]> git.mxchange.org Git - simgear.git/blobdiff - simgear/scene/model/ModelRegistry.cxx
Add writeLocalData functions for internal scenegraph classes
[simgear.git] / simgear / scene / model / ModelRegistry.cxx
index a1f49f2a911d9e51013c7935d75ec59bc9f0c057..3b5b545c4101b48cd93129b34a716c9bef01cf0d 100644 (file)
 // 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.
+
+#ifdef HAVE_CONFIG_H
+#  include <simgear_config.h>
+#endif
+
 #include "ModelRegistry.hxx"
 
 #include <algorithm>
@@ -24,7 +29,6 @@
 
 #include <OpenThreads/ScopedLock>
 
-#include <osg/observer_ptr>
 #include <osg/ref_ptr>
 #include <osg/Group>
 #include <osg/NodeCallback>
 #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 +88,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 +153,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 +162,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 +192,7 @@ protected:
             return newTexture;
         }
     }
+    
     StateSet* cloneStateSet(const StateSet* stateSet)
     {
         typedef pair<int, Texture2D*> Tex2D;
@@ -166,6 +225,28 @@ private:
     FilePathList _pathList;
 };
 
+// Create new userdata structs in a copied model.
+// The BVH trees are shared with the original model, but the velocity fields
+// should usually be distinct fields for distinct models.
+class UserDataCopyVisitor : public osg::NodeVisitor {
+public:
+    UserDataCopyVisitor() :
+        osg::NodeVisitor(osg::NodeVisitor::NODE_VISITOR,
+                         osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
+    {
+    }
+    virtual void apply(osg::Node& node)
+    {
+        osg::ref_ptr<SGSceneUserData> userData;
+        userData = SGSceneUserData::getSceneUserData(&node);
+        if (userData.valid()) {
+            SGSceneUserData* newUserData  = new SGSceneUserData(*userData);
+            newUserData->setVelocity(0);
+            node.setUserData(newUserData);
+        }
+        node.traverse(*this);
+    }
+};
 
 class SGTexCompressionVisitor : public SGTextureStateAttributeVisitor {
 public:
@@ -176,9 +257,13 @@ public:
     if (!texture)
       return;
 
-    // Hmm, true??
-    texture->setDataVariance(osg::Object::STATIC);
+    // Do not touch dynamically generated textures.
+    if (texture->getReadPBuffer())
+      return;
+    if (texture->getDataVariance() == osg::Object::DYNAMIC)
+      return;
 
+    // If no image attached, we assume this one is dynamically generated
     Image* image = texture->getImage(0);
     if (!image)
       return;
@@ -202,6 +287,16 @@ public:
     texture = dynamic_cast<Texture*>(refAttr.first.get());
     if (!texture)
       return;
+
+    // Cannot be static if this is a render to texture thing
+    if (texture->getReadPBuffer())
+      return;
+    if (texture->getDataVariance() == osg::Object::DYNAMIC)
+      return;
+    // If no image attached, we assume this one is dynamically generated
+    Image* image = texture->getImage(0);
+    if (!image)
+      return;
     
     texture->setDataVariance(Object::STATIC);
   }
@@ -210,71 +305,33 @@ public:
   {
     if (!stateSet)
       return;
-    SGTextureStateAttributeVisitor::apply(stateSet);
     stateSet->setDataVariance(Object::STATIC);
+    SGTextureStateAttributeVisitor::apply(stateSet);
   }
 };
 
-class SGAcMaterialCrippleVisitor : public SGStateAttributeVisitor {
-public:
-  virtual void apply(StateSet::RefAttributePair& refAttr)
-  {
-    Material* material;
-    material = dynamic_cast<Material*>(refAttr.first.get());
-    if (!material)
-      return;
-    material->setColorMode(Material::AMBIENT_AND_DIFFUSE);
-  }
-};
-
-// 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,11 +341,16 @@ 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 \""
+            SG_LOG(SG_IO, SG_BULK, "Returning cached image \""
                    << res.getImage()->getFileName() << "\"");
         else
