dev/HumanoidRetargeter.Tests/Formats/GltfTargetBindTests.cs
using System.Numerics;
using System.Text;
using HumanoidRetargeter.Formats.Gltf;
using Xunit;

namespace HumanoidRetargeter.Tests.Formats;

public class GltfTargetBindTests
{
    [Theory]
    [InlineData(false, 1f, 2f)]
    [InlineData(true, 1f, 0f)]
    [InlineData(true, .5f, 2f)]
    public void TargetUsesAuthoredBindWithoutChangingAnimation(bool useBind, float inverseScale, float expectedX)
    {
        var values = new float[] { 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,-1,0,1,
            0,1, 2,1,0, 3,1,0 };
        values[0] = values[5] = values[10] = inverseScale;
        var buffer = new byte[values.Length * 4];
        Buffer.BlockCopy(values, 0, buffer, 0, buffer.Length);
        var json = $$$"""
        {
          "asset":{"version":"2.0"},
          "buffers":[{"byteLength":96,"uri":"data:application/octet-stream;base64,{{{Convert.ToBase64String(buffer)}}}"}],
          "bufferViews":[{"buffer":0,"byteOffset":0,"byteLength":64},
            {"buffer":0,"byteOffset":64,"byteLength":8},
            {"buffer":0,"byteOffset":72,"byteLength":24}],
          "accessors":[{"bufferView":0,"componentType":5126,"count":1,"type":"MAT4"},
            {"bufferView":1,"componentType":5126,"count":2,"type":"SCALAR"},
            {"bufferView":2,"componentType":5126,"count":2,"type":"VEC3"}],
          "nodes":[{"name":"root","children":[1]},
            {"name":"joint","translation":[2,1,0],"rotation":[0,0,0.70710678,0.70710678],"children":[2]},
            {"name":"helper","translation":[0,1,0]}],
          "skins":[{"joints":[1],"inverseBindMatrices":0}],
          "animations":[{"samplers":[{"input":1,"output":2}],
            "channels":[{"sampler":0,"target":{"node":1,"path":"translation"}},
              {"sampler":0,"target":{"node":2,"path":"translation"}}]}]
        }
        """;
        var scene = GltfImporter.Import(Encoding.UTF8.GetBytes(json),
            new GltfImportOptions { UseSkinBindPose = useBind });
        var joint = scene.Skeleton.IndexOf("joint");
        var helper = scene.Skeleton.IndexOf("helper");
        Assert.Equal(new Vector3(expectedX * 100, 100, 0), scene.Skeleton.RestWorld[joint].Pos);
        var corrected = useBind && inverseScale == 1f;
        var expectedHelper = corrected ? new Vector3(0, 200, 0) : new Vector3(100, 100, 0);
        Assert.True(Vector3.Distance(expectedHelper, scene.Skeleton.RestWorld[helper].Pos) < .001f);
        var posedRotation = Quaternion.CreateFromAxisAngle(Vector3.UnitZ, MathF.PI / 2);
        var expectedRotation = corrected ? Quaternion.Identity : posedRotation;
        Assert.True(MathF.Abs(Quaternion.Dot(expectedRotation, scene.Skeleton.RestWorld[joint].Rot)) > .99999f);
        Assert.True(MathF.Abs(Quaternion.Dot(posedRotation, scene.Clips[0].Frames[0][joint].Rot)) > .99999f);
        Assert.Equal(new Vector3(200, 100, 0), scene.Clips[0].Frames[0][joint].Pos);
        Assert.Equal(new Vector3(300, 100, 0), scene.Clips[0].Frames[^1][joint].Pos);
    }
}