Files
simgear/simgear/scene/model/model.cxx
T
James Turner 8dbabc8b43 Texture-compression fixes
Rename a highly confusing method, and disable the older texture-
compression code since it interacts badly with the new texture-cache.
2020-04-07 12:12:52 +01:00

397 lines
12 KiB
C++

// model.cxx - manage a 3D aircraft model.
// Written by David Megginson, started 2002.
//
// This file is in the Public Domain, and comes with no warranty.
#ifdef HAVE_CONFIG_H
#include <simgear_config.h>
#endif
#include <utility>
#include <boost/foreach.hpp>
#include <osg/ref_ptr>
#include <osgDB/FileNameUtils>
#include <osgDB/FileUtils>
#include <osgDB/ReaderWriter>
#include <osgDB/ReadFile>
#include <osgDB/SharedStateManager>
#include <simgear/scene/material/Effect.hxx>
#include <simgear/scene/material/EffectGeode.hxx>
#include <simgear/scene/util/SGSceneFeatures.hxx>
#include <simgear/scene/util/SGSceneUserData.hxx>
#include <simgear/scene/util/CopyOp.hxx>
#include <simgear/scene/util/SplicingVisitor.hxx>
#include <simgear/scene/util/SGReaderWriterOptions.hxx>
#include <simgear/structure/exception.hxx>
#include <simgear/structure/Singleton.hxx>
#include <simgear/props/props.hxx>
#include <simgear/props/props_io.hxx>
#include <simgear/props/condition.hxx>
#include "model.hxx"
using std::vector;
osg::Texture2D*
SGLoadTexture2D(bool staticTexture, const std::string& path,
const osgDB::Options* options,
bool wrapu, bool wrapv, int)
{
osg::ref_ptr<osg::Image> image;
if (options)
#if OSG_VERSION_LESS_THAN(3,4,0)
image = osgDB::readImageFile(path, options);
#else
image = osgDB::readRefImageFile(path, options);
#endif
else
#if OSG_VERSION_LESS_THAN(3,4,0)
image = osgDB::readImageFile(path);
#else
image = osgDB::readRefImageFile(path);
#endif
osg::ref_ptr<osg::Texture2D> texture = new osg::Texture2D;
texture->setImage(image);
texture->setMaxAnisotropy(SGSceneFeatures::instance()->getTextureFilter());
if (staticTexture)
texture->setDataVariance(osg::Object::STATIC);
if (wrapu)
texture->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);
else
texture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP);
if (wrapv)
texture->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);
else
texture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP);
if (image) {
int s = image->s();
int t = image->t();
if (s <= t && 32 <= s) {
SGSceneFeatures::instance()->applyTextureCompression(texture.get());
} else if (t < s && 32 <= t) {
SGSceneFeatures::instance()->applyTextureCompression(texture.get());
}
}
return texture.release();
}
namespace simgear
{
using namespace std;
using namespace osg;
using simgear::CopyOp;
Node* copyModel(Node* model)
{
const CopyOp::CopyFlags flags = (CopyOp::DEEP_COPY_ALL
& ~CopyOp::DEEP_COPY_TEXTURES
& ~CopyOp::DEEP_COPY_IMAGES
& ~CopyOp::DEEP_COPY_STATESETS
& ~CopyOp::DEEP_COPY_STATEATTRIBUTES
& ~CopyOp::DEEP_COPY_ARRAYS
& ~CopyOp::DEEP_COPY_PRIMITIVES
// This will preserve display lists ...
& ~CopyOp::DEEP_COPY_DRAWABLES
& ~CopyOp::DEEP_COPY_SHAPES);
return (CopyOp(flags))(model);
}
TextureUpdateVisitor::TextureUpdateVisitor(const osgDB::FilePathList& pathList) :
NodeAndDrawableVisitor(NodeVisitor::TRAVERSE_ALL_CHILDREN),
_pathList(pathList)
{
}
void TextureUpdateVisitor::apply(osg::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;
#if OSG_VERSION_LESS_THAN(3,4,0)
Image* newImage = readImageFile(fullLiveryFile);
#else
osg::ref_ptr<Image> newImage = readRefImageFile(fullLiveryFile);
#endif
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;
newTexture->setImage(newImage);
#if OSG_VERSION_LESS_THAN(3,4,0)
if (newImage->valid())
#else
if (newImage.valid())
#endif
{
newTexture->setMaxAnisotropy(SGSceneFeatures::instance()->getTextureFilter());
}
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);
}
namespace
{
class MakeEffectVisitor : public SplicingVisitor
{
public:
typedef std::map<string, SGPropertyNode_ptr> EffectMap;
using SplicingVisitor::apply;
MakeEffectVisitor(const SGReaderWriterOptions* options = 0)
: _options(options)
{
}
virtual void apply(osg::Group& node);
virtual void apply(osg::Geode& geode);
EffectMap& getEffectMap() { return _effectMap; }
const EffectMap& getEffectMap() const { return _effectMap; }
void setDefaultEffect(SGPropertyNode* effect)
{
_currentEffectParent = effect;
}
SGPropertyNode* getDefaultEffect() { return _currentEffectParent; }
protected:
EffectMap _effectMap;
SGPropertyNode_ptr _currentEffectParent;
osg::ref_ptr<const SGReaderWriterOptions> _options;
};
void MakeEffectVisitor::apply(osg::Group& node)
{
SGPropertyNode_ptr savedEffectRoot;
const string& nodeName = node.getName();
bool restoreEffect = false;
if (!nodeName.empty()) {
EffectMap::iterator eitr = _effectMap.find(nodeName);
if (eitr != _effectMap.end()) {
savedEffectRoot = _currentEffectParent;
_currentEffectParent = eitr->second;
restoreEffect = true;
}
}
SplicingVisitor::apply(node);
// If a new node was created, copy the user data too.
ref_ptr<SGSceneUserData> userData = SGSceneUserData::getSceneUserData(&node);
if (userData.valid() && _childStack.back().back().get() != &node)
_childStack.back().back()->setUserData(new SGSceneUserData(*userData));
if (restoreEffect)
_currentEffectParent = savedEffectRoot;
}
void MakeEffectVisitor::apply(osg::Geode& geode)
{
if (pushNode(getNewNode(geode)))
return;
osg::StateSet* ss = geode.getStateSet();
if (!ss) {
pushNode(&geode);
return;
}
SGPropertyNode_ptr ssRoot = new SGPropertyNode;
makeParametersFromStateSet(ssRoot, ss);
SGPropertyNode_ptr effectRoot = new SGPropertyNode;
effect::mergePropertyTrees(effectRoot, ssRoot, _currentEffectParent);
Effect* effect = makeEffect(effectRoot, true, _options.get());
EffectGeode* eg = dynamic_cast<EffectGeode*>(&geode);
if (eg) {
eg->setEffect(effect);
} else {
eg = new EffectGeode;
eg->setEffect(effect);
ref_ptr<SGSceneUserData> userData = SGSceneUserData::getSceneUserData(&geode);
if (userData.valid())
eg->setUserData(new SGSceneUserData(*userData));
for (unsigned i = 0; i < geode.getNumDrawables(); ++i) {
osg::Drawable *drawable = geode.getDrawable(i);
eg->addDrawable(drawable);
// Generate tangent vectors etc if needed
osg::Geometry *geom = dynamic_cast<osg::Geometry*>(drawable);
if(geom) eg->runGenerators(geom);
}
}
pushResultNode(&geode, eg);
}
}
namespace
{
class DefaultEffect : public simgear::Singleton<DefaultEffect>
{
public:
DefaultEffect()
{
_effect = new SGPropertyNode;
makeChild(_effect.ptr(), "inherits-from")
->setStringValue("Effects/model-default");
}
virtual ~DefaultEffect() {}
SGPropertyNode* getEffect() { return _effect.ptr(); }
protected:
SGPropertyNode_ptr _effect;
};
}
ref_ptr<Node> instantiateEffects(osg::Node* modelGroup,
PropertyList& effectProps,
const SGReaderWriterOptions* options)
{
SGPropertyNode_ptr defaultEffectPropRoot;
MakeEffectVisitor visitor(options);
MakeEffectVisitor::EffectMap& emap = visitor.getEffectMap();
for (PropertyList::iterator itr = effectProps.begin(),
end = effectProps.end();
itr != end;
++itr)
{
SGPropertyNode_ptr configNode = *itr;
std::vector<SGPropertyNode_ptr> objectNames =
configNode->getChildren("object-name");
SGPropertyNode* defaultNode = configNode->getChild("default");
if (defaultNode && defaultNode->getValue<bool>())
defaultEffectPropRoot = configNode;
BOOST_FOREACH(SGPropertyNode_ptr objNameNode, objectNames) {
emap.insert(make_pair(objNameNode->getStringValue(), configNode));
}
configNode->removeChild("default");
configNode->removeChildren("object-name");
}
if (!defaultEffectPropRoot)
defaultEffectPropRoot = DefaultEffect::instance()->getEffect();
visitor.setDefaultEffect(defaultEffectPropRoot.ptr());
modelGroup->accept(visitor);
osg::NodeList& result = visitor.getResults();
return ref_ptr<Node>(result[0].get());
}
ref_ptr<Node> instantiateMaterialEffects(osg::Node* modelGroup,
const SGReaderWriterOptions* options)
{
SGPropertyNode_ptr effect;
PropertyList effectProps;
if (options->getMaterialLib()) {
const SGGeod loc = SGGeod(options->getLocation());
SGMaterialCache* matcache = options->getMaterialLib()->generateMatCache(loc);
SGMaterial* mat = matcache->find(options->getMaterialName());
delete matcache;
if (mat) {
effect = new SGPropertyNode();
makeChild(effect, "inherits-from")->setStringValue(mat->get_effect_name());
} else {
effect = DefaultEffect::instance()->getEffect();
SG_LOG( SG_TERRAIN, SG_ALERT, "Unable to get effect for " << options->getMaterialName());
}
} else {
effect = DefaultEffect::instance()->getEffect();
}
effect->addChild("default")->setBoolValue(true);
effectProps.push_back(effect);
return instantiateEffects(modelGroup, effectProps, options);
}
}
// end of model.cxx