288 lines
12 KiB
C#
288 lines
12 KiB
C#
// Copyright (c) Microsoft Corporation.
|
|
// Licensed under the MIT License.
|
|
|
|
using Microsoft.MixedReality.Toolkit.Input;
|
|
using Microsoft.MixedReality.Toolkit.Utilities;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
|
|
namespace Microsoft.MixedReality.Toolkit.UI
|
|
{
|
|
/// <summary>
|
|
/// This class must be instantiated by a script that implements the <see cref="Microsoft.MixedReality.Toolkit.Input.IMixedRealitySourceStateHandler"/>,
|
|
/// <see cref="Microsoft.MixedReality.Toolkit.Input.IMixedRealityInputHandler"/> and <see cref="Microsoft.MixedReality.Toolkit.Input.IMixedRealityInputHandler{T}"/>.
|
|
///
|
|
/// ***It must receive EventData arguments from OnInputDown(), OnInputUp(), OnInputChanged() and OnSourceLost().***
|
|
///
|
|
/// This class manages the states of input necessary to calculate a proper grab position
|
|
/// The eventData received on inputdown has the point on the target that was hit by the gaze;
|
|
/// the mixedrealitypose - eventdata received on input changed contains the handposition in eventdata.inputdata.position
|
|
/// It also contains useful retrieval functions.
|
|
/// </summary>
|
|
[System.Obsolete("This component is no longer supported", true)]
|
|
public class GazeHandHelper
|
|
{
|
|
#region Private Variables
|
|
private readonly Dictionary<uint, bool> positionAvailableMap = new Dictionary<uint, bool>();
|
|
private readonly Dictionary<uint, IMixedRealityInputSource> handSourceMap = new Dictionary<uint, IMixedRealityInputSource>();
|
|
private readonly Dictionary<uint, Vector3> handStartPositionMap = new Dictionary<uint, Vector3>();
|
|
private readonly Dictionary<uint, Vector3> handPositionMap = new Dictionary<uint, Vector3>();
|
|
private readonly Dictionary<uint, Vector3> gazePointMap = new Dictionary<uint, Vector3>();
|
|
#endregion Private Variables
|
|
|
|
#region Public Methods
|
|
|
|
/// <summary>
|
|
/// This function must be called from the OnInputDown handler in a script implementing the <see cref="Microsoft.MixedReality.Toolkit.Input.IMixedRealityInputHandler{T}"/>.
|
|
/// </summary>
|
|
/// <param name="eventData">The InputEventData argument 'eventData' is passed through to GazeHandHelper</param>
|
|
public void AddSource(InputEventData eventData)
|
|
{
|
|
IMixedRealityInputSource source = eventData.InputSource;
|
|
if (source != null && IsInDictionary(source.SourceId) == false && source.Pointers != null && source.Pointers.Length > 0)
|
|
{
|
|
handSourceMap.Add(source.SourceId, source);
|
|
gazePointMap.Add(source.SourceId, source.Pointers[0].Position);
|
|
handStartPositionMap.Add(source.SourceId, Vector3.zero);
|
|
handPositionMap.Add(source.SourceId, Vector3.zero);
|
|
positionAvailableMap.Add(source.SourceId, false);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// This function must be called from the OnInputUp handler in a script implementing the <see cref="Microsoft.MixedReality.Toolkit.Input.IMixedRealityInputHandler{T}"/>.
|
|
/// </summary>
|
|
/// <param name="eventData">he InputEventData argument 'eventData' is passed through to GazeHandHelper</param>
|
|
public void RemoveSource(InputEventData eventData)
|
|
{
|
|
uint sourceId = eventData.SourceId;
|
|
handPositionMap.Remove(sourceId);
|
|
handSourceMap.Remove(sourceId);
|
|
gazePointMap.Remove(sourceId);
|
|
handStartPositionMap.Remove(sourceId);
|
|
positionAvailableMap.Remove(sourceId);
|
|
}
|
|
|
|
/// <summary>
|
|
/// This function must be called from the OnSourceLost handler in a script implementing the IMixedRealitySourceStateHandler interface.
|
|
/// </summary>
|
|
public void RemoveSource(SourceStateEventData eventData)
|
|
{
|
|
uint sourceId = eventData.SourceId;
|
|
handPositionMap.Remove(sourceId);
|
|
handSourceMap.Remove(sourceId);
|
|
gazePointMap.Remove(sourceId);
|
|
handStartPositionMap.Remove(sourceId);
|
|
positionAvailableMap.Remove(sourceId);
|
|
}
|
|
|
|
/// <summary>
|
|
/// This function must be called from the OnInputChanged handler in a script implementing the <see cref="Microsoft.MixedReality.Toolkit.Input.IMixedRealityInputHandler{T}"/>.
|
|
/// </summary>
|
|
public void UpdateSource(InputEventData<MixedRealityPose> eventData)
|
|
{
|
|
uint id = eventData.SourceId;
|
|
Vector3 handPosition = eventData.InputData.Position;
|
|
|
|
if (IsInDictionary(id) == true)
|
|
{
|
|
if (handStartPositionMap[id] == Vector3.zero)
|
|
{
|
|
handStartPositionMap[id] = handPosition;
|
|
positionAvailableMap[id] = true;
|
|
}
|
|
|
|
if (true == TryGetPointerPosition(id, out Vector3 currentGazePoint))
|
|
{
|
|
handPositionMap[id] = handPosition + (currentGazePoint - gazePointMap[id]);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// This function returns the number of active hands.
|
|
/// </summary>
|
|
public int GetActiveHandCount()
|
|
{
|
|
int count = 0;
|
|
foreach (uint key in positionAvailableMap.Keys)
|
|
{
|
|
if (positionAvailableMap[key] == true)
|
|
{
|
|
count++;
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
/// <summary>
|
|
/// This function gets the average of the positions of all active hands.
|
|
/// </summary>
|
|
/// <returns>Vector3 representing the average position</returns>
|
|
public Vector3 GetHandsCentroid()
|
|
{
|
|
if (true == TryGetHandsCentroid(out Vector3 centroid))
|
|
{
|
|
return centroid;
|
|
}
|
|
return Vector3.zero;
|
|
}
|
|
|
|
/// <summary>
|
|
/// This function gets an array of all active hand positions
|
|
/// </summary>
|
|
/// <returns>enumerable of Vector3</returns>
|
|
public IEnumerable<Vector3> GetAllHandPositions()
|
|
{
|
|
foreach (uint key in positionAvailableMap.Keys)
|
|
{
|
|
if (positionAvailableMap[key] == true)
|
|
{
|
|
yield return handPositionMap[key];
|
|
}
|
|
}
|
|
yield break;
|
|
}
|
|
|
|
/// <summary>
|
|
/// This function retrieves the position of the first active hand.
|
|
/// </summary>
|
|
/// <returns>Vector3 representing position</returns>
|
|
public Vector3 GetFirstHand()
|
|
{
|
|
foreach (Vector3 hand in GetAllHandPositions())
|
|
return hand;
|
|
|
|
return Vector3.zero;
|
|
}
|
|
|
|
/// <summary>
|
|
/// This function retrieves a reference to the Dictionary that maps hand positions to sourceIds.
|
|
/// This return value is NOT filtered for whether the hands are active. User should check first
|
|
/// using GetActiveHandCount().
|
|
/// </summary>
|
|
/// <returns>Dictionary with uint Keys mapping to Vector3 positions</returns>
|
|
public Dictionary<uint, Vector3> GetHandPositionsDictionary()
|
|
{
|
|
return handPositionMap;
|
|
}
|
|
#endregion Public Methods
|
|
|
|
#region Safe TryGet Style Public Methods
|
|
/// <summary>
|
|
/// TryGet style function to return HandPosition of a certain handedness if available.
|
|
/// </summary>
|
|
/// <param name="handedness">asks for left or right hand or either</param>
|
|
/// <param name="position">out value that gets filled with a Vector3 representing position</param>
|
|
/// <returns>true or false- whether the hand existed</returns>
|
|
public bool TryGetHandPosition(Handedness handedness, out Vector3 position)
|
|
{
|
|
foreach (uint key in positionAvailableMap.Keys)
|
|
{
|
|
if (positionAvailableMap[key] == true)
|
|
{
|
|
if (handSourceMap[key].Pointers != null && handSourceMap[key].Pointers.Length > 0 && handSourceMap[key].Pointers[0].Controller.ControllerHandedness == handedness)
|
|
{
|
|
position = handPositionMap[key];
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
position = Vector3.zero;
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// TryGet style function to return HandPosition of a certain sourceId if available.
|
|
/// </summary>
|
|
/// <param name="id">asks for the hand position associated with a certain IMixedRealityInputSource id</param>
|
|
/// <param name="handPosition">out value that gets filled with a Vector3 representing position</param>
|
|
/// <returns>true or false- whether the hand existed</returns>
|
|
public bool TryGetHandPosition(uint id, out Vector3 handPosition)
|
|
{
|
|
if (IsInDictionary(id) == true && positionAvailableMap[id] == true)
|
|
{
|
|
handPosition = handPositionMap[id];
|
|
return true;
|
|
}
|
|
|
|
handPosition = Vector3.zero;
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// TryGet style function to get the average of all active hand positions.
|
|
/// </summary>
|
|
/// <param name="handsCentroid">out value filled with Vector3 representing average of hand positions</param>
|
|
/// <returns>true if there were any active hands; false if there were no active hands</returns>
|
|
public bool TryGetHandsCentroid(out Vector3 handsCentroid)
|
|
{
|
|
if (GetActiveHandCount() > 0)
|
|
{
|
|
Vector3 agg = Vector3.zero;
|
|
int activeCount = 0;
|
|
foreach (uint key in handPositionMap.Keys)
|
|
{
|
|
if (positionAvailableMap[key] == true)
|
|
{
|
|
agg += handPositionMap[key];
|
|
activeCount++;
|
|
}
|
|
}
|
|
|
|
if (activeCount > 0)
|
|
{
|
|
handsCentroid = agg / (float)activeCount;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
handsCentroid = Vector3.zero;
|
|
return false;
|
|
}
|
|
#endregion Safe TryGet Style Public Methods
|
|
|
|
#region Private Methods
|
|
private bool IsInDictionary(uint id)
|
|
{
|
|
return handSourceMap.ContainsKey(id);
|
|
}
|
|
private bool GetInitialHandPosition(uint id, out Vector3 initialHandPosition)
|
|
{
|
|
if (IsInDictionary(id) == true)
|
|
{
|
|
initialHandPosition = handStartPositionMap[id];
|
|
return true;
|
|
}
|
|
|
|
initialHandPosition = Vector3.zero;
|
|
return false;
|
|
}
|
|
private bool GetInitialGazePosition(uint id, out Vector3 initialGazePosition)
|
|
{
|
|
if (IsInDictionary(id) == true)
|
|
{
|
|
initialGazePosition = gazePointMap[id];
|
|
return true;
|
|
}
|
|
|
|
initialGazePosition = Vector3.zero;
|
|
return false;
|
|
}
|
|
private bool TryGetPointerPosition(uint id, out Vector3 position)
|
|
{
|
|
IMixedRealityInputSource source = handSourceMap[id];
|
|
if (source != null && source.Pointers != null && source.Pointers.Length > 0)
|
|
{
|
|
position = source.Pointers[0].Position;
|
|
return true;
|
|
}
|
|
|
|
position = Vector3.zero;
|
|
return false;
|
|
}
|
|
#endregion Private Methods
|
|
}
|
|
}
|