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 <OpenThreads/Mutex>
15 #include <OpenThreads/ScopedLock>
17 #include <osg/AlphaFunc>
18 #include <osg/Drawable>
20 #include <osg/Geometry>
22 #include <osg/PolygonMode>
23 #include <osg/PolygonOffset>
24 #include <osg/StateSet>
27 #include <osg/Texture2D>
28 #include <osg/Transform>
29 #include <osgDB/ReadFile>
31 #include <simgear/math/interpolater.hxx>
32 #include <simgear/props/condition.hxx>
33 #include <simgear/props/props.hxx>
34 #include <simgear/structure/SGBinding.hxx>
35 #include <simgear/scene/util/SGNodeMasks.hxx>
36 #include <simgear/scene/util/SGSceneUserData.hxx>
37 #include <simgear/scene/util/SGStateAttributeVisitor.hxx>
39 #include "animation.hxx"
42 #include "SGTranslateTransform.hxx"
43 #include "SGMaterialAnimation.hxx"
44 #include "SGRotateTransform.hxx"
45 #include "SGScaleTransform.hxx"
48 ////////////////////////////////////////////////////////////////////////
49 // Static utility functions.
50 ////////////////////////////////////////////////////////////////////////
53 * Set up the transform matrix for a spin or rotation.
56 set_rotation (osg::Matrix &matrix, double position_deg,
57 const SGVec3d ¢er, const SGVec3d &axis)
59 double temp_angle = -SGMiscd::deg2rad(position_deg);
61 double s = sin(temp_angle);
62 double c = cos(temp_angle);
65 // axis was normalized at load time
66 // hint to the compiler to put these into FP registers
71 matrix(0, 0) = t * x * x + c ;
72 matrix(0, 1) = t * y * x - s * z ;
73 matrix(0, 2) = t * z * x + s * y ;
76 matrix(1, 0) = t * x * y + s * z ;
77 matrix(1, 1) = t * y * y + c ;
78 matrix(1, 2) = t * z * y - s * x ;
81 matrix(2, 0) = t * x * z - s * y ;
82 matrix(2, 1) = t * y * z + s * x ;
83 matrix(2, 2) = t * z * z + c ;
86 // hint to the compiler to put these into FP registers
91 matrix(3, 0) = x - x*matrix(0, 0) - y*matrix(1, 0) - z*matrix(2, 0);
92 matrix(3, 1) = y - x*matrix(0, 1) - y*matrix(1, 1) - z*matrix(2, 1);
93 matrix(3, 2) = z - x*matrix(0, 2) - y*matrix(1, 2) - z*matrix(2, 2);
98 * Set up the transform matrix for a translation.
101 set_translation (osg::Matrix &matrix, double position_m, const SGVec3d &axis)
103 SGVec3d xyz = axis * position_m;
104 matrix.makeIdentity();
105 matrix(3, 0) = xyz[0];
106 matrix(3, 1) = xyz[1];
107 matrix(3, 2) = xyz[2];
111 * Modify property value by step and scroll settings in texture translations
114 apply_mods(double property, double step, double scroll, double bias)
120 double scrollval = 0.0;
122 // calculate scroll amount (for odometer like movement)
123 double remainder = step - fmod(fabs(property), step);
124 if (remainder < scroll) {
125 scrollval = (scroll - remainder) / scroll * step;
128 // apply stepping of input value
130 modprop = ((floor(property/step) * step) + scrollval);
132 modprop = ((ceil(property/step) * step) + scrollval);
141 * Read an interpolation table from properties.
143 static SGInterpTable *
144 read_interpolation_table(const SGPropertyNode* props)
146 const SGPropertyNode* table_node = props->getNode("interpolation");
149 return new SGInterpTable(table_node);
152 ////////////////////////////////////////////////////////////////////////
153 // Utility value classes
154 ////////////////////////////////////////////////////////////////////////
155 class SGScaleOffsetValue : public SGDoubleValue {
157 SGScaleOffsetValue(SGPropertyNode const* propertyNode) :
158 _propertyNode(propertyNode),
161 _min(-SGLimitsd::max()),
162 _max(SGLimitsd::max())
164 void setScale(double scale)
166 void setOffset(double offset)
167 { _offset = offset; }
168 void setMin(double min)
170 void setMax(double max)
173 virtual double getValue() const
175 double value = _propertyNode ? _propertyNode->getDoubleValue() : 0;
176 return std::min(_max, std::max(_min, _offset + _scale*value));
179 SGSharedPtr<SGPropertyNode const> _propertyNode;
186 class SGPersScaleOffsetValue : public SGDoubleValue {
188 SGPersScaleOffsetValue(SGPropertyNode const* propertyNode,
189 SGPropertyNode const* config,
190 const char* scalename, const char* offsetname,
191 double defScale = 1, double defOffset = 0) :
192 _propertyNode(propertyNode),
193 _scale(config, scalename, defScale),
194 _offset(config, offsetname, defOffset),
195 _min(-SGLimitsd::max()),
196 _max(SGLimitsd::max())
198 void setScale(double scale)
200 void setOffset(double offset)
201 { _offset = offset; }
202 void setMin(double min)
204 void setMax(double max)
207 virtual double getValue() const
211 double value = _propertyNode ? _propertyNode->getDoubleValue() : 0;
212 return SGMiscd::clip(_offset + _scale*value, _min, _max);
215 SGSharedPtr<SGPropertyNode const> _propertyNode;
216 mutable SGPersonalityParameter<double> _scale;
217 mutable SGPersonalityParameter<double> _offset;
222 class SGInterpTableValue : public SGDoubleValue {
224 SGInterpTableValue(SGPropertyNode const* propertyNode,
225 SGInterpTable const* interpTable) :
226 _propertyNode(propertyNode),
227 _interpTable(interpTable)
229 virtual double getValue() const
230 { return _interpTable->interpolate(_propertyNode ? _propertyNode->getDoubleValue() : 0); }
232 SGSharedPtr<SGPropertyNode const> _propertyNode;
233 SGSharedPtr<SGInterpTable const> _interpTable;
236 class SGTexScaleOffsetValue : public SGDoubleValue {
238 SGTexScaleOffsetValue(const SGPropertyNode* propertyNode) :
239 _propertyNode(propertyNode),
245 _min(-SGLimitsd::max()),
246 _max(SGLimitsd::max())
248 void setScale(double scale)
250 void setOffset(double offset)
251 { _offset = offset; }
252 void setStep(double step)
254 void setScroll(double scroll)
255 { _scroll = scroll; }
256 void setBias(double bias)
258 void setMin(double min)
260 void setMax(double max)
263 virtual double getValue() const
265 double value = _propertyNode ? _propertyNode->getDoubleValue() : 0;
266 value = apply_mods(value, _step, _scroll, _bias);
267 return SGMiscd::clip(_scale*(_offset + value), _min, _max);
270 SGSharedPtr<const SGPropertyNode> _propertyNode;
280 class SGTexTableValue : public SGDoubleValue {
282 SGTexTableValue(const SGPropertyNode* propertyNode,
283 const SGInterpTable* interpTable) :
284 _propertyNode(propertyNode),
285 _interpTable(interpTable)
287 void setStep(double step)
289 void setScroll(double scroll)
290 { _scroll = scroll; }
291 void setBias(double bias)
293 virtual double getValue() const
295 double value = _propertyNode ? _propertyNode->getDoubleValue() : 0;
296 value = apply_mods(value, _step, _scroll, _bias);
297 return _interpTable->interpolate(value);
300 SGSharedPtr<SGPropertyNode const> _propertyNode;
301 SGSharedPtr<SGInterpTable const> _interpTable;
308 unit_string(const char* value, const char* unit)
310 return std::string(value) + unit;
313 static SGDoubleValue*
314 read_value(const SGPropertyNode* configNode, SGPropertyNode* modelRoot,
315 const char* unit, double defMin, double defMax)
317 std::string inputPropertyName;
318 inputPropertyName = configNode->getStringValue("property", "");
319 if (!inputPropertyName.empty()) {
320 SGPropertyNode* inputProperty;
321 inputProperty = modelRoot->getNode(inputPropertyName.c_str(), true);
322 SGInterpTable* interpTable = read_interpolation_table(configNode);
324 SGInterpTableValue* value;
325 value = new SGInterpTableValue(inputProperty, interpTable);
328 std::string offset = unit_string("offset", unit);
329 std::string min = unit_string("min", unit);
330 std::string max = unit_string("max", unit);
332 if (configNode->getBoolValue("use-personality", false)) {
333 SGPersScaleOffsetValue* value;
334 value = new SGPersScaleOffsetValue(inputProperty, configNode,
335 "factor", offset.c_str());
336 value->setMin(configNode->getDoubleValue(min.c_str(), defMin));
337 value->setMax(configNode->getDoubleValue(max.c_str(), defMax));
340 SGScaleOffsetValue* value = new SGScaleOffsetValue(inputProperty);
341 value->setScale(configNode->getDoubleValue("factor", 1));
342 value->setOffset(configNode->getDoubleValue(offset.c_str(), 0));
343 value->setMin(configNode->getDoubleValue(min.c_str(), defMin));
344 value->setMax(configNode->getDoubleValue(max.c_str(), defMax));
353 ////////////////////////////////////////////////////////////////////////
354 // Animation installer
355 ////////////////////////////////////////////////////////////////////////
357 class SGAnimation::RemoveModeVisitor : public SGStateAttributeVisitor {
359 RemoveModeVisitor(osg::StateAttribute::GLMode mode) :
362 virtual void apply(osg::StateSet* stateSet)
366 stateSet->removeMode(_mode);
369 osg::StateAttribute::GLMode _mode;
372 class SGAnimation::RemoveAttributeVisitor : public SGStateAttributeVisitor {
374 RemoveAttributeVisitor(osg::StateAttribute::Type type) :
377 virtual void apply(osg::StateSet* stateSet)
381 while (stateSet->getAttribute(_type)) {
382 stateSet->removeAttribute(_type);
386 osg::StateAttribute::Type _type;
389 class SGAnimation::RemoveTextureModeVisitor : public SGStateAttributeVisitor {
391 RemoveTextureModeVisitor(unsigned unit, osg::StateAttribute::GLMode mode) :
395 virtual void apply(osg::StateSet* stateSet)
399 stateSet->removeTextureMode(_unit, _mode);
403 osg::StateAttribute::GLMode _mode;
406 class SGAnimation::RemoveTextureAttributeVisitor :
407 public SGStateAttributeVisitor {
409 RemoveTextureAttributeVisitor(unsigned unit,
410 osg::StateAttribute::Type type) :
414 virtual void apply(osg::StateSet* stateSet)
418 while (stateSet->getTextureAttribute(_unit, _type)) {
419 stateSet->removeTextureAttribute(_unit, _type);
424 osg::StateAttribute::Type _type;
427 class SGAnimation::BinToInheritVisitor : public SGStateAttributeVisitor {
429 virtual void apply(osg::StateSet* stateSet)
433 stateSet->setRenderBinToInherit();
437 class SGAnimation::DrawableCloneVisitor : public osg::NodeVisitor {
439 DrawableCloneVisitor() :
440 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
442 void apply(osg::Geode& geode)
444 for (unsigned i = 0 ; i < geode.getNumDrawables(); ++i) {
445 osg::CopyOp copyOp(osg::CopyOp::DEEP_COPY_ALL &
446 ~osg::CopyOp::DEEP_COPY_TEXTURES);
447 geode.setDrawable(i, copyOp(geode.getDrawable(i)));
453 SGAnimation::SGAnimation(const SGPropertyNode* configNode,
454 SGPropertyNode* modelRoot) :
455 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
457 _configNode(configNode),
458 _modelRoot(modelRoot)
460 _name = configNode->getStringValue("name", "");
461 _enableHOT = configNode->getBoolValue("enable-hot", true);
462 _disableShadow = configNode->getBoolValue("disable-shadow", false);
463 std::vector<SGPropertyNode_ptr> objectNames =
464 configNode->getChildren("object-name");
465 for (unsigned i = 0; i < objectNames.size(); ++i)
466 _objectNames.push_back(objectNames[i]->getStringValue());
469 SGAnimation::~SGAnimation()
474 SG_LOG(SG_IO, SG_ALERT, "Could not find at least one of the following"
475 " objects for animation:\n");
476 std::list<std::string>::const_iterator i;
477 for (i = _objectNames.begin(); i != _objectNames.end(); ++i)
478 SG_LOG(SG_IO, SG_ALERT, *i << "\n");
482 SGAnimation::animate(osg::Node* node, const SGPropertyNode* configNode,
483 SGPropertyNode* modelRoot)
485 std::string type = configNode->getStringValue("type", "none");
486 if (type == "alpha-test") {
487 SGAlphaTestAnimation animInst(configNode, modelRoot);
488 animInst.apply(node);
489 } else if (type == "billboard") {
490 SGBillboardAnimation animInst(configNode, modelRoot);
491 animInst.apply(node);
492 } else if (type == "blend") {
493 SGBlendAnimation animInst(configNode, modelRoot);
494 animInst.apply(node);
495 } else if (type == "dist-scale") {
496 SGDistScaleAnimation animInst(configNode, modelRoot);
497 animInst.apply(node);
498 } else if (type == "flash") {
499 SGFlashAnimation animInst(configNode, modelRoot);
500 animInst.apply(node);
501 } else if (type == "material") {
502 SGMaterialAnimation animInst(configNode, modelRoot);
503 animInst.apply(node);
504 } else if (type == "noshadow") {
505 SGShadowAnimation animInst(configNode, modelRoot);
506 animInst.apply(node);
507 } else if (type == "pick") {
508 SGPickAnimation animInst(configNode, modelRoot);
509 animInst.apply(node);
510 } else if (type == "range") {
511 SGRangeAnimation animInst(configNode, modelRoot);
512 animInst.apply(node);
513 } else if (type == "rotate" || type == "spin") {
514 SGRotateAnimation animInst(configNode, modelRoot);
515 animInst.apply(node);
516 } else if (type == "scale") {
517 SGScaleAnimation animInst(configNode, modelRoot);
518 animInst.apply(node);
519 } else if (type == "select") {
520 SGSelectAnimation animInst(configNode, modelRoot);
521 animInst.apply(node);
522 } else if (type == "shader") {
523 SGShaderAnimation animInst(configNode, modelRoot);
524 animInst.apply(node);
525 } else if (type == "textranslate" || type == "texrotate" ||
526 type == "texmultiple") {
527 SGTexTransformAnimation animInst(configNode, modelRoot);
528 animInst.apply(node);
529 } else if (type == "timed") {
530 SGTimedAnimation animInst(configNode, modelRoot);
531 animInst.apply(node);
532 } else if (type == "translate") {
533 SGTranslateAnimation animInst(configNode, modelRoot);
534 animInst.apply(node);
535 } else if (type == "null" || type == "none" || type.empty()) {
536 SGGroupAnimation animInst(configNode, modelRoot);
537 animInst.apply(node);
546 SGAnimation::apply(osg::Node* node)
548 // duh what a special case ...
549 if (_objectNames.empty()) {
550 osg::Group* group = node->asGroup();
552 osg::ref_ptr<osg::Group> animationGroup;
553 installInGroup(std::string(), *group, animationGroup);
560 SGAnimation::install(osg::Node& node)
564 node.setNodeMask( SG_NODEMASK_TERRAIN_BIT | node.getNodeMask());
566 node.setNodeMask(~SG_NODEMASK_TERRAIN_BIT & node.getNodeMask());
568 node.setNodeMask( SG_NODEMASK_CASTSHADOW_BIT | node.getNodeMask());
570 node.setNodeMask(~SG_NODEMASK_CASTSHADOW_BIT & node.getNodeMask());
574 SGAnimation::createAnimationGroup(osg::Group& parent)
576 // default implementation, we do not need a new group
577 // for every animation type. Usually animations that just change
578 // the StateSet of some parts of the model
583 SGAnimation::apply(osg::Group& group)
585 // the trick is to first traverse the children and then
586 // possibly splice in a new group node if required.
587 // Else we end up in a recursive loop where we infinitly insert new
591 // Note that this algorithm preserves the order of the child objects
592 // like they appear in the object-name tags.
593 // The timed animations require this
594 osg::ref_ptr<osg::Group> animationGroup;
595 std::list<std::string>::const_iterator nameIt;
596 for (nameIt = _objectNames.begin(); nameIt != _objectNames.end(); ++nameIt)
597 installInGroup(*nameIt, group, animationGroup);
601 SGAnimation::installInGroup(const std::string& name, osg::Group& group,
602 osg::ref_ptr<osg::Group>& animationGroup)
604 int i = group.getNumChildren() - 1;
605 for (; 0 <= i; --i) {
606 osg::Node* child = group.getChild(i);
608 // Check if this one is already processed
609 if (std::find(_installedAnimations.begin(),
610 _installedAnimations.end(), child)
611 != _installedAnimations.end())
614 if (name.empty() || child->getName() == name) {
615 // fire the installation of the animation
618 // create a group node on demand
619 if (!animationGroup.valid()) {
620 animationGroup = createAnimationGroup(group);
621 // Animation type that does not require a new group,
622 // in this case we can stop and look for the next object
623 if (animationGroup.valid() && !_name.empty())
624 animationGroup->setName(_name);
626 if (animationGroup.valid()) {
627 animationGroup->addChild(child);
628 group.removeChild(i);
631 // store that we already have processed this child node
632 // We can hit this one twice if an animation references some
633 // part of a subtree twice
634 _installedAnimations.push_back(child);
640 SGAnimation::removeMode(osg::Node& node, osg::StateAttribute::GLMode mode)
642 RemoveModeVisitor visitor(mode);
643 node.accept(visitor);
647 SGAnimation::removeAttribute(osg::Node& node, osg::StateAttribute::Type type)
649 RemoveAttributeVisitor visitor(type);
650 node.accept(visitor);
654 SGAnimation::removeTextureMode(osg::Node& node, unsigned unit,
655 osg::StateAttribute::GLMode mode)
657 RemoveTextureModeVisitor visitor(unit, mode);
658 node.accept(visitor);
662 SGAnimation::removeTextureAttribute(osg::Node& node, unsigned unit,
663 osg::StateAttribute::Type type)
665 RemoveTextureAttributeVisitor visitor(unit, type);
666 node.accept(visitor);
670 SGAnimation::setRenderBinToInherit(osg::Node& node)
672 BinToInheritVisitor visitor;
673 node.accept(visitor);
677 SGAnimation::cloneDrawables(osg::Node& node)
679 DrawableCloneVisitor visitor;
680 node.accept(visitor);
684 SGAnimation::getCondition() const
686 const SGPropertyNode* conditionNode = _configNode->getChild("condition");
689 return sgReadCondition(_modelRoot, conditionNode);
694 ////////////////////////////////////////////////////////////////////////
695 // Implementation of null animation
696 ////////////////////////////////////////////////////////////////////////
698 // Ok, that is to build a subgraph from different other
699 // graph nodes. I guess that this stems from the time where modellers
700 // could not build hierarchical trees ...
701 SGGroupAnimation::SGGroupAnimation(const SGPropertyNode* configNode,
702 SGPropertyNode* modelRoot):
703 SGAnimation(configNode, modelRoot)
708 SGGroupAnimation::createAnimationGroup(osg::Group& parent)
710 osg::Group* group = new osg::Group;
711 parent.addChild(group);
716 ////////////////////////////////////////////////////////////////////////
717 // Implementation of translate animation
718 ////////////////////////////////////////////////////////////////////////
720 class SGTranslateAnimation::UpdateCallback : public osg::NodeCallback {
722 UpdateCallback(SGCondition const* condition,
723 SGDoubleValue const* animationValue) :
724 _condition(condition),
725 _animationValue(animationValue)
727 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
729 if (!_condition || _condition->test()) {
730 SGTranslateTransform* transform;
731 transform = static_cast<SGTranslateTransform*>(node);
732 transform->setValue(_animationValue->getValue());
737 SGSharedPtr<SGCondition const> _condition;
738 SGSharedPtr<SGDoubleValue const> _animationValue;
741 SGTranslateAnimation::SGTranslateAnimation(const SGPropertyNode* configNode,
742 SGPropertyNode* modelRoot) :
743 SGAnimation(configNode, modelRoot)
745 _condition = getCondition();
746 _animationValue = read_value(configNode, modelRoot, "-m",
747 -SGLimitsd::max(), SGLimitsd::max());
748 _axis[0] = configNode->getDoubleValue("axis/x", 0);
749 _axis[1] = configNode->getDoubleValue("axis/y", 0);
750 _axis[2] = configNode->getDoubleValue("axis/z", 0);
751 if (8*SGLimitsd::min() < norm(_axis))
752 _axis = normalize(_axis);
754 _initialValue = configNode->getDoubleValue("starting-position-m", 0);
755 _initialValue *= configNode->getDoubleValue("factor", 1);
756 _initialValue += configNode->getDoubleValue("offset-m", 0);
760 SGTranslateAnimation::createAnimationGroup(osg::Group& parent)
762 SGTranslateTransform* transform = new SGTranslateTransform;
763 transform->setName("translate animation");
764 if (_animationValue) {
765 UpdateCallback* uc = new UpdateCallback(_condition, _animationValue);
766 transform->setUpdateCallback(uc);
768 transform->setAxis(_axis);
769 transform->setValue(_initialValue);
770 parent.addChild(transform);
775 ////////////////////////////////////////////////////////////////////////
776 // Implementation of rotate/spin animation
777 ////////////////////////////////////////////////////////////////////////
779 class SGRotateAnimation::UpdateCallback : public osg::NodeCallback {
781 UpdateCallback(SGCondition const* condition,
782 SGDoubleValue const* animationValue) :
783 _condition(condition),
784 _animationValue(animationValue)
786 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
788 if (!_condition || _condition->test()) {
789 SGRotateTransform* transform;
790 transform = static_cast<SGRotateTransform*>(node);
791 transform->setAngleDeg(_animationValue->getValue());
796 SGSharedPtr<SGCondition const> _condition;
797 SGSharedPtr<SGDoubleValue const> _animationValue;
800 class SGRotateAnimation::SpinUpdateCallback : public osg::NodeCallback {
802 SpinUpdateCallback(SGCondition const* condition,
803 SGDoubleValue const* animationValue) :
804 _condition(condition),
805 _animationValue(animationValue),
808 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
810 if (!_condition || _condition->test()) {
811 SGRotateTransform* transform;
812 transform = static_cast<SGRotateTransform*>(node);
814 double t = nv->getFrameStamp()->getReferenceTime();
819 double velocity_rpms = _animationValue->getValue()/60;
820 double angle = transform->getAngleDeg();
821 angle += dt*velocity_rpms*360;
822 angle -= 360*floor(angle/360);
823 transform->setAngleDeg(angle);
828 SGSharedPtr<SGCondition const> _condition;
829 SGSharedPtr<SGDoubleValue const> _animationValue;
833 SGRotateAnimation::SGRotateAnimation(const SGPropertyNode* configNode, SGPropertyNode* modelRoot) :
834 SGAnimation(configNode, modelRoot)
836 std::string type = configNode->getStringValue("type", "");
837 _isSpin = (type == "spin");
839 _condition = getCondition();
840 _animationValue = read_value(configNode, modelRoot, "-deg",
841 -SGLimitsd::max(), SGLimitsd::max());
842 _initialValue = configNode->getDoubleValue("starting-position-deg", 0);
843 _initialValue *= configNode->getDoubleValue("factor", 1);
844 _initialValue += configNode->getDoubleValue("offset-deg", 0);
846 _center = SGVec3d::zeros();
847 if (configNode->hasValue("axis/x1-m")) {
849 v1[0] = configNode->getDoubleValue("axis/x1-m", 0);
850 v1[1] = configNode->getDoubleValue("axis/y1-m", 0);
851 v1[2] = configNode->getDoubleValue("axis/z1-m", 0);
852 v2[0] = configNode->getDoubleValue("axis/x2-m", 0);
853 v2[1] = configNode->getDoubleValue("axis/y2-m", 0);
854 v2[2] = configNode->getDoubleValue("axis/z2-m", 0);
855 _center = 0.5*(v1+v2);
858 _axis[0] = configNode->getDoubleValue("axis/x", 0);
859 _axis[1] = configNode->getDoubleValue("axis/y", 0);
860 _axis[2] = configNode->getDoubleValue("axis/z", 0);
862 if (8*SGLimitsd::min() < norm(_axis))
863 _axis = normalize(_axis);
865 _center[0] = configNode->getDoubleValue("center/x-m", _center[0]);
866 _center[1] = configNode->getDoubleValue("center/y-m", _center[1]);
867 _center[2] = configNode->getDoubleValue("center/z-m", _center[2]);
871 SGRotateAnimation::createAnimationGroup(osg::Group& parent)
873 SGRotateTransform* transform = new SGRotateTransform;
874 transform->setName("rotate animation");
876 SpinUpdateCallback* uc;
877 uc = new SpinUpdateCallback(_condition, _animationValue);
878 transform->setUpdateCallback(uc);
879 } else if (_animationValue) {
880 UpdateCallback* uc = new UpdateCallback(_condition, _animationValue);
881 transform->setUpdateCallback(uc);
883 transform->setCenter(_center);
884 transform->setAxis(_axis);
885 transform->setAngleDeg(_initialValue);
886 parent.addChild(transform);
891 ////////////////////////////////////////////////////////////////////////
892 // Implementation of scale animation
893 ////////////////////////////////////////////////////////////////////////
895 class SGScaleAnimation::UpdateCallback : public osg::NodeCallback {
897 UpdateCallback(const SGCondition* condition,
898 SGSharedPtr<const SGDoubleValue> animationValue[3]) :
899 _condition(condition)
901 _animationValue[0] = animationValue[0];
902 _animationValue[1] = animationValue[1];
903 _animationValue[2] = animationValue[2];
905 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
907 if (!_condition || _condition->test()) {
908 SGScaleTransform* transform;
909 transform = static_cast<SGScaleTransform*>(node);
910 SGVec3d scale(_animationValue[0]->getValue(),
911 _animationValue[1]->getValue(),
912 _animationValue[2]->getValue());
913 transform->setScaleFactor(scale);
918 SGSharedPtr<SGCondition const> _condition;
919 SGSharedPtr<SGDoubleValue const> _animationValue[3];
922 SGScaleAnimation::SGScaleAnimation(const SGPropertyNode* configNode,
923 SGPropertyNode* modelRoot) :
924 SGAnimation(configNode, modelRoot)
926 _condition = getCondition();
928 // default offset/factor for all directions
929 double offset = configNode->getDoubleValue("offset", 0);
930 double factor = configNode->getDoubleValue("factor", 1);
932 std::string inputPropertyName;
933 inputPropertyName = configNode->getStringValue("property", "");
934 SGPropertyNode* inputProperty = 0;
935 if (!inputPropertyName.empty()) {
936 inputProperty = modelRoot->getNode(inputPropertyName.c_str(), true);
938 SGInterpTable* interpTable = read_interpolation_table(configNode);
940 SGInterpTableValue* value;
941 value = new SGInterpTableValue(inputProperty, interpTable);
942 _animationValue[0] = value;
943 _animationValue[1] = value;
944 _animationValue[2] = value;
945 } else if (configNode->getBoolValue("use-personality", false)) {
946 SGPersScaleOffsetValue* value;
947 value = new SGPersScaleOffsetValue(inputProperty, configNode,
948 "x-factor", "x-offset",
950 value->setMin(configNode->getDoubleValue("x-min", 0));
951 value->setMax(configNode->getDoubleValue("x-max", SGLimitsd::max()));
952 _animationValue[0] = value;
953 value = new SGPersScaleOffsetValue(inputProperty, configNode,
954 "y-factor", "y-offset",
956 value->setMin(configNode->getDoubleValue("y-min", 0));
957 value->setMax(configNode->getDoubleValue("y-max", SGLimitsd::max()));
958 _animationValue[1] = value;
959 value = new SGPersScaleOffsetValue(inputProperty, configNode,
960 "z-factor", "z-offset",
962 value->setMin(configNode->getDoubleValue("z-min", 0));
963 value->setMax(configNode->getDoubleValue("z-max", SGLimitsd::max()));
964 _animationValue[2] = value;
966 SGScaleOffsetValue* value = new SGScaleOffsetValue(inputProperty);
967 value->setScale(configNode->getDoubleValue("x-factor", factor));
968 value->setOffset(configNode->getDoubleValue("x-offset", offset));
969 value->setMin(configNode->getDoubleValue("x-min", 0));
970 value->setMax(configNode->getDoubleValue("x-max", SGLimitsd::max()));
971 _animationValue[0] = value;
972 value = new SGScaleOffsetValue(inputProperty);
973 value->setScale(configNode->getDoubleValue("y-factor", factor));
974 value->setOffset(configNode->getDoubleValue("y-offset", offset));
975 value->setMin(configNode->getDoubleValue("y-min", 0));
976 value->setMax(configNode->getDoubleValue("y-max", SGLimitsd::max()));
977 _animationValue[1] = value;
978 value = new SGScaleOffsetValue(inputProperty);
979 value->setScale(configNode->getDoubleValue("z-factor", factor));
980 value->setOffset(configNode->getDoubleValue("z-offset", offset));
981 value->setMin(configNode->getDoubleValue("z-min", 0));
982 value->setMax(configNode->getDoubleValue("z-max", SGLimitsd::max()));
983 _animationValue[2] = value;
985 _initialValue[0] = configNode->getDoubleValue("x-starting-scale", 1);
986 _initialValue[0] *= configNode->getDoubleValue("x-factor", factor);
987 _initialValue[0] += configNode->getDoubleValue("x-offset", offset);
988 _initialValue[1] = configNode->getDoubleValue("y-starting-scale", 1);
989 _initialValue[1] *= configNode->getDoubleValue("y-factor", factor);
990 _initialValue[1] += configNode->getDoubleValue("y-offset", offset);
991 _initialValue[2] = configNode->getDoubleValue("z-starting-scale", 1);
992 _initialValue[2] *= configNode->getDoubleValue("z-factor", factor);
993 _initialValue[2] += configNode->getDoubleValue("z-offset", offset);
994 _center[0] = configNode->getDoubleValue("center/x-m", 0);
995 _center[1] = configNode->getDoubleValue("center/y-m", 0);
996 _center[2] = configNode->getDoubleValue("center/z-m", 0);
1000 SGScaleAnimation::createAnimationGroup(osg::Group& parent)
1002 SGScaleTransform* transform = new SGScaleTransform;
1003 transform->setName("scale animation");
1004 transform->setCenter(_center);
1005 transform->setScaleFactor(_initialValue);
1006 UpdateCallback* uc = new UpdateCallback(_condition, _animationValue);
1007 transform->setUpdateCallback(uc);
1008 parent.addChild(transform);
1013 // Don't create a new state state everytime we need GL_NORMALIZE!
1017 OpenThreads::Mutex normalizeMutex;
1019 osg::StateSet* getNormalizeStateSet()
1021 static osg::ref_ptr<osg::StateSet> normalizeStateSet;
1022 OpenThreads::ScopedLock<OpenThreads::Mutex> lock(normalizeMutex);
1023 if (!normalizeStateSet.valid()) {
1024 normalizeStateSet = new osg::StateSet;
1025 normalizeStateSet->setMode(GL_NORMALIZE, osg::StateAttribute::ON);
1026 normalizeStateSet->setDataVariance(osg::Object::STATIC);
1028 return normalizeStateSet.get();
1032 ////////////////////////////////////////////////////////////////////////
1033 // Implementation of dist scale animation
1034 ////////////////////////////////////////////////////////////////////////
1036 class SGDistScaleAnimation::Transform : public osg::Transform {
1038 Transform(const SGPropertyNode* configNode)
1040 setName(configNode->getStringValue("name", "dist scale animation"));
1041 setReferenceFrame(RELATIVE_RF);
1042 setStateSet(getNormalizeStateSet());
1043 _factor = configNode->getFloatValue("factor", 1);
1044 _offset = configNode->getFloatValue("offset", 0);
1045 _min_v = configNode->getFloatValue("min", SGLimitsf::epsilon());
1046 _max_v = configNode->getFloatValue("max", SGLimitsf::max());
1047 _table = read_interpolation_table(configNode);
1048 _center[0] = configNode->getFloatValue("center/x-m", 0);
1049 _center[1] = configNode->getFloatValue("center/y-m", 0);
1050 _center[2] = configNode->getFloatValue("center/z-m", 0);
1052 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
1053 osg::NodeVisitor* nv) const
1055 osg::Matrix transform;
1056 double scale_factor = computeScaleFactor(nv);
1057 transform(0,0) = scale_factor;
1058 transform(1,1) = scale_factor;
1059 transform(2,2) = scale_factor;
1060 transform(3,0) = _center[0]*(1 - scale_factor);
1061 transform(3,1) = _center[1]*(1 - scale_factor);
1062 transform(3,2) = _center[2]*(1 - scale_factor);
1063 matrix.preMult(transform);
1067 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
1068 osg::NodeVisitor* nv) const
1070 double scale_factor = computeScaleFactor(nv);
1071 if (fabs(scale_factor) <= SGLimits<double>::min())
1073 osg::Matrix transform;
1074 double rScaleFactor = 1/scale_factor;
1075 transform(0,0) = rScaleFactor;
1076 transform(1,1) = rScaleFactor;
1077 transform(2,2) = rScaleFactor;
1078 transform(3,0) = _center[0]*(1 - rScaleFactor);
1079 transform(3,1) = _center[1]*(1 - rScaleFactor);
1080 transform(3,2) = _center[2]*(1 - rScaleFactor);
1081 matrix.postMult(transform);
1086 double computeScaleFactor(osg::NodeVisitor* nv) const
1091 double scale_factor = (_center.osg() - nv->getEyePoint()).length();
1093 scale_factor = _factor * scale_factor + _offset;
1095 scale_factor = _table->interpolate( scale_factor );
1097 if (scale_factor < _min_v)
1098 scale_factor = _min_v;
1099 if (scale_factor > _max_v)
1100 scale_factor = _max_v;
1102 return scale_factor;
1105 SGSharedPtr<SGInterpTable> _table;
1114 SGDistScaleAnimation::SGDistScaleAnimation(const SGPropertyNode* configNode,
1115 SGPropertyNode* modelRoot) :
1116 SGAnimation(configNode, modelRoot)
1121 SGDistScaleAnimation::createAnimationGroup(osg::Group& parent)
1123 Transform* transform = new Transform(getConfig());
1124 parent.addChild(transform);
1129 ////////////////////////////////////////////////////////////////////////
1130 // Implementation of flash animation
1131 ////////////////////////////////////////////////////////////////////////
1133 class SGFlashAnimation::Transform : public osg::Transform {
1135 Transform(const SGPropertyNode* configNode)
1137 setReferenceFrame(RELATIVE_RF);
1138 setName(configNode->getStringValue("name", "flash animation"));
1139 setStateSet(getNormalizeStateSet());
1141 _axis[0] = configNode->getFloatValue("axis/x", 0);
1142 _axis[1] = configNode->getFloatValue("axis/y", 0);
1143 _axis[2] = configNode->getFloatValue("axis/z", 1);
1146 _center[0] = configNode->getFloatValue("center/x-m", 0);
1147 _center[1] = configNode->getFloatValue("center/y-m", 0);
1148 _center[2] = configNode->getFloatValue("center/z-m", 0);
1150 _offset = configNode->getFloatValue("offset", 0);
1151 _factor = configNode->getFloatValue("factor", 1);
1152 _power = configNode->getFloatValue("power", 1);
1153 _two_sides = configNode->getBoolValue("two-sides", false);
1155 _min_v = configNode->getFloatValue("min", SGLimitsf::epsilon());
1156 _max_v = configNode->getFloatValue("max", 1);
1158 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
1159 osg::NodeVisitor* nv) const
1161 osg::Matrix transform;
1162 double scale_factor = computeScaleFactor(nv);
1163 transform(0,0) = scale_factor;
1164 transform(1,1) = scale_factor;
1165 transform(2,2) = scale_factor;
1166 transform(3,0) = _center[0]*(1 - scale_factor);
1167 transform(3,1) = _center[1]*(1 - scale_factor);
1168 transform(3,2) = _center[2]*(1 - scale_factor);
1169 matrix.preMult(transform);
1173 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
1174 osg::NodeVisitor* nv) const
1176 double scale_factor = computeScaleFactor(nv);
1177 if (fabs(scale_factor) <= SGLimits<double>::min())
1179 osg::Matrix transform;
1180 double rScaleFactor = 1/scale_factor;
1181 transform(0,0) = rScaleFactor;
1182 transform(1,1) = rScaleFactor;
1183 transform(2,2) = rScaleFactor;
1184 transform(3,0) = _center[0]*(1 - rScaleFactor);
1185 transform(3,1) = _center[1]*(1 - rScaleFactor);
1186 transform(3,2) = _center[2]*(1 - rScaleFactor);
1187 matrix.postMult(transform);
1192 double computeScaleFactor(osg::NodeVisitor* nv) const
1197 osg::Vec3 localEyeToCenter = nv->getEyePoint() - _center;
1198 localEyeToCenter.normalize();
1200 double cos_angle = localEyeToCenter*_axis;
1201 double scale_factor = 0;
1202 if ( _two_sides && cos_angle < 0 )
1203 scale_factor = _factor * pow( -cos_angle, _power ) + _offset;
1204 else if ( cos_angle > 0 )
1205 scale_factor = _factor * pow( cos_angle, _power ) + _offset;
1207 if ( scale_factor < _min_v )
1208 scale_factor = _min_v;
1209 if ( scale_factor > _max_v )
1210 scale_factor = _max_v;
1212 return scale_factor;
1215 virtual osg::BoundingSphere computeBound() const
1217 // avoid being culled away by small feature culling
1218 osg::BoundingSphere bs = osg::Group::computeBound();
1219 bs.radius() *= _max_v;
1226 double _power, _factor, _offset, _min_v, _max_v;
1231 SGFlashAnimation::SGFlashAnimation(const SGPropertyNode* configNode,
1232 SGPropertyNode* modelRoot) :
1233 SGAnimation(configNode, modelRoot)
1238 SGFlashAnimation::createAnimationGroup(osg::Group& parent)
1240 Transform* transform = new Transform(getConfig());
1241 parent.addChild(transform);
1246 ////////////////////////////////////////////////////////////////////////
1247 // Implementation of flash animation
1248 ////////////////////////////////////////////////////////////////////////
1250 class SGBillboardAnimation::Transform : public osg::Transform {
1252 Transform(const SGPropertyNode* configNode) :
1253 _spherical(configNode->getBoolValue("spherical", true))
1255 setReferenceFrame(RELATIVE_RF);
1256 setName(configNode->getStringValue("name", "billboard animation"));
1258 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
1259 osg::NodeVisitor* nv) const
1261 // More or less taken from plibs ssgCutout
1263 matrix(0,0) = 1; matrix(0,1) = 0; matrix(0,2) = 0;
1264 matrix(1,0) = 0; matrix(1,1) = 0; matrix(1,2) = -1;
1265 matrix(2,0) = 0; matrix(2,1) = 1; matrix(2,2) = 0;
1267 osg::Vec3 zAxis(matrix(2, 0), matrix(2, 1), matrix(2, 2));
1268 osg::Vec3 xAxis = osg::Vec3(0, 0, -1)^zAxis;
1269 osg::Vec3 yAxis = zAxis^xAxis;
1275 matrix(0,0) = xAxis[0]; matrix(0,1) = xAxis[1]; matrix(0,2) = xAxis[2];
1276 matrix(1,0) = yAxis[0]; matrix(1,1) = yAxis[1]; matrix(1,2) = yAxis[2];
1277 matrix(2,0) = zAxis[0]; matrix(2,1) = zAxis[1]; matrix(2,2) = zAxis[2];
1282 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
1283 osg::NodeVisitor* nv) const
1285 // Hmm, don't yet know how to get that back ...
1294 SGBillboardAnimation::SGBillboardAnimation(const SGPropertyNode* configNode,
1295 SGPropertyNode* modelRoot) :
1296 SGAnimation(configNode, modelRoot)
1301 SGBillboardAnimation::createAnimationGroup(osg::Group& parent)
1303 Transform* transform = new Transform(getConfig());
1304 parent.addChild(transform);
1309 ////////////////////////////////////////////////////////////////////////
1310 // Implementation of a range animation
1311 ////////////////////////////////////////////////////////////////////////
1313 class SGRangeAnimation::UpdateCallback : public osg::NodeCallback {
1315 UpdateCallback(const SGCondition* condition,
1316 const SGDoubleValue* minAnimationValue,
1317 const SGDoubleValue* maxAnimationValue,
1318 double minValue, double maxValue) :
1319 _condition(condition),
1320 _minAnimationValue(minAnimationValue),
1321 _maxAnimationValue(maxAnimationValue),
1322 _minStaticValue(minValue),
1323 _maxStaticValue(maxValue)
1325 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1327 osg::LOD* lod = static_cast<osg::LOD*>(node);
1328 if (!_condition || _condition->test()) {
1330 if (_minAnimationValue)
1331 minRange = _minAnimationValue->getValue();
1333 minRange = _minStaticValue;
1335 if (_maxAnimationValue)
1336 maxRange = _maxAnimationValue->getValue();
1338 maxRange = _maxStaticValue;
1339 lod->setRange(0, minRange, maxRange);
1341 lod->setRange(0, 0, SGLimitsf::max());
1347 SGSharedPtr<const SGCondition> _condition;
1348 SGSharedPtr<const SGDoubleValue> _minAnimationValue;
1349 SGSharedPtr<const SGDoubleValue> _maxAnimationValue;
1350 double _minStaticValue;
1351 double _maxStaticValue;
1354 SGRangeAnimation::SGRangeAnimation(const SGPropertyNode* configNode,
1355 SGPropertyNode* modelRoot) :
1356 SGAnimation(configNode, modelRoot)
1358 _condition = getCondition();
1360 std::string inputPropertyName;
1361 inputPropertyName = configNode->getStringValue("min-property", "");
1362 if (!inputPropertyName.empty()) {
1363 SGPropertyNode* inputProperty;
1364 inputProperty = modelRoot->getNode(inputPropertyName.c_str(), true);
1365 SGScaleOffsetValue* value = new SGScaleOffsetValue(inputProperty);
1366 value->setScale(configNode->getDoubleValue("min-factor", 1));
1367 _minAnimationValue = value;
1369 inputPropertyName = configNode->getStringValue("max-property", "");
1370 if (!inputPropertyName.empty()) {
1371 SGPropertyNode* inputProperty;
1372 inputProperty = modelRoot->getNode(inputPropertyName.c_str(), true);
1373 SGScaleOffsetValue* value = new SGScaleOffsetValue(inputProperty);
1374 value->setScale(configNode->getDoubleValue("max-factor", 1));
1375 _maxAnimationValue = value;
1378 _initialValue[0] = configNode->getDoubleValue("min-m", 0);
1379 _initialValue[0] *= configNode->getDoubleValue("min-factor", 1);
1380 _initialValue[1] = configNode->getDoubleValue("max-m", SGLimitsf::max());
1381 _initialValue[1] *= configNode->getDoubleValue("max-factor", 1);
1385 SGRangeAnimation::createAnimationGroup(osg::Group& parent)
1387 osg::Group* group = new osg::Group;
1388 group->setName("range animation group");
1390 osg::LOD* lod = new osg::LOD;
1391 lod->setName("range animation node");
1392 parent.addChild(lod);
1394 lod->addChild(group, _initialValue[0], _initialValue[1]);
1395 lod->setCenterMode(osg::LOD::USE_BOUNDING_SPHERE_CENTER);
1396 lod->setRangeMode(osg::LOD::DISTANCE_FROM_EYE_POINT);
1397 if (_minAnimationValue || _maxAnimationValue || _condition) {
1399 uc = new UpdateCallback(_condition, _minAnimationValue, _maxAnimationValue,
1400 _initialValue[0], _initialValue[1]);
1401 lod->setUpdateCallback(uc);
1407 ////////////////////////////////////////////////////////////////////////
1408 // Implementation of a select animation
1409 ////////////////////////////////////////////////////////////////////////
1411 class SGSelectAnimation::UpdateCallback : public osg::NodeCallback {
1413 UpdateCallback(const SGCondition* condition) :
1414 _condition(condition)
1416 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1418 osg::Switch* sw = static_cast<osg::Switch*>(node);
1419 if (_condition->test())
1420 sw->setAllChildrenOn();
1422 sw->setAllChildrenOff();
1427 SGSharedPtr<SGCondition const> _condition;
1430 SGSelectAnimation::SGSelectAnimation(const SGPropertyNode* configNode,
1431 SGPropertyNode* modelRoot) :
1432 SGAnimation(configNode, modelRoot)
1437 SGSelectAnimation::createAnimationGroup(osg::Group& parent)
1439 // if no condition given, this is a noop.
1440 SGSharedPtr<SGCondition const> condition = getCondition();
1441 // trick, gets deleted with all its 'animated' children
1442 // when the animation installer returns
1444 return new osg::Group;
1446 osg::Switch* sw = new osg::Switch;
1447 sw->setName("select animation node");
1448 sw->setUpdateCallback(new UpdateCallback(condition));
1449 parent.addChild(sw);
1455 ////////////////////////////////////////////////////////////////////////
1456 // Implementation of alpha test animation
1457 ////////////////////////////////////////////////////////////////////////
1459 SGAlphaTestAnimation::SGAlphaTestAnimation(const SGPropertyNode* configNode,
1460 SGPropertyNode* modelRoot) :
1461 SGAnimation(configNode, modelRoot)
1466 SGAlphaTestAnimation::install(osg::Node& node)
1468 SGAnimation::install(node);
1470 cloneDrawables(node);
1471 removeMode(node, GL_ALPHA_TEST);
1472 removeAttribute(node, osg::StateAttribute::ALPHAFUNC);
1474 osg::StateSet* stateSet = node.getOrCreateStateSet();
1475 osg::AlphaFunc* alphaFunc = new osg::AlphaFunc;
1476 alphaFunc->setFunction(osg::AlphaFunc::GREATER);
1477 float alphaClamp = getConfig()->getFloatValue("alpha-factor", 0);
1478 alphaFunc->setReferenceValue(alphaClamp);
1479 stateSet->setAttribute(alphaFunc);
1480 stateSet->setMode(GL_ALPHA_TEST, osg::StateAttribute::ON);
1485 //////////////////////////////////////////////////////////////////////
1486 // Blend animation installer
1487 //////////////////////////////////////////////////////////////////////
1489 class SGBlendAnimation::BlendVisitor : public osg::NodeVisitor {
1491 BlendVisitor(float blend) :
1492 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
1494 { setVisitorType(osg::NodeVisitor::NODE_VISITOR); }
1495 virtual void apply(osg::Node& node)
1497 updateStateSet(node.getStateSet());
1500 virtual void apply(osg::Geode& node)
1502 apply((osg::Node&)node);
1503 unsigned nDrawables = node.getNumDrawables();
1504 for (unsigned i = 0; i < nDrawables; ++i) {
1505 osg::Drawable* drawable = node.getDrawable(i);
1506 updateStateSet(drawable->getStateSet());
1507 osg::Geometry* geometry = drawable->asGeometry();
1510 osg::Array* array = geometry->getColorArray();
1513 osg::Vec4Array* vec4Array = dynamic_cast<osg::Vec4Array*>(array);
1516 geometry->dirtyDisplayList();
1518 for (unsigned k = 0; k < vec4Array->size(); ++k) {
1519 (*vec4Array)[k][3] = _blend;
1523 void updateStateSet(osg::StateSet* stateSet)
1527 osg::StateAttribute* stateAttribute;
1528 stateAttribute = stateSet->getAttribute(osg::StateAttribute::MATERIAL);
1529 if (!stateAttribute)
1531 osg::Material* material = dynamic_cast<osg::Material*>(stateAttribute);
1534 material->setAlpha(osg::Material::FRONT_AND_BACK, _blend);
1536 stateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
1537 stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
1539 stateSet->setRenderingHint(osg::StateSet::DEFAULT_BIN);
1546 class SGBlendAnimation::UpdateCallback : public osg::NodeCallback {
1548 UpdateCallback(const SGPropertyNode* configNode, const SGDoubleValue* v) :
1552 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1554 double blend = _animationValue->getValue();
1555 if (blend != _prev_value) {
1556 _prev_value = blend;
1557 BlendVisitor visitor(1-blend);
1558 node->accept(visitor);
1564 SGSharedPtr<SGDoubleValue const> _animationValue;
1568 SGBlendAnimation::SGBlendAnimation(const SGPropertyNode* configNode,
1569 SGPropertyNode* modelRoot)
1570 : SGAnimation(configNode, modelRoot),
1571 _animationValue(read_value(configNode, modelRoot, "", 0, 1))
1576 SGBlendAnimation::createAnimationGroup(osg::Group& parent)
1578 if (!_animationValue)
1581 osg::Group* group = new osg::Switch;
1582 group->setName("blend animation node");
1583 group->setUpdateCallback(new UpdateCallback(getConfig(), _animationValue));
1584 parent.addChild(group);
1589 SGBlendAnimation::install(osg::Node& node)
1591 SGAnimation::install(node);
1592 // make sure we do not change common geometries,
1593 // that also creates new display lists for these subgeometries.
1594 cloneDrawables(node);
1598 //////////////////////////////////////////////////////////////////////
1599 // Timed animation installer
1600 //////////////////////////////////////////////////////////////////////
1604 class SGTimedAnimation::UpdateCallback : public osg::NodeCallback {
1606 UpdateCallback(const SGPropertyNode* configNode) :
1609 _duration_sec(configNode->getDoubleValue("duration-sec", 1)),
1610 _last_time_sec(SGLimitsd::max()),
1611 _use_personality(configNode->getBoolValue("use-personality", false))
1613 std::vector<SGSharedPtr<SGPropertyNode> > nodes;
1614 nodes = configNode->getChildren("branch-duration-sec");
1615 for (size_t i = 0; i < nodes.size(); ++i) {
1616 unsigned ind = nodes[ i ]->getIndex();
1617 while ( ind >= _durations.size() ) {
1618 _durations.push_back(DurationSpec(_duration_sec));
1620 SGPropertyNode_ptr rNode = nodes[i]->getChild("random");
1622 _durations[ind] = DurationSpec(nodes[ i ]->getDoubleValue());
1624 _durations[ind] = DurationSpec(rNode->getDoubleValue( "min", 0),
1625 rNode->getDoubleValue( "max", 1));
1629 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1631 assert(dynamic_cast<osg::Switch*>(node));
1632 osg::Switch* sw = static_cast<osg::Switch*>(node);
1634 unsigned nChildren = sw->getNumChildren();
1636 // blow up the durations vector to the required size
1637 while (_durations.size() < nChildren) {
1638 _durations.push_back(_duration_sec);
1640 // make sure the current index is an duration that really exists
1641 _current_index = _current_index % nChildren;
1643 // update the time and compute the current systems time value
1644 double t = nv->getFrameStamp()->getReferenceTime();
1645 if (_last_time_sec == SGLimitsd::max()) {
1648 double dt = t - _last_time_sec;
1649 if (_use_personality)
1650 dt *= 1 + 0.2*(0.5 - sg_random());
1655 double currentDuration = _durations[_current_index].get();
1656 while (currentDuration < _reminder) {
1657 _reminder -= currentDuration;
1658 _current_index = (_current_index + 1) % nChildren;
1659 currentDuration = _durations[_current_index].get();
1662 sw->setSingleChildOn(_current_index);
1668 struct DurationSpec {
1669 DurationSpec(double t) :
1670 minTime(SGMiscd::max(0.01, t)),
1671 maxTime(SGMiscd::max(0.01, t))
1673 DurationSpec(double t0, double t1) :
1674 minTime(SGMiscd::max(0.01, t0)),
1675 maxTime(SGMiscd::max(0.01, t1))
1678 { return minTime + sg_random()*(maxTime - minTime); }
1682 std::vector<DurationSpec> _durations;
1683 unsigned _current_index;
1685 double _duration_sec;
1686 double _last_time_sec;
1687 bool _use_personality;
1691 SGTimedAnimation::SGTimedAnimation(const SGPropertyNode* configNode,
1692 SGPropertyNode* modelRoot)
1693 : SGAnimation(configNode, modelRoot)
1698 SGTimedAnimation::createAnimationGroup(osg::Group& parent)
1700 osg::Switch* sw = new osg::Switch;
1701 sw->setName("timed animation node");
1702 sw->setUpdateCallback(new UpdateCallback(getConfig()));
1703 parent.addChild(sw);
1708 ////////////////////////////////////////////////////////////////////////
1709 // dynamically switch on/off shadows
1710 ////////////////////////////////////////////////////////////////////////
1712 class SGShadowAnimation::UpdateCallback : public osg::NodeCallback {
1714 UpdateCallback(const SGCondition* condition) :
1715 _condition(condition)
1717 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1719 if (_condition->test())
1720 node->setNodeMask( SG_NODEMASK_CASTSHADOW_BIT | node->getNodeMask());
1722 node->setNodeMask(~SG_NODEMASK_CASTSHADOW_BIT & node->getNodeMask());
1727 SGSharedPtr<const SGCondition> _condition;
1730 SGShadowAnimation::SGShadowAnimation(const SGPropertyNode* configNode,
1731 SGPropertyNode* modelRoot) :
1732 SGAnimation(configNode, modelRoot)
1737 SGShadowAnimation::createAnimationGroup(osg::Group& parent)
1739 SGSharedPtr<SGCondition const> condition = getCondition();
1743 osg::Group* group = new osg::Group;
1744 group->setName("shadow animation");
1745 group->setUpdateCallback(new UpdateCallback(condition));
1746 parent.addChild(group);
1751 ////////////////////////////////////////////////////////////////////////
1752 // Implementation of SGTexTransformAnimation
1753 ////////////////////////////////////////////////////////////////////////
1755 class SGTexTransformAnimation::Transform : public SGReferenced {
1760 virtual ~Transform()
1762 void setValue(double value)
1764 virtual void transform(osg::Matrix&) = 0;
1769 class SGTexTransformAnimation::Translation :
1770 public SGTexTransformAnimation::Transform {
1772 Translation(const SGVec3d& axis) :
1775 void setValue(double value)
1777 virtual void transform(osg::Matrix& matrix)
1780 set_translation(tmp, _value, _axis);
1781 matrix.preMult(tmp);
1787 class SGTexTransformAnimation::Rotation :
1788 public SGTexTransformAnimation::Transform {
1790 Rotation(const SGVec3d& axis, const SGVec3d& center) :
1794 virtual void transform(osg::Matrix& matrix)
1797 set_rotation(tmp, _value, _center, _axis);
1798 matrix.preMult(tmp);
1805 class SGTexTransformAnimation::UpdateCallback :
1806 public osg::StateAttribute::Callback {
1808 UpdateCallback(const SGCondition* condition) :
1809 _condition(condition)
1811 virtual void operator () (osg::StateAttribute* sa, osg::NodeVisitor*)
1813 if (!_condition || _condition->test()) {
1814 TransformList::const_iterator i;
1815 for (i = _transforms.begin(); i != _transforms.end(); ++i)
1816 i->transform->setValue(i->value->getValue());
1818 assert(dynamic_cast<osg::TexMat*>(sa));
1819 osg::TexMat* texMat = static_cast<osg::TexMat*>(sa);
1820 texMat->getMatrix().makeIdentity();
1821 TransformList::const_iterator i;
1822 for (i = _transforms.begin(); i != _transforms.end(); ++i)
1823 i->transform->transform(texMat->getMatrix());
1825 void appendTransform(Transform* transform, SGDoubleValue* value)
1827 Entry entry = { transform, value };
1828 transform->transform(_matrix);
1829 _transforms.push_back(entry);
1834 SGSharedPtr<Transform> transform;
1835 SGSharedPtr<const SGDoubleValue> value;
1837 typedef std::vector<Entry> TransformList;
1838 TransformList _transforms;
1839 SGSharedPtr<const SGCondition> _condition;
1840 osg::Matrix _matrix;
1843 SGTexTransformAnimation::SGTexTransformAnimation(const SGPropertyNode* configNode,
1844 SGPropertyNode* modelRoot) :
1845 SGAnimation(configNode, modelRoot)
1850 SGTexTransformAnimation::createAnimationGroup(osg::Group& parent)
1852 osg::Group* group = new osg::Group;
1853 group->setName("texture transform group");
1854 osg::StateSet* stateSet = group->getOrCreateStateSet();
1855 osg::TexMat* texMat = new osg::TexMat;
1856 UpdateCallback* updateCallback = new UpdateCallback(getCondition());
1857 // interpret the configs ...
1858 std::string type = getType();
1860 if (type == "textranslate") {
1861 appendTexTranslate(getConfig(), updateCallback);
1862 } else if (type == "texrotate") {
1863 appendTexRotate(getConfig(), updateCallback);
1864 } else if (type == "texmultiple") {
1865 std::vector<SGSharedPtr<SGPropertyNode> > transformConfigs;
1866 transformConfigs = getConfig()->getChildren("transform");
1867 for (unsigned i = 0; i < transformConfigs.size(); ++i) {
1868 std::string subtype = transformConfigs[i]->getStringValue("subtype", "");
1869 if (subtype == "textranslate")
1870 appendTexTranslate(transformConfigs[i], updateCallback);
1871 else if (subtype == "texrotate")
1872 appendTexRotate(transformConfigs[i], updateCallback);
1874 SG_LOG(SG_INPUT, SG_ALERT,
1875 "Ignoring unknown texture transform subtype");
1878 SG_LOG(SG_INPUT, SG_ALERT, "Ignoring unknown texture transform type");
1881 texMat->setUpdateCallback(updateCallback);
1882 stateSet->setTextureAttribute(0, texMat);
1883 parent.addChild(group);
1888 SGTexTransformAnimation::appendTexTranslate(const SGPropertyNode* config,
1889 UpdateCallback* updateCallback)
1891 std::string propertyName = config->getStringValue("property", "/null");
1892 SGPropertyNode* inputNode;
1893 inputNode = getModelRoot()->getNode(propertyName.c_str(), true);
1895 SGDoubleValue* animationValue;
1896 SGInterpTable* table = read_interpolation_table(config);
1898 SGTexTableValue* value;
1899 value = new SGTexTableValue(inputNode, table);
1900 value->setStep(config->getDoubleValue("step", 0));
1901 value->setScroll(config->getDoubleValue("scroll", 0));
1902 value->setBias(config->getDoubleValue("bias", 0));
1903 animationValue = value;
1905 SGTexScaleOffsetValue* value;
1906 value = new SGTexScaleOffsetValue(inputNode);
1907 value->setScale(config->getDoubleValue("factor", 1));
1908 value->setOffset(config->getDoubleValue("offset", 0));
1909 value->setStep(config->getDoubleValue("step", 0));
1910 value->setScroll(config->getDoubleValue("scroll", 0));
1911 value->setBias(config->getDoubleValue("bias", 0));
1912 value->setMin(config->getDoubleValue("min", -SGLimitsd::max()));
1913 value->setMax(config->getDoubleValue("max", SGLimitsd::max()));
1914 animationValue = value;
1916 SGVec3d axis(config->getDoubleValue("axis/x", 0),
1917 config->getDoubleValue("axis/y", 0),
1918 config->getDoubleValue("axis/z", 0));
1919 Translation* translation;
1920 translation = new Translation(normalize(axis));
1921 translation->setValue(config->getDoubleValue("starting-position", 0));
1922 updateCallback->appendTransform(translation, animationValue);
1926 SGTexTransformAnimation::appendTexRotate(const SGPropertyNode* config,
1927 UpdateCallback* updateCallback)
1929 std::string propertyName = config->getStringValue("property", "/null");
1930 SGPropertyNode* inputNode;
1931 inputNode = getModelRoot()->getNode(propertyName.c_str(), true);
1933 SGDoubleValue* animationValue;
1934 SGInterpTable* table = read_interpolation_table(config);
1936 SGTexTableValue* value;
1937 value = new SGTexTableValue(inputNode, table);
1938 value->setStep(config->getDoubleValue("step", 0));
1939 value->setScroll(config->getDoubleValue("scroll", 0));
1940 value->setBias(config->getDoubleValue("bias", 0));
1941 animationValue = value;
1943 SGTexScaleOffsetValue* value;
1944 value = new SGTexScaleOffsetValue(inputNode);
1945 value->setScale(config->getDoubleValue("factor", 1));
1946 value->setOffset(config->getDoubleValue("offset-deg", 0));
1947 value->setStep(config->getDoubleValue("step", 0));
1948 value->setScroll(config->getDoubleValue("scroll", 0));
1949 value->setBias(config->getDoubleValue("bias", 0));
1950 value->setMin(config->getDoubleValue("min-deg", -SGLimitsd::max()));
1951 value->setMax(config->getDoubleValue("max-deg", SGLimitsd::max()));
1952 animationValue = value;
1954 SGVec3d axis(config->getDoubleValue("axis/x", 0),
1955 config->getDoubleValue("axis/y", 0),
1956 config->getDoubleValue("axis/z", 0));
1957 SGVec3d center(config->getDoubleValue("center/x", 0),
1958 config->getDoubleValue("center/y", 0),
1959 config->getDoubleValue("center/z", 0));
1961 rotation = new Rotation(normalize(axis), center);
1962 rotation->setValue(config->getDoubleValue("starting-position-deg", 0));
1963 updateCallback->appendTransform(rotation, animationValue);
1967 ////////////////////////////////////////////////////////////////////////
1968 // Implementation of SGPickAnimation
1969 ////////////////////////////////////////////////////////////////////////
1971 class SGPickAnimation::PickCallback : public SGPickCallback {
1973 PickCallback(const SGPropertyNode* configNode,
1974 SGPropertyNode* modelRoot) :
1975 _button(configNode->getIntValue("button", -1)),
1976 _repeatable(configNode->getBoolValue("repeatable", false)),
1977 _repeatInterval(configNode->getDoubleValue("interval-sec", 0.1))
1979 SG_LOG(SG_INPUT, SG_DEBUG, "Reading all bindings");
1980 std::vector<SGPropertyNode_ptr> bindings;
1981 bindings = configNode->getChildren("binding");
1982 for (unsigned int i = 0; i < bindings.size(); ++i) {
1983 _bindingsDown.push_back(new SGBinding(bindings[i], modelRoot));
1986 const SGPropertyNode* upNode = configNode->getChild("mod-up");
1989 bindings = upNode->getChildren("binding");
1990 for (unsigned int i = 0; i < bindings.size(); ++i) {
1991 _bindingsUp.push_back(new SGBinding(bindings[i], modelRoot));
1994 virtual bool buttonPressed(int button, const Info&)
1996 if (0 <= _button && button != _button)
1998 SGBindingList::const_iterator i;
1999 for (i = _bindingsDown.begin(); i != _bindingsDown.end(); ++i)
2004 virtual void buttonReleased(void)
2006 SGBindingList::const_iterator i;
2007 for (i = _bindingsUp.begin(); i != _bindingsUp.end(); ++i)
2010 virtual void update(double dt)
2016 while (_repeatInterval < _repeatTime) {
2017 _repeatTime -= _repeatInterval;
2018 SGBindingList::const_iterator i;
2019 for (i = _bindingsDown.begin(); i != _bindingsDown.end(); ++i)
2024 SGBindingList _bindingsDown;
2025 SGBindingList _bindingsUp;
2028 double _repeatInterval;
2032 SGPickAnimation::SGPickAnimation(const SGPropertyNode* configNode,
2033 SGPropertyNode* modelRoot) :
2034 SGAnimation(configNode, modelRoot)
2039 SGPickAnimation::createAnimationGroup(osg::Group& parent)
2041 osg::Group* commonGroup = new osg::Group;
2043 // Contains the normal geometry that is interactive
2044 osg::ref_ptr<osg::Group> normalGroup = new osg::Group;
2045 normalGroup->addChild(commonGroup);
2047 // Used to render the geometry with just yellow edges
2048 osg::Group* highlightGroup = new osg::Group;
2049 highlightGroup->setNodeMask(SG_NODEMASK_PICK_BIT);
2050 highlightGroup->addChild(commonGroup);
2051 SGSceneUserData* ud;
2052 ud = SGSceneUserData::getOrCreateSceneUserData(highlightGroup);
2053 std::vector<SGPropertyNode_ptr> actions;
2054 actions = getConfig()->getChildren("action");
2055 for (unsigned int i = 0; i < actions.size(); ++i)
2056 ud->addPickCallback(new PickCallback(actions[i], getModelRoot()));
2058 // prepare a state set that paints the edges of this object yellow
2059 osg::StateSet* stateSet = highlightGroup->getOrCreateStateSet();
2060 stateSet->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE);
2062 osg::PolygonOffset* polygonOffset = new osg::PolygonOffset;
2063 polygonOffset->setFactor(-1);
2064 polygonOffset->setUnits(-1);
2065 stateSet->setAttribute(polygonOffset, osg::StateAttribute::OVERRIDE);
2066 stateSet->setMode(GL_POLYGON_OFFSET_LINE, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
2068 osg::PolygonMode* polygonMode = new osg::PolygonMode;
2069 polygonMode->setMode(osg::PolygonMode::FRONT_AND_BACK,
2070 osg::PolygonMode::LINE);
2071 stateSet->setAttribute(polygonMode, osg::StateAttribute::OVERRIDE);
2073 osg::Material* material = new osg::Material;
2074 material->setColorMode(osg::Material::OFF);
2075 material->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(1, 1, 0, 1));
2076 material->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(1, 1, 0, 1));
2077 material->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(1, 1, 0, 1));
2078 material->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0, 0, 0, 0));
2079 stateSet->setAttribute(material, osg::StateAttribute::OVERRIDE);
2081 // Only add normal geometry if configured
2082 if (getConfig()->getBoolValue("visible", true))
2083 parent.addChild(normalGroup.get());
2084 parent.addChild(highlightGroup);