// 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?");
}
}
}