Construct effects from property lists

The material code constructs a property list from its input parameters.

Enable dumping of Pass and Technique objects to a file.

Default effect now uses texture node instead of texture0
This commit is contained in:
timoore 2009-07-15 23:10:44 +00:00 committed by Tim Moore
parent c3b1802e95
commit 4219f16f61
9 changed files with 1017 additions and 166 deletions

View File

@ -1,3 +1,19 @@
// Copyright (C) 2008 - 2009 Tim Moore timoore@redhat.com
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library 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
// Library 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 "Effect.hxx"
#include "Technique.hxx"
#include "Pass.hxx"
@ -5,24 +21,43 @@
#include <algorithm>
#include <functional>
#include <iterator>
#include <map>
#include <utility>
#include <boost/bind.hpp>
#include <boost/foreach.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/tuple/tuple_comparison.hpp>
#include <osg/CullFace>
#include <osg/Drawable>
#include <osg/Material>
#include <osg/Program>
#include <osg/Referenced>
#include <osg/RenderInfo>
#include <osg/ShadeModel>
#include <osg/StateSet>
#include <osg/TexEnv>
#include <osg/Texture2D>
#include <osg/Vec4d>
#include <osgUtil/CullVisitor>
#include <osgDB/Registry>
#include <osgDB/FileUtils>
#include <osgDB/Input>
#include <osgDB/ParameterOutput>
#include <osgDB/ReadFile>
#include <osgDB/Registry>
#include <simgear/scene/util/SGSceneFeatures.hxx>
#include <simgear/scene/util/StateAttributeFactory.hxx>
#include <simgear/structure/OSGUtils.hxx>
#include <simgear/structure/SGExpression.hxx>
namespace simgear
{
using namespace std;
using namespace osg;
using namespace osgUtil;
@ -31,8 +66,8 @@ Effect::Effect()
}
Effect::Effect(const Effect& rhs, const CopyOp& copyop)
: root(rhs.root), parametersProp(rhs.parametersProp)
{
using namespace std;
using namespace boost;
transform(rhs.techniques.begin(), rhs.techniques.end(),
backRefInsertIterator(techniques),
@ -81,6 +116,638 @@ Effect::~Effect()
{
}
class PassAttributeBuilder : public Referenced
{
public:
virtual void buildAttribute(Effect* effect, Pass* pass,
const SGPropertyNode* prop,
const osgDB::ReaderWriter::Options* options)
= 0;
};
typedef map<const string, ref_ptr<PassAttributeBuilder> > PassAttrMap;
PassAttrMap passAttrMap;
template<typename T>
struct InstallAttributeBuilder
{
InstallAttributeBuilder(const string& name)
{
passAttrMap.insert(make_pair(name, new T));
}
};
// Simple tables of strings and OSG constants. The table intialization
// *must* be in alphabetical order.
template <typename T>
struct EffectNameValue
{
// Don't use std::pair because we want to use aggregate intialization.
const char* first;
T second;
class Compare
{
private:
static bool compare(const char* lhs, const char* rhs)
{
return strcmp(lhs, rhs) < 0;
}
public:
bool operator()(const EffectNameValue& lhs,
const EffectNameValue& rhs) const
{
return compare(lhs.first, rhs.first);
}
bool operator()(const char* lhs, const EffectNameValue& rhs) const
{
return compare(lhs, rhs.first);
}
bool operator()(const EffectNameValue& lhs, const char* rhs) const
{
return compare (lhs.first, rhs);
}
};
};
template<typename ENV, typename T, int N>
bool findAttr(const ENV (&attrs)[N], const SGPropertyNode* prop, T& result)
{
if (!prop)
return false;
const char* name = prop->getStringValue();
if (!name)
return false;
std::pair<const ENV*, const ENV*> itrs
= std::equal_range(&attrs[0], &attrs[N], name, typename ENV::Compare());
if (itrs.first == itrs.second) {
return false;
} else {
result = itrs.first->second;
return true;
}
}
void buildPass(Effect* effect, Technique* tniq, const SGPropertyNode* prop,
const osgDB::ReaderWriter::Options* options)
{
Pass* pass = new Pass;
tniq->passes.push_back(pass);
for (int i = 0; i < prop->nChildren(); ++i) {
const SGPropertyNode* attrProp = prop->getChild(i);
PassAttrMap::iterator itr = passAttrMap.find(attrProp->getName());
if (itr != passAttrMap.end())
itr->second->buildAttribute(effect, pass, attrProp, options);
else
SG_LOG(SG_INPUT, SG_ALERT,
"skipping unknown pass attribute " << attrProp->getName());
}
}
osg::Vec4f getColor(const SGPropertyNode* prop)
{
if (prop->nChildren() == 0) {
if (prop->getType() == props::VEC4D) {
return osg::Vec4f(prop->getValue<SGVec4d>().osg());
} else if (prop->getType() == props::VEC3D) {
return osg::Vec4f(prop->getValue<SGVec3d>().osg(), 1.0f);
} else {
SG_LOG(SG_INPUT, SG_ALERT,
"invalid color property " << prop->getName() << " "
<< prop->getStringValue());
return osg::Vec4f(0.0f, 0.0f, 0.0f, 1.0f);
}
} else {
osg::Vec4f result;
static const char* colors[] = {"r", "g", "b"};
for (int i = 0; i < 3; ++i) {
const SGPropertyNode* componentProp = prop->getChild(colors[i]);
result[i] = componentProp ? componentProp->getValue<float>() : 0.0f;
}
const SGPropertyNode* alphaProp = prop->getChild("a");
result[3] = alphaProp ? alphaProp->getValue<float>() : 1.0f;
return result;
}
}
// Given a property node from a pass, get its value either from it or
// from the effect parameters.
const SGPropertyNode* getEffectPropertyNode(Effect* effect,
const SGPropertyNode* prop)
{
if (!prop)
return 0;
if (prop->nChildren() > 0) {
const SGPropertyNode* useProp = prop->getChild("use");
if (!useProp || !effect->parametersProp)
return prop;
return effect->parametersProp->getNode(useProp->getStringValue());
}
return prop;
}
// Get a named child property from pass parameters or effect
// parameters.
const SGPropertyNode* getEffectPropertyChild(Effect* effect,
const SGPropertyNode* prop,
const char* name)
{
const SGPropertyNode* child = prop->getChild(name);
if (!child)
return 0;
else
return getEffectPropertyNode(effect, child);
}
struct LightingBuilder : public PassAttributeBuilder
{
void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
const osgDB::ReaderWriter::Options* options);
};
void LightingBuilder::buildAttribute(Effect* effect, Pass* pass,
const SGPropertyNode* prop,
const osgDB::ReaderWriter::Options* options)
{
const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop);
if (!realProp)
return;
pass->setMode(GL_LIGHTING, (realProp->getValue<bool>() ? StateAttribute::ON
: StateAttribute::OFF));
}
InstallAttributeBuilder<LightingBuilder> installLighting("lighting");
struct ShadeModelBuilder : public PassAttributeBuilder
{
void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
const osgDB::ReaderWriter::Options* options)
{
const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop);
if (!realProp)
return;
StateAttributeFactory *attrFact = StateAttributeFactory::instance();
string propVal = realProp->getStringValue();
if (propVal == "flat")
pass->setAttribute(attrFact->getFlatShadeModel());
else if (propVal == "smooth")
pass->setAttribute(attrFact->getSmoothShadeModel());
else
SG_LOG(SG_INPUT, SG_ALERT,
"invalid shade model property " << propVal);
}
};
InstallAttributeBuilder<ShadeModelBuilder> installShadeModel("shade-model");
struct CullFaceBuilder : PassAttributeBuilder
{
void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
const osgDB::ReaderWriter::Options* options)
{
const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop);
if (!realProp)
return;
StateAttributeFactory *attrFact = StateAttributeFactory::instance();
string propVal = realProp->getStringValue();
if (propVal == "front")
pass->setAttributeAndModes(attrFact->getCullFaceFront());
else if (propVal == "back")
pass->setAttributeAndModes(attrFact->getCullFaceBack());
else if (propVal == "front-back")
pass->setAttributeAndModes(new CullFace(CullFace::FRONT_AND_BACK));
else
SG_LOG(SG_INPUT, SG_ALERT,
"invalid cull face property " << propVal);
}
};
InstallAttributeBuilder<CullFaceBuilder> installCullFace("cull-face");
struct HintBuilder : public PassAttributeBuilder
{
void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
const osgDB::ReaderWriter::Options* options)
{
const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop);
if (!realProp)
return;
string propVal = realProp->getStringValue();
if (propVal == "opaque")
pass->setRenderingHint(StateSet::OPAQUE_BIN);
else if (propVal == "transparent")
pass->setRenderingHint(StateSet::TRANSPARENT_BIN);
}
};
InstallAttributeBuilder<HintBuilder> installHint("rendering-hint");
struct RenderBinBuilder : public PassAttributeBuilder
{
void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
const osgDB::ReaderWriter::Options* options)
{
const SGPropertyNode* binProp = prop->getChild("bin-number");
binProp = getEffectPropertyNode(effect, binProp);
const SGPropertyNode* nameProp = prop->getChild("bin-name");
nameProp = getEffectPropertyNode(effect, nameProp);
if (binProp && nameProp) {
pass->setRenderBinDetails(binProp->getIntValue(),
nameProp->getStringValue());
} else {
if (!binProp)
SG_LOG(SG_INPUT, SG_ALERT,
"No render bin number specified in render bin section");
if (!nameProp)
SG_LOG(SG_INPUT, SG_ALERT,
"No render bin name specified in render bin section");
}
}
};
InstallAttributeBuilder<RenderBinBuilder> installRenderBin("render-bin");
struct MaterialBuilder : public PassAttributeBuilder
{
void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
const osgDB::ReaderWriter::Options* options);
};
EffectNameValue<Material::ColorMode> colorModes[] =
{
{ "ambient", Material::AMBIENT },
{ "ambient-and-diffuse", Material::AMBIENT_AND_DIFFUSE },
{ "diffuse", Material::DIFFUSE },
{ "emissive", Material::EMISSION },
{ "specular", Material::SPECULAR },
{ "off", Material::OFF }
};
void MaterialBuilder::buildAttribute(Effect* effect, Pass* pass,
const SGPropertyNode* prop,
const osgDB::ReaderWriter::Options* options)
{
Material* mat = new Material;
const SGPropertyNode* color = 0;
if ((color = getEffectPropertyChild(effect, prop, "ambient")))
mat->setAmbient(Material::FRONT_AND_BACK, getColor(color));
if ((color = getEffectPropertyChild(effect, prop, "ambient-front")))
mat->setAmbient(Material::FRONT, getColor(color));
if ((color = getEffectPropertyChild(effect, prop, "ambient-back")))
mat->setAmbient(Material::BACK, getColor(color));
if ((color = getEffectPropertyChild(effect, prop, "diffuse")))
mat->setDiffuse(Material::FRONT_AND_BACK, getColor(color));
if ((color = getEffectPropertyChild(effect, prop, "diffuse-front")))
mat->setDiffuse(Material::FRONT, getColor(color));
if ((color = getEffectPropertyChild(effect, prop, "diffuse-back")))
mat->setDiffuse(Material::BACK, getColor(color));
if ((color = getEffectPropertyChild(effect, prop, "specular")))
mat->setSpecular(Material::FRONT_AND_BACK, getColor(color));
if ((color = getEffectPropertyChild(effect, prop, "specular-front")))
mat->setSpecular(Material::FRONT, getColor(color));
if ((color = getEffectPropertyChild(effect, prop, "specular-back")))
mat->setSpecular(Material::BACK, getColor(color));
if ((color = getEffectPropertyChild(effect, prop, "emissive")))
mat->setEmission(Material::FRONT_AND_BACK, getColor(color));
if ((color = getEffectPropertyChild(effect, prop, "emissive-front")))
mat->setEmission(Material::FRONT, getColor(color));
if ((color = getEffectPropertyChild(effect, prop, "emissive-back")))
mat->setEmission(Material::BACK, getColor(color));
const SGPropertyNode* shininess = 0;
if ((shininess = getEffectPropertyChild(effect, prop, "shininess")))
mat->setShininess(Material::FRONT_AND_BACK, shininess->getFloatValue());
if ((shininess = getEffectPropertyChild(effect, prop, "shininess-front")))
mat->setShininess(Material::FRONT, shininess->getFloatValue());
if ((shininess = getEffectPropertyChild(effect, prop, "shininess-back")))
mat->setShininess(Material::BACK, shininess->getFloatValue());
Material::ColorMode colorMode = Material::OFF;
findAttr(colorModes, getEffectPropertyChild(effect, prop, "color-mode"),
colorMode);
mat->setColorMode(colorMode);
pass->setAttribute(mat);
}
InstallAttributeBuilder<MaterialBuilder> installMaterial("material");
struct BlendBuilder : public PassAttributeBuilder
{
void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
const osgDB::ReaderWriter::Options* options)
{
const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop);
if (!realProp)
return;
pass->setMode(GL_BLEND, (realProp->getBoolValue()
? StateAttribute::ON
: StateAttribute::OFF));
}
};
InstallAttributeBuilder<BlendBuilder> installBlend("blend");
struct AlphaTestBuilder : public PassAttributeBuilder
{
void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
const osgDB::ReaderWriter::Options* options)
{
const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop);
if (!realProp)
return;
pass->setMode(GL_ALPHA_TEST, (realProp->getBoolValue()
? StateAttribute::ON
: StateAttribute::OFF));
}
};
InstallAttributeBuilder<AlphaTestBuilder> installAlphaTest("alpha-test");
EffectNameValue<Texture::FilterMode> filterModes[] =
{
{ "linear", Texture::LINEAR },
{ "linear-mipmap-linear", Texture::LINEAR_MIPMAP_LINEAR},
{ "linear-mipmap-nearest", Texture::LINEAR_MIPMAP_NEAREST},
{ "nearest", Texture::NEAREST},
{ "nearest-mipmap-linear", Texture::NEAREST_MIPMAP_LINEAR},
{ "nearest-mipmap-nearest", Texture::NEAREST_MIPMAP_NEAREST}
};
EffectNameValue<Texture::WrapMode> wrapModes[] =
{
{"clamp", Texture::CLAMP},
{"clamp-to-border", Texture::CLAMP_TO_BORDER},
{"clamp-to-edge", Texture::CLAMP_TO_EDGE},
{"mirror", Texture::MIRROR},
{"repeat", Texture::REPEAT}
};
EffectNameValue<TexEnv::Mode> texEnvModes[] =
{
{"add", TexEnv::ADD},
{"blend", TexEnv::BLEND},
{"decal", TexEnv::DECAL},
{"modulate", TexEnv::MODULATE},
{"replace", TexEnv::REPLACE}
};
TexEnv* buildTexEnv(Effect* effect, const SGPropertyNode* prop)
{
const SGPropertyNode* modeProp = getEffectPropertyChild(effect, prop,
"mode");
const SGPropertyNode* colorProp = getEffectPropertyChild(effect, prop,
"color");
if (!modeProp)
return 0;
TexEnv::Mode mode = TexEnv::MODULATE;
findAttr(texEnvModes, modeProp, mode);
if (mode == TexEnv::MODULATE) {
return StateAttributeFactory::instance()->getStandardTexEnv();
}
TexEnv* env = new TexEnv(mode);
if (colorProp)
env->setColor(colorProp->getValue<SGVec4d>().osg());
return env;
}
typedef boost::tuple<string, Texture::FilterMode, Texture::FilterMode,
Texture::WrapMode, Texture::WrapMode,
Texture::WrapMode> TexTuple;
typedef map<TexTuple, ref_ptr<Texture2D> > TexMap;
TexMap texMap;
struct TextureUnitBuilder : PassAttributeBuilder
{
void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
const osgDB::ReaderWriter::Options* options);
};
void TextureUnitBuilder::buildAttribute(Effect* effect, Pass* pass,
const SGPropertyNode* prop,
const osgDB::ReaderWriter::Options* options)
{
// First, all the texture properties
const SGPropertyNode* pTexture2d = prop->getChild("texture2d");
if (!pTexture2d)
return;
const SGPropertyNode* pImage
= getEffectPropertyChild(effect, pTexture2d, "image");
if (!pImage)
return;
const char* imageName = pImage->getStringValue();
Texture::FilterMode minFilter = Texture::LINEAR_MIPMAP_LINEAR;
findAttr(filterModes, getEffectPropertyChild(effect, pTexture2d, "filter"),
minFilter);
Texture::FilterMode magFilter = Texture::LINEAR;
findAttr(filterModes, getEffectPropertyChild(effect, pTexture2d,
"mag-filter"),
magFilter);
const SGPropertyNode* pWrapS
= getEffectPropertyChild(effect, pTexture2d, "wrap-s");
Texture::WrapMode sWrap = Texture::CLAMP;
findAttr(wrapModes, pWrapS, sWrap);
const SGPropertyNode* pWrapT
= getEffectPropertyChild(effect, pTexture2d, "wrap-t");
Texture::WrapMode tWrap = Texture::CLAMP;
findAttr(wrapModes, pWrapT, tWrap);
const SGPropertyNode* pWrapR
= getEffectPropertyChild(effect, pTexture2d, "wrap-r");
Texture::WrapMode rWrap = Texture::CLAMP;
findAttr(wrapModes, pWrapR, rWrap);
TexTuple tuple(imageName, minFilter, magFilter, sWrap, tWrap, rWrap);
TexMap::iterator texIter = texMap.find(tuple);
Texture2D* texture = 0;
if (texIter != texMap.end()) {
texture = texIter->second.get();
} else {
texture = new Texture2D;
osgDB::ReaderWriter::ReadResult result
= osgDB::Registry::instance()->readImage(imageName, options);
if (result.success()) {
osg::Image* image = result.getImage();
texture->setImage(image);
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);
}
texture->setMaxAnisotropy(SGSceneFeatures::instance()
->getTextureFilter());
} else {
SG_LOG(SG_INPUT, SG_ALERT, "failed to load effect texture file "
<< imageName);
}
// texture->setDataVariance(osg::Object::STATIC);
texture->setFilter(Texture::MIN_FILTER, minFilter);
texture->setFilter(Texture::MAG_FILTER, magFilter);
texture->setWrap(Texture::WRAP_S, sWrap);
texture->setWrap(Texture::WRAP_T, tWrap);
texture->setWrap(Texture::WRAP_R, rWrap);
texMap.insert(make_pair(tuple, texture));
}
// Decode the texture unit
int unit = 0;
const SGPropertyNode* pUnit = prop->getChild("unit");
if (pUnit) {
unit = pUnit->getValue<int>();
} else {
const SGPropertyNode* pName = prop->getChild("name");
if (pName)
try {
unit = boost::lexical_cast<int>(pName->getStringValue());
} catch (boost::bad_lexical_cast& lex) {
SG_LOG(SG_INPUT, SG_ALERT, "can't decode name as texture unit "
<< lex.what());
}
}
pass->setTextureAttributeAndModes(unit, texture);
const SGPropertyNode* envProp = prop->getChild("environment");
if (envProp) {
TexEnv* env = buildTexEnv(effect, envProp);
if (env)
pass->setTextureAttributeAndModes(unit, env);
}
}
InstallAttributeBuilder<TextureUnitBuilder> textureUnitBuilder("texture-unit");
typedef map<string, ref_ptr<Program> > ProgramMap;
ProgramMap programMap;
typedef map<string, ref_ptr<Shader> > ShaderMap;
ShaderMap shaderMap;
struct ShaderProgramBuilder : PassAttributeBuilder
{
void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
const osgDB::ReaderWriter::Options* options);
};
void ShaderProgramBuilder::buildAttribute(Effect* effect, Pass* pass,
const SGPropertyNode* prop,
const osgDB::ReaderWriter::Options*
options)
{
PropertyList pVertShaders = prop->getChildren("vertex-shader");
PropertyList pFragShaders = prop->getChildren("fragment-shader");
string programKey;
for (PropertyList::iterator itr = pVertShaders.begin(),
e = pVertShaders.end();
itr != e;
++itr)
{
programKey += (*itr)->getStringValue();
programKey += ";";
}
for (PropertyList::iterator itr = pFragShaders.begin(),
e = pFragShaders.end();
itr != e;
++itr)
{
programKey += (*itr)->getStringValue();
programKey += ";";
}
Program* program = 0;
ProgramMap::iterator pitr = programMap.find(programKey);
if (pitr != programMap.end()) {
program = pitr->second.get();
} else {
program = new Program;
// Add vertex shaders, then fragment shaders
PropertyList& pvec = pVertShaders;
Shader::Type stype = Shader::VERTEX;
for (int i = 0; i < 2; ++i) {
for (PropertyList::iterator nameItr = pvec.begin(), e = pvec.end();
nameItr != e;
++nameItr) {
string shaderName = (*nameItr)->getStringValue();
ShaderMap::iterator sitr = shaderMap.find(shaderName);
if (sitr != shaderMap.end()) {
program->addShader(sitr->second.get());
} else {
string fileName = osgDB::findDataFile(shaderName, options);
if (!fileName.empty()) {
ref_ptr<Shader> shader = new Shader(stype);
if (shader->loadShaderSourceFromFile(fileName)) {
shaderMap.insert(make_pair(shaderName, shader));
program->addShader(shader.get());
}
}
}
}
pvec = pFragShaders;
stype = Shader::FRAGMENT;
}
programMap.insert(make_pair(programKey, program));
}
pass->setAttributeAndModes(program);
}
// Not sure what to do with "name". At one point I wanted to use it to
// order the passes, but I do support render bin and stuff too...
struct NameBuilder : public PassAttributeBuilder
{
void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
const osgDB::ReaderWriter::Options* options)
{
// name can't use <use>
string name = prop->getStringValue();
if (!name.empty())
pass->setName(name);
}
};
InstallAttributeBuilder<NameBuilder> installName("name");
void buildTechnique(Effect* effect, const SGPropertyNode* prop,
const osgDB::ReaderWriter::Options* options)
{
Technique* tniq = new Technique;
effect->techniques.push_back(tniq);
const SGPropertyNode* predProp = prop->getChild("predicate");
if (!predProp) {
tniq->setAlwaysValid(true);
} else {
try {
expression::BindingLayout layout;
int contextLoc = layout.addBinding("__contextId", expression::INT);
SGExpressionb* validExp
= dynamic_cast<SGExpressionb*>(expression::read(predProp
->getChild(0)));
if (validExp)
tniq->setValidExpression(validExp, layout);
else
throw expression::ParseError("technique predicate is not a boolean expression");
}
catch (expression::ParseError& except)
{
SG_LOG(SG_INPUT, SG_ALERT,
"parsing technique predicate " << except.getMessage());
tniq->setAlwaysValid(false);
}
}
PropertyList passProps = prop->getChildren("pass");
for (PropertyList::iterator itr = passProps.begin(), e = passProps.end();
itr != e;
++itr) {
buildPass(effect, tniq, itr->ptr(), options);
}
}
// Walk the techniques property tree, building techniques and
// passes.
bool Effect::realizeTechniques(const osgDB::ReaderWriter::Options* options)
{
PropertyList tniqList = root->getChildren("technique");
for (PropertyList::iterator itr = tniqList.begin(), e = tniqList.end();
itr != e;
++itr)
buildTechnique(this, *itr, options);
}
bool Effect_writeLocalData(const Object& obj, osgDB::Output& fw)
{
const Effect& effect = static_cast<const Effect&>(obj);

View File

@ -1,26 +1,29 @@
// Copyright (C) 2008 Timothy Moore timoore@redhat.com
// Copyright (C) 2008 - 2009 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 library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library 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
// This library 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.
// Library 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_EFFECT_HXX
#define SIMGEAR_EFFECT_HXX 1
#include <vector>
#include <string>
#include <osg/Object>
#include <osgDB/ReaderWriter>
#include <simgear/props/props.hxx>
namespace osg
{
@ -47,11 +50,26 @@ public:
const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY);
osg::StateSet* getDefaultStateSet();
std::vector<osg::ref_ptr<Technique> > techniques;
SGPropertyNode_ptr root;
// Pointer to the parameters node, if it exists
SGPropertyNode_ptr parametersProp;
Technique* chooseTechnique(osg::RenderInfo* renderInfo);
virtual void resizeGLObjectBuffers(unsigned int maxSize);
virtual void releaseGLObjects(osg::State* state = 0) const;
/*
* Build the techniques from the effect properties.
*/
bool realizeTechniques(const osgDB::ReaderWriter::Options* options = 0);
protected:
~Effect();
};
Effect* makeEffect(const std::string& name,
bool realizeTechniques,
const osgDB::ReaderWriter::Options* options = 0);
Effect* makeEffect(SGPropertyNode* prop,
bool realizeTechniques,
const osgDB::ReaderWriter::Options* options = 0);
}
#endif

