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