Files
dungeons/Assets/UltimateXR/Runtime/Scripts/Avatar/Rig/UxrWristTorsionInfo.cs
2024-08-06 21:58:35 +02:00

118 lines
4.6 KiB
C#

// --------------------------------------------------------------------------------------------------------------------
// <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
}
}