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