voxel_tile_raycast/
lib.rs

1use nalgebra::{Vector3, Vector2};
2
3pub mod na {
4    pub use nalgebra::*;
5}
6
7#[cfg(all(feature = "f64", feature = "f32"))]
8compile_error!("f32 and f64 features can not be enabled together");
9
10#[cfg(not(any(feature = "f64", feature = "f32")))]
11compile_error!("f32 or f64 feature should be enabled");
12
13#[cfg(feature = "f64")]
14type Real = f64;
15
16#[cfg(feature = "f32")]
17type Real = f32;
18
19macro_rules! raycast {
20    ($func: ident, $vec_i: ident, $vec_indexes: expr, $stepped_index_func: ident) => {
21        pub fn $func(
22            origin: $vec_i<Real>,
23            dir: $vec_i<Real>,
24            max_dir: Real,
25            mut func: impl FnMut($vec_i<i32>, $vec_i<Real>, $vec_i<i32>) -> bool,
26        ) {
27            if dir == $vec_i::zeros() {
28                panic!("dir is zero");
29            }
30            let dir = dir.normalize();
31            let mut t = 0.0;
32            let mut index = origin.map(|val| val.floor() as i32);
33            let step = dir.map(|val| val.signum() as i32);
34            let t_delta = dir.map(|val| 1.0 / val).abs();
35            let dist = $vec_indexes.map(|val| {
36                if step[val] > 0 {
37                    index[val] as Real + 1.0 - origin[val]
38                } else {
39                    origin[val] - index[val] as Real
40                }
41            });
42            let mut t_max = $vec_indexes.map(|val| {
43                if t_delta[val] < Real::INFINITY {
44                    t_delta[val] * dist[val]
45                } else {
46                    Real::INFINITY
47                }
48            });
49
50            if !func(
51                index,
52                $vec_indexes.map(|val| origin[val] + t * dist[val]),
53                $vec_i::zeros(),
54            ) {
55                while t < max_dir {
56                    let stepped_index = $stepped_index_func(t_max);
57
58                    index[stepped_index] += step[stepped_index];
59                    t = t_max[stepped_index];
60                    t_max[stepped_index] += t_delta[stepped_index];
61
62                    if func(
63                        index,
64                        $vec_indexes.map(|val| origin[val] + t * dist[val]),
65                        {
66                            let mut hit_norm = $vec_i::zeros();
67                            hit_norm[stepped_index] = -step[stepped_index];
68                            hit_norm
69                        },
70                    ) {
71                        break;
72                    }
73                }
74            }
75        }
76    };
77}
78
79#[cfg(feature = "voxel")]
80fn voxel_stepped_index(t_max: Vector3<Real>) -> usize {
81    if t_max.x < t_max.y && t_max.x < t_max.z {
82        0
83    } else if t_max.y < t_max.z {
84        1
85    } else {
86        2
87    }
88}
89
90#[cfg(feature = "tile")]
91fn tile_stepped_index(t_max: Vector2<Real>) -> usize {
92    if t_max.x < t_max.y {
93        0
94    } else {
95        1
96    }
97}
98
99#[cfg(feature = "voxel")]
100raycast!(voxel_raycast, Vector3, Vector3::new(0, 1, 2), voxel_stepped_index);
101#[cfg(feature = "tile")]
102raycast!(tile_raycast, Vector2, Vector2::new(0, 1), tile_stepped_index);