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>
33 #include <osgDB/Registry>
34 #include <osgDB/Input>
35 #include <osgDB/ParameterOutput>
38 #include <simgear/math/interpolater.hxx>
39 #include <simgear/props/condition.hxx>
40 #include <simgear/props/props.hxx>
41 #include <simgear/structure/SGBinding.hxx>
42 #include <simgear/scene/util/SGNodeMasks.hxx>
43 #include <simgear/scene/util/SGSceneUserData.hxx>
44 #include <simgear/scene/util/SGStateAttributeVisitor.hxx>
46 #include "animation.hxx"
49 #include "SGTranslateTransform.hxx"
50 #include "SGMaterialAnimation.hxx"
51 #include "SGRotateTransform.hxx"
52 #include "SGScaleTransform.hxx"
53 #include "SGInteractionAnimation.hxx"
55 using OpenThreads::Mutex;
56 using OpenThreads::ReentrantMutex;
57 using OpenThreads::ScopedLock;
60 ////////////////////////////////////////////////////////////////////////
61 // Static utility functions.
62 ////////////////////////////////////////////////////////////////////////
65 * Set up the transform matrix for a spin or rotation.
68 set_rotation (osg::Matrix &matrix, double position_deg,
69 const SGVec3d ¢er, const SGVec3d &axis)
71 double temp_angle = -SGMiscd::deg2rad(position_deg);
73 double s = sin(temp_angle);
74 double c = cos(temp_angle);
77 // axis was normalized at load time
78 // hint to the compiler to put these into FP registers
83 matrix(0, 0) = t * x * x + c ;
84 matrix(0, 1) = t * y * x - s * z ;
85 matrix(0, 2) = t * z * x + s * y ;
88 matrix(1, 0) = t * x * y + s * z ;
89 matrix(1, 1) = t * y * y + c ;
90 matrix(1, 2) = t * z * y - s * x ;
93 matrix(2, 0) = t * x * z - s * y ;
94 matrix(2, 1) = t * y * z + s * x ;
95 matrix(2, 2) = t * z * z + c ;
98 // hint to the compiler to put these into FP registers
103 matrix(3, 0) = x - x*matrix(0, 0) - y*matrix(1, 0) - z*matrix(2, 0);
104 matrix(3, 1) = y - x*matrix(0, 1) - y*matrix(1, 1) - z*matrix(2, 1);
105 matrix(3, 2) = z - x*matrix(0, 2) - y*matrix(1, 2) - z*matrix(2, 2);
110 * Set up the transform matrix for a translation.
113 set_translation (osg::Matrix &matrix, double position_m, const SGVec3d &axis)
115 SGVec3d xyz = axis * position_m;
116 matrix.makeIdentity();
117 matrix(3, 0) = xyz[0];
118 matrix(3, 1) = xyz[1];
119 matrix(3, 2) = xyz[2];
123 * Read an interpolation table from properties.
125 static SGInterpTable *
126 read_interpolation_table(const SGPropertyNode* props)
128 const SGPropertyNode* table_node = props->getNode("interpolation");
131 return new SGInterpTable(table_node);
135 unit_string(const char* value, const char* unit)
137 return std::string(value) + unit;
140 class SGPersonalityScaleOffsetExpression : public SGUnaryExpression<double> {
142 SGPersonalityScaleOffsetExpression(SGExpression<double>* expr,
143 SGPropertyNode const* config,
144 const std::string& scalename,
145 const std::string& offsetname,
147 double defOffset = 0) :
148 SGUnaryExpression<double>(expr),
149 _scale(config, scalename.c_str(), defScale),
150 _offset(config, offsetname.c_str(), defOffset)
152 void setScale(double scale)
154 void setOffset(double offset)
155 { _offset = offset; }
157 virtual void eval(double& value, const simgear::expression::Binding* b) const
161 value = _offset + _scale*getOperand()->getValue(b);
164 virtual bool isConst() const { return false; }
167 mutable SGPersonalityParameter<double> _scale;
168 mutable SGPersonalityParameter<double> _offset;
172 static SGExpressiond*
173 read_factor_offset(const SGPropertyNode* configNode, SGExpressiond* expr,
174 const std::string& factor, const std::string& offset)
176 double factorValue = configNode->getDoubleValue(factor, 1);
177 if (factorValue != 1)
178 expr = new SGScaleExpression<double>(expr, factorValue);
179 double offsetValue = configNode->getDoubleValue(offset, 0);
180 if (offsetValue != 0)
181 expr = new SGBiasExpression<double>(expr, offsetValue);
185 static SGExpressiond*
186 read_offset_factor(const SGPropertyNode* configNode, SGExpressiond* expr,
187 const std::string& factor, const std::string& offset)
189 double offsetValue = configNode->getDoubleValue(offset, 0);
190 if (offsetValue != 0)
191 expr = new SGBiasExpression<double>(expr, offsetValue);
192 double factorValue = configNode->getDoubleValue(factor, 1);
193 if (factorValue != 1)
194 expr = new SGScaleExpression<double>(expr, factorValue);
199 read_value(const SGPropertyNode* configNode, SGPropertyNode* modelRoot,
200 const char* unit, double defMin, double defMax)
202 SGExpression<double>* value = 0;
204 std::string inputPropertyName = configNode->getStringValue("property", "");
205 if (inputPropertyName.empty()) {
206 std::string spos = unit_string("starting-position", unit);
207 double initPos = configNode->getDoubleValue(spos, 0);
208 value = new SGConstExpression<double>(initPos);
210 SGPropertyNode* inputProperty;
211 inputProperty = modelRoot->getNode(inputPropertyName, true);
212 value = new SGPropertyExpression<double>(inputProperty);
215 SGInterpTable* interpTable = read_interpolation_table(configNode);
217 return new SGInterpTableExpression<double>(value, interpTable);
219 std::string offset = unit_string("offset", unit);
220 std::string min = unit_string("min", unit);
221 std::string max = unit_string("max", unit);
223 if (configNode->getBoolValue("use-personality", false)) {
224 value = new SGPersonalityScaleOffsetExpression(value, configNode,
227 value = read_factor_offset(configNode, value, "factor", offset);
230 double minClip = configNode->getDoubleValue(min, defMin);
231 double maxClip = configNode->getDoubleValue(max, defMax);
232 if (minClip > SGMiscd::min(SGLimitsd::min(), -SGLimitsd::max()) ||
233 maxClip < SGLimitsd::max())
234 value = new SGClipExpression<double>(value, minClip, maxClip);
242 ////////////////////////////////////////////////////////////////////////
243 // Animation installer
244 ////////////////////////////////////////////////////////////////////////
246 class SGAnimation::RemoveModeVisitor : public SGStateAttributeVisitor {
248 RemoveModeVisitor(osg::StateAttribute::GLMode mode) :
251 virtual void apply(osg::StateSet* stateSet)
255 stateSet->removeMode(_mode);
258 osg::StateAttribute::GLMode _mode;
261 class SGAnimation::RemoveAttributeVisitor : public SGStateAttributeVisitor {
263 RemoveAttributeVisitor(osg::StateAttribute::Type type) :
266 virtual void apply(osg::StateSet* stateSet)
270 while (stateSet->getAttribute(_type)) {
271 stateSet->removeAttribute(_type);
275 osg::StateAttribute::Type _type;
278 class SGAnimation::RemoveTextureModeVisitor : public SGStateAttributeVisitor {
280 RemoveTextureModeVisitor(unsigned unit, osg::StateAttribute::GLMode mode) :
284 virtual void apply(osg::StateSet* stateSet)
288 stateSet->removeTextureMode(_unit, _mode);
292 osg::StateAttribute::GLMode _mode;
295 class SGAnimation::RemoveTextureAttributeVisitor :
296 public SGStateAttributeVisitor {
298 RemoveTextureAttributeVisitor(unsigned unit,
299 osg::StateAttribute::Type type) :
303 virtual void apply(osg::StateSet* stateSet)
307 while (stateSet->getTextureAttribute(_unit, _type)) {
308 stateSet->removeTextureAttribute(_unit, _type);
313 osg::StateAttribute::Type _type;
316 class SGAnimation::BinToInheritVisitor : public SGStateAttributeVisitor {
318 virtual void apply(osg::StateSet* stateSet)
322 stateSet->setRenderBinToInherit();
326 class SGAnimation::DrawableCloneVisitor : public osg::NodeVisitor {
328 DrawableCloneVisitor() :
329 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
331 void apply(osg::Geode& geode)
333 for (unsigned i = 0 ; i < geode.getNumDrawables(); ++i) {
334 osg::CopyOp copyOp(osg::CopyOp::DEEP_COPY_ALL &
335 ~osg::CopyOp::DEEP_COPY_TEXTURES);
336 geode.setDrawable(i, copyOp(geode.getDrawable(i)));
343 // Set all drawables to not use display lists. OSG will use
344 // glDrawArrays instead.
345 struct DoDrawArraysVisitor : public osg::NodeVisitor {
346 DoDrawArraysVisitor() :
347 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
349 void apply(osg::Geode& geode)
354 for (int i = 0; i < (int)geode.getNumDrawables(); ++i)
355 geode.getDrawable(i)->setUseDisplayList(false);
360 SGAnimation::SGAnimation(const SGPropertyNode* configNode,
361 SGPropertyNode* modelRoot) :
362 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
364 _configNode(configNode),
365 _modelRoot(modelRoot)
367 _name = configNode->getStringValue("name", "");
368 _enableHOT = configNode->getBoolValue("enable-hot", true);
369 _disableShadow = configNode->getBoolValue("disable-shadow", false);
370 std::vector<SGPropertyNode_ptr> objectNames =
371 configNode->getChildren("object-name");
372 for (unsigned i = 0; i < objectNames.size(); ++i)
373 _objectNames.push_back(objectNames[i]->getStringValue());
376 SGAnimation::~SGAnimation()
381 SG_LOG(SG_IO, SG_ALERT, "Could not find at least one of the following"
382 " objects for animation:\n");
383 std::list<std::string>::const_iterator i;
384 for (i = _objectNames.begin(); i != _objectNames.end(); ++i)
385 SG_LOG(SG_IO, SG_ALERT, *i << "\n");
389 SGAnimation::animate(osg::Node* node, const SGPropertyNode* configNode,
390 SGPropertyNode* modelRoot,
391 const osgDB::ReaderWriter::Options* options)
393 std::string type = configNode->getStringValue("type", "none");
394 if (type == "alpha-test") {
395 SGAlphaTestAnimation animInst(configNode, modelRoot);
396 animInst.apply(node);
397 } else if (type == "billboard") {
398 SGBillboardAnimation animInst(configNode, modelRoot);
399 animInst.apply(node);
400 } else if (type == "blend") {
401 SGBlendAnimation animInst(configNode, modelRoot);
402 animInst.apply(node);
403 } else if (type == "dist-scale") {
404 SGDistScaleAnimation animInst(configNode, modelRoot);
405 animInst.apply(node);
406 } else if (type == "flash") {
407 SGFlashAnimation animInst(configNode, modelRoot);
408 animInst.apply(node);
409 } else if (type == "interaction") {
410 SGInteractionAnimation animInst(configNode, modelRoot);
411 animInst.apply(node);
412 } else if (type == "material") {
413 SGMaterialAnimation animInst(configNode, modelRoot, options);
414 animInst.apply(node);
415 } else if (type == "noshadow") {
416 SGShadowAnimation animInst(configNode, modelRoot);
417 animInst.apply(node);
418 } else if (type == "pick") {
419 SGPickAnimation animInst(configNode, modelRoot);
420 animInst.apply(node);
421 } else if (type == "range") {
422 SGRangeAnimation animInst(configNode, modelRoot);
423 animInst.apply(node);
424 } else if (type == "rotate" || type == "spin") {
425 SGRotateAnimation animInst(configNode, modelRoot);
426 animInst.apply(node);
427 } else if (type == "scale") {
428 SGScaleAnimation animInst(configNode, modelRoot);
429 animInst.apply(node);
430 } else if (type == "select") {
431 SGSelectAnimation animInst(configNode, modelRoot);
432 animInst.apply(node);
433 } else if (type == "shader") {
434 SGShaderAnimation animInst(configNode, modelRoot, options);
435 animInst.apply(node);
436 } else if (type == "textranslate" || type == "texrotate" ||
437 type == "texmultiple") {
438 SGTexTransformAnimation animInst(configNode, modelRoot);
439 animInst.apply(node);
440 } else if (type == "timed") {
441 SGTimedAnimation animInst(configNode, modelRoot);
442 animInst.apply(node);
443 } else if (type == "translate") {
444 SGTranslateAnimation animInst(configNode, modelRoot);
445 animInst.apply(node);
446 } else if (type == "null" || type == "none" || type.empty()) {
447 SGGroupAnimation animInst(configNode, modelRoot);
448 animInst.apply(node);
457 SGAnimation::apply(osg::Node* node)
459 // duh what a special case ...
460 if (_objectNames.empty()) {
461 osg::Group* group = node->asGroup();
463 osg::ref_ptr<osg::Group> animationGroup;
464 installInGroup(std::string(), *group, animationGroup);
471 SGAnimation::install(osg::Node& node)
475 node.setNodeMask( SG_NODEMASK_TERRAIN_BIT | node.getNodeMask());
477 node.setNodeMask(~SG_NODEMASK_TERRAIN_BIT & node.getNodeMask());
479 node.setNodeMask( SG_NODEMASK_CASTSHADOW_BIT | node.getNodeMask());
481 node.setNodeMask(~SG_NODEMASK_CASTSHADOW_BIT & node.getNodeMask());
485 SGAnimation::createAnimationGroup(osg::Group& parent)
487 // default implementation, we do not need a new group
488 // for every animation type. Usually animations that just change
489 // the StateSet of some parts of the model
494 SGAnimation::apply(osg::Group& group)
496 // the trick is to first traverse the children and then
497 // possibly splice in a new group node if required.
498 // Else we end up in a recursive loop where we infinitly insert new
502 // Note that this algorithm preserves the order of the child objects
503 // like they appear in the object-name tags.
504 // The timed animations require this
505 osg::ref_ptr<osg::Group> animationGroup;
506 std::list<std::string>::const_iterator nameIt;
507 for (nameIt = _objectNames.begin(); nameIt != _objectNames.end(); ++nameIt)
508 installInGroup(*nameIt, group, animationGroup);
512 SGAnimation::installInGroup(const std::string& name, osg::Group& group,
513 osg::ref_ptr<osg::Group>& animationGroup)
515 int i = group.getNumChildren() - 1;
516 for (; 0 <= i; --i) {
517 osg::Node* child = group.getChild(i);
519 // Check if this one is already processed
520 if (std::find(_installedAnimations.begin(),
521 _installedAnimations.end(), child)
522 != _installedAnimations.end())
525 if (name.empty() || child->getName() == name) {
526 // fire the installation of the animation
529 // create a group node on demand
530 if (!animationGroup.valid()) {
531 animationGroup = createAnimationGroup(group);
532 // Animation type that does not require a new group,
533 // in this case we can stop and look for the next object
534 if (animationGroup.valid() && !_name.empty())
535 animationGroup->setName(_name);
537 if (animationGroup.valid()) {
538 animationGroup->addChild(child);
539 group.removeChild(i);
542 // store that we already have processed this child node
543 // We can hit this one twice if an animation references some
544 // part of a subtree twice
545 _installedAnimations.push_back(child);
551 SGAnimation::removeMode(osg::Node& node, osg::StateAttribute::GLMode mode)
553 RemoveModeVisitor visitor(mode);
554 node.accept(visitor);
558 SGAnimation::removeAttribute(osg::Node& node, osg::StateAttribute::Type type)
560 RemoveAttributeVisitor visitor(type);
561 node.accept(visitor);
565 SGAnimation::removeTextureMode(osg::Node& node, unsigned unit,
566 osg::StateAttribute::GLMode mode)
568 RemoveTextureModeVisitor visitor(unit, mode);
569 node.accept(visitor);
573 SGAnimation::removeTextureAttribute(osg::Node& node, unsigned unit,
574 osg::StateAttribute::Type type)
576 RemoveTextureAttributeVisitor visitor(unit, type);
577 node.accept(visitor);
581 SGAnimation::setRenderBinToInherit(osg::Node& node)
583 BinToInheritVisitor visitor;
584 node.accept(visitor);
588 SGAnimation::cloneDrawables(osg::Node& node)
590 DrawableCloneVisitor visitor;
591 node.accept(visitor);
595 SGAnimation::getCondition() const
597 const SGPropertyNode* conditionNode = _configNode->getChild("condition");
600 return sgReadCondition(_modelRoot, conditionNode);
605 ////////////////////////////////////////////////////////////////////////
606 // Implementation of null animation
607 ////////////////////////////////////////////////////////////////////////
609 // Ok, that is to build a subgraph from different other
610 // graph nodes. I guess that this stems from the time where modellers
611 // could not build hierarchical trees ...
612 SGGroupAnimation::SGGroupAnimation(const SGPropertyNode* configNode,
613 SGPropertyNode* modelRoot):
614 SGAnimation(configNode, modelRoot)
619 SGGroupAnimation::createAnimationGroup(osg::Group& parent)
621 osg::Group* group = new osg::Group;
622 parent.addChild(group);
627 ////////////////////////////////////////////////////////////////////////
628 // Implementation of translate animation
629 ////////////////////////////////////////////////////////////////////////
631 class SGTranslateAnimation::UpdateCallback : public osg::NodeCallback {
633 UpdateCallback(SGCondition const* condition,
634 SGExpressiond const* animationValue) :
635 _condition(condition),
636 _animationValue(animationValue)
638 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
640 if (!_condition || _condition->test()) {
641 SGTranslateTransform* transform;
642 transform = static_cast<SGTranslateTransform*>(node);
643 transform->setValue(_animationValue->getValue());
648 SGSharedPtr<SGCondition const> _condition;
649 SGSharedPtr<SGExpressiond const> _animationValue;
652 SGTranslateAnimation::SGTranslateAnimation(const SGPropertyNode* configNode,
653 SGPropertyNode* modelRoot) :
654 SGAnimation(configNode, modelRoot)
656 _condition = getCondition();
657 SGSharedPtr<SGExpressiond> value;
658 value = read_value(configNode, modelRoot, "-m",
659 -SGLimitsd::max(), SGLimitsd::max());
660 _animationValue = value->simplify();
662 _initialValue = _animationValue->getValue();
666 if (configNode->hasValue("axis/x1-m")) {
668 v1[0] = configNode->getDoubleValue("axis/x1-m", 0);
669 v1[1] = configNode->getDoubleValue("axis/y1-m", 0);
670 v1[2] = configNode->getDoubleValue("axis/z1-m", 0);
671 v2[0] = configNode->getDoubleValue("axis/x2-m", 0);
672 v2[1] = configNode->getDoubleValue("axis/y2-m", 0);
673 v2[2] = configNode->getDoubleValue("axis/z2-m", 0);
676 _axis[0] = configNode->getDoubleValue("axis/x", 0);
677 _axis[1] = configNode->getDoubleValue("axis/y", 0);
678 _axis[2] = configNode->getDoubleValue("axis/z", 0);
680 if (8*SGLimitsd::min() < norm(_axis))
681 _axis = normalize(_axis);
685 SGTranslateAnimation::createAnimationGroup(osg::Group& parent)
687 SGTranslateTransform* transform = new SGTranslateTransform;
688 transform->setName("translate animation");
689 if (_animationValue && !_animationValue->isConst()) {
690 UpdateCallback* uc = new UpdateCallback(_condition, _animationValue);
691 transform->setUpdateCallback(uc);
693 transform->setAxis(_axis);
694 transform->setValue(_initialValue);
695 parent.addChild(transform);
700 ////////////////////////////////////////////////////////////////////////
701 // Implementation of rotate/spin animation
702 ////////////////////////////////////////////////////////////////////////
704 class SGRotateAnimation::UpdateCallback : public osg::NodeCallback {
706 UpdateCallback(SGCondition const* condition,
707 SGExpressiond const* animationValue) :
708 _condition(condition),
709 _animationValue(animationValue)
711 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
713 if (!_condition || _condition->test()) {
714 SGRotateTransform* transform;
715 transform = static_cast<SGRotateTransform*>(node);
716 transform->setAngleDeg(_animationValue->getValue());
721 SGSharedPtr<SGCondition const> _condition;
722 SGSharedPtr<SGExpressiond const> _animationValue;
725 class SGRotateAnimation::SpinUpdateCallback : public osg::NodeCallback {
727 SpinUpdateCallback(SGCondition const* condition,
728 SGExpressiond const* animationValue) :
729 _condition(condition),
730 _animationValue(animationValue),
733 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
735 if (!_condition || _condition->test()) {
736 SGRotateTransform* transform;
737 transform = static_cast<SGRotateTransform*>(node);
739 double t = nv->getFrameStamp()->getReferenceTime();
744 double velocity_rpms = _animationValue->getValue()/60;
745 double angle = transform->getAngleDeg();
746 angle += dt*velocity_rpms*360;
747 angle -= 360*floor(angle/360);
748 transform->setAngleDeg(angle);
753 SGSharedPtr<SGCondition const> _condition;
754 SGSharedPtr<SGExpressiond const> _animationValue;
758 SGRotateAnimation::SGRotateAnimation(const SGPropertyNode* configNode,
759 SGPropertyNode* modelRoot) :
760 SGAnimation(configNode, modelRoot)
762 std::string type = configNode->getStringValue("type", "");
763 _isSpin = (type == "spin");
765 _condition = getCondition();
766 SGSharedPtr<SGExpressiond> value;
767 value = read_value(configNode, modelRoot, "-deg",
768 -SGLimitsd::max(), SGLimitsd::max());
769 _animationValue = value->simplify();
771 _initialValue = _animationValue->getValue();
775 _center = SGVec3d::zeros();
776 if (configNode->hasValue("axis/x1-m")) {
778 v1[0] = configNode->getDoubleValue("axis/x1-m", 0);
779 v1[1] = configNode->getDoubleValue("axis/y1-m", 0);
780 v1[2] = configNode->getDoubleValue("axis/z1-m", 0);
781 v2[0] = configNode->getDoubleValue("axis/x2-m", 0);
782 v2[1] = configNode->getDoubleValue("axis/y2-m", 0);
783 v2[2] = configNode->getDoubleValue("axis/z2-m", 0);
784 _center = 0.5*(v1+v2);
787 _axis[0] = configNode->getDoubleValue("axis/x", 0);
788 _axis[1] = configNode->getDoubleValue("axis/y", 0);
789 _axis[2] = configNode->getDoubleValue("axis/z", 0);
791 if (8*SGLimitsd::min() < norm(_axis))
792 _axis = normalize(_axis);
794 _center[0] = configNode->getDoubleValue("center/x-m", _center[0]);
795 _center[1] = configNode->getDoubleValue("center/y-m", _center[1]);
796 _center[2] = configNode->getDoubleValue("center/z-m", _center[2]);
800 SGRotateAnimation::createAnimationGroup(osg::Group& parent)
802 SGRotateTransform* transform = new SGRotateTransform;
803 transform->setName("rotate animation");
805 SpinUpdateCallback* uc;
806 uc = new SpinUpdateCallback(_condition, _animationValue);
807 transform->setUpdateCallback(uc);
808 } else if (_animationValue || !_animationValue->isConst()) {
809 UpdateCallback* uc = new UpdateCallback(_condition, _animationValue);
810 transform->setUpdateCallback(uc);
812 transform->setCenter(_center);
813 transform->setAxis(_axis);
814 transform->setAngleDeg(_initialValue);
815 parent.addChild(transform);
820 ////////////////////////////////////////////////////////////////////////
821 // Implementation of scale animation
822 ////////////////////////////////////////////////////////////////////////
824 class SGScaleAnimation::UpdateCallback : public osg::NodeCallback {
826 UpdateCallback(const SGCondition* condition,
827 SGSharedPtr<const SGExpressiond> animationValue[3]) :
828 _condition(condition)
830 _animationValue[0] = animationValue[0];
831 _animationValue[1] = animationValue[1];
832 _animationValue[2] = animationValue[2];
834 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
836 if (!_condition || _condition->test()) {
837 SGScaleTransform* transform;
838 transform = static_cast<SGScaleTransform*>(node);
839 SGVec3d scale(_animationValue[0]->getValue(),
840 _animationValue[1]->getValue(),
841 _animationValue[2]->getValue());
842 transform->setScaleFactor(scale);
847 SGSharedPtr<SGCondition const> _condition;
848 SGSharedPtr<SGExpressiond const> _animationValue[3];
851 SGScaleAnimation::SGScaleAnimation(const SGPropertyNode* configNode,
852 SGPropertyNode* modelRoot) :
853 SGAnimation(configNode, modelRoot)
855 _condition = getCondition();
857 // default offset/factor for all directions
858 double offset = configNode->getDoubleValue("offset", 0);
859 double factor = configNode->getDoubleValue("factor", 1);
861 SGSharedPtr<SGExpressiond> inPropExpr;
863 std::string inputPropertyName;
864 inputPropertyName = configNode->getStringValue("property", "");
865 if (inputPropertyName.empty()) {
866 inPropExpr = new SGConstExpression<double>(0);
868 SGPropertyNode* inputProperty;
869 inputProperty = modelRoot->getNode(inputPropertyName, true);
870 inPropExpr = new SGPropertyExpression<double>(inputProperty);
873 SGInterpTable* interpTable = read_interpolation_table(configNode);
875 SGSharedPtr<SGExpressiond> value;
876 value = new SGInterpTableExpression<double>(inPropExpr, interpTable);
877 _animationValue[0] = value->simplify();
878 _animationValue[1] = value->simplify();
879 _animationValue[2] = value->simplify();
880 } else if (configNode->getBoolValue("use-personality", false)) {
881 SGSharedPtr<SGExpressiond> value;
882 value = new SGPersonalityScaleOffsetExpression(inPropExpr, configNode,
883 "x-factor", "x-offset",
885 double minClip = configNode->getDoubleValue("x-min", 0);
886 double maxClip = configNode->getDoubleValue("x-max", SGLimitsd::max());
887 value = new SGClipExpression<double>(value, minClip, maxClip);
888 _animationValue[0] = value->simplify();
890 value = new SGPersonalityScaleOffsetExpression(inPropExpr, configNode,
891 "y-factor", "y-offset",
893 minClip = configNode->getDoubleValue("y-min", 0);
894 maxClip = configNode->getDoubleValue("y-max", SGLimitsd::max());
895 value = new SGClipExpression<double>(value, minClip, maxClip);
896 _animationValue[1] = value->simplify();
898 value = new SGPersonalityScaleOffsetExpression(inPropExpr, configNode,
899 "z-factor", "z-offset",
901 minClip = configNode->getDoubleValue("z-min", 0);
902 maxClip = configNode->getDoubleValue("z-max", SGLimitsd::max());
903 value = new SGClipExpression<double>(value, minClip, maxClip);
904 _animationValue[2] = value->simplify();
906 SGSharedPtr<SGExpressiond> value;
907 value = read_factor_offset(configNode, inPropExpr, "x-factor", "x-offset");
908 double minClip = configNode->getDoubleValue("x-min", 0);
909 double maxClip = configNode->getDoubleValue("x-max", SGLimitsd::max());
910 value = new SGClipExpression<double>(value, minClip, maxClip);
911 _animationValue[0] = value->simplify();
913 value = read_factor_offset(configNode, inPropExpr, "y-factor", "y-offset");
914 minClip = configNode->getDoubleValue("y-min", 0);
915 maxClip = configNode->getDoubleValue("y-max", SGLimitsd::max());
916 value = new SGClipExpression<double>(value, minClip, maxClip);
917 _animationValue[1] = value->simplify();
919 value = read_factor_offset(configNode, inPropExpr, "z-factor", "z-offset");
920 minClip = configNode->getDoubleValue("z-min", 0);
921 maxClip = configNode->getDoubleValue("z-max", SGLimitsd::max());
922 value = new SGClipExpression<double>(value, minClip, maxClip);
923 _animationValue[2] = value->simplify();
925 _initialValue[0] = configNode->getDoubleValue("x-starting-scale", 1);
926 _initialValue[0] *= configNode->getDoubleValue("x-factor", factor);
927 _initialValue[0] += configNode->getDoubleValue("x-offset", offset);
928 _initialValue[1] = configNode->getDoubleValue("y-starting-scale", 1);
929 _initialValue[1] *= configNode->getDoubleValue("y-factor", factor);
930 _initialValue[1] += configNode->getDoubleValue("y-offset", offset);
931 _initialValue[2] = configNode->getDoubleValue("z-starting-scale", 1);
932 _initialValue[2] *= configNode->getDoubleValue("z-factor", factor);
933 _initialValue[2] += configNode->getDoubleValue("z-offset", offset);
934 _center[0] = configNode->getDoubleValue("center/x-m", 0);
935 _center[1] = configNode->getDoubleValue("center/y-m", 0);
936 _center[2] = configNode->getDoubleValue("center/z-m", 0);
940 SGScaleAnimation::createAnimationGroup(osg::Group& parent)
942 SGScaleTransform* transform = new SGScaleTransform;
943 transform->setName("scale animation");
944 transform->setCenter(_center);
945 transform->setScaleFactor(_initialValue);
946 UpdateCallback* uc = new UpdateCallback(_condition, _animationValue);
947 transform->setUpdateCallback(uc);
948 parent.addChild(transform);
953 // Don't create a new state state everytime we need GL_NORMALIZE!
957 Mutex normalizeMutex;
959 osg::StateSet* getNormalizeStateSet()
961 static osg::ref_ptr<osg::StateSet> normalizeStateSet;
962 ScopedLock<Mutex> lock(normalizeMutex);
963 if (!normalizeStateSet.valid()) {
964 normalizeStateSet = new osg::StateSet;
965 normalizeStateSet->setMode(GL_NORMALIZE, osg::StateAttribute::ON);
966 normalizeStateSet->setDataVariance(osg::Object::STATIC);
968 return normalizeStateSet.get();
972 ////////////////////////////////////////////////////////////////////////
973 // Implementation of dist scale animation
974 ////////////////////////////////////////////////////////////////////////
976 class SGDistScaleAnimation::Transform : public osg::Transform {
978 Transform() : _min_v(0.0), _max_v(0.0), _factor(0.0), _offset(0.0) {}
979 Transform(const Transform& rhs,
980 const osg::CopyOp& copyOp = osg::CopyOp::SHALLOW_COPY)
981 : osg::Transform(rhs, copyOp), _table(rhs._table), _center(rhs._center),
982 _min_v(rhs._min_v), _max_v(rhs._max_v), _factor(rhs._factor),
986 META_Node(simgear, SGDistScaleAnimation::Transform);
987 Transform(const SGPropertyNode* configNode)
989 setName(configNode->getStringValue("name", "dist scale animation"));
990 setReferenceFrame(RELATIVE_RF);
991 setStateSet(getNormalizeStateSet());
992 _factor = configNode->getFloatValue("factor", 1);
993 _offset = configNode->getFloatValue("offset", 0);
994 _min_v = configNode->getFloatValue("min", SGLimitsf::epsilon());
995 _max_v = configNode->getFloatValue("max", SGLimitsf::max());
996 _table = read_interpolation_table(configNode);
997 _center[0] = configNode->getFloatValue("center/x-m", 0);
998 _center[1] = configNode->getFloatValue("center/y-m", 0);
999 _center[2] = configNode->getFloatValue("center/z-m", 0);
1001 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
1002 osg::NodeVisitor* nv) const
1004 osg::Matrix transform;
1005 double scale_factor = computeScaleFactor(nv);
1006 transform(0,0) = scale_factor;
1007 transform(1,1) = scale_factor;
1008 transform(2,2) = scale_factor;
1009 transform(3,0) = _center[0]*(1 - scale_factor);
1010 transform(3,1) = _center[1]*(1 - scale_factor);
1011 transform(3,2) = _center[2]*(1 - scale_factor);
1012 matrix.preMult(transform);
1016 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
1017 osg::NodeVisitor* nv) const
1019 double scale_factor = computeScaleFactor(nv);
1020 if (fabs(scale_factor) <= SGLimits<double>::min())
1022 osg::Matrix transform;
1023 double rScaleFactor = 1/scale_factor;
1024 transform(0,0) = rScaleFactor;
1025 transform(1,1) = rScaleFactor;
1026 transform(2,2) = rScaleFactor;
1027 transform(3,0) = _center[0]*(1 - rScaleFactor);
1028 transform(3,1) = _center[1]*(1 - rScaleFactor);
1029 transform(3,2) = _center[2]*(1 - rScaleFactor);
1030 matrix.postMult(transform);
1034 static bool writeLocalData(const osg::Object& obj, osgDB::Output& fw)
1036 const Transform& trans = static_cast<const Transform&>(obj);
1037 fw.indent() << "center " << trans._center << "\n";
1038 fw.indent() << "min_v " << trans._min_v << "\n";
1039 fw.indent() << "max_v " << trans._max_v << "\n";
1040 fw.indent() << "factor " << trans._factor << "\n";
1041 fw.indent() << "offset " << trans._offset << "\n";
1045 double computeScaleFactor(osg::NodeVisitor* nv) const
1050 double scale_factor = (toOsg(_center) - nv->getEyePoint()).length();
1052 scale_factor = _factor * scale_factor + _offset;
1054 scale_factor = _table->interpolate( scale_factor );
1056 if (scale_factor < _min_v)
1057 scale_factor = _min_v;
1058 if (scale_factor > _max_v)
1059 scale_factor = _max_v;
1061 return scale_factor;
1064 SGSharedPtr<SGInterpTable> _table;
1073 SGDistScaleAnimation::SGDistScaleAnimation(const SGPropertyNode* configNode,
1074 SGPropertyNode* modelRoot) :
1075 SGAnimation(configNode, modelRoot)
1080 SGDistScaleAnimation::createAnimationGroup(osg::Group& parent)
1082 Transform* transform = new Transform(getConfig());
1083 parent.addChild(transform);
1089 osgDB::RegisterDotOsgWrapperProxy distScaleAnimationTransformProxy
1091 new SGDistScaleAnimation::Transform,
1092 "SGDistScaleAnimation::Transform",
1093 "Object Node Transform SGDistScaleAnimation::Transform Group",
1095 &SGDistScaleAnimation::Transform::writeLocalData
1099 ////////////////////////////////////////////////////////////////////////
1100 // Implementation of flash animation
1101 ////////////////////////////////////////////////////////////////////////
1103 class SGFlashAnimation::Transform : public osg::Transform {
1105 Transform() : _power(0.0), _factor(0.0), _offset(0.0), _min_v(0.0),
1106 _max_v(0.0), _two_sides(false)
1109 Transform(const Transform& rhs,
1110 const osg::CopyOp& copyOp = osg::CopyOp::SHALLOW_COPY)
1111 : osg::Transform(rhs, copyOp), _center(rhs._center), _axis(rhs._axis),
1112 _power(rhs._power), _factor(rhs._factor), _offset(rhs._offset),
1113 _min_v(rhs._min_v), _max_v(rhs._max_v), _two_sides(rhs._two_sides)
1116 META_Node(simgear, SGFlashAnimation::Transform);
1118 Transform(const SGPropertyNode* configNode)
1120 setReferenceFrame(RELATIVE_RF);
1121 setName(configNode->getStringValue("name", "flash animation"));
1122 setStateSet(getNormalizeStateSet());
1124 _axis[0] = configNode->getFloatValue("axis/x", 0);
1125 _axis[1] = configNode->getFloatValue("axis/y", 0);
1126 _axis[2] = configNode->getFloatValue("axis/z", 1);
1129 _center[0] = configNode->getFloatValue("center/x-m", 0);
1130 _center[1] = configNode->getFloatValue("center/y-m", 0);
1131 _center[2] = configNode->getFloatValue("center/z-m", 0);
1133 _offset = configNode->getFloatValue("offset", 0);
1134 _factor = configNode->getFloatValue("factor", 1);
1135 _power = configNode->getFloatValue("power", 1);
1136 _two_sides = configNode->getBoolValue("two-sides", false);
1138 _min_v = configNode->getFloatValue("min", SGLimitsf::epsilon());
1139 _max_v = configNode->getFloatValue("max", 1);
1141 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
1142 osg::NodeVisitor* nv) const
1144 osg::Matrix transform;
1145 double scale_factor = computeScaleFactor(nv);
1146 transform(0,0) = scale_factor;
1147 transform(1,1) = scale_factor;
1148 transform(2,2) = scale_factor;
1149 transform(3,0) = _center[0]*(1 - scale_factor);
1150 transform(3,1) = _center[1]*(1 - scale_factor);
1151 transform(3,2) = _center[2]*(1 - scale_factor);
1152 matrix.preMult(transform);
1156 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
1157 osg::NodeVisitor* nv) const
1159 double scale_factor = computeScaleFactor(nv);
1160 if (fabs(scale_factor) <= SGLimits<double>::min())
1162 osg::Matrix transform;
1163 double rScaleFactor = 1/scale_factor;
1164 transform(0,0) = rScaleFactor;
1165 transform(1,1) = rScaleFactor;
1166 transform(2,2) = rScaleFactor;
1167 transform(3,0) = _center[0]*(1 - rScaleFactor);
1168 transform(3,1) = _center[1]*(1 - rScaleFactor);
1169 transform(3,2) = _center[2]*(1 - rScaleFactor);
1170 matrix.postMult(transform);
1174 static bool writeLocalData(const osg::Object& obj, osgDB::Output& fw)
1176 const Transform& trans = static_cast<const Transform&>(obj);
1177 fw.indent() << "center " << trans._center[0] << " "
1178 << trans._center[1] << " " << trans._center[2] << " " << "\n";
1179 fw.indent() << "axis " << trans._axis[0] << " "
1180 << trans._axis[1] << " " << trans._axis[2] << " " << "\n";
1181 fw.indent() << "power " << trans._power << " \n";
1182 fw.indent() << "min_v " << trans._min_v << "\n";
1183 fw.indent() << "max_v " << trans._max_v << "\n";
1184 fw.indent() << "factor " << trans._factor << "\n";
1185 fw.indent() << "offset " << trans._offset << "\n";
1186 fw.indent() << "twosides " << (trans._two_sides ? "true" : "false") << "\n";
1190 double computeScaleFactor(osg::NodeVisitor* nv) const
1195 osg::Vec3 localEyeToCenter = nv->getEyePoint() - _center;
1196 localEyeToCenter.normalize();
1198 double cos_angle = localEyeToCenter*_axis;
1199 double scale_factor = 0;
1200 if ( _two_sides && cos_angle < 0 )
1201 scale_factor = _factor * pow( -cos_angle, _power ) + _offset;
1202 else if ( cos_angle > 0 )
1203 scale_factor = _factor * pow( cos_angle, _power ) + _offset;
1205 if ( scale_factor < _min_v )
1206 scale_factor = _min_v;
1207 if ( scale_factor > _max_v )
1208 scale_factor = _max_v;
1210 return scale_factor;
1213 virtual osg::BoundingSphere computeBound() const
1215 // avoid being culled away by small feature culling
1216 osg::BoundingSphere bs = osg::Group::computeBound();
1217 bs.radius() *= _max_v;
1224 double _power, _factor, _offset, _min_v, _max_v;
1229 SGFlashAnimation::SGFlashAnimation(const SGPropertyNode* configNode,
1230 SGPropertyNode* modelRoot) :
1231 SGAnimation(configNode, modelRoot)
1236 SGFlashAnimation::createAnimationGroup(osg::Group& parent)
1238 Transform* transform = new Transform(getConfig());
1239 parent.addChild(transform);
1245 osgDB::RegisterDotOsgWrapperProxy flashAnimationTransformProxy
1247 new SGFlashAnimation::Transform,
1248 "SGFlashAnimation::Transform",
1249 "Object Node Transform SGFlashAnimation::Transform Group",
1251 &SGFlashAnimation::Transform::writeLocalData
1255 ////////////////////////////////////////////////////////////////////////
1256 // Implementation of billboard animation
1257 ////////////////////////////////////////////////////////////////////////
1259 class SGBillboardAnimation::Transform : public osg::Transform {
1261 Transform() : _spherical(true) {}
1262 Transform(const Transform& rhs,
1263 const osg::CopyOp& copyOp = osg::CopyOp::SHALLOW_COPY)
1264 : osg::Transform(rhs, copyOp), _spherical(rhs._spherical) {}
1265 META_Node(simgear, SGBillboardAnimation::Transform);
1266 Transform(const SGPropertyNode* configNode) :
1267 _spherical(configNode->getBoolValue("spherical", true))
1269 setReferenceFrame(RELATIVE_RF);
1270 setName(configNode->getStringValue("name", "billboard animation"));
1272 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
1273 osg::NodeVisitor* nv) const
1275 // More or less taken from plibs ssgCutout
1277 matrix(0,0) = 1; matrix(0,1) = 0; matrix(0,2) = 0;
1278 matrix(1,0) = 0; matrix(1,1) = 0; matrix(1,2) = -1;
1279 matrix(2,0) = 0; matrix(2,1) = 1; matrix(2,2) = 0;
1281 osg::Vec3 zAxis(matrix(2, 0), matrix(2, 1), matrix(2, 2));
1282 osg::Vec3 xAxis = osg::Vec3(0, 0, -1)^zAxis;
1283 osg::Vec3 yAxis = zAxis^xAxis;
1289 matrix(0,0) = xAxis[0]; matrix(0,1) = xAxis[1]; matrix(0,2) = xAxis[2];
1290 matrix(1,0) = yAxis[0]; matrix(1,1) = yAxis[1]; matrix(1,2) = yAxis[2];
1291 matrix(2,0) = zAxis[0]; matrix(2,1) = zAxis[1]; matrix(2,2) = zAxis[2];
1296 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
1297 osg::NodeVisitor* nv) const
1299 // Hmm, don't yet know how to get that back ...
1302 static bool writeLocalData(const osg::Object& obj, osgDB::Output& fw)
1304 const Transform& trans = static_cast<const Transform&>(obj);
1306 fw.indent() << (trans._spherical ? "true" : "false") << "\n";
1314 SGBillboardAnimation::SGBillboardAnimation(const SGPropertyNode* configNode,
1315 SGPropertyNode* modelRoot) :
1316 SGAnimation(configNode, modelRoot)
1321 SGBillboardAnimation::createAnimationGroup(osg::Group& parent)
1323 Transform* transform = new Transform(getConfig());
1324 parent.addChild(transform);
1330 osgDB::RegisterDotOsgWrapperProxy billboardAnimationTransformProxy
1332 new SGBillboardAnimation::Transform,
1333 "SGBillboardAnimation::Transform",
1334 "Object Node Transform SGBillboardAnimation::Transform Group",
1336 &SGBillboardAnimation::Transform::writeLocalData
1340 ////////////////////////////////////////////////////////////////////////
1341 // Implementation of a range animation
1342 ////////////////////////////////////////////////////////////////////////
1344 class SGRangeAnimation::UpdateCallback : public osg::NodeCallback {
1346 UpdateCallback(const SGCondition* condition,
1347 const SGExpressiond* minAnimationValue,
1348 const SGExpressiond* maxAnimationValue,
1349 double minValue, double maxValue) :
1350 _condition(condition),
1351 _minAnimationValue(minAnimationValue),
1352 _maxAnimationValue(maxAnimationValue),
1353 _minStaticValue(minValue),
1354 _maxStaticValue(maxValue)
1356 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1358 osg::LOD* lod = static_cast<osg::LOD*>(node);
1359 if (!_condition || _condition->test()) {
1361 if (_minAnimationValue)
1362 minRange = _minAnimationValue->getValue();
1364 minRange = _minStaticValue;
1366 if (_maxAnimationValue)
1367 maxRange = _maxAnimationValue->getValue();
1369 maxRange = _maxStaticValue;
1370 lod->setRange(0, minRange, maxRange);
1372 lod->setRange(0, 0, SGLimitsf::max());
1378 SGSharedPtr<const SGCondition> _condition;
1379 SGSharedPtr<const SGExpressiond> _minAnimationValue;
1380 SGSharedPtr<const SGExpressiond> _maxAnimationValue;
1381 double _minStaticValue;
1382 double _maxStaticValue;
1385 SGRangeAnimation::SGRangeAnimation(const SGPropertyNode* configNode,
1386 SGPropertyNode* modelRoot) :
1387 SGAnimation(configNode, modelRoot)
1389 _condition = getCondition();
1391 std::string inputPropertyName;
1392 inputPropertyName = configNode->getStringValue("min-property", "");
1393 if (!inputPropertyName.empty()) {
1394 SGPropertyNode* inputProperty;
1395 inputProperty = modelRoot->getNode(inputPropertyName, true);
1396 SGSharedPtr<SGExpressiond> value;
1397 value = new SGPropertyExpression<double>(inputProperty);
1399 value = read_factor_offset(configNode, value, "min-factor", "min-offset");
1400 _minAnimationValue = value->simplify();
1402 inputPropertyName = configNode->getStringValue("max-property", "");
1403 if (!inputPropertyName.empty()) {
1404 SGPropertyNode* inputProperty;
1405 inputProperty = modelRoot->getNode(inputPropertyName.c_str(), true);
1407 SGSharedPtr<SGExpressiond> value;
1408 value = new SGPropertyExpression<double>(inputProperty);
1410 value = read_factor_offset(configNode, value, "max-factor", "max-offset");
1411 _maxAnimationValue = value->simplify();
1414 _initialValue[0] = configNode->getDoubleValue("min-m", 0);
1415 _initialValue[0] *= configNode->getDoubleValue("min-factor", 1);
1416 _initialValue[1] = configNode->getDoubleValue("max-m", SGLimitsf::max());
1417 _initialValue[1] *= configNode->getDoubleValue("max-factor", 1);
1421 SGRangeAnimation::createAnimationGroup(osg::Group& parent)
1423 osg::Group* group = new osg::Group;
1424 group->setName("range animation group");
1426 osg::LOD* lod = new osg::LOD;
1427 lod->setName("range animation node");
1428 parent.addChild(lod);
1430 lod->addChild(group, _initialValue[0], _initialValue[1]);
1431 lod->setCenterMode(osg::LOD::USE_BOUNDING_SPHERE_CENTER);
1432 lod->setRangeMode(osg::LOD::DISTANCE_FROM_EYE_POINT);
1433 if (_minAnimationValue || _maxAnimationValue || _condition) {
1435 uc = new UpdateCallback(_condition, _minAnimationValue, _maxAnimationValue,
1436 _initialValue[0], _initialValue[1]);
1437 lod->setUpdateCallback(uc);
1443 ////////////////////////////////////////////////////////////////////////
1444 // Implementation of a select animation
1445 ////////////////////////////////////////////////////////////////////////
1447 class SGSelectAnimation::UpdateCallback : public osg::NodeCallback {
1449 UpdateCallback(const SGCondition* condition) :
1450 _condition(condition)
1452 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1454 osg::Switch* sw = static_cast<osg::Switch*>(node);
1455 if (_condition->test())
1456 sw->setAllChildrenOn();
1458 sw->setAllChildrenOff();
1463 SGSharedPtr<SGCondition const> _condition;
1466 SGSelectAnimation::SGSelectAnimation(const SGPropertyNode* configNode,
1467 SGPropertyNode* modelRoot) :
1468 SGAnimation(configNode, modelRoot)
1473 SGSelectAnimation::createAnimationGroup(osg::Group& parent)
1475 // if no condition given, this is a noop.
1476 SGSharedPtr<SGCondition const> condition = getCondition();
1477 // trick, gets deleted with all its 'animated' children
1478 // when the animation installer returns
1480 return new osg::Group;
1482 osg::Switch* sw = new osg::Switch;
1483 sw->setName("select animation node");
1484 sw->setUpdateCallback(new UpdateCallback(condition));
1485 parent.addChild(sw);
1491 ////////////////////////////////////////////////////////////////////////
1492 // Implementation of alpha test animation
1493 ////////////////////////////////////////////////////////////////////////
1495 SGAlphaTestAnimation::SGAlphaTestAnimation(const SGPropertyNode* configNode,
1496 SGPropertyNode* modelRoot) :
1497 SGAnimation(configNode, modelRoot)
1503 // Keep one copy of the most common alpha test its state set.
1504 ReentrantMutex alphaTestMutex;
1505 osg::ref_ptr<osg::AlphaFunc> standardAlphaFunc;
1506 osg::ref_ptr<osg::StateSet> alphaFuncStateSet;
1508 osg::AlphaFunc* makeAlphaFunc(float clamp)
1510 ScopedLock<ReentrantMutex> lock(alphaTestMutex);
1511 if (osg::equivalent(clamp, 0.01f)) {
1512 if (standardAlphaFunc.valid())
1513 return standardAlphaFunc.get();
1516 osg::AlphaFunc* alphaFunc = new osg::AlphaFunc;
1517 alphaFunc->setFunction(osg::AlphaFunc::GREATER);
1518 alphaFunc->setReferenceValue(clamp);
1519 alphaFunc->setDataVariance(osg::Object::STATIC);
1520 if (osg::equivalent(clamp, 0.01f))
1521 standardAlphaFunc = alphaFunc;
1525 osg::StateSet* makeAlphaTestStateSet(float clamp)
1527 using namespace OpenThreads;
1528 ScopedLock<ReentrantMutex> lock(alphaTestMutex);
1529 if (osg::equivalent(clamp, 0.01f)) {
1530 if (alphaFuncStateSet.valid())
1531 return alphaFuncStateSet.get();
1533 osg::AlphaFunc* alphaFunc = makeAlphaFunc(clamp);
1534 osg::StateSet* stateSet = new osg::StateSet;
1535 stateSet->setAttributeAndModes(alphaFunc,
1536 (osg::StateAttribute::ON
1537 | osg::StateAttribute::OVERRIDE));
1538 stateSet->setDataVariance(osg::Object::STATIC);
1539 if (osg::equivalent(clamp, 0.01f))
1540 alphaFuncStateSet = stateSet;
1545 SGAlphaTestAnimation::install(osg::Node& node)
1547 SGAnimation::install(node);
1549 float alphaClamp = getConfig()->getFloatValue("alpha-factor", 0);
1550 osg::StateSet* stateSet = node.getStateSet();
1552 node.setStateSet(makeAlphaTestStateSet(alphaClamp));
1554 stateSet->setAttributeAndModes(makeAlphaFunc(alphaClamp),
1555 (osg::StateAttribute::ON
1556 | osg::StateAttribute::OVERRIDE));
1561 //////////////////////////////////////////////////////////////////////
1562 // Blend animation installer
1563 //////////////////////////////////////////////////////////////////////
1565 // XXX This needs to be replaced by something using TexEnvCombine to
1566 // change the blend factor. Changing the alpha values in the geometry
1568 class SGBlendAnimation::BlendVisitor : public osg::NodeVisitor {
1570 BlendVisitor(float blend) :
1571 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
1573 { setVisitorType(osg::NodeVisitor::NODE_VISITOR); }
1574 virtual void apply(osg::Node& node)
1576 updateStateSet(node.getStateSet());
1579 virtual void apply(osg::Geode& node)
1581 apply((osg::Node&)node);
1582 unsigned nDrawables = node.getNumDrawables();
1583 for (unsigned i = 0; i < nDrawables; ++i) {
1584 osg::Drawable* drawable = node.getDrawable(i);
1585 osg::Geometry* geometry = drawable->asGeometry();
1588 osg::Array* array = geometry->getColorArray();
1591 osg::Vec4Array* vec4Array = dynamic_cast<osg::Vec4Array*>(array);
1594 for (unsigned k = 0; k < vec4Array->size(); ++k) {
1595 (*vec4Array)[k][3] = _blend;
1598 updateStateSet(drawable->getStateSet());
1601 void updateStateSet(osg::StateSet* stateSet)
1605 osg::StateAttribute* stateAttribute;
1606 stateAttribute = stateSet->getAttribute(osg::StateAttribute::MATERIAL);
1607 if (!stateAttribute)
1609 osg::Material* material = dynamic_cast<osg::Material*>(stateAttribute);
1612 material->setAlpha(osg::Material::FRONT_AND_BACK, _blend);
1614 stateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
1615 stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
1617 stateSet->setRenderingHint(osg::StateSet::DEFAULT_BIN);
1624 class SGBlendAnimation::UpdateCallback : public osg::NodeCallback {
1626 UpdateCallback(const SGPropertyNode* configNode, const SGExpressiond* v) :
1630 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1632 double blend = _animationValue->getValue();
1633 if (blend != _prev_value) {
1634 _prev_value = blend;
1635 BlendVisitor visitor(1-blend);
1636 node->accept(visitor);
1642 SGSharedPtr<SGExpressiond const> _animationValue;
1646 SGBlendAnimation::SGBlendAnimation(const SGPropertyNode* configNode,
1647 SGPropertyNode* modelRoot)
1648 : SGAnimation(configNode, modelRoot),
1649 _animationValue(read_value(configNode, modelRoot, "", 0, 1))
1654 SGBlendAnimation::createAnimationGroup(osg::Group& parent)
1656 if (!_animationValue)
1659 osg::Group* group = new osg::Switch;
1660 group->setName("blend animation node");
1661 group->setUpdateCallback(new UpdateCallback(getConfig(), _animationValue));
1662 parent.addChild(group);
1667 SGBlendAnimation::install(osg::Node& node)
1669 SGAnimation::install(node);
1670 // make sure we do not change common geometries,
1671 // that also creates new display lists for these subgeometries.
1672 cloneDrawables(node);
1673 DoDrawArraysVisitor visitor;
1674 node.accept(visitor);
1678 //////////////////////////////////////////////////////////////////////
1679 // Timed animation installer
1680 //////////////////////////////////////////////////////////////////////
1684 class SGTimedAnimation::UpdateCallback : public osg::NodeCallback {
1686 UpdateCallback(const SGPropertyNode* configNode) :
1689 _duration_sec(configNode->getDoubleValue("duration-sec", 1)),
1690 _last_time_sec(SGLimitsd::max()),
1691 _use_personality(configNode->getBoolValue("use-personality", false))
1693 std::vector<SGSharedPtr<SGPropertyNode> > nodes;
1694 nodes = configNode->getChildren("branch-duration-sec");
1695 for (size_t i = 0; i < nodes.size(); ++i) {
1696 unsigned ind = nodes[ i ]->getIndex();
1697 while ( ind >= _durations.size() ) {
1698 _durations.push_back(DurationSpec(_duration_sec));
1700 SGPropertyNode_ptr rNode = nodes[i]->getChild("random");
1702 _durations[ind] = DurationSpec(nodes[ i ]->getDoubleValue());
1704 _durations[ind] = DurationSpec(rNode->getDoubleValue( "min", 0),
1705 rNode->getDoubleValue( "max", 1));
1709 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1711 assert(dynamic_cast<osg::Switch*>(node));
1712 osg::Switch* sw = static_cast<osg::Switch*>(node);
1714 unsigned nChildren = sw->getNumChildren();
1716 // blow up the durations vector to the required size
1717 while (_durations.size() < nChildren) {
1718 _durations.push_back(_duration_sec);
1720 // make sure the current index is an duration that really exists
1721 _current_index = _current_index % nChildren;
1723 // update the time and compute the current systems time value
1724 double t = nv->getFrameStamp()->getReferenceTime();
1725 if (_last_time_sec == SGLimitsd::max()) {
1728 double dt = t - _last_time_sec;
1729 if (_use_personality)
1730 dt *= 1 + 0.2*(0.5 - sg_random());
1735 double currentDuration = _durations[_current_index].get();
1736 while (currentDuration < _reminder) {
1737 _reminder -= currentDuration;
1738 _current_index = (_current_index + 1) % nChildren;
1739 currentDuration = _durations[_current_index].get();
1742 sw->setSingleChildOn(_current_index);
1748 struct DurationSpec {
1749 DurationSpec(double t) :
1750 minTime(SGMiscd::max(0.01, t)),
1751 maxTime(SGMiscd::max(0.01, t))
1753 DurationSpec(double t0, double t1) :
1754 minTime(SGMiscd::max(0.01, t0)),
1755 maxTime(SGMiscd::max(0.01, t1))
1758 { return minTime + sg_random()*(maxTime - minTime); }
1762 std::vector<DurationSpec> _durations;
1763 unsigned _current_index;
1765 double _duration_sec;
1766 double _last_time_sec;
1767 bool _use_personality;
1771 SGTimedAnimation::SGTimedAnimation(const SGPropertyNode* configNode,
1772 SGPropertyNode* modelRoot)
1773 : SGAnimation(configNode, modelRoot)
1778 SGTimedAnimation::createAnimationGroup(osg::Group& parent)
1780 osg::Switch* sw = new osg::Switch;
1781 sw->setName("timed animation node");
1782 sw->setUpdateCallback(new UpdateCallback(getConfig()));
1783 parent.addChild(sw);
1788 ////////////////////////////////////////////////////////////////////////
1789 // dynamically switch on/off shadows
1790 ////////////////////////////////////////////////////////////////////////
1792 class SGShadowAnimation::UpdateCallback : public osg::NodeCallback {
1794 UpdateCallback(const SGCondition* condition) :
1795 _condition(condition)
1797 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1799 if (_condition->test())
1800 node->setNodeMask( SG_NODEMASK_CASTSHADOW_BIT | node->getNodeMask());
1802 node->setNodeMask(~SG_NODEMASK_CASTSHADOW_BIT & node->getNodeMask());
1807 SGSharedPtr<const SGCondition> _condition;
1810 SGShadowAnimation::SGShadowAnimation(const SGPropertyNode* configNode,
1811 SGPropertyNode* modelRoot) :
1812 SGAnimation(configNode, modelRoot)
1817 SGShadowAnimation::createAnimationGroup(osg::Group& parent)
1819 SGSharedPtr<SGCondition const> condition = getCondition();
1823 osg::Group* group = new osg::Group;
1824 group->setName("shadow animation");
1825 group->setUpdateCallback(new UpdateCallback(condition));
1826 parent.addChild(group);
1831 ////////////////////////////////////////////////////////////////////////
1832 // Implementation of SGTexTransformAnimation
1833 ////////////////////////////////////////////////////////////////////////
1835 class SGTexTransformAnimation::Transform : public SGReferenced {
1840 virtual ~Transform()
1842 void setValue(double value)
1844 virtual void transform(osg::Matrix&) = 0;
1849 class SGTexTransformAnimation::Translation :
1850 public SGTexTransformAnimation::Transform {
1852 Translation(const SGVec3d& axis) :
1855 virtual void transform(osg::Matrix& matrix)
1858 set_translation(tmp, _value, _axis);
1859 matrix.preMult(tmp);
1865 class SGTexTransformAnimation::Rotation :
1866 public SGTexTransformAnimation::Transform {
1868 Rotation(const SGVec3d& axis, const SGVec3d& center) :
1872 virtual void transform(osg::Matrix& matrix)
1875 set_rotation(tmp, _value, _center, _axis);
1876 matrix.preMult(tmp);
1883 class SGTexTransformAnimation::UpdateCallback :
1884 public osg::StateAttribute::Callback {
1886 UpdateCallback(const SGCondition* condition) :
1887 _condition(condition)
1889 virtual void operator () (osg::StateAttribute* sa, osg::NodeVisitor*)
1891 if (!_condition || _condition->test()) {
1892 TransformList::const_iterator i;
1893 for (i = _transforms.begin(); i != _transforms.end(); ++i)
1894 i->transform->setValue(i->value->getValue());
1896 assert(dynamic_cast<osg::TexMat*>(sa));
1897 osg::TexMat* texMat = static_cast<osg::TexMat*>(sa);
1898 texMat->getMatrix().makeIdentity();
1899 TransformList::const_iterator i;
1900 for (i = _transforms.begin(); i != _transforms.end(); ++i)
1901 i->transform->transform(texMat->getMatrix());
1903 void appendTransform(Transform* transform, SGExpressiond* value)
1905 Entry entry = { transform, value };
1906 transform->transform(_matrix);
1907 _transforms.push_back(entry);
1912 SGSharedPtr<Transform> transform;
1913 SGSharedPtr<const SGExpressiond> value;
1915 typedef std::vector<Entry> TransformList;
1916 TransformList _transforms;
1917 SGSharedPtr<const SGCondition> _condition;
1918 osg::Matrix _matrix;
1921 SGTexTransformAnimation::SGTexTransformAnimation(const SGPropertyNode* configNode,
1922 SGPropertyNode* modelRoot) :
1923 SGAnimation(configNode, modelRoot)
1928 SGTexTransformAnimation::createAnimationGroup(osg::Group& parent)
1930 osg::Group* group = new osg::Group;
1931 group->setName("texture transform group");
1932 osg::StateSet* stateSet = group->getOrCreateStateSet();
1933 stateSet->setDataVariance(osg::Object::DYNAMIC);
1934 osg::TexMat* texMat = new osg::TexMat;
1935 UpdateCallback* updateCallback = new UpdateCallback(getCondition());
1936 // interpret the configs ...
1937 std::string type = getType();
1939 if (type == "textranslate") {
1940 appendTexTranslate(getConfig(), updateCallback);
1941 } else if (type == "texrotate") {
1942 appendTexRotate(getConfig(), updateCallback);
1943 } else if (type == "texmultiple") {
1944 std::vector<SGSharedPtr<SGPropertyNode> > transformConfigs;
1945 transformConfigs = getConfig()->getChildren("transform");
1946 for (unsigned i = 0; i < transformConfigs.size(); ++i) {
1947 std::string subtype = transformConfigs[i]->getStringValue("subtype", "");
1948 if (subtype == "textranslate")
1949 appendTexTranslate(transformConfigs[i], updateCallback);
1950 else if (subtype == "texrotate")
1951 appendTexRotate(transformConfigs[i], updateCallback);
1953 SG_LOG(SG_INPUT, SG_ALERT,
1954 "Ignoring unknown texture transform subtype");
1957 SG_LOG(SG_INPUT, SG_ALERT, "Ignoring unknown texture transform type");
1960 texMat->setUpdateCallback(updateCallback);
1961 stateSet->setTextureAttribute(0, texMat);
1962 parent.addChild(group);
1967 SGTexTransformAnimation::appendTexTranslate(const SGPropertyNode* config,
1968 UpdateCallback* updateCallback)
1970 std::string propertyName = config->getStringValue("property", "");
1971 SGSharedPtr<SGExpressiond> value;
1972 if (propertyName.empty())
1973 value = new SGConstExpression<double>(0);
1975 SGPropertyNode* inputProperty = getModelRoot()->getNode(propertyName, true);
1976 value = new SGPropertyExpression<double>(inputProperty);
1979 SGInterpTable* table = read_interpolation_table(config);
1981 value = new SGInterpTableExpression<double>(value, table);
1982 double biasValue = config->getDoubleValue("bias", 0);
1984 value = new SGBiasExpression<double>(value, biasValue);
1985 value = new SGStepExpression<double>(value,
1986 config->getDoubleValue("step", 0),
1987 config->getDoubleValue("scroll", 0));
1988 value = value->simplify();
1990 double biasValue = config->getDoubleValue("bias", 0);
1992 value = new SGBiasExpression<double>(value, biasValue);
1993 value = new SGStepExpression<double>(value,
1994 config->getDoubleValue("step", 0),
1995 config->getDoubleValue("scroll", 0));
1996 value = read_offset_factor(config, value, "factor", "offset");
1998 if (config->hasChild("min") || config->hasChild("max")) {
1999 double minClip = config->getDoubleValue("min", -SGLimitsd::max());
2000 double maxClip = config->getDoubleValue("max", SGLimitsd::max());
2001 value = new SGClipExpression<double>(value, minClip, maxClip);
2003 value = value->simplify();
2005 SGVec3d axis(config->getDoubleValue("axis/x", 0),
2006 config->getDoubleValue("axis/y", 0),
2007 config->getDoubleValue("axis/z", 0));
2008 Translation* translation;
2009 translation = new Translation(normalize(axis));
2010 translation->setValue(config->getDoubleValue("starting-position", 0));
2011 updateCallback->appendTransform(translation, value);
2015 SGTexTransformAnimation::appendTexRotate(const SGPropertyNode* config,
2016 UpdateCallback* updateCallback)
2018 std::string propertyName = config->getStringValue("property", "");
2019 SGSharedPtr<SGExpressiond> value;
2020 if (propertyName.empty())
2021 value = new SGConstExpression<double>(0);
2023 SGPropertyNode* inputProperty = getModelRoot()->getNode(propertyName, true);
2024 value = new SGPropertyExpression<double>(inputProperty);
2027 SGInterpTable* table = read_interpolation_table(config);
2029 value = new SGInterpTableExpression<double>(value, table);
2030 double biasValue = config->getDoubleValue("bias", 0);
2032 value = new SGBiasExpression<double>(value, biasValue);
2033 value = new SGStepExpression<double>(value,
2034 config->getDoubleValue("step", 0),
2035 config->getDoubleValue("scroll", 0));
2036 value = value->simplify();
2038 double biasValue = config->getDoubleValue("bias", 0);
2040 value = new SGBiasExpression<double>(value, biasValue);
2041 value = new SGStepExpression<double>(value,
2042 config->getDoubleValue("step", 0),
2043 config->getDoubleValue("scroll", 0));
2044 value = read_offset_factor(config, value, "factor", "offset-deg");
2046 if (config->hasChild("min-deg") || config->hasChild("max-deg")) {
2047 double minClip = config->getDoubleValue("min-deg", -SGLimitsd::max());
2048 double maxClip = config->getDoubleValue("max-deg", SGLimitsd::max());
2049 value = new SGClipExpression<double>(value, minClip, maxClip);
2051 value = value->simplify();
2053 SGVec3d axis(config->getDoubleValue("axis/x", 0),
2054 config->getDoubleValue("axis/y", 0),
2055 config->getDoubleValue("axis/z", 0));
2056 SGVec3d center(config->getDoubleValue("center/x", 0),
2057 config->getDoubleValue("center/y", 0),
2058 config->getDoubleValue("center/z", 0));
2060 rotation = new Rotation(normalize(axis), center);
2061 rotation->setValue(config->getDoubleValue("starting-position-deg", 0));
2062 updateCallback->appendTransform(rotation, value);
2066 ////////////////////////////////////////////////////////////////////////
2067 // Implementation of SGPickAnimation
2068 ////////////////////////////////////////////////////////////////////////
2070 class SGPickAnimation::PickCallback : public SGPickCallback {
2072 PickCallback(const SGPropertyNode* configNode,
2073 SGPropertyNode* modelRoot) :
2074 _repeatable(configNode->getBoolValue("repeatable", false)),
2075 _repeatInterval(configNode->getDoubleValue("interval-sec", 0.1))
2077 SG_LOG(SG_INPUT, SG_DEBUG, "Reading all bindings");
2078 std::vector<SGPropertyNode_ptr> bindings;
2080 bindings = configNode->getChildren("button");
2081 for (unsigned int i = 0; i < bindings.size(); ++i) {
2082 _buttons.push_back( bindings[i]->getIntValue() );
2084 bindings = configNode->getChildren("binding");
2085 for (unsigned int i = 0; i < bindings.size(); ++i) {
2086 _bindingsDown.push_back(new SGBinding(bindings[i], modelRoot));
2089 const SGPropertyNode* upNode = configNode->getChild("mod-up");
2092 bindings = upNode->getChildren("binding");
2093 for (unsigned int i = 0; i < bindings.size(); ++i) {
2094 _bindingsUp.push_back(new SGBinding(bindings[i], modelRoot));
2097 virtual bool buttonPressed(int button, const Info&)
2100 for( std::vector<int>::iterator it = _buttons.begin(); it != _buttons.end(); it++ ) {
2101 if( *it == button ) {
2108 SGBindingList::const_iterator i;
2109 for (i = _bindingsDown.begin(); i != _bindingsDown.end(); ++i)
2111 _repeatTime = -_repeatInterval; // anti-bobble: delay start of repeat
2114 virtual void buttonReleased(void)
2116 SGBindingList::const_iterator i;
2117 for (i = _bindingsUp.begin(); i != _bindingsUp.end(); ++i)
2120 virtual void update(double dt)
2126 while (_repeatInterval < _repeatTime) {
2127 _repeatTime -= _repeatInterval;
2128 SGBindingList::const_iterator i;
2129 for (i = _bindingsDown.begin(); i != _bindingsDown.end(); ++i)
2134 SGBindingList _bindingsDown;
2135 SGBindingList _bindingsUp;
2136 std::vector<int> _buttons;
2138 double _repeatInterval;
2142 SGPickAnimation::SGPickAnimation(const SGPropertyNode* configNode,
2143 SGPropertyNode* modelRoot) :
2144 SGAnimation(configNode, modelRoot)
2149 SGPickAnimation::createAnimationGroup(osg::Group& parent)
2151 osg::Group* commonGroup = new osg::Group;
2153 // Contains the normal geometry that is interactive
2154 osg::ref_ptr<osg::Group> normalGroup = new osg::Group;
2155 normalGroup->setName("pick normal group");
2156 normalGroup->addChild(commonGroup);
2158 // Used to render the geometry with just yellow edges
2159 osg::Group* highlightGroup = new osg::Group;
2160 highlightGroup->setName("pick highlight group");
2161 highlightGroup->setNodeMask(SG_NODEMASK_PICK_BIT);
2162 highlightGroup->addChild(commonGroup);
2163 SGSceneUserData* ud;
2164 ud = SGSceneUserData::getOrCreateSceneUserData(commonGroup);
2165 std::vector<SGPropertyNode_ptr> actions;
2166 actions = getConfig()->getChildren("action");
2167 for (unsigned int i = 0; i < actions.size(); ++i)
2168 ud->addPickCallback(new PickCallback(actions[i], getModelRoot()));
2170 // prepare a state set that paints the edges of this object yellow
2171 osg::StateSet* stateSet = highlightGroup->getOrCreateStateSet();
2172 stateSet->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE);
2174 osg::PolygonOffset* polygonOffset = new osg::PolygonOffset;
2175 polygonOffset->setFactor(-1);
2176 polygonOffset->setUnits(-1);
2177 stateSet->setAttribute(polygonOffset, osg::StateAttribute::OVERRIDE);
2178 stateSet->setMode(GL_POLYGON_OFFSET_LINE, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
2180 osg::PolygonMode* polygonMode = new osg::PolygonMode;
2181 polygonMode->setMode(osg::PolygonMode::FRONT_AND_BACK,
2182 osg::PolygonMode::LINE);
2183 stateSet->setAttribute(polygonMode, osg::StateAttribute::OVERRIDE);
2185 osg::Material* material = new osg::Material;
2186 material->setColorMode(osg::Material::OFF);
2187 material->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(1, 1, 0, 1));
2188 material->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(1, 1, 0, 1));
2189 material->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(1, 1, 0, 1));
2190 material->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0, 0, 0, 0));
2191 stateSet->setAttribute(material, osg::StateAttribute::OVERRIDE);
2193 // Only add normal geometry if configured
2194 if (getConfig()->getBoolValue("visible", true))
2195 parent.addChild(normalGroup.get());
2196 parent.addChild(highlightGroup);