]> git.mxchange.org Git - simgear.git/blobdiff - simgear/scene/model/ModelRegistry.cxx
Build boundingvolumes in the model loading phase.
[simgear.git] / simgear / scene / model / ModelRegistry.cxx
index ab9e03ac80e4da0f39669688aacbfd6f3ccd7ff2..7c2afff648f3e10ab7aabf85770f3d22a02cf367 100644 (file)
 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 #include "ModelRegistry.hxx"
 
+#include <algorithm>
+#include <utility>
+#include <vector>
+
+#include <OpenThreads/ScopedLock>
+
 #include <osg/observer_ptr>
 #include <osg/ref_ptr>
 #include <osg/Group>
 #include <simgear/scene/util/SGSceneFeatures.hxx>
 #include <simgear/scene/util/SGStateAttributeVisitor.hxx>
 #include <simgear/scene/util/SGTextureStateAttributeVisitor.hxx>
+#include <simgear/scene/util/NodeAndDrawableVisitor.hxx>
 
 #include <simgear/structure/exception.hxx>
 #include <simgear/props/props.hxx>
 #include <simgear/props/props_io.hxx>
 #include <simgear/props/condition.hxx>
 
+#include "BoundingVolumeBuildVisitor.hxx"
+
 using namespace std;
 using namespace osg;
 using namespace osgUtil;
 using namespace osgDB;
 using namespace simgear;
 
+using OpenThreads::ReentrantMutex;
+using OpenThreads::ScopedLock;
+
 // Little helper class that holds an extra reference to a
 // loaded 3d model.
 // Since we clone all structural nodes from our 3d models,
@@ -72,71 +84,144 @@ private:
   ref_ptr<Referenced> mReferenced;
 };
 
