struct ParticleUniforms
{
    view_proj:    mat4x4<f32>,
    camera_right: vec3<f32>,
    _pad0:        f32,
    camera_up:    vec3<f32>,
    _pad1:        f32,
}

@group(0) @binding(0)
var<uniform> uniforms: ParticleUniforms;

struct VertexIn
{
    @location(0) position: vec3<f32>,
    @location(1) uv:       vec2<f32>,
}

struct ParticleInstance
{
    @location(2) position: vec3<f32>,
    @location(3) velocity: vec3<f32>,
    @location(4) size:     f32,
    @location(5) color:    vec4<f32>,
    @location(6) age:      f32,
}

struct VertexOut
{
    @builtin(position) clip_pos: vec4<f32>,
    @location(0)       uv:       vec2<f32>,
    @location(1)       color:    vec4<f32>,
    @location(2)       age:      f32,
}

@vertex
fn vs_main(in: VertexIn, inst: ParticleInstance) -> VertexOut
{
    var out: VertexOut;

    let stretch = length(inst.velocity) * 0.05;
    let vel_dir = normalize(select(inst.velocity, vec3<f32>(0.0, 1.0, 0.0),
        length(inst.velocity) > 0.001));

    let world_pos = inst.position
        + uniforms.camera_right * (in.uv.x - 0.5) * inst.size
        + uniforms.camera_up    * (in.uv.y - 0.5) * inst.size
        + vel_dir * (in.uv.y - 0.5) * stretch;

    out.clip_pos = uniforms.view_proj * vec4<f32>(world_pos, 1.0);
    out.uv       = in.uv;
    out.color    = inst.color;
    out.age      = inst.age;
    return out;
}

var<private> bayer: array<f32, 16> = array<f32, 16>(
     0.0,  8.0,  2.0, 10.0,
    12.0,  4.0, 14.0,  6.0,
     3.0, 11.0,  1.0,  9.0,
    15.0,  7.0, 13.0,  5.0,
);

@fragment
fn fs_main(in: VertexOut) -> @location(0) vec4<f32>
{
    let centered  = in.uv * 2.0 - vec2<f32>(1.0, 1.0);
    let dist      = length(centered);
    let circle    = 1.0 - smoothstep(0.7, 1.0, dist);
    let age_alpha = (1.0 - in.age) * in.color.a * circle;

    let fx     = u32(in.clip_pos.x) % 4u;
    let fy     = u32(in.clip_pos.y) % 4u;
    let thresh = bayer[fy * 4u + fx] / 16.0;
    if age_alpha <= thresh
    {
        discard;
    }

    return vec4<f32>(in.color.rgb, 1.0);
}
