// Pooled recover for one part: a = Cpinv * posed(anchor verts), SCATTERED into the shared union
// palette via Map (part-local bone -> union bone). The operator is SLIM: each bone reads only its
// own anchor vertices (Sel), not the whole mesh. Widths are RAGGED — Off carries (base,width) per
// bone, so a bone that needs every vertex costs its neighbours nothing. Rows land in THIS PART'S
// draw space; the convert pass rebases them into the anchor's space before skinning.
// ROWS = 4*partBones.
struct Vtx { float3 position; float3 normal; float4 tangent; };
StructuredBuffer<Vtx> q      : register(t0);
Buffer<float>         Cpinv  : register(t1);   // per bone: 4 rows of `width` coefficients, from 4*base
Buffer<uint>          Map    : register(t2);   // partBones entries: local bone -> union bone, or 0xFFFFFFFF
Buffer<uint>          Sel    : register(t3);   // anchor vertex indices, bone b at [base, base+width)
Buffer<uint>          Off    : register(t4);   // 2 per bone: base, width
RWStructuredBuffer<float4> palOut : register(u1); // 4*unionBones rows (shared across all pool parts)
static const uint ROWS=312;
[numthreads(64,1,1)]
void main(uint3 tid : SV_DispatchThreadID){
    uint i=tid.x; if(i>=ROWS) return;
    uint localBone=i>>2, comp=i&3;
    uint u=Map[localBone];
    if(u==0xFFFFFFFF) return;   // this part does NOT own the bone (another part supports it better) -> don't clobber
    uint sbase=Off[localBone<<1], width=Off[(localBone<<1)|1];
    // These pseudo-inverse rows can be ill-conditioned. Keep the reduction ordered and compensate
    // its rounding error so driver-specific contraction/reassociation cannot amplify it into the pose.
    precise float3 a=float3(0,0,0), correction=float3(0,0,0);
    uint cbase=(sbase<<2)+comp*width;
    for(uint t=0;t<width;t++){
        precise float3 term=Cpinv[cbase+t]*q[Sel[sbase+t]].position;
        precise float3 y=term-correction;
        precise float3 next=a+y;
        correction=(next-a)-y;
        a=next;
    }
    palOut[(u<<2)|comp]=float4(a,(comp==3)?1.0:0.0);   // scatter into this bone's union slot
}
