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.
This commit is contained in:
timoore 2007-11-29 23:55:01 +00:00
parent cafcecf03d
commit 2fbaddbecf
6 changed files with 504 additions and 341 deletions

View 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 \

View File

@ -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);
}

View File

@ -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

View File

@ -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<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)

View File

@ -17,10 +17,15 @@
#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");
}

View File

@ -31,5 +31,6 @@ public:
const osgDB::ReaderWriter::Options* options)
const;
};
#endif