]> git.mxchange.org Git - simgear.git/blobdiff - simgear/scene/model/BVHPageNodeOSG.cxx
Work around apparent OSG 3.2.0 normal binding bug.
[simgear.git] / simgear / scene / model / BVHPageNodeOSG.cxx
index e138e95a31267316bb933d2fee07fb7cf828e4be..e8051847982401f821eca3bce7e4843bfc18deb0 100644 (file)
 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 //
 
+#ifdef HAVE_CONFIG_H
+#  include <simgear_config.h>
+#endif
+
 #include "BVHPageNodeOSG.hxx"
 
 #include "../../bvh/BVHPageRequest.hxx"
 #include "../../bvh/BVHPager.hxx"
 
+#include <osg/io_utils>
 #include <osg/Camera>
 #include <osg/Drawable>
 #include <osg/Geode>
@@ -27,7 +32,6 @@
 #include <osg/PagedLOD>
 #include <osg/ProxyNode>
 #include <osg/Transform>
-#include <osg/TriangleFunctor>
 #include <osgDB/ReadFile>
 
 #include <simgear/scene/material/mat.hxx>
 
 #include <simgear/bvh/BVHStaticGeometryBuilder.hxx>
 
+#include "PrimitiveCollector.hxx"
+
 namespace simgear {
 
 class BVHPageNodeOSG::_NodeVisitor : public osg::NodeVisitor {
 public:
-    class PFunctor : public osg::PrimitiveFunctor {
-    public:
-        PFunctor() :
-            _modeCache(0)
-        {
-            _geometryBuilder = new BVHStaticGeometryBuilder;
-        }
-        virtual ~PFunctor()
+    struct _PrimitiveCollector : public PrimitiveCollector {
+        _PrimitiveCollector(_NodeVisitor& nodeVisitor) :
+            _nodeVisitor(nodeVisitor)
         { }
+        virtual ~_PrimitiveCollector()
+        { }
+        virtual void addPoint(const osg::Vec3d& v1)
+        { }
+        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)
+        { _nodeVisitor.addTriangle(v1, v2, v3); }
+    private:
+        _NodeVisitor& _nodeVisitor;
+    };
 
-        virtual void setVertexArray(unsigned int count, const osg::Vec2* vertices)
-        {
-            _vertices.resize(count);
-            for (unsigned i = 0; i < count; ++i)
-                _vertices[i] = SGVec3f(vertices[i][0], vertices[i][1], 0);
-        }
-
-        virtual void setVertexArray(unsigned int count, const osg::Vec3* vertices)
-        {
-            _vertices.resize(count);
-            for (unsigned i = 0; i < count; ++i)
-                _vertices[i] = SGVec3f(vertices[i][0], vertices[i][1], vertices[i][2]);
-        }
-
-        virtual void setVertexArray(unsigned int count, const osg::Vec4* vertices)
-        {
-            _vertices.resize(count);
-            for (unsigned i = 0; i < count; ++i)
-                _vertices[i] = SGVec3f(vertices[i][0]/vertices[i][3],
-                                       vertices[i][1]/vertices[i][3],
-                                       vertices[i][2]/vertices[i][3]);
-        }
-
-        virtual void setVertexArray(unsigned int count, const osg::Vec2d* vertices)
-        {
-            _vertices.resize(count);
-            for (unsigned i = 0; i < count; ++i)
-                _vertices[i] = SGVec3f(vertices[i][0], vertices[i][1], 0);
-        }
-
-        virtual void setVertexArray(unsigned int count, const osg::Vec3d* vertices)
-        {
-            _vertices.resize(count);
-            for (unsigned i = 0; i < count; ++i)
-                _vertices[i] = SGVec3f(vertices[i][0], vertices[i][1], vertices[i][2]);
-        }
-
-        virtual void setVertexArray(unsigned int count, const osg::Vec4d* vertices) 
-        {
-            _vertices.resize(count);
-            for (unsigned i = 0; i < count; ++i)
-                _vertices[i] = SGVec3f(vertices[i][0]/vertices[i][3],
-                                       vertices[i][1]/vertices[i][3],
-                                       vertices[i][2]/vertices[i][3]);
-        }
-
-        virtual void drawArrays(GLenum mode, GLint first, GLsizei count)
-        {
-            if (_vertices.empty() || count <= 0)
-                return;
-
-            GLsizei end = first + count;
-            switch(mode) {
-            case (GL_TRIANGLES):
-                for (GLsizei i = first; i < end - 2; i += 3) {
-                    addTriangle(i, i + 1, i + 2);
-                }
-                break;
-
-            case (GL_TRIANGLE_STRIP):
-                for (GLsizei i = first; i < end - 2; ++i) {
-                    addTriangle(i, i + 1, i + 2);
-                }
-                break;
-
-            case (GL_QUADS):
-                for (GLsizei i = first; i < end - 3; i += 4) {
-                    addQuad(i, i + 1, i + 2, i + 3);
-                }
-                break;
-
-            case (GL_QUAD_STRIP):
-                for (GLsizei i = first; i < end - 3; i += 2) {
-                    addQuad(i, i + 1, i + 2, i + 3);
-                }
-                break;
-
-            case (GL_POLYGON): // treat polygons as GL_TRIANGLE_FAN
-            case (GL_TRIANGLE_FAN):
-                for (GLsizei i = first; i < end - 2; ++i) {
-                    addTriangle(first, i + 1, i + 2);
-                }
-                break;
-
-            case (GL_POINTS):
-                for (GLsizei i = first; i < end; ++i) {
-                    addPoint(i);
-                }
-                break;
-
-            case (GL_LINES):
-                for (GLsizei i = first; i < end - 1; i += 2) {
-                    addLine(i, i + 1);
-                }
-                break;
-
-            case (GL_LINE_STRIP):
-                for (GLsizei i = first; i < end - 1; ++i) {
-                    addLine(i, i + 1);
-                }
-                break;
-
-            case (GL_LINE_LOOP):
-                for (GLsizei i = first; i < end - 1; ++i) {
-                    addLine(i, i + 1);
-                }
-                addLine(end - 1, first);
-                break;
-
-            default:
-                break;
+    struct _NodeBin {
+        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;
             }
         }
-  
-        virtual void drawElements(GLenum mode, GLsizei count, const GLubyte* indices)
-        {
-            drawElementsTemplate(mode, count, indices);
-        }
-
-        virtual void drawElements(GLenum mode, GLsizei count, const GLushort* indices)
-        {
-            drawElementsTemplate(mode, count, indices);
-        }
-
-        virtual void drawElements(GLenum mode, GLsizei count, const GLuint* indices)
-        {
-            drawElementsTemplate(mode, count, indices);
-        }
-
-        virtual void begin(GLenum mode)
-        {
-            _modeCache = mode;
-            _vertices.resize(0);
-        }
-
-        virtual void vertex(const osg::Vec2& v)
-        {
-            _vertices.push_back(SGVec3f(v[0], v[1], 0));
-        }
-        virtual void vertex(const osg::Vec3& v)
-        {
-            _vertices.push_back(SGVec3f(v[0], v[1], v[2]));
-        }
-        virtual void vertex(const osg::Vec4& v)
-        {
-            _vertices.push_back(SGVec3f(v[0]/v[3], v[1]/v[3], v[2]/v[3]));
-        }
-        virtual void vertex(float x, float y)
-        {
-            _vertices.push_back(SGVec3f(x, y, 0));
-        }
-        virtual void vertex(float x, float y, float z)
-        {
-            _vertices.push_back(SGVec3f(x, y, z));
-        }
-        virtual void vertex(float x, float y, float z, float w)
+        
+        void addNode(const SGSharedPtr<BVHNode>& node)
         {
-            _vertices.push_back(SGVec3f(x/w, y/w, z/w));
-        }
-        virtual void end()
-        {
-            if (_vertices.empty())
+            if (!node.valid())
                 return;
-
-            drawArrays(_modeCache, 0, _vertices.size());
-        }
-
-        template<typename index_type>
-        void drawElementsTemplate(GLenum mode, GLsizei count,
-                                  const index_type* indices)
-        {
-            if (_vertices.empty() || indices == 0 || count <= 0)
+            if (node->getBoundingSphere().empty())
                 return;
-    
-            switch(mode) {
-            case (GL_TRIANGLES):
-                for (GLsizei i = 0; i < count - 2; i += 3) {
-                    addTriangle(indices[i], indices[i + 1], indices[i + 2]);
-                }
-                break;
-
-            case (GL_TRIANGLE_STRIP):
-                for (GLsizei i = 0; i < count - 2; ++i) {
-                    addTriangle(indices[i], indices[i + 1], indices[i + 2]);
-                }
-                break;
-
-            case (GL_QUADS):
-                for (GLsizei i = 0; i < count - 3; i += 4) {
-                    addQuad(indices[i], indices[i + 1], indices[i + 2], indices[i + 3]);
-                }
-                break;
-
-            case (GL_QUAD_STRIP):
-                for (GLsizei i = 0; i < count - 3; i += 2) {
-                    addQuad(indices[i], indices[i + 1], indices[i + 2], indices[i + 3]);
-                }
-                break;
-
-            case (GL_POLYGON):
-            case (GL_TRIANGLE_FAN):
-                for (GLsizei i = 0; i < count - 2; ++i) {
-                    addTriangle(indices[0], indices[i + 1], indices[i + 2]);
-                }
-                break;
-
-            case (GL_POINTS):
-                for(GLsizei i = 0; i < count; ++i) {
-                    addPoint(indices[i]);
-                }
-                break;
-
-            case (GL_LINES):
-                for (GLsizei i = 0; i < count - 1; i += 2) {
-                    addLine(indices[i], indices[i + 1]);
-                }
-                break;
-
-            case (GL_LINE_STRIP):
-                for (GLsizei i = 0; i < count - 1; ++i) {
-                    addLine(indices[i], indices[i + 1]);
-                }
-                break;
-
-            case (GL_LINE_LOOP):
-                for (GLsizei i = 0; i < count - 1; ++i) {
-                    addLine(indices[i], indices[i + 1]);
-                }
-                addLine(indices[count - 1], indices[0]);
-                break;
-
-            default:
-                break;
-            }
-        }    
-
-        void addPoint(unsigned i1)
-        {
-            addPoint(_vertices[i1]);
-        }
-        void addLine(unsigned i1, unsigned i2)
-        {
-            addLine(_vertices[i1], _vertices[i2]);
-        }
-        void addTriangle(unsigned i1, unsigned i2, unsigned i3)
-        {
-            addTriangle(_vertices[i1], _vertices[i2], _vertices[i3]);
-        }
-        void addQuad(unsigned i1, unsigned i2, unsigned i3, unsigned i4)
-        {
-            addQuad(_vertices[i1], _vertices[i2], _vertices[i3], _vertices[i4]);
-        }
-
-        void addPoint(const SGVec3f& v1)
-        {
-        }
-        void addLine(const SGVec3f& v1, const SGVec3f& v2)
-        {
-        }
-        void addTriangle(const SGVec3f& v1, const SGVec3f& v2, const SGVec3f& v3)
-        {
-            _geometryBuilder->addTriangle(v1, v2, v3);
-        }
-        void addQuad(const SGVec3f& v1, const SGVec3f& v2,
-                     const SGVec3f& v3, const SGVec3f& v4)
-        {
-            _geometryBuilder->addTriangle(v1, v2, v3);
-            _geometryBuilder->addTriangle(v1, v3, v4);
-        }
-
-        BVHNode* buildTreeAndClear()
-        {
-            BVHNode* bvNode = _geometryBuilder->buildTree();
-            _geometryBuilder = new BVHStaticGeometryBuilder;
-            _vertices.clear();
-            return bvNode;
-        }
-
-        void swap(PFunctor& primitiveFunctor)
-        {
-            _vertices.swap(primitiveFunctor._vertices);
-            std::swap(_modeCache, primitiveFunctor._modeCache);
-            std::swap(_geometryBuilder, primitiveFunctor._geometryBuilder);
-        }
-
-        void setCurrentMaterial(const BVHMaterial* material)
-        {
-            _geometryBuilder->setCurrentMaterial(material);
-        }
-        const BVHMaterial* getCurrentMaterial() const
-        {
-            return _geometryBuilder->getCurrentMaterial();
+            _nodeVector.push_back(node);
         }
-
-        std::vector<SGVec3f> _vertices;
-        GLenum _modeCache;
-
-        SGSharedPtr<BVHStaticGeometryBuilder> _geometryBuilder;
+        
+    private:
+        typedef std::vector<SGSharedPtr<BVHNode> > _NodeVector;
+        
+        // The current pending node vector.
+        _NodeVector _nodeVector;
     };
-
-    _NodeVisitor() :
-        osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN)
+    
+    _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);
     }