-// Visitor for 
-class SGTextureUpdateVisitor : public SGTextureStateAttributeVisitor {
+// Set the name of a Texture to the simple name of its image
+// file. This can be used to do livery substitution after the image
+// has been deallocated.
+class TextureNameVisitor  : public NodeAndDrawableVisitor {
 public:
-  SGTextureUpdateVisitor(const FilePathList& pathList) :
-    mPathList(pathList)
-  { }
-  Texture2D* textureReplace(int unit,
-                            StateSet::RefAttributePair& refAttr)
-  {
-    Texture2D* texture;
-    texture = dynamic_cast<Texture2D*>(refAttr.first.get());
-    if (!texture)
-      return 0;
-    
-    ref_ptr<Image> image = texture->getImage(0);
-    if (!image)
-      return 0;
-
-    // The currently loaded file name
-    string fullFilePath = image->getFileName();
-    // The short name
-    string fileName = getSimpleFileName(fullFilePath);
-    // The name that should be found with the current database path
-    string fullLiveryFile = findFileInPath(fileName, mPathList);
-    // If they are identical then there is nothing to do
-    if (fullLiveryFile == fullFilePath)
-      return 0;
-
-    image = readImageFile(fullLiveryFile);
-    if (!image)
-      return 0;
+    TextureNameVisitor(NodeVisitor::TraversalMode tm = NodeVisitor::TRAVERSE_ALL_CHILDREN) :
+        NodeAndDrawableVisitor(tm)
+    {
+    }
 
-    CopyOp copyOp(CopyOp::DEEP_COPY_ALL & ~CopyOp::DEEP_COPY_IMAGES);
-    texture = static_cast<Texture2D*>(copyOp(texture));
-    if (!texture)
-      return 0;
-    texture->setImage(image.get());
-    return texture;
-  }
-  virtual void apply(StateSet* stateSet)
-  {
-    if (!stateSet)
-      return;
+    virtual void apply(Node& node)
+    {
+        nameTextures(node.getStateSet());
+        traverse(node);
+    }
 
-    // get a copy that we can safely modify the statesets values.
-    StateSet::TextureAttributeList attrList;
-    attrList = stateSet->getTextureAttributeList();
-    for (unsigned unit = 0; unit < attrList.size(); ++unit) {
-      StateSet::AttributeList::iterator i = attrList[unit].begin();
-      while (i != attrList[unit].end()) {
-        Texture2D* texture = textureReplace(unit, i->second);
-        if (texture) {
-          stateSet->removeTextureAttribute(unit, i->second.first.get());
-          stateSet->setTextureAttribute(unit, texture, i->second.second);
-          stateSet->setTextureMode(unit, GL_TEXTURE_2D, StateAttribute::ON);
+    virtual void apply(Drawable& drawable)
+    {
+        nameTextures(drawable.getStateSet());
+    }
+protected:
+    void nameTextures(StateSet* stateSet)
+    {
+        if (!stateSet)
+            return;
+        int numUnits = stateSet->getTextureAttributeList().size();
+        for (int i = 0; i < numUnits; ++i) {
+            StateAttribute* attr
+                = stateSet->getTextureAttribute(i, StateAttribute::TEXTURE);
+            Texture2D* texture = dynamic_cast<Texture2D*>(attr);
+            if (!texture || !texture->getName().empty())
+                continue;
+            const Image *image = texture->getImage();
+            if (!image)
+                continue;
+            texture->setName(image->getFileName());
         }
-        ++i;
-      }
     }
-  }
+};
+
+// Change the StateSets of a model to hold different textures based on
+// a livery path.
+
+class TextureUpdateVisitor : public NodeAndDrawableVisitor {
+public:
+    TextureUpdateVisitor(const FilePathList& pathList) :
+        NodeAndDrawableVisitor(NodeVisitor::TRAVERSE_ALL_CHILDREN),
+        _pathList(pathList)
+    {
+    }
+    
+    virtual void apply(Node& node)
+    {
+        StateSet* stateSet = cloneStateSet(node.getStateSet());
+        if (stateSet)
+            node.setStateSet(stateSet);
+        traverse(node);
+    }
+
+    virtual void apply(Drawable& drawable)
+    {
+        StateSet* stateSet = cloneStateSet(drawable.getStateSet());
+        if (stateSet)
+            drawable.setStateSet(stateSet);
+    }
+    // Copied from Mathias' earlier SGTextureUpdateVisitor
+protected:
+    Texture2D* textureReplace(int unit, const StateAttribute* attr)
+    {
+        const Texture2D* texture = dynamic_cast<const Texture2D*>(attr);
 
+        if (!texture)
+            return 0;
+    
+        const Image* image = texture->getImage();
+        const string* fullFilePath = 0;
+        if (image) {
+            // The currently loaded file name
+            fullFilePath = &image->getFileName();
+
+        } else {
+            fullFilePath = &texture->getName();
+        }
+        // The short name
+        string fileName = getSimpleFileName(*fullFilePath);
+        if (fileName.empty())
+            return 0;
+        // The name that should be found with the current database path
+        string fullLiveryFile = findFileInPath(fileName, _pathList);
+        // If it is empty or they are identical then there is nothing to do
+        if (fullLiveryFile.empty() || fullLiveryFile == *fullFilePath)
+            return 0;
+        Image* newImage = readImageFile(fullLiveryFile);
+        if (!newImage)
+            return 0;
+        CopyOp copyOp(CopyOp::DEEP_COPY_ALL & ~CopyOp::DEEP_COPY_IMAGES);
+        Texture2D* newTexture = static_cast<Texture2D*>(copyOp(texture));
+        if (!newTexture) {
+            return 0;
+        } else {
+            newTexture->setImage(newImage);
+            return newTexture;
+        }
+    }
+    
+    StateSet* cloneStateSet(const StateSet* stateSet)
+    {
+        typedef pair<int, Texture2D*> Tex2D;
+        vector<Tex2D> newTextures;
+        StateSet* result = 0;
+
+        if (!stateSet)
+            return 0;
+        int numUnits = stateSet->getTextureAttributeList().size();
+        if (numUnits > 0) {
+            for (int i = 0; i < numUnits; ++i) {
+                const StateAttribute* attr
+                    = stateSet->getTextureAttribute(i, StateAttribute::TEXTURE);
+                Texture2D* newTexture = textureReplace(i, attr);
+                if (newTexture)
+                    newTextures.push_back(Tex2D(i, newTexture));
+            }
+            if (!newTextures.empty()) {
+                result = static_cast<StateSet*>(stateSet->clone(CopyOp()));
+                for (vector<Tex2D>::iterator i = newTextures.begin();
+                     i != newTextures.end();
+                     ++i) {
+                    result->setTextureAttribute(i->first, i->second);
+                }
+            }
+        }
+        return result;
+    }
 private:
-  FilePathList mPathList;
+    FilePathList _pathList;
 };
 
+
 class SGTexCompressionVisitor : public SGTextureStateAttributeVisitor {
 public:
   virtual void apply(int, StateSet::RefAttributePair& refAttr)
@@ -172,7 +257,7 @@ public:
     texture = dynamic_cast<Texture*>(refAttr.first.get());
     if (!texture)
       return;
-    
+
     texture->setDataVariance(Object::STATIC);
   }
 
@@ -196,34 +281,52 @@ public:
     material->setColorMode(Material::AMBIENT_AND_DIFFUSE);
   }
 };
+
 } // namespace
 
