using System.Collections.Generic; using UnityEngine; using UnityEngine.XR; namespace Passer.Tracking { /// /// HandSkeleton component for hand tracking with UnityXR /// /// *Important note:* although the Unity XR SDK implements this interface /// no known device is supporting it at the moment. The added value of this /// component is therefore limited. /// Also: because of this limitation this component is untested! /// See: https://forum.unity.com/threads/oculus-quest-hand-tracking-data.853579/ public class UnityXRHandSkeleton : HandSkeleton { #if pUNITYXR public TrackerComponent tracker; protected InputDevice device; protected XRNode xrNode; #region Init protected override void Start() { base.Start(); if (tracker == null) tracker = GetComponentInParent(); xrNode = isLeft ? XRNode.LeftHand : XRNode.RightHand; device = InputDevices.GetDeviceAtXRNode(xrNode); InputDevices.deviceConnected += OnDeviceConnected; InputDevices.deviceDisconnected += OnDeviceDisconnected; } /// /// Hand is detected? /// /// The InputDevice of the hand protected virtual void OnDeviceConnected(InputDevice device) { bool isLeft = (device.characteristics & InputDeviceCharacteristics.Left) != 0; bool isTrackedHand = (device.characteristics & InputDeviceCharacteristics.HandTracking) != 0; if (isTrackedHand && isLeft == this.isLeft) { this.device = device; //Show(true); Debug.Log("Left Hand Connected"); } } /// /// Hand is lost /// /// This also happens when the hand is no longer tracked. /// The InputDevice of the hand protected virtual void OnDeviceDisconnected(InputDevice device) { bool isLeft = (device.characteristics & InputDeviceCharacteristics.Left) != 0; bool isTrackedHand = (device.characteristics & InputDeviceCharacteristics.HandTracking) != 0; if (isTrackedHand && isLeft == this.isLeft) { this.device = device; //Show(false); Debug.Log("Right Hand Connected"); } } protected override void InitializeSkeleton() { if (device.TryGetFeatureValue(CommonUsages.handData, out Hand handData)) { bones = new List(); TrackedBone handBone = TrackedBone.Create("Hand", null); bones.Add(handBone); List thumbBones = new List(); if (handData.TryGetFingerBones(HandFinger.Thumb, thumbBones)) { Transform parentBone = handBone.transform; for (int i = 0; i < thumbBones.Count; i++) { TrackedBone bone = TrackedBone.Create("Thumb" + i, parentBone); bones.Add(bone); parentBone = bone.transform; } } List indexBones = new List(); if (handData.TryGetFingerBones(HandFinger.Index, indexBones)) { Transform parentBone = handBone.transform; for (int i = 0; i < indexBones.Count; i++) { TrackedBone bone = TrackedBone.Create("Index" + i, parentBone); bones.Add(bone); parentBone = bone.transform; } } List middleBones = new List(); if (handData.TryGetFingerBones(HandFinger.Middle, middleBones)) { Transform parentBone = handBone.transform; for (int i = 0; i < middleBones.Count; i++) { TrackedBone bone = TrackedBone.Create("Middle" + i, parentBone); bones.Add(bone); parentBone = bone.transform; } } List ringBones = new List(); if (handData.TryGetFingerBones(HandFinger.Ring, ringBones)) { Transform parentBone = handBone.transform; for (int i = 0; i < ringBones.Count; i++) { TrackedBone bone = TrackedBone.Create("Ring" + i, parentBone); bones.Add(bone); parentBone = bone.transform; } } List pinkyBones = new List(); if (handData.TryGetFingerBones(HandFinger.Pinky, pinkyBones)) { Transform parentBone = handBone.transform; for (int i = 0; i < pinkyBones.Count; i++) { TrackedBone bone = TrackedBone.Create("Little" + i, parentBone); bones.Add(bone); parentBone = bone.transform; } } } } #endregion Init #region Update public override void UpdateComponent() { base.UpdateComponent(); if (bones == null) InitializeSkeleton(); if (bones == null) { status = Tracker.Status.Unavailable; DisableRenderer(); return; } status = Tracker.Status.Unavailable; positionConfidence = 0; rotationConfidence = 0; if (device == null) return; status = Tracker.Status.Present; if (device.TryGetFeatureValue(CommonUsages.handData, out Hand handData)) { Debug.Log("received hand data"); if (handData.TryGetRootBone(out Bone handBone)) { if (handBone.TryGetPosition(out Vector3 position)) { transform.position = tracker.transform.TransformPoint(position); positionConfidence = 1; status = Tracker.Status.Tracking; } if (handBone.TryGetRotation(out Quaternion rotation)) { transform.rotation = tracker.transform.rotation * rotation; rotationConfidence = 1; status = Tracker.Status.Tracking; } } int boneIx = 0; UpdateFinger(ref boneIx, handData, HandFinger.Thumb); UpdateFinger(ref boneIx, handData, HandFinger.Index); UpdateFinger(ref boneIx, handData, HandFinger.Middle); UpdateFinger(ref boneIx, handData, HandFinger.Ring); UpdateFinger(ref boneIx, handData, HandFinger.Pinky); } } protected void UpdateFinger(ref int boneIx, Hand handData, HandFinger fingerId) { List fingerBones = new List(); if (handData.TryGetFingerBones(fingerId, fingerBones)) { for (int i = 0; i < fingerBones.Count; i++) { TrackedBone trackedBone = bones[boneIx]; // We only support forward kinematics on the fingers, // so we only use rotations if (fingerBones[i].TryGetRotation(out Quaternion rotation)) { trackedBone.transform.rotation = tracker.transform.rotation * rotation; } } } } #endregion Update #endif } }