source-engine/sourcevr/sourcevirtualreality.cpp

924 lines
28 KiB
C++
Raw Normal View History

2020-04-22 16:56:21 +00:00
//========= 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;
2023-09-18 21:13:55 +00:00
m_bUsingOffscreenRenderTarget = true;
2020-04-22 16:56:21 +00:00
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;
if(!g_SourceVirtualReality.GetHmd()->ComputeDistortion( m_eEye, u, v, &coords ))
{
Warning("ComputeDistortion failed");
}
2020-04-22 16:56:21 +00:00
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 = 0, y = 0;
uint32_t width = 1024, height = 1024;
m_pExtDisplay->GetWindowBounds( &x, &y, &width, &height );
2020-04-22 16:56:21 +00:00
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 = 0, y = 0, w = 640, h = 480;
// m_pHmd->GetEyeOutputViewport( SourceEyeToHmdEye( eEye ), &x, &y, &w, &h );
m_pExtDisplay->GetEyeOutputViewport( SourceEyeToHmdEye( eEye ), &x, &y, &w, &h );
2020-04-22 16:56:21 +00:00
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 )
{
2023-09-18 21:13:55 +00:00
/*if( fovScale != 1.0f && fovScale > 0.f )
2020-04-22 16:56:21 +00:00
{
float fFovScaleAdjusted = tan( atan( fTop ) / fovScale ) / fTop;
fRight *= fFovScaleAdjusted;
fLeft *= fFovScaleAdjusted;
fTop *= fFovScaleAdjusted;
fBottom *= fFovScaleAdjusted;
2023-09-18 21:13:55 +00:00
}*/
2020-04-22 16:56:21 +00:00
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_pChap )
m_pChap->ResetZeroPose(TrackingUniverseSeated);
2020-04-22 16:56:21 +00:00
}
bool CSourceVirtualReality::SampleTrackingState ( float PlayerGameFov, float fPredictionSeconds )
{
if( !m_pHmd || !m_bActive )
return false;
vr::VREvent_t event;
while( m_pHmd->PollNextEvent( &event, sizeof( event ) ) )
{
//ProcessVREvent( event );
}
vr::TrackedDevicePose_t m_rTrackedDevicePose[ vr::k_unMaxTrackedDeviceCount ];
vr::VRCompositor()->WaitGetPoses(m_rTrackedDevicePose, vr::k_unMaxTrackedDeviceCount, NULL, 0 );
2020-04-22 16:56:21 +00:00
// 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;
}
#include "togl/rendermechanism.h"
2020-04-22 16:56:21 +00:00
// ----------------------------------------------------------------------
// 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;
ITexture *pDistortTexture;
2020-04-22 16:56:21 +00:00
if( eEye == VREye_Left )
pDistortMaterial = m_DistortLeftMaterial;
else
pDistortMaterial = m_DistortRightMaterial;
if( eEye == VREye_Left )
pDistortTexture = m_pDistortionTextureLeft;
else
pDistortTexture = m_pDistortionTextureRight;
2020-04-22 16:56:21 +00:00
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;
r.x = !eEye?0:640;
r.y = 0;
r.width = 640;
r.height = 480;
2020-04-22 16:56:21 +00:00
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;
x = !eEye?0:640;
y = 0;
w = 640;
h = 480;
2023-09-18 21:13:55 +00:00
//m_pExtDisplay->GetEyeOutputViewport( SourceEyeToHmdEye( eEye ), &x, &y, &w, &h );
// pRenderContext->DrawScreenSpaceRectangle ( pDistortMaterial,
// x, y, w, h,
// 0, 0, distortionTextureSize-1,distortionTextureSize-1,distortionTextureSize,distortionTextureSize);
2020-04-22 16:56:21 +00:00
static int id = -1;
//static CDynamicFunctionOpenGL< true, GLvoid ( APIENTRY *)(GLenum pname, GLint *params), GLvoid > glGetIntegerv("glGetIntegerv");
// pRenderContext->Bind(pDistortMaterial);
// pRenderContext->Flush( true );
// ShaderAPITextureHandle_t hndl = materials->GetShaderAPITextureBindHandle(pDistortTexture,0,0);
//if(id < 0)
id = materials->GetShaderAPIGLTexture(m_pPredistortRT,0,0);
static int last_tex[2] = {-1, -1};
// glGetIntegerv(GL_TEXTURE_BINDING_2D, &id);
if(id > 0)
last_tex[eEye != VREye_Left] = id;
2023-09-18 21:13:55 +00:00
// Msg("tex %d\n", id);
const vr::VRTextureBounds_t bounds = { 0.0f, 1.0f, 1.0f, 0.0f };
vr::Texture_t eyeTexture = {(void*)(uintptr_t)last_tex[eEye != VREye_Left], vr::TextureType_OpenGL, vr::ColorSpace_Gamma };
if(last_tex[eEye != VREye_Left] <= 0)
return true;
2023-09-18 21:13:55 +00:00
materials->Flush();
// if(eEye != VREye_Left)
// return 0;
glFinish();
vr::VRCompositor()->Submit(SourceEyeToHmdEye( eEye ), &eyeTexture, &bounds );
2020-04-22 16:56:21 +00:00
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;
x = !eEye?0:640;
y = 0;
w = 640;
h = 480;
m_pExtDisplay->GetEyeOutputViewport( SourceEyeToHmdEye( eEye ), &x, &y, &w, &h );
2023-09-18 21:13:55 +00:00
int id = materials->GetShaderAPIGLTexture(g_StereoGuiTexture,0,0);
vr::Texture_t guiTexture = {(void*)(uintptr_t)id, vr::TextureType_OpenGL, vr::ColorSpace_Gamma };
const vr::VRTextureBounds_t bounds = { 0.0f, 1.0f, 1.0f, 0.0f };
vr::VROverlay()->SetOverlayTextureBounds(m_GuiOverlay, &bounds);
vr::VROverlay()->SetOverlayTexture(m_GuiOverlay, &guiTexture);
2020-04-22 16:56:21 +00:00
// pRenderContext->DrawScreenSpaceRectangle ( pDistortHUDMaterial,
// x, y, w, h,
// 0, 0, distortionTextureSize-1,distortionTextureSize-1,distortionTextureSize,distortionTextureSize);
2020-04-22 16:56:21 +00:00
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, vr::VRApplication_Scene );
m_pExtDisplay = vr::VRExtendedDisplay();
m_pChap = vr::VRChaperone();
if( err != vr::VRInitError_None )
2020-04-22 16:56:21 +00:00
{
Msg( "Unable to initialize HMD tracker. Error code %d\n", err );
return false;
}
if( !vr::VRCompositor() )
{
Msg("Compositor initialization failed. See log file for details");
}
2023-09-18 21:13:55 +00:00
vr::VROverlay()->CreateOverlay("GuiOverlayKey", "GuiOverlay", &m_GuiOverlay);
vr::VROverlay()->ShowOverlay(m_GuiOverlay);
m_pChap->ResetZeroPose(TrackingUniverseSeated);
2020-04-22 16:56:21 +00:00
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);
}