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