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 #include "ConditionNode.hxx"
58 using OpenThreads::Mutex;
59 using OpenThreads::ReentrantMutex;
60 using OpenThreads::ScopedLock;
63 ////////////////////////////////////////////////////////////////////////
64 // Static utility functions.
65 ////////////////////////////////////////////////////////////////////////
68 * Set up the transform matrix for a spin or rotation.
71 set_rotation (osg::Matrix &matrix, double position_deg,
72 const SGVec3d ¢er, const SGVec3d &axis)
74 double temp_angle = -SGMiscd::deg2rad(position_deg);
76 double s = sin(temp_angle);
77 double c = cos(temp_angle);
80 // axis was normalized at load time
81 // hint to the compiler to put these into FP registers
86 matrix(0, 0) = t * x * x + c ;
87 matrix(0, 1) = t * y * x - s * z ;
88 matrix(0, 2) = t * z * x + s * y ;
91 matrix(1, 0) = t * x * y + s * z ;
92 matrix(1, 1) = t * y * y + c ;
93 matrix(1, 2) = t * z * y - s * x ;
96 matrix(2, 0) = t * x * z - s * y ;
97 matrix(2, 1) = t * y * z + s * x ;
98 matrix(2, 2) = t * z * z + c ;
101 // hint to the compiler to put these into FP registers
106 matrix(3, 0) = x - x*matrix(0, 0) - y*matrix(1, 0) - z*matrix(2, 0);
107 matrix(3, 1) = y - x*matrix(0, 1) - y*matrix(1, 1) - z*matrix(2, 1);
108 matrix(3, 2) = z - x*matrix(0, 2) - y*matrix(1, 2) - z*matrix(2, 2);
113 * Set up the transform matrix for a translation.
116 set_translation (osg::Matrix &matrix, double position_m, const SGVec3d &axis)
118 SGVec3d xyz = axis * position_m;
119 matrix.makeIdentity();
120 matrix(3, 0) = xyz[0];
121 matrix(3, 1) = xyz[1];
122 matrix(3, 2) = xyz[2];
126 * Read an interpolation table from properties.
128 static SGInterpTable *
129 read_interpolation_table(const SGPropertyNode* props)
131 const SGPropertyNode* table_node = props->getNode("interpolation");
134 return new SGInterpTable(table_node);
138 unit_string(const char* value, const char* unit)
140 return std::string(value) + unit;
143 class SGPersonalityScaleOffsetExpression : public SGUnaryExpression<double> {
145 SGPersonalityScaleOffsetExpression(SGExpression<double>* expr,
146 SGPropertyNode const* config,
147 const std::string& scalename,
148 const std::string& offsetname,
150 double defOffset = 0) :
151 SGUnaryExpression<double>(expr),
152 _scale(config, scalename.c_str(), defScale),
153 _offset(config, offsetname.c_str(), defOffset)
155 void setScale(double scale)
157 void setOffset(double offset)
158 { _offset = offset; }
160 virtual void eval(double& value, const simgear::expression::Binding* b) const
164 value = _offset + _scale*getOperand()->getValue(b);
167 virtual bool isConst() const { return false; }
170 mutable SGPersonalityParameter<double> _scale;
171 mutable SGPersonalityParameter<double> _offset;
175 static SGExpressiond*
176 read_factor_offset(const SGPropertyNode* configNode, SGExpressiond* expr,
177 const std::string& factor, const std::string& offset)
179 double factorValue = configNode->getDoubleValue(factor, 1);
180 if (factorValue != 1)
181 expr = new SGScaleExpression<double>(expr, factorValue);
182 double offsetValue = configNode->getDoubleValue(offset, 0);
183 if (offsetValue != 0)
184 expr = new SGBiasExpression<double>(expr, offsetValue);
188 static SGExpressiond*
189 read_offset_factor(const SGPropertyNode* configNode, SGExpressiond* expr,
190 const std::string& factor, const std::string& offset)
192 double offsetValue = configNode->getDoubleValue(offset, 0);
193 if (offsetValue != 0)
194 expr = new SGBiasExpression<double>(expr, offsetValue);
195 double factorValue = configNode->getDoubleValue(factor, 1);
196 if (factorValue != 1)
197 expr = new SGScaleExpression<double>(expr, factorValue);
202 read_value(const SGPropertyNode* configNode, SGPropertyNode* modelRoot,
203 const char* unit, double defMin, double defMax)
205 SGExpression<double>* value = 0;
207 std::string inputPropertyName = configNode->getStringValue("property", "");
208 if (inputPropertyName.empty()) {
209 std::string spos = unit_string("starting-position", unit);
210 double initPos = configNode->getDoubleValue(spos, 0);
211 value = new SGConstExpression<double>(initPos);
213 SGPropertyNode* inputProperty;
214 inputProperty = modelRoot->getNode(inputPropertyName, true);
215 value = new SGPropertyExpression<double>(inputProperty);
218 SGInterpTable* interpTable = read_interpolation_table(configNode);
220 return new SGInterpTableExpression<double>(value, interpTable);
222 std::string offset = unit_string("offset", unit);
223 std::string min = unit_string("min", unit);
224 std::string max = unit_string("max", unit);
226 if (configNode->getBoolValue("use-personality", false)) {
227 value = new SGPersonalityScaleOffsetExpression(value, configNode,
230 value = read_factor_offset(configNode, value, "factor", offset);
233 double minClip = configNode->getDoubleValue(min, defMin);
234 double maxClip = configNode->getDoubleValue(max, defMax);
235 if (minClip > SGMiscd::min(SGLimitsd::min(), -SGLimitsd::max()) ||
236 maxClip < SGLimitsd::max())
237 value = new SGClipExpression<double>(value, minClip, maxClip);
245 ////////////////////////////////////////////////////////////////////////
246 // Animation installer
247 ////////////////////////////////////////////////////////////////////////
249 class SGAnimation::RemoveModeVisitor : public SGStateAttributeVisitor {
251 RemoveModeVisitor(osg::StateAttribute::GLMode mode) :
254 virtual void apply(osg::StateSet* stateSet)
258 stateSet->removeMode(_mode);
261 osg::StateAttribute::GLMode _mode;
264 class SGAnimation::RemoveAttributeVisitor : public SGStateAttributeVisitor {
266 RemoveAttributeVisitor(osg::StateAttribute::Type type) :
269 virtual void apply(osg::StateSet* stateSet)
273 while (stateSet->getAttribute(_type)) {
274 stateSet->removeAttribute(_type);
278 osg::StateAttribute::Type _type;
281 class SGAnimation::RemoveTextureModeVisitor : public SGStateAttributeVisitor {
283 RemoveTextureModeVisitor(unsigned unit, osg::StateAttribute::GLMode mode) :
287 virtual void apply(osg::StateSet* stateSet)
291 stateSet->removeTextureMode(_unit, _mode);
295 osg::StateAttribute::GLMode _mode;
298 class SGAnimation::RemoveTextureAttributeVisitor :
299 public SGStateAttributeVisitor {
301 RemoveTextureAttributeVisitor(unsigned unit,
302 osg::StateAttribute::Type type) :
306 virtual void apply(osg::StateSet* stateSet)
310 while (stateSet->getTextureAttribute(_unit, _type)) {
311 stateSet->removeTextureAttribute(_unit, _type);
316 osg::StateAttribute::Type _type;
319 class SGAnimation::BinToInheritVisitor : public SGStateAttributeVisitor {
321 virtual void apply(osg::StateSet* stateSet)
325 stateSet->setRenderBinToInherit();
329 class SGAnimation::DrawableCloneVisitor : public osg::NodeVisitor {
331 DrawableCloneVisitor() :
332 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
334 void apply(osg::Geode& geode)
336 for (unsigned i = 0 ; i < geode.getNumDrawables(); ++i) {
337 osg::CopyOp copyOp(osg::CopyOp::DEEP_COPY_ALL &
338 ~osg::CopyOp::DEEP_COPY_TEXTURES);
339 geode.setDrawable(i, copyOp(geode.getDrawable(i)));
346 // Set all drawables to not use display lists. OSG will use
347 // glDrawArrays instead.
348 struct DoDrawArraysVisitor : public osg::NodeVisitor {
349 DoDrawArraysVisitor() :
350 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
352 void apply(osg::Geode& geode)
357 for (int i = 0; i < (int)geode.getNumDrawables(); ++i)
358 geode.getDrawable(i)->setUseDisplayList(false);
363 SGAnimation::SGAnimation(const SGPropertyNode* configNode,
364 SGPropertyNode* modelRoot) :
365 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
367 _configNode(configNode),
368 _modelRoot(modelRoot)
370 _name = configNode->getStringValue("name", "");
371 _enableHOT = configNode->getBoolValue("enable-hot", true);
372 _disableShadow = configNode->getBoolValue("disable-shadow", false);
373 std::vector<SGPropertyNode_ptr> objectNames =
374 configNode->getChildren("object-name");
375 for (unsigned i = 0; i < objectNames.size(); ++i)
376 _objectNames.push_back(objectNames[i]->getStringValue());
379 SGAnimation::~SGAnimation()
384 SG_LOG(SG_IO, SG_ALERT, "Could not find at least one of the following"
385 " objects for animation:\n");
386 std::list<std::string>::const_iterator i;
387 for (i = _objectNames.begin(); i != _objectNames.end(); ++i)
388 SG_LOG(SG_IO, SG_ALERT, *i << "\n");
392 SGAnimation::animate(osg::Node* node, const SGPropertyNode* configNode,
393 SGPropertyNode* modelRoot,
394 const osgDB::ReaderWriter::Options* options)
396 std::string type = configNode->getStringValue("type", "none");
397 if (type == "alpha-test") {
398 SGAlphaTestAnimation animInst(configNode, modelRoot);
399 animInst.apply(node);
400 } else if (type == "billboard") {
401 SGBillboardAnimation animInst(configNode, modelRoot);
402 animInst.apply(node);
403 } else if (type == "blend") {
404 SGBlendAnimation animInst(configNode, modelRoot);
405 animInst.apply(node);
406 } else if (type == "dist-scale") {
407 SGDistScaleAnimation animInst(configNode, modelRoot);
408 animInst.apply(node);
409 } else if (type == "flash") {
410 SGFlashAnimation animInst(configNode, modelRoot);
411 animInst.apply(node);
412 } else if (type == "interaction") {
413 SGInteractionAnimation animInst(configNode, modelRoot);
414 animInst.apply(node);
415 } else if (type == "material") {
416 SGMaterialAnimation animInst(configNode, modelRoot, options);
417 animInst.apply(node);
418 } else if (type == "noshadow") {
419 SGShadowAnimation animInst(configNode, modelRoot);
420 animInst.apply(node);
421 } else if (type == "pick") {
422 SGPickAnimation animInst(configNode, modelRoot);
423 animInst.apply(node);
424 } else if (type == "range") {
425 SGRangeAnimation animInst(configNode, modelRoot);
426 animInst.apply(node);
427 } else if (type == "rotate" || type == "spin") {
428 SGRotateAnimation animInst(configNode, modelRoot);
429 animInst.apply(node);
430 } else if (type == "scale") {
431 SGScaleAnimation animInst(configNode, modelRoot);
432 animInst.apply(node);
433 } else if (type == "select") {
434 SGSelectAnimation animInst(configNode, modelRoot);
435 animInst.apply(node);
436 } else if (type == "shader") {
437 SGShaderAnimation animInst(configNode, modelRoot, options);
438 animInst.apply(node);
439 } else if (type == "textranslate" || type == "texrotate" ||
440 type == "texmultiple") {
441 SGTexTransformAnimation animInst(configNode, modelRoot);
442 animInst.apply(node);
443 } else if (type == "timed") {
444 SGTimedAnimation animInst(configNode, modelRoot);
445 animInst.apply(node);
446 } else if (type == "translate") {
447 SGTranslateAnimation animInst(configNode, modelRoot);
448 animInst.apply(node);
449 } else if (type == "null" || type == "none" || type.empty()) {
450 SGGroupAnimation animInst(configNode, modelRoot);
451 animInst.apply(node);
460 SGAnimation::apply(osg::Node* node)
462 // duh what a special case ...
463 if (_objectNames.empty()) {
464 osg::Group* group = node->asGroup();
466 osg::ref_ptr<osg::Group> animationGroup;
467 installInGroup(std::string(), *group, animationGroup);
474 SGAnimation::install(osg::Node& node)
478 node.setNodeMask( SG_NODEMASK_TERRAIN_BIT | node.getNodeMask());
480 node.setNodeMask(~SG_NODEMASK_TERRAIN_BIT & node.getNodeMask());
482 node.setNodeMask( SG_NODEMASK_CASTSHADOW_BIT | node.getNodeMask());
484 node.setNodeMask(~SG_NODEMASK_CASTSHADOW_BIT & node.getNodeMask());
488 SGAnimation::createAnimationGroup(osg::Group& parent)
490 // default implementation, we do not need a new group
491 // for every animation type. Usually animations that just change
492 // the StateSet of some parts of the model
497 SGAnimation::apply(osg::Group& group)
499 // the trick is to first traverse the children and then
500 // possibly splice in a new group node if required.
501 // Else we end up in a recursive loop where we infinitly insert new
505 // Note that this algorithm preserves the order of the child objects
506 // like they appear in the object-name tags.
507 // The timed animations require this
508 osg::ref_ptr<osg::Group> animationGroup;
509 std::list<std::string>::const_iterator nameIt;
510 for (nameIt = _objectNames.begin(); nameIt != _objectNames.end(); ++nameIt)
511 installInGroup(*nameIt, group, animationGroup);
515 SGAnimation::installInGroup(const std::string& name, osg::Group& group,
516 osg::ref_ptr<osg::Group>& animationGroup)
518 int i = group.getNumChildren() - 1;
519 for (; 0 <= i; --i) {
520 osg::Node* child = group.getChild(i);
522 // Check if this one is already processed
523 if (std::find(_installedAnimations.begin(),
524 _installedAnimations.end(), child)
525 != _installedAnimations.end())
528 if (name.empty() || child->getName() == name) {
529 // fire the installation of the animation
532 // create a group node on demand
533 if (!animationGroup.valid()) {
534 animationGroup = createAnimationGroup(group);
535 // Animation type that does not require a new group,
536 // in this case we can stop and look for the next object
537 if (animationGroup.valid() && !_name.empty())
538 animationGroup->setName(_name);
540 if (animationGroup.valid()) {
541 animationGroup->addChild(child);
542 group.removeChild(i);
545 // store that we already have processed this child node
546 // We can hit this one twice if an animation references some
547 // part of a subtree twice
548 _installedAnimations.push_back(child);
554 SGAnimation::removeMode(osg::Node& node, osg::StateAttribute::GLMode mode)
556 RemoveModeVisitor visitor(mode);
557 node.accept(visitor);
561 SGAnimation::removeAttribute(osg::Node& node, osg::StateAttribute::Type type)
563 RemoveAttributeVisitor visitor(type);
564 node.accept(visitor);
568 SGAnimation::removeTextureMode(osg::Node& node, unsigned unit,
569 osg::StateAttribute::GLMode mode)
571 RemoveTextureModeVisitor visitor(unit, mode);
572 node.accept(visitor);
576 SGAnimation::removeTextureAttribute(osg::Node& node, unsigned unit,
577 osg::StateAttribute::Type type)
579 RemoveTextureAttributeVisitor visitor(unit, type);
580 node.accept(visitor);
584 SGAnimation::setRenderBinToInherit(osg::Node& node)
586 BinToInheritVisitor visitor;
587 node.accept(visitor);
591 SGAnimation::cloneDrawables(osg::Node& node)
593 DrawableCloneVisitor visitor;
594 node.accept(visitor);
598 SGAnimation::getCondition() const
600 const SGPropertyNode* conditionNode = _configNode->getChild("condition");
603 return sgReadCondition(_modelRoot, conditionNode);
608 ////////////////////////////////////////////////////////////////////////
609 // Implementation of null animation
610 ////////////////////////////////////////////////////////////////////////
612 // Ok, that is to build a subgraph from different other
613 // graph nodes. I guess that this stems from the time where modellers
614 // could not build hierarchical trees ...
615 SGGroupAnimation::SGGroupAnimation(const SGPropertyNode* configNode,
616 SGPropertyNode* modelRoot):
617 SGAnimation(configNode, modelRoot)
622 SGGroupAnimation::createAnimationGroup(osg::Group& parent)
624 osg::Group* group = new osg::Group;
625 parent.addChild(group);
630 ////////////////////////////////////////////////////////////////////////
631 // Implementation of translate animation
632 ////////////////////////////////////////////////////////////////////////
634 class SGTranslateAnimation::UpdateCallback : public osg::NodeCallback {
636 UpdateCallback(SGCondition const* condition,
637 SGExpressiond const* animationValue) :
638 _condition(condition),
639 _animationValue(animationValue)
641 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
643 if (!_condition || _condition->test()) {
644 SGTranslateTransform* transform;
645 transform = static_cast<SGTranslateTransform*>(node);
646 transform->setValue(_animationValue->getValue());
651 SGSharedPtr<SGCondition const> _condition;
652 SGSharedPtr<SGExpressiond const> _animationValue;
655 SGTranslateAnimation::SGTranslateAnimation(const SGPropertyNode* configNode,
656 SGPropertyNode* modelRoot) :
657 SGAnimation(configNode, modelRoot)
659 _condition = getCondition();
660 SGSharedPtr<SGExpressiond> value;
661 value = read_value(configNode, modelRoot, "-m",
662 -SGLimitsd::max(), SGLimitsd::max());
663 _animationValue = value->simplify();
665 _initialValue = _animationValue->getValue();
669 if (configNode->hasValue("axis/x1-m")) {
671 v1[0] = configNode->getDoubleValue("axis/x1-m", 0);
672 v1[1] = configNode->getDoubleValue("axis/y1-m", 0);
673 v1[2] = configNode->getDoubleValue("axis/z1-m", 0);
674 v2[0] = configNode->getDoubleValue("axis/x2-m", 0);
675 v2[1] = configNode->getDoubleValue("axis/y2-m", 0);
676 v2[2] = configNode->getDoubleValue("axis/z2-m", 0);
679 _axis[0] = configNode->getDoubleValue("axis/x", 0);
680 _axis[1] = configNode->getDoubleValue("axis/y", 0);
681 _axis[2] = configNode->getDoubleValue("axis/z", 0);
683 if (8*SGLimitsd::min() < norm(_axis))
684 _axis = normalize(_axis);
688 SGTranslateAnimation::createAnimationGroup(osg::Group& parent)
690 SGTranslateTransform* transform = new SGTranslateTransform;
691 transform->setName("translate animation");
692 if (_animationValue && !_animationValue->isConst()) {
693 UpdateCallback* uc = new UpdateCallback(_condition, _animationValue);
694 transform->setUpdateCallback(uc);
696 transform->setAxis(_axis);
697 transform->setValue(_initialValue);
698 parent.addChild(transform);
703 ////////////////////////////////////////////////////////////////////////
704 // Implementation of rotate/spin animation
705 ////////////////////////////////////////////////////////////////////////
707 class SGRotateAnimation::UpdateCallback : public osg::NodeCallback {
709 UpdateCallback(SGCondition const* condition,
710 SGExpressiond const* animationValue) :
711 _condition(condition),
712 _animationValue(animationValue)
714 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
716 if (!_condition || _condition->test()) {
717 SGRotateTransform* transform;
718 transform = static_cast<SGRotateTransform*>(node);
719 transform->setAngleDeg(_animationValue->getValue());
724 SGSharedPtr<SGCondition const> _condition;
725 SGSharedPtr<SGExpressiond const> _animationValue;
728 class SGRotateAnimation::SpinUpdateCallback : public osg::NodeCallback {
730 SpinUpdateCallback(SGCondition const* condition,
731 SGExpressiond const* animationValue) :
732 _condition(condition),
733 _animationValue(animationValue),
736 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
738 if (!_condition || _condition->test()) {
739 SGRotateTransform* transform;
740 transform = static_cast<SGRotateTransform*>(node);
742 double t = nv->getFrameStamp()->getReferenceTime();
747 double velocity_rpms = _animationValue->getValue()/60;
748 double angle = transform->getAngleDeg();
749 angle += dt*velocity_rpms*360;
750 angle -= 360*floor(angle/360);
751 transform->setAngleDeg(angle);
756 SGSharedPtr<SGCondition const> _condition;
757 SGSharedPtr<SGExpressiond const> _animationValue;
761 SGRotateAnimation::SGRotateAnimation(const SGPropertyNode* configNode,
762 SGPropertyNode* modelRoot) :
763 SGAnimation(configNode, modelRoot)
765 std::string type = configNode->getStringValue("type", "");
766 _isSpin = (type == "spin");
768 _condition = getCondition();
769 SGSharedPtr<SGExpressiond> value;
770 value = read_value(configNode, modelRoot, "-deg",
771 -SGLimitsd::max(), SGLimitsd::max());
772 _animationValue = value->simplify();
774 _initialValue = _animationValue->getValue();
778 _center = SGVec3d::zeros();
779 if (configNode->hasValue("axis/x1-m")) {
781 v1[0] = configNode->getDoubleValue("axis/x1-m", 0);
782 v1[1] = configNode->getDoubleValue("axis/y1-m", 0);
783 v1[2] = configNode->getDoubleValue("axis/z1-m", 0);
784 v2[0] = configNode->getDoubleValue("axis/x2-m", 0);
785 v2[1] = configNode->getDoubleValue("axis/y2-m", 0);
786 v2[2] = configNode->getDoubleValue("axis/z2-m", 0);
787 _center = 0.5*(v1+v2);
790 _axis[0] = configNode->getDoubleValue("axis/x", 0);
791 _axis[1] = configNode->getDoubleValue("axis/y", 0);
792 _axis[2] = configNode->getDoubleValue("axis/z", 0);
794 if (8*SGLimitsd::min() < norm(_axis))
795 _axis = normalize(_axis);
797 _center[0] = configNode->getDoubleValue("center/x-m", _center[0]);
798 _center[1] = configNode->getDoubleValue("center/y-m", _center[1]);
799 _center[2] = configNode->getDoubleValue("center/z-m", _center[2]);
803 SGRotateAnimation::createAnimationGroup(osg::Group& parent)
805 SGRotateTransform* transform = new SGRotateTransform;
806 transform->setName("rotate animation");
808 SpinUpdateCallback* uc;
809 uc = new SpinUpdateCallback(_condition, _animationValue);
810 transform->setUpdateCallback(uc);
811 } else if (_animationValue || !_animationValue->isConst()) {
812 UpdateCallback* uc = new UpdateCallback(_condition, _animationValue);
813 transform->setUpdateCallback(uc);
815 transform->setCenter(_center);
816 transform->setAxis(_axis);
817 transform->setAngleDeg(_initialValue);
818 parent.addChild(transform);
823 ////////////////////////////////////////////////////////////////////////
824 // Implementation of scale animation
825 ////////////////////////////////////////////////////////////////////////
827 class SGScaleAnimation::UpdateCallback : public osg::NodeCallback {
829 UpdateCallback(const SGCondition* condition,
830 SGSharedPtr<const SGExpressiond> animationValue[3]) :
831 _condition(condition)
833 _animationValue[0] = animationValue[0];
834 _animationValue[1] = animationValue[1];
835 _animationValue[2] = animationValue[2];
837 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
839 if (!_condition || _condition->test()) {
840 SGScaleTransform* transform;
841 transform = static_cast<SGScaleTransform*>(node);
842 SGVec3d scale(_animationValue[0]->getValue(),
843 _animationValue[1]->getValue(),
844 _animationValue[2]->getValue());
845 transform->setScaleFactor(scale);
850 SGSharedPtr<SGCondition const> _condition;
851 SGSharedPtr<SGExpressiond const> _animationValue[3];
854 SGScaleAnimation::SGScaleAnimation(const SGPropertyNode* configNode,
855 SGPropertyNode* modelRoot) :
856 SGAnimation(configNode, modelRoot)
858 _condition = getCondition();
860 // default offset/factor for all directions
861 double offset = configNode->getDoubleValue("offset", 0);
862 double factor = configNode->getDoubleValue("factor", 1);
864 SGSharedPtr<SGExpressiond> inPropExpr;
866 std::string inputPropertyName;
867 inputPropertyName = configNode->getStringValue("property", "");
868 if (inputPropertyName.empty()) {
869 inPropExpr = new SGConstExpression<double>(0);
871 SGPropertyNode* inputProperty;
872 inputProperty = modelRoot->getNode(inputPropertyName, true);
873 inPropExpr = new SGPropertyExpression<double>(inputProperty);
876 SGInterpTable* interpTable = read_interpolation_table(configNode);
878 SGSharedPtr<SGExpressiond> value;
879 value = new SGInterpTableExpression<double>(inPropExpr, interpTable);
880 _animationValue[0] = value->simplify();
881 _animationValue[1] = value->simplify();
882 _animationValue[2] = value->simplify();
883 } else if (configNode->getBoolValue("use-personality", false)) {
884 SGSharedPtr<SGExpressiond> value;
885 value = new SGPersonalityScaleOffsetExpression(inPropExpr, configNode,
886 "x-factor", "x-offset",
888 double minClip = configNode->getDoubleValue("x-min", 0);
889 double maxClip = configNode->getDoubleValue("x-max", SGLimitsd::max());
890 value = new SGClipExpression<double>(value, minClip, maxClip);
891 _animationValue[0] = value->simplify();
893 value = new SGPersonalityScaleOffsetExpression(inPropExpr, configNode,
894 "y-factor", "y-offset",
896 minClip = configNode->getDoubleValue("y-min", 0);
897 maxClip = configNode->getDoubleValue("y-max", SGLimitsd::max());
898 value = new SGClipExpression<double>(value, minClip, maxClip);
899 _animationValue[1] = value->simplify();
901 value = new SGPersonalityScaleOffsetExpression(inPropExpr, configNode,
902 "z-factor", "z-offset",
904 minClip = configNode->getDoubleValue("z-min", 0);
905 maxClip = configNode->getDoubleValue("z-max", SGLimitsd::max());
906 value = new SGClipExpression<double>(value, minClip, maxClip);
907 _animationValue[2] = value->simplify();
909 SGSharedPtr<SGExpressiond> value;
910 value = read_factor_offset(configNode, inPropExpr, "x-factor", "x-offset");
911 double minClip = configNode->getDoubleValue("x-min", 0);
912 double maxClip = configNode->getDoubleValue("x-max", SGLimitsd::max());
913 value = new SGClipExpression<double>(value, minClip, maxClip);
914 _animationValue[0] = value->simplify();
916 value = read_factor_offset(configNode, inPropExpr, "y-factor", "y-offset");
917 minClip = configNode->getDoubleValue("y-min", 0);
918 maxClip = configNode->getDoubleValue("y-max", SGLimitsd::max());
919 value = new SGClipExpression<double>(value, minClip, maxClip);
920 _animationValue[1] = value->simplify();
922 value = read_factor_offset(configNode, inPropExpr, "z-factor", "z-offset");
923 minClip = configNode->getDoubleValue("z-min", 0);
924 maxClip = configNode->getDoubleValue("z-max", SGLimitsd::max());
925 value = new SGClipExpression<double>(value, minClip, maxClip);
926 _animationValue[2] = value->simplify();
928 _initialValue[0] = configNode->getDoubleValue("x-starting-scale", 1);
929 _initialValue[0] *= configNode->getDoubleValue("x-factor", factor);
930 _initialValue[0] += configNode->getDoubleValue("x-offset", offset);
931 _initialValue[1] = configNode->getDoubleValue("y-starting-scale", 1);
932 _initialValue[1] *= configNode->getDoubleValue("y-factor", factor);
933 _initialValue[1] += configNode->getDoubleValue("y-offset", offset);
934 _initialValue[2] = configNode->getDoubleValue("z-starting-scale", 1);
935 _initialValue[2] *= configNode->getDoubleValue("z-factor", factor);
936 _initialValue[2] += configNode->getDoubleValue("z-offset", offset);
937 _center[0] = configNode->getDoubleValue("center/x-m", 0);
938 _center[1] = configNode->getDoubleValue("center/y-m", 0);
939 _center[2] = configNode->getDoubleValue("center/z-m", 0);
943 SGScaleAnimation::createAnimationGroup(osg::Group& parent)
945 SGScaleTransform* transform = new SGScaleTransform;
946 transform->setName("scale animation");
947 transform->setCenter(_center);
948 transform->setScaleFactor(_initialValue);
949 UpdateCallback* uc = new UpdateCallback(_condition, _animationValue);
950 transform->setUpdateCallback(uc);
951 parent.addChild(transform);
956 // Don't create a new state state everytime we need GL_NORMALIZE!
960 Mutex normalizeMutex;
962 osg::StateSet* getNormalizeStateSet()
964 static osg::ref_ptr<osg::StateSet> normalizeStateSet;
965 ScopedLock<Mutex> lock(normalizeMutex);
966 if (!normalizeStateSet.valid()) {
967 normalizeStateSet = new osg::StateSet;
968 normalizeStateSet->setMode(GL_NORMALIZE, osg::StateAttribute::ON);
969 normalizeStateSet->setDataVariance(osg::Object::STATIC);
971 return normalizeStateSet.get();
975 ////////////////////////////////////////////////////////////////////////
976 // Implementation of dist scale animation
977 ////////////////////////////////////////////////////////////////////////
979 class SGDistScaleAnimation::Transform : public osg::Transform {
981 Transform() : _min_v(0.0), _max_v(0.0), _factor(0.0), _offset(0.0) {}
982 Transform(const Transform& rhs,
983 const osg::CopyOp& copyOp = osg::CopyOp::SHALLOW_COPY)
984 : osg::Transform(rhs, copyOp), _table(rhs._table), _center(rhs._center),
985 _min_v(rhs._min_v), _max_v(rhs._max_v), _factor(rhs._factor),
989 META_Node(simgear, SGDistScaleAnimation::Transform);
990 Transform(const SGPropertyNode* configNode)
992 setName(configNode->getStringValue("name", "dist scale animation"));
993 setReferenceFrame(RELATIVE_RF);
994 setStateSet(getNormalizeStateSet());
995 _factor = configNode->getFloatValue("factor", 1);
996 _offset = configNode->getFloatValue("offset", 0);
997 _min_v = configNode->getFloatValue("min", SGLimitsf::epsilon());
998 _max_v = configNode->getFloatValue("max", SGLimitsf::max());
999 _table = read_interpolation_table(configNode);
1000 _center[0] = configNode->getFloatValue("center/x-m", 0);
1001 _center[1] = configNode->getFloatValue("center/y-m", 0);
1002 _center[2] = configNode->getFloatValue("center/z-m", 0);
1004 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
1005 osg::NodeVisitor* nv) const
1007 osg::Matrix transform;
1008 double scale_factor = computeScaleFactor(nv);
1009 transform(0,0) = scale_factor;
1010 transform(1,1) = scale_factor;
1011 transform(2,2) = scale_factor;
1012 transform(3,0) = _center[0]*(1 - scale_factor);
1013 transform(3,1) = _center[1]*(1 - scale_factor);
1014 transform(3,2) = _center[2]*(1 - scale_factor);
1015 matrix.preMult(transform);
1019 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
1020 osg::NodeVisitor* nv) const
1022 double scale_factor = computeScaleFactor(nv);
1023 if (fabs(scale_factor) <= SGLimits<double>::min())
1025 osg::Matrix transform;
1026 double rScaleFactor = 1/scale_factor;
1027 transform(0,0) = rScaleFactor;
1028 transform(1,1) = rScaleFactor;
1029 transform(2,2) = rScaleFactor;
1030 transform(3,0) = _center[0]*(1 - rScaleFactor);
1031 transform(3,1) = _center[1]*(1 - rScaleFactor);
1032 transform(3,2) = _center[2]*(1 - rScaleFactor);
1033 matrix.postMult(transform);
1037 static bool writeLocalData(const osg::Object& obj, osgDB::Output& fw)
1039 const Transform& trans = static_cast<const Transform&>(obj);
1040 fw.indent() << "center " << trans._center << "\n";
1041 fw.indent() << "min_v " << trans._min_v << "\n";
1042 fw.indent() << "max_v " << trans._max_v << "\n";
1043 fw.indent() << "factor " << trans._factor << "\n";
1044 fw.indent() << "offset " << trans._offset << "\n";
1048 double computeScaleFactor(osg::NodeVisitor* nv) const
1053 double scale_factor = (toOsg(_center) - nv->getEyePoint()).length();
1055 scale_factor = _factor * scale_factor + _offset;
1057 scale_factor = _table->interpolate( scale_factor );
1059 if (scale_factor < _min_v)
1060 scale_factor = _min_v;
1061 if (scale_factor > _max_v)
1062 scale_factor = _max_v;
1064 return scale_factor;
1067 SGSharedPtr<SGInterpTable> _table;
1076 SGDistScaleAnimation::SGDistScaleAnimation(const SGPropertyNode* configNode,
1077 SGPropertyNode* modelRoot) :
1078 SGAnimation(configNode, modelRoot)
1083 SGDistScaleAnimation::createAnimationGroup(osg::Group& parent)
1085 Transform* transform = new Transform(getConfig());
1086 parent.addChild(transform);
1092 osgDB::RegisterDotOsgWrapperProxy distScaleAnimationTransformProxy
1094 new SGDistScaleAnimation::Transform,
1095 "SGDistScaleAnimation::Transform",
1096 "Object Node Transform SGDistScaleAnimation::Transform Group",
1098 &SGDistScaleAnimation::Transform::writeLocalData
1102 ////////////////////////////////////////////////////////////////////////
1103 // Implementation of flash animation
1104 ////////////////////////////////////////////////////////////////////////
1106 class SGFlashAnimation::Transform : public osg::Transform {
1108 Transform() : _power(0.0), _factor(0.0), _offset(0.0), _min_v(0.0),
1109 _max_v(0.0), _two_sides(false)
1112 Transform(const Transform& rhs,
1113 const osg::CopyOp& copyOp = osg::CopyOp::SHALLOW_COPY)
1114 : osg::Transform(rhs, copyOp), _center(rhs._center), _axis(rhs._axis),
1115 _power(rhs._power), _factor(rhs._factor), _offset(rhs._offset),
1116 _min_v(rhs._min_v), _max_v(rhs._max_v), _two_sides(rhs._two_sides)
1119 META_Node(simgear, SGFlashAnimation::Transform);
1121 Transform(const SGPropertyNode* configNode)
1123 setReferenceFrame(RELATIVE_RF);
1124 setName(configNode->getStringValue("name", "flash animation"));
1125 setStateSet(getNormalizeStateSet());
1127 _axis[0] = configNode->getFloatValue("axis/x", 0);
1128 _axis[1] = configNode->getFloatValue("axis/y", 0);
1129 _axis[2] = configNode->getFloatValue("axis/z", 1);
1132 _center[0] = configNode->getFloatValue("center/x-m", 0);
1133 _center[1] = configNode->getFloatValue("center/y-m", 0);
1134 _center[2] = configNode->getFloatValue("center/z-m", 0);
1136 _offset = configNode->getFloatValue("offset", 0);
1137 _factor = configNode->getFloatValue("factor", 1);
1138 _power = configNode->getFloatValue("power", 1);
1139 _two_sides = configNode->getBoolValue("two-sides", false);
1141 _min_v = configNode->getFloatValue("min", SGLimitsf::epsilon());
1142 _max_v = configNode->getFloatValue("max", 1);
1144 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
1145 osg::NodeVisitor* nv) const
1147 osg::Matrix transform;
1148 double scale_factor = computeScaleFactor(nv);
1149 transform(0,0) = scale_factor;
1150 transform(1,1) = scale_factor;
1151 transform(2,2) = scale_factor;
1152 transform(3,0) = _center[0]*(1 - scale_factor);
1153 transform(3,1) = _center[1]*(1 - scale_factor);
1154 transform(3,2) = _center[2]*(1 - scale_factor);
1155 matrix.preMult(transform);
1159 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
1160 osg::NodeVisitor* nv) const
1162 double scale_factor = computeScaleFactor(nv);
1163 if (fabs(scale_factor) <= SGLimits<double>::min())
1165 osg::Matrix transform;
1166 double rScaleFactor = 1/scale_factor;
1167 transform(0,0) = rScaleFactor;
1168 transform(1,1) = rScaleFactor;
1169 transform(2,2) = rScaleFactor;
1170 transform(3,0) = _center[0]*(1 - rScaleFactor);
1171 transform(3,1) = _center[1]*(1 - rScaleFactor);
1172 transform(3,2) = _center[2]*(1 - rScaleFactor);
1173 matrix.postMult(transform);
1177 static bool writeLocalData(const osg::Object& obj, osgDB::Output& fw)
1179 const Transform& trans = static_cast<const Transform&>(obj);
1180 fw.indent() << "center " << trans._center[0] << " "
1181 << trans._center[1] << " " << trans._center[2] << " " << "\n";
1182 fw.indent() << "axis " << trans._axis[0] << " "
1183 << trans._axis[1] << " " << trans._axis[2] << " " << "\n";
1184 fw.indent() << "power " << trans._power << " \n";
1185 fw.indent() << "min_v " << trans._min_v << "\n";
1186 fw.indent() << "max_v " << trans._max_v << "\n";
1187 fw.indent() << "factor " << trans._factor << "\n";
1188 fw.indent() << "offset " << trans._offset << "\n";
1189 fw.indent() << "twosides " << (trans._two_sides ? "true" : "false") << "\n";
1193 double computeScaleFactor(osg::NodeVisitor* nv) const
1198 osg::Vec3 localEyeToCenter = nv->getEyePoint() - _center;
1199 localEyeToCenter.normalize();
1201 double cos_angle = localEyeToCenter*_axis;
1202 double scale_factor = 0;
1203 if ( _two_sides && cos_angle < 0 )
1204 scale_factor = _factor * pow( -cos_angle, _power ) + _offset;
1205 else if ( cos_angle > 0 )
1206 scale_factor = _factor * pow( cos_angle, _power ) + _offset;
1208 if ( scale_factor < _min_v )
1209 scale_factor = _min_v;
1210 if ( scale_factor > _max_v )
1211 scale_factor = _max_v;
1213 return scale_factor;
1216 virtual osg::BoundingSphere computeBound() const
1218 // avoid being culled away by small feature culling
1219 osg::BoundingSphere bs = osg::Group::computeBound();
1220 bs.radius() *= _max_v;
1227 double _power, _factor, _offset, _min_v, _max_v;
1232 SGFlashAnimation::SGFlashAnimation(const SGPropertyNode* configNode,
1233 SGPropertyNode* modelRoot) :
1234 SGAnimation(configNode, modelRoot)
1239 SGFlashAnimation::createAnimationGroup(osg::Group& parent)
1241 Transform* transform = new Transform(getConfig());
1242 parent.addChild(transform);
1248 osgDB::RegisterDotOsgWrapperProxy flashAnimationTransformProxy
1250 new SGFlashAnimation::Transform,
1251 "SGFlashAnimation::Transform",
1252 "Object Node Transform SGFlashAnimation::Transform Group",
1254 &SGFlashAnimation::Transform::writeLocalData
1258 ////////////////////////////////////////////////////////////////////////
1259 // Implementation of billboard animation
1260 ////////////////////////////////////////////////////////////////////////
1262 class SGBillboardAnimation::Transform : public osg::Transform {
1264 Transform() : _spherical(true) {}
1265 Transform(const Transform& rhs,
1266 const osg::CopyOp& copyOp = osg::CopyOp::SHALLOW_COPY)
1267 : osg::Transform(rhs, copyOp), _spherical(rhs._spherical) {}
1268 META_Node(simgear, SGBillboardAnimation::Transform);
1269 Transform(const SGPropertyNode* configNode) :
1270 _spherical(configNode->getBoolValue("spherical", true))
1272 setReferenceFrame(RELATIVE_RF);
1273 setName(configNode->getStringValue("name", "billboard animation"));
1275 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
1276 osg::NodeVisitor* nv) const
1278 // More or less taken from plibs ssgCutout
1280 matrix(0,0) = 1; matrix(0,1) = 0; matrix(0,2) = 0;
1281 matrix(1,0) = 0; matrix(1,1) = 0; matrix(1,2) = -1;
1282 matrix(2,0) = 0; matrix(2,1) = 1; matrix(2,2) = 0;
1284 osg::Vec3 zAxis(matrix(2, 0), matrix(2, 1), matrix(2, 2));
1285 osg::Vec3 xAxis = osg::Vec3(0, 0, -1)^zAxis;
1286 osg::Vec3 yAxis = zAxis^xAxis;
1292 matrix(0,0) = xAxis[0]; matrix(0,1) = xAxis[1]; matrix(0,2) = xAxis[2];
1293 matrix(1,0) = yAxis[0]; matrix(1,1) = yAxis[1]; matrix(1,2) = yAxis[2];
1294 matrix(2,0) = zAxis[0]; matrix(2,1) = zAxis[1]; matrix(2,2) = zAxis[2];
1299 virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
1300 osg::NodeVisitor* nv) const
1302 // Hmm, don't yet know how to get that back ...
1305 static bool writeLocalData(const osg::Object& obj, osgDB::Output& fw)
1307 const Transform& trans = static_cast<const Transform&>(obj);
1309 fw.indent() << (trans._spherical ? "true" : "false") << "\n";
1317 SGBillboardAnimation::SGBillboardAnimation(const SGPropertyNode* configNode,
1318 SGPropertyNode* modelRoot) :
1319 SGAnimation(configNode, modelRoot)
1324 SGBillboardAnimation::createAnimationGroup(osg::Group& parent)
1326 Transform* transform = new Transform(getConfig());
1327 parent.addChild(transform);
1333 osgDB::RegisterDotOsgWrapperProxy billboardAnimationTransformProxy
1335 new SGBillboardAnimation::Transform,
1336 "SGBillboardAnimation::Transform",
1337 "Object Node Transform SGBillboardAnimation::Transform Group",
1339 &SGBillboardAnimation::Transform::writeLocalData
1343 ////////////////////////////////////////////////////////////////////////
1344 // Implementation of a range animation
1345 ////////////////////////////////////////////////////////////////////////
1347 class SGRangeAnimation::UpdateCallback : public osg::NodeCallback {
1349 UpdateCallback(const SGCondition* condition,
1350 const SGExpressiond* minAnimationValue,
1351 const SGExpressiond* maxAnimationValue,
1352 double minValue, double maxValue) :
1353 _condition(condition),
1354 _minAnimationValue(minAnimationValue),
1355 _maxAnimationValue(maxAnimationValue),
1356 _minStaticValue(minValue),
1357 _maxStaticValue(maxValue)
1359 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1361 osg::LOD* lod = static_cast<osg::LOD*>(node);
1362 if (!_condition || _condition->test()) {
1364 if (_minAnimationValue)
1365 minRange = _minAnimationValue->getValue();
1367 minRange = _minStaticValue;
1369 if (_maxAnimationValue)
1370 maxRange = _maxAnimationValue->getValue();
1372 maxRange = _maxStaticValue;
1373 lod->setRange(0, minRange, maxRange);
1375 lod->setRange(0, 0, SGLimitsf::max());
1381 SGSharedPtr<const SGCondition> _condition;
1382 SGSharedPtr<const SGExpressiond> _minAnimationValue;
1383 SGSharedPtr<const SGExpressiond> _maxAnimationValue;
1384 double _minStaticValue;
1385 double _maxStaticValue;
1388 SGRangeAnimation::SGRangeAnimation(const SGPropertyNode* configNode,
1389 SGPropertyNode* modelRoot) :
1390 SGAnimation(configNode, modelRoot)
1392 _condition = getCondition();
1394 std::string inputPropertyName;
1395 inputPropertyName = configNode->getStringValue("min-property", "");
1396 if (!inputPropertyName.empty()) {
1397 SGPropertyNode* inputProperty;
1398 inputProperty = modelRoot->getNode(inputPropertyName, true);
1399 SGSharedPtr<SGExpressiond> value;
1400 value = new SGPropertyExpression<double>(inputProperty);
1402 value = read_factor_offset(configNode, value, "min-factor", "min-offset");
1403 _minAnimationValue = value->simplify();
1405 inputPropertyName = configNode->getStringValue("max-property", "");
1406 if (!inputPropertyName.empty()) {
1407 SGPropertyNode* inputProperty;
1408 inputProperty = modelRoot->getNode(inputPropertyName.c_str(), true);
1410 SGSharedPtr<SGExpressiond> value;
1411 value = new SGPropertyExpression<double>(inputProperty);
1413 value = read_factor_offset(configNode, value, "max-factor", "max-offset");
1414 _maxAnimationValue = value->simplify();
1417 _initialValue[0] = configNode->getDoubleValue("min-m", 0);
1418 _initialValue[0] *= configNode->getDoubleValue("min-factor", 1);
1419 _initialValue[1] = configNode->getDoubleValue("max-m", SGLimitsf::max());
1420 _initialValue[1] *= configNode->getDoubleValue("max-factor", 1);
1424 SGRangeAnimation::createAnimationGroup(osg::Group& parent)
1426 osg::Group* group = new osg::Group;
1427 group->setName("range animation group");
1429 osg::LOD* lod = new osg::LOD;
1430 lod->setName("range animation node");
1431 parent.addChild(lod);
1433 lod->addChild(group, _initialValue[0], _initialValue[1]);
1434 lod->setCenterMode(osg::LOD::USE_BOUNDING_SPHERE_CENTER);
1435 lod->setRangeMode(osg::LOD::DISTANCE_FROM_EYE_POINT);
1436 if (_minAnimationValue || _maxAnimationValue || _condition) {
1438 uc = new UpdateCallback(_condition, _minAnimationValue, _maxAnimationValue,
1439 _initialValue[0], _initialValue[1]);
1440 lod->setUpdateCallback(uc);
1446 ////////////////////////////////////////////////////////////////////////
1447 // Implementation of a select animation
1448 ////////////////////////////////////////////////////////////////////////
1450 SGSelectAnimation::SGSelectAnimation(const SGPropertyNode* configNode,
1451 SGPropertyNode* modelRoot) :
1452 SGAnimation(configNode, modelRoot)
1457 SGSelectAnimation::createAnimationGroup(osg::Group& parent)
1459 // if no condition given, this is a noop.
1460 SGSharedPtr<SGCondition const> condition = getCondition();
1461 // trick, gets deleted with all its 'animated' children
1462 // when the animation installer returns
1464 return new osg::Group;
1465 simgear::ConditionNode* cn = new simgear::ConditionNode;
1466 cn->setName("select animation node");
1467 cn->setCondition(condition.ptr());
1468 osg::Group* grp = new osg::Group;
1470 parent.addChild(cn);
1476 ////////////////////////////////////////////////////////////////////////
1477 // Implementation of alpha test animation
1478 ////////////////////////////////////////////////////////////////////////
1480 SGAlphaTestAnimation::SGAlphaTestAnimation(const SGPropertyNode* configNode,
1481 SGPropertyNode* modelRoot) :
1482 SGAnimation(configNode, modelRoot)
1488 // Keep one copy of the most common alpha test its state set.
1489 ReentrantMutex alphaTestMutex;
1490 osg::ref_ptr<osg::AlphaFunc> standardAlphaFunc;
1491 osg::ref_ptr<osg::StateSet> alphaFuncStateSet;
1493 osg::AlphaFunc* makeAlphaFunc(float clamp)
1495 ScopedLock<ReentrantMutex> lock(alphaTestMutex);
1496 if (osg::equivalent(clamp, 0.01f)) {
1497 if (standardAlphaFunc.valid())
1498 return standardAlphaFunc.get();
1501 osg::AlphaFunc* alphaFunc = new osg::AlphaFunc;
1502 alphaFunc->setFunction(osg::AlphaFunc::GREATER);
1503 alphaFunc->setReferenceValue(clamp);
1504 alphaFunc->setDataVariance(osg::Object::STATIC);
1505 if (osg::equivalent(clamp, 0.01f))
1506 standardAlphaFunc = alphaFunc;
1510 osg::StateSet* makeAlphaTestStateSet(float clamp)
1512 using namespace OpenThreads;
1513 ScopedLock<ReentrantMutex> lock(alphaTestMutex);
1514 if (osg::equivalent(clamp, 0.01f)) {
1515 if (alphaFuncStateSet.valid())
1516 return alphaFuncStateSet.get();
1518 osg::AlphaFunc* alphaFunc = makeAlphaFunc(clamp);
1519 osg::StateSet* stateSet = new osg::StateSet;
1520 stateSet->setAttributeAndModes(alphaFunc,
1521 (osg::StateAttribute::ON
1522 | osg::StateAttribute::OVERRIDE));
1523 stateSet->setDataVariance(osg::Object::STATIC);
1524 if (osg::equivalent(clamp, 0.01f))
1525 alphaFuncStateSet = stateSet;
1530 SGAlphaTestAnimation::install(osg::Node& node)
1532 SGAnimation::install(node);
1534 float alphaClamp = getConfig()->getFloatValue("alpha-factor", 0);
1535 osg::StateSet* stateSet = node.getStateSet();
1537 node.setStateSet(makeAlphaTestStateSet(alphaClamp));
1539 stateSet->setAttributeAndModes(makeAlphaFunc(alphaClamp),
1540 (osg::StateAttribute::ON
1541 | osg::StateAttribute::OVERRIDE));
1546 //////////////////////////////////////////////////////////////////////
1547 // Blend animation installer
1548 //////////////////////////////////////////////////////////////////////
1550 // XXX This needs to be replaced by something using TexEnvCombine to
1551 // change the blend factor. Changing the alpha values in the geometry
1553 class SGBlendAnimation::BlendVisitor : public osg::NodeVisitor {
1555 BlendVisitor(float blend) :
1556 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
1558 { setVisitorType(osg::NodeVisitor::NODE_VISITOR); }
1559 virtual void apply(osg::Node& node)
1561 updateStateSet(node.getStateSet());
1564 virtual void apply(osg::Geode& node)
1566 apply((osg::Node&)node);
1567 unsigned nDrawables = node.getNumDrawables();
1568 for (unsigned i = 0; i < nDrawables; ++i) {
1569 osg::Drawable* drawable = node.getDrawable(i);
1570 osg::Geometry* geometry = drawable->asGeometry();
1573 osg::Array* array = geometry->getColorArray();
1576 osg::Vec4Array* vec4Array = dynamic_cast<osg::Vec4Array*>(array);
1579 for (unsigned k = 0; k < vec4Array->size(); ++k) {
1580 (*vec4Array)[k][3] = _blend;
1583 updateStateSet(drawable->getStateSet());
1586 void updateStateSet(osg::StateSet* stateSet)
1590 osg::StateAttribute* stateAttribute;
1591 stateAttribute = stateSet->getAttribute(osg::StateAttribute::MATERIAL);
1592 if (!stateAttribute)
1594 osg::Material* material = dynamic_cast<osg::Material*>(stateAttribute);
1597 material->setAlpha(osg::Material::FRONT_AND_BACK, _blend);
1599 stateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
1600 stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
1602 stateSet->setRenderingHint(osg::StateSet::DEFAULT_BIN);
1609 class SGBlendAnimation::UpdateCallback : public osg::NodeCallback {
1611 UpdateCallback(const SGPropertyNode* configNode, const SGExpressiond* v) :
1615 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1617 double blend = _animationValue->getValue();
1618 if (blend != _prev_value) {
1619 _prev_value = blend;
1620 BlendVisitor visitor(1-blend);
1621 node->accept(visitor);
1627 SGSharedPtr<SGExpressiond const> _animationValue;
1631 SGBlendAnimation::SGBlendAnimation(const SGPropertyNode* configNode,
1632 SGPropertyNode* modelRoot)
1633 : SGAnimation(configNode, modelRoot),
1634 _animationValue(read_value(configNode, modelRoot, "", 0, 1))
1639 SGBlendAnimation::createAnimationGroup(osg::Group& parent)
1641 if (!_animationValue)
1644 osg::Group* group = new osg::Switch;
1645 group->setName("blend animation node");
1646 group->setUpdateCallback(new UpdateCallback(getConfig(), _animationValue));
1647 parent.addChild(group);
1652 SGBlendAnimation::install(osg::Node& node)
1654 SGAnimation::install(node);
1655 // make sure we do not change common geometries,
1656 // that also creates new display lists for these subgeometries.
1657 cloneDrawables(node);
1658 DoDrawArraysVisitor visitor;
1659 node.accept(visitor);
1663 //////////////////////////////////////////////////////////////////////
1664 // Timed animation installer
1665 //////////////////////////////////////////////////////////////////////
1669 class SGTimedAnimation::UpdateCallback : public osg::NodeCallback {
1671 UpdateCallback(const SGPropertyNode* configNode) :
1674 _duration_sec(configNode->getDoubleValue("duration-sec", 1)),
1675 _last_time_sec(SGLimitsd::max()),
1676 _use_personality(configNode->getBoolValue("use-personality", false))
1678 std::vector<SGSharedPtr<SGPropertyNode> > nodes;
1679 nodes = configNode->getChildren("branch-duration-sec");
1680 for (size_t i = 0; i < nodes.size(); ++i) {
1681 unsigned ind = nodes[ i ]->getIndex();
1682 while ( ind >= _durations.size() ) {
1683 _durations.push_back(DurationSpec(_duration_sec));
1685 SGPropertyNode_ptr rNode = nodes[i]->getChild("random");
1687 _durations[ind] = DurationSpec(nodes[ i ]->getDoubleValue());
1689 _durations[ind] = DurationSpec(rNode->getDoubleValue( "min", 0),
1690 rNode->getDoubleValue( "max", 1));
1694 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1696 assert(dynamic_cast<osg::Switch*>(node));
1697 osg::Switch* sw = static_cast<osg::Switch*>(node);
1699 unsigned nChildren = sw->getNumChildren();
1701 // blow up the durations vector to the required size
1702 while (_durations.size() < nChildren) {
1703 _durations.push_back(_duration_sec);
1705 // make sure the current index is an duration that really exists
1706 _current_index = _current_index % nChildren;
1708 // update the time and compute the current systems time value
1709 double t = nv->getFrameStamp()->getReferenceTime();
1710 if (_last_time_sec == SGLimitsd::max()) {
1713 double dt = t - _last_time_sec;
1714 if (_use_personality)
1715 dt *= 1 + 0.2*(0.5 - sg_random());
1720 double currentDuration = _durations[_current_index].get();
1721 while (currentDuration < _reminder) {
1722 _reminder -= currentDuration;
1723 _current_index = (_current_index + 1) % nChildren;
1724 currentDuration = _durations[_current_index].get();
1727 sw->setSingleChildOn(_current_index);
1733 struct DurationSpec {
1734 DurationSpec(double t) :
1735 minTime(SGMiscd::max(0.01, t)),
1736 maxTime(SGMiscd::max(0.01, t))
1738 DurationSpec(double t0, double t1) :
1739 minTime(SGMiscd::max(0.01, t0)),
1740 maxTime(SGMiscd::max(0.01, t1))
1743 { return minTime + sg_random()*(maxTime - minTime); }
1747 std::vector<DurationSpec> _durations;
1748 unsigned _current_index;
1750 double _duration_sec;
1751 double _last_time_sec;
1752 bool _use_personality;
1756 SGTimedAnimation::SGTimedAnimation(const SGPropertyNode* configNode,
1757 SGPropertyNode* modelRoot)
1758 : SGAnimation(configNode, modelRoot)
1763 SGTimedAnimation::createAnimationGroup(osg::Group& parent)
1765 osg::Switch* sw = new osg::Switch;
1766 sw->setName("timed animation node");
1767 sw->setUpdateCallback(new UpdateCallback(getConfig()));
1768 parent.addChild(sw);
1773 ////////////////////////////////////////////////////////////////////////
1774 // dynamically switch on/off shadows
1775 ////////////////////////////////////////////////////////////////////////
1777 class SGShadowAnimation::UpdateCallback : public osg::NodeCallback {
1779 UpdateCallback(const SGCondition* condition) :
1780 _condition(condition)
1782 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
1784 if (_condition->test())
1785 node->setNodeMask( SG_NODEMASK_CASTSHADOW_BIT | node->getNodeMask());
1787 node->setNodeMask(~SG_NODEMASK_CASTSHADOW_BIT & node->getNodeMask());
1792 SGSharedPtr<const SGCondition> _condition;
1795 SGShadowAnimation::SGShadowAnimation(const SGPropertyNode* configNode,
1796 SGPropertyNode* modelRoot) :
1797 SGAnimation(configNode, modelRoot)
1802 SGShadowAnimation::createAnimationGroup(osg::Group& parent)
1804 SGSharedPtr<SGCondition const> condition = getCondition();
1808 osg::Group* group = new osg::Group;
1809 group->setName("shadow animation");
1810 group->setUpdateCallback(new UpdateCallback(condition));
1811 parent.addChild(group);
1816 ////////////////////////////////////////////////////////////////////////
1817 // Implementation of SGTexTransformAnimation
1818 ////////////////////////////////////////////////////////////////////////
1820 class SGTexTransformAnimation::Transform : public SGReferenced {
1825 virtual ~Transform()
1827 void setValue(double value)
1829 virtual void transform(osg::Matrix&) = 0;
1834 class SGTexTransformAnimation::Translation :
1835 public SGTexTransformAnimation::Transform {
1837 Translation(const SGVec3d& axis) :
1840 virtual void transform(osg::Matrix& matrix)
1843 set_translation(tmp, _value, _axis);
1844 matrix.preMult(tmp);
1850 class SGTexTransformAnimation::Rotation :
1851 public SGTexTransformAnimation::Transform {
1853 Rotation(const SGVec3d& axis, const SGVec3d& center) :
1857 virtual void transform(osg::Matrix& matrix)
1860 set_rotation(tmp, _value, _center, _axis);
1861 matrix.preMult(tmp);
1868 class SGTexTransformAnimation::UpdateCallback :
1869 public osg::StateAttribute::Callback {
1871 UpdateCallback(const SGCondition* condition) :
1872 _condition(condition)
1874 virtual void operator () (osg::StateAttribute* sa, osg::NodeVisitor*)
1876 if (!_condition || _condition->test()) {
1877 TransformList::const_iterator i;
1878 for (i = _transforms.begin(); i != _transforms.end(); ++i)
1879 i->transform->setValue(i->value->getValue());
1881 assert(dynamic_cast<osg::TexMat*>(sa));
1882 osg::TexMat* texMat = static_cast<osg::TexMat*>(sa);
1883 texMat->getMatrix().makeIdentity();
1884 TransformList::const_iterator i;
1885 for (i = _transforms.begin(); i != _transforms.end(); ++i)
1886 i->transform->transform(texMat->getMatrix());
1888 void appendTransform(Transform* transform, SGExpressiond* value)
1890 Entry entry = { transform, value };
1891 transform->transform(_matrix);
1892 _transforms.push_back(entry);
1897 SGSharedPtr<Transform> transform;
1898 SGSharedPtr<const SGExpressiond> value;
1900 typedef std::vector<Entry> TransformList;
1901 TransformList _transforms;
1902 SGSharedPtr<const SGCondition> _condition;
1903 osg::Matrix _matrix;
1906 SGTexTransformAnimation::SGTexTransformAnimation(const SGPropertyNode* configNode,
1907 SGPropertyNode* modelRoot) :
1908 SGAnimation(configNode, modelRoot)
1913 SGTexTransformAnimation::createAnimationGroup(osg::Group& parent)
1915 osg::Group* group = new osg::Group;
1916 group->setName("texture transform group");
1917 osg::StateSet* stateSet = group->getOrCreateStateSet();
1918 stateSet->setDataVariance(osg::Object::DYNAMIC);
1919 osg::TexMat* texMat = new osg::TexMat;
1920 UpdateCallback* updateCallback = new UpdateCallback(getCondition());
1921 // interpret the configs ...
1922 std::string type = getType();
1924 if (type == "textranslate") {
1925 appendTexTranslate(getConfig(), updateCallback);
1926 } else if (type == "texrotate") {
1927 appendTexRotate(getConfig(), updateCallback);
1928 } else if (type == "texmultiple") {
1929 std::vector<SGSharedPtr<SGPropertyNode> > transformConfigs;
1930 transformConfigs = getConfig()->getChildren("transform");
1931 for (unsigned i = 0; i < transformConfigs.size(); ++i) {
1932 std::string subtype = transformConfigs[i]->getStringValue("subtype", "");
1933 if (subtype == "textranslate")
1934 appendTexTranslate(transformConfigs[i], updateCallback);
1935 else if (subtype == "texrotate")
1936 appendTexRotate(transformConfigs[i], updateCallback);
1938 SG_LOG(SG_INPUT, SG_ALERT,
1939 "Ignoring unknown texture transform subtype");
1942 SG_LOG(SG_INPUT, SG_ALERT, "Ignoring unknown texture transform type");
1945 texMat->setUpdateCallback(updateCallback);
1946 stateSet->setTextureAttribute(0, texMat);
1947 parent.addChild(group);
1952 SGTexTransformAnimation::appendTexTranslate(const SGPropertyNode* config,
1953 UpdateCallback* updateCallback)
1955 std::string propertyName = config->getStringValue("property", "");
1956 SGSharedPtr<SGExpressiond> value;
1957 if (propertyName.empty())
1958 value = new SGConstExpression<double>(0);
1960 SGPropertyNode* inputProperty = getModelRoot()->getNode(propertyName, true);
1961 value = new SGPropertyExpression<double>(inputProperty);
1964 SGInterpTable* table = read_interpolation_table(config);
1966 value = new SGInterpTableExpression<double>(value, table);
1967 double biasValue = config->getDoubleValue("bias", 0);
1969 value = new SGBiasExpression<double>(value, biasValue);
1970 value = new SGStepExpression<double>(value,
1971 config->getDoubleValue("step", 0),
1972 config->getDoubleValue("scroll", 0));
1973 value = value->simplify();
1975 double biasValue = config->getDoubleValue("bias", 0);
1977 value = new SGBiasExpression<double>(value, biasValue);
1978 value = new SGStepExpression<double>(value,
1979 config->getDoubleValue("step", 0),
1980 config->getDoubleValue("scroll", 0));
1981 value = read_offset_factor(config, value, "factor", "offset");
1983 if (config->hasChild("min") || config->hasChild("max")) {
1984 double minClip = config->getDoubleValue("min", -SGLimitsd::max());
1985 double maxClip = config->getDoubleValue("max", SGLimitsd::max());
1986 value = new SGClipExpression<double>(value, minClip, maxClip);
1988 value = value->simplify();
1990 SGVec3d axis(config->getDoubleValue("axis/x", 0),
1991 config->getDoubleValue("axis/y", 0),
1992 config->getDoubleValue("axis/z", 0));
1993 Translation* translation;
1994 translation = new Translation(normalize(axis));
1995 translation->setValue(config->getDoubleValue("starting-position", 0));
1996 updateCallback->appendTransform(translation, value);
2000 SGTexTransformAnimation::appendTexRotate(const SGPropertyNode* config,
2001 UpdateCallback* updateCallback)
2003 std::string propertyName = config->getStringValue("property", "");
2004 SGSharedPtr<SGExpressiond> value;
2005 if (propertyName.empty())
2006 value = new SGConstExpression<double>(0);
2008 SGPropertyNode* inputProperty = getModelRoot()->getNode(propertyName, true);
2009 value = new SGPropertyExpression<double>(inputProperty);
2012 SGInterpTable* table = read_interpolation_table(config);
2014 value = new SGInterpTableExpression<double>(value, table);
2015 double biasValue = config->getDoubleValue("bias", 0);
2017 value = new SGBiasExpression<double>(value, biasValue);
2018 value = new SGStepExpression<double>(value,
2019 config->getDoubleValue("step", 0),
2020 config->getDoubleValue("scroll", 0));
2021 value = value->simplify();
2023 double biasValue = config->getDoubleValue("bias", 0);
2025 value = new SGBiasExpression<double>(value, biasValue);
2026 value = new SGStepExpression<double>(value,
2027 config->getDoubleValue("step", 0),
2028 config->getDoubleValue("scroll", 0));
2029 value = read_offset_factor(config, value, "factor", "offset-deg");
2031 if (config->hasChild("min-deg") || config->hasChild("max-deg")) {
2032 double minClip = config->getDoubleValue("min-deg", -SGLimitsd::max());
2033 double maxClip = config->getDoubleValue("max-deg", SGLimitsd::max());
2034 value = new SGClipExpression<double>(value, minClip, maxClip);
2036 value = value->simplify();
2038 SGVec3d axis(config->getDoubleValue("axis/x", 0),
2039 config->getDoubleValue("axis/y", 0),
2040 config->getDoubleValue("axis/z", 0));
2041 SGVec3d center(config->getDoubleValue("center/x", 0),
2042 config->getDoubleValue("center/y", 0),
2043 config->getDoubleValue("center/z", 0));
2045 rotation = new Rotation(normalize(axis), center);
2046 rotation->setValue(config->getDoubleValue("starting-position-deg", 0));
2047 updateCallback->appendTransform(rotation, value);
2051 ////////////////////////////////////////////////////////////////////////
2052 // Implementation of SGPickAnimation
2053 ////////////////////////////////////////////////////////////////////////
2055 class SGPickAnimation::PickCallback : public SGPickCallback {
2057 PickCallback(const SGPropertyNode* configNode,
2058 SGPropertyNode* modelRoot) :
2059 _repeatable(configNode->getBoolValue("repeatable", false)),
2060 _repeatInterval(configNode->getDoubleValue("interval-sec", 0.1))
2062 SG_LOG(SG_INPUT, SG_DEBUG, "Reading all bindings");
2063 std::vector<SGPropertyNode_ptr> bindings;
2065 bindings = configNode->getChildren("button");
2066 for (unsigned int i = 0; i < bindings.size(); ++i) {
2067 _buttons.push_back( bindings[i]->getIntValue() );
2069 bindings = configNode->getChildren("binding");
2070 for (unsigned int i = 0; i < bindings.size(); ++i) {
2071 _bindingsDown.push_back(new SGBinding(bindings[i], modelRoot));
2074 const SGPropertyNode* upNode = configNode->getChild("mod-up");
2077 bindings = upNode->getChildren("binding");
2078 for (unsigned int i = 0; i < bindings.size(); ++i) {
2079 _bindingsUp.push_back(new SGBinding(bindings[i], modelRoot));
2082 virtual bool buttonPressed(int button, const Info&)
2085 for( std::vector<int>::iterator it = _buttons.begin(); it != _buttons.end(); it++ ) {
2086 if( *it == button ) {
2093 SGBindingList::const_iterator i;
2094 for (i = _bindingsDown.begin(); i != _bindingsDown.end(); ++i)
2096 _repeatTime = -_repeatInterval; // anti-bobble: delay start of repeat
2099 virtual void buttonReleased(void)
2101 SGBindingList::const_iterator i;
2102 for (i = _bindingsUp.begin(); i != _bindingsUp.end(); ++i)
2105 virtual void update(double dt)
2111 while (_repeatInterval < _repeatTime) {
2112 _repeatTime -= _repeatInterval;
2113 SGBindingList::const_iterator i;
2114 for (i = _bindingsDown.begin(); i != _bindingsDown.end(); ++i)
2119 SGBindingList _bindingsDown;
2120 SGBindingList _bindingsUp;
2121 std::vector<int> _buttons;
2123 double _repeatInterval;
2127 class VncVisitor : public osg::NodeVisitor {
2129 VncVisitor(double x, double y, int mask) :
2130 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
2131 _texX(x), _texY(y), _mask(mask), _done(false)
2133 SG_LOG(SG_INPUT, SG_DEBUG, "VncVisitor constructor "
2134 << x << "," << y << " mask " << mask);
2137 virtual void apply(osg::Node &node)
2139 // Some nodes have state sets attached
2140 touchStateSet(node.getStateSet());
2144 // See whether we are a geode worth exploring
2145 osg::Geode *g = dynamic_cast<osg::Geode*>(&node);
2147 // Go find all its drawables
2148 int i = g->getNumDrawables();
2150 osg::Drawable *d = g->getDrawable(i);
2151 if (d) touchDrawable(*d);
2153 // Out of optimism, do the same for EffectGeode
2154 simgear::EffectGeode *eg = dynamic_cast<simgear::EffectGeode*>(&node);
2156 for (simgear::EffectGeode::DrawablesIterator di = eg->drawablesBegin();
2157 di != eg->drawablesEnd(); di++) {
2158 touchDrawable(**di);
2160 // Now see whether the EffectGeode has an Effect
2161 simgear::Effect *e = eg->getEffect();
2163 touchStateSet(e->getDefaultStateSet());
2167 inline void touchDrawable(osg::Drawable &d)
2169 osg::StateSet *ss = d.getStateSet();
2173 void touchStateSet(osg::StateSet *ss)
2176 osg::StateAttribute *sa = ss->getTextureAttribute(0,
2177 osg::StateAttribute::TEXTURE);
2179 osg::Texture *t = sa->asTexture();
2181 osg::Image *img = t->getImage(0);
2184 int pixX = _texX * img->s();
2185 int pixY = _texY * img->t();
2186 _done = img->sendPointerEvent(pixX, pixY, _mask);
2187 SG_LOG(SG_INPUT, SG_DEBUG, "VncVisitor image said " << _done
2188 << " to coord " << pixX << "," << pixY);
2192 inline bool wasSuccessful()
2198 double _texX, _texY;
2204 class SGPickAnimation::VncCallback : public SGPickCallback {
2206 VncCallback(const SGPropertyNode* configNode,
2207 SGPropertyNode* modelRoot,
2211 SG_LOG(SG_INPUT, SG_DEBUG, "Configuring VNC callback");
2212 const char *cornernames[3] = {"top-left", "top-right", "bottom-left"};
2213 SGVec3d *cornercoords[3] = {&_topLeft, &_toRight, &_toDown};
2214 for (int c =0; c < 3; c++) {
2215 const SGPropertyNode* cornerNode = configNode->getChild(cornernames[c]);
2216 *cornercoords[c] = SGVec3d(
2217 cornerNode->getDoubleValue("x"),
2218 cornerNode->getDoubleValue("y"),
2219 cornerNode->getDoubleValue("z"));
2221 _toRight -= _topLeft;
2222 _toDown -= _topLeft;
2223 _squaredRight = dot(_toRight, _toRight);
2224 _squaredDown = dot(_toDown, _toDown);
2227 virtual bool buttonPressed(int button, const Info& info)
2229 SGVec3d loc(info.local);
2230 SG_LOG(SG_INPUT, SG_DEBUG, "VNC pressed " << button << ": " << loc);
2232 _x = dot(loc, _toRight) / _squaredRight;
2233 _y = dot(loc, _toDown) / _squaredDown;
2234 if (_x<0) _x = 0; else if (_x > 1) _x = 1;
2235 if (_y<0) _y = 0; else if (_y > 1) _y = 1;
2236 VncVisitor vv(_x, _y, 1 << button);
2238 return vv.wasSuccessful();
2241 virtual void buttonReleased(void)
2243 SG_LOG(SG_INPUT, SG_DEBUG, "VNC release");
2244 VncVisitor vv(_x, _y, 0);
2247 virtual void update(double dt)
2252 osg::ref_ptr<osg::Group> _node;
2253 SGVec3d _topLeft, _toRight, _toDown;
2254 double _squaredRight, _squaredDown;
2257 SGPickAnimation::SGPickAnimation(const SGPropertyNode* configNode,
2258 SGPropertyNode* modelRoot) :
2259 SGAnimation(configNode, modelRoot)
2264 SGPickAnimation::createAnimationGroup(osg::Group& parent)
2266 osg::Group* commonGroup = new osg::Group;
2268 // Contains the normal geometry that is interactive
2269 osg::ref_ptr<osg::Group> normalGroup = new osg::Group;
2270 normalGroup->setName("pick normal group");
2271 normalGroup->addChild(commonGroup);
2273 // Used to render the geometry with just yellow edges
2274 osg::Group* highlightGroup = new osg::Group;
2275 highlightGroup->setName("pick highlight group");
2276 highlightGroup->setNodeMask(SG_NODEMASK_PICK_BIT);
2277 highlightGroup->addChild(commonGroup);
2278 SGSceneUserData* ud;
2279 ud = SGSceneUserData::getOrCreateSceneUserData(commonGroup);
2281 // add actions that become macro and command invocations
2282 std::vector<SGPropertyNode_ptr> actions;
2283 actions = getConfig()->getChildren("action");
2284 for (unsigned int i = 0; i < actions.size(); ++i)
2285 ud->addPickCallback(new PickCallback(actions[i], getModelRoot()));
2286 // Look for the VNC sessions that want raw mouse input
2287 actions = getConfig()->getChildren("vncaction");
2288 for (unsigned int i = 0; i < actions.size(); ++i)
2289 ud->addPickCallback(new VncCallback(actions[i], getModelRoot(),
2292 // prepare a state set that paints the edges of this object yellow
2293 osg::StateSet* stateSet = highlightGroup->getOrCreateStateSet();
2294 stateSet->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE);
2296 osg::PolygonOffset* polygonOffset = new osg::PolygonOffset;
2297 polygonOffset->setFactor(-1);
2298 polygonOffset->setUnits(-1);
2299 stateSet->setAttribute(polygonOffset, osg::StateAttribute::OVERRIDE);
2300 stateSet->setMode(GL_POLYGON_OFFSET_LINE, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
2302 osg::PolygonMode* polygonMode = new osg::PolygonMode;
2303 polygonMode->setMode(osg::PolygonMode::FRONT_AND_BACK,
2304 osg::PolygonMode::LINE);
2305 stateSet->setAttribute(polygonMode, osg::StateAttribute::OVERRIDE);
2307 osg::Material* material = new osg::Material;
2308 material->setColorMode(osg::Material::OFF);
2309 material->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(1, 1, 0, 1));
2310 material->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(1, 1, 0, 1));
2311 material->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(1, 1, 0, 1));
2312 material->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0, 0, 0, 0));
2313 stateSet->setAttribute(material, osg::StateAttribute::OVERRIDE);
2315 // Only add normal geometry if configured
2316 if (getConfig()->getBoolValue("visible", true))
2317 parent.addChild(normalGroup.get());
2318 parent.addChild(highlightGroup);