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