]> git.tdb.fi Git - libs/gl.git/blobdiff - source/render/occludedscene.cpp
Rearrange soucre files into subdirectories
[libs/gl.git] / source / render / occludedscene.cpp
diff --git a/source/render/occludedscene.cpp b/source/render/occludedscene.cpp
new file mode 100644 (file)
index 0000000..143008c
--- /dev/null
@@ -0,0 +1,190 @@
+#include <algorithm>
+#include <msp/gl/extensions/arb_occlusion_query.h>
+#include <msp/gl/extensions/arb_occlusion_query2.h>
+#include "camera.h"
+#include "occludedscene.h"
+#include "renderer.h"
+#include "sphere.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+OccludedScene::OccludedScene():
+       bounding_mesh((VERTEX3, NORMAL3)),
+       bounding_shader("occluder.glsl"),
+       occluder_min_size(0.25f),
+       cache_dirty(false)
+{
+       static Require req(ARB_occlusion_query);
+       static Require req2(ARB_occlusion_query2);
+
+       /* Use a slightly larger radius to ensure that all parts of the renderable
+       fit inside the icosahedron */
+       IcoSphereBuilder(1.26f, 1).build(bounding_mesh);
+       bounding_mesh.set_winding(&WindingTest::counterclockwise());
+}
+
+OccludedScene::~OccludedScene()
+{
+       vector<unsigned> queries;
+       queries.reserve(occluded_cache.size());
+       for(OccludedArray::iterator i=occluded_cache.begin(); i!=occluded_cache.end(); ++i)
+               queries.push_back(i->query);
+       glDeleteQueries(queries.size(), &queries[0]);
+}
+
+void OccludedScene::add(Renderable &r)
+{
+       renderables.insert(&r);
+       cache_dirty = true;
+}
+
+void OccludedScene::remove(Renderable &r)
+{
+       renderables.erase(&r);
+       cache_dirty = true;
+}
+
+void OccludedScene::populate_cache() const
+{
+       if(!cache_dirty)
+               return;
+
+       if(occluded_cache.size()<renderables.size())
+       {
+               unsigned old_size = occluded_cache.size();
+               occluded_cache.resize(renderables.size());
+               vector<unsigned> new_queries(occluded_cache.size()-old_size);
+               glGenQueries(new_queries.size(), &new_queries[0]);
+               for(unsigned i=0; i<new_queries.size(); ++i)
+                       occluded_cache[old_size+i].query = new_queries[i];
+       }
+
+       OccludedArray::iterator j = occluded_cache.begin();
+       for(RenderableSet::iterator i=renderables.begin(); i!=renderables.end(); ++i, ++j)
+               j->renderable = *i;
+       for(; j!=occluded_cache.end(); ++j)
+       {
+               j->renderable = 0;
+               j->in_frustum = false;
+       }
+
+       cache_dirty = false;
+}
+
+void OccludedScene::setup_frame(Renderer &renderer)
+{
+       populate_cache();
+       for(OccludedArray::const_iterator i=occluded_cache.begin(); i!=occluded_cache.end(); ++i)
+               i->renderable->setup_frame(renderer);
+}
+
+void OccludedScene::finish_frame()
+{
+       for(OccludedArray::const_iterator i=occluded_cache.begin(); i!=occluded_cache.end(); ++i)
+               i->renderable->finish_frame();
+}
+
+void OccludedScene::render(Renderer &renderer, const Tag &tag) const
+{
+       if(renderables.empty())
+               return;
+
+       populate_cache();
+
+       const Camera *camera = renderer.get_camera();
+       if(!camera)
+       {
+               for(OccludedArray::const_iterator i=occluded_cache.begin(); i!=occluded_cache.end(); ++i)
+                       renderer.render(*i->renderable, tag);
+               return;
+       }
+
+       const Vector3 &camera_pos = camera->get_position();
+       const Vector3 &look_dir = camera->get_look_direction();
+       float near_clip = camera->get_near_clip();
+       float frustum_h = tan(camera->get_field_of_view()/2.0f)*2.0f;
+
+       // Perform frustum culling and render any major occluders
+       bool use_frustum = setup_frustum(renderer);
+       for(OccludedArray::iterator i=occluded_cache.begin(); (i!=occluded_cache.end() && i->renderable); ++i)
+       {
+               i->in_frustum = (!use_frustum || !frustum_cull(*i->renderable));
+               if(!i->in_frustum)
+                       continue;
+
+               const Matrix *matrix = i->renderable->get_matrix();
+               i->bounding_sphere = i->renderable->get_bounding_sphere();
+               if(matrix && i->bounding_sphere)
+               {
+                       float depth = dot(*matrix*i->bounding_sphere->get_center()-camera_pos, look_dir);
+                       float size = i->bounding_sphere->get_radius()*2/max(depth, near_clip);
+                       i->occluder = (size>frustum_h*occluder_min_size);
+               }
+               else
+                       // If the size can't be calculated, treat it as occluder
+                       i->occluder = true;
+
+               if(i->occluder)
+                       renderer.render(*i->renderable, tag);
+       }
+
+       // Move all objects within the frustum to the beginning of the array
+       for(OccludedArray::iterator i=occluded_cache.begin(), j=i+renderables.size()-1; i!=j; )
+       {
+               if(i->in_frustum)
+                       ++i;
+               else if(j->in_frustum)
+                       swap(*i, *j);
+               else
+                       --j;
+       }
+
+       {
+               Renderer::Push push(renderer);
+               renderer.set_shader_program(&bounding_shader);
+
+               glColorMask(false, false, false, false);
+               glDepthMask(false);
+
+               // Fire off the occlusion queries
+               for(OccludedArray::const_iterator i=occluded_cache.begin(); (i!=occluded_cache.end() && i->in_frustum); ++i)
+                       if(!i->occluder)
+                       {
+                               glBeginQuery(GL_ANY_SAMPLES_PASSED, i->query);
+                               Renderer::Push push2(renderer);
+                               renderer.transform(Matrix(*i->renderable->get_matrix())
+                                       .translate(i->bounding_sphere->get_center())
+                                       .scale(i->bounding_sphere->get_radius()));
+                               bounding_mesh.draw(renderer);
+                               glEndQuery(GL_ANY_SAMPLES_PASSED);
+                       }
+
+               glColorMask(true, true, true, true);
+               glDepthMask(true);
+       }
+
+       // Render anything that has a chance of being visible
+       for(OccludedArray::const_iterator i=occluded_cache.begin(); (i!=occluded_cache.end() && i->in_frustum); ++i)
+               if(!i->occluder)
+               {
+                       unsigned any_passed = 0;
+                       glGetQueryObjectuiv(i->query, GL_QUERY_RESULT, &any_passed);
+                       if(any_passed)
+                               renderer.render(*i->renderable, tag);
+               }
+}
+
+
+OccludedScene::OccludedRenderable::OccludedRenderable():
+       renderable(0),
+       bounding_sphere(0),
+       in_frustum(false),
+       occluder(false),
+       query(0)
+{ }
+
+} // namespace GL
+} // namespace Msp