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