Merge pull request #15768 from lvonasek/feature_openxr_6dof

OpenXR - 6DoF support
This commit is contained in:
Henrik Rydgård 2022-08-24 18:49:41 +02:00 committed by GitHub
commit 416d8b403b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 925 additions and 545 deletions

View File

@ -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()

View File

@ -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.

View File

@ -12,6 +12,10 @@
#include <GLES3/gl3.h>
#include <GLES3/gl3ext.h>
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();
}

View File

@ -10,8 +10,6 @@
#include <openxr.h>
#include <openxr_platform.h>
#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;
}

View File

@ -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;
}
}
}

View File

@ -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 );

415
Common/VR/VRMath.cpp Normal file
View File

@ -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;
}

46
Common/VR/VRMath.h Normal file
View File

@ -0,0 +1,46 @@
#pragma once
#include <math.h>
#include <openxr.h>
#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);

View File

@ -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);
}

View File

@ -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 );

92
Common/VR/VRTweaks.cpp Normal file
View File

@ -0,0 +1,92 @@
#include "VRTweaks.h"
#include <iostream>
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));
}

12
Common/VR/VRTweaks.h Normal file
View File

@ -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);

View File

@ -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<void(Section *section, ConfigSetting *setting)> func) {

View File

@ -453,6 +453,12 @@ public:
int iFirmwareVersion;
bool bBypassOSKWithKeyboard;
// Virtual reality
bool bEnableVR;
bool bEnable6DoF;
int iCanvasDistance;
int iFieldOfViewPercentage;
// Debugger
int iDisasmWindowX;
int iDisasmWindowY;

View File

@ -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;
}

View File

@ -34,3 +34,5 @@ void __DisplaySetWasPaused();
void Register_sceDisplay_driver();
void __DisplayWaitForVblanks(const char* reason, int vblanks, bool callbacks = false);
void __DisplaySetFramerate(int value);

View File

@ -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[] = {

View File

@ -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;

View File

@ -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");

View File

@ -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 &params)
@ -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);

View File

@ -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

View File

@ -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) {

View File

@ -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.

View File

@ -142,6 +142,10 @@
#include <mach-o/dyld.h>
#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<int> 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) {

View File

@ -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;