// 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 #include #include #include #include //#include #include #include namespace osgDB { class SharedStateManager; } //namespace osgUtil { namespace SceneUtil { // forward declare class 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; typedef std::map 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 TransformStack; typedef std::set DrawableSet; typedef std::set BillboardSet; typedef std::set NodeSet; typedef std::set 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 TransformSet; TransformSet _transformSet; }; /** Remove rendundant nodes, such as groups with one single child.*/ class RemoveEmptyNodesVisitor : public BaseOptimizerVisitor { public: typedef std::set 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 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 mTexCoord; std::vector 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 _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; } } #endif // clang-format on