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