]> git.mxchange.org Git - simgear.git/commitdiff
Initial prototype of knob animation.
authorJames Turner <zakalawe@mac.com>
Thu, 31 Jan 2013 00:15:09 +0000 (00:15 +0000)
committerJames Turner <zakalawe@mac.com>
Thu, 31 Jan 2013 00:15:09 +0000 (00:15 +0000)
simgear/scene/model/SGPickAnimation.cxx
simgear/scene/model/SGPickAnimation.hxx
simgear/scene/model/animation.cxx
simgear/scene/model/animation.hxx
simgear/structure/SGBinding.cxx
simgear/structure/SGBinding.hxx

index a282df192385f06cee018c97834141d8e21669a6..6b6e9c9afa37a2ef3fec4cb1bcc016dc9f33b5ee 100644 (file)
 #include <osg/PolygonMode>
 #include <osg/Material>
 
-#include <simgear/scene/material/EffectGeode.hxx>
 #include <simgear/scene/util/SGPickCallback.hxx>
+#include <simgear/scene/material/EffectGeode.hxx>
 #include <simgear/scene/util/SGSceneUserData.hxx>
 #include <simgear/structure/SGBinding.hxx>
 #include <simgear/scene/util/StateAttributeFactory.hxx>
+#include <simgear/scene/model/SGRotateTransform.hxx>
 
 using namespace simgear;
 
@@ -76,17 +77,14 @@ using OpenThreads::ScopedLock;
      }
      if (!found )
        return false;
-     SGBindingList::const_iterator i;
-     for (i = _bindingsDown.begin(); i != _bindingsDown.end(); ++i)
-       (*i)->fire();
+       
+     fireBindingList(_bindingsDown);
      _repeatTime = -_repeatInterval;    // anti-bobble: delay start of repeat
      return true;
    }
    virtual void buttonReleased(void)
    {
-     SGBindingList::const_iterator i;
-     for (i = _bindingsUp.begin(); i != _bindingsUp.end(); ++i)
-       (*i)->fire();
+       fireBindingList(_bindingsUp);
    }
    virtual void update(double dt)
    {
@@ -96,9 +94,7 @@ using OpenThreads::ScopedLock;
      _repeatTime += dt;
      while (_repeatInterval < _repeatTime) {
        _repeatTime -= _repeatInterval;
-       SGBindingList::const_iterator i;
-       for (i = _bindingsDown.begin(); i != _bindingsDown.end(); ++i)
-         (*i)->fire();
+         fireBindingList(_bindingsDown);
      }
    }
  private:
@@ -252,23 +248,81 @@ using OpenThreads::ScopedLock;
  osg::ref_ptr<osg::Uniform> colorModeUniform;
  }
 
