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 spin or rotation.
78 set_rotation (osg::Matrix &matrix, double position_deg,
79 const SGVec3d ¢er, const SGVec3d &axis)
81 double temp_angle = -SGMiscd::deg2rad(position_deg);
83 double s = sin(temp_angle);
84 double c = cos(temp_angle);
87 // axis was normalized at load time
88 // hint to the compiler to put these into FP registers
93 matrix(0, 0) = t * x * x + c ;
94 matrix(0, 1) = t * y * x - s * z ;
95 matrix(0, 2) = t * z * x + s * y ;
98 matrix(1, 0) = t * x * y + s * z ;
99 matrix(1, 1) = t * y * y + c ;
100 matrix(1, 2) = t * z * y - s * x ;
103 matrix(2, 0) = t * x * z - s * y ;
104 matrix(2, 1) = t * y * z + s * x ;
105 matrix(2, 2) = t * z * z + c ;
108 // hint to the compiler to put these into FP registers
113 matrix(3, 0) = x - x*matrix(0, 0) - y*matrix(1, 0) - z*matrix(2, 0);
114 matrix(3, 1) = y - x*matrix(0, 1) - y*matrix(1, 1) - z*matrix(2, 1);
115 matrix(3, 2) = z - x*matrix(0, 2) - y*matrix(1, 2) - z*matrix(2, 2);
120 * Set up the transform matrix for a translation.
123 set_translation (osg::Matrix &matrix, double position_m, const SGVec3d &axis)
125 SGVec3d xyz = axis * position_m;
126 matrix.makeIdentity();
127 matrix(3, 0) = xyz[0];
128 matrix(3, 1) = xyz[1];
129 matrix(3, 2) = xyz[2];
133 * Read an interpolation table from properties.
135 static SGInterpTable *
136 read_interpolation_table(const SGPropertyNode* props)
138 const SGPropertyNode* table_node = props->getNode("interpolation");
141 return new SGInterpTable(table_node);
145 unit_string(const char* value, const char* unit)
147 return std::string(value) + unit;
150 class SGPersonalityScaleOffsetExpression : public SGUnaryExpression<double> {
152 SGPersonalityScaleOffsetExpression(SGExpression<double>* expr,
153 SGPropertyNode const* config,
154 const std::string& scalename,
155 const std::string& offsetname,
157 double defOffset = 0) :
158 SGUnaryExpression<double>(expr),
159 _scale(config, scalename.c_str(), defScale),
160 _offset(config, offsetname.c_str(), defOffset)
162 void setScale(double scale)
164 void setOffset(double offset)
165 { _offset = offset; }
167 virtual void eval(double& value, const simgear::expression::Binding* b) const
171 value = _offset + _scale*getOperand()->getValue(b);
174 virtual bool isConst() const { return false; }
177 mutable SGPersonalityParameter<double> _scale;
178 mutable SGPersonalityParameter<double> _offset;
182 static SGExpressiond*
183 read_factor_offset(const SGPropertyNode* configNode, SGExpressiond* expr,
184 const std::string& factor, const std::string& offset)
186 double factorValue = configNode->getDoubleValue(factor, 1);
187 if (factorValue != 1)
188 expr = new SGScaleExpression<double>(expr, factorValue);
189 double offsetValue = configNode->getDoubleValue(offset, 0);
190 if (offsetValue != 0)
191 expr = new SGBiasExpression<double>(expr, offsetValue);
195 static SGExpressiond*
196 read_offset_factor(const SGPropertyNode* configNode, SGExpressiond* expr,
197 const std::string& factor, const std::string& offset)
199 double offsetValue = configNode->getDoubleValue(offset, 0);
200 if (offsetValue != 0)
201 expr = new SGBiasExpression<double>(expr, offsetValue);
202 double factorValue = configNode->getDoubleValue(factor, 1);
203 if (factorValue != 1)
204 expr = new SGScaleExpression<double>(expr, factorValue);
209 read_value(const SGPropertyNode* configNode, SGPropertyNode* modelRoot,
210 const char* unit, double defMin, double defMax)
212 const SGPropertyNode * expression = configNode->getNode( "expression" );
213 if( expression != NULL )
214 return SGReadDoubleExpression( modelRoot, expression->getChild(0) );
216 SGExpression<double>* value = 0;
218 std::string inputPropertyName = configNode->getStringValue("property", "");
219 if (inputPropertyName.empty()) {
220 std::string spos = unit_string("starting-position", unit);
221 double initPos = configNode->getDoubleValue(spos, 0);
222 value = new SGConstExpression<double>(initPos);
224 SGPropertyNode* inputProperty;
225 inputProperty = modelRoot->getNode(inputPropertyName, true);
226 value = new SGPropertyExpression<double>(inputProperty);
229 SGInterpTable* interpTable = read_interpolation_table(configNode);
231 return new SGInterpTableExpression<double>(value, interpTable);
233 std::string offset = unit_string("offset", unit);
234 std::string min = unit_string("min", unit);
235 std::string max = unit_string("max", unit);
237 if (configNode->getBoolValue("use-personality", false)) {
238 value = new SGPersonalityScaleOffsetExpression(value, configNode,
241 value = read_factor_offset(configNode, value, "factor", offset);
244 double minClip = configNode->getDoubleValue(min, defMin);
245 double maxClip = configNode->getDoubleValue(max, defMax);
246 if (minClip > SGMiscd::min(SGLimitsd::min(), -SGLimitsd::max()) ||
247 maxClip < SGLimitsd::max())
248 value = new SGClipExpression<double>(value, minClip, maxClip);
256 ////////////////////////////////////////////////////////////////////////
257 // Animation installer
258 ////////////////////////////////////////////////////////////////////////
260 class SGAnimation::RemoveModeVisitor : public SGStateAttributeVisitor {
262 RemoveModeVisitor(osg::StateAttribute::GLMode mode) :
265 virtual void apply(osg::StateSet* stateSet)
269 stateSet->removeMode(_mode);
272 osg::StateAttribute::GLMode _mode;
275 class SGAnimation::RemoveAttributeVisitor : public SGStateAttributeVisitor {
277 RemoveAttributeVisitor(osg::StateAttribute::Type type) :
280 virtual void apply(osg::StateSet* stateSet)
284 while (stateSet->getAttribute(_type)) {
285 stateSet->removeAttribute(_type);
289 osg::StateAttribute::Type _type;
292 class SGAnimation::RemoveTextureModeVisitor : public SGStateAttributeVisitor {
294 RemoveTextureModeVisitor(unsigned unit, osg::StateAttribute::GLMode mode) :
298 virtual void apply(osg::StateSet* stateSet)
302 stateSet->removeTextureMode(_unit, _mode);
306 osg::StateAttribute::GLMode _mode;
309 class SGAnimation::RemoveTextureAttributeVisitor :
310 public SGStateAttributeVisitor {
312 RemoveTextureAttributeVisitor(unsigned unit,
313 osg::StateAttribute::Type type) :
317 virtual void apply(osg::StateSet* stateSet)
321 while (stateSet->getTextureAttribute(_unit, _type)) {
322 stateSet->removeTextureAttribute(_unit, _type);
327 osg::StateAttribute::Type _type;
330 class SGAnimation::BinToInheritVisitor : public SGStateAttributeVisitor {
332 virtual void apply(osg::StateSet* stateSet)
336 stateSet->setRenderBinToInherit();
340 class SGAnimation::DrawableCloneVisitor : public osg::NodeVisitor {
342 DrawableCloneVisitor() :
343 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
345 void apply(osg::Geode& geode)
347 for (unsigned i = 0 ; i < geode.getNumDrawables(); ++i) {
348 osg::CopyOp copyOp(osg::CopyOp::DEEP_COPY_ALL &
349 ~osg::CopyOp::DEEP_COPY_TEXTURES);
350 geode.setDrawable(i, copyOp(geode.getDrawable(i)));
357 // Set all drawables to not use display lists. OSG will use
358 // glDrawArrays instead.
359 struct DoDrawArraysVisitor : public osg::NodeVisitor {
360 DoDrawArraysVisitor() :
361 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
363 void apply(osg::Geode& geode)
368 for (int i = 0; i < (int)geode.getNumDrawables(); ++i)
369 geode.getDrawable(i)->setUseDisplayList(false);
374 SGAnimation::SGAnimation(const SGPropertyNode* configNode,
375 SGPropertyNode* modelRoot) :
376 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
378 _configNode(configNode),
379 _modelRoot(modelRoot)
381 _name = configNode->getStringValue("name", "");
382 _enableHOT = configNode->getBoolValue("enable-hot", true);
383 std::vector<SGPropertyNode_ptr> objectNames =
384 configNode->getChildren("object-name");
385 for (unsigned i = 0; i < objectNames.size(); ++i)
386 _objectNames.push_back(objectNames[i]->getStringValue());
389 SGAnimation::~SGAnimation()
393 std::list<std::string>::const_iterator i;
395 for (i = _objectNames.begin(); i != _objectNames.end(); ++i)
405 SG_LOG(SG_IO, SG_ALERT, "Could not find at least one of the following"
406 " objects for animation: " << info);
412 SGAnimation::animate(osg::Node* node, const SGPropertyNode* configNode,
413 SGPropertyNode* modelRoot,
414 const osgDB::Options* options,
415 const string &path, int i)
417 std::string type = configNode->getStringValue("type", "none");
418 if (type == "alpha-test") {
419 SGAlphaTestAnimation animInst(configNode, modelRoot);
420 animInst.apply(node);
421 } else if (type == "billboard") {
422 SGBillboardAnimation animInst(configNode, modelRoot);
423 animInst.apply(node);
424 } else if (type == "blend") {
425 SGBlendAnimation animInst(configNode, modelRoot);
426 animInst.apply(node);
427 } else if (type == "dist-scale") {
428 SGDistScaleAnimation animInst(configNode, modelRoot);
429 animInst.apply(node);
430 } else if (type == "flash") {
431 SGFlashAnimation animInst(configNode, modelRoot);
432 animInst.apply(node);
433 } else if (type == "interaction") {
434 SGInteractionAnimation animInst(configNode, modelRoot);
435 animInst.apply(node);
436 } else if (type == "material") {
437 SGMaterialAnimation animInst(configNode, modelRoot, options, path);
438 animInst.apply(node);
439 } else if (type == "noshadow") {
440 SGShadowAnimation animInst(configNode, modelRoot);
441 animInst.apply(node);
442 } else if (type == "pick") {
443 SGPickAnimation animInst(configNode, modelRoot);
444 animInst.apply(node);
445 } else if (type == "range") {
446 SGRangeAnimation animInst(configNode, modelRoot);
447 animInst.apply(node);
448 } else if (type == "rotate" || type == "spin") {
449 SGRotateAnimation animInst(configNode, modelRoot);
450 animInst.apply(node);
451 } else if (type == "scale") {
452 SGScaleAnimation animInst(configNode, modelRoot);
453 animInst.apply(node);
454 } else if (type == "select") {
455 SGSelectAnimation animInst(configNode, modelRoot);
456 animInst.apply(node);
457 } else if (type == "shader") {
458 SGShaderAnimation animInst(configNode, modelRoot, options);
459 animInst.apply(node);
460 } else if (type == "textranslate" || type == "texrotate" ||
461 type == "texmultiple") {
462 SGTexTransformAnimation animInst(configNode, modelRoot);
463 animInst.apply(node);
464 } else if (type == "timed") {
465 SGTimedAnimation animInst(configNode, modelRoot);
466 animInst.apply(node);
467 } else if (type == "translate") {
468 SGTranslateAnimation animInst(configNode, modelRoot);
469 animInst.apply(node);
470 } else if (type == "light") {
471 SGLightAnimation animInst(configNode, modelRoot, path, i);
472 animInst.apply(node);
473 } else if (type == "null" || type == "none" || type.empty()) {
474 SGGroupAnimation animInst(configNode, modelRoot);
475 animInst.apply(node);
484 SGAnimation::apply(osg::Node* node)
486 // duh what a special case ...
487 if (_objectNames.empty()) {
488 osg::Group* group = node->asGroup();
490 osg::ref_ptr<osg::Group> animationGroup;
491 installInGroup(std::string(), *group, animationGroup);
498 SGAnimation::install(osg::Node& node)
502 node.setNodeMask( SG_NODEMASK_TERRAIN_BIT | node.getNodeMask());
504 node.setNodeMask(~SG_NODEMASK_TERRAIN_BIT & node.getNodeMask());
508 SGAnimation::createAnimationGroup(osg::Group& parent)
510 // default implementation, we do not need a new group
511 // for every animation type. Usually animations that just change
512 // the StateSet of some parts of the model
517 SGAnimation::apply(osg::Group& group)
519 // the trick is to first traverse the children and then
520 // possibly splice in a new group node if required.
521 // Else we end up in a recursive loop where we infinitly insert new
525 // Note that this algorithm preserves the order of the child objects
526 // like they appear in the object-name tags.
527 // The timed animations require this
528 osg::ref_ptr<osg::Group> animationGroup;
529 std::list<std::string>::const_iterator nameIt;
530 for (nameIt = _objectNames.begin(); nameIt != _objectNames.end(); ++nameIt)
531 installInGroup(*nameIt, group, animationGroup);
535 SGAnimation::installInGroup(const std::string& name, osg::Group& group,
536 osg::ref_ptr<osg::Group>& animationGroup)
538 int i = group.getNumChildren() - 1;
539 for (; 0 <= i; --i) {
540 osg::Node* child = group.getChild(i);
542 // Check if this one is already processed
543 if (std::find(_installedAnimations.begin(),
544 _installedAnimations.end(), child)
545 != _installedAnimations.end())
548 if (name.empty() || child->getName() == name) {
549 // fire the installation of the animation
552 // create a group node on demand
553 if (!animationGroup.valid()) {
554 animationGroup = createAnimationGroup(group);
555 // Animation type that does not require a new group,
556 // in this case we can stop and look for the next object
557 if (animationGroup.valid() && !_name.empty())
558 animationGroup->setName(_name);
560 if (animationGroup.valid()) {
561 animationGroup->addChild(child);
562 group.removeChild(i);
565 // store that we already have processed this child node
566 // We can hit this one twice if an animation references some
567 // part of a subtree twice
568 _installedAnimations.push_back(child);
574 SGAnimation::removeMode(osg::Node& node, osg::StateAttribute::GLMode mode)
576 RemoveModeVisitor visitor(mode);
577 node.accept(visitor);
581 SGAnimation::removeAttribute(osg::Node& node, osg::StateAttribute::Type type)
583 RemoveAttributeVisitor visitor(type);
584 node.accept(visitor);
588 SGAnimation::removeTextureMode(osg::Node& node, unsigned unit,
589 osg::StateAttribute::GLMode mode)
591 RemoveTextureModeVisitor visitor(unit, mode);
592 node.accept(visitor);
596 SGAnimation::removeTextureAttribute(osg::Node& node, unsigned unit,
597 osg::StateAttribute::Type type)
599 RemoveTextureAttributeVisitor visitor(unit, type);
600 node.accept(visitor);
604 SGAnimation::setRenderBinToInherit(osg::Node& node)
606 BinToInheritVisitor visitor;
607 node.accept(visitor);
611 SGAnimation::cloneDrawables(osg::Node& node)
613 DrawableCloneVisitor visitor;
614 node.accept(visitor);
618 SGAnimation::getCondition() const
620 const SGPropertyNode* conditionNode = _configNode->getChild("condition");
623 return sgReadCondition(_modelRoot, conditionNode);
628 ////////////////////////////////////////////////////////////////////////
629 // Implementation of null animation
630 ////////////////////////////////////////////////////////////////////////
632 // Ok, that is to build a subgraph from different other
633 // graph nodes. I guess that this stems from the time where modellers
634 // could not build hierarchical trees ...
635 SGGroupAnimation::SGGroupAnimation(const SGPropertyNode* configNode,
636 SGPropertyNode* modelRoot):
637 SGAnimation(configNode, modelRoot)
642 SGGroupAnimation::createAnimationGroup(osg::Group& parent)
644 osg::Group* group = new osg::Group;
645 parent.addChild(group);
650 ////////////////////////////////////////////////////////////////////////
651 // Implementation of translate animation
652 ////////////////////////////////////////////////////////////////////////
654 class SGTranslateAnimation::UpdateCallback : public osg::NodeCallback {
656 UpdateCallback(SGCondition const* condition,
657 SGExpressiond const* animationValue) :
658 _condition(condition),
659 _animationValue(animationValue)
661 setName("SGTranslateAnimation::UpdateCallback");
663 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
665 if (!_condition || _condition->test()) {
666 SGTranslateTransform* transform;
667 transform = static_cast<SGTranslateTransform*>(node);
668 transform->setValue(_animationValue->getValue());
673 SGSharedPtr<SGCondition const> _condition;
674 SGSharedPtr<SGExpressiond const> _animationValue;
677 SGTranslateAnimation::SGTranslateAnimation(const SGPropertyNode* configNode,
678 SGPropertyNode* modelRoot) :
679 SGAnimation(configNode, modelRoot)
681 _condition = getCondition();
682 SGSharedPtr<SGExpressiond> value;
683 value = read_value(configNode, modelRoot, "-m",
684 -SGLimitsd::max(), SGLimitsd::max());
685 _animationValue = value->simplify();
687 _initialValue = _animationValue->getValue();
691 if (configNode->hasValue("axis/x1-m")) {
693 v1[0] = configNode->getDoubleValue("axis/x1-m", 0);
694 v1[1] = configNode->getDoubleValue("axis/y1-m", 0);
695 v1[2] = configNode->getDoubleValue("axis/z1-m", 0);
696 v2[0] = configNode->getDoubleValue("axis/x2-m", 0);
697 v2[1] = configNode->getDoubleValue("axis/y2-m", 0);
698 v2[2] = configNode->getDoubleValue("axis/z2-m", 0);
701 _axis[0] = configNode->getDoubleValue("axis/x", 0);
702 _axis[1] = configNode->getDoubleValue("axis/y", 0);
703 _axis[2] = configNode->getDoubleValue("axis/z", 0);
705 if (8*SGLimitsd::min() < norm(_axis))
706 _axis = normalize(_axis);
710 SGTranslateAnimation::createAnimationGroup(osg::Group& parent)
712 SGTranslateTransform* transform = new SGTranslateTransform;
713 transform->setName("translate animation");
714 if (_animationValue && !_animationValue->isConst()) {
715 UpdateCallback* uc = new UpdateCallback(_condition, _animationValue);
716 transform->setUpdateCallback(uc);
718 transform->setAxis(_axis);
719 transform->setValue(_initialValue);
720 parent.addChild(transform);
725 ////////////////////////////////////////////////////////////////////////
726 // Implementation of rotate/spin animation
727 ////////////////////////////////////////////////////////////////////////
730 class RotAnimCallback : public osg::NodeCallback
733 RotAnimCallback(SGCondition const* condition,
734 SGExpressiond const* animationValue,
735 double initialValue = 0.0) :
736 _condition(condition),
737 _animationValue(animationValue),
738 _initialValue(initialValue),
741 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv);
743 SGSharedPtr<SGCondition const> _condition;
744 SGSharedPtr<SGExpressiond const> _animationValue;
745 double _initialValue;
749 void RotAnimCallback::operator()(osg::Node* node, osg::NodeVisitor* nv)
752 SGRotateTransform* transform = static_cast<SGRotateTransform*>(node);
753 EffectCullVisitor* cv = dynamic_cast<EffectCullVisitor*>(nv);
758 if (!_condition || _condition->test()) {
759 angle = _animationValue->getValue() - _initialValue;
764 const SGVec3d& sgcenter = transform->getCenter();
765 const SGVec3d& sgaxis = transform->getAxis();
766 Matrixd mat = Matrixd::translate(-sgcenter[0], -sgcenter[1], -sgcenter[2])
767 * Matrixd::rotate(SGMiscd::deg2rad(angle), sgaxis[0], sgaxis[1], sgaxis[2])
768 * Matrixd::translate(sgcenter[0], sgcenter[1], sgcenter[2])
769 * *cv->getModelViewMatrix();
770 ref_ptr<RefMatrix> refmat = new RefMatrix(mat);
771 cv->pushModelViewMatrix(refmat.get(), transform->getReferenceFrame());
772 traverse(transform, nv);
773 cv->popModelViewMatrix();
777 class SpinAnimCallback : public osg::NodeCallback {
779 SpinAnimCallback(SGCondition const* condition,
780 SGExpressiond const* animationValue,
781 double initialValue = 0.0) :
782 _condition(condition),
783 _animationValue(animationValue),
784 _initialValue(initialValue)
786 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv);
788 SGSharedPtr<SGCondition const> _condition;
789 SGSharedPtr<SGExpressiond const> _animationValue;
790 double _initialValue;
792 // This cull callback can run in different threads if there is
793 // more than one camera. It is probably safe to overwrite the
794 // reference values in multiple threads, but we'll provide a
795 // threadsafe way to manage those values just to be safe.
796 struct ReferenceValues {
797 ReferenceValues(double t, double rot, double vel)
798 : _time(t), _rotation(rot), _rotVelocity(vel)
805 OpenThreads::AtomicPtr _referenceValues;
808 void SpinAnimCallback::operator()(osg::Node* node, osg::NodeVisitor* nv)
811 SGRotateTransform* transform = static_cast<SGRotateTransform*>(node);
812 EffectCullVisitor* cv = dynamic_cast<EffectCullVisitor*>(nv);
815 if (!_condition || _condition->test()) {
816 double t = nv->getFrameStamp()->getReferenceTime();
817 double rps = _animationValue->getValue() / 60.0;
818 ReferenceValues* refval = static_cast<ReferenceValues*>(_referenceValues.get());
819 if (!refval || refval->_rotVelocity != rps) {
820 ReferenceValues* newref = 0;
823 newref = new ReferenceValues(t, 0.0, rps);
825 double newRot = refval->_rotation + (t - refval->_time) * refval->_rotVelocity;
826 newref = new ReferenceValues(t, newRot, rps);
828 if (_referenceValues.assign(newref, refval)) {
833 double rotation = refval->_rotation + (t - refval->_time) * rps;
835 double rot = modf(rotation, &intPart);
836 double angle = rot * 2.0 * osg::PI;
837 const SGVec3d& sgcenter = transform->getCenter();
838 const SGVec3d& sgaxis = transform->getAxis();
839 Matrixd mat = Matrixd::translate(-sgcenter[0], -sgcenter[1], -sgcenter[2])
840 * Matrixd::rotate(angle, sgaxis[0], sgaxis[1], sgaxis[2])
841 * Matrixd::translate(sgcenter[0], sgcenter[1], sgcenter[2])
842 * *cv->getModelViewMatrix();
843 ref_ptr<RefMatrix> refmat = new RefMatrix(mat);
844 cv->pushModelViewMatrix(refmat.get(), transform->getReferenceFrame());
845 traverse(transform, nv);
846 cv->popModelViewMatrix();
848 traverse(transform, nv);
852 SGRotateAnimation::SGRotateAnimation(const SGPropertyNode* configNode,
853 SGPropertyNode* modelRoot) :
854 SGAnimation(configNode, modelRoot)
856 std::string type = configNode->getStringValue("type", "");
857 _isSpin = (type == "spin");
859 _condition = getCondition();
860 SGSharedPtr<SGExpressiond> value;
861 value = read_value(configNode, modelRoot, "-deg",
862 -SGLimitsd::max(), SGLimitsd::max());
863 _animationValue = value->simplify();
865 _initialValue = _animationValue->getValue();
868 _center = SGVec3d::zeros();
869 if (configNode->hasValue("axis/x1-m")) {
871 v1[0] = configNode->getDoubleValue("axis/x1-m", 0);
872 v1[1] = configNode->getDoubleValue("axis/y1-m", 0);
873 v1[2] = configNode->getDoubleValue("axis/z1-m", 0);
874 v2[0] = configNode->getDoubleValue("axis/x2-m", 0);
875 v2[1] = configNode->getDoubleValue("axis/y2-m", 0);
876 v2[2] = configNode->getDoubleValue("axis/z2-m", 0);
877 _center = 0.5*(v1+v2);
880 _axis[0] = configNode->getDoubleValue("axis/x", 0);
881 _axis[1] = configNode->getDoubleValue("axis/y", 0);
882 _axis[2] = configNode->getDoubleValue("axis/z", 0);
884 if (8*SGLimitsd::min() < norm(_axis))
885 _axis = normalize(_axis);
887 _center[0] = configNode->getDoubleValue("center/x-m", _center[0]);
888 _center[1] = configNode->getDoubleValue("center/y-m", _center[1]);
889 _center[2] = configNode->getDoubleValue("center/z-m", _center[2]);
893 SGRotateAnimation::createAnimationGroup(osg::Group& parent)
895 SGRotateTransform* transform = new SGRotateTransform;
896 transform->setName("rotate animation");
898 SpinAnimCallback* cc;
899 cc = new SpinAnimCallback(_condition, _animationValue, _initialValue);
900 transform->setCullCallback(cc);
901 } else if (_animationValue || !_animationValue->isConst()) {
902 RotAnimCallback* cc = new RotAnimCallback(_condition, _animationValue, _initialValue);
903 transform->setCullCallback(cc);
905 transform->setCenter(_center);
906 transform->setAxis(_axis);
907 transform->setAngleDeg(_initialValue);
908 parent.addChild(transform);
913 ////////////////////////////////////////////////////////////////////////
914 // Implementation of scale animation
915 ////////////////////////////////////////////////////////////////////////
917 class SGScaleAnimation::UpdateCallback : public osg::NodeCallback {
919 UpdateCallback(const SGCondition* condition,
920 SGSharedPtr<const SGExpressiond> animationValue[3]) :
921 _condition(condition)
923 _animationValue[0] = animationValue[0];
924 _animationValue[1] = animationValue[1];
925 _animationValue[2] = animationValue[2];
926 setName("SGScaleAnimation::UpdateCallback");
928 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
930 if (!_condition || _condition->test()) {
931 SGScaleTransform* transform;
932 transform = static_cast<SGScaleTransform*>(node);
933 SGVec3d scale(_animationValue[0]->getValue(),
934 _animationValue[1]->getValue(),
935 _animationValue[2]->getValue());
936 transform->setScaleFactor(scale);
941 SGSharedPtr<SGCondition const> _condition;
942 SGSharedPtr<SGExpressiond const> _animationValue[3];
945 SGScaleAnimation::SGScaleAnimation(const SGPropertyNode* configNode,
946 SGPropertyNode* modelRoot) :
947 SGAnimation(configNode, modelRoot)
949 _condition = getCondition();
951 // default offset/factor for all directions
952 double offset = configNode->getDoubleValue("offset", 0);
953 double factor = configNode->getDoubleValue("factor", 1);
955 SGSharedPtr<SGExpressiond> inPropExpr;
957 std::string inputPropertyName;
958 inputPropertyName = configNode->getStringValue("property", "");
959 if (inputPropertyName.empty()) {
960 inPropExpr = new SGConstExpression<double>(0);
962 SGPropertyNode* inputProperty;
963 inputProperty = modelRoot->getNode(inputPropertyName, true);
964 inPropExpr = new SGPropertyExpression<double>(inputProperty);
967 SGInterpTable* interpTable = read_interpolation_table(configNode);
969 SGSharedPtr<SGExpressiond> value;
970 value = new SGInterpTableExpression<double>(inPropExpr, interpTable);
971 _animationValue[0] = value->simplify();
972 _animationValue[1] = value->simplify();
973 _animationValue[2] = value->simplify();
974 } else if (configNode->getBoolValue("use-personality", false)) {
975 SGSharedPtr<SGExpressiond> value;
976 value = new SGPersonalityScaleOffsetExpression(inPropExpr, configNode,
977 "x-factor", "x-offset",
979 double minClip = configNode->getDoubleValue("x-min", 0);
980 double maxClip = configNode->getDoubleValue("x-max", SGLimitsd::max());
981 value = new SGClipExpression<double>(value, minClip, maxClip);
982 _animationValue[0] = value->simplify();
984 value = new SGPersonalityScaleOffsetExpression(inPropExpr, configNode,
985 "y-factor", "y-offset",
987 minClip = configNode->getDoubleValue("y-min", 0);
988 maxClip = configNode->getDoubleValue("y-max", SGLimitsd::max());
989 value = new SGClipExpression<double>(value, minClip, maxClip);
990 _animationValue[1] = value->simplify();
992 value = new SGPersonalityScaleOffsetExpression(inPropExpr, configNode,
993 "z-factor", "z-offset",
995 minClip = configNode->getDoubleValue("z-min", 0);
996 maxClip = configNode->getDoubleValue("z-max", SGLimitsd::max());
997 value = new SGClipExpression<double>(value, minClip, maxClip);
998 _animationValue[2] = value->simplify();
1000 SGSharedPtr<SGExpressiond> value;
1001 value = read_factor_offset(configNode, inPropExpr, "x-factor", "x-offset");
1002 double minClip = configNode->getDoubleValue("x-min", 0);
1003 double maxClip = configNode->getDoubleValue("x-max", SGLimitsd::max());
1004 value = new SGClipExpression<double>(value, minClip, maxClip);
1005 _animationValue[0] = value->simplify();
1007 value = read_factor_offset(configNode, inPropExpr, "y-factor", "y-offset");
1008 minClip = configNode->getDoubleValue("y-min", 0);
1009 maxClip = configNode->getDoubleValue("y-max", SGLimitsd::max());
1010 value = new SGClipExpression<double>(value, minClip, maxClip);
1011 _animationValue[1] = value->simplify();
1013 value = read_factor_offset(configNode, inPropExpr, "z-factor", "z-offset");
1014 minClip = configNode->getDoubleValue("z-min", 0);
1015 maxClip = configNode->getDoubleValue("z-max", SGLimitsd::max());
1016 value = new SGClipExpression<double>(value, minClip, maxClip);
1017 _animationValue[2] = value->simplify();
1019 _initialValue[0] = configNode->getDoubleValue("x-starting-scale", 1);
1020 _initialValue[0] *= configNode->getDoubleValue("x-factor", factor);
1021 _initialValue[0] += configNode->getDoubleValue("x-offset", offset);
1022 _initialValue[1] = configNode->getDoubleValue("y-starting-scale", 1);
1023 _initialValue[1] *= configNode->getDoubleValue("y-factor", factor);
1024 _initialValue[1] += configNode->getDoubleValue("y-offset", offset);
1025 _initialValue[2] = configNode->getDoubleValue("z-starting-scale", 1);
1026 _initialValue[2] *= configNode->getDoubleValue("z-factor", factor);
1027 _initialValue[2] += configNode->getDoubleValue("z-offset", offset);
1028 _center[0] = configNode->getDoubleValue("center/x-m", 0);
1029 _center[1] = configNode->getDoubleValue("center/y-m", 0);
1030 _center[2] = configNode->getDoubleValue("center/z-m", 0);
1034 SGScaleAnimation::createAnimationGroup(osg::Group& parent)
1036 SGScaleTransform* transform = new SGScaleTransform;
1037 transform->setName("scale animation");
1038 transform->setCenter(_center);
1039 transform->setScaleFactor(_initialValue);
1040 UpdateCallback* uc = new UpdateCallback(_condition, _animationValue);
1041 transform->setUpdateCallback(uc);
1042 parent.addChild(transform);
1047 // Don't create a new state state everytime we need GL_NORMALIZE!
1051 Mutex normalizeMutex;
1053 osg::StateSet* getNormalizeStateSet()
1055 static osg::ref_ptr<osg::StateSet> normalizeStateSet;
1056 ScopedLock<Mutex> lock(normalizeMutex);
1057 if (!normalizeStateSet.valid()) {
1058 normalizeStateSet = new osg::StateSet;
1059 normalizeStateSet->setMode(GL_NORMALIZE, osg::StateAttribute::ON);
1060 normalizeStateSet->setDataVariance(osg::Object::STATIC);
1062 return normalizeStateSet.get();
1066 ////////////////////////////////////////////////////////////////////////
1067 // Implementation of dist scale animation
1068 ////////////////////////////////////////////////////////////////////////
1070 class SGDistScaleAnimation::Transform : public osg::Transform {
1072 Transform() : _min_v(0.0), _max_v(0.0), _factor(0.0), _offset(0.0) {}
1073 Transform(const Transform& rhs,
1074 const osg::CopyOp& copyOp = osg::CopyOp::SHALLOW_COPY)
1075 : osg::Transform(rhs, copyOp), _table(rhs._table), _center(rhs._center),
1076 _min_v(rhs._min_v), _max_v(rhs._max_v), _factor(rhs._factor),
1077 _offset(rhs._offset)
1080 META_Node(simgear, SGDistScaleAnimation::Transform);
1081 Transform(const SGPropertyNode* configNode)
1083 setName(configNode->getStringValue("name", "dist scale animation"));
1084 setReferenceFrame(RELATIVE_RF);
1085 setStateSet(getNormalizeStateSet());
1086 _factor = configNode->getFloatValue("factor", 1);
1087 _offset = configNode->getFloatValue("offset", 0);
1088 _min_v = configNode->getFloatValue("min", SGLimitsf::epsilon());
1089 _max_v = configNode->getFloatValue("max", SGLimitsf::max());
1090 _table = read_interpolation_table(configNode);
1091 _center[0] = configNode->getFloatValue("center/x-m", 0);
1092 _center[1] = configNode->getFloatValue("center/y-m", 0);
1093 _center[2] = configNode->getFloatValue("center/z-m", 0);
1095 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
1096 osg::NodeVisitor* nv) const
1098 osg::Matrix transform;
1099 double scale_factor = computeScaleFactor(nv);
1100 transform(0,0) = scale_factor;
1101 transform(1,1) = scale_factor;
1102 transform(2,2) = scale_factor;
1103 transform(3,0) = _center[0]*(1 - scale_factor);
1104 transform(3,1) = _center[1]*(1 - scale_factor);
1105 transform(3,2) = _center[2]*(1 - scale_factor);
1106 matrix.preMult(transform);
1110 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
1111 osg::NodeVisitor* nv) const
1113 double scale_factor = computeScaleFactor(nv);
1114 if (fabs(scale_factor) <= SGLimits<double>::min())
1116 osg::Matrix transform;
1117 double rScaleFactor = 1/scale_factor;
1118 transform(0,0) = rScaleFactor;
1119 transform(1,1) = rScaleFactor;
1120 transform(2,2) = rScaleFactor;
1121 transform(3,0) = _center[0]*(1 - rScaleFactor);
1122 transform(3,1) = _center[1]*(1 - rScaleFactor);
1123 transform(3,2) = _center[2]*(1 - rScaleFactor);
1124 matrix.postMult(transform);
1128 static bool writeLocalData(const osg::Object& obj, osgDB::Output& fw)
1130 const Transform& trans = static_cast<const Transform&>(obj);
1131 fw.indent() << "center " << trans._center << "\n";
1132 fw.indent() << "min_v " << trans._min_v << "\n";
1133 fw.indent() << "max_v " << trans._max_v << "\n";
1134 fw.indent() << "factor " << trans._factor << "\n";
1135 fw.indent() << "offset " << trans._offset << "\n";
1139 double computeScaleFactor(osg::NodeVisitor* nv) const
1144 double scale_factor = (toOsg(_center) - nv->getEyePoint()).length();
1146 scale_factor = _factor * scale_factor + _offset;
1148 scale_factor = _table->interpolate( scale_factor );
1150 if (scale_factor < _min_v)
1151 scale_factor = _min_v;
1152 if (scale_factor > _max_v)
1153 scale_factor = _max_v;
1155 return scale_factor;
1158 SGSharedPtr<SGInterpTable> _table;
1167 SGDistScaleAnimation::SGDistScaleAnimation(const SGPropertyNode* configNode,
1168 SGPropertyNode* modelRoot) :
1169 SGAnimation(configNode, modelRoot)
1174 SGDistScaleAnimation::createAnimationGroup(osg::Group& parent)
1176 Transform* transform = new Transform(getConfig());
1177 parent.addChild(transform);
1183 osgDB::RegisterDotOsgWrapperProxy distScaleAnimationTransformProxy
1185 new SGDistScaleAnimation::Transform,
1186 "SGDistScaleAnimation::Transform",
1187 "Object Node Transform SGDistScaleAnimation::Transform Group",
1189 &SGDistScaleAnimation::Transform::writeLocalData
1193 ////////////////////////////////////////////////////////////////////////
1194 // Implementation of flash animation
1195 ////////////////////////////////////////////////////////////////////////
1197 class SGFlashAnimation::Transform : public osg::Transform {
1199 Transform() : _power(0.0), _factor(0.0), _offset(0.0), _min_v(0.0),
1200 _max_v(0.0), _two_sides(false)
1203 Transform(const Transform& rhs,
1204 const osg::CopyOp& copyOp = osg::CopyOp::SHALLOW_COPY)
1205 : osg::Transform(rhs, copyOp), _center(rhs._center), _axis(rhs._axis),
1206 _power(rhs._power), _factor(rhs._factor), _offset(rhs._offset),
1207 _min_v(rhs._min_v), _max_v(rhs._max_v), _two_sides(rhs._two_sides)
1210 META_Node(simgear, SGFlashAnimation::Transform);
1212 Transform(const SGPropertyNode* configNode)
1214 setReferenceFrame(RELATIVE_RF);
1215 setName(configNode->getStringValue("name", "flash animation"));
1216 setStateSet(getNormalizeStateSet());
1218 _axis[0] = configNode->getFloatValue("axis/x", 0);
1219 _axis[1] = configNode->getFloatValue("axis/y", 0);
1220 _axis[2] = configNode->getFloatValue("axis/z", 1);
1223 _center[0] = configNode->getFloatValue("center/x-m", 0);
1224 _center[1] = configNode->getFloatValue("center/y-m", 0);
1225 _center[2] = configNode->getFloatValue("center/z-m", 0);
1227 _offset = configNode->getFloatValue("offset", 0);
1228 _factor = configNode->getFloatValue("factor", 1);
1229 _power = configNode->getFloatValue("power", 1);
1230 _two_sides = configNode->getBoolValue("two-sides", false);
1232 _min_v = configNode->getFloatValue("min", SGLimitsf::epsilon());
1233 _max_v = configNode->getFloatValue("max", 1);
1235 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
1236 osg::NodeVisitor* nv) const
1238 osg::Matrix transform;
1239 double scale_factor = computeScaleFactor(nv);
1240 transform(0,0) = scale_factor;
1241 transform(1,1) = scale_factor;
1242 transform(2,2) = scale_factor;
1243 transform(3,0) = _center[0]*(1 - scale_factor);
1244 transform(3,1) = _center[1]*(1 - scale_factor);
1245 transform(3,2) = _center[2]*(1 - scale_factor);
1246 matrix.preMult(transform);
1250 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
1251 osg::NodeVisitor* nv) const
1253 double scale_factor = computeScaleFactor(nv);
1254 if (fabs(scale_factor) <= SGLimits<double>::min())
1256 osg::Matrix transform;
1257 double rScaleFactor = 1/scale_factor;
1258 transform(0,0) = rScaleFactor;
1259 transform(1,1) = rScaleFactor;
1260 transform(2,2) = rScaleFactor;
1261 transform(3,0) = _center[0]*(1 - rScaleFactor);
1262 transform(3,1) = _center[1]*(1 - rScaleFactor);
1263 transform(3,2) = _center[2]*(1 - rScaleFactor);
1264 matrix.postMult(transform);
1268 static bool writeLocalData(const osg::Object& obj, osgDB::Output& fw)
1270 const Transform& trans = static_cast<const Transform&>(obj);
1271 fw.indent() << "center " << trans._center[0] << " "
1272 << trans._center[1] << " " << trans._center[2] << " " << "\n";
1273 fw.indent() << "axis " << trans._axis[0] << " "
1274 << trans._axis[1] << " " << trans._axis[2] << " " << "\n";
1275 fw.indent() << "power " << trans._power << " \n";
1276 fw.indent() << "min_v " << trans._min_v << "\n";
1277 fw.indent() << "max_v " << trans._max_v << "\n";
1278 fw.indent() << "factor " << trans._factor << "\n";
1279 fw.indent() << "offset " << trans._offset << "\n";
1280 fw.indent() << "twosides " << (trans._two_sides ? "true" : "false") << "\n";
1284 double computeScaleFactor(osg::NodeVisitor* nv) const
1289 osg::Vec3 localEyeToCenter = nv->getEyePoint() - _center;
1290 localEyeToCenter.normalize();
1292 double cos_angle = localEyeToCenter*_axis;
1293 double scale_factor = 0;
1294 if ( _two_sides && cos_angle < 0 )
1295 scale_factor = _factor * pow( -cos_angle, _power ) + _offset;
1296 else if ( cos_angle > 0 )
1297 scale_factor = _factor * pow( cos_angle, _power ) + _offset;
1299 if ( scale_factor < _min_v )
1300 scale_factor = _min_v;
1301 if ( scale_factor > _max_v )
1302 scale_factor = _max_v;
1304 return scale_factor;
1307 virtual osg::BoundingSphere computeBound() const
1309 // avoid being culled away by small feature culling
1310 osg::BoundingSphere bs = osg::Group::computeBound();
1311 bs.radius() *= _max_v;
1318 double _power, _factor, _offset, _min_v, _max_v;
1323 SGFlashAnimation::SGFlashAnimation(const SGPropertyNode* configNode,
1324 SGPropertyNode* modelRoot) :
1325 SGAnimation(configNode, modelRoot)
1330 SGFlashAnimation::createAnimationGroup(osg::Group& parent)
1332 Transform* transform = new Transform(getConfig());
1333 parent.addChild(transform);
1339 osgDB::RegisterDotOsgWrapperProxy flashAnimationTransformProxy
1341 new SGFlashAnimation::Transform,
1342 "SGFlashAnimation::Transform",
1343 "Object Node Transform SGFlashAnimation::Transform Group",
1345 &SGFlashAnimation::Transform::writeLocalData
1349 ////////////////////////////////////////////////////////////////////////
1350 // Implementation of billboard animation
1351 ////////////////////////////////////////////////////////////////////////
1353 class SGBillboardAnimation::Transform : public osg::Transform {
1355 Transform() : _spherical(true) {}
1356 Transform(const Transform& rhs,
1357 const osg::CopyOp& copyOp = osg::CopyOp::SHALLOW_COPY)
1358 : osg::Transform(rhs, copyOp), _spherical(rhs._spherical) {}
1359 META_Node(simgear, SGBillboardAnimation::Transform);
1360 Transform(const SGPropertyNode* configNode) :
1361 _spherical(configNode->getBoolValue("spherical", true))
1363 setReferenceFrame(RELATIVE_RF);
1364 setName(configNode->getStringValue("name", "billboard animation"));
1366 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
1367 osg::NodeVisitor* nv) const
1369 // More or less taken from plibs ssgCutout
1371 matrix(0,0) = 1; matrix(0,1) = 0; matrix(0,2) = 0;
1372 matrix(1,0) = 0; matrix(1,1) = 0; matrix(1,2) = -1;
1373 matrix(2,0) = 0; matrix(2,1) = 1; matrix(2,2) = 0;
1375 osg::Vec3 zAxis(matrix(2, 0), matrix(2, 1), matrix(2, 2));
1376 osg::Vec3 xAxis = osg::Vec3(0, 0, -1)^zAxis;
1377 osg::Vec3 yAxis = zAxis^xAxis;
1383 matrix(0,0) = xAxis[0]; matrix(0,1) = xAxis[1]; matrix(0,2) = xAxis[2];
1384 matrix(1,0) = yAxis[0]; matrix(1,1) = yAxis[1]; matrix(1,2) = yAxis[2];
1385 matrix(2,0) = zAxis[0]; matrix(2,1) = zAxis[1]; matrix(2,2) = zAxis[2];
1390 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
1391 osg::NodeVisitor* nv) const
1393 // Hmm, don't yet know how to get that back ...
1396 static bool writeLocalData(const osg::Object& obj, osgDB::Output& fw)
1398 const Transform& trans = static_cast<const Transform&>(obj);
1400 fw.indent() << (trans._spherical ? "true" : "false") << "\n";
1408 SGBillboardAnimation::SGBillboardAnimation(const SGPropertyNode* configNode,
1409 SGPropertyNode* modelRoot) :
1410 SGAnimation(configNode, modelRoot)
1415 SGBillboardAnimation::createAnimationGroup(osg::Group& parent)
1417 Transform* transform = new Transform(getConfig());
1418 parent.addChild(transform);
1424 osgDB::RegisterDotOsgWrapperProxy billboardAnimationTransformProxy
1426 new SGBillboardAnimation::Transform,
1427 "SGBillboardAnimation::Transform",
1428 "Object Node Transform SGBillboardAnimation::Transform Group",
1430 &SGBillboardAnimation::Transform::writeLocalData
1434 ////////////////////////////////////////////////////////////////////////
1435 // Implementation of a range animation
1436 ////////////////////////////////////////////////////////////////////////
1438 class SGRangeAnimation::UpdateCallback : public osg::NodeCallback {
1440 UpdateCallback(const SGCondition* condition,
1441 const SGExpressiond* minAnimationValue,
1442 const SGExpressiond* maxAnimationValue,
1443 double minValue, double maxValue) :
1444 _condition(condition),
1445 _minAnimationValue(minAnimationValue),
1446 _maxAnimationValue(maxAnimationValue),
1447 _minStaticValue(minValue),
1448 _maxStaticValue(maxValue)
1450 setName("SGRangeAnimation::UpdateCallback");
1452 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1454 osg::LOD* lod = static_cast<osg::LOD*>(node);
1455 if (!_condition || _condition->test()) {
1457 if (_minAnimationValue)
1458 minRange = _minAnimationValue->getValue();
1460 minRange = _minStaticValue;
1462 if (_maxAnimationValue)
1463 maxRange = _maxAnimationValue->getValue();
1465 maxRange = _maxStaticValue;
1466 lod->setRange(0, minRange, maxRange);
1468 lod->setRange(0, 0, SGLimitsf::max());
1474 SGSharedPtr<const SGCondition> _condition;
1475 SGSharedPtr<const SGExpressiond> _minAnimationValue;
1476 SGSharedPtr<const SGExpressiond> _maxAnimationValue;
1477 double _minStaticValue;
1478 double _maxStaticValue;
1481 SGRangeAnimation::SGRangeAnimation(const SGPropertyNode* configNode,
1482 SGPropertyNode* modelRoot) :
1483 SGAnimation(configNode, modelRoot)
1485 _condition = getCondition();
1487 std::string inputPropertyName;
1488 inputPropertyName = configNode->getStringValue("min-property", "");
1489 if (!inputPropertyName.empty()) {
1490 SGPropertyNode* inputProperty;
1491 inputProperty = modelRoot->getNode(inputPropertyName, true);
1492 SGSharedPtr<SGExpressiond> value;
1493 value = new SGPropertyExpression<double>(inputProperty);
1495 value = read_factor_offset(configNode, value, "min-factor", "min-offset");
1496 _minAnimationValue = value->simplify();
1498 inputPropertyName = configNode->getStringValue("max-property", "");
1499 if (!inputPropertyName.empty()) {
1500 SGPropertyNode* inputProperty;
1501 inputProperty = modelRoot->getNode(inputPropertyName.c_str(), true);
1503 SGSharedPtr<SGExpressiond> value;
1504 value = new SGPropertyExpression<double>(inputProperty);
1506 value = read_factor_offset(configNode, value, "max-factor", "max-offset");
1507 _maxAnimationValue = value->simplify();
1510 _initialValue[0] = configNode->getDoubleValue("min-m", 0);
1511 _initialValue[0] *= configNode->getDoubleValue("min-factor", 1);
1512 _initialValue[1] = configNode->getDoubleValue("max-m", SGLimitsf::max());
1513 _initialValue[1] *= configNode->getDoubleValue("max-factor", 1);
1517 SGRangeAnimation::createAnimationGroup(osg::Group& parent)
1519 osg::Group* group = new osg::Group;
1520 group->setName("range animation group");
1522 osg::LOD* lod = new osg::LOD;
1523 lod->setName("range animation node");
1524 parent.addChild(lod);
1526 lod->addChild(group, _initialValue[0], _initialValue[1]);
1527 lod->setCenterMode(osg::LOD::USE_BOUNDING_SPHERE_CENTER);
1528 lod->setRangeMode(osg::LOD::DISTANCE_FROM_EYE_POINT);
1529 if (_minAnimationValue || _maxAnimationValue || _condition) {
1531 uc = new UpdateCallback(_condition, _minAnimationValue, _maxAnimationValue,
1532 _initialValue[0], _initialValue[1]);
1533 lod->setUpdateCallback(uc);
1539 ////////////////////////////////////////////////////////////////////////
1540 // Implementation of a select animation
1541 ////////////////////////////////////////////////////////////////////////
1543 SGSelectAnimation::SGSelectAnimation(const SGPropertyNode* configNode,
1544 SGPropertyNode* modelRoot) :
1545 SGAnimation(configNode, modelRoot)
1550 SGSelectAnimation::createAnimationGroup(osg::Group& parent)
1552 // if no condition given, this is a noop.
1553 SGSharedPtr<SGCondition const> condition = getCondition();
1554 // trick, gets deleted with all its 'animated' children
1555 // when the animation installer returns
1557 return new osg::Group;
1558 simgear::ConditionNode* cn = new simgear::ConditionNode;
1559 cn->setName("select animation node");
1560 cn->setCondition(condition.ptr());
1561 osg::Group* grp = new osg::Group;
1563 parent.addChild(cn);
1569 ////////////////////////////////////////////////////////////////////////
1570 // Implementation of alpha test animation
1571 ////////////////////////////////////////////////////////////////////////
1573 SGAlphaTestAnimation::SGAlphaTestAnimation(const SGPropertyNode* configNode,
1574 SGPropertyNode* modelRoot) :
1575 SGAnimation(configNode, modelRoot)
1581 // Keep one copy of the most common alpha test its state set.
1582 ReentrantMutex alphaTestMutex;
1583 osg::ref_ptr<osg::AlphaFunc> standardAlphaFunc;
1584 osg::ref_ptr<osg::StateSet> alphaFuncStateSet;
1586 osg::AlphaFunc* makeAlphaFunc(float clamp)
1588 ScopedLock<ReentrantMutex> lock(alphaTestMutex);
1589 if (osg::equivalent(clamp, 0.01f)) {
1590 if (standardAlphaFunc.valid())
1591 return standardAlphaFunc.get();
1594 osg::AlphaFunc* alphaFunc = new osg::AlphaFunc;
1595 alphaFunc->setFunction(osg::AlphaFunc::GREATER);
1596 alphaFunc->setReferenceValue(clamp);
1597 alphaFunc->setDataVariance(osg::Object::STATIC);
1598 if (osg::equivalent(clamp, 0.01f))
1599 standardAlphaFunc = alphaFunc;
1603 osg::StateSet* makeAlphaTestStateSet(float clamp)
1605 using namespace OpenThreads;
1606 ScopedLock<ReentrantMutex> lock(alphaTestMutex);
1607 if (osg::equivalent(clamp, 0.01f)) {
1608 if (alphaFuncStateSet.valid())
1609 return alphaFuncStateSet.get();
1611 osg::AlphaFunc* alphaFunc = makeAlphaFunc(clamp);
1612 osg::StateSet* stateSet = new osg::StateSet;
1613 stateSet->setAttributeAndModes(alphaFunc,
1614 (osg::StateAttribute::ON
1615 | osg::StateAttribute::OVERRIDE));
1616 stateSet->setDataVariance(osg::Object::STATIC);
1617 if (osg::equivalent(clamp, 0.01f))
1618 alphaFuncStateSet = stateSet;
1623 SGAlphaTestAnimation::install(osg::Node& node)
1625 SGAnimation::install(node);
1627 float alphaClamp = getConfig()->getFloatValue("alpha-factor", 0);
1628 osg::StateSet* stateSet = node.getStateSet();
1630 node.setStateSet(makeAlphaTestStateSet(alphaClamp));
1632 stateSet->setAttributeAndModes(makeAlphaFunc(alphaClamp),
1633 (osg::StateAttribute::ON
1634 | osg::StateAttribute::OVERRIDE));
1639 //////////////////////////////////////////////////////////////////////
1640 // Blend animation installer
1641 //////////////////////////////////////////////////////////////////////
1643 // XXX This needs to be replaced by something using TexEnvCombine to
1644 // change the blend factor. Changing the alpha values in the geometry
1646 class SGBlendAnimation::BlendVisitor : public osg::NodeVisitor {
1648 BlendVisitor(float blend) :
1649 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
1651 { setVisitorType(osg::NodeVisitor::NODE_VISITOR); }
1652 virtual void apply(osg::Node& node)
1654 updateStateSet(node.getStateSet());
1657 virtual void apply(osg::Geode& node)
1659 apply((osg::Node&)node);
1660 unsigned nDrawables = node.getNumDrawables();
1661 for (unsigned i = 0; i < nDrawables; ++i) {
1662 osg::Drawable* drawable = node.getDrawable(i);
1663 osg::Geometry* geometry = drawable->asGeometry();
1666 osg::Array* array = geometry->getColorArray();
1669 osg::Vec4Array* vec4Array = dynamic_cast<osg::Vec4Array*>(array);
1672 for (unsigned k = 0; k < vec4Array->size(); ++k) {
1673 (*vec4Array)[k][3] = _blend;
1676 updateStateSet(drawable->getStateSet());
1679 void updateStateSet(osg::StateSet* stateSet)
1683 osg::StateAttribute* stateAttribute;
1684 stateAttribute = stateSet->getAttribute(osg::StateAttribute::MATERIAL);
1685 if (!stateAttribute)
1687 osg::Material* material = dynamic_cast<osg::Material*>(stateAttribute);
1690 material->setAlpha(osg::Material::FRONT_AND_BACK, _blend);
1692 stateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
1693 stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
1695 stateSet->setRenderingHint(osg::StateSet::DEFAULT_BIN);
1702 class SGBlendAnimation::UpdateCallback : public osg::NodeCallback {
1704 UpdateCallback(const SGPropertyNode* configNode, const SGExpressiond* v) :
1708 setName("SGBlendAnimation::UpdateCallback");
1710 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1712 double blend = _animationValue->getValue();
1713 if (blend != _prev_value) {
1714 _prev_value = blend;
1715 BlendVisitor visitor(1-blend);
1716 node->accept(visitor);
1722 SGSharedPtr<SGExpressiond const> _animationValue;
1726 SGBlendAnimation::SGBlendAnimation(const SGPropertyNode* configNode,
1727 SGPropertyNode* modelRoot)
1728 : SGAnimation(configNode, modelRoot),
1729 _animationValue(read_value(configNode, modelRoot, "", 0, 1))
1734 SGBlendAnimation::createAnimationGroup(osg::Group& parent)
1736 if (!_animationValue)
1739 osg::Group* group = new osg::Switch;
1740 group->setName("blend animation node");
1741 group->setUpdateCallback(new UpdateCallback(getConfig(), _animationValue));
1742 parent.addChild(group);
1747 SGBlendAnimation::install(osg::Node& node)
1749 SGAnimation::install(node);
1750 // make sure we do not change common geometries,
1751 // that also creates new display lists for these subgeometries.
1752 cloneDrawables(node);
1753 DoDrawArraysVisitor visitor;
1754 node.accept(visitor);
1758 //////////////////////////////////////////////////////////////////////
1759 // Timed animation installer
1760 //////////////////////////////////////////////////////////////////////
1764 class SGTimedAnimation::UpdateCallback : public osg::NodeCallback {
1766 UpdateCallback(const SGPropertyNode* configNode) :
1769 _duration_sec(configNode->getDoubleValue("duration-sec", 1)),
1770 _last_time_sec(SGLimitsd::max()),
1771 _use_personality(configNode->getBoolValue("use-personality", false))
1773 std::vector<SGSharedPtr<SGPropertyNode> > nodes;
1774 nodes = configNode->getChildren("branch-duration-sec");
1775 for (size_t i = 0; i < nodes.size(); ++i) {
1776 unsigned ind = nodes[ i ]->getIndex();
1777 while ( ind >= _durations.size() ) {
1778 _durations.push_back(DurationSpec(_duration_sec));
1780 SGPropertyNode_ptr rNode = nodes[i]->getChild("random");
1782 _durations[ind] = DurationSpec(nodes[ i ]->getDoubleValue());
1784 _durations[ind] = DurationSpec(rNode->getDoubleValue( "min", 0),
1785 rNode->getDoubleValue( "max", 1));
1788 setName("SGTimedAnimation::UpdateCallback");
1790 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1792 assert(dynamic_cast<osg::Switch*>(node));
1793 osg::Switch* sw = static_cast<osg::Switch*>(node);
1795 unsigned nChildren = sw->getNumChildren();
1797 // blow up the durations vector to the required size
1798 while (_durations.size() < nChildren) {
1799 _durations.push_back(_duration_sec);
1801 // make sure the current index is an duration that really exists
1802 _current_index = _current_index % nChildren;
1804 // update the time and compute the current systems time value
1805 double t = nv->getFrameStamp()->getReferenceTime();
1806 if (_last_time_sec == SGLimitsd::max()) {
1809 double dt = t - _last_time_sec;
1810 if (_use_personality)
1811 dt *= 1 + 0.2*(0.5 - sg_random());
1816 double currentDuration = _durations[_current_index].get();
1817 while (currentDuration < _reminder) {
1818 _reminder -= currentDuration;
1819 _current_index = (_current_index + 1) % nChildren;
1820 currentDuration = _durations[_current_index].get();
1823 sw->setSingleChildOn(_current_index);
1829 struct DurationSpec {
1830 DurationSpec(double t) :
1831 minTime(SGMiscd::max(0.01, t)),
1832 maxTime(SGMiscd::max(0.01, t))
1834 DurationSpec(double t0, double t1) :
1835 minTime(SGMiscd::max(0.01, t0)),
1836 maxTime(SGMiscd::max(0.01, t1))
1839 { return minTime + sg_random()*(maxTime - minTime); }
1843 std::vector<DurationSpec> _durations;
1844 unsigned _current_index;
1846 double _duration_sec;
1847 double _last_time_sec;
1848 bool _use_personality;
1852 SGTimedAnimation::SGTimedAnimation(const SGPropertyNode* configNode,
1853 SGPropertyNode* modelRoot)
1854 : SGAnimation(configNode, modelRoot)
1859 SGTimedAnimation::createAnimationGroup(osg::Group& parent)
1861 osg::Switch* sw = new osg::Switch;
1862 sw->setName("timed animation node");
1863 sw->setUpdateCallback(new UpdateCallback(getConfig()));
1864 parent.addChild(sw);
1869 ////////////////////////////////////////////////////////////////////////
1870 // dynamically switch on/off shadows
1871 ////////////////////////////////////////////////////////////////////////
1873 class SGShadowAnimation::UpdateCallback : public osg::NodeCallback {
1875 UpdateCallback(const SGCondition* condition) :
1876 _condition(condition)
1878 setName("SGShadowAnimation::UpdateCallback");
1880 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1882 if (_condition->test())
1883 node->setNodeMask( SG_NODEMASK_CASTSHADOW_BIT | node->getNodeMask());
1885 node->setNodeMask(~SG_NODEMASK_CASTSHADOW_BIT & node->getNodeMask());
1890 SGSharedPtr<const SGCondition> _condition;
1893 SGShadowAnimation::SGShadowAnimation(const SGPropertyNode* configNode,
1894 SGPropertyNode* modelRoot) :
1895 SGAnimation(configNode, modelRoot)
1900 SGShadowAnimation::createAnimationGroup(osg::Group& parent)
1902 SGSharedPtr<SGCondition const> condition = getCondition();
1904 osg::Group* group = new osg::Group;
1905 group->setName("shadow animation");
1907 group->setUpdateCallback(new UpdateCallback(condition));
1909 group->setNodeMask(~SG_NODEMASK_CASTSHADOW_BIT & group->getNodeMask());
1910 parent.addChild(group);
1915 ////////////////////////////////////////////////////////////////////////
1916 // Implementation of SGTexTransformAnimation
1917 ////////////////////////////////////////////////////////////////////////
1919 class SGTexTransformAnimation::Transform : public SGReferenced {
1924 virtual ~Transform()
1926 void setValue(double value)
1928 virtual void transform(osg::Matrix&) = 0;
1933 class SGTexTransformAnimation::Translation :
1934 public SGTexTransformAnimation::Transform {
1936 Translation(const SGVec3d& axis) :
1939 virtual void transform(osg::Matrix& matrix)
1942 set_translation(tmp, _value, _axis);
1943 matrix.preMult(tmp);
1949 class SGTexTransformAnimation::Rotation :
1950 public SGTexTransformAnimation::Transform {
1952 Rotation(const SGVec3d& axis, const SGVec3d& center) :
1956 virtual void transform(osg::Matrix& matrix)
1959 set_rotation(tmp, _value, _center, _axis);
1960 matrix.preMult(tmp);
1967 class SGTexTransformAnimation::UpdateCallback :
1968 public osg::StateAttribute::Callback {
1970 UpdateCallback(const SGCondition* condition) :
1971 _condition(condition)
1973 setName("SGTexTransformAnimation::UpdateCallback");
1975 virtual void operator () (osg::StateAttribute* sa, osg::NodeVisitor*)
1977 if (!_condition || _condition->test()) {
1978 TransformList::const_iterator i;
1979 for (i = _transforms.begin(); i != _transforms.end(); ++i)
1980 i->transform->setValue(i->value->getValue());
1982 assert(dynamic_cast<osg::TexMat*>(sa));
1983 osg::TexMat* texMat = static_cast<osg::TexMat*>(sa);
1984 texMat->getMatrix().makeIdentity();
1985 TransformList::const_iterator i;
1986 for (i = _transforms.begin(); i != _transforms.end(); ++i)
1987 i->transform->transform(texMat->getMatrix());
1989 void appendTransform(Transform* transform, SGExpressiond* value)
1991 Entry entry = { transform, value };
1992 transform->transform(_matrix);
1993 _transforms.push_back(entry);
1998 SGSharedPtr<Transform> transform;
1999 SGSharedPtr<const SGExpressiond> value;
2001 typedef std::vector<Entry> TransformList;
2002 TransformList _transforms;
2003 SGSharedPtr<const SGCondition> _condition;
2004 osg::Matrix _matrix;
2007 SGTexTransformAnimation::SGTexTransformAnimation(const SGPropertyNode* configNode,
2008 SGPropertyNode* modelRoot) :
2009 SGAnimation(configNode, modelRoot)
2014 SGTexTransformAnimation::createAnimationGroup(osg::Group& parent)
2016 osg::Group* group = new osg::Group;
2017 group->setName("texture transform group");
2018 osg::StateSet* stateSet = group->getOrCreateStateSet();
2019 stateSet->setDataVariance(osg::Object::DYNAMIC);
2020 osg::TexMat* texMat = new osg::TexMat;
2021 UpdateCallback* updateCallback = new UpdateCallback(getCondition());
2022 // interpret the configs ...
2023 std::string type = getType();
2025 if (type == "textranslate") {
2026 appendTexTranslate(getConfig(), updateCallback);
2027 } else if (type == "texrotate") {
2028 appendTexRotate(getConfig(), updateCallback);
2029 } else if (type == "texmultiple") {
2030 std::vector<SGSharedPtr<SGPropertyNode> > transformConfigs;
2031 transformConfigs = getConfig()->getChildren("transform");
2032 for (unsigned i = 0; i < transformConfigs.size(); ++i) {
2033 std::string subtype = transformConfigs[i]->getStringValue("subtype", "");
2034 if (subtype == "textranslate")
2035 appendTexTranslate(transformConfigs[i], updateCallback);
2036 else if (subtype == "texrotate")
2037 appendTexRotate(transformConfigs[i], updateCallback);
2039 SG_LOG(SG_INPUT, SG_ALERT,
2040 "Ignoring unknown texture transform subtype");
2043 SG_LOG(SG_INPUT, SG_ALERT, "Ignoring unknown texture transform type");
2046 texMat->setUpdateCallback(updateCallback);
2047 stateSet->setTextureAttribute(0, texMat);
2048 parent.addChild(group);
2053 SGTexTransformAnimation::appendTexTranslate(const SGPropertyNode* config,
2054 UpdateCallback* updateCallback)
2056 std::string propertyName = config->getStringValue("property", "");
2057 SGSharedPtr<SGExpressiond> value;
2058 if (propertyName.empty())
2059 value = new SGConstExpression<double>(0);
2061 SGPropertyNode* inputProperty = getModelRoot()->getNode(propertyName, true);
2062 value = new SGPropertyExpression<double>(inputProperty);
2065 SGInterpTable* table = read_interpolation_table(config);
2067 value = new SGInterpTableExpression<double>(value, table);
2068 double biasValue = config->getDoubleValue("bias", 0);
2070 value = new SGBiasExpression<double>(value, biasValue);
2071 value = new SGStepExpression<double>(value,
2072 config->getDoubleValue("step", 0),
2073 config->getDoubleValue("scroll", 0));
2074 value = value->simplify();
2076 double biasValue = config->getDoubleValue("bias", 0);
2078 value = new SGBiasExpression<double>(value, biasValue);
2079 value = new SGStepExpression<double>(value,
2080 config->getDoubleValue("step", 0),
2081 config->getDoubleValue("scroll", 0));
2082 value = read_offset_factor(config, value, "factor", "offset");
2084 if (config->hasChild("min") || config->hasChild("max")) {
2085 double minClip = config->getDoubleValue("min", -SGLimitsd::max());
2086 double maxClip = config->getDoubleValue("max", SGLimitsd::max());
2087 value = new SGClipExpression<double>(value, minClip, maxClip);
2089 value = value->simplify();
2091 SGVec3d axis(config->getDoubleValue("axis/x", 0),
2092 config->getDoubleValue("axis/y", 0),
2093 config->getDoubleValue("axis/z", 0));
2094 Translation* translation;
2095 translation = new Translation(normalize(axis));
2096 translation->setValue(config->getDoubleValue("starting-position", 0));
2097 updateCallback->appendTransform(translation, value);
2101 SGTexTransformAnimation::appendTexRotate(const SGPropertyNode* config,
2102 UpdateCallback* updateCallback)
2104 std::string propertyName = config->getStringValue("property", "");
2105 SGSharedPtr<SGExpressiond> value;
2106 if (propertyName.empty())
2107 value = new SGConstExpression<double>(0);
2109 SGPropertyNode* inputProperty = getModelRoot()->getNode(propertyName, true);
2110 value = new SGPropertyExpression<double>(inputProperty);
2113 SGInterpTable* table = read_interpolation_table(config);
2115 value = new SGInterpTableExpression<double>(value, table);
2116 double biasValue = config->getDoubleValue("bias", 0);
2118 value = new SGBiasExpression<double>(value, biasValue);
2119 value = new SGStepExpression<double>(value,
2120 config->getDoubleValue("step", 0),
2121 config->getDoubleValue("scroll", 0));
2122 value = value->simplify();
2124 double biasValue = config->getDoubleValue("bias", 0);
2126 value = new SGBiasExpression<double>(value, biasValue);
2127 value = new SGStepExpression<double>(value,
2128 config->getDoubleValue("step", 0),
2129 config->getDoubleValue("scroll", 0));
2130 value = read_offset_factor(config, value, "factor", "offset-deg");
2132 if (config->hasChild("min-deg") || config->hasChild("max-deg")) {
2133 double minClip = config->getDoubleValue("min-deg", -SGLimitsd::max());
2134 double maxClip = config->getDoubleValue("max-deg", SGLimitsd::max());
2135 value = new SGClipExpression<double>(value, minClip, maxClip);
2137 value = value->simplify();
2139 SGVec3d axis(config->getDoubleValue("axis/x", 0),
2140 config->getDoubleValue("axis/y", 0),
2141 config->getDoubleValue("axis/z", 0));
2142 SGVec3d center(config->getDoubleValue("center/x", 0),
2143 config->getDoubleValue("center/y", 0),
2144 config->getDoubleValue("center/z", 0));
2146 rotation = new Rotation(normalize(axis), center);
2147 rotation->setValue(config->getDoubleValue("starting-position-deg", 0));
2148 updateCallback->appendTransform(rotation, value);
2152 ////////////////////////////////////////////////////////////////////////
2153 // Implementation of SGPickAnimation
2154 ////////////////////////////////////////////////////////////////////////
2156 class SGPickAnimation::PickCallback : public SGPickCallback {
2158 PickCallback(const SGPropertyNode* configNode,
2159 SGPropertyNode* modelRoot) :
2160 _repeatable(configNode->getBoolValue("repeatable", false)),
2161 _repeatInterval(configNode->getDoubleValue("interval-sec", 0.1))
2163 SG_LOG(SG_INPUT, SG_DEBUG, "Reading all bindings");
2164 std::vector<SGPropertyNode_ptr> bindings;
2166 bindings = configNode->getChildren("button");
2167 for (unsigned int i = 0; i < bindings.size(); ++i) {
2168 _buttons.push_back( bindings[i]->getIntValue() );
2170 bindings = configNode->getChildren("binding");
2171 for (unsigned int i = 0; i < bindings.size(); ++i) {
2172 _bindingsDown.push_back(new SGBinding(bindings[i], modelRoot));
2175 const SGPropertyNode* upNode = configNode->getChild("mod-up");
2178 bindings = upNode->getChildren("binding");
2179 for (unsigned int i = 0; i < bindings.size(); ++i) {
2180 _bindingsUp.push_back(new SGBinding(bindings[i], modelRoot));
2183 virtual bool buttonPressed(int button, const Info&)
2186 for( std::vector<int>::iterator it = _buttons.begin(); it != _buttons.end(); ++it ) {
2187 if( *it == button ) {
2194 SGBindingList::const_iterator i;
2195 for (i = _bindingsDown.begin(); i != _bindingsDown.end(); ++i)
2197 _repeatTime = -_repeatInterval; // anti-bobble: delay start of repeat
2200 virtual void buttonReleased(void)
2202 SGBindingList::const_iterator i;
2203 for (i = _bindingsUp.begin(); i != _bindingsUp.end(); ++i)
2206 virtual void update(double dt)
2212 while (_repeatInterval < _repeatTime) {
2213 _repeatTime -= _repeatInterval;
2214 SGBindingList::const_iterator i;
2215 for (i = _bindingsDown.begin(); i != _bindingsDown.end(); ++i)
2220 SGBindingList _bindingsDown;
2221 SGBindingList _bindingsUp;
2222 std::vector<int> _buttons;
2224 double _repeatInterval;
2228 class VncVisitor : public osg::NodeVisitor {
2230 VncVisitor(double x, double y, int mask) :
2231 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
2232 _texX(x), _texY(y), _mask(mask), _done(false)
2234 SG_LOG(SG_INPUT, SG_DEBUG, "VncVisitor constructor "
2235 << x << "," << y << " mask " << mask);
2238 virtual void apply(osg::Node &node)
2240 // Some nodes have state sets attached
2241 touchStateSet(node.getStateSet());
2245 // See whether we are a geode worth exploring
2246 osg::Geode *g = dynamic_cast<osg::Geode*>(&node);
2248 // Go find all its drawables
2249 int i = g->getNumDrawables();
2251 osg::Drawable *d = g->getDrawable(i);
2252 if (d) touchDrawable(*d);
2254 // Out of optimism, do the same for EffectGeode
2255 simgear::EffectGeode *eg = dynamic_cast<simgear::EffectGeode*>(&node);
2257 for (simgear::EffectGeode::DrawablesIterator di = eg->drawablesBegin();
2258 di != eg->drawablesEnd(); di++) {
2259 touchDrawable(**di);
2261 // Now see whether the EffectGeode has an Effect
2262 simgear::Effect *e = eg->getEffect();
2264 touchStateSet(e->getDefaultStateSet());
2268 inline void touchDrawable(osg::Drawable &d)
2270 osg::StateSet *ss = d.getStateSet();
2274 void touchStateSet(osg::StateSet *ss)
2277 osg::StateAttribute *sa = ss->getTextureAttribute(0,
2278 osg::StateAttribute::TEXTURE);
2280 osg::Texture *t = sa->asTexture();
2282 osg::Image *img = t->getImage(0);
2285 int pixX = _texX * img->s();
2286 int pixY = _texY * img->t();
2287 _done = img->sendPointerEvent(pixX, pixY, _mask);
2288 SG_LOG(SG_INPUT, SG_DEBUG, "VncVisitor image said " << _done
2289 << " to coord " << pixX << "," << pixY);
2293 inline bool wasSuccessful()
2299 double _texX, _texY;
2305 class SGPickAnimation::VncCallback : public SGPickCallback {
2307 VncCallback(const SGPropertyNode* configNode,
2308 SGPropertyNode* modelRoot,
2312 SG_LOG(SG_INPUT, SG_DEBUG, "Configuring VNC callback");
2313 const char *cornernames[3] = {"top-left", "top-right", "bottom-left"};
2314 SGVec3d *cornercoords[3] = {&_topLeft, &_toRight, &_toDown};
2315 for (int c =0; c < 3; c++) {
2316 const SGPropertyNode* cornerNode = configNode->getChild(cornernames[c]);
2317 *cornercoords[c] = SGVec3d(
2318 cornerNode->getDoubleValue("x"),
2319 cornerNode->getDoubleValue("y"),
2320 cornerNode->getDoubleValue("z"));
2322 _toRight -= _topLeft;
2323 _toDown -= _topLeft;
2324 _squaredRight = dot(_toRight, _toRight);
2325 _squaredDown = dot(_toDown, _toDown);
2328 virtual bool buttonPressed(int button, const Info& info)
2330 SGVec3d loc(info.local);
2331 SG_LOG(SG_INPUT, SG_DEBUG, "VNC pressed " << button << ": " << loc);
2333 _x = dot(loc, _toRight) / _squaredRight;
2334 _y = dot(loc, _toDown) / _squaredDown;
2335 if (_x<0) _x = 0; else if (_x > 1) _x = 1;
2336 if (_y<0) _y = 0; else if (_y > 1) _y = 1;
2337 VncVisitor vv(_x, _y, 1 << button);
2339 return vv.wasSuccessful();
2342 virtual void buttonReleased(void)
2344 SG_LOG(SG_INPUT, SG_DEBUG, "VNC release");
2345 VncVisitor vv(_x, _y, 0);
2348 virtual void update(double dt)
2353 osg::ref_ptr<osg::Group> _node;
2354 SGVec3d _topLeft, _toRight, _toDown;
2355 double _squaredRight, _squaredDown;
2358 SGPickAnimation::SGPickAnimation(const SGPropertyNode* configNode,
2359 SGPropertyNode* modelRoot) :
2360 SGAnimation(configNode, modelRoot)
2366 Mutex colorModeUniformMutex;
2367 osg::ref_ptr<osg::Uniform> colorModeUniform;
2371 SGPickAnimation::createAnimationGroup(osg::Group& parent)
2373 osg::Group* commonGroup = new osg::Group;
2375 // Contains the normal geometry that is interactive
2376 osg::ref_ptr<osg::Group> normalGroup = new osg::Group;
2377 normalGroup->setName("pick normal group");
2378 normalGroup->addChild(commonGroup);
2380 // Used to render the geometry with just yellow edges
2381 osg::Group* highlightGroup = new osg::Group;
2382 highlightGroup->setName("pick highlight group");
2383 highlightGroup->setNodeMask(SG_NODEMASK_PICK_BIT);
2384 highlightGroup->addChild(commonGroup);
2385 SGSceneUserData* ud;
2386 ud = SGSceneUserData::getOrCreateSceneUserData(commonGroup);
2388 // add actions that become macro and command invocations
2389 std::vector<SGPropertyNode_ptr> actions;
2390 actions = getConfig()->getChildren("action");
2391 for (unsigned int i = 0; i < actions.size(); ++i)
2392 ud->addPickCallback(new PickCallback(actions[i], getModelRoot()));
2393 // Look for the VNC sessions that want raw mouse input
2394 actions = getConfig()->getChildren("vncaction");
2395 for (unsigned int i = 0; i < actions.size(); ++i)
2396 ud->addPickCallback(new VncCallback(actions[i], getModelRoot(),
2399 // prepare a state set that paints the edges of this object yellow
2400 // The material and texture attributes are set with
2401 // OVERRIDE|PROTECTED in case there is a material animation on a
2402 // higher node in the scene graph, which would have its material
2403 // attribute set with OVERRIDE.
2404 osg::StateSet* stateSet = highlightGroup->getOrCreateStateSet();
2405 osg::Texture2D* white = StateAttributeFactory::instance()->getWhiteTexture();
2406 stateSet->setTextureAttributeAndModes(0, white,
2407 (osg::StateAttribute::ON
2408 | osg::StateAttribute::OVERRIDE
2409 | osg::StateAttribute::PROTECTED));
2410 osg::PolygonOffset* polygonOffset = new osg::PolygonOffset;
2411 polygonOffset->setFactor(-1);
2412 polygonOffset->setUnits(-1);
2413 stateSet->setAttribute(polygonOffset, osg::StateAttribute::OVERRIDE);
2414 stateSet->setMode(GL_POLYGON_OFFSET_LINE,
2415 osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
2416 osg::PolygonMode* polygonMode = new osg::PolygonMode;
2417 polygonMode->setMode(osg::PolygonMode::FRONT_AND_BACK,
2418 osg::PolygonMode::LINE);
2419 stateSet->setAttribute(polygonMode, osg::StateAttribute::OVERRIDE);
2420 osg::Material* material = new osg::Material;
2421 material->setColorMode(osg::Material::OFF);
2422 material->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(0, 0, 0, 1));
2423 // XXX Alpha < 1.0 in the diffuse material value is a signal to the
2424 // default shader to take the alpha value from the material value
2425 // and not the glColor. In many cases the pick animation geometry is
2426 // transparent, so the outline would not be visible without this hack.
2427 material->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(0, 0, 0, .95));
2428 material->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(1, 1, 0, 1));
2429 material->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0, 0, 0, 0));
2430 stateSet->setAttribute(
2431 material, osg::StateAttribute::OVERRIDE | osg::StateAttribute::PROTECTED);
2432 // The default shader has a colorMode uniform that mimics the
2433 // behavior of Material color mode.
2434 osg::Uniform* cmUniform = 0;
2436 ScopedLock<Mutex> lock(colorModeUniformMutex);
2437 if (!colorModeUniform.valid()) {
2438 colorModeUniform = new osg::Uniform(osg::Uniform::INT, "colorMode");
2439 colorModeUniform->set(0); // MODE_OFF
2440 colorModeUniform->setDataVariance(osg::Object::STATIC);
2442 cmUniform = colorModeUniform.get();
2444 stateSet->addUniform(cmUniform,
2445 osg::StateAttribute::OVERRIDE | osg::StateAttribute::ON);
2446 // Only add normal geometry if configured
2447 if (getConfig()->getBoolValue("visible", true))
2448 parent.addChild(normalGroup.get());
2449 parent.addChild(highlightGroup);