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