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/OsgMath.hxx>
48 #include <simgear/scene/util/SGNodeMasks.hxx>
49 #include <simgear/scene/util/SGSceneUserData.hxx>
50 #include <simgear/scene/util/SGStateAttributeVisitor.hxx>
51 #include <simgear/scene/util/StateAttributeFactory.hxx>
53 #include "animation.hxx"
56 #include "SGTranslateTransform.hxx"
57 #include "SGMaterialAnimation.hxx"
58 #include "SGRotateTransform.hxx"
59 #include "SGScaleTransform.hxx"
60 #include "SGInteractionAnimation.hxx"
62 #include "ConditionNode.hxx"
64 using OpenThreads::Mutex;
65 using OpenThreads::ReentrantMutex;
66 using OpenThreads::ScopedLock;
68 using namespace simgear;
70 ////////////////////////////////////////////////////////////////////////
71 // Static utility functions.
72 ////////////////////////////////////////////////////////////////////////
75 * Set up the transform matrix for a translation.
78 set_translation (osg::Matrix &matrix, double position_m, const SGVec3d &axis)
80 SGVec3d xyz = axis * position_m;
81 matrix.makeIdentity();
82 matrix(3, 0) = xyz[0];
83 matrix(3, 1) = xyz[1];
84 matrix(3, 2) = xyz[2];
88 * Read an interpolation table from properties.
90 static SGInterpTable *
91 read_interpolation_table(const SGPropertyNode* props)
93 const SGPropertyNode* table_node = props->getNode("interpolation");
96 return new SGInterpTable(table_node);
100 unit_string(const char* value, const char* unit)
102 return std::string(value) + unit;
105 class SGPersonalityScaleOffsetExpression : public SGUnaryExpression<double> {
107 SGPersonalityScaleOffsetExpression(SGExpression<double>* expr,
108 SGPropertyNode const* config,
109 const std::string& scalename,
110 const std::string& offsetname,
112 double defOffset = 0) :
113 SGUnaryExpression<double>(expr),
114 _scale(config, scalename.c_str(), defScale),
115 _offset(config, offsetname.c_str(), defOffset)
117 void setScale(double scale)
119 void setOffset(double offset)
120 { _offset = offset; }
122 virtual void eval(double& value, const simgear::expression::Binding* b) const
126 value = _offset + _scale*getOperand()->getValue(b);
129 virtual bool isConst() const { return false; }
132 mutable SGPersonalityParameter<double> _scale;
133 mutable SGPersonalityParameter<double> _offset;
137 static SGExpressiond*
138 read_factor_offset(const SGPropertyNode* configNode, SGExpressiond* expr,
139 const std::string& factor, const std::string& offset)
141 double factorValue = configNode->getDoubleValue(factor, 1);
142 if (factorValue != 1)
143 expr = new SGScaleExpression<double>(expr, factorValue);
144 double offsetValue = configNode->getDoubleValue(offset, 0);
145 if (offsetValue != 0)
146 expr = new SGBiasExpression<double>(expr, offsetValue);
150 static SGExpressiond*
151 read_offset_factor(const SGPropertyNode* configNode, SGExpressiond* expr,
152 const std::string& factor, const std::string& offset)
154 double offsetValue = configNode->getDoubleValue(offset, 0);
155 if (offsetValue != 0)
156 expr = new SGBiasExpression<double>(expr, offsetValue);
157 double factorValue = configNode->getDoubleValue(factor, 1);
158 if (factorValue != 1)
159 expr = new SGScaleExpression<double>(expr, factorValue);
164 read_value(const SGPropertyNode* configNode, SGPropertyNode* modelRoot,
165 const char* unit, double defMin, double defMax)
167 const SGPropertyNode * expression = configNode->getNode( "expression" );
168 if( expression != NULL )
169 return SGReadDoubleExpression( modelRoot, expression->getChild(0) );
171 SGExpression<double>* value = 0;
173 std::string inputPropertyName = configNode->getStringValue("property", "");
174 if (inputPropertyName.empty()) {
175 std::string spos = unit_string("starting-position", unit);
176 double initPos = configNode->getDoubleValue(spos, 0);
177 value = new SGConstExpression<double>(initPos);
179 SGPropertyNode* inputProperty;
180 inputProperty = modelRoot->getNode(inputPropertyName, true);
181 value = new SGPropertyExpression<double>(inputProperty);
184 SGInterpTable* interpTable = read_interpolation_table(configNode);
186 return new SGInterpTableExpression<double>(value, interpTable);
188 std::string offset = unit_string("offset", unit);
189 std::string min = unit_string("min", unit);
190 std::string max = unit_string("max", unit);
192 if (configNode->getBoolValue("use-personality", false)) {
193 value = new SGPersonalityScaleOffsetExpression(value, configNode,
196 value = read_factor_offset(configNode, value, "factor", offset);
199 double minClip = configNode->getDoubleValue(min, defMin);
200 double maxClip = configNode->getDoubleValue(max, defMax);
201 if (minClip > SGMiscd::min(SGLimitsd::min(), -SGLimitsd::max()) ||
202 maxClip < SGLimitsd::max())
203 value = new SGClipExpression<double>(value, minClip, maxClip);
211 ////////////////////////////////////////////////////////////////////////
212 // Animation installer
213 ////////////////////////////////////////////////////////////////////////
215 class SGAnimation::RemoveModeVisitor : public SGStateAttributeVisitor {
217 RemoveModeVisitor(osg::StateAttribute::GLMode mode) :
220 virtual void apply(osg::StateSet* stateSet)
224 stateSet->removeMode(_mode);
227 osg::StateAttribute::GLMode _mode;
230 class SGAnimation::RemoveAttributeVisitor : public SGStateAttributeVisitor {
232 RemoveAttributeVisitor(osg::StateAttribute::Type type) :
235 virtual void apply(osg::StateSet* stateSet)
239 while (stateSet->getAttribute(_type)) {
240 stateSet->removeAttribute(_type);
244 osg::StateAttribute::Type _type;
247 class SGAnimation::RemoveTextureModeVisitor : public SGStateAttributeVisitor {
249 RemoveTextureModeVisitor(unsigned unit, osg::StateAttribute::GLMode mode) :
253 virtual void apply(osg::StateSet* stateSet)
257 stateSet->removeTextureMode(_unit, _mode);
261 osg::StateAttribute::GLMode _mode;
264 class SGAnimation::RemoveTextureAttributeVisitor :
265 public SGStateAttributeVisitor {
267 RemoveTextureAttributeVisitor(unsigned unit,
268 osg::StateAttribute::Type type) :
272 virtual void apply(osg::StateSet* stateSet)
276 while (stateSet->getTextureAttribute(_unit, _type)) {
277 stateSet->removeTextureAttribute(_unit, _type);
282 osg::StateAttribute::Type _type;
285 class SGAnimation::BinToInheritVisitor : public SGStateAttributeVisitor {
287 virtual void apply(osg::StateSet* stateSet)
291 stateSet->setRenderBinToInherit();
295 class SGAnimation::DrawableCloneVisitor : public osg::NodeVisitor {
297 DrawableCloneVisitor() :
298 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
300 void apply(osg::Geode& geode)
302 for (unsigned i = 0 ; i < geode.getNumDrawables(); ++i) {
303 osg::CopyOp copyOp(osg::CopyOp::DEEP_COPY_ALL &
304 ~osg::CopyOp::DEEP_COPY_TEXTURES);
305 geode.setDrawable(i, copyOp(geode.getDrawable(i)));
312 // Set all drawables to not use display lists. OSG will use
313 // glDrawArrays instead.
314 struct DoDrawArraysVisitor : public osg::NodeVisitor {
315 DoDrawArraysVisitor() :
316 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
318 void apply(osg::Geode& geode)
323 for (int i = 0; i < (int)geode.getNumDrawables(); ++i)
324 geode.getDrawable(i)->setUseDisplayList(false);
329 SGAnimation::SGAnimation(const SGPropertyNode* configNode,
330 SGPropertyNode* modelRoot) :
331 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
333 _configNode(configNode),
334 _modelRoot(modelRoot)
336 _name = configNode->getStringValue("name", "");
337 _enableHOT = configNode->getBoolValue("enable-hot", true);
338 std::vector<SGPropertyNode_ptr> objectNames =
339 configNode->getChildren("object-name");
340 for (unsigned i = 0; i < objectNames.size(); ++i)
341 _objectNames.push_back(objectNames[i]->getStringValue());
344 SGAnimation::~SGAnimation()
348 std::list<std::string>::const_iterator i;
350 for (i = _objectNames.begin(); i != _objectNames.end(); ++i)
360 SG_LOG(SG_IO, SG_ALERT, "Could not find at least one of the following"
361 " objects for animation: " << info);
367 SGAnimation::animate(osg::Node* node, const SGPropertyNode* configNode,
368 SGPropertyNode* modelRoot,
369 const osgDB::Options* options,
370 const string &path, int i)
372 std::string type = configNode->getStringValue("type", "none");
373 if (type == "alpha-test") {
374 SGAlphaTestAnimation animInst(configNode, modelRoot);
375 animInst.apply(node);
376 } else if (type == "billboard") {
377 SGBillboardAnimation animInst(configNode, modelRoot);
378 animInst.apply(node);
379 } else if (type == "blend") {
380 SGBlendAnimation animInst(configNode, modelRoot);
381 animInst.apply(node);
382 } else if (type == "dist-scale") {
383 SGDistScaleAnimation animInst(configNode, modelRoot);
384 animInst.apply(node);
385 } else if (type == "flash") {
386 SGFlashAnimation animInst(configNode, modelRoot);
387 animInst.apply(node);
388 } else if (type == "interaction") {
389 SGInteractionAnimation animInst(configNode, modelRoot);
390 animInst.apply(node);
391 } else if (type == "material") {
392 SGMaterialAnimation animInst(configNode, modelRoot, options, path);
393 animInst.apply(node);
394 } else if (type == "noshadow") {
395 SGShadowAnimation animInst(configNode, modelRoot);
396 animInst.apply(node);
397 } else if (type == "pick") {
398 SGPickAnimation animInst(configNode, modelRoot);
399 animInst.apply(node);
400 } else if (type == "range") {
401 SGRangeAnimation animInst(configNode, modelRoot);
402 animInst.apply(node);
403 } else if (type == "rotate" || type == "spin") {
404 SGRotateAnimation animInst(configNode, modelRoot);
405 animInst.apply(node);
406 } else if (type == "scale") {
407 SGScaleAnimation animInst(configNode, modelRoot);
408 animInst.apply(node);
409 } else if (type == "select") {
410 SGSelectAnimation animInst(configNode, modelRoot);
411 animInst.apply(node);
412 } else if (type == "shader") {
413 SGShaderAnimation animInst(configNode, modelRoot, options);
414 animInst.apply(node);
415 } else if (type == "textranslate" || type == "texrotate" ||
416 type == "texmultiple") {
417 SGTexTransformAnimation animInst(configNode, modelRoot);
418 animInst.apply(node);
419 } else if (type == "timed") {
420 SGTimedAnimation animInst(configNode, modelRoot);
421 animInst.apply(node);
422 } else if (type == "translate") {
423 SGTranslateAnimation animInst(configNode, modelRoot);
424 animInst.apply(node);
425 } else if (type == "light") {
426 SGLightAnimation animInst(configNode, modelRoot, path, i);
427 animInst.apply(node);
428 } else if (type == "null" || type == "none" || type.empty()) {
429 SGGroupAnimation animInst(configNode, modelRoot);
430 animInst.apply(node);
439 SGAnimation::apply(osg::Node* node)
441 // duh what a special case ...
442 if (_objectNames.empty()) {
443 osg::Group* group = node->asGroup();
445 osg::ref_ptr<osg::Group> animationGroup;
446 installInGroup(std::string(), *group, animationGroup);
453 SGAnimation::install(osg::Node& node)
457 node.setNodeMask( SG_NODEMASK_TERRAIN_BIT | node.getNodeMask());
459 node.setNodeMask(~SG_NODEMASK_TERRAIN_BIT & node.getNodeMask());
463 SGAnimation::createAnimationGroup(osg::Group& parent)
465 // default implementation, we do not need a new group
466 // for every animation type. Usually animations that just change
467 // the StateSet of some parts of the model
472 SGAnimation::apply(osg::Group& group)
474 // the trick is to first traverse the children and then
475 // possibly splice in a new group node if required.
476 // Else we end up in a recursive loop where we infinitly insert new
480 // Note that this algorithm preserves the order of the child objects
481 // like they appear in the object-name tags.
482 // The timed animations require this
483 osg::ref_ptr<osg::Group> animationGroup;
484 std::list<std::string>::const_iterator nameIt;
485 for (nameIt = _objectNames.begin(); nameIt != _objectNames.end(); ++nameIt)
486 installInGroup(*nameIt, group, animationGroup);
490 SGAnimation::installInGroup(const std::string& name, osg::Group& group,
491 osg::ref_ptr<osg::Group>& animationGroup)
493 int i = group.getNumChildren() - 1;
494 for (; 0 <= i; --i) {
495 osg::Node* child = group.getChild(i);
497 // Check if this one is already processed
498 if (std::find(_installedAnimations.begin(),
499 _installedAnimations.end(), child)
500 != _installedAnimations.end())
503 if (name.empty() || child->getName() == name) {
504 // fire the installation of the animation
507 // create a group node on demand
508 if (!animationGroup.valid()) {
509 animationGroup = createAnimationGroup(group);
510 // Animation type that does not require a new group,
511 // in this case we can stop and look for the next object
512 if (animationGroup.valid() && !_name.empty())
513 animationGroup->setName(_name);
515 if (animationGroup.valid()) {
516 animationGroup->addChild(child);
517 group.removeChild(i);
520 // store that we already have processed this child node
521 // We can hit this one twice if an animation references some
522 // part of a subtree twice
523 _installedAnimations.push_back(child);
529 SGAnimation::removeMode(osg::Node& node, osg::StateAttribute::GLMode mode)
531 RemoveModeVisitor visitor(mode);
532 node.accept(visitor);
536 SGAnimation::removeAttribute(osg::Node& node, osg::StateAttribute::Type type)
538 RemoveAttributeVisitor visitor(type);
539 node.accept(visitor);
543 SGAnimation::removeTextureMode(osg::Node& node, unsigned unit,
544 osg::StateAttribute::GLMode mode)
546 RemoveTextureModeVisitor visitor(unit, mode);
547 node.accept(visitor);
551 SGAnimation::removeTextureAttribute(osg::Node& node, unsigned unit,
552 osg::StateAttribute::Type type)
554 RemoveTextureAttributeVisitor visitor(unit, type);
555 node.accept(visitor);
559 SGAnimation::setRenderBinToInherit(osg::Node& node)
561 BinToInheritVisitor visitor;
562 node.accept(visitor);
566 SGAnimation::cloneDrawables(osg::Node& node)
568 DrawableCloneVisitor visitor;
569 node.accept(visitor);
573 SGAnimation::getCondition() const
575 const SGPropertyNode* conditionNode = _configNode->getChild("condition");
578 return sgReadCondition(_modelRoot, conditionNode);
583 ////////////////////////////////////////////////////////////////////////
584 // Implementation of null animation
585 ////////////////////////////////////////////////////////////////////////
587 // Ok, that is to build a subgraph from different other
588 // graph nodes. I guess that this stems from the time where modellers
589 // could not build hierarchical trees ...
590 SGGroupAnimation::SGGroupAnimation(const SGPropertyNode* configNode,
591 SGPropertyNode* modelRoot):
592 SGAnimation(configNode, modelRoot)
597 SGGroupAnimation::createAnimationGroup(osg::Group& parent)
599 osg::Group* group = new osg::Group;
600 parent.addChild(group);
605 ////////////////////////////////////////////////////////////////////////
606 // Implementation of translate animation
607 ////////////////////////////////////////////////////////////////////////
609 class SGTranslateAnimation::UpdateCallback : public osg::NodeCallback {
611 UpdateCallback(SGCondition const* condition,
612 SGExpressiond const* animationValue) :
613 _condition(condition),
614 _animationValue(animationValue)
616 setName("SGTranslateAnimation::UpdateCallback");
618 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
620 if (!_condition || _condition->test()) {
621 SGTranslateTransform* transform;
622 transform = static_cast<SGTranslateTransform*>(node);
623 transform->setValue(_animationValue->getValue());
628 SGSharedPtr<SGCondition const> _condition;
629 SGSharedPtr<SGExpressiond const> _animationValue;
632 SGTranslateAnimation::SGTranslateAnimation(const SGPropertyNode* configNode,
633 SGPropertyNode* modelRoot) :
634 SGAnimation(configNode, modelRoot)
636 _condition = getCondition();
637 SGSharedPtr<SGExpressiond> value;
638 value = read_value(configNode, modelRoot, "-m",
639 -SGLimitsd::max(), SGLimitsd::max());
640 _animationValue = value->simplify();
642 _initialValue = _animationValue->getValue();
646 if (configNode->hasValue("axis/x1-m")) {
648 v1[0] = configNode->getDoubleValue("axis/x1-m", 0);
649 v1[1] = configNode->getDoubleValue("axis/y1-m", 0);
650 v1[2] = configNode->getDoubleValue("axis/z1-m", 0);
651 v2[0] = configNode->getDoubleValue("axis/x2-m", 0);
652 v2[1] = configNode->getDoubleValue("axis/y2-m", 0);
653 v2[2] = configNode->getDoubleValue("axis/z2-m", 0);
656 _axis[0] = configNode->getDoubleValue("axis/x", 0);
657 _axis[1] = configNode->getDoubleValue("axis/y", 0);
658 _axis[2] = configNode->getDoubleValue("axis/z", 0);
660 if (8*SGLimitsd::min() < norm(_axis))
661 _axis = normalize(_axis);
665 SGTranslateAnimation::createAnimationGroup(osg::Group& parent)
667 SGTranslateTransform* transform = new SGTranslateTransform;
668 transform->setName("translate animation");
669 if (_animationValue && !_animationValue->isConst()) {
670 UpdateCallback* uc = new UpdateCallback(_condition, _animationValue);
671 transform->setUpdateCallback(uc);
673 transform->setAxis(_axis);
674 transform->setValue(_initialValue);
675 parent.addChild(transform);
680 ////////////////////////////////////////////////////////////////////////
681 // Implementation of rotate/spin animation
682 ////////////////////////////////////////////////////////////////////////
684 class SGRotAnimTransform : public SGRotateTransform
687 SGRotAnimTransform();
688 SGRotAnimTransform(const SGRotAnimTransform&,
689 const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY);
690 META_Node(simgear, SGRotAnimTransform);
691 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
692 osg::NodeVisitor* nv) const;
693 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
694 osg::NodeVisitor* nv) const;
695 SGSharedPtr<SGCondition const> _condition;
696 SGSharedPtr<SGExpressiond const> _animationValue;
697 // used when condition is false
698 mutable double _lastAngle;
701 SGRotAnimTransform::SGRotAnimTransform()
706 SGRotAnimTransform::SGRotAnimTransform(const SGRotAnimTransform& rhs,
707 const osg::CopyOp& copyop)
708 : SGRotateTransform(rhs, copyop), _condition(rhs._condition),
709 _animationValue(rhs._animationValue), _lastAngle(rhs._lastAngle)
713 bool SGRotAnimTransform::computeLocalToWorldMatrix(osg::Matrix& matrix,
714 osg::NodeVisitor* nv) const
717 if (!_condition || _condition->test()) {
718 angle = _animationValue->getValue();
723 double angleRad = SGMiscd::deg2rad(angle);
724 if (_referenceFrame == RELATIVE_RF) {
727 set_rotation(tmp, angleRad, getCenter(), getAxis());
731 SGRotateTransform::set_rotation(tmp, angleRad, getCenter(), getAxis());
737 bool SGRotAnimTransform::computeWorldToLocalMatrix(osg::Matrix& matrix,
738 osg::NodeVisitor* nv) const
741 if (!_condition || _condition->test()) {
742 angle = _animationValue->getValue();
747 double angleRad = SGMiscd::deg2rad(angle);
748 if (_referenceFrame == RELATIVE_RF) {
751 set_rotation(tmp, -angleRad, getCenter(), getAxis());
752 matrix.postMult(tmp);
755 set_rotation(tmp, -angleRad, getCenter(), getAxis());
762 class SpinAnimCallback : public osg::NodeCallback {
764 SpinAnimCallback(SGCondition const* condition,
765 SGExpressiond const* animationValue,
766 double initialValue = 0.0) :
767 _condition(condition),
768 _animationValue(animationValue),
769 _initialValue(initialValue)
771 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv);
773 SGSharedPtr<SGCondition const> _condition;
774 SGSharedPtr<SGExpressiond const> _animationValue;
775 double _initialValue;
777 // This cull callback can run in different threads if there is
778 // more than one camera. It is probably safe to overwrite the
779 // reference values in multiple threads, but we'll provide a
780 // threadsafe way to manage those values just to be safe.
781 struct ReferenceValues {
782 ReferenceValues(double t, double rot, double vel)
783 : _time(t), _rotation(rot), _rotVelocity(vel)
790 OpenThreads::AtomicPtr _referenceValues;
793 void SpinAnimCallback::operator()(osg::Node* node, osg::NodeVisitor* nv)
796 SGRotateTransform* transform = static_cast<SGRotateTransform*>(node);
797 EffectCullVisitor* cv = dynamic_cast<EffectCullVisitor*>(nv);
800 if (!_condition || _condition->test()) {
801 double t = nv->getFrameStamp()->getReferenceTime();
802 double rps = _animationValue->getValue() / 60.0;
803 ReferenceValues* refval = static_cast<ReferenceValues*>(_referenceValues.get());
804 if (!refval || refval->_rotVelocity != rps) {
805 ReferenceValues* newref = 0;
808 newref = new ReferenceValues(t, 0.0, rps);
810 double newRot = refval->_rotation + (t - refval->_time) * refval->_rotVelocity;
811 newref = new ReferenceValues(t, newRot, rps);
813 if (_referenceValues.assign(newref, refval)) {
818 double rotation = refval->_rotation + (t - refval->_time) * rps;
820 double rot = modf(rotation, &intPart);
821 double angle = rot * 2.0 * osg::PI;
822 const SGVec3d& sgcenter = transform->getCenter();
823 const SGVec3d& sgaxis = transform->getAxis();
824 Matrixd mat = Matrixd::translate(-sgcenter[0], -sgcenter[1], -sgcenter[2])
825 * Matrixd::rotate(angle, sgaxis[0], sgaxis[1], sgaxis[2])
826 * Matrixd::translate(sgcenter[0], sgcenter[1], sgcenter[2])
827 * *cv->getModelViewMatrix();
828 ref_ptr<RefMatrix> refmat = new RefMatrix(mat);
829 cv->pushModelViewMatrix(refmat.get(), transform->getReferenceFrame());
830 traverse(transform, nv);
831 cv->popModelViewMatrix();
833 traverse(transform, nv);
837 SGRotateAnimation::SGRotateAnimation(const SGPropertyNode* configNode,
838 SGPropertyNode* modelRoot) :
839 SGAnimation(configNode, modelRoot)
841 std::string type = configNode->getStringValue("type", "");
842 _isSpin = (type == "spin");
844 _condition = getCondition();
845 SGSharedPtr<SGExpressiond> value;
846 value = read_value(configNode, modelRoot, "-deg",
847 -SGLimitsd::max(), SGLimitsd::max());
848 _animationValue = value->simplify();
850 _initialValue = _animationValue->getValue();
853 _center = SGVec3d::zeros();
854 if (configNode->hasValue("axis/x1-m")) {
856 v1[0] = configNode->getDoubleValue("axis/x1-m", 0);
857 v1[1] = configNode->getDoubleValue("axis/y1-m", 0);
858 v1[2] = configNode->getDoubleValue("axis/z1-m", 0);
859 v2[0] = configNode->getDoubleValue("axis/x2-m", 0);
860 v2[1] = configNode->getDoubleValue("axis/y2-m", 0);
861 v2[2] = configNode->getDoubleValue("axis/z2-m", 0);
862 _center = 0.5*(v1+v2);
865 _axis[0] = configNode->getDoubleValue("axis/x", 0);
866 _axis[1] = configNode->getDoubleValue("axis/y", 0);
867 _axis[2] = configNode->getDoubleValue("axis/z", 0);
869 if (8*SGLimitsd::min() < norm(_axis))
870 _axis = normalize(_axis);
872 _center[0] = configNode->getDoubleValue("center/x-m", _center[0]);
873 _center[1] = configNode->getDoubleValue("center/y-m", _center[1]);
874 _center[2] = configNode->getDoubleValue("center/z-m", _center[2]);
878 SGRotateAnimation::createAnimationGroup(osg::Group& parent)
881 SGRotateTransform* transform = new SGRotateTransform;
882 transform->setName("spin rotate animation");
883 SpinAnimCallback* cc;
884 cc = new SpinAnimCallback(_condition, _animationValue, _initialValue);
885 transform->setCullCallback(cc);
886 transform->setCenter(_center);
887 transform->setAxis(_axis);
888 transform->setAngleDeg(_initialValue);
889 parent.addChild(transform);
892 SGRotAnimTransform* transform = new SGRotAnimTransform;
893 transform->setName("rotate animation");
894 transform->_condition = _condition;
895 transform->_animationValue = _animationValue;
896 transform->_lastAngle = _initialValue;
897 transform->setCenter(_center);
898 transform->setAxis(_axis);
899 parent.addChild(transform);
905 ////////////////////////////////////////////////////////////////////////
906 // Implementation of scale animation
907 ////////////////////////////////////////////////////////////////////////
909 class SGScaleAnimation::UpdateCallback : public osg::NodeCallback {
911 UpdateCallback(const SGCondition* condition,
912 SGSharedPtr<const SGExpressiond> animationValue[3]) :
913 _condition(condition)
915 _animationValue[0] = animationValue[0];
916 _animationValue[1] = animationValue[1];
917 _animationValue[2] = animationValue[2];
918 setName("SGScaleAnimation::UpdateCallback");
920 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
922 if (!_condition || _condition->test()) {
923 SGScaleTransform* transform;
924 transform = static_cast<SGScaleTransform*>(node);
925 SGVec3d scale(_animationValue[0]->getValue(),
926 _animationValue[1]->getValue(),
927 _animationValue[2]->getValue());
928 transform->setScaleFactor(scale);
933 SGSharedPtr<SGCondition const> _condition;
934 SGSharedPtr<SGExpressiond const> _animationValue[3];
937 SGScaleAnimation::SGScaleAnimation(const SGPropertyNode* configNode,
938 SGPropertyNode* modelRoot) :
939 SGAnimation(configNode, modelRoot)
941 _condition = getCondition();
943 // default offset/factor for all directions
944 double offset = configNode->getDoubleValue("offset", 0);
945 double factor = configNode->getDoubleValue("factor", 1);
947 SGSharedPtr<SGExpressiond> inPropExpr;
949 std::string inputPropertyName;
950 inputPropertyName = configNode->getStringValue("property", "");
951 if (inputPropertyName.empty()) {
952 inPropExpr = new SGConstExpression<double>(0);
954 SGPropertyNode* inputProperty;
955 inputProperty = modelRoot->getNode(inputPropertyName, true);
956 inPropExpr = new SGPropertyExpression<double>(inputProperty);
959 SGInterpTable* interpTable = read_interpolation_table(configNode);
961 SGSharedPtr<SGExpressiond> value;
962 value = new SGInterpTableExpression<double>(inPropExpr, interpTable);
963 _animationValue[0] = value->simplify();
964 _animationValue[1] = value->simplify();
965 _animationValue[2] = value->simplify();
966 } else if (configNode->getBoolValue("use-personality", false)) {
967 SGSharedPtr<SGExpressiond> value;
968 value = new SGPersonalityScaleOffsetExpression(inPropExpr, configNode,
969 "x-factor", "x-offset",
971 double minClip = configNode->getDoubleValue("x-min", 0);
972 double maxClip = configNode->getDoubleValue("x-max", SGLimitsd::max());
973 value = new SGClipExpression<double>(value, minClip, maxClip);
974 _animationValue[0] = value->simplify();
976 value = new SGPersonalityScaleOffsetExpression(inPropExpr, configNode,
977 "y-factor", "y-offset",
979 minClip = configNode->getDoubleValue("y-min", 0);
980 maxClip = configNode->getDoubleValue("y-max", SGLimitsd::max());
981 value = new SGClipExpression<double>(value, minClip, maxClip);
982 _animationValue[1] = value->simplify();
984 value = new SGPersonalityScaleOffsetExpression(inPropExpr, configNode,
985 "z-factor", "z-offset",
987 minClip = configNode->getDoubleValue("z-min", 0);
988 maxClip = configNode->getDoubleValue("z-max", SGLimitsd::max());
989 value = new SGClipExpression<double>(value, minClip, maxClip);
990 _animationValue[2] = value->simplify();
992 SGSharedPtr<SGExpressiond> value;
993 value = read_factor_offset(configNode, inPropExpr, "x-factor", "x-offset");
994 double minClip = configNode->getDoubleValue("x-min", 0);
995 double maxClip = configNode->getDoubleValue("x-max", SGLimitsd::max());
996 value = new SGClipExpression<double>(value, minClip, maxClip);
997 _animationValue[0] = value->simplify();
999 value = read_factor_offset(configNode, inPropExpr, "y-factor", "y-offset");
1000 minClip = configNode->getDoubleValue("y-min", 0);
1001 maxClip = configNode->getDoubleValue("y-max", SGLimitsd::max());
1002 value = new SGClipExpression<double>(value, minClip, maxClip);
1003 _animationValue[1] = value->simplify();
1005 value = read_factor_offset(configNode, inPropExpr, "z-factor", "z-offset");
1006 minClip = configNode->getDoubleValue("z-min", 0);
1007 maxClip = configNode->getDoubleValue("z-max", SGLimitsd::max());
1008 value = new SGClipExpression<double>(value, minClip, maxClip);
1009 _animationValue[2] = value->simplify();
1011 _initialValue[0] = configNode->getDoubleValue("x-starting-scale", 1);
1012 _initialValue[0] *= configNode->getDoubleValue("x-factor", factor);
1013 _initialValue[0] += configNode->getDoubleValue("x-offset", offset);
1014 _initialValue[1] = configNode->getDoubleValue("y-starting-scale", 1);
1015 _initialValue[1] *= configNode->getDoubleValue("y-factor", factor);
1016 _initialValue[1] += configNode->getDoubleValue("y-offset", offset);
1017 _initialValue[2] = configNode->getDoubleValue("z-starting-scale", 1);
1018 _initialValue[2] *= configNode->getDoubleValue("z-factor", factor);
1019 _initialValue[2] += configNode->getDoubleValue("z-offset", offset);
1020 _center[0] = configNode->getDoubleValue("center/x-m", 0);
1021 _center[1] = configNode->getDoubleValue("center/y-m", 0);
1022 _center[2] = configNode->getDoubleValue("center/z-m", 0);
1026 SGScaleAnimation::createAnimationGroup(osg::Group& parent)
1028 SGScaleTransform* transform = new SGScaleTransform;
1029 transform->setName("scale animation");
1030 transform->setCenter(_center);
1031 transform->setScaleFactor(_initialValue);
1032 UpdateCallback* uc = new UpdateCallback(_condition, _animationValue);
1033 transform->setUpdateCallback(uc);
1034 parent.addChild(transform);
1039 // Don't create a new state state everytime we need GL_NORMALIZE!
1043 Mutex normalizeMutex;
1045 osg::StateSet* getNormalizeStateSet()
1047 static osg::ref_ptr<osg::StateSet> normalizeStateSet;
1048 ScopedLock<Mutex> lock(normalizeMutex);
1049 if (!normalizeStateSet.valid()) {
1050 normalizeStateSet = new osg::StateSet;
1051 normalizeStateSet->setMode(GL_NORMALIZE, osg::StateAttribute::ON);
1052 normalizeStateSet->setDataVariance(osg::Object::STATIC);
1054 return normalizeStateSet.get();
1058 ////////////////////////////////////////////////////////////////////////
1059 // Implementation of dist scale animation
1060 ////////////////////////////////////////////////////////////////////////
1062 class SGDistScaleAnimation::Transform : public osg::Transform {
1064 Transform() : _min_v(0.0), _max_v(0.0), _factor(0.0), _offset(0.0) {}
1065 Transform(const Transform& rhs,
1066 const osg::CopyOp& copyOp = osg::CopyOp::SHALLOW_COPY)
1067 : osg::Transform(rhs, copyOp), _table(rhs._table), _center(rhs._center),
1068 _min_v(rhs._min_v), _max_v(rhs._max_v), _factor(rhs._factor),
1069 _offset(rhs._offset)
1072 META_Node(simgear, SGDistScaleAnimation::Transform);
1073 Transform(const SGPropertyNode* configNode)
1075 setName(configNode->getStringValue("name", "dist scale animation"));
1076 setReferenceFrame(RELATIVE_RF);
1077 setStateSet(getNormalizeStateSet());
1078 _factor = configNode->getFloatValue("factor", 1);
1079 _offset = configNode->getFloatValue("offset", 0);
1080 _min_v = configNode->getFloatValue("min", SGLimitsf::epsilon());
1081 _max_v = configNode->getFloatValue("max", SGLimitsf::max());
1082 _table = read_interpolation_table(configNode);
1083 _center[0] = configNode->getFloatValue("center/x-m", 0);
1084 _center[1] = configNode->getFloatValue("center/y-m", 0);
1085 _center[2] = configNode->getFloatValue("center/z-m", 0);
1087 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
1088 osg::NodeVisitor* nv) const
1090 osg::Matrix transform;
1091 double scale_factor = computeScaleFactor(nv);
1092 transform(0,0) = scale_factor;
1093 transform(1,1) = scale_factor;
1094 transform(2,2) = scale_factor;
1095 transform(3,0) = _center[0]*(1 - scale_factor);
1096 transform(3,1) = _center[1]*(1 - scale_factor);
1097 transform(3,2) = _center[2]*(1 - scale_factor);
1098 matrix.preMult(transform);
1102 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
1103 osg::NodeVisitor* nv) const
1105 double scale_factor = computeScaleFactor(nv);
1106 if (fabs(scale_factor) <= SGLimits<double>::min())
1108 osg::Matrix transform;
1109 double rScaleFactor = 1/scale_factor;
1110 transform(0,0) = rScaleFactor;
1111 transform(1,1) = rScaleFactor;
1112 transform(2,2) = rScaleFactor;
1113 transform(3,0) = _center[0]*(1 - rScaleFactor);
1114 transform(3,1) = _center[1]*(1 - rScaleFactor);
1115 transform(3,2) = _center[2]*(1 - rScaleFactor);
1116 matrix.postMult(transform);
1120 static bool writeLocalData(const osg::Object& obj, osgDB::Output& fw)
1122 const Transform& trans = static_cast<const Transform&>(obj);
1123 fw.indent() << "center " << trans._center << "\n";
1124 fw.indent() << "min_v " << trans._min_v << "\n";
1125 fw.indent() << "max_v " << trans._max_v << "\n";
1126 fw.indent() << "factor " << trans._factor << "\n";
1127 fw.indent() << "offset " << trans._offset << "\n";
1131 double computeScaleFactor(osg::NodeVisitor* nv) const
1136 double scale_factor = (toOsg(_center) - nv->getEyePoint()).length();
1138 scale_factor = _factor * scale_factor + _offset;
1140 scale_factor = _table->interpolate( scale_factor );
1142 if (scale_factor < _min_v)
1143 scale_factor = _min_v;
1144 if (scale_factor > _max_v)
1145 scale_factor = _max_v;
1147 return scale_factor;
1150 SGSharedPtr<SGInterpTable> _table;
1159 SGDistScaleAnimation::SGDistScaleAnimation(const SGPropertyNode* configNode,
1160 SGPropertyNode* modelRoot) :
1161 SGAnimation(configNode, modelRoot)
1166 SGDistScaleAnimation::createAnimationGroup(osg::Group& parent)
1168 Transform* transform = new Transform(getConfig());
1169 parent.addChild(transform);
1175 osgDB::RegisterDotOsgWrapperProxy distScaleAnimationTransformProxy
1177 new SGDistScaleAnimation::Transform,
1178 "SGDistScaleAnimation::Transform",
1179 "Object Node Transform SGDistScaleAnimation::Transform Group",
1181 &SGDistScaleAnimation::Transform::writeLocalData
1185 ////////////////////////////////////////////////////////////////////////
1186 // Implementation of flash animation
1187 ////////////////////////////////////////////////////////////////////////
1189 class SGFlashAnimation::Transform : public osg::Transform {
1191 Transform() : _power(0.0), _factor(0.0), _offset(0.0), _min_v(0.0),
1192 _max_v(0.0), _two_sides(false)
1195 Transform(const Transform& rhs,
1196 const osg::CopyOp& copyOp = osg::CopyOp::SHALLOW_COPY)
1197 : osg::Transform(rhs, copyOp), _center(rhs._center), _axis(rhs._axis),
1198 _power(rhs._power), _factor(rhs._factor), _offset(rhs._offset),
1199 _min_v(rhs._min_v), _max_v(rhs._max_v), _two_sides(rhs._two_sides)
1202 META_Node(simgear, SGFlashAnimation::Transform);
1204 Transform(const SGPropertyNode* configNode)
1206 setReferenceFrame(RELATIVE_RF);
1207 setName(configNode->getStringValue("name", "flash animation"));
1208 setStateSet(getNormalizeStateSet());
1210 _axis[0] = configNode->getFloatValue("axis/x", 0);
1211 _axis[1] = configNode->getFloatValue("axis/y", 0);
1212 _axis[2] = configNode->getFloatValue("axis/z", 1);
1215 _center[0] = configNode->getFloatValue("center/x-m", 0);
1216 _center[1] = configNode->getFloatValue("center/y-m", 0);
1217 _center[2] = configNode->getFloatValue("center/z-m", 0);
1219 _offset = configNode->getFloatValue("offset", 0);
1220 _factor = configNode->getFloatValue("factor", 1);
1221 _power = configNode->getFloatValue("power", 1);
1222 _two_sides = configNode->getBoolValue("two-sides", false);
1224 _min_v = configNode->getFloatValue("min", SGLimitsf::epsilon());
1225 _max_v = configNode->getFloatValue("max", 1);
1227 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
1228 osg::NodeVisitor* nv) const
1230 osg::Matrix transform;
1231 double scale_factor = computeScaleFactor(nv);
1232 transform(0,0) = scale_factor;
1233 transform(1,1) = scale_factor;
1234 transform(2,2) = scale_factor;
1235 transform(3,0) = _center[0]*(1 - scale_factor);
1236 transform(3,1) = _center[1]*(1 - scale_factor);
1237 transform(3,2) = _center[2]*(1 - scale_factor);
1238 matrix.preMult(transform);
1242 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
1243 osg::NodeVisitor* nv) const
1245 double scale_factor = computeScaleFactor(nv);
1246 if (fabs(scale_factor) <= SGLimits<double>::min())
1248 osg::Matrix transform;
1249 double rScaleFactor = 1/scale_factor;
1250 transform(0,0) = rScaleFactor;
1251 transform(1,1) = rScaleFactor;
1252 transform(2,2) = rScaleFactor;
1253 transform(3,0) = _center[0]*(1 - rScaleFactor);
1254 transform(3,1) = _center[1]*(1 - rScaleFactor);
1255 transform(3,2) = _center[2]*(1 - rScaleFactor);
1256 matrix.postMult(transform);
1260 static bool writeLocalData(const osg::Object& obj, osgDB::Output& fw)
1262 const Transform& trans = static_cast<const Transform&>(obj);
1263 fw.indent() << "center " << trans._center[0] << " "
1264 << trans._center[1] << " " << trans._center[2] << " " << "\n";
1265 fw.indent() << "axis " << trans._axis[0] << " "
1266 << trans._axis[1] << " " << trans._axis[2] << " " << "\n";
1267 fw.indent() << "power " << trans._power << " \n";
1268 fw.indent() << "min_v " << trans._min_v << "\n";
1269 fw.indent() << "max_v " << trans._max_v << "\n";
1270 fw.indent() << "factor " << trans._factor << "\n";
1271 fw.indent() << "offset " << trans._offset << "\n";
1272 fw.indent() << "twosides " << (trans._two_sides ? "true" : "false") << "\n";
1276 double computeScaleFactor(osg::NodeVisitor* nv) const
1281 osg::Vec3 localEyeToCenter = nv->getEyePoint() - _center;
1282 localEyeToCenter.normalize();
1284 double cos_angle = localEyeToCenter*_axis;
1285 double scale_factor = 0;
1286 if ( _two_sides && cos_angle < 0 )
1287 scale_factor = _factor * pow( -cos_angle, _power ) + _offset;
1288 else if ( cos_angle > 0 )
1289 scale_factor = _factor * pow( cos_angle, _power ) + _offset;
1291 if ( scale_factor < _min_v )
1292 scale_factor = _min_v;
1293 if ( scale_factor > _max_v )
1294 scale_factor = _max_v;
1296 return scale_factor;
1299 virtual osg::BoundingSphere computeBound() const
1301 // avoid being culled away by small feature culling
1302 osg::BoundingSphere bs = osg::Group::computeBound();
1303 bs.radius() *= _max_v;
1310 double _power, _factor, _offset, _min_v, _max_v;
1315 SGFlashAnimation::SGFlashAnimation(const SGPropertyNode* configNode,
1316 SGPropertyNode* modelRoot) :
1317 SGAnimation(configNode, modelRoot)
1322 SGFlashAnimation::createAnimationGroup(osg::Group& parent)
1324 Transform* transform = new Transform(getConfig());
1325 parent.addChild(transform);
1331 osgDB::RegisterDotOsgWrapperProxy flashAnimationTransformProxy
1333 new SGFlashAnimation::Transform,
1334 "SGFlashAnimation::Transform",
1335 "Object Node Transform SGFlashAnimation::Transform Group",
1337 &SGFlashAnimation::Transform::writeLocalData
1341 ////////////////////////////////////////////////////////////////////////
1342 // Implementation of billboard animation
1343 ////////////////////////////////////////////////////////////////////////
1345 class SGBillboardAnimation::Transform : public osg::Transform {
1347 Transform() : _spherical(true) {}
1348 Transform(const Transform& rhs,
1349 const osg::CopyOp& copyOp = osg::CopyOp::SHALLOW_COPY)
1350 : osg::Transform(rhs, copyOp), _spherical(rhs._spherical) {}
1351 META_Node(simgear, SGBillboardAnimation::Transform);
1352 Transform(const SGPropertyNode* configNode) :
1353 _spherical(configNode->getBoolValue("spherical", true))
1355 setReferenceFrame(RELATIVE_RF);
1356 setName(configNode->getStringValue("name", "billboard animation"));
1358 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
1359 osg::NodeVisitor* nv) const
1361 // More or less taken from plibs ssgCutout
1363 matrix(0,0) = 1; matrix(0,1) = 0; matrix(0,2) = 0;
1364 matrix(1,0) = 0; matrix(1,1) = 0; matrix(1,2) = -1;
1365 matrix(2,0) = 0; matrix(2,1) = 1; matrix(2,2) = 0;
1367 osg::Vec3 zAxis(matrix(2, 0), matrix(2, 1), matrix(2, 2));
1368 osg::Vec3 xAxis = osg::Vec3(0, 0, -1)^zAxis;
1369 osg::Vec3 yAxis = zAxis^xAxis;
1375 matrix(0,0) = xAxis[0]; matrix(0,1) = xAxis[1]; matrix(0,2) = xAxis[2];
1376 matrix(1,0) = yAxis[0]; matrix(1,1) = yAxis[1]; matrix(1,2) = yAxis[2];
1377 matrix(2,0) = zAxis[0]; matrix(2,1) = zAxis[1]; matrix(2,2) = zAxis[2];
1382 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
1383 osg::NodeVisitor* nv) const
1385 // Hmm, don't yet know how to get that back ...
1388 static bool writeLocalData(const osg::Object& obj, osgDB::Output& fw)
1390 const Transform& trans = static_cast<const Transform&>(obj);
1392 fw.indent() << (trans._spherical ? "true" : "false") << "\n";
1400 SGBillboardAnimation::SGBillboardAnimation(const SGPropertyNode* configNode,
1401 SGPropertyNode* modelRoot) :
1402 SGAnimation(configNode, modelRoot)
1407 SGBillboardAnimation::createAnimationGroup(osg::Group& parent)
1409 Transform* transform = new Transform(getConfig());
1410 parent.addChild(transform);
1416 osgDB::RegisterDotOsgWrapperProxy billboardAnimationTransformProxy
1418 new SGBillboardAnimation::Transform,
1419 "SGBillboardAnimation::Transform",
1420 "Object Node Transform SGBillboardAnimation::Transform Group",
1422 &SGBillboardAnimation::Transform::writeLocalData
1426 ////////////////////////////////////////////////////////////////////////
1427 // Implementation of a range animation
1428 ////////////////////////////////////////////////////////////////////////
1430 class SGRangeAnimation::UpdateCallback : public osg::NodeCallback {
1432 UpdateCallback(const SGCondition* condition,
1433 const SGExpressiond* minAnimationValue,
1434 const SGExpressiond* maxAnimationValue,
1435 double minValue, double maxValue) :
1436 _condition(condition),
1437 _minAnimationValue(minAnimationValue),
1438 _maxAnimationValue(maxAnimationValue),
1439 _minStaticValue(minValue),
1440 _maxStaticValue(maxValue)
1442 setName("SGRangeAnimation::UpdateCallback");
1444 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1446 osg::LOD* lod = static_cast<osg::LOD*>(node);
1447 if (!_condition || _condition->test()) {
1449 if (_minAnimationValue)
1450 minRange = _minAnimationValue->getValue();
1452 minRange = _minStaticValue;
1454 if (_maxAnimationValue)
1455 maxRange = _maxAnimationValue->getValue();
1457 maxRange = _maxStaticValue;
1458 lod->setRange(0, minRange, maxRange);
1460 lod->setRange(0, 0, SGLimitsf::max());
1466 SGSharedPtr<const SGCondition> _condition;
1467 SGSharedPtr<const SGExpressiond> _minAnimationValue;
1468 SGSharedPtr<const SGExpressiond> _maxAnimationValue;
1469 double _minStaticValue;
1470 double _maxStaticValue;
1473 SGRangeAnimation::SGRangeAnimation(const SGPropertyNode* configNode,
1474 SGPropertyNode* modelRoot) :
1475 SGAnimation(configNode, modelRoot)
1477 _condition = getCondition();
1479 std::string inputPropertyName;
1480 inputPropertyName = configNode->getStringValue("min-property", "");
1481 if (!inputPropertyName.empty()) {
1482 SGPropertyNode* inputProperty;
1483 inputProperty = modelRoot->getNode(inputPropertyName, true);
1484 SGSharedPtr<SGExpressiond> value;
1485 value = new SGPropertyExpression<double>(inputProperty);
1487 value = read_factor_offset(configNode, value, "min-factor", "min-offset");
1488 _minAnimationValue = value->simplify();
1490 inputPropertyName = configNode->getStringValue("max-property", "");
1491 if (!inputPropertyName.empty()) {
1492 SGPropertyNode* inputProperty;
1493 inputProperty = modelRoot->getNode(inputPropertyName.c_str(), true);
1495 SGSharedPtr<SGExpressiond> value;
1496 value = new SGPropertyExpression<double>(inputProperty);
1498 value = read_factor_offset(configNode, value, "max-factor", "max-offset");
1499 _maxAnimationValue = value->simplify();
1502 _initialValue[0] = configNode->getDoubleValue("min-m", 0);
1503 _initialValue[0] *= configNode->getDoubleValue("min-factor", 1);
1504 _initialValue[1] = configNode->getDoubleValue("max-m", SGLimitsf::max());
1505 _initialValue[1] *= configNode->getDoubleValue("max-factor", 1);
1509 SGRangeAnimation::createAnimationGroup(osg::Group& parent)
1511 osg::Group* group = new osg::Group;
1512 group->setName("range animation group");
1514 osg::LOD* lod = new osg::LOD;
1515 lod->setName("range animation node");
1516 parent.addChild(lod);
1518 lod->addChild(group, _initialValue[0], _initialValue[1]);
1519 lod->setCenterMode(osg::LOD::USE_BOUNDING_SPHERE_CENTER);
1520 lod->setRangeMode(osg::LOD::DISTANCE_FROM_EYE_POINT);
1521 if (_minAnimationValue || _maxAnimationValue || _condition) {
1523 uc = new UpdateCallback(_condition, _minAnimationValue, _maxAnimationValue,
1524 _initialValue[0], _initialValue[1]);
1525 lod->setUpdateCallback(uc);
1531 ////////////////////////////////////////////////////////////////////////
1532 // Implementation of a select animation
1533 ////////////////////////////////////////////////////////////////////////
1535 SGSelectAnimation::SGSelectAnimation(const SGPropertyNode* configNode,
1536 SGPropertyNode* modelRoot) :
1537 SGAnimation(configNode, modelRoot)
1542 SGSelectAnimation::createAnimationGroup(osg::Group& parent)
1544 // if no condition given, this is a noop.
1545 SGSharedPtr<SGCondition const> condition = getCondition();
1546 // trick, gets deleted with all its 'animated' children
1547 // when the animation installer returns
1549 return new osg::Group;
1550 simgear::ConditionNode* cn = new simgear::ConditionNode;
1551 cn->setName("select animation node");
1552 cn->setCondition(condition.ptr());
1553 osg::Group* grp = new osg::Group;
1555 parent.addChild(cn);
1561 ////////////////////////////////////////////////////////////////////////
1562 // Implementation of alpha test animation
1563 ////////////////////////////////////////////////////////////////////////
1565 SGAlphaTestAnimation::SGAlphaTestAnimation(const SGPropertyNode* configNode,
1566 SGPropertyNode* modelRoot) :
1567 SGAnimation(configNode, modelRoot)
1573 // Keep one copy of the most common alpha test its state set.
1574 ReentrantMutex alphaTestMutex;
1575 osg::ref_ptr<osg::AlphaFunc> standardAlphaFunc;
1576 osg::ref_ptr<osg::StateSet> alphaFuncStateSet;
1578 osg::AlphaFunc* makeAlphaFunc(float clamp)
1580 ScopedLock<ReentrantMutex> lock(alphaTestMutex);
1581 if (osg::equivalent(clamp, 0.01f)) {
1582 if (standardAlphaFunc.valid())
1583 return standardAlphaFunc.get();
1586 osg::AlphaFunc* alphaFunc = new osg::AlphaFunc;
1587 alphaFunc->setFunction(osg::AlphaFunc::GREATER);
1588 alphaFunc->setReferenceValue(clamp);
1589 alphaFunc->setDataVariance(osg::Object::STATIC);
1590 if (osg::equivalent(clamp, 0.01f))
1591 standardAlphaFunc = alphaFunc;
1595 osg::StateSet* makeAlphaTestStateSet(float clamp)
1597 using namespace OpenThreads;
1598 ScopedLock<ReentrantMutex> lock(alphaTestMutex);
1599 if (osg::equivalent(clamp, 0.01f)) {
1600 if (alphaFuncStateSet.valid())
1601 return alphaFuncStateSet.get();
1603 osg::AlphaFunc* alphaFunc = makeAlphaFunc(clamp);
1604 osg::StateSet* stateSet = new osg::StateSet;
1605 stateSet->setAttributeAndModes(alphaFunc,
1606 (osg::StateAttribute::ON
1607 | osg::StateAttribute::OVERRIDE));
1608 stateSet->setDataVariance(osg::Object::STATIC);
1609 if (osg::equivalent(clamp, 0.01f))
1610 alphaFuncStateSet = stateSet;
1615 SGAlphaTestAnimation::install(osg::Node& node)
1617 SGAnimation::install(node);
1619 float alphaClamp = getConfig()->getFloatValue("alpha-factor", 0);
1620 osg::StateSet* stateSet = node.getStateSet();
1622 node.setStateSet(makeAlphaTestStateSet(alphaClamp));
1624 stateSet->setAttributeAndModes(makeAlphaFunc(alphaClamp),
1625 (osg::StateAttribute::ON
1626 | osg::StateAttribute::OVERRIDE));
1631 //////////////////////////////////////////////////////////////////////
1632 // Blend animation installer
1633 //////////////////////////////////////////////////////////////////////
1635 // XXX This needs to be replaced by something using TexEnvCombine to
1636 // change the blend factor. Changing the alpha values in the geometry
1638 class SGBlendAnimation::BlendVisitor : public osg::NodeVisitor {
1640 BlendVisitor(float blend) :
1641 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
1643 { setVisitorType(osg::NodeVisitor::NODE_VISITOR); }
1644 virtual void apply(osg::Node& node)
1646 updateStateSet(node.getStateSet());
1649 virtual void apply(osg::Geode& node)
1651 apply((osg::Node&)node);
1652 unsigned nDrawables = node.getNumDrawables();
1653 for (unsigned i = 0; i < nDrawables; ++i) {
1654 osg::Drawable* drawable = node.getDrawable(i);
1655 osg::Geometry* geometry = drawable->asGeometry();
1658 osg::Array* array = geometry->getColorArray();
1661 osg::Vec4Array* vec4Array = dynamic_cast<osg::Vec4Array*>(array);
1664 for (unsigned k = 0; k < vec4Array->size(); ++k) {
1665 (*vec4Array)[k][3] = _blend;
1668 updateStateSet(drawable->getStateSet());
1671 void updateStateSet(osg::StateSet* stateSet)
1675 osg::StateAttribute* stateAttribute;
1676 stateAttribute = stateSet->getAttribute(osg::StateAttribute::MATERIAL);
1677 if (!stateAttribute)
1679 osg::Material* material = dynamic_cast<osg::Material*>(stateAttribute);
1682 material->setAlpha(osg::Material::FRONT_AND_BACK, _blend);
1684 stateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
1685 stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
1687 stateSet->setRenderingHint(osg::StateSet::DEFAULT_BIN);
1694 class SGBlendAnimation::UpdateCallback : public osg::NodeCallback {
1696 UpdateCallback(const SGPropertyNode* configNode, const SGExpressiond* v) :
1700 setName("SGBlendAnimation::UpdateCallback");
1702 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1704 double blend = _animationValue->getValue();
1705 if (blend != _prev_value) {
1706 _prev_value = blend;
1707 BlendVisitor visitor(1-blend);
1708 node->accept(visitor);
1714 SGSharedPtr<SGExpressiond const> _animationValue;
1718 SGBlendAnimation::SGBlendAnimation(const SGPropertyNode* configNode,
1719 SGPropertyNode* modelRoot)
1720 : SGAnimation(configNode, modelRoot),
1721 _animationValue(read_value(configNode, modelRoot, "", 0, 1))
1726 SGBlendAnimation::createAnimationGroup(osg::Group& parent)
1728 if (!_animationValue)
1731 osg::Group* group = new osg::Switch;
1732 group->setName("blend animation node");
1733 group->setUpdateCallback(new UpdateCallback(getConfig(), _animationValue));
1734 parent.addChild(group);
1739 SGBlendAnimation::install(osg::Node& node)
1741 SGAnimation::install(node);
1742 // make sure we do not change common geometries,
1743 // that also creates new display lists for these subgeometries.
1744 cloneDrawables(node);
1745 DoDrawArraysVisitor visitor;
1746 node.accept(visitor);
1750 //////////////////////////////////////////////////////////////////////
1751 // Timed animation installer
1752 //////////////////////////////////////////////////////////////////////
1756 class SGTimedAnimation::UpdateCallback : public osg::NodeCallback {
1758 UpdateCallback(const SGPropertyNode* configNode) :
1761 _duration_sec(configNode->getDoubleValue("duration-sec", 1)),
1762 _last_time_sec(SGLimitsd::max()),
1763 _use_personality(configNode->getBoolValue("use-personality", false))
1765 std::vector<SGSharedPtr<SGPropertyNode> > nodes;
1766 nodes = configNode->getChildren("branch-duration-sec");
1767 for (size_t i = 0; i < nodes.size(); ++i) {
1768 unsigned ind = nodes[ i ]->getIndex();
1769 while ( ind >= _durations.size() ) {
1770 _durations.push_back(DurationSpec(_duration_sec));
1772 SGPropertyNode_ptr rNode = nodes[i]->getChild("random");
1774 _durations[ind] = DurationSpec(nodes[ i ]->getDoubleValue());
1776 _durations[ind] = DurationSpec(rNode->getDoubleValue( "min", 0),
1777 rNode->getDoubleValue( "max", 1));
1780 setName("SGTimedAnimation::UpdateCallback");
1782 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1784 assert(dynamic_cast<osg::Switch*>(node));
1785 osg::Switch* sw = static_cast<osg::Switch*>(node);
1787 unsigned nChildren = sw->getNumChildren();
1789 // blow up the durations vector to the required size
1790 while (_durations.size() < nChildren) {
1791 _durations.push_back(_duration_sec);
1793 // make sure the current index is an duration that really exists
1794 _current_index = _current_index % nChildren;
1796 // update the time and compute the current systems time value
1797 double t = nv->getFrameStamp()->getReferenceTime();
1798 if (_last_time_sec == SGLimitsd::max()) {
1801 double dt = t - _last_time_sec;
1802 if (_use_personality)
1803 dt *= 1 + 0.2*(0.5 - sg_random());
1808 double currentDuration = _durations[_current_index].get();
1809 while (currentDuration < _reminder) {
1810 _reminder -= currentDuration;
1811 _current_index = (_current_index + 1) % nChildren;
1812 currentDuration = _durations[_current_index].get();
1815 sw->setSingleChildOn(_current_index);
1821 struct DurationSpec {
1822 DurationSpec(double t) :
1823 minTime(SGMiscd::max(0.01, t)),
1824 maxTime(SGMiscd::max(0.01, t))
1826 DurationSpec(double t0, double t1) :
1827 minTime(SGMiscd::max(0.01, t0)),
1828 maxTime(SGMiscd::max(0.01, t1))
1831 { return minTime + sg_random()*(maxTime - minTime); }
1835 std::vector<DurationSpec> _durations;
1836 unsigned _current_index;
1838 double _duration_sec;
1839 double _last_time_sec;
1840 bool _use_personality;
1844 SGTimedAnimation::SGTimedAnimation(const SGPropertyNode* configNode,
1845 SGPropertyNode* modelRoot)
1846 : SGAnimation(configNode, modelRoot)
1851 SGTimedAnimation::createAnimationGroup(osg::Group& parent)
1853 osg::Switch* sw = new osg::Switch;
1854 sw->setName("timed animation node");
1855 sw->setUpdateCallback(new UpdateCallback(getConfig()));
1856 parent.addChild(sw);
1861 ////////////////////////////////////////////////////////////////////////
1862 // dynamically switch on/off shadows
1863 ////////////////////////////////////////////////////////////////////////
1865 class SGShadowAnimation::UpdateCallback : public osg::NodeCallback {
1867 UpdateCallback(const SGCondition* condition) :
1868 _condition(condition)
1870 setName("SGShadowAnimation::UpdateCallback");
1872 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1874 if (_condition->test())
1875 node->setNodeMask( SG_NODEMASK_CASTSHADOW_BIT | node->getNodeMask());
1877 node->setNodeMask(~SG_NODEMASK_CASTSHADOW_BIT & node->getNodeMask());
1882 SGSharedPtr<const SGCondition> _condition;
1885 SGShadowAnimation::SGShadowAnimation(const SGPropertyNode* configNode,
1886 SGPropertyNode* modelRoot) :
1887 SGAnimation(configNode, modelRoot)
1892 SGShadowAnimation::createAnimationGroup(osg::Group& parent)
1894 SGSharedPtr<SGCondition const> condition = getCondition();
1896 osg::Group* group = new osg::Group;
1897 group->setName("shadow animation");
1899 group->setUpdateCallback(new UpdateCallback(condition));
1901 group->setNodeMask(~SG_NODEMASK_CASTSHADOW_BIT & group->getNodeMask());
1902 parent.addChild(group);
1907 ////////////////////////////////////////////////////////////////////////
1908 // Implementation of SGTexTransformAnimation
1909 ////////////////////////////////////////////////////////////////////////
1911 class SGTexTransformAnimation::Transform : public SGReferenced {
1916 virtual ~Transform()
1918 void setValue(double value)
1920 virtual void transform(osg::Matrix&) = 0;
1925 class SGTexTransformAnimation::Translation :
1926 public SGTexTransformAnimation::Transform {
1928 Translation(const SGVec3d& axis) :
1931 virtual void transform(osg::Matrix& matrix)
1934 set_translation(tmp, _value, _axis);
1935 matrix.preMult(tmp);
1941 class SGTexTransformAnimation::Rotation :
1942 public SGTexTransformAnimation::Transform {
1944 Rotation(const SGVec3d& axis, const SGVec3d& center) :
1948 virtual void transform(osg::Matrix& matrix)
1951 SGRotateTransform::set_rotation(tmp, SGMiscd::deg2rad(_value), _center,
1953 matrix.preMult(tmp);
1960 class SGTexTransformAnimation::UpdateCallback :
1961 public osg::StateAttribute::Callback {
1963 UpdateCallback(const SGCondition* condition) :
1964 _condition(condition)
1966 setName("SGTexTransformAnimation::UpdateCallback");
1968 virtual void operator () (osg::StateAttribute* sa, osg::NodeVisitor*)
1970 if (!_condition || _condition->test()) {
1971 TransformList::const_iterator i;
1972 for (i = _transforms.begin(); i != _transforms.end(); ++i)
1973 i->transform->setValue(i->value->getValue());
1975 assert(dynamic_cast<osg::TexMat*>(sa));
1976 osg::TexMat* texMat = static_cast<osg::TexMat*>(sa);
1977 texMat->getMatrix().makeIdentity();
1978 TransformList::const_iterator i;
1979 for (i = _transforms.begin(); i != _transforms.end(); ++i)
1980 i->transform->transform(texMat->getMatrix());
1982 void appendTransform(Transform* transform, SGExpressiond* value)
1984 Entry entry = { transform, value };
1985 transform->transform(_matrix);
1986 _transforms.push_back(entry);
1991 SGSharedPtr<Transform> transform;
1992 SGSharedPtr<const SGExpressiond> value;
1994 typedef std::vector<Entry> TransformList;
1995 TransformList _transforms;
1996 SGSharedPtr<const SGCondition> _condition;
1997 osg::Matrix _matrix;
2000 SGTexTransformAnimation::SGTexTransformAnimation(const SGPropertyNode* configNode,
2001 SGPropertyNode* modelRoot) :
2002 SGAnimation(configNode, modelRoot)
2007 SGTexTransformAnimation::createAnimationGroup(osg::Group& parent)
2009 osg::Group* group = new osg::Group;
2010 group->setName("texture transform group");
2011 osg::StateSet* stateSet = group->getOrCreateStateSet();
2012 stateSet->setDataVariance(osg::Object::DYNAMIC);
2013 osg::TexMat* texMat = new osg::TexMat;
2014 UpdateCallback* updateCallback = new UpdateCallback(getCondition());
2015 // interpret the configs ...
2016 std::string type = getType();
2018 if (type == "textranslate") {
2019 appendTexTranslate(getConfig(), updateCallback);
2020 } else if (type == "texrotate") {
2021 appendTexRotate(getConfig(), updateCallback);
2022 } else if (type == "texmultiple") {
2023 std::vector<SGSharedPtr<SGPropertyNode> > transformConfigs;
2024 transformConfigs = getConfig()->getChildren("transform");
2025 for (unsigned i = 0; i < transformConfigs.size(); ++i) {
2026 std::string subtype = transformConfigs[i]->getStringValue("subtype", "");
2027 if (subtype == "textranslate")
2028 appendTexTranslate(transformConfigs[i], updateCallback);
2029 else if (subtype == "texrotate")
2030 appendTexRotate(transformConfigs[i], updateCallback);
2032 SG_LOG(SG_INPUT, SG_ALERT,
2033 "Ignoring unknown texture transform subtype");
2036 SG_LOG(SG_INPUT, SG_ALERT, "Ignoring unknown texture transform type");
2039 texMat->setUpdateCallback(updateCallback);
2040 stateSet->setTextureAttribute(0, texMat);
2041 parent.addChild(group);
2046 SGTexTransformAnimation::appendTexTranslate(const SGPropertyNode* config,
2047 UpdateCallback* updateCallback)
2049 std::string propertyName = config->getStringValue("property", "");
2050 SGSharedPtr<SGExpressiond> value;
2051 if (propertyName.empty())
2052 value = new SGConstExpression<double>(0);
2054 SGPropertyNode* inputProperty = getModelRoot()->getNode(propertyName, true);
2055 value = new SGPropertyExpression<double>(inputProperty);
2058 SGInterpTable* table = read_interpolation_table(config);
2060 value = new SGInterpTableExpression<double>(value, table);
2061 double biasValue = config->getDoubleValue("bias", 0);
2063 value = new SGBiasExpression<double>(value, biasValue);
2064 value = new SGStepExpression<double>(value,
2065 config->getDoubleValue("step", 0),
2066 config->getDoubleValue("scroll", 0));
2067 value = value->simplify();
2069 double biasValue = config->getDoubleValue("bias", 0);
2071 value = new SGBiasExpression<double>(value, biasValue);
2072 value = new SGStepExpression<double>(value,
2073 config->getDoubleValue("step", 0),
2074 config->getDoubleValue("scroll", 0));
2075 value = read_offset_factor(config, value, "factor", "offset");
2077 if (config->hasChild("min") || config->hasChild("max")) {
2078 double minClip = config->getDoubleValue("min", -SGLimitsd::max());
2079 double maxClip = config->getDoubleValue("max", SGLimitsd::max());
2080 value = new SGClipExpression<double>(value, minClip, maxClip);
2082 value = value->simplify();
2084 SGVec3d axis(config->getDoubleValue("axis/x", 0),
2085 config->getDoubleValue("axis/y", 0),
2086 config->getDoubleValue("axis/z", 0));
2087 Translation* translation;
2088 translation = new Translation(normalize(axis));
2089 translation->setValue(config->getDoubleValue("starting-position", 0));
2090 updateCallback->appendTransform(translation, value);
2094 SGTexTransformAnimation::appendTexRotate(const SGPropertyNode* config,
2095 UpdateCallback* updateCallback)
2097 std::string propertyName = config->getStringValue("property", "");
2098 SGSharedPtr<SGExpressiond> value;
2099 if (propertyName.empty())
2100 value = new SGConstExpression<double>(0);
2102 SGPropertyNode* inputProperty = getModelRoot()->getNode(propertyName, true);
2103 value = new SGPropertyExpression<double>(inputProperty);
2106 SGInterpTable* table = read_interpolation_table(config);
2108 value = new SGInterpTableExpression<double>(value, table);
2109 double biasValue = config->getDoubleValue("bias", 0);
2111 value = new SGBiasExpression<double>(value, biasValue);
2112 value = new SGStepExpression<double>(value,
2113 config->getDoubleValue("step", 0),
2114 config->getDoubleValue("scroll", 0));
2115 value = value->simplify();
2117 double biasValue = config->getDoubleValue("bias", 0);
2119 value = new SGBiasExpression<double>(value, biasValue);
2120 value = new SGStepExpression<double>(value,
2121 config->getDoubleValue("step", 0),
2122 config->getDoubleValue("scroll", 0));
2123 value = read_offset_factor(config, value, "factor", "offset-deg");
2125 if (config->hasChild("min-deg") || config->hasChild("max-deg")) {
2126 double minClip = config->getDoubleValue("min-deg", -SGLimitsd::max());
2127 double maxClip = config->getDoubleValue("max-deg", SGLimitsd::max());
2128 value = new SGClipExpression<double>(value, minClip, maxClip);
2130 value = value->simplify();
2132 SGVec3d axis(config->getDoubleValue("axis/x", 0),
2133 config->getDoubleValue("axis/y", 0),
2134 config->getDoubleValue("axis/z", 0));
2135 SGVec3d center(config->getDoubleValue("center/x", 0),
2136 config->getDoubleValue("center/y", 0),
2137 config->getDoubleValue("center/z", 0));
2139 rotation = new Rotation(normalize(axis), center);
2140 rotation->setValue(config->getDoubleValue("starting-position-deg", 0));
2141 updateCallback->appendTransform(rotation, value);
2145 ////////////////////////////////////////////////////////////////////////
2146 // Implementation of SGPickAnimation
2147 ////////////////////////////////////////////////////////////////////////
2149 class SGPickAnimation::PickCallback : public SGPickCallback {
2151 PickCallback(const SGPropertyNode* configNode,
2152 SGPropertyNode* modelRoot) :
2153 _repeatable(configNode->getBoolValue("repeatable", false)),
2154 _repeatInterval(configNode->getDoubleValue("interval-sec", 0.1))
2156 SG_LOG(SG_INPUT, SG_DEBUG, "Reading all bindings");
2157 std::vector<SGPropertyNode_ptr> bindings;
2159 bindings = configNode->getChildren("button");
2160 for (unsigned int i = 0; i < bindings.size(); ++i) {
2161 _buttons.push_back( bindings[i]->getIntValue() );
2163 bindings = configNode->getChildren("binding");
2164 for (unsigned int i = 0; i < bindings.size(); ++i) {
2165 _bindingsDown.push_back(new SGBinding(bindings[i], modelRoot));
2168 const SGPropertyNode* upNode = configNode->getChild("mod-up");
2171 bindings = upNode->getChildren("binding");
2172 for (unsigned int i = 0; i < bindings.size(); ++i) {
2173 _bindingsUp.push_back(new SGBinding(bindings[i], modelRoot));
2176 virtual bool buttonPressed(int button, const Info&)
2179 for( std::vector<int>::iterator it = _buttons.begin(); it != _buttons.end(); ++it ) {
2180 if( *it == button ) {
2187 SGBindingList::const_iterator i;
2188 for (i = _bindingsDown.begin(); i != _bindingsDown.end(); ++i)
2190 _repeatTime = -_repeatInterval; // anti-bobble: delay start of repeat
2193 virtual void buttonReleased(void)
2195 SGBindingList::const_iterator i;
2196 for (i = _bindingsUp.begin(); i != _bindingsUp.end(); ++i)
2199 virtual void update(double dt)
2205 while (_repeatInterval < _repeatTime) {
2206 _repeatTime -= _repeatInterval;
2207 SGBindingList::const_iterator i;
2208 for (i = _bindingsDown.begin(); i != _bindingsDown.end(); ++i)
2213 SGBindingList _bindingsDown;
2214 SGBindingList _bindingsUp;
2215 std::vector<int> _buttons;
2217 double _repeatInterval;
2221 class VncVisitor : public osg::NodeVisitor {
2223 VncVisitor(double x, double y, int mask) :
2224 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
2225 _texX(x), _texY(y), _mask(mask), _done(false)
2227 SG_LOG(SG_INPUT, SG_DEBUG, "VncVisitor constructor "
2228 << x << "," << y << " mask " << mask);
2231 virtual void apply(osg::Node &node)
2233 // Some nodes have state sets attached
2234 touchStateSet(node.getStateSet());
2238 // See whether we are a geode worth exploring
2239 osg::Geode *g = dynamic_cast<osg::Geode*>(&node);
2241 // Go find all its drawables
2242 int i = g->getNumDrawables();
2244 osg::Drawable *d = g->getDrawable(i);
2245 if (d) touchDrawable(*d);
2247 // Out of optimism, do the same for EffectGeode
2248 simgear::EffectGeode *eg = dynamic_cast<simgear::EffectGeode*>(&node);
2250 for (simgear::EffectGeode::DrawablesIterator di = eg->drawablesBegin();
2251 di != eg->drawablesEnd(); di++) {
2252 touchDrawable(**di);
2254 // Now see whether the EffectGeode has an Effect
2255 simgear::Effect *e = eg->getEffect();
2257 touchStateSet(e->getDefaultStateSet());
2261 inline void touchDrawable(osg::Drawable &d)
2263 osg::StateSet *ss = d.getStateSet();
2267 void touchStateSet(osg::StateSet *ss)
2270 osg::StateAttribute *sa = ss->getTextureAttribute(0,
2271 osg::StateAttribute::TEXTURE);
2273 osg::Texture *t = sa->asTexture();
2275 osg::Image *img = t->getImage(0);
2278 int pixX = _texX * img->s();
2279 int pixY = _texY * img->t();
2280 _done = img->sendPointerEvent(pixX, pixY, _mask);
2281 SG_LOG(SG_INPUT, SG_DEBUG, "VncVisitor image said " << _done
2282 << " to coord " << pixX << "," << pixY);
2286 inline bool wasSuccessful()
2292 double _texX, _texY;
2298 class SGPickAnimation::VncCallback : public SGPickCallback {
2300 VncCallback(const SGPropertyNode* configNode,
2301 SGPropertyNode* modelRoot,
2305 SG_LOG(SG_INPUT, SG_DEBUG, "Configuring VNC callback");
2306 const char *cornernames[3] = {"top-left", "top-right", "bottom-left"};
2307 SGVec3d *cornercoords[3] = {&_topLeft, &_toRight, &_toDown};
2308 for (int c =0; c < 3; c++) {
2309 const SGPropertyNode* cornerNode = configNode->getChild(cornernames[c]);
2310 *cornercoords[c] = SGVec3d(
2311 cornerNode->getDoubleValue("x"),
2312 cornerNode->getDoubleValue("y"),
2313 cornerNode->getDoubleValue("z"));
2315 _toRight -= _topLeft;
2316 _toDown -= _topLeft;
2317 _squaredRight = dot(_toRight, _toRight);
2318 _squaredDown = dot(_toDown, _toDown);
2321 virtual bool buttonPressed(int button, const Info& info)
2323 SGVec3d loc(info.local);
2324 SG_LOG(SG_INPUT, SG_DEBUG, "VNC pressed " << button << ": " << loc);
2326 _x = dot(loc, _toRight) / _squaredRight;
2327 _y = dot(loc, _toDown) / _squaredDown;
2328 if (_x<0) _x = 0; else if (_x > 1) _x = 1;
2329 if (_y<0) _y = 0; else if (_y > 1) _y = 1;
2330 VncVisitor vv(_x, _y, 1 << button);
2332 return vv.wasSuccessful();
2335 virtual void buttonReleased(void)
2337 SG_LOG(SG_INPUT, SG_DEBUG, "VNC release");
2338 VncVisitor vv(_x, _y, 0);
2341 virtual void update(double dt)
2346 osg::ref_ptr<osg::Group> _node;
2347 SGVec3d _topLeft, _toRight, _toDown;
2348 double _squaredRight, _squaredDown;
2351 SGPickAnimation::SGPickAnimation(const SGPropertyNode* configNode,
2352 SGPropertyNode* modelRoot) :
2353 SGAnimation(configNode, modelRoot)
2359 Mutex colorModeUniformMutex;
2360 osg::ref_ptr<osg::Uniform> colorModeUniform;
2364 SGPickAnimation::createAnimationGroup(osg::Group& parent)
2366 osg::Group* commonGroup = new osg::Group;
2368 // Contains the normal geometry that is interactive
2369 osg::ref_ptr<osg::Group> normalGroup = new osg::Group;
2370 normalGroup->setName("pick normal group");
2371 normalGroup->addChild(commonGroup);
2373 // Used to render the geometry with just yellow edges
2374 osg::Group* highlightGroup = new osg::Group;
2375 highlightGroup->setName("pick highlight group");
2376 highlightGroup->setNodeMask(SG_NODEMASK_PICK_BIT);
2377 highlightGroup->addChild(commonGroup);
2378 SGSceneUserData* ud;
2379 ud = SGSceneUserData::getOrCreateSceneUserData(commonGroup);
2381 // add actions that become macro and command invocations
2382 std::vector<SGPropertyNode_ptr> actions;
2383 actions = getConfig()->getChildren("action");
2384 for (unsigned int i = 0; i < actions.size(); ++i)
2385 ud->addPickCallback(new PickCallback(actions[i], getModelRoot()));
2386 // Look for the VNC sessions that want raw mouse input
2387 actions = getConfig()->getChildren("vncaction");
2388 for (unsigned int i = 0; i < actions.size(); ++i)
2389 ud->addPickCallback(new VncCallback(actions[i], getModelRoot(),
2392 // prepare a state set that paints the edges of this object yellow
2393 // The material and texture attributes are set with
2394 // OVERRIDE|PROTECTED in case there is a material animation on a
2395 // higher node in the scene graph, which would have its material
2396 // attribute set with OVERRIDE.
2397 osg::StateSet* stateSet = highlightGroup->getOrCreateStateSet();
2398 osg::Texture2D* white = StateAttributeFactory::instance()->getWhiteTexture();
2399 stateSet->setTextureAttributeAndModes(0, white,
2400 (osg::StateAttribute::ON
2401 | osg::StateAttribute::OVERRIDE
2402 | osg::StateAttribute::PROTECTED));
2403 osg::PolygonOffset* polygonOffset = new osg::PolygonOffset;
2404 polygonOffset->setFactor(-1);
2405 polygonOffset->setUnits(-1);
2406 stateSet->setAttribute(polygonOffset, osg::StateAttribute::OVERRIDE);
2407 stateSet->setMode(GL_POLYGON_OFFSET_LINE,
2408 osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
2409 osg::PolygonMode* polygonMode = new osg::PolygonMode;
2410 polygonMode->setMode(osg::PolygonMode::FRONT_AND_BACK,
2411 osg::PolygonMode::LINE);
2412 stateSet->setAttribute(polygonMode, osg::StateAttribute::OVERRIDE);
2413 osg::Material* material = new osg::Material;
2414 material->setColorMode(osg::Material::OFF);
2415 material->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(0, 0, 0, 1));
2416 // XXX Alpha < 1.0 in the diffuse material value is a signal to the
2417 // default shader to take the alpha value from the material value
2418 // and not the glColor. In many cases the pick animation geometry is
2419 // transparent, so the outline would not be visible without this hack.
2420 material->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(0, 0, 0, .95));
2421 material->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(1, 1, 0, 1));
2422 material->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0, 0, 0, 0));
2423 stateSet->setAttribute(
2424 material, osg::StateAttribute::OVERRIDE | osg::StateAttribute::PROTECTED);
2425 // The default shader has a colorMode uniform that mimics the
2426 // behavior of Material color mode.
2427 osg::Uniform* cmUniform = 0;
2429 ScopedLock<Mutex> lock(colorModeUniformMutex);
2430 if (!colorModeUniform.valid()) {
2431 colorModeUniform = new osg::Uniform(osg::Uniform::INT, "colorMode");
2432 colorModeUniform->set(0); // MODE_OFF
2433 colorModeUniform->setDataVariance(osg::Object::STATIC);
2435 cmUniform = colorModeUniform.get();
2437 stateSet->addUniform(cmUniform,
2438 osg::StateAttribute::OVERRIDE | osg::StateAttribute::ON);
2439 // Only add normal geometry if configured
2440 if (getConfig()->getBoolValue("visible", true))
2441 parent.addChild(normalGroup.get());
2442 parent.addChild(highlightGroup);