From bc5575f83ae05cd1b80a31a7d1945b8971acd0c4 Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Thu, 24 Apr 2014 17:14:54 +0000 Subject: [PATCH] From Kristofer Tingdahl, "I and my team have gone over the code again, and we feel that we are comfortable in our current proposal for change. It goes deeper than it did before, and I explain why: There was code in the osgViewer/Viewer.cpp and osgViewer/CompositeViewer.cpp that transformed the Y-coordinates of an event. The code in the composite viewer did however miss the touch-data of the event. I thought that it should really be the GUIEventAdapter that should know about this, and hence I added the GUIEventAdapter::setMouseYOrientationAndUpdateCoords which is re-computing the coordinates. First I simply added a boolean to the setMouseYOrientation function: setMouseYOrientation( MouseYOrientation, bool updatecooreds=false ); but then the serializer complained. This function is called from both the Viewer and the CompositeViewer. We have not tested from the viewer, but I cannot see it would not work from visual inspection. The other change is in MultiTouchTrackballManipulator::handleMultiTouchDrag. I have removed the normalisation. The reason for that is that it normalised into screen coordinates from 0,0 to 1,1. The problem with that is that if you have a pinch event and you keep the distance say 300 pixels between your fingers, these 300 pixels represent 0.20 of the screen in the horizontal domain, but 0.3 of the screen in the vertical domain. A rotation of the pinch-fingers will hence result in a zoom in, as the normalised distance is changing between them. A consequence of this is that I have changed the pan-code to use the same algorithm as the middle-mouse-pan. The rest of it is very similar from previous revision, and there has been some fine-tuning here and there. " --- examples/osgviewerQt/osgviewerQt.cpp | 6 +- include/osgGA/GUIEventAdapter | 3 + include/osgQt/GraphicsWindowQt | 11 ++ src/osgGA/GUIEventAdapter.cpp | 17 +++ src/osgGA/MultiTouchTrackballManipulator.cpp | 27 ++--- src/osgQt/GraphicsWindowQt.cpp | 120 ++++++++++++++++++- src/osgViewer/CompositeViewer.cpp | 6 +- src/osgViewer/Viewer.cpp | 11 +- 8 files changed, 164 insertions(+), 37 deletions(-) diff --git a/examples/osgviewerQt/osgviewerQt.cpp b/examples/osgviewerQt/osgviewerQt.cpp index 5e3917df9..ee30256a9 100644 --- a/examples/osgviewerQt/osgviewerQt.cpp +++ b/examples/osgviewerQt/osgviewerQt.cpp @@ -5,7 +5,7 @@ #include #include -#include +#include #include @@ -57,8 +57,8 @@ public: view->setSceneData( scene ); view->addEventHandler( new osgViewer::StatsHandler ); - view->setCameraManipulator( new osgGA::TrackballManipulator ); - + view->setCameraManipulator( new osgGA::MultiTouchTrackballManipulator ); + gw->setTouchEventsEnabled( true ); return gw->getGLWidget(); } diff --git a/include/osgGA/GUIEventAdapter b/include/osgGA/GUIEventAdapter index af5c3f353..8de2fea42 100644 --- a/include/osgGA/GUIEventAdapter +++ b/include/osgGA/GUIEventAdapter @@ -602,6 +602,9 @@ public: /// get mouse-Y orientation (mouse-Y increases upwards or downwards). MouseYOrientation getMouseYOrientation() const { return _mouseYOrientation; } + /// set mouse-Y orientation (mouse-Y increases upwards or downwards) and recompute variables + void setMouseYOrientationAndUpdateCoords(MouseYOrientation myo); + /// set current mouse button state. void setButtonMask(int mask) { _buttonMask = mask; } diff --git a/include/osgQt/GraphicsWindowQt b/include/osgQt/GraphicsWindowQt index b6adef265..30924675c 100644 --- a/include/osgQt/GraphicsWindowQt +++ b/include/osgQt/GraphicsWindowQt @@ -23,8 +23,10 @@ #include #include #include +#include class QInputEvent; +class QGestureEvent; namespace osgViewer { class ViewerBase; @@ -66,6 +68,9 @@ public: inline bool getForwardKeyEvents() const { return _forwardKeyEvents; } virtual void setForwardKeyEvents( bool f ) { _forwardKeyEvents = f; } + + inline bool getTouchEventsEnabled() const { return _touchEventsEnabled; } + void setTouchEventsEnabled( bool e ); void setKeyboardModifiers( QInputEvent* event ); @@ -76,6 +81,7 @@ public: virtual void mouseDoubleClickEvent( QMouseEvent* event ); virtual void mouseMoveEvent( QMouseEvent* event ); virtual void wheelEvent( QWheelEvent* event ); + virtual bool gestureEvent( QGestureEvent* event ); protected: @@ -109,6 +115,8 @@ protected: QQueue _deferredEventQueue; QSet _eventCompressor; + bool _touchEventsEnabled; + bool _forwardKeyEvents; qreal _devicePixelRatio; @@ -157,6 +165,9 @@ public: virtual std::string getWindowName(); virtual void useCursor( bool cursorOn ); virtual void setCursor( MouseCursor cursor ); + inline bool getTouchEventsEnabled() const { return _widget->getTouchEventsEnabled(); } + virtual void setTouchEventsEnabled( bool e ) { _widget->setTouchEventsEnabled(e); } + virtual bool valid() const; virtual bool realizeImplementation(); diff --git a/src/osgGA/GUIEventAdapter.cpp b/src/osgGA/GUIEventAdapter.cpp index 80c50d7fa..0f37689b3 100644 --- a/src/osgGA/GUIEventAdapter.cpp +++ b/src/osgGA/GUIEventAdapter.cpp @@ -132,3 +132,20 @@ void GUIEventAdapter::copyPointerDataFrom(const osgGA::GUIEventAdapter& sourceEv setMouseYOrientation(sourceEvent.getMouseYOrientation()); setPointerDataList(sourceEvent.getPointerDataList()); } + + + +void GUIEventAdapter::setMouseYOrientationAndUpdateCoords(osgGA::GUIEventAdapter::MouseYOrientation myo) +{ + if ( myo==_mouseYOrientation ) + return; + + setMouseYOrientation( myo ); + + _my = _Ymax - _my + _Ymin; + if( isMultiTouchEvent() ) + { + for( TouchData::iterator itr = getTouchData()->begin(); itr != getTouchData()->end(); itr++ ) + itr->y = _Ymax - itr->y + _Ymin; + } +} diff --git a/src/osgGA/MultiTouchTrackballManipulator.cpp b/src/osgGA/MultiTouchTrackballManipulator.cpp index 5d224008d..14e808536 100644 --- a/src/osgGA/MultiTouchTrackballManipulator.cpp +++ b/src/osgGA/MultiTouchTrackballManipulator.cpp @@ -37,29 +37,28 @@ MultiTouchTrackballManipulator::MultiTouchTrackballManipulator( const MultiTouch void MultiTouchTrackballManipulator::handleMultiTouchDrag(const GUIEventAdapter* now, const GUIEventAdapter* last, const double eventTimeDelta) { - osg::Vec2 pt_1_now(now->getTouchPointNormalizedX(0),now->getTouchPointNormalizedY(0)); - osg::Vec2 pt_2_now(now->getTouchPointNormalizedX(1),now->getTouchPointNormalizedY(1)); - osg::Vec2 pt_1_last(last->getTouchPointNormalizedX(0),last->getTouchPointNormalizedY(0)); - osg::Vec2 pt_2_last(last->getTouchPointNormalizedX(1),last->getTouchPointNormalizedY(1)); + const osg::Vec2 pt_1_now( now->getTouchData()->get(0).x, now->getTouchData()->get(0).y); + const osg::Vec2 pt_2_now( now->getTouchData()->get(1).x, now->getTouchData()->get(1).y); + const osg::Vec2 pt_1_last( last->getTouchData()->get(0).x, last->getTouchData()->get(0).y); + const osg::Vec2 pt_2_last( last->getTouchData()->get(1).x, last->getTouchData()->get(1).y); - - - float gap_now((pt_1_now - pt_2_now).length()); - float gap_last((pt_1_last - pt_2_last).length()); + const float gap_now((pt_1_now - pt_2_now).length()); + const float gap_last((pt_1_last - pt_2_last).length()); // osg::notify(osg::ALWAYS) << gap_now << " " << gap_last << std::endl; + const float relativeChange = (gap_last - gap_now)/gap_last; + // zoom gesture - if (fabs(gap_last - gap_now) > 0.02) - zoomModel( (gap_last - gap_now) , true ); + if (fabs(relativeChange) > 0.02) + zoomModel( relativeChange , true ); // drag gesture - osg::Vec2 delta = ((pt_1_last - pt_1_now) + (pt_2_last - pt_2_now)) / 2.0f; + const osg::Vec2 delta = ((pt_1_last - pt_1_now) + (pt_2_last - pt_2_now)) / 2.0f; - float scale = _distance / 3.0f; - - // osg::notify(osg::ALWAYS) << "drag: " << delta << " scale: " << scale << std::endl; + const float scale = -0.3f * _distance * getThrowScale( eventTimeDelta ); + //osg::notify(osg::ALWAYS) << "drag: " << delta << " scale: " << scale << std::endl; panModel( delta.x() * scale, delta.y() * scale); } diff --git a/src/osgQt/GraphicsWindowQt.cpp b/src/osgQt/GraphicsWindowQt.cpp index 55ba43a48..817f02e5b 100644 --- a/src/osgQt/GraphicsWindowQt.cpp +++ b/src/osgQt/GraphicsWindowQt.cpp @@ -11,12 +11,17 @@ * OpenSceneGraph Public License for more details. */ -#include - #include +#include #include #include +#if (QT_VERSION>=QT_VERSION_CHECK(4, 6, 0)) +# define USE_GESTURES +# include +# include +#endif + using namespace osgQt; @@ -136,7 +141,8 @@ static HeartBeat heartBeat; GLWidget::GLWidget( QWidget* parent, const QGLWidget* shareWidget, Qt::WindowFlags f, bool forwardKeyEvents ) : QGLWidget(parent, shareWidget, f), _gw( NULL ), -_forwardKeyEvents( forwardKeyEvents ) +_forwardKeyEvents( forwardKeyEvents ), +_touchEventsEnabled( false ) { _devicePixelRatio = GETDEVICEPIXELRATIO(); } @@ -145,7 +151,8 @@ GLWidget::GLWidget( QGLContext* context, QWidget* parent, const QGLWidget* share bool forwardKeyEvents ) : QGLWidget(context, parent, shareWidget, f), _gw( NULL ), -_forwardKeyEvents( forwardKeyEvents ) +_forwardKeyEvents( forwardKeyEvents ), +_touchEventsEnabled( false ) { _devicePixelRatio = GETDEVICEPIXELRATIO(); } @@ -154,7 +161,8 @@ GLWidget::GLWidget( const QGLFormat& format, QWidget* parent, const QGLWidget* s bool forwardKeyEvents ) : QGLWidget(format, parent, shareWidget, f), _gw( NULL ), -_forwardKeyEvents( forwardKeyEvents ) +_forwardKeyEvents( forwardKeyEvents ), +_touchEventsEnabled( false ) { _devicePixelRatio = GETDEVICEPIXELRATIO(); } @@ -170,6 +178,25 @@ GLWidget::~GLWidget() } } +void GLWidget::setTouchEventsEnabled(bool e) +{ +#ifdef USE_GESTURES + if (e==_touchEventsEnabled) + return; + + _touchEventsEnabled = e; + + if (_touchEventsEnabled) + { + grabGesture(Qt::PinchGesture); + } + else + { + ungrabGesture(Qt::PinchGesture); + } +#endif +} + void GLWidget::processDeferredEvents() { QQueue deferredEventQueueCopy; @@ -189,6 +216,10 @@ void GLWidget::processDeferredEvents() bool GLWidget::event( QEvent* event ) { +#ifdef USE_GESTURES + if ( event->type()==QEvent::Gesture ) + return gestureEvent(static_cast(event)); +#endif // QEvent::Hide // @@ -361,6 +392,85 @@ void GLWidget::wheelEvent( QWheelEvent* event ) (event->delta()>0 ? osgGA::GUIEventAdapter::SCROLL_LEFT : osgGA::GUIEventAdapter::SCROLL_RIGHT) ); } +#ifdef USE_GESTURES +static osgGA::GUIEventAdapter::TouchPhase translateQtGestureState( Qt::GestureState state ) +{ + osgGA::GUIEventAdapter::TouchPhase touchPhase; + switch ( state ) + { + case Qt::GestureStarted: + touchPhase = osgGA::GUIEventAdapter::TOUCH_BEGAN; + break; + case Qt::GestureUpdated: + touchPhase = osgGA::GUIEventAdapter::TOUCH_MOVED; + break; + case Qt::GestureFinished: + case Qt::GestureCanceled: + touchPhase = osgGA::GUIEventAdapter::TOUCH_ENDED; + break; + default: + touchPhase = osgGA::GUIEventAdapter::TOUCH_UNKNOWN; + }; + + return touchPhase; +} +#endif + + +bool GLWidget::gestureEvent( QGestureEvent* qevent ) +{ +#ifndef USE_GESTURES + return false; +#else + + bool accept = false; + + if ( QPinchGesture* pinch = static_cast(qevent->gesture(Qt::PinchGesture) ) ) + { + const QPointF qcenterf = pinch->centerPoint(); + const float angle = pinch->totalRotationAngle(); + const float scale = pinch->totalScaleFactor(); + + const QPoint pinchCenterQt = mapFromGlobal(qcenterf.toPoint()); + const osg::Vec2 pinchCenter( pinchCenterQt.x(), pinchCenterQt.y() ); + + //We don't have absolute positions of the two touches, only a scale and rotation + //Hence we create pseudo-coordinates which are reasonable, and centered around the + //real position + const float radius = (width()+height())/4; + const osg::Vec2 vector( scale*cos(angle)*radius, scale*sin(angle)*radius); + const osg::Vec2 p0 = pinchCenter+vector; + const osg::Vec2 p1 = pinchCenter-vector; + + osg::ref_ptr event = 0; + const osgGA::GUIEventAdapter::TouchPhase touchPhase = translateQtGestureState( pinch->state() ); + if ( touchPhase==osgGA::GUIEventAdapter::TOUCH_BEGAN ) + { + event = _gw->getEventQueue()->touchBegan(0 , touchPhase, p0[0], p0[1] ); + } + else if ( touchPhase==osgGA::GUIEventAdapter::TOUCH_MOVED ) + { + event = _gw->getEventQueue()->touchMoved( 0, touchPhase, p0[0], p0[1] ); + } + else + { + event = _gw->getEventQueue()->touchEnded( 0, touchPhase, p0[0], p0[1], 1 ); + } + + if ( event ) + { + event->addTouchPoint( 1, touchPhase, p1[0], p1[1] ); + accept = true; + } + } + + if ( accept ) + qevent->accept(); + + return accept; +#endif +} + GraphicsWindowQt::GraphicsWindowQt( osg::GraphicsContext::Traits* traits, QWidget* parent, const QGLWidget* shareWidget, Qt::WindowFlags f ) diff --git a/src/osgViewer/CompositeViewer.cpp b/src/osgViewer/CompositeViewer.cpp index 1afdcf9ed..5bb9eff22 100644 --- a/src/osgViewer/CompositeViewer.cpp +++ b/src/osgViewer/CompositeViewer.cpp @@ -1008,11 +1008,7 @@ void CompositeViewer::eventTraversal() event->setInputRange(pd->xMin, pd->yMin, pd->xMax, pd->yMax); event->setMouseYOrientation(osgGA::GUIEventAdapter::Y_INCREASING_UPWARDS); #else - if (event->getMouseYOrientation()!=osgGA::GUIEventAdapter::Y_INCREASING_UPWARDS) - { - event->setY((event->getYmax()-event->getY())+event->getYmin()); - event->setMouseYOrientation(osgGA::GUIEventAdapter::Y_INCREASING_UPWARDS); - } + event->setMouseYOrientationAndUpdateCoords(osgGA::GUIEventAdapter::Y_INCREASING_UPWARDS); #endif _previousEvent = event; diff --git a/src/osgViewer/Viewer.cpp b/src/osgViewer/Viewer.cpp index 3b86fcb51..3ab9f63b0 100644 --- a/src/osgViewer/Viewer.cpp +++ b/src/osgViewer/Viewer.cpp @@ -929,16 +929,7 @@ void Viewer::eventTraversal() event->setInputRange(pd->xMin, pd->yMin, pd->xMax, pd->yMax); event->setMouseYOrientation(osgGA::GUIEventAdapter::Y_INCREASING_UPWARDS); #else - if (event->getMouseYOrientation()!=osgGA::GUIEventAdapter::Y_INCREASING_UPWARDS) - { - event->setY((event->getYmax()-event->getY())+event->getYmin()); - event->setMouseYOrientation(osgGA::GUIEventAdapter::Y_INCREASING_UPWARDS); - if(event->isMultiTouchEvent()) { - for(osgGA::GUIEventAdapter::TouchData::iterator itr = event->getTouchData()->begin(); itr != event->getTouchData()->end(); itr++) { - itr->y = event->getYmax() - itr->y + event->getYmin(); - } - } - } + event->setMouseYOrientationAndUpdateCoords(osgGA::GUIEventAdapter::Y_INCREASING_UPWARDS); #endif eventState->copyPointerDataFrom(*event);