]> git.tdb.fi Git - libs/gl.git/blob - source/batch.cpp
Remove support for legacy OpenGL features
[libs/gl.git] / source / batch.cpp
1 #include <msp/gl/extensions/arb_draw_instanced.h>
2 #include <msp/gl/extensions/ext_draw_range_elements.h>
3 #include <msp/gl/extensions/msp_primitive_restart.h>
4 #include "batch.h"
5 #include "bindable.h"
6 #include "buffer.h"
7 #include "error.h"
8 #include "mesh.h"
9 #include "vertexarray.h"
10
11 using namespace std;
12
13 namespace {
14
15 template<typename T>
16 void append(vector<unsigned char> &data, T i)
17 {
18         data.insert(data.end(), sizeof(T), 0);
19         *(T *)(&data[data.size()-sizeof(T)]) = i;
20 }
21
22 template<typename T, typename U>
23 U convert(T n)
24 {
25         if(!static_cast<T>(~n))
26                 return ~0;
27         else
28                 return n;
29 }
30
31 template<typename T, typename U>
32 void expand(vector<unsigned char> &data)
33 {
34         unsigned count = data.size()/sizeof(T);
35         data.resize(count*sizeof(U));
36         for(unsigned i=count; i--;)
37                 *(U *)(&data[i*sizeof(U)]) = convert<T, U>(*(T *)(&data[i*sizeof(T)]));
38 }
39
40 template<typename T, typename U>
41 void shrink(vector<unsigned char> &data)
42 {
43         unsigned count = data.size()/sizeof(T);
44         for(unsigned i=0; i<count; ++i)
45                 *(U *)(&data[i*sizeof(U)]) = convert<T, U>(*(T *)(&data[i*sizeof(T)]));
46         data.resize(count*sizeof(U));
47 }
48
49 }
50
51 namespace Msp {
52 namespace GL {
53
54 unsigned Batch::restart_index = 0;
55
56 Batch::Batch(PrimitiveType t):
57         prim_type(t),
58         data_type(UNSIGNED_BYTE),
59         min_index(0),
60         max_index(0),
61         restart(false)
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 Batch &Batch::append(const vector<unsigned> &ind)
106 {
107         if(ind.empty())
108                 return *this;
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         return *this;
118 }
119
120 bool Batch::can_append(PrimitiveType other_type)
121 {
122         if(other_type!=prim_type)
123                 return false;
124         else if(prim_type==LINE_STRIP || prim_type==LINE_LOOP || prim_type==TRIANGLE_FAN)
125                 return MSP_primitive_restart;
126         else
127                 return true;
128 }
129
130 Batch &Batch::append(const Batch &other)
131 {
132         if(other.prim_type!=prim_type)
133                 throw invalid_argument("Batch::append");
134         if(prim_type==LINE_STRIP || prim_type==LINE_LOOP || prim_type==TRIANGLE_FAN)
135                 static Require _req(MSP_primitive_restart);
136
137         if(other.data.empty())
138                 return *this;
139
140         if(prim_type==POINTS || prim_type==LINES || prim_type==TRIANGLES)
141                 ;
142         else if(MSP_primitive_restart)
143         {
144                 restart = true;
145                 if(data_type==UNSIGNED_SHORT)
146                         ::append<unsigned short>(data, 0xFFFF);
147                 else if(data_type==UNSIGNED_INT)
148                         ::append<unsigned>(data, 0xFFFFFFFF);
149                 else
150                         data.push_back(0xFF);
151         }
152         else if(prim_type==TRIANGLE_STRIP)
153         {
154                 append(get_index(size()-1));
155                 append(other.get_index(0));
156                 if(size()&1)
157                         append(other.get_index(0));
158         }
159
160         unsigned count = other.size();
161         for(unsigned i=0; i<count; ++i)
162                 append_index(other.get_index(i));
163
164         update_offset();
165         dirty = true;
166
167         return *this;
168 }
169
170 void Batch::append_index(unsigned i)
171 {
172         if(data.empty())
173                 min_index = max_index = i;
174         else
175         {
176                 min_index = min(min_index, i);
177                 max_index = max(max_index, i);
178         }
179
180         if((data_type==UNSIGNED_BYTE || data_type==UNSIGNED_SHORT) && max_index>0xFFFE)
181                 set_data_type(UNSIGNED_INT);
182         else if(data_type==UNSIGNED_BYTE && max_index>0xFE)
183                 set_data_type(UNSIGNED_SHORT);
184
185         if(data_type==UNSIGNED_SHORT)
186                 ::append<unsigned short>(data, i);
187         else if(data_type==UNSIGNED_INT)
188                 ::append<unsigned>(data, i);
189         else
190                 data.push_back(i);
191 }
192
193 unsigned Batch::get_index_size() const
194 {
195         if(data_type==UNSIGNED_SHORT)
196                 return sizeof(unsigned short);
197         else if(data_type==UNSIGNED_INT)
198                 return sizeof(unsigned);
199         return sizeof(unsigned char);
200 }
201
202 unsigned Batch::get_index(unsigned i) const
203 {
204         if(data_type==UNSIGNED_SHORT)
205                 return *(unsigned short *)&data[i*sizeof(unsigned short)];
206         else if(data_type==UNSIGNED_INT)
207                 return *(unsigned *)&data[i*sizeof(unsigned )];
208         else
209                 return data[i];
210 }
211
212 void Batch::draw() const
213 {
214         BindRestore _bind_ibuf(get_buffer(), ELEMENT_ARRAY_BUFFER);
215         const void *data_ptr = setup_draw();
216
217         if(EXT_draw_range_elements)
218                 glDrawRangeElements(prim_type, min_index, max_index, size(), data_type, data_ptr);
219         else
220                 glDrawElements(prim_type, size(), data_type, data_ptr);
221 }
222
223 void Batch::draw_instanced(unsigned count) const
224 {
225         static Require req(ARB_draw_instanced);
226
227         BindRestore _bind_ibuf(get_buffer(), ELEMENT_ARRAY_BUFFER);
228         const void *data_ptr = setup_draw();
229
230         glDrawElementsInstanced(prim_type, size(), data_type, data_ptr, count);
231 }
232
233 const void *Batch::setup_draw() const
234 {
235         if(restart)
236         {
237                 unsigned index;
238                 if(data_type==UNSIGNED_SHORT)
239                         index = 0xFFFF;
240                 else if(data_type==UNSIGNED_INT)
241                         index = 0xFFFFFFFF;
242                 else
243                         index = 0xFF;
244
245                 if(index!=restart_index)
246                         set_restart_index(index);
247         }
248         else if(restart_index && restart_index<=max_index)
249                 set_restart_index(0);
250
251         if(get_buffer())
252         {
253                 if(dirty)
254                         update_buffer();
255
256                 return reinterpret_cast<const void *>(get_offset());
257         }
258         else
259                 return &data[0];
260 }
261
262 void Batch::set_restart_index(unsigned index)
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         restart_index = index;
274 }
275
276
277 Batch::Loader::Loader(Batch &b):
278         DataFile::ObjectLoader<Batch>(b)
279 {
280         add("indices", &Loader::indices);
281 }
282
283 void Batch::Loader::indices(const vector<unsigned> &ind)
284 {
285         obj.append(ind);
286 }
287
288 } // namespace GL
289 } // namespace Msp