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 "SGTranslateTransform.hxx"
40 #include "SGMaterialAnimation.hxx"
41 #include "SGRotateTransform.hxx"
42 #include "SGScaleTransform.hxx"
45 ////////////////////////////////////////////////////////////////////////
46 // Static utility functions.
47 ////////////////////////////////////////////////////////////////////////
50 * Set up the transform matrix for a spin or rotation.
53 set_rotation (osg::Matrix &matrix, double position_deg,
54 const SGVec3d ¢er, const SGVec3d &axis)
56 double temp_angle = -SGMiscd::deg2rad(position_deg);
58 double s = sin(temp_angle);
59 double c = cos(temp_angle);
62 // axis was normalized at load time
63 // hint to the compiler to put these into FP registers
68 matrix(0, 0) = t * x * x + c ;
69 matrix(0, 1) = t * y * x - s * z ;
70 matrix(0, 2) = t * z * x + s * y ;
73 matrix(1, 0) = t * x * y + s * z ;
74 matrix(1, 1) = t * y * y + c ;
75 matrix(1, 2) = t * z * y - s * x ;
78 matrix(2, 0) = t * x * z - s * y ;
79 matrix(2, 1) = t * y * z + s * x ;
80 matrix(2, 2) = t * z * z + c ;
83 // hint to the compiler to put these into FP registers
88 matrix(3, 0) = x - x*matrix(0, 0) - y*matrix(1, 0) - z*matrix(2, 0);
89 matrix(3, 1) = y - x*matrix(0, 1) - y*matrix(1, 1) - z*matrix(2, 1);
90 matrix(3, 2) = z - x*matrix(0, 2) - y*matrix(1, 2) - z*matrix(2, 2);
95 * Set up the transform matrix for a translation.
98 set_translation (osg::Matrix &matrix, double position_m, const SGVec3d &axis)
100 SGVec3d xyz = axis * position_m;
101 matrix.makeIdentity();
102 matrix(3, 0) = xyz[0];
103 matrix(3, 1) = xyz[1];
104 matrix(3, 2) = xyz[2];
108 * Modify property value by step and scroll settings in texture translations
111 apply_mods(double property, double step, double scroll, double bias)
117 double scrollval = 0.0;
119 // calculate scroll amount (for odometer like movement)
120 double remainder = step - fmod(fabs(property), step);
121 if (remainder < scroll) {
122 scrollval = (scroll - remainder) / scroll * step;
125 // apply stepping of input value
127 modprop = ((floor(property/step) * step) + scrollval);
129 modprop = ((ceil(property/step) * step) + scrollval);
138 * Read an interpolation table from properties.
140 static SGInterpTable *
141 read_interpolation_table(const SGPropertyNode* props)
143 const SGPropertyNode* table_node = props->getNode("interpolation");
146 return new SGInterpTable(table_node);
149 ////////////////////////////////////////////////////////////////////////
150 // Utility value classes
151 ////////////////////////////////////////////////////////////////////////
152 class SGScaleOffsetValue : public SGDoubleValue {
154 SGScaleOffsetValue(SGPropertyNode const* propertyNode) :
155 _propertyNode(propertyNode),
158 _min(-SGLimitsd::max()),
159 _max(SGLimitsd::max())
161 void setScale(double scale)
163 void setOffset(double offset)
164 { _offset = offset; }
165 void setMin(double min)
167 void setMax(double max)
170 virtual double getValue() const
172 double value = _propertyNode ? _propertyNode->getDoubleValue() : 0;
173 return std::min(_max, std::max(_min, _offset + _scale*value));
176 SGSharedPtr<SGPropertyNode const> _propertyNode;
183 class SGPersScaleOffsetValue : public SGDoubleValue {
185 SGPersScaleOffsetValue(SGPropertyNode const* propertyNode,
186 SGPropertyNode const* config,
187 const char* scalename, const char* offsetname,
188 double defScale = 1, double defOffset = 0) :
189 _propertyNode(propertyNode),
190 _scale(config, scalename, defScale),
191 _offset(config, offsetname, defOffset),
192 _min(-SGLimitsd::max()),
193 _max(SGLimitsd::max())
195 void setScale(double scale)
197 void setOffset(double offset)
198 { _offset = offset; }
199 void setMin(double min)
201 void setMax(double max)
204 virtual double getValue() const
208 double value = _propertyNode ? _propertyNode->getDoubleValue() : 0;
209 return SGMiscd::clip(_offset + _scale*value, _min, _max);
212 SGSharedPtr<SGPropertyNode const> _propertyNode;
213 mutable SGPersonalityParameter<double> _scale;
214 mutable SGPersonalityParameter<double> _offset;
219 class SGInterpTableValue : public SGDoubleValue {
221 SGInterpTableValue(SGPropertyNode const* propertyNode,
222 SGInterpTable const* interpTable) :
223 _propertyNode(propertyNode),
224 _interpTable(interpTable)
226 virtual double getValue() const
227 { return _interpTable->interpolate(_propertyNode ? _propertyNode->getDoubleValue() : 0); }
229 SGSharedPtr<SGPropertyNode const> _propertyNode;
230 SGSharedPtr<SGInterpTable const> _interpTable;
233 class SGTexScaleOffsetValue : public SGDoubleValue {
235 SGTexScaleOffsetValue(const SGPropertyNode* propertyNode) :
236 _propertyNode(propertyNode),
242 _min(-SGLimitsd::max()),
243 _max(SGLimitsd::max())
245 void setScale(double scale)
247 void setOffset(double offset)
248 { _offset = offset; }
249 void setStep(double step)
251 void setScroll(double scroll)
252 { _scroll = scroll; }
253 void setBias(double bias)
255 void setMin(double min)
257 void setMax(double max)
260 virtual double getValue() const
262 double value = _propertyNode ? _propertyNode->getDoubleValue() : 0;
263 value = apply_mods(value, _step, _scroll, _bias);
264 return SGMiscd::clip(_scale*(_offset + value), _min, _max);
267 SGSharedPtr<const SGPropertyNode> _propertyNode;
277 class SGTexTableValue : public SGDoubleValue {
279 SGTexTableValue(const SGPropertyNode* propertyNode,
280 const SGInterpTable* interpTable) :
281 _propertyNode(propertyNode),
282 _interpTable(interpTable)
284 void setStep(double step)
286 void setScroll(double scroll)
287 { _scroll = scroll; }
288 void setBias(double bias)
290 virtual double getValue() const
292 double value = _propertyNode ? _propertyNode->getDoubleValue() : 0;
293 value = apply_mods(value, _step, _scroll, _bias);
294 return _interpTable->interpolate(value);
297 SGSharedPtr<SGPropertyNode const> _propertyNode;
298 SGSharedPtr<SGInterpTable const> _interpTable;
305 unit_string(const char* value, const char* unit)
307 return std::string(value) + unit;
310 static SGDoubleValue*
311 read_value(const SGPropertyNode* configNode, SGPropertyNode* modelRoot,
312 const char* unit, double defMin, double defMax)
314 std::string inputPropertyName;
315 inputPropertyName = configNode->getStringValue("property", "");
316 if (!inputPropertyName.empty()) {
317 SGPropertyNode* inputProperty;
318 inputProperty = modelRoot->getNode(inputPropertyName.c_str(), true);
319 SGInterpTable* interpTable = read_interpolation_table(configNode);
321 SGInterpTableValue* value;
322 value = new SGInterpTableValue(inputProperty, interpTable);
325 std::string offset = unit_string("offset", unit);
326 std::string min = unit_string("min", unit);
327 std::string max = unit_string("max", unit);
329 if (configNode->getBoolValue("use-personality", false)) {
330 SGPersScaleOffsetValue* value;
331 value = new SGPersScaleOffsetValue(inputProperty, configNode,
332 "factor", offset.c_str());
333 value->setMin(configNode->getDoubleValue(min.c_str(), defMin));
334 value->setMax(configNode->getDoubleValue(max.c_str(), defMax));
337 SGScaleOffsetValue* value = new SGScaleOffsetValue(inputProperty);
338 value->setScale(configNode->getDoubleValue("factor", 1));
339 value->setOffset(configNode->getDoubleValue(offset.c_str(), 0));
340 value->setMin(configNode->getDoubleValue(min.c_str(), defMin));
341 value->setMax(configNode->getDoubleValue(max.c_str(), defMax));
350 ////////////////////////////////////////////////////////////////////////
351 // Animation installer
352 ////////////////////////////////////////////////////////////////////////
354 class SGAnimation::RemoveModeVisitor : public SGStateAttributeVisitor {
356 RemoveModeVisitor(osg::StateAttribute::GLMode mode) :
359 virtual void apply(osg::StateSet* stateSet)
363 stateSet->removeMode(_mode);
366 osg::StateAttribute::GLMode _mode;
369 class SGAnimation::RemoveAttributeVisitor : public SGStateAttributeVisitor {
371 RemoveAttributeVisitor(osg::StateAttribute::Type type) :
374 virtual void apply(osg::StateSet* stateSet)
378 while (stateSet->getAttribute(_type)) {
379 stateSet->removeAttribute(_type);
383 osg::StateAttribute::Type _type;
386 class SGAnimation::RemoveTextureModeVisitor : public SGStateAttributeVisitor {
388 RemoveTextureModeVisitor(unsigned unit, osg::StateAttribute::GLMode mode) :
392 virtual void apply(osg::StateSet* stateSet)
396 stateSet->removeTextureMode(_unit, _mode);
400 osg::StateAttribute::GLMode _mode;
403 class SGAnimation::RemoveTextureAttributeVisitor :
404 public SGStateAttributeVisitor {
406 RemoveTextureAttributeVisitor(unsigned unit,
407 osg::StateAttribute::Type type) :
411 virtual void apply(osg::StateSet* stateSet)
415 while (stateSet->getTextureAttribute(_unit, _type)) {
416 stateSet->removeTextureAttribute(_unit, _type);
421 osg::StateAttribute::Type _type;
424 class SGAnimation::BinToInheritVisitor : public SGStateAttributeVisitor {
426 virtual void apply(osg::StateSet* stateSet)
430 stateSet->setRenderBinToInherit();
434 class SGAnimation::DrawableCloneVisitor : public osg::NodeVisitor {
436 DrawableCloneVisitor() :
437 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
439 void apply(osg::Geode& geode)
441 for (unsigned i = 0 ; i < geode.getNumDrawables(); ++i) {
442 osg::CopyOp copyOp(osg::CopyOp::DEEP_COPY_ALL &
443 ~osg::CopyOp::DEEP_COPY_TEXTURES);
444 geode.setDrawable(i, copyOp(geode.getDrawable(i)));
450 SGAnimation::SGAnimation(const SGPropertyNode* configNode,
451 SGPropertyNode* modelRoot) :
452 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
454 _configNode(configNode),
455 _modelRoot(modelRoot)
457 _name = configNode->getStringValue("name", "");
458 _enableHOT = configNode->getBoolValue("enable-hot", true);
459 _disableShadow = configNode->getBoolValue("disable-shadow", false);
460 std::vector<SGPropertyNode_ptr> objectNames =
461 configNode->getChildren("object-name");
462 for (unsigned i = 0; i < objectNames.size(); ++i)
463 _objectNames.push_back(objectNames[i]->getStringValue());
466 SGAnimation::~SGAnimation()
471 SG_LOG(SG_IO, SG_ALERT, "Could not find at least one of the following"
472 " objects for animation:\n");
473 std::list<std::string>::const_iterator i;
474 for (i = _objectNames.begin(); i != _objectNames.end(); ++i)
475 SG_LOG(SG_IO, SG_ALERT, *i << "\n");
479 SGAnimation::animate(osg::Node* node, const SGPropertyNode* configNode,
480 SGPropertyNode* modelRoot)
482 std::string type = configNode->getStringValue("type", "none");
483 if (type == "alpha-test") {
484 SGAlphaTestAnimation animInst(configNode, modelRoot);
485 animInst.apply(node);
486 } else if (type == "billboard") {
487 SGBillboardAnimation animInst(configNode, modelRoot);
488 animInst.apply(node);
489 } else if (type == "blend") {
490 SGBlendAnimation animInst(configNode, modelRoot);
491 animInst.apply(node);
492 } else if (type == "dist-scale") {
493 SGDistScaleAnimation animInst(configNode, modelRoot);
494 animInst.apply(node);
495 } else if (type == "flash") {
496 SGFlashAnimation animInst(configNode, modelRoot);
497 animInst.apply(node);
498 } else if (type == "material") {
499 SGMaterialAnimation animInst(configNode, modelRoot);
500 animInst.apply(node);
501 } else if (type == "noshadow") {
502 SGShadowAnimation animInst(configNode, modelRoot);
503 animInst.apply(node);
504 } else if (type == "pick") {
505 SGPickAnimation animInst(configNode, modelRoot);
506 animInst.apply(node);
507 } else if (type == "range") {
508 SGRangeAnimation animInst(configNode, modelRoot);
509 animInst.apply(node);
510 } else if (type == "rotate" || type == "spin") {
511 SGRotateAnimation animInst(configNode, modelRoot);
512 animInst.apply(node);
513 } else if (type == "scale") {
514 SGScaleAnimation animInst(configNode, modelRoot);
515 animInst.apply(node);
516 } else if (type == "select") {
517 SGSelectAnimation animInst(configNode, modelRoot);
518 animInst.apply(node);
519 } else if (type == "shader") {
520 SGShaderAnimation animInst(configNode, modelRoot);
521 animInst.apply(node);
522 } else if (type == "textranslate" || type == "texrotate" ||
523 type == "texmultiple") {
524 SGTexTransformAnimation animInst(configNode, modelRoot);
525 animInst.apply(node);
526 } else if (type == "timed") {
527 SGTimedAnimation animInst(configNode, modelRoot);
528 animInst.apply(node);
529 } else if (type == "translate") {
530 SGTranslateAnimation animInst(configNode, modelRoot);
531 animInst.apply(node);
532 } else if (type == "null" || type == "none" || type.empty()) {
533 SGGroupAnimation animInst(configNode, modelRoot);
534 animInst.apply(node);
543 SGAnimation::apply(osg::Node* node)
545 // duh what a special case ...
546 if (_objectNames.empty()) {
547 osg::Group* group = node->asGroup();
549 osg::ref_ptr<osg::Group> animationGroup;
550 installInGroup(std::string(), *group, animationGroup);
557 SGAnimation::install(osg::Node& node)
561 node.setNodeMask( SG_NODEMASK_TERRAIN_BIT | node.getNodeMask());
563 node.setNodeMask(~SG_NODEMASK_TERRAIN_BIT & node.getNodeMask());
565 node.setNodeMask( SG_NODEMASK_CASTSHADOW_BIT | node.getNodeMask());
567 node.setNodeMask(~SG_NODEMASK_CASTSHADOW_BIT & node.getNodeMask());
571 SGAnimation::createAnimationGroup(osg::Group& parent)
573 // default implementation, we do not need a new group
574 // for every animation type. Usually animations that just change
575 // the StateSet of some parts of the model
580 SGAnimation::apply(osg::Group& group)
582 // the trick is to first traverse the children and then
583 // possibly splice in a new group node if required.
584 // Else we end up in a recursive loop where we infinitly insert new
588 // Note that this algorithm preserves the order of the child objects
589 // like they appear in the object-name tags.
590 // The timed animations require this
591 osg::ref_ptr<osg::Group> animationGroup;
592 std::list<std::string>::const_iterator nameIt;
593 for (nameIt = _objectNames.begin(); nameIt != _objectNames.end(); ++nameIt)
594 installInGroup(*nameIt, group, animationGroup);
598 SGAnimation::installInGroup(const std::string& name, osg::Group& group,
599 osg::ref_ptr<osg::Group>& animationGroup)
601 int i = group.getNumChildren() - 1;
602 for (; 0 <= i; --i) {
603 osg::Node* child = group.getChild(i);
605 // Check if this one is already processed
606 if (std::find(_installedAnimations.begin(),
607 _installedAnimations.end(), child)
608 != _installedAnimations.end())
611 if (name.empty() || child->getName() == name) {
612 // fire the installation of the animation
615 // create a group node on demand
616 if (!animationGroup.valid()) {
617 animationGroup = createAnimationGroup(group);
618 // Animation type that does not require a new group,
619 // in this case we can stop and look for the next object
620 if (animationGroup.valid() && !_name.empty())
621 animationGroup->setName(_name);
623 if (animationGroup.valid()) {
624 animationGroup->addChild(child);
625 group.removeChild(i);
628 // store that we already have processed this child node
629 // We can hit this one twice if an animation references some
630 // part of a subtree twice
631 _installedAnimations.push_back(child);
637 SGAnimation::removeMode(osg::Node& node, osg::StateAttribute::GLMode mode)
639 RemoveModeVisitor visitor(mode);
640 node.accept(visitor);
644 SGAnimation::removeAttribute(osg::Node& node, osg::StateAttribute::Type type)
646 RemoveAttributeVisitor visitor(type);
647 node.accept(visitor);
651 SGAnimation::removeTextureMode(osg::Node& node, unsigned unit,
652 osg::StateAttribute::GLMode mode)
654 RemoveTextureModeVisitor visitor(unit, mode);
655 node.accept(visitor);
659 SGAnimation::removeTextureAttribute(osg::Node& node, unsigned unit,
660 osg::StateAttribute::Type type)
662 RemoveTextureAttributeVisitor visitor(unit, type);
663 node.accept(visitor);
667 SGAnimation::setRenderBinToInherit(osg::Node& node)
669 BinToInheritVisitor visitor;
670 node.accept(visitor);
674 SGAnimation::cloneDrawables(osg::Node& node)
676 DrawableCloneVisitor visitor;
677 node.accept(visitor);
681 SGAnimation::getCondition() const
683 const SGPropertyNode* conditionNode = _configNode->getChild("condition");
686 return sgReadCondition(_modelRoot, conditionNode);
691 ////////////////////////////////////////////////////////////////////////
692 // Implementation of null animation
693 ////////////////////////////////////////////////////////////////////////
695 // Ok, that is to build a subgraph from different other
696 // graph nodes. I guess that this stems from the time where modellers
697 // could not build hierarchical trees ...
698 SGGroupAnimation::SGGroupAnimation(const SGPropertyNode* configNode,
699 SGPropertyNode* modelRoot):
700 SGAnimation(configNode, modelRoot)
705 SGGroupAnimation::createAnimationGroup(osg::Group& parent)
707 osg::Group* group = new osg::Group;
708 parent.addChild(group);
713 ////////////////////////////////////////////////////////////////////////
714 // Implementation of translate animation
715 ////////////////////////////////////////////////////////////////////////
717 class SGTranslateAnimation::UpdateCallback : public osg::NodeCallback {
719 UpdateCallback(SGCondition const* condition,
720 SGDoubleValue const* animationValue) :
721 _condition(condition),
722 _animationValue(animationValue)
724 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
726 if (!_condition || _condition->test()) {
727 SGTranslateTransform* transform;
728 transform = static_cast<SGTranslateTransform*>(node);
729 transform->setValue(_animationValue->getValue());
734 SGSharedPtr<SGCondition const> _condition;
735 SGSharedPtr<SGDoubleValue const> _animationValue;
738 SGTranslateAnimation::SGTranslateAnimation(const SGPropertyNode* configNode,
739 SGPropertyNode* modelRoot) :
740 SGAnimation(configNode, modelRoot)
742 _condition = getCondition();
743 _animationValue = read_value(configNode, modelRoot, "-m",
744 -SGLimitsd::max(), SGLimitsd::max());
745 _axis[0] = configNode->getDoubleValue("axis/x", 0);
746 _axis[1] = configNode->getDoubleValue("axis/y", 0);
747 _axis[2] = configNode->getDoubleValue("axis/z", 0);
748 if (8*SGLimitsd::min() < norm(_axis))
749 _axis = normalize(_axis);
751 _initialValue = configNode->getDoubleValue("starting-position-m", 0);
752 _initialValue *= configNode->getDoubleValue("factor", 1);
753 _initialValue += configNode->getDoubleValue("offset-m", 0);
757 SGTranslateAnimation::createAnimationGroup(osg::Group& parent)
759 SGTranslateTransform* transform = new SGTranslateTransform;
760 transform->setName("translate animation");
761 if (_animationValue) {
762 UpdateCallback* uc = new UpdateCallback(_condition, _animationValue);
763 transform->setUpdateCallback(uc);
765 transform->setAxis(_axis);
766 transform->setValue(_initialValue);
767 parent.addChild(transform);
772 ////////////////////////////////////////////////////////////////////////
773 // Implementation of rotate/spin animation
774 ////////////////////////////////////////////////////////////////////////
776 class SGRotateAnimation::UpdateCallback : public osg::NodeCallback {
778 UpdateCallback(SGCondition const* condition,
779 SGDoubleValue const* animationValue) :
780 _condition(condition),
781 _animationValue(animationValue)
783 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
785 if (!_condition || _condition->test()) {
786 SGRotateTransform* transform;
787 transform = static_cast<SGRotateTransform*>(node);
788 transform->setAngleDeg(_animationValue->getValue());
793 SGSharedPtr<SGCondition const> _condition;
794 SGSharedPtr<SGDoubleValue const> _animationValue;
797 class SGRotateAnimation::SpinUpdateCallback : public osg::NodeCallback {
799 SpinUpdateCallback(SGCondition const* condition,
800 SGDoubleValue const* animationValue) :
801 _condition(condition),
802 _animationValue(animationValue),
805 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
807 if (!_condition || _condition->test()) {
808 SGRotateTransform* transform;
809 transform = static_cast<SGRotateTransform*>(node);
811 double t = nv->getFrameStamp()->getReferenceTime();
816 double velocity_rpms = _animationValue->getValue()/60;
817 double angle = transform->getAngleDeg();
818 angle += dt*velocity_rpms*360;
819 angle -= 360*floor(angle/360);
820 transform->setAngleDeg(angle);
825 SGSharedPtr<SGCondition const> _condition;
826 SGSharedPtr<SGDoubleValue const> _animationValue;
830 SGRotateAnimation::SGRotateAnimation(const SGPropertyNode* configNode, SGPropertyNode* modelRoot) :
831 SGAnimation(configNode, modelRoot)
833 std::string type = configNode->getStringValue("type", "");
834 _isSpin = (type == "spin");
836 _condition = getCondition();
837 _animationValue = read_value(configNode, modelRoot, "-deg",
838 -SGLimitsd::max(), SGLimitsd::max());
839 _initialValue = configNode->getDoubleValue("starting-position-deg", 0);
840 _initialValue *= configNode->getDoubleValue("factor", 1);
841 _initialValue += configNode->getDoubleValue("offset-deg", 0);
843 _center = SGVec3d::zeros();
844 if (configNode->hasValue("axis/x1-m")) {
846 v1[0] = configNode->getDoubleValue("axis/x1-m", 0);
847 v1[1] = configNode->getDoubleValue("axis/y1-m", 0);
848 v1[2] = configNode->getDoubleValue("axis/z1-m", 0);
849 v2[0] = configNode->getDoubleValue("axis/x2-m", 0);
850 v2[1] = configNode->getDoubleValue("axis/y2-m", 0);
851 v2[2] = configNode->getDoubleValue("axis/z2-m", 0);
852 _center = 0.5*(v1+v2);
855 _axis[0] = configNode->getDoubleValue("axis/x", 0);
856 _axis[1] = configNode->getDoubleValue("axis/y", 0);
857 _axis[2] = configNode->getDoubleValue("axis/z", 0);
859 if (8*SGLimitsd::min() < norm(_axis))
860 _axis = normalize(_axis);
862 _center[0] = configNode->getDoubleValue("center/x-m", _center[0]);
863 _center[1] = configNode->getDoubleValue("center/y-m", _center[1]);
864 _center[2] = configNode->getDoubleValue("center/z-m", _center[2]);
868 SGRotateAnimation::createAnimationGroup(osg::Group& parent)
870 SGRotateTransform* transform = new SGRotateTransform;
871 transform->setName("rotate animation");
873 SpinUpdateCallback* uc;
874 uc = new SpinUpdateCallback(_condition, _animationValue);
875 transform->setUpdateCallback(uc);
876 } else if (_animationValue) {
877 UpdateCallback* uc = new UpdateCallback(_condition, _animationValue);
878 transform->setUpdateCallback(uc);
880 transform->setCenter(_center);
881 transform->setAxis(_axis);
882 transform->setAngleDeg(_initialValue);
883 parent.addChild(transform);
888 ////////////////////////////////////////////////////////////////////////
889 // Implementation of scale animation
890 ////////////////////////////////////////////////////////////////////////
892 class SGScaleAnimation::UpdateCallback : public osg::NodeCallback {
894 UpdateCallback(const SGCondition* condition,
895 SGSharedPtr<const SGDoubleValue> animationValue[3]) :
896 _condition(condition)
898 _animationValue[0] = animationValue[0];
899 _animationValue[1] = animationValue[1];
900 _animationValue[2] = animationValue[2];
902 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
904 if (!_condition || _condition->test()) {
905 SGScaleTransform* transform;
906 transform = static_cast<SGScaleTransform*>(node);
907 SGVec3d scale(_animationValue[0]->getValue(),
908 _animationValue[1]->getValue(),
909 _animationValue[2]->getValue());
910 transform->setScaleFactor(scale);
915 SGSharedPtr<SGCondition const> _condition;
916 SGSharedPtr<SGDoubleValue const> _animationValue[3];
919 SGScaleAnimation::SGScaleAnimation(const SGPropertyNode* configNode,
920 SGPropertyNode* modelRoot) :
921 SGAnimation(configNode, modelRoot)
923 _condition = getCondition();
925 // default offset/factor for all directions
926 double offset = configNode->getDoubleValue("offset", 0);
927 double factor = configNode->getDoubleValue("factor", 1);
929 std::string inputPropertyName;
930 inputPropertyName = configNode->getStringValue("property", "");
931 SGPropertyNode* inputProperty = 0;
932 if (!inputPropertyName.empty()) {
933 inputProperty = modelRoot->getNode(inputPropertyName.c_str(), true);
935 SGInterpTable* interpTable = read_interpolation_table(configNode);
937 SGInterpTableValue* value;
938 value = new SGInterpTableValue(inputProperty, interpTable);
939 _animationValue[0] = value;
940 _animationValue[1] = value;
941 _animationValue[2] = value;
942 } else if (configNode->getBoolValue("use-personality", false)) {
943 SGPersScaleOffsetValue* value;
944 value = new SGPersScaleOffsetValue(inputProperty, configNode,
945 "x-factor", "x-offset",
947 value->setMin(configNode->getDoubleValue("x-min", 0));
948 value->setMax(configNode->getDoubleValue("x-max", SGLimitsd::max()));
949 _animationValue[0] = value;
950 value = new SGPersScaleOffsetValue(inputProperty, configNode,
951 "y-factor", "y-offset",
953 value->setMin(configNode->getDoubleValue("y-min", 0));
954 value->setMax(configNode->getDoubleValue("y-max", SGLimitsd::max()));
955 _animationValue[1] = value;
956 value = new SGPersScaleOffsetValue(inputProperty, configNode,
957 "z-factor", "z-offset",
959 value->setMin(configNode->getDoubleValue("z-min", 0));
960 value->setMax(configNode->getDoubleValue("z-max", SGLimitsd::max()));
961 _animationValue[2] = value;
963 SGScaleOffsetValue* value = new SGScaleOffsetValue(inputProperty);
964 value->setScale(configNode->getDoubleValue("x-factor", factor));
965 value->setOffset(configNode->getDoubleValue("x-offset", offset));
966 value->setMin(configNode->getDoubleValue("x-min", 0));
967 value->setMax(configNode->getDoubleValue("x-max", SGLimitsd::max()));
968 _animationValue[0] = value;
969 value = new SGScaleOffsetValue(inputProperty);
970 value->setScale(configNode->getDoubleValue("y-factor", factor));
971 value->setOffset(configNode->getDoubleValue("y-offset", offset));
972 value->setMin(configNode->getDoubleValue("y-min", 0));
973 value->setMax(configNode->getDoubleValue("y-max", SGLimitsd::max()));
974 _animationValue[1] = value;
975 value = new SGScaleOffsetValue(inputProperty);
976 value->setScale(configNode->getDoubleValue("z-factor", factor));
977 value->setOffset(configNode->getDoubleValue("z-offset", offset));
978 value->setMin(configNode->getDoubleValue("z-min", 0));
979 value->setMax(configNode->getDoubleValue("z-max", SGLimitsd::max()));
980 _animationValue[2] = value;
982 _initialValue[0] = configNode->getDoubleValue("x-starting-scale", 1);
983 _initialValue[0] *= configNode->getDoubleValue("x-factor", factor);
984 _initialValue[0] += configNode->getDoubleValue("x-offset", offset);
985 _initialValue[1] = configNode->getDoubleValue("y-starting-scale", 1);
986 _initialValue[1] *= configNode->getDoubleValue("y-factor", factor);
987 _initialValue[1] += configNode->getDoubleValue("y-offset", offset);
988 _initialValue[2] = configNode->getDoubleValue("z-starting-scale", 1);
989 _initialValue[2] *= configNode->getDoubleValue("z-factor", factor);
990 _initialValue[2] += configNode->getDoubleValue("z-offset", offset);
991 _center[0] = configNode->getDoubleValue("center/x-m", 0);
992 _center[1] = configNode->getDoubleValue("center/y-m", 0);
993 _center[2] = configNode->getDoubleValue("center/z-m", 0);
997 SGScaleAnimation::createAnimationGroup(osg::Group& parent)
999 SGScaleTransform* transform = new SGScaleTransform;
1000 transform->setName("scale animation");
1001 transform->setCenter(_center);
1002 transform->setScaleFactor(_initialValue);
1003 UpdateCallback* uc = new UpdateCallback(_condition, _animationValue);
1004 transform->setUpdateCallback(uc);
1005 parent.addChild(transform);
1010 ////////////////////////////////////////////////////////////////////////
1011 // Implementation of dist scale animation
1012 ////////////////////////////////////////////////////////////////////////
1014 class SGDistScaleAnimation::Transform : public osg::Transform {
1016 Transform(const SGPropertyNode* configNode)
1018 setName(configNode->getStringValue("name", "dist scale animation"));
1019 setReferenceFrame(RELATIVE_RF);
1020 getOrCreateStateSet()->setMode(GL_NORMALIZE, osg::StateAttribute::ON);
1021 _factor = configNode->getFloatValue("factor", 1);
1022 _offset = configNode->getFloatValue("offset", 0);
1023 _min_v = configNode->getFloatValue("min", SGLimitsf::epsilon());
1024 _max_v = configNode->getFloatValue("max", SGLimitsf::max());
1025 _table = read_interpolation_table(configNode);
1026 _center[0] = configNode->getFloatValue("center/x-m", 0);
1027 _center[1] = configNode->getFloatValue("center/y-m", 0);
1028 _center[2] = configNode->getFloatValue("center/z-m", 0);
1030 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
1031 osg::NodeVisitor* nv) const
1033 osg::Matrix transform;
1034 double scale_factor = computeScaleFactor(nv);
1035 transform(0,0) = scale_factor;
1036 transform(1,1) = scale_factor;
1037 transform(2,2) = scale_factor;
1038 transform(3,0) = _center[0]*(1 - scale_factor);
1039 transform(3,1) = _center[1]*(1 - scale_factor);
1040 transform(3,2) = _center[2]*(1 - scale_factor);
1041 matrix.preMult(transform);
1045 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
1046 osg::NodeVisitor* nv) const
1048 double scale_factor = computeScaleFactor(nv);
1049 if (fabs(scale_factor) <= SGLimits<double>::min())
1051 osg::Matrix transform;
1052 double rScaleFactor = 1/scale_factor;
1053 transform(0,0) = rScaleFactor;
1054 transform(1,1) = rScaleFactor;
1055 transform(2,2) = rScaleFactor;
1056 transform(3,0) = _center[0]*(1 - rScaleFactor);
1057 transform(3,1) = _center[1]*(1 - rScaleFactor);
1058 transform(3,2) = _center[2]*(1 - rScaleFactor);
1059 matrix.postMult(transform);
1064 double computeScaleFactor(osg::NodeVisitor* nv) const
1069 double scale_factor = (_center.osg() - nv->getEyePoint()).length();
1071 scale_factor = _factor * scale_factor + _offset;
1073 scale_factor = _table->interpolate( scale_factor );
1075 if (scale_factor < _min_v)
1076 scale_factor = _min_v;
1077 if (scale_factor > _max_v)
1078 scale_factor = _max_v;
1080 return scale_factor;
1083 SGSharedPtr<SGInterpTable> _table;
1092 SGDistScaleAnimation::SGDistScaleAnimation(const SGPropertyNode* configNode,
1093 SGPropertyNode* modelRoot) :
1094 SGAnimation(configNode, modelRoot)
1099 SGDistScaleAnimation::createAnimationGroup(osg::Group& parent)
1101 Transform* transform = new Transform(getConfig());
1102 parent.addChild(transform);
1107 ////////////////////////////////////////////////////////////////////////
1108 // Implementation of flash animation
1109 ////////////////////////////////////////////////////////////////////////
1111 class SGFlashAnimation::Transform : public osg::Transform {
1113 Transform(const SGPropertyNode* configNode)
1115 setReferenceFrame(RELATIVE_RF);
1116 setName(configNode->getStringValue("name", "flash animation"));
1117 getOrCreateStateSet()->setMode(GL_NORMALIZE, osg::StateAttribute::ON);
1119 _axis[0] = configNode->getFloatValue("axis/x", 0);
1120 _axis[1] = configNode->getFloatValue("axis/y", 0);
1121 _axis[2] = configNode->getFloatValue("axis/z", 1);
1124 _center[0] = configNode->getFloatValue("center/x-m", 0);
1125 _center[1] = configNode->getFloatValue("center/y-m", 0);
1126 _center[2] = configNode->getFloatValue("center/z-m", 0);
1128 _offset = configNode->getFloatValue("offset", 0);
1129 _factor = configNode->getFloatValue("factor", 1);
1130 _power = configNode->getFloatValue("power", 1);
1131 _two_sides = configNode->getBoolValue("two-sides", false);
1133 _min_v = configNode->getFloatValue("min", SGLimitsf::epsilon());
1134 _max_v = configNode->getFloatValue("max", 1);
1136 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
1137 osg::NodeVisitor* nv) const
1139 osg::Matrix transform;
1140 double scale_factor = computeScaleFactor(nv);
1141 transform(0,0) = scale_factor;
1142 transform(1,1) = scale_factor;
1143 transform(2,2) = scale_factor;
1144 transform(3,0) = _center[0]*(1 - scale_factor);
1145 transform(3,1) = _center[1]*(1 - scale_factor);
1146 transform(3,2) = _center[2]*(1 - scale_factor);
1147 matrix.preMult(transform);
1151 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
1152 osg::NodeVisitor* nv) const
1154 double scale_factor = computeScaleFactor(nv);
1155 if (fabs(scale_factor) <= SGLimits<double>::min())
1157 osg::Matrix transform;
1158 double rScaleFactor = 1/scale_factor;
1159 transform(0,0) = rScaleFactor;
1160 transform(1,1) = rScaleFactor;
1161 transform(2,2) = rScaleFactor;
1162 transform(3,0) = _center[0]*(1 - rScaleFactor);
1163 transform(3,1) = _center[1]*(1 - rScaleFactor);
1164 transform(3,2) = _center[2]*(1 - rScaleFactor);
1165 matrix.postMult(transform);
1170 double computeScaleFactor(osg::NodeVisitor* nv) const
1175 osg::Vec3 localEyeToCenter = nv->getEyePoint() - _center;
1176 localEyeToCenter.normalize();
1178 double cos_angle = localEyeToCenter*_axis;
1179 double scale_factor = 0;
1180 if ( _two_sides && cos_angle < 0 )
1181 scale_factor = _factor * pow( -cos_angle, _power ) + _offset;
1182 else if ( cos_angle > 0 )
1183 scale_factor = _factor * pow( cos_angle, _power ) + _offset;
1185 if ( scale_factor < _min_v )
1186 scale_factor = _min_v;
1187 if ( scale_factor > _max_v )
1188 scale_factor = _max_v;
1190 return scale_factor;
1193 virtual osg::BoundingSphere computeBound() const
1195 // avoid being culled away by small feature culling
1196 osg::BoundingSphere bs = osg::Group::computeBound();
1197 bs.radius() *= _max_v;
1204 double _power, _factor, _offset, _min_v, _max_v;
1209 SGFlashAnimation::SGFlashAnimation(const SGPropertyNode* configNode,
1210 SGPropertyNode* modelRoot) :
1211 SGAnimation(configNode, modelRoot)
1216 SGFlashAnimation::createAnimationGroup(osg::Group& parent)
1218 Transform* transform = new Transform(getConfig());
1219 parent.addChild(transform);
1224 ////////////////////////////////////////////////////////////////////////
1225 // Implementation of flash animation
1226 ////////////////////////////////////////////////////////////////////////
1228 class SGBillboardAnimation::Transform : public osg::Transform {
1230 Transform(const SGPropertyNode* configNode) :
1231 _spherical(configNode->getBoolValue("spherical", true))
1233 setReferenceFrame(RELATIVE_RF);
1234 setName(configNode->getStringValue("name", "billboard animation"));
1236 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
1237 osg::NodeVisitor* nv) const
1239 // More or less taken from plibs ssgCutout
1241 matrix(0,0) = 1; matrix(0,1) = 0; matrix(0,2) = 0;
1242 matrix(1,0) = 0; matrix(1,1) = 0; matrix(1,2) = -1;
1243 matrix(2,0) = 0; matrix(2,1) = 1; matrix(2,2) = 0;
1245 osg::Vec3 zAxis(matrix(2, 0), matrix(2, 1), matrix(2, 2));
1246 osg::Vec3 xAxis = osg::Vec3(0, 0, -1)^zAxis;
1247 osg::Vec3 yAxis = zAxis^xAxis;
1253 matrix(0,0) = xAxis[0]; matrix(0,1) = xAxis[1]; matrix(0,2) = xAxis[2];
1254 matrix(1,0) = yAxis[0]; matrix(1,1) = yAxis[1]; matrix(1,2) = yAxis[2];
1255 matrix(2,0) = zAxis[0]; matrix(2,1) = zAxis[1]; matrix(2,2) = zAxis[2];
1260 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
1261 osg::NodeVisitor* nv) const
1263 // Hmm, don't yet know how to get that back ...
1272 SGBillboardAnimation::SGBillboardAnimation(const SGPropertyNode* configNode,
1273 SGPropertyNode* modelRoot) :
1274 SGAnimation(configNode, modelRoot)
1279 SGBillboardAnimation::createAnimationGroup(osg::Group& parent)
1281 Transform* transform = new Transform(getConfig());
1282 parent.addChild(transform);
1287 ////////////////////////////////////////////////////////////////////////
1288 // Implementation of a range animation
1289 ////////////////////////////////////////////////////////////////////////
1291 class SGRangeAnimation::UpdateCallback : public osg::NodeCallback {
1293 UpdateCallback(const SGCondition* condition,
1294 const SGDoubleValue* minAnimationValue,
1295 const SGDoubleValue* maxAnimationValue,
1296 double minValue, double maxValue) :
1297 _condition(condition),
1298 _minAnimationValue(minAnimationValue),
1299 _maxAnimationValue(maxAnimationValue),
1300 _minStaticValue(minValue),
1301 _maxStaticValue(maxValue)
1303 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1305 osg::LOD* lod = static_cast<osg::LOD*>(node);
1306 if (!_condition || _condition->test()) {
1308 if (_minAnimationValue)
1309 minRange = _minAnimationValue->getValue();
1311 minRange = _minStaticValue;
1313 if (_maxAnimationValue)
1314 maxRange = _maxAnimationValue->getValue();
1316 maxRange = _maxStaticValue;
1317 lod->setRange(0, minRange, maxRange);
1319 lod->setRange(0, 0, SGLimitsf::max());
1325 SGSharedPtr<const SGCondition> _condition;
1326 SGSharedPtr<const SGDoubleValue> _minAnimationValue;
1327 SGSharedPtr<const SGDoubleValue> _maxAnimationValue;
1328 double _minStaticValue;
1329 double _maxStaticValue;
1332 SGRangeAnimation::SGRangeAnimation(const SGPropertyNode* configNode,
1333 SGPropertyNode* modelRoot) :
1334 SGAnimation(configNode, modelRoot)
1336 _condition = getCondition();
1338 std::string inputPropertyName;
1339 inputPropertyName = configNode->getStringValue("min-property", "");
1340 if (!inputPropertyName.empty()) {
1341 SGPropertyNode* inputProperty;
1342 inputProperty = modelRoot->getNode(inputPropertyName.c_str(), true);
1343 SGScaleOffsetValue* value = new SGScaleOffsetValue(inputProperty);
1344 value->setScale(configNode->getDoubleValue("min-factor", 1));
1345 _minAnimationValue = value;
1347 inputPropertyName = configNode->getStringValue("max-property", "");
1348 if (!inputPropertyName.empty()) {
1349 SGPropertyNode* inputProperty;
1350 inputProperty = modelRoot->getNode(inputPropertyName.c_str(), true);
1351 SGScaleOffsetValue* value = new SGScaleOffsetValue(inputProperty);
1352 value->setScale(configNode->getDoubleValue("max-factor", 1));
1353 _maxAnimationValue = value;
1356 _initialValue[0] = configNode->getDoubleValue("min-m", 0);
1357 _initialValue[0] *= configNode->getDoubleValue("min-factor", 1);
1358 _initialValue[1] = configNode->getDoubleValue("max-m", SGLimitsf::max());
1359 _initialValue[1] *= configNode->getDoubleValue("max-factor", 1);
1363 SGRangeAnimation::createAnimationGroup(osg::Group& parent)
1365 osg::Group* group = new osg::Group;
1366 group->setName("range animation group");
1368 osg::LOD* lod = new osg::LOD;
1369 lod->setName("range animation node");
1370 parent.addChild(lod);
1372 lod->addChild(group, _initialValue[0], _initialValue[1]);
1373 lod->setCenterMode(osg::LOD::USE_BOUNDING_SPHERE_CENTER);
1374 lod->setRangeMode(osg::LOD::DISTANCE_FROM_EYE_POINT);
1375 if (_minAnimationValue || _maxAnimationValue || _condition) {
1377 uc = new UpdateCallback(_condition, _minAnimationValue, _maxAnimationValue,
1378 _initialValue[0], _initialValue[1]);
1379 lod->setUpdateCallback(uc);
1385 ////////////////////////////////////////////////////////////////////////
1386 // Implementation of a select animation
1387 ////////////////////////////////////////////////////////////////////////
1389 class SGSelectAnimation::UpdateCallback : public osg::NodeCallback {
1391 UpdateCallback(const SGCondition* condition) :
1392 _condition(condition)
1394 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1396 osg::Switch* sw = static_cast<osg::Switch*>(node);
1397 if (_condition->test())
1398 sw->setAllChildrenOn();
1400 sw->setAllChildrenOff();
1405 SGSharedPtr<SGCondition const> _condition;
1408 SGSelectAnimation::SGSelectAnimation(const SGPropertyNode* configNode,
1409 SGPropertyNode* modelRoot) :
1410 SGAnimation(configNode, modelRoot)
1415 SGSelectAnimation::createAnimationGroup(osg::Group& parent)
1417 // if no condition given, this is a noop.
1418 SGSharedPtr<SGCondition const> condition = getCondition();
1419 // trick, gets deleted with all its 'animated' children
1420 // when the animation installer returns
1422 return new osg::Group;
1424 osg::Switch* sw = new osg::Switch;
1425 sw->setName("select animation node");
1426 sw->setUpdateCallback(new UpdateCallback(condition));
1427 parent.addChild(sw);
1433 ////////////////////////////////////////////////////////////////////////
1434 // Implementation of alpha test animation
1435 ////////////////////////////////////////////////////////////////////////
1437 SGAlphaTestAnimation::SGAlphaTestAnimation(const SGPropertyNode* configNode,
1438 SGPropertyNode* modelRoot) :
1439 SGAnimation(configNode, modelRoot)
1444 SGAlphaTestAnimation::install(osg::Node& node)
1446 SGAnimation::install(node);
1448 cloneDrawables(node);
1449 removeMode(node, GL_ALPHA_TEST);
1450 removeAttribute(node, osg::StateAttribute::ALPHAFUNC);
1452 osg::StateSet* stateSet = node.getOrCreateStateSet();
1453 osg::AlphaFunc* alphaFunc = new osg::AlphaFunc;
1454 alphaFunc->setFunction(osg::AlphaFunc::GREATER);
1455 float alphaClamp = getConfig()->getFloatValue("alpha-factor", 0);
1456 alphaFunc->setReferenceValue(alphaClamp);
1457 stateSet->setAttribute(alphaFunc);
1458 stateSet->setMode(GL_ALPHA_TEST, osg::StateAttribute::ON);
1463 //////////////////////////////////////////////////////////////////////
1464 // Blend animation installer
1465 //////////////////////////////////////////////////////////////////////
1467 class SGBlendAnimation::BlendVisitor : public osg::NodeVisitor {
1469 BlendVisitor(float blend) :
1470 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
1472 { setVisitorType(osg::NodeVisitor::NODE_VISITOR); }
1473 virtual void apply(osg::Node& node)
1475 updateStateSet(node.getStateSet());
1478 virtual void apply(osg::Geode& node)
1480 apply((osg::Node&)node);
1481 unsigned nDrawables = node.getNumDrawables();
1482 for (unsigned i = 0; i < nDrawables; ++i) {
1483 osg::Drawable* drawable = node.getDrawable(i);
1484 updateStateSet(drawable->getStateSet());
1485 osg::Geometry* geometry = drawable->asGeometry();
1488 osg::Array* array = geometry->getColorArray();
1491 osg::Vec4Array* vec4Array = dynamic_cast<osg::Vec4Array*>(array);
1494 geometry->dirtyDisplayList();
1496 for (unsigned k = 0; k < vec4Array->size(); ++k) {
1497 (*vec4Array)[k][3] = _blend;
1501 void updateStateSet(osg::StateSet* stateSet)
1505 osg::StateAttribute* stateAttribute;
1506 stateAttribute = stateSet->getAttribute(osg::StateAttribute::MATERIAL);
1507 if (!stateAttribute)
1509 osg::Material* material = dynamic_cast<osg::Material*>(stateAttribute);
1512 material->setAlpha(osg::Material::FRONT_AND_BACK, _blend);
1514 stateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
1515 stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
1517 stateSet->setRenderingHint(osg::StateSet::DEFAULT_BIN);
1524 class SGBlendAnimation::UpdateCallback : public osg::NodeCallback {
1526 UpdateCallback(const SGPropertyNode* configNode, const SGDoubleValue* v) :
1530 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1532 double blend = _animationValue->getValue();
1533 if (blend != _prev_value) {
1534 _prev_value = blend;
1535 BlendVisitor visitor(1-blend);
1536 node->accept(visitor);
1542 SGSharedPtr<SGDoubleValue const> _animationValue;
1546 SGBlendAnimation::SGBlendAnimation(const SGPropertyNode* configNode,
1547 SGPropertyNode* modelRoot)
1548 : SGAnimation(configNode, modelRoot),
1549 _animationValue(read_value(configNode, modelRoot, "", 0, 1))
1554 SGBlendAnimation::createAnimationGroup(osg::Group& parent)
1556 if (!_animationValue)
1559 osg::Group* group = new osg::Switch;
1560 group->setName("blend animation node");
1561 group->setUpdateCallback(new UpdateCallback(getConfig(), _animationValue));
1562 parent.addChild(group);
1567 SGBlendAnimation::install(osg::Node& node)
1569 SGAnimation::install(node);
1570 // make sure we do not change common geometries,
1571 // that also creates new display lists for these subgeometries.
1572 cloneDrawables(node);
1576 //////////////////////////////////////////////////////////////////////
1577 // Timed animation installer
1578 //////////////////////////////////////////////////////////////////////
1582 class SGTimedAnimation::UpdateCallback : public osg::NodeCallback {
1584 UpdateCallback(const SGPropertyNode* configNode) :
1587 _duration_sec(configNode->getDoubleValue("duration-sec", 1)),
1588 _last_time_sec(SGLimitsd::max()),
1589 _use_personality(configNode->getBoolValue("use-personality", false))
1591 std::vector<SGSharedPtr<SGPropertyNode> > nodes;
1592 nodes = configNode->getChildren("branch-duration-sec");
1593 for (size_t i = 0; i < nodes.size(); ++i) {
1594 unsigned ind = nodes[ i ]->getIndex();
1595 while ( ind >= _durations.size() ) {
1596 _durations.push_back(DurationSpec(_duration_sec));
1598 SGPropertyNode_ptr rNode = nodes[i]->getChild("random");
1600 _durations[ind] = DurationSpec(nodes[ i ]->getDoubleValue());
1602 _durations[ind] = DurationSpec(rNode->getDoubleValue( "min", 0),
1603 rNode->getDoubleValue( "max", 1));
1607 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1609 assert(dynamic_cast<osg::Switch*>(node));
1610 osg::Switch* sw = static_cast<osg::Switch*>(node);
1612 unsigned nChildren = sw->getNumChildren();
1614 // blow up the durations vector to the required size
1615 while (_durations.size() < nChildren) {
1616 _durations.push_back(_duration_sec);
1618 // make sure the current index is an duration that really exists
1619 _current_index = _current_index % nChildren;
1621 // update the time and compute the current systems time value
1622 double t = nv->getFrameStamp()->getReferenceTime();
1623 if (_last_time_sec == SGLimitsd::max()) {
1626 double dt = t - _last_time_sec;
1627 if (_use_personality)
1628 dt *= 1 + 0.2*(0.5 - sg_random());
1633 double currentDuration = _durations[_current_index].get();
1634 while (currentDuration < _reminder) {
1635 _reminder -= currentDuration;
1636 _current_index = (_current_index + 1) % nChildren;
1637 currentDuration = _durations[_current_index].get();
1640 sw->setSingleChildOn(_current_index);
1646 struct DurationSpec {
1647 DurationSpec(double t) :
1648 minTime(SGMiscd::max(0.01, t)),
1649 maxTime(SGMiscd::max(0.01, t))
1651 DurationSpec(double t0, double t1) :
1652 minTime(SGMiscd::max(0.01, t0)),
1653 maxTime(SGMiscd::max(0.01, t1))
1656 { return minTime + sg_random()*(maxTime - minTime); }
1660 std::vector<DurationSpec> _durations;
1661 unsigned _current_index;
1663 double _duration_sec;
1664 double _last_time_sec;
1665 bool _use_personality;
1669 SGTimedAnimation::SGTimedAnimation(const SGPropertyNode* configNode,
1670 SGPropertyNode* modelRoot)
1671 : SGAnimation(configNode, modelRoot)
1676 SGTimedAnimation::createAnimationGroup(osg::Group& parent)
1678 osg::Switch* sw = new osg::Switch;
1679 sw->setName("timed animation node");
1680 sw->setUpdateCallback(new UpdateCallback(getConfig()));
1681 parent.addChild(sw);
1686 ////////////////////////////////////////////////////////////////////////
1687 // dynamically switch on/off shadows
1688 ////////////////////////////////////////////////////////////////////////
1690 class SGShadowAnimation::UpdateCallback : public osg::NodeCallback {
1692 UpdateCallback(const SGCondition* condition) :
1693 _condition(condition)
1695 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1697 if (_condition->test())
1698 node->setNodeMask( SG_NODEMASK_CASTSHADOW_BIT | node->getNodeMask());
1700 node->setNodeMask(~SG_NODEMASK_CASTSHADOW_BIT & node->getNodeMask());
1705 SGSharedPtr<const SGCondition> _condition;
1708 SGShadowAnimation::SGShadowAnimation(const SGPropertyNode* configNode,
1709 SGPropertyNode* modelRoot) :
1710 SGAnimation(configNode, modelRoot)
1715 SGShadowAnimation::createAnimationGroup(osg::Group& parent)
1717 SGSharedPtr<SGCondition const> condition = getCondition();
1721 osg::Group* group = new osg::Group;
1722 group->setName("shadow animation");
1723 group->setUpdateCallback(new UpdateCallback(condition));
1724 parent.addChild(group);
1729 ////////////////////////////////////////////////////////////////////////
1730 // Implementation of SGTexTransformAnimation
1731 ////////////////////////////////////////////////////////////////////////
1733 class SGTexTransformAnimation::Transform : public SGReferenced {
1738 virtual ~Transform()
1740 void setValue(double value)
1742 virtual void transform(osg::Matrix&) = 0;
1747 class SGTexTransformAnimation::Translation :
1748 public SGTexTransformAnimation::Transform {
1750 Translation(const SGVec3d& axis) :
1753 void setValue(double value)
1755 virtual void transform(osg::Matrix& matrix)
1758 set_translation(tmp, _value, _axis);
1759 matrix.preMult(tmp);
1765 class SGTexTransformAnimation::Rotation :
1766 public SGTexTransformAnimation::Transform {
1768 Rotation(const SGVec3d& axis, const SGVec3d& center) :
1772 virtual void transform(osg::Matrix& matrix)
1775 set_rotation(tmp, _value, _center, _axis);
1776 matrix.preMult(tmp);
1783 class SGTexTransformAnimation::UpdateCallback :
1784 public osg::StateAttribute::Callback {
1786 UpdateCallback(const SGCondition* condition) :
1787 _condition(condition)
1789 virtual void operator () (osg::StateAttribute* sa, osg::NodeVisitor*)
1791 if (!_condition || _condition->test()) {
1792 TransformList::const_iterator i;
1793 for (i = _transforms.begin(); i != _transforms.end(); ++i)
1794 i->transform->setValue(i->value->getValue());
1796 assert(dynamic_cast<osg::TexMat*>(sa));
1797 osg::TexMat* texMat = static_cast<osg::TexMat*>(sa);
1798 texMat->getMatrix().makeIdentity();
1799 TransformList::const_iterator i;
1800 for (i = _transforms.begin(); i != _transforms.end(); ++i)
1801 i->transform->transform(texMat->getMatrix());
1803 void appendTransform(Transform* transform, SGDoubleValue* value)
1805 Entry entry = { transform, value };
1806 transform->transform(_matrix);
1807 _transforms.push_back(entry);
1812 SGSharedPtr<Transform> transform;
1813 SGSharedPtr<const SGDoubleValue> value;
1815 typedef std::vector<Entry> TransformList;
1816 TransformList _transforms;
1817 SGSharedPtr<const SGCondition> _condition;
1818 osg::Matrix _matrix;
1821 SGTexTransformAnimation::SGTexTransformAnimation(const SGPropertyNode* configNode,
1822 SGPropertyNode* modelRoot) :
1823 SGAnimation(configNode, modelRoot)
1828 SGTexTransformAnimation::createAnimationGroup(osg::Group& parent)
1830 osg::Group* group = new osg::Group;
1831 group->setName("texture transform group");
1832 osg::StateSet* stateSet = group->getOrCreateStateSet();
1833 osg::TexMat* texMat = new osg::TexMat;
1834 UpdateCallback* updateCallback = new UpdateCallback(getCondition());
1835 // interpret the configs ...
1836 std::string type = getType();
1838 if (type == "textranslate") {
1839 appendTexTranslate(getConfig(), updateCallback);
1840 } else if (type == "texrotate") {
1841 appendTexRotate(getConfig(), updateCallback);
1842 } else if (type == "texmultiple") {
1843 std::vector<SGSharedPtr<SGPropertyNode> > transformConfigs;
1844 transformConfigs = getConfig()->getChildren("transform");
1845 for (unsigned i = 0; i < transformConfigs.size(); ++i) {
1846 std::string subtype = transformConfigs[i]->getStringValue("subtype", "");
1847 if (subtype == "textranslate")
1848 appendTexTranslate(transformConfigs[i], updateCallback);
1849 else if (subtype == "texrotate")
1850 appendTexRotate(transformConfigs[i], updateCallback);
1852 SG_LOG(SG_INPUT, SG_ALERT,
1853 "Ignoring unknown texture transform subtype");
1856 SG_LOG(SG_INPUT, SG_ALERT, "Ignoring unknown texture transform type");
1859 texMat->setUpdateCallback(updateCallback);
1860 stateSet->setTextureAttribute(0, texMat);
1861 parent.addChild(group);
1866 SGTexTransformAnimation::appendTexTranslate(const SGPropertyNode* config,
1867 UpdateCallback* updateCallback)
1869 std::string propertyName = config->getStringValue("property", "/null");
1870 SGPropertyNode* inputNode;
1871 inputNode = getModelRoot()->getNode(propertyName.c_str(), true);
1873 SGDoubleValue* animationValue;
1874 SGInterpTable* table = read_interpolation_table(config);
1876 SGTexTableValue* value;
1877 value = new SGTexTableValue(inputNode, table);
1878 value->setStep(config->getDoubleValue("step", 0));
1879 value->setScroll(config->getDoubleValue("scroll", 0));
1880 value->setBias(config->getDoubleValue("bias", 0));
1881 animationValue = value;
1883 SGTexScaleOffsetValue* value;
1884 value = new SGTexScaleOffsetValue(inputNode);
1885 value->setScale(config->getDoubleValue("factor", 1));
1886 value->setOffset(config->getDoubleValue("offset", 0));
1887 value->setStep(config->getDoubleValue("step", 0));
1888 value->setScroll(config->getDoubleValue("scroll", 0));
1889 value->setBias(config->getDoubleValue("bias", 0));
1890 value->setMin(config->getDoubleValue("min", -SGLimitsd::max()));
1891 value->setMax(config->getDoubleValue("max", SGLimitsd::max()));
1892 animationValue = value;
1894 SGVec3d axis(config->getDoubleValue("axis/x", 0),
1895 config->getDoubleValue("axis/y", 0),
1896 config->getDoubleValue("axis/z", 0));
1897 Translation* translation;
1898 translation = new Translation(normalize(axis));
1899 translation->setValue(config->getDoubleValue("starting-position", 0));
1900 updateCallback->appendTransform(translation, animationValue);
1904 SGTexTransformAnimation::appendTexRotate(const SGPropertyNode* config,
1905 UpdateCallback* updateCallback)
1907 std::string propertyName = config->getStringValue("property", "/null");
1908 SGPropertyNode* inputNode;
1909 inputNode = getModelRoot()->getNode(propertyName.c_str(), true);
1911 SGDoubleValue* animationValue;
1912 SGInterpTable* table = read_interpolation_table(config);
1914 SGTexTableValue* value;
1915 value = new SGTexTableValue(inputNode, table);
1916 value->setStep(config->getDoubleValue("step", 0));
1917 value->setScroll(config->getDoubleValue("scroll", 0));
1918 value->setBias(config->getDoubleValue("bias", 0));
1919 animationValue = value;
1921 SGTexScaleOffsetValue* value;
1922 value = new SGTexScaleOffsetValue(inputNode);
1923 value->setScale(config->getDoubleValue("factor", 1));
1924 value->setOffset(config->getDoubleValue("offset-deg", 0));
1925 value->setStep(config->getDoubleValue("step", 0));
1926 value->setScroll(config->getDoubleValue("scroll", 0));
1927 value->setBias(config->getDoubleValue("bias", 0));
1928 value->setMin(config->getDoubleValue("min-deg", -SGLimitsd::max()));
1929 value->setMax(config->getDoubleValue("max-deg", SGLimitsd::max()));
1930 animationValue = value;
1932 SGVec3d axis(config->getDoubleValue("axis/x", 0),
1933 config->getDoubleValue("axis/y", 0),
1934 config->getDoubleValue("axis/z", 0));
1935 SGVec3d center(config->getDoubleValue("center/x", 0),
1936 config->getDoubleValue("center/y", 0),
1937 config->getDoubleValue("center/z", 0));
1939 rotation = new Rotation(normalize(axis), center);
1940 rotation->setValue(config->getDoubleValue("starting-position-deg", 0));
1941 updateCallback->appendTransform(rotation, animationValue);
1945 ////////////////////////////////////////////////////////////////////////
1946 // Implementation of SGPickAnimation
1947 ////////////////////////////////////////////////////////////////////////
1949 class SGPickAnimation::PickCallback : public SGPickCallback {
1951 PickCallback(const SGPropertyNode* configNode,
1952 SGPropertyNode* modelRoot) :
1953 _button(configNode->getIntValue("button", -1)),
1954 _repeatable(configNode->getBoolValue("repeatable", false)),
1955 _repeatInterval(configNode->getDoubleValue("interval-sec", 0.1))
1957 SG_LOG(SG_INPUT, SG_DEBUG, "Reading all bindings");
1958 std::vector<SGPropertyNode_ptr> bindings;
1959 bindings = configNode->getChildren("binding");
1960 for (unsigned int i = 0; i < bindings.size(); ++i) {
1961 _bindingsDown.push_back(new SGBinding(bindings[i], modelRoot));
1964 const SGPropertyNode* upNode = configNode->getChild("mod-up");
1967 bindings = upNode->getChildren("binding");
1968 for (unsigned int i = 0; i < bindings.size(); ++i) {
1969 _bindingsUp.push_back(new SGBinding(bindings[i], modelRoot));
1972 virtual bool buttonPressed(int button, const Info&)
1974 if (0 <= _button && button != _button)
1976 SGBindingList::const_iterator i;
1977 for (i = _bindingsDown.begin(); i != _bindingsDown.end(); ++i)
1982 virtual void buttonReleased(void)
1984 SGBindingList::const_iterator i;
1985 for (i = _bindingsUp.begin(); i != _bindingsUp.end(); ++i)
1988 virtual void update(double dt)
1994 while (_repeatInterval < _repeatTime) {
1995 _repeatTime -= _repeatInterval;
1996 SGBindingList::const_iterator i;
1997 for (i = _bindingsDown.begin(); i != _bindingsDown.end(); ++i)
2002 SGBindingList _bindingsDown;
2003 SGBindingList _bindingsUp;
2006 double _repeatInterval;
2010 SGPickAnimation::SGPickAnimation(const SGPropertyNode* configNode,
2011 SGPropertyNode* modelRoot) :
2012 SGAnimation(configNode, modelRoot)
2017 SGPickAnimation::createAnimationGroup(osg::Group& parent)
2019 osg::Group* commonGroup = new osg::Group;
2021 // Contains the normal geometry that is interactive
2022 osg::ref_ptr<osg::Group> normalGroup = new osg::Group;
2023 normalGroup->addChild(commonGroup);
2025 // Used to render the geometry with just yellow edges
2026 osg::Group* highlightGroup = new osg::Group;
2027 highlightGroup->setNodeMask(SG_NODEMASK_PICK_BIT);
2028 highlightGroup->addChild(commonGroup);
2029 SGSceneUserData* ud;
2030 ud = SGSceneUserData::getOrCreateSceneUserData(highlightGroup);
2031 std::vector<SGPropertyNode_ptr> actions;
2032 actions = getConfig()->getChildren("action");
2033 for (unsigned int i = 0; i < actions.size(); ++i)
2034 ud->addPickCallback(new PickCallback(actions[i], getModelRoot()));
2036 // prepare a state set that paints the edges of this object yellow
2037 osg::StateSet* stateSet = highlightGroup->getOrCreateStateSet();
2038 stateSet->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE);
2040 osg::PolygonOffset* polygonOffset = new osg::PolygonOffset;
2041 polygonOffset->setFactor(-1);
2042 polygonOffset->setUnits(-1);
2043 stateSet->setAttribute(polygonOffset, osg::StateAttribute::OVERRIDE);
2044 stateSet->setMode(GL_POLYGON_OFFSET_LINE, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
2046 osg::PolygonMode* polygonMode = new osg::PolygonMode;
2047 polygonMode->setMode(osg::PolygonMode::FRONT_AND_BACK,
2048 osg::PolygonMode::LINE);
2049 stateSet->setAttribute(polygonMode, osg::StateAttribute::OVERRIDE);
2051 osg::Material* material = new osg::Material;
2052 material->setColorMode(osg::Material::OFF);
2053 material->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(1, 1, 0, 1));
2054 material->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(1, 1, 0, 1));
2055 material->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(1, 1, 0, 1));
2056 material->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0, 0, 0, 0));
2057 stateSet->setAttribute(material, osg::StateAttribute::OVERRIDE);
2059 // Only add normal geometry if configured
2060 if (getConfig()->getBoolValue("visible", true))
2061 parent.addChild(normalGroup.get());
2062 parent.addChild(highlightGroup);