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