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