]> git.mxchange.org Git - simgear.git/blobdiff - simgear/scene/model/SGLightAnimation.cxx
Work around apparent OSG 3.2.0 normal binding bug.
[simgear.git] / simgear / scene / model / SGLightAnimation.cxx
index bad681c31485bfe84196af7446da41099fd4ec2b..b049c15d2a13e8f925a5d741f8158a4b272effe4 100644 (file)
 #  include <simgear_config.h>
 #endif
 
+#include <boost/lexical_cast.hpp>
+
 #include "animation.hxx"
 #include "ConditionNode.hxx"
 
 #include <osg/Geometry>
 #include <osg/MatrixTransform>
+#include <simgear/props/vectorPropTemplates.hxx>
 #include <simgear/scene/material/EffectGeode.hxx>
+#include <simgear/scene/material/Technique.hxx>
+#include <simgear/scene/material/Pass.hxx>
+#include <simgear/scene/util/CopyOp.hxx>
+#include <simgear/scene/util/OsgMath.hxx>
+#include <simgear/scene/util/SGReaderWriterOptions.hxx>
 #include <boost/scoped_array.hpp>
+#include <boost/foreach.hpp>
+
+typedef std::map<std::string, osg::observer_ptr<simgear::Effect> > EffectMap;
+static EffectMap lightEffectMap;
+
+#define GET_COLOR_VALUE(n) \
+    SGVec4d( getConfig()->getDoubleValue(n "/r"), \
+                getConfig()->getDoubleValue(n "/g"), \
+                getConfig()->getDoubleValue(n "/b"), \
+                getConfig()->getDoubleValue(n "/a") )
+
+class SGLightAnimation::UpdateCallback : public osg::NodeCallback {
+public:
+    UpdateCallback(const SGExpressiond* v, SGVec4d a, SGVec4d d, SGVec4d s) :
+        _ambient(a),
+        _diffuse(d),
+        _specular(s),
+        _animationValue(v)
+    {
+        _prev_value = -1;
+    }
+    virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
+    {
+        double dim = _animationValue->getValue();
+        if (dim != _prev_value) {
+            _prev_value = dim;
+            simgear::EffectGeode* geode = dynamic_cast<simgear::EffectGeode*>( node );
+            if (geode != 0) {
+                osg::ref_ptr<simgear::Effect> effect = geode->getEffect();
+
+                SGPropertyNode* params = effect->parametersProp;
+                params->getNode("ambient")->setValue(_ambient * dim);
+                params->getNode("diffuse")->setValue(_diffuse * dim);
+                params->getNode("specular")->setValue(_specular * dim);
+                BOOST_FOREACH(osg::ref_ptr<simgear::Technique>& technique, effect->techniques) {
+                    BOOST_FOREACH(osg::ref_ptr<simgear::Pass>& pass, technique->passes) {
+                        osg::Uniform* amb = pass->getUniform("Ambient");
+                        if (amb)
+                            amb->set(osg::Vec4f(_ambient.x() * dim, _ambient.y() * dim, _ambient.z() * dim, _ambient.w() * dim));
+                        osg::Uniform* dif = pass->getUniform("Diffuse");
+                        if (dif)
+                            dif->set(osg::Vec4f(_diffuse.x() * dim, _diffuse.y() * dim, _diffuse.z() * dim, _diffuse.w() * dim));
+                        osg::Uniform* spe = pass->getUniform("Specular");
+                        if (spe)
+                            spe->set(osg::Vec4f(_specular.x() * dim, _specular.y() * dim, _specular.z() * dim, _specular.w() * dim));
+                    }
+                }
+            }
+        }
+        traverse(node, nv);
+    }
+public:
+    SGVec4d _ambient;
+    SGVec4d _diffuse;
+    SGVec4d _specular;
+    SGSharedPtr<SGExpressiond const> _animationValue;
+    double _prev_value;
+};
 
 SGLightAnimation::SGLightAnimation(const SGPropertyNode* configNode,
-                                   SGPropertyNode* modelRoot) :
-    SGAnimation(configNode, modelRoot)
+                                   SGPropertyNode* modelRoot,
+                                   const osgDB::Options* options,
+                                   const std::string &path, int i) :
+    SGAnimation(configNode, modelRoot),
+    _options(options)
 {
+    _light_type = getConfig()->getStringValue("light-type");
+    _position = SGVec3d( getConfig()->getDoubleValue("position/x"), getConfig()->getDoubleValue("position/y"), getConfig()->getDoubleValue("position/z") );
+    _direction = SGVec3d( getConfig()->getDoubleValue("direction/x"), getConfig()->getDoubleValue("direction/y"), getConfig()->getDoubleValue("direction/z") );
+    double l = length(_direction);
+    if (l > 0.001) _direction /= l;
+    _ambient = GET_COLOR_VALUE("ambient");
+    _diffuse = GET_COLOR_VALUE("diffuse");
+    _specular = GET_COLOR_VALUE("specular");
+    _attenuation = SGVec3d( getConfig()->getDoubleValue("attenuation/c"), getConfig()->getDoubleValue("attenuation/l"), getConfig()->getDoubleValue("attenuation/q") );
+    _exponent = getConfig()->getDoubleValue("exponent");
+    _cutoff = getConfig()->getDoubleValue("cutoff");
+    _near = getConfig()->getDoubleValue("near-m");
+    _far = getConfig()->getDoubleValue("far-m");
+    _key = path + ";" + boost::lexical_cast<std::string>( i );
+
+    SGConstPropertyNode_ptr dim_factor = configNode->getChild("dim-factor");
+    if (dim_factor.valid()) {
+        _animationValue = read_value(dim_factor, modelRoot, "", 0, 1);
+    }
 }
 
 osg::Group*
 SGLightAnimation::createAnimationGroup(osg::Group& parent)
 {
-    osg::MatrixTransform* grp = new osg::MatrixTransform;
-    osg::Vec3 position( getConfig()->getDoubleValue("position/x-m"),
-                        getConfig()->getDoubleValue("position/y-m"),
-                        getConfig()->getDoubleValue("position/z-m") );
-    grp->setMatrix(osg::Matrix::translate(position));
-  parent.addChild(grp);
-  grp->setNodeMask( simgear::MODELLIGHT_BIT );
-  return grp;
+    osg::Group* grp = new osg::Group;
+    grp->setName("light animation node");
+    parent.addChild(grp);
+    grp->setNodeMask( simgear::MODELLIGHT_BIT );
+    return grp;
 }
 
 void
 SGLightAnimation::install(osg::Node& node)
 {
-  SGAnimation::install(node);
-
-  std::string light_type = getConfig()->getStringValue("light-type");
-  if (light_type == "spot") {
-    osg::Vec3 axis( getConfig()->getDoubleValue("axis/x"),
-                    getConfig()->getDoubleValue("axis/y"),
-                    getConfig()->getDoubleValue("axis/z") );
-    double l = axis.length();
-    if (l < 0.001) return;
-    axis /= l;
-
-    osg::Vec3 p1(  axis.z(), axis.x(), axis.y() ),
-              p2 = axis ^ p1;
-    p1 = p2 ^ axis;
-
-    float n = getConfig()->getFloatValue("near-m"),
-          f = getConfig()->getFloatValue("far-m"),
-          a1 = getConfig()->getFloatValue("angle-inner-deg"),
-          a2 = getConfig()->getFloatValue("angle-outer-deg"),
-          r1 = n * tan( a2 * SG_DEGREES_TO_RADIANS ),
-          r2 = f * tan( a2 * SG_DEGREES_TO_RADIANS );
-
-    osg::Geometry* cone = new osg::Geometry;
-cone->setUseDisplayList(false);
-    osg::Vec3Array* vertices = new osg::Vec3Array(36);
-    (*vertices)[0] = osg::Vec3(0.0,0.0,0.0);
-    for (int i=0; i<16; ++i) {
-      (*vertices)[16-i]    = axis*f + p1*r2*cos( i * 2 * M_PI / 16 ) + p2*r2*sin( i * 2 * M_PI / 16 );
+    SGAnimation::install(node);
+
+    bool cacheEffect = false;
+    osg::ref_ptr<simgear::Effect> effect;
+    EffectMap::iterator iter = lightEffectMap.end();
+    if (!_animationValue.valid()) { // Effects with animated properties should be singletons
+        iter = lightEffectMap.find(_key);
+        cacheEffect = true;
     }
-    (*vertices)[17] = (*vertices)[1];
 
-    // Bottom
-    (*vertices)[18] = axis*f;
-    for (int i=0; i<16; ++i) {
-      (*vertices)[19+i]    = axis*f + p1*r2*cos( i * 2 * M_PI / 16 ) + p2*r2*sin( i * 2 * M_PI / 16 );
+    if (iter == lightEffectMap.end() || !iter->second.lock(effect)) {
+        SGPropertyNode_ptr effectProp = new SGPropertyNode;
+        if (_light_type == "spot") {
+            makeChild(effectProp, "name")->setStringValue(_key);
+            makeChild(effectProp, "inherits-from")->setStringValue("Effects/light-spot");
+            double dim = 1.0;
+            if (_animationValue.valid())
+                dim = _animationValue->getValue();
+
+            SGPropertyNode* params = makeChild(effectProp, "parameters");
+            params->getNode("position",true)->setValue(SGVec4d(_position.x(),_position.y(),_position.z(),1.0));
+            params->getNode("direction",true)->setValue(SGVec4d(_direction.x(),_direction.y(),_direction.z(),0.0));
+            params->getNode("ambient",true)->setValue(_ambient * dim);
+            params->getNode("diffuse",true)->setValue(_diffuse * dim);
+            params->getNode("specular",true)->setValue(_specular * dim);
+            params->getNode("attenuation",true)->setValue(_attenuation);
+            params->getNode("exponent",true)->setValue(_exponent);
+            params->getNode("cutoff",true)->setValue(_cutoff);
+            params->getNode("cosCutoff",true)->setValue( cos(_cutoff*SG_DEGREES_TO_RADIANS) );
+            params->getNode("near",true)->setValue(_near);
+            params->getNode("far",true)->setValue(_far);
+        }
+        else if (_light_type == "point") {
+            makeChild(effectProp, "name")->setStringValue(_key);
+            makeChild(effectProp, "inherits-from")->setStringValue("Effects/light-point");
+            double dim = 1.0;
+            if (_animationValue.valid())
+                dim = _animationValue->getValue();
+
+            SGPropertyNode* params = makeChild(effectProp, "parameters");
+            params->getNode("position",true)->setValue(SGVec4d(_position.x(),_position.y(),_position.z(),1.0));
+            params->getNode("ambient",true)->setValue(_ambient * dim);
+            params->getNode("diffuse",true)->setValue(_diffuse * dim);
+            params->getNode("specular",true)->setValue(_specular * dim);
+            params->getNode("attenuation",true)->setValue(_attenuation);
+            params->getNode("near",true)->setValue(_near);
+            params->getNode("far",true)->setValue(_far);
+        } else {
+            return;
+        }
+
+        effect = simgear::makeEffect(effectProp, true, dynamic_cast<const simgear::SGReaderWriterOptions*>(_options.get()));
+        if (iter == lightEffectMap.end() && cacheEffect)
+            lightEffectMap.insert(EffectMap::value_type(_key, effect));
+        else if (cacheEffect)
+            iter->second = effect;
+    } else {
+        effect = iter->second.get();
     }
-    (*vertices)[35] = (*vertices)[19];
-
-    osg::Vec4Array* colours = new osg::Vec4Array(1);
-    (*colours)[0].set(  getConfig()->getFloatValue("color/red"),
-                        getConfig()->getFloatValue("color/green"),
-                        getConfig()->getFloatValue("color/blue"),
-                        1.0f);
-    cone->setColorArray(colours);
-    cone->setColorBinding(osg::Geometry::BIND_OVERALL);
-
-    osg::Vec3Array* normals = new osg::Vec3Array(1);
-    (*normals)[0] = axis;
-    cone->setNormalArray(normals);
-    cone->setNormalBinding(osg::Geometry::BIND_OVERALL);
-
-    cone->setVertexArray(vertices);
-    cone->addPrimitiveSet( new osg::DrawArrays( GL_TRIANGLE_FAN, 0, 18 ) );
-    cone->addPrimitiveSet( new osg::DrawArrays( GL_TRIANGLE_FAN, 18, 18 ) );
-
-    simgear::EffectGeode* geode = new simgear::EffectGeode;
-    geode->addDrawable( cone );
-
-    node.asGroup()->addChild( geode );
-    simgear::Effect *effect = simgear::makeEffect("Effects/light-spot", true);
-    if (effect) {
-      effect->parametersProp->setFloatValue("inner-angle",getConfig()->getFloatValue("angle-inner-deg"));
-      effect->parametersProp->setFloatValue("outer-angle",getConfig()->getFloatValue("angle-outer-deg"));
-      geode->setEffect(effect);
+
+    node.setNodeMask( simgear::MODELLIGHT_BIT );
+    simgear::EffectGeode* geode = dynamic_cast<simgear::EffectGeode*>(&node);
+    if (geode == 0) {
+        osg::Group* grp = node.asGroup();
+        if (grp != 0) {
+            for (size_t i=0; i<grp->getNumChildren(); ++i) {
+                geode = dynamic_cast<simgear::EffectGeode*>(grp->getChild(i));
+                if (geode) {
+                    geode->setNodeMask( simgear::MODELLIGHT_BIT );
+                    geode->setEffect(effect);
+                }
+            }
+        }
     }
-  }
+
+    if (geode != 0 && _animationValue.valid())
+        geode->setUpdateCallback(new UpdateCallback(_animationValue, _ambient, _diffuse, _specular));
 }