+
+void 
+SGPickAnimation::innerSetupPickGroup(osg::Group* commonGroup, osg::Group& parent)
+{
+    // Contains the normal geometry that is interactive
+    osg::ref_ptr<osg::Group> normalGroup = new osg::Group;
+    normalGroup->setName("pick normal group");
+    normalGroup->addChild(commonGroup);
+    
+    // Used to render the geometry with just yellow edges
+    osg::Group* highlightGroup = new osg::Group;
+    highlightGroup->setName("pick highlight group");
+    highlightGroup->setNodeMask(simgear::PICK_BIT);
+    highlightGroup->addChild(commonGroup);
+    
+    // prepare a state set that paints the edges of this object yellow
+    // The material and texture attributes are set with
+    // OVERRIDE|PROTECTED in case there is a material animation on a
+    // higher node in the scene graph, which would have its material
+    // attribute set with OVERRIDE.
+    osg::StateSet* stateSet = highlightGroup->getOrCreateStateSet();
+    osg::Texture2D* white = StateAttributeFactory::instance()->getWhiteTexture();
+    stateSet->setTextureAttributeAndModes(0, white,
+                                          (osg::StateAttribute::ON
+                                           | osg::StateAttribute::OVERRIDE
+                                           | osg::StateAttribute::PROTECTED));
+    osg::PolygonOffset* polygonOffset = new osg::PolygonOffset;
+    polygonOffset->setFactor(-1);
+    polygonOffset->setUnits(-1);
+    stateSet->setAttribute(polygonOffset, osg::StateAttribute::OVERRIDE);
+    stateSet->setMode(GL_POLYGON_OFFSET_LINE,
+                      osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
+    osg::PolygonMode* polygonMode = new osg::PolygonMode;
+    polygonMode->setMode(osg::PolygonMode::FRONT_AND_BACK,
+                         osg::PolygonMode::LINE);
+    stateSet->setAttribute(polygonMode, osg::StateAttribute::OVERRIDE);
+    osg::Material* material = new osg::Material;
+    material->setColorMode(osg::Material::OFF);
+    material->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(0, 0, 0, 1));
+    // XXX Alpha < 1.0 in the diffuse material value is a signal to the
+    // default shader to take the alpha value from the material value
+    // and not the glColor. In many cases the pick animation geometry is
+    // transparent, so the outline would not be visible without this hack.
+    material->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(0, 0, 0, .95));
+    material->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(1, 1, 0, 1));
+    material->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0, 0, 0, 0));
+    stateSet->setAttribute(
+                           material, osg::StateAttribute::OVERRIDE | osg::StateAttribute::PROTECTED);
+    // The default shader has a colorMode uniform that mimics the
+    // behavior of Material color mode.
+    osg::Uniform* cmUniform = 0;
+    {
+        ScopedLock<Mutex> lock(colorModeUniformMutex);
+        if (!colorModeUniform.valid()) {
+            colorModeUniform = new osg::Uniform(osg::Uniform::INT, "colorMode");
+            colorModeUniform->set(0); // MODE_OFF
+            colorModeUniform->setDataVariance(osg::Object::STATIC);
+        }
+        cmUniform = colorModeUniform.get();
+    }
+    stateSet->addUniform(cmUniform,
+                         osg::StateAttribute::OVERRIDE | osg::StateAttribute::ON);
+    
+    // Only add normal geometry if configured
+    if (getConfig()->getBoolValue("visible", true))
+        parent.addChild(normalGroup.get());
+    parent.addChild(highlightGroup);
+}
+
  osg::Group*
  SGPickAnimation::createAnimationGroup(osg::Group& parent)
  {
-   osg::Group* commonGroup = new osg::Group;
-
-   // Contains the normal geometry that is interactive
-   osg::ref_ptr<osg::Group> normalGroup = new osg::Group;
-   normalGroup->setName("pick normal group");
-   normalGroup->addChild(commonGroup);
-
-   // Used to render the geometry with just yellow edges
-   osg::Group* highlightGroup = new osg::Group;
-   highlightGroup->setName("pick highlight group");
-   highlightGroup->setNodeMask(simgear::PICK_BIT);
-   highlightGroup->addChild(commonGroup);
-   SGSceneUserData* ud;
-   ud = SGSceneUserData::getOrCreateSceneUserData(commonGroup);
+     osg::Group* commonGroup = new osg::Group;
+     innerSetupPickGroup(commonGroup, parent);
+     SGSceneUserData* ud = SGSceneUserData::getOrCreateSceneUserData(commonGroup);
 
    // add actions that become macro and command invocations
    std::vector<SGPropertyNode_ptr> actions;
@@ -281,59 +335,137 @@ using OpenThreads::ScopedLock;
      ud->addPickCallback(new VncCallback(actions[i], getModelRoot(),
        &parent));
 
-   // prepare a state set that paints the edges of this object yellow
-   // The material and texture attributes are set with
-   // OVERRIDE|PROTECTED in case there is a material animation on a
-   // higher node in the scene graph, which would have its material
-   // attribute set with OVERRIDE.
-   osg::StateSet* stateSet = highlightGroup->getOrCreateStateSet();
-   osg::Texture2D* white = StateAttributeFactory::instance()->getWhiteTexture();
-   stateSet->setTextureAttributeAndModes(0, white,
-                                         (osg::StateAttribute::ON
-                                          | osg::StateAttribute::OVERRIDE
-                                          | osg::StateAttribute::PROTECTED));
-   osg::PolygonOffset* polygonOffset = new osg::PolygonOffset;
-   polygonOffset->setFactor(-1);
-   polygonOffset->setUnits(-1);
-   stateSet->setAttribute(polygonOffset, osg::StateAttribute::OVERRIDE);
-   stateSet->setMode(GL_POLYGON_OFFSET_LINE,
-                     osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
-   osg::PolygonMode* polygonMode = new osg::PolygonMode;
-   polygonMode->setMode(osg::PolygonMode::FRONT_AND_BACK,
-                        osg::PolygonMode::LINE);
-   stateSet->setAttribute(polygonMode, osg::StateAttribute::OVERRIDE);
-   osg::Material* material = new osg::Material;
-   material->setColorMode(osg::Material::OFF);
-   material->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(0, 0, 0, 1));
-   // XXX Alpha < 1.0 in the diffuse material value is a signal to the
-   // default shader to take the alpha value from the material value
-   // and not the glColor. In many cases the pick animation geometry is
-   // transparent, so the outline would not be visible without this hack.
-   material->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(0, 0, 0, .95));
-   material->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(1, 1, 0, 1));
-   material->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0, 0, 0, 0));
-   stateSet->setAttribute(
-       material, osg::StateAttribute::OVERRIDE | osg::StateAttribute::PROTECTED);
-   // The default shader has a colorMode uniform that mimics the
-   // behavior of Material color mode.
-   osg::Uniform* cmUniform = 0;
-   {
-       ScopedLock<Mutex> lock(colorModeUniformMutex);
-       if (!colorModeUniform.valid()) {
-           colorModeUniform = new osg::Uniform(osg::Uniform::INT, "colorMode");
-           colorModeUniform->set(0); // MODE_OFF
-           colorModeUniform->setDataVariance(osg::Object::STATIC);
-       }
-       cmUniform = colorModeUniform.get();
-   }
-   stateSet->addUniform(cmUniform,
-                        osg::StateAttribute::OVERRIDE | osg::StateAttribute::ON);
-   // Only add normal geometry if configured
-   if (getConfig()->getBoolValue("visible", true))
-     parent.addChild(normalGroup.get());
-   parent.addChild(highlightGroup);
-
-  
    return commonGroup;
 }
