parry3d/query/nonlinear_shape_cast/
nonlinear_shape_cast_voxels_shape.rs1use crate::bounding_volume::BoundingVolume;
2use crate::math::{Point, Real, Vector};
3use crate::query::{NonlinearRigidMotion, QueryDispatcher, ShapeCastHit};
4use crate::shape::{Cuboid, Shape, Voxels};
5
6pub fn cast_shapes_nonlinear_voxels_shape<D>(
8 dispatcher: &D,
9 motion1: &NonlinearRigidMotion,
10 g1: &Voxels,
11 motion2: &NonlinearRigidMotion,
12 g2: &dyn Shape,
13 start_time: Real,
14 end_time: Real,
15 stop_at_penetration: bool,
16) -> Option<ShapeCastHit>
17where
18 D: ?Sized + QueryDispatcher,
19{
20 use num_traits::Bounded;
21
22 let mut motion2 = *motion2;
28 let mut motion1 = *motion1;
29 motion2.linvel -= motion1.linvel;
30 motion1.freeze(start_time);
31 let pos1 = motion1.position_at_time(start_time);
32
33 let mut hit = None;
48 let mut smallest_t = end_time;
49
50 let start_pos2_1 = pos1.inv_mul(&motion2.position_at_time(start_time));
53 let mut end_pos2_1 = pos1.inv_mul(&motion2.position_at_time(end_time));
54 end_pos2_1.translation = start_pos2_1.translation;
55 let start_aabb2_1 = g2
56 .compute_aabb(&start_pos2_1)
57 .merged(&g2.compute_aabb(&end_pos2_1));
58
59 let mut check_voxels_in_range = |search_domain: [Point<i32>; 2]| {
60 for vox in g1.voxels_in_range(search_domain[0], search_domain[1]) {
61 if !vox.state.is_empty() {
62 let center = g1.voxel_center(vox.grid_coords);
64 let cuboid = Cuboid::new(g1.voxel_size() / 2.0);
65 let vox_motion1 = motion1.prepend_translation(center.coords);
66 if let Some(new_hit) = dispatcher
67 .cast_shapes_nonlinear(
68 &vox_motion1,
69 &cuboid,
70 &motion2,
71 g2,
72 start_time,
73 end_time,
74 stop_at_penetration,
75 )
76 .ok()
77 .flatten()
78 {
79 if new_hit.time_of_impact < smallest_t {
80 smallest_t = new_hit.time_of_impact;
81 hit = Some(new_hit);
82 }
83 }
84 }
85 }
86 };
87
88 let mut search_domain = g1.voxel_range_intersecting_local_aabb(&start_aabb2_1);
89 check_voxels_in_range(search_domain);
90
91 let [domain_mins, domain_maxs] = g1.domain();
93
94 loop {
95 let search_domain_aabb = g1.voxel_range_aabb(search_domain[0], search_domain[1]);
96
97 #[cfg(feature = "dim2")]
99 let ii = [0, 1];
100 #[cfg(feature = "dim3")]
101 let ii = [0, 1, 2];
102
103 let toi = ii.map(|i| {
104 if motion2.linvel[i] > 0.0 {
105 let t = (search_domain_aabb.maxs[i] - start_aabb2_1.maxs[i]) / motion2.linvel[i];
106 if t < 0.0 {
107 (Real::max_value(), true)
108 } else {
109 (t, true)
110 }
111 } else if motion2.linvel[i] < 0.0 {
112 let t = (search_domain_aabb.mins[i] - start_aabb2_1.mins[i]) / motion2.linvel[i];
113 if t < 0.0 {
114 (Real::max_value(), false)
115 } else {
116 (t, false)
117 }
118 } else {
119 (Real::max_value(), false)
120 }
121 });
122
123 #[cfg(feature = "dim2")]
124 if toi[0].0 > end_time && toi[1].0 > end_time {
125 break;
126 }
127
128 #[cfg(feature = "dim3")]
129 if toi[0].0 > end_time && toi[1].0 > end_time && toi[2].0 > end_time {
130 break;
131 }
132
133 let imin = Vector::from(toi.map(|t| t.0)).imin();
134
135 if toi[imin].1 {
136 search_domain[0][imin] += 1;
137 search_domain[1][imin] += 1;
138
139 if search_domain[1][imin] <= domain_maxs[imin] {
140 let mut prev_row = search_domain[0];
142 prev_row[imin] = search_domain[1][imin] - 1;
143
144 let range_to_check = [prev_row, search_domain[1]];
145 check_voxels_in_range(range_to_check);
146 } else if search_domain[0][imin] >= domain_maxs[imin] {
147 break;
149 }
150 } else {
151 search_domain[0][imin] -= 1;
152 search_domain[1][imin] -= 1;
153
154 if search_domain[0][imin] >= domain_mins[imin] {
155 let mut next_row = search_domain[1];
157 next_row[imin] = search_domain[0][imin] + 1;
158
159 let range_to_check = [search_domain[0], next_row];
160 check_voxels_in_range(range_to_check);
161 } else if search_domain[1][imin] <= domain_mins[imin] {
162 break;
164 }
165 }
166 }
167
168 hit
169}
170
171pub fn cast_shapes_nonlinear_shape_voxels<D>(
173 dispatcher: &D,
174 motion1: &NonlinearRigidMotion,
175 g1: &dyn Shape,
176 motion2: &NonlinearRigidMotion,
177 g2: &Voxels,
178 start_time: Real,
179 end_time: Real,
180 stop_at_penetration: bool,
181) -> Option<ShapeCastHit>
182where
183 D: ?Sized + QueryDispatcher,
184{
185 cast_shapes_nonlinear_voxels_shape(
186 dispatcher,
187 motion2,
188 g2,
189 motion1,
190 g1,
191 start_time,
192 end_time,
193 stop_at_penetration,
194 )
195 .map(|hit| hit.swapped())
196}