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