diff --git a/UnityProjects/MRTKDevTemplate/Assets/XR/Settings/OpenXR Package Settings.asset b/UnityProjects/MRTKDevTemplate/Assets/XR/Settings/OpenXR Package Settings.asset index 3a4da5b8d..74ba99edb 100644 --- a/UnityProjects/MRTKDevTemplate/Assets/XR/Settings/OpenXR Package Settings.asset +++ b/UnityProjects/MRTKDevTemplate/Assets/XR/Settings/OpenXR Package Settings.asset @@ -363,6 +363,26 @@ MonoBehaviour: company: Unity priority: 0 required: 0 +--- !u!114 &-5000202051992085229 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 30587bb28b5390644ae70f801841a073, type: 3} + m_Name: MRTKFocusFeature Android + m_EditorClassIdentifier: + m_enabled: 1 + nameUi: MRTK3 Session Focus + version: 4.0.0 + featureIdInternal: org.mixedreality.toolkit.input.focus + openxrExtensionStrings: + company: Mixed Reality Toolkit Contributors + priority: 0 + required: 0 --- !u!114 &-4989097037534641705 MonoBehaviour: m_ObjectHideFlags: 0 @@ -394,6 +414,7 @@ MonoBehaviour: - {fileID: -5492763381520605560} - {fileID: 720212866400260749} - {fileID: -2783824018031006640} + - {fileID: -4614918767838866102} - {fileID: 4334429337943452973} - {fileID: -5448262054166454653} - {fileID: -9173909432611776730} @@ -429,6 +450,26 @@ MonoBehaviour: company: Unity priority: 0 required: 0 +--- !u!114 &-4614918767838866102 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 30587bb28b5390644ae70f801841a073, type: 3} + m_Name: MRTKFocusFeature Standalone + m_EditorClassIdentifier: + m_enabled: 1 + nameUi: MRTK3 Session Focus + version: 4.0.0 + featureIdInternal: org.mixedreality.toolkit.input.focus + openxrExtensionStrings: + company: Mixed Reality Toolkit Contributors + priority: 0 + required: 0 --- !u!114 &-3765664599707111037 MonoBehaviour: m_ObjectHideFlags: 0 @@ -536,6 +577,7 @@ MonoBehaviour: - {fileID: -7229825001273466666} - {fileID: 4508051287823359615} - {fileID: 2835676197965704550} + - {fileID: -5000202051992085229} - {fileID: -7431403579802858517} - {fileID: -3273436338079595365} - {fileID: 8773911662759437579} @@ -651,6 +693,26 @@ MonoBehaviour: company: Unity priority: 0 required: 0 +--- !u!114 &-1685409458817180080 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 30587bb28b5390644ae70f801841a073, type: 3} + m_Name: MRTKFocusFeature Metro + m_EditorClassIdentifier: + m_enabled: 1 + nameUi: MRTK3 Session Focus + version: 4.0.0 + featureIdInternal: org.mixedreality.toolkit.input.focus + openxrExtensionStrings: + company: Mixed Reality Toolkit Contributors + priority: 0 + required: 0 --- !u!114 &-1310172892294911860 MonoBehaviour: m_ObjectHideFlags: 0 @@ -934,6 +996,7 @@ MonoBehaviour: - {fileID: 8785654261743502636} - {fileID: 2622006578924267534} - {fileID: -6733472631637007722} + - {fileID: -1685409458817180080} - {fileID: 4575689165844638740} - {fileID: 6698335440458243644} - {fileID: -2219742231848666541} diff --git a/org.mixedrealitytoolkit.core/CHANGELOG.md b/org.mixedrealitytoolkit.core/CHANGELOG.md index ffe977dc0..2585246d7 100644 --- a/org.mixedrealitytoolkit.core/CHANGELOG.md +++ b/org.mixedrealitytoolkit.core/CHANGELOG.md @@ -2,6 +2,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). +## Unreleased + +### Removed + +* Removed input action focus handling from `MRTKLifecycleManager` and moved to the Input package. [PR #1057](https://github.com/MixedRealityToolkit/MixedRealityToolkit-Unity/pull/1057) + ## [4.0.0-pre.2] - 2025-12-05 ### Added diff --git a/org.mixedrealitytoolkit.core/Subsystems/MRTKLifecycleManager.cs b/org.mixedrealitytoolkit.core/Subsystems/MRTKLifecycleManager.cs index ed28c877d..5f1934445 100644 --- a/org.mixedrealitytoolkit.core/Subsystems/MRTKLifecycleManager.cs +++ b/org.mixedrealitytoolkit.core/Subsystems/MRTKLifecycleManager.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using Unity.Profiling; using UnityEngine; -using UnityEngine.InputSystem; namespace MixedReality.Toolkit.Subsystems { @@ -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 managedSubsystems = new List(); /// /// The list of objects being managed by this class. /// - protected List ManagedSubsystems + protected List ManagedSubsystems { get => managedSubsystems; set => managedSubsystems = value; @@ -115,7 +111,7 @@ private void OnDisable() /// /// A Unity event function that is called when the script component has been enabled. - /// + /// private void OnEnable() { using (OnEnableProfilerMarker.Auto()) @@ -180,30 +176,6 @@ private void LateUpdate() } } - /// - /// Sent to all GameObjects when the player gets or loses focus. - /// - /// if the GameObjects have focus, else . - 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 /// diff --git a/org.mixedrealitytoolkit.input/Assets/Prefabs/MRTK XR Rig.prefab b/org.mixedrealitytoolkit.input/Assets/Prefabs/MRTK XR Rig.prefab index 2587371fe..46f67b566 100644 --- a/org.mixedrealitytoolkit.input/Assets/Prefabs/MRTK XR Rig.prefab +++ b/org.mixedrealitytoolkit.input/Assets/Prefabs/MRTK XR Rig.prefab @@ -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 @@ -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} diff --git a/org.mixedrealitytoolkit.input/CHANGELOG.md b/org.mixedrealitytoolkit.input/CHANGELOG.md index bb0000f72..3eb63cd49 100644 --- a/org.mixedrealitytoolkit.input/CHANGELOG.md +++ b/org.mixedrealitytoolkit.input/CHANGELOG.md @@ -2,6 +2,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). +## Unreleased + +### Added + +* Added `MRTKFocusFeature` to provide XrSession focus info to MRTK components. [PR #1057](https://github.com/MixedRealityToolkit/MixedRealityToolkit-Unity/pull/1057) +* Added input action focus handling to disable controller/hand tracked state when the XrSession goes out of focus. [PR #1057](https://github.com/MixedRealityToolkit/MixedRealityToolkit-Unity/pull/1057) + ## [4.0.0-pre.2] - 2025-12-05 ### Changed diff --git a/org.mixedrealitytoolkit.input/Controllers/HandModel.cs b/org.mixedrealitytoolkit.input/Controllers/HandModel.cs index 72d0c9548..8297d01e1 100644 --- a/org.mixedrealitytoolkit.input/Controllers/HandModel.cs +++ b/org.mixedrealitytoolkit.input/Controllers/HandModel.cs @@ -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 @@ -60,15 +59,31 @@ public Transform ModelPrefab #endregion Associated hand select values + /// + /// See . + /// + 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); + } + } + /// /// 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. /// 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."); @@ -78,21 +93,29 @@ protected virtual void Start() selectInputVisualizer.SelectInput = selectInput; } } + + MRTKFocusFeature.XrSessionFocused.SubscribeAndUpdate(OnXrSessionFocus); } /// /// See . /// - protected virtual void Awake() + private void OnDestroy() => MRTKFocusFeature.XrSessionFocused.Unsubscribe(OnXrSessionFocus); + + /// + /// Sent when the XrSession gains or loses focus. + /// + /// if the XrSession has focus, else . + 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); } } } diff --git a/org.mixedrealitytoolkit.input/Editor/InputValidation.cs b/org.mixedrealitytoolkit.input/Editor/InputValidation.cs index 06af1d363..ad7c3be9f 100644 --- a/org.mixedrealitytoolkit.input/Editor/InputValidation.cs +++ b/org.mixedrealitytoolkit.input/Editor/InputValidation.cs @@ -27,29 +27,40 @@ private static void AddInputValidationRule() { foreach (var buildTargetGroup in MRTKProjectValidation.BuildTargetGroups) { - MRTKProjectValidation.AddTargetDependentRules(new List() { GenerateSpeechInteractorRule(buildTargetGroup) }, buildTargetGroup); + MRTKProjectValidation.AddTargetDependentRules(new List() { + GenerateSpeechInteractorRule(buildTargetGroup) + }, buildTargetGroup); #if UNITY_OPENXR_PRESENT // Skip the standalone target as the hand subsystem rule for it is already present for all build targets if (buildTargetGroup != BuildTargetGroup.Standalone) { - MRTKProjectValidation.AddTargetDependentRules(new List() { GenerateUnityHandsRule(buildTargetGroup) }, buildTargetGroup); + MRTKProjectValidation.AddTargetDependentRules(new List() { + GenerateUnityHandsRule(buildTargetGroup), + GenerateMRTKFocusFeatureRule(buildTargetGroup) + }, buildTargetGroup); } #endif } - MRTKProjectValidation.AddTargetIndependentRules(new List() { GenerateSkinWeightsRule(), GenerateGLTFastRule(), + MRTKProjectValidation.AddTargetIndependentRules(new List() { + GenerateSkinWeightsRule(), + GenerateGLTFastRule(), #if UNITY_OPENXR_PRESENT GenerateUnityHandsRule(BuildTargetGroup.Standalone), #endif }); // Only generate the KTX rule for platforms related to Meta - MRTKProjectValidation.AddTargetDependentRules(new List() { GenerateKTXRule(), + MRTKProjectValidation.AddTargetDependentRules(new List() { + GenerateKTXRule(), #if UNITY_OPENXR_PRESENT GenerateAndroidHandsRule(), #endif }, BuildTargetGroup.Android); - MRTKProjectValidation.AddTargetDependentRules(new List() { GenerateKTXRule() }, BuildTargetGroup.Standalone); + + MRTKProjectValidation.AddTargetDependentRules(new List() { + GenerateKTXRule() + }, BuildTargetGroup.Standalone); } private static BuildValidationRule GenerateSpeechInteractorRule(BuildTargetGroup buildTargetGroup) @@ -197,6 +208,43 @@ private static BuildValidationRule GenerateAndroidHandsRule() }; #pragma warning restore CS0618 // Type or member is obsolete } + + private static BuildValidationRule GenerateMRTKFocusFeatureRule(BuildTargetGroup buildTargetGroup) + { + return new BuildValidationRule() + { + Category = "MRTK3", + Message = $"For MRTK3 input to work correctly on OpenXR, enable {MRTKFocusFeature.FriendlyName} in the OpenXR Settings.", + CheckPredicate = () => + { + OpenXRSettings settings = OpenXRSettings.GetSettingsForBuildTargetGroup(buildTargetGroup); + if (settings == null) + { + return false; + } + + MRTKFocusFeature focusFeature = settings.GetFeature(); + return focusFeature != null && focusFeature.enabled; + }, + FixIt = () => + { + OpenXRSettings settings = OpenXRSettings.GetSettingsForBuildTargetGroup(buildTargetGroup); + if (settings == null) + { + return; + } + + MRTKFocusFeature focusFeature = settings.GetFeature(); + if (focusFeature != null) + { + focusFeature.enabled = true; + EditorUtility.SetDirty(settings); + } + }, + FixItMessage = $"Enable {nameof(MRTKFocusFeature)} in the OpenXR settings.", + Error = true + }; + } #endif } } diff --git a/org.mixedrealitytoolkit.input/Features.meta b/org.mixedrealitytoolkit.input/Features.meta new file mode 100644 index 000000000..7c4ed83ba --- /dev/null +++ b/org.mixedrealitytoolkit.input/Features.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8904458f727e8da428312ac577c1a981 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/org.mixedrealitytoolkit.input/Features/MRTKFocusFeature.cs b/org.mixedrealitytoolkit.input/Features/MRTKFocusFeature.cs new file mode 100644 index 000000000..29cd7da46 --- /dev/null +++ b/org.mixedrealitytoolkit.input/Features/MRTKFocusFeature.cs @@ -0,0 +1,59 @@ +// Copyright (c) Mixed Reality Toolkit Contributors +// Licensed under the BSD 3-Clause + +using Unity.XR.CoreUtils.Bindings.Variables; +using UnityEngine; +using UnityEngine.XR.OpenXR.Features; + +#if UNITY_EDITOR +using UnityEditor; +using UnityEditor.XR.OpenXR.Features; +#endif + +namespace MixedReality.Toolkit.Input +{ + /// + /// Provides focus data based on XrSession state. + /// +#if UNITY_EDITOR + [OpenXRFeature( + UiName = FriendlyName, + Desc = "Provides focus data based on XrSession state.", + Company = "Mixed Reality Toolkit Contributors", + Version = "4.0.0", + BuildTargetGroups = new[] { BuildTargetGroup.Standalone, BuildTargetGroup.WSA, BuildTargetGroup.Android }, + Category = FeatureCategory.Feature, + FeatureId = "org.mixedreality.toolkit.input.focus")] +#endif + public sealed class MRTKFocusFeature : OpenXRFeature + { + /// + /// The "friendly" name for this feature. + /// + public const string FriendlyName = "MRTK3 Session Focus"; + + /// + /// Whether the current XrSession has focus or not, stored as a bindable variable that can be subscribed to for value changes. + /// + /// Always in the editor. + public static IReadOnlyBindableVariable XrSessionFocused => xrSessionFocused; + private static readonly BindableVariable xrSessionFocused = new(Application.isEditor); + + /// + protected override void OnSessionStateChange(int oldState, int newState) + { + // If we've lost focus... + // XR_SESSION_STATE_FOCUSED = 5 + if (oldState == 5) + { + xrSessionFocused.Value = false; + } + // ...or if we've gained focus + // XR_SESSION_STATE_FOCUSED = 5 + else if (newState == 5) + { + xrSessionFocused.Value = true; + } + } + } +} diff --git a/org.mixedrealitytoolkit.input/Features/MRTKFocusFeature.cs.meta b/org.mixedrealitytoolkit.input/Features/MRTKFocusFeature.cs.meta new file mode 100644 index 000000000..d5b524e4c --- /dev/null +++ b/org.mixedrealitytoolkit.input/Features/MRTKFocusFeature.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 30587bb28b5390644ae70f801841a073 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/org.mixedrealitytoolkit.input/Subsystems/Hands/HandsProvider.cs b/org.mixedrealitytoolkit.input/Subsystems/Hands/HandsProvider.cs index 019443042..c3e35c2b1 100644 --- a/org.mixedrealitytoolkit.input/Subsystems/Hands/HandsProvider.cs +++ b/org.mixedrealitytoolkit.input/Subsystems/Hands/HandsProvider.cs @@ -60,6 +60,13 @@ private void ResetHands() public override bool TryGetEntireHand(XRNode handNode, out IReadOnlyList jointPoses) { Debug.Assert(handNode == XRNode.LeftHand || handNode == XRNode.RightHand, "Non-hand XRNode used in TryGetEntireHand query."); + + if (!MRTKFocusFeature.XrSessionFocused.Value) + { + jointPoses = Array.Empty(); + return false; + } + return hands[handNode].TryGetEntireHand(out jointPoses); } @@ -67,6 +74,13 @@ public override bool TryGetEntireHand(XRNode handNode, out IReadOnlyList - /// Will be if the the hand joint query as successful. + /// Will be if the hand joint query was successful. /// public bool FullQueryValid { get; protected set; } @@ -41,7 +41,7 @@ public HandDataContainer(XRNode handNode) } /// - /// Reset the hand data query status + /// Reset the hand data query status. /// public void Reset() { @@ -49,16 +49,15 @@ public void Reset() FullQueryValid = false; } - - /// - /// Implemented in derived classes. This method gets all of the joint poses for the hand. + /// + /// Implemented in derived classes. This method gets all of the joint poses for the hand. /// /// The returned list of HandJointPoses /// if the query was successful, otherwise . public abstract bool TryGetEntireHand(out IReadOnlyList joints); - /// - /// Implemented in derived classes. This method gets the specified joint pose. + /// + /// Implemented in derived classes. This method gets the specified joint pose. /// /// The TrackedHandJoint to retrieve the post for. /// The returned HandJointPose. diff --git a/org.mixedrealitytoolkit.input/Utilities/MRTKInputFocusManager.cs b/org.mixedrealitytoolkit.input/Utilities/MRTKInputFocusManager.cs new file mode 100644 index 000000000..73c62d229 --- /dev/null +++ b/org.mixedrealitytoolkit.input/Utilities/MRTKInputFocusManager.cs @@ -0,0 +1,51 @@ +// Copyright (c) Mixed Reality Toolkit Contributors +// Licensed under the BSD 3-Clause + +using UnityEngine; +using UnityEngine.InputSystem; + +namespace MixedReality.Toolkit.Input +{ + /// + /// Manages input based on XrSession focus. + /// + 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; + + private void OnEnable() + { + MRTKFocusFeature.XrSessionFocused.SubscribeAndUpdate(OnXrSessionFocus); + } + + private void OnDisable() + { + MRTKFocusFeature.XrSessionFocused.Unsubscribe(OnXrSessionFocus); + } + + /// + /// Sent when the XrSession gains or loses focus. + /// + /// if the XrSession has focus, else . + private void OnXrSessionFocus(bool focus) + { + // 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)." + + foreach (InputActionReference reference in inputActionReferences) + { + if (focus) + { + reference.action.Enable(); + } + else + { + reference.action.Disable(); + } + } + } + } +} diff --git a/org.mixedrealitytoolkit.input/Utilities/MRTKInputFocusManager.cs.meta b/org.mixedrealitytoolkit.input/Utilities/MRTKInputFocusManager.cs.meta new file mode 100644 index 000000000..ec4c2079a --- /dev/null +++ b/org.mixedrealitytoolkit.input/Utilities/MRTKInputFocusManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 671b97df530b06e46893c4189b3ceab4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/org.mixedrealitytoolkit.input/Visualizers/ControllerVisualizer/ControllerVisualizer.cs b/org.mixedrealitytoolkit.input/Visualizers/ControllerVisualizer/ControllerVisualizer.cs index 2b53c1eef..4ab036c36 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/ControllerVisualizer/ControllerVisualizer.cs +++ b/org.mixedrealitytoolkit.input/Visualizers/ControllerVisualizer/ControllerVisualizer.cs @@ -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;