]> git.tdb.fi Git - libs/gl.git/blob - source/batch.cpp
Add file and line information to ProgramParser errors
[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 void Batch::append(const vector<unsigned> &ind)
107 {
108         if(ind.empty())
109                 return;
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
119 bool Batch::can_append(PrimitiveType other_type)
120 {
121         if(other_type!=prim_type)
122                 return false;
123         else if(prim_type==LINE_STRIP || prim_type==LINE_LOOP || prim_type==TRIANGLE_FAN)
124                 return MSP_primitive_restart || NV_primitive_restart;
125         else
126                 return true;
127 }
128
129 void Batch::append(const Batch &other)
130 {
131         if(other.prim_type!=prim_type)
132                 throw invalid_argument("Batch::append");
133         if(prim_type==LINE_STRIP || prim_type==LINE_LOOP || prim_type==TRIANGLE_FAN)
134         {
135                 if(!MSP_primitive_restart)
136                 {
137                         static Require _req(NV_primitive_restart);
138                         // Make sure we have glEnable/DisableClientState as well
139                         static Require _req2(MSP_legacy_features);
140                 }
141         }
142
143         if(other.data.empty())
144                 return;
145
146         if(prim_type==POINTS || prim_type==LINES || prim_type==TRIANGLES || prim_type==QUADS)
147                 ;
148         else if(MSP_primitive_restart || NV_primitive_restart)
149         {
150                 restart = true;
151                 if(data_type==UNSIGNED_SHORT)
152                         ::append<unsigned short>(data, 0xFFFF);
153                 else if(data_type==UNSIGNED_INT)
154                         ::append<unsigned>(data, 0xFFFFFFFF);
155                 else
156                         data.push_back(0xFF);
157         }
158         else if(prim_type==TRIANGLE_STRIP)
159         {
160                 append(get_index(size()-1));
161                 append(other.get_index(0));
162                 if(size()&1)
163                         append(other.get_index(0));
164         }
165         else if(prim_type==QUAD_STRIP)
166         {
167                 append(get_index(size()-1));
168                 append(get_index(size()-1));
169                 append(other.get_index(0));
170                 append(other.get_index(0));
171         }
172
173         unsigned count = other.size();
174         for(unsigned i=0; i<count; ++i)
175                 append_index(other.get_index(i));
176
177         update_offset();
178         dirty = true;
179 }
180
181 void Batch::append_index(unsigned i)
182 {
183         if(data.empty())
184                 min_index = max_index = i;
185         else
186         {
187                 min_index = min(min_index, i);
188                 max_index = max(max_index, i);
189         }
190
191         if((data_type==UNSIGNED_BYTE || data_type==UNSIGNED_SHORT) && max_index>0xFFFE)
192                 set_data_type(UNSIGNED_INT);
193         else if(data_type==UNSIGNED_BYTE && max_index>0xFE)
194                 set_data_type(UNSIGNED_SHORT);
195
196         if(data_type==UNSIGNED_SHORT)
197                 ::append<unsigned short>(data, i);
198         else if(data_type==UNSIGNED_INT)
199                 ::append<unsigned>(data, i);
200         else
201                 data.push_back(i);
202 }
203
204 unsigned Batch::get_index_size() const
205 {
206         if(data_type==UNSIGNED_SHORT)
207                 return sizeof(unsigned short);
208         else if(data_type==UNSIGNED_INT)
209                 return sizeof(unsigned);
210         return sizeof(unsigned char);
211 }
212
213 unsigned Batch::get_index(unsigned i) const
214 {
215         if(data_type==UNSIGNED_SHORT)
216                 return *(unsigned short *)&data[i*sizeof(unsigned short)];
217         else if(data_type==UNSIGNED_INT)
218                 return *(unsigned *)&data[i*sizeof(unsigned )];
219         else
220                 return data[i];
221 }
222
223 void Batch::draw() const
224 {
225         if(restart)
226         {
227                 unsigned index;
228                 if(data_type==UNSIGNED_SHORT)
229                         index = 0xFFFF;
230                 else if(data_type==UNSIGNED_INT)
231                         index = 0xFFFFFFFF;
232                 else
233                         index = 0xFF;
234
235                 if(index!=restart_index)
236                         set_restart_index(index);
237         }
238         else if(restart_index && restart_index<=max_index)
239                 set_restart_index(0);
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 void Batch::set_restart_index(unsigned index)
261 {
262         if(MSP_primitive_restart)
263         {
264                 if(index>0)
265                 {
266                         if(!restart_index)
267                                 glEnable(GL_PRIMITIVE_RESTART);
268                         glPrimitiveRestartIndex(index);
269                 }
270                 else
271                         glDisable(GL_PRIMITIVE_RESTART);
272         }
273         else
274         {
275                 if(index>0)
276                 {
277                         if(!restart_index)
278                                 glEnableClientState(GL_PRIMITIVE_RESTART_NV);
279                         glPrimitiveRestartIndexNV(index);
280                 }
281                 else
282                         glDisableClientState(GL_PRIMITIVE_RESTART_NV);
283         }
284
285         restart_index = index;
286 }
287
288
289 Batch::Loader::Loader(Batch &b):
290         DataFile::ObjectLoader<Batch>(b)
291 {
292         add("indices", &Loader::indices);
293 }
294
295 void Batch::Loader::indices(const vector<unsigned> &ind)
296 {
297         obj.append(ind);
298 }
299
300 } // namespace GL
301 } // namespace Msp