]> git.mxchange.org Git - simgear.git/blob - simgear/scene/model/animation.cxx
4d9c77312b9d7e856629b4eb2a969f5dae19df9a
[simgear.git] / simgear / scene / model / animation.cxx
1 // animation.cxx - classes to manage model animation.
2 // Written by David Megginson, started 2002.
3 //
4 // This file is in the Public Domain, and comes with no warranty.
5
6 #ifdef HAVE_CONFIG_H
7 #  include <simgear_config.h>
8 #endif
9
10 #include <string.h>             // for strcmp()
11 #include <math.h>
12 #include <algorithm>
13 #include <functional>
14
15 #include <OpenThreads/Mutex>
16 #include <OpenThreads/ReentrantMutex>
17 #include <OpenThreads/ScopedLock>
18
19 #include <osg/AlphaFunc>
20 #include <osg/Drawable>
21 #include <osg/Geode>
22 #include <osg/Geometry>
23 #include <osg/LOD>
24 #include <osg/Math>
25 #include <osg/PolygonMode>
26 #include <osg/PolygonOffset>
27 #include <osg/StateSet>
28 #include <osg/Switch>
29 #include <osg/TexMat>
30 #include <osg/Texture2D>
31 #include <osg/Transform>
32 #include <osgDB/ReadFile>
33
34 #include <simgear/math/interpolater.hxx>
35 #include <simgear/props/condition.hxx>
36 #include <simgear/props/props.hxx>
37 #include <simgear/structure/SGBinding.hxx>
38 #include <simgear/scene/util/SGNodeMasks.hxx>
39 #include <simgear/scene/util/SGSceneUserData.hxx>
40 #include <simgear/scene/util/SGStateAttributeVisitor.hxx>
41
42 #include "animation.hxx"
43 #include "model.hxx"
44
45 #include "SGTranslateTransform.hxx"
46 #include "SGMaterialAnimation.hxx"
47 #include "SGRotateTransform.hxx"
48 #include "SGScaleTransform.hxx"
49
50 \f
51 ////////////////////////////////////////////////////////////////////////
52 // Static utility functions.
53 ////////////////////////////////////////////////////////////////////////
54
55 /**
56  * Set up the transform matrix for a spin or rotation.
57  */
58 static void
59 set_rotation (osg::Matrix &matrix, double position_deg,
60               const SGVec3d &center, const SGVec3d &axis)
61 {
62   double temp_angle = -SGMiscd::deg2rad(position_deg);
63   
64   double s = sin(temp_angle);
65   double c = cos(temp_angle);
66   double t = 1 - c;
67   
68   // axis was normalized at load time 
69   // hint to the compiler to put these into FP registers
70   double x = axis[0];
71   double y = axis[1];
72   double z = axis[2];
73   
74   matrix(0, 0) = t * x * x + c ;
75   matrix(0, 1) = t * y * x - s * z ;
76   matrix(0, 2) = t * z * x + s * y ;
77   matrix(0, 3) = 0;
78   
79   matrix(1, 0) = t * x * y + s * z ;
80   matrix(1, 1) = t * y * y + c ;
81   matrix(1, 2) = t * z * y - s * x ;
82   matrix(1, 3) = 0;
83   
84   matrix(2, 0) = t * x * z - s * y ;
85   matrix(2, 1) = t * y * z + s * x ;
86   matrix(2, 2) = t * z * z + c ;
87   matrix(2, 3) = 0;
88   
89   // hint to the compiler to put these into FP registers
90   x = center[0];
91   y = center[1];
92   z = center[2];
93   
94   matrix(3, 0) = x - x*matrix(0, 0) - y*matrix(1, 0) - z*matrix(2, 0);
95   matrix(3, 1) = y - x*matrix(0, 1) - y*matrix(1, 1) - z*matrix(2, 1);
96   matrix(3, 2) = z - x*matrix(0, 2) - y*matrix(1, 2) - z*matrix(2, 2);
97   matrix(3, 3) = 1;
98 }
99
100 /**
101  * Set up the transform matrix for a translation.
102  */
103 static void
104 set_translation (osg::Matrix &matrix, double position_m, const SGVec3d &axis)
105 {
106   SGVec3d xyz = axis * position_m;
107   matrix.makeIdentity();
108   matrix(3, 0) = xyz[0];
109   matrix(3, 1) = xyz[1];
110   matrix(3, 2) = xyz[2];
111 }
112
113 /**
114  * Read an interpolation table from properties.
115  */
116 static SGInterpTable *
117 read_interpolation_table(const SGPropertyNode* props)
118 {
119   const SGPropertyNode* table_node = props->getNode("interpolation");
120   if (!table_node)
121     return 0;
122   return new SGInterpTable(table_node);
123 }
124
125 static std::string
126 unit_string(const char* value, const char* unit)
127 {
128   return std::string(value) + unit;
129 }
130
131 class SGPersonalityScaleOffsetExpression : public SGUnaryExpression<double> {
132 public:
133   SGPersonalityScaleOffsetExpression(SGExpression<double>* expr,
134                                      SGPropertyNode const* config,
135                                      const std::string& scalename,
136                                      const std::string& offsetname,
137                                      double defScale = 1,
138                                      double defOffset = 0) :
139     SGUnaryExpression<double>(expr),
140     _scale(config, scalename.c_str(), defScale),
141     _offset(config, offsetname.c_str(), defOffset)
142   { }
143   void setScale(double scale)
144   { _scale = scale; }
145   void setOffset(double offset)
146   { _offset = offset; }
147
148   virtual void eval(double& value) const
149   {
150     _offset.shuffle();
151     _scale.shuffle();
152     value = _offset + _scale*getOperand()->getValue();
153   }
154
155   virtual bool isConst() const { return false; }
156
157 private:
158   mutable SGPersonalityParameter<double> _scale;
159   mutable SGPersonalityParameter<double> _offset;
160 };
161
162
163 static SGExpressiond*
164 read_factor_offset(const SGPropertyNode* configNode, SGExpressiond* expr,
165                    const std::string& factor, const std::string& offset)
166 {
167   double factorValue = configNode->getDoubleValue(factor, 1);
168   if (factorValue != 1)
169     expr = new SGScaleExpression<double>(expr, factorValue);
170   double offsetValue = configNode->getDoubleValue(offset, 0);
171   if (offsetValue != 0)
172     expr = new SGBiasExpression<double>(expr, offsetValue);
173   return expr;
174 }
175
176 static SGExpressiond*
177 read_offset_factor(const SGPropertyNode* configNode, SGExpressiond* expr,
178                    const std::string& factor, const std::string& offset)
179 {
180   double offsetValue = configNode->getDoubleValue(offset, 0);
181   if (offsetValue != 0)
182     expr = new SGBiasExpression<double>(expr, offsetValue);
183   double factorValue = configNode->getDoubleValue(factor, 1);
184   if (factorValue != 1)
185     expr = new SGScaleExpression<double>(expr, factorValue);
186   return expr;
187 }
188
189 SGExpressiond*
190 read_value(const SGPropertyNode* configNode, SGPropertyNode* modelRoot,
191            const char* unit, double defMin, double defMax)
192 {
193   SGExpression<double>* value = 0;
194
195   std::string inputPropertyName = configNode->getStringValue("property", "");
196   if (inputPropertyName.empty()) {
197     std::string spos = unit_string("starting-position", unit);
198     double initPos = configNode->getDoubleValue(spos, 0);
199     value = new SGConstExpression<double>(initPos);
200   } else {
201     SGPropertyNode* inputProperty;
202     inputProperty = modelRoot->getNode(inputPropertyName, true);
203     value = new SGPropertyExpression<double>(inputProperty);
204   }
205
206   SGInterpTable* interpTable = read_interpolation_table(configNode);
207   if (interpTable) {
208     return new SGInterpTableExpression<double>(value, interpTable);
209   } else {
210     std::string offset = unit_string("offset", unit);
211     std::string min = unit_string("min", unit);
212     std::string max = unit_string("max", unit);
213     
214     if (configNode->getBoolValue("use-personality", false)) {
215       value = new SGPersonalityScaleOffsetExpression(value, configNode,
216                                                      "factor", offset);
217     } else {
218       value = read_factor_offset(configNode, value, "factor", offset);
219     }
220     
221     double minClip = configNode->getDoubleValue(min, defMin);
222     double maxClip = configNode->getDoubleValue(max, defMax);
223     if (minClip > SGMiscd::min(SGLimitsd::min(), -SGLimitsd::max()) ||
224         maxClip < SGLimitsd::max())
225       value = new SGClipExpression<double>(value, minClip, maxClip);
226     
227     return value;
228   }
229   return 0;
230 }
231
232 \f
233 ////////////////////////////////////////////////////////////////////////
234 // Animation installer
235 ////////////////////////////////////////////////////////////////////////
236
237 class SGAnimation::RemoveModeVisitor : public SGStateAttributeVisitor {
238 public:
239   RemoveModeVisitor(osg::StateAttribute::GLMode mode) :
240     _mode(mode)
241   { }
242   virtual void apply(osg::StateSet* stateSet)
243   {
244     if (!stateSet)
245       return;
246     stateSet->removeMode(_mode);
247   }
248 private:
249   osg::StateAttribute::GLMode _mode;
250 };
251
252 class SGAnimation::RemoveAttributeVisitor : public SGStateAttributeVisitor {
253 public:
254   RemoveAttributeVisitor(osg::StateAttribute::Type type) :
255     _type(type)
256   { }
257   virtual void apply(osg::StateSet* stateSet)
258   {
259     if (!stateSet)
260       return;
261     while (stateSet->getAttribute(_type)) {
262       stateSet->removeAttribute(_type);
263     }
264   }
265 private:
266   osg::StateAttribute::Type _type;
267 };
268
269 class SGAnimation::RemoveTextureModeVisitor : public SGStateAttributeVisitor {
270 public:
271   RemoveTextureModeVisitor(unsigned unit, osg::StateAttribute::GLMode mode) :
272     _unit(unit),
273     _mode(mode)
274   { }
275   virtual void apply(osg::StateSet* stateSet)
276   {
277     if (!stateSet)
278       return;
279     stateSet->removeTextureMode(_unit, _mode);
280   }
281 private:
282   unsigned _unit;
283   osg::StateAttribute::GLMode _mode;
284 };
285
286 class SGAnimation::RemoveTextureAttributeVisitor :
287   public SGStateAttributeVisitor {
288 public:
289   RemoveTextureAttributeVisitor(unsigned unit,
290                                 osg::StateAttribute::Type type) :
291     _unit(unit),
292     _type(type)
293   { }
294   virtual void apply(osg::StateSet* stateSet)
295   {
296     if (!stateSet)
297       return;
298     while (stateSet->getTextureAttribute(_unit, _type)) {
299       stateSet->removeTextureAttribute(_unit, _type);
300     }
301   }
302 private:
303   unsigned _unit;
304   osg::StateAttribute::Type _type;
305 };
306
307 class SGAnimation::BinToInheritVisitor : public SGStateAttributeVisitor {
308 public:
309   virtual void apply(osg::StateSet* stateSet)
310   {
311     if (!stateSet)
312       return;
313     stateSet->setRenderBinToInherit();
314   }
315 };
316
317 class SGAnimation::DrawableCloneVisitor : public osg::NodeVisitor {
318 public:
319   DrawableCloneVisitor() :
320     osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
321   {}
322   void apply(osg::Geode& geode)
323   {
324     for (unsigned i = 0 ; i < geode.getNumDrawables(); ++i) {
325       osg::CopyOp copyOp(osg::CopyOp::DEEP_COPY_ALL &
326                          ~osg::CopyOp::DEEP_COPY_TEXTURES);
327       geode.setDrawable(i, copyOp(geode.getDrawable(i)));
328     }
329   }
330 };
331
332 namespace
333 {
334 // Set all drawables to not use display lists. OSG will use
335 // glDrawArrays instead.
336 struct DoDrawArraysVisitor : public osg::NodeVisitor {
337     DoDrawArraysVisitor() :
338         osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
339     {}
340     void apply(osg::Geode& geode)
341     {
342         using namespace osg;
343         using namespace std;
344
345         for (int i = 0; i < geode.getNumDrawables(); ++i)
346             geode.getDrawable(i)->setUseDisplayList(false);
347     }
348 };
349 }
350
351 SGAnimation::SGAnimation(const SGPropertyNode* configNode,
352                                            SGPropertyNode* modelRoot) :
353   osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
354   _found(false),
355   _configNode(configNode),
356   _modelRoot(modelRoot)
357 {
358   _name = configNode->getStringValue("name", "");
359   _enableHOT = configNode->getBoolValue("enable-hot", true);
360   _disableShadow = configNode->getBoolValue("disable-shadow", false);
361   std::vector<SGPropertyNode_ptr> objectNames =
362     configNode->getChildren("object-name");
363   for (unsigned i = 0; i < objectNames.size(); ++i)
364     _objectNames.push_back(objectNames[i]->getStringValue());
365 }
366
367 SGAnimation::~SGAnimation()
368 {
369   if (_found)
370     return;
371
372   SG_LOG(SG_IO, SG_ALERT, "Could not find at least one of the following"
373          " objects for animation:\n");
374   std::list<std::string>::const_iterator i;
375   for (i = _objectNames.begin(); i != _objectNames.end(); ++i)
376     SG_LOG(SG_IO, SG_ALERT, *i << "\n");
377 }
378
379 bool
380 SGAnimation::animate(osg::Node* node, const SGPropertyNode* configNode,
381                      SGPropertyNode* modelRoot,
382                      const osgDB::ReaderWriter::Options* options)
383 {
384   std::string type = configNode->getStringValue("type", "none");
385   if (type == "alpha-test") {
386     SGAlphaTestAnimation animInst(configNode, modelRoot);
387     animInst.apply(node);
388   } else if (type == "billboard") {
389     SGBillboardAnimation animInst(configNode, modelRoot);
390     animInst.apply(node);
391   } else if (type == "blend") {
392     SGBlendAnimation animInst(configNode, modelRoot);
393     animInst.apply(node);
394   } else if (type == "dist-scale") {
395     SGDistScaleAnimation animInst(configNode, modelRoot);
396     animInst.apply(node);
397   } else if (type == "flash") {
398     SGFlashAnimation animInst(configNode, modelRoot);
399     animInst.apply(node);
400   } else if (type == "material") {
401     SGMaterialAnimation animInst(configNode, modelRoot, options);
402     animInst.apply(node);
403   } else if (type == "noshadow") {
404     SGShadowAnimation animInst(configNode, modelRoot);
405     animInst.apply(node);
406   } else if (type == "pick") {
407     SGPickAnimation animInst(configNode, modelRoot);
408     animInst.apply(node);
409   } else if (type == "range") {
410     SGRangeAnimation animInst(configNode, modelRoot);
411     animInst.apply(node);
412   } else if (type == "rotate" || type == "spin") {
413     SGRotateAnimation animInst(configNode, modelRoot);
414     animInst.apply(node);
415   } else if (type == "scale") {
416     SGScaleAnimation animInst(configNode, modelRoot);
417     animInst.apply(node);
418   } else if (type == "select") {
419     SGSelectAnimation animInst(configNode, modelRoot);
420     animInst.apply(node);
421   } else if (type == "shader") {
422     SGShaderAnimation animInst(configNode, modelRoot, options);
423     animInst.apply(node);
424   } else if (type == "textranslate" || type == "texrotate" ||
425              type == "texmultiple") {
426     SGTexTransformAnimation animInst(configNode, modelRoot);
427     animInst.apply(node);
428   } else if (type == "timed") {
429     SGTimedAnimation animInst(configNode, modelRoot);
430     animInst.apply(node);
431   } else if (type == "translate") {
432     SGTranslateAnimation animInst(configNode, modelRoot);
433     animInst.apply(node);
434   } else if (type == "null" || type == "none" || type.empty()) {
435     SGGroupAnimation animInst(configNode, modelRoot);
436     animInst.apply(node);
437   } else
438     return false;
439
440   return true;
441 }
442   
443   
444 void
445 SGAnimation::apply(osg::Node* node)
446 {
447   // duh what a special case ...
448   if (_objectNames.empty()) {
449     osg::Group* group = node->asGroup();
450     if (group) {
451       osg::ref_ptr<osg::Group> animationGroup;
452       installInGroup(std::string(), *group, animationGroup);
453     }
454   } else
455     node->accept(*this);
456 }
457
458 void
459 SGAnimation::install(osg::Node& node)
460 {
461   _found = true;
462   if (_enableHOT)
463     node.setNodeMask( SG_NODEMASK_TERRAIN_BIT | node.getNodeMask());
464   else
465     node.setNodeMask(~SG_NODEMASK_TERRAIN_BIT & node.getNodeMask());
466   if (!_disableShadow)
467     node.setNodeMask( SG_NODEMASK_CASTSHADOW_BIT | node.getNodeMask());
468   else
469     node.setNodeMask(~SG_NODEMASK_CASTSHADOW_BIT & node.getNodeMask());
470 }
471
472 osg::Group*
473 SGAnimation::createAnimationGroup(osg::Group& parent)
474 {
475   // default implementation, we do not need a new group
476   // for every animation type. Usually animations that just change
477   // the StateSet of some parts of the model
478   return 0;
479 }
480
481 void
482 SGAnimation::apply(osg::Group& group)
483 {
484   // the trick is to first traverse the children and then
485   // possibly splice in a new group node if required.
486   // Else we end up in a recursive loop where we infinitly insert new
487   // groups in between
488   traverse(group);
489
490   // Note that this algorithm preserves the order of the child objects
491   // like they appear in the object-name tags.
492   // The timed animations require this
493   osg::ref_ptr<osg::Group> animationGroup;
494   std::list<std::string>::const_iterator nameIt;
495   for (nameIt = _objectNames.begin(); nameIt != _objectNames.end(); ++nameIt)
496     installInGroup(*nameIt, group, animationGroup);
497 }
498
499 void
500 SGAnimation::installInGroup(const std::string& name, osg::Group& group,
501                             osg::ref_ptr<osg::Group>& animationGroup)
502 {
503   int i = group.getNumChildren() - 1;
504   for (; 0 <= i; --i) {
505     osg::Node* child = group.getChild(i);
506
507     // Check if this one is already processed
508     if (std::find(_installedAnimations.begin(),
509                   _installedAnimations.end(), child)
510         != _installedAnimations.end())
511       continue;
512
513     if (name.empty() || child->getName() == name) {
514       // fire the installation of the animation
515       install(*child);
516       
517       // create a group node on demand
518       if (!animationGroup.valid()) {
519         animationGroup = createAnimationGroup(group);
520         // Animation type that does not require a new group,
521         // in this case we can stop and look for the next object
522         if (animationGroup.valid() && !_name.empty())
523           animationGroup->setName(_name);
524       }
525       if (animationGroup.valid()) {
526         animationGroup->addChild(child);
527         group.removeChild(i);
528       }
529
530       // store that we already have processed this child node
531       // We can hit this one twice if an animation references some
532       // part of a subtree twice
533       _installedAnimations.push_back(child);
534     }
535   }
536 }
537
538 void
539 SGAnimation::removeMode(osg::Node& node, osg::StateAttribute::GLMode mode)
540 {
541   RemoveModeVisitor visitor(mode);
542   node.accept(visitor);
543 }
544
545 void
546 SGAnimation::removeAttribute(osg::Node& node, osg::StateAttribute::Type type)
547 {
548   RemoveAttributeVisitor visitor(type);
549   node.accept(visitor);
550 }
551
552 void
553 SGAnimation::removeTextureMode(osg::Node& node, unsigned unit,
554                                osg::StateAttribute::GLMode mode)
555 {
556   RemoveTextureModeVisitor visitor(unit, mode);
557   node.accept(visitor);
558 }
559
560 void
561 SGAnimation::removeTextureAttribute(osg::Node& node, unsigned unit,
562                                     osg::StateAttribute::Type type)
563 {
564   RemoveTextureAttributeVisitor visitor(unit, type);
565   node.accept(visitor);
566 }
567
568 void
569 SGAnimation::setRenderBinToInherit(osg::Node& node)
570 {
571   BinToInheritVisitor visitor;
572   node.accept(visitor);
573 }
574
575 void
576 SGAnimation::cloneDrawables(osg::Node& node)
577 {
578   DrawableCloneVisitor visitor;
579   node.accept(visitor);
580 }
581
582 const SGCondition*
583 SGAnimation::getCondition() const
584 {
585   const SGPropertyNode* conditionNode = _configNode->getChild("condition");
586   if (!conditionNode)
587     return 0;
588   return sgReadCondition(_modelRoot, conditionNode);
589 }
590
591
592 \f
593 ////////////////////////////////////////////////////////////////////////
594 // Implementation of null animation
595 ////////////////////////////////////////////////////////////////////////
596
597 // Ok, that is to build a subgraph from different other
598 // graph nodes. I guess that this stems from the time where modellers
599 // could not build hierarchical trees ...
600 SGGroupAnimation::SGGroupAnimation(const SGPropertyNode* configNode,
601                                    SGPropertyNode* modelRoot):
602   SGAnimation(configNode, modelRoot)
603 {
604 }
605
606 osg::Group*
607 SGGroupAnimation::createAnimationGroup(osg::Group& parent)
608 {
609   osg::Group* group = new osg::Group;
610   parent.addChild(group);
611   return group;
612 }
613
614 \f
615 ////////////////////////////////////////////////////////////////////////
616 // Implementation of translate animation
617 ////////////////////////////////////////////////////////////////////////
618
619 class SGTranslateAnimation::UpdateCallback : public osg::NodeCallback {
620 public:
621   UpdateCallback(SGCondition const* condition,
622                  SGExpressiond const* animationValue) :
623     _condition(condition),
624     _animationValue(animationValue)
625   { }
626   virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
627   {
628     if (!_condition || _condition->test()) {
629       SGTranslateTransform* transform;
630       transform = static_cast<SGTranslateTransform*>(node);
631       transform->setValue(_animationValue->getValue());
632     }
633     traverse(node, nv);
634   }
635 public:
636   SGSharedPtr<SGCondition const> _condition;
637   SGSharedPtr<SGExpressiond const> _animationValue;
638 };
639
640 SGTranslateAnimation::SGTranslateAnimation(const SGPropertyNode* configNode,
641                                            SGPropertyNode* modelRoot) :
642   SGAnimation(configNode, modelRoot)
643 {
644   _condition = getCondition();
645   SGSharedPtr<SGExpressiond> value;
646   value = read_value(configNode, modelRoot, "-m",
647                      -SGLimitsd::max(), SGLimitsd::max());
648   _animationValue = value->simplify();
649   if (_animationValue)
650     _initialValue = _animationValue->getValue();
651   else
652     _initialValue = 0;
653
654   _axis[0] = configNode->getDoubleValue("axis/x", 0);
655   _axis[1] = configNode->getDoubleValue("axis/y", 0);
656   _axis[2] = configNode->getDoubleValue("axis/z", 0);
657   if (8*SGLimitsd::min() < norm(_axis))
658     _axis = normalize(_axis);
659 }
660
661 osg::Group*
662 SGTranslateAnimation::createAnimationGroup(osg::Group& parent)
663 {
664   SGTranslateTransform* transform = new SGTranslateTransform;
665   transform->setName("translate animation");
666   if (_animationValue && !_animationValue->isConst()) {
667     UpdateCallback* uc = new UpdateCallback(_condition, _animationValue);
668     transform->setUpdateCallback(uc);
669   }
670   transform->setAxis(_axis);
671   transform->setValue(_initialValue);
672   parent.addChild(transform);
673   return transform;
674 }
675
676 \f
677 ////////////////////////////////////////////////////////////////////////
678 // Implementation of rotate/spin animation
679 ////////////////////////////////////////////////////////////////////////
680
681 class SGRotateAnimation::UpdateCallback : public osg::NodeCallback {
682 public:
683   UpdateCallback(SGCondition const* condition,
684                  SGExpressiond const* animationValue) :
685     _condition(condition),
686     _animationValue(animationValue)
687   { }
688   virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
689   {
690     if (!_condition || _condition->test()) {
691       SGRotateTransform* transform;
692       transform = static_cast<SGRotateTransform*>(node);
693       transform->setAngleDeg(_animationValue->getValue());
694     }
695     traverse(node, nv);
696   }
697 public:
698   SGSharedPtr<SGCondition const> _condition;
699   SGSharedPtr<SGExpressiond const> _animationValue;
700 };
701
702 class SGRotateAnimation::SpinUpdateCallback : public osg::NodeCallback {
703 public:
704   SpinUpdateCallback(SGCondition const* condition,
705                      SGExpressiond const* animationValue) :
706     _condition(condition),
707     _animationValue(animationValue),
708     _lastTime(-1)
709   { }
710   virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
711   {
712     if (!_condition || _condition->test()) {
713       SGRotateTransform* transform;
714       transform = static_cast<SGRotateTransform*>(node);
715
716       double t = nv->getFrameStamp()->getReferenceTime();
717       double dt = 0;
718       if (0 <= _lastTime)
719         dt = t - _lastTime;
720       _lastTime = t;
721       double velocity_rpms = _animationValue->getValue()/60;
722       double angle = transform->getAngleDeg();
723       angle += dt*velocity_rpms*360;
724       angle -= 360*floor(angle/360);
725       transform->setAngleDeg(angle);
726     }
727     traverse(node, nv);
728   }
729 public:
730   SGSharedPtr<SGCondition const> _condition;
731   SGSharedPtr<SGExpressiond const> _animationValue;
732   double _lastTime;
733 };
734
735 SGRotateAnimation::SGRotateAnimation(const SGPropertyNode* configNode,
736                                      SGPropertyNode* modelRoot) :
737   SGAnimation(configNode, modelRoot)
738 {
739   std::string type = configNode->getStringValue("type", "");
740   _isSpin = (type == "spin");
741
742   _condition = getCondition();
743   SGSharedPtr<SGExpressiond> value;
744   value = read_value(configNode, modelRoot, "-deg",
745                      -SGLimitsd::max(), SGLimitsd::max());
746   _animationValue = value->simplify();
747   if (_animationValue)
748     _initialValue = _animationValue->getValue();
749   else
750     _initialValue = 0;
751
752   _center = SGVec3d::zeros();
753   if (configNode->hasValue("axis/x1-m")) {
754     SGVec3d v1, v2;
755     v1[0] = configNode->getDoubleValue("axis/x1-m", 0);
756     v1[1] = configNode->getDoubleValue("axis/y1-m", 0);
757     v1[2] = configNode->getDoubleValue("axis/z1-m", 0);
758     v2[0] = configNode->getDoubleValue("axis/x2-m", 0);
759     v2[1] = configNode->getDoubleValue("axis/y2-m", 0);
760     v2[2] = configNode->getDoubleValue("axis/z2-m", 0);
761     _center = 0.5*(v1+v2);
762     _axis = v2 - v1;
763   } else {
764     _axis[0] = configNode->getDoubleValue("axis/x", 0);
765     _axis[1] = configNode->getDoubleValue("axis/y", 0);
766     _axis[2] = configNode->getDoubleValue("axis/z", 0);
767   }
768   if (8*SGLimitsd::min() < norm(_axis))
769     _axis = normalize(_axis);
770
771   _center[0] = configNode->getDoubleValue("center/x-m", _center[0]);
772   _center[1] = configNode->getDoubleValue("center/y-m", _center[1]);
773   _center[2] = configNode->getDoubleValue("center/z-m", _center[2]);
774 }
775
776 osg::Group*
777 SGRotateAnimation::createAnimationGroup(osg::Group& parent)
778 {
779   SGRotateTransform* transform = new SGRotateTransform;
780   transform->setName("rotate animation");
781   if (_isSpin) {
782     SpinUpdateCallback* uc;
783     uc = new SpinUpdateCallback(_condition, _animationValue);
784     transform->setUpdateCallback(uc);
785   } else if (_animationValue || !_animationValue->isConst()) {
786     UpdateCallback* uc = new UpdateCallback(_condition, _animationValue);
787     transform->setUpdateCallback(uc);
788   }
789   transform->setCenter(_center);
790   transform->setAxis(_axis);
791   transform->setAngleDeg(_initialValue);
792   parent.addChild(transform);
793   return transform;
794 }
795
796 \f
797 ////////////////////////////////////////////////////////////////////////
798 // Implementation of scale animation
799 ////////////////////////////////////////////////////////////////////////
800
801 class SGScaleAnimation::UpdateCallback : public osg::NodeCallback {
802 public:
803   UpdateCallback(const SGCondition* condition,
804                  SGSharedPtr<const SGExpressiond> animationValue[3]) :
805     _condition(condition)
806   {
807     _animationValue[0] = animationValue[0];
808     _animationValue[1] = animationValue[1];
809     _animationValue[2] = animationValue[2];
810   }
811   virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
812   {
813     if (!_condition || _condition->test()) {
814       SGScaleTransform* transform;
815       transform = static_cast<SGScaleTransform*>(node);
816       SGVec3d scale(_animationValue[0]->getValue(),
817                     _animationValue[1]->getValue(),
818                     _animationValue[2]->getValue());
819       transform->setScaleFactor(scale);
820     }
821     traverse(node, nv);
822   }
823 public:
824   SGSharedPtr<SGCondition const> _condition;
825   SGSharedPtr<SGExpressiond const> _animationValue[3];
826 };
827
828 SGScaleAnimation::SGScaleAnimation(const SGPropertyNode* configNode,
829                                    SGPropertyNode* modelRoot) :
830   SGAnimation(configNode, modelRoot)
831 {
832   _condition = getCondition();
833
834   // default offset/factor for all directions
835   double offset = configNode->getDoubleValue("offset", 0);
836   double factor = configNode->getDoubleValue("factor", 1);
837
838   SGSharedPtr<SGExpressiond> inPropExpr;
839
840   std::string inputPropertyName;
841   inputPropertyName = configNode->getStringValue("property", "");
842   if (inputPropertyName.empty()) {
843     inPropExpr = new SGConstExpression<double>(0);
844   } else {
845     SGPropertyNode* inputProperty;
846     inputProperty = modelRoot->getNode(inputPropertyName, true);
847     inPropExpr = new SGPropertyExpression<double>(inputProperty);
848   }
849
850   SGInterpTable* interpTable = read_interpolation_table(configNode);
851   if (interpTable) {
852     SGSharedPtr<SGExpressiond> value;
853     value = new SGInterpTableExpression<double>(inPropExpr, interpTable);
854     _animationValue[0] = value->simplify();
855     _animationValue[1] = value->simplify();
856     _animationValue[2] = value->simplify();
857   } else if (configNode->getBoolValue("use-personality", false)) {
858     SGSharedPtr<SGExpressiond> value;
859     value = new SGPersonalityScaleOffsetExpression(inPropExpr, configNode,
860                                                    "x-factor", "x-offset",
861                                                    factor, offset);
862     double minClip = configNode->getDoubleValue("x-min", 0);
863     double maxClip = configNode->getDoubleValue("x-max", SGLimitsd::max());
864     value = new SGClipExpression<double>(value, minClip, maxClip);
865     _animationValue[0] = value->simplify();
866     
867     value = new SGPersonalityScaleOffsetExpression(inPropExpr, configNode,
868                                                    "y-factor", "y-offset",
869                                                    factor, offset);
870     minClip = configNode->getDoubleValue("y-min", 0);
871     maxClip = configNode->getDoubleValue("y-max", SGLimitsd::max());
872     value = new SGClipExpression<double>(value, minClip, maxClip);
873     _animationValue[1] = value->simplify();
874     
875     value = new SGPersonalityScaleOffsetExpression(inPropExpr, configNode,
876                                                    "z-factor", "z-offset",
877                                                    factor, offset);
878     minClip = configNode->getDoubleValue("z-min", 0);
879     maxClip = configNode->getDoubleValue("z-max", SGLimitsd::max());
880     value = new SGClipExpression<double>(value, minClip, maxClip);
881     _animationValue[2] = value->simplify();
882   } else {
883     SGSharedPtr<SGExpressiond> value;
884     value = read_factor_offset(configNode, inPropExpr, "x-factor", "x-offset");
885     double minClip = configNode->getDoubleValue("x-min", 0);
886     double maxClip = configNode->getDoubleValue("x-max", SGLimitsd::max());
887     value = new SGClipExpression<double>(value, minClip, maxClip);
888     _animationValue[0] = value->simplify();
889
890     value = read_factor_offset(configNode, inPropExpr, "y-factor", "y-offset");
891     minClip = configNode->getDoubleValue("y-min", 0);
892     maxClip = configNode->getDoubleValue("y-max", SGLimitsd::max());
893     value = new SGClipExpression<double>(value, minClip, maxClip);
894     _animationValue[1] = value->simplify();
895
896     value = read_factor_offset(configNode, inPropExpr, "z-factor", "z-offset");
897     minClip = configNode->getDoubleValue("z-min", 0);
898     maxClip = configNode->getDoubleValue("z-max", SGLimitsd::max());
899     value = new SGClipExpression<double>(value, minClip, maxClip);
900     _animationValue[2] = value->simplify();
901   }
902   _initialValue[0] = configNode->getDoubleValue("x-starting-scale", 1);
903   _initialValue[0] *= configNode->getDoubleValue("x-factor", factor);
904   _initialValue[0] += configNode->getDoubleValue("x-offset", offset);
905   _initialValue[1] = configNode->getDoubleValue("y-starting-scale", 1);
906   _initialValue[1] *= configNode->getDoubleValue("y-factor", factor);
907   _initialValue[1] += configNode->getDoubleValue("y-offset", offset);
908   _initialValue[2] = configNode->getDoubleValue("z-starting-scale", 1);
909   _initialValue[2] *= configNode->getDoubleValue("z-factor", factor);
910   _initialValue[2] += configNode->getDoubleValue("z-offset", offset);
911   _center[0] = configNode->getDoubleValue("center/x-m", 0);
912   _center[1] = configNode->getDoubleValue("center/y-m", 0);
913   _center[2] = configNode->getDoubleValue("center/z-m", 0);
914 }
915
916 osg::Group*
917 SGScaleAnimation::createAnimationGroup(osg::Group& parent)
918 {
919   SGScaleTransform* transform = new SGScaleTransform;
920   transform->setName("scale animation");
921   transform->setCenter(_center);
922   transform->setScaleFactor(_initialValue);
923   UpdateCallback* uc = new UpdateCallback(_condition, _animationValue);
924   transform->setUpdateCallback(uc);
925   parent.addChild(transform);
926   return transform;
927 }
928
929 \f
930 // Don't create a new state state everytime we need GL_NORMALIZE!
931
932 namespace
933 {
934 OpenThreads::Mutex normalizeMutex;
935
936 osg::StateSet* getNormalizeStateSet()
937 {
938     static osg::ref_ptr<osg::StateSet> normalizeStateSet;
939     OpenThreads::ScopedLock<OpenThreads::Mutex> lock(normalizeMutex);
940     if (!normalizeStateSet.valid()) {
941         normalizeStateSet = new osg::StateSet;
942         normalizeStateSet->setMode(GL_NORMALIZE, osg::StateAttribute::ON);
943         normalizeStateSet->setDataVariance(osg::Object::STATIC);
944     }
945     return normalizeStateSet.get();
946 }
947 }
948
949 ////////////////////////////////////////////////////////////////////////
950 // Implementation of dist scale animation
951 ////////////////////////////////////////////////////////////////////////
952
953 class SGDistScaleAnimation::Transform : public osg::Transform {
954 public:
955   Transform(const SGPropertyNode* configNode)
956   {
957     setName(configNode->getStringValue("name", "dist scale animation"));
958     setReferenceFrame(RELATIVE_RF);
959     setStateSet(getNormalizeStateSet());
960     _factor = configNode->getFloatValue("factor", 1);
961     _offset = configNode->getFloatValue("offset", 0);
962     _min_v = configNode->getFloatValue("min", SGLimitsf::epsilon());
963     _max_v = configNode->getFloatValue("max", SGLimitsf::max());
964     _table = read_interpolation_table(configNode);
965     _center[0] = configNode->getFloatValue("center/x-m", 0);
966     _center[1] = configNode->getFloatValue("center/y-m", 0);
967     _center[2] = configNode->getFloatValue("center/z-m", 0);
968   }
969   virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
970                                          osg::NodeVisitor* nv) const 
971   {
972     osg::Matrix transform;
973     double scale_factor = computeScaleFactor(nv);
974     transform(0,0) = scale_factor;
975     transform(1,1) = scale_factor;
976     transform(2,2) = scale_factor;
977     transform(3,0) = _center[0]*(1 - scale_factor);
978     transform(3,1) = _center[1]*(1 - scale_factor);
979     transform(3,2) = _center[2]*(1 - scale_factor);
980     matrix.preMult(transform);
981     return true;
982   }
983   
984   virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
985                                          osg::NodeVisitor* nv) const
986   {
987     double scale_factor = computeScaleFactor(nv);
988     if (fabs(scale_factor) <= SGLimits<double>::min())
989       return false;
990     osg::Matrix transform;
991     double rScaleFactor = 1/scale_factor;
992     transform(0,0) = rScaleFactor;
993     transform(1,1) = rScaleFactor;
994     transform(2,2) = rScaleFactor;
995     transform(3,0) = _center[0]*(1 - rScaleFactor);
996     transform(3,1) = _center[1]*(1 - rScaleFactor);
997     transform(3,2) = _center[2]*(1 - rScaleFactor);
998     matrix.postMult(transform);
999     return true;
1000   }
1001
1002 private:
1003   double computeScaleFactor(osg::NodeVisitor* nv) const
1004   {
1005     if (!nv)
1006       return 1;
1007
1008     double scale_factor = (_center.osg() - nv->getEyePoint()).length();
1009     if (_table == 0) {
1010       scale_factor = _factor * scale_factor + _offset;
1011     } else {
1012       scale_factor = _table->interpolate( scale_factor );
1013     }
1014     if (scale_factor < _min_v)
1015       scale_factor = _min_v;
1016     if (scale_factor > _max_v)
1017       scale_factor = _max_v;
1018
1019     return scale_factor;
1020   }
1021
1022   SGSharedPtr<SGInterpTable> _table;
1023   SGVec3d _center;
1024   double _min_v;
1025   double _max_v;
1026   double _factor;
1027   double _offset;
1028 };
1029
1030
1031 SGDistScaleAnimation::SGDistScaleAnimation(const SGPropertyNode* configNode,
1032                                            SGPropertyNode* modelRoot) :
1033   SGAnimation(configNode, modelRoot)
1034 {
1035 }
1036
1037 osg::Group*
1038 SGDistScaleAnimation::createAnimationGroup(osg::Group& parent)
1039 {
1040   Transform* transform = new Transform(getConfig());
1041   parent.addChild(transform);
1042   return transform;
1043 }
1044
1045 \f
1046 ////////////////////////////////////////////////////////////////////////
1047 // Implementation of flash animation
1048 ////////////////////////////////////////////////////////////////////////
1049
1050 class SGFlashAnimation::Transform : public osg::Transform {
1051 public:
1052   Transform(const SGPropertyNode* configNode)
1053   {
1054     setReferenceFrame(RELATIVE_RF);
1055     setName(configNode->getStringValue("name", "flash animation"));
1056     setStateSet(getNormalizeStateSet());
1057
1058     _axis[0] = configNode->getFloatValue("axis/x", 0);
1059     _axis[1] = configNode->getFloatValue("axis/y", 0);
1060     _axis[2] = configNode->getFloatValue("axis/z", 1);
1061     _axis.normalize();
1062     
1063     _center[0] = configNode->getFloatValue("center/x-m", 0);
1064     _center[1] = configNode->getFloatValue("center/y-m", 0);
1065     _center[2] = configNode->getFloatValue("center/z-m", 0);
1066     
1067     _offset = configNode->getFloatValue("offset", 0);
1068     _factor = configNode->getFloatValue("factor", 1);
1069     _power = configNode->getFloatValue("power", 1);
1070     _two_sides = configNode->getBoolValue("two-sides", false);
1071     
1072     _min_v = configNode->getFloatValue("min", SGLimitsf::epsilon());
1073     _max_v = configNode->getFloatValue("max", 1);
1074   }
1075   virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
1076                                          osg::NodeVisitor* nv) const 
1077   {
1078     osg::Matrix transform;
1079     double scale_factor = computeScaleFactor(nv);
1080     transform(0,0) = scale_factor;
1081     transform(1,1) = scale_factor;
1082     transform(2,2) = scale_factor;
1083     transform(3,0) = _center[0]*(1 - scale_factor);
1084     transform(3,1) = _center[1]*(1 - scale_factor);
1085     transform(3,2) = _center[2]*(1 - scale_factor);
1086     matrix.preMult(transform);
1087     return true;
1088   }
1089   
1090   virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
1091                                          osg::NodeVisitor* nv) const
1092   {
1093     double scale_factor = computeScaleFactor(nv);
1094     if (fabs(scale_factor) <= SGLimits<double>::min())
1095       return false;
1096     osg::Matrix transform;
1097     double rScaleFactor = 1/scale_factor;
1098     transform(0,0) = rScaleFactor;
1099     transform(1,1) = rScaleFactor;
1100     transform(2,2) = rScaleFactor;
1101     transform(3,0) = _center[0]*(1 - rScaleFactor);
1102     transform(3,1) = _center[1]*(1 - rScaleFactor);
1103     transform(3,2) = _center[2]*(1 - rScaleFactor);
1104     matrix.postMult(transform);
1105     return true;
1106   }
1107
1108 private:
1109   double computeScaleFactor(osg::NodeVisitor* nv) const
1110   {
1111     if (!nv)
1112       return 1;
1113
1114     osg::Vec3 localEyeToCenter = nv->getEyePoint() - _center;
1115     localEyeToCenter.normalize();
1116
1117     double cos_angle = localEyeToCenter*_axis;
1118     double scale_factor = 0;
1119     if ( _two_sides && cos_angle < 0 )
1120       scale_factor = _factor * pow( -cos_angle, _power ) + _offset;
1121     else if ( cos_angle > 0 )
1122       scale_factor = _factor * pow( cos_angle, _power ) + _offset;
1123     
1124     if ( scale_factor < _min_v )
1125       scale_factor = _min_v;
1126     if ( scale_factor > _max_v )
1127       scale_factor = _max_v;
1128
1129     return scale_factor;
1130   }
1131
1132   virtual osg::BoundingSphere computeBound() const
1133   {
1134     // avoid being culled away by small feature culling
1135     osg::BoundingSphere bs = osg::Group::computeBound();
1136     bs.radius() *= _max_v;
1137     return bs;
1138   }
1139
1140 private:
1141   osg::Vec3 _center;
1142   osg::Vec3 _axis;
1143   double _power, _factor, _offset, _min_v, _max_v;
1144   bool _two_sides;
1145 };
1146
1147
1148 SGFlashAnimation::SGFlashAnimation(const SGPropertyNode* configNode,
1149                                    SGPropertyNode* modelRoot) :
1150   SGAnimation(configNode, modelRoot)
1151 {
1152 }
1153
1154 osg::Group*
1155 SGFlashAnimation::createAnimationGroup(osg::Group& parent)
1156 {
1157   Transform* transform = new Transform(getConfig());
1158   parent.addChild(transform);
1159   return transform;
1160 }
1161
1162 \f
1163 ////////////////////////////////////////////////////////////////////////
1164 // Implementation of flash animation
1165 ////////////////////////////////////////////////////////////////////////
1166
1167 class SGBillboardAnimation::Transform : public osg::Transform {
1168 public:
1169   Transform(const SGPropertyNode* configNode) :
1170     _spherical(configNode->getBoolValue("spherical", true))
1171   {
1172     setReferenceFrame(RELATIVE_RF);
1173     setName(configNode->getStringValue("name", "billboard animation"));
1174   }
1175   virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
1176                                          osg::NodeVisitor* nv) const 
1177   {
1178     // More or less taken from plibs ssgCutout
1179     if (_spherical) {
1180       matrix(0,0) = 1; matrix(0,1) = 0; matrix(0,2) = 0;
1181       matrix(1,0) = 0; matrix(1,1) = 0; matrix(1,2) = -1;
1182       matrix(2,0) = 0; matrix(2,1) = 1; matrix(2,2) = 0;
1183     } else {
1184       osg::Vec3 zAxis(matrix(2, 0), matrix(2, 1), matrix(2, 2));
1185       osg::Vec3 xAxis = osg::Vec3(0, 0, -1)^zAxis;
1186       osg::Vec3 yAxis = zAxis^xAxis;
1187       
1188       xAxis.normalize();
1189       yAxis.normalize();
1190       zAxis.normalize();
1191       
1192       matrix(0,0) = xAxis[0]; matrix(0,1) = xAxis[1]; matrix(0,2) = xAxis[2];
1193       matrix(1,0) = yAxis[0]; matrix(1,1) = yAxis[1]; matrix(1,2) = yAxis[2];
1194       matrix(2,0) = zAxis[0]; matrix(2,1) = zAxis[1]; matrix(2,2) = zAxis[2];
1195     }
1196     return true;
1197   }
1198   
1199   virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
1200                                          osg::NodeVisitor* nv) const
1201   {
1202     // Hmm, don't yet know how to get that back ...
1203     return false;
1204   }
1205
1206 private:
1207   bool _spherical;
1208 };
1209
1210
1211 SGBillboardAnimation::SGBillboardAnimation(const SGPropertyNode* configNode,
1212                                            SGPropertyNode* modelRoot) :
1213   SGAnimation(configNode, modelRoot)
1214 {
1215 }
1216
1217 osg::Group*
1218 SGBillboardAnimation::createAnimationGroup(osg::Group& parent)
1219 {
1220   Transform* transform = new Transform(getConfig());
1221   parent.addChild(transform);
1222   return transform;
1223 }
1224
1225 \f
1226 ////////////////////////////////////////////////////////////////////////
1227 // Implementation of a range animation
1228 ////////////////////////////////////////////////////////////////////////
1229
1230 class SGRangeAnimation::UpdateCallback : public osg::NodeCallback {
1231 public:
1232   UpdateCallback(const SGCondition* condition,
1233                  const SGExpressiond* minAnimationValue,
1234                  const SGExpressiond* maxAnimationValue,
1235                  double minValue, double maxValue) :
1236     _condition(condition),
1237     _minAnimationValue(minAnimationValue),
1238     _maxAnimationValue(maxAnimationValue),
1239     _minStaticValue(minValue),
1240     _maxStaticValue(maxValue)
1241   {}
1242   virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1243   {
1244     osg::LOD* lod = static_cast<osg::LOD*>(node);
1245     if (!_condition || _condition->test()) {
1246       double minRange;
1247       if (_minAnimationValue)
1248         minRange = _minAnimationValue->getValue();
1249       else
1250         minRange = _minStaticValue;
1251       double maxRange;
1252       if (_maxAnimationValue)
1253         maxRange = _maxAnimationValue->getValue();
1254       else
1255         maxRange = _maxStaticValue;
1256       lod->setRange(0, minRange, maxRange);
1257     } else {
1258       lod->setRange(0, 0, SGLimitsf::max());
1259     }
1260     traverse(node, nv);
1261   }
1262
1263 private:
1264   SGSharedPtr<const SGCondition> _condition;
1265   SGSharedPtr<const SGExpressiond> _minAnimationValue;
1266   SGSharedPtr<const SGExpressiond> _maxAnimationValue;
1267   double _minStaticValue;
1268   double _maxStaticValue;
1269 };
1270
1271 SGRangeAnimation::SGRangeAnimation(const SGPropertyNode* configNode,
1272                                    SGPropertyNode* modelRoot) :
1273   SGAnimation(configNode, modelRoot)
1274 {
1275   _condition = getCondition();
1276
1277   std::string inputPropertyName;
1278   inputPropertyName = configNode->getStringValue("min-property", "");
1279   if (!inputPropertyName.empty()) {
1280     SGPropertyNode* inputProperty;
1281     inputProperty = modelRoot->getNode(inputPropertyName, true);
1282     SGSharedPtr<SGExpressiond> value;
1283     value = new SGPropertyExpression<double>(inputProperty);
1284
1285     value = read_factor_offset(configNode, value, "min-factor", "min-offset");
1286     _minAnimationValue = value->simplify();
1287   }
1288   inputPropertyName = configNode->getStringValue("max-property", "");
1289   if (!inputPropertyName.empty()) {
1290     SGPropertyNode* inputProperty;
1291     inputProperty = modelRoot->getNode(inputPropertyName.c_str(), true);
1292
1293     SGSharedPtr<SGExpressiond> value;
1294     value = new SGPropertyExpression<double>(inputProperty);
1295
1296     value = read_factor_offset(configNode, value, "max-factor", "max-offset");
1297     _maxAnimationValue = value->simplify();
1298   }
1299
1300   _initialValue[0] = configNode->getDoubleValue("min-m", 0);
1301   _initialValue[0] *= configNode->getDoubleValue("min-factor", 1);
1302   _initialValue[1] = configNode->getDoubleValue("max-m", SGLimitsf::max());
1303   _initialValue[1] *= configNode->getDoubleValue("max-factor", 1);
1304 }
1305
1306 osg::Group*
1307 SGRangeAnimation::createAnimationGroup(osg::Group& parent)
1308 {
1309   osg::Group* group = new osg::Group;
1310   group->setName("range animation group");
1311
1312   osg::LOD* lod = new osg::LOD;
1313   lod->setName("range animation node");
1314   parent.addChild(lod);
1315
1316   lod->addChild(group, _initialValue[0], _initialValue[1]);
1317   lod->setCenterMode(osg::LOD::USE_BOUNDING_SPHERE_CENTER);
1318   lod->setRangeMode(osg::LOD::DISTANCE_FROM_EYE_POINT);
1319   if (_minAnimationValue || _maxAnimationValue || _condition) {
1320     UpdateCallback* uc;
1321     uc = new UpdateCallback(_condition, _minAnimationValue, _maxAnimationValue,
1322                             _initialValue[0], _initialValue[1]);
1323     lod->setUpdateCallback(uc);
1324   }
1325   return group;
1326 }
1327
1328 \f
1329 ////////////////////////////////////////////////////////////////////////
1330 // Implementation of a select animation
1331 ////////////////////////////////////////////////////////////////////////
1332
1333 class SGSelectAnimation::UpdateCallback : public osg::NodeCallback {
1334 public:
1335   UpdateCallback(const SGCondition* condition) :
1336     _condition(condition)
1337   {}
1338   virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1339   {
1340     osg::Switch* sw = static_cast<osg::Switch*>(node);
1341     if (_condition->test())
1342       sw->setAllChildrenOn();
1343     else
1344       sw->setAllChildrenOff();
1345     traverse(node, nv);
1346   }
1347
1348 private:
1349   SGSharedPtr<SGCondition const> _condition;
1350 };
1351
1352 SGSelectAnimation::SGSelectAnimation(const SGPropertyNode* configNode,
1353                                      SGPropertyNode* modelRoot) :
1354   SGAnimation(configNode, modelRoot)
1355 {
1356 }
1357
1358 osg::Group*
1359 SGSelectAnimation::createAnimationGroup(osg::Group& parent)
1360 {
1361   // if no condition given, this is a noop.
1362   SGSharedPtr<SGCondition const> condition = getCondition();
1363   // trick, gets deleted with all its 'animated' children
1364   // when the animation installer returns
1365   if (!condition)
1366     return new osg::Group;
1367
1368   osg::Switch* sw = new osg::Switch;
1369   sw->setName("select animation node");
1370   sw->setUpdateCallback(new UpdateCallback(condition));
1371   parent.addChild(sw);
1372   return sw;
1373 }
1374
1375
1376 \f
1377 ////////////////////////////////////////////////////////////////////////
1378 // Implementation of alpha test animation
1379 ////////////////////////////////////////////////////////////////////////
1380
1381 SGAlphaTestAnimation::SGAlphaTestAnimation(const SGPropertyNode* configNode,
1382                                            SGPropertyNode* modelRoot) :
1383   SGAnimation(configNode, modelRoot)
1384 {
1385 }
1386
1387 namespace
1388 {
1389 // Keep one copy of the most common alpha test its state set.
1390 OpenThreads::ReentrantMutex alphaTestMutex;
1391 osg::ref_ptr<osg::AlphaFunc> standardAlphaFunc;
1392 osg::ref_ptr<osg::StateSet> alphaFuncStateSet;
1393
1394 osg::AlphaFunc* makeAlphaFunc(float clamp)
1395 {
1396     using namespace OpenThreads;
1397     ScopedLock<ReentrantMutex> lock(alphaTestMutex);
1398     if (osg::equivalent(clamp, 0.01f)) {
1399         if (standardAlphaFunc.valid())
1400             return standardAlphaFunc.get();
1401         clamp = .01;
1402     }
1403     osg::AlphaFunc* alphaFunc = new osg::AlphaFunc;
1404     alphaFunc->setFunction(osg::AlphaFunc::GREATER);
1405     alphaFunc->setReferenceValue(clamp);
1406     alphaFunc->setDataVariance(osg::Object::STATIC);
1407     if (osg::equivalent(clamp, 0.01f))
1408         standardAlphaFunc = alphaFunc;
1409     return alphaFunc;
1410 }
1411
1412 osg::StateSet* makeAlphaTestStateSet(float clamp)
1413 {
1414     using namespace OpenThreads;
1415     ScopedLock<ReentrantMutex> lock(alphaTestMutex);
1416     if (osg::equivalent(clamp, 0.01f)) {
1417         if (alphaFuncStateSet.valid())
1418             return alphaFuncStateSet.get();
1419     }
1420     osg::AlphaFunc* alphaFunc = makeAlphaFunc(clamp);
1421     osg::StateSet* stateSet = new osg::StateSet;
1422     stateSet->setAttributeAndModes(alphaFunc,
1423                                    (osg::StateAttribute::ON
1424                                     | osg::StateAttribute::OVERRIDE));
1425     stateSet->setDataVariance(osg::Object::STATIC);
1426     if (osg::equivalent(clamp, 0.01f))
1427         alphaFuncStateSet = stateSet;
1428     return stateSet;
1429 }
1430 }
1431 void
1432 SGAlphaTestAnimation::install(osg::Node& node)
1433 {
1434   SGAnimation::install(node);
1435
1436   float alphaClamp = getConfig()->getFloatValue("alpha-factor", 0);
1437   osg::StateSet* stateSet = node.getStateSet();
1438   if (!stateSet) {
1439       node.setStateSet(makeAlphaTestStateSet(alphaClamp));
1440   } else {
1441       stateSet->setAttributeAndModes(makeAlphaFunc(alphaClamp),
1442                                      (osg::StateAttribute::ON
1443                                       | osg::StateAttribute::OVERRIDE));
1444   }
1445 }
1446
1447 \f
1448 //////////////////////////////////////////////////////////////////////
1449 // Blend animation installer
1450 //////////////////////////////////////////////////////////////////////
1451
1452 // XXX This needs to be replaced by something using TexEnvCombine to
1453 // change the blend factor. Changing the alpha values in the geometry
1454 // is bogus.
1455 class SGBlendAnimation::BlendVisitor : public osg::NodeVisitor {
1456 public:
1457   BlendVisitor(float blend) :
1458     osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
1459     _blend(blend)
1460   { setVisitorType(osg::NodeVisitor::NODE_VISITOR); }
1461   virtual void apply(osg::Node& node)
1462   {
1463     updateStateSet(node.getStateSet());
1464     traverse(node);
1465   }
1466   virtual void apply(osg::Geode& node)
1467   {
1468     apply((osg::Node&)node);
1469     unsigned nDrawables = node.getNumDrawables();
1470     for (unsigned i = 0; i < nDrawables; ++i) {
1471       osg::Drawable* drawable = node.getDrawable(i);
1472       osg::Geometry* geometry = drawable->asGeometry();
1473       if (!geometry)
1474         continue;
1475       osg::Array* array = geometry->getColorArray();
1476       if (!array)
1477         continue;
1478       osg::Vec4Array* vec4Array = dynamic_cast<osg::Vec4Array*>(array);
1479       if (!vec4Array)
1480         continue;
1481       for (unsigned k = 0; k < vec4Array->size(); ++k) {
1482         (*vec4Array)[k][3] = _blend;
1483       }
1484       vec4Array->dirty();
1485       updateStateSet(drawable->getStateSet());
1486     }
1487   }
1488   void updateStateSet(osg::StateSet* stateSet)
1489   {
1490     if (!stateSet)
1491       return;
1492     osg::StateAttribute* stateAttribute;
1493     stateAttribute = stateSet->getAttribute(osg::StateAttribute::MATERIAL);
1494     if (!stateAttribute)
1495       return;
1496     osg::Material* material = dynamic_cast<osg::Material*>(stateAttribute);
1497     if (!material)
1498       return;
1499     material->setAlpha(osg::Material::FRONT_AND_BACK, _blend);
1500     if (_blend < 1) {
1501       stateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
1502       stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
1503     } else {
1504       stateSet->setRenderingHint(osg::StateSet::DEFAULT_BIN);
1505     }
1506   }
1507 private:
1508   float _blend;
1509 };
1510
1511 class SGBlendAnimation::UpdateCallback : public osg::NodeCallback {
1512 public:
1513   UpdateCallback(const SGPropertyNode* configNode, const SGExpressiond* v) :
1514     _prev_value(-1),
1515     _animationValue(v)
1516   { }
1517   virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1518   {
1519     double blend = _animationValue->getValue();
1520     if (blend != _prev_value) {
1521       _prev_value = blend;
1522       BlendVisitor visitor(1-blend);
1523       node->accept(visitor);
1524     }
1525     traverse(node, nv);
1526   }
1527 public:
1528   double _prev_value;
1529   SGSharedPtr<SGExpressiond const> _animationValue;
1530 };
1531
1532
1533 SGBlendAnimation::SGBlendAnimation(const SGPropertyNode* configNode,
1534                                    SGPropertyNode* modelRoot)
1535   : SGAnimation(configNode, modelRoot),
1536     _animationValue(read_value(configNode, modelRoot, "", 0, 1))
1537 {
1538 }
1539
1540 osg::Group*
1541 SGBlendAnimation::createAnimationGroup(osg::Group& parent)
1542 {
1543   if (!_animationValue)
1544     return 0;
1545
1546   osg::Group* group = new osg::Switch;
1547   group->setName("blend animation node");
1548   group->setUpdateCallback(new UpdateCallback(getConfig(), _animationValue));
1549   parent.addChild(group);
1550   return group;
1551 }
1552
1553 void
1554 SGBlendAnimation::install(osg::Node& node)
1555 {
1556   SGAnimation::install(node);
1557   // make sure we do not change common geometries,
1558   // that also creates new display lists for these subgeometries.
1559   cloneDrawables(node);
1560   DoDrawArraysVisitor visitor;
1561   node.accept(visitor);
1562 }
1563
1564 \f
1565 //////////////////////////////////////////////////////////////////////
1566 // Timed animation installer
1567 //////////////////////////////////////////////////////////////////////
1568
1569
1570
1571 class SGTimedAnimation::UpdateCallback : public osg::NodeCallback {
1572 public:
1573   UpdateCallback(const SGPropertyNode* configNode) :
1574     _current_index(0),
1575     _reminder(0),
1576     _duration_sec(configNode->getDoubleValue("duration-sec", 1)),
1577     _last_time_sec(SGLimitsd::max()),
1578     _use_personality(configNode->getBoolValue("use-personality", false))
1579   {
1580     std::vector<SGSharedPtr<SGPropertyNode> > nodes;
1581     nodes = configNode->getChildren("branch-duration-sec");
1582     for (size_t i = 0; i < nodes.size(); ++i) {
1583       unsigned ind = nodes[ i ]->getIndex();
1584       while ( ind >= _durations.size() ) {
1585         _durations.push_back(DurationSpec(_duration_sec));
1586       }
1587       SGPropertyNode_ptr rNode = nodes[i]->getChild("random");
1588       if ( rNode == 0 ) {
1589         _durations[ind] = DurationSpec(nodes[ i ]->getDoubleValue());
1590       } else {
1591         _durations[ind] = DurationSpec(rNode->getDoubleValue( "min", 0),
1592                                        rNode->getDoubleValue( "max", 1));
1593       }
1594     }
1595   }
1596   virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1597   {
1598     assert(dynamic_cast<osg::Switch*>(node));
1599     osg::Switch* sw = static_cast<osg::Switch*>(node);
1600
1601     unsigned nChildren = sw->getNumChildren();
1602
1603     // blow up the durations vector to the required size
1604     while (_durations.size() < nChildren) {
1605       _durations.push_back(_duration_sec);
1606     }
1607     // make sure the current index is an duration that really exists
1608     _current_index = _current_index % nChildren;
1609
1610     // update the time and compute the current systems time value
1611     double t = nv->getFrameStamp()->getReferenceTime();
1612     if (_last_time_sec == SGLimitsd::max()) {
1613       _last_time_sec = t;
1614     } else {
1615       double dt = t - _last_time_sec;
1616       if (_use_personality)
1617         dt *= 1 + 0.2*(0.5 - sg_random());
1618       _reminder += dt;
1619       _last_time_sec = t;
1620     }
1621
1622     double currentDuration = _durations[_current_index].get();
1623     while (currentDuration < _reminder) {
1624       _reminder -= currentDuration;
1625       _current_index = (_current_index + 1) % nChildren;
1626       currentDuration = _durations[_current_index].get();
1627     }
1628
1629     sw->setSingleChildOn(_current_index);
1630
1631     traverse(node, nv);
1632   }
1633
1634 private:
1635   struct DurationSpec {
1636     DurationSpec(double t) :
1637       minTime(SGMiscd::max(0.01, t)),
1638       maxTime(SGMiscd::max(0.01, t))
1639     {}
1640     DurationSpec(double t0, double t1) :
1641       minTime(SGMiscd::max(0.01, t0)),
1642       maxTime(SGMiscd::max(0.01, t1))
1643     {}
1644     double get() const
1645     { return minTime + sg_random()*(maxTime - minTime); }
1646     double minTime;
1647     double maxTime;
1648   };
1649   std::vector<DurationSpec> _durations;
1650   unsigned _current_index;
1651   double _reminder;
1652   double _duration_sec;
1653   double _last_time_sec;
1654   bool _use_personality;
1655 };
1656
1657
1658 SGTimedAnimation::SGTimedAnimation(const SGPropertyNode* configNode,
1659                                    SGPropertyNode* modelRoot)
1660   : SGAnimation(configNode, modelRoot)
1661 {
1662 }
1663
1664 osg::Group*
1665 SGTimedAnimation::createAnimationGroup(osg::Group& parent)
1666 {
1667   osg::Switch* sw = new osg::Switch;
1668   sw->setName("timed animation node");
1669   sw->setUpdateCallback(new UpdateCallback(getConfig()));
1670   parent.addChild(sw);
1671   return sw;
1672 }
1673
1674 \f
1675 ////////////////////////////////////////////////////////////////////////
1676 // dynamically switch on/off shadows
1677 ////////////////////////////////////////////////////////////////////////
1678
1679 class SGShadowAnimation::UpdateCallback : public osg::NodeCallback {
1680 public:
1681   UpdateCallback(const SGCondition* condition) :
1682     _condition(condition)
1683   {}
1684   virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1685   {
1686     if (_condition->test())
1687       node->setNodeMask( SG_NODEMASK_CASTSHADOW_BIT | node->getNodeMask());
1688     else
1689       node->setNodeMask(~SG_NODEMASK_CASTSHADOW_BIT & node->getNodeMask());
1690     traverse(node, nv);
1691   }
1692
1693 private:
1694   SGSharedPtr<const SGCondition> _condition;
1695 };
1696
1697 SGShadowAnimation::SGShadowAnimation(const SGPropertyNode* configNode,
1698                                      SGPropertyNode* modelRoot) :
1699   SGAnimation(configNode, modelRoot)
1700 {
1701 }
1702
1703 osg::Group*
1704 SGShadowAnimation::createAnimationGroup(osg::Group& parent)
1705 {
1706   SGSharedPtr<SGCondition const> condition = getCondition();
1707   if (!condition)
1708     return 0;
1709
1710   osg::Group* group = new osg::Group;
1711   group->setName("shadow animation");
1712   group->setUpdateCallback(new UpdateCallback(condition));
1713   parent.addChild(group);
1714   return group;
1715 }
1716
1717 \f
1718 ////////////////////////////////////////////////////////////////////////
1719 // Implementation of SGTexTransformAnimation
1720 ////////////////////////////////////////////////////////////////////////
1721
1722 class SGTexTransformAnimation::Transform : public SGReferenced {
1723 public:
1724   Transform() :
1725     _value(0)
1726   {}
1727   virtual ~Transform()
1728   { }
1729   void setValue(double value)
1730   { _value = value; }
1731   virtual void transform(osg::Matrix&) = 0;
1732 protected:
1733   double _value;
1734 };
1735
1736 class SGTexTransformAnimation::Translation :
1737   public SGTexTransformAnimation::Transform {
1738 public:
1739   Translation(const SGVec3d& axis) :
1740     _axis(axis)
1741   { }
1742   virtual void transform(osg::Matrix& matrix)
1743   {
1744     osg::Matrix tmp;
1745     set_translation(tmp, _value, _axis);
1746     matrix.preMult(tmp);
1747   }
1748 private:
1749   SGVec3d _axis;
1750 };
1751
1752 class SGTexTransformAnimation::Rotation :
1753   public SGTexTransformAnimation::Transform {
1754 public:
1755   Rotation(const SGVec3d& axis, const SGVec3d& center) :
1756     _axis(axis),
1757     _center(center)
1758   { }
1759   virtual void transform(osg::Matrix& matrix)
1760   {
1761     osg::Matrix tmp;
1762     set_rotation(tmp, _value, _center, _axis);
1763     matrix.preMult(tmp);
1764   }
1765 private:
1766   SGVec3d _axis;
1767   SGVec3d _center;
1768 };
1769
1770 class SGTexTransformAnimation::UpdateCallback :
1771   public osg::StateAttribute::Callback {
1772 public:
1773   UpdateCallback(const SGCondition* condition) :
1774     _condition(condition)
1775   { }
1776   virtual void operator () (osg::StateAttribute* sa, osg::NodeVisitor*)
1777   {
1778     if (!_condition || _condition->test()) {
1779       TransformList::const_iterator i;
1780       for (i = _transforms.begin(); i != _transforms.end(); ++i)
1781         i->transform->setValue(i->value->getValue());
1782     }
1783     assert(dynamic_cast<osg::TexMat*>(sa));
1784     osg::TexMat* texMat = static_cast<osg::TexMat*>(sa);
1785     texMat->getMatrix().makeIdentity();
1786     TransformList::const_iterator i;
1787     for (i = _transforms.begin(); i != _transforms.end(); ++i)
1788       i->transform->transform(texMat->getMatrix());
1789   }
1790   void appendTransform(Transform* transform, SGExpressiond* value)
1791   {
1792     Entry entry = { transform, value };
1793     transform->transform(_matrix);
1794     _transforms.push_back(entry);
1795   }
1796
1797 private:
1798   struct Entry {
1799     SGSharedPtr<Transform> transform;
1800     SGSharedPtr<const SGExpressiond> value;
1801   };
1802   typedef std::vector<Entry> TransformList;
1803   TransformList _transforms;
1804   SGSharedPtr<const SGCondition> _condition;
1805   osg::Matrix _matrix;
1806 };
1807
1808 SGTexTransformAnimation::SGTexTransformAnimation(const SGPropertyNode* configNode,
1809                                                  SGPropertyNode* modelRoot) :
1810   SGAnimation(configNode, modelRoot)
1811 {
1812 }
1813
1814 osg::Group*
1815 SGTexTransformAnimation::createAnimationGroup(osg::Group& parent)
1816 {
1817   osg::Group* group = new osg::Group;
1818   group->setName("texture transform group");
1819   osg::StateSet* stateSet = group->getOrCreateStateSet();
1820   osg::TexMat* texMat = new osg::TexMat;
1821   UpdateCallback* updateCallback = new UpdateCallback(getCondition());
1822   // interpret the configs ...
1823   std::string type = getType();
1824
1825   if (type == "textranslate") {
1826     appendTexTranslate(getConfig(), updateCallback);
1827   } else if (type == "texrotate") {
1828     appendTexRotate(getConfig(), updateCallback);
1829   } else if (type == "texmultiple") {
1830     std::vector<SGSharedPtr<SGPropertyNode> > transformConfigs;
1831     transformConfigs = getConfig()->getChildren("transform");
1832     for (unsigned i = 0; i < transformConfigs.size(); ++i) {
1833       std::string subtype = transformConfigs[i]->getStringValue("subtype", "");
1834       if (subtype == "textranslate")
1835         appendTexTranslate(transformConfigs[i], updateCallback);
1836       else if (subtype == "texrotate")
1837         appendTexRotate(transformConfigs[i], updateCallback);
1838       else
1839         SG_LOG(SG_INPUT, SG_ALERT,
1840                "Ignoring unknown texture transform subtype");
1841     }
1842   } else {
1843     SG_LOG(SG_INPUT, SG_ALERT, "Ignoring unknown texture transform type");
1844   }
1845
1846   texMat->setUpdateCallback(updateCallback);
1847   stateSet->setTextureAttribute(0, texMat);
1848   parent.addChild(group);
1849   return group;
1850 }
1851
1852 void
1853 SGTexTransformAnimation::appendTexTranslate(const SGPropertyNode* config,
1854                                             UpdateCallback* updateCallback)
1855 {
1856   std::string propertyName = config->getStringValue("property", "");
1857   SGSharedPtr<SGExpressiond> value;
1858   if (propertyName.empty())
1859     value = new SGConstExpression<double>(0);
1860   else {
1861     SGPropertyNode* inputProperty = getModelRoot()->getNode(propertyName, true);
1862     value = new SGPropertyExpression<double>(inputProperty);
1863   }
1864
1865   SGInterpTable* table = read_interpolation_table(config);
1866   if (table) {
1867     value = new SGInterpTableExpression<double>(value, table);
1868     double biasValue = config->getDoubleValue("bias", 0);
1869     if (biasValue != 0)
1870       value = new SGBiasExpression<double>(value, biasValue);
1871     value = new SGStepExpression<double>(value,
1872                                          config->getDoubleValue("step", 0),
1873                                          config->getDoubleValue("scroll", 0));
1874     value = value->simplify();
1875   } else {
1876     double biasValue = config->getDoubleValue("bias", 0);
1877     if (biasValue != 0)
1878       value = new SGBiasExpression<double>(value, biasValue);
1879     value = new SGStepExpression<double>(value,
1880                                          config->getDoubleValue("step", 0),
1881                                          config->getDoubleValue("scroll", 0));
1882     value = read_offset_factor(config, value, "factor", "offset");
1883
1884     if (config->hasChild("min") || config->hasChild("max")) {
1885       double minClip = config->getDoubleValue("min", -SGLimitsd::max());
1886       double maxClip = config->getDoubleValue("max", SGLimitsd::max());
1887       value = new SGClipExpression<double>(value, minClip, maxClip);
1888     }
1889     value = value->simplify();
1890   }
1891   SGVec3d axis(config->getDoubleValue("axis/x", 0),
1892                config->getDoubleValue("axis/y", 0),
1893                config->getDoubleValue("axis/z", 0));
1894   Translation* translation;
1895   translation = new Translation(normalize(axis));
1896   translation->setValue(config->getDoubleValue("starting-position", 0));
1897   updateCallback->appendTransform(translation, value);
1898 }
1899
1900 void
1901 SGTexTransformAnimation::appendTexRotate(const SGPropertyNode* config,
1902                                          UpdateCallback* updateCallback)
1903 {
1904   std::string propertyName = config->getStringValue("property", "");
1905   SGSharedPtr<SGExpressiond> value;
1906   if (propertyName.empty())
1907     value = new SGConstExpression<double>(0);
1908   else {
1909     SGPropertyNode* inputProperty = getModelRoot()->getNode(propertyName, true);
1910     value = new SGPropertyExpression<double>(inputProperty);
1911   }
1912
1913   SGInterpTable* table = read_interpolation_table(config);
1914   if (table) {
1915     value = new SGInterpTableExpression<double>(value, table);
1916     double biasValue = config->getDoubleValue("bias", 0);
1917     if (biasValue != 0)
1918       value = new SGBiasExpression<double>(value, biasValue);
1919     value = new SGStepExpression<double>(value,
1920                                          config->getDoubleValue("step", 0),
1921                                          config->getDoubleValue("scroll", 0));
1922     value = value->simplify();
1923   } else {
1924     double biasValue = config->getDoubleValue("bias", 0);
1925     if (biasValue != 0)
1926       value = new SGBiasExpression<double>(value, biasValue);
1927     value = new SGStepExpression<double>(value,
1928                                          config->getDoubleValue("step", 0),
1929                                          config->getDoubleValue("scroll", 0));
1930     value = read_offset_factor(config, value, "factor", "offset-deg");
1931
1932     if (config->hasChild("min-deg") || config->hasChild("max-deg")) {
1933       double minClip = config->getDoubleValue("min-deg", -SGLimitsd::max());
1934       double maxClip = config->getDoubleValue("max-deg", SGLimitsd::max());
1935       value = new SGClipExpression<double>(value, minClip, maxClip);
1936     }
1937     value = value->simplify();
1938   }
1939   SGVec3d axis(config->getDoubleValue("axis/x", 0),
1940                config->getDoubleValue("axis/y", 0),
1941                config->getDoubleValue("axis/z", 0));
1942   SGVec3d center(config->getDoubleValue("center/x", 0),
1943                  config->getDoubleValue("center/y", 0),
1944                  config->getDoubleValue("center/z", 0));
1945   Rotation* rotation;
1946   rotation = new Rotation(normalize(axis), center);
1947   rotation->setValue(config->getDoubleValue("starting-position-deg", 0));
1948   updateCallback->appendTransform(rotation, value);
1949 }
1950
1951
1952 ////////////////////////////////////////////////////////////////////////
1953 // Implementation of SGPickAnimation
1954 ////////////////////////////////////////////////////////////////////////
1955
1956 class SGPickAnimation::PickCallback : public SGPickCallback {
1957 public:
1958   PickCallback(const SGPropertyNode* configNode,
1959                SGPropertyNode* modelRoot) :
1960     _button(configNode->getIntValue("button", -1)),
1961     _repeatable(configNode->getBoolValue("repeatable", false)),
1962     _repeatInterval(configNode->getDoubleValue("interval-sec", 0.1))
1963   {
1964     SG_LOG(SG_INPUT, SG_DEBUG, "Reading all bindings");
1965     std::vector<SGPropertyNode_ptr> bindings;
1966     bindings = configNode->getChildren("binding");
1967     for (unsigned int i = 0; i < bindings.size(); ++i) {
1968       _bindingsDown.push_back(new SGBinding(bindings[i], modelRoot));
1969     }
1970
1971     const SGPropertyNode* upNode = configNode->getChild("mod-up");
1972     if (!upNode)
1973       return;
1974     bindings = upNode->getChildren("binding");
1975     for (unsigned int i = 0; i < bindings.size(); ++i) {
1976       _bindingsUp.push_back(new SGBinding(bindings[i], modelRoot));
1977     }
1978   }
1979   virtual bool buttonPressed(int button, const Info&)
1980   {
1981     if (0 <= _button && button != _button)
1982       return false;
1983     SGBindingList::const_iterator i;
1984     for (i = _bindingsDown.begin(); i != _bindingsDown.end(); ++i)
1985       (*i)->fire();
1986     _repeatTime = 0;
1987     return true;
1988   }
1989   virtual void buttonReleased(void)
1990   {
1991     SGBindingList::const_iterator i;
1992     for (i = _bindingsUp.begin(); i != _bindingsUp.end(); ++i)
1993       (*i)->fire();
1994   }
1995   virtual void update(double dt)
1996   {
1997     if (!_repeatable)
1998       return;
1999
2000     _repeatTime += dt;
2001     while (_repeatInterval < _repeatTime) {
2002       _repeatTime -= _repeatInterval;
2003       SGBindingList::const_iterator i;
2004       for (i = _bindingsDown.begin(); i != _bindingsDown.end(); ++i)
2005         (*i)->fire();
2006     }
2007   }
2008 private:
2009   SGBindingList _bindingsDown;
2010   SGBindingList _bindingsUp;
2011   int _button;
2012   bool _repeatable;
2013   double _repeatInterval;
2014   double _repeatTime;
2015 };
2016
2017 SGPickAnimation::SGPickAnimation(const SGPropertyNode* configNode,
2018                                  SGPropertyNode* modelRoot) :
2019   SGAnimation(configNode, modelRoot)
2020 {
2021 }
2022
2023 osg::Group*
2024 SGPickAnimation::createAnimationGroup(osg::Group& parent)
2025 {
2026   osg::Group* commonGroup = new osg::Group;
2027
2028   // Contains the normal geometry that is interactive
2029   osg::ref_ptr<osg::Group> normalGroup = new osg::Group;
2030   normalGroup->setName("pick normal group");
2031   normalGroup->addChild(commonGroup);
2032
2033   // Used to render the geometry with just yellow edges
2034   osg::Group* highlightGroup = new osg::Group;
2035   highlightGroup->setName("pick highlight group");
2036   highlightGroup->setNodeMask(SG_NODEMASK_PICK_BIT);
2037   highlightGroup->addChild(commonGroup);
2038   SGSceneUserData* ud;
2039   ud = SGSceneUserData::getOrCreateSceneUserData(commonGroup);
2040   std::vector<SGPropertyNode_ptr> actions;
2041   actions = getConfig()->getChildren("action");
2042   for (unsigned int i = 0; i < actions.size(); ++i)
2043     ud->addPickCallback(new PickCallback(actions[i], getModelRoot()));
2044
2045   // prepare a state set that paints the edges of this object yellow
2046   osg::StateSet* stateSet = highlightGroup->getOrCreateStateSet();
2047   stateSet->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE);
2048
2049   osg::PolygonOffset* polygonOffset = new osg::PolygonOffset;
2050   polygonOffset->setFactor(-1);
2051   polygonOffset->setUnits(-1);
2052   stateSet->setAttribute(polygonOffset, osg::StateAttribute::OVERRIDE);
2053   stateSet->setMode(GL_POLYGON_OFFSET_LINE, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
2054
2055   osg::PolygonMode* polygonMode = new osg::PolygonMode;
2056   polygonMode->setMode(osg::PolygonMode::FRONT_AND_BACK,
2057                        osg::PolygonMode::LINE);
2058   stateSet->setAttribute(polygonMode, osg::StateAttribute::OVERRIDE);
2059   
2060   osg::Material* material = new osg::Material;
2061   material->setColorMode(osg::Material::OFF);
2062   material->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(1, 1, 0, 1));
2063   material->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(1, 1, 0, 1));
2064   material->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(1, 1, 0, 1));
2065   material->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0, 0, 0, 0));
2066   stateSet->setAttribute(material, osg::StateAttribute::OVERRIDE);
2067
2068   // Only add normal geometry if configured
2069   if (getConfig()->getBoolValue("visible", true))
2070     parent.addChild(normalGroup.get());
2071   parent.addChild(highlightGroup);
2072
2073   return commonGroup;
2074 }