diff --git a/simgear/scene/model/BoundingVolumeBuildVisitor.hxx b/simgear/scene/model/BoundingVolumeBuildVisitor.hxx new file mode 100644 index 00000000..fd2fb8e7 --- /dev/null +++ b/simgear/scene/model/BoundingVolumeBuildVisitor.hxx @@ -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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +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 + 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 _vertices; + GLenum _modeCache; + + SGSharedPtr _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(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 diff --git a/simgear/scene/model/ModelRegistry.cxx b/simgear/scene/model/ModelRegistry.cxx index 315f079e..7c2afff6 100644 --- a/simgear/scene/model/ModelRegistry.cxx +++ b/simgear/scene/model/ModelRegistry.cxx @@ -50,6 +50,8 @@ #include #include +#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 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 { diff --git a/simgear/scene/model/ModelRegistry.hxx b/simgear/scene/model/ModelRegistry.hxx index 7b7ffe4e..84ae5f0d 100644 --- a/simgear/scene/model/ModelRegistry.hxx +++ b/simgear/scene/model/ModelRegistry.hxx @@ -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