-  
\ No newline at end of file
+
+///////////////////////////////////////////////////////////////////////////
+
+class SGKnobAnimation::KnobPickCallback : public SGPickCallback {
+public:
+    KnobPickCallback(const SGPropertyNode* configNode,
+                 SGPropertyNode* modelRoot) :
+        _direction(DIRECTION_NONE),
+        _repeatInterval(configNode->getDoubleValue("interval-sec", 0.1))
+    {
+        const SGPropertyNode* act = configNode->getChild("action");
+        if (act)
+            _action = readBindingList(act->getChildren("binding"), modelRoot);
+        
+        const SGPropertyNode* cw = configNode->getChild("cw");
+        if (cw)
+            _bindingsCW = readBindingList(cw->getChildren("binding"), modelRoot);
+        
+        const SGPropertyNode* ccw = configNode->getChild("ccw");
+        if (ccw)
+            _bindingsCCW = readBindingList(ccw->getChildren("binding"), modelRoot);
+    }
+    
+    virtual bool buttonPressed(int button, const Info&)
+    {
+        _direction = DIRECTION_NONE;
+        if ((button == 0) || (button == 4)) {
+            _direction = DIRECTION_CLOCKWISE;
+        } else if ((button == 1) || (button == 3)) {
+            _direction = DIRECTION_ANTICLOCKWISE;
+        } else {
+            return false;
+        }
+        
+        _repeatTime = -_repeatInterval;    // anti-bobble: delay start of repeat
+        fire();
+        return true;
+    }
+    
+    virtual void buttonReleased(void)
+    {
+    }
+    
+    virtual void update(double dt)
+    {
+        _repeatTime += dt;
+        while (_repeatInterval < _repeatTime) {
+            _repeatTime -= _repeatInterval;
+            fire();
+        } // of repeat iteration
+    }
+private:
+    void fire()
+    {
+        switch (_direction) {
+            case DIRECTION_CLOCKWISE:
+                fireBindingListWithOffset(_action,  1, 1);
+                fireBindingList(_bindingsCW);
+                break;
+            case DIRECTION_ANTICLOCKWISE:
+                fireBindingListWithOffset(_action, -1, 1);
+                fireBindingList(_bindingsCCW);
+                break;
+            default: break;
+        }
+    }
+    
+    SGBindingList _action;
+    SGBindingList _bindingsCW,
+        _bindingsCCW;
+    
+    enum Direction
+    {
+        DIRECTION_NONE,
+        DIRECTION_CLOCKWISE,
+        DIRECTION_ANTICLOCKWISE
+    };
+    
+    Direction _direction;
+    double _repeatInterval;
+    double _repeatTime;
+};
+
+class SGKnobAnimation::UpdateCallback : public osg::NodeCallback {
+public:
+    UpdateCallback(SGExpressiond const* animationValue) :
+        _animationValue(animationValue)
+    {
+        setName("SGKnobAnimation::UpdateCallback");
+    }
+    virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
+    {
+        SGRotateTransform* transform = static_cast<SGRotateTransform*>(node);
+        transform->setAngleDeg(_animationValue->getValue());
+        traverse(node, nv);
+    }
+    
+private:
+    SGSharedPtr<SGExpressiond const> _animationValue;
+};
+
+
+SGKnobAnimation::SGKnobAnimation(const SGPropertyNode* configNode,
+                                 SGPropertyNode* modelRoot) :
+    SGPickAnimation(configNode, modelRoot)
+{
+    SGSharedPtr<SGExpressiond> value = read_value(configNode, modelRoot, "-deg",
+                                                  -SGLimitsd::max(), SGLimitsd::max());
+    _animationValue = value->simplify();
+    
+    
+    readRotationCenterAndAxis(configNode, _center, _axis);
+}
+
+
+osg::Group*
+SGKnobAnimation::createAnimationGroup(osg::Group& parent)
+{
+    SGRotateTransform* transform = new SGRotateTransform();
+    innerSetupPickGroup(transform, parent);
+    
+    UpdateCallback* uc = new UpdateCallback(_animationValue);
+    transform->setUpdateCallback(uc);
+    transform->setCenter(_center);
+    transform->setAxis(_axis);
+        
+    SGSceneUserData* ud = SGSceneUserData::getOrCreateSceneUserData(transform);
+    ud->setPickCallback(new KnobPickCallback(getConfig(), getModelRoot()));
+    
+    return transform;
+}
+
index c176180900e1852317410aa651e087d603aa2526..615fef046e024979298e0f0e2b24194d3f31d9eb 100644 (file)
@@ -24,6 +24,8 @@
 
 #include <simgear/scene/model/animation.hxx>
 
