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="<