]> git.mxchange.org Git - simgear.git/blobdiff - simgear/scene/model/animation.cxx
Work around apparent OSG 3.2.0 normal binding bug.
[simgear.git] / simgear / scene / model / animation.cxx
index 25dcb20ae7b46aa82d9853e6c15d66ea815bb32f..30cab2e3d244b81efd720a9bbe0e736648200fac 100644 (file)
@@ -24,8 +24,6 @@
 #include <osg/LOD>
 #include <osg/Math>
 #include <osg/Object>
-#include <osg/PolygonMode>
-#include <osg/PolygonOffset>
 #include <osg/StateSet>
 #include <osg/Switch>
 #include <osg/TexMat>
@@ -41,9 +39,9 @@
 #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/material/EffectCullVisitor.hxx>
+#include <simgear/scene/util/DeletionManager.hxx>
 #include <simgear/scene/util/OsgMath.hxx>
 #include <simgear/scene/util/SGNodeMasks.hxx>
 #include <simgear/scene/util/SGSceneUserData.hxx>
@@ -58,6 +56,8 @@
 #include "SGRotateTransform.hxx"
 #include "SGScaleTransform.hxx"
 #include "SGInteractionAnimation.hxx"
+#include "SGPickAnimation.hxx"
+#include "SGTrackToAnimation.hxx"
 
 #include "ConditionNode.hxx"
 
@@ -66,7 +66,6 @@ using OpenThreads::ReentrantMutex;
 using OpenThreads::ScopedLock;
 
 using namespace simgear;
-\f
 ////////////////////////////////////////////////////////////////////////
 // Static utility functions.
 ////////////////////////////////////////////////////////////////////////
@@ -207,7 +206,6 @@ read_value(const SGPropertyNode* configNode, SGPropertyNode* modelRoot,
   return 0;
 }
 
-\f
 ////////////////////////////////////////////////////////////////////////
 // Animation installer
 ////////////////////////////////////////////////////////////////////////
@@ -346,7 +344,7 @@ SGAnimation::~SGAnimation()
   if (!_found)
   {
       std::list<std::string>::const_iterator i;
-      string info;
+      std::string info;
       for (i = _objectNames.begin(); i != _objectNames.end(); ++i)
       {
           if (!info.empty())
@@ -367,7 +365,7 @@ bool
 SGAnimation::animate(osg::Node* node, const SGPropertyNode* configNode,
                      SGPropertyNode* modelRoot,
                      const osgDB::Options* options,
-                     const string &path, int i)
+                     const std::string &path, int i)
 {
   std::string type = configNode->getStringValue("type", "none");
   if (type == "alpha-test") {
@@ -397,6 +395,12 @@ 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 == "slider") {
+    SGSliderAnimation animInst(configNode, modelRoot);
+    animInst.apply(node);
   } else if (type == "range") {
     SGRangeAnimation animInst(configNode, modelRoot);
     animInst.apply(node);
@@ -419,11 +423,14 @@ SGAnimation::animate(osg::Node* node, const SGPropertyNode* configNode,
   } else if (type == "timed") {
     SGTimedAnimation animInst(configNode, modelRoot);
     animInst.apply(node);
+  } else if (type == "locked-track") {
+    SGTrackToAnimation animInst(node, configNode, modelRoot);
+    animInst.apply(node);
   } else if (type == "translate") {
     SGTranslateAnimation animInst(configNode, modelRoot);
     animInst.apply(node);
   } else if (type == "light") {
-    SGLightAnimation animInst(configNode, modelRoot, path, i);
+    SGLightAnimation animInst(configNode, modelRoot, options, path, i);
     animInst.apply(node);
   } else if (type == "null" || type == "none" || type.empty()) {
     SGGroupAnimation animInst(configNode, modelRoot);
@@ -525,6 +532,65 @@ SGAnimation::installInGroup(const std::string& name, osg::Group& group,
   }
 }
 
+//------------------------------------------------------------------------------
+SGVec3d SGAnimation::readVec3( const std::string& name,
+                               const std::string& suffix,
+                               const SGVec3d& def ) const
+{
+  SGVec3d vec;
+  vec[0] = _configNode->getDoubleValue(name + "/x" + suffix, def.x());
+  vec[1] = _configNode->getDoubleValue(name + "/y" + suffix, def.y());
+  vec[2] = _configNode->getDoubleValue(name + "/z" + suffix, def.z());
+  return vec;
+}
+
+//------------------------------------------------------------------------------
+// factored out to share with SGKnobAnimation
+void SGAnimation::readRotationCenterAndAxis( SGVec3d& center,
+                                             SGVec3d& axis ) const
+{
+  center = SGVec3d::zeros();
+  if( _configNode->hasValue("axis/x1-m") )
+  {
+    SGVec3d v1 = readVec3("axis", "1-m"), // axis/[xyz]1-m
+            v2 = readVec3("axis", "2-m"); // axis/[xyz]2-m
+    center = 0.5*(v1+v2);
+    axis = v2 - v1;
+  }
+  else
+  {
+    axis = readVec3("axis");
+  }
+  if( 8 * SGLimitsd::min() < norm(axis) )
+    axis = normalize(axis);
+
+  center = readVec3("center", "-m", center);
+}
+
+//------------------------------------------------------------------------------
+SGExpressiond* SGAnimation::readOffsetValue(const char* tag_name) const
+{
+  const SGPropertyNode* node = _configNode->getChild(tag_name);
+  if( !node )
+    return 0;
+
+  SGExpressiond_ref expression;
+  if( !node->nChildren() )
+    expression = new SGConstExpression<double>(node->getDoubleValue());
+  else
+    expression = SGReadDoubleExpression(_modelRoot, node->getChild(0));
+
+  if( !expression )
+    return 0;
+
+  expression = expression->simplify();
+
+  if( expression->isConst() && expression->getValue() == 0 )
+    return 0;
+
+  return expression.release();
+}
+
 void
 SGAnimation::removeMode(osg::Node& node, osg::StateAttribute::GLMode mode)
 {
@@ -579,7 +645,7 @@ SGAnimation::getCondition() const
 }
 
 
-\f
+
 ////////////////////////////////////////////////////////////////////////
 // Implementation of null animation
 ////////////////////////////////////////////////////////////////////////
@@ -601,7 +667,7 @@ SGGroupAnimation::createAnimationGroup(osg::Group& parent)
   return group;
 }
 
-\f
+
 ////////////////////////////////////////////////////////////////////////
 // Implementation of translate animation
 ////////////////////////////////////////////////////////////////////////
@@ -643,22 +709,7 @@ SGTranslateAnimation::SGTranslateAnimation(const SGPropertyNode* configNode,
   else
     _initialValue = 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);
+  _axis = readTranslateAxis(configNode);
 }
 
 osg::Group*
@@ -676,7 +727,7 @@ SGTranslateAnimation::createAnimationGroup(osg::Group& parent)
   return transform;
 }
 
