// Skin the new body (VCOUNT verts, weighted to UNION bone order) with the CONVERTED palette.
struct Vtx  { float3 position; float3 normal; float4 tangent; };
struct Skin { float4 weight;   uint4  index;  };
// u1 is a stride-zero raw compute buffer. The command list unbinds it after dispatch, then copies
// its bytes into the separate 40-byte vertex buffer used by the draw.
RWByteAddressBuffer     rw_out : register(u1);
StructuredBuffer<Vtx>   bindV  : register(t0);
StructuredBuffer<Skin>  skinB  : register(t1);
// Palette resources are physically structured as 16-byte float4 rows. Keep the shader declaration
// at that exact stride and assemble each 4-row matrix explicitly; rebinding the same resource as a
// StructuredBuffer<Mat> declares a conflicting 64-byte stride and is driver-dependent.
StructuredBuffer<float4> palRows : register(t2);
static const uint VCOUNT=134;
[numthreads(64,1,1)]
void main(uint3 tid : SV_DispatchThreadID){
    uint vid=tid.x; if(vid>=VCOUNT) return;
    Vtx V=bindV[vid]; Skin S=skinB[vid];
    float3 sp=0,sn=0,st=0;
    [unroll] for(int k=0;k<4;k++){
        float wk=S.weight[k]; uint b=S.index[k];
        uint p=b<<2;
        float4x4 M=float4x4(palRows[p],palRows[p+1],palRows[p+2],palRows[p+3]);
        sp+=wk*mul(float4(V.position,1.0),M).xyz;
        sn+=wk*mul(float4(V.normal,0.0),M).xyz;
        st+=wk*mul(float4(V.tangent.xyz,0.0),M).xyz;
    }
    uint o=vid*40;
    rw_out.Store3(o,    asuint(sp));
    rw_out.Store3(o+12, asuint(normalize(sn)));
    rw_out.Store4(o+24, asuint(float4(normalize(st),V.tangent.w)));
}
