From 5d79eb1c9d1c7a2499a4fe4146841a5cd950e807 Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Thu, 29 Apr 2004 22:21:06 +0000 Subject: [PATCH] Added support for fine grained computation of the near plane, by taking into account individual primitives culled against the view frustum. Added better support for computing the near far for billboards. --- include/osgUtil/CullVisitor | 48 +++- src/osgUtil/CullVisitor.cpp | 459 +++++++++++++++++++++++++++++++++--- src/osgUtil/SceneView.cpp | 1 + 3 files changed, 472 insertions(+), 36 deletions(-) diff --git a/include/osgUtil/CullVisitor b/include/osgUtil/CullVisitor index c7fc15b03..a8747bb71 100644 --- a/include/osgUtil/CullVisitor +++ b/include/osgUtil/CullVisitor @@ -198,8 +198,12 @@ class OSGUTIL_EXPORT CullVisitor : public osg::NodeVisitor, public osg::CullStac inline value_type getCalculatedFarPlane() const { return _computed_zfar; } - void updateCalculatedNearFar(const osg::Matrix& matrix,const osg::Drawable& drawable) { updateCalculatedNearFar(matrix,drawable.getBound()); } - void updateCalculatedNearFar(const osg::Matrix& matrix,const osg::BoundingBox& bb); + value_type computeNearestPointInFrustum(const osg::Matrix& matrix, const osg::Polytope::PlaneList& planes,const osg::Drawable& drawable); + + bool updateCalculatedNearFar(const osg::Matrix& matrix,const osg::BoundingBox& bb); + + bool updateCalculatedNearFar(const osg::Matrix& matrix,const osg::Drawable& drawable, bool isBillboard=false); + void updateCalculatedNearFar(const osg::Vec3& pos); /** Add a drawable to current render graph.*/ @@ -341,6 +345,46 @@ class OSGUTIL_EXPORT CullVisitor : public osg::NodeVisitor, public osg::CullStac osg::ref_ptr _impostorSpriteManager; osg::ref_ptr _state; + + + struct MatrixPlanesDrawables + { + MatrixPlanesDrawables(const osg::Matrix& matrix, const osg::Drawable* drawable, const osg::Polytope& frustum): + _matrix(matrix), + _drawable(drawable) + { + // create a new list of planes from the active walls of the frustum. + osg::Polytope::ClippingMask result_mask = frustum.getResultMask(); + osg::Polytope::ClippingMask selector_mask = 0x1; + for(osg::Polytope::PlaneList::const_iterator itr=frustum.getPlaneList().begin(); + itr!=frustum.getPlaneList().end(); + ++itr) + { + if (result_mask&selector_mask) _planes.push_back(*itr); + selector_mask <<= 1; + } + } + + MatrixPlanesDrawables(const MatrixPlanesDrawables& mpd): + _matrix(mpd._matrix), + _drawable(mpd._drawable), + _planes(mpd._planes) {} + + MatrixPlanesDrawables& operator = (const MatrixPlanesDrawables& mpd) + { + _matrix = mpd._matrix; + _drawable = mpd._drawable; + _planes = mpd._planes; + return *this; + } + + osg::Matrix _matrix; + const osg::Drawable* _drawable; + osg::Polytope::PlaneList _planes; + }; + + typedef std::multimap DistanceMatrixDrawableMap; + DistanceMatrixDrawableMap _nearPlaneCandidateMap; }; diff --git a/src/osgUtil/CullVisitor.cpp b/src/osgUtil/CullVisitor.cpp index 6e8c9fbef..3c81a1d9c 100644 --- a/src/osgUtil/CullVisitor.cpp +++ b/src/osgUtil/CullVisitor.cpp @@ -22,6 +22,8 @@ #include #include #include +#include +#include #include #include @@ -104,6 +106,7 @@ CullVisitor::CullVisitor(): _currentReuseRenderLeafIndex(0) { _impostorSpriteManager = new ImpostorSpriteManager; + // _nearFarRatio = 0.000005f; } @@ -148,6 +151,8 @@ void CullVisitor::reset() if (_impostorSpriteManager.valid()) _impostorSpriteManager->reset(); + + _nearPlaneCandidateMap.clear(); } float CullVisitor::getDistanceToEyePoint(const Vec3& pos, bool withLODScale) const @@ -176,9 +181,36 @@ float CullVisitor::getDistanceFromEyePoint(const osg::Vec3& pos, bool withLODSca 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 = "<first<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<tick(); + // osg::notify(osg::NOTICE)<<"Took "<delta_m(start_t,end_t)<<"ms to test "<0.0f) { + // osg::notify(osg::WARN)<<" using "<<_computed_znear<<"\t"<<_computed_zfar< DistancePoint; + typedef std::vector 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"<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 "<=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 "<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< 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="<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"<nd_far) nd_far = d; + osg::notify(osg::NOTICE)<<"\ti="<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="<far in range calculation,"<< std::endl; - osg::notify(osg::WARN)<<" correcting by swapping values d_near="<