// 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;
}
}
}