parry2d/query/ray/
ray_voxels.rs

1use crate::math::{Real, Vector};
2use crate::partitioning::BvhNode;
3use crate::query::{Ray, RayCast, RayIntersection};
4use crate::shape::{FeatureId, Voxels, VoxelsChunkRef};
5
6impl RayCast for Voxels {
7    #[inline]
8    fn cast_local_ray_and_get_normal(
9        &self,
10        ray: &Ray,
11        max_time_of_impact: Real,
12        solid: bool,
13    ) -> Option<RayIntersection> {
14        self.chunk_bvh()
15            .find_best(
16                max_time_of_impact,
17                |node: &BvhNode, best_so_far| node.cast_ray(ray, best_so_far),
18                |primitive, best_so_far| {
19                    let chunk = self.chunk_ref(primitive);
20                    chunk.cast_local_ray_and_get_normal(ray, best_so_far, solid)
21                },
22            )
23            .map(|(_chunk_id, hit)| hit)
24    }
25}
26
27impl<'a> RayCast for VoxelsChunkRef<'a> {
28    #[inline]
29    fn cast_local_ray_and_get_normal(
30        &self,
31        ray: &Ray,
32        max_time_of_impact: Real,
33        solid: bool,
34    ) -> Option<RayIntersection> {
35        use num_traits::Bounded;
36
37        let aabb = self.local_aabb();
38        let (min_t, mut max_t) = aabb.clip_ray_parameters(ray)?;
39
40        #[cfg(feature = "dim2")]
41        let ii = [0, 1];
42        #[cfg(feature = "dim3")]
43        let ii = [0, 1, 2];
44
45        if min_t > max_time_of_impact {
46            return None;
47        }
48
49        max_t = max_t.min(max_time_of_impact);
50        let clip_ray_a = ray.point_at(min_t);
51        let voxel_key_signed = self.voxel_at_point_unchecked(clip_ray_a);
52        let mut voxel_key = self.clamp_voxel(voxel_key_signed);
53        let [domain_mins, domain_maxs] = self.domain();
54
55        loop {
56            let aabb = self.voxel_aabb_unchecked(voxel_key);
57
58            if let Some(voxel) = self.voxel_state(voxel_key) {
59                if !voxel.is_empty() {
60                    // We hit a voxel!
61                    // TODO: if `solid` is false, and we started hitting from the first iteration,
62                    //       then we should continue the ray propagation until we reach empty space again.
63                    let hit = aabb.cast_local_ray_and_get_normal(ray, max_t, solid);
64
65                    if let Some(mut hit) = hit {
66                        // TODO: have the feature id be based on the voxel type?
67                        hit.feature = FeatureId::Face(
68                            self.flat_id(voxel_key).unwrap_or_else(|| unreachable!()),
69                        );
70                        return Some(hit);
71                    }
72                }
73            }
74
75            /*
76             * Find the next voxel to cast the ray on.
77             */
78            let toi = ii.map(|i| {
79                if ray.dir[i] > 0.0 {
80                    let t = (aabb.maxs[i] - ray.origin[i]) / ray.dir[i];
81                    if t < 0.0 {
82                        (Real::max_value(), true)
83                    } else {
84                        (t, true)
85                    }
86                } else if ray.dir[i] < 0.0 {
87                    let t = (aabb.mins[i] - ray.origin[i]) / ray.dir[i];
88                    if t < 0.0 {
89                        (Real::max_value(), false)
90                    } else {
91                        (t, false)
92                    }
93                } else {
94                    (Real::max_value(), false)
95                }
96            });
97
98            #[cfg(feature = "dim2")]
99            if toi[0].0 > max_t && toi[1].0 > max_t {
100                break;
101            }
102
103            #[cfg(feature = "dim3")]
104            if toi[0].0 > max_t && toi[1].0 > max_t && toi[2].0 > max_t {
105                break;
106            }
107
108            let imin = Vector::from(toi.map(|t| t.0)).imin();
109
110            if toi[imin].1 {
111                if voxel_key[imin] < domain_maxs[imin] - 1 {
112                    voxel_key[imin] += 1;
113                } else {
114                    // Leaving the shape’s bounds.
115                    break;
116                }
117            } else if voxel_key[imin] > domain_mins[imin] {
118                voxel_key[imin] -= 1;
119            } else {
120                // Leaving the shape’s bounds.
121                break;
122            }
123        }
124
125        None
126    }
127}