/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2003 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. */ #ifndef OSGUTIL_CULLVISITOR #define OSGUTIL_CULLVISITOR 1 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace osgUtil { /** * Basic NodeVisitor implementation for rendering a scene. * This visitor traverses the scene graph, collecting transparent and * opaque osg::Drawables into a depth sorted transparent bin and a state * sorted opaque bin. The opaque bin is rendered first, and then the * transparent bin in rendered in order from the furthest osg::Drawable * from the eye to the one nearest the eye. */ class OSGUTIL_EXPORT CullVisitor : public osg::NodeVisitor, public osg::CullStack { public: CullVisitor(); virtual ~CullVisitor(); virtual CullVisitor* cloneType() const { return new CullVisitor(); } virtual void reset(); virtual osg::Vec3 getEyePoint() const { return getEyeLocal(); } virtual float getDistanceToEyePoint(const osg::Vec3& pos, bool withLODScale) const; virtual float getDistanceFromEyePoint(const osg::Vec3& pos, bool withLODScale) const; virtual void apply(osg::Node&); virtual void apply(osg::Geode& node); virtual void apply(osg::Billboard& node); virtual void apply(osg::LightSource& node); virtual void apply(osg::ClipNode& node); virtual void apply(osg::Group& node); virtual void apply(osg::Transform& node); virtual void apply(osg::Projection& node); virtual void apply(osg::Switch& node); virtual void apply(osg::LOD& node); virtual void apply(osg::ClearNode& node); virtual void apply(osg::OccluderNode& node); virtual void apply(osg::Impostor& node); void setClearNode(const osg::ClearNode* earthSky) { _clearNode = earthSky; } const osg::ClearNode* getClearNode() const { return _clearNode.get(); } /** Switch the creation of Impostors on or off. * Setting active to false forces the CullVisitor to use the Impostor * LOD children for rendering. Setting active to true forces the * CullVisitor to create the appropriate pre-rendering stages which * render to the ImpostorSprite's texture.*/ void setImpostorsActive(bool active) { _impostorActive = active; } /** Get whether impostors are active or not. */ bool getImpostorsActive() const { return _impostorActive; } /** Set the impostor error threshold. * Used in calculation of whether impostors remain valid.*/ void setImpostorPixelErrorThreshold(float numPixels) { _impostorPixelErrorThreshold=numPixels; } /** Get the impostor error threshold.*/ float getImpostorPixelErrorThreshold() const { return _impostorPixelErrorThreshold; } /** Set whether ImpsotorSprite's should be placed in a depth sorted bin for rendering.*/ void setDepthSortImpostorSprites(bool doDepthSort) { _depthSortImpostorSprites = doDepthSort; } /** Get whether ImpsotorSprite's are depth sorted bin for rendering.*/ bool setDepthSortImpostorSprites() const { return _depthSortImpostorSprites; } /** Set the number of frames that an ImpsotorSprite's is kept whilst not being beyond, * before being recycled.*/ void setNumberOfFrameToKeepImpostorSprites(int numFrames) { _numFramesToKeepImpostorSprites = numFrames; } /** Get the number of frames that an ImpsotorSprite's is kept whilst not being beyond, * before being recycled.*/ int getNumberOfFrameToKeepImpostorSprites() const { return _numFramesToKeepImpostorSprites; } enum ComputeNearFarMode { DO_NOT_COMPUTE_NEAR_FAR = 0, COMPUTE_NEAR_FAR_USING_BOUNDING_VOLUMES, COMPUTE_NEAR_FAR_USING_PRIMITIVES }; void setComputeNearFarMode(ComputeNearFarMode cnfm) { _computeNearFar=cnfm; } ComputeNearFarMode getComputeNearFarMode() const { return _computeNearFar;} /** Push state set on the current state group. * If the state exists in a child state group of the current * state group then move the current state group to that child. * Otherwise, create a new state group for the state set, add * it to the current state group then move the current state * group pointer to the new state group. */ inline void pushStateSet(const osg::StateSet* ss) { _currentRenderGraph = _currentRenderGraph->find_or_insert(ss); if (ss->useRenderBinDetails()) { _currentRenderBin = _currentRenderBin->find_or_insert(ss->getBinNumber(),ss->getBinName()); } } /** Pop the top state set and hence associated state group. * Move the current state group to the parent of the popped * state group. */ inline void popStateSet() { if (_currentRenderGraph->_stateset->useRenderBinDetails()) { _currentRenderBin = _currentRenderBin->_parent; } _currentRenderGraph = _currentRenderGraph->_parent; } inline void setRenderGraph(RenderGraph* rg) { _rootRenderGraph = rg; _currentRenderGraph = rg; } inline RenderGraph* getRootRenderGraph() { return _rootRenderGraph.get(); } inline RenderGraph* getCurrentRenderGraph() { return _currentRenderGraph; } inline void setRenderStage(RenderStage* rg) { _rootRenderStage = rg; _currentRenderBin = rg; } inline RenderStage* getRenderStage() { return _rootRenderStage.get(); } inline RenderBin* getCurrentRenderBin() { return _currentRenderBin; } inline void setCurrentRenderBin(RenderBin* rb) { _currentRenderBin = rb; } inline float getCalculatedNearPlane() const { return _computed_znear; } inline float 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); void updateCalculatedNearFar(const osg::Vec3& pos); /** Add a drawable to current render graph.*/ inline void addDrawable(osg::Drawable* drawable,osg::RefMatrix* matrix); /** Add a drawable and depth to current render graph.*/ inline void addDrawableAndDepth(osg::Drawable* drawable,osg::RefMatrix* matrix,float depth); /** Add an attribute which is positioned related to the modelview matrix.*/ inline void addPositionedAttribute(osg::RefMatrix* matrix,const osg::StateAttribute* attr); /** reimplement CullStack's popProjectionMatrix() adding clamping of the projection matrix to the computed near and far.*/ virtual void popProjectionMatrix(); void setState(osg::State* state) { _state = state; } osg::State* getState() { return _state.get(); } const osg::State* getState() const { return _state.get(); } protected: /** prevent unwanted copy construction.*/ CullVisitor(const CullVisitor&):osg::NodeVisitor(),osg::CullStack() {} /** prevent unwanted copy operator.*/ CullVisitor& operator = (const CullVisitor&) { return *this; } inline void handle_cull_callbacks_and_traverse(osg::Node& node) { osg::NodeCallback* callback = node.getCullCallback(); if (callback) (*callback)(&node,this); else traverse(node); } inline void handle_cull_callbacks_and_accept(osg::Node& node,osg::Node* acceptNode) { osg::NodeCallback* callback = node.getCullCallback(); if (callback) (*callback)(&node,this); else acceptNode->accept(*this); } /** create an impostor sprite by setting up a pre-rendering stage * to generate the impostor texture. */ osg::ImpostorSprite* createImpostorSprite(osg::Impostor& node); osg::ref_ptr _rootRenderGraph; RenderGraph* _currentRenderGraph; osg::ref_ptr _rootRenderStage; RenderBin* _currentRenderBin; osg::ref_ptr _localPreRenderState; ComputeNearFarMode _computeNearFar; float _computed_znear; float _computed_zfar; osg::ref_ptr _clearNode; bool _impostorActive; bool _depthSortImpostorSprites; float _impostorPixelErrorThreshold; int _numFramesToKeepImpostorSprites; typedef std::vector< osg::ref_ptr > RenderLeafList; RenderLeafList _reuseRenderLeafList; unsigned int _currentReuseRenderLeafIndex; inline RenderLeaf* createOrReuseRenderLeaf(osg::Drawable* drawable,osg::RefMatrix* projection,osg::RefMatrix* matrix, float depth=0.0f); osg::ref_ptr _impostorSpriteManager; osg::ref_ptr _state; }; inline void CullVisitor::addDrawable(osg::Drawable* drawable,osg::RefMatrix* matrix) { if (_currentRenderGraph->leaves_empty()) { // this is first leaf to be added to RenderGraph // and therefore should not already know to current render bin, // so need to add it. _currentRenderBin->addRenderGraph(_currentRenderGraph); } //_currentRenderGraph->addLeaf(new RenderLeaf(drawable,matrix)); _currentRenderGraph->addLeaf(createOrReuseRenderLeaf(drawable,_projectionStack.back().get(),matrix)); } /** Add a drawable and depth to current render graph.*/ inline void CullVisitor::addDrawableAndDepth(osg::Drawable* drawable,osg::RefMatrix* matrix,float depth) { if (_currentRenderGraph->leaves_empty()) { // this is first leaf to be added to RenderGraph // and therefore should not already know to current render bin, // so need to add it. _currentRenderBin->addRenderGraph(_currentRenderGraph); } //_currentRenderGraph->addLeaf(new RenderLeaf(drawable,matrix,depth)); _currentRenderGraph->addLeaf(createOrReuseRenderLeaf(drawable,_projectionStack.back().get(),matrix,depth)); } /** Add an attribute which is positioned related to the modelview matrix.*/ inline void CullVisitor::addPositionedAttribute(osg::RefMatrix* matrix,const osg::StateAttribute* attr) { _currentRenderBin->_stage->addPositionedAttribute(matrix,attr); } inline RenderLeaf* CullVisitor::createOrReuseRenderLeaf(osg::Drawable* drawable,osg::RefMatrix* projection,osg::RefMatrix* matrix, float depth) { // skip of any already reused renderleaf. while (_currentReuseRenderLeafIndex<_reuseRenderLeafList.size() && _reuseRenderLeafList[_currentReuseRenderLeafIndex]->referenceCount()>1) { osg::notify(osg::NOTICE)<<"Warning:createOrReuseRenderLeaf() skipping multiply refrenced entry."<< std::endl; ++_currentReuseRenderLeafIndex; } // if still within list, element must be singularly referenced // there return it to be reused. if (_currentReuseRenderLeafIndex<_reuseRenderLeafList.size()) { RenderLeaf* renderleaf = _reuseRenderLeafList[_currentReuseRenderLeafIndex++].get(); renderleaf->set(drawable,projection,matrix,depth); return renderleaf; } // otherwise need to create new renderleaf. RenderLeaf* renderleaf = new RenderLeaf(drawable,projection,matrix,depth); _reuseRenderLeafList.push_back(renderleaf); ++_currentReuseRenderLeafIndex; return renderleaf; } } #endif