247 lines
8.6 KiB
C#
247 lines
8.6 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using UnityEngine.XR.Hands;
|
|
|
|
|
|
#if UNITY_EDITOR
|
|
using UnityEditor;
|
|
#endif
|
|
|
|
namespace XRMultiplayer
|
|
{
|
|
/// <summary>
|
|
/// This class controls the curl of the fingers based on hand tracking or controller tracking.
|
|
/// </summary>
|
|
public class JointBasedHand : MonoBehaviour
|
|
{
|
|
/// <summary>
|
|
/// Controls how the fingers are updated.
|
|
/// Curl allows for a much more lightweight approximation of finger movements.
|
|
/// </summary>
|
|
public bool useCurl
|
|
{
|
|
get => m_UseCurl;
|
|
set => m_UseCurl = value;
|
|
}
|
|
bool m_UseCurl;
|
|
|
|
/// <summary>
|
|
/// Controls the level of fidelity for fingers.
|
|
/// </summary>
|
|
/// <remarks>Set from <see cref="XRHandPoseReplicator.SetFidelity(int)"/>.</remarks>
|
|
public int fidelityLevel
|
|
{
|
|
get => m_FidelityLevel;
|
|
set => m_FidelityLevel = value;
|
|
}
|
|
int m_FidelityLevel;
|
|
|
|
[Header("Setup Settings")]
|
|
/// <summary>
|
|
/// Specify where the root of the hand is.
|
|
/// </summary>
|
|
[SerializeField, Tooltip("Specify where the root of the hand is.")]
|
|
protected Transform m_HandRoot;
|
|
|
|
/// <summary>
|
|
/// Specify the names of the fingers.
|
|
/// </summary>
|
|
[SerializeField, Tooltip("Specify the names of the fingers.")]
|
|
protected string[] m_FingerNames = { "Thumb", "Index", "Middle", "Ring", "Little" };
|
|
|
|
/// <summary>
|
|
/// Specify the start index of the finger joints.
|
|
/// </summary>
|
|
[SerializeField, Tooltip("Specify the start index of the finger joints.")]
|
|
protected XRHandJointID[] m_FingerStartJointIds = { XRHandJointID.ThumbMetacarpal, XRHandJointID.IndexMetacarpal, XRHandJointID.MiddleMetacarpal, XRHandJointID.RingMetacarpal, XRHandJointID.LittleMetacarpal };
|
|
|
|
[Header("Hand Fidelity Options")]
|
|
/// <summary>
|
|
/// Groups of <see cref="JointToTransformReference"/>.
|
|
/// </summary>
|
|
[Tooltip("Groups of Joint To Transform References. Use Context Menu for auto generation.")]
|
|
public HandFidelityOption[] handFidelityOptions;
|
|
|
|
/// <summary>
|
|
/// Sets the Min/Max euler rotation of the fingers.
|
|
/// </summary>
|
|
[SerializeField, Tooltip("Sets the Min/Max euler rotation of the fingers.")]
|
|
protected Vector2 m_MinMaxEulerX = new Vector2(0, 100);
|
|
|
|
|
|
//3, 4, 5 -- Thumb
|
|
//7, 8, 9, 10 -- Index
|
|
//12, 13, 14, 15 -- Middle
|
|
//17, 18, 19, 20 -- Ring
|
|
//22, 23, 24, 25 -- Little
|
|
|
|
/// <inheritdoc/>
|
|
private void Update()
|
|
{
|
|
if (!m_UseCurl) return;
|
|
|
|
m_FidelityLevel = Mathf.Clamp(m_FidelityLevel, 0, handFidelityOptions.Length);
|
|
foreach (var joint in handFidelityOptions[m_FidelityLevel].fingerJoints)
|
|
{
|
|
foreach (var finger in joint.jointTransformReferences)
|
|
{
|
|
Vector3 rot = Vector3.zero;
|
|
rot.x = Mathf.Lerp(m_MinMaxEulerX.x, m_MinMaxEulerX.y, joint.curlAmount);
|
|
finger.jointTransform.localRotation = Quaternion.Euler(rot);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Controls the Curl level of fingers.
|
|
/// </summary>
|
|
/// <remarks>Called from <see cref="XRHandPoseReplicator.GetNetworkCurl()"/>.</remarks>
|
|
/// <param name="fingerID">ID of the specific finger.</param>
|
|
/// <param name="curlAmount">Amount to curl the finger.</param>
|
|
public void SetCurl(int fingerID, float curlAmount)
|
|
{
|
|
handFidelityOptions[m_FidelityLevel].fingerJoints[fingerID].curlAmount = curlAmount;
|
|
}
|
|
|
|
#if UNITY_EDITOR
|
|
/// <summary>
|
|
/// Clears the hand references.
|
|
/// </summary>
|
|
public void ClearHandReferences()
|
|
{
|
|
handFidelityOptions = new HandFidelityOption[3];
|
|
}
|
|
|
|
/// <summary>
|
|
/// Attempts to find and automatically assign the hand references.
|
|
/// </summary>
|
|
[ContextMenu("Setup Hand References")]
|
|
public void SetupHandReferences()
|
|
{
|
|
try
|
|
{
|
|
handFidelityOptions = new HandFidelityOption[3];
|
|
|
|
for (int i = 0; i < handFidelityOptions.Length; i++)
|
|
{
|
|
handFidelityOptions[i].fingerJoints = new FingerJoints[i <= 1 ? 5 : 3];
|
|
|
|
for (int j = 0; j < handFidelityOptions[i].fingerJoints.Length; j++)
|
|
{
|
|
handFidelityOptions[i].fingerJoints[j].fingerName = m_FingerNames[j];
|
|
handFidelityOptions[i].fingerJoints[j].jointTransformReferences = new List<JointToTransformReference>();
|
|
|
|
int jointDepth = i == 0 ? 4 : 3;
|
|
if(j == 0) jointDepth -= 1; // Thumb has 1 less joint than the other fingers
|
|
|
|
int startDepth = i == 0 ? 0 : 1;
|
|
|
|
handFidelityOptions[i].fingerJoints[j].jointTransformReferences = GetFingerJoints(m_FingerNames[j], startDepth, jointDepth, m_FingerStartJointIds[j]);
|
|
}
|
|
|
|
//Get extra fingers as mittens
|
|
if(i == 2)
|
|
{
|
|
handFidelityOptions[i].fingerJoints[2].jointTransformReferences.AddRange(GetFingerJoints(m_FingerNames[3], 1, 3, m_FingerStartJointIds[3]));
|
|
handFidelityOptions[i].fingerJoints[2].jointTransformReferences.AddRange(GetFingerJoints(m_FingerNames[4], 1, 3, m_FingerStartJointIds[4]));
|
|
}
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Utils.LogError($"Error in FindNetworkHandReferences: {e}");
|
|
}
|
|
}
|
|
|
|
List<JointToTransformReference> GetFingerJoints(string fingerName, int startDepth, int jointDepth, XRHandJointID fingerStartJointId)
|
|
{
|
|
try
|
|
{
|
|
List<JointToTransformReference> fingerJoints = new();
|
|
JointToTransformReference currentJoint = new();
|
|
|
|
foreach (Transform child in m_HandRoot)
|
|
{
|
|
if (child.name.Contains(fingerName))
|
|
{
|
|
Transform currentChild = child;
|
|
|
|
//Navigate to the starting joint based on the startDepth
|
|
for(int i = 0; i < startDepth; i++)
|
|
{
|
|
currentChild = currentChild.GetChild(0);
|
|
}
|
|
|
|
// Get all joints in the finger and add them to the list based on the jointDepth
|
|
for (int i = 0; i < jointDepth; i++)
|
|
{
|
|
currentJoint.jointTransform = currentChild;
|
|
int currentHandJointId = (int)fingerStartJointId + i + startDepth;
|
|
currentJoint.xrHandJointID = (XRHandJointID)currentHandJointId;
|
|
fingerJoints.Add(currentJoint);
|
|
currentChild = currentChild.GetChild(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
return fingerJoints;
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Utils.LogError($"Error in GetFingerJoints: {e}");
|
|
return null;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
}
|
|
|
|
[Serializable]
|
|
public struct HandFidelityOption
|
|
{
|
|
public FingerJoints[] fingerJoints;
|
|
}
|
|
|
|
[Serializable]
|
|
public struct FingerJoints
|
|
{
|
|
/// <summary>
|
|
/// Finger Name.
|
|
/// </summary>
|
|
public string fingerName;
|
|
|
|
/// <summary>
|
|
/// The current curl amount of the finger.
|
|
/// </summary>
|
|
[Range(0.0f, 1.0f)]
|
|
public float curlAmount;
|
|
|
|
/// <summary>
|
|
/// <see cref="JointToTransformReference"/> List.
|
|
/// </summary>
|
|
public List<JointToTransformReference> jointTransformReferences;
|
|
}
|
|
|
|
#if UNITY_EDITOR
|
|
[CustomEditor(typeof(JointBasedHand))]
|
|
public class HandCurlerEditor : Editor
|
|
{
|
|
public override void OnInspectorGUI()
|
|
{
|
|
DrawDefaultInspector();
|
|
JointBasedHand myScript = (JointBasedHand)target;
|
|
if (GUILayout.Button("Setup References"))
|
|
{
|
|
myScript.SetupHandReferences();
|
|
}
|
|
|
|
if (GUILayout.Button("Clear Hand References"))
|
|
{
|
|
myScript.ClearHandReferences();
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|