parry2d/query/nonlinear_shape_cast/
nonlinear_shape_cast_voxels_shape.rs1use crate::bounding_volume::BoundingVolume;
2use crate::math::{IVector, IVectorExt, Real, Vector, VectorExt};
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: [IVector; 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);
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.vget(i) > 0.0 {
105 let t = (search_domain_aabb.maxs.vget(i) - start_aabb2_1.maxs.vget(i))
106 / motion2.linvel.vget(i);
107 if t < 0.0 {
108 (Real::max_value(), true)
109 } else {
110 (t, true)
111 }
112 } else if motion2.linvel.vget(i) < 0.0 {
113 let t = (search_domain_aabb.mins.vget(i) - start_aabb2_1.mins.vget(i))
114 / motion2.linvel.vget(i);
115 if t < 0.0 {
116 (Real::max_value(), false)
117 } else {
118 (t, false)
119 }
120 } else {
121 (Real::max_value(), false)
122 }
123 });
124
125 #[cfg(feature = "dim2")]
126 if toi[0].0 > end_time && toi[1].0 > end_time {
127 break;
128 }
129
130 #[cfg(feature = "dim3")]
131 if toi[0].0 > end_time && toi[1].0 > end_time && toi[2].0 > end_time {
132 break;
133 }
134
135 let imin = Vector::from(toi.map(|t| t.0)).min_position();
136
137 if toi[imin].1 {
138 search_domain[0].ivset(imin, search_domain[0].ivget(imin) + 1);
139 search_domain[1].ivset(imin, search_domain[1].ivget(imin) + 1);
140
141 if search_domain[1].ivget(imin) <= domain_maxs.ivget(imin) {
142 let mut prev_row = search_domain[0];
144 prev_row.ivset(imin, search_domain[1].ivget(imin) - 1);
145
146 let range_to_check = [prev_row, search_domain[1]];
147 check_voxels_in_range(range_to_check);
148 } else if search_domain[0].ivget(imin) >= domain_maxs.ivget(imin) {
149 break;
151 }
152 } else {
153 search_domain[0].ivset(imin, search_domain[0].ivget(imin) - 1);
154 search_domain[1].ivset(imin, search_domain[1].ivget(imin) - 1);
155
156 if search_domain[0].ivget(imin) >= domain_mins.ivget(imin) {
157 let mut next_row = search_domain[1];
159 next_row.ivset(imin, search_domain[0].ivget(imin) + 1);
160
161 let range_to_check = [search_domain[0], next_row];
162 check_voxels_in_range(range_to_check);
163 } else if search_domain[1].ivget(imin) <= domain_mins.ivget(imin) {
164 break;
166 }
167 }
168 }
169
170 hit
171}
172
173pub fn cast_shapes_nonlinear_shape_voxels<D>(
175 dispatcher: &D,
176 motion1: &NonlinearRigidMotion,
177 g1: &dyn Shape,
178 motion2: &NonlinearRigidMotion,
179 g2: &Voxels,
180 start_time: Real,
181 end_time: Real,
182 stop_at_penetration: bool,
183) -> Option<ShapeCastHit>
184where
185 D: ?Sized + QueryDispatcher,
186{
187 cast_shapes_nonlinear_voxels_shape(
188 dispatcher,
189 motion2,
190 g2,
191 motion1,
192 g1,
193 start_time,
194 end_time,
195 stop_at_penetration,
196 )
197 .map(|hit| hit.swapped())
198}