Skip to content

Commit

Permalink
[MRTK3] Adding support for unbounded tracking mode (#11750)
Browse files Browse the repository at this point in the history
The `Microsoft.MixedReality.OpenXR.EyeLevelSceneOrigin` component in Microsoft's MR OpenXR plugin will soon be deprecated (so to avoid having mono behaviours within this plugin).  Also, Unity doesn't have UI to set the tracking mode to `TrackingOriginModeFlags.Unbounded` on the `XRInputSubsystem`.

So, to replace  `Microsoft.MixedReality.OpenXR.EyeLevelSceneOrigin`, an `UnboundedTrackingMode` MonoBehaviour has been added to MRTK3. This behavior has been applied to MRTK's XR Rig and will force HL2 into unbounded mode if Unity's `XROrigin.RequestedTrackingOriginMode` is set to "Not Specified". If the platform does not support unbounded mode, the new behavior does nothing.
  • Loading branch information
AMollis committed Jul 28, 2023
1 parent bf1336b commit 03e9b2b
Show file tree
Hide file tree
Showing 8 changed files with 279 additions and 2 deletions.
13 changes: 13 additions & 0 deletions com.microsoft.mrtk.input/Assets/Prefabs/MRTK XR Rig.prefab
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
@@ -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
{
/// <summary>
/// A custom inspector for the <see cref="UnboundedTrackingMode"/> component.
/// </summary>
[CustomEditor(typeof(UnboundedTrackingMode))]
internal class UnboundedTrackingModeInspector : UnityEditor.Editor
{
/// <inheritdoc/>
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<XROrigin>();
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
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 9 additions & 2 deletions com.microsoft.mrtk.input/Editor/MRTK.Input.Editor.asmdef
Expand Up @@ -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"
Expand All @@ -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
Expand Down
6 changes: 6 additions & 0 deletions com.microsoft.mrtk.input/MRTK.Input.asmdef
Expand Up @@ -7,6 +7,7 @@
"Unity.InputSystem",
"Unity.XR.CoreUtils",
"Unity.XR.Interaction.Toolkit",
"Unity.XR.Management",
"glTFast",
"Ktx"
],
Expand Down Expand Up @@ -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
Expand Down
8 changes: 8 additions & 0 deletions com.microsoft.mrtk.input/Tracking.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

178 changes: 178 additions & 0 deletions 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
{
/// <summary>
/// This component works along side Unity's <see cref="XROrigin"/> to ensure that the <see cref="XRInputSubsystem"/> has
/// the <see cref="TrackingOriginModeFlags.Unbounded"/> flag set on its tracking origin mode if this flag is supported.
/// </summary>
/// <remarks>
/// <para>
/// 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
/// <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#XR_MSFT_unbounded_reference_space">OpenXR Specification</see>.
/// </para>
/// <para>
/// The <see cref="TrackingOriginModeFlags.Unbounded"/> flag is only applied if the <see cref="XROrigin.RequestedTrackingOriginMode"/>
/// is set to <see cref="XROrigin.TrackingOriginMode.NotSpecified"/>, the device supports unbounded spaces, and <see cref="XRInputSubsystem"/>
/// current tracking mode is set to <see cref="TrackingOriginModeFlags.Device"/>.
/// </para>
/// </remarks>
[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;

/// <summary>
/// A Unity event function that is called when the script component has been enabled.
/// </summary>
/// <remarks>
/// This will attempt to set <see cref="XRInputSubsystem"/> tracking mode to <see cref="TrackingOriginModeFlags.Unbounded"/>.
/// </remarks>
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<XRInputSubsystem>();
if (m_inputSubsystem == null)
{
Debug.LogWarning($"EyeLevelSceneOrigin: XRInputSubsystem is null.");
return;
}

XROrigin xrOrigin = gameObject.GetComponent<XROrigin>();
if (xrOrigin != null)
{
m_requestedTrackingOriginMode = xrOrigin.RequestedTrackingOriginMode;
}

m_inputSubsystem.trackingOriginUpdated += XrInput_trackingOriginUpdated;

EnsureUnboundedModeSetIfSupported();
}

/// <summary>
/// A Unity event function that is called when the script component has been disabled.
/// </summary>
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();
}
}
}

/// <summary>
/// Apply the <see cref="XROrigin.CameraYOffset"/> to the transform.
/// </summary>
/// <remarks>
/// <para>
/// The <see cref="XROrigin"/> class does not yet support "unbounded" devices, which results in
/// <see cref="XROrigin"/> failing to correctly apply the specified camera height offset for
/// "unbounded" device. This function addresses this limitation that is typically seen during
/// remoting scenarios.
/// </para>
/// <para>
/// Note, problems with the camera height offset are typically seen when using holographic
/// remoting on HoloLens.
/// </para>
/// </remarks>
private void ApplyUnboundedCameraOffset()
{
if (!Application.isPlaying)
{
return;
}

XROrigin xrOrigin = gameObject.GetComponent<XROrigin>();
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
}
}
11 changes: 11 additions & 0 deletions com.microsoft.mrtk.input/Tracking/UnboundedTrackingMode.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 03e9b2b

Please sign in to comment.