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