288 lines
8.9 KiB
C++
288 lines
8.9 KiB
C++
/* -*-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;
|
|
const osg::StateSet* _stateset;
|
|
|
|
int _depth;
|
|
ChildList _children;
|
|
LeafList _leaves;
|
|
|
|
mutable float _averageDistance;
|
|
mutable float _minimumDistance;
|
|
|
|
osg::ref_ptr<osg::Referenced> _userData;
|
|
|
|
|
|
StateGraph():
|
|
osg::Referenced(false),
|
|
_parent(NULL),
|
|
_stateset(NULL),
|
|
_depth(0),
|
|
_averageDistance(0),
|
|
_minimumDistance(0),
|
|
_userData(NULL)
|
|
{
|
|
}
|
|
|
|
StateGraph(StateGraph* parent,const osg::StateSet* stateset):
|
|
osg::Referenced(false),
|
|
_parent(parent),
|
|
_stateset(stateset),
|
|
_depth(0),
|
|
_averageDistance(0),
|
|
_minimumDistance(0),
|
|
_userData(NULL)
|
|
{
|
|
if (_parent) _depth = _parent->_depth + 1;
|
|
}
|
|
|
|
~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;
|
|
}
|
|
}
|
|
|
|
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->_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<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->_stateset) 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->_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;
|
|
}
|
|
|
|
}
|
|
|
|
private:
|
|
|
|
/// disallow copy construction.
|
|
StateGraph(const StateGraph&):osg::Referenced() {}
|
|
/// disallow copy operator.
|
|
StateGraph& operator = (const StateGraph&) { return *this; }
|
|
|
|
};
|
|
|
|
}
|
|
|
|
#endif
|
|
|