// 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;
}
}