From 9535aef59bc4126d7c3c692795d4847343c68f9e Mon Sep 17 00:00:00 2001 From: Thomas Geymayer Date: Thu, 9 May 2013 21:31:27 +0200 Subject: [PATCH] Extend SGPickCallback to allow passing more information to callbacks --- simgear/canvas/CMakeLists.txt | 2 + simgear/canvas/CanvasObjectPlacement.cxx | 244 +++++++++++++++++++++++ simgear/canvas/CanvasObjectPlacement.hxx | 75 +++++++ simgear/canvas/MouseEvent.hxx | 9 + simgear/scene/model/SGPickAnimation.cxx | 47 +++-- simgear/scene/util/SGPickCallback.hxx | 32 ++- 6 files changed, 388 insertions(+), 21 deletions(-) create mode 100644 simgear/canvas/CanvasObjectPlacement.cxx create mode 100644 simgear/canvas/CanvasObjectPlacement.hxx diff --git a/simgear/canvas/CMakeLists.txt b/simgear/canvas/CMakeLists.txt index be1a44ff..644f62af 100644 --- a/simgear/canvas/CMakeLists.txt +++ b/simgear/canvas/CMakeLists.txt @@ -9,6 +9,7 @@ set(HEADERS CanvasEventTypes.hxx CanvasEventVisitor.hxx CanvasMgr.hxx + CanvasObjectPlacement.hxx CanvasPlacement.hxx CanvasSystemAdapter.hxx MouseEvent.hxx @@ -23,6 +24,7 @@ set(SOURCES CanvasEventManager.cxx CanvasEventVisitor.cxx CanvasMgr.cxx + CanvasObjectPlacement.cxx CanvasPlacement.cxx ODGauge.cxx VGInitOperation.cxx diff --git a/simgear/canvas/CanvasObjectPlacement.cxx b/simgear/canvas/CanvasObjectPlacement.cxx new file mode 100644 index 00000000..fc0b738f --- /dev/null +++ b/simgear/canvas/CanvasObjectPlacement.cxx @@ -0,0 +1,244 @@ +// Canvas placement for placing a canvas texture onto osg objects. +// +// It also provides a SGPickCallback for passing mouse events to the canvas and +// manages emissive lighting of the placed canvas. +// +// Copyright (C) 2013 Thomas Geymayer +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + +#include "Canvas.hxx" +#include "CanvasObjectPlacement.hxx" +#include "MouseEvent.hxx" + +#include +#include + +#include + +namespace simgear +{ +namespace canvas +{ + + /** + * Handle picking events on object with a canvas placed onto + */ + class ObjectPickCallback: + public SGPickCallback + { + public: + + ObjectPickCallback(const CanvasWeakPtr& canvas): + _canvas(canvas) + {} + + virtual bool needsUV() const { return true; } + + virtual bool buttonPressed( int, + const osgGA::GUIEventAdapter& ea, + const Info& info ) + { + MouseEventPtr event(new MouseEvent(ea)); + updatePosFromUV(event, info.uv); + + if( ea.getEventType() == osgGA::GUIEventAdapter::SCROLL ) + { + event->type = Event::WHEEL; + event->delta.set(0,0); + switch( ea.getScrollingMotion() ) + { + case osgGA::GUIEventAdapter::SCROLL_UP: + event->delta.y() = 1; + break; + case osgGA::GUIEventAdapter::SCROLL_DOWN: + event->delta.y() = -1; + break; + default: + return false; + } + } + else + { + event->type = Event::MOUSE_DOWN; + } + + return handleEvent(event); + } + + virtual void buttonReleased( int, + const osgGA::GUIEventAdapter& ea, + const Info* info ) + { + if( ea.getEventType() != osgGA::GUIEventAdapter::RELEASE ) + return; + + MouseEventPtr event(new MouseEvent(ea)); + event->type = Event::MOUSE_UP; + updatePosFromUV(event, info ? info->uv : SGVec2d(-1,-1)); + + handleEvent(event); + } + + virtual void mouseMoved( const osgGA::GUIEventAdapter& ea, + const Info* info ) + { + // drag (currently only with LMB) + if( ea.getEventType() != osgGA::GUIEventAdapter::DRAG ) + return; + + MouseEventPtr event(new MouseEvent(ea)); + event->type = Event::DRAG; + updatePosFromUV(event, info ? info->uv : SGVec2d(-1,-1)); + + handleEvent(event); + } + + virtual bool hover( const osg::Vec2d& windowPos, + const Info& info ) + { + // TODO somehow get more info about event (time, modifiers, pressed + // buttons, ...) + MouseEventPtr event(new MouseEvent); + event->type = Event::MOUSE_MOVE; + event->screen_pos = windowPos; + updatePosFromUV(event, info.uv); + + return handleEvent(event); + } + + virtual void mouseLeave( const osg::Vec2d& windowPos ) + { + MouseEventPtr event(new MouseEvent); + event->type = Event::MOUSE_LEAVE; + event->screen_pos = windowPos; + + handleEvent(event); + } + + protected: + CanvasWeakPtr _canvas; + osg::Vec2f _last_pos, + _last_delta; + + void updatePosFromUV(const MouseEventPtr& event, const SGVec2d& uv) + { + CanvasPtr canvas = _canvas.lock(); + if( !canvas ) + return; + + osg::Vec2d pos( uv.x() * canvas->getViewWidth(), + (1 - uv.y()) * canvas->getViewHeight() ); + + _last_delta = pos - _last_pos; + _last_pos = pos; + + event->client_pos = pos; + event->delta = _last_delta; + } + + bool handleEvent(const MouseEventPtr& event) + { + CanvasPtr canvas = _canvas.lock(); + if( !canvas ) + return false; + + return canvas->handleMouseEvent(event); + } + }; + + //---------------------------------------------------------------------------- + ObjectPlacement::ObjectPlacement( SGPropertyNode* node, + const GroupPtr& group, + const CanvasWeakPtr& canvas ): + Placement(node), + _group(group), + _canvas(canvas) + { + // TODO make more generic and extendable for more properties + if( node->hasValue("emission") ) + setEmission( node->getFloatValue("emission") ); + if( node->hasValue("capture-events") ) + setCaptureEvents( node->getBoolValue("capture-events") ); + } + + //---------------------------------------------------------------------------- + ObjectPlacement::~ObjectPlacement() + { + assert( _group->getNumChildren() == 1 ); + osg::Node *child = _group->getChild(0); + + if( _group->getNumParents() ) + { + osg::Group *parent = _group->getParent(0); + parent->addChild(child); + parent->removeChild(_group); + } + + _group->removeChild(child); + } + + //---------------------------------------------------------------------------- + void ObjectPlacement::setEmission(float emit) + { + emit = SGMiscf::clip(emit, 0, 1); + + if( !_material ) + { + _material = new osg::Material; + _material->setColorMode(osg::Material::OFF); + _material->setDataVariance(osg::Object::DYNAMIC); + _group->getOrCreateStateSet() + ->setAttribute(_material, ( osg::StateAttribute::ON + | osg::StateAttribute::OVERRIDE ) ); + } + + _material->setEmission( + osg::Material::FRONT_AND_BACK, + osg::Vec4(emit, emit, emit, emit) + ); + } + + //---------------------------------------------------------------------------- + void ObjectPlacement::setCaptureEvents(bool enable) + { + if( !enable && _scene_user_data ) + return; + + if( enable && !_pick_cb ) + _pick_cb = new ObjectPickCallback(_canvas); + + _scene_user_data = SGSceneUserData::getOrCreateSceneUserData(_group); + _scene_user_data->setPickCallback(enable ? _pick_cb.get() : 0); + } + + //---------------------------------------------------------------------------- + bool ObjectPlacement::childChanged(SGPropertyNode* node) + { + if( node->getParent() != _node ) + return false; + + if( node->getNameString() == "emission" ) + setEmission( node->getFloatValue() ); + else if( node->getNameString() == "capture-events" ) + setCaptureEvents( node->getBoolValue() ); + else + return false; + + return true; + } + +} // namespace canvas +} // namespace simgear diff --git a/simgear/canvas/CanvasObjectPlacement.hxx b/simgear/canvas/CanvasObjectPlacement.hxx new file mode 100644 index 00000000..40c0550a --- /dev/null +++ b/simgear/canvas/CanvasObjectPlacement.hxx @@ -0,0 +1,75 @@ +// Canvas placement for placing a canvas texture onto osg objects. +// +// It also provides a SGPickCallback for passing mouse events to the canvas and +// manages emissive lighting of the placed canvas. +// +// Copyright (C) 2013 Thomas Geymayer +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + +#ifndef CANVAS_PICK_PLACEMENT_HXX_ +#define CANVAS_OBJECT_PLACEMENT_HXX_ + +#include "CanvasPlacement.hxx" +#include "canvas_fwd.hxx" + +#include +#include + +namespace simgear +{ +namespace canvas +{ + + class ObjectPlacement: + public Placement + { + public: + + typedef osg::ref_ptr GroupPtr; + typedef osg::ref_ptr MaterialPtr; + + ObjectPlacement( SGPropertyNode* node, + const GroupPtr& group, + const CanvasWeakPtr& canvas ); + virtual ~ObjectPlacement(); + + /** + * Set emissive lighting of the object the canvas is placed on. + */ + void setEmission(float emit); + + /** + * Set whether pick events should be captured. + */ + void setCaptureEvents(bool enable); + + virtual bool childChanged(SGPropertyNode* child); + + protected: + typedef SGSharedPtr PickCallbackPtr; + typedef osg::ref_ptr SGSceneUserDataPtr; + + GroupPtr _group; + MaterialPtr _material; + CanvasWeakPtr _canvas; + PickCallbackPtr _pick_cb; + SGSceneUserDataPtr _scene_user_data; + }; + +} // namespace canvas +} // namespace simgear + +#endif /* CANVAS_PICK_PLACEMENT_HXX_ */ diff --git a/simgear/canvas/MouseEvent.hxx b/simgear/canvas/MouseEvent.hxx index 5bbfb993..c84fcd27 100644 --- a/simgear/canvas/MouseEvent.hxx +++ b/simgear/canvas/MouseEvent.hxx @@ -38,6 +38,15 @@ namespace canvas click_count(0) {} + MouseEvent(const osgGA::GUIEventAdapter& ea): + button(ea.getButton()), + state(ea.getButtonMask()), + mod(ea.getModKeyMask()), + click_count(0) + { + time = ea.getTime(); + } + osg::Vec2f getScreenPos() const { return screen_pos; } osg::Vec2f getClientPos() const { return client_pos; } osg::Vec2f getDelta() const { return delta; } diff --git a/simgear/scene/model/SGPickAnimation.cxx b/simgear/scene/model/SGPickAnimation.cxx index 9c2ff4fb..dd10a9c8 100644 --- a/simgear/scene/model/SGPickAnimation.cxx +++ b/simgear/scene/model/SGPickAnimation.cxx @@ -54,17 +54,17 @@ static void readOptionalBindingList(const SGPropertyNode* aNode, SGPropertyNode* } -osg::Vec2d eventToWindowCoords(const osgGA::GUIEventAdapter* ea) +osg::Vec2d eventToWindowCoords(const osgGA::GUIEventAdapter& ea) { using namespace osg; - const GraphicsContext* gc = ea->getGraphicsContext(); + const GraphicsContext* gc = ea.getGraphicsContext(); const GraphicsContext::Traits* traits = gc->getTraits() ; // Scale x, y to the dimensions of the window - double x = (((ea->getX() - ea->getXmin()) / (ea->getXmax() - ea->getXmin())) + double x = (((ea.getX() - ea.getXmin()) / (ea.getXmax() - ea.getXmin())) * (double)traits->width); - double y = (((ea->getY() - ea->getYmin()) / (ea->getYmax() - ea->getYmin())) + double y = (((ea.getY() - ea.getYmin()) / (ea.getYmax() - ea.getYmin())) * (double)traits->height); - if (ea->getMouseYOrientation() == osgGA::GUIEventAdapter::Y_INCREASING_DOWNWARDS) + if (ea.getMouseYOrientation() == osgGA::GUIEventAdapter::Y_INCREASING_DOWNWARDS) y = (double)traits->height - y; return osg::Vec2d(x, y); @@ -100,7 +100,9 @@ osg::Vec2d eventToWindowCoords(const osgGA::GUIEventAdapter* ea) _hover = readBindingList(hoverNode->getChildren("binding"), modelRoot); } - virtual bool buttonPressed(int button, const osgGA::GUIEventAdapter* ea, const Info&) + virtual bool buttonPressed( int button, + const osgGA::GUIEventAdapter&, + const Info& ) { if (_buttons.find(button) == _buttons.end()) { return false; @@ -110,7 +112,9 @@ osg::Vec2d eventToWindowCoords(const osgGA::GUIEventAdapter* ea) _repeatTime = -_repeatInterval; // anti-bobble: delay start of repeat return true; } - virtual void buttonReleased(int keyModState) + virtual void buttonReleased( int keyModState, + const osgGA::GUIEventAdapter&, + const Info* ) { SG_UNUSED(keyModState); fireBindingList(_bindingsUp); @@ -129,7 +133,8 @@ osg::Vec2d eventToWindowCoords(const osgGA::GUIEventAdapter* ea) } } - virtual bool hover(const osg::Vec2d& windowPos, const Info& info) + virtual bool hover( const osg::Vec2d& windowPos, + const Info& ) { if (_hover.empty()) { return false; @@ -256,7 +261,9 @@ public: _squaredDown = dot(_toDown, _toDown); } - virtual bool buttonPressed(int button, const osgGA::GUIEventAdapter* ea, const Info& info) + virtual bool buttonPressed( int button, + const osgGA::GUIEventAdapter&, + const Info& info ) { SGVec3d loc(info.local); SG_LOG(SG_INPUT, SG_DEBUG, "VNC pressed " << button << ": " << loc); @@ -270,7 +277,9 @@ public: return vv.wasSuccessful(); } - virtual void buttonReleased(int keyModState) + virtual void buttonReleased( int keyModState, + const osgGA::GUIEventAdapter&, + const Info* ) { SG_UNUSED(keyModState); SG_LOG(SG_INPUT, SG_DEBUG, "VNC release"); @@ -544,11 +553,13 @@ public: } } - virtual bool buttonPressed(int button, const osgGA::GUIEventAdapter* ea, const Info&) + virtual bool buttonPressed( int button, + const osgGA::GUIEventAdapter& ea, + const Info& ) { // the 'be nice to Mac / laptop' users option; alt-clicking spins the // opposite direction. Should make this configurable - if ((button == 0) && (ea->getModKeyMask() & osgGA::GUIEventAdapter::MODKEY_ALT)) { + if ((button == 0) && (ea.getModKeyMask() & osgGA::GUIEventAdapter::MODKEY_ALT)) { button = 1; } @@ -571,7 +582,9 @@ public: return true; } - virtual void buttonReleased(int keyModState) + virtual void buttonReleased( int keyModState, + const osgGA::GUIEventAdapter&, + const Info* ) { // for *clicks*, we only fire on button release if (!_hasDragged) { @@ -592,7 +605,8 @@ public: return _dragDirection; } - virtual void mouseMoved(const osgGA::GUIEventAdapter* ea) + virtual void mouseMoved( const osgGA::GUIEventAdapter& ea, + const Info* ) { _mousePos = eventToWindowCoords(ea); osg::Vec2d deltaMouse = _mousePos - _lastFirePos; @@ -618,7 +632,7 @@ public: if (fabs(delta) >= 1.0) { // determine direction from sign of delta Direction dir = (delta > 0.0) ? DIRECTION_INCREASE : DIRECTION_DECREASE; - fire(ea->getModKeyMask() & osgGA::GUIEventAdapter::MODKEY_SHIFT, dir); + fire(ea.getModKeyMask() & osgGA::GUIEventAdapter::MODKEY_SHIFT, dir); _lastFirePos = _mousePos; } } @@ -636,7 +650,8 @@ public: } // of repeat iteration } - virtual bool hover(const osg::Vec2d& windowPos, const Info& info) + virtual bool hover( const osg::Vec2d& windowPos, + const Info& ) { if (_hover.empty()) { return false; diff --git a/simgear/scene/util/SGPickCallback.hxx b/simgear/scene/util/SGPickCallback.hxx index 838d5b2e..c41bce72 100644 --- a/simgear/scene/util/SGPickCallback.hxx +++ b/simgear/scene/util/SGPickCallback.hxx @@ -52,19 +52,41 @@ public: { } virtual ~SGPickCallback() {} - virtual bool buttonPressed(int button, const osgGA::GUIEventAdapter* event, const Info& info) + + // TODO maybe better provide a single callback to handle all events + virtual bool buttonPressed( int button, + const osgGA::GUIEventAdapter& ea, + const Info& info ) { return false; } virtual void update(double dt, int keyModState) { } - - virtual void buttonReleased(int keyModState) + + /** + * @param info Can be null if no info is available (eg. mouse not over 3d + * object anymore) + */ + virtual void buttonReleased( int keyModState, + const osgGA::GUIEventAdapter& ea, + const Info* info ) + { } + + /** + * @param info Can be null if no info is available (eg. mouse not over 3d + * object anymore) + */ + virtual void mouseMoved( const osgGA::GUIEventAdapter& ea, + const Info* info ) { } - virtual void mouseMoved(const osgGA::GUIEventAdapter* event) + /** + * The mouse is not hovering anymore over the element. + */ + virtual void mouseLeave(const osg::Vec2d& windowPos) { } - virtual bool hover(const osg::Vec2d& windowPos, const Info& info) + virtual bool hover( const osg::Vec2d& windowPos, + const Info& info ) { return false; } virtual Priority getPriority() const -- 2.39.2