Splicing visitor for rewriting scene graphs with a minimum of copying

This commit is contained in:
Tim Moore
2009-11-09 16:57:19 +01:00
parent 14e5e87a4d
commit 63ce675914
5 changed files with 222 additions and 0 deletions

View File

@@ -1231,6 +1231,12 @@
<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>

View File

@@ -1785,6 +1785,14 @@
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"
>

View 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

View File

@@ -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;
}
}

View File

@@ -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