+Node* DefaultProcessPolicy::process(Node* node, const string& filename,
+                                    const ReaderWriter::Options* opt)
+{
+    TextureNameVisitor nameVisitor;
+    node->accept(nameVisitor);
+    return node;
+}
+
 ReaderWriter::ReadResult
 ModelRegistry::readImage(const string& fileName,
                          const ReaderWriter::Options* opt)
 {
+    ScopedLock<ReentrantMutex> lock(readerMutex);
     CallbackMap::iterator iter
         = imageCallbackMap.find(getFileExtension(fileName));
-    if (iter != imageCallbackMap.end() && iter->second.valid())
-        return iter->second->readImage(fileName, opt);
-    string absFileName = findDataFile(fileName);
-    if (!fileExists(absFileName)) {
-        SG_LOG(SG_IO, SG_ALERT, "Cannot find image file \""
-               << fileName << "\"");
-        return ReaderWriter::ReadResult::FILE_NOT_FOUND;
-    }
-
-    Registry* registry = Registry::instance();
-    ReaderWriter::ReadResult res;
-    res = registry->readImageImplementation(absFileName, opt);
-    if (res.loadedFromCache())
-        SG_LOG(SG_IO, SG_INFO, "Returning cached image \""
-               << res.getImage()->getFileName() << "\"");
-    else
-        SG_LOG(SG_IO, SG_INFO, "Reading image \""
-               << res.getImage()->getFileName() << "\"");
+    // XXX Workaround for OSG plugin bug
+    {
+        if (iter != imageCallbackMap.end() && iter->second.valid())
+            return iter->second->readImage(fileName, opt);
+        string absFileName = findDataFile(fileName, opt);
+        if (!fileExists(absFileName)) {
+            SG_LOG(SG_IO, SG_ALERT, "Cannot find image file \""
+                   << fileName << "\"");
+            return ReaderWriter::ReadResult::FILE_NOT_FOUND;
+        }
 
-    return res;
+        Registry* registry = Registry::instance();
+        ReaderWriter::ReadResult res;
+        res = registry->readImageImplementation(absFileName, opt);
+        if (!res.success()) {
+          SG_LOG(SG_IO, SG_WARN, "Image loading failed:" << res.message());
+          return res;
+        }
+        
+        if (res.loadedFromCache())
+            SG_LOG(SG_IO, SG_INFO, "Returning cached image \""
+                   << res.getImage()->getFileName() << "\"");
+        else
+            SG_LOG(SG_IO, SG_INFO, "Reading image \""
+                   << res.getImage()->getFileName() << "\"");
+
+        return res;
+    }
 }
 
 
