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()
15 #include <OpenThreads/Mutex>
16 #include <OpenThreads/ReentrantMutex>
17 #include <OpenThreads/ScopedLock>
19 #include <osg/AlphaFunc>
20 #include <osg/Drawable>
22 #include <osg/Geometry>
25 #include <osg/PolygonMode>
26 #include <osg/PolygonOffset>
27 #include <osg/StateSet>
30 #include <osg/Texture2D>
31 #include <osg/Transform>
32 #include <osgDB/ReadFile>
34 #include <simgear/math/interpolater.hxx>
35 #include <simgear/props/condition.hxx>
36 #include <simgear/props/props.hxx>
37 #include <simgear/structure/SGBinding.hxx>
38 #include <simgear/scene/util/SGNodeMasks.hxx>
39 #include <simgear/scene/util/SGSceneUserData.hxx>
40 #include <simgear/scene/util/SGStateAttributeVisitor.hxx>
42 #include "animation.hxx"
45 #include "SGTranslateTransform.hxx"
46 #include "SGMaterialAnimation.hxx"
47 #include "SGRotateTransform.hxx"
48 #include "SGScaleTransform.hxx"
51 ////////////////////////////////////////////////////////////////////////
52 // Static utility functions.
53 ////////////////////////////////////////////////////////////////////////
56 * Set up the transform matrix for a spin or rotation.
59 set_rotation (osg::Matrix &matrix, double position_deg,
60 const SGVec3d ¢er, const SGVec3d &axis)
62 double temp_angle = -SGMiscd::deg2rad(position_deg);
64 double s = sin(temp_angle);
65 double c = cos(temp_angle);
68 // axis was normalized at load time
69 // hint to the compiler to put these into FP registers
74 matrix(0, 0) = t * x * x + c ;
75 matrix(0, 1) = t * y * x - s * z ;
76 matrix(0, 2) = t * z * x + s * y ;
79 matrix(1, 0) = t * x * y + s * z ;
80 matrix(1, 1) = t * y * y + c ;
81 matrix(1, 2) = t * z * y - s * x ;
84 matrix(2, 0) = t * x * z - s * y ;
85 matrix(2, 1) = t * y * z + s * x ;
86 matrix(2, 2) = t * z * z + c ;
89 // hint to the compiler to put these into FP registers
94 matrix(3, 0) = x - x*matrix(0, 0) - y*matrix(1, 0) - z*matrix(2, 0);
95 matrix(3, 1) = y - x*matrix(0, 1) - y*matrix(1, 1) - z*matrix(2, 1);
96 matrix(3, 2) = z - x*matrix(0, 2) - y*matrix(1, 2) - z*matrix(2, 2);
101 * Set up the transform matrix for a translation.
104 set_translation (osg::Matrix &matrix, double position_m, const SGVec3d &axis)
106 SGVec3d xyz = axis * position_m;
107 matrix.makeIdentity();
108 matrix(3, 0) = xyz[0];
109 matrix(3, 1) = xyz[1];
110 matrix(3, 2) = xyz[2];
114 * Modify property value by step and scroll settings in texture translations
117 apply_mods(double property, double step, double scroll, double bias)
123 double scrollval = 0.0;
125 // calculate scroll amount (for odometer like movement)
126 double remainder = step - fmod(fabs(property), step);
127 if (remainder < scroll) {
128 scrollval = (scroll - remainder) / scroll * step;
131 // apply stepping of input value
133 modprop = ((floor(property/step) * step) + scrollval);
135 modprop = ((ceil(property/step) * step) + scrollval);
144 * Read an interpolation table from properties.
146 static SGInterpTable *
147 read_interpolation_table(const SGPropertyNode* props)
149 const SGPropertyNode* table_node = props->getNode("interpolation");
152 return new SGInterpTable(table_node);
155 ////////////////////////////////////////////////////////////////////////
156 // Utility value classes
157 ////////////////////////////////////////////////////////////////////////
158 class SGScaleOffsetValue : public SGDoubleValue {
160 SGScaleOffsetValue(SGPropertyNode const* propertyNode) :
161 _propertyNode(propertyNode),
164 _min(-SGLimitsd::max()),
165 _max(SGLimitsd::max())
167 void setScale(double scale)
169 void setOffset(double offset)
170 { _offset = offset; }
171 void setMin(double min)
173 void setMax(double max)
176 virtual double getValue() const
178 double value = _propertyNode ? _propertyNode->getDoubleValue() : 0;
179 return std::min(_max, std::max(_min, _offset + _scale*value));
182 SGSharedPtr<SGPropertyNode const> _propertyNode;
189 class SGPersScaleOffsetValue : public SGDoubleValue {
191 SGPersScaleOffsetValue(SGPropertyNode const* propertyNode,
192 SGPropertyNode const* config,
193 const char* scalename, const char* offsetname,
194 double defScale = 1, double defOffset = 0) :
195 _propertyNode(propertyNode),
196 _scale(config, scalename, defScale),
197 _offset(config, offsetname, defOffset),
198 _min(-SGLimitsd::max()),
199 _max(SGLimitsd::max())
201 void setScale(double scale)
203 void setOffset(double offset)
204 { _offset = offset; }
205 void setMin(double min)
207 void setMax(double max)
210 virtual double getValue() const
214 double value = _propertyNode ? _propertyNode->getDoubleValue() : 0;
215 return SGMiscd::clip(_offset + _scale*value, _min, _max);
218 SGSharedPtr<SGPropertyNode const> _propertyNode;
219 mutable SGPersonalityParameter<double> _scale;
220 mutable SGPersonalityParameter<double> _offset;
225 class SGInterpTableValue : public SGDoubleValue {
227 SGInterpTableValue(SGPropertyNode const* propertyNode,
228 SGInterpTable const* interpTable) :
229 _propertyNode(propertyNode),
230 _interpTable(interpTable)
232 virtual double getValue() const
233 { return _interpTable->interpolate(_propertyNode ? _propertyNode->getDoubleValue() : 0); }
235 SGSharedPtr<SGPropertyNode const> _propertyNode;
236 SGSharedPtr<SGInterpTable const> _interpTable;
239 class SGTexScaleOffsetValue : public SGDoubleValue {
241 SGTexScaleOffsetValue(const SGPropertyNode* propertyNode) :
242 _propertyNode(propertyNode),
248 _min(-SGLimitsd::max()),
249 _max(SGLimitsd::max())
251 void setScale(double scale)
253 void setOffset(double offset)
254 { _offset = offset; }
255 void setStep(double step)
257 void setScroll(double scroll)
258 { _scroll = scroll; }
259 void setBias(double bias)
261 void setMin(double min)
263 void setMax(double max)
266 virtual double getValue() const
268 double value = _propertyNode ? _propertyNode->getDoubleValue() : 0;
269 value = apply_mods(value, _step, _scroll, _bias);
270 return SGMiscd::clip(_scale*(_offset + value), _min, _max);
273 SGSharedPtr<const SGPropertyNode> _propertyNode;
283 class SGTexTableValue : public SGDoubleValue {
285 SGTexTableValue(const SGPropertyNode* propertyNode,
286 const SGInterpTable* interpTable) :
287 _propertyNode(propertyNode),
288 _interpTable(interpTable)
290 void setStep(double step)
292 void setScroll(double scroll)
293 { _scroll = scroll; }
294 void setBias(double bias)
296 virtual double getValue() const
298 double value = _propertyNode ? _propertyNode->getDoubleValue() : 0;
299 value = apply_mods(value, _step, _scroll, _bias);
300 return _interpTable->interpolate(value);
303 SGSharedPtr<SGPropertyNode const> _propertyNode;
304 SGSharedPtr<SGInterpTable const> _interpTable;
311 unit_string(const char* value, const char* unit)
313 return std::string(value) + unit;
316 static SGDoubleValue*
317 read_value(const SGPropertyNode* configNode, SGPropertyNode* modelRoot,
318 const char* unit, double defMin, double defMax)
320 std::string inputPropertyName;
321 inputPropertyName = configNode->getStringValue("property", "");
322 if (!inputPropertyName.empty()) {
323 SGPropertyNode* inputProperty;
324 inputProperty = modelRoot->getNode(inputPropertyName.c_str(), true);
325 SGInterpTable* interpTable = read_interpolation_table(configNode);
327 SGInterpTableValue* value;
328 value = new SGInterpTableValue(inputProperty, interpTable);
331 std::string offset = unit_string("offset", unit);
332 std::string min = unit_string("min", unit);
333 std::string max = unit_string("max", unit);
335 if (configNode->getBoolValue("use-personality", false)) {
336 SGPersScaleOffsetValue* value;
337 value = new SGPersScaleOffsetValue(inputProperty, configNode,
338 "factor", offset.c_str());
339 value->setMin(configNode->getDoubleValue(min.c_str(), defMin));
340 value->setMax(configNode->getDoubleValue(max.c_str(), defMax));
343 SGScaleOffsetValue* value = new SGScaleOffsetValue(inputProperty);
344 value->setScale(configNode->getDoubleValue("factor", 1));
345 value->setOffset(configNode->getDoubleValue(offset.c_str(), 0));
346 value->setMin(configNode->getDoubleValue(min.c_str(), defMin));
347 value->setMax(configNode->getDoubleValue(max.c_str(), defMax));
356 ////////////////////////////////////////////////////////////////////////
357 // Animation installer
358 ////////////////////////////////////////////////////////////////////////
360 class SGAnimation::RemoveModeVisitor : public SGStateAttributeVisitor {
362 RemoveModeVisitor(osg::StateAttribute::GLMode mode) :
365 virtual void apply(osg::StateSet* stateSet)
369 stateSet->removeMode(_mode);
372 osg::StateAttribute::GLMode _mode;
375 class SGAnimation::RemoveAttributeVisitor : public SGStateAttributeVisitor {
377 RemoveAttributeVisitor(osg::StateAttribute::Type type) :
380 virtual void apply(osg::StateSet* stateSet)
384 while (stateSet->getAttribute(_type)) {
385 stateSet->removeAttribute(_type);
389 osg::StateAttribute::Type _type;
392 class SGAnimation::RemoveTextureModeVisitor : public SGStateAttributeVisitor {
394 RemoveTextureModeVisitor(unsigned unit, osg::StateAttribute::GLMode mode) :
398 virtual void apply(osg::StateSet* stateSet)
402 stateSet->removeTextureMode(_unit, _mode);
406 osg::StateAttribute::GLMode _mode;
409 class SGAnimation::RemoveTextureAttributeVisitor :
410 public SGStateAttributeVisitor {
412 RemoveTextureAttributeVisitor(unsigned unit,
413 osg::StateAttribute::Type type) :
417 virtual void apply(osg::StateSet* stateSet)
421 while (stateSet->getTextureAttribute(_unit, _type)) {
422 stateSet->removeTextureAttribute(_unit, _type);
427 osg::StateAttribute::Type _type;
430 class SGAnimation::BinToInheritVisitor : public SGStateAttributeVisitor {
432 virtual void apply(osg::StateSet* stateSet)
436 stateSet->setRenderBinToInherit();
440 class SGAnimation::DrawableCloneVisitor : public osg::NodeVisitor {
442 DrawableCloneVisitor() :
443 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
445 void apply(osg::Geode& geode)
447 for (unsigned i = 0 ; i < geode.getNumDrawables(); ++i) {
448 osg::CopyOp copyOp(osg::CopyOp::DEEP_COPY_ALL &
449 ~osg::CopyOp::DEEP_COPY_TEXTURES);
450 geode.setDrawable(i, copyOp(geode.getDrawable(i)));
457 // Set all drawables to not use display lists. OSG will use
458 // glDrawArrays instead.
459 struct DoDrawArraysVisitor : public osg::NodeVisitor {
460 DoDrawArraysVisitor() :
461 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
463 void apply(osg::Geode& geode)
468 for (int i = 0; i < geode.getNumDrawables(); ++i)
469 geode.getDrawable(i)->setUseDisplayList(false);
474 SGAnimation::SGAnimation(const SGPropertyNode* configNode,
475 SGPropertyNode* modelRoot) :
476 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
478 _configNode(configNode),
479 _modelRoot(modelRoot)
481 _name = configNode->getStringValue("name", "");
482 _enableHOT = configNode->getBoolValue("enable-hot", true);
483 _disableShadow = configNode->getBoolValue("disable-shadow", false);
484 std::vector<SGPropertyNode_ptr> objectNames =
485 configNode->getChildren("object-name");
486 for (unsigned i = 0; i < objectNames.size(); ++i)
487 _objectNames.push_back(objectNames[i]->getStringValue());
490 SGAnimation::~SGAnimation()
495 SG_LOG(SG_IO, SG_ALERT, "Could not find at least one of the following"
496 " objects for animation:\n");
497 std::list<std::string>::const_iterator i;
498 for (i = _objectNames.begin(); i != _objectNames.end(); ++i)
499 SG_LOG(SG_IO, SG_ALERT, *i << "\n");
503 SGAnimation::animate(osg::Node* node, const SGPropertyNode* configNode,
504 SGPropertyNode* modelRoot,
505 const osgDB::ReaderWriter::Options* options)
507 std::string type = configNode->getStringValue("type", "none");
508 if (type == "alpha-test") {
509 SGAlphaTestAnimation animInst(configNode, modelRoot);
510 animInst.apply(node);
511 } else if (type == "billboard") {
512 SGBillboardAnimation animInst(configNode, modelRoot);
513 animInst.apply(node);
514 } else if (type == "blend") {
515 SGBlendAnimation animInst(configNode, modelRoot);
516 animInst.apply(node);
517 } else if (type == "dist-scale") {
518 SGDistScaleAnimation animInst(configNode, modelRoot);
519 animInst.apply(node);
520 } else if (type == "flash") {
521 SGFlashAnimation animInst(configNode, modelRoot);
522 animInst.apply(node);
523 } else if (type == "material") {
524 SGMaterialAnimation animInst(configNode, modelRoot);
525 animInst.apply(node);
526 } else if (type == "noshadow") {
527 SGShadowAnimation animInst(configNode, modelRoot);
528 animInst.apply(node);
529 } else if (type == "pick") {
530 SGPickAnimation animInst(configNode, modelRoot);
531 animInst.apply(node);
532 } else if (type == "range") {
533 SGRangeAnimation animInst(configNode, modelRoot);
534 animInst.apply(node);
535 } else if (type == "rotate" || type == "spin") {
536 SGRotateAnimation animInst(configNode, modelRoot);
537 animInst.apply(node);
538 } else if (type == "scale") {
539 SGScaleAnimation animInst(configNode, modelRoot);
540 animInst.apply(node);
541 } else if (type == "select") {
542 SGSelectAnimation animInst(configNode, modelRoot);
543 animInst.apply(node);
544 } else if (type == "shader") {
545 SGShaderAnimation animInst(configNode, modelRoot, options);
546 animInst.apply(node);
547 } else if (type == "textranslate" || type == "texrotate" ||
548 type == "texmultiple") {
549 SGTexTransformAnimation animInst(configNode, modelRoot);
550 animInst.apply(node);
551 } else if (type == "timed") {
552 SGTimedAnimation animInst(configNode, modelRoot);
553 animInst.apply(node);
554 } else if (type == "translate") {
555 SGTranslateAnimation animInst(configNode, modelRoot);
556 animInst.apply(node);
557 } else if (type == "null" || type == "none" || type.empty()) {
558 SGGroupAnimation animInst(configNode, modelRoot);
559 animInst.apply(node);
568 SGAnimation::apply(osg::Node* node)
570 // duh what a special case ...
571 if (_objectNames.empty()) {
572 osg::Group* group = node->asGroup();
574 osg::ref_ptr<osg::Group> animationGroup;
575 installInGroup(std::string(), *group, animationGroup);
582 SGAnimation::install(osg::Node& node)
586 node.setNodeMask( SG_NODEMASK_TERRAIN_BIT | node.getNodeMask());
588 node.setNodeMask(~SG_NODEMASK_TERRAIN_BIT & node.getNodeMask());
590 node.setNodeMask( SG_NODEMASK_CASTSHADOW_BIT | node.getNodeMask());
592 node.setNodeMask(~SG_NODEMASK_CASTSHADOW_BIT & node.getNodeMask());
596 SGAnimation::createAnimationGroup(osg::Group& parent)
598 // default implementation, we do not need a new group
599 // for every animation type. Usually animations that just change
600 // the StateSet of some parts of the model
605 SGAnimation::apply(osg::Group& group)
607 // the trick is to first traverse the children and then
608 // possibly splice in a new group node if required.
609 // Else we end up in a recursive loop where we infinitly insert new
613 // Note that this algorithm preserves the order of the child objects
614 // like they appear in the object-name tags.
615 // The timed animations require this
616 osg::ref_ptr<osg::Group> animationGroup;
617 std::list<std::string>::const_iterator nameIt;
618 for (nameIt = _objectNames.begin(); nameIt != _objectNames.end(); ++nameIt)
619 installInGroup(*nameIt, group, animationGroup);
623 SGAnimation::installInGroup(const std::string& name, osg::Group& group,
624 osg::ref_ptr<osg::Group>& animationGroup)
626 int i = group.getNumChildren() - 1;
627 for (; 0 <= i; --i) {
628 osg::Node* child = group.getChild(i);
630 // Check if this one is already processed
631 if (std::find(_installedAnimations.begin(),
632 _installedAnimations.end(), child)
633 != _installedAnimations.end())
636 if (name.empty() || child->getName() == name) {
637 // fire the installation of the animation
640 // create a group node on demand
641 if (!animationGroup.valid()) {
642 animationGroup = createAnimationGroup(group);
643 // Animation type that does not require a new group,
644 // in this case we can stop and look for the next object
645 if (animationGroup.valid() && !_name.empty())
646 animationGroup->setName(_name);
648 if (animationGroup.valid()) {
649 animationGroup->addChild(child);
650 group.removeChild(i);
653 // store that we already have processed this child node
654 // We can hit this one twice if an animation references some
655 // part of a subtree twice
656 _installedAnimations.push_back(child);
662 SGAnimation::removeMode(osg::Node& node, osg::StateAttribute::GLMode mode)
664 RemoveModeVisitor visitor(mode);
665 node.accept(visitor);
669 SGAnimation::removeAttribute(osg::Node& node, osg::StateAttribute::Type type)
671 RemoveAttributeVisitor visitor(type);
672 node.accept(visitor);
676 SGAnimation::removeTextureMode(osg::Node& node, unsigned unit,
677 osg::StateAttribute::GLMode mode)
679 RemoveTextureModeVisitor visitor(unit, mode);
680 node.accept(visitor);
684 SGAnimation::removeTextureAttribute(osg::Node& node, unsigned unit,
685 osg::StateAttribute::Type type)
687 RemoveTextureAttributeVisitor visitor(unit, type);
688 node.accept(visitor);
692 SGAnimation::setRenderBinToInherit(osg::Node& node)
694 BinToInheritVisitor visitor;
695 node.accept(visitor);
699 SGAnimation::cloneDrawables(osg::Node& node)
701 DrawableCloneVisitor visitor;
702 node.accept(visitor);
706 SGAnimation::getCondition() const
708 const SGPropertyNode* conditionNode = _configNode->getChild("condition");
711 return sgReadCondition(_modelRoot, conditionNode);
716 ////////////////////////////////////////////////////////////////////////
717 // Implementation of null animation
718 ////////////////////////////////////////////////////////////////////////
720 // Ok, that is to build a subgraph from different other
721 // graph nodes. I guess that this stems from the time where modellers
722 // could not build hierarchical trees ...
723 SGGroupAnimation::SGGroupAnimation(const SGPropertyNode* configNode,
724 SGPropertyNode* modelRoot):
725 SGAnimation(configNode, modelRoot)
730 SGGroupAnimation::createAnimationGroup(osg::Group& parent)
732 osg::Group* group = new osg::Group;
733 parent.addChild(group);
738 ////////////////////////////////////////////////////////////////////////
739 // Implementation of translate animation
740 ////////////////////////////////////////////////////////////////////////
742 class SGTranslateAnimation::UpdateCallback : public osg::NodeCallback {
744 UpdateCallback(SGCondition const* condition,
745 SGDoubleValue const* animationValue) :
746 _condition(condition),
747 _animationValue(animationValue)
749 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
751 if (!_condition || _condition->test()) {
752 SGTranslateTransform* transform;
753 transform = static_cast<SGTranslateTransform*>(node);
754 transform->setValue(_animationValue->getValue());
759 SGSharedPtr<SGCondition const> _condition;
760 SGSharedPtr<SGDoubleValue const> _animationValue;
763 SGTranslateAnimation::SGTranslateAnimation(const SGPropertyNode* configNode,
764 SGPropertyNode* modelRoot) :
765 SGAnimation(configNode, modelRoot)
767 _condition = getCondition();
768 _animationValue = read_value(configNode, modelRoot, "-m",
769 -SGLimitsd::max(), SGLimitsd::max());
770 _axis[0] = configNode->getDoubleValue("axis/x", 0);
771 _axis[1] = configNode->getDoubleValue("axis/y", 0);
772 _axis[2] = configNode->getDoubleValue("axis/z", 0);
773 if (8*SGLimitsd::min() < norm(_axis))
774 _axis = normalize(_axis);
776 _initialValue = configNode->getDoubleValue("starting-position-m", 0);
777 _initialValue *= configNode->getDoubleValue("factor", 1);
778 _initialValue += configNode->getDoubleValue("offset-m", 0);
782 SGTranslateAnimation::createAnimationGroup(osg::Group& parent)
784 SGTranslateTransform* transform = new SGTranslateTransform;
785 transform->setName("translate animation");
786 if (_animationValue) {
787 UpdateCallback* uc = new UpdateCallback(_condition, _animationValue);
788 transform->setUpdateCallback(uc);
790 transform->setAxis(_axis);
791 transform->setValue(_initialValue);
792 parent.addChild(transform);
797 ////////////////////////////////////////////////////////////////////////
798 // Implementation of rotate/spin animation
799 ////////////////////////////////////////////////////////////////////////
801 class SGRotateAnimation::UpdateCallback : public osg::NodeCallback {
803 UpdateCallback(SGCondition const* condition,
804 SGDoubleValue const* animationValue) :
805 _condition(condition),
806 _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);
813 transform->setAngleDeg(_animationValue->getValue());
818 SGSharedPtr<SGCondition const> _condition;
819 SGSharedPtr<SGDoubleValue const> _animationValue;
822 class SGRotateAnimation::SpinUpdateCallback : public osg::NodeCallback {
824 SpinUpdateCallback(SGCondition const* condition,
825 SGDoubleValue const* animationValue) :
826 _condition(condition),
827 _animationValue(animationValue),
830 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
832 if (!_condition || _condition->test()) {
833 SGRotateTransform* transform;
834 transform = static_cast<SGRotateTransform*>(node);
836 double t = nv->getFrameStamp()->getReferenceTime();
841 double velocity_rpms = _animationValue->getValue()/60;
842 double angle = transform->getAngleDeg();
843 angle += dt*velocity_rpms*360;
844 angle -= 360*floor(angle/360);
845 transform->setAngleDeg(angle);
850 SGSharedPtr<SGCondition const> _condition;
851 SGSharedPtr<SGDoubleValue const> _animationValue;
855 SGRotateAnimation::SGRotateAnimation(const SGPropertyNode* configNode, SGPropertyNode* modelRoot) :
856 SGAnimation(configNode, modelRoot)
858 std::string type = configNode->getStringValue("type", "");
859 _isSpin = (type == "spin");
861 _condition = getCondition();
862 _animationValue = read_value(configNode, modelRoot, "-deg",
863 -SGLimitsd::max(), SGLimitsd::max());
864 _initialValue = configNode->getDoubleValue("starting-position-deg", 0);
865 _initialValue *= configNode->getDoubleValue("factor", 1);
866 _initialValue += configNode->getDoubleValue("offset-deg", 0);
868 _center = SGVec3d::zeros();
869 if (configNode->hasValue("axis/x1-m")) {
871 v1[0] = configNode->getDoubleValue("axis/x1-m", 0);
872 v1[1] = configNode->getDoubleValue("axis/y1-m", 0);
873 v1[2] = configNode->getDoubleValue("axis/z1-m", 0);
874 v2[0] = configNode->getDoubleValue("axis/x2-m", 0);
875 v2[1] = configNode->getDoubleValue("axis/y2-m", 0);
876 v2[2] = configNode->getDoubleValue("axis/z2-m", 0);
877 _center = 0.5*(v1+v2);
880 _axis[0] = configNode->getDoubleValue("axis/x", 0);
881 _axis[1] = configNode->getDoubleValue("axis/y", 0);
882 _axis[2] = configNode->getDoubleValue("axis/z", 0);
884 if (8*SGLimitsd::min() < norm(_axis))
885 _axis = normalize(_axis);
887 _center[0] = configNode->getDoubleValue("center/x-m", _center[0]);
888 _center[1] = configNode->getDoubleValue("center/y-m", _center[1]);
889 _center[2] = configNode->getDoubleValue("center/z-m", _center[2]);
893 SGRotateAnimation::createAnimationGroup(osg::Group& parent)
895 SGRotateTransform* transform = new SGRotateTransform;
896 transform->setName("rotate animation");
898 SpinUpdateCallback* uc;
899 uc = new SpinUpdateCallback(_condition, _animationValue);
900 transform->setUpdateCallback(uc);
901 } else if (_animationValue) {
902 UpdateCallback* uc = new UpdateCallback(_condition, _animationValue);
903 transform->setUpdateCallback(uc);
905 transform->setCenter(_center);
906 transform->setAxis(_axis);
907 transform->setAngleDeg(_initialValue);
908 parent.addChild(transform);
913 ////////////////////////////////////////////////////////////////////////
914 // Implementation of scale animation
915 ////////////////////////////////////////////////////////////////////////
917 class SGScaleAnimation::UpdateCallback : public osg::NodeCallback {
919 UpdateCallback(const SGCondition* condition,
920 SGSharedPtr<const SGDoubleValue> animationValue[3]) :
921 _condition(condition)
923 _animationValue[0] = animationValue[0];
924 _animationValue[1] = animationValue[1];
925 _animationValue[2] = animationValue[2];
927 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
929 if (!_condition || _condition->test()) {
930 SGScaleTransform* transform;
931 transform = static_cast<SGScaleTransform*>(node);
932 SGVec3d scale(_animationValue[0]->getValue(),
933 _animationValue[1]->getValue(),
934 _animationValue[2]->getValue());
935 transform->setScaleFactor(scale);
940 SGSharedPtr<SGCondition const> _condition;
941 SGSharedPtr<SGDoubleValue const> _animationValue[3];
944 SGScaleAnimation::SGScaleAnimation(const SGPropertyNode* configNode,
945 SGPropertyNode* modelRoot) :
946 SGAnimation(configNode, modelRoot)
948 _condition = getCondition();
950 // default offset/factor for all directions
951 double offset = configNode->getDoubleValue("offset", 0);
952 double factor = configNode->getDoubleValue("factor", 1);
954 std::string inputPropertyName;
955 inputPropertyName = configNode->getStringValue("property", "");
956 SGPropertyNode* inputProperty = 0;
957 if (!inputPropertyName.empty()) {
958 inputProperty = modelRoot->getNode(inputPropertyName.c_str(), true);
960 SGInterpTable* interpTable = read_interpolation_table(configNode);
962 SGInterpTableValue* value;
963 value = new SGInterpTableValue(inputProperty, interpTable);
964 _animationValue[0] = value;
965 _animationValue[1] = value;
966 _animationValue[2] = value;
967 } else if (configNode->getBoolValue("use-personality", false)) {
968 SGPersScaleOffsetValue* value;
969 value = new SGPersScaleOffsetValue(inputProperty, configNode,
970 "x-factor", "x-offset",
972 value->setMin(configNode->getDoubleValue("x-min", 0));
973 value->setMax(configNode->getDoubleValue("x-max", SGLimitsd::max()));
974 _animationValue[0] = value;
975 value = new SGPersScaleOffsetValue(inputProperty, configNode,
976 "y-factor", "y-offset",
978 value->setMin(configNode->getDoubleValue("y-min", 0));
979 value->setMax(configNode->getDoubleValue("y-max", SGLimitsd::max()));
980 _animationValue[1] = value;
981 value = new SGPersScaleOffsetValue(inputProperty, configNode,
982 "z-factor", "z-offset",
984 value->setMin(configNode->getDoubleValue("z-min", 0));
985 value->setMax(configNode->getDoubleValue("z-max", SGLimitsd::max()));
986 _animationValue[2] = value;
988 SGScaleOffsetValue* value = new SGScaleOffsetValue(inputProperty);
989 value->setScale(configNode->getDoubleValue("x-factor", factor));
990 value->setOffset(configNode->getDoubleValue("x-offset", offset));
991 value->setMin(configNode->getDoubleValue("x-min", 0));
992 value->setMax(configNode->getDoubleValue("x-max", SGLimitsd::max()));
993 _animationValue[0] = value;
994 value = new SGScaleOffsetValue(inputProperty);
995 value->setScale(configNode->getDoubleValue("y-factor", factor));
996 value->setOffset(configNode->getDoubleValue("y-offset", offset));
997 value->setMin(configNode->getDoubleValue("y-min", 0));
998 value->setMax(configNode->getDoubleValue("y-max", SGLimitsd::max()));
999 _animationValue[1] = value;
1000 value = new SGScaleOffsetValue(inputProperty);
1001 value->setScale(configNode->getDoubleValue("z-factor", factor));
1002 value->setOffset(configNode->getDoubleValue("z-offset", offset));
1003 value->setMin(configNode->getDoubleValue("z-min", 0));
1004 value->setMax(configNode->getDoubleValue("z-max", SGLimitsd::max()));
1005 _animationValue[2] = value;
1007 _initialValue[0] = configNode->getDoubleValue("x-starting-scale", 1);
1008 _initialValue[0] *= configNode->getDoubleValue("x-factor", factor);
1009 _initialValue[0] += configNode->getDoubleValue("x-offset", offset);
1010 _initialValue[1] = configNode->getDoubleValue("y-starting-scale", 1);
1011 _initialValue[1] *= configNode->getDoubleValue("y-factor", factor);
1012 _initialValue[1] += configNode->getDoubleValue("y-offset", offset);
1013 _initialValue[2] = configNode->getDoubleValue("z-starting-scale", 1);
1014 _initialValue[2] *= configNode->getDoubleValue("z-factor", factor);
1015 _initialValue[2] += configNode->getDoubleValue("z-offset", offset);
1016 _center[0] = configNode->getDoubleValue("center/x-m", 0);
1017 _center[1] = configNode->getDoubleValue("center/y-m", 0);
1018 _center[2] = configNode->getDoubleValue("center/z-m", 0);
1022 SGScaleAnimation::createAnimationGroup(osg::Group& parent)
1024 SGScaleTransform* transform = new SGScaleTransform;
1025 transform->setName("scale animation");
1026 transform->setCenter(_center);
1027 transform->setScaleFactor(_initialValue);
1028 UpdateCallback* uc = new UpdateCallback(_condition, _animationValue);
1029 transform->setUpdateCallback(uc);
1030 parent.addChild(transform);
1035 // Don't create a new state state everytime we need GL_NORMALIZE!
1039 OpenThreads::Mutex normalizeMutex;
1041 osg::StateSet* getNormalizeStateSet()
1043 static osg::ref_ptr<osg::StateSet> normalizeStateSet;
1044 OpenThreads::ScopedLock<OpenThreads::Mutex> lock(normalizeMutex);
1045 if (!normalizeStateSet.valid()) {
1046 normalizeStateSet = new osg::StateSet;
1047 normalizeStateSet->setMode(GL_NORMALIZE, osg::StateAttribute::ON);
1048 normalizeStateSet->setDataVariance(osg::Object::STATIC);
1050 return normalizeStateSet.get();
1054 ////////////////////////////////////////////////////////////////////////
1055 // Implementation of dist scale animation
1056 ////////////////////////////////////////////////////////////////////////
1058 class SGDistScaleAnimation::Transform : public osg::Transform {
1060 Transform(const SGPropertyNode* configNode)
1062 setName(configNode->getStringValue("name", "dist scale animation"));
1063 setReferenceFrame(RELATIVE_RF);
1064 setStateSet(getNormalizeStateSet());
1065 _factor = configNode->getFloatValue("factor", 1);
1066 _offset = configNode->getFloatValue("offset", 0);
1067 _min_v = configNode->getFloatValue("min", SGLimitsf::epsilon());
1068 _max_v = configNode->getFloatValue("max", SGLimitsf::max());
1069 _table = read_interpolation_table(configNode);
1070 _center[0] = configNode->getFloatValue("center/x-m", 0);
1071 _center[1] = configNode->getFloatValue("center/y-m", 0);
1072 _center[2] = configNode->getFloatValue("center/z-m", 0);
1074 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
1075 osg::NodeVisitor* nv) const
1077 osg::Matrix transform;
1078 double scale_factor = computeScaleFactor(nv);
1079 transform(0,0) = scale_factor;
1080 transform(1,1) = scale_factor;
1081 transform(2,2) = scale_factor;
1082 transform(3,0) = _center[0]*(1 - scale_factor);
1083 transform(3,1) = _center[1]*(1 - scale_factor);
1084 transform(3,2) = _center[2]*(1 - scale_factor);
1085 matrix.preMult(transform);
1089 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
1090 osg::NodeVisitor* nv) const
1092 double scale_factor = computeScaleFactor(nv);
1093 if (fabs(scale_factor) <= SGLimits<double>::min())
1095 osg::Matrix transform;
1096 double rScaleFactor = 1/scale_factor;
1097 transform(0,0) = rScaleFactor;
1098 transform(1,1) = rScaleFactor;
1099 transform(2,2) = rScaleFactor;
1100 transform(3,0) = _center[0]*(1 - rScaleFactor);
1101 transform(3,1) = _center[1]*(1 - rScaleFactor);
1102 transform(3,2) = _center[2]*(1 - rScaleFactor);
1103 matrix.postMult(transform);
1108 double computeScaleFactor(osg::NodeVisitor* nv) const
1113 double scale_factor = (_center.osg() - nv->getEyePoint()).length();
1115 scale_factor = _factor * scale_factor + _offset;
1117 scale_factor = _table->interpolate( scale_factor );
1119 if (scale_factor < _min_v)
1120 scale_factor = _min_v;
1121 if (scale_factor > _max_v)
1122 scale_factor = _max_v;
1124 return scale_factor;
1127 SGSharedPtr<SGInterpTable> _table;
1136 SGDistScaleAnimation::SGDistScaleAnimation(const SGPropertyNode* configNode,
1137 SGPropertyNode* modelRoot) :
1138 SGAnimation(configNode, modelRoot)
1143 SGDistScaleAnimation::createAnimationGroup(osg::Group& parent)
1145 Transform* transform = new Transform(getConfig());
1146 parent.addChild(transform);
1151 ////////////////////////////////////////////////////////////////////////
1152 // Implementation of flash animation
1153 ////////////////////////////////////////////////////////////////////////
1155 class SGFlashAnimation::Transform : public osg::Transform {
1157 Transform(const SGPropertyNode* configNode)
1159 setReferenceFrame(RELATIVE_RF);
1160 setName(configNode->getStringValue("name", "flash animation"));
1161 setStateSet(getNormalizeStateSet());
1163 _axis[0] = configNode->getFloatValue("axis/x", 0);
1164 _axis[1] = configNode->getFloatValue("axis/y", 0);
1165 _axis[2] = configNode->getFloatValue("axis/z", 1);
1168 _center[0] = configNode->getFloatValue("center/x-m", 0);
1169 _center[1] = configNode->getFloatValue("center/y-m", 0);
1170 _center[2] = configNode->getFloatValue("center/z-m", 0);
1172 _offset = configNode->getFloatValue("offset", 0);
1173 _factor = configNode->getFloatValue("factor", 1);
1174 _power = configNode->getFloatValue("power", 1);
1175 _two_sides = configNode->getBoolValue("two-sides", false);
1177 _min_v = configNode->getFloatValue("min", SGLimitsf::epsilon());
1178 _max_v = configNode->getFloatValue("max", 1);
1180 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
1181 osg::NodeVisitor* nv) const
1183 osg::Matrix transform;
1184 double scale_factor = computeScaleFactor(nv);
1185 transform(0,0) = scale_factor;
1186 transform(1,1) = scale_factor;
1187 transform(2,2) = scale_factor;
1188 transform(3,0) = _center[0]*(1 - scale_factor);
1189 transform(3,1) = _center[1]*(1 - scale_factor);
1190 transform(3,2) = _center[2]*(1 - scale_factor);
1191 matrix.preMult(transform);
1195 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
1196 osg::NodeVisitor* nv) const
1198 double scale_factor = computeScaleFactor(nv);
1199 if (fabs(scale_factor) <= SGLimits<double>::min())
1201 osg::Matrix transform;
1202 double rScaleFactor = 1/scale_factor;
1203 transform(0,0) = rScaleFactor;
1204 transform(1,1) = rScaleFactor;
1205 transform(2,2) = rScaleFactor;
1206 transform(3,0) = _center[0]*(1 - rScaleFactor);
1207 transform(3,1) = _center[1]*(1 - rScaleFactor);
1208 transform(3,2) = _center[2]*(1 - rScaleFactor);
1209 matrix.postMult(transform);
1214 double computeScaleFactor(osg::NodeVisitor* nv) const
1219 osg::Vec3 localEyeToCenter = nv->getEyePoint() - _center;
1220 localEyeToCenter.normalize();
1222 double cos_angle = localEyeToCenter*_axis;
1223 double scale_factor = 0;
1224 if ( _two_sides && cos_angle < 0 )
1225 scale_factor = _factor * pow( -cos_angle, _power ) + _offset;
1226 else if ( cos_angle > 0 )
1227 scale_factor = _factor * pow( cos_angle, _power ) + _offset;
1229 if ( scale_factor < _min_v )
1230 scale_factor = _min_v;
1231 if ( scale_factor > _max_v )
1232 scale_factor = _max_v;
1234 return scale_factor;
1237 virtual osg::BoundingSphere computeBound() const
1239 // avoid being culled away by small feature culling
1240 osg::BoundingSphere bs = osg::Group::computeBound();
1241 bs.radius() *= _max_v;
1248 double _power, _factor, _offset, _min_v, _max_v;
1253 SGFlashAnimation::SGFlashAnimation(const SGPropertyNode* configNode,
1254 SGPropertyNode* modelRoot) :
1255 SGAnimation(configNode, modelRoot)
1260 SGFlashAnimation::createAnimationGroup(osg::Group& parent)
1262 Transform* transform = new Transform(getConfig());
1263 parent.addChild(transform);
1268 ////////////////////////////////////////////////////////////////////////
1269 // Implementation of flash animation
1270 ////////////////////////////////////////////////////////////////////////
1272 class SGBillboardAnimation::Transform : public osg::Transform {
1274 Transform(const SGPropertyNode* configNode) :
1275 _spherical(configNode->getBoolValue("spherical", true))
1277 setReferenceFrame(RELATIVE_RF);
1278 setName(configNode->getStringValue("name", "billboard animation"));
1280 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
1281 osg::NodeVisitor* nv) const
1283 // More or less taken from plibs ssgCutout
1285 matrix(0,0) = 1; matrix(0,1) = 0; matrix(0,2) = 0;
1286 matrix(1,0) = 0; matrix(1,1) = 0; matrix(1,2) = -1;
1287 matrix(2,0) = 0; matrix(2,1) = 1; matrix(2,2) = 0;
1289 osg::Vec3 zAxis(matrix(2, 0), matrix(2, 1), matrix(2, 2));
1290 osg::Vec3 xAxis = osg::Vec3(0, 0, -1)^zAxis;
1291 osg::Vec3 yAxis = zAxis^xAxis;
1297 matrix(0,0) = xAxis[0]; matrix(0,1) = xAxis[1]; matrix(0,2) = xAxis[2];
1298 matrix(1,0) = yAxis[0]; matrix(1,1) = yAxis[1]; matrix(1,2) = yAxis[2];
1299 matrix(2,0) = zAxis[0]; matrix(2,1) = zAxis[1]; matrix(2,2) = zAxis[2];
1304 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
1305 osg::NodeVisitor* nv) const
1307 // Hmm, don't yet know how to get that back ...
1316 SGBillboardAnimation::SGBillboardAnimation(const SGPropertyNode* configNode,
1317 SGPropertyNode* modelRoot) :
1318 SGAnimation(configNode, modelRoot)
1323 SGBillboardAnimation::createAnimationGroup(osg::Group& parent)
1325 Transform* transform = new Transform(getConfig());
1326 parent.addChild(transform);
1331 ////////////////////////////////////////////////////////////////////////
1332 // Implementation of a range animation
1333 ////////////////////////////////////////////////////////////////////////
1335 class SGRangeAnimation::UpdateCallback : public osg::NodeCallback {
1337 UpdateCallback(const SGCondition* condition,
1338 const SGDoubleValue* minAnimationValue,
1339 const SGDoubleValue* maxAnimationValue,
1340 double minValue, double maxValue) :
1341 _condition(condition),
1342 _minAnimationValue(minAnimationValue),
1343 _maxAnimationValue(maxAnimationValue),
1344 _minStaticValue(minValue),
1345 _maxStaticValue(maxValue)
1347 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1349 osg::LOD* lod = static_cast<osg::LOD*>(node);
1350 if (!_condition || _condition->test()) {
1352 if (_minAnimationValue)
1353 minRange = _minAnimationValue->getValue();
1355 minRange = _minStaticValue;
1357 if (_maxAnimationValue)
1358 maxRange = _maxAnimationValue->getValue();
1360 maxRange = _maxStaticValue;
1361 lod->setRange(0, minRange, maxRange);
1363 lod->setRange(0, 0, SGLimitsf::max());
1369 SGSharedPtr<const SGCondition> _condition;
1370 SGSharedPtr<const SGDoubleValue> _minAnimationValue;
1371 SGSharedPtr<const SGDoubleValue> _maxAnimationValue;
1372 double _minStaticValue;
1373 double _maxStaticValue;
1376 SGRangeAnimation::SGRangeAnimation(const SGPropertyNode* configNode,
1377 SGPropertyNode* modelRoot) :
1378 SGAnimation(configNode, modelRoot)
1380 _condition = getCondition();
1382 std::string inputPropertyName;
1383 inputPropertyName = configNode->getStringValue("min-property", "");
1384 if (!inputPropertyName.empty()) {
1385 SGPropertyNode* inputProperty;
1386 inputProperty = modelRoot->getNode(inputPropertyName.c_str(), true);
1387 SGScaleOffsetValue* value = new SGScaleOffsetValue(inputProperty);
1388 value->setScale(configNode->getDoubleValue("min-factor", 1));
1389 _minAnimationValue = value;
1391 inputPropertyName = configNode->getStringValue("max-property", "");
1392 if (!inputPropertyName.empty()) {
1393 SGPropertyNode* inputProperty;
1394 inputProperty = modelRoot->getNode(inputPropertyName.c_str(), true);
1395 SGScaleOffsetValue* value = new SGScaleOffsetValue(inputProperty);
1396 value->setScale(configNode->getDoubleValue("max-factor", 1));
1397 _maxAnimationValue = value;
1400 _initialValue[0] = configNode->getDoubleValue("min-m", 0);
1401 _initialValue[0] *= configNode->getDoubleValue("min-factor", 1);
1402 _initialValue[1] = configNode->getDoubleValue("max-m", SGLimitsf::max());
1403 _initialValue[1] *= configNode->getDoubleValue("max-factor", 1);
1407 SGRangeAnimation::createAnimationGroup(osg::Group& parent)
1409 osg::Group* group = new osg::Group;
1410 group->setName("range animation group");
1412 osg::LOD* lod = new osg::LOD;
1413 lod->setName("range animation node");
1414 parent.addChild(lod);
1416 lod->addChild(group, _initialValue[0], _initialValue[1]);
1417 lod->setCenterMode(osg::LOD::USE_BOUNDING_SPHERE_CENTER);
1418 lod->setRangeMode(osg::LOD::DISTANCE_FROM_EYE_POINT);
1419 if (_minAnimationValue || _maxAnimationValue || _condition) {
1421 uc = new UpdateCallback(_condition, _minAnimationValue, _maxAnimationValue,
1422 _initialValue[0], _initialValue[1]);
1423 lod->setUpdateCallback(uc);
1429 ////////////////////////////////////////////////////////////////////////
1430 // Implementation of a select animation
1431 ////////////////////////////////////////////////////////////////////////
1433 class SGSelectAnimation::UpdateCallback : public osg::NodeCallback {
1435 UpdateCallback(const SGCondition* condition) :
1436 _condition(condition)
1438 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1440 osg::Switch* sw = static_cast<osg::Switch*>(node);
1441 if (_condition->test())
1442 sw->setAllChildrenOn();
1444 sw->setAllChildrenOff();
1449 SGSharedPtr<SGCondition const> _condition;
1452 SGSelectAnimation::SGSelectAnimation(const SGPropertyNode* configNode,
1453 SGPropertyNode* modelRoot) :
1454 SGAnimation(configNode, modelRoot)
1459 SGSelectAnimation::createAnimationGroup(osg::Group& parent)
1461 // if no condition given, this is a noop.
1462 SGSharedPtr<SGCondition const> condition = getCondition();
1463 // trick, gets deleted with all its 'animated' children
1464 // when the animation installer returns
1466 return new osg::Group;
1468 osg::Switch* sw = new osg::Switch;
1469 sw->setName("select animation node");
1470 sw->setUpdateCallback(new UpdateCallback(condition));
1471 parent.addChild(sw);
1477 ////////////////////////////////////////////////////////////////////////
1478 // Implementation of alpha test animation
1479 ////////////////////////////////////////////////////////////////////////
1481 SGAlphaTestAnimation::SGAlphaTestAnimation(const SGPropertyNode* configNode,
1482 SGPropertyNode* modelRoot) :
1483 SGAnimation(configNode, modelRoot)
1489 // Keep one copy of the most common alpha test its state set.
1490 OpenThreads::ReentrantMutex alphaTestMutex;
1491 osg::ref_ptr<osg::AlphaFunc> standardAlphaFunc;
1492 osg::ref_ptr<osg::StateSet> alphaFuncStateSet;
1494 osg::AlphaFunc* makeAlphaFunc(float clamp)
1496 using namespace OpenThreads;
1497 ScopedLock<ReentrantMutex> lock(alphaTestMutex);
1498 if (osg::equivalent(clamp, 0.01f)) {
1499 if (standardAlphaFunc.valid())
1500 return standardAlphaFunc.get();
1503 osg::AlphaFunc* alphaFunc = new osg::AlphaFunc;
1504 alphaFunc->setFunction(osg::AlphaFunc::GREATER);
1505 alphaFunc->setReferenceValue(clamp);
1506 alphaFunc->setDataVariance(osg::Object::STATIC);
1507 if (osg::equivalent(clamp, 0.01f))
1508 standardAlphaFunc = alphaFunc;
1512 osg::StateSet* makeAlphaTestStateSet(float clamp)
1514 using namespace OpenThreads;
1515 ScopedLock<ReentrantMutex> lock(alphaTestMutex);
1516 if (osg::equivalent(clamp, 0.01f)) {
1517 if (alphaFuncStateSet.valid())
1518 return alphaFuncStateSet.get();
1520 osg::AlphaFunc* alphaFunc = makeAlphaFunc(clamp);
1521 osg::StateSet* stateSet = new osg::StateSet;
1522 stateSet->setAttributeAndModes(alphaFunc,
1523 (osg::StateAttribute::ON
1524 | osg::StateAttribute::OVERRIDE));
1525 stateSet->setDataVariance(osg::Object::STATIC);
1526 if (osg::equivalent(clamp, 0.01f))
1527 alphaFuncStateSet = stateSet;
1532 SGAlphaTestAnimation::install(osg::Node& node)
1534 SGAnimation::install(node);
1536 float alphaClamp = getConfig()->getFloatValue("alpha-factor", 0);
1537 osg::StateSet* stateSet = node.getStateSet();
1539 node.setStateSet(makeAlphaTestStateSet(alphaClamp));
1541 stateSet->setAttributeAndModes(makeAlphaFunc(alphaClamp),
1542 (osg::StateAttribute::ON
1543 | osg::StateAttribute::OVERRIDE));
1548 //////////////////////////////////////////////////////////////////////
1549 // Blend animation installer
1550 //////////////////////////////////////////////////////////////////////
1552 // XXX This needs to be replaced by something using TexEnvCombine to
1553 // change the blend factor. Changing the alpha values in the geometry
1555 class SGBlendAnimation::BlendVisitor : public osg::NodeVisitor {
1557 BlendVisitor(float blend) :
1558 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
1560 { setVisitorType(osg::NodeVisitor::NODE_VISITOR); }
1561 virtual void apply(osg::Node& node)
1563 updateStateSet(node.getStateSet());
1566 virtual void apply(osg::Geode& node)
1568 apply((osg::Node&)node);
1569 unsigned nDrawables = node.getNumDrawables();
1570 for (unsigned i = 0; i < nDrawables; ++i) {
1571 osg::Drawable* drawable = node.getDrawable(i);
1572 osg::Geometry* geometry = drawable->asGeometry();
1575 osg::Array* array = geometry->getColorArray();
1578 osg::Vec4Array* vec4Array = dynamic_cast<osg::Vec4Array*>(array);
1581 for (unsigned k = 0; k < vec4Array->size(); ++k) {
1582 (*vec4Array)[k][3] = _blend;
1585 updateStateSet(drawable->getStateSet());
1588 void updateStateSet(osg::StateSet* stateSet)
1592 osg::StateAttribute* stateAttribute;
1593 stateAttribute = stateSet->getAttribute(osg::StateAttribute::MATERIAL);
1594 if (!stateAttribute)
1596 osg::Material* material = dynamic_cast<osg::Material*>(stateAttribute);
1599 material->setAlpha(osg::Material::FRONT_AND_BACK, _blend);
1601 stateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
1602 stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
1604 stateSet->setRenderingHint(osg::StateSet::DEFAULT_BIN);
1611 class SGBlendAnimation::UpdateCallback : public osg::NodeCallback {
1613 UpdateCallback(const SGPropertyNode* configNode, const SGDoubleValue* v) :
1617 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1619 double blend = _animationValue->getValue();
1620 if (blend != _prev_value) {
1621 _prev_value = blend;
1622 BlendVisitor visitor(1-blend);
1623 node->accept(visitor);
1629 SGSharedPtr<SGDoubleValue const> _animationValue;
1633 SGBlendAnimation::SGBlendAnimation(const SGPropertyNode* configNode,
1634 SGPropertyNode* modelRoot)
1635 : SGAnimation(configNode, modelRoot),
1636 _animationValue(read_value(configNode, modelRoot, "", 0, 1))
1641 SGBlendAnimation::createAnimationGroup(osg::Group& parent)
1643 if (!_animationValue)
1646 osg::Group* group = new osg::Switch;
1647 group->setName("blend animation node");
1648 group->setUpdateCallback(new UpdateCallback(getConfig(), _animationValue));
1649 parent.addChild(group);
1654 SGBlendAnimation::install(osg::Node& node)
1656 SGAnimation::install(node);
1657 // make sure we do not change common geometries,
1658 // that also creates new display lists for these subgeometries.
1659 cloneDrawables(node);
1660 DoDrawArraysVisitor visitor;
1661 node.accept(visitor);
1665 //////////////////////////////////////////////////////////////////////
1666 // Timed animation installer
1667 //////////////////////////////////////////////////////////////////////
1671 class SGTimedAnimation::UpdateCallback : public osg::NodeCallback {
1673 UpdateCallback(const SGPropertyNode* configNode) :
1676 _duration_sec(configNode->getDoubleValue("duration-sec", 1)),
1677 _last_time_sec(SGLimitsd::max()),
1678 _use_personality(configNode->getBoolValue("use-personality", false))
1680 std::vector<SGSharedPtr<SGPropertyNode> > nodes;
1681 nodes = configNode->getChildren("branch-duration-sec");
1682 for (size_t i = 0; i < nodes.size(); ++i) {
1683 unsigned ind = nodes[ i ]->getIndex();
1684 while ( ind >= _durations.size() ) {
1685 _durations.push_back(DurationSpec(_duration_sec));
1687 SGPropertyNode_ptr rNode = nodes[i]->getChild("random");
1689 _durations[ind] = DurationSpec(nodes[ i ]->getDoubleValue());
1691 _durations[ind] = DurationSpec(rNode->getDoubleValue( "min", 0),
1692 rNode->getDoubleValue( "max", 1));
1696 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1698 assert(dynamic_cast<osg::Switch*>(node));
1699 osg::Switch* sw = static_cast<osg::Switch*>(node);
1701 unsigned nChildren = sw->getNumChildren();
1703 // blow up the durations vector to the required size
1704 while (_durations.size() < nChildren) {
1705 _durations.push_back(_duration_sec);
1707 // make sure the current index is an duration that really exists
1708 _current_index = _current_index % nChildren;
1710 // update the time and compute the current systems time value
1711 double t = nv->getFrameStamp()->getReferenceTime();
1712 if (_last_time_sec == SGLimitsd::max()) {
1715 double dt = t - _last_time_sec;
1716 if (_use_personality)
1717 dt *= 1 + 0.2*(0.5 - sg_random());
1722 double currentDuration = _durations[_current_index].get();
1723 while (currentDuration < _reminder) {
1724 _reminder -= currentDuration;
1725 _current_index = (_current_index + 1) % nChildren;
1726 currentDuration = _durations[_current_index].get();
1729 sw->setSingleChildOn(_current_index);
1735 struct DurationSpec {
1736 DurationSpec(double t) :
1737 minTime(SGMiscd::max(0.01, t)),
1738 maxTime(SGMiscd::max(0.01, t))
1740 DurationSpec(double t0, double t1) :
1741 minTime(SGMiscd::max(0.01, t0)),
1742 maxTime(SGMiscd::max(0.01, t1))
1745 { return minTime + sg_random()*(maxTime - minTime); }
1749 std::vector<DurationSpec> _durations;
1750 unsigned _current_index;
1752 double _duration_sec;
1753 double _last_time_sec;
1754 bool _use_personality;
1758 SGTimedAnimation::SGTimedAnimation(const SGPropertyNode* configNode,
1759 SGPropertyNode* modelRoot)
1760 : SGAnimation(configNode, modelRoot)
1765 SGTimedAnimation::createAnimationGroup(osg::Group& parent)
1767 osg::Switch* sw = new osg::Switch;
1768 sw->setName("timed animation node");
1769 sw->setUpdateCallback(new UpdateCallback(getConfig()));
1770 parent.addChild(sw);
1775 ////////////////////////////////////////////////////////////////////////
1776 // dynamically switch on/off shadows
1777 ////////////////////////////////////////////////////////////////////////
1779 class SGShadowAnimation::UpdateCallback : public osg::NodeCallback {
1781 UpdateCallback(const SGCondition* condition) :
1782 _condition(condition)
1784 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1786 if (_condition->test())
1787 node->setNodeMask( SG_NODEMASK_CASTSHADOW_BIT | node->getNodeMask());
1789 node->setNodeMask(~SG_NODEMASK_CASTSHADOW_BIT & node->getNodeMask());
1794 SGSharedPtr<const SGCondition> _condition;
1797 SGShadowAnimation::SGShadowAnimation(const SGPropertyNode* configNode,
1798 SGPropertyNode* modelRoot) :
1799 SGAnimation(configNode, modelRoot)
1804 SGShadowAnimation::createAnimationGroup(osg::Group& parent)
1806 SGSharedPtr<SGCondition const> condition = getCondition();
1810 osg::Group* group = new osg::Group;
1811 group->setName("shadow animation");
1812 group->setUpdateCallback(new UpdateCallback(condition));
1813 parent.addChild(group);
1818 ////////////////////////////////////////////////////////////////////////
1819 // Implementation of SGTexTransformAnimation
1820 ////////////////////////////////////////////////////////////////////////
1822 class SGTexTransformAnimation::Transform : public SGReferenced {
1827 virtual ~Transform()
1829 void setValue(double value)
1831 virtual void transform(osg::Matrix&) = 0;
1836 class SGTexTransformAnimation::Translation :
1837 public SGTexTransformAnimation::Transform {
1839 Translation(const SGVec3d& axis) :
1842 void setValue(double value)
1844 virtual void transform(osg::Matrix& matrix)
1847 set_translation(tmp, _value, _axis);
1848 matrix.preMult(tmp);
1854 class SGTexTransformAnimation::Rotation :
1855 public SGTexTransformAnimation::Transform {
1857 Rotation(const SGVec3d& axis, const SGVec3d& center) :
1861 virtual void transform(osg::Matrix& matrix)
1864 set_rotation(tmp, _value, _center, _axis);
1865 matrix.preMult(tmp);
1872 class SGTexTransformAnimation::UpdateCallback :
1873 public osg::StateAttribute::Callback {
1875 UpdateCallback(const SGCondition* condition) :
1876 _condition(condition)
1878 virtual void operator () (osg::StateAttribute* sa, osg::NodeVisitor*)
1880 if (!_condition || _condition->test()) {
1881 TransformList::const_iterator i;
1882 for (i = _transforms.begin(); i != _transforms.end(); ++i)
1883 i->transform->setValue(i->value->getValue());
1885 assert(dynamic_cast<osg::TexMat*>(sa));
1886 osg::TexMat* texMat = static_cast<osg::TexMat*>(sa);
1887 texMat->getMatrix().makeIdentity();
1888 TransformList::const_iterator i;
1889 for (i = _transforms.begin(); i != _transforms.end(); ++i)
1890 i->transform->transform(texMat->getMatrix());
1892 void appendTransform(Transform* transform, SGDoubleValue* value)
1894 Entry entry = { transform, value };
1895 transform->transform(_matrix);
1896 _transforms.push_back(entry);
1901 SGSharedPtr<Transform> transform;
1902 SGSharedPtr<const SGDoubleValue> value;
1904 typedef std::vector<Entry> TransformList;
1905 TransformList _transforms;
1906 SGSharedPtr<const SGCondition> _condition;
1907 osg::Matrix _matrix;
1910 SGTexTransformAnimation::SGTexTransformAnimation(const SGPropertyNode* configNode,
1911 SGPropertyNode* modelRoot) :
1912 SGAnimation(configNode, modelRoot)
1917 SGTexTransformAnimation::createAnimationGroup(osg::Group& parent)
1919 osg::Group* group = new osg::Group;
1920 group->setName("texture transform group");
1921 osg::StateSet* stateSet = group->getOrCreateStateSet();
1922 osg::TexMat* texMat = new osg::TexMat;
1923 UpdateCallback* updateCallback = new UpdateCallback(getCondition());
1924 // interpret the configs ...
1925 std::string type = getType();
1927 if (type == "textranslate") {
1928 appendTexTranslate(getConfig(), updateCallback);
1929 } else if (type == "texrotate") {
1930 appendTexRotate(getConfig(), updateCallback);
1931 } else if (type == "texmultiple") {
1932 std::vector<SGSharedPtr<SGPropertyNode> > transformConfigs;
1933 transformConfigs = getConfig()->getChildren("transform");
1934 for (unsigned i = 0; i < transformConfigs.size(); ++i) {
1935 std::string subtype = transformConfigs[i]->getStringValue("subtype", "");
1936 if (subtype == "textranslate")
1937 appendTexTranslate(transformConfigs[i], updateCallback);
1938 else if (subtype == "texrotate")
1939 appendTexRotate(transformConfigs[i], updateCallback);
1941 SG_LOG(SG_INPUT, SG_ALERT,
1942 "Ignoring unknown texture transform subtype");
1945 SG_LOG(SG_INPUT, SG_ALERT, "Ignoring unknown texture transform type");
1948 texMat->setUpdateCallback(updateCallback);
1949 stateSet->setTextureAttribute(0, texMat);
1950 parent.addChild(group);
1955 SGTexTransformAnimation::appendTexTranslate(const SGPropertyNode* config,
1956 UpdateCallback* updateCallback)
1958 std::string propertyName = config->getStringValue("property", "/null");
1959 SGPropertyNode* inputNode;
1960 inputNode = getModelRoot()->getNode(propertyName.c_str(), true);
1962 SGDoubleValue* animationValue;
1963 SGInterpTable* table = read_interpolation_table(config);
1965 SGTexTableValue* value;
1966 value = new SGTexTableValue(inputNode, table);
1967 value->setStep(config->getDoubleValue("step", 0));
1968 value->setScroll(config->getDoubleValue("scroll", 0));
1969 value->setBias(config->getDoubleValue("bias", 0));
1970 animationValue = value;
1972 SGTexScaleOffsetValue* value;
1973 value = new SGTexScaleOffsetValue(inputNode);
1974 value->setScale(config->getDoubleValue("factor", 1));
1975 value->setOffset(config->getDoubleValue("offset", 0));
1976 value->setStep(config->getDoubleValue("step", 0));
1977 value->setScroll(config->getDoubleValue("scroll", 0));
1978 value->setBias(config->getDoubleValue("bias", 0));
1979 value->setMin(config->getDoubleValue("min", -SGLimitsd::max()));
1980 value->setMax(config->getDoubleValue("max", SGLimitsd::max()));
1981 animationValue = value;
1983 SGVec3d axis(config->getDoubleValue("axis/x", 0),
1984 config->getDoubleValue("axis/y", 0),
1985 config->getDoubleValue("axis/z", 0));
1986 Translation* translation;
1987 translation = new Translation(normalize(axis));
1988 translation->setValue(config->getDoubleValue("starting-position", 0));
1989 updateCallback->appendTransform(translation, animationValue);
1993 SGTexTransformAnimation::appendTexRotate(const SGPropertyNode* config,
1994 UpdateCallback* updateCallback)
1996 std::string propertyName = config->getStringValue("property", "/null");
1997 SGPropertyNode* inputNode;
1998 inputNode = getModelRoot()->getNode(propertyName.c_str(), true);
2000 SGDoubleValue* animationValue;
2001 SGInterpTable* table = read_interpolation_table(config);
2003 SGTexTableValue* value;
2004 value = new SGTexTableValue(inputNode, table);
2005 value->setStep(config->getDoubleValue("step", 0));
2006 value->setScroll(config->getDoubleValue("scroll", 0));
2007 value->setBias(config->getDoubleValue("bias", 0));
2008 animationValue = value;
2010 SGTexScaleOffsetValue* value;
2011 value = new SGTexScaleOffsetValue(inputNode);
2012 value->setScale(config->getDoubleValue("factor", 1));
2013 value->setOffset(config->getDoubleValue("offset-deg", 0));
2014 value->setStep(config->getDoubleValue("step", 0));
2015 value->setScroll(config->getDoubleValue("scroll", 0));
2016 value->setBias(config->getDoubleValue("bias", 0));
2017 value->setMin(config->getDoubleValue("min-deg", -SGLimitsd::max()));
2018 value->setMax(config->getDoubleValue("max-deg", SGLimitsd::max()));
2019 animationValue = value;
2021 SGVec3d axis(config->getDoubleValue("axis/x", 0),
2022 config->getDoubleValue("axis/y", 0),
2023 config->getDoubleValue("axis/z", 0));
2024 SGVec3d center(config->getDoubleValue("center/x", 0),
2025 config->getDoubleValue("center/y", 0),
2026 config->getDoubleValue("center/z", 0));
2028 rotation = new Rotation(normalize(axis), center);
2029 rotation->setValue(config->getDoubleValue("starting-position-deg", 0));
2030 updateCallback->appendTransform(rotation, animationValue);
2034 ////////////////////////////////////////////////////////////////////////
2035 // Implementation of SGPickAnimation
2036 ////////////////////////////////////////////////////////////////////////
2038 class SGPickAnimation::PickCallback : public SGPickCallback {
2040 PickCallback(const SGPropertyNode* configNode,
2041 SGPropertyNode* modelRoot) :
2042 _button(configNode->getIntValue("button", -1)),
2043 _repeatable(configNode->getBoolValue("repeatable", false)),
2044 _repeatInterval(configNode->getDoubleValue("interval-sec", 0.1))
2046 SG_LOG(SG_INPUT, SG_DEBUG, "Reading all bindings");
2047 std::vector<SGPropertyNode_ptr> bindings;
2048 bindings = configNode->getChildren("binding");
2049 for (unsigned int i = 0; i < bindings.size(); ++i) {
2050 _bindingsDown.push_back(new SGBinding(bindings[i], modelRoot));
2053 const SGPropertyNode* upNode = configNode->getChild("mod-up");
2056 bindings = upNode->getChildren("binding");
2057 for (unsigned int i = 0; i < bindings.size(); ++i) {
2058 _bindingsUp.push_back(new SGBinding(bindings[i], modelRoot));
2061 virtual bool buttonPressed(int button, const Info&)
2063 if (0 <= _button && button != _button)
2065 SGBindingList::const_iterator i;
2066 for (i = _bindingsDown.begin(); i != _bindingsDown.end(); ++i)
2071 virtual void buttonReleased(void)
2073 SGBindingList::const_iterator i;
2074 for (i = _bindingsUp.begin(); i != _bindingsUp.end(); ++i)
2077 virtual void update(double dt)
2083 while (_repeatInterval < _repeatTime) {
2084 _repeatTime -= _repeatInterval;
2085 SGBindingList::const_iterator i;
2086 for (i = _bindingsDown.begin(); i != _bindingsDown.end(); ++i)
2091 SGBindingList _bindingsDown;
2092 SGBindingList _bindingsUp;
2095 double _repeatInterval;
2099 SGPickAnimation::SGPickAnimation(const SGPropertyNode* configNode,
2100 SGPropertyNode* modelRoot) :
2101 SGAnimation(configNode, modelRoot)
2106 SGPickAnimation::createAnimationGroup(osg::Group& parent)
2108 osg::Group* commonGroup = new osg::Group;
2110 // Contains the normal geometry that is interactive
2111 osg::ref_ptr<osg::Group> normalGroup = new osg::Group;
2112 normalGroup->addChild(commonGroup);
2114 // Used to render the geometry with just yellow edges
2115 osg::Group* highlightGroup = new osg::Group;
2116 highlightGroup->setNodeMask(SG_NODEMASK_PICK_BIT);
2117 highlightGroup->addChild(commonGroup);
2118 SGSceneUserData* ud;
2119 ud = SGSceneUserData::getOrCreateSceneUserData(highlightGroup);
2120 std::vector<SGPropertyNode_ptr> actions;
2121 actions = getConfig()->getChildren("action");
2122 for (unsigned int i = 0; i < actions.size(); ++i)
2123 ud->addPickCallback(new PickCallback(actions[i], getModelRoot()));
2125 // prepare a state set that paints the edges of this object yellow
2126 osg::StateSet* stateSet = highlightGroup->getOrCreateStateSet();
2127 stateSet->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE);
2129 osg::PolygonOffset* polygonOffset = new osg::PolygonOffset;
2130 polygonOffset->setFactor(-1);
2131 polygonOffset->setUnits(-1);
2132 stateSet->setAttribute(polygonOffset, osg::StateAttribute::OVERRIDE);
2133 stateSet->setMode(GL_POLYGON_OFFSET_LINE, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
2135 osg::PolygonMode* polygonMode = new osg::PolygonMode;
2136 polygonMode->setMode(osg::PolygonMode::FRONT_AND_BACK,
2137 osg::PolygonMode::LINE);
2138 stateSet->setAttribute(polygonMode, osg::StateAttribute::OVERRIDE);
2140 osg::Material* material = new osg::Material;
2141 material->setColorMode(osg::Material::OFF);
2142 material->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(1, 1, 0, 1));
2143 material->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(1, 1, 0, 1));
2144 material->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(1, 1, 0, 1));
2145 material->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0, 0, 0, 0));
2146 stateSet->setAttribute(material, osg::StateAttribute::OVERRIDE);
2148 // Only add normal geometry if configured
2149 if (getConfig()->getBoolValue("visible", true))
2150 parent.addChild(normalGroup.get());
2151 parent.addChild(highlightGroup);