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