Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 2 additions & 30 deletions org.mixedrealitytoolkit.core/Subsystems/MRTKLifecycleManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
using System.Collections.Generic;
using Unity.Profiling;
using UnityEngine;
using UnityEngine.InputSystem;

namespace MixedReality.Toolkit.Subsystems
{
Expand All @@ -19,15 +18,12 @@ public class MRTKLifecycleManager :
MonoBehaviour,
IDisposable
{
[SerializeField, Tooltip("A set of input actions to enable/disable according to the app's focus state.")]
private InputActionReference[] inputActionReferences;

private List<IMRTKManagedSubsystem> managedSubsystems = new List<IMRTKManagedSubsystem>();

/// <summary>
/// The list of <see cref="IMRTKManagedSubsystem"/> objects being managed by this class.
/// </summary>
protected List<IMRTKManagedSubsystem> ManagedSubsystems
protected List<IMRTKManagedSubsystem> ManagedSubsystems
{
get => managedSubsystems;
set => managedSubsystems = value;
Expand Down Expand Up @@ -115,7 +111,7 @@ private void OnDisable()

/// <summary>
/// A Unity event function that is called when the script component has been enabled.
/// </summary>
/// </summary>
private void OnEnable()
{
using (OnEnableProfilerMarker.Auto())
Expand Down Expand Up @@ -180,30 +176,6 @@ private void LateUpdate()
}
}

/// <summary>
/// Sent to all GameObjects when the player gets or loses focus.
/// </summary>
/// <param name="focus"><see langword="true"/> if the GameObjects have focus, else <see langword="false"/>.</param>
protected void OnApplicationFocus(bool focus)
{
// We want to ensure we're focused for input, as some runtimes continue reporting "tracked" while pose updates are paused.
// This is allowed, per-spec, as a "should": "Runtimes should make input actions inactive while the application is unfocused,
// and applications should react to an inactive input action by skipping rendering of that action's input avatar
// (depictions of hands or other tracked objects controlled by the user)."

foreach (InputActionReference reference in inputActionReferences)
{
if (focus)
{
reference.action.Enable();
}
else
{
reference.action.Disable();
}
}
}

#endregion MonoBehaviour

/// <summary>
Expand Down
13 changes: 13 additions & 0 deletions org.mixedrealitytoolkit.input/Assets/Prefabs/MRTK XR Rig.prefab
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,7 @@ GameObject:
- component: {fileID: 2351505567455720332}
- component: {fileID: 4160709927669568829}
- component: {fileID: 6400715630075217958}
- component: {fileID: 1798554929582623642}
m_Layer: 0
m_Name: MRTK XR Rig
m_TagString: Untagged
Expand Down Expand Up @@ -377,6 +378,18 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 1a107350295baaf4489642caa92f05de, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!114 &1798554929582623642
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2351505567455720334}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 671b97df530b06e46893c4189b3ceab4, type: 3}
m_Name:
m_EditorClassIdentifier:
inputActionReferences:
- {fileID: -7613329581162844239, guid: 18c412191cdc9274897f101c7fd5316f, type: 3}
- {fileID: 3239510804178183174, guid: 18c412191cdc9274897f101c7fd5316f, type: 3}
Expand Down
45 changes: 34 additions & 11 deletions org.mixedrealitytoolkit.input/Controllers/HandModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// Licensed under the BSD 3-Clause

using UnityEngine;
using UnityEngine.XR;
using UnityEngine.XR.Interaction.Toolkit.Inputs.Readers;

namespace MixedReality.Toolkit.Input
Expand Down Expand Up @@ -60,15 +59,31 @@ public Transform ModelPrefab

#endregion Associated hand select values

/// <summary>
/// See <see cref="MonoBehaviour"/>.
/// </summary>
protected virtual void Awake()
{
// Create empty container transform for the model if none specified.
// This is not strictly necessary to create since this GameObject could be used
// as the parent for the instantiated prefab, but doing so anyway for backwards compatibility.
if (modelParent == null)
{
modelParent = new GameObject($"[{gameObject.name}] Model Parent").transform;
modelParent.SetParent(transform, false);
modelParent.SetLocalPositionAndRotation(Vector3.zero, Quaternion.identity);
}
}

/// <summary>
/// A Unity event function that is called on the frame when a script is enabled just before any of the update methods are called the first time.
/// </summary>
protected virtual void Start()
{
// Instantiate the model prefab if it is set
if (ModelPrefab != null)
if (modelPrefab != null)
{
model = Instantiate(ModelPrefab, ModelParent);
model = Instantiate(modelPrefab, modelParent);

Debug.Assert(selectInput != null, $"The Select Input reader for {name} is not set and will not be used with the instantiated hand model.");

Expand All @@ -78,21 +93,29 @@ protected virtual void Start()
selectInputVisualizer.SelectInput = selectInput;
}
}

