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