You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
317 lines
9.9 KiB
317 lines
9.9 KiB
/* -*-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 <osg/Matrix> |
|
#include <osg/Drawable> |
|
#include <osg/StateSet> |
|
#include <osg/State> |
|
#include <osg/Light> |
|
|
|
#include <osgUtil/RenderLeaf> |
|
|
|
#include <set> |
|
#include <vector> |
|
#include <algorithm> |
|
|
|
namespace osgUtil { |
|
|
|
struct LessDepthSortFunctor |
|
{ |
|
bool operator() (const osg::ref_ptr<RenderLeaf>& lhs,const osg::ref_ptr<RenderLeaf>& 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<StateGraph> > ChildList; |
|
typedef std::vector< osg::ref_ptr<RenderLeaf> > LeafList; |
|
|
|
StateGraph* _parent; |
|
|
|
#ifdef OSGUTIL_RENDERBACKEND_USE_REF_PTR |
|
osg::ref_ptr<const osg::StateSet> _stateset; |
|
#else |
|
const osg::StateSet* _stateset; |
|
#endif |
|
|
|
int _depth; |
|
ChildList _children; |
|
LeafList _leaves; |
|
|
|
mutable float _averageDistance; |
|
mutable float _minimumDistance; |
|
|
|
osg::ref_ptr<osg::Referenced> _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(); } |
|
|
|
#ifdef OSGUTIL_RENDERBACKEND_USE_REF_PTR |
|
const osg::StateSet* getStateSet() const { return _stateset.get(); } |
|
#else |
|
const osg::StateSet* getStateSet() const { return _stateset; } |
|
#endif |
|
|
|
/** 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<StateGraph*> 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<StateGraph*>::reverse_iterator itr=return_path.rbegin(); |
|
itr!=return_path.rend(); |
|
++itr) |
|
{ |
|
StateGraph* rg = (*itr); |
|
if (rg->getStateSet()) state.pushStateSet(rg->getStateSet()); |
|
} |
|
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->getStateSet()) state.popStateSet(); |
|
// and push new state. |
|
if (sg_new->getStateSet()) state.pushStateSet(sg_new->getStateSet()); |
|
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->getStateSet()) state.popStateSet(); |
|
sg_curr = sg_curr->_parent; |
|
} |
|
|
|
// use return path to trace back steps to sg_new. |
|
std::vector<StateGraph*> 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->getStateSet()) state.popStateSet(); |
|
sg_curr = sg_curr->_parent; |
|
|
|
return_path.push_back(sg_new); |
|
sg_new = sg_new->_parent; |
|
} |
|
|
|
for(std::vector<StateGraph*>::reverse_iterator itr=return_path.rbegin(); |
|
itr!=return_path.rend(); |
|
++itr) |
|
{ |
|
StateGraph* rg = (*itr); |
|
if (rg->getStateSet()) state.pushStateSet(rg->getStateSet()); |
|
} |
|
|
|
} |
|
|
|
inline static void moveToRootStateGraph(osg::State& state,StateGraph* sg_curr) |
|
{ |
|
// need to pop back all statesets and matrices. |
|
while (sg_curr) |
|
{ |
|
if (sg_curr->getStateSet()) 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->getStateSet()) ++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 |
|
|
|
|