diff --git a/Projects/Android/jni/src/Lambda1VR/L1VR_SurfaceView.c b/Projects/Android/jni/src/Lambda1VR/L1VR_SurfaceView.c index 955f428..ef3ab2a 100644 --- a/Projects/Android/jni/src/Lambda1VR/L1VR_SurfaceView.c +++ b/Projects/Android/jni/src/Lambda1VR/L1VR_SurfaceView.c @@ -356,6 +356,32 @@ bool VR_GetEyeTransform(int eye, float worldScale, vec3_t offset, float* cantDeg } return true; } + +/* +How far to shift the 2D content (the HUD) in an eye's image, in pixels, positive to the right, +so that it sits vr_hud_distance metres ahead of the head. 0 for the mono eyes and the screen layer. +*/ +float VR_GetHudShift(int eye) +{ + if (eye < 0 || eye >= ovrMaxNumEyes || !gAppState.SessionActive || gAppState.Projections == NULL || + VR_UseScreenLayer() || vr_hud_distance == NULL) + return 0.0f; + + const XrView* view = &gAppState.Projections[eye]; + const int width = gAppState.Renderer.FrameBuffer[eye].Width; + const float shift = VrEye_HudShiftPixels(view->pose.position.x, view->fov.angleLeft, view->fov.angleRight, + width, vr_hud_distance->value); + + static bool logged[ovrMaxNumEyes]; + if (!logged[eye]) + { + logged[eye] = true; + ALOGI("[openxr] hud: eye %d at x %.1f mm, focal length %.0f px, vr_hud_distance %.2f m, shift %.1f px", + eye, view->pose.position.x * 1000.0f, VrEye_FocalPixels(view->fov.angleLeft, view->fov.angleRight, width), + vr_hud_distance->value, shift); + } + return shift; +} #endif void R_ChangeDisplaySettings( int width, int height, qboolean fullscreen ); @@ -437,6 +463,9 @@ convar_t *vr_quick_crouchjump; convar_t *vr_stereo_side; convar_t *vr_gesture_triggered_use; convar_t *vr_use_gesture_boundary; +#ifdef L1VR_STEAM_FRAME +convar_t *vr_hud_distance; +#endif void initialize_gl4es(); @@ -496,6 +525,9 @@ void VR_Init() vr_quick_crouchjump = Cvar_Get( "vr_quick_crouchjump", "1", CVAR_ARCHIVE, "Set to 0 to disable quick crouch-jump mode (double clicking jump button triggers duck)" ); vr_gesture_triggered_use = Cvar_Get( "vr_gesture_triggered_use", "1", CVAR_ARCHIVE, "Set to 0 to disable use gesture, 1 to enable" ); vr_use_gesture_boundary = Cvar_Get( "vr_use_gesture_boundary", "0.35", CVAR_ARCHIVE, "Use gesture boundary" ); +#ifdef L1VR_STEAM_FRAME + vr_hud_distance = Cvar_Get( "vr_hud_distance", "1.0", CVAR_ARCHIVE, "How far ahead the HUD sits, in metres" ); +#endif //Not to be changed by users, as it will be overwritten anyway vr_stereo_side = Cvar_Get( "vr_stereo_side", "0", CVAR_READ_ONLY, "Eye being drawn" ); diff --git a/Projects/Android/jni/src/Lambda1VR/VrCvars.h b/Projects/Android/jni/src/Lambda1VR/VrCvars.h index 20859ac..b2756c5 100644 --- a/Projects/Android/jni/src/Lambda1VR/VrCvars.h +++ b/Projects/Android/jni/src/Lambda1VR/VrCvars.h @@ -33,4 +33,8 @@ extern convar_t *vr_gesture_triggered_use; extern convar_t *vr_use_gesture_boundary; //Used and updated continuously during rendering -extern convar_t *vr_stereo_side; \ No newline at end of file +extern convar_t *vr_stereo_side; + +#ifdef L1VR_STEAM_FRAME +extern convar_t *vr_hud_distance; +#endif diff --git a/Projects/Android/jni/src/Xash3D/xash3d/engine/client/cl_game.c b/Projects/Android/jni/src/Xash3D/xash3d/engine/client/cl_game.c index 23f5364..9a9ae37 100644 --- a/Projects/Android/jni/src/Xash3D/xash3d/engine/client/cl_game.c +++ b/Projects/Android/jni/src/Xash3D/xash3d/engine/client/cl_game.c @@ -1148,6 +1148,52 @@ static void CL_DrawPause( void ) R_DrawStretchPic( x, y, width, height, 0, 0, 1, 1, cls.pauseIcon ); } +#ifdef L1VR_STEAM_FRAME +extern convar_t *vr_stereo_side; +float VR_GetHudShift( int eye ); +void R_SetHudShift( float pixels ); +bool isScopeEngaged( void ); + +/* +=============== +CL_RedrawHUD + +The client DLL puts its HUD in front of the screen with a fixed sideways shift of width / 36 +per eye, which it works out from vr_stereo_side. That's about 0.4 m on the Frame's lenses, and +tiring. While it draws, the eyes are given as the mono ones (that's 0 for it, and the 2D +offset onto the optical axis still works for those), and the whole HUD is shifted here +instead, to where vr_hud_distance says. A scope is mono already, so that is left alone. +=============== +*/ +static void CL_RedrawHUD( void ) +{ + const int eye = vr_stereo_side->integer; + float shift; + + if( eye < VR_EYE_LEFT || eye > VR_EYE_RIGHT || isScopeEngaged( )) + { + CL_DrawCenterPrint (); + clgame.dllFuncs.pfnRedraw( cl.time, cl.refdef.intermission ); + return; + } + + shift = VR_GetHudShift( eye ); + + vr_stereo_side->value = eye + 2; + vr_stereo_side->integer = eye + 2; + R_SetHudShift( shift ); + R_Set2DMode( true ); + + CL_DrawCenterPrint (); + clgame.dllFuncs.pfnRedraw( cl.time, cl.refdef.intermission ); + + vr_stereo_side->value = eye; + vr_stereo_side->integer = eye; + R_SetHudShift( 0.0f ); + R_Set2DMode( true ); +} +#endif + void CL_DrawHUD( int state ) { if( state == CL_ACTIVE && !cl.video_prepped ) @@ -1161,14 +1207,22 @@ void CL_DrawHUD( int state ) case CL_ACTIVE: CL_DrawScreenFade (); CL_DrawCrosshair (); +#ifdef L1VR_STEAM_FRAME + CL_RedrawHUD (); +#else CL_DrawCenterPrint (); clgame.dllFuncs.pfnRedraw( cl.time, cl.refdef.intermission ); +#endif break; case CL_PAUSED: CL_DrawScreenFade (); CL_DrawCrosshair (); +#ifdef L1VR_STEAM_FRAME + CL_RedrawHUD (); +#else CL_DrawCenterPrint (); clgame.dllFuncs.pfnRedraw( cl.time, cl.refdef.intermission ); +#endif CL_DrawPause(); break; case CL_LOADING: diff --git a/Projects/Android/jni/src/Xash3D/xash3d/engine/client/cl_scrn.c b/Projects/Android/jni/src/Xash3D/xash3d/engine/client/cl_scrn.c index 61ff4a1..d32ba41 100644 --- a/Projects/Android/jni/src/Xash3D/xash3d/engine/client/cl_scrn.c +++ b/Projects/Android/jni/src/Xash3D/xash3d/engine/client/cl_scrn.c @@ -43,11 +43,20 @@ typedef struct static dirty_t scr_dirty, scr_old_dirty[2]; static qboolean scr_init = false; +#ifdef L1VR_STEAM_FRAME +float VR_GetHudShift( int eye ); +#endif + int GetStereoDepthOffset() { if (vr_stereo_side->value >= VR_EYE_LEFT_MONO) return 0; +#ifdef L1VR_STEAM_FRAME + // the same place the HUD is put + return (int)VR_GetHudShift( vr_stereo_side->integer ); +#endif + return (int)( ( vr_stereo_side->value * -2.0f ) + 1.0f) * (scr_width->integer / 36.0f); } diff --git a/Projects/Android/jni/src/Xash3D/xash3d/engine/client/gl_draw.c b/Projects/Android/jni/src/Xash3D/xash3d/engine/client/gl_draw.c index 5d51b96..877f5b8 100644 --- a/Projects/Android/jni/src/Xash3D/xash3d/engine/client/gl_draw.c +++ b/Projects/Android/jni/src/Xash3D/xash3d/engine/client/gl_draw.c @@ -283,6 +283,16 @@ two are not the same point, so 2D content anchored to the buffer centre would no the eyes. Shifting the ortho window here moves every 2D item at once. =============== */ +#ifdef L1VR_STEAM_FRAME +// extra shift for the HUD pass (see CL_RedrawHUD), in pixels, positive to the right +static float r_hudShift = 0.0f; + +void R_SetHudShift( float pixels ) +{ + r_hudShift = pixels; +} +#endif + void R_Get2DOffset( float *dx, float *dy ) { *dx = *dy = 0.0f; @@ -292,6 +302,9 @@ void R_Get2DOffset( float *dx, float *dy ) return; VR_Get2DOffset( vr_stereo_side->integer, glState.width, glState.height, dx, dy ); +#ifdef L1VR_STEAM_FRAME + *dx += r_hudShift; +#endif } /*