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