1 // animation.cxx - classes to manage model animation.
2 // Written by David Megginson, started 2002.
4 // This file is in the Public Domain, and comes with no warranty.
7 # include <simgear_config.h>
10 #include <string.h> // for strcmp()
15 #include <OpenThreads/Mutex>
16 #include <OpenThreads/ReentrantMutex>
17 #include <OpenThreads/ScopedLock>
19 #include <osg/AlphaFunc>
20 #include <osg/Drawable>
22 #include <osg/Geometry>
26 #include <osg/PolygonMode>
27 #include <osg/PolygonOffset>
28 #include <osg/StateSet>
31 #include <osg/Texture2D>
32 #include <osg/Transform>
33 #include <osg/Uniform>
34 #include <osgDB/ReadFile>
35 #include <osgDB/Registry>
36 #include <osgDB/Input>
37 #include <osgDB/ParameterOutput>
40 #include <simgear/math/interpolater.hxx>
41 #include <simgear/props/condition.hxx>
42 #include <simgear/props/props.hxx>
43 #include <simgear/structure/SGBinding.hxx>
44 #include <simgear/scene/material/EffectGeode.hxx>
45 #include <simgear/scene/util/SGNodeMasks.hxx>
46 #include <simgear/scene/util/SGSceneUserData.hxx>
47 #include <simgear/scene/util/SGStateAttributeVisitor.hxx>
48 #include <simgear/scene/util/StateAttributeFactory.hxx>
50 #include "animation.hxx"
53 #include "SGTranslateTransform.hxx"
54 #include "SGMaterialAnimation.hxx"
55 #include "SGRotateTransform.hxx"
56 #include "SGScaleTransform.hxx"
57 #include "SGInteractionAnimation.hxx"
59 #include "ConditionNode.hxx"
61 using OpenThreads::Mutex;
62 using OpenThreads::ReentrantMutex;
63 using OpenThreads::ScopedLock;
65 using namespace simgear;
67 ////////////////////////////////////////////////////////////////////////
68 // Static utility functions.
69 ////////////////////////////////////////////////////////////////////////
72 * Set up the transform matrix for a spin or rotation.
75 set_rotation (osg::Matrix &matrix, double position_deg,
76 const SGVec3d ¢er, const SGVec3d &axis)
78 double temp_angle = -SGMiscd::deg2rad(position_deg);
80 double s = sin(temp_angle);
81 double c = cos(temp_angle);
84 // axis was normalized at load time
85 // hint to the compiler to put these into FP registers
90 matrix(0, 0) = t * x * x + c ;
91 matrix(0, 1) = t * y * x - s * z ;
92 matrix(0, 2) = t * z * x + s * y ;
95 matrix(1, 0) = t * x * y + s * z ;
96 matrix(1, 1) = t * y * y + c ;
97 matrix(1, 2) = t * z * y - s * x ;
100 matrix(2, 0) = t * x * z - s * y ;
101 matrix(2, 1) = t * y * z + s * x ;
102 matrix(2, 2) = t * z * z + c ;
105 // hint to the compiler to put these into FP registers
110 matrix(3, 0) = x - x*matrix(0, 0) - y*matrix(1, 0) - z*matrix(2, 0);
111 matrix(3, 1) = y - x*matrix(0, 1) - y*matrix(1, 1) - z*matrix(2, 1);
112 matrix(3, 2) = z - x*matrix(0, 2) - y*matrix(1, 2) - z*matrix(2, 2);
117 * Set up the transform matrix for a translation.
120 set_translation (osg::Matrix &matrix, double position_m, const SGVec3d &axis)
122 SGVec3d xyz = axis * position_m;
123 matrix.makeIdentity();
124 matrix(3, 0) = xyz[0];
125 matrix(3, 1) = xyz[1];
126 matrix(3, 2) = xyz[2];
130 * Read an interpolation table from properties.
132 static SGInterpTable *
133 read_interpolation_table(const SGPropertyNode* props)
135 const SGPropertyNode* table_node = props->getNode("interpolation");
138 return new SGInterpTable(table_node);
142 unit_string(const char* value, const char* unit)
144 return std::string(value) + unit;
147 class SGPersonalityScaleOffsetExpression : public SGUnaryExpression<double> {
149 SGPersonalityScaleOffsetExpression(SGExpression<double>* expr,
150 SGPropertyNode const* config,
151 const std::string& scalename,
152 const std::string& offsetname,
154 double defOffset = 0) :
155 SGUnaryExpression<double>(expr),
156 _scale(config, scalename.c_str(), defScale),
157 _offset(config, offsetname.c_str(), defOffset)
159 void setScale(double scale)
161 void setOffset(double offset)
162 { _offset = offset; }
164 virtual void eval(double& value, const simgear::expression::Binding* b) const
168 value = _offset + _scale*getOperand()->getValue(b);
171 virtual bool isConst() const { return false; }
174 mutable SGPersonalityParameter<double> _scale;
175 mutable SGPersonalityParameter<double> _offset;
179 static SGExpressiond*
180 read_factor_offset(const SGPropertyNode* configNode, SGExpressiond* expr,
181 const std::string& factor, const std::string& offset)
183 double factorValue = configNode->getDoubleValue(factor, 1);
184 if (factorValue != 1)
185 expr = new SGScaleExpression<double>(expr, factorValue);
186 double offsetValue = configNode->getDoubleValue(offset, 0);
187 if (offsetValue != 0)
188 expr = new SGBiasExpression<double>(expr, offsetValue);
192 static SGExpressiond*
193 read_offset_factor(const SGPropertyNode* configNode, SGExpressiond* expr,
194 const std::string& factor, const std::string& offset)
196 double offsetValue = configNode->getDoubleValue(offset, 0);
197 if (offsetValue != 0)
198 expr = new SGBiasExpression<double>(expr, offsetValue);
199 double factorValue = configNode->getDoubleValue(factor, 1);
200 if (factorValue != 1)
201 expr = new SGScaleExpression<double>(expr, factorValue);
206 read_value(const SGPropertyNode* configNode, SGPropertyNode* modelRoot,
207 const char* unit, double defMin, double defMax)
209 SGExpression<double>* value = 0;
211 std::string inputPropertyName = configNode->getStringValue("property", "");
212 if (inputPropertyName.empty()) {
213 std::string spos = unit_string("starting-position", unit);
214 double initPos = configNode->getDoubleValue(spos, 0);
215 value = new SGConstExpression<double>(initPos);
217 SGPropertyNode* inputProperty;
218 inputProperty = modelRoot->getNode(inputPropertyName, true);
219 value = new SGPropertyExpression<double>(inputProperty);
222 SGInterpTable* interpTable = read_interpolation_table(configNode);
224 return new SGInterpTableExpression<double>(value, interpTable);
226 std::string offset = unit_string("offset", unit);
227 std::string min = unit_string("min", unit);
228 std::string max = unit_string("max", unit);
230 if (configNode->getBoolValue("use-personality", false)) {
231 value = new SGPersonalityScaleOffsetExpression(value, configNode,
234 value = read_factor_offset(configNode, value, "factor", offset);
237 double minClip = configNode->getDoubleValue(min, defMin);
238 double maxClip = configNode->getDoubleValue(max, defMax);
239 if (minClip > SGMiscd::min(SGLimitsd::min(), -SGLimitsd::max()) ||
240 maxClip < SGLimitsd::max())
241 value = new SGClipExpression<double>(value, minClip, maxClip);
249 ////////////////////////////////////////////////////////////////////////
250 // Animation installer
251 ////////////////////////////////////////////////////////////////////////
253 class SGAnimation::RemoveModeVisitor : public SGStateAttributeVisitor {
255 RemoveModeVisitor(osg::StateAttribute::GLMode mode) :
258 virtual void apply(osg::StateSet* stateSet)
262 stateSet->removeMode(_mode);
265 osg::StateAttribute::GLMode _mode;
268 class SGAnimation::RemoveAttributeVisitor : public SGStateAttributeVisitor {
270 RemoveAttributeVisitor(osg::StateAttribute::Type type) :
273 virtual void apply(osg::StateSet* stateSet)
277 while (stateSet->getAttribute(_type)) {
278 stateSet->removeAttribute(_type);
282 osg::StateAttribute::Type _type;
285 class SGAnimation::RemoveTextureModeVisitor : public SGStateAttributeVisitor {
287 RemoveTextureModeVisitor(unsigned unit, osg::StateAttribute::GLMode mode) :
291 virtual void apply(osg::StateSet* stateSet)
295 stateSet->removeTextureMode(_unit, _mode);
299 osg::StateAttribute::GLMode _mode;
302 class SGAnimation::RemoveTextureAttributeVisitor :
303 public SGStateAttributeVisitor {
305 RemoveTextureAttributeVisitor(unsigned unit,
306 osg::StateAttribute::Type type) :
310 virtual void apply(osg::StateSet* stateSet)
314 while (stateSet->getTextureAttribute(_unit, _type)) {
315 stateSet->removeTextureAttribute(_unit, _type);
320 osg::StateAttribute::Type _type;
323 class SGAnimation::BinToInheritVisitor : public SGStateAttributeVisitor {
325 virtual void apply(osg::StateSet* stateSet)
329 stateSet->setRenderBinToInherit();
333 class SGAnimation::DrawableCloneVisitor : public osg::NodeVisitor {
335 DrawableCloneVisitor() :
336 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
338 void apply(osg::Geode& geode)
340 for (unsigned i = 0 ; i < geode.getNumDrawables(); ++i) {
341 osg::CopyOp copyOp(osg::CopyOp::DEEP_COPY_ALL &
342 ~osg::CopyOp::DEEP_COPY_TEXTURES);
343 geode.setDrawable(i, copyOp(geode.getDrawable(i)));
350 // Set all drawables to not use display lists. OSG will use
351 // glDrawArrays instead.
352 struct DoDrawArraysVisitor : public osg::NodeVisitor {
353 DoDrawArraysVisitor() :
354 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
356 void apply(osg::Geode& geode)
361 for (int i = 0; i < (int)geode.getNumDrawables(); ++i)
362 geode.getDrawable(i)->setUseDisplayList(false);
367 SGAnimation::SGAnimation(const SGPropertyNode* configNode,
368 SGPropertyNode* modelRoot) :
369 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
371 _configNode(configNode),
372 _modelRoot(modelRoot)
374 _name = configNode->getStringValue("name", "");
375 _enableHOT = configNode->getBoolValue("enable-hot", true);
376 _disableShadow = configNode->getBoolValue("disable-shadow", false);
377 std::vector<SGPropertyNode_ptr> objectNames =
378 configNode->getChildren("object-name");
379 for (unsigned i = 0; i < objectNames.size(); ++i)
380 _objectNames.push_back(objectNames[i]->getStringValue());
383 SGAnimation::~SGAnimation()
388 SG_LOG(SG_IO, SG_ALERT, "Could not find at least one of the following"
389 " objects for animation:\n");
390 std::list<std::string>::const_iterator i;
391 for (i = _objectNames.begin(); i != _objectNames.end(); ++i)
392 SG_LOG(SG_IO, SG_ALERT, *i << "\n");
396 SGAnimation::animate(osg::Node* node, const SGPropertyNode* configNode,
397 SGPropertyNode* modelRoot,
398 const osgDB::ReaderWriter::Options* options)
400 std::string type = configNode->getStringValue("type", "none");
401 if (type == "alpha-test") {
402 SGAlphaTestAnimation animInst(configNode, modelRoot);
403 animInst.apply(node);
404 } else if (type == "billboard") {
405 SGBillboardAnimation animInst(configNode, modelRoot);
406 animInst.apply(node);
407 } else if (type == "blend") {
408 SGBlendAnimation animInst(configNode, modelRoot);
409 animInst.apply(node);
410 } else if (type == "dist-scale") {
411 SGDistScaleAnimation animInst(configNode, modelRoot);
412 animInst.apply(node);
413 } else if (type == "flash") {
414 SGFlashAnimation animInst(configNode, modelRoot);
415 animInst.apply(node);
416 } else if (type == "interaction") {
417 SGInteractionAnimation animInst(configNode, modelRoot);
418 animInst.apply(node);
419 } else if (type == "material") {
420 SGMaterialAnimation animInst(configNode, modelRoot, options);
421 animInst.apply(node);
422 } else if (type == "noshadow") {
423 SGShadowAnimation animInst(configNode, modelRoot);
424 animInst.apply(node);
425 } else if (type == "pick") {
426 SGPickAnimation animInst(configNode, modelRoot);
427 animInst.apply(node);
428 } else if (type == "range") {
429 SGRangeAnimation animInst(configNode, modelRoot);
430 animInst.apply(node);
431 } else if (type == "rotate" || type == "spin") {
432 SGRotateAnimation animInst(configNode, modelRoot);
433 animInst.apply(node);
434 } else if (type == "scale") {
435 SGScaleAnimation animInst(configNode, modelRoot);
436 animInst.apply(node);
437 } else if (type == "select") {
438 SGSelectAnimation animInst(configNode, modelRoot);
439 animInst.apply(node);
440 } else if (type == "shader") {
441 SGShaderAnimation animInst(configNode, modelRoot, options);
442 animInst.apply(node);
443 } else if (type == "textranslate" || type == "texrotate" ||
444 type == "texmultiple") {
445 SGTexTransformAnimation animInst(configNode, modelRoot);
446 animInst.apply(node);
447 } else if (type == "timed") {
448 SGTimedAnimation animInst(configNode, modelRoot);
449 animInst.apply(node);
450 } else if (type == "translate") {
451 SGTranslateAnimation animInst(configNode, modelRoot);
452 animInst.apply(node);
453 } else if (type == "null" || type == "none" || type.empty()) {
454 SGGroupAnimation animInst(configNode, modelRoot);
455 animInst.apply(node);
464 SGAnimation::apply(osg::Node* node)
466 // duh what a special case ...
467 if (_objectNames.empty()) {
468 osg::Group* group = node->asGroup();
470 osg::ref_ptr<osg::Group> animationGroup;
471 installInGroup(std::string(), *group, animationGroup);
478 SGAnimation::install(osg::Node& node)
482 node.setNodeMask( SG_NODEMASK_TERRAIN_BIT | node.getNodeMask());
484 node.setNodeMask(~SG_NODEMASK_TERRAIN_BIT & node.getNodeMask());
486 node.setNodeMask( SG_NODEMASK_CASTSHADOW_BIT | node.getNodeMask());
488 node.setNodeMask(~SG_NODEMASK_CASTSHADOW_BIT & node.getNodeMask());
492 SGAnimation::createAnimationGroup(osg::Group& parent)
494 // default implementation, we do not need a new group
495 // for every animation type. Usually animations that just change
496 // the StateSet of some parts of the model
501 SGAnimation::apply(osg::Group& group)
503 // the trick is to first traverse the children and then
504 // possibly splice in a new group node if required.
505 // Else we end up in a recursive loop where we infinitly insert new
509 // Note that this algorithm preserves the order of the child objects
510 // like they appear in the object-name tags.
511 // The timed animations require this
512 osg::ref_ptr<osg::Group> animationGroup;
513 std::list<std::string>::const_iterator nameIt;
514 for (nameIt = _objectNames.begin(); nameIt != _objectNames.end(); ++nameIt)
515 installInGroup(*nameIt, group, animationGroup);
519 SGAnimation::installInGroup(const std::string& name, osg::Group& group,
520 osg::ref_ptr<osg::Group>& animationGroup)
522 int i = group.getNumChildren() - 1;
523 for (; 0 <= i; --i) {
524 osg::Node* child = group.getChild(i);
526 // Check if this one is already processed
527 if (std::find(_installedAnimations.begin(),
528 _installedAnimations.end(), child)
529 != _installedAnimations.end())
532 if (name.empty() || child->getName() == name) {
533 // fire the installation of the animation
536 // create a group node on demand
537 if (!animationGroup.valid()) {
538 animationGroup = createAnimationGroup(group);
539 // Animation type that does not require a new group,
540 // in this case we can stop and look for the next object
541 if (animationGroup.valid() && !_name.empty())
542 animationGroup->setName(_name);
544 if (animationGroup.valid()) {
545 animationGroup->addChild(child);
546 group.removeChild(i);
549 // store that we already have processed this child node
550 // We can hit this one twice if an animation references some
551 // part of a subtree twice
552 _installedAnimations.push_back(child);
558 SGAnimation::removeMode(osg::Node& node, osg::StateAttribute::GLMode mode)
560 RemoveModeVisitor visitor(mode);
561 node.accept(visitor);
565 SGAnimation::removeAttribute(osg::Node& node, osg::StateAttribute::Type type)
567 RemoveAttributeVisitor visitor(type);
568 node.accept(visitor);
572 SGAnimation::removeTextureMode(osg::Node& node, unsigned unit,
573 osg::StateAttribute::GLMode mode)
575 RemoveTextureModeVisitor visitor(unit, mode);
576 node.accept(visitor);
580 SGAnimation::removeTextureAttribute(osg::Node& node, unsigned unit,
581 osg::StateAttribute::Type type)
583 RemoveTextureAttributeVisitor visitor(unit, type);
584 node.accept(visitor);
588 SGAnimation::setRenderBinToInherit(osg::Node& node)
590 BinToInheritVisitor visitor;
591 node.accept(visitor);
595 SGAnimation::cloneDrawables(osg::Node& node)
597 DrawableCloneVisitor visitor;
598 node.accept(visitor);
602 SGAnimation::getCondition() const
604 const SGPropertyNode* conditionNode = _configNode->getChild("condition");
607 return sgReadCondition(_modelRoot, conditionNode);
612 ////////////////////////////////////////////////////////////////////////
613 // Implementation of null animation
614 ////////////////////////////////////////////////////////////////////////
616 // Ok, that is to build a subgraph from different other
617 // graph nodes. I guess that this stems from the time where modellers
618 // could not build hierarchical trees ...
619 SGGroupAnimation::SGGroupAnimation(const SGPropertyNode* configNode,
620 SGPropertyNode* modelRoot):
621 SGAnimation(configNode, modelRoot)
626 SGGroupAnimation::createAnimationGroup(osg::Group& parent)
628 osg::Group* group = new osg::Group;
629 parent.addChild(group);
634 ////////////////////////////////////////////////////////////////////////
635 // Implementation of translate animation
636 ////////////////////////////////////////////////////////////////////////
638 class SGTranslateAnimation::UpdateCallback : public osg::NodeCallback {
640 UpdateCallback(SGCondition const* condition,
641 SGExpressiond const* animationValue) :
642 _condition(condition),
643 _animationValue(animationValue)
645 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
647 if (!_condition || _condition->test()) {
648 SGTranslateTransform* transform;
649 transform = static_cast<SGTranslateTransform*>(node);
650 transform->setValue(_animationValue->getValue());
655 SGSharedPtr<SGCondition const> _condition;
656 SGSharedPtr<SGExpressiond const> _animationValue;
659 SGTranslateAnimation::SGTranslateAnimation(const SGPropertyNode* configNode,
660 SGPropertyNode* modelRoot) :
661 SGAnimation(configNode, modelRoot)
663 _condition = getCondition();
664 SGSharedPtr<SGExpressiond> value;
665 value = read_value(configNode, modelRoot, "-m",
666 -SGLimitsd::max(), SGLimitsd::max());
667 _animationValue = value->simplify();
669 _initialValue = _animationValue->getValue();
673 if (configNode->hasValue("axis/x1-m")) {
675 v1[0] = configNode->getDoubleValue("axis/x1-m", 0);
676 v1[1] = configNode->getDoubleValue("axis/y1-m", 0);
677 v1[2] = configNode->getDoubleValue("axis/z1-m", 0);
678 v2[0] = configNode->getDoubleValue("axis/x2-m", 0);
679 v2[1] = configNode->getDoubleValue("axis/y2-m", 0);
680 v2[2] = configNode->getDoubleValue("axis/z2-m", 0);
683 _axis[0] = configNode->getDoubleValue("axis/x", 0);
684 _axis[1] = configNode->getDoubleValue("axis/y", 0);
685 _axis[2] = configNode->getDoubleValue("axis/z", 0);
687 if (8*SGLimitsd::min() < norm(_axis))
688 _axis = normalize(_axis);
692 SGTranslateAnimation::createAnimationGroup(osg::Group& parent)
694 SGTranslateTransform* transform = new SGTranslateTransform;
695 transform->setName("translate animation");
696 if (_animationValue && !_animationValue->isConst()) {
697 UpdateCallback* uc = new UpdateCallback(_condition, _animationValue);
698 transform->setUpdateCallback(uc);
700 transform->setAxis(_axis);
701 transform->setValue(_initialValue);
702 parent.addChild(transform);
707 ////////////////////////////////////////////////////////////////////////
708 // Implementation of rotate/spin animation
709 ////////////////////////////////////////////////////////////////////////
711 class SGRotateAnimation::UpdateCallback : public osg::NodeCallback {
713 UpdateCallback(SGCondition const* condition,
714 SGExpressiond const* animationValue) :
715 _condition(condition),
716 _animationValue(animationValue)
718 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
720 if (!_condition || _condition->test()) {
721 SGRotateTransform* transform;
722 transform = static_cast<SGRotateTransform*>(node);
723 transform->setAngleDeg(_animationValue->getValue());
728 SGSharedPtr<SGCondition const> _condition;
729 SGSharedPtr<SGExpressiond const> _animationValue;
732 class SGRotateAnimation::SpinUpdateCallback : public osg::NodeCallback {
734 SpinUpdateCallback(SGCondition const* condition,
735 SGExpressiond const* animationValue) :
736 _condition(condition),
737 _animationValue(animationValue),
740 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
742 if (!_condition || _condition->test()) {
743 SGRotateTransform* transform;
744 transform = static_cast<SGRotateTransform*>(node);
746 double t = nv->getFrameStamp()->getReferenceTime();
751 double velocity_rpms = _animationValue->getValue()/60;
752 double angle = transform->getAngleDeg();
753 angle += dt*velocity_rpms*360;
754 angle -= 360*floor(angle/360);
755 transform->setAngleDeg(angle);
760 SGSharedPtr<SGCondition const> _condition;
761 SGSharedPtr<SGExpressiond const> _animationValue;
765 SGRotateAnimation::SGRotateAnimation(const SGPropertyNode* configNode,
766 SGPropertyNode* modelRoot) :
767 SGAnimation(configNode, modelRoot)
769 std::string type = configNode->getStringValue("type", "");
770 _isSpin = (type == "spin");
772 _condition = getCondition();
773 SGSharedPtr<SGExpressiond> value;
774 value = read_value(configNode, modelRoot, "-deg",
775 -SGLimitsd::max(), SGLimitsd::max());
776 _animationValue = value->simplify();
778 _initialValue = _animationValue->getValue();
782 _center = SGVec3d::zeros();
783 if (configNode->hasValue("axis/x1-m")) {
785 v1[0] = configNode->getDoubleValue("axis/x1-m", 0);
786 v1[1] = configNode->getDoubleValue("axis/y1-m", 0);
787 v1[2] = configNode->getDoubleValue("axis/z1-m", 0);
788 v2[0] = configNode->getDoubleValue("axis/x2-m", 0);
789 v2[1] = configNode->getDoubleValue("axis/y2-m", 0);
790 v2[2] = configNode->getDoubleValue("axis/z2-m", 0);
791 _center = 0.5*(v1+v2);
794 _axis[0] = configNode->getDoubleValue("axis/x", 0);
795 _axis[1] = configNode->getDoubleValue("axis/y", 0);
796 _axis[2] = configNode->getDoubleValue("axis/z", 0);
798 if (8*SGLimitsd::min() < norm(_axis))
799 _axis = normalize(_axis);
801 _center[0] = configNode->getDoubleValue("center/x-m", _center[0]);
802 _center[1] = configNode->getDoubleValue("center/y-m", _center[1]);
803 _center[2] = configNode->getDoubleValue("center/z-m", _center[2]);
807 SGRotateAnimation::createAnimationGroup(osg::Group& parent)
809 SGRotateTransform* transform = new SGRotateTransform;
810 transform->setName("rotate animation");
812 SpinUpdateCallback* uc;
813 uc = new SpinUpdateCallback(_condition, _animationValue);
814 transform->setUpdateCallback(uc);
815 } else if (_animationValue || !_animationValue->isConst()) {
816 UpdateCallback* uc = new UpdateCallback(_condition, _animationValue);
817 transform->setUpdateCallback(uc);
819 transform->setCenter(_center);
820 transform->setAxis(_axis);
821 transform->setAngleDeg(_initialValue);
822 parent.addChild(transform);
827 ////////////////////////////////////////////////////////////////////////
828 // Implementation of scale animation
829 ////////////////////////////////////////////////////////////////////////
831 class SGScaleAnimation::UpdateCallback : public osg::NodeCallback {
833 UpdateCallback(const SGCondition* condition,
834 SGSharedPtr<const SGExpressiond> animationValue[3]) :
835 _condition(condition)
837 _animationValue[0] = animationValue[0];
838 _animationValue[1] = animationValue[1];
839 _animationValue[2] = animationValue[2];
841 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
843 if (!_condition || _condition->test()) {
844 SGScaleTransform* transform;
845 transform = static_cast<SGScaleTransform*>(node);
846 SGVec3d scale(_animationValue[0]->getValue(),
847 _animationValue[1]->getValue(),
848 _animationValue[2]->getValue());
849 transform->setScaleFactor(scale);
854 SGSharedPtr<SGCondition const> _condition;
855 SGSharedPtr<SGExpressiond const> _animationValue[3];
858 SGScaleAnimation::SGScaleAnimation(const SGPropertyNode* configNode,
859 SGPropertyNode* modelRoot) :
860 SGAnimation(configNode, modelRoot)
862 _condition = getCondition();
864 // default offset/factor for all directions
865 double offset = configNode->getDoubleValue("offset", 0);
866 double factor = configNode->getDoubleValue("factor", 1);
868 SGSharedPtr<SGExpressiond> inPropExpr;
870 std::string inputPropertyName;
871 inputPropertyName = configNode->getStringValue("property", "");
872 if (inputPropertyName.empty()) {
873 inPropExpr = new SGConstExpression<double>(0);
875 SGPropertyNode* inputProperty;
876 inputProperty = modelRoot->getNode(inputPropertyName, true);
877 inPropExpr = new SGPropertyExpression<double>(inputProperty);
880 SGInterpTable* interpTable = read_interpolation_table(configNode);
882 SGSharedPtr<SGExpressiond> value;
883 value = new SGInterpTableExpression<double>(inPropExpr, interpTable);
884 _animationValue[0] = value->simplify();
885 _animationValue[1] = value->simplify();
886 _animationValue[2] = value->simplify();
887 } else if (configNode->getBoolValue("use-personality", false)) {
888 SGSharedPtr<SGExpressiond> value;
889 value = new SGPersonalityScaleOffsetExpression(inPropExpr, configNode,
890 "x-factor", "x-offset",
892 double minClip = configNode->getDoubleValue("x-min", 0);
893 double maxClip = configNode->getDoubleValue("x-max", SGLimitsd::max());
894 value = new SGClipExpression<double>(value, minClip, maxClip);
895 _animationValue[0] = value->simplify();
897 value = new SGPersonalityScaleOffsetExpression(inPropExpr, configNode,
898 "y-factor", "y-offset",
900 minClip = configNode->getDoubleValue("y-min", 0);
901 maxClip = configNode->getDoubleValue("y-max", SGLimitsd::max());
902 value = new SGClipExpression<double>(value, minClip, maxClip);
903 _animationValue[1] = value->simplify();
905 value = new SGPersonalityScaleOffsetExpression(inPropExpr, configNode,
906 "z-factor", "z-offset",
908 minClip = configNode->getDoubleValue("z-min", 0);
909 maxClip = configNode->getDoubleValue("z-max", SGLimitsd::max());
910 value = new SGClipExpression<double>(value, minClip, maxClip);
911 _animationValue[2] = value->simplify();
913 SGSharedPtr<SGExpressiond> value;
914 value = read_factor_offset(configNode, inPropExpr, "x-factor", "x-offset");
915 double minClip = configNode->getDoubleValue("x-min", 0);
916 double maxClip = configNode->getDoubleValue("x-max", SGLimitsd::max());
917 value = new SGClipExpression<double>(value, minClip, maxClip);
918 _animationValue[0] = value->simplify();
920 value = read_factor_offset(configNode, inPropExpr, "y-factor", "y-offset");
921 minClip = configNode->getDoubleValue("y-min", 0);
922 maxClip = configNode->getDoubleValue("y-max", SGLimitsd::max());
923 value = new SGClipExpression<double>(value, minClip, maxClip);
924 _animationValue[1] = value->simplify();
926 value = read_factor_offset(configNode, inPropExpr, "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 _initialValue[0] = configNode->getDoubleValue("x-starting-scale", 1);
933 _initialValue[0] *= configNode->getDoubleValue("x-factor", factor);
934 _initialValue[0] += configNode->getDoubleValue("x-offset", offset);
935 _initialValue[1] = configNode->getDoubleValue("y-starting-scale", 1);
936 _initialValue[1] *= configNode->getDoubleValue("y-factor", factor);
937 _initialValue[1] += configNode->getDoubleValue("y-offset", offset);
938 _initialValue[2] = configNode->getDoubleValue("z-starting-scale", 1);
939 _initialValue[2] *= configNode->getDoubleValue("z-factor", factor);
940 _initialValue[2] += configNode->getDoubleValue("z-offset", offset);
941 _center[0] = configNode->getDoubleValue("center/x-m", 0);
942 _center[1] = configNode->getDoubleValue("center/y-m", 0);
943 _center[2] = configNode->getDoubleValue("center/z-m", 0);
947 SGScaleAnimation::createAnimationGroup(osg::Group& parent)
949 SGScaleTransform* transform = new SGScaleTransform;
950 transform->setName("scale animation");
951 transform->setCenter(_center);
952 transform->setScaleFactor(_initialValue);
953 UpdateCallback* uc = new UpdateCallback(_condition, _animationValue);
954 transform->setUpdateCallback(uc);
955 parent.addChild(transform);
960 // Don't create a new state state everytime we need GL_NORMALIZE!
964 Mutex normalizeMutex;
966 osg::StateSet* getNormalizeStateSet()
968 static osg::ref_ptr<osg::StateSet> normalizeStateSet;
969 ScopedLock<Mutex> lock(normalizeMutex);
970 if (!normalizeStateSet.valid()) {
971 normalizeStateSet = new osg::StateSet;
972 normalizeStateSet->setMode(GL_NORMALIZE, osg::StateAttribute::ON);
973 normalizeStateSet->setDataVariance(osg::Object::STATIC);
975 return normalizeStateSet.get();
979 ////////////////////////////////////////////////////////////////////////
980 // Implementation of dist scale animation
981 ////////////////////////////////////////////////////////////////////////
983 class SGDistScaleAnimation::Transform : public osg::Transform {
985 Transform() : _min_v(0.0), _max_v(0.0), _factor(0.0), _offset(0.0) {}
986 Transform(const Transform& rhs,
987 const osg::CopyOp& copyOp = osg::CopyOp::SHALLOW_COPY)
988 : osg::Transform(rhs, copyOp), _table(rhs._table), _center(rhs._center),
989 _min_v(rhs._min_v), _max_v(rhs._max_v), _factor(rhs._factor),
993 META_Node(simgear, SGDistScaleAnimation::Transform);
994 Transform(const SGPropertyNode* configNode)
996 setName(configNode->getStringValue("name", "dist scale animation"));
997 setReferenceFrame(RELATIVE_RF);
998 setStateSet(getNormalizeStateSet());
999 _factor = configNode->getFloatValue("factor", 1);
1000 _offset = configNode->getFloatValue("offset", 0);
1001 _min_v = configNode->getFloatValue("min", SGLimitsf::epsilon());
1002 _max_v = configNode->getFloatValue("max", SGLimitsf::max());
1003 _table = read_interpolation_table(configNode);
1004 _center[0] = configNode->getFloatValue("center/x-m", 0);
1005 _center[1] = configNode->getFloatValue("center/y-m", 0);
1006 _center[2] = configNode->getFloatValue("center/z-m", 0);
1008 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
1009 osg::NodeVisitor* nv) const
1011 osg::Matrix transform;
1012 double scale_factor = computeScaleFactor(nv);
1013 transform(0,0) = scale_factor;
1014 transform(1,1) = scale_factor;
1015 transform(2,2) = scale_factor;
1016 transform(3,0) = _center[0]*(1 - scale_factor);
1017 transform(3,1) = _center[1]*(1 - scale_factor);
1018 transform(3,2) = _center[2]*(1 - scale_factor);
1019 matrix.preMult(transform);
1023 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
1024 osg::NodeVisitor* nv) const
1026 double scale_factor = computeScaleFactor(nv);
1027 if (fabs(scale_factor) <= SGLimits<double>::min())
1029 osg::Matrix transform;
1030 double rScaleFactor = 1/scale_factor;
1031 transform(0,0) = rScaleFactor;
1032 transform(1,1) = rScaleFactor;
1033 transform(2,2) = rScaleFactor;
1034 transform(3,0) = _center[0]*(1 - rScaleFactor);
1035 transform(3,1) = _center[1]*(1 - rScaleFactor);
1036 transform(3,2) = _center[2]*(1 - rScaleFactor);
1037 matrix.postMult(transform);
1041 static bool writeLocalData(const osg::Object& obj, osgDB::Output& fw)
1043 const Transform& trans = static_cast<const Transform&>(obj);
1044 fw.indent() << "center " << trans._center << "\n";
1045 fw.indent() << "min_v " << trans._min_v << "\n";
1046 fw.indent() << "max_v " << trans._max_v << "\n";
1047 fw.indent() << "factor " << trans._factor << "\n";
1048 fw.indent() << "offset " << trans._offset << "\n";
1052 double computeScaleFactor(osg::NodeVisitor* nv) const
1057 double scale_factor = (toOsg(_center) - nv->getEyePoint()).length();
1059 scale_factor = _factor * scale_factor + _offset;
1061 scale_factor = _table->interpolate( scale_factor );
1063 if (scale_factor < _min_v)
1064 scale_factor = _min_v;
1065 if (scale_factor > _max_v)
1066 scale_factor = _max_v;
1068 return scale_factor;
1071 SGSharedPtr<SGInterpTable> _table;
1080 SGDistScaleAnimation::SGDistScaleAnimation(const SGPropertyNode* configNode,
1081 SGPropertyNode* modelRoot) :
1082 SGAnimation(configNode, modelRoot)
1087 SGDistScaleAnimation::createAnimationGroup(osg::Group& parent)
1089 Transform* transform = new Transform(getConfig());
1090 parent.addChild(transform);
1096 osgDB::RegisterDotOsgWrapperProxy distScaleAnimationTransformProxy
1098 new SGDistScaleAnimation::Transform,
1099 "SGDistScaleAnimation::Transform",
1100 "Object Node Transform SGDistScaleAnimation::Transform Group",
1102 &SGDistScaleAnimation::Transform::writeLocalData
1106 ////////////////////////////////////////////////////////////////////////
1107 // Implementation of flash animation
1108 ////////////////////////////////////////////////////////////////////////
1110 class SGFlashAnimation::Transform : public osg::Transform {
1112 Transform() : _power(0.0), _factor(0.0), _offset(0.0), _min_v(0.0),
1113 _max_v(0.0), _two_sides(false)
1116 Transform(const Transform& rhs,
1117 const osg::CopyOp& copyOp = osg::CopyOp::SHALLOW_COPY)
1118 : osg::Transform(rhs, copyOp), _center(rhs._center), _axis(rhs._axis),
1119 _power(rhs._power), _factor(rhs._factor), _offset(rhs._offset),
1120 _min_v(rhs._min_v), _max_v(rhs._max_v), _two_sides(rhs._two_sides)
1123 META_Node(simgear, SGFlashAnimation::Transform);
1125 Transform(const SGPropertyNode* configNode)
1127 setReferenceFrame(RELATIVE_RF);
1128 setName(configNode->getStringValue("name", "flash animation"));
1129 setStateSet(getNormalizeStateSet());
1131 _axis[0] = configNode->getFloatValue("axis/x", 0);
1132 _axis[1] = configNode->getFloatValue("axis/y", 0);
1133 _axis[2] = configNode->getFloatValue("axis/z", 1);
1136 _center[0] = configNode->getFloatValue("center/x-m", 0);
1137 _center[1] = configNode->getFloatValue("center/y-m", 0);
1138 _center[2] = configNode->getFloatValue("center/z-m", 0);
1140 _offset = configNode->getFloatValue("offset", 0);
1141 _factor = configNode->getFloatValue("factor", 1);
1142 _power = configNode->getFloatValue("power", 1);
1143 _two_sides = configNode->getBoolValue("two-sides", false);
1145 _min_v = configNode->getFloatValue("min", SGLimitsf::epsilon());
1146 _max_v = configNode->getFloatValue("max", 1);
1148 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
1149 osg::NodeVisitor* nv) const
1151 osg::Matrix transform;
1152 double scale_factor = computeScaleFactor(nv);
1153 transform(0,0) = scale_factor;
1154 transform(1,1) = scale_factor;
1155 transform(2,2) = scale_factor;
1156 transform(3,0) = _center[0]*(1 - scale_factor);
1157 transform(3,1) = _center[1]*(1 - scale_factor);
1158 transform(3,2) = _center[2]*(1 - scale_factor);
1159 matrix.preMult(transform);
1163 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
1164 osg::NodeVisitor* nv) const
1166 double scale_factor = computeScaleFactor(nv);
1167 if (fabs(scale_factor) <= SGLimits<double>::min())
1169 osg::Matrix transform;
1170 double rScaleFactor = 1/scale_factor;
1171 transform(0,0) = rScaleFactor;
1172 transform(1,1) = rScaleFactor;
1173 transform(2,2) = rScaleFactor;
1174 transform(3,0) = _center[0]*(1 - rScaleFactor);
1175 transform(3,1) = _center[1]*(1 - rScaleFactor);
1176 transform(3,2) = _center[2]*(1 - rScaleFactor);
1177 matrix.postMult(transform);
1181 static bool writeLocalData(const osg::Object& obj, osgDB::Output& fw)
1183 const Transform& trans = static_cast<const Transform&>(obj);
1184 fw.indent() << "center " << trans._center[0] << " "
1185 << trans._center[1] << " " << trans._center[2] << " " << "\n";
1186 fw.indent() << "axis " << trans._axis[0] << " "
1187 << trans._axis[1] << " " << trans._axis[2] << " " << "\n";
1188 fw.indent() << "power " << trans._power << " \n";
1189 fw.indent() << "min_v " << trans._min_v << "\n";
1190 fw.indent() << "max_v " << trans._max_v << "\n";
1191 fw.indent() << "factor " << trans._factor << "\n";
1192 fw.indent() << "offset " << trans._offset << "\n";
1193 fw.indent() << "twosides " << (trans._two_sides ? "true" : "false") << "\n";
1197 double computeScaleFactor(osg::NodeVisitor* nv) const
1202 osg::Vec3 localEyeToCenter = nv->getEyePoint() - _center;
1203 localEyeToCenter.normalize();
1205 double cos_angle = localEyeToCenter*_axis;
1206 double scale_factor = 0;
1207 if ( _two_sides && cos_angle < 0 )
1208 scale_factor = _factor * pow( -cos_angle, _power ) + _offset;
1209 else if ( cos_angle > 0 )
1210 scale_factor = _factor * pow( cos_angle, _power ) + _offset;
1212 if ( scale_factor < _min_v )
1213 scale_factor = _min_v;
1214 if ( scale_factor > _max_v )
1215 scale_factor = _max_v;
1217 return scale_factor;
1220 virtual osg::BoundingSphere computeBound() const
1222 // avoid being culled away by small feature culling
1223 osg::BoundingSphere bs = osg::Group::computeBound();
1224 bs.radius() *= _max_v;
1231 double _power, _factor, _offset, _min_v, _max_v;
1236 SGFlashAnimation::SGFlashAnimation(const SGPropertyNode* configNode,
1237 SGPropertyNode* modelRoot) :
1238 SGAnimation(configNode, modelRoot)
1243 SGFlashAnimation::createAnimationGroup(osg::Group& parent)
1245 Transform* transform = new Transform(getConfig());
1246 parent.addChild(transform);
1252 osgDB::RegisterDotOsgWrapperProxy flashAnimationTransformProxy
1254 new SGFlashAnimation::Transform,
1255 "SGFlashAnimation::Transform",
1256 "Object Node Transform SGFlashAnimation::Transform Group",
1258 &SGFlashAnimation::Transform::writeLocalData
1262 ////////////////////////////////////////////////////////////////////////
1263 // Implementation of billboard animation
1264 ////////////////////////////////////////////////////////////////////////
1266 class SGBillboardAnimation::Transform : public osg::Transform {
1268 Transform() : _spherical(true) {}
1269 Transform(const Transform& rhs,
1270 const osg::CopyOp& copyOp = osg::CopyOp::SHALLOW_COPY)
1271 : osg::Transform(rhs, copyOp), _spherical(rhs._spherical) {}
1272 META_Node(simgear, SGBillboardAnimation::Transform);
1273 Transform(const SGPropertyNode* configNode) :
1274 _spherical(configNode->getBoolValue("spherical", true))
1276 setReferenceFrame(RELATIVE_RF);
1277 setName(configNode->getStringValue("name", "billboard animation"));
1279 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
1280 osg::NodeVisitor* nv) const
1282 // More or less taken from plibs ssgCutout
1284 matrix(0,0) = 1; matrix(0,1) = 0; matrix(0,2) = 0;
1285 matrix(1,0) = 0; matrix(1,1) = 0; matrix(1,2) = -1;
1286 matrix(2,0) = 0; matrix(2,1) = 1; matrix(2,2) = 0;
1288 osg::Vec3 zAxis(matrix(2, 0), matrix(2, 1), matrix(2, 2));
1289 osg::Vec3 xAxis = osg::Vec3(0, 0, -1)^zAxis;
1290 osg::Vec3 yAxis = zAxis^xAxis;
1296 matrix(0,0) = xAxis[0]; matrix(0,1) = xAxis[1]; matrix(0,2) = xAxis[2];
1297 matrix(1,0) = yAxis[0]; matrix(1,1) = yAxis[1]; matrix(1,2) = yAxis[2];
1298 matrix(2,0) = zAxis[0]; matrix(2,1) = zAxis[1]; matrix(2,2) = zAxis[2];
1303 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
1304 osg::NodeVisitor* nv) const
1306 // Hmm, don't yet know how to get that back ...
1309 static bool writeLocalData(const osg::Object& obj, osgDB::Output& fw)
1311 const Transform& trans = static_cast<const Transform&>(obj);
1313 fw.indent() << (trans._spherical ? "true" : "false") << "\n";
1321 SGBillboardAnimation::SGBillboardAnimation(const SGPropertyNode* configNode,
1322 SGPropertyNode* modelRoot) :
1323 SGAnimation(configNode, modelRoot)
1328 SGBillboardAnimation::createAnimationGroup(osg::Group& parent)
1330 Transform* transform = new Transform(getConfig());
1331 parent.addChild(transform);
1337 osgDB::RegisterDotOsgWrapperProxy billboardAnimationTransformProxy
1339 new SGBillboardAnimation::Transform,
1340 "SGBillboardAnimation::Transform",
1341 "Object Node Transform SGBillboardAnimation::Transform Group",
1343 &SGBillboardAnimation::Transform::writeLocalData
1347 ////////////////////////////////////////////////////////////////////////
1348 // Implementation of a range animation
1349 ////////////////////////////////////////////////////////////////////////
1351 class SGRangeAnimation::UpdateCallback : public osg::NodeCallback {
1353 UpdateCallback(const SGCondition* condition,
1354 const SGExpressiond* minAnimationValue,
1355 const SGExpressiond* maxAnimationValue,
1356 double minValue, double maxValue) :
1357 _condition(condition),
1358 _minAnimationValue(minAnimationValue),
1359 _maxAnimationValue(maxAnimationValue),
1360 _minStaticValue(minValue),
1361 _maxStaticValue(maxValue)
1363 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1365 osg::LOD* lod = static_cast<osg::LOD*>(node);
1366 if (!_condition || _condition->test()) {
1368 if (_minAnimationValue)
1369 minRange = _minAnimationValue->getValue();
1371 minRange = _minStaticValue;
1373 if (_maxAnimationValue)
1374 maxRange = _maxAnimationValue->getValue();
1376 maxRange = _maxStaticValue;
1377 lod->setRange(0, minRange, maxRange);
1379 lod->setRange(0, 0, SGLimitsf::max());
1385 SGSharedPtr<const SGCondition> _condition;
1386 SGSharedPtr<const SGExpressiond> _minAnimationValue;
1387 SGSharedPtr<const SGExpressiond> _maxAnimationValue;
1388 double _minStaticValue;
1389 double _maxStaticValue;
1392 SGRangeAnimation::SGRangeAnimation(const SGPropertyNode* configNode,
1393 SGPropertyNode* modelRoot) :
1394 SGAnimation(configNode, modelRoot)
1396 _condition = getCondition();
1398 std::string inputPropertyName;
1399 inputPropertyName = configNode->getStringValue("min-property", "");
1400 if (!inputPropertyName.empty()) {
1401 SGPropertyNode* inputProperty;
1402 inputProperty = modelRoot->getNode(inputPropertyName, true);
1403 SGSharedPtr<SGExpressiond> value;
1404 value = new SGPropertyExpression<double>(inputProperty);
1406 value = read_factor_offset(configNode, value, "min-factor", "min-offset");
1407 _minAnimationValue = value->simplify();
1409 inputPropertyName = configNode->getStringValue("max-property", "");
1410 if (!inputPropertyName.empty()) {
1411 SGPropertyNode* inputProperty;
1412 inputProperty = modelRoot->getNode(inputPropertyName.c_str(), true);
1414 SGSharedPtr<SGExpressiond> value;
1415 value = new SGPropertyExpression<double>(inputProperty);
1417 value = read_factor_offset(configNode, value, "max-factor", "max-offset");
1418 _maxAnimationValue = value->simplify();
1421 _initialValue[0] = configNode->getDoubleValue("min-m", 0);
1422 _initialValue[0] *= configNode->getDoubleValue("min-factor", 1);
1423 _initialValue[1] = configNode->getDoubleValue("max-m", SGLimitsf::max());
1424 _initialValue[1] *= configNode->getDoubleValue("max-factor", 1);
1428 SGRangeAnimation::createAnimationGroup(osg::Group& parent)
1430 osg::Group* group = new osg::Group;
1431 group->setName("range animation group");
1433 osg::LOD* lod = new osg::LOD;
1434 lod->setName("range animation node");
1435 parent.addChild(lod);
1437 lod->addChild(group, _initialValue[0], _initialValue[1]);
1438 lod->setCenterMode(osg::LOD::USE_BOUNDING_SPHERE_CENTER);
1439 lod->setRangeMode(osg::LOD::DISTANCE_FROM_EYE_POINT);
1440 if (_minAnimationValue || _maxAnimationValue || _condition) {
1442 uc = new UpdateCallback(_condition, _minAnimationValue, _maxAnimationValue,
1443 _initialValue[0], _initialValue[1]);
1444 lod->setUpdateCallback(uc);
1450 ////////////////////////////////////////////////////////////////////////
1451 // Implementation of a select animation
1452 ////////////////////////////////////////////////////////////////////////
1454 SGSelectAnimation::SGSelectAnimation(const SGPropertyNode* configNode,
1455 SGPropertyNode* modelRoot) :
1456 SGAnimation(configNode, modelRoot)
1461 SGSelectAnimation::createAnimationGroup(osg::Group& parent)
1463 // if no condition given, this is a noop.
1464 SGSharedPtr<SGCondition const> condition = getCondition();
1465 // trick, gets deleted with all its 'animated' children
1466 // when the animation installer returns
1468 return new osg::Group;
1469 simgear::ConditionNode* cn = new simgear::ConditionNode;
1470 cn->setName("select animation node");
1471 cn->setCondition(condition.ptr());
1472 osg::Group* grp = new osg::Group;
1474 parent.addChild(cn);
1480 ////////////////////////////////////////////////////////////////////////
1481 // Implementation of alpha test animation
1482 ////////////////////////////////////////////////////////////////////////
1484 SGAlphaTestAnimation::SGAlphaTestAnimation(const SGPropertyNode* configNode,
1485 SGPropertyNode* modelRoot) :
1486 SGAnimation(configNode, modelRoot)
1492 // Keep one copy of the most common alpha test its state set.
1493 ReentrantMutex alphaTestMutex;
1494 osg::ref_ptr<osg::AlphaFunc> standardAlphaFunc;
1495 osg::ref_ptr<osg::StateSet> alphaFuncStateSet;
1497 osg::AlphaFunc* makeAlphaFunc(float clamp)
1499 ScopedLock<ReentrantMutex> lock(alphaTestMutex);
1500 if (osg::equivalent(clamp, 0.01f)) {
1501 if (standardAlphaFunc.valid())
1502 return standardAlphaFunc.get();
1505 osg::AlphaFunc* alphaFunc = new osg::AlphaFunc;
1506 alphaFunc->setFunction(osg::AlphaFunc::GREATER);
1507 alphaFunc->setReferenceValue(clamp);
1508 alphaFunc->setDataVariance(osg::Object::STATIC);
1509 if (osg::equivalent(clamp, 0.01f))
1510 standardAlphaFunc = alphaFunc;
1514 osg::StateSet* makeAlphaTestStateSet(float clamp)
1516 using namespace OpenThreads;
1517 ScopedLock<ReentrantMutex> lock(alphaTestMutex);
1518 if (osg::equivalent(clamp, 0.01f)) {
1519 if (alphaFuncStateSet.valid())
1520 return alphaFuncStateSet.get();
1522 osg::AlphaFunc* alphaFunc = makeAlphaFunc(clamp);
1523 osg::StateSet* stateSet = new osg::StateSet;
1524 stateSet->setAttributeAndModes(alphaFunc,
1525 (osg::StateAttribute::ON
1526 | osg::StateAttribute::OVERRIDE));
1527 stateSet->setDataVariance(osg::Object::STATIC);
1528 if (osg::equivalent(clamp, 0.01f))
1529 alphaFuncStateSet = stateSet;
1534 SGAlphaTestAnimation::install(osg::Node& node)
1536 SGAnimation::install(node);
1538 float alphaClamp = getConfig()->getFloatValue("alpha-factor", 0);
1539 osg::StateSet* stateSet = node.getStateSet();
1541 node.setStateSet(makeAlphaTestStateSet(alphaClamp));
1543 stateSet->setAttributeAndModes(makeAlphaFunc(alphaClamp),
1544 (osg::StateAttribute::ON
1545 | osg::StateAttribute::OVERRIDE));
1550 //////////////////////////////////////////////////////////////////////
1551 // Blend animation installer
1552 //////////////////////////////////////////////////////////////////////
1554 // XXX This needs to be replaced by something using TexEnvCombine to
1555 // change the blend factor. Changing the alpha values in the geometry
1557 class SGBlendAnimation::BlendVisitor : public osg::NodeVisitor {
1559 BlendVisitor(float blend) :
1560 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
1562 { setVisitorType(osg::NodeVisitor::NODE_VISITOR); }
1563 virtual void apply(osg::Node& node)
1565 updateStateSet(node.getStateSet());
1568 virtual void apply(osg::Geode& node)
1570 apply((osg::Node&)node);
1571 unsigned nDrawables = node.getNumDrawables();
1572 for (unsigned i = 0; i < nDrawables; ++i) {
1573 osg::Drawable* drawable = node.getDrawable(i);
1574 osg::Geometry* geometry = drawable->asGeometry();
1577 osg::Array* array = geometry->getColorArray();
1580 osg::Vec4Array* vec4Array = dynamic_cast<osg::Vec4Array*>(array);
1583 for (unsigned k = 0; k < vec4Array->size(); ++k) {
1584 (*vec4Array)[k][3] = _blend;
1587 updateStateSet(drawable->getStateSet());
1590 void updateStateSet(osg::StateSet* stateSet)
1594 osg::StateAttribute* stateAttribute;
1595 stateAttribute = stateSet->getAttribute(osg::StateAttribute::MATERIAL);
1596 if (!stateAttribute)
1598 osg::Material* material = dynamic_cast<osg::Material*>(stateAttribute);
1601 material->setAlpha(osg::Material::FRONT_AND_BACK, _blend);
1603 stateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
1604 stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
1606 stateSet->setRenderingHint(osg::StateSet::DEFAULT_BIN);
1613 class SGBlendAnimation::UpdateCallback : public osg::NodeCallback {
1615 UpdateCallback(const SGPropertyNode* configNode, const SGExpressiond* v) :
1619 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1621 double blend = _animationValue->getValue();
1622 if (blend != _prev_value) {
1623 _prev_value = blend;
1624 BlendVisitor visitor(1-blend);
1625 node->accept(visitor);
1631 SGSharedPtr<SGExpressiond const> _animationValue;
1635 SGBlendAnimation::SGBlendAnimation(const SGPropertyNode* configNode,
1636 SGPropertyNode* modelRoot)
1637 : SGAnimation(configNode, modelRoot),
1638 _animationValue(read_value(configNode, modelRoot, "", 0, 1))
1643 SGBlendAnimation::createAnimationGroup(osg::Group& parent)
1645 if (!_animationValue)
1648 osg::Group* group = new osg::Switch;
1649 group->setName("blend animation node");
1650 group->setUpdateCallback(new UpdateCallback(getConfig(), _animationValue));
1651 parent.addChild(group);
1656 SGBlendAnimation::install(osg::Node& node)
1658 SGAnimation::install(node);
1659 // make sure we do not change common geometries,
1660 // that also creates new display lists for these subgeometries.
1661 cloneDrawables(node);
1662 DoDrawArraysVisitor visitor;
1663 node.accept(visitor);
1667 //////////////////////////////////////////////////////////////////////
1668 // Timed animation installer
1669 //////////////////////////////////////////////////////////////////////
1673 class SGTimedAnimation::UpdateCallback : public osg::NodeCallback {
1675 UpdateCallback(const SGPropertyNode* configNode) :
1678 _duration_sec(configNode->getDoubleValue("duration-sec", 1)),
1679 _last_time_sec(SGLimitsd::max()),
1680 _use_personality(configNode->getBoolValue("use-personality", false))
1682 std::vector<SGSharedPtr<SGPropertyNode> > nodes;
1683 nodes = configNode->getChildren("branch-duration-sec");
1684 for (size_t i = 0; i < nodes.size(); ++i) {
1685 unsigned ind = nodes[ i ]->getIndex();
1686 while ( ind >= _durations.size() ) {
1687 _durations.push_back(DurationSpec(_duration_sec));
1689 SGPropertyNode_ptr rNode = nodes[i]->getChild("random");
1691 _durations[ind] = DurationSpec(nodes[ i ]->getDoubleValue());
1693 _durations[ind] = DurationSpec(rNode->getDoubleValue( "min", 0),
1694 rNode->getDoubleValue( "max", 1));
1698 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1700 assert(dynamic_cast<osg::Switch*>(node));
1701 osg::Switch* sw = static_cast<osg::Switch*>(node);
1703 unsigned nChildren = sw->getNumChildren();
1705 // blow up the durations vector to the required size
1706 while (_durations.size() < nChildren) {
1707 _durations.push_back(_duration_sec);
1709 // make sure the current index is an duration that really exists
1710 _current_index = _current_index % nChildren;
1712 // update the time and compute the current systems time value
1713 double t = nv->getFrameStamp()->getReferenceTime();
1714 if (_last_time_sec == SGLimitsd::max()) {
1717 double dt = t - _last_time_sec;
1718 if (_use_personality)
1719 dt *= 1 + 0.2*(0.5 - sg_random());
1724 double currentDuration = _durations[_current_index].get();
1725 while (currentDuration < _reminder) {
1726 _reminder -= currentDuration;
1727 _current_index = (_current_index + 1) % nChildren;
1728 currentDuration = _durations[_current_index].get();
1731 sw->setSingleChildOn(_current_index);
1737 struct DurationSpec {
1738 DurationSpec(double t) :
1739 minTime(SGMiscd::max(0.01, t)),
1740 maxTime(SGMiscd::max(0.01, t))
1742 DurationSpec(double t0, double t1) :
1743 minTime(SGMiscd::max(0.01, t0)),
1744 maxTime(SGMiscd::max(0.01, t1))
1747 { return minTime + sg_random()*(maxTime - minTime); }
1751 std::vector<DurationSpec> _durations;
1752 unsigned _current_index;
1754 double _duration_sec;
1755 double _last_time_sec;
1756 bool _use_personality;
1760 SGTimedAnimation::SGTimedAnimation(const SGPropertyNode* configNode,
1761 SGPropertyNode* modelRoot)
1762 : SGAnimation(configNode, modelRoot)
1767 SGTimedAnimation::createAnimationGroup(osg::Group& parent)
1769 osg::Switch* sw = new osg::Switch;
1770 sw->setName("timed animation node");
1771 sw->setUpdateCallback(new UpdateCallback(getConfig()));
1772 parent.addChild(sw);
1777 ////////////////////////////////////////////////////////////////////////
1778 // dynamically switch on/off shadows
1779 ////////////////////////////////////////////////////////////////////////
1781 class SGShadowAnimation::UpdateCallback : public osg::NodeCallback {
1783 UpdateCallback(const SGCondition* condition) :
1784 _condition(condition)
1786 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1788 if (_condition->test())
1789 node->setNodeMask( SG_NODEMASK_CASTSHADOW_BIT | node->getNodeMask());
1791 node->setNodeMask(~SG_NODEMASK_CASTSHADOW_BIT & node->getNodeMask());
1796 SGSharedPtr<const SGCondition> _condition;
1799 SGShadowAnimation::SGShadowAnimation(const SGPropertyNode* configNode,
1800 SGPropertyNode* modelRoot) :
1801 SGAnimation(configNode, modelRoot)
1806 SGShadowAnimation::createAnimationGroup(osg::Group& parent)
1808 SGSharedPtr<SGCondition const> condition = getCondition();
1812 osg::Group* group = new osg::Group;
1813 group->setName("shadow animation");
1814 group->setUpdateCallback(new UpdateCallback(condition));
1815 parent.addChild(group);
1820 ////////////////////////////////////////////////////////////////////////
1821 // Implementation of SGTexTransformAnimation
1822 ////////////////////////////////////////////////////////////////////////
1824 class SGTexTransformAnimation::Transform : public SGReferenced {
1829 virtual ~Transform()
1831 void setValue(double value)
1833 virtual void transform(osg::Matrix&) = 0;
1838 class SGTexTransformAnimation::Translation :
1839 public SGTexTransformAnimation::Transform {
1841 Translation(const SGVec3d& axis) :
1844 virtual void transform(osg::Matrix& matrix)
1847 set_translation(tmp, _value, _axis);
1848 matrix.preMult(tmp);
1854 class SGTexTransformAnimation::Rotation :
1855 public SGTexTransformAnimation::Transform {
1857 Rotation(const SGVec3d& axis, const SGVec3d& center) :
1861 virtual void transform(osg::Matrix& matrix)
1864 set_rotation(tmp, _value, _center, _axis);
1865 matrix.preMult(tmp);
1872 class SGTexTransformAnimation::UpdateCallback :
1873 public osg::StateAttribute::Callback {
1875 UpdateCallback(const SGCondition* condition) :
1876 _condition(condition)
1878 virtual void operator () (osg::StateAttribute* sa, osg::NodeVisitor*)
1880 if (!_condition || _condition->test()) {
1881 TransformList::const_iterator i;
1882 for (i = _transforms.begin(); i != _transforms.end(); ++i)
1883 i->transform->setValue(i->value->getValue());
1885 assert(dynamic_cast<osg::TexMat*>(sa));
1886 osg::TexMat* texMat = static_cast<osg::TexMat*>(sa);
1887 texMat->getMatrix().makeIdentity();
1888 TransformList::const_iterator i;
1889 for (i = _transforms.begin(); i != _transforms.end(); ++i)
1890 i->transform->transform(texMat->getMatrix());
1892 void appendTransform(Transform* transform, SGExpressiond* value)
1894 Entry entry = { transform, value };
1895 transform->transform(_matrix);
1896 _transforms.push_back(entry);
1901 SGSharedPtr<Transform> transform;
1902 SGSharedPtr<const SGExpressiond> value;
1904 typedef std::vector<Entry> TransformList;
1905 TransformList _transforms;
1906 SGSharedPtr<const SGCondition> _condition;
1907 osg::Matrix _matrix;
1910 SGTexTransformAnimation::SGTexTransformAnimation(const SGPropertyNode* configNode,
1911 SGPropertyNode* modelRoot) :
1912 SGAnimation(configNode, modelRoot)
1917 SGTexTransformAnimation::createAnimationGroup(osg::Group& parent)
1919 osg::Group* group = new osg::Group;
1920 group->setName("texture transform group");
1921 osg::StateSet* stateSet = group->getOrCreateStateSet();
1922 stateSet->setDataVariance(osg::Object::DYNAMIC);
1923 osg::TexMat* texMat = new osg::TexMat;
1924 UpdateCallback* updateCallback = new UpdateCallback(getCondition());
1925 // interpret the configs ...
1926 std::string type = getType();
1928 if (type == "textranslate") {
1929 appendTexTranslate(getConfig(), updateCallback);
1930 } else if (type == "texrotate") {
1931 appendTexRotate(getConfig(), updateCallback);
1932 } else if (type == "texmultiple") {
1933 std::vector<SGSharedPtr<SGPropertyNode> > transformConfigs;
1934 transformConfigs = getConfig()->getChildren("transform");
1935 for (unsigned i = 0; i < transformConfigs.size(); ++i) {
1936 std::string subtype = transformConfigs[i]->getStringValue("subtype", "");
1937 if (subtype == "textranslate")
1938 appendTexTranslate(transformConfigs[i], updateCallback);
1939 else if (subtype == "texrotate")
1940 appendTexRotate(transformConfigs[i], updateCallback);
1942 SG_LOG(SG_INPUT, SG_ALERT,
1943 "Ignoring unknown texture transform subtype");
1946 SG_LOG(SG_INPUT, SG_ALERT, "Ignoring unknown texture transform type");
1949 texMat->setUpdateCallback(updateCallback);
1950 stateSet->setTextureAttribute(0, texMat);
1951 parent.addChild(group);
1956 SGTexTransformAnimation::appendTexTranslate(const SGPropertyNode* config,
1957 UpdateCallback* updateCallback)
1959 std::string propertyName = config->getStringValue("property", "");
1960 SGSharedPtr<SGExpressiond> value;
1961 if (propertyName.empty())
1962 value = new SGConstExpression<double>(0);
1964 SGPropertyNode* inputProperty = getModelRoot()->getNode(propertyName, true);
1965 value = new SGPropertyExpression<double>(inputProperty);
1968 SGInterpTable* table = read_interpolation_table(config);
1970 value = new SGInterpTableExpression<double>(value, table);
1971 double biasValue = config->getDoubleValue("bias", 0);
1973 value = new SGBiasExpression<double>(value, biasValue);
1974 value = new SGStepExpression<double>(value,
1975 config->getDoubleValue("step", 0),
1976 config->getDoubleValue("scroll", 0));
1977 value = value->simplify();
1979 double biasValue = config->getDoubleValue("bias", 0);
1981 value = new SGBiasExpression<double>(value, biasValue);
1982 value = new SGStepExpression<double>(value,
1983 config->getDoubleValue("step", 0),
1984 config->getDoubleValue("scroll", 0));
1985 value = read_offset_factor(config, value, "factor", "offset");
1987 if (config->hasChild("min") || config->hasChild("max")) {
1988 double minClip = config->getDoubleValue("min", -SGLimitsd::max());
1989 double maxClip = config->getDoubleValue("max", SGLimitsd::max());
1990 value = new SGClipExpression<double>(value, minClip, maxClip);
1992 value = value->simplify();
1994 SGVec3d axis(config->getDoubleValue("axis/x", 0),
1995 config->getDoubleValue("axis/y", 0),
1996 config->getDoubleValue("axis/z", 0));
1997 Translation* translation;
1998 translation = new Translation(normalize(axis));
1999 translation->setValue(config->getDoubleValue("starting-position", 0));
2000 updateCallback->appendTransform(translation, value);
2004 SGTexTransformAnimation::appendTexRotate(const SGPropertyNode* config,
2005 UpdateCallback* updateCallback)
2007 std::string propertyName = config->getStringValue("property", "");
2008 SGSharedPtr<SGExpressiond> value;
2009 if (propertyName.empty())
2010 value = new SGConstExpression<double>(0);
2012 SGPropertyNode* inputProperty = getModelRoot()->getNode(propertyName, true);
2013 value = new SGPropertyExpression<double>(inputProperty);
2016 SGInterpTable* table = read_interpolation_table(config);
2018 value = new SGInterpTableExpression<double>(value, table);
2019 double biasValue = config->getDoubleValue("bias", 0);
2021 value = new SGBiasExpression<double>(value, biasValue);
2022 value = new SGStepExpression<double>(value,
2023 config->getDoubleValue("step", 0),
2024 config->getDoubleValue("scroll", 0));
2025 value = value->simplify();
2027 double biasValue = config->getDoubleValue("bias", 0);
2029 value = new SGBiasExpression<double>(value, biasValue);
2030 value = new SGStepExpression<double>(value,
2031 config->getDoubleValue("step", 0),
2032 config->getDoubleValue("scroll", 0));
2033 value = read_offset_factor(config, value, "factor", "offset-deg");
2035 if (config->hasChild("min-deg") || config->hasChild("max-deg")) {
2036 double minClip = config->getDoubleValue("min-deg", -SGLimitsd::max());
2037 double maxClip = config->getDoubleValue("max-deg", SGLimitsd::max());
2038 value = new SGClipExpression<double>(value, minClip, maxClip);
2040 value = value->simplify();
2042 SGVec3d axis(config->getDoubleValue("axis/x", 0),
2043 config->getDoubleValue("axis/y", 0),
2044 config->getDoubleValue("axis/z", 0));
2045 SGVec3d center(config->getDoubleValue("center/x", 0),
2046 config->getDoubleValue("center/y", 0),
2047 config->getDoubleValue("center/z", 0));
2049 rotation = new Rotation(normalize(axis), center);
2050 rotation->setValue(config->getDoubleValue("starting-position-deg", 0));
2051 updateCallback->appendTransform(rotation, value);
2055 ////////////////////////////////////////////////////////////////////////
2056 // Implementation of SGPickAnimation
2057 ////////////////////////////////////////////////////////////////////////
2059 class SGPickAnimation::PickCallback : public SGPickCallback {
2061 PickCallback(const SGPropertyNode* configNode,
2062 SGPropertyNode* modelRoot) :
2063 _repeatable(configNode->getBoolValue("repeatable", false)),
2064 _repeatInterval(configNode->getDoubleValue("interval-sec", 0.1))
2066 SG_LOG(SG_INPUT, SG_DEBUG, "Reading all bindings");
2067 std::vector<SGPropertyNode_ptr> bindings;
2069 bindings = configNode->getChildren("button");
2070 for (unsigned int i = 0; i < bindings.size(); ++i) {
2071 _buttons.push_back( bindings[i]->getIntValue() );
2073 bindings = configNode->getChildren("binding");
2074 for (unsigned int i = 0; i < bindings.size(); ++i) {
2075 _bindingsDown.push_back(new SGBinding(bindings[i], modelRoot));
2078 const SGPropertyNode* upNode = configNode->getChild("mod-up");
2081 bindings = upNode->getChildren("binding");
2082 for (unsigned int i = 0; i < bindings.size(); ++i) {
2083 _bindingsUp.push_back(new SGBinding(bindings[i], modelRoot));
2086 virtual bool buttonPressed(int button, const Info&)
2089 for( std::vector<int>::iterator it = _buttons.begin(); it != _buttons.end(); it++ ) {
2090 if( *it == button ) {
2097 SGBindingList::const_iterator i;
2098 for (i = _bindingsDown.begin(); i != _bindingsDown.end(); ++i)
2100 _repeatTime = -_repeatInterval; // anti-bobble: delay start of repeat
2103 virtual void buttonReleased(void)
2105 SGBindingList::const_iterator i;
2106 for (i = _bindingsUp.begin(); i != _bindingsUp.end(); ++i)
2109 virtual void update(double dt)
2115 while (_repeatInterval < _repeatTime) {
2116 _repeatTime -= _repeatInterval;
2117 SGBindingList::const_iterator i;
2118 for (i = _bindingsDown.begin(); i != _bindingsDown.end(); ++i)
2123 SGBindingList _bindingsDown;
2124 SGBindingList _bindingsUp;
2125 std::vector<int> _buttons;
2127 double _repeatInterval;
2131 class VncVisitor : public osg::NodeVisitor {
2133 VncVisitor(double x, double y, int mask) :
2134 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
2135 _texX(x), _texY(y), _mask(mask), _done(false)
2137 SG_LOG(SG_INPUT, SG_DEBUG, "VncVisitor constructor "
2138 << x << "," << y << " mask " << mask);
2141 virtual void apply(osg::Node &node)
2143 // Some nodes have state sets attached
2144 touchStateSet(node.getStateSet());
2148 // See whether we are a geode worth exploring
2149 osg::Geode *g = dynamic_cast<osg::Geode*>(&node);
2151 // Go find all its drawables
2152 int i = g->getNumDrawables();
2154 osg::Drawable *d = g->getDrawable(i);
2155 if (d) touchDrawable(*d);
2157 // Out of optimism, do the same for EffectGeode
2158 simgear::EffectGeode *eg = dynamic_cast<simgear::EffectGeode*>(&node);
2160 for (simgear::EffectGeode::DrawablesIterator di = eg->drawablesBegin();
2161 di != eg->drawablesEnd(); di++) {
2162 touchDrawable(**di);
2164 // Now see whether the EffectGeode has an Effect
2165 simgear::Effect *e = eg->getEffect();
2167 touchStateSet(e->getDefaultStateSet());
2171 inline void touchDrawable(osg::Drawable &d)
2173 osg::StateSet *ss = d.getStateSet();
2177 void touchStateSet(osg::StateSet *ss)
2180 osg::StateAttribute *sa = ss->getTextureAttribute(0,
2181 osg::StateAttribute::TEXTURE);
2183 osg::Texture *t = sa->asTexture();
2185 osg::Image *img = t->getImage(0);
2188 int pixX = _texX * img->s();
2189 int pixY = _texY * img->t();
2190 _done = img->sendPointerEvent(pixX, pixY, _mask);
2191 SG_LOG(SG_INPUT, SG_DEBUG, "VncVisitor image said " << _done
2192 << " to coord " << pixX << "," << pixY);
2196 inline bool wasSuccessful()
2202 double _texX, _texY;
2208 class SGPickAnimation::VncCallback : public SGPickCallback {
2210 VncCallback(const SGPropertyNode* configNode,
2211 SGPropertyNode* modelRoot,
2215 SG_LOG(SG_INPUT, SG_DEBUG, "Configuring VNC callback");
2216 const char *cornernames[3] = {"top-left", "top-right", "bottom-left"};
2217 SGVec3d *cornercoords[3] = {&_topLeft, &_toRight, &_toDown};
2218 for (int c =0; c < 3; c++) {
2219 const SGPropertyNode* cornerNode = configNode->getChild(cornernames[c]);
2220 *cornercoords[c] = SGVec3d(
2221 cornerNode->getDoubleValue("x"),
2222 cornerNode->getDoubleValue("y"),
2223 cornerNode->getDoubleValue("z"));
2225 _toRight -= _topLeft;
2226 _toDown -= _topLeft;
2227 _squaredRight = dot(_toRight, _toRight);
2228 _squaredDown = dot(_toDown, _toDown);
2231 virtual bool buttonPressed(int button, const Info& info)
2233 SGVec3d loc(info.local);
2234 SG_LOG(SG_INPUT, SG_DEBUG, "VNC pressed " << button << ": " << loc);
2236 _x = dot(loc, _toRight) / _squaredRight;
2237 _y = dot(loc, _toDown) / _squaredDown;
2238 if (_x<0) _x = 0; else if (_x > 1) _x = 1;
2239 if (_y<0) _y = 0; else if (_y > 1) _y = 1;
2240 VncVisitor vv(_x, _y, 1 << button);
2242 return vv.wasSuccessful();
2245 virtual void buttonReleased(void)
2247 SG_LOG(SG_INPUT, SG_DEBUG, "VNC release");
2248 VncVisitor vv(_x, _y, 0);
2251 virtual void update(double dt)
2256 osg::ref_ptr<osg::Group> _node;
2257 SGVec3d _topLeft, _toRight, _toDown;
2258 double _squaredRight, _squaredDown;
2261 SGPickAnimation::SGPickAnimation(const SGPropertyNode* configNode,
2262 SGPropertyNode* modelRoot) :
2263 SGAnimation(configNode, modelRoot)
2269 Mutex colorModeUniformMutex;
2270 osg::ref_ptr<osg::Uniform> colorModeUniform;
2274 SGPickAnimation::createAnimationGroup(osg::Group& parent)
2276 osg::Group* commonGroup = new osg::Group;
2278 // Contains the normal geometry that is interactive
2279 osg::ref_ptr<osg::Group> normalGroup = new osg::Group;
2280 normalGroup->setName("pick normal group");
2281 normalGroup->addChild(commonGroup);
2283 // Used to render the geometry with just yellow edges
2284 osg::Group* highlightGroup = new osg::Group;
2285 highlightGroup->setName("pick highlight group");
2286 highlightGroup->setNodeMask(SG_NODEMASK_PICK_BIT);
2287 highlightGroup->addChild(commonGroup);
2288 SGSceneUserData* ud;
2289 ud = SGSceneUserData::getOrCreateSceneUserData(commonGroup);
2291 // add actions that become macro and command invocations
2292 std::vector<SGPropertyNode_ptr> actions;
2293 actions = getConfig()->getChildren("action");
2294 for (unsigned int i = 0; i < actions.size(); ++i)
2295 ud->addPickCallback(new PickCallback(actions[i], getModelRoot()));
2296 // Look for the VNC sessions that want raw mouse input
2297 actions = getConfig()->getChildren("vncaction");
2298 for (unsigned int i = 0; i < actions.size(); ++i)
2299 ud->addPickCallback(new VncCallback(actions[i], getModelRoot(),
2302 // prepare a state set that paints the edges of this object yellow
2303 // The material and texture attributes are set with
2304 // OVERRIDE|PROTECTED in case there is a material animation on a
2305 // higher node in the scene graph, which would have its material
2306 // attribute set with OVERRIDE.
2307 osg::StateSet* stateSet = highlightGroup->getOrCreateStateSet();
2308 osg::Texture2D* white = StateAttributeFactory::instance()->getWhiteTexture();
2309 stateSet->setTextureAttributeAndModes(0, white,
2310 (osg::StateAttribute::ON
2311 | osg::StateAttribute::OVERRIDE
2312 | osg::StateAttribute::PROTECTED));
2313 osg::PolygonOffset* polygonOffset = new osg::PolygonOffset;
2314 polygonOffset->setFactor(-1);
2315 polygonOffset->setUnits(-1);
2316 stateSet->setAttribute(polygonOffset, osg::StateAttribute::OVERRIDE);
2317 stateSet->setMode(GL_POLYGON_OFFSET_LINE,
2318 osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
2319 osg::PolygonMode* polygonMode = new osg::PolygonMode;
2320 polygonMode->setMode(osg::PolygonMode::FRONT_AND_BACK,
2321 osg::PolygonMode::LINE);
2322 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(0, 0, 0, 1));
2326 // XXX Alpha < 1.0 in the diffuse material value is a signal to the
2327 // default shader to take the alpha value from the material value
2328 // and not the glColor. In many cases the pick animation geometry is
2329 // transparent, so the outline would not be visible without this hack.
2330 material->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(0, 0, 0, .95));
2331 material->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(1, 1, 0, 1));
2332 material->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0, 0, 0, 0));
2333 stateSet->setAttribute(
2334 material, osg::StateAttribute::OVERRIDE | osg::StateAttribute::PROTECTED);
2335 // The default shader has a colorMode uniform that mimics the
2336 // behavior of Material color mode.
2337 osg::Uniform* cmUniform = 0;
2339 ScopedLock<Mutex> lock(colorModeUniformMutex);
2340 if (!colorModeUniform.valid()) {
2341 colorModeUniform = new osg::Uniform(osg::Uniform::INT, "colorMode");
2342 colorModeUniform->set(0); // MODE_OFF
2343 colorModeUniform->setDataVariance(osg::Object::STATIC);
2345 cmUniform = colorModeUniform.get();
2347 stateSet->addUniform(cmUniform,
2348 osg::StateAttribute::OVERRIDE | osg::StateAttribute::ON);
2349 // Only add normal geometry if configured
2350 if (getConfig()->getBoolValue("visible", true))
2351 parent.addChild(normalGroup.get());
2352 parent.addChild(highlightGroup);