You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
qml-vr-test/openvroverlaycontroller.cpp

406 lines
12 KiB

//====== 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;
}