parry3d/query/contact_manifolds/
contact_manifolds_voxels_voxels.rs

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