260 lines
7.9 KiB
C++
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());
|
|
|
|
}
|