@@ -279,7 +382,7 @@ osg::Node* OptimizeModelPolicy::optimize(osg::Node* node,
     // STATIC so that textures will be globally shared.
     SGTexDataVarianceVisitor dataVarianceVisitor;
     node->accept(dataVarianceVisitor);
-      
+
     SGTexCompressionVisitor texComp;
     node->accept(texComp);
     return node;
@@ -309,8 +412,9 @@ osg::Node* DefaultCopyPolicy::copy(osg::Node* model, const string& fileName,
     res->addObserver(databaseReference);
 
     // Update liveries
-    SGTextureUpdateVisitor liveryUpdate(getDataFilePathList());
+    TextureUpdateVisitor liveryUpdate(opt->getDatabasePathList());
     res->accept(liveryUpdate);
+
     return res;
 }
 
@@ -319,12 +423,13 @@ string OSGSubstitutePolicy::substitute(const string& name,
 {
     string fileSansExtension = getNameLessExtension(name);
     string osgFileName = fileSansExtension + ".osg";
-    string absFileName = findDataFile(osgFileName);
+    string absFileName = findDataFile(osgFileName, opt);
     return absFileName;
 }
 
 ModelRegistry::ModelRegistry() :
-    _defaultCallback(new DefaultCallback(""))
+    _defaultCallback(new DefaultCallback("")),
+    _nestingLevel(0)
 {
 }
 
@@ -342,28 +447,34 @@ ModelRegistry::addNodeCallbackForExtension(const string& extension,
     nodeCallbackMap.insert(CallbackMap::value_type(extension, callback));
 }
 
-ref_ptr<ModelRegistry> ModelRegistry::instance;
-
-ModelRegistry* ModelRegistry::getInstance()
-
-{
-    if (!instance.valid())
-        instance = new ModelRegistry;
-    return instance.get();
-}
-
 ReaderWriter::ReadResult
 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 {
@@ -382,7 +493,7 @@ public:
     registry->setOptions(options);
     registry->getOrCreateSharedStateManager()->
       setShareMode(SharedStateManager::SHARE_STATESETS);
-    registry->setReadFileCallback(ModelRegistry::getInstance());
+    registry->setReadFileCallback(ModelRegistry::instance());
   }
 };
 
@@ -395,6 +506,25 @@ struct ACOptimizePolicy : public OptimizeModelPolicy {
     {
         _osgOptions &= ~Optimizer::TRISTRIP_GEOMETRY;
     }
+    Node* optimize(Node* node, const string& fileName,
+                   const ReaderWriter::Options* opt)
+    {
+        ref_ptr<Node> optimized
+            = OptimizeModelPolicy::optimize(node, fileName, opt);
+        Group* group = dynamic_cast<Group*>(optimized.get());
+        MatrixTransform* transform
+            = dynamic_cast<MatrixTransform*>(optimized.get());
+        if (((transform && transform->getMatrix().isIdentity()) || group)
+            && group->getName().empty()
+            && group->getNumChildren() == 1) {
+            optimized = static_cast<Node*>(group->getChild(0));
+            group = dynamic_cast<Group*>(optimized.get());
+            if (group && group->getName().empty()
+                && group->getNumChildren() == 1)
+                optimized = static_cast<Node*>(group->getChild(0));
+        }
+        return optimized.release();
+    }
 };
 
 struct ACProcessPolicy {
@@ -436,19 +566,4 @@ typedef ModelRegistryCallback<ACProcessPolicy, DefaultCachePolicy,
 namespace
 {
 ModelRegistryCallbackProxy<ACCallback> g_acRegister("ac");
-}   
-
-
-ReaderWriter::ReadResult
-OSGFileCallback::readImage(const string& fileName,
-                           const ReaderWriter::Options* opt)
-{
-    return Registry::instance()->readImageImplementation(fileName, opt);
-}
-
-ReaderWriter::ReadResult
-OSGFileCallback::readNode(const string& fileName,
-                          const ReaderWriter::Options* opt)
-{
-    return Registry::instance()->readNodeImplementation(fileName, opt);
 }