//====== Copyright Valve Corporation, All rights reserved. ======= #include "openvroverlaycontroller.h" #include #include #include #include #include #include #include #include #include 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&)), this, SLOT( OnSceneChanged(const QList&)) ); // 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& ) { // 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() ); } 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; }