]> git.mxchange.org Git - simgear.git/blob - simgear/scene/model/animation.cxx
From Benoit Laniel: replace SG threading constructs with those from OpenThreads
[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   _axis[0] = configNode->getDoubleValue("axis/x", 0);
659   _axis[1] = configNode->getDoubleValue("axis/y", 0);
660   _axis[2] = configNode->getDoubleValue("axis/z", 0);
661   if (8*SGLimitsd::min() < norm(_axis))
662     _axis = normalize(_axis);
663 }
664
665 osg::Group*
666 SGTranslateAnimation::createAnimationGroup(osg::Group& parent)
667 {
668   SGTranslateTransform* transform = new SGTranslateTransform;
669   transform->setName("translate animation");
670   if (_animationValue && !_animationValue->isConst()) {
671     UpdateCallback* uc = new UpdateCallback(_condition, _animationValue);
672     transform->setUpdateCallback(uc);
673   }
674   transform->setAxis(_axis);
675   transform->setValue(_initialValue);
676   parent.addChild(transform);
677   return transform;
678 }
679
680 \f
681 ////////////////////////////////////////////////////////////////////////
682 // Implementation of rotate/spin animation
683 ////////////////////////////////////////////////////////////////////////
684
685 class SGRotateAnimation::UpdateCallback : public osg::NodeCallback {
686 public:
687   UpdateCallback(SGCondition const* condition,
688                  SGExpressiond const* animationValue) :
689     _condition(condition),
690     _animationValue(animationValue)
691   { }
692   virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
693   {
694     if (!_condition || _condition->test()) {
695       SGRotateTransform* transform;
696       transform = static_cast<SGRotateTransform*>(node);
697       transform->setAngleDeg(_animationValue->getValue());
698     }
699     traverse(node, nv);
700   }
701 public:
702   SGSharedPtr<SGCondition const> _condition;
703   SGSharedPtr<SGExpressiond const> _animationValue;
704 };
705
706 class SGRotateAnimation::SpinUpdateCallback : public osg::NodeCallback {
707 public:
708   SpinUpdateCallback(SGCondition const* condition,
709                      SGExpressiond const* animationValue) :
710     _condition(condition),
711     _animationValue(animationValue),
712     _lastTime(-1)
713   { }
714   virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
715   {
716     if (!_condition || _condition->test()) {
717       SGRotateTransform* transform;
718       transform = static_cast<SGRotateTransform*>(node);
719
720       double t = nv->getFrameStamp()->getReferenceTime();
721       double dt = 0;
722       if (0 <= _lastTime)
723         dt = t - _lastTime;
724       _lastTime = t;
725       double velocity_rpms = _animationValue->getValue()/60;
726       double angle = transform->getAngleDeg();
727       angle += dt*velocity_rpms*360;
728       angle -= 360*floor(angle/360);
729       transform->setAngleDeg(angle);
730     }
731     traverse(node, nv);
732   }
733 public:
734   SGSharedPtr<SGCondition const> _condition;
735   SGSharedPtr<SGExpressiond const> _animationValue;
736   double _lastTime;
737 };
738
739 SGRotateAnimation::SGRotateAnimation(const SGPropertyNode* configNode,
740                                      SGPropertyNode* modelRoot) :
741   SGAnimation(configNode, modelRoot)
742 {
743   std::string type = configNode->getStringValue("type", "");
744   _isSpin = (type == "spin");
745
746   _condition = getCondition();
747   SGSharedPtr<SGExpressiond> value;
748   value = read_value(configNode, modelRoot, "-deg",
749                      -SGLimitsd::max(), SGLimitsd::max());
750   _animationValue = value->simplify();
751   if (_animationValue)
752     _initialValue = _animationValue->getValue();
753   else
754     _initialValue = 0;
755
756   _center = SGVec3d::zeros();
757   if (configNode->hasValue("axis/x1-m")) {
758     SGVec3d v1, v2;
759     v1[0] = configNode->getDoubleValue("axis/x1-m", 0);
760     v1[1] = configNode->getDoubleValue("axis/y1-m", 0);
761     v1[2] = configNode->getDoubleValue("axis/z1-m", 0);
762     v2[0] = configNode->getDoubleValue("axis/x2-m", 0);
763     v2[1] = configNode->getDoubleValue("axis/y2-m", 0);
764     v2[2] = configNode->getDoubleValue("axis/z2-m", 0);
765     _center = 0.5*(v1+v2);
766     _axis = v2 - v1;
767   } else {
768     _axis[0] = configNode->getDoubleValue("axis/x", 0);
769     _axis[1] = configNode->getDoubleValue("axis/y", 0);
770     _axis[2] = configNode->getDoubleValue("axis/z", 0);
771   }
772   if (8*SGLimitsd::min() < norm(_axis))
773     _axis = normalize(_axis);
774
775   _center[0] = configNode->getDoubleValue("center/x-m", _center[0]);
776   _center[1] = configNode->getDoubleValue("center/y-m", _center[1]);
777   _center[2] = configNode->getDoubleValue("center/z-m", _center[2]);
778 }
779
780 osg::Group*
781 SGRotateAnimation::createAnimationGroup(osg::Group& parent)
782 {
783   SGRotateTransform* transform = new SGRotateTransform;
784   transform->setName("rotate animation");
785   if (_isSpin) {
786     SpinUpdateCallback* uc;
787     uc = new SpinUpdateCallback(_condition, _animationValue);
788     transform->setUpdateCallback(uc);
789   } else if (_animationValue || !_animationValue->isConst()) {
790     UpdateCallback* uc = new UpdateCallback(_condition, _animationValue);
791     transform->setUpdateCallback(uc);
792   }
793   transform->setCenter(_center);
794   transform->setAxis(_axis);
795   transform->setAngleDeg(_initialValue);
796   parent.addChild(transform);
797   return transform;
798 }
799
800 \f
801 ////////////////////////////////////////////////////////////////////////
802 // Implementation of scale animation
803 ////////////////////////////////////////////////////////////////////////
804
805 class SGScaleAnimation::UpdateCallback : public osg::NodeCallback {
806 public:
807   UpdateCallback(const SGCondition* condition,
808                  SGSharedPtr<const SGExpressiond> animationValue[3]) :
809     _condition(condition)
810   {
811     _animationValue[0] = animationValue[0];
812     _animationValue[1] = animationValue[1];
813     _animationValue[2] = animationValue[2];
814   }
815   virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
816   {
817     if (!_condition || _condition->test()) {
818       SGScaleTransform* transform;
819       transform = static_cast<SGScaleTransform*>(node);
820       SGVec3d scale(_animationValue[0]->getValue(),
821                     _animationValue[1]->getValue(),
822                     _animationValue[2]->getValue());
823       transform->setScaleFactor(scale);
824     }
825     traverse(node, nv);
826   }
827 public:
828   SGSharedPtr<SGCondition const> _condition;
829   SGSharedPtr<SGExpressiond const> _animationValue[3];
830 };
831
832 SGScaleAnimation::SGScaleAnimation(const SGPropertyNode* configNode,
833                                    SGPropertyNode* modelRoot) :
834   SGAnimation(configNode, modelRoot)
835 {
836   _condition = getCondition();
837
838   // default offset/factor for all directions
839   double offset = configNode->getDoubleValue("offset", 0);
840   double factor = configNode->getDoubleValue("factor", 1);
841
842   SGSharedPtr<SGExpressiond> inPropExpr;
843
844   std::string inputPropertyName;
845   inputPropertyName = configNode->getStringValue("property", "");
846   if (inputPropertyName.empty()) {
847     inPropExpr = new SGConstExpression<double>(0);
848   } else {
849     SGPropertyNode* inputProperty;
850     inputProperty = modelRoot->getNode(inputPropertyName, true);
851     inPropExpr = new SGPropertyExpression<double>(inputProperty);
852   }
853
854   SGInterpTable* interpTable = read_interpolation_table(configNode);
855   if (interpTable) {
856     SGSharedPtr<SGExpressiond> value;
857     value = new SGInterpTableExpression<double>(inPropExpr, interpTable);
858     _animationValue[0] = value->simplify();
859     _animationValue[1] = value->simplify();
860     _animationValue[2] = value->simplify();
861   } else if (configNode->getBoolValue("use-personality", false)) {
862     SGSharedPtr<SGExpressiond> value;
863     value = new SGPersonalityScaleOffsetExpression(inPropExpr, configNode,
864                                                    "x-factor", "x-offset",
865                                                    factor, offset);
866     double minClip = configNode->getDoubleValue("x-min", 0);
867     double maxClip = configNode->getDoubleValue("x-max", SGLimitsd::max());
868     value = new SGClipExpression<double>(value, minClip, maxClip);
869     _animationValue[0] = value->simplify();
870     
871     value = new SGPersonalityScaleOffsetExpression(inPropExpr, configNode,
872                                                    "y-factor", "y-offset",
873                                                    factor, offset);
874     minClip = configNode->getDoubleValue("y-min", 0);
875     maxClip = configNode->getDoubleValue("y-max", SGLimitsd::max());
876     value = new SGClipExpression<double>(value, minClip, maxClip);
877     _animationValue[1] = value->simplify();
878     
879     value = new SGPersonalityScaleOffsetExpression(inPropExpr, configNode,
880                                                    "z-factor", "z-offset",
881                                                    factor, offset);
882     minClip = configNode->getDoubleValue("z-min", 0);
883     maxClip = configNode->getDoubleValue("z-max", SGLimitsd::max());
884     value = new SGClipExpression<double>(value, minClip, maxClip);
885     _animationValue[2] = value->simplify();
886   } else {
887     SGSharedPtr<SGExpressiond> value;
888     value = read_factor_offset(configNode, inPropExpr, "x-factor", "x-offset");
889     double minClip = configNode->getDoubleValue("x-min", 0);
890     double maxClip = configNode->getDoubleValue("x-max", SGLimitsd::max());
891     value = new SGClipExpression<double>(value, minClip, maxClip);
892     _animationValue[0] = value->simplify();
893
894     value = read_factor_offset(configNode, inPropExpr, "y-factor", "y-offset");
895     minClip = configNode->getDoubleValue("y-min", 0);
896     maxClip = configNode->getDoubleValue("y-max", SGLimitsd::max());
897     value = new SGClipExpression<double>(value, minClip, maxClip);
898     _animationValue[1] = value->simplify();
899
900     value = read_factor_offset(configNode, inPropExpr, "z-factor", "z-offset");
901     minClip = configNode->getDoubleValue("z-min", 0);
902     maxClip = configNode->getDoubleValue("z-max", SGLimitsd::max());
903     value = new SGClipExpression<double>(value, minClip, maxClip);
904     _animationValue[2] = value->simplify();
905   }
906   _initialValue[0] = configNode->getDoubleValue("x-starting-scale", 1);
907   _initialValue[0] *= configNode->getDoubleValue("x-factor", factor);
908   _initialValue[0] += configNode->getDoubleValue("x-offset", offset);
909   _initialValue[1] = configNode->getDoubleValue("y-starting-scale", 1);
910   _initialValue[1] *= configNode->getDoubleValue("y-factor", factor);
911   _initialValue[1] += configNode->getDoubleValue("y-offset", offset);
912   _initialValue[2] = configNode->getDoubleValue("z-starting-scale", 1);
913   _initialValue[2] *= configNode->getDoubleValue("z-factor", factor);
914   _initialValue[2] += configNode->getDoubleValue("z-offset", offset);
915   _center[0] = configNode->getDoubleValue("center/x-m", 0);
916   _center[1] = configNode->getDoubleValue("center/y-m", 0);
917   _center[2] = configNode->getDoubleValue("center/z-m", 0);
918 }
919
920 osg::Group*
921 SGScaleAnimation::createAnimationGroup(osg::Group& parent)
922 {
923   SGScaleTransform* transform = new SGScaleTransform;
924   transform->setName("scale animation");
925   transform->setCenter(_center);
926   transform->setScaleFactor(_initialValue);
927   UpdateCallback* uc = new UpdateCallback(_condition, _animationValue);
928   transform->setUpdateCallback(uc);
929   parent.addChild(transform);
930   return transform;
931 }
932
933 \f
934 // Don't create a new state state everytime we need GL_NORMALIZE!
935
936 namespace
937 {
938 Mutex normalizeMutex;
939
940 osg::StateSet* getNormalizeStateSet()
941 {
942     static osg::ref_ptr<osg::StateSet> normalizeStateSet;
943     ScopedLock<Mutex> lock(normalizeMutex);
944     if (!normalizeStateSet.valid()) {
945         normalizeStateSet = new osg::StateSet;
946         normalizeStateSet->setMode(GL_NORMALIZE, osg::StateAttribute::ON);
947         normalizeStateSet->setDataVariance(osg::Object::STATIC);
948     }
949     return normalizeStateSet.get();
950 }
951 }
952
953 ////////////////////////////////////////////////////////////////////////
954 // Implementation of dist scale animation
955 ////////////////////////////////////////////////////////////////////////
956
957 class SGDistScaleAnimation::Transform : public osg::Transform {
958 public:
959   Transform(const SGPropertyNode* configNode)
960   {
961     setName(configNode->getStringValue("name", "dist scale animation"));
962     setReferenceFrame(RELATIVE_RF);
963     setStateSet(getNormalizeStateSet());
964     _factor = configNode->getFloatValue("factor", 1);
965     _offset = configNode->getFloatValue("offset", 0);
966     _min_v = configNode->getFloatValue("min", SGLimitsf::epsilon());
967     _max_v = configNode->getFloatValue("max", SGLimitsf::max());
968     _table = read_interpolation_table(configNode);
969     _center[0] = configNode->getFloatValue("center/x-m", 0);
970     _center[1] = configNode->getFloatValue("center/y-m", 0);
971     _center[2] = configNode->getFloatValue("center/z-m", 0);
972   }
973   virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
974                                          osg::NodeVisitor* nv) const 
975   {
976     osg::Matrix transform;
977     double scale_factor = computeScaleFactor(nv);
978     transform(0,0) = scale_factor;
979     transform(1,1) = scale_factor;
980     transform(2,2) = scale_factor;
981     transform(3,0) = _center[0]*(1 - scale_factor);
982     transform(3,1) = _center[1]*(1 - scale_factor);
983     transform(3,2) = _center[2]*(1 - scale_factor);
984     matrix.preMult(transform);
985     return true;
986   }
987   
988   virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
989                                          osg::NodeVisitor* nv) const
990   {
991     double scale_factor = computeScaleFactor(nv);
992     if (fabs(scale_factor) <= SGLimits<double>::min())
993       return false;
994     osg::Matrix transform;
995     double rScaleFactor = 1/scale_factor;
996     transform(0,0) = rScaleFactor;
997     transform(1,1) = rScaleFactor;
998     transform(2,2) = rScaleFactor;
999     transform(3,0) = _center[0]*(1 - rScaleFactor);
1000     transform(3,1) = _center[1]*(1 - rScaleFactor);
1001     transform(3,2) = _center[2]*(1 - rScaleFactor);
1002     matrix.postMult(transform);
1003     return true;
1004   }
1005
1006 private:
1007   double computeScaleFactor(osg::NodeVisitor* nv) const
1008   {
1009     if (!nv)
1010       return 1;
1011
1012     double scale_factor = (_center.osg() - nv->getEyePoint()).length();
1013     if (_table == 0) {
1014       scale_factor = _factor * scale_factor + _offset;
1015     } else {
1016       scale_factor = _table->interpolate( scale_factor );
1017     }
1018     if (scale_factor < _min_v)
1019       scale_factor = _min_v;
1020     if (scale_factor > _max_v)
1021       scale_factor = _max_v;
1022
1023     return scale_factor;
1024   }
1025
1026   SGSharedPtr<SGInterpTable> _table;
1027   SGVec3d _center;
1028   double _min_v;
1029   double _max_v;
1030   double _factor;
1031   double _offset;
1032 };
1033
1034
1035 SGDistScaleAnimation::SGDistScaleAnimation(const SGPropertyNode* configNode,
1036                                            SGPropertyNode* modelRoot) :
1037   SGAnimation(configNode, modelRoot)
1038 {
1039 }
1040
1041 osg::Group*
1042 SGDistScaleAnimation::createAnimationGroup(osg::Group& parent)
1043 {
1044   Transform* transform = new Transform(getConfig());
1045   parent.addChild(transform);
1046   return transform;
1047 }
1048
1049 \f
1050 ////////////////////////////////////////////////////////////////////////
1051 // Implementation of flash animation
1052 ////////////////////////////////////////////////////////////////////////
1053
1054 class SGFlashAnimation::Transform : public osg::Transform {
1055 public:
1056   Transform(const SGPropertyNode* configNode)
1057   {
1058     setReferenceFrame(RELATIVE_RF);
1059     setName(configNode->getStringValue("name", "flash animation"));
1060     setStateSet(getNormalizeStateSet());
1061
1062     _axis[0] = configNode->getFloatValue("axis/x", 0);
1063     _axis[1] = configNode->getFloatValue("axis/y", 0);
1064     _axis[2] = configNode->getFloatValue("axis/z", 1);
1065     _axis.normalize();
1066     
1067     _center[0] = configNode->getFloatValue("center/x-m", 0);
1068     _center[1] = configNode->getFloatValue("center/y-m", 0);
1069     _center[2] = configNode->getFloatValue("center/z-m", 0);
1070     
1071     _offset = configNode->getFloatValue("offset", 0);
1072     _factor = configNode->getFloatValue("factor", 1);
1073     _power = configNode->getFloatValue("power", 1);
1074     _two_sides = configNode->getBoolValue("two-sides", false);
1075     
1076     _min_v = configNode->getFloatValue("min", SGLimitsf::epsilon());
1077     _max_v = configNode->getFloatValue("max", 1);
1078   }
1079   virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
1080                                          osg::NodeVisitor* nv) const 
1081   {
1082     osg::Matrix transform;
1083     double scale_factor = computeScaleFactor(nv);
1084     transform(0,0) = scale_factor;
1085     transform(1,1) = scale_factor;
1086     transform(2,2) = scale_factor;
1087     transform(3,0) = _center[0]*(1 - scale_factor);
1088     transform(3,1) = _center[1]*(1 - scale_factor);
1089     transform(3,2) = _center[2]*(1 - scale_factor);
1090     matrix.preMult(transform);
1091     return true;
1092   }
1093   
1094   virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
1095                                          osg::NodeVisitor* nv) const
1096   {
1097     double scale_factor = computeScaleFactor(nv);
1098     if (fabs(scale_factor) <= SGLimits<double>::min())
1099       return false;
1100     osg::Matrix transform;
1101     double rScaleFactor = 1/scale_factor;
1102     transform(0,0) = rScaleFactor;
1103     transform(1,1) = rScaleFactor;
1104     transform(2,2) = rScaleFactor;
1105     transform(3,0) = _center[0]*(1 - rScaleFactor);
1106     transform(3,1) = _center[1]*(1 - rScaleFactor);
1107     transform(3,2) = _center[2]*(1 - rScaleFactor);
1108     matrix.postMult(transform);
1109     return true;
1110   }
1111
1112 private:
1113   double computeScaleFactor(osg::NodeVisitor* nv) const
1114   {
1115     if (!nv)
1116       return 1;
1117
1118     osg::Vec3 localEyeToCenter = nv->getEyePoint() - _center;
1119     localEyeToCenter.normalize();
1120
1121     double cos_angle = localEyeToCenter*_axis;
1122     double scale_factor = 0;
1123     if ( _two_sides && cos_angle < 0 )
1124       scale_factor = _factor * pow( -cos_angle, _power ) + _offset;
1125     else if ( cos_angle > 0 )
1126       scale_factor = _factor * pow( cos_angle, _power ) + _offset;
1127     
1128     if ( scale_factor < _min_v )
1129       scale_factor = _min_v;
1130     if ( scale_factor > _max_v )
1131       scale_factor = _max_v;
1132
1133     return scale_factor;
1134   }
1135
1136   virtual osg::BoundingSphere computeBound() const
1137   {
1138     // avoid being culled away by small feature culling
1139     osg::BoundingSphere bs = osg::Group::computeBound();
1140     bs.radius() *= _max_v;
1141     return bs;
1142   }
1143
1144 private:
1145   osg::Vec3 _center;
1146   osg::Vec3 _axis;
1147   double _power, _factor, _offset, _min_v, _max_v;
1148   bool _two_sides;
1149 };
1150
1151
1152 SGFlashAnimation::SGFlashAnimation(const SGPropertyNode* configNode,
1153                                    SGPropertyNode* modelRoot) :
1154   SGAnimation(configNode, modelRoot)
1155 {
1156 }
1157
1158 osg::Group*
1159 SGFlashAnimation::createAnimationGroup(osg::Group& parent)
1160 {
1161   Transform* transform = new Transform(getConfig());
1162   parent.addChild(transform);
1163   return transform;
1164 }
1165
1166 \f
1167 ////////////////////////////////////////////////////////////////////////
1168 // Implementation of flash animation
1169 ////////////////////////////////////////////////////////////////////////
1170
1171 class SGBillboardAnimation::Transform : public osg::Transform {
1172 public:
1173   Transform(const SGPropertyNode* configNode) :
1174     _spherical(configNode->getBoolValue("spherical", true))
1175   {
1176     setReferenceFrame(RELATIVE_RF);
1177     setName(configNode->getStringValue("name", "billboard animation"));
1178   }
1179   virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
1180                                          osg::NodeVisitor* nv) const 
1181   {
1182     // More or less taken from plibs ssgCutout
1183     if (_spherical) {
1184       matrix(0,0) = 1; matrix(0,1) = 0; matrix(0,2) = 0;
1185       matrix(1,0) = 0; matrix(1,1) = 0; matrix(1,2) = -1;
1186       matrix(2,0) = 0; matrix(2,1) = 1; matrix(2,2) = 0;
1187     } else {
1188       osg::Vec3 zAxis(matrix(2, 0), matrix(2, 1), matrix(2, 2));
1189       osg::Vec3 xAxis = osg::Vec3(0, 0, -1)^zAxis;
1190       osg::Vec3 yAxis = zAxis^xAxis;
1191       
1192       xAxis.normalize();
1193       yAxis.normalize();
1194       zAxis.normalize();
1195       
1196       matrix(0,0) = xAxis[0]; matrix(0,1) = xAxis[1]; matrix(0,2) = xAxis[2];
1197       matrix(1,0) = yAxis[0]; matrix(1,1) = yAxis[1]; matrix(1,2) = yAxis[2];
1198       matrix(2,0) = zAxis[0]; matrix(2,1) = zAxis[1]; matrix(2,2) = zAxis[2];
1199     }
1200     return true;
1201   }
1202   
1203   virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
1204                                          osg::NodeVisitor* nv) const
1205   {
1206     // Hmm, don't yet know how to get that back ...
1207     return false;
1208   }
1209
1210 private:
1211   bool _spherical;
1212 };
1213
1214
1215 SGBillboardAnimation::SGBillboardAnimation(const SGPropertyNode* configNode,
1216                                            SGPropertyNode* modelRoot) :
1217   SGAnimation(configNode, modelRoot)
1218 {
1219 }
1220
1221 osg::Group*
1222 SGBillboardAnimation::createAnimationGroup(osg::Group& parent)
1223 {
1224   Transform* transform = new Transform(getConfig());
1225   parent.addChild(transform);
1226   return transform;
1227 }
1228
1229 \f
1230 ////////////////////////////////////////////////////////////////////////
1231 // Implementation of a range animation
1232 ////////////////////////////////////////////////////////////////////////
1233
1234 class SGRangeAnimation::UpdateCallback : public osg::NodeCallback {
1235 public:
1236   UpdateCallback(const SGCondition* condition,
1237                  const SGExpressiond* minAnimationValue,
1238                  const SGExpressiond* maxAnimationValue,
1239                  double minValue, double maxValue) :
1240     _condition(condition),
1241     _minAnimationValue(minAnimationValue),
1242     _maxAnimationValue(maxAnimationValue),
1243     _minStaticValue(minValue),
1244     _maxStaticValue(maxValue)
1245   {}
1246   virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1247   {
1248     osg::LOD* lod = static_cast<osg::LOD*>(node);
1249     if (!_condition || _condition->test()) {
1250       double minRange;
1251       if (_minAnimationValue)
1252         minRange = _minAnimationValue->getValue();
1253       else
1254         minRange = _minStaticValue;
1255       double maxRange;
1256       if (_maxAnimationValue)
1257         maxRange = _maxAnimationValue->getValue();
1258       else
1259         maxRange = _maxStaticValue;
1260       lod->setRange(0, minRange, maxRange);
1261     } else {
1262       lod->setRange(0, 0, SGLimitsf::max());
1263     }
1264     traverse(node, nv);
1265   }
1266
1267 private:
1268   SGSharedPtr<const SGCondition> _condition;
1269   SGSharedPtr<const SGExpressiond> _minAnimationValue;
1270   SGSharedPtr<const SGExpressiond> _maxAnimationValue;
1271   double _minStaticValue;
1272   double _maxStaticValue;
1273 };
1274
1275 SGRangeAnimation::SGRangeAnimation(const SGPropertyNode* configNode,
1276                                    SGPropertyNode* modelRoot) :
1277   SGAnimation(configNode, modelRoot)
1278 {
1279   _condition = getCondition();
1280
1281   std::string inputPropertyName;
1282   inputPropertyName = configNode->getStringValue("min-property", "");
1283   if (!inputPropertyName.empty()) {
1284     SGPropertyNode* inputProperty;
1285     inputProperty = modelRoot->getNode(inputPropertyName, true);
1286     SGSharedPtr<SGExpressiond> value;
1287     value = new SGPropertyExpression<double>(inputProperty);
1288
1289     value = read_factor_offset(configNode, value, "min-factor", "min-offset");
1290     _minAnimationValue = value->simplify();
1291   }
1292   inputPropertyName = configNode->getStringValue("max-property", "");
1293   if (!inputPropertyName.empty()) {
1294     SGPropertyNode* inputProperty;
1295     inputProperty = modelRoot->getNode(inputPropertyName.c_str(), true);
1296
1297     SGSharedPtr<SGExpressiond> value;
1298     value = new SGPropertyExpression<double>(inputProperty);
1299
1300     value = read_factor_offset(configNode, value, "max-factor", "max-offset");
1301     _maxAnimationValue = value->simplify();
1302   }
1303
1304   _initialValue[0] = configNode->getDoubleValue("min-m", 0);
1305   _initialValue[0] *= configNode->getDoubleValue("min-factor", 1);
1306   _initialValue[1] = configNode->getDoubleValue("max-m", SGLimitsf::max());
1307   _initialValue[1] *= configNode->getDoubleValue("max-factor", 1);
1308 }
1309
1310 osg::Group*
1311 SGRangeAnimation::createAnimationGroup(osg::Group& parent)
1312 {
1313   osg::Group* group = new osg::Group;
1314   group->setName("range animation group");
1315
1316   osg::LOD* lod = new osg::LOD;
1317   lod->setName("range animation node");
1318   parent.addChild(lod);
1319
1320   lod->addChild(group, _initialValue[0], _initialValue[1]);
1321   lod->setCenterMode(osg::LOD::USE_BOUNDING_SPHERE_CENTER);
1322   lod->setRangeMode(osg::LOD::DISTANCE_FROM_EYE_POINT);
1323   if (_minAnimationValue || _maxAnimationValue || _condition) {
1324     UpdateCallback* uc;
1325     uc = new UpdateCallback(_condition, _minAnimationValue, _maxAnimationValue,
1326                             _initialValue[0], _initialValue[1]);
1327     lod->setUpdateCallback(uc);
1328   }
1329   return group;
1330 }
1331
1332 \f
1333 ////////////////////////////////////////////////////////////////////////
1334 // Implementation of a select animation
1335 ////////////////////////////////////////////////////////////////////////
1336
1337 class SGSelectAnimation::UpdateCallback : public osg::NodeCallback {
1338 public:
1339   UpdateCallback(const SGCondition* condition) :
1340     _condition(condition)
1341   {}
1342   virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1343   {
1344     osg::Switch* sw = static_cast<osg::Switch*>(node);
1345     if (_condition->test())
1346       sw->setAllChildrenOn();
1347     else
1348       sw->setAllChildrenOff();
1349     traverse(node, nv);
1350   }
1351
1352 private:
1353   SGSharedPtr<SGCondition const> _condition;
1354 };
1355
1356 SGSelectAnimation::SGSelectAnimation(const SGPropertyNode* configNode,
1357                                      SGPropertyNode* modelRoot) :
1358   SGAnimation(configNode, modelRoot)
1359 {
1360 }
1361
1362 osg::Group*
1363 SGSelectAnimation::createAnimationGroup(osg::Group& parent)
1364 {
1365   // if no condition given, this is a noop.
1366   SGSharedPtr<SGCondition const> condition = getCondition();
1367   // trick, gets deleted with all its 'animated' children
1368   // when the animation installer returns
1369   if (!condition)
1370     return new osg::Group;
1371
1372   osg::Switch* sw = new osg::Switch;
1373   sw->setName("select animation node");
1374   sw->setUpdateCallback(new UpdateCallback(condition));
1375   parent.addChild(sw);
1376   return sw;
1377 }
1378
1379
1380 \f
1381 ////////////////////////////////////////////////////////////////////////
1382 // Implementation of alpha test animation
1383 ////////////////////////////////////////////////////////////////////////
1384
1385 SGAlphaTestAnimation::SGAlphaTestAnimation(const SGPropertyNode* configNode,
1386                                            SGPropertyNode* modelRoot) :
1387   SGAnimation(configNode, modelRoot)
1388 {
1389 }
1390
1391 namespace
1392 {
1393 // Keep one copy of the most common alpha test its state set.
1394 ReentrantMutex alphaTestMutex;
1395 osg::ref_ptr<osg::AlphaFunc> standardAlphaFunc;
1396 osg::ref_ptr<osg::StateSet> alphaFuncStateSet;
1397
1398 osg::AlphaFunc* makeAlphaFunc(float clamp)
1399 {
1400     ScopedLock<ReentrantMutex> lock(alphaTestMutex);
1401     if (osg::equivalent(clamp, 0.01f)) {
1402         if (standardAlphaFunc.valid())
1403             return standardAlphaFunc.get();
1404         clamp = .01;
1405     }
1406     osg::AlphaFunc* alphaFunc = new osg::AlphaFunc;
1407     alphaFunc->setFunction(osg::AlphaFunc::GREATER);
1408     alphaFunc->setReferenceValue(clamp);
1409     alphaFunc->setDataVariance(osg::Object::STATIC);
1410     if (osg::equivalent(clamp, 0.01f))
1411         standardAlphaFunc = alphaFunc;
1412     return alphaFunc;
1413 }
1414
1415 osg::StateSet* makeAlphaTestStateSet(float clamp)
1416 {
1417     using namespace OpenThreads;
1418     ScopedLock<ReentrantMutex> lock(alphaTestMutex);
1419     if (osg::equivalent(clamp, 0.01f)) {
1420         if (alphaFuncStateSet.valid())
1421             return alphaFuncStateSet.get();
1422     }
1423     osg::AlphaFunc* alphaFunc = makeAlphaFunc(clamp);
1424     osg::StateSet* stateSet = new osg::StateSet;
1425     stateSet->setAttributeAndModes(alphaFunc,
1426                                    (osg::StateAttribute::ON
1427                                     | osg::StateAttribute::OVERRIDE));
1428     stateSet->setDataVariance(osg::Object::STATIC);
1429     if (osg::equivalent(clamp, 0.01f))
1430         alphaFuncStateSet = stateSet;
1431     return stateSet;
1432 }
1433 }
1434 void
1435 SGAlphaTestAnimation::install(osg::Node& node)
1436 {
1437   SGAnimation::install(node);
1438
1439   float alphaClamp = getConfig()->getFloatValue("alpha-factor", 0);
1440   osg::StateSet* stateSet = node.getStateSet();
1441   if (!stateSet) {
1442       node.setStateSet(makeAlphaTestStateSet(alphaClamp));
1443   } else {
1444       stateSet->setAttributeAndModes(makeAlphaFunc(alphaClamp),
1445                                      (osg::StateAttribute::ON
1446                                       | osg::StateAttribute::OVERRIDE));
1447   }
1448 }
1449
1450 \f
1451 //////////////////////////////////////////////////////////////////////
1452 // Blend animation installer
1453 //////////////////////////////////////////////////////////////////////
1454
1455 // XXX This needs to be replaced by something using TexEnvCombine to
1456 // change the blend factor. Changing the alpha values in the geometry
1457 // is bogus.
1458 class SGBlendAnimation::BlendVisitor : public osg::NodeVisitor {
1459 public:
1460   BlendVisitor(float blend) :
1461     osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
1462     _blend(blend)
1463   { setVisitorType(osg::NodeVisitor::NODE_VISITOR); }
1464   virtual void apply(osg::Node& node)
1465   {
1466     updateStateSet(node.getStateSet());
1467     traverse(node);
1468   }
1469   virtual void apply(osg::Geode& node)
1470   {
1471     apply((osg::Node&)node);
1472     unsigned nDrawables = node.getNumDrawables();
1473     for (unsigned i = 0; i < nDrawables; ++i) {
1474       osg::Drawable* drawable = node.getDrawable(i);
1475       osg::Geometry* geometry = drawable->asGeometry();
1476       if (!geometry)
1477         continue;
1478       osg::Array* array = geometry->getColorArray();
1479       if (!array)
1480         continue;
1481       osg::Vec4Array* vec4Array = dynamic_cast<osg::Vec4Array*>(array);
1482       if (!vec4Array)
1483         continue;
1484       for (unsigned k = 0; k < vec4Array->size(); ++k) {
1485         (*vec4Array)[k][3] = _blend;
1486       }
1487       vec4Array->dirty();
1488       updateStateSet(drawable->getStateSet());
1489     }
1490   }
1491   void updateStateSet(osg::StateSet* stateSet)
1492   {
1493     if (!stateSet)
1494       return;
1495     osg::StateAttribute* stateAttribute;
1496     stateAttribute = stateSet->getAttribute(osg::StateAttribute::MATERIAL);
1497     if (!stateAttribute)
1498       return;
1499     osg::Material* material = dynamic_cast<osg::Material*>(stateAttribute);
1500     if (!material)
1501       return;
1502     material->setAlpha(osg::Material::FRONT_AND_BACK, _blend);
1503     if (_blend < 1) {
1504       stateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
1505       stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
1506     } else {
1507       stateSet->setRenderingHint(osg::StateSet::DEFAULT_BIN);
1508     }
1509   }
1510 private:
1511   float _blend;
1512 };
1513
1514 class SGBlendAnimation::UpdateCallback : public osg::NodeCallback {
1515 public:
1516   UpdateCallback(const SGPropertyNode* configNode, const SGExpressiond* v) :
1517     _prev_value(-1),
1518     _animationValue(v)
1519   { }
1520   virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1521   {
1522     double blend = _animationValue->getValue();
1523     if (blend != _prev_value) {
1524       _prev_value = blend;
1525       BlendVisitor visitor(1-blend);
1526       node->accept(visitor);
1527     }
1528     traverse(node, nv);
1529   }
1530 public:
1531   double _prev_value;
1532   SGSharedPtr<SGExpressiond const> _animationValue;
1533 };
1534
1535
1536 SGBlendAnimation::SGBlendAnimation(const SGPropertyNode* configNode,
1537                                    SGPropertyNode* modelRoot)
1538   : SGAnimation(configNode, modelRoot),
1539     _animationValue(read_value(configNode, modelRoot, "", 0, 1))
1540 {
1541 }
1542
1543 osg::Group*
1544 SGBlendAnimation::createAnimationGroup(osg::Group& parent)
1545 {
1546   if (!_animationValue)
1547     return 0;
1548
1549   osg::Group* group = new osg::Switch;
1550   group->setName("blend animation node");
1551   group->setUpdateCallback(new UpdateCallback(getConfig(), _animationValue));
1552   parent.addChild(group);
1553   return group;
1554 }
1555
1556 void
1557 SGBlendAnimation::install(osg::Node& node)
1558 {
1559   SGAnimation::install(node);
1560   // make sure we do not change common geometries,
1561   // that also creates new display lists for these subgeometries.
1562   cloneDrawables(node);
1563   DoDrawArraysVisitor visitor;
1564   node.accept(visitor);
1565 }
1566
1567 \f
1568 //////////////////////////////////////////////////////////////////////
1569 // Timed animation installer
1570 //////////////////////////////////////////////////////////////////////
1571
1572
1573
1574 class SGTimedAnimation::UpdateCallback : public osg::NodeCallback {
1575 public:
1576   UpdateCallback(const SGPropertyNode* configNode) :
1577     _current_index(0),
1578     _reminder(0),
1579     _duration_sec(configNode->getDoubleValue("duration-sec", 1)),
1580     _last_time_sec(SGLimitsd::max()),
1581     _use_personality(configNode->getBoolValue("use-personality", false))
1582   {
1583     std::vector<SGSharedPtr<SGPropertyNode> > nodes;
1584     nodes = configNode->getChildren("branch-duration-sec");
1585     for (size_t i = 0; i < nodes.size(); ++i) {
1586       unsigned ind = nodes[ i ]->getIndex();
1587       while ( ind >= _durations.size() ) {
1588         _durations.push_back(DurationSpec(_duration_sec));
1589       }
1590       SGPropertyNode_ptr rNode = nodes[i]->getChild("random");
1591       if ( rNode == 0 ) {
1592         _durations[ind] = DurationSpec(nodes[ i ]->getDoubleValue());
1593       } else {
1594         _durations[ind] = DurationSpec(rNode->getDoubleValue( "min", 0),
1595                                        rNode->getDoubleValue( "max", 1));
1596       }
1597     }
1598   }
1599   virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1600   {
1601     assert(dynamic_cast<osg::Switch*>(node));
1602     osg::Switch* sw = static_cast<osg::Switch*>(node);
1603
1604     unsigned nChildren = sw->getNumChildren();
1605
1606     // blow up the durations vector to the required size
1607     while (_durations.size() < nChildren) {
1608       _durations.push_back(_duration_sec);
1609     }
1610     // make sure the current index is an duration that really exists
1611     _current_index = _current_index % nChildren;
1612
1613     // update the time and compute the current systems time value
1614     double t = nv->getFrameStamp()->getReferenceTime();
1615     if (_last_time_sec == SGLimitsd::max()) {
1616       _last_time_sec = t;
1617     } else {
1618       double dt = t - _last_time_sec;
1619       if (_use_personality)
1620         dt *= 1 + 0.2*(0.5 - sg_random());
1621       _reminder += dt;
1622       _last_time_sec = t;
1623     }
1624
1625     double currentDuration = _durations[_current_index].get();
1626     while (currentDuration < _reminder) {
1627       _reminder -= currentDuration;
1628       _current_index = (_current_index + 1) % nChildren;
1629       currentDuration = _durations[_current_index].get();
1630     }
1631
1632     sw->setSingleChildOn(_current_index);
1633
1634     traverse(node, nv);
1635   }
1636
1637 private:
1638   struct DurationSpec {
1639     DurationSpec(double t) :
1640       minTime(SGMiscd::max(0.01, t)),
1641       maxTime(SGMiscd::max(0.01, t))
1642     {}
1643     DurationSpec(double t0, double t1) :
1644       minTime(SGMiscd::max(0.01, t0)),
1645       maxTime(SGMiscd::max(0.01, t1))
1646     {}
1647     double get() const
1648     { return minTime + sg_random()*(maxTime - minTime); }
1649     double minTime;
1650     double maxTime;
1651   };
1652   std::vector<DurationSpec> _durations;
1653   unsigned _current_index;
1654   double _reminder;
1655   double _duration_sec;
1656   double _last_time_sec;
1657   bool _use_personality;
1658 };
1659
1660
1661 SGTimedAnimation::SGTimedAnimation(const SGPropertyNode* configNode,
1662                                    SGPropertyNode* modelRoot)
1663   : SGAnimation(configNode, modelRoot)
1664 {
1665 }
1666
1667 osg::Group*
1668 SGTimedAnimation::createAnimationGroup(osg::Group& parent)
1669 {
1670   osg::Switch* sw = new osg::Switch;
1671   sw->setName("timed animation node");
1672   sw->setUpdateCallback(new UpdateCallback(getConfig()));
1673   parent.addChild(sw);
1674   return sw;
1675 }
1676
1677 \f
1678 ////////////////////////////////////////////////////////////////////////
1679 // dynamically switch on/off shadows
1680 ////////////////////////////////////////////////////////////////////////
1681
1682 class SGShadowAnimation::UpdateCallback : public osg::NodeCallback {
1683 public:
1684   UpdateCallback(const SGCondition* condition) :
1685     _condition(condition)
1686   {}
1687   virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1688   {
1689     if (_condition->test())
1690       node->setNodeMask( SG_NODEMASK_CASTSHADOW_BIT | node->getNodeMask());
1691     else
1692       node->setNodeMask(~SG_NODEMASK_CASTSHADOW_BIT & node->getNodeMask());
1693     traverse(node, nv);
1694   }
1695
1696 private:
1697   SGSharedPtr<const SGCondition> _condition;
1698 };
1699
1700 SGShadowAnimation::SGShadowAnimation(const SGPropertyNode* configNode,
1701                                      SGPropertyNode* modelRoot) :
1702   SGAnimation(configNode, modelRoot)
1703 {
1704 }
1705
1706 osg::Group*
1707 SGShadowAnimation::createAnimationGroup(osg::Group& parent)
1708 {
1709   SGSharedPtr<SGCondition const> condition = getCondition();
1710   if (!condition)
1711     return 0;
1712
1713   osg::Group* group = new osg::Group;
1714   group->setName("shadow animation");
1715   group->setUpdateCallback(new UpdateCallback(condition));
1716   parent.addChild(group);
1717   return group;
1718 }
1719
1720 \f
1721 ////////////////////////////////////////////////////////////////////////
1722 // Implementation of SGTexTransformAnimation
1723 ////////////////////////////////////////////////////////////////////////
1724
1725 class SGTexTransformAnimation::Transform : public SGReferenced {
1726 public:
1727   Transform() :
1728     _value(0)
1729   {}
1730   virtual ~Transform()
1731   { }
1732   void setValue(double value)
1733   { _value = value; }
1734   virtual void transform(osg::Matrix&) = 0;
1735 protected:
1736   double _value;
1737 };
1738
1739 class SGTexTransformAnimation::Translation :
1740   public SGTexTransformAnimation::Transform {
1741 public:
1742   Translation(const SGVec3d& axis) :
1743     _axis(axis)
1744   { }
1745   virtual void transform(osg::Matrix& matrix)
1746   {
1747     osg::Matrix tmp;
1748     set_translation(tmp, _value, _axis);
1749     matrix.preMult(tmp);
1750   }
1751 private:
1752   SGVec3d _axis;
1753 };
1754
1755 class SGTexTransformAnimation::Rotation :
1756   public SGTexTransformAnimation::Transform {
1757 public:
1758   Rotation(const SGVec3d& axis, const SGVec3d& center) :
1759     _axis(axis),
1760     _center(center)
1761   { }
1762   virtual void transform(osg::Matrix& matrix)
1763   {
1764     osg::Matrix tmp;
1765     set_rotation(tmp, _value, _center, _axis);
1766     matrix.preMult(tmp);
1767   }
1768 private:
1769   SGVec3d _axis;
1770   SGVec3d _center;
1771 };
1772
1773 class SGTexTransformAnimation::UpdateCallback :
1774   public osg::StateAttribute::Callback {
1775 public:
1776   UpdateCallback(const SGCondition* condition) :
1777     _condition(condition)
1778   { }
1779   virtual void operator () (osg::StateAttribute* sa, osg::NodeVisitor*)
1780   {
1781     if (!_condition || _condition->test()) {
1782       TransformList::const_iterator i;
1783       for (i = _transforms.begin(); i != _transforms.end(); ++i)
1784         i->transform->setValue(i->value->getValue());
1785     }
1786     assert(dynamic_cast<osg::TexMat*>(sa));
1787     osg::TexMat* texMat = static_cast<osg::TexMat*>(sa);
1788     texMat->getMatrix().makeIdentity();
1789     TransformList::const_iterator i;
1790     for (i = _transforms.begin(); i != _transforms.end(); ++i)
1791       i->transform->transform(texMat->getMatrix());
1792   }
1793   void appendTransform(Transform* transform, SGExpressiond* value)
1794   {
1795     Entry entry = { transform, value };
1796     transform->transform(_matrix);
1797     _transforms.push_back(entry);
1798   }
1799
1800 private:
1801   struct Entry {
1802     SGSharedPtr<Transform> transform;
1803     SGSharedPtr<const SGExpressiond> value;
1804   };
1805   typedef std::vector<Entry> TransformList;
1806   TransformList _transforms;
1807   SGSharedPtr<const SGCondition> _condition;
1808   osg::Matrix _matrix;
1809 };
1810
1811 SGTexTransformAnimation::SGTexTransformAnimation(const SGPropertyNode* configNode,
1812                                                  SGPropertyNode* modelRoot) :
1813   SGAnimation(configNode, modelRoot)
1814 {
1815 }
1816
1817 osg::Group*
1818 SGTexTransformAnimation::createAnimationGroup(osg::Group& parent)
1819 {
1820   osg::Group* group = new osg::Group;
1821   group->setName("texture transform group");
1822   osg::StateSet* stateSet = group->getOrCreateStateSet();
1823   osg::TexMat* texMat = new osg::TexMat;
1824   UpdateCallback* updateCallback = new UpdateCallback(getCondition());
1825   // interpret the configs ...
1826   std::string type = getType();
1827
1828   if (type == "textranslate") {
1829     appendTexTranslate(getConfig(), updateCallback);
1830   } else if (type == "texrotate") {
1831     appendTexRotate(getConfig(), updateCallback);
1832   } else if (type == "texmultiple") {
1833     std::vector<SGSharedPtr<SGPropertyNode> > transformConfigs;
1834     transformConfigs = getConfig()->getChildren("transform");
1835     for (unsigned i = 0; i < transformConfigs.size(); ++i) {
1836       std::string subtype = transformConfigs[i]->getStringValue("subtype", "");
1837       if (subtype == "textranslate")
1838         appendTexTranslate(transformConfigs[i], updateCallback);
1839       else if (subtype == "texrotate")
1840         appendTexRotate(transformConfigs[i], updateCallback);
1841       else
1842         SG_LOG(SG_INPUT, SG_ALERT,
1843                "Ignoring unknown texture transform subtype");
1844     }
1845   } else {
1846     SG_LOG(SG_INPUT, SG_ALERT, "Ignoring unknown texture transform type");
1847   }
1848
1849   texMat->setUpdateCallback(updateCallback);
1850   stateSet->setTextureAttribute(0, texMat);
1851   parent.addChild(group);
1852   return group;
1853 }
1854
1855 void
1856 SGTexTransformAnimation::appendTexTranslate(const SGPropertyNode* config,
1857                                             UpdateCallback* updateCallback)
1858 {
1859   std::string propertyName = config->getStringValue("property", "");
1860   SGSharedPtr<SGExpressiond> value;
1861   if (propertyName.empty())
1862     value = new SGConstExpression<double>(0);
1863   else {
1864     SGPropertyNode* inputProperty = getModelRoot()->getNode(propertyName, true);
1865     value = new SGPropertyExpression<double>(inputProperty);
1866   }
1867
1868   SGInterpTable* table = read_interpolation_table(config);
1869   if (table) {
1870     value = new SGInterpTableExpression<double>(value, table);
1871     double biasValue = config->getDoubleValue("bias", 0);
1872     if (biasValue != 0)
1873       value = new SGBiasExpression<double>(value, biasValue);
1874     value = new SGStepExpression<double>(value,
1875                                          config->getDoubleValue("step", 0),
1876                                          config->getDoubleValue("scroll", 0));
1877     value = value->simplify();
1878   } else {
1879     double biasValue = config->getDoubleValue("bias", 0);
1880     if (biasValue != 0)
1881       value = new SGBiasExpression<double>(value, biasValue);
1882     value = new SGStepExpression<double>(value,
1883                                          config->getDoubleValue("step", 0),
1884                                          config->getDoubleValue("scroll", 0));
1885     value = read_offset_factor(config, value, "factor", "offset");
1886
1887     if (config->hasChild("min") || config->hasChild("max")) {
1888       double minClip = config->getDoubleValue("min", -SGLimitsd::max());
1889       double maxClip = config->getDoubleValue("max", SGLimitsd::max());
1890       value = new SGClipExpression<double>(value, minClip, maxClip);
1891     }
1892     value = value->simplify();
1893   }
1894   SGVec3d axis(config->getDoubleValue("axis/x", 0),
1895                config->getDoubleValue("axis/y", 0),
1896                config->getDoubleValue("axis/z", 0));
1897   Translation* translation;
1898   translation = new Translation(normalize(axis));
1899   translation->setValue(config->getDoubleValue("starting-position", 0));
1900   updateCallback->appendTransform(translation, value);
1901 }
1902
1903 void
1904 SGTexTransformAnimation::appendTexRotate(const SGPropertyNode* config,
1905                                          UpdateCallback* updateCallback)
1906 {
1907   std::string propertyName = config->getStringValue("property", "");
1908   SGSharedPtr<SGExpressiond> value;
1909   if (propertyName.empty())
1910     value = new SGConstExpression<double>(0);
1911   else {
1912     SGPropertyNode* inputProperty = getModelRoot()->getNode(propertyName, true);
1913     value = new SGPropertyExpression<double>(inputProperty);
1914   }
1915
1916   SGInterpTable* table = read_interpolation_table(config);
1917   if (table) {
1918     value = new SGInterpTableExpression<double>(value, table);
1919     double biasValue = config->getDoubleValue("bias", 0);
1920     if (biasValue != 0)
1921       value = new SGBiasExpression<double>(value, biasValue);
1922     value = new SGStepExpression<double>(value,
1923                                          config->getDoubleValue("step", 0),
1924                                          config->getDoubleValue("scroll", 0));
1925     value = value->simplify();
1926   } else {
1927     double biasValue = config->getDoubleValue("bias", 0);
1928     if (biasValue != 0)
1929       value = new SGBiasExpression<double>(value, biasValue);
1930     value = new SGStepExpression<double>(value,
1931                                          config->getDoubleValue("step", 0),
1932                                          config->getDoubleValue("scroll", 0));
1933     value = read_offset_factor(config, value, "factor", "offset-deg");
1934
1935     if (config->hasChild("min-deg") || config->hasChild("max-deg")) {
1936       double minClip = config->getDoubleValue("min-deg", -SGLimitsd::max());
1937       double maxClip = config->getDoubleValue("max-deg", SGLimitsd::max());
1938       value = new SGClipExpression<double>(value, minClip, maxClip);
1939     }
1940     value = value->simplify();
1941   }
1942   SGVec3d axis(config->getDoubleValue("axis/x", 0),
1943                config->getDoubleValue("axis/y", 0),
1944                config->getDoubleValue("axis/z", 0));
1945   SGVec3d center(config->getDoubleValue("center/x", 0),
1946                  config->getDoubleValue("center/y", 0),
1947                  config->getDoubleValue("center/z", 0));
1948   Rotation* rotation;
1949   rotation = new Rotation(normalize(axis), center);
1950   rotation->setValue(config->getDoubleValue("starting-position-deg", 0));
1951   updateCallback->appendTransform(rotation, value);
1952 }
1953
1954
1955 ////////////////////////////////////////////////////////////////////////
1956 // Implementation of SGPickAnimation
1957 ////////////////////////////////////////////////////////////////////////
1958
1959 class SGPickAnimation::PickCallback : public SGPickCallback {
1960 public:
1961   PickCallback(const SGPropertyNode* configNode,
1962                SGPropertyNode* modelRoot) :
1963     _button(configNode->getIntValue("button", -1)),
1964     _repeatable(configNode->getBoolValue("repeatable", false)),
1965     _repeatInterval(configNode->getDoubleValue("interval-sec", 0.1))
1966   {
1967     SG_LOG(SG_INPUT, SG_DEBUG, "Reading all bindings");
1968     std::vector<SGPropertyNode_ptr> bindings;
1969     bindings = configNode->getChildren("binding");
1970     for (unsigned int i = 0; i < bindings.size(); ++i) {
1971       _bindingsDown.push_back(new SGBinding(bindings[i], modelRoot));
1972     }
1973
1974     const SGPropertyNode* upNode = configNode->getChild("mod-up");
1975     if (!upNode)
1976       return;
1977     bindings = upNode->getChildren("binding");
1978     for (unsigned int i = 0; i < bindings.size(); ++i) {
1979       _bindingsUp.push_back(new SGBinding(bindings[i], modelRoot));
1980     }
1981   }
1982   virtual bool buttonPressed(int button, const Info&)
1983   {
1984     if (0 <= _button && button != _button)
1985       return false;
1986     SGBindingList::const_iterator i;
1987     for (i = _bindingsDown.begin(); i != _bindingsDown.end(); ++i)
1988       (*i)->fire();
1989     _repeatTime = 0;
1990     return true;
1991   }
1992   virtual void buttonReleased(void)
1993   {
1994     SGBindingList::const_iterator i;
1995     for (i = _bindingsUp.begin(); i != _bindingsUp.end(); ++i)
1996       (*i)->fire();
1997   }
1998   virtual void update(double dt)
1999   {
2000     if (!_repeatable)
2001       return;
2002
2003     _repeatTime += dt;
2004     while (_repeatInterval < _repeatTime) {
2005       _repeatTime -= _repeatInterval;
2006       SGBindingList::const_iterator i;
2007       for (i = _bindingsDown.begin(); i != _bindingsDown.end(); ++i)
2008         (*i)->fire();
2009     }
2010   }
2011 private:
2012   SGBindingList _bindingsDown;
2013   SGBindingList _bindingsUp;
2014   int _button;
2015   bool _repeatable;
2016   double _repeatInterval;
2017   double _repeatTime;
2018 };
2019
2020 SGPickAnimation::SGPickAnimation(const SGPropertyNode* configNode,
2021                                  SGPropertyNode* modelRoot) :
2022   SGAnimation(configNode, modelRoot)
2023 {
2024 }
2025
2026 osg::Group*
2027 SGPickAnimation::createAnimationGroup(osg::Group& parent)
2028 {
2029   osg::Group* commonGroup = new osg::Group;
2030
2031   // Contains the normal geometry that is interactive
2032   osg::ref_ptr<osg::Group> normalGroup = new osg::Group;
2033   normalGroup->setName("pick normal group");
2034   normalGroup->addChild(commonGroup);
2035
2036   // Used to render the geometry with just yellow edges
2037   osg::Group* highlightGroup = new osg::Group;
2038   highlightGroup->setName("pick highlight group");
2039   highlightGroup->setNodeMask(SG_NODEMASK_PICK_BIT);
2040   highlightGroup->addChild(commonGroup);
2041   SGSceneUserData* ud;
2042   ud = SGSceneUserData::getOrCreateSceneUserData(commonGroup);
2043   std::vector<SGPropertyNode_ptr> actions;
2044   actions = getConfig()->getChildren("action");
2045   for (unsigned int i = 0; i < actions.size(); ++i)
2046     ud->addPickCallback(new PickCallback(actions[i], getModelRoot()));
2047
2048   // prepare a state set that paints the edges of this object yellow
2049   osg::StateSet* stateSet = highlightGroup->getOrCreateStateSet();
2050   stateSet->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE);
2051
2052   osg::PolygonOffset* polygonOffset = new osg::PolygonOffset;
2053   polygonOffset->setFactor(-1);
2054   polygonOffset->setUnits(-1);
2055   stateSet->setAttribute(polygonOffset, osg::StateAttribute::OVERRIDE);
2056   stateSet->setMode(GL_POLYGON_OFFSET_LINE, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
2057
2058   osg::PolygonMode* polygonMode = new osg::PolygonMode;
2059   polygonMode->setMode(osg::PolygonMode::FRONT_AND_BACK,
2060                        osg::PolygonMode::LINE);
2061   stateSet->setAttribute(polygonMode, osg::StateAttribute::OVERRIDE);
2062   
2063   osg::Material* material = new osg::Material;
2064   material->setColorMode(osg::Material::OFF);
2065   material->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(1, 1, 0, 1));
2066   material->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(1, 1, 0, 1));
2067   material->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(1, 1, 0, 1));
2068   material->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0, 0, 0, 0));
2069   stateSet->setAttribute(material, osg::StateAttribute::OVERRIDE);
2070
2071   // Only add normal geometry if configured
2072   if (getConfig()->getBoolValue("visible", true))
2073     parent.addChild(normalGroup.get());
2074   parent.addChild(highlightGroup);
2075
2076   return commonGroup;
2077 }