# include <simgear_config.h>
#endif
-#include <plib/ul.h>
+#include <map>
+
+#include <osg/Group>
+#include <osg/Program>
+#include <osg/Shader>
+#include <osg/StateSet>
+#include <osg/TextureCubeMap>
+#include <osg/TexEnvCombine>
+#include <osg/TexGen>
+#include <osg/Texture1D>
+#include <osgUtil/HighlightMapGenerator>
+
+#include <OpenThreads/Mutex>
+#include <OpenThreads/ScopedLock>
+
+#include <simgear/scene/util/SGUpdateVisitor.hxx>
#include <simgear/props/condition.hxx>
#include <simgear/props/props.hxx>
-#include <simgear/screen/extensions.hxx>
#include <simgear/debug/logstream.hxx>
#include "animation.hxx"
#include "model.hxx"
+
+using OpenThreads::Mutex;
+using OpenThreads::ScopedLock;
+
/*
<animation>
<type>shader</type>
</animation>
*/
+
+
+class SGMapGenCallback :
+ public osg::StateAttribute::Callback {
+public:
+ virtual void operator () (osg::StateAttribute* sa, osg::NodeVisitor* nv)
+ {
+ SGUpdateVisitor* updateVisitor = dynamic_cast<SGUpdateVisitor*>(nv);
+ if (!updateVisitor)
+ return;
+
+ if (distSqr(_lastLightDirection, updateVisitor->getLightDirection()) < 1e-4
+ && distSqr(_lastLightColor, updateVisitor->getAmbientLight()) < 1e-4)
+ return;
+
+ _lastLightDirection = updateVisitor->getLightDirection();
+ _lastLightColor = updateVisitor->getAmbientLight();
+
+ osg::TextureCubeMap *tcm = static_cast<osg::TextureCubeMap*>(sa);
+
+ // FIXME: need an update or callback ...
+ // generate the six highlight map images (light direction = [1, 1, -1])
+ osg::ref_ptr<osgUtil::HighlightMapGenerator> mapgen;
+ mapgen = new osgUtil::HighlightMapGenerator(toOsg(_lastLightDirection),
+ toOsg(_lastLightColor), 5);
+ mapgen->generateMap();
+
+ // assign the six images to the texture object
+ tcm->setImage(osg::TextureCubeMap::POSITIVE_X,
+ mapgen->getImage(osg::TextureCubeMap::POSITIVE_X));
+ tcm->setImage(osg::TextureCubeMap::NEGATIVE_X,
+ mapgen->getImage(osg::TextureCubeMap::NEGATIVE_X));
+ tcm->setImage(osg::TextureCubeMap::POSITIVE_Y,
+ mapgen->getImage(osg::TextureCubeMap::POSITIVE_Y));
+ tcm->setImage(osg::TextureCubeMap::NEGATIVE_Y,
+ mapgen->getImage(osg::TextureCubeMap::NEGATIVE_Y));
+ tcm->setImage(osg::TextureCubeMap::POSITIVE_Z,
+ mapgen->getImage(osg::TextureCubeMap::POSITIVE_Z));
+ tcm->setImage(osg::TextureCubeMap::NEGATIVE_Z,
+ mapgen->getImage(osg::TextureCubeMap::NEGATIVE_Z));
+ }
+private:
+ SGVec3f _lastLightDirection;
+ SGVec4f _lastLightColor;
+};
+
+static Mutex cubeMutex;
+
+#if 0
+static osg::TextureCubeMap*
+getOrCreateTextureCubeMap()
+{
+ static osg::ref_ptr<osg::TextureCubeMap> textureCubeMap;
+ if (textureCubeMap.get())
+ return textureCubeMap.get();
+
+ ScopedLock<Mutex> lock(cubeMutex);
+ if (textureCubeMap.get())
+ return textureCubeMap.get();
+
+ // create and setup the texture object
+ textureCubeMap = new osg::TextureCubeMap;
+
+ textureCubeMap->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP);
+ textureCubeMap->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP);
+ textureCubeMap->setWrap(osg::Texture::WRAP_R, osg::Texture::CLAMP);
+ textureCubeMap->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR_MIPMAP_LINEAR);
+ textureCubeMap->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
+
+ textureCubeMap->setUpdateCallback(new SGMapGenCallback);
+
+ return textureCubeMap.get();
+}
+
+static void create_specular_highlights(osg::Node *node)
+{
+ osg::StateSet *ss = node->getOrCreateStateSet();
+
+ // create and setup the texture object
+ osg::TextureCubeMap *tcm = getOrCreateTextureCubeMap();
+
+ // enable texturing, replacing any textures in the subgraphs
+ ss->setTextureAttributeAndModes(0, tcm, osg::StateAttribute::OVERRIDE | osg::StateAttribute::ON);
+
+ // texture coordinate generation
+ osg::TexGen *tg = new osg::TexGen;
+ tg->setMode(osg::TexGen::REFLECTION_MAP);
+ ss->setTextureAttributeAndModes(0, tg, osg::StateAttribute::OVERRIDE | osg::StateAttribute::ON);
+
+ // use TexEnvCombine to add the highlights to the original lighting
+ osg::TexEnvCombine *te = new osg::TexEnvCombine;
+ te->setCombine_RGB(osg::TexEnvCombine::ADD);
+ te->setSource0_RGB(osg::TexEnvCombine::TEXTURE);
+ te->setOperand0_RGB(osg::TexEnvCombine::SRC_COLOR);
+ te->setSource1_RGB(osg::TexEnvCombine::PRIMARY_COLOR);
+ te->setOperand1_RGB(osg::TexEnvCombine::SRC_COLOR);
+ ss->setTextureAttributeAndModes(0, te, osg::StateAttribute::OVERRIDE | osg::StateAttribute::ON);
+}
+#endif
+
+
+SGShaderAnimation::SGShaderAnimation(const SGPropertyNode* configNode,
+ SGPropertyNode* modelRoot,
+ const osgDB::ReaderWriter::Options*
+ options) :
+ SGAnimation(configNode, modelRoot)
+{
+ const SGPropertyNode* node = configNode->getChild("texture");
+ if (node)
+ _effect_texture = SGLoadTexture2D(node->getStringValue(), options);
+}
+
+namespace {
+class ChromeLightingCallback :
+ public osg::StateAttribute::Callback {
+public:
+ virtual void operator () (osg::StateAttribute* sa, osg::NodeVisitor* nv)
+ {
+ SGUpdateVisitor* updateVisitor = dynamic_cast<SGUpdateVisitor*>(nv);
+ if (!updateVisitor)
+ return;
+ osg::TexEnvCombine *combine = dynamic_cast<osg::TexEnvCombine *>(sa);
+ if (!combine)
+ return;
+ // An approximation for light reflected back by chrome.
+ osg::Vec4 globalColor = toOsg(updateVisitor->getAmbientLight() * .4f
+ + updateVisitor->getDiffuseLight());
+ globalColor.a() = 1.0f;
+ combine->setConstantColor(globalColor);
+ }
+};
+
+typedef std::map<osg::ref_ptr<osg::Texture2D>, osg::ref_ptr<osg::StateSet> >
+StateSetMap;
+}
+
+static Mutex chromeMutex;
+
+// The chrome effect is mixed by the alpha channel of the texture
+// on the model, which will be attached to a node lower in the scene
+// graph: 0 -> completely chrome, 1 -> completely model texture.
+static void create_chrome(osg::Group* group, osg::Texture2D* texture)
+{
+ ScopedLock<Mutex> lock(chromeMutex);
+ static StateSetMap chromeMap;
+ osg::StateSet *stateSet;
+ StateSetMap::iterator iterator = chromeMap.find(texture);
+ if (iterator != chromeMap.end()) {
+ stateSet = iterator->second.get();
+ } else {
+ stateSet = new osg::StateSet;
+ // If the model doesn't have any texture, we need to have one
+ // activated so that we don't need a seperate combiner
+ // attribute for the non-textured case. This texture will be
+ // shadowed by any attached to the model.
+ osg::Image *dummyImage = new osg::Image;
+ dummyImage->allocateImage(1, 1, 1, GL_LUMINANCE_ALPHA,
+ GL_UNSIGNED_BYTE);
+ unsigned char* imageBytes = dummyImage->data(0, 0);
+ imageBytes[0] = 255;
+ imageBytes[1] = 0;
+ osg::Texture2D* dummyTexture = new osg::Texture2D;
+ dummyTexture->setImage(dummyImage);
+ dummyTexture->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);
+ dummyTexture->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);
+ stateSet->setTextureAttributeAndModes(0, dummyTexture,
+ osg::StateAttribute::ON);
+ osg::TexEnvCombine* combine0 = new osg::TexEnvCombine;
+ osg::TexEnvCombine* combine1 = new osg::TexEnvCombine;
+ osg::TexGen* texGen = new osg::TexGen;
+ // Mix the environmental light color and the chrome map on texture
+ // unit 0
+ combine0->setCombine_RGB(osg::TexEnvCombine::MODULATE);
+ combine0->setSource0_RGB(osg::TexEnvCombine::CONSTANT);
+ combine0->setOperand0_RGB(osg::TexEnvCombine::SRC_COLOR);
+ combine0->setSource1_RGB(osg::TexEnvCombine::TEXTURE1);
+ combine0->setOperand1_RGB(osg::TexEnvCombine::SRC_COLOR);
+ combine0->setDataVariance(osg::Object::DYNAMIC);
+ combine0->setUpdateCallback(new ChromeLightingCallback);
+
+ // Interpolate the colored chrome map with the texture on the
+ // model, using the model texture's alpha.
+ combine1->setCombine_RGB(osg::TexEnvCombine::INTERPOLATE);
+ combine1->setSource0_RGB(osg::TexEnvCombine::TEXTURE0);
+ combine1->setOperand0_RGB(osg::TexEnvCombine::SRC_COLOR);
+ combine1->setSource1_RGB(osg::TexEnvCombine::PREVIOUS);
+ combine1->setOperand1_RGB(osg::TexEnvCombine::SRC_COLOR);
+ combine1->setSource2_RGB(osg::TexEnvCombine::TEXTURE0);
+ combine1->setOperand2_RGB(osg::TexEnvCombine::SRC_ALPHA);
+ // Are these used for anything?
+ combine1->setCombine_Alpha(osg::TexEnvCombine::REPLACE);
+ combine1->setSource0_Alpha(osg::TexEnvCombine::TEXTURE1);
+ combine1->setOperand0_Alpha(osg::TexEnvCombine::SRC_ALPHA);
+
+ texGen->setMode(osg::TexGen::SPHERE_MAP);
+ stateSet->setTextureAttribute(0, combine0);
+ stateSet->setTextureAttribute(1, combine1);
+ stateSet->setTextureAttributeAndModes(1, texture,
+ osg::StateAttribute::ON);
+ stateSet->setTextureAttributeAndModes(1, texGen,
+ osg::StateAttribute::ON);
+ chromeMap[texture] = stateSet;
+ }
+ group->setStateSet(stateSet);
+}
+
+osg::Group*
+SGShaderAnimation::createAnimationGroup(osg::Group& parent)
+{
+ osg::Group* group = new osg::Group;
+ group->setName("shader animation");
+ parent.addChild(group);
+
+ std::string shader_name = getConfig()->getStringValue("shader", "");
+// if( shader_name == "fresnel" || shader_name == "reflection" )
+// _shader_type = 1;
+// else if( shader_name == "heat-haze" )
+// _shader_type = 2;
+// else
+ if( shader_name == "chrome")
+#if 0
+ create_specular_highlights(group);
+#endif
+ create_chrome(group, _effect_texture.get());
+ return group;
+}
+
+#if 0
// static Shader *shFresnel=NULL;
// static GLuint texFresnel = 0;
// that the rest of cullbacks and the scene graph are traversed.
traverse(node, nv);
}
+#endif