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