From: Tim Moore Date: Mon, 9 Nov 2009 15:57:19 +0000 (+0100) Subject: Splicing visitor for rewriting scene graphs with a minimum of copying X-Git-Url: https://git.mxchange.org/?a=commitdiff_plain;h=63ce675914f7f68f5553999d74d03e55cb3797cf;p=simgear.git Splicing visitor for rewriting scene graphs with a minimum of copying --- diff --git a/projects/VC7.1/SimGear.vcproj b/projects/VC7.1/SimGear.vcproj index 36dc8e09..bc1a2b66 100755 --- a/projects/VC7.1/SimGear.vcproj +++ b/projects/VC7.1/SimGear.vcproj @@ -1231,6 +1231,12 @@ + + + + diff --git a/projects/VC90/SimGear.vcproj b/projects/VC90/SimGear.vcproj index bf0336bb..eab43855 100644 --- a/projects/VC90/SimGear.vcproj +++ b/projects/VC90/SimGear.vcproj @@ -1785,6 +1785,14 @@ RelativePath="..\..\simgear\scene\util\SGUpdateVisitor.hxx" > + + + + diff --git a/simgear/scene/util/Makefile.am b/simgear/scene/util/Makefile.am index 567ec697..8ac69565 100644 --- a/simgear/scene/util/Makefile.am +++ b/simgear/scene/util/Makefile.am @@ -18,6 +18,7 @@ include_HEADERS = \ PrimitiveUtils.hxx \ QuadTreeBuilder.hxx \ RenderConstants.hxx \ + SplicingVisitor.hxx \ StateAttributeFactory.hxx \ VectorArrayAdapter.hxx @@ -30,6 +31,7 @@ libsgutil_a_SOURCES = \ SGTextureStateAttributeVisitor.cxx \ NodeAndDrawableVisitor.cxx \ PrimitiveUtils.cxx \ + SplicingVisitor.cxx \ StateAttributeFactory.cxx \ QuadTreeBuilder.cxx diff --git a/simgear/scene/util/SplicingVisitor.cxx b/simgear/scene/util/SplicingVisitor.cxx new file mode 100644 index 00000000..66558377 --- /dev/null +++ b/simgear/scene/util/SplicingVisitor.cxx @@ -0,0 +1,113 @@ +#include "SplicingVisitor.hxx" + +namespace simgear +{ +using namespace osg; + +SplicingVisitor::SplicingVisitor() + : NodeVisitor(NodeVisitor::TRAVERSE_ALL_CHILDREN) +{ + _childStack.push_back(NodeList()); +} + +void SplicingVisitor::reset() +{ + _childStack.clear(); + NodeVisitor::reset(); +} + +NodeList SplicingVisitor::traverse(Node& node) +{ + NodeList result; + _childStack.push_back(NodeList()); + NodeVisitor::traverse(node); + result = _childStack.back(); + _childStack.pop_back(); + return result; +} +void SplicingVisitor::apply(Node& node) +{ + NodeVisitor::traverse(node); + pushNode(&node); +} + +void SplicingVisitor::apply(Group& node) +{ + if (pushNode(getNewNode(node))) + return; + pushResultNode(&node, &node, traverse(node)); +} + +Group* SplicingVisitor::pushResultNode(Group* node, Group* newNode, + const NodeList& children) +{ + ref_ptr result; + if (node == newNode) { + result = copyIfNeeded(*node, children); + } else { + result = newNode; + for (NodeList::const_iterator itr = children.begin(), end = children.end(); + itr != end; + ++itr) + result->addChild(itr->get()); + } + _childStack.back().push_back(result); + recordNewNode(node, result); + return result; +} + +Node* SplicingVisitor::pushResultNode(Node* node, Node* newNode) +{ + _childStack.back().push_back(newNode); + recordNewNode(node, newNode); + return newNode; +} + +Node* SplicingVisitor::pushNode(Node* node) +{ + if (node) + _childStack.back().push_back(node); + return node; +} + +Node* SplicingVisitor::getResult() +{ + NodeList& top = _childStack.at(0); + if (top.empty()) { + return 0; + } else if (top.size() == 1) { + return top[0].get(); + } else { + Group* result = new Group; + for (NodeList::iterator itr = top.begin(), end = top.end(); + itr != end; + ++itr) + result->addChild(itr->get()); + return result; + } +} + +Node* SplicingVisitor::getNewNode(osg::Node* node) +{ + ref_ptr tmpPtr(node); + NodeMap::iterator itr; + try { + itr = _visited.find(tmpPtr); + } + catch (...) { + tmpPtr.release(); + throw; + } + if (itr == _visited.end()) + return 0; + else + return itr->second.get(); +} + +bool SplicingVisitor::recordNewNode(osg::Node* oldNode, osg::Node* newNode) +{ + ref_ptr oldTmp(oldNode); + ref_ptr newTmp(newNode); + return _visited.insert(std::make_pair(oldTmp, newTmp)).second; +} +} diff --git a/simgear/scene/util/SplicingVisitor.hxx b/simgear/scene/util/SplicingVisitor.hxx new file mode 100644 index 00000000..8757e941 --- /dev/null +++ b/simgear/scene/util/SplicingVisitor.hxx @@ -0,0 +1,93 @@ +#ifndef SIMGEAR_SPLICINGVISITOR_HXX +#define SIMGEAR_SPLICINGVISITOR_HXX 1 + +#include +#include +#include +#include +#include + +namespace simgear +{ +class SplicingVisitor : public osg::NodeVisitor +{ +public: + META_NodeVisitor(simgear,SplicingVisitor); + SplicingVisitor(); + virtual ~SplicingVisitor() {} + virtual void reset(); + osg::NodeList traverse(osg::Node& node); + using osg::NodeVisitor::apply; + virtual void apply(osg::Node& node); + virtual void apply(osg::Group& node); + template + static T* copyIfNeeded(T& node, const osg::NodeList& children); + template + static T* copy(T& node, const osg::NodeList& children); + /** + * Push the result of processing this node. + * + * If the child list is not equal to the node's current children, + * make a copy of the node. Record the (possibly new) node which + * should be the returned result if the node is visited again. + */ + osg::Group* pushResultNode(osg::Group* node, osg::Group* newNode, + const osg::NodeList& children); + /** + * Push the result of processing this node. + * + * Record the (possibly new) node which should be the returned + * result if the node is visited again. + */ + osg::Node* pushResultNode(osg::Node* node, osg::Node* newNode); + /** + * Push some node onto the list of result nodes. + */ + osg::Node* pushNode(osg::Node* node); + osg::Node* getResult(); + osg::Node* getNewNode(osg::Node& node) + { + return getNewNode(&node); + } + osg::Node* getNewNode(osg::Node* node); + bool recordNewNode(osg::Node* oldNode, osg::Node* newNode); + osg::NodeList& getResults() { return _childStack.back(); } +protected: + std::vector _childStack; + typedef std::map, osg::ref_ptr > NodeMap; + NodeMap _visited; +}; + +template +T* SplicingVisitor::copyIfNeeded(T& node, const osg::NodeList& children) +{ + bool copyNeeded = false; + if (node.getNumChildren() == children.size()) { + for (std::size_t i = 0; i < children.size(); ++i) + if (node.getChild(i) != children[i].get()) { + copyNeeded = true; + break; + } + } else { + copyNeeded = true; + } + if (copyNeeded) + return copy(node, children); + else + return &node; +} + +template +T* SplicingVisitor::copy(T& node, const osg::NodeList& children) +{ + T* result = osg::clone(&node, osg::CopyOp::SHALLOW_COPY); + result->removeChildren(0, result->getNumChildren()); + for (osg::NodeList::const_iterator itr = children.begin(), + end = children.end(); + itr != end; + ++itr) + result->addChild(itr->get()); + return result; +} +} +#endif