remove CopyPolicy from ModelRegistry

Put the responsibility for copying a loaded model directly in
SGReaderWriterXML.
This commit is contained in:
Tim Moore
2009-09-20 09:29:05 +02:00
parent a25e859fa7
commit 6a7c200002
6 changed files with 222 additions and 181 deletions

View File

@@ -128,125 +128,6 @@ protected:
}
};
// Change the StateSets of a model to hold different textures based on
// a livery path.
class TextureUpdateVisitor : public NodeAndDrawableVisitor {
public:
TextureUpdateVisitor(const FilePathList& pathList) :
NodeAndDrawableVisitor(NodeVisitor::TRAVERSE_ALL_CHILDREN),
_pathList(pathList)
{
}
virtual void apply(Node& node)
{
StateSet* stateSet = cloneStateSet(node.getStateSet());
if (stateSet)
node.setStateSet(stateSet);
traverse(node);
}
virtual void apply(Drawable& drawable)
{
StateSet* stateSet = cloneStateSet(drawable.getStateSet());
if (stateSet)
drawable.setStateSet(stateSet);
}
// Copied from Mathias' earlier SGTextureUpdateVisitor
protected:
Texture2D* textureReplace(int unit, const StateAttribute* attr)
{
const Texture2D* texture = dynamic_cast<const Texture2D*>(attr);
if (!texture)
return 0;
const Image* image = texture->getImage();
const string* fullFilePath = 0;
if (image) {
// The currently loaded file name
fullFilePath = &image->getFileName();
} else {
fullFilePath = &texture->getName();
}
// The short name
string fileName = getSimpleFileName(*fullFilePath);
if (fileName.empty())
return 0;
// The name that should be found with the current database path
string fullLiveryFile = findFileInPath(fileName, _pathList);
// If it is empty or they are identical then there is nothing to do
if (fullLiveryFile.empty() || fullLiveryFile == *fullFilePath)
return 0;
Image* newImage = readImageFile(fullLiveryFile);
if (!newImage)
return 0;
CopyOp copyOp(CopyOp::DEEP_COPY_ALL & ~CopyOp::DEEP_COPY_IMAGES);
Texture2D* newTexture = static_cast<Texture2D*>(copyOp(texture));
if (!newTexture) {
return 0;
} else {
newTexture->setImage(newImage);
return newTexture;
}
}
StateSet* cloneStateSet(const StateSet* stateSet)
{
typedef pair<int, Texture2D*> Tex2D;
vector<Tex2D> newTextures;
StateSet* result = 0;
if (!stateSet)
return 0;
int numUnits = stateSet->getTextureAttributeList().size();
if (numUnits > 0) {
for (int i = 0; i < numUnits; ++i) {
const StateAttribute* attr
= stateSet->getTextureAttribute(i, StateAttribute::TEXTURE);
Texture2D* newTexture = textureReplace(i, attr);
if (newTexture)
newTextures.push_back(Tex2D(i, newTexture));
}
if (!newTextures.empty()) {
result = static_cast<StateSet*>(stateSet->clone(CopyOp()));
for (vector<Tex2D>::iterator i = newTextures.begin();
i != newTextures.end();
++i) {
result->setTextureAttribute(i->first, i->second);
}
}
}
return result;
}
private:
FilePathList _pathList;
};
// Create new userdata structs in a copied model.
// The BVH trees are shared with the original model, but the velocity fields
// should usually be distinct fields for distinct models.
class UserDataCopyVisitor : public osg::NodeVisitor {
public:
UserDataCopyVisitor() :
osg::NodeVisitor(osg::NodeVisitor::NODE_VISITOR,
osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
{
}
virtual void apply(osg::Node& node)
{
osg::ref_ptr<SGSceneUserData> userData;
userData = SGSceneUserData::getSceneUserData(&node);
if (userData.valid()) {
SGSceneUserData* newUserData = new SGSceneUserData(*userData);
newUserData->setVelocity(0);
node.setUserData(newUserData);
}
node.traverse(*this);
}
};
class SGTexCompressionVisitor : public SGTextureStateAttributeVisitor {
public:
@@ -416,41 +297,6 @@ osg::Node* OptimizeModelPolicy::optimize(osg::Node* node,
return node;
}
osg::Node* DefaultCopyPolicy::copy(osg::Node* model, const string& fileName,
const osgDB::ReaderWriter::Options* opt)
{
// Add an extra reference to the model stored in the database.
// That is to avoid expiring the object from the cache even if it is still
// in use. Note that the object cache will think that a model is unused
// if the reference count is 1. If we clone all structural nodes here
// we need that extra reference to the original object
SGDatabaseReference* databaseReference;
databaseReference = new SGDatabaseReference(model);
CopyOp::CopyFlags flags = CopyOp::DEEP_COPY_ALL;
flags &= ~CopyOp::DEEP_COPY_TEXTURES;
flags &= ~CopyOp::DEEP_COPY_IMAGES;
flags &= ~CopyOp::DEEP_COPY_STATESETS;
flags &= ~CopyOp::DEEP_COPY_STATEATTRIBUTES;
flags &= ~CopyOp::DEEP_COPY_ARRAYS;
flags &= ~CopyOp::DEEP_COPY_PRIMITIVES;
// This will safe display lists ...
flags &= ~CopyOp::DEEP_COPY_DRAWABLES;
flags &= ~CopyOp::DEEP_COPY_SHAPES;
osg::Node* res = CopyOp(flags)(model);
res->addObserver(databaseReference);
// Update liveries
TextureUpdateVisitor liveryUpdate(opt->getDatabasePathList());
res->accept(liveryUpdate);
// Copy the userdata fields, still sharing the boundingvolumes,
// but introducing new data for velocities.
UserDataCopyVisitor userDataCopyVisitor;
res->accept(userDataCopyVisitor);
return res;
}
string OSGSubstitutePolicy::substitute(const string& name,
const ReaderWriter::Options* opt)
{
@@ -599,7 +445,7 @@ struct ACProcessPolicy {
};
typedef ModelRegistryCallback<ACProcessPolicy, DefaultCachePolicy,
ACOptimizePolicy, DefaultCopyPolicy,
ACOptimizePolicy,
OSGSubstitutePolicy, BuildLeafBVHPolicy>
ACCallback;

View File

@@ -61,12 +61,12 @@ namespace simgear
// readNode function is specified as a template with a bunch of
// pluggable (and predefined) policies.
template <typename ProcessPolicy, typename CachePolicy, typename OptimizePolicy,
typename CopyPolicy, typename SubstitutePolicy, typename BVHPolicy>
typename SubstitutePolicy, typename BVHPolicy>
class ModelRegistryCallback : public osgDB::Registry::ReadFileCallback {
public:
ModelRegistryCallback(const std::string& extension) :
_processPolicy(extension), _cachePolicy(extension),
_optimizePolicy(extension), _copyPolicy(extension),
_optimizePolicy(extension),
_substitutePolicy(extension), _bvhPolicy(extension)
{
}
@@ -100,9 +100,7 @@ public:
_bvhPolicy.buildBVH(fileName, optimizedNode.get());
_cachePolicy.addToCache(fileName, optimizedNode.get());
}
osg::ref_ptr<osg::Node> copyNode;
copyNode = _copyPolicy.copy(optimizedNode.get(), fileName, opt);
return ReaderWriter::ReadResult(copyNode);
return ReaderWriter::ReadResult(optimizedNode);
}
protected:
static osgDB::ReaderWriter::ReadResult
@@ -120,7 +118,6 @@ protected:
ProcessPolicy _processPolicy;
CachePolicy _cachePolicy;
OptimizePolicy _optimizePolicy;
CopyPolicy _copyPolicy;
SubstitutePolicy _substitutePolicy;
BVHPolicy _bvhPolicy;
virtual ~ModelRegistryCallback() {}
@@ -169,21 +166,6 @@ struct NoOptimizePolicy {
}
};
struct DefaultCopyPolicy {
DefaultCopyPolicy(const std::string& extension) {}
osg::Node* copy(osg::Node* node, const std::string& fileName,
const osgDB::ReaderWriter::Options* opt);
};
struct NoCopyPolicy {
NoCopyPolicy(const std::string& extension) {}
osg::Node* copy(osg::Node* node, const std::string& fileName,
const osgDB::ReaderWriter::Options* opt)
{
return node;
}
};
struct OSGSubstitutePolicy {
OSGSubstitutePolicy(const std::string& extension) {}
std::string substitute(const std::string& name,
@@ -215,7 +197,7 @@ struct NoBuildBVHPolicy {
};
typedef ModelRegistryCallback<DefaultProcessPolicy, DefaultCachePolicy,
OptimizeModelPolicy, DefaultCopyPolicy,
OptimizeModelPolicy,
OSGSubstitutePolicy, BuildLeafBVHPolicy>
DefaultCallback;
@@ -251,7 +233,7 @@ protected:
// Callback that only loads the file without any caching or
// postprocessing.
typedef ModelRegistryCallback<DefaultProcessPolicy, NoCachePolicy,
NoOptimizePolicy, NoCopyPolicy,
NoOptimizePolicy,
NoSubstitutePolicy, BuildLeafBVHPolicy>
LoadOnlyCallback;

View File

@@ -105,6 +105,32 @@ private:
SGSharedPtr<SGCondition> mCondition;
};
// Little helper class that holds an extra reference to a
// loaded 3d model.
// Since we clone all structural nodes from our 3d models,
// the database pager will only see one single reference to
// top node of the model and expire it relatively fast.
// We attach that extra reference to every model cloned from
// a base model in the pager. When that cloned model is deleted
// this extra reference is deleted too. So if there are no
// cloned models left the model will expire.
namespace {
class SGDatabaseReference : public osg::Observer
{
public:
SGDatabaseReference(osg::Referenced* referenced) :
mReferenced(referenced)
{ }
virtual void objectDeleted(void*)
{
mReferenced = 0;
}
private:
osg::ref_ptr<osg::Referenced> mReferenced;
};
}
static osg::Node *
sgLoad3DModel_internal(const string &path,
const osgDB::ReaderWriter::Options* options_,
@@ -181,10 +207,31 @@ sgLoad3DModel_internal(const string &path,
texturepath = texturepath.dir();
options->setDatabasePath(texturepath.str());
model = osgDB::readNodeFile(modelpath.str(), options.get());
if (model == 0)
osg::Node* origModel
= osgDB::readNodeFile(modelpath.str(), options.get());
if (!origModel)
throw sg_io_exception("Failed to load 3D model",
sg_location(modelpath.str()));
model = copyModel(origModel);
// Add an extra reference to the model stored in the database.
// That is to avoid expiring the object from the cache even if
// it is still in use. Note that the object cache will think
// that a model is unused if the reference count is 1. If we
// clone all structural nodes here we need that extra
// reference to the original object
SGDatabaseReference* databaseReference;
databaseReference = new SGDatabaseReference(origModel);
model->addObserver(databaseReference);
// Update liveries
TextureUpdateVisitor liveryUpdate(options->getDatabasePathList());
model->accept(liveryUpdate);
// Copy the userdata fields, still sharing the boundingvolumes,
// but introducing new data for velocities.
UserDataCopyVisitor userDataCopyVisitor;
model->accept(userDataCopyVisitor);
}
model->setName(modelpath.str());

View File

@@ -7,11 +7,17 @@
#include <simgear_config.h>
#endif
#include <utility>
#include <osg/ref_ptr>
#include <osgDB/FileNameUtils>
#include <osgDB/FileUtils>
#include <osgDB/ReaderWriter>
#include <osgDB/ReadFile>
#include <osgDB/SharedStateManager>
#include <simgear/scene/util/SGSceneFeatures.hxx>
#include <simgear/scene/util/SGSceneUserData.hxx>
#include <simgear/structure/exception.hxx>
#include <simgear/props/props.hxx>
@@ -59,4 +65,131 @@ SGLoadTexture2D(bool staticTexture, const std::string& path,
return texture.release();
}
namespace simgear
{
using namespace osg;
Node* copyModel(Node* model)
{
CopyOp::CopyFlags flags = CopyOp::DEEP_COPY_ALL;
flags &= ~CopyOp::DEEP_COPY_TEXTURES;
flags &= ~CopyOp::DEEP_COPY_IMAGES;
flags &= ~CopyOp::DEEP_COPY_STATESETS;
flags &= ~CopyOp::DEEP_COPY_STATEATTRIBUTES;
flags &= ~CopyOp::DEEP_COPY_ARRAYS;
flags &= ~CopyOp::DEEP_COPY_PRIMITIVES;
// This will preserve display lists ...
flags &= ~CopyOp::DEEP_COPY_DRAWABLES;
flags &= ~CopyOp::DEEP_COPY_SHAPES;
return static_cast<Node*>(model->clone(CopyOp(flags)));
}
TextureUpdateVisitor::TextureUpdateVisitor(const osgDB::FilePathList& pathList) :
NodeAndDrawableVisitor(NodeVisitor::TRAVERSE_ALL_CHILDREN),
_pathList(pathList)
{
}
void TextureUpdateVisitor::apply(Node& node)
{
StateSet* stateSet = cloneStateSet(node.getStateSet());
if (stateSet)
node.setStateSet(stateSet);
traverse(node);
}
void TextureUpdateVisitor::apply(Drawable& drawable)
{
StateSet* stateSet = cloneStateSet(drawable.getStateSet());
if (stateSet)
drawable.setStateSet(stateSet);
}
Texture2D* TextureUpdateVisitor::textureReplace(int unit, const StateAttribute* attr)
{
using namespace osgDB;
const Texture2D* texture = dynamic_cast<const Texture2D*>(attr);
if (!texture)
return 0;
const Image* image = texture->getImage();
const string* fullFilePath = 0;
if (image) {
// The currently loaded file name
fullFilePath = &image->getFileName();
} else {
fullFilePath = &texture->getName();
}
// The short name
string fileName = getSimpleFileName(*fullFilePath);
if (fileName.empty())
return 0;
// The name that should be found with the current database path
string fullLiveryFile = findFileInPath(fileName, _pathList);
// If it is empty or they are identical then there is nothing to do
if (fullLiveryFile.empty() || fullLiveryFile == *fullFilePath)
return 0;
Image* newImage = readImageFile(fullLiveryFile);
if (!newImage)
return 0;
CopyOp copyOp(CopyOp::DEEP_COPY_ALL & ~CopyOp::DEEP_COPY_IMAGES);
Texture2D* newTexture = static_cast<Texture2D*>(copyOp(texture));
if (!newTexture) {
return 0;
} else {
newTexture->setImage(newImage);
return newTexture;
}
}
StateSet* TextureUpdateVisitor::cloneStateSet(const StateSet* stateSet)
{
typedef std::pair<int, Texture2D*> Tex2D;
vector<Tex2D> newTextures;
StateSet* result = 0;
if (!stateSet)
return 0;
int numUnits = stateSet->getTextureAttributeList().size();
if (numUnits > 0) {
for (int i = 0; i < numUnits; ++i) {
const StateAttribute* attr
= stateSet->getTextureAttribute(i, StateAttribute::TEXTURE);
Texture2D* newTexture = textureReplace(i, attr);
if (newTexture)
newTextures.push_back(Tex2D(i, newTexture));
}
if (!newTextures.empty()) {
result = static_cast<StateSet*>(stateSet->clone(CopyOp()));
for (vector<Tex2D>::iterator i = newTextures.begin();
i != newTextures.end();
++i) {
result->setTextureAttribute(i->first, i->second);
}
}
}
return result;
}
UserDataCopyVisitor::UserDataCopyVisitor() :
NodeVisitor(NodeVisitor::NODE_VISITOR,
NodeVisitor::TRAVERSE_ALL_CHILDREN)
{
}
void UserDataCopyVisitor::apply(Node& node)
{
ref_ptr<SGSceneUserData> userData;
userData = SGSceneUserData::getSceneUserData(&node);
if (userData.valid()) {
SGSceneUserData* newUserData = new SGSceneUserData(*userData);
newUserData->setVelocity(0);
node.setUserData(newUserData);
}
node.traverse(*this);
}
}
// end of model.cxx

View File

@@ -20,6 +20,7 @@
#include <osgDB/ReaderWriter>
#include <simgear/misc/sg_path.hxx>
#include <simgear/scene/util/NodeAndDrawableVisitor.hxx>
osg::Texture2D*
SGLoadTexture2D(bool staticTexture, const std::string& path,
@@ -54,4 +55,36 @@ SGLoadTexture2D(bool staticTexture, const SGPath& path,
mipmaplevels);
}
namespace simgear
{
osg::Node* copyModel(osg::Node* model);
// Change the StateSets of a model to hold different textures based on
// a livery path.
class TextureUpdateVisitor : public NodeAndDrawableVisitor
{
public:
TextureUpdateVisitor(const osgDB::FilePathList& pathList);
virtual void apply(osg::Node& node);
virtual void apply(osg::Drawable& drawable);
// Copied from Mathias' earlier SGTextureUpdateVisitor
protected:
osg::Texture2D* textureReplace(int unit, const osg::StateAttribute* attr);
osg::StateSet* cloneStateSet(const osg::StateSet* stateSet);
private:
osgDB::FilePathList _pathList;
};
// Create new userdata structs in a copied model.
// The BVH trees are shared with the original model, but the velocity fields
// should usually be distinct fields for distinct models.
class UserDataCopyVisitor : public osg::NodeVisitor
{
public:
UserDataCopyVisitor();
virtual void apply(osg::Node& node);
};
}
#endif // __MODEL_HXX

View File

@@ -76,7 +76,7 @@ SGReaderWriterBTG::readNode(const std::string& fileName,
typedef ModelRegistryCallback<DefaultProcessPolicy, NoCachePolicy,
NoOptimizePolicy, NoCopyPolicy,
NoOptimizePolicy,
NoSubstitutePolicy, BuildGroupBVHPolicy>
BTGCallback;