// Copyright (c) Meta Platforms, Inc. and affiliates. All rights reserved. using System; using System.Collections.Generic; using Unity.Collections; using static Unity.Collections.NativeArrayOptions; namespace Meta.XR.Movement { /// /// Utility helper functions for the native plugin. /// public abstract partial class MSDKUtility { /********************************************************** * * Extension Functions * **********************************************************/ /// /// Get the skeleton joint count for a handle. /// /// The handle to get the skeleton info from. /// The type of skeleton to get info from. /// The number of joints. /// True if the function was successfully executed. public static bool GetSkeletonJointCount(ulong handle, SkeletonType skeletonType, out int jointCount) { Result success; using (new ProfilerScope(nameof(GetConfigName))) { success = Api.metaMovementSDK_getSkeletonInfo(handle, skeletonType, out var skeletonInfo); jointCount = skeletonInfo.JointCount; } return success == Result.Success; } /// /// Get the parent joint indexes for a joint as a temp native array. /// /// The handle to get the info from. /// The type of skeleton to get the info from. /// The array of parent joint indexes. /// True if the function was successfully executed. public static bool GetParentJointIndexes(ulong handle, SkeletonType skeletonType, out NativeArray jointIndexArray) { Result success; using (new ProfilerScope(nameof(GetParentJointIndexes))) { unsafe { GetSkeletonJointCount(handle, skeletonType, out var jointCount); jointIndexArray = new NativeArray(jointCount, Allocator.Temp, UninitializedMemory); success = Api.metaMovementSDK_getParentJointIndexes(handle, skeletonType, jointIndexArray.GetPtr(), out jointCount); } } return success == Result.Success; } /// /// Get the child joint indexes for a joint as a temp native array. /// /// The handle to get the info from. /// The type of skeleton to get the info from. /// The index of the joint to get the child indexes for. /// The array of child joint indexes. /// True if the function was successfully executed. public static bool GetChildJointIndexes(ulong handle, SkeletonType skeletonType, int jointIndex, out NativeArray childJointIndexes) { bool success; using (new ProfilerScope(nameof(GetChildJointIndexes))) { success = GetParentJointIndexes(handle, skeletonType, out var parentJointIndexes); var childCount = 0; // count how many joints consider this node to be a parent. foreach (var i in parentJointIndexes) { if (i == jointIndex) { childCount++; } } childJointIndexes = new NativeArray(childCount, Allocator.Temp, UninitializedMemory); var currentChildIndex = 0; for (int currentJointIndex = 0; currentJointIndex < parentJointIndexes.Length; currentJointIndex++) { if (parentJointIndexes[currentJointIndex] == jointIndex) { childJointIndexes[currentChildIndex] = currentJointIndex; currentChildIndex++; } } } return success; } /// /// Get the lowest child joint index. /// /// The handle to get the info from. /// The type of skeleton to get the info from. /// The joint index to start searching the child indexes from. /// The lowest child index. /// True if the function was successfully executed. public static bool GetLowestChildJointIndex(ulong handle, SkeletonType skeletonType, int jointIndex, out int lowestChildIndex) { var stack = new Stack(); lowestChildIndex = -1; stack.Push(jointIndex); while (stack.Count > 0) { var currentJointIndex = stack.Pop(); GetChildJointIndexes(handle, skeletonType, currentJointIndex, out var childIndices); if (childIndices.Length == 0) { if (lowestChildIndex == -1 || currentJointIndex > lowestChildIndex) { lowestChildIndex = currentJointIndex; } } else { foreach (var childIndex in childIndices) { stack.Push(childIndex); } } } return lowestChildIndex != -1; } /// /// Get the names for the parent joint for each joint. /// /// The handle to get the info from. /// The type of skeleton to get the info from. /// The array of parent joint names. /// True if the function was successfully executed. public static bool GetParentJointNames(ulong handle, SkeletonType skeletonType, out string[] parentJointNames) { parentJointNames = Array.Empty(); using (new ProfilerScope(nameof(GetParentJointNames))) { if (!GetJointNames(handle, skeletonType, out var jointNames)) { return false; } parentJointNames = new string[jointNames.Length]; for (var i = 0; i < parentJointNames.Length; i++) { Api.metaMovementSDK_getParentJointIndex(handle, skeletonType, i, out var parentIndex); if (parentIndex == -1) { parentJointNames[i] = string.Empty; continue; } parentJointNames[i] = jointNames[parentIndex]; } } return true; } /// /// Get the specific skeleton t-pose for a skeleton type as a temp native array. /// /// The handle to get the info from. /// The type of skeleton to get the info from. /// The t-pose type. /// /// The skeleton t-pose. /// True if the function was successfully executed. public static bool GetSkeletonTPose(ulong handle, SkeletonType skeletonType, SkeletonTPoseType tposeType, JointRelativeSpaceType jointSpaceType, out NativeArray transformArray) { Result success; using (new ProfilerScope(nameof(GetSkeletonTPoseByRef))) { unsafe { GetSkeletonJointCount(handle, skeletonType, out var jointCount); transformArray = new NativeArray(jointCount, Allocator.Temp, UninitializedMemory); success = Api.metaMovementSDK_getSkeletonTPose(handle, skeletonType, tposeType, jointSpaceType, transformArray.GetPtr(), out jointCount); } } return success == Result.Success; } /// /// Get the names of all known joints for a skeleton type. /// /// The handle to get the info from. /// The type of skeleton to get the info from. /// The array of joint names. /// True if the function was successfully executed. public static bool GetKnownJointNames(ulong handle, SkeletonType skeletonType, out string[] knownJointNames) { knownJointNames = Array.Empty(); using (new ProfilerScope(nameof(GetKnownJointNames))) { if (!GetJointNames(handle, skeletonType, out var jointNames)) { return false; } knownJointNames = new string[(int)KnownJointType.KnownJointCount]; for (var i = KnownJointType.Root; i < KnownJointType.KnownJointCount; i++) { Api.metaMovementSDK_getJointIndexByKnownJointType(handle, skeletonType, i, out var knownJointIndex); if (knownJointIndex == -1) { knownJointNames[(int)i] = string.Empty; continue; } knownJointNames[(int)i] = jointNames[knownJointIndex]; } } return true; } /// /// Get the name of the joint corresponding to a known joint. /// /// The handle to get the joints from. /// The type of skeleton to get the info from. /// The known joint type. /// The name of the known joint. /// True if the function was successfully executed. public static bool GetKnownJointName(ulong handle, SkeletonType skeletonType, KnownJointType knownJointType, out string jointName) { Result success; jointName = string.Empty; using (new ProfilerScope(nameof(GetJointName))) { success = Api.metaMovementSDK_getJointIndexByKnownJointType(handle, skeletonType, knownJointType, out var knownJointIndex); if (success == Result.Success) { if (!GetJointName(handle, skeletonType, knownJointIndex, out jointName)) { return false; } } } return success == Result.Success; } /// /// Get skeleton mapping entries as a temp native array. /// /// The handle to get the joints from. /// The t-pose type. /// The mapping entries array. /// True if the function was successfully executed. public static bool GetSkeletonMappingEntries(ulong handle, SkeletonTPoseType tPoseType, out NativeArray mappingEntriesArray) { Result success; using (new ProfilerScope(nameof(GetSkeletonMappingEntries))) { unsafe { int numMappings = 0; success = Api.metaMovementSDK_getSkeletonMappingEntries(handle, tPoseType, null, out numMappings); if (success == Result.Success && numMappings > 0) { mappingEntriesArray = new NativeArray(numMappings, Allocator.Temp, UninitializedMemory); success = Api.metaMovementSDK_getSkeletonMappingEntries(handle, tPoseType, mappingEntriesArray.GetPtr(), out numMappings); } else { mappingEntriesArray = new NativeArray(0, Allocator.Temp, UninitializedMemory); } } } return success == Result.Success; } /// /// Converts a skeleton from one joint space format to another. /// /// The handle to get the data from. /// The type of skeleton to get the hierarchy info from. /// The joint space format for the skeletonPose /// The joint space format to convert the skeletonPose to /// The input pose of the skeleton represented in a Native Array /// The output pose of the skeleton represented in a Native Array /// True if the function was successfully executed. public static bool ConvertJointPose( ulong handle, SkeletonType skeletonType, JointRelativeSpaceType inJointSpaceType, JointRelativeSpaceType outJointSpaceType, NativeArray inSkeletonPose, out NativeArray outSkeletonPose) { Result success; using (new ProfilerScope(nameof(ConvertJointPose))) { unsafe { outSkeletonPose = new NativeArray(inSkeletonPose.Length, Allocator.Temp); outSkeletonPose.CopyFrom(inSkeletonPose); success = Api.metaMovementSDK_convertJointPose( handle, skeletonType, inJointSpaceType, outJointSpaceType, outSkeletonPose.GetPtr(), outSkeletonPose.Length); } } return success == Result.Success; } } }