1 // animation.cxx - classes to manage model animation.
2 // Written by David Megginson, started 2002.
4 // This file is in the Public Domain, and comes with no warranty.
7 # include <simgear_config.h>
10 #include <string.h> // for strcmp()
15 #include <OpenThreads/Mutex>
16 #include <OpenThreads/ReentrantMutex>
17 #include <OpenThreads/ScopedLock>
19 #include <osg/AlphaFunc>
20 #include <osg/Drawable>
22 #include <osg/Geometry>
26 #include <osg/PolygonMode>
27 #include <osg/PolygonOffset>
28 #include <osg/StateSet>
31 #include <osg/Texture2D>
32 #include <osg/Transform>
33 #include <osg/Uniform>
34 #include <osgDB/ReadFile>
35 #include <osgDB/Registry>
36 #include <osgDB/Input>
37 #include <osgDB/ParameterOutput>
40 #include <simgear/math/interpolater.hxx>
41 #include <simgear/props/condition.hxx>
42 #include <simgear/props/props.hxx>
43 #include <simgear/structure/SGBinding.hxx>
44 #include <simgear/scene/material/EffectGeode.hxx>
45 #include <simgear/scene/util/SGNodeMasks.hxx>
46 #include <simgear/scene/util/SGSceneUserData.hxx>
47 #include <simgear/scene/util/SGStateAttributeVisitor.hxx>
48 #include <simgear/scene/util/StateAttributeFactory.hxx>
50 #include "animation.hxx"
53 #include "SGTranslateTransform.hxx"
54 #include "SGMaterialAnimation.hxx"
55 #include "SGRotateTransform.hxx"
56 #include "SGScaleTransform.hxx"
57 #include "SGInteractionAnimation.hxx"
59 #include "ConditionNode.hxx"
61 using OpenThreads::Mutex;
62 using OpenThreads::ReentrantMutex;
63 using OpenThreads::ScopedLock;
65 using namespace simgear;
67 ////////////////////////////////////////////////////////////////////////
68 // Static utility functions.
69 ////////////////////////////////////////////////////////////////////////
72 * Set up the transform matrix for a spin or rotation.
75 set_rotation (osg::Matrix &matrix, double position_deg,
76 const SGVec3d ¢er, const SGVec3d &axis)
78 double temp_angle = -SGMiscd::deg2rad(position_deg);
80 double s = sin(temp_angle);
81 double c = cos(temp_angle);
84 // axis was normalized at load time
85 // hint to the compiler to put these into FP registers
90 matrix(0, 0) = t * x * x + c ;
91 matrix(0, 1) = t * y * x - s * z ;
92 matrix(0, 2) = t * z * x + s * y ;
95 matrix(1, 0) = t * x * y + s * z ;
96 matrix(1, 1) = t * y * y + c ;
97 matrix(1, 2) = t * z * y - s * x ;
100 matrix(2, 0) = t * x * z - s * y ;
101 matrix(2, 1) = t * y * z + s * x ;
102 matrix(2, 2) = t * z * z + c ;
105 // hint to the compiler to put these into FP registers
110 matrix(3, 0) = x - x*matrix(0, 0) - y*matrix(1, 0) - z*matrix(2, 0);
111 matrix(3, 1) = y - x*matrix(0, 1) - y*matrix(1, 1) - z*matrix(2, 1);
112 matrix(3, 2) = z - x*matrix(0, 2) - y*matrix(1, 2) - z*matrix(2, 2);
117 * Set up the transform matrix for a translation.
120 set_translation (osg::Matrix &matrix, double position_m, const SGVec3d &axis)
122 SGVec3d xyz = axis * position_m;
123 matrix.makeIdentity();
124 matrix(3, 0) = xyz[0];
125 matrix(3, 1) = xyz[1];
126 matrix(3, 2) = xyz[2];
130 * Read an interpolation table from properties.
132 static SGInterpTable *
133 read_interpolation_table(const SGPropertyNode* props)
135 const SGPropertyNode* table_node = props->getNode("interpolation");
138 return new SGInterpTable(table_node);
142 unit_string(const char* value, const char* unit)
144 return std::string(value) + unit;
147 class SGPersonalityScaleOffsetExpression : public SGUnaryExpression<double> {
149 SGPersonalityScaleOffsetExpression(SGExpression<double>* expr,
150 SGPropertyNode const* config,
151 const std::string& scalename,
152 const std::string& offsetname,
154 double defOffset = 0) :
155 SGUnaryExpression<double>(expr),
156 _scale(config, scalename.c_str(), defScale),
157 _offset(config, offsetname.c_str(), defOffset)
159 void setScale(double scale)
161 void setOffset(double offset)
162 { _offset = offset; }
164 virtual void eval(double& value, const simgear::expression::Binding* b) const
168 value = _offset + _scale*getOperand()->getValue(b);
171 virtual bool isConst() const { return false; }
174 mutable SGPersonalityParameter<double> _scale;
175 mutable SGPersonalityParameter<double> _offset;
179 static SGExpressiond*
180 read_factor_offset(const SGPropertyNode* configNode, SGExpressiond* expr,
181 const std::string& factor, const std::string& offset)
183 double factorValue = configNode->getDoubleValue(factor, 1);
184 if (factorValue != 1)
185 expr = new SGScaleExpression<double>(expr, factorValue);
186 double offsetValue = configNode->getDoubleValue(offset, 0);
187 if (offsetValue != 0)
188 expr = new SGBiasExpression<double>(expr, offsetValue);
192 static SGExpressiond*
193 read_offset_factor(const SGPropertyNode* configNode, SGExpressiond* expr,
194 const std::string& factor, const std::string& offset)
196 double offsetValue = configNode->getDoubleValue(offset, 0);
197 if (offsetValue != 0)
198 expr = new SGBiasExpression<double>(expr, offsetValue);
199 double factorValue = configNode->getDoubleValue(factor, 1);
200 if (factorValue != 1)
201 expr = new SGScaleExpression<double>(expr, factorValue);
206 read_value(const SGPropertyNode* configNode, SGPropertyNode* modelRoot,
207 const char* unit, double defMin, double defMax)
209 const SGPropertyNode * expression = configNode->getNode( "expression" );
210 if( expression != NULL )
211 return SGReadDoubleExpression( modelRoot, expression->getChild(0) );
213 SGExpression<double>* value = 0;
215 std::string inputPropertyName = configNode->getStringValue("property", "");
216 if (inputPropertyName.empty()) {
217 std::string spos = unit_string("starting-position", unit);
218 double initPos = configNode->getDoubleValue(spos, 0);
219 value = new SGConstExpression<double>(initPos);
221 SGPropertyNode* inputProperty;
222 inputProperty = modelRoot->getNode(inputPropertyName, true);
223 value = new SGPropertyExpression<double>(inputProperty);
226 SGInterpTable* interpTable = read_interpolation_table(configNode);
228 return new SGInterpTableExpression<double>(value, interpTable);
230 std::string offset = unit_string("offset", unit);
231 std::string min = unit_string("min", unit);
232 std::string max = unit_string("max", unit);
234 if (configNode->getBoolValue("use-personality", false)) {
235 value = new SGPersonalityScaleOffsetExpression(value, configNode,
238 value = read_factor_offset(configNode, value, "factor", offset);
241 double minClip = configNode->getDoubleValue(min, defMin);
242 double maxClip = configNode->getDoubleValue(max, defMax);
243 if (minClip > SGMiscd::min(SGLimitsd::min(), -SGLimitsd::max()) ||
244 maxClip < SGLimitsd::max())
245 value = new SGClipExpression<double>(value, minClip, maxClip);
253 ////////////////////////////////////////////////////////////////////////
254 // Animation installer
255 ////////////////////////////////////////////////////////////////////////
257 class SGAnimation::RemoveModeVisitor : public SGStateAttributeVisitor {
259 RemoveModeVisitor(osg::StateAttribute::GLMode mode) :
262 virtual void apply(osg::StateSet* stateSet)
266 stateSet->removeMode(_mode);
269 osg::StateAttribute::GLMode _mode;
272 class SGAnimation::RemoveAttributeVisitor : public SGStateAttributeVisitor {
274 RemoveAttributeVisitor(osg::StateAttribute::Type type) :
277 virtual void apply(osg::StateSet* stateSet)
281 while (stateSet->getAttribute(_type)) {
282 stateSet->removeAttribute(_type);
286 osg::StateAttribute::Type _type;
289 class SGAnimation::RemoveTextureModeVisitor : public SGStateAttributeVisitor {
291 RemoveTextureModeVisitor(unsigned unit, osg::StateAttribute::GLMode mode) :
295 virtual void apply(osg::StateSet* stateSet)
299 stateSet->removeTextureMode(_unit, _mode);
303 osg::StateAttribute::GLMode _mode;
306 class SGAnimation::RemoveTextureAttributeVisitor :
307 public SGStateAttributeVisitor {
309 RemoveTextureAttributeVisitor(unsigned unit,
310 osg::StateAttribute::Type type) :
314 virtual void apply(osg::StateSet* stateSet)
318 while (stateSet->getTextureAttribute(_unit, _type)) {
319 stateSet->removeTextureAttribute(_unit, _type);
324 osg::StateAttribute::Type _type;
327 class SGAnimation::BinToInheritVisitor : public SGStateAttributeVisitor {
329 virtual void apply(osg::StateSet* stateSet)
333 stateSet->setRenderBinToInherit();
337 class SGAnimation::DrawableCloneVisitor : public osg::NodeVisitor {
339 DrawableCloneVisitor() :
340 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
342 void apply(osg::Geode& geode)
344 for (unsigned i = 0 ; i < geode.getNumDrawables(); ++i) {
345 osg::CopyOp copyOp(osg::CopyOp::DEEP_COPY_ALL &
346 ~osg::CopyOp::DEEP_COPY_TEXTURES);
347 geode.setDrawable(i, copyOp(geode.getDrawable(i)));
354 // Set all drawables to not use display lists. OSG will use
355 // glDrawArrays instead.
356 struct DoDrawArraysVisitor : public osg::NodeVisitor {
357 DoDrawArraysVisitor() :
358 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
360 void apply(osg::Geode& geode)
365 for (int i = 0; i < (int)geode.getNumDrawables(); ++i)
366 geode.getDrawable(i)->setUseDisplayList(false);
371 SGAnimation::SGAnimation(const SGPropertyNode* configNode,
372 SGPropertyNode* modelRoot) :
373 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
375 _configNode(configNode),
376 _modelRoot(modelRoot)
378 _name = configNode->getStringValue("name", "");
379 _enableHOT = configNode->getBoolValue("enable-hot", true);
380 _disableShadow = configNode->getBoolValue("disable-shadow", false);
381 std::vector<SGPropertyNode_ptr> objectNames =
382 configNode->getChildren("object-name");
383 for (unsigned i = 0; i < objectNames.size(); ++i)
384 _objectNames.push_back(objectNames[i]->getStringValue());
387 SGAnimation::~SGAnimation()
392 SG_LOG(SG_IO, SG_ALERT, "Could not find at least one of the following"
393 " objects for animation:\n");
394 std::list<std::string>::const_iterator i;
395 for (i = _objectNames.begin(); i != _objectNames.end(); ++i)
396 SG_LOG(SG_IO, SG_ALERT, *i << "\n");
400 SGAnimation::animate(osg::Node* node, const SGPropertyNode* configNode,
401 SGPropertyNode* modelRoot,
402 const osgDB::ReaderWriter::Options* options)
404 std::string type = configNode->getStringValue("type", "none");
405 if (type == "alpha-test") {
406 SGAlphaTestAnimation animInst(configNode, modelRoot);
407 animInst.apply(node);
408 } else if (type == "billboard") {
409 SGBillboardAnimation animInst(configNode, modelRoot);
410 animInst.apply(node);
411 } else if (type == "blend") {
412 SGBlendAnimation animInst(configNode, modelRoot);
413 animInst.apply(node);
414 } else if (type == "dist-scale") {
415 SGDistScaleAnimation animInst(configNode, modelRoot);
416 animInst.apply(node);
417 } else if (type == "flash") {
418 SGFlashAnimation animInst(configNode, modelRoot);
419 animInst.apply(node);
420 } else if (type == "interaction") {
421 SGInteractionAnimation animInst(configNode, modelRoot);
422 animInst.apply(node);
423 } else if (type == "material") {
424 SGMaterialAnimation animInst(configNode, modelRoot, options);
425 animInst.apply(node);
426 } else if (type == "noshadow") {
427 SGShadowAnimation animInst(configNode, modelRoot);
428 animInst.apply(node);
429 } else if (type == "pick") {
430 SGPickAnimation animInst(configNode, modelRoot);
431 animInst.apply(node);
432 } else if (type == "range") {
433 SGRangeAnimation animInst(configNode, modelRoot);
434 animInst.apply(node);
435 } else if (type == "rotate" || type == "spin") {
436 SGRotateAnimation animInst(configNode, modelRoot);
437 animInst.apply(node);
438 } else if (type == "scale") {
439 SGScaleAnimation animInst(configNode, modelRoot);
440 animInst.apply(node);
441 } else if (type == "select") {
442 SGSelectAnimation animInst(configNode, modelRoot);
443 animInst.apply(node);
444 } else if (type == "shader") {
445 SGShaderAnimation animInst(configNode, modelRoot, options);
446 animInst.apply(node);
447 } else if (type == "textranslate" || type == "texrotate" ||
448 type == "texmultiple") {
449 SGTexTransformAnimation animInst(configNode, modelRoot);
450 animInst.apply(node);
451 } else if (type == "timed") {
452 SGTimedAnimation animInst(configNode, modelRoot);
453 animInst.apply(node);
454 } else if (type == "translate") {
455 SGTranslateAnimation animInst(configNode, modelRoot);
456 animInst.apply(node);
457 } else if (type == "null" || type == "none" || type.empty()) {
458 SGGroupAnimation animInst(configNode, modelRoot);
459 animInst.apply(node);
468 SGAnimation::apply(osg::Node* node)
470 // duh what a special case ...
471 if (_objectNames.empty()) {
472 osg::Group* group = node->asGroup();
474 osg::ref_ptr<osg::Group> animationGroup;
475 installInGroup(std::string(), *group, animationGroup);
482 SGAnimation::install(osg::Node& node)
486 node.setNodeMask( SG_NODEMASK_TERRAIN_BIT | node.getNodeMask());
488 node.setNodeMask(~SG_NODEMASK_TERRAIN_BIT & node.getNodeMask());
490 node.setNodeMask( SG_NODEMASK_CASTSHADOW_BIT | node.getNodeMask());
492 node.setNodeMask(~SG_NODEMASK_CASTSHADOW_BIT & node.getNodeMask());
496 SGAnimation::createAnimationGroup(osg::Group& parent)
498 // default implementation, we do not need a new group
499 // for every animation type. Usually animations that just change
500 // the StateSet of some parts of the model
505 SGAnimation::apply(osg::Group& group)
507 // the trick is to first traverse the children and then
508 // possibly splice in a new group node if required.
509 // Else we end up in a recursive loop where we infinitly insert new
513 // Note that this algorithm preserves the order of the child objects
514 // like they appear in the object-name tags.
515 // The timed animations require this
516 osg::ref_ptr<osg::Group> animationGroup;
517 std::list<std::string>::const_iterator nameIt;
518 for (nameIt = _objectNames.begin(); nameIt != _objectNames.end(); ++nameIt)
519 installInGroup(*nameIt, group, animationGroup);
523 SGAnimation::installInGroup(const std::string& name, osg::Group& group,
524 osg::ref_ptr<osg::Group>& animationGroup)
526 int i = group.getNumChildren() - 1;
527 for (; 0 <= i; --i) {
528 osg::Node* child = group.getChild(i);
530 // Check if this one is already processed
531 if (std::find(_installedAnimations.begin(),
532 _installedAnimations.end(), child)
533 != _installedAnimations.end())
536 if (name.empty() || child->getName() == name) {
537 // fire the installation of the animation
540 // create a group node on demand
541 if (!animationGroup.valid()) {
542 animationGroup = createAnimationGroup(group);
543 // Animation type that does not require a new group,
544 // in this case we can stop and look for the next object
545 if (animationGroup.valid() && !_name.empty())
546 animationGroup->setName(_name);
548 if (animationGroup.valid()) {
549 animationGroup->addChild(child);
550 group.removeChild(i);
553 // store that we already have processed this child node
554 // We can hit this one twice if an animation references some
555 // part of a subtree twice
556 _installedAnimations.push_back(child);
562 SGAnimation::removeMode(osg::Node& node, osg::StateAttribute::GLMode mode)
564 RemoveModeVisitor visitor(mode);
565 node.accept(visitor);
569 SGAnimation::removeAttribute(osg::Node& node, osg::StateAttribute::Type type)
571 RemoveAttributeVisitor visitor(type);
572 node.accept(visitor);
576 SGAnimation::removeTextureMode(osg::Node& node, unsigned unit,
577 osg::StateAttribute::GLMode mode)
579 RemoveTextureModeVisitor visitor(unit, mode);
580 node.accept(visitor);
584 SGAnimation::removeTextureAttribute(osg::Node& node, unsigned unit,
585 osg::StateAttribute::Type type)
587 RemoveTextureAttributeVisitor visitor(unit, type);
588 node.accept(visitor);
592 SGAnimation::setRenderBinToInherit(osg::Node& node)
594 BinToInheritVisitor visitor;
595 node.accept(visitor);
599 SGAnimation::cloneDrawables(osg::Node& node)
601 DrawableCloneVisitor visitor;
602 node.accept(visitor);
606 SGAnimation::getCondition() const
608 const SGPropertyNode* conditionNode = _configNode->getChild("condition");
611 return sgReadCondition(_modelRoot, conditionNode);
616 ////////////////////////////////////////////////////////////////////////
617 // Implementation of null animation
618 ////////////////////////////////////////////////////////////////////////
620 // Ok, that is to build a subgraph from different other
621 // graph nodes. I guess that this stems from the time where modellers
622 // could not build hierarchical trees ...
623 SGGroupAnimation::SGGroupAnimation(const SGPropertyNode* configNode,
624 SGPropertyNode* modelRoot):
625 SGAnimation(configNode, modelRoot)
630 SGGroupAnimation::createAnimationGroup(osg::Group& parent)
632 osg::Group* group = new osg::Group;
633 parent.addChild(group);
638 ////////////////////////////////////////////////////////////////////////
639 // Implementation of translate animation
640 ////////////////////////////////////////////////////////////////////////
642 class SGTranslateAnimation::UpdateCallback : public osg::NodeCallback {
644 UpdateCallback(SGCondition const* condition,
645 SGExpressiond const* animationValue) :
646 _condition(condition),
647 _animationValue(animationValue)
649 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
651 if (!_condition || _condition->test()) {
652 SGTranslateTransform* transform;
653 transform = static_cast<SGTranslateTransform*>(node);
654 transform->setValue(_animationValue->getValue());
659 SGSharedPtr<SGCondition const> _condition;
660 SGSharedPtr<SGExpressiond const> _animationValue;
663 SGTranslateAnimation::SGTranslateAnimation(const SGPropertyNode* configNode,
664 SGPropertyNode* modelRoot) :
665 SGAnimation(configNode, modelRoot)
667 _condition = getCondition();
668 SGSharedPtr<SGExpressiond> value;
669 value = read_value(configNode, modelRoot, "-m",
670 -SGLimitsd::max(), SGLimitsd::max());
671 _animationValue = value->simplify();
673 _initialValue = _animationValue->getValue();
677 if (configNode->hasValue("axis/x1-m")) {
679 v1[0] = configNode->getDoubleValue("axis/x1-m", 0);
680 v1[1] = configNode->getDoubleValue("axis/y1-m", 0);
681 v1[2] = configNode->getDoubleValue("axis/z1-m", 0);
682 v2[0] = configNode->getDoubleValue("axis/x2-m", 0);
683 v2[1] = configNode->getDoubleValue("axis/y2-m", 0);
684 v2[2] = configNode->getDoubleValue("axis/z2-m", 0);
687 _axis[0] = configNode->getDoubleValue("axis/x", 0);
688 _axis[1] = configNode->getDoubleValue("axis/y", 0);
689 _axis[2] = configNode->getDoubleValue("axis/z", 0);
691 if (8*SGLimitsd::min() < norm(_axis))
692 _axis = normalize(_axis);
696 SGTranslateAnimation::createAnimationGroup(osg::Group& parent)
698 SGTranslateTransform* transform = new SGTranslateTransform;
699 transform->setName("translate animation");
700 if (_animationValue && !_animationValue->isConst()) {
701 UpdateCallback* uc = new UpdateCallback(_condition, _animationValue);
702 transform->setUpdateCallback(uc);
704 transform->setAxis(_axis);
705 transform->setValue(_initialValue);
706 parent.addChild(transform);
711 ////////////////////////////////////////////////////////////////////////
712 // Implementation of rotate/spin animation
713 ////////////////////////////////////////////////////////////////////////
715 class SGRotateAnimation::UpdateCallback : public osg::NodeCallback {
717 UpdateCallback(SGCondition const* condition,
718 SGExpressiond const* animationValue) :
719 _condition(condition),
720 _animationValue(animationValue)
722 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
724 if (!_condition || _condition->test()) {
725 SGRotateTransform* transform;
726 transform = static_cast<SGRotateTransform*>(node);
727 transform->setAngleDeg(_animationValue->getValue());
732 SGSharedPtr<SGCondition const> _condition;
733 SGSharedPtr<SGExpressiond const> _animationValue;
736 class SGRotateAnimation::SpinUpdateCallback : public osg::NodeCallback {
738 SpinUpdateCallback(SGCondition const* condition,
739 SGExpressiond const* animationValue) :
740 _condition(condition),
741 _animationValue(animationValue),
744 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
746 if (!_condition || _condition->test()) {
747 SGRotateTransform* transform;
748 transform = static_cast<SGRotateTransform*>(node);
750 double t = nv->getFrameStamp()->getReferenceTime();
755 double velocity_rpms = _animationValue->getValue()/60;
756 double angle = transform->getAngleDeg();
757 angle += dt*velocity_rpms*360;
758 angle -= 360*floor(angle/360);
759 transform->setAngleDeg(angle);
764 SGSharedPtr<SGCondition const> _condition;
765 SGSharedPtr<SGExpressiond const> _animationValue;
769 SGRotateAnimation::SGRotateAnimation(const SGPropertyNode* configNode,
770 SGPropertyNode* modelRoot) :
771 SGAnimation(configNode, modelRoot)
773 std::string type = configNode->getStringValue("type", "");
774 _isSpin = (type == "spin");
776 _condition = getCondition();
777 SGSharedPtr<SGExpressiond> value;
778 value = read_value(configNode, modelRoot, "-deg",
779 -SGLimitsd::max(), SGLimitsd::max());
780 _animationValue = value->simplify();
782 _initialValue = _animationValue->getValue();
786 _center = SGVec3d::zeros();
787 if (configNode->hasValue("axis/x1-m")) {
789 v1[0] = configNode->getDoubleValue("axis/x1-m", 0);
790 v1[1] = configNode->getDoubleValue("axis/y1-m", 0);
791 v1[2] = configNode->getDoubleValue("axis/z1-m", 0);
792 v2[0] = configNode->getDoubleValue("axis/x2-m", 0);
793 v2[1] = configNode->getDoubleValue("axis/y2-m", 0);
794 v2[2] = configNode->getDoubleValue("axis/z2-m", 0);
795 _center = 0.5*(v1+v2);
798 _axis[0] = configNode->getDoubleValue("axis/x", 0);
799 _axis[1] = configNode->getDoubleValue("axis/y", 0);
800 _axis[2] = configNode->getDoubleValue("axis/z", 0);
802 if (8*SGLimitsd::min() < norm(_axis))
803 _axis = normalize(_axis);
805 _center[0] = configNode->getDoubleValue("center/x-m", _center[0]);
806 _center[1] = configNode->getDoubleValue("center/y-m", _center[1]);
807 _center[2] = configNode->getDoubleValue("center/z-m", _center[2]);
811 SGRotateAnimation::createAnimationGroup(osg::Group& parent)
813 SGRotateTransform* transform = new SGRotateTransform;
814 transform->setName("rotate animation");
816 SpinUpdateCallback* uc;
817 uc = new SpinUpdateCallback(_condition, _animationValue);
818 transform->setUpdateCallback(uc);
819 } else if (_animationValue || !_animationValue->isConst()) {
820 UpdateCallback* uc = new UpdateCallback(_condition, _animationValue);
821 transform->setUpdateCallback(uc);
823 transform->setCenter(_center);
824 transform->setAxis(_axis);
825 transform->setAngleDeg(_initialValue);
826 parent.addChild(transform);
831 ////////////////////////////////////////////////////////////////////////
832 // Implementation of scale animation
833 ////////////////////////////////////////////////////////////////////////
835 class SGScaleAnimation::UpdateCallback : public osg::NodeCallback {
837 UpdateCallback(const SGCondition* condition,
838 SGSharedPtr<const SGExpressiond> animationValue[3]) :
839 _condition(condition)
841 _animationValue[0] = animationValue[0];
842 _animationValue[1] = animationValue[1];
843 _animationValue[2] = animationValue[2];
845 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
847 if (!_condition || _condition->test()) {
848 SGScaleTransform* transform;
849 transform = static_cast<SGScaleTransform*>(node);
850 SGVec3d scale(_animationValue[0]->getValue(),
851 _animationValue[1]->getValue(),
852 _animationValue[2]->getValue());
853 transform->setScaleFactor(scale);
858 SGSharedPtr<SGCondition const> _condition;
859 SGSharedPtr<SGExpressiond const> _animationValue[3];
862 SGScaleAnimation::SGScaleAnimation(const SGPropertyNode* configNode,
863 SGPropertyNode* modelRoot) :
864 SGAnimation(configNode, modelRoot)
866 _condition = getCondition();
868 // default offset/factor for all directions
869 double offset = configNode->getDoubleValue("offset", 0);
870 double factor = configNode->getDoubleValue("factor", 1);
872 SGSharedPtr<SGExpressiond> inPropExpr;
874 std::string inputPropertyName;
875 inputPropertyName = configNode->getStringValue("property", "");
876 if (inputPropertyName.empty()) {
877 inPropExpr = new SGConstExpression<double>(0);
879 SGPropertyNode* inputProperty;
880 inputProperty = modelRoot->getNode(inputPropertyName, true);
881 inPropExpr = new SGPropertyExpression<double>(inputProperty);
884 SGInterpTable* interpTable = read_interpolation_table(configNode);
886 SGSharedPtr<SGExpressiond> value;
887 value = new SGInterpTableExpression<double>(inPropExpr, interpTable);
888 _animationValue[0] = value->simplify();
889 _animationValue[1] = value->simplify();
890 _animationValue[2] = value->simplify();
891 } else if (configNode->getBoolValue("use-personality", false)) {
892 SGSharedPtr<SGExpressiond> value;
893 value = new SGPersonalityScaleOffsetExpression(inPropExpr, configNode,
894 "x-factor", "x-offset",
896 double minClip = configNode->getDoubleValue("x-min", 0);
897 double maxClip = configNode->getDoubleValue("x-max", SGLimitsd::max());
898 value = new SGClipExpression<double>(value, minClip, maxClip);
899 _animationValue[0] = value->simplify();
901 value = new SGPersonalityScaleOffsetExpression(inPropExpr, configNode,
902 "y-factor", "y-offset",
904 minClip = configNode->getDoubleValue("y-min", 0);
905 maxClip = configNode->getDoubleValue("y-max", SGLimitsd::max());
906 value = new SGClipExpression<double>(value, minClip, maxClip);
907 _animationValue[1] = value->simplify();
909 value = new SGPersonalityScaleOffsetExpression(inPropExpr, configNode,
910 "z-factor", "z-offset",
912 minClip = configNode->getDoubleValue("z-min", 0);
913 maxClip = configNode->getDoubleValue("z-max", SGLimitsd::max());
914 value = new SGClipExpression<double>(value, minClip, maxClip);
915 _animationValue[2] = value->simplify();
917 SGSharedPtr<SGExpressiond> value;
918 value = read_factor_offset(configNode, inPropExpr, "x-factor", "x-offset");
919 double minClip = configNode->getDoubleValue("x-min", 0);
920 double maxClip = configNode->getDoubleValue("x-max", SGLimitsd::max());
921 value = new SGClipExpression<double>(value, minClip, maxClip);
922 _animationValue[0] = value->simplify();
924 value = read_factor_offset(configNode, inPropExpr, "y-factor", "y-offset");
925 minClip = configNode->getDoubleValue("y-min", 0);
926 maxClip = configNode->getDoubleValue("y-max", SGLimitsd::max());
927 value = new SGClipExpression<double>(value, minClip, maxClip);
928 _animationValue[1] = value->simplify();
930 value = read_factor_offset(configNode, inPropExpr, "z-factor", "z-offset");
931 minClip = configNode->getDoubleValue("z-min", 0);
932 maxClip = configNode->getDoubleValue("z-max", SGLimitsd::max());
933 value = new SGClipExpression<double>(value, minClip, maxClip);
934 _animationValue[2] = value->simplify();
936 _initialValue[0] = configNode->getDoubleValue("x-starting-scale", 1);
937 _initialValue[0] *= configNode->getDoubleValue("x-factor", factor);
938 _initialValue[0] += configNode->getDoubleValue("x-offset", offset);
939 _initialValue[1] = configNode->getDoubleValue("y-starting-scale", 1);
940 _initialValue[1] *= configNode->getDoubleValue("y-factor", factor);
941 _initialValue[1] += configNode->getDoubleValue("y-offset", offset);
942 _initialValue[2] = configNode->getDoubleValue("z-starting-scale", 1);
943 _initialValue[2] *= configNode->getDoubleValue("z-factor", factor);
944 _initialValue[2] += configNode->getDoubleValue("z-offset", offset);
945 _center[0] = configNode->getDoubleValue("center/x-m", 0);
946 _center[1] = configNode->getDoubleValue("center/y-m", 0);
947 _center[2] = configNode->getDoubleValue("center/z-m", 0);
951 SGScaleAnimation::createAnimationGroup(osg::Group& parent)
953 SGScaleTransform* transform = new SGScaleTransform;
954 transform->setName("scale animation");
955 transform->setCenter(_center);
956 transform->setScaleFactor(_initialValue);
957 UpdateCallback* uc = new UpdateCallback(_condition, _animationValue);
958 transform->setUpdateCallback(uc);
959 parent.addChild(transform);
964 // Don't create a new state state everytime we need GL_NORMALIZE!
968 Mutex normalizeMutex;
970 osg::StateSet* getNormalizeStateSet()
972 static osg::ref_ptr<osg::StateSet> normalizeStateSet;
973 ScopedLock<Mutex> lock(normalizeMutex);
974 if (!normalizeStateSet.valid()) {
975 normalizeStateSet = new osg::StateSet;
976 normalizeStateSet->setMode(GL_NORMALIZE, osg::StateAttribute::ON);
977 normalizeStateSet->setDataVariance(osg::Object::STATIC);
979 return normalizeStateSet.get();
983 ////////////////////////////////////////////////////////////////////////
984 // Implementation of dist scale animation
985 ////////////////////////////////////////////////////////////////////////
987 class SGDistScaleAnimation::Transform : public osg::Transform {
989 Transform() : _min_v(0.0), _max_v(0.0), _factor(0.0), _offset(0.0) {}
990 Transform(const Transform& rhs,
991 const osg::CopyOp& copyOp = osg::CopyOp::SHALLOW_COPY)
992 : osg::Transform(rhs, copyOp), _table(rhs._table), _center(rhs._center),
993 _min_v(rhs._min_v), _max_v(rhs._max_v), _factor(rhs._factor),
997 META_Node(simgear, SGDistScaleAnimation::Transform);
998 Transform(const SGPropertyNode* configNode)
1000 setName(configNode->getStringValue("name", "dist scale animation"));
1001 setReferenceFrame(RELATIVE_RF);
1002 setStateSet(getNormalizeStateSet());
1003 _factor = configNode->getFloatValue("factor", 1);
1004 _offset = configNode->getFloatValue("offset", 0);
1005 _min_v = configNode->getFloatValue("min", SGLimitsf::epsilon());
1006 _max_v = configNode->getFloatValue("max", SGLimitsf::max());
1007 _table = read_interpolation_table(configNode);
1008 _center[0] = configNode->getFloatValue("center/x-m", 0);
1009 _center[1] = configNode->getFloatValue("center/y-m", 0);
1010 _center[2] = configNode->getFloatValue("center/z-m", 0);
1012 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
1013 osg::NodeVisitor* nv) const
1015 osg::Matrix transform;
1016 double scale_factor = computeScaleFactor(nv);
1017 transform(0,0) = scale_factor;
1018 transform(1,1) = scale_factor;
1019 transform(2,2) = scale_factor;
1020 transform(3,0) = _center[0]*(1 - scale_factor);
1021 transform(3,1) = _center[1]*(1 - scale_factor);
1022 transform(3,2) = _center[2]*(1 - scale_factor);
1023 matrix.preMult(transform);
1027 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
1028 osg::NodeVisitor* nv) const
1030 double scale_factor = computeScaleFactor(nv);
1031 if (fabs(scale_factor) <= SGLimits<double>::min())
1033 osg::Matrix transform;
1034 double rScaleFactor = 1/scale_factor;
1035 transform(0,0) = rScaleFactor;
1036 transform(1,1) = rScaleFactor;
1037 transform(2,2) = rScaleFactor;
1038 transform(3,0) = _center[0]*(1 - rScaleFactor);
1039 transform(3,1) = _center[1]*(1 - rScaleFactor);
1040 transform(3,2) = _center[2]*(1 - rScaleFactor);
1041 matrix.postMult(transform);
1045 static bool writeLocalData(const osg::Object& obj, osgDB::Output& fw)
1047 const Transform& trans = static_cast<const Transform&>(obj);
1048 fw.indent() << "center " << trans._center << "\n";
1049 fw.indent() << "min_v " << trans._min_v << "\n";
1050 fw.indent() << "max_v " << trans._max_v << "\n";
1051 fw.indent() << "factor " << trans._factor << "\n";
1052 fw.indent() << "offset " << trans._offset << "\n";
1056 double computeScaleFactor(osg::NodeVisitor* nv) const
1061 double scale_factor = (toOsg(_center) - nv->getEyePoint()).length();
1063 scale_factor = _factor * scale_factor + _offset;
1065 scale_factor = _table->interpolate( scale_factor );
1067 if (scale_factor < _min_v)
1068 scale_factor = _min_v;
1069 if (scale_factor > _max_v)
1070 scale_factor = _max_v;
1072 return scale_factor;
1075 SGSharedPtr<SGInterpTable> _table;
1084 SGDistScaleAnimation::SGDistScaleAnimation(const SGPropertyNode* configNode,
1085 SGPropertyNode* modelRoot) :
1086 SGAnimation(configNode, modelRoot)
1091 SGDistScaleAnimation::createAnimationGroup(osg::Group& parent)
1093 Transform* transform = new Transform(getConfig());
1094 parent.addChild(transform);
1100 osgDB::RegisterDotOsgWrapperProxy distScaleAnimationTransformProxy
1102 new SGDistScaleAnimation::Transform,
1103 "SGDistScaleAnimation::Transform",
1104 "Object Node Transform SGDistScaleAnimation::Transform Group",
1106 &SGDistScaleAnimation::Transform::writeLocalData
1110 ////////////////////////////////////////////////////////////////////////
1111 // Implementation of flash animation
1112 ////////////////////////////////////////////////////////////////////////
1114 class SGFlashAnimation::Transform : public osg::Transform {
1116 Transform() : _power(0.0), _factor(0.0), _offset(0.0), _min_v(0.0),
1117 _max_v(0.0), _two_sides(false)
1120 Transform(const Transform& rhs,
1121 const osg::CopyOp& copyOp = osg::CopyOp::SHALLOW_COPY)
1122 : osg::Transform(rhs, copyOp), _center(rhs._center), _axis(rhs._axis),
1123 _power(rhs._power), _factor(rhs._factor), _offset(rhs._offset),
1124 _min_v(rhs._min_v), _max_v(rhs._max_v), _two_sides(rhs._two_sides)
1127 META_Node(simgear, SGFlashAnimation::Transform);
1129 Transform(const SGPropertyNode* configNode)
1131 setReferenceFrame(RELATIVE_RF);
1132 setName(configNode->getStringValue("name", "flash animation"));
1133 setStateSet(getNormalizeStateSet());
1135 _axis[0] = configNode->getFloatValue("axis/x", 0);
1136 _axis[1] = configNode->getFloatValue("axis/y", 0);
1137 _axis[2] = configNode->getFloatValue("axis/z", 1);
1140 _center[0] = configNode->getFloatValue("center/x-m", 0);
1141 _center[1] = configNode->getFloatValue("center/y-m", 0);
1142 _center[2] = configNode->getFloatValue("center/z-m", 0);
1144 _offset = configNode->getFloatValue("offset", 0);
1145 _factor = configNode->getFloatValue("factor", 1);
1146 _power = configNode->getFloatValue("power", 1);
1147 _two_sides = configNode->getBoolValue("two-sides", false);
1149 _min_v = configNode->getFloatValue("min", SGLimitsf::epsilon());
1150 _max_v = configNode->getFloatValue("max", 1);
1152 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
1153 osg::NodeVisitor* nv) const
1155 osg::Matrix transform;
1156 double scale_factor = computeScaleFactor(nv);
1157 transform(0,0) = scale_factor;
1158 transform(1,1) = scale_factor;
1159 transform(2,2) = scale_factor;
1160 transform(3,0) = _center[0]*(1 - scale_factor);
1161 transform(3,1) = _center[1]*(1 - scale_factor);
1162 transform(3,2) = _center[2]*(1 - scale_factor);
1163 matrix.preMult(transform);
1167 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
1168 osg::NodeVisitor* nv) const
1170 double scale_factor = computeScaleFactor(nv);
1171 if (fabs(scale_factor) <= SGLimits<double>::min())
1173 osg::Matrix transform;
1174 double rScaleFactor = 1/scale_factor;
1175 transform(0,0) = rScaleFactor;
1176 transform(1,1) = rScaleFactor;
1177 transform(2,2) = rScaleFactor;
1178 transform(3,0) = _center[0]*(1 - rScaleFactor);
1179 transform(3,1) = _center[1]*(1 - rScaleFactor);
1180 transform(3,2) = _center[2]*(1 - rScaleFactor);
1181 matrix.postMult(transform);
1185 static bool writeLocalData(const osg::Object& obj, osgDB::Output& fw)
1187 const Transform& trans = static_cast<const Transform&>(obj);
1188 fw.indent() << "center " << trans._center[0] << " "
1189 << trans._center[1] << " " << trans._center[2] << " " << "\n";
1190 fw.indent() << "axis " << trans._axis[0] << " "
1191 << trans._axis[1] << " " << trans._axis[2] << " " << "\n";
1192 fw.indent() << "power " << trans._power << " \n";
1193 fw.indent() << "min_v " << trans._min_v << "\n";
1194 fw.indent() << "max_v " << trans._max_v << "\n";
1195 fw.indent() << "factor " << trans._factor << "\n";
1196 fw.indent() << "offset " << trans._offset << "\n";
1197 fw.indent() << "twosides " << (trans._two_sides ? "true" : "false") << "\n";
1201 double computeScaleFactor(osg::NodeVisitor* nv) const
1206 osg::Vec3 localEyeToCenter = nv->getEyePoint() - _center;
1207 localEyeToCenter.normalize();
1209 double cos_angle = localEyeToCenter*_axis;
1210 double scale_factor = 0;
1211 if ( _two_sides && cos_angle < 0 )
1212 scale_factor = _factor * pow( -cos_angle, _power ) + _offset;
1213 else if ( cos_angle > 0 )
1214 scale_factor = _factor * pow( cos_angle, _power ) + _offset;
1216 if ( scale_factor < _min_v )
1217 scale_factor = _min_v;
1218 if ( scale_factor > _max_v )
1219 scale_factor = _max_v;
1221 return scale_factor;
1224 virtual osg::BoundingSphere computeBound() const
1226 // avoid being culled away by small feature culling
1227 osg::BoundingSphere bs = osg::Group::computeBound();
1228 bs.radius() *= _max_v;
1235 double _power, _factor, _offset, _min_v, _max_v;
1240 SGFlashAnimation::SGFlashAnimation(const SGPropertyNode* configNode,
1241 SGPropertyNode* modelRoot) :
1242 SGAnimation(configNode, modelRoot)
1247 SGFlashAnimation::createAnimationGroup(osg::Group& parent)
1249 Transform* transform = new Transform(getConfig());
1250 parent.addChild(transform);
1256 osgDB::RegisterDotOsgWrapperProxy flashAnimationTransformProxy
1258 new SGFlashAnimation::Transform,
1259 "SGFlashAnimation::Transform",
1260 "Object Node Transform SGFlashAnimation::Transform Group",
1262 &SGFlashAnimation::Transform::writeLocalData
1266 ////////////////////////////////////////////////////////////////////////
1267 // Implementation of billboard animation
1268 ////////////////////////////////////////////////////////////////////////
1270 class SGBillboardAnimation::Transform : public osg::Transform {
1272 Transform() : _spherical(true) {}
1273 Transform(const Transform& rhs,
1274 const osg::CopyOp& copyOp = osg::CopyOp::SHALLOW_COPY)
1275 : osg::Transform(rhs, copyOp), _spherical(rhs._spherical) {}
1276 META_Node(simgear, SGBillboardAnimation::Transform);
1277 Transform(const SGPropertyNode* configNode) :
1278 _spherical(configNode->getBoolValue("spherical", true))
1280 setReferenceFrame(RELATIVE_RF);
1281 setName(configNode->getStringValue("name", "billboard animation"));
1283 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
1284 osg::NodeVisitor* nv) const
1286 // More or less taken from plibs ssgCutout
1288 matrix(0,0) = 1; matrix(0,1) = 0; matrix(0,2) = 0;
1289 matrix(1,0) = 0; matrix(1,1) = 0; matrix(1,2) = -1;
1290 matrix(2,0) = 0; matrix(2,1) = 1; matrix(2,2) = 0;
1292 osg::Vec3 zAxis(matrix(2, 0), matrix(2, 1), matrix(2, 2));
1293 osg::Vec3 xAxis = osg::Vec3(0, 0, -1)^zAxis;
1294 osg::Vec3 yAxis = zAxis^xAxis;
1300 matrix(0,0) = xAxis[0]; matrix(0,1) = xAxis[1]; matrix(0,2) = xAxis[2];
1301 matrix(1,0) = yAxis[0]; matrix(1,1) = yAxis[1]; matrix(1,2) = yAxis[2];
1302 matrix(2,0) = zAxis[0]; matrix(2,1) = zAxis[1]; matrix(2,2) = zAxis[2];
1307 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
1308 osg::NodeVisitor* nv) const
1310 // Hmm, don't yet know how to get that back ...
1313 static bool writeLocalData(const osg::Object& obj, osgDB::Output& fw)
1315 const Transform& trans = static_cast<const Transform&>(obj);
1317 fw.indent() << (trans._spherical ? "true" : "false") << "\n";
1325 SGBillboardAnimation::SGBillboardAnimation(const SGPropertyNode* configNode,
1326 SGPropertyNode* modelRoot) :
1327 SGAnimation(configNode, modelRoot)
1332 SGBillboardAnimation::createAnimationGroup(osg::Group& parent)
1334 Transform* transform = new Transform(getConfig());
1335 parent.addChild(transform);
1341 osgDB::RegisterDotOsgWrapperProxy billboardAnimationTransformProxy
1343 new SGBillboardAnimation::Transform,
1344 "SGBillboardAnimation::Transform",
1345 "Object Node Transform SGBillboardAnimation::Transform Group",
1347 &SGBillboardAnimation::Transform::writeLocalData
1351 ////////////////////////////////////////////////////////////////////////
1352 // Implementation of a range animation
1353 ////////////////////////////////////////////////////////////////////////
1355 class SGRangeAnimation::UpdateCallback : public osg::NodeCallback {
1357 UpdateCallback(const SGCondition* condition,
1358 const SGExpressiond* minAnimationValue,
1359 const SGExpressiond* maxAnimationValue,
1360 double minValue, double maxValue) :
1361 _condition(condition),
1362 _minAnimationValue(minAnimationValue),
1363 _maxAnimationValue(maxAnimationValue),
1364 _minStaticValue(minValue),
1365 _maxStaticValue(maxValue)
1367 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1369 osg::LOD* lod = static_cast<osg::LOD*>(node);
1370 if (!_condition || _condition->test()) {
1372 if (_minAnimationValue)
1373 minRange = _minAnimationValue->getValue();
1375 minRange = _minStaticValue;
1377 if (_maxAnimationValue)
1378 maxRange = _maxAnimationValue->getValue();
1380 maxRange = _maxStaticValue;
1381 lod->setRange(0, minRange, maxRange);
1383 lod->setRange(0, 0, SGLimitsf::max());
1389 SGSharedPtr<const SGCondition> _condition;
1390 SGSharedPtr<const SGExpressiond> _minAnimationValue;
1391 SGSharedPtr<const SGExpressiond> _maxAnimationValue;
1392 double _minStaticValue;
1393 double _maxStaticValue;
1396 SGRangeAnimation::SGRangeAnimation(const SGPropertyNode* configNode,
1397 SGPropertyNode* modelRoot) :
1398 SGAnimation(configNode, modelRoot)
1400 _condition = getCondition();
1402 std::string inputPropertyName;
1403 inputPropertyName = configNode->getStringValue("min-property", "");
1404 if (!inputPropertyName.empty()) {
1405 SGPropertyNode* inputProperty;
1406 inputProperty = modelRoot->getNode(inputPropertyName, true);
1407 SGSharedPtr<SGExpressiond> value;
1408 value = new SGPropertyExpression<double>(inputProperty);
1410 value = read_factor_offset(configNode, value, "min-factor", "min-offset");
1411 _minAnimationValue = value->simplify();
1413 inputPropertyName = configNode->getStringValue("max-property", "");
1414 if (!inputPropertyName.empty()) {
1415 SGPropertyNode* inputProperty;
1416 inputProperty = modelRoot->getNode(inputPropertyName.c_str(), true);
1418 SGSharedPtr<SGExpressiond> value;
1419 value = new SGPropertyExpression<double>(inputProperty);
1421 value = read_factor_offset(configNode, value, "max-factor", "max-offset");
1422 _maxAnimationValue = value->simplify();
1425 _initialValue[0] = configNode->getDoubleValue("min-m", 0);
1426 _initialValue[0] *= configNode->getDoubleValue("min-factor", 1);
1427 _initialValue[1] = configNode->getDoubleValue("max-m", SGLimitsf::max());
1428 _initialValue[1] *= configNode->getDoubleValue("max-factor", 1);
1432 SGRangeAnimation::createAnimationGroup(osg::Group& parent)
1434 osg::Group* group = new osg::Group;
1435 group->setName("range animation group");
1437 osg::LOD* lod = new osg::LOD;
1438 lod->setName("range animation node");
1439 parent.addChild(lod);
1441 lod->addChild(group, _initialValue[0], _initialValue[1]);
1442 lod->setCenterMode(osg::LOD::USE_BOUNDING_SPHERE_CENTER);
1443 lod->setRangeMode(osg::LOD::DISTANCE_FROM_EYE_POINT);
1444 if (_minAnimationValue || _maxAnimationValue || _condition) {
1446 uc = new UpdateCallback(_condition, _minAnimationValue, _maxAnimationValue,
1447 _initialValue[0], _initialValue[1]);
1448 lod->setUpdateCallback(uc);
1454 ////////////////////////////////////////////////////////////////////////
1455 // Implementation of a select animation
1456 ////////////////////////////////////////////////////////////////////////
1458 SGSelectAnimation::SGSelectAnimation(const SGPropertyNode* configNode,
1459 SGPropertyNode* modelRoot) :
1460 SGAnimation(configNode, modelRoot)
1465 SGSelectAnimation::createAnimationGroup(osg::Group& parent)
1467 // if no condition given, this is a noop.
1468 SGSharedPtr<SGCondition const> condition = getCondition();
1469 // trick, gets deleted with all its 'animated' children
1470 // when the animation installer returns
1472 return new osg::Group;
1473 simgear::ConditionNode* cn = new simgear::ConditionNode;
1474 cn->setName("select animation node");
1475 cn->setCondition(condition.ptr());
1476 osg::Group* grp = new osg::Group;
1478 parent.addChild(cn);
1484 ////////////////////////////////////////////////////////////////////////
1485 // Implementation of alpha test animation
1486 ////////////////////////////////////////////////////////////////////////
1488 SGAlphaTestAnimation::SGAlphaTestAnimation(const SGPropertyNode* configNode,
1489 SGPropertyNode* modelRoot) :
1490 SGAnimation(configNode, modelRoot)
1496 // Keep one copy of the most common alpha test its state set.
1497 ReentrantMutex alphaTestMutex;
1498 osg::ref_ptr<osg::AlphaFunc> standardAlphaFunc;
1499 osg::ref_ptr<osg::StateSet> alphaFuncStateSet;
1501 osg::AlphaFunc* makeAlphaFunc(float clamp)
1503 ScopedLock<ReentrantMutex> lock(alphaTestMutex);
1504 if (osg::equivalent(clamp, 0.01f)) {
1505 if (standardAlphaFunc.valid())
1506 return standardAlphaFunc.get();
1509 osg::AlphaFunc* alphaFunc = new osg::AlphaFunc;
1510 alphaFunc->setFunction(osg::AlphaFunc::GREATER);
1511 alphaFunc->setReferenceValue(clamp);
1512 alphaFunc->setDataVariance(osg::Object::STATIC);
1513 if (osg::equivalent(clamp, 0.01f))
1514 standardAlphaFunc = alphaFunc;
1518 osg::StateSet* makeAlphaTestStateSet(float clamp)
1520 using namespace OpenThreads;
1521 ScopedLock<ReentrantMutex> lock(alphaTestMutex);
1522 if (osg::equivalent(clamp, 0.01f)) {
1523 if (alphaFuncStateSet.valid())
1524 return alphaFuncStateSet.get();
1526 osg::AlphaFunc* alphaFunc = makeAlphaFunc(clamp);
1527 osg::StateSet* stateSet = new osg::StateSet;
1528 stateSet->setAttributeAndModes(alphaFunc,
1529 (osg::StateAttribute::ON
1530 | osg::StateAttribute::OVERRIDE));
1531 stateSet->setDataVariance(osg::Object::STATIC);
1532 if (osg::equivalent(clamp, 0.01f))
1533 alphaFuncStateSet = stateSet;
1538 SGAlphaTestAnimation::install(osg::Node& node)
1540 SGAnimation::install(node);
1542 float alphaClamp = getConfig()->getFloatValue("alpha-factor", 0);
1543 osg::StateSet* stateSet = node.getStateSet();
1545 node.setStateSet(makeAlphaTestStateSet(alphaClamp));
1547 stateSet->setAttributeAndModes(makeAlphaFunc(alphaClamp),
1548 (osg::StateAttribute::ON
1549 | osg::StateAttribute::OVERRIDE));
1554 //////////////////////////////////////////////////////////////////////
1555 // Blend animation installer
1556 //////////////////////////////////////////////////////////////////////
1558 // XXX This needs to be replaced by something using TexEnvCombine to
1559 // change the blend factor. Changing the alpha values in the geometry
1561 class SGBlendAnimation::BlendVisitor : public osg::NodeVisitor {
1563 BlendVisitor(float blend) :
1564 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
1566 { setVisitorType(osg::NodeVisitor::NODE_VISITOR); }
1567 virtual void apply(osg::Node& node)
1569 updateStateSet(node.getStateSet());
1572 virtual void apply(osg::Geode& node)
1574 apply((osg::Node&)node);
1575 unsigned nDrawables = node.getNumDrawables();
1576 for (unsigned i = 0; i < nDrawables; ++i) {
1577 osg::Drawable* drawable = node.getDrawable(i);
1578 osg::Geometry* geometry = drawable->asGeometry();
1581 osg::Array* array = geometry->getColorArray();
1584 osg::Vec4Array* vec4Array = dynamic_cast<osg::Vec4Array*>(array);
1587 for (unsigned k = 0; k < vec4Array->size(); ++k) {
1588 (*vec4Array)[k][3] = _blend;
1591 updateStateSet(drawable->getStateSet());
1594 void updateStateSet(osg::StateSet* stateSet)
1598 osg::StateAttribute* stateAttribute;
1599 stateAttribute = stateSet->getAttribute(osg::StateAttribute::MATERIAL);
1600 if (!stateAttribute)
1602 osg::Material* material = dynamic_cast<osg::Material*>(stateAttribute);
1605 material->setAlpha(osg::Material::FRONT_AND_BACK, _blend);
1607 stateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
1608 stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
1610 stateSet->setRenderingHint(osg::StateSet::DEFAULT_BIN);
1617 class SGBlendAnimation::UpdateCallback : public osg::NodeCallback {
1619 UpdateCallback(const SGPropertyNode* configNode, const SGExpressiond* v) :
1623 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1625 double blend = _animationValue->getValue();
1626 if (blend != _prev_value) {
1627 _prev_value = blend;
1628 BlendVisitor visitor(1-blend);
1629 node->accept(visitor);
1635 SGSharedPtr<SGExpressiond const> _animationValue;
1639 SGBlendAnimation::SGBlendAnimation(const SGPropertyNode* configNode,
1640 SGPropertyNode* modelRoot)
1641 : SGAnimation(configNode, modelRoot),
1642 _animationValue(read_value(configNode, modelRoot, "", 0, 1))
1647 SGBlendAnimation::createAnimationGroup(osg::Group& parent)
1649 if (!_animationValue)
1652 osg::Group* group = new osg::Switch;
1653 group->setName("blend animation node");
1654 group->setUpdateCallback(new UpdateCallback(getConfig(), _animationValue));
1655 parent.addChild(group);
1660 SGBlendAnimation::install(osg::Node& node)
1662 SGAnimation::install(node);
1663 // make sure we do not change common geometries,
1664 // that also creates new display lists for these subgeometries.
1665 cloneDrawables(node);
1666 DoDrawArraysVisitor visitor;
1667 node.accept(visitor);
1671 //////////////////////////////////////////////////////////////////////
1672 // Timed animation installer
1673 //////////////////////////////////////////////////////////////////////
1677 class SGTimedAnimation::UpdateCallback : public osg::NodeCallback {
1679 UpdateCallback(const SGPropertyNode* configNode) :
1682 _duration_sec(configNode->getDoubleValue("duration-sec", 1)),
1683 _last_time_sec(SGLimitsd::max()),
1684 _use_personality(configNode->getBoolValue("use-personality", false))
1686 std::vector<SGSharedPtr<SGPropertyNode> > nodes;
1687 nodes = configNode->getChildren("branch-duration-sec");
1688 for (size_t i = 0; i < nodes.size(); ++i) {
1689 unsigned ind = nodes[ i ]->getIndex();
1690 while ( ind >= _durations.size() ) {
1691 _durations.push_back(DurationSpec(_duration_sec));
1693 SGPropertyNode_ptr rNode = nodes[i]->getChild("random");
1695 _durations[ind] = DurationSpec(nodes[ i ]->getDoubleValue());
1697 _durations[ind] = DurationSpec(rNode->getDoubleValue( "min", 0),
1698 rNode->getDoubleValue( "max", 1));
1702 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1704 assert(dynamic_cast<osg::Switch*>(node));
1705 osg::Switch* sw = static_cast<osg::Switch*>(node);
1707 unsigned nChildren = sw->getNumChildren();
1709 // blow up the durations vector to the required size
1710 while (_durations.size() < nChildren) {
1711 _durations.push_back(_duration_sec);
1713 // make sure the current index is an duration that really exists
1714 _current_index = _current_index % nChildren;
1716 // update the time and compute the current systems time value
1717 double t = nv->getFrameStamp()->getReferenceTime();
1718 if (_last_time_sec == SGLimitsd::max()) {
1721 double dt = t - _last_time_sec;
1722 if (_use_personality)
1723 dt *= 1 + 0.2*(0.5 - sg_random());
1728 double currentDuration = _durations[_current_index].get();
1729 while (currentDuration < _reminder) {
1730 _reminder -= currentDuration;
1731 _current_index = (_current_index + 1) % nChildren;
1732 currentDuration = _durations[_current_index].get();
1735 sw->setSingleChildOn(_current_index);
1741 struct DurationSpec {
1742 DurationSpec(double t) :
1743 minTime(SGMiscd::max(0.01, t)),
1744 maxTime(SGMiscd::max(0.01, t))
1746 DurationSpec(double t0, double t1) :
1747 minTime(SGMiscd::max(0.01, t0)),
1748 maxTime(SGMiscd::max(0.01, t1))
1751 { return minTime + sg_random()*(maxTime - minTime); }
1755 std::vector<DurationSpec> _durations;
1756 unsigned _current_index;
1758 double _duration_sec;
1759 double _last_time_sec;
1760 bool _use_personality;
1764 SGTimedAnimation::SGTimedAnimation(const SGPropertyNode* configNode,
1765 SGPropertyNode* modelRoot)
1766 : SGAnimation(configNode, modelRoot)
1771 SGTimedAnimation::createAnimationGroup(osg::Group& parent)
1773 osg::Switch* sw = new osg::Switch;
1774 sw->setName("timed animation node");
1775 sw->setUpdateCallback(new UpdateCallback(getConfig()));
1776 parent.addChild(sw);
1781 ////////////////////////////////////////////////////////////////////////
1782 // dynamically switch on/off shadows
1783 ////////////////////////////////////////////////////////////////////////
1785 class SGShadowAnimation::UpdateCallback : public osg::NodeCallback {
1787 UpdateCallback(const SGCondition* condition) :
1788 _condition(condition)
1790 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1792 if (_condition->test())
1793 node->setNodeMask( SG_NODEMASK_CASTSHADOW_BIT | node->getNodeMask());
1795 node->setNodeMask(~SG_NODEMASK_CASTSHADOW_BIT & node->getNodeMask());
1800 SGSharedPtr<const SGCondition> _condition;
1803 SGShadowAnimation::SGShadowAnimation(const SGPropertyNode* configNode,
1804 SGPropertyNode* modelRoot) :
1805 SGAnimation(configNode, modelRoot)
1810 SGShadowAnimation::createAnimationGroup(osg::Group& parent)
1812 SGSharedPtr<SGCondition const> condition = getCondition();
1816 osg::Group* group = new osg::Group;
1817 group->setName("shadow animation");
1818 group->setUpdateCallback(new UpdateCallback(condition));
1819 parent.addChild(group);
1824 ////////////////////////////////////////////////////////////////////////
1825 // Implementation of SGTexTransformAnimation
1826 ////////////////////////////////////////////////////////////////////////
1828 class SGTexTransformAnimation::Transform : public SGReferenced {
1833 virtual ~Transform()
1835 void setValue(double value)
1837 virtual void transform(osg::Matrix&) = 0;
1842 class SGTexTransformAnimation::Translation :
1843 public SGTexTransformAnimation::Transform {
1845 Translation(const SGVec3d& axis) :
1848 virtual void transform(osg::Matrix& matrix)
1851 set_translation(tmp, _value, _axis);
1852 matrix.preMult(tmp);
1858 class SGTexTransformAnimation::Rotation :
1859 public SGTexTransformAnimation::Transform {
1861 Rotation(const SGVec3d& axis, const SGVec3d& center) :
1865 virtual void transform(osg::Matrix& matrix)
1868 set_rotation(tmp, _value, _center, _axis);
1869 matrix.preMult(tmp);
1876 class SGTexTransformAnimation::UpdateCallback :
1877 public osg::StateAttribute::Callback {
1879 UpdateCallback(const SGCondition* condition) :
1880 _condition(condition)
1882 virtual void operator () (osg::StateAttribute* sa, osg::NodeVisitor*)
1884 if (!_condition || _condition->test()) {
1885 TransformList::const_iterator i;
1886 for (i = _transforms.begin(); i != _transforms.end(); ++i)
1887 i->transform->setValue(i->value->getValue());
1889 assert(dynamic_cast<osg::TexMat*>(sa));
1890 osg::TexMat* texMat = static_cast<osg::TexMat*>(sa);
1891 texMat->getMatrix().makeIdentity();
1892 TransformList::const_iterator i;
1893 for (i = _transforms.begin(); i != _transforms.end(); ++i)
1894 i->transform->transform(texMat->getMatrix());
1896 void appendTransform(Transform* transform, SGExpressiond* value)
1898 Entry entry = { transform, value };
1899 transform->transform(_matrix);
1900 _transforms.push_back(entry);
1905 SGSharedPtr<Transform> transform;
1906 SGSharedPtr<const SGExpressiond> value;
1908 typedef std::vector<Entry> TransformList;
1909 TransformList _transforms;
1910 SGSharedPtr<const SGCondition> _condition;
1911 osg::Matrix _matrix;
1914 SGTexTransformAnimation::SGTexTransformAnimation(const SGPropertyNode* configNode,
1915 SGPropertyNode* modelRoot) :
1916 SGAnimation(configNode, modelRoot)
1921 SGTexTransformAnimation::createAnimationGroup(osg::Group& parent)
1923 osg::Group* group = new osg::Group;
1924 group->setName("texture transform group");
1925 osg::StateSet* stateSet = group->getOrCreateStateSet();
1926 stateSet->setDataVariance(osg::Object::DYNAMIC);
1927 osg::TexMat* texMat = new osg::TexMat;
1928 UpdateCallback* updateCallback = new UpdateCallback(getCondition());
1929 // interpret the configs ...
1930 std::string type = getType();
1932 if (type == "textranslate") {
1933 appendTexTranslate(getConfig(), updateCallback);
1934 } else if (type == "texrotate") {
1935 appendTexRotate(getConfig(), updateCallback);
1936 } else if (type == "texmultiple") {
1937 std::vector<SGSharedPtr<SGPropertyNode> > transformConfigs;
1938 transformConfigs = getConfig()->getChildren("transform");
1939 for (unsigned i = 0; i < transformConfigs.size(); ++i) {
1940 std::string subtype = transformConfigs[i]->getStringValue("subtype", "");
1941 if (subtype == "textranslate")
1942 appendTexTranslate(transformConfigs[i], updateCallback);
1943 else if (subtype == "texrotate")
1944 appendTexRotate(transformConfigs[i], updateCallback);
1946 SG_LOG(SG_INPUT, SG_ALERT,
1947 "Ignoring unknown texture transform subtype");
1950 SG_LOG(SG_INPUT, SG_ALERT, "Ignoring unknown texture transform type");
1953 texMat->setUpdateCallback(updateCallback);
1954 stateSet->setTextureAttribute(0, texMat);
1955 parent.addChild(group);
1960 SGTexTransformAnimation::appendTexTranslate(const SGPropertyNode* config,
1961 UpdateCallback* updateCallback)
1963 std::string propertyName = config->getStringValue("property", "");
1964 SGSharedPtr<SGExpressiond> value;
1965 if (propertyName.empty())
1966 value = new SGConstExpression<double>(0);
1968 SGPropertyNode* inputProperty = getModelRoot()->getNode(propertyName, true);
1969 value = new SGPropertyExpression<double>(inputProperty);
1972 SGInterpTable* table = read_interpolation_table(config);
1974 value = new SGInterpTableExpression<double>(value, table);
1975 double biasValue = config->getDoubleValue("bias", 0);
1977 value = new SGBiasExpression<double>(value, biasValue);
1978 value = new SGStepExpression<double>(value,
1979 config->getDoubleValue("step", 0),
1980 config->getDoubleValue("scroll", 0));
1981 value = value->simplify();
1983 double biasValue = config->getDoubleValue("bias", 0);
1985 value = new SGBiasExpression<double>(value, biasValue);
1986 value = new SGStepExpression<double>(value,
1987 config->getDoubleValue("step", 0),
1988 config->getDoubleValue("scroll", 0));
1989 value = read_offset_factor(config, value, "factor", "offset");
1991 if (config->hasChild("min") || config->hasChild("max")) {
1992 double minClip = config->getDoubleValue("min", -SGLimitsd::max());
1993 double maxClip = config->getDoubleValue("max", SGLimitsd::max());
1994 value = new SGClipExpression<double>(value, minClip, maxClip);
1996 value = value->simplify();
1998 SGVec3d axis(config->getDoubleValue("axis/x", 0),
1999 config->getDoubleValue("axis/y", 0),
2000 config->getDoubleValue("axis/z", 0));
2001 Translation* translation;
2002 translation = new Translation(normalize(axis));
2003 translation->setValue(config->getDoubleValue("starting-position", 0));
2004 updateCallback->appendTransform(translation, value);
2008 SGTexTransformAnimation::appendTexRotate(const SGPropertyNode* config,
2009 UpdateCallback* updateCallback)
2011 std::string propertyName = config->getStringValue("property", "");
2012 SGSharedPtr<SGExpressiond> value;
2013 if (propertyName.empty())
2014 value = new SGConstExpression<double>(0);
2016 SGPropertyNode* inputProperty = getModelRoot()->getNode(propertyName, true);
2017 value = new SGPropertyExpression<double>(inputProperty);
2020 SGInterpTable* table = read_interpolation_table(config);
2022 value = new SGInterpTableExpression<double>(value, table);
2023 double biasValue = config->getDoubleValue("bias", 0);
2025 value = new SGBiasExpression<double>(value, biasValue);
2026 value = new SGStepExpression<double>(value,
2027 config->getDoubleValue("step", 0),
2028 config->getDoubleValue("scroll", 0));
2029 value = value->simplify();
2031 double biasValue = config->getDoubleValue("bias", 0);
2033 value = new SGBiasExpression<double>(value, biasValue);
2034 value = new SGStepExpression<double>(value,
2035 config->getDoubleValue("step", 0),
2036 config->getDoubleValue("scroll", 0));
2037 value = read_offset_factor(config, value, "factor", "offset-deg");
2039 if (config->hasChild("min-deg") || config->hasChild("max-deg")) {
2040 double minClip = config->getDoubleValue("min-deg", -SGLimitsd::max());
2041 double maxClip = config->getDoubleValue("max-deg", SGLimitsd::max());
2042 value = new SGClipExpression<double>(value, minClip, maxClip);
2044 value = value->simplify();
2046 SGVec3d axis(config->getDoubleValue("axis/x", 0),
2047 config->getDoubleValue("axis/y", 0),
2048 config->getDoubleValue("axis/z", 0));
2049 SGVec3d center(config->getDoubleValue("center/x", 0),
2050 config->getDoubleValue("center/y", 0),
2051 config->getDoubleValue("center/z", 0));
2053 rotation = new Rotation(normalize(axis), center);
2054 rotation->setValue(config->getDoubleValue("starting-position-deg", 0));
2055 updateCallback->appendTransform(rotation, value);
2059 ////////////////////////////////////////////////////////////////////////
2060 // Implementation of SGPickAnimation
2061 ////////////////////////////////////////////////////////////////////////
2063 class SGPickAnimation::PickCallback : public SGPickCallback {
2065 PickCallback(const SGPropertyNode* configNode,
2066 SGPropertyNode* modelRoot) :
2067 _repeatable(configNode->getBoolValue("repeatable", false)),
2068 _repeatInterval(configNode->getDoubleValue("interval-sec", 0.1))
2070 SG_LOG(SG_INPUT, SG_DEBUG, "Reading all bindings");
2071 std::vector<SGPropertyNode_ptr> bindings;
2073 bindings = configNode->getChildren("button");
2074 for (unsigned int i = 0; i < bindings.size(); ++i) {
2075 _buttons.push_back( bindings[i]->getIntValue() );
2077 bindings = configNode->getChildren("binding");
2078 for (unsigned int i = 0; i < bindings.size(); ++i) {
2079 _bindingsDown.push_back(new SGBinding(bindings[i], modelRoot));
2082 const SGPropertyNode* upNode = configNode->getChild("mod-up");
2085 bindings = upNode->getChildren("binding");
2086 for (unsigned int i = 0; i < bindings.size(); ++i) {
2087 _bindingsUp.push_back(new SGBinding(bindings[i], modelRoot));
2090 virtual bool buttonPressed(int button, const Info&)
2093 for( std::vector<int>::iterator it = _buttons.begin(); it != _buttons.end(); ++it ) {
2094 if( *it == button ) {
2101 SGBindingList::const_iterator i;
2102 for (i = _bindingsDown.begin(); i != _bindingsDown.end(); ++i)
2104 _repeatTime = -_repeatInterval; // anti-bobble: delay start of repeat
2107 virtual void buttonReleased(void)
2109 SGBindingList::const_iterator i;
2110 for (i = _bindingsUp.begin(); i != _bindingsUp.end(); ++i)
2113 virtual void update(double dt)
2119 while (_repeatInterval < _repeatTime) {
2120 _repeatTime -= _repeatInterval;
2121 SGBindingList::const_iterator i;
2122 for (i = _bindingsDown.begin(); i != _bindingsDown.end(); ++i)
2127 SGBindingList _bindingsDown;
2128 SGBindingList _bindingsUp;
2129 std::vector<int> _buttons;
2131 double _repeatInterval;
2135 class VncVisitor : public osg::NodeVisitor {
2137 VncVisitor(double x, double y, int mask) :
2138 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
2139 _texX(x), _texY(y), _mask(mask), _done(false)
2141 SG_LOG(SG_INPUT, SG_DEBUG, "VncVisitor constructor "
2142 << x << "," << y << " mask " << mask);
2145 virtual void apply(osg::Node &node)
2147 // Some nodes have state sets attached
2148 touchStateSet(node.getStateSet());
2152 // See whether we are a geode worth exploring
2153 osg::Geode *g = dynamic_cast<osg::Geode*>(&node);
2155 // Go find all its drawables
2156 int i = g->getNumDrawables();
2158 osg::Drawable *d = g->getDrawable(i);
2159 if (d) touchDrawable(*d);
2161 // Out of optimism, do the same for EffectGeode
2162 simgear::EffectGeode *eg = dynamic_cast<simgear::EffectGeode*>(&node);
2164 for (simgear::EffectGeode::DrawablesIterator di = eg->drawablesBegin();
2165 di != eg->drawablesEnd(); di++) {
2166 touchDrawable(**di);
2168 // Now see whether the EffectGeode has an Effect
2169 simgear::Effect *e = eg->getEffect();
2171 touchStateSet(e->getDefaultStateSet());
2175 inline void touchDrawable(osg::Drawable &d)
2177 osg::StateSet *ss = d.getStateSet();
2181 void touchStateSet(osg::StateSet *ss)
2184 osg::StateAttribute *sa = ss->getTextureAttribute(0,
2185 osg::StateAttribute::TEXTURE);
2187 osg::Texture *t = sa->asTexture();
2189 osg::Image *img = t->getImage(0);
2192 int pixX = _texX * img->s();
2193 int pixY = _texY * img->t();
2194 _done = img->sendPointerEvent(pixX, pixY, _mask);
2195 SG_LOG(SG_INPUT, SG_DEBUG, "VncVisitor image said " << _done
2196 << " to coord " << pixX << "," << pixY);
2200 inline bool wasSuccessful()
2206 double _texX, _texY;
2212 class SGPickAnimation::VncCallback : public SGPickCallback {
2214 VncCallback(const SGPropertyNode* configNode,
2215 SGPropertyNode* modelRoot,
2219 SG_LOG(SG_INPUT, SG_DEBUG, "Configuring VNC callback");
2220 const char *cornernames[3] = {"top-left", "top-right", "bottom-left"};
2221 SGVec3d *cornercoords[3] = {&_topLeft, &_toRight, &_toDown};
2222 for (int c =0; c < 3; c++) {
2223 const SGPropertyNode* cornerNode = configNode->getChild(cornernames[c]);
2224 *cornercoords[c] = SGVec3d(
2225 cornerNode->getDoubleValue("x"),
2226 cornerNode->getDoubleValue("y"),
2227 cornerNode->getDoubleValue("z"));
2229 _toRight -= _topLeft;
2230 _toDown -= _topLeft;
2231 _squaredRight = dot(_toRight, _toRight);
2232 _squaredDown = dot(_toDown, _toDown);
2235 virtual bool buttonPressed(int button, const Info& info)
2237 SGVec3d loc(info.local);
2238 SG_LOG(SG_INPUT, SG_DEBUG, "VNC pressed " << button << ": " << loc);
2240 _x = dot(loc, _toRight) / _squaredRight;
2241 _y = dot(loc, _toDown) / _squaredDown;
2242 if (_x<0) _x = 0; else if (_x > 1) _x = 1;
2243 if (_y<0) _y = 0; else if (_y > 1) _y = 1;
2244 VncVisitor vv(_x, _y, 1 << button);
2246 return vv.wasSuccessful();
2249 virtual void buttonReleased(void)
2251 SG_LOG(SG_INPUT, SG_DEBUG, "VNC release");
2252 VncVisitor vv(_x, _y, 0);
2255 virtual void update(double dt)
2260 osg::ref_ptr<osg::Group> _node;
2261 SGVec3d _topLeft, _toRight, _toDown;
2262 double _squaredRight, _squaredDown;
2265 SGPickAnimation::SGPickAnimation(const SGPropertyNode* configNode,
2266 SGPropertyNode* modelRoot) :
2267 SGAnimation(configNode, modelRoot)
2273 Mutex colorModeUniformMutex;
2274 osg::ref_ptr<osg::Uniform> colorModeUniform;
2278 SGPickAnimation::createAnimationGroup(osg::Group& parent)
2280 osg::Group* commonGroup = new osg::Group;
2282 // Contains the normal geometry that is interactive
2283 osg::ref_ptr<osg::Group> normalGroup = new osg::Group;
2284 normalGroup->setName("pick normal group");
2285 normalGroup->addChild(commonGroup);
2287 // Used to render the geometry with just yellow edges
2288 osg::Group* highlightGroup = new osg::Group;
2289 highlightGroup->setName("pick highlight group");
2290 highlightGroup->setNodeMask(SG_NODEMASK_PICK_BIT);
2291 highlightGroup->addChild(commonGroup);
2292 SGSceneUserData* ud;
2293 ud = SGSceneUserData::getOrCreateSceneUserData(commonGroup);
2295 // add actions that become macro and command invocations
2296 std::vector<SGPropertyNode_ptr> actions;
2297 actions = getConfig()->getChildren("action");
2298 for (unsigned int i = 0; i < actions.size(); ++i)
2299 ud->addPickCallback(new PickCallback(actions[i], getModelRoot()));
2300 // Look for the VNC sessions that want raw mouse input
2301 actions = getConfig()->getChildren("vncaction");
2302 for (unsigned int i = 0; i < actions.size(); ++i)
2303 ud->addPickCallback(new VncCallback(actions[i], getModelRoot(),
2306 // prepare a state set that paints the edges of this object yellow
2307 // The material and texture attributes are set with
2308 // OVERRIDE|PROTECTED in case there is a material animation on a
2309 // higher node in the scene graph, which would have its material
2310 // attribute set with OVERRIDE.
2311 osg::StateSet* stateSet = highlightGroup->getOrCreateStateSet();
2312 osg::Texture2D* white = StateAttributeFactory::instance()->getWhiteTexture();
2313 stateSet->setTextureAttributeAndModes(0, white,
2314 (osg::StateAttribute::ON
2315 | osg::StateAttribute::OVERRIDE
2316 | osg::StateAttribute::PROTECTED));
2317 osg::PolygonOffset* polygonOffset = new osg::PolygonOffset;
2318 polygonOffset->setFactor(-1);
2319 polygonOffset->setUnits(-1);
2320 stateSet->setAttribute(polygonOffset, osg::StateAttribute::OVERRIDE);
2321 stateSet->setMode(GL_POLYGON_OFFSET_LINE,
2322 osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
2323 osg::PolygonMode* polygonMode = new osg::PolygonMode;
2324 polygonMode->setMode(osg::PolygonMode::FRONT_AND_BACK,
2325 osg::PolygonMode::LINE);
2326 stateSet->setAttribute(polygonMode, osg::StateAttribute::OVERRIDE);
2327 osg::Material* material = new osg::Material;
2328 material->setColorMode(osg::Material::OFF);
2329 material->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(0, 0, 0, 1));
2330 // XXX Alpha < 1.0 in the diffuse material value is a signal to the
2331 // default shader to take the alpha value from the material value
2332 // and not the glColor. In many cases the pick animation geometry is
2333 // transparent, so the outline would not be visible without this hack.
2334 material->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(0, 0, 0, .95));
2335 material->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(1, 1, 0, 1));
2336 material->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0, 0, 0, 0));
2337 stateSet->setAttribute(
2338 material, osg::StateAttribute::OVERRIDE | osg::StateAttribute::PROTECTED);
2339 // The default shader has a colorMode uniform that mimics the
2340 // behavior of Material color mode.
2341 osg::Uniform* cmUniform = 0;
2343 ScopedLock<Mutex> lock(colorModeUniformMutex);
2344 if (!colorModeUniform.valid()) {
2345 colorModeUniform = new osg::Uniform(osg::Uniform::INT, "colorMode");
2346 colorModeUniform->set(0); // MODE_OFF
2347 colorModeUniform->setDataVariance(osg::Object::STATIC);
2349 cmUniform = colorModeUniform.get();
2351 stateSet->addUniform(cmUniform,
2352 osg::StateAttribute::OVERRIDE | osg::StateAttribute::ON);
2353 // Only add normal geometry if configured
2354 if (getConfig()->getBoolValue("visible", true))
2355 parent.addChild(normalGroup.get());
2356 parent.addChild(highlightGroup);