From f0eb5262ebfe78d4b95fd41f33ede2b6051876a1 Mon Sep 17 00:00:00 2001 From: Simon Date: Thu, 27 Jul 2023 23:08:48 +0100 Subject: [PATCH] Integration of PCVR into the main repo --- .gitignore | 191 +++ .../Android/jni/OpenJK/Android_client_ja.mk | 6 +- .../Android/jni/OpenJK/Android_client_jo.mk | 6 +- Projects/Android/jni/OpenJK/JKXR/VrCommon.h | 6 +- .../Android/jni/OpenJK/JKXR/VrInputCommon.cpp | 12 + .../jni/OpenJK/JKXR/VrInputDefault.cpp | 75 +- .../jni/OpenJK/JKXR/VrInputWeaponAlign.cpp | 2 - .../JKXR/{ => android}/JKXR_SurfaceView.cpp | 1 + .../OpenJK/JKXR/{ => android}/TBXR_Common.cpp | 14 +- .../OpenJK/JKXR/{ => android}/TBXR_Common.h | 0 .../jni/OpenJK/JKXR/{ => android}/argtable3.c | 0 .../jni/OpenJK/JKXR/{ => android}/argtable3.h | 0 .../OpenJK/JKXR/windows/JKXR_SurfaceView.cpp | 487 ++++++ .../jni/OpenJK/JKXR/windows/TBXR_Common.cpp | 1310 +++++++++++++++++ .../jni/OpenJK/JKXR/windows/TBXR_Common.h | 271 ++++ .../Android/jni/OpenJK/code/CMakeLists.txt | 6 +- 16 files changed, 2338 insertions(+), 49 deletions(-) rename Projects/Android/jni/OpenJK/JKXR/{ => android}/JKXR_SurfaceView.cpp (99%) rename Projects/Android/jni/OpenJK/JKXR/{ => android}/TBXR_Common.cpp (99%) rename Projects/Android/jni/OpenJK/JKXR/{ => android}/TBXR_Common.h (100%) rename Projects/Android/jni/OpenJK/JKXR/{ => android}/argtable3.c (100%) rename Projects/Android/jni/OpenJK/JKXR/{ => android}/argtable3.h (100%) create mode 100644 Projects/Android/jni/OpenJK/JKXR/windows/JKXR_SurfaceView.cpp create mode 100644 Projects/Android/jni/OpenJK/JKXR/windows/TBXR_Common.cpp create mode 100644 Projects/Android/jni/OpenJK/JKXR/windows/TBXR_Common.h diff --git a/.gitignore b/.gitignore index a136641..7a85e6a 100644 --- a/.gitignore +++ b/.gitignore @@ -69,3 +69,194 @@ gradlew.bat JKXR.iml local.properties local.properties + + +################# +## Eclipse +################# + +*.pydevproject +.project +.metadata +bin/ +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.classpath +.settings/ +.loadpath + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# CDT-specific +.cproject + +# PDT-specific +.buildpath + + +################# +## Visual Studio +################# + +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +.vs + +# User-specific files +*.suo +*.user +*.sln.docstates + +# Build results +[Dd]ebug/ +[Rr]elease/ +[Ff]inal/ +[Ff]inal[Bb]uild/ +*_i.c +*_p.c +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.vspscc +.builds +*.dotCover + +winquake.res + +## TODO: If you have NuGet Package Restore enabled, uncomment this +#packages/ + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opensdf +*.sdf + +# Visual Studio profiler +*.psess +*.vsp + +# ReSharper is a .NET coding add-in +_ReSharper* + +# Installshield output folder +[Ee]xpress + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish + +# Others +[Bb]in +[Oo]bj +sql +TestResults +*.Cache +ClientBin +stylecop.* +~$* +*.dbmdl +Generated_Code #added for RIA/Silverlight projects + +# Backup & report files from converting an old project file to a newer +# Visual Studio version. Backup files are not needed, because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML + +# Visual Studio Code +.vscode + + +############ +## Windows +############ + +# Windows image file caches +Thumbs.db + +# Folder config file +Desktop.ini + + +############# +## Python +############# + +*.py[co] + +# Packages +*.egg +*.egg-info +dist +build +build64 +eggs +parts +bin +var +sdist +develop-eggs +.installed.cfg + +# Installer logs +pip-log.txt + +# Unit test / coverage reports +.coverage +.tox + +#Translations +*.mo + +#Mr Developer +.mr.developer.cfg + +# Mac crap +.DS_Store + + +############# +## Unix +############# +*.o +*.so + +############# +## clangd +############# +.clangd + +############# +## CLion +############# +.idea diff --git a/Projects/Android/jni/OpenJK/Android_client_ja.mk b/Projects/Android/jni/OpenJK/Android_client_ja.mk index 08f6ec1..624cd71 100644 --- a/Projects/Android/jni/OpenJK/Android_client_ja.mk +++ b/Projects/Android/jni/OpenJK/Android_client_ja.mk @@ -123,12 +123,12 @@ JK3_SRC = \ ${SHARED_PATH}/qcommon/safe/string.cpp \ -JKXR_SRC_FILES := ${OPENJK_PATH}/JKXR/JKXR_SurfaceView.cpp \ - ${OPENJK_PATH}/JKXR/TBXR_Common.cpp \ +JKXR_SRC_FILES := ${OPENJK_PATH}/JKXR/android/JKXR_SurfaceView.cpp \ + ${OPENJK_PATH}/JKXR/android/TBXR_Common.cpp \ + ${OPENJK_PATH}/JKXR/android/argtable3.c \ ${OPENJK_PATH}/JKXR/VrInputCommon.cpp \ ${OPENJK_PATH}/JKXR/VrInputDefault.cpp \ ${OPENJK_PATH}/JKXR/VrInputWeaponAlign.cpp \ - ${OPENJK_PATH}/JKXR/argtable3.c \ ${OPENJK_PATH}/JKXR/OpenXrInput.cpp LOCAL_SRC_FILES += $(JK3_SRC) $(JKXR_SRC_FILES) diff --git a/Projects/Android/jni/OpenJK/Android_client_jo.mk b/Projects/Android/jni/OpenJK/Android_client_jo.mk index ab6d560..98ba04b 100644 --- a/Projects/Android/jni/OpenJK/Android_client_jo.mk +++ b/Projects/Android/jni/OpenJK/Android_client_jo.mk @@ -123,12 +123,12 @@ JK3_SRC = \ ${SHARED_PATH}/qcommon/safe/string.cpp \ -JKXR_SRC_FILES := ${OPENJK_PATH}/JKXR/JKXR_SurfaceView.cpp \ - ${OPENJK_PATH}/JKXR/TBXR_Common.cpp \ +JKXR_SRC_FILES := ${OPENJK_PATH}/JKXR/android/JKXR_SurfaceView.cpp \ + ${OPENJK_PATH}/JKXR/android/TBXR_Common.cpp \ + ${OPENJK_PATH}/JKXR/android/argtable3.c \ ${OPENJK_PATH}/JKXR/VrInputCommon.cpp \ ${OPENJK_PATH}/JKXR/VrInputDefault.cpp \ ${OPENJK_PATH}/JKXR/VrInputWeaponAlign.cpp \ - ${OPENJK_PATH}/JKXR/argtable3.c \ ${OPENJK_PATH}/JKXR/OpenXrInput.cpp LOCAL_SRC_FILES += $(JK3_SRC) $(JKXR_SRC_FILES) diff --git a/Projects/Android/jni/OpenJK/JKXR/VrCommon.h b/Projects/Android/jni/OpenJK/JKXR/VrCommon.h index 6c40a38..5196f69 100644 --- a/Projects/Android/jni/OpenJK/JKXR/VrCommon.h +++ b/Projects/Android/jni/OpenJK/JKXR/VrCommon.h @@ -6,7 +6,11 @@ #include "VrClientInfo.h" -#include "TBXR_Common.h" +#ifdef _WIN32 +#include "windows/TBXR_Common.h" +#else +#include "android/TBXR_Common.h" +#endif extern long long global_time; diff --git a/Projects/Android/jni/OpenJK/JKXR/VrInputCommon.cpp b/Projects/Android/jni/OpenJK/JKXR/VrInputCommon.cpp index db2fe81..08f30cc 100644 --- a/Projects/Android/jni/OpenJK/JKXR/VrInputCommon.cpp +++ b/Projects/Android/jni/OpenJK/JKXR/VrInputCommon.cpp @@ -131,7 +131,19 @@ void sendButtonAction(const char* action, long buttonDown) Cbuf_AddText( command ); } +#ifndef _WIN32 void PortableMouseAbs(float x,float y); +#else +void PortableMouseAbs(float x, float y) +{ + int absx = 0, absy = 0; + absx = x * 640; + absy = y * 480; + + CL_MouseEvent(absx, absy, 0); +} +#endif + float clamp(float _min, float _val, float _max) { return fmax(fmin(_val, _max), _min); diff --git a/Projects/Android/jni/OpenJK/JKXR/VrInputDefault.cpp b/Projects/Android/jni/OpenJK/JKXR/VrInputDefault.cpp index 2500a42..43fb8e8 100644 --- a/Projects/Android/jni/OpenJK/JKXR/VrInputDefault.cpp +++ b/Projects/Android/jni/OpenJK/JKXR/VrInputDefault.cpp @@ -7,28 +7,28 @@ Authors : Simon Brown *************************************************************************************/ -#include - #include "VrInput.h" #include "VrCvars.h" #include "qcommon/q_shared.h" #include #include + +#ifndef _WIN32 +#include #include #include "android/sys_local.h" +#endif + #include "VrTBDC.h" -#ifdef JK2_MODE -#include "../OpenJK/codeJK2/game/weapons.h" -#include "../OpenJK/codeJK2/game/bg_public.h" -#include "../OpenJK/codeJK2/game/wp_saber.h" -#else -#include "../OpenJK/code/game/weapons.h" -#include "../OpenJK/codeJK2/game/bg_public.h" -#include "../OpenJK/code/game/wp_saber.h" -#include "../OpenJK/code/game/g_vehicles.h" -#endif +#include "game/weapons.h" +#include "game/bg_public.h" +#include "game/wp_saber.h" +#include "game/g_vehicles.h" + +void Sys_QueEvent(int time, sysEventType_t type, int value, int value2, int ptrLength, void* ptr); + void HandleInput_Default( ovrInputStateTrackedRemote *pDominantTrackedRemoteNew, ovrInputStateTrackedRemote *pDominantTrackedRemoteOld, ovrTrackedController* pDominantTracking, ovrInputStateTrackedRemote *pOffTrackedRemoteNew, ovrInputStateTrackedRemote *pOffTrackedRemoteOld, ovrTrackedController* pOffTracking, @@ -58,6 +58,8 @@ void HandleInput_Default( ovrInputStateTrackedRemote *pDominantTrackedRemoteNew, int secondaryButton1; bool secondaryButton2New; bool secondaryButton2Old; + bool secondaryButton1New; + bool secondaryButton1Old; int primaryThumb; int secondaryThumb; if (vr_control_scheme->integer == LEFT_HANDED_DEFAULT && vr_switch_sticks->integer) @@ -107,6 +109,8 @@ void HandleInput_Default( ovrInputStateTrackedRemote *pDominantTrackedRemoteNew, primaryButton2Old = domButton2 & pDominantTrackedRemoteOld->Buttons; secondaryButton2New = offButton2 & pOffTrackedRemoteNew->Buttons; secondaryButton2Old = offButton2 & pOffTrackedRemoteOld->Buttons; + secondaryButton1New = offButton1 & pOffTrackedRemoteNew->Buttons; + secondaryButton1Old = offButton1 & pOffTrackedRemoteOld->Buttons; //Allow weapon alignment mode toggle on x if (vr_align_weapons->value) @@ -232,6 +236,11 @@ void HandleInput_Default( ovrInputStateTrackedRemote *pDominantTrackedRemoteNew, Sys_QueEvent(0, SE_KEY, A_TAB, true, 0, NULL); } + //Close the menu + if (secondaryButton1New && !secondaryButton1Old) { + Sys_QueEvent(0, SE_KEY, A_ESCAPE, true, 0, NULL); + } + } else { @@ -436,8 +445,7 @@ void HandleInput_Default( ovrInputStateTrackedRemote *pDominantTrackedRemoteNew, int mode = (int) Cvar_VariableValue("cg_thirdPerson"); static bool switched = false; if (between(-0.2f, primaryJoystickX, 0.2f) && - (between(0.8f, pPrimaryJoystick->y, 1.0f) || - between(-1.0f, pPrimaryJoystick->y, -0.8f))) { + between(-1.0f, pPrimaryJoystick->y, -0.8f)) { if (!switched) { mode = 1 - mode; sendButtonActionSimple(va("cg_thirdPerson %i", mode)); @@ -463,6 +471,21 @@ void HandleInput_Default( ovrInputStateTrackedRemote *pDominantTrackedRemoteNew, } } + //Switch movement speed + { + static bool switched = false; + if (between(-0.2f, primaryJoystickX, 0.2f) && + between(0.8f, pPrimaryJoystick->y, 1.0f)) { + if (!switched) { + vr.move_speed = (++vr.move_speed) % 3; + switched = true; + } + } + else { + switched = false; + } + } + /* //Parameter Changer static bool changed = false; @@ -715,7 +738,7 @@ void HandleInput_Default( ovrInputStateTrackedRemote *pDominantTrackedRemoteNew, //Positional movement speed correction for when we are not hitting target framerate static double lastframetime = 0; int refresh = TBXR_GetRefresh(); - double newframetime = TBXR_GetTimeInMilliSeconds(); + double newframetime = Sys_Milliseconds(); float multiplier = (float) ((1000.0 / refresh) / (newframetime - lastframetime)); lastframetime = newframetime; @@ -728,12 +751,7 @@ void HandleInput_Default( ovrInputStateTrackedRemote *pDominantTrackedRemoteNew, positional_movementSideways = v[0]; positional_movementForward = v[1]; - ALOGV(" positional_movementSideways: %f, positional_movementForward: %f", - positional_movementSideways, - positional_movementForward); - - - //Jump (A Button) + //Jump (A Button) if ((primaryButtonsNew & primaryButton1) != (primaryButtonsOld & primaryButton1)) { sendButtonAction("+moveup", (primaryButtonsNew & primaryButton1)); } @@ -854,16 +872,13 @@ void HandleInput_Default( ovrInputStateTrackedRemote *pDominantTrackedRemoteNew, v[0] * (vr.move_speed == 0 ? 0.75f : (vr.move_speed == 1 ? 1.0f : 0.5f)); remote_movementForward = v[1] * (vr.move_speed == 0 ? 0.75f : (vr.move_speed == 1 ? 1.0f : 0.5f)); - ALOGV(" remote_movementSideways: %f, remote_movementForward: %f", - remote_movementSideways, - remote_movementForward); + - - if (((secondaryButtonsNew & secondaryButton1) != - (secondaryButtonsOld & secondaryButton1)) && - (secondaryButtonsNew & secondaryButton1)) { - //Toggle walk/run somehow?! - vr.move_speed = (++vr.move_speed) % 3; + //X button invokes menu now + if ((secondaryButtonsNew & secondaryButton1) && + !(secondaryButtonsOld & secondaryButton1)) + { + Sys_QueEvent(0, SE_KEY, A_ESCAPE, true, 0, NULL); } //Open the datapad diff --git a/Projects/Android/jni/OpenJK/JKXR/VrInputWeaponAlign.cpp b/Projects/Android/jni/OpenJK/JKXR/VrInputWeaponAlign.cpp index 1bb5542..6a6b1c4 100644 --- a/Projects/Android/jni/OpenJK/JKXR/VrInputWeaponAlign.cpp +++ b/Projects/Android/jni/OpenJK/JKXR/VrInputWeaponAlign.cpp @@ -7,8 +7,6 @@ Authors : Simon Brown *************************************************************************************/ -#include - #include "VrInput.h" #include "VrCvars.h" diff --git a/Projects/Android/jni/OpenJK/JKXR/JKXR_SurfaceView.cpp b/Projects/Android/jni/OpenJK/JKXR/android/JKXR_SurfaceView.cpp similarity index 99% rename from Projects/Android/jni/OpenJK/JKXR/JKXR_SurfaceView.cpp rename to Projects/Android/jni/OpenJK/JKXR/android/JKXR_SurfaceView.cpp index c075a70..b143adb 100644 --- a/Projects/Android/jni/OpenJK/JKXR/JKXR_SurfaceView.cpp +++ b/Projects/Android/jni/OpenJK/JKXR/android/JKXR_SurfaceView.cpp @@ -320,6 +320,7 @@ void VR_Init() positional_movementForward = 0.0f; vr.snapTurn = 0.0f; vr.immersive_cinematics = true; + vr.move_speed = 1; // Default to full speed now //init randomiser srand(time(NULL)); diff --git a/Projects/Android/jni/OpenJK/JKXR/TBXR_Common.cpp b/Projects/Android/jni/OpenJK/JKXR/android/TBXR_Common.cpp similarity index 99% rename from Projects/Android/jni/OpenJK/JKXR/TBXR_Common.cpp rename to Projects/Android/jni/OpenJK/JKXR/android/TBXR_Common.cpp index 5838a27..81a2e67 100644 --- a/Projects/Android/jni/OpenJK/JKXR/TBXR_Common.cpp +++ b/Projects/Android/jni/OpenJK/JKXR/android/TBXR_Common.cpp @@ -1654,7 +1654,7 @@ void TBXR_InitialiseOpenXR() instanceInfo.next = NULL; OXR(xrGetInstanceProperties(gAppState.Instance, &instanceInfo)); ALOGV( - "Runtime %s: Version : %u.%u.%u", + "OpenXR Runtime %s: Version : %u.%u.%u", instanceInfo.runtimeName, XR_VERSION_MAJOR(instanceInfo.runtimeVersion), XR_VERSION_MINOR(instanceInfo.runtimeVersion), @@ -1894,11 +1894,11 @@ void TBXR_finishEyeBuffer(int eye ) void TBXR_updateProjections() { - XrViewLocateInfo projectionInfo = {}; - projectionInfo.type = XR_TYPE_VIEW_LOCATE_INFO; - projectionInfo.viewConfigurationType = gAppState.ViewportConfig.viewConfigurationType; - projectionInfo.displayTime = gAppState.FrameState.predictedDisplayTime; - projectionInfo.space = gAppState.LocalSpace; + XrViewLocateInfo viewLocateInfo = {}; + viewLocateInfo.type = XR_TYPE_VIEW_LOCATE_INFO; + viewLocateInfo.viewConfigurationType = gAppState.ViewportConfig.viewConfigurationType; + viewLocateInfo.displayTime = gAppState.FrameState.predictedDisplayTime; + viewLocateInfo.space = gAppState.LocalSpace; XrViewState viewState = {XR_TYPE_VIEW_STATE, NULL}; @@ -1907,7 +1907,7 @@ void TBXR_updateProjections() OXR(xrLocateViews( gAppState.Session, - &projectionInfo, + &viewLocateInfo, &viewState, projectionCapacityInput, &projectionCountOutput, diff --git a/Projects/Android/jni/OpenJK/JKXR/TBXR_Common.h b/Projects/Android/jni/OpenJK/JKXR/android/TBXR_Common.h similarity index 100% rename from Projects/Android/jni/OpenJK/JKXR/TBXR_Common.h rename to Projects/Android/jni/OpenJK/JKXR/android/TBXR_Common.h diff --git a/Projects/Android/jni/OpenJK/JKXR/argtable3.c b/Projects/Android/jni/OpenJK/JKXR/android/argtable3.c similarity index 100% rename from Projects/Android/jni/OpenJK/JKXR/argtable3.c rename to Projects/Android/jni/OpenJK/JKXR/android/argtable3.c diff --git a/Projects/Android/jni/OpenJK/JKXR/argtable3.h b/Projects/Android/jni/OpenJK/JKXR/android/argtable3.h similarity index 100% rename from Projects/Android/jni/OpenJK/JKXR/argtable3.h rename to Projects/Android/jni/OpenJK/JKXR/android/argtable3.h diff --git a/Projects/Android/jni/OpenJK/JKXR/windows/JKXR_SurfaceView.cpp b/Projects/Android/jni/OpenJK/JKXR/windows/JKXR_SurfaceView.cpp new file mode 100644 index 0000000..c32cbe3 --- /dev/null +++ b/Projects/Android/jni/OpenJK/JKXR/windows/JKXR_SurfaceView.cpp @@ -0,0 +1,487 @@ +#include +#include +#include +#include + +#include "VrInput.h" +#include "VrCvars.h" + + +#include +#include + + +#ifdef JK2_MODE +#include "game/weapons.h" +#else +#include "game/weapons.h" +#include "game/g_vehicles.h" +#endif + + +/* +================================================================================ + +JKXR Stuff + +================================================================================ +*/ + +bool VR_UseScreenLayer() +{ + vr.using_screen_layer = _UI_IsFullscreen() || + (bool)((vr.cin_camera && !vr.immersive_cinematics) || + vr.misc_camera || + (CL_IsRunningInGameCinematic() || CL_InGameCinematicOnStandBy()) || + (cls.state == CA_CINEMATIC) || + (cls.state == CA_LOADING) || + ( Key_GetCatcher( ) & KEYCATCH_UI ) || + ( Key_GetCatcher( ) & KEYCATCH_CONSOLE )); + + return vr.using_screen_layer; +} + +float VR_GetScreenLayerDistance() +{ + return (2.0f + vr_screen_dist->value); +} + + +void VR_SetHMDOrientation(float pitch, float yaw, float roll) +{ + //Orientation + VectorSet(vr.hmdorientation, pitch, yaw, roll); + VectorSubtract(vr.hmdorientation_last, vr.hmdorientation, vr.hmdorientation_delta); + + //Keep this for our records + VectorCopy(vr.hmdorientation, vr.hmdorientation_last); + + if (!vr.third_person && !vr.remote_npc && !vr.remote_turret +#ifndef JK2_MODE + && !vr.in_vehicle +#endif + ) + { + VectorCopy(vr.hmdorientation, vr.hmdorientation_first); + } + + if (!vr.remote_turret) + { + VectorCopy(vr.weaponangles[ANGLES_ADJUSTED], vr.weaponangles_first[ANGLES_ADJUSTED]); + } + + // View yaw delta + float clientview_yaw = vr.clientviewangles[YAW] - vr.hmdorientation[YAW]; + vr.clientview_yaw_delta = vr.clientview_yaw_last - clientview_yaw; + vr.clientview_yaw_last = clientview_yaw; + + // Max-height is set only once on start, or after re-calibration + // (ignore too low value which is sometimes provided on start) + if (!vr.maxHeight || vr.maxHeight < 1.0) { + vr.maxHeight = vr.hmdposition[1]; + } + + //GB Instantiate initial velocity + if(!vr.tempWeaponVelocity) + { + vr.tempWeaponVelocity = 400.0f; + } + + vr.curHeight = vr.hmdposition[1]; +} + +void VR_SetHMDPosition(float x, float y, float z ) +{ + static bool s_useScreen = qfalse; + + VectorSet(vr.hmdposition, x, y, z); + + //Can be set elsewhere + vr.take_snap |= s_useScreen != VR_UseScreenLayer(); + if (vr.take_snap) + { + s_useScreen = VR_UseScreenLayer(); + + //Record player position on transition + VectorSet(vr.hmdposition_snap, x, y, z); + VectorCopy(vr.hmdorientation, vr.hmdorientation_snap); + if (vr.cin_camera) + { + //Reset snap turn too if in a cinematic + vr.snapTurn = 0; + } + vr.take_snap = false; + } + + VectorSubtract(vr.hmdposition, vr.hmdposition_snap, vr.hmdposition_offset); + + //Position + VectorSubtract(vr.hmdposition_last, vr.hmdposition, vr.hmdposition_delta); + + //Keep this for our records + VectorCopy(vr.hmdposition, vr.hmdposition_last); +} + +void VR_GetMove(float *forward, float *side, float *pos_forward, float *pos_side, float *up, + float *yaw, float *pitch, float *roll) +{ + if (vr.remote_turret) { + *forward = 0.0f; + *pos_forward = 0.0f; + *up = 0.0f; + *side = 0.0f; + *pos_side = 0.0f; + *yaw = vr.snapTurn + vr.hmdorientation_first[YAW] + + vr.weaponangles[ANGLES_ADJUSTED][YAW] - vr.weaponangles_first[ANGLES_ADJUSTED][YAW]; + *pitch = vr.weaponangles[ANGLES_ADJUSTED][PITCH]; + *roll = 0.0f; + } + else if (vr.cgzoommode == 2 || vr.cgzoommode == 4) + { + *forward = 0.0f; + *pos_forward = 0.0f; + *up = 0.0f; + *side = 0.0f; + *pos_side = 0.0f; + *yaw = vr.snapTurn; + *pitch = vr.weaponangles[ANGLES_ADJUSTED][PITCH]; + *roll = 0.0f;//vr.hmdorientation[ROLL]; + } + else if (vr.remote_npc) { + *forward = remote_movementForward; + *pos_forward = 0.0f; + *up = 0.0f; + *side = remote_movementSideways; + *pos_side = 0.0f; + *yaw = vr.hmdorientation[YAW] + vr.snapTurn; + *pitch = vr.hmdorientation[PITCH]; + *roll = 0.0f; + } +#ifndef JK2_MODE + //Special code for JKA's vehicles + else if (vr.in_vehicle) { + //in vehicle + *forward = remote_movementForward; + *pos_forward = 0.0f; + *up = 0.0f; + *side = remote_movementSideways; + *pos_side = 0.0f; + if (vr_vehicle_use_hmd_direction->integer) + { + *yaw = vr.hmdorientation[YAW] + vr.snapTurn; + *pitch = vr.hmdorientation[PITCH]; + } + else + { + *yaw = vr.snapTurn + vr.hmdorientation_first[YAW]; + if (vr.vehicle_type == VH_FIGHTER || vr.vehicle_type == VH_FLIER) + { + *pitch = (vr.weaponangles[ANGLES_ADJUSTED][PITCH] + vr.offhandangles[ANGLES_ADJUSTED][PITCH]) / 2.0f; + } + else + { + *pitch = 0.0f; + } + } + *roll = 0.0f; + } +#endif + else if (!vr.third_person) { + *forward = remote_movementForward; + *pos_forward = positional_movementForward; + *up = remote_movementUp; + *side = remote_movementSideways; + + *pos_side = positional_movementSideways; + *yaw = vr.hmdorientation[YAW] + vr.snapTurn; + *pitch = vr.hmdorientation[PITCH]; + *roll = 0.0f;//vr.hmdorientation[ROLL]; + } else { + //in third person just send the bare minimum + *forward = remote_movementForward; + *pos_forward = 0.0f; + *up = 0.0f; + *side = remote_movementSideways; + *pos_side = 0.0f; + *yaw = vr.snapTurn + vr.hmdorientation_first[YAW]; + *pitch = 0.0f; + *roll = 0.0f; + } +} + + + +void VR_Init() +{ + GlInitExtensions(); + + //First - all the OpenXR stuff and nonsense + TBXR_InitialiseOpenXR(); + TBXR_EnterVR(); + TBXR_InitRenderer(); + TBXR_InitActions(); + TBXR_WaitForSessionActive(); + + //Initialise all our variables + remote_movementSideways = 0.0f; + remote_movementForward = 0.0f; + remote_movementUp = 0.0f; + positional_movementSideways = 0.0f; + positional_movementForward = 0.0f; + vr.snapTurn = 0.0f; + vr.immersive_cinematics = true; + vr.move_speed = 1; // Default to full speed now + + //init randomiser + srand(time(NULL)); + + //Create Cvars + vr_turn_mode = Cvar_Get( "vr_turn_mode", "0", CVAR_ARCHIVE); // 0 = snap, 1 = smooth (3rd person only), 2 = smooth (all modes) + vr_turn_angle = Cvar_Get( "vr_turn_angle", "45", CVAR_ARCHIVE); + vr_positional_factor = Cvar_Get( "vr_positional_factor", "12", CVAR_ARCHIVE); + vr_walkdirection = Cvar_Get( "vr_walkdirection", "1", CVAR_ARCHIVE); + vr_weapon_pitchadjust = Cvar_Get( "vr_weapon_pitchadjust", "-20.0", CVAR_ARCHIVE); + vr_virtual_stock = Cvar_Get( "vr_virtual_stock", "0", CVAR_ARCHIVE); + + //Defaults + vr_control_scheme = Cvar_Get( "vr_control_scheme", "0", CVAR_ARCHIVE); + vr_switch_sticks = Cvar_Get( "vr_switch_sticks", "0", CVAR_ARCHIVE); + + vr_immersive_cinematics = Cvar_Get("vr_immersive_cinematics", "1", CVAR_ARCHIVE); + vr_screen_dist = Cvar_Get( "vr_screen_dist", "3.5", CVAR_ARCHIVE); + vr_weapon_velocity_trigger = Cvar_Get( "vr_weapon_velocity_trigger", "2.0", CVAR_ARCHIVE); + vr_force_velocity_trigger = Cvar_Get( "vr_force_velocity_trigger", "2.09", CVAR_ARCHIVE); + vr_force_distance_trigger = Cvar_Get( "vr_force_distance_trigger", "0.15", CVAR_ARCHIVE); + vr_two_handed_weapons = Cvar_Get ("vr_two_handed_weapons", "1", CVAR_ARCHIVE); + vr_force_motion_controlled = Cvar_Get ("vr_force_motion_controlled", "1", CVAR_ARCHIVE); + vr_force_motion_push = Cvar_Get ("vr_force_motion_push", "3", CVAR_ARCHIVE); + vr_force_motion_pull = Cvar_Get ("vr_force_motion_pull", "4", CVAR_ARCHIVE); + vr_motion_enable_saber = Cvar_Get ("vr_motion_enable_saber", "0", CVAR_ARCHIVE); + vr_crouch_toggle = Cvar_Get ("vr_crouch_toggle", "0", CVAR_ARCHIVE); + vr_irl_crouch_enabled = Cvar_Get ("vr_irl_crouch_enabled", "0", CVAR_ARCHIVE); + vr_irl_crouch_to_stand_ratio = Cvar_Get ("vr_irl_crouch_to_stand_ratio", "0.65", CVAR_ARCHIVE); + vr_saber_block_debounce_time = Cvar_Get ("vr_saber_block_debounce_time", "200", CVAR_ARCHIVE); + vr_haptic_intensity = Cvar_Get ("vr_haptic_intensity", "1.0", CVAR_ARCHIVE); + vr_comfort_vignette = Cvar_Get ("vr_comfort_vignette", "0.0", CVAR_ARCHIVE); + vr_saber_3rdperson_mode = Cvar_Get ("vr_saber_3rdperson_mode", "1", CVAR_ARCHIVE); + vr_vehicle_use_hmd_direction = Cvar_Get ("vr_vehicle_use_hmd_direction", "0", CVAR_ARCHIVE); + vr_vehicle_use_3rd_person = Cvar_Get ("vr_vehicle_use_3rd_person", "0", CVAR_ARCHIVE); + vr_vehicle_use_controller_for_speed = Cvar_Get ("vr_vehicle_use_controller_for_speed", "1", CVAR_ARCHIVE); + vr_gesture_triggered_use = Cvar_Get ("vr_gesture_triggered_use", "1", CVAR_ARCHIVE); + vr_use_gesture_boundary = Cvar_Get ("vr_use_gesture_boundary", "0.35", CVAR_ARCHIVE); + vr_align_weapons = Cvar_Get ("vr_align_weapons", "0", CVAR_ARCHIVE); + vr_refresh = Cvar_Get ("vr_refresh", "72", CVAR_ARCHIVE); + + cvar_t *expanded_menu_enabled = Cvar_Get ("expanded_menu_enabled", "0", CVAR_ARCHIVE); + if (FS_FileExists("expanded_menu.pk3") || FS_BaseFileExists("expanded_menu.pk3")) { + Cvar_Set( "expanded_menu_enabled", "1" ); + } else { + Cvar_Set( "expanded_menu_enabled", "0" ); + } + + vr.menu_right_handed = vr_control_scheme->integer == 0; + + Cvar_Get ("openXRHMD", gAppState.OpenXRHMD, CVAR_ARCHIVE); +} + +int VR_SetRefreshRate(int refreshRate) +{ +// if (strstr(gAppState.OpenXRHMD, "meta") != NULL) +// { +// OXR(gAppState.pfnRequestDisplayRefreshRate(gAppState.Session, (float) refreshRate)); +// return refreshRate; +// } + + return 0; +} + +//All the stuff we want to do each frame specifically for this game +void VR_FrameSetup() +{ + static float refresh = 0; + if (refresh != vr_refresh->value) + { + refresh = vr_refresh->value; + VR_SetRefreshRate(vr_refresh->value); + } + + //get any cvar values required here + vr.immersive_cinematics = (vr_immersive_cinematics->value != 0.0f); +} + +bool VR_GetVRProjection(int eye, float zNear, float zFar, float zZoomX, float zZoomY, float* projection) +{ + //Don't use our projection if playing a cinematic and we are not immersive + if (vr.cin_camera && !vr.immersive_cinematics) + { + return false; + } + + //Just use game-calculated FOV when showing the quad screen + if (vr.using_screen_layer) + { + return false; + } + + XrFovf fov = gAppState.Views[eye].fov; + fov.angleLeft /= zZoomX; + fov.angleRight /= zZoomX; + fov.angleUp /= zZoomY; + fov.angleDown /= zZoomY; + + XrMatrix4x4f_CreateProjectionFov( + (XrMatrix4x4f*)projection, GRAPHICS_OPENGL, + fov, zNear, zFar); + + return true; +} + +void VR_ExternalHapticEvent(const char* event, int position, int flags, int intensity, float angle, float yHeight ) +{ +} + +void VR_HapticUpdateEvent(const char* event, int intensity, float angle ) +{ +} + +void VR_HapticEndFrame() +{ +} + +void VR_HapticStopEvent(const char* event) +{ +} + +void VR_HapticEnable() +{ + +} + +void VR_HapticDisable() +{ +} + +/* + * event - name of event + * position - for the use of external haptics providers to indicate which bit of haptic hardware should be triggered + * flags - a way for the code to specify which controller to produce haptics on, if 0 then weaponFireChannel is calculated in this function + * intensity - 0-100 + * angle - yaw angle (again for external haptics devices) to place the feedback correctly + * yHeight - for external haptics devices to place the feedback correctly + */ +void VR_HapticEvent(const char* event, int position, int flags, int intensity, float angle, float yHeight ) +{ + if (vr_haptic_intensity->value == 0.0f) + { + return; + } + + //Pass on to any external services + VR_ExternalHapticEvent(event, position, flags, intensity, angle, yHeight); + + float fIntensity = intensity / 100.0f; + + //Controller Haptic Support + int weaponFireChannel = vr.weapon_stabilised ? 3 : (vr_control_scheme->integer ? 2 : 1); + + if (flags != 0) + { + weaponFireChannel = flags; + } + if (strcmp(event, "pickup_shield") == 0 || + strcmp(event, "pickup_weapon") == 0 || + strstr(event, "pickup_item") != NULL) + { + TBXR_Vibrate(100, 3, 1.0); + } + else if (strcmp(event, "weapon_switch") == 0) + { + TBXR_Vibrate(250, vr_control_scheme->integer ? 2 : 1, 0.8); + } + else if (strcmp(event, "shotgun") == 0 || strcmp(event, "fireball") == 0) + { + TBXR_Vibrate(400, 3, fIntensity); + } + else if (strcmp(event, "bullet") == 0) + { + TBXR_Vibrate(150, 3, fIntensity); + } + else if (strcmp(event, "chainsaw_fire") == 0) // Saber + { + //Special handling for dual sabers / melee + if (vr.dualsabers || cl.frame.ps.weapon == WP_MELEE) + { + if (position == 4 || + position == 0) // both hands + { + weaponFireChannel = 3; + } + else if (position == 1) // left hand + { + weaponFireChannel = 2; + } + else if (position == 2) // right hand + { + weaponFireChannel = 1; + } + else + { + //no longer need to trigger haptic + return; + } + } + + TBXR_Vibrate(300, weaponFireChannel, fIntensity); + } + else if (strcmp(event, "RTCWQuest:fire_tesla") == 0) // Weapon power build up + { + TBXR_Vibrate(500, weaponFireChannel, fIntensity); + } + else if (strcmp(event, "machinegun_fire") == 0 || strcmp(event, "plasmagun_fire") == 0) + { + TBXR_Vibrate(90, weaponFireChannel, fIntensity); + } + else if (strcmp(event, "shotgun_fire") == 0) + { + TBXR_Vibrate(250, weaponFireChannel, fIntensity); + } + else if (strcmp(event, "rocket_fire") == 0 || + strcmp(event, "RTCWQuest:fire_sniper") == 0 || + strcmp(event, "bfg_fire") == 0 || + strcmp(event, "handgrenade_fire") == 0 ) + { + TBXR_Vibrate(400, weaponFireChannel, fIntensity); + } + else if (strcmp(event, "selector_icon") == 0 || + strcmp(event, "use_button") == 0 ) + { + //Quick blip + TBXR_Vibrate(50, flags, fIntensity); + } +} + +void VR_HandleControllerInput() { + TBXR_UpdateControllers(); + + //Call additional control schemes here + switch (vr_control_scheme->integer) + { + case RIGHT_HANDED_DEFAULT: + HandleInput_Default(&rightTrackedRemoteState_new, &rightTrackedRemoteState_old, &rightRemoteTracking_new, + &leftTrackedRemoteState_new, &leftTrackedRemoteState_old, &leftRemoteTracking_new, + xrButton_A, xrButton_B, xrButton_X, xrButton_Y); + break; + case LEFT_HANDED_DEFAULT: + HandleInput_Default(&leftTrackedRemoteState_new, &leftTrackedRemoteState_old, &leftRemoteTracking_new, + &rightTrackedRemoteState_new, &rightTrackedRemoteState_old, &rightRemoteTracking_new, + xrButton_X, xrButton_Y, xrButton_A, xrButton_B); + break; + case WEAPON_ALIGN: + HandleInput_WeaponAlign(&rightTrackedRemoteState_new, &rightTrackedRemoteState_old, &rightRemoteTracking_new, + &leftTrackedRemoteState_new, &leftTrackedRemoteState_old, &leftRemoteTracking_new, + xrButton_A, xrButton_B, xrButton_X, xrButton_Y); + break; + } +} + + diff --git a/Projects/Android/jni/OpenJK/JKXR/windows/TBXR_Common.cpp b/Projects/Android/jni/OpenJK/JKXR/windows/TBXR_Common.cpp new file mode 100644 index 0000000..bb861c1 --- /dev/null +++ b/Projects/Android/jni/OpenJK/JKXR/windows/TBXR_Common.cpp @@ -0,0 +1,1310 @@ +#include +#include +#include +#include + +#include "VrInput.h" +#include "VrCvars.h" + +#include + +#include "VrInput.h" +#include "VrCommon.h" + +#include + + + +//Let's go to the maximum! +int NUM_MULTI_SAMPLES = 2; +float SS_MULTIPLIER = 0.0f; + +GLboolean stageSupported = GL_FALSE; + + +const char* const requiredExtensionNames[] = { + XR_KHR_OPENGL_ENABLE_EXTENSION_NAME}; + + +const uint32_t numRequiredExtensions = + sizeof(requiredExtensionNames) / sizeof(requiredExtensionNames[0]); + + +PFNGLGENFRAMEBUFFERSPROC glGenFramebuffers; +PFNGLDELETEFRAMEBUFFERSPROC glDeleteFramebuffers; +PFNGLBINDFRAMEBUFFERPROC glBindFramebuffer; +PFNGLBLITFRAMEBUFFERPROC glBlitFramebuffer; +PFNGLGENRENDERBUFFERSPROC glGenRenderbuffers; +PFNGLDELETERENDERBUFFERSPROC glDeleteRenderbuffers; +PFNGLBINDRENDERBUFFERPROC glBindRenderbuffer; +PFNGLISRENDERBUFFERPROC glIsRenderbuffer; +PFNGLRENDERBUFFERSTORAGEPROC glRenderbufferStorage; +PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC glRenderbufferStorageMultisample; +PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC glRenderbufferStorageMultisampleEXT; +PFNGLFRAMEBUFFERRENDERBUFFERPROC glFramebufferRenderbuffer; +PFNGLFRAMEBUFFERTEXTURE2DPROC glFramebufferTexture2D; +PFNGLFRAMEBUFFERTEXTURELAYERPROC glFramebufferTextureLayer; +PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC glFramebufferTextureMultiviewOVR; +PFNGLCHECKFRAMEBUFFERSTATUSPROC glCheckFramebufferStatus; +PFNGLCHECKNAMEDFRAMEBUFFERSTATUSPROC glCheckNamedFramebufferStatus; + +void GlInitExtensions() { + glGenFramebuffers = (PFNGLGENFRAMEBUFFERSPROC)SDL_GL_GetProcAddress("glGenFramebuffers"); + glDeleteFramebuffers = (PFNGLDELETEFRAMEBUFFERSPROC)SDL_GL_GetProcAddress("glDeleteFramebuffers"); + glBindFramebuffer = (PFNGLBINDFRAMEBUFFERPROC)SDL_GL_GetProcAddress("glBindFramebuffer"); + glBlitFramebuffer = (PFNGLBLITFRAMEBUFFERPROC)SDL_GL_GetProcAddress("glBlitFramebuffer"); + glGenRenderbuffers = (PFNGLGENRENDERBUFFERSPROC)SDL_GL_GetProcAddress("glGenRenderbuffers"); + glDeleteRenderbuffers = (PFNGLDELETERENDERBUFFERSPROC)SDL_GL_GetProcAddress("glDeleteRenderbuffers"); + glBindRenderbuffer = (PFNGLBINDRENDERBUFFERPROC)SDL_GL_GetProcAddress("glBindRenderbuffer"); + glIsRenderbuffer = (PFNGLISRENDERBUFFERPROC)SDL_GL_GetProcAddress("glIsRenderbuffer"); + glRenderbufferStorage = (PFNGLRENDERBUFFERSTORAGEPROC)SDL_GL_GetProcAddress("glRenderbufferStorage"); + glRenderbufferStorageMultisample = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC)SDL_GL_GetProcAddress("glRenderbufferStorageMultisample"); + glRenderbufferStorageMultisampleEXT = + (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC)SDL_GL_GetProcAddress("glRenderbufferStorageMultisampleEXT"); + glFramebufferRenderbuffer = (PFNGLFRAMEBUFFERRENDERBUFFERPROC)SDL_GL_GetProcAddress("glFramebufferRenderbuffer"); + glFramebufferTexture2D = (PFNGLFRAMEBUFFERTEXTURE2DPROC)SDL_GL_GetProcAddress("glFramebufferTexture2D"); + glFramebufferTextureLayer = (PFNGLFRAMEBUFFERTEXTURELAYERPROC)SDL_GL_GetProcAddress("glFramebufferTextureLayer"); + glFramebufferTextureMultiviewOVR = (PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC)SDL_GL_GetProcAddress("glFramebufferTextureMultiviewOVR"); + glCheckFramebufferStatus = (PFNGLCHECKFRAMEBUFFERSTATUSPROC)SDL_GL_GetProcAddress("glCheckFramebufferStatus"); + glCheckNamedFramebufferStatus = (PFNGLCHECKNAMEDFRAMEBUFFERSTATUSPROC)SDL_GL_GetProcAddress("glCheckNamedFramebufferStatus"); +} + +/* +================================================================================ + +System Clock Time in millis + +================================================================================ +*/ + +double TBXR_GetTimeInMilliSeconds() +{ + return GetTickCount64(); +} + +int runStatus = -1; +void TBXR_exit(int exitCode) +{ + runStatus = exitCode; +} + + +/* +================================================================================ + +ovrFramebuffer + +================================================================================ +*/ + +static void ovrFramebuffer_Clear(ovrFramebuffer* frameBuffer) { + frameBuffer->Width = 0; + frameBuffer->Height = 0; + frameBuffer->TextureSwapChainLength = 0; + frameBuffer->TextureSwapChainIndex = 0; + frameBuffer->ColorSwapChain.Handle = XR_NULL_HANDLE; + frameBuffer->ColorSwapChain.Width = 0; + frameBuffer->ColorSwapChain.Height = 0; + frameBuffer->ColorSwapChainImage = NULL; + frameBuffer->DepthBuffers = NULL; + frameBuffer->FrameBuffers = NULL; +} + +void TBXR_ClearFrameBuffer(int width, int height); + +static bool ovrFramebuffer_Create( + XrSession session, + ovrFramebuffer* frameBuffer, + const GLenum colorFormat, + const int width, + const int height) { + + frameBuffer->Width = width; + frameBuffer->Height = height; + + XrSwapchainCreateInfo swapChainCreateInfo; + memset(&swapChainCreateInfo, 0, sizeof(swapChainCreateInfo)); + swapChainCreateInfo.type = XR_TYPE_SWAPCHAIN_CREATE_INFO; + swapChainCreateInfo.usageFlags = XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT; + swapChainCreateInfo.mipCount = 1; + swapChainCreateInfo.format = colorFormat; + swapChainCreateInfo.sampleCount = 1; + swapChainCreateInfo.width = width; + swapChainCreateInfo.height = height; + swapChainCreateInfo.faceCount = 1; + swapChainCreateInfo.arraySize = 1; + + frameBuffer->ColorSwapChain.Width = swapChainCreateInfo.width; + frameBuffer->ColorSwapChain.Height = swapChainCreateInfo.height; + + + // Create the swapchain. + OXR(xrCreateSwapchain(session, &swapChainCreateInfo, &frameBuffer->ColorSwapChain.Handle)); + // Get the number of swapchain images. + OXR(xrEnumerateSwapchainImages( + frameBuffer->ColorSwapChain.Handle, 0, &frameBuffer->TextureSwapChainLength, NULL)); + // Allocate the swapchain images array. + frameBuffer->ColorSwapChainImage = (XrSwapchainImageOpenGLKHR*)malloc( + frameBuffer->TextureSwapChainLength * sizeof(XrSwapchainImageOpenGLKHR)); + + // Populate the swapchain image array. + for (uint32_t i = 0; i < frameBuffer->TextureSwapChainLength; i++) { + frameBuffer->ColorSwapChainImage[i].type = XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_KHR; + frameBuffer->ColorSwapChainImage[i].next = NULL; + } + OXR(xrEnumerateSwapchainImages( + frameBuffer->ColorSwapChain.Handle, + frameBuffer->TextureSwapChainLength, + &frameBuffer->TextureSwapChainLength, + (XrSwapchainImageBaseHeader*)frameBuffer->ColorSwapChainImage)); + + frameBuffer->DepthBuffers = + (GLuint*)malloc(frameBuffer->TextureSwapChainLength * sizeof(GLuint)); + frameBuffer->FrameBuffers = + (GLuint*)malloc(frameBuffer->TextureSwapChainLength * sizeof(GLuint)); + + for (uint32_t i = 0; i < frameBuffer->TextureSwapChainLength; i++) { + // Create the color buffer texture. + const GLuint colorTexture = frameBuffer->ColorSwapChainImage[i].image; + + // Create the frame buffer. + frameBuffer->FrameBuffers[i] = 0; + GL(glGenFramebuffers(1, &frameBuffer->FrameBuffers[i])); + + { + GLint width; + GLint height; + glBindTexture(GL_TEXTURE_2D, colorTexture); + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &width); + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &height); + TBXR_ClearFrameBuffer(width, height); + + glGenTextures(1, &frameBuffer->DepthBuffers[i]); + glBindTexture(GL_TEXTURE_2D, frameBuffer->DepthBuffers[i]); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32, width, height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, nullptr); + glBindTexture(GL_TEXTURE_2D,0); + } + } + + return true; +} + +void ovrFramebuffer_Destroy(ovrFramebuffer* frameBuffer) { + GL(glDeleteFramebuffers(frameBuffer->TextureSwapChainLength, frameBuffer->FrameBuffers)); + GL(glDeleteTextures(frameBuffer->TextureSwapChainLength, frameBuffer->DepthBuffers)); + OXR(xrDestroySwapchain(frameBuffer->ColorSwapChain.Handle)); + free(frameBuffer->ColorSwapChainImage); + + free(frameBuffer->DepthBuffers); + free(frameBuffer->FrameBuffers); +} + +void ovrFramebuffer_SetCurrent(ovrFramebuffer* frameBuffer) { + glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer->FrameBuffers[frameBuffer->TextureSwapChainIndex]); + + const GLuint colorTexture = frameBuffer->ColorSwapChainImage[frameBuffer->TextureSwapChainIndex].image; + const uint32_t depthTexture = frameBuffer->DepthBuffers[frameBuffer->TextureSwapChainIndex]; + + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTexture, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthTexture, 0); +} + +void ovrFramebuffer_SetNone() { + GL(glBindFramebuffer(GL_FRAMEBUFFER, 0)); +} + +PFNGLBLITNAMEDFRAMEBUFFERPROC glBlitNamedFramebuffer = NULL; +extern cvar_t* r_mode; +qboolean R_GetModeInfo(int* width, int* height, int mode); + +void ovrFramebuffer_Resolve(ovrFramebuffer* frameBuffer) { + + if (glBlitNamedFramebuffer == NULL) + { + glBlitNamedFramebuffer = (PFNGLBLITNAMEDFRAMEBUFFERPROC)SDL_GL_GetProcAddress("glBlitNamedFramebuffer"); + + } + + const GLuint colorTexture = frameBuffer->ColorSwapChainImage[frameBuffer->TextureSwapChainIndex].image; + + int width, height; + R_GetModeInfo(&width, &height, r_mode->integer); + + glBlitNamedFramebuffer((GLuint)colorTexture, // readFramebuffer + (GLuint)0, // backbuffer // drawFramebuffer + (GLint)0, // srcX0 + (GLint)0, // srcY0 + (GLint)gAppState.Width, // srcX1 + (GLint)gAppState.Height, // srcY1 + (GLint)0, // dstX0 + (GLint)0, // dstY0 + (GLint)width, // dstX1 + (GLint)height, // dstY1 + (GLbitfield)GL_COLOR_BUFFER_BIT, // mask + (GLenum)GL_LINEAR); // filter +} + +void ovrFramebuffer_Acquire(ovrFramebuffer* frameBuffer) { + // Acquire the swapchain image + XrSwapchainImageAcquireInfo acquireInfo = {XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO, NULL}; + OXR(xrAcquireSwapchainImage( + frameBuffer->ColorSwapChain.Handle, &acquireInfo, &frameBuffer->TextureSwapChainIndex)); + + XrSwapchainImageWaitInfo waitInfo; + waitInfo.type = XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO; + waitInfo.next = NULL; + waitInfo.timeout = 1000000000; /* timeout in nanoseconds */ + XrResult res = xrWaitSwapchainImage(frameBuffer->ColorSwapChain.Handle, &waitInfo); + int i = 0; + while (res == XR_TIMEOUT_EXPIRED) { + res = xrWaitSwapchainImage(frameBuffer->ColorSwapChain.Handle, &waitInfo); + i++; + ALOGV( + " Retry xrWaitSwapchainImage %d times due to XR_TIMEOUT_EXPIRED (duration %f seconds)", + i, + waitInfo.timeout * (1E-9)); + } +} + +void ovrFramebuffer_Release(ovrFramebuffer* frameBuffer) { + XrSwapchainImageReleaseInfo releaseInfo = {XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO, NULL}; + OXR(xrReleaseSwapchainImage(frameBuffer->ColorSwapChain.Handle, &releaseInfo)); +} + + +/* +================================================================================ + +ovrRenderer + +================================================================================ +*/ + + +void ovrRenderer_Create( + XrSession session, + ovrRenderer* renderer, + int suggestedEyeTextureWidth, + int suggestedEyeTextureHeight) { + // Create the frame buffers. + for (int eye = 0; eye < ovrMaxNumEyes; eye++) { + ovrFramebuffer_Create( + session, + &renderer->FrameBuffer[eye], + GL_SRGB8_ALPHA8, + suggestedEyeTextureWidth, + suggestedEyeTextureHeight); + } + + ovrFramebuffer_Create( + session, + &renderer->NullFrameBuffer, + GL_SRGB8_ALPHA8, + suggestedEyeTextureWidth, + suggestedEyeTextureHeight); +} + +void ovrRenderer_Destroy(ovrRenderer* renderer) { + for (int eye = 0; eye < ovrMaxNumEyes; eye++) { + ovrFramebuffer_Destroy(&renderer->FrameBuffer[eye]); + } +} + + + +/* +================================================================================ + +ovrMatrix4f + +================================================================================ +*/ + +ovrMatrix4f ovrMatrix4f_CreateFromQuaternion(const XrQuaternionf* q) { + const float ww = q->w * q->w; + const float xx = q->x * q->x; + const float yy = q->y * q->y; + const float zz = q->z * q->z; + + ovrMatrix4f out; + out.M[0][0] = ww + xx - yy - zz; + out.M[0][1] = 2 * (q->x * q->y - q->w * q->z); + out.M[0][2] = 2 * (q->x * q->z + q->w * q->y); + out.M[0][3] = 0; + + out.M[1][0] = 2 * (q->x * q->y + q->w * q->z); + out.M[1][1] = ww - xx + yy - zz; + out.M[1][2] = 2 * (q->y * q->z - q->w * q->x); + out.M[1][3] = 0; + + out.M[2][0] = 2 * (q->x * q->z - q->w * q->y); + out.M[2][1] = 2 * (q->y * q->z + q->w * q->x); + out.M[2][2] = ww - xx - yy + zz; + out.M[2][3] = 0; + + out.M[3][0] = 0; + out.M[3][1] = 0; + out.M[3][2] = 0; + out.M[3][3] = 1; + return out; +} + + +/// Use left-multiplication to accumulate transformations. +ovrMatrix4f ovrMatrix4f_Multiply(const ovrMatrix4f* a, const ovrMatrix4f* b) { + ovrMatrix4f out; + out.M[0][0] = a->M[0][0] * b->M[0][0] + a->M[0][1] * b->M[1][0] + a->M[0][2] * b->M[2][0] + + a->M[0][3] * b->M[3][0]; + out.M[1][0] = a->M[1][0] * b->M[0][0] + a->M[1][1] * b->M[1][0] + a->M[1][2] * b->M[2][0] + + a->M[1][3] * b->M[3][0]; + out.M[2][0] = a->M[2][0] * b->M[0][0] + a->M[2][1] * b->M[1][0] + a->M[2][2] * b->M[2][0] + + a->M[2][3] * b->M[3][0]; + out.M[3][0] = a->M[3][0] * b->M[0][0] + a->M[3][1] * b->M[1][0] + a->M[3][2] * b->M[2][0] + + a->M[3][3] * b->M[3][0]; + + out.M[0][1] = a->M[0][0] * b->M[0][1] + a->M[0][1] * b->M[1][1] + a->M[0][2] * b->M[2][1] + + a->M[0][3] * b->M[3][1]; + out.M[1][1] = a->M[1][0] * b->M[0][1] + a->M[1][1] * b->M[1][1] + a->M[1][2] * b->M[2][1] + + a->M[1][3] * b->M[3][1]; + out.M[2][1] = a->M[2][0] * b->M[0][1] + a->M[2][1] * b->M[1][1] + a->M[2][2] * b->M[2][1] + + a->M[2][3] * b->M[3][1]; + out.M[3][1] = a->M[3][0] * b->M[0][1] + a->M[3][1] * b->M[1][1] + a->M[3][2] * b->M[2][1] + + a->M[3][3] * b->M[3][1]; + + out.M[0][2] = a->M[0][0] * b->M[0][2] + a->M[0][1] * b->M[1][2] + a->M[0][2] * b->M[2][2] + + a->M[0][3] * b->M[3][2]; + out.M[1][2] = a->M[1][0] * b->M[0][2] + a->M[1][1] * b->M[1][2] + a->M[1][2] * b->M[2][2] + + a->M[1][3] * b->M[3][2]; + out.M[2][2] = a->M[2][0] * b->M[0][2] + a->M[2][1] * b->M[1][2] + a->M[2][2] * b->M[2][2] + + a->M[2][3] * b->M[3][2]; + out.M[3][2] = a->M[3][0] * b->M[0][2] + a->M[3][1] * b->M[1][2] + a->M[3][2] * b->M[2][2] + + a->M[3][3] * b->M[3][2]; + + out.M[0][3] = a->M[0][0] * b->M[0][3] + a->M[0][1] * b->M[1][3] + a->M[0][2] * b->M[2][3] + + a->M[0][3] * b->M[3][3]; + out.M[1][3] = a->M[1][0] * b->M[0][3] + a->M[1][1] * b->M[1][3] + a->M[1][2] * b->M[2][3] + + a->M[1][3] * b->M[3][3]; + out.M[2][3] = a->M[2][0] * b->M[0][3] + a->M[2][1] * b->M[1][3] + a->M[2][2] * b->M[2][3] + + a->M[2][3] * b->M[3][3]; + out.M[3][3] = a->M[3][0] * b->M[0][3] + a->M[3][1] * b->M[1][3] + a->M[3][2] * b->M[2][3] + + a->M[3][3] * b->M[3][3]; + return out; +} + +ovrMatrix4f ovrMatrix4f_CreateRotation(const float radiansX, const float radiansY, const float radiansZ) { + const float sinX = sinf(radiansX); + const float cosX = cosf(radiansX); + const ovrMatrix4f rotationX = { + {{1, 0, 0, 0}, {0, cosX, -sinX, 0}, {0, sinX, cosX, 0}, {0, 0, 0, 1}}}; + const float sinY = sinf(radiansY); + const float cosY = cosf(radiansY); + const ovrMatrix4f rotationY = { + {{cosY, 0, sinY, 0}, {0, 1, 0, 0}, {-sinY, 0, cosY, 0}, {0, 0, 0, 1}}}; + const float sinZ = sinf(radiansZ); + const float cosZ = cosf(radiansZ); + const ovrMatrix4f rotationZ = { + {{cosZ, -sinZ, 0, 0}, {sinZ, cosZ, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}}}; + const ovrMatrix4f rotationXY = ovrMatrix4f_Multiply(&rotationY, &rotationX); + return ovrMatrix4f_Multiply(&rotationZ, &rotationXY); +} + +XrVector4f XrVector4f_MultiplyMatrix4f(const ovrMatrix4f* a, const XrVector4f* v) { + XrVector4f out; + out.x = a->M[0][0] * v->x + a->M[0][1] * v->y + a->M[0][2] * v->z + a->M[0][3] * v->w; + out.y = a->M[1][0] * v->x + a->M[1][1] * v->y + a->M[1][2] * v->z + a->M[1][3] * v->w; + out.z = a->M[2][0] * v->x + a->M[2][1] * v->y + a->M[2][2] * v->z + a->M[2][3] * v->w; + out.w = a->M[3][0] * v->x + a->M[3][1] * v->y + a->M[3][2] * v->z + a->M[3][3] * v->w; + return out; +} + +#ifndef EPSILON +#define EPSILON 0.001f +#endif + +static XrVector3f normalizeVec(XrVector3f vec) { + //NOTE: leave w-component untouched + //@@const float EPSILON = 0.000001f; + float xxyyzz = vec.x*vec.x + vec.y*vec.y + vec.z*vec.z; + //@@if(xxyyzz < EPSILON) + //@@ return *this; // do nothing if it is zero vector + + //float invLength = invSqrt(xxyyzz); + XrVector3f result; + float invLength = 1.0f / sqrtf(xxyyzz); + result.x = vec.x * invLength; + result.y = vec.y * invLength; + result.z = vec.z * invLength; + return result; +} + +void NormalizeAngles(vec3_t angles) +{ + while (angles[0] >= 90) angles[0] -= 180; + while (angles[1] >= 180) angles[1] -= 360; + while (angles[2] >= 180) angles[2] -= 360; + while (angles[0] < -90) angles[0] += 180; + while (angles[1] < -180) angles[1] += 360; + while (angles[2] < -180) angles[2] += 360; +} + +void GetAnglesFromVectors(const XrVector3f forward, const XrVector3f right, const XrVector3f up, vec3_t angles) +{ + float sr, sp, sy, cr, cp, cy; + + sp = -forward.z; + + float cp_x_cy = forward.x; + float cp_x_sy = forward.y; + float cp_x_sr = -right.z; + float cp_x_cr = up.z; + + float yaw = atan2(cp_x_sy, cp_x_cy); + float roll = atan2(cp_x_sr, cp_x_cr); + + cy = cos(yaw); + sy = sin(yaw); + cr = cos(roll); + sr = sin(roll); + + if (fabs(cy) > EPSILON) + { + cp = cp_x_cy / cy; + } + else if (fabs(sy) > EPSILON) + { + cp = cp_x_sy / sy; + } + else if (fabs(sr) > EPSILON) + { + cp = cp_x_sr / sr; + } + else if (fabs(cr) > EPSILON) + { + cp = cp_x_cr / cr; + } + else + { + cp = cos(asin(sp)); + } + + float pitch = atan2(sp, cp); + + angles[0] = pitch / (M_PI*2.f / 360.f); + angles[1] = yaw / (M_PI*2.f / 360.f); + angles[2] = roll / (M_PI*2.f / 360.f); + + NormalizeAngles(angles); +} + + +void QuatToYawPitchRoll(XrQuaternionf q, vec3_t rotation, vec3_t out) { + + ovrMatrix4f mat = ovrMatrix4f_CreateFromQuaternion( &q ); + + if (rotation[0] != 0.0f || rotation[1] != 0.0f || rotation[2] != 0.0f) + { + ovrMatrix4f rot = ovrMatrix4f_CreateRotation(DEG2RAD(rotation[0]), DEG2RAD(rotation[1]), DEG2RAD(rotation[2])); + mat = ovrMatrix4f_Multiply(&mat, &rot); + } + + XrVector4f v1 = {0, 0, -1, 0}; + XrVector4f v2 = {1, 0, 0, 0}; + XrVector4f v3 = {0, 1, 0, 0}; + + XrVector4f forwardInVRSpace = XrVector4f_MultiplyMatrix4f(&mat, &v1); + XrVector4f rightInVRSpace = XrVector4f_MultiplyMatrix4f(&mat, &v2); + XrVector4f upInVRSpace = XrVector4f_MultiplyMatrix4f(&mat, &v3); + + XrVector3f forward = {-forwardInVRSpace.z, -forwardInVRSpace.x, forwardInVRSpace.y}; + XrVector3f right = {-rightInVRSpace.z, -rightInVRSpace.x, rightInVRSpace.y}; + XrVector3f up = {-upInVRSpace.z, -upInVRSpace.x, upInVRSpace.y}; + + XrVector3f forwardNormal = normalizeVec(forward); + XrVector3f rightNormal = normalizeVec(right); + XrVector3f upNormal = normalizeVec(up); + + GetAnglesFromVectors(forwardNormal, rightNormal, upNormal, out); +} + +/* +======================== +TBXR_Vibrate +======================== +*/ + +void TBXR_Vibrate( int duration, int chan, float intensity ); + +/* +================================================================================ + +ovrRenderThread + +================================================================================ +*/ + + + +void ovrApp_Clear(ovrApp* app) { + app->Focused = false; + app->Instance = XR_NULL_HANDLE; + app->Session = XR_NULL_HANDLE; + memset(&app->ViewportConfig, 0, sizeof(XrViewConfigurationProperties)); + memset(&app->ViewConfigurationView, 0, ovrMaxNumEyes * sizeof(XrViewConfigurationView)); + app->SystemId = XR_NULL_SYSTEM_ID; + + app->LocalSpace = XR_NULL_HANDLE; + app->ViewSpace = XR_NULL_HANDLE; + app->StageSpace = XR_NULL_HANDLE; + + app->SessionActive = false; + app->SupportedDisplayRefreshRates = NULL; + app->RequestedDisplayRefreshRateIndex = 0; + app->NumSupportedDisplayRefreshRates = 0; + app->pfnGetDisplayRefreshRate = NULL; + app->pfnRequestDisplayRefreshRate = NULL; + app->SwapInterval = 1; + app->MainThreadTid = 0; + app->RenderThreadTid = 0; +} + + + +void ovrApp_HandleSessionStateChanges(ovrApp* app, XrSessionState state) { + if (state == XR_SESSION_STATE_READY) { + assert(app->SessionActive == false); + + XrSessionBeginInfo sessionBeginInfo; + memset(&sessionBeginInfo, 0, sizeof(sessionBeginInfo)); + sessionBeginInfo.type = XR_TYPE_SESSION_BEGIN_INFO; + sessionBeginInfo.next = NULL; + sessionBeginInfo.primaryViewConfigurationType = app->ViewportConfig.viewConfigurationType; + + XrResult result; + OXR(result = xrBeginSession(app->Session, &sessionBeginInfo)); + + app->SessionActive = (result == XR_SUCCESS); + } else if (state == XR_SESSION_STATE_STOPPING) { + assert(app->SessionActive); + + OXR(xrEndSession(app->Session)); + app->SessionActive = false; + } +} + +GLboolean ovrApp_HandleXrEvents(ovrApp* app) { + XrEventDataBuffer eventDataBuffer = {}; + GLboolean recenter = GL_FALSE; + + // Poll for events + for (;;) { + XrEventDataBaseHeader* baseEventHeader = (XrEventDataBaseHeader*)(&eventDataBuffer); + baseEventHeader->type = XR_TYPE_EVENT_DATA_BUFFER; + baseEventHeader->next = NULL; + XrResult r; + OXR(r = xrPollEvent(app->Instance, &eventDataBuffer)); + if (r != XR_SUCCESS) { + break; + } + + switch (baseEventHeader->type) { + case XR_TYPE_EVENT_DATA_EVENTS_LOST: + ALOGV("xrPollEvent: received XR_TYPE_EVENT_DATA_EVENTS_LOST event"); + break; + case XR_TYPE_EVENT_DATA_INSTANCE_LOSS_PENDING: { + const XrEventDataInstanceLossPending* instance_loss_pending_event = + (XrEventDataInstanceLossPending*)(baseEventHeader); + ALOGV( + "xrPollEvent: received XR_TYPE_EVENT_DATA_INSTANCE_LOSS_PENDING event: time %f", + FromXrTime(instance_loss_pending_event->lossTime)); + } break; + case XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED: + ALOGV("xrPollEvent: received XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED event"); + break; + case XR_TYPE_EVENT_DATA_PERF_SETTINGS_EXT: { + const XrEventDataPerfSettingsEXT* perf_settings_event = + (XrEventDataPerfSettingsEXT*)(baseEventHeader); + ALOGV( + "xrPollEvent: received XR_TYPE_EVENT_DATA_PERF_SETTINGS_EXT event: type %d subdomain %d : level %d -> level %d", + perf_settings_event->type, + perf_settings_event->subDomain, + perf_settings_event->fromLevel, + perf_settings_event->toLevel); + } break; + case XR_TYPE_EVENT_DATA_DISPLAY_REFRESH_RATE_CHANGED_FB: { + const XrEventDataDisplayRefreshRateChangedFB* refresh_rate_changed_event = + (XrEventDataDisplayRefreshRateChangedFB*)(baseEventHeader); + ALOGV( + "xrPollEvent: received XR_TYPE_EVENT_DATA_DISPLAY_REFRESH_RATE_CHANGED_FB event: fromRate %f -> toRate %f", + refresh_rate_changed_event->fromDisplayRefreshRate, + refresh_rate_changed_event->toDisplayRefreshRate); + } break; + case XR_TYPE_EVENT_DATA_REFERENCE_SPACE_CHANGE_PENDING: { + XrEventDataReferenceSpaceChangePending* ref_space_change_event = + (XrEventDataReferenceSpaceChangePending*)(baseEventHeader); + ALOGV( + "xrPollEvent: received XR_TYPE_EVENT_DATA_REFERENCE_SPACE_CHANGE_PENDING event: changed space: %d for session %p at time %f", + ref_space_change_event->referenceSpaceType, + (void*)ref_space_change_event->session, + FromXrTime(ref_space_change_event->changeTime)); + recenter = GL_TRUE; + } break; + case XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED: { + const XrEventDataSessionStateChanged* session_state_changed_event = + (XrEventDataSessionStateChanged*)(baseEventHeader); + ALOGV( + "xrPollEvent: received XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED: %d for session %p at time %f", + session_state_changed_event->state, + (void*)session_state_changed_event->session, + FromXrTime(session_state_changed_event->time)); + + switch (session_state_changed_event->state) { + case XR_SESSION_STATE_FOCUSED: + app->Focused = true; + break; + case XR_SESSION_STATE_VISIBLE: + app->Focused = false; + break; + case XR_SESSION_STATE_READY: + case XR_SESSION_STATE_STOPPING: + ovrApp_HandleSessionStateChanges(app, session_state_changed_event->state); + break; + default: + break; + } + } break; + default: + ALOGV("xrPollEvent: Unknown event"); + break; + } + } + return recenter; +} + + +ovrApp gAppState; +bool destroyed = qfalse; + +void TBXR_GetScreenRes(int *width, int *height) +{ + *width = gAppState.Width; + *height = gAppState.Height; +} + +XrInstance TBXR_GetXrInstance() { + return gAppState.Instance; +} + +static void TBXR_ProcessMessageQueue() { + +} + +void ovrTrackedController_Clear(ovrTrackedController* controller) { + controller->Active = false; + controller->Pose = XrPosef_Identity(); +} + +void TBXR_InitialiseResolution() +{ + // Enumerate the viewport configurations. + uint32_t viewportConfigTypeCount = 0; + OXR(xrEnumerateViewConfigurations( + gAppState.Instance, gAppState.SystemId, 0, &viewportConfigTypeCount, NULL)); + + XrViewConfigurationType* viewportConfigurationTypes = + (XrViewConfigurationType*)malloc(viewportConfigTypeCount * sizeof(XrViewConfigurationType)); + + OXR(xrEnumerateViewConfigurations( + gAppState.Instance, + gAppState.SystemId, + viewportConfigTypeCount, + &viewportConfigTypeCount, + viewportConfigurationTypes)); + + ALOGV("Available Viewport Configuration Types: %d", viewportConfigTypeCount); + + for (uint32_t i = 0; i < viewportConfigTypeCount; i++) { + const XrViewConfigurationType viewportConfigType = viewportConfigurationTypes[i]; + + ALOGV( + "Viewport configuration type %d : %s", + viewportConfigType, + viewportConfigType == XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO ? "Selected" : ""); + + XrViewConfigurationProperties viewportConfig; + viewportConfig.type = XR_TYPE_VIEW_CONFIGURATION_PROPERTIES; + viewportConfig.next = NULL; + OXR(xrGetViewConfigurationProperties( + gAppState.Instance, gAppState.SystemId, viewportConfigType, &viewportConfig)); + ALOGV( + "FovMutable=%s ConfigurationType %d", + viewportConfig.fovMutable ? "true" : "false", + viewportConfig.viewConfigurationType); + + uint32_t viewCount; + OXR(xrEnumerateViewConfigurationViews( + gAppState.Instance, gAppState.SystemId, viewportConfigType, 0, &viewCount, NULL)); + + if (viewCount > 0) { + XrViewConfigurationView* elements = + (XrViewConfigurationView*)malloc(viewCount * sizeof(XrViewConfigurationView)); + + for (uint32_t e = 0; e < viewCount; e++) { + elements[e].type = XR_TYPE_VIEW_CONFIGURATION_VIEW; + elements[e].next = NULL; + } + + OXR(xrEnumerateViewConfigurationViews( + gAppState.Instance, + gAppState.SystemId, + viewportConfigType, + viewCount, + &viewCount, + elements)); + + // Cache the view config properties for the selected config type. + if (viewportConfigType == XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO) { + assert(viewCount == ovrMaxNumEyes); + for (uint32_t e = 0; e < viewCount; e++) { + gAppState.ViewConfigurationView[e] = elements[e]; + } + } + + free(elements); + } else { + ALOGE("Empty viewport configuration type: %d", viewCount); + } + } + + free(viewportConfigurationTypes); + + //Shortcut to width and height + gAppState.Width = gAppState.ViewConfigurationView[0].recommendedImageRectWidth; + gAppState.Height = gAppState.ViewConfigurationView[0].recommendedImageRectHeight; +} + +void TBXR_EnterVR( ) { + + if (gAppState.Session) { + Com_Printf("TBXR_EnterVR called with existing session"); + return; + } + + // Create the OpenXR Session. + XrGraphicsBindingOpenGLWin32KHR graphicsBindingAndroidOpenGL = {}; + graphicsBindingAndroidOpenGL.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_WIN32_KHR; + graphicsBindingAndroidOpenGL.next = NULL; + graphicsBindingAndroidOpenGL.hDC = wglGetCurrentDC(); + graphicsBindingAndroidOpenGL.hGLRC = wglGetCurrentContext(); + + XrSessionCreateInfo sessionCreateInfo = {}; + memset(&sessionCreateInfo, 0, sizeof(sessionCreateInfo)); + sessionCreateInfo.type = XR_TYPE_SESSION_CREATE_INFO; + sessionCreateInfo.next = &graphicsBindingAndroidOpenGL; + sessionCreateInfo.createFlags = 0; + sessionCreateInfo.systemId = gAppState.SystemId; + + XrResult initResult; + OXR(initResult = xrCreateSession(gAppState.Instance, &sessionCreateInfo, &gAppState.Session)); + if (initResult != XR_SUCCESS) { + ALOGE("Failed to create XR session: %d.", initResult); + exit(1); + } + + // Create a space to the first path + XrReferenceSpaceCreateInfo spaceCreateInfo = {}; + spaceCreateInfo.type = XR_TYPE_REFERENCE_SPACE_CREATE_INFO; + spaceCreateInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_VIEW; + spaceCreateInfo.poseInReferenceSpace.orientation.w = 1.0f; + OXR(xrCreateReferenceSpace(gAppState.Session, &spaceCreateInfo, &gAppState.ViewSpace)); +} + +void TBXR_LeaveVR( ) { + if (gAppState.Session) { + OXR(xrDestroySpace(gAppState.ViewSpace)); + + if (gAppState.StageSpace != XR_NULL_HANDLE) { + OXR(xrDestroySpace(gAppState.StageSpace)); + } + + if (gAppState.LocalSpace != XR_NULL_HANDLE) { + OXR(xrDestroySpace(gAppState.LocalSpace)); + } + + OXR(xrDestroySession(gAppState.Session)); + gAppState.Session = NULL; + } + + ovrRenderer_Destroy( &gAppState.Renderer ); +} + +void TBXR_InitRenderer( ) { + // Get the viewport configuration info for the chosen viewport configuration type. + gAppState.ViewportConfig.type = XR_TYPE_VIEW_CONFIGURATION_PROPERTIES; + + OXR(xrGetViewConfigurationProperties( + gAppState.Instance, gAppState.SystemId, XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO, &gAppState.ViewportConfig)); + + + + + uint32_t numOutputSpaces = 0; + OXR(xrEnumerateReferenceSpaces(gAppState.Session, 0, &numOutputSpaces, NULL)); + + XrReferenceSpaceType* referenceSpaces = + (XrReferenceSpaceType*)malloc(numOutputSpaces * sizeof(XrReferenceSpaceType)); + + OXR(xrEnumerateReferenceSpaces( + gAppState.Session, numOutputSpaces, &numOutputSpaces, referenceSpaces)); + + for (uint32_t i = 0; i < numOutputSpaces; i++) { + if (referenceSpaces[i] == XR_REFERENCE_SPACE_TYPE_STAGE) { + stageSupported = GL_TRUE; + break; + } + } + + free(referenceSpaces); + + TBXR_Recenter(); + + gAppState.Views = (XrView*)(malloc(ovrMaxNumEyes * sizeof(XrView))); + for (int eye = 0; eye < ovrMaxNumEyes; eye++) { + memset(&gAppState.Views[eye], 0, sizeof(XrView)); + gAppState.Views[eye].type = XR_TYPE_VIEW; + } + + ovrRenderer_Create( + gAppState.Session, + &gAppState.Renderer, + gAppState.ViewConfigurationView[0].recommendedImageRectWidth, + gAppState.ViewConfigurationView[0].recommendedImageRectHeight); +} + +void VR_DestroyRenderer( ) +{ + ovrRenderer_Destroy(&gAppState.Renderer); + free(gAppState.Views); +} + +void TBXR_InitialiseOpenXR() +{ + //First, find out which HMD we are using + gAppState.OpenXRHMD = "meta";// (char*)getenv("OPENXR_HMD"); + + + // Create the OpenXR instance. + XrApplicationInfo appInfo; + memset(&appInfo, 0, sizeof(appInfo)); + strcpy(appInfo.applicationName, "JKXR"); + appInfo.applicationVersion = 0; + strcpy(appInfo.engineName, "JKXR"); + appInfo.engineVersion = 0; + appInfo.apiVersion = XR_CURRENT_API_VERSION; + + XrInstanceCreateInfo instanceCreateInfo; + memset(&instanceCreateInfo, 0, sizeof(instanceCreateInfo)); + instanceCreateInfo.type = XR_TYPE_INSTANCE_CREATE_INFO; + instanceCreateInfo.next = NULL; + instanceCreateInfo.createFlags = 0; + instanceCreateInfo.applicationInfo = appInfo; + instanceCreateInfo.enabledApiLayerCount = 0; + instanceCreateInfo.enabledApiLayerNames = NULL; + instanceCreateInfo.enabledExtensionCount = numRequiredExtensions; + instanceCreateInfo.enabledExtensionNames = requiredExtensionNames; + + XrResult initResult; + OXR(initResult = xrCreateInstance(&instanceCreateInfo, &gAppState.Instance)); + if (initResult != XR_SUCCESS) { + ALOGE("Failed to create XR instance: %d.", initResult); + exit(1); + } + + XrInstanceProperties instanceInfo; + instanceInfo.type = XR_TYPE_INSTANCE_PROPERTIES; + instanceInfo.next = NULL; + OXR(xrGetInstanceProperties(gAppState.Instance, &instanceInfo)); + ALOGV( + "OpenXR Runtime %s: Version : %u.%u.%u", + instanceInfo.runtimeName, + XR_VERSION_MAJOR(instanceInfo.runtimeVersion), + XR_VERSION_MINOR(instanceInfo.runtimeVersion), + XR_VERSION_PATCH(instanceInfo.runtimeVersion)); + + XrSystemGetInfo systemGetInfo; + memset(&systemGetInfo, 0, sizeof(systemGetInfo)); + systemGetInfo.type = XR_TYPE_SYSTEM_GET_INFO; + systemGetInfo.next = NULL; + systemGetInfo.formFactor = XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY; + + OXR(initResult = xrGetSystem(gAppState.Instance, &systemGetInfo, &gAppState.SystemId)); + if (initResult != XR_SUCCESS) { + ALOGE("Failed to get system."); + exit(1); + } + + // Get the graphics requirements. + PFN_xrGetOpenGLGraphicsRequirementsKHR pfnGetOpenGLGraphicsRequirementsKHR = NULL; + OXR(xrGetInstanceProcAddr( + gAppState.Instance, + "xrGetOpenGLGraphicsRequirementsKHR", + (PFN_xrVoidFunction * )(&pfnGetOpenGLGraphicsRequirementsKHR))); + + XrGraphicsRequirementsOpenGLKHR graphicsRequirements = {}; + graphicsRequirements.type = XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_KHR; + OXR(pfnGetOpenGLGraphicsRequirementsKHR(gAppState.Instance, gAppState.SystemId, + &graphicsRequirements)); + + TBXR_InitialiseResolution(); + + gAppState.Initialised = true; +} + +void TBXR_Recenter() { + + // Calculate recenter reference + XrReferenceSpaceCreateInfo spaceCreateInfo = {}; + spaceCreateInfo.type = XR_TYPE_REFERENCE_SPACE_CREATE_INFO; + spaceCreateInfo.poseInReferenceSpace.orientation.w = 1.0f; + if (gAppState.StageSpace != XR_NULL_HANDLE) { + vec3_t rotation = {0, 0, 0}; + XrSpaceLocation loc = {}; + loc.type = XR_TYPE_SPACE_LOCATION; + OXR(xrLocateSpace(gAppState.ViewSpace, gAppState.StageSpace, gAppState.FrameState.predictedDisplayTime, &loc)); + QuatToYawPitchRoll(loc.pose.orientation, rotation, vr.hmdorientation); + } + + // Delete previous space instances + if (gAppState.StageSpace != XR_NULL_HANDLE) { + OXR(xrDestroySpace(gAppState.StageSpace)); + } + + spaceCreateInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_LOCAL; + OXR(xrCreateReferenceSpace(gAppState.Session, &spaceCreateInfo, &gAppState.LocalSpace)); + + spaceCreateInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_STAGE; + OXR(xrCreateReferenceSpace(gAppState.Session, &spaceCreateInfo, &gAppState.StageSpace)); + ALOGV("Created stage space"); +} + +void TBXR_WaitForSessionActive() +{//Now wait for the session to be ready + while (!gAppState.SessionActive) { + TBXR_ProcessMessageQueue(); + if (ovrApp_HandleXrEvents(&gAppState)) { + TBXR_Recenter(); + } + } +} + +static void TBXR_GetHMDOrientation() { + + if (gAppState.FrameState.predictedDisplayTime == 0) + { + return; + } + + // Get the HMD pose, predicted for the middle of the time period during which + // the new eye images will be displayed. The number of frames predicted ahead + // depends on the pipeline depth of the engine and the synthesis rate. + // The better the prediction, the less black will be pulled in at the edges. + XrSpaceLocation loc = {}; + loc.type = XR_TYPE_SPACE_LOCATION; + OXR(xrLocateSpace(gAppState.ViewSpace, gAppState.StageSpace, gAppState.FrameState.predictedDisplayTime, &loc)); + gAppState.xfStageFromHead = loc.pose; + + const XrQuaternionf quatHmd = gAppState.xfStageFromHead.orientation; + const XrVector3f positionHmd = gAppState.xfStageFromHead.position; + + vec3_t rotation = {0, 0, 0}; + vec3_t hmdorientation = {0, 0, 0}; + QuatToYawPitchRoll(quatHmd, rotation, hmdorientation); + VR_SetHMDPosition(positionHmd.x, positionHmd.y, positionHmd.z); + VR_SetHMDOrientation(hmdorientation[0], hmdorientation[1], hmdorientation[2]); +} + + +//All the stuff we want to do each frame +void TBXR_FrameSetup() +{ + if (!gAppState.Initialised) + { + return; + } + + if (gAppState.FrameSetup) + { + return; + } + + while (!destroyed) + { + TBXR_ProcessMessageQueue(); + + if (ovrApp_HandleXrEvents(&gAppState)) + { + TBXR_Recenter(); + } + + if (gAppState.SessionActive == GL_FALSE) + { + continue; + } + + break; + } + + if (destroyed) + { + TBXR_LeaveVR(); + + exit(0); // in case Java doesn't do the job + } + + + // NOTE: OpenXR does not use the concept of frame indices. Instead, + // XrWaitFrame returns the predicted display time. + //XrFrameWaitInfo waitFrameInfo = {}; + //waitFrameInfo.type = XR_TYPE_FRAME_WAIT_INFO; + //waitFrameInfo.next = NULL; + + memset(&gAppState.FrameState, 0, sizeof(XrFrameState)); + gAppState.FrameState.type = XR_TYPE_FRAME_STATE; + OXR(xrWaitFrame(gAppState.Session, NULL, &gAppState.FrameState)); + + // Get the HMD pose, predicted for the middle of the time period during which + // the new eye images will be displayed. The number of frames predicted ahead + // depends on the pipeline depth of the engine and the synthesis rate. + // The better the prediction, the less black will be pulled in at the edges. + XrFrameBeginInfo beginFrameDesc = {}; + beginFrameDesc.type = XR_TYPE_FRAME_BEGIN_INFO; + beginFrameDesc.next = NULL; + OXR(xrBeginFrame(gAppState.Session, &beginFrameDesc)); + + //Game specific frame setup stuff called here + VR_FrameSetup(); + + //Get controller state here + TBXR_GetHMDOrientation(); + VR_HandleControllerInput(); + + TBXR_ProcessHaptics(); + + gAppState.FrameSetup = true; +} + +int TBXR_GetRefresh() +{ + return gAppState.currentDisplayRefreshRate ? gAppState.currentDisplayRefreshRate : 90; +} + +#define GL_FRAMEBUFFER_SRGB 0x8DB9 + +void TBXR_ClearFrameBuffer(int width, int height) +{ + glEnable( GL_SCISSOR_TEST ); + glViewport( 0, 0, width, height ); + + //Black + glClearColor( 0.0f, 0.0f, 0.0f, 1.0f ); + + glScissor( 0, 0, width, height ); + glClear( GL_COLOR_BUFFER_BIT ); + + glScissor( 0, 0, 0, 0 ); + glDisable( GL_SCISSOR_TEST ); + + //This is a bit of a hack, but we need to do this to correct for the fact that the engine uses linear RGB colorspace + //but openxr uses SRGB (or something, must admit I don't really understand, but adding this works to make it look good again) + glDisable( GL_FRAMEBUFFER_SRGB ); +} + +void TBXR_prepareEyeBuffer(int eye ) +{ + vr.eye = eye; + ovrFramebuffer* frameBuffer = &(gAppState.Renderer.FrameBuffer[eye]); + ovrFramebuffer_Acquire(frameBuffer); + ovrFramebuffer_SetCurrent(frameBuffer); + TBXR_ClearFrameBuffer(frameBuffer->ColorSwapChain.Width, frameBuffer->ColorSwapChain.Height); + + ovrFramebuffer_Acquire(&gAppState.Renderer.NullFrameBuffer); + + //Seems odd, but used to move the HUD elements to be central on the player's view + //HMDs with a symmetric fov (like the PICO) will have 0 in this value, but the Meta Quest + //will have an asymmetric fov and the HUD would be very misaligned as a result + vr.off_center_fov = -(gAppState.Views[eye].fov.angleLeft + gAppState.Views[eye].fov.angleRight) / 2.0f; +} + +void WIN_SwapWindow(); + +void TBXR_finishEyeBuffer(int eye ) +{ + ovrRenderer *renderer = &gAppState.Renderer; + + ovrFramebuffer *frameBuffer = &(renderer->FrameBuffer[eye]); + + // Clear the alpha channel, other way OpenXR would not transfer the framebuffer fully + glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE); + glClearColor(0.0, 0.0, 0.0, 1.0); + glClear(GL_COLOR_BUFFER_BIT); + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + + ovrFramebuffer_Release(&gAppState.Renderer.NullFrameBuffer); + + ovrFramebuffer_SetNone(); + + if (eye == 0) + { + ovrFramebuffer_Resolve(frameBuffer); + + WIN_SwapWindow(); + + //Clear the back buffer so we don't randomly have a little screen copy showing up + glViewport(0, 0, gAppState.Width, gAppState.Height); + glClearColor(0.0, 0.0, 0.0, 1.0); + glClear(GL_COLOR_BUFFER_BIT); + } + + ovrFramebuffer_Release(frameBuffer); +} + +void TBXR_updateProjections() +{ + XrViewLocateInfo viewLocateInfo = {}; + viewLocateInfo.type = XR_TYPE_VIEW_LOCATE_INFO; + viewLocateInfo.viewConfigurationType = gAppState.ViewportConfig.viewConfigurationType; + viewLocateInfo.displayTime = gAppState.FrameState.predictedDisplayTime; + viewLocateInfo.space = gAppState.LocalSpace; + + XrViewState viewState = {XR_TYPE_VIEW_STATE, NULL}; + + uint32_t projectionCapacityInput = ovrMaxNumEyes; + uint32_t projectionCountOutput = projectionCapacityInput; + + OXR(xrLocateViews( + gAppState.Session, + &viewLocateInfo, + &viewState, + projectionCapacityInput, + &projectionCountOutput, + gAppState.Views)); +} + +void TBXR_submitFrame() +{ + if (gAppState.SessionActive == GL_FALSE) { + return; + } + + TBXR_updateProjections(); + + //Calculate the maximum extent fov for use in culling in the engine (we won't want to cull inside this fov) + vr.fov_x = (fabs(gAppState.Views[0].fov.angleLeft) + fabs(gAppState.Views[1].fov.angleLeft)) * 180.0f / M_PI; + vr.fov_y = (fabs(gAppState.Views[0].fov.angleUp) + fabs(gAppState.Views[0].fov.angleUp)) * 180.0f / M_PI; + + + XrFrameEndInfo endFrameInfo = {}; + endFrameInfo.type = XR_TYPE_FRAME_END_INFO; + endFrameInfo.displayTime = gAppState.FrameState.predictedDisplayTime; + endFrameInfo.environmentBlendMode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE; + + const XrCompositionLayerBaseHeader* layers[ovrMaxLayerCount] = {}; + int layerCount = 0; + endFrameInfo.layers = layers; + + XrCompositionLayerProjection projection_layer; + XrCompositionLayerProjectionView projection_layer_elements[2] = {}; + XrCompositionLayerQuad quad_layer; + + if (!VR_UseScreenLayer()) + { + memset(&projection_layer, 0, sizeof(XrCompositionLayerProjection)); + projection_layer.type = XR_TYPE_COMPOSITION_LAYER_PROJECTION; + projection_layer.layerFlags = XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT; + projection_layer.layerFlags |= XR_COMPOSITION_LAYER_CORRECT_CHROMATIC_ABERRATION_BIT; + projection_layer.space = gAppState.LocalSpace; + projection_layer.viewCount = ovrMaxNumEyes; + projection_layer.views = projection_layer_elements; + + for (int eye = 0; eye < ovrMaxNumEyes; eye++) + { + XrFovf fov = gAppState.Views[eye].fov; + if (vr.cgzoommode) + { + fov.angleLeft *= 1.2f; + fov.angleRight *= 1.2f; + fov.angleUp *= 1.2f; + fov.angleDown *= 1.2f; + } + + memset(&projection_layer_elements[eye], 0, sizeof(XrCompositionLayerProjectionView)); + projection_layer_elements[eye].type = XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW; + projection_layer_elements[eye].pose = gAppState.Views[eye].pose; + projection_layer_elements[eye].fov = fov; + projection_layer_elements[eye].subImage.swapchain = gAppState.Renderer.FrameBuffer[eye].ColorSwapChain.Handle; + projection_layer_elements[eye].subImage.imageRect.extent.width = gAppState.Renderer.FrameBuffer[eye].ColorSwapChain.Width; + projection_layer_elements[eye].subImage.imageRect.extent.height = gAppState.Renderer.FrameBuffer[eye].ColorSwapChain.Height; + } + + // Compose the layers for this frame. + layers[layerCount++] = (const XrCompositionLayerBaseHeader*)&projection_layer; + } + else + { + //Empty black projection for now + memset(&projection_layer, 0, sizeof(XrCompositionLayerProjection)); + projection_layer.type = XR_TYPE_COMPOSITION_LAYER_PROJECTION; + projection_layer.layerFlags = XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT; + projection_layer.space = gAppState.LocalSpace; + projection_layer.viewCount = ovrMaxNumEyes; + projection_layer.views = projection_layer_elements; + + for (int eye = 0; eye < ovrMaxNumEyes; eye++) + { + memset(&projection_layer_elements[eye], 0, sizeof(XrCompositionLayerProjectionView)); + projection_layer_elements[eye].type = XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW; + projection_layer_elements[eye].pose = gAppState.Views[eye].pose; + projection_layer_elements[eye].fov = gAppState.Views[eye].fov; + projection_layer_elements[eye].subImage.swapchain = gAppState.Renderer.NullFrameBuffer.ColorSwapChain.Handle; + projection_layer_elements[eye].subImage.imageRect.extent.width = gAppState.Renderer.NullFrameBuffer.ColorSwapChain.Width; + projection_layer_elements[eye].subImage.imageRect.extent.height = gAppState.Renderer.NullFrameBuffer.ColorSwapChain.Height; + } + + // Compose the layers for this frame. + layers[layerCount++] = (const XrCompositionLayerBaseHeader*)&projection_layer; + + memset(&quad_layer, 0, sizeof(XrCompositionLayerQuad)); + + // Build the quad layers + int32_t width = gAppState.Renderer.FrameBuffer[0].ColorSwapChain.Width; + int32_t height = gAppState.Renderer.FrameBuffer[0].ColorSwapChain.Height; + quad_layer.type = XR_TYPE_COMPOSITION_LAYER_QUAD; + quad_layer.layerFlags = XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT; + quad_layer.space = gAppState.StageSpace; + quad_layer.eyeVisibility =XR_EYE_VISIBILITY_BOTH; + quad_layer.subImage.swapchain = gAppState.Renderer.FrameBuffer[0].ColorSwapChain.Handle; + quad_layer.subImage.imageRect.extent.width = width; + quad_layer.subImage.imageRect.extent.height = height; + const XrVector3f axis = { 0.0f, 1.0f, 0.0f }; + XrVector3f pos = { + gAppState.xfStageFromHead.position.x - sin(DEG2RAD(vr.hmdorientation_snap[YAW])) * VR_GetScreenLayerDistance(), + 1.0f, + gAppState.xfStageFromHead.position.z - cos(DEG2RAD(vr.hmdorientation_snap[YAW])) * VR_GetScreenLayerDistance() + }; + quad_layer.pose.orientation = XrQuaternionf_CreateFromVectorAngle(axis, DEG2RAD(vr.hmdorientation_snap[YAW])); + quad_layer.pose.position = pos; + XrExtent2Df size = { 6.0f, 5.5f }; + quad_layer.size = size; + + layers[layerCount++] = (const XrCompositionLayerBaseHeader*)&quad_layer; + } + + + endFrameInfo.layerCount = layerCount; + OXR(xrEndFrame(gAppState.Session, &endFrameInfo)); + + gAppState.FrameSetup = false; +} diff --git a/Projects/Android/jni/OpenJK/JKXR/windows/TBXR_Common.h b/Projects/Android/jni/OpenJK/JKXR/windows/TBXR_Common.h new file mode 100644 index 0000000..b38cad3 --- /dev/null +++ b/Projects/Android/jni/OpenJK/JKXR/windows/TBXR_Common.h @@ -0,0 +1,271 @@ +#if !defined(tbxr_common_h) +#define tbxr_common_h + +#if defined(_WIN32) + +// OpenXR Header +#include +#include +#include +#include +#include + +//#define GL_GLEXT_PROTOTYPES +#define GL_EXT_color_subtable +#include +#include + +extern PFNGLGENFRAMEBUFFERSPROC glGenFramebuffers; +extern PFNGLDELETEFRAMEBUFFERSPROC glDeleteFramebuffers; +extern PFNGLBINDFRAMEBUFFERPROC glBindFramebuffer; +extern PFNGLBLITFRAMEBUFFERPROC glBlitFramebuffer; +extern PFNGLGENRENDERBUFFERSPROC glGenRenderbuffers; +extern PFNGLDELETERENDERBUFFERSPROC glDeleteRenderbuffers; +extern PFNGLBINDRENDERBUFFERPROC glBindRenderbuffer; +extern PFNGLISRENDERBUFFERPROC glIsRenderbuffer; +extern PFNGLRENDERBUFFERSTORAGEPROC glRenderbufferStorage; +extern PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC glRenderbufferStorageMultisample; +extern PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC glRenderbufferStorageMultisampleEXT; +extern PFNGLFRAMEBUFFERRENDERBUFFERPROC glFramebufferRenderbuffer; +extern PFNGLFRAMEBUFFERTEXTURE2DPROC glFramebufferTexture2D; +extern PFNGLFRAMEBUFFERTEXTURELAYERPROC glFramebufferTextureLayer; +extern PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC glFramebufferTextureMultiviewOVR; +extern PFNGLCHECKFRAMEBUFFERSTATUSPROC glCheckFramebufferStatus; +extern PFNGLCHECKNAMEDFRAMEBUFFERSTATUSPROC glCheckNamedFramebufferStatus; + +void GlInitExtensions(); + +#define GL_RGBA16F 0x881A + +#endif + +#ifndef NDEBUG +#define DEBUG 1 +#endif + +#define LOG_TAG "TBXR" + + +#define ALOGE(...) Com_Printf(__VA_ARGS__) + +#if DEBUG +#define ALOGV(...) Com_Printf(__VA_ARGS__) +#else +#define ALOGV(...) +#endif + + +enum { ovrMaxLayerCount = 3 }; +enum { ovrMaxNumEyes = 2 }; + +typedef enum xrButton_ { + xrButton_A = 0x00000001, // Set for trigger pulled on the Gear VR and Go Controllers + xrButton_B = 0x00000002, + xrButton_RThumb = 0x00000004, + xrButton_RShoulder = 0x00000008, + xrButton_X = 0x00000100, + xrButton_Y = 0x00000200, + xrButton_LThumb = 0x00000400, + xrButton_LShoulder = 0x00000800, + xrButton_Up = 0x00010000, + xrButton_Down = 0x00020000, + xrButton_Left = 0x00040000, + xrButton_Right = 0x00080000, + xrButton_Enter = 0x00100000, + xrButton_Back = 0x00200000, + xrButton_GripTrigger = 0x04000000, + xrButton_Trigger = 0x20000000, + xrButton_Joystick = 0x80000000, + + //Define additional controller touch points (not button presses) + xrButton_ThumbRest = 0x00000010, + + xrButton_EnumSize = 0x7fffffff +} xrButton; + +typedef struct { + uint32_t Buttons; + uint32_t Touches; + float IndexTrigger; + float GripTrigger; + XrVector2f Joystick; +} ovrInputStateTrackedRemote; + +typedef struct { + GLboolean Active; + XrPosef Pose; + XrSpaceVelocity Velocity; +} ovrTrackedController; + +typedef enum control_scheme { + RIGHT_HANDED_DEFAULT = 0, + LEFT_HANDED_DEFAULT = 10, + WEAPON_ALIGN = 99 +} control_scheme_t; + +typedef struct { + float M[4][4]; +} ovrMatrix4f; + + +typedef struct { + XrSwapchain Handle; + uint32_t Width; + uint32_t Height; +} ovrSwapChain; + +typedef struct { + int Width; + int Height; + + uint32_t TextureSwapChainLength; + uint32_t TextureSwapChainIndex; + ovrSwapChain ColorSwapChain; + XrSwapchainImageOpenGLKHR* ColorSwapChainImage; + GLuint* DepthBuffers; + GLuint* FrameBuffers; +} ovrFramebuffer; + +/* +================================================================================ + +ovrRenderer + +================================================================================ +*/ + +typedef struct +{ + ovrFramebuffer FrameBuffer[ovrMaxNumEyes]; + ovrFramebuffer NullFrameBuffer; // Used to draw black projection view when showing quad layer +} ovrRenderer; + + + +#define GL(func) func; + +// Forward declarations +XrInstance TBXR_GetXrInstance(); + +#if defined(DEBUG) +static void +OXR_CheckErrors(XrInstance instance, XrResult result, const char* function, bool failOnError) { + if (XR_FAILED(result)) { + char errorBuffer[XR_MAX_RESULT_STRING_SIZE]; + xrResultToString(instance, result, errorBuffer); + if (failOnError) { + //ALOGE("OpenXR error: %s: %s\n", function, errorBuffer); + } else { + //ALOGV("OpenXR error: %s: %s\n", function, errorBuffer); + } + } +} +#endif + +#if defined(DEBUG) +#define OXR(func) OXR_CheckErrors(TBXR_GetXrInstance(), func, #func, true); +#else +#define OXR(func) func; +#endif + + +typedef struct +{ + bool Initialised; + bool Resumed; + bool Focused; + bool FrameSetup; + char* OpenXRHMD; + + float Width; + float Height; + + XrInstance Instance; + XrSession Session; + XrViewConfigurationProperties ViewportConfig; + XrViewConfigurationView ViewConfigurationView[ovrMaxNumEyes]; + XrSystemId SystemId; + + XrSpace LocalSpace; + XrSpace ViewSpace; + XrSpace StageSpace; + + GLboolean SessionActive; + XrPosef xfStageFromHead; + XrView* Views; + XrMatrix4x4f ProjectionMatrices[2]; + + + float currentDisplayRefreshRate; + float* SupportedDisplayRefreshRates; + uint32_t RequestedDisplayRefreshRateIndex; + uint32_t NumSupportedDisplayRefreshRates; + PFN_xrGetDisplayRefreshRateFB pfnGetDisplayRefreshRate; + PFN_xrRequestDisplayRefreshRateFB pfnRequestDisplayRefreshRate; + + XrFrameState FrameState; + int SwapInterval; + int MainThreadTid; + int RenderThreadTid; + + ovrRenderer Renderer; + ovrTrackedController TrackedController[2]; +} ovrApp; + + +enum +{ + MESSAGE_ON_CREATE, + MESSAGE_ON_START, + MESSAGE_ON_RESUME, + MESSAGE_ON_PAUSE, + MESSAGE_ON_STOP, + MESSAGE_ON_DESTROY, + MESSAGE_ON_SURFACE_CREATED, + MESSAGE_ON_SURFACE_DESTROYED +}; + +extern ovrApp gAppState; + + +void ovrTrackedController_Clear(ovrTrackedController* controller); + +void * AppThreadFunction(void * parm ); + + +//Functions that need to be implemented by the game specific code +void VR_FrameSetup(); +bool VR_UseScreenLayer(); +float VR_GetScreenLayerDistance(); +bool VR_GetVRProjection(int eye, float zNear, float zFar, float zZoomX, float zZoomY, float* projection); +void VR_HandleControllerInput(); +void VR_SetHMDOrientation(float pitch, float yaw, float roll ); +void VR_SetHMDPosition(float x, float y, float z ); +void VR_HapticEvent(const char* event, int position, int flags, int intensity, float angle, float yHeight ); +void VR_HapticUpdateEvent(const char* event, int intensity, float angle ); +void VR_HapticEndFrame(); +void VR_HapticStopEvent(const char* event); +void VR_HapticEnable(); +void VR_HapticDisable(); + + +//Reusable Team Beef OpenXR stuff (in TBXR_Common.cpp) +double TBXR_GetTimeInMilliSeconds(); +int TBXR_GetRefresh(); +void TBXR_Recenter(); +void TBXR_InitialiseOpenXR(); +void TBXR_WaitForSessionActive(); +void TBXR_InitRenderer(); +void TBXR_EnterVR(); +void TBXR_GetScreenRes(int *width, int *height); +void TBXR_InitActions( void ); +void TBXR_Vibrate(int duration, int channel, float intensity ); +void TBXR_ProcessHaptics(); +void TBXR_FrameSetup(); +void TBXR_updateProjections(); +void TBXR_UpdateControllers( ); +void TBXR_prepareEyeBuffer(int eye ); +void TBXR_finishEyeBuffer(int eye ); +void TBXR_submitFrame(); + +#endif //vrcommon_h \ No newline at end of file diff --git a/Projects/Android/jni/OpenJK/code/CMakeLists.txt b/Projects/Android/jni/OpenJK/code/CMakeLists.txt index 9aee6fc..9eaac8e 100644 --- a/Projects/Android/jni/OpenJK/code/CMakeLists.txt +++ b/Projects/Android/jni/OpenJK/code/CMakeLists.txt @@ -340,10 +340,10 @@ if(BuildSPEngine OR BuildJK2SPEngine) # JKXR files set(SPEngineJKXRFiles - "${CMAKE_SOURCE_DIR}/JKXR/JKXR_SurfaceView.cpp" + "${CMAKE_SOURCE_DIR}/JKXR/windows/JKXR_SurfaceView.cpp" + "${CMAKE_SOURCE_DIR}/JKXR/windows/TBXR_Common.cpp" + "${CMAKE_SOURCE_DIR}/JKXR/windows/TBXR_Common.h" "${CMAKE_SOURCE_DIR}/JKXR/OpenXrInput.cpp" - "${CMAKE_SOURCE_DIR}/JKXR/TBXR_Common.cpp" - "${CMAKE_SOURCE_DIR}/JKXR/TBXR_Common.h" "${CMAKE_SOURCE_DIR}/JKXR/VrClientInfo.h" "${CMAKE_SOURCE_DIR}/JKXR/VrCommon.h" "${CMAKE_SOURCE_DIR}/JKXR/VrInputCommon.cpp"