struct vb0 {
    float3 position; // 12
    float3 normal; // 24
	float4 tangent; // 40
};

RWStructuredBuffer<vb0> base : register(u1);
StructuredBuffer<vb0> eyes_key : register(t0);

StructuredBuffer<vb0> cursor_left : register(t1);
StructuredBuffer<vb0> cursor_right : register(t2);
StructuredBuffer<vb0> cursor_up : register(t3);
StructuredBuffer<vb0> cursor_down : register(t4);

StructuredBuffer<vb0> eye_size_buffer : register(t5);
StructuredBuffer<vb0> eye_cross_buffer : register(t6);
StructuredBuffer<vb0> blink : register(t7);
StructuredBuffer<vb0> iris_blink : register(t8);
StructuredBuffer<vb0> mouth_key : register(t9);
StructuredBuffer<vb0> mouth_center_left : register(t10);
StructuredBuffer<vb0> mouth_center_right : register(t11);
StructuredBuffer<vb0> mouth_center_up : register(t12);
StructuredBuffer<vb0> mouth_center_down : register(t13);

StructuredBuffer<vb0> mouth_tongue1 : register(t14);
StructuredBuffer<vb0> mouth_tongue2 : register(t15);
StructuredBuffer<vb0> mouth_tongue3 : register(t16);

StructuredBuffer<vb0> mouth_tongue_left : register(t17);
StructuredBuffer<vb0> mouth_tongue_right : register(t18);
StructuredBuffer<vb0> mouth_tongue_up : register(t19);
StructuredBuffer<vb0> mouth_tongue_down : register(t20);

StructuredBuffer<vb0> mouth_left_in : register(t21);
StructuredBuffer<vb0> mouth_left_out : register(t22);
StructuredBuffer<vb0> mouth_left_up : register(t23);
StructuredBuffer<vb0> mouth_left_down : register(t24);

StructuredBuffer<vb0> mouth_right_in : register(t25);
StructuredBuffer<vb0> mouth_right_out : register(t26);
StructuredBuffer<vb0> mouth_right_up : register(t27);
StructuredBuffer<vb0> mouth_right_down : register(t28);

StructuredBuffer<vb0> mouth1x_left : register(t29);
StructuredBuffer<vb0> mouth1x_right : register(t30);
StructuredBuffer<vb0> mouth2x_left : register(t31);
StructuredBuffer<vb0> mouth2x_right : register(t32);
StructuredBuffer<vb0> mouth3x_left : register(t33);
StructuredBuffer<vb0> mouth3x_right : register(t34);

StructuredBuffer<vb0> mouth1y_left : register(t35);
StructuredBuffer<vb0> mouth1y_right : register(t36);
StructuredBuffer<vb0> mouth2y_left : register(t37);
StructuredBuffer<vb0> mouth2y_right : register(t38);
StructuredBuffer<vb0> mouth3y_left : register(t39);
StructuredBuffer<vb0> mouth3y_right : register(t40);
StructuredBuffer<vb0> brows_blink : register(t41);
StructuredBuffer<vb0> brows_lx_left : register(t42);
StructuredBuffer<vb0> brows_lx_right : register(t43);
StructuredBuffer<vb0> brows_ly_up : register(t44);
StructuredBuffer<vb0> brows_ly_down : register(t45);
StructuredBuffer<vb0> brows_rx_left : register(t46);
StructuredBuffer<vb0> brows_rx_right : register(t47);
StructuredBuffer<vb0> brows_ry_up : register(t48);
StructuredBuffer<vb0> brows_ry_down : register(t49);

StructuredBuffer<vb0> brows3_lx_left : register(t50);
StructuredBuffer<vb0> brows3_lx_right : register(t51);
StructuredBuffer<vb0> brows3_ly_up : register(t52);
StructuredBuffer<vb0> brows3_ly_down : register(t53);
StructuredBuffer<vb0> brows3_rx_left : register(t54);
StructuredBuffer<vb0> brows3_rx_right : register(t55);
StructuredBuffer<vb0> brows3_ry_up : register(t56);
StructuredBuffer<vb0> brows3_ry_down : register(t57);

StructuredBuffer<vb0> brows2_lx_left : register(t58);
StructuredBuffer<vb0> brows2_lx_right : register(t59);
StructuredBuffer<vb0> brows2_ly_up : register(t60);
StructuredBuffer<vb0> brows2_ly_down : register(t61);
StructuredBuffer<vb0> brows2_rx_left : register(t62);
StructuredBuffer<vb0> brows2_rx_right : register(t63);
StructuredBuffer<vb0> brows2_ry_up : register(t64);
StructuredBuffer<vb0> brows2_ry_down : register(t65);

