]> git.mxchange.org Git - simgear.git/commitdiff
Splicing visitor for rewriting scene graphs with a minimum of copying
authorTim Moore <timoore@redhat.com>
Mon, 9 Nov 2009 15:57:19 +0000 (16:57 +0100)
committerTim Moore <timoore@redhat.com>
Fri, 13 Nov 2009 21:41:11 +0000 (22:41 +0100)
projects/VC7.1/SimGear.vcproj
projects/VC90/SimGear.vcproj
simgear/scene/util/Makefile.am
simgear/scene/util/SplicingVisitor.cxx [new file with mode: 0644]
simgear/scene/util/SplicingVisitor.hxx [new file with mode: 0644]

index 36dc8e09b5cb45147d1e6fbeb4f15e058559520a..bc1a2b66b8be7b51d581a20424a53734a41355a9 100755 (executable)
                        <File
                                RelativePath="..\..\simgear\scene\util\SGUpdateVisitor.hxx">
                        </File>
+                       <File
+                               RelativePath="..\..\simgear\scene\util\SplicingVisitor.cxx">
+                       </File>
+                       <File
+                               RelativePath="..\..\simgear\scene\util\SplicingVisitor.hxx">
+                       </File>
                        <File
                                RelativePath="..\..\simgear\scene\util\StateAttributeFactory.cxx">
                        </File>
index bf0336bbf40de32050203f912ea491fe125477a6..eab4385560781c3ae79d835422e7a0a035d3c65e 100644 (file)
                                RelativePath="..\..\simgear\scene\util\SGUpdateVisitor.hxx"
                                >
                        </File>
+                       <File
+                               RelativePath="..\..\simgear\scene\util\SplicingVisitor.cxx"
+                               >
+                       </File>
+                       <File
+                               RelativePath="..\..\simgear\scene\util\SplicingVisitor.hxx"
+                               >
+                       </File>
                        <File
                                RelativePath="..\..\simgear\scene\util\StateAttributeFactory.cxx"
                                >
index 567ec69708dbd9c223f6da3c72775e7cc833f43c..8ac69565a6e7f93f131db81e0353b038e31a0445 100644 (file)
@@ -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 (file)
index 0000000..6655837
--- /dev/null
@@ -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<Group> 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<Node> 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<Node> oldTmp(oldNode);
+    ref_ptr<Node> 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 (file)
index 0000000..8757e94
--- /dev/null
@@ -0,0 +1,93 @@
+#ifndef SIMGEAR_SPLICINGVISITOR_HXX
+#define SIMGEAR_SPLICINGVISITOR_HXX 1
+
+#include <cstddef>
+#include <map>
+#include <vector>
+#include <osg/NodeVisitor>
+#include <osg/Group>
+
+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<typename T>
+    static T* copyIfNeeded(T& node, const osg::NodeList& children);
+    template<typename T>
+    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<osg::NodeList> _childStack;
+    typedef std::map<osg::ref_ptr<osg::Node>, osg::ref_ptr<osg::Node> > NodeMap;
+    NodeMap _visited;
+};
+
+template<typename T>
+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<typename T>
+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