// -------------------------------------------------------------------------------------------------------------------- // // Copyright (c) VRMADA, All rights reserved. // // -------------------------------------------------------------------------------------------------------------------- using UltimateXR.Core; using UltimateXR.Core.Math; using UltimateXR.Manipulation.HandPoses; using UnityEngine; namespace UltimateXR.Avatar.Rig { /// /// Runtime, lightweight version of . See . /// public class UxrRuntimeFingerDescriptor { #region Public Types & Data /// /// Gets whether the descriptor contains metacarpal information. /// public bool HasMetacarpalInfo { get; private set; } /// /// Gets the metacarpal local rotation. /// public Quaternion MetacarpalRotation { get; private set; } /// /// Gets the proximal local rotation. /// public Quaternion ProximalRotation { get; private set; } /// /// Gets the intermediate local rotation. /// public Quaternion IntermediateRotation { get; private set; } /// /// Gets the proximal local rotation. /// public Quaternion DistalRotation { get; private set; } #endregion #region Constructors & Finalizer /// /// Default constructor. /// public UxrRuntimeFingerDescriptor() { } /// /// Constructor. /// /// Avatar to compute the runtime finger descriptor for /// Which hand to process /// The source data /// Which finger to store public UxrRuntimeFingerDescriptor(UxrAvatar avatar, UxrHandSide handSide, UxrHandDescriptor handDescriptor, UxrFingerType fingerType) { UxrAvatarHand avatarHand = avatar.GetHand(handSide); UxrAvatarFinger avatarFinger = avatarHand.GetFinger(fingerType); UxrUniversalLocalAxes handLocalAxes = avatar.AvatarRigInfo.GetArmInfo(handSide).HandUniversalLocalAxes; UxrUniversalLocalAxes fingerLocalAxes = avatar.AvatarRigInfo.GetArmInfo(handSide).FingerUniversalLocalAxes; UxrFingerDescriptor fingerDescriptor = handDescriptor.GetFinger(fingerType); HasMetacarpalInfo = fingerDescriptor.HasMetacarpalInfo && avatarFinger.Metacarpal != null; Quaternion metacarpalWorldRotation; Quaternion proximalWorldRotation; // Compute world rotations if (HasMetacarpalInfo) { metacarpalWorldRotation = GetRotation(avatarHand.Wrist, avatarFinger.Metacarpal, fingerDescriptor.Metacarpal, handLocalAxes, fingerLocalAxes); proximalWorldRotation = GetRotation(avatarFinger.Metacarpal, avatarFinger.Proximal, fingerDescriptor.Proximal, fingerLocalAxes, fingerLocalAxes); } else { metacarpalWorldRotation = Quaternion.identity; proximalWorldRotation = GetRotation(avatarHand.Wrist, avatarFinger.Proximal, fingerDescriptor.ProximalNoMetacarpal, handLocalAxes, fingerLocalAxes); } Quaternion intermediateWorldRotation = GetRotation(avatarFinger.Proximal, avatarFinger.Intermediate, fingerDescriptor.Intermediate, fingerLocalAxes, fingerLocalAxes); Quaternion distalWorldRotation = GetRotation(avatarFinger.Intermediate, avatarFinger.Distal, fingerDescriptor.Distal, fingerLocalAxes, fingerLocalAxes); // Compute relative rotations if (HasMetacarpalInfo) { MetacarpalRotation = Quaternion.Inverse(avatarHand.Wrist.rotation) * metacarpalWorldRotation; ProximalRotation = Quaternion.Inverse(avatarFinger.Metacarpal.rotation) * proximalWorldRotation; } else { MetacarpalRotation = Quaternion.identity; ProximalRotation = Quaternion.Inverse(avatarHand.Wrist.rotation) * proximalWorldRotation; } IntermediateRotation = Quaternion.Inverse(avatarFinger.Proximal.rotation) * intermediateWorldRotation; DistalRotation = Quaternion.Inverse(avatarFinger.Intermediate.rotation) * distalWorldRotation; } /// /// Constructor. /// /// Whether the finger contains metacarpal information /// Metacarpal local rotation (optional) /// Proximal local rotation /// Intermediate local rotation /// Distal local rotation public UxrRuntimeFingerDescriptor(bool hasMetacarpalInfo, Quaternion metacarpalRotation, Quaternion proximalRotation, Quaternion intermediateRotation, Quaternion distalRotation) { HasMetacarpalInfo = hasMetacarpalInfo; MetacarpalRotation = metacarpalRotation; ProximalRotation = proximalRotation; IntermediateRotation = intermediateRotation; DistalRotation = distalRotation; } #endregion #region Public Methods /// /// Copies the data from another descriptor. /// /// Descriptor to copy the data from public void CopyFrom(UxrRuntimeFingerDescriptor fingerDescriptor) { if (fingerDescriptor == null) { return; } HasMetacarpalInfo = fingerDescriptor.HasMetacarpalInfo; MetacarpalRotation = fingerDescriptor.MetacarpalRotation; ProximalRotation = fingerDescriptor.ProximalRotation; IntermediateRotation = fingerDescriptor.IntermediateRotation; DistalRotation = fingerDescriptor.DistalRotation; } /// /// Interpolates towards another runtime finger descriptor. /// /// Runtime finger descriptor /// Interpolation value [0.0, 1.0] public void InterpolateTo(UxrRuntimeFingerDescriptor fingerDescriptor, float blend) { if (fingerDescriptor == null) { return; } if (HasMetacarpalInfo && fingerDescriptor.HasMetacarpalInfo) { MetacarpalRotation = Quaternion.Slerp(MetacarpalRotation, fingerDescriptor.MetacarpalRotation, blend); } ProximalRotation = Quaternion.Slerp(ProximalRotation, fingerDescriptor.ProximalRotation, blend); IntermediateRotation = Quaternion.Slerp(IntermediateRotation, fingerDescriptor.IntermediateRotation, blend); DistalRotation = Quaternion.Slerp(DistalRotation, fingerDescriptor.DistalRotation, blend); } #endregion #region Private Methods /// /// Gets the local rotation of a when applied to an object. /// /// Parent the node descriptor references its rotation to /// Transform to get the local rotation of /// /// Bone information in the well-known coordinate system of a /// /// Coordinate system of the transform /// Coordinate system of the transform /// /// Local rotation that should be applied to when using /// /// private static Quaternion GetRotation(Transform parent, Transform node, UxrFingerNodeDescriptor nodeDescriptor, UxrUniversalLocalAxes parentLocalAxes, UxrUniversalLocalAxes nodeLocalAxes) { Matrix4x4 nodeLocalAxesMatrix = new Matrix4x4(); nodeLocalAxesMatrix.SetColumn(0, nodeLocalAxes.LocalRight); nodeLocalAxesMatrix.SetColumn(1, nodeLocalAxes.LocalUp); nodeLocalAxesMatrix.SetColumn(2, nodeLocalAxes.LocalForward); nodeLocalAxesMatrix.SetColumn(3, new Vector4(0, 0, 0, 1)); Quaternion nodeUniversalToActual = Quaternion.Inverse(nodeLocalAxesMatrix.rotation); Matrix4x4 parentUniversalMatrix = new Matrix4x4(); parentUniversalMatrix.SetColumn(0, parent.TransformVector(parentLocalAxes.LocalRight)); parentUniversalMatrix.SetColumn(1, parent.TransformVector(parentLocalAxes.LocalUp)); parentUniversalMatrix.SetColumn(2, parent.TransformVector(parentLocalAxes.LocalForward)); parentUniversalMatrix.SetColumn(3, new Vector4(0, 0, 0, 1)); Matrix4x4 nodeUniversalMatrix = new Matrix4x4(); nodeUniversalMatrix.SetColumn(0, parentUniversalMatrix.MultiplyVector(nodeDescriptor.Right)); nodeUniversalMatrix.SetColumn(1, parentUniversalMatrix.MultiplyVector(nodeDescriptor.Up)); nodeUniversalMatrix.SetColumn(2, parentUniversalMatrix.MultiplyVector(nodeDescriptor.Forward)); nodeUniversalMatrix.SetColumn(3, new Vector4(0, 0, 0, 1)); return nodeUniversalMatrix.rotation * nodeUniversalToActual; } #endregion } }