mixedreality/com.microsoft.mixedreality..../Core/Providers/Hands/HandPoseUtils.cs

216 lines
12 KiB
C#

//------------------------------------------------------------------------------ -
//MRTK - Quest
//https ://github.com/provencher/MRTK-Quest
//------------------------------------------------------------------------------ -
//
//MIT License
//
//Copyright(c) 2020 Eric Provencher
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files(the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions :
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
//------------------------------------------------------------------------------ -
using Microsoft.MixedReality.Toolkit.Input;
using UnityEngine;
namespace Microsoft.MixedReality.Toolkit.Utilities
{
/// <summary>
/// Utilities for detecting hand poses. useful for systems without native gesture support and for raising
/// your own events based on specific hand pose values.
/// </summary>
public static class HandPoseUtils
{
/// <summary>
/// Returns true if index finger tip is closer to wrist than index knuckle joint.
/// </summary>
/// <param name="hand">Hand to query joint pose against.</param>
public static bool IsIndexGrabbing(Handedness hand)
{
if (HandJointUtils.TryGetJointPose(TrackedHandJoint.Wrist, hand, out var wristPose) &&
HandJointUtils.TryGetJointPose(TrackedHandJoint.IndexTip, hand, out var indexTipPose) &&
HandJointUtils.TryGetJointPose(TrackedHandJoint.IndexKnuckle, hand, out var indexKnucklePose))
{
// compare wrist-knuckle to wrist-tip
Vector3 wristToIndexTip = indexTipPose.Position - wristPose.Position;
Vector3 wristToIndexKnuckle = indexKnucklePose.Position - wristPose.Position;
return wristToIndexKnuckle.sqrMagnitude >= wristToIndexTip.sqrMagnitude;
}
return false;
}
/// <summary>
/// Returns true if middle finger tip is closer to wrist than middle knuckle joint.
/// </summary>
/// <param name="hand">Hand to query joint pose against.</param>
public static bool IsMiddleGrabbing(Handedness hand)
{
if (HandJointUtils.TryGetJointPose(TrackedHandJoint.Wrist, hand, out var wristPose) &&
HandJointUtils.TryGetJointPose(TrackedHandJoint.MiddleTip, hand, out var indexTipPose) &&
HandJointUtils.TryGetJointPose(TrackedHandJoint.MiddleKnuckle, hand, out var indexKnucklePose))
{
// compare wrist-knuckle to wrist-tip
Vector3 wristToIndexTip = indexTipPose.Position - wristPose.Position;
Vector3 wristToIndexKnuckle = indexKnucklePose.Position - wristPose.Position;
return wristToIndexKnuckle.sqrMagnitude >= wristToIndexTip.sqrMagnitude;
}
return false;
}
/// <summary>
/// Returns true if middle thumb tip is closer to pinky knuckle than thumb knuckle joint.
/// </summary>
/// <param name="hand">Hand to query joint pose against.</param>
public static bool IsThumbGrabbing(Handedness hand)
{
if (HandJointUtils.TryGetJointPose(TrackedHandJoint.PinkyKnuckle, hand, out var pinkyKnucklePose) &&
HandJointUtils.TryGetJointPose(TrackedHandJoint.ThumbTip, hand, out var thumbTipPose) &&
HandJointUtils.TryGetJointPose(TrackedHandJoint.ThumbProximalJoint, hand, out var thumbKnucklePose))
{
// compare pinkyKnuckle-ThumbKnuckle to pinkyKnuckle-ThumbTip
Vector3 pinkyKnuckleToThumbTip = thumbTipPose.Position - pinkyKnucklePose.Position;
Vector3 pinkyKnuckleToThumbKnuckle = thumbKnucklePose.Position - pinkyKnucklePose.Position;
return pinkyKnuckleToThumbKnuckle.sqrMagnitude >= pinkyKnuckleToThumbTip.sqrMagnitude;
}
return false;
}
/*
* Finger Curl Utils: Util Functions to calculate the curl of a specific finger.
* Author: Chaitanya Shah
* github: https://github.com/chetu3319
*/
/// <summary>
/// Returns curl of ranging from 0 to 1. 1 if index finger curled/closer to wrist. 0 if the finger is not curled.
/// </summary>
/// <param name="handedness">Handedness to query joint pose against.</param>
/// <returns> Float ranging from 0 to 1. 0 if index finger is straight/not curled, 1 if index finger is curled</returns>
public static float IndexFingerCurl(Handedness handedness)
{
if (HandJointUtils.TryGetJointPose(TrackedHandJoint.Wrist, handedness, out var wristPose) &&
HandJointUtils.TryGetJointPose(TrackedHandJoint.IndexTip, handedness, out var fingerTipPose) &&
HandJointUtils.TryGetJointPose(TrackedHandJoint.IndexKnuckle, handedness, out var fingerKnucklePose))
{
return CalculateCurl(wristPose.Position, fingerKnucklePose.Position, fingerTipPose.Position);
}
return 0.0f;
}
/// <summary>
/// Returns curl of middle finger ranging from 0 to 1. 1 if index finger curled/closer to wrist. 0 if the finger is not curled.
/// </summary>
/// <param name="handedness">Handedness to query joint pose against.</param>
/// <returns> Float ranging from 0 to 1. 0 if middle finger is straight/not curled, 1 if middle finger is curled</returns>
public static float MiddleFingerCurl(Handedness handedness)
{
if (HandJointUtils.TryGetJointPose(TrackedHandJoint.Wrist, handedness, out var wristPose) &&
HandJointUtils.TryGetJointPose(TrackedHandJoint.MiddleTip, handedness, out var fingerTipPose) &&
HandJointUtils.TryGetJointPose(TrackedHandJoint.MiddleKnuckle, handedness, out var fingerKnucklePose))
{
return CalculateCurl(wristPose.Position, fingerKnucklePose.Position, fingerTipPose.Position);
}
return 0.0f;
}
/// <summary>
/// Returns curl of ring finger ranging from 0 to 1. 1 if ring finger curled/closer to wrist. 0 if the finger is not curled.
/// </summary>
/// <param name="handedness">Handedness to query joint pose against.</param>
/// <returns> Float ranging from 0 to 1. 0 if ring finger is straight/not curled, 1 if ring finger is curled</returns>
public static float RingFingerCurl(Handedness handedness)
{
if (HandJointUtils.TryGetJointPose(TrackedHandJoint.Wrist, handedness, out var wristPose) &&
HandJointUtils.TryGetJointPose(TrackedHandJoint.RingTip, handedness, out var fingerTipPose) &&
HandJointUtils.TryGetJointPose(TrackedHandJoint.RingKnuckle, handedness, out var fingerKnucklePose))
{
return CalculateCurl(wristPose.Position, fingerKnucklePose.Position, fingerTipPose.Position);
}
return 0.0f;
}
/// <summary>
/// Returns curl of pinky finger ranging from 0 to 1. 1 if pinky finger curled/closer to wrist. 0 if the finger is not curled.
/// </summary>
/// <param name="handedness">Handedness to query joint pose against.</param>
/// <returns> Float ranging from 0 to 1. 0 if pinky finger is straight/not curled, 1 if pinky finger is curled</returns>
public static float PinkyFingerCurl(Handedness handedness)
{
if (HandJointUtils.TryGetJointPose(TrackedHandJoint.Wrist, handedness, out var wristPose) &&
HandJointUtils.TryGetJointPose(TrackedHandJoint.PinkyTip, handedness, out var fingerTipPose) &&
HandJointUtils.TryGetJointPose(TrackedHandJoint.PinkyKnuckle, handedness, out var fingerKnucklePose))
{
return CalculateCurl(wristPose.Position, fingerKnucklePose.Position, fingerTipPose.Position);
}
return 0.0f;
}
/// <summary>
/// Returns curl of thumb finger ranging from 0 to 1. 1 if thumb finger curled/closer to wrist. 0 if the finger is not curled.
/// </summary>
/// <param name="handedness">Handedness to query joint pose against.</param>
/// <returns> Float ranging from 0 to 1. 0 if thumb finger is straight/not curled, 1 if thumb finger is curled</returns>
public static float ThumbFingerCurl(Handedness handedness)
{
if (HandJointUtils.TryGetJointPose(TrackedHandJoint.PinkyKnuckle, handedness, out var pinkyKnuckle) &&
HandJointUtils.TryGetJointPose(TrackedHandJoint.ThumbTip, handedness, out var thumbTip) &&
HandJointUtils.TryGetJointPose(TrackedHandJoint.ThumbProximalJoint, handedness, out var thumbKnuckle))
{
return CalculateCurl(pinkyKnuckle.Position, thumbKnuckle.Position, thumbTip.Position);
}
return 0.0f;
}
/// <summary>
/// Curl calculation of a finger based on the angle made by vectors wristToFingerKuncle and fingerKuckleToFingerTip.
/// </summary>
static private float CalculateCurl(Vector3 wristJoint, Vector3 fingerKnuckleJoint, Vector3 fingerTipJoint)
{
var palmToFinger = (fingerKnuckleJoint - wristJoint).normalized;
var fingerKnuckleToTip = (fingerKnuckleJoint - fingerTipJoint).normalized;
var curl = Vector3.Dot(fingerKnuckleToTip, palmToFinger);
// Redefining the range from [-1,1] to [0,1]
curl = (curl + 1) / 2.0f;
return curl;
}
/// <summary>
/// Pinch calculation of the index finger with the thumb based on the distance between the finger tip and the thumb tip.
/// 4 cm (0.04 unity units) is the threshold for fingers being far apart and pinch being read as 0.
/// </summary>
/// <param name="handedness">Handedness to query joint pose against.</param>
/// <returns> Float ranging from 0 to 1. 0 if the thumb and finger are not pinched together, 1 if thumb finger are pinched together</returns>
private const float IndexThumbSqrMagnitudeThreshold = 0.0016f;
public static float CalculateIndexPinch(Handedness handedness)
{
HandJointUtils.TryGetJointPose(TrackedHandJoint.IndexTip, handedness, out var indexPose);
HandJointUtils.TryGetJointPose(TrackedHandJoint.ThumbTip, handedness, out var thumbPose);
Vector3 distanceVector = indexPose.Position - thumbPose.Position;
float indexThumbSqrMagnitude = distanceVector.sqrMagnitude;
float pinchStrength = Mathf.Clamp(1 - indexThumbSqrMagnitude / IndexThumbSqrMagnitudeThreshold, 0.0f, 1.0f);
return pinchStrength;
}
}
}