// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using Microsoft.MixedReality.Toolkit.Utilities;
using UnityEngine;
namespace Microsoft.MixedReality.Toolkit.Input
{
///
/// Utilities for accessing position, rotation of rays.
///
public static class InputRayUtils
{
///
/// Gets the ray representing the position and direction of the user's head.
///
/// The ray the head gaze
public static Ray GetHeadGazeRay()
{
return new Ray(CameraCache.Main.transform.position, CameraCache.Main.transform.forward);
}
///
/// Gets the ray representing the position and direction of the user's eyes.
///
/// The ray being returned
///
/// True if the ray is being returned, false otherwise.
///
public static bool TryGetEyeGazeRay(out Ray ray)
{
ray = new Ray();
IMixedRealityEyeGazeProvider eyeGazeProvider = CoreServices.InputSystem?.EyeGazeProvider;
if ((eyeGazeProvider == null) ||
!eyeGazeProvider.IsEyeTrackingDataValid)
{
return false;
}
ray = eyeGazeProvider.LatestEyeGaze;
return true;
}
///
/// Gets the ray associated with the user's hand.
///
/// The handedness of the hand
/// The ray being returned
///
/// True if the ray is being returned, false otherwise.
///
public static bool TryGetHandRay(Handedness hand, out Ray ray)
{
ray = new Ray();
IMixedRealityController controller;
if (TryGetControllerInstance(InputSourceType.Hand, hand, out controller))
{
MixedRealityInteractionMapping mapping;
if (TryGetInteractionMapping(controller, DeviceInputType.SpatialPointer, out mapping))
{
ray.origin = mapping.PositionData;
ray.direction = MathUtilities.GetDirection(mapping.RotationData);
return true;
}
}
return false;
}
///
/// Gets the ray associated with the motion controller.
///
/// The handedness of the motion controller
/// The ray being returned
///
/// True if the ray is being returned, false otherwise.
///
public static bool TryGetMotionControllerRay(Handedness hand, out Ray ray)
{
ray = new Ray();
IMixedRealityController controller;
if (TryGetControllerInstance(InputSourceType.Controller, hand, out controller))
{
MixedRealityInteractionMapping mapping;
if (TryGetInteractionMapping(controller, DeviceInputType.SpatialPointer, out mapping))
{
ray.origin = mapping.PositionData;
ray.direction = MathUtilities.GetDirection(mapping.RotationData);
return true;
}
}
return false;
}
///
/// Gets the first instance matching the specified source type and hand.
///
/// Type of the input source
/// The handedness of the controller
/// The instance being returned
///
/// True if the controller instance is being returned, false otherwise.
///
private static bool TryGetControllerInstance(InputSourceType sourceType, Handedness hand, out IMixedRealityController controller)
{
controller = null;
System.Collections.Generic.HashSet controllers = CoreServices.InputSystem?.DetectedControllers;
if (controllers == null)
{
return false;
}
foreach (IMixedRealityController c in controllers)
{
if ((c.InputSource?.SourceType == sourceType) &&
(c.ControllerHandedness == hand))
{
controller = c;
return true;
}
}
return false;
}
///
/// Gets the matching the for
/// the specified controller.
///
/// The instance
/// The type of device input
/// The being returned
///
/// True if the interaction mapping is being returned, false otherwise.
///
private static bool TryGetInteractionMapping(IMixedRealityController controller, DeviceInputType inputType, out MixedRealityInteractionMapping mapping)
{
mapping = null;
MixedRealityInteractionMapping[] mappings = controller.Interactions;
for (int i = 0; i < mappings.Length; i++)
{
if (mappings[i].InputType == inputType)
{
mapping = mappings[i];
return true;
}
}
return false;
}
///
/// Gets the ray associated with the desired input source type
/// and hand.
///
/// The type of input source
/// The handedness of the input source
/// The ray being returned
///
/// True if the ray is being returned, false otherwise.
///
public static bool TryGetRay(InputSourceType sourceType, Handedness hand, out Ray ray)
{
bool success = false;
switch (sourceType)
{
case InputSourceType.Head:
// The head does not have a handedness, so we ignore the hand parameter.
ray = GetHeadGazeRay();
success = true;
break;
case InputSourceType.Eyes:
// The eyes do not have a handedness, so we ignore the hand parameter.
success = TryGetEyeGazeRay(out ray);
break;
case InputSourceType.Hand:
success = TryGetHandRay(hand, out ray);
break;
case InputSourceType.Controller:
success = TryGetMotionControllerRay(hand, out ray);
break;
default:
Debug.Log($"It is not supported to get the ray for {sourceType} sources.");
ray = new Ray();
success = false;
break;
}
return success;
}
}
}