diff --git a/Projects/Android/AndroidManifest.xml b/Projects/Android/AndroidManifest.xml index ce3b2b7..2969b7b 100644 --- a/Projects/Android/AndroidManifest.xml +++ b/Projects/Android/AndroidManifest.xml @@ -1,8 +1,8 @@ + android:versionCode="34" + android:versionName="1.6.0" android:installLocation="auto" > @@ -17,9 +17,17 @@ + + + + + + + + diff --git a/Projects/Android/jni/QzDoom/OpenXrInput_MetaQuest.cpp b/Projects/Android/jni/QzDoom/OpenXrInput_MetaQuest.cpp new file mode 100644 index 0000000..c424dfe --- /dev/null +++ b/Projects/Android/jni/QzDoom/OpenXrInput_MetaQuest.cpp @@ -0,0 +1,500 @@ +#ifdef META_QUEST + +#include "VrInput.h" + +extern ovrApp gAppState; + +XrSpace CreateActionSpace(XrAction poseAction, XrPath subactionPath) { + XrActionSpaceCreateInfo asci = {}; + asci.type = XR_TYPE_ACTION_SPACE_CREATE_INFO; + asci.action = poseAction; + asci.poseInActionSpace.orientation.w = 1.0f; + asci.subactionPath = subactionPath; + XrSpace actionSpace = XR_NULL_HANDLE; + OXR(xrCreateActionSpace(gAppState.Session, &asci, &actionSpace)); + return actionSpace; +} + +XrActionSuggestedBinding ActionSuggestedBinding(XrAction action, const char* bindingString) { + XrActionSuggestedBinding asb; + asb.action = action; + XrPath bindingPath; + OXR(xrStringToPath(gAppState.Instance, bindingString, &bindingPath)); + asb.binding = bindingPath; + return asb; +} + +XrActionSet CreateActionSet(int priority, const char* name, const char* localizedName) { + XrActionSetCreateInfo asci = {}; + asci.type = XR_TYPE_ACTION_SET_CREATE_INFO; + asci.next = NULL; + asci.priority = priority; + strcpy(asci.actionSetName, name); + strcpy(asci.localizedActionSetName, localizedName); + XrActionSet actionSet = XR_NULL_HANDLE; + OXR(xrCreateActionSet(gAppState.Instance, &asci, &actionSet)); + return actionSet; +} + +XrAction CreateAction( + XrActionSet actionSet, + XrActionType type, + const char* actionName, + const char* localizedName, + int countSubactionPaths, + XrPath* subactionPaths) { + ALOGV("CreateAction %s, %" PRIi32, actionName, countSubactionPaths); + + XrActionCreateInfo aci = {}; + aci.type = XR_TYPE_ACTION_CREATE_INFO; + aci.next = NULL; + aci.actionType = type; + if (countSubactionPaths > 0) { + aci.countSubactionPaths = countSubactionPaths; + aci.subactionPaths = subactionPaths; + } + strcpy(aci.actionName, actionName); + strcpy(aci.localizedActionName, localizedName ? localizedName : actionName); + XrAction action = XR_NULL_HANDLE; + OXR(xrCreateAction(actionSet, &aci, &action)); + return action; +} + +bool ActionPoseIsActive(XrAction action, XrPath subactionPath) { + XrActionStateGetInfo getInfo = {}; + getInfo.type = XR_TYPE_ACTION_STATE_GET_INFO; + getInfo.action = action; + getInfo.subactionPath = subactionPath; + + XrActionStatePose state = {}; + state.type = XR_TYPE_ACTION_STATE_POSE; + OXR(xrGetActionStatePose(gAppState.Session, &getInfo, &state)); + return state.isActive != XR_FALSE; +} + +XrActionStateFloat GetActionStateFloat(XrAction action) { + XrActionStateGetInfo getInfo = {}; + getInfo.type = XR_TYPE_ACTION_STATE_GET_INFO; + getInfo.action = action; + + XrActionStateFloat state = {}; + state.type = XR_TYPE_ACTION_STATE_FLOAT; + + OXR(xrGetActionStateFloat(gAppState.Session, &getInfo, &state)); + return state; +} + +XrActionStateBoolean GetActionStateBoolean(XrAction action) { + XrActionStateGetInfo getInfo = {}; + getInfo.type = XR_TYPE_ACTION_STATE_GET_INFO; + getInfo.action = action; + + XrActionStateBoolean state = {}; + state.type = XR_TYPE_ACTION_STATE_BOOLEAN; + + OXR(xrGetActionStateBoolean(gAppState.Session, &getInfo, &state)); + return state; +} + +XrActionStateVector2f GetActionStateVector2(XrAction action) { + XrActionStateGetInfo getInfo = {}; + getInfo.type = XR_TYPE_ACTION_STATE_GET_INFO; + getInfo.action = action; + + XrActionStateVector2f state = {}; + state.type = XR_TYPE_ACTION_STATE_VECTOR2F; + + OXR(xrGetActionStateVector2f(gAppState.Session, &getInfo, &state)); + return state; +} + + +//OpenXR +XrPath leftHandPath; +XrPath rightHandPath; +XrAction handPoseLeftAction; +XrAction handPoseRightAction; +XrAction indexLeftAction; +XrAction indexRightAction; +XrAction menuAction; +XrAction buttonAAction; +XrAction buttonBAction; +XrAction buttonXAction; +XrAction buttonYAction; +XrAction gripLeftAction; +XrAction gripRightAction; +XrAction moveOnLeftJoystickAction; +XrAction moveOnRightJoystickAction; +XrAction thumbstickLeftClickAction; +XrAction thumbstickRightClickAction; +XrAction vibrateLeftFeedback; +XrAction vibrateRightFeedback; +XrActionSet runningActionSet; +XrSpace leftControllerAimSpace = XR_NULL_HANDLE; +XrSpace rightControllerAimSpace = XR_NULL_HANDLE; +bool inputInitialized = false; +bool useSimpleProfile = false; + +void TBXR_InitActions( void ) +{ + // Actions + runningActionSet = CreateActionSet(1, "running_action_set", "Action Set used on main loop"); + indexLeftAction = CreateAction(runningActionSet, XR_ACTION_TYPE_BOOLEAN_INPUT, "index_left", "Index left", 0, NULL); + indexRightAction = CreateAction(runningActionSet, XR_ACTION_TYPE_BOOLEAN_INPUT, "index_right", "Index right", 0, NULL); + menuAction = CreateAction(runningActionSet, XR_ACTION_TYPE_BOOLEAN_INPUT, "menu_action", "Menu", 0, NULL); + buttonAAction = CreateAction(runningActionSet, XR_ACTION_TYPE_BOOLEAN_INPUT, "button_a", "Button A", 0, NULL); + buttonBAction = CreateAction(runningActionSet, XR_ACTION_TYPE_BOOLEAN_INPUT, "button_b", "Button B", 0, NULL); + buttonXAction = CreateAction(runningActionSet, XR_ACTION_TYPE_BOOLEAN_INPUT, "button_x", "Button X", 0, NULL); + buttonYAction = CreateAction(runningActionSet, XR_ACTION_TYPE_BOOLEAN_INPUT, "button_y", "Button Y", 0, NULL); + gripLeftAction = CreateAction(runningActionSet, XR_ACTION_TYPE_FLOAT_INPUT, "grip_left", "Grip left", 0, NULL); + gripRightAction = CreateAction(runningActionSet, XR_ACTION_TYPE_FLOAT_INPUT, "grip_right", "Grip right", 0, NULL); + moveOnLeftJoystickAction = CreateAction(runningActionSet, XR_ACTION_TYPE_VECTOR2F_INPUT, "move_on_left_joy", "Move on left Joy", 0, NULL); + moveOnRightJoystickAction = CreateAction(runningActionSet, XR_ACTION_TYPE_VECTOR2F_INPUT, "move_on_right_joy", "Move on right Joy", 0, NULL); + thumbstickLeftClickAction = CreateAction(runningActionSet, XR_ACTION_TYPE_BOOLEAN_INPUT, "thumbstick_left", "Thumbstick left", 0, NULL); + thumbstickRightClickAction = CreateAction(runningActionSet, XR_ACTION_TYPE_BOOLEAN_INPUT, "thumbstick_right", "Thumbstick right", 0, NULL); + vibrateLeftFeedback = CreateAction(runningActionSet, XR_ACTION_TYPE_VIBRATION_OUTPUT, "vibrate_left_feedback", "Vibrate Left Controller Feedback", 0, NULL); + vibrateRightFeedback = CreateAction(runningActionSet, XR_ACTION_TYPE_VIBRATION_OUTPUT, "vibrate_right_feedback", "Vibrate Right Controller Feedback", 0, NULL); + + OXR(xrStringToPath(gAppState.Instance, "/user/hand/left", &leftHandPath)); + OXR(xrStringToPath(gAppState.Instance, "/user/hand/right", &rightHandPath)); + handPoseLeftAction = CreateAction(runningActionSet, XR_ACTION_TYPE_POSE_INPUT, "hand_pose_left", NULL, 1, &leftHandPath); + handPoseRightAction = CreateAction(runningActionSet, XR_ACTION_TYPE_POSE_INPUT, "hand_pose_right", NULL, 1, &rightHandPath); + + if (leftControllerAimSpace == XR_NULL_HANDLE) { + leftControllerAimSpace = CreateActionSpace(handPoseLeftAction, leftHandPath); + } + if (rightControllerAimSpace == XR_NULL_HANDLE) { + rightControllerAimSpace = CreateActionSpace(handPoseRightAction, rightHandPath); + } + + XrPath interactionProfilePath = XR_NULL_PATH; + XrPath interactionProfilePathTouch = XR_NULL_PATH; + XrPath interactionProfilePathKHRSimple = XR_NULL_PATH; + + OXR(xrStringToPath(gAppState.Instance, "/interaction_profiles/oculus/touch_controller", &interactionProfilePathTouch)); + OXR(xrStringToPath(gAppState.Instance, "/interaction_profiles/khr/simple_controller", &interactionProfilePathKHRSimple)); + + // Toggle this to force simple as a first choice, otherwise use it as a last resort + if (useSimpleProfile) { + ALOGV("xrSuggestInteractionProfileBindings found bindings for Khronos SIMPLE controller"); + interactionProfilePath = interactionProfilePathKHRSimple; + } else { + // Query Set + XrActionSet queryActionSet = CreateActionSet(1, "query_action_set", "Action Set used to query device caps"); + XrAction dummyAction = CreateAction(queryActionSet, XR_ACTION_TYPE_BOOLEAN_INPUT, "dummy_action", "Dummy Action", 0, NULL); + + // Map bindings + XrActionSuggestedBinding bindings[1]; + int currBinding = 0; + bindings[currBinding++] = ActionSuggestedBinding(dummyAction, "/user/hand/right/input/system/click"); + + XrInteractionProfileSuggestedBinding suggestedBindings = {}; + suggestedBindings.type = XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING; + suggestedBindings.next = NULL; + suggestedBindings.suggestedBindings = bindings; + suggestedBindings.countSuggestedBindings = currBinding; + + // Try all + suggestedBindings.interactionProfile = interactionProfilePathTouch; + XrResult suggestTouchResult = xrSuggestInteractionProfileBindings(gAppState.Instance, &suggestedBindings); + OXR(suggestTouchResult); + + if (XR_SUCCESS == suggestTouchResult) { + ALOGV("xrSuggestInteractionProfileBindings found bindings for QUEST controller"); + interactionProfilePath = interactionProfilePathTouch; + } + + if (interactionProfilePath == XR_NULL_PATH) { + // Simple as a fallback + bindings[0] = ActionSuggestedBinding(dummyAction, "/user/hand/right/input/select/click"); + suggestedBindings.interactionProfile = interactionProfilePathKHRSimple; + XrResult suggestKHRSimpleResult = xrSuggestInteractionProfileBindings(gAppState.Instance, &suggestedBindings); + OXR(suggestKHRSimpleResult); + if (XR_SUCCESS == suggestKHRSimpleResult) { + ALOGV("xrSuggestInteractionProfileBindings found bindings for Khronos SIMPLE controller"); + interactionProfilePath = interactionProfilePathKHRSimple; + } else { + ALOGE("xrSuggestInteractionProfileBindings did NOT find any bindings."); + assert(false); + } + } + } + + // Action creation + { + // Map bindings + XrActionSuggestedBinding bindings[32]; // large enough for all profiles + int currBinding = 0; + + { + if (interactionProfilePath == interactionProfilePathTouch) { + bindings[currBinding++] = ActionSuggestedBinding(indexLeftAction, "/user/hand/left/input/trigger"); + bindings[currBinding++] = ActionSuggestedBinding(indexRightAction, "/user/hand/right/input/trigger"); + bindings[currBinding++] = ActionSuggestedBinding(menuAction, "/user/hand/left/input/menu/click"); + bindings[currBinding++] = ActionSuggestedBinding(buttonXAction, "/user/hand/left/input/x/click"); + bindings[currBinding++] = ActionSuggestedBinding(buttonYAction, "/user/hand/left/input/y/click"); + bindings[currBinding++] = ActionSuggestedBinding(buttonAAction, "/user/hand/right/input/a/click"); + bindings[currBinding++] = ActionSuggestedBinding(buttonBAction, "/user/hand/right/input/b/click"); + bindings[currBinding++] = ActionSuggestedBinding(gripLeftAction, "/user/hand/left/input/squeeze/value"); + bindings[currBinding++] = ActionSuggestedBinding(gripRightAction, "/user/hand/right/input/squeeze/value"); + bindings[currBinding++] = ActionSuggestedBinding(moveOnLeftJoystickAction, "/user/hand/left/input/thumbstick"); + bindings[currBinding++] = ActionSuggestedBinding(moveOnRightJoystickAction, "/user/hand/right/input/thumbstick"); + bindings[currBinding++] = ActionSuggestedBinding(thumbstickLeftClickAction, "/user/hand/left/input/thumbstick/click"); + bindings[currBinding++] = ActionSuggestedBinding(thumbstickRightClickAction, "/user/hand/right/input/thumbstick/click"); + bindings[currBinding++] = ActionSuggestedBinding(vibrateLeftFeedback, "/user/hand/left/output/haptic"); + bindings[currBinding++] = ActionSuggestedBinding(vibrateRightFeedback, "/user/hand/right/output/haptic"); + bindings[currBinding++] = ActionSuggestedBinding(handPoseLeftAction, "/user/hand/left/input/aim/pose"); + bindings[currBinding++] = ActionSuggestedBinding(handPoseRightAction, "/user/hand/right/input/aim/pose"); + } + + if (interactionProfilePath == interactionProfilePathKHRSimple) { + bindings[currBinding++] = ActionSuggestedBinding(indexLeftAction, "/user/hand/left/input/select/click"); + bindings[currBinding++] = ActionSuggestedBinding(indexRightAction, "/user/hand/right/input/select/click"); + bindings[currBinding++] = ActionSuggestedBinding(buttonAAction, "/user/hand/left/input/menu/click"); + bindings[currBinding++] = ActionSuggestedBinding(buttonXAction, "/user/hand/right/input/menu/click"); + bindings[currBinding++] = ActionSuggestedBinding(vibrateLeftFeedback, "/user/hand/left/output/haptic"); + bindings[currBinding++] = ActionSuggestedBinding(vibrateRightFeedback, "/user/hand/right/output/haptic"); + bindings[currBinding++] = ActionSuggestedBinding(handPoseLeftAction, "/user/hand/left/input/aim/pose"); + bindings[currBinding++] = ActionSuggestedBinding(handPoseRightAction, "/user/hand/right/input/aim/pose"); + } + } + + XrInteractionProfileSuggestedBinding suggestedBindings = {}; + suggestedBindings.type = XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING; + suggestedBindings.next = NULL; + suggestedBindings.interactionProfile = interactionProfilePath; + suggestedBindings.suggestedBindings = bindings; + suggestedBindings.countSuggestedBindings = currBinding; + OXR(xrSuggestInteractionProfileBindings(gAppState.Instance, &suggestedBindings)); + + // Enumerate actions + XrPath actionPathsBuffer[32]; + char stringBuffer[256]; + XrAction actionsToEnumerate[] = { + indexLeftAction, + indexRightAction, + menuAction, + buttonAAction, + buttonBAction, + buttonXAction, + buttonYAction, + gripLeftAction, + gripRightAction, + moveOnLeftJoystickAction, + moveOnRightJoystickAction, + thumbstickLeftClickAction, + thumbstickRightClickAction, + vibrateLeftFeedback, + vibrateRightFeedback, + handPoseLeftAction, + handPoseRightAction + }; + for (size_t i = 0; i < sizeof(actionsToEnumerate) / sizeof(actionsToEnumerate[0]); ++i) { + XrBoundSourcesForActionEnumerateInfo enumerateInfo = {}; + enumerateInfo.type = XR_TYPE_BOUND_SOURCES_FOR_ACTION_ENUMERATE_INFO; + enumerateInfo.next = NULL; + enumerateInfo.action = actionsToEnumerate[i]; + + // Get Count + uint32_t countOutput = 0; + OXR(xrEnumerateBoundSourcesForAction( + gAppState.Session, &enumerateInfo, 0 /* request size */, &countOutput, NULL)); + ALOGV( + "xrEnumerateBoundSourcesForAction action=%lld count=%u", + (long long)enumerateInfo.action, + countOutput); + + if (countOutput < 32) { + OXR(xrEnumerateBoundSourcesForAction( + gAppState.Session, &enumerateInfo, 32, &countOutput, actionPathsBuffer)); + for (uint32_t a = 0; a < countOutput; ++a) { + XrInputSourceLocalizedNameGetInfo nameGetInfo = {}; + nameGetInfo.type = XR_TYPE_INPUT_SOURCE_LOCALIZED_NAME_GET_INFO; + nameGetInfo.next = NULL; + nameGetInfo.sourcePath = actionPathsBuffer[a]; + nameGetInfo.whichComponents = XR_INPUT_SOURCE_LOCALIZED_NAME_USER_PATH_BIT | + XR_INPUT_SOURCE_LOCALIZED_NAME_INTERACTION_PROFILE_BIT | + XR_INPUT_SOURCE_LOCALIZED_NAME_COMPONENT_BIT; + + uint32_t stringCount = 0u; + OXR(xrGetInputSourceLocalizedName( + gAppState.Session, &nameGetInfo, 0, &stringCount, NULL)); + if (stringCount < 256) { + OXR(xrGetInputSourceLocalizedName( + gAppState.Session, &nameGetInfo, 256, &stringCount, stringBuffer)); + char pathStr[256]; + uint32_t strLen = 0; + OXR(xrPathToString( + gAppState.Instance, + actionPathsBuffer[a], + (uint32_t)sizeof(pathStr), + &strLen, + pathStr)); + ALOGV( + " -> path = %lld `%s` -> `%s`", + (long long)actionPathsBuffer[a], + pathStr, + stringBuffer); + } + } + } + } + } + + // Attach to session + XrSessionActionSetsAttachInfo attachInfo = {}; + attachInfo.type = XR_TYPE_SESSION_ACTION_SETS_ATTACH_INFO; + attachInfo.next = NULL; + attachInfo.countActionSets = 1; + attachInfo.actionSets = &runningActionSet; + OXR(xrAttachSessionActionSets(gAppState.Session, &attachInfo)); + + inputInitialized = true; +} + +void TBXR_SyncActions( void ) +{ + // sync action data + XrActiveActionSet activeActionSet = {}; + activeActionSet.actionSet = runningActionSet; + activeActionSet.subactionPath = XR_NULL_PATH; + + XrActionsSyncInfo syncInfo = {}; + syncInfo.type = XR_TYPE_ACTIONS_SYNC_INFO; + syncInfo.next = NULL; + syncInfo.countActiveActionSets = 1; + syncInfo.activeActionSets = &activeActionSet; + OXR(xrSyncActions(gAppState.Session, &syncInfo)); + + // query input action states + XrActionStateGetInfo getInfo = {}; + getInfo.type = XR_TYPE_ACTION_STATE_GET_INFO; + getInfo.next = NULL; + getInfo.subactionPath = XR_NULL_PATH; +} + +void TBXR_UpdateControllers( ) +{ + TBXR_SyncActions(); + + //get controller poses + XrAction controller[] = {handPoseLeftAction, handPoseRightAction}; + XrPath subactionPath[] = {leftHandPath, rightHandPath}; + XrSpace controllerSpace[] = {leftControllerAimSpace, rightControllerAimSpace}; + for (int i = 0; i < 2; i++) { + if (ActionPoseIsActive(controller[i], subactionPath[i])) { + XrSpaceVelocity vel = {}; + vel.type = XR_TYPE_SPACE_VELOCITY; + XrSpaceLocation loc = {}; + loc.type = XR_TYPE_SPACE_LOCATION; + loc.next = &vel; + OXR(xrLocateSpace(controllerSpace[i], gAppState.CurrentSpace, gAppState.PredictedDisplayTime, &loc)); + + gAppState.TrackedController[i].Active = (loc.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) != 0; + gAppState.TrackedController[i].Pose = loc.pose; + gAppState.TrackedController[i].Velocity = vel; + } else { + ovrTrackedController_Clear(&gAppState.TrackedController[i]); + } + } + + leftRemoteTracking_new = gAppState.TrackedController[0]; + rightRemoteTracking_new = gAppState.TrackedController[1]; + + + memset(&leftTrackedRemoteState_new, 0, sizeof leftTrackedRemoteState_new); + memset(&rightTrackedRemoteState_new, 0, sizeof rightTrackedRemoteState_new); + + //button mapping + if (GetActionStateBoolean(menuAction).currentState) leftTrackedRemoteState_new.Buttons |= xrButton_Enter; + if (GetActionStateBoolean(buttonXAction).currentState) leftTrackedRemoteState_new.Buttons |= xrButton_X; + if (GetActionStateBoolean(buttonYAction).currentState) leftTrackedRemoteState_new.Buttons |= xrButton_Y; + leftTrackedRemoteState_new.GripTrigger = GetActionStateFloat(gripLeftAction).currentState; + if (leftTrackedRemoteState_new.GripTrigger > 0.5f) leftTrackedRemoteState_new.Buttons |= xrButton_GripTrigger; + if (GetActionStateBoolean(thumbstickLeftClickAction).currentState) leftTrackedRemoteState_new.Buttons |= xrButton_LThumb; + + if (GetActionStateBoolean(buttonAAction).currentState) rightTrackedRemoteState_new.Buttons |= xrButton_A; + if (GetActionStateBoolean(buttonBAction).currentState) rightTrackedRemoteState_new.Buttons |= xrButton_B; + rightTrackedRemoteState_new.GripTrigger = GetActionStateFloat(gripRightAction).currentState; + if (rightTrackedRemoteState_new.GripTrigger > 0.5f) rightTrackedRemoteState_new.Buttons |= xrButton_GripTrigger; + if (GetActionStateBoolean(thumbstickRightClickAction).currentState) rightTrackedRemoteState_new.Buttons |= xrButton_RThumb; + + //index finger click + if (GetActionStateBoolean(indexLeftAction).currentState) leftTrackedRemoteState_new.Buttons |= xrButton_Trigger; + if (GetActionStateBoolean(indexRightAction).currentState) rightTrackedRemoteState_new.Buttons |= xrButton_Trigger; + + //thumbstick + XrActionStateVector2f moveJoystickState; + moveJoystickState = GetActionStateVector2(moveOnLeftJoystickAction); + leftTrackedRemoteState_new.Joystick.x = moveJoystickState.currentState.x; + leftTrackedRemoteState_new.Joystick.y = moveJoystickState.currentState.y; + + moveJoystickState = GetActionStateVector2(moveOnRightJoystickAction); + rightTrackedRemoteState_new.Joystick.x = moveJoystickState.currentState.x; + rightTrackedRemoteState_new.Joystick.y = moveJoystickState.currentState.y; +} + +//0 = left, 1 = right +float vibration_channel_duration[2] = {0.0f, 0.0f}; +float vibration_channel_intensity[2] = {0.0f, 0.0f}; + +void TBXR_Vibrate( int duration, int chan, float intensity ) +{ + for (int i = 0; i < 2; ++i) + { + if ((i + 1) & chan) + { + if (vibration_channel_duration[i] > 0.0f) + return; + + if (vibration_channel_duration[i] == -1.0f && duration != 0.0f) + return; + + vibration_channel_duration[i] = duration; + vibration_channel_intensity[i] = intensity; + } + } +} + +void TBXR_ProcessHaptics() { + static float lastFrameTime = 0.0f; + float timestamp = (float)(TBXR_GetTimeInMilliSeconds( )); + float frametime = timestamp - lastFrameTime; + lastFrameTime = timestamp; + + for (int i = 0; i < 2; ++i) { + if (vibration_channel_duration[i] > 0.0f || + vibration_channel_duration[i] == -1.0f) { + + // fire haptics using output action + XrHapticVibration vibration = {}; + vibration.type = XR_TYPE_HAPTIC_VIBRATION; + vibration.next = NULL; + vibration.amplitude = vibration_channel_intensity[i]; + vibration.duration = ToXrTime(vibration_channel_duration[i]); + vibration.frequency = 3000; + XrHapticActionInfo hapticActionInfo = {}; + hapticActionInfo.type = XR_TYPE_HAPTIC_ACTION_INFO; + hapticActionInfo.next = NULL; + hapticActionInfo.action = i == 0 ? vibrateLeftFeedback : vibrateRightFeedback; + OXR(xrApplyHapticFeedback(gAppState.Session, &hapticActionInfo, (const XrHapticBaseHeader*)&vibration)); + + if (vibration_channel_duration[i] != -1.0f) { + vibration_channel_duration[i] -= frametime; + + if (vibration_channel_duration[i] < 0.0f) { + vibration_channel_duration[i] = 0.0f; + vibration_channel_intensity[i] = 0.0f; + } + } + } else { + // Stop haptics + XrHapticActionInfo hapticActionInfo = {}; + hapticActionInfo.type = XR_TYPE_HAPTIC_ACTION_INFO; + hapticActionInfo.next = NULL; + hapticActionInfo.action = i == 0 ? vibrateLeftFeedback : vibrateRightFeedback; + OXR(xrStopHapticFeedback(gAppState.Session, &hapticActionInfo)); + } + } +} +#endif diff --git a/Projects/Android/jni/QzDoom/OpenXrInput_PicoXR.cpp b/Projects/Android/jni/QzDoom/OpenXrInput_PicoXR.cpp new file mode 100644 index 0000000..5c9a7ab --- /dev/null +++ b/Projects/Android/jni/QzDoom/OpenXrInput_PicoXR.cpp @@ -0,0 +1,639 @@ +#ifdef PICO_XR + +#include "VrInput.h" + +extern ovrApp gAppState; + +XrResult CheckXrResult(XrResult res, const char* originator) { + if (XR_FAILED(res)) { + //Com_Printf("error: %s", originator); + } + return res; +} + +#define CHECK_XRCMD(cmd) CheckXrResult(cmd, #cmd); + +#define SIDE_LEFT 0 +#define SIDE_RIGHT 1 +#define SIDE_COUNT 2 + + +XrActionSet actionSet; +XrAction grabAction; +XrAction poseAction; +XrAction vibrateAction; +XrAction quitAction; +/*************************pico******************/ +XrAction touchpadAction; +XrAction AXAction; +XrAction homeAction; +XrAction BYAction; +XrAction backAction; +XrAction sideAction; +XrAction triggerAction; +XrAction joystickAction; +XrAction batteryAction; +//---add new---------- +XrAction AXTouchAction; +XrAction BYTouchAction; +XrAction RockerTouchAction; +XrAction TriggerTouchAction; +XrAction ThumbrestTouchAction; +XrAction GripAction; +//---add new----------zgt +XrAction AAction; +XrAction BAction; +XrAction XAction; +XrAction YAction; +XrAction ATouchAction; +XrAction BTouchAction; +XrAction XTouchAction; +XrAction YTouchAction; +XrAction aimAction; +/*************************pico******************/ +XrSpace aimSpace[SIDE_COUNT]; +XrPath handSubactionPath[SIDE_COUNT]; +XrSpace handSpace[SIDE_COUNT]; + + + +XrActionSuggestedBinding ActionSuggestedBinding(XrAction action, XrPath path) { + XrActionSuggestedBinding asb; + asb.action = action; + asb.binding = path; + return asb; +} + +XrActionStateBoolean GetActionStateBoolean(XrAction action, int hand) { + XrActionStateGetInfo getInfo = {}; + getInfo.type = XR_TYPE_ACTION_STATE_GET_INFO; + getInfo.action = action; + if (hand >= 0) + getInfo.subactionPath = handSubactionPath[hand]; + + XrActionStateBoolean state = {}; + state.type = XR_TYPE_ACTION_STATE_BOOLEAN; + CHECK_XRCMD(xrGetActionStateBoolean(gAppState.Session, &getInfo, &state)); + return state; +} + +XrActionStateFloat GetActionStateFloat(XrAction action, int hand) { + XrActionStateGetInfo getInfo = {}; + getInfo.type = XR_TYPE_ACTION_STATE_GET_INFO; + getInfo.action = action; + if (hand >= 0) + getInfo.subactionPath = handSubactionPath[hand]; + + XrActionStateFloat state = {}; + state.type = XR_TYPE_ACTION_STATE_FLOAT; + CHECK_XRCMD(xrGetActionStateFloat(gAppState.Session, &getInfo, &state)); + return state; +} + +XrActionStateVector2f GetActionStateVector2(XrAction action, int hand) { + XrActionStateGetInfo getInfo = {}; + getInfo.type = XR_TYPE_ACTION_STATE_GET_INFO; + getInfo.action = action; + if (hand >= 0) + getInfo.subactionPath = handSubactionPath[hand]; + + XrActionStateVector2f state = {}; + state.type = XR_TYPE_ACTION_STATE_VECTOR2F; + CHECK_XRCMD(xrGetActionStateVector2f(gAppState.Session, &getInfo, &state)); + return state; +} + +void TBXR_InitActions( void ) +{ + // Create an action set. + { + XrActionSetCreateInfo actionSetInfo = {}; + actionSetInfo.type = XR_TYPE_ACTION_SET_CREATE_INFO; + strcpy(actionSetInfo.actionSetName, "gameplay"); + strcpy(actionSetInfo.localizedActionSetName, "Gameplay"); + actionSetInfo.priority = 0; + CHECK_XRCMD(xrCreateActionSet(gAppState.Instance, &actionSetInfo, &actionSet)); + } + + // Get the XrPath for the left and right hands - we will use them as subaction paths. + CHECK_XRCMD(xrStringToPath(gAppState.Instance, "/user/hand/left", &handSubactionPath[SIDE_LEFT])); + CHECK_XRCMD(xrStringToPath(gAppState.Instance, "/user/hand/right", &handSubactionPath[SIDE_RIGHT])); + + // Create actions. + { + // Create an input action for grabbing objects with the left and right hands. + XrActionCreateInfo actionInfo = {}; + actionInfo.type = XR_TYPE_ACTION_CREATE_INFO; + actionInfo.actionType = XR_ACTION_TYPE_FLOAT_INPUT; + strcpy(actionInfo.actionName, "grab_object"); + strcpy(actionInfo.localizedActionName, "Grab Object"); + actionInfo.countSubactionPaths = SIDE_COUNT; + actionInfo.subactionPaths = handSubactionPath; + CHECK_XRCMD(xrCreateAction(actionSet, &actionInfo, &grabAction)); + + // Create an input action getting the left and right hand poses. + actionInfo.actionType = XR_ACTION_TYPE_POSE_INPUT; + strcpy(actionInfo.actionName, "hand_pose"); + strcpy(actionInfo.localizedActionName, "Hand Pose"); + actionInfo.countSubactionPaths = SIDE_COUNT; + actionInfo.subactionPaths = handSubactionPath; + CHECK_XRCMD(xrCreateAction(actionSet, &actionInfo, &poseAction)); + + actionInfo.actionType = XR_ACTION_TYPE_POSE_INPUT; + strcpy(actionInfo.actionName, "aim_pose"); + strcpy(actionInfo.localizedActionName, "Aim Pose"); + actionInfo.countSubactionPaths = SIDE_COUNT; + actionInfo.subactionPaths = handSubactionPath; + CHECK_XRCMD(xrCreateAction(actionSet, &actionInfo, &aimAction)); + + // Create output actions for vibrating the left and right controller. + actionInfo.actionType = XR_ACTION_TYPE_VIBRATION_OUTPUT; + strcpy(actionInfo.actionName, "vibrate_hand"); + strcpy(actionInfo.localizedActionName, "Vibrate Hand"); + actionInfo.countSubactionPaths = SIDE_COUNT; + actionInfo.subactionPaths = handSubactionPath; + CHECK_XRCMD(xrCreateAction(actionSet, &actionInfo, &vibrateAction)); + + // Create input actions for quitting the session using the left and right controller. + // Since it doesn't matter which hand did this, we do not specify subaction paths for it. + // We will just suggest bindings for both hands, where possible. + actionInfo.actionType = XR_ACTION_TYPE_BOOLEAN_INPUT; + strcpy(actionInfo.actionName, "quit_session"); + strcpy(actionInfo.localizedActionName, "Quit Session"); + actionInfo.countSubactionPaths = SIDE_COUNT; + actionInfo.subactionPaths = handSubactionPath; + CHECK_XRCMD(xrCreateAction(actionSet, &actionInfo, &quitAction)); + /**********************************pico***************************************/ + // Create input actions for toucpad key using the left and right controller. + actionInfo.actionType = XR_ACTION_TYPE_BOOLEAN_INPUT; + strcpy(actionInfo.actionName, "touchpad"); + strcpy(actionInfo.localizedActionName, "Touchpad"); + actionInfo.countSubactionPaths = SIDE_COUNT; + actionInfo.subactionPaths = handSubactionPath; + CHECK_XRCMD(xrCreateAction(actionSet, &actionInfo, &touchpadAction)); + + actionInfo.actionType = XR_ACTION_TYPE_BOOLEAN_INPUT; + strcpy(actionInfo.actionName, "axkey"); + strcpy(actionInfo.localizedActionName, "AXkey"); + actionInfo.countSubactionPaths = SIDE_COUNT; + actionInfo.subactionPaths = handSubactionPath; + CHECK_XRCMD(xrCreateAction(actionSet, &actionInfo, &AXAction)); + + + actionInfo.actionType = XR_ACTION_TYPE_BOOLEAN_INPUT; + strcpy(actionInfo.actionName, "homekey"); + strcpy(actionInfo.localizedActionName, "Homekey"); + actionInfo.countSubactionPaths = SIDE_COUNT; + actionInfo.subactionPaths = handSubactionPath; + CHECK_XRCMD(xrCreateAction(actionSet, &actionInfo, &homeAction)); + + actionInfo.actionType = XR_ACTION_TYPE_BOOLEAN_INPUT; + strcpy(actionInfo.actionName, "bykey"); + strcpy(actionInfo.localizedActionName, "BYkey"); + actionInfo.countSubactionPaths = SIDE_COUNT; + actionInfo.subactionPaths = handSubactionPath; + CHECK_XRCMD(xrCreateAction(actionSet, &actionInfo, &BYAction)); + + actionInfo.actionType = XR_ACTION_TYPE_BOOLEAN_INPUT; + strcpy(actionInfo.actionName, "backkey"); + strcpy(actionInfo.localizedActionName, "Backkey"); + actionInfo.countSubactionPaths = SIDE_COUNT; + actionInfo.subactionPaths = handSubactionPath; + CHECK_XRCMD(xrCreateAction(actionSet, &actionInfo, &backAction)); + + actionInfo.actionType = XR_ACTION_TYPE_BOOLEAN_INPUT; + strcpy(actionInfo.actionName, "sidekey"); + strcpy(actionInfo.localizedActionName, "Sidekey"); + actionInfo.countSubactionPaths = SIDE_COUNT; + actionInfo.subactionPaths = handSubactionPath; + CHECK_XRCMD(xrCreateAction(actionSet, &actionInfo, &sideAction)); + + actionInfo.actionType = XR_ACTION_TYPE_FLOAT_INPUT; + strcpy(actionInfo.actionName, "trigger"); + strcpy(actionInfo.localizedActionName, "Trigger"); + actionInfo.countSubactionPaths = SIDE_COUNT; + actionInfo.subactionPaths = handSubactionPath; + CHECK_XRCMD(xrCreateAction(actionSet, &actionInfo, &triggerAction)); + + actionInfo.actionType = XR_ACTION_TYPE_VECTOR2F_INPUT; + strcpy(actionInfo.actionName, "joystick"); + strcpy(actionInfo.localizedActionName, "Joystick"); + actionInfo.countSubactionPaths = SIDE_COUNT; + actionInfo.subactionPaths = handSubactionPath; + CHECK_XRCMD(xrCreateAction(actionSet, &actionInfo, &joystickAction)); + + actionInfo.actionType = XR_ACTION_TYPE_FLOAT_INPUT; + strcpy(actionInfo.actionName, "battery"); + strcpy(actionInfo.localizedActionName, "battery"); + actionInfo.countSubactionPaths = SIDE_COUNT; + actionInfo.subactionPaths = handSubactionPath; + CHECK_XRCMD(xrCreateAction(actionSet, &actionInfo, &batteryAction)); + //------------------------add new--------------------------------- + actionInfo.actionType = XR_ACTION_TYPE_BOOLEAN_INPUT; + strcpy(actionInfo.actionName, "axtouch"); + strcpy(actionInfo.localizedActionName, "AXtouch"); + actionInfo.countSubactionPaths = SIDE_COUNT; + actionInfo.subactionPaths = handSubactionPath; + CHECK_XRCMD(xrCreateAction(actionSet, &actionInfo, &AXTouchAction)); + + actionInfo.actionType = XR_ACTION_TYPE_BOOLEAN_INPUT; + strcpy(actionInfo.actionName, "bytouch"); + strcpy(actionInfo.localizedActionName, "BYtouch"); + actionInfo.countSubactionPaths = SIDE_COUNT; + actionInfo.subactionPaths = handSubactionPath; + CHECK_XRCMD(xrCreateAction(actionSet, &actionInfo, &BYTouchAction)); + + actionInfo.actionType = XR_ACTION_TYPE_BOOLEAN_INPUT; + strcpy(actionInfo.actionName, "rockertouch"); + strcpy(actionInfo.localizedActionName, "Rockertouch"); + actionInfo.countSubactionPaths = SIDE_COUNT; + actionInfo.subactionPaths = handSubactionPath; + CHECK_XRCMD(xrCreateAction(actionSet, &actionInfo, &RockerTouchAction)); + + actionInfo.actionType = XR_ACTION_TYPE_BOOLEAN_INPUT; + strcpy(actionInfo.actionName, "triggertouch"); + strcpy(actionInfo.localizedActionName, "Triggertouch"); + actionInfo.countSubactionPaths = SIDE_COUNT; + actionInfo.subactionPaths = handSubactionPath; + CHECK_XRCMD(xrCreateAction(actionSet, &actionInfo, &TriggerTouchAction)); + + actionInfo.actionType = XR_ACTION_TYPE_BOOLEAN_INPUT; + strcpy(actionInfo.actionName, "thumbresttouch"); + strcpy(actionInfo.localizedActionName, "Thumbresttouch"); + actionInfo.countSubactionPaths = SIDE_COUNT; + actionInfo.subactionPaths = handSubactionPath; + CHECK_XRCMD(xrCreateAction(actionSet, &actionInfo, &ThumbrestTouchAction)); + + actionInfo.actionType = XR_ACTION_TYPE_FLOAT_INPUT; + strcpy(actionInfo.actionName, "gripvalue"); + strcpy(actionInfo.localizedActionName, "GripValue"); + actionInfo.countSubactionPaths = SIDE_COUNT; + actionInfo.subactionPaths = handSubactionPath; + CHECK_XRCMD(xrCreateAction(actionSet, &actionInfo, &GripAction)); + + //--------------add new----------zgt + actionInfo.actionType = XR_ACTION_TYPE_BOOLEAN_INPUT; + strcpy(actionInfo.actionName, "akey"); + strcpy(actionInfo.localizedActionName, "Akey"); + actionInfo.countSubactionPaths = SIDE_COUNT; + actionInfo.subactionPaths = handSubactionPath; + CHECK_XRCMD(xrCreateAction(actionSet, &actionInfo, &AAction)); + + actionInfo.actionType = XR_ACTION_TYPE_BOOLEAN_INPUT; + strcpy(actionInfo.actionName, "bkey"); + strcpy(actionInfo.localizedActionName, "Bkey"); + actionInfo.countSubactionPaths = SIDE_COUNT; + actionInfo.subactionPaths = handSubactionPath; + CHECK_XRCMD(xrCreateAction(actionSet, &actionInfo, &BAction)); + + actionInfo.actionType = XR_ACTION_TYPE_BOOLEAN_INPUT; + strcpy(actionInfo.actionName, "xkey"); + strcpy(actionInfo.localizedActionName, "Xkey"); + actionInfo.countSubactionPaths = SIDE_COUNT; + actionInfo.subactionPaths = handSubactionPath; + CHECK_XRCMD(xrCreateAction(actionSet, &actionInfo, &XAction)); + + actionInfo.actionType = XR_ACTION_TYPE_BOOLEAN_INPUT; + strcpy(actionInfo.actionName, "ykey"); + strcpy(actionInfo.localizedActionName, "Ykey"); + actionInfo.countSubactionPaths = SIDE_COUNT; + actionInfo.subactionPaths = handSubactionPath; + CHECK_XRCMD(xrCreateAction(actionSet, &actionInfo, &YAction)); + + actionInfo.actionType = XR_ACTION_TYPE_BOOLEAN_INPUT; + strcpy(actionInfo.actionName, "atouch"); + strcpy(actionInfo.localizedActionName, "Atouch"); + actionInfo.countSubactionPaths = SIDE_COUNT; + actionInfo.subactionPaths = handSubactionPath; + CHECK_XRCMD(xrCreateAction(actionSet, &actionInfo, &ATouchAction)); + + actionInfo.actionType = XR_ACTION_TYPE_BOOLEAN_INPUT; + strcpy(actionInfo.actionName, "btouch"); + strcpy(actionInfo.localizedActionName, "Btouch"); + actionInfo.countSubactionPaths = SIDE_COUNT; + actionInfo.subactionPaths = handSubactionPath; + CHECK_XRCMD(xrCreateAction(actionSet, &actionInfo, &BTouchAction)); + + actionInfo.actionType = XR_ACTION_TYPE_BOOLEAN_INPUT; + strcpy(actionInfo.actionName, "xtouch"); + strcpy(actionInfo.localizedActionName, "Xtouch"); + actionInfo.countSubactionPaths = SIDE_COUNT; + actionInfo.subactionPaths = handSubactionPath; + CHECK_XRCMD(xrCreateAction(actionSet, &actionInfo, &XTouchAction)); + + actionInfo.actionType = XR_ACTION_TYPE_BOOLEAN_INPUT; + strcpy(actionInfo.actionName, "ytouch"); + strcpy(actionInfo.localizedActionName, "Ytouch"); + actionInfo.countSubactionPaths = SIDE_COUNT; + actionInfo.subactionPaths = handSubactionPath; + CHECK_XRCMD(xrCreateAction(actionSet, &actionInfo, &YTouchAction)); + /**********************************pico***************************************/ + + + } + + XrPath selectPath[SIDE_COUNT]; + XrPath squeezeValuePath[SIDE_COUNT]; + XrPath squeezeClickPath[SIDE_COUNT]; + XrPath posePath[SIDE_COUNT]; + XrPath hapticPath[SIDE_COUNT]; + XrPath menuClickPath[SIDE_COUNT]; + XrPath systemPath[SIDE_COUNT]; + XrPath thumbrestPath[SIDE_COUNT]; + XrPath triggerTouchPath[SIDE_COUNT]; + XrPath triggerValuePath[SIDE_COUNT]; + XrPath thumbstickClickPath[SIDE_COUNT]; + XrPath thumbstickTouchPath[SIDE_COUNT]; + XrPath thumbstickPosPath[SIDE_COUNT]; + XrPath aimPath[SIDE_COUNT]; + + /**************************pico************************************/ + XrPath touchpadPath[SIDE_COUNT]; + XrPath AXValuePath[SIDE_COUNT]; + XrPath homeClickPath[SIDE_COUNT]; + XrPath BYValuePath[SIDE_COUNT]; + XrPath backPath[SIDE_COUNT]; + XrPath sideClickPath[SIDE_COUNT]; + XrPath triggerPath[SIDE_COUNT]; + XrPath joystickPath[SIDE_COUNT]; + XrPath batteryPath[SIDE_COUNT]; + //--------------add new---------- + XrPath GripPath[SIDE_COUNT]; + XrPath AXTouchPath[SIDE_COUNT]; + XrPath BYTouchPath[SIDE_COUNT]; + XrPath RockerTouchPath[SIDE_COUNT]; + XrPath TriggerTouchPath[SIDE_COUNT]; + XrPath ThumbresetTouchPath[SIDE_COUNT]; + //--------------add new----------zgt + XrPath AValuePath[SIDE_COUNT]; + XrPath BValuePath[SIDE_COUNT]; + XrPath XValuePath[SIDE_COUNT]; + XrPath YValuePath[SIDE_COUNT]; + XrPath ATouchPath[SIDE_COUNT]; + XrPath BTouchPath[SIDE_COUNT]; + XrPath XTouchPath[SIDE_COUNT]; + XrPath YTouchPath[SIDE_COUNT]; + /**************************pico************************************/ + CHECK_XRCMD(xrStringToPath(gAppState.Instance, "/user/hand/left/input/select/click", &selectPath[SIDE_LEFT])); + CHECK_XRCMD(xrStringToPath(gAppState.Instance, "/user/hand/right/input/select/click", &selectPath[SIDE_RIGHT])); + CHECK_XRCMD(xrStringToPath(gAppState.Instance, "/user/hand/left/input/menu/click", &menuClickPath[SIDE_LEFT])); + CHECK_XRCMD(xrStringToPath(gAppState.Instance, "/user/hand/right/input/menu/click", &menuClickPath[SIDE_RIGHT])); + + CHECK_XRCMD(xrStringToPath(gAppState.Instance, "/user/hand/left/input/squeeze/value", &squeezeValuePath[SIDE_LEFT])); + CHECK_XRCMD(xrStringToPath(gAppState.Instance, "/user/hand/right/input/squeeze/value", &squeezeValuePath[SIDE_RIGHT])); + CHECK_XRCMD(xrStringToPath(gAppState.Instance, "/user/hand/left/input/squeeze/click", &squeezeClickPath[SIDE_LEFT])); + CHECK_XRCMD(xrStringToPath(gAppState.Instance, "/user/hand/right/input/squeeze/click", &squeezeClickPath[SIDE_RIGHT])); + CHECK_XRCMD(xrStringToPath(gAppState.Instance, "/user/hand/left/input/grip/pose", &posePath[SIDE_LEFT])); + CHECK_XRCMD(xrStringToPath(gAppState.Instance, "/user/hand/right/input/grip/pose", &posePath[SIDE_RIGHT])); + CHECK_XRCMD(xrStringToPath(gAppState.Instance, "/user/hand/left/input/aim/pose", &aimPath[SIDE_LEFT])); + CHECK_XRCMD(xrStringToPath(gAppState.Instance, "/user/hand/right/input/aim/pose", &aimPath[SIDE_RIGHT])); + + CHECK_XRCMD(xrStringToPath(gAppState.Instance, "/user/hand/left/output/haptic", &hapticPath[SIDE_LEFT])); + CHECK_XRCMD(xrStringToPath(gAppState.Instance, "/user/hand/right/output/haptic", &hapticPath[SIDE_RIGHT])); + + CHECK_XRCMD(xrStringToPath(gAppState.Instance, "/user/hand/left/input/trigger/touch", &triggerTouchPath[SIDE_LEFT])); + CHECK_XRCMD(xrStringToPath(gAppState.Instance, "/user/hand/right/input/trigger/touch", &triggerTouchPath[SIDE_RIGHT])); + CHECK_XRCMD(xrStringToPath(gAppState.Instance, "/user/hand/left/input/trigger/value", &triggerValuePath[SIDE_LEFT])); + CHECK_XRCMD(xrStringToPath(gAppState.Instance, "/user/hand/right/input/trigger/value", &triggerValuePath[SIDE_RIGHT])); + + CHECK_XRCMD(xrStringToPath(gAppState.Instance, "/user/hand/left/input/thumbstick/click", &thumbstickClickPath[SIDE_LEFT])); + CHECK_XRCMD(xrStringToPath(gAppState.Instance, "/user/hand/right/input/thumbstick/click", &thumbstickClickPath[SIDE_RIGHT])); + CHECK_XRCMD(xrStringToPath(gAppState.Instance, "/user/hand/left/input/thumbstick/touch", &thumbstickTouchPath[SIDE_LEFT])); + CHECK_XRCMD(xrStringToPath(gAppState.Instance, "/user/hand/right/input/thumbstick/touch", &thumbstickTouchPath[SIDE_RIGHT])); + CHECK_XRCMD(xrStringToPath(gAppState.Instance, "/user/hand/left/input/thumbstick", &thumbstickPosPath[SIDE_LEFT])); + CHECK_XRCMD(xrStringToPath(gAppState.Instance, "/user/hand/right/input/thumbstick", &thumbstickPosPath[SIDE_RIGHT])); + + CHECK_XRCMD(xrStringToPath(gAppState.Instance, "/user/hand/left/input/system/click", &systemPath[SIDE_LEFT])); + CHECK_XRCMD(xrStringToPath(gAppState.Instance, "/user/hand/right/input/system/click", &systemPath[SIDE_RIGHT])); + CHECK_XRCMD(xrStringToPath(gAppState.Instance, "/user/hand/left/input/thumbrest/touch", &thumbrestPath[SIDE_LEFT])); + CHECK_XRCMD(xrStringToPath(gAppState.Instance, "/user/hand/right/input/thumbrest/touch", &thumbrestPath[SIDE_RIGHT])); + + /**************************pico************************************/ + CHECK_XRCMD(xrStringToPath(gAppState.Instance, "/user/hand/left/input/back/click", &backPath[SIDE_LEFT])); + CHECK_XRCMD(xrStringToPath(gAppState.Instance, "/user/hand/right/input/back/click", &backPath[SIDE_RIGHT])); + CHECK_XRCMD(xrStringToPath(gAppState.Instance, "/user/hand/left/input/battery/value", &batteryPath[SIDE_LEFT])); + CHECK_XRCMD(xrStringToPath(gAppState.Instance, "/user/hand/right/input/battery/value", &batteryPath[SIDE_RIGHT])); + + CHECK_XRCMD(xrStringToPath(gAppState.Instance, "/user/hand/left/input/x/click", &XValuePath[SIDE_LEFT])); + CHECK_XRCMD(xrStringToPath(gAppState.Instance, "/user/hand/left/input/y/click", &YValuePath[SIDE_LEFT])); + CHECK_XRCMD(xrStringToPath(gAppState.Instance, "/user/hand/right/input/a/click", &AValuePath[SIDE_RIGHT])); + CHECK_XRCMD(xrStringToPath(gAppState.Instance, "/user/hand/right/input/b/click", &BValuePath[SIDE_RIGHT])); + CHECK_XRCMD(xrStringToPath(gAppState.Instance, "/user/hand/left/input/x/touch", &XTouchPath[SIDE_LEFT])); + CHECK_XRCMD(xrStringToPath(gAppState.Instance, "/user/hand/left/input/y/touch", &YTouchPath[SIDE_LEFT])); + CHECK_XRCMD(xrStringToPath(gAppState.Instance, "/user/hand/right/input/a/touch", &ATouchPath[SIDE_RIGHT])); + CHECK_XRCMD(xrStringToPath(gAppState.Instance, "/user/hand/right/input/b/touch", &BTouchPath[SIDE_RIGHT])); + /**************************pico************************************/ + XrActionSuggestedBinding bindings[128]; + int currBinding = 0; + + // Suggest bindings for the Pico Neo 3 controller + { + XrPath picoMixedRealityInteractionProfilePath; + CHECK_XRCMD(xrStringToPath(gAppState.Instance, "/interaction_profiles/pico/neo3_controller", + &picoMixedRealityInteractionProfilePath)); + + bindings[currBinding++] = ActionSuggestedBinding(touchpadAction, thumbstickClickPath[SIDE_LEFT]); + bindings[currBinding++] = ActionSuggestedBinding(touchpadAction, thumbstickClickPath[SIDE_RIGHT]); + bindings[currBinding++] = ActionSuggestedBinding(joystickAction, thumbstickPosPath[SIDE_LEFT]); + bindings[currBinding++] = ActionSuggestedBinding(joystickAction, thumbstickPosPath[SIDE_RIGHT]); + bindings[currBinding++] = ActionSuggestedBinding(RockerTouchAction, thumbstickTouchPath[SIDE_LEFT]); + bindings[currBinding++] = ActionSuggestedBinding(RockerTouchAction, thumbstickTouchPath[SIDE_RIGHT]); + + bindings[currBinding++] = ActionSuggestedBinding(triggerAction, triggerValuePath[SIDE_LEFT]); + bindings[currBinding++] = ActionSuggestedBinding(triggerAction, triggerValuePath[SIDE_RIGHT]); + bindings[currBinding++] = ActionSuggestedBinding(TriggerTouchAction, triggerTouchPath[SIDE_LEFT]); + bindings[currBinding++] = ActionSuggestedBinding(TriggerTouchAction, triggerTouchPath[SIDE_RIGHT]); + + bindings[currBinding++] = ActionSuggestedBinding(sideAction, squeezeClickPath[SIDE_LEFT]); + bindings[currBinding++] = ActionSuggestedBinding(sideAction, squeezeClickPath[SIDE_RIGHT]); + bindings[currBinding++] = ActionSuggestedBinding(GripAction, squeezeValuePath[SIDE_LEFT]); + bindings[currBinding++] = ActionSuggestedBinding(GripAction, squeezeValuePath[SIDE_RIGHT]); + bindings[currBinding++] = ActionSuggestedBinding(poseAction, posePath[SIDE_LEFT]); + bindings[currBinding++] = ActionSuggestedBinding(poseAction, posePath[SIDE_RIGHT]); + + bindings[currBinding++] = ActionSuggestedBinding(homeAction, systemPath[SIDE_LEFT]); + bindings[currBinding++] = ActionSuggestedBinding(homeAction, systemPath[SIDE_RIGHT]); + bindings[currBinding++] = ActionSuggestedBinding(backAction, backPath[SIDE_LEFT]); + bindings[currBinding++] = ActionSuggestedBinding(backAction, backPath[SIDE_RIGHT]); + bindings[currBinding++] = ActionSuggestedBinding(batteryAction, batteryPath[SIDE_LEFT]); + bindings[currBinding++] = ActionSuggestedBinding(batteryAction, batteryPath[SIDE_RIGHT]); + + bindings[currBinding++] = ActionSuggestedBinding(ThumbrestTouchAction, thumbrestPath[SIDE_LEFT]); + bindings[currBinding++] = ActionSuggestedBinding(ThumbrestTouchAction, thumbrestPath[SIDE_RIGHT]); + bindings[currBinding++] = ActionSuggestedBinding(vibrateAction, hapticPath[SIDE_LEFT]); + bindings[currBinding++] = ActionSuggestedBinding(vibrateAction, hapticPath[SIDE_RIGHT]); + + bindings[currBinding++] = ActionSuggestedBinding(XTouchAction, XTouchPath[SIDE_LEFT]); + bindings[currBinding++] = ActionSuggestedBinding(YTouchAction, YTouchPath[SIDE_LEFT]); + bindings[currBinding++] = ActionSuggestedBinding(ATouchAction, ATouchPath[SIDE_RIGHT]); + bindings[currBinding++] = ActionSuggestedBinding(BTouchAction, BTouchPath[SIDE_RIGHT]); + bindings[currBinding++] = ActionSuggestedBinding(XAction, XValuePath[SIDE_LEFT]); + bindings[currBinding++] = ActionSuggestedBinding(YAction, YValuePath[SIDE_LEFT]); + bindings[currBinding++] = ActionSuggestedBinding(AAction, AValuePath[SIDE_RIGHT]); + bindings[currBinding++] = ActionSuggestedBinding(BAction, BValuePath[SIDE_RIGHT]); + bindings[currBinding++] = ActionSuggestedBinding(aimAction, aimPath[SIDE_LEFT]); + bindings[currBinding++] = ActionSuggestedBinding(aimAction, aimPath[SIDE_RIGHT]); + + XrInteractionProfileSuggestedBinding suggestedBindings = {}; + suggestedBindings.type = XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING; + suggestedBindings.interactionProfile = picoMixedRealityInteractionProfilePath; + suggestedBindings.suggestedBindings = bindings; + suggestedBindings.countSuggestedBindings = currBinding; + CHECK_XRCMD(xrSuggestInteractionProfileBindings(gAppState.Instance, &suggestedBindings)); + } + + XrActionSpaceCreateInfo actionSpaceInfo = {}; + actionSpaceInfo.type = XR_TYPE_ACTION_SPACE_CREATE_INFO; + actionSpaceInfo.action = poseAction; + actionSpaceInfo.poseInActionSpace.orientation.w = 1.f; + actionSpaceInfo.subactionPath = handSubactionPath[SIDE_LEFT]; + CHECK_XRCMD(xrCreateActionSpace(gAppState.Session, &actionSpaceInfo, &handSpace[SIDE_LEFT])); + actionSpaceInfo.subactionPath = handSubactionPath[SIDE_RIGHT]; + CHECK_XRCMD(xrCreateActionSpace(gAppState.Session, &actionSpaceInfo, &handSpace[SIDE_RIGHT])); + actionSpaceInfo.action = aimAction; + actionSpaceInfo.poseInActionSpace.orientation.w = 1.f; + actionSpaceInfo.subactionPath = handSubactionPath[SIDE_LEFT]; + CHECK_XRCMD(xrCreateActionSpace(gAppState.Session, &actionSpaceInfo, &aimSpace[SIDE_LEFT])); + actionSpaceInfo.subactionPath = handSubactionPath[SIDE_RIGHT]; + CHECK_XRCMD(xrCreateActionSpace(gAppState.Session, &actionSpaceInfo, &aimSpace[SIDE_RIGHT])); + + XrSessionActionSetsAttachInfo attachInfo = {}; + attachInfo.type = XR_TYPE_SESSION_ACTION_SETS_ATTACH_INFO; + attachInfo.countActionSets = 1; + attachInfo.actionSets = &actionSet; + CHECK_XRCMD(xrAttachSessionActionSets(gAppState.Session, &attachInfo)); +} + +void TBXR_SyncActions( void ) +{ + XrActiveActionSet activeActionSet = {}; + activeActionSet.actionSet = actionSet; + activeActionSet.subactionPath = XR_NULL_PATH; + XrActionsSyncInfo syncInfo; + syncInfo.type = XR_TYPE_ACTIONS_SYNC_INFO; + syncInfo.countActiveActionSets = 1; + syncInfo.activeActionSets = &activeActionSet; + CHECK_XRCMD(xrSyncActions(gAppState.Session, &syncInfo)); +} + +void TBXR_UpdateControllers( ) +{ + TBXR_SyncActions(); + + //get controller poses + for (int i = 0; i < 2; i++) { + XrSpaceVelocity vel = {}; + vel.type = XR_TYPE_SPACE_VELOCITY; + XrSpaceLocation loc = {}; + loc.type = XR_TYPE_SPACE_LOCATION; + loc.next = &vel; + XrResult res = xrLocateSpace(aimSpace[i], gAppState.CurrentSpace, gAppState.PredictedDisplayTime, &loc); + if (res != XR_SUCCESS) { + //Com_Printf("xrLocateSpace error: %d", (int)res); + } + + gAppState.TrackedController[i].Active = (loc.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) != 0; + gAppState.TrackedController[i].Pose = loc.pose; + gAppState.TrackedController[i].Velocity = vel; + } + + leftRemoteTracking_new = gAppState.TrackedController[0]; + rightRemoteTracking_new = gAppState.TrackedController[1]; + + + + //button mapping + leftTrackedRemoteState_new.Buttons = 0; + if (GetActionStateBoolean(backAction, SIDE_LEFT).currentState) leftTrackedRemoteState_new.Buttons |= xrButton_Enter; + if (GetActionStateBoolean(XAction, SIDE_LEFT).currentState) leftTrackedRemoteState_new.Buttons |= xrButton_X; + if (GetActionStateBoolean(YAction, SIDE_LEFT).currentState) leftTrackedRemoteState_new.Buttons |= xrButton_Y; + if (GetActionStateBoolean(sideAction, SIDE_LEFT).currentState) leftTrackedRemoteState_new.Buttons |= xrButton_GripTrigger; + if (GetActionStateBoolean(touchpadAction, SIDE_LEFT).currentState) leftTrackedRemoteState_new.Buttons |= xrButton_LThumb; + if (GetActionStateFloat(triggerAction, SIDE_LEFT).currentState > 0.5f) leftTrackedRemoteState_new.Buttons |= xrButton_Trigger; + + rightTrackedRemoteState_new.Buttons = 0; + if (GetActionStateBoolean(backAction, SIDE_RIGHT).currentState) rightTrackedRemoteState_new.Buttons |= xrButton_Enter; + if (GetActionStateBoolean(AAction, SIDE_RIGHT).currentState) rightTrackedRemoteState_new.Buttons |= xrButton_A; + if (GetActionStateBoolean(BAction, SIDE_RIGHT).currentState) rightTrackedRemoteState_new.Buttons |= xrButton_B; + if (GetActionStateBoolean(sideAction, SIDE_RIGHT).currentState) rightTrackedRemoteState_new.Buttons |= xrButton_GripTrigger; + if (GetActionStateBoolean(touchpadAction, SIDE_RIGHT).currentState) rightTrackedRemoteState_new.Buttons |= xrButton_RThumb; + if (GetActionStateFloat(triggerAction, SIDE_RIGHT).currentState > 0.5f) rightTrackedRemoteState_new.Buttons |= xrButton_Trigger; + + //thumbstick + XrActionStateVector2f moveJoystickState; + moveJoystickState = GetActionStateVector2(joystickAction, SIDE_LEFT); + leftTrackedRemoteState_new.Joystick.x = moveJoystickState.currentState.x; + leftTrackedRemoteState_new.Joystick.y = moveJoystickState.currentState.y; + + moveJoystickState = GetActionStateVector2(joystickAction, SIDE_RIGHT); + rightTrackedRemoteState_new.Joystick.x = moveJoystickState.currentState.x; + rightTrackedRemoteState_new.Joystick.y = moveJoystickState.currentState.y; +} + + +//0 = left, 1 = right +float vibration_channel_duration[2] = {0.0f, 0.0f}; +float vibration_channel_intensity[2] = {0.0f, 0.0f}; + +void TBXR_Vibrate( int duration, int chan, float intensity ) +{ + for (int i = 0; i < 2; ++i) + { + if ((i + 1) & chan) + { + if (vibration_channel_duration[i] > 0.0f) + return; + + if (vibration_channel_duration[i] == -1.0f && duration != 0.0f) + return; + + vibration_channel_duration[i] = duration; + vibration_channel_intensity[i] = intensity; + } + } +} + +void TBXR_ProcessHaptics() { + static float lastFrameTime = 0.0f; + float timestamp = (float)(TBXR_GetTimeInMilliSeconds( )); + float frametime = timestamp - lastFrameTime; + lastFrameTime = timestamp; + + for (int i = 0; i < 2; ++i) { + if (vibration_channel_duration[i] > 0.0f || + vibration_channel_duration[i] == -1.0f) { + + // fire haptics using output action + XrHapticVibration vibration = {}; + vibration.type = XR_TYPE_HAPTIC_VIBRATION; + vibration.next = NULL; + vibration.amplitude = vibration_channel_intensity[i]; + vibration.duration = ToXrTime(vibration_channel_duration[i]); + vibration.frequency = 3000; + XrHapticActionInfo hapticActionInfo = {}; + hapticActionInfo.type = XR_TYPE_HAPTIC_ACTION_INFO; + hapticActionInfo.next = NULL; + hapticActionInfo.action = vibrateAction; + hapticActionInfo.subactionPath = handSubactionPath[i]; + OXR(xrApplyHapticFeedback(gAppState.Session, &hapticActionInfo, (const XrHapticBaseHeader*)&vibration)); + + if (vibration_channel_duration[i] != -1.0f) { + vibration_channel_duration[i] -= frametime; + + if (vibration_channel_duration[i] < 0.0f) { + vibration_channel_duration[i] = 0.0f; + vibration_channel_intensity[i] = 0.0f; + } + } + } else { + // Stop haptics + XrHapticActionInfo hapticActionInfo = {}; + hapticActionInfo.type = XR_TYPE_HAPTIC_ACTION_INFO; + hapticActionInfo.next = NULL; + hapticActionInfo.action = vibrateAction; + hapticActionInfo.subactionPath = handSubactionPath[i]; + OXR(xrStopHapticFeedback(gAppState.Session, &hapticActionInfo)); + } + } +} +#endif //PICO_XR diff --git a/Projects/Android/jni/QzDoom/QzDoom_OpenXR.cpp b/Projects/Android/jni/QzDoom/QzDoom_OpenXR.cpp new file mode 100644 index 0000000..d41d153 --- /dev/null +++ b/Projects/Android/jni/QzDoom/QzDoom_OpenXR.cpp @@ -0,0 +1,724 @@ +#include +#include +#include +#include +#include +#include +#include +#include // for prctl( PR_SET_NAME ) +#include +#include // for native window JNI +#include + +#include "argtable3.h" +#include "VrInput.h" + + +//#define ENABLE_GL_DEBUG +#define ENABLE_GL_DEBUG_VERBOSE 1 + +//Let's go to the maximum! +extern int NUM_MULTI_SAMPLES; +extern int REFRESH ; +extern float SS_MULTIPLIER ; + + +/* global arg_xxx structs */ +struct arg_dbl *ss; +struct arg_int *cpu; +struct arg_int *gpu; +struct arg_int *msaa; +struct arg_int *refresh; +struct arg_end *end; + +char **argv; +int argc=0; + + +//Define all variables here that were externs in the VrCommon.h +bool qzdoom_initialised; +long long global_time; +float playerYaw; +bool resetDoomYaw; +bool resetPreviousPitch; +float doomYaw; +float previousPitch; +float vrFOV; +vec3_t worldPosition; +vec3_t hmdPosition; +vec3_t hmdorientation; +vec3_t positionDeltaThisFrame; +vec3_t weaponangles; +vec3_t weaponoffset; +bool weaponStabilised; + +vec3_t offhandangles; +vec3_t offhandoffset; +bool player_moving; +bool shutdown; +bool ready_teleport; +bool trigger_teleport; +bool cinemamode; + +//This is now controlled by the engine +static bool useVirtualScreen = true; + +static bool hasIWADs = false; +static bool hasLauncher = false; + +/* +================================================================================ + +QuestZDoom Stuff + +================================================================================ +*/ + +void QzDoom_setUseScreenLayer(bool use) +{ + useVirtualScreen = use; +} + +int QzDoom_SetRefreshRate(int refreshRate) +{ +#ifdef META_QUEST + OXR(gAppState.pfnRequestDisplayRefreshRate(gAppState.Session, (float)refreshRate)); + return refreshRate; +#endif + + return 0; +} + +void QzDoom_GetScreenRes(uint32_t *width, uint32_t *height) +{ + int iWidth, iHeight; + TBXR_GetScreenRes(&iWidth, &iHeight); + *width = iWidth; + *height = iHeight; +} + +bool VR_UseScreenLayer() +{ + return useVirtualScreen || cinemamode; +} + +float VR_GetScreenLayerDistance() +{ + return 4.0f; +} + +static void UnEscapeQuotes( char *arg ) +{ + char *last = NULL; + while( *arg ) { + if( *arg == '"' && *last == '\\' ) { + char *c_curr = arg; + char *c_last = last; + while( *c_curr ) { + *c_last = *c_curr; + c_last = c_curr; + c_curr++; + } + *c_last = '\0'; + } + last = arg; + arg++; + } +} + +static int ParseCommandLine(char *cmdline, char **argv) +{ + char *bufp; + char *lastp = NULL; + int argc, last_argc; + argc = last_argc = 0; + for ( bufp = cmdline; *bufp; ) { + while ( isspace(*bufp) ) { + ++bufp; + } + if ( *bufp == '"' ) { + ++bufp; + if ( *bufp ) { + if ( argv ) { + argv[argc] = bufp; + } + ++argc; + } + while ( *bufp && ( *bufp != '"' || *lastp == '\\' ) ) { + lastp = bufp; + ++bufp; + } + } else { + if ( *bufp ) { + if ( argv ) { + argv[argc] = bufp; + } + ++argc; + } + while ( *bufp && ! isspace(*bufp) ) { + ++bufp; + } + } + if ( *bufp ) { + if ( argv ) { + *bufp = '\0'; + } + ++bufp; + } + if( argv && last_argc != argc ) { + UnEscapeQuotes( argv[last_argc] ); + } + last_argc = argc; + } + if ( argv ) { + argv[argc] = NULL; + } + return(argc); +} + + +void VR_SetHMDOrientation(float pitch, float yaw, float roll) +{ + VectorSet(hmdorientation, pitch, yaw, roll); + + if (!VR_UseScreenLayer()) + { + playerYaw = yaw; + } +} + +void VR_SetHMDPosition(float x, float y, float z ) +{ + VectorSet(hmdPosition, x, y, z); + + positionDeltaThisFrame[0] = (worldPosition[0] - x); + positionDeltaThisFrame[1] = (worldPosition[1] - y); + positionDeltaThisFrame[2] = (worldPosition[2] - z); + + worldPosition[0] = x; + worldPosition[1] = y; + worldPosition[2] = z; +} + +void VR_GetMove(float *joy_forward, float *joy_side, float *hmd_forward, float *hmd_side, float *up, + float *yaw, float *pitch, float *roll) +{ + *joy_forward = remote_movementForward; + *hmd_forward = positional_movementForward; + *up = remote_movementUp; + *joy_side = remote_movementSideways; + *hmd_side = positional_movementSideways; + *yaw = cinemamode ? cinemamodeYaw : hmdorientation[YAW] + snapTurn; + *pitch = cinemamode ? cinemamodePitch : hmdorientation[PITCH]; + *roll = cinemamode ? 0.0f : hmdorientation[ROLL]; +} + +void VR_DoomMain(int argc, char** argv); + +void VR_Init() +{ + //Initialise all our variables + playerYaw = 0.0f; + resetDoomYaw = true; + resetPreviousPitch = true; + remote_movementSideways = 0.0f; + remote_movementForward = 0.0f; + remote_movementUp = 0.0f; + positional_movementSideways = 0.0f; + positional_movementForward = 0.0f; + snapTurn = 0.0f; + cinemamodeYaw = 0.0f; + cinemamodePitch = 0.0f; + + //init randomiser + srand(time(NULL)); + + shutdown = false; + ready_teleport = false; + trigger_teleport = false; + + cinemamode = false; + + chdir("/sdcard/QuestZDoom"); +} + +int VR_main( int argc, char* argv[] ); + +void * AppThreadFunction(void * parm ) { + gAppThread = (ovrAppThread *) parm; + + java.Vm = gAppThread->JavaVm; + java.Vm->AttachCurrentThread(&java.Env, NULL); + java.ActivityObject = gAppThread->ActivityObject; + + jclass cls = java.Env->GetObjectClass(java.ActivityObject); + + // Note that AttachCurrentThread will reset the thread name. + prctl(PR_SET_NAME, (long) "AppThreadFunction", 0, 0, 0); + + //Set device defaults + if (SS_MULTIPLIER == 0.0f) + { + //GB Override as refresh is now 72 by default as we decided a higher res is better as 90hz has stutters + SS_MULTIPLIER = 1.25f; + } + else if (SS_MULTIPLIER > 1.5f) + { + SS_MULTIPLIER = 1.5f; + } + + gAppState.MainThreadTid = gettid(); + + VR_Init(); + + TBXR_InitialiseOpenXR(); + + TBXR_EnterVR(); + TBXR_InitRenderer(); + TBXR_InitActions(); + + TBXR_WaitForSessionActive(); + + if (REFRESH != 0) + { + QzDoom_SetRefreshRate(REFRESH); + } + + if (hasIWADs)// && hasLauncher) + { + //Should now be all set up and ready - start the Doom main loop + VR_DoomMain(argc, argv); + } + + TBXR_LeaveVR(); + + //Ask Java to shut down + jni_shutdown(); + + return NULL; +} + +//All the stuff we want to do each frame specifically for this game +void VR_FrameSetup() +{ + +} + +bool VR_GetVRProjection(int eye, float zNear, float zFar, float* projection) +{ +#ifdef PICO_XR + XrMatrix4x4f_CreateProjectionFov( + &(gAppState.ProjectionMatrices[eye]), GRAPHICS_OPENGL_ES, + gAppState.Projections[eye].fov, zNear, zFar); +#endif + +#ifdef META_QUEST + XrFovf fov = {}; + for (int eye = 0; eye < ovrMaxNumEyes; eye++) { + fov.angleLeft += gAppState.Projections[eye].fov.angleLeft / 2.0f; + fov.angleRight += gAppState.Projections[eye].fov.angleRight / 2.0f; + fov.angleUp += gAppState.Projections[eye].fov.angleUp / 2.0f; + fov.angleDown += gAppState.Projections[eye].fov.angleDown / 2.0f; + } + XrMatrix4x4f_CreateProjectionFov( + &(gAppState.ProjectionMatrices[eye]), GRAPHICS_OPENGL_ES, + fov, zNear, zFar); +#endif + + memcpy(projection, gAppState.ProjectionMatrices[eye].m, 16 * sizeof(float)); + return true; +} + + +extern "C" { +void jni_haptic_event(const char *event, int position, int intensity, float angle, float yHeight); +void jni_haptic_updateevent(const char *event, int intensity, float angle); +void jni_haptic_stopevent(const char *event); +void jni_haptic_endframe(); +void jni_haptic_enable(); +void jni_haptic_disable(); +}; + +void VR_ExternalHapticEvent(const char* event, int position, int flags, int intensity, float angle, float yHeight ) +{ + jni_haptic_event(event, position, intensity, angle, yHeight); +} + +void VR_HapticStopEvent(const char* event) +{ + jni_haptic_stopevent(event); +} + +void VR_HapticEnable() +{ + static bool firstTime = true; + if (firstTime) { + jni_haptic_enable(); + firstTime = false; + jni_haptic_event("fire_pistol", 0, 100, 0, 0); + } +} + +void VR_HapticDisable() +{ + jni_haptic_disable(); +} + +void VR_HapticEvent(const char* event, int position, int intensity, float angle, float yHeight ) +{ + static char buffer[256]; + + memset(buffer, 0, 256); + for(int i = 0; event[i]; i++) + { + buffer[i] = tolower(event[i]); + } + + jni_haptic_event(buffer, position, intensity, angle, yHeight); +} + +void QzDoom_Vibrate(float duration, int channel, float intensity ) +{ + TBXR_Vibrate(duration, channel+1, intensity); +} + +void VR_HandleControllerInput() { + TBXR_UpdateControllers(); + + //Call additional control schemes here + switch (vr_control_scheme) + { + case RIGHT_HANDED_DEFAULT: + HandleInput_Default(vr_control_scheme, + &rightTrackedRemoteState_new, &rightTrackedRemoteState_old, &rightRemoteTracking_new, + &leftTrackedRemoteState_new, &leftTrackedRemoteState_old, &leftRemoteTracking_new, + xrButton_A, xrButton_B, xrButton_X, xrButton_Y); + break; + case LEFT_HANDED_DEFAULT: + case LEFT_HANDED_ALT: + HandleInput_Default(vr_control_scheme, + &leftTrackedRemoteState_new, &leftTrackedRemoteState_old, &leftRemoteTracking_new, + &rightTrackedRemoteState_new, &rightTrackedRemoteState_old, &rightRemoteTracking_new, + xrButton_X, xrButton_Y, xrButton_A, xrButton_B); + break; + } +} + +/* +================================================================================ + +Activity lifecycle + +================================================================================ +*/ + +jmethodID android_shutdown; +static JavaVM *jVM; +static jobject jniCallbackObj=0; + +void jni_shutdown() +{ + ALOGV("Calling: jni_shutdown"); + JNIEnv *env; + jobject tmp; + if ((jVM->GetEnv((void**) &env, JNI_VERSION_1_4))<0) + { + jVM->AttachCurrentThread(&env, NULL); + } + return env->CallVoidMethod(jniCallbackObj, android_shutdown); +} + +void VR_Shutdown() +{ + jni_shutdown(); +} + +jmethodID android_haptic_event; +jmethodID android_haptic_stopevent; +jmethodID android_haptic_enable; +jmethodID android_haptic_disable; + +void jni_haptic_event(const char* event, int position, int intensity, float angle, float yHeight) +{ + JNIEnv *env; + jobject tmp; + if ((jVM->GetEnv((void**) &env, JNI_VERSION_1_4))<0) + { + jVM->AttachCurrentThread(&env, NULL); + } + + jstring StringArg1 = env->NewStringUTF(event); + + return env->CallVoidMethod(jniCallbackObj, android_haptic_event, StringArg1, position, intensity, angle, yHeight); +} + +void jni_haptic_stopevent(const char* event) +{ + ALOGV("Calling: jni_haptic_stopevent"); + JNIEnv *env; + jobject tmp; + if ((jVM->GetEnv((void**) &env, JNI_VERSION_1_4))<0) + { + jVM->AttachCurrentThread(&env, NULL); + } + + jstring StringArg1 = env->NewStringUTF(event); + + return env->CallVoidMethod(jniCallbackObj, android_haptic_stopevent, StringArg1); +} + + +void jni_haptic_enable() +{ + ALOGV("Calling: jni_haptic_enable"); + JNIEnv *env; + jobject tmp; + if ((jVM->GetEnv((void**) &env, JNI_VERSION_1_4))<0) + { + jVM->AttachCurrentThread(&env, NULL); + } + + return env->CallVoidMethod(jniCallbackObj, android_haptic_enable); +} + +void jni_haptic_disable() +{ + ALOGV("Calling: jni_haptic_disable"); + JNIEnv *env; + jobject tmp; + if ((jVM->GetEnv((void**) &env, JNI_VERSION_1_4))<0) + { + jVM->AttachCurrentThread(&env, NULL); + } + + return env->CallVoidMethod(jniCallbackObj, android_haptic_disable); +} + +extern "C" { + +int JNI_OnLoad(JavaVM* vm, void* reserved) +{ + JNIEnv *env; + jVM = vm; + if(vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) + { + ALOGE("Failed JNI_OnLoad"); + return -1; + } + + return JNI_VERSION_1_4; +} + +JNIEXPORT jlong JNICALL Java_com_drbeef_questzdoom_GLES3JNILib_onCreate( JNIEnv * env, jclass activityClass, jobject activity, + jstring commandLineParams, jboolean jHasIWADs, jboolean jHasLauncher) +{ + ALOGV( " GLES3JNILib::onCreate()" ); + + /* the global arg_xxx structs are initialised within the argtable */ + void *argtable[] = { + ss = arg_dbl0("s", "supersampling", "", "super sampling value (default: Q1: 1.2, Q2: 1.35)"), + cpu = arg_int0("c", "cpu", "", "CPU perf index 1-4 (default: 2)"), + gpu = arg_int0("g", "gpu", "", "GPU perf index 1-4 (default: 3)"), + msaa = arg_int0("m", "msaa", "", "MSAA (default: 1)"), + refresh = arg_int0("r", "refresh", "", "Refresh Rate (default: Q1: 72, Q2: 72)"), + end = arg_end(20) + }; + + hasIWADs = jHasIWADs != 0; + hasLauncher = jHasLauncher != 0; + + jboolean iscopy; + const char *arg = env->GetStringUTFChars(commandLineParams, &iscopy); + + char *cmdLine = NULL; + if (arg && strlen(arg)) + { + cmdLine = strdup(arg); + } + + env->ReleaseStringUTFChars(commandLineParams, arg); + + ALOGV("Command line %s", cmdLine); + argv = (char**)malloc(sizeof(char*) * 255); + argc = ParseCommandLine(strdup(cmdLine), argv); + + /* verify the argtable[] entries were allocated sucessfully */ + if (arg_nullcheck(argtable) == 0) { + /* Parse the command line as defined by argtable[] */ + arg_parse(argc, argv, argtable); + + if (ss->count > 0 && ss->dval[0] > 0.0) + { + SS_MULTIPLIER = ss->dval[0]; + } + + if (msaa->count > 0 && msaa->ival[0] > 0 && msaa->ival[0] < 10) + { + NUM_MULTI_SAMPLES = msaa->ival[0]; + } + + if (refresh->count > 0 && refresh->ival[0] > 0 && refresh->ival[0] <= 120) + { + REFRESH = refresh->ival[0]; + } + } + + ovrAppThread * appThread = (ovrAppThread *) malloc( sizeof( ovrAppThread ) ); + ovrAppThread_Create( appThread, env, activity, activityClass ); + + surfaceMessageQueue_Enable(&appThread->MessageQueue, true); + srufaceMessage message; + surfaceMessage_Init(&message, MESSAGE_ON_CREATE, MQ_WAIT_PROCESSED); + surfaceMessageQueue_PostMessage(&appThread->MessageQueue, &message); + + return (jlong)((size_t)appThread); +} + + +JNIEXPORT void JNICALL Java_com_drbeef_questzdoom_GLES3JNILib_onStart( JNIEnv * env, jobject obj, jlong handle, jobject obj1) +{ + ALOGV( " GLES3JNILib::onStart()" ); + + + jniCallbackObj = (jobject)env->NewGlobalRef( obj1); + jclass callbackClass = env->GetObjectClass( jniCallbackObj); + + android_shutdown = env->GetMethodID(callbackClass,"shutdown","()V"); + + android_haptic_event = env->GetMethodID(callbackClass, "haptic_event", "(Ljava/lang/String;IIFF)V"); + android_haptic_stopevent = env->GetMethodID(callbackClass, "haptic_stopevent", "(Ljava/lang/String;)V"); + android_haptic_enable = env->GetMethodID(callbackClass, "haptic_enable", "()V"); + android_haptic_disable = env->GetMethodID(callbackClass, "haptic_disable", "()V"); + + ovrAppThread * appThread = (ovrAppThread *)((size_t)handle); + srufaceMessage message; + surfaceMessage_Init(&message, MESSAGE_ON_START, MQ_WAIT_PROCESSED); + surfaceMessageQueue_PostMessage(&appThread->MessageQueue, &message); +} + +JNIEXPORT void JNICALL Java_com_drbeef_questzdoom_GLES3JNILib_onResume( JNIEnv * env, jobject obj, jlong handle ) +{ + ALOGV( " GLES3JNILib::onResume()" ); + ovrAppThread * appThread = (ovrAppThread *)((size_t)handle); + srufaceMessage message; + surfaceMessage_Init(&message, MESSAGE_ON_RESUME, MQ_WAIT_PROCESSED); + surfaceMessageQueue_PostMessage(&appThread->MessageQueue, &message); +} + +JNIEXPORT void JNICALL Java_com_drbeef_questzdoom_GLES3JNILib_onPause( JNIEnv * env, jobject obj, jlong handle ) +{ + ALOGV( " GLES3JNILib::onPause()" ); + ovrAppThread * appThread = (ovrAppThread *)((size_t)handle); + srufaceMessage message; + surfaceMessage_Init(&message, MESSAGE_ON_PAUSE, MQ_WAIT_PROCESSED); + surfaceMessageQueue_PostMessage(&appThread->MessageQueue, &message); +} + +JNIEXPORT void JNICALL Java_com_drbeef_questzdoom_GLES3JNILib_onStop( JNIEnv * env, jobject obj, jlong handle ) +{ + ALOGV( " GLES3JNILib::onStop()" ); + ovrAppThread * appThread = (ovrAppThread *)((size_t)handle); + srufaceMessage message; + surfaceMessage_Init(&message, MESSAGE_ON_STOP, MQ_WAIT_PROCESSED); + surfaceMessageQueue_PostMessage(&appThread->MessageQueue, &message); +} + +JNIEXPORT void JNICALL Java_com_drbeef_questzdoom_GLES3JNILib_onDestroy( JNIEnv * env, jobject obj, jlong handle ) +{ + ALOGV( " GLES3JNILib::onDestroy()" ); + ovrAppThread * appThread = (ovrAppThread *)((size_t)handle); + srufaceMessage message; + surfaceMessage_Init(&message, MESSAGE_ON_DESTROY, MQ_WAIT_PROCESSED); + surfaceMessageQueue_PostMessage(&appThread->MessageQueue, &message); + surfaceMessageQueue_Enable(&appThread->MessageQueue, false); + + ovrAppThread_Destroy( appThread, env ); + free( appThread ); +} + +/* +================================================================================ + +Surface lifecycle + +================================================================================ +*/ + +JNIEXPORT void JNICALL Java_com_drbeef_questzdoom_GLES3JNILib_onSurfaceCreated( JNIEnv * env, jobject obj, jlong handle, jobject surface ) +{ + ALOGV( " GLES3JNILib::onSurfaceCreated()" ); + ovrAppThread * appThread = (ovrAppThread *)((size_t)handle); + + ANativeWindow * newNativeWindow = ANativeWindow_fromSurface( env, surface ); + if ( ANativeWindow_getWidth( newNativeWindow ) < ANativeWindow_getHeight( newNativeWindow ) ) + { + // An app that is relaunched after pressing the home button gets an initial surface with + // the wrong orientation even though android:screenOrientation="landscape" is set in the + // manifest. The choreographer callback will also never be called for this surface because + // the surface is immediately replaced with a new surface with the correct orientation. + ALOGE( " Surface not in landscape mode!" ); + } + + ALOGV( " NativeWindow = ANativeWindow_fromSurface( env, surface )" ); + appThread->NativeWindow = newNativeWindow; + srufaceMessage message; + surfaceMessage_Init(&message, MESSAGE_ON_SURFACE_CREATED, MQ_WAIT_PROCESSED); + surfaceMessage_SetPointerParm(&message, 0, appThread->NativeWindow); + surfaceMessageQueue_PostMessage(&appThread->MessageQueue, &message); +} + +JNIEXPORT void JNICALL Java_com_drbeef_questzdoom_GLES3JNILib_onSurfaceChanged( JNIEnv * env, jobject obj, jlong handle, jobject surface ) +{ + ALOGV( " GLES3JNILib::onSurfaceChanged()" ); + ovrAppThread * appThread = (ovrAppThread *)((size_t)handle); + + ANativeWindow * newNativeWindow = ANativeWindow_fromSurface( env, surface ); + if ( ANativeWindow_getWidth( newNativeWindow ) < ANativeWindow_getHeight( newNativeWindow ) ) + { + // An app that is relaunched after pressing the home button gets an initial surface with + // the wrong orientation even though android:screenOrientation="landscape" is set in the + // manifest. The choreographer callback will also never be called for this surface because + // the surface is immediately replaced with a new surface with the correct orientation. + ALOGE( " Surface not in landscape mode!" ); + } + + if ( newNativeWindow != appThread->NativeWindow ) + { + if ( appThread->NativeWindow != NULL ) + { + srufaceMessage message; + surfaceMessage_Init(&message, MESSAGE_ON_SURFACE_DESTROYED, MQ_WAIT_PROCESSED); + surfaceMessageQueue_PostMessage(&appThread->MessageQueue, &message); + ALOGV( " ANativeWindow_release( NativeWindow )" ); + ANativeWindow_release( appThread->NativeWindow ); + appThread->NativeWindow = NULL; + } + if ( newNativeWindow != NULL ) + { + ALOGV( " NativeWindow = ANativeWindow_fromSurface( env, surface )" ); + appThread->NativeWindow = newNativeWindow; + srufaceMessage message; + surfaceMessage_Init(&message, MESSAGE_ON_SURFACE_CREATED, MQ_WAIT_PROCESSED); + surfaceMessage_SetPointerParm(&message, 0, appThread->NativeWindow); + surfaceMessageQueue_PostMessage(&appThread->MessageQueue, &message); + } + } + else if ( newNativeWindow != NULL ) + { + ANativeWindow_release( newNativeWindow ); + } +} + +JNIEXPORT void JNICALL Java_com_drbeef_questzdoom_GLES3JNILib_onSurfaceDestroyed( JNIEnv * env, jobject obj, jlong handle ) +{ + ALOGV( " GLES3JNILib::onSurfaceDestroyed()" ); + ovrAppThread * appThread = (ovrAppThread *)((size_t)handle); + srufaceMessage message; + surfaceMessage_Init(&message, MESSAGE_ON_SURFACE_DESTROYED, MQ_WAIT_PROCESSED); + surfaceMessageQueue_PostMessage(&appThread->MessageQueue, &message); + ALOGV( " ANativeWindow_release( NativeWindow )" ); + ANativeWindow_release( appThread->NativeWindow ); + appThread->NativeWindow = NULL; +} + +} \ No newline at end of file diff --git a/Projects/Android/jni/QzDoom/TBXR_Common.cpp b/Projects/Android/jni/QzDoom/TBXR_Common.cpp new file mode 100644 index 0000000..89de929 --- /dev/null +++ b/Projects/Android/jni/QzDoom/TBXR_Common.cpp @@ -0,0 +1,2020 @@ +#include +#include +#include +#include +#include +#include +#include // for prctl( PR_SET_NAME ) +#include +#include // for native window JNI +#include + +#include "argtable3.h" +#include "VrInput.h" + +#include +#include +#include +#include +#define GL_GLEXT_PROTOTYPES + +#include "VrInput.h" +#include "VrCommon.h" +#include "../SupportLibs/jpeg8d/jinclude.h" +#include "../SupportLibs/libpng/pngconf.h" + +#if !defined( EGL_OPENGL_ES3_BIT_KHR ) +#define EGL_OPENGL_ES3_BIT_KHR 0x0040 +#endif + +// EXT_texture_border_clamp +#ifndef GL_CLAMP_TO_BORDER +#define GL_CLAMP_TO_BORDER 0x812D +#endif + +#ifndef GL_TEXTURE_BORDER_COLOR +#define GL_TEXTURE_BORDER_COLOR 0x1004 +#endif + +#ifndef GLAPI +#define GLAPI extern +#endif +//#define ENABLE_GL_DEBUG +#define ENABLE_GL_DEBUG_VERBOSE 1 + +#define EGL_SYNC + +#if defined EGL_SYNC +// EGL_KHR_reusable_sync +PFNEGLCREATESYNCKHRPROC eglCreateSyncKHR; +PFNEGLDESTROYSYNCKHRPROC eglDestroySyncKHR; +PFNEGLCLIENTWAITSYNCKHRPROC eglClientWaitSyncKHR; +PFNEGLSIGNALSYNCKHRPROC eglSignalSyncKHR; +PFNEGLGETSYNCATTRIBKHRPROC eglGetSyncAttribKHR; +#endif + +//Let's go to the maximum! +int NUM_MULTI_SAMPLES = 2; +int REFRESH = 0; +float SS_MULTIPLIER = 0.0f; + +GLboolean stageSupported = GL_FALSE; + +#ifdef META_QUEST + +const char* const requiredExtensionNames[] = { + XR_KHR_OPENGL_ES_ENABLE_EXTENSION_NAME, + XR_EXT_PERFORMANCE_SETTINGS_EXTENSION_NAME, + XR_KHR_ANDROID_THREAD_SETTINGS_EXTENSION_NAME, + XR_FB_DISPLAY_REFRESH_RATE_EXTENSION_NAME, + XR_FB_COLOR_SPACE_EXTENSION_NAME}; +#endif //META_QUEST + +#ifdef PICO_XR +#define XR_PICO_CONFIGS_EXT_EXTENSION_NAME "XR_PICO_configs_ext" + +enum ConfigsEXT +{ + RENDER_TEXTURE_WIDTH = 0, + RENDER_TEXTURE_HEIGHT, + SHOW_FPS, + RUNTIME_LOG_LEVEL, + PXRPLUGIN_LOG_LEVEL, + UNITY_LOG_LEVEL, + UNREAL_LOG_LEVEL, + NATIVE_LOG_LEVEL, + TARGET_FRAME_RATE, + NECK_MODEL_X, + NECK_MODEL_Y, + NECK_MODEL_Z, + DISPLAY_REFRESH_RATE, + ENABLE_6DOF, + CONTROLLER_TYPE, + PHYSICAL_IPD, + TO_DELTA_SENSOR_Y, + GET_DISPLAY_RATE, + FOVEATION_SUBSAMPLED_ENABLED = 18, + TRACKING_ORIGIN_HEIGHT +}; +typedef XrResult (XRAPI_PTR *PFN_xrGetConfigPICO)( + XrSession session, + enum ConfigsEXT configIndex, + float * configData); +PFN_xrGetConfigPICO pfnXrGetConfigPICO; + + +enum ConfigsSetEXT +{ + UNREAL_VERSION = 0, + TRACKING_ORIGIN, + OPENGL_NOERROR, + ENABLE_SIX_DOF, + PRESENTATION_FLAG, + ENABLE_CPT, + PLATFORM, + FOVEATION_LEVEL, + SET_DISPLAY_RATE = 8, + MRC_TEXTURE_ID = 9, +}; + +typedef XrResult (XRAPI_PTR *PFN_xrSetConfigPICO) ( + XrSession session, + enum ConfigsSetEXT configIndex, + char * configData); +PFN_xrSetConfigPICO pfnXrSetConfigPICO; + +const char* const requiredExtensionNames[] = { + XR_KHR_ANDROID_CREATE_INSTANCE_EXTENSION_NAME, + XR_EXT_PERFORMANCE_SETTINGS_EXTENSION_NAME, + XR_KHR_OPENGL_ES_ENABLE_EXTENSION_NAME, + XR_PICO_CONFIGS_EXT_EXTENSION_NAME}; + +#endif //PICO_XR + +const uint32_t numRequiredExtensions = + sizeof(requiredExtensionNames) / sizeof(requiredExtensionNames[0]); + + +/* +================================================================================ + +System Clock Time in millis + +================================================================================ +*/ + +double TBXR_GetTimeInMilliSeconds() +{ + struct timespec now; + clock_gettime( CLOCK_MONOTONIC, &now ); + return ( now.tv_sec * 1e9 + now.tv_nsec ) * (double)(1e-6); +} + +int runStatus = -1; +void TBXR_exit(int exitCode) +{ + runStatus = exitCode; +} + +/* +================================================================================ + +OpenGL-ES Utility Functions + +================================================================================ +*/ + +typedef struct +{ + bool multi_view; // GL_OVR_multiview, GL_OVR_multiview2 + bool EXT_texture_border_clamp; // GL_EXT_texture_border_clamp, GL_OES_texture_border_clamp +} OpenGLExtensions_t; + +OpenGLExtensions_t glExtensions; + +static void EglInitExtensions() +{ +#if defined EGL_SYNC + eglCreateSyncKHR = (PFNEGLCREATESYNCKHRPROC) eglGetProcAddress( "eglCreateSyncKHR" ); + eglDestroySyncKHR = (PFNEGLDESTROYSYNCKHRPROC) eglGetProcAddress( "eglDestroySyncKHR" ); + eglClientWaitSyncKHR = (PFNEGLCLIENTWAITSYNCKHRPROC) eglGetProcAddress( "eglClientWaitSyncKHR" ); + eglSignalSyncKHR = (PFNEGLSIGNALSYNCKHRPROC) eglGetProcAddress( "eglSignalSyncKHR" ); + eglGetSyncAttribKHR = (PFNEGLGETSYNCATTRIBKHRPROC) eglGetProcAddress( "eglGetSyncAttribKHR" ); +#endif + + const char * allExtensions = (const char *)glGetString( GL_EXTENSIONS ); + if ( allExtensions != NULL ) + { + glExtensions.multi_view = strstr( allExtensions, "GL_OVR_multiview2" ) && + strstr( allExtensions, "GL_OVR_multiview_multisampled_render_to_texture" ); + + glExtensions.EXT_texture_border_clamp = false;//strstr( allExtensions, "GL_EXT_texture_border_clamp" ) || + //strstr( allExtensions, "GL_OES_texture_border_clamp" ); + } +} + +static const char * EglErrorString( const EGLint error ) +{ + switch ( error ) + { + case EGL_SUCCESS: return "EGL_SUCCESS"; + case EGL_NOT_INITIALIZED: return "EGL_NOT_INITIALIZED"; + case EGL_BAD_ACCESS: return "EGL_BAD_ACCESS"; + case EGL_BAD_ALLOC: return "EGL_BAD_ALLOC"; + case EGL_BAD_ATTRIBUTE: return "EGL_BAD_ATTRIBUTE"; + case EGL_BAD_CONTEXT: return "EGL_BAD_CONTEXT"; + case EGL_BAD_CONFIG: return "EGL_BAD_CONFIG"; + case EGL_BAD_CURRENT_SURFACE: return "EGL_BAD_CURRENT_SURFACE"; + case EGL_BAD_DISPLAY: return "EGL_BAD_DISPLAY"; + case EGL_BAD_SURFACE: return "EGL_BAD_SURFACE"; + case EGL_BAD_MATCH: return "EGL_BAD_MATCH"; + case EGL_BAD_PARAMETER: return "EGL_BAD_PARAMETER"; + case EGL_BAD_NATIVE_PIXMAP: return "EGL_BAD_NATIVE_PIXMAP"; + case EGL_BAD_NATIVE_WINDOW: return "EGL_BAD_NATIVE_WINDOW"; + case EGL_CONTEXT_LOST: return "EGL_CONTEXT_LOST"; + default: return "unknown"; + } +} + +static const char * GlFrameBufferStatusString( GLenum status ) +{ + switch ( status ) + { + case GL_FRAMEBUFFER_UNDEFINED: return "GL_FRAMEBUFFER_UNDEFINED"; + case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: return "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT"; + case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: return "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT"; + case GL_FRAMEBUFFER_UNSUPPORTED: return "GL_FRAMEBUFFER_UNSUPPORTED"; + case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: return "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE"; + default: return "unknown"; + } +} + + +/* +================================================================================ + +ovrEgl + +================================================================================ +*/ + +static void ovrEgl_Clear( ovrEgl * egl ) +{ + egl->MajorVersion = 0; + egl->MinorVersion = 0; + egl->Display = 0; + egl->Config = 0; + egl->TinySurface = EGL_NO_SURFACE; + egl->MainSurface = EGL_NO_SURFACE; + egl->Context = EGL_NO_CONTEXT; +} + +static void ovrEgl_CreateContext( ovrEgl * egl, const ovrEgl * shareEgl ) +{ + if ( egl->Display != 0 ) + { + return; + } + + egl->Display = eglGetDisplay( EGL_DEFAULT_DISPLAY ); + ALOGV( " eglInitialize( Display, &MajorVersion, &MinorVersion )" ); + eglInitialize( egl->Display, &egl->MajorVersion, &egl->MinorVersion ); + // Do NOT use eglChooseConfig, because the Android EGL code pushes in multisample + // flags in eglChooseConfig if the user has selected the "force 4x MSAA" option in + // settings, and that is completely wasted for our warp target. + const int MAX_CONFIGS = 1024; + EGLConfig configs[MAX_CONFIGS]; + EGLint numConfigs = 0; + if ( eglGetConfigs( egl->Display, configs, MAX_CONFIGS, &numConfigs ) == EGL_FALSE ) + { + ALOGE( " eglGetConfigs() failed: %s", EglErrorString( eglGetError() ) ); + return; + } + const EGLint configAttribs[] = + { + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, // need alpha for the multi-pass timewarp compositor + EGL_DEPTH_SIZE, 0, + EGL_STENCIL_SIZE, 0, + EGL_SAMPLES, 0, + EGL_NONE + }; + egl->Config = 0; + for ( int i = 0; i < numConfigs; i++ ) + { + EGLint value = 0; + + eglGetConfigAttrib( egl->Display, configs[i], EGL_RENDERABLE_TYPE, &value ); + if ( ( value & EGL_OPENGL_ES3_BIT_KHR ) != EGL_OPENGL_ES3_BIT_KHR ) + { + continue; + } + + // The pbuffer config also needs to be compatible with normal window rendering + // so it can share textures with the window context. + eglGetConfigAttrib( egl->Display, configs[i], EGL_SURFACE_TYPE, &value ); + if ( ( value & ( EGL_WINDOW_BIT | EGL_PBUFFER_BIT ) ) != ( EGL_WINDOW_BIT | EGL_PBUFFER_BIT ) ) + { + continue; + } + + int j = 0; + for ( ; configAttribs[j] != EGL_NONE; j += 2 ) + { + eglGetConfigAttrib( egl->Display, configs[i], configAttribs[j], &value ); + if ( value != configAttribs[j + 1] ) + { + break; + } + } + if ( configAttribs[j] == EGL_NONE ) + { + egl->Config = configs[i]; + break; + } + } + if ( egl->Config == 0 ) + { + ALOGE( " eglChooseConfig() failed: %s", EglErrorString( eglGetError() ) ); + return; + } + EGLint contextAttribs[] = + { + EGL_CONTEXT_CLIENT_VERSION, 3, + EGL_NONE + }; + ALOGV( " Context = eglCreateContext( Display, Config, EGL_NO_CONTEXT, contextAttribs )" ); + egl->Context = eglCreateContext( egl->Display, egl->Config, ( shareEgl != NULL ) ? shareEgl->Context : EGL_NO_CONTEXT, contextAttribs ); + if ( egl->Context == EGL_NO_CONTEXT ) + { + ALOGE( " eglCreateContext() failed: %s", EglErrorString( eglGetError() ) ); + return; + } + const EGLint surfaceAttribs[] = + { + EGL_WIDTH, 16, + EGL_HEIGHT, 16, + EGL_NONE + }; + ALOGV( " TinySurface = eglCreatePbufferSurface( Display, Config, surfaceAttribs )" ); + egl->TinySurface = eglCreatePbufferSurface( egl->Display, egl->Config, surfaceAttribs ); + if ( egl->TinySurface == EGL_NO_SURFACE ) + { + ALOGE( " eglCreatePbufferSurface() failed: %s", EglErrorString( eglGetError() ) ); + eglDestroyContext( egl->Display, egl->Context ); + egl->Context = EGL_NO_CONTEXT; + return; + } + ALOGV( " eglMakeCurrent( Display, TinySurface, TinySurface, Context )" ); + if ( eglMakeCurrent( egl->Display, egl->TinySurface, egl->TinySurface, egl->Context ) == EGL_FALSE ) + { + ALOGE( " eglMakeCurrent() failed: %s", EglErrorString( eglGetError() ) ); + eglDestroySurface( egl->Display, egl->TinySurface ); + eglDestroyContext( egl->Display, egl->Context ); + egl->Context = EGL_NO_CONTEXT; + return; + } +} + +static void ovrEgl_DestroyContext( ovrEgl * egl ) +{ + if ( egl->Display != 0 ) + { + ALOGE( " eglMakeCurrent( Display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT )" ); + if ( eglMakeCurrent( egl->Display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT ) == EGL_FALSE ) + { + ALOGE( " eglMakeCurrent() failed: %s", EglErrorString( eglGetError() ) ); + } + } + if ( egl->Context != EGL_NO_CONTEXT ) + { + ALOGE( " eglDestroyContext( Display, Context )" ); + if ( eglDestroyContext( egl->Display, egl->Context ) == EGL_FALSE ) + { + ALOGE( " eglDestroyContext() failed: %s", EglErrorString( eglGetError() ) ); + } + egl->Context = EGL_NO_CONTEXT; + } + if ( egl->TinySurface != EGL_NO_SURFACE ) + { + ALOGE( " eglDestroySurface( Display, TinySurface )" ); + if ( eglDestroySurface( egl->Display, egl->TinySurface ) == EGL_FALSE ) + { + ALOGE( " eglDestroySurface() failed: %s", EglErrorString( eglGetError() ) ); + } + egl->TinySurface = EGL_NO_SURFACE; + } + if ( egl->Display != 0 ) + { + ALOGE( " eglTerminate( Display )" ); + if ( eglTerminate( egl->Display ) == EGL_FALSE ) + { + ALOGE( " eglTerminate() failed: %s", EglErrorString( eglGetError() ) ); + } + egl->Display = 0; + } +} + +/* +================================================================================ + +ovrFramebuffer + +================================================================================ +*/ + +static void ovrFramebuffer_Clear(ovrFramebuffer* frameBuffer) { + frameBuffer->Width = 0; + frameBuffer->Height = 0; + frameBuffer->Multisamples = 0; + frameBuffer->TextureSwapChainLength = 0; + frameBuffer->TextureSwapChainIndex = 0; + frameBuffer->ColorSwapChain.Handle = XR_NULL_HANDLE; + frameBuffer->ColorSwapChain.Width = 0; + frameBuffer->ColorSwapChain.Height = 0; + frameBuffer->ColorSwapChainImage = NULL; + frameBuffer->DepthBuffers = NULL; + frameBuffer->FrameBuffers = NULL; +} + +typedef void (GL_APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (GL_APIENTRYP PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEEXTPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLsizei samples); + + +static bool ovrFramebuffer_Create( + XrSession session, + ovrFramebuffer* frameBuffer, + const GLenum colorFormat, + const int width, + const int height, + const int multisamples) { + PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC glRenderbufferStorageMultisampleEXT = + (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC)eglGetProcAddress( + "glRenderbufferStorageMultisampleEXT"); + PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEEXTPROC glFramebufferTexture2DMultisampleEXT = + (PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEEXTPROC)eglGetProcAddress( + "glFramebufferTexture2DMultisampleEXT"); + + frameBuffer->Width = width; + frameBuffer->Height = height; + frameBuffer->Multisamples = multisamples; + + XrSwapchainCreateInfo swapChainCreateInfo; + memset(&swapChainCreateInfo, 0, sizeof(swapChainCreateInfo)); + swapChainCreateInfo.type = XR_TYPE_SWAPCHAIN_CREATE_INFO; + swapChainCreateInfo.usageFlags = XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT; + swapChainCreateInfo.format = colorFormat; + swapChainCreateInfo.sampleCount = 1; + swapChainCreateInfo.width = width; + swapChainCreateInfo.height = height; + swapChainCreateInfo.faceCount = 1; + swapChainCreateInfo.arraySize = 1; + swapChainCreateInfo.mipCount = 1; + + frameBuffer->ColorSwapChain.Width = swapChainCreateInfo.width; + frameBuffer->ColorSwapChain.Height = swapChainCreateInfo.height; + + // Create the swapchain. + OXR(xrCreateSwapchain(session, &swapChainCreateInfo, &frameBuffer->ColorSwapChain.Handle)); + // Get the number of swapchain images. + OXR(xrEnumerateSwapchainImages( + frameBuffer->ColorSwapChain.Handle, 0, &frameBuffer->TextureSwapChainLength, NULL)); + // Allocate the swapchain images array. + frameBuffer->ColorSwapChainImage = (XrSwapchainImageOpenGLESKHR*)malloc( + frameBuffer->TextureSwapChainLength * sizeof(XrSwapchainImageOpenGLESKHR)); + + // Populate the swapchain image array. + for (uint32_t i = 0; i < frameBuffer->TextureSwapChainLength; i++) { + frameBuffer->ColorSwapChainImage[i].type = XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_ES_KHR; + frameBuffer->ColorSwapChainImage[i].next = NULL; + } + OXR(xrEnumerateSwapchainImages( + frameBuffer->ColorSwapChain.Handle, + frameBuffer->TextureSwapChainLength, + &frameBuffer->TextureSwapChainLength, + (XrSwapchainImageBaseHeader*)frameBuffer->ColorSwapChainImage)); + + frameBuffer->DepthBuffers = + (GLuint*)malloc(frameBuffer->TextureSwapChainLength * sizeof(GLuint)); + frameBuffer->FrameBuffers = + (GLuint*)malloc(frameBuffer->TextureSwapChainLength * sizeof(GLuint)); + + for (uint32_t i = 0; i < frameBuffer->TextureSwapChainLength; i++) { + // Create the color buffer texture. + const GLuint colorTexture = frameBuffer->ColorSwapChainImage[i].image; + + GLfloat borderColor[] = {0.0f, 0.0f, 0.0f, 0.0f}; + GLenum colorTextureTarget = GL_TEXTURE_2D; + GL(glTexParameterfv(colorTextureTarget, GL_TEXTURE_BORDER_COLOR, borderColor)); + GL(glBindTexture(colorTextureTarget, colorTexture)); + GL(glTexParameteri(colorTextureTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); + GL(glTexParameteri(colorTextureTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); + GL(glTexParameteri(colorTextureTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); + GL(glTexParameteri(colorTextureTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); + GL(glBindTexture(colorTextureTarget, 0)); + + if (glRenderbufferStorageMultisampleEXT != NULL && + glFramebufferTexture2DMultisampleEXT != NULL) { + // Create multisampled depth buffer. + GL(glGenRenderbuffers(1, &frameBuffer->DepthBuffers[i])); + GL(glBindRenderbuffer(GL_RENDERBUFFER, frameBuffer->DepthBuffers[i])); + GL(glRenderbufferStorageMultisampleEXT( + GL_RENDERBUFFER, multisamples, GL_DEPTH_COMPONENT24, width, height)); + GL(glBindRenderbuffer(GL_RENDERBUFFER, 0)); + + // Create the frame buffer. + // NOTE: glFramebufferTexture2DMultisampleEXT only works with GL_FRAMEBUFFER. + GL(glGenFramebuffers(1, &frameBuffer->FrameBuffers[i])); + GL(glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer->FrameBuffers[i])); + GL(glFramebufferTexture2DMultisampleEXT( + GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, + colorTexture, + 0, + multisamples)); + GL(glFramebufferRenderbuffer( + GL_FRAMEBUFFER, + GL_DEPTH_ATTACHMENT, + GL_RENDERBUFFER, + frameBuffer->DepthBuffers[i])); + GL(GLenum renderFramebufferStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER)); + GL(glBindFramebuffer(GL_FRAMEBUFFER, 0)); + if (renderFramebufferStatus != GL_FRAMEBUFFER_COMPLETE) { + ALOGE( + "Incomplete frame buffer object: %s", + GlFrameBufferStatusString(renderFramebufferStatus)); + return false; + } + } else { + return false; + } + } + + return true; +} + +void ovrFramebuffer_Destroy(ovrFramebuffer* frameBuffer) { + GL(glDeleteFramebuffers(frameBuffer->TextureSwapChainLength, frameBuffer->FrameBuffers)); + GL(glDeleteRenderbuffers(frameBuffer->TextureSwapChainLength, frameBuffer->DepthBuffers)); + OXR(xrDestroySwapchain(frameBuffer->ColorSwapChain.Handle)); + free(frameBuffer->ColorSwapChainImage); + + free(frameBuffer->DepthBuffers); + free(frameBuffer->FrameBuffers); + + ovrFramebuffer_Clear(frameBuffer); +} + +void ovrFramebuffer_SetCurrent(ovrFramebuffer* frameBuffer) { + GL(glBindFramebuffer( + GL_DRAW_FRAMEBUFFER, frameBuffer->FrameBuffers[frameBuffer->TextureSwapChainIndex])); +} + +void ovrFramebuffer_SetNone() { + GL(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0)); +} + +void ovrFramebuffer_Resolve(ovrFramebuffer* frameBuffer) { + // Discard the depth buffer, so the tiler won't need to write it back out to memory. + const GLenum depthAttachment[1] = {GL_DEPTH_ATTACHMENT}; + glInvalidateFramebuffer(GL_DRAW_FRAMEBUFFER, 1, depthAttachment); + + // We now let the resolve happen implicitly. +} + +void ovrFramebuffer_Acquire(ovrFramebuffer* frameBuffer) { + // Acquire the swapchain image + XrSwapchainImageAcquireInfo acquireInfo = {XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO, NULL}; + OXR(xrAcquireSwapchainImage( + frameBuffer->ColorSwapChain.Handle, &acquireInfo, &frameBuffer->TextureSwapChainIndex)); + + XrSwapchainImageWaitInfo waitInfo; + waitInfo.type = XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO; + waitInfo.next = NULL; + waitInfo.timeout = 1000000000; /* timeout in nanoseconds */ + XrResult res = xrWaitSwapchainImage(frameBuffer->ColorSwapChain.Handle, &waitInfo); + int i = 0; + while (res == XR_TIMEOUT_EXPIRED) { + res = xrWaitSwapchainImage(frameBuffer->ColorSwapChain.Handle, &waitInfo); + i++; + ALOGV( + " Retry xrWaitSwapchainImage %d times due to XR_TIMEOUT_EXPIRED (duration %f seconds)", + i, + waitInfo.timeout * (1E-9)); + } +} + +void ovrFramebuffer_Release(ovrFramebuffer* frameBuffer) { + XrSwapchainImageReleaseInfo releaseInfo = {XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO, NULL}; + OXR(xrReleaseSwapchainImage(frameBuffer->ColorSwapChain.Handle, &releaseInfo)); +} + + +/* +================================================================================ + +ovrRenderer + +================================================================================ +*/ + +void ovrRenderer_Clear(ovrRenderer* renderer) { + for (int eye = 0; eye < ovrMaxNumEyes; eye++) { + ovrFramebuffer_Clear(&renderer->FrameBuffer[eye]); + } +} + +void ovrRenderer_Create( + XrSession session, + ovrRenderer* renderer, + int suggestedEyeTextureWidth, + int suggestedEyeTextureHeight) { + // Create the frame buffers. + for (int eye = 0; eye < ovrMaxNumEyes; eye++) { + ovrFramebuffer_Create( + session, + &renderer->FrameBuffer[eye], + GL_SRGB8_ALPHA8, + suggestedEyeTextureWidth, + suggestedEyeTextureHeight, + NUM_MULTI_SAMPLES); + } +} + +void ovrRenderer_Destroy(ovrRenderer* renderer) { + for (int eye = 0; eye < ovrMaxNumEyes; eye++) { + ovrFramebuffer_Destroy(&renderer->FrameBuffer[eye]); + } +} + + + +/* +================================================================================ + +ovrMatrix4f + +================================================================================ +*/ + +ovrMatrix4f ovrMatrix4f_CreateFromQuaternion(const XrQuaternionf* q) { + const float ww = q->w * q->w; + const float xx = q->x * q->x; + const float yy = q->y * q->y; + const float zz = q->z * q->z; + + ovrMatrix4f out; + out.M[0][0] = ww + xx - yy - zz; + out.M[0][1] = 2 * (q->x * q->y - q->w * q->z); + out.M[0][2] = 2 * (q->x * q->z + q->w * q->y); + out.M[0][3] = 0; + + out.M[1][0] = 2 * (q->x * q->y + q->w * q->z); + out.M[1][1] = ww - xx + yy - zz; + out.M[1][2] = 2 * (q->y * q->z - q->w * q->x); + out.M[1][3] = 0; + + out.M[2][0] = 2 * (q->x * q->z - q->w * q->y); + out.M[2][1] = 2 * (q->y * q->z + q->w * q->x); + out.M[2][2] = ww - xx - yy + zz; + out.M[2][3] = 0; + + out.M[3][0] = 0; + out.M[3][1] = 0; + out.M[3][2] = 0; + out.M[3][3] = 1; + return out; +} + + +/// Use left-multiplication to accumulate transformations. +ovrMatrix4f ovrMatrix4f_Multiply(const ovrMatrix4f* a, const ovrMatrix4f* b) { + ovrMatrix4f out; + out.M[0][0] = a->M[0][0] * b->M[0][0] + a->M[0][1] * b->M[1][0] + a->M[0][2] * b->M[2][0] + + a->M[0][3] * b->M[3][0]; + out.M[1][0] = a->M[1][0] * b->M[0][0] + a->M[1][1] * b->M[1][0] + a->M[1][2] * b->M[2][0] + + a->M[1][3] * b->M[3][0]; + out.M[2][0] = a->M[2][0] * b->M[0][0] + a->M[2][1] * b->M[1][0] + a->M[2][2] * b->M[2][0] + + a->M[2][3] * b->M[3][0]; + out.M[3][0] = a->M[3][0] * b->M[0][0] + a->M[3][1] * b->M[1][0] + a->M[3][2] * b->M[2][0] + + a->M[3][3] * b->M[3][0]; + + out.M[0][1] = a->M[0][0] * b->M[0][1] + a->M[0][1] * b->M[1][1] + a->M[0][2] * b->M[2][1] + + a->M[0][3] * b->M[3][1]; + out.M[1][1] = a->M[1][0] * b->M[0][1] + a->M[1][1] * b->M[1][1] + a->M[1][2] * b->M[2][1] + + a->M[1][3] * b->M[3][1]; + out.M[2][1] = a->M[2][0] * b->M[0][1] + a->M[2][1] * b->M[1][1] + a->M[2][2] * b->M[2][1] + + a->M[2][3] * b->M[3][1]; + out.M[3][1] = a->M[3][0] * b->M[0][1] + a->M[3][1] * b->M[1][1] + a->M[3][2] * b->M[2][1] + + a->M[3][3] * b->M[3][1]; + + out.M[0][2] = a->M[0][0] * b->M[0][2] + a->M[0][1] * b->M[1][2] + a->M[0][2] * b->M[2][2] + + a->M[0][3] * b->M[3][2]; + out.M[1][2] = a->M[1][0] * b->M[0][2] + a->M[1][1] * b->M[1][2] + a->M[1][2] * b->M[2][2] + + a->M[1][3] * b->M[3][2]; + out.M[2][2] = a->M[2][0] * b->M[0][2] + a->M[2][1] * b->M[1][2] + a->M[2][2] * b->M[2][2] + + a->M[2][3] * b->M[3][2]; + out.M[3][2] = a->M[3][0] * b->M[0][2] + a->M[3][1] * b->M[1][2] + a->M[3][2] * b->M[2][2] + + a->M[3][3] * b->M[3][2]; + + out.M[0][3] = a->M[0][0] * b->M[0][3] + a->M[0][1] * b->M[1][3] + a->M[0][2] * b->M[2][3] + + a->M[0][3] * b->M[3][3]; + out.M[1][3] = a->M[1][0] * b->M[0][3] + a->M[1][1] * b->M[1][3] + a->M[1][2] * b->M[2][3] + + a->M[1][3] * b->M[3][3]; + out.M[2][3] = a->M[2][0] * b->M[0][3] + a->M[2][1] * b->M[1][3] + a->M[2][2] * b->M[2][3] + + a->M[2][3] * b->M[3][3]; + out.M[3][3] = a->M[3][0] * b->M[0][3] + a->M[3][1] * b->M[1][3] + a->M[3][2] * b->M[2][3] + + a->M[3][3] * b->M[3][3]; + return out; +} + +ovrMatrix4f ovrMatrix4f_CreateRotation(const float radiansX, const float radiansY, const float radiansZ) { + const float sinX = sinf(radiansX); + const float cosX = cosf(radiansX); + const ovrMatrix4f rotationX = { + {{1, 0, 0, 0}, {0, cosX, -sinX, 0}, {0, sinX, cosX, 0}, {0, 0, 0, 1}}}; + const float sinY = sinf(radiansY); + const float cosY = cosf(radiansY); + const ovrMatrix4f rotationY = { + {{cosY, 0, sinY, 0}, {0, 1, 0, 0}, {-sinY, 0, cosY, 0}, {0, 0, 0, 1}}}; + const float sinZ = sinf(radiansZ); + const float cosZ = cosf(radiansZ); + const ovrMatrix4f rotationZ = { + {{cosZ, -sinZ, 0, 0}, {sinZ, cosZ, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}}}; + const ovrMatrix4f rotationXY = ovrMatrix4f_Multiply(&rotationY, &rotationX); + return ovrMatrix4f_Multiply(&rotationZ, &rotationXY); +} + +XrVector4f XrVector4f_MultiplyMatrix4f(const ovrMatrix4f* a, const XrVector4f* v) { + XrVector4f out; + out.x = a->M[0][0] * v->x + a->M[0][1] * v->y + a->M[0][2] * v->z + a->M[0][3] * v->w; + out.y = a->M[1][0] * v->x + a->M[1][1] * v->y + a->M[1][2] * v->z + a->M[1][3] * v->w; + out.z = a->M[2][0] * v->x + a->M[2][1] * v->y + a->M[2][2] * v->z + a->M[2][3] * v->w; + out.w = a->M[3][0] * v->x + a->M[3][1] * v->y + a->M[3][2] * v->z + a->M[3][3] * v->w; + return out; +} + +#ifndef EPSILON +#define EPSILON 0.001f +#endif + +static XrVector3f normalizeVec(XrVector3f vec) { + //NOTE: leave w-component untouched + //@@const float EPSILON = 0.000001f; + float xxyyzz = vec.x*vec.x + vec.y*vec.y + vec.z*vec.z; + //@@if(xxyyzz < EPSILON) + //@@ return *this; // do nothing if it is zero vector + + //float invLength = invSqrt(xxyyzz); + XrVector3f result; + float invLength = 1.0f / sqrtf(xxyyzz); + result.x = vec.x * invLength; + result.y = vec.y * invLength; + result.z = vec.z * invLength; + return result; +} + +void NormalizeAngles(vec3_t angles) +{ + while (angles[0] >= 90) angles[0] -= 180; + while (angles[1] >= 180) angles[1] -= 360; + while (angles[2] >= 180) angles[2] -= 360; + while (angles[0] < -90) angles[0] += 180; + while (angles[1] < -180) angles[1] += 360; + while (angles[2] < -180) angles[2] += 360; +} + +void GetAnglesFromVectors(const XrVector3f forward, const XrVector3f right, const XrVector3f up, vec3_t angles) +{ + float sr, sp, sy, cr, cp, cy; + + sp = -forward.z; + + float cp_x_cy = forward.x; + float cp_x_sy = forward.y; + float cp_x_sr = -right.z; + float cp_x_cr = up.z; + + float yaw = atan2(cp_x_sy, cp_x_cy); + float roll = atan2(cp_x_sr, cp_x_cr); + + cy = cos(yaw); + sy = sin(yaw); + cr = cos(roll); + sr = sin(roll); + + if (fabs(cy) > EPSILON) + { + cp = cp_x_cy / cy; + } + else if (fabs(sy) > EPSILON) + { + cp = cp_x_sy / sy; + } + else if (fabs(sr) > EPSILON) + { + cp = cp_x_sr / sr; + } + else if (fabs(cr) > EPSILON) + { + cp = cp_x_cr / cr; + } + else + { + cp = cos(asin(sp)); + } + + float pitch = atan2(sp, cp); + + angles[0] = pitch / (M_PI*2.f / 360.f); + angles[1] = yaw / (M_PI*2.f / 360.f); + angles[2] = roll / (M_PI*2.f / 360.f); + + NormalizeAngles(angles); +} + + +void QuatToYawPitchRoll(XrQuaternionf q, vec3_t rotation, vec3_t out) { + + ovrMatrix4f mat = ovrMatrix4f_CreateFromQuaternion( &q ); + + if (rotation[0] != 0.0f || rotation[1] != 0.0f || rotation[2] != 0.0f) + { + ovrMatrix4f rot = ovrMatrix4f_CreateRotation(DEG2RAD(rotation[0]), DEG2RAD(rotation[1]), DEG2RAD(rotation[2])); + mat = ovrMatrix4f_Multiply(&mat, &rot); + } + + XrVector4f v1 = {0, 0, -1, 0}; + XrVector4f v2 = {1, 0, 0, 0}; + XrVector4f v3 = {0, 1, 0, 0}; + + XrVector4f forwardInVRSpace = XrVector4f_MultiplyMatrix4f(&mat, &v1); + XrVector4f rightInVRSpace = XrVector4f_MultiplyMatrix4f(&mat, &v2); + XrVector4f upInVRSpace = XrVector4f_MultiplyMatrix4f(&mat, &v3); + + XrVector3f forward = {-forwardInVRSpace.z, -forwardInVRSpace.x, forwardInVRSpace.y}; + XrVector3f right = {-rightInVRSpace.z, -rightInVRSpace.x, rightInVRSpace.y}; + XrVector3f up = {-upInVRSpace.z, -upInVRSpace.x, upInVRSpace.y}; + + XrVector3f forwardNormal = normalizeVec(forward); + XrVector3f rightNormal = normalizeVec(right); + XrVector3f upNormal = normalizeVec(up); + + GetAnglesFromVectors(forwardNormal, rightNormal, upNormal, out); +} + +/* +======================== +TBXR_Vibrate +======================== +*/ + +void TBXR_Vibrate( int duration, int chan, float intensity ); + +/* +================================================================================ + +ovrRenderThread + +================================================================================ +*/ + + + +void ovrApp_Clear(ovrApp* app) { + app->Focused = false; + app->Instance = XR_NULL_HANDLE; + app->Session = XR_NULL_HANDLE; + memset(&app->ViewportConfig, 0, sizeof(XrViewConfigurationProperties)); + memset(&app->ViewConfigurationView, 0, ovrMaxNumEyes * sizeof(XrViewConfigurationView)); + app->SystemId = XR_NULL_SYSTEM_ID; + app->HeadSpace = XR_NULL_HANDLE; + app->StageSpace = XR_NULL_HANDLE; + app->FakeStageSpace = XR_NULL_HANDLE; + app->CurrentSpace = XR_NULL_HANDLE; + app->SessionActive = false; + app->SupportedDisplayRefreshRates = NULL; + app->RequestedDisplayRefreshRateIndex = 0; + app->NumSupportedDisplayRefreshRates = 0; + app->pfnGetDisplayRefreshRate = NULL; + app->pfnRequestDisplayRefreshRate = NULL; + app->SwapInterval = 1; + memset(app->Layers, 0, sizeof(xrCompositorLayer_Union) * ovrMaxLayerCount); + app->LayerCount = 0; + app->MainThreadTid = 0; + app->RenderThreadTid = 0; + ovrEgl_Clear( &app->Egl ); + ovrRenderer_Clear(&app->Renderer); +} + + + +void ovrApp_HandleSessionStateChanges(ovrApp* app, XrSessionState state) { + if (state == XR_SESSION_STATE_READY) { + assert(app->SessionActive == false); + + XrSessionBeginInfo sessionBeginInfo; + memset(&sessionBeginInfo, 0, sizeof(sessionBeginInfo)); + sessionBeginInfo.type = XR_TYPE_SESSION_BEGIN_INFO; + sessionBeginInfo.next = NULL; + sessionBeginInfo.primaryViewConfigurationType = app->ViewportConfig.viewConfigurationType; + + XrResult result; + OXR(result = xrBeginSession(app->Session, &sessionBeginInfo)); + + app->SessionActive = (result == XR_SUCCESS); + + // Set session state once we have entered VR mode and have a valid session object. + if (app->SessionActive) + { + XrPerfSettingsLevelEXT cpuPerfLevel = XR_PERF_SETTINGS_LEVEL_BOOST_EXT; + XrPerfSettingsLevelEXT gpuPerfLevel = XR_PERF_SETTINGS_LEVEL_BOOST_EXT; + + PFN_xrPerfSettingsSetPerformanceLevelEXT pfnPerfSettingsSetPerformanceLevelEXT = NULL; + OXR(xrGetInstanceProcAddr( + app->Instance, + "xrPerfSettingsSetPerformanceLevelEXT", + (PFN_xrVoidFunction * )(&pfnPerfSettingsSetPerformanceLevelEXT))); + + OXR(pfnPerfSettingsSetPerformanceLevelEXT( + app->Session, XR_PERF_SETTINGS_DOMAIN_CPU_EXT, cpuPerfLevel)); + OXR(pfnPerfSettingsSetPerformanceLevelEXT( + app->Session, XR_PERF_SETTINGS_DOMAIN_GPU_EXT, gpuPerfLevel)); + +#ifdef META_QUEST + PFN_xrSetAndroidApplicationThreadKHR pfnSetAndroidApplicationThreadKHR = NULL; + OXR(xrGetInstanceProcAddr( + app->Instance, + "xrSetAndroidApplicationThreadKHR", + (PFN_xrVoidFunction * )(&pfnSetAndroidApplicationThreadKHR))); + + OXR(pfnSetAndroidApplicationThreadKHR( + app->Session, XR_ANDROID_THREAD_TYPE_APPLICATION_MAIN_KHR, app->MainThreadTid)); + OXR(pfnSetAndroidApplicationThreadKHR( + app->Session, XR_ANDROID_THREAD_TYPE_RENDERER_MAIN_KHR, app->RenderThreadTid)); +#endif + } + } else if (state == XR_SESSION_STATE_STOPPING) { + assert(app->SessionActive); + + OXR(xrEndSession(app->Session)); + app->SessionActive = false; + } +} + +GLboolean ovrApp_HandleXrEvents(ovrApp* app) { + XrEventDataBuffer eventDataBuffer = {}; + GLboolean recenter = GL_FALSE; + + // Poll for events + for (;;) { + XrEventDataBaseHeader* baseEventHeader = (XrEventDataBaseHeader*)(&eventDataBuffer); + baseEventHeader->type = XR_TYPE_EVENT_DATA_BUFFER; + baseEventHeader->next = NULL; + XrResult r; + OXR(r = xrPollEvent(app->Instance, &eventDataBuffer)); + if (r != XR_SUCCESS) { + break; + } + + switch (baseEventHeader->type) { + case XR_TYPE_EVENT_DATA_EVENTS_LOST: + ALOGV("xrPollEvent: received XR_TYPE_EVENT_DATA_EVENTS_LOST event"); + break; + case XR_TYPE_EVENT_DATA_INSTANCE_LOSS_PENDING: { + const XrEventDataInstanceLossPending* instance_loss_pending_event = + (XrEventDataInstanceLossPending*)(baseEventHeader); + ALOGV( + "xrPollEvent: received XR_TYPE_EVENT_DATA_INSTANCE_LOSS_PENDING event: time %f", + FromXrTime(instance_loss_pending_event->lossTime)); + } break; + case XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED: + ALOGV("xrPollEvent: received XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED event"); + break; + case XR_TYPE_EVENT_DATA_PERF_SETTINGS_EXT: { + const XrEventDataPerfSettingsEXT* perf_settings_event = + (XrEventDataPerfSettingsEXT*)(baseEventHeader); + ALOGV( + "xrPollEvent: received XR_TYPE_EVENT_DATA_PERF_SETTINGS_EXT event: type %d subdomain %d : level %d -> level %d", + perf_settings_event->type, + perf_settings_event->subDomain, + perf_settings_event->fromLevel, + perf_settings_event->toLevel); + } break; +#ifdef META_QUEST + case XR_TYPE_EVENT_DATA_DISPLAY_REFRESH_RATE_CHANGED_FB: { + const XrEventDataDisplayRefreshRateChangedFB* refresh_rate_changed_event = + (XrEventDataDisplayRefreshRateChangedFB*)(baseEventHeader); + ALOGV( + "xrPollEvent: received XR_TYPE_EVENT_DATA_DISPLAY_REFRESH_RATE_CHANGED_FB event: fromRate %f -> toRate %f", + refresh_rate_changed_event->fromDisplayRefreshRate, + refresh_rate_changed_event->toDisplayRefreshRate); + } break; +#endif + case XR_TYPE_EVENT_DATA_REFERENCE_SPACE_CHANGE_PENDING: { + XrEventDataReferenceSpaceChangePending* ref_space_change_event = + (XrEventDataReferenceSpaceChangePending*)(baseEventHeader); + ALOGV( + "xrPollEvent: received XR_TYPE_EVENT_DATA_REFERENCE_SPACE_CHANGE_PENDING event: changed space: %d for session %p at time %f", + ref_space_change_event->referenceSpaceType, + (void*)ref_space_change_event->session, + FromXrTime(ref_space_change_event->changeTime)); + recenter = GL_TRUE; + } break; + case XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED: { + const XrEventDataSessionStateChanged* session_state_changed_event = + (XrEventDataSessionStateChanged*)(baseEventHeader); + ALOGV( + "xrPollEvent: received XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED: %d for session %p at time %f", + session_state_changed_event->state, + (void*)session_state_changed_event->session, + FromXrTime(session_state_changed_event->time)); + + switch (session_state_changed_event->state) { + case XR_SESSION_STATE_FOCUSED: + app->Focused = true; + break; + case XR_SESSION_STATE_VISIBLE: + app->Focused = false; + break; + case XR_SESSION_STATE_READY: + case XR_SESSION_STATE_STOPPING: + ovrApp_HandleSessionStateChanges(app, session_state_changed_event->state); + break; + default: + break; + } + } break; + default: + ALOGV("xrPollEvent: Unknown event"); + break; + } + } + return recenter; +} + + + +void ovrAppThread_Create( ovrAppThread * appThread, JNIEnv * env, jobject activityObject, jclass activityClass ) +{ + env->GetJavaVM( &appThread->JavaVm ); + appThread->ActivityObject = env->NewGlobalRef( activityObject ); + appThread->ActivityClass = (jclass)env->NewGlobalRef( activityClass ); + appThread->Thread = 0; + appThread->NativeWindow = NULL; + surfaceMessageQueue_Create(&appThread->MessageQueue); + + const int createErr = pthread_create( &appThread->Thread, NULL, AppThreadFunction, appThread ); + if ( createErr != 0 ) + { + ALOGE( "pthread_create returned %i", createErr ); + } +} + +void ovrAppThread_Destroy( ovrAppThread * appThread, JNIEnv * env ) +{ + pthread_join( appThread->Thread, NULL ); + env->DeleteGlobalRef( appThread->ActivityObject ); + env->DeleteGlobalRef( appThread->ActivityClass ); + surfaceMessageQueue_Destroy(&appThread->MessageQueue); +} + + +/* +================================================================================ + +surfaceMessageQueue + +================================================================================ +*/ + + +void surfaceMessage_Init(srufaceMessage * message, const int id, const int wait ) +{ + message->Id = id; + message->Wait = (ovrMQWait)wait; + memset( message->Parms, 0, sizeof( message->Parms ) ); +} + +void surfaceMessage_SetPointerParm(srufaceMessage * message, int index, void * ptr ) { *(void **)&message->Parms[index] = ptr; } +void * surfaceMessage_GetPointerParm(srufaceMessage * message, int index ) { return *(void **)&message->Parms[index]; } + + +void surfaceMessageQueue_Create(surfaceMessageQueue * messageQueue ) +{ + messageQueue->Head = 0; + messageQueue->Tail = 0; + messageQueue->Wait = MQ_WAIT_NONE; + messageQueue->EnabledFlag = false; + messageQueue->PostedFlag = false; + messageQueue->ReceivedFlag = false; + messageQueue->ProcessedFlag = false; + + pthread_mutexattr_t attr; + pthread_mutexattr_init( &attr ); + pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_ERRORCHECK ); + pthread_mutex_init( &messageQueue->Mutex, &attr ); + pthread_mutexattr_destroy( &attr ); + pthread_cond_init( &messageQueue->PostedCondition, NULL ); + pthread_cond_init( &messageQueue->ReceivedCondition, NULL ); + pthread_cond_init( &messageQueue->ProcessedCondition, NULL ); +} + +void surfaceMessageQueue_Destroy(surfaceMessageQueue * messageQueue ) +{ + pthread_mutex_destroy( &messageQueue->Mutex ); + pthread_cond_destroy( &messageQueue->PostedCondition ); + pthread_cond_destroy( &messageQueue->ReceivedCondition ); + pthread_cond_destroy( &messageQueue->ProcessedCondition ); +} + +void surfaceMessageQueue_Enable(surfaceMessageQueue * messageQueue, const bool set ) +{ + messageQueue->EnabledFlag = set; +} + +void surfaceMessageQueue_PostMessage(surfaceMessageQueue * messageQueue, const srufaceMessage * message ) +{ + if ( !messageQueue->EnabledFlag ) + { + return; + } + while ( messageQueue->Tail - messageQueue->Head >= MAX_MESSAGES ) + { + usleep( 1000 ); + } + pthread_mutex_lock( &messageQueue->Mutex ); + messageQueue->Messages[messageQueue->Tail & ( MAX_MESSAGES - 1 )] = *message; + messageQueue->Tail++; + messageQueue->PostedFlag = true; + pthread_cond_broadcast( &messageQueue->PostedCondition ); + if ( message->Wait == MQ_WAIT_RECEIVED ) + { + while ( !messageQueue->ReceivedFlag ) + { + pthread_cond_wait( &messageQueue->ReceivedCondition, &messageQueue->Mutex ); + } + messageQueue->ReceivedFlag = false; + } + else if ( message->Wait == MQ_WAIT_PROCESSED ) + { + while ( !messageQueue->ProcessedFlag ) + { + pthread_cond_wait( &messageQueue->ProcessedCondition, &messageQueue->Mutex ); + } + messageQueue->ProcessedFlag = false; + } + pthread_mutex_unlock( &messageQueue->Mutex ); +} + +static void ovrMessageQueue_SleepUntilMessage(surfaceMessageQueue * messageQueue ) +{ + if ( messageQueue->Wait == MQ_WAIT_PROCESSED ) + { + messageQueue->ProcessedFlag = true; + pthread_cond_broadcast( &messageQueue->ProcessedCondition ); + messageQueue->Wait = MQ_WAIT_NONE; + } + pthread_mutex_lock( &messageQueue->Mutex ); + if ( messageQueue->Tail > messageQueue->Head ) + { + pthread_mutex_unlock( &messageQueue->Mutex ); + return; + } + while ( !messageQueue->PostedFlag ) + { + pthread_cond_wait( &messageQueue->PostedCondition, &messageQueue->Mutex ); + } + messageQueue->PostedFlag = false; + pthread_mutex_unlock( &messageQueue->Mutex ); +} + +static bool surfaceMessageQueue_GetNextMessage(surfaceMessageQueue * messageQueue, srufaceMessage * message, bool waitForMessages ) +{ + if ( messageQueue->Wait == MQ_WAIT_PROCESSED ) + { + messageQueue->ProcessedFlag = true; + pthread_cond_broadcast( &messageQueue->ProcessedCondition ); + messageQueue->Wait = MQ_WAIT_NONE; + } + if ( waitForMessages ) + { + ovrMessageQueue_SleepUntilMessage( messageQueue ); + } + pthread_mutex_lock( &messageQueue->Mutex ); + if ( messageQueue->Tail <= messageQueue->Head ) + { + pthread_mutex_unlock( &messageQueue->Mutex ); + return false; + } + *message = messageQueue->Messages[messageQueue->Head & ( MAX_MESSAGES - 1 )]; + messageQueue->Head++; + pthread_mutex_unlock( &messageQueue->Mutex ); + if ( message->Wait == MQ_WAIT_RECEIVED ) + { + messageQueue->ReceivedFlag = true; + pthread_cond_broadcast( &messageQueue->ReceivedCondition ); + } + else if ( message->Wait == MQ_WAIT_PROCESSED ) + { + messageQueue->Wait = MQ_WAIT_PROCESSED; + } + return true; +} + +ovrAppThread * gAppThread = NULL; +ovrApp gAppState; +ovrJava java; +bool destroyed = false; + +void TBXR_GetScreenRes(int *width, int *height) +{ + *width = gAppState.Width; + *height = gAppState.Height; +} + +XrInstance TBXR_GetXrInstance() { + return gAppState.Instance; +} + +static void TBXR_ProcessMessageQueue() { + for ( ; ; ) + { + srufaceMessage message; + if ( !surfaceMessageQueue_GetNextMessage(&gAppThread->MessageQueue, &message, false) ) + { + break; + } + + switch ( message.Id ) + { + case MESSAGE_ON_CREATE: + { + break; + } + case MESSAGE_ON_START: + { + break; + } + case MESSAGE_ON_RESUME: + { + //If we get here, then user has opted not to quit + gAppState.Resumed = true; + break; + } + case MESSAGE_ON_PAUSE: + { + gAppState.Resumed = false; + break; + } + case MESSAGE_ON_STOP: + { + break; + } + case MESSAGE_ON_DESTROY: + { + gAppState.NativeWindow = NULL; + destroyed = true; + //shutdown = true; + break; + } + case MESSAGE_ON_SURFACE_CREATED: { gAppState.NativeWindow = (ANativeWindow *) surfaceMessage_GetPointerParm( + &message, 0); break; } + case MESSAGE_ON_SURFACE_DESTROYED: { gAppState.NativeWindow = NULL; break; } + } + } +} + +void ovrTrackedController_Clear(ovrTrackedController* controller) { + controller->Active = false; + controller->Pose = XrPosef_Identity(); +} + + +void TBXR_InitialiseResolution() +{ + // Enumerate the viewport configurations. + uint32_t viewportConfigTypeCount = 0; + OXR(xrEnumerateViewConfigurations( + gAppState.Instance, gAppState.SystemId, 0, &viewportConfigTypeCount, NULL)); + + XrViewConfigurationType* viewportConfigurationTypes = + (XrViewConfigurationType*)malloc(viewportConfigTypeCount * sizeof(XrViewConfigurationType)); + + OXR(xrEnumerateViewConfigurations( + gAppState.Instance, + gAppState.SystemId, + viewportConfigTypeCount, + &viewportConfigTypeCount, + viewportConfigurationTypes)); + + ALOGV("Available Viewport Configuration Types: %d", viewportConfigTypeCount); + + for (uint32_t i = 0; i < viewportConfigTypeCount; i++) { + const XrViewConfigurationType viewportConfigType = viewportConfigurationTypes[i]; + + ALOGV( + "Viewport configuration type %d : %s", + viewportConfigType, + viewportConfigType == XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO ? "Selected" : ""); + + XrViewConfigurationProperties viewportConfig; + viewportConfig.type = XR_TYPE_VIEW_CONFIGURATION_PROPERTIES; + OXR(xrGetViewConfigurationProperties( + gAppState.Instance, gAppState.SystemId, viewportConfigType, &viewportConfig)); + ALOGV( + "FovMutable=%s ConfigurationType %d", + viewportConfig.fovMutable ? "true" : "false", + viewportConfig.viewConfigurationType); + + uint32_t viewCount; + OXR(xrEnumerateViewConfigurationViews( + gAppState.Instance, gAppState.SystemId, viewportConfigType, 0, &viewCount, NULL)); + + if (viewCount > 0) { + XrViewConfigurationView* elements = + (XrViewConfigurationView*)malloc(viewCount * sizeof(XrViewConfigurationView)); + + for (uint32_t e = 0; e < viewCount; e++) { + elements[e].type = XR_TYPE_VIEW_CONFIGURATION_VIEW; + elements[e].next = NULL; + } + + OXR(xrEnumerateViewConfigurationViews( + gAppState.Instance, + gAppState.SystemId, + viewportConfigType, + viewCount, + &viewCount, + elements)); + + // Cache the view config properties for the selected config type. + if (viewportConfigType == XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO) { + assert(viewCount == ovrMaxNumEyes); + for (uint32_t e = 0; e < viewCount; e++) { + gAppState.ViewConfigurationView[e] = elements[e]; + } + } + + free(elements); + } else { + ALOGE("Empty viewport configuration type: %d", viewCount); + } + } + + free(viewportConfigurationTypes); + + //Shortcut to width and height + gAppState.Width = gAppState.ViewConfigurationView[0].recommendedImageRectWidth * SS_MULTIPLIER; + gAppState.Height = gAppState.ViewConfigurationView[0].recommendedImageRectHeight * SS_MULTIPLIER; +} + +void TBXR_EnterVR( ) { + + if (gAppState.Session) { + //Com_Printf("TBXR_EnterVR called with existing session"); + return; + } + + // Create the OpenXR Session. + XrGraphicsBindingOpenGLESAndroidKHR graphicsBindingAndroidGLES = {}; + graphicsBindingAndroidGLES.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_ES_ANDROID_KHR; + graphicsBindingAndroidGLES.next = NULL; + graphicsBindingAndroidGLES.display = eglGetCurrentDisplay(); + graphicsBindingAndroidGLES.config = eglGetCurrentSurface(EGL_DRAW); + graphicsBindingAndroidGLES.context = eglGetCurrentContext(); + + XrSessionCreateInfo sessionCreateInfo = {}; + memset(&sessionCreateInfo, 0, sizeof(sessionCreateInfo)); + sessionCreateInfo.type = XR_TYPE_SESSION_CREATE_INFO; + sessionCreateInfo.next = &graphicsBindingAndroidGLES; + sessionCreateInfo.createFlags = 0; + sessionCreateInfo.systemId = gAppState.SystemId; + + XrResult initResult; + OXR(initResult = xrCreateSession(gAppState.Instance, &sessionCreateInfo, &gAppState.Session)); + if (initResult != XR_SUCCESS) { + ALOGE("Failed to create XR session: %d.", initResult); + exit(1); + } + + // Create a space to the first path + XrReferenceSpaceCreateInfo spaceCreateInfo = {}; + spaceCreateInfo.type = XR_TYPE_REFERENCE_SPACE_CREATE_INFO; + spaceCreateInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_VIEW; + spaceCreateInfo.poseInReferenceSpace.orientation.w = 1.0f; + OXR(xrCreateReferenceSpace(gAppState.Session, &spaceCreateInfo, &gAppState.HeadSpace)); +} + +void TBXR_LeaveVR( ) { + if (gAppState.Session) { + OXR(xrDestroySpace(gAppState.HeadSpace)); + // StageSpace is optional. + if (gAppState.StageSpace != XR_NULL_HANDLE) { + OXR(xrDestroySpace(gAppState.StageSpace)); + } + OXR(xrDestroySpace(gAppState.FakeStageSpace)); + gAppState.CurrentSpace = XR_NULL_HANDLE; + OXR(xrDestroySession(gAppState.Session)); + gAppState.Session = NULL; + } + + ovrRenderer_Destroy( &gAppState.Renderer ); + ovrEgl_DestroyContext( &gAppState.Egl ); + java.Vm->DetachCurrentThread( ); +} + +void TBXR_InitRenderer( ) { + // Get the viewport configuration info for the chosen viewport configuration type. + gAppState.ViewportConfig.type = XR_TYPE_VIEW_CONFIGURATION_PROPERTIES; + + OXR(xrGetViewConfigurationProperties( + gAppState.Instance, gAppState.SystemId, XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO, &gAppState.ViewportConfig)); + + +#ifdef META_QUEST + XrSystemColorSpacePropertiesFB colorSpacePropertiesFB = {}; + colorSpacePropertiesFB.type = XR_TYPE_SYSTEM_COLOR_SPACE_PROPERTIES_FB; + + XrSystemProperties systemProperties = {}; + systemProperties.type = XR_TYPE_SYSTEM_PROPERTIES; + systemProperties.next = &colorSpacePropertiesFB; + OXR(xrGetSystemProperties(gAppState.Instance, gAppState.SystemId, &systemProperties)); + + // Enumerate the supported color space options for the system. + { + PFN_xrEnumerateColorSpacesFB pfnxrEnumerateColorSpacesFB = NULL; + OXR(xrGetInstanceProcAddr( + gAppState.Instance, + "xrEnumerateColorSpacesFB", + (PFN_xrVoidFunction*)(&pfnxrEnumerateColorSpacesFB))); + + uint32_t colorSpaceCountOutput = 0; + OXR(pfnxrEnumerateColorSpacesFB(gAppState.Session, 0, &colorSpaceCountOutput, NULL)); + + XrColorSpaceFB* colorSpaces = + (XrColorSpaceFB*)malloc(colorSpaceCountOutput * sizeof(XrColorSpaceFB)); + + OXR(pfnxrEnumerateColorSpacesFB( + gAppState.Session, colorSpaceCountOutput, &colorSpaceCountOutput, colorSpaces)); + ALOGV("Supported ColorSpaces:"); + + for (uint32_t i = 0; i < colorSpaceCountOutput; i++) { + ALOGV("%d:%d", i, colorSpaces[i]); + } + + const XrColorSpaceFB requestColorSpace = XR_COLOR_SPACE_REC2020_FB; + + PFN_xrSetColorSpaceFB pfnxrSetColorSpaceFB = NULL; + OXR(xrGetInstanceProcAddr( + gAppState.Instance, "xrSetColorSpaceFB", (PFN_xrVoidFunction*)(&pfnxrSetColorSpaceFB))); + + OXR(pfnxrSetColorSpaceFB(gAppState.Session, requestColorSpace)); + + free(colorSpaces); + } + + // Get the supported display refresh rates for the system. + { + PFN_xrEnumerateDisplayRefreshRatesFB pfnxrEnumerateDisplayRefreshRatesFB = NULL; + OXR(xrGetInstanceProcAddr( + gAppState.Instance, + "xrEnumerateDisplayRefreshRatesFB", + (PFN_xrVoidFunction*)(&pfnxrEnumerateDisplayRefreshRatesFB))); + + OXR(pfnxrEnumerateDisplayRefreshRatesFB( + gAppState.Session, 0, &gAppState.NumSupportedDisplayRefreshRates, NULL)); + + gAppState.SupportedDisplayRefreshRates = + (float*)malloc(gAppState.NumSupportedDisplayRefreshRates * sizeof(float)); + OXR(pfnxrEnumerateDisplayRefreshRatesFB( + gAppState.Session, + gAppState.NumSupportedDisplayRefreshRates, + &gAppState.NumSupportedDisplayRefreshRates, + gAppState.SupportedDisplayRefreshRates)); + ALOGV("Supported Refresh Rates:"); + for (uint32_t i = 0; i < gAppState.NumSupportedDisplayRefreshRates; i++) { + ALOGV("%d:%f", i, gAppState.SupportedDisplayRefreshRates[i]); + } + + OXR(xrGetInstanceProcAddr( + gAppState.Instance, + "xrGetDisplayRefreshRateFB", + (PFN_xrVoidFunction*)(&gAppState.pfnGetDisplayRefreshRate))); + + OXR(gAppState.pfnGetDisplayRefreshRate(gAppState.Session, &gAppState.currentDisplayRefreshRate)); + ALOGV("Current System Display Refresh Rate: %f", gAppState.currentDisplayRefreshRate); + + OXR(xrGetInstanceProcAddr( + gAppState.Instance, + "xrRequestDisplayRefreshRateFB", + (PFN_xrVoidFunction*)(&gAppState.pfnRequestDisplayRefreshRate))); + + // Test requesting the system default. + OXR(gAppState.pfnRequestDisplayRefreshRate(gAppState.Session, 0.0f)); + ALOGV("Requesting system default display refresh rate"); + } +#endif + + uint32_t numOutputSpaces = 0; + OXR(xrEnumerateReferenceSpaces(gAppState.Session, 0, &numOutputSpaces, NULL)); + + XrReferenceSpaceType* referenceSpaces = + (XrReferenceSpaceType*)malloc(numOutputSpaces * sizeof(XrReferenceSpaceType)); + + OXR(xrEnumerateReferenceSpaces( + gAppState.Session, numOutputSpaces, &numOutputSpaces, referenceSpaces)); + + for (uint32_t i = 0; i < numOutputSpaces; i++) { + if (referenceSpaces[i] == XR_REFERENCE_SPACE_TYPE_STAGE) { + stageSupported = GL_TRUE; + break; + } + } + + free(referenceSpaces); + + if (gAppState.CurrentSpace == XR_NULL_HANDLE) { + TBXR_Recenter(); + } + + gAppState.Projections = (XrView*)(malloc(ovrMaxNumEyes * sizeof(XrView))); + for (int eye = 0; eye < ovrMaxNumEyes; eye++) { + memset(&gAppState.Projections[eye], 0, sizeof(XrView)); + gAppState.Projections[eye].type = XR_TYPE_VIEW; + } + +#ifdef PICO_XR + xrGetInstanceProcAddr(gAppState.Instance,"xrSetConfigPICO", (PFN_xrVoidFunction*)(&pfnXrSetConfigPICO)); + xrGetInstanceProcAddr(gAppState.Instance,"xrGetConfigPICO", (PFN_xrVoidFunction*)(&pfnXrGetConfigPICO)); + + pfnXrSetConfigPICO(gAppState.Session,TRACKING_ORIGIN,"0"); + pfnXrSetConfigPICO(gAppState.Session,TRACKING_ORIGIN,"1"); + + pfnXrGetConfigPICO(gAppState.Session, GET_DISPLAY_RATE, &gAppState.currentDisplayRefreshRate); +#endif + + ovrRenderer_Create( + gAppState.Session, + &gAppState.Renderer, + gAppState.Width, + gAppState.Height); +} + +void VR_DestroyRenderer( ) +{ + ovrRenderer_Destroy(&gAppState.Renderer); + free(gAppState.Projections); +} + +void TBXR_InitialiseOpenXR() +{ + ovrApp_Clear(&gAppState); + gAppState.Java = java; + + ovrEgl_CreateContext(&gAppState.Egl, NULL); + EglInitExtensions(); + + PFN_xrInitializeLoaderKHR xrInitializeLoaderKHR; + xrGetInstanceProcAddr( + XR_NULL_HANDLE, "xrInitializeLoaderKHR", (PFN_xrVoidFunction*)&xrInitializeLoaderKHR); + if (xrInitializeLoaderKHR != NULL) { + XrLoaderInitInfoAndroidKHR loaderInitializeInfoAndroid; + memset(&loaderInitializeInfoAndroid, 0, sizeof(loaderInitializeInfoAndroid)); + loaderInitializeInfoAndroid.type = XR_TYPE_LOADER_INIT_INFO_ANDROID_KHR; + loaderInitializeInfoAndroid.next = NULL; + loaderInitializeInfoAndroid.applicationVM = java.Vm; + loaderInitializeInfoAndroid.applicationContext = java.ActivityObject; + xrInitializeLoaderKHR((XrLoaderInitInfoBaseHeaderKHR*)&loaderInitializeInfoAndroid); + } + + // Create the OpenXR instance. + XrApplicationInfo appInfo; + memset(&appInfo, 0, sizeof(appInfo)); + strcpy(appInfo.applicationName, "QuestZDoom"); + appInfo.applicationVersion = 0; + strcpy(appInfo.engineName, "QuestZDoom"); + appInfo.engineVersion = 0; + appInfo.apiVersion = XR_CURRENT_API_VERSION; + + XrInstanceCreateInfo instanceCreateInfo; + memset(&instanceCreateInfo, 0, sizeof(instanceCreateInfo)); + instanceCreateInfo.type = XR_TYPE_INSTANCE_CREATE_INFO; +#ifdef META_QUEST + instanceCreateInfo.next = NULL; +#endif +#ifdef PICO_XR + XrInstanceCreateInfoAndroidKHR instanceCreateInfoAndroid = {XR_TYPE_INSTANCE_CREATE_INFO_ANDROID_KHR}; + instanceCreateInfoAndroid.applicationVM = java.Vm; + instanceCreateInfoAndroid.applicationActivity = java.ActivityObject; + + instanceCreateInfo.next = (XrBaseInStructure*)&instanceCreateInfoAndroid; +#endif + instanceCreateInfo.createFlags = 0; + instanceCreateInfo.applicationInfo = appInfo; + instanceCreateInfo.enabledApiLayerCount = 0; + instanceCreateInfo.enabledApiLayerNames = NULL; + instanceCreateInfo.enabledExtensionCount = numRequiredExtensions; + instanceCreateInfo.enabledExtensionNames = requiredExtensionNames; + + XrResult initResult; + OXR(initResult = xrCreateInstance(&instanceCreateInfo, &gAppState.Instance)); + if (initResult != XR_SUCCESS) { + ALOGE("Failed to create XR instance: %d.", initResult); + exit(1); + } + + XrInstanceProperties instanceInfo; + instanceInfo.type = XR_TYPE_INSTANCE_PROPERTIES; + instanceInfo.next = NULL; + OXR(xrGetInstanceProperties(gAppState.Instance, &instanceInfo)); + ALOGV( + "Runtime %s: Version : %u.%u.%u", + instanceInfo.runtimeName, + XR_VERSION_MAJOR(instanceInfo.runtimeVersion), + XR_VERSION_MINOR(instanceInfo.runtimeVersion), + XR_VERSION_PATCH(instanceInfo.runtimeVersion)); + + XrSystemGetInfo systemGetInfo; + memset(&systemGetInfo, 0, sizeof(systemGetInfo)); + systemGetInfo.type = XR_TYPE_SYSTEM_GET_INFO; + systemGetInfo.next = NULL; + systemGetInfo.formFactor = XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY; + + OXR(initResult = xrGetSystem(gAppState.Instance, &systemGetInfo, &gAppState.SystemId)); + if (initResult != XR_SUCCESS) { + ALOGE("Failed to get system."); + exit(1); + } + + // Get the graphics requirements. + PFN_xrGetOpenGLESGraphicsRequirementsKHR pfnGetOpenGLESGraphicsRequirementsKHR = NULL; + OXR(xrGetInstanceProcAddr( + gAppState.Instance, + "xrGetOpenGLESGraphicsRequirementsKHR", + (PFN_xrVoidFunction * )(&pfnGetOpenGLESGraphicsRequirementsKHR))); + + XrGraphicsRequirementsOpenGLESKHR graphicsRequirements = {}; + graphicsRequirements.type = XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_ES_KHR; + OXR(pfnGetOpenGLESGraphicsRequirementsKHR(gAppState.Instance, gAppState.SystemId, + &graphicsRequirements)); + +#ifdef META_QUEST + XrSystemColorSpacePropertiesFB colorSpacePropertiesFB = {}; +colorSpacePropertiesFB.type = XR_TYPE_SYSTEM_COLOR_SPACE_PROPERTIES_FB; + +XrSystemProperties systemProperties = {}; +systemProperties.type = XR_TYPE_SYSTEM_PROPERTIES; +systemProperties.next = &colorSpacePropertiesFB; +OXR(xrGetSystemProperties(gAppState.Instance, gAppState.SystemId, &systemProperties)); + +ALOGV("System Color Space Properties: colorspace=%d", colorSpacePropertiesFB.colorSpace); +#endif + + TBXR_InitialiseResolution(); +} + +void TBXR_Recenter() { + + // Calculate recenter reference + XrReferenceSpaceCreateInfo spaceCreateInfo = {}; + spaceCreateInfo.type = XR_TYPE_REFERENCE_SPACE_CREATE_INFO; + spaceCreateInfo.poseInReferenceSpace.orientation.w = 1.0f; + if (gAppState.CurrentSpace != XR_NULL_HANDLE) { + vec3_t rotation = {0, 0, 0}; + XrSpaceLocation loc = {}; + loc.type = XR_TYPE_SPACE_LOCATION; + OXR(xrLocateSpace(gAppState.HeadSpace, gAppState.CurrentSpace, gAppState.PredictedDisplayTime, &loc)); + QuatToYawPitchRoll(loc.pose.orientation, rotation, hmdorientation); + + spaceCreateInfo.poseInReferenceSpace.orientation.x = 0; + spaceCreateInfo.poseInReferenceSpace.orientation.y = 0; + spaceCreateInfo.poseInReferenceSpace.orientation.z = 0; + spaceCreateInfo.poseInReferenceSpace.orientation.w = 1; + } + + // Delete previous space instances + if (gAppState.StageSpace != XR_NULL_HANDLE) { + OXR(xrDestroySpace(gAppState.StageSpace)); + } + if (gAppState.FakeStageSpace != XR_NULL_HANDLE) { + OXR(xrDestroySpace(gAppState.FakeStageSpace)); + } + + // 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; + spaceCreateInfo.poseInReferenceSpace.position.y = -1.6750f; + OXR(xrCreateReferenceSpace(gAppState.Session, &spaceCreateInfo, &gAppState.FakeStageSpace)); + ALOGV("Created fake stage space from local space with offset"); + gAppState.CurrentSpace = gAppState.FakeStageSpace; + + if (stageSupported) { + spaceCreateInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_STAGE; + spaceCreateInfo.poseInReferenceSpace.position.y = 0.0f; + OXR(xrCreateReferenceSpace(gAppState.Session, &spaceCreateInfo, &gAppState.StageSpace)); + ALOGV("Created stage space"); + gAppState.CurrentSpace = gAppState.StageSpace; + } +} + +void TBXR_UpdateStageBounds() { + XrExtent2Df stageBounds = {}; + + XrResult result; + OXR(result = xrGetReferenceSpaceBoundsRect( + gAppState.Session, XR_REFERENCE_SPACE_TYPE_STAGE, &stageBounds)); + if (result != XR_SUCCESS) { + ALOGE("Stage bounds query failed: using small defaults"); + stageBounds.width = 1.0f; + stageBounds.height = 1.0f; + + gAppState.CurrentSpace = gAppState.FakeStageSpace; + } +} + +void TBXR_WaitForSessionActive() +{//Now wait for the session to be ready + while (!gAppState.SessionActive) { + TBXR_ProcessMessageQueue(); + if (ovrApp_HandleXrEvents(&gAppState)) { + TBXR_Recenter(); + } + } +} + +static void TBXR_GetHMDOrientation() { + + if (gAppState.PredictedDisplayTime == 0) + { + return; + } + + // Get the HMD pose, predicted for the middle of the time period during which + // the new eye images will be displayed. The number of frames predicted ahead + // depends on the pipeline depth of the engine and the synthesis rate. + // The better the prediction, the less black will be pulled in at the edges. + XrSpaceLocation loc = {}; + loc.type = XR_TYPE_SPACE_LOCATION; + OXR(xrLocateSpace(gAppState.HeadSpace, gAppState.CurrentSpace, gAppState.PredictedDisplayTime, &loc)); + gAppState.xfStageFromHead = loc.pose; + + const XrQuaternionf quatHmd = gAppState.xfStageFromHead.orientation; + const XrVector3f positionHmd = gAppState.xfStageFromHead.position; + + vec3_t rotation = {0, 0, 0}; + vec3_t hmdorientation = {0, 0, 0}; + QuatToYawPitchRoll(quatHmd, rotation, hmdorientation); + VR_SetHMDPosition(positionHmd.x, positionHmd.y, positionHmd.z); + VR_SetHMDOrientation(hmdorientation[0], hmdorientation[1], hmdorientation[2]); +} + + +//All the stuff we want to do each frame +void TBXR_FrameSetup() +{ + if (gAppState.FrameSetup) + { + return; + } + + while (!destroyed) + { + TBXR_ProcessMessageQueue(); + + GLboolean stageBoundsDirty = GL_TRUE; + if (ovrApp_HandleXrEvents(&gAppState)) + { + TBXR_Recenter(); + } + + if (gAppState.SessionActive == GL_FALSE) + { + continue; + } + + if (stageBoundsDirty) + { + TBXR_UpdateStageBounds(); + stageBoundsDirty = GL_FALSE; + } + + break; + } + + if (destroyed) + { + TBXR_LeaveVR(); + //Ask Java to shut down + VR_Shutdown(); + + exit(0); // in case Java doesn't do the job + } + + + // NOTE: OpenXR does not use the concept of frame indices. Instead, + // XrWaitFrame returns the predicted display time. + XrFrameWaitInfo waitFrameInfo = {}; + waitFrameInfo.type = XR_TYPE_FRAME_WAIT_INFO; + waitFrameInfo.next = NULL; + + XrFrameState frameState = {}; + frameState.type = XR_TYPE_FRAME_STATE; + frameState.next = NULL; + + OXR(xrWaitFrame(gAppState.Session, &waitFrameInfo, &frameState)); + gAppState.PredictedDisplayTime = frameState.predictedDisplayTime; + if (!frameState.shouldRender) { + return; + } + + // Get the HMD pose, predicted for the middle of the time period during which + // the new eye images will be displayed. The number of frames predicted ahead + // depends on the pipeline depth of the engine and the synthesis rate. + // The better the prediction, the less black will be pulled in at the edges. + XrFrameBeginInfo beginFrameDesc = {}; + beginFrameDesc.type = XR_TYPE_FRAME_BEGIN_INFO; + beginFrameDesc.next = NULL; + OXR(xrBeginFrame(gAppState.Session, &beginFrameDesc)); + + //Game specific frame setup stuff called here + VR_FrameSetup(); + + //Get controller state here + TBXR_GetHMDOrientation(); + VR_HandleControllerInput(); + + TBXR_ProcessHaptics(); + + gAppState.FrameSetup = true; +} + +int TBXR_GetRefresh() +{ + return gAppState.currentDisplayRefreshRate; +} + +#define GL_FRAMEBUFFER_SRGB 0x8DB9 + +void TBXR_ClearFrameBuffer(int width, int height) +{ + glEnable( GL_SCISSOR_TEST ); + glViewport( 0, 0, width, height ); + + //Black + glClearColor( 0.0f, 0.0f, 0.0f, 1.0f ); + + glScissor( 0, 0, width, height ); + glClear( GL_COLOR_BUFFER_BIT ); + + glScissor( 0, 0, 0, 0 ); + glDisable( GL_SCISSOR_TEST ); + + //This is a bit of a hack, but we need to do this to correct for the fact that the engine uses linear RGB colorspace + //but openxr uses SRGB (or something, must admit I don't really understand, but adding this works to make it look good again) + glDisable( GL_FRAMEBUFFER_SRGB ); +} + +void TBXR_prepareEyeBuffer(int eye ) +{ + ovrFramebuffer* frameBuffer = &(gAppState.Renderer.FrameBuffer[eye]); + ovrFramebuffer_Acquire(frameBuffer); + ovrFramebuffer_SetCurrent(frameBuffer); + TBXR_ClearFrameBuffer(frameBuffer->ColorSwapChain.Width, frameBuffer->ColorSwapChain.Height); +} + +void TBXR_finishEyeBuffer(int eye ) +{ + ovrRenderer *renderer = &gAppState.Renderer; + + ovrFramebuffer *frameBuffer = &(renderer->FrameBuffer[eye]); + + // Clear the alpha channel, other way OpenXR would not transfer the framebuffer fully + glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE); + glClearColor(0.0, 0.0, 0.0, 1.0); + glClear(GL_COLOR_BUFFER_BIT); + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + + //Clear edge to prevent smearing + ovrFramebuffer_Resolve(frameBuffer); + ovrFramebuffer_Release(frameBuffer); + ovrFramebuffer_SetNone(); +} + +void TBXR_updateProjections() +{ + XrViewLocateInfo projectionInfo = {}; + projectionInfo.type = XR_TYPE_VIEW_LOCATE_INFO; + projectionInfo.viewConfigurationType = gAppState.ViewportConfig.viewConfigurationType; + projectionInfo.displayTime = gAppState.PredictedDisplayTime; + projectionInfo.space = gAppState.HeadSpace; + + XrViewState viewState = {XR_TYPE_VIEW_STATE, NULL}; + + uint32_t projectionCapacityInput = ovrMaxNumEyes; + uint32_t projectionCountOutput = projectionCapacityInput; + + OXR(xrLocateViews( + gAppState.Session, + &projectionInfo, + &viewState, + projectionCapacityInput, + &projectionCountOutput, + gAppState.Projections)); +} + +float fov_y = 0; +float QzDoom_GetFOV() +{ + return fov_y; +} + +void TBXR_submitFrame() +{ + if (gAppState.SessionActive == GL_FALSE) { + return; + } + + TBXR_updateProjections(); + + XrFovf fov = {}; + XrPosef viewTransform[2]; + + for (int eye = 0; eye < ovrMaxNumEyes; eye++) { + XrPosef xfHeadFromEye = gAppState.Projections[eye].pose; + XrPosef xfStageFromEye = XrPosef_Multiply(gAppState.xfStageFromHead, xfHeadFromEye); + viewTransform[eye] = XrPosef_Inverse(xfStageFromEye); + fov.angleLeft += gAppState.Projections[eye].fov.angleLeft / 2.0f; + fov.angleRight += gAppState.Projections[eye].fov.angleRight / 2.0f; + fov.angleUp += gAppState.Projections[eye].fov.angleUp / 2.0f; + fov.angleDown += gAppState.Projections[eye].fov.angleDown / 2.0f; + } + + fov_y = (fabs(fov.angleUp) + fabs(fov.angleDown)) * 180.0f / M_PI; + + + gAppState.LayerCount = 0; + memset(gAppState.Layers, 0, sizeof(xrCompositorLayer_Union) * ovrMaxLayerCount); + + XrCompositionLayerProjectionView projection_layer_elements[2] = {}; + if (!VR_UseScreenLayer()) { + XrCompositionLayerProjection projection_layer = {}; + projection_layer.type = XR_TYPE_COMPOSITION_LAYER_PROJECTION; + projection_layer.layerFlags = XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT; + projection_layer.layerFlags |= XR_COMPOSITION_LAYER_CORRECT_CHROMATIC_ABERRATION_BIT; + projection_layer.space = gAppState.CurrentSpace; + projection_layer.viewCount = ovrMaxNumEyes; + projection_layer.views = projection_layer_elements; + + for (int eye = 0; eye < ovrMaxNumEyes; eye++) { + ovrFramebuffer* frameBuffer = &gAppState.Renderer.FrameBuffer[eye]; + + memset(&projection_layer_elements[eye], 0, sizeof(XrCompositionLayerProjectionView)); + projection_layer_elements[eye].type = XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW; + projection_layer_elements[eye].pose = gAppState.xfStageFromHead; + projection_layer_elements[eye].fov = fov; + memset(&projection_layer_elements[eye].subImage, 0, sizeof(XrSwapchainSubImage)); + projection_layer_elements[eye].subImage.swapchain = + frameBuffer->ColorSwapChain.Handle; + projection_layer_elements[eye].subImage.imageRect.offset.x = 0; + projection_layer_elements[eye].subImage.imageRect.offset.y = 0; + projection_layer_elements[eye].subImage.imageRect.extent.width = + frameBuffer->ColorSwapChain.Width; + projection_layer_elements[eye].subImage.imageRect.extent.height = + frameBuffer->ColorSwapChain.Height; + projection_layer_elements[eye].subImage.imageArrayIndex = 0; + } + + gAppState.Layers[gAppState.LayerCount++].Projection = projection_layer; + } else { + + // Build the quad layer + XrCompositionLayerQuad quad_layer = {}; + int width = gAppState.Renderer.FrameBuffer[0].ColorSwapChain.Width; + int height = gAppState.Renderer.FrameBuffer[0].ColorSwapChain.Height; + quad_layer.type = XR_TYPE_COMPOSITION_LAYER_QUAD; + quad_layer.next = NULL; + quad_layer.layerFlags = XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT; + quad_layer.space = gAppState.CurrentSpace; + quad_layer.eyeVisibility = XR_EYE_VISIBILITY_BOTH; + memset(&quad_layer.subImage, 0, sizeof(XrSwapchainSubImage)); + quad_layer.subImage.swapchain = gAppState.Renderer.FrameBuffer[0].ColorSwapChain.Handle; + quad_layer.subImage.imageRect.offset.x = 0; + quad_layer.subImage.imageRect.offset.y = 0; + quad_layer.subImage.imageRect.extent.width = width; + quad_layer.subImage.imageRect.extent.height = height; + quad_layer.subImage.imageArrayIndex = 0; + const XrVector3f axis = {0.0f, 1.0f, 0.0f}; + XrVector3f pos = { + gAppState.xfStageFromHead.position.x - sin(DEG2RAD(playerYaw)) * VR_GetScreenLayerDistance(), + 1.0f, + gAppState.xfStageFromHead.position.z - cos(DEG2RAD(playerYaw)) * VR_GetScreenLayerDistance() + }; + quad_layer.pose.orientation = XrQuaternionf_CreateFromVectorAngle(axis, DEG2RAD(playerYaw)); + quad_layer.pose.position = pos; + XrExtent2Df size = {5.0f, 4.5f}; + quad_layer.size = size; + + gAppState.Layers[gAppState.LayerCount++].Quad = quad_layer; + } + + // Compose the layers for this frame. + const XrCompositionLayerBaseHeader* layers[ovrMaxLayerCount] = {}; + for (int i = 0; i < gAppState.LayerCount; i++) { + layers[i] = (const XrCompositionLayerBaseHeader*)&gAppState.Layers[i]; + } + + XrFrameEndInfo endFrameInfo = {}; + endFrameInfo.type = XR_TYPE_FRAME_END_INFO; + endFrameInfo.displayTime = gAppState.PredictedDisplayTime; + endFrameInfo.environmentBlendMode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE; + endFrameInfo.layerCount = gAppState.LayerCount; + endFrameInfo.layers = layers; + + OXR(xrEndFrame(gAppState.Session, &endFrameInfo)); + + gAppState.FrameSetup = false; +} diff --git a/Projects/Android/jni/QzDoom/TBXR_Common.h b/Projects/Android/jni/QzDoom/TBXR_Common.h new file mode 100644 index 0000000..4764ed3 --- /dev/null +++ b/Projects/Android/jni/QzDoom/TBXR_Common.h @@ -0,0 +1,332 @@ +#if !defined(tbxr_common_h) +#define tbxr_common_h + +//OpenXR +#define XR_USE_GRAPHICS_API_OPENGL_ES 1 +#define XR_USE_PLATFORM_ANDROID 1 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifndef NDEBUG +#define DEBUG 1 +#endif + +#define LOG_TAG "TBXR" + + +#define ALOGE(...) __android_log_print( ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__ ) + +#if DEBUG +#define ALOGV(...) __android_log_print( ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__ ) +#else +#define ALOGV(...) +#endif + +enum { ovrMaxLayerCount = 1 }; +enum { ovrMaxNumEyes = 2 }; + +typedef enum xrButton_ { + xrButton_A = 0x00000001, // Set for trigger pulled on the Gear VR and Go Controllers + xrButton_B = 0x00000002, + xrButton_RThumb = 0x00000004, + xrButton_RShoulder = 0x00000008, + xrButton_X = 0x00000100, + xrButton_Y = 0x00000200, + xrButton_LThumb = 0x00000400, + xrButton_LShoulder = 0x00000800, + xrButton_Up = 0x00010000, + xrButton_Down = 0x00020000, + xrButton_Left = 0x00040000, + xrButton_Right = 0x00080000, + xrButton_Enter = 0x00100000, + xrButton_Back = 0x00200000, + xrButton_GripTrigger = 0x04000000, + xrButton_Trigger = 0x20000000, + xrButton_Joystick = 0x80000000, + xrButton_EnumSize = 0x7fffffff +} xrButton; + +typedef struct { + uint32_t Buttons; + float IndexTrigger; + float GripTrigger; + XrVector2f Joystick; +} ovrInputStateTrackedRemote; + +typedef struct { + GLboolean Active; + XrPosef Pose; + XrSpaceVelocity Velocity; +} ovrTrackedController; + +typedef enum control_scheme { + RIGHT_HANDED_DEFAULT = 0, + LEFT_HANDED_DEFAULT = 10, + LEFT_HANDED_ALT = 11 +} control_scheme_t; + +typedef struct { + float M[4][4]; +} ovrMatrix4f; + + +typedef struct { + XrSwapchain Handle; + uint32_t Width; + uint32_t Height; +} ovrSwapChain; + +typedef struct { + int Width; + int Height; + int Multisamples; + uint32_t TextureSwapChainLength; + uint32_t TextureSwapChainIndex; + ovrSwapChain ColorSwapChain; + XrSwapchainImageOpenGLESKHR* ColorSwapChainImage; + GLuint* DepthBuffers; + GLuint* FrameBuffers; +} ovrFramebuffer; + +/* +================================================================================ + +ovrRenderer + +================================================================================ +*/ + +typedef struct +{ + ovrFramebuffer FrameBuffer[ovrMaxNumEyes]; +} ovrRenderer; + +/* +================================================================================ + +ovrApp + +================================================================================ +*/ + + +typedef enum +{ + MQ_WAIT_NONE, // don't wait + MQ_WAIT_RECEIVED, // wait until the consumer thread has received the message + MQ_WAIT_PROCESSED // wait until the consumer thread has processed the message +} ovrMQWait; + +#define MAX_MESSAGE_PARMS 8 +#define MAX_MESSAGES 1024 + +typedef struct +{ + int Id; + ovrMQWait Wait; + long long Parms[MAX_MESSAGE_PARMS]; +} srufaceMessage; + +typedef struct +{ + srufaceMessage Messages[MAX_MESSAGES]; + volatile int Head; // dequeue at the head + volatile int Tail; // enqueue at the tail + ovrMQWait Wait; + volatile bool EnabledFlag; + volatile bool PostedFlag; + volatile bool ReceivedFlag; + volatile bool ProcessedFlag; + pthread_mutex_t Mutex; + pthread_cond_t PostedCondition; + pthread_cond_t ReceivedCondition; + pthread_cond_t ProcessedCondition; +} surfaceMessageQueue; + +typedef struct +{ + JavaVM * JavaVm; + jobject ActivityObject; + jclass ActivityClass; + pthread_t Thread; + surfaceMessageQueue MessageQueue; + ANativeWindow * NativeWindow; +} ovrAppThread; + + +typedef union { + XrCompositionLayerProjection Projection; + XrCompositionLayerQuad Quad; +} xrCompositorLayer_Union; + +#define GL(func) func; + +// Forward declarations +XrInstance TBXR_GetXrInstance(); + +#if defined(DEBUG) +static void +OXR_CheckErrors(XrInstance instance, XrResult result, const char* function, bool failOnError) { + if (XR_FAILED(result)) { + char errorBuffer[XR_MAX_RESULT_STRING_SIZE]; + xrResultToString(instance, result, errorBuffer); + if (failOnError) { + ALOGE("OpenXR error: %s: %s\n", function, errorBuffer); + } else { + ALOGV("OpenXR error: %s: %s\n", function, errorBuffer); + } + } +} +#endif + +#if defined(DEBUG) +#define OXR(func) OXR_CheckErrors(TBXR_GetXrInstance(), func, #func, true); +#else +#define OXR(func) func; +#endif + +typedef struct { + EGLint MajorVersion; + EGLint MinorVersion; + EGLDisplay Display; + EGLConfig Config; + EGLSurface TinySurface; + EGLSurface MainSurface; + EGLContext Context; +} ovrEgl; + +/// Java details about an activity +typedef struct ovrJava_ { + JavaVM* Vm; //< Java Virtual Machine + JNIEnv* Env; //< Thread specific environment + jobject ActivityObject; //< Java activity object +} ovrJava; + +typedef struct +{ + ovrJava Java; + ovrEgl Egl; + ANativeWindow* NativeWindow; + bool Resumed; + bool Focused; + bool FrameSetup; + + float Width; + float Height; + + XrInstance Instance; + XrSession Session; + XrViewConfigurationProperties ViewportConfig; + XrViewConfigurationView ViewConfigurationView[ovrMaxNumEyes]; + XrSystemId SystemId; + XrSpace HeadSpace; + XrSpace StageSpace; + XrSpace FakeStageSpace; + XrSpace CurrentSpace; + GLboolean SessionActive; + XrPosef xfStageFromHead; + XrView* Projections; + XrMatrix4x4f ProjectionMatrices[2]; + + + float currentDisplayRefreshRate; + float* SupportedDisplayRefreshRates; + uint32_t RequestedDisplayRefreshRateIndex; + uint32_t NumSupportedDisplayRefreshRates; + PFN_xrGetDisplayRefreshRateFB pfnGetDisplayRefreshRate; + PFN_xrRequestDisplayRefreshRateFB pfnRequestDisplayRefreshRate; + + XrTime PredictedDisplayTime; + int SwapInterval; + int MainThreadTid; + int RenderThreadTid; + xrCompositorLayer_Union Layers[ovrMaxLayerCount]; + int LayerCount; + ovrRenderer Renderer; + ovrTrackedController TrackedController[2]; +} ovrApp; + + +enum +{ + MESSAGE_ON_CREATE, + MESSAGE_ON_START, + MESSAGE_ON_RESUME, + MESSAGE_ON_PAUSE, + MESSAGE_ON_STOP, + MESSAGE_ON_DESTROY, + MESSAGE_ON_SURFACE_CREATED, + MESSAGE_ON_SURFACE_DESTROYED +}; + +extern ovrAppThread * gAppThread; +extern ovrApp gAppState; +extern ovrJava java; + + +void ovrTrackedController_Clear(ovrTrackedController* controller); + +void * AppThreadFunction(void * parm ); + +void ovrAppThread_Create( ovrAppThread * appThread, JNIEnv * env, jobject activityObject, jclass activityClass ); +void ovrAppThread_Destroy( ovrAppThread * appThread, JNIEnv * env ); + +/* + * Surface Lifecycle Message Queue + */ +void surfaceMessage_Init(srufaceMessage * message, const int id, const int wait ); +void * surfaceMessage_GetPointerParm(srufaceMessage * message, int index ); +void surfaceMessage_SetPointerParm(srufaceMessage * message, int index, void * ptr ); + +void surfaceMessageQueue_Create(surfaceMessageQueue * messageQueue ); +void surfaceMessageQueue_Destroy(surfaceMessageQueue * messageQueue ); +void surfaceMessageQueue_Enable(surfaceMessageQueue * messageQueue, const bool set ); +void surfaceMessageQueue_PostMessage(surfaceMessageQueue * messageQueue, const srufaceMessage * message ); + +//Functions that need to be implemented by the game specific code +void VR_FrameSetup(); +bool VR_UseScreenLayer(); +float VR_GetScreenLayerDistance(); +bool VR_GetVRProjection(int eye, float zNear, float zFar, float* projection); +void VR_HandleControllerInput(); +void VR_SetHMDOrientation(float pitch, float yaw, float roll ); +void VR_SetHMDPosition(float x, float y, float z ); +void VR_HapticEvent(const char* event, int position, int intensity, float angle, float yHeight ); +void VR_HapticUpdateEvent(const char* event, int intensity, float angle ); +void VR_HapticEndFrame(); +void VR_HapticStopEvent(const char* event); +void VR_HapticEnable(); +void VR_HapticDisable(); +extern "C" void VR_Shutdown(); + + +//Reusable Team Beef OpenXR stuff (in TBXR_Common.cpp) +double TBXR_GetTimeInMilliSeconds(); +int TBXR_GetRefresh(); +void TBXR_Recenter(); +void TBXR_InitialiseOpenXR(); +void TBXR_WaitForSessionActive(); +void TBXR_InitRenderer(); +void TBXR_EnterVR(); +void TBXR_LeaveVR( ); +void TBXR_GetScreenRes(int *width, int *height); +void TBXR_InitActions( void ); +void TBXR_Vibrate(int duration, int channel, float intensity ); +void TBXR_ProcessHaptics(); +void TBXR_FrameSetup(); +void TBXR_updateProjections(); +void TBXR_UpdateControllers( ); +void TBXR_prepareEyeBuffer(int eye ); +void TBXR_finishEyeBuffer(int eye ); +void TBXR_submitFrame(); + +#endif //vrcommon_h \ No newline at end of file diff --git a/Projects/Android/jni/QzDoom/VrCommon.h b/Projects/Android/jni/QzDoom/VrCommon.h index b4eae6e..cf746e5 100644 --- a/Projects/Android/jni/QzDoom/VrCommon.h +++ b/Projects/Android/jni/QzDoom/VrCommon.h @@ -1,13 +1,22 @@ #if !defined(vrcommon_h) #define vrcommon_h -#ifdef __cplusplus -extern "C" -{ -#endif +#include "TBXR_Common.h" + +#include "c_cvars.h" + +EXTERN_CVAR(Int, vr_control_scheme) +EXTERN_CVAR(Bool, vr_move_use_offhand) +EXTERN_CVAR(Float, vr_weaponRotate); +EXTERN_CVAR(Float, vr_snapTurn); +EXTERN_CVAR(Float, vr_ipd); +EXTERN_CVAR(Float, vr_weaponScale); +EXTERN_CVAR(Bool, vr_teleport); +EXTERN_CVAR(Bool, vr_switch_sticks); +EXTERN_CVAR(Bool, vr_secondary_button_mappings); +EXTERN_CVAR(Bool, vr_two_handed_weapons); +EXTERN_CVAR(Bool, vr_crouch_use_button); -//#include -#include #include @@ -50,16 +59,6 @@ extern vec3_t positionDeltaThisFrame; extern vec3_t weaponangles; extern vec3_t weaponoffset; -extern bool weaponStabilised; -extern float vr_weapon_pitchadjust; -extern bool vr_moveuseoffhand; -extern bool vr_switchsticks; -extern bool vr_secondarybuttonmappings; -extern bool vr_twohandedweapons; -extern float vr_snapturn_angle; -extern float vr_use_teleport; - - extern vec3_t offhandangles; extern vec3_t offhandoffset; @@ -71,41 +70,17 @@ extern bool trigger_teleport; extern bool shutdown; void shutdownVR(); -float radians(float deg); -float degrees(float rad); bool isMultiplayer(); -double GetTimeInMilliSeconds(); float length(float x, float y); float nonLinearFilter(float in); bool between(float min, float val, float max); void rotateAboutOrigin(float v1, float v2, float rotation, vec2_t out); -void QuatToYawPitchRoll(ovrQuatf q, vec3_t rotation, vec3_t out); void handleTrackedControllerButton(ovrInputStateTrackedRemote * trackedRemoteState, ovrInputStateTrackedRemote * prevTrackedRemoteState, uint32_t button, int key); +void QuatToYawPitchRoll(XrQuaternionf q, vec3_t rotation, vec3_t out); //Called from engine code -bool QzDoom_useScreenLayer(); -void QzDoom_GetScreenRes(uint32_t *width, uint32_t *height); -void QzDoom_Vibrate(float duration, int channel, float intensity ); -bool QzDoom_processMessageQueue(); -void QzDoom_FrameSetup(); void QzDoom_setUseScreenLayer(bool use); -void QzDoom_processHaptics(); -void QzDoom_getHMDOrientation(ovrTracking2 *tracking); -void QzDoom_getTrackedRemotesOrientation(int vr_control_scheme); -int QzDoom_SetRefreshRate(int refreshRate); +void jni_shutdown(); -void QzDoom_HapticEvent(const char* event, int position, int intensity, float angle, float yHeight ); -void QzDoom_HapticEnable(); -void QzDoom_HapticDisable(); - -void incrementFrameIndex(); - -void QzDoom_prepareEyeBuffer(int eye ); -void QzDoom_finishEyeBuffer(int eye ); -void QzDoom_submitFrame(ovrTracking2 *tracking); - -#ifdef __cplusplus -} // extern "C" -#endif #endif //vrcommon_h \ No newline at end of file diff --git a/Projects/Android/jni/QzDoom/VrCompositor.c b/Projects/Android/jni/QzDoom/VrCompositor.c deleted file mode 100644 index 382eaa5..0000000 --- a/Projects/Android/jni/QzDoom/VrCompositor.c +++ /dev/null @@ -1,595 +0,0 @@ -/************************************************************************************ - -Filename : VrCompositor.c - -*************************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include // for prctl( PR_SET_NAME ) -#include -#include // for AWINDOW_FLAG_KEEP_SCREEN_ON - -#include -#include -#include -#include - - - -#include -#include - -#include "VrCompositor.h" - - - -/* -================================================================================ - -renderState - -================================================================================ -*/ - - -void getCurrentRenderState( renderState * state) -{ - state->VertexBuffer = 0; - state->IndexBuffer = 0; - state->VertexArrayObject = 0; - state->Program = 0; - - glGetIntegerv(GL_ARRAY_BUFFER, &state->VertexBuffer ); - glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER, &state->IndexBuffer ); - glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &state->VertexArrayObject ); - glGetIntegerv(GL_CURRENT_PROGRAM, &state->Program ); -} - -void restoreRenderState( renderState * state ) -{ - GL( glUseProgram( state->Program ) ); - GL( glBindVertexArray( state->VertexArrayObject ) ); - GL( glBindBuffer( GL_ARRAY_BUFFER, state->VertexBuffer ) ); - GL( glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, state->IndexBuffer ) ); -} - - -/* -================================================================================ - -ovrGeometry - -================================================================================ -*/ - -enum VertexAttributeLocation -{ - VERTEX_ATTRIBUTE_LOCATION_POSITION, - VERTEX_ATTRIBUTE_LOCATION_COLOR, - VERTEX_ATTRIBUTE_LOCATION_UV, -}; - -typedef struct -{ - enum VertexAttributeLocation location; - const char * name; -} ovrVertexAttribute; - -static ovrVertexAttribute ProgramVertexAttributes[] = -{ - { VERTEX_ATTRIBUTE_LOCATION_POSITION, "vertexPosition" }, - { VERTEX_ATTRIBUTE_LOCATION_COLOR, "vertexColor" }, - { VERTEX_ATTRIBUTE_LOCATION_UV, "vertexUv" }, -}; - -static void ovrGeometry_Clear( ovrGeometry * geometry ) -{ - geometry->VertexBuffer = 0; - geometry->IndexBuffer = 0; - geometry->VertexArrayObject = 0; - geometry->VertexCount = 0; - geometry->IndexCount = 0; - for ( int i = 0; i < MAX_VERTEX_ATTRIB_POINTERS; i++ ) - { - memset( &geometry->VertexAttribs[i], 0, sizeof( geometry->VertexAttribs[i] ) ); - geometry->VertexAttribs[i].Index = -1; - } -} - -static void ovrGeometry_CreateGroundPlane( ovrGeometry * geometry ) -{ - typedef struct - { - float positions[4][4]; - unsigned char colors[4][4]; - } ovrCubeVertices; - - static const ovrCubeVertices cubeVertices = - { - // positions - { - { 4.5f, -1.2f, 4.5f, 1.0f }, - { 4.5f, -1.2f, -4.5f, 1.0f }, - { -4.5f, -1.2f, -4.5f, 1.0f }, - { -4.5f, -1.2f, 4.5f, 1.0f } - }, - // colors - { - { 255, 0, 0, 255 }, - { 0, 255, 0, 255 }, - { 0, 0, 255, 255 }, - { 255, 255, 0, 255 }, - }, - }; - - static const unsigned short cubeIndices[6] = - { - 0, 1, 2, - 0, 2, 3, - }; - - geometry->VertexCount = 4; - geometry->IndexCount = 6; - - geometry->VertexAttribs[0].Index = VERTEX_ATTRIBUTE_LOCATION_POSITION; - geometry->VertexAttribs[0].Size = 4; - geometry->VertexAttribs[0].Type = GL_FLOAT; - geometry->VertexAttribs[0].Normalized = false; - geometry->VertexAttribs[0].Stride = sizeof( cubeVertices.positions[0] ); - geometry->VertexAttribs[0].Pointer = (const GLvoid *)offsetof( ovrCubeVertices, positions ); - - geometry->VertexAttribs[1].Index = VERTEX_ATTRIBUTE_LOCATION_COLOR; - geometry->VertexAttribs[1].Size = 4; - geometry->VertexAttribs[1].Type = GL_UNSIGNED_BYTE; - geometry->VertexAttribs[1].Normalized = true; - geometry->VertexAttribs[1].Stride = sizeof( cubeVertices.colors[0] ); - geometry->VertexAttribs[1].Pointer = (const GLvoid *)offsetof( ovrCubeVertices, colors ); - - renderState state; - getCurrentRenderState(&state); - - GL( glGenBuffers( 1, &geometry->VertexBuffer ) ); - GL( glBindBuffer( GL_ARRAY_BUFFER, geometry->VertexBuffer ) ); - GL( glBufferData( GL_ARRAY_BUFFER, sizeof( cubeVertices ), &cubeVertices, GL_STATIC_DRAW ) ); - - GL( glGenBuffers( 1, &geometry->IndexBuffer ) ); - GL( glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, geometry->IndexBuffer ) ); - GL( glBufferData( GL_ELEMENT_ARRAY_BUFFER, sizeof( cubeIndices ), cubeIndices, GL_STATIC_DRAW ) ); - - restoreRenderState(&state); -} - -static void ovrGeometry_Destroy( ovrGeometry * geometry ) -{ - GL( glDeleteBuffers( 1, &geometry->IndexBuffer ) ); - GL( glDeleteBuffers( 1, &geometry->VertexBuffer ) ); - - ovrGeometry_Clear( geometry ); -} - -static void ovrGeometry_CreateVAO( ovrGeometry * geometry ) -{ - renderState state; - getCurrentRenderState(&state); - - GL( glGenVertexArrays( 1, &geometry->VertexArrayObject ) ); - GL( glBindVertexArray( geometry->VertexArrayObject ) ); - - GL( glBindBuffer( GL_ARRAY_BUFFER, geometry->VertexBuffer ) ); - - for ( int i = 0; i < MAX_VERTEX_ATTRIB_POINTERS; i++ ) - { - if ( geometry->VertexAttribs[i].Index != -1 ) - { - GL( glEnableVertexAttribArray( geometry->VertexAttribs[i].Index ) ); - GL( glVertexAttribPointer( geometry->VertexAttribs[i].Index, geometry->VertexAttribs[i].Size, - geometry->VertexAttribs[i].Type, geometry->VertexAttribs[i].Normalized, - geometry->VertexAttribs[i].Stride, geometry->VertexAttribs[i].Pointer ) ); - } - } - - GL( glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, geometry->IndexBuffer ) ); - - restoreRenderState(&state); -} - -static void ovrGeometry_DestroyVAO( ovrGeometry * geometry ) -{ - GL( glDeleteVertexArrays( 1, &geometry->VertexArrayObject ) ); -} - -/* -================================================================================ - -ovrProgram - -================================================================================ -*/ - - -typedef struct -{ - enum - { - UNIFORM_VIEW_PROJ_MATRIX, - } index; - enum - { - UNIFORM_TYPE_VECTOR4, - UNIFORM_TYPE_MATRIX4X4, - UNIFORM_TYPE_INT, - UNIFORM_TYPE_BUFFER, - } type; - const char * name; -} ovrUniform; - -static ovrUniform ProgramUniforms[] = -{ - { UNIFORM_VIEW_PROJ_MATRIX, UNIFORM_TYPE_MATRIX4X4, "viewProjectionMatrix" }, -}; - -static void ovrProgram_Clear( ovrProgram * program ) -{ - program->Program = 0; - program->VertexShader = 0; - program->FragmentShader = 0; - memset( program->UniformLocation, 0, sizeof( program->UniformLocation ) ); - memset( program->UniformBinding, 0, sizeof( program->UniformBinding ) ); - memset( program->Textures, 0, sizeof( program->Textures ) ); -} - -static bool ovrProgram_Create( ovrProgram * program, const char * vertexSource, const char * fragmentSource ) -{ - GLint r; - - GL( program->VertexShader = glCreateShader( GL_VERTEX_SHADER ) ); - - GL( glShaderSource( program->VertexShader, 1, &vertexSource, 0 ) ); - GL( glCompileShader( program->VertexShader ) ); - GL( glGetShaderiv( program->VertexShader, GL_COMPILE_STATUS, &r ) ); - if ( r == GL_FALSE ) - { - GLchar msg[4096]; - GL( glGetShaderInfoLog( program->VertexShader, sizeof( msg ), 0, msg ) ); - ALOGE( "%s\n%s\n", vertexSource, msg ); - return false; - } - - GL( program->FragmentShader = glCreateShader( GL_FRAGMENT_SHADER ) ); - GL( glShaderSource( program->FragmentShader, 1, &fragmentSource, 0 ) ); - GL( glCompileShader( program->FragmentShader ) ); - GL( glGetShaderiv( program->FragmentShader, GL_COMPILE_STATUS, &r ) ); - if ( r == GL_FALSE ) - { - GLchar msg[4096]; - GL( glGetShaderInfoLog( program->FragmentShader, sizeof( msg ), 0, msg ) ); - ALOGE( "%s\n%s\n", fragmentSource, msg ); - return false; - } - - GL( program->Program = glCreateProgram() ); - GL( glAttachShader( program->Program, program->VertexShader ) ); - GL( glAttachShader( program->Program, program->FragmentShader ) ); - - // Bind the vertex attribute locations. - for ( int i = 0; i < sizeof( ProgramVertexAttributes ) / sizeof( ProgramVertexAttributes[0] ); i++ ) - { - GL( glBindAttribLocation( program->Program, ProgramVertexAttributes[i].location, ProgramVertexAttributes[i].name ) ); - } - - GL( glLinkProgram( program->Program ) ); - GL( glGetProgramiv( program->Program, GL_LINK_STATUS, &r ) ); - if ( r == GL_FALSE ) - { - GLchar msg[4096]; - GL( glGetProgramInfoLog( program->Program, sizeof( msg ), 0, msg ) ); - ALOGE( "Linking program failed: %s\n", msg ); - return false; - } - - int numBufferBindings = 0; - - // Get the uniform locations. - memset( program->UniformLocation, -1, sizeof( program->UniformLocation ) ); - for ( int i = 0; i < sizeof( ProgramUniforms ) / sizeof( ProgramUniforms[0] ); i++ ) - { - const int uniformIndex = ProgramUniforms[i].index; - if ( ProgramUniforms[i].type == UNIFORM_TYPE_BUFFER ) - { - GL( program->UniformLocation[uniformIndex] = glGetUniformBlockIndex( program->Program, ProgramUniforms[i].name ) ); - program->UniformBinding[uniformIndex] = numBufferBindings++; - GL( glUniformBlockBinding( program->Program, program->UniformLocation[uniformIndex], program->UniformBinding[uniformIndex] ) ); - } - else - { - GL( program->UniformLocation[uniformIndex] = glGetUniformLocation( program->Program, ProgramUniforms[i].name ) ); - program->UniformBinding[uniformIndex] = program->UniformLocation[uniformIndex]; - } - } - - renderState state; - getCurrentRenderState(&state); - - GL( glUseProgram( program->Program ) ); - - // Get the texture locations. - for ( int i = 0; i < MAX_PROGRAM_TEXTURES; i++ ) - { - char name[32]; - sprintf( name, "Texture%i", i ); - program->Textures[i] = glGetUniformLocation( program->Program, name ); - if ( program->Textures[i] != -1 ) - { - GL( glUniform1i( program->Textures[i], i ) ); - } - } - - restoreRenderState(&state); - - return true; -} - -static void ovrProgram_Destroy( ovrProgram * program ) -{ - if ( program->Program != 0 ) - { - GL( glDeleteProgram( program->Program ) ); - program->Program = 0; - } - if ( program->VertexShader != 0 ) - { - GL( glDeleteShader( program->VertexShader ) ); - program->VertexShader = 0; - } - if ( program->FragmentShader != 0 ) - { - GL( glDeleteShader( program->FragmentShader ) ); - program->FragmentShader = 0; - } -} - -static const char VERTEX_SHADER[] = - "#version 300 es\n" - "in vec3 vertexPosition;\n" - "in vec4 vertexColor;\n" - "uniform mat4 viewProjectionMatrix;\n" - "out vec4 fragmentColor;\n" - "void main()\n" - "{\n" - " gl_Position = viewProjectionMatrix * vec4( vertexPosition, 1.0 );\n" - " fragmentColor = vertexColor;\n" - "}\n"; - -static const char FRAGMENT_SHADER[] = - "#version 300 es\n" - "in lowp vec4 fragmentColor;\n" - "out lowp vec4 outColor;\n" - "void main()\n" - "{\n" - " outColor = fragmentColor;\n" - "}\n"; - -/* -================================================================================ - -ovrScene - -================================================================================ -*/ - -void ovrScene_Clear( ovrScene * scene ) -{ - scene->CreatedScene = false; - scene->CreatedVAOs = false; - ovrProgram_Clear( &scene->Program ); - ovrGeometry_Clear( &scene->GroundPlane ); - ovrRenderer_Clear( &scene->CylinderRenderer ); - - scene->CylinderWidth = 0; - scene->CylinderHeight = 0; -} - -bool ovrScene_IsCreated( ovrScene * scene ) -{ - return scene->CreatedScene; -} - -void ovrScene_CreateVAOs( ovrScene * scene ) -{ - if ( !scene->CreatedVAOs ) - { - ovrGeometry_CreateVAO( &scene->GroundPlane ); - scene->CreatedVAOs = true; - } -} - -void ovrScene_DestroyVAOs( ovrScene * scene ) -{ - if ( scene->CreatedVAOs ) - { - ovrGeometry_DestroyVAO( &scene->GroundPlane ); - scene->CreatedVAOs = false; - } -} - -void ovrScene_Create( int width, int height, ovrScene * scene, const ovrJava * java ) -{ - // Simple ground plane geometry. - { - ovrProgram_Create( &scene->Program, VERTEX_SHADER, FRAGMENT_SHADER ); - ovrGeometry_CreateGroundPlane( &scene->GroundPlane ); - ovrScene_CreateVAOs( scene ); - } - - // Create Cylinder renderer - { - scene->CylinderWidth = width; - scene->CylinderHeight = height; - - //Create cylinder renderer - ovrRenderer_Create( width, height, &scene->CylinderRenderer, java ); - } - - scene->CreatedScene = true; -} - -void ovrScene_Destroy( ovrScene * scene ) -{ - ovrScene_DestroyVAOs( scene ); - ovrProgram_Destroy( &scene->Program ); - ovrGeometry_Destroy( &scene->GroundPlane ); - ovrRenderer_Destroy( &scene->CylinderRenderer ); - - scene->CreatedScene = false; -} - -/* -================================================================================ - -ovrRenderer - -================================================================================ -*/ - -ovrLayerProjection2 ovrRenderer_RenderGroundPlaneToEyeBuffer( ovrRenderer * renderer, const ovrJava * java, - const ovrScene * scene, const ovrTracking2 * tracking ) -{ - ovrLayerProjection2 layer = vrapi_DefaultLayerProjection2(); - layer.HeadPose = tracking->HeadPose; - for ( int eye = 0; eye < VRAPI_FRAME_LAYER_EYE_MAX; eye++ ) - { - ovrFramebuffer * frameBuffer = &renderer->FrameBuffer[eye]; - layer.Textures[eye].ColorSwapChain = frameBuffer->ColorTextureSwapChain; - layer.Textures[eye].SwapChainIndex = frameBuffer->ProcessingTextureSwapChainIndex; - layer.Textures[eye].TexCoordsFromTanAngles = ovrMatrix4f_TanAngleMatrixFromProjection( &tracking->Eye[eye].ProjectionMatrix ); - } - layer.Header.Flags |= VRAPI_FRAME_LAYER_FLAG_CHROMATIC_ABERRATION_CORRECTION; - - for ( int eye = 0; eye < VRAPI_FRAME_LAYER_EYE_MAX; eye++ ) - { - ovrFramebuffer * frameBuffer = &renderer->FrameBuffer[eye]; - ovrFramebuffer_SetCurrent( frameBuffer ); - - renderState state; - getCurrentRenderState(&state); - - GL( glUseProgram( scene->Program.Program ) ); - - ovrMatrix4f viewProjMatrix = ovrMatrix4f_Multiply( &tracking->Eye[eye].ProjectionMatrix, &tracking->Eye[eye].ViewMatrix ); - glUniformMatrix4fv( scene->Program.UniformLocation[UNIFORM_VIEW_PROJ_MATRIX], 1, GL_TRUE, &viewProjMatrix.M[0][0] ); - - GL( glEnable( GL_SCISSOR_TEST ) ); - GL( glDepthMask( GL_TRUE ) ); - GL( glEnable( GL_DEPTH_TEST ) ); - GL( glDepthFunc( GL_LEQUAL ) ); - GL( glEnable( GL_CULL_FACE ) ); - GL( glCullFace( GL_BACK ) ); - GL( glViewport( 0, 0, frameBuffer->Width, frameBuffer->Height ) ); - GL( glScissor( 0, 0, frameBuffer->Width, frameBuffer->Height ) ); - GL( glClearColor( 0.0f, 0.0f, 0.0f, 1.0f ) ); - GL( glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ) ); - - //bind buffers - GL( glBindBuffer( GL_ARRAY_BUFFER, scene->GroundPlane.VertexBuffer ) ); - GL( glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, scene->GroundPlane.IndexBuffer ) ); - GL( glBindVertexArray( scene->GroundPlane.VertexArrayObject ) ); - - GL( glDrawElements( GL_TRIANGLES, scene->GroundPlane.IndexCount, GL_UNSIGNED_SHORT, NULL ) ); - - restoreRenderState(&state); - - // Explicitly clear the border texels to black when GL_CLAMP_TO_BORDER is not available. - ovrFramebuffer_ClearEdgeTexels( frameBuffer ); - - ovrFramebuffer_Resolve( frameBuffer ); - ovrFramebuffer_Advance( frameBuffer ); - } - - ovrFramebuffer_SetNone(); - - return layer; -} - -// Assumes landscape cylinder shape. -static ovrMatrix4f CylinderModelMatrix( const int texWidth, const int texHeight, - const ovrVector3f translation, - const float rotateYaw, - const float rotatePitch, - const float radius, - const float density ) -{ - const ovrMatrix4f scaleMatrix = ovrMatrix4f_CreateScale( radius, radius * (float)texHeight * VRAPI_PI / density, radius ); - const ovrMatrix4f transMatrix = ovrMatrix4f_CreateTranslation( translation.x, translation.y, translation.z ); - const ovrMatrix4f rotXMatrix = ovrMatrix4f_CreateRotation( rotateYaw, 0.0f, 0.0f ); - const ovrMatrix4f rotYMatrix = ovrMatrix4f_CreateRotation( 0.0f, rotatePitch, 0.0f ); - - const ovrMatrix4f m0 = ovrMatrix4f_Multiply( &transMatrix, &scaleMatrix ); - const ovrMatrix4f m1 = ovrMatrix4f_Multiply( &rotXMatrix, &m0 ); - const ovrMatrix4f m2 = ovrMatrix4f_Multiply( &rotYMatrix, &m1 ); - - return m2; -} - -ovrLayerCylinder2 BuildCylinderLayer( ovrRenderer * cylinderRenderer, - const int textureWidth, const int textureHeight, - const ovrTracking2 * tracking, float rotatePitch ) -{ - ovrLayerCylinder2 layer = vrapi_DefaultLayerCylinder2(); - - const float fadeLevel = 1.0f; - layer.Header.ColorScale.x = - layer.Header.ColorScale.y = - layer.Header.ColorScale.z = - layer.Header.ColorScale.w = fadeLevel; - layer.Header.SrcBlend = VRAPI_FRAME_LAYER_BLEND_SRC_ALPHA; - layer.Header.DstBlend = VRAPI_FRAME_LAYER_BLEND_ONE_MINUS_SRC_ALPHA; - - //layer.Header.Flags = VRAPI_FRAME_LAYER_FLAG_CLIP_TO_TEXTURE_RECT; - - layer.HeadPose = tracking->HeadPose; - - const float density = 4500.0f; - const float rotateYaw = 0.0f; - const float radius = 4.0f; - const ovrVector3f translation = { 0.0f, 0.0f, -2.5f }; - - ovrMatrix4f cylinderTransform = - CylinderModelMatrix( textureWidth, textureHeight, translation, - rotateYaw, rotatePitch, radius, density ); - - const float circScale = density * 0.5f / textureWidth; - const float circBias = -circScale * ( 0.5f * ( 1.0f - 1.0f / circScale ) ); - - for ( int eye = 0; eye < VRAPI_FRAME_LAYER_EYE_MAX; eye++ ) - { - ovrFramebuffer * cylinderFrameBuffer = &cylinderRenderer->FrameBuffer[eye]; - - ovrMatrix4f modelViewMatrix = ovrMatrix4f_Multiply( &tracking->Eye[eye].ViewMatrix, &cylinderTransform ); - layer.Textures[eye].TexCoordsFromTanAngles = ovrMatrix4f_Inverse( &modelViewMatrix ); - layer.Textures[eye].ColorSwapChain = cylinderFrameBuffer->ColorTextureSwapChain; - layer.Textures[eye].SwapChainIndex = cylinderFrameBuffer->ReadyTextureSwapChainIndex; - - // Texcoord scale and bias is just a representation of the aspect ratio. The positioning - // of the cylinder is handled entirely by the TexCoordsFromTanAngles matrix. - - const float texScaleX = circScale; - const float texBiasX = circBias; - const float texScaleY = -0.5f; - const float texBiasY = texScaleY * ( 0.5f * ( 1.0f - ( 1.0f / texScaleY ) ) ); - - layer.Textures[eye].TextureMatrix.M[0][0] = texScaleX; - layer.Textures[eye].TextureMatrix.M[0][2] = texBiasX; - layer.Textures[eye].TextureMatrix.M[1][1] = texScaleY; - layer.Textures[eye].TextureMatrix.M[1][2] = -texBiasY; - - layer.Textures[eye].TextureRect.width = 1.0f; - layer.Textures[eye].TextureRect.height = 1.0f; - } - - return layer; -} diff --git a/Projects/Android/jni/QzDoom/VrCompositor.h b/Projects/Android/jni/QzDoom/VrCompositor.h deleted file mode 100644 index b8d4402..0000000 --- a/Projects/Android/jni/QzDoom/VrCompositor.h +++ /dev/null @@ -1,217 +0,0 @@ -/************************************************************************************ - -Filename : VrCompositor.h - -*************************************************************************************/ - -#include "VrInput.h" - -#define CHECK_GL_ERRORS -#ifdef CHECK_GL_ERRORS - -static const char * GlErrorString( GLenum error ) -{ - switch ( error ) - { - case GL_NO_ERROR: return "GL_NO_ERROR"; - case GL_INVALID_ENUM: return "GL_INVALID_ENUM"; - case GL_INVALID_VALUE: return "GL_INVALID_VALUE"; - case GL_INVALID_OPERATION: return "GL_INVALID_OPERATION"; - case GL_INVALID_FRAMEBUFFER_OPERATION: return "GL_INVALID_FRAMEBUFFER_OPERATION"; - case GL_OUT_OF_MEMORY: return "GL_OUT_OF_MEMORY"; - default: return "unknown"; - } -} - -static void GLCheckErrors( int line ) -{ - for ( int i = 0; i < 10; i++ ) - { - const GLenum error = glGetError(); - if ( error == GL_NO_ERROR ) - { - break; - } - ALOGE( "GL error on line %d: %s", line, GlErrorString( error ) ); - } -} - -#define GL( func ) func; GLCheckErrors( __LINE__ ); - -#else // CHECK_GL_ERRORS - -#define GL( func ) func; - -#endif // CHECK_GL_ERRORS - - -/* -================================================================================ - -ovrFramebuffer - -================================================================================ -*/ - -typedef struct -{ - int Width; - int Height; - int Multisamples; - int TextureSwapChainLength; - int ProcessingTextureSwapChainIndex; - int ReadyTextureSwapChainIndex; - ovrTextureSwapChain * ColorTextureSwapChain; - GLuint * DepthBuffers; - GLuint * FrameBuffers; -} ovrFramebuffer; - -void ovrFramebuffer_SetCurrent( ovrFramebuffer * frameBuffer ); -void ovrFramebuffer_Destroy( ovrFramebuffer * frameBuffer ); -void ovrFramebuffer_SetNone(); -void ovrFramebuffer_Resolve( ovrFramebuffer * frameBuffer ); -void ovrFramebuffer_Advance( ovrFramebuffer * frameBuffer ); -void ovrFramebuffer_ClearEdgeTexels( ovrFramebuffer * frameBuffer ); - -/* -================================================================================ - -ovrRenderer - -================================================================================ -*/ - -typedef struct -{ - ovrFramebuffer FrameBuffer[VRAPI_FRAME_LAYER_EYE_MAX]; - int NumBuffers; -} ovrRenderer; - - -void ovrRenderer_Clear( ovrRenderer * renderer ); -void ovrRenderer_Create( int width, int height, ovrRenderer * renderer, const ovrJava * java ); -void ovrRenderer_Destroy( ovrRenderer * renderer ); - - -/* -================================================================================ - -renderState - -================================================================================ -*/ - -typedef struct -{ - GLint VertexBuffer; - GLint IndexBuffer; - GLint VertexArrayObject; - GLint Program; - GLint VertexShader; - GLint FragmentShader; -} renderState; - -void getCurrentRenderState( renderState * state); -void restoreRenderState( renderState * state ); - -/* -================================================================================ - -ovrGeometry - -================================================================================ -*/ - -typedef struct -{ - GLint Index; - GLint Size; - GLenum Type; - GLboolean Normalized; - GLsizei Stride; - const GLvoid * Pointer; -} ovrVertexAttribPointer; - -#define MAX_VERTEX_ATTRIB_POINTERS 3 - -typedef struct -{ - GLuint VertexBuffer; - GLuint IndexBuffer; - GLuint VertexArrayObject; - int VertexCount; - int IndexCount; - ovrVertexAttribPointer VertexAttribs[MAX_VERTEX_ATTRIB_POINTERS]; -} ovrGeometry; - -/* -================================================================================ - -ovrProgram - -================================================================================ -*/ - -#define MAX_PROGRAM_UNIFORMS 8 -#define MAX_PROGRAM_TEXTURES 8 - -typedef struct -{ - GLuint Program; - GLuint VertexShader; - GLuint FragmentShader; - // These will be -1 if not used by the program. - GLint UniformLocation[MAX_PROGRAM_UNIFORMS]; // ProgramUniforms[].name - GLint UniformBinding[MAX_PROGRAM_UNIFORMS]; // ProgramUniforms[].name - GLint Textures[MAX_PROGRAM_TEXTURES]; // Texture%i -} ovrProgram; - -/* -================================================================================ - -ovrScene - -================================================================================ -*/ - -typedef struct -{ - bool CreatedScene; - bool CreatedVAOs; - ovrProgram Program; - ovrGeometry GroundPlane; - - //Proper renderer for stereo rendering to the cylinder layer - ovrRenderer CylinderRenderer; - - int CylinderWidth; - int CylinderHeight; -} ovrScene; - -bool ovrScene_IsCreated( ovrScene * scene ); -void ovrScene_Clear( ovrScene * scene ); -void ovrScene_Create( int width, int height, ovrScene * scene, const ovrJava * java ); -void ovrScene_CreateVAOs( ovrScene * scene ); -void ovrScene_DestroyVAOs( ovrScene * scene ); -void ovrScene_Destroy( ovrScene * scene ); - -/* -================================================================================ - -ovrRenderer - -================================================================================ -*/ - -ovrLayerProjection2 ovrRenderer_RenderGroundPlaneToEyeBuffer( ovrRenderer * renderer, const ovrJava * java, - const ovrScene * scene, const ovrTracking2 * tracking ); - -ovrLayerProjection2 ovrRenderer_RenderToEyeBuffer( ovrRenderer * renderer, const ovrJava * java, - const ovrTracking2 * tracking ); - -ovrLayerCylinder2 BuildCylinderLayer( ovrRenderer * cylinderRenderer, - const int textureWidth, const int textureHeight, - const ovrTracking2 * tracking, float rotateYaw ); -; - - diff --git a/Projects/Android/jni/QzDoom/VrInput.h b/Projects/Android/jni/QzDoom/VrInput.h index c23826a..05edeec 100644 --- a/Projects/Android/jni/QzDoom/VrInput.h +++ b/Projects/Android/jni/QzDoom/VrInput.h @@ -5,12 +5,25 @@ #include "VrCommon.h" +extern ovrInputStateTrackedRemote leftTrackedRemoteState_old; +extern ovrInputStateTrackedRemote leftTrackedRemoteState_new; +extern ovrTrackedController leftRemoteTracking_new; -void acquireTrackedRemotesData(const ovrMobile *Ovr, double displayTime); +extern ovrInputStateTrackedRemote rightTrackedRemoteState_old; +extern ovrInputStateTrackedRemote rightTrackedRemoteState_new; +extern ovrTrackedController rightRemoteTracking_new; -void HandleInput_Default( int control_scheme, ovrInputStateGamepad *pFootTrackingNew, ovrInputStateGamepad *pFootTrackingOld, ovrInputStateTrackedRemote *pDominantTrackedRemoteNew, - ovrInputStateTrackedRemote *pDominantTrackedRemoteOld, ovrTracking* pDominantTracking, - ovrInputStateTrackedRemote *pOffTrackedRemoteNew, ovrInputStateTrackedRemote *pOffTrackedRemoteOld, ovrTracking* pOffTracking, +extern float remote_movementSideways; +extern float remote_movementForward; +extern float remote_movementUp; +extern float positional_movementSideways; +extern float positional_movementForward; +extern float snapTurn; + + +void HandleInput_Default( int control_scheme, ovrInputStateTrackedRemote *pDominantTrackedRemoteNew, + ovrInputStateTrackedRemote *pDominantTrackedRemoteOld, ovrTrackedController* pDominantTracking, + ovrInputStateTrackedRemote *pOffTrackedRemoteNew, ovrInputStateTrackedRemote *pOffTrackedRemoteOld, ovrTrackedController* pOffTracking, int domButton1, int domButton2, int offButton1, int offButton2 ); diff --git a/Projects/Android/jni/QzDoom/VrInputCommon.c b/Projects/Android/jni/QzDoom/VrInputCommon.c deleted file mode 100644 index c5db7e8..0000000 --- a/Projects/Android/jni/QzDoom/VrInputCommon.c +++ /dev/null @@ -1,160 +0,0 @@ -/************************************************************************************ - -Filename : VrInputRight.c -Content : Handles common controller input functionality -Created : September 2019 -Authors : Simon Brown - -*************************************************************************************/ - -#include -#include -#include -#include -#include - -#include "VrInput.h" - -ovrInputStateTrackedRemote leftTrackedRemoteState_old; -ovrInputStateTrackedRemote leftTrackedRemoteState_new; -ovrTracking leftRemoteTracking_new; - -ovrInputStateTrackedRemote rightTrackedRemoteState_old; -ovrInputStateTrackedRemote rightTrackedRemoteState_new; -ovrTracking rightRemoteTracking_new; - -ovrInputStateGamepad footTrackedRemoteState_old; -ovrInputStateGamepad footTrackedRemoteState_new; - - -ovrDeviceID controllerIDs[2]; - -float remote_movementSideways; -float remote_movementForward; -float remote_movementUp; -float positional_movementSideways; -float positional_movementForward; -float snapTurn; - -float cinemamodeYaw; -float cinemamodePitch; - -void Joy_GenerateButtonEvents(int oldbuttons, int newbuttons, int numbuttons, int base); - -void handleTrackedControllerButton(ovrInputStateTrackedRemote * trackedRemoteState, ovrInputStateTrackedRemote * prevTrackedRemoteState, uint32_t button, int key) -{ - Joy_GenerateButtonEvents(prevTrackedRemoteState->Buttons & button ? 1 : 0, trackedRemoteState->Buttons & button ? 1 : 0, 1, key); -} - -static void Matrix4x4_Transform (const matrix4x4 *in, const float v[3], float out[3]) -{ - out[0] = v[0] * (*in)[0][0] + v[1] * (*in)[0][1] + v[2] * (*in)[0][2] + (*in)[0][3]; - out[1] = v[0] * (*in)[1][0] + v[1] * (*in)[1][1] + v[2] * (*in)[1][2] + (*in)[1][3]; - out[2] = v[0] * (*in)[2][0] + v[1] * (*in)[2][1] + v[2] * (*in)[2][2] + (*in)[2][3]; -} - -void Matrix4x4_CreateFromEntity( matrix4x4 out, const vec3_t angles, const vec3_t origin, float scale ); - -void rotateAboutOrigin(float v1, float v2, float rotation, vec2_t out) -{ - vec3_t temp = {0.0f, 0.0f, 0.0f}; - temp[0] = v1; - temp[1] = v2; - - vec3_t v = {0.0f, 0.0f, 0.0f}; - matrix4x4 matrix; - vec3_t angles = {0.0f, rotation, 0.0f}; - vec3_t origin = {0.0f, 0.0f, 0.0f}; - Matrix4x4_CreateFromEntity(matrix, angles, origin, 1.0f); - Matrix4x4_Transform(&matrix, temp, v); - - out[0] = v[0]; - out[1] = v[1]; -} - -float length(float x, float y) -{ - return sqrtf(powf(x, 2.0f) + powf(y, 2.0f)); -} - -#define NLF_DEADZONE 0.1 -#define NLF_POWER 2.2 - -float nonLinearFilter(float in) -{ - float val = 0.0f; - if (in > NLF_DEADZONE) - { - val = in; - val -= NLF_DEADZONE; - val /= (1.0f - NLF_DEADZONE); - val = powf(val, NLF_POWER); - } - else if (in < -NLF_DEADZONE) - { - val = in; - val += NLF_DEADZONE; - val /= (1.0f - NLF_DEADZONE); - val = -powf(fabsf(val), NLF_POWER); - } - - return val; -} - -bool between(float min, float val, float max) -{ - return (min < val) && (val < max); -} - -void acquireTrackedRemotesData(const ovrMobile *Ovr, double displayTime) {//The amount of yaw changed by controller - for ( int i = 0; ; i++ ) { - ovrInputCapabilityHeader capsHeader; - ovrResult result = vrapi_EnumerateInputDevices(Ovr, i, &capsHeader); - if (result < 0) { - break; - } - - if (capsHeader.Type == ovrControllerType_Gamepad) { - - ovrInputGamepadCapabilities remoteCaps; - remoteCaps.Header = capsHeader; - if (vrapi_GetInputDeviceCapabilities(Ovr, &remoteCaps.Header) >= 0) { - // remote is connected - ovrInputStateGamepad remoteState; - remoteState.Header.ControllerType = ovrControllerType_Gamepad; - if ( vrapi_GetCurrentInputState( Ovr, capsHeader.DeviceID, &remoteState.Header ) >= 0 ) - { - // act on device state returned in remoteState - footTrackedRemoteState_new = remoteState; - } - } - } - else if (capsHeader.Type == ovrControllerType_TrackedRemote) { - ovrTracking remoteTracking; - ovrInputTrackedRemoteCapabilities remoteCaps; - remoteCaps.Header = capsHeader; - if ( vrapi_GetInputDeviceCapabilities( Ovr, &remoteCaps.Header ) >= 0 ) - { - // remote is connected - ovrInputStateTrackedRemote remoteState; - remoteState.Header.ControllerType = ovrControllerType_TrackedRemote; - - if(vrapi_GetCurrentInputState(Ovr, capsHeader.DeviceID, &remoteState.Header) >= 0) { - if (vrapi_GetInputTrackingState(Ovr, capsHeader.DeviceID, displayTime, - &remoteTracking) >= 0) { - // act on device state returned in remoteState - if (remoteCaps.ControllerCapabilities & ovrControllerCaps_RightHand) { - rightTrackedRemoteState_new = remoteState; - rightRemoteTracking_new = remoteTracking; - controllerIDs[1] = capsHeader.DeviceID; - } else { - leftTrackedRemoteState_new = remoteState; - leftRemoteTracking_new = remoteTracking; - controllerIDs[0] = capsHeader.DeviceID; - } - } - } - } - } - } -} diff --git a/Projects/Android/jni/QzDoom/VrInputCommon.cpp b/Projects/Android/jni/QzDoom/VrInputCommon.cpp new file mode 100644 index 0000000..7d37810 --- /dev/null +++ b/Projects/Android/jni/QzDoom/VrInputCommon.cpp @@ -0,0 +1,98 @@ +/************************************************************************************ + +Filename : VrInputRight.c +Content : Handles common controller input functionality +Created : September 2019 +Authors : Simon Brown + +*************************************************************************************/ + + +#include "VrInput.h" + +ovrInputStateTrackedRemote leftTrackedRemoteState_old; +ovrInputStateTrackedRemote leftTrackedRemoteState_new; +ovrTrackedController leftRemoteTracking_new; + +ovrInputStateTrackedRemote rightTrackedRemoteState_old; +ovrInputStateTrackedRemote rightTrackedRemoteState_new; +ovrTrackedController rightRemoteTracking_new; + + +float remote_movementSideways; +float remote_movementForward; +float remote_movementUp; +float positional_movementSideways; +float positional_movementForward; +float snapTurn; + +float cinemamodeYaw; +float cinemamodePitch; + +void Joy_GenerateButtonEvents(int oldbuttons, int newbuttons, int numbuttons, int base); + +void handleTrackedControllerButton(ovrInputStateTrackedRemote * trackedRemoteState, ovrInputStateTrackedRemote * prevTrackedRemoteState, uint32_t button, int key) +{ + Joy_GenerateButtonEvents(prevTrackedRemoteState->Buttons & button ? 1 : 0, trackedRemoteState->Buttons & button ? 1 : 0, 1, key); +} + +static void Matrix4x4_Transform (const matrix4x4 *in, const float v[3], float out[3]) +{ + out[0] = v[0] * (*in)[0][0] + v[1] * (*in)[0][1] + v[2] * (*in)[0][2] + (*in)[0][3]; + out[1] = v[0] * (*in)[1][0] + v[1] * (*in)[1][1] + v[2] * (*in)[1][2] + (*in)[1][3]; + out[2] = v[0] * (*in)[2][0] + v[1] * (*in)[2][1] + v[2] * (*in)[2][2] + (*in)[2][3]; +} + +void Matrix4x4_CreateFromEntity( matrix4x4 out, const vec3_t angles, const vec3_t origin, float scale ); + +void rotateAboutOrigin(float v1, float v2, float rotation, vec2_t out) +{ + vec3_t temp = {0.0f, 0.0f, 0.0f}; + temp[0] = v1; + temp[1] = v2; + + vec3_t v = {0.0f, 0.0f, 0.0f}; + matrix4x4 matrix; + vec3_t angles = {0.0f, rotation, 0.0f}; + vec3_t origin = {0.0f, 0.0f, 0.0f}; + Matrix4x4_CreateFromEntity(matrix, angles, origin, 1.0f); + Matrix4x4_Transform(&matrix, temp, v); + + out[0] = v[0]; + out[1] = v[1]; +} + +float length(float x, float y) +{ + return sqrtf(powf(x, 2.0f) + powf(y, 2.0f)); +} + +#define NLF_DEADZONE 0.1 +#define NLF_POWER 2.2 + +float nonLinearFilter(float in) +{ + float val = 0.0f; + if (in > NLF_DEADZONE) + { + val = in; + val -= NLF_DEADZONE; + val /= (1.0f - NLF_DEADZONE); + val = powf(val, NLF_POWER); + } + else if (in < -NLF_DEADZONE) + { + val = in; + val += NLF_DEADZONE; + val /= (1.0f - NLF_DEADZONE); + val = -powf(fabsf(val), NLF_POWER); + } + + return val; +} + +bool between(float min, float val, float max) +{ + return (min < val) && (val < max); +} + diff --git a/Projects/Android/jni/QzDoom/VrInputDefault.c b/Projects/Android/jni/QzDoom/VrInputDefault.cpp similarity index 74% rename from Projects/Android/jni/QzDoom/VrInputDefault.c rename to Projects/Android/jni/QzDoom/VrInputDefault.cpp index a25360b..c85bac2 100644 --- a/Projects/Android/jni/QzDoom/VrInputDefault.c +++ b/Projects/Android/jni/QzDoom/VrInputDefault.cpp @@ -7,11 +7,7 @@ Authors : Simon Brown *************************************************************************************/ -#include -#include -#include -#include -#include + #include #include "VrInput.h" @@ -20,17 +16,11 @@ Authors : Simon Brown extern ovrInputStateTrackedRemote leftTrackedRemoteState_old; extern ovrInputStateTrackedRemote leftTrackedRemoteState_new; -extern ovrTracking leftRemoteTracking_new; +extern ovrTrackedController leftRemoteTracking_new; extern ovrInputStateTrackedRemote rightTrackedRemoteState_old; extern ovrInputStateTrackedRemote rightTrackedRemoteState_new; -extern ovrTracking rightRemoteTracking_new; - -extern ovrInputStateGamepad footTrackedRemoteState_old; -extern ovrInputStateGamepad footTrackedRemoteState_new; - - -extern ovrDeviceID controllerIDs[2]; +extern ovrTrackedController rightRemoteTracking_new; extern float remote_movementSideways; extern float remote_movementForward; @@ -42,29 +32,30 @@ extern float snapTurn; extern float cinemamodeYaw; extern float cinemamodePitch; +extern bool weaponStabilised; + int getGameState(); int getMenuState(); void Joy_GenerateButtonEvents(int oldbuttons, int newbuttons, int numbuttons, int base); float getViewpointYaw(); -void HandleInput_Default( int control_scheme, ovrInputStateGamepad *pFootTrackingNew, ovrInputStateGamepad *pFootTrackingOld, - ovrInputStateTrackedRemote *pDominantTrackedRemoteNew, ovrInputStateTrackedRemote *pDominantTrackedRemoteOld, ovrTracking* pDominantTracking, - ovrInputStateTrackedRemote *pOffTrackedRemoteNew, ovrInputStateTrackedRemote *pOffTrackedRemoteOld, ovrTracking* pOffTracking, +void HandleInput_Default( int control_scheme, ovrInputStateTrackedRemote *pDominantTrackedRemoteNew, ovrInputStateTrackedRemote *pDominantTrackedRemoteOld, ovrTrackedController* pDominantTracking, + ovrInputStateTrackedRemote *pOffTrackedRemoteNew, ovrInputStateTrackedRemote *pOffTrackedRemoteOld, ovrTrackedController* pOffTracking, int domButton1, int domButton2, int offButton1, int offButton2 ) { //Menu button - invoke menu - handleTrackedControllerButton(&leftTrackedRemoteState_new, &leftTrackedRemoteState_old, ovrButton_Enter, KEY_ESCAPE); - handleTrackedControllerButton(&rightTrackedRemoteState_new, &rightTrackedRemoteState_old, ovrButton_Enter, KEY_ESCAPE); // For users who have switched the buttons + handleTrackedControllerButton(&leftTrackedRemoteState_new, &leftTrackedRemoteState_old, xrButton_Enter, KEY_ESCAPE); + handleTrackedControllerButton(&rightTrackedRemoteState_new, &rightTrackedRemoteState_old, xrButton_Enter, KEY_ESCAPE); // For users who have switched the buttons //Dominant Grip works like a shift key - bool dominantGripPushedOld = vr_secondarybuttonmappings ? - (pDominantTrackedRemoteOld->Buttons & ovrButton_GripTrigger) != 0 : false; - bool dominantGripPushedNew = vr_secondarybuttonmappings ? - (pDominantTrackedRemoteNew->Buttons & ovrButton_GripTrigger) != 0 : false; + bool dominantGripPushedOld = vr_secondary_button_mappings ? + (pDominantTrackedRemoteOld->Buttons & xrButton_GripTrigger) : false; + bool dominantGripPushedNew = vr_secondary_button_mappings ? + (pDominantTrackedRemoteNew->Buttons & xrButton_GripTrigger) : false; ovrInputStateTrackedRemote *pPrimaryTrackedRemoteNew, *pPrimaryTrackedRemoteOld, *pSecondaryTrackedRemoteNew, *pSecondaryTrackedRemoteOld; - if (vr_switchsticks) + if (vr_switch_sticks) { pPrimaryTrackedRemoteNew = pOffTrackedRemoteNew; pPrimaryTrackedRemoteOld = pOffTrackedRemoteOld; @@ -129,19 +120,19 @@ void HandleInput_Default( int control_scheme, ovrInputStateGamepad *pFootTrackin // Only do the following if we are definitely not in the menu if (getMenuState() == 0) { - float distance = sqrtf(powf(pOffTracking->HeadPose.Pose.Position.x - - pDominantTracking->HeadPose.Pose.Position.x, 2) + - powf(pOffTracking->HeadPose.Pose.Position.y - - pDominantTracking->HeadPose.Pose.Position.y, 2) + - powf(pOffTracking->HeadPose.Pose.Position.z - - pDominantTracking->HeadPose.Pose.Position.z, 2)); + float distance = sqrtf(powf(pOffTracking->Pose.position.x - + pDominantTracking->Pose.position.x, 2) + + powf(pOffTracking->Pose.position.y - + pDominantTracking->Pose.position.y, 2) + + powf(pOffTracking->Pose.position.z - + pDominantTracking->Pose.position.z, 2)); //Turn on weapon stabilisation? - if (vr_twohandedweapons && - (pOffTrackedRemoteNew->Buttons & ovrButton_GripTrigger) != - (pOffTrackedRemoteOld->Buttons & ovrButton_GripTrigger)) { + if (vr_two_handed_weapons && + (pOffTrackedRemoteNew->Buttons & xrButton_GripTrigger) != + (pOffTrackedRemoteOld->Buttons & xrButton_GripTrigger)) { - if (pOffTrackedRemoteNew->Buttons & ovrButton_GripTrigger) { + if (pOffTrackedRemoteNew->Buttons & xrButton_GripTrigger) { if (distance < 0.50f) { weaponStabilised = true; } @@ -153,9 +144,9 @@ void HandleInput_Default( int control_scheme, ovrInputStateGamepad *pFootTrackin //dominant hand stuff first { ///Weapon location relative to view - weaponoffset[0] = pDominantTracking->HeadPose.Pose.Position.x - hmdPosition[0]; - weaponoffset[1] = pDominantTracking->HeadPose.Pose.Position.y - hmdPosition[1]; - weaponoffset[2] = pDominantTracking->HeadPose.Pose.Position.z - hmdPosition[2]; + weaponoffset[0] = pDominantTracking->Pose.position.x - hmdPosition[0]; + weaponoffset[1] = pDominantTracking->Pose.position.y - hmdPosition[1]; + weaponoffset[2] = pDominantTracking->Pose.position.z - hmdPosition[2]; vec2_t v; float yawRotation = getViewpointYaw() - hmdorientation[YAW]; @@ -164,23 +155,22 @@ void HandleInput_Default( int control_scheme, ovrInputStateGamepad *pFootTrackin weaponoffset[2] = v[0]; //Set gun angles - const ovrQuatf quatRemote = pDominantTracking->HeadPose.Pose.Orientation; vec3_t rotation = {0}; - rotation[PITCH] = vr_weapon_pitchadjust; - QuatToYawPitchRoll(quatRemote, rotation, weaponangles); + rotation[PITCH] = vr_weaponRotate; + QuatToYawPitchRoll(pDominantTracking->Pose.orientation, rotation, weaponangles); if (weaponStabilised) { - float z = pOffTracking->HeadPose.Pose.Position.z - - pDominantTracking->HeadPose.Pose.Position.z; - float x = pOffTracking->HeadPose.Pose.Position.x - - pDominantTracking->HeadPose.Pose.Position.x; - float y = pOffTracking->HeadPose.Pose.Position.y - - pDominantTracking->HeadPose.Pose.Position.y; + float z = pOffTracking->Pose.position.z - + pDominantTracking->Pose.position.z; + float x = pOffTracking->Pose.position.x - + pDominantTracking->Pose.position.x; + float y = pOffTracking->Pose.position.y - + pDominantTracking->Pose.position.y; float zxDist = length(x, z); if (zxDist != 0.0f && z != 0.0f) { - VectorSet(weaponangles, -degrees(atanf(y / zxDist)), -degrees(atan2f(x, -z)), + VectorSet(weaponangles, -RAD2DEG(atanf(y / zxDist)), -RAD2DEG(atan2f(x, -z)), weaponangles[ROLL]); } } @@ -190,9 +180,9 @@ void HandleInput_Default( int control_scheme, ovrInputStateGamepad *pFootTrackin //off-hand stuff { - offhandoffset[0] = pOffTracking->HeadPose.Pose.Position.x - hmdPosition[0]; - offhandoffset[1] = pOffTracking->HeadPose.Pose.Position.y - hmdPosition[1]; - offhandoffset[2] = pOffTracking->HeadPose.Pose.Position.z - hmdPosition[2]; + offhandoffset[0] = pOffTracking->Pose.position.x - hmdPosition[0]; + offhandoffset[1] = pOffTracking->Pose.position.y - hmdPosition[1]; + offhandoffset[2] = pOffTracking->Pose.position.z - hmdPosition[2]; vec2_t v; float yawRotation = getViewpointYaw() - hmdorientation[YAW]; @@ -201,10 +191,10 @@ void HandleInput_Default( int control_scheme, ovrInputStateGamepad *pFootTrackin offhandoffset[2] = v[0]; vec3_t rotation = {0}; - rotation[PITCH] = vr_weapon_pitchadjust; - QuatToYawPitchRoll(pOffTracking->HeadPose.Pose.Orientation, rotation, offhandangles); + rotation[PITCH] = vr_weaponRotate; + QuatToYawPitchRoll(pOffTracking->Pose.orientation, rotation, offhandangles); - if (vr_moveuseoffhand) { + if (vr_move_use_offhand) { controllerYawHeading = offhandangles[YAW] - hmdorientation[YAW]; } else { controllerYawHeading = 0.0f; @@ -214,9 +204,9 @@ void HandleInput_Default( int control_scheme, ovrInputStateGamepad *pFootTrackin //Positional movement { ALOGV(" Right-Controller-Position: %f, %f, %f", - pDominantTracking->HeadPose.Pose.Position.x, - pDominantTracking->HeadPose.Pose.Position.y, - pDominantTracking->HeadPose.Pose.Position.z); + pDominantTracking->Pose.position.x, + pDominantTracking->Pose.position.y, + pDominantTracking->Pose.position.z); vec2_t v; rotateAboutOrigin(positionDeltaThisFrame[0], positionDeltaThisFrame[2], @@ -232,12 +222,12 @@ void HandleInput_Default( int control_scheme, ovrInputStateGamepad *pFootTrackin //Off-hand specific stuff { ALOGV(" Left-Controller-Position: %f, %f, %f", - pOffTracking->HeadPose.Pose.Position.x, - pOffTracking->HeadPose.Pose.Position.y, - pOffTracking->HeadPose.Pose.Position.z); + pOffTracking->Pose.position.x, + pOffTracking->Pose.position.y, + pOffTracking->Pose.position.z); //Teleport - only does anything if vr_teleport cvar is true - if (vr_use_teleport) { + if (vr_teleport) { if ((pSecondaryTrackedRemoteOld->Joystick.y > 0.7f) && !ready_teleport) { ready_teleport = true; } else if ((pSecondaryTrackedRemoteOld->Joystick.y < 0.7f) && ready_teleport) { @@ -250,8 +240,8 @@ void HandleInput_Default( int control_scheme, ovrInputStateGamepad *pFootTrackin //and we don't get movement jitter when the joystick doesn't quite center properly float dist = length(pSecondaryTrackedRemoteNew->Joystick.x, pSecondaryTrackedRemoteNew->Joystick.y); float nlf = nonLinearFilter(dist); - float x = nlf * pSecondaryTrackedRemoteNew->Joystick.x + pFootTrackingNew->LeftJoystick.x; - float y = nlf * pSecondaryTrackedRemoteNew->Joystick.y - pFootTrackingNew->LeftJoystick.y; + float x = nlf * pSecondaryTrackedRemoteNew->Joystick.x; + float y = nlf * pSecondaryTrackedRemoteNew->Joystick.y; //Apply a simple deadzone player_moving = (fabs(x) + fabs(y)) > 0.05f; @@ -276,8 +266,8 @@ void HandleInput_Default( int control_scheme, ovrInputStateGamepad *pFootTrackin if (pPrimaryTrackedRemoteNew->Joystick.x > 0.6f) { if (increaseSnap) { resetDoomYaw = true; - snapTurn -= vr_snapturn_angle; - if (vr_snapturn_angle > 10.0f) { + snapTurn -= vr_snapTurn; + if (vr_snapTurn > 10.0f) { increaseSnap = false; } @@ -293,10 +283,10 @@ void HandleInput_Default( int control_scheme, ovrInputStateGamepad *pFootTrackin if (pPrimaryTrackedRemoteNew->Joystick.x < -0.6f) { if (decreaseSnap) { resetDoomYaw = true; - snapTurn += vr_snapturn_angle; + snapTurn += vr_snapTurn; //If snap turn configured for less than 10 degrees - if (vr_snapturn_angle > 10.0f) { + if (vr_snapTurn > 10.0f) { decreaseSnap = false; } @@ -328,13 +318,13 @@ void HandleInput_Default( int control_scheme, ovrInputStateGamepad *pFootTrackin //If snap turn set to 0, then we can use left/right on the stick as mappable functions Joy_GenerateButtonEvents( - (pPrimaryTrackedRemoteOld->Joystick.x > 0.7f && !dominantGripPushedOld && !vr_snapturn_angle ? 1 : 0), - (pPrimaryTrackedRemoteNew->Joystick.x > 0.7f && !dominantGripPushedNew && !vr_snapturn_angle ? 1 : 0), + (pPrimaryTrackedRemoteOld->Joystick.x > 0.7f && !dominantGripPushedOld && !vr_snapTurn ? 1 : 0), + (pPrimaryTrackedRemoteNew->Joystick.x > 0.7f && !dominantGripPushedNew && !vr_snapTurn ? 1 : 0), 1, KEY_MWHEELLEFT); Joy_GenerateButtonEvents( - (pPrimaryTrackedRemoteOld->Joystick.x < -0.7f && !dominantGripPushedOld && !vr_snapturn_angle ? 1 : 0), - (pPrimaryTrackedRemoteNew->Joystick.x < -0.7f && !dominantGripPushedNew && !vr_snapturn_angle ? 1 : 0), + (pPrimaryTrackedRemoteOld->Joystick.x < -0.7f && !dominantGripPushedOld && !vr_snapTurn ? 1 : 0), + (pPrimaryTrackedRemoteNew->Joystick.x < -0.7f && !dominantGripPushedNew && !vr_snapTurn ? 1 : 0), 1, KEY_MWHEELRIGHT); } @@ -343,8 +333,8 @@ void HandleInput_Default( int control_scheme, ovrInputStateGamepad *pFootTrackin { //Fire Joy_GenerateButtonEvents( - ((pDominantTrackedRemoteOld->Buttons & ovrButton_Trigger) != 0) && !dominantGripPushedOld ? 1 : 0, - ((pDominantTrackedRemoteNew->Buttons & ovrButton_Trigger) != 0) && !dominantGripPushedNew ? 1 : 0, + ((pDominantTrackedRemoteOld->Buttons & xrButton_Trigger) != 0) && !dominantGripPushedOld ? 1 : 0, + ((pDominantTrackedRemoteNew->Buttons & xrButton_Trigger) != 0) && !dominantGripPushedNew ? 1 : 0, 1, KEY_PAD_RTRIGGER); //"Use" (open door, toggle switch etc) @@ -361,24 +351,26 @@ void HandleInput_Default( int control_scheme, ovrInputStateGamepad *pFootTrackin // Inv Use Joy_GenerateButtonEvents( - ((pDominantTrackedRemoteOld->Buttons & ovrButton_Joystick) != 0) && !dominantGripPushedOld ? 1 : 0, - ((pDominantTrackedRemoteNew->Buttons & ovrButton_Joystick) != 0) && !dominantGripPushedNew ? 1 : 0, + ((pDominantTrackedRemoteOld->Buttons & xrButton_Joystick) != 0) && !dominantGripPushedOld ? 1 : 0, + ((pDominantTrackedRemoteNew->Buttons & xrButton_Joystick) != 0) && !dominantGripPushedNew ? 1 : 0, 1, KEY_ENTER); + //Button touching (but not pressing) needs adding to OpenXR mappings +/* //No Default Binding Joy_GenerateButtonEvents( ((pDominantTrackedRemoteOld->Touches & ovrTouch_ThumbRest) != 0) && !dominantGripPushedOld ? 1 : 0, ((pDominantTrackedRemoteNew->Touches & ovrTouch_ThumbRest) != 0) && !dominantGripPushedNew ? 1 : 0, 1, KEY_JOY5); - +*/ } //Dominant Hand - Secondary keys (grip pushed) { //Alt-Fire Joy_GenerateButtonEvents( - ((pDominantTrackedRemoteOld->Buttons & ovrButton_Trigger) != 0) && dominantGripPushedOld ? 1 : 0, - ((pDominantTrackedRemoteNew->Buttons & ovrButton_Trigger) != 0) && dominantGripPushedNew ? 1 : 0, + ((pDominantTrackedRemoteOld->Buttons & xrButton_Trigger) != 0) && dominantGripPushedOld ? 1 : 0, + ((pDominantTrackedRemoteNew->Buttons & xrButton_Trigger) != 0) && dominantGripPushedNew ? 1 : 0, 1, KEY_PAD_LTRIGGER); //Crouch @@ -395,21 +387,24 @@ void HandleInput_Default( int control_scheme, ovrInputStateGamepad *pFootTrackin //No Binding Joy_GenerateButtonEvents( - ((pDominantTrackedRemoteOld->Buttons & ovrButton_Joystick) != 0) && dominantGripPushedOld ? 1 : 0, - ((pDominantTrackedRemoteNew->Buttons & ovrButton_Joystick) != 0) && dominantGripPushedNew ? 1 : 0, + ((pDominantTrackedRemoteOld->Buttons & xrButton_Joystick) != 0) && dominantGripPushedOld ? 1 : 0, + ((pDominantTrackedRemoteNew->Buttons & xrButton_Joystick) != 0) && dominantGripPushedNew ? 1 : 0, 1, KEY_TAB); + //Button touching (but not pressing) needs adding to OpenXR mappings +/* //No Default Binding Joy_GenerateButtonEvents( ((pDominantTrackedRemoteOld->Touches & ovrTouch_ThumbRest) != 0) && dominantGripPushedOld ? 1 : 0, ((pDominantTrackedRemoteNew->Touches & ovrTouch_ThumbRest) != 0) && dominantGripPushedNew ? 1 : 0, 1, KEY_JOY6); +*/ //Use grip as an extra button //Alt-Fire Joy_GenerateButtonEvents( - ((pDominantTrackedRemoteOld->Buttons & ovrButton_GripTrigger) != 0) && !dominantGripPushedOld ? 1 : 0, - ((pDominantTrackedRemoteNew->Buttons & ovrButton_GripTrigger) != 0) && !dominantGripPushedNew ? 1 : 0, + ((pDominantTrackedRemoteOld->Buttons & xrButton_GripTrigger) != 0) && !dominantGripPushedOld ? 1 : 0, + ((pDominantTrackedRemoteNew->Buttons & xrButton_GripTrigger) != 0) && !dominantGripPushedNew ? 1 : 0, 1, KEY_PAD_LTRIGGER); } @@ -418,8 +413,8 @@ void HandleInput_Default( int control_scheme, ovrInputStateGamepad *pFootTrackin { //No Default Binding Joy_GenerateButtonEvents( - ((pOffTrackedRemoteOld->Buttons & ovrButton_Trigger) != 0) && !dominantGripPushedOld ? 1 : 0, - ((pOffTrackedRemoteNew->Buttons & ovrButton_Trigger) != 0) && !dominantGripPushedNew ? 1 : 0, + ((pOffTrackedRemoteOld->Buttons & xrButton_Trigger) != 0) && !dominantGripPushedOld ? 1 : 0, + ((pOffTrackedRemoteNew->Buttons & xrButton_Trigger) != 0) && !dominantGripPushedNew ? 1 : 0, 1, KEY_LSHIFT); //No Default Binding @@ -436,19 +431,22 @@ void HandleInput_Default( int control_scheme, ovrInputStateGamepad *pFootTrackin //"Use" (open door, toggle switch etc) - Can be rebound for other uses Joy_GenerateButtonEvents( - ((pOffTrackedRemoteOld->Buttons & ovrButton_Joystick) != 0) && !dominantGripPushedOld ? 1 : 0, - ((pOffTrackedRemoteNew->Buttons & ovrButton_Joystick) != 0) && !dominantGripPushedNew ? 1 : 0, + ((pOffTrackedRemoteOld->Buttons & xrButton_Joystick) != 0) && !dominantGripPushedOld ? 1 : 0, + ((pOffTrackedRemoteNew->Buttons & xrButton_Joystick) != 0) && !dominantGripPushedNew ? 1 : 0, 1, KEY_SPACE); + //Button touching (but not pressing) needs adding to OpenXR mappings +/* //No Default Binding Joy_GenerateButtonEvents( ((pOffTrackedRemoteOld->Touches & ovrTouch_ThumbRest) != 0) && !dominantGripPushedOld ? 1 : 0, ((pOffTrackedRemoteNew->Touches & ovrTouch_ThumbRest) != 0) && !dominantGripPushedNew ? 1 : 0, 1, KEY_JOY7); +*/ Joy_GenerateButtonEvents( - ((pOffTrackedRemoteOld->Buttons & ovrButton_GripTrigger) != 0) && !dominantGripPushedOld ? 1 : 0, - ((pOffTrackedRemoteNew->Buttons & ovrButton_GripTrigger) != 0) && !dominantGripPushedNew ? 1 : 0, + ((pOffTrackedRemoteOld->Buttons & xrButton_GripTrigger) != 0) && !dominantGripPushedOld ? 1 : 0, + ((pOffTrackedRemoteNew->Buttons & xrButton_GripTrigger) != 0) && !dominantGripPushedNew ? 1 : 0, 1, KEY_PAD_RTHUMB); } @@ -456,8 +454,8 @@ void HandleInput_Default( int control_scheme, ovrInputStateGamepad *pFootTrackin { //No Default Binding Joy_GenerateButtonEvents( - ((pOffTrackedRemoteOld->Buttons & ovrButton_Trigger) != 0) && dominantGripPushedOld ? 1 : 0, - ((pOffTrackedRemoteNew->Buttons & ovrButton_Trigger) != 0) && dominantGripPushedNew ? 1 : 0, + ((pOffTrackedRemoteOld->Buttons & xrButton_Trigger) != 0) && dominantGripPushedOld ? 1 : 0, + ((pOffTrackedRemoteNew->Buttons & xrButton_Trigger) != 0) && dominantGripPushedNew ? 1 : 0, 1, KEY_LALT); //Move Down @@ -474,19 +472,22 @@ void HandleInput_Default( int control_scheme, ovrInputStateGamepad *pFootTrackin //Land Joy_GenerateButtonEvents( - ((pOffTrackedRemoteOld->Buttons & ovrButton_Joystick) != 0) && dominantGripPushedOld ? 1 : 0, - ((pOffTrackedRemoteNew->Buttons & ovrButton_Joystick) != 0) && dominantGripPushedNew ? 1 : 0, + ((pOffTrackedRemoteOld->Buttons & xrButton_Joystick) != 0) && dominantGripPushedOld ? 1 : 0, + ((pOffTrackedRemoteNew->Buttons & xrButton_Joystick) != 0) && dominantGripPushedNew ? 1 : 0, 1, KEY_HOME); + //Button touching (but not pressing) needs adding to OpenXR mappings +/* //No Default Binding Joy_GenerateButtonEvents( ((pOffTrackedRemoteOld->Touches & ovrTouch_ThumbRest) != 0) && dominantGripPushedOld ? 1 : 0, ((pOffTrackedRemoteNew->Touches & ovrTouch_ThumbRest) != 0) && dominantGripPushedNew ? 1 : 0, 1, KEY_JOY8); +*/ Joy_GenerateButtonEvents( - ((pOffTrackedRemoteOld->Buttons & ovrButton_GripTrigger) != 0) && dominantGripPushedOld && !vr_twohandedweapons ? 1 : 0, - ((pOffTrackedRemoteNew->Buttons & ovrButton_GripTrigger) != 0) && dominantGripPushedNew && !vr_twohandedweapons ? 1 : 0, + ((pOffTrackedRemoteOld->Buttons & xrButton_GripTrigger) != 0) && dominantGripPushedOld && !vr_two_handed_weapons ? 1 : 0, + ((pOffTrackedRemoteNew->Buttons & xrButton_GripTrigger) != 0) && dominantGripPushedNew && !vr_two_handed_weapons ? 1 : 0, 1, KEY_PAD_DPAD_UP); } diff --git a/Projects/Android/jni/gzdoom-g3.3mgw_mobile b/Projects/Android/jni/gzdoom-g3.3mgw_mobile index 88800f0..2324921 160000 --- a/Projects/Android/jni/gzdoom-g3.3mgw_mobile +++ b/Projects/Android/jni/gzdoom-g3.3mgw_mobile @@ -1 +1 @@ -Subproject commit 88800f0fc2f5a8cb41b0714ebfd234b9f0ba624e +Subproject commit 232492121898049ef54155d193bd563ad6955a05 diff --git a/Projects/Android/local.properties b/Projects/Android/local.properties index f7e86f8..a832ec8 100644 --- a/Projects/Android/local.properties +++ b/Projects/Android/local.properties @@ -5,5 +5,5 @@ # For customization when using a Version Control System, please read the # header note. #Wed Feb 02 12:33:02 GMT 2022 -ndk.dir=C\:\\Users\\simon\\AppData\\Local\\Android\\Sdk\\ndk\\20.0.5594570 +ndk.dir=C\:\\Users\\Simon\\AppData\\Local\\Android\\Sdk\\ndk\\21.1.6352462 sdk.dir=C\:\\Users\\Simon\\AppData\\Local\\Android\\Sdk diff --git a/Projects/Android/settings.gradle b/Projects/Android/settings.gradle index 068619d..c12cafa 100644 --- a/Projects/Android/settings.gradle +++ b/Projects/Android/settings.gradle @@ -1,4 +1,4 @@ rootProject.projectDir = new File(settingsDir, '../../../..') -rootProject.name = "QuestzDoom" +rootProject.name = "QuestZDoom" -include ':', 'VrSamples:QuestzDoom:Projects:Android' +include ':', 'XrSamples:QuestZDoom:Projects:Android' diff --git a/Projects/Projects.iml b/Projects/Projects.iml index 1c4a12c..29f2cdb 100644 --- a/Projects/Projects.iml +++ b/Projects/Projects.iml @@ -1,9 +1,9 @@ - + - diff --git a/VrSamples-QuestzDoom.iml b/XrSamples-QuestzDoom.iml similarity index 84% rename from VrSamples-QuestzDoom.iml rename to XrSamples-QuestzDoom.iml index 7b53052..22cedfe 100644 --- a/VrSamples-QuestzDoom.iml +++ b/XrSamples-QuestzDoom.iml @@ -1,9 +1,9 @@ - + - diff --git a/assets/res/lzdoom.pk3 b/assets/res/lzdoom.pk3 index 34c2e74..0965c44 100644 Binary files a/assets/res/lzdoom.pk3 and b/assets/res/lzdoom.pk3 differ