1 // animation.cxx - classes to manage model animation.
2 // Written by David Megginson, started 2002.
4 // This file is in the Public Domain, and comes with no warranty.
7 # include <simgear_config.h>
10 #include <string.h> // for strcmp()
15 #include <OpenThreads/Mutex>
16 #include <OpenThreads/ReentrantMutex>
17 #include <OpenThreads/ScopedLock>
19 #include <osg/AlphaFunc>
20 #include <osg/Drawable>
22 #include <osg/Geometry>
25 #include <osg/PolygonMode>
26 #include <osg/PolygonOffset>
27 #include <osg/StateSet>
30 #include <osg/Texture2D>
31 #include <osg/Transform>
32 #include <osgDB/ReadFile>
34 #include <simgear/math/interpolater.hxx>
35 #include <simgear/props/condition.hxx>
36 #include <simgear/props/props.hxx>
37 #include <simgear/structure/SGBinding.hxx>
38 #include <simgear/scene/util/SGNodeMasks.hxx>
39 #include <simgear/scene/util/SGSceneUserData.hxx>
40 #include <simgear/scene/util/SGStateAttributeVisitor.hxx>
42 #include "animation.hxx"
45 #include "SGTranslateTransform.hxx"
46 #include "SGMaterialAnimation.hxx"
47 #include "SGRotateTransform.hxx"
48 #include "SGScaleTransform.hxx"
51 ////////////////////////////////////////////////////////////////////////
52 // Static utility functions.
53 ////////////////////////////////////////////////////////////////////////
56 * Set up the transform matrix for a spin or rotation.
59 set_rotation (osg::Matrix &matrix, double position_deg,
60 const SGVec3d ¢er, const SGVec3d &axis)
62 double temp_angle = -SGMiscd::deg2rad(position_deg);
64 double s = sin(temp_angle);
65 double c = cos(temp_angle);
68 // axis was normalized at load time
69 // hint to the compiler to put these into FP registers
74 matrix(0, 0) = t * x * x + c ;
75 matrix(0, 1) = t * y * x - s * z ;
76 matrix(0, 2) = t * z * x + s * y ;
79 matrix(1, 0) = t * x * y + s * z ;
80 matrix(1, 1) = t * y * y + c ;
81 matrix(1, 2) = t * z * y - s * x ;
84 matrix(2, 0) = t * x * z - s * y ;
85 matrix(2, 1) = t * y * z + s * x ;
86 matrix(2, 2) = t * z * z + c ;
89 // hint to the compiler to put these into FP registers
94 matrix(3, 0) = x - x*matrix(0, 0) - y*matrix(1, 0) - z*matrix(2, 0);
95 matrix(3, 1) = y - x*matrix(0, 1) - y*matrix(1, 1) - z*matrix(2, 1);
96 matrix(3, 2) = z - x*matrix(0, 2) - y*matrix(1, 2) - z*matrix(2, 2);
101 * Set up the transform matrix for a translation.
104 set_translation (osg::Matrix &matrix, double position_m, const SGVec3d &axis)
106 SGVec3d xyz = axis * position_m;
107 matrix.makeIdentity();
108 matrix(3, 0) = xyz[0];
109 matrix(3, 1) = xyz[1];
110 matrix(3, 2) = xyz[2];
114 * Read an interpolation table from properties.
116 static SGInterpTable *
117 read_interpolation_table(const SGPropertyNode* props)
119 const SGPropertyNode* table_node = props->getNode("interpolation");
122 return new SGInterpTable(table_node);
126 unit_string(const char* value, const char* unit)
128 return std::string(value) + unit;
131 class SGPersonalityScaleOffsetExpression : public SGUnaryExpression<double> {
133 SGPersonalityScaleOffsetExpression(SGExpression<double>* expr,
134 SGPropertyNode const* config,
135 const std::string& scalename,
136 const std::string& offsetname,
138 double defOffset = 0) :
139 SGUnaryExpression<double>(expr),
140 _scale(config, scalename.c_str(), defScale),
141 _offset(config, offsetname.c_str(), defOffset)
143 void setScale(double scale)
145 void setOffset(double offset)
146 { _offset = offset; }
148 virtual void eval(double& value) const
152 value = _offset + _scale*getOperand()->getValue();
155 virtual bool isConst() const { return false; }
158 mutable SGPersonalityParameter<double> _scale;
159 mutable SGPersonalityParameter<double> _offset;
163 static SGExpressiond*
164 read_factor_offset(const SGPropertyNode* configNode, SGExpressiond* expr,
165 const std::string& factor, const std::string& offset)
167 double factorValue = configNode->getDoubleValue(factor, 1);
168 if (factorValue != 1)
169 expr = new SGScaleExpression<double>(expr, factorValue);
170 double offsetValue = configNode->getDoubleValue(offset, 0);
171 if (offsetValue != 0)
172 expr = new SGBiasExpression<double>(expr, offsetValue);
176 static SGExpressiond*
177 read_offset_factor(const SGPropertyNode* configNode, SGExpressiond* expr,
178 const std::string& factor, const std::string& offset)
180 double offsetValue = configNode->getDoubleValue(offset, 0);
181 if (offsetValue != 0)
182 expr = new SGBiasExpression<double>(expr, offsetValue);
183 double factorValue = configNode->getDoubleValue(factor, 1);
184 if (factorValue != 1)
185 expr = new SGScaleExpression<double>(expr, factorValue);
189 static SGExpressiond*
190 read_value(const SGPropertyNode* configNode, SGPropertyNode* modelRoot,
191 const char* unit, double defMin, double defMax)
193 SGExpression<double>* value = 0;
195 std::string inputPropertyName = configNode->getStringValue("property", "");
196 if (inputPropertyName.empty()) {
197 std::string spos = unit_string("starting-position", unit);
198 double initPos = configNode->getDoubleValue(spos, 0);
199 value = new SGConstExpression<double>(initPos);
201 SGPropertyNode* inputProperty;
202 inputProperty = modelRoot->getNode(inputPropertyName, true);
203 value = new SGPropertyExpression<double>(inputProperty);
206 SGInterpTable* interpTable = read_interpolation_table(configNode);
208 return new SGInterpTableExpression<double>(value, interpTable);
210 std::string offset = unit_string("offset", unit);
211 std::string min = unit_string("min", unit);
212 std::string max = unit_string("max", unit);
214 if (configNode->getBoolValue("use-personality", false)) {
215 value = new SGPersonalityScaleOffsetExpression(value, configNode,
218 value = read_factor_offset(configNode, value, "factor", offset);
221 double minClip = configNode->getDoubleValue(min, defMin);
222 double maxClip = configNode->getDoubleValue(max, defMax);
223 if (minClip > SGMiscd::min(SGLimitsd::min(), -SGLimitsd::max()) ||
224 maxClip < SGLimitsd::max())
225 value = new SGClipExpression<double>(value, minClip, maxClip);
233 ////////////////////////////////////////////////////////////////////////
234 // Animation installer
235 ////////////////////////////////////////////////////////////////////////
237 class SGAnimation::RemoveModeVisitor : public SGStateAttributeVisitor {
239 RemoveModeVisitor(osg::StateAttribute::GLMode mode) :
242 virtual void apply(osg::StateSet* stateSet)
246 stateSet->removeMode(_mode);
249 osg::StateAttribute::GLMode _mode;
252 class SGAnimation::RemoveAttributeVisitor : public SGStateAttributeVisitor {
254 RemoveAttributeVisitor(osg::StateAttribute::Type type) :
257 virtual void apply(osg::StateSet* stateSet)
261 while (stateSet->getAttribute(_type)) {
262 stateSet->removeAttribute(_type);
266 osg::StateAttribute::Type _type;
269 class SGAnimation::RemoveTextureModeVisitor : public SGStateAttributeVisitor {
271 RemoveTextureModeVisitor(unsigned unit, osg::StateAttribute::GLMode mode) :
275 virtual void apply(osg::StateSet* stateSet)
279 stateSet->removeTextureMode(_unit, _mode);
283 osg::StateAttribute::GLMode _mode;
286 class SGAnimation::RemoveTextureAttributeVisitor :
287 public SGStateAttributeVisitor {
289 RemoveTextureAttributeVisitor(unsigned unit,
290 osg::StateAttribute::Type type) :
294 virtual void apply(osg::StateSet* stateSet)
298 while (stateSet->getTextureAttribute(_unit, _type)) {
299 stateSet->removeTextureAttribute(_unit, _type);
304 osg::StateAttribute::Type _type;
307 class SGAnimation::BinToInheritVisitor : public SGStateAttributeVisitor {
309 virtual void apply(osg::StateSet* stateSet)
313 stateSet->setRenderBinToInherit();
317 class SGAnimation::DrawableCloneVisitor : public osg::NodeVisitor {
319 DrawableCloneVisitor() :
320 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
322 void apply(osg::Geode& geode)
324 for (unsigned i = 0 ; i < geode.getNumDrawables(); ++i) {
325 osg::CopyOp copyOp(osg::CopyOp::DEEP_COPY_ALL &
326 ~osg::CopyOp::DEEP_COPY_TEXTURES);
327 geode.setDrawable(i, copyOp(geode.getDrawable(i)));
334 // Set all drawables to not use display lists. OSG will use
335 // glDrawArrays instead.
336 struct DoDrawArraysVisitor : public osg::NodeVisitor {
337 DoDrawArraysVisitor() :
338 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
340 void apply(osg::Geode& geode)
345 for (int i = 0; i < geode.getNumDrawables(); ++i)
346 geode.getDrawable(i)->setUseDisplayList(false);
351 SGAnimation::SGAnimation(const SGPropertyNode* configNode,
352 SGPropertyNode* modelRoot) :
353 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
355 _configNode(configNode),
356 _modelRoot(modelRoot)
358 _name = configNode->getStringValue("name", "");
359 _enableHOT = configNode->getBoolValue("enable-hot", true);
360 _disableShadow = configNode->getBoolValue("disable-shadow", false);
361 std::vector<SGPropertyNode_ptr> objectNames =
362 configNode->getChildren("object-name");
363 for (unsigned i = 0; i < objectNames.size(); ++i)
364 _objectNames.push_back(objectNames[i]->getStringValue());
367 SGAnimation::~SGAnimation()
372 SG_LOG(SG_IO, SG_ALERT, "Could not find at least one of the following"
373 " objects for animation:\n");
374 std::list<std::string>::const_iterator i;
375 for (i = _objectNames.begin(); i != _objectNames.end(); ++i)
376 SG_LOG(SG_IO, SG_ALERT, *i << "\n");
380 SGAnimation::animate(osg::Node* node, const SGPropertyNode* configNode,
381 SGPropertyNode* modelRoot,
382 const osgDB::ReaderWriter::Options* options)
384 std::string type = configNode->getStringValue("type", "none");
385 if (type == "alpha-test") {
386 SGAlphaTestAnimation animInst(configNode, modelRoot);
387 animInst.apply(node);
388 } else if (type == "billboard") {
389 SGBillboardAnimation animInst(configNode, modelRoot);
390 animInst.apply(node);
391 } else if (type == "blend") {
392 SGBlendAnimation animInst(configNode, modelRoot);
393 animInst.apply(node);
394 } else if (type == "dist-scale") {
395 SGDistScaleAnimation animInst(configNode, modelRoot);
396 animInst.apply(node);
397 } else if (type == "flash") {
398 SGFlashAnimation animInst(configNode, modelRoot);
399 animInst.apply(node);
400 } else if (type == "material") {
401 SGMaterialAnimation animInst(configNode, modelRoot, options);
402 animInst.apply(node);
403 } else if (type == "noshadow") {
404 SGShadowAnimation animInst(configNode, modelRoot);
405 animInst.apply(node);
406 } else if (type == "pick") {
407 SGPickAnimation animInst(configNode, modelRoot);
408 animInst.apply(node);
409 } else if (type == "range") {
410 SGRangeAnimation animInst(configNode, modelRoot);
411 animInst.apply(node);
412 } else if (type == "rotate" || type == "spin") {
413 SGRotateAnimation animInst(configNode, modelRoot);
414 animInst.apply(node);
415 } else if (type == "scale") {
416 SGScaleAnimation animInst(configNode, modelRoot);
417 animInst.apply(node);
418 } else if (type == "select") {
419 SGSelectAnimation animInst(configNode, modelRoot);
420 animInst.apply(node);
421 } else if (type == "shader") {
422 SGShaderAnimation animInst(configNode, modelRoot, options);
423 animInst.apply(node);
424 } else if (type == "textranslate" || type == "texrotate" ||
425 type == "texmultiple") {
426 SGTexTransformAnimation animInst(configNode, modelRoot);
427 animInst.apply(node);
428 } else if (type == "timed") {
429 SGTimedAnimation animInst(configNode, modelRoot);
430 animInst.apply(node);
431 } else if (type == "translate") {
432 SGTranslateAnimation animInst(configNode, modelRoot);
433 animInst.apply(node);
434 } else if (type == "null" || type == "none" || type.empty()) {
435 SGGroupAnimation animInst(configNode, modelRoot);
436 animInst.apply(node);
445 SGAnimation::apply(osg::Node* node)
447 // duh what a special case ...
448 if (_objectNames.empty()) {
449 osg::Group* group = node->asGroup();
451 osg::ref_ptr<osg::Group> animationGroup;
452 installInGroup(std::string(), *group, animationGroup);
459 SGAnimation::install(osg::Node& node)
463 node.setNodeMask( SG_NODEMASK_TERRAIN_BIT | node.getNodeMask());
465 node.setNodeMask(~SG_NODEMASK_TERRAIN_BIT & node.getNodeMask());
467 node.setNodeMask( SG_NODEMASK_CASTSHADOW_BIT | node.getNodeMask());
469 node.setNodeMask(~SG_NODEMASK_CASTSHADOW_BIT & node.getNodeMask());
473 SGAnimation::createAnimationGroup(osg::Group& parent)
475 // default implementation, we do not need a new group
476 // for every animation type. Usually animations that just change
477 // the StateSet of some parts of the model
482 SGAnimation::apply(osg::Group& group)
484 // the trick is to first traverse the children and then
485 // possibly splice in a new group node if required.
486 // Else we end up in a recursive loop where we infinitly insert new
490 // Note that this algorithm preserves the order of the child objects
491 // like they appear in the object-name tags.
492 // The timed animations require this
493 osg::ref_ptr<osg::Group> animationGroup;
494 std::list<std::string>::const_iterator nameIt;
495 for (nameIt = _objectNames.begin(); nameIt != _objectNames.end(); ++nameIt)
496 installInGroup(*nameIt, group, animationGroup);
500 SGAnimation::installInGroup(const std::string& name, osg::Group& group,
501 osg::ref_ptr<osg::Group>& animationGroup)
503 int i = group.getNumChildren() - 1;
504 for (; 0 <= i; --i) {
505 osg::Node* child = group.getChild(i);
507 // Check if this one is already processed
508 if (std::find(_installedAnimations.begin(),
509 _installedAnimations.end(), child)
510 != _installedAnimations.end())
513 if (name.empty() || child->getName() == name) {
514 // fire the installation of the animation
517 // create a group node on demand
518 if (!animationGroup.valid()) {
519 animationGroup = createAnimationGroup(group);
520 // Animation type that does not require a new group,
521 // in this case we can stop and look for the next object
522 if (animationGroup.valid() && !_name.empty())
523 animationGroup->setName(_name);
525 if (animationGroup.valid()) {
526 animationGroup->addChild(child);
527 group.removeChild(i);
530 // store that we already have processed this child node
531 // We can hit this one twice if an animation references some
532 // part of a subtree twice
533 _installedAnimations.push_back(child);
539 SGAnimation::removeMode(osg::Node& node, osg::StateAttribute::GLMode mode)
541 RemoveModeVisitor visitor(mode);
542 node.accept(visitor);
546 SGAnimation::removeAttribute(osg::Node& node, osg::StateAttribute::Type type)
548 RemoveAttributeVisitor visitor(type);
549 node.accept(visitor);
553 SGAnimation::removeTextureMode(osg::Node& node, unsigned unit,
554 osg::StateAttribute::GLMode mode)
556 RemoveTextureModeVisitor visitor(unit, mode);
557 node.accept(visitor);
561 SGAnimation::removeTextureAttribute(osg::Node& node, unsigned unit,
562 osg::StateAttribute::Type type)
564 RemoveTextureAttributeVisitor visitor(unit, type);
565 node.accept(visitor);
569 SGAnimation::setRenderBinToInherit(osg::Node& node)
571 BinToInheritVisitor visitor;
572 node.accept(visitor);
576 SGAnimation::cloneDrawables(osg::Node& node)
578 DrawableCloneVisitor visitor;
579 node.accept(visitor);
583 SGAnimation::getCondition() const
585 const SGPropertyNode* conditionNode = _configNode->getChild("condition");
588 return sgReadCondition(_modelRoot, conditionNode);
593 ////////////////////////////////////////////////////////////////////////
594 // Implementation of null animation
595 ////////////////////////////////////////////////////////////////////////
597 // Ok, that is to build a subgraph from different other
598 // graph nodes. I guess that this stems from the time where modellers
599 // could not build hierarchical trees ...
600 SGGroupAnimation::SGGroupAnimation(const SGPropertyNode* configNode,
601 SGPropertyNode* modelRoot):
602 SGAnimation(configNode, modelRoot)
607 SGGroupAnimation::createAnimationGroup(osg::Group& parent)
609 osg::Group* group = new osg::Group;
610 parent.addChild(group);
615 ////////////////////////////////////////////////////////////////////////
616 // Implementation of translate animation
617 ////////////////////////////////////////////////////////////////////////
619 class SGTranslateAnimation::UpdateCallback : public osg::NodeCallback {
621 UpdateCallback(SGCondition const* condition,
622 SGExpressiond const* animationValue) :
623 _condition(condition),
624 _animationValue(animationValue)
626 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
628 if (!_condition || _condition->test()) {
629 SGTranslateTransform* transform;
630 transform = static_cast<SGTranslateTransform*>(node);
631 transform->setValue(_animationValue->getValue());
636 SGSharedPtr<SGCondition const> _condition;
637 SGSharedPtr<SGExpressiond const> _animationValue;
640 SGTranslateAnimation::SGTranslateAnimation(const SGPropertyNode* configNode,
641 SGPropertyNode* modelRoot) :
642 SGAnimation(configNode, modelRoot)
644 _condition = getCondition();
645 SGSharedPtr<SGExpressiond> value;
646 value = read_value(configNode, modelRoot, "-m",
647 -SGLimitsd::max(), SGLimitsd::max());
648 _animationValue = value->simplify();
650 _initialValue = _animationValue->getValue();
654 _axis[0] = configNode->getDoubleValue("axis/x", 0);
655 _axis[1] = configNode->getDoubleValue("axis/y", 0);
656 _axis[2] = configNode->getDoubleValue("axis/z", 0);
657 if (8*SGLimitsd::min() < norm(_axis))
658 _axis = normalize(_axis);
662 SGTranslateAnimation::createAnimationGroup(osg::Group& parent)
664 SGTranslateTransform* transform = new SGTranslateTransform;
665 transform->setName("translate animation");
666 if (_animationValue && !_animationValue->isConst()) {
667 UpdateCallback* uc = new UpdateCallback(_condition, _animationValue);
668 transform->setUpdateCallback(uc);
670 transform->setAxis(_axis);
671 transform->setValue(_initialValue);
672 parent.addChild(transform);
677 ////////////////////////////////////////////////////////////////////////
678 // Implementation of rotate/spin animation
679 ////////////////////////////////////////////////////////////////////////
681 class SGRotateAnimation::UpdateCallback : public osg::NodeCallback {
683 UpdateCallback(SGCondition const* condition,
684 SGExpressiond const* animationValue) :
685 _condition(condition),
686 _animationValue(animationValue)
688 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
690 if (!_condition || _condition->test()) {
691 SGRotateTransform* transform;
692 transform = static_cast<SGRotateTransform*>(node);
693 transform->setAngleDeg(_animationValue->getValue());
698 SGSharedPtr<SGCondition const> _condition;
699 SGSharedPtr<SGExpressiond const> _animationValue;
702 class SGRotateAnimation::SpinUpdateCallback : public osg::NodeCallback {
704 SpinUpdateCallback(SGCondition const* condition,
705 SGExpressiond const* animationValue) :
706 _condition(condition),
707 _animationValue(animationValue),
710 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
712 if (!_condition || _condition->test()) {
713 SGRotateTransform* transform;
714 transform = static_cast<SGRotateTransform*>(node);
716 double t = nv->getFrameStamp()->getReferenceTime();
721 double velocity_rpms = _animationValue->getValue()/60;
722 double angle = transform->getAngleDeg();
723 angle += dt*velocity_rpms*360;
724 angle -= 360*floor(angle/360);
725 transform->setAngleDeg(angle);
730 SGSharedPtr<SGCondition const> _condition;
731 SGSharedPtr<SGExpressiond const> _animationValue;
735 SGRotateAnimation::SGRotateAnimation(const SGPropertyNode* configNode,
736 SGPropertyNode* modelRoot) :
737 SGAnimation(configNode, modelRoot)
739 std::string type = configNode->getStringValue("type", "");
740 _isSpin = (type == "spin");
742 _condition = getCondition();
743 SGSharedPtr<SGExpressiond> value;
744 value = read_value(configNode, modelRoot, "-deg",
745 -SGLimitsd::max(), SGLimitsd::max());
746 _animationValue = value->simplify();
748 _initialValue = _animationValue->getValue();
752 _center = SGVec3d::zeros();
753 if (configNode->hasValue("axis/x1-m")) {
755 v1[0] = configNode->getDoubleValue("axis/x1-m", 0);
756 v1[1] = configNode->getDoubleValue("axis/y1-m", 0);
757 v1[2] = configNode->getDoubleValue("axis/z1-m", 0);
758 v2[0] = configNode->getDoubleValue("axis/x2-m", 0);
759 v2[1] = configNode->getDoubleValue("axis/y2-m", 0);
760 v2[2] = configNode->getDoubleValue("axis/z2-m", 0);
761 _center = 0.5*(v1+v2);
764 _axis[0] = configNode->getDoubleValue("axis/x", 0);
765 _axis[1] = configNode->getDoubleValue("axis/y", 0);
766 _axis[2] = configNode->getDoubleValue("axis/z", 0);
768 if (8*SGLimitsd::min() < norm(_axis))
769 _axis = normalize(_axis);
771 _center[0] = configNode->getDoubleValue("center/x-m", _center[0]);
772 _center[1] = configNode->getDoubleValue("center/y-m", _center[1]);
773 _center[2] = configNode->getDoubleValue("center/z-m", _center[2]);
777 SGRotateAnimation::createAnimationGroup(osg::Group& parent)
779 SGRotateTransform* transform = new SGRotateTransform;
780 transform->setName("rotate animation");
782 SpinUpdateCallback* uc;
783 uc = new SpinUpdateCallback(_condition, _animationValue);
784 transform->setUpdateCallback(uc);
785 } else if (_animationValue || !_animationValue->isConst()) {
786 UpdateCallback* uc = new UpdateCallback(_condition, _animationValue);
787 transform->setUpdateCallback(uc);
789 transform->setCenter(_center);
790 transform->setAxis(_axis);
791 transform->setAngleDeg(_initialValue);
792 parent.addChild(transform);
797 ////////////////////////////////////////////////////////////////////////
798 // Implementation of scale animation
799 ////////////////////////////////////////////////////////////////////////
801 class SGScaleAnimation::UpdateCallback : public osg::NodeCallback {
803 UpdateCallback(const SGCondition* condition,
804 SGSharedPtr<const SGExpressiond> animationValue[3]) :
805 _condition(condition)
807 _animationValue[0] = animationValue[0];
808 _animationValue[1] = animationValue[1];
809 _animationValue[2] = animationValue[2];
811 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
813 if (!_condition || _condition->test()) {
814 SGScaleTransform* transform;
815 transform = static_cast<SGScaleTransform*>(node);
816 SGVec3d scale(_animationValue[0]->getValue(),
817 _animationValue[1]->getValue(),
818 _animationValue[2]->getValue());
819 transform->setScaleFactor(scale);
824 SGSharedPtr<SGCondition const> _condition;
825 SGSharedPtr<SGExpressiond const> _animationValue[3];
828 SGScaleAnimation::SGScaleAnimation(const SGPropertyNode* configNode,
829 SGPropertyNode* modelRoot) :
830 SGAnimation(configNode, modelRoot)
832 _condition = getCondition();
834 // default offset/factor for all directions
835 double offset = configNode->getDoubleValue("offset", 0);
836 double factor = configNode->getDoubleValue("factor", 1);
838 SGSharedPtr<SGExpressiond> inPropExpr;
840 std::string inputPropertyName;
841 inputPropertyName = configNode->getStringValue("property", "");
842 if (inputPropertyName.empty()) {
843 inPropExpr = new SGConstExpression<double>(0);
845 SGPropertyNode* inputProperty;
846 inputProperty = modelRoot->getNode(inputPropertyName, true);
847 inPropExpr = new SGPropertyExpression<double>(inputProperty);
850 SGInterpTable* interpTable = read_interpolation_table(configNode);
852 SGSharedPtr<SGExpressiond> value;
853 value = new SGInterpTableExpression<double>(inPropExpr, interpTable);
854 _animationValue[0] = value->simplify();
855 _animationValue[1] = value->simplify();
856 _animationValue[2] = value->simplify();
857 } else if (configNode->getBoolValue("use-personality", false)) {
858 SGSharedPtr<SGExpressiond> value;
859 value = new SGPersonalityScaleOffsetExpression(inPropExpr, configNode,
860 "x-factor", "x-offset",
862 double minClip = configNode->getDoubleValue("x-min", 0);
863 double maxClip = configNode->getDoubleValue("x-max", SGLimitsd::max());
864 value = new SGClipExpression<double>(value, minClip, maxClip);
865 _animationValue[0] = value->simplify();
867 value = new SGPersonalityScaleOffsetExpression(inPropExpr, configNode,
868 "y-factor", "y-offset",
870 minClip = configNode->getDoubleValue("y-min", 0);
871 maxClip = configNode->getDoubleValue("y-max", SGLimitsd::max());
872 value = new SGClipExpression<double>(value, minClip, maxClip);
873 _animationValue[1] = value->simplify();
875 value = new SGPersonalityScaleOffsetExpression(inPropExpr, configNode,
876 "z-factor", "z-offset",
878 minClip = configNode->getDoubleValue("z-min", 0);
879 maxClip = configNode->getDoubleValue("z-max", SGLimitsd::max());
880 value = new SGClipExpression<double>(value, minClip, maxClip);
881 _animationValue[2] = value->simplify();
883 SGSharedPtr<SGExpressiond> value;
884 value = read_factor_offset(configNode, inPropExpr, "x-factor", "x-offset");
885 double minClip = configNode->getDoubleValue("x-min", 0);
886 double maxClip = configNode->getDoubleValue("x-max", SGLimitsd::max());
887 value = new SGClipExpression<double>(value, minClip, maxClip);
888 _animationValue[0] = value->simplify();
890 value = read_factor_offset(configNode, inPropExpr, "y-factor", "y-offset");
891 minClip = configNode->getDoubleValue("y-min", 0);
892 maxClip = configNode->getDoubleValue("y-max", SGLimitsd::max());
893 value = new SGClipExpression<double>(value, minClip, maxClip);
894 _animationValue[1] = value->simplify();
896 value = read_factor_offset(configNode, inPropExpr, "z-factor", "z-offset");
897 minClip = configNode->getDoubleValue("z-min", 0);
898 maxClip = configNode->getDoubleValue("z-max", SGLimitsd::max());
899 value = new SGClipExpression<double>(value, minClip, maxClip);
900 _animationValue[2] = value->simplify();
902 _initialValue[0] = configNode->getDoubleValue("x-starting-scale", 1);
903 _initialValue[0] *= configNode->getDoubleValue("x-factor", factor);
904 _initialValue[0] += configNode->getDoubleValue("x-offset", offset);
905 _initialValue[1] = configNode->getDoubleValue("y-starting-scale", 1);
906 _initialValue[1] *= configNode->getDoubleValue("y-factor", factor);
907 _initialValue[1] += configNode->getDoubleValue("y-offset", offset);
908 _initialValue[2] = configNode->getDoubleValue("z-starting-scale", 1);
909 _initialValue[2] *= configNode->getDoubleValue("z-factor", factor);
910 _initialValue[2] += configNode->getDoubleValue("z-offset", offset);
911 _center[0] = configNode->getDoubleValue("center/x-m", 0);
912 _center[1] = configNode->getDoubleValue("center/y-m", 0);
913 _center[2] = configNode->getDoubleValue("center/z-m", 0);
917 SGScaleAnimation::createAnimationGroup(osg::Group& parent)
919 SGScaleTransform* transform = new SGScaleTransform;
920 transform->setName("scale animation");
921 transform->setCenter(_center);
922 transform->setScaleFactor(_initialValue);
923 UpdateCallback* uc = new UpdateCallback(_condition, _animationValue);
924 transform->setUpdateCallback(uc);
925 parent.addChild(transform);
930 // Don't create a new state state everytime we need GL_NORMALIZE!
934 OpenThreads::Mutex normalizeMutex;
936 osg::StateSet* getNormalizeStateSet()
938 static osg::ref_ptr<osg::StateSet> normalizeStateSet;
939 OpenThreads::ScopedLock<OpenThreads::Mutex> lock(normalizeMutex);
940 if (!normalizeStateSet.valid()) {
941 normalizeStateSet = new osg::StateSet;
942 normalizeStateSet->setMode(GL_NORMALIZE, osg::StateAttribute::ON);
943 normalizeStateSet->setDataVariance(osg::Object::STATIC);
945 return normalizeStateSet.get();
949 ////////////////////////////////////////////////////////////////////////
950 // Implementation of dist scale animation
951 ////////////////////////////////////////////////////////////////////////
953 class SGDistScaleAnimation::Transform : public osg::Transform {
955 Transform(const SGPropertyNode* configNode)
957 setName(configNode->getStringValue("name", "dist scale animation"));
958 setReferenceFrame(RELATIVE_RF);
959 setStateSet(getNormalizeStateSet());
960 _factor = configNode->getFloatValue("factor", 1);
961 _offset = configNode->getFloatValue("offset", 0);
962 _min_v = configNode->getFloatValue("min", SGLimitsf::epsilon());
963 _max_v = configNode->getFloatValue("max", SGLimitsf::max());
964 _table = read_interpolation_table(configNode);
965 _center[0] = configNode->getFloatValue("center/x-m", 0);
966 _center[1] = configNode->getFloatValue("center/y-m", 0);
967 _center[2] = configNode->getFloatValue("center/z-m", 0);
969 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
970 osg::NodeVisitor* nv) const
972 osg::Matrix transform;
973 double scale_factor = computeScaleFactor(nv);
974 transform(0,0) = scale_factor;
975 transform(1,1) = scale_factor;
976 transform(2,2) = scale_factor;
977 transform(3,0) = _center[0]*(1 - scale_factor);
978 transform(3,1) = _center[1]*(1 - scale_factor);
979 transform(3,2) = _center[2]*(1 - scale_factor);
980 matrix.preMult(transform);
984 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
985 osg::NodeVisitor* nv) const
987 double scale_factor = computeScaleFactor(nv);
988 if (fabs(scale_factor) <= SGLimits<double>::min())
990 osg::Matrix transform;
991 double rScaleFactor = 1/scale_factor;
992 transform(0,0) = rScaleFactor;
993 transform(1,1) = rScaleFactor;
994 transform(2,2) = rScaleFactor;
995 transform(3,0) = _center[0]*(1 - rScaleFactor);
996 transform(3,1) = _center[1]*(1 - rScaleFactor);
997 transform(3,2) = _center[2]*(1 - rScaleFactor);
998 matrix.postMult(transform);
1003 double computeScaleFactor(osg::NodeVisitor* nv) const
1008 double scale_factor = (_center.osg() - nv->getEyePoint()).length();
1010 scale_factor = _factor * scale_factor + _offset;
1012 scale_factor = _table->interpolate( scale_factor );
1014 if (scale_factor < _min_v)
1015 scale_factor = _min_v;
1016 if (scale_factor > _max_v)
1017 scale_factor = _max_v;
1019 return scale_factor;
1022 SGSharedPtr<SGInterpTable> _table;
1031 SGDistScaleAnimation::SGDistScaleAnimation(const SGPropertyNode* configNode,
1032 SGPropertyNode* modelRoot) :
1033 SGAnimation(configNode, modelRoot)
1038 SGDistScaleAnimation::createAnimationGroup(osg::Group& parent)
1040 Transform* transform = new Transform(getConfig());
1041 parent.addChild(transform);
1046 ////////////////////////////////////////////////////////////////////////
1047 // Implementation of flash animation
1048 ////////////////////////////////////////////////////////////////////////
1050 class SGFlashAnimation::Transform : public osg::Transform {
1052 Transform(const SGPropertyNode* configNode)
1054 setReferenceFrame(RELATIVE_RF);
1055 setName(configNode->getStringValue("name", "flash animation"));
1056 setStateSet(getNormalizeStateSet());
1058 _axis[0] = configNode->getFloatValue("axis/x", 0);
1059 _axis[1] = configNode->getFloatValue("axis/y", 0);
1060 _axis[2] = configNode->getFloatValue("axis/z", 1);
1063 _center[0] = configNode->getFloatValue("center/x-m", 0);
1064 _center[1] = configNode->getFloatValue("center/y-m", 0);
1065 _center[2] = configNode->getFloatValue("center/z-m", 0);
1067 _offset = configNode->getFloatValue("offset", 0);
1068 _factor = configNode->getFloatValue("factor", 1);
1069 _power = configNode->getFloatValue("power", 1);
1070 _two_sides = configNode->getBoolValue("two-sides", false);
1072 _min_v = configNode->getFloatValue("min", SGLimitsf::epsilon());
1073 _max_v = configNode->getFloatValue("max", 1);
1075 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
1076 osg::NodeVisitor* nv) const
1078 osg::Matrix transform;
1079 double scale_factor = computeScaleFactor(nv);
1080 transform(0,0) = scale_factor;
1081 transform(1,1) = scale_factor;
1082 transform(2,2) = scale_factor;
1083 transform(3,0) = _center[0]*(1 - scale_factor);
1084 transform(3,1) = _center[1]*(1 - scale_factor);
1085 transform(3,2) = _center[2]*(1 - scale_factor);
1086 matrix.preMult(transform);
1090 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
1091 osg::NodeVisitor* nv) const
1093 double scale_factor = computeScaleFactor(nv);
1094 if (fabs(scale_factor) <= SGLimits<double>::min())
1096 osg::Matrix transform;
1097 double rScaleFactor = 1/scale_factor;
1098 transform(0,0) = rScaleFactor;
1099 transform(1,1) = rScaleFactor;
1100 transform(2,2) = rScaleFactor;
1101 transform(3,0) = _center[0]*(1 - rScaleFactor);
1102 transform(3,1) = _center[1]*(1 - rScaleFactor);
1103 transform(3,2) = _center[2]*(1 - rScaleFactor);
1104 matrix.postMult(transform);
1109 double computeScaleFactor(osg::NodeVisitor* nv) const
1114 osg::Vec3 localEyeToCenter = nv->getEyePoint() - _center;
1115 localEyeToCenter.normalize();
1117 double cos_angle = localEyeToCenter*_axis;
1118 double scale_factor = 0;
1119 if ( _two_sides && cos_angle < 0 )
1120 scale_factor = _factor * pow( -cos_angle, _power ) + _offset;
1121 else if ( cos_angle > 0 )
1122 scale_factor = _factor * pow( cos_angle, _power ) + _offset;
1124 if ( scale_factor < _min_v )
1125 scale_factor = _min_v;
1126 if ( scale_factor > _max_v )
1127 scale_factor = _max_v;
1129 return scale_factor;
1132 virtual osg::BoundingSphere computeBound() const
1134 // avoid being culled away by small feature culling
1135 osg::BoundingSphere bs = osg::Group::computeBound();
1136 bs.radius() *= _max_v;
1143 double _power, _factor, _offset, _min_v, _max_v;
1148 SGFlashAnimation::SGFlashAnimation(const SGPropertyNode* configNode,
1149 SGPropertyNode* modelRoot) :
1150 SGAnimation(configNode, modelRoot)
1155 SGFlashAnimation::createAnimationGroup(osg::Group& parent)
1157 Transform* transform = new Transform(getConfig());
1158 parent.addChild(transform);
1163 ////////////////////////////////////////////////////////////////////////
1164 // Implementation of flash animation
1165 ////////////////////////////////////////////////////////////////////////
1167 class SGBillboardAnimation::Transform : public osg::Transform {
1169 Transform(const SGPropertyNode* configNode) :
1170 _spherical(configNode->getBoolValue("spherical", true))
1172 setReferenceFrame(RELATIVE_RF);
1173 setName(configNode->getStringValue("name", "billboard animation"));
1175 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
1176 osg::NodeVisitor* nv) const
1178 // More or less taken from plibs ssgCutout
1180 matrix(0,0) = 1; matrix(0,1) = 0; matrix(0,2) = 0;
1181 matrix(1,0) = 0; matrix(1,1) = 0; matrix(1,2) = -1;
1182 matrix(2,0) = 0; matrix(2,1) = 1; matrix(2,2) = 0;
1184 osg::Vec3 zAxis(matrix(2, 0), matrix(2, 1), matrix(2, 2));
1185 osg::Vec3 xAxis = osg::Vec3(0, 0, -1)^zAxis;
1186 osg::Vec3 yAxis = zAxis^xAxis;
1192 matrix(0,0) = xAxis[0]; matrix(0,1) = xAxis[1]; matrix(0,2) = xAxis[2];
1193 matrix(1,0) = yAxis[0]; matrix(1,1) = yAxis[1]; matrix(1,2) = yAxis[2];
1194 matrix(2,0) = zAxis[0]; matrix(2,1) = zAxis[1]; matrix(2,2) = zAxis[2];
1199 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
1200 osg::NodeVisitor* nv) const
1202 // Hmm, don't yet know how to get that back ...
1211 SGBillboardAnimation::SGBillboardAnimation(const SGPropertyNode* configNode,
1212 SGPropertyNode* modelRoot) :
1213 SGAnimation(configNode, modelRoot)
1218 SGBillboardAnimation::createAnimationGroup(osg::Group& parent)
1220 Transform* transform = new Transform(getConfig());
1221 parent.addChild(transform);
1226 ////////////////////////////////////////////////////////////////////////
1227 // Implementation of a range animation
1228 ////////////////////////////////////////////////////////////////////////
1230 class SGRangeAnimation::UpdateCallback : public osg::NodeCallback {
1232 UpdateCallback(const SGCondition* condition,
1233 const SGExpressiond* minAnimationValue,
1234 const SGExpressiond* maxAnimationValue,
1235 double minValue, double maxValue) :
1236 _condition(condition),
1237 _minAnimationValue(minAnimationValue),
1238 _maxAnimationValue(maxAnimationValue),
1239 _minStaticValue(minValue),
1240 _maxStaticValue(maxValue)
1242 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1244 osg::LOD* lod = static_cast<osg::LOD*>(node);
1245 if (!_condition || _condition->test()) {
1247 if (_minAnimationValue)
1248 minRange = _minAnimationValue->getValue();
1250 minRange = _minStaticValue;
1252 if (_maxAnimationValue)
1253 maxRange = _maxAnimationValue->getValue();
1255 maxRange = _maxStaticValue;
1256 lod->setRange(0, minRange, maxRange);
1258 lod->setRange(0, 0, SGLimitsf::max());
1264 SGSharedPtr<const SGCondition> _condition;
1265 SGSharedPtr<const SGExpressiond> _minAnimationValue;
1266 SGSharedPtr<const SGExpressiond> _maxAnimationValue;
1267 double _minStaticValue;
1268 double _maxStaticValue;
1271 SGRangeAnimation::SGRangeAnimation(const SGPropertyNode* configNode,
1272 SGPropertyNode* modelRoot) :
1273 SGAnimation(configNode, modelRoot)
1275 _condition = getCondition();
1277 std::string inputPropertyName;
1278 inputPropertyName = configNode->getStringValue("min-property", "");
1279 if (!inputPropertyName.empty()) {
1280 SGPropertyNode* inputProperty;
1281 inputProperty = modelRoot->getNode(inputPropertyName, true);
1282 SGSharedPtr<SGExpressiond> value;
1283 value = new SGPropertyExpression<double>(inputProperty);
1285 value = read_factor_offset(configNode, value, "min-factor", "min-offset");
1286 _minAnimationValue = value->simplify();
1288 inputPropertyName = configNode->getStringValue("max-property", "");
1289 if (!inputPropertyName.empty()) {
1290 SGPropertyNode* inputProperty;
1291 inputProperty = modelRoot->getNode(inputPropertyName.c_str(), true);
1293 SGSharedPtr<SGExpressiond> value;
1294 value = new SGPropertyExpression<double>(inputProperty);
1296 value = read_factor_offset(configNode, value, "max-factor", "max-offset");
1297 _maxAnimationValue = value->simplify();
1300 _initialValue[0] = configNode->getDoubleValue("min-m", 0);
1301 _initialValue[0] *= configNode->getDoubleValue("min-factor", 1);
1302 _initialValue[1] = configNode->getDoubleValue("max-m", SGLimitsf::max());
1303 _initialValue[1] *= configNode->getDoubleValue("max-factor", 1);
1307 SGRangeAnimation::createAnimationGroup(osg::Group& parent)
1309 osg::Group* group = new osg::Group;
1310 group->setName("range animation group");
1312 osg::LOD* lod = new osg::LOD;
1313 lod->setName("range animation node");
1314 parent.addChild(lod);
1316 lod->addChild(group, _initialValue[0], _initialValue[1]);
1317 lod->setCenterMode(osg::LOD::USE_BOUNDING_SPHERE_CENTER);
1318 lod->setRangeMode(osg::LOD::DISTANCE_FROM_EYE_POINT);
1319 if (_minAnimationValue || _maxAnimationValue || _condition) {
1321 uc = new UpdateCallback(_condition, _minAnimationValue, _maxAnimationValue,
1322 _initialValue[0], _initialValue[1]);
1323 lod->setUpdateCallback(uc);
1329 ////////////////////////////////////////////////////////////////////////
1330 // Implementation of a select animation
1331 ////////////////////////////////////////////////////////////////////////
1333 class SGSelectAnimation::UpdateCallback : public osg::NodeCallback {
1335 UpdateCallback(const SGCondition* condition) :
1336 _condition(condition)
1338 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1340 osg::Switch* sw = static_cast<osg::Switch*>(node);
1341 if (_condition->test())
1342 sw->setAllChildrenOn();
1344 sw->setAllChildrenOff();
1349 SGSharedPtr<SGCondition const> _condition;
1352 SGSelectAnimation::SGSelectAnimation(const SGPropertyNode* configNode,
1353 SGPropertyNode* modelRoot) :
1354 SGAnimation(configNode, modelRoot)
1359 SGSelectAnimation::createAnimationGroup(osg::Group& parent)
1361 // if no condition given, this is a noop.
1362 SGSharedPtr<SGCondition const> condition = getCondition();
1363 // trick, gets deleted with all its 'animated' children
1364 // when the animation installer returns
1366 return new osg::Group;
1368 osg::Switch* sw = new osg::Switch;
1369 sw->setName("select animation node");
1370 sw->setUpdateCallback(new UpdateCallback(condition));
1371 parent.addChild(sw);
1377 ////////////////////////////////////////////////////////////////////////
1378 // Implementation of alpha test animation
1379 ////////////////////////////////////////////////////////////////////////
1381 SGAlphaTestAnimation::SGAlphaTestAnimation(const SGPropertyNode* configNode,
1382 SGPropertyNode* modelRoot) :
1383 SGAnimation(configNode, modelRoot)
1389 // Keep one copy of the most common alpha test its state set.
1390 OpenThreads::ReentrantMutex alphaTestMutex;
1391 osg::ref_ptr<osg::AlphaFunc> standardAlphaFunc;
1392 osg::ref_ptr<osg::StateSet> alphaFuncStateSet;
1394 osg::AlphaFunc* makeAlphaFunc(float clamp)
1396 using namespace OpenThreads;
1397 ScopedLock<ReentrantMutex> lock(alphaTestMutex);
1398 if (osg::equivalent(clamp, 0.01f)) {
1399 if (standardAlphaFunc.valid())
1400 return standardAlphaFunc.get();
1403 osg::AlphaFunc* alphaFunc = new osg::AlphaFunc;
1404 alphaFunc->setFunction(osg::AlphaFunc::GREATER);
1405 alphaFunc->setReferenceValue(clamp);
1406 alphaFunc->setDataVariance(osg::Object::STATIC);
1407 if (osg::equivalent(clamp, 0.01f))
1408 standardAlphaFunc = alphaFunc;
1412 osg::StateSet* makeAlphaTestStateSet(float clamp)
1414 using namespace OpenThreads;
1415 ScopedLock<ReentrantMutex> lock(alphaTestMutex);
1416 if (osg::equivalent(clamp, 0.01f)) {
1417 if (alphaFuncStateSet.valid())
1418 return alphaFuncStateSet.get();
1420 osg::AlphaFunc* alphaFunc = makeAlphaFunc(clamp);
1421 osg::StateSet* stateSet = new osg::StateSet;
1422 stateSet->setAttributeAndModes(alphaFunc,
1423 (osg::StateAttribute::ON
1424 | osg::StateAttribute::OVERRIDE));
1425 stateSet->setDataVariance(osg::Object::STATIC);
1426 if (osg::equivalent(clamp, 0.01f))
1427 alphaFuncStateSet = stateSet;
1432 SGAlphaTestAnimation::install(osg::Node& node)
1434 SGAnimation::install(node);
1436 float alphaClamp = getConfig()->getFloatValue("alpha-factor", 0);
1437 osg::StateSet* stateSet = node.getStateSet();
1439 node.setStateSet(makeAlphaTestStateSet(alphaClamp));
1441 stateSet->setAttributeAndModes(makeAlphaFunc(alphaClamp),
1442 (osg::StateAttribute::ON
1443 | osg::StateAttribute::OVERRIDE));
1448 //////////////////////////////////////////////////////////////////////
1449 // Blend animation installer
1450 //////////////////////////////////////////////////////////////////////
1452 // XXX This needs to be replaced by something using TexEnvCombine to
1453 // change the blend factor. Changing the alpha values in the geometry
1455 class SGBlendAnimation::BlendVisitor : public osg::NodeVisitor {
1457 BlendVisitor(float blend) :
1458 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
1460 { setVisitorType(osg::NodeVisitor::NODE_VISITOR); }
1461 virtual void apply(osg::Node& node)
1463 updateStateSet(node.getStateSet());
1466 virtual void apply(osg::Geode& node)
1468 apply((osg::Node&)node);
1469 unsigned nDrawables = node.getNumDrawables();
1470 for (unsigned i = 0; i < nDrawables; ++i) {
1471 osg::Drawable* drawable = node.getDrawable(i);
1472 osg::Geometry* geometry = drawable->asGeometry();
1475 osg::Array* array = geometry->getColorArray();
1478 osg::Vec4Array* vec4Array = dynamic_cast<osg::Vec4Array*>(array);
1481 for (unsigned k = 0; k < vec4Array->size(); ++k) {
1482 (*vec4Array)[k][3] = _blend;
1485 updateStateSet(drawable->getStateSet());
1488 void updateStateSet(osg::StateSet* stateSet)
1492 osg::StateAttribute* stateAttribute;
1493 stateAttribute = stateSet->getAttribute(osg::StateAttribute::MATERIAL);
1494 if (!stateAttribute)
1496 osg::Material* material = dynamic_cast<osg::Material*>(stateAttribute);
1499 material->setAlpha(osg::Material::FRONT_AND_BACK, _blend);
1501 stateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
1502 stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
1504 stateSet->setRenderingHint(osg::StateSet::DEFAULT_BIN);
1511 class SGBlendAnimation::UpdateCallback : public osg::NodeCallback {
1513 UpdateCallback(const SGPropertyNode* configNode, const SGExpressiond* v) :
1517 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1519 double blend = _animationValue->getValue();
1520 if (blend != _prev_value) {
1521 _prev_value = blend;
1522 BlendVisitor visitor(1-blend);
1523 node->accept(visitor);
1529 SGSharedPtr<SGExpressiond const> _animationValue;
1533 SGBlendAnimation::SGBlendAnimation(const SGPropertyNode* configNode,
1534 SGPropertyNode* modelRoot)
1535 : SGAnimation(configNode, modelRoot),
1536 _animationValue(read_value(configNode, modelRoot, "", 0, 1))
1541 SGBlendAnimation::createAnimationGroup(osg::Group& parent)
1543 if (!_animationValue)
1546 osg::Group* group = new osg::Switch;
1547 group->setName("blend animation node");
1548 group->setUpdateCallback(new UpdateCallback(getConfig(), _animationValue));
1549 parent.addChild(group);
1554 SGBlendAnimation::install(osg::Node& node)
1556 SGAnimation::install(node);
1557 // make sure we do not change common geometries,
1558 // that also creates new display lists for these subgeometries.
1559 cloneDrawables(node);
1560 DoDrawArraysVisitor visitor;
1561 node.accept(visitor);
1565 //////////////////////////////////////////////////////////////////////
1566 // Timed animation installer
1567 //////////////////////////////////////////////////////////////////////
1571 class SGTimedAnimation::UpdateCallback : public osg::NodeCallback {
1573 UpdateCallback(const SGPropertyNode* configNode) :
1576 _duration_sec(configNode->getDoubleValue("duration-sec", 1)),
1577 _last_time_sec(SGLimitsd::max()),
1578 _use_personality(configNode->getBoolValue("use-personality", false))
1580 std::vector<SGSharedPtr<SGPropertyNode> > nodes;
1581 nodes = configNode->getChildren("branch-duration-sec");
1582 for (size_t i = 0; i < nodes.size(); ++i) {
1583 unsigned ind = nodes[ i ]->getIndex();
1584 while ( ind >= _durations.size() ) {
1585 _durations.push_back(DurationSpec(_duration_sec));
1587 SGPropertyNode_ptr rNode = nodes[i]->getChild("random");
1589 _durations[ind] = DurationSpec(nodes[ i ]->getDoubleValue());
1591 _durations[ind] = DurationSpec(rNode->getDoubleValue( "min", 0),
1592 rNode->getDoubleValue( "max", 1));
1596 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1598 assert(dynamic_cast<osg::Switch*>(node));
1599 osg::Switch* sw = static_cast<osg::Switch*>(node);
1601 unsigned nChildren = sw->getNumChildren();
1603 // blow up the durations vector to the required size
1604 while (_durations.size() < nChildren) {
1605 _durations.push_back(_duration_sec);
1607 // make sure the current index is an duration that really exists
1608 _current_index = _current_index % nChildren;
1610 // update the time and compute the current systems time value
1611 double t = nv->getFrameStamp()->getReferenceTime();
1612 if (_last_time_sec == SGLimitsd::max()) {
1615 double dt = t - _last_time_sec;
1616 if (_use_personality)
1617 dt *= 1 + 0.2*(0.5 - sg_random());
1622 double currentDuration = _durations[_current_index].get();
1623 while (currentDuration < _reminder) {
1624 _reminder -= currentDuration;
1625 _current_index = (_current_index + 1) % nChildren;
1626 currentDuration = _durations[_current_index].get();
1629 sw->setSingleChildOn(_current_index);
1635 struct DurationSpec {
1636 DurationSpec(double t) :
1637 minTime(SGMiscd::max(0.01, t)),
1638 maxTime(SGMiscd::max(0.01, t))
1640 DurationSpec(double t0, double t1) :
1641 minTime(SGMiscd::max(0.01, t0)),
1642 maxTime(SGMiscd::max(0.01, t1))
1645 { return minTime + sg_random()*(maxTime - minTime); }
1649 std::vector<DurationSpec> _durations;
1650 unsigned _current_index;
1652 double _duration_sec;
1653 double _last_time_sec;
1654 bool _use_personality;
1658 SGTimedAnimation::SGTimedAnimation(const SGPropertyNode* configNode,
1659 SGPropertyNode* modelRoot)
1660 : SGAnimation(configNode, modelRoot)
1665 SGTimedAnimation::createAnimationGroup(osg::Group& parent)
1667 osg::Switch* sw = new osg::Switch;
1668 sw->setName("timed animation node");
1669 sw->setUpdateCallback(new UpdateCallback(getConfig()));
1670 parent.addChild(sw);
1675 ////////////////////////////////////////////////////////////////////////
1676 // dynamically switch on/off shadows
1677 ////////////////////////////////////////////////////////////////////////
1679 class SGShadowAnimation::UpdateCallback : public osg::NodeCallback {
1681 UpdateCallback(const SGCondition* condition) :
1682 _condition(condition)
1684 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1686 if (_condition->test())
1687 node->setNodeMask( SG_NODEMASK_CASTSHADOW_BIT | node->getNodeMask());
1689 node->setNodeMask(~SG_NODEMASK_CASTSHADOW_BIT & node->getNodeMask());
1694 SGSharedPtr<const SGCondition> _condition;
1697 SGShadowAnimation::SGShadowAnimation(const SGPropertyNode* configNode,
1698 SGPropertyNode* modelRoot) :
1699 SGAnimation(configNode, modelRoot)
1704 SGShadowAnimation::createAnimationGroup(osg::Group& parent)
1706 SGSharedPtr<SGCondition const> condition = getCondition();
1710 osg::Group* group = new osg::Group;
1711 group->setName("shadow animation");
1712 group->setUpdateCallback(new UpdateCallback(condition));
1713 parent.addChild(group);
1718 ////////////////////////////////////////////////////////////////////////
1719 // Implementation of SGTexTransformAnimation
1720 ////////////////////////////////////////////////////////////////////////
1722 class SGTexTransformAnimation::Transform : public SGReferenced {
1727 virtual ~Transform()
1729 void setValue(double value)
1731 virtual void transform(osg::Matrix&) = 0;
1736 class SGTexTransformAnimation::Translation :
1737 public SGTexTransformAnimation::Transform {
1739 Translation(const SGVec3d& axis) :
1742 virtual void transform(osg::Matrix& matrix)
1745 set_translation(tmp, _value, _axis);
1746 matrix.preMult(tmp);
1752 class SGTexTransformAnimation::Rotation :
1753 public SGTexTransformAnimation::Transform {
1755 Rotation(const SGVec3d& axis, const SGVec3d& center) :
1759 virtual void transform(osg::Matrix& matrix)
1762 set_rotation(tmp, _value, _center, _axis);
1763 matrix.preMult(tmp);
1770 class SGTexTransformAnimation::UpdateCallback :
1771 public osg::StateAttribute::Callback {
1773 UpdateCallback(const SGCondition* condition) :
1774 _condition(condition)
1776 virtual void operator () (osg::StateAttribute* sa, osg::NodeVisitor*)
1778 if (!_condition || _condition->test()) {
1779 TransformList::const_iterator i;
1780 for (i = _transforms.begin(); i != _transforms.end(); ++i)
1781 i->transform->setValue(i->value->getValue());
1783 assert(dynamic_cast<osg::TexMat*>(sa));
1784 osg::TexMat* texMat = static_cast<osg::TexMat*>(sa);
1785 texMat->getMatrix().makeIdentity();
1786 TransformList::const_iterator i;
1787 for (i = _transforms.begin(); i != _transforms.end(); ++i)
1788 i->transform->transform(texMat->getMatrix());
1790 void appendTransform(Transform* transform, SGExpressiond* value)
1792 Entry entry = { transform, value };
1793 transform->transform(_matrix);
1794 _transforms.push_back(entry);
1799 SGSharedPtr<Transform> transform;
1800 SGSharedPtr<const SGExpressiond> value;
1802 typedef std::vector<Entry> TransformList;
1803 TransformList _transforms;
1804 SGSharedPtr<const SGCondition> _condition;
1805 osg::Matrix _matrix;
1808 SGTexTransformAnimation::SGTexTransformAnimation(const SGPropertyNode* configNode,
1809 SGPropertyNode* modelRoot) :
1810 SGAnimation(configNode, modelRoot)
1815 SGTexTransformAnimation::createAnimationGroup(osg::Group& parent)
1817 osg::Group* group = new osg::Group;
1818 group->setName("texture transform group");
1819 osg::StateSet* stateSet = group->getOrCreateStateSet();
1820 osg::TexMat* texMat = new osg::TexMat;
1821 UpdateCallback* updateCallback = new UpdateCallback(getCondition());
1822 // interpret the configs ...
1823 std::string type = getType();
1825 if (type == "textranslate") {
1826 appendTexTranslate(getConfig(), updateCallback);
1827 } else if (type == "texrotate") {
1828 appendTexRotate(getConfig(), updateCallback);
1829 } else if (type == "texmultiple") {
1830 std::vector<SGSharedPtr<SGPropertyNode> > transformConfigs;
1831 transformConfigs = getConfig()->getChildren("transform");
1832 for (unsigned i = 0; i < transformConfigs.size(); ++i) {
1833 std::string subtype = transformConfigs[i]->getStringValue("subtype", "");
1834 if (subtype == "textranslate")
1835 appendTexTranslate(transformConfigs[i], updateCallback);
1836 else if (subtype == "texrotate")
1837 appendTexRotate(transformConfigs[i], updateCallback);
1839 SG_LOG(SG_INPUT, SG_ALERT,
1840 "Ignoring unknown texture transform subtype");
1843 SG_LOG(SG_INPUT, SG_ALERT, "Ignoring unknown texture transform type");
1846 texMat->setUpdateCallback(updateCallback);
1847 stateSet->setTextureAttribute(0, texMat);
1848 parent.addChild(group);
1853 SGTexTransformAnimation::appendTexTranslate(const SGPropertyNode* config,
1854 UpdateCallback* updateCallback)
1856 std::string propertyName = config->getStringValue("property", "");
1857 SGSharedPtr<SGExpressiond> value;
1858 if (propertyName.empty())
1859 value = new SGConstExpression<double>(0);
1861 SGPropertyNode* inputProperty = getModelRoot()->getNode(propertyName, true);
1862 value = new SGPropertyExpression<double>(inputProperty);
1865 SGInterpTable* table = read_interpolation_table(config);
1867 value = new SGInterpTableExpression<double>(value, table);
1868 double biasValue = config->getDoubleValue("bias", 0);
1870 value = new SGBiasExpression<double>(value, biasValue);
1871 value = new SGStepExpression<double>(value,
1872 config->getDoubleValue("step", 0),
1873 config->getDoubleValue("scroll", 0));
1874 value = value->simplify();
1876 double biasValue = config->getDoubleValue("bias", 0);
1878 value = new SGBiasExpression<double>(value, biasValue);
1879 value = new SGStepExpression<double>(value,
1880 config->getDoubleValue("step", 0),
1881 config->getDoubleValue("scroll", 0));
1882 value = read_offset_factor(config, value, "factor", "offset");
1884 if (config->hasChild("min") || config->hasChild("max")) {
1885 double minClip = config->getDoubleValue("min", -SGLimitsd::max());
1886 double maxClip = config->getDoubleValue("max", SGLimitsd::max());
1887 value = new SGClipExpression<double>(value, minClip, maxClip);
1889 value = value->simplify();
1891 SGVec3d axis(config->getDoubleValue("axis/x", 0),
1892 config->getDoubleValue("axis/y", 0),
1893 config->getDoubleValue("axis/z", 0));
1894 Translation* translation;
1895 translation = new Translation(normalize(axis));
1896 translation->setValue(config->getDoubleValue("starting-position", 0));
1897 updateCallback->appendTransform(translation, value);
1901 SGTexTransformAnimation::appendTexRotate(const SGPropertyNode* config,
1902 UpdateCallback* updateCallback)
1904 std::string propertyName = config->getStringValue("property", "");
1905 SGSharedPtr<SGExpressiond> value;
1906 if (propertyName.empty())
1907 value = new SGConstExpression<double>(0);
1909 SGPropertyNode* inputProperty = getModelRoot()->getNode(propertyName, true);
1910 value = new SGPropertyExpression<double>(inputProperty);
1913 SGInterpTable* table = read_interpolation_table(config);
1915 value = new SGInterpTableExpression<double>(value, table);
1916 double biasValue = config->getDoubleValue("bias", 0);
1918 value = new SGBiasExpression<double>(value, biasValue);
1919 value = new SGStepExpression<double>(value,
1920 config->getDoubleValue("step", 0),
1921 config->getDoubleValue("scroll", 0));
1922 value = value->simplify();
1924 double biasValue = config->getDoubleValue("bias", 0);
1926 value = new SGBiasExpression<double>(value, biasValue);
1927 value = new SGStepExpression<double>(value,
1928 config->getDoubleValue("step", 0),
1929 config->getDoubleValue("scroll", 0));
1930 value = read_offset_factor(config, value, "factor", "offset-deg");
1932 if (config->hasChild("min-deg") || config->hasChild("max-deg")) {
1933 double minClip = config->getDoubleValue("min-deg", -SGLimitsd::max());
1934 double maxClip = config->getDoubleValue("max-deg", SGLimitsd::max());
1935 value = new SGClipExpression<double>(value, minClip, maxClip);
1937 value = value->simplify();
1939 SGVec3d axis(config->getDoubleValue("axis/x", 0),
1940 config->getDoubleValue("axis/y", 0),
1941 config->getDoubleValue("axis/z", 0));
1942 SGVec3d center(config->getDoubleValue("center/x", 0),
1943 config->getDoubleValue("center/y", 0),
1944 config->getDoubleValue("center/z", 0));
1946 rotation = new Rotation(normalize(axis), center);
1947 rotation->setValue(config->getDoubleValue("starting-position-deg", 0));
1948 updateCallback->appendTransform(rotation, value);
1952 ////////////////////////////////////////////////////////////////////////
1953 // Implementation of SGPickAnimation
1954 ////////////////////////////////////////////////////////////////////////
1956 class SGPickAnimation::PickCallback : public SGPickCallback {
1958 PickCallback(const SGPropertyNode* configNode,
1959 SGPropertyNode* modelRoot) :
1960 _button(configNode->getIntValue("button", -1)),
1961 _repeatable(configNode->getBoolValue("repeatable", false)),
1962 _repeatInterval(configNode->getDoubleValue("interval-sec", 0.1))
1964 SG_LOG(SG_INPUT, SG_DEBUG, "Reading all bindings");
1965 std::vector<SGPropertyNode_ptr> bindings;
1966 bindings = configNode->getChildren("binding");
1967 for (unsigned int i = 0; i < bindings.size(); ++i) {
1968 _bindingsDown.push_back(new SGBinding(bindings[i], modelRoot));
1971 const SGPropertyNode* upNode = configNode->getChild("mod-up");
1974 bindings = upNode->getChildren("binding");
1975 for (unsigned int i = 0; i < bindings.size(); ++i) {
1976 _bindingsUp.push_back(new SGBinding(bindings[i], modelRoot));
1979 virtual bool buttonPressed(int button, const Info&)
1981 if (0 <= _button && button != _button)
1983 SGBindingList::const_iterator i;
1984 for (i = _bindingsDown.begin(); i != _bindingsDown.end(); ++i)
1989 virtual void buttonReleased(void)
1991 SGBindingList::const_iterator i;
1992 for (i = _bindingsUp.begin(); i != _bindingsUp.end(); ++i)
1995 virtual void update(double dt)
2001 while (_repeatInterval < _repeatTime) {
2002 _repeatTime -= _repeatInterval;
2003 SGBindingList::const_iterator i;
2004 for (i = _bindingsDown.begin(); i != _bindingsDown.end(); ++i)
2009 SGBindingList _bindingsDown;
2010 SGBindingList _bindingsUp;
2013 double _repeatInterval;
2017 SGPickAnimation::SGPickAnimation(const SGPropertyNode* configNode,
2018 SGPropertyNode* modelRoot) :
2019 SGAnimation(configNode, modelRoot)
2024 SGPickAnimation::createAnimationGroup(osg::Group& parent)
2026 osg::Group* commonGroup = new osg::Group;
2028 // Contains the normal geometry that is interactive
2029 osg::ref_ptr<osg::Group> normalGroup = new osg::Group;
2030 normalGroup->addChild(commonGroup);
2032 // Used to render the geometry with just yellow edges
2033 osg::Group* highlightGroup = new osg::Group;
2034 highlightGroup->setNodeMask(SG_NODEMASK_PICK_BIT);
2035 highlightGroup->addChild(commonGroup);
2036 SGSceneUserData* ud;
2037 ud = SGSceneUserData::getOrCreateSceneUserData(highlightGroup);
2038 std::vector<SGPropertyNode_ptr> actions;
2039 actions = getConfig()->getChildren("action");
2040 for (unsigned int i = 0; i < actions.size(); ++i)
2041 ud->addPickCallback(new PickCallback(actions[i], getModelRoot()));
2043 // prepare a state set that paints the edges of this object yellow
2044 osg::StateSet* stateSet = highlightGroup->getOrCreateStateSet();
2045 stateSet->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE);
2047 osg::PolygonOffset* polygonOffset = new osg::PolygonOffset;
2048 polygonOffset->setFactor(-1);
2049 polygonOffset->setUnits(-1);
2050 stateSet->setAttribute(polygonOffset, osg::StateAttribute::OVERRIDE);
2051 stateSet->setMode(GL_POLYGON_OFFSET_LINE, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
2053 osg::PolygonMode* polygonMode = new osg::PolygonMode;
2054 polygonMode->setMode(osg::PolygonMode::FRONT_AND_BACK,
2055 osg::PolygonMode::LINE);
2056 stateSet->setAttribute(polygonMode, osg::StateAttribute::OVERRIDE);
2058 osg::Material* material = new osg::Material;
2059 material->setColorMode(osg::Material::OFF);
2060 material->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(1, 1, 0, 1));
2061 material->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(1, 1, 0, 1));
2062 material->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(1, 1, 0, 1));
2063 material->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0, 0, 0, 0));
2064 stateSet->setAttribute(material, osg::StateAttribute::OVERRIDE);
2066 // Only add normal geometry if configured
2067 if (getConfig()->getBoolValue("visible", true))
2068 parent.addChild(normalGroup.get());
2069 parent.addChild(highlightGroup);