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