Files
OpenSceneGraph/src/osgUtil/CullVisitor.cpp
Robert Osfield 5cb66efc86 Moved the testing and setting up of FBO/Pbuffer extension from CullVisitor to
support into RenderStage to allow better fallback implementations.
2005-10-05 16:21:51 +00:00

1260 lines
39 KiB
C++

/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2005 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/Transform>
#include <osg/Projection>
#include <osg/Geode>
#include <osg/LOD>
#include <osg/Billboard>
#include <osg/LightSource>
#include <osg/ClipNode>
#include <osg/TexGenNode>
#include <osg/OccluderNode>
#include <osg/Notify>
#include <osg/TexEnv>
#include <osg/AlphaFunc>
#include <osg/LineSegment>
#include <osg/TriangleFunctor>
#include <osg/Geometry>
#include <osg/io_utils>
#include <osgUtil/CullVisitor>
#include <float.h>
#include <algorithm>
#include <osg/Timer>
using namespace osg;
using namespace osgUtil;
inline float MAX_F(float a, float b)
{ return a>b?a:b; }
inline int EQUAL_F(float a, float b)
{ return a == b || fabsf(a-b) <= MAX_F(fabsf(a),fabsf(b))*1e-3f; }
class PrintVisitor : public NodeVisitor
{
public:
PrintVisitor(std::ostream& out):
NodeVisitor(NodeVisitor::TRAVERSE_ALL_CHILDREN),
_out(out)
{
_indent = 0;
_step = 4;
}
inline void moveIn() { _indent += _step; }
inline void moveOut() { _indent -= _step; }
inline void writeIndent()
{
for(int i=0;i<_indent;++i) _out << " ";
}
virtual void apply(Node& node)
{
moveIn();
writeIndent(); _out << node.className() <<std::endl;
traverse(node);
moveOut();
}
virtual void apply(Geode& node) { apply((Node&)node); }
virtual void apply(Billboard& node) { apply((Geode&)node); }
virtual void apply(LightSource& node) { apply((Group&)node); }
virtual void apply(ClipNode& node) { apply((Group&)node); }
virtual void apply(Group& node) { apply((Node&)node); }
virtual void apply(Transform& node) { apply((Group&)node); }
virtual void apply(Projection& node) { apply((Group&)node); }
virtual void apply(Switch& node) { apply((Group&)node); }
virtual void apply(LOD& node) { apply((Group&)node); }
protected:
std::ostream& _out;
int _indent;
int _step;
};
CullVisitor::CullVisitor():
NodeVisitor(CULL_VISITOR,TRAVERSE_ACTIVE_CHILDREN),
_currentRenderGraph(NULL),
_currentRenderBin(NULL),
_computed_znear(FLT_MAX),
_computed_zfar(-FLT_MAX),
_currentReuseRenderLeafIndex(0)
{
// _nearFarRatio = 0.000005f;
}
CullVisitor::~CullVisitor()
{
reset();
}
void CullVisitor::reset()
{
//
// first unref all referenced objects and then empty the containers.
//
CullStack::reset();
// reset the calculated near far planes.
_computed_znear = FLT_MAX;
_computed_zfar = -FLT_MAX;
osg::Vec3 lookVector(0.0,0.0,-1.0);
_bbCornerFar = (lookVector.x()>=0?1:0) |
(lookVector.y()>=0?2:0) |
(lookVector.z()>=0?4:0);
_bbCornerNear = (~_bbCornerFar)&7;
// reset the resuse lists.
_currentReuseMatrixIndex = 0;
_currentReuseRenderLeafIndex = 0;
for(RenderLeafList::iterator itr=_reuseRenderLeafList.begin();
itr!=_reuseRenderLeafList.end();
++itr)
{
(*itr)->reset();
}
_nearPlaneCandidateMap.clear();
}
float CullVisitor::getDistanceToEyePoint(const Vec3& pos, bool withLODScale) const
{
if (withLODScale) return (pos-getEyeLocal()).length()*getLODScale();
else return (pos-getEyeLocal()).length();
}
inline CullVisitor::value_type distance(const osg::Vec3& coord,const osg::Matrix& matrix)
{
//std::cout << "distance("<<coord<<", "<<matrix<<")"<<std::endl;
return -((CullVisitor::value_type)coord[0]*(CullVisitor::value_type)matrix(0,2)+(CullVisitor::value_type)coord[1]*(CullVisitor::value_type)matrix(1,2)+(CullVisitor::value_type)coord[2]*(CullVisitor::value_type)matrix(2,2)+matrix(3,2));
}
float CullVisitor::getDistanceFromEyePoint(const osg::Vec3& pos, bool withLODScale) const
{
const Matrix& matrix = *_modelviewStack.back();
float dist = distance(pos,matrix);
if (withLODScale) return dist*getLODScale();
else return dist;
}
void CullVisitor::popProjectionMatrix()
{
if (!_nearPlaneCandidateMap.empty())
{
// osg::Timer_t start_t = osg::Timer::instance()->tick();
// update near from defferred list of drawables
unsigned int numTests = 0;
for(DistanceMatrixDrawableMap::iterator itr=_nearPlaneCandidateMap.begin();
itr!=_nearPlaneCandidateMap.end() && itr->first<_computed_znear;
++itr)
{
++numTests;
// osg::notify(osg::WARN)<<"testing computeNearestPointInFrustum with d_near = "<<itr->first<<std::endl;
value_type d_near = computeNearestPointInFrustum(itr->second._matrix, itr->second._planes,*(itr->second._drawable));
if (d_near<_computed_znear)
{
_computed_znear = d_near;
// osg::notify(osg::WARN)<<"updating znear to "<<_computed_znear<<std::endl;
}
}
// osg::Timer_t end_t = osg::Timer::instance()->tick();
// osg::notify(osg::NOTICE)<<"Took "<<osg::Timer::instance()->delta_m(start_t,end_t)<<"ms to test "<<numTests<<" out of "<<_nearPlaneCandidateMap.size()<<std::endl;
}
if (_computeNearFar && _computed_zfar>=_computed_znear)
{
//osg::notify(osg::INFO)<<"clamping "<< "znear="<<_computed_znear << " zfar="<<_computed_zfar<<std::endl;
// adjust the projection matrix so that it encompases the local coords.
// so it doesn't cull them out.
osg::Matrix& projection = *_projectionStack.back();
value_type tmp_znear = _computed_znear;
value_type tmp_zfar = _computed_zfar;
clampProjectionMatrix(projection, tmp_znear, tmp_zfar);
}
else
{
//osg::notify(osg::INFO)<<"Not clamping "<< "znear="<<_computed_znear << " zfar="<<_computed_zfar<<std::endl;
}
CullStack::popProjectionMatrix();
}
template<class matrix_type, class value_type>
bool _clampProjectionMatrix(matrix_type& projection, double& znear, double& zfar, value_type nearFarRatio)
{
if (zfar>=znear)
{
double epsilon = 1e-6;
if (fabs(projection(0,3))<epsilon && fabs(projection(1,3))<epsilon && fabs(projection(2,3))<epsilon )
{
// osg::notify(osg::INFO) << "Orthographic matrix before clamping"<<projection<<std::endl;
value_type delta_span = (zfar-znear)*0.02;
if (delta_span<1.0) delta_span = 1.0;
value_type desired_znear = znear - delta_span;
value_type desired_zfar = zfar + delta_span;
// assign the clamped values back to the computed values.
znear = desired_znear;
zfar = desired_zfar;
projection(2,2)=-2.0f/(desired_zfar-desired_znear);
projection(3,2)=-(desired_zfar+desired_znear)/(desired_zfar-desired_znear);
// osg::notify(osg::INFO) << "Orthographic matrix after clamping "<<projection<<std::endl;
return true;
}
else
{
// osg::notify(osg::INFO) << "Persepective matrix before clamping"<<projection<<std::endl;
//std::cout << "_computed_znear"<<_computed_znear<<std::endl;
//std::cout << "_computed_zfar"<<_computed_zfar<<std::endl;
value_type zfarPushRatio = 1.02;
value_type znearPullRatio = 0.98;
//znearPullRatio = 0.99;
value_type desired_znear = znear * znearPullRatio;
value_type desired_zfar = zfar * zfarPushRatio;
// near plane clamping.
double min_near_plane = zfar*nearFarRatio;
if (desired_znear<min_near_plane) desired_znear=min_near_plane;
// assign the clamped values back to the computed values.
znear = desired_znear;
zfar = desired_zfar;
value_type trans_near_plane = (-desired_znear*projection(2,2)+projection(3,2))/(-desired_znear*projection(2,3)+projection(3,3));
value_type trans_far_plane = (-desired_zfar*projection(2,2)+projection(3,2))/(-desired_zfar*projection(2,3)+projection(3,3));
value_type ratio = fabs(2.0/(trans_near_plane-trans_far_plane));
value_type center = -(trans_near_plane+trans_far_plane)/2.0;
projection.postMult(osg::Matrix(1.0f,0.0f,0.0f,0.0f,
0.0f,1.0f,0.0f,0.0f,
0.0f,0.0f,ratio,0.0f,
0.0f,0.0f,center*ratio,1.0f));
// osg::notify(osg::INFO) << "Persepective matrix after clamping"<<projection<<std::endl;
return true;
}
}
return false;
}
bool CullVisitor::clampProjectionMatrixImplementation(osg::Matrixf& projection, double& znear, double& zfar) const
{
return _clampProjectionMatrix( projection, znear, zfar, _nearFarRatio );
}
bool CullVisitor::clampProjectionMatrixImplementation(osg::Matrixd& projection, double& znear, double& zfar) const
{
return _clampProjectionMatrix( projection, znear, zfar, _nearFarRatio );
}
struct ComputeNearestPointFunctor
{
ComputeNearestPointFunctor():
_planes(0) {}
void set(CullVisitor::value_type znear, const osg::Matrix& matrix, const osg::Polytope::PlaneList* planes)
{
_znear = znear;
_matrix = matrix;
_planes = planes;
}
typedef std::pair<float, osg::Vec3> DistancePoint;
typedef std::vector<DistancePoint> Polygon;
CullVisitor::value_type _znear;
osg::Matrix _matrix;
const osg::Polytope::PlaneList* _planes;
Polygon _polygonOriginal;
Polygon _polygonNew;
Polygon _pointCache;
inline void operator() ( const osg::Vec3 &v1, const osg::Vec3 &v2, const osg::Vec3 &v3, bool)
{
CullVisitor::value_type n1 = distance(v1,_matrix);
CullVisitor::value_type n2 = distance(v2,_matrix);
CullVisitor::value_type n3 = distance(v3,_matrix);
// check if triangle is total behind znear, if so disguard
if (n1 >= _znear &&
n2 >= _znear &&
n3 >= _znear)
{
//osg::notify(osg::NOTICE)<<"Triangle totally beyond znear"<<std::endl;
return;
}
if (n1 < 0.0 &&
n2 < 0.0 &&
n3 < 0.0)
{
// osg::notify(osg::NOTICE)<<"Triangle totally behind eye point"<<std::endl;
return;
}
// check which planes the points
osg::Polytope::ClippingMask selector_mask = 0x1;
osg::Polytope::ClippingMask active_mask = 0x0;
osg::Polytope::PlaneList::const_iterator pitr;
for(pitr = _planes->begin();
pitr != _planes->end();
++pitr)
{
const osg::Plane& plane = *pitr;
float d1 = plane.distance(v1);
float d2 = plane.distance(v2);
float d3 = plane.distance(v3);
unsigned int numOutside = ((d1<0.0)?1:0) + ((d2<0.0)?1:0) + ((d3<0.0)?1:0);
if (numOutside==3)
{
//osg::notify(osg::NOTICE)<<"Triangle totally outside frustum "<<d1<<"\t"<<d2<<"\t"<<d3<<std::endl;
return;
}
unsigned int numInside = ((d1>=0.0)?1:0) + ((d2>=0.0)?1:0) + ((d3>=0.0)?1:0);
if (numInside<3)
{
active_mask = active_mask | selector_mask;
}
//osg::notify(osg::NOTICE)<<"Triangle ok w.r.t plane "<<d1<<"\t"<<d2<<"\t"<<d3<<std::endl;
selector_mask <<= 1;
}
if (active_mask==0)
{
_znear = osg::minimum(_znear,n1);
_znear = osg::minimum(_znear,n2);
_znear = osg::minimum(_znear,n3);
// osg::notify(osg::NOTICE)<<"Triangle all inside frustum "<<n1<<"\t"<<n2<<"\t"<<n3<<" number of plane="<<_planes->size()<<std::endl;
return;
}
//return;
// numPartiallyInside>0) so we have a triangle cutting an frustum wall,
// this means that use brute force methods for deviding up triangle.
//osg::notify(osg::NOTICE)<<"Using brute force method of triangle cutting frustum walls"<<std::endl;
_polygonOriginal.clear();
_polygonOriginal.push_back(DistancePoint(0,v1));
_polygonOriginal.push_back(DistancePoint(0,v2));
_polygonOriginal.push_back(DistancePoint(0,v3));
selector_mask = 0x1;
for(pitr = _planes->begin();
pitr != _planes->end() && !_polygonOriginal.empty();
++pitr)
{
if (active_mask & selector_mask)
{
// polygon bisects plane so need to divide it up.
const osg::Plane& plane = *pitr;
_polygonNew.clear();
// assign the distance from the current plane.
for(Polygon::iterator polyItr = _polygonOriginal.begin();
polyItr != _polygonOriginal.end();
++polyItr)
{
polyItr->first = plane.distance(polyItr->second);
}
// create the new polygon by clamping against the
unsigned int psize = _polygonOriginal.size();
for(unsigned int ci = 0; ci < psize; ++ci)
{
unsigned int ni = (ci+1)%psize;
bool computeIntersection = false;
if (_polygonOriginal[ci].first>=0.0f)
{
_polygonNew.push_back(_polygonOriginal[ci]);
if (_polygonOriginal[ni].first<0.0f) computeIntersection = true;
}
else if (_polygonOriginal[ni].first>0.0f) computeIntersection = true;
if (computeIntersection)
{
// segment intersects with the plane, compute new position.
float r = _polygonOriginal[ci].first/(_polygonOriginal[ci].first-_polygonOriginal[ni].first);
_polygonNew.push_back(DistancePoint(0.0f,_polygonOriginal[ci].second*(1.0f-r) + _polygonOriginal[ni].second*r));
}
}
_polygonOriginal.swap(_polygonNew);
}
selector_mask <<= 1;
}
// now take the nearst points to the eye point.
for(Polygon::iterator polyItr = _polygonOriginal.begin();
polyItr != _polygonOriginal.end();
++polyItr)
{
CullVisitor::value_type dist = distance(polyItr->second,_matrix);
if (dist < _znear)
{
_znear = dist;
//osg::notify(osg::NOTICE)<<"Near plane updated "<<_znear<<std::endl;
}
}
}
};
CullVisitor::value_type CullVisitor::computeNearestPointInFrustum(const osg::Matrix& matrix, const osg::Polytope::PlaneList& planes,const osg::Drawable& drawable)
{
// osg::notify(osg::WARN)<<"CullVisitor::computeNearestPointInFrustum("<<getTraversalNumber()<<"\t"<<planes.size()<<std::endl;
osg::TriangleFunctor<ComputeNearestPointFunctor> cnpf;
cnpf.set(_computed_znear, matrix, &planes);
drawable.accept(cnpf);
return cnpf._znear;
}
bool CullVisitor::updateCalculatedNearFar(const osg::Matrix& matrix,const osg::BoundingBox& bb)
{
// efficient computation of near and far, only taking into account the nearest and furthest
// corners of the bounding box.
value_type d_near = distance(bb.corner(_bbCornerNear),matrix);
value_type d_far = distance(bb.corner(_bbCornerFar),matrix);
if (d_near>d_far)
{
std::swap(d_near,d_far);
if ( !EQUAL_F(d_near, d_far) )
{
osg::notify(osg::WARN)<<"Warning: CullVisitor::updateCalculatedNearFar(.) near>far in range calculation,"<< std::endl;
osg::notify(osg::WARN)<<" correcting by swapping values d_near="<<d_near<<" dfar="<<d_far<< std::endl;
}
}
if (d_far<0.0)
{
// whole object behind the eye point so disguard
return false;
}
if (d_near<_computed_znear) _computed_znear = d_near;
if (d_far>_computed_zfar) _computed_zfar = d_far;
return true;
}
bool CullVisitor::updateCalculatedNearFar(const osg::Matrix& matrix,const osg::Drawable& drawable, bool isBillboard)
{
const osg::BoundingBox& bb = drawable.getBound();
value_type d_near, d_far;
if (isBillboard)
{
#ifdef TIME_BILLBOARD_NEAR_FAR_CALCULATION
static unsigned int lastFrameNumber = getTraversalNumber();
static unsigned int numBillboards = 0;
static double elapsed_time = 0.0;
if (lastFrameNumber != getTraversalNumber())
{
osg::notify(osg::NOTICE)<<"Took "<<elapsed_time<<"ms to test "<<numBillboards<<" billboards"<<std::endl;
numBillboards = 0;
elapsed_time = 0.0;
lastFrameNumber = getTraversalNumber();
}
osg::Timer_t start_t = osg::Timer::instance()->tick();
#endif
osg::Vec3 lookVector(-matrix(0,2),-matrix(1,2),-matrix(2,2));
unsigned int bbCornerFar = (lookVector.x()>=0?1:0) +
(lookVector.y()>=0?2:0) +
(lookVector.z()>=0?4:0);
unsigned int bbCornerNear = (~bbCornerFar)&7;
d_near = distance(bb.corner(bbCornerNear),matrix);
d_far = distance(bb.corner(bbCornerFar),matrix);
osg::notify(osg::NOTICE).precision(15);
if (false)
{
osg::notify(osg::NOTICE)<<"TESTING Billboard near/far computation"<<std::endl;
// osg::notify(osg::WARN)<<"Checking corners of billboard "<<std::endl;
// deprecated brute force way, use all corners of the bounding box.
value_type nd_near, nd_far;
nd_near = nd_far = distance(bb.corner(0),matrix);
for(unsigned int i=0;i<8;++i)
{
value_type d = distance(bb.corner(i),matrix);
if (d<nd_near) nd_near = d;
if (d>nd_far) nd_far = d;
osg::notify(osg::NOTICE)<<"\ti="<<i<<"\td="<<d<<std::endl;
}
if (nd_near==d_near && nd_far==d_far)
{
osg::notify(osg::NOTICE)<<"\tBillboard near/far computation correct "<<std::endl;
}
else
{
osg::notify(osg::NOTICE)<<"\tBillboard near/far computation ERROR\n\t\t"<<d_near<<"\t"<<nd_near
<<"\n\t\t"<<d_far<<"\t"<<nd_far<<std::endl;
}
}
#ifdef TIME_BILLBOARD_NEAR_FAR_CALCULATION
osg::Timer_t end_t = osg::Timer::instance()->tick();
elapsed_time += osg::Timer::instance()->delta_m(start_t,end_t);
++numBillboards;
#endif
}
else
{
// efficient computation of near and far, only taking into account the nearest and furthest
// corners of the bounding box.
d_near = distance(bb.corner(_bbCornerNear),matrix);
d_far = distance(bb.corner(_bbCornerFar),matrix);
}
if (d_near>d_far)
{
std::swap(d_near,d_far);
if ( !EQUAL_F(d_near, d_far) )
{
osg::notify(osg::WARN)<<"Warning: CullVisitor::updateCalculatedNearFar(.) near>far in range calculation,"<< std::endl;
osg::notify(osg::WARN)<<" correcting by swapping values d_near="<<d_near<<" dfar="<<d_far<< std::endl;
}
}
if (d_far<0.0)
{
// whole object behind the eye point so disguard
return false;
}
if (d_near<_computed_znear)
{
if (_computeNearFar==COMPUTE_NEAR_FAR_USING_PRIMITIVES)
{
osg::Polytope& frustum = getCurrentCullingSet().getFrustum();
if (frustum.getCurrentMask() && frustum.getResultMask())
{
if (isBillboard)
{
// osg::notify(osg::WARN)<<"Adding billboard into deffered list"<<std::endl;
osg::Polytope transformed_frustum;
transformed_frustum.setAndTransformProvidingInverse(getProjectionCullingStack().back().getFrustum(),matrix);
// insert drawable into the deferred list of drawables which will be handled at the popProjectionMatrix().
_nearPlaneCandidateMap.insert(
DistanceMatrixDrawableMap::value_type(d_near,MatrixPlanesDrawables(matrix,&drawable,transformed_frustum)) );
}
else
{
// insert drawable into the deferred list of drawables which will be handled at the popProjectionMatrix().
_nearPlaneCandidateMap.insert(
DistanceMatrixDrawableMap::value_type(d_near,MatrixPlanesDrawables(matrix,&drawable,frustum)) );
}
// use the far point if its nearer than current znear as this is a conservative estimate of the znear
// while the final computation for this drawable is deferred.
if (d_far<_computed_znear)
{
if (d_far<0.0) osg::notify(osg::WARN)<<" 1) sett near with dnear="<<d_near<<" dfar="<<d_far<< std::endl;
else _computed_znear = d_far;
}
}
else
{
if (d_near<0.0) osg::notify(osg::WARN)<<" 2) sett near with d_near="<<d_near<< std::endl;
else _computed_znear = d_near;
}
}
else
{
//if (d_near<0.0) osg::notify(osg::WARN)<<" 3) set near with d_near="<<d_near<< std::endl;
_computed_znear = d_near;
}
}
if (d_far>_computed_zfar) _computed_zfar = d_far;
/*
// deprecated brute force way, use all corners of the bounding box.
updateCalculatedNearFar(bb.corner(0));
updateCalculatedNearFar(bb.corner(1));
updateCalculatedNearFar(bb.corner(2));
updateCalculatedNearFar(bb.corner(3));
updateCalculatedNearFar(bb.corner(4));
updateCalculatedNearFar(bb.corner(5));
updateCalculatedNearFar(bb.corner(6));
updateCalculatedNearFar(bb.corner(7));
*/
return true;
}
void CullVisitor::updateCalculatedNearFar(const osg::Vec3& pos)
{
float d;
if (!_modelviewStack.empty())
{
const osg::Matrix& matrix = *(_modelviewStack.back());
d = distance(pos,matrix);
}
else
{
d = -pos.z();
}
if (d<_computed_znear)
{
_computed_znear = d;
if (d<0.0) osg::notify(osg::WARN)<<"Alerting billboard ="<<d<< std::endl;
}
if (d>_computed_zfar) _computed_zfar = d;
}
void CullVisitor::apply(Node& node)
{
if (isCulled(node)) return;
// push the culling mode.
pushCurrentMask();
// push the node's state.
StateSet* node_state = node.getStateSet();
if (node_state) pushStateSet(node_state);
handle_cull_callbacks_and_traverse(node);
// pop the node's state off the geostate stack.
if (node_state) popStateSet();
// pop the culling mode.
popCurrentMask();
}
void CullVisitor::apply(Geode& node)
{
if (isCulled(node)) return;
// push the node's state.
StateSet* node_state = node.getStateSet();
if (node_state) pushStateSet(node_state);
// traverse any call callbacks and traverse any children.
handle_cull_callbacks_and_traverse(node);
RefMatrix& matrix = getModelViewMatrix();
for(unsigned int i=0;i<node.getNumDrawables();++i)
{
Drawable* drawable = node.getDrawable(i);
const BoundingBox &bb =drawable->getBound();
if( drawable->getCullCallback() )
{
if( drawable->getCullCallback()->cull( this, drawable, _state.valid()?_state.get():NULL ) == true )
continue;
}
//else
{
if (node.isCullingActive() && isCulled(bb)) continue;
}
if (_computeNearFar && bb.valid())
{
if (!updateCalculatedNearFar(matrix,*drawable,false)) continue;
}
// need to track how push/pops there are, so we can unravel the stack correctly.
unsigned int numPopStateSetRequired = 0;
// push the geoset's state on the geostate stack.
StateSet* stateset = drawable->getStateSet();
if (stateset)
{
++numPopStateSetRequired;
pushStateSet(stateset);
}
CullingSet& cs = getCurrentCullingSet();
if (!cs.getStateFrustumList().empty())
{
osg::CullingSet::StateFrustumList& sfl = cs.getStateFrustumList();
for(osg::CullingSet::StateFrustumList::iterator itr = sfl.begin();
itr != sfl.end();
++itr)
{
if (itr->second.contains(bb))
{
++numPopStateSetRequired;
pushStateSet(itr->first.get());
}
}
}
if (bb.valid()) addDrawableAndDepth(drawable,&matrix,distance(bb.center(),matrix));
else addDrawableAndDepth(drawable,&matrix,0.0f);
for(unsigned int i=0;i< numPopStateSetRequired; ++i)
{
popStateSet();
}
}
// pop the node's state off the geostate stack.
if (node_state) popStateSet();
}
void CullVisitor::apply(Billboard& node)
{
if (isCulled(node)) return;
// push the node's state.
StateSet* node_state = node.getStateSet();
if (node_state) pushStateSet(node_state);
// traverse any call callbacks and traverse any children.
handle_cull_callbacks_and_traverse(node);
const Vec3& eye_local = getEyeLocal();
const RefMatrix& modelview = getModelViewMatrix();
for(unsigned int i=0;i<node.getNumDrawables();++i)
{
const Vec3& pos = node.getPosition(i);
Drawable* drawable = node.getDrawable(i);
// need to modify isCulled to handle the billboard offset.
// if (isCulled(drawable->getBound())) continue;
RefMatrix* billboard_matrix = createOrReuseMatrix(modelview);
node.computeMatrix(*billboard_matrix,eye_local,pos);
if (_computeNearFar && drawable->getBound().valid()) updateCalculatedNearFar(*billboard_matrix,*drawable,true);
float d = distance(pos,modelview);
/*
if (_computeNearFar)
{
if (d<_computed_znear)
{
if (d<0.0) osg::notify(osg::WARN)<<"Alerting billboard handling ="<<d<< std::endl;
_computed_znear = d;
}
if (d>_computed_zfar) _computed_zfar = d;
}
*/
StateSet* stateset = drawable->getStateSet();
if (stateset) pushStateSet(stateset);
addDrawableAndDepth(drawable,billboard_matrix,d);
if (stateset) popStateSet();
}
// pop the node's state off the geostate stack.
if (node_state) popStateSet();
}
void CullVisitor::apply(LightSource& node)
{
// push the node's state.
StateSet* node_state = node.getStateSet();
if (node_state) pushStateSet(node_state);
StateAttribute* light = node.getLight();
if (light)
{
if (node.getReferenceFrame()==osg::LightSource::RELATIVE_RF)
{
RefMatrix& matrix = getModelViewMatrix();
addPositionedAttribute(&matrix,light);
}
else
{
// relative to absolute.
addPositionedAttribute(0,light);
}
}
handle_cull_callbacks_and_traverse(node);
// pop the node's state off the geostate stack.
if (node_state) popStateSet();
}
void CullVisitor::apply(ClipNode& node)
{
// push the node's state.
StateSet* node_state = node.getStateSet();
if (node_state) pushStateSet(node_state);
RefMatrix& matrix = getModelViewMatrix();
const ClipNode::ClipPlaneList& planes = node.getClipPlaneList();
for(ClipNode::ClipPlaneList::const_iterator itr=planes.begin();
itr!=planes.end();
++itr)
{
addPositionedAttribute(&matrix,itr->get());
}
handle_cull_callbacks_and_traverse(node);
// pop the node's state off the geostate stack.
if (node_state) popStateSet();
}
void CullVisitor::apply(TexGenNode& node)
{
// push the node's state.
StateSet* node_state = node.getStateSet();
if (node_state) pushStateSet(node_state);
if (node.getReferenceFrame()==osg::TexGenNode::RELATIVE_RF)
{
RefMatrix& matrix = getModelViewMatrix();
addPositionedTextureAttribute(node.getTextureUnit(), &matrix ,node.getTexGen());
}
else
{
addPositionedTextureAttribute(node.getTextureUnit(), 0 ,node.getTexGen());
}
handle_cull_callbacks_and_traverse(node);
// pop the node's state off the geostate stack.
if (node_state) popStateSet();
}
void CullVisitor::apply(Group& node)
{
if (isCulled(node)) return;
// push the culling mode.
pushCurrentMask();
// push the node's state.
StateSet* node_state = node.getStateSet();
if (node_state) pushStateSet(node_state);
handle_cull_callbacks_and_traverse(node);
// pop the node's state off the render graph stack.
if (node_state) popStateSet();
// pop the culling mode.
popCurrentMask();
}
void CullVisitor::apply(Transform& node)
{
if (isCulled(node)) return;
// push the culling mode.
pushCurrentMask();
// push the node's state.
StateSet* node_state = node.getStateSet();
if (node_state) pushStateSet(node_state);
ref_ptr<RefMatrix> matrix = createOrReuseMatrix(getModelViewMatrix());
node.computeLocalToWorldMatrix(*matrix,this);
pushModelViewMatrix(matrix.get());
handle_cull_callbacks_and_traverse(node);
popModelViewMatrix();
// pop the node's state off the render graph stack.
if (node_state) popStateSet();
// pop the culling mode.
popCurrentMask();
}
void CullVisitor::apply(Projection& node)
{
// push the culling mode.
pushCurrentMask();
// push the node's state.
StateSet* node_state = node.getStateSet();
if (node_state) pushStateSet(node_state);
// record previous near and far values.
float previous_znear = _computed_znear;
float previous_zfar = _computed_zfar;
// take a copy of the current near plane candidates
DistanceMatrixDrawableMap previousNearPlaneCandidateMap;
previousNearPlaneCandidateMap.swap(_nearPlaneCandidateMap);
_computed_znear = FLT_MAX;
_computed_zfar = -FLT_MAX;
ref_ptr<osg::RefMatrix> matrix = createOrReuseMatrix(node.getMatrix());
pushProjectionMatrix(matrix.get());
//osg::notify(osg::INFO)<<"Push projection "<<*matrix<<std::endl;
// note do culling check after the frustum has been updated to ensure
// that the node is not culled prematurely.
if (!isCulled(node))
{
handle_cull_callbacks_and_traverse(node);
}
popProjectionMatrix();
//osg::notify(osg::INFO)<<"Pop projection "<<*matrix<<std::endl;
_computed_znear = previous_znear;
_computed_zfar = previous_zfar;
// swap back the near plane candidates
previousNearPlaneCandidateMap.swap(_nearPlaneCandidateMap);
// pop the node's state off the render graph stack.
if (node_state) popStateSet();
// pop the culling mode.
popCurrentMask();
}
void CullVisitor::apply(Switch& node)
{
apply((Group&)node);
}
void CullVisitor::apply(LOD& node)
{
if (isCulled(node)) return;
// push the culling mode.
pushCurrentMask();
// push the node's state.
StateSet* node_state = node.getStateSet();
if (node_state) pushStateSet(node_state);
handle_cull_callbacks_and_traverse(node);
// pop the node's state off the render graph stack.
if (node_state) popStateSet();
// pop the culling mode.
popCurrentMask();
}
void CullVisitor::apply(osg::ClearNode& node)
{
// simply override the current earth sky.
setClearNode(&node);
// push the node's state.
StateSet* node_state = node.getStateSet();
if (node_state) pushStateSet(node_state);
handle_cull_callbacks_and_traverse(node);
// pop the node's state off the render graph stack.
if (node_state) popStateSet();
}
void CullVisitor::apply(osg::CameraNode& camera)
{
// push the node's state.
StateSet* node_state = camera.getStateSet();
if (node_state) pushStateSet(node_state);
// set the compute near far mode.
ComputeNearFarMode saved_compute_near_far_mode = getComputeNearFarMode();
setComputeNearFarMode( camera.getComputeNearFarMode());
osg::RefMatrix& originalModelView = getModelViewMatrix();
if (camera.getReferenceFrame()==osg::Transform::ABSOLUTE_RF)
{
pushProjectionMatrix(createOrReuseMatrix(camera.getProjectionMatrix()));
pushModelViewMatrix(createOrReuseMatrix(camera.getViewMatrix()));
}
else if (camera.getTransformOrder()==osg::CameraNode::POST_MULTIPLE)
{
pushProjectionMatrix(createOrReuseMatrix(getProjectionMatrix()*camera.getProjectionMatrix()));
pushModelViewMatrix(createOrReuseMatrix(getModelViewMatrix()*camera.getViewMatrix()));
}
else // pre multiple
{
pushProjectionMatrix(createOrReuseMatrix(camera.getProjectionMatrix()*getProjectionMatrix()));
pushModelViewMatrix(createOrReuseMatrix(camera.getViewMatrix()*getModelViewMatrix()));
}
if (camera.getRenderOrder()==osg::CameraNode::NESTED_RENDER)
{
handle_cull_callbacks_and_traverse(camera);
}
else
{
// set up lighting.
// currently ignore lights in the scene graph itself..
// will do later.
osgUtil::RenderStage* previous_stage = getCurrentRenderBin()->getStage();
// use render to texture stage.
// create the render to texture stage.
osg::ref_ptr<osgUtil::RenderStage> rtts = dynamic_cast<osgUtil::RenderStage*>(camera.getRenderingCache());
if (!rtts)
{
rtts = new osgUtil::RenderStage;
rtts->setCameraNode(&camera);
if (camera.getDrawBuffer() != GL_NONE)
{
rtts->setDrawBuffer(camera.getDrawBuffer());
}
else
{
// inherit draw buffer from above.
rtts->setDrawBuffer(previous_stage->getDrawBuffer());
}
if (camera.getReadBuffer() != GL_NONE)
{
rtts->setReadBuffer(camera.getReadBuffer());
}
else
{
// inherit read buffer from above.
rtts->setReadBuffer(previous_stage->getReadBuffer());
}
camera.setRenderingCache(rtts.get());
}
else
{
// reusing render to texture stage, so need to reset it to empty it from previous frames contents.
rtts->reset();
}
// set up the background color and clear mask.
rtts->setClearColor(camera.getClearColor());
rtts->setClearMask(camera.getClearMask());
// set the color mask.
osg::ColorMask* colorMask = camera.getColorMask()!=0 ? camera.getColorMask() : previous_stage->getColorMask();
rtts->setColorMask(colorMask);
// set up the viewport.
osg::Viewport* viewport = camera.getViewport()!=0 ? camera.getViewport() : previous_stage->getViewport();
rtts->setViewport( viewport );
// set up to charge the same PositionalStateContainer is the parent previous stage.
osg::Matrix inhertiedMVtolocalMV;
inhertiedMVtolocalMV.invert(originalModelView);
inhertiedMVtolocalMV.postMult(getModelViewMatrix());
rtts->setInheritedPositionalStateContainerMatrix(inhertiedMVtolocalMV);
rtts->setInheritedPositionalStateContainer(previous_stage->getPositionalStateContainer());
// record the render bin, to be restored after creation
// of the render to text
osgUtil::RenderBin* previousRenderBin = getCurrentRenderBin();
// set the current renderbin to be the newly created stage.
setCurrentRenderBin(rtts.get());
// set the cull traversal mask of camera node
osg::Node::NodeMask saved_mask = getCullMask();
if (camera.getInheritanceMask() & CULL_MASK)
{
setTraversalMask(camera.getCullMask());
}
// traverse the subgraph
{
handle_cull_callbacks_and_traverse(camera);
}
// restore the cull traversal mask of camera node
if (camera.getInheritanceMask() & CULL_MASK)
{
setTraversalMask(saved_mask);
}
// restore the previous renderbin.
setCurrentRenderBin(previousRenderBin);
if (rtts->getRenderGraphList().size()==0 && rtts->getRenderBinList().size()==0)
{
// getting to this point means that all the subgraph has been
// culled by small feature culling or is beyond LOD ranges.
}
// and the render to texture stage to the current stages
// dependancy list.
switch(camera.getRenderOrder())
{
case osg::CameraNode::PRE_RENDER :
getCurrentRenderBin()->getStage()->addPreRenderStage(rtts.get());
break;
default :
getCurrentRenderBin()->getStage()->addPostRenderStage(rtts.get());
break;
}
}
// restore the previous model view matrix.
popModelViewMatrix();
// restore the previous model view matrix.
popProjectionMatrix();
// restore the previous compute near far mode
setComputeNearFarMode(saved_compute_near_far_mode);
// pop the node's state off the render graph stack.
if (node_state) popStateSet();
}
void CullVisitor::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;
}
// push the culling mode.
pushCurrentMask();
// push the node's state.
StateSet* node_state = node.getStateSet();
if (node_state) pushStateSet(node_state);
handle_cull_callbacks_and_traverse(node);
// pop the node's state off the render graph stack.
if (node_state) popStateSet();
// pop the culling mode.
popCurrentMask();
// pop the current mask for the disabled occluder
popOccludersCurrentMask(_nodePath);
}