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