bvh: Build bigger leaf nodes for paged bvh nodes.

Flatten the leaf nodes in the paged bounding volume tree
as good as possible. The implementation still assumes a
whole world database which actually holds for our usual
scenery.
This commit is contained in:
Mathias Froehlich
2012-09-27 18:21:13 +02:00
parent 63aa16b97c
commit c04ec5953f
2 changed files with 189 additions and 166 deletions
+185 -162
View File
@@ -24,6 +24,7 @@
#include "../../bvh/BVHPageRequest.hxx" #include "../../bvh/BVHPageRequest.hxx"
#include "../../bvh/BVHPager.hxx" #include "../../bvh/BVHPager.hxx"
#include <osg/io_utils>
#include <osg/Camera> #include <osg/Camera>
#include <osg/Drawable> #include <osg/Drawable>
#include <osg/Geode> #include <osg/Geode>
@@ -48,50 +49,71 @@ namespace simgear {
class BVHPageNodeOSG::_NodeVisitor : public osg::NodeVisitor { class BVHPageNodeOSG::_NodeVisitor : public osg::NodeVisitor {
public: public:
class _PrimitiveCollector : public PrimitiveCollector { struct _PrimitiveCollector : public PrimitiveCollector {
public: _PrimitiveCollector(_NodeVisitor& nodeVisitor) :
_PrimitiveCollector() : _nodeVisitor(nodeVisitor)
_geometryBuilder(new BVHStaticGeometryBuilder)
{ } { }
virtual ~_PrimitiveCollector() virtual ~_PrimitiveCollector()
{ } { }
virtual void addPoint(const osg::Vec3d& v1) virtual void addPoint(const osg::Vec3d& v1)
{ } { }
virtual void addLine(const osg::Vec3d& v1, const osg::Vec3d& v2) virtual void addLine(const osg::Vec3d& v1, const osg::Vec3d& v2)
{ } { }
virtual void addTriangle(const osg::Vec3d& v1, const osg::Vec3d& v2, const osg::Vec3d& v3) virtual void addTriangle(const osg::Vec3d& v1, const osg::Vec3d& v2, const osg::Vec3d& v3)
{ { _nodeVisitor.addTriangle(v1, v2, v3); }
_geometryBuilder->addTriangle(toVec3f(toSG(v1)), toVec3f(toSG(v2)), toVec3f(toSG(v3))); private:
} _NodeVisitor& _nodeVisitor;
BVHNode* buildTreeAndClear()
{
BVHNode* bvNode = _geometryBuilder->buildTree();
_geometryBuilder = new BVHStaticGeometryBuilder;
return bvNode;
}
void swap(_PrimitiveCollector& primitiveCollector)
{
PrimitiveCollector::swap(primitiveCollector);
std::swap(_geometryBuilder, primitiveCollector._geometryBuilder);
}
void setCurrentMaterial(const BVHMaterial* material)
{
_geometryBuilder->setCurrentMaterial(material);
}
const BVHMaterial* getCurrentMaterial() const
{
return _geometryBuilder->getCurrentMaterial();
}
SGSharedPtr<BVHStaticGeometryBuilder> _geometryBuilder;
}; };
_NodeVisitor() : struct _NodeBin {
osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN) SGSharedPtr<BVHNode> getNode(const osg::Matrix& matrix)
{
if (_nodeVector.empty())
return SGSharedPtr<BVHNode>();
if (!matrix.isIdentity()) {
// If we have a non trivial matrix we need a
// transform node in any case.
SGSharedPtr<BVHTransform> transform = new BVHTransform;
transform->setToWorldTransform(SGMatrixd(matrix.ptr()));
for (_NodeVector::iterator i = _nodeVector.begin();
i != _nodeVector.end(); ++i)
transform->addChild(i->get());
return transform;
} else {
// If the matrix is an identity, return the
// smallest possible subtree.
if (_nodeVector.size() == 1)
return _nodeVector.front();
SGSharedPtr<BVHGroup> group = new BVHGroup;
for (_NodeVector::iterator i = _nodeVector.begin();
i != _nodeVector.end(); ++i)
group->addChild(i->get());
return group;
}
}
void addNode(const SGSharedPtr<BVHNode>& node)
{
if (!node.valid())
return;
if (node->getBoundingSphere().empty())
return;
_nodeVector.push_back(node);
}
private:
typedef std::vector<SGSharedPtr<BVHNode> > _NodeVector;
// The current pending node vector.
_NodeVector _nodeVector;
};
_NodeVisitor(bool flatten, const osg::Matrix& localToWorldMatrix = osg::Matrix()) :
osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN),
_localToWorldMatrix(localToWorldMatrix),
_geometryBuilder(new BVHStaticGeometryBuilder),
_flatten(flatten)
{ {
setTraversalMask(SG_NODEMASK_TERRAIN_BIT); setTraversalMask(SG_NODEMASK_TERRAIN_BIT);
} }
@@ -99,63 +121,47 @@ public:
{ {
} }
const BVHMaterial* pushMaterial(osg::Geode* geode) void addTriangle(const osg::Vec3d& v1, const osg::Vec3d& v2, const osg::Vec3d& v3)
{ {
const BVHMaterial* oldMaterial = _primitiveCollector.getCurrentMaterial(); _geometryBuilder->addTriangle(toVec3f(toSG(_localToWorldMatrix.preMult(v1))),
const BVHMaterial* material = SGMaterialLib::findMaterial(geode); toVec3f(toSG(_localToWorldMatrix.preMult(v2))),
if (material) toVec3f(toSG(_localToWorldMatrix.preMult(v3))));
_primitiveCollector.setCurrentMaterial(material); }
return oldMaterial;
void setCenter(const osg::Vec3& center)
{
_centerMatrix.preMultTranslate(center);
_localToWorldMatrix.postMultTranslate(-center);
if (1e6 < center.length()) {
SGGeod geod = SGGeod::fromCart(toVec3d(toSG(center)));
SGQuatd orientation = SGQuatd::fromLonLat(geod);
_centerMatrix.preMultRotate(toOsg(orientation));
_localToWorldMatrix.postMultRotate(toOsg(inverse(orientation)));
}
} }
virtual void apply(osg::Geode& geode) virtual void apply(osg::Geode& geode)
{ {
const BVHMaterial* oldMaterial = pushMaterial(&geode); const BVHMaterial* oldMaterial = _geometryBuilder->getCurrentMaterial();
if (const BVHMaterial* material = SGMaterialLib::findMaterial(&geode))
_geometryBuilder->setCurrentMaterial(material);
_PrimitiveCollector primitiveCollector(*this);
for(unsigned i = 0; i < geode.getNumDrawables(); ++i) for(unsigned i = 0; i < geode.getNumDrawables(); ++i)
geode.getDrawable(i)->accept(_primitiveCollector); geode.getDrawable(i)->accept(primitiveCollector);
_primitiveCollector.setCurrentMaterial(oldMaterial); _geometryBuilder->setCurrentMaterial(oldMaterial);
} }
virtual void apply(osg::Group& group) virtual void apply(osg::Node& node)
{ {
// FIXME optimize this to collapse more leafs if (_flatten) {
traverse(node);
// push the current active primitive list } else {
_PrimitiveCollector previousPrimitives; _NodeVisitor nodeVisitor(_flatten, _localToWorldMatrix);
_primitiveCollector.swap(previousPrimitives); nodeVisitor.traverse(node);
_nodeBin.addNode(nodeVisitor.getNode(osg::Matrix::identity()));
const BVHMaterial* mat = previousPrimitives.getCurrentMaterial();
_primitiveCollector.setCurrentMaterial(mat);
NodeVector nodeVector;
_nodeVector.swap(nodeVector);
// walk the children
traverse(group);
// 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();
_nodeVector.swap(nodeVector);
if (!nodeVector.empty()) {
if (nodeVector.size() == 1) {
_nodeVector.push_back(nodeVector.front());
} else {
SGSharedPtr<BVHGroup> group = new BVHGroup;
for (NodeVector::iterator i = nodeVector.begin();
i != nodeVector.end(); ++i)
group->addChild(i->get());
_nodeVector.push_back(group);
}
} }
// pop the current active primitive list
_primitiveCollector.swap(previousPrimitives);
} }
virtual void apply(osg::Transform& transform) virtual void apply(osg::Transform& transform)
@@ -163,42 +169,30 @@ public:
if (transform.getReferenceFrame() != osg::Transform::RELATIVE_RF) if (transform.getReferenceFrame() != osg::Transform::RELATIVE_RF)
return; return;
osg::Matrix matrix; // FIXME identify and handle dynamic transforms
if (!transform.computeLocalToWorldMatrix(matrix, this))
return;
// push the current active primitive list if (_flatten) {
_PrimitiveCollector previousPrimitives; // propagate the matrix further down into the nodes and
_primitiveCollector.swap(previousPrimitives); // build a flat leaf tree as far as possible
const BVHMaterial* mat = previousPrimitives.getCurrentMaterial(); // save away and accumulate the localToWorldMatrix
_primitiveCollector.setCurrentMaterial(mat); osg::Matrix localToWorldMatrix = _localToWorldMatrix;
if (!transform.computeLocalToWorldMatrix(_localToWorldMatrix, this))
return;
NodeVector nodeVector; traverse(transform);
_nodeVector.swap(nodeVector);
// walk the children _localToWorldMatrix = localToWorldMatrix;
traverse(transform); } else {
// accumulate the localToWorldMatrix
osg::Matrix localToWorldMatrix = _localToWorldMatrix;
if (!transform.computeLocalToWorldMatrix(localToWorldMatrix, this))
return;
// We know whenever we see a transform, we need to flush the // evaluate the loca to world matrix here in this group node.
// collected bounding volume tree since these transforms are not _NodeVisitor nodeVisitor(_flatten);
// handled by the plain leafs. nodeVisitor.traverse(transform);
addBoundingVolumeTreeToNode(); _nodeBin.addNode(nodeVisitor.getNode(localToWorldMatrix));
_nodeVector.swap(nodeVector);
// pop the current active primitive list
_primitiveCollector.swap(previousPrimitives);
if (!nodeVector.empty()) {
SGSharedPtr<BVHTransform> bvhTransform = new BVHTransform;
bvhTransform->setToWorldTransform(SGMatrixd(matrix.ptr()));
for (NodeVector::iterator i = nodeVector.begin();
i != nodeVector.end(); ++i)
bvhTransform->addChild(i->get());
_nodeVector.push_back(bvhTransform);
} }
} }
@@ -209,42 +203,46 @@ public:
apply(static_cast<osg::Transform&>(camera)); apply(static_cast<osg::Transform&>(camera));
} }
void addBoundingVolumeTreeToNode()
{
// Build the flat tree.
BVHNode* bvNode = _primitiveCollector.buildTreeAndClear();
// Nothing in there?
if (!bvNode)
return;
if (bvNode->getBoundingSphere().empty())
return;
_nodeVector.push_back(bvNode);
}
virtual void apply(osg::PagedLOD& pagedLOD) virtual void apply(osg::PagedLOD& pagedLOD)
{ {
float range = std::numeric_limits<float>::max();
unsigned numFileNames = pagedLOD.getNumFileNames(); unsigned numFileNames = pagedLOD.getNumFileNames();
for (unsigned i = 0; i < numFileNames; ++i) { if (_flatten) {
if (range < pagedLOD.getMaxRange(i)) // In flattening mode treat lod nodes as proxy nodes
continue; for (unsigned i = 0; i < numFileNames; ++i) {
range = pagedLOD.getMaxRange(i); if (i < pagedLOD.getNumChildren() && pagedLOD.getChild(i))
} continue;
osg::ref_ptr<osg::Node> node;
std::vector<std::string> nameList; if (pagedLOD.getMinRange(i) <= 0) {
for (unsigned i = pagedLOD.getNumChildren(); i < pagedLOD.getNumFileNames(); ++i) { osg::ref_ptr<const osgDB::Options> options;
if (pagedLOD.getMaxRange(i) <= range) { options = getOptions(pagedLOD.getDatabaseOptions(), pagedLOD.getDatabasePath());
node = osgDB::readRefNodeFile(pagedLOD.getFileName(i), options.get());
}
if (!node.valid())
node = new osg::Group;
if (i < pagedLOD.getNumChildren())
pagedLOD.setChild(i, node);
else
pagedLOD.addChild(node);
}
} else {
// in non flattening mode translate to bvh page nodes
std::vector<std::string> nameList;
for (unsigned i = pagedLOD.getNumChildren(); i < numFileNames; ++i) {
if (0 < pagedLOD.getMinRange(i))
continue;
nameList.push_back(pagedLOD.getFileName(i)); nameList.push_back(pagedLOD.getFileName(i));
} }
}
if (!nameList.empty()) { _NodeBin nodeBin;
SGSphered boundingSphere(toVec3d(toSG(pagedLOD.getCenter())), pagedLOD.getRadius()); if (!nameList.empty()) {
_nodeVector.push_back(new BVHPageNodeOSG(nameList, boundingSphere, pagedLOD.getDatabaseOptions())); osg::ref_ptr<const osgDB::Options> options;
options = getOptions(pagedLOD.getDatabaseOptions(), pagedLOD.getDatabasePath());
SGSphered boundingSphere(toVec3d(toSG(pagedLOD.getCenter())), pagedLOD.getRadius());
nodeBin.addNode(new BVHPageNodeOSG(nameList, boundingSphere, options.get()));
}
_nodeBin.addNode(nodeBin.getNode(_localToWorldMatrix));
} }
// For the rest that might be already there, traverse this as lod // For the rest that might be already there, traverse this as lod
apply(static_cast<osg::LOD&>(pagedLOD)); apply(static_cast<osg::LOD&>(pagedLOD));
} }
@@ -255,10 +253,10 @@ public:
for (unsigned i = 0; i < numFileNames; ++i) { for (unsigned i = 0; i < numFileNames; ++i) {
if (i < proxyNode.getNumChildren() && proxyNode.getChild(i)) if (i < proxyNode.getNumChildren() && proxyNode.getChild(i))
continue; continue;
// FIXME evaluate proxyNode.getDatabasePath() osg::ref_ptr<const osgDB::Options> options;
options = getOptions(proxyNode.getDatabaseOptions(), proxyNode.getDatabasePath());
osg::ref_ptr<osg::Node> node; osg::ref_ptr<osg::Node> node;
node = osgDB::readNodeFile(proxyNode.getFileName(i), node = osgDB::readRefNodeFile(proxyNode.getFileName(i), options.get());
dynamic_cast<const osgDB::Options*>(proxyNode.getDatabaseOptions()));
if (!node.valid()) if (!node.valid())
node = new osg::Group; node = new osg::Group;
if (i < proxyNode.getNumChildren()) if (i < proxyNode.getNumChildren())
@@ -270,25 +268,47 @@ public:
apply(static_cast<osg::Group&>(proxyNode)); apply(static_cast<osg::Group&>(proxyNode));
} }
SGSharedPtr<BVHNode> getBVHNode() static osg::ref_ptr<const osgDB::Options>
getOptions(const osg::Referenced* referenced, const std::string& databasePath)
{ {
addBoundingVolumeTreeToNode(); osg::ref_ptr<const osgDB::Options> options = dynamic_cast<const osgDB::Options*>(referenced);
if (!options.valid())
options = osgDB::Registry::instance()->getOptions();
if (databasePath.empty())
return options;
osg::ref_ptr<osgDB::Options> writable;
if (options.valid())
writable = static_cast<osgDB::Options*>(options->clone(osg::CopyOp()));
else
writable = new osgDB::Options;
writable->getDatabasePathList().push_front(databasePath);
return writable;
}
if (_nodeVector.empty()) SGSharedPtr<BVHNode> getNode(const osg::Matrix& matrix = osg::Matrix())
return SGSharedPtr<BVHNode>(); {
if (_nodeVector.size() == 1) // Flush any pendig leaf nodes
return _nodeVector.front(); if (_geometryBuilder.valid()) {
SGSharedPtr<BVHGroup> group = new BVHGroup; _nodeBin.addNode(_geometryBuilder->buildTree());
for (NodeVector::iterator i = _nodeVector.begin(); _geometryBuilder.clear();
i != _nodeVector.end(); ++i) }
group->addChild(i->get());
return group; return _nodeBin.getNode(matrix*_centerMatrix);
} }
private: private:
_PrimitiveCollector _primitiveCollector; // The part of the accumulated model view matrix that
typedef std::vector<SGSharedPtr<BVHNode> > NodeVector; // is put into a BVHTransform node.
NodeVector _nodeVector; osg::Matrix _localToWorldMatrix;
// The matrix that centers and aligns the leaf.
osg::Matrix _centerMatrix;
// The current pending nodes.
_NodeBin _nodeBin;
SGSharedPtr<BVHStaticGeometryBuilder> _geometryBuilder;
bool _flatten;
}; };
class BVHPageNodeOSG::_Request : public BVHPageRequest { class BVHPageNodeOSG::_Request : public BVHPageRequest {
@@ -330,21 +350,24 @@ private:
}; };
SGSharedPtr<BVHNode> SGSharedPtr<BVHNode>
BVHPageNodeOSG::load(const std::string& name, const osg::ref_ptr<osg::Referenced>& options) BVHPageNodeOSG::load(const std::string& name, const osg::ref_ptr<const osg::Referenced>& options)
{ {
osg::ref_ptr<osg::Node> node; osg::ref_ptr<osg::Node> node;
node = osgDB::readNodeFile(name, dynamic_cast<const osgDB::Options*>(options.get())); node = osgDB::readRefNodeFile(name, dynamic_cast<const osgDB::Options*>(options.get()));
if (!node.valid()) if (!node.valid())
return SGSharedPtr<BVHNode>(); return SGSharedPtr<BVHNode>();
_NodeVisitor nodeVisitor; bool flatten = (node->getBound()._radius < 30000);
_NodeVisitor nodeVisitor(flatten);
if (flatten)
nodeVisitor.setCenter(node->getBound()._center);
node->accept(nodeVisitor); node->accept(nodeVisitor);
return nodeVisitor.getBVHNode(); return nodeVisitor.getNode();
} }
BVHPageNodeOSG::BVHPageNodeOSG(const std::string& name, BVHPageNodeOSG::BVHPageNodeOSG(const std::string& name,
const SGSphered& boundingSphere, const SGSphered& boundingSphere,
const osg::ref_ptr<osg::Referenced>& options) : const osg::ref_ptr<const osg::Referenced>& options) :
_boundingSphere(boundingSphere), _boundingSphere(boundingSphere),
_options(options) _options(options)
{ {
@@ -353,7 +376,7 @@ BVHPageNodeOSG::BVHPageNodeOSG(const std::string& name,
BVHPageNodeOSG::BVHPageNodeOSG(const std::vector<std::string>& nameList, BVHPageNodeOSG::BVHPageNodeOSG(const std::vector<std::string>& nameList,
const SGSphered& boundingSphere, const SGSphered& boundingSphere,
const osg::ref_ptr<osg::Referenced>& options) : const osg::ref_ptr<const osg::Referenced>& options) :
_modelList(nameList), _modelList(nameList),
_boundingSphere(boundingSphere), _boundingSphere(boundingSphere),
_options(options) _options(options)
+4 -4
View File
@@ -28,10 +28,10 @@ namespace simgear {
class BVHPageNodeOSG : public BVHPageNode { class BVHPageNodeOSG : public BVHPageNode {
public: public:
BVHPageNodeOSG(const std::string& name, const SGSphered& boundingSphere, BVHPageNodeOSG(const std::string& name, const SGSphered& boundingSphere,
const osg::ref_ptr<osg::Referenced>& options); const osg::ref_ptr<const osg::Referenced>& options);
BVHPageNodeOSG(const std::vector<std::string>& nameList, BVHPageNodeOSG(const std::vector<std::string>& nameList,
const SGSphered& boundingSphere, const SGSphered& boundingSphere,
const osg::ref_ptr<osg::Referenced>& options); const osg::ref_ptr<const osg::Referenced>& options);
virtual ~BVHPageNodeOSG(); virtual ~BVHPageNodeOSG();
virtual BVHPageRequest* newRequest(); virtual BVHPageRequest* newRequest();
@@ -39,7 +39,7 @@ public:
void setBoundingSphere(const SGSphered& sphere); void setBoundingSphere(const SGSphered& sphere);
static SGSharedPtr<BVHNode> static SGSharedPtr<BVHNode>
load(const std::string& name, const osg::ref_ptr<osg::Referenced>& options); load(const std::string& name, const osg::ref_ptr<const osg::Referenced>& options);
protected: protected:
virtual SGSphered computeBoundingSphere() const; virtual SGSphered computeBoundingSphere() const;
@@ -54,7 +54,7 @@ private:
/// The bounding sphere as given by the lod node. /// The bounding sphere as given by the lod node.
SGSphered _boundingSphere; SGSphered _boundingSphere;
/// The osg loader options that are active for this subtree /// The osg loader options that are active for this subtree
osg::ref_ptr<osg::Referenced> _options; osg::ref_ptr<const osg::Referenced> _options;
}; };
} }