MRTKInputFocusManager.XrSessionHasFocus.SubscribeAndUpdate(OnXrSessionFocus);
}

/// <summary>
/// See <see cref="MonoBehaviour"/>.
/// </summary>
protected virtual void Awake()
private void OnDestroy() => MRTKInputFocusManager.XrSessionHasFocus.Unsubscribe(OnXrSessionFocus);

/// <summary>
/// Sent to all GameObjects when the player gets or loses focus.
/// </summary>
/// <param name="focus"><see langword="true"/> if the GameObjects have focus, else <see langword="false"/>.</param>
private void OnXrSessionFocus(bool focus)
{
// Create empty container transform for the model if none specified.
// This is not strictly necessary to create since this GameObject could be used
// as the parent for the instantiated prefab, but doing so anyway for backwards compatibility.
if (modelParent == null)
// We want to ensure we're focused for input visualization, as some runtimes continue reporting "tracked" while pose updates are paused.
// This is allowed, per-spec, as a "should": "Runtimes should make input actions inactive while the application is unfocused,
// and applications should react to an inactive input action by skipping rendering of that action's input avatar
// (depictions of hands or other tracked objects controlled by the user)."

if (modelParent != null)
{
modelParent = new GameObject($"[{gameObject.name}] Model Parent").transform;
modelParent.SetParent(transform, false);
modelParent.SetLocalPositionAndRotation(Vector3.zero, Quaternion.identity);
modelParent.gameObject.SetActive(focus);
}
}
}
Expand Down
6 changes: 6 additions & 0 deletions org.mixedrealitytoolkit.input/MRTK.Input.asmdef
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"glTFast",
"Microsoft.MixedReality.OpenXR",
"MixedReality.Toolkit.Core",
"Snapdragon.Spaces.Runtime",
"Unity.InputSystem",
"Unity.XR.CoreUtils",
"Unity.XR.Hands",
Expand Down Expand Up @@ -49,6 +50,11 @@
"name": "com.unity.xr.openxr",
"expression": "",
"define": "UNITY_OPENXR_PRESENT"
},
{
"name": "com.qualcomm.snapdragon.spaces",
"expression": "",
"define": "SNAPDRAGON_SPACES_PRESENT"
}
],
"noEngineReferences": false
Expand Down
14 changes: 14 additions & 0 deletions org.mixedrealitytoolkit.input/Subsystems/Hands/HandsProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,27 @@ private void ResetHands()
public override bool TryGetEntireHand(XRNode handNode, out IReadOnlyList<HandJointPose> jointPoses)
{
Debug.Assert(handNode == XRNode.LeftHand || handNode == XRNode.RightHand, "Non-hand XRNode used in TryGetEntireHand query.");

if (!MRTKInputFocusManager.XrSessionHasFocus.Value)
{
jointPoses = Array.Empty<HandJointPose>();
return false;
}

return hands[handNode].TryGetEntireHand(out jointPoses);
}

/// <inheritdoc/>
public override bool TryGetJoint(TrackedHandJoint joint, XRNode handNode, out HandJointPose jointPose)
{
Debug.Assert(handNode == XRNode.LeftHand || handNode == XRNode.RightHand, "Non-hand XRNode used in TryGetJoint query.");

if (!MRTKInputFocusManager.XrSessionHasFocus.Value)
{
jointPose = default;
return false;
}

return hands[handNode].TryGetJoint(joint, out jointPose);
}

