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/OsgMath.hxx>
46 #include <simgear/scene/util/SGNodeMasks.hxx>
47 #include <simgear/scene/util/SGSceneUserData.hxx>
48 #include <simgear/scene/util/SGStateAttributeVisitor.hxx>
49 #include <simgear/scene/util/StateAttributeFactory.hxx>
51 #include "animation.hxx"
54 #include "SGTranslateTransform.hxx"
55 #include "SGMaterialAnimation.hxx"
56 #include "SGRotateTransform.hxx"
57 #include "SGScaleTransform.hxx"
58 #include "SGInteractionAnimation.hxx"
60 #include "ConditionNode.hxx"
62 using OpenThreads::Mutex;
63 using OpenThreads::ReentrantMutex;
64 using OpenThreads::ScopedLock;
66 using namespace simgear;
68 ////////////////////////////////////////////////////////////////////////
69 // Static utility functions.
70 ////////////////////////////////////////////////////////////////////////
73 * Set up the transform matrix for a spin or rotation.
76 set_rotation (osg::Matrix &matrix, double position_deg,
77 const SGVec3d ¢er, const SGVec3d &axis)
79 double temp_angle = -SGMiscd::deg2rad(position_deg);
81 double s = sin(temp_angle);
82 double c = cos(temp_angle);
85 // axis was normalized at load time
86 // hint to the compiler to put these into FP registers
91 matrix(0, 0) = t * x * x + c ;
92 matrix(0, 1) = t * y * x - s * z ;
93 matrix(0, 2) = t * z * x + s * y ;
96 matrix(1, 0) = t * x * y + s * z ;
97 matrix(1, 1) = t * y * y + c ;
98 matrix(1, 2) = t * z * y - s * x ;
101 matrix(2, 0) = t * x * z - s * y ;
102 matrix(2, 1) = t * y * z + s * x ;
103 matrix(2, 2) = t * z * z + c ;
106 // hint to the compiler to put these into FP registers
111 matrix(3, 0) = x - x*matrix(0, 0) - y*matrix(1, 0) - z*matrix(2, 0);
112 matrix(3, 1) = y - x*matrix(0, 1) - y*matrix(1, 1) - z*matrix(2, 1);
113 matrix(3, 2) = z - x*matrix(0, 2) - y*matrix(1, 2) - z*matrix(2, 2);
118 * Set up the transform matrix for a translation.
121 set_translation (osg::Matrix &matrix, double position_m, const SGVec3d &axis)
123 SGVec3d xyz = axis * position_m;
124 matrix.makeIdentity();
125 matrix(3, 0) = xyz[0];
126 matrix(3, 1) = xyz[1];
127 matrix(3, 2) = xyz[2];
131 * Read an interpolation table from properties.
133 static SGInterpTable *
134 read_interpolation_table(const SGPropertyNode* props)
136 const SGPropertyNode* table_node = props->getNode("interpolation");
139 return new SGInterpTable(table_node);
143 unit_string(const char* value, const char* unit)
145 return std::string(value) + unit;
148 class SGPersonalityScaleOffsetExpression : public SGUnaryExpression<double> {
150 SGPersonalityScaleOffsetExpression(SGExpression<double>* expr,
151 SGPropertyNode const* config,
152 const std::string& scalename,
153 const std::string& offsetname,
155 double defOffset = 0) :
156 SGUnaryExpression<double>(expr),
157 _scale(config, scalename.c_str(), defScale),
158 _offset(config, offsetname.c_str(), defOffset)
160 void setScale(double scale)
162 void setOffset(double offset)
163 { _offset = offset; }
165 virtual void eval(double& value, const simgear::expression::Binding* b) const
169 value = _offset + _scale*getOperand()->getValue(b);
172 virtual bool isConst() const { return false; }
175 mutable SGPersonalityParameter<double> _scale;
176 mutable SGPersonalityParameter<double> _offset;
180 static SGExpressiond*
181 read_factor_offset(const SGPropertyNode* configNode, SGExpressiond* expr,
182 const std::string& factor, const std::string& offset)
184 double factorValue = configNode->getDoubleValue(factor, 1);
185 if (factorValue != 1)
186 expr = new SGScaleExpression<double>(expr, factorValue);
187 double offsetValue = configNode->getDoubleValue(offset, 0);
188 if (offsetValue != 0)
189 expr = new SGBiasExpression<double>(expr, offsetValue);
193 static SGExpressiond*
194 read_offset_factor(const SGPropertyNode* configNode, SGExpressiond* expr,
195 const std::string& factor, const std::string& offset)
197 double offsetValue = configNode->getDoubleValue(offset, 0);
198 if (offsetValue != 0)
199 expr = new SGBiasExpression<double>(expr, offsetValue);
200 double factorValue = configNode->getDoubleValue(factor, 1);
201 if (factorValue != 1)
202 expr = new SGScaleExpression<double>(expr, factorValue);
207 read_value(const SGPropertyNode* configNode, SGPropertyNode* modelRoot,
208 const char* unit, double defMin, double defMax)
210 const SGPropertyNode * expression = configNode->getNode( "expression" );
211 if( expression != NULL )
212 return SGReadDoubleExpression( modelRoot, expression->getChild(0) );
214 SGExpression<double>* value = 0;
216 std::string inputPropertyName = configNode->getStringValue("property", "");
217 if (inputPropertyName.empty()) {
218 std::string spos = unit_string("starting-position", unit);
219 double initPos = configNode->getDoubleValue(spos, 0);
220 value = new SGConstExpression<double>(initPos);
222 SGPropertyNode* inputProperty;
223 inputProperty = modelRoot->getNode(inputPropertyName, true);
224 value = new SGPropertyExpression<double>(inputProperty);
227 SGInterpTable* interpTable = read_interpolation_table(configNode);
229 return new SGInterpTableExpression<double>(value, interpTable);
231 std::string offset = unit_string("offset", unit);
232 std::string min = unit_string("min", unit);
233 std::string max = unit_string("max", unit);
235 if (configNode->getBoolValue("use-personality", false)) {
236 value = new SGPersonalityScaleOffsetExpression(value, configNode,
239 value = read_factor_offset(configNode, value, "factor", offset);
242 double minClip = configNode->getDoubleValue(min, defMin);
243 double maxClip = configNode->getDoubleValue(max, defMax);
244 if (minClip > SGMiscd::min(SGLimitsd::min(), -SGLimitsd::max()) ||
245 maxClip < SGLimitsd::max())
246 value = new SGClipExpression<double>(value, minClip, maxClip);
254 ////////////////////////////////////////////////////////////////////////
255 // Animation installer
256 ////////////////////////////////////////////////////////////////////////
258 class SGAnimation::RemoveModeVisitor : public SGStateAttributeVisitor {
260 RemoveModeVisitor(osg::StateAttribute::GLMode mode) :
263 virtual void apply(osg::StateSet* stateSet)
267 stateSet->removeMode(_mode);
270 osg::StateAttribute::GLMode _mode;
273 class SGAnimation::RemoveAttributeVisitor : public SGStateAttributeVisitor {
275 RemoveAttributeVisitor(osg::StateAttribute::Type type) :
278 virtual void apply(osg::StateSet* stateSet)
282 while (stateSet->getAttribute(_type)) {
283 stateSet->removeAttribute(_type);
287 osg::StateAttribute::Type _type;
290 class SGAnimation::RemoveTextureModeVisitor : public SGStateAttributeVisitor {
292 RemoveTextureModeVisitor(unsigned unit, osg::StateAttribute::GLMode mode) :
296 virtual void apply(osg::StateSet* stateSet)
300 stateSet->removeTextureMode(_unit, _mode);
304 osg::StateAttribute::GLMode _mode;
307 class SGAnimation::RemoveTextureAttributeVisitor :
308 public SGStateAttributeVisitor {
310 RemoveTextureAttributeVisitor(unsigned unit,
311 osg::StateAttribute::Type type) :
315 virtual void apply(osg::StateSet* stateSet)
319 while (stateSet->getTextureAttribute(_unit, _type)) {
320 stateSet->removeTextureAttribute(_unit, _type);
325 osg::StateAttribute::Type _type;
328 class SGAnimation::BinToInheritVisitor : public SGStateAttributeVisitor {
330 virtual void apply(osg::StateSet* stateSet)
334 stateSet->setRenderBinToInherit();
338 class SGAnimation::DrawableCloneVisitor : public osg::NodeVisitor {
340 DrawableCloneVisitor() :
341 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
343 void apply(osg::Geode& geode)
345 for (unsigned i = 0 ; i < geode.getNumDrawables(); ++i) {
346 osg::CopyOp copyOp(osg::CopyOp::DEEP_COPY_ALL &
347 ~osg::CopyOp::DEEP_COPY_TEXTURES);
348 geode.setDrawable(i, copyOp(geode.getDrawable(i)));
355 // Set all drawables to not use display lists. OSG will use
356 // glDrawArrays instead.
357 struct DoDrawArraysVisitor : public osg::NodeVisitor {
358 DoDrawArraysVisitor() :
359 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
361 void apply(osg::Geode& geode)
366 for (int i = 0; i < (int)geode.getNumDrawables(); ++i)
367 geode.getDrawable(i)->setUseDisplayList(false);
372 SGAnimation::SGAnimation(const SGPropertyNode* configNode,
373 SGPropertyNode* modelRoot) :
374 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
376 _configNode(configNode),
377 _modelRoot(modelRoot)
379 _name = configNode->getStringValue("name", "");
380 _enableHOT = configNode->getBoolValue("enable-hot", true);
381 _disableShadow = configNode->getBoolValue("disable-shadow", false);
382 std::vector<SGPropertyNode_ptr> objectNames =
383 configNode->getChildren("object-name");
384 for (unsigned i = 0; i < objectNames.size(); ++i)
385 _objectNames.push_back(objectNames[i]->getStringValue());
388 SGAnimation::~SGAnimation()
392 std::list<std::string>::const_iterator i;
394 for (i = _objectNames.begin(); i != _objectNames.end(); ++i)
404 SG_LOG(SG_IO, SG_ALERT, "Could not find at least one of the following"
405 " objects for animation: " << info);
411 SGAnimation::animate(osg::Node* node, const SGPropertyNode* configNode,
412 SGPropertyNode* modelRoot,
413 const osgDB::Options* options,
414 const string &path, int i)
416 std::string type = configNode->getStringValue("type", "none");
417 if (type == "alpha-test") {
418 SGAlphaTestAnimation animInst(configNode, modelRoot);
419 animInst.apply(node);
420 } else if (type == "billboard") {
421 SGBillboardAnimation animInst(configNode, modelRoot);
422 animInst.apply(node);
423 } else if (type == "blend") {
424 SGBlendAnimation animInst(configNode, modelRoot);
425 animInst.apply(node);
426 } else if (type == "dist-scale") {
427 SGDistScaleAnimation animInst(configNode, modelRoot);
428 animInst.apply(node);
429 } else if (type == "flash") {
430 SGFlashAnimation animInst(configNode, modelRoot);
431 animInst.apply(node);
432 } else if (type == "interaction") {
433 SGInteractionAnimation animInst(configNode, modelRoot);
434 animInst.apply(node);
435 } else if (type == "material") {
436 SGMaterialAnimation animInst(configNode, modelRoot, options, path);
437 animInst.apply(node);
438 } else if (type == "noshadow") {
439 SGShadowAnimation animInst(configNode, modelRoot);
440 animInst.apply(node);
441 } else if (type == "pick") {
442 SGPickAnimation animInst(configNode, modelRoot);
443 animInst.apply(node);
444 } else if (type == "range") {
445 SGRangeAnimation animInst(configNode, modelRoot);
446 animInst.apply(node);
447 } else if (type == "rotate" || type == "spin") {
448 SGRotateAnimation animInst(configNode, modelRoot);
449 animInst.apply(node);
450 } else if (type == "scale") {
451 SGScaleAnimation animInst(configNode, modelRoot);
452 animInst.apply(node);
453 } else if (type == "select") {
454 SGSelectAnimation animInst(configNode, modelRoot);
455 animInst.apply(node);
456 } else if (type == "shader") {
457 SGShaderAnimation animInst(configNode, modelRoot, options);
458 animInst.apply(node);
459 } else if (type == "textranslate" || type == "texrotate" ||
460 type == "texmultiple") {
461 SGTexTransformAnimation animInst(configNode, modelRoot);
462 animInst.apply(node);
463 } else if (type == "timed") {
464 SGTimedAnimation animInst(configNode, modelRoot);
465 animInst.apply(node);
466 } else if (type == "translate") {
467 SGTranslateAnimation animInst(configNode, modelRoot);
468 animInst.apply(node);
469 } else if (type == "light") {
470 SGLightAnimation animInst(configNode, modelRoot, path, i);
471 animInst.apply(node);
472 } else if (type == "null" || type == "none" || type.empty()) {
473 SGGroupAnimation animInst(configNode, modelRoot);
474 animInst.apply(node);
483 SGAnimation::apply(osg::Node* node)
485 // duh what a special case ...
486 if (_objectNames.empty()) {
487 osg::Group* group = node->asGroup();
489 osg::ref_ptr<osg::Group> animationGroup;
490 installInGroup(std::string(), *group, animationGroup);
497 SGAnimation::install(osg::Node& node)
501 node.setNodeMask( SG_NODEMASK_TERRAIN_BIT | node.getNodeMask());
503 node.setNodeMask(~SG_NODEMASK_TERRAIN_BIT & node.getNodeMask());
505 node.setNodeMask( SG_NODEMASK_CASTSHADOW_BIT | node.getNodeMask());
507 node.setNodeMask(~SG_NODEMASK_CASTSHADOW_BIT & node.getNodeMask());
511 SGAnimation::createAnimationGroup(osg::Group& parent)
513 // default implementation, we do not need a new group
514 // for every animation type. Usually animations that just change
515 // the StateSet of some parts of the model
520 SGAnimation::apply(osg::Group& group)
522 // the trick is to first traverse the children and then
523 // possibly splice in a new group node if required.
524 // Else we end up in a recursive loop where we infinitly insert new
528 // Note that this algorithm preserves the order of the child objects
529 // like they appear in the object-name tags.
530 // The timed animations require this
531 osg::ref_ptr<osg::Group> animationGroup;
532 std::list<std::string>::const_iterator nameIt;
533 for (nameIt = _objectNames.begin(); nameIt != _objectNames.end(); ++nameIt)
534 installInGroup(*nameIt, group, animationGroup);
538 SGAnimation::installInGroup(const std::string& name, osg::Group& group,
539 osg::ref_ptr<osg::Group>& animationGroup)
541 int i = group.getNumChildren() - 1;
542 for (; 0 <= i; --i) {
543 osg::Node* child = group.getChild(i);
545 // Check if this one is already processed
546 if (std::find(_installedAnimations.begin(),
547 _installedAnimations.end(), child)
548 != _installedAnimations.end())
551 if (name.empty() || child->getName() == name) {
552 // fire the installation of the animation
555 // create a group node on demand
556 if (!animationGroup.valid()) {
557 animationGroup = createAnimationGroup(group);
558 // Animation type that does not require a new group,
559 // in this case we can stop and look for the next object
560 if (animationGroup.valid() && !_name.empty())
561 animationGroup->setName(_name);
563 if (animationGroup.valid()) {
564 animationGroup->addChild(child);
565 group.removeChild(i);
568 // store that we already have processed this child node
569 // We can hit this one twice if an animation references some
570 // part of a subtree twice
571 _installedAnimations.push_back(child);
577 SGAnimation::removeMode(osg::Node& node, osg::StateAttribute::GLMode mode)
579 RemoveModeVisitor visitor(mode);
580 node.accept(visitor);
584 SGAnimation::removeAttribute(osg::Node& node, osg::StateAttribute::Type type)
586 RemoveAttributeVisitor visitor(type);
587 node.accept(visitor);
591 SGAnimation::removeTextureMode(osg::Node& node, unsigned unit,
592 osg::StateAttribute::GLMode mode)
594 RemoveTextureModeVisitor visitor(unit, mode);
595 node.accept(visitor);
599 SGAnimation::removeTextureAttribute(osg::Node& node, unsigned unit,
600 osg::StateAttribute::Type type)
602 RemoveTextureAttributeVisitor visitor(unit, type);
603 node.accept(visitor);
607 SGAnimation::setRenderBinToInherit(osg::Node& node)
609 BinToInheritVisitor visitor;
610 node.accept(visitor);
614 SGAnimation::cloneDrawables(osg::Node& node)
616 DrawableCloneVisitor visitor;
617 node.accept(visitor);
621 SGAnimation::getCondition() const
623 const SGPropertyNode* conditionNode = _configNode->getChild("condition");
626 return sgReadCondition(_modelRoot, conditionNode);
631 ////////////////////////////////////////////////////////////////////////
632 // Implementation of null animation
633 ////////////////////////////////////////////////////////////////////////
635 // Ok, that is to build a subgraph from different other
636 // graph nodes. I guess that this stems from the time where modellers
637 // could not build hierarchical trees ...
638 SGGroupAnimation::SGGroupAnimation(const SGPropertyNode* configNode,
639 SGPropertyNode* modelRoot):
640 SGAnimation(configNode, modelRoot)
645 SGGroupAnimation::createAnimationGroup(osg::Group& parent)
647 osg::Group* group = new osg::Group;
648 parent.addChild(group);
653 ////////////////////////////////////////////////////////////////////////
654 // Implementation of translate animation
655 ////////////////////////////////////////////////////////////////////////
657 class SGTranslateAnimation::UpdateCallback : public osg::NodeCallback {
659 UpdateCallback(SGCondition const* condition,
660 SGExpressiond const* animationValue) :
661 _condition(condition),
662 _animationValue(animationValue)
664 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
666 if (!_condition || _condition->test()) {
667 SGTranslateTransform* transform;
668 transform = static_cast<SGTranslateTransform*>(node);
669 transform->setValue(_animationValue->getValue());
674 SGSharedPtr<SGCondition const> _condition;
675 SGSharedPtr<SGExpressiond const> _animationValue;
678 SGTranslateAnimation::SGTranslateAnimation(const SGPropertyNode* configNode,
679 SGPropertyNode* modelRoot) :
680 SGAnimation(configNode, modelRoot)
682 _condition = getCondition();
683 SGSharedPtr<SGExpressiond> value;
684 value = read_value(configNode, modelRoot, "-m",
685 -SGLimitsd::max(), SGLimitsd::max());
686 _animationValue = value->simplify();
688 _initialValue = _animationValue->getValue();
692 if (configNode->hasValue("axis/x1-m")) {
694 v1[0] = configNode->getDoubleValue("axis/x1-m", 0);
695 v1[1] = configNode->getDoubleValue("axis/y1-m", 0);
696 v1[2] = configNode->getDoubleValue("axis/z1-m", 0);
697 v2[0] = configNode->getDoubleValue("axis/x2-m", 0);
698 v2[1] = configNode->getDoubleValue("axis/y2-m", 0);
699 v2[2] = configNode->getDoubleValue("axis/z2-m", 0);
702 _axis[0] = configNode->getDoubleValue("axis/x", 0);
703 _axis[1] = configNode->getDoubleValue("axis/y", 0);
704 _axis[2] = configNode->getDoubleValue("axis/z", 0);
706 if (8*SGLimitsd::min() < norm(_axis))
707 _axis = normalize(_axis);
711 SGTranslateAnimation::createAnimationGroup(osg::Group& parent)
713 SGTranslateTransform* transform = new SGTranslateTransform;
714 transform->setName("translate animation");
715 if (_animationValue && !_animationValue->isConst()) {
716 UpdateCallback* uc = new UpdateCallback(_condition, _animationValue);
717 transform->setUpdateCallback(uc);
719 transform->setAxis(_axis);
720 transform->setValue(_initialValue);
721 parent.addChild(transform);
726 ////////////////////////////////////////////////////////////////////////
727 // Implementation of rotate/spin animation
728 ////////////////////////////////////////////////////////////////////////
730 class SGRotateAnimation::UpdateCallback : public osg::NodeCallback {
732 UpdateCallback(SGCondition const* condition,
733 SGExpressiond const* animationValue) :
734 _condition(condition),
735 _animationValue(animationValue)
737 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
739 if (!_condition || _condition->test()) {
740 SGRotateTransform* transform;
741 transform = static_cast<SGRotateTransform*>(node);
742 transform->setAngleDeg(_animationValue->getValue());
747 SGSharedPtr<SGCondition const> _condition;
748 SGSharedPtr<SGExpressiond const> _animationValue;
751 class SGRotateAnimation::SpinUpdateCallback : public osg::NodeCallback {
753 SpinUpdateCallback(SGCondition const* condition,
754 SGExpressiond const* animationValue) :
755 _condition(condition),
756 _animationValue(animationValue),
759 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
761 if (!_condition || _condition->test()) {
762 SGRotateTransform* transform;
763 transform = static_cast<SGRotateTransform*>(node);
765 double t = nv->getFrameStamp()->getReferenceTime();
770 double velocity_rpms = _animationValue->getValue()/60;
771 double angle = transform->getAngleDeg();
772 angle += dt*velocity_rpms*360;
773 angle -= 360*floor(angle/360);
774 transform->setAngleDeg(angle);
779 SGSharedPtr<SGCondition const> _condition;
780 SGSharedPtr<SGExpressiond const> _animationValue;
784 SGRotateAnimation::SGRotateAnimation(const SGPropertyNode* configNode,
785 SGPropertyNode* modelRoot) :
786 SGAnimation(configNode, modelRoot)
788 std::string type = configNode->getStringValue("type", "");
789 _isSpin = (type == "spin");
791 _condition = getCondition();
792 SGSharedPtr<SGExpressiond> value;
793 value = read_value(configNode, modelRoot, "-deg",
794 -SGLimitsd::max(), SGLimitsd::max());
795 _animationValue = value->simplify();
797 _initialValue = _animationValue->getValue();
801 _center = SGVec3d::zeros();
802 if (configNode->hasValue("axis/x1-m")) {
804 v1[0] = configNode->getDoubleValue("axis/x1-m", 0);
805 v1[1] = configNode->getDoubleValue("axis/y1-m", 0);
806 v1[2] = configNode->getDoubleValue("axis/z1-m", 0);
807 v2[0] = configNode->getDoubleValue("axis/x2-m", 0);
808 v2[1] = configNode->getDoubleValue("axis/y2-m", 0);
809 v2[2] = configNode->getDoubleValue("axis/z2-m", 0);
810 _center = 0.5*(v1+v2);
813 _axis[0] = configNode->getDoubleValue("axis/x", 0);
814 _axis[1] = configNode->getDoubleValue("axis/y", 0);
815 _axis[2] = configNode->getDoubleValue("axis/z", 0);
817 if (8*SGLimitsd::min() < norm(_axis))
818 _axis = normalize(_axis);
820 _center[0] = configNode->getDoubleValue("center/x-m", _center[0]);
821 _center[1] = configNode->getDoubleValue("center/y-m", _center[1]);
822 _center[2] = configNode->getDoubleValue("center/z-m", _center[2]);
826 SGRotateAnimation::createAnimationGroup(osg::Group& parent)
828 SGRotateTransform* transform = new SGRotateTransform;
829 transform->setName("rotate animation");
831 SpinUpdateCallback* uc;
832 uc = new SpinUpdateCallback(_condition, _animationValue);
833 transform->setUpdateCallback(uc);
834 } else if (_animationValue || !_animationValue->isConst()) {
835 UpdateCallback* uc = new UpdateCallback(_condition, _animationValue);
836 transform->setUpdateCallback(uc);
838 transform->setCenter(_center);
839 transform->setAxis(_axis);
840 transform->setAngleDeg(_initialValue);
841 parent.addChild(transform);
846 ////////////////////////////////////////////////////////////////////////
847 // Implementation of scale animation
848 ////////////////////////////////////////////////////////////////////////
850 class SGScaleAnimation::UpdateCallback : public osg::NodeCallback {
852 UpdateCallback(const SGCondition* condition,
853 SGSharedPtr<const SGExpressiond> animationValue[3]) :
854 _condition(condition)
856 _animationValue[0] = animationValue[0];
857 _animationValue[1] = animationValue[1];
858 _animationValue[2] = animationValue[2];
860 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
862 if (!_condition || _condition->test()) {
863 SGScaleTransform* transform;
864 transform = static_cast<SGScaleTransform*>(node);
865 SGVec3d scale(_animationValue[0]->getValue(),
866 _animationValue[1]->getValue(),
867 _animationValue[2]->getValue());
868 transform->setScaleFactor(scale);
873 SGSharedPtr<SGCondition const> _condition;
874 SGSharedPtr<SGExpressiond const> _animationValue[3];
877 SGScaleAnimation::SGScaleAnimation(const SGPropertyNode* configNode,
878 SGPropertyNode* modelRoot) :
879 SGAnimation(configNode, modelRoot)
881 _condition = getCondition();
883 // default offset/factor for all directions
884 double offset = configNode->getDoubleValue("offset", 0);
885 double factor = configNode->getDoubleValue("factor", 1);
887 SGSharedPtr<SGExpressiond> inPropExpr;
889 std::string inputPropertyName;
890 inputPropertyName = configNode->getStringValue("property", "");
891 if (inputPropertyName.empty()) {
892 inPropExpr = new SGConstExpression<double>(0);
894 SGPropertyNode* inputProperty;
895 inputProperty = modelRoot->getNode(inputPropertyName, true);
896 inPropExpr = new SGPropertyExpression<double>(inputProperty);
899 SGInterpTable* interpTable = read_interpolation_table(configNode);
901 SGSharedPtr<SGExpressiond> value;
902 value = new SGInterpTableExpression<double>(inPropExpr, interpTable);
903 _animationValue[0] = value->simplify();
904 _animationValue[1] = value->simplify();
905 _animationValue[2] = value->simplify();
906 } else if (configNode->getBoolValue("use-personality", false)) {
907 SGSharedPtr<SGExpressiond> value;
908 value = new SGPersonalityScaleOffsetExpression(inPropExpr, configNode,
909 "x-factor", "x-offset",
911 double minClip = configNode->getDoubleValue("x-min", 0);
912 double maxClip = configNode->getDoubleValue("x-max", SGLimitsd::max());
913 value = new SGClipExpression<double>(value, minClip, maxClip);
914 _animationValue[0] = value->simplify();
916 value = new SGPersonalityScaleOffsetExpression(inPropExpr, configNode,
917 "y-factor", "y-offset",
919 minClip = configNode->getDoubleValue("y-min", 0);
920 maxClip = configNode->getDoubleValue("y-max", SGLimitsd::max());
921 value = new SGClipExpression<double>(value, minClip, maxClip);
922 _animationValue[1] = value->simplify();
924 value = new SGPersonalityScaleOffsetExpression(inPropExpr, configNode,
925 "z-factor", "z-offset",
927 minClip = configNode->getDoubleValue("z-min", 0);
928 maxClip = configNode->getDoubleValue("z-max", SGLimitsd::max());
929 value = new SGClipExpression<double>(value, minClip, maxClip);
930 _animationValue[2] = value->simplify();
932 SGSharedPtr<SGExpressiond> value;
933 value = read_factor_offset(configNode, inPropExpr, "x-factor", "x-offset");
934 double minClip = configNode->getDoubleValue("x-min", 0);
935 double maxClip = configNode->getDoubleValue("x-max", SGLimitsd::max());
936 value = new SGClipExpression<double>(value, minClip, maxClip);
937 _animationValue[0] = value->simplify();
939 value = read_factor_offset(configNode, inPropExpr, "y-factor", "y-offset");
940 minClip = configNode->getDoubleValue("y-min", 0);
941 maxClip = configNode->getDoubleValue("y-max", SGLimitsd::max());
942 value = new SGClipExpression<double>(value, minClip, maxClip);
943 _animationValue[1] = value->simplify();
945 value = read_factor_offset(configNode, inPropExpr, "z-factor", "z-offset");
946 minClip = configNode->getDoubleValue("z-min", 0);
947 maxClip = configNode->getDoubleValue("z-max", SGLimitsd::max());
948 value = new SGClipExpression<double>(value, minClip, maxClip);
949 _animationValue[2] = value->simplify();
951 _initialValue[0] = configNode->getDoubleValue("x-starting-scale", 1);
952 _initialValue[0] *= configNode->getDoubleValue("x-factor", factor);
953 _initialValue[0] += configNode->getDoubleValue("x-offset", offset);
954 _initialValue[1] = configNode->getDoubleValue("y-starting-scale", 1);
955 _initialValue[1] *= configNode->getDoubleValue("y-factor", factor);
956 _initialValue[1] += configNode->getDoubleValue("y-offset", offset);
957 _initialValue[2] = configNode->getDoubleValue("z-starting-scale", 1);
958 _initialValue[2] *= configNode->getDoubleValue("z-factor", factor);
959 _initialValue[2] += configNode->getDoubleValue("z-offset", offset);
960 _center[0] = configNode->getDoubleValue("center/x-m", 0);
961 _center[1] = configNode->getDoubleValue("center/y-m", 0);
962 _center[2] = configNode->getDoubleValue("center/z-m", 0);
966 SGScaleAnimation::createAnimationGroup(osg::Group& parent)
968 SGScaleTransform* transform = new SGScaleTransform;
969 transform->setName("scale animation");
970 transform->setCenter(_center);
971 transform->setScaleFactor(_initialValue);
972 UpdateCallback* uc = new UpdateCallback(_condition, _animationValue);
973 transform->setUpdateCallback(uc);
974 parent.addChild(transform);
979 // Don't create a new state state everytime we need GL_NORMALIZE!
983 Mutex normalizeMutex;
985 osg::StateSet* getNormalizeStateSet()
987 static osg::ref_ptr<osg::StateSet> normalizeStateSet;
988 ScopedLock<Mutex> lock(normalizeMutex);
989 if (!normalizeStateSet.valid()) {
990 normalizeStateSet = new osg::StateSet;
991 normalizeStateSet->setMode(GL_NORMALIZE, osg::StateAttribute::ON);
992 normalizeStateSet->setDataVariance(osg::Object::STATIC);
994 return normalizeStateSet.get();
998 ////////////////////////////////////////////////////////////////////////
999 // Implementation of dist scale animation
1000 ////////////////////////////////////////////////////////////////////////
1002 class SGDistScaleAnimation::Transform : public osg::Transform {
1004 Transform() : _min_v(0.0), _max_v(0.0), _factor(0.0), _offset(0.0) {}
1005 Transform(const Transform& rhs,
1006 const osg::CopyOp& copyOp = osg::CopyOp::SHALLOW_COPY)
1007 : osg::Transform(rhs, copyOp), _table(rhs._table), _center(rhs._center),
1008 _min_v(rhs._min_v), _max_v(rhs._max_v), _factor(rhs._factor),
1009 _offset(rhs._offset)
1012 META_Node(simgear, SGDistScaleAnimation::Transform);
1013 Transform(const SGPropertyNode* configNode)
1015 setName(configNode->getStringValue("name", "dist scale animation"));
1016 setReferenceFrame(RELATIVE_RF);
1017 setStateSet(getNormalizeStateSet());
1018 _factor = configNode->getFloatValue("factor", 1);
1019 _offset = configNode->getFloatValue("offset", 0);
1020 _min_v = configNode->getFloatValue("min", SGLimitsf::epsilon());
1021 _max_v = configNode->getFloatValue("max", SGLimitsf::max());
1022 _table = read_interpolation_table(configNode);
1023 _center[0] = configNode->getFloatValue("center/x-m", 0);
1024 _center[1] = configNode->getFloatValue("center/y-m", 0);
1025 _center[2] = configNode->getFloatValue("center/z-m", 0);
1027 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
1028 osg::NodeVisitor* nv) const
1030 osg::Matrix transform;
1031 double scale_factor = computeScaleFactor(nv);
1032 transform(0,0) = scale_factor;
1033 transform(1,1) = scale_factor;
1034 transform(2,2) = scale_factor;
1035 transform(3,0) = _center[0]*(1 - scale_factor);
1036 transform(3,1) = _center[1]*(1 - scale_factor);
1037 transform(3,2) = _center[2]*(1 - scale_factor);
1038 matrix.preMult(transform);
1042 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
1043 osg::NodeVisitor* nv) const
1045 double scale_factor = computeScaleFactor(nv);
1046 if (fabs(scale_factor) <= SGLimits<double>::min())
1048 osg::Matrix transform;
1049 double rScaleFactor = 1/scale_factor;
1050 transform(0,0) = rScaleFactor;
1051 transform(1,1) = rScaleFactor;
1052 transform(2,2) = rScaleFactor;
1053 transform(3,0) = _center[0]*(1 - rScaleFactor);
1054 transform(3,1) = _center[1]*(1 - rScaleFactor);
1055 transform(3,2) = _center[2]*(1 - rScaleFactor);
1056 matrix.postMult(transform);
1060 static bool writeLocalData(const osg::Object& obj, osgDB::Output& fw)
1062 const Transform& trans = static_cast<const Transform&>(obj);
1063 fw.indent() << "center " << trans._center << "\n";
1064 fw.indent() << "min_v " << trans._min_v << "\n";
1065 fw.indent() << "max_v " << trans._max_v << "\n";
1066 fw.indent() << "factor " << trans._factor << "\n";
1067 fw.indent() << "offset " << trans._offset << "\n";
1071 double computeScaleFactor(osg::NodeVisitor* nv) const
1076 double scale_factor = (toOsg(_center) - nv->getEyePoint()).length();
1078 scale_factor = _factor * scale_factor + _offset;
1080 scale_factor = _table->interpolate( scale_factor );
1082 if (scale_factor < _min_v)
1083 scale_factor = _min_v;
1084 if (scale_factor > _max_v)
1085 scale_factor = _max_v;
1087 return scale_factor;
1090 SGSharedPtr<SGInterpTable> _table;
1099 SGDistScaleAnimation::SGDistScaleAnimation(const SGPropertyNode* configNode,
1100 SGPropertyNode* modelRoot) :
1101 SGAnimation(configNode, modelRoot)
1106 SGDistScaleAnimation::createAnimationGroup(osg::Group& parent)
1108 Transform* transform = new Transform(getConfig());
1109 parent.addChild(transform);
1115 osgDB::RegisterDotOsgWrapperProxy distScaleAnimationTransformProxy
1117 new SGDistScaleAnimation::Transform,
1118 "SGDistScaleAnimation::Transform",
1119 "Object Node Transform SGDistScaleAnimation::Transform Group",
1121 &SGDistScaleAnimation::Transform::writeLocalData
1125 ////////////////////////////////////////////////////////////////////////
1126 // Implementation of flash animation
1127 ////////////////////////////////////////////////////////////////////////
1129 class SGFlashAnimation::Transform : public osg::Transform {
1131 Transform() : _power(0.0), _factor(0.0), _offset(0.0), _min_v(0.0),
1132 _max_v(0.0), _two_sides(false)
1135 Transform(const Transform& rhs,
1136 const osg::CopyOp& copyOp = osg::CopyOp::SHALLOW_COPY)
1137 : osg::Transform(rhs, copyOp), _center(rhs._center), _axis(rhs._axis),
1138 _power(rhs._power), _factor(rhs._factor), _offset(rhs._offset),
1139 _min_v(rhs._min_v), _max_v(rhs._max_v), _two_sides(rhs._two_sides)
1142 META_Node(simgear, SGFlashAnimation::Transform);
1144 Transform(const SGPropertyNode* configNode)
1146 setReferenceFrame(RELATIVE_RF);
1147 setName(configNode->getStringValue("name", "flash animation"));
1148 setStateSet(getNormalizeStateSet());
1150 _axis[0] = configNode->getFloatValue("axis/x", 0);
1151 _axis[1] = configNode->getFloatValue("axis/y", 0);
1152 _axis[2] = configNode->getFloatValue("axis/z", 1);
1155 _center[0] = configNode->getFloatValue("center/x-m", 0);
1156 _center[1] = configNode->getFloatValue("center/y-m", 0);
1157 _center[2] = configNode->getFloatValue("center/z-m", 0);
1159 _offset = configNode->getFloatValue("offset", 0);
1160 _factor = configNode->getFloatValue("factor", 1);
1161 _power = configNode->getFloatValue("power", 1);
1162 _two_sides = configNode->getBoolValue("two-sides", false);
1164 _min_v = configNode->getFloatValue("min", SGLimitsf::epsilon());
1165 _max_v = configNode->getFloatValue("max", 1);
1167 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
1168 osg::NodeVisitor* nv) const
1170 osg::Matrix transform;
1171 double scale_factor = computeScaleFactor(nv);
1172 transform(0,0) = scale_factor;
1173 transform(1,1) = scale_factor;
1174 transform(2,2) = scale_factor;
1175 transform(3,0) = _center[0]*(1 - scale_factor);
1176 transform(3,1) = _center[1]*(1 - scale_factor);
1177 transform(3,2) = _center[2]*(1 - scale_factor);
1178 matrix.preMult(transform);
1182 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
1183 osg::NodeVisitor* nv) const
1185 double scale_factor = computeScaleFactor(nv);
1186 if (fabs(scale_factor) <= SGLimits<double>::min())
1188 osg::Matrix transform;
1189 double rScaleFactor = 1/scale_factor;
1190 transform(0,0) = rScaleFactor;
1191 transform(1,1) = rScaleFactor;
1192 transform(2,2) = rScaleFactor;
1193 transform(3,0) = _center[0]*(1 - rScaleFactor);
1194 transform(3,1) = _center[1]*(1 - rScaleFactor);
1195 transform(3,2) = _center[2]*(1 - rScaleFactor);
1196 matrix.postMult(transform);
1200 static bool writeLocalData(const osg::Object& obj, osgDB::Output& fw)
1202 const Transform& trans = static_cast<const Transform&>(obj);
1203 fw.indent() << "center " << trans._center[0] << " "
1204 << trans._center[1] << " " << trans._center[2] << " " << "\n";
1205 fw.indent() << "axis " << trans._axis[0] << " "
1206 << trans._axis[1] << " " << trans._axis[2] << " " << "\n";
1207 fw.indent() << "power " << trans._power << " \n";
1208 fw.indent() << "min_v " << trans._min_v << "\n";
1209 fw.indent() << "max_v " << trans._max_v << "\n";
1210 fw.indent() << "factor " << trans._factor << "\n";
1211 fw.indent() << "offset " << trans._offset << "\n";
1212 fw.indent() << "twosides " << (trans._two_sides ? "true" : "false") << "\n";
1216 double computeScaleFactor(osg::NodeVisitor* nv) const
1221 osg::Vec3 localEyeToCenter = nv->getEyePoint() - _center;
1222 localEyeToCenter.normalize();
1224 double cos_angle = localEyeToCenter*_axis;
1225 double scale_factor = 0;
1226 if ( _two_sides && cos_angle < 0 )
1227 scale_factor = _factor * pow( -cos_angle, _power ) + _offset;
1228 else if ( cos_angle > 0 )
1229 scale_factor = _factor * pow( cos_angle, _power ) + _offset;
1231 if ( scale_factor < _min_v )
1232 scale_factor = _min_v;
1233 if ( scale_factor > _max_v )
1234 scale_factor = _max_v;
1236 return scale_factor;
1239 virtual osg::BoundingSphere computeBound() const
1241 // avoid being culled away by small feature culling
1242 osg::BoundingSphere bs = osg::Group::computeBound();
1243 bs.radius() *= _max_v;
1250 double _power, _factor, _offset, _min_v, _max_v;
1255 SGFlashAnimation::SGFlashAnimation(const SGPropertyNode* configNode,
1256 SGPropertyNode* modelRoot) :
1257 SGAnimation(configNode, modelRoot)
1262 SGFlashAnimation::createAnimationGroup(osg::Group& parent)
1264 Transform* transform = new Transform(getConfig());
1265 parent.addChild(transform);
1271 osgDB::RegisterDotOsgWrapperProxy flashAnimationTransformProxy
1273 new SGFlashAnimation::Transform,
1274 "SGFlashAnimation::Transform",
1275 "Object Node Transform SGFlashAnimation::Transform Group",
1277 &SGFlashAnimation::Transform::writeLocalData
1281 ////////////////////////////////////////////////////////////////////////
1282 // Implementation of billboard animation
1283 ////////////////////////////////////////////////////////////////////////
1285 class SGBillboardAnimation::Transform : public osg::Transform {
1287 Transform() : _spherical(true) {}
1288 Transform(const Transform& rhs,
1289 const osg::CopyOp& copyOp = osg::CopyOp::SHALLOW_COPY)
1290 : osg::Transform(rhs, copyOp), _spherical(rhs._spherical) {}
1291 META_Node(simgear, SGBillboardAnimation::Transform);
1292 Transform(const SGPropertyNode* configNode) :
1293 _spherical(configNode->getBoolValue("spherical", true))
1295 setReferenceFrame(RELATIVE_RF);
1296 setName(configNode->getStringValue("name", "billboard animation"));
1298 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
1299 osg::NodeVisitor* nv) const
1301 // More or less taken from plibs ssgCutout
1303 matrix(0,0) = 1; matrix(0,1) = 0; matrix(0,2) = 0;
1304 matrix(1,0) = 0; matrix(1,1) = 0; matrix(1,2) = -1;
1305 matrix(2,0) = 0; matrix(2,1) = 1; matrix(2,2) = 0;
1307 osg::Vec3 zAxis(matrix(2, 0), matrix(2, 1), matrix(2, 2));
1308 osg::Vec3 xAxis = osg::Vec3(0, 0, -1)^zAxis;
1309 osg::Vec3 yAxis = zAxis^xAxis;
1315 matrix(0,0) = xAxis[0]; matrix(0,1) = xAxis[1]; matrix(0,2) = xAxis[2];
1316 matrix(1,0) = yAxis[0]; matrix(1,1) = yAxis[1]; matrix(1,2) = yAxis[2];
1317 matrix(2,0) = zAxis[0]; matrix(2,1) = zAxis[1]; matrix(2,2) = zAxis[2];
1322 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
1323 osg::NodeVisitor* nv) const
1325 // Hmm, don't yet know how to get that back ...
1328 static bool writeLocalData(const osg::Object& obj, osgDB::Output& fw)
1330 const Transform& trans = static_cast<const Transform&>(obj);
1332 fw.indent() << (trans._spherical ? "true" : "false") << "\n";
1340 SGBillboardAnimation::SGBillboardAnimation(const SGPropertyNode* configNode,
1341 SGPropertyNode* modelRoot) :
1342 SGAnimation(configNode, modelRoot)
1347 SGBillboardAnimation::createAnimationGroup(osg::Group& parent)
1349 Transform* transform = new Transform(getConfig());
1350 parent.addChild(transform);
1356 osgDB::RegisterDotOsgWrapperProxy billboardAnimationTransformProxy
1358 new SGBillboardAnimation::Transform,
1359 "SGBillboardAnimation::Transform",
1360 "Object Node Transform SGBillboardAnimation::Transform Group",
1362 &SGBillboardAnimation::Transform::writeLocalData
1366 ////////////////////////////////////////////////////////////////////////
1367 // Implementation of a range animation
1368 ////////////////////////////////////////////////////////////////////////
1370 class SGRangeAnimation::UpdateCallback : public osg::NodeCallback {
1372 UpdateCallback(const SGCondition* condition,
1373 const SGExpressiond* minAnimationValue,
1374 const SGExpressiond* maxAnimationValue,
1375 double minValue, double maxValue) :
1376 _condition(condition),
1377 _minAnimationValue(minAnimationValue),
1378 _maxAnimationValue(maxAnimationValue),
1379 _minStaticValue(minValue),
1380 _maxStaticValue(maxValue)
1382 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1384 osg::LOD* lod = static_cast<osg::LOD*>(node);
1385 if (!_condition || _condition->test()) {
1387 if (_minAnimationValue)
1388 minRange = _minAnimationValue->getValue();
1390 minRange = _minStaticValue;
1392 if (_maxAnimationValue)
1393 maxRange = _maxAnimationValue->getValue();
1395 maxRange = _maxStaticValue;
1396 lod->setRange(0, minRange, maxRange);
1398 lod->setRange(0, 0, SGLimitsf::max());
1404 SGSharedPtr<const SGCondition> _condition;
1405 SGSharedPtr<const SGExpressiond> _minAnimationValue;
1406 SGSharedPtr<const SGExpressiond> _maxAnimationValue;
1407 double _minStaticValue;
1408 double _maxStaticValue;
1411 SGRangeAnimation::SGRangeAnimation(const SGPropertyNode* configNode,
1412 SGPropertyNode* modelRoot) :
1413 SGAnimation(configNode, modelRoot)
1415 _condition = getCondition();
1417 std::string inputPropertyName;
1418 inputPropertyName = configNode->getStringValue("min-property", "");
1419 if (!inputPropertyName.empty()) {
1420 SGPropertyNode* inputProperty;
1421 inputProperty = modelRoot->getNode(inputPropertyName, true);
1422 SGSharedPtr<SGExpressiond> value;
1423 value = new SGPropertyExpression<double>(inputProperty);
1425 value = read_factor_offset(configNode, value, "min-factor", "min-offset");
1426 _minAnimationValue = value->simplify();
1428 inputPropertyName = configNode->getStringValue("max-property", "");
1429 if (!inputPropertyName.empty()) {
1430 SGPropertyNode* inputProperty;
1431 inputProperty = modelRoot->getNode(inputPropertyName.c_str(), true);
1433 SGSharedPtr<SGExpressiond> value;
1434 value = new SGPropertyExpression<double>(inputProperty);
1436 value = read_factor_offset(configNode, value, "max-factor", "max-offset");
1437 _maxAnimationValue = value->simplify();
1440 _initialValue[0] = configNode->getDoubleValue("min-m", 0);
1441 _initialValue[0] *= configNode->getDoubleValue("min-factor", 1);
1442 _initialValue[1] = configNode->getDoubleValue("max-m", SGLimitsf::max());
1443 _initialValue[1] *= configNode->getDoubleValue("max-factor", 1);
1447 SGRangeAnimation::createAnimationGroup(osg::Group& parent)
1449 osg::Group* group = new osg::Group;
1450 group->setName("range animation group");
1452 osg::LOD* lod = new osg::LOD;
1453 lod->setName("range animation node");
1454 parent.addChild(lod);
1456 lod->addChild(group, _initialValue[0], _initialValue[1]);
1457 lod->setCenterMode(osg::LOD::USE_BOUNDING_SPHERE_CENTER);
1458 lod->setRangeMode(osg::LOD::DISTANCE_FROM_EYE_POINT);
1459 if (_minAnimationValue || _maxAnimationValue || _condition) {
1461 uc = new UpdateCallback(_condition, _minAnimationValue, _maxAnimationValue,
1462 _initialValue[0], _initialValue[1]);
1463 lod->setUpdateCallback(uc);
1469 ////////////////////////////////////////////////////////////////////////
1470 // Implementation of a select animation
1471 ////////////////////////////////////////////////////////////////////////
1473 SGSelectAnimation::SGSelectAnimation(const SGPropertyNode* configNode,
1474 SGPropertyNode* modelRoot) :
1475 SGAnimation(configNode, modelRoot)
1480 SGSelectAnimation::createAnimationGroup(osg::Group& parent)
1482 // if no condition given, this is a noop.
1483 SGSharedPtr<SGCondition const> condition = getCondition();
1484 // trick, gets deleted with all its 'animated' children
1485 // when the animation installer returns
1487 return new osg::Group;
1488 simgear::ConditionNode* cn = new simgear::ConditionNode;
1489 cn->setName("select animation node");
1490 cn->setCondition(condition.ptr());
1491 osg::Group* grp = new osg::Group;
1493 parent.addChild(cn);
1499 ////////////////////////////////////////////////////////////////////////
1500 // Implementation of alpha test animation
1501 ////////////////////////////////////////////////////////////////////////
1503 SGAlphaTestAnimation::SGAlphaTestAnimation(const SGPropertyNode* configNode,
1504 SGPropertyNode* modelRoot) :
1505 SGAnimation(configNode, modelRoot)
1511 // Keep one copy of the most common alpha test its state set.
1512 ReentrantMutex alphaTestMutex;
1513 osg::ref_ptr<osg::AlphaFunc> standardAlphaFunc;
1514 osg::ref_ptr<osg::StateSet> alphaFuncStateSet;
1516 osg::AlphaFunc* makeAlphaFunc(float clamp)
1518 ScopedLock<ReentrantMutex> lock(alphaTestMutex);
1519 if (osg::equivalent(clamp, 0.01f)) {
1520 if (standardAlphaFunc.valid())
1521 return standardAlphaFunc.get();
1524 osg::AlphaFunc* alphaFunc = new osg::AlphaFunc;
1525 alphaFunc->setFunction(osg::AlphaFunc::GREATER);
1526 alphaFunc->setReferenceValue(clamp);
1527 alphaFunc->setDataVariance(osg::Object::STATIC);
1528 if (osg::equivalent(clamp, 0.01f))
1529 standardAlphaFunc = alphaFunc;
1533 osg::StateSet* makeAlphaTestStateSet(float clamp)
1535 using namespace OpenThreads;
1536 ScopedLock<ReentrantMutex> lock(alphaTestMutex);
1537 if (osg::equivalent(clamp, 0.01f)) {
1538 if (alphaFuncStateSet.valid())
1539 return alphaFuncStateSet.get();
1541 osg::AlphaFunc* alphaFunc = makeAlphaFunc(clamp);
1542 osg::StateSet* stateSet = new osg::StateSet;
1543 stateSet->setAttributeAndModes(alphaFunc,
1544 (osg::StateAttribute::ON
1545 | osg::StateAttribute::OVERRIDE));
1546 stateSet->setDataVariance(osg::Object::STATIC);
1547 if (osg::equivalent(clamp, 0.01f))
1548 alphaFuncStateSet = stateSet;
1553 SGAlphaTestAnimation::install(osg::Node& node)
1555 SGAnimation::install(node);
1557 float alphaClamp = getConfig()->getFloatValue("alpha-factor", 0);
1558 osg::StateSet* stateSet = node.getStateSet();
1560 node.setStateSet(makeAlphaTestStateSet(alphaClamp));
1562 stateSet->setAttributeAndModes(makeAlphaFunc(alphaClamp),
1563 (osg::StateAttribute::ON
1564 | osg::StateAttribute::OVERRIDE));
1569 //////////////////////////////////////////////////////////////////////
1570 // Blend animation installer
1571 //////////////////////////////////////////////////////////////////////
1573 // XXX This needs to be replaced by something using TexEnvCombine to
1574 // change the blend factor. Changing the alpha values in the geometry
1576 class SGBlendAnimation::BlendVisitor : public osg::NodeVisitor {
1578 BlendVisitor(float blend) :
1579 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
1581 { setVisitorType(osg::NodeVisitor::NODE_VISITOR); }
1582 virtual void apply(osg::Node& node)
1584 updateStateSet(node.getStateSet());
1587 virtual void apply(osg::Geode& node)
1589 apply((osg::Node&)node);
1590 unsigned nDrawables = node.getNumDrawables();
1591 for (unsigned i = 0; i < nDrawables; ++i) {
1592 osg::Drawable* drawable = node.getDrawable(i);
1593 osg::Geometry* geometry = drawable->asGeometry();
1596 osg::Array* array = geometry->getColorArray();
1599 osg::Vec4Array* vec4Array = dynamic_cast<osg::Vec4Array*>(array);
1602 for (unsigned k = 0; k < vec4Array->size(); ++k) {
1603 (*vec4Array)[k][3] = _blend;
1606 updateStateSet(drawable->getStateSet());
1609 void updateStateSet(osg::StateSet* stateSet)
1613 osg::StateAttribute* stateAttribute;
1614 stateAttribute = stateSet->getAttribute(osg::StateAttribute::MATERIAL);
1615 if (!stateAttribute)
1617 osg::Material* material = dynamic_cast<osg::Material*>(stateAttribute);
1620 material->setAlpha(osg::Material::FRONT_AND_BACK, _blend);
1622 stateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
1623 stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
1625 stateSet->setRenderingHint(osg::StateSet::DEFAULT_BIN);
1632 class SGBlendAnimation::UpdateCallback : public osg::NodeCallback {
1634 UpdateCallback(const SGPropertyNode* configNode, const SGExpressiond* v) :
1638 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1640 double blend = _animationValue->getValue();
1641 if (blend != _prev_value) {
1642 _prev_value = blend;
1643 BlendVisitor visitor(1-blend);
1644 node->accept(visitor);
1650 SGSharedPtr<SGExpressiond const> _animationValue;
1654 SGBlendAnimation::SGBlendAnimation(const SGPropertyNode* configNode,
1655 SGPropertyNode* modelRoot)
1656 : SGAnimation(configNode, modelRoot),
1657 _animationValue(read_value(configNode, modelRoot, "", 0, 1))
1662 SGBlendAnimation::createAnimationGroup(osg::Group& parent)
1664 if (!_animationValue)
1667 osg::Group* group = new osg::Switch;
1668 group->setName("blend animation node");
1669 group->setUpdateCallback(new UpdateCallback(getConfig(), _animationValue));
1670 parent.addChild(group);
1675 SGBlendAnimation::install(osg::Node& node)
1677 SGAnimation::install(node);
1678 // make sure we do not change common geometries,
1679 // that also creates new display lists for these subgeometries.
1680 cloneDrawables(node);
1681 DoDrawArraysVisitor visitor;
1682 node.accept(visitor);
1686 //////////////////////////////////////////////////////////////////////
1687 // Timed animation installer
1688 //////////////////////////////////////////////////////////////////////
1692 class SGTimedAnimation::UpdateCallback : public osg::NodeCallback {
1694 UpdateCallback(const SGPropertyNode* configNode) :
1697 _duration_sec(configNode->getDoubleValue("duration-sec", 1)),
1698 _last_time_sec(SGLimitsd::max()),
1699 _use_personality(configNode->getBoolValue("use-personality", false))
1701 std::vector<SGSharedPtr<SGPropertyNode> > nodes;
1702 nodes = configNode->getChildren("branch-duration-sec");
1703 for (size_t i = 0; i < nodes.size(); ++i) {
1704 unsigned ind = nodes[ i ]->getIndex();
1705 while ( ind >= _durations.size() ) {
1706 _durations.push_back(DurationSpec(_duration_sec));
1708 SGPropertyNode_ptr rNode = nodes[i]->getChild("random");
1710 _durations[ind] = DurationSpec(nodes[ i ]->getDoubleValue());
1712 _durations[ind] = DurationSpec(rNode->getDoubleValue( "min", 0),
1713 rNode->getDoubleValue( "max", 1));
1717 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1719 assert(dynamic_cast<osg::Switch*>(node));
1720 osg::Switch* sw = static_cast<osg::Switch*>(node);
1722 unsigned nChildren = sw->getNumChildren();
1724 // blow up the durations vector to the required size
1725 while (_durations.size() < nChildren) {
1726 _durations.push_back(_duration_sec);
1728 // make sure the current index is an duration that really exists
1729 _current_index = _current_index % nChildren;
1731 // update the time and compute the current systems time value
1732 double t = nv->getFrameStamp()->getReferenceTime();
1733 if (_last_time_sec == SGLimitsd::max()) {
1736 double dt = t - _last_time_sec;
1737 if (_use_personality)
1738 dt *= 1 + 0.2*(0.5 - sg_random());
1743 double currentDuration = _durations[_current_index].get();
1744 while (currentDuration < _reminder) {
1745 _reminder -= currentDuration;
1746 _current_index = (_current_index + 1) % nChildren;
1747 currentDuration = _durations[_current_index].get();
1750 sw->setSingleChildOn(_current_index);
1756 struct DurationSpec {
1757 DurationSpec(double t) :
1758 minTime(SGMiscd::max(0.01, t)),
1759 maxTime(SGMiscd::max(0.01, t))
1761 DurationSpec(double t0, double t1) :
1762 minTime(SGMiscd::max(0.01, t0)),
1763 maxTime(SGMiscd::max(0.01, t1))
1766 { return minTime + sg_random()*(maxTime - minTime); }
1770 std::vector<DurationSpec> _durations;
1771 unsigned _current_index;
1773 double _duration_sec;
1774 double _last_time_sec;
1775 bool _use_personality;
1779 SGTimedAnimation::SGTimedAnimation(const SGPropertyNode* configNode,
1780 SGPropertyNode* modelRoot)
1781 : SGAnimation(configNode, modelRoot)
1786 SGTimedAnimation::createAnimationGroup(osg::Group& parent)
1788 osg::Switch* sw = new osg::Switch;
1789 sw->setName("timed animation node");
1790 sw->setUpdateCallback(new UpdateCallback(getConfig()));
1791 parent.addChild(sw);
1796 ////////////////////////////////////////////////////////////////////////
1797 // dynamically switch on/off shadows
1798 ////////////////////////////////////////////////////////////////////////
1800 class SGShadowAnimation::UpdateCallback : public osg::NodeCallback {
1802 UpdateCallback(const SGCondition* condition) :
1803 _condition(condition)
1805 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1807 if (_condition->test())
1808 node->setNodeMask( SG_NODEMASK_CASTSHADOW_BIT | node->getNodeMask());
1810 node->setNodeMask(~SG_NODEMASK_CASTSHADOW_BIT & node->getNodeMask());
1815 SGSharedPtr<const SGCondition> _condition;
1818 SGShadowAnimation::SGShadowAnimation(const SGPropertyNode* configNode,
1819 SGPropertyNode* modelRoot) :
1820 SGAnimation(configNode, modelRoot)
1825 SGShadowAnimation::createAnimationGroup(osg::Group& parent)
1827 SGSharedPtr<SGCondition const> condition = getCondition();
1831 osg::Group* group = new osg::Group;
1832 group->setName("shadow animation");
1833 group->setUpdateCallback(new UpdateCallback(condition));
1834 parent.addChild(group);
1839 ////////////////////////////////////////////////////////////////////////
1840 // Implementation of SGTexTransformAnimation
1841 ////////////////////////////////////////////////////////////////////////
1843 class SGTexTransformAnimation::Transform : public SGReferenced {
1848 virtual ~Transform()
1850 void setValue(double value)
1852 virtual void transform(osg::Matrix&) = 0;
1857 class SGTexTransformAnimation::Translation :
1858 public SGTexTransformAnimation::Transform {
1860 Translation(const SGVec3d& axis) :
1863 virtual void transform(osg::Matrix& matrix)
1866 set_translation(tmp, _value, _axis);
1867 matrix.preMult(tmp);
1873 class SGTexTransformAnimation::Rotation :
1874 public SGTexTransformAnimation::Transform {
1876 Rotation(const SGVec3d& axis, const SGVec3d& center) :
1880 virtual void transform(osg::Matrix& matrix)
1883 set_rotation(tmp, _value, _center, _axis);
1884 matrix.preMult(tmp);
1891 class SGTexTransformAnimation::UpdateCallback :
1892 public osg::StateAttribute::Callback {
1894 UpdateCallback(const SGCondition* condition) :
1895 _condition(condition)
1897 virtual void operator () (osg::StateAttribute* sa, osg::NodeVisitor*)
1899 if (!_condition || _condition->test()) {
1900 TransformList::const_iterator i;
1901 for (i = _transforms.begin(); i != _transforms.end(); ++i)
1902 i->transform->setValue(i->value->getValue());
1904 assert(dynamic_cast<osg::TexMat*>(sa));
1905 osg::TexMat* texMat = static_cast<osg::TexMat*>(sa);
1906 texMat->getMatrix().makeIdentity();
1907 TransformList::const_iterator i;
1908 for (i = _transforms.begin(); i != _transforms.end(); ++i)
1909 i->transform->transform(texMat->getMatrix());
1911 void appendTransform(Transform* transform, SGExpressiond* value)
1913 Entry entry = { transform, value };
1914 transform->transform(_matrix);
1915 _transforms.push_back(entry);
1920 SGSharedPtr<Transform> transform;
1921 SGSharedPtr<const SGExpressiond> value;
1923 typedef std::vector<Entry> TransformList;
1924 TransformList _transforms;
1925 SGSharedPtr<const SGCondition> _condition;
1926 osg::Matrix _matrix;
1929 SGTexTransformAnimation::SGTexTransformAnimation(const SGPropertyNode* configNode,
1930 SGPropertyNode* modelRoot) :
1931 SGAnimation(configNode, modelRoot)
1936 SGTexTransformAnimation::createAnimationGroup(osg::Group& parent)
1938 osg::Group* group = new osg::Group;
1939 group->setName("texture transform group");
1940 osg::StateSet* stateSet = group->getOrCreateStateSet();
1941 stateSet->setDataVariance(osg::Object::DYNAMIC);
1942 osg::TexMat* texMat = new osg::TexMat;
1943 UpdateCallback* updateCallback = new UpdateCallback(getCondition());
1944 // interpret the configs ...
1945 std::string type = getType();
1947 if (type == "textranslate") {
1948 appendTexTranslate(getConfig(), updateCallback);
1949 } else if (type == "texrotate") {
1950 appendTexRotate(getConfig(), updateCallback);
1951 } else if (type == "texmultiple") {
1952 std::vector<SGSharedPtr<SGPropertyNode> > transformConfigs;
1953 transformConfigs = getConfig()->getChildren("transform");
1954 for (unsigned i = 0; i < transformConfigs.size(); ++i) {
1955 std::string subtype = transformConfigs[i]->getStringValue("subtype", "");
1956 if (subtype == "textranslate")
1957 appendTexTranslate(transformConfigs[i], updateCallback);
1958 else if (subtype == "texrotate")
1959 appendTexRotate(transformConfigs[i], updateCallback);
1961 SG_LOG(SG_INPUT, SG_ALERT,
1962 "Ignoring unknown texture transform subtype");
1965 SG_LOG(SG_INPUT, SG_ALERT, "Ignoring unknown texture transform type");
1968 texMat->setUpdateCallback(updateCallback);
1969 stateSet->setTextureAttribute(0, texMat);
1970 parent.addChild(group);
1975 SGTexTransformAnimation::appendTexTranslate(const SGPropertyNode* config,
1976 UpdateCallback* updateCallback)
1978 std::string propertyName = config->getStringValue("property", "");
1979 SGSharedPtr<SGExpressiond> value;
1980 if (propertyName.empty())
1981 value = new SGConstExpression<double>(0);
1983 SGPropertyNode* inputProperty = getModelRoot()->getNode(propertyName, true);
1984 value = new SGPropertyExpression<double>(inputProperty);
1987 SGInterpTable* table = read_interpolation_table(config);
1989 value = new SGInterpTableExpression<double>(value, table);
1990 double biasValue = config->getDoubleValue("bias", 0);
1992 value = new SGBiasExpression<double>(value, biasValue);
1993 value = new SGStepExpression<double>(value,
1994 config->getDoubleValue("step", 0),
1995 config->getDoubleValue("scroll", 0));
1996 value = value->simplify();
1998 double biasValue = config->getDoubleValue("bias", 0);
2000 value = new SGBiasExpression<double>(value, biasValue);
2001 value = new SGStepExpression<double>(value,
2002 config->getDoubleValue("step", 0),
2003 config->getDoubleValue("scroll", 0));
2004 value = read_offset_factor(config, value, "factor", "offset");
2006 if (config->hasChild("min") || config->hasChild("max")) {
2007 double minClip = config->getDoubleValue("min", -SGLimitsd::max());
2008 double maxClip = config->getDoubleValue("max", SGLimitsd::max());
2009 value = new SGClipExpression<double>(value, minClip, maxClip);
2011 value = value->simplify();
2013 SGVec3d axis(config->getDoubleValue("axis/x", 0),
2014 config->getDoubleValue("axis/y", 0),
2015 config->getDoubleValue("axis/z", 0));
2016 Translation* translation;
2017 translation = new Translation(normalize(axis));
2018 translation->setValue(config->getDoubleValue("starting-position", 0));
2019 updateCallback->appendTransform(translation, value);
2023 SGTexTransformAnimation::appendTexRotate(const SGPropertyNode* config,
2024 UpdateCallback* updateCallback)
2026 std::string propertyName = config->getStringValue("property", "");
2027 SGSharedPtr<SGExpressiond> value;
2028 if (propertyName.empty())
2029 value = new SGConstExpression<double>(0);
2031 SGPropertyNode* inputProperty = getModelRoot()->getNode(propertyName, true);
2032 value = new SGPropertyExpression<double>(inputProperty);
2035 SGInterpTable* table = read_interpolation_table(config);
2037 value = new SGInterpTableExpression<double>(value, table);
2038 double biasValue = config->getDoubleValue("bias", 0);
2040 value = new SGBiasExpression<double>(value, biasValue);
2041 value = new SGStepExpression<double>(value,
2042 config->getDoubleValue("step", 0),
2043 config->getDoubleValue("scroll", 0));
2044 value = value->simplify();
2046 double biasValue = config->getDoubleValue("bias", 0);
2048 value = new SGBiasExpression<double>(value, biasValue);
2049 value = new SGStepExpression<double>(value,
2050 config->getDoubleValue("step", 0),
2051 config->getDoubleValue("scroll", 0));
2052 value = read_offset_factor(config, value, "factor", "offset-deg");
2054 if (config->hasChild("min-deg") || config->hasChild("max-deg")) {
2055 double minClip = config->getDoubleValue("min-deg", -SGLimitsd::max());
2056 double maxClip = config->getDoubleValue("max-deg", SGLimitsd::max());
2057 value = new SGClipExpression<double>(value, minClip, maxClip);
2059 value = value->simplify();
2061 SGVec3d axis(config->getDoubleValue("axis/x", 0),
2062 config->getDoubleValue("axis/y", 0),
2063 config->getDoubleValue("axis/z", 0));
2064 SGVec3d center(config->getDoubleValue("center/x", 0),
2065 config->getDoubleValue("center/y", 0),
2066 config->getDoubleValue("center/z", 0));
2068 rotation = new Rotation(normalize(axis), center);
2069 rotation->setValue(config->getDoubleValue("starting-position-deg", 0));
2070 updateCallback->appendTransform(rotation, value);
2074 ////////////////////////////////////////////////////////////////////////
2075 // Implementation of SGPickAnimation
2076 ////////////////////////////////////////////////////////////////////////
2078 class SGPickAnimation::PickCallback : public SGPickCallback {
2080 PickCallback(const SGPropertyNode* configNode,
2081 SGPropertyNode* modelRoot) :
2082 _repeatable(configNode->getBoolValue("repeatable", false)),
2083 _repeatInterval(configNode->getDoubleValue("interval-sec", 0.1))
2085 SG_LOG(SG_INPUT, SG_DEBUG, "Reading all bindings");
2086 std::vector<SGPropertyNode_ptr> bindings;
2088 bindings = configNode->getChildren("button");
2089 for (unsigned int i = 0; i < bindings.size(); ++i) {
2090 _buttons.push_back( bindings[i]->getIntValue() );
2092 bindings = configNode->getChildren("binding");
2093 for (unsigned int i = 0; i < bindings.size(); ++i) {
2094 _bindingsDown.push_back(new SGBinding(bindings[i], modelRoot));
2097 const SGPropertyNode* upNode = configNode->getChild("mod-up");
2100 bindings = upNode->getChildren("binding");
2101 for (unsigned int i = 0; i < bindings.size(); ++i) {
2102 _bindingsUp.push_back(new SGBinding(bindings[i], modelRoot));
2105 virtual bool buttonPressed(int button, const Info&)
2108 for( std::vector<int>::iterator it = _buttons.begin(); it != _buttons.end(); ++it ) {
2109 if( *it == button ) {
2116 SGBindingList::const_iterator i;
2117 for (i = _bindingsDown.begin(); i != _bindingsDown.end(); ++i)
2119 _repeatTime = -_repeatInterval; // anti-bobble: delay start of repeat
2122 virtual void buttonReleased(void)
2124 SGBindingList::const_iterator i;
2125 for (i = _bindingsUp.begin(); i != _bindingsUp.end(); ++i)
2128 virtual void update(double dt)
2134 while (_repeatInterval < _repeatTime) {
2135 _repeatTime -= _repeatInterval;
2136 SGBindingList::const_iterator i;
2137 for (i = _bindingsDown.begin(); i != _bindingsDown.end(); ++i)
2142 SGBindingList _bindingsDown;
2143 SGBindingList _bindingsUp;
2144 std::vector<int> _buttons;
2146 double _repeatInterval;
2150 class VncVisitor : public osg::NodeVisitor {
2152 VncVisitor(double x, double y, int mask) :
2153 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
2154 _texX(x), _texY(y), _mask(mask), _done(false)
2156 SG_LOG(SG_INPUT, SG_DEBUG, "VncVisitor constructor "
2157 << x << "," << y << " mask " << mask);
2160 virtual void apply(osg::Node &node)
2162 // Some nodes have state sets attached
2163 touchStateSet(node.getStateSet());
2167 // See whether we are a geode worth exploring
2168 osg::Geode *g = dynamic_cast<osg::Geode*>(&node);
2170 // Go find all its drawables
2171 int i = g->getNumDrawables();
2173 osg::Drawable *d = g->getDrawable(i);
2174 if (d) touchDrawable(*d);
2176 // Out of optimism, do the same for EffectGeode
2177 simgear::EffectGeode *eg = dynamic_cast<simgear::EffectGeode*>(&node);
2179 for (simgear::EffectGeode::DrawablesIterator di = eg->drawablesBegin();
2180 di != eg->drawablesEnd(); di++) {
2181 touchDrawable(**di);
2183 // Now see whether the EffectGeode has an Effect
2184 simgear::Effect *e = eg->getEffect();
2186 touchStateSet(e->getDefaultStateSet());
2190 inline void touchDrawable(osg::Drawable &d)
2192 osg::StateSet *ss = d.getStateSet();
2196 void touchStateSet(osg::StateSet *ss)
2199 osg::StateAttribute *sa = ss->getTextureAttribute(0,
2200 osg::StateAttribute::TEXTURE);
2202 osg::Texture *t = sa->asTexture();
2204 osg::Image *img = t->getImage(0);
2207 int pixX = _texX * img->s();
2208 int pixY = _texY * img->t();
2209 _done = img->sendPointerEvent(pixX, pixY, _mask);
2210 SG_LOG(SG_INPUT, SG_DEBUG, "VncVisitor image said " << _done
2211 << " to coord " << pixX << "," << pixY);
2215 inline bool wasSuccessful()
2221 double _texX, _texY;
2227 class SGPickAnimation::VncCallback : public SGPickCallback {
2229 VncCallback(const SGPropertyNode* configNode,
2230 SGPropertyNode* modelRoot,
2234 SG_LOG(SG_INPUT, SG_DEBUG, "Configuring VNC callback");
2235 const char *cornernames[3] = {"top-left", "top-right", "bottom-left"};
2236 SGVec3d *cornercoords[3] = {&_topLeft, &_toRight, &_toDown};
2237 for (int c =0; c < 3; c++) {
2238 const SGPropertyNode* cornerNode = configNode->getChild(cornernames[c]);
2239 *cornercoords[c] = SGVec3d(
2240 cornerNode->getDoubleValue("x"),
2241 cornerNode->getDoubleValue("y"),
2242 cornerNode->getDoubleValue("z"));
2244 _toRight -= _topLeft;
2245 _toDown -= _topLeft;
2246 _squaredRight = dot(_toRight, _toRight);
2247 _squaredDown = dot(_toDown, _toDown);
2250 virtual bool buttonPressed(int button, const Info& info)
2252 SGVec3d loc(info.local);
2253 SG_LOG(SG_INPUT, SG_DEBUG, "VNC pressed " << button << ": " << loc);
2255 _x = dot(loc, _toRight) / _squaredRight;
2256 _y = dot(loc, _toDown) / _squaredDown;
2257 if (_x<0) _x = 0; else if (_x > 1) _x = 1;
2258 if (_y<0) _y = 0; else if (_y > 1) _y = 1;
2259 VncVisitor vv(_x, _y, 1 << button);
2261 return vv.wasSuccessful();
2264 virtual void buttonReleased(void)
2266 SG_LOG(SG_INPUT, SG_DEBUG, "VNC release");
2267 VncVisitor vv(_x, _y, 0);
2270 virtual void update(double dt)
2275 osg::ref_ptr<osg::Group> _node;
2276 SGVec3d _topLeft, _toRight, _toDown;
2277 double _squaredRight, _squaredDown;
2280 SGPickAnimation::SGPickAnimation(const SGPropertyNode* configNode,
2281 SGPropertyNode* modelRoot) :
2282 SGAnimation(configNode, modelRoot)
2288 Mutex colorModeUniformMutex;
2289 osg::ref_ptr<osg::Uniform> colorModeUniform;
2293 SGPickAnimation::createAnimationGroup(osg::Group& parent)
2295 osg::Group* commonGroup = new osg::Group;
2297 // Contains the normal geometry that is interactive
2298 osg::ref_ptr<osg::Group> normalGroup = new osg::Group;
2299 normalGroup->setName("pick normal group");
2300 normalGroup->addChild(commonGroup);
2302 // Used to render the geometry with just yellow edges
2303 osg::Group* highlightGroup = new osg::Group;
2304 highlightGroup->setName("pick highlight group");
2305 highlightGroup->setNodeMask(SG_NODEMASK_PICK_BIT);
2306 highlightGroup->addChild(commonGroup);
2307 SGSceneUserData* ud;
2308 ud = SGSceneUserData::getOrCreateSceneUserData(commonGroup);
2310 // add actions that become macro and command invocations
2311 std::vector<SGPropertyNode_ptr> actions;
2312 actions = getConfig()->getChildren("action");
2313 for (unsigned int i = 0; i < actions.size(); ++i)
2314 ud->addPickCallback(new PickCallback(actions[i], getModelRoot()));
2315 // Look for the VNC sessions that want raw mouse input
2316 actions = getConfig()->getChildren("vncaction");
2317 for (unsigned int i = 0; i < actions.size(); ++i)
2318 ud->addPickCallback(new VncCallback(actions[i], getModelRoot(),
2321 // prepare a state set that paints the edges of this object yellow
2322 // The material and texture attributes are set with
2323 // OVERRIDE|PROTECTED in case there is a material animation on a
2324 // higher node in the scene graph, which would have its material
2325 // attribute set with OVERRIDE.
2326 osg::StateSet* stateSet = highlightGroup->getOrCreateStateSet();
2327 osg::Texture2D* white = StateAttributeFactory::instance()->getWhiteTexture();
2328 stateSet->setTextureAttributeAndModes(0, white,
2329 (osg::StateAttribute::ON
2330 | osg::StateAttribute::OVERRIDE
2331 | osg::StateAttribute::PROTECTED));
2332 osg::PolygonOffset* polygonOffset = new osg::PolygonOffset;
2333 polygonOffset->setFactor(-1);
2334 polygonOffset->setUnits(-1);
2335 stateSet->setAttribute(polygonOffset, osg::StateAttribute::OVERRIDE);
2336 stateSet->setMode(GL_POLYGON_OFFSET_LINE,
2337 osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
2338 osg::PolygonMode* polygonMode = new osg::PolygonMode;
2339 polygonMode->setMode(osg::PolygonMode::FRONT_AND_BACK,
2340 osg::PolygonMode::LINE);
2341 stateSet->setAttribute(polygonMode, osg::StateAttribute::OVERRIDE);
2342 osg::Material* material = new osg::Material;
2343 material->setColorMode(osg::Material::OFF);
2344 material->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(0, 0, 0, 1));
2345 // XXX Alpha < 1.0 in the diffuse material value is a signal to the
2346 // default shader to take the alpha value from the material value
2347 // and not the glColor. In many cases the pick animation geometry is
2348 // transparent, so the outline would not be visible without this hack.
2349 material->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(0, 0, 0, .95));
2350 material->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(1, 1, 0, 1));
2351 material->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0, 0, 0, 0));
2352 stateSet->setAttribute(
2353 material, osg::StateAttribute::OVERRIDE | osg::StateAttribute::PROTECTED);
2354 // The default shader has a colorMode uniform that mimics the
2355 // behavior of Material color mode.
2356 osg::Uniform* cmUniform = 0;
2358 ScopedLock<Mutex> lock(colorModeUniformMutex);
2359 if (!colorModeUniform.valid()) {
2360 colorModeUniform = new osg::Uniform(osg::Uniform::INT, "colorMode");
2361 colorModeUniform->set(0); // MODE_OFF
2362 colorModeUniform->setDataVariance(osg::Object::STATIC);
2364 cmUniform = colorModeUniform.get();
2366 stateSet->addUniform(cmUniform,
2367 osg::StateAttribute::OVERRIDE | osg::StateAttribute::ON);
2368 // Only add normal geometry if configured
2369 if (getConfig()->getBoolValue("visible", true))
2370 parent.addChild(normalGroup.get());
2371 parent.addChild(highlightGroup);