/* -*-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. */ #ifndef OSGUTIL_STATEGRAPH #define OSGUTIL_STATEGRAPH 1 #include #include #include #include #include #include #include #include #include namespace osgUtil { struct LessDepthSortFunctor { bool operator() (const osg::ref_ptr& lhs,const osg::ref_ptr& rhs) { return (lhs->_depth < rhs->_depth); } }; /** StateGraph - contained in a renderBin, defines the scene to be drawn. */ class OSGUTIL_EXPORT StateGraph : public osg::Referenced { public: typedef std::map< const osg::StateSet*, osg::ref_ptr > ChildList; typedef std::vector< osg::ref_ptr > LeafList; StateGraph* _parent; const osg::StateSet* _stateset; int _depth; ChildList _children; LeafList _leaves; mutable float _averageDistance; mutable float _minimumDistance; osg::ref_ptr _userData; bool _dynamic; StateGraph(): osg::Referenced(false), _parent(NULL), _stateset(NULL), _depth(0), _averageDistance(0), _minimumDistance(0), _userData(NULL), _dynamic(false) { } StateGraph(StateGraph* parent,const osg::StateSet* stateset): osg::Referenced(false), _parent(parent), _stateset(stateset), _depth(0), _averageDistance(0), _minimumDistance(0), _userData(NULL), _dynamic(false) { if (_parent) _depth = _parent->_depth + 1; if (_parent && _parent->_dynamic) _dynamic = true; else _dynamic = stateset->getDataVariance()==osg::Object::DYNAMIC; } ~StateGraph() {} StateGraph* cloneType() const { return new StateGraph; } void setUserData(osg::Referenced* obj) { _userData = obj; } osg::Referenced* getUserData() { return _userData.get(); } const osg::Referenced* getUserData() const { return _userData.get(); } /** return true if all of drawables, lights and children are empty.*/ inline bool empty() const { return _leaves.empty() && _children.empty(); } inline bool leaves_empty() const { return _leaves.empty(); } inline float getAverageDistance() const { if (_averageDistance==FLT_MAX && !_leaves.empty()) { _averageDistance = 0.0f; for(LeafList::const_iterator itr=_leaves.begin(); itr!=_leaves.end(); ++itr) { _averageDistance += (*itr)->_depth; } _averageDistance /= (float)_leaves.size(); } return _averageDistance; } inline float getMinimumDistance() const { if (_minimumDistance==FLT_MAX && !_leaves.empty()) { LeafList::const_iterator itr=_leaves.begin(); _minimumDistance = (*itr)->_depth; ++itr; for(; itr!=_leaves.end(); ++itr) { if ((*itr)->_depth<_minimumDistance) _minimumDistance=(*itr)->_depth; } } return _minimumDistance; } inline void sortFrontToBack() { std::sort(_leaves.begin(),_leaves.end(),LessDepthSortFunctor()); } /** Reset the internal contents of a StateGraph, including deleting all children.*/ void reset(); /** Recursively clean the StateGraph of all its drawables, lights and depths. * Leaves children intact, and ready to be populated again.*/ void clean(); /** Recursively prune the StateGraph of empty children.*/ void prune(); inline StateGraph* find_or_insert(const osg::StateSet* stateset) { // search for the appropriate state group, return it if found. ChildList::iterator itr = _children.find(stateset); if (itr!=_children.end()) return itr->second.get(); // create a state group and insert it into the children list // then return the state group. StateGraph* sg = new StateGraph(this,stateset); _children[stateset] = sg; return sg; } /** add a render leaf.*/ inline void addLeaf(RenderLeaf* leaf) { if (leaf) { _averageDistance = FLT_MAX; // signify dirty. _minimumDistance = FLT_MAX; // signify dirty. _leaves.push_back(leaf); leaf->_parent = this; if (_dynamic) leaf->_dynamic = true; } } static inline void moveStateGraph(osg::State& state,StateGraph* sg_curr,StateGraph* sg_new) { if (sg_new==sg_curr || sg_new==NULL) return; if (sg_curr==NULL) { // use return path to trace back steps to sg_new. std::vector return_path; // need to pop back root render graph. do { return_path.push_back(sg_new); sg_new = sg_new->_parent; } while (sg_new); for(std::vector::reverse_iterator itr=return_path.rbegin(); itr!=return_path.rend(); ++itr) { StateGraph* rg = (*itr); if (rg->_stateset) state.pushStateSet(rg->_stateset); } return; } // first handle the typical case which is two state groups // are neighbours. if (sg_curr->_parent==sg_new->_parent) { // state has changed so need to pop old state. if (sg_curr->_stateset) state.popStateSet(); // and push new state. if (sg_new->_stateset) state.pushStateSet(sg_new->_stateset); return; } // need to pop back up to the same depth as the new state group. while (sg_curr->_depth>sg_new->_depth) { if (sg_curr->_stateset) state.popStateSet(); sg_curr = sg_curr->_parent; } // use return path to trace back steps to sg_new. std::vector return_path; // need to pop back up to the same depth as the curr state group. while (sg_new->_depth>sg_curr->_depth) { return_path.push_back(sg_new); sg_new = sg_new->_parent; } // now pop back up both parent paths until they agree. // DRT - 10/22/02 // should be this to conform with above case where two StateGraph // nodes have the same parent while (sg_curr != sg_new) { if (sg_curr->_stateset) state.popStateSet(); sg_curr = sg_curr->_parent; return_path.push_back(sg_new); sg_new = sg_new->_parent; } for(std::vector::reverse_iterator itr=return_path.rbegin(); itr!=return_path.rend(); ++itr) { StateGraph* rg = (*itr); if (rg->_stateset) state.pushStateSet(rg->_stateset); } } inline static void moveToRootStateGraph(osg::State& state,StateGraph* sg_curr) { // need to pop back all statesets and matrices. while (sg_curr) { if (sg_curr->_stateset) state.popStateSet(); sg_curr = sg_curr->_parent; } } inline static int numToPop(StateGraph* sg_curr) { int numToPop = 0; // need to pop back all statesets and matrices. while (sg_curr) { if (sg_curr->_stateset) ++numToPop; sg_curr = sg_curr->_parent; } return numToPop; } private: /// disallow copy construction. StateGraph(const StateGraph&):osg::Referenced() {} /// disallow copy operator. StateGraph& operator = (const StateGraph&) { return *this; } }; } #endif