StructuredBuffer<vb0> brows1_l_left : register(t66);
StructuredBuffer<vb0> brows2_l_left : register(t67);
StructuredBuffer<vb0> brows3_l_left : register(t68);
StructuredBuffer<vb0> brows1_l_right : register(t69);
StructuredBuffer<vb0> brows2_l_right : register(t70);
StructuredBuffer<vb0> brows3_l_right : register(t71);

StructuredBuffer<vb0> brows1_r_left : register(t72);
StructuredBuffer<vb0> brows2_r_left : register(t73);
StructuredBuffer<vb0> brows3_r_left : register(t74);
StructuredBuffer<vb0> brows1_r_right : register(t75);
StructuredBuffer<vb0> brows2_r_right : register(t76);
StructuredBuffer<vb0> brows3_r_right : register(t77);

Texture1D<float4> IniParams : register(t120);

StructuredBuffer<vb0> brows_expr : register(t78);

static float3 BlendTongueRaw(float slider, float3 t1, float3 t2, float3 t3)
{
    if (slider < 0.3)
    {
        float s = saturate(slider / 0.3);
        return t1 * s;
    }
    else if (slider < 0.6)
    {
        float a = saturate((slider - 0.3) / 0.3);
        return lerp(t1, t2, a);
    }
    else
    {
        float a = saturate((slider - 0.6) / 0.4);
        return lerp(t2, t3, a);
    }
}

static float MaskFromRaw(float3 raw)
{
    return smoothstep(0.0005, 0.002, length(raw));
}

static float3 deltaFromCursor(float2 cursor, StructuredBuffer<vb0> rightBuf, StructuredBuffer<vb0> leftBuf, StructuredBuffer<vb0> downBuf, StructuredBuffer<vb0> upBuf, uint idx, float3 base_pos)
{
    return (rightBuf[idx].position - base_pos) * max(0.0, (cursor.x - 0.5) * 2.0)
         + (leftBuf[idx].position - base_pos) * max(0.0, (0.5 - cursor.x) * 2.0)
         + (downBuf[idx].position - base_pos) * max(0.0, (cursor.y - 0.5) * 2.0)
         + (upBuf[idx].position - base_pos) * max(0.0, (0.5 - cursor.y) * 2.0);
}

static float3 SplitLeftBaseRight(float s, float3 leftDelta, float3 rightDelta)
{
    if (s < 0.5)
    {
        float t = saturate(s / 0.5);
        return lerp(leftDelta, float3(0.0,0.0,0.0), t);
    }
    else
    {
        float t = saturate((s - 0.5) / 0.5);
        return lerp(float3(0.0,0.0,0.0), rightDelta, t);
    }
}

