Editor/Core/Import/Fbx/FbxMeshChannel.cs
#nullable enable annotations
namespace HumanoidRigger.Formats.Fbx;

/// <summary>Resolves FBX layer mapping and indexing before triangle expansion.</summary>
internal sealed class FbxMeshChannel
{
    readonly string mapping,reference,label;
    readonly float[] values;
    readonly int[] indices;
    readonly int width;
    FbxMeshChannel(FbxNode layer,string valueName,string indexName,int width)
    {
        label=layer.Name;this.width=width;
        mapping=layer.Child("MappingInformationType")?.Prop<string>(0)??"";
        reference=layer.Child("ReferenceInformationType")?.Prop<string>(0)??"";
        values=layer.Child(valueName)?.AsFloatArray(0)??[];
        indices=(layer.Child(indexName)??layer.Child(valueName+"Index"))?.AsIntArray(0)??[];
        if(values.Length%width!=0||values.Any(v=>!float.IsFinite(v)))throw new FormatException($"Invalid FBX {label} values.");
        if(mapping is not ("ByVertice" or "ByVertex" or "ByControlPoint" or "ByPolygonVertex" or "ByPolygon" or "AllSame"))throw new FormatException($"Unsupported FBX {label} mapping: {mapping}.");
        if(reference is not ("Direct" or "IndexToDirect" or "Index"))throw new FormatException($"Unsupported FBX {label} indexing: {reference}.");
    }
    public static FbxMeshChannel? Read(FbxNode geometry,string type,string values,string indices,int width)
    {
        var link=geometry.ChildrenNamed("Layer").SelectMany(l=>l.ChildrenNamed("LayerElement")).FirstOrDefault(e=>e.Child("Type")?.Prop<string>(0)==type);
        var layers=geometry.ChildrenNamed(type).ToArray();
        var layer=link is null?layers.FirstOrDefault():layers.FirstOrDefault(l=>l.Prop<int>(0)==link.Child("TypedIndex")?.Prop<int>(0));
        if(layer is null&&link is not null)throw new FormatException($"Missing FBX {type} layer.");
        return layer is null?null:new(layer,values,indices,width);
    }
    public void Copy(int controlPoint,int corner,int polygon,Span<float> destination)
    {
        int index=mapping switch{"ByPolygonVertex"=>corner,"ByPolygon"=>polygon,"AllSame"=>0,_=>controlPoint};
        if(reference!="Direct")
        {
            if(index<0||index>=indices.Length)throw new FormatException($"Invalid FBX {label} index array.");
            index=indices[index];
        }
        if(index<0||index>=values.Length/width)throw new FormatException($"FBX {label} refers to a missing value.");
        values.AsSpan(index*width,width).CopyTo(destination);
    }
}