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