using System; using System.Collections.Generic; using System.Linq; using UnityEngine; namespace UnityEngine.XR.Hands { /// /// Utility methods for . /// public static class XRHandSkeletonDriverUtility { /// /// Set up the joint references for an by checking the /// hierarchy and comparing the children GameObject's names with the names of the joints. /// The GameObjects names are expected to either start or end with the name of the joint. /// /// /// This method is expensive because it searches the hierarchy by comparing the names of GameObjects so /// it should not be called frequently. Instead, the joint references can be set once and serialized /// in either the scene or a prefab. /// /// The component to setup. /// A list of strings that will be cleared and populated with the joints that are missing. public static void FindJointsFromRoot(XRHandSkeletonDriver skeletonDriver, List missingJointNames = null) { void SetupJointTransformReference( XRHandJointID jointId, Transform jointTransform) { var reference = new JointToTransformReference { jointTransform = jointTransform, xrHandJointID = jointId }; skeletonDriver.jointTransformReferences.Add(reference); } if (missingJointNames != null) missingJointNames.Clear(); skeletonDriver.jointTransformReferences.Clear(); Transform wristRootTransform = null; // First check if the parent itself is the wrist var rootTransformName = skeletonDriver.rootTransform.name; if (StartsOrEndsWith(rootTransformName, XRHandJointID.Wrist.ToString())) { wristRootTransform = skeletonDriver.rootTransform; } else // Otherwise check under parent for wrist joint as a child { for (var childIndex = 0; childIndex < skeletonDriver.rootTransform.childCount; ++childIndex) { var child = skeletonDriver.rootTransform.GetChild(childIndex); if (child.gameObject.name.EndsWith(XRHandJointID.Wrist.ToString())) wristRootTransform = child; } } if (wristRootTransform == null) { if (missingJointNames != null) missingJointNames.Add(XRHandJointID.Wrist.ToString()); } else { SetupJointTransformReference(XRHandJointID.Wrist, wristRootTransform); Transform palmTransform = null; // Find all the joints under the wrist for (var childIndex = 0; childIndex < wristRootTransform.childCount; ++childIndex) { var child = wristRootTransform.GetChild(childIndex); // Find the palm joint if (child.name.EndsWith(XRHandJointID.Palm.ToString())) { palmTransform = child; continue; } // Find the finger joints for (var fingerIndex = (int)XRHandFingerID.Thumb; fingerIndex <= (int)XRHandFingerID.Little; ++fingerIndex) { var fingerId = (XRHandFingerID)fingerIndex; var jointIdFront = fingerId.GetFrontJointID(); if (!StartsOrEndsWith(child.name, jointIdFront.ToString())) continue; SetupJointTransformReference(jointIdFront, child); var lastChild = child; var jointIndexBack = fingerId.GetBackJointID().ToIndex(); // Find the rest of the joints for the finger for (var jointIndex = jointIdFront.ToIndex() + 1; jointIndex <= jointIndexBack; ++jointIndex) { // Find the next child that ends with the joint name var jointName = XRHandJointIDUtility.FromIndex(jointIndex).ToString(); for (var nextChildIndex = 0; nextChildIndex < lastChild.childCount; ++nextChildIndex) { var nextChild = lastChild.GetChild(nextChildIndex); if (StartsOrEndsWith(nextChild.name, jointName)) { lastChild = nextChild; break; } } if (StartsOrEndsWith(lastChild.name, jointName)) { var jointId = XRHandJointIDUtility.FromIndex(jointIndex); SetupJointTransformReference(jointId, lastChild); } else if (missingJointNames != null) missingJointNames.Add(jointName); } } } for (var fingerIndex = (int)XRHandFingerID.Thumb; fingerIndex <= (int)XRHandFingerID.Little; ++fingerIndex) { var fingerId = (XRHandFingerID)fingerIndex; var jointIdFront = fingerId.GetFrontJointID(); // Check if front joint id is present in the list of joint references if (skeletonDriver.jointTransformReferences.Any(jointReference => jointReference.xrHandJointID == jointIdFront)) continue; if (missingJointNames != null) missingJointNames.Add(jointIdFront.ToString()); } if (palmTransform != null) { SetupJointTransformReference(XRHandJointID.Palm, palmTransform); } else if (missingJointNames != null) { missingJointNames.Add(XRHandJointID.Palm.ToString()); } } } static bool StartsOrEndsWith(string value, string searchTerm) { return value.StartsWith(searchTerm, StringComparison.InvariantCultureIgnoreCase) || value.EndsWith(searchTerm, StringComparison.InvariantCultureIgnoreCase); } } }