From 2a60e5e338528f9431c0c27e59610a7a54f4432e Mon Sep 17 00:00:00 2001 From: Richard Harrison Date: Sun, 29 Oct 2017 01:04:36 +0200 Subject: [PATCH] Added touch animation. Designed for 2d objects, such as a canvas placements, this permits the receipt of touch (mouse click) events to enable the simulation of avionics with a touchscreen. The coordinates are passed in as arguments to the action; these can be accessed with Nasal via the cmdarg() method. example: touch true VSDImage 0 false nasal --- simgear/scene/model/SGPickAnimation.cxx | 154 ++++++++++++++++++++++ simgear/scene/model/SGPickAnimation.hxx | 14 ++ simgear/scene/model/SGReaderWriterXML.cxx | 2 +- simgear/scene/model/animation.cxx | 3 + 4 files changed, 172 insertions(+), 1 deletion(-) diff --git a/simgear/scene/model/SGPickAnimation.cxx b/simgear/scene/model/SGPickAnimation.cxx index 7b4c4d35..3fa643d6 100644 --- a/simgear/scene/model/SGPickAnimation.cxx +++ b/simgear/scene/model/SGPickAnimation.cxx @@ -832,3 +832,157 @@ SGSliderAnimation::setupCallbacks(SGSceneUserData* ud, osg::Group*) { ud->setPickCallback(new KnobSliderPickCallback(getConfig(), getModelRoot())); } + +/* + * touch screen is a 2d surface that will pass parameters to the callbacks indicating the + * normalized coordinates of hover or touch. Touch is defined as a button click. + * For compatibility with touchscreen operations this class does not differentiate between + * which buttons are touched, simply because this isn't how touchscreens work. + * Some touchscreens (e.g. SAW) can have a Z-axis indicating the pressure. This is not + * simulated. + */ + + +/** +* Handle picking events on object with a canvas placed onto +*/ +class TouchPickCallback : public SGPickCallback { + public: + TouchPickCallback(const SGPropertyNode* configNode, + SGPropertyNode* modelRoot) : + _repeatable(configNode->getBoolValue("repeatable", false)), + _repeatInterval(configNode->getDoubleValue("interval-sec", 0.1)), + SGPickCallback(PriorityPanel) + { + std::vector bindings; + + bindings = configNode->getChildren("touch"); + for (unsigned int i = 0; i < bindings.size(); ++i) { + _touches.insert(bindings[i]->getIntValue()); + } + + _bindingsTouched = readBindingList(configNode->getChildren("binding"), modelRoot); + readOptionalBindingList(configNode, modelRoot, "mod-up", _bindingsReleased); + + if (configNode->hasChild("cursor")) { + _cursorName = configNode->getStringValue("cursor"); + } + } + + void addHoverBindings(const SGPropertyNode* hoverNode, + SGPropertyNode* modelRoot) + { + _hover = readBindingList(hoverNode->getChildren("binding"), modelRoot); + } + + virtual bool buttonPressed(int touchIdx, + const osgGA::GUIEventAdapter& event, + const Info& info) + { + if (_touches.find(touchIdx) == _touches.end()) { + return false; + } + + if (!anyBindingEnabled(_bindingsTouched)) { + return false; + } + SGPropertyNode_ptr params(new SGPropertyNode); + params->setDoubleValue("x", info.uv[0]); + params->setDoubleValue("y", info.uv[1]); + + _repeatTime = -_repeatInterval; // anti-bobble: delay start of repeat + fireBindingList(_bindingsTouched, params.ptr()); + return true; + } + virtual void buttonReleased(int keyModState, + const osgGA::GUIEventAdapter&, + const Info* info) + { + SG_UNUSED(keyModState); + SGPropertyNode_ptr params(new SGPropertyNode); + params->setDoubleValue("x", info->uv[0]); + params->setDoubleValue("y", info->uv[1]); + fireBindingList(_bindingsReleased, params.ptr()); + } + + virtual void update(double dt, int keyModState) + { + SG_UNUSED(keyModState); + if (!_repeatable) + return; + + _repeatTime += dt; + while (_repeatInterval < _repeatTime) { + _repeatTime -= _repeatInterval; + fireBindingList(_bindingsTouched); + } + } + + virtual bool hover(const osg::Vec2d& windowPos, + const Info& info) + { + if (!anyBindingEnabled(_hover)) { + return false; + } + + SGPropertyNode_ptr params(new SGPropertyNode); + params->setDoubleValue("x", info.uv[0]); + params->setDoubleValue("y", info.uv[1]); + fireBindingList(_hover, params.ptr()); + return true; + } + + std::string getCursor() const + { + return _cursorName; + } + + virtual bool needsUV() const { return true; } + + private: + SGBindingList _bindingsTouched; + SGBindingList _bindingsReleased; + SGBindingList _hover; + std::set _touches; + std::string _cursorName; + bool _repeatable; + double _repeatInterval; + double _repeatTime; +}; + +SGTouchAnimation::SGTouchAnimation(simgear::SGTransientModelData &modelData) : + SGPickAnimation(modelData) +{ +} + +osg::Group* SGTouchAnimation::createMainGroup(osg::Group* pr) +{ + SGRotateTransform* transform = new SGRotateTransform(); + pr->addChild(transform); + return transform; +} + +void SGTouchAnimation::setupCallbacks(SGSceneUserData* ud, osg::Group*) +{ + TouchPickCallback* touchCb = NULL; + + // add actions that become macro and command invocations + std::vector actions; + actions = getConfig()->getChildren("action"); + for (unsigned int i = 0; i < actions.size(); ++i) { + touchCb = new TouchPickCallback(actions[i], getModelRoot()); + ud->addPickCallback(touchCb); + } + + if (getConfig()->hasChild("hovered")) { + if (!touchCb) { + // make a trivial PickCallback to hang the hovered off of + SGPropertyNode_ptr dummyNode(new SGPropertyNode); + touchCb = new TouchPickCallback(dummyNode.ptr(), getModelRoot()); + ud->addPickCallback(touchCb); + } + + touchCb->addHoverBindings(getConfig()->getNode("hovered"), getModelRoot()); + } +// ud->setPickCallback(new TouchPickCallback(getConfig(), getModelRoot())); +} diff --git a/simgear/scene/model/SGPickAnimation.hxx b/simgear/scene/model/SGPickAnimation.hxx index 580f02a7..00695467 100644 --- a/simgear/scene/model/SGPickAnimation.hxx +++ b/simgear/scene/model/SGPickAnimation.hxx @@ -114,5 +114,19 @@ private: SGSharedPtr _animationValue; }; +class SGTouchAnimation : public SGPickAnimation +{ +public: + SGTouchAnimation(simgear::SGTransientModelData &modelData); + + +protected: + virtual osg::Group* createMainGroup(osg::Group* pr); + + virtual void setupCallbacks(SGSceneUserData* ud, osg::Group* parent); + +private: +}; + #endif // of SG_SCENE_PICK_ANIMATION_HXX diff --git a/simgear/scene/model/SGReaderWriterXML.cxx b/simgear/scene/model/SGReaderWriterXML.cxx index e46e6921..4c6304ac 100644 --- a/simgear/scene/model/SGReaderWriterXML.cxx +++ b/simgear/scene/model/SGReaderWriterXML.cxx @@ -226,7 +226,7 @@ namespace { { string typeString(aNode->getStringValue("type")); // exclude these so we don't show yellow outlines in preview mode - return (typeString == "pick") || (typeString == "knob") || (typeString == "slider"); + return (typeString == "pick") || (typeString == "knob") || (typeString == "slider") || (typeString == "touch"); } }; diff --git a/simgear/scene/model/animation.cxx b/simgear/scene/model/animation.cxx index 3e7f4399..3d3302ff 100644 --- a/simgear/scene/model/animation.cxx +++ b/simgear/scene/model/animation.cxx @@ -497,6 +497,9 @@ SGAnimation::animate(simgear::SGTransientModelData &modelData) } else if (type == "slider") { SGSliderAnimation anim(modelData); anim.apply(modelData.getNode()); + } else if (type == "touch") { + SGTouchAnimation anim(modelData); + anim.apply(modelData.getNode()); } else if (type == "range") { SGRangeAnimation anim(modelData); anim.apply(modelData);