parry2d/query/ray/
ray_voxels.rs1use 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 let hit = aabb.cast_local_ray_and_get_normal(ray, max_t, solid);
64
65 if let Some(mut hit) = hit {
66 hit.feature = FeatureId::Face(
68 self.flat_id(voxel_key).unwrap_or_else(|| unreachable!()),
69 );
70 return Some(hit);
71 }
72 }
73 }
74
75 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 break;
116 }
117 } else if voxel_key[imin] > domain_mins[imin] {
118 voxel_key[imin] -= 1;
119 } else {
120 break;
122 }
123 }
124
125 None
126 }
127}