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