[numthreads(1,1,1)]
void main(uint3 DTid : SV_DispatchThreadID)
{
    uint i = DTid.x;

    float4 params = IniParams.Load(0);
    float eyes_weight = IniParams[102].y;

    float eye_size = IniParams[103].y;
    float eye_cross = IniParams[104].y;

    float blink_start = IniParams[105].y;
    float blink_dur = IniParams[107].y;
    float global_time = IniParams[110].y;

    float eye_resource_flag = IniParams[106].y;

    float3 base_pos = base[i].position;
    float2 cursor = saturate(float2(IniParams[100].y, IniParams[101].y));

    float3 cursor_delta = deltaFromCursor(cursor, cursor_right, cursor_left, cursor_down, cursor_up, i, base_pos);

    float2 mouth_cursor = saturate(float2(IniParams[101].z, IniParams[102].z));
    float3 mouth_cursor_delta = deltaFromCursor(mouth_cursor, mouth_center_right, mouth_center_left, mouth_center_down, mouth_center_up, i, base_pos);

    float3 tongue_direction_delta = deltaFromCursor(mouth_cursor, mouth_tongue_right, mouth_tongue_left, mouth_tongue_down, mouth_tongue_up, i, base_pos);

    float2 mouth_left_cursor = saturate(float2(IniParams[106].z, IniParams[107].z));
    float2 mouth_right_cursor = saturate(float2(IniParams[104].z, IniParams[105].z));

    float3 mouth_left_delta = deltaFromCursor(mouth_left_cursor, mouth_left_out, mouth_left_in, mouth_left_down, mouth_left_up, i, base_pos);

    float3 mouth_right_delta = deltaFromCursor(mouth_right_cursor, mouth_right_in, mouth_right_out, mouth_right_down, mouth_right_up, i, base_pos);

    float s1x = IniParams[108].z;
    float s2x = IniParams[109].z;
    float s3x = IniParams[110].z;
    float s1y = IniParams[111].z;
    float s2y = IniParams[112].z;
    float s3y = IniParams[113].z;

    float3 m1x_left = (mouth1x_left[i].position - base_pos);
    float3 m1x_right = (mouth1x_right[i].position - base_pos);
    float3 m2x_left = (mouth2x_left[i].position - base_pos);
    float3 m2x_right = (mouth2x_right[i].position - base_pos);
    float3 m3x_left = (mouth3x_left[i].position - base_pos);
    float3 m3x_right = (mouth3x_right[i].position - base_pos);

    float3 m1y_left = (mouth1y_left[i].position - base_pos);
    float3 m1y_right = (mouth1y_right[i].position - base_pos);
    float3 m2y_left = (mouth2y_left[i].position - base_pos);
    float3 m2y_right = (mouth2y_right[i].position - base_pos);
    float3 m3y_left = (mouth3y_left[i].position - base_pos);
    float3 m3y_right = (mouth3y_right[i].position - base_pos);

    float3 mouth1x_delta = SplitLeftBaseRight(s1x, m1x_left, m1x_right);
    float3 mouth2x_delta = SplitLeftBaseRight(s2x, m2x_left, m2x_right);
    float3 mouth3x_delta = SplitLeftBaseRight(s3x, m3x_left, m3x_right);

    float3 mouth1y_delta = SplitLeftBaseRight(s1y, m1y_left, m1y_right);
    float3 mouth2y_delta = SplitLeftBaseRight(s2y, m2y_left, m2y_right);
    float3 mouth3y_delta = SplitLeftBaseRight(s3y, m3y_left, m3y_right);

    float3 raw_eyes_key_delta = (eyes_key[i].position - base_pos);
    float eyes_key_mag = length(raw_eyes_key_delta);
    float eyes_key_mask = smoothstep(0.0005, 0.002, eyes_key_mag);

    float applied_eyes_key_scale = eyes_weight;

    if (eye_resource_flag >= 6.0 && eye_resource_flag <= 14.0)
        applied_eyes_key_scale *= max(0.0, 1.0 - (0.1 * eyes_key_mask));

    float3 eyes_key_delta = raw_eyes_key_delta * applied_eyes_key_scale;
    float3 eye_size_delta = (eye_size_buffer[i].position - base_pos) * (1.0 - eye_size);
    float3 eye_cross_delta = (eye_cross_buffer[i].position - base_pos) * eye_cross;

    float3 blink_vec = (blink[i].position - base_pos);
    float blink_mod = 1.0;

    float blink_weight = 0.0;
    float dur = max(blink_dur, 0.0001);
    float t = saturate((global_time - blink_start) / dur);
    float u = (t < 0.5) ? (t * 2.0) : ((1.0 - t) * 2.0);
    float smooth = u * u * (3.0 - 2.0 * u);
    blink_weight = smooth;

    if ((eye_resource_flag >= 2.0 && eye_resource_flag < 3.0) || (eye_resource_flag >= 5.0 && eye_resource_flag < 6.0))
        blink_mod = max(0.0, 1.0 - eyes_weight);

    else if (eye_resource_flag >= 3.0 && eye_resource_flag <= 4.0)
    {
        float eyes_key_mag = length(eyes_key_delta);
        float mask = smoothstep(0.0005, 0.002, eyes_key_mag);
        blink_mod *= (1.0 - (eyes_weight * mask));
    }

    float3 blink_delta = blink_vec * (blink_weight * blink_mod);

    float3 brows_blink_delta = float3(0.0, 0.0, 0.0);
    {
        float3 raw_brows = (brows_blink[i].position - base_pos);
        brows_blink_delta = raw_brows * (blink_weight * blink_mod);
    }

    float brows_weight = IniParams[112].x;
    float3 brows_expr_delta = float3(0.0, 0.0, 0.0);
    {
        float3 raw_brows_expr = (brows_expr[i].position - base_pos);
        float be_mag = length(raw_brows_expr);
        float be_mask = smoothstep(0.00001, 0.0001, be_mag);
        brows_expr_delta = raw_brows_expr * (brows_weight * be_mask);
    }

    float b1l_x = saturate(IniParams[100].x);
    float b1l_y = saturate(IniParams[101].x);
    float b1r_x = saturate(IniParams[102].x);
    float b1r_y = saturate(IniParams[103].x);
    float b3l_x = saturate(IniParams[104].x);
    float b3l_y = saturate(IniParams[105].x);
    float b3r_x = saturate(IniParams[106].x);
    float b3r_y = saturate(IniParams[107].x);
    float b2l_x = saturate(IniParams[108].x);
    float b2l_y = saturate(IniParams[109].x);
    float b2r_x = saturate(IniParams[110].x);
    float b2r_y = saturate(IniParams[111].x);

    float3 b1lx_left = (brows_lx_left[i].position - base_pos);
    float3 b1lx_right = (brows_lx_right[i].position - base_pos);
    float3 b1ly_down = (brows_ly_down[i].position - base_pos);
    float3 b1ly_up = (brows_ly_up[i].position - base_pos);

    float3 b1rx_left = (brows_rx_left[i].position - base_pos);
    float3 b1rx_right = (brows_rx_right[i].position - base_pos);
    float3 b1ry_down = (brows_ry_down[i].position - base_pos);
    float3 b1ry_up = (brows_ry_up[i].position - base_pos);

    float3 brows1l_x_delta = SplitLeftBaseRight(b1l_x, b1lx_left, b1lx_right);
    float3 brows1l_y_delta = SplitLeftBaseRight(b1l_y, b1ly_down, b1ly_up);

    float3 brows1r_x_delta = SplitLeftBaseRight(b1r_x, b1rx_left, b1rx_right);
    float3 brows1r_y_delta = SplitLeftBaseRight(b1r_y, b1ry_down, b1ry_up);

    float3 b2lx_left = (brows2_lx_left[i].position - base_pos);
    float3 b2lx_right = (brows2_lx_right[i].position - base_pos);
    float3 b2ly_down = (brows2_ly_down[i].position - base_pos);
    float3 b2ly_up = (brows2_ly_up[i].position - base_pos);

    float3 b2rx_left = (brows2_rx_left[i].position - base_pos);
    float3 b2rx_right = (brows2_rx_right[i].position - base_pos);
    float3 b2ry_down = (brows2_ry_down[i].position - base_pos);
    float3 b2ry_up = (brows2_ry_up[i].position - base_pos);

    float3 brows2l_x_delta = SplitLeftBaseRight(b2l_x, b2lx_left, b2lx_right);
    float3 brows2l_y_delta = SplitLeftBaseRight(b2l_y, b2ly_down, b2ly_up);

    float3 brows2r_x_delta = SplitLeftBaseRight(b2r_x, b2rx_left, b2rx_right);
    float3 brows2r_y_delta = SplitLeftBaseRight(b2r_y, b2ry_down, b2ry_up);

    float3 b3lx_left = (brows3_lx_left[i].position - base_pos);
    float3 b3lx_right = (brows3_lx_right[i].position - base_pos);
    float3 b3ly_down = (brows3_ly_down[i].position - base_pos);
    float3 b3ly_up = (brows3_ly_up[i].position - base_pos);

    float3 b3rx_left = (brows3_rx_left[i].position - base_pos);
    float3 b3rx_right = (brows3_rx_right[i].position - base_pos);
    float3 b3ry_down = (brows3_ry_down[i].position - base_pos);
    float3 b3ry_up = (brows3_ry_up[i].position - base_pos);

    float3 brows3l_x_delta = SplitLeftBaseRight(b3l_x, b3lx_left, b3lx_right);
    float3 brows3l_y_delta = SplitLeftBaseRight(b3l_y, b3ly_down, b3ly_up);

    float3 brows3r_x_delta = SplitLeftBaseRight(b3r_x, b3rx_left, b3rx_right);
    float3 brows3r_y_delta = SplitLeftBaseRight(b3r_y, b3ry_down, b3ry_up);

    float sb1l = IniParams[114].z;
    float sb2l = IniParams[115].z;
    float sb3l = IniParams[116].z;
    float sb1r = IniParams[117].z;
    float sb2r = IniParams[118].z;
    float sb3r = IniParams[119].z;

    float3 b1l_s_left = (brows1_l_left[i].position - base_pos);
    float3 b1l_s_right = (brows1_l_right[i].position - base_pos);
    float3 brows1_l_slider_delta = SplitLeftBaseRight(sb1l, b1l_s_left, b1l_s_right);

    float3 b2l_s_left = (brows2_l_left[i].position - base_pos);
    float3 b2l_s_right = (brows2_l_right[i].position - base_pos);
    float3 brows2_l_slider_delta = SplitLeftBaseRight(sb2l, b2l_s_left, b2l_s_right);

    float3 b3l_s_left = (brows3_l_left[i].position - base_pos);
    float3 b3l_s_right = (brows3_l_right[i].position - base_pos);
    float3 brows3_l_slider_delta = SplitLeftBaseRight(sb3l, b3l_s_left, b3l_s_right);

    float3 b1r_s_left = (brows1_r_left[i].position - base_pos);
    float3 b1r_s_right = (brows1_r_right[i].position - base_pos);
    float3 brows1_r_slider_delta = SplitLeftBaseRight(sb1r, b1r_s_left, b1r_s_right);

    float3 b2r_s_left = (brows2_r_left[i].position - base_pos);
    float3 b2r_s_right = (brows2_r_right[i].position - base_pos);
    float3 brows2_r_slider_delta = SplitLeftBaseRight(sb2r, b2r_s_left, b2r_s_right);

    float3 b3r_s_left = (brows3_r_left[i].position - base_pos);
    float3 b3r_s_right = (brows3_r_right[i].position - base_pos);
    float3 brows3_r_slider_delta = SplitLeftBaseRight(sb3r, b3r_s_left, b3r_s_right);

    if (eye_resource_flag >= 6.0 && eye_resource_flag <= 14.0)
    {
        applied_eyes_key_scale = eyes_weight * max(0.0, 1.0 - (blink_weight * eyes_key_mask));

        eyes_key_delta = raw_eyes_key_delta * applied_eyes_key_scale;
    }
    
    float iris_delay = IniParams[111].y;
    float iris_dur_scale = IniParams[112].y;
    float iris_bounce = IniParams[113].y;

    float iris_start = blink_start + iris_delay;
    float iris_dur = max(dur * iris_dur_scale, 0.0001);
    float iris_t = saturate((global_time - iris_start) / iris_dur);

    float iu = (iris_t < 0.5) ? (iris_t * 2.0) : ((1.0 - iris_t) * 2.0);
    float ismooth = iu * iu * (3.0 - 2.0 * iu);

    float bounce = iris_bounce * sin(iu * 3.14159265) * (1.0 - iu);
    float iris_weight = clamp(ismooth + bounce, 0.0, 1.0);

    float3 iris_raw = (iris_blink[i].position - base_pos);
    float iris_mag = length(iris_raw);
    float iris_mask = smoothstep(0.0001, 0.0008, iris_mag);
    float3 iris_delta = iris_raw * (iris_weight * iris_mask);

    float mouth_weight = IniParams[100].z;
    float3 mouth_raw = (mouth_key[i].position - base_pos);
    float mouth_mag = length(mouth_raw);
    float mouth_mask = smoothstep(0.0005, 0.002, mouth_mag) * mouth_weight;

    blink_delta *= (1.0 - mouth_mask);
    iris_delta *= (1.0 - mouth_mask);

    float tongue_slider = mouth_weight;
    float3 tongue_delta = float3(0.0, 0.0, 0.0);

    float mouth_resource_flag = IniParams[103].z;
    if (mouth_resource_flag >= 10.0 && mouth_resource_flag < 11.0)
    {
        float3 t1 = (mouth_tongue1[i].position - base_pos);
        float3 t2 = (mouth_tongue2[i].position - base_pos);
        float3 t3 = (mouth_tongue3[i].position - base_pos);

        float3 raw = BlendTongueRaw(tongue_slider, t1, t2, t3);
        float mask = MaskFromRaw(raw);
        tongue_delta = raw * mask;
    }

    float3 mouth_delta;
    if (mouth_resource_flag >= 10.0 && mouth_resource_flag < 11.0)
    {
        mouth_delta = tongue_delta;
    }
    else
    {
        mouth_delta = mouth_raw * mouth_weight;
    }

    float tongue_correct_strength = 0.0;
    if (mouth_resource_flag >= 10.0 && mouth_resource_flag < 11.0)
        tongue_correct_strength = 1.0;
    else if (mouth_resource_flag > 0.0)
        tongue_correct_strength = 0.5;

    base[i].position = base_pos + eyes_key_delta + eye_size_delta + eye_cross_delta + cursor_delta + mouth_cursor_delta + blink_delta + brows_blink_delta + brows_expr_delta + brows1l_x_delta + brows1l_y_delta + brows1r_x_delta + brows1r_y_delta + brows2l_x_delta + brows2l_y_delta + brows2r_x_delta + brows2r_y_delta + brows3l_x_delta + brows3l_y_delta + brows3r_x_delta + brows3r_y_delta + brows1_l_slider_delta + brows2_l_slider_delta + brows3_l_slider_delta + brows1_r_slider_delta + brows2_r_slider_delta + brows3_r_slider_delta + iris_delta + mouth_delta + (tongue_direction_delta * tongue_correct_strength) + mouth_left_delta + mouth_right_delta + mouth1x_delta + mouth2x_delta + mouth3x_delta + mouth1y_delta + mouth2y_delta + mouth3y_delta;
}