]> git.mxchange.org Git - simgear.git/commitdiff
remove CopyPolicy from ModelRegistry
authorTim Moore <timoore@redhat.com>
Sun, 20 Sep 2009 07:29:05 +0000 (09:29 +0200)
committerTim Moore <timoore@redhat.com>
Fri, 13 Nov 2009 21:41:10 +0000 (22:41 +0100)
Put the responsibility for copying a loaded model directly in
SGReaderWriterXML.

simgear/scene/model/ModelRegistry.cxx
simgear/scene/model/ModelRegistry.hxx
simgear/scene/model/SGReaderWriterXML.cxx
simgear/scene/model/model.cxx
simgear/scene/model/model.hxx
simgear/scene/tgdb/SGReaderWriterBTG.cxx

index 3b5b545c4101b48cd93129b34a716c9bef01cf0d..1728020d64df06be17b7337d1ecb036b0dc90726 100644 (file)
@@ -128,125 +128,6 @@ protected:
     }
 };
 
-// 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 _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:
@@ -416,41 +297,6 @@ osg::Node* OptimizeModelPolicy::optimize(osg::Node* node,
     return node;
 }
 
-osg::Node* DefaultCopyPolicy::copy(osg::Node* model, const string& fileName,
-                                   const osgDB::ReaderWriter::Options* opt)
-{
-    // Add an extra reference to the model stored in the database.
-    // 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
-    SGDatabaseReference* databaseReference;
-    databaseReference = new SGDatabaseReference(model);
-    CopyOp::CopyFlags flags = CopyOp::DEEP_COPY_ALL;
-    flags &= ~CopyOp::DEEP_COPY_TEXTURES;
-    flags &= ~CopyOp::DEEP_COPY_IMAGES;
-    flags &= ~CopyOp::DEEP_COPY_STATESETS;
-    flags &= ~CopyOp::DEEP_COPY_STATEATTRIBUTES;
-    flags &= ~CopyOp::DEEP_COPY_ARRAYS;
-    flags &= ~CopyOp::DEEP_COPY_PRIMITIVES;
-    // This will safe display lists ...
-    flags &= ~CopyOp::DEEP_COPY_DRAWABLES;
-    flags &= ~CopyOp::DEEP_COPY_SHAPES;
-    osg::Node* res = CopyOp(flags)(model);
-    res->addObserver(databaseReference);
-
-    // Update liveries
-    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;
-}
-
 string OSGSubstitutePolicy::substitute(const string& name,
                                        const ReaderWriter::Options* opt)
 {
@@ -599,7 +445,7 @@ struct ACProcessPolicy {
 };
 
 typedef ModelRegistryCallback<ACProcessPolicy, DefaultCachePolicy,
-                              ACOptimizePolicy, DefaultCopyPolicy,
+                              ACOptimizePolicy,
                               OSGSubstitutePolicy, BuildLeafBVHPolicy>
 ACCallback;
 
index 909c9460247184d6b481d2ab86dbd06b25ee7cfe..0096315156c6cbdbd3e9c21033539aa82f874a03 100644 (file)
@@ -61,12 +61,12 @@ namespace simgear
 // readNode function is specified as a template with a bunch of
 // pluggable (and predefined) policies.
 template <typename ProcessPolicy, typename CachePolicy, typename OptimizePolicy,
-          typename CopyPolicy, typename SubstitutePolicy, typename BVHPolicy>
+          typename SubstitutePolicy, typename BVHPolicy>
 class ModelRegistryCallback : public osgDB::Registry::ReadFileCallback {
 public:
     ModelRegistryCallback(const std::string& extension) :
         _processPolicy(extension), _cachePolicy(extension),
-        _optimizePolicy(extension), _copyPolicy(extension),
+        _optimizePolicy(extension),
         _substitutePolicy(extension), _bvhPolicy(extension)
     {
     }
@@ -100,9 +100,7 @@ public:
             _bvhPolicy.buildBVH(fileName, optimizedNode.get());
             _cachePolicy.addToCache(fileName, optimizedNode.get());
         }
-        osg::ref_ptr<osg::Node> copyNode;
-        copyNode = _copyPolicy.copy(optimizedNode.get(), fileName, opt);
-        return ReaderWriter::ReadResult(copyNode);
+        return ReaderWriter::ReadResult(optimizedNode);
     }
 protected:
     static osgDB::ReaderWriter::ReadResult
@@ -120,7 +118,6 @@ protected:
     ProcessPolicy _processPolicy;
     CachePolicy _cachePolicy;
     OptimizePolicy _optimizePolicy;
