Skip to main content

steel_core/player/game_mode/
raycast.rs

1use super::{ClipBlockShape, ClipFluid, DVec3, World, WorldAabb, swap};
2
3pub(super) fn piercing_ray_hit_t(
4    world: &World,
5    bounding_box: WorldAabb,
6    from: DVec3,
7    to: DVec3,
8    entity_margin: f64,
9) -> Option<f64> {
10    if let Some(hit_t) = ray_aabb_hit_t(bounding_box, from, to) {
11        return Some(hit_t);
12    }
13    if entity_margin <= 0.0 {
14        return None;
15    }
16
17    let outside_hit_t = ray_aabb_hit_t(bounding_box.inflate(entity_margin), from, to)?;
18    let outside_hit = from + (to - from) * outside_hit_t;
19    let mut towards_target = DVec3::new(
20        f64::midpoint(bounding_box.min_x(), bounding_box.max_x()),
21        f64::midpoint(bounding_box.min_y(), bounding_box.max_y()),
22        f64::midpoint(bounding_box.min_z(), bounding_box.max_z()),
23    );
24    let block_hit = world.clip(
25        outside_hit,
26        towards_target,
27        ClipBlockShape::Collider,
28        ClipFluid::None,
29    );
30    if !block_hit.is_miss() {
31        towards_target = block_hit.location;
32    }
33    ray_aabb_hit_t(bounding_box, outside_hit, towards_target).map(|_| outside_hit_t)
34}
35
36fn ray_aabb_hit_t(aabb: WorldAabb, from: DVec3, to: DVec3) -> Option<f64> {
37    if aabb.contains(from) {
38        return Some(0.0);
39    }
40
41    let delta = to - from;
42    let mut t_min = 0.0_f64;
43    let mut t_max = 1.0_f64;
44    if !update_ray_axis(
45        from.x,
46        delta.x,
47        aabb.min_x(),
48        aabb.max_x(),
49        &mut t_min,
50        &mut t_max,
51    ) {
52        return None;
53    }
54    if !update_ray_axis(
55        from.y,
56        delta.y,
57        aabb.min_y(),
58        aabb.max_y(),
59        &mut t_min,
60        &mut t_max,
61    ) {
62        return None;
63    }
64    if !update_ray_axis(
65        from.z,
66        delta.z,
67        aabb.min_z(),
68        aabb.max_z(),
69        &mut t_min,
70        &mut t_max,
71    ) {
72        return None;
73    }
74
75    Some(t_min)
76}
77
78fn update_ray_axis(
79    start: f64,
80    delta: f64,
81    min: f64,
82    max: f64,
83    t_min: &mut f64,
84    t_max: &mut f64,
85) -> bool {
86    if delta.abs() < f64::EPSILON {
87        return start >= min && start <= max;
88    }
89
90    let inverse_delta = 1.0 / delta;
91    let mut enter = (min - start) * inverse_delta;
92    let mut exit = (max - start) * inverse_delta;
93    if enter > exit {
94        swap(&mut enter, &mut exit);
95    }
96
97    *t_min = (*t_min).max(enter);
98    *t_max = (*t_max).min(exit);
99    *t_min <= *t_max
100}