From: James Turner Date: Thu, 31 Jan 2013 00:15:09 +0000 (+0000) Subject: Initial prototype of knob animation. X-Git-Url: https://git.mxchange.org/?a=commitdiff_plain;h=4d4e474464b45a62832b51bc44e9ffc6d4946e5e;p=simgear.git Initial prototype of knob animation. --- diff --git a/simgear/scene/model/SGPickAnimation.cxx b/simgear/scene/model/SGPickAnimation.cxx index a282df19..6b6e9c9a 100644 --- a/simgear/scene/model/SGPickAnimation.cxx +++ b/simgear/scene/model/SGPickAnimation.cxx @@ -27,11 +27,12 @@ #include #include -#include #include +#include #include #include #include +#include using namespace simgear; @@ -76,17 +77,14 @@ using OpenThreads::ScopedLock; } if (!found ) return false; - SGBindingList::const_iterator i; - for (i = _bindingsDown.begin(); i != _bindingsDown.end(); ++i) - (*i)->fire(); + + fireBindingList(_bindingsDown); _repeatTime = -_repeatInterval; // anti-bobble: delay start of repeat return true; } virtual void buttonReleased(void) { - SGBindingList::const_iterator i; - for (i = _bindingsUp.begin(); i != _bindingsUp.end(); ++i) - (*i)->fire(); + fireBindingList(_bindingsUp); } virtual void update(double dt) { @@ -96,9 +94,7 @@ using OpenThreads::ScopedLock; _repeatTime += dt; while (_repeatInterval < _repeatTime) { _repeatTime -= _repeatInterval; - SGBindingList::const_iterator i; - for (i = _bindingsDown.begin(); i != _bindingsDown.end(); ++i) - (*i)->fire(); + fireBindingList(_bindingsDown); } } private: @@ -252,23 +248,81 @@ using OpenThreads::ScopedLock; osg::ref_ptr colorModeUniform; } + +void +SGPickAnimation::innerSetupPickGroup(osg::Group* commonGroup, osg::Group& parent) +{ + // Contains the normal geometry that is interactive + osg::ref_ptr normalGroup = new osg::Group; + normalGroup->setName("pick normal group"); + normalGroup->addChild(commonGroup); + + // Used to render the geometry with just yellow edges + osg::Group* highlightGroup = new osg::Group; + highlightGroup->setName("pick highlight group"); + highlightGroup->setNodeMask(simgear::PICK_BIT); + highlightGroup->addChild(commonGroup); + + // prepare a state set that paints the edges of this object yellow + // The material and texture attributes are set with + // OVERRIDE|PROTECTED in case there is a material animation on a + // higher node in the scene graph, which would have its material + // attribute set with OVERRIDE. + osg::StateSet* stateSet = highlightGroup->getOrCreateStateSet(); + osg::Texture2D* white = StateAttributeFactory::instance()->getWhiteTexture(); + stateSet->setTextureAttributeAndModes(0, white, + (osg::StateAttribute::ON + | osg::StateAttribute::OVERRIDE + | osg::StateAttribute::PROTECTED)); + osg::PolygonOffset* polygonOffset = new osg::PolygonOffset; + polygonOffset->setFactor(-1); + polygonOffset->setUnits(-1); + stateSet->setAttribute(polygonOffset, osg::StateAttribute::OVERRIDE); + stateSet->setMode(GL_POLYGON_OFFSET_LINE, + osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); + osg::PolygonMode* polygonMode = new osg::PolygonMode; + polygonMode->setMode(osg::PolygonMode::FRONT_AND_BACK, + osg::PolygonMode::LINE); + stateSet->setAttribute(polygonMode, osg::StateAttribute::OVERRIDE); + osg::Material* material = new osg::Material; + material->setColorMode(osg::Material::OFF); + material->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(0, 0, 0, 1)); + // XXX Alpha < 1.0 in the diffuse material value is a signal to the + // default shader to take the alpha value from the material value + // and not the glColor. In many cases the pick animation geometry is + // transparent, so the outline would not be visible without this hack. + material->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(0, 0, 0, .95)); + material->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(1, 1, 0, 1)); + material->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0, 0, 0, 0)); + stateSet->setAttribute( + material, osg::StateAttribute::OVERRIDE | osg::StateAttribute::PROTECTED); + // The default shader has a colorMode uniform that mimics the + // behavior of Material color mode. + osg::Uniform* cmUniform = 0; + { + ScopedLock lock(colorModeUniformMutex); + if (!colorModeUniform.valid()) { + colorModeUniform = new osg::Uniform(osg::Uniform::INT, "colorMode"); + colorModeUniform->set(0); // MODE_OFF + colorModeUniform->setDataVariance(osg::Object::STATIC); + } + cmUniform = colorModeUniform.get(); + } + stateSet->addUniform(cmUniform, + osg::StateAttribute::OVERRIDE | osg::StateAttribute::ON); + + // Only add normal geometry if configured + if (getConfig()->getBoolValue("visible", true)) + parent.addChild(normalGroup.get()); + parent.addChild(highlightGroup); +} + osg::Group* SGPickAnimation::createAnimationGroup(osg::Group& parent) { - osg::Group* commonGroup = new osg::Group; - - // Contains the normal geometry that is interactive - osg::ref_ptr normalGroup = new osg::Group; - normalGroup->setName("pick normal group"); - normalGroup->addChild(commonGroup); - - // Used to render the geometry with just yellow edges - osg::Group* highlightGroup = new osg::Group; - highlightGroup->setName("pick highlight group"); - highlightGroup->setNodeMask(simgear::PICK_BIT); - highlightGroup->addChild(commonGroup); - SGSceneUserData* ud; - ud = SGSceneUserData::getOrCreateSceneUserData(commonGroup); + osg::Group* commonGroup = new osg::Group; + innerSetupPickGroup(commonGroup, parent); + SGSceneUserData* ud = SGSceneUserData::getOrCreateSceneUserData(commonGroup); // add actions that become macro and command invocations std::vector actions; @@ -281,59 +335,137 @@ using OpenThreads::ScopedLock; ud->addPickCallback(new VncCallback(actions[i], getModelRoot(), &parent)); - // prepare a state set that paints the edges of this object yellow - // The material and texture attributes are set with - // OVERRIDE|PROTECTED in case there is a material animation on a - // higher node in the scene graph, which would have its material - // attribute set with OVERRIDE. - osg::StateSet* stateSet = highlightGroup->getOrCreateStateSet(); - osg::Texture2D* white = StateAttributeFactory::instance()->getWhiteTexture(); - stateSet->setTextureAttributeAndModes(0, white, - (osg::StateAttribute::ON - | osg::StateAttribute::OVERRIDE - | osg::StateAttribute::PROTECTED)); - osg::PolygonOffset* polygonOffset = new osg::PolygonOffset; - polygonOffset->setFactor(-1); - polygonOffset->setUnits(-1); - stateSet->setAttribute(polygonOffset, osg::StateAttribute::OVERRIDE); - stateSet->setMode(GL_POLYGON_OFFSET_LINE, - osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); - osg::PolygonMode* polygonMode = new osg::PolygonMode; - polygonMode->setMode(osg::PolygonMode::FRONT_AND_BACK, - osg::PolygonMode::LINE); - stateSet->setAttribute(polygonMode, osg::StateAttribute::OVERRIDE); - osg::Material* material = new osg::Material; - material->setColorMode(osg::Material::OFF); - material->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(0, 0, 0, 1)); - // XXX Alpha < 1.0 in the diffuse material value is a signal to the - // default shader to take the alpha value from the material value - // and not the glColor. In many cases the pick animation geometry is - // transparent, so the outline would not be visible without this hack. - material->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(0, 0, 0, .95)); - material->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(1, 1, 0, 1)); - material->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0, 0, 0, 0)); - stateSet->setAttribute( - material, osg::StateAttribute::OVERRIDE | osg::StateAttribute::PROTECTED); - // The default shader has a colorMode uniform that mimics the - // behavior of Material color mode. - osg::Uniform* cmUniform = 0; - { - ScopedLock lock(colorModeUniformMutex); - if (!colorModeUniform.valid()) { - colorModeUniform = new osg::Uniform(osg::Uniform::INT, "colorMode"); - colorModeUniform->set(0); // MODE_OFF - colorModeUniform->setDataVariance(osg::Object::STATIC); - } - cmUniform = colorModeUniform.get(); - } - stateSet->addUniform(cmUniform, - osg::StateAttribute::OVERRIDE | osg::StateAttribute::ON); - // Only add normal geometry if configured - if (getConfig()->getBoolValue("visible", true)) - parent.addChild(normalGroup.get()); - parent.addChild(highlightGroup); - - return commonGroup; } - \ No newline at end of file + +/////////////////////////////////////////////////////////////////////////// + +class SGKnobAnimation::KnobPickCallback : public SGPickCallback { +public: + KnobPickCallback(const SGPropertyNode* configNode, + SGPropertyNode* modelRoot) : + _direction(DIRECTION_NONE), + _repeatInterval(configNode->getDoubleValue("interval-sec", 0.1)) + { + const SGPropertyNode* act = configNode->getChild("action"); + if (act) + _action = readBindingList(act->getChildren("binding"), modelRoot); + + const SGPropertyNode* cw = configNode->getChild("cw"); + if (cw) + _bindingsCW = readBindingList(cw->getChildren("binding"), modelRoot); + + const SGPropertyNode* ccw = configNode->getChild("ccw"); + if (ccw) + _bindingsCCW = readBindingList(ccw->getChildren("binding"), modelRoot); + } + + virtual bool buttonPressed(int button, const Info&) + { + _direction = DIRECTION_NONE; + if ((button == 0) || (button == 4)) { + _direction = DIRECTION_CLOCKWISE; + } else if ((button == 1) || (button == 3)) { + _direction = DIRECTION_ANTICLOCKWISE; + } else { + return false; + } + + _repeatTime = -_repeatInterval; // anti-bobble: delay start of repeat + fire(); + return true; + } + + virtual void buttonReleased(void) + { + } + + virtual void update(double dt) + { + _repeatTime += dt; + while (_repeatInterval < _repeatTime) { + _repeatTime -= _repeatInterval; + fire(); + } // of repeat iteration + } +private: + void fire() + { + switch (_direction) { + case DIRECTION_CLOCKWISE: + fireBindingListWithOffset(_action, 1, 1); + fireBindingList(_bindingsCW); + break; + case DIRECTION_ANTICLOCKWISE: + fireBindingListWithOffset(_action, -1, 1); + fireBindingList(_bindingsCCW); + break; + default: break; + } + } + + SGBindingList _action; + SGBindingList _bindingsCW, + _bindingsCCW; + + enum Direction + { + DIRECTION_NONE, + DIRECTION_CLOCKWISE, + DIRECTION_ANTICLOCKWISE + }; + + Direction _direction; + double _repeatInterval; + double _repeatTime; +}; + +class SGKnobAnimation::UpdateCallback : public osg::NodeCallback { +public: + UpdateCallback(SGExpressiond const* animationValue) : + _animationValue(animationValue) + { + setName("SGKnobAnimation::UpdateCallback"); + } + virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) + { + SGRotateTransform* transform = static_cast(node); + transform->setAngleDeg(_animationValue->getValue()); + traverse(node, nv); + } + +private: + SGSharedPtr _animationValue; +}; + + +SGKnobAnimation::SGKnobAnimation(const SGPropertyNode* configNode, + SGPropertyNode* modelRoot) : + SGPickAnimation(configNode, modelRoot) +{ + SGSharedPtr value = read_value(configNode, modelRoot, "-deg", + -SGLimitsd::max(), SGLimitsd::max()); + _animationValue = value->simplify(); + + + readRotationCenterAndAxis(configNode, _center, _axis); +} + + +osg::Group* +SGKnobAnimation::createAnimationGroup(osg::Group& parent) +{ + SGRotateTransform* transform = new SGRotateTransform(); + innerSetupPickGroup(transform, parent); + + UpdateCallback* uc = new UpdateCallback(_animationValue); + transform->setUpdateCallback(uc); + transform->setCenter(_center); + transform->setAxis(_axis); + + SGSceneUserData* ud = SGSceneUserData::getOrCreateSceneUserData(transform); + ud->setPickCallback(new KnobPickCallback(getConfig(), getModelRoot())); + + return transform; +} + diff --git a/simgear/scene/model/SGPickAnimation.hxx b/simgear/scene/model/SGPickAnimation.hxx index c1761809..615fef04 100644 --- a/simgear/scene/model/SGPickAnimation.hxx +++ b/simgear/scene/model/SGPickAnimation.hxx @@ -24,6 +24,8 @@ #include +// forward decls +class SGPickCallback; ////////////////////////////////////////////////////////////////////// // Pick animation @@ -34,10 +36,32 @@ public: SGPickAnimation(const SGPropertyNode* configNode, SGPropertyNode* modelRoot); virtual osg::Group* createAnimationGroup(osg::Group& parent); + + +protected: + void innerSetupPickGroup(osg::Group* commonGroup, osg::Group& parent); + private: class PickCallback; class VncCallback; }; + +class SGKnobAnimation : public SGPickAnimation +{ +public: + SGKnobAnimation(const SGPropertyNode* configNode, + SGPropertyNode* modelRoot); + virtual osg::Group* createAnimationGroup(osg::Group& parent); + +private: + class KnobPickCallback; + class UpdateCallback; + + SGVec3d _axis; + SGVec3d _center; + SGSharedPtr _animationValue; +}; + #endif // of SG_SCENE_PICK_ANIMATION_HXX diff --git a/simgear/scene/model/animation.cxx b/simgear/scene/model/animation.cxx index edc9e16c..3561f7ce 100644 --- a/simgear/scene/model/animation.cxx +++ b/simgear/scene/model/animation.cxx @@ -396,6 +396,9 @@ SGAnimation::animate(osg::Node* node, const SGPropertyNode* configNode, } else if (type == "pick") { SGPickAnimation animInst(configNode, modelRoot); animInst.apply(node); + } else if (type == "knob") { + SGKnobAnimation animInst(configNode, modelRoot); + animInst.apply(node); } else if (type == "range") { SGRangeAnimation animInst(configNode, modelRoot); animInst.apply(node); @@ -846,6 +849,33 @@ void SpinAnimCallback::operator()(osg::Node* node, osg::NodeVisitor* nv) } } +// factored out to share with SGKnobAnimation +void readRotationCenterAndAxis(const SGPropertyNode* configNode, SGVec3d& center, SGVec3d& axis) +{ + center = SGVec3d::zeros(); + if (configNode->hasValue("axis/x1-m")) { + SGVec3d v1, v2; + v1[0] = configNode->getDoubleValue("axis/x1-m", 0); + v1[1] = configNode->getDoubleValue("axis/y1-m", 0); + v1[2] = configNode->getDoubleValue("axis/z1-m", 0); + v2[0] = configNode->getDoubleValue("axis/x2-m", 0); + v2[1] = configNode->getDoubleValue("axis/y2-m", 0); + v2[2] = configNode->getDoubleValue("axis/z2-m", 0); + center = 0.5*(v1+v2); + axis = v2 - v1; + } else { + axis[0] = configNode->getDoubleValue("axis/x", 0); + axis[1] = configNode->getDoubleValue("axis/y", 0); + axis[2] = configNode->getDoubleValue("axis/z", 0); + } + if (8*SGLimitsd::min() < norm(axis)) + axis = normalize(axis); + + center[0] = configNode->getDoubleValue("center/x-m", center[0]); + center[1] = configNode->getDoubleValue("center/y-m", center[1]); + center[2] = configNode->getDoubleValue("center/z-m", center[2]); +} + SGRotateAnimation::SGRotateAnimation(const SGPropertyNode* configNode, SGPropertyNode* modelRoot) : SGAnimation(configNode, modelRoot) @@ -862,28 +892,8 @@ SGRotateAnimation::SGRotateAnimation(const SGPropertyNode* configNode, _initialValue = _animationValue->getValue(); else _initialValue = 0; - _center = SGVec3d::zeros(); - if (configNode->hasValue("axis/x1-m")) { - SGVec3d v1, v2; - v1[0] = configNode->getDoubleValue("axis/x1-m", 0); - v1[1] = configNode->getDoubleValue("axis/y1-m", 0); - v1[2] = configNode->getDoubleValue("axis/z1-m", 0); - v2[0] = configNode->getDoubleValue("axis/x2-m", 0); - v2[1] = configNode->getDoubleValue("axis/y2-m", 0); - v2[2] = configNode->getDoubleValue("axis/z2-m", 0); - _center = 0.5*(v1+v2); - _axis = v2 - v1; - } else { - _axis[0] = configNode->getDoubleValue("axis/x", 0); - _axis[1] = configNode->getDoubleValue("axis/y", 0); - _axis[2] = configNode->getDoubleValue("axis/z", 0); - } - if (8*SGLimitsd::min() < norm(_axis)) - _axis = normalize(_axis); - - _center[0] = configNode->getDoubleValue("center/x-m", _center[0]); - _center[1] = configNode->getDoubleValue("center/y-m", _center[1]); - _center[2] = configNode->getDoubleValue("center/z-m", _center[2]); + + readRotationCenterAndAxis(configNode, _center, _axis); } osg::Group* diff --git a/simgear/scene/model/animation.hxx b/simgear/scene/model/animation.hxx index 6c95bf3b..2bcf49ae 100644 --- a/simgear/scene/model/animation.hxx +++ b/simgear/scene/model/animation.hxx @@ -34,7 +34,9 @@ SGExpressiond* read_value(const SGPropertyNode* configNode, SGPropertyNode* modelRoot, const char* unit, double defMin, double defMax); - + +void readRotationCenterAndAxis(const SGPropertyNode* configNode, SGVec3d& center, SGVec3d& axis); + ////////////////////////////////////////////////////////////////////// // Base class for animation installers ////////////////////////////////////////////////////////////////////// diff --git a/simgear/structure/SGBinding.cxx b/simgear/structure/SGBinding.cxx index db501d40..43f55baf 100644 --- a/simgear/structure/SGBinding.cxx +++ b/simgear/structure/SGBinding.cxx @@ -114,3 +114,20 @@ void fireBindingList(const SGBindingList& aBindings) b->fire(); } } + +void fireBindingListWithOffset(const SGBindingList& aBindings, double offset, double max) +{ + BOOST_FOREACH(SGBinding_ptr b, aBindings) { + b->fire(offset, max); + } +} + +SGBindingList readBindingList(const simgear::PropertyList& aNodes, SGPropertyNode* aRoot) +{ + SGBindingList result; + BOOST_FOREACH(SGPropertyNode* node, aNodes) { + result.push_back(new SGBinding(node, aRoot)); + } + + return result; +} diff --git a/simgear/structure/SGBinding.hxx b/simgear/structure/SGBinding.hxx index 23f38820..026da270 100644 --- a/simgear/structure/SGBinding.hxx +++ b/simgear/structure/SGBinding.hxx @@ -136,4 +136,15 @@ typedef std::map SGBindingMap; */ void fireBindingList(const SGBindingList& aBindings); +/** + * fire every binding in a list with a setting value + + */ +void fireBindingListWithOffset(const SGBindingList& aBindings, double offset, double max); + +/** + * read multiple bindings from property-list format + */ +SGBindingList readBindingList(const simgear::PropertyList& aNodes, SGPropertyNode* aRoot); + #endif