]> git.mxchange.org Git - simgear.git/blobdiff - simgear/scene/model/animation.cxx
Improved tile cache priority scheme.
[simgear.git] / simgear / scene / model / animation.cxx
index 96057e80ef40c13cb565ebf8b879a92dbafa6f88..489419c24b78dc2d6642bd0e62da28448dd196fc 100644 (file)
 #include <osg/Texture2D>
 #include <osg/Transform>
 #include <osgDB/ReadFile>
+#include <osgDB/Registry>
+#include <osgDB/Input>
+#include <osgDB/ParameterOutput>
+
 
 #include <simgear/math/interpolater.hxx>
 #include <simgear/props/condition.hxx>
 #include <simgear/props/props.hxx>
 #include <simgear/structure/SGBinding.hxx>
+#include <simgear/scene/material/EffectGeode.hxx>
 #include <simgear/scene/util/SGNodeMasks.hxx>
 #include <simgear/scene/util/SGSceneUserData.hxx>
 #include <simgear/scene/util/SGStateAttributeVisitor.hxx>
@@ -46,6 +51,9 @@
 #include "SGMaterialAnimation.hxx"
 #include "SGRotateTransform.hxx"
 #include "SGScaleTransform.hxx"
+#include "SGInteractionAnimation.hxx"
+
+#include "ConditionNode.hxx"
 
 using OpenThreads::Mutex;
 using OpenThreads::ReentrantMutex;
@@ -149,11 +157,11 @@ public:
   void setOffset(double offset)
   { _offset = offset; }
 
-  virtual void eval(double& value) const
+  virtual void eval(double& value, const simgear::expression::Binding* b) const
   {
     _offset.shuffle();
     _scale.shuffle();
-    value = _offset + _scale*getOperand()->getValue();
+    value = _offset + _scale*getOperand()->getValue(b);
   }
 
   virtual bool isConst() const { return false; }
@@ -346,7 +354,7 @@ struct DoDrawArraysVisitor : public osg::NodeVisitor {
         using namespace osg;
         using namespace std;
 
-        for (int i = 0; i < geode.getNumDrawables(); ++i)
+        for (int i = 0; i < (int)geode.getNumDrawables(); ++i)
             geode.getDrawable(i)->setUseDisplayList(false);
     }
 };
@@ -401,6 +409,9 @@ SGAnimation::animate(osg::Node* node, const SGPropertyNode* configNode,
   } else if (type == "flash") {
     SGFlashAnimation animInst(configNode, modelRoot);
     animInst.apply(node);
+  } else if (type == "interaction") {
+    SGInteractionAnimation animInst(configNode, modelRoot);
+    animInst.apply(node);
   } else if (type == "material") {
     SGMaterialAnimation animInst(configNode, modelRoot, options);
     animInst.apply(node);
@@ -655,9 +666,20 @@ SGTranslateAnimation::SGTranslateAnimation(const SGPropertyNode* configNode,
   else
     _initialValue = 0;
 
-  _axis[0] = configNode->getDoubleValue("axis/x", 0);
-  _axis[1] = configNode->getDoubleValue("axis/y", 0);
-  _axis[2] = configNode->getDoubleValue("axis/z", 0);
+  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);
+    _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);
 }
