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