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"
49 #include "SGInteractionAnimation.hxx"
51 using OpenThreads::Mutex;
52 using OpenThreads::ReentrantMutex;
53 using OpenThreads::ScopedLock;
56 ////////////////////////////////////////////////////////////////////////
57 // Static utility functions.
58 ////////////////////////////////////////////////////////////////////////
61 * Set up the transform matrix for a spin or rotation.
64 set_rotation (osg::Matrix &matrix, double position_deg,
65 const SGVec3d ¢er, const SGVec3d &axis)
67 double temp_angle = -SGMiscd::deg2rad(position_deg);
69 double s = sin(temp_angle);
70 double c = cos(temp_angle);
73 // axis was normalized at load time
74 // hint to the compiler to put these into FP registers
79 matrix(0, 0) = t * x * x + c ;
80 matrix(0, 1) = t * y * x - s * z ;
81 matrix(0, 2) = t * z * x + s * y ;
84 matrix(1, 0) = t * x * y + s * z ;
85 matrix(1, 1) = t * y * y + c ;
86 matrix(1, 2) = t * z * y - s * x ;
89 matrix(2, 0) = t * x * z - s * y ;
90 matrix(2, 1) = t * y * z + s * x ;
91 matrix(2, 2) = t * z * z + c ;
94 // hint to the compiler to put these into FP registers
99 matrix(3, 0) = x - x*matrix(0, 0) - y*matrix(1, 0) - z*matrix(2, 0);
100 matrix(3, 1) = y - x*matrix(0, 1) - y*matrix(1, 1) - z*matrix(2, 1);
101 matrix(3, 2) = z - x*matrix(0, 2) - y*matrix(1, 2) - z*matrix(2, 2);
106 * Set up the transform matrix for a translation.
109 set_translation (osg::Matrix &matrix, double position_m, const SGVec3d &axis)
111 SGVec3d xyz = axis * position_m;
112 matrix.makeIdentity();
113 matrix(3, 0) = xyz[0];
114 matrix(3, 1) = xyz[1];
115 matrix(3, 2) = xyz[2];
119 * Read an interpolation table from properties.
121 static SGInterpTable *
122 read_interpolation_table(const SGPropertyNode* props)
124 const SGPropertyNode* table_node = props->getNode("interpolation");
127 return new SGInterpTable(table_node);
131 unit_string(const char* value, const char* unit)
133 return std::string(value) + unit;
136 class SGPersonalityScaleOffsetExpression : public SGUnaryExpression<double> {
138 SGPersonalityScaleOffsetExpression(SGExpression<double>* expr,
139 SGPropertyNode const* config,
140 const std::string& scalename,
141 const std::string& offsetname,
143 double defOffset = 0) :
144 SGUnaryExpression<double>(expr),
145 _scale(config, scalename.c_str(), defScale),
146 _offset(config, offsetname.c_str(), defOffset)
148 void setScale(double scale)
150 void setOffset(double offset)
151 { _offset = offset; }
153 virtual void eval(double& value, const simgear::expression::Binding* b) const
157 value = _offset + _scale*getOperand()->getValue(b);
160 virtual bool isConst() const { return false; }
163 mutable SGPersonalityParameter<double> _scale;
164 mutable SGPersonalityParameter<double> _offset;
168 static SGExpressiond*
169 read_factor_offset(const SGPropertyNode* configNode, SGExpressiond* expr,
170 const std::string& factor, const std::string& offset)
172 double factorValue = configNode->getDoubleValue(factor, 1);
173 if (factorValue != 1)
174 expr = new SGScaleExpression<double>(expr, factorValue);
175 double offsetValue = configNode->getDoubleValue(offset, 0);
176 if (offsetValue != 0)
177 expr = new SGBiasExpression<double>(expr, offsetValue);
181 static SGExpressiond*
182 read_offset_factor(const SGPropertyNode* configNode, SGExpressiond* expr,
183 const std::string& factor, const std::string& offset)
185 double offsetValue = configNode->getDoubleValue(offset, 0);
186 if (offsetValue != 0)
187 expr = new SGBiasExpression<double>(expr, offsetValue);
188 double factorValue = configNode->getDoubleValue(factor, 1);
189 if (factorValue != 1)
190 expr = new SGScaleExpression<double>(expr, factorValue);
195 read_value(const SGPropertyNode* configNode, SGPropertyNode* modelRoot,
196 const char* unit, double defMin, double defMax)
198 SGExpression<double>* value = 0;
200 std::string inputPropertyName = configNode->getStringValue("property", "");
201 if (inputPropertyName.empty()) {
202 std::string spos = unit_string("starting-position", unit);
203 double initPos = configNode->getDoubleValue(spos, 0);
204 value = new SGConstExpression<double>(initPos);
206 SGPropertyNode* inputProperty;
207 inputProperty = modelRoot->getNode(inputPropertyName, true);
208 value = new SGPropertyExpression<double>(inputProperty);
211 SGInterpTable* interpTable = read_interpolation_table(configNode);
213 return new SGInterpTableExpression<double>(value, interpTable);
215 std::string offset = unit_string("offset", unit);
216 std::string min = unit_string("min", unit);
217 std::string max = unit_string("max", unit);
219 if (configNode->getBoolValue("use-personality", false)) {
220 value = new SGPersonalityScaleOffsetExpression(value, configNode,
223 value = read_factor_offset(configNode, value, "factor", offset);
226 double minClip = configNode->getDoubleValue(min, defMin);
227 double maxClip = configNode->getDoubleValue(max, defMax);
228 if (minClip > SGMiscd::min(SGLimitsd::min(), -SGLimitsd::max()) ||
229 maxClip < SGLimitsd::max())
230 value = new SGClipExpression<double>(value, minClip, maxClip);
238 ////////////////////////////////////////////////////////////////////////
239 // Animation installer
240 ////////////////////////////////////////////////////////////////////////
242 class SGAnimation::RemoveModeVisitor : public SGStateAttributeVisitor {
244 RemoveModeVisitor(osg::StateAttribute::GLMode mode) :
247 virtual void apply(osg::StateSet* stateSet)
251 stateSet->removeMode(_mode);
254 osg::StateAttribute::GLMode _mode;
257 class SGAnimation::RemoveAttributeVisitor : public SGStateAttributeVisitor {
259 RemoveAttributeVisitor(osg::StateAttribute::Type type) :
262 virtual void apply(osg::StateSet* stateSet)
266 while (stateSet->getAttribute(_type)) {
267 stateSet->removeAttribute(_type);
271 osg::StateAttribute::Type _type;
274 class SGAnimation::RemoveTextureModeVisitor : public SGStateAttributeVisitor {
276 RemoveTextureModeVisitor(unsigned unit, osg::StateAttribute::GLMode mode) :
280 virtual void apply(osg::StateSet* stateSet)
284 stateSet->removeTextureMode(_unit, _mode);
288 osg::StateAttribute::GLMode _mode;
291 class SGAnimation::RemoveTextureAttributeVisitor :
292 public SGStateAttributeVisitor {
294 RemoveTextureAttributeVisitor(unsigned unit,
295 osg::StateAttribute::Type type) :
299 virtual void apply(osg::StateSet* stateSet)
303 while (stateSet->getTextureAttribute(_unit, _type)) {
304 stateSet->removeTextureAttribute(_unit, _type);
309 osg::StateAttribute::Type _type;
312 class SGAnimation::BinToInheritVisitor : public SGStateAttributeVisitor {
314 virtual void apply(osg::StateSet* stateSet)
318 stateSet->setRenderBinToInherit();
322 class SGAnimation::DrawableCloneVisitor : public osg::NodeVisitor {
324 DrawableCloneVisitor() :
325 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
327 void apply(osg::Geode& geode)
329 for (unsigned i = 0 ; i < geode.getNumDrawables(); ++i) {
330 osg::CopyOp copyOp(osg::CopyOp::DEEP_COPY_ALL &
331 ~osg::CopyOp::DEEP_COPY_TEXTURES);
332 geode.setDrawable(i, copyOp(geode.getDrawable(i)));
339 // Set all drawables to not use display lists. OSG will use
340 // glDrawArrays instead.
341 struct DoDrawArraysVisitor : public osg::NodeVisitor {
342 DoDrawArraysVisitor() :
343 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
345 void apply(osg::Geode& geode)
350 for (int i = 0; i < geode.getNumDrawables(); ++i)
351 geode.getDrawable(i)->setUseDisplayList(false);
356 SGAnimation::SGAnimation(const SGPropertyNode* configNode,
357 SGPropertyNode* modelRoot) :
358 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
360 _configNode(configNode),
361 _modelRoot(modelRoot)
363 _name = configNode->getStringValue("name", "");
364 _enableHOT = configNode->getBoolValue("enable-hot", true);
365 _disableShadow = configNode->getBoolValue("disable-shadow", false);
366 std::vector<SGPropertyNode_ptr> objectNames =
367 configNode->getChildren("object-name");
368 for (unsigned i = 0; i < objectNames.size(); ++i)
369 _objectNames.push_back(objectNames[i]->getStringValue());
372 SGAnimation::~SGAnimation()
377 SG_LOG(SG_IO, SG_ALERT, "Could not find at least one of the following"
378 " objects for animation:\n");
379 std::list<std::string>::const_iterator i;
380 for (i = _objectNames.begin(); i != _objectNames.end(); ++i)
381 SG_LOG(SG_IO, SG_ALERT, *i << "\n");
385 SGAnimation::animate(osg::Node* node, const SGPropertyNode* configNode,
386 SGPropertyNode* modelRoot,
387 const osgDB::ReaderWriter::Options* options)
389 std::string type = configNode->getStringValue("type", "none");
390 if (type == "alpha-test") {
391 SGAlphaTestAnimation animInst(configNode, modelRoot);
392 animInst.apply(node);
393 } else if (type == "billboard") {
394 SGBillboardAnimation animInst(configNode, modelRoot);
395 animInst.apply(node);
396 } else if (type == "blend") {
397 SGBlendAnimation animInst(configNode, modelRoot);
398 animInst.apply(node);
399 } else if (type == "dist-scale") {
400 SGDistScaleAnimation animInst(configNode, modelRoot);
401 animInst.apply(node);
402 } else if (type == "flash") {
403 SGFlashAnimation animInst(configNode, modelRoot);
404 animInst.apply(node);
405 } else if (type == "interaction") {
406 SGInteractionAnimation animInst(configNode, modelRoot);
407 animInst.apply(node);
408 } else if (type == "material") {
409 SGMaterialAnimation animInst(configNode, modelRoot, options);
410 animInst.apply(node);
411 } else if (type == "noshadow") {
412 SGShadowAnimation animInst(configNode, modelRoot);
413 animInst.apply(node);
414 } else if (type == "pick") {
415 SGPickAnimation animInst(configNode, modelRoot);
416 animInst.apply(node);
417 } else if (type == "range") {
418 SGRangeAnimation animInst(configNode, modelRoot);
419 animInst.apply(node);
420 } else if (type == "rotate" || type == "spin") {
421 SGRotateAnimation animInst(configNode, modelRoot);
422 animInst.apply(node);
423 } else if (type == "scale") {
424 SGScaleAnimation animInst(configNode, modelRoot);
425 animInst.apply(node);
426 } else if (type == "select") {
427 SGSelectAnimation animInst(configNode, modelRoot);
428 animInst.apply(node);
429 } else if (type == "shader") {
430 SGShaderAnimation animInst(configNode, modelRoot, options);
431 animInst.apply(node);
432 } else if (type == "textranslate" || type == "texrotate" ||
433 type == "texmultiple") {
434 SGTexTransformAnimation animInst(configNode, modelRoot);
435 animInst.apply(node);
436 } else if (type == "timed") {
437 SGTimedAnimation animInst(configNode, modelRoot);
438 animInst.apply(node);
439 } else if (type == "translate") {
440 SGTranslateAnimation animInst(configNode, modelRoot);
441 animInst.apply(node);
442 } else if (type == "null" || type == "none" || type.empty()) {
443 SGGroupAnimation animInst(configNode, modelRoot);
444 animInst.apply(node);
453 SGAnimation::apply(osg::Node* node)
455 // duh what a special case ...
456 if (_objectNames.empty()) {
457 osg::Group* group = node->asGroup();
459 osg::ref_ptr<osg::Group> animationGroup;
460 installInGroup(std::string(), *group, animationGroup);
467 SGAnimation::install(osg::Node& node)
471 node.setNodeMask( SG_NODEMASK_TERRAIN_BIT | node.getNodeMask());
473 node.setNodeMask(~SG_NODEMASK_TERRAIN_BIT & node.getNodeMask());
475 node.setNodeMask( SG_NODEMASK_CASTSHADOW_BIT | node.getNodeMask());
477 node.setNodeMask(~SG_NODEMASK_CASTSHADOW_BIT & node.getNodeMask());
481 SGAnimation::createAnimationGroup(osg::Group& parent)
483 // default implementation, we do not need a new group
484 // for every animation type. Usually animations that just change
485 // the StateSet of some parts of the model
490 SGAnimation::apply(osg::Group& group)
492 // the trick is to first traverse the children and then
493 // possibly splice in a new group node if required.
494 // Else we end up in a recursive loop where we infinitly insert new
498 // Note that this algorithm preserves the order of the child objects
499 // like they appear in the object-name tags.
500 // The timed animations require this
501 osg::ref_ptr<osg::Group> animationGroup;
502 std::list<std::string>::const_iterator nameIt;
503 for (nameIt = _objectNames.begin(); nameIt != _objectNames.end(); ++nameIt)
504 installInGroup(*nameIt, group, animationGroup);
508 SGAnimation::installInGroup(const std::string& name, osg::Group& group,
509 osg::ref_ptr<osg::Group>& animationGroup)
511 int i = group.getNumChildren() - 1;
512 for (; 0 <= i; --i) {
513 osg::Node* child = group.getChild(i);
515 // Check if this one is already processed
516 if (std::find(_installedAnimations.begin(),
517 _installedAnimations.end(), child)
518 != _installedAnimations.end())
521 if (name.empty() || child->getName() == name) {
522 // fire the installation of the animation
525 // create a group node on demand
526 if (!animationGroup.valid()) {
527 animationGroup = createAnimationGroup(group);
528 // Animation type that does not require a new group,
529 // in this case we can stop and look for the next object
530 if (animationGroup.valid() && !_name.empty())
531 animationGroup->setName(_name);
533 if (animationGroup.valid()) {
534 animationGroup->addChild(child);
535 group.removeChild(i);
538 // store that we already have processed this child node
539 // We can hit this one twice if an animation references some
540 // part of a subtree twice
541 _installedAnimations.push_back(child);
547 SGAnimation::removeMode(osg::Node& node, osg::StateAttribute::GLMode mode)
549 RemoveModeVisitor visitor(mode);
550 node.accept(visitor);
554 SGAnimation::removeAttribute(osg::Node& node, osg::StateAttribute::Type type)
556 RemoveAttributeVisitor visitor(type);
557 node.accept(visitor);
561 SGAnimation::removeTextureMode(osg::Node& node, unsigned unit,
562 osg::StateAttribute::GLMode mode)
564 RemoveTextureModeVisitor visitor(unit, mode);
565 node.accept(visitor);
569 SGAnimation::removeTextureAttribute(osg::Node& node, unsigned unit,
570 osg::StateAttribute::Type type)
572 RemoveTextureAttributeVisitor visitor(unit, type);
573 node.accept(visitor);
577 SGAnimation::setRenderBinToInherit(osg::Node& node)
579 BinToInheritVisitor visitor;
580 node.accept(visitor);
584 SGAnimation::cloneDrawables(osg::Node& node)
586 DrawableCloneVisitor visitor;
587 node.accept(visitor);
591 SGAnimation::getCondition() const
593 const SGPropertyNode* conditionNode = _configNode->getChild("condition");
596 return sgReadCondition(_modelRoot, conditionNode);
601 ////////////////////////////////////////////////////////////////////////
602 // Implementation of null animation
603 ////////////////////////////////////////////////////////////////////////
605 // Ok, that is to build a subgraph from different other
606 // graph nodes. I guess that this stems from the time where modellers
607 // could not build hierarchical trees ...
608 SGGroupAnimation::SGGroupAnimation(const SGPropertyNode* configNode,
609 SGPropertyNode* modelRoot):
610 SGAnimation(configNode, modelRoot)
615 SGGroupAnimation::createAnimationGroup(osg::Group& parent)
617 osg::Group* group = new osg::Group;
618 parent.addChild(group);
623 ////////////////////////////////////////////////////////////////////////
624 // Implementation of translate animation
625 ////////////////////////////////////////////////////////////////////////
627 class SGTranslateAnimation::UpdateCallback : public osg::NodeCallback {
629 UpdateCallback(SGCondition const* condition,
630 SGExpressiond const* animationValue) :
631 _condition(condition),
632 _animationValue(animationValue)
634 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
636 if (!_condition || _condition->test()) {
637 SGTranslateTransform* transform;
638 transform = static_cast<SGTranslateTransform*>(node);
639 transform->setValue(_animationValue->getValue());
644 SGSharedPtr<SGCondition const> _condition;
645 SGSharedPtr<SGExpressiond const> _animationValue;
648 SGTranslateAnimation::SGTranslateAnimation(const SGPropertyNode* configNode,
649 SGPropertyNode* modelRoot) :
650 SGAnimation(configNode, modelRoot)
652 _condition = getCondition();
653 SGSharedPtr<SGExpressiond> value;
654 value = read_value(configNode, modelRoot, "-m",
655 -SGLimitsd::max(), SGLimitsd::max());
656 _animationValue = value->simplify();
658 _initialValue = _animationValue->getValue();
662 if (configNode->hasValue("axis/x1-m")) {
664 v1[0] = configNode->getDoubleValue("axis/x1-m", 0);
665 v1[1] = configNode->getDoubleValue("axis/y1-m", 0);
666 v1[2] = configNode->getDoubleValue("axis/z1-m", 0);
667 v2[0] = configNode->getDoubleValue("axis/x2-m", 0);
668 v2[1] = configNode->getDoubleValue("axis/y2-m", 0);
669 v2[2] = configNode->getDoubleValue("axis/z2-m", 0);
672 _axis[0] = configNode->getDoubleValue("axis/x", 0);
673 _axis[1] = configNode->getDoubleValue("axis/y", 0);
674 _axis[2] = configNode->getDoubleValue("axis/z", 0);
676 if (8*SGLimitsd::min() < norm(_axis))
677 _axis = normalize(_axis);
681 SGTranslateAnimation::createAnimationGroup(osg::Group& parent)
683 SGTranslateTransform* transform = new SGTranslateTransform;
684 transform->setName("translate animation");
685 if (_animationValue && !_animationValue->isConst()) {
686 UpdateCallback* uc = new UpdateCallback(_condition, _animationValue);
687 transform->setUpdateCallback(uc);
689 transform->setAxis(_axis);
690 transform->setValue(_initialValue);
691 parent.addChild(transform);
696 ////////////////////////////////////////////////////////////////////////
697 // Implementation of rotate/spin animation
698 ////////////////////////////////////////////////////////////////////////
700 class SGRotateAnimation::UpdateCallback : public osg::NodeCallback {
702 UpdateCallback(SGCondition const* condition,
703 SGExpressiond const* animationValue) :
704 _condition(condition),
705 _animationValue(animationValue)
707 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
709 if (!_condition || _condition->test()) {
710 SGRotateTransform* transform;
711 transform = static_cast<SGRotateTransform*>(node);
712 transform->setAngleDeg(_animationValue->getValue());
717 SGSharedPtr<SGCondition const> _condition;
718 SGSharedPtr<SGExpressiond const> _animationValue;
721 class SGRotateAnimation::SpinUpdateCallback : public osg::NodeCallback {
723 SpinUpdateCallback(SGCondition const* condition,
724 SGExpressiond const* animationValue) :
725 _condition(condition),
726 _animationValue(animationValue),
729 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
731 if (!_condition || _condition->test()) {
732 SGRotateTransform* transform;
733 transform = static_cast<SGRotateTransform*>(node);
735 double t = nv->getFrameStamp()->getReferenceTime();
740 double velocity_rpms = _animationValue->getValue()/60;
741 double angle = transform->getAngleDeg();
742 angle += dt*velocity_rpms*360;
743 angle -= 360*floor(angle/360);
744 transform->setAngleDeg(angle);
749 SGSharedPtr<SGCondition const> _condition;
750 SGSharedPtr<SGExpressiond const> _animationValue;
754 SGRotateAnimation::SGRotateAnimation(const SGPropertyNode* configNode,
755 SGPropertyNode* modelRoot) :
756 SGAnimation(configNode, modelRoot)
758 std::string type = configNode->getStringValue("type", "");
759 _isSpin = (type == "spin");
761 _condition = getCondition();
762 SGSharedPtr<SGExpressiond> value;
763 value = read_value(configNode, modelRoot, "-deg",
764 -SGLimitsd::max(), SGLimitsd::max());
765 _animationValue = value->simplify();
767 _initialValue = _animationValue->getValue();
771 _center = SGVec3d::zeros();
772 if (configNode->hasValue("axis/x1-m")) {
774 v1[0] = configNode->getDoubleValue("axis/x1-m", 0);
775 v1[1] = configNode->getDoubleValue("axis/y1-m", 0);
776 v1[2] = configNode->getDoubleValue("axis/z1-m", 0);
777 v2[0] = configNode->getDoubleValue("axis/x2-m", 0);
778 v2[1] = configNode->getDoubleValue("axis/y2-m", 0);
779 v2[2] = configNode->getDoubleValue("axis/z2-m", 0);
780 _center = 0.5*(v1+v2);
783 _axis[0] = configNode->getDoubleValue("axis/x", 0);
784 _axis[1] = configNode->getDoubleValue("axis/y", 0);
785 _axis[2] = configNode->getDoubleValue("axis/z", 0);
787 if (8*SGLimitsd::min() < norm(_axis))
788 _axis = normalize(_axis);
790 _center[0] = configNode->getDoubleValue("center/x-m", _center[0]);
791 _center[1] = configNode->getDoubleValue("center/y-m", _center[1]);
792 _center[2] = configNode->getDoubleValue("center/z-m", _center[2]);
796 SGRotateAnimation::createAnimationGroup(osg::Group& parent)
798 SGRotateTransform* transform = new SGRotateTransform;
799 transform->setName("rotate animation");
801 SpinUpdateCallback* uc;
802 uc = new SpinUpdateCallback(_condition, _animationValue);
803 transform->setUpdateCallback(uc);
804 } else if (_animationValue || !_animationValue->isConst()) {
805 UpdateCallback* uc = new UpdateCallback(_condition, _animationValue);
806 transform->setUpdateCallback(uc);
808 transform->setCenter(_center);
809 transform->setAxis(_axis);
810 transform->setAngleDeg(_initialValue);
811 parent.addChild(transform);
816 ////////////////////////////////////////////////////////////////////////
817 // Implementation of scale animation
818 ////////////////////////////////////////////////////////////////////////
820 class SGScaleAnimation::UpdateCallback : public osg::NodeCallback {
822 UpdateCallback(const SGCondition* condition,
823 SGSharedPtr<const SGExpressiond> animationValue[3]) :
824 _condition(condition)
826 _animationValue[0] = animationValue[0];
827 _animationValue[1] = animationValue[1];
828 _animationValue[2] = animationValue[2];
830 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
832 if (!_condition || _condition->test()) {
833 SGScaleTransform* transform;
834 transform = static_cast<SGScaleTransform*>(node);
835 SGVec3d scale(_animationValue[0]->getValue(),
836 _animationValue[1]->getValue(),
837 _animationValue[2]->getValue());
838 transform->setScaleFactor(scale);
843 SGSharedPtr<SGCondition const> _condition;
844 SGSharedPtr<SGExpressiond const> _animationValue[3];
847 SGScaleAnimation::SGScaleAnimation(const SGPropertyNode* configNode,
848 SGPropertyNode* modelRoot) :
849 SGAnimation(configNode, modelRoot)
851 _condition = getCondition();
853 // default offset/factor for all directions
854 double offset = configNode->getDoubleValue("offset", 0);
855 double factor = configNode->getDoubleValue("factor", 1);
857 SGSharedPtr<SGExpressiond> inPropExpr;
859 std::string inputPropertyName;
860 inputPropertyName = configNode->getStringValue("property", "");
861 if (inputPropertyName.empty()) {
862 inPropExpr = new SGConstExpression<double>(0);
864 SGPropertyNode* inputProperty;
865 inputProperty = modelRoot->getNode(inputPropertyName, true);
866 inPropExpr = new SGPropertyExpression<double>(inputProperty);
869 SGInterpTable* interpTable = read_interpolation_table(configNode);
871 SGSharedPtr<SGExpressiond> value;
872 value = new SGInterpTableExpression<double>(inPropExpr, interpTable);
873 _animationValue[0] = value->simplify();
874 _animationValue[1] = value->simplify();
875 _animationValue[2] = value->simplify();
876 } else if (configNode->getBoolValue("use-personality", false)) {
877 SGSharedPtr<SGExpressiond> value;
878 value = new SGPersonalityScaleOffsetExpression(inPropExpr, configNode,
879 "x-factor", "x-offset",
881 double minClip = configNode->getDoubleValue("x-min", 0);
882 double maxClip = configNode->getDoubleValue("x-max", SGLimitsd::max());
883 value = new SGClipExpression<double>(value, minClip, maxClip);
884 _animationValue[0] = value->simplify();
886 value = new SGPersonalityScaleOffsetExpression(inPropExpr, configNode,
887 "y-factor", "y-offset",
889 minClip = configNode->getDoubleValue("y-min", 0);
890 maxClip = configNode->getDoubleValue("y-max", SGLimitsd::max());
891 value = new SGClipExpression<double>(value, minClip, maxClip);
892 _animationValue[1] = value->simplify();
894 value = new SGPersonalityScaleOffsetExpression(inPropExpr, configNode,
895 "z-factor", "z-offset",
897 minClip = configNode->getDoubleValue("z-min", 0);
898 maxClip = configNode->getDoubleValue("z-max", SGLimitsd::max());
899 value = new SGClipExpression<double>(value, minClip, maxClip);
900 _animationValue[2] = value->simplify();
902 SGSharedPtr<SGExpressiond> value;
903 value = read_factor_offset(configNode, inPropExpr, "x-factor", "x-offset");
904 double minClip = configNode->getDoubleValue("x-min", 0);
905 double maxClip = configNode->getDoubleValue("x-max", SGLimitsd::max());
906 value = new SGClipExpression<double>(value, minClip, maxClip);
907 _animationValue[0] = value->simplify();
909 value = read_factor_offset(configNode, inPropExpr, "y-factor", "y-offset");
910 minClip = configNode->getDoubleValue("y-min", 0);
911 maxClip = configNode->getDoubleValue("y-max", SGLimitsd::max());
912 value = new SGClipExpression<double>(value, minClip, maxClip);
913 _animationValue[1] = value->simplify();
915 value = read_factor_offset(configNode, inPropExpr, "z-factor", "z-offset");
916 minClip = configNode->getDoubleValue("z-min", 0);
917 maxClip = configNode->getDoubleValue("z-max", SGLimitsd::max());
918 value = new SGClipExpression<double>(value, minClip, maxClip);
919 _animationValue[2] = value->simplify();
921 _initialValue[0] = configNode->getDoubleValue("x-starting-scale", 1);
922 _initialValue[0] *= configNode->getDoubleValue("x-factor", factor);
923 _initialValue[0] += configNode->getDoubleValue("x-offset", offset);
924 _initialValue[1] = configNode->getDoubleValue("y-starting-scale", 1);
925 _initialValue[1] *= configNode->getDoubleValue("y-factor", factor);
926 _initialValue[1] += configNode->getDoubleValue("y-offset", offset);
927 _initialValue[2] = configNode->getDoubleValue("z-starting-scale", 1);
928 _initialValue[2] *= configNode->getDoubleValue("z-factor", factor);
929 _initialValue[2] += configNode->getDoubleValue("z-offset", offset);
930 _center[0] = configNode->getDoubleValue("center/x-m", 0);
931 _center[1] = configNode->getDoubleValue("center/y-m", 0);
932 _center[2] = configNode->getDoubleValue("center/z-m", 0);
936 SGScaleAnimation::createAnimationGroup(osg::Group& parent)
938 SGScaleTransform* transform = new SGScaleTransform;
939 transform->setName("scale animation");
940 transform->setCenter(_center);
941 transform->setScaleFactor(_initialValue);
942 UpdateCallback* uc = new UpdateCallback(_condition, _animationValue);
943 transform->setUpdateCallback(uc);
944 parent.addChild(transform);
949 // Don't create a new state state everytime we need GL_NORMALIZE!
953 Mutex normalizeMutex;
955 osg::StateSet* getNormalizeStateSet()
957 static osg::ref_ptr<osg::StateSet> normalizeStateSet;
958 ScopedLock<Mutex> lock(normalizeMutex);
959 if (!normalizeStateSet.valid()) {
960 normalizeStateSet = new osg::StateSet;
961 normalizeStateSet->setMode(GL_NORMALIZE, osg::StateAttribute::ON);
962 normalizeStateSet->setDataVariance(osg::Object::STATIC);
964 return normalizeStateSet.get();
968 ////////////////////////////////////////////////////////////////////////
969 // Implementation of dist scale animation
970 ////////////////////////////////////////////////////////////////////////
972 class SGDistScaleAnimation::Transform : public osg::Transform {
974 Transform(const SGPropertyNode* configNode)
976 setName(configNode->getStringValue("name", "dist scale animation"));
977 setReferenceFrame(RELATIVE_RF);
978 setStateSet(getNormalizeStateSet());
979 _factor = configNode->getFloatValue("factor", 1);
980 _offset = configNode->getFloatValue("offset", 0);
981 _min_v = configNode->getFloatValue("min", SGLimitsf::epsilon());
982 _max_v = configNode->getFloatValue("max", SGLimitsf::max());
983 _table = read_interpolation_table(configNode);
984 _center[0] = configNode->getFloatValue("center/x-m", 0);
985 _center[1] = configNode->getFloatValue("center/y-m", 0);
986 _center[2] = configNode->getFloatValue("center/z-m", 0);
988 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
989 osg::NodeVisitor* nv) const
991 osg::Matrix transform;
992 double scale_factor = computeScaleFactor(nv);
993 transform(0,0) = scale_factor;
994 transform(1,1) = scale_factor;
995 transform(2,2) = scale_factor;
996 transform(3,0) = _center[0]*(1 - scale_factor);
997 transform(3,1) = _center[1]*(1 - scale_factor);
998 transform(3,2) = _center[2]*(1 - scale_factor);
999 matrix.preMult(transform);
1003 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
1004 osg::NodeVisitor* nv) const
1006 double scale_factor = computeScaleFactor(nv);
1007 if (fabs(scale_factor) <= SGLimits<double>::min())
1009 osg::Matrix transform;
1010 double rScaleFactor = 1/scale_factor;
1011 transform(0,0) = rScaleFactor;
1012 transform(1,1) = rScaleFactor;
1013 transform(2,2) = rScaleFactor;
1014 transform(3,0) = _center[0]*(1 - rScaleFactor);
1015 transform(3,1) = _center[1]*(1 - rScaleFactor);
1016 transform(3,2) = _center[2]*(1 - rScaleFactor);
1017 matrix.postMult(transform);
1022 double computeScaleFactor(osg::NodeVisitor* nv) const
1027 double scale_factor = (_center.osg() - nv->getEyePoint()).length();
1029 scale_factor = _factor * scale_factor + _offset;
1031 scale_factor = _table->interpolate( scale_factor );
1033 if (scale_factor < _min_v)
1034 scale_factor = _min_v;
1035 if (scale_factor > _max_v)
1036 scale_factor = _max_v;
1038 return scale_factor;
1041 SGSharedPtr<SGInterpTable> _table;
1050 SGDistScaleAnimation::SGDistScaleAnimation(const SGPropertyNode* configNode,
1051 SGPropertyNode* modelRoot) :
1052 SGAnimation(configNode, modelRoot)
1057 SGDistScaleAnimation::createAnimationGroup(osg::Group& parent)
1059 Transform* transform = new Transform(getConfig());
1060 parent.addChild(transform);
1065 ////////////////////////////////////////////////////////////////////////
1066 // Implementation of flash animation
1067 ////////////////////////////////////////////////////////////////////////
1069 class SGFlashAnimation::Transform : public osg::Transform {
1071 Transform(const SGPropertyNode* configNode)
1073 setReferenceFrame(RELATIVE_RF);
1074 setName(configNode->getStringValue("name", "flash animation"));
1075 setStateSet(getNormalizeStateSet());
1077 _axis[0] = configNode->getFloatValue("axis/x", 0);
1078 _axis[1] = configNode->getFloatValue("axis/y", 0);
1079 _axis[2] = configNode->getFloatValue("axis/z", 1);
1082 _center[0] = configNode->getFloatValue("center/x-m", 0);
1083 _center[1] = configNode->getFloatValue("center/y-m", 0);
1084 _center[2] = configNode->getFloatValue("center/z-m", 0);
1086 _offset = configNode->getFloatValue("offset", 0);
1087 _factor = configNode->getFloatValue("factor", 1);
1088 _power = configNode->getFloatValue("power", 1);
1089 _two_sides = configNode->getBoolValue("two-sides", false);
1091 _min_v = configNode->getFloatValue("min", SGLimitsf::epsilon());
1092 _max_v = configNode->getFloatValue("max", 1);
1094 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
1095 osg::NodeVisitor* nv) const
1097 osg::Matrix transform;
1098 double scale_factor = computeScaleFactor(nv);
1099 transform(0,0) = scale_factor;
1100 transform(1,1) = scale_factor;
1101 transform(2,2) = scale_factor;
1102 transform(3,0) = _center[0]*(1 - scale_factor);
1103 transform(3,1) = _center[1]*(1 - scale_factor);
1104 transform(3,2) = _center[2]*(1 - scale_factor);
1105 matrix.preMult(transform);
1109 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
1110 osg::NodeVisitor* nv) const
1112 double scale_factor = computeScaleFactor(nv);
1113 if (fabs(scale_factor) <= SGLimits<double>::min())
1115 osg::Matrix transform;
1116 double rScaleFactor = 1/scale_factor;
1117 transform(0,0) = rScaleFactor;
1118 transform(1,1) = rScaleFactor;
1119 transform(2,2) = rScaleFactor;
1120 transform(3,0) = _center[0]*(1 - rScaleFactor);
1121 transform(3,1) = _center[1]*(1 - rScaleFactor);
1122 transform(3,2) = _center[2]*(1 - rScaleFactor);
1123 matrix.postMult(transform);
1128 double computeScaleFactor(osg::NodeVisitor* nv) const
1133 osg::Vec3 localEyeToCenter = nv->getEyePoint() - _center;
1134 localEyeToCenter.normalize();
1136 double cos_angle = localEyeToCenter*_axis;
1137 double scale_factor = 0;
1138 if ( _two_sides && cos_angle < 0 )
1139 scale_factor = _factor * pow( -cos_angle, _power ) + _offset;
1140 else if ( cos_angle > 0 )
1141 scale_factor = _factor * pow( cos_angle, _power ) + _offset;
1143 if ( scale_factor < _min_v )
1144 scale_factor = _min_v;
1145 if ( scale_factor > _max_v )
1146 scale_factor = _max_v;
1148 return scale_factor;
1151 virtual osg::BoundingSphere computeBound() const
1153 // avoid being culled away by small feature culling
1154 osg::BoundingSphere bs = osg::Group::computeBound();
1155 bs.radius() *= _max_v;
1162 double _power, _factor, _offset, _min_v, _max_v;
1167 SGFlashAnimation::SGFlashAnimation(const SGPropertyNode* configNode,
1168 SGPropertyNode* modelRoot) :
1169 SGAnimation(configNode, modelRoot)
1174 SGFlashAnimation::createAnimationGroup(osg::Group& parent)
1176 Transform* transform = new Transform(getConfig());
1177 parent.addChild(transform);
1182 ////////////////////////////////////////////////////////////////////////
1183 // Implementation of flash animation
1184 ////////////////////////////////////////////////////////////////////////
1186 class SGBillboardAnimation::Transform : public osg::Transform {
1188 Transform(const SGPropertyNode* configNode) :
1189 _spherical(configNode->getBoolValue("spherical", true))
1191 setReferenceFrame(RELATIVE_RF);
1192 setName(configNode->getStringValue("name", "billboard animation"));
1194 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
1195 osg::NodeVisitor* nv) const
1197 // More or less taken from plibs ssgCutout
1199 matrix(0,0) = 1; matrix(0,1) = 0; matrix(0,2) = 0;
1200 matrix(1,0) = 0; matrix(1,1) = 0; matrix(1,2) = -1;
1201 matrix(2,0) = 0; matrix(2,1) = 1; matrix(2,2) = 0;
1203 osg::Vec3 zAxis(matrix(2, 0), matrix(2, 1), matrix(2, 2));
1204 osg::Vec3 xAxis = osg::Vec3(0, 0, -1)^zAxis;
1205 osg::Vec3 yAxis = zAxis^xAxis;
1211 matrix(0,0) = xAxis[0]; matrix(0,1) = xAxis[1]; matrix(0,2) = xAxis[2];
1212 matrix(1,0) = yAxis[0]; matrix(1,1) = yAxis[1]; matrix(1,2) = yAxis[2];
1213 matrix(2,0) = zAxis[0]; matrix(2,1) = zAxis[1]; matrix(2,2) = zAxis[2];
1218 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
1219 osg::NodeVisitor* nv) const
1221 // Hmm, don't yet know how to get that back ...
1230 SGBillboardAnimation::SGBillboardAnimation(const SGPropertyNode* configNode,
1231 SGPropertyNode* modelRoot) :
1232 SGAnimation(configNode, modelRoot)
1237 SGBillboardAnimation::createAnimationGroup(osg::Group& parent)
1239 Transform* transform = new Transform(getConfig());
1240 parent.addChild(transform);
1245 ////////////////////////////////////////////////////////////////////////
1246 // Implementation of a range animation
1247 ////////////////////////////////////////////////////////////////////////
1249 class SGRangeAnimation::UpdateCallback : public osg::NodeCallback {
1251 UpdateCallback(const SGCondition* condition,
1252 const SGExpressiond* minAnimationValue,
1253 const SGExpressiond* maxAnimationValue,
1254 double minValue, double maxValue) :
1255 _condition(condition),
1256 _minAnimationValue(minAnimationValue),
1257 _maxAnimationValue(maxAnimationValue),
1258 _minStaticValue(minValue),
1259 _maxStaticValue(maxValue)
1261 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1263 osg::LOD* lod = static_cast<osg::LOD*>(node);
1264 if (!_condition || _condition->test()) {
1266 if (_minAnimationValue)
1267 minRange = _minAnimationValue->getValue();
1269 minRange = _minStaticValue;
1271 if (_maxAnimationValue)
1272 maxRange = _maxAnimationValue->getValue();
1274 maxRange = _maxStaticValue;
1275 lod->setRange(0, minRange, maxRange);
1277 lod->setRange(0, 0, SGLimitsf::max());
1283 SGSharedPtr<const SGCondition> _condition;
1284 SGSharedPtr<const SGExpressiond> _minAnimationValue;
1285 SGSharedPtr<const SGExpressiond> _maxAnimationValue;
1286 double _minStaticValue;
1287 double _maxStaticValue;
1290 SGRangeAnimation::SGRangeAnimation(const SGPropertyNode* configNode,
1291 SGPropertyNode* modelRoot) :
1292 SGAnimation(configNode, modelRoot)
1294 _condition = getCondition();
1296 std::string inputPropertyName;
1297 inputPropertyName = configNode->getStringValue("min-property", "");
1298 if (!inputPropertyName.empty()) {
1299 SGPropertyNode* inputProperty;
1300 inputProperty = modelRoot->getNode(inputPropertyName, true);
1301 SGSharedPtr<SGExpressiond> value;
1302 value = new SGPropertyExpression<double>(inputProperty);
1304 value = read_factor_offset(configNode, value, "min-factor", "min-offset");
1305 _minAnimationValue = value->simplify();
1307 inputPropertyName = configNode->getStringValue("max-property", "");
1308 if (!inputPropertyName.empty()) {
1309 SGPropertyNode* inputProperty;
1310 inputProperty = modelRoot->getNode(inputPropertyName.c_str(), true);
1312 SGSharedPtr<SGExpressiond> value;
1313 value = new SGPropertyExpression<double>(inputProperty);
1315 value = read_factor_offset(configNode, value, "max-factor", "max-offset");
1316 _maxAnimationValue = value->simplify();
1319 _initialValue[0] = configNode->getDoubleValue("min-m", 0);
1320 _initialValue[0] *= configNode->getDoubleValue("min-factor", 1);
1321 _initialValue[1] = configNode->getDoubleValue("max-m", SGLimitsf::max());
1322 _initialValue[1] *= configNode->getDoubleValue("max-factor", 1);
1326 SGRangeAnimation::createAnimationGroup(osg::Group& parent)
1328 osg::Group* group = new osg::Group;
1329 group->setName("range animation group");
1331 osg::LOD* lod = new osg::LOD;
1332 lod->setName("range animation node");
1333 parent.addChild(lod);
1335 lod->addChild(group, _initialValue[0], _initialValue[1]);
1336 lod->setCenterMode(osg::LOD::USE_BOUNDING_SPHERE_CENTER);
1337 lod->setRangeMode(osg::LOD::DISTANCE_FROM_EYE_POINT);
1338 if (_minAnimationValue || _maxAnimationValue || _condition) {
1340 uc = new UpdateCallback(_condition, _minAnimationValue, _maxAnimationValue,
1341 _initialValue[0], _initialValue[1]);
1342 lod->setUpdateCallback(uc);
1348 ////////////////////////////////////////////////////////////////////////
1349 // Implementation of a select animation
1350 ////////////////////////////////////////////////////////////////////////
1352 class SGSelectAnimation::UpdateCallback : public osg::NodeCallback {
1354 UpdateCallback(const SGCondition* condition) :
1355 _condition(condition)
1357 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1359 osg::Switch* sw = static_cast<osg::Switch*>(node);
1360 if (_condition->test())
1361 sw->setAllChildrenOn();
1363 sw->setAllChildrenOff();
1368 SGSharedPtr<SGCondition const> _condition;
1371 SGSelectAnimation::SGSelectAnimation(const SGPropertyNode* configNode,
1372 SGPropertyNode* modelRoot) :
1373 SGAnimation(configNode, modelRoot)
1378 SGSelectAnimation::createAnimationGroup(osg::Group& parent)
1380 // if no condition given, this is a noop.
1381 SGSharedPtr<SGCondition const> condition = getCondition();
1382 // trick, gets deleted with all its 'animated' children
1383 // when the animation installer returns
1385 return new osg::Group;
1387 osg::Switch* sw = new osg::Switch;
1388 sw->setName("select animation node");
1389 sw->setUpdateCallback(new UpdateCallback(condition));
1390 parent.addChild(sw);
1396 ////////////////////////////////////////////////////////////////////////
1397 // Implementation of alpha test animation
1398 ////////////////////////////////////////////////////////////////////////
1400 SGAlphaTestAnimation::SGAlphaTestAnimation(const SGPropertyNode* configNode,
1401 SGPropertyNode* modelRoot) :
1402 SGAnimation(configNode, modelRoot)
1408 // Keep one copy of the most common alpha test its state set.
1409 ReentrantMutex alphaTestMutex;
1410 osg::ref_ptr<osg::AlphaFunc> standardAlphaFunc;
1411 osg::ref_ptr<osg::StateSet> alphaFuncStateSet;
1413 osg::AlphaFunc* makeAlphaFunc(float clamp)
1415 ScopedLock<ReentrantMutex> lock(alphaTestMutex);
1416 if (osg::equivalent(clamp, 0.01f)) {
1417 if (standardAlphaFunc.valid())
1418 return standardAlphaFunc.get();
1421 osg::AlphaFunc* alphaFunc = new osg::AlphaFunc;
1422 alphaFunc->setFunction(osg::AlphaFunc::GREATER);
1423 alphaFunc->setReferenceValue(clamp);
1424 alphaFunc->setDataVariance(osg::Object::STATIC);
1425 if (osg::equivalent(clamp, 0.01f))
1426 standardAlphaFunc = alphaFunc;
1430 osg::StateSet* makeAlphaTestStateSet(float clamp)
1432 using namespace OpenThreads;
1433 ScopedLock<ReentrantMutex> lock(alphaTestMutex);
1434 if (osg::equivalent(clamp, 0.01f)) {
1435 if (alphaFuncStateSet.valid())
1436 return alphaFuncStateSet.get();
1438 osg::AlphaFunc* alphaFunc = makeAlphaFunc(clamp);
1439 osg::StateSet* stateSet = new osg::StateSet;
1440 stateSet->setAttributeAndModes(alphaFunc,
1441 (osg::StateAttribute::ON
1442 | osg::StateAttribute::OVERRIDE));
1443 stateSet->setDataVariance(osg::Object::STATIC);
1444 if (osg::equivalent(clamp, 0.01f))
1445 alphaFuncStateSet = stateSet;
1450 SGAlphaTestAnimation::install(osg::Node& node)
1452 SGAnimation::install(node);
1454 float alphaClamp = getConfig()->getFloatValue("alpha-factor", 0);
1455 osg::StateSet* stateSet = node.getStateSet();
1457 node.setStateSet(makeAlphaTestStateSet(alphaClamp));
1459 stateSet->setAttributeAndModes(makeAlphaFunc(alphaClamp),
1460 (osg::StateAttribute::ON
1461 | osg::StateAttribute::OVERRIDE));
1466 //////////////////////////////////////////////////////////////////////
1467 // Blend animation installer
1468 //////////////////////////////////////////////////////////////////////
1470 // XXX This needs to be replaced by something using TexEnvCombine to
1471 // change the blend factor. Changing the alpha values in the geometry
1473 class SGBlendAnimation::BlendVisitor : public osg::NodeVisitor {
1475 BlendVisitor(float blend) :
1476 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
1478 { setVisitorType(osg::NodeVisitor::NODE_VISITOR); }
1479 virtual void apply(osg::Node& node)
1481 updateStateSet(node.getStateSet());
1484 virtual void apply(osg::Geode& node)
1486 apply((osg::Node&)node);
1487 unsigned nDrawables = node.getNumDrawables();
1488 for (unsigned i = 0; i < nDrawables; ++i) {
1489 osg::Drawable* drawable = node.getDrawable(i);
1490 osg::Geometry* geometry = drawable->asGeometry();
1493 osg::Array* array = geometry->getColorArray();
1496 osg::Vec4Array* vec4Array = dynamic_cast<osg::Vec4Array*>(array);
1499 for (unsigned k = 0; k < vec4Array->size(); ++k) {
1500 (*vec4Array)[k][3] = _blend;
1503 updateStateSet(drawable->getStateSet());
1506 void updateStateSet(osg::StateSet* stateSet)
1510 osg::StateAttribute* stateAttribute;
1511 stateAttribute = stateSet->getAttribute(osg::StateAttribute::MATERIAL);
1512 if (!stateAttribute)
1514 osg::Material* material = dynamic_cast<osg::Material*>(stateAttribute);
1517 material->setAlpha(osg::Material::FRONT_AND_BACK, _blend);
1519 stateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
1520 stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
1522 stateSet->setRenderingHint(osg::StateSet::DEFAULT_BIN);
1529 class SGBlendAnimation::UpdateCallback : public osg::NodeCallback {
1531 UpdateCallback(const SGPropertyNode* configNode, const SGExpressiond* v) :
1535 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1537 double blend = _animationValue->getValue();
1538 if (blend != _prev_value) {
1539 _prev_value = blend;
1540 BlendVisitor visitor(1-blend);
1541 node->accept(visitor);
1547 SGSharedPtr<SGExpressiond const> _animationValue;
1551 SGBlendAnimation::SGBlendAnimation(const SGPropertyNode* configNode,
1552 SGPropertyNode* modelRoot)
1553 : SGAnimation(configNode, modelRoot),
1554 _animationValue(read_value(configNode, modelRoot, "", 0, 1))
1559 SGBlendAnimation::createAnimationGroup(osg::Group& parent)
1561 if (!_animationValue)
1564 osg::Group* group = new osg::Switch;
1565 group->setName("blend animation node");
1566 group->setUpdateCallback(new UpdateCallback(getConfig(), _animationValue));
1567 parent.addChild(group);
1572 SGBlendAnimation::install(osg::Node& node)
1574 SGAnimation::install(node);
1575 // make sure we do not change common geometries,
1576 // that also creates new display lists for these subgeometries.
1577 cloneDrawables(node);
1578 DoDrawArraysVisitor visitor;
1579 node.accept(visitor);
1583 //////////////////////////////////////////////////////////////////////
1584 // Timed animation installer
1585 //////////////////////////////////////////////////////////////////////
1589 class SGTimedAnimation::UpdateCallback : public osg::NodeCallback {
1591 UpdateCallback(const SGPropertyNode* configNode) :
1594 _duration_sec(configNode->getDoubleValue("duration-sec", 1)),
1595 _last_time_sec(SGLimitsd::max()),
1596 _use_personality(configNode->getBoolValue("use-personality", false))
1598 std::vector<SGSharedPtr<SGPropertyNode> > nodes;
1599 nodes = configNode->getChildren("branch-duration-sec");
1600 for (size_t i = 0; i < nodes.size(); ++i) {
1601 unsigned ind = nodes[ i ]->getIndex();
1602 while ( ind >= _durations.size() ) {
1603 _durations.push_back(DurationSpec(_duration_sec));
1605 SGPropertyNode_ptr rNode = nodes[i]->getChild("random");
1607 _durations[ind] = DurationSpec(nodes[ i ]->getDoubleValue());
1609 _durations[ind] = DurationSpec(rNode->getDoubleValue( "min", 0),
1610 rNode->getDoubleValue( "max", 1));
1614 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1616 assert(dynamic_cast<osg::Switch*>(node));
1617 osg::Switch* sw = static_cast<osg::Switch*>(node);
1619 unsigned nChildren = sw->getNumChildren();
1621 // blow up the durations vector to the required size
1622 while (_durations.size() < nChildren) {
1623 _durations.push_back(_duration_sec);
1625 // make sure the current index is an duration that really exists
1626 _current_index = _current_index % nChildren;
1628 // update the time and compute the current systems time value
1629 double t = nv->getFrameStamp()->getReferenceTime();
1630 if (_last_time_sec == SGLimitsd::max()) {
1633 double dt = t - _last_time_sec;
1634 if (_use_personality)
1635 dt *= 1 + 0.2*(0.5 - sg_random());
1640 double currentDuration = _durations[_current_index].get();
1641 while (currentDuration < _reminder) {
1642 _reminder -= currentDuration;
1643 _current_index = (_current_index + 1) % nChildren;
1644 currentDuration = _durations[_current_index].get();
1647 sw->setSingleChildOn(_current_index);
1653 struct DurationSpec {
1654 DurationSpec(double t) :
1655 minTime(SGMiscd::max(0.01, t)),
1656 maxTime(SGMiscd::max(0.01, t))
1658 DurationSpec(double t0, double t1) :
1659 minTime(SGMiscd::max(0.01, t0)),
1660 maxTime(SGMiscd::max(0.01, t1))
1663 { return minTime + sg_random()*(maxTime - minTime); }
1667 std::vector<DurationSpec> _durations;
1668 unsigned _current_index;
1670 double _duration_sec;
1671 double _last_time_sec;
1672 bool _use_personality;
1676 SGTimedAnimation::SGTimedAnimation(const SGPropertyNode* configNode,
1677 SGPropertyNode* modelRoot)
1678 : SGAnimation(configNode, modelRoot)
1683 SGTimedAnimation::createAnimationGroup(osg::Group& parent)
1685 osg::Switch* sw = new osg::Switch;
1686 sw->setName("timed animation node");
1687 sw->setUpdateCallback(new UpdateCallback(getConfig()));
1688 parent.addChild(sw);
1693 ////////////////////////////////////////////////////////////////////////
1694 // dynamically switch on/off shadows
1695 ////////////////////////////////////////////////////////////////////////
1697 class SGShadowAnimation::UpdateCallback : public osg::NodeCallback {
1699 UpdateCallback(const SGCondition* condition) :
1700 _condition(condition)
1702 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1704 if (_condition->test())
1705 node->setNodeMask( SG_NODEMASK_CASTSHADOW_BIT | node->getNodeMask());
1707 node->setNodeMask(~SG_NODEMASK_CASTSHADOW_BIT & node->getNodeMask());
1712 SGSharedPtr<const SGCondition> _condition;
1715 SGShadowAnimation::SGShadowAnimation(const SGPropertyNode* configNode,
1716 SGPropertyNode* modelRoot) :
1717 SGAnimation(configNode, modelRoot)
1722 SGShadowAnimation::createAnimationGroup(osg::Group& parent)
1724 SGSharedPtr<SGCondition const> condition = getCondition();
1728 osg::Group* group = new osg::Group;
1729 group->setName("shadow animation");
1730 group->setUpdateCallback(new UpdateCallback(condition));
1731 parent.addChild(group);
1736 ////////////////////////////////////////////////////////////////////////
1737 // Implementation of SGTexTransformAnimation
1738 ////////////////////////////////////////////////////////////////////////
1740 class SGTexTransformAnimation::Transform : public SGReferenced {
1745 virtual ~Transform()
1747 void setValue(double value)
1749 virtual void transform(osg::Matrix&) = 0;
1754 class SGTexTransformAnimation::Translation :
1755 public SGTexTransformAnimation::Transform {
1757 Translation(const SGVec3d& axis) :
1760 virtual void transform(osg::Matrix& matrix)
1763 set_translation(tmp, _value, _axis);
1764 matrix.preMult(tmp);
1770 class SGTexTransformAnimation::Rotation :
1771 public SGTexTransformAnimation::Transform {
1773 Rotation(const SGVec3d& axis, const SGVec3d& center) :
1777 virtual void transform(osg::Matrix& matrix)
1780 set_rotation(tmp, _value, _center, _axis);
1781 matrix.preMult(tmp);
1788 class SGTexTransformAnimation::UpdateCallback :
1789 public osg::StateAttribute::Callback {
1791 UpdateCallback(const SGCondition* condition) :
1792 _condition(condition)
1794 virtual void operator () (osg::StateAttribute* sa, osg::NodeVisitor*)
1796 if (!_condition || _condition->test()) {
1797 TransformList::const_iterator i;
1798 for (i = _transforms.begin(); i != _transforms.end(); ++i)
1799 i->transform->setValue(i->value->getValue());
1801 assert(dynamic_cast<osg::TexMat*>(sa));
1802 osg::TexMat* texMat = static_cast<osg::TexMat*>(sa);
1803 texMat->getMatrix().makeIdentity();
1804 TransformList::const_iterator i;
1805 for (i = _transforms.begin(); i != _transforms.end(); ++i)
1806 i->transform->transform(texMat->getMatrix());
1808 void appendTransform(Transform* transform, SGExpressiond* value)
1810 Entry entry = { transform, value };
1811 transform->transform(_matrix);
1812 _transforms.push_back(entry);
1817 SGSharedPtr<Transform> transform;
1818 SGSharedPtr<const SGExpressiond> value;
1820 typedef std::vector<Entry> TransformList;
1821 TransformList _transforms;
1822 SGSharedPtr<const SGCondition> _condition;
1823 osg::Matrix _matrix;
1826 SGTexTransformAnimation::SGTexTransformAnimation(const SGPropertyNode* configNode,
1827 SGPropertyNode* modelRoot) :
1828 SGAnimation(configNode, modelRoot)
1833 SGTexTransformAnimation::createAnimationGroup(osg::Group& parent)
1835 osg::Group* group = new osg::Group;
1836 group->setName("texture transform group");
1837 osg::StateSet* stateSet = group->getOrCreateStateSet();
1838 stateSet->setDataVariance(osg::Object::DYNAMIC);
1839 osg::TexMat* texMat = new osg::TexMat;
1840 UpdateCallback* updateCallback = new UpdateCallback(getCondition());
1841 // interpret the configs ...
1842 std::string type = getType();
1844 if (type == "textranslate") {
1845 appendTexTranslate(getConfig(), updateCallback);
1846 } else if (type == "texrotate") {
1847 appendTexRotate(getConfig(), updateCallback);
1848 } else if (type == "texmultiple") {
1849 std::vector<SGSharedPtr<SGPropertyNode> > transformConfigs;
1850 transformConfigs = getConfig()->getChildren("transform");
1851 for (unsigned i = 0; i < transformConfigs.size(); ++i) {
1852 std::string subtype = transformConfigs[i]->getStringValue("subtype", "");
1853 if (subtype == "textranslate")
1854 appendTexTranslate(transformConfigs[i], updateCallback);
1855 else if (subtype == "texrotate")
1856 appendTexRotate(transformConfigs[i], updateCallback);
1858 SG_LOG(SG_INPUT, SG_ALERT,
1859 "Ignoring unknown texture transform subtype");
1862 SG_LOG(SG_INPUT, SG_ALERT, "Ignoring unknown texture transform type");
1865 texMat->setUpdateCallback(updateCallback);
1866 stateSet->setTextureAttribute(0, texMat);
1867 parent.addChild(group);
1872 SGTexTransformAnimation::appendTexTranslate(const SGPropertyNode* config,
1873 UpdateCallback* updateCallback)
1875 std::string propertyName = config->getStringValue("property", "");
1876 SGSharedPtr<SGExpressiond> value;
1877 if (propertyName.empty())
1878 value = new SGConstExpression<double>(0);
1880 SGPropertyNode* inputProperty = getModelRoot()->getNode(propertyName, true);
1881 value = new SGPropertyExpression<double>(inputProperty);
1884 SGInterpTable* table = read_interpolation_table(config);
1886 value = new SGInterpTableExpression<double>(value, table);
1887 double biasValue = config->getDoubleValue("bias", 0);
1889 value = new SGBiasExpression<double>(value, biasValue);
1890 value = new SGStepExpression<double>(value,
1891 config->getDoubleValue("step", 0),
1892 config->getDoubleValue("scroll", 0));
1893 value = value->simplify();
1895 double biasValue = config->getDoubleValue("bias", 0);
1897 value = new SGBiasExpression<double>(value, biasValue);
1898 value = new SGStepExpression<double>(value,
1899 config->getDoubleValue("step", 0),
1900 config->getDoubleValue("scroll", 0));
1901 value = read_offset_factor(config, value, "factor", "offset");
1903 if (config->hasChild("min") || config->hasChild("max")) {
1904 double minClip = config->getDoubleValue("min", -SGLimitsd::max());
1905 double maxClip = config->getDoubleValue("max", SGLimitsd::max());
1906 value = new SGClipExpression<double>(value, minClip, maxClip);
1908 value = value->simplify();
1910 SGVec3d axis(config->getDoubleValue("axis/x", 0),
1911 config->getDoubleValue("axis/y", 0),
1912 config->getDoubleValue("axis/z", 0));
1913 Translation* translation;
1914 translation = new Translation(normalize(axis));
1915 translation->setValue(config->getDoubleValue("starting-position", 0));
1916 updateCallback->appendTransform(translation, value);
1920 SGTexTransformAnimation::appendTexRotate(const SGPropertyNode* config,
1921 UpdateCallback* updateCallback)
1923 std::string propertyName = config->getStringValue("property", "");
1924 SGSharedPtr<SGExpressiond> value;
1925 if (propertyName.empty())
1926 value = new SGConstExpression<double>(0);
1928 SGPropertyNode* inputProperty = getModelRoot()->getNode(propertyName, true);
1929 value = new SGPropertyExpression<double>(inputProperty);
1932 SGInterpTable* table = read_interpolation_table(config);
1934 value = new SGInterpTableExpression<double>(value, table);
1935 double biasValue = config->getDoubleValue("bias", 0);
1937 value = new SGBiasExpression<double>(value, biasValue);
1938 value = new SGStepExpression<double>(value,
1939 config->getDoubleValue("step", 0),
1940 config->getDoubleValue("scroll", 0));
1941 value = value->simplify();
1943 double biasValue = config->getDoubleValue("bias", 0);
1945 value = new SGBiasExpression<double>(value, biasValue);
1946 value = new SGStepExpression<double>(value,
1947 config->getDoubleValue("step", 0),
1948 config->getDoubleValue("scroll", 0));
1949 value = read_offset_factor(config, value, "factor", "offset-deg");
1951 if (config->hasChild("min-deg") || config->hasChild("max-deg")) {
1952 double minClip = config->getDoubleValue("min-deg", -SGLimitsd::max());
1953 double maxClip = config->getDoubleValue("max-deg", SGLimitsd::max());
1954 value = new SGClipExpression<double>(value, minClip, maxClip);
1956 value = value->simplify();
1958 SGVec3d axis(config->getDoubleValue("axis/x", 0),
1959 config->getDoubleValue("axis/y", 0),
1960 config->getDoubleValue("axis/z", 0));
1961 SGVec3d center(config->getDoubleValue("center/x", 0),
1962 config->getDoubleValue("center/y", 0),
1963 config->getDoubleValue("center/z", 0));
1965 rotation = new Rotation(normalize(axis), center);
1966 rotation->setValue(config->getDoubleValue("starting-position-deg", 0));
1967 updateCallback->appendTransform(rotation, value);
1971 ////////////////////////////////////////////////////////////////////////
1972 // Implementation of SGPickAnimation
1973 ////////////////////////////////////////////////////////////////////////
1975 class SGPickAnimation::PickCallback : public SGPickCallback {
1977 PickCallback(const SGPropertyNode* configNode,
1978 SGPropertyNode* modelRoot) :
1979 _repeatable(configNode->getBoolValue("repeatable", false)),
1980 _repeatInterval(configNode->getDoubleValue("interval-sec", 0.1))
1982 SG_LOG(SG_INPUT, SG_DEBUG, "Reading all bindings");
1983 std::vector<SGPropertyNode_ptr> bindings;
1985 bindings = configNode->getChildren("button");
1986 for (unsigned int i = 0; i < bindings.size(); ++i) {
1987 _buttons.push_back( bindings[i]->getIntValue() );
1989 bindings = configNode->getChildren("binding");
1990 for (unsigned int i = 0; i < bindings.size(); ++i) {
1991 _bindingsDown.push_back(new SGBinding(bindings[i], modelRoot));
1994 const SGPropertyNode* upNode = configNode->getChild("mod-up");
1997 bindings = upNode->getChildren("binding");
1998 for (unsigned int i = 0; i < bindings.size(); ++i) {
1999 _bindingsUp.push_back(new SGBinding(bindings[i], modelRoot));
2002 virtual bool buttonPressed(int button, const Info&)
2005 for( std::vector<int>::iterator it = _buttons.begin(); it != _buttons.end(); it++ ) {
2006 if( *it == button ) {
2013 SGBindingList::const_iterator i;
2014 for (i = _bindingsDown.begin(); i != _bindingsDown.end(); ++i)
2019 virtual void buttonReleased(void)
2021 SGBindingList::const_iterator i;
2022 for (i = _bindingsUp.begin(); i != _bindingsUp.end(); ++i)
2025 virtual void update(double dt)
2031 while (_repeatInterval < _repeatTime) {
2032 _repeatTime -= _repeatInterval;
2033 SGBindingList::const_iterator i;
2034 for (i = _bindingsDown.begin(); i != _bindingsDown.end(); ++i)
2039 SGBindingList _bindingsDown;
2040 SGBindingList _bindingsUp;
2041 std::vector<int> _buttons;
2043 double _repeatInterval;
2047 SGPickAnimation::SGPickAnimation(const SGPropertyNode* configNode,
2048 SGPropertyNode* modelRoot) :
2049 SGAnimation(configNode, modelRoot)
2054 SGPickAnimation::createAnimationGroup(osg::Group& parent)
2056 osg::Group* commonGroup = new osg::Group;
2058 // Contains the normal geometry that is interactive
2059 osg::ref_ptr<osg::Group> normalGroup = new osg::Group;
2060 normalGroup->setName("pick normal group");
2061 normalGroup->addChild(commonGroup);
2063 // Used to render the geometry with just yellow edges
2064 osg::Group* highlightGroup = new osg::Group;
2065 highlightGroup->setName("pick highlight group");
2066 highlightGroup->setNodeMask(SG_NODEMASK_PICK_BIT);
2067 highlightGroup->addChild(commonGroup);
2068 SGSceneUserData* ud;
2069 ud = SGSceneUserData::getOrCreateSceneUserData(commonGroup);
2070 std::vector<SGPropertyNode_ptr> actions;
2071 actions = getConfig()->getChildren("action");
2072 for (unsigned int i = 0; i < actions.size(); ++i)
2073 ud->addPickCallback(new PickCallback(actions[i], getModelRoot()));
2075 // prepare a state set that paints the edges of this object yellow
2076 osg::StateSet* stateSet = highlightGroup->getOrCreateStateSet();
2077 stateSet->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE);
2079 osg::PolygonOffset* polygonOffset = new osg::PolygonOffset;
2080 polygonOffset->setFactor(-1);
2081 polygonOffset->setUnits(-1);
2082 stateSet->setAttribute(polygonOffset, osg::StateAttribute::OVERRIDE);
2083 stateSet->setMode(GL_POLYGON_OFFSET_LINE, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
2085 osg::PolygonMode* polygonMode = new osg::PolygonMode;
2086 polygonMode->setMode(osg::PolygonMode::FRONT_AND_BACK,
2087 osg::PolygonMode::LINE);
2088 stateSet->setAttribute(polygonMode, osg::StateAttribute::OVERRIDE);
2090 osg::Material* material = new osg::Material;
2091 material->setColorMode(osg::Material::OFF);
2092 material->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(1, 1, 0, 1));
2093 material->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(1, 1, 0, 1));
2094 material->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(1, 1, 0, 1));
2095 material->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0, 0, 0, 0));
2096 stateSet->setAttribute(material, osg::StateAttribute::OVERRIDE);
2098 // Only add normal geometry if configured
2099 if (getConfig()->getBoolValue("visible", true))
2100 parent.addChild(normalGroup.get());
2101 parent.addChild(highlightGroup);