-            SG_LOG(SG_IO, SG_INFO, "Reading image \""
+            SG_LOG(SG_IO, SG_BULK, "Reading image \""
                    << res.getImage()->getFileName() << "\"");
 
         return res;
@@ -303,10 +365,10 @@ osg::Node* DefaultCachePolicy::find(const string& fileName,
     osg::Node* cached
         = dynamic_cast<Node*>(registry->getFromObjectCache(fileName));
     if (cached)
-        SG_LOG(SG_IO, SG_INFO, "Got cached model \""
+        SG_LOG(SG_IO, SG_BULK, "Got cached model \""
                << fileName << "\"");
     else
-        SG_LOG(SG_IO, SG_INFO, "Reading model \""
+        SG_LOG(SG_IO, SG_BULK, "Reading model \""
                << fileName << "\"");
     return cached;
 }
@@ -348,17 +410,17 @@ 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;
 }
 
 osg::Node* DefaultCopyPolicy::copy(osg::Node* model, const string& fileName,
-                    const osgDB::ReaderWriter::Options* opt)
+                                   const osgDB::ReaderWriter::Options* opt)
 {
     // Add an extra reference to the model stored in the database.
-    // That it to avoid expiring the object from the cache even if it is still
+    // That is to avoid expiring the object from the cache even if it is still
     // in use. Note that the object cache will think that a model is unused
     // if the reference count is 1. If we clone all structural nodes here
     // we need that extra reference to the original object
@@ -381,6 +443,11 @@ osg::Node* DefaultCopyPolicy::copy(osg::Node* model, const string& fileName,
     TextureUpdateVisitor liveryUpdate(opt->getDatabasePathList());
     res->accept(liveryUpdate);
 
+    // Copy the userdata fields, still sharing the boundingvolumes,
+    // but introducing new data for velocities.
+    UserDataCopyVisitor userDataCopyVisitor;
+    res->accept(userDataCopyVisitor);
+
     return res;
 }
 
@@ -393,6 +460,32 @@ string OSGSubstitutePolicy::substitute(const string& name,
     return absFileName;
 }
 
+
+void
+BuildLeafBVHPolicy::buildBVH(const std::string& fileName, osg::Node* node)
+{
+    SG_LOG(SG_IO, SG_BULK, "Building leaf attached boundingvolume tree for \""
+           << fileName << "\".");
+    BoundingVolumeBuildVisitor bvBuilder(true);
+    node->accept(bvBuilder);
+}
+
+void
+BuildGroupBVHPolicy::buildBVH(const std::string& fileName, osg::Node* node)
+{
+    SG_LOG(SG_IO, SG_BULK, "Building group attached boundingvolume tree for \""
+           << fileName << "\".");
+    BoundingVolumeBuildVisitor bvBuilder(false);
+    node->accept(bvBuilder);
+}
+
+void
+NoBuildBVHPolicy::buildBVH(const std::string& fileName, osg::Node*)
+{
+    SG_LOG(SG_IO, SG_BULK, "Omitting boundingvolume tree for \""
+           << fileName << "\".");
+}
+
 ModelRegistry::ModelRegistry() :
     _defaultCallback(new DefaultCallback(""))
 {
@@ -412,31 +505,24 @@ 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);
+
     // XXX Workaround for OSG plugin bug.
-    OptionsPusher pusher(opt);
-    Registry* registry = Registry::instance();
+//    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);
+
+    return result;
 }
 
 class SGReadCallbackInstaller {
@@ -455,7 +541,7 @@ public:
     registry->setOptions(options);
     registry->getOrCreateSharedStateManager()->
       setShareMode(SharedStateManager::SHARE_STATESETS);
-    registry->setReadFileCallback(ModelRegistry::getInstance());
+    registry->setReadFileCallback(ModelRegistry::instance());
   }
 };
 
@@ -473,13 +559,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));
@@ -506,25 +593,17 @@ struct ACProcessPolicy {
         transform->setDataVariance(Object::STATIC);
         transform->setMatrix(m);
         transform->addChild(node);
-        // Ok, this step is questionable.
-        // It is there to have the same visual appearance of ac objects for the
-        // first cut. Osg's ac3d loader will correctly set materials from the
-        // ac file. But the old plib loader used GL_AMBIENT_AND_DIFFUSE for the
-        // materials that in effect igored the ambient part specified in the
-        // file. We emulate that for the first cut here by changing all
-        // ac models here. But in the long term we should use the
-        // unchanged model and fix the input files instead ...
-        SGAcMaterialCrippleVisitor matCriple;
-        root->accept(matCriple);
+
         return root;
     }
 };
 
 typedef ModelRegistryCallback<ACProcessPolicy, DefaultCachePolicy,
                               ACOptimizePolicy, DefaultCopyPolicy,
-                              OSGSubstitutePolicy> ACCallback;
+                              OSGSubstitutePolicy, BuildLeafBVHPolicy>
+ACCallback;
 
 namespace
 {
 ModelRegistryCallbackProxy<ACCallback> g_acRegister("ac");
-}   
+}