1use crate::bounding_volume::{Aabb, BoundingVolume};
2use crate::math::{IVector, IVectorExt, Int, Pose, Real, Vector, VectorExt, 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).loosened(prediction);
144 let domain2_1 = Aabb {
145 mins: aabb2_1.mins - radius1 * 10.0,
146 maxs: aabb2_1.maxs + radius1 * 10.0,
147 };
148
149 if let Some(intersection_aabb1) = aabb1.intersection(&aabb2_1) {
150 for vox1 in voxels1.voxels_intersecting_local_aabb(&intersection_aabb1) {
151 let vox_type1 = vox1.state.voxel_type();
152
153 if vox_type1 == VoxelType::Empty || vox_type1 == VoxelType::Interior {
155 continue;
156 }
157
158 let canon1 = CanonicalVoxelShape::from_voxel(voxels1, &vox1);
159
160 let (sub_detector, manifold_updated) =
163 match workspace.sub_detectors.entry(canon1.workspace_key.into()) {
164 Entry::Occupied(entry) => {
165 let sub_detector = entry.into_mut();
166
167 if sub_detector.timestamp != new_timestamp {
168 let manifold = old_manifolds[sub_detector.manifold_id].take();
169 *sub_detector = VoxelsShapeSubDetector {
170 manifold_id: manifolds.len(),
171 timestamp: new_timestamp,
172 selected_contacts: 0,
173 };
174
175 manifolds.push(manifold);
176 (sub_detector, false)
177 } else {
178 (sub_detector, true)
179 }
180 }
181 Entry::Vacant(entry) => {
182 let sub_detector = VoxelsShapeSubDetector {
183 manifold_id: manifolds.len(),
184 selected_contacts: 0,
185 timestamp: new_timestamp,
186 };
187
188 let vid = vox1.linear_id.flat_id() as u32;
189 let (id1, id2) = if flipped { (0, vid) } else { (vid, 0) };
190 manifolds.push(ContactManifold::with_data(
191 id1,
192 id2,
193 ManifoldData::default(),
194 ));
195
196 (entry.insert(sub_detector), false)
197 }
198 };
199
200 let manifold = &mut manifolds[sub_detector.manifold_id];
204
205 if !manifold_updated {
206 let (canonical_center1, canonical_pseudo_cube1) =
207 canon1.cuboid(voxels1, &vox1, domain2_1);
208
209 let canonical_shape1 = &canonical_pseudo_cube1 as &dyn Shape;
210 let canonical_pos12 = Pose::from_translation(-canonical_center1) * pos12;
211
212 let prev_center = if flipped {
219 manifold
220 .subshape_pos2()
221 .as_ref()
222 .map(|p| p.translation)
223 .unwrap_or_default()
224 } else {
225 manifold
226 .subshape_pos1()
227 .as_ref()
228 .map(|p| p.translation)
229 .unwrap_or_default()
230 };
231 let delta_center = canonical_center1 - prev_center;
232
233 if flipped {
234 for pt in &mut manifold.points {
235 pt.local_p2 -= delta_center;
236 }
237 } else {
238 for pt in &mut manifold.points {
239 pt.local_p1 -= delta_center;
240 }
241 }
242
243 if flipped {
245 manifold.set_subshape_pos2(Some(Pose::from_translation(canonical_center1)));
246 let _ = dispatcher.contact_manifold_convex_convex(
247 &canonical_pos12.inverse(),
248 shape2,
249 canonical_shape1,
250 None,
251 None,
252 prediction,
253 manifold,
254 );
255 } else {
256 manifold.set_subshape_pos1(Some(Pose::from_translation(canonical_center1)));
257 let _ = dispatcher.contact_manifold_convex_convex(
258 &canonical_pos12,
259 canonical_shape1,
260 shape2,
261 None,
262 None,
263 prediction,
264 manifold,
265 );
266 }
267 }
268
269 let test_voxel = Cuboid::new(radius1 + Vector::splat(1.0e-2));
273 let penetration_dir1 = if flipped {
274 manifold.local_n2
275 } else {
276 manifold.local_n1
277 };
278
279 for (i, pt) in manifold.points.iter().enumerate() {
280 if pt.dist < 0.0 {
281 let cuboid1 = Cuboid::new(radius1);
285 let sp1 = cuboid1.local_support_point(-penetration_dir1) + vox1.center;
286 let sm2 = shape2
287 .as_support_map()
288 .expect("Unsupported collision pair.");
289 let sp2 = sm2.support_point(pos12, penetration_dir1);
290 let test_dist = (sp2 - sp1).dot(-penetration_dir1);
291 let keep = test_dist < pt.dist;
292
293 if !keep {
294 continue;
297 }
298 }
299
300 let pt_in_voxel_space = if flipped {
301 manifold.subshape_pos2().transform_point(pt.local_p2) - vox1.center
302 } else {
303 manifold.subshape_pos1().transform_point(pt.local_p1) - vox1.center
304 };
305 sub_detector.selected_contacts |=
306 (test_voxel.contains_local_point(pt_in_voxel_space) as u32) << i;
307 }
308 }
309 }
310
311 for sub_detector in workspace.sub_detectors.values() {
313 if sub_detector.timestamp == new_timestamp {
314 let manifold = &mut manifolds[sub_detector.manifold_id];
315 let mut k = 0;
316 manifold.points.retain(|_| {
317 let keep = (sub_detector.selected_contacts & (1 << k)) != 0;
318 k += 1;
319 keep
320 });
321 }
322 }
323
324 workspace
326 .sub_detectors
327 .retain(|_, detector| detector.timestamp == new_timestamp);
328}
329
330#[derive(Copy, Clone, Debug)]
331pub(crate) struct CanonicalVoxelShape {
332 pub range: [IVector; 2],
333 pub workspace_key: [u32; 2],
334}
335
336impl CanonicalVoxelShape {
337 pub fn from_voxel(voxels: &Voxels, vox: &VoxelData) -> Self {
338 let mut key_low = vox.grid_coords;
339 let mut key_high = key_low;
340
341 let mins = voxels.domain()[0] - IVector::splat(1);
345 let maxs = voxels.domain()[1];
346 let counts = maxs - mins;
347 let mask1 = vox.state.free_faces();
348
349 let adjust_canon = |axis: AxisMask, i: usize, key: &mut IVector, val: Int| {
350 if !mask1.contains(axis) {
351 key.ivset(i, val.as_());
352 }
353 };
354
355 adjust_canon(AxisMask::X_POS, 0, &mut key_high, maxs.x);
356 adjust_canon(AxisMask::X_NEG, 0, &mut key_low, mins.x);
357 adjust_canon(AxisMask::Y_POS, 1, &mut key_high, maxs.y);
358 adjust_canon(AxisMask::Y_NEG, 1, &mut key_low, mins.y);
359
360 #[cfg(feature = "dim3")]
361 {
362 adjust_canon(AxisMask::Z_POS, 2, &mut key_high, maxs.z);
363 adjust_canon(AxisMask::Z_NEG, 2, &mut key_low, mins.z);
364 }
365
366 #[cfg(feature = "dim2")]
367 let workspace_id = |vox: IVector| {
368 let local = vox - mins;
369 (local.x + local.y * counts.x) as u32
370 };
371
372 #[cfg(feature = "dim3")]
373 let workspace_id = |vox: IVector| {
374 let local = vox - mins;
375 (local.x + local.y * counts.x + local.z * counts.x * counts.y) as u32
376 };
377
378 Self {
379 range: [key_low, key_high],
380 workspace_key: [workspace_id(key_low), workspace_id(key_high)],
381 }
382 }
383
384 pub fn cuboid(&self, voxels: &Voxels, vox: &VoxelData, domain2_1: Aabb) -> (Vector, Cuboid) {
385 let radius = voxels.voxel_size() / 2.0;
386 let mut canonical_mins = voxels.voxel_center(self.range[0]);
387 let mut canonical_maxs = voxels.voxel_center(self.range[1]);
388
389 for k in 0..DIM {
390 if self.range[0].ivget(k) != vox.grid_coords.ivget(k) {
391 canonical_mins.vset(k, canonical_mins.vget(k).max(domain2_1.mins.vget(k)));
392 }
393
394 if self.range[1].ivget(k) != vox.grid_coords.ivget(k) {
395 canonical_maxs.vset(k, canonical_maxs.vget(k).min(domain2_1.maxs.vget(k)));
396 }
397 }
398
399 let canonical_half_extents = (canonical_maxs - canonical_mins) / 2.0 + radius;
400 let canonical_center = canonical_mins.midpoint(canonical_maxs);
401 let canonical_cube = Cuboid::new(canonical_half_extents);
402
403 (canonical_center, canonical_cube)
404 }
405}