]> git.mxchange.org Git - simgear.git/commitdiff
Build boundingvolumes in the model loading phase.
authorfrohlich <frohlich>
Sun, 1 Mar 2009 15:40:22 +0000 (15:40 +0000)
committerTim Moore <timoore@redhat.com>
Thu, 5 Mar 2009 09:32:06 +0000 (10:32 +0100)
Modified Files:
ModelRegistry.hxx ModelRegistry.cxx
Added Files:
BoundingVolumeBuildVisitor.hxx

simgear/scene/model/BoundingVolumeBuildVisitor.hxx [new file with mode: 0644]
simgear/scene/model/ModelRegistry.cxx
simgear/scene/model/ModelRegistry.hxx

diff --git a/simgear/scene/model/BoundingVolumeBuildVisitor.hxx b/simgear/scene/model/BoundingVolumeBuildVisitor.hxx
new file mode 100644 (file)
index 0000000..fd2fb8e
--- /dev/null
@@ -0,0 +1,511 @@
+
+// Copyright (C) 2008 - 2009  Mathias Froehlich - Mathias.Froehlich@web.de
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Library General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//
+
+#ifndef SimGear_BoundingVolumeBuildVisitor_hxx
+#define SimGear_BoundingVolumeBuildVisitor_hxx
+
+#include <osg/Camera>
+#include <osg/Drawable>
+#include <osg/Geode>
+#include <osg/Group>
+#include <osg/MatrixTransform>
+#include <osg/PagedLOD>
+#include <osg/Transform>
+#include <osg/TriangleFunctor>
+
+#include <simgear/scene/material/mat.hxx>
+#include <simgear/scene/material/matlib.hxx>
+#include <simgear/scene/util/SGNodeMasks.hxx>
+#include <simgear/scene/util/SGSceneUserData.hxx>
+#include <simgear/math/SGGeometry.hxx>
+
+#include <simgear/scene/bvh/BVHStaticGeometryBuilder.hxx>
+
+namespace simgear {
+
+class BoundingVolumeBuildVisitor : public osg::NodeVisitor {
+public:
+    class PFunctor : public osg::PrimitiveFunctor {
+    public:
+        PFunctor() :
+            _modeCache(0)
+        {
+            _geometryBuilder = new BVHStaticGeometryBuilder;
+        }
+        virtual ~PFunctor()
+        { }
+
+        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;
+
+            switch(mode) {
+            case (GL_TRIANGLES):
+                for (GLsizei i = first; i < first + count; i += 3) {
+                    addTriangle(i, i + 1, i + 2);
+                }
+                break;
+
+            case (GL_TRIANGLE_STRIP):
+                for (GLsizei i = 0; i < count; i += 3) {
+                    if (i%2)
+                        addTriangle(first + i, first + i + 2, first + i + 1);
+                    else
+                        addTriangle(first + i, first + i + 1, first + i + 2);
+                }
+                break;
+
+            case (GL_QUADS):
+                for (GLsizei i = first; i < first + count; i += 4) {
+                    addQuad(i, i + 1, i + 2, i + 3);
+                }
+                break;
+
+            case (GL_QUAD_STRIP):
+                for (GLsizei i = 0; i < count - 2; i += 2) {
+                    if (i%4)
+                        addQuad(first + i + 1, first + i, i + 3, first + i + 2);
+                    else
+                        addQuad(first + i, first + i + 1, first + i + 2, first + i + 3);
+                }
+                break;
+
+            case (GL_POLYGON): // treat polygons as GL_TRIANGLE_FAN
+            case (GL_TRIANGLE_FAN):
+                for (GLsizei i = first + 2; i < first + count; ++i) {
+                    addTriangle(first, i - 1, i);
+                }
+                break;
+
+            case (GL_POINTS):
+                for (GLsizei i = 0; i < count; ++i) {
+                    addPoint(first + i);
+                }
+                break;
+
+            case (GL_LINES):
+                for (GLsizei i = first; i < first + count; i += 2) {
+                    addLine(i, i + 1);
+                }
+                break;
+
+            case (GL_LINE_STRIP):
+                for (GLsizei i = first; i < first + count; ++i) {
+                    addLine(i, i + 1);
+                }
+                break;
+
+            case (GL_LINE_LOOP):
+                for (GLsizei i = first; i < first + count; ++i) {
+                    addLine(i, i + 1);
+                }
+                addLine(first + count - 1, first);
+                break;
+
+            default:
+                break;
+            }
+        }
+  
+        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)
+        {
+            _vertices.push_back(SGVec3f(x/w, y/w, z/w));
+        }
+        virtual void end()
+        {
+            if (_vertices.empty())
+                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)
+                return;
+    
+            switch(mode) {
+            case (GL_TRIANGLES):
+                for (GLsizei i = 0; i < count; i += 3) {
+                    addTriangle(indices[i], indices[i + 1], indices[i + 2]);
+                }
+                break;
+
+            case (GL_TRIANGLE_STRIP):
+                for (GLsizei i = 2; i < count; ++i) {
+                    if (i%2)
+                        addTriangle(indices[i - 2], indices[i], indices[i - 1]);
+                    else
+                        addTriangle(indices[i - 2], indices[i - 1], indices[i]);
+                }
+                break;
+
+            case (GL_QUADS):
+                for (GLsizei i = 0; i < count; i += 4) {
+                    addQuad(indices[i], indices[i + 1], indices[i + 2], indices[i + 3]);
+                }
+                break;
+
+            case (GL_QUAD_STRIP):
+                for (GLsizei i = 3; i < count; i += 2) {
+                    if (i%4)
+                        addQuad(indices[i - 2], indices[i - 3], indices[i], indices[i - 1]);
+                    else
+                        addQuad(indices[i - 3], indices[i - 2], indices[i - 1], indices[i]);
+                }
+                break;
+
+            case (GL_POLYGON):
+            case (GL_TRIANGLE_FAN):
+                for (GLsizei i = 2; i < count; ++i) {
+                    addTriangle(indices[0], indices[i - 1], indices[i]);
+                }
+                break;
+
+            case (GL_POINTS):
+                for(GLsizei i = 0; i < count; ++i) {
+                    addPoint(indices[i]);
+                }
+                break;
+
+            case (GL_LINES):
+                for (GLsizei i = 0; i < count; i += 2) {
+                    addLine(indices[i], indices[i + 1]);
+                }
+                break;
+
+            case (GL_LINE_STRIP):
+                for (GLsizei i = 0; i < count; ++i) {
+                    addLine(indices[i], indices[i + 1]);
+                }
+                break;
+
+            case (GL_LINE_LOOP):
+                for (GLsizei i = 0; i < count; ++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 SGMaterial* material)
+        {
+            _geometryBuilder->setCurrentMaterial(material);
+        }
+        const SGMaterial* getCurrentMaterial() const
+        {
+            return _geometryBuilder->getCurrentMaterial();
+        }
+
+        std::vector<SGVec3f> _vertices;
+        GLenum _modeCache;
+
+        SGSharedPtr<BVHStaticGeometryBuilder> _geometryBuilder;
+    };
+
+
+    // class PrimitiveIndexFunctor
+    // {
+    // public:
+
+    //     virtual ~PrimitiveIndexFunctor() {}
+
+    //     virtual void setVertexArray(unsigned int count,const Vec2* vertices) = 0;
+    //     virtual void setVertexArray(unsigned int count,const Vec3* vertices) = 0;
+    //     virtual void setVertexArray(unsigned int count,const Vec4* vertices) = 0;
+
+    //     virtual void setVertexArray(unsigned int count,const Vec2d* vertices) = 0;
+    //     virtual void setVertexArray(unsigned int count,const Vec3d* vertices) = 0;
+    //     virtual void setVertexArray(unsigned int count,const Vec4d* vertices) = 0;
+
+    //     virtual void drawArrays(GLenum mode,GLint first,GLsizei count) = 0;
+    //     virtual void drawElements(GLenum mode,GLsizei count,const GLubyte* indices) = 0;
+    //     virtual void drawElements(GLenum mode,GLsizei count,const GLushort* indices) = 0;
+    //     virtual void drawElements(GLenum mode,GLsizei count,const GLuint* indices) = 0;
+
+    //     virtual void begin(GLenum mode) = 0;
+    //     virtual void vertex(unsigned int pos) = 0;
+    //     virtual void end() = 0;
+    // };
+
+    BoundingVolumeBuildVisitor() :
+        osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN)
+    {
+        setTraversalMask(SG_NODEMASK_TERRAIN_BIT);
+    }
+    ~BoundingVolumeBuildVisitor()
+    {
+    }
+
+    const SGMaterial* pushMaterial(osg::StateSet* stateSet)
+    {
+        const SGMaterial* oldMaterial = _primitiveFunctor.getCurrentMaterial();
+        const SGMaterial* material = SGMaterialLib::findMaterial(stateSet);
+        if (material)
+            _primitiveFunctor.setCurrentMaterial(material);
+        return oldMaterial;
+    }
+
+    void fillWith(osg::Drawable* drawable)
+    {
+        const SGMaterial* oldMaterial = pushMaterial(drawable->getStateSet());
+        drawable->accept(_primitiveFunctor);
+        _primitiveFunctor.setCurrentMaterial(oldMaterial);
+    }
+
+    virtual void apply(osg::Geode& geode)
+    {
+        const SGMaterial* oldMaterial = pushMaterial(geode.getStateSet());
+
+        if (!hasBoundingVolumeTree(geode))
+            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)
+            addBoundingVolumeTreeToNode(geode);
+        _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());
+
+        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)
+    {
+        // 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&)
+    {
+        // Do nothing. In this case we get called by the loading process anyway
+    }
+
+    virtual void apply(osg::Camera& camera)
+    {
+        if (camera.getRenderOrder() != osg::Camera::NESTED_RENDER)
+            return;
+        apply(static_cast<osg::Transform&>(camera));
+    }
+
+    void addBoundingVolumeTreeToNode(osg::Node& node)
+    {
+        // Build the flat tree.
+        BVHNode* bvNode = _primitiveFunctor.buildTreeAndClear();
+
+        // Nothing in there?
+        if (!bvNode)
+            return;
+
+        SGSceneUserData* userData;
+        userData = SGSceneUserData::getOrCreateSceneUserData(&node);
+        userData->setBVHNode(bvNode);
+    }
+
+    bool hasBoundingVolumeTree(osg::Node& node)
+    {
+        SGSceneUserData* userData;
+        userData = SGSceneUserData::getSceneUserData(&node);
+        if (!userData)
+            return false;
+        if (!userData->getBVHNode())
+            return false;
+        return true;
+    }
+
+private:
+    PFunctor _primitiveFunctor;
+};
+
+}
+
+#endif
index 315f079ea481b1eca6f288375d92fe72e4bec39c..7c2afff648f3e10ab7aabf85770f3d22a02cf367 100644 (file)
@@ -50,6 +50,8 @@
 #include <simgear/props/props_io.hxx>
 #include <simgear/props/condition.hxx>
 
