]> git.tdb.fi Git - libs/gl.git/blob - source/occludedscene.cpp
Animate only those components which are present in Transforms
[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(Renderable &r)
40 {
41         renderables.insert(&r);
42         cache_dirty = true;
43 }
44
45 void OccludedScene::remove(Renderable &r)
46 {
47         renderables.erase(&r);
48         cache_dirty = true;
49 }
50
51 void OccludedScene::populate_cache() const
52 {
53         if(!cache_dirty)
54                 return;
55
56         if(occluded_cache.size()<renderables.size())
57         {
58                 unsigned old_size = occluded_cache.size();
59                 occluded_cache.resize(renderables.size());
60                 vector<unsigned> new_queries(occluded_cache.size()-old_size);
61                 glGenQueries(new_queries.size(), &new_queries[0]);
62                 for(unsigned i=0; i<new_queries.size(); ++i)
63                         occluded_cache[old_size+i].query = new_queries[i];
64         }
65
66         OccludedArray::iterator j = occluded_cache.begin();
67         for(RenderableSet::iterator i=renderables.begin(); i!=renderables.end(); ++i, ++j)
68                 j->renderable = *i;
69         for(; j!=occluded_cache.end(); ++j)
70         {
71                 j->renderable = 0;
72                 j->in_frustum = false;
73         }
74
75         cache_dirty = false;
76 }
77
78 void OccludedScene::setup_frame(Renderer &renderer)
79 {
80         populate_cache();
81         for(OccludedArray::const_iterator i=occluded_cache.begin(); i!=occluded_cache.end(); ++i)
82                 i->renderable->setup_frame(renderer);
83 }
84
85 void OccludedScene::finish_frame()
86 {
87         for(OccludedArray::const_iterator i=occluded_cache.begin(); i!=occluded_cache.end(); ++i)
88                 i->renderable->finish_frame();
89 }
90
91 void OccludedScene::render(Renderer &renderer, const Tag &tag) const
92 {
93         if(renderables.empty())
94                 return;
95
96         populate_cache();
97
98         const Camera *camera = renderer.get_camera();
99         if(!camera)
100         {
101                 for(OccludedArray::const_iterator i=occluded_cache.begin(); i!=occluded_cache.end(); ++i)
102                         renderer.render(*i->renderable, tag);
103                 return;
104         }
105
106         const Vector3 &camera_pos = camera->get_position();
107         const Vector3 &look_dir = camera->get_look_direction();
108         float near_clip = camera->get_near_clip();
109         float frustum_h = tan(camera->get_field_of_view()/2.0f)*2.0f;
110
111         // Perform frustum culling and render any major occluders
112         bool use_frustum = setup_frustum(renderer);
113         for(OccludedArray::iterator i=occluded_cache.begin(); (i!=occluded_cache.end() && i->renderable); ++i)
114         {
115                 i->in_frustum = (!use_frustum || !frustum_cull(*i->renderable));
116                 if(!i->in_frustum)
117                         continue;
118
119                 const Matrix *matrix = i->renderable->get_matrix();
120                 i->bounding_sphere = i->renderable->get_bounding_sphere();
121                 if(matrix && i->bounding_sphere)
122                 {
123                         float depth = dot(*matrix*i->bounding_sphere->get_center()-camera_pos, look_dir);
124                         float size = i->bounding_sphere->get_radius()*2/max(depth, near_clip);
125                         i->occluder = (size>frustum_h*occluder_min_size);
126                 }
127                 else
128                         // If the size can't be calculated, treat it as occluder
129                         i->occluder = true;
130
131                 if(i->occluder)
132                         renderer.render(*i->renderable, tag);
133         }
134
135         // Move all objects within the frustum to the beginning of the array
136         for(OccludedArray::iterator i=occluded_cache.begin(), j=i+renderables.size()-1; i!=j; )
137         {
138                 if(i->in_frustum)
139                         ++i;
140                 else if(j->in_frustum)
141                         swap(*i, *j);
142                 else
143                         --j;
144         }
145
146         {
147                 Renderer::Push push(renderer);
148                 renderer.set_shader_program(&bounding_shader);
149
150                 glColorMask(false, false, false, false);
151                 glDepthMask(false);
152
153                 // Fire off the occlusion queries
154                 for(OccludedArray::const_iterator i=occluded_cache.begin(); (i!=occluded_cache.end() && i->in_frustum); ++i)
155                         if(!i->occluder)
156                         {
157                                 glBeginQuery(GL_ANY_SAMPLES_PASSED, i->query);
158                                 Renderer::Push push2(renderer);
159                                 renderer.transform(Matrix(*i->renderable->get_matrix())
160                                         .translate(i->bounding_sphere->get_center())
161                                         .scale(i->bounding_sphere->get_radius()));
162                                 bounding_mesh.draw(renderer);
163                                 glEndQuery(GL_ANY_SAMPLES_PASSED);
164                         }
165
166                 glColorMask(true, true, true, true);
167                 glDepthMask(true);
168         }
169
170         // Render anything that has a chance of being visible
171         for(OccludedArray::const_iterator i=occluded_cache.begin(); (i!=occluded_cache.end() && i->in_frustum); ++i)
172                 if(!i->occluder)
173                 {
174                         unsigned any_passed = 0;
175                         glGetQueryObjectuiv(i->query, GL_QUERY_RESULT, &any_passed);
176                         if(any_passed)
177                                 renderer.render(*i->renderable, tag);
178                 }
179 }
180
181
182 OccludedScene::OccludedRenderable::OccludedRenderable():
183         renderable(0),
184         bounding_sphere(0),
185         in_frustum(false),
186         occluder(false),
187         query(0)
188 { }
189
190 } // namespace GL
191 } // namespace Msp