| //====== Copyright Valve Corporation, All rights reserved. ======= |
| |
| |
| #include "openvroverlaycontroller.h" |
| |
| |
| #include <QOpenGLFramebufferObjectFormat> |
| #include <QOpenGLPaintDevice> |
| #include <QPainter> |
| #include <QtWidgets/QWidget> |
| #include <QMouseEvent> |
| #include <QtWidgets/QGraphicsSceneMouseEvent> |
| #include <QtWidgets/QApplication> |
| #include <QtWidgets/QGraphicsEllipseItem> |
| #include <QCursor> |
| |
| using namespace vr; |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: |
| //----------------------------------------------------------------------------- |
| COpenVROverlayController *s_pSharedVRController = NULL; |
| |
| COpenVROverlayController *COpenVROverlayController::SharedInstance() |
| { |
| if ( !s_pSharedVRController ) |
| { |
| s_pSharedVRController = new COpenVROverlayController(); |
| } |
| return s_pSharedVRController; |
| } |
| |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: |
| //----------------------------------------------------------------------------- |
| COpenVROverlayController::COpenVROverlayController() |
| : BaseClass() |
| , m_strVRDriver( "No Driver" ) |
| , m_strVRDisplay( "No Display" ) |
| , m_eLastHmdError( vr::VRInitError_None ) |
| , m_eCompositorError( vr::VRInitError_None ) |
| , m_eOverlayError( vr::VRInitError_None ) |
| , m_ulOverlayHandle( vr::k_ulOverlayHandleInvalid ) |
| , m_pOpenGLContext( NULL ) |
| , m_pScene( NULL ) |
| , m_pFbo( NULL ) |
| , m_pOffscreenSurface ( NULL ) |
| , m_pPumpEventsTimer( NULL ) |
| , m_pWidget( NULL ) |
| , m_lastMouseButtons( 0 ) |
| { |
| } |
| |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: |
| //----------------------------------------------------------------------------- |
| COpenVROverlayController::~COpenVROverlayController() |
| { |
| } |
| |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: Helper to get a string from a tracked device property and turn it |
| // into a QString |
| //----------------------------------------------------------------------------- |
| QString GetTrackedDeviceString( vr::IVRSystem *pHmd, vr::TrackedDeviceIndex_t unDevice, vr::TrackedDeviceProperty prop ) |
| { |
| char buf[128]; |
| vr::TrackedPropertyError err; |
| pHmd->GetStringTrackedDeviceProperty( unDevice, prop, buf, sizeof( buf ), &err ); |
| if( err != vr::TrackedProp_Success ) |
| { |
| return QString( "Error Getting String: " ) + pHmd->GetPropErrorNameFromEnum( err ); |
| } |
| else |
| { |
| return buf; |
| } |
| } |
| |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: |
| //----------------------------------------------------------------------------- |
| bool COpenVROverlayController::Init() |
| { |
| bool bSuccess = true; |
| |
| m_strName = "systemoverlay"; |
| |
| QStringList arguments = qApp->arguments(); |
| |
| int nNameArg = arguments.indexOf( "-name" ); |
| if( nNameArg != -1 && nNameArg + 2 <= arguments.size() ) |
| { |
| m_strName = arguments.at( nNameArg + 1 ); |
| } |
| |
| QSurfaceFormat format; |
| format.setMajorVersion( 4 ); |
| format.setMinorVersion( 1 ); |
| format.setProfile( QSurfaceFormat::CompatibilityProfile ); |
| |
| m_pOpenGLContext = new QOpenGLContext(); |
| m_pOpenGLContext->setFormat( format ); |
| bSuccess = m_pOpenGLContext->create(); |
| if( !bSuccess ) |
| return false; |
| |
| // create an offscreen surface to attach the context and FBO to |
| m_pOffscreenSurface = new QOffscreenSurface(); |
| m_pOffscreenSurface->create(); |
| m_pOpenGLContext->makeCurrent( m_pOffscreenSurface ); |
| |
| m_pScene = new QGraphicsScene(); |
| connect( m_pScene, SIGNAL(changed(const QList<QRectF>&)), this, SLOT( OnSceneChanged(const QList<QRectF>&)) ); |
| |
| // Loading the OpenVR Runtime |
| bSuccess = ConnectToVRRuntime(); |
| |
| bSuccess = bSuccess && vr::VRCompositor() != NULL; |
| |
| if( vr::VROverlay() ) |
| { |
| std::string sKey = std::string( "sample." ) + m_strName.toStdString(); |
| vr::VROverlayError overlayError = vr::VROverlay()->CreateDashboardOverlay( sKey.c_str(), m_strName.toStdString().c_str(), &m_ulOverlayHandle, &m_ulOverlayThumbnailHandle ); |
| bSuccess = bSuccess && overlayError == vr::VROverlayError_None; |
| } |
| |
| if( bSuccess ) |
| { |
| vr::VROverlay()->SetOverlayWidthInMeters( m_ulOverlayHandle, 1.5f ); |
| vr::VROverlay()->SetOverlayInputMethod( m_ulOverlayHandle, vr::VROverlayInputMethod_Mouse ); |
| |
| m_pPumpEventsTimer = new QTimer( this ); |
| connect(m_pPumpEventsTimer, SIGNAL( timeout() ), this, SLOT( OnTimeoutPumpEvents() ) ); |
| m_pPumpEventsTimer->setInterval( 20 ); |
| m_pPumpEventsTimer->start(); |
| |
| } |
| return true; |
| } |
| |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: |
| //----------------------------------------------------------------------------- |
| void COpenVROverlayController::Shutdown() |
| { |
| DisconnectFromVRRuntime(); |
| |
| delete m_pScene; |
| delete m_pFbo; |
| delete m_pOffscreenSurface; |
| |
| if( m_pOpenGLContext ) |
| { |
| // m_pOpenGLContext->destroy(); |
| delete m_pOpenGLContext; |
| m_pOpenGLContext = NULL; |
| } |
| } |
| |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: |
| //----------------------------------------------------------------------------- |
| void COpenVROverlayController::OnSceneChanged( const QList<QRectF>& ) |
| { |
| // skip rendering if the overlay isn't visible |
| if( ( m_ulOverlayHandle == k_ulOverlayHandleInvalid ) || !vr::VROverlay() || |
| ( !vr::VROverlay()->IsOverlayVisible( m_ulOverlayHandle ) && !vr::VROverlay()->IsOverlayVisible( m_ulOverlayThumbnailHandle ) ) ) |
| return; |
| |
| m_pOpenGLContext->makeCurrent( m_pOffscreenSurface ); |
| m_pFbo->bind(); |
| |
| QOpenGLPaintDevice device( m_pFbo->size() ); |
| QPainter painter( &device ); |
| |
| m_pScene->render( &painter ); |
| |
| m_pFbo->release(); |
| |
| GLuint unTexture = m_pFbo->texture(); |
| if( unTexture != 0 ) |
| { |
| vr::Texture_t texture = {(void*)(uintptr_t)unTexture, vr::TextureType_OpenGL, vr::ColorSpace_Auto }; |
| vr::VROverlay()->SetOverlayTexture( m_ulOverlayHandle, &texture ); |
| } |
| } |
| |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: |
| //----------------------------------------------------------------------------- |
| void COpenVROverlayController::OnTimeoutPumpEvents() |
| { |
| if( !vr::VRSystem() ) |
| return; |
| |
| vr::VREvent_t vrEvent; |
| while( vr::VROverlay()->PollNextOverlayEvent( m_ulOverlayHandle, &vrEvent, sizeof( vrEvent ) ) ) |
| { |
| switch( vrEvent.eventType ) |
| { |
| case vr::VREvent_MouseMove: |
| { |
| QPointF ptNewMouse( vrEvent.data.mouse.x, vrEvent.data.mouse.y ); |
| QPoint ptGlobal = ptNewMouse.toPoint(); |
| QGraphicsSceneMouseEvent mouseEvent( QEvent::GraphicsSceneMouseMove ); |
| mouseEvent.setWidget( NULL ); |
| mouseEvent.setPos( ptNewMouse ); |
| mouseEvent.setScenePos( ptGlobal ); |
| mouseEvent.setScreenPos( ptGlobal ); |
| mouseEvent.setLastPos( m_ptLastMouse ); |
| mouseEvent.setLastScenePos( m_pWidget->mapToGlobal( m_ptLastMouse.toPoint() ) ); |
| mouseEvent.setLastScreenPos( m_pWidget->mapToGlobal( m_ptLastMouse.toPoint() ) ); |
| mouseEvent.setButtons( m_lastMouseButtons ); |
| mouseEvent.setButton( Qt::NoButton ); |
| mouseEvent.setModifiers( 0 ); |
| mouseEvent.setAccepted( false ); |
| |
| m_ptLastMouse = ptNewMouse; |
| QApplication::sendEvent( m_pScene, &mouseEvent ); |
| |
| OnSceneChanged( QList<QRectF>() ); |
| } |
| break; |
| |
| case vr::VREvent_MouseButtonDown: |
| { |
| Qt::MouseButton button = vrEvent.data.mouse.button == vr::VRMouseButton_Right ? Qt::RightButton : Qt::LeftButton; |
| |
| m_lastMouseButtons |= button; |
| |
| QPoint ptGlobal = m_ptLastMouse.toPoint(); |
| QGraphicsSceneMouseEvent mouseEvent( QEvent::GraphicsSceneMousePress ); |
| mouseEvent.setWidget( NULL ); |
| mouseEvent.setPos( m_ptLastMouse ); |
| mouseEvent.setButtonDownPos( button, m_ptLastMouse ); |
| mouseEvent.setButtonDownScenePos( button, ptGlobal); |
| mouseEvent.setButtonDownScreenPos( button, ptGlobal ); |
| mouseEvent.setScenePos( ptGlobal ); |
| mouseEvent.setScreenPos( ptGlobal ); |
| mouseEvent.setLastPos( m_ptLastMouse ); |
| mouseEvent.setLastScenePos( ptGlobal ); |
| mouseEvent.setLastScreenPos( ptGlobal ); |
| mouseEvent.setButtons( m_lastMouseButtons ); |
| mouseEvent.setButton( button ); |
| mouseEvent.setModifiers( 0 ); |
| mouseEvent.setAccepted( false ); |
| |
| QApplication::sendEvent( m_pScene, &mouseEvent ); |
| } |
| break; |
| |
| case vr::VREvent_MouseButtonUp: |
| { |
| Qt::MouseButton button = vrEvent.data.mouse.button == vr::VRMouseButton_Right ? Qt::RightButton : Qt::LeftButton; |
| m_lastMouseButtons &= ~button; |
| |
| QPoint ptGlobal = m_ptLastMouse.toPoint(); |
| QGraphicsSceneMouseEvent mouseEvent( QEvent::GraphicsSceneMouseRelease ); |
| mouseEvent.setWidget( NULL ); |
| mouseEvent.setPos( m_ptLastMouse ); |
| mouseEvent.setScenePos( ptGlobal ); |
| mouseEvent.setScreenPos( ptGlobal ); |
| mouseEvent.setLastPos( m_ptLastMouse ); |
| mouseEvent.setLastScenePos( ptGlobal ); |
| mouseEvent.setLastScreenPos( ptGlobal ); |
| mouseEvent.setButtons( m_lastMouseButtons ); |
| mouseEvent.setButton( button ); |
| mouseEvent.setModifiers( 0 ); |
| mouseEvent.setAccepted( false ); |
| |
| QApplication::sendEvent( m_pScene, &mouseEvent ); |
| } |
| break; |
| |
| case vr::VREvent_OverlayShown: |
| { |
| m_pWidget->repaint(); |
| } |
| break; |
| |
| case vr::VREvent_Quit: |
| QApplication::exit(); |
| break; |
| } |
| } |
| |
| if( m_ulOverlayThumbnailHandle != vr::k_ulOverlayHandleInvalid ) |
| { |
| while( vr::VROverlay()->PollNextOverlayEvent( m_ulOverlayThumbnailHandle, &vrEvent, sizeof( vrEvent) ) ) |
| { |
| switch( vrEvent.eventType ) |
| { |
| case vr::VREvent_OverlayShown: |
| { |
| m_pWidget->repaint(); |
| } |
| break; |
| } |
| } |
| } |
| |
| } |
| |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: |
| //----------------------------------------------------------------------------- |
| void COpenVROverlayController::SetWidget( QWidget *pWidget ) |
| { |
| if( m_pScene ) |
| { |
| // all of the mouse handling stuff requires that the widget be at 0,0 |
| pWidget->move( 0, 0 ); |
| m_pScene->addWidget( pWidget ); |
| } |
| m_pWidget = pWidget; |
| |
| m_pFbo = new QOpenGLFramebufferObject( pWidget->width(), pWidget->height(), GL_TEXTURE_2D ); |
| |
| if( vr::VROverlay() ) |
| { |
| vr::HmdVector2_t vecWindowSize = |
| { |
| (float)pWidget->width(), |
| (float)pWidget->height() |
| }; |
| vr::VROverlay()->SetOverlayMouseScale( m_ulOverlayHandle, &vecWindowSize ); |
| } |
| |
| } |
| |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: |
| //----------------------------------------------------------------------------- |
| bool COpenVROverlayController::ConnectToVRRuntime() |
| { |
| m_eLastHmdError = vr::VRInitError_None; |
| vr::IVRSystem *pVRSystem = vr::VR_Init( &m_eLastHmdError, vr::VRApplication_Overlay ); |
| |
| if ( m_eLastHmdError != vr::VRInitError_None ) |
| { |
| m_strVRDriver = "No Driver"; |
| m_strVRDisplay = "No Display"; |
| return false; |
| } |
| |
| m_strVRDriver = GetTrackedDeviceString(pVRSystem, vr::k_unTrackedDeviceIndex_Hmd, vr::Prop_TrackingSystemName_String); |
| m_strVRDisplay = GetTrackedDeviceString(pVRSystem, vr::k_unTrackedDeviceIndex_Hmd, vr::Prop_SerialNumber_String); |
| |
| return true; |
| } |
| |
| |
| void COpenVROverlayController::DisconnectFromVRRuntime() |
| { |
| vr::VR_Shutdown(); |
| } |
| |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: |
| //----------------------------------------------------------------------------- |
| QString COpenVROverlayController::GetVRDriverString() |
| { |
| return m_strVRDriver; |
| } |
| |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: |
| //----------------------------------------------------------------------------- |
| QString COpenVROverlayController::GetVRDisplayString() |
| { |
| return m_strVRDisplay; |
| } |
| |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: |
| //----------------------------------------------------------------------------- |
| bool COpenVROverlayController::BHMDAvailable() |
| { |
| return vr::VRSystem() != NULL; |
| } |
| |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: |
| //----------------------------------------------------------------------------- |
| |
| vr::HmdError COpenVROverlayController::GetLastHmdError() |
| { |
| return m_eLastHmdError; |
| } |
| |
| |