diff --git a/include/osgUtil/Optimizer b/include/osgUtil/Optimizer index 0dbd9c48a..bf4939b0b 100644 --- a/include/osgUtil/Optimizer +++ b/include/osgUtil/Optimizer @@ -83,6 +83,7 @@ class OSGUTIL_EXPORT Optimizer FLATTEN_BILLBOARDS = 0x2000, TEXTURE_ATLAS_BUILDER = 0x4000, STATIC_OBJECT_DETECTION = 0x8000, + FLATTEN_STATIC_TRANSFORMS_DUPLICATING_SHARED_SUBGRAPHS = 0x100, DEFAULT_OPTIMIZATIONS = FLATTEN_STATIC_TRANSFORMS | REMOVE_REDUNDANT_NODES | REMOVE_LOADED_PROXY_NODES | @@ -92,7 +93,7 @@ class OSGUTIL_EXPORT Optimizer CHECK_GEOMETRY | OPTIMIZE_TEXTURE_SETTINGS | STATIC_OBJECT_DETECTION, - ALL_OPTIMIZATIONS = FLATTEN_STATIC_TRANSFORMS | + ALL_OPTIMIZATIONS = FLATTEN_STATIC_TRANSFORMS_DUPLICATING_SHARED_SUBGRAPHS | REMOVE_REDUNDANT_NODES | REMOVE_LOADED_PROXY_NODES | COMBINE_ADJACENT_LODS | @@ -250,7 +251,10 @@ class OSGUTIL_EXPORT Optimizer /** Flatten Static Transform nodes by applying their transform to the * geometry on the leaves of the scene graph, then removing the - * now redundant transforms.*/ + * 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 OSGUTIL_EXPORT FlattenStaticTransformsVisitor : public BaseOptimizerVisitor { public: @@ -282,6 +286,36 @@ class OSGUTIL_EXPORT Optimizer TransformSet _transformSet; }; + /** FlattenStaticTransformsDuplicatingSharedSubgraphsVisitor is similar to + * to FlattenStaticTransformsVisitor in that is desgined to remove static transforms + * from the scene graph, pushing down the transforms to the geometry leaves of the scene graph, + * but with the difference that any subgraphs that are shared between different transforms + * of duplicated and flatten individually. This results in more static transforms + * being removed, but also means that more data is generated, and as a result may + * not always be the most appropriate flatten visitor to use.*/ + class FlattenStaticTransformsDuplicatingSharedSubgraphsVisitor : public BaseOptimizerVisitor + { + public: + + FlattenStaticTransformsDuplicatingSharedSubgraphsVisitor(Optimizer* optimizer=0): + BaseOptimizerVisitor(optimizer, FLATTEN_STATIC_TRANSFORMS_DUPLICATING_SHARED_SUBGRAPHS) {} + + virtual void reset(); + + virtual void apply(osg::Group& group); + virtual void apply(osg::Transform& transform); + virtual void apply(osg::LOD& lod); + virtual void apply(osg::Geode& geode); + virtual void apply(osg::Billboard& billboard); + + protected: + + void transformDrawables(osg::Geode& geode); + void transformBillboard(osg::Billboard& billboard); + + std::vector _matrixStack; + + }; /** Combine Static Transform nodes that sit above one another.*/ class OSGUTIL_EXPORT CombineStaticTransformsVisitor : public BaseOptimizerVisitor diff --git a/src/osgUtil/Optimizer.cpp b/src/osgUtil/Optimizer.cpp index 375ec856b..aa85481d5 100644 --- a/src/osgUtil/Optimizer.cpp +++ b/src/osgUtil/Optimizer.cpp @@ -50,7 +50,7 @@ void Optimizer::reset() { } -static osg::ApplicationUsageProxy Optimizer_e0(osg::ApplicationUsage::ENVIRONMENTAL_VARIABLE,"OSG_OPTIMIZER \" []\"","OFF | DEFAULT | FLATTEN_STATIC_TRANSFORMS | REMOVE_REDUNDANT_NODES | COMBINE_ADJACENT_LODS | SHARE_DUPLICATE_STATE | MERGE_GEOMETRY | MERGE_GEODES | SPATIALIZE_GROUPS | COPY_SHARED_NODES | TRISTRIP_GEOMETRY | OPTIMIZE_TEXTURE_SETTINGS | REMOVE_LOADED_PROXY_NODES | TESSELLATE_GEOMETRY | CHECK_GEOMETRY | FLATTEN_BILLBOARDS | TEXTURE_ATLAS_BUILDER | STATIC_OBJECT_DETECTION"); +static osg::ApplicationUsageProxy Optimizer_e0(osg::ApplicationUsage::ENVIRONMENTAL_VARIABLE,"OSG_OPTIMIZER \" []\"","OFF | DEFAULT | FLATTEN_STATIC_TRANSFORMS | FLATTEN_STATIC_TRANSFORMS_DUPLICATING_SHARED_SUBGRAPHS | REMOVE_REDUNDANT_NODES | COMBINE_ADJACENT_LODS | SHARE_DUPLICATE_STATE | MERGE_GEOMETRY | MERGE_GEODES | SPATIALIZE_GROUPS | COPY_SHARED_NODES | TRISTRIP_GEOMETRY | OPTIMIZE_TEXTURE_SETTINGS | REMOVE_LOADED_PROXY_NODES | TESSELLATE_GEOMETRY | CHECK_GEOMETRY | FLATTEN_BILLBOARDS | TEXTURE_ATLAS_BUILDER | STATIC_OBJECT_DETECTION"); void Optimizer::optimize(osg::Node* node) { @@ -70,6 +70,9 @@ void Optimizer::optimize(osg::Node* node) if(str.find("~FLATTEN_STATIC_TRANSFORMS")!=std::string::npos) options ^= FLATTEN_STATIC_TRANSFORMS; else if(str.find("FLATTEN_STATIC_TRANSFORMS")!=std::string::npos) options |= FLATTEN_STATIC_TRANSFORMS; + if(str.find("~FLATTEN_STATIC_TRANSFORMS_DUPLICATING_SHARED_SUBGRAPHS")!=std::string::npos) options ^= FLATTEN_STATIC_TRANSFORMS_DUPLICATING_SHARED_SUBGRAPHS; + else if(str.find("FLATTEN_STATIC_TRANSFORMS_DUPLICATING_SHARED_SUBGRAPHS")!=std::string::npos) options |= FLATTEN_STATIC_TRANSFORMS_DUPLICATING_SHARED_SUBGRAPHS; + if(str.find("~REMOVE_REDUNDANT_NODES")!=std::string::npos) options ^= REMOVE_REDUNDANT_NODES; else if(str.find("REMOVE_REDUNDANT_NODES")!=std::string::npos) options |= REMOVE_REDUNDANT_NODES; @@ -236,6 +239,16 @@ void Optimizer::optimize(osg::Node* node, unsigned int options) } + if (options & FLATTEN_STATIC_TRANSFORMS_DUPLICATING_SHARED_SUBGRAPHS) + { + osg::notify(osg::NOTICE)<<"Optimizer::optimize() doing FLATTEN_STATIC_TRANSFORMS_DUPLICATING_SHARED_SUBGRAPHS"<accept(fstdssv); + + } + if (options & MERGE_GEODES) { osg::notify(osg::INFO)<<"Optimizer::optimize() doing MERGE_GEODES"< 1 && nodepathsize > 1) + { + // copy this Group + osg::ref_ptr new_obj = group.clone(osg::CopyOp::DEEP_COPY_NODES | osg::CopyOp::DEEP_COPY_DRAWABLES | osg::CopyOp::DEEP_COPY_ARRAYS); + osg::Group* new_group = dynamic_cast(new_obj.get()); + + // New Group should only be added to parent through which this Group + // was traversed, not to all parents of this Group. + osg::Group* parent_group = dynamic_cast(_nodePath[nodepathsize-2]); + if(parent_group) + { + parent_group->replaceChild(&group, new_group); + // traverse the new Group + traverse(*(new_group)); + } + else + { + osg::notify(osg::NOTICE) << "No parent for this Group" << std::endl; + } + } + else + { + // traverse original node + traverse(group); + } +} + + +void Optimizer::FlattenStaticTransformsDuplicatingSharedSubgraphsVisitor::apply(osg::Transform& transform) +{ + bool pushed = false; + + // only continue if there is a parent and this is a STATIC transform + const unsigned int nodepathsize = _nodePath.size(); + if(transform.getDataVariance() == osg::Object::STATIC && nodepathsize > 1) + { + osg::Matrix matrix; + if(!_matrixStack.empty()) + matrix = _matrixStack.back(); + transform.computeLocalToWorldMatrix(matrix, this); + _matrixStack.push_back(matrix); + pushed = true; + + // convert this Transform to a Group + osg::ref_ptr group = new osg::Group(dynamic_cast(transform), + osg::CopyOp::DEEP_COPY_NODES | osg::CopyOp::DEEP_COPY_DRAWABLES | osg::CopyOp::DEEP_COPY_ARRAYS); + + // New Group should only be added to parent through which this Transform + // was traversed, not to all parents of this Transform. + osg::Group* parent_group = dynamic_cast(_nodePath[nodepathsize-2]); + if(parent_group) + { + parent_group->replaceChild(&transform, group.get()); + // traverse the new Group + traverse(*(group.get())); + } + else + { + osg::notify(osg::NOTICE) << "No parent for this Group" << std::endl; + } + } + else + { + // traverse original node + traverse(transform); + } + + // pop matrix off of stack + if(pushed) + _matrixStack.pop_back(); +} + + +void Optimizer::FlattenStaticTransformsDuplicatingSharedSubgraphsVisitor::apply(osg::LOD& lod) +{ + const unsigned int nodepathsize = _nodePath.size(); + if(!_matrixStack.empty() && lod.getNumParents() > 1 && nodepathsize > 1) + { + osg::ref_ptr new_lod = new osg::LOD(lod, + osg::CopyOp::DEEP_COPY_NODES | osg::CopyOp::DEEP_COPY_DRAWABLES | osg::CopyOp::DEEP_COPY_ARRAYS); + + // New LOD should only be added to parent through which this LOD + // was traversed, not to all parents of this LOD. + osg::Group* parent_group = dynamic_cast(_nodePath[nodepathsize-2]); + if(parent_group) + { + parent_group->replaceChild(&lod, new_lod.get()); + + // move center point + if(!_matrixStack.empty()) + new_lod->setCenter(new_lod->getCenter() * _matrixStack.back()); + + // traverse the new Group + traverse(*(new_lod.get())); + } + else + osg::notify(osg::NOTICE) << "No parent for this LOD" << std::endl; + } + else + { + // move center point + if(!_matrixStack.empty()) + lod.setCenter(lod.getCenter() * _matrixStack.back()); + + traverse(lod); + } +} + + +void Optimizer::FlattenStaticTransformsDuplicatingSharedSubgraphsVisitor::apply(osg::Geode& geode) +{ + if(!_matrixStack.empty()) + { + // If there is only one parent, just transform all vertices and normals + if(geode.getNumParents() == 1) + { + transformDrawables(geode); + } + else + { + // Else make a copy and then transform + const unsigned int nodepathsize = _nodePath.size(); + if(nodepathsize > 1) + { + // convert this Transform to a Group + osg::ref_ptr new_geode = new osg::Geode(geode, + osg::CopyOp::DEEP_COPY_NODES | osg::CopyOp::DEEP_COPY_DRAWABLES | osg::CopyOp::DEEP_COPY_ARRAYS); + + // New Group should only be added to parent through which this Transform + // was traversed, not to all parents of this Transform. + osg::Group* parent_group = dynamic_cast(_nodePath[nodepathsize-2]); + if(parent_group) + parent_group->replaceChild(&geode, new_geode.get()); + else + osg::notify(osg::NOTICE) << "No parent for this Geode" << std::endl; + + transformDrawables(*(new_geode.get())); + } + } + } +} + + +void Optimizer::FlattenStaticTransformsDuplicatingSharedSubgraphsVisitor::apply(osg::Billboard& billboard) +{ + if(!_matrixStack.empty()) + { + // If there is only one parent, just transform this Billboard + if(billboard.getNumParents() == 1) + { + transformBillboard(billboard); + } + else + { + // Else make a copy and then transform + const unsigned int nodepathsize = _nodePath.size(); + if(nodepathsize > 1) + { + // convert this Transform to a Group + osg::ref_ptr new_billboard = new osg::Billboard(billboard, + osg::CopyOp::DEEP_COPY_NODES | osg::CopyOp::DEEP_COPY_DRAWABLES | osg::CopyOp::DEEP_COPY_ARRAYS); + + // New Billboard should only be added to parent through which this Billboard + // was traversed, not to all parents of this Billboard. + osg::Group* parent_group = dynamic_cast(_nodePath[nodepathsize-2]); + if(parent_group) + parent_group->replaceChild(&billboard, new_billboard.get()); + else + osg::notify(osg::NOTICE) << "No parent for this Billboard" << std::endl; + + transformBillboard(*(new_billboard.get())); + } + } + } +} + + +void Optimizer::FlattenStaticTransformsDuplicatingSharedSubgraphsVisitor::transformDrawables(osg::Geode& geode) +{ + for(unsigned int i=0; iasGeometry(); + if(geometry) + { + // transform all geometry + osg::Vec3Array* verts = dynamic_cast(geometry->getVertexArray()); + if(verts) + { + for(unsigned int j=0; jsize(); j++) + (*verts)[j] = (*verts)[j] * _matrixStack.back(); + } + else + { + osg::Vec4Array* verts = dynamic_cast(geometry->getVertexArray()); + if(verts) + { + for(unsigned int j=0; jsize(); j++) + (*verts)[j] = _matrixStack.back() * (*verts)[j]; + } + } + osg::Vec3Array* normals = dynamic_cast(geometry->getNormalArray()); + if(normals) + { + for(unsigned int j=0; jsize(); j++) + (*normals)[j] = osg::Matrix::transform3x3((*normals)[j], _matrixStack.back()); + } + + geometry->dirtyBound(); + geometry->dirtyDisplayList(); + } + } + + geode.dirtyBound(); +} + + +void Optimizer::FlattenStaticTransformsDuplicatingSharedSubgraphsVisitor::transformBillboard(osg::Billboard& billboard) +{ + osg::Vec3 axis = osg::Matrix::transform3x3(billboard.getAxis(), _matrixStack.back()); + axis.normalize(); + billboard.setAxis(axis); + + osg::Vec3 normal = osg::Matrix::transform3x3(billboard.getNormal(), _matrixStack.back()); + normal.normalize(); + billboard.setNormal(normal); + + for(unsigned int i=0; i