OpenSceneGraph/src/osg/CollectOccludersVisitor.cpp
2012-03-21 17:36:20 +00:00

260 lines
7.9 KiB
C++

/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
*
* This library is open source and may be redistributed and/or modified under
* the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
* (at your option) any later version. The full license is in LICENSE file
* included with this distribution, and on the openscenegraph.org website.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* OpenSceneGraph Public License for more details.
*/
#include <osg/CollectOccludersVisitor>
#include <osg/Transform>
#include <osg/Switch>
#include <osg/LOD>
#include <osg/OccluderNode>
#include <osg/Projection>
#include <algorithm>
using namespace osg;
CollectOccludersVisitor::CollectOccludersVisitor():
NodeVisitor(COLLECT_OCCLUDER_VISITOR,TRAVERSE_ACTIVE_CHILDREN)
{
setCullingMode(VIEW_FRUSTUM_CULLING|
NEAR_PLANE_CULLING|
FAR_PLANE_CULLING|
SMALL_FEATURE_CULLING);
_minimumShadowOccluderVolume = 0.005f;
_maximumNumberOfActiveOccluders = 10;
_createDrawables = false;
}
CollectOccludersVisitor::~CollectOccludersVisitor()
{
}
void CollectOccludersVisitor::reset()
{
CullStack::reset();
_occluderSet.clear();
}
float CollectOccludersVisitor::getDistanceToEyePoint(const Vec3& pos, bool withLODScale) const
{
if (withLODScale) return (pos-getEyeLocal()).length()*getLODScale();
else return (pos-getEyeLocal()).length();
}
float CollectOccludersVisitor::getDistanceToViewPoint(const Vec3& pos, bool withLODScale) const
{
if (withLODScale) return (pos-getViewPointLocal()).length()*getLODScale();
else return (pos-getViewPointLocal()).length();
}
float CollectOccludersVisitor::getDistanceFromEyePoint(const Vec3& pos, bool withLODScale) const
{
const Matrix& matrix = *_modelviewStack.back();
float dist = -(pos[0]*matrix(0,2)+pos[1]*matrix(1,2)+pos[2]*matrix(2,2)+matrix(3,2));
if (withLODScale) return dist*getLODScale();
else return dist*getLODScale();
}
void CollectOccludersVisitor::apply(osg::Node& node)
{
if (isCulled(node)) return;
// push the culling mode.
pushCurrentMask();
handle_cull_callbacks_and_traverse(node);
// pop the culling mode.
popCurrentMask();
}
void CollectOccludersVisitor::apply(osg::Transform& node)
{
if (isCulled(node)) return;
// push the culling mode.
pushCurrentMask();
ref_ptr<osg::RefMatrix> matrix = createOrReuseMatrix(*getModelViewMatrix());
node.computeLocalToWorldMatrix(*matrix,this);
pushModelViewMatrix(matrix.get(), node.getReferenceFrame());
handle_cull_callbacks_and_traverse(node);
popModelViewMatrix();
// pop the culling mode.
popCurrentMask();
}
void CollectOccludersVisitor::apply(osg::Projection& node)
{
if (isCulled(node)) return;
// push the culling mode.
pushCurrentMask();
ref_ptr<osg::RefMatrix> matrix = createOrReuseMatrix(node.getMatrix());
pushProjectionMatrix(matrix.get());
handle_cull_callbacks_and_traverse(node);
popProjectionMatrix();
// pop the culling mode.
popCurrentMask();
}
void CollectOccludersVisitor::apply(osg::Switch& node)
{
apply((Group&)node);
}
void CollectOccludersVisitor::apply(osg::LOD& node)
{
if (isCulled(node)) return;
// push the culling mode.
pushCurrentMask();
handle_cull_callbacks_and_traverse(node);
// pop the culling mode.
popCurrentMask();
}
void CollectOccludersVisitor::apply(osg::OccluderNode& node)
{
// need to check if occlusion node is in the occluder
// list, if so disable the appropriate ShadowOccluderVolume
disableAndPushOccludersCurrentMask(_nodePath);
if (isCulled(node))
{
popOccludersCurrentMask(_nodePath);
return;
}
// std::cout<<"CollectOccludersVisitor:: We have found an Occlusion node in frustum"<<&node<<std::endl;
// push the culling mode.
pushCurrentMask();
if (node.getOccluder())
{
// computeOccluder will check if the occluder is the view frustum,
// if it ins't then the it will return false, when in it will
// clip the occluder's polygons in clip space, then create occluder
// planes, all with their normals facing inward towards the volume,
// and then transform them back into projection space.
ShadowVolumeOccluder svo;
if (svo.computeOccluder(_nodePath, *node.getOccluder(), *this,_createDrawables))
{
if (svo.getVolume()>_minimumShadowOccluderVolume)
{
// need to test occluder against view frustum.
//std::cout << " adding in Occluder"<<std::endl;
_occluderSet.insert(svo);
}
else
{
//std::cout << " rejecting Occluder as its volume is too small "<<svo.getVolume()<<std::endl;
}
}
}
handle_cull_callbacks_and_traverse(node);
// pop the culling mode.
popCurrentMask();
// pop the current mask for the disabled occluder
popOccludersCurrentMask(_nodePath);
}
void CollectOccludersVisitor::removeOccludedOccluders()
{
if (_occluderSet.empty()) return;
ShadowVolumeOccluderSet::iterator occludeeItr=_occluderSet.begin();
// skip the first element as this can't be occluded by anything else.
occludeeItr++;
// step through the rest of the occluders, remove occluders which are themselves occluded.
for(;
occludeeItr!=_occluderSet.end();
++occludeeItr)
{
// search for any occluders that occlude the current occluder,
// we only need to test any occluder near the front of the set since
// you can't be occluder by something smaller than you.
ShadowVolumeOccluder& occludee = const_cast<ShadowVolumeOccluder&>(*occludeeItr);
ShadowVolumeOccluder::HoleList& holeList = occludee.getHoleList();
for(ShadowVolumeOccluderSet::iterator occluderItr=_occluderSet.begin();
occluderItr!=occludeeItr;
++occluderItr)
{
// cast away constness of the std::set element since ShadowVolumeOccluder::contains() is non const,
// and the std::set is a const, just for the invariance of the operator <!! Ahhhhh. oh well the below
// should be robust since contains won't change the getVolume which is used by the operator <. Honest, :-)
ShadowVolumeOccluder* occluder = const_cast<ShadowVolumeOccluder*>(&(*occluderItr));
if (occluder->contains(occludee.getOccluder().getReferenceVertexList()))
{
// erase occluder from set.
// take a copy of the iterator then rewind it one element so to prevent invalidating the occludeeItr.
ShadowVolumeOccluderSet::iterator eraseItr = occludeeItr--;
_occluderSet.erase(eraseItr);
break;
}
// now check all the holes in the occludee against the occluder,
// do so in reverse order so that the iterators remain valid.
for(ShadowVolumeOccluder::HoleList::reverse_iterator holeItr=holeList.rbegin();
holeItr!=holeList.rend();
)
{
if (occluder->contains((*holeItr).getReferenceVertexList()))
{
holeList.erase((++holeItr).base());
}
else
{
++holeItr;
}
}
}
}
if (_occluderSet.size()<=_maximumNumberOfActiveOccluders) return;
// move the iterator to the _maximumNumberOfActiveOccluders th occluder.
occludeeItr = _occluderSet.begin();
for(unsigned int i=0;i<_maximumNumberOfActiveOccluders;++i)
++occludeeItr;
// discard last occluders.
_occluderSet.erase(occludeeItr,_occluderSet.end());
}