]> git.tdb.fi Git - libs/gl.git/blob - source/batch.cpp
Remove a number of rarely used legacy features
[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         /* XXX Should probably provide a fallback to glDrawElements since this class
63         is pretty much required to render anything. */
64         static Require _req(EXT_draw_range_elements);
65 }
66
67 Batch::~Batch()
68 {
69 }
70
71 void Batch::set_data_type(DataType t)
72 {
73         if(t!=UNSIGNED_BYTE && t!=UNSIGNED_SHORT && t!=UNSIGNED_INT)
74                 throw invalid_argument("Batch::set_data_type");
75         if(t==UNSIGNED_BYTE && max_index>0xFE)
76                 throw invalid_operation("Batch::set_data_type");
77         else if(t==UNSIGNED_SHORT && max_index>0xFFFE)
78                 throw invalid_operation("Batch::set_data_type");
79
80         if(data_type==UNSIGNED_BYTE && t==UNSIGNED_SHORT)
81                 expand<unsigned char, unsigned short>(data);
82         else if(data_type==UNSIGNED_BYTE && t==UNSIGNED_INT)
83                 expand<unsigned char, unsigned>(data);
84         else if(data_type==UNSIGNED_SHORT && t==UNSIGNED_INT)
85                 expand<unsigned short, unsigned>(data);
86         else if(data_type==UNSIGNED_INT && t==UNSIGNED_BYTE)
87                 shrink<unsigned, unsigned char>(data);
88         else if(data_type==UNSIGNED_INT && t==UNSIGNED_SHORT)
89                 shrink<unsigned, unsigned short>(data);
90         else if(data_type==UNSIGNED_SHORT && t==UNSIGNED_BYTE)
91                 shrink<unsigned short, unsigned char>(data);
92
93         data_type = t;
94         update_offset();
95         dirty = true;
96 }
97
98 Batch &Batch::append(unsigned i)
99 {
100         append_index(i);
101
102         update_offset();
103         dirty = true;
104
105         return *this;
106 }
107
108 void Batch::append(const vector<unsigned> &ind)
109 {
110         if(ind.empty())
111                 return;
112
113         data.reserve(data.size()+ind.size()*get_index_size());
114         for(vector<unsigned>::const_iterator i=ind.begin(); i!=ind.end(); ++i)
115                 append_index(*i);
116
117         update_offset();
118         dirty = true;
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 NV_primitive_restart;
127         else
128                 return true;
129 }
130
131 void 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                 static Require _req(NV_primitive_restart);
137
138         if(other.data.empty())
139                 return;
140
141         if(prim_type==POINTS || prim_type==LINES || prim_type==TRIANGLES || prim_type==QUADS)
142                 ;
143         else if(NV_primitive_restart)
144         {
145                 restart = true;
146                 if(data_type==UNSIGNED_SHORT)
147                         ::append<unsigned short>(data, 0xFFFF);
148                 else if(data_type==UNSIGNED_INT)
149                         ::append<unsigned>(data, 0xFFFFFFFF);
150                 else
151                         data.push_back(0xFF);
152         }
153         else if(prim_type==TRIANGLE_STRIP)
154         {
155                 append(get_index(size()-1));
156                 append(other.get_index(0));
157                 if(size()&1)
158                         append(other.get_index(0));
159         }
160         else if(prim_type==QUAD_STRIP)
161         {
162                 append(get_index(size()-1));
163                 append(get_index(size()-1));
164                 append(other.get_index(0));
165                 append(other.get_index(0));
166         }
167
168         unsigned count = other.size();
169         for(unsigned i=0; i<count; ++i)
170                 append_index(other.get_index(i));
171
172         update_offset();
173         dirty = true;
174 }
175
176 void Batch::append_index(unsigned i)
177 {
178         if(data.empty())
179                 min_index = max_index = i;
180         else
181         {
182                 min_index = min(min_index, i);
183                 max_index = max(max_index, i);
184         }
185
186         if((data_type==UNSIGNED_BYTE || data_type==UNSIGNED_SHORT) && max_index>0xFFFE)
187                 set_data_type(UNSIGNED_INT);
188         else if(data_type==UNSIGNED_BYTE && max_index>0xFE)
189                 set_data_type(UNSIGNED_SHORT);
190
191         if(data_type==UNSIGNED_SHORT)
192                 ::append<unsigned short>(data, i);
193         else if(data_type==UNSIGNED_INT)
194                 ::append<unsigned>(data, i);
195         else
196                 data.push_back(i);
197 }
198
199 unsigned Batch::get_index_size() const
200 {
201         if(data_type==UNSIGNED_SHORT)
202                 return sizeof(unsigned short);
203         else if(data_type==UNSIGNED_INT)
204                 return sizeof(unsigned);
205         return sizeof(unsigned char);
206 }
207
208 unsigned Batch::get_index(unsigned i) const
209 {
210         if(data_type==UNSIGNED_SHORT)
211                 return *(unsigned short *)&data[i*sizeof(unsigned short)];
212         else if(data_type==UNSIGNED_INT)
213                 return *(unsigned *)&data[i*sizeof(unsigned )];
214         else
215                 return data[i];
216 }
217
218 void Batch::draw() const
219 {
220         if(restart)
221         {
222                 unsigned index;
223                 if(data_type==UNSIGNED_SHORT)
224                         index = 0xFFFF;
225                 else if(data_type==UNSIGNED_INT)
226                         index = 0xFFFFFFFF;
227                 else
228                         index = 0xFF;
229
230                 if(index!=restart_index)
231                 {
232                         if(!restart_index)
233                                 glEnableClientState(GL_PRIMITIVE_RESTART_NV);
234                         glPrimitiveRestartIndexNV(index);
235                         restart_index = index;
236                 }
237         }
238         else if(restart_index && restart_index<=max_index)
239         {
240                 glDisableClientState(GL_PRIMITIVE_RESTART_NV);
241                 restart_index = 0;
242         }
243
244         Buffer *ibuf = get_buffer();
245         BindRestore _bind_ibuf(ibuf, ELEMENT_ARRAY_BUFFER);
246         if(ibuf)
247         {
248                 if(dirty)
249                         update_buffer();
250
251                 glDrawRangeElements(prim_type, min_index, max_index, size(), data_type, reinterpret_cast<void *>(get_offset()));
252         }
253         else
254                 glDrawRangeElements(prim_type, min_index, max_index, size(), data_type, &data[0]);
255 }
256
257
258 Batch::Loader::Loader(Batch &b):
259         DataFile::ObjectLoader<Batch>(b)
260 {
261         add("indices", &Loader::indices);
262 }
263
264 void Batch::Loader::indices(const vector<unsigned> &ind)
265 {
266         obj.append(ind);
267 }
268
269 } // namespace GL
270 } // namespace Msp