diff --git a/com.microsoft.mrtk.input/Assets/Prefabs/MRTK XR Rig.prefab b/com.microsoft.mrtk.input/Assets/Prefabs/MRTK XR Rig.prefab index a2f02b7c5a3..cab3e681a97 100644 --- a/com.microsoft.mrtk.input/Assets/Prefabs/MRTK XR Rig.prefab +++ b/com.microsoft.mrtk.input/Assets/Prefabs/MRTK XR Rig.prefab @@ -259,6 +259,7 @@ GameObject: m_Component: - component: {fileID: 2351505566903569412} - component: {fileID: 3712792914886690938} + - component: {fileID: 2813607766961918107} m_Layer: 0 m_Name: Camera Offset m_TagString: Untagged @@ -302,6 +303,18 @@ MonoBehaviour: m_CameraFloorOffsetObject: {fileID: 2351505566903569413} m_RequestedTrackingOriginMode: 0 m_CameraYOffset: 1.6 +--- !u!114 &2813607766961918107 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2351505566903569413} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: c4d642881628ba842b14068a50038965, type: 3} + m_Name: + m_EditorClassIdentifier: --- !u!1 &2351505567455720334 GameObject: m_ObjectHideFlags: 0 diff --git a/com.microsoft.mrtk.input/Editor/Inspectors/UnboundedTrackingModeInspector.cs b/com.microsoft.mrtk.input/Editor/Inspectors/UnboundedTrackingModeInspector.cs new file mode 100644 index 00000000000..7787d3bd21f --- /dev/null +++ b/com.microsoft.mrtk.input/Editor/Inspectors/UnboundedTrackingModeInspector.cs @@ -0,0 +1,43 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using Unity.XR.CoreUtils; +using UnityEditor; +using UnityEngine; +using UnityEngine.XR; + +namespace Microsoft.MixedReality.Toolkit.Input.Editor +{ + /// + /// A custom inspector for the component. + /// + [CustomEditor(typeof(UnboundedTrackingMode))] + internal class UnboundedTrackingModeInspector : UnityEditor.Editor + { + /// + public override void OnInspectorGUI() + { +#if UNITYXR_MANAGEMENT_PRESENT + EditorGUILayout.HelpBox("This MRTK component works along side XROrigin to ensure that the XRInputSubsystem " + + "has the TrackingOriginModeFlags.Unbounded flag set if this flag is supported.\n\nNote, the " + + "TrackingOriginModeFlags.Unbounded flag is only applied if the XROrigin.RequestedTrackingOriginMode is " + + "set to \"Not Specified\" and the device supports unbounded spaces.", MessageType.Info); + + if (target is UnboundedTrackingMode unboundedTrackingMode) + { + XROrigin xrOrigin = unboundedTrackingMode.GetComponent(); + if (xrOrigin.RequestedTrackingOriginMode != XROrigin.TrackingOriginMode.NotSpecified) + { + EditorGUILayout.HelpBox("The XROrigin's tracking origin mode is not set to \"Not Specified\". This " + + "component will only put the XRInputSubsystem into unbounded mode if XROrigin's tracking mode is set " + + "\"Not Specified\"", MessageType.Warning); + } + } +#else + EditorGUILayout.HelpBox("This MRTK component is unable to put the XRInputSubsystem into unbounded mode, as the " + + "com.unity.xr.management package hasn't been included. Please include com.unity.xr.management version 4.2 " + + "for this component to function.", MessageType.Warning); +#endif + } + } +} diff --git a/com.microsoft.mrtk.input/Editor/Inspectors/UnboundedTrackingModeInspector.cs.meta b/com.microsoft.mrtk.input/Editor/Inspectors/UnboundedTrackingModeInspector.cs.meta new file mode 100644 index 00000000000..4898e2385c2 --- /dev/null +++ b/com.microsoft.mrtk.input/Editor/Inspectors/UnboundedTrackingModeInspector.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a92af3e7be6129f479310a1ea305454f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.microsoft.mrtk.input/Editor/MRTK.Input.Editor.asmdef b/com.microsoft.mrtk.input/Editor/MRTK.Input.Editor.asmdef index d18f758fc6b..a59769276b4 100644 --- a/com.microsoft.mrtk.input/Editor/MRTK.Input.Editor.asmdef +++ b/com.microsoft.mrtk.input/Editor/MRTK.Input.Editor.asmdef @@ -4,10 +4,12 @@ "references": [ "Microsoft.MixedReality.Toolkit.Core", "Microsoft.MixedReality.Toolkit.Core.Editor", - "Microsoft.MixedReality.Toolkit.Input", + "Microsoft.MixedReality.Toolkit.Input", + "Unity.XR.CoreUtils", "Unity.XR.CoreUtils.Editor", "Unity.XR.Interaction.Toolkit", - "Unity.XR.Interaction.Toolkit.Editor" + "Unity.XR.Interaction.Toolkit.Editor", + "Unity.XR.Management" ], "includePlatforms": [ "Editor" @@ -28,6 +30,11 @@ "name": "com.atteneder.ktx", "expression": "", "define": "KTX_PRESENT" + }, + { + "name": "com.unity.xr.management", + "expression": "4.2", + "define": "UNITYXR_MANAGEMENT_PRESENT" } ], "noEngineReferences": false diff --git a/com.microsoft.mrtk.input/MRTK.Input.asmdef b/com.microsoft.mrtk.input/MRTK.Input.asmdef index f65471091a9..57e7d84add2 100644 --- a/com.microsoft.mrtk.input/MRTK.Input.asmdef +++ b/com.microsoft.mrtk.input/MRTK.Input.asmdef @@ -7,6 +7,7 @@ "Unity.InputSystem", "Unity.XR.CoreUtils", "Unity.XR.Interaction.Toolkit", + "Unity.XR.Management", "glTFast", "Ktx" ], @@ -37,6 +38,11 @@ "name": "com.atteneder.ktx", "expression": "", "define": "KTX_PRESENT" + }, + { + "name": "com.unity.xr.management", + "expression": "4.2", + "define": "UNITYXR_MANAGEMENT_PRESENT" } ], "noEngineReferences": false diff --git a/com.microsoft.mrtk.input/Tracking.meta b/com.microsoft.mrtk.input/Tracking.meta new file mode 100644 index 00000000000..4ca6f2fcb1d --- /dev/null +++ b/com.microsoft.mrtk.input/Tracking.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 212f7ab4a905c6048b79925baa646b66 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.microsoft.mrtk.input/Tracking/UnboundedTrackingMode.cs b/com.microsoft.mrtk.input/Tracking/UnboundedTrackingMode.cs new file mode 100644 index 00000000000..e9af5ff51ac --- /dev/null +++ b/com.microsoft.mrtk.input/Tracking/UnboundedTrackingMode.cs @@ -0,0 +1,178 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using Unity.XR.CoreUtils; +using UnityEngine; +using UnityEngine.XR; + +#if MROPENXR_PRESENT +using Microsoft.MixedReality.OpenXR.Remoting; +#endif + +#if UNITYXR_MANAGEMENT_PRESENT +using UnityEngine.XR.Management; +#endif + +namespace Microsoft.MixedReality.Toolkit.Input +{ + /// + /// This component works along side Unity's to ensure that the has + /// the flag set on its tracking origin mode if this flag is supported. + /// + /// + /// + /// Devices like the HoloLens 2 should use unbounded reference space. This reference space enables the viewer to move + /// freely through a complex environment, often many meters from where they started, while always optimizing for coordinate + /// system stability near the viewer. For more information about unbounded space see the + /// OpenXR Specification. + /// + /// + /// The flag is only applied if the + /// is set to , the device supports unbounded spaces, and + /// current tracking mode is set to . + /// + /// + [RequireComponent(typeof(XROrigin))] + [AddComponentMenu("MRTK/Input/Unbounded Tracking Mode")] + public class UnboundedTrackingMode : MonoBehaviour + { +#if UNITYXR_MANAGEMENT_PRESENT + private XRInputSubsystem m_inputSubsystem; + private XROrigin.TrackingOriginMode m_requestedTrackingOriginMode = XROrigin.TrackingOriginMode.NotSpecified; + + /// + /// A Unity event function that is called when the script component has been enabled. + /// + /// + /// This will attempt to set tracking mode to . + /// + private void OnEnable() + { + XRGeneralSettings xrSettings = XRGeneralSettings.Instance; + if (xrSettings == null) + { + Debug.LogWarning($"EyeLevelSceneOrigin: XRGeneralSettings is null."); + return; + } + + XRManagerSettings xrManager = xrSettings.Manager; + if (xrManager == null) + { + Debug.LogWarning($"EyeLevelSceneOrigin: XRManagerSettings is null."); + return; + } + + XRLoader xrLoader = xrManager.activeLoader; + if (xrLoader == null) + { + Debug.LogWarning($"EyeLevelSceneOrigin: XRLoader is null."); + return; + } + + m_inputSubsystem = xrLoader.GetLoadedSubsystem(); + if (m_inputSubsystem == null) + { + Debug.LogWarning($"EyeLevelSceneOrigin: XRInputSubsystem is null."); + return; + } + + XROrigin xrOrigin = gameObject.GetComponent(); + if (xrOrigin != null) + { + m_requestedTrackingOriginMode = xrOrigin.RequestedTrackingOriginMode; + } + + m_inputSubsystem.trackingOriginUpdated += XrInput_trackingOriginUpdated; + + EnsureUnboundedModeSetIfSupported(); + } + + /// + /// A Unity event function that is called when the script component has been disabled. + /// + private void OnDisable() + { + if (m_inputSubsystem != null) + { + m_inputSubsystem.trackingOriginUpdated -= XrInput_trackingOriginUpdated; + m_inputSubsystem = null; + } + } + + private void XrInput_trackingOriginUpdated(XRInputSubsystem obj) + { + if (isActiveAndEnabled) + { + EnsureUnboundedModeSetIfSupported(); + } + } + + private void EnsureUnboundedModeSetIfSupported() + { + TrackingOriginModeFlags currentMode = m_inputSubsystem.GetTrackingOriginMode(); + TrackingOriginModeFlags desiredMode = GetDesiredTrackingOriginMode(m_inputSubsystem); + + if (m_requestedTrackingOriginMode == XROrigin.TrackingOriginMode.NotSpecified && + (currentMode == TrackingOriginModeFlags.Device || currentMode == TrackingOriginModeFlags.Unbounded) && + currentMode != desiredMode) + { + Debug.Log($"UnboundedTrackingMode: TrySetTrackingOriginMode to {desiredMode}"); + if (!m_inputSubsystem.TrySetTrackingOriginMode(desiredMode)) + { + Debug.LogWarning($"UnboundedTrackingMode: Failed to set tracking origin to {desiredMode}."); + } + else if (desiredMode == TrackingOriginModeFlags.Unbounded) + { + ApplyUnboundedCameraOffset(); + } + } + } + + /// + /// Apply the to the transform. + /// + /// + /// + /// The class does not yet support "unbounded" devices, which results in + /// failing to correctly apply the specified camera height offset for + /// "unbounded" device. This function addresses this limitation that is typically seen during + /// remoting scenarios. + /// + /// + /// Note, problems with the camera height offset are typically seen when using holographic + /// remoting on HoloLens. + /// + /// + private void ApplyUnboundedCameraOffset() + { + if (!Application.isPlaying) + { + return; + } + + XROrigin xrOrigin = gameObject.GetComponent(); + if (xrOrigin != null && + xrOrigin.CameraFloorOffsetObject != null) + { + var offsetTransform = xrOrigin.CameraFloorOffsetObject.transform; + var desiredPosition = offsetTransform.localPosition; + desiredPosition.y = xrOrigin.CameraYOffset; + offsetTransform.localPosition = desiredPosition; + } + } + + private static TrackingOriginModeFlags GetDesiredTrackingOriginMode(XRInputSubsystem xrInput) + { + TrackingOriginModeFlags supportedFlags = xrInput.GetSupportedTrackingOriginModes(); + TrackingOriginModeFlags targetFlag = TrackingOriginModeFlags.Device; // All OpenXR runtime must support LOCAL space + + if (supportedFlags.HasFlag(TrackingOriginModeFlags.Unbounded)) + { + targetFlag = TrackingOriginModeFlags.Unbounded; + } + + return targetFlag; + } +#endif + } +} diff --git a/com.microsoft.mrtk.input/Tracking/UnboundedTrackingMode.cs.meta b/com.microsoft.mrtk.input/Tracking/UnboundedTrackingMode.cs.meta new file mode 100644 index 00000000000..91a7fb04315 --- /dev/null +++ b/com.microsoft.mrtk.input/Tracking/UnboundedTrackingMode.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c4d642881628ba842b14068a50038965 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: