package phys import "core:math" import "core:math/linalg" import "core:log" Vec2 :: [2]f32 Rect :: struct { start: Vec2, size: Vec2, } Layer :: enum (u16) { Hard, // hard collisions; don't let bodies intersect at all. default Soft, // soft collisions; push away other bodies with a force Enemy, // enemy hitboxes Player, // player hitboxes Enemy_Projectile, Player_Projectile, } Layer_Set :: bit_set[Layer;u16] Collision_Type :: enum (u8) { Up, Down, Right, Left, Horizontal, Vertical, } Raycast :: struct { start: Vec2, dir: Vec2, mask: Layer_Set, } Body :: struct { handle: Body_Handle, bin_idx: i32, rect: Rect, active: bool, pos: Vec2, vel: Vec2, collisions: bit_set[Collision_Type;u8], layers: Layer_Set, mask: Layer_Set, } make_body :: proc( rect: Rect, layers := Layer_Set{.Hard}, mask := Layer_Set{.Hard}, ) -> Body_Handle { b := Body { rect = rect, layers = layers, mask = mask, active = true, } return add_body(b) } @(require_results) make_raycast :: #force_inline proc( start: Vec2, end: Vec2, mask := Layer_Set{.Hard}, ) -> Raycast { return Raycast{ start, end, mask, } } @(require_results) aabb_hori :: proc(a: Rect, b: Rect) -> bool { return a.start.x < b.start.x + b.size.x && b.start.x < a.start.x + a.size.x } @(require_results) aabb_vert :: proc(a: Rect, b: Rect) -> bool { return a.start.y < b.start.y + b.size.y && b.start.y < a.start.y + a.size.y } @(require_results) aabb :: proc(a: Rect, b: Rect) -> bool { return aabb_hori(a, b) && aabb_vert(a, b) } @(require_results) point_aabb_hori :: proc(r: Rect, p: Vec2) -> bool { return r.start.x < p.x && r.start.x + r.size.x > p.x } @(require_results) point_aabb_vert :: proc(r: Rect, p: Vec2) -> bool { return r.start.y < p.y && r.start.y + r.size.y > p.y } @(require_results) point_aabb :: proc(r: Rect, p: Vec2) -> bool { return point_aabb_hori(r, p) && point_aabb_vert(r, p) } @(require_results) raycast_to_aabb :: proc(rc: Raycast, body: Body) -> bool { body_min := body.pos + body.rect.start body_max := body_min + body.rect.size rc_dir_to_body := linalg.normalize0((body_min + body_max) * 0.5 - rc.start) // Don't consider bodies behind the ray if linalg.dot(rc_dir_to_body, rc.dir) < 0 { return false; } tmin := -math.INF_F32 tmax := math.INF_F32 dir_inv := 1.0 / rc.dir tx_min := (body_min.x - rc.start.x) * dir_inv.x tx_max := (body_max.x - rc.start.x) * dir_inv.x tmin = max(tmin, min(tx_max, tx_min)) tmax = min(tmax, max(tx_max, tx_min)) ty_min := (body_min.y - rc.start.y) * dir_inv.y ty_max := (body_max.y - rc.start.y) * dir_inv.y tmin = max(tmin, min(ty_max, ty_min)) tmax = min(tmax, max(ty_max, ty_min)) return tmax >= tmin }