]> git.mxchange.org Git - simgear.git/commitdiff
Move SGReadFileCallback from model.cxx to public class ModelRegistry
authortimoore <timoore>
Thu, 29 Nov 2007 23:55:01 +0000 (23:55 +0000)
committertimoore <timoore>
Thu, 29 Nov 2007 23:55:01 +0000 (23:55 +0000)
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
simgear/scene/model/ModelRegistry.cxx [new file with mode: 0644]
simgear/scene/model/ModelRegistry.hxx [new file with mode: 0644]
simgear/scene/model/model.cxx
simgear/scene/tgdb/SGReaderWriterBTG.cxx
simgear/scene/tgdb/SGReaderWriterBTG.hxx

index 70b01db64af5371c63d18eefdf315ed9cbdb9cba..192af7d30836753e150a6c10a7533ee82c04d582 100644 (file)
@@ -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 (file)
index 0000000..670e03a
--- /dev/null
@@ -0,0 +1,410 @@
+#include "ModelRegistry.hxx"
+
+#include <osg/observer_ptr>
+#include <osg/ref_ptr>
+#include <osg/Group>
+#include <osg/NodeCallback>
+#include <osg/Switch>
+#include <osg/Material>
+#include <osg/MatrixTransform>
+#include <osgDB/Archive>
+#include <osgDB/FileNameUtils>
+#include <osgDB/FileUtils>
+#include <osgDB/ReadFile>
+#include <osgDB/WriteFile>
+#include <osgDB/Registry>
+#include <osgDB/SharedStateManager>
+#include <osgUtil/Optimizer>
+
+#include <simgear/scene/util/SGSceneFeatures.hxx>
+#include <simgear/scene/util/SGStateAttributeVisitor.hxx>
+#include <simgear/scene/util/SGTextureStateAttributeVisitor.hxx>
+
+#include <simgear/structure/exception.hxx>
+#include <simgear/props/props.hxx>
+#include <simgear/props/props_io.hxx>
+#include <simgear/props/condition.hxx>
+
+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<Referenced> 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<Texture2D*>(refAttr.first.get());
+    if (!texture)
+      return 0;
+    
+    ref_ptr<Image> 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<Texture2D*>(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<Texture2D*>(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<Texture*>(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<Material*>(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<Node*>(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<Group> 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> 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 (file)
index 0000000..770d4ee
--- /dev/null
@@ -0,0 +1,82 @@
+// ModelRegistry.hxx -- interface to the OSG model registry
+//
+// Copyright (C) 2007  Tim Moore <timoore@redhat.com>
+//
+// 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 <osg/ref_ptr>
+#include <osgDB/ReaderWriter>
+#include <osgDB/Registry>
+
+#include <simgear/compiler.h>
+
+#include STL_STRING
+#include <map>
+
+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<ModelRegistry> instance;
+    typedef std::map<std::string, osg::ref_ptr<osgDB::Registry::ReadFileCallback> >
+    CallbackMap;
+    CallbackMap imageCallbackMap;
+    CallbackMap nodeCallbackMap;
+};
+
+// Proxy for registering extension-based callbacks
+
+template<typename T>
+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
index 749b45c3f7d2a023a85f9b908f01a0c0c00e8db8..4c49fd359ef81d4003256e21fa0a3d336461524b 100644 (file)
 #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<osg::Referenced> 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<osg::Texture2D*>(refAttr.first.get());
-    if (!texture)
-      return 0;
-    
-    osg::ref_ptr<osg::Image> 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<osg::Texture2D*>(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<osg::Texture2D*>(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<osg::Texture*>(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<osg::Material*>(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<osg::Node*>(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<osg::Group> 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)
index daa7f70e629ea25afd4d9004331dd58b030e4f4e..4735f0e3035a4228a7a2fa95ce101898313ca75b 100644 (file)
 
 #include <osgDB/FileNameUtils>
 #include <osgDB/Registry>
+
+#include <simgear/scene/model/ModelRegistry.hxx>
+
 #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<OSGFileCallback> g_btgCallbackProxy("btg");
+}
index 20070b24bb2a6e44bbf8fc7bd269a815e7a557a2..3c0ccc80f507f4d2eeded75d7a3e1a46640a0015 100644 (file)
@@ -31,5 +31,6 @@ public:
                                 const osgDB::ReaderWriter::Options* options)
         const;
 };
+
 #endif