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