View File

@ -22,6 +22,7 @@ libsgmaterial_a_SOURCES = \
GLPredicate.cxx \
Pass.cxx \
Technique.cxx \
makeEffect.cxx \
mat.cxx \
matlib.cxx \
matmodel.cxx

View File

@ -1,5 +1,7 @@
#include "Pass.hxx"
#include <osgDB/Registry>
namespace simgear
{
@ -7,4 +9,16 @@ Pass::Pass(const Pass& rhs, const osg::CopyOp& copyop) :
osg::StateSet(rhs, copyop)
{
}
namespace
{
osgDB::RegisterDotOsgWrapperProxy PassProxy
(
new Pass,
"simgear::Pass",
"Object simgear::Pass StateSet ",
0,
0
);
}
}

View File

@ -15,6 +15,7 @@
#include <osgDB/Input>
#include <osgDB/ParameterOutput>
#include <simgear/props/props.hxx>
#include <simgear/structure/OSGUtils.hxx>
namespace simgear
@ -217,6 +218,14 @@ public:
}
};
Expression* glVersionParser(const SGPropertyNode* exp,
expression::Parser* parser)
{
return new GLVersionExpression();
}
expression::ExpParserRegistrar glVersionRegistrar("glversion", glVersionParser);
class ExtensionSupportedExpression
: public GeneralNaryExpression<bool, int>
{
@ -237,6 +246,18 @@ protected:
string _extString;
};
Expression* extensionSupportedParser(const SGPropertyNode* exp,
expression::Parser* parser)
{
if (exp->getType() == props::STRING
|| exp->getType() == props::UNSPECIFIED)
return new ExtensionSupportedExpression(exp->getStringValue());
throw expression::ParseError("extension-supported expression has wrong type");
}
expression::ExpParserRegistrar
extensionSupportedRegistrar("extension-supported", extensionSupportedParser);
void Technique::setGLExtensionsPred(float glVersion,
const std::vector<std::string>& extensions)
{
@ -246,9 +267,14 @@ void Technique::setGLExtensionsPred(float glVersion,
int contextLoc = layout.addBinding("__contextId", INT);
VariableExpression<int>* contextExp
= new VariableExpression<int>(contextLoc);
SGExpression<bool>* versionTest
= makePredicate<std::less_equal>(new SGConstExpression<float>(glVersion),
new GLVersionExpression);
#if 0
LessEqualExpression<float>* versionTest
= new LessEqualExpression<float>(new SGConstExpression<float>(glVersion),
new GLVersionExpression);
#endif
AndExpression* extensionsExp = 0;
for (vector<string>::const_iterator itr = extensions.begin(),
e = extensions.end();
@ -285,7 +311,7 @@ bool Technique_writeLocalData(const Object& obj, osgDB::Output& fw)
fw.indent() << "shadowingStateSet\n";
fw.writeObject(*tniq.getShadowingStateSet());
}
fw.indent() << "passes\n";
fw.indent() << "num_passes " << tniq.passes.size() << "\n";
BOOST_FOREACH(const ref_ptr<Pass>& pass, tniq.passes) {
fw.writeObject(*pass);
}

View File

@ -0,0 +1,190 @@
#include "Effect.hxx"
#include "Technique.hxx"
#include "Pass.hxx"
#include <algorithm>
#include <cstring>
#include <map>
#include <sstream>
#include <boost/lexical_cast.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/tuple/tuple_comparison.hpp>
#include <OpenThreads/ReentrantMutex>
#include <OpenThreads/ScopedLock>
#include <osg/Material>
#include <osg/Program>
#include <osg/Referenced>
#include <osg/Texture2D>
#include <osg/Vec4d>
#include <osgDB/FileUtils>
#include <osgDB/ReadFile>
#include <osgDB/Registry>
#include <simgear/debug/logstream.hxx>
#include <simgear/props/props_io.hxx>
#include <simgear/scene/util/SGSceneFeatures.hxx>
#include <simgear/structure/SGExpression.hxx>
namespace simgear
{
using namespace std;
using namespace osg;
typedef vector<const SGPropertyNode*> RawPropVector;
typedef map<const string, ref_ptr<Effect> > EffectMap;
namespace
{
EffectMap effectMap;
OpenThreads::ReentrantMutex effectMutex;
}
/** Merge two property trees, producing a new tree.
* If the nodes are both leaves, value comes from left leaf.
* Otherwise, The children are examined. If a left and right child are
* "identical," they are merged and the result placed in the children
* of the result. Otherwise the left children are placed after the
* right children in the result.
*
* Nodes are considered identical if:
* Their names are equal;
* Either they both have "name" children and their values are equal;
* or their indexes are equal.
*/
struct PropPredicate
: public unary_function<const SGPropertyNode*, bool>
{
PropPredicate(const SGPropertyNode* node_) : node(node_) {}
bool operator()(const SGPropertyNode* arg) const
{
if (strcmp(node->getName(), arg->getName()))
return false;
const SGPropertyNode* nodeName = node->getChild("name");
const SGPropertyNode* argName = arg->getChild("name");
if (nodeName && argName)
return !strcmp(nodeName->getStringValue(),
argName->getStringValue());
else if (!(nodeName || argName))
return node->getIndex() == arg->getIndex();
else
return false;
}
const SGPropertyNode* node;
};
void mergePropertyTrees(SGPropertyNode* resultNode,
const SGPropertyNode* left, const SGPropertyNode* right)
{
if (left->nChildren() == 0) {
copyProperties(left, resultNode);
return;
}
resultNode->setAttributes(right->getAttributes());
RawPropVector leftChildren;
for (int i = 0; i < left->nChildren(); ++i)
leftChildren.push_back(left->getChild(i));
// Maximum index of nodes (with same names) we've created.
map<string, int> nodeIndex;
// Merge identical nodes
for (int i = 0; i < right->nChildren(); ++i) {
const SGPropertyNode* node = right->getChild(i);
RawPropVector::iterator litr
= find_if(leftChildren.begin(), leftChildren.end(),
PropPredicate(node));
SGPropertyNode* newChild
= resultNode->getChild(node->getName(),
nodeIndex[node->getName()]++, true);
if (litr != leftChildren.end()) {
mergePropertyTrees(newChild, *litr, node);
leftChildren.erase(litr);
} else {
copyProperties(node, newChild);
}
}
for (RawPropVector::iterator itr = leftChildren.begin(),
e = leftChildren.end();
itr != e;
++itr) {
SGPropertyNode* newChild
= resultNode->getChild((*itr)->getName(),
nodeIndex[(*itr)->getName()]++, true);
copyProperties(*itr, newChild);
}
}
Effect* makeEffect(const string& name,
bool realizeTechniques,
const osgDB::ReaderWriter::Options* options)
{
OpenThreads::ScopedLock<OpenThreads::ReentrantMutex> lock(effectMutex);
EffectMap::iterator itr = effectMap.find(name);
if (itr != effectMap.end())
return itr->second.get();
string effectFileName(name);
effectFileName += ".eff";
string absFileName
= osgDB::Registry::instance()->findDataFile(effectFileName, options,
osgDB::CASE_SENSITIVE);
if (absFileName.empty())
return 0;
SGPropertyNode_ptr effectProps = new SGPropertyNode();
readProperties(absFileName, effectProps.ptr(), 0, true);
Effect* result = makeEffect(effectProps.ptr(), realizeTechniques, options);
if (result)
effectMap.insert(make_pair(name, result));
return result;
}
Effect* makeEffect(SGPropertyNode* prop,
bool realizeTechniques,
const osgDB::ReaderWriter::Options* options)
{
// Give default names to techniques and passes
vector<SGPropertyNode_ptr> techniques = prop->getChildren("technique");
for (int i = 0; i < techniques.size(); ++i) {
SGPropertyNode* tniqProp = techniques[i].ptr();
if (!tniqProp->hasChild("name"))
setValue(tniqProp->getChild("name", 0, true),
boost::lexical_cast<string>(i));
vector<SGPropertyNode_ptr> passes = tniqProp->getChildren("pass");
for (int j = 0; j < passes.size(); ++j) {
SGPropertyNode* passProp = passes[j].ptr();
if (!passProp->hasChild("name"))
setValue(passProp->getChild("name", 0, true),
boost::lexical_cast<string>(j));
vector<SGPropertyNode_ptr> texUnits
= passProp->getChildren("texture-unit");
for (int k = 0; k < texUnits.size(); ++k) {
SGPropertyNode* texUnitProp = texUnits[k].ptr();
if (!texUnitProp->hasChild("name"))
setValue(texUnitProp->getChild("name", 0, true),
boost::lexical_cast<string>(k));
}
}
}
Effect* effect = new Effect;
// Merge with the parent effect, if any
const SGPropertyNode* inheritProp = prop->getChild("inherits-from");
Effect* parent = 0;
if (inheritProp) {
parent = makeEffect(inheritProp->getStringValue(), realizeTechniques,
options);
effect->root = new SGPropertyNode;
mergePropertyTrees(effect->root, prop, parent->root);
effect->root->removeChild("inherits-from");
} else {
effect->root = prop;
}
effect->parametersProp = effect->root->getChild("parameters");
if (realizeTechniques)
effect->realizeTechniques(options);
return effect;
}
}

View File

@ -32,6 +32,7 @@
#include <vector>
#include<string>
#include <boost/foreach.hpp>
#include "mat.hxx"
#include <osg/CullFace>
@ -40,14 +41,17 @@
#include <osg/StateSet>
#include <osg/TexEnv>
#include <osg/Texture2D>
#include <osgDB/Options>
#include <osgDB/ReadFile>
#include <osgDB/Registry>
#include <osgDB/FileUtils>
#include <simgear/debug/logstream.hxx>
#include <simgear/misc/sg_path.hxx>
#include <simgear/misc/sgstream.hxx>
#include <simgear/props/props_io.hxx>
#include <simgear/scene/model/model.hxx>
#include <simgear/scene/util/RenderConstants.hxx>
#include <simgear/scene/util/StateAttributeFactory.hxx>
#include "Effect.hxx"
@ -55,6 +59,7 @@
#include "Pass.hxx"
using std::map;
using std::string;
using namespace simgear;
@ -62,33 +67,18 @@ using namespace simgear;
// Constructors and destructor.
////////////////////////////////////////////////////////////////////////
SGMaterial::_internal_state::_internal_state(osg::StateSet *s,
const std::string &t, bool l ) :
state(s), texture_path(t), texture_loaded(l)
SGMaterial::_internal_state::_internal_state(Effect *e, const string &t, bool l,
const osgDB::Options* o ) :
effect(e), texture_path(t), effect_realized(l), options(o)
{
}
SGMaterial::SGMaterial( const string &fg_root, const SGPropertyNode *props )
SGMaterial::SGMaterial( const osgDB::Options* options,
const SGPropertyNode *props )
{
init();
read_properties( fg_root, props );
build_state( false );
}
SGMaterial::SGMaterial( const string &texpath )
{
init();
_internal_state st( NULL, texpath, false );
_status.push_back( st );
build_state( true );
}
SGMaterial::SGMaterial( osg::StateSet *s )
{
init();
set_state( s );
read_properties( options, props );
buildEffectProperties(options);
}
SGMaterial::~SGMaterial (void)
@ -101,7 +91,8 @@ SGMaterial::~SGMaterial (void)
////////////////////////////////////////////////////////////////////////
void
SGMaterial::read_properties( const string &fg_root, const SGPropertyNode *props)
SGMaterial::read_properties(const osgDB::Options* options,
const SGPropertyNode *props)
{
// Gather the path(s) to the texture(s)
vector<SGPropertyNode_ptr> textures = props->getChildren("texture");
@ -111,29 +102,29 @@ SGMaterial::read_properties( const string &fg_root, const SGPropertyNode *props)
if (tname.empty()) {
tname = "unknown.rgb";
}
SGPath tpath( fg_root );
tpath.append("Textures.high");
SGPath tpath("Textures.high");
tpath.append(tname);
if ( !osgDB::fileExists(tpath.str()) ) {
tpath = SGPath( fg_root );
tpath.append("Textures");
osgDB::Registry* reg = osgDB::Registry::instance();
string fullTexPath = reg->findDataFile(tpath.str(), options,
osgDB::CASE_SENSITIVE);
if (fullTexPath.empty()) {
tpath = SGPath("Textures");
tpath.append(tname);
fullTexPath = reg->findDataFile(tpath.str(), options,
osgDB::CASE_SENSITIVE);
}
if ( osgDB::fileExists(tpath.str()) ) {
_internal_state st( NULL, tpath.str(), false );
if (!fullTexPath.empty() ) {
_internal_state st( NULL, fullTexPath, false, options );
_status.push_back( st );
}
}
if (textures.size() == 0) {
string tname = "unknown.rgb";
SGPath tpath( fg_root );
tpath.append("Textures");
SGPath tpath("Textures");
tpath.append("Terrain");
tpath.append(tname);
_internal_state st( NULL, tpath.str(), true );
tpath.append("unknown.rgb");
_internal_state st( NULL, tpath.str(), true, options );
_status.push_back( st );
}
@ -148,10 +139,12 @@ SGMaterial::read_properties( const string &fg_root, const SGPropertyNode *props)
tree_width = props->getDoubleValue("tree-width-m", 0.0);
tree_range = props->getDoubleValue("tree-range-m", 0.0);
tree_varieties = props->getIntValue("tree-varieties", 1);
SGPath tpath( fg_root );
tpath.append(props->getStringValue("tree-texture"));
tree_texture = tpath.str();
const SGPropertyNode* treeTexNode = props->getChild("tree-texture");
if (treeTexNode) {
string treeTexPath = props->getStringValue("tree-texture");
tree_texture = osgDB::Registry::instance()
->findDataFile(treeTexPath, options, osgDB::CASE_SENSITIVE);
}
// surface values for use with ground reactions
solid = props->getBoolValue("solid", true);
@ -238,10 +231,9 @@ Effect* SGMaterial::get_effect(int n)
return 0;
}
int i = n >= 0 ? n : _current_ptr;
if(!_status[i].texture_loaded) {
assignTexture(_status[i].state.get(), _status[i].texture_path,
wrapu, wrapv, mipmap);
_status[i].texture_loaded = true;
if(!_status[i].effect_realized) {
_status[i].effect->realizeTechniques(_status[i].options.get());
_status[i].effect_realized = true;
}
// XXX This business of returning a "random" alternate texture is
// really bogus. It means that the appearance of the terrain
@ -250,70 +242,42 @@ Effect* SGMaterial::get_effect(int n)
return _status[i].effect.get();
}
void
SGMaterial::build_state( bool defer_tex_load )
void SGMaterial::buildEffectProperties(const osgDB::Options* options)
{
StateAttributeFactory *attrFact = StateAttributeFactory::instance();
SGMaterialUserData* user = new SGMaterialUserData(this);
for (unsigned int i = 0; i < _status.size(); i++)
{
Pass *pass = new Pass;
pass->setUserData(user);
// Set up the textured state
pass->setAttribute(attrFact->getSmoothShadeModel());
pass->setAttributeAndModes(attrFact->getCullFaceBack());
pass->setMode(GL_LIGHTING, osg::StateAttribute::ON);
_status[i].texture_loaded = false;
osg::Material* material = new osg::Material;
material->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE);
material->setAmbient(osg::Material::FRONT_AND_BACK, ambient.osg());
material->setDiffuse(osg::Material::FRONT_AND_BACK, diffuse.osg());
material->setSpecular(osg::Material::FRONT_AND_BACK, specular.osg());
material->setEmission(osg::Material::FRONT_AND_BACK, emission.osg());
material->setShininess(osg::Material::FRONT_AND_BACK, shininess );
pass->setAttribute(material);
using namespace osg;
SGPropertyNode_ptr propRoot = new SGPropertyNode();
makeChild(propRoot, "inherits-from")
->setStringValue("Effects/terrain-default");
SGPropertyNode* paramProp = makeChild(propRoot, "parameters");
SGPropertyNode* materialProp = makeChild(paramProp, "material");
makeChild(materialProp, "ambient")->setValue(SGVec4d(ambient));
makeChild(materialProp, "diffuse")->setValue(SGVec4d(diffuse));
makeChild(materialProp, "specular")->setValue(SGVec4d(specular));
makeChild(materialProp, "emissive")->setValue(SGVec4d(emission));
makeChild(materialProp, "shininess")->setFloatValue(shininess);
if (ambient[3] < 1 || diffuse[3] < 1 ||
specular[3] < 1 || emission[3] < 1) {
pass->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
pass->setMode(GL_BLEND, osg::StateAttribute::ON);
pass->setMode(GL_ALPHA_TEST, osg::StateAttribute::ON);
} else {
pass->setRenderingHint(osg::StateSet::OPAQUE_BIN);
pass->setMode(GL_BLEND, osg::StateAttribute::OFF);
pass->setMode(GL_ALPHA_TEST, osg::StateAttribute::OFF);
makeChild(paramProp, "transparent")->setBoolValue(true);
SGPropertyNode* binProp = makeChild(paramProp, "render-bin");
makeChild(binProp, "bin-number")->setIntValue(TRANSPARENT_BIN);
makeChild(binProp, "bin-name")->setStringValue("DepthSortedBin");
}
_status[i].state = pass;
Technique* tniq = new Technique(true);
tniq->passes.push_back(pass);
Effect* effect = new Effect;
effect->techniques.push_back(tniq);
effect->setUserData(user);
_status[i].effect = effect;
}
}
void SGMaterial::set_state( osg::StateSet *s )
BOOST_FOREACH(_internal_state& matState, _status)
{
_status.push_back( _internal_state( s, "", true ) );
SGPropertyNode_ptr effectProp = new SGPropertyNode();
copyProperties(propRoot, effectProp);
SGPropertyNode* effectParamProp = effectProp->getChild("parameters", 0);
SGPropertyNode* texProp = makeChild(effectParamProp, "texture");
SGPropertyNode* tex2dProp = makeChild(texProp, "texture2d");
makeChild(tex2dProp, "image")->setStringValue(matState.texture_path);
makeChild(tex2dProp, "filter")
->setStringValue(mipmap ? "linear-mipmap-linear" : "nearest");
makeChild(tex2dProp, "wrap-s")
->setStringValue(wrapu ? "repeat" : "clamp");
makeChild(tex2dProp, "wrap-t")
->setStringValue(wrapv ? "repeat" : "clamp");
matState.effect = makeEffect(effectProp, false, options);
}
void SGMaterial::assignTexture( osg::StateSet *state, const std::string &fname,
bool _wrapu, bool _wrapv, bool _mipmap )
{
osg::Texture2D* texture = SGLoadTexture2D(fname, 0, _wrapu, _wrapv,
mipmap ? -1 : 0);
texture->setMaxAnisotropy( SGGetTextureFilter());
state->setTextureAttributeAndModes(0, texture);
StateAttributeFactory *attrFact = StateAttributeFactory::instance();
state->setTextureAttributeAndModes(0, attrFact->getStandardTexEnv());
}
SGMaterialGlyph* SGMaterial::get_glyph (const string& name) const

View File

@ -45,6 +45,11 @@ namespace osg
class StateSet;
}
namespace osgDB
{
class Options;
}
#include <simgear/props/props.hxx>
#include <simgear/structure/SGSharedPtr.hxx>
#include <simgear/scene/util/SGSceneFeatures.hxx>
@ -83,28 +88,7 @@ public:
* state information for the material. This node is usually
* loaded from the $FG_ROOT/materials.xml file.
*/
SGMaterial( const std::string &fg_root, const SGPropertyNode *props);
/**
* Construct a material from an absolute texture path.
*
* @param texture_path A string containing an absolute path
* to a texture file (usually RGB).
*/
SGMaterial( const std::string &texpath );
/**
* Construct a material around an existing state.
*
* This constructor allows the application to create a custom,
* low-level state for the scene graph and wrap a material around
* it. Note: the pointer ownership is transferred to the material.
*
* @param s The state for this material.
*/
SGMaterial( osg::StateSet *s );
SGMaterial( const osgDB::Options*, const SGPropertyNode *props);
/**
* Destructor.
@ -275,11 +259,12 @@ protected:
protected:
struct _internal_state {
_internal_state( osg::StateSet *s, const std::string &t, bool l );
osg::ref_ptr<osg::StateSet> state;
_internal_state(simgear::Effect *e, const std::string &t, bool l,
const osgDB::Options *o);
osg::ref_ptr<simgear::Effect> effect;
std::string texture_path;
bool texture_loaded;
bool effect_realized;
osg::ref_ptr<const osgDB::Options> options;
};
private:
@ -356,14 +341,9 @@ private:
// Internal constructors and methods.
////////////////////////////////////////////////////////////////////
SGMaterial( const std::string &fg_root, const SGMaterial &mat ); // unimplemented
void read_properties( const std::string &fg_root, const SGPropertyNode *props );
void build_state( bool defer_tex_load );
void set_state( osg::StateSet *s );
void assignTexture( osg::StateSet *state, const std::string &fname, bool _wrapu = true, bool _wrapv = true, bool _mipmap = true );
void read_properties(const osgDB::Options* options,
const SGPropertyNode *props);
void buildEffectProperties(const osgDB::Options* options);
};

View File

@ -36,18 +36,7 @@
#include <string.h>
#include <string>
#include <osg/AlphaFunc>
#include <osg/BlendFunc>
#include <osg/CullFace>
#include <osg/Material>
#include <osg/Point>
#include <osg/PointSprite>
#include <osg/PolygonMode>
#include <osg/PolygonOffset>
#include <osg/StateSet>
#include <osg/TexEnv>
#include <osg/TexGen>
#include <osg/Texture2D>
#include <osgDB/Options>
#include <simgear/debug/logstream.hxx>
#include <simgear/misc/sg_path.hxx>
@ -82,7 +71,9 @@ bool SGMaterialLib::load( const string &fg_root, const string& mpath,
<< ex.getMessage() );
throw;
}
osg::ref_ptr<osgDB::Options> options = new osgDB::Options;
options->setObjectCacheHint(osgDB::Options::CACHE_ALL);
options->setDatabasePath(fg_root);
int nMaterials = materials.nChildren();
for (int i = 0; i < nMaterials; i++) {
const SGPropertyNode *node = materials.getChild(i);
@ -97,7 +88,7 @@ bool SGMaterialLib::load( const string &fg_root, const string& mpath,
}
}
SGSharedPtr<SGMaterial> m = new SGMaterial(fg_root, node);
SGSharedPtr<SGMaterial> m = new SGMaterial(options.get(), node);
vector<SGPropertyNode_ptr>names = node->getChildren("name");
for ( unsigned int j = 0; j < names.size(); j++ ) {