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>
38 #include <simgear/math/interpolater.hxx>
39 #include <simgear/props/condition.hxx>
40 #include <simgear/props/props.hxx>
41 #include <simgear/scene/material/EffectGeode.hxx>
42 #include <simgear/scene/material/EffectCullVisitor.hxx>
43 #include <simgear/scene/util/DeletionManager.hxx>
44 #include <simgear/scene/util/OsgMath.hxx>
45 #include <simgear/scene/util/SGNodeMasks.hxx>
46 #include <simgear/scene/util/SGSceneUserData.hxx>
47 #include <simgear/scene/util/SGStateAttributeVisitor.hxx>
48 #include <simgear/scene/util/StateAttributeFactory.hxx>
52 #include "animation.hxx"
55 #include "SGTranslateTransform.hxx"
56 #include "SGMaterialAnimation.hxx"
57 #include "SGRotateTransform.hxx"
58 #include "SGScaleTransform.hxx"
59 #include "SGInteractionAnimation.hxx"
60 #include "SGPickAnimation.hxx"
61 #include "SGTrackToAnimation.hxx"
63 #include "ConditionNode.hxx"
65 using OpenThreads::Mutex;
66 using OpenThreads::ReentrantMutex;
67 using OpenThreads::ScopedLock;
69 using namespace simgear;
70 ////////////////////////////////////////////////////////////////////////
71 // Static utility functions.
72 ////////////////////////////////////////////////////////////////////////
75 * Set up the transform matrix for a translation.
78 set_translation (osg::Matrix &matrix, double position_m, const SGVec3d &axis)
80 SGVec3d xyz = axis * position_m;
81 matrix.makeIdentity();
82 matrix(3, 0) = xyz[0];
83 matrix(3, 1) = xyz[1];
84 matrix(3, 2) = xyz[2];
88 * Read an interpolation table from properties.
90 static SGInterpTable *
91 read_interpolation_table(const SGPropertyNode* props)
93 const SGPropertyNode* table_node = props->getNode("interpolation");
96 return new SGInterpTable(table_node);
100 unit_string(const char* value, const char* unit)
102 return std::string(value) + unit;
105 class SGPersonalityScaleOffsetExpression : public SGUnaryExpression<double> {
107 SGPersonalityScaleOffsetExpression(SGExpression<double>* expr,
108 SGPropertyNode const* config,
109 const std::string& scalename,
110 const std::string& offsetname,
112 double defOffset = 0) :
113 SGUnaryExpression<double>(expr),
114 _scale(config, scalename.c_str(), defScale),
115 _offset(config, offsetname.c_str(), defOffset)
117 void setScale(double scale)
119 void setOffset(double offset)
120 { _offset = offset; }
122 virtual void eval(double& value, const simgear::expression::Binding* b) const
126 value = _offset + _scale*getOperand()->getValue(b);
129 virtual bool isConst() const { return false; }
132 mutable SGPersonalityParameter<double> _scale;
133 mutable SGPersonalityParameter<double> _offset;
137 static SGExpressiond*
138 read_factor_offset(const SGPropertyNode* configNode, SGExpressiond* expr,
139 const std::string& factor, const std::string& offset)
141 double factorValue = configNode->getDoubleValue(factor, 1);
142 if (factorValue != 1)
143 expr = new SGScaleExpression<double>(expr, factorValue);
144 double offsetValue = configNode->getDoubleValue(offset, 0);
145 if (offsetValue != 0)
146 expr = new SGBiasExpression<double>(expr, offsetValue);
150 static SGExpressiond*
151 read_offset_factor(const SGPropertyNode* configNode, SGExpressiond* expr,
152 const std::string& factor, const std::string& offset)
154 double offsetValue = configNode->getDoubleValue(offset, 0);
155 if (offsetValue != 0)
156 expr = new SGBiasExpression<double>(expr, offsetValue);
157 double factorValue = configNode->getDoubleValue(factor, 1);
158 if (factorValue != 1)
159 expr = new SGScaleExpression<double>(expr, factorValue);
164 read_value(const SGPropertyNode* configNode, SGPropertyNode* modelRoot,
165 const char* unit, double defMin, double defMax)
167 const SGPropertyNode * expression = configNode->getNode( "expression" );
168 if( expression != NULL )
169 return SGReadDoubleExpression( modelRoot, expression->getChild(0) );
171 SGExpression<double>* value = 0;
173 std::string inputPropertyName = configNode->getStringValue("property", "");
174 if (inputPropertyName.empty()) {
175 std::string spos = unit_string("starting-position", unit);
176 double initPos = configNode->getDoubleValue(spos, 0);
177 value = new SGConstExpression<double>(initPos);
179 SGPropertyNode* inputProperty;
180 inputProperty = modelRoot->getNode(inputPropertyName, true);
181 value = new SGPropertyExpression<double>(inputProperty);
184 SGInterpTable* interpTable = read_interpolation_table(configNode);
186 return new SGInterpTableExpression<double>(value, interpTable);
188 std::string offset = unit_string("offset", unit);
189 std::string min = unit_string("min", unit);
190 std::string max = unit_string("max", unit);
192 if (configNode->getBoolValue("use-personality", false)) {
193 value = new SGPersonalityScaleOffsetExpression(value, configNode,
196 value = read_factor_offset(configNode, value, "factor", offset);
199 double minClip = configNode->getDoubleValue(min, defMin);
200 double maxClip = configNode->getDoubleValue(max, defMax);
201 if (minClip > SGMiscd::min(SGLimitsd::min(), -SGLimitsd::max()) ||
202 maxClip < SGLimitsd::max())
203 value = new SGClipExpression<double>(value, minClip, maxClip);
210 ////////////////////////////////////////////////////////////////////////
211 // Animation installer
212 ////////////////////////////////////////////////////////////////////////
214 class SGAnimation::RemoveModeVisitor : public SGStateAttributeVisitor {
216 RemoveModeVisitor(osg::StateAttribute::GLMode mode) :
219 virtual void apply(osg::StateSet* stateSet)
223 stateSet->removeMode(_mode);
226 osg::StateAttribute::GLMode _mode;
229 class SGAnimation::RemoveAttributeVisitor : public SGStateAttributeVisitor {
231 RemoveAttributeVisitor(osg::StateAttribute::Type type) :
234 virtual void apply(osg::StateSet* stateSet)
238 while (stateSet->getAttribute(_type)) {
239 stateSet->removeAttribute(_type);
243 osg::StateAttribute::Type _type;
246 class SGAnimation::RemoveTextureModeVisitor : public SGStateAttributeVisitor {
248 RemoveTextureModeVisitor(unsigned unit, osg::StateAttribute::GLMode mode) :
252 virtual void apply(osg::StateSet* stateSet)
256 stateSet->removeTextureMode(_unit, _mode);
260 osg::StateAttribute::GLMode _mode;
263 class SGAnimation::RemoveTextureAttributeVisitor :
264 public SGStateAttributeVisitor {
266 RemoveTextureAttributeVisitor(unsigned unit,
267 osg::StateAttribute::Type type) :
271 virtual void apply(osg::StateSet* stateSet)
275 while (stateSet->getTextureAttribute(_unit, _type)) {
276 stateSet->removeTextureAttribute(_unit, _type);
281 osg::StateAttribute::Type _type;
284 class SGAnimation::BinToInheritVisitor : public SGStateAttributeVisitor {
286 virtual void apply(osg::StateSet* stateSet)
290 stateSet->setRenderBinToInherit();
294 class SGAnimation::DrawableCloneVisitor : public osg::NodeVisitor {
296 DrawableCloneVisitor() :
297 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
299 void apply(osg::Geode& geode)
301 for (unsigned i = 0 ; i < geode.getNumDrawables(); ++i) {
302 osg::CopyOp copyOp(osg::CopyOp::DEEP_COPY_ALL &
303 ~osg::CopyOp::DEEP_COPY_TEXTURES);
304 geode.setDrawable(i, copyOp(geode.getDrawable(i)));
311 // Set all drawables to not use display lists. OSG will use
312 // glDrawArrays instead.
313 struct DoDrawArraysVisitor : public osg::NodeVisitor {
314 DoDrawArraysVisitor() :
315 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
317 void apply(osg::Geode& geode)
322 for (int i = 0; i < (int)geode.getNumDrawables(); ++i)
323 geode.getDrawable(i)->setUseDisplayList(false);
328 SGAnimation::SGAnimation(const SGPropertyNode* configNode,
329 SGPropertyNode* modelRoot) :
330 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
332 _configNode(configNode),
333 _modelRoot(modelRoot)
335 _name = configNode->getStringValue("name", "");
336 _enableHOT = configNode->getBoolValue("enable-hot", true);
337 std::vector<SGPropertyNode_ptr> objectNames =
338 configNode->getChildren("object-name");
339 for (unsigned i = 0; i < objectNames.size(); ++i)
340 _objectNames.push_back(objectNames[i]->getStringValue());
343 SGAnimation::~SGAnimation()
347 std::list<std::string>::const_iterator i;
349 for (i = _objectNames.begin(); i != _objectNames.end(); ++i)
359 SG_LOG(SG_IO, SG_ALERT, "Could not find at least one of the following"
360 " objects for animation: " << info);
366 SGAnimation::animate(osg::Node* node, const SGPropertyNode* configNode,
367 SGPropertyNode* modelRoot,
368 const osgDB::Options* options,
369 const std::string &path, int i)
371 std::string type = configNode->getStringValue("type", "none");
372 if (type == "alpha-test") {
373 SGAlphaTestAnimation animInst(configNode, modelRoot);
374 animInst.apply(node);
375 } else if (type == "billboard") {
376 SGBillboardAnimation animInst(configNode, modelRoot);
377 animInst.apply(node);
378 } else if (type == "blend") {
379 SGBlendAnimation animInst(configNode, modelRoot);
380 animInst.apply(node);
381 } else if (type == "dist-scale") {
382 SGDistScaleAnimation animInst(configNode, modelRoot);
383 animInst.apply(node);
384 } else if (type == "flash") {
385 SGFlashAnimation animInst(configNode, modelRoot);
386 animInst.apply(node);
387 } else if (type == "interaction") {
388 SGInteractionAnimation animInst(configNode, modelRoot);
389 animInst.apply(node);
390 } else if (type == "material") {
391 SGMaterialAnimation animInst(configNode, modelRoot, options, path);
392 animInst.apply(node);
393 } else if (type == "noshadow") {
394 SGShadowAnimation animInst(configNode, modelRoot);
395 animInst.apply(node);
396 } else if (type == "pick") {
397 SGPickAnimation animInst(configNode, modelRoot);
398 animInst.apply(node);
399 } else if (type == "knob") {
400 SGKnobAnimation animInst(configNode, modelRoot);
401 animInst.apply(node);
402 } else if (type == "slider") {
403 SGSliderAnimation animInst(configNode, modelRoot);
404 animInst.apply(node);
405 } else if (type == "range") {
406 SGRangeAnimation animInst(configNode, modelRoot);
407 animInst.apply(node);
408 } else if (type == "rotate" || type == "spin") {
409 SGRotateAnimation animInst(configNode, modelRoot);
410 animInst.apply(node);
411 } else if (type == "scale") {
412 SGScaleAnimation animInst(configNode, modelRoot);
413 animInst.apply(node);
414 } else if (type == "select") {
415 SGSelectAnimation animInst(configNode, modelRoot);
416 animInst.apply(node);
417 } else if (type == "shader") {
418 SGShaderAnimation animInst(configNode, modelRoot, options);
419 animInst.apply(node);
420 } else if (type == "textranslate" || type == "texrotate" ||
421 type == "textrapezoid" || type == "texmultiple") {
422 SGTexTransformAnimation animInst(configNode, modelRoot);
423 animInst.apply(node);
424 } else if (type == "timed") {
425 SGTimedAnimation animInst(configNode, modelRoot);
426 animInst.apply(node);
427 } else if (type == "locked-track") {
428 SGTrackToAnimation animInst(node, configNode, modelRoot);
429 animInst.apply(node);
430 } else if (type == "translate") {
431 SGTranslateAnimation animInst(configNode, modelRoot);
432 animInst.apply(node);
433 } else if (type == "light") {
434 SGLightAnimation animInst(configNode, modelRoot, options, path, i);
435 animInst.apply(node);
436 } else if (type == "null" || type == "none" || type.empty()) {
437 SGGroupAnimation animInst(configNode, modelRoot);
438 animInst.apply(node);
447 SGAnimation::apply(osg::Node* node)
449 // duh what a special case ...
450 if (_objectNames.empty()) {
451 osg::Group* group = node->asGroup();
453 osg::ref_ptr<osg::Group> animationGroup;
454 installInGroup(std::string(), *group, animationGroup);
461 SGAnimation::install(osg::Node& node)
465 node.setNodeMask( SG_NODEMASK_TERRAIN_BIT | node.getNodeMask());
467 node.setNodeMask(~SG_NODEMASK_TERRAIN_BIT & node.getNodeMask());
471 SGAnimation::createAnimationGroup(osg::Group& parent)
473 // default implementation, we do not need a new group
474 // for every animation type. Usually animations that just change
475 // the StateSet of some parts of the model
480 SGAnimation::apply(osg::Group& group)
482 // the trick is to first traverse the children and then
483 // possibly splice in a new group node if required.
484 // Else we end up in a recursive loop where we infinitly insert new
488 // Note that this algorithm preserves the order of the child objects
489 // like they appear in the object-name tags.
490 // The timed animations require this
491 osg::ref_ptr<osg::Group> animationGroup;
492 std::list<std::string>::const_iterator nameIt;
493 for (nameIt = _objectNames.begin(); nameIt != _objectNames.end(); ++nameIt)
494 installInGroup(*nameIt, group, animationGroup);
498 SGAnimation::installInGroup(const std::string& name, osg::Group& group,
499 osg::ref_ptr<osg::Group>& animationGroup)
501 int i = group.getNumChildren() - 1;
502 for (; 0 <= i; --i) {
503 osg::Node* child = group.getChild(i);
505 // Check if this one is already processed
506 if (std::find(_installedAnimations.begin(),
507 _installedAnimations.end(), child)
508 != _installedAnimations.end())
511 if (name.empty() || child->getName() == name) {
512 // fire the installation of the animation
515 // create a group node on demand
516 if (!animationGroup.valid()) {
517 animationGroup = createAnimationGroup(group);
518 // Animation type that does not require a new group,
519 // in this case we can stop and look for the next object
520 if (animationGroup.valid() && !_name.empty())
521 animationGroup->setName(_name);
523 if (animationGroup.valid()) {
524 animationGroup->addChild(child);
525 group.removeChild(i);
528 // store that we already have processed this child node
529 // We can hit this one twice if an animation references some
530 // part of a subtree twice
531 _installedAnimations.push_back(child);
536 //------------------------------------------------------------------------------
537 SGVec3d SGAnimation::readVec3( const SGPropertyNode& cfg,
538 const std::string& name,
539 const std::string& suffix,
540 const SGVec3d& def ) const
543 vec[0] = cfg.getDoubleValue(name + "/x" + suffix, def.x());
544 vec[1] = cfg.getDoubleValue(name + "/y" + suffix, def.y());
545 vec[2] = cfg.getDoubleValue(name + "/z" + suffix, def.z());
549 //------------------------------------------------------------------------------
550 SGVec3d SGAnimation::readVec3( const std::string& name,
551 const std::string& suffix,
552 const SGVec3d& def ) const
554 return readVec3(*_configNode, name, suffix, def);
557 //------------------------------------------------------------------------------
558 // factored out to share with SGKnobAnimation
559 void SGAnimation::readRotationCenterAndAxis( SGVec3d& center,
560 SGVec3d& axis ) const
562 center = SGVec3d::zeros();
563 if( _configNode->hasValue("axis/x1-m") )
565 SGVec3d v1 = readVec3("axis", "1-m"), // axis/[xyz]1-m
566 v2 = readVec3("axis", "2-m"); // axis/[xyz]2-m
567 center = 0.5*(v1+v2);
572 axis = readVec3("axis");
574 if( 8 * SGLimitsd::min() < norm(axis) )
575 axis = normalize(axis);
577 center = readVec3("center", "-m", center);
580 //------------------------------------------------------------------------------
581 SGExpressiond* SGAnimation::readOffsetValue(const char* tag_name) const
583 const SGPropertyNode* node = _configNode->getChild(tag_name);
587 SGExpressiond_ref expression;
588 if( !node->nChildren() )
589 expression = new SGConstExpression<double>(node->getDoubleValue());
591 expression = SGReadDoubleExpression(_modelRoot, node->getChild(0));
596 expression = expression->simplify();
598 if( expression->isConst() && expression->getValue() == 0 )
601 return expression.release();
605 SGAnimation::removeMode(osg::Node& node, osg::StateAttribute::GLMode mode)
607 RemoveModeVisitor visitor(mode);
608 node.accept(visitor);
612 SGAnimation::removeAttribute(osg::Node& node, osg::StateAttribute::Type type)
614 RemoveAttributeVisitor visitor(type);
615 node.accept(visitor);
619 SGAnimation::removeTextureMode(osg::Node& node, unsigned unit,
620 osg::StateAttribute::GLMode mode)
622 RemoveTextureModeVisitor visitor(unit, mode);
623 node.accept(visitor);
627 SGAnimation::removeTextureAttribute(osg::Node& node, unsigned unit,
628 osg::StateAttribute::Type type)
630 RemoveTextureAttributeVisitor visitor(unit, type);
631 node.accept(visitor);
635 SGAnimation::setRenderBinToInherit(osg::Node& node)
637 BinToInheritVisitor visitor;
638 node.accept(visitor);
642 SGAnimation::cloneDrawables(osg::Node& node)
644 DrawableCloneVisitor visitor;
645 node.accept(visitor);
649 SGAnimation::getCondition() const
651 const SGPropertyNode* conditionNode = _configNode->getChild("condition");
654 return sgReadCondition(_modelRoot, conditionNode);
659 ////////////////////////////////////////////////////////////////////////
660 // Implementation of null animation
661 ////////////////////////////////////////////////////////////////////////
663 // Ok, that is to build a subgraph from different other
664 // graph nodes. I guess that this stems from the time where modellers
665 // could not build hierarchical trees ...
666 SGGroupAnimation::SGGroupAnimation(const SGPropertyNode* configNode,
667 SGPropertyNode* modelRoot):
668 SGAnimation(configNode, modelRoot)
673 SGGroupAnimation::createAnimationGroup(osg::Group& parent)
675 osg::Group* group = new osg::Group;
676 parent.addChild(group);
681 ////////////////////////////////////////////////////////////////////////
682 // Implementation of translate animation
683 ////////////////////////////////////////////////////////////////////////
685 class SGTranslateAnimation::UpdateCallback : public osg::NodeCallback {
687 UpdateCallback(SGCondition const* condition,
688 SGExpressiond const* animationValue) :
689 _condition(condition),
690 _animationValue(animationValue)
692 setName("SGTranslateAnimation::UpdateCallback");
694 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
696 if (!_condition || _condition->test()) {
697 SGTranslateTransform* transform;
698 transform = static_cast<SGTranslateTransform*>(node);
699 transform->setValue(_animationValue->getValue());
704 SGSharedPtr<SGCondition const> _condition;
705 SGSharedPtr<SGExpressiond const> _animationValue;
708 SGTranslateAnimation::SGTranslateAnimation(const SGPropertyNode* configNode,
709 SGPropertyNode* modelRoot) :
710 SGAnimation(configNode, modelRoot)
712 _condition = getCondition();
713 SGSharedPtr<SGExpressiond> value;
714 value = read_value(configNode, modelRoot, "-m",
715 -SGLimitsd::max(), SGLimitsd::max());
716 _animationValue = value->simplify();
718 _initialValue = _animationValue->getValue();
722 _axis = readTranslateAxis(configNode);
726 SGTranslateAnimation::createAnimationGroup(osg::Group& parent)
728 SGTranslateTransform* transform = new SGTranslateTransform;
729 transform->setName("translate animation");
730 if (_animationValue && !_animationValue->isConst()) {
731 UpdateCallback* uc = new UpdateCallback(_condition, _animationValue);
732 transform->setUpdateCallback(uc);
734 transform->setAxis(_axis);
735 transform->setValue(_initialValue);
736 parent.addChild(transform);
741 ////////////////////////////////////////////////////////////////////////
742 // Implementation of rotate/spin animation
743 ////////////////////////////////////////////////////////////////////////
745 class SGRotAnimTransform : public SGRotateTransform
748 SGRotAnimTransform();
749 SGRotAnimTransform(const SGRotAnimTransform&,
750 const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY);
751 META_Node(simgear, SGRotAnimTransform);
752 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
753 osg::NodeVisitor* nv) const;
754 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
755 osg::NodeVisitor* nv) const;
756 SGSharedPtr<SGCondition const> _condition;
757 SGSharedPtr<SGExpressiond const> _animationValue;
758 // used when condition is false
759 mutable double _lastAngle;
762 SGRotAnimTransform::SGRotAnimTransform()
767 SGRotAnimTransform::SGRotAnimTransform(const SGRotAnimTransform& rhs,
768 const osg::CopyOp& copyop)
769 : SGRotateTransform(rhs, copyop), _condition(rhs._condition),
770 _animationValue(rhs._animationValue), _lastAngle(rhs._lastAngle)
774 bool SGRotAnimTransform::computeLocalToWorldMatrix(osg::Matrix& matrix,
775 osg::NodeVisitor* nv) const
778 if (!_condition || _condition->test()) {
779 angle = _animationValue->getValue();
784 double angleRad = SGMiscd::deg2rad(angle);
785 if (_referenceFrame == RELATIVE_RF) {
788 set_rotation(tmp, angleRad, getCenter(), getAxis());
792 SGRotateTransform::set_rotation(tmp, angleRad, getCenter(), getAxis());
798 bool SGRotAnimTransform::computeWorldToLocalMatrix(osg::Matrix& matrix,
799 osg::NodeVisitor* nv) const
802 if (!_condition || _condition->test()) {
803 angle = _animationValue->getValue();
808 double angleRad = SGMiscd::deg2rad(angle);
809 if (_referenceFrame == RELATIVE_RF) {
812 set_rotation(tmp, -angleRad, getCenter(), getAxis());
813 matrix.postMult(tmp);
816 set_rotation(tmp, -angleRad, getCenter(), getAxis());
823 class SpinAnimCallback : public osg::NodeCallback {
825 SpinAnimCallback(SGCondition const* condition,
826 SGExpressiond const* animationValue,
827 double initialValue = 0.0) :
828 _condition(condition),
829 _animationValue(animationValue),
830 _initialValue(initialValue)
832 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv);
834 SGSharedPtr<SGCondition const> _condition;
835 SGSharedPtr<SGExpressiond const> _animationValue;
836 double _initialValue;
838 // This cull callback can run in different threads if there is
839 // more than one camera. It is probably safe to overwrite the
840 // reference values in multiple threads, but we'll provide a
841 // threadsafe way to manage those values just to be safe.
842 struct ReferenceValues : public osg::Referenced
844 ReferenceValues(double t, double rot, double vel)
845 : _time(t), _rotation(rot), _rotVelocity(vel)
852 OpenThreads::AtomicPtr _referenceValues;
855 void SpinAnimCallback::operator()(osg::Node* node, osg::NodeVisitor* nv)
858 SGRotateTransform* transform = static_cast<SGRotateTransform*>(node);
859 EffectCullVisitor* cv = dynamic_cast<EffectCullVisitor*>(nv);
862 if (!_condition || _condition->test()) {
863 double t = nv->getFrameStamp()->getSimulationTime();
864 double rps = _animationValue->getValue() / 60.0;
865 ref_ptr<ReferenceValues>
866 refval(static_cast<ReferenceValues*>(_referenceValues.get()));
867 if (!refval || refval->_rotVelocity != rps) {
868 ref_ptr<ReferenceValues> newref;
869 if (!refval.valid()) {
871 newref = new ReferenceValues(t, 0.0, rps);
873 double newRot = refval->_rotation + (t - refval->_time) * refval->_rotVelocity;
874 newref = new ReferenceValues(t, newRot, rps);
876 // increment reference pointer, because it will be stored
877 // naked in _referenceValues.
879 if (_referenceValues.assign(newref, refval)) {
880 if (refval.valid()) {
881 DeletionManager::instance()->addStaleObject(refval.get());
885 // Another thread installed new values before us
888 // Whatever happened, we can use the reference values just
892 double rotation = refval->_rotation + (t - refval->_time) * rps;
894 double rot = modf(rotation, &intPart);
895 double angle = rot * 2.0 * osg::PI;
896 const SGVec3d& sgcenter = transform->getCenter();
897 const SGVec3d& sgaxis = transform->getAxis();
898 Matrixd mat = Matrixd::translate(-sgcenter[0], -sgcenter[1], -sgcenter[2])
899 * Matrixd::rotate(angle, sgaxis[0], sgaxis[1], sgaxis[2])
900 * Matrixd::translate(sgcenter[0], sgcenter[1], sgcenter[2])
901 * *cv->getModelViewMatrix();
902 ref_ptr<RefMatrix> refmat = new RefMatrix(mat);
903 cv->pushModelViewMatrix(refmat.get(), transform->getReferenceFrame());
904 traverse(transform, nv);
905 cv->popModelViewMatrix();
907 traverse(transform, nv);
911 SGVec3d readTranslateAxis(const SGPropertyNode* configNode)
915 if (configNode->hasValue("axis/x1-m")) {
917 v1[0] = configNode->getDoubleValue("axis/x1-m", 0);
918 v1[1] = configNode->getDoubleValue("axis/y1-m", 0);
919 v1[2] = configNode->getDoubleValue("axis/z1-m", 0);
920 v2[0] = configNode->getDoubleValue("axis/x2-m", 0);
921 v2[1] = configNode->getDoubleValue("axis/y2-m", 0);
922 v2[2] = configNode->getDoubleValue("axis/z2-m", 0);
925 axis[0] = configNode->getDoubleValue("axis/x", 0);
926 axis[1] = configNode->getDoubleValue("axis/y", 0);
927 axis[2] = configNode->getDoubleValue("axis/z", 0);
929 if (8*SGLimitsd::min() < norm(axis))
930 axis = normalize(axis);
935 SGRotateAnimation::SGRotateAnimation(const SGPropertyNode* configNode,
936 SGPropertyNode* modelRoot) :
937 SGAnimation(configNode, modelRoot)
939 std::string type = configNode->getStringValue("type", "");
940 _isSpin = (type == "spin");
942 _condition = getCondition();
943 SGSharedPtr<SGExpressiond> value;
944 value = read_value(configNode, modelRoot, "-deg",
945 -SGLimitsd::max(), SGLimitsd::max());
946 _animationValue = value->simplify();
948 _initialValue = _animationValue->getValue();
952 readRotationCenterAndAxis(_center, _axis);
956 SGRotateAnimation::createAnimationGroup(osg::Group& parent)
959 SGRotateTransform* transform = new SGRotateTransform;
960 transform->setName("spin rotate animation");
961 SpinAnimCallback* cc;
962 cc = new SpinAnimCallback(_condition, _animationValue, _initialValue);
963 transform->setCullCallback(cc);
964 transform->setCenter(_center);
965 transform->setAxis(_axis);
966 transform->setAngleDeg(_initialValue);
967 parent.addChild(transform);
970 SGRotAnimTransform* transform = new SGRotAnimTransform;
971 transform->setName("rotate animation");
972 transform->_condition = _condition;
973 transform->_animationValue = _animationValue;
974 transform->_lastAngle = _initialValue;
975 transform->setCenter(_center);
976 transform->setAxis(_axis);
977 parent.addChild(transform);
983 ////////////////////////////////////////////////////////////////////////
984 // Implementation of scale animation
985 ////////////////////////////////////////////////////////////////////////
987 class SGScaleAnimation::UpdateCallback : public osg::NodeCallback {
989 UpdateCallback(const SGCondition* condition,
990 SGSharedPtr<const SGExpressiond> animationValue[3]) :
991 _condition(condition)
993 _animationValue[0] = animationValue[0];
994 _animationValue[1] = animationValue[1];
995 _animationValue[2] = animationValue[2];
996 setName("SGScaleAnimation::UpdateCallback");
998 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1000 if (!_condition || _condition->test()) {
1001 SGScaleTransform* transform;
1002 transform = static_cast<SGScaleTransform*>(node);
1003 SGVec3d scale(_animationValue[0]->getValue(),
1004 _animationValue[1]->getValue(),
1005 _animationValue[2]->getValue());
1006 transform->setScaleFactor(scale);
1011 SGSharedPtr<SGCondition const> _condition;
1012 SGSharedPtr<SGExpressiond const> _animationValue[3];
1015 SGScaleAnimation::SGScaleAnimation(const SGPropertyNode* configNode,
1016 SGPropertyNode* modelRoot) :
1017 SGAnimation(configNode, modelRoot)
1019 _condition = getCondition();
1021 // default offset/factor for all directions
1022 double offset = configNode->getDoubleValue("offset", 0);
1023 double factor = configNode->getDoubleValue("factor", 1);
1025 SGSharedPtr<SGExpressiond> inPropExpr;
1027 std::string inputPropertyName;
1028 inputPropertyName = configNode->getStringValue("property", "");
1029 if (inputPropertyName.empty()) {
1030 inPropExpr = new SGConstExpression<double>(0);
1032 SGPropertyNode* inputProperty;
1033 inputProperty = modelRoot->getNode(inputPropertyName, true);
1034 inPropExpr = new SGPropertyExpression<double>(inputProperty);
1037 SGInterpTable* interpTable = read_interpolation_table(configNode);
1039 SGSharedPtr<SGExpressiond> value;
1040 value = new SGInterpTableExpression<double>(inPropExpr, interpTable);
1041 _animationValue[0] = value->simplify();
1042 _animationValue[1] = value->simplify();
1043 _animationValue[2] = value->simplify();
1044 } else if (configNode->getBoolValue("use-personality", false)) {
1045 SGSharedPtr<SGExpressiond> value;
1046 value = new SGPersonalityScaleOffsetExpression(inPropExpr, configNode,
1047 "x-factor", "x-offset",
1049 double minClip = configNode->getDoubleValue("x-min", 0);
1050 double maxClip = configNode->getDoubleValue("x-max", SGLimitsd::max());
1051 value = new SGClipExpression<double>(value, minClip, maxClip);
1052 _animationValue[0] = value->simplify();
1054 value = new SGPersonalityScaleOffsetExpression(inPropExpr, configNode,
1055 "y-factor", "y-offset",
1057 minClip = configNode->getDoubleValue("y-min", 0);
1058 maxClip = configNode->getDoubleValue("y-max", SGLimitsd::max());
1059 value = new SGClipExpression<double>(value, minClip, maxClip);
1060 _animationValue[1] = value->simplify();
1062 value = new SGPersonalityScaleOffsetExpression(inPropExpr, configNode,
1063 "z-factor", "z-offset",
1065 minClip = configNode->getDoubleValue("z-min", 0);
1066 maxClip = configNode->getDoubleValue("z-max", SGLimitsd::max());
1067 value = new SGClipExpression<double>(value, minClip, maxClip);
1068 _animationValue[2] = value->simplify();
1070 SGSharedPtr<SGExpressiond> value;
1071 value = read_factor_offset(configNode, inPropExpr, "x-factor", "x-offset");
1072 double minClip = configNode->getDoubleValue("x-min", 0);
1073 double maxClip = configNode->getDoubleValue("x-max", SGLimitsd::max());
1074 value = new SGClipExpression<double>(value, minClip, maxClip);
1075 _animationValue[0] = value->simplify();
1077 value = read_factor_offset(configNode, inPropExpr, "y-factor", "y-offset");
1078 minClip = configNode->getDoubleValue("y-min", 0);
1079 maxClip = configNode->getDoubleValue("y-max", SGLimitsd::max());
1080 value = new SGClipExpression<double>(value, minClip, maxClip);
1081 _animationValue[1] = value->simplify();
1083 value = read_factor_offset(configNode, inPropExpr, "z-factor", "z-offset");
1084 minClip = configNode->getDoubleValue("z-min", 0);
1085 maxClip = configNode->getDoubleValue("z-max", SGLimitsd::max());
1086 value = new SGClipExpression<double>(value, minClip, maxClip);
1087 _animationValue[2] = value->simplify();
1089 _initialValue[0] = configNode->getDoubleValue("x-starting-scale", 1);
1090 _initialValue[0] *= configNode->getDoubleValue("x-factor", factor);
1091 _initialValue[0] += configNode->getDoubleValue("x-offset", offset);
1092 _initialValue[1] = configNode->getDoubleValue("y-starting-scale", 1);
1093 _initialValue[1] *= configNode->getDoubleValue("y-factor", factor);
1094 _initialValue[1] += configNode->getDoubleValue("y-offset", offset);
1095 _initialValue[2] = configNode->getDoubleValue("z-starting-scale", 1);
1096 _initialValue[2] *= configNode->getDoubleValue("z-factor", factor);
1097 _initialValue[2] += configNode->getDoubleValue("z-offset", offset);
1098 _center[0] = configNode->getDoubleValue("center/x-m", 0);
1099 _center[1] = configNode->getDoubleValue("center/y-m", 0);
1100 _center[2] = configNode->getDoubleValue("center/z-m", 0);
1104 SGScaleAnimation::createAnimationGroup(osg::Group& parent)
1106 SGScaleTransform* transform = new SGScaleTransform;
1107 transform->setName("scale animation");
1108 transform->setCenter(_center);
1109 transform->setScaleFactor(_initialValue);
1110 UpdateCallback* uc = new UpdateCallback(_condition, _animationValue);
1111 transform->setUpdateCallback(uc);
1112 parent.addChild(transform);
1117 // Don't create a new state state everytime we need GL_NORMALIZE!
1121 Mutex normalizeMutex;
1123 osg::StateSet* getNormalizeStateSet()
1125 static osg::ref_ptr<osg::StateSet> normalizeStateSet;
1126 ScopedLock<Mutex> lock(normalizeMutex);
1127 if (!normalizeStateSet.valid()) {
1128 normalizeStateSet = new osg::StateSet;
1129 normalizeStateSet->setMode(GL_NORMALIZE, osg::StateAttribute::ON);
1130 normalizeStateSet->setDataVariance(osg::Object::STATIC);
1132 return normalizeStateSet.get();
1136 ////////////////////////////////////////////////////////////////////////
1137 // Implementation of dist scale animation
1138 ////////////////////////////////////////////////////////////////////////
1140 class SGDistScaleAnimation::Transform : public osg::Transform {
1142 Transform() : _min_v(0.0), _max_v(0.0), _factor(0.0), _offset(0.0) {}
1143 Transform(const Transform& rhs,
1144 const osg::CopyOp& copyOp = osg::CopyOp::SHALLOW_COPY)
1145 : osg::Transform(rhs, copyOp), _table(rhs._table), _center(rhs._center),
1146 _min_v(rhs._min_v), _max_v(rhs._max_v), _factor(rhs._factor),
1147 _offset(rhs._offset)
1150 META_Node(simgear, SGDistScaleAnimation::Transform);
1151 Transform(const SGPropertyNode* configNode)
1153 setName(configNode->getStringValue("name", "dist scale animation"));
1154 setReferenceFrame(RELATIVE_RF);
1155 setStateSet(getNormalizeStateSet());
1156 _factor = configNode->getFloatValue("factor", 1);
1157 _offset = configNode->getFloatValue("offset", 0);
1158 _min_v = configNode->getFloatValue("min", SGLimitsf::epsilon());
1159 _max_v = configNode->getFloatValue("max", SGLimitsf::max());
1160 _table = read_interpolation_table(configNode);
1161 _center[0] = configNode->getFloatValue("center/x-m", 0);
1162 _center[1] = configNode->getFloatValue("center/y-m", 0);
1163 _center[2] = configNode->getFloatValue("center/z-m", 0);
1165 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
1166 osg::NodeVisitor* nv) const
1168 osg::Matrix transform;
1169 double scale_factor = computeScaleFactor(nv);
1170 transform(0,0) = scale_factor;
1171 transform(1,1) = scale_factor;
1172 transform(2,2) = scale_factor;
1173 transform(3,0) = _center[0]*(1 - scale_factor);
1174 transform(3,1) = _center[1]*(1 - scale_factor);
1175 transform(3,2) = _center[2]*(1 - scale_factor);
1176 matrix.preMult(transform);
1180 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
1181 osg::NodeVisitor* nv) const
1183 double scale_factor = computeScaleFactor(nv);
1184 if (fabs(scale_factor) <= SGLimits<double>::min())
1186 osg::Matrix transform;
1187 double rScaleFactor = 1/scale_factor;
1188 transform(0,0) = rScaleFactor;
1189 transform(1,1) = rScaleFactor;
1190 transform(2,2) = rScaleFactor;
1191 transform(3,0) = _center[0]*(1 - rScaleFactor);
1192 transform(3,1) = _center[1]*(1 - rScaleFactor);
1193 transform(3,2) = _center[2]*(1 - rScaleFactor);
1194 matrix.postMult(transform);
1198 static bool writeLocalData(const osg::Object& obj, osgDB::Output& fw)
1200 const Transform& trans = static_cast<const Transform&>(obj);
1201 fw.indent() << "center " << trans._center << "\n";
1202 fw.indent() << "min_v " << trans._min_v << "\n";
1203 fw.indent() << "max_v " << trans._max_v << "\n";
1204 fw.indent() << "factor " << trans._factor << "\n";
1205 fw.indent() << "offset " << trans._offset << "\n";
1209 double computeScaleFactor(osg::NodeVisitor* nv) const
1214 double scale_factor = (toOsg(_center) - nv->getEyePoint()).length();
1216 scale_factor = _factor * scale_factor + _offset;
1218 scale_factor = _table->interpolate( scale_factor );
1220 if (scale_factor < _min_v)
1221 scale_factor = _min_v;
1222 if (scale_factor > _max_v)
1223 scale_factor = _max_v;
1225 return scale_factor;
1228 SGSharedPtr<SGInterpTable> _table;
1237 SGDistScaleAnimation::SGDistScaleAnimation(const SGPropertyNode* configNode,
1238 SGPropertyNode* modelRoot) :
1239 SGAnimation(configNode, modelRoot)
1244 SGDistScaleAnimation::createAnimationGroup(osg::Group& parent)
1246 Transform* transform = new Transform(getConfig());
1247 parent.addChild(transform);
1253 osgDB::RegisterDotOsgWrapperProxy distScaleAnimationTransformProxy
1255 new SGDistScaleAnimation::Transform,
1256 "SGDistScaleAnimation::Transform",
1257 "Object Node Transform SGDistScaleAnimation::Transform Group",
1259 &SGDistScaleAnimation::Transform::writeLocalData
1263 ////////////////////////////////////////////////////////////////////////
1264 // Implementation of flash animation
1265 ////////////////////////////////////////////////////////////////////////
1267 class SGFlashAnimation::Transform : public osg::Transform {
1269 Transform() : _power(0.0), _factor(0.0), _offset(0.0), _min_v(0.0),
1270 _max_v(0.0), _two_sides(false)
1273 Transform(const Transform& rhs,
1274 const osg::CopyOp& copyOp = osg::CopyOp::SHALLOW_COPY)
1275 : osg::Transform(rhs, copyOp), _center(rhs._center), _axis(rhs._axis),
1276 _power(rhs._power), _factor(rhs._factor), _offset(rhs._offset),
1277 _min_v(rhs._min_v), _max_v(rhs._max_v), _two_sides(rhs._two_sides)
1280 META_Node(simgear, SGFlashAnimation::Transform);
1282 Transform(const SGPropertyNode* configNode)
1284 setReferenceFrame(RELATIVE_RF);
1285 setName(configNode->getStringValue("name", "flash animation"));
1286 setStateSet(getNormalizeStateSet());
1288 _axis[0] = configNode->getFloatValue("axis/x", 0);
1289 _axis[1] = configNode->getFloatValue("axis/y", 0);
1290 _axis[2] = configNode->getFloatValue("axis/z", 1);
1293 _center[0] = configNode->getFloatValue("center/x-m", 0);
1294 _center[1] = configNode->getFloatValue("center/y-m", 0);
1295 _center[2] = configNode->getFloatValue("center/z-m", 0);
1297 _offset = configNode->getFloatValue("offset", 0);
1298 _factor = configNode->getFloatValue("factor", 1);
1299 _power = configNode->getFloatValue("power", 1);
1300 _two_sides = configNode->getBoolValue("two-sides", false);
1302 _min_v = configNode->getFloatValue("min", SGLimitsf::epsilon());
1303 _max_v = configNode->getFloatValue("max", 1);
1305 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
1306 osg::NodeVisitor* nv) const
1308 osg::Matrix transform;
1309 double scale_factor = computeScaleFactor(nv);
1310 transform(0,0) = scale_factor;
1311 transform(1,1) = scale_factor;
1312 transform(2,2) = scale_factor;
1313 transform(3,0) = _center[0]*(1 - scale_factor);
1314 transform(3,1) = _center[1]*(1 - scale_factor);
1315 transform(3,2) = _center[2]*(1 - scale_factor);
1316 matrix.preMult(transform);
1320 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
1321 osg::NodeVisitor* nv) const
1323 double scale_factor = computeScaleFactor(nv);
1324 if (fabs(scale_factor) <= SGLimits<double>::min())
1326 osg::Matrix transform;
1327 double rScaleFactor = 1/scale_factor;
1328 transform(0,0) = rScaleFactor;
1329 transform(1,1) = rScaleFactor;
1330 transform(2,2) = rScaleFactor;
1331 transform(3,0) = _center[0]*(1 - rScaleFactor);
1332 transform(3,1) = _center[1]*(1 - rScaleFactor);
1333 transform(3,2) = _center[2]*(1 - rScaleFactor);
1334 matrix.postMult(transform);
1338 static bool writeLocalData(const osg::Object& obj, osgDB::Output& fw)
1340 const Transform& trans = static_cast<const Transform&>(obj);
1341 fw.indent() << "center " << trans._center[0] << " "
1342 << trans._center[1] << " " << trans._center[2] << " " << "\n";
1343 fw.indent() << "axis " << trans._axis[0] << " "
1344 << trans._axis[1] << " " << trans._axis[2] << " " << "\n";
1345 fw.indent() << "power " << trans._power << " \n";
1346 fw.indent() << "min_v " << trans._min_v << "\n";
1347 fw.indent() << "max_v " << trans._max_v << "\n";
1348 fw.indent() << "factor " << trans._factor << "\n";
1349 fw.indent() << "offset " << trans._offset << "\n";
1350 fw.indent() << "twosides " << (trans._two_sides ? "true" : "false") << "\n";
1354 double computeScaleFactor(osg::NodeVisitor* nv) const
1359 osg::Vec3 localEyeToCenter = nv->getEyePoint() - _center;
1360 localEyeToCenter.normalize();
1362 double cos_angle = localEyeToCenter*_axis;
1363 double scale_factor = 0;
1364 if ( _two_sides && cos_angle < 0 )
1365 scale_factor = _factor * pow( -cos_angle, _power ) + _offset;
1366 else if ( cos_angle > 0 )
1367 scale_factor = _factor * pow( cos_angle, _power ) + _offset;
1369 if ( scale_factor < _min_v )
1370 scale_factor = _min_v;
1371 if ( scale_factor > _max_v )
1372 scale_factor = _max_v;
1374 return scale_factor;
1377 virtual osg::BoundingSphere computeBound() const
1379 // avoid being culled away by small feature culling
1380 osg::BoundingSphere bs = osg::Group::computeBound();
1381 bs.radius() *= _max_v;
1388 double _power, _factor, _offset, _min_v, _max_v;
1393 SGFlashAnimation::SGFlashAnimation(const SGPropertyNode* configNode,
1394 SGPropertyNode* modelRoot) :
1395 SGAnimation(configNode, modelRoot)
1400 SGFlashAnimation::createAnimationGroup(osg::Group& parent)
1402 Transform* transform = new Transform(getConfig());
1403 parent.addChild(transform);
1409 osgDB::RegisterDotOsgWrapperProxy flashAnimationTransformProxy
1411 new SGFlashAnimation::Transform,
1412 "SGFlashAnimation::Transform",
1413 "Object Node Transform SGFlashAnimation::Transform Group",
1415 &SGFlashAnimation::Transform::writeLocalData
1419 ////////////////////////////////////////////////////////////////////////
1420 // Implementation of billboard animation
1421 ////////////////////////////////////////////////////////////////////////
1423 class SGBillboardAnimation::Transform : public osg::Transform {
1425 Transform() : _spherical(true) {}
1426 Transform(const Transform& rhs,
1427 const osg::CopyOp& copyOp = osg::CopyOp::SHALLOW_COPY)
1428 : osg::Transform(rhs, copyOp), _spherical(rhs._spherical) {}
1429 META_Node(simgear, SGBillboardAnimation::Transform);
1430 Transform(const SGPropertyNode* configNode) :
1431 _spherical(configNode->getBoolValue("spherical", true))
1433 setReferenceFrame(RELATIVE_RF);
1434 setName(configNode->getStringValue("name", "billboard animation"));
1436 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
1437 osg::NodeVisitor* nv) const
1439 // More or less taken from plibs ssgCutout
1441 matrix(0,0) = 1; matrix(0,1) = 0; matrix(0,2) = 0;
1442 matrix(1,0) = 0; matrix(1,1) = 0; matrix(1,2) = -1;
1443 matrix(2,0) = 0; matrix(2,1) = 1; matrix(2,2) = 0;
1445 osg::Vec3 zAxis(matrix(2, 0), matrix(2, 1), matrix(2, 2));
1446 osg::Vec3 xAxis = osg::Vec3(0, 0, -1)^zAxis;
1447 osg::Vec3 yAxis = zAxis^xAxis;
1453 matrix(0,0) = xAxis[0]; matrix(0,1) = xAxis[1]; matrix(0,2) = xAxis[2];
1454 matrix(1,0) = yAxis[0]; matrix(1,1) = yAxis[1]; matrix(1,2) = yAxis[2];
1455 matrix(2,0) = zAxis[0]; matrix(2,1) = zAxis[1]; matrix(2,2) = zAxis[2];
1460 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
1461 osg::NodeVisitor* nv) const
1463 // Hmm, don't yet know how to get that back ...
1466 static bool writeLocalData(const osg::Object& obj, osgDB::Output& fw)
1468 const Transform& trans = static_cast<const Transform&>(obj);
1470 fw.indent() << (trans._spherical ? "true" : "false") << "\n";
1478 SGBillboardAnimation::SGBillboardAnimation(const SGPropertyNode* configNode,
1479 SGPropertyNode* modelRoot) :
1480 SGAnimation(configNode, modelRoot)
1485 SGBillboardAnimation::createAnimationGroup(osg::Group& parent)
1487 Transform* transform = new Transform(getConfig());
1488 parent.addChild(transform);
1494 osgDB::RegisterDotOsgWrapperProxy billboardAnimationTransformProxy
1496 new SGBillboardAnimation::Transform,
1497 "SGBillboardAnimation::Transform",
1498 "Object Node Transform SGBillboardAnimation::Transform Group",
1500 &SGBillboardAnimation::Transform::writeLocalData
1504 ////////////////////////////////////////////////////////////////////////
1505 // Implementation of a range animation
1506 ////////////////////////////////////////////////////////////////////////
1508 class SGRangeAnimation::UpdateCallback : public osg::NodeCallback {
1510 UpdateCallback(const SGCondition* condition,
1511 const SGExpressiond* minAnimationValue,
1512 const SGExpressiond* maxAnimationValue,
1513 double minValue, double maxValue) :
1514 _condition(condition),
1515 _minAnimationValue(minAnimationValue),
1516 _maxAnimationValue(maxAnimationValue),
1517 _minStaticValue(minValue),
1518 _maxStaticValue(maxValue)
1520 setName("SGRangeAnimation::UpdateCallback");
1522 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1524 osg::LOD* lod = static_cast<osg::LOD*>(node);
1525 if (!_condition || _condition->test()) {
1527 if (_minAnimationValue)
1528 minRange = _minAnimationValue->getValue();
1530 minRange = _minStaticValue;
1532 if (_maxAnimationValue)
1533 maxRange = _maxAnimationValue->getValue();
1535 maxRange = _maxStaticValue;
1536 lod->setRange(0, minRange, maxRange);
1538 lod->setRange(0, 0, SGLimitsf::max());
1544 SGSharedPtr<const SGCondition> _condition;
1545 SGSharedPtr<const SGExpressiond> _minAnimationValue;
1546 SGSharedPtr<const SGExpressiond> _maxAnimationValue;
1547 double _minStaticValue;
1548 double _maxStaticValue;
1551 SGRangeAnimation::SGRangeAnimation(const SGPropertyNode* configNode,
1552 SGPropertyNode* modelRoot) :
1553 SGAnimation(configNode, modelRoot)
1555 _condition = getCondition();
1557 std::string inputPropertyName;
1558 inputPropertyName = configNode->getStringValue("min-property", "");
1559 if (!inputPropertyName.empty()) {
1560 SGPropertyNode* inputProperty;
1561 inputProperty = modelRoot->getNode(inputPropertyName, true);
1562 SGSharedPtr<SGExpressiond> value;
1563 value = new SGPropertyExpression<double>(inputProperty);
1565 value = read_factor_offset(configNode, value, "min-factor", "min-offset");
1566 _minAnimationValue = value->simplify();
1568 inputPropertyName = configNode->getStringValue("max-property", "");
1569 if (!inputPropertyName.empty()) {
1570 SGPropertyNode* inputProperty;
1571 inputProperty = modelRoot->getNode(inputPropertyName.c_str(), true);
1573 SGSharedPtr<SGExpressiond> value;
1574 value = new SGPropertyExpression<double>(inputProperty);
1576 value = read_factor_offset(configNode, value, "max-factor", "max-offset");
1577 _maxAnimationValue = value->simplify();
1580 _initialValue[0] = configNode->getDoubleValue("min-m", 0);
1581 _initialValue[0] *= configNode->getDoubleValue("min-factor", 1);
1582 _initialValue[1] = configNode->getDoubleValue("max-m", SGLimitsf::max());
1583 _initialValue[1] *= configNode->getDoubleValue("max-factor", 1);
1587 SGRangeAnimation::createAnimationGroup(osg::Group& parent)
1589 osg::Group* group = new osg::Group;
1590 group->setName("range animation group");
1592 osg::LOD* lod = new osg::LOD;
1593 lod->setName("range animation node");
1594 parent.addChild(lod);
1596 lod->addChild(group, _initialValue[0], _initialValue[1]);
1597 lod->setCenterMode(osg::LOD::USE_BOUNDING_SPHERE_CENTER);
1598 lod->setRangeMode(osg::LOD::DISTANCE_FROM_EYE_POINT);
1599 if (_minAnimationValue || _maxAnimationValue || _condition) {
1601 uc = new UpdateCallback(_condition, _minAnimationValue, _maxAnimationValue,
1602 _initialValue[0], _initialValue[1]);
1603 lod->setUpdateCallback(uc);
1609 ////////////////////////////////////////////////////////////////////////
1610 // Implementation of a select animation
1611 ////////////////////////////////////////////////////////////////////////
1613 SGSelectAnimation::SGSelectAnimation(const SGPropertyNode* configNode,
1614 SGPropertyNode* modelRoot) :
1615 SGAnimation(configNode, modelRoot)
1620 SGSelectAnimation::createAnimationGroup(osg::Group& parent)
1622 // if no condition given, this is a noop.
1623 SGSharedPtr<SGCondition const> condition = getCondition();
1624 // trick, gets deleted with all its 'animated' children
1625 // when the animation installer returns
1627 return new osg::Group;
1628 simgear::ConditionNode* cn = new simgear::ConditionNode;
1629 cn->setName("select animation node");
1630 cn->setCondition(condition.ptr());
1631 osg::Group* grp = new osg::Group;
1633 parent.addChild(cn);
1639 ////////////////////////////////////////////////////////////////////////
1640 // Implementation of alpha test animation
1641 ////////////////////////////////////////////////////////////////////////
1643 SGAlphaTestAnimation::SGAlphaTestAnimation(const SGPropertyNode* configNode,
1644 SGPropertyNode* modelRoot) :
1645 SGAnimation(configNode, modelRoot)
1651 // Keep one copy of the most common alpha test its state set.
1652 ReentrantMutex alphaTestMutex;
1653 osg::ref_ptr<osg::AlphaFunc> standardAlphaFunc;
1654 osg::ref_ptr<osg::StateSet> alphaFuncStateSet;
1656 osg::AlphaFunc* makeAlphaFunc(float clamp)
1658 ScopedLock<ReentrantMutex> lock(alphaTestMutex);
1659 if (osg::equivalent(clamp, 0.01f)) {
1660 if (standardAlphaFunc.valid())
1661 return standardAlphaFunc.get();
1664 osg::AlphaFunc* alphaFunc = new osg::AlphaFunc;
1665 alphaFunc->setFunction(osg::AlphaFunc::GREATER);
1666 alphaFunc->setReferenceValue(clamp);
1667 alphaFunc->setDataVariance(osg::Object::STATIC);
1668 if (osg::equivalent(clamp, 0.01f))
1669 standardAlphaFunc = alphaFunc;
1673 osg::StateSet* makeAlphaTestStateSet(float clamp)
1675 using namespace OpenThreads;
1676 ScopedLock<ReentrantMutex> lock(alphaTestMutex);
1677 if (osg::equivalent(clamp, 0.01f)) {
1678 if (alphaFuncStateSet.valid())
1679 return alphaFuncStateSet.get();
1681 osg::AlphaFunc* alphaFunc = makeAlphaFunc(clamp);
1682 osg::StateSet* stateSet = new osg::StateSet;
1683 stateSet->setAttributeAndModes(alphaFunc,
1684 (osg::StateAttribute::ON
1685 | osg::StateAttribute::OVERRIDE));
1686 stateSet->setDataVariance(osg::Object::STATIC);
1687 if (osg::equivalent(clamp, 0.01f))
1688 alphaFuncStateSet = stateSet;
1693 SGAlphaTestAnimation::install(osg::Node& node)
1695 SGAnimation::install(node);
1697 float alphaClamp = getConfig()->getFloatValue("alpha-factor", 0);
1698 osg::StateSet* stateSet = node.getStateSet();
1700 node.setStateSet(makeAlphaTestStateSet(alphaClamp));
1702 stateSet->setAttributeAndModes(makeAlphaFunc(alphaClamp),
1703 (osg::StateAttribute::ON
1704 | osg::StateAttribute::OVERRIDE));
1709 //////////////////////////////////////////////////////////////////////
1710 // Blend animation installer
1711 //////////////////////////////////////////////////////////////////////
1713 // XXX This needs to be replaced by something using TexEnvCombine to
1714 // change the blend factor. Changing the alpha values in the geometry
1716 class SGBlendAnimation::BlendVisitor : public osg::NodeVisitor {
1718 BlendVisitor(float blend) :
1719 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
1721 { setVisitorType(osg::NodeVisitor::NODE_VISITOR); }
1722 virtual void apply(osg::Node& node)
1724 updateStateSet(node.getStateSet());
1727 virtual void apply(osg::Geode& node)
1729 apply((osg::Node&)node);
1730 unsigned nDrawables = node.getNumDrawables();
1731 for (unsigned i = 0; i < nDrawables; ++i) {
1732 osg::Drawable* drawable = node.getDrawable(i);
1733 osg::Geometry* geometry = drawable->asGeometry();
1736 osg::Array* array = geometry->getColorArray();
1739 osg::Vec4Array* vec4Array = dynamic_cast<osg::Vec4Array*>(array);
1742 for (unsigned k = 0; k < vec4Array->size(); ++k) {
1743 (*vec4Array)[k][3] = _blend;
1746 updateStateSet(drawable->getStateSet());
1749 void updateStateSet(osg::StateSet* stateSet)
1753 osg::StateAttribute* stateAttribute;
1754 stateAttribute = stateSet->getAttribute(osg::StateAttribute::MATERIAL);
1755 if (!stateAttribute)
1757 osg::Material* material = dynamic_cast<osg::Material*>(stateAttribute);
1760 material->setAlpha(osg::Material::FRONT_AND_BACK, _blend);
1762 stateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
1763 stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
1765 stateSet->setRenderingHint(osg::StateSet::DEFAULT_BIN);
1772 class SGBlendAnimation::UpdateCallback : public osg::NodeCallback {
1774 UpdateCallback(const SGPropertyNode* configNode, const SGExpressiond* v) :
1778 setName("SGBlendAnimation::UpdateCallback");
1780 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1782 double blend = _animationValue->getValue();
1783 if (blend != _prev_value) {
1784 _prev_value = blend;
1785 BlendVisitor visitor(1-blend);
1786 node->accept(visitor);
1792 SGSharedPtr<SGExpressiond const> _animationValue;
1796 SGBlendAnimation::SGBlendAnimation(const SGPropertyNode* configNode,
1797 SGPropertyNode* modelRoot)
1798 : SGAnimation(configNode, modelRoot),
1799 _animationValue(read_value(configNode, modelRoot, "", 0, 1))
1804 SGBlendAnimation::createAnimationGroup(osg::Group& parent)
1806 if (!_animationValue)
1809 osg::Group* group = new osg::Switch;
1810 group->setName("blend animation node");
1811 group->setUpdateCallback(new UpdateCallback(getConfig(), _animationValue));
1812 parent.addChild(group);
1817 SGBlendAnimation::install(osg::Node& node)
1819 SGAnimation::install(node);
1820 // make sure we do not change common geometries,
1821 // that also creates new display lists for these subgeometries.
1822 cloneDrawables(node);
1823 DoDrawArraysVisitor visitor;
1824 node.accept(visitor);
1828 //////////////////////////////////////////////////////////////////////
1829 // Timed animation installer
1830 //////////////////////////////////////////////////////////////////////
1834 class SGTimedAnimation::UpdateCallback : public osg::NodeCallback {
1836 UpdateCallback(const SGPropertyNode* configNode) :
1839 _duration_sec(configNode->getDoubleValue("duration-sec", 1)),
1840 _last_time_sec(SGLimitsd::max()),
1841 _use_personality(configNode->getBoolValue("use-personality", false))
1843 std::vector<SGSharedPtr<SGPropertyNode> > nodes;
1844 nodes = configNode->getChildren("branch-duration-sec");
1845 for (size_t i = 0; i < nodes.size(); ++i) {
1846 unsigned ind = nodes[ i ]->getIndex();
1847 while ( ind >= _durations.size() ) {
1848 _durations.push_back(DurationSpec(_duration_sec));
1850 SGPropertyNode_ptr rNode = nodes[i]->getChild("random");
1852 _durations[ind] = DurationSpec(nodes[ i ]->getDoubleValue());
1854 _durations[ind] = DurationSpec(rNode->getDoubleValue( "min", 0),
1855 rNode->getDoubleValue( "max", 1));
1858 setName("SGTimedAnimation::UpdateCallback");
1860 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1862 assert(dynamic_cast<osg::Switch*>(node));
1863 osg::Switch* sw = static_cast<osg::Switch*>(node);
1865 unsigned nChildren = sw->getNumChildren();
1867 // blow up the durations vector to the required size
1868 while (_durations.size() < nChildren) {
1869 _durations.push_back(_duration_sec);
1871 // make sure the current index is an duration that really exists
1872 _current_index = _current_index % nChildren;
1874 // update the time and compute the current systems time value
1875 double t = nv->getFrameStamp()->getSimulationTime();
1876 if (_last_time_sec == SGLimitsd::max()) {
1879 double dt = t - _last_time_sec;
1880 if (_use_personality)
1881 dt *= 1 + 0.2*(0.5 - sg_random());
1886 double currentDuration = _durations[_current_index].get();
1887 while (currentDuration < _reminder) {
1888 _reminder -= currentDuration;
1889 _current_index = (_current_index + 1) % nChildren;
1890 currentDuration = _durations[_current_index].get();
1893 sw->setSingleChildOn(_current_index);
1899 struct DurationSpec {
1900 DurationSpec(double t) :
1901 minTime(SGMiscd::max(0.01, t)),
1902 maxTime(SGMiscd::max(0.01, t))
1904 DurationSpec(double t0, double t1) :
1905 minTime(SGMiscd::max(0.01, t0)),
1906 maxTime(SGMiscd::max(0.01, t1))
1909 { return minTime + sg_random()*(maxTime - minTime); }
1913 std::vector<DurationSpec> _durations;
1914 unsigned _current_index;
1916 double _duration_sec;
1917 double _last_time_sec;
1918 bool _use_personality;
1922 SGTimedAnimation::SGTimedAnimation(const SGPropertyNode* configNode,
1923 SGPropertyNode* modelRoot)
1924 : SGAnimation(configNode, modelRoot)
1929 SGTimedAnimation::createAnimationGroup(osg::Group& parent)
1931 osg::Switch* sw = new osg::Switch;
1932 sw->setName("timed animation node");
1933 sw->setUpdateCallback(new UpdateCallback(getConfig()));
1934 parent.addChild(sw);
1939 ////////////////////////////////////////////////////////////////////////
1940 // dynamically switch on/off shadows
1941 ////////////////////////////////////////////////////////////////////////
1943 class SGShadowAnimation::UpdateCallback : public osg::NodeCallback {
1945 UpdateCallback(const SGCondition* condition) :
1946 _condition(condition)
1948 setName("SGShadowAnimation::UpdateCallback");
1950 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1952 if (_condition->test())
1953 node->setNodeMask( SG_NODEMASK_CASTSHADOW_BIT | node->getNodeMask());
1955 node->setNodeMask(~SG_NODEMASK_CASTSHADOW_BIT & node->getNodeMask());
1960 SGSharedPtr<const SGCondition> _condition;
1963 SGShadowAnimation::SGShadowAnimation(const SGPropertyNode* configNode,
1964 SGPropertyNode* modelRoot) :
1965 SGAnimation(configNode, modelRoot)
1970 SGShadowAnimation::createAnimationGroup(osg::Group& parent)
1972 SGSharedPtr<SGCondition const> condition = getCondition();
1974 osg::Group* group = new osg::Group;
1975 group->setName("shadow animation");
1977 group->setUpdateCallback(new UpdateCallback(condition));
1979 group->setNodeMask(~SG_NODEMASK_CASTSHADOW_BIT & group->getNodeMask());
1980 parent.addChild(group);
1985 ////////////////////////////////////////////////////////////////////////
1986 // Implementation of SGTexTransformAnimation
1987 ////////////////////////////////////////////////////////////////////////
1989 class SGTexTransformAnimation::Transform : public SGReferenced {
1994 virtual ~Transform()
1996 void setValue(double value)
1998 virtual void transform(osg::Matrix&) = 0;
2003 class SGTexTransformAnimation::Translation :
2004 public SGTexTransformAnimation::Transform {
2006 Translation(const SGVec3d& axis) :
2009 virtual void transform(osg::Matrix& matrix)
2012 set_translation(tmp, _value, _axis);
2013 matrix.preMult(tmp);
2019 class SGTexTransformAnimation::Rotation :
2020 public SGTexTransformAnimation::Transform {
2022 Rotation(const SGVec3d& axis, const SGVec3d& center) :
2026 virtual void transform(osg::Matrix& matrix)
2029 SGRotateTransform::set_rotation(tmp, SGMiscd::deg2rad(_value), _center,
2031 matrix.preMult(tmp);
2038 class SGTexTransformAnimation::Trapezoid :
2039 public SGTexTransformAnimation::Transform {
2042 enum Side { TOP, RIGHT, BOTTOM, LEFT };
2044 Trapezoid(Side side):
2047 virtual void transform(osg::Matrix& matrix)
2049 VGfloat sx0 = 0, sy0 = 0,
2073 VGUErrorCode err = vguComputeWarpQuadToSquare( sx0, sy0,
2078 if( err != VGU_NO_ERROR )
2081 matrix.preMult( osg::Matrix(
2082 mat[0][0], mat[0][1], 0, mat[0][2],
2083 mat[1][0], mat[1][1], 0, mat[1][2],
2085 mat[2][0], mat[2][1], 0, mat[2][2]
2093 class SGTexTransformAnimation::UpdateCallback :
2094 public osg::StateAttribute::Callback {
2096 UpdateCallback(const SGCondition* condition) :
2097 _condition(condition)
2099 setName("SGTexTransformAnimation::UpdateCallback");
2101 virtual void operator () (osg::StateAttribute* sa, osg::NodeVisitor*)
2103 if (!_condition || _condition->test()) {
2104 TransformList::const_iterator i;
2105 for (i = _transforms.begin(); i != _transforms.end(); ++i)
2106 i->transform->setValue(i->value->getValue());
2108 assert(dynamic_cast<osg::TexMat*>(sa));
2109 osg::TexMat* texMat = static_cast<osg::TexMat*>(sa);
2110 texMat->getMatrix().makeIdentity();
2111 TransformList::const_iterator i;
2112 for (i = _transforms.begin(); i != _transforms.end(); ++i)
2113 i->transform->transform(texMat->getMatrix());
2115 void appendTransform(Transform* transform, SGExpressiond* value)
2117 Entry entry = { transform, value };
2118 transform->transform(_matrix);
2119 _transforms.push_back(entry);
2124 SGSharedPtr<Transform> transform;
2125 SGSharedPtr<const SGExpressiond> value;
2127 typedef std::vector<Entry> TransformList;
2128 TransformList _transforms;
2129 SGSharedPtr<const SGCondition> _condition;
2130 osg::Matrix _matrix;
2133 SGTexTransformAnimation::SGTexTransformAnimation(const SGPropertyNode* configNode,
2134 SGPropertyNode* modelRoot) :
2135 SGAnimation(configNode, modelRoot)
2140 SGTexTransformAnimation::createAnimationGroup(osg::Group& parent)
2142 osg::Group* group = new osg::Group;
2143 group->setName("texture transform group");
2144 osg::StateSet* stateSet = group->getOrCreateStateSet();
2145 stateSet->setDataVariance(osg::Object::DYNAMIC);
2146 osg::TexMat* texMat = new osg::TexMat;
2147 UpdateCallback* updateCallback = new UpdateCallback(getCondition());
2148 // interpret the configs ...
2149 std::string type = getType();
2151 if (type == "textranslate") {
2152 appendTexTranslate(*getConfig(), updateCallback);
2153 } else if (type == "texrotate") {
2154 appendTexRotate(*getConfig(), updateCallback);
2155 } else if (type == "textrapezoid") {
2156 appendTexTrapezoid(*getConfig(), updateCallback);
2157 } else if (type == "texmultiple") {
2158 std::vector<SGSharedPtr<SGPropertyNode> > transformConfigs;
2159 transformConfigs = getConfig()->getChildren("transform");
2160 for (unsigned i = 0; i < transformConfigs.size(); ++i) {
2161 std::string subtype = transformConfigs[i]->getStringValue("subtype", "");
2162 if (subtype == "textranslate")
2163 appendTexTranslate(*transformConfigs[i], updateCallback);
2164 else if (subtype == "texrotate")
2165 appendTexRotate(*transformConfigs[i], updateCallback);
2166 else if (subtype == "textrapezoid")
2167 appendTexTrapezoid(*transformConfigs[i], updateCallback);
2169 SG_LOG(SG_INPUT, SG_ALERT,
2170 "Ignoring unknown texture transform subtype");
2173 SG_LOG(SG_INPUT, SG_ALERT, "Ignoring unknown texture transform type");
2176 texMat->setUpdateCallback(updateCallback);
2177 stateSet->setTextureAttribute(0, texMat);
2178 parent.addChild(group);
2183 SGTexTransformAnimation::readValue( const SGPropertyNode& cfg,
2184 const std::string& suffix )
2186 std::string prop_name = cfg.getStringValue("property");
2187 SGSharedPtr<SGExpressiond> value;
2188 if( prop_name.empty() )
2189 value = new SGConstExpression<double>(0);
2191 value = new SGPropertyExpression<double>
2193 getModelRoot()->getNode(prop_name, true)
2196 SGInterpTable* table = read_interpolation_table(&cfg);
2199 value = new SGInterpTableExpression<double>(value, table);
2200 double biasValue = cfg.getDoubleValue("bias", 0);
2202 value = new SGBiasExpression<double>(value, biasValue);
2203 value = new SGStepExpression<double>( value,
2204 cfg.getDoubleValue("step", 0),
2205 cfg.getDoubleValue("scroll", 0) );
2209 double biasValue = cfg.getDoubleValue("bias", 0);
2211 value = new SGBiasExpression<double>(value, biasValue);
2212 value = new SGStepExpression<double>(value,
2213 cfg.getDoubleValue("step", 0),
2214 cfg.getDoubleValue("scroll", 0));
2215 value = read_offset_factor(&cfg, value, "factor", "offset" + suffix);
2217 if( cfg.hasChild("min" + suffix)
2218 || cfg.hasChild("max" + suffix) )
2220 double minClip = cfg.getDoubleValue("min" + suffix, -SGLimitsd::max());
2221 double maxClip = cfg.getDoubleValue("max" + suffix, SGLimitsd::max());
2222 value = new SGClipExpression<double>(value, minClip, maxClip);
2226 return value.release()->simplify();
2230 SGTexTransformAnimation::appendTexTranslate( const SGPropertyNode& cfg,
2231 UpdateCallback* updateCallback )
2233 Translation* translation = new Translation(normalize(readVec3(cfg, "axis")));
2234 translation->setValue(cfg.getDoubleValue("starting-position", 0));
2235 updateCallback->appendTransform(translation, readValue(cfg));
2239 SGTexTransformAnimation::appendTexRotate( const SGPropertyNode& cfg,
2240 UpdateCallback* updateCallback )
2242 Rotation* rotation = new Rotation( normalize(readVec3(cfg, "axis")),
2243 readVec3(cfg, "center") );
2244 rotation->setValue(cfg.getDoubleValue("starting-position-deg", 0));
2245 updateCallback->appendTransform(rotation, readValue(cfg, "-deg"));
2249 SGTexTransformAnimation::appendTexTrapezoid( const SGPropertyNode& cfg,
2250 UpdateCallback* updateCallback )
2252 Trapezoid::Side side = Trapezoid::TOP;
2253 const std::string side_str = cfg.getStringValue("side");
2254 if( side_str == "right" )
2255 side = Trapezoid::RIGHT;
2256 else if( side_str == "bottom" )
2257 side = Trapezoid::BOTTOM;
2258 else if( side_str == "left" )
2259 side = Trapezoid::LEFT;
2261 Trapezoid* trapezoid = new Trapezoid(side);
2262 trapezoid->setValue(cfg.getDoubleValue("starting-position", 0));
2263 updateCallback->appendTransform(trapezoid, readValue(cfg));