From 6a7c2000027cd22eea603e936ddbad1a5bfc8b04 Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Sun, 20 Sep 2009 09:29:05 +0200 Subject: [PATCH] remove CopyPolicy from ModelRegistry Put the responsibility for copying a loaded model directly in SGReaderWriterXML. --- simgear/scene/model/ModelRegistry.cxx | 156 +--------------------- simgear/scene/model/ModelRegistry.hxx | 28 +--- simgear/scene/model/SGReaderWriterXML.cxx | 51 ++++++- simgear/scene/model/model.cxx | 133 ++++++++++++++++++ simgear/scene/model/model.hxx | 33 +++++ simgear/scene/tgdb/SGReaderWriterBTG.cxx | 2 +- 6 files changed, 222 insertions(+), 181 deletions(-) diff --git a/simgear/scene/model/ModelRegistry.cxx b/simgear/scene/model/ModelRegistry.cxx index 3b5b545c..1728020d 100644 --- a/simgear/scene/model/ModelRegistry.cxx +++ b/simgear/scene/model/ModelRegistry.cxx @@ -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(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(copyOp(texture)); - if (!newTexture) { - return 0; - } else { - newTexture->setImage(newImage); - return newTexture; - } - } - - StateSet* cloneStateSet(const StateSet* stateSet) - { - typedef pair Tex2D; - vector 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->clone(CopyOp())); - for (vector::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 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 ACCallback; diff --git a/simgear/scene/model/ModelRegistry.hxx b/simgear/scene/model/ModelRegistry.hxx index 909c9460..00963151 100644 --- a/simgear/scene/model/ModelRegistry.hxx +++ b/simgear/scene/model/ModelRegistry.hxx @@ -61,12 +61,12 @@ namespace simgear // readNode function is specified as a template with a bunch of // pluggable (and predefined) policies. template + 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 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 DefaultCallback; @@ -251,7 +233,7 @@ protected: // Callback that only loads the file without any caching or // postprocessing. typedef ModelRegistryCallback LoadOnlyCallback; diff --git a/simgear/scene/model/SGReaderWriterXML.cxx b/simgear/scene/model/SGReaderWriterXML.cxx index 31c0c56d..75026bcf 100644 --- a/simgear/scene/model/SGReaderWriterXML.cxx +++ b/simgear/scene/model/SGReaderWriterXML.cxx @@ -105,6 +105,32 @@ private: SGSharedPtr 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 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()); diff --git a/simgear/scene/model/model.cxx b/simgear/scene/model/model.cxx index 54e74924..5672ece6 100644 --- a/simgear/scene/model/model.cxx +++ b/simgear/scene/model/model.cxx @@ -7,11 +7,17 @@ #include #endif +#include + #include +#include +#include +#include #include #include #include +#include #include #include @@ -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(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(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(copyOp(texture)); + if (!newTexture) { + return 0; + } else { + newTexture->setImage(newImage); + return newTexture; + } +} + +StateSet* TextureUpdateVisitor::cloneStateSet(const StateSet* stateSet) +{ + typedef std::pair Tex2D; + vector 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->clone(CopyOp())); + for (vector::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 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 diff --git a/simgear/scene/model/model.hxx b/simgear/scene/model/model.hxx index bef5a322..238b385c 100644 --- a/simgear/scene/model/model.hxx +++ b/simgear/scene/model/model.hxx @@ -20,6 +20,7 @@ #include #include +#include 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 diff --git a/simgear/scene/tgdb/SGReaderWriterBTG.cxx b/simgear/scene/tgdb/SGReaderWriterBTG.cxx index 16602a5a..ad3a6774 100644 --- a/simgear/scene/tgdb/SGReaderWriterBTG.cxx +++ b/simgear/scene/tgdb/SGReaderWriterBTG.cxx @@ -76,7 +76,7 @@ SGReaderWriterBTG::readNode(const std::string& fileName, typedef ModelRegistryCallback BTGCallback; -- 2.39.5