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