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"
61 #include "ConditionNode.hxx"
63 using OpenThreads::Mutex;
64 using OpenThreads::ReentrantMutex;
65 using OpenThreads::ScopedLock;
67 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);
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 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 == "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 == "translate") {
428 SGTranslateAnimation animInst(configNode, modelRoot);
429 animInst.apply(node);
430 } else if (type == "light") {
431 SGLightAnimation animInst(configNode, modelRoot, options, path, i);
432 animInst.apply(node);
433 } else if (type == "null" || type == "none" || type.empty()) {
434 SGGroupAnimation animInst(configNode, modelRoot);
435 animInst.apply(node);
444 SGAnimation::apply(osg::Node* node)
446 // duh what a special case ...
447 if (_objectNames.empty()) {
448 osg::Group* group = node->asGroup();
450 osg::ref_ptr<osg::Group> animationGroup;
451 installInGroup(std::string(), *group, animationGroup);
458 SGAnimation::install(osg::Node& node)
462 node.setNodeMask( SG_NODEMASK_TERRAIN_BIT | node.getNodeMask());
464 node.setNodeMask(~SG_NODEMASK_TERRAIN_BIT & node.getNodeMask());
468 SGAnimation::createAnimationGroup(osg::Group& parent)
470 // default implementation, we do not need a new group
471 // for every animation type. Usually animations that just change
472 // the StateSet of some parts of the model
477 SGAnimation::apply(osg::Group& group)
479 // the trick is to first traverse the children and then
480 // possibly splice in a new group node if required.
481 // Else we end up in a recursive loop where we infinitly insert new
485 // Note that this algorithm preserves the order of the child objects
486 // like they appear in the object-name tags.
487 // The timed animations require this
488 osg::ref_ptr<osg::Group> animationGroup;
489 std::list<std::string>::const_iterator nameIt;
490 for (nameIt = _objectNames.begin(); nameIt != _objectNames.end(); ++nameIt)
491 installInGroup(*nameIt, group, animationGroup);
495 SGAnimation::installInGroup(const std::string& name, osg::Group& group,
496 osg::ref_ptr<osg::Group>& animationGroup)
498 int i = group.getNumChildren() - 1;
499 for (; 0 <= i; --i) {
500 osg::Node* child = group.getChild(i);
502 // Check if this one is already processed
503 if (std::find(_installedAnimations.begin(),
504 _installedAnimations.end(), child)
505 != _installedAnimations.end())
508 if (name.empty() || child->getName() == name) {
509 // fire the installation of the animation
512 // create a group node on demand
513 if (!animationGroup.valid()) {
514 animationGroup = createAnimationGroup(group);
515 // Animation type that does not require a new group,
516 // in this case we can stop and look for the next object
517 if (animationGroup.valid() && !_name.empty())
518 animationGroup->setName(_name);
520 if (animationGroup.valid()) {
521 animationGroup->addChild(child);
522 group.removeChild(i);
525 // store that we already have processed this child node
526 // We can hit this one twice if an animation references some
527 // part of a subtree twice
528 _installedAnimations.push_back(child);
534 SGAnimation::removeMode(osg::Node& node, osg::StateAttribute::GLMode mode)
536 RemoveModeVisitor visitor(mode);
537 node.accept(visitor);
541 SGAnimation::removeAttribute(osg::Node& node, osg::StateAttribute::Type type)
543 RemoveAttributeVisitor visitor(type);
544 node.accept(visitor);
548 SGAnimation::removeTextureMode(osg::Node& node, unsigned unit,
549 osg::StateAttribute::GLMode mode)
551 RemoveTextureModeVisitor visitor(unit, mode);
552 node.accept(visitor);
556 SGAnimation::removeTextureAttribute(osg::Node& node, unsigned unit,
557 osg::StateAttribute::Type type)
559 RemoveTextureAttributeVisitor visitor(unit, type);
560 node.accept(visitor);
564 SGAnimation::setRenderBinToInherit(osg::Node& node)
566 BinToInheritVisitor visitor;
567 node.accept(visitor);
571 SGAnimation::cloneDrawables(osg::Node& node)
573 DrawableCloneVisitor visitor;
574 node.accept(visitor);
578 SGAnimation::getCondition() const
580 const SGPropertyNode* conditionNode = _configNode->getChild("condition");
583 return sgReadCondition(_modelRoot, conditionNode);
588 ////////////////////////////////////////////////////////////////////////
589 // Implementation of null animation
590 ////////////////////////////////////////////////////////////////////////
592 // Ok, that is to build a subgraph from different other
593 // graph nodes. I guess that this stems from the time where modellers
594 // could not build hierarchical trees ...
595 SGGroupAnimation::SGGroupAnimation(const SGPropertyNode* configNode,
596 SGPropertyNode* modelRoot):
597 SGAnimation(configNode, modelRoot)
602 SGGroupAnimation::createAnimationGroup(osg::Group& parent)
604 osg::Group* group = new osg::Group;
605 parent.addChild(group);
610 ////////////////////////////////////////////////////////////////////////
611 // Implementation of translate animation
612 ////////////////////////////////////////////////////////////////////////
614 class SGTranslateAnimation::UpdateCallback : public osg::NodeCallback {
616 UpdateCallback(SGCondition const* condition,
617 SGExpressiond const* animationValue) :
618 _condition(condition),
619 _animationValue(animationValue)
621 setName("SGTranslateAnimation::UpdateCallback");
623 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
625 if (!_condition || _condition->test()) {
626 SGTranslateTransform* transform;
627 transform = static_cast<SGTranslateTransform*>(node);
628 transform->setValue(_animationValue->getValue());
633 SGSharedPtr<SGCondition const> _condition;
634 SGSharedPtr<SGExpressiond const> _animationValue;
637 SGTranslateAnimation::SGTranslateAnimation(const SGPropertyNode* configNode,
638 SGPropertyNode* modelRoot) :
639 SGAnimation(configNode, modelRoot)
641 _condition = getCondition();
642 SGSharedPtr<SGExpressiond> value;
643 value = read_value(configNode, modelRoot, "-m",
644 -SGLimitsd::max(), SGLimitsd::max());
645 _animationValue = value->simplify();
647 _initialValue = _animationValue->getValue();
651 _axis = readTranslateAxis(configNode);
655 SGTranslateAnimation::createAnimationGroup(osg::Group& parent)
657 SGTranslateTransform* transform = new SGTranslateTransform;
658 transform->setName("translate animation");
659 if (_animationValue && !_animationValue->isConst()) {
660 UpdateCallback* uc = new UpdateCallback(_condition, _animationValue);
661 transform->setUpdateCallback(uc);
663 transform->setAxis(_axis);
664 transform->setValue(_initialValue);
665 parent.addChild(transform);
670 ////////////////////////////////////////////////////////////////////////
671 // Implementation of rotate/spin animation
672 ////////////////////////////////////////////////////////////////////////
674 class SGRotAnimTransform : public SGRotateTransform
677 SGRotAnimTransform();
678 SGRotAnimTransform(const SGRotAnimTransform&,
679 const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY);
680 META_Node(simgear, SGRotAnimTransform);
681 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
682 osg::NodeVisitor* nv) const;
683 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
684 osg::NodeVisitor* nv) const;
685 SGSharedPtr<SGCondition const> _condition;
686 SGSharedPtr<SGExpressiond const> _animationValue;
687 // used when condition is false
688 mutable double _lastAngle;
691 SGRotAnimTransform::SGRotAnimTransform()
696 SGRotAnimTransform::SGRotAnimTransform(const SGRotAnimTransform& rhs,
697 const osg::CopyOp& copyop)
698 : SGRotateTransform(rhs, copyop), _condition(rhs._condition),
699 _animationValue(rhs._animationValue), _lastAngle(rhs._lastAngle)
703 bool SGRotAnimTransform::computeLocalToWorldMatrix(osg::Matrix& matrix,
704 osg::NodeVisitor* nv) const
707 if (!_condition || _condition->test()) {
708 angle = _animationValue->getValue();
713 double angleRad = SGMiscd::deg2rad(angle);
714 if (_referenceFrame == RELATIVE_RF) {
717 set_rotation(tmp, angleRad, getCenter(), getAxis());
721 SGRotateTransform::set_rotation(tmp, angleRad, getCenter(), getAxis());
727 bool SGRotAnimTransform::computeWorldToLocalMatrix(osg::Matrix& matrix,
728 osg::NodeVisitor* nv) const
731 if (!_condition || _condition->test()) {
732 angle = _animationValue->getValue();
737 double angleRad = SGMiscd::deg2rad(angle);
738 if (_referenceFrame == RELATIVE_RF) {
741 set_rotation(tmp, -angleRad, getCenter(), getAxis());
742 matrix.postMult(tmp);
745 set_rotation(tmp, -angleRad, getCenter(), getAxis());
752 class SpinAnimCallback : public osg::NodeCallback {
754 SpinAnimCallback(SGCondition const* condition,
755 SGExpressiond const* animationValue,
756 double initialValue = 0.0) :
757 _condition(condition),
758 _animationValue(animationValue),
759 _initialValue(initialValue)
761 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv);
763 SGSharedPtr<SGCondition const> _condition;
764 SGSharedPtr<SGExpressiond const> _animationValue;
765 double _initialValue;
767 // This cull callback can run in different threads if there is
768 // more than one camera. It is probably safe to overwrite the
769 // reference values in multiple threads, but we'll provide a
770 // threadsafe way to manage those values just to be safe.
771 struct ReferenceValues : public osg::Referenced
773 ReferenceValues(double t, double rot, double vel)
774 : _time(t), _rotation(rot), _rotVelocity(vel)
781 OpenThreads::AtomicPtr _referenceValues;
784 void SpinAnimCallback::operator()(osg::Node* node, osg::NodeVisitor* nv)
787 SGRotateTransform* transform = static_cast<SGRotateTransform*>(node);
788 EffectCullVisitor* cv = dynamic_cast<EffectCullVisitor*>(nv);
791 if (!_condition || _condition->test()) {
792 double t = nv->getFrameStamp()->getReferenceTime();
793 double rps = _animationValue->getValue() / 60.0;
794 ref_ptr<ReferenceValues>
795 refval(static_cast<ReferenceValues*>(_referenceValues.get()));
796 if (!refval || refval->_rotVelocity != rps) {
797 ref_ptr<ReferenceValues> newref;
798 if (!refval.valid()) {
800 newref = new ReferenceValues(t, 0.0, rps);
802 double newRot = refval->_rotation + (t - refval->_time) * refval->_rotVelocity;
803 newref = new ReferenceValues(t, newRot, rps);
805 // increment reference pointer, because it will be stored
806 // naked in _referenceValues.
808 if (_referenceValues.assign(newref, refval)) {
809 if (refval.valid()) {
810 DeletionManager::instance()->addStaleObject(refval.get());
814 // Another thread installed new values before us
817 // Whatever happened, we can use the reference values just
821 double rotation = refval->_rotation + (t - refval->_time) * rps;
823 double rot = modf(rotation, &intPart);
824 double angle = rot * 2.0 * osg::PI;
825 const SGVec3d& sgcenter = transform->getCenter();
826 const SGVec3d& sgaxis = transform->getAxis();
827 Matrixd mat = Matrixd::translate(-sgcenter[0], -sgcenter[1], -sgcenter[2])
828 * Matrixd::rotate(angle, sgaxis[0], sgaxis[1], sgaxis[2])
829 * Matrixd::translate(sgcenter[0], sgcenter[1], sgcenter[2])
830 * *cv->getModelViewMatrix();
831 ref_ptr<RefMatrix> refmat = new RefMatrix(mat);
832 cv->pushModelViewMatrix(refmat.get(), transform->getReferenceFrame());
833 traverse(transform, nv);
834 cv->popModelViewMatrix();
836 traverse(transform, nv);
840 // factored out to share with SGKnobAnimation
841 void readRotationCenterAndAxis(const SGPropertyNode* configNode, SGVec3d& center, SGVec3d& axis)
843 center = SGVec3d::zeros();
844 if (configNode->hasValue("axis/x1-m")) {
846 v1[0] = configNode->getDoubleValue("axis/x1-m", 0);
847 v1[1] = configNode->getDoubleValue("axis/y1-m", 0);
848 v1[2] = configNode->getDoubleValue("axis/z1-m", 0);
849 v2[0] = configNode->getDoubleValue("axis/x2-m", 0);
850 v2[1] = configNode->getDoubleValue("axis/y2-m", 0);
851 v2[2] = configNode->getDoubleValue("axis/z2-m", 0);
852 center = 0.5*(v1+v2);
855 axis[0] = configNode->getDoubleValue("axis/x", 0);
856 axis[1] = configNode->getDoubleValue("axis/y", 0);
857 axis[2] = configNode->getDoubleValue("axis/z", 0);
859 if (8*SGLimitsd::min() < norm(axis))
860 axis = normalize(axis);
862 center[0] = configNode->getDoubleValue("center/x-m", center[0]);
863 center[1] = configNode->getDoubleValue("center/y-m", center[1]);
864 center[2] = configNode->getDoubleValue("center/z-m", center[2]);
867 SGVec3d readTranslateAxis(const SGPropertyNode* configNode)
871 if (configNode->hasValue("axis/x1-m")) {
873 v1[0] = configNode->getDoubleValue("axis/x1-m", 0);
874 v1[1] = configNode->getDoubleValue("axis/y1-m", 0);
875 v1[2] = configNode->getDoubleValue("axis/z1-m", 0);
876 v2[0] = configNode->getDoubleValue("axis/x2-m", 0);
877 v2[1] = configNode->getDoubleValue("axis/y2-m", 0);
878 v2[2] = configNode->getDoubleValue("axis/z2-m", 0);
881 axis[0] = configNode->getDoubleValue("axis/x", 0);
882 axis[1] = configNode->getDoubleValue("axis/y", 0);
883 axis[2] = configNode->getDoubleValue("axis/z", 0);
885 if (8*SGLimitsd::min() < norm(axis))
886 axis = normalize(axis);
891 SGRotateAnimation::SGRotateAnimation(const SGPropertyNode* configNode,
892 SGPropertyNode* modelRoot) :
893 SGAnimation(configNode, modelRoot)
895 std::string type = configNode->getStringValue("type", "");
896 _isSpin = (type == "spin");
898 _condition = getCondition();
899 SGSharedPtr<SGExpressiond> value;
900 value = read_value(configNode, modelRoot, "-deg",
901 -SGLimitsd::max(), SGLimitsd::max());
902 _animationValue = value->simplify();
904 _initialValue = _animationValue->getValue();
908 readRotationCenterAndAxis(configNode, _center, _axis);
912 SGRotateAnimation::createAnimationGroup(osg::Group& parent)
915 SGRotateTransform* transform = new SGRotateTransform;
916 transform->setName("spin rotate animation");
917 SpinAnimCallback* cc;
918 cc = new SpinAnimCallback(_condition, _animationValue, _initialValue);
919 transform->setCullCallback(cc);
920 transform->setCenter(_center);
921 transform->setAxis(_axis);
922 transform->setAngleDeg(_initialValue);
923 parent.addChild(transform);
926 SGRotAnimTransform* transform = new SGRotAnimTransform;
927 transform->setName("rotate animation");
928 transform->_condition = _condition;
929 transform->_animationValue = _animationValue;
930 transform->_lastAngle = _initialValue;
931 transform->setCenter(_center);
932 transform->setAxis(_axis);
933 parent.addChild(transform);
939 ////////////////////////////////////////////////////////////////////////
940 // Implementation of scale animation
941 ////////////////////////////////////////////////////////////////////////
943 class SGScaleAnimation::UpdateCallback : public osg::NodeCallback {
945 UpdateCallback(const SGCondition* condition,
946 SGSharedPtr<const SGExpressiond> animationValue[3]) :
947 _condition(condition)
949 _animationValue[0] = animationValue[0];
950 _animationValue[1] = animationValue[1];
951 _animationValue[2] = animationValue[2];
952 setName("SGScaleAnimation::UpdateCallback");
954 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
956 if (!_condition || _condition->test()) {
957 SGScaleTransform* transform;
958 transform = static_cast<SGScaleTransform*>(node);
959 SGVec3d scale(_animationValue[0]->getValue(),
960 _animationValue[1]->getValue(),
961 _animationValue[2]->getValue());
962 transform->setScaleFactor(scale);
967 SGSharedPtr<SGCondition const> _condition;
968 SGSharedPtr<SGExpressiond const> _animationValue[3];
971 SGScaleAnimation::SGScaleAnimation(const SGPropertyNode* configNode,
972 SGPropertyNode* modelRoot) :
973 SGAnimation(configNode, modelRoot)
975 _condition = getCondition();
977 // default offset/factor for all directions
978 double offset = configNode->getDoubleValue("offset", 0);
979 double factor = configNode->getDoubleValue("factor", 1);
981 SGSharedPtr<SGExpressiond> inPropExpr;
983 std::string inputPropertyName;
984 inputPropertyName = configNode->getStringValue("property", "");
985 if (inputPropertyName.empty()) {
986 inPropExpr = new SGConstExpression<double>(0);
988 SGPropertyNode* inputProperty;
989 inputProperty = modelRoot->getNode(inputPropertyName, true);
990 inPropExpr = new SGPropertyExpression<double>(inputProperty);
993 SGInterpTable* interpTable = read_interpolation_table(configNode);
995 SGSharedPtr<SGExpressiond> value;
996 value = new SGInterpTableExpression<double>(inPropExpr, interpTable);
997 _animationValue[0] = value->simplify();
998 _animationValue[1] = value->simplify();
999 _animationValue[2] = value->simplify();
1000 } else if (configNode->getBoolValue("use-personality", false)) {
1001 SGSharedPtr<SGExpressiond> value;
1002 value = new SGPersonalityScaleOffsetExpression(inPropExpr, configNode,
1003 "x-factor", "x-offset",
1005 double minClip = configNode->getDoubleValue("x-min", 0);
1006 double maxClip = configNode->getDoubleValue("x-max", SGLimitsd::max());
1007 value = new SGClipExpression<double>(value, minClip, maxClip);
1008 _animationValue[0] = value->simplify();
1010 value = new SGPersonalityScaleOffsetExpression(inPropExpr, configNode,
1011 "y-factor", "y-offset",
1013 minClip = configNode->getDoubleValue("y-min", 0);
1014 maxClip = configNode->getDoubleValue("y-max", SGLimitsd::max());
1015 value = new SGClipExpression<double>(value, minClip, maxClip);
1016 _animationValue[1] = value->simplify();
1018 value = new SGPersonalityScaleOffsetExpression(inPropExpr, configNode,
1019 "z-factor", "z-offset",
1021 minClip = configNode->getDoubleValue("z-min", 0);
1022 maxClip = configNode->getDoubleValue("z-max", SGLimitsd::max());
1023 value = new SGClipExpression<double>(value, minClip, maxClip);
1024 _animationValue[2] = value->simplify();
1026 SGSharedPtr<SGExpressiond> value;
1027 value = read_factor_offset(configNode, inPropExpr, "x-factor", "x-offset");
1028 double minClip = configNode->getDoubleValue("x-min", 0);
1029 double maxClip = configNode->getDoubleValue("x-max", SGLimitsd::max());
1030 value = new SGClipExpression<double>(value, minClip, maxClip);
1031 _animationValue[0] = value->simplify();
1033 value = read_factor_offset(configNode, inPropExpr, "y-factor", "y-offset");
1034 minClip = configNode->getDoubleValue("y-min", 0);
1035 maxClip = configNode->getDoubleValue("y-max", SGLimitsd::max());
1036 value = new SGClipExpression<double>(value, minClip, maxClip);
1037 _animationValue[1] = value->simplify();
1039 value = read_factor_offset(configNode, inPropExpr, "z-factor", "z-offset");
1040 minClip = configNode->getDoubleValue("z-min", 0);
1041 maxClip = configNode->getDoubleValue("z-max", SGLimitsd::max());
1042 value = new SGClipExpression<double>(value, minClip, maxClip);
1043 _animationValue[2] = value->simplify();
1045 _initialValue[0] = configNode->getDoubleValue("x-starting-scale", 1);
1046 _initialValue[0] *= configNode->getDoubleValue("x-factor", factor);
1047 _initialValue[0] += configNode->getDoubleValue("x-offset", offset);
1048 _initialValue[1] = configNode->getDoubleValue("y-starting-scale", 1);
1049 _initialValue[1] *= configNode->getDoubleValue("y-factor", factor);
1050 _initialValue[1] += configNode->getDoubleValue("y-offset", offset);
1051 _initialValue[2] = configNode->getDoubleValue("z-starting-scale", 1);
1052 _initialValue[2] *= configNode->getDoubleValue("z-factor", factor);
1053 _initialValue[2] += configNode->getDoubleValue("z-offset", offset);
1054 _center[0] = configNode->getDoubleValue("center/x-m", 0);
1055 _center[1] = configNode->getDoubleValue("center/y-m", 0);
1056 _center[2] = configNode->getDoubleValue("center/z-m", 0);
1060 SGScaleAnimation::createAnimationGroup(osg::Group& parent)
1062 SGScaleTransform* transform = new SGScaleTransform;
1063 transform->setName("scale animation");
1064 transform->setCenter(_center);
1065 transform->setScaleFactor(_initialValue);
1066 UpdateCallback* uc = new UpdateCallback(_condition, _animationValue);
1067 transform->setUpdateCallback(uc);
1068 parent.addChild(transform);
1073 // Don't create a new state state everytime we need GL_NORMALIZE!
1077 Mutex normalizeMutex;
1079 osg::StateSet* getNormalizeStateSet()
1081 static osg::ref_ptr<osg::StateSet> normalizeStateSet;
1082 ScopedLock<Mutex> lock(normalizeMutex);
1083 if (!normalizeStateSet.valid()) {
1084 normalizeStateSet = new osg::StateSet;
1085 normalizeStateSet->setMode(GL_NORMALIZE, osg::StateAttribute::ON);
1086 normalizeStateSet->setDataVariance(osg::Object::STATIC);
1088 return normalizeStateSet.get();
1092 ////////////////////////////////////////////////////////////////////////
1093 // Implementation of dist scale animation
1094 ////////////////////////////////////////////////////////////////////////
1096 class SGDistScaleAnimation::Transform : public osg::Transform {
1098 Transform() : _min_v(0.0), _max_v(0.0), _factor(0.0), _offset(0.0) {}
1099 Transform(const Transform& rhs,
1100 const osg::CopyOp& copyOp = osg::CopyOp::SHALLOW_COPY)
1101 : osg::Transform(rhs, copyOp), _table(rhs._table), _center(rhs._center),
1102 _min_v(rhs._min_v), _max_v(rhs._max_v), _factor(rhs._factor),
1103 _offset(rhs._offset)
1106 META_Node(simgear, SGDistScaleAnimation::Transform);
1107 Transform(const SGPropertyNode* configNode)
1109 setName(configNode->getStringValue("name", "dist scale animation"));
1110 setReferenceFrame(RELATIVE_RF);
1111 setStateSet(getNormalizeStateSet());
1112 _factor = configNode->getFloatValue("factor", 1);
1113 _offset = configNode->getFloatValue("offset", 0);
1114 _min_v = configNode->getFloatValue("min", SGLimitsf::epsilon());
1115 _max_v = configNode->getFloatValue("max", SGLimitsf::max());
1116 _table = read_interpolation_table(configNode);
1117 _center[0] = configNode->getFloatValue("center/x-m", 0);
1118 _center[1] = configNode->getFloatValue("center/y-m", 0);
1119 _center[2] = configNode->getFloatValue("center/z-m", 0);
1121 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
1122 osg::NodeVisitor* nv) const
1124 osg::Matrix transform;
1125 double scale_factor = computeScaleFactor(nv);
1126 transform(0,0) = scale_factor;
1127 transform(1,1) = scale_factor;
1128 transform(2,2) = scale_factor;
1129 transform(3,0) = _center[0]*(1 - scale_factor);
1130 transform(3,1) = _center[1]*(1 - scale_factor);
1131 transform(3,2) = _center[2]*(1 - scale_factor);
1132 matrix.preMult(transform);
1136 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
1137 osg::NodeVisitor* nv) const
1139 double scale_factor = computeScaleFactor(nv);
1140 if (fabs(scale_factor) <= SGLimits<double>::min())
1142 osg::Matrix transform;
1143 double rScaleFactor = 1/scale_factor;
1144 transform(0,0) = rScaleFactor;
1145 transform(1,1) = rScaleFactor;
1146 transform(2,2) = rScaleFactor;
1147 transform(3,0) = _center[0]*(1 - rScaleFactor);
1148 transform(3,1) = _center[1]*(1 - rScaleFactor);
1149 transform(3,2) = _center[2]*(1 - rScaleFactor);
1150 matrix.postMult(transform);
1154 static bool writeLocalData(const osg::Object& obj, osgDB::Output& fw)
1156 const Transform& trans = static_cast<const Transform&>(obj);
1157 fw.indent() << "center " << trans._center << "\n";
1158 fw.indent() << "min_v " << trans._min_v << "\n";
1159 fw.indent() << "max_v " << trans._max_v << "\n";
1160 fw.indent() << "factor " << trans._factor << "\n";
1161 fw.indent() << "offset " << trans._offset << "\n";
1165 double computeScaleFactor(osg::NodeVisitor* nv) const
1170 double scale_factor = (toOsg(_center) - nv->getEyePoint()).length();
1172 scale_factor = _factor * scale_factor + _offset;
1174 scale_factor = _table->interpolate( scale_factor );
1176 if (scale_factor < _min_v)
1177 scale_factor = _min_v;
1178 if (scale_factor > _max_v)
1179 scale_factor = _max_v;
1181 return scale_factor;
1184 SGSharedPtr<SGInterpTable> _table;
1193 SGDistScaleAnimation::SGDistScaleAnimation(const SGPropertyNode* configNode,
1194 SGPropertyNode* modelRoot) :
1195 SGAnimation(configNode, modelRoot)
1200 SGDistScaleAnimation::createAnimationGroup(osg::Group& parent)
1202 Transform* transform = new Transform(getConfig());
1203 parent.addChild(transform);
1209 osgDB::RegisterDotOsgWrapperProxy distScaleAnimationTransformProxy
1211 new SGDistScaleAnimation::Transform,
1212 "SGDistScaleAnimation::Transform",
1213 "Object Node Transform SGDistScaleAnimation::Transform Group",
1215 &SGDistScaleAnimation::Transform::writeLocalData
1219 ////////////////////////////////////////////////////////////////////////
1220 // Implementation of flash animation
1221 ////////////////////////////////////////////////////////////////////////
1223 class SGFlashAnimation::Transform : public osg::Transform {
1225 Transform() : _power(0.0), _factor(0.0), _offset(0.0), _min_v(0.0),
1226 _max_v(0.0), _two_sides(false)
1229 Transform(const Transform& rhs,
1230 const osg::CopyOp& copyOp = osg::CopyOp::SHALLOW_COPY)
1231 : osg::Transform(rhs, copyOp), _center(rhs._center), _axis(rhs._axis),
1232 _power(rhs._power), _factor(rhs._factor), _offset(rhs._offset),
1233 _min_v(rhs._min_v), _max_v(rhs._max_v), _two_sides(rhs._two_sides)
1236 META_Node(simgear, SGFlashAnimation::Transform);
1238 Transform(const SGPropertyNode* configNode)
1240 setReferenceFrame(RELATIVE_RF);
1241 setName(configNode->getStringValue("name", "flash animation"));
1242 setStateSet(getNormalizeStateSet());
1244 _axis[0] = configNode->getFloatValue("axis/x", 0);
1245 _axis[1] = configNode->getFloatValue("axis/y", 0);
1246 _axis[2] = configNode->getFloatValue("axis/z", 1);
1249 _center[0] = configNode->getFloatValue("center/x-m", 0);
1250 _center[1] = configNode->getFloatValue("center/y-m", 0);
1251 _center[2] = configNode->getFloatValue("center/z-m", 0);
1253 _offset = configNode->getFloatValue("offset", 0);
1254 _factor = configNode->getFloatValue("factor", 1);
1255 _power = configNode->getFloatValue("power", 1);
1256 _two_sides = configNode->getBoolValue("two-sides", false);
1258 _min_v = configNode->getFloatValue("min", SGLimitsf::epsilon());
1259 _max_v = configNode->getFloatValue("max", 1);
1261 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
1262 osg::NodeVisitor* nv) const
1264 osg::Matrix transform;
1265 double scale_factor = computeScaleFactor(nv);
1266 transform(0,0) = scale_factor;
1267 transform(1,1) = scale_factor;
1268 transform(2,2) = scale_factor;
1269 transform(3,0) = _center[0]*(1 - scale_factor);
1270 transform(3,1) = _center[1]*(1 - scale_factor);
1271 transform(3,2) = _center[2]*(1 - scale_factor);
1272 matrix.preMult(transform);
1276 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
1277 osg::NodeVisitor* nv) const
1279 double scale_factor = computeScaleFactor(nv);
1280 if (fabs(scale_factor) <= SGLimits<double>::min())
1282 osg::Matrix transform;
1283 double rScaleFactor = 1/scale_factor;
1284 transform(0,0) = rScaleFactor;
1285 transform(1,1) = rScaleFactor;
1286 transform(2,2) = rScaleFactor;
1287 transform(3,0) = _center[0]*(1 - rScaleFactor);
1288 transform(3,1) = _center[1]*(1 - rScaleFactor);
1289 transform(3,2) = _center[2]*(1 - rScaleFactor);
1290 matrix.postMult(transform);
1294 static bool writeLocalData(const osg::Object& obj, osgDB::Output& fw)
1296 const Transform& trans = static_cast<const Transform&>(obj);
1297 fw.indent() << "center " << trans._center[0] << " "
1298 << trans._center[1] << " " << trans._center[2] << " " << "\n";
1299 fw.indent() << "axis " << trans._axis[0] << " "
1300 << trans._axis[1] << " " << trans._axis[2] << " " << "\n";
1301 fw.indent() << "power " << trans._power << " \n";
1302 fw.indent() << "min_v " << trans._min_v << "\n";
1303 fw.indent() << "max_v " << trans._max_v << "\n";
1304 fw.indent() << "factor " << trans._factor << "\n";
1305 fw.indent() << "offset " << trans._offset << "\n";
1306 fw.indent() << "twosides " << (trans._two_sides ? "true" : "false") << "\n";
1310 double computeScaleFactor(osg::NodeVisitor* nv) const
1315 osg::Vec3 localEyeToCenter = nv->getEyePoint() - _center;
1316 localEyeToCenter.normalize();
1318 double cos_angle = localEyeToCenter*_axis;
1319 double scale_factor = 0;
1320 if ( _two_sides && cos_angle < 0 )
1321 scale_factor = _factor * pow( -cos_angle, _power ) + _offset;
1322 else if ( cos_angle > 0 )
1323 scale_factor = _factor * pow( cos_angle, _power ) + _offset;
1325 if ( scale_factor < _min_v )
1326 scale_factor = _min_v;
1327 if ( scale_factor > _max_v )
1328 scale_factor = _max_v;
1330 return scale_factor;
1333 virtual osg::BoundingSphere computeBound() const
1335 // avoid being culled away by small feature culling
1336 osg::BoundingSphere bs = osg::Group::computeBound();
1337 bs.radius() *= _max_v;
1344 double _power, _factor, _offset, _min_v, _max_v;
1349 SGFlashAnimation::SGFlashAnimation(const SGPropertyNode* configNode,
1350 SGPropertyNode* modelRoot) :
1351 SGAnimation(configNode, modelRoot)
1356 SGFlashAnimation::createAnimationGroup(osg::Group& parent)
1358 Transform* transform = new Transform(getConfig());
1359 parent.addChild(transform);
1365 osgDB::RegisterDotOsgWrapperProxy flashAnimationTransformProxy
1367 new SGFlashAnimation::Transform,
1368 "SGFlashAnimation::Transform",
1369 "Object Node Transform SGFlashAnimation::Transform Group",
1371 &SGFlashAnimation::Transform::writeLocalData
1375 ////////////////////////////////////////////////////////////////////////
1376 // Implementation of billboard animation
1377 ////////////////////////////////////////////////////////////////////////
1379 class SGBillboardAnimation::Transform : public osg::Transform {
1381 Transform() : _spherical(true) {}
1382 Transform(const Transform& rhs,
1383 const osg::CopyOp& copyOp = osg::CopyOp::SHALLOW_COPY)
1384 : osg::Transform(rhs, copyOp), _spherical(rhs._spherical) {}
1385 META_Node(simgear, SGBillboardAnimation::Transform);
1386 Transform(const SGPropertyNode* configNode) :
1387 _spherical(configNode->getBoolValue("spherical", true))
1389 setReferenceFrame(RELATIVE_RF);
1390 setName(configNode->getStringValue("name", "billboard animation"));
1392 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
1393 osg::NodeVisitor* nv) const
1395 // More or less taken from plibs ssgCutout
1397 matrix(0,0) = 1; matrix(0,1) = 0; matrix(0,2) = 0;
1398 matrix(1,0) = 0; matrix(1,1) = 0; matrix(1,2) = -1;
1399 matrix(2,0) = 0; matrix(2,1) = 1; matrix(2,2) = 0;
1401 osg::Vec3 zAxis(matrix(2, 0), matrix(2, 1), matrix(2, 2));
1402 osg::Vec3 xAxis = osg::Vec3(0, 0, -1)^zAxis;
1403 osg::Vec3 yAxis = zAxis^xAxis;
1409 matrix(0,0) = xAxis[0]; matrix(0,1) = xAxis[1]; matrix(0,2) = xAxis[2];
1410 matrix(1,0) = yAxis[0]; matrix(1,1) = yAxis[1]; matrix(1,2) = yAxis[2];
1411 matrix(2,0) = zAxis[0]; matrix(2,1) = zAxis[1]; matrix(2,2) = zAxis[2];
1416 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
1417 osg::NodeVisitor* nv) const
1419 // Hmm, don't yet know how to get that back ...
1422 static bool writeLocalData(const osg::Object& obj, osgDB::Output& fw)
1424 const Transform& trans = static_cast<const Transform&>(obj);
1426 fw.indent() << (trans._spherical ? "true" : "false") << "\n";
1434 SGBillboardAnimation::SGBillboardAnimation(const SGPropertyNode* configNode,
1435 SGPropertyNode* modelRoot) :
1436 SGAnimation(configNode, modelRoot)
1441 SGBillboardAnimation::createAnimationGroup(osg::Group& parent)
1443 Transform* transform = new Transform(getConfig());
1444 parent.addChild(transform);
1450 osgDB::RegisterDotOsgWrapperProxy billboardAnimationTransformProxy
1452 new SGBillboardAnimation::Transform,
1453 "SGBillboardAnimation::Transform",
1454 "Object Node Transform SGBillboardAnimation::Transform Group",
1456 &SGBillboardAnimation::Transform::writeLocalData
1460 ////////////////////////////////////////////////////////////////////////
1461 // Implementation of a range animation
1462 ////////////////////////////////////////////////////////////////////////
1464 class SGRangeAnimation::UpdateCallback : public osg::NodeCallback {
1466 UpdateCallback(const SGCondition* condition,
1467 const SGExpressiond* minAnimationValue,
1468 const SGExpressiond* maxAnimationValue,
1469 double minValue, double maxValue) :
1470 _condition(condition),
1471 _minAnimationValue(minAnimationValue),
1472 _maxAnimationValue(maxAnimationValue),
1473 _minStaticValue(minValue),
1474 _maxStaticValue(maxValue)
1476 setName("SGRangeAnimation::UpdateCallback");
1478 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1480 osg::LOD* lod = static_cast<osg::LOD*>(node);
1481 if (!_condition || _condition->test()) {
1483 if (_minAnimationValue)
1484 minRange = _minAnimationValue->getValue();
1486 minRange = _minStaticValue;
1488 if (_maxAnimationValue)
1489 maxRange = _maxAnimationValue->getValue();
1491 maxRange = _maxStaticValue;
1492 lod->setRange(0, minRange, maxRange);
1494 lod->setRange(0, 0, SGLimitsf::max());
1500 SGSharedPtr<const SGCondition> _condition;
1501 SGSharedPtr<const SGExpressiond> _minAnimationValue;
1502 SGSharedPtr<const SGExpressiond> _maxAnimationValue;
1503 double _minStaticValue;
1504 double _maxStaticValue;
1507 SGRangeAnimation::SGRangeAnimation(const SGPropertyNode* configNode,
1508 SGPropertyNode* modelRoot) :
1509 SGAnimation(configNode, modelRoot)
1511 _condition = getCondition();
1513 std::string inputPropertyName;
1514 inputPropertyName = configNode->getStringValue("min-property", "");
1515 if (!inputPropertyName.empty()) {
1516 SGPropertyNode* inputProperty;
1517 inputProperty = modelRoot->getNode(inputPropertyName, true);
1518 SGSharedPtr<SGExpressiond> value;
1519 value = new SGPropertyExpression<double>(inputProperty);
1521 value = read_factor_offset(configNode, value, "min-factor", "min-offset");
1522 _minAnimationValue = value->simplify();
1524 inputPropertyName = configNode->getStringValue("max-property", "");
1525 if (!inputPropertyName.empty()) {
1526 SGPropertyNode* inputProperty;
1527 inputProperty = modelRoot->getNode(inputPropertyName.c_str(), true);
1529 SGSharedPtr<SGExpressiond> value;
1530 value = new SGPropertyExpression<double>(inputProperty);
1532 value = read_factor_offset(configNode, value, "max-factor", "max-offset");
1533 _maxAnimationValue = value->simplify();
1536 _initialValue[0] = configNode->getDoubleValue("min-m", 0);
1537 _initialValue[0] *= configNode->getDoubleValue("min-factor", 1);
1538 _initialValue[1] = configNode->getDoubleValue("max-m", SGLimitsf::max());
1539 _initialValue[1] *= configNode->getDoubleValue("max-factor", 1);
1543 SGRangeAnimation::createAnimationGroup(osg::Group& parent)
1545 osg::Group* group = new osg::Group;
1546 group->setName("range animation group");
1548 osg::LOD* lod = new osg::LOD;
1549 lod->setName("range animation node");
1550 parent.addChild(lod);
1552 lod->addChild(group, _initialValue[0], _initialValue[1]);
1553 lod->setCenterMode(osg::LOD::USE_BOUNDING_SPHERE_CENTER);
1554 lod->setRangeMode(osg::LOD::DISTANCE_FROM_EYE_POINT);
1555 if (_minAnimationValue || _maxAnimationValue || _condition) {
1557 uc = new UpdateCallback(_condition, _minAnimationValue, _maxAnimationValue,
1558 _initialValue[0], _initialValue[1]);
1559 lod->setUpdateCallback(uc);
1565 ////////////////////////////////////////////////////////////////////////
1566 // Implementation of a select animation
1567 ////////////////////////////////////////////////////////////////////////
1569 SGSelectAnimation::SGSelectAnimation(const SGPropertyNode* configNode,
1570 SGPropertyNode* modelRoot) :
1571 SGAnimation(configNode, modelRoot)
1576 SGSelectAnimation::createAnimationGroup(osg::Group& parent)
1578 // if no condition given, this is a noop.
1579 SGSharedPtr<SGCondition const> condition = getCondition();
1580 // trick, gets deleted with all its 'animated' children
1581 // when the animation installer returns
1583 return new osg::Group;
1584 simgear::ConditionNode* cn = new simgear::ConditionNode;
1585 cn->setName("select animation node");
1586 cn->setCondition(condition.ptr());
1587 osg::Group* grp = new osg::Group;
1589 parent.addChild(cn);
1595 ////////////////////////////////////////////////////////////////////////
1596 // Implementation of alpha test animation
1597 ////////////////////////////////////////////////////////////////////////
1599 SGAlphaTestAnimation::SGAlphaTestAnimation(const SGPropertyNode* configNode,
1600 SGPropertyNode* modelRoot) :
1601 SGAnimation(configNode, modelRoot)
1607 // Keep one copy of the most common alpha test its state set.
1608 ReentrantMutex alphaTestMutex;
1609 osg::ref_ptr<osg::AlphaFunc> standardAlphaFunc;
1610 osg::ref_ptr<osg::StateSet> alphaFuncStateSet;
1612 osg::AlphaFunc* makeAlphaFunc(float clamp)
1614 ScopedLock<ReentrantMutex> lock(alphaTestMutex);
1615 if (osg::equivalent(clamp, 0.01f)) {
1616 if (standardAlphaFunc.valid())
1617 return standardAlphaFunc.get();
1620 osg::AlphaFunc* alphaFunc = new osg::AlphaFunc;
1621 alphaFunc->setFunction(osg::AlphaFunc::GREATER);
1622 alphaFunc->setReferenceValue(clamp);
1623 alphaFunc->setDataVariance(osg::Object::STATIC);
1624 if (osg::equivalent(clamp, 0.01f))
1625 standardAlphaFunc = alphaFunc;
1629 osg::StateSet* makeAlphaTestStateSet(float clamp)
1631 using namespace OpenThreads;
1632 ScopedLock<ReentrantMutex> lock(alphaTestMutex);
1633 if (osg::equivalent(clamp, 0.01f)) {
1634 if (alphaFuncStateSet.valid())
1635 return alphaFuncStateSet.get();
1637 osg::AlphaFunc* alphaFunc = makeAlphaFunc(clamp);
1638 osg::StateSet* stateSet = new osg::StateSet;
1639 stateSet->setAttributeAndModes(alphaFunc,
1640 (osg::StateAttribute::ON
1641 | osg::StateAttribute::OVERRIDE));
1642 stateSet->setDataVariance(osg::Object::STATIC);
1643 if (osg::equivalent(clamp, 0.01f))
1644 alphaFuncStateSet = stateSet;
1649 SGAlphaTestAnimation::install(osg::Node& node)
1651 SGAnimation::install(node);
1653 float alphaClamp = getConfig()->getFloatValue("alpha-factor", 0);
1654 osg::StateSet* stateSet = node.getStateSet();
1656 node.setStateSet(makeAlphaTestStateSet(alphaClamp));
1658 stateSet->setAttributeAndModes(makeAlphaFunc(alphaClamp),
1659 (osg::StateAttribute::ON
1660 | osg::StateAttribute::OVERRIDE));
1665 //////////////////////////////////////////////////////////////////////
1666 // Blend animation installer
1667 //////////////////////////////////////////////////////////////////////
1669 // XXX This needs to be replaced by something using TexEnvCombine to
1670 // change the blend factor. Changing the alpha values in the geometry
1672 class SGBlendAnimation::BlendVisitor : public osg::NodeVisitor {
1674 BlendVisitor(float blend) :
1675 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
1677 { setVisitorType(osg::NodeVisitor::NODE_VISITOR); }
1678 virtual void apply(osg::Node& node)
1680 updateStateSet(node.getStateSet());
1683 virtual void apply(osg::Geode& node)
1685 apply((osg::Node&)node);
1686 unsigned nDrawables = node.getNumDrawables();
1687 for (unsigned i = 0; i < nDrawables; ++i) {
1688 osg::Drawable* drawable = node.getDrawable(i);
1689 osg::Geometry* geometry = drawable->asGeometry();
1692 osg::Array* array = geometry->getColorArray();
1695 osg::Vec4Array* vec4Array = dynamic_cast<osg::Vec4Array*>(array);
1698 for (unsigned k = 0; k < vec4Array->size(); ++k) {
1699 (*vec4Array)[k][3] = _blend;
1702 updateStateSet(drawable->getStateSet());
1705 void updateStateSet(osg::StateSet* stateSet)
1709 osg::StateAttribute* stateAttribute;
1710 stateAttribute = stateSet->getAttribute(osg::StateAttribute::MATERIAL);
1711 if (!stateAttribute)
1713 osg::Material* material = dynamic_cast<osg::Material*>(stateAttribute);
1716 material->setAlpha(osg::Material::FRONT_AND_BACK, _blend);
1718 stateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
1719 stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
1721 stateSet->setRenderingHint(osg::StateSet::DEFAULT_BIN);
1728 class SGBlendAnimation::UpdateCallback : public osg::NodeCallback {
1730 UpdateCallback(const SGPropertyNode* configNode, const SGExpressiond* v) :
1734 setName("SGBlendAnimation::UpdateCallback");
1736 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1738 double blend = _animationValue->getValue();
1739 if (blend != _prev_value) {
1740 _prev_value = blend;
1741 BlendVisitor visitor(1-blend);
1742 node->accept(visitor);
1748 SGSharedPtr<SGExpressiond const> _animationValue;
1752 SGBlendAnimation::SGBlendAnimation(const SGPropertyNode* configNode,
1753 SGPropertyNode* modelRoot)
1754 : SGAnimation(configNode, modelRoot),
1755 _animationValue(read_value(configNode, modelRoot, "", 0, 1))
1760 SGBlendAnimation::createAnimationGroup(osg::Group& parent)
1762 if (!_animationValue)
1765 osg::Group* group = new osg::Switch;
1766 group->setName("blend animation node");
1767 group->setUpdateCallback(new UpdateCallback(getConfig(), _animationValue));
1768 parent.addChild(group);
1773 SGBlendAnimation::install(osg::Node& node)
1775 SGAnimation::install(node);
1776 // make sure we do not change common geometries,
1777 // that also creates new display lists for these subgeometries.
1778 cloneDrawables(node);
1779 DoDrawArraysVisitor visitor;
1780 node.accept(visitor);
1784 //////////////////////////////////////////////////////////////////////
1785 // Timed animation installer
1786 //////////////////////////////////////////////////////////////////////
1790 class SGTimedAnimation::UpdateCallback : public osg::NodeCallback {
1792 UpdateCallback(const SGPropertyNode* configNode) :
1795 _duration_sec(configNode->getDoubleValue("duration-sec", 1)),
1796 _last_time_sec(SGLimitsd::max()),
1797 _use_personality(configNode->getBoolValue("use-personality", false))
1799 std::vector<SGSharedPtr<SGPropertyNode> > nodes;
1800 nodes = configNode->getChildren("branch-duration-sec");
1801 for (size_t i = 0; i < nodes.size(); ++i) {
1802 unsigned ind = nodes[ i ]->getIndex();
1803 while ( ind >= _durations.size() ) {
1804 _durations.push_back(DurationSpec(_duration_sec));
1806 SGPropertyNode_ptr rNode = nodes[i]->getChild("random");
1808 _durations[ind] = DurationSpec(nodes[ i ]->getDoubleValue());
1810 _durations[ind] = DurationSpec(rNode->getDoubleValue( "min", 0),
1811 rNode->getDoubleValue( "max", 1));
1814 setName("SGTimedAnimation::UpdateCallback");
1816 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1818 assert(dynamic_cast<osg::Switch*>(node));
1819 osg::Switch* sw = static_cast<osg::Switch*>(node);
1821 unsigned nChildren = sw->getNumChildren();
1823 // blow up the durations vector to the required size
1824 while (_durations.size() < nChildren) {
1825 _durations.push_back(_duration_sec);
1827 // make sure the current index is an duration that really exists
1828 _current_index = _current_index % nChildren;
1830 // update the time and compute the current systems time value
1831 double t = nv->getFrameStamp()->getReferenceTime();
1832 if (_last_time_sec == SGLimitsd::max()) {
1835 double dt = t - _last_time_sec;
1836 if (_use_personality)
1837 dt *= 1 + 0.2*(0.5 - sg_random());
1842 double currentDuration = _durations[_current_index].get();
1843 while (currentDuration < _reminder) {
1844 _reminder -= currentDuration;
1845 _current_index = (_current_index + 1) % nChildren;
1846 currentDuration = _durations[_current_index].get();
1849 sw->setSingleChildOn(_current_index);
1855 struct DurationSpec {
1856 DurationSpec(double t) :
1857 minTime(SGMiscd::max(0.01, t)),
1858 maxTime(SGMiscd::max(0.01, t))
1860 DurationSpec(double t0, double t1) :
1861 minTime(SGMiscd::max(0.01, t0)),
1862 maxTime(SGMiscd::max(0.01, t1))
1865 { return minTime + sg_random()*(maxTime - minTime); }
1869 std::vector<DurationSpec> _durations;
1870 unsigned _current_index;
1872 double _duration_sec;
1873 double _last_time_sec;
1874 bool _use_personality;
1878 SGTimedAnimation::SGTimedAnimation(const SGPropertyNode* configNode,
1879 SGPropertyNode* modelRoot)
1880 : SGAnimation(configNode, modelRoot)
1885 SGTimedAnimation::createAnimationGroup(osg::Group& parent)
1887 osg::Switch* sw = new osg::Switch;
1888 sw->setName("timed animation node");
1889 sw->setUpdateCallback(new UpdateCallback(getConfig()));
1890 parent.addChild(sw);
1895 ////////////////////////////////////////////////////////////////////////
1896 // dynamically switch on/off shadows
1897 ////////////////////////////////////////////////////////////////////////
1899 class SGShadowAnimation::UpdateCallback : public osg::NodeCallback {
1901 UpdateCallback(const SGCondition* condition) :
1902 _condition(condition)
1904 setName("SGShadowAnimation::UpdateCallback");
1906 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1908 if (_condition->test())
1909 node->setNodeMask( SG_NODEMASK_CASTSHADOW_BIT | node->getNodeMask());
1911 node->setNodeMask(~SG_NODEMASK_CASTSHADOW_BIT & node->getNodeMask());
1916 SGSharedPtr<const SGCondition> _condition;
1919 SGShadowAnimation::SGShadowAnimation(const SGPropertyNode* configNode,
1920 SGPropertyNode* modelRoot) :
1921 SGAnimation(configNode, modelRoot)
1926 SGShadowAnimation::createAnimationGroup(osg::Group& parent)
1928 SGSharedPtr<SGCondition const> condition = getCondition();
1930 osg::Group* group = new osg::Group;
1931 group->setName("shadow animation");
1933 group->setUpdateCallback(new UpdateCallback(condition));
1935 group->setNodeMask(~SG_NODEMASK_CASTSHADOW_BIT & group->getNodeMask());
1936 parent.addChild(group);
1941 ////////////////////////////////////////////////////////////////////////
1942 // Implementation of SGTexTransformAnimation
1943 ////////////////////////////////////////////////////////////////////////
1945 class SGTexTransformAnimation::Transform : public SGReferenced {
1950 virtual ~Transform()
1952 void setValue(double value)
1954 virtual void transform(osg::Matrix&) = 0;
1959 class SGTexTransformAnimation::Translation :
1960 public SGTexTransformAnimation::Transform {
1962 Translation(const SGVec3d& axis) :
1965 virtual void transform(osg::Matrix& matrix)
1968 set_translation(tmp, _value, _axis);
1969 matrix.preMult(tmp);
1975 class SGTexTransformAnimation::Rotation :
1976 public SGTexTransformAnimation::Transform {
1978 Rotation(const SGVec3d& axis, const SGVec3d& center) :
1982 virtual void transform(osg::Matrix& matrix)
1985 SGRotateTransform::set_rotation(tmp, SGMiscd::deg2rad(_value), _center,
1987 matrix.preMult(tmp);
1994 class SGTexTransformAnimation::UpdateCallback :
1995 public osg::StateAttribute::Callback {
1997 UpdateCallback(const SGCondition* condition) :
1998 _condition(condition)
2000 setName("SGTexTransformAnimation::UpdateCallback");
2002 virtual void operator () (osg::StateAttribute* sa, osg::NodeVisitor*)
2004 if (!_condition || _condition->test()) {
2005 TransformList::const_iterator i;
2006 for (i = _transforms.begin(); i != _transforms.end(); ++i)
2007 i->transform->setValue(i->value->getValue());
2009 assert(dynamic_cast<osg::TexMat*>(sa));
2010 osg::TexMat* texMat = static_cast<osg::TexMat*>(sa);
2011 texMat->getMatrix().makeIdentity();
2012 TransformList::const_iterator i;
2013 for (i = _transforms.begin(); i != _transforms.end(); ++i)
2014 i->transform->transform(texMat->getMatrix());
2016 void appendTransform(Transform* transform, SGExpressiond* value)
2018 Entry entry = { transform, value };
2019 transform->transform(_matrix);
2020 _transforms.push_back(entry);
2025 SGSharedPtr<Transform> transform;
2026 SGSharedPtr<const SGExpressiond> value;
2028 typedef std::vector<Entry> TransformList;
2029 TransformList _transforms;
2030 SGSharedPtr<const SGCondition> _condition;
2031 osg::Matrix _matrix;
2034 SGTexTransformAnimation::SGTexTransformAnimation(const SGPropertyNode* configNode,
2035 SGPropertyNode* modelRoot) :
2036 SGAnimation(configNode, modelRoot)
2041 SGTexTransformAnimation::createAnimationGroup(osg::Group& parent)
2043 osg::Group* group = new osg::Group;
2044 group->setName("texture transform group");
2045 osg::StateSet* stateSet = group->getOrCreateStateSet();
2046 stateSet->setDataVariance(osg::Object::DYNAMIC);
2047 osg::TexMat* texMat = new osg::TexMat;
2048 UpdateCallback* updateCallback = new UpdateCallback(getCondition());
2049 // interpret the configs ...
2050 std::string type = getType();
2052 if (type == "textranslate") {
2053 appendTexTranslate(getConfig(), updateCallback);
2054 } else if (type == "texrotate") {
2055 appendTexRotate(getConfig(), updateCallback);
2056 } else if (type == "texmultiple") {
2057 std::vector<SGSharedPtr<SGPropertyNode> > transformConfigs;
2058 transformConfigs = getConfig()->getChildren("transform");
2059 for (unsigned i = 0; i < transformConfigs.size(); ++i) {
2060 std::string subtype = transformConfigs[i]->getStringValue("subtype", "");
2061 if (subtype == "textranslate")
2062 appendTexTranslate(transformConfigs[i], updateCallback);
2063 else if (subtype == "texrotate")
2064 appendTexRotate(transformConfigs[i], updateCallback);
2066 SG_LOG(SG_INPUT, SG_ALERT,
2067 "Ignoring unknown texture transform subtype");
2070 SG_LOG(SG_INPUT, SG_ALERT, "Ignoring unknown texture transform type");
2073 texMat->setUpdateCallback(updateCallback);
2074 stateSet->setTextureAttribute(0, texMat);
2075 parent.addChild(group);
2080 SGTexTransformAnimation::appendTexTranslate(const SGPropertyNode* config,
2081 UpdateCallback* updateCallback)
2083 std::string propertyName = config->getStringValue("property", "");
2084 SGSharedPtr<SGExpressiond> value;
2085 if (propertyName.empty())
2086 value = new SGConstExpression<double>(0);
2088 SGPropertyNode* inputProperty = getModelRoot()->getNode(propertyName, true);
2089 value = new SGPropertyExpression<double>(inputProperty);
2092 SGInterpTable* table = read_interpolation_table(config);
2094 value = new SGInterpTableExpression<double>(value, table);
2095 double biasValue = config->getDoubleValue("bias", 0);
2097 value = new SGBiasExpression<double>(value, biasValue);
2098 value = new SGStepExpression<double>(value,
2099 config->getDoubleValue("step", 0),
2100 config->getDoubleValue("scroll", 0));
2101 value = value->simplify();
2103 double biasValue = config->getDoubleValue("bias", 0);
2105 value = new SGBiasExpression<double>(value, biasValue);
2106 value = new SGStepExpression<double>(value,
2107 config->getDoubleValue("step", 0),
2108 config->getDoubleValue("scroll", 0));
2109 value = read_offset_factor(config, value, "factor", "offset");
2111 if (config->hasChild("min") || config->hasChild("max")) {
2112 double minClip = config->getDoubleValue("min", -SGLimitsd::max());
2113 double maxClip = config->getDoubleValue("max", SGLimitsd::max());
2114 value = new SGClipExpression<double>(value, minClip, maxClip);
2116 value = value->simplify();
2118 SGVec3d axis(config->getDoubleValue("axis/x", 0),
2119 config->getDoubleValue("axis/y", 0),
2120 config->getDoubleValue("axis/z", 0));
2121 Translation* translation;
2122 translation = new Translation(normalize(axis));
2123 translation->setValue(config->getDoubleValue("starting-position", 0));
2124 updateCallback->appendTransform(translation, value);
2128 SGTexTransformAnimation::appendTexRotate(const SGPropertyNode* config,
2129 UpdateCallback* updateCallback)
2131 std::string propertyName = config->getStringValue("property", "");
2132 SGSharedPtr<SGExpressiond> value;
2133 if (propertyName.empty())
2134 value = new SGConstExpression<double>(0);
2136 SGPropertyNode* inputProperty = getModelRoot()->getNode(propertyName, true);
2137 value = new SGPropertyExpression<double>(inputProperty);
2140 SGInterpTable* table = read_interpolation_table(config);
2142 value = new SGInterpTableExpression<double>(value, table);
2143 double biasValue = config->getDoubleValue("bias", 0);
2145 value = new SGBiasExpression<double>(value, biasValue);
2146 value = new SGStepExpression<double>(value,
2147 config->getDoubleValue("step", 0),
2148 config->getDoubleValue("scroll", 0));
2149 value = value->simplify();
2151 double biasValue = config->getDoubleValue("bias", 0);
2153 value = new SGBiasExpression<double>(value, biasValue);
2154 value = new SGStepExpression<double>(value,
2155 config->getDoubleValue("step", 0),
2156 config->getDoubleValue("scroll", 0));
2157 value = read_offset_factor(config, value, "factor", "offset-deg");
2159 if (config->hasChild("min-deg") || config->hasChild("max-deg")) {
2160 double minClip = config->getDoubleValue("min-deg", -SGLimitsd::max());
2161 double maxClip = config->getDoubleValue("max-deg", SGLimitsd::max());
2162 value = new SGClipExpression<double>(value, minClip, maxClip);
2164 value = value->simplify();
2166 SGVec3d axis(config->getDoubleValue("axis/x", 0),
2167 config->getDoubleValue("axis/y", 0),
2168 config->getDoubleValue("axis/z", 0));
2169 SGVec3d center(config->getDoubleValue("center/x", 0),
2170 config->getDoubleValue("center/y", 0),
2171 config->getDoubleValue("center/z", 0));
2173 rotation = new Rotation(normalize(axis), center);
2174 rotation->setValue(config->getDoubleValue("starting-position-deg", 0));
2175 updateCallback->appendTransform(rotation, value);