]> git.tdb.fi Git - libs/gl.git/blob - source/mesh.cpp
Clear manager in destructors of individual resource classes
[libs/gl.git] / source / mesh.cpp
1 #include <msp/gl/extensions/arb_vertex_array_object.h>
2 #include <msp/gl/extensions/arb_vertex_shader.h>
3 #include <msp/gl/extensions/nv_primitive_restart.h>
4 #include "buffer.h"
5 #include "error.h"
6 #include "mesh.h"
7 #include "renderer.h"
8 #include "resourcemanager.h"
9
10 using namespace std;
11
12 namespace Msp {
13 namespace GL {
14
15 Mesh::Mesh(ResourceManager *rm):
16         vertices(VERTEX3)
17 {
18         init(rm);
19 }
20
21 Mesh::Mesh(const VertexFormat &f, ResourceManager *rm):
22         vertices(f)
23 {
24         init(rm);
25 }
26
27 void Mesh::init(ResourceManager *rm)
28 {
29         vbuf = 0;
30         ibuf = 0;
31         vao_id = 0;
32         defer_buffers = true;
33         dirty = true;
34         disallow_rendering = false;
35         winding = 0;
36
37         if(rm)
38                 set_manager(rm);
39 }
40
41 Mesh::~Mesh()
42 {
43         set_manager(0);
44         delete vbuf;
45         delete ibuf;
46         if(vao_id)
47                 glDeleteVertexArrays(1, &vao_id);
48 }
49
50 void Mesh::clear()
51 {
52         vertices.clear();
53         batches.clear();
54 }
55
56 void Mesh::use_buffers(bool b)
57 {
58         defer_buffers = false;
59         if(b)
60                 create_buffers();
61         else
62         {
63                 vertices.use_buffer(0);
64                 delete vbuf;
65                 vbuf = 0;
66                 delete ibuf;
67                 ibuf = 0;
68         }
69 }
70
71 void Mesh::create_buffers()
72 {
73         defer_buffers = false;
74
75         if(!vbuf)
76                 vbuf = new Buffer(ARRAY_BUFFER);
77         vertices.use_buffer(vbuf);
78
79         if(!ibuf)
80                 ibuf = new Buffer(ELEMENT_ARRAY_BUFFER);
81
82         if(ARB_vertex_array_object && !vao_id)
83                 glGenVertexArrays(1, &vao_id);
84 }
85
86 void Mesh::setup_vao() const
87 {
88         Bind bind_vbuf(vbuf, ARRAY_BUFFER);
89
90         const VertexFormat &fmt = vertices.get_format();
91         unsigned stride = get_stride(fmt)*sizeof(float);
92         float *ptr = 0;
93         for(const unsigned char *c=fmt.begin(); c!=fmt.end(); ++c)
94         {
95                 unsigned t = get_component_type(*c);
96                 if(t>=get_component_type(ATTRIB1))
97                         t -= get_component_type(ATTRIB1);
98                 unsigned sz = get_component_size(*c);
99                 if(*c==COLOR4_UBYTE)
100                         glVertexAttribPointer(t, 4, GL_UNSIGNED_BYTE, true, stride, ptr);
101                 else
102                         glVertexAttribPointer(t, sz, GL_FLOAT, false, stride, ptr);
103                 glEnableVertexAttribArray(t);
104                 ptr += sz;
105         }
106         glBindBuffer(ELEMENT_ARRAY_BUFFER, ibuf->get_id());
107
108         dirty = false;
109 }
110
111 unsigned Mesh::get_n_vertices() const
112 {
113         return vertices.size();
114 }
115
116 float *Mesh::modify_vertex(unsigned i)
117 {
118         return vertices.modify(i);
119 }
120
121 void Mesh::add_batch(const Batch &b)
122 {
123         if(defer_buffers)
124                 create_buffers();
125
126         if(!batches.empty() && batches.back().can_append(b.get_type()))
127                 batches.back().append(b);
128         else
129         {
130                 Batch *prev = (batches.empty() ? 0 : &batches.back());
131                 batches.push_back(b);
132                 if(ibuf)
133                         batches.back().use_buffer(ibuf, prev);
134         }
135 }
136
137 void Mesh::set_winding(const WindingTest *w)
138 {
139         winding = w;
140 }
141
142 void Mesh::draw() const
143 {
144         const Mesh *cur = current();
145         if(cur && cur!=this)
146                 throw invalid_operation("Mesh::draw");
147
148         if(manager)
149         {
150                 manager->resource_used(*this);
151                 if(disallow_rendering)
152                         return;
153         }
154
155         if(!current())
156                 vertices.apply();
157         BindRestore bind_ibuf(ibuf, ELEMENT_ARRAY_BUFFER);
158         Bind bind_winding(winding);
159
160         for(list<Batch>::const_iterator i=batches.begin(); i!=batches.end(); ++i)
161                 i->draw();
162 }
163
164 void Mesh::draw(Renderer &renderer) const
165 {
166         if(manager)
167         {
168                 manager->resource_used(*this);
169                 if(disallow_rendering)
170                         return;
171         }
172
173         renderer.set_mesh(this);
174         renderer.set_winding_test(winding);
175
176         for(list<Batch>::const_iterator i=batches.begin(); i!=batches.end(); ++i)
177                 renderer.draw(*i);
178 }
179
180 void Mesh::bind() const
181 {
182         /* If VAOs are not supported, vao_id is zero and set_current won't get
183         called.  Thus unbind won't try to call a null function either. */
184         if(!vao_id)
185         {
186                 unbind();
187                 vertices.apply(false);
188         }
189         else if(set_current(this))
190         {
191                 glBindVertexArray(vao_id);
192                 vertices.refresh();
193                 if(dirty)
194                         setup_vao();
195         }
196 }
197
198 void Mesh::unbind()
199 {
200         if(set_current(0))
201                 glBindVertexArray(0);
202 }
203
204 Resource::AsyncLoader *Mesh::load(IO::Seekable &io, const Resources *)
205 {
206         return new AsyncLoader(*this, io);
207 }
208
209 UInt64 Mesh::get_data_size() const
210 {
211         UInt64 size = 0;
212         if(vbuf)
213                 size += vbuf->get_size();
214         if(ibuf)
215                 size += ibuf->get_size();
216         return size;
217 }
218
219 void Mesh::unload()
220 {
221         vertices.clear();
222         vertices.use_buffer(0);
223         batches.clear();
224         delete vbuf;
225         delete ibuf;
226         defer_buffers = (vbuf || ibuf);
227         vbuf = 0;
228         ibuf = 0;
229 }
230
231
232 Mesh::Loader::Loader(Mesh &m):
233         DataFile::ObjectLoader<Mesh>(m)
234 {
235         add("batch",    &Loader::batch);
236         add("vertices", &Loader::vertices);
237         add("winding",  &Loader::winding);
238 }
239
240 void Mesh::Loader::vertices(const vector<VertexComponent> &c)
241 {
242         if(c.empty())
243                 throw invalid_argument("No vertex components");
244
245         VertexFormat fmt;
246         for(vector<VertexComponent>::const_iterator i=c.begin(); i!=c.end(); ++i)
247                 fmt = (fmt, *i);
248         obj.vertices.reset(fmt);
249         obj.dirty = true;
250         load_sub(obj.vertices);
251 }
252
253 void Mesh::Loader::batch(PrimitiveType p)
254 {
255         Batch btc(p);
256         load_sub(btc);
257         obj.add_batch(btc);
258 }
259
260 void Mesh::Loader::winding(FaceWinding w)
261 {
262         if(w==CLOCKWISE)
263                 obj.winding = &WindingTest::clockwise();
264         else if(w==COUNTERCLOCKWISE)
265                 obj.winding = &WindingTest::counterclockwise();
266 }
267
268
269 Mesh::AsyncLoader::AsyncLoader(Mesh &m, IO::Seekable &i):
270         mesh(m),
271         io(i),
272         vertex_updater(0),
273         index_updater(0),
274         phase(0)
275 {
276         // Make sure the extension is initialized in the rendering thread.
277         (bool)NV_primitive_restart;
278
279         mesh.disallow_rendering = true;
280         if(mesh.defer_buffers)
281                 mesh.create_buffers();
282 }
283
284 Mesh::AsyncLoader::~AsyncLoader()
285 {
286         mesh.disallow_rendering = false;
287         delete vertex_updater;
288         delete index_updater;
289 }
290
291 bool Mesh::AsyncLoader::needs_sync() const
292 {
293         return phase%2;
294 }
295
296 bool Mesh::AsyncLoader::process()
297 {
298         if(phase==0)
299         {
300                 // TODO use correct filename
301                 DataFile::Parser parser(io, "async");
302                 Loader loader(mesh);
303                 loader.load(parser);
304         }
305         else if(phase==1)
306         {
307                 vertex_updater = mesh.vertices.refresh_async();
308                 if(!mesh.batches.empty())
309                         index_updater = mesh.batches.front().refresh_async();
310         }
311         else if(phase==2)
312         {
313                 if(vertex_updater)
314                         vertex_updater->upload_data();
315                 if(index_updater)
316                         index_updater->upload_data();
317         }
318         else if(phase==3)
319         {
320                 delete vertex_updater;
321                 vertex_updater = 0;
322                 delete index_updater;
323                 index_updater = 0;
324         }
325
326         ++phase;
327         if(phase==1 && !mesh.vbuf && !mesh.ibuf)
328                 phase += 3;
329         return phase>3;
330 }
331
332 } // namespace GL
333 } // namespace Msp