+++ /dev/null
-#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