// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System; using System.Runtime.InteropServices; using UnityEngine; namespace Microsoft.MixedReality.OpenXR { /// /// Represents the set of gestures that may be recognized by a GestureRecognizer. /// [Flags] public enum GestureSettings { /// /// An empty gesture setting /// None = 0, /// /// A single air tap. /// Tap = 1, /// /// A double air tap /// DoubleTap = 1 << 1, /// /// Hold at the end of air tap /// Hold = 1 << 2, /// /// Manipulate gesture to control X, Y, and Z translations. /// ManipulationTranslate = 1 << 3, /// /// Navigation gesture at X direction. /// NavigationX = 1 << 4, /// /// Navigation gesture at Y direction. /// NavigationY = 1 << 5, /// /// Navigation gesture at Z direction. /// NavigationZ = 1 << 6, /// /// Navigation gesture at X direction and suppress Y and Z direction. /// NavigationRailsX = 1 << 7, /// /// Navigation gesture at Y direction and suppress X and Z direction. /// NavigationRailsY = 1 << 8, /// /// Navigation gesture at Z direction and suppress X and Y direction. /// NavigationRailsZ = 1 << 9, } /// /// Represents the type of gesture recognizer event /// public enum GestureEventType { /// /// Indicates a new recognition is started. /// RecognitionStarted, /// /// Indicates the recognition is ended. /// RecognitionEnded, /// /// Indicates a tap is detected. /// Tapped, /// /// Indicates a hold gesture is detected. /// HoldStarted, /// /// Indicates a hold gesture is completed. /// HoldCompleted, /// /// Indicates a hold gesture is canceled. /// HoldCanceled, /// /// Indicates a manipulation gesture is started. /// ManipulationStarted, /// /// Indicates a manipulation gesture is updating the input location. /// ManipulationUpdated, /// /// Indicates a manipulation gesture is completed. /// ManipulationCompleted, /// /// Indicates a manipulation gesture is canceled. /// ManipulationCanceled, /// /// Indicates a navigation gesture is started. /// NavigationStarted, /// /// Indicates a navigation gesture is updating the input location. /// NavigationUpdated, /// /// Indicates a navigation gesture is completed. /// NavigationCompleted, /// /// Indicates a navigation gesture is canceled. /// NavigationCanceled, } /// /// Represents the hand that initiated the gesture. /// public enum GestureHandedness { /// /// The gesture is not associated with any specific hand, for example when a gesture is triggered by voice command. /// Unspecified, /// /// The gesture is initiated by left hand. /// Left, /// /// The gesture is initiated by right hand. /// Right, } /// /// The data of a gesture event, include the event type, handedness, poses etc. /// [StructLayout(LayoutKind.Sequential, Pack = 8)] public struct GestureEventData { /// /// Get the type of gesture event. /// public GestureEventType EventType => nativeData.eventType; /// /// Get which hand triggers this gesture event, or it's not related to specific hand. /// public GestureHandedness Handedness => nativeData.handedness; /// /// Get the data for tap or double tap event. /// It only has value if and only if the eventType == GestureEventType.Tapped /// public TappedEventData? TappedData => nativeData.Get(nativeData.tappedData, nativeData.IsTappedEvent()); /// /// Get the data for manipulation gesture event. /// It only has value if and only if the eventType == GestureEventType.ManipulationStarted/Updated/Completed /// public ManipulationEventData? ManipulationData => nativeData.Get(nativeData.manipulationData, nativeData.IsManipulationEvent()); /// /// Get the data for navigation gesture event. /// It only has value if and only if the eventType == GestureEventType.NavigationStarted/Updated/Completed /// public NavigationEventData? NavigationData => nativeData.Get(nativeData.navigationData, nativeData.IsNavigationEvent()); private readonly NativeGestureEventData nativeData; } /// /// The data of a tap gesture event /// [StructLayout(LayoutKind.Sequential, Pack = 8)] public struct TappedEventData { /// /// The tap number represented by this gesture, either 1 or 2. /// public uint TapCount; } /// /// The data of a manipulation gesture event /// [StructLayout(LayoutKind.Sequential, Pack = 8)] public struct ManipulationEventData { /// /// Get the relative translation of the hand since the start of a Manipulation gesture. /// public Vector3 CumulativeTranslation; } /// /// The data of a navigation gesture event /// [StructLayout(LayoutKind.Sequential, Pack = 8)] public struct NavigationEventData { /// /// Gets whether the navigation gesture the user is performing involves motion on the horizontal axis. /// public bool IsNavigatingX => m_directionFlags.HasFlag(NativeDirectionFlags.X); /// /// Gets whether the navigation gesture the user is performing involves motion on the vertical axis. /// public bool IsNavigatingY => m_directionFlags.HasFlag(NativeDirectionFlags.Y); /// /// Gets whether the navigation gesture the user is performing involves motion on the depth axis. /// public bool IsNavigatingZ => m_directionFlags.HasFlag(NativeDirectionFlags.Z); /// /// Gets the normalized offset of the hand or motion controller within the unit cube for all axes for this Navigation gesture. /// /// X direction is from left to right. Y direction is from bottom to top. Z direction is from back to forward. public Vector3 NormalizedOffset; private NativeDirectionFlags m_directionFlags; } /// /// A gesture recognizer interprets user interactions from hands, motion controllers, and system voice commands /// to surface spatial gesture events, which users target using their gaze or hand's pointing ray. /// public class GestureRecognizer { /// /// Create a new GestureRecognizer using the given settings. /// /// If the given setting is not compatible, the new GestureRecognizer will still be created, /// though it won't produce any gesture event. The setting can be corrected later through the GestureSettings property. public GestureRecognizer(GestureSettings settings) { GestureSettings = settings; } /// /// Set the gesture settings to configure which gestures to recognize. /// public GestureSettings GestureSettings { get { return m_requestedSettings; } set { if (m_requestedSettings != value) { m_requestedSettings = value; if (m_gestureSubsystem != null) { m_gestureSubsystem.GestureSettings = value; } else { m_gestureSubsystem = GestureSubsystem.TryCreateGestureSubsystem(value); } } } } /// /// Start monitor the user interactions and recognize the configured gestures. /// public void Start() { if (m_gestureSubsystem != null) { m_gestureSubsystem.Start(); } } /// /// Stop monitor the user interactions /// public void Stop() { if (m_gestureSubsystem != null) { m_gestureSubsystem.Stop(); } } /// /// Get the next gesture recognition event data, or return false when the event queue is empty. /// /// App allocated data struct to receive the data. /// If function returns false, the content in eventData is undefined and should be avoided. public bool TryGetNextEvent(ref GestureEventData eventData) { return m_gestureSubsystem != null && m_gestureSubsystem.TryGetNextEvent(ref eventData); } /// /// Cancel all pending gestures and reset to initial state. All events in the queue will be discarded. /// public void CancelPendingGestures() { if (m_gestureSubsystem != null) { m_gestureSubsystem.CancelPendingGestures(); } } /// /// Destroy the GestureRecognizer when the application is done with it. /// public void Destroy() { if (m_gestureSubsystem != null) { m_gestureSubsystem.Dispose(); m_gestureSubsystem = null; } } /// /// Destroy the GestureRecognizer when the application is done with it. /// [Obsolete("Obsolete and will be removed in future releases. Use the Destroy() function at appropriated place instead.")] public void Dispose() { Destroy(); } private GestureSubsystem m_gestureSubsystem; private GestureSettings m_requestedSettings; } }