diff --git a/CMakeLists.txt b/CMakeLists.txt index 09cae88753..11a68a3ed9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1086,8 +1086,12 @@ if(ANDROID) Common/VR/VRFramebuffer.h Common/VR/VRInput.cpp Common/VR/VRInput.h + Common/VR/VRMath.cpp + Common/VR/VRMath.h Common/VR/VRRenderer.cpp Common/VR/VRRenderer.h + Common/VR/VRTweaks.cpp + Common/VR/VRTweaks.h ) set(nativeExtraLibs ${nativeExtraLibs} openxr) endif() diff --git a/Common/GPU/OpenGL/GLRenderManager.cpp b/Common/GPU/OpenGL/GLRenderManager.cpp index afff7a7b40..3fcca9eaf8 100644 --- a/Common/GPU/OpenGL/GLRenderManager.cpp +++ b/Common/GPU/OpenGL/GLRenderManager.cpp @@ -9,6 +9,7 @@ #include "Common/Math/math_util.h" #ifdef OPENXR +#include "Core/Config.h" #include "VR/VRBase.h" #include "VR/VRRenderer.h" #endif @@ -204,6 +205,19 @@ bool GLRenderManager::ThreadFrame() { return false; #ifdef OPENXR VR_BeginFrame(VR_GetEngine()); + + // Decide if the scene is 3D or not + if (g_Config.bEnableVR && !VR_GetConfig(VR_CONFIG_FORCE_2D) && (VR_GetConfig(VR_CONFIG_3D_GEOMETRY_COUNT) > 15)) { + VR_SetConfig(VR_CONFIG_MODE, VR_MODE_MONO_6DOF); + } else { + VR_SetConfig(VR_CONFIG_MODE, VR_MODE_FLAT_SCREEN); + } + VR_SetConfig(VR_CONFIG_3D_GEOMETRY_COUNT, VR_GetConfig(VR_CONFIG_3D_GEOMETRY_COUNT) / 2); + + // Set customizations + VR_SetConfig(VR_CONFIG_6DOF_ENABLED, g_Config.bEnable6DoF); + VR_SetConfig(VR_CONFIG_CANVAS_DISTANCE, g_Config.iCanvasDistance); + VR_SetConfig(VR_CONFIG_FOV_SCALE, g_Config.iFieldOfViewPercentage); #endif // In case of syncs or other partial completion, we keep going until we complete a frame. diff --git a/Common/VR/VRFramebuffer.cpp b/Common/VR/VRFramebuffer.cpp index 3b15a90c08..be535e606c 100644 --- a/Common/VR/VRFramebuffer.cpp +++ b/Common/VR/VRFramebuffer.cpp @@ -12,6 +12,10 @@ #include #include +double FromXrTime(const XrTime time) { + return (time * 1e-9); +} + /* ================================================================================ @@ -20,7 +24,6 @@ ovrFramebuffer ================================================================================ */ - void ovrFramebuffer_Clear(ovrFramebuffer* frameBuffer) { frameBuffer->Width = 0; frameBuffer->Height = 0; @@ -343,193 +346,3 @@ int ovrApp_HandleXrEvents(ovrApp* app) { } return recenter; } - -/* -================================================================================ - -ovrMatrix4f - -================================================================================ -*/ - -ovrMatrix4f ovrMatrix4f_CreateProjectionFov( - const float angleLeft, - const float angleRight, - const float angleUp, - const float angleDown, - const float nearZ, - const float farZ) { - - const float tanAngleLeft = tanf(angleLeft); - const float tanAngleRight = tanf(angleRight); - - const float tanAngleDown = tanf(angleDown); - const float tanAngleUp = tanf(angleUp); - - const float tanAngleWidth = tanAngleRight - tanAngleLeft; - - // Set to tanAngleDown - tanAngleUp for a clip space with positive Y - // down (Vulkan). Set to tanAngleUp - tanAngleDown for a clip space with - // positive Y up (OpenGL / D3D / Metal). - const float tanAngleHeight = tanAngleUp - tanAngleDown; - - // Set to nearZ for a [-1,1] Z clip space (OpenGL / OpenGL ES). - // Set to zero for a [0,1] Z clip space (Vulkan / D3D / Metal). - const float offsetZ = nearZ; - - ovrMatrix4f result; - if (farZ <= nearZ) { - // place the far plane at infinity - result.M[0][0] = 2 / tanAngleWidth; - result.M[0][1] = 0; - result.M[0][2] = (tanAngleRight + tanAngleLeft) / tanAngleWidth; - result.M[0][3] = 0; - - result.M[1][0] = 0; - result.M[1][1] = 2 / tanAngleHeight; - result.M[1][2] = (tanAngleUp + tanAngleDown) / tanAngleHeight; - result.M[1][3] = 0; - - result.M[2][0] = 0; - result.M[2][1] = 0; - result.M[2][2] = -1; - result.M[2][3] = -(nearZ + offsetZ); - - result.M[3][0] = 0; - result.M[3][1] = 0; - result.M[3][2] = -1; - result.M[3][3] = 0; - } else { - // normal projection - result.M[0][0] = 2 / tanAngleWidth; - result.M[0][1] = 0; - result.M[0][2] = (tanAngleRight + tanAngleLeft) / tanAngleWidth; - result.M[0][3] = 0; - - result.M[1][0] = 0; - result.M[1][1] = 2 / tanAngleHeight; - result.M[1][2] = (tanAngleUp + tanAngleDown) / tanAngleHeight; - result.M[1][3] = 0; - - result.M[2][0] = 0; - result.M[2][1] = 0; - result.M[2][2] = -(farZ + offsetZ) / (farZ - nearZ); - result.M[2][3] = -(farZ * (nearZ + offsetZ)) / (farZ - nearZ); - - result.M[3][0] = 0; - result.M[3][1] = 0; - result.M[3][2] = -1; - result.M[3][3] = 0; - } - return result; -} - -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; -} - -/* -================================================================================ - -ovrTrackedController - -================================================================================ -*/ - -void ovrTrackedController_Clear(ovrTrackedController* controller) { - controller->Active = false; - controller->Pose = XrPosef_Identity(); -} diff --git a/Common/VR/VRFramebuffer.h b/Common/VR/VRFramebuffer.h index 066d500375..90e14a2030 100644 --- a/Common/VR/VRFramebuffer.h +++ b/Common/VR/VRFramebuffer.h @@ -10,8 +10,6 @@ #include #include -#define MATH_PI 3.14159265358979323846f - #define ALOGE(...) printf(__VA_ARGS__) #define ALOGV(...) printf(__VA_ARGS__) @@ -55,11 +53,6 @@ typedef struct { ovrFramebuffer FrameBuffer[ovrMaxNumEyes]; } ovrRenderer; -typedef struct { - int Active; - XrPosef Pose; -} ovrTrackedController; - typedef struct { int Focused; @@ -83,14 +76,8 @@ typedef struct { int TouchPadDownLastFrame; ovrRenderer Renderer; - ovrTrackedController TrackedController[2]; } ovrApp; - -typedef struct { - float M[4][4]; -} ovrMatrix4f; - typedef struct { uint64_t frameIndex; ovrApp appState; @@ -114,141 +101,3 @@ void ovrRenderer_Create( int suggestedEyeTextureWidth, int suggestedEyeTextureHeight); void ovrRenderer_Destroy(ovrRenderer* renderer); - -void ovrTrackedController_Clear(ovrTrackedController* controller); - -ovrMatrix4f ovrMatrix4f_Multiply(const ovrMatrix4f* a, const ovrMatrix4f* b); -ovrMatrix4f ovrMatrix4f_CreateRotation(const float radiansX, const float radiansY, const float radiansZ); -ovrMatrix4f ovrMatrix4f_CreateFromQuaternion(const XrQuaternionf* q); -ovrMatrix4f ovrMatrix4f_CreateProjectionFov( - const float fovDegreesX, - const float fovDegreesY, - const float offsetX, - const float offsetY, - const float nearZ, - const float farZ); - -XrVector4f XrVector4f_MultiplyMatrix4f(const ovrMatrix4f* a, const XrVector4f* v); - - -/// THESE METHODS HAVE ORIGIN IN openxr_oculus_helpers.h - -static inline double FromXrTime(const XrTime time) { - return (time * 1e-9); -} - -static inline XrTime ToXrTime(const double timeInSeconds) { - return (timeInSeconds * 1e9); -} - -static inline XrPosef XrPosef_Identity() { - XrPosef r; - r.orientation.x = 0; - r.orientation.y = 0; - r.orientation.z = 0; - r.orientation.w = 1; - r.position.x = 0; - r.position.y = 0; - r.position.z = 0; - return r; -} - -static inline float XrVector3f_LengthSquared(const XrVector3f v) { - return v.x * v.x + v.y * v.y + v.z * v.z;; -} - -static inline float XrVector3f_Length(const XrVector3f v) { - return sqrtf(XrVector3f_LengthSquared(v)); -} - -static inline XrVector3f XrVector3f_ScalarMultiply(const XrVector3f v, float scale) { - XrVector3f u; - u.x = v.x * scale; - u.y = v.y * scale; - u.z = v.z * scale; - return u; -} - -static inline XrVector3f XrVector3f_Normalized(const XrVector3f v) { - float rcpLen = 1.0f / XrVector3f_Length(v); - return XrVector3f_ScalarMultiply(v, rcpLen); -} - -static inline XrQuaternionf XrQuaternionf_CreateFromVectorAngle( - const XrVector3f axis, - const float angle) { - XrQuaternionf r; - if (XrVector3f_LengthSquared(axis) == 0.0f) { - r.x = 0; - r.y = 0; - r.z = 0; - r.w = 1; - return r; - } - - XrVector3f unitAxis = XrVector3f_Normalized(axis); - float sinHalfAngle = sinf(angle * 0.5f); - - r.w = cosf(angle * 0.5f); - r.x = unitAxis.x * sinHalfAngle; - r.y = unitAxis.y * sinHalfAngle; - r.z = unitAxis.z * sinHalfAngle; - return r; -} - -static inline XrQuaternionf XrQuaternionf_Multiply(const XrQuaternionf a, const XrQuaternionf b) { - XrQuaternionf c; - c.x = a.w * b.x + a.x * b.w + a.y * b.z - a.z * b.y; - c.y = a.w * b.y - a.x * b.z + a.y * b.w + a.z * b.x; - c.z = a.w * b.z + a.x * b.y - a.y * b.x + a.z * b.w; - c.w = a.w * b.w - a.x * b.x - a.y * b.y - a.z * b.z; - return c; -} - -static inline XrQuaternionf XrQuaternionf_Inverse(const XrQuaternionf q) { - XrQuaternionf r; - r.x = -q.x; - r.y = -q.y; - r.z = -q.z; - r.w = q.w; - return r; -} - -static inline XrVector3f XrQuaternionf_Rotate(const XrQuaternionf a, const XrVector3f v) { - XrVector3f r; - XrQuaternionf q = {v.x, v.y, v.z, 0.0f}; - XrQuaternionf aq = XrQuaternionf_Multiply(a, q); - XrQuaternionf aInv = XrQuaternionf_Inverse(a); - XrQuaternionf aqaInv = XrQuaternionf_Multiply(aq, aInv); - r.x = aqaInv.x; - r.y = aqaInv.y; - r.z = aqaInv.z; - return r; -} - -static inline XrVector3f XrVector3f_Add(const XrVector3f u, const XrVector3f v) { - XrVector3f w; - w.x = u.x + v.x; - w.y = u.y + v.y; - w.z = u.z + v.z; - return w; -} - -static inline XrVector3f XrPosef_Transform(const XrPosef a, const XrVector3f v) { - XrVector3f r0 = XrQuaternionf_Rotate(a.orientation, v); - return XrVector3f_Add(r0, a.position); -} - -static inline XrPosef XrPosef_Multiply(const XrPosef a, const XrPosef b) { - XrPosef c; - c.orientation = XrQuaternionf_Multiply(a.orientation, b.orientation); - c.position = XrPosef_Transform(a, b.position); - return c; -} - -static inline XrPosef XrPosef_Inverse(const XrPosef a) { - XrPosef b; - b.orientation = XrQuaternionf_Inverse(a.orientation); - b.position = XrQuaternionf_Rotate(b.orientation, XrVector3f_ScalarMultiply(a.position, -1.0f)); - return b; -} diff --git a/Common/VR/VRInput.cpp b/Common/VR/VRInput.cpp index 46044a9907..998547d26e 100644 --- a/Common/VR/VRInput.cpp +++ b/Common/VR/VRInput.cpp @@ -40,9 +40,6 @@ XrActionStateVector2f moveJoystickState[2]; float vibration_channel_duration[2] = {0.0f, 0.0f}; float vibration_channel_intensity[2] = {0.0f, 0.0f}; -float radians(float deg) { - return (deg * M_PI) / 180.0; -} unsigned long sys_timeBase = 0; int milliseconds(void) { @@ -58,98 +55,22 @@ int milliseconds(void) { return (tp.tv_sec - sys_timeBase)*1000 + tp.tv_usec/1000; } -#ifndef EPSILON -#define EPSILON 0.001f -#endif - -XrVector3f normalizeVec(XrVector3f vec) { - float xxyyzz = vec.x*vec.x + vec.y*vec.y + vec.z*vec.z; - - 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; +XrTime ToXrTime(const double timeInSeconds) { + return (timeInSeconds * 1e9); } -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); -} - -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(radians(rotation[0]), radians(rotation[1]), radians(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); -} - -void VR_Vibrate( int duration, int chan, float intensity ) { +void INVR_Vibrate( int duration, int chan, float intensity ) { for (int i = 0; i < 2; ++i) { - int channel = (i + 1) & chan; + int channel = i & chan; if (channel) { - if (vibration_channel_duration[channel-1] > 0.0f) + if (vibration_channel_duration[channel] > 0.0f) return; - if (vibration_channel_duration[channel-1] == -1.0f && duration != 0.0f) + if (vibration_channel_duration[channel] == -1.0f && duration != 0.0f) return; - vibration_channel_duration[channel-1] = duration; - vibration_channel_intensity[channel-1] = intensity; + vibration_channel_duration[channel] = duration; + vibration_channel_intensity[channel] = intensity; } } } diff --git a/Common/VR/VRInput.h b/Common/VR/VRInput.h index a59d71744d..241bf2791e 100644 --- a/Common/VR/VRInput.h +++ b/Common/VR/VRInput.h @@ -2,13 +2,6 @@ #include "VRBase.h" -// angle indexes -#define PITCH 0 -#define YAW 1 -#define ROLL 2 - -typedef float vec3_t[3]; - typedef enum ovrButton_ { ovrButton_A = 0x00000001, // Set for trigger pulled on the Gear VR and Go Controllers ovrButton_B = 0x00000002, @@ -39,5 +32,4 @@ void IN_VRInit( engine_t *engine ); void IN_VRInputFrame( engine_t* engine ); uint32_t IN_VRGetButtonState( int controllerIndex ); XrVector2f IN_VRGetJoystickState( int controllerIndex ); - -void QuatToYawPitchRoll(XrQuaternionf q, vec3_t rotation, vec3_t out); +void INVR_Vibrate( int duration, int chan, float intensity ); diff --git a/Common/VR/VRMath.cpp b/Common/VR/VRMath.cpp new file mode 100644 index 0000000000..204e591fad --- /dev/null +++ b/Common/VR/VRMath.cpp @@ -0,0 +1,415 @@ +#include "VRMath.h" + +float ToDegrees(float rad) { + return (float)(rad / M_PI * 180.0f); +} + +float ToRadians(float deg) { + return (float)(deg * M_PI / 180.0f); +} + +/* +================================================================================ + +ovrMatrix4f + +================================================================================ +*/ + +float ovrMatrix4f_Minor(const ovrMatrix4f* m, int r0, int r1, int r2, int c0, int c1, int c2) { + return m->M[r0][c0] * (m->M[r1][c1] * m->M[r2][c2] - m->M[r2][c1] * m->M[r1][c2]) - + m->M[r0][c1] * (m->M[r1][c0] * m->M[r2][c2] - m->M[r2][c0] * m->M[r1][c2]) + + m->M[r0][c2] * (m->M[r1][c0] * m->M[r2][c1] - m->M[r2][c0] * m->M[r1][c1]); +} + +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; +} + +ovrMatrix4f ovrMatrix4f_CreateProjectionFov( + const float angleLeft, + const float angleRight, + const float angleUp, + const float angleDown, + const float nearZ, + const float farZ) { + + const float tanAngleLeft = tanf(angleLeft); + const float tanAngleRight = tanf(angleRight); + + const float tanAngleDown = tanf(angleDown); + const float tanAngleUp = tanf(angleUp); + + const float tanAngleWidth = tanAngleRight - tanAngleLeft; + + // Set to tanAngleDown - tanAngleUp for a clip space with positive Y + // down (Vulkan). Set to tanAngleUp - tanAngleDown for a clip space with + // positive Y up (OpenGL / D3D / Metal). + const float tanAngleHeight = tanAngleUp - tanAngleDown; + + // Set to nearZ for a [-1,1] Z clip space (OpenGL / OpenGL ES). + // Set to zero for a [0,1] Z clip space (Vulkan / D3D / Metal). + const float offsetZ = nearZ; + + ovrMatrix4f result; + if (farZ <= nearZ) { + // place the far plane at infinity + result.M[0][0] = 2 / tanAngleWidth; + result.M[0][1] = 0; + result.M[0][2] = (tanAngleRight + tanAngleLeft) / tanAngleWidth; + result.M[0][3] = 0; + + result.M[1][0] = 0; + result.M[1][1] = 2 / tanAngleHeight; + result.M[1][2] = (tanAngleUp + tanAngleDown) / tanAngleHeight; + result.M[1][3] = 0; + + result.M[2][0] = 0; + result.M[2][1] = 0; + result.M[2][2] = -1; + result.M[2][3] = -(nearZ + offsetZ); + + result.M[3][0] = 0; + result.M[3][1] = 0; + result.M[3][2] = -1; + result.M[3][3] = 0; + } else { + // normal projection + result.M[0][0] = 2 / tanAngleWidth; + result.M[0][1] = 0; + result.M[0][2] = (tanAngleRight + tanAngleLeft) / tanAngleWidth; + result.M[0][3] = 0; + + result.M[1][0] = 0; + result.M[1][1] = 2 / tanAngleHeight; + result.M[1][2] = (tanAngleUp + tanAngleDown) / tanAngleHeight; + result.M[1][3] = 0; + + result.M[2][0] = 0; + result.M[2][1] = 0; + result.M[2][2] = -(farZ + offsetZ) / (farZ - nearZ); + result.M[2][3] = -(farZ * (nearZ + offsetZ)) / (farZ - nearZ); + + result.M[3][0] = 0; + result.M[3][1] = 0; + result.M[3][2] = -1; + result.M[3][3] = 0; + } + return result; +} + +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); +} + +ovrMatrix4f ovrMatrix4f_Inverse(const ovrMatrix4f* m) { + const float rcpDet = 1.0f / + (m->M[0][0] * ovrMatrix4f_Minor(m, 1, 2, 3, 1, 2, 3) - + m->M[0][1] * ovrMatrix4f_Minor(m, 1, 2, 3, 0, 2, 3) + + m->M[0][2] * ovrMatrix4f_Minor(m, 1, 2, 3, 0, 1, 3) - + m->M[0][3] * ovrMatrix4f_Minor(m, 1, 2, 3, 0, 1, 2)); + ovrMatrix4f out; + out.M[0][0] = ovrMatrix4f_Minor(m, 1, 2, 3, 1, 2, 3) * rcpDet; + out.M[0][1] = -ovrMatrix4f_Minor(m, 0, 2, 3, 1, 2, 3) * rcpDet; + out.M[0][2] = ovrMatrix4f_Minor(m, 0, 1, 3, 1, 2, 3) * rcpDet; + out.M[0][3] = -ovrMatrix4f_Minor(m, 0, 1, 2, 1, 2, 3) * rcpDet; + out.M[1][0] = -ovrMatrix4f_Minor(m, 1, 2, 3, 0, 2, 3) * rcpDet; + out.M[1][1] = ovrMatrix4f_Minor(m, 0, 2, 3, 0, 2, 3) * rcpDet; + out.M[1][2] = -ovrMatrix4f_Minor(m, 0, 1, 3, 0, 2, 3) * rcpDet; + out.M[1][3] = ovrMatrix4f_Minor(m, 0, 1, 2, 0, 2, 3) * rcpDet; + out.M[2][0] = ovrMatrix4f_Minor(m, 1, 2, 3, 0, 1, 3) * rcpDet; + out.M[2][1] = -ovrMatrix4f_Minor(m, 0, 2, 3, 0, 1, 3) * rcpDet; + out.M[2][2] = ovrMatrix4f_Minor(m, 0, 1, 3, 0, 1, 3) * rcpDet; + out.M[2][3] = -ovrMatrix4f_Minor(m, 0, 1, 2, 0, 1, 3) * rcpDet; + out.M[3][0] = -ovrMatrix4f_Minor(m, 1, 2, 3, 0, 1, 2) * rcpDet; + out.M[3][1] = ovrMatrix4f_Minor(m, 0, 2, 3, 0, 1, 2) * rcpDet; + out.M[3][2] = -ovrMatrix4f_Minor(m, 0, 1, 3, 0, 1, 2) * rcpDet; + out.M[3][3] = ovrMatrix4f_Minor(m, 0, 1, 2, 0, 1, 2) * rcpDet; + 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; +} + +XrVector3f ovrMatrix4f_ToEulerAngles(const ovrMatrix4f* m) { + XrVector4f v1 = {0, 0, -1, 0}; + XrVector4f v2 = {1, 0, 0, 0}; + XrVector4f v3 = {0, 1, 0, 0}; + + XrVector4f forwardInVRSpace = XrVector4f_MultiplyMatrix4f(m, &v1); + XrVector4f rightInVRSpace = XrVector4f_MultiplyMatrix4f(m, &v2); + XrVector4f upInVRSpace = XrVector4f_MultiplyMatrix4f(m, &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 = XrVector3f_Normalized(forward); + XrVector3f rightNormal = XrVector3f_Normalized(right); + XrVector3f upNormal = XrVector3f_Normalized(up); + + return XrVector3f_GetAnglesFromVectors(forwardNormal, rightNormal, upNormal); +} + +/* +================================================================================ + +XrPosef + +================================================================================ +*/ + +XrPosef XrPosef_Identity() { + XrPosef r; + r.orientation.x = 0; + r.orientation.y = 0; + r.orientation.z = 0; + r.orientation.w = 1; + r.position.x = 0; + r.position.y = 0; + r.position.z = 0; + return r; +} + +XrPosef XrPosef_Inverse(const XrPosef a) { + XrPosef b; + b.orientation = XrQuaternionf_Inverse(a.orientation); + b.position = XrQuaternionf_Rotate(b.orientation, XrVector3f_ScalarMultiply(a.position, -1.0f)); + return b; +} + +XrPosef XrPosef_Multiply(const XrPosef a, const XrPosef b) { + XrPosef c; + c.orientation = XrQuaternionf_Multiply(a.orientation, b.orientation); + c.position = XrPosef_Transform(a, b.position); + return c; +} + +XrVector3f XrPosef_Transform(const XrPosef a, const XrVector3f v) { + XrVector3f r0 = XrQuaternionf_Rotate(a.orientation, v); + return XrVector3f_Add(r0, a.position); +} + +/* +================================================================================ + +XrQuaternionf + +================================================================================ +*/ + +XrQuaternionf XrQuaternionf_CreateFromVectorAngle(const XrVector3f axis, const float angle) { + XrQuaternionf r; + if (XrVector3f_LengthSquared(axis) == 0.0f) { + r.x = 0; + r.y = 0; + r.z = 0; + r.w = 1; + return r; + } + + XrVector3f unitAxis = XrVector3f_Normalized(axis); + float sinHalfAngle = sinf(angle * 0.5f); + + r.w = cosf(angle * 0.5f); + r.x = unitAxis.x * sinHalfAngle; + r.y = unitAxis.y * sinHalfAngle; + r.z = unitAxis.z * sinHalfAngle; + return r; +} + +XrQuaternionf XrQuaternionf_Inverse(const XrQuaternionf q) { + XrQuaternionf r; + r.x = -q.x; + r.y = -q.y; + r.z = -q.z; + r.w = q.w; + return r; +} + +XrQuaternionf XrQuaternionf_Multiply(const XrQuaternionf a, const XrQuaternionf b) { + XrQuaternionf c; + c.x = a.w * b.x + a.x * b.w + a.y * b.z - a.z * b.y; + c.y = a.w * b.y - a.x * b.z + a.y * b.w + a.z * b.x; + c.z = a.w * b.z + a.x * b.y - a.y * b.x + a.z * b.w; + c.w = a.w * b.w - a.x * b.x - a.y * b.y - a.z * b.z; + return c; +} + +XrVector3f XrQuaternionf_Rotate(const XrQuaternionf a, const XrVector3f v) { + XrVector3f r; + XrQuaternionf q = {v.x, v.y, v.z, 0.0f}; + XrQuaternionf aq = XrQuaternionf_Multiply(a, q); + XrQuaternionf aInv = XrQuaternionf_Inverse(a); + XrQuaternionf aqaInv = XrQuaternionf_Multiply(aq, aInv); + r.x = aqaInv.x; + r.y = aqaInv.y; + r.z = aqaInv.z; + return r; +} + +XrVector3f XrQuaternionf_ToEulerAngles(const XrQuaternionf q) { + ovrMatrix4f m = ovrMatrix4f_CreateFromQuaternion( &q ); + return ovrMatrix4f_ToEulerAngles(&m); +} + +/* +================================================================================ + +XrVector3f, XrVector4f + +================================================================================ +*/ + +float XrVector3f_Length(const XrVector3f v) { + return sqrtf(XrVector3f_LengthSquared(v)); +} + +float XrVector3f_LengthSquared(const XrVector3f v) { + return v.x * v.x + v.y * v.y + v.z * v.z;; +} + +XrVector3f XrVector3f_Add(const XrVector3f u, const XrVector3f v) { + XrVector3f w; + w.x = u.x + v.x; + w.y = u.y + v.y; + w.z = u.z + v.z; + return w; +} + +XrVector3f XrVector3f_GetAnglesFromVectors(const XrVector3f forward, const XrVector3f right, const XrVector3f up) { + 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); + + XrVector3f angles; + angles.x = ToDegrees(pitch); + angles.y = ToDegrees(yaw); + angles.z = ToDegrees(roll); + return angles; +} + +XrVector3f XrVector3f_Normalized(const XrVector3f v) { + float rcpLen = 1.0f / XrVector3f_Length(v); + return XrVector3f_ScalarMultiply(v, rcpLen); +} + +XrVector3f XrVector3f_ScalarMultiply(const XrVector3f v, float scale) { + XrVector3f u; + u.x = v.x * scale; + u.y = v.y * scale; + u.z = v.z * scale; + return u; +} + +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; +} diff --git a/Common/VR/VRMath.h b/Common/VR/VRMath.h new file mode 100644 index 0000000000..c0ccb36aac --- /dev/null +++ b/Common/VR/VRMath.h @@ -0,0 +1,46 @@ +#pragma once + +#include +#include + +#ifndef EPSILON +#define EPSILON 0.001f +#endif + +typedef struct { + float M[4][4]; +} ovrMatrix4f; + +float ToDegrees(float rad); +float ToRadians(float deg); + +// ovrMatrix4f +float ovrMatrix4f_Minor(const ovrMatrix4f* m, int r0, int r1, int r2, int c0, int c1, int c2); +ovrMatrix4f ovrMatrix4f_CreateFromQuaternion(const XrQuaternionf* q); +ovrMatrix4f ovrMatrix4f_CreateProjectionFov(const float fovDegreesX, const float fovDegreesY, const float offsetX, const float offsetY, const float nearZ, const float farZ); +ovrMatrix4f ovrMatrix4f_CreateRotation(const float radiansX, const float radiansY, const float radiansZ); +ovrMatrix4f ovrMatrix4f_Inverse(const ovrMatrix4f* m); +ovrMatrix4f ovrMatrix4f_Multiply(const ovrMatrix4f* a, const ovrMatrix4f* b); +XrVector3f ovrMatrix4f_ToEulerAngles(const ovrMatrix4f* m); + +// XrPosef +XrPosef XrPosef_Identity(); +XrPosef XrPosef_Inverse(const XrPosef a); +XrPosef XrPosef_Multiply(const XrPosef a, const XrPosef b); +XrVector3f XrPosef_Transform(const XrPosef a, const XrVector3f v); + +// XrQuaternionf +XrQuaternionf XrQuaternionf_CreateFromVectorAngle(const XrVector3f axis, const float angle); +XrQuaternionf XrQuaternionf_Inverse(const XrQuaternionf q); +XrQuaternionf XrQuaternionf_Multiply(const XrQuaternionf a, const XrQuaternionf b); +XrVector3f XrQuaternionf_Rotate(const XrQuaternionf a, const XrVector3f v); +XrVector3f XrQuaternionf_ToEulerAngles(const XrQuaternionf q); + +// XrVector3f, XrVector4f +float XrVector3f_Length(const XrVector3f v); +float XrVector3f_LengthSquared(const XrVector3f v); +XrVector3f XrVector3f_Add(const XrVector3f u, const XrVector3f v); +XrVector3f XrVector3f_GetAnglesFromVectors(const XrVector3f forward, const XrVector3f right, const XrVector3f up); +XrVector3f XrVector3f_Normalized(const XrVector3f v); +XrVector3f XrVector3f_ScalarMultiply(const XrVector3f v, float scale); +XrVector4f XrVector4f_MultiplyMatrix4f(const ovrMatrix4f* a, const XrVector4f* v); diff --git a/Common/VR/VRRenderer.cpp b/Common/VR/VRRenderer.cpp index 12fb37cf7b..01871b8acb 100644 --- a/Common/VR/VRRenderer.cpp +++ b/Common/VR/VRRenderer.cpp @@ -14,14 +14,13 @@ XrPosef invViewTransform[2]; XrFrameState frameState = {}; GLboolean initialized = GL_FALSE; GLboolean stageSupported = GL_FALSE; -VRMode vrMode = VR_MODE_FLAT_SCREEN; +int vrConfig[VR_CONFIG_MAX] = {}; +float menuPitch = 0; float menuYaw = 0; float recenterYaw = 0; -vec3_t hmdorientation; -vec3_t hmdposition; - -extern float radians(float deg); +XrVector3f hmdorientation; +XrVector3f hmdposition; void VR_UpdateStageBounds(ovrApp* pappState) { XrExtent2Df stageBounds = {}; @@ -135,10 +134,9 @@ void VR_Recenter(engine_t* engine) { XrSpaceLocation loc = {}; loc.type = XR_TYPE_SPACE_LOCATION; OXR(xrLocateSpace(engine->appState.HeadSpace, engine->appState.CurrentSpace, engine->predictedDisplayTime, &loc)); - vec3_t rotation = {0, 0, 0}; - QuatToYawPitchRoll(loc.pose.orientation, rotation, hmdorientation); + hmdorientation = XrQuaternionf_ToEulerAngles(loc.pose.orientation); - recenterYaw += radians(hmdorientation[YAW]); + recenterYaw += ToRadians(hmdorientation.y); spaceCreateInfo.poseInReferenceSpace.orientation.x = 0; spaceCreateInfo.poseInReferenceSpace.orientation.y = sin(recenterYaw / 2); spaceCreateInfo.poseInReferenceSpace.orientation.z = 0; @@ -156,7 +154,9 @@ void VR_Recenter(engine_t* engine) { // Create a default stage space to use if SPACE_TYPE_STAGE is not // supported, or calls to xrGetReferenceSpaceBoundsRect fail. spaceCreateInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_LOCAL; +#ifdef OPENXR_FLOOR_STAGE spaceCreateInfo.poseInReferenceSpace.position.y = -1.6750f; +#endif OXR(xrCreateReferenceSpace(engine->appState.Session, &spaceCreateInfo, &engine->appState.FakeStageSpace)); ALOGV("Created fake stage space from local space with offset"); engine->appState.CurrentSpace = engine->appState.FakeStageSpace; @@ -166,14 +166,21 @@ void VR_Recenter(engine_t* engine) { spaceCreateInfo.poseInReferenceSpace.position.y = 0.0; OXR(xrCreateReferenceSpace(engine->appState.Session, &spaceCreateInfo, &engine->appState.StageSpace)); ALOGV("Created stage space"); +#ifdef OPENXR_FLOOR_STAGE engine->appState.CurrentSpace = engine->appState.StageSpace; +#endif } // Update menu orientation + menuPitch = hmdorientation.x; menuYaw = 0; } void VR_InitRenderer( engine_t* engine ) { + if (initialized) { + VR_DestroyRenderer(engine); + } + int eyeW, eyeH; VR_GetResolution(engine, &eyeW, &eyeH); @@ -297,11 +304,8 @@ void VR_BeginFrame( engine_t* engine ) { } // Update HMD and controllers - vec3_t rotation = {0, 0, 0}; - QuatToYawPitchRoll(invViewTransform[0].orientation, rotation, hmdorientation); - hmdposition[0] = invViewTransform[0].position.x; - hmdposition[1] = invViewTransform[0].position.y; - hmdposition[2] = invViewTransform[0].position.z; + hmdorientation = XrQuaternionf_ToEulerAngles(invViewTransform[0].orientation); + hmdposition = invViewTransform[0].position; IN_VRInputFrame(engine); engine->appState.LayerCount = 0; @@ -309,8 +313,6 @@ void VR_BeginFrame( engine_t* engine ) { for (int eye = 0; eye < ovrMaxNumEyes; eye++) { ovrFramebuffer* frameBuffer = &engine->appState.Renderer.FrameBuffer[eye]; - int swapchainIndex = frameBuffer->TextureSwapChainIndex; - int glFramebuffer = frameBuffer->FrameBuffers[swapchainIndex]; ovrFramebuffer_Acquire(frameBuffer); ovrFramebuffer_SetCurrent(frameBuffer); @@ -335,19 +337,22 @@ void VR_EndFrame( engine_t* engine ) { ovrFramebuffer_SetNone(); XrCompositionLayerProjectionView projection_layer_elements[2] = {}; + int vrMode = vrConfig[VR_CONFIG_MODE]; if ((vrMode == VR_MODE_MONO_6DOF) || (vrMode == VR_MODE_STEREO_6DOF)) { - menuYaw = hmdorientation[YAW]; + menuYaw = hmdorientation.y; for (int eye = 0; eye < ovrMaxNumEyes; eye++) { + XrFovf fov = projections[eye].fov; ovrFramebuffer* frameBuffer = &engine->appState.Renderer.FrameBuffer[eye]; if (vrMode == VR_MODE_MONO_6DOF) { frameBuffer = &engine->appState.Renderer.FrameBuffer[0]; + fov = projections[0].fov; } memset(&projection_layer_elements[eye], 0, sizeof(XrCompositionLayerProjectionView)); projection_layer_elements[eye].type = XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW; projection_layer_elements[eye].pose = invViewTransform[eye]; - projection_layer_elements[eye].fov = projections[eye].fov; + projection_layer_elements[eye].fov = fov; memset(&projection_layer_elements[eye].subImage, 0, sizeof(XrSwapchainSubImage)); projection_layer_elements[eye].subImage.swapchain = frameBuffer->ColorSwapChain.Handle; @@ -384,17 +389,19 @@ void VR_EndFrame( engine_t* engine ) { cylinder_layer.subImage.imageRect.extent.width = width; cylinder_layer.subImage.imageRect.extent.height = height; cylinder_layer.subImage.imageArrayIndex = 0; - const XrVector3f axis = {0.0f, 1.0f, 0.0f}; + float distance = vrConfig[VR_CONFIG_CANVAS_DISTANCE]; XrVector3f pos = { - invViewTransform[0].position.x - sin(radians(menuYaw)) * 6.0f, + invViewTransform[0].position.x - sin(ToRadians(menuYaw)) * distance, invViewTransform[0].position.y, - invViewTransform[0].position.z - cos(radians(menuYaw)) * 6.0f + invViewTransform[0].position.z - cos(ToRadians(menuYaw)) * distance }; - cylinder_layer.pose.orientation = XrQuaternionf_CreateFromVectorAngle(axis, radians(menuYaw)); + XrQuaternionf pitch = XrQuaternionf_CreateFromVectorAngle({1, 0, 0}, -ToRadians(menuPitch)); + XrQuaternionf yaw = XrQuaternionf_CreateFromVectorAngle({0, 1, 0}, ToRadians(menuYaw)); + cylinder_layer.pose.orientation = XrQuaternionf_Multiply(pitch, yaw); cylinder_layer.pose.position = pos; cylinder_layer.radius = 12.0f; - cylinder_layer.centralAngle = MATH_PI * 0.5f; - cylinder_layer.aspectRatio = height / (float)width; + cylinder_layer.centralAngle = M_PI * 0.5f; + cylinder_layer.aspectRatio = 1; engine->appState.Layers[engine->appState.LayerCount++].Cylinder = cylinder_layer; } else { @@ -422,6 +429,14 @@ void VR_EndFrame( engine_t* engine ) { } } +int VR_GetConfig( VRConfig config ) { + return vrConfig[config]; +} + +void VR_SetConfig( VRConfig config, int value) { + vrConfig[config] = value; +} + void VR_BindFramebuffer( engine_t* engine, int eye ) { if (!initialized) return; ovrFramebuffer* frameBuffer = &engine->appState.Renderer.FrameBuffer[eye]; @@ -433,18 +448,49 @@ void VR_BindFramebuffer( engine_t* engine, int eye ) { ovrMatrix4f VR_GetMatrix( VRMatrix matrix ) { ovrMatrix4f output; if (matrix == VR_PROJECTION_MATRIX_HUD) { - float hudScale = radians(15.0f); + float hudScale = ToRadians(15.0f); output = ovrMatrix4f_CreateProjectionFov(-hudScale, hudScale, hudScale, -hudScale, 1.0f, 0.0f ); } else if ((matrix == VR_PROJECTION_MATRIX_LEFT_EYE) || (matrix == VR_PROJECTION_MATRIX_RIGHT_EYE)) { XrFovf fov = matrix == VR_PROJECTION_MATRIX_LEFT_EYE ? projections[0].fov : projections[1].fov; + float fovScale = vrConfig[VR_CONFIG_FOV_SCALE] * 0.01f; + fov.angleLeft *= fovScale; + fov.angleRight *= fovScale; + fov.angleUp *= fovScale; + fov.angleDown *= fovScale; output = ovrMatrix4f_CreateProjectionFov(fov.angleLeft, fov.angleRight, fov.angleUp, fov.angleDown, 1.0f, 0.0f ); } else if ((matrix == VR_VIEW_MATRIX_LEFT_EYE) || (matrix == VR_VIEW_MATRIX_RIGHT_EYE)) { XrPosef invView = matrix == VR_VIEW_MATRIX_LEFT_EYE ? invViewTransform[0] : invViewTransform[1]; - XrPosef view = XrPosef_Inverse(invView); - output = ovrMatrix4f_CreateFromQuaternion(&view.orientation); - output.M[3][0] = view.position.x; - output.M[3][1] = view.position.y; - output.M[3][2] = view.position.z; + + // get axis mirroring configuration + float mx = vrConfig[VR_CONFIG_MIRROR_PITCH] ? -1 : 1; + float my = vrConfig[VR_CONFIG_MIRROR_YAW] ? -1 : 1; + float mz = vrConfig[VR_CONFIG_MIRROR_ROLL] ? -1 : 1; + + // ensure there is maximally one axis to mirror rotation + if (mx + my + mz < 0) { + mx *= -1.0f; + my *= -1.0f; + mz *= -1.0f; + } else { + invView = XrPosef_Inverse(invView); + } + + // create updated quaternion + if (mx + my + mz < 3 - EPSILON) { + XrVector3f rotation = XrQuaternionf_ToEulerAngles(invView.orientation); + XrQuaternionf pitch = XrQuaternionf_CreateFromVectorAngle({1, 0, 0}, mx * ToRadians(rotation.x)); + XrQuaternionf yaw = XrQuaternionf_CreateFromVectorAngle({0, 1, 0}, my * ToRadians(rotation.y)); + XrQuaternionf roll = XrQuaternionf_CreateFromVectorAngle({0, 0, 1}, mz * ToRadians(rotation.z)); + invView.orientation = XrQuaternionf_Multiply(roll, XrQuaternionf_Multiply(pitch, yaw)); + } + + output = ovrMatrix4f_CreateFromQuaternion(&invView.orientation); + if (vrConfig[VR_CONFIG_6DOF_ENABLED]) { + float scale = (float)VR_GetConfig(VR_CONFIG_6DOF_SCALE) * 0.001f; + output.M[0][3] -= hmdposition.x * (vrConfig[VR_CONFIG_MIRROR_AXIS_X] ? -1.0f : 1.0f) * scale; + output.M[1][3] -= hmdposition.y * (vrConfig[VR_CONFIG_MIRROR_AXIS_Y] ? -1.0f : 1.0f) * scale; + output.M[2][3] -= hmdposition.z * (vrConfig[VR_CONFIG_MIRROR_AXIS_Z] ? -1.0f : 1.0f) * scale; + } } else { assert(false); } diff --git a/Common/VR/VRRenderer.h b/Common/VR/VRRenderer.h index 1792429432..721342252c 100644 --- a/Common/VR/VRRenderer.h +++ b/Common/VR/VRRenderer.h @@ -1,6 +1,24 @@ #pragma once #include "VRFramebuffer.h" +#include "VRMath.h" + +enum VRConfig { + VR_CONFIG_MODE, + VR_CONFIG_6DOF_ENABLED, + VR_CONFIG_6DOF_SCALE, + VR_CONFIG_MIRROR_AXIS_X, + VR_CONFIG_MIRROR_AXIS_Y, + VR_CONFIG_MIRROR_AXIS_Z, + VR_CONFIG_MIRROR_PITCH, + VR_CONFIG_MIRROR_YAW, + VR_CONFIG_MIRROR_ROLL, + VR_CONFIG_3D_GEOMETRY_COUNT, + VR_CONFIG_FOV_SCALE, + VR_CONFIG_FORCE_2D, + VR_CONFIG_CANVAS_DISTANCE, + VR_CONFIG_MAX +}; enum VRMatrix { VR_PROJECTION_MATRIX_HUD = 0, @@ -22,7 +40,9 @@ void VR_DestroyRenderer( engine_t* engine ); void VR_BeginFrame( engine_t* engine ); void VR_EndFrame( engine_t* engine ); -void VR_SetMode( VRMode mode ); + +int VR_GetConfig( VRConfig config ); +void VR_SetConfig( VRConfig config, int value); void VR_BindFramebuffer( engine_t* engine, int eye ); ovrMatrix4f VR_GetMatrix( VRMatrix matrix ); diff --git a/Common/VR/VRTweaks.cpp b/Common/VR/VRTweaks.cpp new file mode 100644 index 0000000000..7332bb734e --- /dev/null +++ b/Common/VR/VRTweaks.cpp @@ -0,0 +1,92 @@ +#include "VRTweaks.h" +#include + +bool VR_TweakIsMatrixBigScale(float* matrix) { + for (int i = 0; i < 2; i++) { + float value = matrix[i * 4 + i]; + if (fabs(value) < 10.0f) return false; + } + return true; +} + +bool VR_TweakIsMatrixIdentity(float* matrix) { + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + float value = matrix[i * 4 + j]; + + // Other number than zero on non-diagonale + if ((i != j) && (fabs(value) > EPSILON)) return false; + // Other number than one on diagonale + if ((i == j) && (fabs(value - 1.0f) > EPSILON)) return false; + } + } + return true; +} + +bool VR_TweakIsMatrixOneOrtho(float* matrix) { + float value = matrix[15]; + return fabs(value - 1) < EPSILON; +} + +bool VR_TweakIsMatrixOneScale(float* matrix) { + for (int i = 0; i < 2; i++) { + float value = matrix[i * 4 + i]; + if (fabs(value - 1) > EPSILON) return false; + } + return true; +} + +bool VR_TweakIsMatrixOneTransform(float* matrix) { + for (int j = 0; j < 4; j++) { + float value = matrix[12 + j]; + if (fabs(fabs(value) - 1.0f) > EPSILON) return false; + } + return true; +} + +void VR_TweakMirroring(float* projMatrix) { + VR_SetConfig(VR_CONFIG_MIRROR_AXIS_X, projMatrix[0] < 0); + VR_SetConfig(VR_CONFIG_MIRROR_AXIS_Y, projMatrix[5] < 0); + VR_SetConfig(VR_CONFIG_MIRROR_AXIS_Z, projMatrix[10] > 0); + if ((projMatrix[0] < 0) && (projMatrix[10] < 0)) { //e.g. Dante's inferno + VR_SetConfig(VR_CONFIG_MIRROR_PITCH, true); + VR_SetConfig(VR_CONFIG_MIRROR_YAW, true); + VR_SetConfig(VR_CONFIG_MIRROR_ROLL, false); + } else if (projMatrix[10] < 0) { //e.g. GTA - Liberty city + VR_SetConfig(VR_CONFIG_MIRROR_PITCH, false); + VR_SetConfig(VR_CONFIG_MIRROR_YAW, false); + VR_SetConfig(VR_CONFIG_MIRROR_ROLL, false); + } else if (projMatrix[5] < 0) { //e.g. PES 2014 + VR_SetConfig(VR_CONFIG_MIRROR_PITCH, true); + VR_SetConfig(VR_CONFIG_MIRROR_YAW, true); + VR_SetConfig(VR_CONFIG_MIRROR_ROLL, false); + } else { //e.g. Lego Pirates + VR_SetConfig(VR_CONFIG_MIRROR_PITCH, false); + VR_SetConfig(VR_CONFIG_MIRROR_YAW, true); + VR_SetConfig(VR_CONFIG_MIRROR_ROLL, true); + } +} + +void VR_TweakProjection(float* src, float* dst, VRMatrix matrix) { + memcpy(dst, src, 16 * sizeof(float)); + ovrMatrix4f hmdProjection = VR_GetMatrix(matrix); + dst[0] = (dst[0] > 0 ? 1.0f : -1.0f) * hmdProjection.M[0][0]; + dst[5] = (dst[5] > 0 ? 1.0f : -1.0f) * hmdProjection.M[1][1]; +} + +void VR_TweakView(float* view, float* projMatrix, VRMatrix matrix) { + // Get view matrix from the game + ovrMatrix4f gameView; + memcpy(gameView.M, view, 16 * sizeof(float)); + + // Set 6DoF scale + float scale = pow(fabs(projMatrix[14]), 1.15f); + VR_SetConfig(VR_CONFIG_6DOF_SCALE, (int)(scale * 1000)); + + // Get view matrix from the headset + ovrMatrix4f hmdView = VR_GetMatrix(matrix); + + // Combine the matrices + ovrMatrix4f renderView = ovrMatrix4f_Multiply(&hmdView, &gameView); + memcpy(view, renderView.M, 16 * sizeof(float)); +} diff --git a/Common/VR/VRTweaks.h b/Common/VR/VRTweaks.h new file mode 100644 index 0000000000..7d3e06dbc6 --- /dev/null +++ b/Common/VR/VRTweaks.h @@ -0,0 +1,12 @@ +#pragma once + +#include "VRRenderer.h" + +bool VR_TweakIsMatrixBigScale(float* matrix); +bool VR_TweakIsMatrixIdentity(float* matrix); +bool VR_TweakIsMatrixOneOrtho(float* matrix); +bool VR_TweakIsMatrixOneScale(float* matrix); +bool VR_TweakIsMatrixOneTransform(float* matrix); +void VR_TweakMirroring(float* projMatrix); +void VR_TweakProjection(float* src, float* dst, VRMatrix matrix); +void VR_TweakView(float* view, float* projMatrix, VRMatrix matrix); diff --git a/Core/Config.cpp b/Core/Config.cpp index 1949ecf426..055a9036d9 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -1201,6 +1201,14 @@ static ConfigSetting themeSettings[] = { ConfigSetting(false), }; + +static ConfigSetting vrSettings[] = { + ConfigSetting("VREnable", &g_Config.bEnableVR, true), + ConfigSetting("VREnable6DoF", &g_Config.bEnable6DoF, true), + ConfigSetting("VRCanvasDistance", &g_Config.iCanvasDistance, 6), + ConfigSetting("VRFieldOfView", &g_Config.iFieldOfViewPercentage, 100), +}; + static ConfigSectionSettings sections[] = { {"General", generalSettings}, {"CPU", cpuSettings}, @@ -1213,6 +1221,7 @@ static ConfigSectionSettings sections[] = { {"JIT", jitSettings}, {"Upgrade", upgradeSettings}, {"Theme", themeSettings}, + {"VR", vrSettings}, }; static void IterateSettings(IniFile &iniFile, std::function func) { diff --git a/Core/Config.h b/Core/Config.h index 7a35ff0d09..4edd2147e0 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -453,6 +453,12 @@ public: int iFirmwareVersion; bool bBypassOSKWithKeyboard; + // Virtual reality + bool bEnableVR; + bool bEnable6DoF; + int iCanvasDistance; + int iFieldOfViewPercentage; + // Debugger int iDisasmWindowX; int iDisasmWindowY; diff --git a/Core/HLE/sceDisplay.cpp b/Core/HLE/sceDisplay.cpp index 114573e494..7db5998ff7 100644 --- a/Core/HLE/sceDisplay.cpp +++ b/Core/HLE/sceDisplay.cpp @@ -104,8 +104,10 @@ static int height; static bool wasPaused; static bool flippedThisFrame; +static int framerate = 60; + // 1.001f to compensate for the classic 59.94 NTSC framerate that the PSP seems to have. -static const double timePerVblank = 1.001f / 60.0f; +static double timePerVblank = 1.001f / (float)framerate; // Don't include this in the state, time increases regardless of state. static double curFrameTime; @@ -127,7 +129,7 @@ const double vblankMs = 0.7315; // These are guesses based on tests. const double vsyncStartMs = 0.5925; const double vsyncEndMs = 0.7265; -const double frameMs = 1001.0 / 60.0; +double frameMs = 1001.0 / (double)framerate; enum { PSP_DISPLAY_SETBUF_IMMEDIATE = 0, @@ -158,7 +160,7 @@ static void ScheduleLagSync(int over = 0) { if (lagSyncScheduled) { // Reset over if it became too high, such as after pausing or initial loading. // There's no real sense in it being more than 1/60th of a second. - if (over > 1000000 / 60) { + if (over > 1000000 / framerate) { over = 0; } CoreTiming::ScheduleEvent(usToCycles(1000 + over), lagSyncEvent, 0); @@ -358,7 +360,7 @@ static int FrameTimingLimit() { return PSP_CoreParameter().analogFpsLimit; if (PSP_CoreParameter().fastForward) return 0; - return 60; + return framerate; } static bool FrameTimingThrottled() { @@ -389,8 +391,8 @@ static void DoFrameTiming(bool &throttle, bool &skipFrame, float timestep) { return; float scaledTimestep = timestep; - if (fpsLimit > 0 && fpsLimit != 60) { - scaledTimestep *= 60.0f / fpsLimit; + if (fpsLimit > 0 && fpsLimit != framerate) { + scaledTimestep *= (float)framerate / fpsLimit; } if (lastFrameTime == 0.0 || wasPaused) { @@ -464,9 +466,9 @@ static void DoFrameIdleTiming() { float scaledVblank = timePerVblank; int fpsLimit = FrameTimingLimit(); - if (fpsLimit != 0 && fpsLimit != 60) { + if (fpsLimit != 0 && fpsLimit != framerate) { // 0 is handled in FrameTimingThrottled(). - scaledVblank *= 60.0f / fpsLimit; + scaledVblank *= (float)framerate / fpsLimit; } // If we have over at least a vblank of spare time, maintain at least 30fps in delay. @@ -586,7 +588,7 @@ void __DisplayFlip(int cyclesLate) { bool forceNoFlip = false; float refreshRate = System_GetPropertyFloat(SYSPROP_DISPLAY_REFRESH_RATE); // Avoid skipping on devices that have 58 or 59 FPS, except when alternate speed is set. - bool refreshRateNeedsSkip = FrameTimingLimit() != 60 && FrameTimingLimit() > refreshRate; + bool refreshRateNeedsSkip = FrameTimingLimit() != framerate && FrameTimingLimit() > refreshRate; // Alternative to frameskip fast-forward, where we draw everything. // Useful if skipping a frame breaks graphics or for checking drawing speed. if (fastForwardSkipFlip && (!FrameTimingThrottled() || refreshRateNeedsSkip)) { @@ -685,9 +687,9 @@ void hleLagSync(u64 userdata, int cyclesLate) { float scale = 1.0f; int fpsLimit = FrameTimingLimit(); - if (fpsLimit != 0 && fpsLimit != 60) { + if (fpsLimit != 0 && fpsLimit != framerate) { // 0 is handled in FrameTimingThrottled(). - scale = 60.0f / fpsLimit; + scale = (float)framerate / fpsLimit; } const double goal = lastLagSync + (scale / 1000.0f); @@ -845,7 +847,7 @@ u32 sceDisplaySetFramebuf(u32 topaddr, int linesize, int pixelformat, int sync) } // 1001 to account for NTSC timing (59.94 fps.) - u64 expected = msToCycles(1001) / 60 - LEEWAY_CYCLES_PER_FLIP; + u64 expected = msToCycles(1001) / framerate - LEEWAY_CYCLES_PER_FLIP; lastFlipCycles = now; nextFlipCycles = std::max(lastFlipCycles, nextFlipCycles) + expected; } @@ -1077,3 +1079,9 @@ void Register_sceDisplay() { void Register_sceDisplay_driver() { RegisterModule("sceDisplay_driver", ARRAY_SIZE(sceDisplay), sceDisplay); } + +void __DisplaySetFramerate(int value) { + framerate = value; + timePerVblank = 1.001f / (float)framerate; + frameMs = 1001.0 / (double)framerate; +} diff --git a/Core/HLE/sceDisplay.h b/Core/HLE/sceDisplay.h index bb134af815..b68133e8a2 100644 --- a/Core/HLE/sceDisplay.h +++ b/Core/HLE/sceDisplay.h @@ -34,3 +34,5 @@ void __DisplaySetWasPaused(); void Register_sceDisplay_driver(); void __DisplayWaitForVblanks(const char* reason, int vblanks, bool callbacks = false); + +void __DisplaySetFramerate(int value); diff --git a/Core/KeyMapDefaults.cpp b/Core/KeyMapDefaults.cpp index d4aa8b13e0..d6d21639cb 100644 --- a/Core/KeyMapDefaults.cpp +++ b/Core/KeyMapDefaults.cpp @@ -317,6 +317,7 @@ static const DefMappingStruct defaultVRLeftController[] = { {CTRL_SELECT , NKCODE_BUTTON_THUMBL}, {CTRL_LTRIGGER , NKCODE_BUTTON_X}, {CTRL_RTRIGGER , NKCODE_BUTTON_Y}, + {CTRL_SCREEN , NKCODE_ALT_LEFT}, }; static const DefMappingStruct defaultVRRightController[] = { diff --git a/GPU/Common/PresentationCommon.cpp b/GPU/Common/PresentationCommon.cpp index 5f79861fd7..1603234ad0 100644 --- a/GPU/Common/PresentationCommon.cpp +++ b/GPU/Common/PresentationCommon.cpp @@ -35,6 +35,10 @@ #include "GPU/Common/PresentationCommon.h" #include "Common/GPU/ShaderTranslation.h" +#ifdef OPENXR +#include "VR/VRRenderer.h" +#endif + struct Vertex { float x, y, z; float u, v; @@ -73,6 +77,14 @@ void CenterDisplayOutputRect(FRect *rc, float origW, float origH, const FRect &f bool rotated = rotation == ROTATION_LOCKED_VERTICAL || rotation == ROTATION_LOCKED_VERTICAL180; +#ifdef OPENXR + if (VR_GetConfig(VR_CONFIG_MODE) == VR_MODE_FLAT_SCREEN) { + g_Config.iSmallDisplayZoomType = (int)SmallDisplayZoom::AUTO; + } else { + g_Config.iSmallDisplayZoomType = (int)SmallDisplayZoom::STRETCH; + } +#endif + if (g_Config.iSmallDisplayZoomType == (int)SmallDisplayZoom::STRETCH) { outW = frame.w; outH = frame.h; diff --git a/GPU/Common/VertexShaderGenerator.cpp b/GPU/Common/VertexShaderGenerator.cpp index 52624b6dcc..4e3a03e8ba 100644 --- a/GPU/Common/VertexShaderGenerator.cpp +++ b/GPU/Common/VertexShaderGenerator.cpp @@ -534,6 +534,11 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag WRITE(p, "uniform lowp float u_rotation;\n"); } +#ifdef OPENXR + WRITE(p, "uniform lowp float u_scaleX;\n"); + WRITE(p, "uniform lowp float u_scaleY;\n"); +#endif + if (useHWTransform || !hasColor) { WRITE(p, "uniform lowp vec4 u_matambientalpha;\n"); // matambient + matalpha *uniformMask |= DIRTY_MATAMBIENTALPHA; @@ -1183,6 +1188,12 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag WRITE(p, " if (%sgl_Position.z == %sgl_Position.w) %sgl_Position.z *= 0.999999;\n", compat.vsOutPrefix, compat.vsOutPrefix, compat.vsOutPrefix); } +#ifdef OPENXR + WRITE(p, " if ((u_scaleX < 0.99) || (u_scaleY < 0.99)) {\n"); + WRITE(p, " %sgl_Position.x *= u_scaleX;\n", compat.vsOutPrefix); + WRITE(p, " %sgl_Position.y *= u_scaleY;\n", compat.vsOutPrefix); + WRITE(p, " }\n"); +#endif if (compat.shaderLanguage == HLSL_D3D11 || compat.shaderLanguage == HLSL_D3D9) { WRITE(p, " return Out;\n"); diff --git a/GPU/GLES/ShaderManagerGLES.cpp b/GPU/GLES/ShaderManagerGLES.cpp index abceadb41a..6a174332ee 100644 --- a/GPU/GLES/ShaderManagerGLES.cpp +++ b/GPU/GLES/ShaderManagerGLES.cpp @@ -50,6 +50,12 @@ #include "GPU/GLES/DrawEngineGLES.h" #include "GPU/GLES/FramebufferManagerGLES.h" +#ifdef OPENXR +#include "VR/VRBase.h" +#include "VR/VRRenderer.h" +#include "VR/VRTweaks.h" +#endif + using namespace Lin; Shader::Shader(GLRenderManager *render, const char *code, const std::string &desc, const ShaderDescGLES ¶ms) @@ -126,6 +132,10 @@ LinkedShader::LinkedShader(GLRenderManager *render, VShaderID VSID, Shader *vs, queries.push_back({ &u_cullRangeMin, "u_cullRangeMin" }); queries.push_back({ &u_cullRangeMax, "u_cullRangeMax" }); queries.push_back({ &u_rotation, "u_rotation" }); +#ifdef OPENXR + queries.push_back({ &u_scaleX, "u_scaleX" }); + queries.push_back({ &u_scaleY, "u_scaleY" }); +#endif #ifdef USE_BONE_ARRAY queries.push_back({ &u_bone, "u_bone" }); @@ -294,6 +304,9 @@ void LinkedShader::use(const ShaderID &VSID) { void LinkedShader::UpdateUniforms(u32 vertType, const ShaderID &vsid, bool useBufferedRendering) { u64 dirty = dirtyUniforms & availableUniforms; dirtyUniforms = 0; +#ifdef OPENXR + dirty |= DIRTY_VIEWMATRIX; +#endif if (!dirty) return; @@ -308,10 +321,48 @@ void LinkedShader::UpdateUniforms(u32 vertType, const ShaderID &vsid, bool useBu render_->SetUniformUI1(&u_depal_mask_shift_off_fmt, val); } +#ifdef OPENXR + // Count 3D instances + bool is2D = VR_TweakIsMatrixBigScale(gstate.projMatrix) || + VR_TweakIsMatrixIdentity(gstate.projMatrix) || + VR_TweakIsMatrixOneOrtho(gstate.projMatrix) || + VR_TweakIsMatrixOneScale(gstate.projMatrix) || + VR_TweakIsMatrixOneTransform(gstate.projMatrix); + if (!is2D && !gstate.isModeThrough()) { + VR_SetConfig(VR_CONFIG_3D_GEOMETRY_COUNT, VR_GetConfig(VR_CONFIG_3D_GEOMETRY_COUNT) + 1); + } + + // Set HUD mode + bool is3D = gstate.isDepthWriteEnabled(); + bool flatScreen = VR_GetConfig(VR_CONFIG_MODE) == VR_MODE_FLAT_SCREEN; + bool hud = is2D && !is3D && !flatScreen && + gstate.isModeThrough() && //2D content requires orthographic projection + gstate.isAlphaBlendEnabled() && //2D content has to be blended + !gstate.isLightingEnabled() && //2D content cannot be rendered with lights on + !gstate.isFogEnabled(); //2D content cannot be rendered with fog on + if (hud) { + float scale = 0.5f; + render_->SetUniformF1(&u_scaleX, scale); + render_->SetUniformF1(&u_scaleY, scale / 480.0f * 272.0f); + } else { + render_->SetUniformF1(&u_scaleX, 1.0f); + render_->SetUniformF1(&u_scaleY, 1.0f); + } +#endif + // Update any dirty uniforms before we draw if (dirty & DIRTY_PROJMATRIX) { Matrix4x4 flippedMatrix; +#ifdef OPENXR + if (flatScreen || is2D) { + memcpy(&flippedMatrix, gstate.projMatrix, 16 * sizeof(float)); + } else { + VR_TweakProjection(gstate.projMatrix, flippedMatrix.m, VR_PROJECTION_MATRIX_LEFT_EYE); + VR_TweakMirroring(gstate.projMatrix); + } +#else memcpy(&flippedMatrix, gstate.projMatrix, 16 * sizeof(float)); +#endif const bool invertedY = useBufferedRendering ? (gstate_c.vpHeight < 0) : (gstate_c.vpHeight > 0); if (invertedY) { @@ -468,7 +519,18 @@ void LinkedShader::UpdateUniforms(u32 vertType, const ShaderID &vsid, bool useBu SetMatrix4x3(render_, &u_world, gstate.worldMatrix); } if (dirty & DIRTY_VIEWMATRIX) { +#ifdef OPENXR + if (flatScreen || is2D) { + SetMatrix4x3(render_, &u_view, gstate.viewMatrix); + } else { + float m4x4[16]; + ConvertMatrix4x3To4x4Transposed(m4x4, gstate.viewMatrix); + VR_TweakView(m4x4, gstate.projMatrix, VR_VIEW_MATRIX_LEFT_EYE); + render_->SetUniformM4x4(&u_view, m4x4); + } +#else SetMatrix4x3(render_, &u_view, gstate.viewMatrix); +#endif } if (dirty & DIRTY_TEXMATRIX) { SetMatrix4x3(render_, &u_texmtx, gstate.tgenMatrix); diff --git a/GPU/GLES/ShaderManagerGLES.h b/GPU/GLES/ShaderManagerGLES.h index bcf5130805..b6f6272d70 100644 --- a/GPU/GLES/ShaderManagerGLES.h +++ b/GPU/GLES/ShaderManagerGLES.h @@ -62,6 +62,8 @@ public: int u_cullRangeMax; int u_rotation; int u_mipBias; + int u_scaleX; + int u_scaleY; #ifdef USE_BONE_ARRAY int u_bone; // array, size is numBones diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index a21fcde46f..68f7d8a78a 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -224,6 +224,9 @@ void GameSettingsScreen::CreateViews() { auto ri = GetI18NCategory("RemoteISO"); auto ps = GetI18NCategory("PostShaders"); auto th = GetI18NCategory("Themes"); + auto vr = GetI18NCategory("VR"); + + int deviceType = System_GetPropertyInt(SYSPROP_DEVICE_TYPE); root_ = new AnchorLayout(new LayoutParams(FILL_PARENT, FILL_PARENT)); @@ -316,8 +319,10 @@ void GameSettingsScreen::CreateViews() { }); blockTransfer->SetDisabledPtr(&g_Config.bSoftwareRendering); - CheckBox *softwareGPU = graphicsSettings->Add(new CheckBox(&g_Config.bSoftwareRendering, gr->T("Software Rendering", "Software Rendering (slow)"))); - softwareGPU->SetEnabled(!PSP_IsInited()); + if (deviceType != DEVICE_TYPE_VR) { + CheckBox *softwareGPU = graphicsSettings->Add(new CheckBox(&g_Config.bSoftwareRendering, gr->T("Software Rendering", "Software Rendering (slow)"))); + softwareGPU->SetEnabled(!PSP_IsInited()); + } graphicsSettings->Add(new ItemHeader(gr->T("Frame Rate Control"))); static const char *frameSkip[] = {"Off", "1", "2", "3", "4", "5", "6", "7", "8"}; @@ -390,39 +395,39 @@ void GameSettingsScreen::CreateViews() { } } -#ifndef OPENXR - graphicsSettings->Add(new ItemHeader(gr->T("Screen layout"))); + if (deviceType != DEVICE_TYPE_VR) { + graphicsSettings->Add(new ItemHeader(gr->T("Screen layout"))); #if !defined(MOBILE_DEVICE) - graphicsSettings->Add(new CheckBox(&g_Config.bFullScreen, gr->T("FullScreen", "Full Screen")))->OnClick.Handle(this, &GameSettingsScreen::OnFullscreenChange); - if (System_GetPropertyInt(SYSPROP_DISPLAY_COUNT) > 1) { - CheckBox *fullscreenMulti = new CheckBox(&g_Config.bFullScreenMulti, gr->T("Use all displays")); - fullscreenMulti->SetEnabledFunc([] { - return g_Config.UseFullScreen(); - }); - graphicsSettings->Add(fullscreenMulti)->OnClick.Handle(this, &GameSettingsScreen::OnFullscreenMultiChange); - } + graphicsSettings->Add(new CheckBox(&g_Config.bFullScreen, gr->T("FullScreen", "Full Screen")))->OnClick.Handle(this, &GameSettingsScreen::OnFullscreenChange); + if (System_GetPropertyInt(SYSPROP_DISPLAY_COUNT) > 1) { + CheckBox *fullscreenMulti = new CheckBox(&g_Config.bFullScreenMulti, gr->T("Use all displays")); + fullscreenMulti->SetEnabledFunc([] { + return g_Config.UseFullScreen(); + }); + graphicsSettings->Add(fullscreenMulti)->OnClick.Handle(this, &GameSettingsScreen::OnFullscreenMultiChange); + } #endif - // Display Layout Editor: To avoid overlapping touch controls on large tablets, meet geeky demands for integer zoom/unstretched image etc. - displayEditor_ = graphicsSettings->Add(new Choice(gr->T("Display layout editor"))); - displayEditor_->OnClick.Handle(this, &GameSettingsScreen::OnDisplayLayoutEditor); + // Display Layout Editor: To avoid overlapping touch controls on large tablets, meet geeky demands for integer zoom/unstretched image etc. + displayEditor_ = graphicsSettings->Add(new Choice(gr->T("Display layout editor"))); + displayEditor_->OnClick.Handle(this, &GameSettingsScreen::OnDisplayLayoutEditor); #if PPSSPP_PLATFORM(ANDROID) - // Hide insets option if no insets, or OS too old. - if (System_GetPropertyInt(SYSPROP_SYSTEMVERSION) >= 28 && - (System_GetPropertyFloat(SYSPROP_DISPLAY_SAFE_INSET_LEFT) != 0.0f || - System_GetPropertyFloat(SYSPROP_DISPLAY_SAFE_INSET_TOP) != 0.0f || - System_GetPropertyFloat(SYSPROP_DISPLAY_SAFE_INSET_RIGHT) != 0.0f || - System_GetPropertyFloat(SYSPROP_DISPLAY_SAFE_INSET_BOTTOM) != 0.0f)) { - graphicsSettings->Add(new CheckBox(&g_Config.bIgnoreScreenInsets, gr->T("Ignore camera notch when centering"))); - } + // Hide insets option if no insets, or OS too old. + if (System_GetPropertyInt(SYSPROP_SYSTEMVERSION) >= 28 && + (System_GetPropertyFloat(SYSPROP_DISPLAY_SAFE_INSET_LEFT) != 0.0f || + System_GetPropertyFloat(SYSPROP_DISPLAY_SAFE_INSET_TOP) != 0.0f || + System_GetPropertyFloat(SYSPROP_DISPLAY_SAFE_INSET_RIGHT) != 0.0f || + System_GetPropertyFloat(SYSPROP_DISPLAY_SAFE_INSET_BOTTOM) != 0.0f)) { + graphicsSettings->Add(new CheckBox(&g_Config.bIgnoreScreenInsets, gr->T("Ignore camera notch when centering"))); + } - // Hide Immersive Mode on pre-kitkat Android - if (System_GetPropertyInt(SYSPROP_SYSTEMVERSION) >= 19) { - // Let's reuse the Fullscreen translation string from desktop. - graphicsSettings->Add(new CheckBox(&g_Config.bImmersiveMode, gr->T("FullScreen", "Full Screen")))->OnClick.Handle(this, &GameSettingsScreen::OnImmersiveModeChange); + // Hide Immersive Mode on pre-kitkat Android + if (System_GetPropertyInt(SYSPROP_SYSTEMVERSION) >= 19) { + // Let's reuse the Fullscreen translation string from desktop. + graphicsSettings->Add(new CheckBox(&g_Config.bImmersiveMode, gr->T("FullScreen", "Full Screen")))->OnClick.Handle(this, &GameSettingsScreen::OnImmersiveModeChange); + } +#endif } -#endif -#endif graphicsSettings->Add(new ItemHeader(gr->T("Performance"))); static const char *internalResolutions[] = { "Auto (1:1)", "1x PSP", "2x PSP", "3x PSP", "4x PSP", "5x PSP", "6x PSP", "7x PSP", "8x PSP", "9x PSP", "10x PSP" }; @@ -433,7 +438,6 @@ void GameSettingsScreen::CreateViews() { }); #if PPSSPP_PLATFORM(ANDROID) - int deviceType = System_GetPropertyInt(SYSPROP_DEVICE_TYPE); if ((deviceType != DEVICE_TYPE_TV) && (deviceType != DEVICE_TYPE_VR)) { static const char *deviceResolutions[] = { "Native device resolution", "Auto (same as Rendering)", "1x PSP", "2x PSP", "3x PSP", "4x PSP", "5x PSP" }; int max_res_temp = std::max(System_GetPropertyInt(SYSPROP_DISPLAY_XRES), System_GetPropertyInt(SYSPROP_DISPLAY_YRES)) / 480 + 2; @@ -468,8 +472,10 @@ void GameSettingsScreen::CreateViews() { inflightChoice->OnChoice.Handle(this, &GameSettingsScreen::OnInflightFramesChoice); } - CheckBox *hwTransform = graphicsSettings->Add(new CheckBox(&g_Config.bHardwareTransform, gr->T("Hardware Transform"))); - hwTransform->SetDisabledPtr(&g_Config.bSoftwareRendering); + if (deviceType != DEVICE_TYPE_VR) { + CheckBox *hwTransform = graphicsSettings->Add(new CheckBox(&g_Config.bHardwareTransform, gr->T("Hardware Transform"))); + hwTransform->SetDisabledPtr(&g_Config.bSoftwareRendering); + } CheckBox *swSkin = graphicsSettings->Add(new CheckBox(&g_Config.bSoftwareSkinning, gr->T("Software Skinning"))); swSkin->OnClick.Add([=](EventParams &e) { @@ -588,10 +594,8 @@ void GameSettingsScreen::CreateViews() { static const char *bufFilters[] = { "Linear", "Nearest", }; graphicsSettings->Add(new PopupMultiChoice(&g_Config.iBufFilter, gr->T("Screen Scaling Filter"), bufFilters, 1, ARRAY_SIZE(bufFilters), gr->GetName(), screenManager())); -#ifdef OPENXR - bool showCardboardSettings = false; -#elif PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(IOS) - bool showCardboardSettings = true; +#ifdef PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(IOS) + bool showCardboardSettings = deviceType != DEVICE_TYPE_VR; #else // If you enabled it through the ini, you can see this. Useful for testing. bool showCardboardSettings = g_Config.bEnableCardboardVR; @@ -717,11 +721,12 @@ void GameSettingsScreen::CreateViews() { customizeTilt->SetEnabledFunc([] { return g_Config.iTiltInputType != 0; }); + } else if (System_GetPropertyInt(SYSPROP_DEVICE_TYPE) == DEVICE_TYPE_VR) { + controlsSettings->Add(new CheckBox(&g_Config.bHapticFeedback, co->T("HapticFeedback", "Haptic Feedback (vibration)"))); } -#ifndef OPENXR // TVs don't have touch control, at least not yet. - if (System_GetPropertyInt(SYSPROP_DEVICE_TYPE) != DEVICE_TYPE_TV) { + if ((deviceType != DEVICE_TYPE_TV) && (deviceType != DEVICE_TYPE_VR)) { controlsSettings->Add(new ItemHeader(co->T("OnScreen", "On-Screen Touch Controls"))); controlsSettings->Add(new CheckBox(&g_Config.bShowTouchControls, co->T("OnScreen", "On-Screen Touch Controls"))); layoutEditorChoice_ = controlsSettings->Add(new Choice(co->T("Customize Touch Controls"))); @@ -769,29 +774,30 @@ void GameSettingsScreen::CreateViews() { gesture->SetEnabledPtr(&g_Config.bShowTouchControls); } - controlsSettings->Add(new ItemHeader(co->T("Keyboard", "Keyboard Control Settings"))); + if (deviceType != DEVICE_TYPE_VR) { + controlsSettings->Add(new ItemHeader(co->T("Keyboard", "Keyboard Control Settings"))); #if defined(USING_WIN_UI) - controlsSettings->Add(new CheckBox(&g_Config.bIgnoreWindowsKey, co->T("Ignore Windows Key"))); + controlsSettings->Add(new CheckBox(&g_Config.bIgnoreWindowsKey, co->T("Ignore Windows Key"))); #endif // #if defined(USING_WIN_UI) - auto analogLimiter = new PopupSliderChoiceFloat(&g_Config.fAnalogLimiterDeadzone, 0.0f, 1.0f, co->T("Analog Limiter"), 0.10f, screenManager(), "/ 1.0"); - controlsSettings->Add(analogLimiter); - analogLimiter->OnChange.Add([=](EventParams &e) { - settingInfo_->Show(co->T("AnalogLimiter Tip", "When the analog limiter button is pressed"), e.v); - return UI::EVENT_CONTINUE; - }); + auto analogLimiter = new PopupSliderChoiceFloat(&g_Config.fAnalogLimiterDeadzone, 0.0f, 1.0f, co->T("Analog Limiter"), 0.10f, screenManager(), "/ 1.0"); + controlsSettings->Add(analogLimiter); + analogLimiter->OnChange.Add([=](EventParams &e) { + settingInfo_->Show(co->T("AnalogLimiter Tip", "When the analog limiter button is pressed"), e.v); + return UI::EVENT_CONTINUE; + }); #if defined(USING_WIN_UI) || defined(SDL) - controlsSettings->Add(new ItemHeader(co->T("Mouse", "Mouse settings"))); - CheckBox *mouseControl = controlsSettings->Add(new CheckBox(&g_Config.bMouseControl, co->T("Use Mouse Control"))); - mouseControl->OnClick.Add([=](EventParams &e) { - if(g_Config.bMouseControl) - settingInfo_->Show(co->T("MouseControl Tip", "You can now map mouse in control mapping screen by pressing the 'M' icon."), e.v); - return UI::EVENT_CONTINUE; - }); - controlsSettings->Add(new CheckBox(&g_Config.bMouseConfine, co->T("Confine Mouse", "Trap mouse within window/display area")))->SetEnabledPtr(&g_Config.bMouseControl); - controlsSettings->Add(new PopupSliderChoiceFloat(&g_Config.fMouseSensitivity, 0.01f, 1.0f, co->T("Mouse sensitivity"), 0.01f, screenManager(), "x"))->SetEnabledPtr(&g_Config.bMouseControl); - controlsSettings->Add(new PopupSliderChoiceFloat(&g_Config.fMouseSmoothing, 0.0f, 0.95f, co->T("Mouse smoothing"), 0.05f, screenManager(), "x"))->SetEnabledPtr(&g_Config.bMouseControl); -#endif + controlsSettings->Add(new ItemHeader(co->T("Mouse", "Mouse settings"))); + CheckBox *mouseControl = controlsSettings->Add(new CheckBox(&g_Config.bMouseControl, co->T("Use Mouse Control"))); + mouseControl->OnClick.Add([=](EventParams &e) { + if(g_Config.bMouseControl) + settingInfo_->Show(co->T("MouseControl Tip", "You can now map mouse in control mapping screen by pressing the 'M' icon."), e.v); + return UI::EVENT_CONTINUE; + }); + controlsSettings->Add(new CheckBox(&g_Config.bMouseConfine, co->T("Confine Mouse", "Trap mouse within window/display area")))->SetEnabledPtr(&g_Config.bMouseControl); + controlsSettings->Add(new PopupSliderChoiceFloat(&g_Config.fMouseSensitivity, 0.01f, 1.0f, co->T("Mouse sensitivity"), 0.01f, screenManager(), "x"))->SetEnabledPtr(&g_Config.bMouseControl); + controlsSettings->Add(new PopupSliderChoiceFloat(&g_Config.fMouseSmoothing, 0.0f, 0.95f, co->T("Mouse smoothing"), 0.05f, screenManager(), "x"))->SetEnabledPtr(&g_Config.bMouseControl); #endif + } LinearLayout *networkingSettings = AddTab("GameSettingsNetworking", ms->T("Networking")); @@ -1106,6 +1112,17 @@ void GameSettingsScreen::CreateViews() { ApplySearchFilter(); } #endif + + if (deviceType == DEVICE_TYPE_VR) { + LinearLayout *vrSettings = AddTab("GameSettingsVR", ms->T("VR")); + vrSettings->Add(new ItemHeader(vr->T("Virtual reality"))); + vrSettings->Add(new CheckBox(&g_Config.bEnableVR, vr->T("Enable virtual reality"))); + CheckBox *vr6DoF = vrSettings->Add(new CheckBox(&g_Config.bEnable6DoF, vr->T("Enable 6 degrees of freedom movement"))); + vr6DoF->SetEnabledPtr(&g_Config.bEnableVR); + PopupSliderChoice *vrFieldOfView = vrSettings->Add(new PopupSliderChoice(&g_Config.iFieldOfViewPercentage, 100, 150, vr->T("Field of view scale", "Headset's field of view scale"), 10, screenManager(), vr->T("% of native FoV"))); + vrFieldOfView->SetEnabledPtr(&g_Config.bEnableVR); + vrSettings->Add(new PopupSliderChoice(&g_Config.iCanvasDistance, 1, 10, vr->T("Distance to 2D menus and scenes", "Distance to 2D menus and scenes"), 1, screenManager(), "")); + } } UI::LinearLayout *GameSettingsScreen::AddTab(const char *tag, const std::string &title, bool isSearch) { diff --git a/UI/MainScreen.cpp b/UI/MainScreen.cpp index cb5fbf0116..3e025d06ba 100644 --- a/UI/MainScreen.cpp +++ b/UI/MainScreen.cpp @@ -1137,13 +1137,12 @@ void MainScreen::CreateViews() { rightColumnItems->Add(new Choice(mm->T("Game Settings", "Settings")))->OnClick.Handle(this, &MainScreen::OnGameSettings); rightColumnItems->Add(new Choice(mm->T("Credits")))->OnClick.Handle(this, &MainScreen::OnCredits); rightColumnItems->Add(new Choice(mm->T("www.ppsspp.org")))->OnClick.Handle(this, &MainScreen::OnPPSSPPOrg); -#ifndef OPENXR - if (!System_GetPropertyBool(SYSPROP_APP_GOLD)) { + + if (!System_GetPropertyBool(SYSPROP_APP_GOLD) && (System_GetPropertyInt(SYSPROP_DEVICE_TYPE) != DEVICE_TYPE_VR)) { Choice *gold = rightColumnItems->Add(new Choice(mm->T("Buy PPSSPP Gold"))); gold->OnClick.Handle(this, &MainScreen::OnSupport); gold->SetIcon(ImageID("I_ICONGOLD"), 0.5f); } -#endif #if !PPSSPP_PLATFORM(UWP) // Having an exit button is against UWP guidelines. diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index cc1e08ff4d..d740c323bc 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -142,6 +142,10 @@ #include #endif +#ifdef OPENXR +#include "VR/VRRenderer.h" +#endif + ScreenManager *screenManager; std::string config_filename; @@ -1297,6 +1301,19 @@ bool NativeTouch(const TouchInput &touch) { } bool NativeKey(const KeyInput &key) { + + // Hack to quick enable 2D mode in VR game mode. +#ifdef OPENXR + std::vector nativeKeys; + if (KeyMap::KeyToPspButton(key.deviceId, key.keyCode, &nativeKeys)) { + for (int& nativeKey : nativeKeys) { + if (nativeKey == CTRL_SCREEN) { + VR_SetConfig(VR_CONFIG_FORCE_2D, key.flags & KEY_DOWN); + } + } + } +#endif + // INFO_LOG(SYSTEM, "Key code: %i flags: %i", key.keyCode, key.flags); #if !defined(MOBILE_DEVICE) if (g_Config.bPauseExitsEmulator) { diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index 53013337b7..aaf9d290db 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -94,6 +94,7 @@ struct JNIEnv {}; #include "UI/GameInfoCache.h" #ifdef OPENXR +#include "Core/HLE/sceDisplay.h" #include "VR/VRBase.h" #include "VR/VRInput.h" #include "VR/VRRenderer.h" @@ -818,6 +819,8 @@ retry: java.AppVersion = gitVer.ToInteger(); strcpy(java.AppName, "PPSSPP"); VR_Init(java); + + __DisplaySetFramerate(72); #endif } @@ -934,6 +937,15 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeApp_shutdown(JNIEnv *, jclass) { // JavaEGL extern "C" bool Java_org_ppsspp_ppsspp_NativeRenderer_displayInit(JNIEnv * env, jobject obj) { + +#ifdef OPENXR + if (!renderer_inited) { + VR_EnterVR(VR_GetEngine()); + IN_VRInit(VR_GetEngine()); + } + VR_InitRenderer(VR_GetEngine()); +#endif + // We should be running on the render thread here. std::string errorMessage; if (renderer_inited) { @@ -990,11 +1002,6 @@ extern "C" bool Java_org_ppsspp_ppsspp_NativeRenderer_displayInit(JNIEnv * env, }, nullptr); graphicsContext->ThreadStart(); -#ifdef OPENXR - VR_EnterVR(VR_GetEngine()); - VR_InitRenderer(VR_GetEngine()); - IN_VRInit(VR_GetEngine()); -#endif renderer_inited = true; } NativeMessageReceived("recreateviews", ""); @@ -1131,6 +1138,9 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeRenderer_displayRender(JNIEnv *env, keyInput.deviceId = controllerIds[j]; if (m.pressed != pressed) { + if (pressed && g_Config.bHapticFeedback) { + INVR_Vibrate(100, j, 1000); + } NativeKey(keyInput); m.pressed = pressed; m.repeat = 0;