/* -*-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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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; } CullVisitor::CullVisitor(): osg::NodeVisitor(CULL_VISITOR,TRAVERSE_ACTIVE_CHILDREN), _currentStateGraph(NULL), _currentRenderBin(NULL), _traversalNumber(0), _computed_znear(FLT_MAX), _computed_zfar(-FLT_MAX), _currentReuseRenderLeafIndex(0), _numberOfEncloseOverrideRenderBinDetails(0) { _identifier = new Identifier; } CullVisitor::CullVisitor(const CullVisitor& rhs): NodeVisitor(rhs), CullStack(rhs), _currentStateGraph(NULL), _currentRenderBin(NULL), _traversalNumber(0), _computed_znear(FLT_MAX), _computed_zfar(-FLT_MAX), _currentReuseRenderLeafIndex(0), _numberOfEncloseOverrideRenderBinDetails(0), _identifier(rhs._identifier) { } CullVisitor::~CullVisitor() { reset(); } osg::ref_ptr& CullVisitor::prototype() { static osg::ref_ptr s_CullVisitor = new CullVisitor; return s_CullVisitor; } CullVisitor* CullVisitor::create() { return CullVisitor::prototype().valid() ? CullVisitor::prototype()->clone() : new CullVisitor; } void CullVisitor::reset() { // // first unref all referenced objects and then empty the containers. // CullStack::reset(); _renderBinStack.clear(); _numberOfEncloseOverrideRenderBinDetails = 0; // reset the traversal number _traversalNumber = 0; // 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; // Only reset the RenderLeaf objects used last frame. for(RenderLeafList::iterator itr=_reuseRenderLeafList.begin(), iter_end=_reuseRenderLeafList.begin()+_currentReuseRenderLeafIndex; itr!=iter_end; ++itr) { (*itr)->reset(); } // reset the resuse lists. _currentReuseRenderLeafIndex = 0; _nearPlaneCandidateMap.clear(); _farPlaneCandidateMap.clear(); } float CullVisitor::getDistanceToEyePoint(const Vec3& pos, bool withLODScale) const { if (withLODScale) return (pos-getEyeLocal()).length()*getLODScale(); else return (pos-getEyeLocal()).length(); } float CullVisitor::getDistanceToViewPoint(const Vec3& pos, bool withLODScale) const { if (withLODScale) return (pos-getViewPointLocal()).length()*getLODScale(); else return (pos-getViewPointLocal()).length(); } inline CullVisitor::value_type distance(const osg::Vec3& coord,const osg::Matrix& matrix) { 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::computeNearPlane() { //OSG_NOTICE<<"CullVisitor::computeNearPlane() _computed_znear="<<_computed_znear<<", _computed_zfar="<<_computed_zfar<tick(); #endif // 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; value_type d_near = computeNearestPointInFrustum(itr->second._matrix, itr->second._planes,*(itr->second._drawable)); //OSG_NOTICE<<" testing computeNearestPointInFrustum with, drawable"<second._drawable<<", "<first<<", d_near = "<tick(); OSG_NOTICE<<"Took "<delta_m(start_t,end_t)<<"ms to test "<tick(); // update near from defferred list of drawables unsigned int numTests = 0; for(DistanceMatrixDrawableMap::reverse_iterator itr=_farPlaneCandidateMap.rbegin(); itr!=_farPlaneCandidateMap.rend() && itr->first>_computed_zfar; ++itr) { ++numTests; // OSG_WARN<<"testing computeFurthestPointInFrustum with d_far = "<first<second._matrix, itr->second._planes,*(itr->second._drawable)); if (d_far>_computed_zfar) { _computed_zfar = d_far; // OSG_WARN<<"updating zfar to "<<_computed_zfar<tick(); //OSG_NOTICE<<"Took "<delta_m(start_t,end_t)<<"ms to test "< struct ComputeNearFarFunctor { ComputeNearFarFunctor(): _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 DistancePoint; typedef std::vector Polygon; Comparator _comparator; CullVisitor::value_type _znear; osg::Matrix _matrix; const osg::Polytope::PlaneList* _planes; Polygon _polygonOriginal; Polygon _polygonNew; Polygon _pointCache; // Handle Points inline void operator() ( const osg::Vec3 &v1, bool) { CullVisitor::value_type n1 = distance(v1,_matrix); // check if point is behind znear, if so discard if (_comparator.greaterEqual(n1,_znear)) { //OSG_NOTICE<<"Point beyond znear"<begin(); pitr != _planes->end(); ++pitr) { const osg::Plane& plane = *pitr; float d1 = plane.distance(v1); if (d1<0.0) { //OSG_NOTICE<<"Point outside frustum "<begin(); pitr != _planes->end(); ++pitr) { const osg::Plane& plane = *pitr; float d1 = plane.distance(v1); float d2 = plane.distance(v2); unsigned int numOutside = ((d1<0.0)?1:0) + ((d2<0.0)?1:0); if (numOutside==2) { //OSG_NOTICE<<"Line totally outside frustum "<=0.0)?1:0) + ((d2>=0.0)?1:0); if (numInside<2) { active_mask = active_mask | selector_mask; } //OSG_NOTICE<<"Line ok w.r.t plane "<begin(); pitr != _planes->end(); ++pitr) { if (active_mask & selector_mask) { // clip line to plane const osg::Plane& plane = *pitr; // assign the distance from the current plane. p1.first = plane.distance(p1.second); p2.first = plane.distance(p2.second); if (p1.first >= 0.0f) { // p1 is in. if (p2.first < 0.0) { // p2 is out. // replace p2 with intersection float r = p1.first/(p1.first-p2.first); p2 = DistancePoint(0.0f, p1.second*(1.0f-r) + p2.second*r); } } else if (p2.first >= 0.0f) { // p1 is out and p2 is in. // replace p1 with intersection float r = p1.first/(p1.first-p2.first); p1 = DistancePoint(0.0f, p1.second*(1.0f-r) + p2.second*r); } // The case where both are out was handled above. } selector_mask <<= 1; } n1 = distance(p1.second,_matrix); n2 = distance(p2.second,_matrix); _znear = _comparator.minimum(n1, n2); //OSG_NOTICE<<"Near plane updated "<<_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_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_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 (_comparator.less(dist,_znear)) { _znear = dist; //OSG_NOTICE<<"Near plane updated "<<_znear<operator()(v1, v2, v3, treatVertexDataAsTemporary); this->operator()(v1, v3, v4, treatVertexDataAsTemporary); } }; struct LessComparator { inline bool less(CullVisitor::value_type lhs, CullVisitor::value_type rhs) const { return lhs=rhs; } inline CullVisitor::value_type minimum(CullVisitor::value_type lhs, CullVisitor::value_type rhs) const { return lhs ComputeNearestPointFunctor; struct GreaterComparator { inline bool less(CullVisitor::value_type lhs, CullVisitor::value_type rhs) const { return lhs>rhs; } inline bool lessEqual(CullVisitor::value_type lhs, CullVisitor::value_type rhs) const { return lhs>=rhs; } inline bool greaterEqual(CullVisitor::value_type lhs, CullVisitor::value_type rhs) const { return lhs<=rhs; } inline CullVisitor::value_type minimum(CullVisitor::value_type lhs, CullVisitor::value_type rhs) const { return lhs>rhs?lhs:rhs; } }; typedef ComputeNearFarFunctor ComputeFurthestPointFunctor; CullVisitor::value_type CullVisitor::computeNearestPointInFrustum(const osg::Matrix& matrix, const osg::Polytope::PlaneList& planes,const osg::Drawable& drawable) { // OSG_NOTICE<<"CullVisitor::computeNearestPointInFrustum("< cnpf; cnpf.set(FLT_MAX, matrix, &planes); drawable.accept(cnpf); return cnpf._znear; } CullVisitor::value_type CullVisitor::computeFurthestPointInFrustum(const osg::Matrix& matrix, const osg::Polytope::PlaneList& planes,const osg::Drawable& drawable) { //OSG_NOTICE<<"CullVisitor::computeFurthestPointInFrustum("< cnpf; cnpf.set(-FLT_MAX, 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_WARN<<"Warning: CullVisitor::updateCalculatedNearFar(.) near>far in range calculation,"<< std::endl; 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_NOTICE.precision(15); if (false) { OSG_NOTICE<<"TESTING Billboard near/far computation"<nd_far) nd_far = d; 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_WARN<<"Warning: CullVisitor::updateCalculatedNearFar(.) near>far in range calculation,"<< std::endl; OSG_WARN<<" correcting by swapping values d_near="<_computed_zfar) { _farPlaneCandidateMap.insert(DistanceMatrixDrawableMap::value_type(d_far,mpd) ); } } // 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>=0.0 && d_far<_computed_znear) { //_computed_znear = d_far; } if (_computeNearFar==COMPUTE_NEAR_FAR_USING_PRIMITIVES) { // use the near point if its further than current zfar as this is a conservative estimate of the zfar // while the final computation for this drawable is deferred. if (d_near>=0.0 && d_near>_computed_zfar) { // _computed_zfar = d_near; } } else // computing zfar using bounding sphere { if (d_far>_computed_zfar) _computed_zfar = d_far; } } else { if (d_near<_computed_znear) _computed_znear = d_near; if (d_far>_computed_zfar) _computed_zfar = d_far; } } } else { if (d_near<_computed_znear) _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_WARN<<"Alerting billboard ="<_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 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(osg::Drawable& drawable) { RefMatrix& matrix = *getModelViewMatrix(); const BoundingBox &bb =drawable.getBoundingBox(); if( drawable.getCullCallback() ) { osg::DrawableCullCallback* dcb = drawable.getCullCallback()->asDrawableCullCallback(); if (dcb) { if( dcb->cull( this, &drawable, &_renderInfo ) == true ) return; } } if (drawable.isCullingActive() && isCulled(bb)) return; if (_computeNearFar && bb.valid()) { if (!updateCalculatedNearFar(matrix,drawable,false)) return; } // 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()); } } } float depth = bb.valid() ? distance(bb.center(),matrix) : 0.0f; if (osg::isNaN(depth)) { OSG_NOTICE<<"CullVisitor::apply(Geode&) detected NaN,"<getName() << "\"" << std::endl; } } else { addDrawableAndDepth(&drawable,&matrix,depth); } for(unsigned int i=0;i< numPopStateSetRequired; ++i) { 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); // Don't traverse billboard, since drawables are handled manually below //handle_cull_callbacks_and_traverse(node); const Vec3& eye_local = getEyeLocal(); const RefMatrix& modelview = *getModelViewMatrix(); for(unsigned int i=0;igetBound())) continue; if( drawable->getCullCallback() ) { osg::DrawableCullCallback* dcb = drawable->getCullCallback()->asDrawableCullCallback(); if (dcb && dcb->cull( this, drawable, &_renderInfo ) == true ) continue; } RefMatrix* billboard_matrix = createOrReuseMatrix(modelview); node.computeMatrix(*billboard_matrix,eye_local,pos); if (_computeNearFar && drawable->getBoundingBox().valid()) updateCalculatedNearFar(*billboard_matrix,*drawable,true); float depth = distance(pos,modelview); /* if (_computeNearFar) { if (d<_computed_znear) { if (d<0.0) OSG_WARN<<"Alerting billboard handling ="<_computed_zfar) _computed_zfar = d; } */ StateSet* stateset = drawable->getStateSet(); if (stateset) pushStateSet(stateset); if (osg::isNaN(depth)) { OSG_NOTICE<<"CullVisitor::apply(Billboard&) detected NaN,"<getName() << "\"" << std::endl; } } else { addDrawableAndDepth(drawable,billboard_matrix,depth); } 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) { if (node.getReferenceFrame()==osg::ClipNode::RELATIVE_RF) { addPositionedAttribute(&matrix,itr->get()); } else { addPositionedAttribute(0,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); RefMatrix* matrix = createOrReuseMatrix(*getModelViewMatrix()); node.computeLocalToWorldMatrix(*matrix,this); pushModelViewMatrix(matrix, node.getReferenceFrame()); 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); DistanceMatrixDrawableMap previousFarPlaneCandidateMap; previousFarPlaneCandidateMap.swap(_farPlaneCandidateMap); _computed_znear = FLT_MAX; _computed_zfar = -FLT_MAX; RefMatrix *matrix = createOrReuseMatrix(node.getMatrix()); pushProjectionMatrix(matrix); //OSG_INFO<<"Push projection "<<*matrix<getStage()->setClearColor(node.getClearColor()); getCurrentRenderBin()->getStage()->setClearMask(node.getClearMask()); } else { // we have an earth sky implementation to do the work for us // so we don't need to clear. getCurrentRenderBin()->getStage()->setClearMask(0); } // 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(); } namespace osgUtil { class RenderStageCache : public osg::Object, public osg::Observer { public: typedef std::map > RenderStageMap; RenderStageCache() {} RenderStageCache(const RenderStageCache&, const osg::CopyOp&) {} virtual ~RenderStageCache() { for(RenderStageMap::iterator itr = _renderStageMap.begin(); itr != _renderStageMap.end(); ++itr) { itr->first->removeObserver(this); } } META_Object(osgUtil, RenderStageCache); virtual void objectDeleted(void* object) { osg::Referenced* ref = reinterpret_cast(object); OpenThreads::ScopedLock lock(_mutex); RenderStageMap::iterator itr = _renderStageMap.find(ref); if (itr!=_renderStageMap.end()) { _renderStageMap.erase(itr); } } void setRenderStage(osg::Referenced* cv, RenderStage* rs) { OpenThreads::ScopedLock lock(_mutex); RenderStageMap::iterator itr = _renderStageMap.find(cv); if (itr==_renderStageMap.end()) { _renderStageMap[cv] = rs; cv->addObserver(this); } else { itr->second = rs; } } RenderStage* getRenderStage(osg::Referenced* cv) { OpenThreads::ScopedLock lock(_mutex); RenderStageMap::iterator itr = _renderStageMap.find(cv); if (itr!=_renderStageMap.end()) { return itr->second.get(); } else { return 0; } } /** Resize any per context GLObject buffers to specified size. */ virtual void resizeGLObjectBuffers(unsigned int maxSize) { for(RenderStageMap::const_iterator itr = _renderStageMap.begin(); itr != _renderStageMap.end(); ++itr) { itr->second->resizeGLObjectBuffers(maxSize); } } /** If State is non-zero, this function releases any associated OpenGL objects for * the specified graphics context. Otherwise, releases OpenGL objexts * for all graphics contexts. */ virtual void releaseGLObjects(osg::State* state= 0) const { for(RenderStageMap::const_iterator itr = _renderStageMap.begin(); itr != _renderStageMap.end(); ++itr) { itr->second->releaseGLObjects(state); } } OpenThreads::Mutex _mutex; RenderStageMap _renderStageMap; }; } void CullVisitor::apply(osg::Camera& camera) { // push the node's state. StateSet* node_state = camera.getStateSet(); if (node_state) pushStateSet(node_state); //#define DEBUG_CULLSETTINGS #ifdef DEBUG_CULLSETTINGS if (osg::isNotifyEnabled(osg::NOTICE)) { OSG_NOTICE<getStage(); // unsigned int contextID = getState() ? getState()->getContextID() : 0; // use render to texture stage. // create the render to texture stage. osg::ref_ptr rsCache = dynamic_cast(camera.getRenderingCache()); if (!rsCache) { rsCache = new osgUtil::RenderStageCache; camera.setRenderingCache(rsCache.get()); } osg::ref_ptr rtts = rsCache->getRenderStage(this); if (!rtts) { OpenThreads::ScopedLock lock(*(camera.getDataChangeMutex())); rtts = _rootRenderStage.valid() ? osg::cloneType(_rootRenderStage.get()) : new osgUtil::RenderStage; rsCache->setRenderStage(this,rtts.get()); rtts->setCamera(&camera); if ( camera.getInheritanceMask() & DRAW_BUFFER ) { // inherit draw buffer from above. rtts->setDrawBuffer(previous_stage->getDrawBuffer(),previous_stage->getDrawBufferApplyMask()); } else { rtts->setDrawBuffer(camera.getDrawBuffer()); } if ( camera.getInheritanceMask() & READ_BUFFER ) { // inherit read buffer from above. rtts->setReadBuffer(previous_stage->getReadBuffer(), previous_stage->getReadBufferApplyMask()); } else { rtts->setReadBuffer(camera.getReadBuffer()); } } else { // reusing render to texture stage, so need to reset it to empty it from previous frames contents. rtts->reset(); } // set up clear masks/values rtts->setClearDepth(camera.getClearDepth()); rtts->setClearAccum(camera.getClearAccum()); rtts->setClearStencil(camera.getClearStencil()); rtts->setClearMask((camera.getInheritanceMask() & CLEAR_MASK) ? previous_stage->getClearMask() : camera.getClearMask()); rtts->setClearColor((camera.getInheritanceMask() & CLEAR_COLOR) ? previous_stage->getClearColor() : camera.getClearColor()); // 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 initial view matrix rtts->setInitialViewMatrix(modelview); // set up to charge the same PositionalStateContainer is the parent previous stage. osg::Matrix inheritedMVtolocalMV; inheritedMVtolocalMV.invert(originalModelView); inheritedMVtolocalMV.postMult(*getModelViewMatrix()); rtts->setInheritedPositionalStateContainerMatrix(inheritedMVtolocalMV); 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()); // traverse the subgraph { handle_cull_callbacks_and_traverse(camera); } // restore the previous renderbin. setCurrentRenderBin(previousRenderBin); if (rtts->getStateGraphList().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 // dependency list. switch(camera.getRenderOrder()) { case osg::Camera::PRE_RENDER: getCurrentRenderBin()->getStage()->addPreRenderStage(rtts.get(),camera.getRenderOrderNum()); break; default: getCurrentRenderBin()->getStage()->addPostRenderStage(rtts.get(),camera.getRenderOrderNum()); break; } } // restore the previous model view matrix. popModelViewMatrix(); // restore the previous model view matrix. popProjectionMatrix(); // restore the original near and far values _computed_znear = previous_znear; _computed_zfar = previous_zfar; // swap back the near plane candidates previousNearPlaneCandidateMap.swap(_nearPlaneCandidateMap); previousFarPlaneCandidateMap.swap(_farPlaneCandidateMap); if (camera.getViewport()) popViewport(); // restore the previous traversal mask settings if (mustSetCullMask) setTraversalMask(savedTraversalMask); // restore the previous cull settings setCullSettings(saved_cull_settings); // 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); } void CullVisitor::apply(osg::OcclusionQueryNode& 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); osg::Camera* camera = getCurrentCamera(); // If previous query indicates visible, then traverse as usual. if (node.getPassed( camera, *this )) handle_cull_callbacks_and_traverse(node); // Traverse the query subtree if OcclusionQueryNode needs to issue another query. node.traverseQuery( camera, *this ); // Traverse the debug bounding geometry, if enabled. node.traverseDebug( *this ); // pop the node's state off the render graph stack. if (node_state) popStateSet(); // pop the culling mode. popCurrentMask(); }