aboutsummaryrefslogtreecommitdiff
path: root/src/phys
diff options
context:
space:
mode:
Diffstat (limited to 'src/phys')
-rw-r--r--src/phys/body.odin53
-rw-r--r--src/phys/world.odin43
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)
}
}
}