/* -*-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 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() <=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(); } 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("<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 "<=_computed_znear) { //osg::notify(osg::INFO)<<"clamping "<< "znear="<<_computed_znear << " zfar="<<_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="<getStage(); unsigned int contextID = getState() ? getState()->getContextID() : 0; // use render to texture stage. // create the render to texture stage. osg::ref_ptr rtts = dynamic_cast(camera.getRenderingCache(contextID)); if (!rtts) { OpenThreads::ScopedLock lock(*(camera.getDataChangeMutex())); 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(contextID, 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->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 // dependancy list. switch(camera.getRenderOrder()) { case osg::CameraNode::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 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); }