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