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.
This commit is contained in:
timoore 2008-04-23 18:13:50 +00:00
parent 8f48515b80
commit 8f46f2cac8
6 changed files with 220 additions and 60 deletions

View File

@ -1163,6 +1163,12 @@
<File
RelativePath="..\..\simgear\scene\util\StateAttributeFactory.hxx">
</File>
<File
RelativePath="..\..\simgear\scene\util\NodeAndDrawableVisitor.cxx">
</File>
<File
RelativePath="..\..\simgear\scene\util\NodeAndDrawableVisitor.cxx.hxx">
</File>
</Filter>
<File
RelativePath="..\..\simgear\simgear_config.h-msvc71">

View File

@ -19,6 +19,8 @@
#include "ModelRegistry.hxx"
#include <algorithm>
#include <utility>
#include <vector>
#include <OpenThreads/ScopedLock>
@ -41,6 +43,7 @@
#include <simgear/scene/util/SGSceneFeatures.hxx>
#include <simgear/scene/util/SGStateAttributeVisitor.hxx>
#include <simgear/scene/util/SGTextureStateAttributeVisitor.hxx>
#include <simgear/scene/util/NodeAndDrawableVisitor.hxx>
#include <simgear/structure/exception.hxx>
#include <simgear/props/props.hxx>
@ -76,71 +79,94 @@ private:
ref_ptr<Referenced> 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<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 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;
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;
}
TextureUpdateVisitor(const FilePathList& pathList) : _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 whole 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(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<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 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;
}

View File

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

View File

@ -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 <osg/Drawable>
#include <osg/Geode>
#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<Geode*>(&node);
if (geode) {
unsigned numDrawables = geode->getNumDrawables();
for (unsigned i = 0; i < numDrawables; ++i)
apply(*geode->getDrawable(i));
} else {
NodeVisitor::traverse(node);
}
}
}

View File

@ -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 <osg/Drawable>
#include <osg/NodeVisitor>
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

View File

@ -16,6 +16,7 @@
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
void Reshape(int w, int h);