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