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