]> git.tdb.fi Git - libs/gl.git/blob - source/render/occludedscene.cpp
Guard against hitting the end of the array in get_gl_type
[libs/gl.git] / source / render / occludedscene.cpp
1 #include <algorithm>
2 #include <msp/gl/extensions/arb_occlusion_query.h>
3 #include <msp/gl/extensions/arb_occlusion_query2.h>
4 #include "camera.h"
5 #include "occludedscene.h"
6 #include "renderer.h"
7 #include "resources.h"
8 #include "sphere.h"
9
10 using namespace std;
11
12 namespace Msp {
13 namespace GL {
14
15 OccludedScene::OccludedScene(Resources &resources):
16         bounding_mesh(resources.get<Mesh>("_occluder.mesh")),
17         bounding_shader(resources.get<Program>("_occluder.glsl.shader")),
18         occluder_min_size(0.25f),
19         cache_dirty(false)
20 {
21         static Require req(ARB_occlusion_query);
22         static Require req2(ARB_occlusion_query2);
23 }
24
25 OccludedScene::~OccludedScene()
26 {
27         vector<unsigned> queries;
28         queries.reserve(occluded_cache.size());
29         for(OccludedArray::iterator i=occluded_cache.begin(); i!=occluded_cache.end(); ++i)
30                 queries.push_back(i->query);
31         glDeleteQueries(queries.size(), &queries[0]);
32 }
33
34 void OccludedScene::add(Renderable &r)
35 {
36         renderables.insert(&r);
37         cache_dirty = true;
38 }
39
40 void OccludedScene::remove(Renderable &r)
41 {
42         renderables.erase(&r);
43         cache_dirty = true;
44 }
45
46 void OccludedScene::populate_cache() const
47 {
48         if(!cache_dirty)
49                 return;
50
51         if(occluded_cache.size()<renderables.size())
52         {
53                 unsigned old_size = occluded_cache.size();
54                 occluded_cache.resize(renderables.size());
55                 vector<unsigned> new_queries(occluded_cache.size()-old_size);
56                 glGenQueries(new_queries.size(), &new_queries[0]);
57                 for(unsigned i=0; i<new_queries.size(); ++i)
58                         occluded_cache[old_size+i].query = new_queries[i];
59         }
60
61         OccludedArray::iterator j = occluded_cache.begin();
62         for(RenderableSet::iterator i=renderables.begin(); i!=renderables.end(); ++i, ++j)
63                 j->renderable = *i;
64         for(; j!=occluded_cache.end(); ++j)
65         {
66                 j->renderable = 0;
67                 j->in_frustum = false;
68         }
69
70         cache_dirty = false;
71 }
72
73 void OccludedScene::setup_frame(Renderer &renderer)
74 {
75         populate_cache();
76         for(OccludedArray::const_iterator i=occluded_cache.begin(); i!=occluded_cache.end(); ++i)
77                 i->renderable->setup_frame(renderer);
78 }
79
80 void OccludedScene::finish_frame()
81 {
82         for(OccludedArray::const_iterator i=occluded_cache.begin(); i!=occluded_cache.end(); ++i)
83                 i->renderable->finish_frame();
84 }
85
86 void OccludedScene::render(Renderer &renderer, const Tag &tag) const
87 {
88         if(renderables.empty())
89                 return;
90
91         populate_cache();
92
93         const Camera *camera = renderer.get_camera();
94         if(!camera)
95         {
96                 for(OccludedArray::const_iterator i=occluded_cache.begin(); i!=occluded_cache.end(); ++i)
97                         renderer.render(*i->renderable, tag);
98                 return;
99         }
100
101         const Vector3 &camera_pos = camera->get_position();
102         const Vector3 &look_dir = camera->get_look_direction();
103         float near_clip = camera->get_near_clip();
104         float frustum_h = tan(camera->get_field_of_view()/2.0f)*2.0f;
105
106         // Perform frustum culling and render any major occluders
107         bool use_frustum = setup_frustum(renderer);
108         for(OccludedArray::iterator i=occluded_cache.begin(); (i!=occluded_cache.end() && i->renderable); ++i)
109         {
110                 i->in_frustum = (!use_frustum || !frustum_cull(*i->renderable));
111                 if(!i->in_frustum)
112                         continue;
113
114                 const Matrix *matrix = i->renderable->get_matrix();
115                 i->bounding_sphere = i->renderable->get_bounding_sphere();
116                 if(matrix && i->bounding_sphere)
117                 {
118                         float depth = dot(*matrix*i->bounding_sphere->get_center()-camera_pos, look_dir);
119                         float size = i->bounding_sphere->get_radius()*2/max(depth, near_clip);
120                         i->occluder = (size>frustum_h*occluder_min_size);
121                 }
122                 else
123                         // If the size can't be calculated, treat it as occluder
124                         i->occluder = true;
125
126                 if(i->occluder)
127                         renderer.render(*i->renderable, tag);
128         }
129
130         // Move all objects within the frustum to the beginning of the array
131         for(OccludedArray::iterator i=occluded_cache.begin(), j=i+renderables.size()-1; i!=j; )
132         {
133                 if(i->in_frustum)
134                         ++i;
135                 else if(j->in_frustum)
136                         swap(*i, *j);
137                 else
138                         --j;
139         }
140
141         {
142                 Renderer::Push push(renderer);
143                 renderer.set_shader_program(&bounding_shader);
144
145                 glColorMask(false, false, false, false);
146                 glDepthMask(false);
147
148                 // Fire off the occlusion queries
149                 for(OccludedArray::const_iterator i=occluded_cache.begin(); (i!=occluded_cache.end() && i->in_frustum); ++i)
150                         if(!i->occluder)
151                         {
152                                 glBeginQuery(GL_ANY_SAMPLES_PASSED, i->query);
153                                 Renderer::Push push2(renderer);
154                                 renderer.transform(Matrix(*i->renderable->get_matrix())
155                                         .translate(i->bounding_sphere->get_center())
156                                         .scale(i->bounding_sphere->get_radius()));
157                                 bounding_mesh.draw(renderer);
158                                 glEndQuery(GL_ANY_SAMPLES_PASSED);
159                         }
160
161                 glColorMask(true, true, true, true);
162                 glDepthMask(true);
163         }
164
165         // Render anything that has a chance of being visible
166         for(OccludedArray::const_iterator i=occluded_cache.begin(); (i!=occluded_cache.end() && i->in_frustum); ++i)
167                 if(!i->occluder)
168                 {
169                         unsigned any_passed = 0;
170                         glGetQueryObjectuiv(i->query, GL_QUERY_RESULT, &any_passed);
171                         if(any_passed)
172                                 renderer.render(*i->renderable, tag);
173                 }
174 }
175
176
177 OccludedScene::OccludedRenderable::OccludedRenderable():
178         renderable(0),
179         bounding_sphere(0),
180         in_frustum(false),
181         occluder(false),
182         query(0)
183 { }
184
185 } // namespace GL
186 } // namespace Msp