+// forward decls
+class SGPickCallback;
 
 //////////////////////////////////////////////////////////////////////
 // Pick animation
@@ -34,10 +36,32 @@ public:
   SGPickAnimation(const SGPropertyNode* configNode,
                   SGPropertyNode* modelRoot);
   virtual osg::Group* createAnimationGroup(osg::Group& parent);
+    
+    
+protected:
+    void innerSetupPickGroup(osg::Group* commonGroup, osg::Group& parent);
+    
 private:
   class PickCallback;
   class VncCallback;
 };
 
+
+class SGKnobAnimation : public SGPickAnimation
+{
+public:
+    SGKnobAnimation(const SGPropertyNode* configNode,
+                    SGPropertyNode* modelRoot);
+    virtual osg::Group* createAnimationGroup(osg::Group& parent);
+
+private:
+    class KnobPickCallback;
+    class UpdateCallback;
+    
+    SGVec3d _axis;
+    SGVec3d _center;
+    SGSharedPtr<SGExpressiond const> _animationValue;
+};
+
 #endif // of SG_SCENE_PICK_ANIMATION_HXX
 
index edc9e16c83b9cfb61aa98d140399eaa397cef662..3561f7ceb2fa6463c74e32151a1f9c418e3c1fbd 100644 (file)
@@ -396,6 +396,9 @@ SGAnimation::animate(osg::Node* node, const SGPropertyNode* configNode,
   } else if (type == "pick") {
     SGPickAnimation animInst(configNode, modelRoot);
     animInst.apply(node);
+  } else if (type == "knob") {
+    SGKnobAnimation animInst(configNode, modelRoot);
+    animInst.apply(node);
   } else if (type == "range") {
     SGRangeAnimation animInst(configNode, modelRoot);
     animInst.apply(node);
@@ -846,6 +849,33 @@ void SpinAnimCallback::operator()(osg::Node* node, osg::NodeVisitor* nv)
     }
 }
 
+// factored out to share with SGKnobAnimation
+void readRotationCenterAndAxis(const SGPropertyNode* configNode, SGVec3d& center, SGVec3d& axis)
+{
+    center = SGVec3d::zeros();
+    if (configNode->hasValue("axis/x1-m")) {
+        SGVec3d v1, v2;
+        v1[0] = configNode->getDoubleValue("axis/x1-m", 0);
+        v1[1] = configNode->getDoubleValue("axis/y1-m", 0);
+        v1[2] = configNode->getDoubleValue("axis/z1-m", 0);
+        v2[0] = configNode->getDoubleValue("axis/x2-m", 0);
+        v2[1] = configNode->getDoubleValue("axis/y2-m", 0);
+        v2[2] = configNode->getDoubleValue("axis/z2-m", 0);
+        center = 0.5*(v1+v2);
+        axis = v2 - v1;
+    } else {
+        axis[0] = configNode->getDoubleValue("axis/x", 0);
+        axis[1] = configNode->getDoubleValue("axis/y", 0);
+        axis[2] = configNode->getDoubleValue("axis/z", 0);
+    }
+    if (8*SGLimitsd::min() < norm(axis))
+        axis = normalize(axis);
+    
+    center[0] = configNode->getDoubleValue("center/x-m", center[0]);
+    center[1] = configNode->getDoubleValue("center/y-m", center[1]);
+    center[2] = configNode->getDoubleValue("center/z-m", center[2]);
+}
+
 SGRotateAnimation::SGRotateAnimation(const SGPropertyNode* configNode,
                                      SGPropertyNode* modelRoot) :
   SGAnimation(configNode, modelRoot)
