]> 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 a1f49f2a911d9e51013c7935d75ec59bc9f0c057..7c2afff648f3e10ab7aabf85770f3d22a02cf367 100644 (file)
 #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,
@@ -79,11 +84,57 @@ private:
   ref_ptr<Referenced> mReferenced;
 };
 
+// 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:
+    TextureNameVisitor(NodeVisitor::TraversalMode tm = NodeVisitor::TRAVERSE_ALL_CHILDREN) :
+        NodeAndDrawableVisitor(tm)
+    {
+    }
+
+    virtual void apply(Node& node)
+    {
+        nameTextures(node.getStateSet());
+        traverse(node);
+    }
+
+    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());
+        }
+    }
+};
+
 // Change the StateSets of a model to hold different textures based on
 // a livery path.
+
 class TextureUpdateVisitor : public NodeAndDrawableVisitor {
 public:
-    TextureUpdateVisitor(const FilePathList& pathList) : _pathList(pathList) {}
+    TextureUpdateVisitor(const FilePathList& pathList) :
+        NodeAndDrawableVisitor(NodeVisitor::TRAVERSE_ALL_CHILDREN),
+        _pathList(pathList)
+    {
+    }
+    
     virtual void apply(Node& node)
     {
         StateSet* stateSet = cloneStateSet(node.getStateSet());
@@ -98,7 +149,7 @@ public:
         if (stateSet)
             drawable.setStateSet(stateSet);
     }
-    // Copied whole from Mathias' earlier SGTextureUpdateVisitor
+    // Copied from Mathias' earlier SGTextureUpdateVisitor
 protected:
     Texture2D* textureReplace(int unit, const StateAttribute* attr)
     {
@@ -107,24 +158,27 @@ protected:
         if (!texture)
             return 0;
     
-        const Image* image = texture->getImage(0);
-        if (!image)
-            return 0;
+        const Image* image = texture->getImage();
+        const string* fullFilePath = 0;
+        if (image) {
+            // The currently loaded file name
+            fullFilePath = &image->getFileName();
 
-        // The currently loaded file name
-        const string& fullFilePath = image->getFileName();
+        } else {
+            fullFilePath = &texture->getName();
+        }
         // The short name
-        string fileName = getSimpleFileName(fullFilePath);
+        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)
+        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) {
@@ -134,6 +188,7 @@ protected:
             return newTexture;
         }
     }
+    
     StateSet* cloneStateSet(const StateSet* stateSet)
     {
         typedef pair<int, Texture2D*> Tex2D;
@@ -202,7 +257,7 @@ public:
     texture = dynamic_cast<Texture*>(refAttr.first.get());
     if (!texture)
       return;
-    
+
     texture->setDataVariance(Object::STATIC);
   }
 
@@ -227,54 +282,28 @@ public:
   }
 };
 
-// Work around an OSG bug - the file loaders don't use the file path
-// in options while the file is being loaded.
-
-struct OptionsPusher {
-    FilePathList localPathList;
-    bool validOptions;
-    OptionsPusher(const ReaderWriter::Options* options):
-        validOptions(false)
-    {
-        if (!options)
-            return;
-        Registry* registry = Registry::instance();
-        localPathList = registry->getDataFilePathList();
-        const FilePathList& regPathList = registry->getDataFilePathList();
-        const FilePathList& optionsPathList = options->getDatabasePathList();
-        for (FilePathList::const_iterator iter = optionsPathList.begin();
-             iter != optionsPathList.end();
-             ++iter) {
-            if (find(regPathList.begin(), regPathList.end(), *iter)
-                == regPathList.end())
-                localPathList.push_back(*iter);
-        }
-        // Save the current Registry path list and install the augmented one.
-        localPathList.swap(registry->getDataFilePathList());
-        validOptions = true;
-    }
-    ~OptionsPusher()
-    {
-        // Restore the old path list
-        if (validOptions)
-            localPathList.swap(Registry::instance()->getDataFilePathList());
-    }
-};
 } // 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)
 {
-    OpenThreads::ScopedLock<OpenThreads::ReentrantMutex> lock(readerMutex);
+    ScopedLock<ReentrantMutex> lock(readerMutex);
     CallbackMap::iterator iter
         = imageCallbackMap.find(getFileExtension(fileName));
     // XXX Workaround for OSG plugin bug
     {
-        OptionsPusher pusher(opt);
         if (iter != imageCallbackMap.end() && iter->second.valid())
             return iter->second->readImage(fileName, opt);
-        string absFileName = findDataFile(fileName);
+        string absFileName = findDataFile(fileName, opt);
         if (!fileExists(absFileName)) {
             SG_LOG(SG_IO, SG_ALERT, "Cannot find image file \""
                    << fileName << "\"");
@@ -284,6 +313,11 @@ ModelRegistry::readImage(const string& fileName,
         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() << "\"");
@@ -348,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;
@@ -394,7 +428,8 @@ string OSGSubstitutePolicy::substitute(const string& name,
 }
 
 ModelRegistry::ModelRegistry() :
-    _defaultCallback(new DefaultCallback(""))
+    _defaultCallback(new DefaultCallback("")),
+    _nestingLevel(0)
 {
 }
 
@@ -412,31 +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)
 {
-    OpenThreads::ScopedLock<OpenThreads::ReentrantMutex> lock(readerMutex);
+    ScopedLock<ReentrantMutex> lock(readerMutex);
+    ++_nestingLevel;
+
     // XXX Workaround for OSG plugin bug.
-    OptionsPusher pusher(opt);
     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 {
@@ -455,7 +493,7 @@ public:
     registry->setOptions(options);
     registry->getOrCreateSharedStateManager()->
       setShareMode(SharedStateManager::SHARE_STATESETS);
-    registry->setReadFileCallback(ModelRegistry::getInstance());
+    registry->setReadFileCallback(ModelRegistry::instance());
   }
 };
 
@@ -473,13 +511,14 @@ struct ACOptimizePolicy : public OptimizeModelPolicy {
     {
         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()
-            && transform->getName().empty()
-            && transform->getNumChildren() == 1) {
-            optimized = static_cast<Node*>(transform->getChild(0));
-            Group* group = dynamic_cast<Group*>(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));
@@ -527,4 +566,4 @@ typedef ModelRegistryCallback<ACProcessPolicy, DefaultCachePolicy,
 namespace
 {
 ModelRegistryCallbackProxy<ACCallback> g_acRegister("ac");
-}   
+}