dynamis-world 0.6.0

GPU-driven physics engine
Documentation
@group(0) @binding(0) var<uniform> params: StepParams;
@group(0) @binding(1) var<storage, read> entry_cells: array<u32>;
@group(0) @binding(2) var<storage, read> entry_colliders: array<u32>;
@group(0) @binding(3) var<storage, read_write> entry_count: array<atomic<u32>>;
@group(0) @binding(4) var<storage, read_write> pair_major: array<u32>;
@group(0) @binding(5) var<storage, read_write> pair_minor: array<u32>;
@group(0) @binding(6) var<storage, read_write> pair_count: array<atomic<u32>>;
@group(0) @binding(7) var<storage, read_write> spillover: array<atomic<u32>>;
@group(0) @binding(8) var<storage, read> body_activity: array<u32>;

fn collider_is_awake(collider: u32) -> bool {
    return body_activity[collider / MAX_COLLIDERS_PER_BODY] != 0u;
}

fn emit_pair(first: u32, second: u32) {
    if (first == second) {
        return;
    }
    let a = min(first, second);
    let b = max(first, second);
    let slot = atomicAdd(&pair_count[0], 1u);
    if (slot < arrayLength(&pair_minor)) {
        pair_minor[slot] = b;
        pair_major[slot] = a;
    } else {
        atomicAdd(&spillover[0], 1u);
    }
}

@compute @workgroup_size(WORKGROUP_SIZE)
fn main(@builtin(global_invocation_id) gid: vec3u) {
    let index = gid.y * (WORKGROUPS_PER_ROW * WORKGROUP_SIZE) + gid.x;
    let live = min(atomicLoad(&entry_count[0]), arrayLength(&entry_cells));
    if (index >= live) {
        return;
    }
    if (!collider_is_awake(entry_colliders[index])) {
        return;
    }
    let cell = entry_cells[index];
    var cursor = index + 1u;
    while (cursor < live && entry_cells[cursor] == cell) {
        emit_pair(entry_colliders[index], entry_colliders[cursor]);
        cursor = cursor + 1u;
    }
    var back = index;
    while (back > 0u && entry_cells[back - 1u] == cell) {
        back = back - 1u;
        if (!collider_is_awake(entry_colliders[back])) {
            emit_pair(entry_colliders[back], entry_colliders[index]);
        }
    }
}