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/Atomic>
16 #include <OpenThreads/Mutex>
17 #include <OpenThreads/ReentrantMutex>
18 #include <OpenThreads/ScopedLock>
20 #include <osg/AlphaFunc>
21 #include <osg/Drawable>
23 #include <osg/Geometry>
27 #include <osg/PolygonMode>
28 #include <osg/PolygonOffset>
29 #include <osg/StateSet>
32 #include <osg/Texture2D>
33 #include <osg/Transform>
34 #include <osg/Uniform>
35 #include <osgDB/ReadFile>
36 #include <osgDB/Registry>
37 #include <osgDB/Input>
38 #include <osgDB/ParameterOutput>
41 #include <simgear/math/interpolater.hxx>
42 #include <simgear/props/condition.hxx>
43 #include <simgear/props/props.hxx>
44 #include <simgear/structure/SGBinding.hxx>
45 #include <simgear/scene/material/EffectGeode.hxx>
46 #include <simgear/scene/material/EffectCullVisitor.hxx>
47 #include <simgear/scene/util/DeletionManager.hxx>
48 #include <simgear/scene/util/OsgMath.hxx>
49 #include <simgear/scene/util/SGNodeMasks.hxx>
50 #include <simgear/scene/util/SGSceneUserData.hxx>
51 #include <simgear/scene/util/SGStateAttributeVisitor.hxx>
52 #include <simgear/scene/util/StateAttributeFactory.hxx>
54 #include "animation.hxx"
57 #include "SGTranslateTransform.hxx"
58 #include "SGMaterialAnimation.hxx"
59 #include "SGRotateTransform.hxx"
60 #include "SGScaleTransform.hxx"
61 #include "SGInteractionAnimation.hxx"
63 #include "ConditionNode.hxx"
65 using OpenThreads::Mutex;
66 using OpenThreads::ReentrantMutex;
67 using OpenThreads::ScopedLock;
69 using namespace simgear;
71 ////////////////////////////////////////////////////////////////////////
72 // Static utility functions.
73 ////////////////////////////////////////////////////////////////////////
76 * Set up the transform matrix for a translation.
79 set_translation (osg::Matrix &matrix, double position_m, const SGVec3d &axis)
81 SGVec3d xyz = axis * position_m;
82 matrix.makeIdentity();
83 matrix(3, 0) = xyz[0];
84 matrix(3, 1) = xyz[1];
85 matrix(3, 2) = xyz[2];
89 * Read an interpolation table from properties.
91 static SGInterpTable *
92 read_interpolation_table(const SGPropertyNode* props)
94 const SGPropertyNode* table_node = props->getNode("interpolation");
97 return new SGInterpTable(table_node);
101 unit_string(const char* value, const char* unit)
103 return std::string(value) + unit;
106 class SGPersonalityScaleOffsetExpression : public SGUnaryExpression<double> {
108 SGPersonalityScaleOffsetExpression(SGExpression<double>* expr,
109 SGPropertyNode const* config,
110 const std::string& scalename,
111 const std::string& offsetname,
113 double defOffset = 0) :
114 SGUnaryExpression<double>(expr),
115 _scale(config, scalename.c_str(), defScale),
116 _offset(config, offsetname.c_str(), defOffset)
118 void setScale(double scale)
120 void setOffset(double offset)
121 { _offset = offset; }
123 virtual void eval(double& value, const simgear::expression::Binding* b) const
127 value = _offset + _scale*getOperand()->getValue(b);
130 virtual bool isConst() const { return false; }
133 mutable SGPersonalityParameter<double> _scale;
134 mutable SGPersonalityParameter<double> _offset;
138 static SGExpressiond*
139 read_factor_offset(const SGPropertyNode* configNode, SGExpressiond* expr,
140 const std::string& factor, const std::string& offset)
142 double factorValue = configNode->getDoubleValue(factor, 1);
143 if (factorValue != 1)
144 expr = new SGScaleExpression<double>(expr, factorValue);
145 double offsetValue = configNode->getDoubleValue(offset, 0);
146 if (offsetValue != 0)
147 expr = new SGBiasExpression<double>(expr, offsetValue);
151 static SGExpressiond*
152 read_offset_factor(const SGPropertyNode* configNode, SGExpressiond* expr,
153 const std::string& factor, const std::string& offset)
155 double offsetValue = configNode->getDoubleValue(offset, 0);
156 if (offsetValue != 0)
157 expr = new SGBiasExpression<double>(expr, offsetValue);
158 double factorValue = configNode->getDoubleValue(factor, 1);
159 if (factorValue != 1)
160 expr = new SGScaleExpression<double>(expr, factorValue);
165 read_value(const SGPropertyNode* configNode, SGPropertyNode* modelRoot,
166 const char* unit, double defMin, double defMax)
168 const SGPropertyNode * expression = configNode->getNode( "expression" );
169 if( expression != NULL )
170 return SGReadDoubleExpression( modelRoot, expression->getChild(0) );
172 SGExpression<double>* value = 0;
174 std::string inputPropertyName = configNode->getStringValue("property", "");
175 if (inputPropertyName.empty()) {
176 std::string spos = unit_string("starting-position", unit);
177 double initPos = configNode->getDoubleValue(spos, 0);
178 value = new SGConstExpression<double>(initPos);
180 SGPropertyNode* inputProperty;
181 inputProperty = modelRoot->getNode(inputPropertyName, true);
182 value = new SGPropertyExpression<double>(inputProperty);
185 SGInterpTable* interpTable = read_interpolation_table(configNode);
187 return new SGInterpTableExpression<double>(value, interpTable);
189 std::string offset = unit_string("offset", unit);
190 std::string min = unit_string("min", unit);
191 std::string max = unit_string("max", unit);
193 if (configNode->getBoolValue("use-personality", false)) {
194 value = new SGPersonalityScaleOffsetExpression(value, configNode,
197 value = read_factor_offset(configNode, value, "factor", offset);
200 double minClip = configNode->getDoubleValue(min, defMin);
201 double maxClip = configNode->getDoubleValue(max, defMax);
202 if (minClip > SGMiscd::min(SGLimitsd::min(), -SGLimitsd::max()) ||
203 maxClip < SGLimitsd::max())
204 value = new SGClipExpression<double>(value, minClip, maxClip);
212 ////////////////////////////////////////////////////////////////////////
213 // Animation installer
214 ////////////////////////////////////////////////////////////////////////
216 class SGAnimation::RemoveModeVisitor : public SGStateAttributeVisitor {
218 RemoveModeVisitor(osg::StateAttribute::GLMode mode) :
221 virtual void apply(osg::StateSet* stateSet)
225 stateSet->removeMode(_mode);
228 osg::StateAttribute::GLMode _mode;
231 class SGAnimation::RemoveAttributeVisitor : public SGStateAttributeVisitor {
233 RemoveAttributeVisitor(osg::StateAttribute::Type type) :
236 virtual void apply(osg::StateSet* stateSet)
240 while (stateSet->getAttribute(_type)) {
241 stateSet->removeAttribute(_type);
245 osg::StateAttribute::Type _type;
248 class SGAnimation::RemoveTextureModeVisitor : public SGStateAttributeVisitor {
250 RemoveTextureModeVisitor(unsigned unit, osg::StateAttribute::GLMode mode) :
254 virtual void apply(osg::StateSet* stateSet)
258 stateSet->removeTextureMode(_unit, _mode);
262 osg::StateAttribute::GLMode _mode;
265 class SGAnimation::RemoveTextureAttributeVisitor :
266 public SGStateAttributeVisitor {
268 RemoveTextureAttributeVisitor(unsigned unit,
269 osg::StateAttribute::Type type) :
273 virtual void apply(osg::StateSet* stateSet)
277 while (stateSet->getTextureAttribute(_unit, _type)) {
278 stateSet->removeTextureAttribute(_unit, _type);
283 osg::StateAttribute::Type _type;
286 class SGAnimation::BinToInheritVisitor : public SGStateAttributeVisitor {
288 virtual void apply(osg::StateSet* stateSet)
292 stateSet->setRenderBinToInherit();
296 class SGAnimation::DrawableCloneVisitor : public osg::NodeVisitor {
298 DrawableCloneVisitor() :
299 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
301 void apply(osg::Geode& geode)
303 for (unsigned i = 0 ; i < geode.getNumDrawables(); ++i) {
304 osg::CopyOp copyOp(osg::CopyOp::DEEP_COPY_ALL &
305 ~osg::CopyOp::DEEP_COPY_TEXTURES);
306 geode.setDrawable(i, copyOp(geode.getDrawable(i)));
313 // Set all drawables to not use display lists. OSG will use
314 // glDrawArrays instead.
315 struct DoDrawArraysVisitor : public osg::NodeVisitor {
316 DoDrawArraysVisitor() :
317 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
319 void apply(osg::Geode& geode)
324 for (int i = 0; i < (int)geode.getNumDrawables(); ++i)
325 geode.getDrawable(i)->setUseDisplayList(false);
330 SGAnimation::SGAnimation(const SGPropertyNode* configNode,
331 SGPropertyNode* modelRoot) :
332 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
334 _configNode(configNode),
335 _modelRoot(modelRoot)
337 _name = configNode->getStringValue("name", "");
338 _enableHOT = configNode->getBoolValue("enable-hot", true);
339 std::vector<SGPropertyNode_ptr> objectNames =
340 configNode->getChildren("object-name");
341 for (unsigned i = 0; i < objectNames.size(); ++i)
342 _objectNames.push_back(objectNames[i]->getStringValue());
345 SGAnimation::~SGAnimation()
349 std::list<std::string>::const_iterator i;
351 for (i = _objectNames.begin(); i != _objectNames.end(); ++i)
361 SG_LOG(SG_IO, SG_ALERT, "Could not find at least one of the following"
362 " objects for animation: " << info);
368 SGAnimation::animate(osg::Node* node, const SGPropertyNode* configNode,
369 SGPropertyNode* modelRoot,
370 const osgDB::Options* options,
371 const string &path, int i)
373 std::string type = configNode->getStringValue("type", "none");
374 if (type == "alpha-test") {
375 SGAlphaTestAnimation animInst(configNode, modelRoot);
376 animInst.apply(node);
377 } else if (type == "billboard") {
378 SGBillboardAnimation animInst(configNode, modelRoot);
379 animInst.apply(node);
380 } else if (type == "blend") {
381 SGBlendAnimation animInst(configNode, modelRoot);
382 animInst.apply(node);
383 } else if (type == "dist-scale") {
384 SGDistScaleAnimation animInst(configNode, modelRoot);
385 animInst.apply(node);
386 } else if (type == "flash") {
387 SGFlashAnimation animInst(configNode, modelRoot);
388 animInst.apply(node);
389 } else if (type == "interaction") {
390 SGInteractionAnimation animInst(configNode, modelRoot);
391 animInst.apply(node);
392 } else if (type == "material") {
393 SGMaterialAnimation animInst(configNode, modelRoot, options, path);
394 animInst.apply(node);
395 } else if (type == "noshadow") {
396 SGShadowAnimation animInst(configNode, modelRoot);
397 animInst.apply(node);
398 } else if (type == "pick") {
399 SGPickAnimation animInst(configNode, modelRoot);
400 animInst.apply(node);
401 } else if (type == "range") {
402 SGRangeAnimation animInst(configNode, modelRoot);
403 animInst.apply(node);
404 } else if (type == "rotate" || type == "spin") {
405 SGRotateAnimation animInst(configNode, modelRoot);
406 animInst.apply(node);
407 } else if (type == "scale") {
408 SGScaleAnimation animInst(configNode, modelRoot);
409 animInst.apply(node);
410 } else if (type == "select") {
411 SGSelectAnimation animInst(configNode, modelRoot);
412 animInst.apply(node);
413 } else if (type == "shader") {
414 SGShaderAnimation animInst(configNode, modelRoot, options);
415 animInst.apply(node);
416 } else if (type == "textranslate" || type == "texrotate" ||
417 type == "texmultiple") {
418 SGTexTransformAnimation animInst(configNode, modelRoot);
419 animInst.apply(node);
420 } else if (type == "timed") {
421 SGTimedAnimation animInst(configNode, modelRoot);
422 animInst.apply(node);
423 } else if (type == "translate") {
424 SGTranslateAnimation animInst(configNode, modelRoot);
425 animInst.apply(node);
426 } else if (type == "light") {
427 SGLightAnimation animInst(configNode, modelRoot, options, path, i);
428 animInst.apply(node);
429 } else if (type == "null" || type == "none" || type.empty()) {
430 SGGroupAnimation animInst(configNode, modelRoot);
431 animInst.apply(node);
440 SGAnimation::apply(osg::Node* node)
442 // duh what a special case ...
443 if (_objectNames.empty()) {
444 osg::Group* group = node->asGroup();
446 osg::ref_ptr<osg::Group> animationGroup;
447 installInGroup(std::string(), *group, animationGroup);
454 SGAnimation::install(osg::Node& node)
458 node.setNodeMask( SG_NODEMASK_TERRAIN_BIT | node.getNodeMask());
460 node.setNodeMask(~SG_NODEMASK_TERRAIN_BIT & node.getNodeMask());
464 SGAnimation::createAnimationGroup(osg::Group& parent)
466 // default implementation, we do not need a new group
467 // for every animation type. Usually animations that just change
468 // the StateSet of some parts of the model
473 SGAnimation::apply(osg::Group& group)
475 // the trick is to first traverse the children and then
476 // possibly splice in a new group node if required.
477 // Else we end up in a recursive loop where we infinitly insert new
481 // Note that this algorithm preserves the order of the child objects
482 // like they appear in the object-name tags.
483 // The timed animations require this
484 osg::ref_ptr<osg::Group> animationGroup;
485 std::list<std::string>::const_iterator nameIt;
486 for (nameIt = _objectNames.begin(); nameIt != _objectNames.end(); ++nameIt)
487 installInGroup(*nameIt, group, animationGroup);
491 SGAnimation::installInGroup(const std::string& name, osg::Group& group,
492 osg::ref_ptr<osg::Group>& animationGroup)
494 int i = group.getNumChildren() - 1;
495 for (; 0 <= i; --i) {
496 osg::Node* child = group.getChild(i);
498 // Check if this one is already processed
499 if (std::find(_installedAnimations.begin(),
500 _installedAnimations.end(), child)
501 != _installedAnimations.end())
504 if (name.empty() || child->getName() == name) {
505 // fire the installation of the animation
508 // create a group node on demand
509 if (!animationGroup.valid()) {
510 animationGroup = createAnimationGroup(group);
511 // Animation type that does not require a new group,
512 // in this case we can stop and look for the next object
513 if (animationGroup.valid() && !_name.empty())
514 animationGroup->setName(_name);
516 if (animationGroup.valid()) {
517 animationGroup->addChild(child);
518 group.removeChild(i);
521 // store that we already have processed this child node
522 // We can hit this one twice if an animation references some
523 // part of a subtree twice
524 _installedAnimations.push_back(child);
530 SGAnimation::removeMode(osg::Node& node, osg::StateAttribute::GLMode mode)
532 RemoveModeVisitor visitor(mode);
533 node.accept(visitor);
537 SGAnimation::removeAttribute(osg::Node& node, osg::StateAttribute::Type type)
539 RemoveAttributeVisitor visitor(type);
540 node.accept(visitor);
544 SGAnimation::removeTextureMode(osg::Node& node, unsigned unit,
545 osg::StateAttribute::GLMode mode)
547 RemoveTextureModeVisitor visitor(unit, mode);
548 node.accept(visitor);
552 SGAnimation::removeTextureAttribute(osg::Node& node, unsigned unit,
553 osg::StateAttribute::Type type)
555 RemoveTextureAttributeVisitor visitor(unit, type);
556 node.accept(visitor);
560 SGAnimation::setRenderBinToInherit(osg::Node& node)
562 BinToInheritVisitor visitor;
563 node.accept(visitor);
567 SGAnimation::cloneDrawables(osg::Node& node)
569 DrawableCloneVisitor visitor;
570 node.accept(visitor);
574 SGAnimation::getCondition() const
576 const SGPropertyNode* conditionNode = _configNode->getChild("condition");
579 return sgReadCondition(_modelRoot, conditionNode);
584 ////////////////////////////////////////////////////////////////////////
585 // Implementation of null animation
586 ////////////////////////////////////////////////////////////////////////
588 // Ok, that is to build a subgraph from different other
589 // graph nodes. I guess that this stems from the time where modellers
590 // could not build hierarchical trees ...
591 SGGroupAnimation::SGGroupAnimation(const SGPropertyNode* configNode,
592 SGPropertyNode* modelRoot):
593 SGAnimation(configNode, modelRoot)
598 SGGroupAnimation::createAnimationGroup(osg::Group& parent)
600 osg::Group* group = new osg::Group;
601 parent.addChild(group);
606 ////////////////////////////////////////////////////////////////////////
607 // Implementation of translate animation
608 ////////////////////////////////////////////////////////////////////////
610 class SGTranslateAnimation::UpdateCallback : public osg::NodeCallback {
612 UpdateCallback(SGCondition const* condition,
613 SGExpressiond const* animationValue) :
614 _condition(condition),
615 _animationValue(animationValue)
617 setName("SGTranslateAnimation::UpdateCallback");
619 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
621 if (!_condition || _condition->test()) {
622 SGTranslateTransform* transform;
623 transform = static_cast<SGTranslateTransform*>(node);
624 transform->setValue(_animationValue->getValue());
629 SGSharedPtr<SGCondition const> _condition;
630 SGSharedPtr<SGExpressiond const> _animationValue;
633 SGTranslateAnimation::SGTranslateAnimation(const SGPropertyNode* configNode,
634 SGPropertyNode* modelRoot) :
635 SGAnimation(configNode, modelRoot)
637 _condition = getCondition();
638 SGSharedPtr<SGExpressiond> value;
639 value = read_value(configNode, modelRoot, "-m",
640 -SGLimitsd::max(), SGLimitsd::max());
641 _animationValue = value->simplify();
643 _initialValue = _animationValue->getValue();
647 if (configNode->hasValue("axis/x1-m")) {
649 v1[0] = configNode->getDoubleValue("axis/x1-m", 0);
650 v1[1] = configNode->getDoubleValue("axis/y1-m", 0);
651 v1[2] = configNode->getDoubleValue("axis/z1-m", 0);
652 v2[0] = configNode->getDoubleValue("axis/x2-m", 0);
653 v2[1] = configNode->getDoubleValue("axis/y2-m", 0);
654 v2[2] = configNode->getDoubleValue("axis/z2-m", 0);
657 _axis[0] = configNode->getDoubleValue("axis/x", 0);
658 _axis[1] = configNode->getDoubleValue("axis/y", 0);
659 _axis[2] = configNode->getDoubleValue("axis/z", 0);
661 if (8*SGLimitsd::min() < norm(_axis))
662 _axis = normalize(_axis);
666 SGTranslateAnimation::createAnimationGroup(osg::Group& parent)
668 SGTranslateTransform* transform = new SGTranslateTransform;
669 transform->setName("translate animation");
670 if (_animationValue && !_animationValue->isConst()) {
671 UpdateCallback* uc = new UpdateCallback(_condition, _animationValue);
672 transform->setUpdateCallback(uc);
674 transform->setAxis(_axis);
675 transform->setValue(_initialValue);
676 parent.addChild(transform);
681 ////////////////////////////////////////////////////////////////////////
682 // Implementation of rotate/spin animation
683 ////////////////////////////////////////////////////////////////////////
685 class SGRotAnimTransform : public SGRotateTransform
688 SGRotAnimTransform();
689 SGRotAnimTransform(const SGRotAnimTransform&,
690 const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY);
691 META_Node(simgear, SGRotAnimTransform);
692 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
693 osg::NodeVisitor* nv) const;
694 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
695 osg::NodeVisitor* nv) const;
696 SGSharedPtr<SGCondition const> _condition;
697 SGSharedPtr<SGExpressiond const> _animationValue;
698 // used when condition is false
699 mutable double _lastAngle;
702 SGRotAnimTransform::SGRotAnimTransform()
707 SGRotAnimTransform::SGRotAnimTransform(const SGRotAnimTransform& rhs,
708 const osg::CopyOp& copyop)
709 : SGRotateTransform(rhs, copyop), _condition(rhs._condition),
710 _animationValue(rhs._animationValue), _lastAngle(rhs._lastAngle)
714 bool SGRotAnimTransform::computeLocalToWorldMatrix(osg::Matrix& matrix,
715 osg::NodeVisitor* nv) const
718 if (!_condition || _condition->test()) {
719 angle = _animationValue->getValue();
724 double angleRad = SGMiscd::deg2rad(angle);
725 if (_referenceFrame == RELATIVE_RF) {
728 set_rotation(tmp, angleRad, getCenter(), getAxis());
732 SGRotateTransform::set_rotation(tmp, angleRad, getCenter(), getAxis());
738 bool SGRotAnimTransform::computeWorldToLocalMatrix(osg::Matrix& matrix,
739 osg::NodeVisitor* nv) const
742 if (!_condition || _condition->test()) {
743 angle = _animationValue->getValue();
748 double angleRad = SGMiscd::deg2rad(angle);
749 if (_referenceFrame == RELATIVE_RF) {
752 set_rotation(tmp, -angleRad, getCenter(), getAxis());
753 matrix.postMult(tmp);
756 set_rotation(tmp, -angleRad, getCenter(), getAxis());
763 class SpinAnimCallback : public osg::NodeCallback {
765 SpinAnimCallback(SGCondition const* condition,
766 SGExpressiond const* animationValue,
767 double initialValue = 0.0) :
768 _condition(condition),
769 _animationValue(animationValue),
770 _initialValue(initialValue)
772 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv);
774 SGSharedPtr<SGCondition const> _condition;
775 SGSharedPtr<SGExpressiond const> _animationValue;
776 double _initialValue;
778 // This cull callback can run in different threads if there is
779 // more than one camera. It is probably safe to overwrite the
780 // reference values in multiple threads, but we'll provide a
781 // threadsafe way to manage those values just to be safe.
782 struct ReferenceValues : public osg::Referenced
784 ReferenceValues(double t, double rot, double vel)
785 : _time(t), _rotation(rot), _rotVelocity(vel)
792 OpenThreads::AtomicPtr _referenceValues;
795 void SpinAnimCallback::operator()(osg::Node* node, osg::NodeVisitor* nv)
798 SGRotateTransform* transform = static_cast<SGRotateTransform*>(node);
799 EffectCullVisitor* cv = dynamic_cast<EffectCullVisitor*>(nv);
802 if (!_condition || _condition->test()) {
803 double t = nv->getFrameStamp()->getReferenceTime();
804 double rps = _animationValue->getValue() / 60.0;
805 ref_ptr<ReferenceValues>
806 refval(static_cast<ReferenceValues*>(_referenceValues.get()));
807 if (!refval || refval->_rotVelocity != rps) {
808 ref_ptr<ReferenceValues> newref;
809 if (!refval.valid()) {
811 newref = new ReferenceValues(t, 0.0, rps);
813 double newRot = refval->_rotation + (t - refval->_time) * refval->_rotVelocity;
814 newref = new ReferenceValues(t, newRot, rps);
816 // increment reference pointer, because it will be stored
817 // naked in _referenceValues.
819 if (_referenceValues.assign(newref, refval)) {
820 if (refval.valid()) {
821 DeletionManager::instance()->addStaleObject(refval.get());
825 // Another thread installed new values before us
828 // Whatever happened, we can use the reference values just
832 double rotation = refval->_rotation + (t - refval->_time) * rps;
834 double rot = modf(rotation, &intPart);
835 double angle = rot * 2.0 * osg::PI;
836 const SGVec3d& sgcenter = transform->getCenter();
837 const SGVec3d& sgaxis = transform->getAxis();
838 Matrixd mat = Matrixd::translate(-sgcenter[0], -sgcenter[1], -sgcenter[2])
839 * Matrixd::rotate(angle, sgaxis[0], sgaxis[1], sgaxis[2])
840 * Matrixd::translate(sgcenter[0], sgcenter[1], sgcenter[2])
841 * *cv->getModelViewMatrix();
842 ref_ptr<RefMatrix> refmat = new RefMatrix(mat);
843 cv->pushModelViewMatrix(refmat.get(), transform->getReferenceFrame());
844 traverse(transform, nv);
845 cv->popModelViewMatrix();
847 traverse(transform, nv);
851 SGRotateAnimation::SGRotateAnimation(const SGPropertyNode* configNode,
852 SGPropertyNode* modelRoot) :
853 SGAnimation(configNode, modelRoot)
855 std::string type = configNode->getStringValue("type", "");
856 _isSpin = (type == "spin");
858 _condition = getCondition();
859 SGSharedPtr<SGExpressiond> value;
860 value = read_value(configNode, modelRoot, "-deg",
861 -SGLimitsd::max(), SGLimitsd::max());
862 _animationValue = value->simplify();
864 _initialValue = _animationValue->getValue();
867 _center = SGVec3d::zeros();
868 if (configNode->hasValue("axis/x1-m")) {
870 v1[0] = configNode->getDoubleValue("axis/x1-m", 0);
871 v1[1] = configNode->getDoubleValue("axis/y1-m", 0);
872 v1[2] = configNode->getDoubleValue("axis/z1-m", 0);
873 v2[0] = configNode->getDoubleValue("axis/x2-m", 0);
874 v2[1] = configNode->getDoubleValue("axis/y2-m", 0);
875 v2[2] = configNode->getDoubleValue("axis/z2-m", 0);
876 _center = 0.5*(v1+v2);
879 _axis[0] = configNode->getDoubleValue("axis/x", 0);
880 _axis[1] = configNode->getDoubleValue("axis/y", 0);
881 _axis[2] = configNode->getDoubleValue("axis/z", 0);
883 if (8*SGLimitsd::min() < norm(_axis))
884 _axis = normalize(_axis);
886 _center[0] = configNode->getDoubleValue("center/x-m", _center[0]);
887 _center[1] = configNode->getDoubleValue("center/y-m", _center[1]);
888 _center[2] = configNode->getDoubleValue("center/z-m", _center[2]);
892 SGRotateAnimation::createAnimationGroup(osg::Group& parent)
895 SGRotateTransform* transform = new SGRotateTransform;
896 transform->setName("spin rotate animation");
897 SpinAnimCallback* cc;
898 cc = new SpinAnimCallback(_condition, _animationValue, _initialValue);
899 transform->setCullCallback(cc);
900 transform->setCenter(_center);
901 transform->setAxis(_axis);
902 transform->setAngleDeg(_initialValue);
903 parent.addChild(transform);
906 SGRotAnimTransform* transform = new SGRotAnimTransform;
907 transform->setName("rotate animation");
908 transform->_condition = _condition;
909 transform->_animationValue = _animationValue;
910 transform->_lastAngle = _initialValue;
911 transform->setCenter(_center);
912 transform->setAxis(_axis);
913 parent.addChild(transform);
919 ////////////////////////////////////////////////////////////////////////
920 // Implementation of scale animation
921 ////////////////////////////////////////////////////////////////////////
923 class SGScaleAnimation::UpdateCallback : public osg::NodeCallback {
925 UpdateCallback(const SGCondition* condition,
926 SGSharedPtr<const SGExpressiond> animationValue[3]) :
927 _condition(condition)
929 _animationValue[0] = animationValue[0];
930 _animationValue[1] = animationValue[1];
931 _animationValue[2] = animationValue[2];
932 setName("SGScaleAnimation::UpdateCallback");
934 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
936 if (!_condition || _condition->test()) {
937 SGScaleTransform* transform;
938 transform = static_cast<SGScaleTransform*>(node);
939 SGVec3d scale(_animationValue[0]->getValue(),
940 _animationValue[1]->getValue(),
941 _animationValue[2]->getValue());
942 transform->setScaleFactor(scale);
947 SGSharedPtr<SGCondition const> _condition;
948 SGSharedPtr<SGExpressiond const> _animationValue[3];
951 SGScaleAnimation::SGScaleAnimation(const SGPropertyNode* configNode,
952 SGPropertyNode* modelRoot) :
953 SGAnimation(configNode, modelRoot)
955 _condition = getCondition();
957 // default offset/factor for all directions
958 double offset = configNode->getDoubleValue("offset", 0);
959 double factor = configNode->getDoubleValue("factor", 1);
961 SGSharedPtr<SGExpressiond> inPropExpr;
963 std::string inputPropertyName;
964 inputPropertyName = configNode->getStringValue("property", "");
965 if (inputPropertyName.empty()) {
966 inPropExpr = new SGConstExpression<double>(0);
968 SGPropertyNode* inputProperty;
969 inputProperty = modelRoot->getNode(inputPropertyName, true);
970 inPropExpr = new SGPropertyExpression<double>(inputProperty);
973 SGInterpTable* interpTable = read_interpolation_table(configNode);
975 SGSharedPtr<SGExpressiond> value;
976 value = new SGInterpTableExpression<double>(inPropExpr, interpTable);
977 _animationValue[0] = value->simplify();
978 _animationValue[1] = value->simplify();
979 _animationValue[2] = value->simplify();
980 } else if (configNode->getBoolValue("use-personality", false)) {
981 SGSharedPtr<SGExpressiond> value;
982 value = new SGPersonalityScaleOffsetExpression(inPropExpr, configNode,
983 "x-factor", "x-offset",
985 double minClip = configNode->getDoubleValue("x-min", 0);
986 double maxClip = configNode->getDoubleValue("x-max", SGLimitsd::max());
987 value = new SGClipExpression<double>(value, minClip, maxClip);
988 _animationValue[0] = value->simplify();
990 value = new SGPersonalityScaleOffsetExpression(inPropExpr, configNode,
991 "y-factor", "y-offset",
993 minClip = configNode->getDoubleValue("y-min", 0);
994 maxClip = configNode->getDoubleValue("y-max", SGLimitsd::max());
995 value = new SGClipExpression<double>(value, minClip, maxClip);
996 _animationValue[1] = value->simplify();
998 value = new SGPersonalityScaleOffsetExpression(inPropExpr, configNode,
999 "z-factor", "z-offset",
1001 minClip = configNode->getDoubleValue("z-min", 0);
1002 maxClip = configNode->getDoubleValue("z-max", SGLimitsd::max());
1003 value = new SGClipExpression<double>(value, minClip, maxClip);
1004 _animationValue[2] = value->simplify();
1006 SGSharedPtr<SGExpressiond> value;
1007 value = read_factor_offset(configNode, inPropExpr, "x-factor", "x-offset");
1008 double minClip = configNode->getDoubleValue("x-min", 0);
1009 double maxClip = configNode->getDoubleValue("x-max", SGLimitsd::max());
1010 value = new SGClipExpression<double>(value, minClip, maxClip);
1011 _animationValue[0] = value->simplify();
1013 value = read_factor_offset(configNode, inPropExpr, "y-factor", "y-offset");
1014 minClip = configNode->getDoubleValue("y-min", 0);
1015 maxClip = configNode->getDoubleValue("y-max", SGLimitsd::max());
1016 value = new SGClipExpression<double>(value, minClip, maxClip);
1017 _animationValue[1] = value->simplify();
1019 value = read_factor_offset(configNode, inPropExpr, "z-factor", "z-offset");
1020 minClip = configNode->getDoubleValue("z-min", 0);
1021 maxClip = configNode->getDoubleValue("z-max", SGLimitsd::max());
1022 value = new SGClipExpression<double>(value, minClip, maxClip);
1023 _animationValue[2] = value->simplify();
1025 _initialValue[0] = configNode->getDoubleValue("x-starting-scale", 1);
1026 _initialValue[0] *= configNode->getDoubleValue("x-factor", factor);
1027 _initialValue[0] += configNode->getDoubleValue("x-offset", offset);
1028 _initialValue[1] = configNode->getDoubleValue("y-starting-scale", 1);
1029 _initialValue[1] *= configNode->getDoubleValue("y-factor", factor);
1030 _initialValue[1] += configNode->getDoubleValue("y-offset", offset);
1031 _initialValue[2] = configNode->getDoubleValue("z-starting-scale", 1);
1032 _initialValue[2] *= configNode->getDoubleValue("z-factor", factor);
1033 _initialValue[2] += configNode->getDoubleValue("z-offset", offset);
1034 _center[0] = configNode->getDoubleValue("center/x-m", 0);
1035 _center[1] = configNode->getDoubleValue("center/y-m", 0);
1036 _center[2] = configNode->getDoubleValue("center/z-m", 0);
1040 SGScaleAnimation::createAnimationGroup(osg::Group& parent)
1042 SGScaleTransform* transform = new SGScaleTransform;
1043 transform->setName("scale animation");
1044 transform->setCenter(_center);
1045 transform->setScaleFactor(_initialValue);
1046 UpdateCallback* uc = new UpdateCallback(_condition, _animationValue);
1047 transform->setUpdateCallback(uc);
1048 parent.addChild(transform);
1053 // Don't create a new state state everytime we need GL_NORMALIZE!
1057 Mutex normalizeMutex;
1059 osg::StateSet* getNormalizeStateSet()
1061 static osg::ref_ptr<osg::StateSet> normalizeStateSet;
1062 ScopedLock<Mutex> lock(normalizeMutex);
1063 if (!normalizeStateSet.valid()) {
1064 normalizeStateSet = new osg::StateSet;
1065 normalizeStateSet->setMode(GL_NORMALIZE, osg::StateAttribute::ON);
1066 normalizeStateSet->setDataVariance(osg::Object::STATIC);
1068 return normalizeStateSet.get();
1072 ////////////////////////////////////////////////////////////////////////
1073 // Implementation of dist scale animation
1074 ////////////////////////////////////////////////////////////////////////
1076 class SGDistScaleAnimation::Transform : public osg::Transform {
1078 Transform() : _min_v(0.0), _max_v(0.0), _factor(0.0), _offset(0.0) {}
1079 Transform(const Transform& rhs,
1080 const osg::CopyOp& copyOp = osg::CopyOp::SHALLOW_COPY)
1081 : osg::Transform(rhs, copyOp), _table(rhs._table), _center(rhs._center),
1082 _min_v(rhs._min_v), _max_v(rhs._max_v), _factor(rhs._factor),
1083 _offset(rhs._offset)
1086 META_Node(simgear, SGDistScaleAnimation::Transform);
1087 Transform(const SGPropertyNode* configNode)
1089 setName(configNode->getStringValue("name", "dist scale animation"));
1090 setReferenceFrame(RELATIVE_RF);
1091 setStateSet(getNormalizeStateSet());
1092 _factor = configNode->getFloatValue("factor", 1);
1093 _offset = configNode->getFloatValue("offset", 0);
1094 _min_v = configNode->getFloatValue("min", SGLimitsf::epsilon());
1095 _max_v = configNode->getFloatValue("max", SGLimitsf::max());
1096 _table = read_interpolation_table(configNode);
1097 _center[0] = configNode->getFloatValue("center/x-m", 0);
1098 _center[1] = configNode->getFloatValue("center/y-m", 0);
1099 _center[2] = configNode->getFloatValue("center/z-m", 0);
1101 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
1102 osg::NodeVisitor* nv) const
1104 osg::Matrix transform;
1105 double scale_factor = computeScaleFactor(nv);
1106 transform(0,0) = scale_factor;
1107 transform(1,1) = scale_factor;
1108 transform(2,2) = scale_factor;
1109 transform(3,0) = _center[0]*(1 - scale_factor);
1110 transform(3,1) = _center[1]*(1 - scale_factor);
1111 transform(3,2) = _center[2]*(1 - scale_factor);
1112 matrix.preMult(transform);
1116 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
1117 osg::NodeVisitor* nv) const
1119 double scale_factor = computeScaleFactor(nv);
1120 if (fabs(scale_factor) <= SGLimits<double>::min())
1122 osg::Matrix transform;
1123 double rScaleFactor = 1/scale_factor;
1124 transform(0,0) = rScaleFactor;
1125 transform(1,1) = rScaleFactor;
1126 transform(2,2) = rScaleFactor;
1127 transform(3,0) = _center[0]*(1 - rScaleFactor);
1128 transform(3,1) = _center[1]*(1 - rScaleFactor);
1129 transform(3,2) = _center[2]*(1 - rScaleFactor);
1130 matrix.postMult(transform);
1134 static bool writeLocalData(const osg::Object& obj, osgDB::Output& fw)
1136 const Transform& trans = static_cast<const Transform&>(obj);
1137 fw.indent() << "center " << trans._center << "\n";
1138 fw.indent() << "min_v " << trans._min_v << "\n";
1139 fw.indent() << "max_v " << trans._max_v << "\n";
1140 fw.indent() << "factor " << trans._factor << "\n";
1141 fw.indent() << "offset " << trans._offset << "\n";
1145 double computeScaleFactor(osg::NodeVisitor* nv) const
1150 double scale_factor = (toOsg(_center) - nv->getEyePoint()).length();
1152 scale_factor = _factor * scale_factor + _offset;
1154 scale_factor = _table->interpolate( scale_factor );
1156 if (scale_factor < _min_v)
1157 scale_factor = _min_v;
1158 if (scale_factor > _max_v)
1159 scale_factor = _max_v;
1161 return scale_factor;
1164 SGSharedPtr<SGInterpTable> _table;
1173 SGDistScaleAnimation::SGDistScaleAnimation(const SGPropertyNode* configNode,
1174 SGPropertyNode* modelRoot) :
1175 SGAnimation(configNode, modelRoot)
1180 SGDistScaleAnimation::createAnimationGroup(osg::Group& parent)
1182 Transform* transform = new Transform(getConfig());
1183 parent.addChild(transform);
1189 osgDB::RegisterDotOsgWrapperProxy distScaleAnimationTransformProxy
1191 new SGDistScaleAnimation::Transform,
1192 "SGDistScaleAnimation::Transform",
1193 "Object Node Transform SGDistScaleAnimation::Transform Group",
1195 &SGDistScaleAnimation::Transform::writeLocalData
1199 ////////////////////////////////////////////////////////////////////////
1200 // Implementation of flash animation
1201 ////////////////////////////////////////////////////////////////////////
1203 class SGFlashAnimation::Transform : public osg::Transform {
1205 Transform() : _power(0.0), _factor(0.0), _offset(0.0), _min_v(0.0),
1206 _max_v(0.0), _two_sides(false)
1209 Transform(const Transform& rhs,
1210 const osg::CopyOp& copyOp = osg::CopyOp::SHALLOW_COPY)
1211 : osg::Transform(rhs, copyOp), _center(rhs._center), _axis(rhs._axis),
1212 _power(rhs._power), _factor(rhs._factor), _offset(rhs._offset),
1213 _min_v(rhs._min_v), _max_v(rhs._max_v), _two_sides(rhs._two_sides)
1216 META_Node(simgear, SGFlashAnimation::Transform);
1218 Transform(const SGPropertyNode* configNode)
1220 setReferenceFrame(RELATIVE_RF);
1221 setName(configNode->getStringValue("name", "flash animation"));
1222 setStateSet(getNormalizeStateSet());
1224 _axis[0] = configNode->getFloatValue("axis/x", 0);
1225 _axis[1] = configNode->getFloatValue("axis/y", 0);
1226 _axis[2] = configNode->getFloatValue("axis/z", 1);
1229 _center[0] = configNode->getFloatValue("center/x-m", 0);
1230 _center[1] = configNode->getFloatValue("center/y-m", 0);
1231 _center[2] = configNode->getFloatValue("center/z-m", 0);
1233 _offset = configNode->getFloatValue("offset", 0);
1234 _factor = configNode->getFloatValue("factor", 1);
1235 _power = configNode->getFloatValue("power", 1);
1236 _two_sides = configNode->getBoolValue("two-sides", false);
1238 _min_v = configNode->getFloatValue("min", SGLimitsf::epsilon());
1239 _max_v = configNode->getFloatValue("max", 1);
1241 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
1242 osg::NodeVisitor* nv) const
1244 osg::Matrix transform;
1245 double scale_factor = computeScaleFactor(nv);
1246 transform(0,0) = scale_factor;
1247 transform(1,1) = scale_factor;
1248 transform(2,2) = scale_factor;
1249 transform(3,0) = _center[0]*(1 - scale_factor);
1250 transform(3,1) = _center[1]*(1 - scale_factor);
1251 transform(3,2) = _center[2]*(1 - scale_factor);
1252 matrix.preMult(transform);
1256 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
1257 osg::NodeVisitor* nv) const
1259 double scale_factor = computeScaleFactor(nv);
1260 if (fabs(scale_factor) <= SGLimits<double>::min())
1262 osg::Matrix transform;
1263 double rScaleFactor = 1/scale_factor;
1264 transform(0,0) = rScaleFactor;
1265 transform(1,1) = rScaleFactor;
1266 transform(2,2) = rScaleFactor;
1267 transform(3,0) = _center[0]*(1 - rScaleFactor);
1268 transform(3,1) = _center[1]*(1 - rScaleFactor);
1269 transform(3,2) = _center[2]*(1 - rScaleFactor);
1270 matrix.postMult(transform);
1274 static bool writeLocalData(const osg::Object& obj, osgDB::Output& fw)
1276 const Transform& trans = static_cast<const Transform&>(obj);
1277 fw.indent() << "center " << trans._center[0] << " "
1278 << trans._center[1] << " " << trans._center[2] << " " << "\n";
1279 fw.indent() << "axis " << trans._axis[0] << " "
1280 << trans._axis[1] << " " << trans._axis[2] << " " << "\n";
1281 fw.indent() << "power " << trans._power << " \n";
1282 fw.indent() << "min_v " << trans._min_v << "\n";
1283 fw.indent() << "max_v " << trans._max_v << "\n";
1284 fw.indent() << "factor " << trans._factor << "\n";
1285 fw.indent() << "offset " << trans._offset << "\n";
1286 fw.indent() << "twosides " << (trans._two_sides ? "true" : "false") << "\n";
1290 double computeScaleFactor(osg::NodeVisitor* nv) const
1295 osg::Vec3 localEyeToCenter = nv->getEyePoint() - _center;
1296 localEyeToCenter.normalize();
1298 double cos_angle = localEyeToCenter*_axis;
1299 double scale_factor = 0;
1300 if ( _two_sides && cos_angle < 0 )
1301 scale_factor = _factor * pow( -cos_angle, _power ) + _offset;
1302 else if ( cos_angle > 0 )
1303 scale_factor = _factor * pow( cos_angle, _power ) + _offset;
1305 if ( scale_factor < _min_v )
1306 scale_factor = _min_v;
1307 if ( scale_factor > _max_v )
1308 scale_factor = _max_v;
1310 return scale_factor;
1313 virtual osg::BoundingSphere computeBound() const
1315 // avoid being culled away by small feature culling
1316 osg::BoundingSphere bs = osg::Group::computeBound();
1317 bs.radius() *= _max_v;
1324 double _power, _factor, _offset, _min_v, _max_v;
1329 SGFlashAnimation::SGFlashAnimation(const SGPropertyNode* configNode,
1330 SGPropertyNode* modelRoot) :
1331 SGAnimation(configNode, modelRoot)
1336 SGFlashAnimation::createAnimationGroup(osg::Group& parent)
1338 Transform* transform = new Transform(getConfig());
1339 parent.addChild(transform);
1345 osgDB::RegisterDotOsgWrapperProxy flashAnimationTransformProxy
1347 new SGFlashAnimation::Transform,
1348 "SGFlashAnimation::Transform",
1349 "Object Node Transform SGFlashAnimation::Transform Group",
1351 &SGFlashAnimation::Transform::writeLocalData
1355 ////////////////////////////////////////////////////////////////////////
1356 // Implementation of billboard animation
1357 ////////////////////////////////////////////////////////////////////////
1359 class SGBillboardAnimation::Transform : public osg::Transform {
1361 Transform() : _spherical(true) {}
1362 Transform(const Transform& rhs,
1363 const osg::CopyOp& copyOp = osg::CopyOp::SHALLOW_COPY)
1364 : osg::Transform(rhs, copyOp), _spherical(rhs._spherical) {}
1365 META_Node(simgear, SGBillboardAnimation::Transform);
1366 Transform(const SGPropertyNode* configNode) :
1367 _spherical(configNode->getBoolValue("spherical", true))
1369 setReferenceFrame(RELATIVE_RF);
1370 setName(configNode->getStringValue("name", "billboard animation"));
1372 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
1373 osg::NodeVisitor* nv) const
1375 // More or less taken from plibs ssgCutout
1377 matrix(0,0) = 1; matrix(0,1) = 0; matrix(0,2) = 0;
1378 matrix(1,0) = 0; matrix(1,1) = 0; matrix(1,2) = -1;
1379 matrix(2,0) = 0; matrix(2,1) = 1; matrix(2,2) = 0;
1381 osg::Vec3 zAxis(matrix(2, 0), matrix(2, 1), matrix(2, 2));
1382 osg::Vec3 xAxis = osg::Vec3(0, 0, -1)^zAxis;
1383 osg::Vec3 yAxis = zAxis^xAxis;
1389 matrix(0,0) = xAxis[0]; matrix(0,1) = xAxis[1]; matrix(0,2) = xAxis[2];
1390 matrix(1,0) = yAxis[0]; matrix(1,1) = yAxis[1]; matrix(1,2) = yAxis[2];
1391 matrix(2,0) = zAxis[0]; matrix(2,1) = zAxis[1]; matrix(2,2) = zAxis[2];
1396 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
1397 osg::NodeVisitor* nv) const
1399 // Hmm, don't yet know how to get that back ...
1402 static bool writeLocalData(const osg::Object& obj, osgDB::Output& fw)
1404 const Transform& trans = static_cast<const Transform&>(obj);
1406 fw.indent() << (trans._spherical ? "true" : "false") << "\n";
1414 SGBillboardAnimation::SGBillboardAnimation(const SGPropertyNode* configNode,
1415 SGPropertyNode* modelRoot) :
1416 SGAnimation(configNode, modelRoot)
1421 SGBillboardAnimation::createAnimationGroup(osg::Group& parent)
1423 Transform* transform = new Transform(getConfig());
1424 parent.addChild(transform);
1430 osgDB::RegisterDotOsgWrapperProxy billboardAnimationTransformProxy
1432 new SGBillboardAnimation::Transform,
1433 "SGBillboardAnimation::Transform",
1434 "Object Node Transform SGBillboardAnimation::Transform Group",
1436 &SGBillboardAnimation::Transform::writeLocalData
1440 ////////////////////////////////////////////////////////////////////////
1441 // Implementation of a range animation
1442 ////////////////////////////////////////////////////////////////////////
1444 class SGRangeAnimation::UpdateCallback : public osg::NodeCallback {
1446 UpdateCallback(const SGCondition* condition,
1447 const SGExpressiond* minAnimationValue,
1448 const SGExpressiond* maxAnimationValue,
1449 double minValue, double maxValue) :
1450 _condition(condition),
1451 _minAnimationValue(minAnimationValue),
1452 _maxAnimationValue(maxAnimationValue),
1453 _minStaticValue(minValue),
1454 _maxStaticValue(maxValue)
1456 setName("SGRangeAnimation::UpdateCallback");
1458 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1460 osg::LOD* lod = static_cast<osg::LOD*>(node);
1461 if (!_condition || _condition->test()) {
1463 if (_minAnimationValue)
1464 minRange = _minAnimationValue->getValue();
1466 minRange = _minStaticValue;
1468 if (_maxAnimationValue)
1469 maxRange = _maxAnimationValue->getValue();
1471 maxRange = _maxStaticValue;
1472 lod->setRange(0, minRange, maxRange);
1474 lod->setRange(0, 0, SGLimitsf::max());
1480 SGSharedPtr<const SGCondition> _condition;
1481 SGSharedPtr<const SGExpressiond> _minAnimationValue;
1482 SGSharedPtr<const SGExpressiond> _maxAnimationValue;
1483 double _minStaticValue;
1484 double _maxStaticValue;
1487 SGRangeAnimation::SGRangeAnimation(const SGPropertyNode* configNode,
1488 SGPropertyNode* modelRoot) :
1489 SGAnimation(configNode, modelRoot)
1491 _condition = getCondition();
1493 std::string inputPropertyName;
1494 inputPropertyName = configNode->getStringValue("min-property", "");
1495 if (!inputPropertyName.empty()) {
1496 SGPropertyNode* inputProperty;
1497 inputProperty = modelRoot->getNode(inputPropertyName, true);
1498 SGSharedPtr<SGExpressiond> value;
1499 value = new SGPropertyExpression<double>(inputProperty);
1501 value = read_factor_offset(configNode, value, "min-factor", "min-offset");
1502 _minAnimationValue = value->simplify();
1504 inputPropertyName = configNode->getStringValue("max-property", "");
1505 if (!inputPropertyName.empty()) {
1506 SGPropertyNode* inputProperty;
1507 inputProperty = modelRoot->getNode(inputPropertyName.c_str(), true);
1509 SGSharedPtr<SGExpressiond> value;
1510 value = new SGPropertyExpression<double>(inputProperty);
1512 value = read_factor_offset(configNode, value, "max-factor", "max-offset");
1513 _maxAnimationValue = value->simplify();
1516 _initialValue[0] = configNode->getDoubleValue("min-m", 0);
1517 _initialValue[0] *= configNode->getDoubleValue("min-factor", 1);
1518 _initialValue[1] = configNode->getDoubleValue("max-m", SGLimitsf::max());
1519 _initialValue[1] *= configNode->getDoubleValue("max-factor", 1);
1523 SGRangeAnimation::createAnimationGroup(osg::Group& parent)
1525 osg::Group* group = new osg::Group;
1526 group->setName("range animation group");
1528 osg::LOD* lod = new osg::LOD;
1529 lod->setName("range animation node");
1530 parent.addChild(lod);
1532 lod->addChild(group, _initialValue[0], _initialValue[1]);
1533 lod->setCenterMode(osg::LOD::USE_BOUNDING_SPHERE_CENTER);
1534 lod->setRangeMode(osg::LOD::DISTANCE_FROM_EYE_POINT);
1535 if (_minAnimationValue || _maxAnimationValue || _condition) {
1537 uc = new UpdateCallback(_condition, _minAnimationValue, _maxAnimationValue,
1538 _initialValue[0], _initialValue[1]);
1539 lod->setUpdateCallback(uc);
1545 ////////////////////////////////////////////////////////////////////////
1546 // Implementation of a select animation
1547 ////////////////////////////////////////////////////////////////////////
1549 SGSelectAnimation::SGSelectAnimation(const SGPropertyNode* configNode,
1550 SGPropertyNode* modelRoot) :
1551 SGAnimation(configNode, modelRoot)
1556 SGSelectAnimation::createAnimationGroup(osg::Group& parent)
1558 // if no condition given, this is a noop.
1559 SGSharedPtr<SGCondition const> condition = getCondition();
1560 // trick, gets deleted with all its 'animated' children
1561 // when the animation installer returns
1563 return new osg::Group;
1564 simgear::ConditionNode* cn = new simgear::ConditionNode;
1565 cn->setName("select animation node");
1566 cn->setCondition(condition.ptr());
1567 osg::Group* grp = new osg::Group;
1569 parent.addChild(cn);
1575 ////////////////////////////////////////////////////////////////////////
1576 // Implementation of alpha test animation
1577 ////////////////////////////////////////////////////////////////////////
1579 SGAlphaTestAnimation::SGAlphaTestAnimation(const SGPropertyNode* configNode,
1580 SGPropertyNode* modelRoot) :
1581 SGAnimation(configNode, modelRoot)
1587 // Keep one copy of the most common alpha test its state set.
1588 ReentrantMutex alphaTestMutex;
1589 osg::ref_ptr<osg::AlphaFunc> standardAlphaFunc;
1590 osg::ref_ptr<osg::StateSet> alphaFuncStateSet;
1592 osg::AlphaFunc* makeAlphaFunc(float clamp)
1594 ScopedLock<ReentrantMutex> lock(alphaTestMutex);
1595 if (osg::equivalent(clamp, 0.01f)) {
1596 if (standardAlphaFunc.valid())
1597 return standardAlphaFunc.get();
1600 osg::AlphaFunc* alphaFunc = new osg::AlphaFunc;
1601 alphaFunc->setFunction(osg::AlphaFunc::GREATER);
1602 alphaFunc->setReferenceValue(clamp);
1603 alphaFunc->setDataVariance(osg::Object::STATIC);
1604 if (osg::equivalent(clamp, 0.01f))
1605 standardAlphaFunc = alphaFunc;
1609 osg::StateSet* makeAlphaTestStateSet(float clamp)
1611 using namespace OpenThreads;
1612 ScopedLock<ReentrantMutex> lock(alphaTestMutex);
1613 if (osg::equivalent(clamp, 0.01f)) {
1614 if (alphaFuncStateSet.valid())
1615 return alphaFuncStateSet.get();
1617 osg::AlphaFunc* alphaFunc = makeAlphaFunc(clamp);
1618 osg::StateSet* stateSet = new osg::StateSet;
1619 stateSet->setAttributeAndModes(alphaFunc,
1620 (osg::StateAttribute::ON
1621 | osg::StateAttribute::OVERRIDE));
1622 stateSet->setDataVariance(osg::Object::STATIC);
1623 if (osg::equivalent(clamp, 0.01f))
1624 alphaFuncStateSet = stateSet;
1629 SGAlphaTestAnimation::install(osg::Node& node)
1631 SGAnimation::install(node);
1633 float alphaClamp = getConfig()->getFloatValue("alpha-factor", 0);
1634 osg::StateSet* stateSet = node.getStateSet();
1636 node.setStateSet(makeAlphaTestStateSet(alphaClamp));
1638 stateSet->setAttributeAndModes(makeAlphaFunc(alphaClamp),
1639 (osg::StateAttribute::ON
1640 | osg::StateAttribute::OVERRIDE));
1645 //////////////////////////////////////////////////////////////////////
1646 // Blend animation installer
1647 //////////////////////////////////////////////////////////////////////
1649 // XXX This needs to be replaced by something using TexEnvCombine to
1650 // change the blend factor. Changing the alpha values in the geometry
1652 class SGBlendAnimation::BlendVisitor : public osg::NodeVisitor {
1654 BlendVisitor(float blend) :
1655 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
1657 { setVisitorType(osg::NodeVisitor::NODE_VISITOR); }
1658 virtual void apply(osg::Node& node)
1660 updateStateSet(node.getStateSet());
1663 virtual void apply(osg::Geode& node)
1665 apply((osg::Node&)node);
1666 unsigned nDrawables = node.getNumDrawables();
1667 for (unsigned i = 0; i < nDrawables; ++i) {
1668 osg::Drawable* drawable = node.getDrawable(i);
1669 osg::Geometry* geometry = drawable->asGeometry();
1672 osg::Array* array = geometry->getColorArray();
1675 osg::Vec4Array* vec4Array = dynamic_cast<osg::Vec4Array*>(array);
1678 for (unsigned k = 0; k < vec4Array->size(); ++k) {
1679 (*vec4Array)[k][3] = _blend;
1682 updateStateSet(drawable->getStateSet());
1685 void updateStateSet(osg::StateSet* stateSet)
1689 osg::StateAttribute* stateAttribute;
1690 stateAttribute = stateSet->getAttribute(osg::StateAttribute::MATERIAL);
1691 if (!stateAttribute)
1693 osg::Material* material = dynamic_cast<osg::Material*>(stateAttribute);
1696 material->setAlpha(osg::Material::FRONT_AND_BACK, _blend);
1698 stateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
1699 stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
1701 stateSet->setRenderingHint(osg::StateSet::DEFAULT_BIN);
1708 class SGBlendAnimation::UpdateCallback : public osg::NodeCallback {
1710 UpdateCallback(const SGPropertyNode* configNode, const SGExpressiond* v) :
1714 setName("SGBlendAnimation::UpdateCallback");
1716 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1718 double blend = _animationValue->getValue();
1719 if (blend != _prev_value) {
1720 _prev_value = blend;
1721 BlendVisitor visitor(1-blend);
1722 node->accept(visitor);
1728 SGSharedPtr<SGExpressiond const> _animationValue;
1732 SGBlendAnimation::SGBlendAnimation(const SGPropertyNode* configNode,
1733 SGPropertyNode* modelRoot)
1734 : SGAnimation(configNode, modelRoot),
1735 _animationValue(read_value(configNode, modelRoot, "", 0, 1))
1740 SGBlendAnimation::createAnimationGroup(osg::Group& parent)
1742 if (!_animationValue)
1745 osg::Group* group = new osg::Switch;
1746 group->setName("blend animation node");
1747 group->setUpdateCallback(new UpdateCallback(getConfig(), _animationValue));
1748 parent.addChild(group);
1753 SGBlendAnimation::install(osg::Node& node)
1755 SGAnimation::install(node);
1756 // make sure we do not change common geometries,
1757 // that also creates new display lists for these subgeometries.
1758 cloneDrawables(node);
1759 DoDrawArraysVisitor visitor;
1760 node.accept(visitor);
1764 //////////////////////////////////////////////////////////////////////
1765 // Timed animation installer
1766 //////////////////////////////////////////////////////////////////////
1770 class SGTimedAnimation::UpdateCallback : public osg::NodeCallback {
1772 UpdateCallback(const SGPropertyNode* configNode) :
1775 _duration_sec(configNode->getDoubleValue("duration-sec", 1)),
1776 _last_time_sec(SGLimitsd::max()),
1777 _use_personality(configNode->getBoolValue("use-personality", false))
1779 std::vector<SGSharedPtr<SGPropertyNode> > nodes;
1780 nodes = configNode->getChildren("branch-duration-sec");
1781 for (size_t i = 0; i < nodes.size(); ++i) {
1782 unsigned ind = nodes[ i ]->getIndex();
1783 while ( ind >= _durations.size() ) {
1784 _durations.push_back(DurationSpec(_duration_sec));
1786 SGPropertyNode_ptr rNode = nodes[i]->getChild("random");
1788 _durations[ind] = DurationSpec(nodes[ i ]->getDoubleValue());
1790 _durations[ind] = DurationSpec(rNode->getDoubleValue( "min", 0),
1791 rNode->getDoubleValue( "max", 1));
1794 setName("SGTimedAnimation::UpdateCallback");
1796 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1798 assert(dynamic_cast<osg::Switch*>(node));
1799 osg::Switch* sw = static_cast<osg::Switch*>(node);
1801 unsigned nChildren = sw->getNumChildren();
1803 // blow up the durations vector to the required size
1804 while (_durations.size() < nChildren) {
1805 _durations.push_back(_duration_sec);
1807 // make sure the current index is an duration that really exists
1808 _current_index = _current_index % nChildren;
1810 // update the time and compute the current systems time value
1811 double t = nv->getFrameStamp()->getReferenceTime();
1812 if (_last_time_sec == SGLimitsd::max()) {
1815 double dt = t - _last_time_sec;
1816 if (_use_personality)
1817 dt *= 1 + 0.2*(0.5 - sg_random());
1822 double currentDuration = _durations[_current_index].get();
1823 while (currentDuration < _reminder) {
1824 _reminder -= currentDuration;
1825 _current_index = (_current_index + 1) % nChildren;
1826 currentDuration = _durations[_current_index].get();
1829 sw->setSingleChildOn(_current_index);
1835 struct DurationSpec {
1836 DurationSpec(double t) :
1837 minTime(SGMiscd::max(0.01, t)),
1838 maxTime(SGMiscd::max(0.01, t))
1840 DurationSpec(double t0, double t1) :
1841 minTime(SGMiscd::max(0.01, t0)),
1842 maxTime(SGMiscd::max(0.01, t1))
1845 { return minTime + sg_random()*(maxTime - minTime); }
1849 std::vector<DurationSpec> _durations;
1850 unsigned _current_index;
1852 double _duration_sec;
1853 double _last_time_sec;
1854 bool _use_personality;
1858 SGTimedAnimation::SGTimedAnimation(const SGPropertyNode* configNode,
1859 SGPropertyNode* modelRoot)
1860 : SGAnimation(configNode, modelRoot)
1865 SGTimedAnimation::createAnimationGroup(osg::Group& parent)
1867 osg::Switch* sw = new osg::Switch;
1868 sw->setName("timed animation node");
1869 sw->setUpdateCallback(new UpdateCallback(getConfig()));
1870 parent.addChild(sw);
1875 ////////////////////////////////////////////////////////////////////////
1876 // dynamically switch on/off shadows
1877 ////////////////////////////////////////////////////////////////////////
1879 class SGShadowAnimation::UpdateCallback : public osg::NodeCallback {
1881 UpdateCallback(const SGCondition* condition) :
1882 _condition(condition)
1884 setName("SGShadowAnimation::UpdateCallback");
1886 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1888 if (_condition->test())
1889 node->setNodeMask( SG_NODEMASK_CASTSHADOW_BIT | node->getNodeMask());
1891 node->setNodeMask(~SG_NODEMASK_CASTSHADOW_BIT & node->getNodeMask());
1896 SGSharedPtr<const SGCondition> _condition;
1899 SGShadowAnimation::SGShadowAnimation(const SGPropertyNode* configNode,
1900 SGPropertyNode* modelRoot) :
1901 SGAnimation(configNode, modelRoot)
1906 SGShadowAnimation::createAnimationGroup(osg::Group& parent)
1908 SGSharedPtr<SGCondition const> condition = getCondition();
1910 osg::Group* group = new osg::Group;
1911 group->setName("shadow animation");
1913 group->setUpdateCallback(new UpdateCallback(condition));
1915 group->setNodeMask(~SG_NODEMASK_CASTSHADOW_BIT & group->getNodeMask());
1916 parent.addChild(group);
1921 ////////////////////////////////////////////////////////////////////////
1922 // Implementation of SGTexTransformAnimation
1923 ////////////////////////////////////////////////////////////////////////
1925 class SGTexTransformAnimation::Transform : public SGReferenced {
1930 virtual ~Transform()
1932 void setValue(double value)
1934 virtual void transform(osg::Matrix&) = 0;
1939 class SGTexTransformAnimation::Translation :
1940 public SGTexTransformAnimation::Transform {
1942 Translation(const SGVec3d& axis) :
1945 virtual void transform(osg::Matrix& matrix)
1948 set_translation(tmp, _value, _axis);
1949 matrix.preMult(tmp);
1955 class SGTexTransformAnimation::Rotation :
1956 public SGTexTransformAnimation::Transform {
1958 Rotation(const SGVec3d& axis, const SGVec3d& center) :
1962 virtual void transform(osg::Matrix& matrix)
1965 SGRotateTransform::set_rotation(tmp, SGMiscd::deg2rad(_value), _center,
1967 matrix.preMult(tmp);
1974 class SGTexTransformAnimation::UpdateCallback :
1975 public osg::StateAttribute::Callback {
1977 UpdateCallback(const SGCondition* condition) :
1978 _condition(condition)
1980 setName("SGTexTransformAnimation::UpdateCallback");
1982 virtual void operator () (osg::StateAttribute* sa, osg::NodeVisitor*)
1984 if (!_condition || _condition->test()) {
1985 TransformList::const_iterator i;
1986 for (i = _transforms.begin(); i != _transforms.end(); ++i)
1987 i->transform->setValue(i->value->getValue());
1989 assert(dynamic_cast<osg::TexMat*>(sa));
1990 osg::TexMat* texMat = static_cast<osg::TexMat*>(sa);
1991 texMat->getMatrix().makeIdentity();
1992 TransformList::const_iterator i;
1993 for (i = _transforms.begin(); i != _transforms.end(); ++i)
1994 i->transform->transform(texMat->getMatrix());
1996 void appendTransform(Transform* transform, SGExpressiond* value)
1998 Entry entry = { transform, value };
1999 transform->transform(_matrix);
2000 _transforms.push_back(entry);
2005 SGSharedPtr<Transform> transform;
2006 SGSharedPtr<const SGExpressiond> value;
2008 typedef std::vector<Entry> TransformList;
2009 TransformList _transforms;
2010 SGSharedPtr<const SGCondition> _condition;
2011 osg::Matrix _matrix;
2014 SGTexTransformAnimation::SGTexTransformAnimation(const SGPropertyNode* configNode,
2015 SGPropertyNode* modelRoot) :
2016 SGAnimation(configNode, modelRoot)
2021 SGTexTransformAnimation::createAnimationGroup(osg::Group& parent)
2023 osg::Group* group = new osg::Group;
2024 group->setName("texture transform group");
2025 osg::StateSet* stateSet = group->getOrCreateStateSet();
2026 stateSet->setDataVariance(osg::Object::DYNAMIC);
2027 osg::TexMat* texMat = new osg::TexMat;
2028 UpdateCallback* updateCallback = new UpdateCallback(getCondition());
2029 // interpret the configs ...
2030 std::string type = getType();
2032 if (type == "textranslate") {
2033 appendTexTranslate(getConfig(), updateCallback);
2034 } else if (type == "texrotate") {
2035 appendTexRotate(getConfig(), updateCallback);
2036 } else if (type == "texmultiple") {
2037 std::vector<SGSharedPtr<SGPropertyNode> > transformConfigs;
2038 transformConfigs = getConfig()->getChildren("transform");
2039 for (unsigned i = 0; i < transformConfigs.size(); ++i) {
2040 std::string subtype = transformConfigs[i]->getStringValue("subtype", "");
2041 if (subtype == "textranslate")
2042 appendTexTranslate(transformConfigs[i], updateCallback);
2043 else if (subtype == "texrotate")
2044 appendTexRotate(transformConfigs[i], updateCallback);
2046 SG_LOG(SG_INPUT, SG_ALERT,
2047 "Ignoring unknown texture transform subtype");
2050 SG_LOG(SG_INPUT, SG_ALERT, "Ignoring unknown texture transform type");
2053 texMat->setUpdateCallback(updateCallback);
2054 stateSet->setTextureAttribute(0, texMat);
2055 parent.addChild(group);
2060 SGTexTransformAnimation::appendTexTranslate(const SGPropertyNode* config,
2061 UpdateCallback* updateCallback)
2063 std::string propertyName = config->getStringValue("property", "");
2064 SGSharedPtr<SGExpressiond> value;
2065 if (propertyName.empty())
2066 value = new SGConstExpression<double>(0);
2068 SGPropertyNode* inputProperty = getModelRoot()->getNode(propertyName, true);
2069 value = new SGPropertyExpression<double>(inputProperty);
2072 SGInterpTable* table = read_interpolation_table(config);
2074 value = new SGInterpTableExpression<double>(value, table);
2075 double biasValue = config->getDoubleValue("bias", 0);
2077 value = new SGBiasExpression<double>(value, biasValue);
2078 value = new SGStepExpression<double>(value,
2079 config->getDoubleValue("step", 0),
2080 config->getDoubleValue("scroll", 0));
2081 value = value->simplify();
2083 double biasValue = config->getDoubleValue("bias", 0);
2085 value = new SGBiasExpression<double>(value, biasValue);
2086 value = new SGStepExpression<double>(value,
2087 config->getDoubleValue("step", 0),
2088 config->getDoubleValue("scroll", 0));
2089 value = read_offset_factor(config, value, "factor", "offset");
2091 if (config->hasChild("min") || config->hasChild("max")) {
2092 double minClip = config->getDoubleValue("min", -SGLimitsd::max());
2093 double maxClip = config->getDoubleValue("max", SGLimitsd::max());
2094 value = new SGClipExpression<double>(value, minClip, maxClip);
2096 value = value->simplify();
2098 SGVec3d axis(config->getDoubleValue("axis/x", 0),
2099 config->getDoubleValue("axis/y", 0),
2100 config->getDoubleValue("axis/z", 0));
2101 Translation* translation;
2102 translation = new Translation(normalize(axis));
2103 translation->setValue(config->getDoubleValue("starting-position", 0));
2104 updateCallback->appendTransform(translation, value);
2108 SGTexTransformAnimation::appendTexRotate(const SGPropertyNode* config,
2109 UpdateCallback* updateCallback)
2111 std::string propertyName = config->getStringValue("property", "");
2112 SGSharedPtr<SGExpressiond> value;
2113 if (propertyName.empty())
2114 value = new SGConstExpression<double>(0);
2116 SGPropertyNode* inputProperty = getModelRoot()->getNode(propertyName, true);
2117 value = new SGPropertyExpression<double>(inputProperty);
2120 SGInterpTable* table = read_interpolation_table(config);
2122 value = new SGInterpTableExpression<double>(value, table);
2123 double biasValue = config->getDoubleValue("bias", 0);
2125 value = new SGBiasExpression<double>(value, biasValue);
2126 value = new SGStepExpression<double>(value,
2127 config->getDoubleValue("step", 0),
2128 config->getDoubleValue("scroll", 0));
2129 value = value->simplify();
2131 double biasValue = config->getDoubleValue("bias", 0);
2133 value = new SGBiasExpression<double>(value, biasValue);
2134 value = new SGStepExpression<double>(value,
2135 config->getDoubleValue("step", 0),
2136 config->getDoubleValue("scroll", 0));
2137 value = read_offset_factor(config, value, "factor", "offset-deg");
2139 if (config->hasChild("min-deg") || config->hasChild("max-deg")) {
2140 double minClip = config->getDoubleValue("min-deg", -SGLimitsd::max());
2141 double maxClip = config->getDoubleValue("max-deg", SGLimitsd::max());
2142 value = new SGClipExpression<double>(value, minClip, maxClip);
2144 value = value->simplify();
2146 SGVec3d axis(config->getDoubleValue("axis/x", 0),
2147 config->getDoubleValue("axis/y", 0),
2148 config->getDoubleValue("axis/z", 0));
2149 SGVec3d center(config->getDoubleValue("center/x", 0),
2150 config->getDoubleValue("center/y", 0),
2151 config->getDoubleValue("center/z", 0));
2153 rotation = new Rotation(normalize(axis), center);
2154 rotation->setValue(config->getDoubleValue("starting-position-deg", 0));
2155 updateCallback->appendTransform(rotation, value);
2159 ////////////////////////////////////////////////////////////////////////
2160 // Implementation of SGPickAnimation
2161 ////////////////////////////////////////////////////////////////////////
2163 class SGPickAnimation::PickCallback : public SGPickCallback {
2165 PickCallback(const SGPropertyNode* configNode,
2166 SGPropertyNode* modelRoot) :
2167 _repeatable(configNode->getBoolValue("repeatable", false)),
2168 _repeatInterval(configNode->getDoubleValue("interval-sec", 0.1))
2170 SG_LOG(SG_INPUT, SG_DEBUG, "Reading all bindings");
2171 std::vector<SGPropertyNode_ptr> bindings;
2173 bindings = configNode->getChildren("button");
2174 for (unsigned int i = 0; i < bindings.size(); ++i) {
2175 _buttons.push_back( bindings[i]->getIntValue() );
2177 bindings = configNode->getChildren("binding");
2178 for (unsigned int i = 0; i < bindings.size(); ++i) {
2179 _bindingsDown.push_back(new SGBinding(bindings[i], modelRoot));
2182 const SGPropertyNode* upNode = configNode->getChild("mod-up");
2185 bindings = upNode->getChildren("binding");
2186 for (unsigned int i = 0; i < bindings.size(); ++i) {
2187 _bindingsUp.push_back(new SGBinding(bindings[i], modelRoot));
2190 virtual bool buttonPressed(int button, const Info&)
2193 for( std::vector<int>::iterator it = _buttons.begin(); it != _buttons.end(); ++it ) {
2194 if( *it == button ) {
2201 SGBindingList::const_iterator i;
2202 for (i = _bindingsDown.begin(); i != _bindingsDown.end(); ++i)
2204 _repeatTime = -_repeatInterval; // anti-bobble: delay start of repeat
2207 virtual void buttonReleased(void)
2209 SGBindingList::const_iterator i;
2210 for (i = _bindingsUp.begin(); i != _bindingsUp.end(); ++i)
2213 virtual void update(double dt)
2219 while (_repeatInterval < _repeatTime) {
2220 _repeatTime -= _repeatInterval;
2221 SGBindingList::const_iterator i;
2222 for (i = _bindingsDown.begin(); i != _bindingsDown.end(); ++i)
2227 SGBindingList _bindingsDown;
2228 SGBindingList _bindingsUp;
2229 std::vector<int> _buttons;
2231 double _repeatInterval;
2235 class VncVisitor : public osg::NodeVisitor {
2237 VncVisitor(double x, double y, int mask) :
2238 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
2239 _texX(x), _texY(y), _mask(mask), _done(false)
2241 SG_LOG(SG_INPUT, SG_DEBUG, "VncVisitor constructor "
2242 << x << "," << y << " mask " << mask);
2245 virtual void apply(osg::Node &node)
2247 // Some nodes have state sets attached
2248 touchStateSet(node.getStateSet());
2252 // See whether we are a geode worth exploring
2253 osg::Geode *g = dynamic_cast<osg::Geode*>(&node);
2255 // Go find all its drawables
2256 int i = g->getNumDrawables();
2258 osg::Drawable *d = g->getDrawable(i);
2259 if (d) touchDrawable(*d);
2261 // Out of optimism, do the same for EffectGeode
2262 simgear::EffectGeode *eg = dynamic_cast<simgear::EffectGeode*>(&node);
2264 for (simgear::EffectGeode::DrawablesIterator di = eg->drawablesBegin();
2265 di != eg->drawablesEnd(); di++) {
2266 touchDrawable(**di);
2268 // Now see whether the EffectGeode has an Effect
2269 simgear::Effect *e = eg->getEffect();
2271 touchStateSet(e->getDefaultStateSet());
2275 inline void touchDrawable(osg::Drawable &d)
2277 osg::StateSet *ss = d.getStateSet();
2281 void touchStateSet(osg::StateSet *ss)
2284 osg::StateAttribute *sa = ss->getTextureAttribute(0,
2285 osg::StateAttribute::TEXTURE);
2287 osg::Texture *t = sa->asTexture();
2289 osg::Image *img = t->getImage(0);
2292 int pixX = _texX * img->s();
2293 int pixY = _texY * img->t();
2294 _done = img->sendPointerEvent(pixX, pixY, _mask);
2295 SG_LOG(SG_INPUT, SG_DEBUG, "VncVisitor image said " << _done
2296 << " to coord " << pixX << "," << pixY);
2300 inline bool wasSuccessful()
2306 double _texX, _texY;
2312 class SGPickAnimation::VncCallback : public SGPickCallback {
2314 VncCallback(const SGPropertyNode* configNode,
2315 SGPropertyNode* modelRoot,
2319 SG_LOG(SG_INPUT, SG_DEBUG, "Configuring VNC callback");
2320 const char *cornernames[3] = {"top-left", "top-right", "bottom-left"};
2321 SGVec3d *cornercoords[3] = {&_topLeft, &_toRight, &_toDown};
2322 for (int c =0; c < 3; c++) {
2323 const SGPropertyNode* cornerNode = configNode->getChild(cornernames[c]);
2324 *cornercoords[c] = SGVec3d(
2325 cornerNode->getDoubleValue("x"),
2326 cornerNode->getDoubleValue("y"),
2327 cornerNode->getDoubleValue("z"));
2329 _toRight -= _topLeft;
2330 _toDown -= _topLeft;
2331 _squaredRight = dot(_toRight, _toRight);
2332 _squaredDown = dot(_toDown, _toDown);
2335 virtual bool buttonPressed(int button, const Info& info)
2337 SGVec3d loc(info.local);
2338 SG_LOG(SG_INPUT, SG_DEBUG, "VNC pressed " << button << ": " << loc);
2340 _x = dot(loc, _toRight) / _squaredRight;
2341 _y = dot(loc, _toDown) / _squaredDown;
2342 if (_x<0) _x = 0; else if (_x > 1) _x = 1;
2343 if (_y<0) _y = 0; else if (_y > 1) _y = 1;
2344 VncVisitor vv(_x, _y, 1 << button);
2346 return vv.wasSuccessful();
2349 virtual void buttonReleased(void)
2351 SG_LOG(SG_INPUT, SG_DEBUG, "VNC release");
2352 VncVisitor vv(_x, _y, 0);
2355 virtual void update(double dt)
2360 osg::ref_ptr<osg::Group> _node;
2361 SGVec3d _topLeft, _toRight, _toDown;
2362 double _squaredRight, _squaredDown;
2365 SGPickAnimation::SGPickAnimation(const SGPropertyNode* configNode,
2366 SGPropertyNode* modelRoot) :
2367 SGAnimation(configNode, modelRoot)
2373 Mutex colorModeUniformMutex;
2374 osg::ref_ptr<osg::Uniform> colorModeUniform;
2378 SGPickAnimation::createAnimationGroup(osg::Group& parent)
2380 osg::Group* commonGroup = new osg::Group;
2382 // Contains the normal geometry that is interactive
2383 osg::ref_ptr<osg::Group> normalGroup = new osg::Group;
2384 normalGroup->setName("pick normal group");
2385 normalGroup->addChild(commonGroup);
2387 // Used to render the geometry with just yellow edges
2388 osg::Group* highlightGroup = new osg::Group;
2389 highlightGroup->setName("pick highlight group");
2390 highlightGroup->setNodeMask(SG_NODEMASK_PICK_BIT);
2391 highlightGroup->addChild(commonGroup);
2392 SGSceneUserData* ud;
2393 ud = SGSceneUserData::getOrCreateSceneUserData(commonGroup);
2395 // add actions that become macro and command invocations
2396 std::vector<SGPropertyNode_ptr> actions;
2397 actions = getConfig()->getChildren("action");
2398 for (unsigned int i = 0; i < actions.size(); ++i)
2399 ud->addPickCallback(new PickCallback(actions[i], getModelRoot()));
2400 // Look for the VNC sessions that want raw mouse input
2401 actions = getConfig()->getChildren("vncaction");
2402 for (unsigned int i = 0; i < actions.size(); ++i)
2403 ud->addPickCallback(new VncCallback(actions[i], getModelRoot(),
2406 // prepare a state set that paints the edges of this object yellow
2407 // The material and texture attributes are set with
2408 // OVERRIDE|PROTECTED in case there is a material animation on a
2409 // higher node in the scene graph, which would have its material
2410 // attribute set with OVERRIDE.
2411 osg::StateSet* stateSet = highlightGroup->getOrCreateStateSet();
2412 osg::Texture2D* white = StateAttributeFactory::instance()->getWhiteTexture();
2413 stateSet->setTextureAttributeAndModes(0, white,
2414 (osg::StateAttribute::ON
2415 | osg::StateAttribute::OVERRIDE
2416 | osg::StateAttribute::PROTECTED));
2417 osg::PolygonOffset* polygonOffset = new osg::PolygonOffset;
2418 polygonOffset->setFactor(-1);
2419 polygonOffset->setUnits(-1);
2420 stateSet->setAttribute(polygonOffset, osg::StateAttribute::OVERRIDE);
2421 stateSet->setMode(GL_POLYGON_OFFSET_LINE,
2422 osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
2423 osg::PolygonMode* polygonMode = new osg::PolygonMode;
2424 polygonMode->setMode(osg::PolygonMode::FRONT_AND_BACK,
2425 osg::PolygonMode::LINE);
2426 stateSet->setAttribute(polygonMode, osg::StateAttribute::OVERRIDE);
2427 osg::Material* material = new osg::Material;
2428 material->setColorMode(osg::Material::OFF);
2429 material->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(0, 0, 0, 1));
2430 // XXX Alpha < 1.0 in the diffuse material value is a signal to the
2431 // default shader to take the alpha value from the material value
2432 // and not the glColor. In many cases the pick animation geometry is
2433 // transparent, so the outline would not be visible without this hack.
2434 material->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(0, 0, 0, .95));
2435 material->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(1, 1, 0, 1));
2436 material->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0, 0, 0, 0));
2437 stateSet->setAttribute(
2438 material, osg::StateAttribute::OVERRIDE | osg::StateAttribute::PROTECTED);
2439 // The default shader has a colorMode uniform that mimics the
2440 // behavior of Material color mode.
2441 osg::Uniform* cmUniform = 0;
2443 ScopedLock<Mutex> lock(colorModeUniformMutex);
2444 if (!colorModeUniform.valid()) {
2445 colorModeUniform = new osg::Uniform(osg::Uniform::INT, "colorMode");
2446 colorModeUniform->set(0); // MODE_OFF
2447 colorModeUniform->setDataVariance(osg::Object::STATIC);
2449 cmUniform = colorModeUniform.get();
2451 stateSet->addUniform(cmUniform,
2452 osg::StateAttribute::OVERRIDE | osg::StateAttribute::ON);
2453 // Only add normal geometry if configured
2454 if (getConfig()->getBoolValue("visible", true))
2455 parent.addChild(normalGroup.get());
2456 parent.addChild(highlightGroup);