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