// --------------------------------------------------------------------------------------------------------------------
//
// Copyright (c) VRMADA, All rights reserved.
//
// --------------------------------------------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using UltimateXR.Core;
using UltimateXR.Extensions.Unity.Math;
using UnityEngine;
namespace UltimateXR.Avatar.Rig
{
///
/// Stores bone references of an Avatar's hand.
///
[Serializable]
public class UxrAvatarHand
{
#region Inspector Properties/Serialized Fields
[SerializeField] private Transform _wrist;
[SerializeField] private UxrAvatarFinger _thumb;
[SerializeField] private UxrAvatarFinger _index;
[SerializeField] private UxrAvatarFinger _middle;
[SerializeField] private UxrAvatarFinger _ring;
[SerializeField] private UxrAvatarFinger _little;
#endregion
#region Public Types & Data
///
/// Gets a sequence of all the non-null transforms in the hand, including the wrist.
///
public IEnumerable Transforms
{
get
{
if (Wrist != null)
{
yield return Wrist;
}
foreach (Transform transform in FingerTransforms)
{
yield return transform;
}
}
}
///
/// Gets a sequence of all the non-null finger transforms in the hand.
///
public IEnumerable FingerTransforms
{
get
{
foreach (Transform transform in Index.Transforms)
{
yield return transform;
}
foreach (Transform transform in Middle.Transforms)
{
yield return transform;
}
foreach (Transform transform in Ring.Transforms)
{
yield return transform;
}
foreach (Transform transform in Little.Transforms)
{
yield return transform;
}
foreach (Transform transform in Thumb.Transforms)
{
yield return transform;
}
}
}
///
/// Gets or sets the wrist transform. The wrist is the root transform in the hand.
///
public Transform Wrist
{
get => _wrist;
set => _wrist = value;
}
///
/// Gets or sets the thumb finger.
///
public UxrAvatarFinger Thumb
{
get => _thumb;
set => _thumb = value;
}
///
/// Gets or sets the index finger.
///
public UxrAvatarFinger Index
{
get => _index;
set => _index = value;
}
///
/// Gets or sets the middle finger.
///
public UxrAvatarFinger Middle
{
get => _middle;
set => _middle = value;
}
///
/// Gets or sets the ring finger.
///
public UxrAvatarFinger Ring
{
get => _ring;
set => _ring = value;
}
///
/// Gets or sets the little finger.
///
public UxrAvatarFinger Little
{
get => _little;
set => _little = value;
}
#endregion
#region Constructors & Finalizer
///
/// Default constructor.
///
public UxrAvatarHand()
{
_thumb = new UxrAvatarFinger();
_index = new UxrAvatarFinger();
_middle = new UxrAvatarFinger();
_ring = new UxrAvatarFinger();
_little = new UxrAvatarFinger();
}
#endregion
#region Public Methods
///
/// Checks if the hand has all finger references plus the wrist.
///
/// Whether the hand has all finger bone data plus the wrist.
public bool HasFullHandData()
{
return Wrist != null && Thumb.HasData() && Index.HasData() && Middle.HasData() && Ring.HasData() && Little.HasData();
}
///
/// Checks if the hand has all finger references.
///
/// Whether the hand has all finger bone data.
public bool HasFingerData()
{
return Thumb.HasData() && Index.HasData() && Middle.HasData() && Ring.HasData() && Little.HasData();
}
///
/// Gets the information of a given finger.
///
/// Finger to get
/// Finger information
public UxrAvatarFinger GetFinger(UxrFingerType fingerType)
{
switch (fingerType)
{
case UxrFingerType.Thumb: return Thumb;
case UxrFingerType.Index: return Index;
case UxrFingerType.Middle: return Middle;
case UxrFingerType.Ring: return Ring;
case UxrFingerType.Little: return Little;
default: throw new ArgumentOutOfRangeException(nameof(fingerType), fingerType, null);
}
}
///
/// Tries to compute the palm center in world coordinates.
///
/// Returns the palm center in world coordinates
/// Whether the center could be computed. False if some required bone references are missing
public bool GetPalmCenter(out Vector3 center)
{
center = Vector3.zero;
if (Wrist == null || Index.Proximal == null || Little.Proximal == null)
{
return false;
}
Vector3 a = Vector3.zero;
Vector3 b = Wrist.InverseTransformPoint(Index.Proximal.position);
Vector3 c = Wrist.InverseTransformPoint(Little.Proximal.position);
center = _wrist.TransformPoint(Vector3Ext.Average(a, b, c));
return true;
}
///
/// Tries to compute the direction that goes out of the palm in world coordinates.
///
/// Which hand it is
/// Returns the palm vector in world coordinates
/// Whether the vector could be computed. False if some required bone references are missing
public bool GetPalmOutDirection(UxrHandSide handSide, out Vector3 direction)
{
direction = Vector3.zero;
if (Wrist == null || Index.Proximal == null || Little.Proximal == null)
{
return false;
}
Vector3 localPalmToIndex = Wrist.InverseTransformPoint(Index.Proximal.position).normalized;
Vector3 localPalmToLittle = Wrist.InverseTransformPoint(Little.Proximal.position).normalized;
direction = Wrist.TransformDirection(Vector3.Cross(localPalmToIndex, localPalmToLittle)).normalized;
if (handSide == UxrHandSide.Right)
{
direction = -direction;
}
return true;
}
///
/// Tries to compute the palm-to-finger direction in world coordinates.
///
/// Returns the palm-to-finger direction in world coordinates
/// Whether the vector could be computed. False if some required bone references are missing
public bool GetPalmToFingerDirection(out Vector3 direction)
{
direction = Vector3.zero;
if (Wrist == null || Index.Proximal == null || Little.Proximal == null)
{
return false;
}
direction = ((Index.Proximal.position + Little.Proximal.position) * 0.5f - _wrist.position).normalized;
return true;
}
#endregion
}
}