// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using Microsoft.MixedReality.Toolkit.Physics; using Microsoft.MixedReality.Toolkit.Utilities; using System; using System.Collections.Generic; using UnityEngine; namespace Microsoft.MixedReality.Toolkit.Input { public static class PointerUtils { /// /// Tries to get the end point of a hand ray. /// If no hand ray of given handedness is found, returns false and sets result to zero. /// /// Handedness of ray /// The output position /// True if pointer found, false otherwise. If not found, endPoint is set to zero public static bool TryGetHandRayEndPoint(Handedness handedness, out Vector3 endPoint) { return TryGetPointerEndpoint(handedness, InputSourceType.Hand, out endPoint); } /// /// Tries to get the end point of a motion controller. /// If no pointer of given handedness is found, returns false and sets result to zero. /// /// Handedness of ray /// The output position /// True if pointer found, false otherwise. If not found, endPoint is set to zero public static bool TryGetMotionControllerEndPoint(Handedness handedness, out Vector3 endPoint) { return TryGetPointerEndpoint(handedness, InputSourceType.Controller, out endPoint); } /// /// Tries to get the end point of a pointer by source type and handedness. /// If no pointer of given handedness is found, returns false and sets result to zero. /// /// Type of pointer to query /// Handedness of pointer /// Input type of pointer /// Output point position /// True if pointer found, false otherwise. If not found, endPoint is set to zero public static bool TryGetPointerEndpoint(Handedness handedness, InputSourceType inputType, out Vector3 endPoint) where T : IMixedRealityPointer { foreach (var pointer in GetPointers(handedness, inputType)) { FocusDetails? details = pointer?.Result?.Details; if (details.HasValue) { endPoint = details.Value.Point; return true; } } endPoint = Vector3.zero; return false; } /// /// Tries to get the end point of a pointer of a pointer type and handedness. /// If no pointer of given handedness is found, returns false and sets result to zero. /// /// Type of pointer to query /// Handedness of pointer /// The output point position /// True if pointer found, false otherwise. If not found, endPoint is set to zero public static bool TryGetPointerEndpoint(Handedness handedness, out Vector3 endPoint) where T : class, IMixedRealityPointer { T pointer = GetPointer(handedness); FocusDetails? details = pointer?.Result?.Details; if (!details.HasValue) { endPoint = Vector3.zero; return false; } endPoint = details.Value.Point; return true; } /// /// Find the first detected pointer of the given type with matching handedness. /// public static T GetPointer(Handedness handedness) where T : class, IMixedRealityPointer { foreach (var pointer in CoreServices.InputSystem.FocusProvider.GetPointers()) { if (pointer.Controller?.ControllerHandedness.IsMatch(handedness) == true) { return pointer; } } return null; } /// /// Returns iterator over all pointers of specific type, with specific handedness. /// /// Return only pointers with this input type /// Handedness of pointer /// Iterator over all pointers of specific type, with specific handedness public static IEnumerable GetPointers(Handedness handedness = Handedness.Any) where T : IMixedRealityPointer { foreach (var pointer in GetPointers()) { if (pointer is T pointerConcrete && pointer.Controller?.ControllerHandedness.IsMatch(handedness) == true) { yield return pointerConcrete; } } } /// /// Returns all pointers with given handedness and input type. /// /// Handedness of pointer /// Only return pointers of this input source type /// Iterator over all pointers that match the source type, with specific handedness public static IEnumerable GetPointers(Handedness handedness, InputSourceType sourceType) where T : IMixedRealityPointer { foreach (var pointer in GetPointers(handedness)) { if (pointer.Controller?.ControllerHandedness.IsMatch(handedness) == true && pointer.InputSourceParent.SourceType == sourceType) { yield return pointer; } } } /// /// Iterate over all pointers in the input system. May contain duplicates. /// public static IEnumerable GetPointers() { HashSet inputSources = CoreServices.InputSystem?.DetectedInputSources; if (inputSources == null) { yield break; } foreach (IMixedRealityInputSource inputSource in inputSources) { foreach (IMixedRealityPointer pointer in inputSource.Pointers) { yield return pointer; } } } /// /// Queries input system for the behavior of a given pointer type. See . /// /// Type of pointer to query /// Handedness to query /// for the given pointer type and handedness public static PointerBehavior GetPointerBehavior(Handedness handedness, InputSourceType inputSourceType) where T : class, IMixedRealityPointer { if (CoreServices.InputSystem?.FocusProvider is IPointerPreferences preferences) { if (typeof(T) == typeof(GGVPointer)) { return preferences.GazePointerBehavior; } return preferences.GetPointerBehavior(handedness, inputSourceType); } else { WarnAboutSettingCustomPointerBehaviors(); return PointerBehavior.Default; } } /// /// Sets the behavior for the hand ray with given handedness /// /// Desired . /// Specify handedness to restrict to only right, left hands. public static void SetHandRayPointerBehavior(PointerBehavior pointerBehavior, Handedness handedness = Handedness.Any) { SetPointerBehavior(pointerBehavior, InputSourceType.Hand, handedness); } /// /// Sets the behavior for the motion controller ray with given handedness /// /// Desired . /// Specify handedness to restrict to only right, left. public static void SetMotionControllerRayPointerBehavior(PointerBehavior pointerBehavior, Handedness handedness = Handedness.Any) { SetPointerBehavior(pointerBehavior, InputSourceType.Controller, handedness); } /// /// Sets the behavior for the grab pointer with given handedness. /// /// Desired . /// Specify handedness to restrict to only right, left. public static void SetHandGrabPointerBehavior(PointerBehavior pointerBehavior, Handedness handedness = Handedness.Any) { SetPointerBehavior(pointerBehavior, InputSourceType.Hand, handedness); } /// /// Sets the behavior for the poke pointer with given handedness. /// /// Desired . /// Specify handedness to restrict to only right, left. public static void SetHandPokePointerBehavior(PointerBehavior pointerBehavior, Handedness handedness = Handedness.Any) { SetPointerBehavior(pointerBehavior, InputSourceType.Hand, handedness); } /// /// Sets the behavior for the gaze pointer. /// /// Desired . public static void SetGazePointerBehavior(PointerBehavior pointerBehavior) { if (CoreServices.InputSystem.FocusProvider is IPointerPreferences pointerPreferences) { pointerPreferences.GazePointerBehavior = pointerBehavior; foreach (InputSourceType sourceType in Enum.GetValues(typeof(InputSourceType))) { pointerPreferences.SetPointerBehavior(Handedness.Any, sourceType, pointerBehavior); } } else { WarnAboutSettingCustomPointerBehaviors(); } } /// /// Sets the pointer behavior for pointer of type T, for all input types. /// /// All pointer types that equal or derive from this type will be set. /// Desired . /// Specify handedness to restrict to only right, left. public static void SetPointerBehavior(PointerBehavior pointerBehavior, Handedness handedness = Handedness.Any) where T : class, IMixedRealityPointer { foreach (InputSourceType type in Enum.GetValues(typeof(InputSourceType))) { SetPointerBehavior(pointerBehavior, type, handedness); } } /// /// Sets the behavior for the given pointer type and input type. /// /// All pointer types that equal or derive from this type will be set. /// Desired . /// Allows setting different behaviors for different input types (hands, controllers, etc.) /// Specify handedness to restrict to only right, left. public static void SetPointerBehavior(PointerBehavior pointerBehavior, InputSourceType sourceType, Handedness handedness = Handedness.Any) where T : class, IMixedRealityPointer { if (CoreServices.InputSystem.FocusProvider is IPointerPreferences preferences) { preferences.SetPointerBehavior(handedness, sourceType, pointerBehavior); } else { WarnAboutSettingCustomPointerBehaviors(); } } private static void WarnAboutSettingCustomPointerBehaviors() { Debug.LogWarning("Setting custom pointer behaviors only works if the input system is using the default MRTK focus provider. " + "Are you using a custom Focus Provider that doesn't implement IPointerPreferences?"); } } }