@@ -362,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 = _primitiveFunctor.getCurrentMaterial();
-        const BVHMaterial* material = SGMaterialLib::findMaterial(geode);
-        if (material)
-            _primitiveFunctor.setCurrentMaterial(material);
-        return oldMaterial;
+        _geometryBuilder->addTriangle(toVec3f(toSG(_localToWorldMatrix.preMult(v1))),
+                                      toVec3f(toSG(_localToWorldMatrix.preMult(v2))),
+                                      toVec3f(toSG(_localToWorldMatrix.preMult(v3))));
+    }
+
+    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)
     {
-        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)
-            geode.getDrawable(i)->accept(_primitiveFunctor);
+            geode.getDrawable(i)->accept(primitiveCollector);
 
-        _primitiveFunctor.setCurrentMaterial(oldMaterial);
+        _geometryBuilder->setCurrentMaterial(oldMaterial);
     }
 
-    virtual void apply(osg::Group& group)
+    virtual void apply(osg::Node& node)
     {
-        // FIXME optimize this to collapse more leafs
-
-        // push the current active primitive list
-        PFunctor previousPrimitives;
-        _primitiveFunctor.swap(previousPrimitives);
-
-        const BVHMaterial* mat = previousPrimitives.getCurrentMaterial();
-        _primitiveFunctor.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);
-            }
+        if (_flatten) {
+            traverse(node);
+        } else {
+            _NodeVisitor nodeVisitor(_flatten, _localToWorldMatrix);
+            nodeVisitor.traverse(node);
+            _nodeBin.addNode(nodeVisitor.getNode(osg::Matrix::identity()));
         }