@@ -862,28 +892,8 @@ SGRotateAnimation::SGRotateAnimation(const SGPropertyNode* configNode,
     _initialValue = _animationValue->getValue();
   else
     _initialValue = 0;
-  _center = SGVec3d::zeros();
-  if (configNode->hasValue("axis/x1-m")) {
-    SGVec3d v1, v2;
-    v1[0] = configNode->getDoubleValue("axis/x1-m", 0);
-    v1[1] = configNode->getDoubleValue("axis/y1-m", 0);
-    v1[2] = configNode->getDoubleValue("axis/z1-m", 0);
-    v2[0] = configNode->getDoubleValue("axis/x2-m", 0);
-    v2[1] = configNode->getDoubleValue("axis/y2-m", 0);
-    v2[2] = configNode->getDoubleValue("axis/z2-m", 0);
-    _center = 0.5*(v1+v2);
-    _axis = v2 - v1;
-  } else {
-    _axis[0] = configNode->getDoubleValue("axis/x", 0);
-    _axis[1] = configNode->getDoubleValue("axis/y", 0);
-    _axis[2] = configNode->getDoubleValue("axis/z", 0);
-  }
-  if (8*SGLimitsd::min() < norm(_axis))
-    _axis = normalize(_axis);
-
-  _center[0] = configNode->getDoubleValue("center/x-m", _center[0]);
-  _center[1] = configNode->getDoubleValue("center/y-m", _center[1]);
-  _center[2] = configNode->getDoubleValue("center/z-m", _center[2]);
+  
+  readRotationCenterAndAxis(configNode, _center, _axis);
 }
 
 osg::Group*
index 6c95bf3b6b9c68a3148f641fbc1a3ec43e535928..2bcf49ae57214020839b3963231fbb3496d4d477 100644 (file)
@@ -34,7 +34,9 @@
 SGExpressiond*
 read_value(const SGPropertyNode* configNode, SGPropertyNode* modelRoot,
            const char* unit, double defMin, double defMax);
-\f
+
+void readRotationCenterAndAxis(const SGPropertyNode* configNode, SGVec3d& center, SGVec3d& axis);
+
 //////////////////////////////////////////////////////////////////////
 // Base class for animation installers
 //////////////////////////////////////////////////////////////////////
index db501d4073de354b7740d07497a67edb2c25a3b3..43f55baf20b042beb23b8d054793c0a5a7ca0101 100644 (file)
@@ -114,3 +114,20 @@ void fireBindingList(const SGBindingList& aBindings)
         b->fire();
     }
 }
+
+void fireBindingListWithOffset(const SGBindingList& aBindings, double offset, double max)
+{
+    BOOST_FOREACH(SGBinding_ptr b, aBindings) {
+        b->fire(offset, max);
+    }
+}
+
+SGBindingList readBindingList(const simgear::PropertyList& aNodes, SGPropertyNode* aRoot)
+{
+    SGBindingList result;
+    BOOST_FOREACH(SGPropertyNode* node, aNodes) {
+        result.push_back(new SGBinding(node, aRoot));
+    }
+    
+    return result;
+}
index 23f38820ce2bc754980d77966682711a2adb1055..026da27078ac6af765c7105109fba62ef130fa3a 100644 (file)
@@ -136,4 +136,15 @@ typedef std::map<unsigned,SGBindingList> SGBindingMap;
  */
 void fireBindingList(const SGBindingList& aBindings);
 
+/**
+ * fire every binding in a list with a setting value
+ */
+void fireBindingListWithOffset(const SGBindingList& aBindings, double offset, double max);
+
+/**
+ * read multiple bindings from property-list format
+ */
+SGBindingList readBindingList(const simgear::PropertyList& aNodes, SGPropertyNode* aRoot);
+
 #endif