1 // animation.cxx - classes to manage model animation.
2 // Written by David Megginson, started 2002.
4 // This file is in the Public Domain, and comes with no warranty.
7 # include <simgear_config.h>
10 #include <string.h> // for strcmp()
14 #include <osg/AlphaFunc>
15 #include <osg/Drawable>
17 #include <osg/Geometry>
19 #include <osg/PolygonMode>
20 #include <osg/PolygonOffset>
21 #include <osg/StateSet>
24 #include <osg/Texture2D>
25 #include <osg/Transform>
26 #include <osgDB/ReadFile>
28 #include <simgear/math/interpolater.hxx>
29 #include <simgear/props/condition.hxx>
30 #include <simgear/props/props.hxx>
31 #include <simgear/structure/SGBinding.hxx>
32 #include <simgear/scene/util/SGNodeMasks.hxx>
33 #include <simgear/scene/util/SGSceneUserData.hxx>
34 #include <simgear/scene/util/SGStateAttributeVisitor.hxx>
36 #include "animation.hxx"
39 #include "SGMaterialAnimation.hxx"
42 ////////////////////////////////////////////////////////////////////////
43 // Static utility functions.
44 ////////////////////////////////////////////////////////////////////////
47 * Set up the transform matrix for a spin or rotation.
50 set_rotation (osg::Matrix &matrix, double position_deg,
51 const SGVec3d ¢er, const SGVec3d &axis)
53 double temp_angle = -SGMiscd::deg2rad(position_deg);
55 double s = sin(temp_angle);
56 double c = cos(temp_angle);
59 // axis was normalized at load time
60 // hint to the compiler to put these into FP registers
65 matrix(0, 0) = t * x * x + c ;
66 matrix(0, 1) = t * y * x - s * z ;
67 matrix(0, 2) = t * z * x + s * y ;
70 matrix(1, 0) = t * x * y + s * z ;
71 matrix(1, 1) = t * y * y + c ;
72 matrix(1, 2) = t * z * y - s * x ;
75 matrix(2, 0) = t * x * z - s * y ;
76 matrix(2, 1) = t * y * z + s * x ;
77 matrix(2, 2) = t * z * z + c ;
80 // hint to the compiler to put these into FP registers
85 matrix(3, 0) = x - x*matrix(0, 0) - y*matrix(1, 0) - z*matrix(2, 0);
86 matrix(3, 1) = y - x*matrix(0, 1) - y*matrix(1, 1) - z*matrix(2, 1);
87 matrix(3, 2) = z - x*matrix(0, 2) - y*matrix(1, 2) - z*matrix(2, 2);
92 * Set up the transform matrix for a translation.
95 set_translation (osg::Matrix &matrix, double position_m, const SGVec3d &axis)
97 SGVec3d xyz = axis * position_m;
98 matrix.makeIdentity();
99 matrix(3, 0) = xyz[0];
100 matrix(3, 1) = xyz[1];
101 matrix(3, 2) = xyz[2];
105 * Modify property value by step and scroll settings in texture translations
108 apply_mods(double property, double step, double scroll, double bias)
114 double scrollval = 0.0;
116 // calculate scroll amount (for odometer like movement)
117 double remainder = step - fmod(fabs(property), step);
118 if (remainder < scroll) {
119 scrollval = (scroll - remainder) / scroll * step;
122 // apply stepping of input value
124 modprop = ((floor(property/step) * step) + scrollval);
126 modprop = ((ceil(property/step) * step) + scrollval);
135 * Read an interpolation table from properties.
137 static SGInterpTable *
138 read_interpolation_table(const SGPropertyNode* props)
140 const SGPropertyNode* table_node = props->getNode("interpolation");
143 return new SGInterpTable(table_node);
146 ////////////////////////////////////////////////////////////////////////
147 // Utility value classes
148 ////////////////////////////////////////////////////////////////////////
149 class SGScaleOffsetValue : public SGDoubleValue {
151 SGScaleOffsetValue(SGPropertyNode const* propertyNode) :
152 _propertyNode(propertyNode),
155 _min(-SGLimitsd::max()),
156 _max(SGLimitsd::max())
158 void setScale(double scale)
160 void setOffset(double offset)
161 { _offset = offset; }
162 void setMin(double min)
164 void setMax(double max)
167 virtual double getValue() const
169 double value = _propertyNode ? _propertyNode->getDoubleValue() : 0;
170 return std::min(_max, std::max(_min, _offset + _scale*value));
173 SGSharedPtr<SGPropertyNode const> _propertyNode;
180 class SGPersScaleOffsetValue : public SGDoubleValue {
182 SGPersScaleOffsetValue(SGPropertyNode const* propertyNode,
183 SGPropertyNode const* config,
184 const char* scalename, const char* offsetname,
185 double defScale = 1, double defOffset = 0) :
186 _propertyNode(propertyNode),
187 _scale(config, scalename, defScale),
188 _offset(config, offsetname, defOffset),
189 _min(-SGLimitsd::max()),
190 _max(SGLimitsd::max())
192 void setScale(double scale)
194 void setOffset(double offset)
195 { _offset = offset; }
196 void setMin(double min)
198 void setMax(double max)
201 virtual double getValue() const
205 double value = _propertyNode ? _propertyNode->getDoubleValue() : 0;
206 return SGMiscd::clip(_offset + _scale*value, _min, _max);
209 SGSharedPtr<SGPropertyNode const> _propertyNode;
210 mutable SGPersonalityParameter<double> _scale;
211 mutable SGPersonalityParameter<double> _offset;
216 class SGInterpTableValue : public SGDoubleValue {
218 SGInterpTableValue(SGPropertyNode const* propertyNode,
219 SGInterpTable const* interpTable) :
220 _propertyNode(propertyNode),
221 _interpTable(interpTable)
223 virtual double getValue() const
224 { return _interpTable->interpolate(_propertyNode ? _propertyNode->getDoubleValue() : 0); }
226 SGSharedPtr<SGPropertyNode const> _propertyNode;
227 SGSharedPtr<SGInterpTable const> _interpTable;
230 class SGTexScaleOffsetValue : public SGDoubleValue {
232 SGTexScaleOffsetValue(const SGPropertyNode* propertyNode) :
233 _propertyNode(propertyNode),
239 _min(-SGLimitsd::max()),
240 _max(SGLimitsd::max())
242 void setScale(double scale)
244 void setOffset(double offset)
245 { _offset = offset; }
246 void setStep(double step)
248 void setScroll(double scroll)
249 { _scroll = scroll; }
250 void setBias(double bias)
252 void setMin(double min)
254 void setMax(double max)
257 virtual double getValue() const
259 double value = _propertyNode ? _propertyNode->getDoubleValue() : 0;
260 value = apply_mods(value, _step, _scroll, _bias);
261 return SGMiscd::clip(_scale*(_offset + value), _min, _max);
264 SGSharedPtr<const SGPropertyNode> _propertyNode;
274 class SGTexTableValue : public SGDoubleValue {
276 SGTexTableValue(const SGPropertyNode* propertyNode,
277 const SGInterpTable* interpTable) :
278 _propertyNode(propertyNode),
279 _interpTable(interpTable)
281 void setStep(double step)
283 void setScroll(double scroll)
284 { _scroll = scroll; }
285 void setBias(double bias)
287 virtual double getValue() const
289 double value = _propertyNode ? _propertyNode->getDoubleValue() : 0;
290 value = apply_mods(value, _step, _scroll, _bias);
291 return _interpTable->interpolate(value);
294 SGSharedPtr<SGPropertyNode const> _propertyNode;
295 SGSharedPtr<SGInterpTable const> _interpTable;
302 unit_string(const char* value, const char* unit)
304 return std::string(value) + unit;
307 static SGDoubleValue*
308 read_value(const SGPropertyNode* configNode, SGPropertyNode* modelRoot,
309 const char* unit, double defMin, double defMax)
311 std::string inputPropertyName;
312 inputPropertyName = configNode->getStringValue("property", "");
313 if (!inputPropertyName.empty()) {
314 SGPropertyNode* inputProperty;
315 inputProperty = modelRoot->getNode(inputPropertyName.c_str(), true);
316 SGInterpTable* interpTable = read_interpolation_table(configNode);
318 SGInterpTableValue* value;
319 value = new SGInterpTableValue(inputProperty, interpTable);
322 std::string offset = unit_string("offset", unit);
323 std::string min = unit_string("min", unit);
324 std::string max = unit_string("max", unit);
326 if (configNode->getBoolValue("use-personality", false)) {
327 SGPersScaleOffsetValue* value;
328 value = new SGPersScaleOffsetValue(inputProperty, configNode,
329 "factor", offset.c_str());
330 value->setMin(configNode->getDoubleValue(min.c_str(), defMin));
331 value->setMax(configNode->getDoubleValue(max.c_str(), defMax));
334 SGScaleOffsetValue* value = new SGScaleOffsetValue(inputProperty);
335 value->setScale(configNode->getDoubleValue("factor", 1));
336 value->setOffset(configNode->getDoubleValue(offset.c_str(), 0));
337 value->setMin(configNode->getDoubleValue(min.c_str(), defMin));
338 value->setMax(configNode->getDoubleValue(max.c_str(), defMax));
347 ////////////////////////////////////////////////////////////////////////
348 // Animation installer
349 ////////////////////////////////////////////////////////////////////////
351 class SGAnimation::RemoveModeVisitor : public SGStateAttributeVisitor {
353 RemoveModeVisitor(osg::StateAttribute::GLMode mode) :
356 virtual void apply(osg::StateSet* stateSet)
360 stateSet->removeMode(_mode);
363 osg::StateAttribute::GLMode _mode;
366 class SGAnimation::RemoveAttributeVisitor : public SGStateAttributeVisitor {
368 RemoveAttributeVisitor(osg::StateAttribute::Type type) :
371 virtual void apply(osg::StateSet* stateSet)
375 while (stateSet->getAttribute(_type)) {
376 stateSet->removeAttribute(_type);
380 osg::StateAttribute::Type _type;
383 class SGAnimation::RemoveTextureModeVisitor : public SGStateAttributeVisitor {
385 RemoveTextureModeVisitor(unsigned unit, osg::StateAttribute::GLMode mode) :
389 virtual void apply(osg::StateSet* stateSet)
393 stateSet->removeTextureMode(_unit, _mode);
397 osg::StateAttribute::GLMode _mode;
400 class SGAnimation::RemoveTextureAttributeVisitor :
401 public SGStateAttributeVisitor {
403 RemoveTextureAttributeVisitor(unsigned unit,
404 osg::StateAttribute::Type type) :
408 virtual void apply(osg::StateSet* stateSet)
412 while (stateSet->getTextureAttribute(_unit, _type)) {
413 stateSet->removeTextureAttribute(_unit, _type);
418 osg::StateAttribute::Type _type;
421 class SGAnimation::BinToInheritVisitor : public SGStateAttributeVisitor {
423 virtual void apply(osg::StateSet* stateSet)
427 stateSet->setRenderBinToInherit();
431 class SGAnimation::DrawableCloneVisitor : public osg::NodeVisitor {
433 DrawableCloneVisitor() :
434 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
436 void apply(osg::Geode& geode)
438 for (unsigned i = 0 ; i < geode.getNumDrawables(); ++i) {
439 osg::CopyOp copyOp(osg::CopyOp::DEEP_COPY_ALL &
440 ~osg::CopyOp::DEEP_COPY_TEXTURES);
441 geode.setDrawable(i, copyOp(geode.getDrawable(i)));
447 SGAnimation::SGAnimation(const SGPropertyNode* configNode,
448 SGPropertyNode* modelRoot) :
449 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
451 _configNode(configNode),
452 _modelRoot(modelRoot)
454 _name = configNode->getStringValue("name", "");
455 _enableHOT = configNode->getBoolValue("enable-hot", true);
456 _disableShadow = configNode->getBoolValue("disable-shadow", false);
457 std::vector<SGPropertyNode_ptr> objectNames =
458 configNode->getChildren("object-name");
459 for (unsigned i = 0; i < objectNames.size(); ++i)
460 _objectNames.push_back(objectNames[i]->getStringValue());
463 SGAnimation::~SGAnimation()
468 SG_LOG(SG_IO, SG_ALERT, "Could not find at least one of the following"
469 " objects for animation:\n");
470 std::list<std::string>::const_iterator i;
471 for (i = _objectNames.begin(); i != _objectNames.end(); ++i)
472 SG_LOG(SG_IO, SG_ALERT, *i << "\n");
476 SGAnimation::animate(osg::Node* node, const SGPropertyNode* configNode,
477 SGPropertyNode* modelRoot)
479 std::string type = configNode->getStringValue("type", "none");
480 if (type == "alpha-test") {
481 SGAlphaTestAnimation animInst(configNode, modelRoot);
482 animInst.apply(node);
483 } else if (type == "billboard") {
484 SGBillboardAnimation animInst(configNode, modelRoot);
485 animInst.apply(node);
486 } else if (type == "blend") {
487 SGBlendAnimation animInst(configNode, modelRoot);
488 animInst.apply(node);
489 } else if (type == "dist-scale") {
490 SGDistScaleAnimation animInst(configNode, modelRoot);
491 animInst.apply(node);
492 } else if (type == "flash") {
493 SGFlashAnimation animInst(configNode, modelRoot);
494 animInst.apply(node);
495 } else if (type == "material") {
496 SGMaterialAnimation animInst(configNode, modelRoot);
497 animInst.apply(node);
498 } else if (type == "noshadow") {
499 SGShadowAnimation animInst(configNode, modelRoot);
500 animInst.apply(node);
501 } else if (type == "pick") {
502 SGPickAnimation animInst(configNode, modelRoot);
503 animInst.apply(node);
504 } else if (type == "range") {
505 SGRangeAnimation animInst(configNode, modelRoot);
506 animInst.apply(node);
507 } else if (type == "rotate" || type == "spin") {
508 SGRotateAnimation animInst(configNode, modelRoot);
509 animInst.apply(node);
510 } else if (type == "scale") {
511 SGScaleAnimation animInst(configNode, modelRoot);
512 animInst.apply(node);
513 } else if (type == "select") {
514 SGSelectAnimation animInst(configNode, modelRoot);
515 animInst.apply(node);
516 } else if (type == "shader") {
517 SGShaderAnimation animInst(configNode, modelRoot);
518 animInst.apply(node);
519 } else if (type == "textranslate" || type == "texrotate" ||
520 type == "texmultiple") {
521 SGTexTransformAnimation animInst(configNode, modelRoot);
522 animInst.apply(node);
523 } else if (type == "timed") {
524 SGTimedAnimation animInst(configNode, modelRoot);
525 animInst.apply(node);
526 } else if (type == "translate") {
527 SGTranslateAnimation animInst(configNode, modelRoot);
528 animInst.apply(node);
529 } else if (type == "null" || type == "none" || type.empty()) {
530 SGGroupAnimation animInst(configNode, modelRoot);
531 animInst.apply(node);
540 SGAnimation::apply(osg::Node* node)
542 // duh what a special case ...
543 if (_objectNames.empty()) {
544 osg::Group* group = node->asGroup();
546 osg::ref_ptr<osg::Group> animationGroup;
547 installInGroup(std::string(), *group, animationGroup);
554 SGAnimation::install(osg::Node& node)
558 node.setNodeMask( SG_NODEMASK_TERRAIN_BIT | node.getNodeMask());
560 node.setNodeMask(~SG_NODEMASK_TERRAIN_BIT & node.getNodeMask());
562 node.setNodeMask( SG_NODEMASK_CASTSHADOW_BIT | node.getNodeMask());
564 node.setNodeMask(~SG_NODEMASK_CASTSHADOW_BIT & node.getNodeMask());
568 SGAnimation::createAnimationGroup(osg::Group& parent)
570 // default implementation, we do not need a new group
571 // for every animation type. Usually animations that just change
572 // the StateSet of some parts of the model
577 SGAnimation::apply(osg::Group& group)
579 // the trick is to first traverse the children and then
580 // possibly splice in a new group node if required.
581 // Else we end up in a recursive loop where we infinitly insert new
585 // Note that this algorithm preserves the order of the child objects
586 // like they appear in the object-name tags.
587 // The timed animations require this
588 osg::ref_ptr<osg::Group> animationGroup;
589 std::list<std::string>::const_iterator nameIt;
590 for (nameIt = _objectNames.begin(); nameIt != _objectNames.end(); ++nameIt)
591 installInGroup(*nameIt, group, animationGroup);
595 SGAnimation::installInGroup(const std::string& name, osg::Group& group,
596 osg::ref_ptr<osg::Group>& animationGroup)
598 int i = group.getNumChildren() - 1;
599 for (; 0 <= i; --i) {
600 osg::Node* child = group.getChild(i);
602 // Check if this one is already processed
603 if (std::find(_installedAnimations.begin(),
604 _installedAnimations.end(), child)
605 != _installedAnimations.end())
608 if (name.empty() || child->getName() == name) {
609 // fire the installation of the animation
612 // create a group node on demand
613 if (!animationGroup.valid()) {
614 animationGroup = createAnimationGroup(group);
615 // Animation type that does not require a new group,
616 // in this case we can stop and look for the next object
617 if (animationGroup.valid() && !_name.empty())
618 animationGroup->setName(_name);
620 if (animationGroup.valid()) {
621 animationGroup->addChild(child);
622 group.removeChild(i);
625 // store that we already have processed this child node
626 // We can hit this one twice if an animation references some
627 // part of a subtree twice
628 _installedAnimations.push_back(child);
634 SGAnimation::removeMode(osg::Node& node, osg::StateAttribute::GLMode mode)
636 RemoveModeVisitor visitor(mode);
637 node.accept(visitor);
641 SGAnimation::removeAttribute(osg::Node& node, osg::StateAttribute::Type type)
643 RemoveAttributeVisitor visitor(type);
644 node.accept(visitor);
648 SGAnimation::removeTextureMode(osg::Node& node, unsigned unit,
649 osg::StateAttribute::GLMode mode)
651 RemoveTextureModeVisitor visitor(unit, mode);
652 node.accept(visitor);
656 SGAnimation::removeTextureAttribute(osg::Node& node, unsigned unit,
657 osg::StateAttribute::Type type)
659 RemoveTextureAttributeVisitor visitor(unit, type);
660 node.accept(visitor);
664 SGAnimation::setRenderBinToInherit(osg::Node& node)
666 BinToInheritVisitor visitor;
667 node.accept(visitor);
671 SGAnimation::cloneDrawables(osg::Node& node)
673 DrawableCloneVisitor visitor;
674 node.accept(visitor);
678 SGAnimation::getCondition() const
680 const SGPropertyNode* conditionNode = _configNode->getChild("condition");
683 return sgReadCondition(_modelRoot, conditionNode);
688 ////////////////////////////////////////////////////////////////////////
689 // Implementation of null animation
690 ////////////////////////////////////////////////////////////////////////
692 // Ok, that is to build a subgraph from different other
693 // graph nodes. I guess that this stems from the time where modellers
694 // could not build hierarchical trees ...
695 SGGroupAnimation::SGGroupAnimation(const SGPropertyNode* configNode,
696 SGPropertyNode* modelRoot):
697 SGAnimation(configNode, modelRoot)
702 SGGroupAnimation::createAnimationGroup(osg::Group& parent)
704 osg::Group* group = new osg::Group;
705 parent.addChild(group);
710 ////////////////////////////////////////////////////////////////////////
711 // Implementation of translate animation
712 ////////////////////////////////////////////////////////////////////////
714 class SGTranslateAnimation::Transform : public osg::Transform {
719 { setReferenceFrame(RELATIVE_RF); }
720 void setAxis(const SGVec3d& axis)
721 { _axis = axis; dirtyBound(); }
722 void setValue(double value)
723 { _value = value; dirtyBound(); }
724 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
725 osg::NodeVisitor* nv) const
727 assert(_referenceFrame == RELATIVE_RF);
729 set_translation(tmp, _value, _axis);
733 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
734 osg::NodeVisitor* nv) const
736 assert(_referenceFrame == RELATIVE_RF);
738 set_translation(tmp, -_value, _axis);
739 matrix.postMult(tmp);
747 class SGTranslateAnimation::UpdateCallback : public osg::NodeCallback {
749 UpdateCallback(SGCondition const* condition,
750 SGDoubleValue const* animationValue) :
751 _condition(condition),
752 _animationValue(animationValue)
754 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
756 if (!_condition || _condition->test()) {
757 SGTranslateAnimation::Transform* transform;
758 transform = static_cast<SGTranslateAnimation::Transform*>(node);
759 transform->setValue(_animationValue->getValue());
764 SGSharedPtr<SGCondition const> _condition;
765 SGSharedPtr<SGDoubleValue const> _animationValue;
768 SGTranslateAnimation::SGTranslateAnimation(const SGPropertyNode* configNode,
769 SGPropertyNode* modelRoot) :
770 SGAnimation(configNode, modelRoot)
772 _condition = getCondition();
773 _animationValue = read_value(configNode, modelRoot, "-m",
774 -SGLimitsd::max(), SGLimitsd::max());
775 _axis[0] = configNode->getDoubleValue("axis/x", 0);
776 _axis[1] = configNode->getDoubleValue("axis/y", 0);
777 _axis[2] = configNode->getDoubleValue("axis/z", 0);
778 if (8*SGLimitsd::min() < norm(_axis))
779 _axis = normalize(_axis);
781 _initialValue = configNode->getDoubleValue("starting-position-m", 0);
782 _initialValue *= configNode->getDoubleValue("factor", 1);
783 _initialValue += configNode->getDoubleValue("offset-m", 0);
787 SGTranslateAnimation::createAnimationGroup(osg::Group& parent)
789 Transform* transform = new Transform;
790 transform->setName("translate animation");
791 if (_animationValue) {
792 UpdateCallback* uc = new UpdateCallback(_condition, _animationValue);
793 transform->setUpdateCallback(uc);
795 transform->setAxis(_axis);
796 transform->setValue(_initialValue);
797 parent.addChild(transform);
802 ////////////////////////////////////////////////////////////////////////
803 // Implementation of rotate/spin animation
804 ////////////////////////////////////////////////////////////////////////
806 class SGRotateAnimation::Transform : public osg::Transform {
809 { setReferenceFrame(RELATIVE_RF); }
810 void setCenter(const SGVec3d& center)
811 { _center = center; dirtyBound(); }
812 void setAxis(const SGVec3d& axis)
813 { _axis = axis; dirtyBound(); }
814 void setAngle(double angle)
816 double getAngle() const
818 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
819 osg::NodeVisitor* nv) const
821 // This is the fast path, optimize a bit
822 assert(_referenceFrame == RELATIVE_RF);
825 set_rotation(tmp, _angle, _center, _axis);
829 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
830 osg::NodeVisitor* nv) const
832 assert(_referenceFrame == RELATIVE_RF);
835 set_rotation(tmp, -_angle, _center, _axis);
836 matrix.postMult(tmp);
839 virtual osg::BoundingSphere computeBound() const
841 osg::BoundingSphere bs = osg::Group::computeBound();
842 osg::BoundingSphere centerbs(_center.osg(), bs.radius());
843 centerbs.expandBy(bs);
853 class SGRotateAnimation::UpdateCallback : public osg::NodeCallback {
855 UpdateCallback(SGCondition const* condition,
856 SGDoubleValue const* animationValue) :
857 _condition(condition),
858 _animationValue(animationValue)
860 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
862 if (!_condition || _condition->test()) {
863 SGRotateAnimation::Transform* transform;
864 transform = static_cast<SGRotateAnimation::Transform*>(node);
865 transform->setAngle(_animationValue->getValue());
870 SGSharedPtr<SGCondition const> _condition;
871 SGSharedPtr<SGDoubleValue const> _animationValue;
874 class SGRotateAnimation::SpinUpdateCallback : public osg::NodeCallback {
876 SpinUpdateCallback(SGCondition const* condition,
877 SGDoubleValue const* animationValue) :
878 _condition(condition),
879 _animationValue(animationValue),
882 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
884 if (!_condition || _condition->test()) {
885 SGRotateAnimation::Transform* transform;
886 transform = static_cast<SGRotateAnimation::Transform*>(node);
888 double t = nv->getFrameStamp()->getReferenceTime();
893 double velocity_rpms = _animationValue->getValue()/60;
894 double angle = transform->getAngle();
895 angle += dt*velocity_rpms*360;
896 angle -= 360*floor(angle/360);
897 transform->setAngle(angle);
902 SGSharedPtr<SGCondition const> _condition;
903 SGSharedPtr<SGDoubleValue const> _animationValue;
907 SGRotateAnimation::SGRotateAnimation(const SGPropertyNode* configNode, SGPropertyNode* modelRoot) :
908 SGAnimation(configNode, modelRoot)
910 std::string type = configNode->getStringValue("type", "");
911 _isSpin = (type == "spin");
913 _condition = getCondition();
914 _animationValue = read_value(configNode, modelRoot, "-deg",
915 -SGLimitsd::max(), SGLimitsd::max());
916 _initialValue = configNode->getDoubleValue("starting-position-deg", 0);
917 _initialValue *= configNode->getDoubleValue("factor", 1);
918 _initialValue += configNode->getDoubleValue("offset-deg", 0);
920 _center = SGVec3d::zeros();
921 if (configNode->hasValue("axis/x1-m")) {
923 v1[0] = configNode->getDoubleValue("axis/x1-m", 0);
924 v1[1] = configNode->getDoubleValue("axis/y1-m", 0);
925 v1[2] = configNode->getDoubleValue("axis/z1-m", 0);
926 v2[0] = configNode->getDoubleValue("axis/x2-m", 0);
927 v2[1] = configNode->getDoubleValue("axis/y2-m", 0);
928 v2[2] = configNode->getDoubleValue("axis/z2-m", 0);
929 _center = 0.5*(v1+v2);
932 _axis[0] = configNode->getDoubleValue("axis/x", 0);
933 _axis[1] = configNode->getDoubleValue("axis/y", 0);
934 _axis[2] = configNode->getDoubleValue("axis/z", 0);
936 if (8*SGLimitsd::min() < norm(_axis))
937 _axis = normalize(_axis);
939 _center[0] = configNode->getDoubleValue("center/x-m", _center[0]);
940 _center[1] = configNode->getDoubleValue("center/y-m", _center[1]);
941 _center[2] = configNode->getDoubleValue("center/z-m", _center[2]);
945 SGRotateAnimation::createAnimationGroup(osg::Group& parent)
947 Transform* transform = new Transform;
948 transform->setName("rotate animation");
950 SpinUpdateCallback* uc;
951 uc = new SpinUpdateCallback(_condition, _animationValue);
952 transform->setUpdateCallback(uc);
953 } else if (_animationValue) {
954 UpdateCallback* uc = new UpdateCallback(_condition, _animationValue);
955 transform->setUpdateCallback(uc);
957 transform->setCenter(_center);
958 transform->setAxis(_axis);
959 transform->setAngle(_initialValue);
960 parent.addChild(transform);
965 ////////////////////////////////////////////////////////////////////////
966 // Implementation of scale animation
967 ////////////////////////////////////////////////////////////////////////
969 class SGScaleAnimation::Transform : public osg::Transform {
973 _scaleFactor(1, 1, 1),
976 setReferenceFrame(RELATIVE_RF);
978 void setCenter(const SGVec3d& center)
983 void setScaleFactor(const SGVec3d& scaleFactor)
985 if (_boundScale < normI(scaleFactor))
987 _scaleFactor = scaleFactor;
989 void setScaleFactor(double scaleFactor)
991 if (_boundScale < fabs(scaleFactor))
993 _scaleFactor = SGVec3d(scaleFactor, scaleFactor, scaleFactor);
995 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
996 osg::NodeVisitor* nv) const
998 assert(_referenceFrame == RELATIVE_RF);
999 osg::Matrix transform;
1000 transform(0,0) = _scaleFactor[0];
1001 transform(1,1) = _scaleFactor[1];
1002 transform(2,2) = _scaleFactor[2];
1003 transform(3,0) = _center[0]*(1 - _scaleFactor[0]);
1004 transform(3,1) = _center[1]*(1 - _scaleFactor[1]);
1005 transform(3,2) = _center[2]*(1 - _scaleFactor[2]);
1006 matrix.preMult(transform);
1009 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
1010 osg::NodeVisitor* nv) const
1012 assert(_referenceFrame == RELATIVE_RF);
1013 if (fabs(_scaleFactor[0]) < SGLimitsd::min())
1015 if (fabs(_scaleFactor[1]) < SGLimitsd::min())
1017 if (fabs(_scaleFactor[2]) < SGLimitsd::min())
1019 SGVec3d rScaleFactor(1/_scaleFactor[0],
1022 osg::Matrix transform;
1023 transform(0,0) = rScaleFactor[0];
1024 transform(1,1) = rScaleFactor[1];
1025 transform(2,2) = rScaleFactor[2];
1026 transform(3,0) = _center[0]*(1 - rScaleFactor[0]);
1027 transform(3,1) = _center[1]*(1 - rScaleFactor[1]);
1028 transform(3,2) = _center[2]*(1 - rScaleFactor[2]);
1029 matrix.postMult(transform);
1032 virtual osg::BoundingSphere computeBound() const
1034 osg::BoundingSphere bs = osg::Group::computeBound();
1035 _boundScale = normI(_scaleFactor);
1036 bs.radius() *= _boundScale;
1042 SGVec3d _scaleFactor;
1043 mutable double _boundScale;
1046 class SGScaleAnimation::UpdateCallback : public osg::NodeCallback {
1048 UpdateCallback(const SGCondition* condition,
1049 SGSharedPtr<const SGDoubleValue> animationValue[3]) :
1050 _condition(condition)
1052 _animationValue[0] = animationValue[0];
1053 _animationValue[1] = animationValue[1];
1054 _animationValue[2] = animationValue[2];
1056 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1058 if (!_condition || _condition->test()) {
1059 SGScaleAnimation::Transform* transform;
1060 transform = static_cast<SGScaleAnimation::Transform*>(node);
1061 SGVec3d scale(_animationValue[0]->getValue(),
1062 _animationValue[1]->getValue(),
1063 _animationValue[2]->getValue());
1064 transform->setScaleFactor(scale);
1069 SGSharedPtr<SGCondition const> _condition;
1070 SGSharedPtr<SGDoubleValue const> _animationValue[3];
1073 SGScaleAnimation::SGScaleAnimation(const SGPropertyNode* configNode,
1074 SGPropertyNode* modelRoot) :
1075 SGAnimation(configNode, modelRoot)
1077 _condition = getCondition();
1079 // default offset/factor for all directions
1080 double offset = configNode->getDoubleValue("offset", 1);
1081 double factor = configNode->getDoubleValue("factor", 1);
1083 std::string inputPropertyName;
1084 inputPropertyName = configNode->getStringValue("property", "");
1085 SGPropertyNode* inputProperty = 0;
1086 if (!inputPropertyName.empty()) {
1087 inputProperty = modelRoot->getNode(inputPropertyName.c_str(), true);
1089 SGInterpTable* interpTable = read_interpolation_table(configNode);
1091 SGInterpTableValue* value;
1092 value = new SGInterpTableValue(inputProperty, interpTable);
1093 _animationValue[0] = value;
1094 _animationValue[1] = value;
1095 _animationValue[2] = value;
1096 } else if (configNode->getBoolValue("use-personality", false)) {
1097 SGPersScaleOffsetValue* value;
1098 value = new SGPersScaleOffsetValue(inputProperty, configNode,
1099 "x-factor", "x-offset",
1101 value->setMin(configNode->getDoubleValue("x-min", 0));
1102 value->setMax(configNode->getDoubleValue("x-max", SGLimitsd::max()));
1103 _animationValue[0] = value;
1104 value = new SGPersScaleOffsetValue(inputProperty, configNode,
1105 "y-factor", "y-offset",
1107 value->setMin(configNode->getDoubleValue("y-min", 0));
1108 value->setMax(configNode->getDoubleValue("y-max", SGLimitsd::max()));
1109 _animationValue[1] = value;
1110 value = new SGPersScaleOffsetValue(inputProperty, configNode,
1111 "z-factor", "z-offset",
1113 value->setMin(configNode->getDoubleValue("z-min", 0));
1114 value->setMax(configNode->getDoubleValue("z-max", SGLimitsd::max()));
1115 _animationValue[2] = value;
1117 SGScaleOffsetValue* value = new SGScaleOffsetValue(inputProperty);
1118 value->setScale(configNode->getDoubleValue("x-factor", factor));
1119 value->setOffset(configNode->getDoubleValue("x-offset", offset));
1120 value->setMin(configNode->getDoubleValue("x-min", 0));
1121 value->setMax(configNode->getDoubleValue("x-max", SGLimitsd::max()));
1122 _animationValue[0] = value;
1123 value = new SGScaleOffsetValue(inputProperty);
1124 value->setScale(configNode->getDoubleValue("y-factor", factor));
1125 value->setOffset(configNode->getDoubleValue("y-offset", offset));
1126 value->setMin(configNode->getDoubleValue("y-min", 0));
1127 value->setMax(configNode->getDoubleValue("y-max", SGLimitsd::max()));
1128 _animationValue[1] = value;
1129 value = new SGScaleOffsetValue(inputProperty);
1130 value->setScale(configNode->getDoubleValue("z-factor", factor));
1131 value->setOffset(configNode->getDoubleValue("z-offset", offset));
1132 value->setMin(configNode->getDoubleValue("z-min", 0));
1133 value->setMax(configNode->getDoubleValue("z-max", SGLimitsd::max()));
1134 _animationValue[2] = value;
1136 _initialValue[0] = configNode->getDoubleValue("x-starting-scale", 1);
1137 _initialValue[0] *= configNode->getDoubleValue("x-factor", factor);
1138 _initialValue[0] += configNode->getDoubleValue("x-offset", offset);
1139 _initialValue[1] = configNode->getDoubleValue("y-starting-scale", 1);
1140 _initialValue[1] *= configNode->getDoubleValue("y-factor", factor);
1141 _initialValue[1] += configNode->getDoubleValue("y-offset", offset);
1142 _initialValue[2] = configNode->getDoubleValue("z-starting-scale", 1);
1143 _initialValue[2] *= configNode->getDoubleValue("z-factor", factor);
1144 _initialValue[2] += configNode->getDoubleValue("z-offset", offset);
1145 _center[0] = configNode->getDoubleValue("center/x-m", 0);
1146 _center[1] = configNode->getDoubleValue("center/y-m", 0);
1147 _center[2] = configNode->getDoubleValue("center/z-m", 0);
1151 SGScaleAnimation::createAnimationGroup(osg::Group& parent)
1153 Transform* transform = new Transform;
1154 transform->setName("scale animation");
1155 transform->setCenter(_center);
1156 transform->setScaleFactor(_initialValue);
1157 UpdateCallback* uc = new UpdateCallback(_condition, _animationValue);
1158 transform->setUpdateCallback(uc);
1159 parent.addChild(transform);
1164 ////////////////////////////////////////////////////////////////////////
1165 // Implementation of dist scale animation
1166 ////////////////////////////////////////////////////////////////////////
1168 class SGDistScaleAnimation::Transform : public osg::Transform {
1170 Transform(const SGPropertyNode* configNode)
1172 setName(configNode->getStringValue("name", "dist scale animation"));
1173 setReferenceFrame(RELATIVE_RF);
1174 getOrCreateStateSet()->setMode(GL_NORMALIZE, osg::StateAttribute::ON);
1175 _factor = configNode->getFloatValue("factor", 1);
1176 _offset = configNode->getFloatValue("offset", 0);
1177 _min_v = configNode->getFloatValue("min", SGLimitsf::epsilon());
1178 _max_v = configNode->getFloatValue("max", SGLimitsf::max());
1179 _table = read_interpolation_table(configNode);
1180 _center[0] = configNode->getFloatValue("center/x-m", 0);
1181 _center[1] = configNode->getFloatValue("center/y-m", 0);
1182 _center[2] = configNode->getFloatValue("center/z-m", 0);
1184 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
1185 osg::NodeVisitor* nv) const
1187 osg::Matrix transform;
1188 double scale_factor = computeScaleFactor(nv);
1189 transform(0,0) = scale_factor;
1190 transform(1,1) = scale_factor;
1191 transform(2,2) = scale_factor;
1192 transform(3,0) = _center[0]*(1 - scale_factor);
1193 transform(3,1) = _center[1]*(1 - scale_factor);
1194 transform(3,2) = _center[2]*(1 - scale_factor);
1195 matrix.preMult(transform);
1199 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
1200 osg::NodeVisitor* nv) const
1202 double scale_factor = computeScaleFactor(nv);
1203 if (fabs(scale_factor) <= SGLimits<double>::min())
1205 osg::Matrix transform;
1206 double rScaleFactor = 1/scale_factor;
1207 transform(0,0) = rScaleFactor;
1208 transform(1,1) = rScaleFactor;
1209 transform(2,2) = rScaleFactor;
1210 transform(3,0) = _center[0]*(1 - rScaleFactor);
1211 transform(3,1) = _center[1]*(1 - rScaleFactor);
1212 transform(3,2) = _center[2]*(1 - rScaleFactor);
1213 matrix.postMult(transform);
1218 double computeScaleFactor(osg::NodeVisitor* nv) const
1223 double scale_factor = (_center.osg() - nv->getEyePoint()).length();
1225 scale_factor = _factor * scale_factor + _offset;
1227 scale_factor = _table->interpolate( scale_factor );
1229 if (scale_factor < _min_v)
1230 scale_factor = _min_v;
1231 if (scale_factor > _max_v)
1232 scale_factor = _max_v;
1234 return scale_factor;
1237 SGSharedPtr<SGInterpTable> _table;
1246 SGDistScaleAnimation::SGDistScaleAnimation(const SGPropertyNode* configNode,
1247 SGPropertyNode* modelRoot) :
1248 SGAnimation(configNode, modelRoot)
1253 SGDistScaleAnimation::createAnimationGroup(osg::Group& parent)
1255 Transform* transform = new Transform(getConfig());
1256 parent.addChild(transform);
1261 ////////////////////////////////////////////////////////////////////////
1262 // Implementation of flash animation
1263 ////////////////////////////////////////////////////////////////////////
1265 class SGFlashAnimation::Transform : public osg::Transform {
1267 Transform(const SGPropertyNode* configNode)
1269 setReferenceFrame(RELATIVE_RF);
1270 setName(configNode->getStringValue("name", "flash animation"));
1271 getOrCreateStateSet()->setMode(GL_NORMALIZE, osg::StateAttribute::ON);
1273 _axis[0] = configNode->getFloatValue("axis/x", 0);
1274 _axis[1] = configNode->getFloatValue("axis/y", 0);
1275 _axis[2] = configNode->getFloatValue("axis/z", 1);
1278 _center[0] = configNode->getFloatValue("center/x-m", 0);
1279 _center[1] = configNode->getFloatValue("center/y-m", 0);
1280 _center[2] = configNode->getFloatValue("center/z-m", 0);
1282 _offset = configNode->getFloatValue("offset", 0);
1283 _factor = configNode->getFloatValue("factor", 1);
1284 _power = configNode->getFloatValue("power", 1);
1285 _two_sides = configNode->getBoolValue("two-sides", false);
1287 _min_v = configNode->getFloatValue("min", SGLimitsf::epsilon());
1288 _max_v = configNode->getFloatValue("max", 1);
1290 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
1291 osg::NodeVisitor* nv) const
1293 osg::Matrix transform;
1294 double scale_factor = computeScaleFactor(nv);
1295 transform(0,0) = scale_factor;
1296 transform(1,1) = scale_factor;
1297 transform(2,2) = scale_factor;
1298 transform(3,0) = _center[0]*(1 - scale_factor);
1299 transform(3,1) = _center[1]*(1 - scale_factor);
1300 transform(3,2) = _center[2]*(1 - scale_factor);
1301 matrix.preMult(transform);
1305 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
1306 osg::NodeVisitor* nv) const
1308 double scale_factor = computeScaleFactor(nv);
1309 if (fabs(scale_factor) <= SGLimits<double>::min())
1311 osg::Matrix transform;
1312 double rScaleFactor = 1/scale_factor;
1313 transform(0,0) = rScaleFactor;
1314 transform(1,1) = rScaleFactor;
1315 transform(2,2) = rScaleFactor;
1316 transform(3,0) = _center[0]*(1 - rScaleFactor);
1317 transform(3,1) = _center[1]*(1 - rScaleFactor);
1318 transform(3,2) = _center[2]*(1 - rScaleFactor);
1319 matrix.postMult(transform);
1324 double computeScaleFactor(osg::NodeVisitor* nv) const
1329 osg::Vec3 localEyeToCenter = nv->getEyePoint() - _center;
1330 localEyeToCenter.normalize();
1332 double cos_angle = localEyeToCenter*_axis;
1333 double scale_factor = 0;
1334 if ( _two_sides && cos_angle < 0 )
1335 scale_factor = _factor * pow( -cos_angle, _power ) + _offset;
1336 else if ( cos_angle > 0 )
1337 scale_factor = _factor * pow( cos_angle, _power ) + _offset;
1339 if ( scale_factor < _min_v )
1340 scale_factor = _min_v;
1341 if ( scale_factor > _max_v )
1342 scale_factor = _max_v;
1344 return scale_factor;
1347 virtual osg::BoundingSphere computeBound() const
1349 // avoid being culled away by small feature culling
1350 osg::BoundingSphere bs = osg::Group::computeBound();
1351 bs.radius() *= _max_v;
1358 double _power, _factor, _offset, _min_v, _max_v;
1363 SGFlashAnimation::SGFlashAnimation(const SGPropertyNode* configNode,
1364 SGPropertyNode* modelRoot) :
1365 SGAnimation(configNode, modelRoot)
1370 SGFlashAnimation::createAnimationGroup(osg::Group& parent)
1372 Transform* transform = new Transform(getConfig());
1373 parent.addChild(transform);
1378 ////////////////////////////////////////////////////////////////////////
1379 // Implementation of flash animation
1380 ////////////////////////////////////////////////////////////////////////
1382 class SGBillboardAnimation::Transform : public osg::Transform {
1384 Transform(const SGPropertyNode* configNode) :
1385 _spherical(configNode->getBoolValue("spherical", true))
1387 setReferenceFrame(RELATIVE_RF);
1388 setName(configNode->getStringValue("name", "billboard animation"));
1390 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
1391 osg::NodeVisitor* nv) const
1393 // More or less taken from plibs ssgCutout
1395 matrix(0,0) = 1; matrix(0,1) = 0; matrix(0,2) = 0;
1396 matrix(1,0) = 0; matrix(1,1) = 0; matrix(1,2) = -1;
1397 matrix(2,0) = 0; matrix(2,1) = 1; matrix(2,2) = 0;
1399 osg::Vec3 zAxis(matrix(2, 0), matrix(2, 1), matrix(2, 2));
1400 osg::Vec3 xAxis = osg::Vec3(0, 0, -1)^zAxis;
1401 osg::Vec3 yAxis = zAxis^xAxis;
1407 matrix(0,0) = xAxis[0]; matrix(0,1) = xAxis[1]; matrix(0,2) = xAxis[2];
1408 matrix(1,0) = yAxis[0]; matrix(1,1) = yAxis[1]; matrix(1,2) = yAxis[2];
1409 matrix(2,0) = zAxis[0]; matrix(2,1) = zAxis[1]; matrix(2,2) = zAxis[2];
1414 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
1415 osg::NodeVisitor* nv) const
1417 // Hmm, don't yet know how to get that back ...
1426 SGBillboardAnimation::SGBillboardAnimation(const SGPropertyNode* configNode,
1427 SGPropertyNode* modelRoot) :
1428 SGAnimation(configNode, modelRoot)
1433 SGBillboardAnimation::createAnimationGroup(osg::Group& parent)
1435 Transform* transform = new Transform(getConfig());
1436 parent.addChild(transform);
1441 ////////////////////////////////////////////////////////////////////////
1442 // Implementation of a range animation
1443 ////////////////////////////////////////////////////////////////////////
1445 class SGRangeAnimation::UpdateCallback : public osg::NodeCallback {
1447 UpdateCallback(const SGCondition* condition,
1448 const SGDoubleValue* minAnimationValue,
1449 const SGDoubleValue* maxAnimationValue,
1450 double minValue, double maxValue) :
1451 _condition(condition),
1452 _minAnimationValue(minAnimationValue),
1453 _maxAnimationValue(maxAnimationValue),
1454 _minStaticValue(minValue),
1455 _maxStaticValue(maxValue)
1457 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1459 osg::LOD* lod = static_cast<osg::LOD*>(node);
1460 if (!_condition || _condition->test()) {
1462 if (_minAnimationValue)
1463 minRange = _minAnimationValue->getValue();
1465 minRange = _minStaticValue;
1467 if (_maxAnimationValue)
1468 maxRange = _maxAnimationValue->getValue();
1470 maxRange = _maxStaticValue;
1471 lod->setRange(0, minRange, maxRange);
1473 lod->setRange(0, 0, SGLimitsf::max());
1479 SGSharedPtr<const SGCondition> _condition;
1480 SGSharedPtr<const SGDoubleValue> _minAnimationValue;
1481 SGSharedPtr<const SGDoubleValue> _maxAnimationValue;
1482 double _minStaticValue;
1483 double _maxStaticValue;
1486 SGRangeAnimation::SGRangeAnimation(const SGPropertyNode* configNode,
1487 SGPropertyNode* modelRoot) :
1488 SGAnimation(configNode, modelRoot)
1490 _condition = getCondition();
1492 std::string inputPropertyName;
1493 inputPropertyName = configNode->getStringValue("min-property", "");
1494 if (!inputPropertyName.empty()) {
1495 SGPropertyNode* inputProperty;
1496 inputProperty = modelRoot->getNode(inputPropertyName.c_str(), true);
1497 SGScaleOffsetValue* value = new SGScaleOffsetValue(inputProperty);
1498 value->setScale(configNode->getDoubleValue("min-factor", 1));
1499 _minAnimationValue = value;
1501 inputPropertyName = configNode->getStringValue("max-property", "");
1502 if (!inputPropertyName.empty()) {
1503 SGPropertyNode* inputProperty;
1504 inputProperty = modelRoot->getNode(inputPropertyName.c_str(), true);
1505 SGScaleOffsetValue* value = new SGScaleOffsetValue(inputProperty);
1506 value->setScale(configNode->getDoubleValue("max-factor", 1));
1507 _maxAnimationValue = value;
1510 _initialValue[0] = configNode->getDoubleValue("min-m", 0);
1511 _initialValue[0] *= configNode->getDoubleValue("min-factor", 1);
1512 _initialValue[1] = configNode->getDoubleValue("max-m", SGLimitsf::max());
1513 _initialValue[1] *= configNode->getDoubleValue("max-factor", 1);
1517 SGRangeAnimation::createAnimationGroup(osg::Group& parent)
1519 osg::Group* group = new osg::Group;
1520 group->setName("range animation group");
1522 osg::LOD* lod = new osg::LOD;
1523 lod->setName("range animation node");
1524 parent.addChild(lod);
1526 lod->addChild(group, _initialValue[0], _initialValue[1]);
1527 lod->setCenterMode(osg::LOD::USE_BOUNDING_SPHERE_CENTER);
1528 lod->setRangeMode(osg::LOD::DISTANCE_FROM_EYE_POINT);
1529 if (_minAnimationValue || _maxAnimationValue || _condition) {
1531 uc = new UpdateCallback(_condition, _minAnimationValue, _maxAnimationValue,
1532 _initialValue[0], _initialValue[1]);
1533 lod->setUpdateCallback(uc);
1539 ////////////////////////////////////////////////////////////////////////
1540 // Implementation of a select animation
1541 ////////////////////////////////////////////////////////////////////////
1543 class SGSelectAnimation::UpdateCallback : public osg::NodeCallback {
1545 UpdateCallback(const SGCondition* condition) :
1546 _condition(condition)
1548 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1550 osg::Switch* sw = static_cast<osg::Switch*>(node);
1551 if (_condition->test())
1552 sw->setAllChildrenOn();
1554 sw->setAllChildrenOff();
1559 SGSharedPtr<SGCondition const> _condition;
1562 SGSelectAnimation::SGSelectAnimation(const SGPropertyNode* configNode,
1563 SGPropertyNode* modelRoot) :
1564 SGAnimation(configNode, modelRoot)
1569 SGSelectAnimation::createAnimationGroup(osg::Group& parent)
1571 // if no condition given, this is a noop.
1572 SGSharedPtr<SGCondition const> condition = getCondition();
1573 // trick, gets deleted with all its 'animated' children
1574 // when the animation installer returns
1576 return new osg::Group;
1578 osg::Switch* sw = new osg::Switch;
1579 sw->setName("select animation node");
1580 sw->setUpdateCallback(new UpdateCallback(condition));
1581 parent.addChild(sw);
1587 ////////////////////////////////////////////////////////////////////////
1588 // Implementation of alpha test animation
1589 ////////////////////////////////////////////////////////////////////////
1591 SGAlphaTestAnimation::SGAlphaTestAnimation(const SGPropertyNode* configNode,
1592 SGPropertyNode* modelRoot) :
1593 SGAnimation(configNode, modelRoot)
1598 SGAlphaTestAnimation::install(osg::Node& node)
1600 SGAnimation::install(node);
1602 cloneDrawables(node);
1603 removeMode(node, GL_ALPHA_TEST);
1604 removeAttribute(node, osg::StateAttribute::ALPHAFUNC);
1606 osg::StateSet* stateSet = node.getOrCreateStateSet();
1607 osg::AlphaFunc* alphaFunc = new osg::AlphaFunc;
1608 alphaFunc->setFunction(osg::AlphaFunc::GREATER);
1609 float alphaClamp = getConfig()->getFloatValue("alpha-factor", 0);
1610 alphaFunc->setReferenceValue(alphaClamp);
1611 stateSet->setAttribute(alphaFunc);
1612 stateSet->setMode(GL_ALPHA_TEST, osg::StateAttribute::ON);
1617 //////////////////////////////////////////////////////////////////////
1618 // Blend animation installer
1619 //////////////////////////////////////////////////////////////////////
1621 class SGBlendAnimation::BlendVisitor : public osg::NodeVisitor {
1623 BlendVisitor(float blend) :
1624 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
1626 { setVisitorType(osg::NodeVisitor::NODE_VISITOR); }
1627 virtual void apply(osg::Node& node)
1629 updateStateSet(node.getStateSet());
1632 virtual void apply(osg::Geode& node)
1634 apply((osg::Node&)node);
1635 unsigned nDrawables = node.getNumDrawables();
1636 for (unsigned i = 0; i < nDrawables; ++i) {
1637 osg::Drawable* drawable = node.getDrawable(i);
1638 updateStateSet(drawable->getStateSet());
1639 osg::Geometry* geometry = drawable->asGeometry();
1642 osg::Array* array = geometry->getColorArray();
1645 osg::Vec4Array* vec4Array = dynamic_cast<osg::Vec4Array*>(array);
1648 geometry->dirtyDisplayList();
1650 for (unsigned k = 0; k < vec4Array->size(); ++k) {
1651 (*vec4Array)[k][3] = _blend;
1655 void updateStateSet(osg::StateSet* stateSet)
1659 osg::StateAttribute* stateAttribute;
1660 stateAttribute = stateSet->getAttribute(osg::StateAttribute::MATERIAL);
1661 if (!stateAttribute)
1663 osg::Material* material = dynamic_cast<osg::Material*>(stateAttribute);
1666 material->setAlpha(osg::Material::FRONT_AND_BACK, _blend);
1668 stateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
1669 stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
1671 stateSet->setRenderingHint(osg::StateSet::DEFAULT_BIN);
1678 class SGBlendAnimation::UpdateCallback : public osg::NodeCallback {
1680 UpdateCallback(const SGPropertyNode* configNode, const SGDoubleValue* v) :
1684 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1686 double blend = _animationValue->getValue();
1687 if (blend != _prev_value) {
1688 _prev_value = blend;
1689 BlendVisitor visitor(1-blend);
1690 node->accept(visitor);
1696 SGSharedPtr<SGDoubleValue const> _animationValue;
1700 SGBlendAnimation::SGBlendAnimation(const SGPropertyNode* configNode,
1701 SGPropertyNode* modelRoot)
1702 : SGAnimation(configNode, modelRoot),
1703 _animationValue(read_value(configNode, modelRoot, "", 0, 1))
1708 SGBlendAnimation::createAnimationGroup(osg::Group& parent)
1710 if (!_animationValue)
1713 osg::Group* group = new osg::Switch;
1714 group->setName("blend animation node");
1715 group->setUpdateCallback(new UpdateCallback(getConfig(), _animationValue));
1716 parent.addChild(group);
1721 SGBlendAnimation::install(osg::Node& node)
1723 SGAnimation::install(node);
1724 // make sure we do not change common geometries,
1725 // that also creates new display lists for these subgeometries.
1726 cloneDrawables(node);
1730 //////////////////////////////////////////////////////////////////////
1731 // Timed animation installer
1732 //////////////////////////////////////////////////////////////////////
1736 class SGTimedAnimation::UpdateCallback : public osg::NodeCallback {
1738 UpdateCallback(const SGPropertyNode* configNode) :
1741 _duration_sec(configNode->getDoubleValue("duration-sec", 1)),
1742 _last_time_sec(SGLimitsd::max()),
1743 _use_personality(configNode->getBoolValue("use-personality", false))
1745 std::vector<SGSharedPtr<SGPropertyNode> > nodes;
1746 nodes = configNode->getChildren("branch-duration-sec");
1747 for (size_t i = 0; i < nodes.size(); ++i) {
1748 unsigned ind = nodes[ i ]->getIndex();
1749 while ( ind >= _durations.size() ) {
1750 _durations.push_back(DurationSpec(_duration_sec));
1752 SGPropertyNode_ptr rNode = nodes[i]->getChild("random");
1754 _durations[ind] = DurationSpec(nodes[ i ]->getDoubleValue());
1756 _durations[ind] = DurationSpec(rNode->getDoubleValue( "min", 0),
1757 rNode->getDoubleValue( "max", 1));
1761 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1763 assert(dynamic_cast<osg::Switch*>(node));
1764 osg::Switch* sw = static_cast<osg::Switch*>(node);
1766 unsigned nChildren = sw->getNumChildren();
1768 // blow up the durations vector to the required size
1769 while (_durations.size() < nChildren) {
1770 _durations.push_back(_duration_sec);
1772 // make sure the current index is an duration that really exists
1773 _current_index = _current_index % nChildren;
1775 // update the time and compute the current systems time value
1776 double t = nv->getFrameStamp()->getReferenceTime();
1777 if (_last_time_sec == SGLimitsd::max()) {
1780 double dt = t - _last_time_sec;
1781 if (_use_personality)
1782 dt *= 1 + 0.2*(0.5 - sg_random());
1787 double currentDuration = _durations[_current_index].get();
1788 while (currentDuration < _reminder) {
1789 _reminder -= currentDuration;
1790 _current_index = (_current_index + 1) % nChildren;
1791 currentDuration = _durations[_current_index].get();
1794 sw->setSingleChildOn(_current_index);
1800 struct DurationSpec {
1801 DurationSpec(double t) :
1802 minTime(SGMiscd::max(0.01, t)),
1803 maxTime(SGMiscd::max(0.01, t))
1805 DurationSpec(double t0, double t1) :
1806 minTime(SGMiscd::max(0.01, t0)),
1807 maxTime(SGMiscd::max(0.01, t1))
1810 { return minTime + sg_random()*(maxTime - minTime); }
1814 std::vector<DurationSpec> _durations;
1815 unsigned _current_index;
1817 double _duration_sec;
1818 double _last_time_sec;
1819 bool _use_personality;
1823 SGTimedAnimation::SGTimedAnimation(const SGPropertyNode* configNode,
1824 SGPropertyNode* modelRoot)
1825 : SGAnimation(configNode, modelRoot)
1830 SGTimedAnimation::createAnimationGroup(osg::Group& parent)
1832 osg::Switch* sw = new osg::Switch;
1833 sw->setName("timed animation node");
1834 sw->setUpdateCallback(new UpdateCallback(getConfig()));
1835 parent.addChild(sw);
1840 ////////////////////////////////////////////////////////////////////////
1841 // dynamically switch on/off shadows
1842 ////////////////////////////////////////////////////////////////////////
1844 class SGShadowAnimation::UpdateCallback : public osg::NodeCallback {
1846 UpdateCallback(const SGCondition* condition) :
1847 _condition(condition)
1849 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1851 if (_condition->test())
1852 node->setNodeMask( SG_NODEMASK_CASTSHADOW_BIT | node->getNodeMask());
1854 node->setNodeMask(~SG_NODEMASK_CASTSHADOW_BIT & node->getNodeMask());
1859 SGSharedPtr<const SGCondition> _condition;
1862 SGShadowAnimation::SGShadowAnimation(const SGPropertyNode* configNode,
1863 SGPropertyNode* modelRoot) :
1864 SGAnimation(configNode, modelRoot)
1869 SGShadowAnimation::createAnimationGroup(osg::Group& parent)
1871 SGSharedPtr<SGCondition const> condition = getCondition();
1875 osg::Group* group = new osg::Group;
1876 group->setName("shadow animation");
1877 group->setUpdateCallback(new UpdateCallback(condition));
1878 parent.addChild(group);
1883 ////////////////////////////////////////////////////////////////////////
1884 // Implementation of SGTexTransformAnimation
1885 ////////////////////////////////////////////////////////////////////////
1887 class SGTexTransformAnimation::Transform : public SGReferenced {
1892 virtual ~Transform()
1894 void setValue(double value)
1896 virtual void transform(osg::Matrix&) = 0;
1901 class SGTexTransformAnimation::Translation :
1902 public SGTexTransformAnimation::Transform {
1904 Translation(const SGVec3d& axis) :
1907 void setValue(double value)
1909 virtual void transform(osg::Matrix& matrix)
1912 set_translation(tmp, _value, _axis);
1913 matrix.preMult(tmp);
1919 class SGTexTransformAnimation::Rotation :
1920 public SGTexTransformAnimation::Transform {
1922 Rotation(const SGVec3d& axis, const SGVec3d& center) :
1926 virtual void transform(osg::Matrix& matrix)
1929 set_rotation(tmp, _value, _center, _axis);
1930 matrix.preMult(tmp);
1937 class SGTexTransformAnimation::UpdateCallback :
1938 public osg::StateAttribute::Callback {
1940 UpdateCallback(const SGCondition* condition) :
1941 _condition(condition)
1943 virtual void operator () (osg::StateAttribute* sa, osg::NodeVisitor*)
1945 if (!_condition || _condition->test()) {
1946 TransformList::const_iterator i;
1947 for (i = _transforms.begin(); i != _transforms.end(); ++i)
1948 i->transform->setValue(i->value->getValue());
1950 assert(dynamic_cast<osg::TexMat*>(sa));
1951 osg::TexMat* texMat = static_cast<osg::TexMat*>(sa);
1952 texMat->getMatrix().makeIdentity();
1953 TransformList::const_iterator i;
1954 for (i = _transforms.begin(); i != _transforms.end(); ++i)
1955 i->transform->transform(texMat->getMatrix());
1957 void appendTransform(Transform* transform, SGDoubleValue* value)
1959 Entry entry = { transform, value };
1960 transform->transform(_matrix);
1961 _transforms.push_back(entry);
1966 SGSharedPtr<Transform> transform;
1967 SGSharedPtr<const SGDoubleValue> value;
1969 typedef std::vector<Entry> TransformList;
1970 TransformList _transforms;
1971 SGSharedPtr<const SGCondition> _condition;
1972 osg::Matrix _matrix;
1975 SGTexTransformAnimation::SGTexTransformAnimation(const SGPropertyNode* configNode,
1976 SGPropertyNode* modelRoot) :
1977 SGAnimation(configNode, modelRoot)
1982 SGTexTransformAnimation::createAnimationGroup(osg::Group& parent)
1984 osg::Group* group = new osg::Group;
1985 group->setName("texture transform group");
1986 osg::StateSet* stateSet = group->getOrCreateStateSet();
1987 osg::TexMat* texMat = new osg::TexMat;
1988 UpdateCallback* updateCallback = new UpdateCallback(getCondition());
1989 // interpret the configs ...
1990 std::string type = getType();
1992 if (type == "textranslate") {
1993 appendTexTranslate(getConfig(), updateCallback);
1994 } else if (type == "texrotate") {
1995 appendTexRotate(getConfig(), updateCallback);
1996 } else if (type == "texmultiple") {
1997 std::vector<SGSharedPtr<SGPropertyNode> > transformConfigs;
1998 transformConfigs = getConfig()->getChildren("transform");
1999 for (unsigned i = 0; i < transformConfigs.size(); ++i) {
2000 std::string subtype = transformConfigs[i]->getStringValue("subtype", "");
2001 if (subtype == "textranslate")
2002 appendTexTranslate(transformConfigs[i], updateCallback);
2003 else if (subtype == "texrotate")
2004 appendTexRotate(transformConfigs[i], updateCallback);
2006 SG_LOG(SG_INPUT, SG_ALERT,
2007 "Ignoring unknown texture transform subtype");
2010 SG_LOG(SG_INPUT, SG_ALERT, "Ignoring unknown texture transform type");
2013 texMat->setUpdateCallback(updateCallback);
2014 stateSet->setTextureAttribute(0, texMat);
2015 parent.addChild(group);
2020 SGTexTransformAnimation::appendTexTranslate(const SGPropertyNode* config,
2021 UpdateCallback* updateCallback)
2023 std::string propertyName = config->getStringValue("property", "/null");
2024 SGPropertyNode* inputNode;
2025 inputNode = getModelRoot()->getNode(propertyName.c_str(), true);
2027 SGDoubleValue* animationValue;
2028 SGInterpTable* table = read_interpolation_table(config);
2030 SGTexTableValue* value;
2031 value = new SGTexTableValue(inputNode, table);
2032 value->setStep(config->getDoubleValue("step", 0));
2033 value->setScroll(config->getDoubleValue("scroll", 0));
2034 value->setBias(config->getDoubleValue("bias", 0));
2035 animationValue = value;
2037 SGTexScaleOffsetValue* value;
2038 value = new SGTexScaleOffsetValue(inputNode);
2039 value->setScale(config->getDoubleValue("factor", 1));
2040 value->setOffset(config->getDoubleValue("offset", 0));
2041 value->setStep(config->getDoubleValue("step", 0));
2042 value->setScroll(config->getDoubleValue("scroll", 0));
2043 value->setBias(config->getDoubleValue("bias", 0));
2044 value->setMin(config->getDoubleValue("min", -SGLimitsd::max()));
2045 value->setMax(config->getDoubleValue("max", SGLimitsd::max()));
2046 animationValue = value;
2048 SGVec3d axis(config->getDoubleValue("axis/x", 0),
2049 config->getDoubleValue("axis/y", 0),
2050 config->getDoubleValue("axis/z", 0));
2051 Translation* translation;
2052 translation = new Translation(normalize(axis));
2053 translation->setValue(config->getDoubleValue("starting-position", 0));
2054 updateCallback->appendTransform(translation, animationValue);
2058 SGTexTransformAnimation::appendTexRotate(const SGPropertyNode* config,
2059 UpdateCallback* updateCallback)
2061 std::string propertyName = config->getStringValue("property", "/null");
2062 SGPropertyNode* inputNode;
2063 inputNode = getModelRoot()->getNode(propertyName.c_str(), true);
2065 SGDoubleValue* animationValue;
2066 SGInterpTable* table = read_interpolation_table(config);
2068 SGTexTableValue* value;
2069 value = new SGTexTableValue(inputNode, table);
2070 value->setStep(config->getDoubleValue("step", 0));
2071 value->setScroll(config->getDoubleValue("scroll", 0));
2072 value->setBias(config->getDoubleValue("bias", 0));
2073 animationValue = value;
2075 SGTexScaleOffsetValue* value;
2076 value = new SGTexScaleOffsetValue(inputNode);
2077 value->setScale(config->getDoubleValue("factor", 1));
2078 value->setOffset(config->getDoubleValue("offset-deg", 0));
2079 value->setStep(config->getDoubleValue("step", 0));
2080 value->setScroll(config->getDoubleValue("scroll", 0));
2081 value->setBias(config->getDoubleValue("bias", 0));
2082 value->setMin(config->getDoubleValue("min-deg", -SGLimitsd::max()));
2083 value->setMax(config->getDoubleValue("max-deg", SGLimitsd::max()));
2084 animationValue = value;
2086 SGVec3d axis(config->getDoubleValue("axis/x", 0),
2087 config->getDoubleValue("axis/y", 0),
2088 config->getDoubleValue("axis/z", 0));
2089 SGVec3d center(config->getDoubleValue("center/x", 0),
2090 config->getDoubleValue("center/y", 0),
2091 config->getDoubleValue("center/z", 0));
2093 rotation = new Rotation(normalize(axis), center);
2094 rotation->setValue(config->getDoubleValue("starting-position-deg", 0));
2095 updateCallback->appendTransform(rotation, animationValue);
2099 ////////////////////////////////////////////////////////////////////////
2100 // Implementation of SGPickAnimation
2101 ////////////////////////////////////////////////////////////////////////
2103 class SGPickAnimation::PickCallback : public SGPickCallback {
2105 PickCallback(const SGPropertyNode* configNode,
2106 SGPropertyNode* modelRoot) :
2107 _button(configNode->getIntValue("button", -1)),
2108 _repeatable(configNode->getBoolValue("repeatable", false)),
2109 _repeatInterval(configNode->getDoubleValue("interval-sec", 0.1))
2111 SG_LOG(SG_INPUT, SG_DEBUG, "Reading all bindings");
2112 std::vector<SGPropertyNode_ptr> bindings;
2113 bindings = configNode->getChildren("binding");
2114 for (unsigned int i = 0; i < bindings.size(); ++i) {
2115 _bindingsDown.push_back(new SGBinding(bindings[i], modelRoot));
2118 const SGPropertyNode* upNode = configNode->getChild("mod-up");
2121 bindings = upNode->getChildren("binding");
2122 for (unsigned int i = 0; i < bindings.size(); ++i) {
2123 _bindingsUp.push_back(new SGBinding(bindings[i], modelRoot));
2126 virtual bool buttonPressed(int button, const Info&)
2128 if (0 <= _button && button != _button)
2130 SGBindingList::const_iterator i;
2131 for (i = _bindingsDown.begin(); i != _bindingsDown.end(); ++i)
2136 virtual void buttonReleased(void)
2138 SGBindingList::const_iterator i;
2139 for (i = _bindingsUp.begin(); i != _bindingsUp.end(); ++i)
2142 virtual void update(double dt)
2148 while (_repeatInterval < _repeatTime) {
2149 _repeatTime -= _repeatInterval;
2150 SGBindingList::const_iterator i;
2151 for (i = _bindingsDown.begin(); i != _bindingsDown.end(); ++i)
2156 SGBindingList _bindingsDown;
2157 SGBindingList _bindingsUp;
2160 double _repeatInterval;
2164 SGPickAnimation::SGPickAnimation(const SGPropertyNode* configNode,
2165 SGPropertyNode* modelRoot) :
2166 SGAnimation(configNode, modelRoot)
2171 SGPickAnimation::createAnimationGroup(osg::Group& parent)
2173 osg::Group* commonGroup = new osg::Group;
2175 // Contains the normal geometry that is interactive
2176 osg::ref_ptr<osg::Group> normalGroup = new osg::Group;
2177 normalGroup->addChild(commonGroup);
2179 // Used to render the geometry with just yellow edges
2180 osg::Group* highlightGroup = new osg::Group;
2181 highlightGroup->setNodeMask(SG_NODEMASK_PICK_BIT);
2182 highlightGroup->addChild(commonGroup);
2183 SGSceneUserData* ud;
2184 ud = SGSceneUserData::getOrCreateSceneUserData(highlightGroup);
2185 std::vector<SGPropertyNode_ptr> actions;
2186 actions = getConfig()->getChildren("action");
2187 for (unsigned int i = 0; i < actions.size(); ++i)
2188 ud->addPickCallback(new PickCallback(actions[i], getModelRoot()));
2190 // prepare a state set that paints the edges of this object yellow
2191 osg::StateSet* stateSet = highlightGroup->getOrCreateStateSet();
2192 stateSet->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE);
2194 osg::PolygonOffset* polygonOffset = new osg::PolygonOffset;
2195 polygonOffset->setFactor(-1);
2196 polygonOffset->setUnits(-1);
2197 stateSet->setAttribute(polygonOffset, osg::StateAttribute::OVERRIDE);
2198 stateSet->setMode(GL_POLYGON_OFFSET_LINE, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
2200 osg::PolygonMode* polygonMode = new osg::PolygonMode;
2201 polygonMode->setMode(osg::PolygonMode::FRONT_AND_BACK,
2202 osg::PolygonMode::LINE);
2203 stateSet->setAttribute(polygonMode, osg::StateAttribute::OVERRIDE);
2205 osg::Material* material = new osg::Material;
2206 material->setColorMode(osg::Material::OFF);
2207 material->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(1, 1, 0, 1));
2208 material->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(1, 1, 0, 1));
2209 material->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(1, 1, 0, 1));
2210 material->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0, 0, 0, 0));
2211 stateSet->setAttribute(material, osg::StateAttribute::OVERRIDE);
2213 // Only add normal geometry if configured
2214 if (getConfig()->getBoolValue("visible", true))
2215 parent.addChild(normalGroup.get());
2216 parent.addChild(highlightGroup);