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)
113 double scrollval = 0.0;
115 // calculate scroll amount (for odometer like movement)
116 double remainder = step - fmod(fabs(property), step);
117 if (remainder < scroll) {
118 scrollval = (scroll - remainder) / scroll * step;
121 // apply stepping of input value
123 modprop = ((floor(property/step) * step) + scrollval);
125 modprop = ((ceil(property/step) * step) + scrollval);
134 * Read an interpolation table from properties.
136 static SGInterpTable *
137 read_interpolation_table(const SGPropertyNode* props)
139 const SGPropertyNode* table_node = props->getNode("interpolation");
142 return new SGInterpTable(table_node);
145 ////////////////////////////////////////////////////////////////////////
146 // Utility value classes
147 ////////////////////////////////////////////////////////////////////////
148 class SGScaleOffsetValue : public SGDoubleValue {
150 SGScaleOffsetValue(SGPropertyNode const* propertyNode) :
151 _propertyNode(propertyNode),
154 _min(-SGLimitsd::max()),
155 _max(SGLimitsd::max())
157 void setScale(double scale)
159 void setOffset(double offset)
160 { _offset = offset; }
161 void setMin(double min)
163 void setMax(double max)
166 virtual double getValue() const
168 double value = _propertyNode ? _propertyNode->getDoubleValue() : 0;
169 return std::min(_max, std::max(_min, _offset + _scale*value));
172 SGSharedPtr<SGPropertyNode const> _propertyNode;
179 class SGPersScaleOffsetValue : public SGDoubleValue {
181 SGPersScaleOffsetValue(SGPropertyNode const* propertyNode,
182 SGPropertyNode const* config,
183 const char* scalename, const char* offsetname,
184 double defScale = 1, double defOffset = 0) :
185 _propertyNode(propertyNode),
186 _scale(config, scalename, defScale),
187 _offset(config, offsetname, defOffset),
188 _min(-SGLimitsd::max()),
189 _max(SGLimitsd::max())
191 void setScale(double scale)
193 void setOffset(double offset)
194 { _offset = offset; }
195 void setMin(double min)
197 void setMax(double max)
200 virtual double getValue() const
204 double value = _propertyNode ? _propertyNode->getDoubleValue() : 0;
205 return SGMiscd::clip(_offset + _scale*value, _min, _max);
208 SGSharedPtr<SGPropertyNode const> _propertyNode;
209 mutable SGPersonalityParameter<double> _scale;
210 mutable SGPersonalityParameter<double> _offset;
215 class SGInterpTableValue : public SGDoubleValue {
217 SGInterpTableValue(SGPropertyNode const* propertyNode,
218 SGInterpTable const* interpTable) :
219 _propertyNode(propertyNode),
220 _interpTable(interpTable)
222 virtual double getValue() const
223 { return _interpTable->interpolate(_propertyNode ? _propertyNode->getDoubleValue() : 0); }
225 SGSharedPtr<SGPropertyNode const> _propertyNode;
226 SGSharedPtr<SGInterpTable const> _interpTable;
229 class SGTexScaleOffsetValue : public SGDoubleValue {
231 SGTexScaleOffsetValue(const SGPropertyNode* propertyNode) :
232 _propertyNode(propertyNode),
237 _min(-SGLimitsd::max()),
238 _max(SGLimitsd::max())
240 void setScale(double scale)
242 void setOffset(double offset)
243 { _offset = offset; }
244 void setStep(double step)
246 void setScroll(double scroll)
247 { _scroll = scroll; }
248 void setMin(double min)
250 void setMax(double max)
253 virtual double getValue() const
255 double value = _propertyNode ? _propertyNode->getDoubleValue() : 0;
256 value = apply_mods(value, _step, _scroll);
257 return SGMiscd::clip(_scale*(_offset + value), _min, _max);
260 SGSharedPtr<const SGPropertyNode> _propertyNode;
269 class SGTexTableValue : public SGDoubleValue {
271 SGTexTableValue(const SGPropertyNode* propertyNode,
272 const SGInterpTable* interpTable) :
273 _propertyNode(propertyNode),
274 _interpTable(interpTable)
276 void setStep(double step)
278 void setScroll(double scroll)
279 { _scroll = scroll; }
280 virtual double getValue() const
282 double value = _propertyNode ? _propertyNode->getDoubleValue() : 0;
283 value = apply_mods(value, _step, _scroll);
284 return _interpTable->interpolate(value);
287 SGSharedPtr<SGPropertyNode const> _propertyNode;
288 SGSharedPtr<SGInterpTable const> _interpTable;
294 unit_string(const char* value, const char* unit)
296 return std::string(value) + unit;
299 static SGDoubleValue*
300 read_value(const SGPropertyNode* configNode, SGPropertyNode* modelRoot,
301 const char* unit, double defMin, double defMax)
303 std::string inputPropertyName;
304 inputPropertyName = configNode->getStringValue("property", "");
305 if (!inputPropertyName.empty()) {
306 SGPropertyNode* inputProperty;
307 inputProperty = modelRoot->getNode(inputPropertyName.c_str(), true);
308 SGInterpTable* interpTable = read_interpolation_table(configNode);
310 SGInterpTableValue* value;
311 value = new SGInterpTableValue(inputProperty, interpTable);
314 std::string offset = unit_string("offset", unit);
315 std::string min = unit_string("min", unit);
316 std::string max = unit_string("max", unit);
318 if (configNode->getBoolValue("use-personality", false)) {
319 SGPersScaleOffsetValue* value;
320 value = new SGPersScaleOffsetValue(inputProperty, configNode,
321 "factor", offset.c_str());
322 value->setMin(configNode->getDoubleValue(min.c_str(), defMin));
323 value->setMax(configNode->getDoubleValue(max.c_str(), defMax));
326 SGScaleOffsetValue* value = new SGScaleOffsetValue(inputProperty);
327 value->setScale(configNode->getDoubleValue("factor", 1));
328 value->setOffset(configNode->getDoubleValue(offset.c_str(), 0));
329 value->setMin(configNode->getDoubleValue(min.c_str(), defMin));
330 value->setMax(configNode->getDoubleValue(max.c_str(), defMax));
339 ////////////////////////////////////////////////////////////////////////
340 // Animation installer
341 ////////////////////////////////////////////////////////////////////////
343 class SGAnimation::RemoveModeVisitor : public SGStateAttributeVisitor {
345 RemoveModeVisitor(osg::StateAttribute::GLMode mode) :
348 virtual void apply(osg::StateSet* stateSet)
352 stateSet->removeMode(_mode);
355 osg::StateAttribute::GLMode _mode;
358 class SGAnimation::RemoveAttributeVisitor : public SGStateAttributeVisitor {
360 RemoveAttributeVisitor(osg::StateAttribute::Type type) :
363 virtual void apply(osg::StateSet* stateSet)
367 while (stateSet->getAttribute(_type)) {
368 stateSet->removeAttribute(_type);
372 osg::StateAttribute::Type _type;
375 class SGAnimation::RemoveTextureModeVisitor : public SGStateAttributeVisitor {
377 RemoveTextureModeVisitor(unsigned unit, osg::StateAttribute::GLMode mode) :
381 virtual void apply(osg::StateSet* stateSet)
385 stateSet->removeTextureMode(_unit, _mode);
389 osg::StateAttribute::GLMode _mode;
392 class SGAnimation::RemoveTextureAttributeVisitor :
393 public SGStateAttributeVisitor {
395 RemoveTextureAttributeVisitor(unsigned unit,
396 osg::StateAttribute::Type type) :
400 virtual void apply(osg::StateSet* stateSet)
404 while (stateSet->getTextureAttribute(_unit, _type)) {
405 stateSet->removeTextureAttribute(_unit, _type);
410 osg::StateAttribute::Type _type;
413 class SGAnimation::BinToInheritVisitor : public SGStateAttributeVisitor {
415 virtual void apply(osg::StateSet* stateSet)
419 stateSet->setRenderBinToInherit();
423 class SGAnimation::DrawableCloneVisitor : public osg::NodeVisitor {
425 DrawableCloneVisitor() :
426 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
428 void apply(osg::Geode& geode)
430 for (unsigned i = 0 ; i < geode.getNumDrawables(); ++i) {
431 osg::CopyOp copyOp(osg::CopyOp::DEEP_COPY_ALL &
432 ~osg::CopyOp::DEEP_COPY_TEXTURES);
433 geode.setDrawable(i, copyOp(geode.getDrawable(i)));
439 SGAnimation::SGAnimation(const SGPropertyNode* configNode,
440 SGPropertyNode* modelRoot) :
441 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
443 _configNode(configNode),
444 _modelRoot(modelRoot)
446 _name = configNode->getStringValue("name", "");
447 _enableHOT = configNode->getBoolValue("enable-hot", true);
448 _disableShadow = configNode->getBoolValue("disable-shadow", false);
449 std::vector<SGPropertyNode_ptr> objectNames =
450 configNode->getChildren("object-name");
451 for (unsigned i = 0; i < objectNames.size(); ++i)
452 _objectNames.push_back(objectNames[i]->getStringValue());
455 SGAnimation::~SGAnimation()
460 SG_LOG(SG_IO, SG_ALERT, "Could not find at least one of the following"
461 " objects for animation:\n");
462 std::list<std::string>::const_iterator i;
463 for (i = _objectNames.begin(); i != _objectNames.end(); ++i)
464 SG_LOG(SG_IO, SG_ALERT, *i << "\n");
468 SGAnimation::animate(osg::Node* node, const SGPropertyNode* configNode,
469 SGPropertyNode* modelRoot)
471 std::string type = configNode->getStringValue("type", "none");
472 if (type == "alpha-test") {
473 SGAlphaTestAnimation animInst(configNode, modelRoot);
474 animInst.apply(node);
475 } else if (type == "billboard") {
476 SGBillboardAnimation animInst(configNode, modelRoot);
477 animInst.apply(node);
478 } else if (type == "blend") {
479 SGBlendAnimation animInst(configNode, modelRoot);
480 animInst.apply(node);
481 } else if (type == "dist-scale") {
482 SGDistScaleAnimation animInst(configNode, modelRoot);
483 animInst.apply(node);
484 } else if (type == "flash") {
485 SGFlashAnimation animInst(configNode, modelRoot);
486 animInst.apply(node);
487 } else if (type == "material") {
488 SGMaterialAnimation animInst(configNode, modelRoot);
489 animInst.apply(node);
490 } else if (type == "noshadow") {
491 SGShadowAnimation animInst(configNode, modelRoot);
492 animInst.apply(node);
493 } else if (type == "pick") {
494 SGPickAnimation animInst(configNode, modelRoot);
495 animInst.apply(node);
496 } else if (type == "range") {
497 SGRangeAnimation animInst(configNode, modelRoot);
498 animInst.apply(node);
499 } else if (type == "rotate" || type == "spin") {
500 SGRotateAnimation animInst(configNode, modelRoot);
501 animInst.apply(node);
502 } else if (type == "scale") {
503 SGScaleAnimation animInst(configNode, modelRoot);
504 animInst.apply(node);
505 } else if (type == "select") {
506 SGSelectAnimation animInst(configNode, modelRoot);
507 animInst.apply(node);
508 } else if (type == "shader") {
509 SGShaderAnimation animInst(configNode, modelRoot);
510 animInst.apply(node);
511 } else if (type == "textranslate" || type == "texrotate" ||
512 type == "texmultiple") {
513 SGTexTransformAnimation animInst(configNode, modelRoot);
514 animInst.apply(node);
515 } else if (type == "timed") {
516 SGTimedAnimation animInst(configNode, modelRoot);
517 animInst.apply(node);
518 } else if (type == "translate") {
519 SGTranslateAnimation animInst(configNode, modelRoot);
520 animInst.apply(node);
521 } else if (type == "null" || type == "none" || type.empty()) {
522 SGGroupAnimation animInst(configNode, modelRoot);
523 animInst.apply(node);
532 SGAnimation::apply(osg::Node* node)
534 // duh what a special case ...
535 if (_objectNames.empty()) {
536 osg::Group* group = node->asGroup();
538 osg::ref_ptr<osg::Group> animationGroup;
539 installInGroup(std::string(), *group, animationGroup);
546 SGAnimation::install(osg::Node& node)
550 node.setNodeMask( SG_NODEMASK_TERRAIN_BIT | node.getNodeMask());
552 node.setNodeMask(~SG_NODEMASK_TERRAIN_BIT & node.getNodeMask());
554 node.setNodeMask( SG_NODEMASK_CASTSHADOW_BIT | node.getNodeMask());
556 node.setNodeMask(~SG_NODEMASK_CASTSHADOW_BIT & node.getNodeMask());
560 SGAnimation::createAnimationGroup(osg::Group& parent)
562 // default implementation, we do not need a new group
563 // for every animation type. Usually animations that just change
564 // the StateSet of some parts of the model
569 SGAnimation::apply(osg::Group& group)
571 // the trick is to first traverse the children and then
572 // possibly splice in a new group node if required.
573 // Else we end up in a recursive loop where we infinitly insert new
577 // Note that this algorithm preserves the order of the child objects
578 // like they appear in the object-name tags.
579 // The timed animations require this
580 osg::ref_ptr<osg::Group> animationGroup;
581 std::list<std::string>::const_iterator nameIt;
582 for (nameIt = _objectNames.begin(); nameIt != _objectNames.end(); ++nameIt)
583 installInGroup(*nameIt, group, animationGroup);
587 SGAnimation::installInGroup(const std::string& name, osg::Group& group,
588 osg::ref_ptr<osg::Group>& animationGroup)
590 int i = group.getNumChildren() - 1;
591 for (; 0 <= i; --i) {
592 osg::Node* child = group.getChild(i);
594 // Check if this one is already processed
595 if (std::find(_installedAnimations.begin(),
596 _installedAnimations.end(), child)
597 != _installedAnimations.end())
600 if (name.empty() || child->getName() == name) {
601 // fire the installation of the animation
604 // create a group node on demand
605 if (!animationGroup.valid()) {
606 animationGroup = createAnimationGroup(group);
607 // Animation type that does not require a new group,
608 // in this case we can stop and look for the next object
609 if (animationGroup.valid() && !_name.empty())
610 animationGroup->setName(_name);
612 if (animationGroup.valid()) {
613 animationGroup->addChild(child);
614 group.removeChild(i);
617 // store that we already have processed this child node
618 // We can hit this one twice if an animation references some
619 // part of a subtree twice
620 _installedAnimations.push_back(child);
626 SGAnimation::removeMode(osg::Node& node, osg::StateAttribute::GLMode mode)
628 RemoveModeVisitor visitor(mode);
629 node.accept(visitor);
633 SGAnimation::removeAttribute(osg::Node& node, osg::StateAttribute::Type type)
635 RemoveAttributeVisitor visitor(type);
636 node.accept(visitor);
640 SGAnimation::removeTextureMode(osg::Node& node, unsigned unit,
641 osg::StateAttribute::GLMode mode)
643 RemoveTextureModeVisitor visitor(unit, mode);
644 node.accept(visitor);
648 SGAnimation::removeTextureAttribute(osg::Node& node, unsigned unit,
649 osg::StateAttribute::Type type)
651 RemoveTextureAttributeVisitor visitor(unit, type);
652 node.accept(visitor);
656 SGAnimation::setRenderBinToInherit(osg::Node& node)
658 BinToInheritVisitor visitor;
659 node.accept(visitor);
663 SGAnimation::cloneDrawables(osg::Node& node)
665 DrawableCloneVisitor visitor;
666 node.accept(visitor);
670 SGAnimation::getCondition() const
672 const SGPropertyNode* conditionNode = _configNode->getChild("condition");
675 return sgReadCondition(_modelRoot, conditionNode);
680 ////////////////////////////////////////////////////////////////////////
681 // Implementation of null animation
682 ////////////////////////////////////////////////////////////////////////
684 // Ok, that is to build a subgraph from different other
685 // graph nodes. I guess that this stems from the time where modellers
686 // could not build hierarchical trees ...
687 SGGroupAnimation::SGGroupAnimation(const SGPropertyNode* configNode,
688 SGPropertyNode* modelRoot):
689 SGAnimation(configNode, modelRoot)
694 SGGroupAnimation::createAnimationGroup(osg::Group& parent)
696 osg::Group* group = new osg::Group;
697 parent.addChild(group);
702 ////////////////////////////////////////////////////////////////////////
703 // Implementation of translate animation
704 ////////////////////////////////////////////////////////////////////////
706 class SGTranslateAnimation::Transform : public osg::Transform {
711 { setReferenceFrame(RELATIVE_RF); }
712 void setAxis(const SGVec3d& axis)
713 { _axis = axis; dirtyBound(); }
714 void setValue(double value)
715 { _value = value; dirtyBound(); }
716 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
717 osg::NodeVisitor* nv) const
719 assert(_referenceFrame == RELATIVE_RF);
721 set_translation(tmp, _value, _axis);
725 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
726 osg::NodeVisitor* nv) const
728 assert(_referenceFrame == RELATIVE_RF);
730 set_translation(tmp, -_value, _axis);
731 matrix.postMult(tmp);
739 class SGTranslateAnimation::UpdateCallback : public osg::NodeCallback {
741 UpdateCallback(SGCondition const* condition,
742 SGDoubleValue const* animationValue) :
743 _condition(condition),
744 _animationValue(animationValue)
746 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
748 if (!_condition || _condition->test()) {
749 SGTranslateAnimation::Transform* transform;
750 transform = static_cast<SGTranslateAnimation::Transform*>(node);
751 transform->setValue(_animationValue->getValue());
756 SGSharedPtr<SGCondition const> _condition;
757 SGSharedPtr<SGDoubleValue const> _animationValue;
760 SGTranslateAnimation::SGTranslateAnimation(const SGPropertyNode* configNode,
761 SGPropertyNode* modelRoot) :
762 SGAnimation(configNode, modelRoot)
764 _condition = getCondition();
765 _animationValue = read_value(configNode, modelRoot, "-m",
766 -SGLimitsd::max(), SGLimitsd::max());
767 _axis[0] = configNode->getDoubleValue("axis/x", 0);
768 _axis[1] = configNode->getDoubleValue("axis/y", 0);
769 _axis[2] = configNode->getDoubleValue("axis/z", 0);
770 if (8*SGLimitsd::min() < norm(_axis))
771 _axis = normalize(_axis);
773 _initialValue = configNode->getDoubleValue("starting-position-m", 0);
774 _initialValue *= configNode->getDoubleValue("factor", 1);
775 _initialValue += configNode->getDoubleValue("offset-m", 0);
779 SGTranslateAnimation::createAnimationGroup(osg::Group& parent)
781 Transform* transform = new Transform;
782 transform->setName("translate animation");
783 if (_animationValue) {
784 UpdateCallback* uc = new UpdateCallback(_condition, _animationValue);
785 transform->setUpdateCallback(uc);
787 transform->setAxis(_axis);
788 transform->setValue(_initialValue);
789 parent.addChild(transform);
794 ////////////////////////////////////////////////////////////////////////
795 // Implementation of rotate/spin animation
796 ////////////////////////////////////////////////////////////////////////
798 class SGRotateAnimation::Transform : public osg::Transform {
801 { setReferenceFrame(RELATIVE_RF); }
802 void setCenter(const SGVec3d& center)
803 { _center = center; dirtyBound(); }
804 void setAxis(const SGVec3d& axis)
805 { _axis = axis; dirtyBound(); }
806 void setAngle(double angle)
808 double getAngle() const
810 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
811 osg::NodeVisitor* nv) const
813 // This is the fast path, optimize a bit
814 assert(_referenceFrame == RELATIVE_RF);
817 set_rotation(tmp, _angle, _center, _axis);
821 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
822 osg::NodeVisitor* nv) const
824 assert(_referenceFrame == RELATIVE_RF);
827 set_rotation(tmp, -_angle, _center, _axis);
828 matrix.postMult(tmp);
831 virtual osg::BoundingSphere computeBound() const
833 osg::BoundingSphere bs = osg::Group::computeBound();
834 osg::BoundingSphere centerbs(_center.osg(), bs.radius());
835 centerbs.expandBy(bs);
845 class SGRotateAnimation::UpdateCallback : public osg::NodeCallback {
847 UpdateCallback(SGCondition const* condition,
848 SGDoubleValue const* animationValue) :
849 _condition(condition),
850 _animationValue(animationValue)
852 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
854 if (!_condition || _condition->test()) {
855 SGRotateAnimation::Transform* transform;
856 transform = static_cast<SGRotateAnimation::Transform*>(node);
857 transform->setAngle(_animationValue->getValue());
862 SGSharedPtr<SGCondition const> _condition;
863 SGSharedPtr<SGDoubleValue const> _animationValue;
866 class SGRotateAnimation::SpinUpdateCallback : public osg::NodeCallback {
868 SpinUpdateCallback(SGCondition const* condition,
869 SGDoubleValue const* animationValue) :
870 _condition(condition),
871 _animationValue(animationValue),
874 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
876 if (!_condition || _condition->test()) {
877 SGRotateAnimation::Transform* transform;
878 transform = static_cast<SGRotateAnimation::Transform*>(node);
880 double t = nv->getFrameStamp()->getReferenceTime();
885 double velocity_rpms = _animationValue->getValue()/60;
886 double angle = transform->getAngle();
887 angle += dt*velocity_rpms*360;
888 angle -= 360*floor(angle/360);
889 transform->setAngle(angle);
894 SGSharedPtr<SGCondition const> _condition;
895 SGSharedPtr<SGDoubleValue const> _animationValue;
899 SGRotateAnimation::SGRotateAnimation(const SGPropertyNode* configNode, SGPropertyNode* modelRoot) :
900 SGAnimation(configNode, modelRoot)
902 std::string type = configNode->getStringValue("type", "");
903 _isSpin = (type == "spin");
905 _condition = getCondition();
906 _animationValue = read_value(configNode, modelRoot, "-deg",
907 -SGLimitsd::max(), SGLimitsd::max());
908 _initialValue = configNode->getDoubleValue("starting-position-deg", 0);
909 _initialValue *= configNode->getDoubleValue("factor", 1);
910 _initialValue += configNode->getDoubleValue("offset-deg", 0);
912 _center = SGVec3d::zeros();
913 if (configNode->hasValue("axis/x1-m")) {
915 v1[0] = configNode->getDoubleValue("axis/x1-m", 0);
916 v1[1] = configNode->getDoubleValue("axis/y1-m", 0);
917 v1[2] = configNode->getDoubleValue("axis/z1-m", 0);
918 v2[0] = configNode->getDoubleValue("axis/x2-m", 0);
919 v2[1] = configNode->getDoubleValue("axis/y2-m", 0);
920 v2[2] = configNode->getDoubleValue("axis/z2-m", 0);
921 _center = 0.5*(v1+v2);
924 _axis[0] = configNode->getDoubleValue("axis/x", 0);
925 _axis[1] = configNode->getDoubleValue("axis/y", 0);
926 _axis[2] = configNode->getDoubleValue("axis/z", 0);
928 if (8*SGLimitsd::min() < norm(_axis))
929 _axis = normalize(_axis);
931 _center[0] = configNode->getDoubleValue("center/x-m", _center[0]);
932 _center[1] = configNode->getDoubleValue("center/y-m", _center[1]);
933 _center[2] = configNode->getDoubleValue("center/z-m", _center[2]);
937 SGRotateAnimation::createAnimationGroup(osg::Group& parent)
939 Transform* transform = new Transform;
940 transform->setName("rotate animation");
942 SpinUpdateCallback* uc;
943 uc = new SpinUpdateCallback(_condition, _animationValue);
944 transform->setUpdateCallback(uc);
945 } else if (_animationValue) {
946 UpdateCallback* uc = new UpdateCallback(_condition, _animationValue);
947 transform->setUpdateCallback(uc);
949 transform->setCenter(_center);
950 transform->setAxis(_axis);
951 transform->setAngle(_initialValue);
952 parent.addChild(transform);
957 ////////////////////////////////////////////////////////////////////////
958 // Implementation of scale animation
959 ////////////////////////////////////////////////////////////////////////
961 class SGScaleAnimation::Transform : public osg::Transform {
965 _scaleFactor(1, 1, 1),
968 setReferenceFrame(RELATIVE_RF);
970 void setCenter(const SGVec3d& center)
975 void setScaleFactor(const SGVec3d& scaleFactor)
977 if (_boundScale < normI(scaleFactor))
979 _scaleFactor = scaleFactor;
981 void setScaleFactor(double scaleFactor)
983 if (_boundScale < fabs(scaleFactor))
985 _scaleFactor = SGVec3d(scaleFactor, scaleFactor, scaleFactor);
987 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
988 osg::NodeVisitor* nv) const
990 assert(_referenceFrame == RELATIVE_RF);
991 osg::Matrix transform;
992 transform(0,0) = _scaleFactor[0];
993 transform(1,1) = _scaleFactor[1];
994 transform(2,2) = _scaleFactor[2];
995 transform(3,0) = _center[0]*(1 - _scaleFactor[0]);
996 transform(3,1) = _center[1]*(1 - _scaleFactor[1]);
997 transform(3,2) = _center[2]*(1 - _scaleFactor[2]);
998 matrix.preMult(transform);
1001 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
1002 osg::NodeVisitor* nv) const
1004 assert(_referenceFrame == RELATIVE_RF);
1005 if (fabs(_scaleFactor[0]) < SGLimitsd::min())
1007 if (fabs(_scaleFactor[1]) < SGLimitsd::min())
1009 if (fabs(_scaleFactor[2]) < SGLimitsd::min())
1011 SGVec3d rScaleFactor(1/_scaleFactor[0],
1014 osg::Matrix transform;
1015 transform(0,0) = rScaleFactor[0];
1016 transform(1,1) = rScaleFactor[1];
1017 transform(2,2) = rScaleFactor[2];
1018 transform(3,0) = _center[0]*(1 - rScaleFactor[0]);
1019 transform(3,1) = _center[1]*(1 - rScaleFactor[1]);
1020 transform(3,2) = _center[2]*(1 - rScaleFactor[2]);
1021 matrix.postMult(transform);
1024 virtual osg::BoundingSphere computeBound() const
1026 osg::BoundingSphere bs = osg::Group::computeBound();
1027 _boundScale = normI(_scaleFactor);
1028 bs.radius() *= _boundScale;
1034 SGVec3d _scaleFactor;
1035 mutable double _boundScale;
1038 class SGScaleAnimation::UpdateCallback : public osg::NodeCallback {
1040 UpdateCallback(const SGCondition* condition,
1041 SGSharedPtr<const SGDoubleValue> animationValue[3]) :
1042 _condition(condition)
1044 _animationValue[0] = animationValue[0];
1045 _animationValue[1] = animationValue[1];
1046 _animationValue[2] = animationValue[2];
1048 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1050 if (!_condition || _condition->test()) {
1051 SGScaleAnimation::Transform* transform;
1052 transform = static_cast<SGScaleAnimation::Transform*>(node);
1053 SGVec3d scale(_animationValue[0]->getValue(),
1054 _animationValue[1]->getValue(),
1055 _animationValue[2]->getValue());
1056 transform->setScaleFactor(scale);
1061 SGSharedPtr<SGCondition const> _condition;
1062 SGSharedPtr<SGDoubleValue const> _animationValue[3];
1065 SGScaleAnimation::SGScaleAnimation(const SGPropertyNode* configNode,
1066 SGPropertyNode* modelRoot) :
1067 SGAnimation(configNode, modelRoot)
1069 _condition = getCondition();
1071 // default offset/factor for all directions
1072 double offset = configNode->getDoubleValue("offset", 1);
1073 double factor = configNode->getDoubleValue("factor", 1);
1075 std::string inputPropertyName;
1076 inputPropertyName = configNode->getStringValue("property", "");
1077 SGPropertyNode* inputProperty = 0;
1078 if (!inputPropertyName.empty()) {
1079 inputProperty = modelRoot->getNode(inputPropertyName.c_str(), true);
1081 SGInterpTable* interpTable = read_interpolation_table(configNode);
1083 SGInterpTableValue* value;
1084 value = new SGInterpTableValue(inputProperty, interpTable);
1085 _animationValue[0] = value;
1086 _animationValue[1] = value;
1087 _animationValue[2] = value;
1088 } else if (configNode->getBoolValue("use-personality", false)) {
1089 SGPersScaleOffsetValue* value;
1090 value = new SGPersScaleOffsetValue(inputProperty, configNode,
1091 "x-factor", "x-offset",
1093 value->setMin(configNode->getDoubleValue("x-min", 0));
1094 value->setMax(configNode->getDoubleValue("x-max", SGLimitsd::max()));
1095 _animationValue[0] = value;
1096 value = new SGPersScaleOffsetValue(inputProperty, configNode,
1097 "y-factor", "y-offset",
1099 value->setMin(configNode->getDoubleValue("y-min", 0));
1100 value->setMax(configNode->getDoubleValue("y-max", SGLimitsd::max()));
1101 _animationValue[1] = value;
1102 value = new SGPersScaleOffsetValue(inputProperty, configNode,
1103 "z-factor", "z-offset",
1105 value->setMin(configNode->getDoubleValue("z-min", 0));
1106 value->setMax(configNode->getDoubleValue("z-max", SGLimitsd::max()));
1107 _animationValue[2] = value;
1109 SGScaleOffsetValue* value = new SGScaleOffsetValue(inputProperty);
1110 value->setScale(configNode->getDoubleValue("x-factor", factor));
1111 value->setOffset(configNode->getDoubleValue("x-offset", offset));
1112 value->setMin(configNode->getDoubleValue("x-min", 0));
1113 value->setMax(configNode->getDoubleValue("x-max", SGLimitsd::max()));
1114 _animationValue[0] = value;
1115 value = new SGScaleOffsetValue(inputProperty);
1116 value->setScale(configNode->getDoubleValue("y-factor", factor));
1117 value->setOffset(configNode->getDoubleValue("y-offset", offset));
1118 value->setMin(configNode->getDoubleValue("y-min", 0));
1119 value->setMax(configNode->getDoubleValue("y-max", SGLimitsd::max()));
1120 _animationValue[1] = value;
1121 value = new SGScaleOffsetValue(inputProperty);
1122 value->setScale(configNode->getDoubleValue("z-factor", factor));
1123 value->setOffset(configNode->getDoubleValue("z-offset", offset));
1124 value->setMin(configNode->getDoubleValue("z-min", 0));
1125 value->setMax(configNode->getDoubleValue("z-max", SGLimitsd::max()));
1126 _animationValue[2] = value;
1128 _initialValue[0] = configNode->getDoubleValue("x-starting-scale", 1);
1129 _initialValue[0] *= configNode->getDoubleValue("x-factor", factor);
1130 _initialValue[0] += configNode->getDoubleValue("x-offset", offset);
1131 _initialValue[1] = configNode->getDoubleValue("y-starting-scale", 1);
1132 _initialValue[1] *= configNode->getDoubleValue("y-factor", factor);
1133 _initialValue[1] += configNode->getDoubleValue("y-offset", offset);
1134 _initialValue[2] = configNode->getDoubleValue("z-starting-scale", 1);
1135 _initialValue[2] *= configNode->getDoubleValue("z-factor", factor);
1136 _initialValue[2] += configNode->getDoubleValue("z-offset", offset);
1137 _center[0] = configNode->getDoubleValue("center/x-m", 0);
1138 _center[1] = configNode->getDoubleValue("center/y-m", 0);
1139 _center[2] = configNode->getDoubleValue("center/z-m", 0);
1143 SGScaleAnimation::createAnimationGroup(osg::Group& parent)
1145 Transform* transform = new Transform;
1146 transform->setName("scale animation");
1147 transform->setCenter(_center);
1148 transform->setScaleFactor(_initialValue);
1149 UpdateCallback* uc = new UpdateCallback(_condition, _animationValue);
1150 transform->setUpdateCallback(uc);
1151 parent.addChild(transform);
1156 ////////////////////////////////////////////////////////////////////////
1157 // Implementation of dist scale animation
1158 ////////////////////////////////////////////////////////////////////////
1160 class SGDistScaleAnimation::Transform : public osg::Transform {
1162 Transform(const SGPropertyNode* configNode)
1164 setName(configNode->getStringValue("name", "dist scale animation"));
1165 setReferenceFrame(RELATIVE_RF);
1166 getOrCreateStateSet()->setMode(GL_NORMALIZE, osg::StateAttribute::ON);
1167 _factor = configNode->getFloatValue("factor", 1);
1168 _offset = configNode->getFloatValue("offset", 0);
1169 _min_v = configNode->getFloatValue("min", SGLimitsf::epsilon());
1170 _max_v = configNode->getFloatValue("max", SGLimitsf::max());
1171 _table = read_interpolation_table(configNode);
1172 _center[0] = configNode->getFloatValue("center/x-m", 0);
1173 _center[1] = configNode->getFloatValue("center/y-m", 0);
1174 _center[2] = configNode->getFloatValue("center/z-m", 0);
1176 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
1177 osg::NodeVisitor* nv) const
1179 osg::Matrix transform;
1180 double scale_factor = computeScaleFactor(nv);
1181 transform(0,0) = scale_factor;
1182 transform(1,1) = scale_factor;
1183 transform(2,2) = scale_factor;
1184 transform(3,0) = _center[0]*(1 - scale_factor);
1185 transform(3,1) = _center[1]*(1 - scale_factor);
1186 transform(3,2) = _center[2]*(1 - scale_factor);
1187 matrix.preMult(transform);
1191 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
1192 osg::NodeVisitor* nv) const
1194 double scale_factor = computeScaleFactor(nv);
1195 if (fabs(scale_factor) <= SGLimits<double>::min())
1197 osg::Matrix transform;
1198 double rScaleFactor = 1/scale_factor;
1199 transform(0,0) = rScaleFactor;
1200 transform(1,1) = rScaleFactor;
1201 transform(2,2) = rScaleFactor;
1202 transform(3,0) = _center[0]*(1 - rScaleFactor);
1203 transform(3,1) = _center[1]*(1 - rScaleFactor);
1204 transform(3,2) = _center[2]*(1 - rScaleFactor);
1205 matrix.postMult(transform);
1210 double computeScaleFactor(osg::NodeVisitor* nv) const
1215 double scale_factor = (_center.osg() - nv->getEyePoint()).length();
1217 scale_factor = _factor * scale_factor + _offset;
1219 scale_factor = _table->interpolate( scale_factor );
1221 if (scale_factor < _min_v)
1222 scale_factor = _min_v;
1223 if (scale_factor > _max_v)
1224 scale_factor = _max_v;
1226 return scale_factor;
1229 SGSharedPtr<SGInterpTable> _table;
1238 SGDistScaleAnimation::SGDistScaleAnimation(const SGPropertyNode* configNode,
1239 SGPropertyNode* modelRoot) :
1240 SGAnimation(configNode, modelRoot)
1245 SGDistScaleAnimation::createAnimationGroup(osg::Group& parent)
1247 Transform* transform = new Transform(getConfig());
1248 parent.addChild(transform);
1253 ////////////////////////////////////////////////////////////////////////
1254 // Implementation of flash animation
1255 ////////////////////////////////////////////////////////////////////////
1257 class SGFlashAnimation::Transform : public osg::Transform {
1259 Transform(const SGPropertyNode* configNode)
1261 setReferenceFrame(RELATIVE_RF);
1262 setName(configNode->getStringValue("name", "flash animation"));
1263 getOrCreateStateSet()->setMode(GL_NORMALIZE, osg::StateAttribute::ON);
1265 _axis[0] = configNode->getFloatValue("axis/x", 0);
1266 _axis[1] = configNode->getFloatValue("axis/y", 0);
1267 _axis[2] = configNode->getFloatValue("axis/z", 1);
1270 _center[0] = configNode->getFloatValue("center/x-m", 0);
1271 _center[1] = configNode->getFloatValue("center/y-m", 0);
1272 _center[2] = configNode->getFloatValue("center/z-m", 0);
1274 _offset = configNode->getFloatValue("offset", 0);
1275 _factor = configNode->getFloatValue("factor", 1);
1276 _power = configNode->getFloatValue("power", 1);
1277 _two_sides = configNode->getBoolValue("two-sides", false);
1279 _min_v = configNode->getFloatValue("min", SGLimitsf::epsilon());
1280 _max_v = configNode->getFloatValue("max", 1);
1282 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
1283 osg::NodeVisitor* nv) const
1285 osg::Matrix transform;
1286 double scale_factor = computeScaleFactor(nv);
1287 transform(0,0) = scale_factor;
1288 transform(1,1) = scale_factor;
1289 transform(2,2) = scale_factor;
1290 transform(3,0) = _center[0]*(1 - scale_factor);
1291 transform(3,1) = _center[1]*(1 - scale_factor);
1292 transform(3,2) = _center[2]*(1 - scale_factor);
1293 matrix.preMult(transform);
1297 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
1298 osg::NodeVisitor* nv) const
1300 double scale_factor = computeScaleFactor(nv);
1301 if (fabs(scale_factor) <= SGLimits<double>::min())
1303 osg::Matrix transform;
1304 double rScaleFactor = 1/scale_factor;
1305 transform(0,0) = rScaleFactor;
1306 transform(1,1) = rScaleFactor;
1307 transform(2,2) = rScaleFactor;
1308 transform(3,0) = _center[0]*(1 - rScaleFactor);
1309 transform(3,1) = _center[1]*(1 - rScaleFactor);
1310 transform(3,2) = _center[2]*(1 - rScaleFactor);
1311 matrix.postMult(transform);
1316 double computeScaleFactor(osg::NodeVisitor* nv) const
1321 osg::Vec3 localEyeToCenter = nv->getEyePoint() - _center;
1322 localEyeToCenter.normalize();
1324 double cos_angle = localEyeToCenter*_axis;
1325 double scale_factor = 0;
1326 if ( _two_sides && cos_angle < 0 )
1327 scale_factor = _factor * pow( -cos_angle, _power ) + _offset;
1328 else if ( cos_angle > 0 )
1329 scale_factor = _factor * pow( cos_angle, _power ) + _offset;
1331 if ( scale_factor < _min_v )
1332 scale_factor = _min_v;
1333 if ( scale_factor > _max_v )
1334 scale_factor = _max_v;
1336 return scale_factor;
1339 virtual osg::BoundingSphere computeBound() const
1341 // avoid being culled away by small feature culling
1342 osg::BoundingSphere bs = osg::Group::computeBound();
1343 bs.radius() *= _max_v;
1350 double _power, _factor, _offset, _min_v, _max_v;
1355 SGFlashAnimation::SGFlashAnimation(const SGPropertyNode* configNode,
1356 SGPropertyNode* modelRoot) :
1357 SGAnimation(configNode, modelRoot)
1362 SGFlashAnimation::createAnimationGroup(osg::Group& parent)
1364 Transform* transform = new Transform(getConfig());
1365 parent.addChild(transform);
1370 ////////////////////////////////////////////////////////////////////////
1371 // Implementation of flash animation
1372 ////////////////////////////////////////////////////////////////////////
1374 class SGBillboardAnimation::Transform : public osg::Transform {
1376 Transform(const SGPropertyNode* configNode) :
1377 _spherical(configNode->getBoolValue("spherical", true))
1379 setReferenceFrame(RELATIVE_RF);
1380 setName(configNode->getStringValue("name", "billboard animation"));
1382 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
1383 osg::NodeVisitor* nv) const
1385 // More or less taken from plibs ssgCutout
1387 matrix(0,0) = 1; matrix(0,1) = 0; matrix(0,2) = 0;
1388 matrix(1,0) = 0; matrix(1,1) = 0; matrix(1,2) = -1;
1389 matrix(2,0) = 0; matrix(2,1) = 1; matrix(2,2) = 0;
1391 osg::Vec3 zAxis(matrix(2, 0), matrix(2, 1), matrix(2, 2));
1392 osg::Vec3 xAxis = osg::Vec3(0, 0, -1)^zAxis;
1393 osg::Vec3 yAxis = zAxis^xAxis;
1399 matrix(0,0) = xAxis[0]; matrix(0,1) = xAxis[1]; matrix(0,2) = xAxis[2];
1400 matrix(1,0) = yAxis[0]; matrix(1,1) = yAxis[1]; matrix(1,2) = yAxis[2];
1401 matrix(2,0) = zAxis[0]; matrix(2,1) = zAxis[1]; matrix(2,2) = zAxis[2];
1406 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
1407 osg::NodeVisitor* nv) const
1409 // Hmm, don't yet know how to get that back ...
1418 SGBillboardAnimation::SGBillboardAnimation(const SGPropertyNode* configNode,
1419 SGPropertyNode* modelRoot) :
1420 SGAnimation(configNode, modelRoot)
1425 SGBillboardAnimation::createAnimationGroup(osg::Group& parent)
1427 Transform* transform = new Transform(getConfig());
1428 parent.addChild(transform);
1433 ////////////////////////////////////////////////////////////////////////
1434 // Implementation of a range animation
1435 ////////////////////////////////////////////////////////////////////////
1437 class SGRangeAnimation::UpdateCallback : public osg::NodeCallback {
1439 UpdateCallback(const SGCondition* condition,
1440 const SGDoubleValue* minAnimationValue,
1441 const SGDoubleValue* maxAnimationValue,
1442 double minValue, double maxValue) :
1443 _condition(condition),
1444 _minAnimationValue(minAnimationValue),
1445 _maxAnimationValue(maxAnimationValue),
1446 _minStaticValue(minValue),
1447 _maxStaticValue(maxValue)
1449 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1451 osg::LOD* lod = static_cast<osg::LOD*>(node);
1452 if (!_condition || _condition->test()) {
1454 if (_minAnimationValue)
1455 minRange = _minAnimationValue->getValue();
1457 minRange = _minStaticValue;
1459 if (_maxAnimationValue)
1460 maxRange = _maxAnimationValue->getValue();
1462 maxRange = _maxStaticValue;
1463 lod->setRange(0, minRange, maxRange);
1465 lod->setRange(0, 0, SGLimitsf::max());
1471 SGSharedPtr<const SGCondition> _condition;
1472 SGSharedPtr<const SGDoubleValue> _minAnimationValue;
1473 SGSharedPtr<const SGDoubleValue> _maxAnimationValue;
1474 double _minStaticValue;
1475 double _maxStaticValue;
1478 SGRangeAnimation::SGRangeAnimation(const SGPropertyNode* configNode,
1479 SGPropertyNode* modelRoot) :
1480 SGAnimation(configNode, modelRoot)
1482 _condition = getCondition();
1484 std::string inputPropertyName;
1485 inputPropertyName = configNode->getStringValue("min-property", "");
1486 if (!inputPropertyName.empty()) {
1487 SGPropertyNode* inputProperty;
1488 inputProperty = modelRoot->getNode(inputPropertyName.c_str(), true);
1489 SGScaleOffsetValue* value = new SGScaleOffsetValue(inputProperty);
1490 value->setScale(configNode->getDoubleValue("min-factor", 1));
1491 _minAnimationValue = value;
1493 inputPropertyName = configNode->getStringValue("max-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("max-factor", 1));
1499 _maxAnimationValue = value;
1502 _initialValue[0] = configNode->getDoubleValue("min-m", 0);
1503 _initialValue[0] *= configNode->getDoubleValue("min-factor", 1);
1504 _initialValue[1] = configNode->getDoubleValue("max-m", SGLimitsf::max());
1505 _initialValue[1] *= configNode->getDoubleValue("max-factor", 1);
1509 SGRangeAnimation::createAnimationGroup(osg::Group& parent)
1511 osg::Group* group = new osg::Group;
1512 group->setName("range animation group");
1514 osg::LOD* lod = new osg::LOD;
1515 lod->setName("range animation node");
1516 parent.addChild(lod);
1518 lod->addChild(group, _initialValue[0], _initialValue[1]);
1519 lod->setCenterMode(osg::LOD::USE_BOUNDING_SPHERE_CENTER);
1520 lod->setRangeMode(osg::LOD::DISTANCE_FROM_EYE_POINT);
1521 if (_minAnimationValue || _maxAnimationValue || _condition) {
1523 uc = new UpdateCallback(_condition, _minAnimationValue, _maxAnimationValue,
1524 _initialValue[0], _initialValue[1]);
1525 lod->setUpdateCallback(uc);
1531 ////////////////////////////////////////////////////////////////////////
1532 // Implementation of a select animation
1533 ////////////////////////////////////////////////////////////////////////
1535 class SGSelectAnimation::UpdateCallback : public osg::NodeCallback {
1537 UpdateCallback(const SGCondition* condition) :
1538 _condition(condition)
1540 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1542 osg::Switch* sw = static_cast<osg::Switch*>(node);
1543 if (_condition->test())
1544 sw->setAllChildrenOn();
1546 sw->setAllChildrenOff();
1551 SGSharedPtr<SGCondition const> _condition;
1554 SGSelectAnimation::SGSelectAnimation(const SGPropertyNode* configNode,
1555 SGPropertyNode* modelRoot) :
1556 SGAnimation(configNode, modelRoot)
1561 SGSelectAnimation::createAnimationGroup(osg::Group& parent)
1563 // if no condition given, this is a noop.
1564 SGSharedPtr<SGCondition const> condition = getCondition();
1565 // trick, gets deleted with all its 'animated' children
1566 // when the animation installer returns
1568 return new osg::Group;
1570 osg::Switch* sw = new osg::Switch;
1571 sw->setName("select animation node");
1572 sw->setUpdateCallback(new UpdateCallback(condition));
1573 parent.addChild(sw);
1579 ////////////////////////////////////////////////////////////////////////
1580 // Implementation of alpha test animation
1581 ////////////////////////////////////////////////////////////////////////
1583 SGAlphaTestAnimation::SGAlphaTestAnimation(const SGPropertyNode* configNode,
1584 SGPropertyNode* modelRoot) :
1585 SGAnimation(configNode, modelRoot)
1590 SGAlphaTestAnimation::install(osg::Node& node)
1592 SGAnimation::install(node);
1594 cloneDrawables(node);
1595 removeMode(node, GL_ALPHA_TEST);
1596 removeAttribute(node, osg::StateAttribute::ALPHAFUNC);
1598 osg::StateSet* stateSet = node.getOrCreateStateSet();
1599 osg::AlphaFunc* alphaFunc = new osg::AlphaFunc;
1600 alphaFunc->setFunction(osg::AlphaFunc::GREATER);
1601 float alphaClamp = getConfig()->getFloatValue("alpha-factor", 0);
1602 alphaFunc->setReferenceValue(alphaClamp);
1603 stateSet->setAttribute(alphaFunc);
1604 stateSet->setMode(GL_ALPHA_TEST, osg::StateAttribute::ON);
1609 //////////////////////////////////////////////////////////////////////
1610 // Blend animation installer
1611 //////////////////////////////////////////////////////////////////////
1613 class SGBlendAnimation::BlendVisitor : public osg::NodeVisitor {
1615 BlendVisitor(float blend) :
1616 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
1618 { setVisitorType(osg::NodeVisitor::NODE_VISITOR); }
1619 virtual void apply(osg::Node& node)
1621 updateStateSet(node.getStateSet());
1624 virtual void apply(osg::Geode& node)
1626 apply((osg::Node&)node);
1627 unsigned nDrawables = node.getNumDrawables();
1628 for (unsigned i = 0; i < nDrawables; ++i) {
1629 osg::Drawable* drawable = node.getDrawable(i);
1630 updateStateSet(drawable->getStateSet());
1631 osg::Geometry* geometry = drawable->asGeometry();
1634 osg::Array* array = geometry->getColorArray();
1637 osg::Vec4Array* vec4Array = dynamic_cast<osg::Vec4Array*>(array);
1640 geometry->dirtyDisplayList();
1642 for (unsigned k = 0; k < vec4Array->size(); ++k) {
1643 (*vec4Array)[k][3] = _blend;
1647 void updateStateSet(osg::StateSet* stateSet)
1651 osg::StateAttribute* stateAttribute;
1652 stateAttribute = stateSet->getAttribute(osg::StateAttribute::MATERIAL);
1653 if (!stateAttribute)
1655 osg::Material* material = dynamic_cast<osg::Material*>(stateAttribute);
1658 material->setAlpha(osg::Material::FRONT_AND_BACK, _blend);
1660 stateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
1661 stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
1663 stateSet->setRenderingHint(osg::StateSet::DEFAULT_BIN);
1670 class SGBlendAnimation::UpdateCallback : public osg::NodeCallback {
1672 UpdateCallback(const SGPropertyNode* configNode, const SGDoubleValue* v) :
1676 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1678 double blend = _animationValue->getValue();
1679 if (blend != _prev_value) {
1680 _prev_value = blend;
1681 BlendVisitor visitor(1-blend);
1682 node->accept(visitor);
1688 SGSharedPtr<SGDoubleValue const> _animationValue;
1692 SGBlendAnimation::SGBlendAnimation(const SGPropertyNode* configNode,
1693 SGPropertyNode* modelRoot)
1694 : SGAnimation(configNode, modelRoot),
1695 _animationValue(read_value(configNode, modelRoot, "", 0, 1))
1700 SGBlendAnimation::createAnimationGroup(osg::Group& parent)
1702 if (!_animationValue)
1705 osg::Group* group = new osg::Switch;
1706 group->setName("blend animation node");
1707 group->setUpdateCallback(new UpdateCallback(getConfig(), _animationValue));
1708 parent.addChild(group);
1713 SGBlendAnimation::install(osg::Node& node)
1715 SGAnimation::install(node);
1716 // make sure we do not change common geometries,
1717 // that also creates new display lists for these subgeometries.
1718 cloneDrawables(node);
1722 //////////////////////////////////////////////////////////////////////
1723 // Timed animation installer
1724 //////////////////////////////////////////////////////////////////////
1728 class SGTimedAnimation::UpdateCallback : public osg::NodeCallback {
1730 UpdateCallback(const SGPropertyNode* configNode) :
1733 _duration_sec(configNode->getDoubleValue("duration-sec", 1)),
1734 _last_time_sec(SGLimitsd::max()),
1735 _use_personality(configNode->getBoolValue("use-personality", false))
1737 std::vector<SGSharedPtr<SGPropertyNode> > nodes;
1738 nodes = configNode->getChildren("branch-duration-sec");
1739 for (size_t i = 0; i < nodes.size(); ++i) {
1740 unsigned ind = nodes[ i ]->getIndex();
1741 while ( ind >= _durations.size() ) {
1742 _durations.push_back(DurationSpec(_duration_sec));
1744 SGPropertyNode_ptr rNode = nodes[i]->getChild("random");
1746 _durations[ind] = DurationSpec(nodes[ i ]->getDoubleValue());
1748 _durations[ind] = DurationSpec(rNode->getDoubleValue( "min", 0),
1749 rNode->getDoubleValue( "max", 1));
1753 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1755 assert(dynamic_cast<osg::Switch*>(node));
1756 osg::Switch* sw = static_cast<osg::Switch*>(node);
1758 unsigned nChildren = sw->getNumChildren();
1760 // blow up the durations vector to the required size
1761 while (_durations.size() < nChildren) {
1762 _durations.push_back(_duration_sec);
1764 // make sure the current index is an duration that really exists
1765 _current_index = _current_index % nChildren;
1767 // update the time and compute the current systems time value
1768 double t = nv->getFrameStamp()->getReferenceTime();
1769 if (_last_time_sec == SGLimitsd::max()) {
1772 double dt = t - _last_time_sec;
1773 if (_use_personality)
1774 dt *= 1 + 0.2*(0.5 - sg_random());
1779 double currentDuration = _durations[_current_index].get();
1780 while (currentDuration < _reminder) {
1781 _reminder -= currentDuration;
1782 _current_index = (_current_index + 1) % nChildren;
1783 currentDuration = _durations[_current_index].get();
1786 sw->setSingleChildOn(_current_index);
1792 struct DurationSpec {
1793 DurationSpec(double t) :
1794 minTime(SGMiscd::max(0.01, t)),
1795 maxTime(SGMiscd::max(0.01, t))
1797 DurationSpec(double t0, double t1) :
1798 minTime(SGMiscd::max(0.01, t0)),
1799 maxTime(SGMiscd::max(0.01, t1))
1802 { return minTime + sg_random()*(maxTime - minTime); }
1806 std::vector<DurationSpec> _durations;
1807 unsigned _current_index;
1809 double _duration_sec;
1810 double _last_time_sec;
1811 bool _use_personality;
1815 SGTimedAnimation::SGTimedAnimation(const SGPropertyNode* configNode,
1816 SGPropertyNode* modelRoot)
1817 : SGAnimation(configNode, modelRoot)
1822 SGTimedAnimation::createAnimationGroup(osg::Group& parent)
1824 osg::Switch* sw = new osg::Switch;
1825 sw->setName("timed animation node");
1826 sw->setUpdateCallback(new UpdateCallback(getConfig()));
1827 parent.addChild(sw);
1832 ////////////////////////////////////////////////////////////////////////
1833 // dynamically switch on/off shadows
1834 ////////////////////////////////////////////////////////////////////////
1836 class SGShadowAnimation::UpdateCallback : public osg::NodeCallback {
1838 UpdateCallback(const SGCondition* condition) :
1839 _condition(condition)
1841 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1843 if (_condition->test())
1844 node->setNodeMask( SG_NODEMASK_CASTSHADOW_BIT | node->getNodeMask());
1846 node->setNodeMask(~SG_NODEMASK_CASTSHADOW_BIT & node->getNodeMask());
1851 SGSharedPtr<const SGCondition> _condition;
1854 SGShadowAnimation::SGShadowAnimation(const SGPropertyNode* configNode,
1855 SGPropertyNode* modelRoot) :
1856 SGAnimation(configNode, modelRoot)
1861 SGShadowAnimation::createAnimationGroup(osg::Group& parent)
1863 SGSharedPtr<SGCondition const> condition = getCondition();
1867 osg::Group* group = new osg::Group;
1868 group->setName("shadow animation");
1869 group->setUpdateCallback(new UpdateCallback(condition));
1870 parent.addChild(group);
1875 ////////////////////////////////////////////////////////////////////////
1876 // Implementation of SGTexTransformAnimation
1877 ////////////////////////////////////////////////////////////////////////
1879 class SGTexTransformAnimation::Transform : public SGReferenced {
1884 virtual ~Transform()
1886 void setValue(double value)
1888 virtual void transform(osg::Matrix&) = 0;
1893 class SGTexTransformAnimation::Translation :
1894 public SGTexTransformAnimation::Transform {
1896 Translation(const SGVec3d& axis) :
1899 void setValue(double value)
1901 virtual void transform(osg::Matrix& matrix)
1904 set_translation(tmp, _value, _axis);
1905 matrix.preMult(tmp);
1911 class SGTexTransformAnimation::Rotation :
1912 public SGTexTransformAnimation::Transform {
1914 Rotation(const SGVec3d& axis, const SGVec3d& center) :
1918 virtual void transform(osg::Matrix& matrix)
1921 set_rotation(tmp, _value, _center, _axis);
1922 matrix.preMult(tmp);
1929 class SGTexTransformAnimation::UpdateCallback :
1930 public osg::StateAttribute::Callback {
1932 UpdateCallback(const SGCondition* condition) :
1933 _condition(condition)
1935 virtual void operator () (osg::StateAttribute* sa, osg::NodeVisitor*)
1937 if (!_condition || _condition->test()) {
1938 TransformList::const_iterator i;
1939 for (i = _transforms.begin(); i != _transforms.end(); ++i)
1940 i->transform->setValue(i->value->getValue());
1942 assert(dynamic_cast<osg::TexMat*>(sa));
1943 osg::TexMat* texMat = static_cast<osg::TexMat*>(sa);
1944 texMat->getMatrix().makeIdentity();
1945 TransformList::const_iterator i;
1946 for (i = _transforms.begin(); i != _transforms.end(); ++i)
1947 i->transform->transform(texMat->getMatrix());
1949 void appendTransform(Transform* transform, SGDoubleValue* value)
1951 Entry entry = { transform, value };
1952 transform->transform(_matrix);
1953 _transforms.push_back(entry);
1958 SGSharedPtr<Transform> transform;
1959 SGSharedPtr<const SGDoubleValue> value;
1961 typedef std::vector<Entry> TransformList;
1962 TransformList _transforms;
1963 SGSharedPtr<const SGCondition> _condition;
1964 osg::Matrix _matrix;
1967 SGTexTransformAnimation::SGTexTransformAnimation(const SGPropertyNode* configNode,
1968 SGPropertyNode* modelRoot) :
1969 SGAnimation(configNode, modelRoot)
1974 SGTexTransformAnimation::createAnimationGroup(osg::Group& parent)
1976 osg::Group* group = new osg::Group;
1977 group->setName("texture transform group");
1978 osg::StateSet* stateSet = group->getOrCreateStateSet();
1979 osg::TexMat* texMat = new osg::TexMat;
1980 UpdateCallback* updateCallback = new UpdateCallback(getCondition());
1981 // interpret the configs ...
1982 std::string type = getType();
1984 if (type == "textranslate") {
1985 appendTexTranslate(getConfig(), updateCallback);
1986 } else if (type == "texrotate") {
1987 appendTexRotate(getConfig(), updateCallback);
1988 } else if (type == "texmultiple") {
1989 std::vector<SGSharedPtr<SGPropertyNode> > transformConfigs;
1990 transformConfigs = getConfig()->getChildren("transform");
1991 for (unsigned i = 0; i < transformConfigs.size(); ++i) {
1992 std::string subtype = transformConfigs[i]->getStringValue("subtype", "");
1993 if (subtype == "textranslate")
1994 appendTexTranslate(transformConfigs[i], updateCallback);
1995 else if (subtype == "texrotate")
1996 appendTexRotate(transformConfigs[i], updateCallback);
1998 SG_LOG(SG_INPUT, SG_ALERT,
1999 "Ignoring unknown texture transform subtype");
2002 SG_LOG(SG_INPUT, SG_ALERT, "Ignoring unknown texture transform type");
2005 texMat->setUpdateCallback(updateCallback);
2006 stateSet->setTextureAttribute(0, texMat);
2007 parent.addChild(group);
2012 SGTexTransformAnimation::appendTexTranslate(const SGPropertyNode* config,
2013 UpdateCallback* updateCallback)
2015 std::string propertyName = config->getStringValue("property", "/null");
2016 SGPropertyNode* inputNode;
2017 inputNode = getModelRoot()->getNode(propertyName.c_str(), true);
2019 SGDoubleValue* animationValue;
2020 SGInterpTable* table = read_interpolation_table(config);
2022 SGTexTableValue* value;
2023 value = new SGTexTableValue(inputNode, table);
2024 value->setStep(config->getDoubleValue("step", 0));
2025 value->setScroll(config->getDoubleValue("scroll", 0));
2026 animationValue = value;
2028 SGTexScaleOffsetValue* value;
2029 value = new SGTexScaleOffsetValue(inputNode);
2030 value->setScale(config->getDoubleValue("factor", 1));
2031 value->setOffset(config->getDoubleValue("offset", 0));
2032 value->setStep(config->getDoubleValue("step", 0));
2033 value->setScroll(config->getDoubleValue("scroll", 0));
2034 value->setMin(config->getDoubleValue("min", -SGLimitsd::max()));
2035 value->setMax(config->getDoubleValue("max", SGLimitsd::max()));
2036 animationValue = value;
2038 SGVec3d axis(config->getDoubleValue("axis/x", 0),
2039 config->getDoubleValue("axis/y", 0),
2040 config->getDoubleValue("axis/z", 0));
2041 Translation* translation;
2042 translation = new Translation(normalize(axis));
2043 translation->setValue(config->getDoubleValue("starting-position", 0));
2044 updateCallback->appendTransform(translation, animationValue);
2048 SGTexTransformAnimation::appendTexRotate(const SGPropertyNode* config,
2049 UpdateCallback* updateCallback)
2051 std::string propertyName = config->getStringValue("property", "/null");
2052 SGPropertyNode* inputNode;
2053 inputNode = getModelRoot()->getNode(propertyName.c_str(), true);
2055 SGDoubleValue* animationValue;
2056 SGInterpTable* table = read_interpolation_table(config);
2058 SGTexTableValue* value;
2059 value = new SGTexTableValue(inputNode, table);
2060 value->setStep(config->getDoubleValue("step", 0));
2061 value->setScroll(config->getDoubleValue("scroll", 0));
2062 animationValue = value;
2064 SGTexScaleOffsetValue* value;
2065 value = new SGTexScaleOffsetValue(inputNode);
2066 value->setScale(config->getDoubleValue("factor", 1));
2067 value->setOffset(config->getDoubleValue("offset-deg", 0));
2068 value->setStep(config->getDoubleValue("step", 0));
2069 value->setScroll(config->getDoubleValue("scroll", 0));
2070 value->setMin(config->getDoubleValue("min-deg", -SGLimitsd::max()));
2071 value->setMax(config->getDoubleValue("max-deg", SGLimitsd::max()));
2072 animationValue = value;
2074 SGVec3d axis(config->getDoubleValue("axis/x", 0),
2075 config->getDoubleValue("axis/y", 0),
2076 config->getDoubleValue("axis/z", 0));
2077 SGVec3d center(config->getDoubleValue("center/x", 0),
2078 config->getDoubleValue("center/y", 0),
2079 config->getDoubleValue("center/z", 0));
2081 rotation = new Rotation(normalize(axis), center);
2082 rotation->setValue(config->getDoubleValue("starting-position-deg", 0));
2083 updateCallback->appendTransform(rotation, animationValue);
2087 ////////////////////////////////////////////////////////////////////////
2088 // Implementation of SGPickAnimation
2089 ////////////////////////////////////////////////////////////////////////
2091 class SGPickAnimation::PickCallback : public SGPickCallback {
2093 PickCallback(const SGPropertyNode* configNode,
2094 SGPropertyNode* modelRoot) :
2095 _button(configNode->getIntValue("button", -1)),
2096 _repeatable(configNode->getBoolValue("repeatable", false)),
2097 _repeatInterval(configNode->getDoubleValue("interval-sec", 0.1))
2099 SG_LOG(SG_INPUT, SG_DEBUG, "Reading all bindings");
2100 std::vector<SGPropertyNode_ptr> bindings;
2101 bindings = configNode->getChildren("binding");
2102 for (unsigned int i = 0; i < bindings.size(); ++i) {
2103 _bindingsDown.push_back(new SGBinding(bindings[i], modelRoot));
2106 const SGPropertyNode* upNode = configNode->getChild("mod-up");
2109 bindings = upNode->getChildren("binding");
2110 for (unsigned int i = 0; i < bindings.size(); ++i) {
2111 _bindingsUp.push_back(new SGBinding(bindings[i], modelRoot));
2114 virtual bool buttonPressed(int button, const Info&)
2116 if (0 <= _button && button != _button)
2118 SGBindingList::const_iterator i;
2119 for (i = _bindingsDown.begin(); i != _bindingsDown.end(); ++i)
2124 virtual void buttonReleased(void)
2126 SGBindingList::const_iterator i;
2127 for (i = _bindingsUp.begin(); i != _bindingsUp.end(); ++i)
2130 virtual void update(double dt)
2136 while (_repeatInterval < _repeatTime) {
2137 _repeatTime -= _repeatInterval;
2138 SGBindingList::const_iterator i;
2139 for (i = _bindingsDown.begin(); i != _bindingsDown.end(); ++i)
2144 SGBindingList _bindingsDown;
2145 SGBindingList _bindingsUp;
2148 double _repeatInterval;
2152 SGPickAnimation::SGPickAnimation(const SGPropertyNode* configNode,
2153 SGPropertyNode* modelRoot) :
2154 SGAnimation(configNode, modelRoot)
2159 SGPickAnimation::createAnimationGroup(osg::Group& parent)
2161 osg::Group* commonGroup = new osg::Group;
2163 // Contains the normal geometry that is interactive
2164 osg::ref_ptr<osg::Group> normalGroup = new osg::Group;
2165 normalGroup->addChild(commonGroup);
2167 // Used to render the geometry with just yellow edges
2168 osg::Group* highlightGroup = new osg::Group;
2169 highlightGroup->setNodeMask(SG_NODEMASK_PICK_BIT);
2170 highlightGroup->addChild(commonGroup);
2171 SGSceneUserData* ud;
2172 ud = SGSceneUserData::getOrCreateSceneUserData(highlightGroup);
2173 std::vector<SGPropertyNode_ptr> actions;
2174 actions = getConfig()->getChildren("action");
2175 for (unsigned int i = 0; i < actions.size(); ++i)
2176 ud->addPickCallback(new PickCallback(actions[i], getModelRoot()));
2178 // prepare a state set that paints the edges of this object yellow
2179 osg::StateSet* stateSet = highlightGroup->getOrCreateStateSet();
2180 stateSet->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE);
2182 osg::PolygonOffset* polygonOffset = new osg::PolygonOffset;
2183 polygonOffset->setFactor(-1);
2184 polygonOffset->setUnits(-1);
2185 stateSet->setAttribute(polygonOffset, osg::StateAttribute::OVERRIDE);
2186 stateSet->setMode(GL_POLYGON_OFFSET_LINE, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
2188 osg::PolygonMode* polygonMode = new osg::PolygonMode;
2189 polygonMode->setMode(osg::PolygonMode::FRONT_AND_BACK,
2190 osg::PolygonMode::LINE);
2191 stateSet->setAttribute(polygonMode, osg::StateAttribute::OVERRIDE);
2193 osg::Material* material = new osg::Material;
2194 material->setColorMode(osg::Material::OFF);
2195 material->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(1, 1, 0, 1));
2196 material->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(1, 1, 0, 1));
2197 material->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(1, 1, 0, 1));
2198 material->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0, 0, 0, 0));
2199 stateSet->setAttribute(material, osg::StateAttribute::OVERRIDE);
2201 // Only add normal geometry if configured
2202 if (getConfig()->getBoolValue("visible", true))
2203 parent.addChild(normalGroup.get());
2204 parent.addChild(highlightGroup);