-\f
+
 ////////////////////////////////////////////////////////////////////////
 // Implementation of rotate/spin animation
 ////////////////////////////////////////////////////////////////////////
@@ -778,7 +829,8 @@ protected:
     // more than one camera. It is probably safe to overwrite the
     // reference values in multiple threads, but we'll provide a
     // threadsafe way to manage those values just to be safe.
-    struct ReferenceValues {
+    struct ReferenceValues : public osg::Referenced
+    {
         ReferenceValues(double t, double rot, double vel)
             : _time(t), _rotation(rot), _rotVelocity(vel)
         {
@@ -798,21 +850,33 @@ void SpinAnimCallback::operator()(osg::Node* node, osg::NodeVisitor* nv)
     if (!cv)
         return;
     if (!_condition || _condition->test()) {
-        double t = nv->getFrameStamp()->getReferenceTime();
+        double t = nv->getFrameStamp()->getSimulationTime();
         double rps = _animationValue->getValue() / 60.0;
-        ReferenceValues* refval = static_cast<ReferenceValues*>(_referenceValues.get());
-        if (!refval || refval->_rotVelocity != rps) {
-            ReferenceValues* newref = 0;
-            if (!refval) {
+        ref_ptr<ReferenceValues>
+            refval(static_cast<ReferenceValues*>(_referenceValues.get()));
+    if (!refval || refval->_rotVelocity != rps) {
+            ref_ptr<ReferenceValues> newref;
+            if (!refval.valid()) {
                 // initialization
                 newref = new ReferenceValues(t, 0.0, rps);
             } else {
                 double newRot = refval->_rotation + (t - refval->_time) * refval->_rotVelocity;
                 newref = new ReferenceValues(t, newRot, rps);
             }
+            // increment reference pointer, because it will be stored
+            // naked in _referenceValues.
+            newref->ref();
             if (_referenceValues.assign(newref, refval)) {
-                delete refval;
+                if (refval.valid()) {
+                    DeletionManager::instance()->addStaleObject(refval.get());
+                    refval->unref();
+                }
+            } else {
+                // Another thread installed new values before us
+                newref->unref();
             }
+            // Whatever happened, we can use the reference values just
+            // calculated.
             refval = newref;
         }
         double rotation = refval->_rotation + (t - refval->_time) * rps;
@@ -834,6 +898,30 @@ void SpinAnimCallback::operator()(osg::Node* node, osg::NodeVisitor* nv)
     }
 }
 
+SGVec3d readTranslateAxis(const SGPropertyNode* configNode)
+{
+    SGVec3d axis;
+    
+    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);
+
+    return axis;
+}
+
 SGRotateAnimation::SGRotateAnimation(const SGPropertyNode* configNode,
                                      SGPropertyNode* modelRoot) :
   SGAnimation(configNode, modelRoot)
@@ -850,28 +938,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(_center, _axis);
 }
 
 osg::Group*
@@ -901,7 +969,7 @@ SGRotateAnimation::createAnimationGroup(osg::Group& parent)
     }
 }
 
-\f
+
 ////////////////////////////////////////////////////////////////////////
 // Implementation of scale animation
 ////////////////////////////////////////////////////////////////////////
