Add ultimate xr

This commit is contained in:
2024-08-06 21:58:35 +02:00
parent 864033bf10
commit 7165bacd9d
3952 changed files with 2162037 additions and 35 deletions

View File

@@ -0,0 +1,108 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="UxrAvatarArm.cs" company="VRMADA">
// Copyright (c) VRMADA, All rights reserved.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using UnityEngine;
namespace UltimateXR.Avatar.Rig
{
/// <summary>
/// Stores bone references of an Avatar's arm.
/// </summary>
[Serializable]
public class UxrAvatarArm
{
#region Inspector Properties/Serialized Fields
[SerializeField] private Transform _clavicle;
[SerializeField] private Transform _upperArm;
[SerializeField] private Transform _forearm;
[SerializeField] private UxrAvatarHand _hand;
#endregion
#region Public Types & Data
/// <summary>
/// Gets a sequence of all the non-null transforms in the arm.
/// </summary>
public IEnumerable<Transform> Transforms
{
get
{
if (Clavicle != null)
{
yield return Clavicle;
}
if (UpperArm != null)
{
yield return UpperArm;
}
if (Forearm != null)
{
yield return Forearm;
}
foreach (Transform transform in _hand.Transforms)
{
yield return transform;
}
}
}
/// <summary>
/// Gets or sets the clavicle transform.
/// </summary>
public Transform Clavicle
{
get => _clavicle;
set => _clavicle = value;
}
/// <summary>
/// Gets or sets the upper arm transform.
/// </summary>
public Transform UpperArm
{
get => _upperArm;
set => _upperArm = value;
}
/// <summary>
/// Gets or sets the forearm transform.
/// </summary>
public Transform Forearm
{
get => _forearm;
set => _forearm = value;
}
/// <summary>
/// Gets or sets the hand.
/// </summary>
public UxrAvatarHand Hand
{
get => _hand;
set => _hand = value;
}
#endregion
#region Constructors & Finalizer
/// <summary>
/// Default constructor.
/// </summary>
public UxrAvatarArm()
{
_hand = new UxrAvatarHand();
}
#endregion
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: da57810a3b5b46bea5eeb76c7126a84e
timeCreated: 1642869983

View File

@@ -0,0 +1,308 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="UxrAvatarArmInfo.cs" company="VRMADA">
// Copyright (c) VRMADA, All rights reserved.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using UltimateXR.Core;
using UltimateXR.Core.Math;
using UltimateXR.Extensions.Unity.Math;
using UltimateXR.Manipulation;
using UnityEngine;
namespace UltimateXR.Avatar.Rig
{
/// <summary>
/// Stores information of an avatar rig's arm.
/// </summary>
[Serializable]
public class UxrAvatarArmInfo
{
#region Inspector Properties/Serialized Fields
[SerializeField] private UxrAvatar _avatar;
[SerializeField] private UxrHandSide _side;
[SerializeField] private float _upperArmLength;
[SerializeField] private float _forearmLength;
[SerializeField] private UxrUniversalLocalAxes _fingerUniversalLocalAxes;
[SerializeField] private UxrUniversalLocalAxes _handUniversalLocalAxes;
[SerializeField] private UxrUniversalLocalAxes _armUniversalLocalAxes;
[SerializeField] private UxrUniversalLocalAxes _forearmUniversalLocalAxes;
[SerializeField] private UxrUniversalLocalAxes _clavicleUniversalLocalAxes;
[SerializeField] private UxrAvatarFingerInfo _thumbInfo;
[SerializeField] private UxrAvatarFingerInfo _indexInfo;
[SerializeField] private UxrAvatarFingerInfo _middleInfo;
[SerializeField] private UxrAvatarFingerInfo _ringInfo;
[SerializeField] private UxrAvatarFingerInfo _littleInfo;
#endregion
#region Public Types & Data
/// <summary>
/// Gets the thumb finger information.
/// </summary>
public UxrAvatarFingerInfo ThumbInfo => _thumbInfo;
/// <summary>
/// Gets the index finger information.
/// </summary>
public UxrAvatarFingerInfo IndexInfo => _indexInfo;
/// <summary>
/// Gets the middle finger information.
/// </summary>
public UxrAvatarFingerInfo MiddleInfo => _middleInfo;
/// <summary>
/// Gets the ring finger information.
/// </summary>
public UxrAvatarFingerInfo RingInfo => _ringInfo;
/// <summary>
/// Gets the little finger information.
/// </summary>
public UxrAvatarFingerInfo LittleInfo => _littleInfo;
/// <summary>
/// Enumerates all the finger information.
/// </summary>
public IEnumerable<UxrAvatarFingerInfo> Fingers
{
get
{
yield return _thumbInfo;
yield return _indexInfo;
yield return _middleInfo;
yield return _ringInfo;
yield return _littleInfo;
}
}
/// <summary>
/// Gets the upper arm length. From shoulder to elbow.
/// </summary>
public float UpperArmLength
{
get => _upperArmLength;
private set => _upperArmLength = value;
}
/// <summary>
/// Gets the forearm length. From elbow to wrist.
/// </summary>
public float ForearmLength
{
get => _forearmLength;
private set => _forearmLength = value;
}
/// <summary>
/// Gets the universal coordinate system for the fingers: right = axis around which the finger curls, up =
/// knuckles up, forward = finger direction.
/// </summary>
public UxrUniversalLocalAxes FingerUniversalLocalAxes
{
get => _fingerUniversalLocalAxes;
private set => _fingerUniversalLocalAxes = value;
}
/// <summary>
/// Gets the universal coordinate system for the hand: right = from wrist to right (thumb direction), up = -palm
/// facing vector, forward = finger direction.
/// </summary>
public UxrUniversalLocalAxes HandUniversalLocalAxes
{
get => _handUniversalLocalAxes;
private set => _handUniversalLocalAxes = value;
}
/// <summary>
/// Gets the universal coordinate system for the arm: forward is arm->elbow, up is elbow rotation axis
/// </summary>
public UxrUniversalLocalAxes ArmUniversalLocalAxes
{
get => _armUniversalLocalAxes;
private set => _armUniversalLocalAxes = value;
}
/// <summary>
/// Gets the universal coordinate system for the forearm: forward is arm->hand, up is elbow rotation axis
/// </summary>
public UxrUniversalLocalAxes ForearmUniversalLocalAxes
{
get => _forearmUniversalLocalAxes;
private set => _forearmUniversalLocalAxes = value;
}
/// <summary>
/// Gets the universal coordinate system for the clavicle: forward is clavicle->arm, up is avatar up axis
/// </summary>
public UxrUniversalLocalAxes ClavicleUniversalLocalAxes
{
get => _clavicleUniversalLocalAxes;
private set => _clavicleUniversalLocalAxes = value;
}
// Updated every frame
/// <summary>
/// Gets the wrist torsion info.
/// </summary>
public UxrWristTorsionInfo WristTorsionInfo { get; private set; } = new UxrWristTorsionInfo();
#endregion
#region Internal Methods
/// <summary>
/// Computes and stores all the arm information of an avatar.
/// </summary>
/// <param name="avatar">Avatar whose arm to compute the information of</param>
/// <param name="side">Which side to compute</param>
internal void Compute(UxrAvatar avatar, UxrHandSide side)
{
_avatar = avatar;
_side = side;
SolveHandAndFingerAxes(avatar.GetHand(side), side);
ComputeArmRigInfo(avatar, avatar.GetArm(side), side);
}
#endregion
#region Private Methods
/// <summary>
/// Gets the outwards-pointing elbow rotation axis in world coordinates.
/// </summary>
/// <param name="avatar">The avatar the arm nodes belong to</param>
/// <param name="forearm">The arm's forearm transform</param>
/// <param name="armForward">The arm forward looking vector, the one pointing from shoulder to elbow</param>
/// <param name="forearmForward">The forearm forward looking vector, the one pointing from elbow to hand</param>
/// <returns>Elbow rotation axis</returns>
private static Vector3 GetWorldElbowAxis(UxrAvatar avatar, Transform forearm, Vector3 armForward, Vector3 forearmForward)
{
bool isLeft = avatar.transform.InverseTransformPoint(forearm.position).x < 0.0f;
float elbowAngle = Vector3.Angle(armForward, forearmForward);
Vector3 elbowAxis = Vector3.Cross(forearmForward, isLeft ? -armForward : armForward).normalized;
Transform leftHand = avatar.GetHandBone(UxrHandSide.Left);
Transform rightHand = avatar.GetHandBone(UxrHandSide.Right);
if (leftHand && rightHand)
{
Vector3 avatarRight = (rightHand.position - leftHand.position).normalized;
Vector3 forward = Vector3.Cross(avatarRight, Vector3.up);
elbowAxis = Vector3.Cross(isLeft ? forearmForward : -forearmForward, forward).normalized;
}
else if (elbowAngle < ElbowMinAngleThreshold)
{
// Assume T-pose if elbow angle is too small
elbowAxis = Vector3.up;
}
return forearm.TransformDirection(forearm.InverseTransformDirection(elbowAxis).GetClosestAxis());
}
/// <summary>
/// Tries to find out which axes are pointing right/up/forward in the hand and finger nodes. These "universal" axes
/// will be used to rotate the nodes, so that any coordinate system can be used no matter how the hand was authored.
/// </summary>
/// <param name="hand">The hand to compute the axes for</param>
/// <param name="side">Whether it is a left hand or right hand</param>
/// <returns>Boolean telling whether the axes could be solved. If any necessary transform is missing it will fail</returns>
private bool SolveHandAndFingerAxes(UxrAvatarHand hand, UxrHandSide side)
{
Transform indexProximal = hand.Index.Proximal;
Transform indexDistal = hand.Index.Distal;
Transform middleProximal = hand.Middle.Proximal;
Transform ringProximal = hand.Ring.Proximal;
if (!hand.Wrist || !indexProximal || !indexDistal || !middleProximal || !ringProximal)
{
return false;
}
float handCenter = 0.5f; // [0, 1]
Vector3 handAxesRight = hand.Wrist.InverseTransformDirection(indexProximal.position - middleProximal.position).GetClosestAxis() * (side == UxrHandSide.Left ? 1.0f : -1.0f);
Vector3 handAxesForward = hand.Wrist.InverseTransformDirection((Vector3.Lerp(ringProximal.position, middleProximal.position, handCenter) - hand.Wrist.position).normalized);
Vector3 handAxesUp = Vector3.Cross(handAxesForward, handAxesRight).normalized;
handAxesRight = Vector3.Cross(handAxesUp, handAxesForward).normalized;
HandUniversalLocalAxes = UxrUniversalLocalAxes.FromAxes(hand.Wrist, handAxesRight, handAxesUp, handAxesForward);
Vector3 fingerAxesRight = indexProximal.InverseTransformDirection(indexProximal.position - middleProximal.position).GetClosestAxis() * (side == UxrHandSide.Left ? 1.0f : -1.0f);
Vector3 fingerAxesForward = indexProximal.InverseTransformDirection(indexDistal.position - indexProximal.position).GetClosestAxis();
Vector3 fingerAxesUp = Vector3.Cross(fingerAxesForward, fingerAxesRight);
FingerUniversalLocalAxes = UxrUniversalLocalAxes.FromAxes(indexProximal, fingerAxesRight, fingerAxesUp, fingerAxesForward);
return true;
}
/// <summary>
/// Computes and stores information of the arm's rig.
/// </summary>
/// <param name="avatar">Avatar whose right arm to compute information of</param>
/// <param name="arm">Arm to compute the information of</param>
/// <param name="side">Which side it is</param>
private void ComputeArmRigInfo(UxrAvatar avatar, UxrAvatarArm arm, UxrHandSide side)
{
if (arm.UpperArm != null && arm.Forearm != null && arm.Hand.Wrist != null)
{
Vector3 armForward = (arm.Forearm.position - arm.UpperArm.position).normalized;
Vector3 forearmForward = (arm.Hand.Wrist.position - arm.Forearm.position).normalized;
Vector3 elbowAxis = GetWorldElbowAxis(avatar, arm.Forearm, armForward, forearmForward);
Vector3 armLocalForward = arm.UpperArm.InverseTransformDirection(armForward).GetClosestAxis();
Vector3 armLocalElbowAxis = arm.UpperArm.InverseTransformDirection(elbowAxis).GetClosestAxis();
Vector3 forearmLocalForward = arm.Forearm.InverseTransformDirection(forearmForward).GetClosestAxis();
Vector3 forearmLocalElbowAxis = arm.Forearm.InverseTransformDirection(elbowAxis).GetClosestAxis();
ArmUniversalLocalAxes = UxrUniversalLocalAxes.FromUpForward(arm.UpperArm, armLocalElbowAxis, armLocalForward);
ForearmUniversalLocalAxes = UxrUniversalLocalAxes.FromUpForward(arm.Forearm, armLocalElbowAxis, forearmLocalForward);
UpperArmLength = Vector3.Distance(arm.UpperArm.position, arm.Forearm.position);
ForearmLength = Vector3.Distance(arm.Forearm.position, arm.Hand.Wrist.position);
if (arm.Clavicle != null)
{
Vector3 clavicleForward = (arm.UpperArm.position - arm.Clavicle.position).normalized;
Vector3 clavicleLocalForwardAxis = arm.Clavicle.InverseTransformDirection(clavicleForward).GetClosestAxis();
Vector3 clavicleLocalUpAxis = arm.Clavicle.InverseTransformDirection(avatar.transform.up).GetClosestAxis();
ClavicleUniversalLocalAxes = UxrUniversalLocalAxes.FromUpForward(arm.Clavicle, clavicleLocalUpAxis, clavicleLocalForwardAxis);
}
}
UxrGrabber grabber = avatar.GetGrabber(side);
if (grabber && grabber.HandRenderer && grabber.HandRenderer is SkinnedMeshRenderer handRenderer)
{
_thumbInfo = new UxrAvatarFingerInfo();
_indexInfo = new UxrAvatarFingerInfo();
_middleInfo = new UxrAvatarFingerInfo();
_ringInfo = new UxrAvatarFingerInfo();
_littleInfo = new UxrAvatarFingerInfo();
_thumbInfo.Compute(avatar, handRenderer, side, UxrFingerType.Thumb);
_indexInfo.Compute(avatar, handRenderer, side, UxrFingerType.Index);
_middleInfo.Compute(avatar, handRenderer, side, UxrFingerType.Middle);
_ringInfo.Compute(avatar, handRenderer, side, UxrFingerType.Ring);
_littleInfo.Compute(avatar, handRenderer, side, UxrFingerType.Little);
}
}
#endregion
#region Private Types & Data
/// <summary>
/// Minimum angle between arm and forearm to compute elbow axis using cross product.
/// </summary>
private const float ElbowMinAngleThreshold = 3.0f;
#endregion
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b35d6ec8a0474d098337128d5632355a
timeCreated: 1665136779

View File

@@ -0,0 +1,138 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="UxrAvatarFinger.cs" company="VRMADA">
// Copyright (c) VRMADA, All rights reserved.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using UnityEngine;
namespace UltimateXR.Avatar.Rig
{
/// <summary>
/// Stores bone references of an Avatar's finger.
/// </summary>
[Serializable]
public class UxrAvatarFinger
{
#region Inspector Properties/Serialized Fields
[SerializeField] private Transform _metacarpal;
[SerializeField] private Transform _proximal;
[SerializeField] private Transform _intermediate;
[SerializeField] private Transform _distal;
#endregion
#region Public Types & Data
/// <summary>
/// Gets a sequence of all the non-null transforms in the finger.
/// </summary>
public IEnumerable<Transform> Transforms
{
get
{
if (Metacarpal != null)
{
yield return Metacarpal;
}
if (Proximal != null)
{
yield return Proximal;
}
if (Intermediate != null)
{
yield return Intermediate;
}
if (Distal != null)
{
yield return Distal;
}
}
}
/// <summary>
/// Gets or sets the metacarpal bone transform. Metacarpal bones are optional.
/// </summary>
public Transform Metacarpal
{
get => _metacarpal;
set => _metacarpal = value;
}
/// <summary>
/// Gets or sets the proximal bone transform.
/// </summary>
public Transform Proximal
{
get => _proximal;
set => _proximal = value;
}
/// <summary>
/// Gets or sets the intermediate bone transform.
/// </summary>
public Transform Intermediate
{
get => _intermediate;
set => _intermediate = value;
}
/// <summary>
/// Gets or sets the distal bone transform.
/// </summary>
public Transform Distal
{
get => _distal;
set => _distal = value;
}
#endregion
#region Public Methods
/// <summary>
/// Checks if the finger has the required bone references. The only optional bone is the metacarpal bone, which may be
/// null.
/// </summary>
/// <returns>Whether the finger has all the required bone data.</returns>
public bool HasData()
{
return Proximal != null && Intermediate != null && Distal != null;
}
/// <summary>
/// Sets up the finger bones using a list starting from the metacarpal (if there are 4 elements) or the proximal (if
/// there are 3).
/// </summary>
/// <param name="bones">Finger bone list. It may be either 4 or 3 bones, depending if the metacarpal bone is included.</param>
public void SetupFingerBones(List<Transform> bones)
{
if (Metacarpal == null && bones.Count == 4)
{
Metacarpal = bones[0];
}
if (Proximal == null)
{
Proximal = bones[bones.Count == 4 ? 1 : 0];
}
if (Intermediate == null)
{
Intermediate = bones[bones.Count == 4 ? 2 : 1];
}
if (Distal == null)
{
Distal = bones[bones.Count == 4 ? 3 : 2];
}
}
#endregion
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3e673c1d8af742c8860001ea223db873
timeCreated: 1642870254

View File

@@ -0,0 +1,75 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="UxrAvatarFingerBoneInfo.cs" company="VRMADA">
// Copyright (c) VRMADA, All rights reserved.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
using System;
using UltimateXR.Core.Math;
using UltimateXR.Extensions.Unity.Render;
using UnityEngine;
namespace UltimateXR.Avatar.Rig
{
/// <summary>
/// Stores information of the bone in an <see cref="UxrAvatarFingerInfo" />.
/// </summary>
[Serializable]
public class UxrAvatarFingerBoneInfo
{
#region Inspector Properties/Serialized Fields
[SerializeField] private float _length;
[SerializeField] private float _radius;
#endregion
#region Public Types & Data
/// <summary>
/// Gets the bone length.
/// </summary>
public float Length => _length;
/// <summary>
/// Gets the radius of the finger along this bone.
/// </summary>
public float Radius => _radius;
#endregion
#region Internal Methods
/// <summary>
/// Computes the bone length and radius.
/// </summary>
/// <param name="handRenderer">Hand renderer</param>
/// <param name="bone">Finger bone</param>
/// <param name="nextBone">Next finger bone downwards the hierarchy or null for the last bone</param>
/// <param name="universalFingerAxes">Finger universal coordinate system</param>
internal void Compute(SkinnedMeshRenderer handRenderer, Transform bone, Transform nextBone, UxrUniversalLocalAxes universalFingerAxes)
{
// Compute bounds representing the influenced part in the mesh in local bone coordinates.
Bounds localBounds = MeshExt.GetBoneInfluenceBounds(handRenderer, bone);
_length = 0.0f;
// Compute bone length:
if (bone && nextBone)
{
// If we have a next bone, simply compute distance.
_length = Vector3.Distance(bone.position, nextBone.position);
}
else if (bone)
{
// If we have no next bone (for example, the distal bone is the last in the hierarchy), we compute the length using the influenced mesh part's local bounds.
_length = Vector3.Scale(universalFingerAxes.LocalForward, localBounds.size).magnitude;
}
// Compute radius using the influenced part of the mesh's bounds computed earlier.
_radius = 0.5f * Mathf.Max(Vector3.Scale(universalFingerAxes.LocalRight, localBounds.size).magnitude, Vector3.Scale(universalFingerAxes.LocalUp, localBounds.size).magnitude);
}
#endregion
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 017ad516096f4f4d83da5739003991c1
timeCreated: 1665409719

View File

@@ -0,0 +1,190 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="UxrAvatarFingerInfo.cs" company="VRMADA">
// Copyright (c) VRMADA, All rights reserved.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
using System;
using UltimateXR.Core;
using UltimateXR.Core.Math;
using UnityEngine;
namespace UltimateXR.Avatar.Rig
{
/// <summary>
/// Stores information of an avatar rig's finger.
/// </summary>
[Serializable]
public class UxrAvatarFingerInfo
{
#region Inspector Properties/Serialized Fields
[SerializeField] private UxrAvatar _avatar;
[SerializeField] private UxrHandSide _side;
[SerializeField] private UxrFingerType _finger;
[SerializeField] private UxrAvatarFingerBoneInfo _metacarpalInfo;
[SerializeField] private UxrAvatarFingerBoneInfo _proximalInfo;
[SerializeField] private UxrAvatarFingerBoneInfo _intermediateInfo;
[SerializeField] private UxrAvatarFingerBoneInfo _distalInfo;
[SerializeField] private Vector3 _distalLocalTip;
[SerializeField] private Vector3 _distalLocalFingerPrintCenter;
#endregion
#region Public Types & Data
/// <summary>
/// Gets the metacarpal bone info.
/// </summary>
public UxrAvatarFingerBoneInfo MetacarpalInfo => _metacarpalInfo;
/// <summary>
/// Gets the proximal bone info.
/// </summary>
public UxrAvatarFingerBoneInfo ProximalInfo => _proximalInfo;
/// <summary>
/// Gets the intermediate bone info.
/// </summary>
public UxrAvatarFingerBoneInfo IntermediateInfo => _intermediateInfo;
/// <summary>
/// Gets the distal bone info.
/// </summary>
public UxrAvatarFingerBoneInfo DistalInfo => _distalInfo;
/// <summary>
/// Gets the finger tip in local coordinates of the distal bone.
/// </summary>
public Vector3 DistalLocalTip => _distalLocalTip;
/// <summary>
/// Gets an approximate position of the finger print center in local coordinates of the distal bone. The position is
/// computed as a position at 2/3 of the distance between the distal bone start and the tip and at the bottom part of
/// the distal using the distal radius.
/// </summary>
public Vector3 DistalLocalFingerPrintCenter => _distalLocalFingerPrintCenter;
/// <summary>
/// Gets the tip position in world-space.
/// </summary>
public Vector3 TipPosition
{
get
{
if (_avatar)
{
UxrAvatarFinger avatarFinger = _avatar.GetHand(_side).GetFinger(_finger);
if (avatarFinger.Distal)
{
return avatarFinger.Distal.TransformPoint(_distalLocalTip);
}
}
return Vector3.zero;
}
}
/// <summary>
/// Gets the tip forward direction in world-space.
/// </summary>
public Vector3 TipDirection
{
get
{
if (_avatar)
{
UxrAvatarFinger avatarFinger = _avatar.GetHand(_side).GetFinger(_finger);
if (avatarFinger.Distal)
{
UxrUniversalLocalAxes fingerAxes = _avatar.AvatarRigInfo.GetArmInfo(_side).FingerUniversalLocalAxes;
return avatarFinger.Distal.TransformVector(fingerAxes.LocalForward);
}
}
return Vector3.zero;
}
}
/// <summary>
/// Gets the finger print approximate position. The position is computed as a position at 2/3 of the distance between
/// the distal bone start and the tip and at the bottom part of the distal using the distal radius.
/// </summary>
public Vector3 FingerPrintPosition
{
get
{
if (_avatar)
{
UxrAvatarFinger avatarFinger = _avatar.GetHand(_side).GetFinger(_finger);
if (avatarFinger.Distal)
{
return avatarFinger.Distal.TransformPoint(_distalLocalFingerPrintCenter);
}
}
return Vector3.zero;
}
}
/// <summary>
/// Gets the finger print direction in world-space. The direction points from the finger print center downwards.
/// </summary>
public Vector3 FingerPrintDirection
{
get
{
if (_avatar)
{
UxrAvatarFinger avatarFinger = _avatar.GetHand(_side).GetFinger(_finger);
if (avatarFinger.Distal)
{
UxrUniversalLocalAxes fingerAxes = _avatar.AvatarRigInfo.GetArmInfo(_side).FingerUniversalLocalAxes;
return -avatarFinger.Distal.TransformVector(fingerAxes.LocalUp);
}
}
return Vector3.zero;
}
}
#endregion
#region Internal Methods
/// <summary>
/// Computes the finger information.
/// </summary>
/// <param name="avatar">Avatar whose finger information to compute</param>
/// <param name="handRenderer">Hand renderer</param>
/// <param name="side">Which hand side the finger belongs to</param>
/// <param name="finger">Which finger to compute</param>
internal void Compute(UxrAvatar avatar, SkinnedMeshRenderer handRenderer, UxrHandSide side, UxrFingerType finger)
{
_avatar = avatar;
_side = side;
_finger = finger;
UxrUniversalLocalAxes fingerAxes = avatar.AvatarRigInfo.GetArmInfo(side).FingerUniversalLocalAxes;
UxrAvatarFinger avatarFinger = avatar.GetHand(side).GetFinger(finger);
_metacarpalInfo = new UxrAvatarFingerBoneInfo();
_proximalInfo = new UxrAvatarFingerBoneInfo();
_intermediateInfo = new UxrAvatarFingerBoneInfo();
_distalInfo = new UxrAvatarFingerBoneInfo();
_metacarpalInfo.Compute(handRenderer, avatarFinger.Metacarpal, avatarFinger.Proximal, fingerAxes);
_proximalInfo.Compute(handRenderer, avatarFinger.Proximal, avatarFinger.Intermediate, fingerAxes);
_intermediateInfo.Compute(handRenderer, avatarFinger.Intermediate, avatarFinger.Distal, fingerAxes);
_distalInfo.Compute(handRenderer, avatarFinger.Distal, null, fingerAxes);
_distalLocalTip = fingerAxes.LocalForward * _distalInfo.Length;
_distalLocalFingerPrintCenter = fingerAxes.LocalForward * (_distalInfo.Length * 0.66f) - fingerAxes.LocalUp * (_distalInfo.Radius * 0.66f);
}
#endregion
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 78a4d19d90f3ad74ba6d15bad66d317e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,266 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="UxrAvatarHand.cs" company="VRMADA">
// Copyright (c) VRMADA, All rights reserved.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using UltimateXR.Core;
using UltimateXR.Extensions.Unity.Math;
using UnityEngine;
namespace UltimateXR.Avatar.Rig
{
/// <summary>
/// Stores bone references of an Avatar's hand.
/// </summary>
[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
/// <summary>
/// Gets a sequence of all the non-null transforms in the hand, including the wrist.
/// </summary>
public IEnumerable<Transform> Transforms
{
get
{
if (Wrist != null)
{
yield return Wrist;
}
foreach (Transform transform in FingerTransforms)
{
yield return transform;
}
}
}
/// <summary>
/// Gets a sequence of all the non-null finger transforms in the hand.
/// </summary>
public IEnumerable<Transform> 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;
}
}
}
/// <summary>
/// Gets or sets the wrist transform. The wrist is the root transform in the hand.
/// </summary>
public Transform Wrist
{
get => _wrist;
set => _wrist = value;
}
/// <summary>
/// Gets or sets the thumb finger.
/// </summary>
public UxrAvatarFinger Thumb
{
get => _thumb;
set => _thumb = value;
}
/// <summary>
/// Gets or sets the index finger.
/// </summary>
public UxrAvatarFinger Index
{
get => _index;
set => _index = value;
}
/// <summary>
/// Gets or sets the middle finger.
/// </summary>
public UxrAvatarFinger Middle
{
get => _middle;
set => _middle = value;
}
/// <summary>
/// Gets or sets the ring finger.
/// </summary>
public UxrAvatarFinger Ring
{
get => _ring;
set => _ring = value;
}
/// <summary>
/// Gets or sets the little finger.
/// </summary>
public UxrAvatarFinger Little
{
get => _little;
set => _little = value;
}
#endregion
#region Constructors & Finalizer
/// <summary>
/// Default constructor.
/// </summary>
public UxrAvatarHand()
{
_thumb = new UxrAvatarFinger();
_index = new UxrAvatarFinger();
_middle = new UxrAvatarFinger();
_ring = new UxrAvatarFinger();
_little = new UxrAvatarFinger();
}
#endregion
#region Public Methods
/// <summary>
/// Checks if the hand has all finger references plus the wrist.
/// </summary>
/// <returns>Whether the hand has all finger bone data plus the wrist.</returns>
public bool HasFullHandData()
{
return Wrist != null && Thumb.HasData() && Index.HasData() && Middle.HasData() && Ring.HasData() && Little.HasData();
}
/// <summary>
/// Checks if the hand has all finger references.
/// </summary>
/// <returns>Whether the hand has all finger bone data.</returns>
public bool HasFingerData()
{
return Thumb.HasData() && Index.HasData() && Middle.HasData() && Ring.HasData() && Little.HasData();
}
/// <summary>
/// Gets the information of a given finger.
/// </summary>
/// <param name="fingerType">Finger to get</param>
/// <returns>Finger information</returns>
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);
}
}
/// <summary>
/// Tries to compute the palm center in world coordinates.
/// </summary>
/// <param name="center">Returns the palm center in world coordinates</param>
/// <returns>Whether the center could be computed. False if some required bone references are missing</returns>
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;
}
/// <summary>
/// Tries to compute the direction that goes out of the palm in world coordinates.
/// </summary>
/// <param name="handSide">Which hand it is</param>
/// <param name="direction">Returns the palm vector in world coordinates</param>
/// <returns>Whether the vector could be computed. False if some required bone references are missing</returns>
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;
}
/// <summary>
/// Tries to compute the palm-to-finger direction in world coordinates.
/// </summary>
/// <param name="direction">Returns the palm-to-finger direction in world coordinates</param>
/// <returns>Whether the vector could be computed. False if some required bone references are missing</returns>
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
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3919586f4d604a16b0ec7add5c69efc7
timeCreated: 1642870018

View File

@@ -0,0 +1,111 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="UxrAvatarHead.cs" company="VRMADA">
// Copyright (c) VRMADA, All rights reserved.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using UnityEngine;
namespace UltimateXR.Avatar.Rig
{
/// <summary>
/// Stores bone references of an Avatar's head.
/// </summary>
[Serializable]
public class UxrAvatarHead
{
#region Inspector Properties/Serialized Fields
[SerializeField] private Transform _leftEye;
[SerializeField] private Transform _rightEye;
[SerializeField] private Transform _jaw;
[SerializeField] private Transform _head;
[SerializeField] private Transform _neck;
#endregion
#region Public Types & Data
/// <summary>
/// Gets a sequence of all the non-null transforms in the head.
/// </summary>
public IEnumerable<Transform> Transforms
{
get
{
if (LeftEye != null)
{
yield return LeftEye;
}
if (RightEye != null)
{
yield return RightEye;
}
if (Jaw != null)
{
yield return Jaw;
}
if (Head != null)
{
yield return Head;
}
if (Neck != null)
{
yield return Neck;
}
}
}
/// <summary>
/// Gets or sets the left eye transform.
/// </summary>
public Transform LeftEye
{
get => _leftEye;
set => _leftEye = value;
}
/// <summary>
/// Gets or sets the upper leg transform.
/// </summary>
public Transform RightEye
{
get => _rightEye;
set => _rightEye = value;
}
/// <summary>
/// Gets or sets the jaw transform.
/// </summary>
public Transform Jaw
{
get => _jaw;
set => _jaw = value;
}
/// <summary>
/// Gets or sets the head transform.
/// </summary>
public Transform Head
{
get => _head;
set => _head = value;
}
/// <summary>
/// Gets or sets the neck transform.
/// </summary>
public Transform Neck
{
get => _neck;
set => _neck = value;
}
#endregion
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f4b4f49218ec4da0ad4ef5a11873e935
timeCreated: 1642869938

View File

@@ -0,0 +1,96 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="UxrAvatarLeg.cs" company="VRMADA">
// Copyright (c) VRMADA, All rights reserved.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using UnityEngine;
namespace UltimateXR.Avatar.Rig
{
/// <summary>
/// Stores bone references of an Avatar's leg.
/// </summary>
[Serializable]
public class UxrAvatarLeg
{
#region Inspector Properties/Serialized Fields
[SerializeField] private Transform _upperLeg;
[SerializeField] private Transform _lowerLeg;
[SerializeField] private Transform _foot;
[SerializeField] private Transform _toes;
#endregion
#region Public Types & Data
/// <summary>
/// Gets a sequence of all the non-null transforms in the leg.
/// </summary>
public IEnumerable<Transform> Transforms
{
get
{
if (UpperLeg != null)
{
yield return UpperLeg;
}
if (LowerLeg != null)
{
yield return LowerLeg;
}
if (Foot != null)
{
yield return Foot;
}
if (Toes != null)
{
yield return Toes;
}
}
}
/// <summary>
/// Gets or sets the upper leg transform.
/// </summary>
public Transform UpperLeg
{
get => _upperLeg;
set => _upperLeg = value;
}
/// <summary>
/// Gets or sets the lower leg transform.
/// </summary>
public Transform LowerLeg
{
get => _lowerLeg;
set => _lowerLeg = value;
}
/// <summary>
/// Gets or sets the foot transform.
/// </summary>
public Transform Foot
{
get => _foot;
set => _foot = value;
}
/// <summary>
/// Gets or sets the toes transform.
/// </summary>
public Transform Toes
{
get => _toes;
set => _toes = value;
}
#endregion
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: da12d13ccf124d2ebdc953bc3e6fd252
timeCreated: 1642870229

View File

@@ -0,0 +1,158 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="UxrAvatarRig.HandRuntimeTransformation.cs" company="VRMADA">
// Copyright (c) VRMADA, All rights reserved.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
using System.Collections.Generic;
using UltimateXR.Core;
using UltimateXR.Core.Math;
using UnityEngine;
namespace UltimateXR.Avatar.Rig
{
partial class UxrAvatarRig
{
#region Public Methods
/// <summary>
/// Saves all the transform information of the bones of a hand so that it can later be restored using
/// <see cref="PopHandTransforms" />.
/// </summary>
/// <param name="hand">Hand to store all the transforms information of</param>
/// <returns>Transform information</returns>
public static Dictionary<Transform, UxrTransform> PushHandTransforms(UxrAvatarHand hand)
{
Dictionary<Transform, UxrTransform> transforms = new Dictionary<Transform, UxrTransform>();
foreach (Transform transform in hand.Transforms)
{
transforms.Add(transform, new UxrTransform(transform));
}
return transforms;
}
/// <summary>
/// Restores all the transform information of the bones of a hand saved using <see cref="PushHandTransforms" />.
/// </summary>
/// <param name="hand">Hand to restore</param>
/// <param name="transforms">Transform information</param>
/// <remarks>The transform information is restored using local position/rotation/scale values</remarks>
public static void PopHandTransforms(UxrAvatarHand hand, Dictionary<Transform, UxrTransform> transforms)
{
foreach (var transform in transforms)
{
transform.Value.ApplyLocalTo(transform.Key);
}
}
/// <summary>
/// Curls an avatar finger.
/// </summary>
/// <param name="avatar">Avatar to curl the finger of</param>
/// <param name="handSide">Which hand the finger belongs to</param>
/// <param name="finger">Finger to curl</param>
/// <param name="proximalCurl">Curl angle in degrees for the proximal bone</param>
/// <param name="intermediateCurl">Curl angle in degrees for the intermediate bone</param>
/// <param name="distalCurl">Curl angle in degrees for the distal bone</param>
/// <param name="spread">Spread angle in degrees for the finger (finger "left" or "right" amount with respect to the wrist)</param>
public static void CurlFinger(UxrAvatar avatar, UxrHandSide handSide, UxrAvatarFinger finger, float proximalCurl, float intermediateCurl, float distalCurl, float spread = 0.0f)
{
UxrUniversalLocalAxes fingerAxes = avatar.AvatarRigInfo.GetArmInfo(handSide).FingerUniversalLocalAxes;
if (avatar.GetInitialBoneLocalRotation(finger.Proximal, out Quaternion localRotationProximal))
{
finger.Proximal.Rotate(fingerAxes.LocalRight, proximalCurl, Space.Self);
finger.Proximal.Rotate(fingerAxes.LocalUp, spread * (handSide == UxrHandSide.Left ? 1.0f : -1.0f), Space.Self);
}
if (avatar.GetInitialBoneLocalRotation(finger.Intermediate, out Quaternion localRotationIntermediate))
{
finger.Intermediate.Rotate(fingerAxes.LocalRight, intermediateCurl, Space.Self);
}
if (avatar.GetInitialBoneLocalRotation(finger.Distal, out Quaternion localRotationDistal))
{
finger.Distal.Rotate(fingerAxes.LocalRight, distalCurl, Space.Self);
}
}
/// <summary>
/// Updates the hand transforms using a runtime hand descriptor.
/// </summary>
/// <param name="avatar">Avatar to update</param>
/// <param name="handSide">The hand to update</param>
/// <param name="handDescriptor">The runtime descriptor of the hand pose</param>
public static void UpdateHandUsingRuntimeDescriptor(UxrAvatar avatar, UxrHandSide handSide, UxrRuntimeHandDescriptor handDescriptor)
{
UxrAvatarHand hand = avatar.GetHand(handSide);
UpdateFingerUsingRuntimeDescriptor(hand.Thumb, handDescriptor.Thumb);
UpdateFingerUsingRuntimeDescriptor(hand.Index, handDescriptor.Index);
UpdateFingerUsingRuntimeDescriptor(hand.Middle, handDescriptor.Middle);
UpdateFingerUsingRuntimeDescriptor(hand.Ring, handDescriptor.Ring);
UpdateFingerUsingRuntimeDescriptor(hand.Little, handDescriptor.Little);
}
/// <summary>
/// Updates the hand transforms blending between two runtime hand descriptors.
/// </summary>
/// <param name="avatar">Avatar to update</param>
/// <param name="handSide">The hand to update</param>
/// <param name="handDescriptorA">The runtime descriptor of the hand pose to blend from</param>
/// <param name="handDescriptorB">The runtime descriptor of the hand pose to blend to</param>
/// <param name="blend">Interpolation value [0.0, 1.0]</param>
public static void UpdateHandUsingRuntimeDescriptor(UxrAvatar avatar, UxrHandSide handSide, UxrRuntimeHandDescriptor handDescriptorA, UxrRuntimeHandDescriptor handDescriptorB, float blend)
{
UxrAvatarHand hand = avatar.GetHand(handSide);
UpdateFingerUsingRuntimeDescriptor(hand.Thumb, handDescriptorA.Thumb, handDescriptorB.Thumb, blend);
UpdateFingerUsingRuntimeDescriptor(hand.Index, handDescriptorA.Index, handDescriptorB.Index, blend);
UpdateFingerUsingRuntimeDescriptor(hand.Middle, handDescriptorA.Middle, handDescriptorB.Middle, blend);
UpdateFingerUsingRuntimeDescriptor(hand.Ring, handDescriptorA.Ring, handDescriptorB.Ring, blend);
UpdateFingerUsingRuntimeDescriptor(hand.Little, handDescriptorA.Little, handDescriptorB.Little, blend);
}
#endregion
#region Private Methods
/// <summary>
/// Updates a finger's transforms from a runtime finger descriptor.
/// </summary>
/// <param name="finger">The finger to update</param>
/// <param name="fingerDescriptor">The runtime descriptor to get the data from</param>
private static void UpdateFingerUsingRuntimeDescriptor(UxrAvatarFinger finger, UxrRuntimeFingerDescriptor fingerDescriptor)
{
if (fingerDescriptor.HasMetacarpalInfo)
{
finger.Metacarpal.localRotation = fingerDescriptor.MetacarpalRotation;
}
finger.Proximal.localRotation = fingerDescriptor.ProximalRotation;
finger.Intermediate.localRotation = fingerDescriptor.IntermediateRotation;
finger.Distal.localRotation = fingerDescriptor.DistalRotation;
}
/// <summary>
/// Updates a finger's transforms from a runtime finger descriptor.
/// </summary>
/// <param name="finger">The finger to update</param>
/// <param name="fingerDescriptorA">The runtime descriptor to blend from</param>
/// <param name="fingerDescriptorB">The runtime descriptor to blend to</param>
/// <param name="blend">The interpolation parameter [0.0, 1.0]</param>
private static void UpdateFingerUsingRuntimeDescriptor(UxrAvatarFinger finger, UxrRuntimeFingerDescriptor fingerDescriptorA, UxrRuntimeFingerDescriptor fingerDescriptorB, float blend)
{
if (fingerDescriptorA.HasMetacarpalInfo && fingerDescriptorB.HasMetacarpalInfo)
{
finger.Metacarpal.localRotation = Quaternion.Slerp(fingerDescriptorA.MetacarpalRotation, fingerDescriptorB.MetacarpalRotation, blend);
}
finger.Proximal.localRotation = Quaternion.Slerp(fingerDescriptorA.ProximalRotation, fingerDescriptorB.ProximalRotation, blend);
finger.Intermediate.localRotation = Quaternion.Slerp(fingerDescriptorA.IntermediateRotation, fingerDescriptorB.IntermediateRotation, blend);
finger.Distal.localRotation = Quaternion.Slerp(fingerDescriptorA.DistalRotation, fingerDescriptorB.DistalRotation, blend);
}
#endregion
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 31205aed49ef71b4ba0d90af889b71d0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,195 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="UxrAvatarRig.HandTransformation.cs" company="VRMADA">
// Copyright (c) VRMADA, All rights reserved.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
using UltimateXR.Core;
using UltimateXR.Core.Math;
using UltimateXR.Manipulation.HandPoses;
using UnityEngine;
namespace UltimateXR.Avatar.Rig
{
partial class UxrAvatarRig
{
#region Public Methods
/// <summary>
/// Updates an avatar's hand transforms using a fixed hand descriptor.
/// </summary>
/// <param name="avatar">The avatar to update</param>
/// <param name="handSide">Which hand to update</param>
/// <param name="handDescriptor">The descriptor to get the data from</param>
public static void UpdateHandUsingDescriptor(UxrAvatar avatar, UxrHandSide handSide, UxrHandDescriptor handDescriptor)
{
UpdateHandUsingDescriptor(avatar.GetHand(handSide), handDescriptor, avatar.AvatarRigInfo.GetArmInfo(handSide).HandUniversalLocalAxes, avatar.AvatarRigInfo.GetArmInfo(handSide).FingerUniversalLocalAxes);
}
/// <summary>
/// Updates an avatar's hand transforms using two hand descriptors and a blend value.
/// </summary>
/// <param name="avatar">The avatar to update</param>
/// <param name="handSide">Which hand to update</param>
/// <param name="handDescriptorA">The descriptor for the hand pose to blend from</param>
/// <param name="handDescriptorB">The descriptor for the hand pose to blend to</param>
/// <param name="blend">The interpolation value [0.0, 1.0]</param>
public static void UpdateHandUsingDescriptor(UxrAvatar avatar, UxrHandSide handSide, UxrHandDescriptor handDescriptorA, UxrHandDescriptor handDescriptorB, float blend)
{
UpdateHandUsingDescriptor(avatar.GetHand(handSide), handDescriptorA, handDescriptorB, blend, avatar.AvatarRigInfo.GetArmInfo(handSide).HandUniversalLocalAxes, avatar.AvatarRigInfo.GetArmInfo(handSide).FingerUniversalLocalAxes);
}
/// <summary>
/// Updates the hand transforms using a hand descriptor.
/// </summary>
/// <param name="hand">The hand to update</param>
/// <param name="handDescriptor">The descriptor of the hand pose</param>
/// <param name="handLocalAxes">The universal coordinate system of the hand transform</param>
/// <param name="handLocalFingerAxes">The universal coordinate system of the finger transforms</param>
public static void UpdateHandUsingDescriptor(UxrAvatarHand hand, UxrHandDescriptor handDescriptor, UxrUniversalLocalAxes handLocalAxes, UxrUniversalLocalAxes handLocalFingerAxes)
{
UpdateFingerUsingDescriptor(hand.Wrist, hand.Thumb, handDescriptor.Thumb, handLocalAxes, handLocalFingerAxes);
UpdateFingerUsingDescriptor(hand.Wrist, hand.Index, handDescriptor.Index, handLocalAxes, handLocalFingerAxes);
UpdateFingerUsingDescriptor(hand.Wrist, hand.Middle, handDescriptor.Middle, handLocalAxes, handLocalFingerAxes);
UpdateFingerUsingDescriptor(hand.Wrist, hand.Ring, handDescriptor.Ring, handLocalAxes, handLocalFingerAxes);
UpdateFingerUsingDescriptor(hand.Wrist, hand.Little, handDescriptor.Little, handLocalAxes, handLocalFingerAxes);
}
/// <summary>
/// Updates the hand transforms using two hand descriptors and an interpolation value.
/// </summary>
/// <param name="hand">The hand to update</param>
/// <param name="handDescriptorA">The descriptor of the hand pose to blend from</param>
/// <param name="handDescriptorB">The descriptor of the hand pose to blend to</param>
/// <param name="blend">The interpolation value [0.0, 1.0]</param>
/// <param name="handLocalAxes">The universal coordinate system of the hand transform</param>
/// <param name="fingerLocalAxes">The universal coordinate system of the finger transforms</param>
public static void UpdateHandUsingDescriptor(UxrAvatarHand hand, UxrHandDescriptor handDescriptorA, UxrHandDescriptor handDescriptorB, float blend, UxrUniversalLocalAxes handLocalAxes, UxrUniversalLocalAxes fingerLocalAxes)
{
UpdateFingerUsingDescriptor(hand.Wrist, hand.Thumb, handDescriptorA.Thumb, handDescriptorB.Thumb, blend, handLocalAxes, fingerLocalAxes);
UpdateFingerUsingDescriptor(hand.Wrist, hand.Index, handDescriptorA.Index, handDescriptorB.Index, blend, handLocalAxes, fingerLocalAxes);
UpdateFingerUsingDescriptor(hand.Wrist, hand.Middle, handDescriptorA.Middle, handDescriptorB.Middle, blend, handLocalAxes, fingerLocalAxes);
UpdateFingerUsingDescriptor(hand.Wrist, hand.Ring, handDescriptorA.Ring, handDescriptorB.Ring, blend, handLocalAxes, fingerLocalAxes);
UpdateFingerUsingDescriptor(hand.Wrist, hand.Little, handDescriptorA.Little, handDescriptorB.Little, blend, handLocalAxes, fingerLocalAxes);
}
#endregion
#region Private Methods
/// <summary>
/// Updates a finger's transforms from a finger descriptor.
/// </summary>
/// <param name="wrist">The wrist (root) transform of the hand</param>
/// <param name="finger">The finger to update</param>
/// <param name="fingerDescriptor">The descriptor to get the data from</param>
/// <param name="handLocalAxes">The universal coordinate system of the hand transform</param>
/// <param name="fingerLocalAxes">The universal coordinate system of the finger transforms</param>
private static void UpdateFingerUsingDescriptor(Transform wrist, UxrAvatarFinger finger, UxrFingerDescriptor fingerDescriptor, UxrUniversalLocalAxes handLocalAxes, UxrUniversalLocalAxes fingerLocalAxes)
{
if (fingerDescriptor.HasMetacarpalInfo && finger.Metacarpal)
{
UpdateFingerNodeUsingDescriptor(wrist, finger.Metacarpal, fingerDescriptor.Metacarpal, handLocalAxes, fingerLocalAxes);
UpdateFingerNodeUsingDescriptor(finger.Metacarpal, finger.Proximal, fingerDescriptor.Proximal, fingerLocalAxes, fingerLocalAxes);
}
else
{
UpdateFingerNodeUsingDescriptor(wrist, finger.Proximal, fingerDescriptor.ProximalNoMetacarpal, handLocalAxes, fingerLocalAxes);
}
UpdateFingerNodeUsingDescriptor(finger.Proximal, finger.Intermediate, fingerDescriptor.Intermediate, fingerLocalAxes, fingerLocalAxes);
UpdateFingerNodeUsingDescriptor(finger.Intermediate, finger.Distal, fingerDescriptor.Distal, fingerLocalAxes, fingerLocalAxes);
}
/// <summary>
/// Updates a finger's transforms using two finger descriptors and an interpolation value.
/// </summary>
/// <param name="wrist">The wrist (root) transform of the hand</param>
/// <param name="finger">The finger to update</param>
/// <param name="fingerDescriptorA">The descriptor A to get the data from</param>
/// <param name="fingerDescriptorB">The descriptor B to get the data from</param>
/// <param name="blend">The interpolation value [0.0, 1.0]</param>
/// <param name="handLocalAxes">The universal coordinate system of the hand transform</param>
/// <param name="fingerLocalAxes">The universal coordinate system of the finger transforms</param>
private static void UpdateFingerUsingDescriptor(Transform wrist,
UxrAvatarFinger finger,
UxrFingerDescriptor fingerDescriptorA,
UxrFingerDescriptor fingerDescriptorB,
float blend,
UxrUniversalLocalAxes handLocalAxes,
UxrUniversalLocalAxes fingerLocalAxes)
{
if (fingerDescriptorA.HasMetacarpalInfo && finger.Metacarpal)
{
UpdateFingerNodeUsingDescriptor(wrist, finger.Metacarpal, fingerDescriptorA.Metacarpal, fingerDescriptorB.Metacarpal, blend, handLocalAxes, fingerLocalAxes);
UpdateFingerNodeUsingDescriptor(finger.Metacarpal, finger.Proximal, fingerDescriptorA.Proximal, fingerDescriptorB.Proximal, blend, fingerLocalAxes, fingerLocalAxes);
}
else
{
UpdateFingerNodeUsingDescriptor(wrist, finger.Proximal, fingerDescriptorA.ProximalNoMetacarpal, fingerDescriptorB.ProximalNoMetacarpal, blend, handLocalAxes, fingerLocalAxes);
}
UpdateFingerNodeUsingDescriptor(finger.Proximal, finger.Intermediate, fingerDescriptorA.Intermediate, fingerDescriptorB.Intermediate, blend, fingerLocalAxes, fingerLocalAxes);
UpdateFingerNodeUsingDescriptor(finger.Intermediate, finger.Distal, fingerDescriptorA.Distal, fingerDescriptorB.Distal, blend, fingerLocalAxes, fingerLocalAxes);
}
/// <summary>
/// Updates a finger bone transform from a node descriptor.
/// </summary>
/// <param name="parent">The node parent</param>
/// <param name="node">The node being updated</param>
/// <param name="nodeDescriptor">The descriptor to get the data from</param>
/// <param name="parentLocalAxes">The universal coordinate system of the parent's transform</param>
/// <param name="nodeLocalAxes">The universal coordinate system of the node transform</param>
private static void UpdateFingerNodeUsingDescriptor(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));
node.rotation = nodeUniversalMatrix.rotation * nodeUniversalToActual;
}
/// <summary>
/// Updates a finger bone transform using two node descriptors and an interpolation value.
/// </summary>
/// <param name="parent">The node parent</param>
/// <param name="node">The node being updated</param>
/// <param name="nodeDescriptorA">The descriptor A to get the data from</param>
/// <param name="nodeDescriptorB">The descriptor B to get the data from</param>
/// <param name="blend">The interpolation value [0.0, 1.0]</param>
/// <param name="parentLocalAxes">The universal coordinate system of the parent's transform</param>
/// <param name="nodeLocalAxes">The universal coordinate system of the node transform</param>
private static void UpdateFingerNodeUsingDescriptor(Transform parent,
Transform node,
UxrFingerNodeDescriptor nodeDescriptorA,
UxrFingerNodeDescriptor nodeDescriptorB,
float blend,
UxrUniversalLocalAxes parentLocalAxes,
UxrUniversalLocalAxes nodeLocalAxes)
{
UpdateFingerNodeUsingDescriptor(parent, node, nodeDescriptorA, parentLocalAxes, nodeLocalAxes);
Quaternion rotA = node.rotation;
UpdateFingerNodeUsingDescriptor(parent, node, nodeDescriptorB, parentLocalAxes, nodeLocalAxes);
Quaternion rotB = node.rotation;
node.rotation = Quaternion.Slerp(rotA, rotB, blend);
}
#endregion
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: edfbdcd7e7998f54484279e8009b488e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2f2c662dbf55f174fa6f6573d46902d4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,278 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="UxrAvatarRig.cs" company="VRMADA">
// Copyright (c) VRMADA, All rights reserved.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using UltimateXR.Core;
using UltimateXR.Extensions.Unity;
using UnityEngine;
namespace UltimateXR.Avatar.Rig
{
/// <summary>
/// <para>
/// Stores references to all elements in an avatar rig. These are the <see cref="Transform" /> components of the
/// bones that drive the visual representation of a humanoid avatar.
/// </para>
/// It also contains functionality to transform the hand using hand poses.
/// </summary>
[Serializable]
public partial class UxrAvatarRig
{
#region Inspector Properties/Serialized Fields
[SerializeField] private UxrAvatarHead _head;
[SerializeField] private UxrAvatarArm _leftArm;
[SerializeField] private UxrAvatarArm _rightArm;
[SerializeField] private Transform _upperChest;
[SerializeField] private Transform _chest;
[SerializeField] private Transform _spine;
[SerializeField] private Transform _hips;
[SerializeField] private UxrAvatarLeg _leftLeg;
[SerializeField] private UxrAvatarLeg _rightLeg;
#endregion
#region Public Types & Data
/// <summary>
/// Number of fingers in a hand.
/// </summary>
public const int HandFingerCount = 5;
/// <summary>
/// Gets a sequence of all the non-null transforms in the avatar rig.
/// </summary>
public IEnumerable<Transform> Transforms
{
get
{
foreach (Transform transform in Head.Transforms)
{
yield return transform;
}
foreach (Transform transform in LeftArm.Transforms)
{
yield return transform;
}
foreach (Transform transform in RightArm.Transforms)
{
yield return transform;
}
foreach (Transform transform in LeftLeg.Transforms)
{
yield return transform;
}
foreach (Transform transform in RightLeg.Transforms)
{
yield return transform;
}
if (UpperChest != null)
{
yield return UpperChest;
}
if (Chest != null)
{
yield return Chest;
}
if (Spine != null)
{
yield return Spine;
}
if (Hips != null)
{
yield return Hips;
}
}
}
/// <summary>
/// Gets the head.
/// </summary>
public UxrAvatarHead Head => _head;
/// <summary>
/// Gets the left arm.
/// </summary>
public UxrAvatarArm LeftArm => _leftArm;
/// <summary>
/// Gets the right arm.
/// </summary>
public UxrAvatarArm RightArm => _rightArm;
/// <summary>
/// Gets the upper chest transform or null if there isn't any.
/// </summary>
public Transform UpperChest => _upperChest;
/// <summary>
/// Gets the chest transform or null if there isn't any.
/// </summary>
public Transform Chest => _chest;
/// <summary>
/// Gets the spine transform or null if there isn't any.
/// </summary>
public Transform Spine => _spine;
/// <summary>
/// Gets the hips transform or null if there isn't any.
/// </summary>
public Transform Hips => _hips;
/// <summary>
/// Gets the left leg.
/// </summary>
public UxrAvatarLeg LeftLeg => _leftLeg;
/// <summary>
/// Gets the right leg.
/// </summary>
public UxrAvatarLeg RightLeg => _rightLeg;
#endregion
#region Constructors & Finalizer
/// <summary>
/// Constructor.
/// </summary>
public UxrAvatarRig()
{
ClearRigElements();
}
#endregion
#region Public Methods
/// <summary>
/// Checks which side a transform is part of, based on which wrist it hangs from or if it hangs from an
/// <see cref="UxrHandIntegration" />.
/// </summary>
/// <param name="transform">Transform to check which side it is part of</param>
/// <param name="side">Returns the side, if found</param>
/// <returns>Whether a side was found</returns>
public static bool GetHandSide(Transform transform, out UxrHandSide side)
{
UxrAvatar avatar = transform.SafeGetComponentInParent<UxrAvatar>();
if (avatar)
{
if (transform.HasParent(avatar.LeftHandBone))
{
side = UxrHandSide.Left;
return true;
}
if (transform.HasParent(avatar.RightHandBone))
{
side = UxrHandSide.Right;
return true;
}
UxrHandIntegration handIntegration = transform.SafeGetComponentInParent<UxrHandIntegration>();
if (handIntegration)
{
side = handIntegration.HandSide;
return true;
}
}
side = UxrHandSide.Left;
return false;
}
/// <summary>
/// Sets all the rig element references to null.
/// </summary>
public void ClearRigElements()
{
_head = new UxrAvatarHead();
_leftArm = new UxrAvatarArm();
_rightArm = new UxrAvatarArm();
_leftLeg = new UxrAvatarLeg();
_rightLeg = new UxrAvatarLeg();
_upperChest = null;
_chest = null;
_spine = null;
_hips = null;
}
/// <summary>
/// Gets the avatar arms.
/// </summary>
/// <returns>Avatar arms</returns>
public IEnumerable<UxrAvatarArm> GetArms()
{
if (_leftArm != null)
{
yield return _leftArm;
}
if (_rightArm != null)
{
yield return _rightArm;
}
}
/// <summary>
/// Gets whether the given rig has any of the references used in upper body IK (head, neck, upper chest, chest or
/// spine).
/// </summary>
/// <returns>Whether the given rig has any upper body reference used in IK</returns>
public bool HasAnyUpperBodyIKReference()
{
return _head.Head != null || _head.Neck != null || _upperChest != null || _chest != null || _spine != null;
}
/// <summary>
/// Gets whether the given rig has all arm references (upper arm, forearm and hand).
/// </summary>
/// <returns>Whether the given rig has arm references for both sides</returns>
public bool HasArmData()
{
return LeftArm.Hand.Wrist != null && RightArm.Hand.Wrist != null && LeftArm.Forearm != null && RightArm.Forearm != null && LeftArm.UpperArm != null && RightArm.UpperArm != null;
}
/// <summary>
/// Gets whether the given rig has all hand and finger bone references.
/// </summary>
/// <returns>Whether the given rig has all the references</returns>
public bool HasFullHandData()
{
return LeftArm.Hand.Wrist != null && RightArm.Hand.Wrist != null && LeftArm.Hand.HasFingerData() && RightArm.Hand.HasFingerData();
}
/// <summary>
/// Gets whether the given rig has all finger data.
/// </summary>
/// <returns>Whether the given rig has all finger data of both hands</returns>
public bool HasFingerData()
{
return LeftArm.Hand.HasFingerData() && RightArm.Hand.HasFingerData();
}
/// <summary>
/// Gets whether the given rig has all finger data.
/// </summary>
/// <param name="handSide">Which hand to check</param>
/// <returns>Whether the given rig has the given hand finger data</returns>
public bool HasFingerData(UxrHandSide handSide)
{
return handSide == UxrHandSide.Left ? LeftArm.Hand.HasFingerData() : RightArm.Hand.HasFingerData();
}
#endregion
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 0b73bc61877ab504f86f43111f8741f6
timeCreated: 1500467196
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,100 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="UxrAvatarRigInfo.cs" company="VRMADA">
// Copyright (c) VRMADA, All rights reserved.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using UltimateXR.Core;
using UltimateXR.Core.Math;
using UnityEngine;
namespace UltimateXR.Avatar.Rig
{
/// <summary>
/// <para>
/// Stores information about the rig to facilitate transformations no matter the coordinate system used by
/// the avatar hierarchy (See <see cref="UxrUniversalLocalAxes" />).
/// </para>
/// It also stores lengths and sizes that can facilitate operations such as Inverse Kinematics, calibration, etc.
/// </summary>
[Serializable]
public class UxrAvatarRigInfo
{
#region Inspector Properties/Serialized Fields
[SerializeField] private int _version;
[SerializeField] private UxrAvatar _avatar;
[SerializeField] private UxrAvatarArmInfo _leftArmInfo = new UxrAvatarArmInfo();
[SerializeField] private UxrAvatarArmInfo _rightArmInfo = new UxrAvatarArmInfo();
#endregion
#region Public Types & Data
/// <summary>
/// Enumerates the arm information.
/// </summary>
public IEnumerable<UxrAvatarArmInfo> Arms
{
get
{
yield return _leftArmInfo;
yield return _rightArmInfo;
}
}
#endregion
#region Internal Types & Data
internal const int CurrentVersion = 1;
/// <summary>
/// Gets the version this data was serialized for. It allows to control if new data needs to be computed.
/// </summary>
internal int SerializedVersion => _version;
#endregion
#region Public Methods
/// <summary>
/// Gets the arm information.
/// </summary>
/// <param name="side">Which side to retrieve</param>
/// <returns>Arm information</returns>
public UxrAvatarArmInfo GetArmInfo(UxrHandSide side)
{
return side == UxrHandSide.Left ? _leftArmInfo : _rightArmInfo;
}
#endregion
#region Internal Methods
/// <summary>
/// Computes the information of an avatar's rig.
/// </summary>
/// <param name="avatar">Avatar whose rig to compute the information of</param>
internal void Compute(UxrAvatar avatar)
{
_version = CurrentVersion;
_avatar = avatar;
_leftArmInfo.Compute(avatar, UxrHandSide.Left);
_rightArmInfo.Compute(avatar, UxrHandSide.Right);
}
/// <summary>
/// Updates information to the current frame.
/// </summary>
internal void UpdateInfo()
{
GetArmInfo(UxrHandSide.Left).WristTorsionInfo.UpdateInfo(_avatar.AvatarRig.LeftArm.Forearm, _avatar.LeftHandBone, GetArmInfo(UxrHandSide.Left));
GetArmInfo(UxrHandSide.Right).WristTorsionInfo.UpdateInfo(_avatar.AvatarRig.RightArm.Forearm, _avatar.RightHandBone, GetArmInfo(UxrHandSide.Right));
}
#endregion
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ab7172ff6a084e05ac48004c5a3005f8
timeCreated: 1642870290

View File

@@ -0,0 +1,24 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="UxrAvatarRigType.cs" company="VRMADA">
// Copyright (c) VRMADA, All rights reserved.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
namespace UltimateXR.Avatar.Rig
{
/// <summary>
/// Enumerates the different rig types handled by the <see cref="UxrAvatar" /> inspector to make sure
/// that only the relevant rig elements are shown.
/// </summary>
public enum UxrAvatarRigType
{
/// <summary>
/// Simple setup: head and two hands
/// </summary>
HandsOnly,
/// <summary>
/// Full body, including torso, neck... etc.
/// </summary>
HalfOrFullBody
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a1bf48fd9417e3e4296ae3f98040766b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,20 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="UxrFingerType.cs" company="VRMADA">
// Copyright (c) VRMADA, All rights reserved.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
namespace UltimateXR.Avatar.Rig
{
/// <summary>
/// Enumerates the different fingers in a hand.
/// </summary>
public enum UxrFingerType
{
None,
Thumb,
Index,
Middle,
Ring,
Little
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a420255799f344bdbe38b3f23d781e2c
timeCreated: 1643731537

View File

@@ -0,0 +1,214 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="UxrRuntimeFingerDescriptor.cs" company="VRMADA">
// Copyright (c) VRMADA, All rights reserved.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
using UltimateXR.Core;
using UltimateXR.Core.Math;
using UltimateXR.Manipulation.HandPoses;
using UnityEngine;
namespace UltimateXR.Avatar.Rig
{
/// <summary>
/// Runtime, lightweight version of <see cref="UxrFingerDescriptor" />. See <see cref="UxrRuntimeHandDescriptor" />.
/// </summary>
public class UxrRuntimeFingerDescriptor
{
#region Public Types & Data
/// <summary>
/// Gets whether the descriptor contains metacarpal information.
/// </summary>
public bool HasMetacarpalInfo { get; private set; }
/// <summary>
/// Gets the metacarpal local rotation.
/// </summary>
public Quaternion MetacarpalRotation { get; private set; }
/// <summary>
/// Gets the proximal local rotation.
/// </summary>
public Quaternion ProximalRotation { get; private set; }
/// <summary>
/// Gets the intermediate local rotation.
/// </summary>
public Quaternion IntermediateRotation { get; private set; }
/// <summary>
/// Gets the proximal local rotation.
/// </summary>
public Quaternion DistalRotation { get; private set; }
#endregion
#region Constructors & Finalizer
/// <summary>
/// Default constructor.
/// </summary>
public UxrRuntimeFingerDescriptor()
{
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="avatar">Avatar to compute the runtime finger descriptor for</param>
/// <param name="handSide">Which hand to process</param>
/// <param name="handDescriptor">The source data</param>
/// <param name="fingerType">Which finger to store</param>
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;
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="hasMetacarpalInfo">Whether the finger contains metacarpal information</param>
/// <param name="metacarpalRotation">Metacarpal local rotation (optional)</param>
/// <param name="proximalRotation">Proximal local rotation</param>
/// <param name="intermediateRotation">Intermediate local rotation</param>
/// <param name="distalRotation">Distal local rotation</param>
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
/// <summary>
/// Copies the data from another descriptor.
/// </summary>
/// <param name="fingerDescriptor">Descriptor to copy the data from</param>
public void CopyFrom(UxrRuntimeFingerDescriptor fingerDescriptor)
{
if (fingerDescriptor == null)
{
return;
}
HasMetacarpalInfo = fingerDescriptor.HasMetacarpalInfo;
MetacarpalRotation = fingerDescriptor.MetacarpalRotation;
ProximalRotation = fingerDescriptor.ProximalRotation;
IntermediateRotation = fingerDescriptor.IntermediateRotation;
DistalRotation = fingerDescriptor.DistalRotation;
}
/// <summary>
/// Interpolates towards another runtime finger descriptor.
/// </summary>
/// <param name="fingerDescriptor">Runtime finger descriptor</param>
/// <param name="blend">Interpolation value [0.0, 1.0]</param>
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
/// <summary>
/// Gets the local rotation of a <see cref="UxrFingerDescriptor" /> when applied to an object.
/// </summary>
/// <param name="parent">Parent the node descriptor references its rotation to</param>
/// <param name="node">Transform to get the local rotation of</param>
/// <param name="nodeDescriptor">
/// Bone information in the well-known coordinate system of a <see cref="UxrHandPoseAsset" />
/// </param>
/// <param name="parentLocalAxes">Coordinate system of the <paramref name="parent" /> transform</param>
/// <param name="nodeLocalAxes">Coordinate system of the <paramref name="node" /> transform</param>
/// <returns>
/// Local rotation that should be applied to <paramref name="node" /> when using
/// <paramref name="nodeDescriptor" />
/// </returns>
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
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1c3762c7ba56108419a2aa6db53865c5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,111 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="UxrRuntimeHandDescriptor.cs" company="VRMADA">
// Copyright (c) VRMADA, All rights reserved.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
using UltimateXR.Core;
using UltimateXR.Manipulation.HandPoses;
namespace UltimateXR.Avatar.Rig
{
/// <summary>
/// Runtime, lightweight version of <see cref="UxrHandDescriptor" />. It is used to describe the local orientations of
/// finger bones of a <see cref="UxrHandPoseAsset" /> for a given <see cref="UxrAvatar" />.
/// <see cref="UxrHandPoseAsset" /> objects contain orientations in a well-known space. They are used to adapt hand
/// poses independently of the coordinate system used by each avatar. This means an additional transformation needs to
/// be performed to get to each avatar's coordinate system. <see cref="UxrRuntimeHandDescriptor" /> is used
/// to have a high performant version that already contains the bone orientations in each avatar's coordinate system
/// so that hand pose blending can be computed using much less processing power.
/// </summary>
public class UxrRuntimeHandDescriptor
{
#region Public Types & Data
public UxrRuntimeFingerDescriptor Index { get; }
public UxrRuntimeFingerDescriptor Middle { get; }
public UxrRuntimeFingerDescriptor Ring { get; }
public UxrRuntimeFingerDescriptor Little { get; }
public UxrRuntimeFingerDescriptor Thumb { get; }
#endregion
#region Constructors & Finalizer
/// <summary>
/// Default constructor.
/// </summary>
public UxrRuntimeHandDescriptor()
{
Index = new UxrRuntimeFingerDescriptor();
Middle = new UxrRuntimeFingerDescriptor();
Ring = new UxrRuntimeFingerDescriptor();
Little = new UxrRuntimeFingerDescriptor();
Thumb = new UxrRuntimeFingerDescriptor();
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="avatar">Avatar to compute the runtime hand descriptor for</param>
/// <param name="handSide">Which hand to store</param>
/// <param name="handPoseAsset">Hand pose to transform</param>
/// <param name="handPoseType">Which hand pose information to store</param>
/// <param name="blendPoseType">
/// If <paramref name="handPoseType" /> is <see cref="UxrHandPoseType.Blend" />, which pose to
/// store
/// </param>
public UxrRuntimeHandDescriptor(UxrAvatar avatar, UxrHandSide handSide, UxrHandPoseAsset handPoseAsset, UxrHandPoseType handPoseType, UxrBlendPoseType blendPoseType)
{
UxrHandDescriptor handDescriptor = handPoseAsset.GetHandDescriptor(handSide, handPoseType, blendPoseType);
Index = new UxrRuntimeFingerDescriptor(avatar, handSide, handDescriptor, UxrFingerType.Index);
Middle = new UxrRuntimeFingerDescriptor(avatar, handSide, handDescriptor, UxrFingerType.Middle);
Ring = new UxrRuntimeFingerDescriptor(avatar, handSide, handDescriptor, UxrFingerType.Ring);
Little = new UxrRuntimeFingerDescriptor(avatar, handSide, handDescriptor, UxrFingerType.Little);
Thumb = new UxrRuntimeFingerDescriptor(avatar, handSide, handDescriptor, UxrFingerType.Thumb);
}
#endregion
#region Public Methods
/// <summary>
/// Copies the data from another descriptor.
/// </summary>
/// <param name="handDescriptor">Descriptor to compute the data from</param>
public void CopyFrom(UxrRuntimeHandDescriptor handDescriptor)
{
if (handDescriptor == null)
{
return;
}
Index.CopyFrom(handDescriptor.Index);
Middle.CopyFrom(handDescriptor.Middle);
Ring.CopyFrom(handDescriptor.Ring);
Little.CopyFrom(handDescriptor.Little);
Thumb.CopyFrom(handDescriptor.Thumb);
}
/// <summary>
/// Interpolates towards another runtime hand descriptor.
/// </summary>
/// <param name="handDescriptor">Runtime hand descriptor to interpolate towards</param>
/// <param name="blend">Interpolation value [0.0, 1.0]</param>
public void InterpolateTo(UxrRuntimeHandDescriptor handDescriptor, float blend)
{
if (handDescriptor == null)
{
return;
}
Index.InterpolateTo(handDescriptor.Index, blend);
Middle.InterpolateTo(handDescriptor.Middle, blend);
Ring.InterpolateTo(handDescriptor.Ring, blend);
Little.InterpolateTo(handDescriptor.Little, blend);
Thumb.InterpolateTo(handDescriptor.Thumb, blend);
}
#endregion
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 990a211ff33b9ee4e967c89dd4b56f87
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,86 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="UxrRuntimeHandPose.cs" company="VRMADA">
// Copyright (c) VRMADA, All rights reserved.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
using System;
using UltimateXR.Core;
using UltimateXR.Manipulation.HandPoses;
namespace UltimateXR.Avatar.Rig
{
/// <summary>
/// Runtime, lightweight version of <see cref="UxrHandPoseAsset" />. It is used to describe the local orientations of
/// finger bones of a <see cref="UxrHandPoseAsset" /> for a given <see cref="UxrAvatar" />.
/// <see cref="UxrHandPoseAsset" /> objects contain orientations in a well-known space. They are used to adapt hand
/// poses independently of the coordinate system used by each avatar. This means an additional transformation needs to
/// be performed to get to each avatar's coordinate system. <see cref="UxrRuntimeHandPose" /> is used
/// to have a high performant version that already contains the bone orientations in each avatar's coordinate system
/// so that hand pose blending can be computed using much less processing power.
/// </summary>
public class UxrRuntimeHandPose
{
#region Public Types & Data
public string PoseName { get; }
public UxrHandPoseType PoseType { get; }
#endregion
#region Constructors & Finalizer
/// <summary>
/// Constructor.
/// </summary>
/// <param name="avatar">Avatar to compute the runtime hand pose for</param>
/// <param name="handPoseAsset">Hand pose in a well-known coordinate system</param>
public UxrRuntimeHandPose(UxrAvatar avatar, UxrHandPoseAsset handPoseAsset)
{
PoseName = handPoseAsset.name;
PoseType = handPoseAsset.PoseType;
HandDescriptorLeft = new UxrRuntimeHandDescriptor(avatar, UxrHandSide.Left, handPoseAsset, UxrHandPoseType.Fixed, UxrBlendPoseType.None);
HandDescriptorRight = new UxrRuntimeHandDescriptor(avatar, UxrHandSide.Right, handPoseAsset, UxrHandPoseType.Fixed, UxrBlendPoseType.None);
HandDescriptorOpenLeft = new UxrRuntimeHandDescriptor(avatar, UxrHandSide.Left, handPoseAsset, UxrHandPoseType.Blend, UxrBlendPoseType.OpenGrip);
HandDescriptorOpenRight = new UxrRuntimeHandDescriptor(avatar, UxrHandSide.Right, handPoseAsset, UxrHandPoseType.Blend, UxrBlendPoseType.OpenGrip);
HandDescriptorClosedLeft = new UxrRuntimeHandDescriptor(avatar, UxrHandSide.Left, handPoseAsset, UxrHandPoseType.Blend, UxrBlendPoseType.ClosedGrip);
HandDescriptorClosedRight = new UxrRuntimeHandDescriptor(avatar, UxrHandSide.Right, handPoseAsset, UxrHandPoseType.Blend, UxrBlendPoseType.ClosedGrip);
}
#endregion
#region Public Methods
/// <summary>
/// Gets the given hand descriptor, based on the <see cref="PoseType" />.
/// </summary>
/// <param name="handSide">Hand to get the descriptor for</param>
/// <param name="blendPoseType">
/// If <see cref="PoseType" /> is <see cref="UxrHandPoseType.Blend" />, whether to get the open or
/// closed pose descriptor.
/// </param>
/// <returns>Hand descriptor</returns>
public UxrRuntimeHandDescriptor GetHandDescriptor(UxrHandSide handSide, UxrBlendPoseType blendPoseType = UxrBlendPoseType.None)
{
return PoseType switch
{
UxrHandPoseType.Fixed => handSide == UxrHandSide.Left ? HandDescriptorLeft : HandDescriptorRight,
UxrHandPoseType.Blend when blendPoseType == UxrBlendPoseType.OpenGrip => handSide == UxrHandSide.Left ? HandDescriptorOpenLeft : HandDescriptorOpenRight,
UxrHandPoseType.Blend when blendPoseType == UxrBlendPoseType.ClosedGrip => handSide == UxrHandSide.Left ? HandDescriptorClosedLeft : HandDescriptorClosedRight,
_ => throw new ArgumentOutOfRangeException(nameof(blendPoseType), blendPoseType, null)
};
}
#endregion
#region Private Types & Data
private UxrRuntimeHandDescriptor HandDescriptorLeft { get; }
private UxrRuntimeHandDescriptor HandDescriptorRight { get; }
private UxrRuntimeHandDescriptor HandDescriptorOpenLeft { get; }
private UxrRuntimeHandDescriptor HandDescriptorOpenRight { get; }
private UxrRuntimeHandDescriptor HandDescriptorClosedLeft { get; }
private UxrRuntimeHandDescriptor HandDescriptorClosedRight { get; }
#endregion
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d59567a2d8284214d9dad39f48fc22b7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,118 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="UxrWristTorsionInfo.cs" company="VRMADA">
// Copyright (c) VRMADA, All rights reserved.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
using UnityEngine;
namespace UltimateXR.Avatar.Rig
{
/// <summary>
/// Stores information about wrist torsion. Updated by <see cref="UxrAvatarRig" />.
/// </summary>
public class UxrWristTorsionInfo
{
#region Public Types & Data
/// <summary>
/// The wrist rotation along the elbow->hand axis.
/// </summary>
public float WristTorsionAngle { get; private set; }
#endregion
#region Constructors & Finalizer
/// <summary>
/// Constructor.
/// </summary>
public UxrWristTorsionInfo()
{
_wristTorsionMinAngle = WristTorsionLimitSideLong;
_wristTorsionMaxAngle = -WristTorsionLimitSideShort;
}
#endregion
#region Public Methods
/// <summary>
/// Updates the wrist torsion information to the current frame.
/// </summary>
/// <param name="forearm">Forearm bone</param>
/// <param name="hand">Hand bone</param>
/// <param name="armInfo">Arm information</param>
public void UpdateInfo(Transform forearm, Transform hand, UxrAvatarArmInfo armInfo)
{
if (hand && forearm && armInfo.ForearmUniversalLocalAxes != null && armInfo.HandUniversalLocalAxes != null)
{
Vector3 currentHandForwardInForearm = forearm.InverseTransformDirection(armInfo.HandUniversalLocalAxes.WorldForward);
Vector3 currentHandUpInForearm = forearm.InverseTransformDirection(armInfo.HandUniversalLocalAxes.WorldUp);
Vector3 currentHandRightInForearm = forearm.InverseTransformDirection(armInfo.HandUniversalLocalAxes.WorldRight);
currentHandUpInForearm = Vector3.ProjectOnPlane(currentHandUpInForearm, armInfo.ForearmUniversalLocalAxes.LocalForward);
currentHandRightInForearm = Vector3.ProjectOnPlane(currentHandRightInForearm, armInfo.ForearmUniversalLocalAxes.LocalForward);
float angleRight = Vector3.SignedAngle(armInfo.ForearmUniversalLocalAxes.LocalRight, currentHandRightInForearm, armInfo.ForearmUniversalLocalAxes.LocalForward);
float angle = angleRight; // Works better than angleUp because of hand movement constraints
// Check for twists greater than 180 degrees, to know which way to turn to
if (WristTorsionAngle > WristTorsionOvershootAngleThreshold && angle < -WristTorsionOvershootAngleThreshold)
{
_wristTorsionOvershoot++;
}
else if (WristTorsionAngle < -WristTorsionOvershootAngleThreshold && angle > WristTorsionOvershootAngleThreshold)
{
_wristTorsionOvershoot--;
}
// Compute the overshoot if necessary
float finalAngle = angle;
if (_wristTorsionOvershoot > 0)
{
finalAngle = 180.0f + 360.0f * (_wristTorsionOvershoot - 1) + (angle + 180.0f);
}
else if (_wristTorsionOvershoot < 0)
{
finalAngle = -180.0f - -360.0f * (_wristTorsionOvershoot + 1) - (180.0f - angle);
}
if (finalAngle > _wristTorsionMinAngle && _wristTorsionOvershoot != 0)
{
_wristTorsionOvershoot = 0;
finalAngle = finalAngle + 360.0f;
}
else if (finalAngle < _wristTorsionMaxAngle && _wristTorsionOvershoot != 0)
{
_wristTorsionOvershoot = 0;
finalAngle = finalAngle - 360.0f;
}
// Rotation
WristTorsionAngle = finalAngle;
}
}
#endregion
#region Private Types & Data
// Constants
private const float WristTorsionOvershootAngleThreshold = 150.0f;
private const float WristTorsionLimitSideShort = 200.0f;
private const float WristTorsionLimitSideLong = 300.0f;
// Internal vars
private readonly float _wristTorsionMinAngle;
private readonly float _wristTorsionMaxAngle;
private int _wristTorsionOvershoot;
#endregion
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 8a49fd0b3c9a41a29147e60e8e6c5324
timeCreated: 1642870539