// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using Microsoft.MixedReality.Toolkit.Physics; using System.Collections.Generic; using Unity.Profiling; using UnityEngine; using UnityEngine.EventSystems; namespace Microsoft.MixedReality.Toolkit { /// /// Extension methods for Unity's EventSystem /// public static class EventSystemExtensions { private static readonly List RaycastResults = new List(); private static readonly RaycastResultComparer RaycastResultComparer = new RaycastResultComparer(); private static readonly ProfilerMarker RaycastPerfMarker = new ProfilerMarker("[MRTK] EventSystemExtensions.Raycast"); /// /// Executes a raycast all and returns the closest element. /// Fixes the current issue with Unity's raycast sorting which does not consider separate canvases. /// /// /// Takes an optional RaycastResultComparer, which will be used to select the highest priority raycast result. /// /// RaycastResult if hit, or an empty RaycastResult if nothing was hit public static RaycastResult Raycast(this EventSystem eventSystem, PointerEventData pointerEventData, LayerMask[] layerMasks, RaycastResultComparer raycastResultComparer = null) { using (RaycastPerfMarker.Auto()) { eventSystem.RaycastAll(pointerEventData, RaycastResults); return PrioritizeRaycastResult(layerMasks, raycastResultComparer); } } private static readonly ProfilerMarker PrioritizeRaycastResultPerfMarker = new ProfilerMarker("[MRTK] EventSystemExtensions.PrioritizeRaycastResult"); /// /// Sorts the available Raycasts in to a priority order for query. /// /// The layer mask priority. /// private static RaycastResult PrioritizeRaycastResult(LayerMask[] priority, RaycastResultComparer raycastResultComparer) { using (PrioritizeRaycastResultPerfMarker.Auto()) { // If not specified, default to the in-box RaycastResultComparer. if (raycastResultComparer == null) { raycastResultComparer = RaycastResultComparer; } ComparableRaycastResult maxResult = default(ComparableRaycastResult); for (var i = 0; i < RaycastResults.Count; i++) { if (RaycastResults[i].gameObject == null) { continue; } var layerMaskIndex = RaycastResults[i].gameObject.layer.FindLayerListIndex(priority); if (layerMaskIndex == -1) { continue; } var result = new ComparableRaycastResult(RaycastResults[i], layerMaskIndex); if (maxResult.RaycastResult.module == null || raycastResultComparer.Compare(maxResult, result) < 0) { maxResult = result; } } return maxResult.RaycastResult; } } /// /// Bubbles up an event to the parents of the root game object if the event data is not already used. /// /// The EventFunction type. /// Events start executing on the parent of this game object. /// Data associated with the Executing event. /// Function to execute on the gameObject components. /// GameObject that handled the event public static GameObject ExecuteHierarchyUpward(GameObject root, BaseEventData eventData, ExecuteEvents.EventFunction callbackFunction) where T : IEventSystemHandler { if (!eventData.used && root != null) { var parent = root.transform.parent; if (parent != null) { return ExecuteEvents.ExecuteHierarchy(parent.gameObject, eventData, callbackFunction); } } return null; } } }