]> git.tdb.fi Git - libs/gl.git/blob - source/renderer.cpp
Remove bind mechanics from Mesh and VertexArray
[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 "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 "vertexsetup.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         default_camera(c),
26         changed(0),
27         state_stack(1)
28 {
29         state_stack.reserve(16);
30         shdata_stack.reserve(32);
31         state = &state_stack.back();
32
33         if(c)
34                 set_camera(*c);
35         else
36         {
37                 standard_shdata.uniform("projection_matrix", Matrix());
38                 standard_shdata.uniform("eye_world_matrix", Matrix());
39         }
40 }
41
42 Renderer::~Renderer()
43 {
44         end();
45 }
46
47 void Renderer::begin(const Camera *c)
48 {
49         end();
50         if(c)
51                 set_camera(*c);
52 }
53
54 void Renderer::set_camera(const Camera &c)
55 {
56         state->camera = &c;
57         standard_shdata.uniform("projection_matrix", state->camera->get_projection_matrix());
58         standard_shdata.uniform("eye_world_matrix", state->camera->get_view_matrix());
59         changed |= STANDARD_SHDATA;
60         set_matrix(state->camera->get_view_matrix());
61 }
62
63 void Renderer::set_matrix(const Matrix &matrix)
64 {
65         state->modelview_matrix = matrix;
66         changed |= MATRIX;
67 }
68
69 void Renderer::transform(const Matrix &matrix)
70 {
71         state->modelview_matrix *= matrix;
72         changed |= MATRIX;
73 }
74
75 void Renderer::set_texture(const Texture *t)
76 {
77         state->texture = t;
78         state->texturing = 0;
79 }
80
81 void Renderer::set_texturing(const Texturing *t)
82 {
83         state->texturing = t;
84         state->texture = 0;
85 }
86
87 unsigned Renderer::allocate_effect_texunit()
88 {
89         return --state->lowest_effect_texunit;
90 }
91
92 void Renderer::set_material(const Material *m)
93 {
94         state->material = m;
95         changed |= MATERIAL_SHDATA;
96 }
97
98 void Renderer::set_lighting(const Lighting *l)
99 {
100         state->lighting = l;
101         state->lighting_matrix = state->modelview_matrix;
102         if(l)
103         {
104                 l->update_shader_data(standard_shdata, state->lighting_matrix);
105                 changed |= STANDARD_SHDATA;
106         }
107 }
108
109 void Renderer::set_clipping(const Clipping *c)
110 {
111         state->clipping = c;
112         state->clipping_matrix = state->modelview_matrix;
113         if(c)
114         {
115                 c->update_shader_data(standard_shdata, state->clipping_matrix);
116                 changed |= STANDARD_SHDATA;
117         }
118 }
119
120 void Renderer::set_shader_program(const Program *p, const ProgramData *d)
121 {
122         state->shprog = p;
123         if(p && d)
124                 add_shader_data(*d);
125 }
126
127 void Renderer::add_shader_data(const ProgramData &d)
128 {
129         if(state->shdata_count<shdata_stack.size() && shdata_stack[state->shdata_count]==&d)
130                 ++state->shdata_count;
131         else
132         {
133                 flush_shader_data();
134                 shdata_stack.push_back(&d);
135                 state->shdata_count = shdata_stack.size();
136                 changed |= SHADER_DATA;
137         }
138 }
139
140 void Renderer::flush_shader_data()
141 {
142         if(shdata_stack.size()>state->shdata_count)
143                 shdata_stack.erase(shdata_stack.begin()+state->shdata_count, shdata_stack.end());
144 }
145
146 void Renderer::set_vertex_setup(const VertexSetup *vs)
147 {
148         state->vertex_setup = vs;
149 }
150
151 void Renderer::set_winding_test(const WindingTest *w)
152 {
153         state->winding_test = w;
154 }
155
156 void Renderer::set_reverse_winding(bool r)
157 {
158         state->reverse_winding = r;
159 }
160
161 void Renderer::set_object_lod_bias(unsigned b)
162 {
163         state->object_lod_bias = b;
164 }
165
166 void Renderer::push_state()
167 {
168         state_stack.push_back(state_stack.back());
169         state = &state_stack.back();
170 }
171
172 void Renderer::pop_state()
173 {
174         if(state_stack.size()==1)
175                 throw stack_underflow("Renderer::pop_state");
176
177         const Camera *old_camera = state->camera;
178         const Lighting *old_lighting = state->lighting;
179         const Clipping *old_clipping = state->clipping;
180         state_stack.pop_back();
181         state = &state_stack.back();
182         changed |= MATRIX;
183         bool camera_changed = (state->camera!=old_camera);
184         if(camera_changed)
185         {
186                 if(state->camera)
187                 {
188                         standard_shdata.uniform("projection_matrix", state->camera->get_projection_matrix());
189                         standard_shdata.uniform("eye_world_matrix", state->camera->get_view_matrix());
190                 }
191                 else
192                 {
193                         standard_shdata.uniform("projection_matrix", Matrix());
194                         standard_shdata.uniform("eye_world_matrix", Matrix());
195                 }
196                 changed |= STANDARD_SHDATA;
197         }
198         /* This actually should compare the relevant matrices rather than check for
199         camera, but in practice lighting and clipping is set right after the camera
200         and a boolean check is much faster than matrix comparison. */
201         if(state->lighting!=old_lighting || camera_changed)
202         {
203                 if(state->lighting)
204                 {
205                         state->lighting->update_shader_data(standard_shdata, state->lighting_matrix);
206                         changed |= STANDARD_SHDATA;
207                 }
208         }
209         if(state->clipping!=old_clipping || camera_changed)
210         {
211                 if(state->clipping)
212                 {
213                         state->clipping->update_shader_data(standard_shdata, state->clipping_matrix);
214                         changed |= STANDARD_SHDATA;
215                 }
216         }
217 }
218
219 void Renderer::end()
220 {
221         if(state_stack.size()>1)
222                 throw invalid_operation("Renderer::end");
223
224         *state = State();
225         if(default_camera)
226                 set_camera(*default_camera);
227         else
228                 standard_shdata.uniform("projection_matrix", Matrix());
229         shdata_stack.clear();
230         excluded.clear();
231
232         Texturing::unbind();
233         Texture::unbind_from(0);
234         Clipping::unbind();
235         Program::unbind();
236         VertexSetup::unbind();
237         Buffer::unbind_from(ELEMENT_ARRAY_BUFFER);
238         WindingTest::unbind();
239 }
240
241 void Renderer::exclude(const Renderable &renderable)
242 {
243         excluded.insert(&renderable);
244 }
245
246 void Renderer::include(const Renderable &renderable)
247 {
248         excluded.erase(&renderable);
249 }
250
251 void Renderer::render(const Renderable &renderable, const Tag &tag)
252 {
253         if(!excluded.count(&renderable))
254                 renderable.render(*this, tag);
255 }
256
257 void Renderer::draw(const Batch &batch)
258 {
259         apply_state();
260
261         batch.draw();
262 }
263
264 void Renderer::draw_instanced(const Batch &batch, unsigned count)
265 {
266         apply_state();
267
268         batch.draw_instanced(count);
269 }
270
271 void Renderer::apply_state()
272 {
273         /* We (mostly) let the objects themselves figure out if the binding has
274         changed */
275
276         if(state->texturing)
277                 state->texturing->bind();
278         else
279         {
280                 Texturing::unbind();
281                 if(state->texture)
282                         state->texture->bind_to(0);
283                 else
284                         Texture::unbind_from(0);
285         }
286
287         if(state->clipping)
288                 state->clipping->bind();
289         else
290                 Clipping::unbind();
291
292         if(state->shprog)
293         {
294                 bool shprog_changed = (state->shprog!=Program::current());
295                 state->shprog->bind();
296
297                 if(changed&MATRIX)
298                 {
299                         standard_shdata.uniform("eye_obj_matrix", state->modelview_matrix);
300                         LinAl::SquareMatrix<float, 3> nm = state->modelview_matrix.block<3, 3>(0, 0);
301                         nm = transpose(invert(nm));
302                         standard_shdata.uniform_matrix3("eye_obj_normal_matrix", &nm(0, 0));
303                         changed = (changed&~MATRIX)|STANDARD_SHDATA;
304                 }
305
306                 if(state->material && ((changed&MATERIAL_SHDATA) || shprog_changed))
307                 {
308                         state->material->get_shader_data().apply();
309                         changed &= ~MATERIAL_SHDATA;
310                 }
311
312                 if((changed&STANDARD_SHDATA) || shprog_changed)
313                 {
314                         standard_shdata.apply();
315                         changed &= ~STANDARD_SHDATA;
316                 }
317
318                 bool extra_shdata = (shdata_stack.size()>state->shdata_count);
319
320                 if((changed&SHADER_DATA) || shprog_changed || extra_shdata)
321                 {
322                         if(extra_shdata)
323                                 shdata_stack.erase(shdata_stack.begin()+state->shdata_count, shdata_stack.end());
324                         for(vector<const ProgramData *>::const_iterator i=shdata_stack.begin(); i!=shdata_stack.end(); ++i)
325                                 (*i)->apply();
326                         changed &= ~SHADER_DATA;
327                 }
328         }
329         else
330                 Program::unbind();
331
332         if(state->vertex_setup)
333                 state->vertex_setup->bind();
334         else
335                 VertexSetup::unbind();
336
337         if(state->winding_test)
338         {
339                 if(state->reverse_winding)
340                         state->winding_test->get_reverse().bind();
341                 else
342                         state->winding_test->bind();
343         }
344         else
345                 WindingTest::unbind();
346 }
347
348
349 Renderer::State::State():
350         camera(0),
351         texture(0),
352         texturing(0),
353         lowest_effect_texunit(TexUnit::get_n_units()),
354         material(0),
355         lighting(0),
356         clipping(0),
357         shprog(0),
358         shdata_count(0),
359         vertex_setup(0),
360         winding_test(0),
361         reverse_winding(false),
362         object_lod_bias(0)
363 { }
364
365 } // namespace GL
366 } // namespace Msp