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>
25 #include <osg/PolygonMode>
26 #include <osg/PolygonOffset>
27 #include <osg/StateSet>
30 #include <osg/Texture2D>
31 #include <osg/Transform>
32 #include <osgDB/ReadFile>
33 #include <osgDB/Registry>
34 #include <osgDB/Input>
35 #include <osgDB/ParameterOutput>
38 #include <simgear/math/interpolater.hxx>
39 #include <simgear/props/condition.hxx>
40 #include <simgear/props/props.hxx>
41 #include <simgear/structure/SGBinding.hxx>
42 #include <simgear/scene/material/EffectGeode.hxx>
43 #include <simgear/scene/util/SGNodeMasks.hxx>
44 #include <simgear/scene/util/SGSceneUserData.hxx>
45 #include <simgear/scene/util/SGStateAttributeVisitor.hxx>
47 #include "animation.hxx"
50 #include "SGTranslateTransform.hxx"
51 #include "SGMaterialAnimation.hxx"
52 #include "SGRotateTransform.hxx"
53 #include "SGScaleTransform.hxx"
54 #include "SGInteractionAnimation.hxx"
56 using OpenThreads::Mutex;
57 using OpenThreads::ReentrantMutex;
58 using OpenThreads::ScopedLock;
61 ////////////////////////////////////////////////////////////////////////
62 // Static utility functions.
63 ////////////////////////////////////////////////////////////////////////
66 * Set up the transform matrix for a spin or rotation.
69 set_rotation (osg::Matrix &matrix, double position_deg,
70 const SGVec3d ¢er, const SGVec3d &axis)
72 double temp_angle = -SGMiscd::deg2rad(position_deg);
74 double s = sin(temp_angle);
75 double c = cos(temp_angle);
78 // axis was normalized at load time
79 // hint to the compiler to put these into FP registers
84 matrix(0, 0) = t * x * x + c ;
85 matrix(0, 1) = t * y * x - s * z ;
86 matrix(0, 2) = t * z * x + s * y ;
89 matrix(1, 0) = t * x * y + s * z ;
90 matrix(1, 1) = t * y * y + c ;
91 matrix(1, 2) = t * z * y - s * x ;
94 matrix(2, 0) = t * x * z - s * y ;
95 matrix(2, 1) = t * y * z + s * x ;
96 matrix(2, 2) = t * z * z + c ;
99 // hint to the compiler to put these into FP registers
104 matrix(3, 0) = x - x*matrix(0, 0) - y*matrix(1, 0) - z*matrix(2, 0);
105 matrix(3, 1) = y - x*matrix(0, 1) - y*matrix(1, 1) - z*matrix(2, 1);
106 matrix(3, 2) = z - x*matrix(0, 2) - y*matrix(1, 2) - z*matrix(2, 2);
111 * Set up the transform matrix for a translation.
114 set_translation (osg::Matrix &matrix, double position_m, const SGVec3d &axis)
116 SGVec3d xyz = axis * position_m;
117 matrix.makeIdentity();
118 matrix(3, 0) = xyz[0];
119 matrix(3, 1) = xyz[1];
120 matrix(3, 2) = xyz[2];
124 * Read an interpolation table from properties.
126 static SGInterpTable *
127 read_interpolation_table(const SGPropertyNode* props)
129 const SGPropertyNode* table_node = props->getNode("interpolation");
132 return new SGInterpTable(table_node);
136 unit_string(const char* value, const char* unit)
138 return std::string(value) + unit;
141 class SGPersonalityScaleOffsetExpression : public SGUnaryExpression<double> {
143 SGPersonalityScaleOffsetExpression(SGExpression<double>* expr,
144 SGPropertyNode const* config,
145 const std::string& scalename,
146 const std::string& offsetname,
148 double defOffset = 0) :
149 SGUnaryExpression<double>(expr),
150 _scale(config, scalename.c_str(), defScale),
151 _offset(config, offsetname.c_str(), defOffset)
153 void setScale(double scale)
155 void setOffset(double offset)
156 { _offset = offset; }
158 virtual void eval(double& value, const simgear::expression::Binding* b) const
162 value = _offset + _scale*getOperand()->getValue(b);
165 virtual bool isConst() const { return false; }
168 mutable SGPersonalityParameter<double> _scale;
169 mutable SGPersonalityParameter<double> _offset;
173 static SGExpressiond*
174 read_factor_offset(const SGPropertyNode* configNode, SGExpressiond* expr,
175 const std::string& factor, const std::string& offset)
177 double factorValue = configNode->getDoubleValue(factor, 1);
178 if (factorValue != 1)
179 expr = new SGScaleExpression<double>(expr, factorValue);
180 double offsetValue = configNode->getDoubleValue(offset, 0);
181 if (offsetValue != 0)
182 expr = new SGBiasExpression<double>(expr, offsetValue);
186 static SGExpressiond*
187 read_offset_factor(const SGPropertyNode* configNode, SGExpressiond* expr,
188 const std::string& factor, const std::string& offset)
190 double offsetValue = configNode->getDoubleValue(offset, 0);
191 if (offsetValue != 0)
192 expr = new SGBiasExpression<double>(expr, offsetValue);
193 double factorValue = configNode->getDoubleValue(factor, 1);
194 if (factorValue != 1)
195 expr = new SGScaleExpression<double>(expr, factorValue);
200 read_value(const SGPropertyNode* configNode, SGPropertyNode* modelRoot,
201 const char* unit, double defMin, double defMax)
203 SGExpression<double>* value = 0;
205 std::string inputPropertyName = configNode->getStringValue("property", "");
206 if (inputPropertyName.empty()) {
207 std::string spos = unit_string("starting-position", unit);
208 double initPos = configNode->getDoubleValue(spos, 0);
209 value = new SGConstExpression<double>(initPos);
211 SGPropertyNode* inputProperty;
212 inputProperty = modelRoot->getNode(inputPropertyName, true);
213 value = new SGPropertyExpression<double>(inputProperty);
216 SGInterpTable* interpTable = read_interpolation_table(configNode);
218 return new SGInterpTableExpression<double>(value, interpTable);
220 std::string offset = unit_string("offset", unit);
221 std::string min = unit_string("min", unit);
222 std::string max = unit_string("max", unit);
224 if (configNode->getBoolValue("use-personality", false)) {
225 value = new SGPersonalityScaleOffsetExpression(value, configNode,
228 value = read_factor_offset(configNode, value, "factor", offset);
231 double minClip = configNode->getDoubleValue(min, defMin);
232 double maxClip = configNode->getDoubleValue(max, defMax);
233 if (minClip > SGMiscd::min(SGLimitsd::min(), -SGLimitsd::max()) ||
234 maxClip < SGLimitsd::max())
235 value = new SGClipExpression<double>(value, minClip, maxClip);
243 ////////////////////////////////////////////////////////////////////////
244 // Animation installer
245 ////////////////////////////////////////////////////////////////////////
247 class SGAnimation::RemoveModeVisitor : public SGStateAttributeVisitor {
249 RemoveModeVisitor(osg::StateAttribute::GLMode mode) :
252 virtual void apply(osg::StateSet* stateSet)
256 stateSet->removeMode(_mode);
259 osg::StateAttribute::GLMode _mode;
262 class SGAnimation::RemoveAttributeVisitor : public SGStateAttributeVisitor {
264 RemoveAttributeVisitor(osg::StateAttribute::Type type) :
267 virtual void apply(osg::StateSet* stateSet)
271 while (stateSet->getAttribute(_type)) {
272 stateSet->removeAttribute(_type);
276 osg::StateAttribute::Type _type;
279 class SGAnimation::RemoveTextureModeVisitor : public SGStateAttributeVisitor {
281 RemoveTextureModeVisitor(unsigned unit, osg::StateAttribute::GLMode mode) :
285 virtual void apply(osg::StateSet* stateSet)
289 stateSet->removeTextureMode(_unit, _mode);
293 osg::StateAttribute::GLMode _mode;
296 class SGAnimation::RemoveTextureAttributeVisitor :
297 public SGStateAttributeVisitor {
299 RemoveTextureAttributeVisitor(unsigned unit,
300 osg::StateAttribute::Type type) :
304 virtual void apply(osg::StateSet* stateSet)
308 while (stateSet->getTextureAttribute(_unit, _type)) {
309 stateSet->removeTextureAttribute(_unit, _type);
314 osg::StateAttribute::Type _type;
317 class SGAnimation::BinToInheritVisitor : public SGStateAttributeVisitor {
319 virtual void apply(osg::StateSet* stateSet)
323 stateSet->setRenderBinToInherit();
327 class SGAnimation::DrawableCloneVisitor : public osg::NodeVisitor {
329 DrawableCloneVisitor() :
330 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
332 void apply(osg::Geode& geode)
334 for (unsigned i = 0 ; i < geode.getNumDrawables(); ++i) {
335 osg::CopyOp copyOp(osg::CopyOp::DEEP_COPY_ALL &
336 ~osg::CopyOp::DEEP_COPY_TEXTURES);
337 geode.setDrawable(i, copyOp(geode.getDrawable(i)));
344 // Set all drawables to not use display lists. OSG will use
345 // glDrawArrays instead.
346 struct DoDrawArraysVisitor : public osg::NodeVisitor {
347 DoDrawArraysVisitor() :
348 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
350 void apply(osg::Geode& geode)
355 for (int i = 0; i < (int)geode.getNumDrawables(); ++i)
356 geode.getDrawable(i)->setUseDisplayList(false);
361 SGAnimation::SGAnimation(const SGPropertyNode* configNode,
362 SGPropertyNode* modelRoot) :
363 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
365 _configNode(configNode),
366 _modelRoot(modelRoot)
368 _name = configNode->getStringValue("name", "");
369 _enableHOT = configNode->getBoolValue("enable-hot", true);
370 _disableShadow = configNode->getBoolValue("disable-shadow", false);
371 std::vector<SGPropertyNode_ptr> objectNames =
372 configNode->getChildren("object-name");
373 for (unsigned i = 0; i < objectNames.size(); ++i)
374 _objectNames.push_back(objectNames[i]->getStringValue());
377 SGAnimation::~SGAnimation()
382 SG_LOG(SG_IO, SG_ALERT, "Could not find at least one of the following"
383 " objects for animation:\n");
384 std::list<std::string>::const_iterator i;
385 for (i = _objectNames.begin(); i != _objectNames.end(); ++i)
386 SG_LOG(SG_IO, SG_ALERT, *i << "\n");
390 SGAnimation::animate(osg::Node* node, const SGPropertyNode* configNode,
391 SGPropertyNode* modelRoot,
392 const osgDB::ReaderWriter::Options* options)
394 std::string type = configNode->getStringValue("type", "none");
395 if (type == "alpha-test") {
396 SGAlphaTestAnimation animInst(configNode, modelRoot);
397 animInst.apply(node);
398 } else if (type == "billboard") {
399 SGBillboardAnimation animInst(configNode, modelRoot);
400 animInst.apply(node);
401 } else if (type == "blend") {
402 SGBlendAnimation animInst(configNode, modelRoot);
403 animInst.apply(node);
404 } else if (type == "dist-scale") {
405 SGDistScaleAnimation animInst(configNode, modelRoot);
406 animInst.apply(node);
407 } else if (type == "flash") {
408 SGFlashAnimation animInst(configNode, modelRoot);
409 animInst.apply(node);
410 } else if (type == "interaction") {
411 SGInteractionAnimation animInst(configNode, modelRoot);
412 animInst.apply(node);
413 } else if (type == "material") {
414 SGMaterialAnimation animInst(configNode, modelRoot, options);
415 animInst.apply(node);
416 } else if (type == "noshadow") {
417 SGShadowAnimation animInst(configNode, modelRoot);
418 animInst.apply(node);
419 } else if (type == "pick") {
420 SGPickAnimation animInst(configNode, modelRoot);
421 animInst.apply(node);
422 } else if (type == "range") {
423 SGRangeAnimation animInst(configNode, modelRoot);
424 animInst.apply(node);
425 } else if (type == "rotate" || type == "spin") {
426 SGRotateAnimation animInst(configNode, modelRoot);
427 animInst.apply(node);
428 } else if (type == "scale") {
429 SGScaleAnimation animInst(configNode, modelRoot);
430 animInst.apply(node);
431 } else if (type == "select") {
432 SGSelectAnimation animInst(configNode, modelRoot);
433 animInst.apply(node);
434 } else if (type == "shader") {
435 SGShaderAnimation animInst(configNode, modelRoot, options);
436 animInst.apply(node);
437 } else if (type == "textranslate" || type == "texrotate" ||
438 type == "texmultiple") {
439 SGTexTransformAnimation animInst(configNode, modelRoot);
440 animInst.apply(node);
441 } else if (type == "timed") {
442 SGTimedAnimation animInst(configNode, modelRoot);
443 animInst.apply(node);
444 } else if (type == "translate") {
445 SGTranslateAnimation animInst(configNode, modelRoot);
446 animInst.apply(node);
447 } else if (type == "null" || type == "none" || type.empty()) {
448 SGGroupAnimation animInst(configNode, modelRoot);
449 animInst.apply(node);
458 SGAnimation::apply(osg::Node* node)
460 // duh what a special case ...
461 if (_objectNames.empty()) {
462 osg::Group* group = node->asGroup();
464 osg::ref_ptr<osg::Group> animationGroup;
465 installInGroup(std::string(), *group, animationGroup);
472 SGAnimation::install(osg::Node& node)
476 node.setNodeMask( SG_NODEMASK_TERRAIN_BIT | node.getNodeMask());
478 node.setNodeMask(~SG_NODEMASK_TERRAIN_BIT & node.getNodeMask());
480 node.setNodeMask( SG_NODEMASK_CASTSHADOW_BIT | node.getNodeMask());
482 node.setNodeMask(~SG_NODEMASK_CASTSHADOW_BIT & node.getNodeMask());
486 SGAnimation::createAnimationGroup(osg::Group& parent)
488 // default implementation, we do not need a new group
489 // for every animation type. Usually animations that just change
490 // the StateSet of some parts of the model
495 SGAnimation::apply(osg::Group& group)
497 // the trick is to first traverse the children and then
498 // possibly splice in a new group node if required.
499 // Else we end up in a recursive loop where we infinitly insert new
503 // Note that this algorithm preserves the order of the child objects
504 // like they appear in the object-name tags.
505 // The timed animations require this
506 osg::ref_ptr<osg::Group> animationGroup;
507 std::list<std::string>::const_iterator nameIt;
508 for (nameIt = _objectNames.begin(); nameIt != _objectNames.end(); ++nameIt)
509 installInGroup(*nameIt, group, animationGroup);
513 SGAnimation::installInGroup(const std::string& name, osg::Group& group,
514 osg::ref_ptr<osg::Group>& animationGroup)
516 int i = group.getNumChildren() - 1;
517 for (; 0 <= i; --i) {
518 osg::Node* child = group.getChild(i);
520 // Check if this one is already processed
521 if (std::find(_installedAnimations.begin(),
522 _installedAnimations.end(), child)
523 != _installedAnimations.end())
526 if (name.empty() || child->getName() == name) {
527 // fire the installation of the animation
530 // create a group node on demand
531 if (!animationGroup.valid()) {
532 animationGroup = createAnimationGroup(group);
533 // Animation type that does not require a new group,
534 // in this case we can stop and look for the next object
535 if (animationGroup.valid() && !_name.empty())
536 animationGroup->setName(_name);
538 if (animationGroup.valid()) {
539 animationGroup->addChild(child);
540 group.removeChild(i);
543 // store that we already have processed this child node
544 // We can hit this one twice if an animation references some
545 // part of a subtree twice
546 _installedAnimations.push_back(child);
552 SGAnimation::removeMode(osg::Node& node, osg::StateAttribute::GLMode mode)
554 RemoveModeVisitor visitor(mode);
555 node.accept(visitor);
559 SGAnimation::removeAttribute(osg::Node& node, osg::StateAttribute::Type type)
561 RemoveAttributeVisitor visitor(type);
562 node.accept(visitor);
566 SGAnimation::removeTextureMode(osg::Node& node, unsigned unit,
567 osg::StateAttribute::GLMode mode)
569 RemoveTextureModeVisitor visitor(unit, mode);
570 node.accept(visitor);
574 SGAnimation::removeTextureAttribute(osg::Node& node, unsigned unit,
575 osg::StateAttribute::Type type)
577 RemoveTextureAttributeVisitor visitor(unit, type);
578 node.accept(visitor);
582 SGAnimation::setRenderBinToInherit(osg::Node& node)
584 BinToInheritVisitor visitor;
585 node.accept(visitor);
589 SGAnimation::cloneDrawables(osg::Node& node)
591 DrawableCloneVisitor visitor;
592 node.accept(visitor);
596 SGAnimation::getCondition() const
598 const SGPropertyNode* conditionNode = _configNode->getChild("condition");
601 return sgReadCondition(_modelRoot, conditionNode);
606 ////////////////////////////////////////////////////////////////////////
607 // Implementation of null animation
608 ////////////////////////////////////////////////////////////////////////
610 // Ok, that is to build a subgraph from different other
611 // graph nodes. I guess that this stems from the time where modellers
612 // could not build hierarchical trees ...
613 SGGroupAnimation::SGGroupAnimation(const SGPropertyNode* configNode,
614 SGPropertyNode* modelRoot):
615 SGAnimation(configNode, modelRoot)
620 SGGroupAnimation::createAnimationGroup(osg::Group& parent)
622 osg::Group* group = new osg::Group;
623 parent.addChild(group);
628 ////////////////////////////////////////////////////////////////////////
629 // Implementation of translate animation
630 ////////////////////////////////////////////////////////////////////////
632 class SGTranslateAnimation::UpdateCallback : public osg::NodeCallback {
634 UpdateCallback(SGCondition const* condition,
635 SGExpressiond const* animationValue) :
636 _condition(condition),
637 _animationValue(animationValue)
639 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
641 if (!_condition || _condition->test()) {
642 SGTranslateTransform* transform;
643 transform = static_cast<SGTranslateTransform*>(node);
644 transform->setValue(_animationValue->getValue());
649 SGSharedPtr<SGCondition const> _condition;
650 SGSharedPtr<SGExpressiond const> _animationValue;
653 SGTranslateAnimation::SGTranslateAnimation(const SGPropertyNode* configNode,
654 SGPropertyNode* modelRoot) :
655 SGAnimation(configNode, modelRoot)
657 _condition = getCondition();
658 SGSharedPtr<SGExpressiond> value;
659 value = read_value(configNode, modelRoot, "-m",
660 -SGLimitsd::max(), SGLimitsd::max());
661 _animationValue = value->simplify();
663 _initialValue = _animationValue->getValue();
667 if (configNode->hasValue("axis/x1-m")) {
669 v1[0] = configNode->getDoubleValue("axis/x1-m", 0);
670 v1[1] = configNode->getDoubleValue("axis/y1-m", 0);
671 v1[2] = configNode->getDoubleValue("axis/z1-m", 0);
672 v2[0] = configNode->getDoubleValue("axis/x2-m", 0);
673 v2[1] = configNode->getDoubleValue("axis/y2-m", 0);
674 v2[2] = configNode->getDoubleValue("axis/z2-m", 0);
677 _axis[0] = configNode->getDoubleValue("axis/x", 0);
678 _axis[1] = configNode->getDoubleValue("axis/y", 0);
679 _axis[2] = configNode->getDoubleValue("axis/z", 0);
681 if (8*SGLimitsd::min() < norm(_axis))
682 _axis = normalize(_axis);
686 SGTranslateAnimation::createAnimationGroup(osg::Group& parent)
688 SGTranslateTransform* transform = new SGTranslateTransform;
689 transform->setName("translate animation");
690 if (_animationValue && !_animationValue->isConst()) {
691 UpdateCallback* uc = new UpdateCallback(_condition, _animationValue);
692 transform->setUpdateCallback(uc);
694 transform->setAxis(_axis);
695 transform->setValue(_initialValue);
696 parent.addChild(transform);
701 ////////////////////////////////////////////////////////////////////////
702 // Implementation of rotate/spin animation
703 ////////////////////////////////////////////////////////////////////////
705 class SGRotateAnimation::UpdateCallback : public osg::NodeCallback {
707 UpdateCallback(SGCondition const* condition,
708 SGExpressiond const* animationValue) :
709 _condition(condition),
710 _animationValue(animationValue)
712 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
714 if (!_condition || _condition->test()) {
715 SGRotateTransform* transform;
716 transform = static_cast<SGRotateTransform*>(node);
717 transform->setAngleDeg(_animationValue->getValue());
722 SGSharedPtr<SGCondition const> _condition;
723 SGSharedPtr<SGExpressiond const> _animationValue;
726 class SGRotateAnimation::SpinUpdateCallback : public osg::NodeCallback {
728 SpinUpdateCallback(SGCondition const* condition,
729 SGExpressiond const* animationValue) :
730 _condition(condition),
731 _animationValue(animationValue),
734 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
736 if (!_condition || _condition->test()) {
737 SGRotateTransform* transform;
738 transform = static_cast<SGRotateTransform*>(node);
740 double t = nv->getFrameStamp()->getReferenceTime();
745 double velocity_rpms = _animationValue->getValue()/60;
746 double angle = transform->getAngleDeg();
747 angle += dt*velocity_rpms*360;
748 angle -= 360*floor(angle/360);
749 transform->setAngleDeg(angle);
754 SGSharedPtr<SGCondition const> _condition;
755 SGSharedPtr<SGExpressiond const> _animationValue;
759 SGRotateAnimation::SGRotateAnimation(const SGPropertyNode* configNode,
760 SGPropertyNode* modelRoot) :
761 SGAnimation(configNode, modelRoot)
763 std::string type = configNode->getStringValue("type", "");
764 _isSpin = (type == "spin");
766 _condition = getCondition();
767 SGSharedPtr<SGExpressiond> value;
768 value = read_value(configNode, modelRoot, "-deg",
769 -SGLimitsd::max(), SGLimitsd::max());
770 _animationValue = value->simplify();
772 _initialValue = _animationValue->getValue();
776 _center = SGVec3d::zeros();
777 if (configNode->hasValue("axis/x1-m")) {
779 v1[0] = configNode->getDoubleValue("axis/x1-m", 0);
780 v1[1] = configNode->getDoubleValue("axis/y1-m", 0);
781 v1[2] = configNode->getDoubleValue("axis/z1-m", 0);
782 v2[0] = configNode->getDoubleValue("axis/x2-m", 0);
783 v2[1] = configNode->getDoubleValue("axis/y2-m", 0);
784 v2[2] = configNode->getDoubleValue("axis/z2-m", 0);
785 _center = 0.5*(v1+v2);
788 _axis[0] = configNode->getDoubleValue("axis/x", 0);
789 _axis[1] = configNode->getDoubleValue("axis/y", 0);
790 _axis[2] = configNode->getDoubleValue("axis/z", 0);
792 if (8*SGLimitsd::min() < norm(_axis))
793 _axis = normalize(_axis);
795 _center[0] = configNode->getDoubleValue("center/x-m", _center[0]);
796 _center[1] = configNode->getDoubleValue("center/y-m", _center[1]);
797 _center[2] = configNode->getDoubleValue("center/z-m", _center[2]);
801 SGRotateAnimation::createAnimationGroup(osg::Group& parent)
803 SGRotateTransform* transform = new SGRotateTransform;
804 transform->setName("rotate animation");
806 SpinUpdateCallback* uc;
807 uc = new SpinUpdateCallback(_condition, _animationValue);
808 transform->setUpdateCallback(uc);
809 } else if (_animationValue || !_animationValue->isConst()) {
810 UpdateCallback* uc = new UpdateCallback(_condition, _animationValue);
811 transform->setUpdateCallback(uc);
813 transform->setCenter(_center);
814 transform->setAxis(_axis);
815 transform->setAngleDeg(_initialValue);
816 parent.addChild(transform);
821 ////////////////////////////////////////////////////////////////////////
822 // Implementation of scale animation
823 ////////////////////////////////////////////////////////////////////////
825 class SGScaleAnimation::UpdateCallback : public osg::NodeCallback {
827 UpdateCallback(const SGCondition* condition,
828 SGSharedPtr<const SGExpressiond> animationValue[3]) :
829 _condition(condition)
831 _animationValue[0] = animationValue[0];
832 _animationValue[1] = animationValue[1];
833 _animationValue[2] = animationValue[2];
835 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
837 if (!_condition || _condition->test()) {
838 SGScaleTransform* transform;
839 transform = static_cast<SGScaleTransform*>(node);
840 SGVec3d scale(_animationValue[0]->getValue(),
841 _animationValue[1]->getValue(),
842 _animationValue[2]->getValue());
843 transform->setScaleFactor(scale);
848 SGSharedPtr<SGCondition const> _condition;
849 SGSharedPtr<SGExpressiond const> _animationValue[3];
852 SGScaleAnimation::SGScaleAnimation(const SGPropertyNode* configNode,
853 SGPropertyNode* modelRoot) :
854 SGAnimation(configNode, modelRoot)
856 _condition = getCondition();
858 // default offset/factor for all directions
859 double offset = configNode->getDoubleValue("offset", 0);
860 double factor = configNode->getDoubleValue("factor", 1);
862 SGSharedPtr<SGExpressiond> inPropExpr;
864 std::string inputPropertyName;
865 inputPropertyName = configNode->getStringValue("property", "");
866 if (inputPropertyName.empty()) {
867 inPropExpr = new SGConstExpression<double>(0);
869 SGPropertyNode* inputProperty;
870 inputProperty = modelRoot->getNode(inputPropertyName, true);
871 inPropExpr = new SGPropertyExpression<double>(inputProperty);
874 SGInterpTable* interpTable = read_interpolation_table(configNode);
876 SGSharedPtr<SGExpressiond> value;
877 value = new SGInterpTableExpression<double>(inPropExpr, interpTable);
878 _animationValue[0] = value->simplify();
879 _animationValue[1] = value->simplify();
880 _animationValue[2] = value->simplify();
881 } else if (configNode->getBoolValue("use-personality", false)) {
882 SGSharedPtr<SGExpressiond> value;
883 value = new SGPersonalityScaleOffsetExpression(inPropExpr, configNode,
884 "x-factor", "x-offset",
886 double minClip = configNode->getDoubleValue("x-min", 0);
887 double maxClip = configNode->getDoubleValue("x-max", SGLimitsd::max());
888 value = new SGClipExpression<double>(value, minClip, maxClip);
889 _animationValue[0] = value->simplify();
891 value = new SGPersonalityScaleOffsetExpression(inPropExpr, configNode,
892 "y-factor", "y-offset",
894 minClip = configNode->getDoubleValue("y-min", 0);
895 maxClip = configNode->getDoubleValue("y-max", SGLimitsd::max());
896 value = new SGClipExpression<double>(value, minClip, maxClip);
897 _animationValue[1] = value->simplify();
899 value = new SGPersonalityScaleOffsetExpression(inPropExpr, configNode,
900 "z-factor", "z-offset",
902 minClip = configNode->getDoubleValue("z-min", 0);
903 maxClip = configNode->getDoubleValue("z-max", SGLimitsd::max());
904 value = new SGClipExpression<double>(value, minClip, maxClip);
905 _animationValue[2] = value->simplify();
907 SGSharedPtr<SGExpressiond> value;
908 value = read_factor_offset(configNode, inPropExpr, "x-factor", "x-offset");
909 double minClip = configNode->getDoubleValue("x-min", 0);
910 double maxClip = configNode->getDoubleValue("x-max", SGLimitsd::max());
911 value = new SGClipExpression<double>(value, minClip, maxClip);
912 _animationValue[0] = value->simplify();
914 value = read_factor_offset(configNode, inPropExpr, "y-factor", "y-offset");
915 minClip = configNode->getDoubleValue("y-min", 0);
916 maxClip = configNode->getDoubleValue("y-max", SGLimitsd::max());
917 value = new SGClipExpression<double>(value, minClip, maxClip);
918 _animationValue[1] = value->simplify();
920 value = read_factor_offset(configNode, inPropExpr, "z-factor", "z-offset");
921 minClip = configNode->getDoubleValue("z-min", 0);
922 maxClip = configNode->getDoubleValue("z-max", SGLimitsd::max());
923 value = new SGClipExpression<double>(value, minClip, maxClip);
924 _animationValue[2] = value->simplify();
926 _initialValue[0] = configNode->getDoubleValue("x-starting-scale", 1);
927 _initialValue[0] *= configNode->getDoubleValue("x-factor", factor);
928 _initialValue[0] += configNode->getDoubleValue("x-offset", offset);
929 _initialValue[1] = configNode->getDoubleValue("y-starting-scale", 1);
930 _initialValue[1] *= configNode->getDoubleValue("y-factor", factor);
931 _initialValue[1] += configNode->getDoubleValue("y-offset", offset);
932 _initialValue[2] = configNode->getDoubleValue("z-starting-scale", 1);
933 _initialValue[2] *= configNode->getDoubleValue("z-factor", factor);
934 _initialValue[2] += configNode->getDoubleValue("z-offset", offset);
935 _center[0] = configNode->getDoubleValue("center/x-m", 0);
936 _center[1] = configNode->getDoubleValue("center/y-m", 0);
937 _center[2] = configNode->getDoubleValue("center/z-m", 0);
941 SGScaleAnimation::createAnimationGroup(osg::Group& parent)
943 SGScaleTransform* transform = new SGScaleTransform;
944 transform->setName("scale animation");
945 transform->setCenter(_center);
946 transform->setScaleFactor(_initialValue);
947 UpdateCallback* uc = new UpdateCallback(_condition, _animationValue);
948 transform->setUpdateCallback(uc);
949 parent.addChild(transform);
954 // Don't create a new state state everytime we need GL_NORMALIZE!
958 Mutex normalizeMutex;
960 osg::StateSet* getNormalizeStateSet()
962 static osg::ref_ptr<osg::StateSet> normalizeStateSet;
963 ScopedLock<Mutex> lock(normalizeMutex);
964 if (!normalizeStateSet.valid()) {
965 normalizeStateSet = new osg::StateSet;
966 normalizeStateSet->setMode(GL_NORMALIZE, osg::StateAttribute::ON);
967 normalizeStateSet->setDataVariance(osg::Object::STATIC);
969 return normalizeStateSet.get();
973 ////////////////////////////////////////////////////////////////////////
974 // Implementation of dist scale animation
975 ////////////////////////////////////////////////////////////////////////
977 class SGDistScaleAnimation::Transform : public osg::Transform {
979 Transform() : _min_v(0.0), _max_v(0.0), _factor(0.0), _offset(0.0) {}
980 Transform(const Transform& rhs,
981 const osg::CopyOp& copyOp = osg::CopyOp::SHALLOW_COPY)
982 : osg::Transform(rhs, copyOp), _table(rhs._table), _center(rhs._center),
983 _min_v(rhs._min_v), _max_v(rhs._max_v), _factor(rhs._factor),
987 META_Node(simgear, SGDistScaleAnimation::Transform);
988 Transform(const SGPropertyNode* configNode)
990 setName(configNode->getStringValue("name", "dist scale animation"));
991 setReferenceFrame(RELATIVE_RF);
992 setStateSet(getNormalizeStateSet());
993 _factor = configNode->getFloatValue("factor", 1);
994 _offset = configNode->getFloatValue("offset", 0);
995 _min_v = configNode->getFloatValue("min", SGLimitsf::epsilon());
996 _max_v = configNode->getFloatValue("max", SGLimitsf::max());
997 _table = read_interpolation_table(configNode);
998 _center[0] = configNode->getFloatValue("center/x-m", 0);
999 _center[1] = configNode->getFloatValue("center/y-m", 0);
1000 _center[2] = configNode->getFloatValue("center/z-m", 0);
1002 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
1003 osg::NodeVisitor* nv) const
1005 osg::Matrix transform;
1006 double scale_factor = computeScaleFactor(nv);
1007 transform(0,0) = scale_factor;
1008 transform(1,1) = scale_factor;
1009 transform(2,2) = scale_factor;
1010 transform(3,0) = _center[0]*(1 - scale_factor);
1011 transform(3,1) = _center[1]*(1 - scale_factor);
1012 transform(3,2) = _center[2]*(1 - scale_factor);
1013 matrix.preMult(transform);
1017 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
1018 osg::NodeVisitor* nv) const
1020 double scale_factor = computeScaleFactor(nv);
1021 if (fabs(scale_factor) <= SGLimits<double>::min())
1023 osg::Matrix transform;
1024 double rScaleFactor = 1/scale_factor;
1025 transform(0,0) = rScaleFactor;
1026 transform(1,1) = rScaleFactor;
1027 transform(2,2) = rScaleFactor;
1028 transform(3,0) = _center[0]*(1 - rScaleFactor);
1029 transform(3,1) = _center[1]*(1 - rScaleFactor);
1030 transform(3,2) = _center[2]*(1 - rScaleFactor);
1031 matrix.postMult(transform);
1035 static bool writeLocalData(const osg::Object& obj, osgDB::Output& fw)
1037 const Transform& trans = static_cast<const Transform&>(obj);
1038 fw.indent() << "center " << trans._center << "\n";
1039 fw.indent() << "min_v " << trans._min_v << "\n";
1040 fw.indent() << "max_v " << trans._max_v << "\n";
1041 fw.indent() << "factor " << trans._factor << "\n";
1042 fw.indent() << "offset " << trans._offset << "\n";
1046 double computeScaleFactor(osg::NodeVisitor* nv) const
1051 double scale_factor = (toOsg(_center) - nv->getEyePoint()).length();
1053 scale_factor = _factor * scale_factor + _offset;
1055 scale_factor = _table->interpolate( scale_factor );
1057 if (scale_factor < _min_v)
1058 scale_factor = _min_v;
1059 if (scale_factor > _max_v)
1060 scale_factor = _max_v;
1062 return scale_factor;
1065 SGSharedPtr<SGInterpTable> _table;
1074 SGDistScaleAnimation::SGDistScaleAnimation(const SGPropertyNode* configNode,
1075 SGPropertyNode* modelRoot) :
1076 SGAnimation(configNode, modelRoot)
1081 SGDistScaleAnimation::createAnimationGroup(osg::Group& parent)
1083 Transform* transform = new Transform(getConfig());
1084 parent.addChild(transform);
1090 osgDB::RegisterDotOsgWrapperProxy distScaleAnimationTransformProxy
1092 new SGDistScaleAnimation::Transform,
1093 "SGDistScaleAnimation::Transform",
1094 "Object Node Transform SGDistScaleAnimation::Transform Group",
1096 &SGDistScaleAnimation::Transform::writeLocalData
1100 ////////////////////////////////////////////////////////////////////////
1101 // Implementation of flash animation
1102 ////////////////////////////////////////////////////////////////////////
1104 class SGFlashAnimation::Transform : public osg::Transform {
1106 Transform() : _power(0.0), _factor(0.0), _offset(0.0), _min_v(0.0),
1107 _max_v(0.0), _two_sides(false)
1110 Transform(const Transform& rhs,
1111 const osg::CopyOp& copyOp = osg::CopyOp::SHALLOW_COPY)
1112 : osg::Transform(rhs, copyOp), _center(rhs._center), _axis(rhs._axis),
1113 _power(rhs._power), _factor(rhs._factor), _offset(rhs._offset),
1114 _min_v(rhs._min_v), _max_v(rhs._max_v), _two_sides(rhs._two_sides)
1117 META_Node(simgear, SGFlashAnimation::Transform);
1119 Transform(const SGPropertyNode* configNode)
1121 setReferenceFrame(RELATIVE_RF);
1122 setName(configNode->getStringValue("name", "flash animation"));
1123 setStateSet(getNormalizeStateSet());
1125 _axis[0] = configNode->getFloatValue("axis/x", 0);
1126 _axis[1] = configNode->getFloatValue("axis/y", 0);
1127 _axis[2] = configNode->getFloatValue("axis/z", 1);
1130 _center[0] = configNode->getFloatValue("center/x-m", 0);
1131 _center[1] = configNode->getFloatValue("center/y-m", 0);
1132 _center[2] = configNode->getFloatValue("center/z-m", 0);
1134 _offset = configNode->getFloatValue("offset", 0);
1135 _factor = configNode->getFloatValue("factor", 1);
1136 _power = configNode->getFloatValue("power", 1);
1137 _two_sides = configNode->getBoolValue("two-sides", false);
1139 _min_v = configNode->getFloatValue("min", SGLimitsf::epsilon());
1140 _max_v = configNode->getFloatValue("max", 1);
1142 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
1143 osg::NodeVisitor* nv) const
1145 osg::Matrix transform;
1146 double scale_factor = computeScaleFactor(nv);
1147 transform(0,0) = scale_factor;
1148 transform(1,1) = scale_factor;
1149 transform(2,2) = scale_factor;
1150 transform(3,0) = _center[0]*(1 - scale_factor);
1151 transform(3,1) = _center[1]*(1 - scale_factor);
1152 transform(3,2) = _center[2]*(1 - scale_factor);
1153 matrix.preMult(transform);
1157 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
1158 osg::NodeVisitor* nv) const
1160 double scale_factor = computeScaleFactor(nv);
1161 if (fabs(scale_factor) <= SGLimits<double>::min())
1163 osg::Matrix transform;
1164 double rScaleFactor = 1/scale_factor;
1165 transform(0,0) = rScaleFactor;
1166 transform(1,1) = rScaleFactor;
1167 transform(2,2) = rScaleFactor;
1168 transform(3,0) = _center[0]*(1 - rScaleFactor);
1169 transform(3,1) = _center[1]*(1 - rScaleFactor);
1170 transform(3,2) = _center[2]*(1 - rScaleFactor);
1171 matrix.postMult(transform);
1175 static bool writeLocalData(const osg::Object& obj, osgDB::Output& fw)
1177 const Transform& trans = static_cast<const Transform&>(obj);
1178 fw.indent() << "center " << trans._center[0] << " "
1179 << trans._center[1] << " " << trans._center[2] << " " << "\n";
1180 fw.indent() << "axis " << trans._axis[0] << " "
1181 << trans._axis[1] << " " << trans._axis[2] << " " << "\n";
1182 fw.indent() << "power " << trans._power << " \n";
1183 fw.indent() << "min_v " << trans._min_v << "\n";
1184 fw.indent() << "max_v " << trans._max_v << "\n";
1185 fw.indent() << "factor " << trans._factor << "\n";
1186 fw.indent() << "offset " << trans._offset << "\n";
1187 fw.indent() << "twosides " << (trans._two_sides ? "true" : "false") << "\n";
1191 double computeScaleFactor(osg::NodeVisitor* nv) const
1196 osg::Vec3 localEyeToCenter = nv->getEyePoint() - _center;
1197 localEyeToCenter.normalize();
1199 double cos_angle = localEyeToCenter*_axis;
1200 double scale_factor = 0;
1201 if ( _two_sides && cos_angle < 0 )
1202 scale_factor = _factor * pow( -cos_angle, _power ) + _offset;
1203 else if ( cos_angle > 0 )
1204 scale_factor = _factor * pow( cos_angle, _power ) + _offset;
1206 if ( scale_factor < _min_v )
1207 scale_factor = _min_v;
1208 if ( scale_factor > _max_v )
1209 scale_factor = _max_v;
1211 return scale_factor;
1214 virtual osg::BoundingSphere computeBound() const
1216 // avoid being culled away by small feature culling
1217 osg::BoundingSphere bs = osg::Group::computeBound();
1218 bs.radius() *= _max_v;
1225 double _power, _factor, _offset, _min_v, _max_v;
1230 SGFlashAnimation::SGFlashAnimation(const SGPropertyNode* configNode,
1231 SGPropertyNode* modelRoot) :
1232 SGAnimation(configNode, modelRoot)
1237 SGFlashAnimation::createAnimationGroup(osg::Group& parent)
1239 Transform* transform = new Transform(getConfig());
1240 parent.addChild(transform);
1246 osgDB::RegisterDotOsgWrapperProxy flashAnimationTransformProxy
1248 new SGFlashAnimation::Transform,
1249 "SGFlashAnimation::Transform",
1250 "Object Node Transform SGFlashAnimation::Transform Group",
1252 &SGFlashAnimation::Transform::writeLocalData
1256 ////////////////////////////////////////////////////////////////////////
1257 // Implementation of billboard animation
1258 ////////////////////////////////////////////////////////////////////////
1260 class SGBillboardAnimation::Transform : public osg::Transform {
1262 Transform() : _spherical(true) {}
1263 Transform(const Transform& rhs,
1264 const osg::CopyOp& copyOp = osg::CopyOp::SHALLOW_COPY)
1265 : osg::Transform(rhs, copyOp), _spherical(rhs._spherical) {}
1266 META_Node(simgear, SGBillboardAnimation::Transform);
1267 Transform(const SGPropertyNode* configNode) :
1268 _spherical(configNode->getBoolValue("spherical", true))
1270 setReferenceFrame(RELATIVE_RF);
1271 setName(configNode->getStringValue("name", "billboard animation"));
1273 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
1274 osg::NodeVisitor* nv) const
1276 // More or less taken from plibs ssgCutout
1278 matrix(0,0) = 1; matrix(0,1) = 0; matrix(0,2) = 0;
1279 matrix(1,0) = 0; matrix(1,1) = 0; matrix(1,2) = -1;
1280 matrix(2,0) = 0; matrix(2,1) = 1; matrix(2,2) = 0;
1282 osg::Vec3 zAxis(matrix(2, 0), matrix(2, 1), matrix(2, 2));
1283 osg::Vec3 xAxis = osg::Vec3(0, 0, -1)^zAxis;
1284 osg::Vec3 yAxis = zAxis^xAxis;
1290 matrix(0,0) = xAxis[0]; matrix(0,1) = xAxis[1]; matrix(0,2) = xAxis[2];
1291 matrix(1,0) = yAxis[0]; matrix(1,1) = yAxis[1]; matrix(1,2) = yAxis[2];
1292 matrix(2,0) = zAxis[0]; matrix(2,1) = zAxis[1]; matrix(2,2) = zAxis[2];
1297 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
1298 osg::NodeVisitor* nv) const
1300 // Hmm, don't yet know how to get that back ...
1303 static bool writeLocalData(const osg::Object& obj, osgDB::Output& fw)
1305 const Transform& trans = static_cast<const Transform&>(obj);
1307 fw.indent() << (trans._spherical ? "true" : "false") << "\n";
1315 SGBillboardAnimation::SGBillboardAnimation(const SGPropertyNode* configNode,
1316 SGPropertyNode* modelRoot) :
1317 SGAnimation(configNode, modelRoot)
1322 SGBillboardAnimation::createAnimationGroup(osg::Group& parent)
1324 Transform* transform = new Transform(getConfig());
1325 parent.addChild(transform);
1331 osgDB::RegisterDotOsgWrapperProxy billboardAnimationTransformProxy
1333 new SGBillboardAnimation::Transform,
1334 "SGBillboardAnimation::Transform",
1335 "Object Node Transform SGBillboardAnimation::Transform Group",
1337 &SGBillboardAnimation::Transform::writeLocalData
1341 ////////////////////////////////////////////////////////////////////////
1342 // Implementation of a range animation
1343 ////////////////////////////////////////////////////////////////////////
1345 class SGRangeAnimation::UpdateCallback : public osg::NodeCallback {
1347 UpdateCallback(const SGCondition* condition,
1348 const SGExpressiond* minAnimationValue,
1349 const SGExpressiond* maxAnimationValue,
1350 double minValue, double maxValue) :
1351 _condition(condition),
1352 _minAnimationValue(minAnimationValue),
1353 _maxAnimationValue(maxAnimationValue),
1354 _minStaticValue(minValue),
1355 _maxStaticValue(maxValue)
1357 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1359 osg::LOD* lod = static_cast<osg::LOD*>(node);
1360 if (!_condition || _condition->test()) {
1362 if (_minAnimationValue)
1363 minRange = _minAnimationValue->getValue();
1365 minRange = _minStaticValue;
1367 if (_maxAnimationValue)
1368 maxRange = _maxAnimationValue->getValue();
1370 maxRange = _maxStaticValue;
1371 lod->setRange(0, minRange, maxRange);
1373 lod->setRange(0, 0, SGLimitsf::max());
1379 SGSharedPtr<const SGCondition> _condition;
1380 SGSharedPtr<const SGExpressiond> _minAnimationValue;
1381 SGSharedPtr<const SGExpressiond> _maxAnimationValue;
1382 double _minStaticValue;
1383 double _maxStaticValue;
1386 SGRangeAnimation::SGRangeAnimation(const SGPropertyNode* configNode,
1387 SGPropertyNode* modelRoot) :
1388 SGAnimation(configNode, modelRoot)
1390 _condition = getCondition();
1392 std::string inputPropertyName;
1393 inputPropertyName = configNode->getStringValue("min-property", "");
1394 if (!inputPropertyName.empty()) {
1395 SGPropertyNode* inputProperty;
1396 inputProperty = modelRoot->getNode(inputPropertyName, true);
1397 SGSharedPtr<SGExpressiond> value;
1398 value = new SGPropertyExpression<double>(inputProperty);
1400 value = read_factor_offset(configNode, value, "min-factor", "min-offset");
1401 _minAnimationValue = value->simplify();
1403 inputPropertyName = configNode->getStringValue("max-property", "");
1404 if (!inputPropertyName.empty()) {
1405 SGPropertyNode* inputProperty;
1406 inputProperty = modelRoot->getNode(inputPropertyName.c_str(), true);
1408 SGSharedPtr<SGExpressiond> value;
1409 value = new SGPropertyExpression<double>(inputProperty);
1411 value = read_factor_offset(configNode, value, "max-factor", "max-offset");
1412 _maxAnimationValue = value->simplify();
1415 _initialValue[0] = configNode->getDoubleValue("min-m", 0);
1416 _initialValue[0] *= configNode->getDoubleValue("min-factor", 1);
1417 _initialValue[1] = configNode->getDoubleValue("max-m", SGLimitsf::max());
1418 _initialValue[1] *= configNode->getDoubleValue("max-factor", 1);
1422 SGRangeAnimation::createAnimationGroup(osg::Group& parent)
1424 osg::Group* group = new osg::Group;
1425 group->setName("range animation group");
1427 osg::LOD* lod = new osg::LOD;
1428 lod->setName("range animation node");
1429 parent.addChild(lod);
1431 lod->addChild(group, _initialValue[0], _initialValue[1]);
1432 lod->setCenterMode(osg::LOD::USE_BOUNDING_SPHERE_CENTER);
1433 lod->setRangeMode(osg::LOD::DISTANCE_FROM_EYE_POINT);
1434 if (_minAnimationValue || _maxAnimationValue || _condition) {
1436 uc = new UpdateCallback(_condition, _minAnimationValue, _maxAnimationValue,
1437 _initialValue[0], _initialValue[1]);
1438 lod->setUpdateCallback(uc);
1444 ////////////////////////////////////////////////////////////////////////
1445 // Implementation of a select animation
1446 ////////////////////////////////////////////////////////////////////////
1448 class SGSelectAnimation::UpdateCallback : public osg::NodeCallback {
1450 UpdateCallback(const SGCondition* condition) :
1451 _condition(condition)
1453 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1455 osg::Switch* sw = static_cast<osg::Switch*>(node);
1456 if (_condition->test())
1457 sw->setAllChildrenOn();
1459 sw->setAllChildrenOff();
1464 SGSharedPtr<SGCondition const> _condition;
1467 SGSelectAnimation::SGSelectAnimation(const SGPropertyNode* configNode,
1468 SGPropertyNode* modelRoot) :
1469 SGAnimation(configNode, modelRoot)
1474 SGSelectAnimation::createAnimationGroup(osg::Group& parent)
1476 // if no condition given, this is a noop.
1477 SGSharedPtr<SGCondition const> condition = getCondition();
1478 // trick, gets deleted with all its 'animated' children
1479 // when the animation installer returns
1481 return new osg::Group;
1483 osg::Switch* sw = new osg::Switch;
1484 sw->setName("select animation node");
1485 sw->setUpdateCallback(new UpdateCallback(condition));
1486 parent.addChild(sw);
1492 ////////////////////////////////////////////////////////////////////////
1493 // Implementation of alpha test animation
1494 ////////////////////////////////////////////////////////////////////////
1496 SGAlphaTestAnimation::SGAlphaTestAnimation(const SGPropertyNode* configNode,
1497 SGPropertyNode* modelRoot) :
1498 SGAnimation(configNode, modelRoot)
1504 // Keep one copy of the most common alpha test its state set.
1505 ReentrantMutex alphaTestMutex;
1506 osg::ref_ptr<osg::AlphaFunc> standardAlphaFunc;
1507 osg::ref_ptr<osg::StateSet> alphaFuncStateSet;
1509 osg::AlphaFunc* makeAlphaFunc(float clamp)
1511 ScopedLock<ReentrantMutex> lock(alphaTestMutex);
1512 if (osg::equivalent(clamp, 0.01f)) {
1513 if (standardAlphaFunc.valid())
1514 return standardAlphaFunc.get();
1517 osg::AlphaFunc* alphaFunc = new osg::AlphaFunc;
1518 alphaFunc->setFunction(osg::AlphaFunc::GREATER);
1519 alphaFunc->setReferenceValue(clamp);
1520 alphaFunc->setDataVariance(osg::Object::STATIC);
1521 if (osg::equivalent(clamp, 0.01f))
1522 standardAlphaFunc = alphaFunc;
1526 osg::StateSet* makeAlphaTestStateSet(float clamp)
1528 using namespace OpenThreads;
1529 ScopedLock<ReentrantMutex> lock(alphaTestMutex);
1530 if (osg::equivalent(clamp, 0.01f)) {
1531 if (alphaFuncStateSet.valid())
1532 return alphaFuncStateSet.get();
1534 osg::AlphaFunc* alphaFunc = makeAlphaFunc(clamp);
1535 osg::StateSet* stateSet = new osg::StateSet;
1536 stateSet->setAttributeAndModes(alphaFunc,
1537 (osg::StateAttribute::ON
1538 | osg::StateAttribute::OVERRIDE));
1539 stateSet->setDataVariance(osg::Object::STATIC);
1540 if (osg::equivalent(clamp, 0.01f))
1541 alphaFuncStateSet = stateSet;
1546 SGAlphaTestAnimation::install(osg::Node& node)
1548 SGAnimation::install(node);
1550 float alphaClamp = getConfig()->getFloatValue("alpha-factor", 0);
1551 osg::StateSet* stateSet = node.getStateSet();
1553 node.setStateSet(makeAlphaTestStateSet(alphaClamp));
1555 stateSet->setAttributeAndModes(makeAlphaFunc(alphaClamp),
1556 (osg::StateAttribute::ON
1557 | osg::StateAttribute::OVERRIDE));
1562 //////////////////////////////////////////////////////////////////////
1563 // Blend animation installer
1564 //////////////////////////////////////////////////////////////////////
1566 // XXX This needs to be replaced by something using TexEnvCombine to
1567 // change the blend factor. Changing the alpha values in the geometry
1569 class SGBlendAnimation::BlendVisitor : public osg::NodeVisitor {
1571 BlendVisitor(float blend) :
1572 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
1574 { setVisitorType(osg::NodeVisitor::NODE_VISITOR); }
1575 virtual void apply(osg::Node& node)
1577 updateStateSet(node.getStateSet());
1580 virtual void apply(osg::Geode& node)
1582 apply((osg::Node&)node);
1583 unsigned nDrawables = node.getNumDrawables();
1584 for (unsigned i = 0; i < nDrawables; ++i) {
1585 osg::Drawable* drawable = node.getDrawable(i);
1586 osg::Geometry* geometry = drawable->asGeometry();
1589 osg::Array* array = geometry->getColorArray();
1592 osg::Vec4Array* vec4Array = dynamic_cast<osg::Vec4Array*>(array);
1595 for (unsigned k = 0; k < vec4Array->size(); ++k) {
1596 (*vec4Array)[k][3] = _blend;
1599 updateStateSet(drawable->getStateSet());
1602 void updateStateSet(osg::StateSet* stateSet)
1606 osg::StateAttribute* stateAttribute;
1607 stateAttribute = stateSet->getAttribute(osg::StateAttribute::MATERIAL);
1608 if (!stateAttribute)
1610 osg::Material* material = dynamic_cast<osg::Material*>(stateAttribute);
1613 material->setAlpha(osg::Material::FRONT_AND_BACK, _blend);
1615 stateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
1616 stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
1618 stateSet->setRenderingHint(osg::StateSet::DEFAULT_BIN);
1625 class SGBlendAnimation::UpdateCallback : public osg::NodeCallback {
1627 UpdateCallback(const SGPropertyNode* configNode, const SGExpressiond* v) :
1631 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1633 double blend = _animationValue->getValue();
1634 if (blend != _prev_value) {
1635 _prev_value = blend;
1636 BlendVisitor visitor(1-blend);
1637 node->accept(visitor);
1643 SGSharedPtr<SGExpressiond const> _animationValue;
1647 SGBlendAnimation::SGBlendAnimation(const SGPropertyNode* configNode,
1648 SGPropertyNode* modelRoot)
1649 : SGAnimation(configNode, modelRoot),
1650 _animationValue(read_value(configNode, modelRoot, "", 0, 1))
1655 SGBlendAnimation::createAnimationGroup(osg::Group& parent)
1657 if (!_animationValue)
1660 osg::Group* group = new osg::Switch;
1661 group->setName("blend animation node");
1662 group->setUpdateCallback(new UpdateCallback(getConfig(), _animationValue));
1663 parent.addChild(group);
1668 SGBlendAnimation::install(osg::Node& node)
1670 SGAnimation::install(node);
1671 // make sure we do not change common geometries,
1672 // that also creates new display lists for these subgeometries.
1673 cloneDrawables(node);
1674 DoDrawArraysVisitor visitor;
1675 node.accept(visitor);
1679 //////////////////////////////////////////////////////////////////////
1680 // Timed animation installer
1681 //////////////////////////////////////////////////////////////////////
1685 class SGTimedAnimation::UpdateCallback : public osg::NodeCallback {
1687 UpdateCallback(const SGPropertyNode* configNode) :
1690 _duration_sec(configNode->getDoubleValue("duration-sec", 1)),
1691 _last_time_sec(SGLimitsd::max()),
1692 _use_personality(configNode->getBoolValue("use-personality", false))
1694 std::vector<SGSharedPtr<SGPropertyNode> > nodes;
1695 nodes = configNode->getChildren("branch-duration-sec");
1696 for (size_t i = 0; i < nodes.size(); ++i) {
1697 unsigned ind = nodes[ i ]->getIndex();
1698 while ( ind >= _durations.size() ) {
1699 _durations.push_back(DurationSpec(_duration_sec));
1701 SGPropertyNode_ptr rNode = nodes[i]->getChild("random");
1703 _durations[ind] = DurationSpec(nodes[ i ]->getDoubleValue());
1705 _durations[ind] = DurationSpec(rNode->getDoubleValue( "min", 0),
1706 rNode->getDoubleValue( "max", 1));
1710 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1712 assert(dynamic_cast<osg::Switch*>(node));
1713 osg::Switch* sw = static_cast<osg::Switch*>(node);
1715 unsigned nChildren = sw->getNumChildren();
1717 // blow up the durations vector to the required size
1718 while (_durations.size() < nChildren) {
1719 _durations.push_back(_duration_sec);
1721 // make sure the current index is an duration that really exists
1722 _current_index = _current_index % nChildren;
1724 // update the time and compute the current systems time value
1725 double t = nv->getFrameStamp()->getReferenceTime();
1726 if (_last_time_sec == SGLimitsd::max()) {
1729 double dt = t - _last_time_sec;
1730 if (_use_personality)
1731 dt *= 1 + 0.2*(0.5 - sg_random());
1736 double currentDuration = _durations[_current_index].get();
1737 while (currentDuration < _reminder) {
1738 _reminder -= currentDuration;
1739 _current_index = (_current_index + 1) % nChildren;
1740 currentDuration = _durations[_current_index].get();
1743 sw->setSingleChildOn(_current_index);
1749 struct DurationSpec {
1750 DurationSpec(double t) :
1751 minTime(SGMiscd::max(0.01, t)),
1752 maxTime(SGMiscd::max(0.01, t))
1754 DurationSpec(double t0, double t1) :
1755 minTime(SGMiscd::max(0.01, t0)),
1756 maxTime(SGMiscd::max(0.01, t1))
1759 { return minTime + sg_random()*(maxTime - minTime); }
1763 std::vector<DurationSpec> _durations;
1764 unsigned _current_index;
1766 double _duration_sec;
1767 double _last_time_sec;
1768 bool _use_personality;
1772 SGTimedAnimation::SGTimedAnimation(const SGPropertyNode* configNode,
1773 SGPropertyNode* modelRoot)
1774 : SGAnimation(configNode, modelRoot)
1779 SGTimedAnimation::createAnimationGroup(osg::Group& parent)
1781 osg::Switch* sw = new osg::Switch;
1782 sw->setName("timed animation node");
1783 sw->setUpdateCallback(new UpdateCallback(getConfig()));
1784 parent.addChild(sw);
1789 ////////////////////////////////////////////////////////////////////////
1790 // dynamically switch on/off shadows
1791 ////////////////////////////////////////////////////////////////////////
1793 class SGShadowAnimation::UpdateCallback : public osg::NodeCallback {
1795 UpdateCallback(const SGCondition* condition) :
1796 _condition(condition)
1798 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1800 if (_condition->test())
1801 node->setNodeMask( SG_NODEMASK_CASTSHADOW_BIT | node->getNodeMask());
1803 node->setNodeMask(~SG_NODEMASK_CASTSHADOW_BIT & node->getNodeMask());
1808 SGSharedPtr<const SGCondition> _condition;
1811 SGShadowAnimation::SGShadowAnimation(const SGPropertyNode* configNode,
1812 SGPropertyNode* modelRoot) :
1813 SGAnimation(configNode, modelRoot)
1818 SGShadowAnimation::createAnimationGroup(osg::Group& parent)
1820 SGSharedPtr<SGCondition const> condition = getCondition();
1824 osg::Group* group = new osg::Group;
1825 group->setName("shadow animation");
1826 group->setUpdateCallback(new UpdateCallback(condition));
1827 parent.addChild(group);
1832 ////////////////////////////////////////////////////////////////////////
1833 // Implementation of SGTexTransformAnimation
1834 ////////////////////////////////////////////////////////////////////////
1836 class SGTexTransformAnimation::Transform : public SGReferenced {
1841 virtual ~Transform()
1843 void setValue(double value)
1845 virtual void transform(osg::Matrix&) = 0;
1850 class SGTexTransformAnimation::Translation :
1851 public SGTexTransformAnimation::Transform {
1853 Translation(const SGVec3d& axis) :
1856 virtual void transform(osg::Matrix& matrix)
1859 set_translation(tmp, _value, _axis);
1860 matrix.preMult(tmp);
1866 class SGTexTransformAnimation::Rotation :
1867 public SGTexTransformAnimation::Transform {
1869 Rotation(const SGVec3d& axis, const SGVec3d& center) :
1873 virtual void transform(osg::Matrix& matrix)
1876 set_rotation(tmp, _value, _center, _axis);
1877 matrix.preMult(tmp);
1884 class SGTexTransformAnimation::UpdateCallback :
1885 public osg::StateAttribute::Callback {
1887 UpdateCallback(const SGCondition* condition) :
1888 _condition(condition)
1890 virtual void operator () (osg::StateAttribute* sa, osg::NodeVisitor*)
1892 if (!_condition || _condition->test()) {
1893 TransformList::const_iterator i;
1894 for (i = _transforms.begin(); i != _transforms.end(); ++i)
1895 i->transform->setValue(i->value->getValue());
1897 assert(dynamic_cast<osg::TexMat*>(sa));
1898 osg::TexMat* texMat = static_cast<osg::TexMat*>(sa);
1899 texMat->getMatrix().makeIdentity();
1900 TransformList::const_iterator i;
1901 for (i = _transforms.begin(); i != _transforms.end(); ++i)
1902 i->transform->transform(texMat->getMatrix());
1904 void appendTransform(Transform* transform, SGExpressiond* value)
1906 Entry entry = { transform, value };
1907 transform->transform(_matrix);
1908 _transforms.push_back(entry);
1913 SGSharedPtr<Transform> transform;
1914 SGSharedPtr<const SGExpressiond> value;
1916 typedef std::vector<Entry> TransformList;
1917 TransformList _transforms;
1918 SGSharedPtr<const SGCondition> _condition;
1919 osg::Matrix _matrix;
1922 SGTexTransformAnimation::SGTexTransformAnimation(const SGPropertyNode* configNode,
1923 SGPropertyNode* modelRoot) :
1924 SGAnimation(configNode, modelRoot)
1929 SGTexTransformAnimation::createAnimationGroup(osg::Group& parent)
1931 osg::Group* group = new osg::Group;
1932 group->setName("texture transform group");
1933 osg::StateSet* stateSet = group->getOrCreateStateSet();
1934 stateSet->setDataVariance(osg::Object::DYNAMIC);
1935 osg::TexMat* texMat = new osg::TexMat;
1936 UpdateCallback* updateCallback = new UpdateCallback(getCondition());
1937 // interpret the configs ...
1938 std::string type = getType();
1940 if (type == "textranslate") {
1941 appendTexTranslate(getConfig(), updateCallback);
1942 } else if (type == "texrotate") {
1943 appendTexRotate(getConfig(), updateCallback);
1944 } else if (type == "texmultiple") {
1945 std::vector<SGSharedPtr<SGPropertyNode> > transformConfigs;
1946 transformConfigs = getConfig()->getChildren("transform");
1947 for (unsigned i = 0; i < transformConfigs.size(); ++i) {
1948 std::string subtype = transformConfigs[i]->getStringValue("subtype", "");
1949 if (subtype == "textranslate")
1950 appendTexTranslate(transformConfigs[i], updateCallback);
1951 else if (subtype == "texrotate")
1952 appendTexRotate(transformConfigs[i], updateCallback);
1954 SG_LOG(SG_INPUT, SG_ALERT,
1955 "Ignoring unknown texture transform subtype");
1958 SG_LOG(SG_INPUT, SG_ALERT, "Ignoring unknown texture transform type");
1961 texMat->setUpdateCallback(updateCallback);
1962 stateSet->setTextureAttribute(0, texMat);
1963 parent.addChild(group);
1968 SGTexTransformAnimation::appendTexTranslate(const SGPropertyNode* config,
1969 UpdateCallback* updateCallback)
1971 std::string propertyName = config->getStringValue("property", "");
1972 SGSharedPtr<SGExpressiond> value;
1973 if (propertyName.empty())
1974 value = new SGConstExpression<double>(0);
1976 SGPropertyNode* inputProperty = getModelRoot()->getNode(propertyName, true);
1977 value = new SGPropertyExpression<double>(inputProperty);
1980 SGInterpTable* table = read_interpolation_table(config);
1982 value = new SGInterpTableExpression<double>(value, table);
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 = value->simplify();
1991 double biasValue = config->getDoubleValue("bias", 0);
1993 value = new SGBiasExpression<double>(value, biasValue);
1994 value = new SGStepExpression<double>(value,
1995 config->getDoubleValue("step", 0),
1996 config->getDoubleValue("scroll", 0));
1997 value = read_offset_factor(config, value, "factor", "offset");
1999 if (config->hasChild("min") || config->hasChild("max")) {
2000 double minClip = config->getDoubleValue("min", -SGLimitsd::max());
2001 double maxClip = config->getDoubleValue("max", SGLimitsd::max());
2002 value = new SGClipExpression<double>(value, minClip, maxClip);
2004 value = value->simplify();
2006 SGVec3d axis(config->getDoubleValue("axis/x", 0),
2007 config->getDoubleValue("axis/y", 0),
2008 config->getDoubleValue("axis/z", 0));
2009 Translation* translation;
2010 translation = new Translation(normalize(axis));
2011 translation->setValue(config->getDoubleValue("starting-position", 0));
2012 updateCallback->appendTransform(translation, value);
2016 SGTexTransformAnimation::appendTexRotate(const SGPropertyNode* config,
2017 UpdateCallback* updateCallback)
2019 std::string propertyName = config->getStringValue("property", "");
2020 SGSharedPtr<SGExpressiond> value;
2021 if (propertyName.empty())
2022 value = new SGConstExpression<double>(0);
2024 SGPropertyNode* inputProperty = getModelRoot()->getNode(propertyName, true);
2025 value = new SGPropertyExpression<double>(inputProperty);
2028 SGInterpTable* table = read_interpolation_table(config);
2030 value = new SGInterpTableExpression<double>(value, table);
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 = value->simplify();
2039 double biasValue = config->getDoubleValue("bias", 0);
2041 value = new SGBiasExpression<double>(value, biasValue);
2042 value = new SGStepExpression<double>(value,
2043 config->getDoubleValue("step", 0),
2044 config->getDoubleValue("scroll", 0));
2045 value = read_offset_factor(config, value, "factor", "offset-deg");
2047 if (config->hasChild("min-deg") || config->hasChild("max-deg")) {
2048 double minClip = config->getDoubleValue("min-deg", -SGLimitsd::max());
2049 double maxClip = config->getDoubleValue("max-deg", SGLimitsd::max());
2050 value = new SGClipExpression<double>(value, minClip, maxClip);
2052 value = value->simplify();
2054 SGVec3d axis(config->getDoubleValue("axis/x", 0),
2055 config->getDoubleValue("axis/y", 0),
2056 config->getDoubleValue("axis/z", 0));
2057 SGVec3d center(config->getDoubleValue("center/x", 0),
2058 config->getDoubleValue("center/y", 0),
2059 config->getDoubleValue("center/z", 0));
2061 rotation = new Rotation(normalize(axis), center);
2062 rotation->setValue(config->getDoubleValue("starting-position-deg", 0));
2063 updateCallback->appendTransform(rotation, value);
2067 ////////////////////////////////////////////////////////////////////////
2068 // Implementation of SGPickAnimation
2069 ////////////////////////////////////////////////////////////////////////
2071 class SGPickAnimation::PickCallback : public SGPickCallback {
2073 PickCallback(const SGPropertyNode* configNode,
2074 SGPropertyNode* modelRoot) :
2075 _repeatable(configNode->getBoolValue("repeatable", false)),
2076 _repeatInterval(configNode->getDoubleValue("interval-sec", 0.1))
2078 SG_LOG(SG_INPUT, SG_DEBUG, "Reading all bindings");
2079 std::vector<SGPropertyNode_ptr> bindings;
2081 bindings = configNode->getChildren("button");
2082 for (unsigned int i = 0; i < bindings.size(); ++i) {
2083 _buttons.push_back( bindings[i]->getIntValue() );
2085 bindings = configNode->getChildren("binding");
2086 for (unsigned int i = 0; i < bindings.size(); ++i) {
2087 _bindingsDown.push_back(new SGBinding(bindings[i], modelRoot));
2090 const SGPropertyNode* upNode = configNode->getChild("mod-up");
2093 bindings = upNode->getChildren("binding");
2094 for (unsigned int i = 0; i < bindings.size(); ++i) {
2095 _bindingsUp.push_back(new SGBinding(bindings[i], modelRoot));
2098 virtual bool buttonPressed(int button, const Info&)
2101 for( std::vector<int>::iterator it = _buttons.begin(); it != _buttons.end(); it++ ) {
2102 if( *it == button ) {
2109 SGBindingList::const_iterator i;
2110 for (i = _bindingsDown.begin(); i != _bindingsDown.end(); ++i)
2112 _repeatTime = -_repeatInterval; // anti-bobble: delay start of repeat
2115 virtual void buttonReleased(void)
2117 SGBindingList::const_iterator i;
2118 for (i = _bindingsUp.begin(); i != _bindingsUp.end(); ++i)
2121 virtual void update(double dt)
2127 while (_repeatInterval < _repeatTime) {
2128 _repeatTime -= _repeatInterval;
2129 SGBindingList::const_iterator i;
2130 for (i = _bindingsDown.begin(); i != _bindingsDown.end(); ++i)
2135 SGBindingList _bindingsDown;
2136 SGBindingList _bindingsUp;
2137 std::vector<int> _buttons;
2139 double _repeatInterval;
2143 class VncVisitor : public osg::NodeVisitor {
2145 VncVisitor(double x, double y, int mask) :
2146 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
2147 _texX(x), _texY(y), _mask(mask), _done(false)
2149 SG_LOG(SG_INPUT, SG_DEBUG, "VncVisitor constructor "
2150 << x << "," << y << " mask " << mask);
2153 virtual void apply(osg::Node &node)
2155 // Some nodes have state sets attached
2156 touchStateSet(node.getStateSet());
2160 // See whether we are a geode worth exploring
2161 osg::Geode *g = dynamic_cast<osg::Geode*>(&node);
2163 // Go find all its drawables
2164 int i = g->getNumDrawables();
2166 osg::Drawable *d = g->getDrawable(i);
2167 if (d) touchDrawable(*d);
2169 // Out of optimism, do the same for EffectGeode
2170 simgear::EffectGeode *eg = dynamic_cast<simgear::EffectGeode*>(&node);
2172 for (simgear::EffectGeode::DrawablesIterator di = eg->drawablesBegin();
2173 di != eg->drawablesEnd(); di++) {
2174 touchDrawable(**di);
2176 // Now see whether the EffectGeode has an Effect
2177 simgear::Effect *e = eg->getEffect();
2179 touchStateSet(e->getDefaultStateSet());
2183 inline void touchDrawable(osg::Drawable &d)
2185 osg::StateSet *ss = d.getStateSet();
2189 void touchStateSet(osg::StateSet *ss)
2192 osg::StateAttribute *sa = ss->getTextureAttribute(0,
2193 osg::StateAttribute::TEXTURE);
2195 osg::Texture *t = sa->asTexture();
2197 osg::Image *img = t->getImage(0);
2200 int pixX = _texX * img->s();
2201 int pixY = _texY * img->t();
2202 _done = img->sendPointerEvent(pixX, pixY, _mask);
2203 SG_LOG(SG_INPUT, SG_DEBUG, "VncVisitor image said " << _done
2204 << " to coord " << pixX << "," << pixY);
2208 inline bool wasSuccessful()
2214 double _texX, _texY;
2220 class SGPickAnimation::VncCallback : public SGPickCallback {
2222 VncCallback(const SGPropertyNode* configNode,
2223 SGPropertyNode* modelRoot,
2227 SG_LOG(SG_INPUT, SG_DEBUG, "Configuring VNC callback");
2228 const char *cornernames[3] = {"top-left", "top-right", "bottom-left"};
2229 SGVec3d *cornercoords[3] = {&_topLeft, &_toRight, &_toDown};
2230 for (int c =0; c < 3; c++) {
2231 const SGPropertyNode* cornerNode = configNode->getChild(cornernames[c]);
2232 *cornercoords[c] = SGVec3d(
2233 cornerNode->getDoubleValue("x"),
2234 cornerNode->getDoubleValue("y"),
2235 cornerNode->getDoubleValue("z"));
2237 _toRight -= _topLeft;
2238 _toDown -= _topLeft;
2239 _squaredRight = dot(_toRight, _toRight);
2240 _squaredDown = dot(_toDown, _toDown);
2243 virtual bool buttonPressed(int button, const Info& info)
2245 SGVec3d loc(info.local);
2246 SG_LOG(SG_INPUT, SG_DEBUG, "VNC pressed " << button << ": " << loc);
2248 _x = dot(loc, _toRight) / _squaredRight;
2249 _y = dot(loc, _toDown) / _squaredDown;
2250 if (_x<0) _x = 0; else if (_x > 1) _x = 1;
2251 if (_y<0) _y = 0; else if (_y > 1) _y = 1;
2252 VncVisitor vv(_x, _y, 1 << button);
2254 return vv.wasSuccessful();
2257 virtual void buttonReleased(void)
2259 SG_LOG(SG_INPUT, SG_DEBUG, "VNC release");
2260 VncVisitor vv(_x, _y, 0);
2263 virtual void update(double dt)
2268 osg::ref_ptr<osg::Group> _node;
2269 SGVec3d _topLeft, _toRight, _toDown;
2270 double _squaredRight, _squaredDown;
2273 SGPickAnimation::SGPickAnimation(const SGPropertyNode* configNode,
2274 SGPropertyNode* modelRoot) :
2275 SGAnimation(configNode, modelRoot)
2280 SGPickAnimation::createAnimationGroup(osg::Group& parent)
2282 osg::Group* commonGroup = new osg::Group;
2284 // Contains the normal geometry that is interactive
2285 osg::ref_ptr<osg::Group> normalGroup = new osg::Group;
2286 normalGroup->setName("pick normal group");
2287 normalGroup->addChild(commonGroup);
2289 // Used to render the geometry with just yellow edges
2290 osg::Group* highlightGroup = new osg::Group;
2291 highlightGroup->setName("pick highlight group");
2292 highlightGroup->setNodeMask(SG_NODEMASK_PICK_BIT);
2293 highlightGroup->addChild(commonGroup);
2294 SGSceneUserData* ud;
2295 ud = SGSceneUserData::getOrCreateSceneUserData(commonGroup);
2297 // add actions that become macro and command invocations
2298 std::vector<SGPropertyNode_ptr> actions;
2299 actions = getConfig()->getChildren("action");
2300 for (unsigned int i = 0; i < actions.size(); ++i)
2301 ud->addPickCallback(new PickCallback(actions[i], getModelRoot()));
2302 // Look for the VNC sessions that want raw mouse input
2303 actions = getConfig()->getChildren("vncaction");
2304 for (unsigned int i = 0; i < actions.size(); ++i)
2305 ud->addPickCallback(new VncCallback(actions[i], getModelRoot(),
2308 // prepare a state set that paints the edges of this object yellow
2309 osg::StateSet* stateSet = highlightGroup->getOrCreateStateSet();
2310 stateSet->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE);
2312 osg::PolygonOffset* polygonOffset = new osg::PolygonOffset;
2313 polygonOffset->setFactor(-1);
2314 polygonOffset->setUnits(-1);
2315 stateSet->setAttribute(polygonOffset, osg::StateAttribute::OVERRIDE);
2316 stateSet->setMode(GL_POLYGON_OFFSET_LINE, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
2318 osg::PolygonMode* polygonMode = new osg::PolygonMode;
2319 polygonMode->setMode(osg::PolygonMode::FRONT_AND_BACK,
2320 osg::PolygonMode::LINE);
2321 stateSet->setAttribute(polygonMode, osg::StateAttribute::OVERRIDE);
2323 osg::Material* material = new osg::Material;
2324 material->setColorMode(osg::Material::OFF);
2325 material->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(1, 1, 0, 1));
2326 material->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(1, 1, 0, 1));
2327 material->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(1, 1, 0, 1));
2328 material->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0, 0, 0, 0));
2329 stateSet->setAttribute(material, osg::StateAttribute::OVERRIDE);
2331 // Only add normal geometry if configured
2332 if (getConfig()->getBoolValue("visible", true))
2333 parent.addChild(normalGroup.get());
2334 parent.addChild(highlightGroup);