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_write> body_states: array<BodyState>;
@group(0) @binding(2) var<storage, read> body_descs: array<BodyDescriptor>;
@group(0) @binding(3) var<storage, read> colliders: array<Collider>;
@group(0) @binding(4) var<storage, read> pair_major: array<u32>;
@group(0) @binding(5) var<storage, read> pair_minor: array<u32>;
@group(0) @binding(6) var<storage, read_write> pair_count: array<atomic<u32>>;

fn load_body(slot: u32) -> Body {
    return Body(body_states[slot], body_descs[slot]);
}

fn sweep_retreat(
    moving: Body, moving_collider: Collider,
    static_body: Body, static_collider: Collider,
) -> f32 {
    if (!body_has_ccd(moving)) {
        return 1.0;
    }
    let motion = moving.state.position - moving.state.prev_position;
    let sweep_length = length(motion);
    let bound = min_radius(moving_collider) + min_radius(static_collider);
    if (sweep_length <= bound * 0.5) {
        return 1.0;
    }
    let moving_world = world_collider(moving.state, moving_collider);
    let static_world = world_collider(static_body.state, static_collider);
    if (static_world.kind == SHAPE_NONE) {
        return 1.0;
    }
    let hit = convex_hit_at(
        moving_world,
        moving.state.prev_position,
        motion / sweep_length,
        static_world,
        min_radius(moving_collider),
        sweep_length,
    );
    if (hit.distance <= 0.0 || hit.distance >= sweep_length) {
        return 1.0;
    }
    return min(hit.distance / (sweep_length * 0.98), 1.0);
}

fn retreat(slot: u32, moving: Body, moving_collider: Collider, other: Body, other_collider: Collider) {
    let time = sweep_retreat(moving, moving_collider, other, other_collider);
    if (time >= 1.0) {
        return;
    }
    var state = moving.state;
    state.position = state.prev_position + (state.position - state.prev_position) * time;
    let axis = sign_normalize(
        world_collider(other.state, other_collider).center - world_collider(state, moving_collider).center);
    let normal_speed = dot(state.velocity, axis);
    if (normal_speed > 0.0) {
        let restitution = material_combine(
            moving_collider.restitution,
            other_collider.restitution,
            params.restitution_combine,
        );
        state.velocity = state.velocity - axis * normal_speed * (1.0 + restitution);
    }
    body_states[slot] = state;
}

@compute @workgroup_size(WORKGROUP_SIZE)
fn main(@builtin(global_invocation_id) gid: vec3u) {
    let index = gid.y * (WORKGROUPS_PER_ROW * WORKGROUP_SIZE) + gid.x;
    if (index >= min(atomicLoad(&pair_count[0]), arrayLength(&pair_major))) {
        return;
    }
    if (index > 0u && pair_major[index] == pair_major[index - 1u] && pair_minor[index] == pair_minor[index - 1u]) {
        return;
    }
    let first_slot = pair_major[index];
    let second_slot = pair_minor[index];
    let first_body_slot = first_slot / MAX_COLLIDERS_PER_BODY;
    let second_body_slot = second_slot / MAX_COLLIDERS_PER_BODY;
    if (first_body_slot == second_body_slot) {
        return;
    }
    let first = load_body(first_body_slot);
    let second = load_body(second_body_slot);
    if (body_is_static(first) && body_is_static(second)) {
        return;
    }
    let first_collider = colliders[first_slot];
    let second_collider = colliders[second_slot];
    if (first_collider.kind == SHAPE_NONE || second_collider.kind == SHAPE_NONE) {
        return;
    }
    if (!collider_filter_intersects(first, first_collider, second, second_collider)) {
        return;
    }
    retreat(first_body_slot, first, first_collider, second, second_collider);
    retreat(second_body_slot, second, second_collider, first, first_collider);
}