summaryrefslogtreecommitdiff
path: root/sourcevr
diff options
context:
space:
mode:
Diffstat (limited to 'sourcevr')
-rw-r--r--sourcevr/cbase.h12
-rw-r--r--sourcevr/sourcevirtualreality.cpp857
-rw-r--r--sourcevr/sourcevirtualreality.h134
-rw-r--r--sourcevr/sourcevr.vpc72
-rw-r--r--sourcevr/stdafx.cpp2
5 files changed, 1077 insertions, 0 deletions
diff --git a/sourcevr/cbase.h b/sourcevr/cbase.h
new file mode 100644
index 0000000..ecf83a4
--- /dev/null
+++ b/sourcevr/cbase.h
@@ -0,0 +1,12 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+
+#include "platform.h"
+
+#include <vector>
+#include <math.h>
+#include <assert.h>
+#include <float.h>
+
+// Prevent OpenCV and other Vortex modules from using D3D (they will want D3D11, and we're a D3D9 game)
+#define NO_D3D
+
diff --git a/sourcevr/sourcevirtualreality.cpp b/sourcevr/sourcevirtualreality.cpp
new file mode 100644
index 0000000..de7180c
--- /dev/null
+++ b/sourcevr/sourcevirtualreality.cpp
@@ -0,0 +1,857 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//===========================================================================//
+#include "cbase.h"
+
+#include "sourcevirtualreality.h"
+#include "icommandline.h"
+#include "filesystem.h"
+#include "materialsystem/imaterial.h"
+#include "materialsystem/imesh.h"
+#include "materialsystem/imaterialvar.h"
+#include "renderparm.h"
+#include "openvr/openvr.h"
+
+using namespace vr;
+
+CSourceVirtualReality g_SourceVirtualReality;
+EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CSourceVirtualReality, ISourceVirtualReality,
+ SOURCE_VIRTUAL_REALITY_INTERFACE_VERSION, g_SourceVirtualReality );
+
+static VMatrix VMatrixFrom44(const float v[4][4]);
+static VMatrix VMatrixFrom34(const float v[3][4]);
+static VMatrix OpenVRToSourceCoordinateSystem(const VMatrix& vortex);
+
+// --------------------------------------------------------------------
+// Purpose: Set the current HMD pose as the zero pose
+// --------------------------------------------------------------------
+void CC_VR_Reset_Home_Pos( const CCommand& args )
+{
+ g_SourceVirtualReality.AcquireNewZeroPose();
+}
+static ConCommand vr_reset_home_pos("vr_reset_home_pos", CC_VR_Reset_Home_Pos, "Sets the current HMD position as the zero point" );
+
+
+// --------------------------------------------------------------------
+// Purpose: Reinitialize the IHeadtrack object
+// --------------------------------------------------------------------
+void CC_VR_Track_Reinit( const CCommand& args )
+{
+ if( g_SourceVirtualReality.ResetTracking() )
+ {
+ // Tracker can't be restarted: show a message, but don't quit.
+ Warning("Can't reset HMD tracker");
+ }
+}
+static ConCommand vr_track_reinit("vr_track_reinit", CC_VR_Track_Reinit, "Reinitializes HMD tracking" );
+
+
+// Disable distortion processing altogether.
+ConVar vr_distortion_enable ( "vr_distortion_enable", "1" );
+
+// Disable distortion by changing the distortion texture itself, so that the rendering path is otherwise identical.
+// This won't take effect until the texture is refresed.
+ConVar vr_debug_nodistortion ( "vr_debug_nodistortion", "0" );
+
+// Disable just the chromatic aberration correction in the distortion texture, to make undistort quality/artifacts
+// easier to see and debug. As above, won't take effect until the texture is refreshed.
+ConVar vr_debug_nochromatic ( "vr_debug_nochromatic", "0" );
+
+// Resolution of the undistort map.
+static const int distortionTextureSize = 128;
+
+
+void CC_vr_refresh_distortion_texture( const CCommand& args )
+{
+ g_SourceVirtualReality.RefreshDistortionTexture();
+}
+ConCommand vr_refresh_distortion_texture( "vr_refresh_distortion_texture", CC_vr_refresh_distortion_texture );
+
+ConVar vr_use_offscreen_render_target( "vr_use_offscreen_render_target", "0", 0, "Experimental: Use larger offscreen render target for pre-distorted scene in VR" );
+
+// --------------------------------------------------------------------
+// construction/destruction
+// --------------------------------------------------------------------
+CSourceVirtualReality::CSourceVirtualReality()
+ : m_textureGeneratorLeft( vr::Eye_Left ),
+ m_textureGeneratorRight( vr::Eye_Right )
+{
+ m_bActive = false;
+ m_bUsingOffscreenRenderTarget = false;
+ m_pHmd = NULL;
+}
+
+CSourceVirtualReality::~CSourceVirtualReality()
+{
+}
+
+
+// --------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------
+bool CSourceVirtualReality::Connect( CreateInterfaceFn factory )
+{
+ if ( !factory )
+ return false;
+
+ if ( !BaseClass::Connect( factory ) )
+ return false;
+
+ if ( !g_pFullFileSystem )
+ {
+ Warning( "The head tracker requires the filesystem to run!\n" );
+ return false;
+ }
+
+ return true;
+}
+
+
+// --------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------
+void CSourceVirtualReality::Disconnect()
+{
+ BaseClass::Disconnect();
+}
+
+
+// --------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------
+void * CSourceVirtualReality::QueryInterface( const char *pInterfaceName )
+{
+ CreateInterfaceFn factory = Sys_GetFactoryThis(); // This silly construction is necessary
+ return factory( pInterfaceName, NULL ); // to prevent the LTCG compiler from crashing.
+}
+
+
+// --------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------
+InitReturnVal_t CSourceVirtualReality::Init()
+{
+ InitReturnVal_t nRetVal = BaseClass::Init();
+ if ( nRetVal != INIT_OK )
+ return nRetVal;
+
+ MathLib_Init( 2.2f, 2.2f, 0.0f, 2.0f );
+
+ // if our tracker expects to use the texture base distortion shader,
+ // make the procedural textures for that shader now
+ m_pDistortionTextureLeft.Init( materials->CreateProceduralTexture( "vr_distort_map_left", TEXTURE_GROUP_PIXEL_SHADERS,
+ distortionTextureSize, distortionTextureSize, IMAGE_FORMAT_RGBA16161616,
+ TEXTUREFLAGS_NOMIP | TEXTUREFLAGS_NOLOD | TEXTUREFLAGS_NODEBUGOVERRIDE |
+ TEXTUREFLAGS_SINGLECOPY | TEXTUREFLAGS_CLAMPS | TEXTUREFLAGS_CLAMPT ) );
+ m_pDistortionTextureRight.Init( materials->CreateProceduralTexture( "vr_distort_map_right", TEXTURE_GROUP_PIXEL_SHADERS,
+ distortionTextureSize, distortionTextureSize, IMAGE_FORMAT_RGBA16161616,
+ TEXTUREFLAGS_NOMIP | TEXTUREFLAGS_NOLOD | TEXTUREFLAGS_NODEBUGOVERRIDE |
+ TEXTUREFLAGS_SINGLECOPY | TEXTUREFLAGS_CLAMPS | TEXTUREFLAGS_CLAMPT ) );
+ m_pDistortionTextureLeft->SetTextureRegenerator( &m_textureGeneratorLeft );
+ m_pDistortionTextureRight->SetTextureRegenerator( &m_textureGeneratorRight );
+
+ return INIT_OK;
+}
+
+void CSourceVirtualReality::RefreshDistortionTexture()
+{
+ m_pDistortionTextureLeft->Download();
+ m_pDistortionTextureRight->Download();
+}
+
+
+void CDistortionTextureRegen::RegenerateTextureBits( ITexture *pTexture, IVTFTexture *pVTFTexture, Rect_t *pSubRect )
+{
+ // only do this if we have an HMD
+ if( !g_SourceVirtualReality.GetHmd() )
+ return;
+
+ unsigned short *imageData = (unsigned short*) pVTFTexture->ImageData( 0, 0, 0 );
+ enum ImageFormat imageFormat = pVTFTexture->Format();
+ if( imageFormat != IMAGE_FORMAT_RGBA16161616 )
+ {
+ return;
+ }
+
+
+ // we use different UVs for the full FB source texture
+ float fUScale;
+ float fUOffset;
+ if( g_SourceVirtualReality.UsingOffscreenRenderTarget() )
+ {
+ fUScale = 1.f;
+ fUOffset = 0.f;
+ }
+ else
+ {
+ fUScale = 0.5f;
+ fUOffset = m_eEye == Eye_Left ? 0.f : 0.5f;
+ }
+
+ // optimize
+ int width = pVTFTexture->Width();
+ int height = pVTFTexture->Height();
+ float fHeight = height;
+ float fWidth = width;
+ int x, y;
+ for( y = 0; y < height; y++ )
+ {
+ for( x = 0; x < width; x++ )
+ {
+ int offset = 4 * ( x + y * width );
+ assert( offset < width * height * 4 );
+
+ float u = ( (float)x + 0.5f) / fWidth;
+ float v = ( (float)y + 0.5f) / fHeight;
+
+ DistortionCoordinates_t coords = g_SourceVirtualReality.GetHmd()->ComputeDistortion( m_eEye, u, v );
+
+ coords.rfRed[0] = Clamp( coords.rfRed[0], 0.f, 1.f ) * fUScale + fUOffset;
+ coords.rfGreen[0] = Clamp( coords.rfGreen[0], 0.f, 1.f ) * fUScale + fUOffset;
+ coords.rfBlue[0] = Clamp( coords.rfBlue[0], 0.f, 1.f ) * fUScale + fUOffset;
+
+ if ( vr_debug_nodistortion.GetBool() )
+ {
+ coords.rfRed[0] = coords.rfGreen[0] = coords.rfBlue[0] = u * fUScale + fUOffset;
+ coords.rfRed[1] = coords.rfGreen[1] = coords.rfBlue[1] = v;
+ }
+
+ if ( vr_debug_nochromatic.GetBool() )
+ {
+ coords.rfRed[0] = coords.rfBlue[0] = coords.rfGreen[0];
+ coords.rfRed[1] = coords.rfBlue[1] = coords.rfGreen[1];
+ }
+
+ imageData[offset + 0] = (unsigned short)(Clamp( coords.rfRed[0], 0.f, 1.f ) * 65535.f );
+ imageData[offset + 1] = (unsigned short)(Clamp( coords.rfRed[1], 0.f, 1.f ) * 65535.f );
+ imageData[offset + 2] = (unsigned short)(Clamp( coords.rfBlue[0], 0.f, 1.f ) * 65535.f );
+ imageData[offset + 3] = (unsigned short)(Clamp( coords.rfBlue[1], 0.f, 1.f ) * 65535.f );
+ }
+ }
+}
+
+
+// --------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------
+void CSourceVirtualReality::Shutdown()
+{
+ BaseClass::Shutdown();
+
+ if( m_pHmd )
+ VR_Shutdown();
+
+ m_pDistortionTextureLeft.Shutdown();
+ m_pDistortionTextureRight.Shutdown();
+}
+
+
+// --------------------------------------------------------------------
+// Purpose: Let the caller know if we're in VR mode
+// --------------------------------------------------------------------
+bool CSourceVirtualReality::ShouldRunInVR()
+{
+ return m_bActive && m_pHmd;
+}
+
+
+// --------------------------------------------------------------------
+// Purpose: Returns true if there's an Hmd connected and everything
+// started up.
+// --------------------------------------------------------------------
+bool CSourceVirtualReality::IsHmdConnected()
+{
+ // we really just care if OpenVR init was successful
+ return EnsureOpenVRInited();
+}
+
+
+// --------------------------------------------------------------------
+// Purpose: Let the caller know how big to make the window and where
+// to put it.
+// --------------------------------------------------------------------
+bool CSourceVirtualReality::GetDisplayBounds( VRRect_t *pRect )
+{
+ if( m_pHmd )
+ {
+ int32_t x, y;
+ uint32_t width, height;
+ m_pHmd->GetWindowBounds( &x, &y, &width, &height );
+ pRect->nX = x;
+ pRect->nY = y;
+ pRect->nWidth = width;
+ pRect->nHeight = height;
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+// --------------------------------------------------------------------
+// Purpose: Allocates the pre-distortion render targets.
+// --------------------------------------------------------------------
+void CSourceVirtualReality::CreateRenderTargets( IMaterialSystem *pMaterialSystem )
+{
+ if( !m_pHmd || !m_bActive )
+ return;
+
+ g_StereoGuiTexture.Init( materials->CreateNamedRenderTargetTextureEx2(
+ "_rt_gui",
+ 640, 480, RT_SIZE_OFFSCREEN,
+ materials->GetBackBufferFormat(),
+ MATERIAL_RT_DEPTH_SHARED,
+ TEXTUREFLAGS_CLAMPS | TEXTUREFLAGS_CLAMPT,
+ CREATERENDERTARGETFLAGS_HDR )
+ );
+
+ if( UsingOffscreenRenderTarget() )
+ {
+ uint32_t nWidth, nHeight;
+ m_pHmd->GetRecommendedRenderTargetSize( &nWidth, &nHeight );
+
+ m_pPredistortRT.Init( pMaterialSystem->CreateNamedRenderTargetTextureEx2(
+ "_rt_vr_predistort",
+ nWidth, nHeight, RT_SIZE_LITERAL,
+ IMAGE_FORMAT_RGBA8888,
+ MATERIAL_RT_DEPTH_SEPARATE,
+ TEXTUREFLAGS_RENDERTARGET |TEXTUREFLAGS_NOMIP/*TEXTUREFLAGS_CLAMPS | TEXTUREFLAGS_CLAMPT */,
+ 0 ) );
+
+ //TODO: Figure out what I really want for the depth texture format
+ m_pPredistortRTDepth.Init( pMaterialSystem->CreateNamedRenderTargetTextureEx2( "_rt_vr_predistort_depth", nWidth, nHeight,
+ RT_SIZE_LITERAL, IMAGE_FORMAT_NV_DST24, MATERIAL_RT_DEPTH_NONE,
+ TEXTUREFLAGS_CLAMPS | TEXTUREFLAGS_CLAMPT |TEXTUREFLAGS_NOMIP,
+ 0 ) );
+ }
+
+}
+
+void CSourceVirtualReality::ShutdownRenderTargets()
+{
+ g_StereoGuiTexture.Shutdown();
+ m_pPredistortRT.Shutdown();
+ m_pPredistortRTDepth.Shutdown();
+}
+
+
+// Returns the (possibly overridden) framebuffer size for render target sizing.
+void CSourceVirtualReality::GetRenderTargetFrameBufferDimensions( int & nWidth, int & nHeight )
+{
+ if( m_pHmd && UsingOffscreenRenderTarget() )
+ {
+
+ uint32_t w, h;
+ m_pHmd->GetRecommendedRenderTargetSize( &w, &h );
+ nWidth = w;
+ nHeight = h;
+ }
+ else
+ {
+ // this will cause material system to fall back to the
+ // actual size of the frame buffer
+ nWidth = nHeight = 0;
+ }
+}
+
+
+// --------------------------------------------------------------------
+// Purpose: fetches the render target for the specified eye
+// --------------------------------------------------------------------
+ITexture *CSourceVirtualReality::GetRenderTarget( ISourceVirtualReality::VREye eEye, ISourceVirtualReality::EWhichRenderTarget eWhich )
+{
+ // we don't use any render targets if distortion is disabled
+ // Just let the game render to the frame buffer.
+ if( !vr_distortion_enable.GetBool() )
+ return NULL;
+
+ if( !m_bActive || !m_pHmd )
+ return NULL;
+
+ if( !UsingOffscreenRenderTarget() )
+ return NULL;
+
+ switch( eWhich )
+ {
+ case ISourceVirtualReality::RT_Color:
+ return m_pPredistortRT;
+
+ case ISourceVirtualReality::RT_Depth:
+ return m_pPredistortRTDepth;
+ }
+ return NULL;
+}
+
+vr::Hmd_Eye SourceEyeToHmdEye( ISourceVirtualReality::VREye eEye )
+{
+ if( eEye == ISourceVirtualReality::VREye_Left )
+ return vr::Eye_Left;
+ else
+ return vr::Eye_Right;
+}
+
+
+// --------------------------------------------------------------------
+// Purpose: Let the caller know if we're in VR mode
+// --------------------------------------------------------------------
+void CSourceVirtualReality::GetViewportBounds( VREye eEye, int *pnX, int *pnY, int *pnWidth, int *pnHeight )
+{
+ if( !m_pHmd || !m_bActive )
+ {
+ *pnWidth = 0;
+ *pnHeight = 0;
+ return;
+ }
+
+ // if there are textures, use those
+ if( m_pPredistortRT && vr_distortion_enable.GetBool() )
+ {
+ if( pnX && pnY )
+ {
+ *pnX = 0;
+ *pnY = 0;
+ }
+ *pnWidth = m_pPredistortRT->GetActualWidth();
+ *pnHeight = m_pPredistortRT->GetActualHeight();
+ }
+ else
+ {
+ uint32_t x, y, w, h;
+ m_pHmd->GetEyeOutputViewport( SourceEyeToHmdEye( eEye ), &x, &y, &w, &h );
+ if( pnX && pnY )
+ {
+ *pnX = x;
+ *pnY = y;
+ }
+ *pnWidth = w;
+ *pnHeight = h;
+ }
+}
+
+
+// --------------------------------------------------------------------
+// Purpose: Returns the current pose
+// --------------------------------------------------------------------
+VMatrix CSourceVirtualReality::GetMideyePose()
+{
+ return m_ZeroFromHeadPose;
+}
+
+// ----------------------------------------------------------------------
+// Purpose: Create a 4x4 projection transform from eye projection and distortion parameters
+// ----------------------------------------------------------------------
+inline static void ComposeProjectionTransform(float fLeft, float fRight, float fTop, float fBottom, float zNear, float zFar, float fovScale, VMatrix *pmProj )
+{
+ if( fovScale != 1.0f && fovScale > 0.f )
+ {
+ float fFovScaleAdjusted = tan( atan( fTop ) / fovScale ) / fTop;
+ fRight *= fFovScaleAdjusted;
+ fLeft *= fFovScaleAdjusted;
+ fTop *= fFovScaleAdjusted;
+ fBottom *= fFovScaleAdjusted;
+ }
+
+ float idx = 1.0f / (fRight - fLeft);
+ float idy = 1.0f / (fBottom - fTop);
+ float idz = 1.0f / (zFar - zNear);
+ float sx = fRight + fLeft;
+ float sy = fBottom + fTop;
+
+ float (*p)[4] = pmProj->m;
+ p[0][0] = 2*idx; p[0][1] = 0; p[0][2] = sx*idx; p[0][3] = 0;
+ p[1][0] = 0; p[1][1] = 2*idy; p[1][2] = sy*idy; p[1][3] = 0;
+ p[2][0] = 0; p[2][1] = 0; p[2][2] = -zFar*idz; p[2][3] = -zFar*zNear*idz;
+ p[3][0] = 0; p[3][1] = 0; p[3][2] = -1.0f; p[3][3] = 0;
+}
+
+
+// ----------------------------------------------------------------------
+// Purpose: Computes and returns the projection matrix for the eye
+// ----------------------------------------------------------------------
+bool CSourceVirtualReality::GetEyeProjectionMatrix ( VMatrix *pResult, VREye eEye, float zNear, float zFar, float fovScale )
+{
+ Assert ( pResult != NULL );
+ if( !pResult || !m_pHmd || !m_bActive )
+ return false;
+
+ float fLeft, fRight, fTop, fBottom;
+ m_pHmd->GetProjectionRaw( SourceEyeToHmdEye( eEye ), &fLeft, &fRight, &fTop, &fBottom );
+
+ ComposeProjectionTransform( fLeft, fRight, fTop, fBottom, zNear, zFar, fovScale, pResult );
+ return true;
+}
+
+
+// ----------------------------------------------------------------------
+// Purpose: Returns the mid eye from left/right eye part of the view
+// matrix transform chain.
+// ----------------------------------------------------------------------
+VMatrix CSourceVirtualReality::GetMidEyeFromEye( VREye eEye )
+{
+ if( m_pHmd )
+ {
+ vr::HmdMatrix34_t matMidEyeFromEye = m_pHmd->GetEyeToHeadTransform( SourceEyeToHmdEye( eEye ) );
+ return OpenVRToSourceCoordinateSystem( VMatrixFrom34( matMidEyeFromEye.m ) );
+ }
+ else
+ {
+ VMatrix mat;
+ mat.Identity();
+ return mat;
+ }
+}
+
+// returns the adapter index to use for VR mode
+int CSourceVirtualReality::GetVRModeAdapter()
+{
+ if( EnsureOpenVRInited() )
+ {
+ Assert( m_pHmd );
+ return m_pHmd->GetD3D9AdapterIndex();
+ }
+ else
+ {
+ return -1;
+ }
+}
+
+bool CSourceVirtualReality::WillDriftInYaw()
+{
+ if( m_pHmd )
+ return m_pHmd->GetBoolTrackedDeviceProperty( vr::k_unTrackedDeviceIndex_Hmd, Prop_WillDriftInYaw_Bool );
+ else
+ return false;
+}
+
+void CSourceVirtualReality::AcquireNewZeroPose()
+{
+ // just let the next tracker update re-zero us
+ if( m_pHmd )
+ m_pHmd->ResetSeatedZeroPose();
+}
+
+bool CSourceVirtualReality::SampleTrackingState ( float PlayerGameFov, float fPredictionSeconds )
+{
+ if( !m_pHmd || !m_bActive )
+ return false;
+
+ // If tracker can't return a pose (it's possibly recalibrating itself)
+ // then we will freeze tracking at its current state, rather than
+ // snapping it back to the zero position
+ vr::TrackedDevicePose_t pose;
+
+ if ( m_pHmd->IsTrackedDeviceConnected( k_unTrackedDeviceIndex_Hmd ) )
+ {
+ float fSecondsSinceLastVsync;
+ m_pHmd->GetTimeSinceLastVsync( &fSecondsSinceLastVsync, NULL );
+
+ float fFrameDuration = 1.f / m_pHmd->GetFloatTrackedDeviceProperty( vr::k_unTrackedDeviceIndex_Hmd,
+ vr::Prop_DisplayFrequency_Float );
+ float fPredictedSecondsFromNow = fFrameDuration - fSecondsSinceLastVsync \
+ + m_pHmd->GetFloatTrackedDeviceProperty( vr::k_unTrackedDeviceIndex_Hmd,
+ vr::Prop_SecondsFromVsyncToPhotons_Float );
+
+ // Use Seated here because everything using this interface or older is expecting a seated experience
+ m_pHmd->GetDeviceToAbsoluteTrackingPose( vr::TrackingUniverseSeated, fPredictedSecondsFromNow, &pose, 1 );
+
+ m_bHaveValidPose = pose.bPoseIsValid;
+ }
+ else
+ {
+ m_bHaveValidPose = false;
+ }
+
+ if( !m_bHaveValidPose )
+ return false;
+
+ m_ZeroFromHeadPose = OpenVRToSourceCoordinateSystem( VMatrixFrom34( pose.mDeviceToAbsoluteTracking.m ) );
+
+ return true;
+}
+
+
+// ----------------------------------------------------------------------
+// Purpose: Performs the distortion required for the HMD display
+// ----------------------------------------------------------------------
+bool CSourceVirtualReality::DoDistortionProcessing ( VREye eEye )
+{
+ if( !ShouldRunInVR() )
+ return false;
+ if ( !vr_distortion_enable.GetBool() )
+ {
+ return false;
+ }
+
+ CMatRenderContextPtr pRenderContext( materials );
+
+ IMaterial *pDistortMaterial;
+ if( eEye == VREye_Left )
+ pDistortMaterial = m_DistortLeftMaterial;
+ else
+ pDistortMaterial = m_DistortRightMaterial;
+
+ if( !UsingOffscreenRenderTarget() )
+ {
+ // copy the frame buffer to the source texture
+ ITexture *pFullFrameFB1 = materials->FindTexture( "_rt_FullFrameFB1", TEXTURE_GROUP_RENDER_TARGET );
+ if( !pFullFrameFB1 )
+ return false;
+
+ Rect_t r;
+ this->GetViewportBounds( eEye, &r.x, &r.y, &r.width, &r.height );
+ pRenderContext->CopyRenderTargetToTextureEx( pFullFrameFB1, 0, &r, &r );
+ }
+
+ // This is where we are rendering to
+ uint32_t x, y, w, h;
+ m_pHmd->GetEyeOutputViewport( SourceEyeToHmdEye( eEye ), &x, &y, &w, &h );
+
+ pRenderContext->DrawScreenSpaceRectangle ( pDistortMaterial,
+ x, y, w, h,
+ 0, 0, distortionTextureSize-1,distortionTextureSize-1,distortionTextureSize,distortionTextureSize);
+
+ return true;
+}
+
+
+// --------------------------------------------------------------------
+// Pastes the HUD directly onto the backbuffer / render target, including
+// applying the undistort.
+// --------------------------------------------------------------------
+bool CSourceVirtualReality::CompositeHud ( VREye eEye, float ndcHudBounds[4], bool bDoUndistort, bool bBlackout, bool bTranslucent )
+{
+ // run away if we're not doing VR at all
+ if ( ! ShouldRunInVR() )
+ return false;
+
+ bDoUndistort = bDoUndistort && vr_distortion_enable.GetBool();
+
+ IMaterial *pDistortHUDMaterial = ( eEye == VREye_Left ) ? m_DistortHUDLeftMaterial : m_DistortHUDRightMaterial;
+
+ // The translucency flag will enable/disable both blending and alpha test. The only case where we don't want them enabled
+ // is when we're blacking out the entire screen (we use blending to smooth the edges of the HUD, and we use alpha test to kill
+ // the pixels outside the HUD). Note that right now I'm not expecting to see a mode with bTranslucent and bBlackout
+ // both true (maybe happens in sniper mode?).
+ pDistortHUDMaterial->SetMaterialVarFlag( MATERIAL_VAR_TRANSLUCENT, ! bBlackout );
+
+ // The ndcHudBounds are the min x, min y, max x, max y of where we want to paste the HUD texture in NDC coordinates
+ // of the main 3D view. We conver to UV (0->1) space here for the shader.
+ float huduvs[4];
+ huduvs[0] = ndcHudBounds[0] * 0.5 + 0.5;
+ huduvs[1] = ndcHudBounds[1] * 0.5 + 0.5;
+ huduvs[2] = ndcHudBounds[2] * 0.5 + 0.5;
+ huduvs[3] = ndcHudBounds[3] * 0.5 + 0.5;
+
+ // Fix up coordinates depending on whether we're rendering to a buffer sized for one eye or two.
+ // (note that disabling distortion also disables use of the offscreen render target)
+ if ( vr_distortion_enable.GetBool() && ! UsingOffscreenRenderTarget() )
+ {
+ huduvs[0] *= 0.5;
+ huduvs[2] *= 0.5;
+ if ( eEye == VREye_Right )
+ {
+ huduvs[0] += 0.5;
+ huduvs[2] += 0.5;
+ }
+ }
+
+ IMaterialVar *pVar;
+
+ pVar = pDistortHUDMaterial->FindVar( "$distortbounds", NULL );
+ if ( pVar )
+ {
+ pVar->SetVecValue( huduvs, 4 );
+ }
+
+ pVar = pDistortHUDMaterial->FindVar( "$hudtranslucent", NULL );
+ if ( pVar )
+ {
+ pVar->SetIntValue( bTranslucent );
+ }
+
+ pVar = pDistortHUDMaterial->FindVar( "$hudundistort", NULL );
+ if ( pVar )
+ {
+ pVar->SetIntValue( bDoUndistort );
+ }
+
+ CMatRenderContextPtr pRenderContext( materials );
+
+ uint32_t x, y, w, h;
+ m_pHmd->GetEyeOutputViewport( SourceEyeToHmdEye( eEye ), &x, &y, &w, &h );
+
+ pRenderContext->DrawScreenSpaceRectangle ( pDistortHUDMaterial,
+ x, y, w, h,
+ 0, 0, distortionTextureSize-1,distortionTextureSize-1,distortionTextureSize,distortionTextureSize);
+
+ return true;
+}
+
+
+bool CSourceVirtualReality::EnsureOpenVRInited()
+{
+ if( m_pHmd )
+ return true;
+
+ return StartTracker();
+}
+
+bool CSourceVirtualReality::StartTracker()
+{
+ Assert( m_pHmd == NULL );
+
+ // Initialize SteamVR
+ vr::HmdError err;
+ m_pHmd = vr::VR_Init( &err );
+ if( err != HmdError_None )
+ {
+ Msg( "Unable to initialize HMD tracker. Error code %d\n", err );
+ return false;
+ }
+
+ m_pHmd->ResetSeatedZeroPose();
+
+ m_bHaveValidPose = false;
+ m_ZeroFromHeadPose.Identity();
+
+ return true;
+}
+
+void CSourceVirtualReality::StopTracker()
+{
+ if ( m_pHmd )
+ {
+ VR_Shutdown();
+ m_pHmd = NULL;
+ }
+}
+
+bool CSourceVirtualReality::ResetTracking()
+{
+ StopTracker();
+ return StartTracker();
+}
+
+
+bool CSourceVirtualReality::Activate()
+{
+ // init the HMD itself
+ if( !ResetTracking() )
+ return false;
+
+ m_bActive = true;
+ m_bUsingOffscreenRenderTarget = vr_use_offscreen_render_target.GetBool();
+
+ m_warpMaterial.Init( "dev/warp", "Other" );
+ if( UsingOffscreenRenderTarget() )
+ {
+ m_DistortLeftMaterial.Init( "vr/vr_distort_texture_left", "Other" );
+ m_DistortRightMaterial.Init( "vr/vr_distort_texture_right", "Other" );
+ }
+ else
+ {
+ m_DistortLeftMaterial.Init( "vr/vr_distort_texture_left_nort", "Other" );
+ m_DistortRightMaterial.Init( "vr/vr_distort_texture_right_nort", "Other" );
+ }
+ m_InWorldUIMaterial.Init( "vgui/inworldui", "Other" );
+ m_InWorldUIOpaqueMaterial.Init( "vgui/inworldui_opaque", "Other" );
+ m_blackMaterial.Init( "vgui/black", "Other" );
+
+ m_DistortHUDLeftMaterial.Init( "vr/vr_distort_hud_left", "Other" );
+ m_DistortHUDRightMaterial.Init( "vr/vr_distort_hud_right", "Other" );
+
+ RefreshDistortionTexture();
+ return true;
+}
+
+
+void CSourceVirtualReality::Deactivate()
+{
+ m_bActive = false;
+ m_bShouldForceVRMode = false;
+
+ m_warpMaterial.Shutdown();
+ m_DistortLeftMaterial.Shutdown();
+ m_DistortRightMaterial.Shutdown();
+ m_DistortHUDLeftMaterial.Shutdown();
+ m_DistortHUDRightMaterial.Shutdown();
+ m_InWorldUIMaterial.Shutdown();
+ m_InWorldUIOpaqueMaterial.Shutdown();
+ m_blackMaterial.Shutdown();
+}
+
+bool CSourceVirtualReality::ShouldForceVRMode()
+{
+ return m_bShouldForceVRMode;
+}
+
+void CSourceVirtualReality::SetShouldForceVRMode()
+{
+ m_bShouldForceVRMode = true;
+}
+
+static VMatrix OpenVRToSourceCoordinateSystem(const VMatrix& vortex)
+{
+ const float inchesPerMeter = (float)(39.3700787);
+
+ // From Vortex: X=right, Y=up, Z=backwards, scale is meters.
+ // To Source: X=forwards, Y=left, Z=up, scale is inches.
+ //
+ // s_from_v = [ 0 0 -1 0
+ // -1 0 0 0
+ // 0 1 0 0
+ // 0 0 0 1];
+ //
+ // We want to compute vmatrix = s_from_v * vortex * v_from_s; v_from_s = s_from_v'
+ // Given vortex =
+ // [00 01 02 03
+ // 10 11 12 13
+ // 20 21 22 23
+ // 30 31 32 33]
+ //
+ // s_from_v * vortex * s_from_v' =
+ // 22 20 -21 -23
+ // 02 00 -01 -03
+ // -12 -10 11 13
+ // -32 -30 31 33
+ //
+ const vec_t (*v)[4] = vortex.m;
+ VMatrix result(
+ v[2][2], v[2][0], -v[2][1], -v[2][3] * inchesPerMeter,
+ v[0][2], v[0][0], -v[0][1], -v[0][3] * inchesPerMeter,
+ -v[1][2], -v[1][0], v[1][1], v[1][3] * inchesPerMeter,
+ -v[3][2], -v[3][0], v[3][1], v[3][3]);
+
+ return result;
+}
+
+static VMatrix VMatrixFrom44(const float v[4][4])
+{
+ return VMatrix(
+ v[0][0], v[0][1], v[0][2], v[0][3],
+ v[1][0], v[1][1], v[1][2], v[1][3],
+ v[2][0], v[2][1], v[2][2], v[2][3],
+ v[3][0], v[3][1], v[3][2], v[3][3]);
+}
+
+static VMatrix VMatrixFrom34(const float v[3][4])
+{
+ return VMatrix(
+ v[0][0], v[0][1], v[0][2], v[0][3],
+ v[1][0], v[1][1], v[1][2], v[1][3],
+ v[2][0], v[2][1], v[2][2], v[2][3],
+ 0, 0, 0, 1 );
+}
+
+static VMatrix VMatrixFrom33(const float v[3][3])
+{
+ return VMatrix(
+ v[0][0], v[0][1], v[0][2], 0,
+ v[1][0], v[1][1], v[1][2], 0,
+ v[2][0], v[2][1], v[2][2], 0,
+ 0, 0, 0, 1);
+}
diff --git a/sourcevr/sourcevirtualreality.h b/sourcevr/sourcevirtualreality.h
new file mode 100644
index 0000000..0e78750
--- /dev/null
+++ b/sourcevr/sourcevirtualreality.h
@@ -0,0 +1,134 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: The implementation of ISourceVirtualReality, which provides utility
+// functions for VR including head tracking, window/viewport information,
+// rendering information, and distortion
+//
+//=============================================================================
+
+#ifndef SOURCEVIRTUALREALITY_H
+#define SOURCEVIRTUALREALITY_H
+#if defined( _WIN32 )
+#pragma once
+#endif
+
+#include "tier3/tier3.h"
+#include "sourcevr/isourcevirtualreality.h"
+#include "materialsystem/itexture.h"
+#include "materialsystem/MaterialSystemUtil.h"
+#include "openvr/openvr.h"
+
+
+// this is a callback so we can regenerate the distortion texture whenever we need to
+class CDistortionTextureRegen : public ITextureRegenerator
+{
+public:
+ CDistortionTextureRegen( vr::Hmd_Eye eEye ) : m_eEye( eEye ) {}
+ virtual void RegenerateTextureBits( ITexture *pTexture, IVTFTexture *pVTFTexture, Rect_t *pSubRect ) OVERRIDE;
+ virtual void Release() OVERRIDE {}
+
+private:
+ vr::Hmd_Eye m_eEye;
+};
+
+//-----------------------------------------------------------------------------
+// The implementation
+//-----------------------------------------------------------------------------
+
+
+class CSourceVirtualReality: public CTier3AppSystem< ISourceVirtualReality >
+{
+ typedef CTier3AppSystem< ISourceVirtualReality > BaseClass;
+
+public:
+
+ CSourceVirtualReality();
+ ~CSourceVirtualReality();
+
+ //---------------------------------------------------------
+ // Initialization and shutdown
+ //---------------------------------------------------------
+
+ //
+ // IAppSystem
+ //
+ virtual bool Connect( CreateInterfaceFn factory );
+ virtual void Disconnect();
+ virtual void * QueryInterface( const char *pInterfaceName );
+
+ // these will come from the engine
+ virtual InitReturnVal_t Init();
+ virtual void Shutdown();
+
+
+ //---------------------------------------------------------
+ // ISourceVirtualReality implementation
+ //---------------------------------------------------------
+ virtual bool ShouldRunInVR() OVERRIDE;
+ virtual bool IsHmdConnected() OVERRIDE;
+ virtual void GetViewportBounds( VREye eEye, int *pnX, int *pnY, int *pnWidth, int *pnHeight ) OVERRIDE;
+ virtual bool DoDistortionProcessing ( VREye eEye ) OVERRIDE;
+ virtual bool CompositeHud ( VREye eEye, float ndcHudBounds[4], bool bDoUndistort, bool bBlackout, bool bTranslucent ) OVERRIDE;
+ virtual VMatrix GetMideyePose() OVERRIDE;
+ virtual bool SampleTrackingState ( float PlayerGameFov, float fPredictionSeconds ) OVERRIDE;
+ virtual bool GetEyeProjectionMatrix ( VMatrix *pResult, VREye, float zNear, float zFar, float fovScale ) OVERRIDE;
+ virtual bool WillDriftInYaw() OVERRIDE;
+ virtual bool GetDisplayBounds( VRRect_t *pRect ) OVERRIDE;
+ virtual VMatrix GetMidEyeFromEye( VREye eEye ) OVERRIDE;
+ virtual int GetVRModeAdapter() OVERRIDE;
+ virtual void CreateRenderTargets( IMaterialSystem *pMaterialSystem ) OVERRIDE;
+ virtual void ShutdownRenderTargets() OVERRIDE;
+ virtual ITexture *GetRenderTarget( VREye eEye, EWhichRenderTarget eWhich ) OVERRIDE;
+ virtual void GetRenderTargetFrameBufferDimensions( int & nWidth, int & nHeight ) OVERRIDE;
+ virtual bool Activate() OVERRIDE;
+ virtual void Deactivate() OVERRIDE;
+ virtual bool ShouldForceVRMode( ) OVERRIDE;
+ virtual void SetShouldForceVRMode( ) OVERRIDE;
+
+ void RefreshDistortionTexture();
+ void AcquireNewZeroPose();
+
+ bool StartTracker();
+ void StopTracker();
+ bool ResetTracking(); // Called to reset tracking
+
+ // makes sure we've initialized OpenVR so we can use
+ // m_pHmd
+ bool EnsureOpenVRInited();
+
+ // Prefer this to the convar so that convar will stick for the entire
+ // VR activation. We can't lazy-crate render targets and don't
+ // want to create the "just in case" somebody turns on this experimental
+ // mode
+ bool UsingOffscreenRenderTarget() const { return m_bUsingOffscreenRenderTarget; }
+
+ vr::IVRSystem * GetHmd() { return m_pHmd; }
+private:
+ bool m_bActive;
+ bool m_bShouldForceVRMode;
+ bool m_bUsingOffscreenRenderTarget;
+ CDistortionTextureRegen m_textureGeneratorLeft;
+ CDistortionTextureRegen m_textureGeneratorRight;
+ CTextureReference g_StereoGuiTexture;
+ CTextureReference m_pDistortionTextureLeft;
+ CTextureReference m_pDistortionTextureRight;
+ CTextureReference m_pPredistortRT;
+ CTextureReference m_pPredistortRTDepth;
+ CMaterialReference m_warpMaterial;
+ CMaterialReference m_DistortLeftMaterial;
+ CMaterialReference m_DistortRightMaterial;
+ CMaterialReference m_DistortHUDLeftMaterial;
+ CMaterialReference m_DistortHUDRightMaterial;
+ CMaterialReference m_InWorldUIMaterial;
+ CMaterialReference m_InWorldUIOpaqueMaterial;
+ CMaterialReference m_blackMaterial;
+
+ vr::IVRSystem *m_pHmd;
+
+ bool m_bHaveValidPose;
+ VMatrix m_ZeroFromHeadPose;
+
+};
+
+
+#endif // SOURCEVIRTUALREALITY_H
diff --git a/sourcevr/sourcevr.vpc b/sourcevr/sourcevr.vpc
new file mode 100644
index 0000000..e357f02
--- /dev/null
+++ b/sourcevr/sourcevr.vpc
@@ -0,0 +1,72 @@
+//-----------------------------------------------------------------------------
+// SOURCEVR.VPC
+//
+// Wraps OpenVR and provides VR-related services to other bits of Source
+//-----------------------------------------------------------------------------
+
+$Macro SRCDIR ".."
+$Macro OUTBINDIR "..\..\game\bin"
+$Macro BINNAME "sourcevr"
+
+$Include "$SRCDIR\vpc_scripts\source_dll_base.vpc"
+
+$Configuration
+{
+ $General
+ {
+ $AdditionalProjectDependencies "$BASE;filesystem_stdio"
+ }
+
+ $Compiler
+ {
+ $PreprocessorDefinitions "$BASE;SOURCEVR_DLL;VERSION_SAFE_STEAM_API_INTERFACES"
+ $Create/UsePrecompiledHeader "Use Precompiled Header (/Yu)"
+ $Create/UsePCHThroughFile "cbase.h"
+ $PrecompiledHeaderFile "$(IntDir)/sourcevr.pch"
+ }
+
+ $Linker
+ {
+ $IgnoreImportLibrary "TRUE"
+ }
+}
+
+$Project "SourceVR"
+{
+ $Folder "Source Files"
+ {
+ $Folder "Precompiled Header"
+ {
+ $File "$SRCDIR\sourcevr\stdafx.cpp"
+ {
+ $Configuration
+ {
+ $Compiler
+ {
+ $Create/UsePrecompiledHeader "Create Precompiled Header (/Yc)"
+ }
+ }
+ }
+ $File "$SRCDIR\sourcevr\cbase.h"
+ }
+
+ $Folder "Public Headers"
+ {
+ $File "$SRCDIR\public\sourcevr\isourcevirtualreality.h"
+ $File "$SRCDIR\public\openvr\openvr.h"
+ }
+
+ $File "$SRCDIR\sourcevr\sourcevirtualreality.h"
+ $File "$SRCDIR\sourcevr\sourcevirtualreality.cpp"
+ }
+
+ $Folder "Link Libraries"
+ {
+ $ImpLibexternal openvr_api
+ $Lib mathlib
+ $Lib tier2
+ $Lib tier3
+ $Lib appframework
+ $ImpLibexternal steam_api
+ }
+}
diff --git a/sourcevr/stdafx.cpp b/sourcevr/stdafx.cpp
new file mode 100644
index 0000000..ce81dd7
--- /dev/null
+++ b/sourcevr/stdafx.cpp
@@ -0,0 +1,2 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+#include "cbase.h" \ No newline at end of file