-
-        // pop the current active primitive list
-        _primitiveFunctor.swap(previousPrimitives);
     }
 
     virtual void apply(osg::Transform& transform)
@@ -426,42 +169,30 @@ public:
         if (transform.getReferenceFrame() != osg::Transform::RELATIVE_RF)
             return;
 
-        osg::Matrix matrix;
-        if (!transform.computeLocalToWorldMatrix(matrix, this))
-            return;
-
-        // push the current active primitive list
-        PFunctor previousPrimitives;
-        _primitiveFunctor.swap(previousPrimitives);
-
-        const BVHMaterial* mat = previousPrimitives.getCurrentMaterial();
-        _primitiveFunctor.setCurrentMaterial(mat);
-
-        NodeVector nodeVector;
-        _nodeVector.swap(nodeVector);
-
-        // walk the children
-        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();
+        // FIXME identify and handle dynamic transforms
 
-        _nodeVector.swap(nodeVector);
+        if (_flatten) {
+            // propagate the matrix further down into the nodes and
+            // build a flat leaf tree as far as possible
 
-        // pop the current active primitive list
-        _primitiveFunctor.swap(previousPrimitives);
+            // save away and accumulate the localToWorldMatrix
+            osg::Matrix localToWorldMatrix = _localToWorldMatrix;
+            if (!transform.computeLocalToWorldMatrix(_localToWorldMatrix, this))
+                return;
 
-        if (!nodeVector.empty()) {
-            SGSharedPtr<BVHTransform> bvhTransform = new BVHTransform;
-            bvhTransform->setToWorldTransform(SGMatrixd(matrix.ptr()));
+            traverse(transform);
 
-            for (NodeVector::iterator i = nodeVector.begin();
-                 i != nodeVector.end(); ++i)
-                bvhTransform->addChild(i->get());
+            _localToWorldMatrix = localToWorldMatrix;
+        } else {
+            // accumulate the localToWorldMatrix
+            osg::Matrix localToWorldMatrix = _localToWorldMatrix;
+            if (!transform.computeLocalToWorldMatrix(localToWorldMatrix, this))
+                return;
 
-            _nodeVector.push_back(bvhTransform);
+            // evaluate the loca to world matrix here in this group node.
+            _NodeVisitor nodeVisitor(_flatten);
+            nodeVisitor.traverse(transform);
+            _nodeBin.addNode(nodeVisitor.getNode(localToWorldMatrix));
         }
     }
 
