Code/HumanoidRetargeter/Cleanup/FkUtil.cs

Utility class with allocation-free forward-kinematics helpers used by cleanup passes. It computes full-skeleton world transforms into a caller-owned buffer (ToWorld) and computes a single bone world transform by walking the ancestor chain recursively (BoneWorld). Results match the full FK implementation.

Native Interop
using HumanoidRetargeter.Maths;
using SkeletonModel = HumanoidRetargeter.Skeleton.Skeleton;

namespace HumanoidRetargeter.Cleanup;

/// <summary>
/// Allocation-free forward-kinematics helpers shared by the cleanup passes.
/// Both helpers perform exactly the same sequence of <see cref="XForm.Compose"/>
/// operations as <see cref="HumanoidRetargeter.Skeleton.Pose.ToWorld"/>, so their
/// results are bit-identical to a full FK pass — they only avoid the per-call
/// allocations (scratch buffer reuse, single-bone ancestor walks).
/// </summary>
internal static class FkUtil
{
    /// <summary>
    /// Full-skeleton FK from <paramref name="locals"/> into the caller-owned
    /// <paramref name="world"/> scratch buffer (one world transform per bone,
    /// skeleton bone order). The buffer must be at least <c>locals.Length</c> long.
    /// </summary>
    public static void ToWorld(XForm[] locals, SkeletonModel skeleton, XForm[] world)
    {
        for (int i = 0; i < locals.Length; i++)
        {
            var parent = skeleton[i].ParentIndex;
            world[i] = parent < 0 ? locals[i] : XForm.Compose(world[parent], locals[i]);
        }
    }

    /// <summary>
    /// World transform of a single bone via an ancestor-chain walk: composes the same
    /// nested chain a full FK pass would, restricted to the bone's ancestors, so the
    /// result is bit-identical to <c>ToWorld(...)[bone]</c> without touching any other bone.
    /// </summary>
    public static XForm BoneWorld(XForm[] locals, SkeletonModel skeleton, int bone)
    {
        var parent = skeleton[bone].ParentIndex;
        return parent < 0
            ? locals[bone]
            : XForm.Compose(BoneWorld(locals, skeleton, parent), locals[bone]);
    }
}