+#include "BoundingVolumeBuildVisitor.hxx"
+
 using namespace std;
 using namespace osg;
 using namespace osgUtil;
@@ -426,7 +428,8 @@ string OSGSubstitutePolicy::substitute(const string& name,
 }
 
 ModelRegistry::ModelRegistry() :
-    _defaultCallback(new DefaultCallback(""))
+    _defaultCallback(new DefaultCallback("")),
+    _nestingLevel(0)
 {
 }
 
@@ -449,15 +452,29 @@ ModelRegistry::readNode(const string& fileName,
                         const ReaderWriter::Options* opt)
 {
     ScopedLock<ReentrantMutex> lock(readerMutex);
+    ++_nestingLevel;
+
     // XXX Workaround for OSG plugin bug.
     Registry* registry = Registry::instance();
     ReaderWriter::ReadResult res;
-    Node* cached = 0;
     CallbackMap::iterator iter
         = nodeCallbackMap.find(getFileExtension(fileName));
+    ReaderWriter::ReadResult result;
     if (iter != nodeCallbackMap.end() && iter->second.valid())
-        return iter->second->readNode(fileName, opt);
-    return _defaultCallback->readNode(fileName, opt);
+        result = iter->second->readNode(fileName, opt);
+    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;
 }
 
 class SGReadCallbackInstaller {
index 7b7ffe4eb2b43b180c623e388035df76e5654211..84ae5f0dd0e3a74e9539ad255f68be551fca60b5 100644 (file)
@@ -227,6 +227,7 @@ protected:
     // 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