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.
857 lines
25 KiB
857 lines
25 KiB
//========= 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); |
|
}
|
|
|