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