struct BoundingVolumeData {
    center: vec3<f32>,
    _pad0: f32,
    half_extents: vec3<f32>,
    _pad1: f32,
    orientation: vec4<f32>,
    transform: mat4x4<f32>,
    color: vec4<f32>,
}

struct LineData {
    start: vec4<f32>,
    end: vec4<f32>,
    color: vec4<f32>,
    entity_id: u32,
    visible: u32,
    _padding: vec2<u32>,
}

struct Params {
    bounding_volume_count: u32,
    line_offset: u32,
    _padding: vec2<u32>,
}

@group(0) @binding(0)
var<storage, read> bounding_volumes: array<BoundingVolumeData>;

@group(0) @binding(1)
var<storage, read_write> lines: array<LineData>;

@group(0) @binding(2)
var<uniform> params: Params;

fn quat_rotate(q: vec4<f32>, v: vec3<f32>) -> vec3<f32> {
    let u = q.xyz;
    let s = q.w;
    return 2.0 * dot(u, v) * u + (s * s - dot(u, u)) * v + 2.0 * s * cross(u, v);
}

fn get_obb_corner(center: vec3<f32>, half_extents: vec3<f32>, orientation: vec4<f32>, signs: vec3<f32>) -> vec3<f32> {
    let local_corner = half_extents * signs;
    return center + quat_rotate(orientation, local_corner);
}

@compute @workgroup_size(64)
fn main(@builtin(global_invocation_id) global_id: vec3<u32>) {
    let bv_index = global_id.x;
    if (bv_index >= params.bounding_volume_count) {
        return;
    }

    let bv = bounding_volumes[bv_index];
    let transform = bv.transform;

    let local_corners = array<vec3<f32>, 8>(
        get_obb_corner(bv.center, bv.half_extents, bv.orientation, vec3<f32>(-1.0, -1.0, -1.0)),
        get_obb_corner(bv.center, bv.half_extents, bv.orientation, vec3<f32>( 1.0, -1.0, -1.0)),
        get_obb_corner(bv.center, bv.half_extents, bv.orientation, vec3<f32>(-1.0,  1.0, -1.0)),
        get_obb_corner(bv.center, bv.half_extents, bv.orientation, vec3<f32>( 1.0,  1.0, -1.0)),
        get_obb_corner(bv.center, bv.half_extents, bv.orientation, vec3<f32>(-1.0, -1.0,  1.0)),
        get_obb_corner(bv.center, bv.half_extents, bv.orientation, vec3<f32>( 1.0, -1.0,  1.0)),
        get_obb_corner(bv.center, bv.half_extents, bv.orientation, vec3<f32>(-1.0,  1.0,  1.0)),
        get_obb_corner(bv.center, bv.half_extents, bv.orientation, vec3<f32>( 1.0,  1.0,  1.0)),
    );

    var world_corners: array<vec3<f32>, 8>;
    for (var i = 0u; i < 8u; i++) {
        let world_pos = transform * vec4<f32>(local_corners[i], 1.0);
        world_corners[i] = world_pos.xyz;
    }

    let edges = array<vec2<u32>, 12>(
        vec2<u32>(0u, 1u),
        vec2<u32>(1u, 3u),
        vec2<u32>(3u, 2u),
        vec2<u32>(2u, 0u),
        vec2<u32>(4u, 5u),
        vec2<u32>(5u, 7u),
        vec2<u32>(7u, 6u),
        vec2<u32>(6u, 4u),
        vec2<u32>(0u, 4u),
        vec2<u32>(1u, 5u),
        vec2<u32>(2u, 6u),
        vec2<u32>(3u, 7u),
    );

    let base_line_index = params.line_offset + bv_index * 12u;
    for (var i = 0u; i < 12u; i++) {
        let edge = edges[i];
        let line_index = base_line_index + i;

        lines[line_index].start = vec4<f32>(world_corners[edge.x], 1.0);
        lines[line_index].end = vec4<f32>(world_corners[edge.y], 1.0);
        lines[line_index].color = bv.color;
        lines[line_index].entity_id = bv_index;
        lines[line_index].visible = 1u;
        lines[line_index]._padding = vec2<u32>(0u, 0u);
    }
}
