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