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