250 lines
9.7 KiB
C#
250 lines
9.7 KiB
C#
/*
|
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
* All rights reserved.
|
|
*
|
|
* Licensed under the Oculus SDK License Agreement (the "License");
|
|
* you may not use the Oculus SDK except in compliance with the License,
|
|
* which is provided at the time of installation or download, or which
|
|
* otherwise accompanies this software in either electronic or hard copy form.
|
|
*
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* https://developer.oculus.com/licenses/oculussdk/
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
using Oculus.Interaction.Input;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
|
|
namespace Oculus.Interaction.PoseDetection
|
|
{
|
|
public enum FingerFeature
|
|
{
|
|
[Tooltip(FingerFeatureProperties.FeatureCurlShortHelpText)]
|
|
Curl,
|
|
|
|
[Tooltip(FingerFeatureProperties.FeatureFlexionShortHelpText)]
|
|
Flexion,
|
|
|
|
[Tooltip(FingerFeatureProperties.FeatureAbductionShortHelpText)]
|
|
Abduction,
|
|
|
|
[Tooltip(FingerFeatureProperties.FeatureOppositionShortHelpText)]
|
|
Opposition
|
|
}
|
|
|
|
public class FingerShapes
|
|
{
|
|
#region Joints Visualization Mappings
|
|
private static readonly HandJointId[][] CURL_LINE_JOINTS =
|
|
{
|
|
new [] {HandJointId.HandThumb2, HandJointId.HandThumb3, HandJointId.HandThumbTip},
|
|
new [] {HandJointId.HandIndex2, HandJointId.HandIndex3, HandJointId.HandIndexTip},
|
|
new [] {HandJointId.HandMiddle2, HandJointId.HandMiddle3, HandJointId.HandMiddleTip},
|
|
new [] {HandJointId.HandRing2, HandJointId.HandRing3, HandJointId.HandRingTip},
|
|
new [] {HandJointId.HandPinky2, HandJointId.HandPinky3, HandJointId.HandPinkyTip}
|
|
};
|
|
private static readonly HandJointId[][] FLEXION_LINE_JOINTS =
|
|
{
|
|
new [] {HandJointId.HandThumb1, HandJointId.HandThumb2, HandJointId.HandThumb3},
|
|
new [] {HandJointId.HandIndex1, HandJointId.HandIndex2, HandJointId.HandIndex3},
|
|
new [] {HandJointId.HandMiddle1, HandJointId.HandMiddle2, HandJointId.HandMiddle3},
|
|
new [] {HandJointId.HandRing1, HandJointId.HandRing2, HandJointId.HandRing3},
|
|
new [] {HandJointId.HandPinky1, HandJointId.HandPinky2, HandJointId.HandPinky3}
|
|
};
|
|
private static readonly HandJointId[][] ABDUCTION_LINE_JOINTS =
|
|
{
|
|
new [] {HandJointId.HandThumbTip, HandJointId.HandThumb1, HandJointId.HandIndex1, HandJointId.HandIndexTip},
|
|
new [] {HandJointId.HandIndexTip, HandJointId.HandIndex1, HandJointId.HandMiddle1, HandJointId.HandMiddleTip},
|
|
new [] {HandJointId.HandMiddleTip, HandJointId.HandMiddle1, HandJointId.HandRing1, HandJointId.HandRingTip},
|
|
new [] {HandJointId.HandRingTip, HandJointId.HandRing1, HandJointId.HandPinky1, HandJointId.HandPinkyTip},
|
|
Array.Empty<HandJointId>()
|
|
};
|
|
private static readonly HandJointId[][] OPPOSITION_LINE_JOINTS =
|
|
{
|
|
Array.Empty<HandJointId>(),
|
|
new [] {HandJointId.HandThumbTip, HandJointId.HandIndexTip},
|
|
new [] {HandJointId.HandThumbTip, HandJointId.HandMiddleTip},
|
|
new [] {HandJointId.HandThumbTip, HandJointId.HandRingTip},
|
|
new [] {HandJointId.HandThumbTip, HandJointId.HandPinkyTip},
|
|
};
|
|
#endregion
|
|
|
|
#region Joint Calculation Mappings
|
|
private static readonly HandJointId[][] CURL_ANGLE_JOINTS =
|
|
{
|
|
new[]
|
|
{
|
|
HandJointId.HandThumb1, HandJointId.HandThumb2, HandJointId.HandThumb3,
|
|
HandJointId.HandThumbTip
|
|
},
|
|
new[]
|
|
{
|
|
HandJointId.HandIndex1, HandJointId.HandIndex2, HandJointId.HandIndex3,
|
|
HandJointId.HandIndexTip
|
|
},
|
|
new[]
|
|
{
|
|
HandJointId.HandMiddle1, HandJointId.HandMiddle2, HandJointId.HandMiddle3,
|
|
HandJointId.HandMiddleTip
|
|
},
|
|
new[]
|
|
{
|
|
HandJointId.HandRing1, HandJointId.HandRing2, HandJointId.HandRing3,
|
|
HandJointId.HandRingTip
|
|
},
|
|
new[]
|
|
{
|
|
HandJointId.HandPinky1, HandJointId.HandPinky2, HandJointId.HandPinky3,
|
|
HandJointId.HandPinkyTip
|
|
}
|
|
};
|
|
|
|
#endregion
|
|
|
|
public virtual float GetValue(HandFinger finger, FingerFeature feature, IHand hand)
|
|
{
|
|
switch (feature)
|
|
{
|
|
case FingerFeature.Curl:
|
|
return GetCurlValue(finger, hand);
|
|
case FingerFeature.Flexion:
|
|
return GetFlexionValue(finger, hand);
|
|
case FingerFeature.Abduction:
|
|
return GetAbductionValue(finger, hand);
|
|
case FingerFeature.Opposition:
|
|
return GetOppositionValue(finger, hand);
|
|
|
|
default:
|
|
return 0.0f;
|
|
}
|
|
}
|
|
|
|
private static float PosesCurlValue(Pose p0, Pose p1, Pose p2)
|
|
{
|
|
Vector3 bone1 = p0.position - p1.position;
|
|
Vector3 bone2 = p2.position - p1.position;
|
|
Vector3 hinge = p1.rotation * Constants.LeftThumbSide;
|
|
float angle = Vector3.SignedAngle(bone1, bone2, hinge);
|
|
if (angle < 0f) angle += 360f;
|
|
return angle;
|
|
}
|
|
|
|
public static float PosesListCurlValue(Pose[] poses)
|
|
{
|
|
float angleSum = 0;
|
|
for (int i = 0; i < poses.Length - 2; i++)
|
|
{
|
|
angleSum += PosesCurlValue(poses[i], poses[i + 1], poses[i + 2]);
|
|
}
|
|
return angleSum;
|
|
}
|
|
|
|
protected float JointsCurlValue(HandJointId[] joints, IHand hand)
|
|
{
|
|
if (!hand.GetJointPosesFromWrist(out ReadOnlyHandJointPoses poses))
|
|
{
|
|
return 0.0f;
|
|
}
|
|
|
|
float angleSum = 0;
|
|
for (int i = 0; i < joints.Length - 2; i++)
|
|
{
|
|
angleSum += PosesCurlValue(poses[(int)joints[i]],
|
|
poses[(int)joints[i + 1]],
|
|
poses[(int)joints[i + 2]]);
|
|
}
|
|
return angleSum;
|
|
}
|
|
|
|
public float GetCurlValue(HandFinger finger, IHand hand)
|
|
{
|
|
HandJointId[] handJointIds = CURL_ANGLE_JOINTS[(int)finger];
|
|
return JointsCurlValue(handJointIds, hand) / (handJointIds.Length - 2);
|
|
}
|
|
|
|
public float GetFlexionValue(HandFinger finger, IHand hand)
|
|
{
|
|
if (!hand.GetJointPosesFromWrist(out ReadOnlyHandJointPoses poses))
|
|
{
|
|
return 0.0f;
|
|
}
|
|
|
|
HandJointId knuckle = HandJointUtils.GetHandFingerProximal(finger);
|
|
Vector3 handDir = Constants.RightDorsal;
|
|
Vector3 knucklesUp = poses[knuckle].rotation * Constants.RightDorsal;
|
|
Vector3 fingerDir = Vector3.ProjectOnPlane(knucklesUp, Constants.RightThumbSide);
|
|
return 180f + Vector3.SignedAngle(handDir, fingerDir, Constants.RightPinkySide);
|
|
}
|
|
|
|
public float GetAbductionValue(HandFinger finger, IHand hand)
|
|
{
|
|
if (finger == HandFinger.Pinky
|
|
|| !hand.GetJointPosesFromWrist(out ReadOnlyHandJointPoses poses))
|
|
{
|
|
return 0.0f;
|
|
}
|
|
|
|
HandFinger nextFinger = finger + 1;
|
|
Vector3 fingerProximal = poses[HandJointUtils.GetHandFingerProximal(finger)].position;
|
|
Vector3 proximalMidpoint = Vector3.Lerp(
|
|
fingerProximal,
|
|
poses[HandJointUtils.GetHandFingerProximal(nextFinger)].position,
|
|
0.5f);
|
|
Vector3 normal1;
|
|
if (finger == HandFinger.Thumb)
|
|
{
|
|
normal1 = poses[HandJointUtils.GetHandFingerTip(finger)].position -
|
|
fingerProximal;
|
|
}
|
|
else
|
|
{
|
|
normal1 = poses[HandJointUtils.GetHandFingerTip(finger)].position -
|
|
proximalMidpoint;
|
|
}
|
|
|
|
Vector3 normal2 = poses[HandJointUtils.GetHandFingerTip(nextFinger)].position -
|
|
proximalMidpoint;
|
|
Vector3 axis = Vector3.Cross(normal1, normal2);
|
|
return Vector3.SignedAngle(normal1, normal2, axis);
|
|
}
|
|
|
|
public float GetOppositionValue(HandFinger finger, IHand hand)
|
|
{
|
|
if (finger == HandFinger.Thumb
|
|
|| !hand.GetJointPosesFromWrist(out ReadOnlyHandJointPoses poses))
|
|
{
|
|
return 0.0f;
|
|
}
|
|
|
|
Vector3 pos1 = poses[HandJointUtils.GetHandFingerTip(finger)].position;
|
|
Vector3 pos2 = poses[HandJointId.HandThumbTip].position;
|
|
return Vector3.Magnitude(pos1 - pos2);
|
|
}
|
|
|
|
public virtual IReadOnlyList<HandJointId> GetJointsAffected(HandFinger finger, FingerFeature feature)
|
|
{
|
|
switch (feature)
|
|
{
|
|
case FingerFeature.Curl:
|
|
return CURL_LINE_JOINTS[(int)finger];
|
|
case FingerFeature.Flexion:
|
|
return FLEXION_LINE_JOINTS[(int)finger];
|
|
case FingerFeature.Abduction:
|
|
return ABDUCTION_LINE_JOINTS[(int)finger];
|
|
case FingerFeature.Opposition:
|
|
return OPPOSITION_LINE_JOINTS[(int)finger];
|
|
default:
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
}
|