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_value(const SGPropertyNode* configNode, SGPropertyNode* modelRoot,
178 const char* unit, double defMin, double defMax)
180 SGExpression<double>* value = 0;
182 std::string inputPropertyName = configNode->getStringValue("property", "");
183 if (inputPropertyName.empty()) {
184 std::string spos = unit_string("starting-position", unit);
185 double initPos = configNode->getDoubleValue(spos, 0);
186 value = new SGConstExpression<double>(initPos);
188 SGPropertyNode* inputProperty;
189 inputProperty = modelRoot->getNode(inputPropertyName, true);
190 value = new SGPropertyExpression<double>(inputProperty);
193 SGInterpTable* interpTable = read_interpolation_table(configNode);
195 return new SGInterpTableExpression<double>(value, interpTable);
197 std::string offset = unit_string("offset", unit);
198 std::string min = unit_string("min", unit);
199 std::string max = unit_string("max", unit);
201 if (configNode->getBoolValue("use-personality", false)) {
202 value = new SGPersonalityScaleOffsetExpression(value, configNode,
205 value = read_factor_offset(configNode, value, "factor", offset);
208 double minClip = configNode->getDoubleValue(min, defMin);
209 double maxClip = configNode->getDoubleValue(max, defMax);
210 if (minClip > SGMiscd::min(SGLimitsd::min(), -SGLimitsd::max()) ||
211 maxClip < SGLimitsd::max())
212 value = new SGClipExpression<double>(value, minClip, maxClip);
220 ////////////////////////////////////////////////////////////////////////
221 // Animation installer
222 ////////////////////////////////////////////////////////////////////////
224 class SGAnimation::RemoveModeVisitor : public SGStateAttributeVisitor {
226 RemoveModeVisitor(osg::StateAttribute::GLMode mode) :
229 virtual void apply(osg::StateSet* stateSet)
233 stateSet->removeMode(_mode);
236 osg::StateAttribute::GLMode _mode;
239 class SGAnimation::RemoveAttributeVisitor : public SGStateAttributeVisitor {
241 RemoveAttributeVisitor(osg::StateAttribute::Type type) :
244 virtual void apply(osg::StateSet* stateSet)
248 while (stateSet->getAttribute(_type)) {
249 stateSet->removeAttribute(_type);
253 osg::StateAttribute::Type _type;
256 class SGAnimation::RemoveTextureModeVisitor : public SGStateAttributeVisitor {
258 RemoveTextureModeVisitor(unsigned unit, osg::StateAttribute::GLMode mode) :
262 virtual void apply(osg::StateSet* stateSet)
266 stateSet->removeTextureMode(_unit, _mode);
270 osg::StateAttribute::GLMode _mode;
273 class SGAnimation::RemoveTextureAttributeVisitor :
274 public SGStateAttributeVisitor {
276 RemoveTextureAttributeVisitor(unsigned unit,
277 osg::StateAttribute::Type type) :
281 virtual void apply(osg::StateSet* stateSet)
285 while (stateSet->getTextureAttribute(_unit, _type)) {
286 stateSet->removeTextureAttribute(_unit, _type);
291 osg::StateAttribute::Type _type;
294 class SGAnimation::BinToInheritVisitor : public SGStateAttributeVisitor {
296 virtual void apply(osg::StateSet* stateSet)
300 stateSet->setRenderBinToInherit();
304 class SGAnimation::DrawableCloneVisitor : public osg::NodeVisitor {
306 DrawableCloneVisitor() :
307 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
309 void apply(osg::Geode& geode)
311 for (unsigned i = 0 ; i < geode.getNumDrawables(); ++i) {
312 osg::CopyOp copyOp(osg::CopyOp::DEEP_COPY_ALL &
313 ~osg::CopyOp::DEEP_COPY_TEXTURES);
314 geode.setDrawable(i, copyOp(geode.getDrawable(i)));
321 // Set all drawables to not use display lists. OSG will use
322 // glDrawArrays instead.
323 struct DoDrawArraysVisitor : public osg::NodeVisitor {
324 DoDrawArraysVisitor() :
325 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
327 void apply(osg::Geode& geode)
332 for (int i = 0; i < geode.getNumDrawables(); ++i)
333 geode.getDrawable(i)->setUseDisplayList(false);
338 SGAnimation::SGAnimation(const SGPropertyNode* configNode,
339 SGPropertyNode* modelRoot) :
340 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
342 _configNode(configNode),
343 _modelRoot(modelRoot)
345 _name = configNode->getStringValue("name", "");
346 _enableHOT = configNode->getBoolValue("enable-hot", true);
347 _disableShadow = configNode->getBoolValue("disable-shadow", false);
348 std::vector<SGPropertyNode_ptr> objectNames =
349 configNode->getChildren("object-name");
350 for (unsigned i = 0; i < objectNames.size(); ++i)
351 _objectNames.push_back(objectNames[i]->getStringValue());
354 SGAnimation::~SGAnimation()
359 SG_LOG(SG_IO, SG_ALERT, "Could not find at least one of the following"
360 " objects for animation:\n");
361 std::list<std::string>::const_iterator i;
362 for (i = _objectNames.begin(); i != _objectNames.end(); ++i)
363 SG_LOG(SG_IO, SG_ALERT, *i << "\n");
367 SGAnimation::animate(osg::Node* node, const SGPropertyNode* configNode,
368 SGPropertyNode* modelRoot,
369 const osgDB::ReaderWriter::Options* options)
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 == "material") {
388 SGMaterialAnimation animInst(configNode, modelRoot, options);
389 animInst.apply(node);
390 } else if (type == "noshadow") {
391 SGShadowAnimation animInst(configNode, modelRoot);
392 animInst.apply(node);
393 } else if (type == "pick") {
394 SGPickAnimation animInst(configNode, modelRoot);
395 animInst.apply(node);
396 } else if (type == "range") {
397 SGRangeAnimation animInst(configNode, modelRoot);
398 animInst.apply(node);
399 } else if (type == "rotate" || type == "spin") {
400 SGRotateAnimation animInst(configNode, modelRoot);
401 animInst.apply(node);
402 } else if (type == "scale") {
403 SGScaleAnimation animInst(configNode, modelRoot);
404 animInst.apply(node);
405 } else if (type == "select") {
406 SGSelectAnimation animInst(configNode, modelRoot);
407 animInst.apply(node);
408 } else if (type == "shader") {
409 SGShaderAnimation animInst(configNode, modelRoot, options);
410 animInst.apply(node);
411 } else if (type == "textranslate" || type == "texrotate" ||
412 type == "texmultiple") {
413 SGTexTransformAnimation animInst(configNode, modelRoot);
414 animInst.apply(node);
415 } else if (type == "timed") {
416 SGTimedAnimation animInst(configNode, modelRoot);
417 animInst.apply(node);
418 } else if (type == "translate") {
419 SGTranslateAnimation animInst(configNode, modelRoot);
420 animInst.apply(node);
421 } else if (type == "null" || type == "none" || type.empty()) {
422 SGGroupAnimation animInst(configNode, modelRoot);
423 animInst.apply(node);
432 SGAnimation::apply(osg::Node* node)
434 // duh what a special case ...
435 if (_objectNames.empty()) {
436 osg::Group* group = node->asGroup();
438 osg::ref_ptr<osg::Group> animationGroup;
439 installInGroup(std::string(), *group, animationGroup);
446 SGAnimation::install(osg::Node& node)
450 node.setNodeMask( SG_NODEMASK_TERRAIN_BIT | node.getNodeMask());
452 node.setNodeMask(~SG_NODEMASK_TERRAIN_BIT & node.getNodeMask());
454 node.setNodeMask( SG_NODEMASK_CASTSHADOW_BIT | node.getNodeMask());
456 node.setNodeMask(~SG_NODEMASK_CASTSHADOW_BIT & node.getNodeMask());
460 SGAnimation::createAnimationGroup(osg::Group& parent)
462 // default implementation, we do not need a new group
463 // for every animation type. Usually animations that just change
464 // the StateSet of some parts of the model
469 SGAnimation::apply(osg::Group& group)
471 // the trick is to first traverse the children and then
472 // possibly splice in a new group node if required.
473 // Else we end up in a recursive loop where we infinitly insert new
477 // Note that this algorithm preserves the order of the child objects
478 // like they appear in the object-name tags.
479 // The timed animations require this
480 osg::ref_ptr<osg::Group> animationGroup;
481 std::list<std::string>::const_iterator nameIt;
482 for (nameIt = _objectNames.begin(); nameIt != _objectNames.end(); ++nameIt)
483 installInGroup(*nameIt, group, animationGroup);
487 SGAnimation::installInGroup(const std::string& name, osg::Group& group,
488 osg::ref_ptr<osg::Group>& animationGroup)
490 int i = group.getNumChildren() - 1;
491 for (; 0 <= i; --i) {
492 osg::Node* child = group.getChild(i);
494 // Check if this one is already processed
495 if (std::find(_installedAnimations.begin(),
496 _installedAnimations.end(), child)
497 != _installedAnimations.end())
500 if (name.empty() || child->getName() == name) {
501 // fire the installation of the animation
504 // create a group node on demand
505 if (!animationGroup.valid()) {
506 animationGroup = createAnimationGroup(group);
507 // Animation type that does not require a new group,
508 // in this case we can stop and look for the next object
509 if (animationGroup.valid() && !_name.empty())
510 animationGroup->setName(_name);
512 if (animationGroup.valid()) {
513 animationGroup->addChild(child);
514 group.removeChild(i);
517 // store that we already have processed this child node
518 // We can hit this one twice if an animation references some
519 // part of a subtree twice
520 _installedAnimations.push_back(child);
526 SGAnimation::removeMode(osg::Node& node, osg::StateAttribute::GLMode mode)
528 RemoveModeVisitor visitor(mode);
529 node.accept(visitor);
533 SGAnimation::removeAttribute(osg::Node& node, osg::StateAttribute::Type type)
535 RemoveAttributeVisitor visitor(type);
536 node.accept(visitor);
540 SGAnimation::removeTextureMode(osg::Node& node, unsigned unit,
541 osg::StateAttribute::GLMode mode)
543 RemoveTextureModeVisitor visitor(unit, mode);
544 node.accept(visitor);
548 SGAnimation::removeTextureAttribute(osg::Node& node, unsigned unit,
549 osg::StateAttribute::Type type)
551 RemoveTextureAttributeVisitor visitor(unit, type);
552 node.accept(visitor);
556 SGAnimation::setRenderBinToInherit(osg::Node& node)
558 BinToInheritVisitor visitor;
559 node.accept(visitor);
563 SGAnimation::cloneDrawables(osg::Node& node)
565 DrawableCloneVisitor visitor;
566 node.accept(visitor);
570 SGAnimation::getCondition() const
572 const SGPropertyNode* conditionNode = _configNode->getChild("condition");
575 return sgReadCondition(_modelRoot, conditionNode);
580 ////////////////////////////////////////////////////////////////////////
581 // Implementation of null animation
582 ////////////////////////////////////////////////////////////////////////
584 // Ok, that is to build a subgraph from different other
585 // graph nodes. I guess that this stems from the time where modellers
586 // could not build hierarchical trees ...
587 SGGroupAnimation::SGGroupAnimation(const SGPropertyNode* configNode,
588 SGPropertyNode* modelRoot):
589 SGAnimation(configNode, modelRoot)
594 SGGroupAnimation::createAnimationGroup(osg::Group& parent)
596 osg::Group* group = new osg::Group;
597 parent.addChild(group);
602 ////////////////////////////////////////////////////////////////////////
603 // Implementation of translate animation
604 ////////////////////////////////////////////////////////////////////////
606 class SGTranslateAnimation::UpdateCallback : public osg::NodeCallback {
608 UpdateCallback(SGCondition const* condition,
609 SGExpressiond const* animationValue) :
610 _condition(condition),
611 _animationValue(animationValue)
613 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
615 if (!_condition || _condition->test()) {
616 SGTranslateTransform* transform;
617 transform = static_cast<SGTranslateTransform*>(node);
618 transform->setValue(_animationValue->getValue());
623 SGSharedPtr<SGCondition const> _condition;
624 SGSharedPtr<SGExpressiond const> _animationValue;
627 SGTranslateAnimation::SGTranslateAnimation(const SGPropertyNode* configNode,
628 SGPropertyNode* modelRoot) :
629 SGAnimation(configNode, modelRoot)
631 _condition = getCondition();
632 SGSharedPtr<SGExpressiond> value;
633 value = read_value(configNode, modelRoot, "-m",
634 -SGLimitsd::max(), SGLimitsd::max());
635 _animationValue = value->simplify();
637 _initialValue = _animationValue->getValue();
641 _axis[0] = configNode->getDoubleValue("axis/x", 0);
642 _axis[1] = configNode->getDoubleValue("axis/y", 0);
643 _axis[2] = configNode->getDoubleValue("axis/z", 0);
644 if (8*SGLimitsd::min() < norm(_axis))
645 _axis = normalize(_axis);
649 SGTranslateAnimation::createAnimationGroup(osg::Group& parent)
651 SGTranslateTransform* transform = new SGTranslateTransform;
652 transform->setName("translate animation");
653 if (_animationValue && !_animationValue->isConst()) {
654 UpdateCallback* uc = new UpdateCallback(_condition, _animationValue);
655 transform->setUpdateCallback(uc);
657 transform->setAxis(_axis);
658 transform->setValue(_initialValue);
659 parent.addChild(transform);
664 ////////////////////////////////////////////////////////////////////////
665 // Implementation of rotate/spin animation
666 ////////////////////////////////////////////////////////////////////////
668 class SGRotateAnimation::UpdateCallback : public osg::NodeCallback {
670 UpdateCallback(SGCondition const* condition,
671 SGExpressiond const* animationValue) :
672 _condition(condition),
673 _animationValue(animationValue)
675 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
677 if (!_condition || _condition->test()) {
678 SGRotateTransform* transform;
679 transform = static_cast<SGRotateTransform*>(node);
680 transform->setAngleDeg(_animationValue->getValue());
685 SGSharedPtr<SGCondition const> _condition;
686 SGSharedPtr<SGExpressiond const> _animationValue;
689 class SGRotateAnimation::SpinUpdateCallback : public osg::NodeCallback {
691 SpinUpdateCallback(SGCondition const* condition,
692 SGExpressiond const* animationValue) :
693 _condition(condition),
694 _animationValue(animationValue),
697 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
699 if (!_condition || _condition->test()) {
700 SGRotateTransform* transform;
701 transform = static_cast<SGRotateTransform*>(node);
703 double t = nv->getFrameStamp()->getReferenceTime();
708 double velocity_rpms = _animationValue->getValue()/60;
709 double angle = transform->getAngleDeg();
710 angle += dt*velocity_rpms*360;
711 angle -= 360*floor(angle/360);
712 transform->setAngleDeg(angle);
717 SGSharedPtr<SGCondition const> _condition;
718 SGSharedPtr<SGExpressiond const> _animationValue;
722 SGRotateAnimation::SGRotateAnimation(const SGPropertyNode* configNode,
723 SGPropertyNode* modelRoot) :
724 SGAnimation(configNode, modelRoot)
726 std::string type = configNode->getStringValue("type", "");
727 _isSpin = (type == "spin");
729 _condition = getCondition();
730 SGSharedPtr<SGExpressiond> value;
731 value = read_value(configNode, modelRoot, "-deg",
732 -SGLimitsd::max(), SGLimitsd::max());
733 _animationValue = value->simplify();
735 _initialValue = _animationValue->getValue();
739 _center = SGVec3d::zeros();
740 if (configNode->hasValue("axis/x1-m")) {
742 v1[0] = configNode->getDoubleValue("axis/x1-m", 0);
743 v1[1] = configNode->getDoubleValue("axis/y1-m", 0);
744 v1[2] = configNode->getDoubleValue("axis/z1-m", 0);
745 v2[0] = configNode->getDoubleValue("axis/x2-m", 0);
746 v2[1] = configNode->getDoubleValue("axis/y2-m", 0);
747 v2[2] = configNode->getDoubleValue("axis/z2-m", 0);
748 _center = 0.5*(v1+v2);
751 _axis[0] = configNode->getDoubleValue("axis/x", 0);
752 _axis[1] = configNode->getDoubleValue("axis/y", 0);
753 _axis[2] = configNode->getDoubleValue("axis/z", 0);
755 if (8*SGLimitsd::min() < norm(_axis))
756 _axis = normalize(_axis);
758 _center[0] = configNode->getDoubleValue("center/x-m", _center[0]);
759 _center[1] = configNode->getDoubleValue("center/y-m", _center[1]);
760 _center[2] = configNode->getDoubleValue("center/z-m", _center[2]);
764 SGRotateAnimation::createAnimationGroup(osg::Group& parent)
766 SGRotateTransform* transform = new SGRotateTransform;
767 transform->setName("rotate animation");
769 SpinUpdateCallback* uc;
770 uc = new SpinUpdateCallback(_condition, _animationValue);
771 transform->setUpdateCallback(uc);
772 } else if (_animationValue || !_animationValue->isConst()) {
773 UpdateCallback* uc = new UpdateCallback(_condition, _animationValue);
774 transform->setUpdateCallback(uc);
776 transform->setCenter(_center);
777 transform->setAxis(_axis);
778 transform->setAngleDeg(_initialValue);
779 parent.addChild(transform);
784 ////////////////////////////////////////////////////////////////////////
785 // Implementation of scale animation
786 ////////////////////////////////////////////////////////////////////////
788 class SGScaleAnimation::UpdateCallback : public osg::NodeCallback {
790 UpdateCallback(const SGCondition* condition,
791 SGSharedPtr<const SGExpressiond> animationValue[3]) :
792 _condition(condition)
794 _animationValue[0] = animationValue[0];
795 _animationValue[1] = animationValue[1];
796 _animationValue[2] = animationValue[2];
798 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
800 if (!_condition || _condition->test()) {
801 SGScaleTransform* transform;
802 transform = static_cast<SGScaleTransform*>(node);
803 SGVec3d scale(_animationValue[0]->getValue(),
804 _animationValue[1]->getValue(),
805 _animationValue[2]->getValue());
806 transform->setScaleFactor(scale);
811 SGSharedPtr<SGCondition const> _condition;
812 SGSharedPtr<SGExpressiond const> _animationValue[3];
815 SGScaleAnimation::SGScaleAnimation(const SGPropertyNode* configNode,
816 SGPropertyNode* modelRoot) :
817 SGAnimation(configNode, modelRoot)
819 _condition = getCondition();
821 // default offset/factor for all directions
822 double offset = configNode->getDoubleValue("offset", 0);
823 double factor = configNode->getDoubleValue("factor", 1);
825 SGSharedPtr<SGExpressiond> inPropExpr;
827 std::string inputPropertyName;
828 inputPropertyName = configNode->getStringValue("property", "");
829 if (inputPropertyName.empty()) {
830 inPropExpr = new SGConstExpression<double>(0);
832 SGPropertyNode* inputProperty;
833 inputProperty = modelRoot->getNode(inputPropertyName, true);
834 inPropExpr = new SGPropertyExpression<double>(inputProperty);
837 SGInterpTable* interpTable = read_interpolation_table(configNode);
839 SGSharedPtr<SGExpressiond> value;
840 value = new SGInterpTableExpression<double>(inPropExpr, interpTable);
841 _animationValue[0] = value->simplify();
842 _animationValue[1] = value->simplify();
843 _animationValue[2] = value->simplify();
844 } else if (configNode->getBoolValue("use-personality", false)) {
845 SGSharedPtr<SGExpressiond> value;
846 value = new SGPersonalityScaleOffsetExpression(inPropExpr, configNode,
847 "x-factor", "x-offset",
849 double minClip = configNode->getDoubleValue("x-min", 0);
850 double maxClip = configNode->getDoubleValue("x-max", SGLimitsd::max());
851 value = new SGClipExpression<double>(value, minClip, maxClip);
852 _animationValue[0] = value->simplify();
854 value = new SGPersonalityScaleOffsetExpression(inPropExpr, configNode,
855 "y-factor", "y-offset",
857 minClip = configNode->getDoubleValue("y-min", 0);
858 maxClip = configNode->getDoubleValue("y-max", SGLimitsd::max());
859 value = new SGClipExpression<double>(value, minClip, maxClip);
860 _animationValue[1] = value->simplify();
862 value = new SGPersonalityScaleOffsetExpression(inPropExpr, configNode,
863 "z-factor", "z-offset",
865 minClip = configNode->getDoubleValue("z-min", 0);
866 maxClip = configNode->getDoubleValue("z-max", SGLimitsd::max());
867 value = new SGClipExpression<double>(value, minClip, maxClip);
868 _animationValue[2] = value->simplify();
870 SGSharedPtr<SGExpressiond> value;
871 value = read_factor_offset(configNode, inPropExpr, "x-factor", "x-offset");
872 double minClip = configNode->getDoubleValue("x-min", 0);
873 double maxClip = configNode->getDoubleValue("x-max", SGLimitsd::max());
874 value = new SGClipExpression<double>(value, minClip, maxClip);
875 _animationValue[0] = value->simplify();
877 value = read_factor_offset(configNode, inPropExpr, "y-factor", "y-offset");
878 minClip = configNode->getDoubleValue("y-min", 0);
879 maxClip = configNode->getDoubleValue("y-max", SGLimitsd::max());
880 value = new SGClipExpression<double>(value, minClip, maxClip);
881 _animationValue[1] = value->simplify();
883 value = read_factor_offset(configNode, inPropExpr, "z-factor", "z-offset");
884 minClip = configNode->getDoubleValue("z-min", 0);
885 maxClip = configNode->getDoubleValue("z-max", SGLimitsd::max());
886 value = new SGClipExpression<double>(value, minClip, maxClip);
887 _animationValue[2] = value->simplify();
889 _initialValue[0] = configNode->getDoubleValue("x-starting-scale", 1);
890 _initialValue[0] *= configNode->getDoubleValue("x-factor", factor);
891 _initialValue[0] += configNode->getDoubleValue("x-offset", offset);
892 _initialValue[1] = configNode->getDoubleValue("y-starting-scale", 1);
893 _initialValue[1] *= configNode->getDoubleValue("y-factor", factor);
894 _initialValue[1] += configNode->getDoubleValue("y-offset", offset);
895 _initialValue[2] = configNode->getDoubleValue("z-starting-scale", 1);
896 _initialValue[2] *= configNode->getDoubleValue("z-factor", factor);
897 _initialValue[2] += configNode->getDoubleValue("z-offset", offset);
898 _center[0] = configNode->getDoubleValue("center/x-m", 0);
899 _center[1] = configNode->getDoubleValue("center/y-m", 0);
900 _center[2] = configNode->getDoubleValue("center/z-m", 0);
904 SGScaleAnimation::createAnimationGroup(osg::Group& parent)
906 SGScaleTransform* transform = new SGScaleTransform;
907 transform->setName("scale animation");
908 transform->setCenter(_center);
909 transform->setScaleFactor(_initialValue);
910 UpdateCallback* uc = new UpdateCallback(_condition, _animationValue);
911 transform->setUpdateCallback(uc);
912 parent.addChild(transform);
917 // Don't create a new state state everytime we need GL_NORMALIZE!
921 OpenThreads::Mutex normalizeMutex;
923 osg::StateSet* getNormalizeStateSet()
925 static osg::ref_ptr<osg::StateSet> normalizeStateSet;
926 OpenThreads::ScopedLock<OpenThreads::Mutex> lock(normalizeMutex);
927 if (!normalizeStateSet.valid()) {
928 normalizeStateSet = new osg::StateSet;
929 normalizeStateSet->setMode(GL_NORMALIZE, osg::StateAttribute::ON);
930 normalizeStateSet->setDataVariance(osg::Object::STATIC);
932 return normalizeStateSet.get();
936 ////////////////////////////////////////////////////////////////////////
937 // Implementation of dist scale animation
938 ////////////////////////////////////////////////////////////////////////
940 class SGDistScaleAnimation::Transform : public osg::Transform {
942 Transform(const SGPropertyNode* configNode)
944 setName(configNode->getStringValue("name", "dist scale animation"));
945 setReferenceFrame(RELATIVE_RF);
946 setStateSet(getNormalizeStateSet());
947 _factor = configNode->getFloatValue("factor", 1);
948 _offset = configNode->getFloatValue("offset", 0);
949 _min_v = configNode->getFloatValue("min", SGLimitsf::epsilon());
950 _max_v = configNode->getFloatValue("max", SGLimitsf::max());
951 _table = read_interpolation_table(configNode);
952 _center[0] = configNode->getFloatValue("center/x-m", 0);
953 _center[1] = configNode->getFloatValue("center/y-m", 0);
954 _center[2] = configNode->getFloatValue("center/z-m", 0);
956 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
957 osg::NodeVisitor* nv) const
959 osg::Matrix transform;
960 double scale_factor = computeScaleFactor(nv);
961 transform(0,0) = scale_factor;
962 transform(1,1) = scale_factor;
963 transform(2,2) = scale_factor;
964 transform(3,0) = _center[0]*(1 - scale_factor);
965 transform(3,1) = _center[1]*(1 - scale_factor);
966 transform(3,2) = _center[2]*(1 - scale_factor);
967 matrix.preMult(transform);
971 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
972 osg::NodeVisitor* nv) const
974 double scale_factor = computeScaleFactor(nv);
975 if (fabs(scale_factor) <= SGLimits<double>::min())
977 osg::Matrix transform;
978 double rScaleFactor = 1/scale_factor;
979 transform(0,0) = rScaleFactor;
980 transform(1,1) = rScaleFactor;
981 transform(2,2) = rScaleFactor;
982 transform(3,0) = _center[0]*(1 - rScaleFactor);
983 transform(3,1) = _center[1]*(1 - rScaleFactor);
984 transform(3,2) = _center[2]*(1 - rScaleFactor);
985 matrix.postMult(transform);
990 double computeScaleFactor(osg::NodeVisitor* nv) const
995 double scale_factor = (_center.osg() - nv->getEyePoint()).length();
997 scale_factor = _factor * scale_factor + _offset;
999 scale_factor = _table->interpolate( scale_factor );
1001 if (scale_factor < _min_v)
1002 scale_factor = _min_v;
1003 if (scale_factor > _max_v)
1004 scale_factor = _max_v;
1006 return scale_factor;
1009 SGSharedPtr<SGInterpTable> _table;
1018 SGDistScaleAnimation::SGDistScaleAnimation(const SGPropertyNode* configNode,
1019 SGPropertyNode* modelRoot) :
1020 SGAnimation(configNode, modelRoot)
1025 SGDistScaleAnimation::createAnimationGroup(osg::Group& parent)
1027 Transform* transform = new Transform(getConfig());
1028 parent.addChild(transform);
1033 ////////////////////////////////////////////////////////////////////////
1034 // Implementation of flash animation
1035 ////////////////////////////////////////////////////////////////////////
1037 class SGFlashAnimation::Transform : public osg::Transform {
1039 Transform(const SGPropertyNode* configNode)
1041 setReferenceFrame(RELATIVE_RF);
1042 setName(configNode->getStringValue("name", "flash animation"));
1043 setStateSet(getNormalizeStateSet());
1045 _axis[0] = configNode->getFloatValue("axis/x", 0);
1046 _axis[1] = configNode->getFloatValue("axis/y", 0);
1047 _axis[2] = configNode->getFloatValue("axis/z", 1);
1050 _center[0] = configNode->getFloatValue("center/x-m", 0);
1051 _center[1] = configNode->getFloatValue("center/y-m", 0);
1052 _center[2] = configNode->getFloatValue("center/z-m", 0);
1054 _offset = configNode->getFloatValue("offset", 0);
1055 _factor = configNode->getFloatValue("factor", 1);
1056 _power = configNode->getFloatValue("power", 1);
1057 _two_sides = configNode->getBoolValue("two-sides", false);
1059 _min_v = configNode->getFloatValue("min", SGLimitsf::epsilon());
1060 _max_v = configNode->getFloatValue("max", 1);
1062 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
1063 osg::NodeVisitor* nv) const
1065 osg::Matrix transform;
1066 double scale_factor = computeScaleFactor(nv);
1067 transform(0,0) = scale_factor;
1068 transform(1,1) = scale_factor;
1069 transform(2,2) = scale_factor;
1070 transform(3,0) = _center[0]*(1 - scale_factor);
1071 transform(3,1) = _center[1]*(1 - scale_factor);
1072 transform(3,2) = _center[2]*(1 - scale_factor);
1073 matrix.preMult(transform);
1077 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
1078 osg::NodeVisitor* nv) const
1080 double scale_factor = computeScaleFactor(nv);
1081 if (fabs(scale_factor) <= SGLimits<double>::min())
1083 osg::Matrix transform;
1084 double rScaleFactor = 1/scale_factor;
1085 transform(0,0) = rScaleFactor;
1086 transform(1,1) = rScaleFactor;
1087 transform(2,2) = rScaleFactor;
1088 transform(3,0) = _center[0]*(1 - rScaleFactor);
1089 transform(3,1) = _center[1]*(1 - rScaleFactor);
1090 transform(3,2) = _center[2]*(1 - rScaleFactor);
1091 matrix.postMult(transform);
1096 double computeScaleFactor(osg::NodeVisitor* nv) const
1101 osg::Vec3 localEyeToCenter = nv->getEyePoint() - _center;
1102 localEyeToCenter.normalize();
1104 double cos_angle = localEyeToCenter*_axis;
1105 double scale_factor = 0;
1106 if ( _two_sides && cos_angle < 0 )
1107 scale_factor = _factor * pow( -cos_angle, _power ) + _offset;
1108 else if ( cos_angle > 0 )
1109 scale_factor = _factor * pow( cos_angle, _power ) + _offset;
1111 if ( scale_factor < _min_v )
1112 scale_factor = _min_v;
1113 if ( scale_factor > _max_v )
1114 scale_factor = _max_v;
1116 return scale_factor;
1119 virtual osg::BoundingSphere computeBound() const
1121 // avoid being culled away by small feature culling
1122 osg::BoundingSphere bs = osg::Group::computeBound();
1123 bs.radius() *= _max_v;
1130 double _power, _factor, _offset, _min_v, _max_v;
1135 SGFlashAnimation::SGFlashAnimation(const SGPropertyNode* configNode,
1136 SGPropertyNode* modelRoot) :
1137 SGAnimation(configNode, modelRoot)
1142 SGFlashAnimation::createAnimationGroup(osg::Group& parent)
1144 Transform* transform = new Transform(getConfig());
1145 parent.addChild(transform);
1150 ////////////////////////////////////////////////////////////////////////
1151 // Implementation of flash animation
1152 ////////////////////////////////////////////////////////////////////////
1154 class SGBillboardAnimation::Transform : public osg::Transform {
1156 Transform(const SGPropertyNode* configNode) :
1157 _spherical(configNode->getBoolValue("spherical", true))
1159 setReferenceFrame(RELATIVE_RF);
1160 setName(configNode->getStringValue("name", "billboard animation"));
1162 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
1163 osg::NodeVisitor* nv) const
1165 // More or less taken from plibs ssgCutout
1167 matrix(0,0) = 1; matrix(0,1) = 0; matrix(0,2) = 0;
1168 matrix(1,0) = 0; matrix(1,1) = 0; matrix(1,2) = -1;
1169 matrix(2,0) = 0; matrix(2,1) = 1; matrix(2,2) = 0;
1171 osg::Vec3 zAxis(matrix(2, 0), matrix(2, 1), matrix(2, 2));
1172 osg::Vec3 xAxis = osg::Vec3(0, 0, -1)^zAxis;
1173 osg::Vec3 yAxis = zAxis^xAxis;
1179 matrix(0,0) = xAxis[0]; matrix(0,1) = xAxis[1]; matrix(0,2) = xAxis[2];
1180 matrix(1,0) = yAxis[0]; matrix(1,1) = yAxis[1]; matrix(1,2) = yAxis[2];
1181 matrix(2,0) = zAxis[0]; matrix(2,1) = zAxis[1]; matrix(2,2) = zAxis[2];
1186 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
1187 osg::NodeVisitor* nv) const
1189 // Hmm, don't yet know how to get that back ...
1198 SGBillboardAnimation::SGBillboardAnimation(const SGPropertyNode* configNode,
1199 SGPropertyNode* modelRoot) :
1200 SGAnimation(configNode, modelRoot)
1205 SGBillboardAnimation::createAnimationGroup(osg::Group& parent)
1207 Transform* transform = new Transform(getConfig());
1208 parent.addChild(transform);
1213 ////////////////////////////////////////////////////////////////////////
1214 // Implementation of a range animation
1215 ////////////////////////////////////////////////////////////////////////
1217 class SGRangeAnimation::UpdateCallback : public osg::NodeCallback {
1219 UpdateCallback(const SGCondition* condition,
1220 const SGExpressiond* minAnimationValue,
1221 const SGExpressiond* maxAnimationValue,
1222 double minValue, double maxValue) :
1223 _condition(condition),
1224 _minAnimationValue(minAnimationValue),
1225 _maxAnimationValue(maxAnimationValue),
1226 _minStaticValue(minValue),
1227 _maxStaticValue(maxValue)
1229 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1231 osg::LOD* lod = static_cast<osg::LOD*>(node);
1232 if (!_condition || _condition->test()) {
1234 if (_minAnimationValue)
1235 minRange = _minAnimationValue->getValue();
1237 minRange = _minStaticValue;
1239 if (_maxAnimationValue)
1240 maxRange = _maxAnimationValue->getValue();
1242 maxRange = _maxStaticValue;
1243 lod->setRange(0, minRange, maxRange);
1245 lod->setRange(0, 0, SGLimitsf::max());
1251 SGSharedPtr<const SGCondition> _condition;
1252 SGSharedPtr<const SGExpressiond> _minAnimationValue;
1253 SGSharedPtr<const SGExpressiond> _maxAnimationValue;
1254 double _minStaticValue;
1255 double _maxStaticValue;
1258 SGRangeAnimation::SGRangeAnimation(const SGPropertyNode* configNode,
1259 SGPropertyNode* modelRoot) :
1260 SGAnimation(configNode, modelRoot)
1262 _condition = getCondition();
1264 std::string inputPropertyName;
1265 inputPropertyName = configNode->getStringValue("min-property", "");
1266 if (!inputPropertyName.empty()) {
1267 SGPropertyNode* inputProperty;
1268 inputProperty = modelRoot->getNode(inputPropertyName, true);
1269 SGSharedPtr<SGExpressiond> value;
1270 value = new SGPropertyExpression<double>(inputProperty);
1272 value = read_factor_offset(configNode, value, "min-factor", "min-offset");
1273 _minAnimationValue = value->simplify();
1275 inputPropertyName = configNode->getStringValue("max-property", "");
1276 if (!inputPropertyName.empty()) {
1277 SGPropertyNode* inputProperty;
1278 inputProperty = modelRoot->getNode(inputPropertyName.c_str(), true);
1280 SGSharedPtr<SGExpressiond> value;
1281 value = new SGPropertyExpression<double>(inputProperty);
1283 value = read_factor_offset(configNode, value, "max-factor", "max-offset");
1284 _maxAnimationValue = value->simplify();
1287 _initialValue[0] = configNode->getDoubleValue("min-m", 0);
1288 _initialValue[0] *= configNode->getDoubleValue("min-factor", 1);
1289 _initialValue[1] = configNode->getDoubleValue("max-m", SGLimitsf::max());
1290 _initialValue[1] *= configNode->getDoubleValue("max-factor", 1);
1294 SGRangeAnimation::createAnimationGroup(osg::Group& parent)
1296 osg::Group* group = new osg::Group;
1297 group->setName("range animation group");
1299 osg::LOD* lod = new osg::LOD;
1300 lod->setName("range animation node");
1301 parent.addChild(lod);
1303 lod->addChild(group, _initialValue[0], _initialValue[1]);
1304 lod->setCenterMode(osg::LOD::USE_BOUNDING_SPHERE_CENTER);
1305 lod->setRangeMode(osg::LOD::DISTANCE_FROM_EYE_POINT);
1306 if (_minAnimationValue || _maxAnimationValue || _condition) {
1308 uc = new UpdateCallback(_condition, _minAnimationValue, _maxAnimationValue,
1309 _initialValue[0], _initialValue[1]);
1310 lod->setUpdateCallback(uc);
1316 ////////////////////////////////////////////////////////////////////////
1317 // Implementation of a select animation
1318 ////////////////////////////////////////////////////////////////////////
1320 class SGSelectAnimation::UpdateCallback : public osg::NodeCallback {
1322 UpdateCallback(const SGCondition* condition) :
1323 _condition(condition)
1325 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1327 osg::Switch* sw = static_cast<osg::Switch*>(node);
1328 if (_condition->test())
1329 sw->setAllChildrenOn();
1331 sw->setAllChildrenOff();
1336 SGSharedPtr<SGCondition const> _condition;
1339 SGSelectAnimation::SGSelectAnimation(const SGPropertyNode* configNode,
1340 SGPropertyNode* modelRoot) :
1341 SGAnimation(configNode, modelRoot)
1346 SGSelectAnimation::createAnimationGroup(osg::Group& parent)
1348 // if no condition given, this is a noop.
1349 SGSharedPtr<SGCondition const> condition = getCondition();
1350 // trick, gets deleted with all its 'animated' children
1351 // when the animation installer returns
1353 return new osg::Group;
1355 osg::Switch* sw = new osg::Switch;
1356 sw->setName("select animation node");
1357 sw->setUpdateCallback(new UpdateCallback(condition));
1358 parent.addChild(sw);
1364 ////////////////////////////////////////////////////////////////////////
1365 // Implementation of alpha test animation
1366 ////////////////////////////////////////////////////////////////////////
1368 SGAlphaTestAnimation::SGAlphaTestAnimation(const SGPropertyNode* configNode,
1369 SGPropertyNode* modelRoot) :
1370 SGAnimation(configNode, modelRoot)
1376 // Keep one copy of the most common alpha test its state set.
1377 OpenThreads::ReentrantMutex alphaTestMutex;
1378 osg::ref_ptr<osg::AlphaFunc> standardAlphaFunc;
1379 osg::ref_ptr<osg::StateSet> alphaFuncStateSet;
1381 osg::AlphaFunc* makeAlphaFunc(float clamp)
1383 using namespace OpenThreads;
1384 ScopedLock<ReentrantMutex> lock(alphaTestMutex);
1385 if (osg::equivalent(clamp, 0.01f)) {
1386 if (standardAlphaFunc.valid())
1387 return standardAlphaFunc.get();
1390 osg::AlphaFunc* alphaFunc = new osg::AlphaFunc;
1391 alphaFunc->setFunction(osg::AlphaFunc::GREATER);
1392 alphaFunc->setReferenceValue(clamp);
1393 alphaFunc->setDataVariance(osg::Object::STATIC);
1394 if (osg::equivalent(clamp, 0.01f))
1395 standardAlphaFunc = alphaFunc;
1399 osg::StateSet* makeAlphaTestStateSet(float clamp)
1401 using namespace OpenThreads;
1402 ScopedLock<ReentrantMutex> lock(alphaTestMutex);
1403 if (osg::equivalent(clamp, 0.01f)) {
1404 if (alphaFuncStateSet.valid())
1405 return alphaFuncStateSet.get();
1407 osg::AlphaFunc* alphaFunc = makeAlphaFunc(clamp);
1408 osg::StateSet* stateSet = new osg::StateSet;
1409 stateSet->setAttributeAndModes(alphaFunc,
1410 (osg::StateAttribute::ON
1411 | osg::StateAttribute::OVERRIDE));
1412 stateSet->setDataVariance(osg::Object::STATIC);
1413 if (osg::equivalent(clamp, 0.01f))
1414 alphaFuncStateSet = stateSet;
1419 SGAlphaTestAnimation::install(osg::Node& node)
1421 SGAnimation::install(node);
1423 float alphaClamp = getConfig()->getFloatValue("alpha-factor", 0);
1424 osg::StateSet* stateSet = node.getStateSet();
1426 node.setStateSet(makeAlphaTestStateSet(alphaClamp));
1428 stateSet->setAttributeAndModes(makeAlphaFunc(alphaClamp),
1429 (osg::StateAttribute::ON
1430 | osg::StateAttribute::OVERRIDE));
1435 //////////////////////////////////////////////////////////////////////
1436 // Blend animation installer
1437 //////////////////////////////////////////////////////////////////////
1439 // XXX This needs to be replaced by something using TexEnvCombine to
1440 // change the blend factor. Changing the alpha values in the geometry
1442 class SGBlendAnimation::BlendVisitor : public osg::NodeVisitor {
1444 BlendVisitor(float blend) :
1445 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
1447 { setVisitorType(osg::NodeVisitor::NODE_VISITOR); }
1448 virtual void apply(osg::Node& node)
1450 updateStateSet(node.getStateSet());
1453 virtual void apply(osg::Geode& node)
1455 apply((osg::Node&)node);
1456 unsigned nDrawables = node.getNumDrawables();
1457 for (unsigned i = 0; i < nDrawables; ++i) {
1458 osg::Drawable* drawable = node.getDrawable(i);
1459 osg::Geometry* geometry = drawable->asGeometry();
1462 osg::Array* array = geometry->getColorArray();
1465 osg::Vec4Array* vec4Array = dynamic_cast<osg::Vec4Array*>(array);
1468 for (unsigned k = 0; k < vec4Array->size(); ++k) {
1469 (*vec4Array)[k][3] = _blend;
1472 updateStateSet(drawable->getStateSet());
1475 void updateStateSet(osg::StateSet* stateSet)
1479 osg::StateAttribute* stateAttribute;
1480 stateAttribute = stateSet->getAttribute(osg::StateAttribute::MATERIAL);
1481 if (!stateAttribute)
1483 osg::Material* material = dynamic_cast<osg::Material*>(stateAttribute);
1486 material->setAlpha(osg::Material::FRONT_AND_BACK, _blend);
1488 stateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
1489 stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
1491 stateSet->setRenderingHint(osg::StateSet::DEFAULT_BIN);
1498 class SGBlendAnimation::UpdateCallback : public osg::NodeCallback {
1500 UpdateCallback(const SGPropertyNode* configNode, const SGExpressiond* v) :
1504 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1506 double blend = _animationValue->getValue();
1507 if (blend != _prev_value) {
1508 _prev_value = blend;
1509 BlendVisitor visitor(1-blend);
1510 node->accept(visitor);
1516 SGSharedPtr<SGExpressiond const> _animationValue;
1520 SGBlendAnimation::SGBlendAnimation(const SGPropertyNode* configNode,
1521 SGPropertyNode* modelRoot)
1522 : SGAnimation(configNode, modelRoot),
1523 _animationValue(read_value(configNode, modelRoot, "", 0, 1))
1528 SGBlendAnimation::createAnimationGroup(osg::Group& parent)
1530 if (!_animationValue)
1533 osg::Group* group = new osg::Switch;
1534 group->setName("blend animation node");
1535 group->setUpdateCallback(new UpdateCallback(getConfig(), _animationValue));
1536 parent.addChild(group);
1541 SGBlendAnimation::install(osg::Node& node)
1543 SGAnimation::install(node);
1544 // make sure we do not change common geometries,
1545 // that also creates new display lists for these subgeometries.
1546 cloneDrawables(node);
1547 DoDrawArraysVisitor visitor;
1548 node.accept(visitor);
1552 //////////////////////////////////////////////////////////////////////
1553 // Timed animation installer
1554 //////////////////////////////////////////////////////////////////////
1558 class SGTimedAnimation::UpdateCallback : public osg::NodeCallback {
1560 UpdateCallback(const SGPropertyNode* configNode) :
1563 _duration_sec(configNode->getDoubleValue("duration-sec", 1)),
1564 _last_time_sec(SGLimitsd::max()),
1565 _use_personality(configNode->getBoolValue("use-personality", false))
1567 std::vector<SGSharedPtr<SGPropertyNode> > nodes;
1568 nodes = configNode->getChildren("branch-duration-sec");
1569 for (size_t i = 0; i < nodes.size(); ++i) {
1570 unsigned ind = nodes[ i ]->getIndex();
1571 while ( ind >= _durations.size() ) {
1572 _durations.push_back(DurationSpec(_duration_sec));
1574 SGPropertyNode_ptr rNode = nodes[i]->getChild("random");
1576 _durations[ind] = DurationSpec(nodes[ i ]->getDoubleValue());
1578 _durations[ind] = DurationSpec(rNode->getDoubleValue( "min", 0),
1579 rNode->getDoubleValue( "max", 1));
1583 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1585 assert(dynamic_cast<osg::Switch*>(node));
1586 osg::Switch* sw = static_cast<osg::Switch*>(node);
1588 unsigned nChildren = sw->getNumChildren();
1590 // blow up the durations vector to the required size
1591 while (_durations.size() < nChildren) {
1592 _durations.push_back(_duration_sec);
1594 // make sure the current index is an duration that really exists
1595 _current_index = _current_index % nChildren;
1597 // update the time and compute the current systems time value
1598 double t = nv->getFrameStamp()->getReferenceTime();
1599 if (_last_time_sec == SGLimitsd::max()) {
1602 double dt = t - _last_time_sec;
1603 if (_use_personality)
1604 dt *= 1 + 0.2*(0.5 - sg_random());
1609 double currentDuration = _durations[_current_index].get();
1610 while (currentDuration < _reminder) {
1611 _reminder -= currentDuration;
1612 _current_index = (_current_index + 1) % nChildren;
1613 currentDuration = _durations[_current_index].get();
1616 sw->setSingleChildOn(_current_index);
1622 struct DurationSpec {
1623 DurationSpec(double t) :
1624 minTime(SGMiscd::max(0.01, t)),
1625 maxTime(SGMiscd::max(0.01, t))
1627 DurationSpec(double t0, double t1) :
1628 minTime(SGMiscd::max(0.01, t0)),
1629 maxTime(SGMiscd::max(0.01, t1))
1632 { return minTime + sg_random()*(maxTime - minTime); }
1636 std::vector<DurationSpec> _durations;
1637 unsigned _current_index;
1639 double _duration_sec;
1640 double _last_time_sec;
1641 bool _use_personality;
1645 SGTimedAnimation::SGTimedAnimation(const SGPropertyNode* configNode,
1646 SGPropertyNode* modelRoot)
1647 : SGAnimation(configNode, modelRoot)
1652 SGTimedAnimation::createAnimationGroup(osg::Group& parent)
1654 osg::Switch* sw = new osg::Switch;
1655 sw->setName("timed animation node");
1656 sw->setUpdateCallback(new UpdateCallback(getConfig()));
1657 parent.addChild(sw);
1662 ////////////////////////////////////////////////////////////////////////
1663 // dynamically switch on/off shadows
1664 ////////////////////////////////////////////////////////////////////////
1666 class SGShadowAnimation::UpdateCallback : public osg::NodeCallback {
1668 UpdateCallback(const SGCondition* condition) :
1669 _condition(condition)
1671 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1673 if (_condition->test())
1674 node->setNodeMask( SG_NODEMASK_CASTSHADOW_BIT | node->getNodeMask());
1676 node->setNodeMask(~SG_NODEMASK_CASTSHADOW_BIT & node->getNodeMask());
1681 SGSharedPtr<const SGCondition> _condition;
1684 SGShadowAnimation::SGShadowAnimation(const SGPropertyNode* configNode,
1685 SGPropertyNode* modelRoot) :
1686 SGAnimation(configNode, modelRoot)
1691 SGShadowAnimation::createAnimationGroup(osg::Group& parent)
1693 SGSharedPtr<SGCondition const> condition = getCondition();
1697 osg::Group* group = new osg::Group;
1698 group->setName("shadow animation");
1699 group->setUpdateCallback(new UpdateCallback(condition));
1700 parent.addChild(group);
1705 ////////////////////////////////////////////////////////////////////////
1706 // Implementation of SGTexTransformAnimation
1707 ////////////////////////////////////////////////////////////////////////
1709 class SGTexTransformAnimation::Transform : public SGReferenced {
1714 virtual ~Transform()
1716 void setValue(double value)
1718 virtual void transform(osg::Matrix&) = 0;
1723 class SGTexTransformAnimation::Translation :
1724 public SGTexTransformAnimation::Transform {
1726 Translation(const SGVec3d& axis) :
1729 virtual void transform(osg::Matrix& matrix)
1732 set_translation(tmp, _value, _axis);
1733 matrix.preMult(tmp);
1739 class SGTexTransformAnimation::Rotation :
1740 public SGTexTransformAnimation::Transform {
1742 Rotation(const SGVec3d& axis, const SGVec3d& center) :
1746 virtual void transform(osg::Matrix& matrix)
1749 set_rotation(tmp, _value, _center, _axis);
1750 matrix.preMult(tmp);
1757 class SGTexTransformAnimation::UpdateCallback :
1758 public osg::StateAttribute::Callback {
1760 UpdateCallback(const SGCondition* condition) :
1761 _condition(condition)
1763 virtual void operator () (osg::StateAttribute* sa, osg::NodeVisitor*)
1765 if (!_condition || _condition->test()) {
1766 TransformList::const_iterator i;
1767 for (i = _transforms.begin(); i != _transforms.end(); ++i)
1768 i->transform->setValue(i->value->getValue());
1770 assert(dynamic_cast<osg::TexMat*>(sa));
1771 osg::TexMat* texMat = static_cast<osg::TexMat*>(sa);
1772 texMat->getMatrix().makeIdentity();
1773 TransformList::const_iterator i;
1774 for (i = _transforms.begin(); i != _transforms.end(); ++i)
1775 i->transform->transform(texMat->getMatrix());
1777 void appendTransform(Transform* transform, SGExpressiond* value)
1779 Entry entry = { transform, value };
1780 transform->transform(_matrix);
1781 _transforms.push_back(entry);
1786 SGSharedPtr<Transform> transform;
1787 SGSharedPtr<const SGExpressiond> value;
1789 typedef std::vector<Entry> TransformList;
1790 TransformList _transforms;
1791 SGSharedPtr<const SGCondition> _condition;
1792 osg::Matrix _matrix;
1795 SGTexTransformAnimation::SGTexTransformAnimation(const SGPropertyNode* configNode,
1796 SGPropertyNode* modelRoot) :
1797 SGAnimation(configNode, modelRoot)
1802 SGTexTransformAnimation::createAnimationGroup(osg::Group& parent)
1804 osg::Group* group = new osg::Group;
1805 group->setName("texture transform group");
1806 osg::StateSet* stateSet = group->getOrCreateStateSet();
1807 osg::TexMat* texMat = new osg::TexMat;
1808 UpdateCallback* updateCallback = new UpdateCallback(getCondition());
1809 // interpret the configs ...
1810 std::string type = getType();
1812 if (type == "textranslate") {
1813 appendTexTranslate(getConfig(), updateCallback);
1814 } else if (type == "texrotate") {
1815 appendTexRotate(getConfig(), updateCallback);
1816 } else if (type == "texmultiple") {
1817 std::vector<SGSharedPtr<SGPropertyNode> > transformConfigs;
1818 transformConfigs = getConfig()->getChildren("transform");
1819 for (unsigned i = 0; i < transformConfigs.size(); ++i) {
1820 std::string subtype = transformConfigs[i]->getStringValue("subtype", "");
1821 if (subtype == "textranslate")
1822 appendTexTranslate(transformConfigs[i], updateCallback);
1823 else if (subtype == "texrotate")
1824 appendTexRotate(transformConfigs[i], updateCallback);
1826 SG_LOG(SG_INPUT, SG_ALERT,
1827 "Ignoring unknown texture transform subtype");
1830 SG_LOG(SG_INPUT, SG_ALERT, "Ignoring unknown texture transform type");
1833 texMat->setUpdateCallback(updateCallback);
1834 stateSet->setTextureAttribute(0, texMat);
1835 parent.addChild(group);
1840 SGTexTransformAnimation::appendTexTranslate(const SGPropertyNode* config,
1841 UpdateCallback* updateCallback)
1843 std::string propertyName = config->getStringValue("property", "");
1844 SGSharedPtr<SGExpressiond> value;
1845 if (propertyName.empty())
1846 value = new SGConstExpression<double>(0);
1848 SGPropertyNode* inputProperty = getModelRoot()->getNode(propertyName, true);
1849 value = new SGPropertyExpression<double>(inputProperty);
1852 SGInterpTable* table = read_interpolation_table(config);
1854 value = new SGInterpTableExpression<double>(value, table);
1855 double biasValue = config->getDoubleValue("bias", 0);
1857 value = new SGBiasExpression<double>(value, biasValue);
1858 value = new SGStepExpression<double>(value,
1859 config->getDoubleValue("step", 0),
1860 config->getDoubleValue("scroll", 0));
1861 value = value->simplify();
1863 double biasValue = config->getDoubleValue("bias", 0);
1865 value = new SGBiasExpression<double>(value, biasValue);
1866 value = new SGStepExpression<double>(value,
1867 config->getDoubleValue("step", 0),
1868 config->getDoubleValue("scroll", 0));
1869 value = read_factor_offset(config, value, "factor", "offset");
1871 if (config->hasChild("min") || config->hasChild("max")) {
1872 double minClip = config->getDoubleValue("min", -SGLimitsd::max());
1873 double maxClip = config->getDoubleValue("max", SGLimitsd::max());
1874 value = new SGClipExpression<double>(value, minClip, maxClip);
1876 value = value->simplify();
1878 SGVec3d axis(config->getDoubleValue("axis/x", 0),
1879 config->getDoubleValue("axis/y", 0),
1880 config->getDoubleValue("axis/z", 0));
1881 Translation* translation;
1882 translation = new Translation(normalize(axis));
1883 translation->setValue(config->getDoubleValue("starting-position", 0));
1884 updateCallback->appendTransform(translation, value);
1888 SGTexTransformAnimation::appendTexRotate(const SGPropertyNode* config,
1889 UpdateCallback* updateCallback)
1891 std::string propertyName = config->getStringValue("property", "");
1892 SGSharedPtr<SGExpressiond> value;
1893 if (propertyName.empty())
1894 value = new SGConstExpression<double>(0);
1896 SGPropertyNode* inputProperty = getModelRoot()->getNode(propertyName, true);
1897 value = new SGPropertyExpression<double>(inputProperty);
1900 SGInterpTable* table = read_interpolation_table(config);
1902 value = new SGInterpTableExpression<double>(value, table);
1903 double biasValue = config->getDoubleValue("bias", 0);
1905 value = new SGBiasExpression<double>(value, biasValue);
1906 value = new SGStepExpression<double>(value,
1907 config->getDoubleValue("step", 0),
1908 config->getDoubleValue("scroll", 0));
1909 value = value->simplify();
1911 double biasValue = config->getDoubleValue("bias", 0);
1913 value = new SGBiasExpression<double>(value, biasValue);
1914 value = new SGStepExpression<double>(value,
1915 config->getDoubleValue("step", 0),
1916 config->getDoubleValue("scroll", 0));
1917 value = read_factor_offset(config, value, "factor", "offset-deg");
1919 if (config->hasChild("min-deg") || config->hasChild("max-deg")) {
1920 double minClip = config->getDoubleValue("min-deg", -SGLimitsd::max());
1921 double maxClip = config->getDoubleValue("max-deg", SGLimitsd::max());
1922 value = new SGClipExpression<double>(value, minClip, maxClip);
1924 value = value->simplify();
1926 SGVec3d axis(config->getDoubleValue("axis/x", 0),
1927 config->getDoubleValue("axis/y", 0),
1928 config->getDoubleValue("axis/z", 0));
1929 SGVec3d center(config->getDoubleValue("center/x", 0),
1930 config->getDoubleValue("center/y", 0),
1931 config->getDoubleValue("center/z", 0));
1933 rotation = new Rotation(normalize(axis), center);
1934 rotation->setValue(config->getDoubleValue("starting-position-deg", 0));
1935 updateCallback->appendTransform(rotation, value);
1939 ////////////////////////////////////////////////////////////////////////
1940 // Implementation of SGPickAnimation
1941 ////////////////////////////////////////////////////////////////////////
1943 class SGPickAnimation::PickCallback : public SGPickCallback {
1945 PickCallback(const SGPropertyNode* configNode,
1946 SGPropertyNode* modelRoot) :
1947 _button(configNode->getIntValue("button", -1)),
1948 _repeatable(configNode->getBoolValue("repeatable", false)),
1949 _repeatInterval(configNode->getDoubleValue("interval-sec", 0.1))
1951 SG_LOG(SG_INPUT, SG_DEBUG, "Reading all bindings");
1952 std::vector<SGPropertyNode_ptr> bindings;
1953 bindings = configNode->getChildren("binding");
1954 for (unsigned int i = 0; i < bindings.size(); ++i) {
1955 _bindingsDown.push_back(new SGBinding(bindings[i], modelRoot));
1958 const SGPropertyNode* upNode = configNode->getChild("mod-up");
1961 bindings = upNode->getChildren("binding");
1962 for (unsigned int i = 0; i < bindings.size(); ++i) {
1963 _bindingsUp.push_back(new SGBinding(bindings[i], modelRoot));
1966 virtual bool buttonPressed(int button, const Info&)
1968 if (0 <= _button && button != _button)
1970 SGBindingList::const_iterator i;
1971 for (i = _bindingsDown.begin(); i != _bindingsDown.end(); ++i)
1976 virtual void buttonReleased(void)
1978 SGBindingList::const_iterator i;
1979 for (i = _bindingsUp.begin(); i != _bindingsUp.end(); ++i)
1982 virtual void update(double dt)
1988 while (_repeatInterval < _repeatTime) {
1989 _repeatTime -= _repeatInterval;
1990 SGBindingList::const_iterator i;
1991 for (i = _bindingsDown.begin(); i != _bindingsDown.end(); ++i)
1996 SGBindingList _bindingsDown;
1997 SGBindingList _bindingsUp;
2000 double _repeatInterval;
2004 SGPickAnimation::SGPickAnimation(const SGPropertyNode* configNode,
2005 SGPropertyNode* modelRoot) :
2006 SGAnimation(configNode, modelRoot)
2011 SGPickAnimation::createAnimationGroup(osg::Group& parent)
2013 osg::Group* commonGroup = new osg::Group;
2015 // Contains the normal geometry that is interactive
2016 osg::ref_ptr<osg::Group> normalGroup = new osg::Group;
2017 normalGroup->addChild(commonGroup);
2019 // Used to render the geometry with just yellow edges
2020 osg::Group* highlightGroup = new osg::Group;
2021 highlightGroup->setNodeMask(SG_NODEMASK_PICK_BIT);
2022 highlightGroup->addChild(commonGroup);
2023 SGSceneUserData* ud;
2024 ud = SGSceneUserData::getOrCreateSceneUserData(highlightGroup);
2025 std::vector<SGPropertyNode_ptr> actions;
2026 actions = getConfig()->getChildren("action");
2027 for (unsigned int i = 0; i < actions.size(); ++i)
2028 ud->addPickCallback(new PickCallback(actions[i], getModelRoot()));
2030 // prepare a state set that paints the edges of this object yellow
2031 osg::StateSet* stateSet = highlightGroup->getOrCreateStateSet();
2032 stateSet->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE);
2034 osg::PolygonOffset* polygonOffset = new osg::PolygonOffset;
2035 polygonOffset->setFactor(-1);
2036 polygonOffset->setUnits(-1);
2037 stateSet->setAttribute(polygonOffset, osg::StateAttribute::OVERRIDE);
2038 stateSet->setMode(GL_POLYGON_OFFSET_LINE, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
2040 osg::PolygonMode* polygonMode = new osg::PolygonMode;
2041 polygonMode->setMode(osg::PolygonMode::FRONT_AND_BACK,
2042 osg::PolygonMode::LINE);
2043 stateSet->setAttribute(polygonMode, osg::StateAttribute::OVERRIDE);
2045 osg::Material* material = new osg::Material;
2046 material->setColorMode(osg::Material::OFF);
2047 material->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(1, 1, 0, 1));
2048 material->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(1, 1, 0, 1));
2049 material->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(1, 1, 0, 1));
2050 material->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0, 0, 0, 0));
2051 stateSet->setAttribute(material, osg::StateAttribute::OVERRIDE);
2053 // Only add normal geometry if configured
2054 if (getConfig()->getBoolValue("visible", true))
2055 parent.addChild(normalGroup.get());
2056 parent.addChild(highlightGroup);