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