OpenSceneGraph/include/osgUtil/Optimizer

718 lines
28 KiB
Plaintext
Raw Normal View History

2006-07-18 23:21:48 +08:00
/* -*-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_OPTIMIZER
#define OSGUTIL_OPTIMIZER
#include <osg/NodeVisitor>
#include <osg/Matrix>
#include <osg/Geometry>
#include <osg/Transform>
#include <osg/Texture2D>
#include <osgUtil/Export>
#include <set>
namespace osgUtil {
// forward declare
class Optimizer;
/** Helper base class for implementing Optimizer techniques.*/
class OSGUTIL_EXPORT BaseOptimizerVisitor : public osg::NodeVisitor
{
public:
BaseOptimizerVisitor(Optimizer* optimizer, unsigned int operation):
osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
_optimizer(optimizer),
_operationType(operation)
{
setNodeMaskOverride(0xffffffff);
}
inline bool isOperationPermissibleForObject(const osg::StateSet* object) const;
inline bool isOperationPermissibleForObject(const osg::StateAttribute* object) const;
inline bool isOperationPermissibleForObject(const osg::Drawable* object) const;
inline bool isOperationPermissibleForObject(const osg::Node* object) const;
protected:
Optimizer* _optimizer;
unsigned int _operationType;
};
/** Traverses scene graph to improve efficiency. See OptimizationOptions.
* For example of usage see examples/osgimpostor or osgviewer.
*/
class OSGUTIL_EXPORT Optimizer
{
public:
Optimizer() {}
virtual ~Optimizer() {}
enum OptimizationOptions
{
FLATTEN_STATIC_TRANSFORMS = 0x001,
REMOVE_REDUNDANT_NODES = 0x002,
REMOVE_LOADED_PROXY_NODES = 0x004,
COMBINE_ADJACENT_LODS = 0x008,
SHARE_DUPLICATE_STATE = 0x010,
MERGE_GEOMETRY = 0x020,
CHECK_GEOMETRY = 0x040,
SPATIALIZE_GROUPS = 0x080,
COPY_SHARED_NODES = 0x100,
TRISTRIP_GEOMETRY = 0x200,
TESSELATE_GEOMETRY = 0x400,
OPTIMIZE_TEXTURE_SETTINGS = 0x800,
MERGE_GEODES = 0x1000,
FLATTEN_BILLBOARDS = 0x2000,
DEFAULT_OPTIMIZATIONS = FLATTEN_STATIC_TRANSFORMS |
REMOVE_REDUNDANT_NODES |
REMOVE_LOADED_PROXY_NODES |
COMBINE_ADJACENT_LODS |
SHARE_DUPLICATE_STATE |
MERGE_GEOMETRY |
CHECK_GEOMETRY |
OPTIMIZE_TEXTURE_SETTINGS,
ALL_OPTIMIZATIONS = FLATTEN_STATIC_TRANSFORMS |
REMOVE_REDUNDANT_NODES |
REMOVE_LOADED_PROXY_NODES |
COMBINE_ADJACENT_LODS |
SHARE_DUPLICATE_STATE |
MERGE_GEODES |
MERGE_GEOMETRY |
CHECK_GEOMETRY |
SPATIALIZE_GROUPS |
COPY_SHARED_NODES |
TRISTRIP_GEOMETRY |
OPTIMIZE_TEXTURE_SETTINGS
};
/** Reset internal data to initial state - the getPermissibleOptionsMap is cleared.*/
void reset();
/** Traverse the node and its subgraph with a series of optimization
* visitors, specified by the OptimizationOptions.*/
void optimize(osg::Node* node);
/** Traverse the node and its subgraph with a series of optimization
* visitors, specified by the OptimizationOptions.*/
virtual void optimize(osg::Node* node, unsigned int options);
/** Callback for customizing what operations are permitted on objects in the scene graph.*/
struct IsOperationPermissibleForObjectCallback : public osg::Referenced
{
virtual bool isOperationPermissibleForObjectImplementation(const Optimizer* optimizer, const osg::StateSet* stateset,unsigned int option) const
{
return optimizer->isOperationPermissibleForObjectImplementation(stateset,option);
}
virtual bool isOperationPermissibleForObjectImplementation(const Optimizer* optimizer, const osg::StateAttribute* attribute,unsigned int option) const
{
return optimizer->isOperationPermissibleForObjectImplementation(attribute,option);
}
virtual bool isOperationPermissibleForObjectImplementation(const Optimizer* optimizer, const osg::Drawable* drawable,unsigned int option) const
{
return optimizer->isOperationPermissibleForObjectImplementation(drawable,option);
}
virtual bool isOperationPermissibleForObjectImplementation(const Optimizer* optimizer, const osg::Node* node,unsigned int option) const
{
return optimizer->isOperationPermissibleForObjectImplementation(node,option);
}
};
/** Set the callback for customizing what operations are permitted on objects in the scene graph.*/
void setIsOperationPermissibleForObjectCallback(IsOperationPermissibleForObjectCallback* callback) { _isOperationPermissibleForObjectCallback=callback; }
/** Get the callback for customizing what operations are permitted on objects in the scene graph.*/
IsOperationPermissibleForObjectCallback* getIsOperationPermissibleForObjectCallback() { return _isOperationPermissibleForObjectCallback.get(); }
/** Get the callback for customizing what operations are permitted on objects in the scene graph.*/
const IsOperationPermissibleForObjectCallback* getIsOperationPermissibleForObjectCallback() const { return _isOperationPermissibleForObjectCallback.get(); }
inline void setPermissibleOptimizationsForObject(const osg::Object* object, unsigned int options)
{
_permissibleOptimizationsMap[object] = options;
}
inline unsigned int getPermissibleOptimizationsForObject(const osg::Object* object) const
{
PermissibleOptimizationsMap::const_iterator itr = _permissibleOptimizationsMap.find(object);
if (itr!=_permissibleOptimizationsMap.end()) return itr->second;
else return 0xffffffff;
}
inline bool isOperationPermissibleForObject(const osg::StateSet* object, unsigned int option) const
{
if (_isOperationPermissibleForObjectCallback.valid())
return _isOperationPermissibleForObjectCallback->isOperationPermissibleForObjectImplementation(this,object,option);
else
2005-03-24 04:35:19 +08:00
return isOperationPermissibleForObjectImplementation(object,option);
}
inline bool isOperationPermissibleForObject(const osg::StateAttribute* object, unsigned int option) const
{
if (_isOperationPermissibleForObjectCallback.valid())
return _isOperationPermissibleForObjectCallback->isOperationPermissibleForObjectImplementation(this,object,option);
else
2005-03-24 04:35:19 +08:00
return isOperationPermissibleForObjectImplementation(object,option);
}
inline bool isOperationPermissibleForObject(const osg::Drawable* object, unsigned int option) const
{
if (_isOperationPermissibleForObjectCallback.valid())
return _isOperationPermissibleForObjectCallback->isOperationPermissibleForObjectImplementation(this,object,option);
else
2005-03-24 04:35:19 +08:00
return isOperationPermissibleForObjectImplementation(object,option);
}
inline bool isOperationPermissibleForObject(const osg::Node* object, unsigned int option) const
{
if (_isOperationPermissibleForObjectCallback.valid())
return _isOperationPermissibleForObjectCallback->isOperationPermissibleForObjectImplementation(this,object,option);
else
2005-03-24 04:35:19 +08:00
return isOperationPermissibleForObjectImplementation(object,option);
}
bool isOperationPermissibleForObjectImplementation(const osg::StateSet* stateset, unsigned int option) const
{
return (option & getPermissibleOptimizationsForObject(stateset))!=0;
}
bool isOperationPermissibleForObjectImplementation(const osg::StateAttribute* attribute, unsigned int option) const
{
return (option & getPermissibleOptimizationsForObject(attribute))!=0;
}
bool isOperationPermissibleForObjectImplementation(const osg::Drawable* drawable, unsigned int option) const
{
if (option & (REMOVE_REDUNDANT_NODES|MERGE_GEOMETRY))
{
if (drawable->getUserData()) return false;
if (drawable->getUpdateCallback()) return false;
if (drawable->getEventCallback()) return false;
if (drawable->getCullCallback()) return false;
}
return (option & getPermissibleOptimizationsForObject(drawable))!=0;
}
bool isOperationPermissibleForObjectImplementation(const osg::Node* node, unsigned int option) const
{
if (option & (REMOVE_REDUNDANT_NODES|COMBINE_ADJACENT_LODS|FLATTEN_STATIC_TRANSFORMS))
{
if (node->getUserData()) return false;
if (node->getUpdateCallback()) return false;
if (node->getEventCallback()) return false;
if (node->getCullCallback()) return false;
if (node->getNumDescriptions()>0) return false;
if (node->getStateSet()) return false;
if (node->getNodeMask()!=0xffffffff) return false;
// if (!node->getName().empty()) return false;
}
return (option & getPermissibleOptimizationsForObject(node))!=0;
}
protected:
osg::ref_ptr<IsOperationPermissibleForObjectCallback> _isOperationPermissibleForObjectCallback;
typedef std::map<const osg::Object*,unsigned int> PermissibleOptimizationsMap;
PermissibleOptimizationsMap _permissibleOptimizationsMap;
public:
/** Flatten Static Transform nodes by applying their transform to the
* geometry on the leaves of the scene graph, then removing the
* now redundant transforms.*/
class OSGUTIL_EXPORT FlattenStaticTransformsVisitor : public BaseOptimizerVisitor
{
public:
FlattenStaticTransformsVisitor(Optimizer* optimizer=0):
BaseOptimizerVisitor(optimizer, FLATTEN_STATIC_TRANSFORMS) {}
virtual void apply(osg::Node& geode);
virtual void apply(osg::Geode& geode);
virtual void apply(osg::Billboard& geode);
virtual void apply(osg::ProxyNode& node);
virtual void apply(osg::Transform& transform);
bool removeTransforms(osg::Node* nodeWeCannotRemove);
protected:
typedef std::vector<osg::Transform*> TransformStack;
typedef std::set<osg::Drawable*> DrawableSet;
typedef std::set<osg::Billboard*> BillboardSet;
typedef std::set<osg::Node* > NodeSet;
typedef std::set<osg::Transform*> TransformSet;
TransformStack _transformStack;
NodeSet _excludedNodeSet;
DrawableSet _drawableSet;
BillboardSet _billboardSet;
TransformSet _transformSet;
};
/** Combine Static Transform nodes that sit above one another.*/
class OSGUTIL_EXPORT CombineStaticTransformsVisitor : public BaseOptimizerVisitor
{
public:
CombineStaticTransformsVisitor(Optimizer* optimizer=0):
BaseOptimizerVisitor(optimizer, FLATTEN_STATIC_TRANSFORMS) {}
virtual void apply(osg::MatrixTransform& transform);
bool removeTransforms(osg::Node* nodeWeCannotRemove);
protected:
typedef std::set<osg::MatrixTransform*> TransformSet;
TransformSet _transformSet;
};
/** Remove rendundant nodes, such as groups with one single child.*/
class OSGUTIL_EXPORT RemoveEmptyNodesVisitor : public BaseOptimizerVisitor
{
public:
typedef std::set<osg::Node*> NodeList;
NodeList _redundantNodeList;
RemoveEmptyNodesVisitor(Optimizer* optimizer=0):
BaseOptimizerVisitor(optimizer, REMOVE_REDUNDANT_NODES) {}
virtual void apply(osg::Geode& geode);
virtual void apply(osg::Group& group);
void removeEmptyNodes();
};
/** Remove redundant nodes, such as groups with one single child.*/
class OSGUTIL_EXPORT RemoveRedundantNodesVisitor : public BaseOptimizerVisitor
{
public:
typedef std::set<osg::Node*> NodeList;
NodeList _redundantNodeList;
RemoveRedundantNodesVisitor(Optimizer* optimizer=0):
BaseOptimizerVisitor(optimizer, REMOVE_REDUNDANT_NODES) {}
virtual void apply(osg::Group& group);
virtual void apply(osg::Transform& transform);
void removeRedundantNodes();
};
/** Remove loaded proxy nodes.*/
class OSGUTIL_EXPORT RemoveLoadedProxyNodesVisitor : public BaseOptimizerVisitor
{
public:
typedef std::set<osg::Node*> NodeList;
NodeList _redundantNodeList;
RemoveLoadedProxyNodesVisitor(Optimizer* optimizer=0):
BaseOptimizerVisitor(optimizer, REMOVE_LOADED_PROXY_NODES) {}
virtual void apply(osg::ProxyNode& group);
void removeRedundantNodes();
};
/** Tesselate all geodes, to remove POLYGONS.*/
class OSGUTIL_EXPORT TesselateVisitor : public BaseOptimizerVisitor
{
public:
typedef std::set<osg::Group*> GroupList;
GroupList _groupList;
TesselateVisitor(Optimizer* optimizer=0):
BaseOptimizerVisitor(optimizer, TESSELATE_GEOMETRY) {}
virtual void apply(osg::Geode& geode);
};
/** Optimize the LOD groups, by combining adjacent LOD's which have
* complementary ranges.*/
class OSGUTIL_EXPORT CombineLODsVisitor : public BaseOptimizerVisitor
{
public:
typedef std::set<osg::Group*> GroupList;
GroupList _groupList;
CombineLODsVisitor(Optimizer* optimizer=0):
BaseOptimizerVisitor(optimizer, COMBINE_ADJACENT_LODS) {}
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 OSGUTIL_EXPORT StateVisitor : public BaseOptimizerVisitor
{
public:
/// default to traversing all children.
StateVisitor(Optimizer* optimizer=0):
BaseOptimizerVisitor(optimizer, SHARE_DUPLICATE_STATE) {}
/** 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;
};
/** Combine geodes
*/
class OSGUTIL_EXPORT MergeGeodesVisitor : public BaseOptimizerVisitor
{
public:
/// default to traversing all children.
MergeGeodesVisitor(Optimizer* optimizer=0):
BaseOptimizerVisitor(optimizer, MERGE_GEODES) {}
virtual void apply(osg::Group& group);
bool mergeGeodes(osg::Group& group);
protected:
bool mergeGeode(osg::Geode& lhs, osg::Geode& rhs);
};
class OSGUTIL_EXPORT CheckGeometryVisitor : public BaseOptimizerVisitor
{
public:
/// default to traversing all children.
CheckGeometryVisitor(Optimizer* optimizer=0):
BaseOptimizerVisitor(optimizer, CHECK_GEOMETRY) {}
virtual void apply(osg::Geode& geode) { checkGeode(geode); }
void checkGeode(osg::Geode& geode);
};
class OSGUTIL_EXPORT MergeGeometryVisitor : public BaseOptimizerVisitor
{
public:
/// default to traversing all children.
MergeGeometryVisitor(Optimizer* optimizer=0) :
BaseOptimizerVisitor(optimizer, MERGE_GEOMETRY),
_targetMaximumNumberOfVertices(10000) {}
void setTargetMaximumNumberOfVertices(unsigned int num)
{
_targetMaximumNumberOfVertices = num;
}
unsigned int getTargetMaximumNumberOfVertices() const
{
return _targetMaximumNumberOfVertices;
}
virtual void apply(osg::Geode& geode) { mergeGeode(geode); }
virtual void apply(osg::Billboard&) { /* don't do anything*/ }
bool mergeGeode(osg::Geode& geode);
static bool geometryContainsSharedArrays(osg::Geometry& geom);
static bool mergeGeometry(osg::Geometry& lhs,osg::Geometry& rhs);
static bool mergePrimitive(osg::DrawArrays& lhs,osg::DrawArrays& rhs);
static bool mergePrimitive(osg::DrawArrayLengths& lhs,osg::DrawArrayLengths& rhs);
static bool mergePrimitive(osg::DrawElementsUByte& lhs,osg::DrawElementsUByte& rhs);
static bool mergePrimitive(osg::DrawElementsUShort& lhs,osg::DrawElementsUShort& rhs);
static bool mergePrimitive(osg::DrawElementsUInt& lhs,osg::DrawElementsUInt& rhs);
protected:
unsigned int _targetMaximumNumberOfVertices;
};
/** Spatialize scene into a balanced quad/oct tree.*/
class OSGUTIL_EXPORT SpatializeGroupsVisitor : public BaseOptimizerVisitor
{
public:
SpatializeGroupsVisitor(Optimizer* optimizer=0):
BaseOptimizerVisitor(optimizer, SPATIALIZE_GROUPS) {}
virtual void apply(osg::Group& group);
bool divide(unsigned int maxNumTreesPerCell=8);
bool divide(osg::Group* group, unsigned int maxNumTreesPerCell);
typedef std::set<osg::Group*> GroupsToDivideList;
GroupsToDivideList _groupsToDivideList;
};
/** Copy any shared subgraphs, enabling flattening of static transforms.*/
class OSGUTIL_EXPORT CopySharedSubgraphsVisitor : public BaseOptimizerVisitor
{
public:
CopySharedSubgraphsVisitor(Optimizer* optimizer=0):
BaseOptimizerVisitor(optimizer, COPY_SHARED_NODES) {}
virtual void apply(osg::Node& node);
void copySharedNodes();
typedef std::set<osg::Node*> SharedNodeList;
SharedNodeList _sharedNodeList;
};
/** For all textures apply settings.*/
class OSGUTIL_EXPORT TextureVisitor : public BaseOptimizerVisitor
{
public:
TextureVisitor(bool changeAutoUnRef, bool valueAutoUnRef,
bool changeClientImageStorage, bool valueClientImageStorage,
bool changeAnisotropy, float valueAnisotropy,
Optimizer* optimizer=0):
BaseOptimizerVisitor(optimizer, OPTIMIZE_TEXTURE_SETTINGS),
_changeAutoUnRef(changeAutoUnRef), _valueAutoUnRef(valueAutoUnRef),
_changeClientImageStorage(changeClientImageStorage), _valueClientImageStorage(valueClientImageStorage),
_changeAnisotropy(changeAnisotropy), _valueAnisotropy(valueAnisotropy) {}
virtual void apply(osg::Geode& node);
virtual void apply(osg::Node& node);
void apply(osg::StateSet& stateset);
void apply(osg::Texture& texture);
bool _changeAutoUnRef, _valueAutoUnRef;
bool _changeClientImageStorage, _valueClientImageStorage;
bool _changeAnisotropy;
float _valueAnisotropy;
};
/** Flatten MatrixTransform/Billboard pairs.*/
class OSGUTIL_EXPORT FlattenBillboardVisitor : public BaseOptimizerVisitor
{
public:
FlattenBillboardVisitor(Optimizer* optimizer=0):
BaseOptimizerVisitor(optimizer, FLATTEN_BILLBOARDS) {}
typedef std::vector<osg::NodePath> NodePathList;
typedef std::map<osg::Billboard*, NodePathList > BillboardNodePathMap;
virtual void reset();
virtual void apply(osg::Billboard& billboard);
void process();
BillboardNodePathMap _billboards;
};
/** Texture Atlas Builder creates a set of textures/images which each contain multiple images.
* Texture Atlas' are used to make it possible to use much wider batching of data. */
class OSGUTIL_EXPORT TextureAtlasBuilder
{
public:
TextureAtlasBuilder();
void setMaximumAtlasSize(unsigned int width, unsigned int height);
unsigned int getMaximumAtlasWidth() const { return _maximumAtlasWidth; }
unsigned int getMaximumAtlasHeight() const { return _maximumAtlasHeight; }
void setMargin(unsigned int margin);
unsigned int getMargin() const { return _margin; }
void addSource(const osg::Image* image);
void addSource(const osg::Texture2D* texture);
unsigned int getNumSources() const { return _sourceList.size(); }
const osg::Image* getSourceImage(unsigned int i) { return _sourceList[i]->_image.get(); }
const osg::Texture2D* getSourceTexture(unsigned int i) { return _sourceList[i]->_texture.get(); }
void buildAtlas();
osg::Image* getImageAtlas(unsigned int i);
osg::Texture2D* getTextureAtlas(unsigned int i);
osg::Matrix getTextureMatrix(unsigned int i);
osg::Image* getImageAtlas(const osg::Image* image);
osg::Texture2D* getTextureAtlas(const osg::Image* image);
osg::Matrix getTextureMatrix(const osg::Image* image);
osg::Image* getImageAtlas(const osg::Texture2D* image);
osg::Texture2D* getTextureAtlas(const osg::Texture2D* texture);
osg::Matrix getTextureMatrix(const osg::Texture2D* texture);
protected:
unsigned int _maximumAtlasWidth;
unsigned int _maximumAtlasHeight;
unsigned int _margin;
// forward declare
class Atlas;
class Source : public osg::Referenced
{
public:
Source():
_x(0),_y(0),_atlas(0) {}
Source(const osg::Image* image):
_x(0),_y(0),_atlas(0),_image(image) {}
Source(const osg::Texture2D* texture):
_x(0),_y(0),_atlas(0),_texture(texture) { if (texture) _image = texture->getImage(); }
unsigned int _x;
unsigned int _y;
Atlas* _atlas;
osg::ref_ptr<const osg::Image> _image;
osg::ref_ptr<const osg::Texture2D> _texture;
osg::Matrix computeTextureMatrix() const;
protected:
virtual ~Source() {}
};
typedef std::vector< osg::ref_ptr<Source> > SourceList;
class Atlas : public osg::Referenced
{
public:
Atlas(unsigned int width, unsigned height, unsigned margin):
_maximumAtlasWidth(width),
_maximumAtlasHeight(height),
_margin(margin),
_x(0),
_width(0),
_height(0){}
unsigned int _maximumAtlasWidth;
unsigned int _maximumAtlasHeight;
unsigned int _margin;
osg::ref_ptr<osg::Texture2D> _texture;
osg::ref_ptr<osg::Image> _image;
SourceList _sourceList;
unsigned int _x;
unsigned int _y;
unsigned int _width;
unsigned int _height;
bool doesSourceFit(Source* source);
bool addSource(Source* source);
void clampToNearestPowerOfTwoSize();
void copySources();
protected:
virtual ~Atlas() {}
};
typedef std::vector< osg::ref_ptr<Atlas> > AtlasList;
Source* getSource(const osg::Image* image);
Source* getSource(const osg::Texture2D* texture);
SourceList _sourceList;
AtlasList _atasList;
};
};
inline bool BaseOptimizerVisitor::isOperationPermissibleForObject(const osg::StateSet* object) const
{
return _optimizer ? _optimizer->isOperationPermissibleForObject(object,_operationType) : true;
}
inline bool BaseOptimizerVisitor::isOperationPermissibleForObject(const osg::StateAttribute* object) const
{
return _optimizer ? _optimizer->isOperationPermissibleForObject(object,_operationType) : true;
}
inline bool BaseOptimizerVisitor::isOperationPermissibleForObject(const osg::Drawable* object) const
{
return _optimizer ? _optimizer->isOperationPermissibleForObject(object,_operationType) : true;
}
inline bool BaseOptimizerVisitor::isOperationPermissibleForObject(const osg::Node* object) const
{
return _optimizer ? _optimizer->isOperationPermissibleForObject(object,_operationType) : true;
}
}
#endif