diff options
| author | iamcheeseman <[hidden email]> | 2026-02-15 13:17:36 -0500 |
|---|---|---|
| committer | iamcheeseman <[hidden email]> | 2026-02-15 13:17:36 -0500 |
| commit | cab0d6e99d96f621e6efcf1ed6b5537cf122ad96 (patch) | |
| tree | c2ec966dbced8eff07ac89fe3cdde29dafe604e7 /src/phys | |
| parent | 93c27830060788dea1c364465d29e44d05a3064e (diff) | |
Wall sliding/jumping
Diffstat (limited to 'src/phys')
| -rw-r--r-- | src/phys/body.odin | 53 | ||||
| -rw-r--r-- | src/phys/world.odin | 43 |
2 files changed, 75 insertions, 21 deletions
diff --git a/src/phys/body.odin b/src/phys/body.odin index ed8f6b5..54177e5 100644 --- a/src/phys/body.odin +++ b/src/phys/body.odin @@ -33,10 +33,16 @@ Collision_Type :: enum (u8) { Raycast :: struct { start: Vec2, - dir: Vec2, + end: Vec2, mask: Layer_Set, } +Raycast_Collision :: struct { + body: Body_Handle, + enter: Vec2, + exit: Vec2, +} + Body :: struct { handle: Body_Handle, bin_idx: i32, @@ -108,33 +114,46 @@ point_aabb :: proc(r: Rect, p: Vec2) -> bool { } @(require_results) -raycast_to_aabb :: proc(rc: Raycast, body: Body) -> bool { +raycast_to_aabb :: proc( + rc: Raycast, + body: Body, +) -> (collision := Raycast_Collision{}, collided := false) { 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) + rc_dir_to_body := (body_min + body_max) * 0.5 - rc.start + rc_dir := linalg.normalize0(rc.end - rc.start) // Don't consider bodies behind the ray - if linalg.dot(rc_dir_to_body, rc.dir) < 0 { - return false; + if linalg.dot(rc_dir_to_body, rc_dir) < 0 { + return } - tmin := -math.INF_F32 - tmax := math.INF_F32 + near := -math.INF_F32 + far := math.INF_F32 - dir_inv := 1.0 / rc.dir + 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 + tx_near := (body_min.x - rc.start.x) * dir_inv.x + tx_far := (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)) + near = max(near, min(tx_far, tx_near)) + far = min(far, max(tx_far, tx_near)) - ty_min := (body_min.y - rc.start.y) * dir_inv.y - ty_max := (body_max.y - rc.start.y) * dir_inv.y + ty_near := (body_min.y - rc.start.y) * dir_inv.y + ty_far := (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)) + near = max(near, min(ty_far, ty_near)) + far = min(far, max(ty_far, ty_near)) + + collision.body = body.handle + collision.enter = rc.start + rc_dir * near + collision.exit = rc.start + rc_dir * far + + rc_len := linalg.length2(rc.end - rc.start) + rc_dist_to_body := linalg.length2(rc.start - collision.enter) + + collided = far >= near && rc_dist_to_body <= rc_len - return tmax >= tmin + return } diff --git a/src/phys/world.odin b/src/phys/world.odin index 4a10590..25db947 100644 --- a/src/phys/world.odin +++ b/src/phys/world.odin @@ -158,6 +158,40 @@ remove_body :: proc(h: Body_Handle) { world.bodies[h.idx] = {} } +@(require_results) +get_closest_raycast_collision :: proc( + rc: Raycast, +) -> (Raycast_Collision, bool) { + nearest_collision: Raycast_Collision + nearest_dist := math.INF_F32 + + bin_list := get_surrounding_bins(rc.start) + + for bin in bin_list { + for b_h in bin { + b := _get_body(b_h) + + if b.layers & rc.mask == nil { + continue + } + + b_rect := b.rect + b_rect.start += b.pos + + collision, collided := raycast_to_aabb(rc, b^) + if collided { + dist := linalg.length2(collision.enter - rc.start) + if dist < nearest_dist { + nearest_dist = dist + nearest_collision = collision + } + } + } + } + + return nearest_collision, nearest_collision.body.uses > 0 +} + is_colliding :: proc{ is_body_colliding, is_raycast_colliding, @@ -184,8 +218,8 @@ get_colliding_bodies :: proc{ get_colliding_bodies_raycast_vs_body :: proc( rc: Raycast, allocator := context.allocator -) -> []Body_Handle { - bodies := make([dynamic]Body_Handle, allocator) +) -> []Raycast_Collision { + bodies := make([dynamic]Raycast_Collision, allocator) bin_list := get_surrounding_bins(rc.start) @@ -200,8 +234,9 @@ get_colliding_bodies_raycast_vs_body :: proc( b_rect := b.rect b_rect.start += b.pos - if raycast_to_aabb(rc, b^) { - append(&bodies, b_h) + collision, collided := raycast_to_aabb(rc, b^) + if collided { + append(&bodies, collision) } } } |
