From 8f46f2cac8adaaef514e520cc688533b83a78ffe Mon Sep 17 00:00:00 2001 From: timoore Date: Wed, 23 Apr 2008 18:13:50 +0000 Subject: [PATCH] Rewrite livery texture replacement to copy StateSet objects Also, add a NodeAndDrawableVisitor that descends into osg::Drawable. That motivation for this is that it's a bad idea to modify state sets that the osgDB::SharedStateManager might be keeping. --- projects/VC7.1/SimGear.vcproj | 6 + simgear/scene/model/ModelRegistry.cxx | 143 +++++++++++------- simgear/scene/util/Makefile.am | 2 + simgear/scene/util/NodeAndDrawableVisitor.cxx | 73 +++++++++ simgear/scene/util/NodeAndDrawableVisitor.hxx | 51 +++++++ simgear/screen/TestRenderTexture.cpp | 1 + 6 files changed, 218 insertions(+), 58 deletions(-) create mode 100644 simgear/scene/util/NodeAndDrawableVisitor.cxx create mode 100644 simgear/scene/util/NodeAndDrawableVisitor.hxx diff --git a/projects/VC7.1/SimGear.vcproj b/projects/VC7.1/SimGear.vcproj index f9c17d42..96ddc4ce 100755 --- a/projects/VC7.1/SimGear.vcproj +++ b/projects/VC7.1/SimGear.vcproj @@ -1163,6 +1163,12 @@ + + + + diff --git a/simgear/scene/model/ModelRegistry.cxx b/simgear/scene/model/ModelRegistry.cxx index 5d123109..a1f49f2a 100644 --- a/simgear/scene/model/ModelRegistry.cxx +++ b/simgear/scene/model/ModelRegistry.cxx @@ -19,6 +19,8 @@ #include "ModelRegistry.hxx" #include +#include +#include #include @@ -41,6 +43,7 @@ #include #include #include +#include #include #include @@ -76,71 +79,94 @@ private: ref_ptr mReferenced; }; -// Visitor for -class SGTextureUpdateVisitor : public SGTextureStateAttributeVisitor { +// Change the StateSets of a model to hold different textures based on +// a livery path. +class TextureUpdateVisitor : public NodeAndDrawableVisitor { 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 it is empty or they are identical then there is nothing to do - if (fullLiveryFile.empty() || fullLiveryFile == fullFilePath) - return 0; - - image = readImageFile(fullLiveryFile); - if (!image) - return 0; + TextureUpdateVisitor(const FilePathList& pathList) : _pathList(pathList) {} + virtual void apply(Node& node) + { + StateSet* stateSet = cloneStateSet(node.getStateSet()); + if (stateSet) + node.setStateSet(stateSet); + traverse(node); + } - 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; + virtual void apply(Drawable& drawable) + { + StateSet* stateSet = cloneStateSet(drawable.getStateSet()); + if (stateSet) + drawable.setStateSet(stateSet); + } + // Copied whole from Mathias' earlier SGTextureUpdateVisitor +protected: + Texture2D* textureReplace(int unit, const StateAttribute* attr) + { + const Texture2D* texture = dynamic_cast(attr); - // 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); + if (!texture) + return 0; + + const Image* image = texture->getImage(0); + if (!image) + return 0; + + // The currently loaded file name + const 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, _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(copyOp(texture)); + if (!newTexture) { + return 0; + } else { + newTexture->setImage(newImage); + return newTexture; } - ++i; - } } - } - + StateSet* cloneStateSet(const StateSet* stateSet) + { + typedef pair Tex2D; + vector 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->clone(CopyOp())); + for (vector::iterator i = newTextures.begin(); + i != newTextures.end(); + ++i) { + result->setTextureAttribute(i->first, i->second); + } + } + } + return result; + } private: - FilePathList mPathList; + FilePathList _pathList; }; + class SGTexCompressionVisitor : public SGTextureStateAttributeVisitor { public: virtual void apply(int, StateSet::RefAttributePair& refAttr) @@ -352,8 +378,9 @@ osg::Node* DefaultCopyPolicy::copy(osg::Node* model, const string& fileName, res->addObserver(databaseReference); // Update liveries - SGTextureUpdateVisitor liveryUpdate(opt->getDatabasePathList()); + TextureUpdateVisitor liveryUpdate(opt->getDatabasePathList()); res->accept(liveryUpdate); + return res; } diff --git a/simgear/scene/util/Makefile.am b/simgear/scene/util/Makefile.am index 121900a5..28af3fdc 100644 --- a/simgear/scene/util/Makefile.am +++ b/simgear/scene/util/Makefile.am @@ -14,6 +14,7 @@ include_HEADERS = \ SGStateAttributeVisitor.hxx \ SGTextureStateAttributeVisitor.hxx \ SGUpdateVisitor.hxx \ + NodeAndDrawableVisitor.hxx \ QuadTreeBuilder.hxx \ RenderConstants.hxx \ StateAttributeFactory.hxx \ @@ -26,6 +27,7 @@ libsgutil_a_SOURCES = \ SGSceneUserData.cxx \ SGStateAttributeVisitor.cxx \ SGTextureStateAttributeVisitor.cxx \ + NodeAndDrawableVisitor.cxx \ StateAttributeFactory.cxx \ QuadTreeBuilder.cxx diff --git a/simgear/scene/util/NodeAndDrawableVisitor.cxx b/simgear/scene/util/NodeAndDrawableVisitor.cxx new file mode 100644 index 00000000..257171c7 --- /dev/null +++ b/simgear/scene/util/NodeAndDrawableVisitor.cxx @@ -0,0 +1,73 @@ +/* -*-c++-*- + * + * Copyright (C) 2008 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. + * + */ + +#include +#include + +#include "NodeAndDrawableVisitor.hxx" + +namespace simgear +{ +using namespace osg; + +NodeAndDrawableVisitor::NodeAndDrawableVisitor(NodeVisitor::TraversalMode tm) : + NodeVisitor(tm) +{ +} + +NodeAndDrawableVisitor::NodeAndDrawableVisitor(NodeVisitor::VisitorType type, + NodeVisitor::TraversalMode tm) : + NodeVisitor(type, tm) +{ +} + +NodeAndDrawableVisitor::~NodeAndDrawableVisitor() +{ +} + +void NodeAndDrawableVisitor::apply(Node& node) +{ + traverse(node); +} + +void NodeAndDrawableVisitor::apply(Drawable& Drawable) +{ +} + +void NodeAndDrawableVisitor::traverse(Node& node) +{ + TraversalMode tm = getTraversalMode(); + if (tm == TRAVERSE_NONE) { + return; + } else if (tm == TRAVERSE_PARENTS) { + NodeVisitor::traverse(node); + return; + } + Geode* geode = dynamic_cast(&node); + if (geode) { + unsigned numDrawables = geode->getNumDrawables(); + for (unsigned i = 0; i < numDrawables; ++i) + apply(*geode->getDrawable(i)); + } else { + NodeVisitor::traverse(node); + } +} +} diff --git a/simgear/scene/util/NodeAndDrawableVisitor.hxx b/simgear/scene/util/NodeAndDrawableVisitor.hxx new file mode 100644 index 00000000..cb780d93 --- /dev/null +++ b/simgear/scene/util/NodeAndDrawableVisitor.hxx @@ -0,0 +1,51 @@ +/* -*-c++-*- + * + * Copyright (C) 2008 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 SIMGEAR_NODEANDDRAWABLEVISITOR_HXX +#define SIMGEAR_NODEANDDRAWABLEVISITOR_HXX 1 + +#include +#include + +namespace simgear +{ +/** A node visitor that descends into Drawables too. + */ +class NodeAndDrawableVisitor : public osg::NodeVisitor +{ +public: + NodeAndDrawableVisitor(osg::NodeVisitor::TraversalMode tm = osg::NodeVisitor::TRAVERSE_NONE); + NodeAndDrawableVisitor(osg::NodeVisitor::VisitorType type, + osg::NodeVisitor::TraversalMode tm = osg::NodeVisitor::TRAVERSE_NONE); + virtual ~NodeAndDrawableVisitor(); + using osg::NodeVisitor::apply; + virtual void apply(osg::Node& node); + /** Visit a Drawable node. Note that you cannot write an apply() + method with an argument that is a subclass of Drawable and expect + it to be called, because this visitor can't add the double dispatch + machinery of NodeVisitor to the existing OSG Drawable subclasses. + */ + virtual void apply(osg::Drawable& drawable); + // hides NodeVisitor::traverse + void traverse(osg::Node& node); +}; + +} +#endif diff --git a/simgear/screen/TestRenderTexture.cpp b/simgear/screen/TestRenderTexture.cpp index 490e41e9..2c7fc236 100644 --- a/simgear/screen/TestRenderTexture.cpp +++ b/simgear/screen/TestRenderTexture.cpp @@ -16,6 +16,7 @@ #include #include +#include void Reshape(int w, int h); -- 2.39.5