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:
parent
c3b1802e95
commit
4219f16f61
@ -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);
|
||||
|
@ -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
|
||||
|
@ -22,6 +22,7 @@ libsgmaterial_a_SOURCES = \
|
||||
GLPredicate.cxx \
|
||||
Pass.cxx \
|
||||
Technique.cxx \
|
||||
makeEffect.cxx \
|
||||
mat.cxx \
|
||||
matlib.cxx \
|
||||
matmodel.cxx
|
||||
|
@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
190
simgear/scene/material/makeEffect.cxx
Normal file
190
simgear/scene/material/makeEffect.cxx
Normal 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;
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
_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;
|
||||
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) {
|
||||
makeChild(paramProp, "transparent")->setBoolValue(true);
|
||||
SGPropertyNode* binProp = makeChild(paramProp, "render-bin");
|
||||
makeChild(binProp, "bin-number")->setIntValue(TRANSPARENT_BIN);
|
||||
makeChild(binProp, "bin-name")->setStringValue("DepthSortedBin");
|
||||
}
|
||||
BOOST_FOREACH(_internal_state& matState, _status)
|
||||
{
|
||||
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::set_state( osg::StateSet *s )
|
||||
{
|
||||
_status.push_back( _internal_state( s, "", true ) );
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
||||
|
@ -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++ ) {
|
||||
|
Loading…
Reference in New Issue
Block a user