parry3d/query/contact_manifolds/
contact_manifolds_voxels_shape.rs1use crate::bounding_volume::Aabb;
2use crate::math::{IVector, Int, Pose, Real, Vector, DIM};
3use crate::query::{
4 ContactManifold, ContactManifoldsWorkspace, PersistentQueryDispatcher, PointQuery,
5 TypedWorkspaceData, WorkspaceData,
6};
7use crate::shape::{AxisMask, Cuboid, Shape, SupportMap, VoxelData, VoxelType, Voxels};
8use crate::utils::hashmap::{Entry, HashMap};
9use crate::utils::PoseOpt;
10use alloc::{boxed::Box, vec::Vec};
11use num::AsPrimitive;
12
13#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
14#[cfg_attr(
15 feature = "rkyv",
16 derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize)
17)]
18#[derive(Clone)]
19pub(crate) struct VoxelsShapeSubDetector {
20 pub manifold_id: usize,
21 pub selected_contacts: u32,
22 pub timestamp: bool,
23}
24
25#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
26#[derive(Clone, Eq, Hash, PartialEq)]
27pub(crate) struct VoxelsWorkspaceKey<const N: usize> {
28 #[cfg_attr(feature = "serde-serialize", serde(with = "serde_arrays"))]
29 idx: [u32; N],
30}
31
32impl<const N: usize> From<[u32; N]> for VoxelsWorkspaceKey<N> {
33 fn from(idx: [u32; N]) -> Self {
34 Self { idx }
35 }
36}
37
38#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
42#[derive(Clone, Default)]
43pub struct VoxelsShapeContactManifoldsWorkspace<const N: usize> {
44 pub(crate) timestamp: bool,
45 pub(crate) sub_detectors: HashMap<VoxelsWorkspaceKey<N>, VoxelsShapeSubDetector>,
46}
47
48impl<const N: usize> VoxelsShapeContactManifoldsWorkspace<N> {
49 pub fn new() -> Self {
51 Self::default()
52 }
53
54 pub(crate) fn ensure_exists(workspace: &mut Option<ContactManifoldsWorkspace>)
55 where
56 Self: WorkspaceData,
57 {
58 if workspace
59 .as_ref()
60 .and_then(|w| {
61 w.0.downcast_ref::<VoxelsShapeContactManifoldsWorkspace<N>>()
62 })
63 .is_some()
64 {
65 return;
66 }
67
68 *workspace = Some(ContactManifoldsWorkspace(Box::new(
69 VoxelsShapeContactManifoldsWorkspace::new(),
70 )));
71 }
72}
73
74impl WorkspaceData for VoxelsShapeContactManifoldsWorkspace<2> {
75 fn as_typed_workspace_data(&self) -> TypedWorkspaceData<'_> {
76 TypedWorkspaceData::VoxelsShapeContactManifoldsWorkspace(self)
77 }
78
79 fn clone_dyn(&self) -> Box<dyn WorkspaceData> {
80 Box::new(self.clone())
81 }
82}
83
84pub fn contact_manifolds_voxels_shape_shapes<ManifoldData, ContactData>(
86 dispatcher: &dyn PersistentQueryDispatcher<ManifoldData, ContactData>,
87 pos12: &Pose,
88 shape1: &dyn Shape,
89 shape2: &dyn Shape,
90 prediction: Real,
91 manifolds: &mut Vec<ContactManifold<ManifoldData, ContactData>>,
92 workspace: &mut Option<ContactManifoldsWorkspace>,
93) where
94 ManifoldData: Default + Clone,
95 ContactData: Default + Copy,
96{
97 if let Some(voxels1) = shape1.as_voxels() {
98 contact_manifolds_voxels_shape(
99 dispatcher, pos12, voxels1, shape2, prediction, manifolds, workspace, false,
100 );
101 } else if let Some(voxels2) = shape2.as_voxels() {
102 contact_manifolds_voxels_shape(
103 dispatcher,
104 &pos12.inverse(),
105 voxels2,
106 shape1,
107 prediction,
108 manifolds,
109 workspace,
110 true,
111 );
112 }
113}
114
115pub fn contact_manifolds_voxels_shape<ManifoldData, ContactData>(
117 dispatcher: &dyn PersistentQueryDispatcher<ManifoldData, ContactData>,
118 pos12: &Pose,
119 voxels1: &Voxels,
120 shape2: &dyn Shape,
121 prediction: Real,
122 manifolds: &mut Vec<ContactManifold<ManifoldData, ContactData>>,
123 workspace: &mut Option<ContactManifoldsWorkspace>,
124 flipped: bool,
125) where
126 ManifoldData: Default + Clone,
127 ContactData: Default + Copy,
128{
129 VoxelsShapeContactManifoldsWorkspace::<2>::ensure_exists(workspace);
130 let workspace: &mut VoxelsShapeContactManifoldsWorkspace<2> =
131 workspace.as_mut().unwrap().0.downcast_mut().unwrap();
132 let new_timestamp = !workspace.timestamp;
133 workspace.timestamp = new_timestamp;
134
135 let mut old_manifolds = core::mem::take(manifolds);
137
138 let radius1 = voxels1.voxel_size() / 2.0;
139
140 let aabb1 = voxels1.local_aabb();
141 let aabb2_1 = shape2.compute_aabb(pos12);
142 let domain2_1 = Aabb {
143 mins: aabb2_1.mins - radius1 * 10.0,
144 maxs: aabb2_1.maxs + radius1 * 10.0,
145 };
146
147 if let Some(intersection_aabb1) = aabb1.intersection(&aabb2_1) {
148 for vox1 in voxels1.voxels_intersecting_local_aabb(&intersection_aabb1) {
149 let vox_type1 = vox1.state.voxel_type();
150
151 if vox_type1 == VoxelType::Empty || vox_type1 == VoxelType::Interior {
153 continue;
154 }
155
156 let canon1 = CanonicalVoxelShape::from_voxel(voxels1, &vox1);
157
158 let (sub_detector, manifold_updated) =
161 match workspace.sub_detectors.entry(canon1.workspace_key.into()) {
162 Entry::Occupied(entry) => {
163 let sub_detector = entry.into_mut();
164
165 if sub_detector.timestamp != new_timestamp {
166 let manifold = old_manifolds[sub_detector.manifold_id].take();
167 *sub_detector = VoxelsShapeSubDetector {
168 manifold_id: manifolds.len(),
169 timestamp: new_timestamp,
170 selected_contacts: 0,
171 };
172
173 manifolds.push(manifold);
174 (sub_detector, false)
175 } else {
176 (sub_detector, true)
177 }
178 }
179 Entry::Vacant(entry) => {
180 let sub_detector = VoxelsShapeSubDetector {
181 manifold_id: manifolds.len(),
182 selected_contacts: 0,
183 timestamp: new_timestamp,
184 };
185
186 let vid = vox1.linear_id.flat_id() as u32;
187 let (id1, id2) = if flipped { (0, vid) } else { (vid, 0) };
188 manifolds.push(ContactManifold::with_data(
189 id1,
190 id2,
191 ManifoldData::default(),
192 ));
193
194 (entry.insert(sub_detector), false)
195 }
196 };
197
198 let manifold = &mut manifolds[sub_detector.manifold_id];
202
203 if !manifold_updated {
204 let (canonical_center1, canonical_pseudo_cube1) =
205 canon1.cuboid(voxels1, &vox1, domain2_1);
206
207 let canonical_shape1 = &canonical_pseudo_cube1 as &dyn Shape;
208 let canonical_pos12 = Pose::from_translation(-canonical_center1) * pos12;
209
210 let prev_center = if flipped {
217 manifold
218 .subshape_pos2
219 .as_ref()
220 .map(|p| p.translation)
221 .unwrap_or_default()
222 } else {
223 manifold
224 .subshape_pos1
225 .as_ref()
226 .map(|p| p.translation)
227 .unwrap_or_default()
228 };
229 let delta_center = canonical_center1 - prev_center;
230
231 if flipped {
232 for pt in &mut manifold.points {
233 pt.local_p2 -= delta_center;
234 }
235 } else {
236 for pt in &mut manifold.points {
237 pt.local_p1 -= delta_center;
238 }
239 }
240
241 if flipped {
243 manifold.subshape_pos2 = Some(Pose::from_translation(canonical_center1));
244 let _ = dispatcher.contact_manifold_convex_convex(
245 &canonical_pos12.inverse(),
246 shape2,
247 canonical_shape1,
248 None,
249 None,
250 prediction,
251 manifold,
252 );
253 } else {
254 manifold.subshape_pos1 = Some(Pose::from_translation(canonical_center1));
255 let _ = dispatcher.contact_manifold_convex_convex(
256 &canonical_pos12,
257 canonical_shape1,
258 shape2,
259 None,
260 None,
261 prediction,
262 manifold,
263 );
264 }
265 }
266
267 let test_voxel = Cuboid::new(radius1 + Vector::splat(1.0e-2));
271 let penetration_dir1 = if flipped {
272 manifold.local_n2
273 } else {
274 manifold.local_n1
275 };
276
277 for (i, pt) in manifold.points.iter().enumerate() {
278 if pt.dist < 0.0 {
279 let cuboid1 = Cuboid::new(radius1);
283 let sp1 = cuboid1.local_support_point(-penetration_dir1) + vox1.center;
284 let sm2 = shape2
285 .as_support_map()
286 .expect("Unsupported collision pair.");
287 let sp2 = sm2.support_point(pos12, penetration_dir1);
288 let test_dist = (sp2 - sp1).dot(-penetration_dir1);
289 let keep = test_dist < pt.dist;
290
291 if !keep {
292 continue;
295 }
296 }
297
298 let pt_in_voxel_space = if flipped {
299 manifold.subshape_pos2.transform_point(pt.local_p2) - vox1.center
300 } else {
301 manifold.subshape_pos1.transform_point(pt.local_p1) - vox1.center
302 };
303 sub_detector.selected_contacts |=
304 (test_voxel.contains_local_point(pt_in_voxel_space) as u32) << i;
305 }
306 }
307 }
308
309 for sub_detector in workspace.sub_detectors.values() {
311 if sub_detector.timestamp == new_timestamp {
312 let manifold = &mut manifolds[sub_detector.manifold_id];
313 let mut k = 0;
314 manifold.points.retain(|_| {
315 let keep = (sub_detector.selected_contacts & (1 << k)) != 0;
316 k += 1;
317 keep
318 });
319 }
320 }
321
322 workspace
324 .sub_detectors
325 .retain(|_, detector| detector.timestamp == new_timestamp);
326}
327
328#[derive(Copy, Clone, Debug)]
329pub(crate) struct CanonicalVoxelShape {
330 pub range: [IVector; 2],
331 pub workspace_key: [u32; 2],
332}
333
334impl CanonicalVoxelShape {
335 pub fn from_voxel(voxels: &Voxels, vox: &VoxelData) -> Self {
336 let mut key_low = vox.grid_coords;
337 let mut key_high = key_low;
338
339 let mins = voxels.domain()[0] - IVector::splat(1);
343 let maxs = voxels.domain()[1];
344 let counts = maxs - mins;
345 let mask1 = vox.state.free_faces();
346
347 let adjust_canon = |axis: AxisMask, i: usize, key: &mut IVector, val: Int| {
348 if !mask1.contains(axis) {
349 key[i] = val.as_();
350 }
351 };
352
353 adjust_canon(AxisMask::X_POS, 0, &mut key_high, maxs[0]);
354 adjust_canon(AxisMask::X_NEG, 0, &mut key_low, mins[0]);
355 adjust_canon(AxisMask::Y_POS, 1, &mut key_high, maxs[1]);
356 adjust_canon(AxisMask::Y_NEG, 1, &mut key_low, mins[1]);
357
358 #[cfg(feature = "dim3")]
359 {
360 adjust_canon(AxisMask::Z_POS, 2, &mut key_high, maxs[2]);
361 adjust_canon(AxisMask::Z_NEG, 2, &mut key_low, mins[2]);
362 }
363
364 #[cfg(feature = "dim2")]
365 let workspace_id = |vox: IVector| {
366 let local = vox - mins;
367 (local.x + local.y * counts.x) as u32
368 };
369
370 #[cfg(feature = "dim3")]
371 let workspace_id = |vox: IVector| {
372 let local = vox - mins;
373 (local.x + local.y * counts.x + local.z * counts.x * counts.y) as u32
374 };
375
376 Self {
377 range: [key_low, key_high],
378 workspace_key: [workspace_id(key_low), workspace_id(key_high)],
379 }
380 }
381
382 pub fn cuboid(&self, voxels: &Voxels, vox: &VoxelData, domain2_1: Aabb) -> (Vector, Cuboid) {
383 let radius = voxels.voxel_size() / 2.0;
384 let mut canonical_mins = voxels.voxel_center(self.range[0]);
385 let mut canonical_maxs = voxels.voxel_center(self.range[1]);
386
387 for k in 0..DIM {
388 if self.range[0][k] != vox.grid_coords[k] {
389 canonical_mins[k] = canonical_mins[k].max(domain2_1.mins[k]);
390 }
391
392 if self.range[1][k] != vox.grid_coords[k] {
393 canonical_maxs[k] = canonical_maxs[k].min(domain2_1.maxs[k]);
394 }
395 }
396
397 let canonical_half_extents = (canonical_maxs - canonical_mins) / 2.0 + radius;
398 let canonical_center = canonical_mins.midpoint(canonical_maxs);
399 let canonical_cube = Cuboid::new(canonical_half_extents);
400
401 (canonical_center, canonical_cube)
402 }
403}