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"
50 using OpenThreads::Mutex;
51 using OpenThreads::ReentrantMutex;
52 using OpenThreads::ScopedLock;
55 ////////////////////////////////////////////////////////////////////////
56 // Static utility functions.
57 ////////////////////////////////////////////////////////////////////////
60 * Set up the transform matrix for a spin or rotation.
63 set_rotation (osg::Matrix &matrix, double position_deg,
64 const SGVec3d ¢er, const SGVec3d &axis)
66 double temp_angle = -SGMiscd::deg2rad(position_deg);
68 double s = sin(temp_angle);
69 double c = cos(temp_angle);
72 // axis was normalized at load time
73 // hint to the compiler to put these into FP registers
78 matrix(0, 0) = t * x * x + c ;
79 matrix(0, 1) = t * y * x - s * z ;
80 matrix(0, 2) = t * z * x + s * y ;
83 matrix(1, 0) = t * x * y + s * z ;
84 matrix(1, 1) = t * y * y + c ;
85 matrix(1, 2) = t * z * y - s * x ;
88 matrix(2, 0) = t * x * z - s * y ;
89 matrix(2, 1) = t * y * z + s * x ;
90 matrix(2, 2) = t * z * z + c ;
93 // hint to the compiler to put these into FP registers
98 matrix(3, 0) = x - x*matrix(0, 0) - y*matrix(1, 0) - z*matrix(2, 0);
99 matrix(3, 1) = y - x*matrix(0, 1) - y*matrix(1, 1) - z*matrix(2, 1);
100 matrix(3, 2) = z - x*matrix(0, 2) - y*matrix(1, 2) - z*matrix(2, 2);
105 * Set up the transform matrix for a translation.
108 set_translation (osg::Matrix &matrix, double position_m, const SGVec3d &axis)
110 SGVec3d xyz = axis * position_m;
111 matrix.makeIdentity();
112 matrix(3, 0) = xyz[0];
113 matrix(3, 1) = xyz[1];
114 matrix(3, 2) = xyz[2];
118 * Read an interpolation table from properties.
120 static SGInterpTable *
121 read_interpolation_table(const SGPropertyNode* props)
123 const SGPropertyNode* table_node = props->getNode("interpolation");
126 return new SGInterpTable(table_node);
130 unit_string(const char* value, const char* unit)
132 return std::string(value) + unit;
135 class SGPersonalityScaleOffsetExpression : public SGUnaryExpression<double> {
137 SGPersonalityScaleOffsetExpression(SGExpression<double>* expr,
138 SGPropertyNode const* config,
139 const std::string& scalename,
140 const std::string& offsetname,
142 double defOffset = 0) :
143 SGUnaryExpression<double>(expr),
144 _scale(config, scalename.c_str(), defScale),
145 _offset(config, offsetname.c_str(), defOffset)
147 void setScale(double scale)
149 void setOffset(double offset)
150 { _offset = offset; }
152 virtual void eval(double& value) const
156 value = _offset + _scale*getOperand()->getValue();
159 virtual bool isConst() const { return false; }
162 mutable SGPersonalityParameter<double> _scale;
163 mutable SGPersonalityParameter<double> _offset;
167 static SGExpressiond*
168 read_factor_offset(const SGPropertyNode* configNode, SGExpressiond* expr,
169 const std::string& factor, const std::string& offset)
171 double factorValue = configNode->getDoubleValue(factor, 1);
172 if (factorValue != 1)
173 expr = new SGScaleExpression<double>(expr, factorValue);
174 double offsetValue = configNode->getDoubleValue(offset, 0);
175 if (offsetValue != 0)
176 expr = new SGBiasExpression<double>(expr, offsetValue);
180 static SGExpressiond*
181 read_offset_factor(const SGPropertyNode* configNode, SGExpressiond* expr,
182 const std::string& factor, const std::string& offset)
184 double offsetValue = configNode->getDoubleValue(offset, 0);
185 if (offsetValue != 0)
186 expr = new SGBiasExpression<double>(expr, offsetValue);
187 double factorValue = configNode->getDoubleValue(factor, 1);
188 if (factorValue != 1)
189 expr = new SGScaleExpression<double>(expr, factorValue);
194 read_value(const SGPropertyNode* configNode, SGPropertyNode* modelRoot,
195 const char* unit, double defMin, double defMax)
197 SGExpression<double>* value = 0;
199 std::string inputPropertyName = configNode->getStringValue("property", "");
200 if (inputPropertyName.empty()) {
201 std::string spos = unit_string("starting-position", unit);
202 double initPos = configNode->getDoubleValue(spos, 0);
203 value = new SGConstExpression<double>(initPos);
205 SGPropertyNode* inputProperty;
206 inputProperty = modelRoot->getNode(inputPropertyName, true);
207 value = new SGPropertyExpression<double>(inputProperty);
210 SGInterpTable* interpTable = read_interpolation_table(configNode);
212 return new SGInterpTableExpression<double>(value, interpTable);
214 std::string offset = unit_string("offset", unit);
215 std::string min = unit_string("min", unit);
216 std::string max = unit_string("max", unit);
218 if (configNode->getBoolValue("use-personality", false)) {
219 value = new SGPersonalityScaleOffsetExpression(value, configNode,
222 value = read_factor_offset(configNode, value, "factor", offset);
225 double minClip = configNode->getDoubleValue(min, defMin);
226 double maxClip = configNode->getDoubleValue(max, defMax);
227 if (minClip > SGMiscd::min(SGLimitsd::min(), -SGLimitsd::max()) ||
228 maxClip < SGLimitsd::max())
229 value = new SGClipExpression<double>(value, minClip, maxClip);
237 ////////////////////////////////////////////////////////////////////////
238 // Animation installer
239 ////////////////////////////////////////////////////////////////////////
241 class SGAnimation::RemoveModeVisitor : public SGStateAttributeVisitor {
243 RemoveModeVisitor(osg::StateAttribute::GLMode mode) :
246 virtual void apply(osg::StateSet* stateSet)
250 stateSet->removeMode(_mode);
253 osg::StateAttribute::GLMode _mode;
256 class SGAnimation::RemoveAttributeVisitor : public SGStateAttributeVisitor {
258 RemoveAttributeVisitor(osg::StateAttribute::Type type) :
261 virtual void apply(osg::StateSet* stateSet)
265 while (stateSet->getAttribute(_type)) {
266 stateSet->removeAttribute(_type);
270 osg::StateAttribute::Type _type;
273 class SGAnimation::RemoveTextureModeVisitor : public SGStateAttributeVisitor {
275 RemoveTextureModeVisitor(unsigned unit, osg::StateAttribute::GLMode mode) :
279 virtual void apply(osg::StateSet* stateSet)
283 stateSet->removeTextureMode(_unit, _mode);
287 osg::StateAttribute::GLMode _mode;
290 class SGAnimation::RemoveTextureAttributeVisitor :
291 public SGStateAttributeVisitor {
293 RemoveTextureAttributeVisitor(unsigned unit,
294 osg::StateAttribute::Type type) :
298 virtual void apply(osg::StateSet* stateSet)
302 while (stateSet->getTextureAttribute(_unit, _type)) {
303 stateSet->removeTextureAttribute(_unit, _type);
308 osg::StateAttribute::Type _type;
311 class SGAnimation::BinToInheritVisitor : public SGStateAttributeVisitor {
313 virtual void apply(osg::StateSet* stateSet)
317 stateSet->setRenderBinToInherit();
321 class SGAnimation::DrawableCloneVisitor : public osg::NodeVisitor {
323 DrawableCloneVisitor() :
324 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
326 void apply(osg::Geode& geode)
328 for (unsigned i = 0 ; i < geode.getNumDrawables(); ++i) {
329 osg::CopyOp copyOp(osg::CopyOp::DEEP_COPY_ALL &
330 ~osg::CopyOp::DEEP_COPY_TEXTURES);
331 geode.setDrawable(i, copyOp(geode.getDrawable(i)));
338 // Set all drawables to not use display lists. OSG will use
339 // glDrawArrays instead.
340 struct DoDrawArraysVisitor : public osg::NodeVisitor {
341 DoDrawArraysVisitor() :
342 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
344 void apply(osg::Geode& geode)
349 for (int i = 0; i < geode.getNumDrawables(); ++i)
350 geode.getDrawable(i)->setUseDisplayList(false);
355 SGAnimation::SGAnimation(const SGPropertyNode* configNode,
356 SGPropertyNode* modelRoot) :
357 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
359 _configNode(configNode),
360 _modelRoot(modelRoot)
362 _name = configNode->getStringValue("name", "");
363 _enableHOT = configNode->getBoolValue("enable-hot", true);
364 _disableShadow = configNode->getBoolValue("disable-shadow", false);
365 std::vector<SGPropertyNode_ptr> objectNames =
366 configNode->getChildren("object-name");
367 for (unsigned i = 0; i < objectNames.size(); ++i)
368 _objectNames.push_back(objectNames[i]->getStringValue());
371 SGAnimation::~SGAnimation()
376 SG_LOG(SG_IO, SG_ALERT, "Could not find at least one of the following"
377 " objects for animation:\n");
378 std::list<std::string>::const_iterator i;
379 for (i = _objectNames.begin(); i != _objectNames.end(); ++i)
380 SG_LOG(SG_IO, SG_ALERT, *i << "\n");
384 SGAnimation::animate(osg::Node* node, const SGPropertyNode* configNode,
385 SGPropertyNode* modelRoot,
386 const osgDB::ReaderWriter::Options* options)
388 std::string type = configNode->getStringValue("type", "none");
389 if (type == "alpha-test") {
390 SGAlphaTestAnimation animInst(configNode, modelRoot);
391 animInst.apply(node);
392 } else if (type == "billboard") {
393 SGBillboardAnimation animInst(configNode, modelRoot);
394 animInst.apply(node);
395 } else if (type == "blend") {
396 SGBlendAnimation animInst(configNode, modelRoot);
397 animInst.apply(node);
398 } else if (type == "dist-scale") {
399 SGDistScaleAnimation animInst(configNode, modelRoot);
400 animInst.apply(node);
401 } else if (type == "flash") {
402 SGFlashAnimation animInst(configNode, modelRoot);
403 animInst.apply(node);
404 } else if (type == "material") {
405 SGMaterialAnimation animInst(configNode, modelRoot, options);
406 animInst.apply(node);
407 } else if (type == "noshadow") {
408 SGShadowAnimation animInst(configNode, modelRoot);
409 animInst.apply(node);
410 } else if (type == "pick") {
411 SGPickAnimation animInst(configNode, modelRoot);
412 animInst.apply(node);
413 } else if (type == "range") {
414 SGRangeAnimation animInst(configNode, modelRoot);
415 animInst.apply(node);
416 } else if (type == "rotate" || type == "spin") {
417 SGRotateAnimation animInst(configNode, modelRoot);
418 animInst.apply(node);
419 } else if (type == "scale") {
420 SGScaleAnimation animInst(configNode, modelRoot);
421 animInst.apply(node);
422 } else if (type == "select") {
423 SGSelectAnimation animInst(configNode, modelRoot);
424 animInst.apply(node);
425 } else if (type == "shader") {
426 SGShaderAnimation animInst(configNode, modelRoot, options);
427 animInst.apply(node);
428 } else if (type == "textranslate" || type == "texrotate" ||
429 type == "texmultiple") {
430 SGTexTransformAnimation animInst(configNode, modelRoot);
431 animInst.apply(node);
432 } else if (type == "timed") {
433 SGTimedAnimation animInst(configNode, modelRoot);
434 animInst.apply(node);
435 } else if (type == "translate") {
436 SGTranslateAnimation animInst(configNode, modelRoot);
437 animInst.apply(node);
438 } else if (type == "null" || type == "none" || type.empty()) {
439 SGGroupAnimation animInst(configNode, modelRoot);
440 animInst.apply(node);
449 SGAnimation::apply(osg::Node* node)
451 // duh what a special case ...
452 if (_objectNames.empty()) {
453 osg::Group* group = node->asGroup();
455 osg::ref_ptr<osg::Group> animationGroup;
456 installInGroup(std::string(), *group, animationGroup);
463 SGAnimation::install(osg::Node& node)
467 node.setNodeMask( SG_NODEMASK_TERRAIN_BIT | node.getNodeMask());
469 node.setNodeMask(~SG_NODEMASK_TERRAIN_BIT & node.getNodeMask());
471 node.setNodeMask( SG_NODEMASK_CASTSHADOW_BIT | node.getNodeMask());
473 node.setNodeMask(~SG_NODEMASK_CASTSHADOW_BIT & node.getNodeMask());
477 SGAnimation::createAnimationGroup(osg::Group& parent)
479 // default implementation, we do not need a new group
480 // for every animation type. Usually animations that just change
481 // the StateSet of some parts of the model
486 SGAnimation::apply(osg::Group& group)
488 // the trick is to first traverse the children and then
489 // possibly splice in a new group node if required.
490 // Else we end up in a recursive loop where we infinitly insert new
494 // Note that this algorithm preserves the order of the child objects
495 // like they appear in the object-name tags.
496 // The timed animations require this
497 osg::ref_ptr<osg::Group> animationGroup;
498 std::list<std::string>::const_iterator nameIt;
499 for (nameIt = _objectNames.begin(); nameIt != _objectNames.end(); ++nameIt)
500 installInGroup(*nameIt, group, animationGroup);
504 SGAnimation::installInGroup(const std::string& name, osg::Group& group,
505 osg::ref_ptr<osg::Group>& animationGroup)
507 int i = group.getNumChildren() - 1;
508 for (; 0 <= i; --i) {
509 osg::Node* child = group.getChild(i);
511 // Check if this one is already processed
512 if (std::find(_installedAnimations.begin(),
513 _installedAnimations.end(), child)
514 != _installedAnimations.end())
517 if (name.empty() || child->getName() == name) {
518 // fire the installation of the animation
521 // create a group node on demand
522 if (!animationGroup.valid()) {
523 animationGroup = createAnimationGroup(group);
524 // Animation type that does not require a new group,
525 // in this case we can stop and look for the next object
526 if (animationGroup.valid() && !_name.empty())
527 animationGroup->setName(_name);
529 if (animationGroup.valid()) {
530 animationGroup->addChild(child);
531 group.removeChild(i);
534 // store that we already have processed this child node
535 // We can hit this one twice if an animation references some
536 // part of a subtree twice
537 _installedAnimations.push_back(child);
543 SGAnimation::removeMode(osg::Node& node, osg::StateAttribute::GLMode mode)
545 RemoveModeVisitor visitor(mode);
546 node.accept(visitor);
550 SGAnimation::removeAttribute(osg::Node& node, osg::StateAttribute::Type type)
552 RemoveAttributeVisitor visitor(type);
553 node.accept(visitor);
557 SGAnimation::removeTextureMode(osg::Node& node, unsigned unit,
558 osg::StateAttribute::GLMode mode)
560 RemoveTextureModeVisitor visitor(unit, mode);
561 node.accept(visitor);
565 SGAnimation::removeTextureAttribute(osg::Node& node, unsigned unit,
566 osg::StateAttribute::Type type)
568 RemoveTextureAttributeVisitor visitor(unit, type);
569 node.accept(visitor);
573 SGAnimation::setRenderBinToInherit(osg::Node& node)
575 BinToInheritVisitor visitor;
576 node.accept(visitor);
580 SGAnimation::cloneDrawables(osg::Node& node)
582 DrawableCloneVisitor visitor;
583 node.accept(visitor);
587 SGAnimation::getCondition() const
589 const SGPropertyNode* conditionNode = _configNode->getChild("condition");
592 return sgReadCondition(_modelRoot, conditionNode);
597 ////////////////////////////////////////////////////////////////////////
598 // Implementation of null animation
599 ////////////////////////////////////////////////////////////////////////
601 // Ok, that is to build a subgraph from different other
602 // graph nodes. I guess that this stems from the time where modellers
603 // could not build hierarchical trees ...
604 SGGroupAnimation::SGGroupAnimation(const SGPropertyNode* configNode,
605 SGPropertyNode* modelRoot):
606 SGAnimation(configNode, modelRoot)
611 SGGroupAnimation::createAnimationGroup(osg::Group& parent)
613 osg::Group* group = new osg::Group;
614 parent.addChild(group);
619 ////////////////////////////////////////////////////////////////////////
620 // Implementation of translate animation
621 ////////////////////////////////////////////////////////////////////////
623 class SGTranslateAnimation::UpdateCallback : public osg::NodeCallback {
625 UpdateCallback(SGCondition const* condition,
626 SGExpressiond const* animationValue) :
627 _condition(condition),
628 _animationValue(animationValue)
630 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
632 if (!_condition || _condition->test()) {
633 SGTranslateTransform* transform;
634 transform = static_cast<SGTranslateTransform*>(node);
635 transform->setValue(_animationValue->getValue());
640 SGSharedPtr<SGCondition const> _condition;
641 SGSharedPtr<SGExpressiond const> _animationValue;
644 SGTranslateAnimation::SGTranslateAnimation(const SGPropertyNode* configNode,
645 SGPropertyNode* modelRoot) :
646 SGAnimation(configNode, modelRoot)
648 _condition = getCondition();
649 SGSharedPtr<SGExpressiond> value;
650 value = read_value(configNode, modelRoot, "-m",
651 -SGLimitsd::max(), SGLimitsd::max());
652 _animationValue = value->simplify();
654 _initialValue = _animationValue->getValue();
658 if (configNode->hasValue("axis/x1-m")) {
660 v1[0] = configNode->getDoubleValue("axis/x1-m", 0);
661 v1[1] = configNode->getDoubleValue("axis/y1-m", 0);
662 v1[2] = configNode->getDoubleValue("axis/z1-m", 0);
663 v2[0] = configNode->getDoubleValue("axis/x2-m", 0);
664 v2[1] = configNode->getDoubleValue("axis/y2-m", 0);
665 v2[2] = configNode->getDoubleValue("axis/z2-m", 0);
668 _axis[0] = configNode->getDoubleValue("axis/x", 0);
669 _axis[1] = configNode->getDoubleValue("axis/y", 0);
670 _axis[2] = configNode->getDoubleValue("axis/z", 0);
672 if (8*SGLimitsd::min() < norm(_axis))
673 _axis = normalize(_axis);
677 SGTranslateAnimation::createAnimationGroup(osg::Group& parent)
679 SGTranslateTransform* transform = new SGTranslateTransform;
680 transform->setName("translate animation");
681 if (_animationValue && !_animationValue->isConst()) {
682 UpdateCallback* uc = new UpdateCallback(_condition, _animationValue);
683 transform->setUpdateCallback(uc);
685 transform->setAxis(_axis);
686 transform->setValue(_initialValue);
687 parent.addChild(transform);
692 ////////////////////////////////////////////////////////////////////////
693 // Implementation of rotate/spin animation
694 ////////////////////////////////////////////////////////////////////////
696 class SGRotateAnimation::UpdateCallback : public osg::NodeCallback {
698 UpdateCallback(SGCondition const* condition,
699 SGExpressiond const* animationValue) :
700 _condition(condition),
701 _animationValue(animationValue)
703 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
705 if (!_condition || _condition->test()) {
706 SGRotateTransform* transform;
707 transform = static_cast<SGRotateTransform*>(node);
708 transform->setAngleDeg(_animationValue->getValue());
713 SGSharedPtr<SGCondition const> _condition;
714 SGSharedPtr<SGExpressiond const> _animationValue;
717 class SGRotateAnimation::SpinUpdateCallback : public osg::NodeCallback {
719 SpinUpdateCallback(SGCondition const* condition,
720 SGExpressiond const* animationValue) :
721 _condition(condition),
722 _animationValue(animationValue),
725 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
727 if (!_condition || _condition->test()) {
728 SGRotateTransform* transform;
729 transform = static_cast<SGRotateTransform*>(node);
731 double t = nv->getFrameStamp()->getReferenceTime();
736 double velocity_rpms = _animationValue->getValue()/60;
737 double angle = transform->getAngleDeg();
738 angle += dt*velocity_rpms*360;
739 angle -= 360*floor(angle/360);
740 transform->setAngleDeg(angle);
745 SGSharedPtr<SGCondition const> _condition;
746 SGSharedPtr<SGExpressiond const> _animationValue;
750 SGRotateAnimation::SGRotateAnimation(const SGPropertyNode* configNode,
751 SGPropertyNode* modelRoot) :
752 SGAnimation(configNode, modelRoot)
754 std::string type = configNode->getStringValue("type", "");
755 _isSpin = (type == "spin");
757 _condition = getCondition();
758 SGSharedPtr<SGExpressiond> value;
759 value = read_value(configNode, modelRoot, "-deg",
760 -SGLimitsd::max(), SGLimitsd::max());
761 _animationValue = value->simplify();
763 _initialValue = _animationValue->getValue();
767 _center = SGVec3d::zeros();
768 if (configNode->hasValue("axis/x1-m")) {
770 v1[0] = configNode->getDoubleValue("axis/x1-m", 0);
771 v1[1] = configNode->getDoubleValue("axis/y1-m", 0);
772 v1[2] = configNode->getDoubleValue("axis/z1-m", 0);
773 v2[0] = configNode->getDoubleValue("axis/x2-m", 0);
774 v2[1] = configNode->getDoubleValue("axis/y2-m", 0);
775 v2[2] = configNode->getDoubleValue("axis/z2-m", 0);
776 _center = 0.5*(v1+v2);
779 _axis[0] = configNode->getDoubleValue("axis/x", 0);
780 _axis[1] = configNode->getDoubleValue("axis/y", 0);
781 _axis[2] = configNode->getDoubleValue("axis/z", 0);
783 if (8*SGLimitsd::min() < norm(_axis))
784 _axis = normalize(_axis);
786 _center[0] = configNode->getDoubleValue("center/x-m", _center[0]);
787 _center[1] = configNode->getDoubleValue("center/y-m", _center[1]);
788 _center[2] = configNode->getDoubleValue("center/z-m", _center[2]);
792 SGRotateAnimation::createAnimationGroup(osg::Group& parent)
794 SGRotateTransform* transform = new SGRotateTransform;
795 transform->setName("rotate animation");
797 SpinUpdateCallback* uc;
798 uc = new SpinUpdateCallback(_condition, _animationValue);
799 transform->setUpdateCallback(uc);
800 } else if (_animationValue || !_animationValue->isConst()) {
801 UpdateCallback* uc = new UpdateCallback(_condition, _animationValue);
802 transform->setUpdateCallback(uc);
804 transform->setCenter(_center);
805 transform->setAxis(_axis);
806 transform->setAngleDeg(_initialValue);
807 parent.addChild(transform);
812 ////////////////////////////////////////////////////////////////////////
813 // Implementation of scale animation
814 ////////////////////////////////////////////////////////////////////////
816 class SGScaleAnimation::UpdateCallback : public osg::NodeCallback {
818 UpdateCallback(const SGCondition* condition,
819 SGSharedPtr<const SGExpressiond> animationValue[3]) :
820 _condition(condition)
822 _animationValue[0] = animationValue[0];
823 _animationValue[1] = animationValue[1];
824 _animationValue[2] = animationValue[2];
826 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
828 if (!_condition || _condition->test()) {
829 SGScaleTransform* transform;
830 transform = static_cast<SGScaleTransform*>(node);
831 SGVec3d scale(_animationValue[0]->getValue(),
832 _animationValue[1]->getValue(),
833 _animationValue[2]->getValue());
834 transform->setScaleFactor(scale);
839 SGSharedPtr<SGCondition const> _condition;
840 SGSharedPtr<SGExpressiond const> _animationValue[3];
843 SGScaleAnimation::SGScaleAnimation(const SGPropertyNode* configNode,
844 SGPropertyNode* modelRoot) :
845 SGAnimation(configNode, modelRoot)
847 _condition = getCondition();
849 // default offset/factor for all directions
850 double offset = configNode->getDoubleValue("offset", 0);
851 double factor = configNode->getDoubleValue("factor", 1);
853 SGSharedPtr<SGExpressiond> inPropExpr;
855 std::string inputPropertyName;
856 inputPropertyName = configNode->getStringValue("property", "");
857 if (inputPropertyName.empty()) {
858 inPropExpr = new SGConstExpression<double>(0);
860 SGPropertyNode* inputProperty;
861 inputProperty = modelRoot->getNode(inputPropertyName, true);
862 inPropExpr = new SGPropertyExpression<double>(inputProperty);
865 SGInterpTable* interpTable = read_interpolation_table(configNode);
867 SGSharedPtr<SGExpressiond> value;
868 value = new SGInterpTableExpression<double>(inPropExpr, interpTable);
869 _animationValue[0] = value->simplify();
870 _animationValue[1] = value->simplify();
871 _animationValue[2] = value->simplify();
872 } else if (configNode->getBoolValue("use-personality", false)) {
873 SGSharedPtr<SGExpressiond> value;
874 value = new SGPersonalityScaleOffsetExpression(inPropExpr, configNode,
875 "x-factor", "x-offset",
877 double minClip = configNode->getDoubleValue("x-min", 0);
878 double maxClip = configNode->getDoubleValue("x-max", SGLimitsd::max());
879 value = new SGClipExpression<double>(value, minClip, maxClip);
880 _animationValue[0] = value->simplify();
882 value = new SGPersonalityScaleOffsetExpression(inPropExpr, configNode,
883 "y-factor", "y-offset",
885 minClip = configNode->getDoubleValue("y-min", 0);
886 maxClip = configNode->getDoubleValue("y-max", SGLimitsd::max());
887 value = new SGClipExpression<double>(value, minClip, maxClip);
888 _animationValue[1] = value->simplify();
890 value = new SGPersonalityScaleOffsetExpression(inPropExpr, configNode,
891 "z-factor", "z-offset",
893 minClip = configNode->getDoubleValue("z-min", 0);
894 maxClip = configNode->getDoubleValue("z-max", SGLimitsd::max());
895 value = new SGClipExpression<double>(value, minClip, maxClip);
896 _animationValue[2] = value->simplify();
898 SGSharedPtr<SGExpressiond> value;
899 value = read_factor_offset(configNode, inPropExpr, "x-factor", "x-offset");
900 double minClip = configNode->getDoubleValue("x-min", 0);
901 double maxClip = configNode->getDoubleValue("x-max", SGLimitsd::max());
902 value = new SGClipExpression<double>(value, minClip, maxClip);
903 _animationValue[0] = value->simplify();
905 value = read_factor_offset(configNode, inPropExpr, "y-factor", "y-offset");
906 minClip = configNode->getDoubleValue("y-min", 0);
907 maxClip = configNode->getDoubleValue("y-max", SGLimitsd::max());
908 value = new SGClipExpression<double>(value, minClip, maxClip);
909 _animationValue[1] = value->simplify();
911 value = read_factor_offset(configNode, inPropExpr, "z-factor", "z-offset");
912 minClip = configNode->getDoubleValue("z-min", 0);
913 maxClip = configNode->getDoubleValue("z-max", SGLimitsd::max());
914 value = new SGClipExpression<double>(value, minClip, maxClip);
915 _animationValue[2] = value->simplify();
917 _initialValue[0] = configNode->getDoubleValue("x-starting-scale", 1);
918 _initialValue[0] *= configNode->getDoubleValue("x-factor", factor);
919 _initialValue[0] += configNode->getDoubleValue("x-offset", offset);
920 _initialValue[1] = configNode->getDoubleValue("y-starting-scale", 1);
921 _initialValue[1] *= configNode->getDoubleValue("y-factor", factor);
922 _initialValue[1] += configNode->getDoubleValue("y-offset", offset);
923 _initialValue[2] = configNode->getDoubleValue("z-starting-scale", 1);
924 _initialValue[2] *= configNode->getDoubleValue("z-factor", factor);
925 _initialValue[2] += configNode->getDoubleValue("z-offset", offset);
926 _center[0] = configNode->getDoubleValue("center/x-m", 0);
927 _center[1] = configNode->getDoubleValue("center/y-m", 0);
928 _center[2] = configNode->getDoubleValue("center/z-m", 0);
932 SGScaleAnimation::createAnimationGroup(osg::Group& parent)
934 SGScaleTransform* transform = new SGScaleTransform;
935 transform->setName("scale animation");
936 transform->setCenter(_center);
937 transform->setScaleFactor(_initialValue);
938 UpdateCallback* uc = new UpdateCallback(_condition, _animationValue);
939 transform->setUpdateCallback(uc);
940 parent.addChild(transform);
945 // Don't create a new state state everytime we need GL_NORMALIZE!
949 Mutex normalizeMutex;
951 osg::StateSet* getNormalizeStateSet()
953 static osg::ref_ptr<osg::StateSet> normalizeStateSet;
954 ScopedLock<Mutex> lock(normalizeMutex);
955 if (!normalizeStateSet.valid()) {
956 normalizeStateSet = new osg::StateSet;
957 normalizeStateSet->setMode(GL_NORMALIZE, osg::StateAttribute::ON);
958 normalizeStateSet->setDataVariance(osg::Object::STATIC);
960 return normalizeStateSet.get();
964 ////////////////////////////////////////////////////////////////////////
965 // Implementation of dist scale animation
966 ////////////////////////////////////////////////////////////////////////
968 class SGDistScaleAnimation::Transform : public osg::Transform {
970 Transform(const SGPropertyNode* configNode)
972 setName(configNode->getStringValue("name", "dist scale animation"));
973 setReferenceFrame(RELATIVE_RF);
974 setStateSet(getNormalizeStateSet());
975 _factor = configNode->getFloatValue("factor", 1);
976 _offset = configNode->getFloatValue("offset", 0);
977 _min_v = configNode->getFloatValue("min", SGLimitsf::epsilon());
978 _max_v = configNode->getFloatValue("max", SGLimitsf::max());
979 _table = read_interpolation_table(configNode);
980 _center[0] = configNode->getFloatValue("center/x-m", 0);
981 _center[1] = configNode->getFloatValue("center/y-m", 0);
982 _center[2] = configNode->getFloatValue("center/z-m", 0);
984 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
985 osg::NodeVisitor* nv) const
987 osg::Matrix transform;
988 double scale_factor = computeScaleFactor(nv);
989 transform(0,0) = scale_factor;
990 transform(1,1) = scale_factor;
991 transform(2,2) = scale_factor;
992 transform(3,0) = _center[0]*(1 - scale_factor);
993 transform(3,1) = _center[1]*(1 - scale_factor);
994 transform(3,2) = _center[2]*(1 - scale_factor);
995 matrix.preMult(transform);
999 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
1000 osg::NodeVisitor* nv) const
1002 double scale_factor = computeScaleFactor(nv);
1003 if (fabs(scale_factor) <= SGLimits<double>::min())
1005 osg::Matrix transform;
1006 double rScaleFactor = 1/scale_factor;
1007 transform(0,0) = rScaleFactor;
1008 transform(1,1) = rScaleFactor;
1009 transform(2,2) = rScaleFactor;
1010 transform(3,0) = _center[0]*(1 - rScaleFactor);
1011 transform(3,1) = _center[1]*(1 - rScaleFactor);
1012 transform(3,2) = _center[2]*(1 - rScaleFactor);
1013 matrix.postMult(transform);
1018 double computeScaleFactor(osg::NodeVisitor* nv) const
1023 double scale_factor = (_center.osg() - nv->getEyePoint()).length();
1025 scale_factor = _factor * scale_factor + _offset;
1027 scale_factor = _table->interpolate( scale_factor );
1029 if (scale_factor < _min_v)
1030 scale_factor = _min_v;
1031 if (scale_factor > _max_v)
1032 scale_factor = _max_v;
1034 return scale_factor;
1037 SGSharedPtr<SGInterpTable> _table;
1046 SGDistScaleAnimation::SGDistScaleAnimation(const SGPropertyNode* configNode,
1047 SGPropertyNode* modelRoot) :
1048 SGAnimation(configNode, modelRoot)
1053 SGDistScaleAnimation::createAnimationGroup(osg::Group& parent)
1055 Transform* transform = new Transform(getConfig());
1056 parent.addChild(transform);
1061 ////////////////////////////////////////////////////////////////////////
1062 // Implementation of flash animation
1063 ////////////////////////////////////////////////////////////////////////
1065 class SGFlashAnimation::Transform : public osg::Transform {
1067 Transform(const SGPropertyNode* configNode)
1069 setReferenceFrame(RELATIVE_RF);
1070 setName(configNode->getStringValue("name", "flash animation"));
1071 setStateSet(getNormalizeStateSet());
1073 _axis[0] = configNode->getFloatValue("axis/x", 0);
1074 _axis[1] = configNode->getFloatValue("axis/y", 0);
1075 _axis[2] = configNode->getFloatValue("axis/z", 1);
1078 _center[0] = configNode->getFloatValue("center/x-m", 0);
1079 _center[1] = configNode->getFloatValue("center/y-m", 0);
1080 _center[2] = configNode->getFloatValue("center/z-m", 0);
1082 _offset = configNode->getFloatValue("offset", 0);
1083 _factor = configNode->getFloatValue("factor", 1);
1084 _power = configNode->getFloatValue("power", 1);
1085 _two_sides = configNode->getBoolValue("two-sides", false);
1087 _min_v = configNode->getFloatValue("min", SGLimitsf::epsilon());
1088 _max_v = configNode->getFloatValue("max", 1);
1090 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
1091 osg::NodeVisitor* nv) const
1093 osg::Matrix transform;
1094 double scale_factor = computeScaleFactor(nv);
1095 transform(0,0) = scale_factor;
1096 transform(1,1) = scale_factor;
1097 transform(2,2) = scale_factor;
1098 transform(3,0) = _center[0]*(1 - scale_factor);
1099 transform(3,1) = _center[1]*(1 - scale_factor);
1100 transform(3,2) = _center[2]*(1 - scale_factor);
1101 matrix.preMult(transform);
1105 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
1106 osg::NodeVisitor* nv) const
1108 double scale_factor = computeScaleFactor(nv);
1109 if (fabs(scale_factor) <= SGLimits<double>::min())
1111 osg::Matrix transform;
1112 double rScaleFactor = 1/scale_factor;
1113 transform(0,0) = rScaleFactor;
1114 transform(1,1) = rScaleFactor;
1115 transform(2,2) = rScaleFactor;
1116 transform(3,0) = _center[0]*(1 - rScaleFactor);
1117 transform(3,1) = _center[1]*(1 - rScaleFactor);
1118 transform(3,2) = _center[2]*(1 - rScaleFactor);
1119 matrix.postMult(transform);
1124 double computeScaleFactor(osg::NodeVisitor* nv) const
1129 osg::Vec3 localEyeToCenter = nv->getEyePoint() - _center;
1130 localEyeToCenter.normalize();
1132 double cos_angle = localEyeToCenter*_axis;
1133 double scale_factor = 0;
1134 if ( _two_sides && cos_angle < 0 )
1135 scale_factor = _factor * pow( -cos_angle, _power ) + _offset;
1136 else if ( cos_angle > 0 )
1137 scale_factor = _factor * pow( cos_angle, _power ) + _offset;
1139 if ( scale_factor < _min_v )
1140 scale_factor = _min_v;
1141 if ( scale_factor > _max_v )
1142 scale_factor = _max_v;
1144 return scale_factor;
1147 virtual osg::BoundingSphere computeBound() const
1149 // avoid being culled away by small feature culling
1150 osg::BoundingSphere bs = osg::Group::computeBound();
1151 bs.radius() *= _max_v;
1158 double _power, _factor, _offset, _min_v, _max_v;
1163 SGFlashAnimation::SGFlashAnimation(const SGPropertyNode* configNode,
1164 SGPropertyNode* modelRoot) :
1165 SGAnimation(configNode, modelRoot)
1170 SGFlashAnimation::createAnimationGroup(osg::Group& parent)
1172 Transform* transform = new Transform(getConfig());
1173 parent.addChild(transform);
1178 ////////////////////////////////////////////////////////////////////////
1179 // Implementation of flash animation
1180 ////////////////////////////////////////////////////////////////////////
1182 class SGBillboardAnimation::Transform : public osg::Transform {
1184 Transform(const SGPropertyNode* configNode) :
1185 _spherical(configNode->getBoolValue("spherical", true))
1187 setReferenceFrame(RELATIVE_RF);
1188 setName(configNode->getStringValue("name", "billboard animation"));
1190 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
1191 osg::NodeVisitor* nv) const
1193 // More or less taken from plibs ssgCutout
1195 matrix(0,0) = 1; matrix(0,1) = 0; matrix(0,2) = 0;
1196 matrix(1,0) = 0; matrix(1,1) = 0; matrix(1,2) = -1;
1197 matrix(2,0) = 0; matrix(2,1) = 1; matrix(2,2) = 0;
1199 osg::Vec3 zAxis(matrix(2, 0), matrix(2, 1), matrix(2, 2));
1200 osg::Vec3 xAxis = osg::Vec3(0, 0, -1)^zAxis;
1201 osg::Vec3 yAxis = zAxis^xAxis;
1207 matrix(0,0) = xAxis[0]; matrix(0,1) = xAxis[1]; matrix(0,2) = xAxis[2];
1208 matrix(1,0) = yAxis[0]; matrix(1,1) = yAxis[1]; matrix(1,2) = yAxis[2];
1209 matrix(2,0) = zAxis[0]; matrix(2,1) = zAxis[1]; matrix(2,2) = zAxis[2];
1214 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
1215 osg::NodeVisitor* nv) const
1217 // Hmm, don't yet know how to get that back ...
1226 SGBillboardAnimation::SGBillboardAnimation(const SGPropertyNode* configNode,
1227 SGPropertyNode* modelRoot) :
1228 SGAnimation(configNode, modelRoot)
1233 SGBillboardAnimation::createAnimationGroup(osg::Group& parent)
1235 Transform* transform = new Transform(getConfig());
1236 parent.addChild(transform);
1241 ////////////////////////////////////////////////////////////////////////
1242 // Implementation of a range animation
1243 ////////////////////////////////////////////////////////////////////////
1245 class SGRangeAnimation::UpdateCallback : public osg::NodeCallback {
1247 UpdateCallback(const SGCondition* condition,
1248 const SGExpressiond* minAnimationValue,
1249 const SGExpressiond* maxAnimationValue,
1250 double minValue, double maxValue) :
1251 _condition(condition),
1252 _minAnimationValue(minAnimationValue),
1253 _maxAnimationValue(maxAnimationValue),
1254 _minStaticValue(minValue),
1255 _maxStaticValue(maxValue)
1257 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1259 osg::LOD* lod = static_cast<osg::LOD*>(node);
1260 if (!_condition || _condition->test()) {
1262 if (_minAnimationValue)
1263 minRange = _minAnimationValue->getValue();
1265 minRange = _minStaticValue;
1267 if (_maxAnimationValue)
1268 maxRange = _maxAnimationValue->getValue();
1270 maxRange = _maxStaticValue;
1271 lod->setRange(0, minRange, maxRange);
1273 lod->setRange(0, 0, SGLimitsf::max());
1279 SGSharedPtr<const SGCondition> _condition;
1280 SGSharedPtr<const SGExpressiond> _minAnimationValue;
1281 SGSharedPtr<const SGExpressiond> _maxAnimationValue;
1282 double _minStaticValue;
1283 double _maxStaticValue;
1286 SGRangeAnimation::SGRangeAnimation(const SGPropertyNode* configNode,
1287 SGPropertyNode* modelRoot) :
1288 SGAnimation(configNode, modelRoot)
1290 _condition = getCondition();
1292 std::string inputPropertyName;
1293 inputPropertyName = configNode->getStringValue("min-property", "");
1294 if (!inputPropertyName.empty()) {
1295 SGPropertyNode* inputProperty;
1296 inputProperty = modelRoot->getNode(inputPropertyName, true);
1297 SGSharedPtr<SGExpressiond> value;
1298 value = new SGPropertyExpression<double>(inputProperty);
1300 value = read_factor_offset(configNode, value, "min-factor", "min-offset");
1301 _minAnimationValue = value->simplify();
1303 inputPropertyName = configNode->getStringValue("max-property", "");
1304 if (!inputPropertyName.empty()) {
1305 SGPropertyNode* inputProperty;
1306 inputProperty = modelRoot->getNode(inputPropertyName.c_str(), true);
1308 SGSharedPtr<SGExpressiond> value;
1309 value = new SGPropertyExpression<double>(inputProperty);
1311 value = read_factor_offset(configNode, value, "max-factor", "max-offset");
1312 _maxAnimationValue = value->simplify();
1315 _initialValue[0] = configNode->getDoubleValue("min-m", 0);
1316 _initialValue[0] *= configNode->getDoubleValue("min-factor", 1);
1317 _initialValue[1] = configNode->getDoubleValue("max-m", SGLimitsf::max());
1318 _initialValue[1] *= configNode->getDoubleValue("max-factor", 1);
1322 SGRangeAnimation::createAnimationGroup(osg::Group& parent)
1324 osg::Group* group = new osg::Group;
1325 group->setName("range animation group");
1327 osg::LOD* lod = new osg::LOD;
1328 lod->setName("range animation node");
1329 parent.addChild(lod);
1331 lod->addChild(group, _initialValue[0], _initialValue[1]);
1332 lod->setCenterMode(osg::LOD::USE_BOUNDING_SPHERE_CENTER);
1333 lod->setRangeMode(osg::LOD::DISTANCE_FROM_EYE_POINT);
1334 if (_minAnimationValue || _maxAnimationValue || _condition) {
1336 uc = new UpdateCallback(_condition, _minAnimationValue, _maxAnimationValue,
1337 _initialValue[0], _initialValue[1]);
1338 lod->setUpdateCallback(uc);
1344 ////////////////////////////////////////////////////////////////////////
1345 // Implementation of a select animation
1346 ////////////////////////////////////////////////////////////////////////
1348 class SGSelectAnimation::UpdateCallback : public osg::NodeCallback {
1350 UpdateCallback(const SGCondition* condition) :
1351 _condition(condition)
1353 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1355 osg::Switch* sw = static_cast<osg::Switch*>(node);
1356 if (_condition->test())
1357 sw->setAllChildrenOn();
1359 sw->setAllChildrenOff();
1364 SGSharedPtr<SGCondition const> _condition;
1367 SGSelectAnimation::SGSelectAnimation(const SGPropertyNode* configNode,
1368 SGPropertyNode* modelRoot) :
1369 SGAnimation(configNode, modelRoot)
1374 SGSelectAnimation::createAnimationGroup(osg::Group& parent)
1376 // if no condition given, this is a noop.
1377 SGSharedPtr<SGCondition const> condition = getCondition();
1378 // trick, gets deleted with all its 'animated' children
1379 // when the animation installer returns
1381 return new osg::Group;
1383 osg::Switch* sw = new osg::Switch;
1384 sw->setName("select animation node");
1385 sw->setUpdateCallback(new UpdateCallback(condition));
1386 parent.addChild(sw);
1392 ////////////////////////////////////////////////////////////////////////
1393 // Implementation of alpha test animation
1394 ////////////////////////////////////////////////////////////////////////
1396 SGAlphaTestAnimation::SGAlphaTestAnimation(const SGPropertyNode* configNode,
1397 SGPropertyNode* modelRoot) :
1398 SGAnimation(configNode, modelRoot)
1404 // Keep one copy of the most common alpha test its state set.
1405 ReentrantMutex alphaTestMutex;
1406 osg::ref_ptr<osg::AlphaFunc> standardAlphaFunc;
1407 osg::ref_ptr<osg::StateSet> alphaFuncStateSet;
1409 osg::AlphaFunc* makeAlphaFunc(float clamp)
1411 ScopedLock<ReentrantMutex> lock(alphaTestMutex);
1412 if (osg::equivalent(clamp, 0.01f)) {
1413 if (standardAlphaFunc.valid())
1414 return standardAlphaFunc.get();
1417 osg::AlphaFunc* alphaFunc = new osg::AlphaFunc;
1418 alphaFunc->setFunction(osg::AlphaFunc::GREATER);
1419 alphaFunc->setReferenceValue(clamp);
1420 alphaFunc->setDataVariance(osg::Object::STATIC);
1421 if (osg::equivalent(clamp, 0.01f))
1422 standardAlphaFunc = alphaFunc;
1426 osg::StateSet* makeAlphaTestStateSet(float clamp)
1428 using namespace OpenThreads;
1429 ScopedLock<ReentrantMutex> lock(alphaTestMutex);
1430 if (osg::equivalent(clamp, 0.01f)) {
1431 if (alphaFuncStateSet.valid())
1432 return alphaFuncStateSet.get();
1434 osg::AlphaFunc* alphaFunc = makeAlphaFunc(clamp);
1435 osg::StateSet* stateSet = new osg::StateSet;
1436 stateSet->setAttributeAndModes(alphaFunc,
1437 (osg::StateAttribute::ON
1438 | osg::StateAttribute::OVERRIDE));
1439 stateSet->setDataVariance(osg::Object::STATIC);
1440 if (osg::equivalent(clamp, 0.01f))
1441 alphaFuncStateSet = stateSet;
1446 SGAlphaTestAnimation::install(osg::Node& node)
1448 SGAnimation::install(node);
1450 float alphaClamp = getConfig()->getFloatValue("alpha-factor", 0);
1451 osg::StateSet* stateSet = node.getStateSet();
1453 node.setStateSet(makeAlphaTestStateSet(alphaClamp));
1455 stateSet->setAttributeAndModes(makeAlphaFunc(alphaClamp),
1456 (osg::StateAttribute::ON
1457 | osg::StateAttribute::OVERRIDE));
1462 //////////////////////////////////////////////////////////////////////
1463 // Blend animation installer
1464 //////////////////////////////////////////////////////////////////////
1466 // XXX This needs to be replaced by something using TexEnvCombine to
1467 // change the blend factor. Changing the alpha values in the geometry
1469 class SGBlendAnimation::BlendVisitor : public osg::NodeVisitor {
1471 BlendVisitor(float blend) :
1472 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
1474 { setVisitorType(osg::NodeVisitor::NODE_VISITOR); }
1475 virtual void apply(osg::Node& node)
1477 updateStateSet(node.getStateSet());
1480 virtual void apply(osg::Geode& node)
1482 apply((osg::Node&)node);
1483 unsigned nDrawables = node.getNumDrawables();
1484 for (unsigned i = 0; i < nDrawables; ++i) {
1485 osg::Drawable* drawable = node.getDrawable(i);
1486 osg::Geometry* geometry = drawable->asGeometry();
1489 osg::Array* array = geometry->getColorArray();
1492 osg::Vec4Array* vec4Array = dynamic_cast<osg::Vec4Array*>(array);
1495 for (unsigned k = 0; k < vec4Array->size(); ++k) {
1496 (*vec4Array)[k][3] = _blend;
1499 updateStateSet(drawable->getStateSet());
1502 void updateStateSet(osg::StateSet* stateSet)
1506 osg::StateAttribute* stateAttribute;
1507 stateAttribute = stateSet->getAttribute(osg::StateAttribute::MATERIAL);
1508 if (!stateAttribute)
1510 osg::Material* material = dynamic_cast<osg::Material*>(stateAttribute);
1513 material->setAlpha(osg::Material::FRONT_AND_BACK, _blend);
1515 stateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
1516 stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
1518 stateSet->setRenderingHint(osg::StateSet::DEFAULT_BIN);
1525 class SGBlendAnimation::UpdateCallback : public osg::NodeCallback {
1527 UpdateCallback(const SGPropertyNode* configNode, const SGExpressiond* v) :
1531 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1533 double blend = _animationValue->getValue();
1534 if (blend != _prev_value) {
1535 _prev_value = blend;
1536 BlendVisitor visitor(1-blend);
1537 node->accept(visitor);
1543 SGSharedPtr<SGExpressiond const> _animationValue;
1547 SGBlendAnimation::SGBlendAnimation(const SGPropertyNode* configNode,
1548 SGPropertyNode* modelRoot)
1549 : SGAnimation(configNode, modelRoot),
1550 _animationValue(read_value(configNode, modelRoot, "", 0, 1))
1555 SGBlendAnimation::createAnimationGroup(osg::Group& parent)
1557 if (!_animationValue)
1560 osg::Group* group = new osg::Switch;
1561 group->setName("blend animation node");
1562 group->setUpdateCallback(new UpdateCallback(getConfig(), _animationValue));
1563 parent.addChild(group);
1568 SGBlendAnimation::install(osg::Node& node)
1570 SGAnimation::install(node);
1571 // make sure we do not change common geometries,
1572 // that also creates new display lists for these subgeometries.
1573 cloneDrawables(node);
1574 DoDrawArraysVisitor visitor;
1575 node.accept(visitor);
1579 //////////////////////////////////////////////////////////////////////
1580 // Timed animation installer
1581 //////////////////////////////////////////////////////////////////////
1585 class SGTimedAnimation::UpdateCallback : public osg::NodeCallback {
1587 UpdateCallback(const SGPropertyNode* configNode) :
1590 _duration_sec(configNode->getDoubleValue("duration-sec", 1)),
1591 _last_time_sec(SGLimitsd::max()),
1592 _use_personality(configNode->getBoolValue("use-personality", false))
1594 std::vector<SGSharedPtr<SGPropertyNode> > nodes;
1595 nodes = configNode->getChildren("branch-duration-sec");
1596 for (size_t i = 0; i < nodes.size(); ++i) {
1597 unsigned ind = nodes[ i ]->getIndex();
1598 while ( ind >= _durations.size() ) {
1599 _durations.push_back(DurationSpec(_duration_sec));
1601 SGPropertyNode_ptr rNode = nodes[i]->getChild("random");
1603 _durations[ind] = DurationSpec(nodes[ i ]->getDoubleValue());
1605 _durations[ind] = DurationSpec(rNode->getDoubleValue( "min", 0),
1606 rNode->getDoubleValue( "max", 1));
1610 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1612 assert(dynamic_cast<osg::Switch*>(node));
1613 osg::Switch* sw = static_cast<osg::Switch*>(node);
1615 unsigned nChildren = sw->getNumChildren();
1617 // blow up the durations vector to the required size
1618 while (_durations.size() < nChildren) {
1619 _durations.push_back(_duration_sec);
1621 // make sure the current index is an duration that really exists
1622 _current_index = _current_index % nChildren;
1624 // update the time and compute the current systems time value
1625 double t = nv->getFrameStamp()->getReferenceTime();
1626 if (_last_time_sec == SGLimitsd::max()) {
1629 double dt = t - _last_time_sec;
1630 if (_use_personality)
1631 dt *= 1 + 0.2*(0.5 - sg_random());
1636 double currentDuration = _durations[_current_index].get();
1637 while (currentDuration < _reminder) {
1638 _reminder -= currentDuration;
1639 _current_index = (_current_index + 1) % nChildren;
1640 currentDuration = _durations[_current_index].get();
1643 sw->setSingleChildOn(_current_index);
1649 struct DurationSpec {
1650 DurationSpec(double t) :
1651 minTime(SGMiscd::max(0.01, t)),
1652 maxTime(SGMiscd::max(0.01, t))
1654 DurationSpec(double t0, double t1) :
1655 minTime(SGMiscd::max(0.01, t0)),
1656 maxTime(SGMiscd::max(0.01, t1))
1659 { return minTime + sg_random()*(maxTime - minTime); }
1663 std::vector<DurationSpec> _durations;
1664 unsigned _current_index;
1666 double _duration_sec;
1667 double _last_time_sec;
1668 bool _use_personality;
1672 SGTimedAnimation::SGTimedAnimation(const SGPropertyNode* configNode,
1673 SGPropertyNode* modelRoot)
1674 : SGAnimation(configNode, modelRoot)
1679 SGTimedAnimation::createAnimationGroup(osg::Group& parent)
1681 osg::Switch* sw = new osg::Switch;
1682 sw->setName("timed animation node");
1683 sw->setUpdateCallback(new UpdateCallback(getConfig()));
1684 parent.addChild(sw);
1689 ////////////////////////////////////////////////////////////////////////
1690 // dynamically switch on/off shadows
1691 ////////////////////////////////////////////////////////////////////////
1693 class SGShadowAnimation::UpdateCallback : public osg::NodeCallback {
1695 UpdateCallback(const SGCondition* condition) :
1696 _condition(condition)
1698 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1700 if (_condition->test())
1701 node->setNodeMask( SG_NODEMASK_CASTSHADOW_BIT | node->getNodeMask());
1703 node->setNodeMask(~SG_NODEMASK_CASTSHADOW_BIT & node->getNodeMask());
1708 SGSharedPtr<const SGCondition> _condition;
1711 SGShadowAnimation::SGShadowAnimation(const SGPropertyNode* configNode,
1712 SGPropertyNode* modelRoot) :
1713 SGAnimation(configNode, modelRoot)
1718 SGShadowAnimation::createAnimationGroup(osg::Group& parent)
1720 SGSharedPtr<SGCondition const> condition = getCondition();
1724 osg::Group* group = new osg::Group;
1725 group->setName("shadow animation");
1726 group->setUpdateCallback(new UpdateCallback(condition));
1727 parent.addChild(group);
1732 ////////////////////////////////////////////////////////////////////////
1733 // Implementation of SGTexTransformAnimation
1734 ////////////////////////////////////////////////////////////////////////
1736 class SGTexTransformAnimation::Transform : public SGReferenced {
1741 virtual ~Transform()
1743 void setValue(double value)
1745 virtual void transform(osg::Matrix&) = 0;
1750 class SGTexTransformAnimation::Translation :
1751 public SGTexTransformAnimation::Transform {
1753 Translation(const SGVec3d& axis) :
1756 virtual void transform(osg::Matrix& matrix)
1759 set_translation(tmp, _value, _axis);
1760 matrix.preMult(tmp);
1766 class SGTexTransformAnimation::Rotation :
1767 public SGTexTransformAnimation::Transform {
1769 Rotation(const SGVec3d& axis, const SGVec3d& center) :
1773 virtual void transform(osg::Matrix& matrix)
1776 set_rotation(tmp, _value, _center, _axis);
1777 matrix.preMult(tmp);
1784 class SGTexTransformAnimation::UpdateCallback :
1785 public osg::StateAttribute::Callback {
1787 UpdateCallback(const SGCondition* condition) :
1788 _condition(condition)
1790 virtual void operator () (osg::StateAttribute* sa, osg::NodeVisitor*)
1792 if (!_condition || _condition->test()) {
1793 TransformList::const_iterator i;
1794 for (i = _transforms.begin(); i != _transforms.end(); ++i)
1795 i->transform->setValue(i->value->getValue());
1797 assert(dynamic_cast<osg::TexMat*>(sa));
1798 osg::TexMat* texMat = static_cast<osg::TexMat*>(sa);
1799 texMat->getMatrix().makeIdentity();
1800 TransformList::const_iterator i;
1801 for (i = _transforms.begin(); i != _transforms.end(); ++i)
1802 i->transform->transform(texMat->getMatrix());
1804 void appendTransform(Transform* transform, SGExpressiond* value)
1806 Entry entry = { transform, value };
1807 transform->transform(_matrix);
1808 _transforms.push_back(entry);
1813 SGSharedPtr<Transform> transform;
1814 SGSharedPtr<const SGExpressiond> value;
1816 typedef std::vector<Entry> TransformList;
1817 TransformList _transforms;
1818 SGSharedPtr<const SGCondition> _condition;
1819 osg::Matrix _matrix;
1822 SGTexTransformAnimation::SGTexTransformAnimation(const SGPropertyNode* configNode,
1823 SGPropertyNode* modelRoot) :
1824 SGAnimation(configNode, modelRoot)
1829 SGTexTransformAnimation::createAnimationGroup(osg::Group& parent)
1831 osg::Group* group = new osg::Group;
1832 group->setName("texture transform group");
1833 osg::StateSet* stateSet = group->getOrCreateStateSet();
1834 stateSet->setDataVariance(osg::Object::DYNAMIC);
1835 osg::TexMat* texMat = new osg::TexMat;
1836 UpdateCallback* updateCallback = new UpdateCallback(getCondition());
1837 // interpret the configs ...
1838 std::string type = getType();
1840 if (type == "textranslate") {
1841 appendTexTranslate(getConfig(), updateCallback);
1842 } else if (type == "texrotate") {
1843 appendTexRotate(getConfig(), updateCallback);
1844 } else if (type == "texmultiple") {
1845 std::vector<SGSharedPtr<SGPropertyNode> > transformConfigs;
1846 transformConfigs = getConfig()->getChildren("transform");
1847 for (unsigned i = 0; i < transformConfigs.size(); ++i) {
1848 std::string subtype = transformConfigs[i]->getStringValue("subtype", "");
1849 if (subtype == "textranslate")
1850 appendTexTranslate(transformConfigs[i], updateCallback);
1851 else if (subtype == "texrotate")
1852 appendTexRotate(transformConfigs[i], updateCallback);
1854 SG_LOG(SG_INPUT, SG_ALERT,
1855 "Ignoring unknown texture transform subtype");
1858 SG_LOG(SG_INPUT, SG_ALERT, "Ignoring unknown texture transform type");
1861 texMat->setUpdateCallback(updateCallback);
1862 stateSet->setTextureAttribute(0, texMat);
1863 parent.addChild(group);
1868 SGTexTransformAnimation::appendTexTranslate(const SGPropertyNode* config,
1869 UpdateCallback* updateCallback)
1871 std::string propertyName = config->getStringValue("property", "");
1872 SGSharedPtr<SGExpressiond> value;
1873 if (propertyName.empty())
1874 value = new SGConstExpression<double>(0);
1876 SGPropertyNode* inputProperty = getModelRoot()->getNode(propertyName, true);
1877 value = new SGPropertyExpression<double>(inputProperty);
1880 SGInterpTable* table = read_interpolation_table(config);
1882 value = new SGInterpTableExpression<double>(value, table);
1883 double biasValue = config->getDoubleValue("bias", 0);
1885 value = new SGBiasExpression<double>(value, biasValue);
1886 value = new SGStepExpression<double>(value,
1887 config->getDoubleValue("step", 0),
1888 config->getDoubleValue("scroll", 0));
1889 value = value->simplify();
1891 double biasValue = config->getDoubleValue("bias", 0);
1893 value = new SGBiasExpression<double>(value, biasValue);
1894 value = new SGStepExpression<double>(value,
1895 config->getDoubleValue("step", 0),
1896 config->getDoubleValue("scroll", 0));
1897 value = read_offset_factor(config, value, "factor", "offset");
1899 if (config->hasChild("min") || config->hasChild("max")) {
1900 double minClip = config->getDoubleValue("min", -SGLimitsd::max());
1901 double maxClip = config->getDoubleValue("max", SGLimitsd::max());
1902 value = new SGClipExpression<double>(value, minClip, maxClip);
1904 value = value->simplify();
1906 SGVec3d axis(config->getDoubleValue("axis/x", 0),
1907 config->getDoubleValue("axis/y", 0),
1908 config->getDoubleValue("axis/z", 0));
1909 Translation* translation;
1910 translation = new Translation(normalize(axis));
1911 translation->setValue(config->getDoubleValue("starting-position", 0));
1912 updateCallback->appendTransform(translation, value);
1916 SGTexTransformAnimation::appendTexRotate(const SGPropertyNode* config,
1917 UpdateCallback* updateCallback)
1919 std::string propertyName = config->getStringValue("property", "");
1920 SGSharedPtr<SGExpressiond> value;
1921 if (propertyName.empty())
1922 value = new SGConstExpression<double>(0);
1924 SGPropertyNode* inputProperty = getModelRoot()->getNode(propertyName, true);
1925 value = new SGPropertyExpression<double>(inputProperty);
1928 SGInterpTable* table = read_interpolation_table(config);
1930 value = new SGInterpTableExpression<double>(value, table);
1931 double biasValue = config->getDoubleValue("bias", 0);
1933 value = new SGBiasExpression<double>(value, biasValue);
1934 value = new SGStepExpression<double>(value,
1935 config->getDoubleValue("step", 0),
1936 config->getDoubleValue("scroll", 0));
1937 value = value->simplify();
1939 double biasValue = config->getDoubleValue("bias", 0);
1941 value = new SGBiasExpression<double>(value, biasValue);
1942 value = new SGStepExpression<double>(value,
1943 config->getDoubleValue("step", 0),
1944 config->getDoubleValue("scroll", 0));
1945 value = read_offset_factor(config, value, "factor", "offset-deg");
1947 if (config->hasChild("min-deg") || config->hasChild("max-deg")) {
1948 double minClip = config->getDoubleValue("min-deg", -SGLimitsd::max());
1949 double maxClip = config->getDoubleValue("max-deg", SGLimitsd::max());
1950 value = new SGClipExpression<double>(value, minClip, maxClip);
1952 value = value->simplify();
1954 SGVec3d axis(config->getDoubleValue("axis/x", 0),
1955 config->getDoubleValue("axis/y", 0),
1956 config->getDoubleValue("axis/z", 0));
1957 SGVec3d center(config->getDoubleValue("center/x", 0),
1958 config->getDoubleValue("center/y", 0),
1959 config->getDoubleValue("center/z", 0));
1961 rotation = new Rotation(normalize(axis), center);
1962 rotation->setValue(config->getDoubleValue("starting-position-deg", 0));
1963 updateCallback->appendTransform(rotation, value);
1967 ////////////////////////////////////////////////////////////////////////
1968 // Implementation of SGPickAnimation
1969 ////////////////////////////////////////////////////////////////////////
1971 class SGPickAnimation::PickCallback : public SGPickCallback {
1973 PickCallback(const SGPropertyNode* configNode,
1974 SGPropertyNode* modelRoot) :
1975 _button(configNode->getIntValue("button", -1)),
1976 _repeatable(configNode->getBoolValue("repeatable", false)),
1977 _repeatInterval(configNode->getDoubleValue("interval-sec", 0.1))
1979 SG_LOG(SG_INPUT, SG_DEBUG, "Reading all bindings");
1980 std::vector<SGPropertyNode_ptr> bindings;
1981 bindings = configNode->getChildren("binding");
1982 for (unsigned int i = 0; i < bindings.size(); ++i) {
1983 _bindingsDown.push_back(new SGBinding(bindings[i], modelRoot));
1986 const SGPropertyNode* upNode = configNode->getChild("mod-up");
1989 bindings = upNode->getChildren("binding");
1990 for (unsigned int i = 0; i < bindings.size(); ++i) {
1991 _bindingsUp.push_back(new SGBinding(bindings[i], modelRoot));
1994 virtual bool buttonPressed(int button, const Info&)
1996 if (0 <= _button && button != _button)
1998 SGBindingList::const_iterator i;
1999 for (i = _bindingsDown.begin(); i != _bindingsDown.end(); ++i)
2004 virtual void buttonReleased(void)
2006 SGBindingList::const_iterator i;
2007 for (i = _bindingsUp.begin(); i != _bindingsUp.end(); ++i)
2010 virtual void update(double dt)
2016 while (_repeatInterval < _repeatTime) {
2017 _repeatTime -= _repeatInterval;
2018 SGBindingList::const_iterator i;
2019 for (i = _bindingsDown.begin(); i != _bindingsDown.end(); ++i)
2024 SGBindingList _bindingsDown;
2025 SGBindingList _bindingsUp;
2028 double _repeatInterval;
2032 SGPickAnimation::SGPickAnimation(const SGPropertyNode* configNode,
2033 SGPropertyNode* modelRoot) :
2034 SGAnimation(configNode, modelRoot)
2039 SGPickAnimation::createAnimationGroup(osg::Group& parent)
2041 osg::Group* commonGroup = new osg::Group;
2043 // Contains the normal geometry that is interactive
2044 osg::ref_ptr<osg::Group> normalGroup = new osg::Group;
2045 normalGroup->setName("pick normal group");
2046 normalGroup->addChild(commonGroup);
2048 // Used to render the geometry with just yellow edges
2049 osg::Group* highlightGroup = new osg::Group;
2050 highlightGroup->setName("pick highlight group");
2051 highlightGroup->setNodeMask(SG_NODEMASK_PICK_BIT);
2052 highlightGroup->addChild(commonGroup);
2053 SGSceneUserData* ud;
2054 ud = SGSceneUserData::getOrCreateSceneUserData(commonGroup);
2055 std::vector<SGPropertyNode_ptr> actions;
2056 actions = getConfig()->getChildren("action");
2057 for (unsigned int i = 0; i < actions.size(); ++i)
2058 ud->addPickCallback(new PickCallback(actions[i], getModelRoot()));
2060 // prepare a state set that paints the edges of this object yellow
2061 osg::StateSet* stateSet = highlightGroup->getOrCreateStateSet();
2062 stateSet->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE);
2064 osg::PolygonOffset* polygonOffset = new osg::PolygonOffset;
2065 polygonOffset->setFactor(-1);
2066 polygonOffset->setUnits(-1);
2067 stateSet->setAttribute(polygonOffset, osg::StateAttribute::OVERRIDE);
2068 stateSet->setMode(GL_POLYGON_OFFSET_LINE, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
2070 osg::PolygonMode* polygonMode = new osg::PolygonMode;
2071 polygonMode->setMode(osg::PolygonMode::FRONT_AND_BACK,
2072 osg::PolygonMode::LINE);
2073 stateSet->setAttribute(polygonMode, osg::StateAttribute::OVERRIDE);
2075 osg::Material* material = new osg::Material;
2076 material->setColorMode(osg::Material::OFF);
2077 material->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(1, 1, 0, 1));
2078 material->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(1, 1, 0, 1));
2079 material->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(1, 1, 0, 1));
2080 material->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0, 0, 0, 0));
2081 stateSet->setAttribute(material, osg::StateAttribute::OVERRIDE);
2083 // Only add normal geometry if configured
2084 if (getConfig()->getBoolValue("visible", true))
2085 parent.addChild(normalGroup.get());
2086 parent.addChild(highlightGroup);