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