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