dev/HumanoidRetargeter.Tests/Formats/GltfModelSkinTests.cs
using System.Globalization;
using System.Text;
using System.Text.RegularExpressions;
using HumanoidRetargeter.Formats.Gltf;
using Xunit;

namespace HumanoidRetargeter.Tests.Formats;

public class GltfModelSkinTests
{
    [Theory]
    [InlineData(true, true, .5f, 700f)]
    [InlineData(true, false, .5f, 1000f)]
    [InlineData(false, false, .5f, 10100f)]
    [InlineData(true, true, 0f, 600f)]
    public void MeshUsesSkinBindTransformsInsteadOfMeshNode(bool skinned, bool inverseBinds, float weight, float expectedX)
    {
        using var stream = new MemoryStream();
        using var writer = new BinaryWriter(stream);
        // Three vertices, two equally weighted joints. Parent scale must be baked once.
        foreach (var y in new[] { 0f, 1f, 2f })
            foreach (var value in new[] { 1f, y, 0f }) writer.Write(value);
        for (var i = 0; i < 3; i++) writer.Write(new byte[] { 0, 1, 0, 0 });
        for (var i = 0; i < 3; i++)
            foreach (var value in new[] { weight, weight, 0f, 0f }) writer.Write(value);
        foreach (var x in new[] { -1f, -2f })
            foreach (var value in new[] { 1f,0,0,0, 0,1,0,0, 0,0,1,0, x,0,0,1 }) writer.Write(value);
        var json = $$$"""
        {
          "asset":{"version":"2.0"},
          "buffers":[{"byteLength":224,"uri":"data:application/octet-stream;base64,{{{Convert.ToBase64String(stream.ToArray())}}}"}],
          "bufferViews":[
            {"buffer":0,"byteOffset":0,"byteLength":36},
            {"buffer":0,"byteOffset":36,"byteLength":12},
            {"buffer":0,"byteOffset":48,"byteLength":48},
            {"buffer":0,"byteOffset":96,"byteLength":128}],
          "accessors":[
            {"bufferView":0,"componentType":5126,"count":3,"type":"VEC3"},
            {"bufferView":1,"componentType":5121,"count":3,"type":"VEC4"},
            {"bufferView":2,"componentType":5126,"count":3,"type":"VEC4"},
            {"bufferView":3,"componentType":5126,"count":2,"type":"MAT4"}],
          "nodes":[
            {"name":"root","scale":[2,2,2],"children":[1,2]},
            {"name":"jointA","translation":[3,0,0]},
            {"name":"jointB","translation":[5,0,0]},
            {"name":"mesh","mesh":0,"translation":[100,0,0]{{{(skinned ? ",\"skin\":0" : "")}}}}],
          "skins":[{"joints":[1,2]{{{(inverseBinds ? ",\"inverseBindMatrices\":3" : "")}}}}],
          "meshes":[{"primitives":[{"attributes":{"POSITION":0,"JOINTS_0":1,"WEIGHTS_0":2}}]}]
        }
        """;
        var bytes = Encoding.UTF8.GetBytes(json);
        var skeleton = GltfImporter.Import(bytes).Skeleton;
        var dmx = GltfModelDmxWriter.Write(bytes, skeleton, "test");
        var match = Regex.Match(dmx, """"position\$0" "vector3_array"\s*\[\s*"([^"]+)"""");
        Assert.True(match.Success);
        var xPosition = float.Parse(match.Groups[1].Value.Split(' ')[0], CultureInfo.InvariantCulture);
        Assert.Equal(expectedX, xPosition, 3);
    }
}