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