Expand Down
2 changes: 1 addition & 1 deletion org.mixedrealitytoolkit.input/Tracking/HandPoseDriver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,6 @@ private bool IsTrackingNone()
private bool TryGetPolyfillDevicePose(out Pose devicePose)
{
bool poseRetrieved = false;
Handedness handedness = HandNode.ToHandedness();

// palmPose retrieved in global space.
if (XRSubsystemHelpers.HandsAggregator != null &&
Expand All @@ -135,6 +134,7 @@ private bool TryGetPolyfillDevicePose(out Pose devicePose)
// our global palm pose back into scene-origin-space.
devicePose = PlayspaceUtilities.InverseTransformPose(palmPose.Pose);

Handedness handedness = HandNode.ToHandedness();
switch (handedness)
{
case Handedness.Left:
Expand Down
13 changes: 6 additions & 7 deletions org.mixedrealitytoolkit.input/Utilities/HandDataContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public abstract class HandDataContainer
public bool AlreadyFullQueried { get; protected set; }

/// <summary>
/// Will be <see langword="true"/> if the the hand joint query as successful.
/// Will be <see langword="true"/> if the hand joint query was successful.
/// </summary>
public bool FullQueryValid { get; protected set; }

Expand All @@ -41,24 +41,23 @@ public HandDataContainer(XRNode handNode)
}

/// <summary>
/// Reset the hand data query status
/// Reset the hand data query status.
/// </summary>
public void Reset()
{
AlreadyFullQueried = false;
FullQueryValid = false;
}


/// <summary>
/// Implemented in derived classes. This method gets all of the joint poses for the hand.
/// <summary>
/// Implemented in derived classes. This method gets all of the joint poses for the hand.
/// </summary>
/// <param name="joints"> The returned list of HandJointPoses</param>
/// <returns><see langword="true"/> if the query was successful, otherwise <see langword="false"/>.</returns>
public abstract bool TryGetEntireHand(out IReadOnlyList<HandJointPose> joints);

/// <summary>
/// Implemented in derived classes. This method gets the specified joint pose.
/// <summary>
/// Implemented in derived classes. This method gets the specified joint pose.
/// </summary>
/// <param name="joint">The TrackedHandJoint to retrieve the post for.</param>
/// <param name="pose">The returned HandJointPose.</param>
Expand Down
103 changes: 103 additions & 0 deletions org.mixedrealitytoolkit.input/Utilities/MRTKInputFocusManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// Copyright (c) Mixed Reality Toolkit Contributors
// Licensed under the BSD 3-Clause

using System.Collections.Generic;
using Unity.XR.CoreUtils.Bindings.Variables;
using UnityEngine;
using UnityEngine.InputSystem;

namespace MixedReality.Toolkit.Input
{
/// <summary>
/// Manages input based on XrSession focus.
/// </summary>
public sealed class MRTKInputFocusManager : MonoBehaviour
{
[SerializeField, Tooltip("A set of input actions to enable/disable according to the app's focus state.")]
private InputActionReference[] inputActionReferences;

/// <summary>
/// Whether the current XrSession has focus or not, stored as a bindable variable that can be subscribed to for value changes.
/// </summary>
/// <remarks>Always <see langword="true"/> in the editor.</remarks>
public static IReadOnlyBindableVariable<bool> XrSessionHasFocus => xrSessionHasFocus;
private static readonly BindableVariable<bool> xrSessionHasFocus = new(Application.isEditor);

/// <summary>
/// We want to ensure we're focused for input, as some runtimes continue reporting "tracked" while pose updates are paused.
/// This is allowed, per-spec, as a "should": "Runtimes should make input actions inactive while the application is unfocused,
/// and applications should react to an inactive input action by skipping rendering of that action's input avatar
/// (depictions of hands or other tracked objects controlled by the user)."
/// </summary>
private void OnFocusChange(bool focus)
{
xrSessionHasFocus.Value = focus;

foreach (InputActionReference reference in inputActionReferences)
{
if (focus)
{
reference.action.Enable();
}
else
{
reference.action.Disable();
}
}
}

#if SNAPDRAGON_SPACES_PRESENT
private static readonly List<Qualcomm.Snapdragon.Spaces.SpacesOpenXRFeature> featureList = new();
private static int lastSessionState = -1;
private Qualcomm.Snapdragon.Spaces.SpacesOpenXRFeature spacesOpenXRFeature = null;

private void Update()
{
if (spacesOpenXRFeature == null)
{
int count = UnityEngine.XR.OpenXR.OpenXRSettings.Instance.GetFeatures(featureList);
for (int i = 0; i < count; i++)
{
Qualcomm.Snapdragon.Spaces.SpacesOpenXRFeature feature = featureList[i];
if (feature != null && feature.enabled)
{
spacesOpenXRFeature = feature;
break;
}
}
}

// XrSessionState maps better to this behavior than OnApplicationFocus but isn't
// easily available in Unity. For now, only the Snapdragon Spaces plugin provides it.
if (spacesOpenXRFeature != null && lastSessionState != spacesOpenXRFeature.SessionState)
{
// If we've lost focus...
// XR_SESSION_STATE_FOCUSED = 5
if (lastSessionState == 5)
{
OnFocusChange(false);
}
// ...or if we've gained focus
// XR_SESSION_STATE_FOCUSED = 5
else if (spacesOpenXRFeature.SessionState == 5)
{
OnFocusChange(true);
}

lastSessionState = spacesOpenXRFeature.SessionState;
}
}
#elif !UNITY_EDITOR
/// <summary>
/// Sent to all GameObjects when the player gets or loses focus.
/// </summary>
/// <param name="focus"><see langword="true"/> if the GameObjects have focus, else <see langword="false"/>.</param>
/// <remarks>
/// Ideally, we'd use XrSessionState here, as it maps better to this behavior than OnApplicationFocus, but
/// it isn't easily available in Unity. For now, only the Snapdragon Spaces plugin provides it, and we use
/// OnApplicationFocus for the rest.
/// </remarks>
private void OnApplicationFocus(bool focus) => OnFocusChange(focus);
#endif
}
}

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

Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// Licensed under the BSD 3-Clause

using MixedReality.Toolkit.Input.Simulation;
using MixedReality.Toolkit.Subsystems;
using System;
using System.Threading.Tasks;
using UnityEngine;
Expand Down