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