Splicing visitor for rewriting scene graphs with a minimum of copying
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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"
|
||||
>
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
113
simgear/scene/util/SplicingVisitor.cxx
Normal file
113
simgear/scene/util/SplicingVisitor.cxx
Normal 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;
|
||||
}
|
||||
}
|
||||
93
simgear/scene/util/SplicingVisitor.hxx
Normal file
93
simgear/scene/util/SplicingVisitor.hxx
Normal 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
|
||||
Reference in New Issue
Block a user