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