]> git.tdb.fi Git - libs/gl.git/blob - source/renderer.cpp
Set the appropriate flags on Renderer state changes
[libs/gl.git] / source / renderer.cpp
1 #include "batch.h"
2 #include "buffer.h"
3 #include "camera.h"
4 #include "clipping.h"
5 #include "error.h"
6 #include "lighting.h"
7 #include "material.h"
8 #include "mesh.h"
9 #include "program.h"
10 #include "programdata.h"
11 #include "renderable.h"
12 #include "renderer.h"
13 #include "texture.h"
14 #include "texturing.h"
15 #include "texunit.h"
16 #include "vertexarray.h"
17 #include "windingtest.h"
18
19 using namespace std;
20
21 namespace Msp {
22 namespace GL {
23
24 Renderer::Renderer(const Camera *c):
25         changed(0),
26         matrices_loaded(false),
27         shdata_applied(0),
28         camera(0),
29         state_stack(1)
30 {
31         state_stack.reserve(16);
32         shdata_stack.reserve(32);
33         state = &state_stack.back();
34
35         begin(c);
36 }
37
38 Renderer::~Renderer()
39 {
40         end();
41 }
42
43 void Renderer::begin(const Camera *c)
44 {
45         if(state_stack.size()>1)
46                 throw invalid_operation("Renderer::begin");
47
48         reset_state();
49         excluded.clear();
50         camera = c;
51
52         if(camera)
53         {
54                 state->modelview_matrix = camera->get_view_matrix();
55                 standard_shdata.uniform("projection_matrix", camera->get_projection_matrix());
56         }
57         else
58         {
59                 state->modelview_matrix = MatrixStack::modelview().top();
60                 standard_shdata.uniform("projection_matrix", MatrixStack::projection().top());
61         }
62         changed |= MATRIX|STANDARD_SHDATA;
63 }
64
65 void Renderer::set_matrix(const Matrix &matrix)
66 {
67         state->modelview_matrix = matrix;
68         changed |= MATRIX;
69 }
70
71 void Renderer::transform(const Matrix &matrix)
72 {
73         state->modelview_matrix *= matrix;
74         changed |= MATRIX;
75 }
76
77 void Renderer::set_texture(const Texture *t)
78 {
79         state->texture = t;
80         state->texturing = 0;
81 }
82
83 void Renderer::set_texturing(const Texturing *t)
84 {
85         state->texturing = t;
86         state->texture = 0;
87 }
88
89 unsigned Renderer::allocate_effect_texunit()
90 {
91         return --state->lowest_effect_texunit;
92 }
93
94 void Renderer::set_material(const Material *m)
95 {
96         state->material = m;
97         changed |= MATERIAL_SHDATA;
98 }
99
100 void Renderer::set_lighting(const Lighting *l)
101 {
102         state->lighting = l;
103         state->lighting_matrix = state->modelview_matrix;
104         if(l)
105         {
106                 l->update_shader_data(standard_shdata, state->lighting_matrix);
107                 changed |= STANDARD_SHDATA;
108         }
109         changed |= LEGACY_LIGHTING;
110 }
111
112 void Renderer::set_clipping(const Clipping *c)
113 {
114         state->clipping = c;
115         state->clipping_matrix = state->modelview_matrix;
116         if(c)
117         {
118                 c->update_shader_data(standard_shdata, state->clipping_matrix);
119                 changed |= STANDARD_SHDATA;
120         }
121         changed |= LEGACY_CLIPPING;
122 }
123
124 void Renderer::set_shader_program(const Program *p, const ProgramData *d)
125 {
126         state->shprog = p;
127         if(p && d)
128                 add_shader_data(*d);
129 }
130
131 void Renderer::add_shader_data(const ProgramData &d)
132 {
133         shdata_stack.push_back(&d);
134         state->shdata_count = shdata_stack.size();
135         changed |= SHADER_DATA;
136 }
137
138 void Renderer::set_mesh(const Mesh *m)
139 {
140         state->mesh = m;
141 }
142
143 void Renderer::set_winding_test(const WindingTest *w)
144 {
145         state->winding_test = w;
146 }
147
148 void Renderer::set_reverse_winding(bool r)
149 {
150         state->reverse_winding = r;
151 }
152
153 void Renderer::push_state()
154 {
155         state_stack.push_back(state_stack.back());
156         state = &state_stack.back();
157 }
158
159 void Renderer::pop_state()
160 {
161         if(state_stack.size()==1)
162                 throw stack_underflow("Renderer::pop_state");
163
164         const Lighting *old_lighting = state->lighting;
165         const Clipping *old_clipping = state->clipping;
166         state_stack.pop_back();
167         state = &state_stack.back();
168         if(shdata_stack.size()>state->shdata_count)
169         {
170                 shdata_stack.erase(shdata_stack.begin()+state->shdata_count, shdata_stack.end());
171                 changed |= SHADER_DATA;
172         }
173         shdata_applied = min<unsigned>(shdata_applied, shdata_stack.size());
174         changed |= MATRIX;
175         if(state->lighting!=old_lighting)
176         {
177                 if(state->lighting)
178                 {
179                         state->lighting->update_shader_data(standard_shdata, state->lighting_matrix);
180                         changed |= STANDARD_SHDATA;
181                 }
182                 changed |= LEGACY_LIGHTING;
183         }
184         if(state->clipping!=old_clipping)
185         {
186                 if(state->clipping)
187                 {
188                         state->clipping->update_shader_data(standard_shdata, state->clipping_matrix);
189                         changed |= STANDARD_SHDATA;
190                 }
191                 changed |= LEGACY_CLIPPING;
192         }
193 }
194
195 void Renderer::escape()
196 {
197         apply_state();
198         Buffer::unbind_from(ELEMENT_ARRAY_BUFFER);
199         matrices_loaded = false;
200 }
201
202 void Renderer::end()
203 {
204         if(state_stack.size()>1)
205                 throw invalid_operation("Renderer::end");
206
207         reset_state();
208
209         Mesh::unbind();
210         Texturing::unbind();
211         Texture::unbind_from(0);
212         Material::unbind();
213         Lighting::unbind();
214         Clipping::unbind();
215         Program::unbind();
216         Buffer::unbind_from(ELEMENT_ARRAY_BUFFER);
217         WindingTest::unbind();
218 }
219
220 void Renderer::exclude(const Renderable &renderable)
221 {
222         excluded.insert(&renderable);
223 }
224
225 void Renderer::include(const Renderable &renderable)
226 {
227         excluded.erase(&renderable);
228 }
229
230 void Renderer::render(const Renderable &renderable, const Tag &tag)
231 {
232         if(!excluded.count(&renderable))
233                 renderable.render(*this, tag);
234 }
235
236 void Renderer::draw(const Batch &batch)
237 {
238         apply_state();
239
240         bool legacy_bindings = (!state->shprog || state->shprog->uses_legacy_variables());
241         if(state->mesh && legacy_bindings)
242         {
243                 if(const Buffer *ibuf = state->mesh->get_index_buffer())
244                         ibuf->bind_to(ELEMENT_ARRAY_BUFFER);
245                 else
246                         Buffer::unbind_from(ELEMENT_ARRAY_BUFFER);
247         }
248
249         batch.draw();
250 }
251
252 void Renderer::apply_state()
253 {
254         /* We (mostly) let the objects themselves figure out if the binding has
255         changed */
256
257         bool legacy_bindings = (!state->shprog || state->shprog->uses_legacy_variables());
258
259         if(state->texturing)
260                 state->texturing->bind();
261         else
262         {
263                 Texturing::unbind();
264                 if(state->texture)
265                         state->texture->bind_to(0);
266                 else
267                         Texture::unbind_from(0);
268         }
269
270         if(legacy_bindings)
271         {
272                 if(state->material)
273                         state->material->bind();
274                 else
275                         Material::unbind();
276
277                 if(changed&LEGACY_LIGHTING)
278                 {
279                         if(state->lighting)
280                         {
281                                 MatrixStack::modelview() = state->lighting_matrix;
282                                 state->lighting->bind();
283                                 changed = (changed&~LEGACY_LIGHTING)|LEGACY_MATRIX;
284                         }
285                         else
286                                 Lighting::unbind();
287                 }
288         }
289
290         if(state->clipping)
291         {
292                 if(legacy_bindings)
293                 {
294                         if(changed&LEGACY_CLIPPING)
295                         {
296                                 MatrixStack::modelview() = state->clipping_matrix;
297                                 state->clipping->bind(true);
298                                 changed = (changed&~LEGACY_CLIPPING)|LEGACY_MATRIX;
299                         }
300                 }
301                 else
302                         state->clipping->bind(false);
303         }
304         else
305                 Clipping::unbind();
306
307         if(state->shprog)
308         {
309                 bool shprog_changed = (state->shprog!=Program::current());
310                 state->shprog->bind();
311
312                 if(!legacy_bindings)
313                 {
314                         if(changed&MODERN_MATRIX)
315                         {
316                                 standard_shdata.uniform("eye_obj_matrix", state->modelview_matrix);
317                                 LinAl::SquareMatrix<float, 3> nm = state->modelview_matrix.block<3, 3>(0, 0);
318                                 nm = transpose(invert(nm));
319                                 standard_shdata.uniform_matrix3("eye_obj_normal_matrix", &nm(0, 0));
320                                 changed = (changed&~MODERN_MATRIX)|STANDARD_SHDATA;
321                         }
322
323                         if(state->material && (changed&MATERIAL_SHDATA))
324                         {
325                                 state->material->get_shader_data().apply();
326                                 changed &= ~MATERIAL_SHDATA;
327                         }
328
329                         if(changed&STANDARD_SHDATA)
330                         {
331                                 standard_shdata.apply();
332                                 changed &= ~STANDARD_SHDATA;
333                         }
334                 }
335
336                 if((changed&SHADER_DATA) || shprog_changed)
337                 {
338                         vector<const ProgramData *>::const_iterator i = shdata_stack.begin();
339                         if(!shprog_changed)
340                                 i += shdata_applied;
341                         for(; i!=shdata_stack.end(); ++i)
342                                 (*i)->apply();
343                         changed &= ~SHADER_DATA;
344                         shdata_applied = shdata_stack.size();
345                 }
346         }
347         else
348                 Program::unbind();
349
350         if(state->mesh)
351         {
352                 if(legacy_bindings)
353                 {
354                         Mesh::unbind();
355                         state->mesh->get_vertices().apply();
356                 }
357                 else
358                         state->mesh->bind();
359         }
360         else
361                 Mesh::unbind();
362
363         if(state->winding_test)
364         {
365                 if(state->reverse_winding)
366                         state->winding_test->get_reverse().bind();
367                 else
368                         state->winding_test->bind();
369         }
370         else
371                 WindingTest::unbind();
372
373         if(legacy_bindings)
374         {
375                 if(!matrices_loaded)
376                 {
377                         MatrixStack::modelview().push();
378                         if(camera)
379                         {
380                                 MatrixStack::projection().push();
381                                 camera->apply();
382                         }
383                         matrices_loaded = true;
384                 }
385
386                 if(changed&LEGACY_MATRIX)
387                 {
388                         MatrixStack::modelview() = state->modelview_matrix;
389                         changed &= ~LEGACY_MATRIX;
390                 }
391         }
392 }
393
394 void Renderer::reset_state()
395 {
396         if(!matrices_loaded)
397                 return;
398
399         if(camera)
400                 MatrixStack::projection().pop();
401         MatrixStack::modelview().pop();
402         matrices_loaded = false;
403         changed |= MATRIX;
404
405         *state = State();
406 }
407
408
409 Renderer::State::State():
410         texture(0),
411         texturing(0),
412         lowest_effect_texunit(TexUnit::get_n_units()),
413         material(0),
414         lighting(0),
415         clipping(0),
416         shprog(0),
417         shdata_count(0),
418         mesh(0),
419         winding_test(0),
420         reverse_winding(false)
421 { }
422
423 } // namespace GL
424 } // namespace Msp