parry3d/query/contact_manifolds/
contact_manifolds_voxels_composite_shape.rs1use crate::bounding_volume::Aabb;
2use crate::math::{Pose, Real, Vector};
3use crate::query::details::{
4 CanonicalVoxelShape, VoxelsShapeContactManifoldsWorkspace, VoxelsShapeSubDetector,
5};
6use crate::query::{
7 ContactManifold, ContactManifoldsWorkspace, PersistentQueryDispatcher, PointQuery,
8 TypedWorkspaceData, WorkspaceData,
9};
10use crate::shape::{CompositeShape, Cuboid, Shape, SupportMap, VoxelType, Voxels};
11use crate::utils::hashmap::Entry;
12use crate::utils::PoseOpt;
13use alloc::{boxed::Box, vec::Vec};
14
15impl WorkspaceData for VoxelsShapeContactManifoldsWorkspace<3> {
16 fn as_typed_workspace_data(&self) -> TypedWorkspaceData<'_> {
17 TypedWorkspaceData::VoxelsCompositeShapeContactManifoldsWorkspace(self)
18 }
19
20 fn clone_dyn(&self) -> Box<dyn WorkspaceData> {
21 Box::new(self.clone())
22 }
23}
24
25pub fn contact_manifolds_voxels_composite_shape_shapes<ManifoldData, ContactData>(
27 dispatcher: &dyn PersistentQueryDispatcher<ManifoldData, ContactData>,
28 pos12: &Pose,
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(shape2)) = (shape1.as_voxels(), shape2.as_composite_shape()) {
39 contact_manifolds_voxels_composite_shape(
40 dispatcher, pos12, voxels1, shape2, prediction, manifolds, workspace, false,
41 );
42 } else if let (Some(shape1), Some(voxels2)) = (shape1.as_composite_shape(), shape2.as_voxels())
43 {
44 contact_manifolds_voxels_composite_shape(
45 dispatcher,
46 &pos12.inverse(),
47 voxels2,
48 shape1,
49 prediction,
50 manifolds,
51 workspace,
52 true,
53 );
54 }
55}
56
57pub fn contact_manifolds_voxels_composite_shape<ManifoldData, ContactData>(
59 dispatcher: &dyn PersistentQueryDispatcher<ManifoldData, ContactData>,
60 pos12: &Pose,
61 voxels1: &Voxels,
62 shape2: &dyn CompositeShape,
63 prediction: Real,
64 manifolds: &mut Vec<ContactManifold<ManifoldData, ContactData>>,
65 workspace: &mut Option<ContactManifoldsWorkspace>,
66 flipped: bool,
67) where
68 ManifoldData: Default + Clone,
69 ContactData: Default + Copy,
70{
71 VoxelsShapeContactManifoldsWorkspace::<3>::ensure_exists(workspace);
72 let workspace: &mut VoxelsShapeContactManifoldsWorkspace<3> =
73 workspace.as_mut().unwrap().0.downcast_mut().unwrap();
74 let new_timestamp = !workspace.timestamp;
75 workspace.timestamp = new_timestamp;
76
77 let mut old_manifolds = core::mem::take(manifolds);
79 let bvh2 = shape2.bvh();
80
81 let radius1 = voxels1.voxel_size() / 2.0;
82
83 let aabb1 = voxels1.local_aabb();
84 let aabb2_1 = shape2.bvh().root_aabb().transform_by(pos12);
85 let domain2_1 = Aabb {
86 mins: aabb2_1.mins - radius1 * 10.0,
87 maxs: aabb2_1.maxs + radius1 * 10.0,
88 };
89
90 if let Some(intersection_aabb1) = aabb1.intersection(&aabb2_1) {
91 for vox1 in voxels1.voxels_intersecting_local_aabb(&intersection_aabb1) {
92 let vox_type1 = vox1.state.voxel_type();
93
94 if vox_type1 == VoxelType::Empty || vox_type1 == VoxelType::Interior {
96 continue;
97 }
98
99 let canon1 = CanonicalVoxelShape::from_voxel(voxels1, &vox1);
104 let (canonical_center1, canonical_shape1) = canon1.cuboid(voxels1, &vox1, domain2_1);
105 let canonical_pose12 = Pose::from_translation(-canonical_center1) * pos12;
106
107 let mut detect_hit = |leaf2: u32| {
108 let key = [canon1.workspace_key[0], canon1.workspace_key[1], leaf2];
109
110 let (sub_detector, manifold_updated) =
113 match workspace.sub_detectors.entry(key.into()) {
114 Entry::Occupied(entry) => {
115 let sub_detector = entry.into_mut();
116
117 if sub_detector.timestamp != new_timestamp {
118 let manifold = old_manifolds[sub_detector.manifold_id].take();
119 *sub_detector = VoxelsShapeSubDetector {
120 manifold_id: manifolds.len(),
121 timestamp: new_timestamp,
122 selected_contacts: 0,
123 };
124
125 manifolds.push(manifold);
126 (sub_detector, false)
127 } else {
128 (sub_detector, true)
129 }
130 }
131 Entry::Vacant(entry) => {
132 let sub_detector = VoxelsShapeSubDetector {
133 manifold_id: manifolds.len(),
134 selected_contacts: 0,
135 timestamp: new_timestamp,
136 };
137
138 let vox_id = vox1.linear_id.flat_id() as u32;
139 let (id1, id2) = if flipped {
140 (leaf2, vox_id)
141 } else {
142 (vox_id, leaf2)
143 };
144 manifolds.push(ContactManifold::with_data(
145 id1,
146 id2,
147 ManifoldData::default(),
148 ));
149
150 (entry.insert(sub_detector), false)
151 }
152 };
153
154 let manifold = &mut manifolds[sub_detector.manifold_id];
158
159 shape2.map_part_at(leaf2, &mut |part_pos2, part_shape2, _| {
160 let relative_pos12 = part_pos2.prepend_to(&canonical_pose12);
161
162 if !manifold_updated {
163 let prev_center = if flipped {
170 manifold
171 .subshape_pos2
172 .as_ref()
173 .map(|p| p.translation)
174 .unwrap_or_default()
175 } else {
176 manifold
177 .subshape_pos1
178 .as_ref()
179 .map(|p| p.translation)
180 .unwrap_or_default()
181 };
182 let delta_center = canonical_center1 - prev_center;
183
184 if flipped {
185 for pt in &mut manifold.points {
186 pt.local_p2 -= delta_center;
187 }
188 } else {
189 for pt in &mut manifold.points {
190 pt.local_p1 -= delta_center;
191 }
192 }
193
194 if flipped {
196 manifold.subshape_pos1 = part_pos2.copied();
197 manifold.subshape_pos2 =
198 Some(Pose::from_translation(canonical_center1));
199 let _ = dispatcher.contact_manifold_convex_convex(
200 &relative_pos12.inverse(),
201 part_shape2,
202 &canonical_shape1,
203 None,
204 None,
205 prediction,
206 manifold,
207 );
208 } else {
209 manifold.subshape_pos1 =
210 Some(Pose::from_translation(canonical_center1));
211 manifold.subshape_pos2 = part_pos2.copied();
212 let _ = dispatcher.contact_manifold_convex_convex(
213 &relative_pos12,
214 &canonical_shape1,
215 part_shape2,
216 None,
217 None,
218 prediction,
219 manifold,
220 );
221 }
222 }
223
224 let test_voxel = Cuboid::new(radius1 + Vector::splat(1.0e-2));
228 let penetration_dir1 = if flipped {
229 manifold.local_n2
230 } else {
231 manifold.local_n1
232 };
233
234 for (i, pt) in manifold.points.iter().enumerate() {
235 if pt.dist < 0.0 {
236 let cuboid1 = Cuboid::new(radius1);
240 let sp1 = cuboid1.local_support_point(-penetration_dir1) + vox1.center;
241 let sm2 = part_shape2
242 .as_support_map()
243 .expect("Unsupported collision pair.");
244 let sp2 = sm2.support_point(&relative_pos12, penetration_dir1)
245 + canonical_center1;
246 let test_dist = (sp2 - sp1).dot(-penetration_dir1);
247 let keep = test_dist < pt.dist;
248
249 if !keep {
250 continue;
253 }
254 }
255
256 let pt_in_voxel_space = if flipped {
257 manifold.subshape_pos2.transform_point(pt.local_p2) - vox1.center
258 } else {
259 manifold.subshape_pos1.transform_point(pt.local_p1) - vox1.center
260 };
261 sub_detector.selected_contacts |=
262 (test_voxel.contains_local_point(pt_in_voxel_space) as u32) << i;
263 }
264 });
265 };
266
267 let canon_aabb1_2 = canonical_shape1.compute_aabb(&canonical_pose12.inverse());
268
269 for leaf_id in bvh2.intersect_aabb(&canon_aabb1_2) {
270 detect_hit(leaf_id);
271 }
272 }
273 }
274
275 for sub_detector in workspace.sub_detectors.values() {
277 if sub_detector.timestamp == new_timestamp {
278 let manifold = &mut manifolds[sub_detector.manifold_id];
279 let mut k = 0;
280 manifold.points.retain(|_| {
281 let keep = (sub_detector.selected_contacts & (1 << k)) != 0;
282 k += 1;
283 keep
284 });
285 }
286 }
287
288 workspace
290 .sub_detectors
291 .retain(|_, detector| detector.timestamp == new_timestamp);
292}