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