first commit

This commit is contained in:
2026-04-07 03:14:32 +03:00
commit b3992fec6b
1026 changed files with 366769 additions and 0 deletions

View File

@@ -0,0 +1,218 @@
// Copyright (C) 2014-2024 Gleechi Technology AB. All rights reserved.
using UnityEngine;
using System;
using System.Collections.Generic;
using UnityEngine.Events;
namespace VirtualGrasp.Scripts
{
using static VG_FingerAnimator.VG_FingerAnimationData;
/**
* VG_FingerAnimator animates the fingers on a selected hand side using a list of animation clips.
* Useful for making manual corrections on primary grasps,
* or animating fingers during inhand manipulation of articulated objects.
*/
[LIBVIRTUALGRASP_UNITY_SCRIPT]
[HelpURL("https://docs.virtualgrasp.com/unity_component_vgfingeranimator." + VG_Version.__VG_VERSION__ + ".html")]
public partial class VG_FingerAnimator : MonoBehaviour
{
[SerializeField, Tooltip("Hand side to animate")]
private HandSide m_hand = HandSide.LEFT;
[SerializeField, Tooltip("Animation clips for directing finger animation")]
private List<VG_FingerAnimationData> m_fingerAnimations = new List<VG_FingerAnimationData>();
[SerializeField, Tooltip("Optional, only enable animation when this object is grasped. If unassigned this transform will be used.")]
private Transform m_interactableObject = null;
[SerializeField]
private AnimationEvents m_events = new AnimationEvents();
private Transform m_holdingHand = null;
private float m_animationDrive = 1.0f;
private Dictionary<Transform, Vector3> m_startRotation= new Dictionary<Transform, Vector3>();
private Dictionary<Transform, Vector3> m_targetRotation = new Dictionary<Transform, Vector3>();
/// <summary>
/// Pass in a float in range [0,1] to drive the animation
/// </summary>
public void DriveAnimation(float animationDrive)
{
this.m_animationDrive = Mathf.Clamp01(animationDrive);
}
/// <summary>
/// Stops animation drive, bones will be rotated according to values inside the animation clips
/// </summary>
public void StopAnimationDrive()
{
this.m_animationDrive = 1f;
}
void Awake()
{
if (m_interactableObject == null)
m_interactableObject = this.transform;
VG_Controller.OnObjectGrasped.AddListener(OnObjectGrasped);
VG_Controller.OnObjectReleased.AddListener(OnObjectReleased);
enabled = false;
}
private void OnObjectGrasped(VG_HandStatus status)
{
if (status.m_selectedObject != m_interactableObject) return;
m_holdingHand = status.m_hand;
this.enabled = true;
}
private void OnObjectReleased(VG_HandStatus status)
{
if (status.m_selectedObject != m_interactableObject) return;
if (m_holdingHand == status.m_hand)
{
m_holdingHand = null;
VG_Controller.GetGraspingAvatars(m_interactableObject, out var hands);
foreach (var hand in hands)
{
m_holdingHand = VG_Controller.GetHand(hand.Key, hand.Value).m_hand;
}
this.enabled = m_holdingHand != null;
}
}
void OnEnable()
{
m_events.OnAnimationStarted?.Invoke();
}
void OnDisable()
{
m_events.OnAnimationStopped?.Invoke();
}
private void LateUpdate()
{
for (int i = 0; i < m_fingerAnimations.Count; i++)
{
VG_FingerAnimationData animation = m_fingerAnimations[i];
if (animation.disabled) continue;
for (int fingerIndex = 0; fingerIndex < 5; fingerIndex++)
{
Finger fingerEnum = FingerEnumFromIndex(fingerIndex);
if (animation.finger.HasFlag(fingerEnum))
{
for (int boneIndex = 0; boneIndex < 3; boneIndex++)
{
Bone boneEnum = BoneEnumFromIndex(boneIndex);
if (animation.bone.HasFlag(boneEnum))
{
int avatarID = VG_Controller.GetHand(m_holdingHand).m_avatarID;
AnimateFingerBone(avatarID, fingerIndex, boneIndex, animation);
}
}
}
}
}
}
private void AnimateFingerBone(int avatarID, int fingerIndex, int boneIndex, VG_FingerAnimationData animation)
{
if (VG_Controller.GetFingerBone(avatarID, (VG_HandSide)this.m_hand, fingerIndex, boneIndex, out Transform bone) != VG_ReturnCode.SUCCESS)
return;
Vector3 startRotationEuler = bone.localRotation.eulerAngles;
Vector3 targetRotationEuler = startRotationEuler + animation.rotation;
if (m_startRotation.ContainsKey(bone))
startRotationEuler = m_startRotation[bone];
else
m_startRotation.Add(bone, startRotationEuler);
if (m_targetRotation.ContainsKey(bone))
targetRotationEuler = m_targetRotation[bone];
else
m_targetRotation.Add(bone, targetRotationEuler);
Quaternion startRotation = Quaternion.Euler(startRotationEuler);
Quaternion targetRotation = Quaternion.Euler(targetRotationEuler);
float targetAnimationDrive = this.m_animationDrive;
if (animation.ignoreDrive)
{
targetAnimationDrive = 1f;
}
else if (animation.invertDrive)
{
targetAnimationDrive = 1f - targetAnimationDrive;
}
bone.localRotation = Quaternion.Slerp(startRotation, targetRotation, targetAnimationDrive);
}
}
public partial class VG_FingerAnimator
{
public enum HandSide
{
LEFT = VG_HandSide.LEFT,
RIGHT = VG_HandSide.RIGHT
}
[Serializable]
public class VG_FingerAnimationData
{
[Tooltip("Disables this animation clip")]
public bool disabled = true;
[Tooltip("Makes this animation clip always reach target rotation, effectively ignoring any animation drive")]
public bool ignoreDrive = false;
[Tooltip("Makes this animation clip reach target rotation when the animation drive value is 0 rather than 1")]
public bool invertDrive = false;
[System.Flags]
public enum Finger
{
Thumb = 1, Index = 2, Middle = 4, Ring = 8, Pinky = 16
};
[Tooltip("Fingers to animate in this clip, multiple can be selected")]
public Finger finger = Finger.Thumb;
[System.Flags]
public enum Bone
{
Proximal = 1, Middle = 2, Distal = 4
};
[Tooltip("Bones to animate in this clip, multiple can be selected")]
public Bone bone = Bone.Proximal;
[Tooltip("Target rotation relative to the initial rotation for selected bones")]
public Vector3 rotation;
public static Bone BoneEnumFromIndex(int boneIndex)
{
return boneIndex switch
{
0 => Bone.Proximal,
1 => Bone.Middle,
2 => Bone.Distal,
_ => throw new ArgumentException($"Couldn't map index {boneIndex} to bone enum")
};
}
public static Finger FingerEnumFromIndex(int fingerIndex)
{
return fingerIndex switch
{
0 => Finger.Thumb,
1 => Finger.Index,
2 => Finger.Middle,
3 => Finger.Ring,
4 => Finger.Pinky,
_ => throw new ArgumentException($"Couldn't map index {fingerIndex} to finger enum")
};
}
}
[Serializable]
public class AnimationEvents
{
[SerializeField]
public UnityEvent OnAnimationStarted, OnAnimationStopped;
}
}
}