]> git.tdb.fi Git - libs/gl.git/blob - source/batch.cpp
Some refactoring of the draw code path
[libs/gl.git] / source / batch.cpp
1 #include <msp/gl/extensions/ext_draw_range_elements.h>
2 #include <msp/gl/extensions/msp_legacy_features.h>
3 #include <msp/gl/extensions/msp_primitive_restart.h>
4 #include <msp/gl/extensions/nv_primitive_restart.h>
5 #include "batch.h"
6 #include "bindable.h"
7 #include "buffer.h"
8 #include "error.h"
9 #include "mesh.h"
10 #include "vertexarray.h"
11
12 using namespace std;
13
14 namespace {
15
16 template<typename T>
17 void append(vector<unsigned char> &data, T i)
18 {
19         data.insert(data.end(), sizeof(T), 0);
20         *(T *)(&data[data.size()-sizeof(T)]) = i;
21 }
22
23 template<typename T, typename U>
24 U convert(T n)
25 {
26         if(!static_cast<T>(~n))
27                 return ~0;
28         else
29                 return n;
30 }
31
32 template<typename T, typename U>
33 void expand(vector<unsigned char> &data)
34 {
35         unsigned count = data.size()/sizeof(T);
36         data.resize(count*sizeof(U));
37         for(unsigned i=count; i--;)
38                 *(U *)(&data[i*sizeof(U)]) = convert<T, U>(*(T *)(&data[i*sizeof(T)]));
39 }
40
41 template<typename T, typename U>
42 void shrink(vector<unsigned char> &data)
43 {
44         unsigned count = data.size()/sizeof(T);
45         for(unsigned i=0; i<count; ++i)
46                 *(U *)(&data[i*sizeof(U)]) = convert<T, U>(*(T *)(&data[i*sizeof(T)]));
47         data.resize(count*sizeof(U));
48 }
49
50 }
51
52 namespace Msp {
53 namespace GL {
54
55 unsigned Batch::restart_index = 0;
56
57 Batch::Batch(PrimitiveType t):
58         prim_type(t),
59         data_type(UNSIGNED_BYTE),
60         min_index(0),
61         max_index(0),
62         restart(false)
63 { }
64
65 Batch::~Batch()
66 {
67 }
68
69 void Batch::set_data_type(DataType t)
70 {
71         if(t!=UNSIGNED_BYTE && t!=UNSIGNED_SHORT && t!=UNSIGNED_INT)
72                 throw invalid_argument("Batch::set_data_type");
73         if(t==UNSIGNED_BYTE && max_index>0xFE)
74                 throw invalid_operation("Batch::set_data_type");
75         else if(t==UNSIGNED_SHORT && max_index>0xFFFE)
76                 throw invalid_operation("Batch::set_data_type");
77
78         if(data_type==UNSIGNED_BYTE && t==UNSIGNED_SHORT)
79                 expand<unsigned char, unsigned short>(data);
80         else if(data_type==UNSIGNED_BYTE && t==UNSIGNED_INT)
81                 expand<unsigned char, unsigned>(data);
82         else if(data_type==UNSIGNED_SHORT && t==UNSIGNED_INT)
83                 expand<unsigned short, unsigned>(data);
84         else if(data_type==UNSIGNED_INT && t==UNSIGNED_BYTE)
85                 shrink<unsigned, unsigned char>(data);
86         else if(data_type==UNSIGNED_INT && t==UNSIGNED_SHORT)
87                 shrink<unsigned, unsigned short>(data);
88         else if(data_type==UNSIGNED_SHORT && t==UNSIGNED_BYTE)
89                 shrink<unsigned short, unsigned char>(data);
90
91         data_type = t;
92         update_offset();
93         dirty = true;
94 }
95
96 Batch &Batch::append(unsigned i)
97 {
98         append_index(i);
99
100         update_offset();
101         dirty = true;
102
103         return *this;
104 }
105
106 Batch &Batch::append(const vector<unsigned> &ind)
107 {
108         if(ind.empty())
109                 return *this;
110
111         data.reserve(data.size()+ind.size()*get_index_size());
112         for(vector<unsigned>::const_iterator i=ind.begin(); i!=ind.end(); ++i)
113                 append_index(*i);
114
115         update_offset();
116         dirty = true;
117
118         return *this;
119 }
120
121 bool Batch::can_append(PrimitiveType other_type)
122 {
123         if(other_type!=prim_type)
124                 return false;
125         else if(prim_type==LINE_STRIP || prim_type==LINE_LOOP || prim_type==TRIANGLE_FAN)
126                 return MSP_primitive_restart || NV_primitive_restart;
127         else
128                 return true;
129 }
130
131 Batch &Batch::append(const Batch &other)
132 {
133         if(other.prim_type!=prim_type)
134                 throw invalid_argument("Batch::append");
135         if(prim_type==LINE_STRIP || prim_type==LINE_LOOP || prim_type==TRIANGLE_FAN)
136         {
137                 if(!MSP_primitive_restart)
138                 {
139                         static Require _req(NV_primitive_restart);
140                         // Make sure we have glEnable/DisableClientState as well
141                         static Require _req2(MSP_legacy_features);
142                 }
143         }
144
145         if(other.data.empty())
146                 return *this;
147
148         if(prim_type==POINTS || prim_type==LINES || prim_type==TRIANGLES || prim_type==QUADS)
149                 ;
150         else if(MSP_primitive_restart || NV_primitive_restart)
151         {
152                 restart = true;
153                 if(data_type==UNSIGNED_SHORT)
154                         ::append<unsigned short>(data, 0xFFFF);
155                 else if(data_type==UNSIGNED_INT)
156                         ::append<unsigned>(data, 0xFFFFFFFF);
157                 else
158                         data.push_back(0xFF);
159         }
160         else if(prim_type==TRIANGLE_STRIP)
161         {
162                 append(get_index(size()-1));
163                 append(other.get_index(0));
164                 if(size()&1)
165                         append(other.get_index(0));
166         }
167         else if(prim_type==QUAD_STRIP)
168         {
169                 append(get_index(size()-1));
170                 append(get_index(size()-1));
171                 append(other.get_index(0));
172                 append(other.get_index(0));
173         }
174
175         unsigned count = other.size();
176         for(unsigned i=0; i<count; ++i)
177                 append_index(other.get_index(i));
178
179         update_offset();
180         dirty = true;
181
182         return *this;
183 }
184
185 void Batch::append_index(unsigned i)
186 {
187         if(data.empty())
188                 min_index = max_index = i;
189         else
190         {
191                 min_index = min(min_index, i);
192                 max_index = max(max_index, i);
193         }
194
195         if((data_type==UNSIGNED_BYTE || data_type==UNSIGNED_SHORT) && max_index>0xFFFE)
196                 set_data_type(UNSIGNED_INT);
197         else if(data_type==UNSIGNED_BYTE && max_index>0xFE)
198                 set_data_type(UNSIGNED_SHORT);
199
200         if(data_type==UNSIGNED_SHORT)
201                 ::append<unsigned short>(data, i);
202         else if(data_type==UNSIGNED_INT)
203                 ::append<unsigned>(data, i);
204         else
205                 data.push_back(i);
206 }
207
208 unsigned Batch::get_index_size() const
209 {
210         if(data_type==UNSIGNED_SHORT)
211                 return sizeof(unsigned short);
212         else if(data_type==UNSIGNED_INT)
213                 return sizeof(unsigned);
214         return sizeof(unsigned char);
215 }
216
217 unsigned Batch::get_index(unsigned i) const
218 {
219         if(data_type==UNSIGNED_SHORT)
220                 return *(unsigned short *)&data[i*sizeof(unsigned short)];
221         else if(data_type==UNSIGNED_INT)
222                 return *(unsigned *)&data[i*sizeof(unsigned )];
223         else
224                 return data[i];
225 }
226
227 void Batch::draw() const
228 {
229         BindRestore _bind_ibuf(get_buffer(), ELEMENT_ARRAY_BUFFER);
230         const void *data_ptr = setup_draw();
231
232         if(EXT_draw_range_elements)
233                 glDrawRangeElements(prim_type, min_index, max_index, size(), data_type, data_ptr);
234         else
235                 glDrawElements(prim_type, size(), data_type, data_ptr);
236 }
237
238 const void *Batch::setup_draw() const
239 {
240         if(restart)
241         {
242                 unsigned index;
243                 if(data_type==UNSIGNED_SHORT)
244                         index = 0xFFFF;
245                 else if(data_type==UNSIGNED_INT)
246                         index = 0xFFFFFFFF;
247                 else
248                         index = 0xFF;
249
250                 if(index!=restart_index)
251                         set_restart_index(index);
252         }
253         else if(restart_index && restart_index<=max_index)
254                 set_restart_index(0);
255
256         if(get_buffer())
257         {
258                 if(dirty)
259                         update_buffer();
260
261                 return reinterpret_cast<const void *>(get_offset());
262         }
263         else
264                 return &data[0];
265 }
266
267 void Batch::set_restart_index(unsigned index)
268 {
269         if(MSP_primitive_restart)
270         {
271                 if(index>0)
272                 {
273                         if(!restart_index)
274                                 glEnable(GL_PRIMITIVE_RESTART);
275                         glPrimitiveRestartIndex(index);
276                 }
277                 else
278                         glDisable(GL_PRIMITIVE_RESTART);
279         }
280         else
281         {
282                 if(index>0)
283                 {
284                         if(!restart_index)
285                                 glEnableClientState(GL_PRIMITIVE_RESTART_NV);
286                         glPrimitiveRestartIndexNV(index);
287                 }
288                 else
289                         glDisableClientState(GL_PRIMITIVE_RESTART_NV);
290         }
291
292         restart_index = index;
293 }
294
295
296 Batch::Loader::Loader(Batch &b):
297         DataFile::ObjectLoader<Batch>(b)
298 {
299         add("indices", &Loader::indices);
300 }
301
302 void Batch::Loader::indices(const vector<unsigned> &ind)
303 {
304         obj.append(ind);
305 }
306
307 } // namespace GL
308 } // namespace Msp