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/Atomic>
16 #include <OpenThreads/Mutex>
17 #include <OpenThreads/ReentrantMutex>
18 #include <OpenThreads/ScopedLock>
20 #include <osg/AlphaFunc>
21 #include <osg/Drawable>
23 #include <osg/Geometry>
27 #include <osg/StateSet>
30 #include <osg/Texture2D>
31 #include <osg/Transform>
32 #include <osg/Uniform>
33 #include <osgDB/ReadFile>
34 #include <osgDB/Registry>
35 #include <osgDB/Input>
36 #include <osgDB/ParameterOutput>
39 #include <simgear/math/interpolater.hxx>
40 #include <simgear/props/condition.hxx>
41 #include <simgear/props/props.hxx>
42 #include <simgear/scene/material/EffectGeode.hxx>
43 #include <simgear/scene/material/EffectCullVisitor.hxx>
44 #include <simgear/scene/util/DeletionManager.hxx>
45 #include <simgear/scene/util/OsgMath.hxx>
46 #include <simgear/scene/util/SGNodeMasks.hxx>
47 #include <simgear/scene/util/SGSceneUserData.hxx>
48 #include <simgear/scene/util/SGStateAttributeVisitor.hxx>
49 #include <simgear/scene/util/StateAttributeFactory.hxx>
51 #include "animation.hxx"
54 #include "SGTranslateTransform.hxx"
55 #include "SGMaterialAnimation.hxx"
56 #include "SGRotateTransform.hxx"
57 #include "SGScaleTransform.hxx"
58 #include "SGInteractionAnimation.hxx"
59 #include "SGPickAnimation.hxx"
60 #include "SGTrackToAnimation.hxx"
62 #include "ConditionNode.hxx"
64 using OpenThreads::Mutex;
65 using OpenThreads::ReentrantMutex;
66 using OpenThreads::ScopedLock;
68 using namespace simgear;
69 ////////////////////////////////////////////////////////////////////////
70 // Static utility functions.
71 ////////////////////////////////////////////////////////////////////////
74 * Set up the transform matrix for a translation.
77 set_translation (osg::Matrix &matrix, double position_m, const SGVec3d &axis)
79 SGVec3d xyz = axis * position_m;
80 matrix.makeIdentity();
81 matrix(3, 0) = xyz[0];
82 matrix(3, 1) = xyz[1];
83 matrix(3, 2) = xyz[2];
87 * Read an interpolation table from properties.
89 static SGInterpTable *
90 read_interpolation_table(const SGPropertyNode* props)
92 const SGPropertyNode* table_node = props->getNode("interpolation");
95 return new SGInterpTable(table_node);
99 unit_string(const char* value, const char* unit)
101 return std::string(value) + unit;
104 class SGPersonalityScaleOffsetExpression : public SGUnaryExpression<double> {
106 SGPersonalityScaleOffsetExpression(SGExpression<double>* expr,
107 SGPropertyNode const* config,
108 const std::string& scalename,
109 const std::string& offsetname,
111 double defOffset = 0) :
112 SGUnaryExpression<double>(expr),
113 _scale(config, scalename.c_str(), defScale),
114 _offset(config, offsetname.c_str(), defOffset)
116 void setScale(double scale)
118 void setOffset(double offset)
119 { _offset = offset; }
121 virtual void eval(double& value, const simgear::expression::Binding* b) const
125 value = _offset + _scale*getOperand()->getValue(b);
128 virtual bool isConst() const { return false; }
131 mutable SGPersonalityParameter<double> _scale;
132 mutable SGPersonalityParameter<double> _offset;
136 static SGExpressiond*
137 read_factor_offset(const SGPropertyNode* configNode, SGExpressiond* expr,
138 const std::string& factor, const std::string& offset)
140 double factorValue = configNode->getDoubleValue(factor, 1);
141 if (factorValue != 1)
142 expr = new SGScaleExpression<double>(expr, factorValue);
143 double offsetValue = configNode->getDoubleValue(offset, 0);
144 if (offsetValue != 0)
145 expr = new SGBiasExpression<double>(expr, offsetValue);
149 static SGExpressiond*
150 read_offset_factor(const SGPropertyNode* configNode, SGExpressiond* expr,
151 const std::string& factor, const std::string& offset)
153 double offsetValue = configNode->getDoubleValue(offset, 0);
154 if (offsetValue != 0)
155 expr = new SGBiasExpression<double>(expr, offsetValue);
156 double factorValue = configNode->getDoubleValue(factor, 1);
157 if (factorValue != 1)
158 expr = new SGScaleExpression<double>(expr, factorValue);
163 read_value(const SGPropertyNode* configNode, SGPropertyNode* modelRoot,
164 const char* unit, double defMin, double defMax)
166 const SGPropertyNode * expression = configNode->getNode( "expression" );
167 if( expression != NULL )
168 return SGReadDoubleExpression( modelRoot, expression->getChild(0) );
170 SGExpression<double>* value = 0;
172 std::string inputPropertyName = configNode->getStringValue("property", "");
173 if (inputPropertyName.empty()) {
174 std::string spos = unit_string("starting-position", unit);
175 double initPos = configNode->getDoubleValue(spos, 0);
176 value = new SGConstExpression<double>(initPos);
178 SGPropertyNode* inputProperty;
179 inputProperty = modelRoot->getNode(inputPropertyName, true);
180 value = new SGPropertyExpression<double>(inputProperty);
183 SGInterpTable* interpTable = read_interpolation_table(configNode);
185 return new SGInterpTableExpression<double>(value, interpTable);
187 std::string offset = unit_string("offset", unit);
188 std::string min = unit_string("min", unit);
189 std::string max = unit_string("max", unit);
191 if (configNode->getBoolValue("use-personality", false)) {
192 value = new SGPersonalityScaleOffsetExpression(value, configNode,
195 value = read_factor_offset(configNode, value, "factor", offset);
198 double minClip = configNode->getDoubleValue(min, defMin);
199 double maxClip = configNode->getDoubleValue(max, defMax);
200 if (minClip > SGMiscd::min(SGLimitsd::min(), -SGLimitsd::max()) ||
201 maxClip < SGLimitsd::max())
202 value = new SGClipExpression<double>(value, minClip, maxClip);
209 ////////////////////////////////////////////////////////////////////////
210 // Animation installer
211 ////////////////////////////////////////////////////////////////////////
213 class SGAnimation::RemoveModeVisitor : public SGStateAttributeVisitor {
215 RemoveModeVisitor(osg::StateAttribute::GLMode mode) :
218 virtual void apply(osg::StateSet* stateSet)
222 stateSet->removeMode(_mode);
225 osg::StateAttribute::GLMode _mode;
228 class SGAnimation::RemoveAttributeVisitor : public SGStateAttributeVisitor {
230 RemoveAttributeVisitor(osg::StateAttribute::Type type) :
233 virtual void apply(osg::StateSet* stateSet)
237 while (stateSet->getAttribute(_type)) {
238 stateSet->removeAttribute(_type);
242 osg::StateAttribute::Type _type;
245 class SGAnimation::RemoveTextureModeVisitor : public SGStateAttributeVisitor {
247 RemoveTextureModeVisitor(unsigned unit, osg::StateAttribute::GLMode mode) :
251 virtual void apply(osg::StateSet* stateSet)
255 stateSet->removeTextureMode(_unit, _mode);
259 osg::StateAttribute::GLMode _mode;
262 class SGAnimation::RemoveTextureAttributeVisitor :
263 public SGStateAttributeVisitor {
265 RemoveTextureAttributeVisitor(unsigned unit,
266 osg::StateAttribute::Type type) :
270 virtual void apply(osg::StateSet* stateSet)
274 while (stateSet->getTextureAttribute(_unit, _type)) {
275 stateSet->removeTextureAttribute(_unit, _type);
280 osg::StateAttribute::Type _type;
283 class SGAnimation::BinToInheritVisitor : public SGStateAttributeVisitor {
285 virtual void apply(osg::StateSet* stateSet)
289 stateSet->setRenderBinToInherit();
293 class SGAnimation::DrawableCloneVisitor : public osg::NodeVisitor {
295 DrawableCloneVisitor() :
296 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
298 void apply(osg::Geode& geode)
300 for (unsigned i = 0 ; i < geode.getNumDrawables(); ++i) {
301 osg::CopyOp copyOp(osg::CopyOp::DEEP_COPY_ALL &
302 ~osg::CopyOp::DEEP_COPY_TEXTURES);
303 geode.setDrawable(i, copyOp(geode.getDrawable(i)));
310 // Set all drawables to not use display lists. OSG will use
311 // glDrawArrays instead.
312 struct DoDrawArraysVisitor : public osg::NodeVisitor {
313 DoDrawArraysVisitor() :
314 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
316 void apply(osg::Geode& geode)
321 for (int i = 0; i < (int)geode.getNumDrawables(); ++i)
322 geode.getDrawable(i)->setUseDisplayList(false);
327 SGAnimation::SGAnimation(const SGPropertyNode* configNode,
328 SGPropertyNode* modelRoot) :
329 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
331 _configNode(configNode),
332 _modelRoot(modelRoot)
334 _name = configNode->getStringValue("name", "");
335 _enableHOT = configNode->getBoolValue("enable-hot", true);
336 std::vector<SGPropertyNode_ptr> objectNames =
337 configNode->getChildren("object-name");
338 for (unsigned i = 0; i < objectNames.size(); ++i)
339 _objectNames.push_back(objectNames[i]->getStringValue());
342 SGAnimation::~SGAnimation()
346 std::list<std::string>::const_iterator i;
348 for (i = _objectNames.begin(); i != _objectNames.end(); ++i)
358 SG_LOG(SG_IO, SG_ALERT, "Could not find at least one of the following"
359 " objects for animation: " << info);
365 SGAnimation::animate(osg::Node* node, const SGPropertyNode* configNode,
366 SGPropertyNode* modelRoot,
367 const osgDB::Options* options,
368 const string &path, int i)
370 std::string type = configNode->getStringValue("type", "none");
371 if (type == "alpha-test") {
372 SGAlphaTestAnimation animInst(configNode, modelRoot);
373 animInst.apply(node);
374 } else if (type == "billboard") {
375 SGBillboardAnimation animInst(configNode, modelRoot);
376 animInst.apply(node);
377 } else if (type == "blend") {
378 SGBlendAnimation animInst(configNode, modelRoot);
379 animInst.apply(node);
380 } else if (type == "dist-scale") {
381 SGDistScaleAnimation animInst(configNode, modelRoot);
382 animInst.apply(node);
383 } else if (type == "flash") {
384 SGFlashAnimation animInst(configNode, modelRoot);
385 animInst.apply(node);
386 } else if (type == "interaction") {
387 SGInteractionAnimation animInst(configNode, modelRoot);
388 animInst.apply(node);
389 } else if (type == "material") {
390 SGMaterialAnimation animInst(configNode, modelRoot, options, path);
391 animInst.apply(node);
392 } else if (type == "noshadow") {
393 SGShadowAnimation animInst(configNode, modelRoot);
394 animInst.apply(node);
395 } else if (type == "pick") {
396 SGPickAnimation animInst(configNode, modelRoot);
397 animInst.apply(node);
398 } else if (type == "knob") {
399 SGKnobAnimation animInst(configNode, modelRoot);
400 animInst.apply(node);
401 } else if (type == "slider") {
402 SGSliderAnimation animInst(configNode, modelRoot);
403 animInst.apply(node);
404 } else if (type == "range") {
405 SGRangeAnimation animInst(configNode, modelRoot);
406 animInst.apply(node);
407 } else if (type == "rotate" || type == "spin") {
408 SGRotateAnimation animInst(configNode, modelRoot);
409 animInst.apply(node);
410 } else if (type == "scale") {
411 SGScaleAnimation animInst(configNode, modelRoot);
412 animInst.apply(node);
413 } else if (type == "select") {
414 SGSelectAnimation animInst(configNode, modelRoot);
415 animInst.apply(node);
416 } else if (type == "shader") {
417 SGShaderAnimation animInst(configNode, modelRoot, options);
418 animInst.apply(node);
419 } else if (type == "textranslate" || type == "texrotate" ||
420 type == "texmultiple") {
421 SGTexTransformAnimation animInst(configNode, modelRoot);
422 animInst.apply(node);
423 } else if (type == "timed") {
424 SGTimedAnimation animInst(configNode, modelRoot);
425 animInst.apply(node);
426 } else if (type == "locked-track") {
427 SGTrackToAnimation animInst(node, configNode, modelRoot);
428 animInst.apply(node);
429 } else if (type == "translate") {
430 SGTranslateAnimation animInst(configNode, modelRoot);
431 animInst.apply(node);
432 } else if (type == "light") {
433 SGLightAnimation animInst(configNode, modelRoot, options, path, i);
434 animInst.apply(node);
435 } else if (type == "null" || type == "none" || type.empty()) {
436 SGGroupAnimation animInst(configNode, modelRoot);
437 animInst.apply(node);
446 SGAnimation::apply(osg::Node* node)
448 // duh what a special case ...
449 if (_objectNames.empty()) {
450 osg::Group* group = node->asGroup();
452 osg::ref_ptr<osg::Group> animationGroup;
453 installInGroup(std::string(), *group, animationGroup);
460 SGAnimation::install(osg::Node& node)
464 node.setNodeMask( SG_NODEMASK_TERRAIN_BIT | node.getNodeMask());
466 node.setNodeMask(~SG_NODEMASK_TERRAIN_BIT & node.getNodeMask());
470 SGAnimation::createAnimationGroup(osg::Group& parent)
472 // default implementation, we do not need a new group
473 // for every animation type. Usually animations that just change
474 // the StateSet of some parts of the model
479 SGAnimation::apply(osg::Group& group)
481 // the trick is to first traverse the children and then
482 // possibly splice in a new group node if required.
483 // Else we end up in a recursive loop where we infinitly insert new
487 // Note that this algorithm preserves the order of the child objects
488 // like they appear in the object-name tags.
489 // The timed animations require this
490 osg::ref_ptr<osg::Group> animationGroup;
491 std::list<std::string>::const_iterator nameIt;
492 for (nameIt = _objectNames.begin(); nameIt != _objectNames.end(); ++nameIt)
493 installInGroup(*nameIt, group, animationGroup);
497 SGAnimation::installInGroup(const std::string& name, osg::Group& group,
498 osg::ref_ptr<osg::Group>& animationGroup)
500 int i = group.getNumChildren() - 1;
501 for (; 0 <= i; --i) {
502 osg::Node* child = group.getChild(i);
504 // Check if this one is already processed
505 if (std::find(_installedAnimations.begin(),
506 _installedAnimations.end(), child)
507 != _installedAnimations.end())
510 if (name.empty() || child->getName() == name) {
511 // fire the installation of the animation
514 // create a group node on demand
515 if (!animationGroup.valid()) {
516 animationGroup = createAnimationGroup(group);
517 // Animation type that does not require a new group,
518 // in this case we can stop and look for the next object
519 if (animationGroup.valid() && !_name.empty())
520 animationGroup->setName(_name);
522 if (animationGroup.valid()) {
523 animationGroup->addChild(child);
524 group.removeChild(i);
527 // store that we already have processed this child node
528 // We can hit this one twice if an animation references some
529 // part of a subtree twice
530 _installedAnimations.push_back(child);
535 //------------------------------------------------------------------------------
536 SGVec3d SGAnimation::readVec3( const std::string& name,
537 const std::string& suffix,
538 const SGVec3d& def ) const
541 vec[0] = _configNode->getDoubleValue(name + "/x" + suffix, def.x());
542 vec[1] = _configNode->getDoubleValue(name + "/y" + suffix, def.y());
543 vec[2] = _configNode->getDoubleValue(name + "/z" + suffix, def.z());
547 //------------------------------------------------------------------------------
548 // factored out to share with SGKnobAnimation
549 void SGAnimation::readRotationCenterAndAxis( SGVec3d& center,
550 SGVec3d& axis ) const
552 center = SGVec3d::zeros();
553 if( _configNode->hasValue("axis/x1-m") )
555 SGVec3d v1 = readVec3("axis", "1-m"), // axis/[xyz]1-m
556 v2 = readVec3("axis", "2-m"); // axis/[xyz]2-m
557 center = 0.5*(v1+v2);
562 axis = readVec3("axis");
564 if( 8 * SGLimitsd::min() < norm(axis) )
565 axis = normalize(axis);
567 center = readVec3("center", "-m", center);
570 //------------------------------------------------------------------------------
571 SGExpressiond* SGAnimation::readOffsetValue(const char* tag_name) const
573 const SGPropertyNode* node = _configNode->getChild(tag_name);
577 SGExpressiond_ref expression;
578 if( !node->nChildren() )
579 expression = new SGConstExpression<double>(node->getDoubleValue());
581 expression = SGReadDoubleExpression(_modelRoot, node->getChild(0));
586 expression = expression->simplify();
588 if( expression->isConst() && expression->getValue() == 0 )
591 return expression.release();
595 SGAnimation::removeMode(osg::Node& node, osg::StateAttribute::GLMode mode)
597 RemoveModeVisitor visitor(mode);
598 node.accept(visitor);
602 SGAnimation::removeAttribute(osg::Node& node, osg::StateAttribute::Type type)
604 RemoveAttributeVisitor visitor(type);
605 node.accept(visitor);
609 SGAnimation::removeTextureMode(osg::Node& node, unsigned unit,
610 osg::StateAttribute::GLMode mode)
612 RemoveTextureModeVisitor visitor(unit, mode);
613 node.accept(visitor);
617 SGAnimation::removeTextureAttribute(osg::Node& node, unsigned unit,
618 osg::StateAttribute::Type type)
620 RemoveTextureAttributeVisitor visitor(unit, type);
621 node.accept(visitor);
625 SGAnimation::setRenderBinToInherit(osg::Node& node)
627 BinToInheritVisitor visitor;
628 node.accept(visitor);
632 SGAnimation::cloneDrawables(osg::Node& node)
634 DrawableCloneVisitor visitor;
635 node.accept(visitor);
639 SGAnimation::getCondition() const
641 const SGPropertyNode* conditionNode = _configNode->getChild("condition");
644 return sgReadCondition(_modelRoot, conditionNode);
649 ////////////////////////////////////////////////////////////////////////
650 // Implementation of null animation
651 ////////////////////////////////////////////////////////////////////////
653 // Ok, that is to build a subgraph from different other
654 // graph nodes. I guess that this stems from the time where modellers
655 // could not build hierarchical trees ...
656 SGGroupAnimation::SGGroupAnimation(const SGPropertyNode* configNode,
657 SGPropertyNode* modelRoot):
658 SGAnimation(configNode, modelRoot)
663 SGGroupAnimation::createAnimationGroup(osg::Group& parent)
665 osg::Group* group = new osg::Group;
666 parent.addChild(group);
671 ////////////////////////////////////////////////////////////////////////
672 // Implementation of translate animation
673 ////////////////////////////////////////////////////////////////////////
675 class SGTranslateAnimation::UpdateCallback : public osg::NodeCallback {
677 UpdateCallback(SGCondition const* condition,
678 SGExpressiond const* animationValue) :
679 _condition(condition),
680 _animationValue(animationValue)
682 setName("SGTranslateAnimation::UpdateCallback");
684 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
686 if (!_condition || _condition->test()) {
687 SGTranslateTransform* transform;
688 transform = static_cast<SGTranslateTransform*>(node);
689 transform->setValue(_animationValue->getValue());
694 SGSharedPtr<SGCondition const> _condition;
695 SGSharedPtr<SGExpressiond const> _animationValue;
698 SGTranslateAnimation::SGTranslateAnimation(const SGPropertyNode* configNode,
699 SGPropertyNode* modelRoot) :
700 SGAnimation(configNode, modelRoot)
702 _condition = getCondition();
703 SGSharedPtr<SGExpressiond> value;
704 value = read_value(configNode, modelRoot, "-m",
705 -SGLimitsd::max(), SGLimitsd::max());
706 _animationValue = value->simplify();
708 _initialValue = _animationValue->getValue();
712 _axis = readTranslateAxis(configNode);
716 SGTranslateAnimation::createAnimationGroup(osg::Group& parent)
718 SGTranslateTransform* transform = new SGTranslateTransform;
719 transform->setName("translate animation");
720 if (_animationValue && !_animationValue->isConst()) {
721 UpdateCallback* uc = new UpdateCallback(_condition, _animationValue);
722 transform->setUpdateCallback(uc);
724 transform->setAxis(_axis);
725 transform->setValue(_initialValue);
726 parent.addChild(transform);
731 ////////////////////////////////////////////////////////////////////////
732 // Implementation of rotate/spin animation
733 ////////////////////////////////////////////////////////////////////////
735 class SGRotAnimTransform : public SGRotateTransform
738 SGRotAnimTransform();
739 SGRotAnimTransform(const SGRotAnimTransform&,
740 const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY);
741 META_Node(simgear, SGRotAnimTransform);
742 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
743 osg::NodeVisitor* nv) const;
744 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
745 osg::NodeVisitor* nv) const;
746 SGSharedPtr<SGCondition const> _condition;
747 SGSharedPtr<SGExpressiond const> _animationValue;
748 // used when condition is false
749 mutable double _lastAngle;
752 SGRotAnimTransform::SGRotAnimTransform()
757 SGRotAnimTransform::SGRotAnimTransform(const SGRotAnimTransform& rhs,
758 const osg::CopyOp& copyop)
759 : SGRotateTransform(rhs, copyop), _condition(rhs._condition),
760 _animationValue(rhs._animationValue), _lastAngle(rhs._lastAngle)
764 bool SGRotAnimTransform::computeLocalToWorldMatrix(osg::Matrix& matrix,
765 osg::NodeVisitor* nv) const
768 if (!_condition || _condition->test()) {
769 angle = _animationValue->getValue();
774 double angleRad = SGMiscd::deg2rad(angle);
775 if (_referenceFrame == RELATIVE_RF) {
778 set_rotation(tmp, angleRad, getCenter(), getAxis());
782 SGRotateTransform::set_rotation(tmp, angleRad, getCenter(), getAxis());
788 bool SGRotAnimTransform::computeWorldToLocalMatrix(osg::Matrix& matrix,
789 osg::NodeVisitor* nv) const
792 if (!_condition || _condition->test()) {
793 angle = _animationValue->getValue();
798 double angleRad = SGMiscd::deg2rad(angle);
799 if (_referenceFrame == RELATIVE_RF) {
802 set_rotation(tmp, -angleRad, getCenter(), getAxis());
803 matrix.postMult(tmp);
806 set_rotation(tmp, -angleRad, getCenter(), getAxis());
813 class SpinAnimCallback : public osg::NodeCallback {
815 SpinAnimCallback(SGCondition const* condition,
816 SGExpressiond const* animationValue,
817 double initialValue = 0.0) :
818 _condition(condition),
819 _animationValue(animationValue),
820 _initialValue(initialValue)
822 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv);
824 SGSharedPtr<SGCondition const> _condition;
825 SGSharedPtr<SGExpressiond const> _animationValue;
826 double _initialValue;
828 // This cull callback can run in different threads if there is
829 // more than one camera. It is probably safe to overwrite the
830 // reference values in multiple threads, but we'll provide a
831 // threadsafe way to manage those values just to be safe.
832 struct ReferenceValues : public osg::Referenced
834 ReferenceValues(double t, double rot, double vel)
835 : _time(t), _rotation(rot), _rotVelocity(vel)
842 OpenThreads::AtomicPtr _referenceValues;
845 void SpinAnimCallback::operator()(osg::Node* node, osg::NodeVisitor* nv)
848 SGRotateTransform* transform = static_cast<SGRotateTransform*>(node);
849 EffectCullVisitor* cv = dynamic_cast<EffectCullVisitor*>(nv);
852 if (!_condition || _condition->test()) {
853 double t = nv->getFrameStamp()->getReferenceTime();
854 double rps = _animationValue->getValue() / 60.0;
855 ref_ptr<ReferenceValues>
856 refval(static_cast<ReferenceValues*>(_referenceValues.get()));
857 if (!refval || refval->_rotVelocity != rps) {
858 ref_ptr<ReferenceValues> newref;
859 if (!refval.valid()) {
861 newref = new ReferenceValues(t, 0.0, rps);
863 double newRot = refval->_rotation + (t - refval->_time) * refval->_rotVelocity;
864 newref = new ReferenceValues(t, newRot, rps);
866 // increment reference pointer, because it will be stored
867 // naked in _referenceValues.
869 if (_referenceValues.assign(newref, refval)) {
870 if (refval.valid()) {
871 DeletionManager::instance()->addStaleObject(refval.get());
875 // Another thread installed new values before us
878 // Whatever happened, we can use the reference values just
882 double rotation = refval->_rotation + (t - refval->_time) * rps;
884 double rot = modf(rotation, &intPart);
885 double angle = rot * 2.0 * osg::PI;
886 const SGVec3d& sgcenter = transform->getCenter();
887 const SGVec3d& sgaxis = transform->getAxis();
888 Matrixd mat = Matrixd::translate(-sgcenter[0], -sgcenter[1], -sgcenter[2])
889 * Matrixd::rotate(angle, sgaxis[0], sgaxis[1], sgaxis[2])
890 * Matrixd::translate(sgcenter[0], sgcenter[1], sgcenter[2])
891 * *cv->getModelViewMatrix();
892 ref_ptr<RefMatrix> refmat = new RefMatrix(mat);
893 cv->pushModelViewMatrix(refmat.get(), transform->getReferenceFrame());
894 traverse(transform, nv);
895 cv->popModelViewMatrix();
897 traverse(transform, nv);
901 SGVec3d readTranslateAxis(const SGPropertyNode* configNode)
905 if (configNode->hasValue("axis/x1-m")) {
907 v1[0] = configNode->getDoubleValue("axis/x1-m", 0);
908 v1[1] = configNode->getDoubleValue("axis/y1-m", 0);
909 v1[2] = configNode->getDoubleValue("axis/z1-m", 0);
910 v2[0] = configNode->getDoubleValue("axis/x2-m", 0);
911 v2[1] = configNode->getDoubleValue("axis/y2-m", 0);
912 v2[2] = configNode->getDoubleValue("axis/z2-m", 0);
915 axis[0] = configNode->getDoubleValue("axis/x", 0);
916 axis[1] = configNode->getDoubleValue("axis/y", 0);
917 axis[2] = configNode->getDoubleValue("axis/z", 0);
919 if (8*SGLimitsd::min() < norm(axis))
920 axis = normalize(axis);
925 SGRotateAnimation::SGRotateAnimation(const SGPropertyNode* configNode,
926 SGPropertyNode* modelRoot) :
927 SGAnimation(configNode, modelRoot)
929 std::string type = configNode->getStringValue("type", "");
930 _isSpin = (type == "spin");
932 _condition = getCondition();
933 SGSharedPtr<SGExpressiond> value;
934 value = read_value(configNode, modelRoot, "-deg",
935 -SGLimitsd::max(), SGLimitsd::max());
936 _animationValue = value->simplify();
938 _initialValue = _animationValue->getValue();
942 readRotationCenterAndAxis(_center, _axis);
946 SGRotateAnimation::createAnimationGroup(osg::Group& parent)
949 SGRotateTransform* transform = new SGRotateTransform;
950 transform->setName("spin rotate animation");
951 SpinAnimCallback* cc;
952 cc = new SpinAnimCallback(_condition, _animationValue, _initialValue);
953 transform->setCullCallback(cc);
954 transform->setCenter(_center);
955 transform->setAxis(_axis);
956 transform->setAngleDeg(_initialValue);
957 parent.addChild(transform);
960 SGRotAnimTransform* transform = new SGRotAnimTransform;
961 transform->setName("rotate animation");
962 transform->_condition = _condition;
963 transform->_animationValue = _animationValue;
964 transform->_lastAngle = _initialValue;
965 transform->setCenter(_center);
966 transform->setAxis(_axis);
967 parent.addChild(transform);
973 ////////////////////////////////////////////////////////////////////////
974 // Implementation of scale animation
975 ////////////////////////////////////////////////////////////////////////
977 class SGScaleAnimation::UpdateCallback : public osg::NodeCallback {
979 UpdateCallback(const SGCondition* condition,
980 SGSharedPtr<const SGExpressiond> animationValue[3]) :
981 _condition(condition)
983 _animationValue[0] = animationValue[0];
984 _animationValue[1] = animationValue[1];
985 _animationValue[2] = animationValue[2];
986 setName("SGScaleAnimation::UpdateCallback");
988 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
990 if (!_condition || _condition->test()) {
991 SGScaleTransform* transform;
992 transform = static_cast<SGScaleTransform*>(node);
993 SGVec3d scale(_animationValue[0]->getValue(),
994 _animationValue[1]->getValue(),
995 _animationValue[2]->getValue());
996 transform->setScaleFactor(scale);
1001 SGSharedPtr<SGCondition const> _condition;
1002 SGSharedPtr<SGExpressiond const> _animationValue[3];
1005 SGScaleAnimation::SGScaleAnimation(const SGPropertyNode* configNode,
1006 SGPropertyNode* modelRoot) :
1007 SGAnimation(configNode, modelRoot)
1009 _condition = getCondition();
1011 // default offset/factor for all directions
1012 double offset = configNode->getDoubleValue("offset", 0);
1013 double factor = configNode->getDoubleValue("factor", 1);
1015 SGSharedPtr<SGExpressiond> inPropExpr;
1017 std::string inputPropertyName;
1018 inputPropertyName = configNode->getStringValue("property", "");
1019 if (inputPropertyName.empty()) {
1020 inPropExpr = new SGConstExpression<double>(0);
1022 SGPropertyNode* inputProperty;
1023 inputProperty = modelRoot->getNode(inputPropertyName, true);
1024 inPropExpr = new SGPropertyExpression<double>(inputProperty);
1027 SGInterpTable* interpTable = read_interpolation_table(configNode);
1029 SGSharedPtr<SGExpressiond> value;
1030 value = new SGInterpTableExpression<double>(inPropExpr, interpTable);
1031 _animationValue[0] = value->simplify();
1032 _animationValue[1] = value->simplify();
1033 _animationValue[2] = value->simplify();
1034 } else if (configNode->getBoolValue("use-personality", false)) {
1035 SGSharedPtr<SGExpressiond> value;
1036 value = new SGPersonalityScaleOffsetExpression(inPropExpr, configNode,
1037 "x-factor", "x-offset",
1039 double minClip = configNode->getDoubleValue("x-min", 0);
1040 double maxClip = configNode->getDoubleValue("x-max", SGLimitsd::max());
1041 value = new SGClipExpression<double>(value, minClip, maxClip);
1042 _animationValue[0] = value->simplify();
1044 value = new SGPersonalityScaleOffsetExpression(inPropExpr, configNode,
1045 "y-factor", "y-offset",
1047 minClip = configNode->getDoubleValue("y-min", 0);
1048 maxClip = configNode->getDoubleValue("y-max", SGLimitsd::max());
1049 value = new SGClipExpression<double>(value, minClip, maxClip);
1050 _animationValue[1] = value->simplify();
1052 value = new SGPersonalityScaleOffsetExpression(inPropExpr, configNode,
1053 "z-factor", "z-offset",
1055 minClip = configNode->getDoubleValue("z-min", 0);
1056 maxClip = configNode->getDoubleValue("z-max", SGLimitsd::max());
1057 value = new SGClipExpression<double>(value, minClip, maxClip);
1058 _animationValue[2] = value->simplify();
1060 SGSharedPtr<SGExpressiond> value;
1061 value = read_factor_offset(configNode, inPropExpr, "x-factor", "x-offset");
1062 double minClip = configNode->getDoubleValue("x-min", 0);
1063 double maxClip = configNode->getDoubleValue("x-max", SGLimitsd::max());
1064 value = new SGClipExpression<double>(value, minClip, maxClip);
1065 _animationValue[0] = value->simplify();
1067 value = read_factor_offset(configNode, inPropExpr, "y-factor", "y-offset");
1068 minClip = configNode->getDoubleValue("y-min", 0);
1069 maxClip = configNode->getDoubleValue("y-max", SGLimitsd::max());
1070 value = new SGClipExpression<double>(value, minClip, maxClip);
1071 _animationValue[1] = value->simplify();
1073 value = read_factor_offset(configNode, inPropExpr, "z-factor", "z-offset");
1074 minClip = configNode->getDoubleValue("z-min", 0);
1075 maxClip = configNode->getDoubleValue("z-max", SGLimitsd::max());
1076 value = new SGClipExpression<double>(value, minClip, maxClip);
1077 _animationValue[2] = value->simplify();
1079 _initialValue[0] = configNode->getDoubleValue("x-starting-scale", 1);
1080 _initialValue[0] *= configNode->getDoubleValue("x-factor", factor);
1081 _initialValue[0] += configNode->getDoubleValue("x-offset", offset);
1082 _initialValue[1] = configNode->getDoubleValue("y-starting-scale", 1);
1083 _initialValue[1] *= configNode->getDoubleValue("y-factor", factor);
1084 _initialValue[1] += configNode->getDoubleValue("y-offset", offset);
1085 _initialValue[2] = configNode->getDoubleValue("z-starting-scale", 1);
1086 _initialValue[2] *= configNode->getDoubleValue("z-factor", factor);
1087 _initialValue[2] += configNode->getDoubleValue("z-offset", offset);
1088 _center[0] = configNode->getDoubleValue("center/x-m", 0);
1089 _center[1] = configNode->getDoubleValue("center/y-m", 0);
1090 _center[2] = configNode->getDoubleValue("center/z-m", 0);
1094 SGScaleAnimation::createAnimationGroup(osg::Group& parent)
1096 SGScaleTransform* transform = new SGScaleTransform;
1097 transform->setName("scale animation");
1098 transform->setCenter(_center);
1099 transform->setScaleFactor(_initialValue);
1100 UpdateCallback* uc = new UpdateCallback(_condition, _animationValue);
1101 transform->setUpdateCallback(uc);
1102 parent.addChild(transform);
1107 // Don't create a new state state everytime we need GL_NORMALIZE!
1111 Mutex normalizeMutex;
1113 osg::StateSet* getNormalizeStateSet()
1115 static osg::ref_ptr<osg::StateSet> normalizeStateSet;
1116 ScopedLock<Mutex> lock(normalizeMutex);
1117 if (!normalizeStateSet.valid()) {
1118 normalizeStateSet = new osg::StateSet;
1119 normalizeStateSet->setMode(GL_NORMALIZE, osg::StateAttribute::ON);
1120 normalizeStateSet->setDataVariance(osg::Object::STATIC);
1122 return normalizeStateSet.get();
1126 ////////////////////////////////////////////////////////////////////////
1127 // Implementation of dist scale animation
1128 ////////////////////////////////////////////////////////////////////////
1130 class SGDistScaleAnimation::Transform : public osg::Transform {
1132 Transform() : _min_v(0.0), _max_v(0.0), _factor(0.0), _offset(0.0) {}
1133 Transform(const Transform& rhs,
1134 const osg::CopyOp& copyOp = osg::CopyOp::SHALLOW_COPY)
1135 : osg::Transform(rhs, copyOp), _table(rhs._table), _center(rhs._center),
1136 _min_v(rhs._min_v), _max_v(rhs._max_v), _factor(rhs._factor),
1137 _offset(rhs._offset)
1140 META_Node(simgear, SGDistScaleAnimation::Transform);
1141 Transform(const SGPropertyNode* configNode)
1143 setName(configNode->getStringValue("name", "dist scale animation"));
1144 setReferenceFrame(RELATIVE_RF);
1145 setStateSet(getNormalizeStateSet());
1146 _factor = configNode->getFloatValue("factor", 1);
1147 _offset = configNode->getFloatValue("offset", 0);
1148 _min_v = configNode->getFloatValue("min", SGLimitsf::epsilon());
1149 _max_v = configNode->getFloatValue("max", SGLimitsf::max());
1150 _table = read_interpolation_table(configNode);
1151 _center[0] = configNode->getFloatValue("center/x-m", 0);
1152 _center[1] = configNode->getFloatValue("center/y-m", 0);
1153 _center[2] = configNode->getFloatValue("center/z-m", 0);
1155 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
1156 osg::NodeVisitor* nv) const
1158 osg::Matrix transform;
1159 double scale_factor = computeScaleFactor(nv);
1160 transform(0,0) = scale_factor;
1161 transform(1,1) = scale_factor;
1162 transform(2,2) = scale_factor;
1163 transform(3,0) = _center[0]*(1 - scale_factor);
1164 transform(3,1) = _center[1]*(1 - scale_factor);
1165 transform(3,2) = _center[2]*(1 - scale_factor);
1166 matrix.preMult(transform);
1170 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
1171 osg::NodeVisitor* nv) const
1173 double scale_factor = computeScaleFactor(nv);
1174 if (fabs(scale_factor) <= SGLimits<double>::min())
1176 osg::Matrix transform;
1177 double rScaleFactor = 1/scale_factor;
1178 transform(0,0) = rScaleFactor;
1179 transform(1,1) = rScaleFactor;
1180 transform(2,2) = rScaleFactor;
1181 transform(3,0) = _center[0]*(1 - rScaleFactor);
1182 transform(3,1) = _center[1]*(1 - rScaleFactor);
1183 transform(3,2) = _center[2]*(1 - rScaleFactor);
1184 matrix.postMult(transform);
1188 static bool writeLocalData(const osg::Object& obj, osgDB::Output& fw)
1190 const Transform& trans = static_cast<const Transform&>(obj);
1191 fw.indent() << "center " << trans._center << "\n";
1192 fw.indent() << "min_v " << trans._min_v << "\n";
1193 fw.indent() << "max_v " << trans._max_v << "\n";
1194 fw.indent() << "factor " << trans._factor << "\n";
1195 fw.indent() << "offset " << trans._offset << "\n";
1199 double computeScaleFactor(osg::NodeVisitor* nv) const
1204 double scale_factor = (toOsg(_center) - nv->getEyePoint()).length();
1206 scale_factor = _factor * scale_factor + _offset;
1208 scale_factor = _table->interpolate( scale_factor );
1210 if (scale_factor < _min_v)
1211 scale_factor = _min_v;
1212 if (scale_factor > _max_v)
1213 scale_factor = _max_v;
1215 return scale_factor;
1218 SGSharedPtr<SGInterpTable> _table;
1227 SGDistScaleAnimation::SGDistScaleAnimation(const SGPropertyNode* configNode,
1228 SGPropertyNode* modelRoot) :
1229 SGAnimation(configNode, modelRoot)
1234 SGDistScaleAnimation::createAnimationGroup(osg::Group& parent)
1236 Transform* transform = new Transform(getConfig());
1237 parent.addChild(transform);
1243 osgDB::RegisterDotOsgWrapperProxy distScaleAnimationTransformProxy
1245 new SGDistScaleAnimation::Transform,
1246 "SGDistScaleAnimation::Transform",
1247 "Object Node Transform SGDistScaleAnimation::Transform Group",
1249 &SGDistScaleAnimation::Transform::writeLocalData
1253 ////////////////////////////////////////////////////////////////////////
1254 // Implementation of flash animation
1255 ////////////////////////////////////////////////////////////////////////
1257 class SGFlashAnimation::Transform : public osg::Transform {
1259 Transform() : _power(0.0), _factor(0.0), _offset(0.0), _min_v(0.0),
1260 _max_v(0.0), _two_sides(false)
1263 Transform(const Transform& rhs,
1264 const osg::CopyOp& copyOp = osg::CopyOp::SHALLOW_COPY)
1265 : osg::Transform(rhs, copyOp), _center(rhs._center), _axis(rhs._axis),
1266 _power(rhs._power), _factor(rhs._factor), _offset(rhs._offset),
1267 _min_v(rhs._min_v), _max_v(rhs._max_v), _two_sides(rhs._two_sides)
1270 META_Node(simgear, SGFlashAnimation::Transform);
1272 Transform(const SGPropertyNode* configNode)
1274 setReferenceFrame(RELATIVE_RF);
1275 setName(configNode->getStringValue("name", "flash animation"));
1276 setStateSet(getNormalizeStateSet());
1278 _axis[0] = configNode->getFloatValue("axis/x", 0);
1279 _axis[1] = configNode->getFloatValue("axis/y", 0);
1280 _axis[2] = configNode->getFloatValue("axis/z", 1);
1283 _center[0] = configNode->getFloatValue("center/x-m", 0);
1284 _center[1] = configNode->getFloatValue("center/y-m", 0);
1285 _center[2] = configNode->getFloatValue("center/z-m", 0);
1287 _offset = configNode->getFloatValue("offset", 0);
1288 _factor = configNode->getFloatValue("factor", 1);
1289 _power = configNode->getFloatValue("power", 1);
1290 _two_sides = configNode->getBoolValue("two-sides", false);
1292 _min_v = configNode->getFloatValue("min", SGLimitsf::epsilon());
1293 _max_v = configNode->getFloatValue("max", 1);
1295 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
1296 osg::NodeVisitor* nv) const
1298 osg::Matrix transform;
1299 double scale_factor = computeScaleFactor(nv);
1300 transform(0,0) = scale_factor;
1301 transform(1,1) = scale_factor;
1302 transform(2,2) = scale_factor;
1303 transform(3,0) = _center[0]*(1 - scale_factor);
1304 transform(3,1) = _center[1]*(1 - scale_factor);
1305 transform(3,2) = _center[2]*(1 - scale_factor);
1306 matrix.preMult(transform);
1310 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
1311 osg::NodeVisitor* nv) const
1313 double scale_factor = computeScaleFactor(nv);
1314 if (fabs(scale_factor) <= SGLimits<double>::min())
1316 osg::Matrix transform;
1317 double rScaleFactor = 1/scale_factor;
1318 transform(0,0) = rScaleFactor;
1319 transform(1,1) = rScaleFactor;
1320 transform(2,2) = rScaleFactor;
1321 transform(3,0) = _center[0]*(1 - rScaleFactor);
1322 transform(3,1) = _center[1]*(1 - rScaleFactor);
1323 transform(3,2) = _center[2]*(1 - rScaleFactor);
1324 matrix.postMult(transform);
1328 static bool writeLocalData(const osg::Object& obj, osgDB::Output& fw)
1330 const Transform& trans = static_cast<const Transform&>(obj);
1331 fw.indent() << "center " << trans._center[0] << " "
1332 << trans._center[1] << " " << trans._center[2] << " " << "\n";
1333 fw.indent() << "axis " << trans._axis[0] << " "
1334 << trans._axis[1] << " " << trans._axis[2] << " " << "\n";
1335 fw.indent() << "power " << trans._power << " \n";
1336 fw.indent() << "min_v " << trans._min_v << "\n";
1337 fw.indent() << "max_v " << trans._max_v << "\n";
1338 fw.indent() << "factor " << trans._factor << "\n";
1339 fw.indent() << "offset " << trans._offset << "\n";
1340 fw.indent() << "twosides " << (trans._two_sides ? "true" : "false") << "\n";
1344 double computeScaleFactor(osg::NodeVisitor* nv) const
1349 osg::Vec3 localEyeToCenter = nv->getEyePoint() - _center;
1350 localEyeToCenter.normalize();
1352 double cos_angle = localEyeToCenter*_axis;
1353 double scale_factor = 0;
1354 if ( _two_sides && cos_angle < 0 )
1355 scale_factor = _factor * pow( -cos_angle, _power ) + _offset;
1356 else if ( cos_angle > 0 )
1357 scale_factor = _factor * pow( cos_angle, _power ) + _offset;
1359 if ( scale_factor < _min_v )
1360 scale_factor = _min_v;
1361 if ( scale_factor > _max_v )
1362 scale_factor = _max_v;
1364 return scale_factor;
1367 virtual osg::BoundingSphere computeBound() const
1369 // avoid being culled away by small feature culling
1370 osg::BoundingSphere bs = osg::Group::computeBound();
1371 bs.radius() *= _max_v;
1378 double _power, _factor, _offset, _min_v, _max_v;
1383 SGFlashAnimation::SGFlashAnimation(const SGPropertyNode* configNode,
1384 SGPropertyNode* modelRoot) :
1385 SGAnimation(configNode, modelRoot)
1390 SGFlashAnimation::createAnimationGroup(osg::Group& parent)
1392 Transform* transform = new Transform(getConfig());
1393 parent.addChild(transform);
1399 osgDB::RegisterDotOsgWrapperProxy flashAnimationTransformProxy
1401 new SGFlashAnimation::Transform,
1402 "SGFlashAnimation::Transform",
1403 "Object Node Transform SGFlashAnimation::Transform Group",
1405 &SGFlashAnimation::Transform::writeLocalData
1409 ////////////////////////////////////////////////////////////////////////
1410 // Implementation of billboard animation
1411 ////////////////////////////////////////////////////////////////////////
1413 class SGBillboardAnimation::Transform : public osg::Transform {
1415 Transform() : _spherical(true) {}
1416 Transform(const Transform& rhs,
1417 const osg::CopyOp& copyOp = osg::CopyOp::SHALLOW_COPY)
1418 : osg::Transform(rhs, copyOp), _spherical(rhs._spherical) {}
1419 META_Node(simgear, SGBillboardAnimation::Transform);
1420 Transform(const SGPropertyNode* configNode) :
1421 _spherical(configNode->getBoolValue("spherical", true))
1423 setReferenceFrame(RELATIVE_RF);
1424 setName(configNode->getStringValue("name", "billboard animation"));
1426 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
1427 osg::NodeVisitor* nv) const
1429 // More or less taken from plibs ssgCutout
1431 matrix(0,0) = 1; matrix(0,1) = 0; matrix(0,2) = 0;
1432 matrix(1,0) = 0; matrix(1,1) = 0; matrix(1,2) = -1;
1433 matrix(2,0) = 0; matrix(2,1) = 1; matrix(2,2) = 0;
1435 osg::Vec3 zAxis(matrix(2, 0), matrix(2, 1), matrix(2, 2));
1436 osg::Vec3 xAxis = osg::Vec3(0, 0, -1)^zAxis;
1437 osg::Vec3 yAxis = zAxis^xAxis;
1443 matrix(0,0) = xAxis[0]; matrix(0,1) = xAxis[1]; matrix(0,2) = xAxis[2];
1444 matrix(1,0) = yAxis[0]; matrix(1,1) = yAxis[1]; matrix(1,2) = yAxis[2];
1445 matrix(2,0) = zAxis[0]; matrix(2,1) = zAxis[1]; matrix(2,2) = zAxis[2];
1450 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
1451 osg::NodeVisitor* nv) const
1453 // Hmm, don't yet know how to get that back ...
1456 static bool writeLocalData(const osg::Object& obj, osgDB::Output& fw)
1458 const Transform& trans = static_cast<const Transform&>(obj);
1460 fw.indent() << (trans._spherical ? "true" : "false") << "\n";
1468 SGBillboardAnimation::SGBillboardAnimation(const SGPropertyNode* configNode,
1469 SGPropertyNode* modelRoot) :
1470 SGAnimation(configNode, modelRoot)
1475 SGBillboardAnimation::createAnimationGroup(osg::Group& parent)
1477 Transform* transform = new Transform(getConfig());
1478 parent.addChild(transform);
1484 osgDB::RegisterDotOsgWrapperProxy billboardAnimationTransformProxy
1486 new SGBillboardAnimation::Transform,
1487 "SGBillboardAnimation::Transform",
1488 "Object Node Transform SGBillboardAnimation::Transform Group",
1490 &SGBillboardAnimation::Transform::writeLocalData
1494 ////////////////////////////////////////////////////////////////////////
1495 // Implementation of a range animation
1496 ////////////////////////////////////////////////////////////////////////
1498 class SGRangeAnimation::UpdateCallback : public osg::NodeCallback {
1500 UpdateCallback(const SGCondition* condition,
1501 const SGExpressiond* minAnimationValue,
1502 const SGExpressiond* maxAnimationValue,
1503 double minValue, double maxValue) :
1504 _condition(condition),
1505 _minAnimationValue(minAnimationValue),
1506 _maxAnimationValue(maxAnimationValue),
1507 _minStaticValue(minValue),
1508 _maxStaticValue(maxValue)
1510 setName("SGRangeAnimation::UpdateCallback");
1512 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1514 osg::LOD* lod = static_cast<osg::LOD*>(node);
1515 if (!_condition || _condition->test()) {
1517 if (_minAnimationValue)
1518 minRange = _minAnimationValue->getValue();
1520 minRange = _minStaticValue;
1522 if (_maxAnimationValue)
1523 maxRange = _maxAnimationValue->getValue();
1525 maxRange = _maxStaticValue;
1526 lod->setRange(0, minRange, maxRange);
1528 lod->setRange(0, 0, SGLimitsf::max());
1534 SGSharedPtr<const SGCondition> _condition;
1535 SGSharedPtr<const SGExpressiond> _minAnimationValue;
1536 SGSharedPtr<const SGExpressiond> _maxAnimationValue;
1537 double _minStaticValue;
1538 double _maxStaticValue;
1541 SGRangeAnimation::SGRangeAnimation(const SGPropertyNode* configNode,
1542 SGPropertyNode* modelRoot) :
1543 SGAnimation(configNode, modelRoot)
1545 _condition = getCondition();
1547 std::string inputPropertyName;
1548 inputPropertyName = configNode->getStringValue("min-property", "");
1549 if (!inputPropertyName.empty()) {
1550 SGPropertyNode* inputProperty;
1551 inputProperty = modelRoot->getNode(inputPropertyName, true);
1552 SGSharedPtr<SGExpressiond> value;
1553 value = new SGPropertyExpression<double>(inputProperty);
1555 value = read_factor_offset(configNode, value, "min-factor", "min-offset");
1556 _minAnimationValue = value->simplify();
1558 inputPropertyName = configNode->getStringValue("max-property", "");
1559 if (!inputPropertyName.empty()) {
1560 SGPropertyNode* inputProperty;
1561 inputProperty = modelRoot->getNode(inputPropertyName.c_str(), true);
1563 SGSharedPtr<SGExpressiond> value;
1564 value = new SGPropertyExpression<double>(inputProperty);
1566 value = read_factor_offset(configNode, value, "max-factor", "max-offset");
1567 _maxAnimationValue = value->simplify();
1570 _initialValue[0] = configNode->getDoubleValue("min-m", 0);
1571 _initialValue[0] *= configNode->getDoubleValue("min-factor", 1);
1572 _initialValue[1] = configNode->getDoubleValue("max-m", SGLimitsf::max());
1573 _initialValue[1] *= configNode->getDoubleValue("max-factor", 1);
1577 SGRangeAnimation::createAnimationGroup(osg::Group& parent)
1579 osg::Group* group = new osg::Group;
1580 group->setName("range animation group");
1582 osg::LOD* lod = new osg::LOD;
1583 lod->setName("range animation node");
1584 parent.addChild(lod);
1586 lod->addChild(group, _initialValue[0], _initialValue[1]);
1587 lod->setCenterMode(osg::LOD::USE_BOUNDING_SPHERE_CENTER);
1588 lod->setRangeMode(osg::LOD::DISTANCE_FROM_EYE_POINT);
1589 if (_minAnimationValue || _maxAnimationValue || _condition) {
1591 uc = new UpdateCallback(_condition, _minAnimationValue, _maxAnimationValue,
1592 _initialValue[0], _initialValue[1]);
1593 lod->setUpdateCallback(uc);
1599 ////////////////////////////////////////////////////////////////////////
1600 // Implementation of a select animation
1601 ////////////////////////////////////////////////////////////////////////
1603 SGSelectAnimation::SGSelectAnimation(const SGPropertyNode* configNode,
1604 SGPropertyNode* modelRoot) :
1605 SGAnimation(configNode, modelRoot)
1610 SGSelectAnimation::createAnimationGroup(osg::Group& parent)
1612 // if no condition given, this is a noop.
1613 SGSharedPtr<SGCondition const> condition = getCondition();
1614 // trick, gets deleted with all its 'animated' children
1615 // when the animation installer returns
1617 return new osg::Group;
1618 simgear::ConditionNode* cn = new simgear::ConditionNode;
1619 cn->setName("select animation node");
1620 cn->setCondition(condition.ptr());
1621 osg::Group* grp = new osg::Group;
1623 parent.addChild(cn);
1629 ////////////////////////////////////////////////////////////////////////
1630 // Implementation of alpha test animation
1631 ////////////////////////////////////////////////////////////////////////
1633 SGAlphaTestAnimation::SGAlphaTestAnimation(const SGPropertyNode* configNode,
1634 SGPropertyNode* modelRoot) :
1635 SGAnimation(configNode, modelRoot)
1641 // Keep one copy of the most common alpha test its state set.
1642 ReentrantMutex alphaTestMutex;
1643 osg::ref_ptr<osg::AlphaFunc> standardAlphaFunc;
1644 osg::ref_ptr<osg::StateSet> alphaFuncStateSet;
1646 osg::AlphaFunc* makeAlphaFunc(float clamp)
1648 ScopedLock<ReentrantMutex> lock(alphaTestMutex);
1649 if (osg::equivalent(clamp, 0.01f)) {
1650 if (standardAlphaFunc.valid())
1651 return standardAlphaFunc.get();
1654 osg::AlphaFunc* alphaFunc = new osg::AlphaFunc;
1655 alphaFunc->setFunction(osg::AlphaFunc::GREATER);
1656 alphaFunc->setReferenceValue(clamp);
1657 alphaFunc->setDataVariance(osg::Object::STATIC);
1658 if (osg::equivalent(clamp, 0.01f))
1659 standardAlphaFunc = alphaFunc;
1663 osg::StateSet* makeAlphaTestStateSet(float clamp)
1665 using namespace OpenThreads;
1666 ScopedLock<ReentrantMutex> lock(alphaTestMutex);
1667 if (osg::equivalent(clamp, 0.01f)) {
1668 if (alphaFuncStateSet.valid())
1669 return alphaFuncStateSet.get();
1671 osg::AlphaFunc* alphaFunc = makeAlphaFunc(clamp);
1672 osg::StateSet* stateSet = new osg::StateSet;
1673 stateSet->setAttributeAndModes(alphaFunc,
1674 (osg::StateAttribute::ON
1675 | osg::StateAttribute::OVERRIDE));
1676 stateSet->setDataVariance(osg::Object::STATIC);
1677 if (osg::equivalent(clamp, 0.01f))
1678 alphaFuncStateSet = stateSet;
1683 SGAlphaTestAnimation::install(osg::Node& node)
1685 SGAnimation::install(node);
1687 float alphaClamp = getConfig()->getFloatValue("alpha-factor", 0);
1688 osg::StateSet* stateSet = node.getStateSet();
1690 node.setStateSet(makeAlphaTestStateSet(alphaClamp));
1692 stateSet->setAttributeAndModes(makeAlphaFunc(alphaClamp),
1693 (osg::StateAttribute::ON
1694 | osg::StateAttribute::OVERRIDE));
1699 //////////////////////////////////////////////////////////////////////
1700 // Blend animation installer
1701 //////////////////////////////////////////////////////////////////////
1703 // XXX This needs to be replaced by something using TexEnvCombine to
1704 // change the blend factor. Changing the alpha values in the geometry
1706 class SGBlendAnimation::BlendVisitor : public osg::NodeVisitor {
1708 BlendVisitor(float blend) :
1709 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
1711 { setVisitorType(osg::NodeVisitor::NODE_VISITOR); }
1712 virtual void apply(osg::Node& node)
1714 updateStateSet(node.getStateSet());
1717 virtual void apply(osg::Geode& node)
1719 apply((osg::Node&)node);
1720 unsigned nDrawables = node.getNumDrawables();
1721 for (unsigned i = 0; i < nDrawables; ++i) {
1722 osg::Drawable* drawable = node.getDrawable(i);
1723 osg::Geometry* geometry = drawable->asGeometry();
1726 osg::Array* array = geometry->getColorArray();
1729 osg::Vec4Array* vec4Array = dynamic_cast<osg::Vec4Array*>(array);
1732 for (unsigned k = 0; k < vec4Array->size(); ++k) {
1733 (*vec4Array)[k][3] = _blend;
1736 updateStateSet(drawable->getStateSet());
1739 void updateStateSet(osg::StateSet* stateSet)
1743 osg::StateAttribute* stateAttribute;
1744 stateAttribute = stateSet->getAttribute(osg::StateAttribute::MATERIAL);
1745 if (!stateAttribute)
1747 osg::Material* material = dynamic_cast<osg::Material*>(stateAttribute);
1750 material->setAlpha(osg::Material::FRONT_AND_BACK, _blend);
1752 stateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
1753 stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
1755 stateSet->setRenderingHint(osg::StateSet::DEFAULT_BIN);
1762 class SGBlendAnimation::UpdateCallback : public osg::NodeCallback {
1764 UpdateCallback(const SGPropertyNode* configNode, const SGExpressiond* v) :
1768 setName("SGBlendAnimation::UpdateCallback");
1770 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1772 double blend = _animationValue->getValue();
1773 if (blend != _prev_value) {
1774 _prev_value = blend;
1775 BlendVisitor visitor(1-blend);
1776 node->accept(visitor);
1782 SGSharedPtr<SGExpressiond const> _animationValue;
1786 SGBlendAnimation::SGBlendAnimation(const SGPropertyNode* configNode,
1787 SGPropertyNode* modelRoot)
1788 : SGAnimation(configNode, modelRoot),
1789 _animationValue(read_value(configNode, modelRoot, "", 0, 1))
1794 SGBlendAnimation::createAnimationGroup(osg::Group& parent)
1796 if (!_animationValue)
1799 osg::Group* group = new osg::Switch;
1800 group->setName("blend animation node");
1801 group->setUpdateCallback(new UpdateCallback(getConfig(), _animationValue));
1802 parent.addChild(group);
1807 SGBlendAnimation::install(osg::Node& node)
1809 SGAnimation::install(node);
1810 // make sure we do not change common geometries,
1811 // that also creates new display lists for these subgeometries.
1812 cloneDrawables(node);
1813 DoDrawArraysVisitor visitor;
1814 node.accept(visitor);
1818 //////////////////////////////////////////////////////////////////////
1819 // Timed animation installer
1820 //////////////////////////////////////////////////////////////////////
1824 class SGTimedAnimation::UpdateCallback : public osg::NodeCallback {
1826 UpdateCallback(const SGPropertyNode* configNode) :
1829 _duration_sec(configNode->getDoubleValue("duration-sec", 1)),
1830 _last_time_sec(SGLimitsd::max()),
1831 _use_personality(configNode->getBoolValue("use-personality", false))
1833 std::vector<SGSharedPtr<SGPropertyNode> > nodes;
1834 nodes = configNode->getChildren("branch-duration-sec");
1835 for (size_t i = 0; i < nodes.size(); ++i) {
1836 unsigned ind = nodes[ i ]->getIndex();
1837 while ( ind >= _durations.size() ) {
1838 _durations.push_back(DurationSpec(_duration_sec));
1840 SGPropertyNode_ptr rNode = nodes[i]->getChild("random");
1842 _durations[ind] = DurationSpec(nodes[ i ]->getDoubleValue());
1844 _durations[ind] = DurationSpec(rNode->getDoubleValue( "min", 0),
1845 rNode->getDoubleValue( "max", 1));
1848 setName("SGTimedAnimation::UpdateCallback");
1850 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1852 assert(dynamic_cast<osg::Switch*>(node));
1853 osg::Switch* sw = static_cast<osg::Switch*>(node);
1855 unsigned nChildren = sw->getNumChildren();
1857 // blow up the durations vector to the required size
1858 while (_durations.size() < nChildren) {
1859 _durations.push_back(_duration_sec);
1861 // make sure the current index is an duration that really exists
1862 _current_index = _current_index % nChildren;
1864 // update the time and compute the current systems time value
1865 double t = nv->getFrameStamp()->getReferenceTime();
1866 if (_last_time_sec == SGLimitsd::max()) {
1869 double dt = t - _last_time_sec;
1870 if (_use_personality)
1871 dt *= 1 + 0.2*(0.5 - sg_random());
1876 double currentDuration = _durations[_current_index].get();
1877 while (currentDuration < _reminder) {
1878 _reminder -= currentDuration;
1879 _current_index = (_current_index + 1) % nChildren;
1880 currentDuration = _durations[_current_index].get();
1883 sw->setSingleChildOn(_current_index);
1889 struct DurationSpec {
1890 DurationSpec(double t) :
1891 minTime(SGMiscd::max(0.01, t)),
1892 maxTime(SGMiscd::max(0.01, t))
1894 DurationSpec(double t0, double t1) :
1895 minTime(SGMiscd::max(0.01, t0)),
1896 maxTime(SGMiscd::max(0.01, t1))
1899 { return minTime + sg_random()*(maxTime - minTime); }
1903 std::vector<DurationSpec> _durations;
1904 unsigned _current_index;
1906 double _duration_sec;
1907 double _last_time_sec;
1908 bool _use_personality;
1912 SGTimedAnimation::SGTimedAnimation(const SGPropertyNode* configNode,
1913 SGPropertyNode* modelRoot)
1914 : SGAnimation(configNode, modelRoot)
1919 SGTimedAnimation::createAnimationGroup(osg::Group& parent)
1921 osg::Switch* sw = new osg::Switch;
1922 sw->setName("timed animation node");
1923 sw->setUpdateCallback(new UpdateCallback(getConfig()));
1924 parent.addChild(sw);
1929 ////////////////////////////////////////////////////////////////////////
1930 // dynamically switch on/off shadows
1931 ////////////////////////////////////////////////////////////////////////
1933 class SGShadowAnimation::UpdateCallback : public osg::NodeCallback {
1935 UpdateCallback(const SGCondition* condition) :
1936 _condition(condition)
1938 setName("SGShadowAnimation::UpdateCallback");
1940 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1942 if (_condition->test())
1943 node->setNodeMask( SG_NODEMASK_CASTSHADOW_BIT | node->getNodeMask());
1945 node->setNodeMask(~SG_NODEMASK_CASTSHADOW_BIT & node->getNodeMask());
1950 SGSharedPtr<const SGCondition> _condition;
1953 SGShadowAnimation::SGShadowAnimation(const SGPropertyNode* configNode,
1954 SGPropertyNode* modelRoot) :
1955 SGAnimation(configNode, modelRoot)
1960 SGShadowAnimation::createAnimationGroup(osg::Group& parent)
1962 SGSharedPtr<SGCondition const> condition = getCondition();
1964 osg::Group* group = new osg::Group;
1965 group->setName("shadow animation");
1967 group->setUpdateCallback(new UpdateCallback(condition));
1969 group->setNodeMask(~SG_NODEMASK_CASTSHADOW_BIT & group->getNodeMask());
1970 parent.addChild(group);
1975 ////////////////////////////////////////////////////////////////////////
1976 // Implementation of SGTexTransformAnimation
1977 ////////////////////////////////////////////////////////////////////////
1979 class SGTexTransformAnimation::Transform : public SGReferenced {
1984 virtual ~Transform()
1986 void setValue(double value)
1988 virtual void transform(osg::Matrix&) = 0;
1993 class SGTexTransformAnimation::Translation :
1994 public SGTexTransformAnimation::Transform {
1996 Translation(const SGVec3d& axis) :
1999 virtual void transform(osg::Matrix& matrix)
2002 set_translation(tmp, _value, _axis);
2003 matrix.preMult(tmp);
2009 class SGTexTransformAnimation::Rotation :
2010 public SGTexTransformAnimation::Transform {
2012 Rotation(const SGVec3d& axis, const SGVec3d& center) :
2016 virtual void transform(osg::Matrix& matrix)
2019 SGRotateTransform::set_rotation(tmp, SGMiscd::deg2rad(_value), _center,
2021 matrix.preMult(tmp);
2028 class SGTexTransformAnimation::UpdateCallback :
2029 public osg::StateAttribute::Callback {
2031 UpdateCallback(const SGCondition* condition) :
2032 _condition(condition)
2034 setName("SGTexTransformAnimation::UpdateCallback");
2036 virtual void operator () (osg::StateAttribute* sa, osg::NodeVisitor*)
2038 if (!_condition || _condition->test()) {
2039 TransformList::const_iterator i;
2040 for (i = _transforms.begin(); i != _transforms.end(); ++i)
2041 i->transform->setValue(i->value->getValue());
2043 assert(dynamic_cast<osg::TexMat*>(sa));
2044 osg::TexMat* texMat = static_cast<osg::TexMat*>(sa);
2045 texMat->getMatrix().makeIdentity();
2046 TransformList::const_iterator i;
2047 for (i = _transforms.begin(); i != _transforms.end(); ++i)
2048 i->transform->transform(texMat->getMatrix());
2050 void appendTransform(Transform* transform, SGExpressiond* value)
2052 Entry entry = { transform, value };
2053 transform->transform(_matrix);
2054 _transforms.push_back(entry);
2059 SGSharedPtr<Transform> transform;
2060 SGSharedPtr<const SGExpressiond> value;
2062 typedef std::vector<Entry> TransformList;
2063 TransformList _transforms;
2064 SGSharedPtr<const SGCondition> _condition;
2065 osg::Matrix _matrix;
2068 SGTexTransformAnimation::SGTexTransformAnimation(const SGPropertyNode* configNode,
2069 SGPropertyNode* modelRoot) :
2070 SGAnimation(configNode, modelRoot)
2075 SGTexTransformAnimation::createAnimationGroup(osg::Group& parent)
2077 osg::Group* group = new osg::Group;
2078 group->setName("texture transform group");
2079 osg::StateSet* stateSet = group->getOrCreateStateSet();
2080 stateSet->setDataVariance(osg::Object::DYNAMIC);
2081 osg::TexMat* texMat = new osg::TexMat;
2082 UpdateCallback* updateCallback = new UpdateCallback(getCondition());
2083 // interpret the configs ...
2084 std::string type = getType();
2086 if (type == "textranslate") {
2087 appendTexTranslate(getConfig(), updateCallback);
2088 } else if (type == "texrotate") {
2089 appendTexRotate(getConfig(), updateCallback);
2090 } else if (type == "texmultiple") {
2091 std::vector<SGSharedPtr<SGPropertyNode> > transformConfigs;
2092 transformConfigs = getConfig()->getChildren("transform");
2093 for (unsigned i = 0; i < transformConfigs.size(); ++i) {
2094 std::string subtype = transformConfigs[i]->getStringValue("subtype", "");
2095 if (subtype == "textranslate")
2096 appendTexTranslate(transformConfigs[i], updateCallback);
2097 else if (subtype == "texrotate")
2098 appendTexRotate(transformConfigs[i], updateCallback);
2100 SG_LOG(SG_INPUT, SG_ALERT,
2101 "Ignoring unknown texture transform subtype");
2104 SG_LOG(SG_INPUT, SG_ALERT, "Ignoring unknown texture transform type");
2107 texMat->setUpdateCallback(updateCallback);
2108 stateSet->setTextureAttribute(0, texMat);
2109 parent.addChild(group);
2114 SGTexTransformAnimation::appendTexTranslate(const SGPropertyNode* config,
2115 UpdateCallback* updateCallback)
2117 std::string propertyName = config->getStringValue("property", "");
2118 SGSharedPtr<SGExpressiond> value;
2119 if (propertyName.empty())
2120 value = new SGConstExpression<double>(0);
2122 SGPropertyNode* inputProperty = getModelRoot()->getNode(propertyName, true);
2123 value = new SGPropertyExpression<double>(inputProperty);
2126 SGInterpTable* table = read_interpolation_table(config);
2128 value = new SGInterpTableExpression<double>(value, table);
2129 double biasValue = config->getDoubleValue("bias", 0);
2131 value = new SGBiasExpression<double>(value, biasValue);
2132 value = new SGStepExpression<double>(value,
2133 config->getDoubleValue("step", 0),
2134 config->getDoubleValue("scroll", 0));
2135 value = value->simplify();
2137 double biasValue = config->getDoubleValue("bias", 0);
2139 value = new SGBiasExpression<double>(value, biasValue);
2140 value = new SGStepExpression<double>(value,
2141 config->getDoubleValue("step", 0),
2142 config->getDoubleValue("scroll", 0));
2143 value = read_offset_factor(config, value, "factor", "offset");
2145 if (config->hasChild("min") || config->hasChild("max")) {
2146 double minClip = config->getDoubleValue("min", -SGLimitsd::max());
2147 double maxClip = config->getDoubleValue("max", SGLimitsd::max());
2148 value = new SGClipExpression<double>(value, minClip, maxClip);
2150 value = value->simplify();
2152 SGVec3d axis(config->getDoubleValue("axis/x", 0),
2153 config->getDoubleValue("axis/y", 0),
2154 config->getDoubleValue("axis/z", 0));
2155 Translation* translation;
2156 translation = new Translation(normalize(axis));
2157 translation->setValue(config->getDoubleValue("starting-position", 0));
2158 updateCallback->appendTransform(translation, value);
2162 SGTexTransformAnimation::appendTexRotate(const SGPropertyNode* config,
2163 UpdateCallback* updateCallback)
2165 std::string propertyName = config->getStringValue("property", "");
2166 SGSharedPtr<SGExpressiond> value;
2167 if (propertyName.empty())
2168 value = new SGConstExpression<double>(0);
2170 SGPropertyNode* inputProperty = getModelRoot()->getNode(propertyName, true);
2171 value = new SGPropertyExpression<double>(inputProperty);
2174 SGInterpTable* table = read_interpolation_table(config);
2176 value = new SGInterpTableExpression<double>(value, table);
2177 double biasValue = config->getDoubleValue("bias", 0);
2179 value = new SGBiasExpression<double>(value, biasValue);
2180 value = new SGStepExpression<double>(value,
2181 config->getDoubleValue("step", 0),
2182 config->getDoubleValue("scroll", 0));
2183 value = value->simplify();
2185 double biasValue = config->getDoubleValue("bias", 0);
2187 value = new SGBiasExpression<double>(value, biasValue);
2188 value = new SGStepExpression<double>(value,
2189 config->getDoubleValue("step", 0),
2190 config->getDoubleValue("scroll", 0));
2191 value = read_offset_factor(config, value, "factor", "offset-deg");
2193 if (config->hasChild("min-deg") || config->hasChild("max-deg")) {
2194 double minClip = config->getDoubleValue("min-deg", -SGLimitsd::max());
2195 double maxClip = config->getDoubleValue("max-deg", SGLimitsd::max());
2196 value = new SGClipExpression<double>(value, minClip, maxClip);
2198 value = value->simplify();
2200 SGVec3d axis(config->getDoubleValue("axis/x", 0),
2201 config->getDoubleValue("axis/y", 0),
2202 config->getDoubleValue("axis/z", 0));
2203 SGVec3d center(config->getDoubleValue("center/x", 0),
2204 config->getDoubleValue("center/y", 0),
2205 config->getDoubleValue("center/z", 0));
2207 rotation = new Rotation(normalize(axis), center);
2208 rotation->setValue(config->getDoubleValue("starting-position-deg", 0));
2209 updateCallback->appendTransform(rotation, value);