// **** CYCLIC ANIMATION SHADER ****
// Contributors: Zlevir

// Based on shapekey shader by Cybertron, SinsOfSeven

struct VertexAttributes {
    uint2 position;
    uint normal; 
    uint texcoord;
    uint tangent;
};

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

struct PoseAttributes {
    float4 x;
    float4 y;
    float4 z;
};
//pose[frame][vg] = [4x3 matrix] [0,0,0,1]

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.z = f16tof32(base[i].position.x >> 16);
    pos.y = f16tof32(base[i].position.y & 0xffff) * -1.0;
    pos.w = f16tof32(base[i].position.y >> 16);
    normal.x = float(i8toi32(base[i].normal.x & 0xff));
    normal.z = float(i8toi32(base[i].normal.x>>8 & 0xff));
    normal.y = float(i8toi32(base[i].normal.x>>16 & 0xff)) * -1.0;
    normal.w = 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;
    //trans x
    trans = pose[frame*((int)VG_COUNT)+blend[i].indicies.x].x * blend[i].weights.x *(1.0-inter);
    trans += pose[frame*((int)VG_COUNT)+blend[i].indicies.y].x * blend[i].weights.y *(1.0-inter);
    trans += pose[frame*((int)VG_COUNT)+blend[i].indicies.z].x * blend[i].weights.z *(1.0-inter);
    trans += pose[frame*((int)VG_COUNT)+blend[i].indicies.w].x * blend[i].weights.w *(1.0-inter);
    trans += pose[(frame+1)*((int)VG_COUNT)+blend[i].indicies.x].x * blend[i].weights.x *inter;
    trans += pose[(frame+1)*((int)VG_COUNT)+blend[i].indicies.y].x * blend[i].weights.y *inter;
    trans += pose[(frame+1)*((int)VG_COUNT)+blend[i].indicies.z].x * blend[i].weights.z *inter;
    trans += pose[(frame+1)*((int)VG_COUNT)+blend[i].indicies.w].x * blend[i].weights.w *inter;
    pos_result.x = dot(trans, pos);
    normal_result.x = dot(trans, normal);
    //trans y
    trans = pose[frame*((int)VG_COUNT)+blend[i].indicies.x].y * blend[i].weights.x *(1.0-inter);
    trans += pose[frame*((int)VG_COUNT)+blend[i].indicies.y].y * blend[i].weights.y *(1.0-inter);
    trans += pose[frame*((int)VG_COUNT)+blend[i].indicies.z].y * blend[i].weights.z *(1.0-inter);
    trans += pose[frame*((int)VG_COUNT)+blend[i].indicies.w].y * blend[i].weights.w *(1.0-inter);
    trans += pose[(frame+1)*((int)VG_COUNT)+blend[i].indicies.x].y * blend[i].weights.x *inter;
    trans += pose[(frame+1)*((int)VG_COUNT)+blend[i].indicies.y].y * blend[i].weights.y *inter;
    trans += pose[(frame+1)*((int)VG_COUNT)+blend[i].indicies.z].y * blend[i].weights.z *inter;
    trans += pose[(frame+1)*((int)VG_COUNT)+blend[i].indicies.w].y * blend[i].weights.w *inter;
    pos_result.y = dot(trans, pos);
    normal_result.y = dot(trans, normal);
    //trans z
    trans = pose[frame*((int)VG_COUNT)+blend[i].indicies.x].z * blend[i].weights.x *(1.0-inter);
    trans += pose[frame*((int)VG_COUNT)+blend[i].indicies.y].z * blend[i].weights.y *(1.0-inter);
    trans += pose[frame*((int)VG_COUNT)+blend[i].indicies.z].z * blend[i].weights.z *(1.0-inter);
    trans += pose[frame*((int)VG_COUNT)+blend[i].indicies.w].z * blend[i].weights.w *(1.0-inter);
    trans += pose[(frame+1)*((int)VG_COUNT)+blend[i].indicies.x].z * blend[i].weights.x *inter;
    trans += pose[(frame+1)*((int)VG_COUNT)+blend[i].indicies.y].z * blend[i].weights.y *inter;
    trans += pose[(frame+1)*((int)VG_COUNT)+blend[i].indicies.z].z * blend[i].weights.z *inter;
    trans += pose[(frame+1)*((int)VG_COUNT)+blend[i].indicies.w].z * blend[i].weights.w *inter;
    pos_result.z = dot(trans, pos);
    normal_result.z = dot(trans, normal);
    pos_result.w = 1.0;
    normal_result.w = 1.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.z)<<16 | (uint)f32tof16(pos_result.x);
    rw_buffer[i].position.y = (uint)f32tof16(pos_result.w)<<16 | (uint)f32tof16(pos_result.y*-1.0);
    rw_buffer[i].normal.x = i32toi8(int(normal_result.w))<<24 | i32toi8(int(normal_result.y*-1.0))<<16 | i32toi8(int(normal_result.z))<<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;
}