Improove bounding volume building in the scenery loading process.

Refactor common code in the BoundingVolumeBuildVisitor.hxx.

Modified Files:
	simgear/scene/model/BoundingVolumeBuildVisitor.hxx
	simgear/scene/model/ModelRegistry.cxx
	simgear/scene/model/ModelRegistry.hxx
This commit is contained in:
frohlich
2009-03-18 07:53:50 +01:00
committed by Tim Moore
parent 252deed5b9
commit 035dcc7ab7
3 changed files with 132 additions and 73 deletions
@@ -23,7 +23,6 @@
#include <osg/Drawable> #include <osg/Drawable>
#include <osg/Geode> #include <osg/Geode>
#include <osg/Group> #include <osg/Group>
#include <osg/MatrixTransform>
#include <osg/PagedLOD> #include <osg/PagedLOD>
#include <osg/Transform> #include <osg/Transform>
#include <osg/TriangleFunctor> #include <osg/TriangleFunctor>
@@ -375,8 +374,9 @@ public:
// virtual void end() = 0; // virtual void end() = 0;
// }; // };
BoundingVolumeBuildVisitor() : BoundingVolumeBuildVisitor(bool dumpIntoLeafs) :
osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN) osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN),
_dumpIntoLeafs(dumpIntoLeafs)
{ {
setTraversalMask(SG_NODEMASK_TERRAIN_BIT); setTraversalMask(SG_NODEMASK_TERRAIN_BIT);
} }
@@ -402,57 +402,39 @@ public:
virtual void apply(osg::Geode& geode) virtual void apply(osg::Geode& geode)
{ {
if (hasBoundingVolumeTree(geode))
return;
const SGMaterial* oldMaterial = pushMaterial(geode.getStateSet()); 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) for(unsigned i = 0; i < geode.getNumDrawables(); ++i)
fillWith(geode.getDrawable(i)); fillWith(geode.getDrawable(i));
// Flush the bounding volume tree if we reached the topmost group // Flush the bounding volume tree if we reached the topmost group
if (getNodePath().size() <= 1)
addBoundingVolumeTreeToNode(geode); 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); _primitiveFunctor.setCurrentMaterial(oldMaterial);
} }
virtual void apply(osg::Group& group) virtual void apply(osg::Group& group)
{ { traverseAndCollect(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());
if (!hasBoundingVolumeTree(group))
traverse(group);
// Flush the bounding volume tree if we reached the topmost group
if (getNodePath().size() <= 1)
addBoundingVolumeTreeToNode(group);
_primitiveFunctor.setCurrentMaterial(oldMaterial);
}
virtual void apply(osg::Transform& transform) virtual void apply(osg::Transform& transform)
{ { traverseAndDump(transform); }
// 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);
// 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);
// pop the current active primitive list
_primitiveFunctor.swap(previousPrimitives);
}
virtual void apply(osg::PagedLOD&) virtual void apply(osg::PagedLOD&)
{ {
@@ -463,7 +445,55 @@ public:
{ {
if (camera.getRenderOrder() != osg::Camera::NESTED_RENDER) if (camera.getRenderOrder() != osg::Camera::NESTED_RENDER)
return; return;
apply(static_cast<osg::Transform&>(camera)); traverseAndDump(camera);
}
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);
// walk the children
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(node);
// pop the current active primitive list
_primitiveFunctor.swap(previousPrimitives);
_primitiveFunctor.setCurrentMaterial(oldMaterial);
}
void traverseAndCollect(osg::Node& node)
{
// Already been here??
if (hasBoundingVolumeTree(node))
return;
// 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) void addBoundingVolumeTreeToNode(osg::Node& node)
@@ -493,6 +523,7 @@ public:
private: private:
PFunctor _primitiveFunctor; PFunctor _primitiveFunctor;
bool _dumpIntoLeafs;
}; };
} }
+31 -22
View File
@@ -416,17 +416,10 @@ osg::Node* OptimizeModelPolicy::optimize(osg::Node* node,
} }
osg::Node* DefaultCopyPolicy::copy(osg::Node* model, const string& fileName, 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. // 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 // 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 // if the reference count is 1. If we clone all structural nodes here
// we need that extra reference to the original object // we need that extra reference to the original object
@@ -466,9 +459,34 @@ string OSGSubstitutePolicy::substitute(const string& name,
return absFileName; 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() : ModelRegistry::ModelRegistry() :
_defaultCallback(new DefaultCallback("")), _defaultCallback(new DefaultCallback(""))
_nestingLevel(0)
{ {
} }
@@ -491,7 +509,6 @@ ModelRegistry::readNode(const string& fileName,
const ReaderWriter::Options* opt) const ReaderWriter::Options* opt)
{ {
ScopedLock<ReentrantMutex> lock(readerMutex); ScopedLock<ReentrantMutex> lock(readerMutex);
++_nestingLevel;
// XXX Workaround for OSG plugin bug. // XXX Workaround for OSG plugin bug.
Registry* registry = Registry::instance(); Registry* registry = Registry::instance();
@@ -504,15 +521,6 @@ ModelRegistry::readNode(const string& fileName,
else else
result = _defaultCallback->readNode(fileName, opt); 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; return result;
} }
@@ -600,7 +608,8 @@ struct ACProcessPolicy {
typedef ModelRegistryCallback<ACProcessPolicy, DefaultCachePolicy, typedef ModelRegistryCallback<ACProcessPolicy, DefaultCachePolicy,
ACOptimizePolicy, DefaultCopyPolicy, ACOptimizePolicy, DefaultCopyPolicy,
OSGSubstitutePolicy> ACCallback; OSGSubstitutePolicy, BuildLeafBVHPolicy>
ACCallback;
namespace namespace
{ {
+27 -8
View File
@@ -61,13 +61,13 @@ namespace simgear
// readNode function is specified as a template with a bunch of // readNode function is specified as a template with a bunch of
// pluggable (and predefined) policies. // pluggable (and predefined) policies.
template <typename ProcessPolicy, typename CachePolicy, typename OptimizePolicy, template <typename ProcessPolicy, typename CachePolicy, typename OptimizePolicy,
typename CopyPolicy, typename SubstitutePolicy> typename CopyPolicy, typename SubstitutePolicy, typename BVHPolicy>
class ModelRegistryCallback : public osgDB::Registry::ReadFileCallback { class ModelRegistryCallback : public osgDB::Registry::ReadFileCallback {
public: public:
ModelRegistryCallback(const std::string& extension) : ModelRegistryCallback(const std::string& extension) :
_processPolicy(extension), _cachePolicy(extension), _processPolicy(extension), _cachePolicy(extension),
_optimizePolicy(extension), _copyPolicy(extension), _optimizePolicy(extension), _copyPolicy(extension),
_substitutePolicy(extension) _substitutePolicy(extension), _bvhPolicy(extension)
{ {
} }
virtual osgDB::ReaderWriter::ReadResult virtual osgDB::ReaderWriter::ReadResult
@@ -97,11 +97,12 @@ public:
optimizedNode = _optimizePolicy.optimize(processedNode.get(), optimizedNode = _optimizePolicy.optimize(processedNode.get(),
fileName, opt); fileName, opt);
} }
_bvhPolicy.buildBVH(fileName, optimizedNode.get());
_cachePolicy.addToCache(fileName, optimizedNode.get()); _cachePolicy.addToCache(fileName, optimizedNode.get());
} }
return ReaderWriter::ReadResult(_copyPolicy.copy(optimizedNode.get(), osg::ref_ptr<osg::Node> copyNode;
fileName, copyNode = _copyPolicy.copy(optimizedNode.get(), fileName, opt);
opt)); return ReaderWriter::ReadResult(copyNode);
} }
protected: protected:
static osgDB::ReaderWriter::ReadResult static osgDB::ReaderWriter::ReadResult
@@ -121,6 +122,7 @@ protected:
OptimizePolicy _optimizePolicy; OptimizePolicy _optimizePolicy;
CopyPolicy _copyPolicy; CopyPolicy _copyPolicy;
SubstitutePolicy _substitutePolicy; SubstitutePolicy _substitutePolicy;
BVHPolicy _bvhPolicy;
virtual ~ModelRegistryCallback() {} virtual ~ModelRegistryCallback() {}
}; };
@@ -196,9 +198,26 @@ struct NoSubstitutePolicy {
return std::string(); 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, typedef ModelRegistryCallback<DefaultProcessPolicy, DefaultCachePolicy,
OptimizeModelPolicy, DefaultCopyPolicy, OptimizeModelPolicy, DefaultCopyPolicy,
OSGSubstitutePolicy> DefaultCallback; OSGSubstitutePolicy, BuildLeafBVHPolicy>
DefaultCallback;
// The manager for the callbacks // The manager for the callbacks
class ModelRegistry : public osgDB::Registry::ReadFileCallback, class ModelRegistry : public osgDB::Registry::ReadFileCallback,
@@ -227,14 +246,14 @@ protected:
// Protect against simultaneous calls from main thread (MP models) // Protect against simultaneous calls from main thread (MP models)
// and pager thread. // and pager thread.
OpenThreads::ReentrantMutex readerMutex; OpenThreads::ReentrantMutex readerMutex;
unsigned _nestingLevel;
}; };
// Callback that only loads the file without any caching or // Callback that only loads the file without any caching or
// postprocessing. // postprocessing.
typedef ModelRegistryCallback<DefaultProcessPolicy, NoCachePolicy, typedef ModelRegistryCallback<DefaultProcessPolicy, NoCachePolicy,
NoOptimizePolicy, NoCopyPolicy, NoOptimizePolicy, NoCopyPolicy,
NoSubstitutePolicy> LoadOnlyCallback; NoSubstitutePolicy, BuildLeafBVHPolicy>
LoadOnlyCallback;
// Proxy for registering extension-based callbacks // Proxy for registering extension-based callbacks