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