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