From 2fbaddbecf27288e1da335dce591791e9a6016af Mon Sep 17 00:00:00 2001 From: timoore Date: Thu, 29 Nov 2007 23:55:01 +0000 Subject: [PATCH] Move SGReadFileCallback from model.cxx to public class ModelRegistry Move SGReadFileCallback and all its help classes into a new ModelRegistry class that also provides an interface to add custom callbacks for specific file extensions. SGReaderWriterBTG uses that to keep any further processing from being done on .btg files. Various namespace-releated cleanup was done on this code too. --- simgear/scene/model/Makefile.am | 2 + simgear/scene/model/ModelRegistry.cxx | 410 +++++++++++++++++++++++ simgear/scene/model/ModelRegistry.hxx | 82 +++++ simgear/scene/model/model.cxx | 340 ------------------- simgear/scene/tgdb/SGReaderWriterBTG.cxx | 10 +- simgear/scene/tgdb/SGReaderWriterBTG.hxx | 1 + 6 files changed, 504 insertions(+), 341 deletions(-) create mode 100644 simgear/scene/model/ModelRegistry.cxx create mode 100644 simgear/scene/model/ModelRegistry.hxx diff --git a/simgear/scene/model/Makefile.am b/simgear/scene/model/Makefile.am index 70b01db6..192af7d3 100644 --- a/simgear/scene/model/Makefile.am +++ b/simgear/scene/model/Makefile.am @@ -9,6 +9,7 @@ include_HEADERS = \ location.hxx \ model.hxx \ modellib.hxx \ + ModelRegistry.hxx \ persparam.hxx \ placement.hxx \ placementtrans.hxx \ @@ -23,6 +24,7 @@ libsgmodel_a_SOURCES = \ location.cxx \ model.cxx \ modellib.cxx \ + ModelRegistry.cxx \ persparam.cxx \ placement.cxx \ placementtrans.cxx \ diff --git a/simgear/scene/model/ModelRegistry.cxx b/simgear/scene/model/ModelRegistry.cxx new file mode 100644 index 00000000..670e03a0 --- /dev/null +++ b/simgear/scene/model/ModelRegistry.cxx @@ -0,0 +1,410 @@ +#include "ModelRegistry.hxx" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +using namespace std; +using namespace osg; +using namespace osgDB; +using namespace simgear; + +// 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 Observer { +public: + SGDatabaseReference(Referenced* referenced) : + mReferenced(referenced) + { } + virtual void objectDeleted(void*) + { + mReferenced = 0; + } +private: + ref_ptr mReferenced; +}; + +// Visitor for +class SGTextureUpdateVisitor : public SGTextureStateAttributeVisitor { +public: + SGTextureUpdateVisitor(const FilePathList& pathList) : + mPathList(pathList) + { } + Texture2D* textureReplace(int unit, + StateSet::RefAttributePair& refAttr) + { + Texture2D* texture; + texture = dynamic_cast(refAttr.first.get()); + if (!texture) + return 0; + + ref_ptr 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; + + CopyOp copyOp(CopyOp::DEEP_COPY_ALL & ~CopyOp::DEEP_COPY_IMAGES); + texture = static_cast(copyOp(texture)); + if (!texture) + return 0; + texture->setImage(image.get()); + return texture; + } + virtual void apply(StateSet* stateSet) + { + if (!stateSet) + return; + + // 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); + } + ++i; + } + } + } + +private: + FilePathList mPathList; +}; + +class SGTexCompressionVisitor : public SGTextureStateAttributeVisitor { +public: + virtual void apply(int, StateSet::RefAttributePair& refAttr) + { + Texture2D* texture; + texture = dynamic_cast(refAttr.first.get()); + if (!texture) + return; + + // Hmm, true?? + texture->setDataVariance(osg::Object::STATIC); + + Image* image = texture->getImage(0); + if (!image) + return; + + int s = image->s(); + int t = image->t(); + + if (s <= t && 32 <= s) { + SGSceneFeatures::instance()->setTextureCompression(texture); + } else if (t < s && 32 <= t) { + SGSceneFeatures::instance()->setTextureCompression(texture); + } + } +}; + +class SGTexDataVarianceVisitor : public SGTextureStateAttributeVisitor { +public: + virtual void apply(int, StateSet::RefAttributePair& refAttr) + { + Texture* texture; + texture = dynamic_cast(refAttr.first.get()); + if (!texture) + return; + + texture->setDataVariance(Object::STATIC); + } +}; + +class SGAcMaterialCrippleVisitor : public SGStateAttributeVisitor { +public: + virtual void apply(StateSet::RefAttributePair& refAttr) + { + Material* material; + material = dynamic_cast(refAttr.first.get()); + if (!material) + return; + material->setColorMode(Material::AMBIENT_AND_DIFFUSE); + } +}; +} // namespace + +ReaderWriter::ReadResult +ModelRegistry::readImage(const string& fileName, + const ReaderWriter::Options* opt) +{ + 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() << "\""); + + return res; +} + +ReaderWriter::ReadResult +ModelRegistry::readNode(const string& fileName, + const ReaderWriter::Options* opt) +{ + Registry* registry = Registry::instance(); + ReaderWriter::ReadResult res; + Node* cached = 0; + CallbackMap::iterator iter + = nodeCallbackMap.find(getFileExtension(fileName)); + if (iter != nodeCallbackMap.end() && iter->second.valid()) + return iter->second->readNode(fileName, opt); + // First, look for a file with the same name, and the extension + // ".osg" and, if it exists, load it instead. This allows for + // substitution of optimized models for ones named in the scenery. + bool optimizeModel = true; + string fileSansExtension = getNameLessExtension(fileName); + string osgFileName = fileSansExtension + ".osg"; + string absFileName = findDataFile(osgFileName); + // The absolute file name is passed to the reader plugin, which + // calls findDataFile again... but that's OK, it should find the + // file by its absolute path straight away. + if (fileExists(absFileName)) { + optimizeModel = false; + } else { + absFileName = findDataFile(fileName); + } + if (!fileExists(absFileName)) { + SG_LOG(SG_IO, SG_ALERT, "Cannot find model file \"" + << fileName << "\""); + return ReaderWriter::ReadResult::FILE_NOT_FOUND; + } + cached + = dynamic_cast(registry->getFromObjectCache(absFileName)); + if (cached) { + SG_LOG(SG_IO, SG_INFO, "Got cached model \"" + << absFileName << "\""); + } else { + SG_LOG(SG_IO, SG_INFO, "Reading model \"" + << absFileName << "\""); + res = registry->readNodeImplementation(absFileName, opt); + if (!res.validNode()) + return res; + + bool needTristrip = true; + if (getLowerCaseFileExtension(fileName) == "ac") { + // we get optimal geometry from the loader. + needTristrip = false; + Matrix m(1, 0, 0, 0, + 0, 0, 1, 0, + 0, -1, 0, 0, + 0, 0, 0, 1); + + ref_ptr root = new Group; + MatrixTransform* transform = new MatrixTransform; + root->addChild(transform); + + transform->setDataVariance(Object::STATIC); + transform->setMatrix(m); + transform->addChild(res.getNode()); + + res = ReaderWriter::ReadResult(0); + + if (optimizeModel) { + osgUtil::Optimizer optimizer; + unsigned opts = osgUtil::Optimizer::FLATTEN_STATIC_TRANSFORMS; + optimizer.optimize(root.get(), opts); + } + + // strip away unneeded groups + if (root->getNumChildren() == 1 && root->getName().empty()) { + res = ReaderWriter::ReadResult(root->getChild(0)); + } else + res = ReaderWriter::ReadResult(root.get()); + + // 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; + res.getNode()->accept(matCriple); + } + + if (optimizeModel) { + osgUtil::Optimizer optimizer; + unsigned opts = 0; + // Don't use this one. It will break animation names ... + // opts |= osgUtil::Optimizer::REMOVE_REDUNDANT_NODES; + + // opts |= osgUtil::Optimizer::REMOVE_LOADED_PROXY_NODES; + // opts |= osgUtil::Optimizer::COMBINE_ADJACENT_LODS; + // opts |= osgUtil::Optimizer::SHARE_DUPLICATE_STATE; + opts |= osgUtil::Optimizer::MERGE_GEOMETRY; + // opts |= osgUtil::Optimizer::CHECK_GEOMETRY; + // opts |= osgUtil::Optimizer::SPATIALIZE_GROUPS; + // opts |= osgUtil::Optimizer::COPY_SHARED_NODES; + opts |= osgUtil::Optimizer::FLATTEN_STATIC_TRANSFORMS; + if (needTristrip) + opts |= osgUtil::Optimizer::TRISTRIP_GEOMETRY; + // opts |= osgUtil::Optimizer::TESSELATE_GEOMETRY; + // opts |= osgUtil::Optimizer::OPTIMIZE_TEXTURE_SETTINGS; + optimizer.optimize(res.getNode(), opts); + } + // Make sure the data variance of sharable objects is set to STATIC ... + SGTexDataVarianceVisitor dataVarianceVisitor; + res.getNode()->accept(dataVarianceVisitor); + // ... so that textures are now globally shared + registry->getSharedStateManager()->share(res.getNode()); + + SGTexCompressionVisitor texComp; + res.getNode()->accept(texComp); + cached = res.getNode(); + registry->addEntryToObjectCache(absFileName, cached); + } + // 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 + // 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(cached); + CopyOp::CopyFlags flags = CopyOp::DEEP_COPY_ALL; + flags &= ~CopyOp::DEEP_COPY_TEXTURES; + flags &= ~CopyOp::DEEP_COPY_IMAGES; + 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; + res = ReaderWriter::ReadResult(CopyOp(flags)(cached)); + res.getNode()->addObserver(databaseReference); + + // Update liveries + SGTextureUpdateVisitor liveryUpdate(getDataFilePathList()); + res.getNode()->accept(liveryUpdate); + + // Make sure the data variance of sharable objects is set to STATIC ... + SGTexDataVarianceVisitor dataVarianceVisitor; + res.getNode()->accept(dataVarianceVisitor); + // ... so that textures are now globally shared + registry->getOrCreateSharedStateManager()->share(res.getNode(), 0); + + return res; +} + +void +ModelRegistry::addImageCallbackForExtension(const string& extension, + Registry::ReadFileCallback* callback) +{ + imageCallbackMap.insert(CallbackMap::value_type(extension, callback)); +} + +void +ModelRegistry::addNodeCallbackForExtension(const string& extension, + Registry::ReadFileCallback* callback) +{ + nodeCallbackMap.insert(CallbackMap::value_type(extension, callback)); +} + +ref_ptr ModelRegistry::instance; + +ModelRegistry* ModelRegistry::getInstance() + +{ + if (!instance.valid()) + instance = new ModelRegistry; + return instance.get(); +} + +class SGReadCallbackInstaller { +public: + SGReadCallbackInstaller() + { + // XXX I understand why we want this, but this seems like a weird + // place to set this option. + Referenced::setThreadSafeReferenceCounting(true); + + Registry* registry = Registry::instance(); + ReaderWriter::Options* options = new ReaderWriter::Options; + // We manage node caching ourselves + int cacheOptions = ReaderWriter::Options::CACHE_ALL + & ~ReaderWriter::Options::CACHE_NODES; + options-> + setObjectCacheHint((ReaderWriter::Options::CacheHintOptions)cacheOptions); + registry->setOptions(options); + registry->getOrCreateSharedStateManager()-> + setShareMode(SharedStateManager::SHARE_TEXTURES); + registry->setReadFileCallback(ModelRegistry::getInstance()); + } +}; + +static SGReadCallbackInstaller readCallbackInstaller; + +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); +} diff --git a/simgear/scene/model/ModelRegistry.hxx b/simgear/scene/model/ModelRegistry.hxx new file mode 100644 index 00000000..770d4ee4 --- /dev/null +++ b/simgear/scene/model/ModelRegistry.hxx @@ -0,0 +1,82 @@ +// ModelRegistry.hxx -- interface to the OSG model registry +// +// Copyright (C) 2007 Tim Moore +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// 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. +#ifndef _SG_MODELREGISTRY_HXX +#define _SG_MODELREGISTRY_HXX 1 + +#include +#include +#include + +#include + +#include STL_STRING +#include + +namespace simgear +{ +class ModelRegistry : public osgDB::Registry::ReadFileCallback { +public: + virtual osgDB::ReaderWriter::ReadResult + readImage(const std::string& fileName, + const osgDB::ReaderWriter::Options* opt); + virtual osgDB::ReaderWriter::ReadResult + readNode(const std::string& fileName, + const osgDB::ReaderWriter::Options* opt); + void addImageCallbackForExtension(const std::string& extension, + osgDB::Registry::ReadFileCallback* + callback); + void addNodeCallbackForExtension(const std::string& extension, + osgDB::Registry::ReadFileCallback* + callback); + static ModelRegistry* getInstance(); +protected: + static osg::ref_ptr instance; + typedef std::map > + CallbackMap; + CallbackMap imageCallbackMap; + CallbackMap nodeCallbackMap; +}; + +// Proxy for registering extension-based callbacks + +template +class ModelRegistryCallbackProxy +{ +public: + ModelRegistryCallbackProxy(std::string extension) + { + ModelRegistry::getInstance()->addNodeCallbackForExtension(extension, + new T); + } +}; + +// Callback for file extensions that load files using the default OSG +// implementation. + +class OSGFileCallback : public osgDB::Registry::ReadFileCallback { +public: + virtual osgDB::ReaderWriter::ReadResult + readImage(const std::string& fileName, + const osgDB::ReaderWriter::Options* opt); + virtual osgDB::ReaderWriter::ReadResult + readNode(const std::string& fileName, + const osgDB::ReaderWriter::Options* opt); +}; + +} +#endif // _SG_MODELREGISTRY_HXX diff --git a/simgear/scene/model/model.cxx b/simgear/scene/model/model.cxx index 749b45c3..4c49fd35 100644 --- a/simgear/scene/model/model.cxx +++ b/simgear/scene/model/model.cxx @@ -35,346 +35,6 @@ #include "model.hxx" SG_USING_STD(vector); -SG_USING_STD(set); - -// 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. -class SGDatabaseReference : public osg::Observer { -public: - SGDatabaseReference(osg::Referenced* referenced) : - mReferenced(referenced) - { } - virtual void objectDeleted(void*) - { - mReferenced = 0; - } -private: - osg::ref_ptr mReferenced; -}; - -// Visitor for -class SGTextureUpdateVisitor : public SGTextureStateAttributeVisitor { -public: - SGTextureUpdateVisitor(const osgDB::FilePathList& pathList) : - mPathList(pathList) - { } - osg::Texture2D* textureReplace(int unit, - osg::StateSet::RefAttributePair& refAttr) - { - osg::Texture2D* texture; - texture = dynamic_cast(refAttr.first.get()); - if (!texture) - return 0; - - osg::ref_ptr image = texture->getImage(0); - if (!image) - return 0; - - // The currently loaded file name - std::string fullFilePath = image->getFileName(); - // The short name - std::string fileName = osgDB::getSimpleFileName(fullFilePath); - // The name that should be found with the current database path - std::string fullLiveryFile = osgDB::findFileInPath(fileName, mPathList); - // If they are identical then there is nothing to do - if (fullLiveryFile == fullFilePath) - return 0; - - image = osgDB::readImageFile(fullLiveryFile); - if (!image) - return 0; - - osg::CopyOp copyOp(osg::CopyOp::DEEP_COPY_ALL & - ~osg::CopyOp::DEEP_COPY_IMAGES); - texture = static_cast(copyOp(texture)); - if (!texture) - return 0; - texture->setImage(image.get()); - return texture; - } - virtual void apply(osg::StateSet* stateSet) - { - if (!stateSet) - return; - - // get a copy that we can safely modify the statesets values. - osg::StateSet::TextureAttributeList attrList; - attrList = stateSet->getTextureAttributeList(); - for (unsigned unit = 0; unit < attrList.size(); ++unit) { - osg::StateSet::AttributeList::iterator i; - i = attrList[unit].begin(); - while (i != attrList[unit].end()) { - osg::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, - osg::StateAttribute::ON); - } - ++i; - } - } - } - -private: - osgDB::FilePathList mPathList; -}; - -class SGTexCompressionVisitor : public SGTextureStateAttributeVisitor { -public: - virtual void apply(int, osg::StateSet::RefAttributePair& refAttr) - { - osg::Texture2D* texture; - texture = dynamic_cast(refAttr.first.get()); - if (!texture) - return; - - // Hmm, true?? - texture->setDataVariance(osg::Object::STATIC); - - osg::Image* image = texture->getImage(0); - if (!image) - return; - - int s = image->s(); - int t = image->t(); - - if (s <= t && 32 <= s) { - SGSceneFeatures::instance()->setTextureCompression(texture); - } else if (t < s && 32 <= t) { - SGSceneFeatures::instance()->setTextureCompression(texture); - } - } -}; - -class SGTexDataVarianceVisitor : public SGTextureStateAttributeVisitor { -public: - virtual void apply(int, osg::StateSet::RefAttributePair& refAttr) - { - osg::Texture* texture; - texture = dynamic_cast(refAttr.first.get()); - if (!texture) - return; - - texture->setDataVariance(osg::Object::STATIC); - } -}; - -class SGAcMaterialCrippleVisitor : public SGStateAttributeVisitor { -public: - virtual void apply(osg::StateSet::RefAttributePair& refAttr) - { - osg::Material* material; - material = dynamic_cast(refAttr.first.get()); - if (!material) - return; - material->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE); - } -}; - -class SGReadFileCallback : - public osgDB::Registry::ReadFileCallback { -public: - virtual osgDB::ReaderWriter::ReadResult - readImage(const std::string& fileName, - const osgDB::ReaderWriter::Options* opt) - { - std::string absFileName = osgDB::findDataFile(fileName); - if (!osgDB::fileExists(absFileName)) { - SG_LOG(SG_IO, SG_ALERT, "Cannot find image file \"" - << fileName << "\""); - return osgDB::ReaderWriter::ReadResult::FILE_NOT_FOUND; - } - - osgDB::Registry* registry = osgDB::Registry::instance(); - osgDB::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() << "\""); - - return res; - } - - virtual osgDB::ReaderWriter::ReadResult - readNode(const std::string& fileName, - const osgDB::ReaderWriter::Options* opt) - { - osgDB::Registry* registry = osgDB::Registry::instance(); - osgDB::ReaderWriter::ReadResult res; - osg::Node* cached = 0; - // The BTG loader automatically looks for ".btg.gz" if a file with - // the .btg extension doesn't exist. Also, we don't want to add - // nodes, run the optimizer, etc. on the btg model.So, let it do - // its thing. - if (osgDB::equalCaseInsensitive(osgDB::getFileExtension(fileName), "btg")) { - return registry->readNodeImplementation(fileName, opt); - } - // First, look for a file with the same name, and the extension - // ".osg" and, if it exists, load it instead. This allows for - // substitution of optimized models for ones named in the scenery. - bool optimizeModel = true; - std::string fileSansExtension = osgDB::getNameLessExtension(fileName); - std::string osgFileName = fileSansExtension + ".osg"; - std::string absFileName = osgDB::findDataFile(osgFileName); - if (osgDB::fileExists(absFileName)) { - optimizeModel = false; - } else { - absFileName = osgDB::findDataFile(fileName); - } - if (!osgDB::fileExists(absFileName)) { - SG_LOG(SG_IO, SG_ALERT, "Cannot find model file \"" - << fileName << "\""); - return osgDB::ReaderWriter::ReadResult::FILE_NOT_FOUND; - } - cached - = dynamic_cast(registry->getFromObjectCache(absFileName)); - if (cached) { - SG_LOG(SG_IO, SG_INFO, "Got cached model \"" - << absFileName << "\""); - } else { - SG_LOG(SG_IO, SG_INFO, "Reading model \"" - << absFileName << "\""); - res = registry->readNodeImplementation(absFileName, opt); - if (!res.validNode()) - return res; - - bool needTristrip = true; - if (osgDB::getLowerCaseFileExtension(fileName) == "ac") { - // we get optimal geometry from the loader. - needTristrip = false; - osg::Matrix m(1, 0, 0, 0, - 0, 0, 1, 0, - 0, -1, 0, 0, - 0, 0, 0, 1); - - osg::ref_ptr root = new osg::Group; - osg::MatrixTransform* transform = new osg::MatrixTransform; - root->addChild(transform); - - transform->setDataVariance(osg::Object::STATIC); - transform->setMatrix(m); - transform->addChild(res.getNode()); - - res = osgDB::ReaderWriter::ReadResult(0); - - if (optimizeModel) { - osgUtil::Optimizer optimizer; - unsigned opts = osgUtil::Optimizer::FLATTEN_STATIC_TRANSFORMS; - optimizer.optimize(root.get(), opts); - } - - // strip away unneeded groups - if (root->getNumChildren() == 1 && root->getName().empty()) { - res = osgDB::ReaderWriter::ReadResult(root->getChild(0)); - } else - res = osgDB::ReaderWriter::ReadResult(root.get()); - - // 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; - res.getNode()->accept(matCriple); - } - - if (optimizeModel) { - osgUtil::Optimizer optimizer; - unsigned opts = 0; - // Don't use this one. It will break animation names ... - // opts |= osgUtil::Optimizer::REMOVE_REDUNDANT_NODES; - - // opts |= osgUtil::Optimizer::REMOVE_LOADED_PROXY_NODES; - // opts |= osgUtil::Optimizer::COMBINE_ADJACENT_LODS; - // opts |= osgUtil::Optimizer::SHARE_DUPLICATE_STATE; - opts |= osgUtil::Optimizer::MERGE_GEOMETRY; - // opts |= osgUtil::Optimizer::CHECK_GEOMETRY; - // opts |= osgUtil::Optimizer::SPATIALIZE_GROUPS; - // opts |= osgUtil::Optimizer::COPY_SHARED_NODES; - opts |= osgUtil::Optimizer::FLATTEN_STATIC_TRANSFORMS; - if (needTristrip) - opts |= osgUtil::Optimizer::TRISTRIP_GEOMETRY; - // opts |= osgUtil::Optimizer::TESSELATE_GEOMETRY; - // opts |= osgUtil::Optimizer::OPTIMIZE_TEXTURE_SETTINGS; - optimizer.optimize(res.getNode(), opts); - } - // Make sure the data variance of sharable objects is set to STATIC ... - SGTexDataVarianceVisitor dataVarianceVisitor; - res.getNode()->accept(dataVarianceVisitor); - // ... so that textures are now globally shared - registry->getSharedStateManager()->share(res.getNode()); - - SGTexCompressionVisitor texComp; - res.getNode()->accept(texComp); - cached = res.getNode(); - registry->addEntryToObjectCache(absFileName, cached); - } - // 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 - // 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(cached); - osg::CopyOp::CopyFlags flags = osg::CopyOp::DEEP_COPY_ALL; - flags &= ~osg::CopyOp::DEEP_COPY_TEXTURES; - flags &= ~osg::CopyOp::DEEP_COPY_IMAGES; - flags &= ~osg::CopyOp::DEEP_COPY_ARRAYS; - flags &= ~osg::CopyOp::DEEP_COPY_PRIMITIVES; - // This will safe display lists ... - flags &= ~osg::CopyOp::DEEP_COPY_DRAWABLES; - flags &= ~osg::CopyOp::DEEP_COPY_SHAPES; - res = osgDB::ReaderWriter::ReadResult(osg::CopyOp(flags)(cached)); - res.getNode()->addObserver(databaseReference); - - // Update liveries - SGTextureUpdateVisitor liveryUpdate(osgDB::getDataFilePathList()); - res.getNode()->accept(liveryUpdate); - - // Make sure the data variance of sharable objects is set to STATIC ... - SGTexDataVarianceVisitor dataVarianceVisitor; - res.getNode()->accept(dataVarianceVisitor); - // ... so that textures are now globally shared - registry->getOrCreateSharedStateManager()->share(res.getNode(), 0); - - return res; - } -}; - -class SGReadCallbackInstaller { -public: - SGReadCallbackInstaller() - { - osg::Referenced::setThreadSafeReferenceCounting(true); - - osgDB::Registry* registry = osgDB::Registry::instance(); - osgDB::ReaderWriter::Options* options = new osgDB::ReaderWriter::Options; - // We manage node caching ourselves - int cacheOptions = osgDB::ReaderWriter::Options::CACHE_ALL - & ~osgDB::ReaderWriter::Options::CACHE_NODES; - options-> - setObjectCacheHint((osgDB::ReaderWriter::Options::CacheHintOptions)cacheOptions); - registry->setOptions(options); - registry->getOrCreateSharedStateManager()->setShareMode(osgDB::SharedStateManager::SHARE_TEXTURES); - registry->setReadFileCallback(new SGReadFileCallback); - } -}; - -static SGReadCallbackInstaller readCallbackInstaller; osg::Texture2D* SGLoadTexture2D(const std::string& path, bool wrapu, bool wrapv, int) diff --git a/simgear/scene/tgdb/SGReaderWriterBTG.cxx b/simgear/scene/tgdb/SGReaderWriterBTG.cxx index daa7f70e..4735f0e3 100644 --- a/simgear/scene/tgdb/SGReaderWriterBTG.cxx +++ b/simgear/scene/tgdb/SGReaderWriterBTG.cxx @@ -17,10 +17,15 @@ #include #include + +#include + #include "SGReaderWriterBTGOptions.hxx" #include "SGReaderWriterBTG.hxx" #include "obj.hxx" +using namespace simgear; + const char* SGReaderWriterBTG::className() const { return "BTG Database reader"; @@ -64,4 +69,7 @@ SGReaderWriterBTG::readNode(const std::string& fileName, return ReadResult::FILE_NOT_HANDLED; } - +namespace +{ +ModelRegistryCallbackProxy g_btgCallbackProxy("btg"); +} diff --git a/simgear/scene/tgdb/SGReaderWriterBTG.hxx b/simgear/scene/tgdb/SGReaderWriterBTG.hxx index 20070b24..3c0ccc80 100644 --- a/simgear/scene/tgdb/SGReaderWriterBTG.hxx +++ b/simgear/scene/tgdb/SGReaderWriterBTG.hxx @@ -31,5 +31,6 @@ public: const osgDB::ReaderWriter::Options* options) const; }; + #endif -- 2.39.5