+// 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"
#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;
}
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),
{
}
+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);
--- /dev/null
+#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;
+}
+
+}
#include <vector>
#include<string>
+#include <boost/foreach.hpp>
#include "mat.hxx"
#include <osg/CullFace>
#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"
#include "Pass.hxx"
using std::map;
+using std::string;
using namespace simgear;
\f
// 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)
////////////////////////////////////////////////////////////////////////
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");
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 );
}
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);
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
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++)
+ 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)
{
- 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;
+ 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
{
map<string, SGSharedPtr<SGMaterialGlyph> >::const_iterator it;