-    CopyPolicy _copyPolicy;
     SubstitutePolicy _substitutePolicy;
     BVHPolicy _bvhPolicy;
     virtual ~ModelRegistryCallback() {}
@@ -169,21 +166,6 @@ struct NoOptimizePolicy {
     }
 };
 
-struct DefaultCopyPolicy {
-    DefaultCopyPolicy(const std::string& extension) {}
-    osg::Node* copy(osg::Node* node, const std::string& fileName,
-                    const osgDB::ReaderWriter::Options* opt);
-};
-
-struct NoCopyPolicy {
-    NoCopyPolicy(const std::string& extension) {}
-    osg::Node* copy(osg::Node* node, const std::string& fileName,
-                    const osgDB::ReaderWriter::Options* opt)
-    {
-        return node;
-    }
-};
-
 struct OSGSubstitutePolicy {
     OSGSubstitutePolicy(const std::string& extension) {}
     std::string substitute(const std::string& name,
@@ -215,7 +197,7 @@ struct NoBuildBVHPolicy {
 };
 
 typedef ModelRegistryCallback<DefaultProcessPolicy, DefaultCachePolicy,
-                              OptimizeModelPolicy, DefaultCopyPolicy,
+                              OptimizeModelPolicy,
                               OSGSubstitutePolicy, BuildLeafBVHPolicy>
 DefaultCallback;
 
@@ -251,7 +233,7 @@ protected:
 // Callback that only loads the file without any caching or
 // postprocessing.
 typedef ModelRegistryCallback<DefaultProcessPolicy, NoCachePolicy,
-                              NoOptimizePolicy, NoCopyPolicy,
+                              NoOptimizePolicy,
                               NoSubstitutePolicy, BuildLeafBVHPolicy>
 LoadOnlyCallback;
 
index 31c0c56dddaf42ad27e0589a78851e9a30cc2539..75026bcfebad5a807b3720bff60b783e39bc88cf 100644 (file)
@@ -105,6 +105,32 @@ private:
     SGSharedPtr<SGCondition> mCondition;
 };
 
+
+// Little helper class that holds an extra reference to a
+// loaded 3d model.
+// Since we clone all structural nodes from our 3d models,
+// the database pager will only see one single reference to
+// top node of the model and expire it relatively fast.
+// We attach that extra reference to every model cloned from
+// a base model in the pager. When that cloned model is deleted
+// this extra reference is deleted too. So if there are no
+// cloned models left the model will expire.
+namespace {
+class SGDatabaseReference : public osg::Observer
+{
+public:
+    SGDatabaseReference(osg::Referenced* referenced) :
+        mReferenced(referenced)
+    { }
+    virtual void objectDeleted(void*)
+    {
+        mReferenced = 0;
+    }
+private:
+    osg::ref_ptr<osg::Referenced> mReferenced;
+};
+}
+
 static osg::Node *
 sgLoad3DModel_internal(const string &path,
                        const osgDB::ReaderWriter::Options* options_,
@@ -181,10 +207,31 @@ sgLoad3DModel_internal(const string &path,
             texturepath = texturepath.dir();
 
         options->setDatabasePath(texturepath.str());
-        model = osgDB::readNodeFile(modelpath.str(), options.get());
-        if (model == 0)
+        osg::Node* origModel
+            = osgDB::readNodeFile(modelpath.str(), options.get());
+
+        if (!origModel)
             throw sg_io_exception("Failed to load 3D model",
                                   sg_location(modelpath.str()));
+        model = copyModel(origModel);
+        // Add an extra reference to the model stored in the database.
+        // 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
+        SGDatabaseReference* databaseReference;
+        databaseReference = new SGDatabaseReference(origModel);
+        model->addObserver(databaseReference);
+
+        // Update liveries
+        TextureUpdateVisitor liveryUpdate(options->getDatabasePathList());
+        model->accept(liveryUpdate);
+
+        // Copy the userdata fields, still sharing the boundingvolumes,
+        // but introducing new data for velocities.
+        UserDataCopyVisitor userDataCopyVisitor;
+        model->accept(userDataCopyVisitor);
     }
     model->setName(modelpath.str());
 
index 54e749245e5082d575e56467944d5046f8a415db..5672ece6b810bf1940d035ae0b9b7442c832a342 100644 (file)
@@ -7,11 +7,17 @@
 #include <simgear_config.h>
 #endif
 
+#include <utility>
+
 #include <osg/ref_ptr>
+#include <osgDB/FileNameUtils>
+#include <osgDB/FileUtils>
+#include <osgDB/ReaderWriter>
 #include <osgDB/ReadFile>
 #include <osgDB/SharedStateManager>
 
 #include <simgear/scene/util/SGSceneFeatures.hxx>
+#include <simgear/scene/util/SGSceneUserData.hxx>
 
 #include <simgear/structure/exception.hxx>
 #include <simgear/props/props.hxx>
@@ -59,4 +65,131 @@ SGLoadTexture2D(bool staticTexture, const std::string& path,
   return texture.release();
 }
 
+namespace simgear
+{
+using namespace osg;
+
+Node* copyModel(Node* model)
+{
+    CopyOp::CopyFlags flags = CopyOp::DEEP_COPY_ALL;
+    flags &= ~CopyOp::DEEP_COPY_TEXTURES;
+    flags &= ~CopyOp::DEEP_COPY_IMAGES;
+    flags &= ~CopyOp::DEEP_COPY_STATESETS;
+    flags &= ~CopyOp::DEEP_COPY_STATEATTRIBUTES;
+    flags &= ~CopyOp::DEEP_COPY_ARRAYS;
+    flags &= ~CopyOp::DEEP_COPY_PRIMITIVES;
+    // This will preserve display lists ...
+    flags &= ~CopyOp::DEEP_COPY_DRAWABLES;
+    flags &= ~CopyOp::DEEP_COPY_SHAPES;
+    return static_cast<Node*>(model->clone(CopyOp(flags)));
+}
+
+TextureUpdateVisitor::TextureUpdateVisitor(const osgDB::FilePathList& pathList) :
+    NodeAndDrawableVisitor(NodeVisitor::TRAVERSE_ALL_CHILDREN),
+    _pathList(pathList)
+{
+}
+
+void TextureUpdateVisitor::apply(Node& node)
+{
+    StateSet* stateSet = cloneStateSet(node.getStateSet());
+    if (stateSet)
+        node.setStateSet(stateSet);
+    traverse(node);
+}
+
+void TextureUpdateVisitor::apply(Drawable& drawable)
+{
+    StateSet* stateSet = cloneStateSet(drawable.getStateSet());
+    if (stateSet)
+        drawable.setStateSet(stateSet);
+}
+
+Texture2D* TextureUpdateVisitor::textureReplace(int unit, const StateAttribute* attr)
+{
+    using namespace osgDB;
+    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* TextureUpdateVisitor::cloneStateSet(const StateSet* stateSet)
+{
+    typedef std::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;
+}
+
+UserDataCopyVisitor::UserDataCopyVisitor() :
+    NodeVisitor(NodeVisitor::NODE_VISITOR,
+                NodeVisitor::TRAVERSE_ALL_CHILDREN)
+{
+}
+
+void UserDataCopyVisitor::apply(Node& node)
+{
+    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);
+}
+
+}
 // end of model.cxx
index bef5a32246d65f9d8691f7eef05da70f91a89d0a..238b385c7fff73f6dbf8edce4e31009d171b558b 100644 (file)
@@ -20,6 +20,7 @@
 #include <osgDB/ReaderWriter>
 
 #include <simgear/misc/sg_path.hxx>
+#include <simgear/scene/util/NodeAndDrawableVisitor.hxx>
 
 osg::Texture2D*
 SGLoadTexture2D(bool staticTexture, const std::string& path,
@@ -54,4 +55,36 @@ SGLoadTexture2D(bool staticTexture, const SGPath& path,
                            mipmaplevels);
 }
 
+namespace simgear
+{
+osg::Node* copyModel(osg::Node* model);
+
+// Change the StateSets of a model to hold different textures based on
+// a livery path.
+
+class TextureUpdateVisitor : public NodeAndDrawableVisitor
+{
+public:
+    TextureUpdateVisitor(const osgDB::FilePathList& pathList);
+    virtual void apply(osg::Node& node);
+    virtual void apply(osg::Drawable& drawable);
+    // Copied from Mathias' earlier SGTextureUpdateVisitor
+protected:
+    osg::Texture2D* textureReplace(int unit, const osg::StateAttribute* attr);
+    osg::StateSet* cloneStateSet(const osg::StateSet* stateSet);
+private:
+    osgDB::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();
+    virtual void apply(osg::Node& node);
+};
+
+}
 #endif // __MODEL_HXX
index 16602a5a0e4ca3929e75d10bab91f1494a2a745e..ad3a6774769c0cce68afd072016bb434e914d21f 100644 (file)
@@ -76,7 +76,7 @@ SGReaderWriterBTG::readNode(const std::string& fileName,
 
 
 typedef ModelRegistryCallback<DefaultProcessPolicy, NoCachePolicy,
-                              NoOptimizePolicy, NoCopyPolicy,
+                              NoOptimizePolicy,
                               NoSubstitutePolicy, BuildGroupBVHPolicy>
 BTGCallback;