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