@@ -472,42 +203,46 @@ public:
         apply(static_cast<osg::Transform&>(camera));
     }
 
-    void addBoundingVolumeTreeToNode()
-    {
-        // Build the flat tree.
-        BVHNode* bvNode = _primitiveFunctor.buildTreeAndClear();
-
-        // Nothing in there?
-        if (!bvNode)
-            return;
-        if (bvNode->getBoundingSphere().empty())
-            return;
-
-        _nodeVector.push_back(bvNode);
-    }
-
     virtual void apply(osg::PagedLOD& pagedLOD)
     {
-        float range = std::numeric_limits<float>::max();
         unsigned numFileNames = pagedLOD.getNumFileNames();
-        for (unsigned i = 0; i < numFileNames; ++i) {
-            if (range < pagedLOD.getMaxRange(i))
-                continue;
-            range = pagedLOD.getMaxRange(i);
-        }
-
-        std::vector<std::string> nameList;
-        for (unsigned i = pagedLOD.getNumChildren(); i < pagedLOD.getNumFileNames(); ++i) {
-            if (pagedLOD.getMaxRange(i) <= range) {
+        if (_flatten) {
+            // In flattening mode treat lod nodes as proxy nodes
+            for (unsigned i = 0; i < numFileNames; ++i) {
+                if (i < pagedLOD.getNumChildren() && pagedLOD.getChild(i))
+                    continue;
+                osg::ref_ptr<osg::Node> node;
+                if (pagedLOD.getMinRange(i) <= 0) {
+                    osg::ref_ptr<const osgDB::Options> options;
+                    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));
             }
-        }
 
-        if (!nameList.empty()) {
-            SGSphered boundingSphere(toVec3d(toSG(pagedLOD.getCenter())), pagedLOD.getRadius());
-            _nodeVector.push_back(new BVHPageNodeOSG(nameList, boundingSphere, pagedLOD.getDatabaseOptions()));
+            _NodeBin nodeBin;
+            if (!nameList.empty()) {
+                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
         apply(static_cast<osg::LOD&>(pagedLOD));
     }
@@ -518,10 +253,10 @@ public:
         for (unsigned i = 0; i < numFileNames; ++i) {
             if (i < proxyNode.getNumChildren() && proxyNode.getChild(i))
                 continue;
-            // FIXME evaluate proxyNode.getDatabasePath()
+            osg::ref_ptr<const osgDB::Options> options;
+            options = getOptions(proxyNode.getDatabaseOptions(), proxyNode.getDatabasePath());
             osg::ref_ptr<osg::Node> node;
-            node = osgDB::readNodeFile(proxyNode.getFileName(i),
-              dynamic_cast<const osgDB::Options*>(proxyNode.getDatabaseOptions()));
+            node = osgDB::readRefNodeFile(proxyNode.getFileName(i), options.get());
             if (!node.valid())
                 node = new osg::Group;
             if (i < proxyNode.getNumChildren())
@@ -533,25 +268,47 @@ public:
         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();
-
-        if (_nodeVector.empty())
-            return SGSharedPtr<BVHNode>();
-        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;
+        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;
+    }
+
+    SGSharedPtr<BVHNode> getNode(const osg::Matrix& matrix = osg::Matrix())
+    {
+        // Flush any pendig leaf nodes
+        if (_geometryBuilder.valid()) {
+            _nodeBin.addNode(_geometryBuilder->buildTree());
+            _geometryBuilder.clear();
+        }
+
+        return _nodeBin.getNode(matrix*_centerMatrix);
     }
 
 private:
-    PFunctor _primitiveFunctor;
-    typedef std::vector<SGSharedPtr<BVHNode> > NodeVector;
-    NodeVector _nodeVector;
+    // The part of the accumulated model view matrix that
+    // is put into a BVHTransform node.
+    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 {
@@ -587,26 +344,30 @@ public:
         return _pageNode.get();
     }
 
+private:
     SGSharedPtr<BVHPageNodeOSG> _pageNode;
     std::vector<SGSharedPtr<BVHNode> > _nodeVector;
 };
 
 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;
-    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())
         return SGSharedPtr<BVHNode>();
 
-    _NodeVisitor nodeVisitor;
+    bool flatten = (node->getBound()._radius < 30000);
+    _NodeVisitor nodeVisitor(flatten);
+    if (flatten)
+        nodeVisitor.setCenter(node->getBound()._center);
     node->accept(nodeVisitor);
-    return nodeVisitor.getBVHNode();
+    return nodeVisitor.getNode();
 }
 
 BVHPageNodeOSG::BVHPageNodeOSG(const std::string& name,
                                const SGSphered& boundingSphere,
-                               const osg::ref_ptr<osg::Referenced>& options) :
+                               const osg::ref_ptr<const osg::Referenced>& options) :
     _boundingSphere(boundingSphere),
     _options(options)
 {
@@ -615,7 +376,7 @@ BVHPageNodeOSG::BVHPageNodeOSG(const std::string& name,
 
 BVHPageNodeOSG::BVHPageNodeOSG(const std::vector<std::string>& nameList,
                                const SGSphered& boundingSphere,
-                               const osg::ref_ptr<osg::Referenced>& options) :
+                               const osg::ref_ptr<const osg::Referenced>& options) :
     _modelList(nameList),
     _boundingSphere(boundingSphere),
     _options(options)