Something went wrong. Try again.
OpenMW is an open implementation of the Morrowind engine
Something went wrong. Try again.
21 kB · 487 lines
C++
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488// clang-format off/* -*-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.*/
/* Modified for OpenMW */
#ifndef OPENMW_OSGUTIL_OPTIMIZER#define OPENMW_OSGUTIL_OPTIMIZER
#include <osg/NodeVisitor>#include <osg/Matrix>#include <osg/Geometry>#include <osg/Transform>#include <osg/Texture2D>
//#include <osgUtil/Export>
#include <set>#include <mutex>
// NOLINTBEGIN(readability-identifier-naming)
namespace osgDB{ class SharedStateManager;}
//namespace osgUtil {namespace SceneUtil {
// forward declareclass Optimizer;
/** Helper base class for implementing Optimizer techniques.*/class 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 Optimizer{
public:
Optimizer() : _mergeAlphaBlending(false), _sharedStateManager(nullptr), _sharedStateMutex(nullptr) {} virtual ~Optimizer() {}
enum OptimizationOptions { FLATTEN_STATIC_TRANSFORMS = (1 << 0), REMOVE_REDUNDANT_NODES = (1 << 1), REMOVE_LOADED_PROXY_NODES = (1 << 2), COMBINE_ADJACENT_LODS = (1 << 3), SHARE_DUPLICATE_STATE = (1 << 4), MERGE_GEOMETRY = (1 << 5), CHECK_GEOMETRY = (1 << 6), // deprecated, currently no-op MAKE_FAST_GEOMETRY = (1 << 7), SPATIALIZE_GROUPS = (1 << 8), COPY_SHARED_NODES = (1 << 9), TRISTRIP_GEOMETRY = (1 << 10), TESSELLATE_GEOMETRY = (1 << 11), OPTIMIZE_TEXTURE_SETTINGS = (1 << 12), MERGE_GEODES = (1 << 13), FLATTEN_BILLBOARDS = (1 << 14), TEXTURE_ATLAS_BUILDER = (1 << 15), STATIC_OBJECT_DETECTION = (1 << 16), FLATTEN_STATIC_TRANSFORMS_DUPLICATING_SHARED_SUBGRAPHS = (1 << 17), INDEX_MESH = (1 << 18), VERTEX_POSTTRANSFORM = (1 << 19), VERTEX_PRETRANSFORM = (1 << 20), DEFAULT_OPTIMIZATIONS = FLATTEN_STATIC_TRANSFORMS | REMOVE_REDUNDANT_NODES | REMOVE_LOADED_PROXY_NODES | COMBINE_ADJACENT_LODS | SHARE_DUPLICATE_STATE | MERGE_GEOMETRY | MAKE_FAST_GEOMETRY | CHECK_GEOMETRY | OPTIMIZE_TEXTURE_SETTINGS | STATIC_OBJECT_DETECTION, ALL_OPTIMIZATIONS = FLATTEN_STATIC_TRANSFORMS_DUPLICATING_SHARED_SUBGRAPHS | REMOVE_REDUNDANT_NODES | REMOVE_LOADED_PROXY_NODES | COMBINE_ADJACENT_LODS | SHARE_DUPLICATE_STATE | MERGE_GEODES | MERGE_GEOMETRY | MAKE_FAST_GEOMETRY | CHECK_GEOMETRY | SPATIALIZE_GROUPS | COPY_SHARED_NODES | TRISTRIP_GEOMETRY | OPTIMIZE_TEXTURE_SETTINGS | TEXTURE_ATLAS_BUILDER | STATIC_OBJECT_DETECTION };
void setMergeAlphaBlending(bool merge) { _mergeAlphaBlending = merge; } void setViewPoint(const osg::Vec3f& viewPoint) { _viewPoint = viewPoint; }
void setSharedStateManager(osgDB::SharedStateManager* sharedStateManager, std::mutex* sharedStateMutex) { _sharedStateMutex = sharedStateMutex; _sharedStateManager = sharedStateManager; }
/** 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.*/ 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 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 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 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 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;
osg::Vec3f _viewPoint; bool _mergeAlphaBlending;
osgDB::SharedStateManager* _sharedStateManager; mutable std::mutex* _sharedStateMutex;
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. Static transformed subgraphs that have multiple * parental paths above them are not flattened, if you require this then * the subgraphs have to be duplicated - for this use the * FlattenStaticTransformsDuplicatingSharedSubgraphsVisitor. */ class FlattenStaticTransformsVisitor : public BaseOptimizerVisitor { public:
FlattenStaticTransformsVisitor(Optimizer* optimizer=0): BaseOptimizerVisitor(optimizer, FLATTEN_STATIC_TRANSFORMS) {}
void apply(osg::Node& node) override; void apply(osg::Geometry& geometry) override; void apply(osg::Drawable& drawable) override; void apply(osg::Billboard& billboard) override; void apply(osg::Transform& transform) override final; void apply(osg::MatrixTransform& transform) override;
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 CombineStaticTransformsVisitor : public BaseOptimizerVisitor { public:
CombineStaticTransformsVisitor(Optimizer* optimizer=0): BaseOptimizerVisitor(optimizer, FLATTEN_STATIC_TRANSFORMS) {}
void apply(osg::MatrixTransform& transform) override; void apply(osg::Geometry&) override { }
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 RemoveEmptyNodesVisitor : public BaseOptimizerVisitor { public:
typedef std::set<osg::Node*> NodeList; NodeList _redundantNodeList;
RemoveEmptyNodesVisitor(Optimizer* optimizer=0): BaseOptimizerVisitor(optimizer, REMOVE_REDUNDANT_NODES) {}
void apply(osg::Group& group) override; void apply(osg::Geometry&) override { }
void removeEmptyNodes();
};
/** Remove redundant nodes, such as groups with one single child.*/ class RemoveRedundantNodesVisitor : public BaseOptimizerVisitor { public:
typedef std::set<osg::Node*> NodeList; NodeList _redundantNodeList;
RemoveRedundantNodesVisitor(Optimizer* optimizer=0): BaseOptimizerVisitor(optimizer, REMOVE_REDUNDANT_NODES) {}
void apply(osg::Group& group) override; void apply(osg::Transform& transform) override; void apply(osg::LOD& lod) override; void apply(osg::Switch& switchNode) override; void apply(osg::Sequence& sequenceNode) override; void apply(osg::Geometry&) override { }
bool isOperationPermissible(osg::Node& node);
void removeRedundantNodes();
};
/** Merge adjacent Groups that have the same StateSet. */ class MergeGroupsVisitor : public SceneUtil::BaseOptimizerVisitor { public: MergeGroupsVisitor(SceneUtil::Optimizer* optimizer) : BaseOptimizerVisitor(optimizer, REMOVE_REDUNDANT_NODES) { }
bool isOperationPermissible(osg::Group& node);
void apply(osg::Geometry&) override { } void apply(osg::Group& group) override; void apply(osg::LOD& lod) override; void apply(osg::Switch& switchNode) override; void apply(osg::Sequence& sequenceNode) override; };
struct GeometryArraySizes { unsigned mVertex = 0; unsigned mNormal = 0; unsigned mColor = 0; unsigned mSecondaryColor = 0; unsigned mFogCoord = 0; std::vector<unsigned> mTexCoord; std::vector<unsigned> mVertexAttrib; };
class MergeGeometryVisitor : public BaseOptimizerVisitor { public:
/// default to traversing all children. MergeGeometryVisitor(Optimizer* optimizer=0) : BaseOptimizerVisitor(optimizer, MERGE_GEOMETRY), _targetMaximumNumberOfVertices(10000), _alphaBlendingActive(false), _mergeAlphaBlending(false) {}
void setMergeAlphaBlending(bool merge) { _mergeAlphaBlending = merge; } void setViewPoint(const osg::Vec3f& viewPoint) { _viewPoint = viewPoint; }
void setTargetMaximumNumberOfVertices(unsigned int num) { _targetMaximumNumberOfVertices = num; }
unsigned int getTargetMaximumNumberOfVertices() const { return _targetMaximumNumberOfVertices; }
bool pushStateSet(osg::StateSet* stateSet); void popStateSet(); void checkAlphaBlendingActive(); void apply(osg::Geometry&) override { } void apply(osg::Group& group) override; void apply(osg::Billboard&) override { /* don't do anything*/ }
bool mergeGroup(osg::Group& group);
static bool mergeGeometry(osg::Geometry& lhs, osg::Geometry& rhs, const GeometryArraySizes& sizes);
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; std::vector<osg::StateSet*> _stateSetStack; bool _alphaBlendingActive; bool _mergeAlphaBlending; osg::Vec3f _viewPoint; };
};
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;}
}
// NOLINTEND(readability-identifier-naming)
#endif// clang-format on