@@ -1035,7 +1103,7 @@ SGScaleAnimation::createAnimationGroup(osg::Group& parent)
   return transform;
 }
 
-\f
+
 // Don't create a new state state everytime we need GL_NORMALIZE!
 
 namespace
@@ -1181,7 +1249,7 @@ namespace
    &SGDistScaleAnimation::Transform::writeLocalData
    );
 }
-\f
+
 ////////////////////////////////////////////////////////////////////////
 // Implementation of flash animation
 ////////////////////////////////////////////////////////////////////////
@@ -1337,7 +1405,7 @@ namespace
    &SGFlashAnimation::Transform::writeLocalData
    );
 }
-\f
+
 ////////////////////////////////////////////////////////////////////////
 // Implementation of billboard animation
 ////////////////////////////////////////////////////////////////////////
@@ -1422,7 +1490,7 @@ namespace
    &SGBillboardAnimation::Transform::writeLocalData
    );
 }
-\f
+
 ////////////////////////////////////////////////////////////////////////
 // Implementation of a range animation
 ////////////////////////////////////////////////////////////////////////
@@ -1527,7 +1595,7 @@ SGRangeAnimation::createAnimationGroup(osg::Group& parent)
   return group;
 }
 
-\f
+
 ////////////////////////////////////////////////////////////////////////
 // Implementation of a select animation
 ////////////////////////////////////////////////////////////////////////
@@ -1557,7 +1625,7 @@ SGSelectAnimation::createAnimationGroup(osg::Group& parent)
 }
 
 
-\f
+
 ////////////////////////////////////////////////////////////////////////
 // Implementation of alpha test animation
 ////////////////////////////////////////////////////////////////////////
@@ -1627,7 +1695,7 @@ SGAlphaTestAnimation::install(osg::Node& node)
   }
 }
 
-\f
+
 //////////////////////////////////////////////////////////////////////
 // Blend animation installer
 //////////////////////////////////////////////////////////////////////
@@ -1746,7 +1814,7 @@ SGBlendAnimation::install(osg::Node& node)
   node.accept(visitor);
 }
 
-\f
+
 //////////////////////////////////////////////////////////////////////
 // Timed animation installer
 //////////////////////////////////////////////////////////////////////
@@ -1794,7 +1862,7 @@ public:
     _current_index = _current_index % nChildren;
 
     // update the time and compute the current systems time value
-    double t = nv->getFrameStamp()->getReferenceTime();
+    double t = nv->getFrameStamp()->getSimulationTime();
     if (_last_time_sec == SGLimitsd::max()) {
       _last_time_sec = t;
     } else {
@@ -1857,7 +1925,7 @@ SGTimedAnimation::createAnimationGroup(osg::Group& parent)
   return sw;
 }
 
-\f
+
 ////////////////////////////////////////////////////////////////////////
 // dynamically switch on/off shadows
 ////////////////////////////////////////////////////////////////////////
@@ -1903,7 +1971,7 @@ SGShadowAnimation::createAnimationGroup(osg::Group& parent)
   return group;
 }
 
-\f
+
 ////////////////////////////////////////////////////////////////////////
 // Implementation of SGTexTransformAnimation
 ////////////////////////////////////////////////////////////////////////
@@ -2141,305 +2209,3 @@ SGTexTransformAnimation::appendTexRotate(const SGPropertyNode* config,
   updateCallback->appendTransform(rotation, value);
 }
 
-
-////////////////////////////////////////////////////////////////////////
-// Implementation of SGPickAnimation
-////////////////////////////////////////////////////////////////////////
-
-class SGPickAnimation::PickCallback : public SGPickCallback {
-public:
-  PickCallback(const SGPropertyNode* configNode,
-               SGPropertyNode* modelRoot) :
-    _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));
-    }
-
-    const SGPropertyNode* upNode = configNode->getChild("mod-up");
-    if (!upNode)
-      return;
-    bindings = upNode->getChildren("binding");
-    for (unsigned int i = 0; i < bindings.size(); ++i) {
-      _bindingsUp.push_back(new SGBinding(bindings[i], modelRoot));
-    }
-  }
-  virtual bool buttonPressed(int button, const Info&)
-  {
-    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 = -_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();
-  }
-  virtual void update(double dt)
-  {
-    if (!_repeatable)
-      return;
-
-    _repeatTime += dt;
-    while (_repeatInterval < _repeatTime) {
-      _repeatTime -= _repeatInterval;
-      SGBindingList::const_iterator i;
-      for (i = _bindingsDown.begin(); i != _bindingsDown.end(); ++i)
-        (*i)->fire();
-    }
-  }
-private:
-  SGBindingList _bindingsDown;
-  SGBindingList _bindingsUp;
-  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)
-{
-}
-
-namespace
-{
-Mutex colorModeUniformMutex;
-osg::ref_ptr<osg::Uniform> colorModeUniform;
-}
-
-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(SG_NODEMASK_PICK_BIT);
-  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
-  // 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;
-}