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