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