]> git.tdb.fi Git - libs/gl.git/blob - source/batch.cpp
Add a utility class for switching renderables
[libs/gl.git] / source / batch.cpp
1 #include <msp/gl/extensions/ext_draw_range_elements.h>
2 #include <msp/gl/extensions/msp_legacy_features.h>
3 #include <msp/gl/extensions/msp_primitive_restart.h>
4 #include <msp/gl/extensions/nv_primitive_restart.h>
5 #include "batch.h"
6 #include "bindable.h"
7 #include "buffer.h"
8 #include "error.h"
9 #include "mesh.h"
10 #include "vertexarray.h"
11
12 using namespace std;
13
14 namespace {
15
16 template<typename T>
17 void append(vector<unsigned char> &data, T i)
18 {
19         data.insert(data.end(), sizeof(T), 0);
20         *(T *)(&data[data.size()-sizeof(T)]) = i;
21 }
22
23 template<typename T, typename U>
24 U convert(T n)
25 {
26         if(!static_cast<T>(~n))
27                 return ~0;
28         else
29                 return n;
30 }
31
32 template<typename T, typename U>
33 void expand(vector<unsigned char> &data)
34 {
35         unsigned count = data.size()/sizeof(T);
36         data.resize(count*sizeof(U));
37         for(unsigned i=count; i--;)
38                 *(U *)(&data[i*sizeof(U)]) = convert<T, U>(*(T *)(&data[i*sizeof(T)]));
39 }
40
41 template<typename T, typename U>
42 void shrink(vector<unsigned char> &data)
43 {
44         unsigned count = data.size()/sizeof(T);
45         for(unsigned i=0; i<count; ++i)
46                 *(U *)(&data[i*sizeof(U)]) = convert<T, U>(*(T *)(&data[i*sizeof(T)]));
47         data.resize(count*sizeof(U));
48 }
49
50 }
51
52 namespace Msp {
53 namespace GL {
54
55 unsigned Batch::restart_index = 0;
56
57 Batch::Batch(PrimitiveType t):
58         prim_type(t),
59         data_type(UNSIGNED_BYTE),
60         min_index(0),
61         max_index(0),
62         restart(false)
63 { }
64
65 Batch::~Batch()
66 {
67 }
68
69 void Batch::set_data_type(DataType t)
70 {
71         if(t!=UNSIGNED_BYTE && t!=UNSIGNED_SHORT && t!=UNSIGNED_INT)
72                 throw invalid_argument("Batch::set_data_type");
73         if(t==UNSIGNED_BYTE && max_index>0xFE)
74                 throw invalid_operation("Batch::set_data_type");
75         else if(t==UNSIGNED_SHORT && max_index>0xFFFE)
76                 throw invalid_operation("Batch::set_data_type");
77
78         if(data_type==UNSIGNED_BYTE && t==UNSIGNED_SHORT)
79                 expand<unsigned char, unsigned short>(data);
80         else if(data_type==UNSIGNED_BYTE && t==UNSIGNED_INT)
81                 expand<unsigned char, unsigned>(data);
82         else if(data_type==UNSIGNED_SHORT && t==UNSIGNED_INT)
83                 expand<unsigned short, unsigned>(data);
84         else if(data_type==UNSIGNED_INT && t==UNSIGNED_BYTE)
85                 shrink<unsigned, unsigned char>(data);
86         else if(data_type==UNSIGNED_INT && t==UNSIGNED_SHORT)
87                 shrink<unsigned, unsigned short>(data);
88         else if(data_type==UNSIGNED_SHORT && t==UNSIGNED_BYTE)
89                 shrink<unsigned short, unsigned char>(data);
90
91         data_type = t;
92         update_offset();
93         dirty = true;
94 }
95
96 Batch &Batch::append(unsigned i)
97 {
98         append_index(i);
99
100         update_offset();
101         dirty = true;
102
103         return *this;
104 }
105
106 Batch &Batch::append(const vector<unsigned> &ind)
107 {
108         if(ind.empty())
109                 return *this;
110
111         data.reserve(data.size()+ind.size()*get_index_size());
112         for(vector<unsigned>::const_iterator i=ind.begin(); i!=ind.end(); ++i)
113                 append_index(*i);
114
115         update_offset();
116         dirty = true;
117
118         return *this;
119 }
120
121 bool Batch::can_append(PrimitiveType other_type)
122 {
123         if(other_type!=prim_type)
124                 return false;
125         else if(prim_type==LINE_STRIP || prim_type==LINE_LOOP || prim_type==TRIANGLE_FAN)
126                 return MSP_primitive_restart || NV_primitive_restart;
127         else
128                 return true;
129 }
130
131 Batch &Batch::append(const Batch &other)
132 {
133         if(other.prim_type!=prim_type)
134                 throw invalid_argument("Batch::append");
135         if(prim_type==LINE_STRIP || prim_type==LINE_LOOP || prim_type==TRIANGLE_FAN)
136         {
137                 if(!MSP_primitive_restart)
138                 {
139                         static Require _req(NV_primitive_restart);
140                         // Make sure we have glEnable/DisableClientState as well
141                         static Require _req2(MSP_legacy_features);
142                 }
143         }
144
145         if(other.data.empty())
146                 return *this;
147
148         if(prim_type==POINTS || prim_type==LINES || prim_type==TRIANGLES || prim_type==QUADS)
149                 ;
150         else if(MSP_primitive_restart || NV_primitive_restart)
151         {
152                 restart = true;
153                 if(data_type==UNSIGNED_SHORT)
154                         ::append<unsigned short>(data, 0xFFFF);
155                 else if(data_type==UNSIGNED_INT)
156                         ::append<unsigned>(data, 0xFFFFFFFF);
157                 else
158                         data.push_back(0xFF);
159         }
160         else if(prim_type==TRIANGLE_STRIP)
161         {
162                 append(get_index(size()-1));
163                 append(other.get_index(0));
164                 if(size()&1)
165                         append(other.get_index(0));
166         }
167         else if(prim_type==QUAD_STRIP)
168         {
169                 append(get_index(size()-1));
170                 append(get_index(size()-1));
171                 append(other.get_index(0));
172                 append(other.get_index(0));
173         }
174
175         unsigned count = other.size();
176         for(unsigned i=0; i<count; ++i)
177                 append_index(other.get_index(i));
178
179         update_offset();
180         dirty = true;
181
182         return *this;
183 }
184
185 void Batch::append_index(unsigned i)
186 {
187         if(data.empty())
188                 min_index = max_index = i;
189         else
190         {
191                 min_index = min(min_index, i);
192                 max_index = max(max_index, i);
193         }
194
195         if((data_type==UNSIGNED_BYTE || data_type==UNSIGNED_SHORT) && max_index>0xFFFE)
196                 set_data_type(UNSIGNED_INT);
197         else if(data_type==UNSIGNED_BYTE && max_index>0xFE)
198                 set_data_type(UNSIGNED_SHORT);
199
200         if(data_type==UNSIGNED_SHORT)
201                 ::append<unsigned short>(data, i);
202         else if(data_type==UNSIGNED_INT)
203                 ::append<unsigned>(data, i);
204         else
205                 data.push_back(i);
206 }
207
208 unsigned Batch::get_index_size() const
209 {
210         if(data_type==UNSIGNED_SHORT)
211                 return sizeof(unsigned short);
212         else if(data_type==UNSIGNED_INT)
213                 return sizeof(unsigned);
214         return sizeof(unsigned char);
215 }
216
217 unsigned Batch::get_index(unsigned i) const
218 {
219         if(data_type==UNSIGNED_SHORT)
220                 return *(unsigned short *)&data[i*sizeof(unsigned short)];
221         else if(data_type==UNSIGNED_INT)
222                 return *(unsigned *)&data[i*sizeof(unsigned )];
223         else
224                 return data[i];
225 }
226
227 void Batch::draw() const
228 {
229         if(restart)
230         {
231                 unsigned index;
232                 if(data_type==UNSIGNED_SHORT)
233                         index = 0xFFFF;
234                 else if(data_type==UNSIGNED_INT)
235                         index = 0xFFFFFFFF;
236                 else
237                         index = 0xFF;
238
239                 if(index!=restart_index)
240                         set_restart_index(index);
241         }
242         else if(restart_index && restart_index<=max_index)
243                 set_restart_index(0);
244
245         Buffer *ibuf = get_buffer();
246         const void *data_ptr;
247         BindRestore _bind_ibuf(ibuf, ELEMENT_ARRAY_BUFFER);
248         if(ibuf)
249         {
250                 if(dirty)
251                         update_buffer();
252
253                 data_ptr = reinterpret_cast<const void *>(get_offset());
254         }
255         else
256                 data_ptr = &data[0];
257
258         if(EXT_draw_range_elements)
259                 glDrawRangeElements(prim_type, min_index, max_index, size(), data_type, data_ptr);
260         else
261                 glDrawElements(prim_type, size(), data_type, data_ptr);
262 }
263
264 void Batch::set_restart_index(unsigned index)
265 {
266         if(MSP_primitive_restart)
267         {
268                 if(index>0)
269                 {
270                         if(!restart_index)
271                                 glEnable(GL_PRIMITIVE_RESTART);
272                         glPrimitiveRestartIndex(index);
273                 }
274                 else
275                         glDisable(GL_PRIMITIVE_RESTART);
276         }
277         else
278         {
279                 if(index>0)
280                 {
281                         if(!restart_index)
282                                 glEnableClientState(GL_PRIMITIVE_RESTART_NV);
283                         glPrimitiveRestartIndexNV(index);
284                 }
285                 else
286                         glDisableClientState(GL_PRIMITIVE_RESTART_NV);
287         }
288
289         restart_index = index;
290 }
291
292
293 Batch::Loader::Loader(Batch &b):
294         DataFile::ObjectLoader<Batch>(b)
295 {
296         add("indices", &Loader::indices);
297 }
298
299 void Batch::Loader::indices(const vector<unsigned> &ind)
300 {
301         obj.append(ind);
302 }
303
304 } // namespace GL
305 } // namespace Msp