// Armature shader (preserve volume skinning)
struct VertexAttributes {
    uint2 position;
    uint normal; 
    uint tangent;
    uint texcoord;
    uint texcoord1;
};

struct BlendAttributes {
    float4 weights;
    int4 indicies;
};
//blend[index] = 

struct PoseAttributes {
    float3 S;
    float3 T;
    float4 QR;
    float4 QD;
};


RWStructuredBuffer<VertexAttributes> rw_buffer : register(u5);
StructuredBuffer<VertexAttributes> base : register(t50);
StructuredBuffer<BlendAttributes> blend : register(t51);
StructuredBuffer<PoseAttributes> pose : register(t52);

Texture1D<float4> IniParams : register(t120);
#define TIME IniParams[88].x
#define VG_COUNT IniParams[89].x
int i8toi32(uint src);
uint i32toi8(int src);
float3x4 m_scale(float3x4 m, float v);
float4 apply_trans(float4 pos, float3x4 trans);
[numthreads(1, 1, 1)]
void main(uint3 threadID : SV_DispatchThreadID)
{
    uint i = threadID.x;
    float4 pos; //x,y,z,1
    float4 normal;
    //translate f16 position to f32
    //transform the model back to blender axis (x,y,z) -> (x,z,-y)
    pos.x = f16tof32(base[i].position.x & 0xffff);
    pos.y = f16tof32(base[i].position.x >> 16);
    pos.z = f16tof32(base[i].position.y & 0xffff);
    pos.w = 1.0f ;//f16tof32(base[i].position.y >> 16);
    normal.x = float(i8toi32(base[i].normal.x & 0xff));
    normal.y = float(i8toi32(base[i].normal.x>>8 & 0xff));
    normal.z = float(i8toi32(base[i].normal.x>>16 & 0xff));
    normal.w = 0.0; //float(i8toi32(base[i].normal.x>>24 & 0xff));
    
    int frame = (int)floor(TIME);
    float inter = TIME - floor(TIME);
    //vg = blend[i].indicies
    //vg_weight = blend[i].weights
    
    float4 trans, pos_result, normal_result;
    int4 blend_indicy, blend_indicy_next;
    float3 scale, bias;
    blend_indicy = frame*((int)VG_COUNT)+blend[i].indicies;
    blend_indicy_next = (frame+1)*((int)VG_COUNT)+blend[i].indicies;

    //perform LBS for scale factor
    
    scale  = pose[blend_indicy.x].S * blend[i].weights.x *(1.0f-inter);
    scale += pose[blend_indicy.y].S * blend[i].weights.y *(1.0f-inter);
    scale += pose[blend_indicy.z].S * blend[i].weights.z *(1.0f-inter);
    scale += pose[blend_indicy.w].S * blend[i].weights.w *(1.0f-inter);
    scale += pose[blend_indicy_next.x].S * blend[i].weights.x *inter;
    scale += pose[blend_indicy_next.y].S * blend[i].weights.y *inter;
    scale += pose[blend_indicy_next.z].S * blend[i].weights.z *inter;
    scale += pose[blend_indicy_next.w].S * blend[i].weights.w *inter;
    bias   = pose[blend_indicy.x].T * blend[i].weights.x *(1.0f-inter);
    bias  += pose[blend_indicy.y].T * blend[i].weights.y *(1.0f-inter);
    bias  += pose[blend_indicy.z].T * blend[i].weights.z *(1.0f-inter);
    bias  += pose[blend_indicy.w].T * blend[i].weights.w *(1.0f-inter);
    bias  += pose[blend_indicy_next.x].T * blend[i].weights.x *inter;
    bias  += pose[blend_indicy_next.y].T * blend[i].weights.y *inter;
    bias  += pose[blend_indicy_next.z].T * blend[i].weights.z *inter;
    bias  += pose[blend_indicy_next.w].T * blend[i].weights.w *inter;
    pos.xyz = pos.xyz*scale + bias;
    //perform DLB
    float4 qr, qd;
    qr = pose[blend_indicy.x].QR * blend[i].weights.x *(1.0f-inter);
    qd = pose[blend_indicy.x].QD * blend[i].weights.x *(1.0f-inter);
    if (dot(pose[blend_indicy.x].QR, pose[blend_indicy.y].QR) < 0) {
        qr -= pose[blend_indicy.y].QR * blend[i].weights.y *(1.0f-inter);
        qd -= pose[blend_indicy.y].QD * blend[i].weights.y *(1.0f-inter);
    } else {
        qr += pose[blend_indicy.y].QR * blend[i].weights.y *(1.0f-inter);
        qd += pose[blend_indicy.y].QD * blend[i].weights.y *(1.0f-inter);
    }
    if (dot(pose[blend_indicy.x].QR, pose[blend_indicy.z].QR) < 0) {
        qr -= pose[blend_indicy.z].QR * blend[i].weights.z *(1.0f-inter);
        qd -= pose[blend_indicy.z].QD * blend[i].weights.z *(1.0f-inter);
    } else {
        qr += pose[blend_indicy.z].QR * blend[i].weights.z *(1.0f-inter);
        qd += pose[blend_indicy.z].QD * blend[i].weights.z *(1.0f-inter);
    }
    if (dot(pose[blend_indicy.x].QR, pose[blend_indicy.w].QR) < 0) {
        qr -= pose[blend_indicy.w].QR * blend[i].weights.w *(1.0f-inter);
        qd -= pose[blend_indicy.w].QD * blend[i].weights.w *(1.0f-inter);
    } else {
        qr += pose[blend_indicy.w].QR * blend[i].weights.w *(1.0f-inter);
        qd += pose[blend_indicy.w].QD * blend[i].weights.w *(1.0f-inter);
    }

    if (dot(pose[blend_indicy.x].QR, pose[blend_indicy_next.x].QR) < 0) {
        qr -= pose[blend_indicy_next.x].QR * blend[i].weights.x *inter;
        qd -= pose[blend_indicy_next.x].QD * blend[i].weights.x *inter;
    } else {
        qr += pose[blend_indicy_next.x].QR * blend[i].weights.x *inter;
        qd += pose[blend_indicy_next.x].QD * blend[i].weights.x *inter;
    }
    if (dot(pose[blend_indicy.x].QR, pose[blend_indicy_next.y].QR) < 0) {
        qr -= pose[blend_indicy_next.y].QR * blend[i].weights.y *inter;
        qd -= pose[blend_indicy_next.y].QD * blend[i].weights.y *inter;
    } else {
        qr += pose[blend_indicy_next.y].QR * blend[i].weights.y *inter;
        qd += pose[blend_indicy_next.y].QD * blend[i].weights.y *inter;
    }
    if (dot(pose[blend_indicy.x].QR, pose[blend_indicy_next.z].QR) < 0) {
        qr -= pose[blend_indicy_next.z].QR * blend[i].weights.z *inter;
        qd -= pose[blend_indicy_next.z].QD * blend[i].weights.z *inter;
    } else {
        qr += pose[blend_indicy_next.z].QR * blend[i].weights.z *inter;
        qd += pose[blend_indicy_next.z].QD * blend[i].weights.z *inter;
    }
    if (dot(pose[blend_indicy.x].QR, pose[blend_indicy_next.w].QR) < 0) {
        qr -= pose[blend_indicy_next.w].QR * blend[i].weights.w *inter;
        qd -= pose[blend_indicy_next.w].QD * blend[i].weights.w *inter;
    } else {
        qr += pose[blend_indicy_next.w].QR * blend[i].weights.w *inter;
        qd += pose[blend_indicy_next.w].QD * blend[i].weights.w *inter;
    }
    qd = qd / length(qr);
    qr = qr / length(qr);

    trans = float4(1.0f-2.0f*qr.y*qr.y-2.0f*qr.z*qr.z, 
                   2.0f*(qr.x*qr.y-qr.w*qr.z), 
                   2.0f*(qr.x*qr.z+qr.w*qr.y),
                   2.0f*(-qd.w*qr.x+qd.x*qr.w-qd.y*qr.z+qd.z*qr.y));
    pos_result.x = dot(trans, pos);
    normal_result.x = dot(trans, normal);
    trans = float4(2.0f*(qr.x*qr.y+qr.w*qr.z), 
                   1.0f-2.0f*qr.x*qr.x-2.0f*qr.z*qr.z, 
                   2.0f*(qr.y*qr.z-qr.w*qr.x),
                   2.0f*(-qd.w*qr.y+qd.x*qr.z+qd.y*qr.w-qd.z*qr.x));
    pos_result.y = dot(trans, pos);
    normal_result.y = dot(trans, normal);
    trans = float4(2.0f*(qr.x*qr.z-qr.w*qr.y), 
                   2.0f*(qr.y*qr.z+qr.w*qr.x), 
                   1.0f-2.0f*qr.x*qr.x-2.0f*qr.y*qr.y,
                   2.0f*(-qd.w*qr.z-qd.x*qr.y+qd.y*qr.x+qd.z*qr.w));
    pos_result.z = dot(trans, pos);
    normal_result.z = dot(trans, normal);
    //pos_result.xyzw = pos.xyzw;
    //normal_result.xyzw = normal.xyzw;
    pos_result.w = 1.0f;
    normal_result.w = 0.0;
    //trans back to f16
    //transform the model back to game axis (x,z,-y) -> (x,y,z)
    rw_buffer[i].position.x = (uint)f32tof16(pos_result.y)<<16 | (uint)f32tof16(pos_result.x);
    rw_buffer[i].position.y = (uint)f32tof16(pos_result.w)<<16 | (uint)f32tof16(pos_result.z);
    rw_buffer[i].normal.x = i32toi8(int(normal_result.w))<<24 | i32toi8(int(normal_result.z))<<16 | i32toi8(int(normal_result.y))<<8 | i32toi8(int(normal_result.x));

}

float4 apply_trans(float4 pos, float3x4 trans) {
    float4 result;
    result.x = dot(trans[0], pos);
    result.y = dot(trans[1], pos);
    result.z = dot(trans[2], pos);
    result.w = 1;
    return result;
}

int i8toi32(uint src) {
    if (src & 0x80) {
        return src & 0x7f | 0xffffff80;
    } else {
        return src & 0x7f;
    }
}

uint i32toi8(int src) {
    if (src > 127) {
        src = 127;
    }
    if (src < -128) {
        src = -128;
    }
    return (uint)src & 0x000000ff;
}

float3x4 m_scale(float3x4 m, float v)
{

    m[0][0] *= v; m[1][0] *= v; m[2][0] *= v;
    m[0][1] *= v; m[1][1] *= v; m[2][1] *= v;
    m[0][2] *= v; m[1][2] *= v; m[2][2] *= v;
    m[0][3] *= v; m[1][3] *= v; m[2][3] *= v;
    return m;
}