Refactor common code in the BoundingVolumeBuildVisitor.hxx.
Modified Files:
simgear/scene/model/BoundingVolumeBuildVisitor.hxx
simgear/scene/model/ModelRegistry.cxx
simgear/scene/model/ModelRegistry.hxx
#include <osg/Drawable>
#include <osg/Geode>
#include <osg/Group>
-#include <osg/MatrixTransform>
#include <osg/PagedLOD>
#include <osg/Transform>
#include <osg/TriangleFunctor>
// virtual void end() = 0;
// };
- BoundingVolumeBuildVisitor() :
- osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN)
+ BoundingVolumeBuildVisitor(bool dumpIntoLeafs) :
+ osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN),
+ _dumpIntoLeafs(dumpIntoLeafs)
{
setTraversalMask(SG_NODEMASK_TERRAIN_BIT);
}
virtual void apply(osg::Geode& geode)
{
+ if (hasBoundingVolumeTree(geode))
+ return;
+
const SGMaterial* oldMaterial = pushMaterial(geode.getStateSet());
- if (!hasBoundingVolumeTree(geode))
+ bool flushHere = getNodePath().size() <= 1 || _dumpIntoLeafs;
+ if (flushHere) {
+ // push the current active primitive list
+ PFunctor previousPrimitives;
+ _primitiveFunctor.swap(previousPrimitives);
+
+ // walk the children
for(unsigned i = 0; i < geode.getNumDrawables(); ++i)
fillWith(geode.getDrawable(i));
- // Flush the bounding volume tree if we reached the topmost group
- if (getNodePath().size() <= 1)
+ // Flush the bounding volume tree if we reached the topmost group
addBoundingVolumeTreeToNode(geode);
+
+ // pop the current active primitive list
+ _primitiveFunctor.swap(previousPrimitives);
+ } else {
+ for(unsigned i = 0; i < geode.getNumDrawables(); ++i)
+ fillWith(geode.getDrawable(i));
+ }
+
_primitiveFunctor.setCurrentMaterial(oldMaterial);
}
virtual void apply(osg::Group& group)
- {
- // Note that we do not need to push the already collected list of
- // primitives, since we are now in the topmost node ...
-
- const SGMaterial* oldMaterial = pushMaterial(group.getStateSet());
+ { traverseAndCollect(group); }
- if (!hasBoundingVolumeTree(group))
- traverse(group);
+ virtual void apply(osg::Transform& transform)
+ { traverseAndDump(transform); }
- // Flush the bounding volume tree if we reached the topmost group
- if (getNodePath().size() <= 1)
- addBoundingVolumeTreeToNode(group);
+ virtual void apply(osg::PagedLOD&)
+ {
+ // Do nothing. In this case we get called by the loading process anyway
+ }
- _primitiveFunctor.setCurrentMaterial(oldMaterial);
+ virtual void apply(osg::Camera& camera)
+ {
+ if (camera.getRenderOrder() != osg::Camera::NESTED_RENDER)
+ return;
+ traverseAndDump(camera);
}
- virtual void apply(osg::Transform& transform)
+ void traverseAndDump(osg::Node& node)
{
+ if (hasBoundingVolumeTree(node))
+ return;
+
+ const SGMaterial* oldMaterial = pushMaterial(node.getStateSet());
+
// push the current active primitive list
PFunctor previousPrimitives;
_primitiveFunctor.swap(previousPrimitives);
- const SGMaterial* oldMaterial = pushMaterial(transform.getStateSet());
-
// walk the children
- if (!hasBoundingVolumeTree(transform))
- traverse(transform);
+ traverse(node);
// We know whenever we see a transform, we need to flush the
// collected bounding volume tree since these transforms are not
// handled by the plain leafs.
- addBoundingVolumeTreeToNode(transform);
-
- _primitiveFunctor.setCurrentMaterial(oldMaterial);
+ addBoundingVolumeTreeToNode(node);
// pop the current active primitive list
_primitiveFunctor.swap(previousPrimitives);
- }
- virtual void apply(osg::PagedLOD&)
- {
- // Do nothing. In this case we get called by the loading process anyway
+ _primitiveFunctor.setCurrentMaterial(oldMaterial);
}
- virtual void apply(osg::Camera& camera)
+ void traverseAndCollect(osg::Node& node)
{
- if (camera.getRenderOrder() != osg::Camera::NESTED_RENDER)
+ // Already been here??
+ if (hasBoundingVolumeTree(node))
return;
- apply(static_cast<osg::Transform&>(camera));
+
+ // Force a flush of the bvtree if we are in the topmost node.
+ if (getNodePath().size() <= 1) {
+ traverseAndDump(node);
+ return;
+ }
+
+ // Note that we do not need to push the already collected list of
+ // primitives, since we are now in the topmost node ...
+
+ const SGMaterial* oldMaterial = pushMaterial(node.getStateSet());
+
+ // walk the children
+ traverse(node);
+
+ _primitiveFunctor.setCurrentMaterial(oldMaterial);
}
void addBoundingVolumeTreeToNode(osg::Node& node)
private:
PFunctor _primitiveFunctor;
+ bool _dumpIntoLeafs;
};
}
}
osg::Node* DefaultCopyPolicy::copy(osg::Node* model, const string& fileName,
- const osgDB::ReaderWriter::Options* opt)
+ const osgDB::ReaderWriter::Options* opt)
{
- /// Crude hack for the bounding volume sharing problem.
- /// Better solution this week.
- /// Note that this does not really build in the case we come here
- /// the second time for the same node
- BoundingVolumeBuildVisitor bvBuilder;
- model->accept(bvBuilder);
-
// Add an extra reference to the model stored in the database.
- // That it to avoid expiring the object from the cache even if it is still
+ // That is to avoid expiring the object from the cache even if it is still
// in use. Note that the object cache will think that a model is unused
// if the reference count is 1. If we clone all structural nodes here
// we need that extra reference to the original object
return absFileName;
}
+
+void
+BuildLeafBVHPolicy::buildBVH(const std::string& fileName, osg::Node* node)
+{
+ SG_LOG(SG_IO, SG_INFO, "Building leaf attached boundingvolume tree for \""
+ << fileName << "\".");
+ BoundingVolumeBuildVisitor bvBuilder(true);
+ node->accept(bvBuilder);
+}
+
+void
+BuildGroupBVHPolicy::buildBVH(const std::string& fileName, osg::Node* node)
+{
+ SG_LOG(SG_IO, SG_INFO, "Building group attached boundingvolume tree for \""
+ << fileName << "\".");
+ BoundingVolumeBuildVisitor bvBuilder(false);
+ node->accept(bvBuilder);
+}
+
+void
+NoBuildBVHPolicy::buildBVH(const std::string& fileName, osg::Node*)
+{
+ SG_LOG(SG_IO, SG_INFO, "Omitting boundingvolume tree for \""
+ << fileName << "\".");
+}
+
ModelRegistry::ModelRegistry() :
- _defaultCallback(new DefaultCallback("")),
- _nestingLevel(0)
+ _defaultCallback(new DefaultCallback(""))
{
}
const ReaderWriter::Options* opt)
{
ScopedLock<ReentrantMutex> lock(readerMutex);
- ++_nestingLevel;
// XXX Workaround for OSG plugin bug.
Registry* registry = Registry::instance();
else
result = _defaultCallback->readNode(fileName, opt);
- if (0 == --_nestingLevel) {
- SG_LOG(SG_IO, SG_INFO, "Building boundingvolume tree for \""
- << fileName << "\".");
- BoundingVolumeBuildVisitor bvBuilder;
- result.getNode()->accept(bvBuilder);
- } else {
- SG_LOG(SG_IO, SG_INFO, "Defering boundingvolume tree built for \""
- << fileName << "\" to parent.");
- }
return result;
}
typedef ModelRegistryCallback<ACProcessPolicy, DefaultCachePolicy,
ACOptimizePolicy, DefaultCopyPolicy,
- OSGSubstitutePolicy> ACCallback;
+ OSGSubstitutePolicy, BuildLeafBVHPolicy>
+ACCallback;
namespace
{
// readNode function is specified as a template with a bunch of
// pluggable (and predefined) policies.
template <typename ProcessPolicy, typename CachePolicy, typename OptimizePolicy,
- typename CopyPolicy, typename SubstitutePolicy>
+ typename CopyPolicy, typename SubstitutePolicy, typename BVHPolicy>
class ModelRegistryCallback : public osgDB::Registry::ReadFileCallback {
public:
ModelRegistryCallback(const std::string& extension) :
_processPolicy(extension), _cachePolicy(extension),
_optimizePolicy(extension), _copyPolicy(extension),
- _substitutePolicy(extension)
+ _substitutePolicy(extension), _bvhPolicy(extension)
{
}
virtual osgDB::ReaderWriter::ReadResult
optimizedNode = _optimizePolicy.optimize(processedNode.get(),
fileName, opt);
}
+ _bvhPolicy.buildBVH(fileName, optimizedNode.get());
_cachePolicy.addToCache(fileName, optimizedNode.get());
}
- return ReaderWriter::ReadResult(_copyPolicy.copy(optimizedNode.get(),
- fileName,
- opt));
+ osg::ref_ptr<osg::Node> copyNode;
+ copyNode = _copyPolicy.copy(optimizedNode.get(), fileName, opt);
+ return ReaderWriter::ReadResult(copyNode);
}
protected:
static osgDB::ReaderWriter::ReadResult
OptimizePolicy _optimizePolicy;
CopyPolicy _copyPolicy;
SubstitutePolicy _substitutePolicy;
+ BVHPolicy _bvhPolicy;
virtual ~ModelRegistryCallback() {}
};
return std::string();
}
};
+
+struct BuildLeafBVHPolicy {
+ BuildLeafBVHPolicy(const std::string& extension) {}
+ void buildBVH(const std::string& fileName, osg::Node* node);
+};
+
+struct BuildGroupBVHPolicy {
+ BuildGroupBVHPolicy(const std::string& extension) {}
+ void buildBVH(const std::string& fileName, osg::Node* node);
+};
+
+struct NoBuildBVHPolicy {
+ NoBuildBVHPolicy(const std::string& extension) {}
+ void buildBVH(const std::string& fileName, osg::Node* node);
+};
+
typedef ModelRegistryCallback<DefaultProcessPolicy, DefaultCachePolicy,
OptimizeModelPolicy, DefaultCopyPolicy,
- OSGSubstitutePolicy> DefaultCallback;
+ OSGSubstitutePolicy, BuildLeafBVHPolicy>
+DefaultCallback;
// The manager for the callbacks
class ModelRegistry : public osgDB::Registry::ReadFileCallback,
// Protect against simultaneous calls from main thread (MP models)
// and pager thread.
OpenThreads::ReentrantMutex readerMutex;
- unsigned _nestingLevel;
};
// Callback that only loads the file without any caching or
// postprocessing.
typedef ModelRegistryCallback<DefaultProcessPolicy, NoCachePolicy,
NoOptimizePolicy, NoCopyPolicy,
- NoSubstitutePolicy> LoadOnlyCallback;
+ NoSubstitutePolicy, BuildLeafBVHPolicy>
+LoadOnlyCallback;
// Proxy for registering extension-based callbacks