mirror of
https://github.com/hrydgard/ppsspp.git
synced 2024-11-26 23:10:38 +00:00
Merge pull request #15768 from lvonasek/feature_openxr_6dof
OpenXR - 6DoF support
This commit is contained in:
commit
416d8b403b
@ -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()
|
||||
|
@ -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.
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
415
Common/VR/VRMath.cpp
Normal 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
46
Common/VR/VRMath.h
Normal 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);
|
@ -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);
|
||||
}
|
||||
|
@ -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
92
Common/VR/VRTweaks.cpp
Normal 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
12
Common/VR/VRTweaks.h
Normal 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);
|
@ -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) {
|
||||
|
@ -453,6 +453,12 @@ public:
|
||||
int iFirmwareVersion;
|
||||
bool bBypassOSKWithKeyboard;
|
||||
|
||||
// Virtual reality
|
||||
bool bEnableVR;
|
||||
bool bEnable6DoF;
|
||||
int iCanvasDistance;
|
||||
int iFieldOfViewPercentage;
|
||||
|
||||
// Debugger
|
||||
int iDisasmWindowX;
|
||||
int iDisasmWindowY;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -34,3 +34,5 @@ void __DisplaySetWasPaused();
|
||||
|
||||
void Register_sceDisplay_driver();
|
||||
void __DisplayWaitForVblanks(const char* reason, int vblanks, bool callbacks = false);
|
||||
|
||||
void __DisplaySetFramerate(int value);
|
||||
|
@ -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[] = {
|
||||
|
@ -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;
|
||||
|
@ -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");
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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.
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user