]> git.tdb.fi Git - libs/gl.git/blob - source/batch.cpp
Move Batch members around
[libs/gl.git] / source / batch.cpp
1 #include "batch.h"
2 #include "bindable.h"
3 #include "buffer.h"
4 #include "error.h"
5 #include "ext_draw_range_elements.h"
6 #include "nv_primitive_restart.h"
7 #include "vertexarray.h"
8
9 using namespace std;
10
11 namespace {
12
13 template<typename T>
14 void append(vector<unsigned char> &data, T i)
15 {
16         data.insert(data.end(), sizeof(T), 0);
17         *(T *)(&data[data.size()-sizeof(T)]) = i;
18 }
19
20 template<typename T, typename U>
21 U convert(T n)
22 {
23         if(!static_cast<T>(~n))
24                 return ~0;
25         else
26                 return n;
27 }
28
29 template<typename T, typename U>
30 void expand(vector<unsigned char> &data)
31 {
32         unsigned count = data.size()/sizeof(T);
33         data.resize(count*sizeof(U));
34         for(unsigned i=count; i--;)
35                 *(U *)(&data[i*sizeof(U)]) = convert<T, U>(*(T *)(&data[i*sizeof(T)]));
36 }
37
38 template<typename T, typename U>
39 void shrink(vector<unsigned char> &data)
40 {
41         unsigned count = data.size()/sizeof(T);
42         for(unsigned i=0; i<count; ++i)
43                 *(U *)(&data[i*sizeof(U)]) = convert<T, U>(*(T *)(&data[i*sizeof(T)]));
44         data.resize(count*sizeof(U));
45 }
46
47 }
48
49 namespace Msp {
50 namespace GL {
51
52 unsigned Batch::restart_index = 0;
53
54 Batch::Batch(PrimitiveType t):
55         prim_type(t),
56         data_type(UNSIGNED_BYTE),
57         min_index(0),
58         max_index(0),
59         restart(false),
60         ibuf(0),
61         ibuf_offset(0),
62         next_in_ibuf(0),
63         prev_in_ibuf(0),
64         dirty(false)
65 {
66         /* XXX Should probably provide a fallback to glDrawElements since this class
67         is pretty much required to render anything. */
68         static Require _req(EXT_draw_range_elements);
69 }
70
71 Batch::~Batch()
72 {
73         unlink_from_ibuf();
74 }
75
76 void Batch::set_data_type(DataType t)
77 {
78         if(t!=UNSIGNED_BYTE && t!=UNSIGNED_SHORT && t!=UNSIGNED_INT)
79                 throw invalid_argument("Batch::set_data_type");
80         if(t==UNSIGNED_BYTE && max_index>0xFE)
81                 throw invalid_operation("Batch::set_data_type");
82         else if(t==UNSIGNED_SHORT && max_index>0xFFFE)
83                 throw invalid_operation("Batch::set_data_type");
84
85         if(data_type==UNSIGNED_BYTE && t==UNSIGNED_SHORT)
86                 expand<unsigned char, unsigned short>(data);
87         else if(data_type==UNSIGNED_BYTE && t==UNSIGNED_INT)
88                 expand<unsigned char, unsigned>(data);
89         else if(data_type==UNSIGNED_SHORT && t==UNSIGNED_INT)
90                 expand<unsigned short, unsigned>(data);
91         else if(data_type==UNSIGNED_INT && t==UNSIGNED_BYTE)
92                 shrink<unsigned, unsigned char>(data);
93         else if(data_type==UNSIGNED_INT && t==UNSIGNED_SHORT)
94                 shrink<unsigned, unsigned short>(data);
95         else if(data_type==UNSIGNED_SHORT && t==UNSIGNED_BYTE)
96                 shrink<unsigned short, unsigned char>(data);
97
98         data_type = t;
99         update_ibuf_offsets();
100         dirty = true;
101 }
102
103 void Batch::use_index_buffer(Buffer *buf, Batch *prev)
104 {
105         if(buf && prev && prev->ibuf!=buf)
106                 throw invalid_argument("Batch::use_index_buffer");
107
108         if(!buf)
109         {
110                 prev = 0;
111                 unlink_from_ibuf();
112         }
113
114         ibuf = buf;
115         prev_in_ibuf = prev;
116         next_in_ibuf = 0;
117         if(prev)
118         {
119                 prev->next_in_ibuf = this;
120                 ibuf_offset = prev->ibuf_offset+prev->data.size();
121         }
122         else
123                 ibuf_offset = 0;
124
125         dirty = true;
126 }
127
128 void Batch::unlink_from_ibuf()
129 {
130         if(next_in_ibuf)
131                 next_in_ibuf->prev_in_ibuf = prev_in_ibuf;
132         if(prev_in_ibuf)
133         {
134                 prev_in_ibuf->next_in_ibuf = next_in_ibuf;
135                 prev_in_ibuf->update_ibuf_offsets();
136         }
137         else if(next_in_ibuf)
138         {
139                 next_in_ibuf->ibuf_offset = 0;
140                 next_in_ibuf->update_ibuf_offsets();
141         }
142 }
143
144 void Batch::update_ibuf_offsets()
145 {
146         for(Batch *b=this; b->next_in_ibuf; b=b->next_in_ibuf)
147                 b->next_in_ibuf->ibuf_offset = b->ibuf_offset+b->data.size();
148 }
149
150 Batch &Batch::append(unsigned i)
151 {
152         if(data.empty())
153                 min_index = max_index = i;
154         else
155         {
156                 min_index = min(min_index, i);
157                 max_index = max(max_index, i);
158         }
159
160         if((data_type==UNSIGNED_BYTE || data_type==UNSIGNED_SHORT) && max_index>0xFFFE)
161                 set_data_type(UNSIGNED_INT);
162         else if(data_type==UNSIGNED_BYTE && max_index>0xFE)
163                 set_data_type(UNSIGNED_SHORT);
164
165         if(data_type==UNSIGNED_SHORT)
166                 ::append<unsigned short>(data, i);
167         else if(data_type==UNSIGNED_INT)
168                 ::append<unsigned>(data, i);
169         else
170                 data.push_back(i);
171         
172         update_ibuf_offsets();
173         dirty = true;
174
175         return *this;
176 }
177
178 void Batch::append(const vector<unsigned> &ind)
179 {
180         if(ind.empty())
181                 return;
182
183         if(data.empty())
184                 min_index = max_index = ind.front();
185
186         for(vector<unsigned>::const_iterator i=ind.begin(); i!=ind.end(); ++i)
187         {
188                 min_index = min(min_index, *i);
189                 max_index = max(max_index, *i);
190         }
191
192         if((data_type==UNSIGNED_BYTE || data_type==UNSIGNED_SHORT) && max_index>0xFFFE)
193                 set_data_type(UNSIGNED_INT);
194         else if(data_type==UNSIGNED_BYTE && max_index>0xFE)
195                 set_data_type(UNSIGNED_SHORT);
196
197         unsigned base = data.size();
198         data.resize(data.size()+ind.size()*get_index_size());
199         if(data_type==UNSIGNED_SHORT)
200         {
201                 unsigned short *ptr = reinterpret_cast<unsigned short *>(&data[base]);
202                 for(unsigned i=0; i<ind.size(); ++i)
203                         ptr[i] = ind[i];
204         }
205         else if(data_type==UNSIGNED_INT)
206         {
207                 unsigned *ptr = reinterpret_cast<unsigned *>(&data[base]);
208                 for(unsigned i=0; i<ind.size(); ++i)
209                         ptr[i] = ind[i];
210         }
211         else
212         {
213                 for(unsigned i=0; i<ind.size(); ++i)
214                         data[base+i] = ind[i];
215         }
216 }
217
218 void Batch::append(const Batch &other)
219 {
220         if(other.prim_type!=prim_type)
221                 throw invalid_argument("Batch::append");
222         if(prim_type==LINE_STRIP || prim_type==LINE_LOOP)
223                 throw incompatible_data("Batch::append");
224         else if(prim_type==POLYGON)
225                 throw incompatible_data("Batch::append");
226         else if(prim_type==TRIANGLE_FAN)
227                 static Require _req(NV_primitive_restart);
228
229         if(other.data.empty())
230                 return;
231
232         if(NV_primitive_restart)
233         {
234                 restart = true;
235                 if(data_type==UNSIGNED_SHORT)
236                         ::append<unsigned short>(data, 0xFFFF);
237                 else if(data_type==UNSIGNED_INT)
238                         ::append<unsigned>(data, 0xFFFFFFFF);
239                 else
240                         data.push_back(0xFF);
241         }
242         else if(prim_type==TRIANGLE_STRIP)
243         {
244                 append(get_index(size()-1));
245                 append(other.get_index(0));
246                 if(size()&1)
247                         append(other.get_index(0));
248         }
249         else if(prim_type==QUAD_STRIP)
250         {
251                 append(get_index(size()-1));
252                 append(get_index(size()-1));
253                 append(other.get_index(0));
254                 append(other.get_index(0));
255         }
256
257         unsigned count = other.size();
258         for(unsigned i=0; i<count; ++i)
259                 append(other.get_index(i));
260 }
261
262 unsigned Batch::get_index_size() const
263 {
264         if(data_type==UNSIGNED_SHORT)
265                 return sizeof(unsigned short);
266         else if(data_type==UNSIGNED_INT)
267                 return sizeof(unsigned);
268         return sizeof(unsigned char);
269 }
270
271 unsigned Batch::get_index(unsigned i) const
272 {
273         if(data_type==UNSIGNED_SHORT)
274                 return *(unsigned short *)&data[i*sizeof(unsigned short)];
275         else if(data_type==UNSIGNED_INT)
276                 return *(unsigned *)&data[i*sizeof(unsigned )];
277         else
278                 return data[i];
279 }
280
281 void Batch::draw() const
282 {
283         if(restart)
284         {
285                 unsigned index;
286                 if(data_type==UNSIGNED_SHORT)
287                         index = 0xFFFF;
288                 else if(data_type==UNSIGNED_INT)
289                         index = 0xFFFFFFFF;
290                 else
291                         index = 0xFF;
292
293                 if(index!=restart_index)
294                 {
295                         if(!restart_index)
296                                 glEnableClientState(GL_PRIMITIVE_RESTART_NV);
297                         glPrimitiveRestartIndexNV(index);
298                         restart_index = index;
299                 }
300         }
301         else if(restart_index && restart_index<max_index)
302         {
303                 glDisableClientState(GL_PRIMITIVE_RESTART_NV);
304                 restart_index = 0;
305         }
306
307         if(ibuf)
308         {
309                 if(dirty)
310                 {
311                         const Batch *b = this;
312                         for(; b->prev_in_ibuf; b=b->prev_in_ibuf) ;
313
314                         unsigned chain_size = 0;
315                         for(const Batch *a=b; a; a=a->next_in_ibuf)
316                                 chain_size += a->data.size();
317
318                         ibuf->data(chain_size, 0);
319
320                         for(; b; b=b->next_in_ibuf)
321                         {
322                                 ibuf->sub_data(b->ibuf_offset, b->data.size(), &b->data[0]);
323                                 b->dirty = false;
324                         }
325                 }
326
327                 BufferAlias<ELEMENT_ARRAY_BUFFER> alias(*ibuf);
328                 Bind bind_ibuf(alias, true);
329
330                 glDrawRangeElements(prim_type, min_index, max_index, size(), data_type, reinterpret_cast<void *>(ibuf_offset));
331         }
332         else
333                 glDrawRangeElements(prim_type, min_index, max_index, size(), data_type, &data[0]);
334 }
335
336
337 Batch::Loader::Loader(Batch &b):
338         DataFile::ObjectLoader<Batch>(b)
339 {
340         add("indices", &Loader::indices);
341 }
342
343 void Batch::Loader::indices(const vector<unsigned> &ind)
344 {
345         obj.append(ind);
346 }
347
348 } // namespace GL
349 } // namespace Msp