parry3d/query/contact_manifolds/
contact_manifolds_voxels_voxels.rs

1use crate::bounding_volume::BoundingVolume;
2use crate::math::{Pose, Real, Vector};
3use crate::query::contact_manifolds::{CanonicalVoxelShape, VoxelsShapeContactManifoldsWorkspace};
4use crate::query::details::VoxelsShapeSubDetector;
5use crate::query::{
6    ContactManifold, ContactManifoldsWorkspace, PersistentQueryDispatcher, PointQuery,
7    TypedWorkspaceData, WorkspaceData,
8};
9use crate::shape::{Cuboid, Shape, SupportMap, VoxelData, VoxelType, Voxels};
10use crate::utils::hashmap::Entry;
11use crate::utils::PoseOpt;
12use alloc::{boxed::Box, vec::Vec};
13
14impl WorkspaceData for VoxelsShapeContactManifoldsWorkspace<4> {
15    fn as_typed_workspace_data(&self) -> TypedWorkspaceData<'_> {
16        TypedWorkspaceData::VoxelsVoxelsContactManifoldsWorkspace(self)
17    }
18
19    fn clone_dyn(&self) -> Box<dyn WorkspaceData> {
20        Box::new(self.clone())
21    }
22}
23
24/// Computes the contact manifold between a convex shape and a ball, both represented as a `Shape` trait-object.
25pub fn contact_manifolds_voxels_voxels_shapes<ManifoldData, ContactData>(
26    dispatcher: &dyn PersistentQueryDispatcher<ManifoldData, ContactData>,
27    pos12: &Pose,
28    shape1: &dyn Shape,
29    shape2: &dyn Shape,
30    prediction: Real,
31    manifolds: &mut Vec<ContactManifold<ManifoldData, ContactData>>,
32    workspace: &mut Option<ContactManifoldsWorkspace>,
33) where
34    ManifoldData: Default + Clone,
35    ContactData: Default + Copy,
36{
37    if let (Some(voxels1), Some(voxels2)) = (shape1.as_voxels(), shape2.as_voxels()) {
38        contact_manifolds_voxels_voxels(
39            dispatcher, pos12, voxels1, voxels2, prediction, manifolds, workspace,
40        );
41    }
42}
43
44/// Computes the contact manifold between a convex shape and a ball.
45pub fn contact_manifolds_voxels_voxels<'a, ManifoldData, ContactData>(
46    dispatcher: &dyn PersistentQueryDispatcher<ManifoldData, ContactData>,
47    pos12: &Pose,
48    voxels1: &'a Voxels,
49    voxels2: &'a Voxels,
50    prediction: Real,
51    manifolds: &mut Vec<ContactManifold<ManifoldData, ContactData>>,
52    workspace: &mut Option<ContactManifoldsWorkspace>,
53) where
54    ManifoldData: Default + Clone,
55    ContactData: Default + Copy,
56{
57    VoxelsShapeContactManifoldsWorkspace::<4>::ensure_exists(workspace);
58    let workspace: &mut VoxelsShapeContactManifoldsWorkspace<4> =
59        workspace.as_mut().unwrap().0.downcast_mut().unwrap();
60    let new_timestamp = !workspace.timestamp;
61    workspace.timestamp = new_timestamp;
62
63    // TODO: avoid reallocating the new `manifolds` vec at each step.
64    let mut old_manifolds = core::mem::take(manifolds);
65
66    let radius1 = voxels1.voxel_size() / 2.0;
67    let radius2 = voxels2.voxel_size() / 2.0;
68
69    let aabb1 = voxels1.local_aabb().loosened(prediction / 2.0);
70    let aabb2 = voxels2.local_aabb().loosened(prediction / 2.0);
71
72    let pos21 = pos12.inverse();
73
74    let mut checked_hits = 0;
75
76    if let Some((intersection_aabb1, intersection_aabb2)) =
77        aabb1.aligned_intersections(pos12, &aabb2)
78    {
79        let domain_margin = (radius1 + radius2) * 10.0;
80        let full_domain2_1 = voxels2.compute_aabb(pos12).add_half_extents(domain_margin);
81        let domain2_1 = full_domain2_1
82            .intersection(&aabb1.add_half_extents(domain_margin))
83            .unwrap_or(full_domain2_1);
84        let full_domain1_2 = voxels1.compute_aabb(&pos21).add_half_extents(domain_margin);
85        let domain1_2 = full_domain1_2
86            .intersection(&aabb2.add_half_extents(domain_margin))
87            .unwrap_or(full_domain1_2);
88
89        let mut detect_hit = |canon1: CanonicalVoxelShape,
90                              canon2: CanonicalVoxelShape,
91                              vox1: &VoxelData,
92                              vox2: &VoxelData| {
93            // Compute canonical shapes and dispatch.
94            let workspace_key = [
95                canon1.workspace_key[0],
96                canon1.workspace_key[1],
97                canon2.workspace_key[0],
98                canon2.workspace_key[1],
99            ];
100
101            checked_hits += 1;
102
103            // TODO: could we refactor the workspace system with the Voxels-Shape
104            //       collision detection system?
105            let (sub_detector, manifold_updated) =
106                match workspace.sub_detectors.entry(workspace_key.into()) {
107                    Entry::Occupied(entry) => {
108                        let sub_detector = entry.into_mut();
109
110                        if sub_detector.timestamp != new_timestamp {
111                            let manifold = old_manifolds[sub_detector.manifold_id].take();
112                            *sub_detector = VoxelsShapeSubDetector {
113                                manifold_id: manifolds.len(),
114                                timestamp: new_timestamp,
115                                selected_contacts: 0,
116                            };
117
118                            manifolds.push(manifold);
119                            (sub_detector, false)
120                        } else {
121                            (sub_detector, true)
122                        }
123                    }
124                    Entry::Vacant(entry) => {
125                        let sub_detector = VoxelsShapeSubDetector {
126                            manifold_id: manifolds.len(),
127                            selected_contacts: 0,
128                            timestamp: new_timestamp,
129                        };
130
131                        manifolds.push(ContactManifold::with_data(
132                            vox1.linear_id.flat_id() as u32,
133                            vox2.linear_id.flat_id() as u32,
134                            ManifoldData::default(),
135                        ));
136
137                        (entry.insert(sub_detector), false)
138                    }
139                };
140
141            /*
142             * Update the contact manifold if needed.
143             */
144            let manifold = &mut manifolds[sub_detector.manifold_id];
145            let (canonical_center1, canonical_pseudo_cube1) =
146                canon1.cuboid(voxels1, vox1, domain2_1);
147            let (canonical_center2, canonical_pseudo_cube2) =
148                canon2.cuboid(voxels2, vox2, domain1_2);
149
150            if !manifold_updated {
151                let canonical_pos12 = Pose::from_translation(-canonical_center1)
152                    * pos12
153                    * Pose::from_translation(canonical_center2);
154
155                // If we already computed contacts in the previous simulation step, their
156                // local points are relative to the previously calculated canonical shape
157                // which might not have the same local center as the one computed in this
158                // step (because it’s based on the position of shape2 relative to voxels1).
159                // So we need to adjust the local points to account for the position difference
160                // and keep the point at the same "canonical-shape-space" location as in the previous frame.
161                let prev_center1 = manifold
162                    .subshape_pos1
163                    .as_ref()
164                    .map(|p| p.translation)
165                    .unwrap_or_default();
166                let delta_center1 = canonical_center1 - prev_center1;
167                let prev_center2 = manifold
168                    .subshape_pos2
169                    .as_ref()
170                    .map(|p| p.translation)
171                    .unwrap_or_default();
172                let delta_center2 = canonical_center2 - prev_center2;
173
174                for pt in &mut manifold.points {
175                    pt.local_p1 -= delta_center1;
176                    pt.local_p2 -= delta_center2;
177                }
178
179                // Update contacts.
180                manifold.subshape_pos1 = Some(Pose::from_translation(canonical_center1));
181                manifold.subshape_pos2 = Some(Pose::from_translation(canonical_center2));
182                let _ = dispatcher.contact_manifold_convex_convex(
183                    &canonical_pos12,
184                    &canonical_pseudo_cube1,
185                    &canonical_pseudo_cube2,
186                    None,
187                    None,
188                    prediction,
189                    manifold,
190                );
191            }
192
193            /*
194             * Filter-out points that don’t belong to this block.
195             */
196            let test_voxel1 = Cuboid::new(radius1 + Vector::splat(1.0e-2));
197            let test_voxel2 = Cuboid::new(radius2 + Vector::splat(1.0e-2));
198            let penetration_dir1 = manifold.local_n1;
199
200            for (i, pt) in manifold.points.iter().enumerate() {
201                if pt.dist < 0.0 {
202                    // If this is a penetration, double-check that we are not hitting the
203                    // interior of the infinitely expanded canonical shape by checking if
204                    // the opposite normal had led to a better vector.
205                    let cuboid1 = Cuboid::new(radius1);
206                    let cuboid2 = Cuboid::new(radius2);
207                    let sp1 = cuboid1.local_support_point(-penetration_dir1) + vox1.center;
208                    let sp2 = cuboid2.support_point(
209                        &(pos12 * Pose::from_translation(vox2.center)),
210                        penetration_dir1,
211                    );
212                    let test_dist = (sp2 - sp1).dot(-penetration_dir1);
213                    let keep = test_dist < pt.dist;
214
215                    if !keep {
216                        // We don’t want to keep this point as it is an incorrect penetration
217                        // caused by the canonical shape expansion.
218                        continue;
219                    }
220                }
221
222                let pt_in_voxel_space1 =
223                    manifold.subshape_pos1.transform_point(pt.local_p1) - vox1.center;
224                let pt_in_voxel_space2 =
225                    manifold.subshape_pos2.transform_point(pt.local_p2) - vox2.center;
226                sub_detector.selected_contacts |=
227                    ((test_voxel1.contains_local_point(pt_in_voxel_space1) as u32) << i)
228                        & ((test_voxel2.contains_local_point(pt_in_voxel_space2) as u32) << i);
229            }
230        };
231
232        for vox1 in voxels1.voxels_intersecting_local_aabb(&intersection_aabb1) {
233            let type1 = vox1.state.voxel_type();
234            match type1 {
235                #[cfg(feature = "dim2")]
236                VoxelType::Vertex => { /* Ok */ }
237                #[cfg(feature = "dim3")]
238                VoxelType::Vertex | VoxelType::Edge => { /* Ok */ }
239                _ => continue,
240            }
241
242            let canon1 = CanonicalVoxelShape::from_voxel(voxels1, &vox1);
243            let centered_aabb1_2 = Cuboid::new(radius1 + Vector::splat(prediction))
244                .compute_aabb(&(pos21 * Pose::from_translation(vox1.center)));
245
246            for vox2 in voxels2.voxels_intersecting_local_aabb(&centered_aabb1_2) {
247                let type2 = vox2.state.voxel_type();
248
249                #[cfg(feature = "dim2")]
250                match (type1, type2) {
251                    (VoxelType::Vertex, VoxelType::Vertex)
252                    | (VoxelType::Vertex, VoxelType::Face)
253                    | (VoxelType::Face, VoxelType::Vertex) => { /* OK */ }
254                    _ => continue, /* Ignore */
255                }
256
257                #[cfg(feature = "dim3")]
258                match (type1, type2) {
259                    (VoxelType::Vertex, VoxelType::Vertex)
260                    | (VoxelType::Vertex, VoxelType::Edge)
261                    | (VoxelType::Vertex, VoxelType::Face)
262                    | (VoxelType::Edge, VoxelType::Vertex)
263                    | (VoxelType::Edge, VoxelType::Edge)
264                    | (VoxelType::Face, VoxelType::Vertex) => { /* OK */ }
265                    _ => continue, /* Ignore */
266                }
267
268                let canon2 = CanonicalVoxelShape::from_voxel(voxels2, &vox2);
269                detect_hit(canon1, canon2, &vox1, &vox2);
270            }
271        }
272
273        for vox2 in voxels2.voxels_intersecting_local_aabb(&intersection_aabb2) {
274            let type2 = vox2.state.voxel_type();
275            match type2 {
276                #[cfg(feature = "dim2")]
277                VoxelType::Vertex => { /* Ok */ }
278                #[cfg(feature = "dim3")]
279                VoxelType::Vertex | VoxelType::Edge => { /* Ok */ }
280                _ => continue,
281            }
282
283            let canon2 = CanonicalVoxelShape::from_voxel(voxels2, &vox2);
284            let centered_aabb2_1 = Cuboid::new(radius2 + Vector::splat(prediction))
285                .compute_aabb(&(pos12 * Pose::from_translation(vox2.center)));
286
287            for vox1 in voxels1.voxels_intersecting_local_aabb(&centered_aabb2_1) {
288                let type1 = vox1.state.voxel_type();
289
290                #[cfg(feature = "dim2")]
291                match (type1, type2) {
292                    (VoxelType::Vertex, VoxelType::Vertex)
293                    | (VoxelType::Vertex, VoxelType::Face)
294                    | (VoxelType::Face, VoxelType::Vertex) => { /* OK */ }
295                    _ => continue, /* Ignore */
296                }
297
298                #[cfg(feature = "dim3")]
299                match (type1, type2) {
300                    (VoxelType::Vertex, VoxelType::Vertex)
301                    | (VoxelType::Vertex, VoxelType::Edge)
302                    | (VoxelType::Vertex, VoxelType::Face)
303                    | (VoxelType::Edge, VoxelType::Vertex)
304                    | (VoxelType::Edge, VoxelType::Edge)
305                    | (VoxelType::Face, VoxelType::Vertex) => { /* OK */ }
306                    _ => continue, /* Ignore */
307                }
308
309                let canon1 = CanonicalVoxelShape::from_voxel(voxels1, &vox1);
310                detect_hit(canon1, canon2, &vox1, &vox2);
311            }
312        }
313
314        // Remove contacts marked as ignored.
315        for sub_detector in workspace.sub_detectors.values() {
316            if sub_detector.timestamp == new_timestamp {
317                let manifold = &mut manifolds[sub_detector.manifold_id];
318                let mut k = 0;
319                manifold.points.retain(|_| {
320                    let keep = (sub_detector.selected_contacts & (1 << k)) != 0;
321                    k += 1;
322                    keep
323                });
324            }
325        }
326    }
327
328    // Remove detectors which no longer index a valid manifold.
329    workspace
330        .sub_detectors
331        .retain(|_, detector| detector.timestamp == new_timestamp);
332}