#ifndef TO_WORLD_GLSLV
#define TO_WORLD_GLSLV

#include <math.glslv>

/*==============================================================================
                                    VARS
==============================================================================*/
#var BILLBOARD_ALIGN BILLBOARD_ALIGN_VIEW
#var BILLBOARD_SPHERICAL 0
#var BILLBOARD_RANDOM 0
#var BILLBOARD 0
#var BILLBOARD_JITTERED 0
#var BILLBOARD_PRES_GLOB_ORIENTATION 0
#var STATIC_BATCH 0
#var REFLECTION_PASS REFL_PASS_NONE
/*============================================================================*/

#define MAX_BILLBOARD_ANGLE M_PI_4

#if BILLBOARD_SPHERICAL || !BILLBOARD && (BILLBOARD_ALIGN == BILLBOARD_ALIGN_VIEW)
mat3 billboard_spherical(vec3 center_pos, mat3 view_tsr) {
    vec4 bb_q = tsr_get_quat(view_tsr);
    // NOTE: camera is rotated downward by default,
    // inversed vertex order during reflection pass
#if REFLECTION_PASS == REFL_PASS_PLANE
    vec4 right_q = qsetAxisAngle(RIGHT_VECTOR, -M_PI/2.0);
#else
    vec4 right_q = qsetAxisAngle(RIGHT_VECTOR, M_PI/2.0);
#endif
    bb_q = qmult(right_q, bb_q);
    bb_q = qinv(bb_q);

    mat3 bb_tsr = tsr_identity();
    bb_tsr = tsr_set_trans(center_pos, bb_tsr);
    bb_tsr = tsr_set_quat(bb_q, bb_tsr);

    return bb_tsr;
}
#else
mat3 billboard_cylindrical(vec3 camera_eye, vec3 center_pos) {
    vec3 center_to_cam = camera_eye - center_pos;
    center_to_cam.z = 0.0;
    center_to_cam = normalize(center_to_cam);

    vec4 bb_q = qfrom_dir(center_to_cam, TOWARD_VECTOR);
    mat3 bb_tsr = tsr_identity();
    bb_tsr = tsr_set_trans(center_pos, bb_tsr);
    bb_tsr = tsr_set_quat(bb_q, bb_tsr);

    return bb_tsr;
}
#endif // BILLBOARD_SPHERICAL


#if BILLBOARD_JITTERED
mat3 bend_jitter_rotate_tsr(in vec3 wind_world, float wind_param, float jitter_amp,
        float jitter_freq, vec3 vec_seed, mat3 model_tsr) {

    float seed = fract(length(vec_seed) / 0.17); // [0, 1]
    float rand_freq = jitter_freq + seed / 10.0; // + 0%-10%
    float rand_phase = seed;
    if (jitter_freq != 0.0)
        rand_phase /= jitter_freq ; //  [0, 1/freq]

    wind_world *= 1.0 + 0.5 * sin(wind_param); // make wind gusty

    // jitter rotation angle
    float bj_angle = length(wind_world) * jitter_amp * sin(2.0*3.14 * wind_param
            * rand_freq + rand_phase);
    vec4 bj_quat = qsetAxisAngle(TOWARD_VECTOR, bj_angle);

    // rotate tsr from the right
    vec4 model_quat = tsr_get_quat(model_tsr);
    model_quat = qmult(model_quat, bj_quat);
    model_tsr = tsr_set_quat(model_quat, model_tsr);

    return model_tsr;
}
#endif

mat3 billboard_tsr(in vec3 camera_eye, in vec3 wcen, in mat3 view_tsr, 
        in mat3 model_tsr) {
#if BILLBOARD_SPHERICAL || !BILLBOARD && (BILLBOARD_ALIGN == BILLBOARD_ALIGN_VIEW)
    mat3 bill_tsr = billboard_spherical(wcen, view_tsr);
#elif BILLBOARD_RANDOM
    // get initial random rotation angle
    float seed = fract((wcen.x * 1.43 - wcen.z * 0.123 + wcen.y * 6.1));
    float alpha_rand = 2.0 * M_PI * seed;

    vec4 view_quat = tsr_get_quat(view_tsr);
    float alpha_cam = asin(2.0 * (view_quat.x * view_quat.y - view_quat.z * view_quat.w));

    float alpha_diff = alpha_cam - alpha_rand;
    if (alpha_diff < 0.0)
        alpha_diff = 2.0 * M_PI + alpha_diff;

    float res_angle = alpha_rand;
    if (alpha_diff <= MAX_BILLBOARD_ANGLE)
        res_angle += alpha_diff;
    else if (alpha_diff <= M_PI - MAX_BILLBOARD_ANGLE)
        res_angle += MAX_BILLBOARD_ANGLE * (2.0 * alpha_diff - M_PI) 
                / (2.0 * MAX_BILLBOARD_ANGLE - M_PI);
    else if (alpha_diff <= M_PI + MAX_BILLBOARD_ANGLE)
        res_angle += alpha_diff - M_PI;
    else if (alpha_diff <= 2.0 * M_PI - MAX_BILLBOARD_ANGLE)
        res_angle += MAX_BILLBOARD_ANGLE * (2.0 * alpha_diff - M_PI) 
                / (2.0 * MAX_BILLBOARD_ANGLE - M_PI) + M_PI;
    else
        res_angle += alpha_diff - 2.0 * M_PI;

    vec4 bill_quat = qsetAxisAngle(UP_VECTOR, res_angle);
    mat3 bill_tsr = tsr_identity();
    bill_tsr = tsr_set_trans(wcen, bill_tsr);
    bill_tsr = tsr_set_quat(bill_quat, bill_tsr);
#else
    mat3 bill_tsr = billboard_cylindrical(camera_eye, wcen);
#endif

#if BILLBOARD_PRES_GLOB_ORIENTATION && !STATIC_BATCH
    // NOTE: translation is already in bill_tsr
    model_tsr = tsr_set_trans(vec3(0.0), model_tsr);
    // apply scale and rotation
    bill_tsr = tsr_multiply(bill_tsr, model_tsr);
#else
    // apply scale only
    vec3 bill_scale = tsr_get_scale(bill_tsr);
    vec3 model_scale = tsr_get_scale(model_tsr);
    bill_tsr = tsr_set_scale(bill_scale * model_scale, bill_tsr);
#endif

    return bill_tsr;
}

vertex to_world(in vec3 pos, in vec3 cen, in vec3 tng, in vec3 shd_tng, in vec3 bnr, 
        in vec3 nrm, in mat3 model_tsr) {

    pos = tsr9_transform(model_tsr, pos);
    cen = tsr9_transform(model_tsr, cen);
    tng = tsr9_transform_dir(model_tsr, tng);
    shd_tng = tsr9_transform_dir(model_tsr, shd_tng);
    bnr = tsr9_transform_dir(model_tsr, bnr);
    nrm = tsr9_transform_normal(model_tsr, nrm);

    return tbn_norm(vertex(pos, cen, tng, shd_tng, bnr, nrm, vec3(0.0)));
}

#endif
