Add ultimate xr
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 61659f6f84b345fb80c4ea36eb6e428b
|
||||
timeCreated: 1643797242
|
||||
@@ -0,0 +1,53 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="IUxrAvatarControllerUpdater.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UltimateXR.Manipulation;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Avatar.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// Internal interface for avatar controllers to make updating publicly available only from within the framework.
|
||||
/// Child classes from <see cref="UxrAvatarController" /> will implement these through the protected methods.
|
||||
/// </summary>
|
||||
internal interface IUxrAvatarControllerUpdater
|
||||
{
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Updates the avatar for the given frame. This is normally in charge of updating input devices, tracking devices and
|
||||
/// locomotion.
|
||||
/// Animation is left for a later stage (<see cref="UpdateAvatarAnimation" />), to make sure it is performed in the
|
||||
/// right order right after Unity has updated the built-in animation components such as <see cref="Animator" />.
|
||||
/// </summary>
|
||||
void UpdateAvatar();
|
||||
|
||||
/// <summary>
|
||||
/// Updates the avatar using the current tracking data.
|
||||
/// </summary>
|
||||
void UpdateAvatarUsingTrackingDevices();
|
||||
|
||||
/// <summary>
|
||||
/// Updates the avatar manipulation actions based on user input.
|
||||
/// </summary>
|
||||
void UpdateAvatarManipulation();
|
||||
|
||||
/// <summary>
|
||||
/// Updates the animation and rig transforms for the given frame. It is performed in a later stage than
|
||||
/// <see cref="UpdateAvatar" /> to make sure the transforms override the transforms that Unity may have updated using
|
||||
/// built-in components such as <see cref="Animator" />.
|
||||
/// </summary>
|
||||
void UpdateAvatarAnimation();
|
||||
|
||||
/// <summary>
|
||||
/// Updates the avatar for a given frame, at the end of all stages and UltimateXR manager updates such as the
|
||||
/// <see cref="UxrGrabManager" />. It can be used to perform operations that require to be executed at the end of all
|
||||
/// stages, such as Inverse Kinematics.
|
||||
/// </summary>
|
||||
void UpdateAvatarPostProcess();
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bb702973c8fd0cc48a9313073ec7f4d5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,43 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrAnimationType.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
namespace UltimateXR.Avatar.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// Enumerates the different well-known hand animation types (poses).
|
||||
/// </summary>
|
||||
public enum UxrAnimationType
|
||||
{
|
||||
/// <summary>
|
||||
/// Non-relevant left hand pose
|
||||
/// </summary>
|
||||
LeftHandOther,
|
||||
|
||||
/// <summary>
|
||||
/// Non-relevant right hand pose
|
||||
/// </summary>
|
||||
RightHandOther,
|
||||
|
||||
/// <summary>
|
||||
/// Left hand pose used for grabbing.
|
||||
/// </summary>
|
||||
LeftHandGrab,
|
||||
|
||||
/// <summary>
|
||||
/// Right hand pose used for grabbing.
|
||||
/// </summary>
|
||||
RightHandGrab,
|
||||
|
||||
/// <summary>
|
||||
/// Left hand pose used for pointing with the finger.
|
||||
/// </summary>
|
||||
LeftFingerPoint,
|
||||
|
||||
/// <summary>
|
||||
/// Right hand pose used for pointing with the finger.
|
||||
/// </summary>
|
||||
RightFingerPoint
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 318001c1b92249558b000fc871900128
|
||||
timeCreated: 1642868726
|
||||
@@ -0,0 +1,216 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrAvatarController.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UltimateXR.Core;
|
||||
using UltimateXR.Core.Components.Composite;
|
||||
using UltimateXR.Devices;
|
||||
using UltimateXR.Locomotion;
|
||||
using UltimateXR.Manipulation;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Avatar.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for <see cref="UxrAvatar" /> controllers. <see cref="UxrAvatarController" /> components are responsible
|
||||
/// for updating the avatar. UltimateXR provides the <see cref="UxrStandardAvatarController" /> which has great
|
||||
/// functionality. For flexibility and scalability, different avatar controllers can be created if required.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <see cref="UxrAvatarController" /> components require the <see cref="UxrAvatar" /> component in the same
|
||||
/// <see cref="GameObject" /> or any of its parents. Only one <see cref="UxrAvatarController" /> component type can
|
||||
/// be active at the same time.
|
||||
/// </remarks>
|
||||
[DisallowMultipleComponent]
|
||||
[RequireComponent(typeof(UxrAvatar))]
|
||||
public abstract class UxrAvatarController : UxrAvatarComponent<UxrAvatarController>, IUxrAvatarControllerUpdater
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[SerializeField] private bool _allowHandTracking = true;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the avatar controller finished startup and can be updated.
|
||||
/// </summary>
|
||||
public abstract bool Initialized { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the avatar is using smooth locomotion. Smooth locomotion updates the position every
|
||||
/// frame, whereas non-smooth locomotion will move the avatar from one place to another in "jumps".
|
||||
/// </summary>
|
||||
public abstract bool UsesSmoothLocomotion { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether hand tracking is used when available.
|
||||
/// </summary>
|
||||
public bool AllowHandTracking
|
||||
{
|
||||
get => _allowHandTracking;
|
||||
set => _allowHandTracking = value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Explicit IUxrAvatarControllerUpdater
|
||||
|
||||
/// <inheritdoc />
|
||||
void IUxrAvatarControllerUpdater.UpdateAvatar()
|
||||
{
|
||||
// Will call the protected method, which is allowed to be overriden by child classes while
|
||||
// hiding the functionality so that it is handled by the framework in the correct order.
|
||||
UpdateAvatar();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
void IUxrAvatarControllerUpdater.UpdateAvatarUsingTrackingDevices()
|
||||
{
|
||||
UpdateAvatarUsingTrackingDevices();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
void IUxrAvatarControllerUpdater.UpdateAvatarManipulation()
|
||||
{
|
||||
// Will call the protected method, which is allowed to be overriden by child classes while
|
||||
// hiding the functionality so that it is handled by the framework in the correct order.
|
||||
UpdateAvatarManipulation();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
void IUxrAvatarControllerUpdater.UpdateAvatarAnimation()
|
||||
{
|
||||
// Will call the protected method, which is allowed to be overriden by child classes while
|
||||
// hiding the functionality so that it is handled by the framework in the correct order.
|
||||
UpdateAvatarAnimation();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
void IUxrAvatarControllerUpdater.UpdateAvatarPostProcess()
|
||||
{
|
||||
// Will call the protected method, which is allowed to be overriden by child classes while
|
||||
// hiding the functionality so that it is handled by the framework in the correct order.
|
||||
UpdateAvatarPostProcess();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Gets if the hand is available to interact with UI elements, such as pressing buttons. This is used by the UI
|
||||
/// interaction system to ignore the hand for these events.
|
||||
/// For example, when the hand is holding an object it could be desirable to not let it interact inadvertently with any
|
||||
/// user interface.
|
||||
/// </summary>
|
||||
/// <param name="handSide">Which hand to check</param>
|
||||
/// <returns>Whether the given handed can interact with user interfaces</returns>
|
||||
public virtual bool CanHandInteractWithUI(UxrHandSide handSide)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Protected Methods
|
||||
|
||||
/// <summary>
|
||||
/// Updates the avatar for the given frame. This is normally in charge of updating input devices, tracking devices and
|
||||
/// locomotion.
|
||||
/// Animation is left for a later stage (<see cref="UpdateAvatarAnimation" />), to make sure it is performed in the
|
||||
/// right order right after Unity has updated the built-in animation components such as <see cref="Animator" />.
|
||||
/// </summary>
|
||||
protected virtual void UpdateAvatar()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the avatar manipulation actions based on user input.
|
||||
/// </summary>
|
||||
protected virtual void UpdateAvatarManipulation()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the animation and rig transforms for the given frame. It is performed in a later stage than
|
||||
/// <see cref="UpdateAvatar" /> to make sure the transforms override the transforms that Unity may have updated using
|
||||
/// built-in components such as <see cref="Animator" />.
|
||||
/// </summary>
|
||||
protected virtual void UpdateAvatarAnimation()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the avatar for a given frame, at the end of all stages and UltimateXR manager updates such as the
|
||||
/// <see cref="UxrGrabManager" />. It can be used to perform operations that require to be executed at the end of all
|
||||
/// stages, such as Inverse Kinematics.
|
||||
/// </summary>
|
||||
protected virtual void UpdateAvatarPostProcess()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the currently enabled input devices.
|
||||
/// </summary>
|
||||
protected void UpdateInputDevice()
|
||||
{
|
||||
foreach (UxrControllerInput controllerInput in Avatar.EnabledControllerInputs)
|
||||
{
|
||||
// Call method using internal interface
|
||||
((IUxrControllerInputUpdater)controllerInput).UpdateInput();
|
||||
}
|
||||
|
||||
// Refresh render mode
|
||||
Avatar.RenderMode = Avatar.RenderMode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the tracking devices.
|
||||
/// </summary>
|
||||
protected void UpdateTrackingDevices()
|
||||
{
|
||||
foreach (UxrTrackingDevice trackingDevice in Avatar.TrackingDevices)
|
||||
{
|
||||
if (trackingDevice && trackingDevice.enabled)
|
||||
{
|
||||
// Update tracking by calling internal interface
|
||||
((IUxrTrackingUpdater)trackingDevice).UpdateSensors();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the avatar using the current tracking data.
|
||||
/// </summary>
|
||||
protected void UpdateAvatarUsingTrackingDevices()
|
||||
{
|
||||
foreach (UxrTrackingDevice trackingDevice in Avatar.TrackingDevices)
|
||||
{
|
||||
if (trackingDevice && trackingDevice.enabled)
|
||||
{
|
||||
// Update avatar by calling internal interface
|
||||
((IUxrTrackingUpdater)trackingDevice).UpdateAvatar();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the enabled locomotion components in the avatar.
|
||||
/// </summary>
|
||||
protected void UpdateLocomotion()
|
||||
{
|
||||
foreach (UxrLocomotion locomotion in UxrLocomotion.GetComponents<UxrLocomotion>(Avatar))
|
||||
{
|
||||
if (locomotion.gameObject.activeInHierarchy && locomotion.enabled)
|
||||
{
|
||||
((IUxrLocomotionUpdater)locomotion).UpdateLocomotion();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 013c3c69bff86ce49b3a90666e902653
|
||||
timeCreated: 1500469656
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,92 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrAvatarControllerEvent.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using UltimateXR.Devices;
|
||||
using UltimateXR.Manipulation.HandPoses;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Avatar.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// Describes an event that maps an XR controller input to a hand pose. This allows to show different poses when
|
||||
/// certain buttons are pressed. It also allows to describe which poses need to be used when grabbing or pointing
|
||||
/// with the finger.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class UxrAvatarControllerEvent
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[SerializeField] private UxrInputButtons _buttons;
|
||||
[SerializeField] private UxrAnimationType _animationType;
|
||||
[SerializeField] private UxrHandPoseAsset _handPose;
|
||||
[SerializeField] [Range(0.0f, 1.0f)] private float _poseBlendValue;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets the hand pose name that should be used on the event.
|
||||
/// </summary>
|
||||
public string PoseName => string.IsNullOrEmpty(_poseNameOverride) ? _handPose != null ? _handPose.name : null : _poseNameOverride;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the button(s) that trigger the animation event.
|
||||
/// </summary>
|
||||
public UxrInputButtons Buttons
|
||||
{
|
||||
get => _buttons;
|
||||
set => _buttons = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of animation the event represents. This allows to keep track of certain key animations such as
|
||||
/// grabbing or pointing with the finger, that are used in the framework.
|
||||
/// </summary>
|
||||
public UxrAnimationType TypeOfAnimation
|
||||
{
|
||||
get => _animationType;
|
||||
set => _animationType = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the pose name that will be used instead of the pose stored. If null, the pose will be used instead.
|
||||
/// </summary>
|
||||
public string PoseNameOverride
|
||||
{
|
||||
get => _poseNameOverride;
|
||||
set => _poseNameOverride = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the pose blend value if the pose is <see cref="UxrHandPoseType.Blend" />.
|
||||
/// </summary>
|
||||
public float PoseBlendValue
|
||||
{
|
||||
get => _poseBlendValue;
|
||||
set => _poseBlendValue = value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Overrides object
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
return $"Event type: {_animationType}, button(s): {_buttons}, pose: {PoseName}, blend: {_poseBlendValue}";
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
private string _poseNameOverride;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ae488b3f906744fa8c3716371798b17e
|
||||
timeCreated: 1642868667
|
||||
@@ -0,0 +1,23 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrEventVarAction.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Avatar.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// Enumerates possible actions over an <see cref="Animator" /> component. Used by
|
||||
/// <see cref="UxrStandardAvatarController" />.
|
||||
/// </summary>
|
||||
public enum UxrEventVarAction
|
||||
{
|
||||
DoNothing,
|
||||
ToggleBool,
|
||||
SetBool,
|
||||
SetInt,
|
||||
SetFloat,
|
||||
Trigger
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3030f7d0b28644dca25a59c235aa629d
|
||||
timeCreated: 1642868731
|
||||
@@ -0,0 +1,87 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrFingerPointingVolume.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UltimateXR.Core;
|
||||
using UltimateXR.Core.Components;
|
||||
using UltimateXR.Extensions.Unity.Math;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Avatar.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// Component that describes a box volume where any <see cref="UxrAvatar" /> hand that gets inside automatically adopts
|
||||
/// a finger pointing pose. This is useful to place in front of UI screens or where precise finger pressing interaction
|
||||
/// is required.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The finger pointing pose should only be adopted if it doesn't interfere with any other interaction, such
|
||||
/// as the grab pose while grabbing an object inside the volume.
|
||||
/// </remarks>
|
||||
[RequireComponent(typeof(BoxCollider))]
|
||||
public class UxrFingerPointingVolume : UxrComponent<UxrFingerPointingVolume>
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[SerializeField] private bool _leftHand = true;
|
||||
[SerializeField] private bool _rightHand = true;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="BoxCollider" /> component describing the enclosed space where to adopt the finger pointing
|
||||
/// pose.
|
||||
/// </summary>
|
||||
public BoxCollider Box => GetCachedComponent<BoxCollider>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the left hand should adopt the pose when inside.
|
||||
/// </summary>
|
||||
public bool UseLeftHand
|
||||
{
|
||||
get => _leftHand;
|
||||
set => _leftHand = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the right hand should adopt the pose when inside.
|
||||
/// </summary>
|
||||
public bool UseRightHand
|
||||
{
|
||||
get => _rightHand;
|
||||
set => _rightHand = value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a point is inside the <see cref="BoxCollider" /> attached to the <see cref="GameObject" /> this component
|
||||
/// is attached to.
|
||||
/// </summary>
|
||||
/// <param name="point">Point in world coordinates</param>
|
||||
/// <param name="margin">Margin to add to the box sides</param>
|
||||
/// <returns>True if it is inside, false if not</returns>
|
||||
public bool IsPointInside(Vector3 point, float margin = 0.0f)
|
||||
{
|
||||
return point.IsInsideBox(Box, Vector3.one * margin);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the volume is compatible with the given hand. This allows some volumes to work for the left or
|
||||
/// right hand only.
|
||||
/// </summary>
|
||||
/// <param name="handSide">Hand to check</param>
|
||||
/// <returns>Boolean telling whether the given hand is compatible or not</returns>
|
||||
public bool IsCompatible(UxrHandSide handSide)
|
||||
{
|
||||
return (handSide == UxrHandSide.Left && UseLeftHand) || (handSide == UxrHandSide.Right && UseRightHand);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: db12429e9acfd824a9ecbc4760a629bf
|
||||
timeCreated: 1504532444
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,162 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrHandPoseVolume.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UltimateXR.Core;
|
||||
using UltimateXR.Core.Components;
|
||||
using UltimateXR.Extensions.Unity.Math;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Avatar.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// Component that describes a box volume where any <see cref="UxrAvatar" /> hand that gets inside automatically adopts
|
||||
/// a given hand pose.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The finger pointing pose should only be adopted if it doesn't interfere with any other interaction, such
|
||||
/// as the grab pose while grabbing an object inside the volume.
|
||||
/// </remarks>
|
||||
[RequireComponent(typeof(BoxCollider))]
|
||||
public class UxrHandPoseVolume : UxrComponent<UxrHandPoseVolume>
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[SerializeField] private string _poseName;
|
||||
[SerializeField] private bool _leftHand = true;
|
||||
[SerializeField] private bool _rightHand = true;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="BoxCollider" /> component describing the enclosed space where to adopt the pose.
|
||||
/// </summary>
|
||||
public BoxCollider Box => GetCachedComponent<BoxCollider>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the left hand should adopt the pose when inside.
|
||||
/// </summary>
|
||||
public bool UseLeftHand
|
||||
{
|
||||
get => _leftHand;
|
||||
set => _leftHand = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the right hand should adopt the pose when inside.
|
||||
/// </summary>
|
||||
public bool UseRightHand
|
||||
{
|
||||
get => _rightHand;
|
||||
set => _rightHand = value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unity
|
||||
|
||||
/// <summary>
|
||||
/// Subscribes to the avatars updated event.
|
||||
/// </summary>
|
||||
protected override void OnEnable()
|
||||
{
|
||||
base.OnEnable();
|
||||
|
||||
UxrManager.AvatarsUpdated += UxrManager_AvatarsUpdated;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unsubscribes from the avatars updated event.
|
||||
/// </summary>
|
||||
protected override void OnDisable()
|
||||
{
|
||||
base.OnDisable();
|
||||
|
||||
UxrManager.AvatarsUpdated -= UxrManager_AvatarsUpdated;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Handling Methods
|
||||
|
||||
/// <summary>
|
||||
/// Called every frame after the avatars have been updated. Performs the hand check.
|
||||
/// </summary>
|
||||
private void UxrManager_AvatarsUpdated()
|
||||
{
|
||||
if (UxrAvatar.LocalAvatar == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (UxrAvatar.LocalAvatar.AvatarController is UxrStandardAvatarController avatarController)
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsCompatible(UxrHandSide.Left) && IsPointInside(UxrAvatar.LocalAvatar.GetHand(UxrHandSide.Left).Wrist.position))
|
||||
{
|
||||
avatarController.LeftHandDefaultPoseNameOverride = _poseName;
|
||||
_leftWasInside = true;
|
||||
}
|
||||
else if (_leftWasInside)
|
||||
{
|
||||
avatarController.LeftHandDefaultPoseNameOverride = null;
|
||||
_leftWasInside = false;
|
||||
}
|
||||
|
||||
if (IsCompatible(UxrHandSide.Right) && IsPointInside(UxrAvatar.LocalAvatar.GetHand(UxrHandSide.Right).Wrist.position))
|
||||
{
|
||||
avatarController.RightHandDefaultPoseNameOverride = _poseName;
|
||||
_rightWasInside = true;
|
||||
}
|
||||
else if (_rightWasInside)
|
||||
{
|
||||
avatarController.RightHandDefaultPoseNameOverride = null;
|
||||
_rightWasInside = false;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a point is inside the <see cref="BoxCollider" /> attached to the <see cref="GameObject" /> this component
|
||||
/// is attached to.
|
||||
/// </summary>
|
||||
/// <param name="point">Point in world coordinates</param>
|
||||
/// <param name="margin">Margin to add to the box sides</param>
|
||||
/// <returns>True if it is inside, false if not</returns>
|
||||
private bool IsPointInside(Vector3 point, float margin = 0.0f)
|
||||
{
|
||||
return point.IsInsideBox(Box, Vector3.one * margin);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the volume is compatible with the given hand. This allows some volumes to work for the left or
|
||||
/// right hand only.
|
||||
/// </summary>
|
||||
/// <param name="handSide">Hand to check</param>
|
||||
/// <returns>Boolean telling whether the given hand is compatible or not</returns>
|
||||
private bool IsCompatible(UxrHandSide handSide)
|
||||
{
|
||||
return (handSide == UxrHandSide.Left && UseLeftHand) || (handSide == UxrHandSide.Right && UseRightHand);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
private bool _leftWasInside;
|
||||
private bool _rightWasInside;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 75af3a4860c324a4f8873ef035f52bec
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,48 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrStandardAvatarController.ControllerEventTypes.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
|
||||
namespace UltimateXR.Avatar.Controllers
|
||||
{
|
||||
public partial class UxrStandardAvatarController
|
||||
{
|
||||
#region Private Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Flags that control which <see cref="UxrAnimationType" /> to process when processing events.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
private enum ControllerEventTypes
|
||||
{
|
||||
/// <summary>
|
||||
/// No types.
|
||||
/// </summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// All other animation types that are not <see cref="Grab" /> or <see cref="Point" />.
|
||||
/// </summary>
|
||||
Other = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Events that are for the grab animation.
|
||||
/// </summary>
|
||||
Grab = 1 << 1,
|
||||
|
||||
/// <summary>
|
||||
/// Events that are for the finger pointing animation.
|
||||
/// </summary>
|
||||
Point = 1 << 2,
|
||||
|
||||
/// <summary>
|
||||
/// All event.
|
||||
/// </summary>
|
||||
All = ~None
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 94f8b332033d4435b104cf1d25c38dac
|
||||
timeCreated: 1645458547
|
||||
@@ -0,0 +1,44 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrStandardAvatarController.EventProcessing.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Avatar.Controllers
|
||||
{
|
||||
public partial class UxrStandardAvatarController
|
||||
{
|
||||
#region Private Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Flags that control which actions to perform when processing events.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
private enum EventProcessing
|
||||
{
|
||||
/// <summary>
|
||||
/// Do nothing.
|
||||
/// </summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Update the internal state.
|
||||
/// </summary>
|
||||
InternalVars = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Execute the actions that change <see cref="Animator" /> variables.
|
||||
/// </summary>
|
||||
ExecuteActions = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Everything.
|
||||
/// </summary>
|
||||
All = ~None
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 42a426d70a134452895a00468c1c35ae
|
||||
timeCreated: 1645458565
|
||||
@@ -0,0 +1,78 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrStandardAvatarController.HandInfo.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UltimateXR.Devices;
|
||||
|
||||
namespace UltimateXR.Avatar.Controllers
|
||||
{
|
||||
public sealed partial class UxrStandardAvatarController
|
||||
{
|
||||
#region Private Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Stores relevant information of a hand required by the <see cref="UxrStandardAvatarController" /> at runtime.
|
||||
/// </summary>
|
||||
private class HandInfo
|
||||
{
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the index of the grab animation event. That is, the event whose animation is
|
||||
/// <see cref="UxrAnimationType.LeftHandGrab" />/ <see cref="UxrAnimationType.RightHandGrab" />.
|
||||
/// </summary>
|
||||
public int GrabEventIndex { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the hand pose name of <see cref="GrabEventIndex" /> at the beginning.
|
||||
/// </summary>
|
||||
public string InitialHandGrabPoseName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="UxrAvatarControllerEvent.Buttons" /> required to activate the
|
||||
/// <see cref="GrabEventIndex" /> at the beginning.
|
||||
/// </summary>
|
||||
public UxrInputButtons InitialHandGrabButtons { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the hand is currently grabbing.
|
||||
/// </summary>
|
||||
public bool IsGrabbing { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the hand is currently pointing.
|
||||
/// </summary>
|
||||
public bool IsPointing { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the hand has currently a finger tip inside a <see cref="IsInsideFingerPointingVolume" />.
|
||||
/// </summary>
|
||||
public bool IsInsideFingerPointingVolume { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the hand was grabbing last frame.
|
||||
/// </summary>
|
||||
public bool WasGrabbingLastFrame { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the hand was pointing last frame.
|
||||
/// </summary>
|
||||
public bool WasPointingLastFrame { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the hand should be let grab again. Used to control grab/release.
|
||||
/// </summary>
|
||||
public bool LetGrabAgain { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the value between range [0.0, 1.0] that controls the grab pose blending.
|
||||
/// </summary>
|
||||
public float GrabBlendValue { get; set; }
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 90694b6e290d409199c86be7fa7e7f29
|
||||
timeCreated: 1645461833
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: eed24b4dea1e5534ba091b2188144a9f
|
||||
timeCreated: 1500469656
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/UltimateXR/Runtime/Scripts/Avatar/Rig.meta
Normal file
8
Assets/UltimateXR/Runtime/Scripts/Avatar/Rig.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6bbea2b37477e0f458e60a77d09f4942
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
108
Assets/UltimateXR/Runtime/Scripts/Avatar/Rig/UxrAvatarArm.cs
Normal file
108
Assets/UltimateXR/Runtime/Scripts/Avatar/Rig/UxrAvatarArm.cs
Normal 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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: da57810a3b5b46bea5eeb76c7126a84e
|
||||
timeCreated: 1642869983
|
||||
308
Assets/UltimateXR/Runtime/Scripts/Avatar/Rig/UxrAvatarArmInfo.cs
Normal file
308
Assets/UltimateXR/Runtime/Scripts/Avatar/Rig/UxrAvatarArmInfo.cs
Normal 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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b35d6ec8a0474d098337128d5632355a
|
||||
timeCreated: 1665136779
|
||||
138
Assets/UltimateXR/Runtime/Scripts/Avatar/Rig/UxrAvatarFinger.cs
Normal file
138
Assets/UltimateXR/Runtime/Scripts/Avatar/Rig/UxrAvatarFinger.cs
Normal 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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3e673c1d8af742c8860001ea223db873
|
||||
timeCreated: 1642870254
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 017ad516096f4f4d83da5739003991c1
|
||||
timeCreated: 1665409719
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 78a4d19d90f3ad74ba6d15bad66d317e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
266
Assets/UltimateXR/Runtime/Scripts/Avatar/Rig/UxrAvatarHand.cs
Normal file
266
Assets/UltimateXR/Runtime/Scripts/Avatar/Rig/UxrAvatarHand.cs
Normal 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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3919586f4d604a16b0ec7add5c69efc7
|
||||
timeCreated: 1642870018
|
||||
111
Assets/UltimateXR/Runtime/Scripts/Avatar/Rig/UxrAvatarHead.cs
Normal file
111
Assets/UltimateXR/Runtime/Scripts/Avatar/Rig/UxrAvatarHead.cs
Normal 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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f4b4f49218ec4da0ad4ef5a11873e935
|
||||
timeCreated: 1642869938
|
||||
96
Assets/UltimateXR/Runtime/Scripts/Avatar/Rig/UxrAvatarLeg.cs
Normal file
96
Assets/UltimateXR/Runtime/Scripts/Avatar/Rig/UxrAvatarLeg.cs
Normal 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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: da12d13ccf124d2ebdc953bc3e6fd252
|
||||
timeCreated: 1642870229
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 31205aed49ef71b4ba0d90af889b71d0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2f2c662dbf55f174fa6f6573d46902d4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
278
Assets/UltimateXR/Runtime/Scripts/Avatar/Rig/UxrAvatarRig.cs
Normal file
278
Assets/UltimateXR/Runtime/Scripts/Avatar/Rig/UxrAvatarRig.cs
Normal 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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0b73bc61877ab504f86f43111f8741f6
|
||||
timeCreated: 1500467196
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
100
Assets/UltimateXR/Runtime/Scripts/Avatar/Rig/UxrAvatarRigInfo.cs
Normal file
100
Assets/UltimateXR/Runtime/Scripts/Avatar/Rig/UxrAvatarRigInfo.cs
Normal 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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ab7172ff6a084e05ac48004c5a3005f8
|
||||
timeCreated: 1642870290
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a1bf48fd9417e3e4296ae3f98040766b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a420255799f344bdbe38b3f23d781e2c
|
||||
timeCreated: 1643731537
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1c3762c7ba56108419a2aa6db53865c5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 990a211ff33b9ee4e967c89dd4b56f87
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d59567a2d8284214d9dad39f48fc22b7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8a49fd0b3c9a41a29147e60e8e6c5324
|
||||
timeCreated: 1642870539
|
||||
200
Assets/UltimateXR/Runtime/Scripts/Avatar/UxrAvatar.HandState.cs
Normal file
200
Assets/UltimateXR/Runtime/Scripts/Avatar/UxrAvatar.HandState.cs
Normal file
@@ -0,0 +1,200 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrAvatar.HandState.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UltimateXR.Avatar.Rig;
|
||||
using UltimateXR.Core;
|
||||
using UltimateXR.Manipulation.HandPoses;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Avatar
|
||||
{
|
||||
public partial class UxrAvatar
|
||||
{
|
||||
#region Private Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Stores the state of an <see cref="UxrAvatar" /> hand.
|
||||
/// </summary>
|
||||
private class HandState
|
||||
{
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current hand pose name or null if there is no current hand pose set.
|
||||
/// </summary>
|
||||
public string CurrentHandPoseName => CurrentHandPose?.PoseName;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current hand pose.
|
||||
/// </summary>
|
||||
public UxrRuntimeHandPose CurrentHandPose => _currentHandPose;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current blend value, if <see cref="CurrentHandPose" /> is a <see cref="UxrHandPoseType.Blend" /> pose.
|
||||
/// </summary>
|
||||
public float CurrentBlendValue => _currentBlendValue;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether a given event would change the current state.
|
||||
/// </summary>
|
||||
/// <param name="e">Event arguments</param>
|
||||
/// <returns>Whether the event would change the current state</returns>
|
||||
public bool IsChange(UxrAvatarHandPoseChangeEventArgs e)
|
||||
{
|
||||
if (CurrentHandPose == null || e.PoseName != CurrentHandPoseName)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (CurrentHandPose.PoseType == UxrHandPoseType.Blend && Mathf.Abs(e.BlendValue - CurrentBlendValue) > BlendEpsilon)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changes the current pose.
|
||||
/// </summary>
|
||||
/// <param name="handPose">New hand pose</param>
|
||||
/// <param name="blendValue">New blend value, if the given pose is <see cref="UxrHandPoseType.Blend" /></param>
|
||||
public void SetPose(UxrRuntimeHandPose handPose, float blendValue)
|
||||
{
|
||||
if (CurrentHandPose != handPose)
|
||||
{
|
||||
_needsBlend = true;
|
||||
|
||||
if (CurrentHandPose != null)
|
||||
{
|
||||
// Start smooth transition to new pose.
|
||||
|
||||
_currentHandPoseFrom = CurrentHandPose;
|
||||
_currentHandPose = handPose;
|
||||
_blendTimer = PoseTransitionSeconds;
|
||||
}
|
||||
else
|
||||
{
|
||||
// First initialization: transition immediately.
|
||||
|
||||
_currentHandPoseFrom = handPose;
|
||||
_currentHandPose = handPose;
|
||||
_blendTimer = -1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
_currentBlendValueFrom = CurrentBlendValue;
|
||||
|
||||
if (!Mathf.Approximately(_currentBlendValue, blendValue) && CurrentHandPose != null && CurrentHandPose.PoseType == UxrHandPoseType.Blend)
|
||||
{
|
||||
_currentBlendValue = blendValue;
|
||||
_needsBlend = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the hand, mainly transitioning smoothly between poses.
|
||||
/// </summary>
|
||||
/// <param name="avatar">The avatar to update</param>
|
||||
/// <param name="handSide">The hand to update</param>
|
||||
/// <param name="deltaTime">Delta time in seconds</param>
|
||||
public void Update(UxrAvatar avatar, UxrHandSide handSide, float deltaTime)
|
||||
{
|
||||
if (_blendTimer > 0.0f)
|
||||
{
|
||||
_blendTimer -= deltaTime;
|
||||
}
|
||||
|
||||
if (_blendTimer < 0.0f)
|
||||
{
|
||||
_blendTimer = -1.0f;
|
||||
}
|
||||
|
||||
if (_needsBlend)
|
||||
{
|
||||
// Blend the poses
|
||||
|
||||
float t = Mathf.Clamp01(1.0f - _blendTimer / PoseTransitionSeconds);
|
||||
BlendPoses(avatar, handSide, t);
|
||||
}
|
||||
|
||||
if (_blendTimer < 0.0f)
|
||||
{
|
||||
_needsBlend = false;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// Updates the avatar hand, interpolating between the "from" and "to" poses.
|
||||
/// </summary>
|
||||
/// <param name="avatar">Avatar to update</param>
|
||||
/// <param name="handSide">Hand to update</param>
|
||||
/// <param name="t">Interpolation value [0.0, 1.0]</param>
|
||||
private void BlendPoses(UxrAvatar avatar, UxrHandSide handSide, float t)
|
||||
{
|
||||
if (_currentHandPoseFrom == null || _currentHandPose == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Compute the hand descriptors to interpolate
|
||||
|
||||
_currentDescriptorFrom.CopyFrom(_currentHandPoseFrom.GetHandDescriptor(handSide, UxrBlendPoseType.OpenGrip));
|
||||
_currentDescriptor.CopyFrom(_currentHandPose.GetHandDescriptor(handSide, UxrBlendPoseType.OpenGrip));
|
||||
|
||||
// If any of the hand poses has a blend type, compute the blended pose first
|
||||
|
||||
if (_currentHandPoseFrom.PoseType == UxrHandPoseType.Blend)
|
||||
{
|
||||
_currentDescriptorFrom.InterpolateTo(_currentHandPoseFrom.GetHandDescriptor(handSide, UxrBlendPoseType.ClosedGrip), _currentBlendValueFrom);
|
||||
}
|
||||
|
||||
if (_currentHandPose.PoseType == UxrHandPoseType.Blend)
|
||||
{
|
||||
_currentDescriptor.InterpolateTo(_currentHandPose.GetHandDescriptor(handSide, UxrBlendPoseType.ClosedGrip), _currentBlendValue);
|
||||
}
|
||||
|
||||
// Now interpolate between the two and update the hand transforms
|
||||
|
||||
UxrAvatarRig.UpdateHandUsingRuntimeDescriptor(avatar, handSide, _currentDescriptorFrom, _currentDescriptor, t);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Time in seconds it takes to transition from one pose to another.
|
||||
/// </summary>
|
||||
private const float PoseTransitionSeconds = 0.1f;
|
||||
|
||||
/// <summary>
|
||||
/// Value that will be used as epsilon to compare two pose blend values.
|
||||
/// </summary>
|
||||
private const float BlendEpsilon = 0.005f;
|
||||
|
||||
private readonly UxrRuntimeHandDescriptor _currentDescriptorFrom = new UxrRuntimeHandDescriptor();
|
||||
private readonly UxrRuntimeHandDescriptor _currentDescriptor = new UxrRuntimeHandDescriptor();
|
||||
private UxrRuntimeHandPose _currentHandPoseFrom;
|
||||
private UxrRuntimeHandPose _currentHandPose;
|
||||
private float _currentBlendValueFrom;
|
||||
private float _currentBlendValue;
|
||||
private float _blendTimer = -1.0f;
|
||||
private bool _needsBlend;
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1ece573ebabb4f9c9c7bdcbb764bed95
|
||||
timeCreated: 1648650314
|
||||
146
Assets/UltimateXR/Runtime/Scripts/Avatar/UxrAvatar.StateSave.cs
Normal file
146
Assets/UltimateXR/Runtime/Scripts/Avatar/UxrAvatar.StateSave.cs
Normal file
@@ -0,0 +1,146 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrAvatar.StateSave.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System.Linq;
|
||||
using UltimateXR.Animation.Interpolation;
|
||||
using UltimateXR.Core;
|
||||
using UltimateXR.Core.StateSave;
|
||||
using UltimateXR.Devices;
|
||||
|
||||
namespace UltimateXR.Avatar
|
||||
{
|
||||
public partial class UxrAvatar
|
||||
{
|
||||
#region Protected Overrides UxrComponent
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override UxrTransformSpace TransformStateSaveSpace => GetLocalTransformIfParentedOr(UxrTransformSpace.World);
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override bool RequiresTransformSerialization(UxrStateSaveLevel level)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void SerializeState(bool isReading, int stateSerializationVersion, UxrStateSaveLevel level, UxrStateSaveOptions options)
|
||||
{
|
||||
base.SerializeState(isReading, stateSerializationVersion, level, options);
|
||||
|
||||
// TODO: Figure out how to avoid cheating by saving UxrCameraWallFade state too.
|
||||
|
||||
SerializeStateTransform(level, options, CamTransformName, UxrTransformSpace.Avatar, CameraComponent.transform);
|
||||
SerializeStateTransform(level, options, LeftHandTransformName, UxrTransformSpace.Avatar, LeftHandBone);
|
||||
SerializeStateTransform(level, options, RightHandTransformName, UxrTransformSpace.Avatar, RightHandBone);
|
||||
|
||||
// Controller and hand poses are already handled through events, we don't serialize them in incremental changes
|
||||
|
||||
if (level > UxrStateSaveLevel.ChangesSincePreviousSave)
|
||||
{
|
||||
// Avatar render mode
|
||||
|
||||
SerializeStateValue(level, options, nameof(_renderMode), ref _renderMode);
|
||||
|
||||
// We serialize the controller input
|
||||
|
||||
SerializeStateValue(level, options, null, ref _externalControllerInput);
|
||||
|
||||
// Hand poses
|
||||
|
||||
string leftPoseName = GetCurrentRuntimeHandPose(UxrHandSide.Left)?.PoseName;
|
||||
string rightPoseName = GetCurrentRuntimeHandPose(UxrHandSide.Right)?.PoseName;
|
||||
|
||||
float leftBlendValue = GetCurrentHandPoseBlendValue(UxrHandSide.Left);
|
||||
float rightBlendValue = GetCurrentHandPoseBlendValue(UxrHandSide.Right);
|
||||
|
||||
SerializeStateValue(level, options, "leftPose", ref leftPoseName);
|
||||
SerializeStateValue(level, options, "rightPose", ref rightPoseName);
|
||||
SerializeStateValue(level, options, "leftBlend", ref leftBlendValue);
|
||||
SerializeStateValue(level, options, "rightBlend", ref rightBlendValue);
|
||||
|
||||
if (isReading)
|
||||
{
|
||||
// Render mode
|
||||
|
||||
SetAvatarRenderMode(_renderMode, UxrControllerInput.GetComponents(this).ToList());
|
||||
|
||||
// When deserializing, we need to manually set the hand pose state from the serialized data.
|
||||
|
||||
if (leftPoseName != null)
|
||||
{
|
||||
SetCurrentHandPose(UxrHandSide.Left, leftPoseName, leftBlendValue);
|
||||
}
|
||||
|
||||
if (rightPoseName != null)
|
||||
{
|
||||
SetCurrentHandPose(UxrHandSide.Right, rightPoseName, rightBlendValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override UxrVarInterpolator GetInterpolator(string varName)
|
||||
{
|
||||
if (IsTransformPositionVarName(varName, CamTransformName))
|
||||
{
|
||||
return _camPosInterpolator;
|
||||
}
|
||||
if (IsTransformRotationVarName(varName, CamTransformName))
|
||||
{
|
||||
return _camRotInterpolator;
|
||||
}
|
||||
if (IsTransformPositionVarName(varName, LeftHandTransformName))
|
||||
{
|
||||
return _leftHandPosInterpolator;
|
||||
}
|
||||
if (IsTransformRotationVarName(varName, LeftHandTransformName))
|
||||
{
|
||||
return _leftHandRotInterpolator;
|
||||
}
|
||||
if (IsTransformPositionVarName(varName, RightHandTransformName))
|
||||
{
|
||||
return _rightHandPosInterpolator;
|
||||
}
|
||||
if (IsTransformRotationVarName(varName, RightHandTransformName))
|
||||
{
|
||||
return _rightHandRotInterpolator;
|
||||
}
|
||||
|
||||
// Null means using the default interpolator for the type
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void InterpolateState(in UxrStateInterpolationVars vars, float t)
|
||||
{
|
||||
base.InterpolateState(in vars, t);
|
||||
|
||||
InterpolateStateTransform(vars, t, CamTransformName, CameraComponent.transform, UxrTransformSpace.Avatar);
|
||||
InterpolateStateTransform(vars, t, LeftHandTransformName, LeftHandBone, UxrTransformSpace.Avatar);
|
||||
InterpolateStateTransform(vars, t, RightHandTransformName, RightHandBone, UxrTransformSpace.Avatar);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
private const string CamTransformName = "cam.tf";
|
||||
private const string LeftHandTransformName = "left.tf";
|
||||
private const string RightHandTransformName = "right.tf";
|
||||
|
||||
private const float SmoothPosInterpolation = 0.3f;
|
||||
private const float SmoothRotInterpolation = 0.3f;
|
||||
|
||||
private readonly UxrVector3Interpolator _camPosInterpolator = new UxrVector3Interpolator(SmoothPosInterpolation);
|
||||
private readonly UxrQuaternionInterpolator _camRotInterpolator = new UxrQuaternionInterpolator(SmoothRotInterpolation);
|
||||
private readonly UxrVector3Interpolator _leftHandPosInterpolator = new UxrVector3Interpolator(SmoothPosInterpolation);
|
||||
private readonly UxrQuaternionInterpolator _leftHandRotInterpolator = new UxrQuaternionInterpolator(SmoothRotInterpolation);
|
||||
private readonly UxrVector3Interpolator _rightHandPosInterpolator = new UxrVector3Interpolator(SmoothPosInterpolation);
|
||||
private readonly UxrQuaternionInterpolator _rightHandRotInterpolator = new UxrQuaternionInterpolator(SmoothRotInterpolation);
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0ec427c787d64708a4729428f544b320
|
||||
timeCreated: 1705744443
|
||||
1702
Assets/UltimateXR/Runtime/Scripts/Avatar/UxrAvatar.cs
Normal file
1702
Assets/UltimateXR/Runtime/Scripts/Avatar/UxrAvatar.cs
Normal file
File diff suppressed because it is too large
Load Diff
12
Assets/UltimateXR/Runtime/Scripts/Avatar/UxrAvatar.cs.meta
Normal file
12
Assets/UltimateXR/Runtime/Scripts/Avatar/UxrAvatar.cs.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 53722f72346fa6848ae1386be516505b
|
||||
timeCreated: 1500467196
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,35 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrAvatarEventArgs.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
namespace UltimateXR.Avatar
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for avatar events.
|
||||
/// </summary>
|
||||
public class UxrAvatarEventArgs
|
||||
{
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Avatar the event belongs to.
|
||||
/// </summary>
|
||||
public UxrAvatar Avatar { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors & Finalizer
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="avatar">Avatar</param>
|
||||
public UxrAvatarEventArgs(UxrAvatar avatar)
|
||||
{
|
||||
Avatar = avatar;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fc54d1f82aae4fb449ee16cb3a088f6d
|
||||
timeCreated: 1500467196
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,65 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrAvatarHandPoseChangeEventArgs.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using UltimateXR.Core;
|
||||
using UltimateXR.Manipulation.HandPoses;
|
||||
|
||||
namespace UltimateXR.Avatar
|
||||
{
|
||||
/// <summary>
|
||||
/// Event args for a hand pose change in an <see cref="UxrAvatar" />.
|
||||
/// </summary>
|
||||
public class UxrAvatarHandPoseChangeEventArgs : EventArgs
|
||||
{
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets which hand the event belongs to.
|
||||
/// </summary>
|
||||
public UxrHandSide HandSide { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the pose to change to.
|
||||
/// </summary>
|
||||
public string PoseName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the new blend value if the pose type is <see cref="UxrHandPoseType.Blend" />.
|
||||
/// </summary>
|
||||
public float BlendValue { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors & Finalizer
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="handSide">Which hand the event belongs to</param>
|
||||
/// <param name="poseName">Name of the pose to change to</param>
|
||||
/// <param name="blendValue">New blend value</param>
|
||||
public UxrAvatarHandPoseChangeEventArgs(UxrHandSide handSide,
|
||||
string poseName = "",
|
||||
float blendValue = 0.0f)
|
||||
{
|
||||
HandSide = handSide;
|
||||
PoseName = poseName;
|
||||
BlendValue = blendValue;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Overrides object
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
return $"Hand pose changed (Hand={HandSide}, PoseName={PoseName}, BlendValue={BlendValue})";
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3cf57dbde327ff442a7ee2f27bfae7d5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
24
Assets/UltimateXR/Runtime/Scripts/Avatar/UxrAvatarMode.cs
Normal file
24
Assets/UltimateXR/Runtime/Scripts/Avatar/UxrAvatarMode.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrAvatarMode.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
namespace UltimateXR.Avatar
|
||||
{
|
||||
/// <summary>
|
||||
/// Avatar operating modes.
|
||||
/// </summary>
|
||||
public enum UxrAvatarMode
|
||||
{
|
||||
/// <summary>
|
||||
/// Avatar is updated automatically using input/tracking components. This is the avatar that is controlled by the user.
|
||||
/// </summary>
|
||||
Local,
|
||||
|
||||
/// <summary>
|
||||
/// "Puppet" mode where avatar is not updated internally and transforms are required to be updated externally instead.
|
||||
/// These are remote avatars controlled by other users in a networking scenario, avatars in replay mode, etc...
|
||||
/// </summary>
|
||||
UpdateExternally
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 95e6931452bff3d4c9de15ecb25f5e77
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,192 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrAvatarMoveEventArgs.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using UltimateXR.Core;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Avatar
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains information about an <see cref="UxrAvatar" /> that has moved/rotated. Avatars are moved/rotated
|
||||
/// through <see cref="UxrManager" /> functionality such as:
|
||||
/// <list type="bullet">
|
||||
/// <item>
|
||||
/// <see
|
||||
/// cref="UxrManager.MoveAvatarTo(UxrAvatar,UnityEngine.Vector3,UnityEngine.Vector3,bool)">
|
||||
/// UxrManager.Instance.MoveAvatarTo
|
||||
/// </see>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <see cref="UxrManager.RotateAvatar">UxrManager.Instance.RotateAvatar</see>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <see
|
||||
/// cref="UxrManager.TeleportLocalAvatar">
|
||||
/// UxrManager.Instance.TeleportLocalAvatar
|
||||
/// </see>
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// These methods will move/rotate the root transform of the avatar. If a user moves or rotates in the real-world, the
|
||||
/// camera transform will be updated but the root avatar transform will remain fixed. Only moving or teleporting the
|
||||
/// avatar will generate <see cref="UxrAvatarMoveEventArgs" /> events.
|
||||
/// </summary>
|
||||
public class UxrAvatarMoveEventArgs : EventArgs
|
||||
{
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets the old <see cref="UxrAvatar" /> position.
|
||||
/// </summary>
|
||||
public Vector3 OldPosition { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the old <see cref="UxrAvatar" /> rotation.
|
||||
/// </summary>
|
||||
public Quaternion OldRotation { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the new <see cref="UxrAvatar" /> position.
|
||||
/// </summary>
|
||||
public Vector3 NewPosition { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the new <see cref="UxrAvatar" /> rotation.
|
||||
/// </summary>
|
||||
public Quaternion NewRotation { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the old <see cref="UxrAvatar" /> forward vector.
|
||||
/// </summary>
|
||||
public Vector3 OldForward { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the new <see cref="UxrAvatar" /> forward vector.
|
||||
/// </summary>
|
||||
public Vector3 NewForward { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the old <see cref="UxrAvatar" /> local to world matrix.
|
||||
/// </summary>
|
||||
public Matrix4x4 OldWorldMatrix { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the new <see cref="UxrAvatar" /> local to world matrix.
|
||||
/// </summary>
|
||||
public Matrix4x4 NewWorldMatrix { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the avatar has changed its position.
|
||||
/// </summary>
|
||||
public bool HasTranslation { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the avatar has changed its rotation.
|
||||
/// </summary>
|
||||
public bool HasRotation { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors & Finalizer
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="oldPosition">Old <see cref="UxrAvatar" /> position</param>
|
||||
/// <param name="oldRotation">Old <see cref="UxrAvatar" /> rotation</param>
|
||||
/// <param name="newPosition">New <see cref="UxrAvatar" /> position</param>
|
||||
/// <param name="newRotation">New <see cref="UxrAvatar" /> rotation</param>
|
||||
public UxrAvatarMoveEventArgs(Vector3 oldPosition, Quaternion oldRotation, Vector3 newPosition, Quaternion newRotation)
|
||||
{
|
||||
OldPosition = oldPosition;
|
||||
OldRotation = oldRotation;
|
||||
NewPosition = newPosition;
|
||||
NewRotation = newRotation;
|
||||
|
||||
ComputeInternalData();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Overrides object
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
if (HasTranslation && HasRotation)
|
||||
{
|
||||
return $"Avatar moved (OldPosition={OldPosition}, OldRotation={OldRotation}, NewPosition={NewPosition}, NewRotation={NewRotation})";
|
||||
}
|
||||
|
||||
if (HasTranslation)
|
||||
{
|
||||
return $"Avatar moved (OldPosition={OldPosition}, NewPosition={NewPosition})";
|
||||
}
|
||||
|
||||
return $"Avatar moved (OldRotation={OldPosition}, NewRotation={NewPosition})";
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Reorients and repositions a transform so that it keeps the relative position/orientation to the avatar after the
|
||||
/// position changed event.
|
||||
/// </summary>
|
||||
/// <param name="transform">Transform to reorient/reposition</param>
|
||||
public void ReorientRelativeToAvatar(Transform transform)
|
||||
{
|
||||
GetKeepRelativeOrientationToAvatar(transform, out Vector3 position, out Quaternion rotation);
|
||||
transform.SetPositionAndRotation(position, rotation);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the new position and rotation an object would need to have to keep the same relative position/rotation to
|
||||
/// the avatar after moving.
|
||||
/// </summary>
|
||||
/// <param name="transform">The transform to get the new position/rotation of</param>
|
||||
/// <param name="position">The new position</param>
|
||||
/// <param name="rotation">The new rotation</param>
|
||||
public void GetKeepRelativeOrientationToAvatar(Transform transform, out Vector3 position, out Quaternion rotation)
|
||||
{
|
||||
Vector3 relativePos = _oldWorldMatrixInverse.MultiplyPoint(transform.position);
|
||||
Quaternion relativeRot = _oldRotationInverse * transform.rotation;
|
||||
|
||||
position = NewWorldMatrix.MultiplyPoint(relativePos);
|
||||
rotation = NewRotation * relativeRot;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// Computes the helper properties and internal variables.
|
||||
/// </summary>
|
||||
private void ComputeInternalData()
|
||||
{
|
||||
OldForward = OldRotation * Vector3.forward;
|
||||
NewForward = NewRotation * Vector3.forward;
|
||||
OldWorldMatrix = Matrix4x4.TRS(OldPosition, OldRotation, Vector3.one);
|
||||
NewWorldMatrix = Matrix4x4.TRS(NewPosition, NewRotation, Vector3.one);
|
||||
|
||||
_oldWorldMatrixInverse = OldWorldMatrix.inverse;
|
||||
_oldRotationInverse = Quaternion.Inverse(OldRotation);
|
||||
|
||||
HasTranslation = OldPosition != NewPosition;
|
||||
HasRotation = OldRotation != NewRotation;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
private Matrix4x4 _oldWorldMatrixInverse;
|
||||
private Quaternion _oldRotationInverse;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 82402aa810bb0b0439d0414dac492b92
|
||||
timeCreated: 1500467196
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,50 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrAvatarRenderModes.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
|
||||
namespace UltimateXR.Avatar
|
||||
{
|
||||
/// <summary>
|
||||
/// Flags describing the different avatar render elements that can be enabled/disabled separately.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum UxrAvatarRenderModes
|
||||
{
|
||||
/// <summary>
|
||||
/// Avatar isn't rendered. All components will still work, which means the avatar can still interact with the
|
||||
/// environment. It can be used in mixed reality for example to let the hand colliders interact with
|
||||
/// the scenario even though the hands aren't rendered.
|
||||
/// </summary>
|
||||
None,
|
||||
|
||||
/// <summary>
|
||||
/// Left input controller 3D model is rendered. In single controller setups, both left and right will target the same
|
||||
/// controller.
|
||||
/// </summary>
|
||||
LeftController = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Right input controller 3D model is rendered. In single controller setups, both left and right will target the same
|
||||
/// controller.
|
||||
/// </summary>
|
||||
RightController = 1 << 1,
|
||||
|
||||
/// <summary>
|
||||
/// Avatar is rendered.
|
||||
/// </summary>
|
||||
Avatar = 1 << 2,
|
||||
|
||||
/// <summary>
|
||||
/// All input controllers are rendered.
|
||||
/// </summary>
|
||||
AllControllers = LeftController | RightController,
|
||||
|
||||
/// <summary>
|
||||
/// All input controllers and the avatar are rendered.
|
||||
/// </summary>
|
||||
AllControllersAndAvatar = Avatar | AllControllers
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 05a15335888e1294b8fb8fa95e4fded7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,25 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrAvatarStartedEventArgs.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
namespace UltimateXR.Avatar
|
||||
{
|
||||
/// <summary>
|
||||
/// Arguments for the avatar started event.
|
||||
/// </summary>
|
||||
public class UxrAvatarStartedEventArgs : UxrAvatarEventArgs
|
||||
{
|
||||
#region Constructors & Finalizer
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="avatar">Avatar</param>
|
||||
public UxrAvatarStartedEventArgs(UxrAvatar avatar) : base(avatar)
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bf8b4bfd91544103bf92ee64a7a1a937
|
||||
timeCreated: 1648644790
|
||||
@@ -0,0 +1,38 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrAvatarUpdateEventArgs.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UltimateXR.Core;
|
||||
|
||||
namespace UltimateXR.Avatar
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains information about an avatar update event.
|
||||
/// </summary>
|
||||
public class UxrAvatarUpdateEventArgs : UxrAvatarEventArgs
|
||||
{
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets the update stage the update event belongs to.
|
||||
/// </summary>
|
||||
public UxrUpdateStage UpdateStage { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors & Finalizer
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="avatar">Avatar the event describes</param>
|
||||
/// <param name="updateStage">Update stage the event belongs to</param>
|
||||
public UxrAvatarUpdateEventArgs(UxrAvatar avatar, UxrUpdateStage updateStage) : base(avatar)
|
||||
{
|
||||
UpdateStage = updateStage;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4858ff3efca89a1429b9ec152c538511
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,23 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrHandIntegration.GizmoHandSize.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
namespace UltimateXR.Avatar
|
||||
{
|
||||
public partial class UxrHandIntegration
|
||||
{
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Enumerates the supported gizmo hand sizes.
|
||||
/// </summary>
|
||||
public enum GizmoHandSize
|
||||
{
|
||||
Big,
|
||||
Small
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 61b692ce114b4714bede1a8706220099
|
||||
timeCreated: 1652364469
|
||||
132
Assets/UltimateXR/Runtime/Scripts/Avatar/UxrHandIntegration.cs
Normal file
132
Assets/UltimateXR/Runtime/Scripts/Avatar/UxrHandIntegration.cs
Normal file
@@ -0,0 +1,132 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrHandIntegration.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UltimateXR.Avatar.Rig;
|
||||
using UltimateXR.Core;
|
||||
using UltimateXR.Core.Components.Composite;
|
||||
using UltimateXR.Manipulation;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Avatar
|
||||
{
|
||||
/// <summary>
|
||||
/// Component that allows to select different graphical variations for the hands that are shown grabbing the
|
||||
/// controllers using IK.
|
||||
/// </summary>
|
||||
[DisallowMultipleComponent]
|
||||
public partial class UxrHandIntegration : UxrAvatarComponent<UxrHandIntegration>
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[SerializeField] private GizmoHandSize _gizmoHandSize;
|
||||
[SerializeField] private UxrHandSide _handSide;
|
||||
[SerializeField] [HideInInspector] private int _selectedRenderPipeline;
|
||||
[SerializeField] private string _objectVariationName;
|
||||
[SerializeField] private string _materialVariationName;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Types & Data
|
||||
|
||||
public UxrHandSide HandSide => _handSide;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Tries to align the hand integration transform to fit the current avatar's hand as best as possible.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// True if the transform was changed, false otherwise. False means that the avatar hand was missing the required
|
||||
/// bone references, either fingers or the wrist
|
||||
/// </returns>
|
||||
public bool TryToMatchHand()
|
||||
{
|
||||
// Get component and look for requirements:
|
||||
|
||||
UxrAvatar avatar = GetComponentInParent<UxrAvatar>();
|
||||
|
||||
if (avatar == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
UxrAvatarHand hand = avatar.GetHand(_handSide);
|
||||
|
||||
if (hand == null || !hand.HasFingerData() || hand.Wrist == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set position
|
||||
|
||||
transform.position = hand.Wrist.position;
|
||||
|
||||
// Set orientation knowing that UltimateXR hand integrations use:
|
||||
// left hand: X = to forearm, Y = backhand, Z = right.
|
||||
// right hand: X = to forearm, Y = palm, Z = left.
|
||||
|
||||
hand.GetPalmCenter(out Vector3 palmCenter);
|
||||
hand.GetPalmToFingerDirection(out Vector3 palmToFinger);
|
||||
hand.GetPalmOutDirection(_handSide, out Vector3 palmOut);
|
||||
|
||||
if (_handSide == UxrHandSide.Left)
|
||||
{
|
||||
transform.rotation = Quaternion.LookRotation(Vector3.Cross(-palmToFinger, -palmOut), -palmOut);
|
||||
}
|
||||
else if (_handSide == UxrHandSide.Right)
|
||||
{
|
||||
transform.rotation = Quaternion.LookRotation(Vector3.Cross(-palmToFinger, palmOut), palmOut);
|
||||
}
|
||||
|
||||
// Keeping the new orientation, try to offset so that the grabber stays in the center of the palm.
|
||||
|
||||
UxrGrabber grabber = GetComponentInChildren<UxrGrabber>();
|
||||
|
||||
if (grabber)
|
||||
{
|
||||
Vector3 offset = grabber.transform.position - (palmCenter + palmOut * 0.02f);
|
||||
|
||||
// Remove left/right/up/down offset
|
||||
|
||||
offset = transform.InverseTransformDirection(offset);
|
||||
offset.z = 0.0f;
|
||||
offset = transform.TransformDirection(offset);
|
||||
|
||||
// Apply offset
|
||||
|
||||
transform.position -= offset;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unity
|
||||
|
||||
/// <summary>
|
||||
/// Parents the hand integration to the avatar's hand at the beginning so that all components in the hand integration
|
||||
/// work correctly.
|
||||
/// </summary>
|
||||
protected override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
|
||||
if (Avatar)
|
||||
{
|
||||
Transform handBone = Avatar.GetHandBone(_handSide);
|
||||
|
||||
if (handBone)
|
||||
{
|
||||
transform.SetParent(handBone);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 56fd8cf6e42e4b745b83f45c486ff4d2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user