Added osgUtil::Optimizer which contains four visitor each designed for doing
different types of optimization on the scene graph - state optimization, flattening static transforms, combining LOD's and removing redundent groups. The new Optimizer replaces the once seperate OptimizerStateVisitor.
This commit is contained in:
parent
54d490e24b
commit
ccc3d3fd8a
@ -137,7 +137,7 @@ SOURCE=..\..\src\osgUtil\NvTriStripObjects.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\src\osgUtil\OptimizeStateVisitor.cpp
|
||||
SOURCE=..\..\src\osgUtil\Optimizer.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
@ -265,7 +265,7 @@ SOURCE=..\..\src\osgUtil\NvTriStripObjects.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\include\osgUtil\OptimizeStateVisitor
|
||||
SOURCE=..\..\include\osgUtil\Optimizer
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
|
@ -1,47 +0,0 @@
|
||||
//C++ header - Open Scene Graph - Copyright (C) 1998-2001 Robert Osfield
|
||||
//Distributed under the terms of the GNU Library General Public License (LGPL)
|
||||
//as published by the Free Software Foundation.
|
||||
|
||||
#ifndef OSGUTIL_OPTIMIZESTATEVISITOR
|
||||
#define OSGUTIL_OPTIMIZESTATEVISITOR
|
||||
|
||||
#include <osg/NodeVisitor>
|
||||
|
||||
#include <osgUtil/Export>
|
||||
|
||||
namespace osgUtil {
|
||||
|
||||
/** Insert impostor nodes into scene graph.
|
||||
* For example of usage see src/Demos/osgimpostor.
|
||||
*/
|
||||
class OSGUTIL_EXPORT OptimizeStateVisitor : public osg::NodeVisitor
|
||||
{
|
||||
public:
|
||||
|
||||
/// default to traversing all children.
|
||||
OptimizeStateVisitor() : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) {}
|
||||
|
||||
/** empty visitor, make it ready for next traversal.*/
|
||||
virtual void reset();
|
||||
|
||||
|
||||
virtual void apply(osg::Node& node);
|
||||
|
||||
virtual void apply(osg::Geode& geode);
|
||||
|
||||
void optimize();
|
||||
|
||||
protected:
|
||||
|
||||
void addStateSet(osg::StateSet* stateset,osg::Object* obj);
|
||||
|
||||
typedef std::set<osg::Object*> ObjectSet;
|
||||
typedef std::map<osg::StateSet*,ObjectSet> StateSetMap;
|
||||
|
||||
StateSetMap _statesets;
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif
|
135
include/osgUtil/Optimizer
Normal file
135
include/osgUtil/Optimizer
Normal file
@ -0,0 +1,135 @@
|
||||
//C++ header - Open Scene Graph - Copyright (C) 1998-2001 Robert Osfield
|
||||
//Distributed under the terms of the GNU Library General Public License (LGPL)
|
||||
//as published by the Free Software Foundation.
|
||||
|
||||
#ifndef OSGUTIL_OPTIMIZER
|
||||
#define OSGUTIL_OPTIMIZER
|
||||
|
||||
#include <osg/NodeVisitor>
|
||||
#include <osg/Matrix>
|
||||
|
||||
#include <osgUtil/Export>
|
||||
|
||||
namespace osgUtil {
|
||||
|
||||
/** Insert impostor nodes into scene graph.
|
||||
* For example of usage see src/Demos/osgimpostor.
|
||||
*/
|
||||
|
||||
class OSGUTIL_EXPORT Optimizer
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
Optimizer() {}
|
||||
|
||||
enum OptimizationOptions
|
||||
{
|
||||
FLATTEN_STATIC_TRANSFORMS = 0x1,
|
||||
REMOVE_REDUNDENT_NODES = 0x2,
|
||||
COMBINE_ADJACENT_LODS = 0x4,
|
||||
SHARE_DUPLICATE_STATE = 0x8,
|
||||
ALL_OPTIMIZATIONS = FLATTEN_STATIC_TRANSFORMS |
|
||||
REMOVE_REDUNDENT_NODES |
|
||||
COMBINE_ADJACENT_LODS |
|
||||
SHARE_DUPLICATE_STATE
|
||||
};
|
||||
|
||||
/** traverse the node and its subgraph with a series of optimization
|
||||
* visitors, specificied by the OptizationOptions.*/
|
||||
virtual void optimize(osg::Node* node, unsigned int options = ALL_OPTIMIZATIONS);
|
||||
|
||||
|
||||
|
||||
/** Flatten Static Trasform nodes by applying their transform to the
|
||||
* geometry on the leaves of the scene graph, then removing the
|
||||
* now redundent transforms.*/
|
||||
class FlattenStaticTransformsVisitor : public osg::NodeVisitor
|
||||
{
|
||||
public:
|
||||
|
||||
typedef std::vector<osg::Matrix> MatrixStack;
|
||||
MatrixStack _matrixStack;
|
||||
|
||||
typedef std::set<osg::Transform*> TransformList;
|
||||
TransformList _transformList;
|
||||
|
||||
FlattenStaticTransformsVisitor():osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) {}
|
||||
|
||||
virtual void apply(osg::Geode& geode);
|
||||
virtual void apply(osg::Billboard& billboard);
|
||||
virtual void apply(osg::LOD& lod);
|
||||
virtual void apply(osg::Transform& transform);
|
||||
|
||||
void removeTransforms();
|
||||
|
||||
};
|
||||
|
||||
/** Remove rendundent nodes, such as groups with one single child.*/
|
||||
class RemoveRedundentNodesVisitor : public osg::NodeVisitor
|
||||
{
|
||||
public:
|
||||
|
||||
typedef std::set<osg::Node*> NodeList;
|
||||
NodeList _redundentNodeList;
|
||||
|
||||
RemoveRedundentNodesVisitor():osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) {}
|
||||
|
||||
virtual void apply(osg::Group& group);
|
||||
|
||||
void removeRedundentNodes();
|
||||
|
||||
};
|
||||
|
||||
/** Optimize the LOD groups, by combining adjacent LOD's which have
|
||||
* complementary ranges.*/
|
||||
class CombineLODsVisitor : public osg::NodeVisitor
|
||||
{
|
||||
public:
|
||||
|
||||
typedef std::set<osg::Group*> GroupList;
|
||||
GroupList _groupList;
|
||||
|
||||
CombineLODsVisitor():osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) {}
|
||||
|
||||
virtual void apply(osg::LOD& lod);
|
||||
|
||||
void combineLODs();
|
||||
|
||||
};
|
||||
|
||||
/** Optimize State in the scene graph by removing duplicate state,
|
||||
* replacing it with shared instances, both for StateAttributes,
|
||||
* and whole StateSets.*/
|
||||
class StateVisitor : public osg::NodeVisitor
|
||||
{
|
||||
public:
|
||||
|
||||
/// default to traversing all children.
|
||||
StateVisitor() : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) {}
|
||||
|
||||
/** empty visitor, make it ready for next traversal.*/
|
||||
virtual void reset();
|
||||
|
||||
virtual void apply(osg::Node& node);
|
||||
|
||||
virtual void apply(osg::Geode& geode);
|
||||
|
||||
void optimize();
|
||||
|
||||
protected:
|
||||
|
||||
void addStateSet(osg::StateSet* stateset,osg::Object* obj);
|
||||
|
||||
typedef std::set<osg::Object*> ObjectSet;
|
||||
typedef std::map<osg::StateSet*,ObjectSet> StateSetMap;
|
||||
|
||||
StateSetMap _statesets;
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif
|
@ -18,319 +18,7 @@
|
||||
#include <osgGLUT/glut>
|
||||
#include <osgGLUT/Viewer>
|
||||
|
||||
#include <osgUtil/OptimizeStateVisitor>
|
||||
|
||||
|
||||
|
||||
class TransformFunctor : public osg::Drawable::AttributeFunctor
|
||||
{
|
||||
public:
|
||||
|
||||
osg::Matrix _m;
|
||||
osg::Matrix _im;
|
||||
|
||||
TransformFunctor(const osg::Matrix& m):
|
||||
osg::Drawable::AttributeFunctor(osg::Drawable::COORDS|osg::Drawable::NORMALS)
|
||||
{
|
||||
_m = m;
|
||||
_im.invert(_m);
|
||||
}
|
||||
|
||||
virtual ~TransformFunctor() {}
|
||||
|
||||
virtual bool apply(osg::Drawable::AttributeBitMask abm,osg::Vec3* begin,osg::Vec3* end)
|
||||
{
|
||||
if (abm == osg::Drawable::COORDS)
|
||||
{
|
||||
for (osg::Vec3* itr=begin;itr<end;++itr)
|
||||
{
|
||||
(*itr) = (*itr)*_m;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else if (abm == osg::Drawable::NORMALS)
|
||||
{
|
||||
for (osg::Vec3* itr=begin;itr<end;++itr)
|
||||
{
|
||||
// note post mult by inverse for normals.
|
||||
(*itr) = osg::Matrix::transform3x3(_im,(*itr));
|
||||
(*itr).normalize();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
class FlattenStaticTransformsVisitor : public osg::NodeVisitor
|
||||
{
|
||||
public:
|
||||
|
||||
typedef std::vector<osg::Matrix> MatrixStack;
|
||||
MatrixStack _matrixStack;
|
||||
|
||||
typedef std::set<osg::Transform*> TransformList;
|
||||
TransformList _transformList;
|
||||
|
||||
FlattenStaticTransformsVisitor():osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) {}
|
||||
|
||||
virtual void apply(osg::Geode& geode)
|
||||
{
|
||||
if (!_matrixStack.empty())
|
||||
{
|
||||
TransformFunctor tf(_matrixStack.back());
|
||||
for(int i=0;i<geode.getNumDrawables();++i)
|
||||
{
|
||||
geode.getDrawable(i)->applyAttributeOperation(tf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual void apply(osg::Billboard& billboard)
|
||||
{
|
||||
if (!_matrixStack.empty())
|
||||
{
|
||||
osg::Matrix& matrix = _matrixStack.back();
|
||||
TransformFunctor tf(matrix);
|
||||
|
||||
osg::Vec3 axis = osg::Matrix::transform3x3(tf._im,billboard.getAxis());
|
||||
billboard.setAxis(axis);
|
||||
|
||||
for(int i=0;i<billboard.getNumDrawables();++i)
|
||||
{
|
||||
billboard.setPos(i,billboard.getPos(i)*matrix);
|
||||
billboard.getDrawable(i)->applyAttributeOperation(tf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual void apply(osg::LOD& lod)
|
||||
{
|
||||
if (!_matrixStack.empty())
|
||||
{
|
||||
lod.setCenter(lod.getCenter()*_matrixStack.back());
|
||||
}
|
||||
traverse(lod);
|
||||
}
|
||||
|
||||
virtual void apply(osg::Transform& transform)
|
||||
{
|
||||
if (_matrixStack.empty())
|
||||
{
|
||||
_matrixStack.push_back(transform.getMatrix());
|
||||
}
|
||||
else
|
||||
{
|
||||
_matrixStack.push_back(transform.getMatrix()*_matrixStack.back());
|
||||
}
|
||||
|
||||
traverse(transform);
|
||||
|
||||
_transformList.insert(&transform);
|
||||
|
||||
// reset the matrix to identity.
|
||||
transform.getMatrix().makeIdent();
|
||||
|
||||
_matrixStack.pop_back();
|
||||
}
|
||||
|
||||
void removeTransforms()
|
||||
{
|
||||
for(TransformList::iterator itr=_transformList.begin();
|
||||
itr!=_transformList.end();
|
||||
++itr)
|
||||
{
|
||||
osg::ref_ptr<osg::Transform> transform = *itr;
|
||||
osg::ref_ptr<osg::Group> group = new osg::Group;
|
||||
|
||||
int i;
|
||||
for(i=0;i<transform->getNumChildren();++i)
|
||||
{
|
||||
for(int j=0;j<transform->getNumParents();++j)
|
||||
{
|
||||
group->addChild(transform->getChild(i));
|
||||
}
|
||||
}
|
||||
|
||||
for(i=transform->getNumParents()-1;i>=0;--i)
|
||||
{
|
||||
transform->getParent(i)->replaceChild(transform.get(),group.get());
|
||||
}
|
||||
|
||||
}
|
||||
_transformList.clear();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
class RemoveRedundentNodesVisitor : public osg::NodeVisitor
|
||||
{
|
||||
public:
|
||||
|
||||
typedef std::set<osg::Node*> NodeList;
|
||||
NodeList _redundentNodeList;
|
||||
|
||||
RemoveRedundentNodesVisitor():osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) {}
|
||||
|
||||
virtual void apply(osg::Group& group)
|
||||
{
|
||||
if (typeid(group)==typeid(osg::Group))
|
||||
{
|
||||
if (group.getNumParents()>0 && group.getNumChildren()<=1)
|
||||
{
|
||||
_redundentNodeList.insert(&group);
|
||||
}
|
||||
}
|
||||
traverse(group);
|
||||
}
|
||||
|
||||
|
||||
void removeRedundentNodes()
|
||||
{
|
||||
for(NodeList::iterator itr=_redundentNodeList.begin();
|
||||
itr!=_redundentNodeList.end();
|
||||
++itr)
|
||||
{
|
||||
osg::ref_ptr<osg::Group> group = dynamic_cast<osg::Group*>(*itr);
|
||||
if (group.valid())
|
||||
{
|
||||
|
||||
for(int j=group->getNumParents()-1;j>=0;--j)
|
||||
{
|
||||
for(int i=0;i<group->getNumChildren();++i)
|
||||
{
|
||||
group->getParent(j)->addChild(group->getChild(i));
|
||||
}
|
||||
group->getParent(j)->removeChild(group.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
_redundentNodeList.clear();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class CombineLODsVisitor : public osg::NodeVisitor
|
||||
{
|
||||
public:
|
||||
|
||||
typedef std::set<osg::Group*> GroupList;
|
||||
GroupList _groupList;
|
||||
|
||||
CombineLODsVisitor():osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) {}
|
||||
|
||||
virtual void apply(osg::LOD& lod)
|
||||
{
|
||||
for(int i=0;i<lod.getNumParents();++i)
|
||||
{
|
||||
if (typeid(*lod.getParent(i))==typeid(osg::Group))
|
||||
{
|
||||
_groupList.insert(lod.getParent(i));
|
||||
}
|
||||
}
|
||||
traverse(lod);
|
||||
}
|
||||
|
||||
|
||||
void combineLODs()
|
||||
{
|
||||
for(GroupList::iterator itr=_groupList.begin();
|
||||
itr!=_groupList.end();
|
||||
++itr)
|
||||
{
|
||||
osg::Group* group = *itr;
|
||||
|
||||
typedef std::set<osg::LOD*> LODSet;
|
||||
|
||||
LODSet lodChildren;
|
||||
|
||||
for(int i=0;i<group->getNumChildren();++i)
|
||||
{
|
||||
osg::Node* child = group->getChild(i);
|
||||
osg::LOD* lod = dynamic_cast<osg::LOD*>(child);
|
||||
if (lod)
|
||||
{
|
||||
if (lod->getNumRanges()-1==lod->getNumChildren())
|
||||
{
|
||||
lodChildren.insert(lod);
|
||||
}
|
||||
else
|
||||
{
|
||||
// wonky LOD, numRanges should = numChildren+1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (lodChildren.size()>=2)
|
||||
{
|
||||
osg::BoundingBox bb;
|
||||
LODSet::iterator lod_itr;
|
||||
for(lod_itr=lodChildren.begin();
|
||||
lod_itr!=lodChildren.end();
|
||||
++lod_itr)
|
||||
{
|
||||
|
||||
bb.expandBy((*lod_itr)->getCenter());
|
||||
}
|
||||
if (bb.radius()<1e-2)
|
||||
{
|
||||
typedef std::pair<float,float> RangePair;
|
||||
typedef std::multimap<RangePair,osg::Node*> RangeMap;
|
||||
RangeMap rangeMap;
|
||||
float maxRange = 0.0f;
|
||||
for(lod_itr=lodChildren.begin();
|
||||
lod_itr!=lodChildren.end();
|
||||
++lod_itr)
|
||||
{
|
||||
|
||||
osg::LOD* lod = *lod_itr;
|
||||
for(int i=0;i<lod->getNumRanges()-1;++i)
|
||||
{
|
||||
if (maxRange<lod->getRange(i+1)) maxRange = lod->getRange(i+1);
|
||||
rangeMap.insert(RangeMap::value_type(RangePair(lod->getRange(i),lod->getRange(i+1)),lod->getChild(i)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// create new LOD containing all other LOD's children.
|
||||
osg::LOD* newLOD = new osg::LOD;
|
||||
newLOD->setName("newLOD");
|
||||
newLOD->setCenter(bb.center());
|
||||
|
||||
int i=0;
|
||||
for(RangeMap::iterator c_itr=rangeMap.begin();
|
||||
c_itr!=rangeMap.end();
|
||||
++c_itr,++i)
|
||||
{
|
||||
newLOD->setRange(i,c_itr->first.first);
|
||||
newLOD->addChild(c_itr->second);
|
||||
}
|
||||
newLOD->setRange(i,maxRange);
|
||||
|
||||
// add LOD into parent.
|
||||
group->addChild(newLOD);
|
||||
|
||||
// remove all the old LOD's from group.
|
||||
for(lod_itr=lodChildren.begin();
|
||||
lod_itr!=lodChildren.end();
|
||||
++lod_itr)
|
||||
{
|
||||
group->removeChild(*lod_itr);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
_groupList.clear();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#include <osgUtil/Optimizer>
|
||||
|
||||
|
||||
/*
|
||||
@ -452,28 +140,10 @@ int main( int argc, char **argv )
|
||||
osg::Timer_t after_load = timer.tick();
|
||||
cout << "Time for load = "<<timer.delta_s(before_load,after_load)<<" seconds"<<endl;
|
||||
|
||||
// note, the Microsoft visual C++ compilers can't handle the STL
|
||||
// in the OptimizeStateVisitor and crash if we run it. For the
|
||||
// time being we'll just not use the optimize visitor under windows.
|
||||
#ifndef WIN32
|
||||
// optimize the state in scene graph, removing duplicate state.
|
||||
osgUtil::OptimizeStateVisitor osv;
|
||||
rootnode->accept(osv);
|
||||
osv.optimize();
|
||||
#endif
|
||||
|
||||
CombineLODsVisitor clv;
|
||||
rootnode->accept(clv);
|
||||
clv.combineLODs();
|
||||
|
||||
FlattenStaticTransformsVisitor fstv;
|
||||
rootnode->accept(fstv);
|
||||
fstv.removeTransforms();
|
||||
|
||||
RemoveRedundentNodesVisitor rrnv;
|
||||
rootnode->accept(rrnv);
|
||||
rrnv.removeRedundentNodes();
|
||||
|
||||
// run optimization over the scene graph
|
||||
osgUtil::Optimizer optimzer;
|
||||
optimzer.optimize(rootnode);
|
||||
|
||||
// initialize the viewer.
|
||||
osgGLUT::Viewer viewer;
|
||||
|
@ -13,7 +13,7 @@ C++FILES = \
|
||||
IntersectVisitor.cpp\
|
||||
InsertImpostorsVisitor.cpp\
|
||||
NvTriStripObjects.cpp\
|
||||
OptimizeStateVisitor.cpp\
|
||||
Optimizer.cpp\
|
||||
RenderBin.cpp\
|
||||
RenderGraph.cpp\
|
||||
RenderLeaf.cpp\
|
||||
@ -55,7 +55,7 @@ TARGET_INCLUDE_FILES = \
|
||||
osgUtil/GUIEventHandler\
|
||||
osgUtil/IntersectVisitor\
|
||||
osgUtil/InsertImpostorsVisitor\
|
||||
osgUtil/OptimizeStateVisitor\
|
||||
osgUtil/Optimizer\
|
||||
osgUtil/RenderBin\
|
||||
osgUtil/RenderGraph\
|
||||
osgUtil/RenderLeaf\
|
||||
|
@ -1,195 +0,0 @@
|
||||
#include <osg/Notify>
|
||||
#include <osg/Geode>
|
||||
|
||||
#include <osgUtil/OptimizeStateVisitor>
|
||||
|
||||
#include <typeinfo>
|
||||
#include <algorithm>
|
||||
|
||||
using namespace osgUtil;
|
||||
|
||||
|
||||
struct LessAttributeFunctor
|
||||
{
|
||||
bool operator () (const osg::StateAttribute* lhs,const osg::StateAttribute* rhs) const
|
||||
{
|
||||
return (*lhs<*rhs);
|
||||
}
|
||||
};
|
||||
|
||||
struct LessStateSetFunctor
|
||||
{
|
||||
bool operator () (const osg::StateSet* lhs,const osg::StateSet* rhs) const
|
||||
{
|
||||
return (*lhs<*rhs);
|
||||
}
|
||||
};
|
||||
|
||||
void OptimizeStateVisitor::reset()
|
||||
{
|
||||
_statesets.clear();
|
||||
}
|
||||
|
||||
void OptimizeStateVisitor::addStateSet(osg::StateSet* stateset,osg::Object* obj)
|
||||
{
|
||||
_statesets[stateset].insert(obj);
|
||||
}
|
||||
|
||||
void OptimizeStateVisitor::apply(osg::Node& node)
|
||||
{
|
||||
osg::StateSet* ss = node.getStateSet();
|
||||
if (ss) addStateSet(ss,&node);
|
||||
|
||||
traverse(node);
|
||||
}
|
||||
|
||||
void OptimizeStateVisitor::apply(osg::Geode& geode)
|
||||
{
|
||||
osg::StateSet* ss = geode.getStateSet();
|
||||
if (ss) addStateSet(ss,&geode);
|
||||
for(int i=0;i<geode.getNumDrawables();++i)
|
||||
{
|
||||
osg::Drawable* drawable = geode.getDrawable(i);
|
||||
if (drawable)
|
||||
{
|
||||
ss = drawable->getStateSet();
|
||||
if (ss) addStateSet(ss,drawable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OptimizeStateVisitor::optimize()
|
||||
{
|
||||
osg::notify(osg::INFO) << "Num of StateSet="<<_statesets.size()<<endl;
|
||||
|
||||
|
||||
{
|
||||
// create map from state attributes to stateset which contain them.
|
||||
typedef std::set<osg::StateSet*> StateSetList;
|
||||
typedef std::map<osg::StateAttribute*,StateSetList> AttributeToStateSetMap;
|
||||
|
||||
AttributeToStateSetMap _attributeToStateSetMap;
|
||||
|
||||
// NOTE will need to track state attribute override value too.
|
||||
|
||||
for(StateSetMap::iterator sitr=_statesets.begin();
|
||||
sitr!=_statesets.end();
|
||||
++sitr)
|
||||
{
|
||||
osg::StateSet::AttributeList& attributes = sitr->first->getAttributeList();
|
||||
for(osg::StateSet::AttributeList::iterator aitr= attributes.begin();
|
||||
aitr!=attributes.end();
|
||||
++aitr)
|
||||
{
|
||||
_attributeToStateSetMap[aitr->second.first.get()].insert(sitr->first);
|
||||
}
|
||||
}
|
||||
|
||||
if (_attributeToStateSetMap.size()<2)
|
||||
{
|
||||
osg::notify(osg::INFO) << "Too few state attributes to optimize."<<endl;
|
||||
return;
|
||||
}
|
||||
|
||||
// create unique set of state attribute pointers.
|
||||
typedef std::vector<osg::StateAttribute*> AttributeList;
|
||||
AttributeList _attributeList;
|
||||
|
||||
for(AttributeToStateSetMap::iterator aitr=_attributeToStateSetMap.begin();
|
||||
aitr!=_attributeToStateSetMap.end();
|
||||
++aitr)
|
||||
{
|
||||
_attributeList.push_back(aitr->first);
|
||||
}
|
||||
|
||||
// sort the attributes so that equal attributes sit along side each
|
||||
// other.
|
||||
std::sort(_attributeList.begin(),_attributeList.end(),LessAttributeFunctor());
|
||||
|
||||
|
||||
osg::notify(osg::INFO) << "state attribute list"<<endl;
|
||||
for(AttributeList::iterator aaitr = _attributeList.begin();
|
||||
aaitr!=_attributeList.end();
|
||||
++aaitr)
|
||||
{
|
||||
osg::notify(osg::INFO) << " "<<*aaitr << " "<<(*aaitr)->className()<<endl;
|
||||
}
|
||||
|
||||
osg::notify(osg::INFO) << "searching for duplicate attributes"<<endl;
|
||||
// find the duplicates.
|
||||
AttributeList::iterator first_unique = _attributeList.begin();
|
||||
AttributeList::iterator current = first_unique; ++current;
|
||||
for(; current!=_attributeList.end();++current)
|
||||
{
|
||||
if (**current==**first_unique)
|
||||
{
|
||||
osg::notify(osg::INFO) << " found duplicate "<<(*current)->className()<<" first="<<*first_unique<<" current="<<*current<<endl;
|
||||
StateSetList& statesetlist = _attributeToStateSetMap[*current];
|
||||
for(StateSetList::iterator sitr=statesetlist.begin();
|
||||
sitr!=statesetlist.end();
|
||||
++sitr)
|
||||
{
|
||||
osg::notify(osg::INFO) << " replace duplicate "<<*current<<" with "<<*first_unique<< endl;
|
||||
osg::StateSet* stateset = *sitr;
|
||||
stateset->setAttribute(*first_unique);
|
||||
}
|
||||
}
|
||||
else first_unique = current;
|
||||
}
|
||||
|
||||
}
|
||||
// duplicate state attributes removed.
|
||||
// now need to look at duplicate state sets.
|
||||
|
||||
{
|
||||
// create the list of stateset's.
|
||||
typedef std::vector<osg::StateSet*> StateSetSortList;
|
||||
StateSetSortList _statesetSortList;
|
||||
for(StateSetMap::iterator ssitr=_statesets.begin();
|
||||
ssitr!=_statesets.end();
|
||||
++ssitr)
|
||||
{
|
||||
_statesetSortList.push_back(ssitr->first);
|
||||
}
|
||||
|
||||
|
||||
// sort the StateSet's so that equal StateSet's sit along side each
|
||||
// other.
|
||||
std::sort(_statesetSortList.begin(),_statesetSortList.end(),LessStateSetFunctor());
|
||||
|
||||
osg::notify(osg::INFO) << "searching for duplicate attributes"<<endl;
|
||||
// find the duplicates.
|
||||
StateSetSortList::iterator first_unique = _statesetSortList.begin();
|
||||
StateSetSortList::iterator current = first_unique; ++current;
|
||||
for(; current!=_statesetSortList.end();++current)
|
||||
{
|
||||
if (**current==**first_unique)
|
||||
{
|
||||
osg::notify(osg::INFO) << " found duplicate "<<(*current)->className()<<" first="<<*first_unique<<" current="<<*current<<endl;
|
||||
ObjectSet& objSet = _statesets[*current];
|
||||
for(ObjectSet::iterator sitr=objSet.begin();
|
||||
sitr!=objSet.end();
|
||||
++sitr)
|
||||
{
|
||||
osg::notify(osg::INFO) << " replace duplicate "<<*current<<" with "<<*first_unique<< endl;
|
||||
osg::Object* obj = *sitr;
|
||||
osg::Drawable* drawable = dynamic_cast<osg::Drawable*>(obj);
|
||||
if (drawable)
|
||||
{
|
||||
drawable->setStateSet(*first_unique);
|
||||
}
|
||||
else
|
||||
{
|
||||
osg::Node* node = dynamic_cast<osg::Node*>(obj);
|
||||
if (node)
|
||||
{
|
||||
node->setStateSet(*first_unique);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else first_unique = current;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
530
src/osgUtil/Optimizer.cpp
Normal file
530
src/osgUtil/Optimizer.cpp
Normal file
@ -0,0 +1,530 @@
|
||||
#include <osgUtil/Optimizer>
|
||||
|
||||
#include <osg/Transform>
|
||||
#include <osg/LOD>
|
||||
#include <osg/Impostor>
|
||||
#include <osg/Billboard>
|
||||
#include <osg/Notify>
|
||||
|
||||
#include <typeinfo>
|
||||
#include <algorithm>
|
||||
|
||||
using namespace osgUtil;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Overall Optimizetion function.
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void Optimizer::optimize(osg::Node* node, unsigned int options)
|
||||
{
|
||||
|
||||
if (options & FLATTEN_STATIC_TRANSFORMS)
|
||||
{
|
||||
FlattenStaticTransformsVisitor fstv;
|
||||
node->accept(fstv);
|
||||
fstv.removeTransforms();
|
||||
}
|
||||
|
||||
if (options & REMOVE_REDUNDENT_NODES)
|
||||
{
|
||||
RemoveRedundentNodesVisitor rrnv;
|
||||
node->accept(rrnv);
|
||||
rrnv.removeRedundentNodes();
|
||||
}
|
||||
|
||||
if (options & COMBINE_ADJACENT_LODS)
|
||||
{
|
||||
CombineLODsVisitor clv;
|
||||
node->accept(clv);
|
||||
clv.combineLODs();
|
||||
}
|
||||
|
||||
if (options & SHARE_DUPLICATE_STATE)
|
||||
{
|
||||
#ifndef WIN32
|
||||
// optimize the state in scene graph, removing duplicate state.
|
||||
#endif
|
||||
StateVisitor osv;
|
||||
node->accept(osv);
|
||||
osv.optimize();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class TransformFunctor : public osg::Drawable::AttributeFunctor
|
||||
{
|
||||
public:
|
||||
|
||||
osg::Matrix _m;
|
||||
osg::Matrix _im;
|
||||
|
||||
TransformFunctor(const osg::Matrix& m):
|
||||
osg::Drawable::AttributeFunctor(osg::Drawable::COORDS|osg::Drawable::NORMALS)
|
||||
{
|
||||
_m = m;
|
||||
_im.invert(_m);
|
||||
}
|
||||
|
||||
virtual ~TransformFunctor() {}
|
||||
|
||||
virtual bool apply(osg::Drawable::AttributeBitMask abm,osg::Vec3* begin,osg::Vec3* end)
|
||||
{
|
||||
if (abm == osg::Drawable::COORDS)
|
||||
{
|
||||
for (osg::Vec3* itr=begin;itr<end;++itr)
|
||||
{
|
||||
(*itr) = (*itr)*_m;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else if (abm == osg::Drawable::NORMALS)
|
||||
{
|
||||
for (osg::Vec3* itr=begin;itr<end;++itr)
|
||||
{
|
||||
// note post mult by inverse for normals.
|
||||
(*itr) = osg::Matrix::transform3x3(_im,(*itr));
|
||||
(*itr).normalize();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Optimize State Visitor
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct LessAttributeFunctor
|
||||
{
|
||||
bool operator () (const osg::StateAttribute* lhs,const osg::StateAttribute* rhs) const
|
||||
{
|
||||
return (*lhs<*rhs);
|
||||
}
|
||||
};
|
||||
|
||||
struct LessStateSetFunctor
|
||||
{
|
||||
bool operator () (const osg::StateSet* lhs,const osg::StateSet* rhs) const
|
||||
{
|
||||
return (*lhs<*rhs);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
void Optimizer::StateVisitor::reset()
|
||||
{
|
||||
_statesets.clear();
|
||||
}
|
||||
|
||||
void Optimizer::StateVisitor::addStateSet(osg::StateSet* stateset,osg::Object* obj)
|
||||
{
|
||||
_statesets[stateset].insert(obj);
|
||||
}
|
||||
|
||||
void Optimizer::StateVisitor::apply(osg::Node& node)
|
||||
{
|
||||
osg::StateSet* ss = node.getStateSet();
|
||||
if (ss) addStateSet(ss,&node);
|
||||
|
||||
traverse(node);
|
||||
}
|
||||
|
||||
void Optimizer::StateVisitor::apply(osg::Geode& geode)
|
||||
{
|
||||
osg::StateSet* ss = geode.getStateSet();
|
||||
if (ss) addStateSet(ss,&geode);
|
||||
for(int i=0;i<geode.getNumDrawables();++i)
|
||||
{
|
||||
osg::Drawable* drawable = geode.getDrawable(i);
|
||||
if (drawable)
|
||||
{
|
||||
ss = drawable->getStateSet();
|
||||
if (ss) addStateSet(ss,drawable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Optimizer::StateVisitor::optimize()
|
||||
{
|
||||
osg::notify(osg::INFO) << "Num of StateSet="<<_statesets.size()<<endl;
|
||||
|
||||
|
||||
{
|
||||
// create map from state attributes to stateset which contain them.
|
||||
typedef std::set<osg::StateSet*> StateSetList;
|
||||
typedef std::map<osg::StateAttribute*,StateSetList> AttributeToStateSetMap;
|
||||
|
||||
AttributeToStateSetMap _attributeToStateSetMap;
|
||||
|
||||
// NOTE will need to track state attribute override value too.
|
||||
|
||||
for(StateSetMap::iterator sitr=_statesets.begin();
|
||||
sitr!=_statesets.end();
|
||||
++sitr)
|
||||
{
|
||||
osg::StateSet::AttributeList& attributes = sitr->first->getAttributeList();
|
||||
for(osg::StateSet::AttributeList::iterator aitr= attributes.begin();
|
||||
aitr!=attributes.end();
|
||||
++aitr)
|
||||
{
|
||||
_attributeToStateSetMap[aitr->second.first.get()].insert(sitr->first);
|
||||
}
|
||||
}
|
||||
|
||||
if (_attributeToStateSetMap.size()<2)
|
||||
{
|
||||
osg::notify(osg::INFO) << "Too few state attributes to optimize."<<endl;
|
||||
return;
|
||||
}
|
||||
|
||||
// create unique set of state attribute pointers.
|
||||
typedef std::vector<osg::StateAttribute*> AttributeList;
|
||||
AttributeList _attributeList;
|
||||
|
||||
for(AttributeToStateSetMap::iterator aitr=_attributeToStateSetMap.begin();
|
||||
aitr!=_attributeToStateSetMap.end();
|
||||
++aitr)
|
||||
{
|
||||
_attributeList.push_back(aitr->first);
|
||||
}
|
||||
|
||||
// sort the attributes so that equal attributes sit along side each
|
||||
// other.
|
||||
std::sort(_attributeList.begin(),_attributeList.end(),LessAttributeFunctor());
|
||||
|
||||
|
||||
osg::notify(osg::INFO) << "state attribute list"<<endl;
|
||||
for(AttributeList::iterator aaitr = _attributeList.begin();
|
||||
aaitr!=_attributeList.end();
|
||||
++aaitr)
|
||||
{
|
||||
osg::notify(osg::INFO) << " "<<*aaitr << " "<<(*aaitr)->className()<<endl;
|
||||
}
|
||||
|
||||
osg::notify(osg::INFO) << "searching for duplicate attributes"<<endl;
|
||||
// find the duplicates.
|
||||
AttributeList::iterator first_unique = _attributeList.begin();
|
||||
AttributeList::iterator current = first_unique; ++current;
|
||||
for(; current!=_attributeList.end();++current)
|
||||
{
|
||||
if (**current==**first_unique)
|
||||
{
|
||||
osg::notify(osg::INFO) << " found duplicate "<<(*current)->className()<<" first="<<*first_unique<<" current="<<*current<<endl;
|
||||
StateSetList& statesetlist = _attributeToStateSetMap[*current];
|
||||
for(StateSetList::iterator sitr=statesetlist.begin();
|
||||
sitr!=statesetlist.end();
|
||||
++sitr)
|
||||
{
|
||||
osg::notify(osg::INFO) << " replace duplicate "<<*current<<" with "<<*first_unique<< endl;
|
||||
osg::StateSet* stateset = *sitr;
|
||||
stateset->setAttribute(*first_unique);
|
||||
}
|
||||
}
|
||||
else first_unique = current;
|
||||
}
|
||||
|
||||
}
|
||||
// duplicate state attributes removed.
|
||||
// now need to look at duplicate state sets.
|
||||
|
||||
{
|
||||
// create the list of stateset's.
|
||||
typedef std::vector<osg::StateSet*> StateSetSortList;
|
||||
StateSetSortList _statesetSortList;
|
||||
for(StateSetMap::iterator ssitr=_statesets.begin();
|
||||
ssitr!=_statesets.end();
|
||||
++ssitr)
|
||||
{
|
||||
_statesetSortList.push_back(ssitr->first);
|
||||
}
|
||||
|
||||
|
||||
// sort the StateSet's so that equal StateSet's sit along side each
|
||||
// other.
|
||||
std::sort(_statesetSortList.begin(),_statesetSortList.end(),LessStateSetFunctor());
|
||||
|
||||
osg::notify(osg::INFO) << "searching for duplicate attributes"<<endl;
|
||||
// find the duplicates.
|
||||
StateSetSortList::iterator first_unique = _statesetSortList.begin();
|
||||
StateSetSortList::iterator current = first_unique; ++current;
|
||||
for(; current!=_statesetSortList.end();++current)
|
||||
{
|
||||
if (**current==**first_unique)
|
||||
{
|
||||
osg::notify(osg::INFO) << " found duplicate "<<(*current)->className()<<" first="<<*first_unique<<" current="<<*current<<endl;
|
||||
ObjectSet& objSet = _statesets[*current];
|
||||
for(ObjectSet::iterator sitr=objSet.begin();
|
||||
sitr!=objSet.end();
|
||||
++sitr)
|
||||
{
|
||||
osg::notify(osg::INFO) << " replace duplicate "<<*current<<" with "<<*first_unique<< endl;
|
||||
osg::Object* obj = *sitr;
|
||||
osg::Drawable* drawable = dynamic_cast<osg::Drawable*>(obj);
|
||||
if (drawable)
|
||||
{
|
||||
drawable->setStateSet(*first_unique);
|
||||
}
|
||||
else
|
||||
{
|
||||
osg::Node* node = dynamic_cast<osg::Node*>(obj);
|
||||
if (node)
|
||||
{
|
||||
node->setStateSet(*first_unique);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else first_unique = current;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Flatten static transforms
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void Optimizer::FlattenStaticTransformsVisitor::apply(osg::Geode& geode)
|
||||
{
|
||||
if (!_matrixStack.empty())
|
||||
{
|
||||
TransformFunctor tf(_matrixStack.back());
|
||||
for(int i=0;i<geode.getNumDrawables();++i)
|
||||
{
|
||||
geode.getDrawable(i)->applyAttributeOperation(tf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Optimizer::FlattenStaticTransformsVisitor::apply(osg::Billboard& billboard)
|
||||
{
|
||||
if (!_matrixStack.empty())
|
||||
{
|
||||
osg::Matrix& matrix = _matrixStack.back();
|
||||
TransformFunctor tf(matrix);
|
||||
|
||||
osg::Vec3 axis = osg::Matrix::transform3x3(tf._im,billboard.getAxis());
|
||||
billboard.setAxis(axis);
|
||||
|
||||
for(int i=0;i<billboard.getNumDrawables();++i)
|
||||
{
|
||||
billboard.setPos(i,billboard.getPos(i)*matrix);
|
||||
billboard.getDrawable(i)->applyAttributeOperation(tf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Optimizer::FlattenStaticTransformsVisitor::apply(osg::LOD& lod)
|
||||
{
|
||||
if (!_matrixStack.empty())
|
||||
{
|
||||
lod.setCenter(lod.getCenter()*_matrixStack.back());
|
||||
}
|
||||
traverse(lod);
|
||||
}
|
||||
|
||||
void Optimizer::FlattenStaticTransformsVisitor::apply(osg::Transform& transform)
|
||||
{
|
||||
if (_matrixStack.empty())
|
||||
{
|
||||
_matrixStack.push_back(transform.getMatrix());
|
||||
}
|
||||
else
|
||||
{
|
||||
_matrixStack.push_back(transform.getMatrix()*_matrixStack.back());
|
||||
}
|
||||
|
||||
traverse(transform);
|
||||
|
||||
_transformList.insert(&transform);
|
||||
|
||||
// reset the matrix to identity.
|
||||
transform.getMatrix().makeIdent();
|
||||
|
||||
_matrixStack.pop_back();
|
||||
}
|
||||
|
||||
void Optimizer::FlattenStaticTransformsVisitor::removeTransforms()
|
||||
{
|
||||
for(TransformList::iterator itr=_transformList.begin();
|
||||
itr!=_transformList.end();
|
||||
++itr)
|
||||
{
|
||||
osg::ref_ptr<osg::Transform> transform = *itr;
|
||||
osg::ref_ptr<osg::Group> group = new osg::Group;
|
||||
|
||||
int i;
|
||||
for(i=0;i<transform->getNumChildren();++i)
|
||||
{
|
||||
for(int j=0;j<transform->getNumParents();++j)
|
||||
{
|
||||
group->addChild(transform->getChild(i));
|
||||
}
|
||||
}
|
||||
|
||||
for(i=transform->getNumParents()-1;i>=0;--i)
|
||||
{
|
||||
transform->getParent(i)->replaceChild(transform.get(),group.get());
|
||||
}
|
||||
|
||||
}
|
||||
_transformList.clear();
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// RemoveRedundentNodes.
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void Optimizer::RemoveRedundentNodesVisitor::apply(osg::Group& group)
|
||||
{
|
||||
if (typeid(group)==typeid(osg::Group))
|
||||
{
|
||||
if (group.getNumParents()>0 && group.getNumChildren()<=1)
|
||||
{
|
||||
_redundentNodeList.insert(&group);
|
||||
}
|
||||
}
|
||||
traverse(group);
|
||||
}
|
||||
|
||||
|
||||
void Optimizer::RemoveRedundentNodesVisitor::removeRedundentNodes()
|
||||
{
|
||||
for(NodeList::iterator itr=_redundentNodeList.begin();
|
||||
itr!=_redundentNodeList.end();
|
||||
++itr)
|
||||
{
|
||||
osg::ref_ptr<osg::Group> group = dynamic_cast<osg::Group*>(*itr);
|
||||
if (group.valid())
|
||||
{
|
||||
|
||||
for(int j=group->getNumParents()-1;j>=0;--j)
|
||||
{
|
||||
for(int i=0;i<group->getNumChildren();++i)
|
||||
{
|
||||
group->getParent(j)->addChild(group->getChild(i));
|
||||
}
|
||||
group->getParent(j)->removeChild(group.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
_redundentNodeList.clear();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// combine LOD's.
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
void Optimizer::CombineLODsVisitor::apply(osg::LOD& lod)
|
||||
{
|
||||
for(int i=0;i<lod.getNumParents();++i)
|
||||
{
|
||||
if (typeid(*lod.getParent(i))==typeid(osg::Group))
|
||||
{
|
||||
_groupList.insert(lod.getParent(i));
|
||||
}
|
||||
}
|
||||
traverse(lod);
|
||||
}
|
||||
|
||||
|
||||
void Optimizer::CombineLODsVisitor::combineLODs()
|
||||
{
|
||||
for(GroupList::iterator itr=_groupList.begin();
|
||||
itr!=_groupList.end();
|
||||
++itr)
|
||||
{
|
||||
osg::Group* group = *itr;
|
||||
|
||||
typedef std::set<osg::LOD*> LODSet;
|
||||
|
||||
LODSet lodChildren;
|
||||
|
||||
for(int i=0;i<group->getNumChildren();++i)
|
||||
{
|
||||
osg::Node* child = group->getChild(i);
|
||||
osg::LOD* lod = dynamic_cast<osg::LOD*>(child);
|
||||
if (lod)
|
||||
{
|
||||
if (lod->getNumRanges()-1==lod->getNumChildren())
|
||||
{
|
||||
lodChildren.insert(lod);
|
||||
}
|
||||
else
|
||||
{
|
||||
// wonky LOD, numRanges should = numChildren+1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (lodChildren.size()>=2)
|
||||
{
|
||||
osg::BoundingBox bb;
|
||||
LODSet::iterator lod_itr;
|
||||
for(lod_itr=lodChildren.begin();
|
||||
lod_itr!=lodChildren.end();
|
||||
++lod_itr)
|
||||
{
|
||||
|
||||
bb.expandBy((*lod_itr)->getCenter());
|
||||
}
|
||||
if (bb.radius()<1e-2)
|
||||
{
|
||||
typedef std::pair<float,float> RangePair;
|
||||
typedef std::multimap<RangePair,osg::Node*> RangeMap;
|
||||
RangeMap rangeMap;
|
||||
float maxRange = 0.0f;
|
||||
for(lod_itr=lodChildren.begin();
|
||||
lod_itr!=lodChildren.end();
|
||||
++lod_itr)
|
||||
{
|
||||
|
||||
osg::LOD* lod = *lod_itr;
|
||||
for(int i=0;i<lod->getNumRanges()-1;++i)
|
||||
{
|
||||
if (maxRange<lod->getRange(i+1)) maxRange = lod->getRange(i+1);
|
||||
rangeMap.insert(RangeMap::value_type(RangePair(lod->getRange(i),lod->getRange(i+1)),lod->getChild(i)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// create new LOD containing all other LOD's children.
|
||||
osg::LOD* newLOD = new osg::LOD;
|
||||
newLOD->setName("newLOD");
|
||||
newLOD->setCenter(bb.center());
|
||||
|
||||
int i=0;
|
||||
for(RangeMap::iterator c_itr=rangeMap.begin();
|
||||
c_itr!=rangeMap.end();
|
||||
++c_itr,++i)
|
||||
{
|
||||
newLOD->setRange(i,c_itr->first.first);
|
||||
newLOD->addChild(c_itr->second);
|
||||
}
|
||||
newLOD->setRange(i,maxRange);
|
||||
|
||||
// add LOD into parent.
|
||||
group->addChild(newLOD);
|
||||
|
||||
// remove all the old LOD's from group.
|
||||
for(lod_itr=lodChildren.begin();
|
||||
lod_itr!=lodChildren.end();
|
||||
++lod_itr)
|
||||
{
|
||||
group->removeChild(*lod_itr);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
_groupList.clear();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user