@@ -956,6 +978,15 @@ osg::StateSet* getNormalizeStateSet()
 
 class SGDistScaleAnimation::Transform : public osg::Transform {
 public:
+  Transform() : _min_v(0.0), _max_v(0.0), _factor(0.0), _offset(0.0) {}
+  Transform(const Transform& rhs,
+            const osg::CopyOp& copyOp = osg::CopyOp::SHALLOW_COPY)
+    : osg::Transform(rhs, copyOp), _table(rhs._table), _center(rhs._center),
+      _min_v(rhs._min_v), _max_v(rhs._max_v), _factor(rhs._factor),
+      _offset(rhs._offset)
+  {
+  }
+  META_Node(simgear, SGDistScaleAnimation::Transform);
   Transform(const SGPropertyNode* configNode)
   {
     setName(configNode->getStringValue("name", "dist scale animation"));
@@ -1003,13 +1034,23 @@ public:
     return true;
   }
 
+  static bool writeLocalData(const osg::Object& obj, osgDB::Output& fw)
+  {
+    const Transform& trans = static_cast<const Transform&>(obj);
+    fw.indent() << "center " << trans._center << "\n";
+    fw.indent() << "min_v " << trans._min_v << "\n";
+    fw.indent() << "max_v " << trans._max_v << "\n";
+    fw.indent() << "factor " << trans._factor << "\n";
+    fw.indent() << "offset " << trans._offset << "\n";
+    return true;
+  }
 private:
   double computeScaleFactor(osg::NodeVisitor* nv) const
   {
     if (!nv)
       return 1;
 
-    double scale_factor = (_center.osg() - nv->getEyePoint()).length();
+    double scale_factor = (toOsg(_center) - nv->getEyePoint()).length();
     if (_table == 0) {
       scale_factor = _factor * scale_factor + _offset;
     } else {
@@ -1046,6 +1087,17 @@ SGDistScaleAnimation::createAnimationGroup(osg::Group& parent)
   return transform;
 }
 
+namespace
+{
+  osgDB::RegisterDotOsgWrapperProxy distScaleAnimationTransformProxy
+  (
+   new SGDistScaleAnimation::Transform,
+   "SGDistScaleAnimation::Transform",
+   "Object Node Transform SGDistScaleAnimation::Transform Group",
+   0,
+   &SGDistScaleAnimation::Transform::writeLocalData
+   );
+}
 \f
 ////////////////////////////////////////////////////////////////////////
 // Implementation of flash animation
@@ -1053,6 +1105,19 @@ SGDistScaleAnimation::createAnimationGroup(osg::Group& parent)
 
 class SGFlashAnimation::Transform : public osg::Transform {
 public:
+  Transform() : _power(0.0), _factor(0.0), _offset(0.0), _min_v(0.0),
+                _max_v(0.0), _two_sides(false)
+  {}
+
+  Transform(const Transform& rhs,
+            const osg::CopyOp& copyOp = osg::CopyOp::SHALLOW_COPY)
+    : osg::Transform(rhs, copyOp), _center(rhs._center), _axis(rhs._axis),
+      _power(rhs._power), _factor(rhs._factor), _offset(rhs._offset),
+      _min_v(rhs._min_v), _max_v(rhs._max_v), _two_sides(rhs._two_sides)
+  {
+  }
+  META_Node(simgear, SGFlashAnimation::Transform);
+
   Transform(const SGPropertyNode* configNode)
   {
     setReferenceFrame(RELATIVE_RF);
@@ -1109,6 +1174,21 @@ public:
     return true;
   }
 
+  static bool writeLocalData(const osg::Object& obj, osgDB::Output& fw)
+  {
+    const Transform& trans = static_cast<const Transform&>(obj);
+    fw.indent() << "center " << trans._center[0] << " "
+                << trans._center[1] << " " << trans._center[2] << " " << "\n";
+    fw.indent() << "axis " << trans._axis[0] << " "
+                << trans._axis[1] << " " << trans._axis[2] << " " << "\n";
+    fw.indent() << "power " << trans._power << " \n";
+    fw.indent() << "min_v " << trans._min_v << "\n";
+    fw.indent() << "max_v " << trans._max_v << "\n";
+    fw.indent() << "factor " << trans._factor << "\n";
+    fw.indent() << "offset " << trans._offset << "\n";
+    fw.indent() << "twosides " << (trans._two_sides ? "true" : "false") << "\n";
+    return true;
+  }
 private:
   double computeScaleFactor(osg::NodeVisitor* nv) const
   {
@@ -1163,13 +1243,29 @@ SGFlashAnimation::createAnimationGroup(osg::Group& parent)
   return transform;
 }
 
+namespace
+{
+  osgDB::RegisterDotOsgWrapperProxy flashAnimationTransformProxy
+  (
+   new SGFlashAnimation::Transform,
+   "SGFlashAnimation::Transform",
+   "Object Node Transform SGFlashAnimation::Transform Group",
+   0,
+   &SGFlashAnimation::Transform::writeLocalData
+   );
+}
 \f
 ////////////////////////////////////////////////////////////////////////
-// Implementation of flash animation
+// Implementation of billboard animation
 ////////////////////////////////////////////////////////////////////////
 
 class SGBillboardAnimation::Transform : public osg::Transform {
 public:
+  Transform() : _spherical(true) {}
+  Transform(const Transform& rhs,
+            const osg::CopyOp& copyOp = osg::CopyOp::SHALLOW_COPY)
+    : osg::Transform(rhs, copyOp), _spherical(rhs._spherical) {}
+  META_Node(simgear, SGBillboardAnimation::Transform);
   Transform(const SGPropertyNode* configNode) :
     _spherical(configNode->getBoolValue("spherical", true))
   {
@@ -1206,7 +1302,13 @@ public:
     // Hmm, don't yet know how to get that back ...
     return false;
   }
+  static bool writeLocalData(const osg::Object& obj, osgDB::Output& fw)
+  {
+    const Transform& trans = static_cast<const Transform&>(obj);
 
+    fw.indent() << (trans._spherical ? "true" : "false") << "\n";
+    return true;
+  }
 private:
   bool _spherical;
 };
@@ -1226,6 +1328,17 @@ SGBillboardAnimation::createAnimationGroup(osg::Group& parent)
   return transform;
 }
 
+namespace
+{
+  osgDB::RegisterDotOsgWrapperProxy billboardAnimationTransformProxy
+  (
+   new SGBillboardAnimation::Transform,
+   "SGBillboardAnimation::Transform",
+   "Object Node Transform SGBillboardAnimation::Transform Group",
+   0,
+   &SGBillboardAnimation::Transform::writeLocalData
+   );
+}
 \f
 ////////////////////////////////////////////////////////////////////////
 // Implementation of a range animation
@@ -1334,25 +1447,6 @@ SGRangeAnimation::createAnimationGroup(osg::Group& parent)
 // Implementation of a select animation
 ////////////////////////////////////////////////////////////////////////
 
-class SGSelectAnimation::UpdateCallback : public osg::NodeCallback {
-public:
-  UpdateCallback(const SGCondition* condition) :
-    _condition(condition)
-  {}
-  virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
-  {
-    osg::Switch* sw = static_cast<osg::Switch*>(node);
-    if (_condition->test())
-      sw->setAllChildrenOn();
-    else
-      sw->setAllChildrenOff();
-    traverse(node, nv);
-  }
-
-private:
-  SGSharedPtr<SGCondition const> _condition;
-};
-
 SGSelectAnimation::SGSelectAnimation(const SGPropertyNode* configNode,
                                      SGPropertyNode* modelRoot) :
   SGAnimation(configNode, modelRoot)
@@ -1368,12 +1462,13 @@ SGSelectAnimation::createAnimationGroup(osg::Group& parent)
   // when the animation installer returns
   if (!condition)
     return new osg::Group;
-
-  osg::Switch* sw = new osg::Switch;
-  sw->setName("select animation node");
-  sw->setUpdateCallback(new UpdateCallback(condition));
-  parent.addChild(sw);
-  return sw;
+  simgear::ConditionNode* cn = new simgear::ConditionNode;
+  cn->setName("select animation node");
+  cn->setCondition(condition.ptr());
+  osg::Group* grp = new osg::Group;
+  cn->addChild(grp);
+  parent.addChild(cn);
+  return grp;
 }
 
 
@@ -1961,12 +2056,16 @@ class SGPickAnimation::PickCallback : public SGPickCallback {
 public:
   PickCallback(const SGPropertyNode* configNode,
                SGPropertyNode* modelRoot) :
-    _button(configNode->getIntValue("button", -1)),
     _repeatable(configNode->getBoolValue("repeatable", false)),
     _repeatInterval(configNode->getDoubleValue("interval-sec", 0.1))
   {
     SG_LOG(SG_INPUT, SG_DEBUG, "Reading all bindings");
     std::vector<SGPropertyNode_ptr> bindings;
+
+    bindings = configNode->getChildren("button");
+    for (unsigned int i = 0; i < bindings.size(); ++i) {
+      _buttons.push_back( bindings[i]->getIntValue() );
+    }
     bindings = configNode->getChildren("binding");
     for (unsigned int i = 0; i < bindings.size(); ++i) {
       _bindingsDown.push_back(new SGBinding(bindings[i], modelRoot));
@@ -1982,12 +2081,19 @@ public:
   }
   virtual bool buttonPressed(int button, const Info&)
   {
-    if (0 <= _button && button != _button)
+    bool found = false;
+    for( std::vector<int>::iterator it = _buttons.begin(); it != _buttons.end(); it++ ) {
+      if( *it == button ) {
+        found = true;
+        break;
+      }
+    }
+    if (!found )
       return false;
     SGBindingList::const_iterator i;
     for (i = _bindingsDown.begin(); i != _bindingsDown.end(); ++i)
       (*i)->fire();
-    _repeatTime = 0;
+    _repeatTime = -_repeatInterval;    // anti-bobble: delay start of repeat
     return true;
   }
   virtual void buttonReleased(void)
@@ -2012,12 +2118,142 @@ public:
 private:
   SGBindingList _bindingsDown;
   SGBindingList _bindingsUp;
-  int _button;
+  std::vector<int> _buttons;
   bool _repeatable;
   double _repeatInterval;
   double _repeatTime;
 };
 
+class VncVisitor : public osg::NodeVisitor {
+ public:
+  VncVisitor(double x, double y, int mask) :
+    osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
+    _texX(x), _texY(y), _mask(mask), _done(false)
+  {
+    SG_LOG(SG_INPUT, SG_DEBUG, "VncVisitor constructor "
+      << x << "," << y << " mask " << mask);
+  }
+
+  virtual void apply(osg::Node &node)
+  {
+    // Some nodes have state sets attached
+    touchStateSet(node.getStateSet());
+    if (!_done)
+      traverse(node);
+    if (_done) return;
+    // See whether we are a geode worth exploring
+    osg::Geode *g = dynamic_cast<osg::Geode*>(&node);
+    if (!g) return;
+    // Go find all its drawables
+    int i = g->getNumDrawables();
+    while (--i >= 0) {
+      osg::Drawable *d = g->getDrawable(i);
+      if (d) touchDrawable(*d);
+    }
+    // Out of optimism, do the same for EffectGeode
+    simgear::EffectGeode *eg = dynamic_cast<simgear::EffectGeode*>(&node);
+    if (!eg) return;
+    for (simgear::EffectGeode::DrawablesIterator di = eg->drawablesBegin();
+         di != eg->drawablesEnd(); di++) {
+      touchDrawable(**di);
+    }
+    // Now see whether the EffectGeode has an Effect
+    simgear::Effect *e = eg->getEffect();
+    if (e) {
+      touchStateSet(e->getDefaultStateSet());
+    }
+  }
+
+  inline void touchDrawable(osg::Drawable &d)
+  {
+    osg::StateSet *ss = d.getStateSet();
+    touchStateSet(ss);
+  }
+
+  void touchStateSet(osg::StateSet *ss)
+  {
+    if (!ss) return;
+    osg::StateAttribute *sa = ss->getTextureAttribute(0,
+      osg::StateAttribute::TEXTURE);
+    if (!sa) return;
+    osg::Texture *t = sa->asTexture();
+    if (!t) return;
+    osg::Image *img = t->getImage(0);
+    if (!img) return;
+    if (!_done) {
+      int pixX = _texX * img->s();
+      int pixY = _texY * img->t();
+      _done = img->sendPointerEvent(pixX, pixY, _mask);
+      SG_LOG(SG_INPUT, SG_DEBUG, "VncVisitor image said " << _done
+        << " to coord " << pixX << "," << pixY);
+    }
+  }
+
+  inline bool wasSuccessful()
+  {
+    return _done;
+  }
+
+ private:
+  double _texX, _texY;
+  int _mask;
+  bool _done;
+};
+
+
+class SGPickAnimation::VncCallback : public SGPickCallback {
+public:
+  VncCallback(const SGPropertyNode* configNode,
+               SGPropertyNode* modelRoot,
+               osg::Group *node)
+      : _node(node)
+  {
+    SG_LOG(SG_INPUT, SG_DEBUG, "Configuring VNC callback");
+    const char *cornernames[3] = {"top-left", "top-right", "bottom-left"};
+    SGVec3d *cornercoords[3] = {&_topLeft, &_toRight, &_toDown};
+    for (int c =0; c < 3; c++) {
+      const SGPropertyNode* cornerNode = configNode->getChild(cornernames[c]);
+      *cornercoords[c] = SGVec3d(
+        cornerNode->getDoubleValue("x"),
+        cornerNode->getDoubleValue("y"),
+        cornerNode->getDoubleValue("z"));
+    }
+    _toRight -= _topLeft;
+    _toDown -= _topLeft;
+    _squaredRight = dot(_toRight, _toRight);
+    _squaredDown = dot(_toDown, _toDown);
+  }
+
+  virtual bool buttonPressed(int button, const Info& info)
+  {
+    SGVec3d loc(info.local);
+    SG_LOG(SG_INPUT, SG_DEBUG, "VNC pressed " << button << ": " << loc);
+    loc -= _topLeft;
+    _x = dot(loc, _toRight) / _squaredRight;
+    _y = dot(loc, _toDown) / _squaredDown;
+    if (_x<0) _x = 0; else if (_x > 1) _x = 1;
+    if (_y<0) _y = 0; else if (_y > 1) _y = 1;
+    VncVisitor vv(_x, _y, 1 << button);
+    _node->accept(vv);
+    return vv.wasSuccessful();
+
+  }
+  virtual void buttonReleased(void)
+  {
+    SG_LOG(SG_INPUT, SG_DEBUG, "VNC release");
+    VncVisitor vv(_x, _y, 0);
+    _node->accept(vv);
+  }
+  virtual void update(double dt)
+  {
+  }
+private:
+  double _x, _y;
+  osg::ref_ptr<osg::Group> _node;
+  SGVec3d _topLeft, _toRight, _toDown;
+  double _squaredRight, _squaredDown;
+};
+
 SGPickAnimation::SGPickAnimation(const SGPropertyNode* configNode,
                                  SGPropertyNode* modelRoot) :
   SGAnimation(configNode, modelRoot)
@@ -2041,10 +2277,17 @@ SGPickAnimation::createAnimationGroup(osg::Group& parent)
   highlightGroup->addChild(commonGroup);
   SGSceneUserData* ud;
   ud = SGSceneUserData::getOrCreateSceneUserData(commonGroup);
+
+  // add actions that become macro and command invocations
   std::vector<SGPropertyNode_ptr> actions;
   actions = getConfig()->getChildren("action");
   for (unsigned int i = 0; i < actions.size(); ++i)
     ud->addPickCallback(new PickCallback(actions[i], getModelRoot()));
+  // Look for the VNC sessions that want raw mouse input
+  actions = getConfig()->getChildren("vncaction");
+  for (unsigned int i = 0; i < actions.size(); ++i)
+    ud->addPickCallback(new VncCallback(actions[i], getModelRoot(),
+      &parent));
 
   // prepare a state set that paints the edges of this object yellow
   osg::StateSet* stateSet = highlightGroup->getOrCreateStateSet();