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: