mirror of
https://github.com/izzy2lost/ppsspp.git
synced 2026-03-10 12:43:04 -07:00
These naturally come in bunches on many platforms like Android, so lay some groundwork to also handle them in bunches to minimize locking in the future. Linux buildfix
964 lines
30 KiB
C++
964 lines
30 KiB
C++
#include "Common/VR/PPSSPPVR.h"
|
|
|
|
#include "Common/VR/VRBase.h"
|
|
#if XR_USE_GRAPHICS_API_OPENGL || XR_USE_GRAPHICS_API_OPENGL_ES
|
|
#include "Common/GPU/OpenGL/GLRenderManager.h"
|
|
#endif
|
|
|
|
#include "Common/VR/VRInput.h"
|
|
#include "Common/VR/VRMath.h"
|
|
#include "Common/VR/VRRenderer.h"
|
|
|
|
#include "Common/Input/InputState.h"
|
|
#include "Common/Input/KeyCodes.h"
|
|
|
|
#include "Common/GPU/Vulkan/VulkanContext.h"
|
|
|
|
#include "Common/Math/lin/matrix4x4.h"
|
|
|
|
#include "Common/Input/InputState.h"
|
|
#include "Common/Input/KeyCodes.h"
|
|
|
|
#include "Core/HLE/sceDisplay.h"
|
|
#include "Core/HLE/sceCtrl.h"
|
|
|
|
#include "Core/Config.h"
|
|
#include "Core/KeyMap.h"
|
|
#include "Core/System.h"
|
|
|
|
enum VRMatrix {
|
|
VR_PROJECTION_MATRIX,
|
|
VR_VIEW_MATRIX_LEFT_EYE,
|
|
VR_VIEW_MATRIX_RIGHT_EYE,
|
|
VR_MATRIX_COUNT
|
|
};
|
|
|
|
enum VRMirroring {
|
|
VR_MIRRORING_AXIS_X,
|
|
VR_MIRRORING_AXIS_Y,
|
|
VR_MIRRORING_AXIS_Z,
|
|
VR_MIRRORING_PITCH,
|
|
VR_MIRRORING_YAW,
|
|
VR_MIRRORING_ROLL,
|
|
VR_MIRRORING_COUNT
|
|
};
|
|
|
|
static VRAppMode appMode = VR_MENU_MODE;
|
|
static std::map<int, std::map<int, float> > pspAxis;
|
|
static std::map<int, bool> pspKeys; // key can be virtual, so not using the enum.
|
|
|
|
static int vr3DGeometryCount = 0;
|
|
static long vrCompat[VR_COMPAT_MAX];
|
|
static bool vrFlatForced = false;
|
|
static bool vrFlatGame = false;
|
|
static float vrMatrix[VR_MATRIX_COUNT][16];
|
|
static bool vrMirroring[VR_MIRRORING_COUNT];
|
|
static int vrMirroringVariant = 0;
|
|
static XrView vrView[2];
|
|
|
|
static void (*cbNativeAxis)(const AxisInput *axis, size_t count);
|
|
static bool (*cbNativeKey)(const KeyInput &key);
|
|
static void (*cbNativeTouch)(const TouchInput &touch);
|
|
|
|
/*
|
|
================================================================================
|
|
|
|
VR button mapping
|
|
|
|
================================================================================
|
|
*/
|
|
|
|
struct ButtonMapping {
|
|
ovrButton ovr;
|
|
InputKeyCode keycode;
|
|
bool pressed;
|
|
int repeat;
|
|
|
|
ButtonMapping(InputKeyCode keycode, ovrButton ovr) {
|
|
this->keycode = keycode;
|
|
this->ovr = ovr;
|
|
pressed = false;
|
|
repeat = 0;
|
|
}
|
|
};
|
|
|
|
static std::vector<ButtonMapping> leftControllerMapping = {
|
|
ButtonMapping(NKCODE_BUTTON_X, ovrButton_X),
|
|
ButtonMapping(NKCODE_BUTTON_Y, ovrButton_Y),
|
|
ButtonMapping(NKCODE_ALT_LEFT, ovrButton_GripTrigger),
|
|
ButtonMapping(NKCODE_DPAD_UP, ovrButton_Up),
|
|
ButtonMapping(NKCODE_DPAD_DOWN, ovrButton_Down),
|
|
ButtonMapping(NKCODE_DPAD_LEFT, ovrButton_Left),
|
|
ButtonMapping(NKCODE_DPAD_RIGHT, ovrButton_Right),
|
|
ButtonMapping(NKCODE_BUTTON_THUMBL, ovrButton_LThumb),
|
|
ButtonMapping(NKCODE_ENTER, ovrButton_Trigger),
|
|
ButtonMapping(NKCODE_BACK, ovrButton_Enter),
|
|
};
|
|
|
|
static std::vector<ButtonMapping> rightControllerMapping = {
|
|
ButtonMapping(NKCODE_BUTTON_A, ovrButton_A),
|
|
ButtonMapping(NKCODE_BUTTON_B, ovrButton_B),
|
|
ButtonMapping(NKCODE_ALT_RIGHT, ovrButton_GripTrigger),
|
|
ButtonMapping(NKCODE_DPAD_UP, ovrButton_Up),
|
|
ButtonMapping(NKCODE_DPAD_DOWN, ovrButton_Down),
|
|
ButtonMapping(NKCODE_DPAD_LEFT, ovrButton_Left),
|
|
ButtonMapping(NKCODE_DPAD_RIGHT, ovrButton_Right),
|
|
ButtonMapping(NKCODE_BUTTON_THUMBR, ovrButton_RThumb),
|
|
ButtonMapping(NKCODE_ENTER, ovrButton_Trigger),
|
|
};
|
|
|
|
static const InputDeviceID controllerIds[] = {DEVICE_ID_XR_CONTROLLER_LEFT, DEVICE_ID_XR_CONTROLLER_RIGHT};
|
|
static std::vector<ButtonMapping> controllerMapping[2] = {
|
|
leftControllerMapping,
|
|
rightControllerMapping
|
|
};
|
|
static bool controllerMotion[2][5] = {};
|
|
static bool hmdMotion[4] = {};
|
|
static float hmdMotionLast[2] = {};
|
|
static float hmdMotionDiff[2] = {};
|
|
static float hmdMotionDiffLast[2] = {};
|
|
static int mouseController = 1;
|
|
static bool mousePressed = false;
|
|
|
|
inline float clampFloat(float x, float minValue, float maxValue) {
|
|
if (x < minValue) return minValue;
|
|
if (x > maxValue) return maxValue;
|
|
return x;
|
|
}
|
|
|
|
/*
|
|
================================================================================
|
|
|
|
VR app flow integration
|
|
|
|
================================================================================
|
|
*/
|
|
|
|
bool IsVREnabled() {
|
|
// For now, let the OPENXR build flag control enablement.
|
|
// This will change.
|
|
#ifdef OPENXR
|
|
return true;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
#if PPSSPP_PLATFORM(ANDROID)
|
|
void InitVROnAndroid(void* vm, void* activity, const char* system, int version, const char* name) {
|
|
|
|
//Get device vendor (uppercase)
|
|
char vendor[64];
|
|
sscanf(system, "%[^:]", vendor);
|
|
for (unsigned int i = 0; i < strlen(vendor); i++) {
|
|
if ((vendor[i] >= 'a') && (vendor[i] <= 'z')) {
|
|
vendor[i] = vendor[i] - 'a' + 'A';
|
|
}
|
|
}
|
|
|
|
//Set platform flags
|
|
if (strcmp(vendor, "PICO") == 0) {
|
|
VR_SetPlatformFLag(VR_PLATFORM_CONTROLLER_PICO, true);
|
|
VR_SetPlatformFLag(VR_PLATFORM_EXTENSION_INSTANCE, true);
|
|
} else if ((strcmp(vendor, "META") == 0) || (strcmp(vendor, "OCULUS") == 0)) {
|
|
VR_SetPlatformFLag(VR_PLATFORM_CONTROLLER_QUEST, true);
|
|
VR_SetPlatformFLag(VR_PLATFORM_EXTENSION_FOVEATION, true);
|
|
VR_SetPlatformFLag(VR_PLATFORM_EXTENSION_PASSTHROUGH, true);
|
|
VR_SetPlatformFLag(VR_PLATFORM_EXTENSION_PERFORMANCE, true);
|
|
}
|
|
VR_SetPlatformFLag(VR_PLATFORM_RENDERER_VULKAN, (GPUBackend)g_Config.iGPUBackend == GPUBackend::VULKAN);
|
|
|
|
//Init VR
|
|
ovrJava java;
|
|
java.Vm = (JavaVM*)vm;
|
|
java.ActivityObject = (jobject)activity;
|
|
VR_Init(&java, name, version);
|
|
}
|
|
#endif
|
|
|
|
void EnterVR(bool firstStart, void* vulkanContext) {
|
|
if (firstStart) {
|
|
engine_t* engine = VR_GetEngine();
|
|
bool useVulkan = (GPUBackend)g_Config.iGPUBackend == GPUBackend::VULKAN;
|
|
if (useVulkan) {
|
|
auto* context = (VulkanContext*)vulkanContext;
|
|
engine->graphicsBindingVulkan = {};
|
|
engine->graphicsBindingVulkan.type = XR_TYPE_GRAPHICS_BINDING_VULKAN_KHR;
|
|
engine->graphicsBindingVulkan.next = NULL;
|
|
engine->graphicsBindingVulkan.device = context->GetDevice();
|
|
engine->graphicsBindingVulkan.instance = context->GetInstance();
|
|
engine->graphicsBindingVulkan.physicalDevice = context->GetCurrentPhysicalDevice();
|
|
engine->graphicsBindingVulkan.queueFamilyIndex = context->GetGraphicsQueueFamilyIndex();
|
|
engine->graphicsBindingVulkan.queueIndex = 0;
|
|
VR_EnterVR(engine, &engine->graphicsBindingVulkan);
|
|
} else {
|
|
VR_EnterVR(engine, nullptr);
|
|
}
|
|
IN_VRInit(engine);
|
|
}
|
|
VR_SetConfig(VR_CONFIG_VIEWPORT_VALID, false);
|
|
}
|
|
|
|
void GetVRResolutionPerEye(int* width, int* height) {
|
|
if (VR_GetEngine()->appState.Instance) {
|
|
VR_GetResolution(VR_GetEngine(), width, height);
|
|
}
|
|
}
|
|
|
|
void SetVRCallbacks(void (*axis)(const AxisInput *axis, size_t count), bool(*key)(const KeyInput &key), void (*touch)(const TouchInput &touch)) {
|
|
cbNativeAxis = axis;
|
|
cbNativeKey = key;
|
|
cbNativeTouch = touch;
|
|
}
|
|
|
|
/*
|
|
================================================================================
|
|
|
|
VR input integration
|
|
|
|
================================================================================
|
|
*/
|
|
|
|
void SetVRAppMode(VRAppMode mode) {
|
|
appMode = mode;
|
|
}
|
|
|
|
void UpdateVRInput(bool haptics, float dp_xscale, float dp_yscale) {
|
|
//axis
|
|
if (pspKeys[(int)VIRTKEY_VR_CAMERA_ADJUST]) {
|
|
AxisInput axis[2] = {};
|
|
axis[0].deviceId = DEVICE_ID_DEFAULT;
|
|
axis[1].deviceId = DEVICE_ID_DEFAULT;
|
|
for (int j = 0; j < 2; j++) {
|
|
XrVector2f joystick = IN_VRGetJoystickState(j);
|
|
|
|
//horizontal
|
|
axis[0].axisId = j == 0 ? JOYSTICK_AXIS_X : JOYSTICK_AXIS_Z;
|
|
axis[0].value = joystick.x;
|
|
|
|
//vertical
|
|
axis[1].axisId = j == 0 ? JOYSTICK_AXIS_Y : JOYSTICK_AXIS_RZ;
|
|
axis[1].value = -joystick.y;
|
|
cbNativeAxis(axis, 2);
|
|
}
|
|
}
|
|
|
|
//buttons
|
|
KeyInput keyInput = {};
|
|
for (int j = 0; j < 2; j++) {
|
|
int status = IN_VRGetButtonState(j);
|
|
for (ButtonMapping& m : controllerMapping[j]) {
|
|
|
|
//fill KeyInput structure
|
|
bool pressed = status & m.ovr;
|
|
keyInput.flags = pressed ? KEY_DOWN : KEY_UP;
|
|
keyInput.keyCode = m.keycode;
|
|
keyInput.deviceId = controllerIds[j];
|
|
|
|
//process the key action
|
|
|
|
if (m.pressed != pressed) {
|
|
if (pressed && haptics) {
|
|
INVR_Vibrate(100, j, 1000);
|
|
}
|
|
cbNativeKey(keyInput);
|
|
m.pressed = pressed;
|
|
m.repeat = 0;
|
|
} else if (pressed && (m.repeat > 30)) {
|
|
keyInput.flags |= KEY_IS_REPEAT;
|
|
cbNativeKey(keyInput);
|
|
m.repeat = 0;
|
|
} else {
|
|
m.repeat++;
|
|
}
|
|
}
|
|
}
|
|
|
|
//motion control
|
|
if (g_Config.bEnableMotions) {
|
|
for (int j = 0; j < 2; j++) {
|
|
bool activate;
|
|
float limit = g_Config.fMotionLength; //length of needed movement in meters
|
|
XrVector3f axis = {0, 1, 0};
|
|
float center = ToRadians(VR_GetConfigFloat(VR_CONFIG_MENU_YAW));
|
|
XrQuaternionf orientation = XrQuaternionf_CreateFromVectorAngle(axis, center);
|
|
XrVector3f position = XrQuaternionf_Rotate(orientation, IN_VRGetPose(j).position);
|
|
|
|
//up
|
|
activate = position.y > limit;
|
|
keyInput.flags = activate ? KEY_DOWN : KEY_UP;
|
|
keyInput.keyCode = NKCODE_EXT_MOTION_UP;
|
|
keyInput.deviceId = controllerIds[j];
|
|
if (controllerMotion[j][0] != activate) cbNativeKey(keyInput);
|
|
controllerMotion[j][0] = activate;
|
|
|
|
//down
|
|
activate = position.y < -limit * 1.5f;
|
|
keyInput.flags = activate ? KEY_DOWN : KEY_UP;
|
|
keyInput.keyCode = NKCODE_EXT_MOTION_DOWN;
|
|
keyInput.deviceId = controllerIds[j];
|
|
if (controllerMotion[j][1] != activate) cbNativeKey(keyInput);
|
|
controllerMotion[j][1] = activate;
|
|
|
|
//left
|
|
activate = position.x < -limit * (j == 0 ? 1.0f : 0.25f);
|
|
keyInput.flags = activate ? KEY_DOWN : KEY_UP;
|
|
keyInput.keyCode = NKCODE_EXT_MOTION_LEFT;
|
|
keyInput.deviceId = controllerIds[j];
|
|
if (controllerMotion[j][2] != activate) cbNativeKey(keyInput);
|
|
controllerMotion[j][2] = activate;
|
|
|
|
//right
|
|
activate = position.x > limit * (j == 1 ? 1.0f : 0.25f);
|
|
keyInput.flags = activate ? KEY_DOWN : KEY_UP;
|
|
keyInput.keyCode = NKCODE_EXT_MOTION_RIGHT;
|
|
keyInput.deviceId = controllerIds[j];
|
|
if (controllerMotion[j][3] != activate) cbNativeKey(keyInput);
|
|
controllerMotion[j][3] = activate;
|
|
|
|
//forward
|
|
activate = position.z < -limit;
|
|
keyInput.flags = activate ? KEY_DOWN : KEY_UP;
|
|
keyInput.keyCode = NKCODE_EXT_MOTION_FORWARD;
|
|
keyInput.deviceId = controllerIds[j];
|
|
if (controllerMotion[j][4] != activate) cbNativeKey(keyInput);
|
|
controllerMotion[j][4] = activate;
|
|
}
|
|
}
|
|
|
|
// Head control
|
|
if (g_Config.bHeadRotationEnabled) {
|
|
float pitch = -VR_GetHMDAngles().x;
|
|
float yaw = -VR_GetHMDAngles().y;
|
|
bool disable = vrFlatForced || appMode == VR_MENU_MODE;
|
|
bool isVR = !IsFlatVRScene();
|
|
|
|
// calculate delta angles of the rotation
|
|
if (isVR) {
|
|
float f = g_Config.bHeadRotationSmoothing ? 0.5f : 1.0f;
|
|
float deltaPitch = pitch - hmdMotionLast[0];
|
|
float deltaYaw = yaw - hmdMotionLast[1];
|
|
while (deltaYaw >= 180) deltaYaw -= 360;
|
|
while (deltaYaw < -180) deltaYaw += 360;
|
|
hmdMotionLast[0] = pitch;
|
|
hmdMotionLast[1] = yaw;
|
|
hmdMotionDiffLast[0] = hmdMotionDiffLast[0] * (1-f) + hmdMotionDiff[0] * f;
|
|
hmdMotionDiffLast[1] = hmdMotionDiffLast[1] * (1-f) + hmdMotionDiff[1] * f;
|
|
hmdMotionDiff[0] += deltaPitch;
|
|
hmdMotionDiff[1] += deltaYaw;
|
|
pitch = hmdMotionDiff[0];
|
|
yaw = hmdMotionDiff[1];
|
|
}
|
|
|
|
bool activate;
|
|
float limit = isVR ? g_Config.fHeadRotationScale : 20;
|
|
keyInput.deviceId = DEVICE_ID_XR_HMD;
|
|
|
|
//left
|
|
activate = !disable && yaw < -limit;
|
|
keyInput.flags = activate ? KEY_DOWN : KEY_UP;
|
|
keyInput.keyCode = NKCODE_EXT_ROTATION_LEFT;
|
|
if (hmdMotion[2] != activate) cbNativeKey(keyInput);
|
|
if (isVR && activate) hmdMotionDiff[1] += limit;
|
|
hmdMotion[2] = activate;
|
|
|
|
//right
|
|
activate = !disable && yaw > limit;
|
|
keyInput.flags = activate ? KEY_DOWN : KEY_UP;
|
|
keyInput.keyCode = NKCODE_EXT_ROTATION_RIGHT;
|
|
if (hmdMotion[3] != activate) cbNativeKey(keyInput);
|
|
if (isVR && activate) hmdMotionDiff[1] -= limit;
|
|
hmdMotion[3] = activate;
|
|
}
|
|
|
|
// Camera adjust
|
|
if (pspKeys[VIRTKEY_VR_CAMERA_ADJUST]) {
|
|
for (auto& device : pspAxis) {
|
|
for (auto& axis : device.second) {
|
|
switch(axis.first) {
|
|
case JOYSTICK_AXIS_X:
|
|
if (axis.second < -0.75f) g_Config.fCameraSide -= 0.1f;
|
|
if (axis.second > 0.75f) g_Config.fCameraSide += 0.1f;
|
|
g_Config.fCameraSide = clampFloat(g_Config.fCameraSide, -150.0f, 150.0f);
|
|
break;
|
|
case JOYSTICK_AXIS_Y:
|
|
if (axis.second > 0.75f) g_Config.fCameraHeight -= 0.1f;
|
|
if (axis.second < -0.75f) g_Config.fCameraHeight += 0.1f;
|
|
g_Config.fCameraHeight = clampFloat(g_Config.fCameraHeight, -150.0f, 150.0f);
|
|
break;
|
|
case JOYSTICK_AXIS_Z:
|
|
if (g_Config.bEnableVR) {
|
|
if (axis.second < -0.75f) g_Config.fHeadUpDisplayScale -= 0.01f;
|
|
if (axis.second > 0.75f) g_Config.fHeadUpDisplayScale += 0.01f;
|
|
g_Config.fHeadUpDisplayScale = clampFloat(g_Config.fHeadUpDisplayScale, 0.0f, 1.5f);
|
|
} else {
|
|
if (axis.second < -0.75f) g_Config.fCanvas3DDistance += 0.1f;
|
|
if (axis.second > 0.75f) g_Config.fCanvas3DDistance -= 0.1f;
|
|
g_Config.fCanvas3DDistance = clampFloat(g_Config.fCanvas3DDistance, 1.0f, 15.0f);
|
|
}
|
|
break;
|
|
case JOYSTICK_AXIS_RZ:
|
|
if (axis.second > 0.75f) g_Config.fCameraDistance -= 0.1f;
|
|
if (axis.second < -0.75f) g_Config.fCameraDistance += 0.1f;
|
|
g_Config.fCameraDistance = clampFloat(g_Config.fCameraDistance, -150.0f, 150.0f);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//enable or disable mouse
|
|
for (int j = 0; j < 2; j++) {
|
|
bool pressed = IN_VRGetButtonState(j) & ovrButton_Trigger;
|
|
if (pressed) {
|
|
int lastController = mouseController;
|
|
mouseController = j;
|
|
|
|
//prevent misclicks when changing the left/right controller
|
|
if (lastController != mouseController) {
|
|
mousePressed = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
//mouse cursor
|
|
if ((mouseController >= 0) && ((appMode == VR_DIALOG_MODE) || (appMode == VR_MENU_MODE))) {
|
|
//get position on screen
|
|
XrPosef pose = IN_VRGetPose(mouseController);
|
|
XrVector3f angles = XrQuaternionf_ToEulerAngles(pose.orientation);
|
|
float width = (float)VR_GetConfig(VR_CONFIG_VIEWPORT_WIDTH);
|
|
float height = (float)VR_GetConfig(VR_CONFIG_VIEWPORT_HEIGHT);
|
|
float cx = width / 2;
|
|
float cy = height / 2;
|
|
float speed = (cx + cy) / 2;
|
|
float x = cx - tan(ToRadians(angles.y - VR_GetConfigFloat(VR_CONFIG_MENU_YAW))) * speed;
|
|
float y = cy - tan(ToRadians(angles.x)) * speed * VR_GetConfigFloat(VR_CONFIG_CANVAS_ASPECT);
|
|
|
|
//set renderer
|
|
VR_SetConfig(VR_CONFIG_MOUSE_X, (int)x);
|
|
VR_SetConfig(VR_CONFIG_MOUSE_Y, (int)y);
|
|
VR_SetConfig(VR_CONFIG_MOUSE_SIZE, 6 * (int)pow(VR_GetConfigFloat(VR_CONFIG_CANVAS_DISTANCE), 0.25f));
|
|
VR_SetConfig(VR_CONFIG_CANVAS_6DOF, g_Config.bEnable6DoF);
|
|
|
|
//inform engine about the status
|
|
TouchInput touch;
|
|
touch.id = mouseController;
|
|
touch.x = x * dp_xscale;
|
|
touch.y = (height - y - 1) * dp_yscale / VR_GetConfigFloat(VR_CONFIG_CANVAS_ASPECT);
|
|
bool pressed = IN_VRGetButtonState(mouseController) & ovrButton_Trigger;
|
|
if (mousePressed != pressed) {
|
|
if (pressed) {
|
|
touch.flags = TOUCH_DOWN;
|
|
cbNativeTouch(touch);
|
|
touch.flags = TOUCH_UP;
|
|
cbNativeTouch(touch);
|
|
}
|
|
mousePressed = pressed;
|
|
}
|
|
|
|
// mouse wheel emulation
|
|
// TODO: Spams key-up events if nothing changed!
|
|
for (int j = 0; j < 2; j++) {
|
|
keyInput.deviceId = controllerIds[j];
|
|
float scroll = -IN_VRGetJoystickState(j).y;
|
|
keyInput.flags = scroll < -0.5f ? KEY_DOWN : KEY_UP;
|
|
keyInput.keyCode = NKCODE_EXT_MOUSEWHEEL_UP;
|
|
cbNativeKey(keyInput);
|
|
keyInput.flags = scroll > 0.5f ? KEY_DOWN : KEY_UP;
|
|
keyInput.keyCode = NKCODE_EXT_MOUSEWHEEL_DOWN;
|
|
cbNativeKey(keyInput);
|
|
}
|
|
} else {
|
|
VR_SetConfig(VR_CONFIG_MOUSE_SIZE, 0);
|
|
}
|
|
}
|
|
|
|
bool UpdateVRAxis(const AxisInput &axis) {
|
|
if (pspAxis.find(axis.deviceId) == pspAxis.end()) {
|
|
pspAxis[axis.deviceId] = std::map<int, float>();
|
|
}
|
|
pspAxis[axis.deviceId][axis.axisId] = axis.value;
|
|
return !pspKeys[VIRTKEY_VR_CAMERA_ADJUST];
|
|
}
|
|
|
|
bool UpdateVRKeys(const KeyInput &key) {
|
|
//store key value
|
|
std::vector<int> nativeKeys;
|
|
bool wasScreenKeyOn = pspKeys[CTRL_SCREEN];
|
|
bool wasCameraAdjustOn = pspKeys[VIRTKEY_VR_CAMERA_ADJUST];
|
|
if (KeyMap::InputMappingToPspButton(InputMapping(key.deviceId, key.keyCode), &nativeKeys)) {
|
|
for (int& nativeKey : nativeKeys) {
|
|
pspKeys[nativeKey] = key.flags & KEY_DOWN;
|
|
}
|
|
}
|
|
|
|
//block VR controller keys in the UI mode
|
|
if ((key.deviceId == controllerIds[0]) || (key.deviceId == controllerIds[1])) {
|
|
if ((appMode == VR_DIALOG_MODE) || (appMode == VR_MENU_MODE)) {
|
|
switch (key.keyCode) {
|
|
case NKCODE_BACK:
|
|
case NKCODE_EXT_MOUSEWHEEL_UP:
|
|
case NKCODE_EXT_MOUSEWHEEL_DOWN:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Update force flat 2D mode
|
|
if (g_Config.bManualForceVR) {
|
|
if (!wasScreenKeyOn && pspKeys[CTRL_SCREEN]) {
|
|
vrFlatForced = !vrFlatForced;
|
|
}
|
|
} else {
|
|
vrFlatForced = pspKeys[CTRL_SCREEN];
|
|
}
|
|
|
|
// Release keys on enabling camera adjust
|
|
if (!wasCameraAdjustOn && pspKeys[VIRTKEY_VR_CAMERA_ADJUST]) {
|
|
KeyInput keyUp;
|
|
keyUp.deviceId = key.deviceId;
|
|
keyUp.flags = KEY_UP;
|
|
|
|
pspKeys[VIRTKEY_VR_CAMERA_ADJUST] = false;
|
|
for (auto& pspKey : pspKeys) {
|
|
if (pspKey.second) {
|
|
keyUp.keyCode = (InputKeyCode)pspKey.first;
|
|
cbNativeKey(keyUp);
|
|
}
|
|
}
|
|
pspKeys[VIRTKEY_VR_CAMERA_ADJUST] = true;
|
|
}
|
|
|
|
// Reset camera adjust
|
|
if (pspKeys[VIRTKEY_VR_CAMERA_ADJUST] && pspKeys[VIRTKEY_VR_CAMERA_RESET]) {
|
|
g_Config.fCanvas3DDistance = 3.0f;
|
|
g_Config.fCameraHeight = 0;
|
|
g_Config.fCameraSide = 0;
|
|
g_Config.fCameraDistance = 0;
|
|
g_Config.fHeadUpDisplayScale = 0.3f;
|
|
}
|
|
|
|
//block keys by camera adjust
|
|
return !pspKeys[VIRTKEY_VR_CAMERA_ADJUST];
|
|
}
|
|
|
|
/*
|
|
================================================================================
|
|
|
|
// VR games compatibility
|
|
|
|
================================================================================
|
|
*/
|
|
|
|
#if XR_USE_GRAPHICS_API_OPENGL || XR_USE_GRAPHICS_API_OPENGL_ES
|
|
|
|
void PreprocessSkyplane(GLRStep* step) {
|
|
|
|
// Do not do anything if the scene is not in VR.
|
|
if (IsFlatVRScene()) {
|
|
return;
|
|
}
|
|
|
|
// Check if it is the step we need to modify.
|
|
for (auto& cmd : step->commands) {
|
|
if (cmd.cmd == GLRRenderCommand::BIND_FB_TEXTURE) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Clear sky with the fog color.
|
|
if (!vrCompat[VR_COMPAT_FBO_CLEAR]) {
|
|
GLRRenderData &skyClear = step->commands.insert(step->commands.begin());
|
|
skyClear.cmd = GLRRenderCommand::CLEAR;
|
|
skyClear.clear.colorMask = 0xF;
|
|
skyClear.clear.clearMask = GL_COLOR_BUFFER_BIT; // don't need to initialize clearZ, clearStencil
|
|
skyClear.clear.clearColor = vrCompat[VR_COMPAT_FOG_COLOR];
|
|
skyClear.clear.scissorX = 0;
|
|
skyClear.clear.scissorY = 0;
|
|
skyClear.clear.scissorW = 0; // signal no scissor
|
|
skyClear.clear.scissorH = 0;
|
|
vrCompat[VR_COMPAT_FBO_CLEAR] = true;
|
|
}
|
|
|
|
// Remove original sky plane.
|
|
bool depthEnabled = false;
|
|
for (auto& command : step->commands) {
|
|
if (command.cmd == GLRRenderCommand::DEPTH) {
|
|
depthEnabled = command.depth.enabled;
|
|
} else if ((command.cmd == GLRRenderCommand::DRAW && command.draw.indexBuffer != nullptr) && !depthEnabled) {
|
|
command.draw.count = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
void PreprocessStepVR(void* step) {
|
|
auto* glrStep = (GLRStep*)step;
|
|
if (vrCompat[VR_COMPAT_SKYPLANE]) PreprocessSkyplane(glrStep);
|
|
}
|
|
|
|
#else
|
|
|
|
void PreprocessStepVR(void* step) {}
|
|
|
|
#endif
|
|
|
|
void SetVRCompat(VRCompatFlag flag, long value) {
|
|
vrCompat[flag] = value;
|
|
}
|
|
|
|
/*
|
|
================================================================================
|
|
|
|
VR rendering integration
|
|
|
|
================================================================================
|
|
*/
|
|
|
|
void* BindVRFramebuffer() {
|
|
return VR_BindFramebuffer(VR_GetEngine());
|
|
}
|
|
|
|
bool StartVRRender() {
|
|
if (!VR_GetConfig(VR_CONFIG_VIEWPORT_VALID)) {
|
|
VR_InitRenderer(VR_GetEngine(), IsMultiviewSupported());
|
|
VR_SetConfig(VR_CONFIG_VIEWPORT_VALID, true);
|
|
}
|
|
|
|
if (VR_InitFrame(VR_GetEngine())) {
|
|
|
|
// VR flags
|
|
bool vrIncompatibleGame = PSP_CoreParameter().compat.vrCompat().ForceFlatScreen;
|
|
bool vrScene = !vrFlatForced && (g_Config.bManualForceVR || (vr3DGeometryCount > 15));
|
|
bool vrStereo = !PSP_CoreParameter().compat.vrCompat().ForceMono && g_Config.bEnableStereo;
|
|
|
|
// Get VR status
|
|
for (int eye = 0; eye < ovrMaxNumEyes; eye++) {
|
|
vrView[eye] = VR_GetView(eye);
|
|
}
|
|
UpdateVRViewMatrices();
|
|
|
|
// Update projection matrix
|
|
XrFovf fov = {};
|
|
for (int eye = 0; eye < ovrMaxNumEyes; eye++) {
|
|
fov.angleLeft += vrView[eye].fov.angleLeft / 2.0f;
|
|
fov.angleRight += vrView[eye].fov.angleRight / 2.0f;
|
|
fov.angleUp += vrView[eye].fov.angleUp / 2.0f;
|
|
fov.angleDown += vrView[eye].fov.angleDown / 2.0f;
|
|
}
|
|
float nearZ = g_Config.fFieldOfViewPercentage / 200.0f;
|
|
float tanAngleLeft = tanf(fov.angleLeft);
|
|
float tanAngleRight = tanf(fov.angleRight);
|
|
float tanAngleDown = tanf(fov.angleDown);
|
|
float tanAngleUp = tanf(fov.angleUp);
|
|
float M[16] = {};
|
|
M[0] = 2 / (tanAngleRight - tanAngleLeft);
|
|
M[2] = (tanAngleRight + tanAngleLeft) / (tanAngleRight - tanAngleLeft);
|
|
M[5] = 2 / (tanAngleUp - tanAngleDown);
|
|
M[6] = (tanAngleUp + tanAngleDown) / (tanAngleUp - tanAngleDown);
|
|
M[10] = -1;
|
|
M[11] = -(nearZ + nearZ);
|
|
M[14] = -1;
|
|
memcpy(vrMatrix[VR_PROJECTION_MATRIX], M, sizeof(float) * 16);
|
|
|
|
// Decide if the scene is 3D or not
|
|
VR_SetConfigFloat(VR_CONFIG_CANVAS_ASPECT, 480.0f / 272.0f);
|
|
if (g_Config.bEnableVR && !vrIncompatibleGame && (appMode == VR_GAME_MODE) && vrScene) {
|
|
VR_SetConfig(VR_CONFIG_MODE, vrStereo ? VR_MODE_STEREO_6DOF : VR_MODE_MONO_6DOF);
|
|
vrFlatGame = false;
|
|
} else {
|
|
VR_SetConfig(VR_CONFIG_MODE, vrStereo ? VR_MODE_STEREO_SCREEN : VR_MODE_MONO_SCREEN);
|
|
if (IsGameVRScene()) {
|
|
vrFlatGame = true;
|
|
}
|
|
}
|
|
vr3DGeometryCount /= 2;
|
|
|
|
// Set compatibility
|
|
vrCompat[VR_COMPAT_SKYPLANE] = PSP_CoreParameter().compat.vrCompat().Skyplane;
|
|
|
|
// Set customizations
|
|
__DisplaySetFramerate(g_Config.bForce72Hz ? 72 : 60);
|
|
VR_SetConfigFloat(VR_CONFIG_CANVAS_DISTANCE, vrScene && (appMode == VR_GAME_MODE) ? g_Config.fCanvas3DDistance : g_Config.fCanvasDistance);
|
|
VR_SetConfig(VR_CONFIG_PASSTHROUGH, g_Config.bPassthrough);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void FinishVRRender() {
|
|
VR_FinishFrame(VR_GetEngine());
|
|
}
|
|
|
|
void PreVRFrameRender(int fboIndex) {
|
|
VR_BeginFrame(VR_GetEngine(), fboIndex);
|
|
vrCompat[VR_COMPAT_FBO_CLEAR] = false;
|
|
}
|
|
|
|
void PostVRFrameRender() {
|
|
VR_EndFrame(VR_GetEngine());
|
|
}
|
|
|
|
int GetVRFBOIndex() {
|
|
return VR_GetConfig(VR_CONFIG_CURRENT_FBO);
|
|
}
|
|
|
|
int GetVRPassesCount() {
|
|
bool vrStereo = !PSP_CoreParameter().compat.vrCompat().ForceMono && g_Config.bEnableStereo;
|
|
if (!IsMultiviewSupported() && vrStereo) {
|
|
return 2;
|
|
} else {
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
bool IsMultiviewSupported() {
|
|
return false;
|
|
}
|
|
|
|
bool IsPassthroughSupported() {
|
|
return VR_GetPlatformFlag(VR_PLATFORM_EXTENSION_PASSTHROUGH);
|
|
}
|
|
|
|
bool IsFlatVRGame() {
|
|
return vrFlatGame;
|
|
}
|
|
|
|
bool IsFlatVRScene() {
|
|
int vrMode = VR_GetConfig(VR_CONFIG_MODE);
|
|
return (vrMode == VR_MODE_MONO_SCREEN) || (vrMode == VR_MODE_STEREO_SCREEN);
|
|
}
|
|
|
|
bool IsGameVRScene() {
|
|
return (appMode == VR_GAME_MODE) || (appMode == VR_DIALOG_MODE);
|
|
}
|
|
|
|
bool Is2DVRObject(float* projMatrix, bool ortho) {
|
|
|
|
// Quick analyze if the object is in 2D
|
|
if ((fabs(fabs(projMatrix[12]) - 1.0f) < EPSILON) && (fabs(fabs(projMatrix[13]) - 1.0f) < EPSILON) && (fabs(fabs(projMatrix[14]) - 1.0f) < EPSILON)) {
|
|
return true;
|
|
} else if ((fabs(projMatrix[0]) > 10.0f) && (fabs(projMatrix[5]) > 10.0f)) {
|
|
return true;
|
|
} else if (fabs(projMatrix[15] - 1) < EPSILON) {
|
|
return true;
|
|
}
|
|
|
|
// Update 3D geometry count
|
|
bool identity = IsMatrixIdentity(projMatrix);
|
|
if (!identity && !ortho) {
|
|
vr3DGeometryCount++;
|
|
}
|
|
return identity || ortho;
|
|
}
|
|
|
|
void UpdateVRParams(float* projMatrix) {
|
|
|
|
// Set mirroring of axes
|
|
vrMirroring[VR_MIRRORING_AXIS_X] = projMatrix[0] < 0;
|
|
vrMirroring[VR_MIRRORING_AXIS_Y] = projMatrix[5] < 0;
|
|
vrMirroring[VR_MIRRORING_AXIS_Z] = projMatrix[10] > 0;
|
|
|
|
int variant = 1;
|
|
variant += projMatrix[0] < 0;
|
|
variant += (projMatrix[5] < 0) << 1;
|
|
variant += (projMatrix[10] < 0) << 2;
|
|
if (PSP_CoreParameter().compat.vrCompat().MirroringVariant > 0) {
|
|
variant = PSP_CoreParameter().compat.vrCompat().MirroringVariant;
|
|
}
|
|
|
|
switch (variant) {
|
|
case 1: //e.g. ATV
|
|
vrMirroring[VR_MIRRORING_PITCH] = false;
|
|
vrMirroring[VR_MIRRORING_YAW] = true;
|
|
vrMirroring[VR_MIRRORING_ROLL] = true;
|
|
break;
|
|
case 2: //e.g. Tales of the World
|
|
vrMirroring[VR_MIRRORING_PITCH] = false;
|
|
vrMirroring[VR_MIRRORING_YAW] = false;
|
|
vrMirroring[VR_MIRRORING_ROLL] = false;
|
|
break;
|
|
case 3: //e.g.PES 2014
|
|
case 4: //untested
|
|
case 6: //e.g Dante's Inferno
|
|
case 8: //untested
|
|
vrMirroring[VR_MIRRORING_PITCH] = true;
|
|
vrMirroring[VR_MIRRORING_YAW] = true;
|
|
vrMirroring[VR_MIRRORING_ROLL] = false;
|
|
break;
|
|
case 5: //e.g. Assassins Creed
|
|
case 7: //e.g. Ghost in the shell
|
|
vrMirroring[VR_MIRRORING_PITCH] = true;
|
|
vrMirroring[VR_MIRRORING_YAW] = false;
|
|
vrMirroring[VR_MIRRORING_ROLL] = true;
|
|
break;
|
|
default:
|
|
assert(false);
|
|
std::exit(1);
|
|
}
|
|
|
|
if (vrMirroringVariant != variant) {
|
|
vrMirroringVariant = variant;
|
|
UpdateVRViewMatrices();
|
|
}
|
|
}
|
|
|
|
void UpdateVRProjection(float* projMatrix, float* leftEye, float* rightEye) {
|
|
float* hmdProjection = vrMatrix[VR_PROJECTION_MATRIX];
|
|
for (int i = 0; i < 16; i++) {
|
|
if ((hmdProjection[i] > 0) != (projMatrix[i] > 0)) {
|
|
hmdProjection[i] *= -1.0f;
|
|
}
|
|
}
|
|
memcpy(leftEye, hmdProjection, 16 * sizeof(float));
|
|
memcpy(rightEye, hmdProjection, 16 * sizeof(float));
|
|
}
|
|
|
|
void UpdateVRView(float* leftEye, float* rightEye) {
|
|
float* dst[] = {leftEye, rightEye};
|
|
float* matrix[] = {vrMatrix[VR_VIEW_MATRIX_LEFT_EYE], vrMatrix[VR_VIEW_MATRIX_RIGHT_EYE]};
|
|
for (int index = 0; index < 2; index++) {
|
|
|
|
// Validate the view matrix
|
|
if (PSP_CoreParameter().compat.vrCompat().IdentityViewHack && IsMatrixIdentity(dst[index])) {
|
|
return;
|
|
}
|
|
|
|
// Get view matrix from the game
|
|
Lin::Matrix4x4 gameView = {};
|
|
memcpy(gameView.m, dst[index], 16 * sizeof(float));
|
|
|
|
// Get view matrix from the headset
|
|
Lin::Matrix4x4 hmdView = {};
|
|
memcpy(hmdView.m, matrix[index], 16 * sizeof(float));
|
|
|
|
// Combine the matrices
|
|
Lin::Matrix4x4 renderView = hmdView * gameView;
|
|
memcpy(dst[index], renderView.m, 16 * sizeof(float));
|
|
}
|
|
}
|
|
|
|
void UpdateVRViewMatrices() {
|
|
|
|
// Get 6DoF scale
|
|
float scale = 1.0f;
|
|
if (PSP_CoreParameter().compat.vrCompat().UnitsPerMeter > 0) {
|
|
scale = PSP_CoreParameter().compat.vrCompat().UnitsPerMeter;
|
|
}
|
|
|
|
// Get input
|
|
bool flatScreen = false;
|
|
XrPosef invView = vrView[0].pose;
|
|
int vrMode = VR_GetConfig(VR_CONFIG_MODE);
|
|
if ((vrMode == VR_MODE_MONO_SCREEN) || (vrMode == VR_MODE_STEREO_SCREEN)) {
|
|
invView = XrPosef_Identity();
|
|
flatScreen = true;
|
|
}
|
|
|
|
// get axis mirroring configuration
|
|
float mx = vrMirroring[VR_MIRRORING_PITCH] ? -1.0f : 1.0f;
|
|
float my = vrMirroring[VR_MIRRORING_YAW] ? -1.0f : 1.0f;
|
|
float mz = vrMirroring[VR_MIRRORING_ROLL] ? -1.0f : 1.0f;
|
|
|
|
// 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);
|
|
}
|
|
|
|
// apply camera pitch offset
|
|
XrVector3f positionOffset = {g_Config.fCameraSide, g_Config.fCameraHeight, g_Config.fCameraDistance};
|
|
if (!flatScreen) {
|
|
float pitchOffset = 0;
|
|
switch (g_Config.iCameraPitch) {
|
|
case 1: //Top view -> First person
|
|
pitchOffset = 90;
|
|
positionOffset = {positionOffset.x, positionOffset.z, -positionOffset.y};
|
|
break;
|
|
case 2: //First person -> Top view
|
|
pitchOffset = -90;
|
|
positionOffset = {positionOffset.x, -positionOffset.z + 20, positionOffset.y};
|
|
break;
|
|
}
|
|
XrQuaternionf rotationOffset = XrQuaternionf_CreateFromVectorAngle({1, 0, 0}, ToRadians(pitchOffset));
|
|
invView.orientation = XrQuaternionf_Multiply(rotationOffset, invView.orientation);
|
|
}
|
|
|
|
// decompose rotation
|
|
XrVector3f rotation = XrQuaternionf_ToEulerAngles(invView.orientation);
|
|
float mPitch = mx * ToRadians(rotation.x);
|
|
float mYaw = my * ToRadians(rotation.y);
|
|
float mRoll = mz * ToRadians(rotation.z);
|
|
|
|
// use in-game camera interpolated rotation
|
|
if (g_Config.bHeadRotationEnabled) mYaw = -my * ToRadians(hmdMotionDiffLast[1]); // horizontal
|
|
|
|
// create updated quaternion
|
|
XrQuaternionf pitch = XrQuaternionf_CreateFromVectorAngle({1, 0, 0}, mPitch);
|
|
XrQuaternionf yaw = XrQuaternionf_CreateFromVectorAngle({0, 1, 0}, mYaw);
|
|
XrQuaternionf roll = XrQuaternionf_CreateFromVectorAngle({0, 0, 1}, mRoll);
|
|
invView.orientation = XrQuaternionf_Multiply(roll, XrQuaternionf_Multiply(pitch, yaw));
|
|
|
|
float M[16];
|
|
XrQuaternionf_ToMatrix4f(&invView.orientation, M);
|
|
|
|
// Apply 6Dof head movement
|
|
if (g_Config.bEnable6DoF && !g_Config.bHeadRotationEnabled && (g_Config.iCameraPitch == 0)) {
|
|
M[3] -= vrView[0].pose.position.x * (vrMirroring[VR_MIRRORING_AXIS_X] ? -1.0f : 1.0f) * scale;
|
|
M[7] -= vrView[0].pose.position.y * (vrMirroring[VR_MIRRORING_AXIS_Y] ? -1.0f : 1.0f) * scale;
|
|
M[11] -= vrView[0].pose.position.z * (vrMirroring[VR_MIRRORING_AXIS_Z] ? -1.0f : 1.0f) * scale;
|
|
}
|
|
// Camera adjust - distance
|
|
if (fabsf(positionOffset.z) > 0.0f) {
|
|
XrVector3f forward = {0.0f, 0.0f, positionOffset.z * scale};
|
|
forward = XrQuaternionf_Rotate(invView.orientation, forward);
|
|
forward = XrVector3f_ScalarMultiply(forward, vrMirroring[VR_MIRRORING_AXIS_Z] ? -1.0f : 1.0f);
|
|
M[3] += forward.x;
|
|
M[7] += forward.y;
|
|
M[11] += forward.z;
|
|
}
|
|
// Camera adjust - height
|
|
if (fabsf(positionOffset.y) > 0.0f) {
|
|
XrVector3f up = {0.0f, -positionOffset.y * scale, 0.0f};
|
|
up = XrQuaternionf_Rotate(invView.orientation, up);
|
|
up = XrVector3f_ScalarMultiply(up, vrMirroring[VR_MIRRORING_AXIS_Y] ? -1.0f : 1.0f);
|
|
M[3] += up.x;
|
|
M[7] += up.y;
|
|
M[11] += up.z;
|
|
}
|
|
// Camera adjust - side
|
|
if (fabsf(positionOffset.x) > 0.0f) {
|
|
XrVector3f side = {-positionOffset.x * scale, 0.0f, 0.0f};
|
|
side = XrQuaternionf_Rotate(invView.orientation, side);
|
|
side = XrVector3f_ScalarMultiply(side, vrMirroring[VR_MIRRORING_AXIS_X] ? -1.0f : 1.0f);
|
|
M[3] += side.x;
|
|
M[7] += side.y;
|
|
M[11] += side.z;
|
|
}
|
|
|
|
for (int matrix = VR_VIEW_MATRIX_LEFT_EYE; matrix <= VR_VIEW_MATRIX_RIGHT_EYE; matrix++) {
|
|
|
|
// Stereoscopy
|
|
bool vrStereo = !PSP_CoreParameter().compat.vrCompat().ForceMono && g_Config.bEnableStereo;
|
|
if (vrStereo) {
|
|
bool mirrored = vrMirroring[VR_MIRRORING_AXIS_Z] ^ (matrix == VR_VIEW_MATRIX_RIGHT_EYE);
|
|
float dx = fabs(vrView[1].pose.position.x - vrView[0].pose.position.x);
|
|
float dy = fabs(vrView[1].pose.position.y - vrView[0].pose.position.y);
|
|
float dz = fabs(vrView[1].pose.position.z - vrView[0].pose.position.z);
|
|
float ipd = sqrt(dx * dx + dy * dy + dz * dz);
|
|
XrVector3f separation = {ipd * scale * 0.5f, 0.0f, 0.0f};
|
|
separation = XrQuaternionf_Rotate(invView.orientation, separation);
|
|
separation = XrVector3f_ScalarMultiply(separation, mirrored ? -1.0f : 2.0f);
|
|
M[3] += separation.x;
|
|
M[7] += separation.y;
|
|
M[11] += separation.z;
|
|
}
|
|
|
|
memcpy(vrMatrix[matrix], M, sizeof(float) * 16);
|
|
}
|
|
}
|