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