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