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