ncollide3d/pipeline/narrow_phase/contact_generator/
trimesh_trimesh_manifold_generator.rs

1use crate::math::{Isometry, Vector};
2use crate::pipeline::narrow_phase::{ContactDispatcher, ContactManifoldGenerator};
3use crate::query::{
4    self, visitors::AABBSetsInterferencesCollector, Contact, ContactKinematic, ContactManifold,
5    ContactPrediction, ContactPreprocessor, ContactTrackingMode, NeighborhoodGeometry,
6};
7use crate::shape::{
8    ClippingCache, CompositeShape, ConvexPolygonalFeature, FeatureId, Segment,
9    SegmentPointLocation, Shape, TriMesh, Triangle,
10};
11use na::{self, RealField, Unit};
12use std::mem;
13
14/// Collision detector between a concave shape and another shape.
15pub struct TriMeshTriMeshManifoldGenerator<N: RealField + Copy> {
16    clip_cache: ClippingCache<N>,
17    new_contacts: Vec<(Contact<N>, FeatureId, FeatureId)>,
18    convex_feature1: ConvexPolygonalFeature<N>,
19    convex_feature2: ConvexPolygonalFeature<N>,
20    interferences: Vec<(usize, usize)>,
21}
22
23impl<N: RealField + Copy> TriMeshTriMeshManifoldGenerator<N> {
24    /// Creates a new collision detector between a concave shape and another shape.
25    pub fn new() -> TriMeshTriMeshManifoldGenerator<N> {
26        TriMeshTriMeshManifoldGenerator {
27            clip_cache: ClippingCache::new(),
28            new_contacts: Vec::new(),
29            convex_feature1: ConvexPolygonalFeature::with_size(3),
30            convex_feature2: ConvexPolygonalFeature::with_size(3),
31            interferences: Vec::new(),
32        }
33    }
34}
35
36impl<N: RealField + Copy> TriMeshTriMeshManifoldGenerator<N> {
37    fn compute_faces_closest_points(
38        &mut self,
39        m12: &Isometry<N>,
40        m21: &Isometry<N>,
41        m1: &Isometry<N>,
42        mesh1: &TriMesh<N>,
43        i1: usize,
44        proc1: Option<&dyn ContactPreprocessor<N>>,
45        m2: &Isometry<N>,
46        mesh2: &TriMesh<N>,
47        i2: usize,
48        proc2: Option<&dyn ContactPreprocessor<N>>,
49        prediction: &ContactPrediction<N>,
50        manifold: &mut ContactManifold<N>,
51    ) {
52        let face1 = &mesh1.faces()[i1];
53        let face2 = &mesh2.faces()[i2];
54
55        let pts1 = mesh1.points();
56        let pts2 = mesh2.points();
57        let t1 = Triangle::new(
58            pts1[face1.indices.x],
59            pts1[face1.indices.y],
60            pts1[face1.indices.z],
61        );
62        let t2 = Triangle::new(
63            m12 * pts2[face2.indices.x],
64            m12 * pts2[face2.indices.y],
65            m12 * pts2[face2.indices.z],
66        );
67
68        if let (Some(n1), Some(n2)) = (face1.normal, face2.normal) {
69            let n2 = m12 * n2;
70
71            /*
72             * Start with the SAT.
73             */
74            #[inline(always)]
75            fn penetration<N: RealField + Copy>(a: (N, N), b: (N, N)) -> Option<(N, bool)> {
76                assert!(a.0 <= a.1 && b.0 <= b.1);
77                if a.0 > b.1 || b.0 > a.1 {
78                    // The intervals are disjoint.
79                    None
80                } else {
81                    let depth1 = b.1 - a.0;
82                    let depth2 = a.1 - b.0;
83
84                    if depth1 < depth2 {
85                        Some((depth1, true))
86                    } else {
87                        Some((depth2, false))
88                    }
89                }
90            }
91
92            #[inline(always)]
93            fn sort2<N: RealField + Copy>(a: N, b: N) -> (N, N) {
94                if a > b {
95                    (b, a)
96                } else {
97                    (a, b)
98                }
99            }
100
101            // This loop is a trick to be able to easily stop the search for a separating axis as
102            // as we find one using `break 'search` (without having to do all this on a separate function
103            // and do a return instead of breaks).
104            'search: loop {
105                let _big: N = na::convert(10000000.0);
106                let mut penetration_depth = (N::max_value().unwrap(), false);
107                let mut penetration_dir = Vector::y_axis();
108
109                // First, test normals.
110                let proj1 = t1.a.coords.dot(&n1);
111                let mut interval1 = (proj1, proj1);
112                let interval2 = t2.extents_on_dir(&n1);
113
114                if mesh1.oriented() {
115                    interval1.0 = -_big;
116                }
117
118                if let Some(overlap) = penetration(interval1, interval2) {
119                    if overlap.0 < penetration_depth.0 {
120                        penetration_depth = overlap;
121                        penetration_dir = n1;
122                    }
123                } else {
124                    // The triangles are disjoint.
125                    break;
126                }
127
128                let proj2 = t2.a.coords.dot(&n2);
129                let mut interval2 = (proj2, proj2);
130                let interval1 = t1.extents_on_dir(&n2);
131
132                if mesh2.oriented() {
133                    interval2.0 = -_big;
134                }
135
136                if let Some(overlap) = penetration(interval1, interval2) {
137                    if overlap.0 < penetration_depth.0 {
138                        penetration_depth = overlap;
139                        penetration_dir = n2;
140                    }
141                } else {
142                    // The triangles are disjoint.
143                    break;
144                }
145
146                let edge_dirs_a = t1.edges_scaled_directions();
147                let edge_dirs_b = t2.edges_scaled_directions();
148
149                // Second, test edges cross products.
150                for (i, e1) in edge_dirs_a.iter().enumerate() {
151                    for (j, e2) in edge_dirs_b.iter().enumerate() {
152                        if let Some(dir) = Unit::try_new(e1.cross(e2), N::default_epsilon()) {
153                            let mut interval1 = sort2(
154                                dir.dot(&t1.vertices()[i].coords),
155                                dir.dot(&t1.vertices()[(i + 2) % 3].coords),
156                            );
157                            let mut interval2 = sort2(
158                                dir.dot(&t2.vertices()[j].coords),
159                                dir.dot(&t2.vertices()[(j + 2) % 3].coords),
160                            );
161
162                            let eid1 = face1.edges[i];
163                            let eid2 = face2.edges[j];
164
165                            if mesh1.oriented() {
166                                if mesh1.edge_tangent_cone_contains_dir(eid1, None, &dir) {
167                                    interval1.0 = -_big;
168                                } else if mesh1.edge_tangent_cone_contains_dir(eid1, None, &-dir) {
169                                    interval1.1 = _big;
170                                }
171                            }
172
173                            if mesh2.oriented() {
174                                if mesh2.edge_tangent_cone_contains_dir(eid2, None, &(m21 * dir)) {
175                                    interval2.0 = -_big;
176                                } else if mesh2.edge_tangent_cone_contains_dir(
177                                    eid2,
178                                    None,
179                                    &-(m21 * dir),
180                                ) {
181                                    interval2.1 = _big;
182                                }
183                            }
184
185                            if let Some(overlap) = penetration(interval1, interval2) {
186                                if overlap.0 < penetration_depth.0 {
187                                    penetration_depth = overlap;
188                                    penetration_dir = dir;
189                                }
190                            } else {
191                                // Triangles are disjoint.
192                                break 'search;
193                            }
194                        }
195                    }
196                }
197
198                // If we reached this point, no separating axis was found: the triangles intersect.
199                if let (Some(side_normals1), Some(side_normals2)) =
200                    (face1.side_normals.as_ref(), face2.side_normals.as_ref())
201                {
202                    for i in 0..3 {
203                        self.convex_feature1.vertices[i] = m1 * t1.vertices()[i];
204                        self.convex_feature1.edge_normals[i] = m1 * *side_normals1[i];
205                        self.convex_feature1.vertices_id[i] = FeatureId::Vertex(face1.indices[i]);
206                        self.convex_feature1.edges_id[i] = FeatureId::Edge(face1.edges[i]);
207
208                        self.convex_feature2.vertices[i] = m1 * t2.vertices()[i]; // m1 because t1 is in the local-space of the first geometry.
209                        self.convex_feature2.edge_normals[i] = m2 * *side_normals2[i];
210                        self.convex_feature2.vertices_id[i] = FeatureId::Vertex(face2.indices[i]);
211                        self.convex_feature2.edges_id[i] = FeatureId::Edge(face2.edges[i]);
212                    }
213
214                    let normal = if !penetration_depth.1 {
215                        m1 * penetration_dir
216                    } else {
217                        m1 * -penetration_dir
218                    };
219
220                    self.convex_feature1.normal = face1.normal.map(|n| m1 * n);
221                    self.convex_feature1.feature_id = FeatureId::Face(i1);
222
223                    // XXX: do we have to swap the vertices and edge normals too?
224                    if let Some(normal_f1) = self.convex_feature1.normal.as_mut() {
225                        if normal_f1.dot(&normal) < N::zero() {
226                            *normal_f1 = -*normal_f1;
227                            self.convex_feature1.feature_id =
228                                FeatureId::Face(i1 + mesh1.faces().len());
229                            self.convex_feature1.vertices.swap(0, 1);
230                            self.convex_feature1.edge_normals.swap(1, 2);
231                            self.convex_feature1.vertices_id.swap(0, 1);
232                            self.convex_feature1.edges_id.swap(1, 2);
233                        }
234                    }
235
236                    self.convex_feature2.normal = face2.normal.map(|n| m2 * n);
237                    self.convex_feature2.feature_id = FeatureId::Face(i2);
238
239                    if let Some(normal_f2) = self.convex_feature2.normal.as_mut() {
240                        if -normal_f2.dot(&normal) < N::zero() {
241                            *normal_f2 = -*normal_f2;
242                            self.convex_feature2.feature_id =
243                                FeatureId::Face(i2 + mesh2.faces().len());
244                            self.convex_feature2.vertices.swap(0, 1);
245                            self.convex_feature2.edge_normals.swap(1, 2);
246                            self.convex_feature2.vertices_id.swap(0, 1);
247                            self.convex_feature2.edges_id.swap(1, 2);
248                        }
249                    }
250
251                    self.convex_feature1.clip(
252                        &self.convex_feature2,
253                        &normal,
254                        prediction,
255                        &mut self.clip_cache,
256                        &mut self.new_contacts,
257                    );
258
259                    for (c, f1, f2) in self.new_contacts.drain(..) {
260                        self.convex_feature1.add_contact_to_manifold(
261                            &self.convex_feature2,
262                            c,
263                            m1,
264                            f1,
265                            None,
266                            m2,
267                            f2,
268                            None,
269                            manifold,
270                        );
271                    }
272                }
273
274                return;
275            }
276
277            /*
278             * The two triangles don't intersect.
279             * Compute all the LMDs considering the given linear and angular tolerances.
280             */
281            for i in 0..3 {
282                let id_e1 = face1.edges[i];
283                let e1 = &mesh1.edges()[id_e1];
284                let seg1 = Segment::new(pts1[e1.indices.x], pts1[e1.indices.y]);
285
286                for j in 0..3 {
287                    let id_e2 = face2.edges[j];
288                    let e2 = &mesh2.edges()[id_e2];
289                    // FIXME: don't transform the points at each loop.
290                    // Use the corresponding edge from t2 instead.
291                    let seg2 = Segment::new(m12 * pts2[e2.indices.x], m12 * pts2[e2.indices.y]);
292
293                    let locs = query::closest_points_segment_segment_with_locations_nD(
294                        (&seg1.a, &seg1.b),
295                        (&seg2.a, &seg2.b),
296                    );
297                    let p1 = seg1.point_at(&locs.0);
298                    let p2 = seg2.point_at(&locs.1);
299                    if let Some(dir) = Unit::try_new(p2 - p1, N::default_epsilon()) {
300                        match locs {
301                            (
302                                SegmentPointLocation::OnVertex(i),
303                                SegmentPointLocation::OnVertex(j),
304                            ) => {
305                                let ip1 = e1.indices[i];
306                                let ip2 = e2.indices[j];
307                                if mesh1.vertex_tangent_cone_polar_contains_dir(
308                                    ip1,
309                                    &dir,
310                                    prediction.sin_angular1(),
311                                ) && mesh2.vertex_tangent_cone_polar_contains_dir(
312                                    ip2,
313                                    &(m21 * -dir),
314                                    prediction.sin_angular2(),
315                                ) {
316                                    // Accept the contact.
317                                    let contact = Contact::new_wo_depth(m1 * p1, m1 * p2, m1 * dir);
318                                    let mut kinematic = ContactKinematic::new();
319                                    kinematic.set_approx1(
320                                        FeatureId::Vertex(ip1),
321                                        pts1[ip1],
322                                        NeighborhoodGeometry::Point,
323                                    );
324                                    kinematic.set_approx2(
325                                        FeatureId::Vertex(ip2),
326                                        pts2[ip2],
327                                        NeighborhoodGeometry::Point,
328                                    );
329                                    let _ = manifold.push(contact, kinematic, p1, proc1, proc2);
330                                }
331                            }
332                            (
333                                SegmentPointLocation::OnVertex(i),
334                                SegmentPointLocation::OnEdge(_),
335                            ) => {
336                                let ip1 = e1.indices[i];
337                                if mesh1.vertex_tangent_cone_polar_contains_dir(
338                                    ip1,
339                                    &dir,
340                                    prediction.sin_angular1(),
341                                ) && mesh2.edge_tangent_cone_polar_contains_orthogonal_dir(
342                                    id_e2,
343                                    &(m21 * -dir),
344                                    prediction.sin_angular2(),
345                                ) {
346                                    // Accept the contact.
347                                    let contact = Contact::new_wo_depth(m1 * p1, m1 * p2, m1 * dir);
348                                    let mut kinematic = ContactKinematic::new();
349                                    kinematic.set_approx1(
350                                        FeatureId::Vertex(ip1),
351                                        pts1[ip1],
352                                        NeighborhoodGeometry::Point,
353                                    );
354                                    kinematic.set_approx2(
355                                        FeatureId::Edge(id_e2),
356                                        pts2[e2.indices.x],
357                                        NeighborhoodGeometry::Line(m21 * seg2.direction().unwrap()),
358                                    );
359                                    let _ = manifold.push(contact, kinematic, p1, proc1, proc2);
360                                }
361                            }
362                            (
363                                SegmentPointLocation::OnEdge(_),
364                                SegmentPointLocation::OnVertex(j),
365                            ) => {
366                                let ip2 = e2.indices[j];
367                                if mesh1.edge_tangent_cone_polar_contains_orthogonal_dir(
368                                    id_e1,
369                                    &dir,
370                                    prediction.sin_angular1(),
371                                ) && mesh2.vertex_tangent_cone_polar_contains_dir(
372                                    ip2,
373                                    &(m21 * -dir),
374                                    prediction.sin_angular2(),
375                                ) {
376                                    // Accept the contact.
377                                    let contact = Contact::new_wo_depth(m1 * p1, m1 * p2, m1 * dir);
378                                    let mut kinematic = ContactKinematic::new();
379                                    kinematic.set_approx1(
380                                        FeatureId::Edge(id_e1),
381                                        pts1[e1.indices.x],
382                                        NeighborhoodGeometry::Line(seg1.direction().unwrap()),
383                                    );
384                                    kinematic.set_approx2(
385                                        FeatureId::Vertex(ip2),
386                                        pts2[ip2],
387                                        NeighborhoodGeometry::Point,
388                                    );
389
390                                    let _ = manifold.push(contact, kinematic, p1, proc1, proc2);
391                                }
392                            }
393                            (SegmentPointLocation::OnEdge(_), SegmentPointLocation::OnEdge(_)) => {
394                                if mesh1.edge_tangent_cone_polar_contains_orthogonal_dir(
395                                    id_e1,
396                                    &dir,
397                                    prediction.sin_angular1(),
398                                ) && mesh2.edge_tangent_cone_polar_contains_orthogonal_dir(
399                                    id_e2,
400                                    &(m21 * -dir),
401                                    prediction.sin_angular2(),
402                                ) {
403                                    // Accept the contact.
404                                    let contact = Contact::new_wo_depth(m1 * p1, m1 * p2, m1 * dir);
405                                    let mut kinematic = ContactKinematic::new();
406                                    kinematic.set_approx1(
407                                        FeatureId::Edge(id_e1),
408                                        pts1[e1.indices.x],
409                                        NeighborhoodGeometry::Line(seg1.direction().unwrap()),
410                                    );
411                                    kinematic.set_approx2(
412                                        FeatureId::Edge(id_e2),
413                                        pts2[e2.indices.x],
414                                        NeighborhoodGeometry::Line(m21 * seg2.direction().unwrap()),
415                                    );
416                                    let _ = manifold.push(contact, kinematic, p1, proc1, proc2);
417                                }
418                            }
419                        }
420                    }
421                }
422            }
423
424            // Project vertices for face1 to the plane of face2.
425            'vloop1: for iv in face1.indices.iter() {
426                let p1 = pts1[*iv];
427
428                for (side2, ref_pt2) in face2
429                    .side_normals
430                    .as_ref()
431                    .unwrap()
432                    .iter()
433                    .zip(t2.vertices().iter())
434                {
435                    // FIXME: too bad we will re-transform side2 for each iv...
436                    let dpt = p1 - ref_pt2;
437                    if dpt.dot(&(m12 * side2)) >= N::zero() {
438                        continue 'vloop1;
439                    }
440                }
441
442                let dpt = p1 - t2.a;
443                let dist = dpt.dot(&n2);
444
445                if dist >= N::zero()
446                    && mesh1.vertex_tangent_cone_polar_contains_dir(
447                        *iv,
448                        &-n2,
449                        prediction.sin_angular1(),
450                    )
451                {
452                    let proj = p1 + *n2 * -dist;
453
454                    // Accept the contact.
455                    let contact = Contact::new(m1 * p1, m1 * proj, m1 * -n2, -dist);
456                    let mut kinematic = ContactKinematic::new();
457                    kinematic.set_approx1(FeatureId::Vertex(*iv), p1, NeighborhoodGeometry::Point);
458                    kinematic.set_approx2(
459                        FeatureId::Face(i2),
460                        pts2[face2.indices.x],
461                        NeighborhoodGeometry::Plane(face2.normal.unwrap()),
462                    );
463                    let _ = manifold.push(contact, kinematic, p1, proc1, proc2);
464                }
465            }
466
467            // Project vertices for face2 to the plane of face1.
468            'vloop2: for iv in face2.indices.iter() {
469                // FIXME: don't re-transform the point.
470                // Re-use the corresponding vertex from t2 instead.
471                let p2 = m12 * pts2[*iv];
472
473                for (side1, ref_pt1) in face1
474                    .side_normals
475                    .as_ref()
476                    .unwrap()
477                    .iter()
478                    .zip(t1.vertices().iter())
479                {
480                    let dpt = p2 - ref_pt1;
481                    if dpt.dot(side1) >= N::zero() {
482                        continue 'vloop2;
483                    }
484                }
485
486                let dpt = p2 - t1.a;
487                let dist = dpt.dot(&n1);
488
489                if dist >= N::zero()
490                    && mesh2.vertex_tangent_cone_polar_contains_dir(
491                        *iv,
492                        &(m21 * -n1),
493                        prediction.sin_angular2(),
494                    )
495                {
496                    let proj = p2 + *n1 * -dist;
497
498                    // Accept the contact.
499                    let contact = Contact::new(m1 * proj, m1 * p2, m1 * n1, -dist);
500                    let mut kinematic = ContactKinematic::new();
501                    kinematic.set_approx1(
502                        FeatureId::Face(i1),
503                        t1.a,
504                        NeighborhoodGeometry::Plane(n1),
505                    );
506                    kinematic.set_approx2(
507                        FeatureId::Vertex(*iv),
508                        m21 * p2,
509                        NeighborhoodGeometry::Point,
510                    );
511                    let _ = manifold.push(contact, kinematic, proj, proc1, proc2);
512                }
513            }
514        }
515    }
516}
517
518impl<N: RealField + Copy> ContactManifoldGenerator<N> for TriMeshTriMeshManifoldGenerator<N> {
519    fn generate_contacts(
520        &mut self,
521        _: &dyn ContactDispatcher<N>,
522        m1: &Isometry<N>,
523        g1: &dyn Shape<N>,
524        proc1: Option<&dyn ContactPreprocessor<N>>,
525        m2: &Isometry<N>,
526        g2: &dyn Shape<N>,
527        proc2: Option<&dyn ContactPreprocessor<N>>,
528        prediction: &ContactPrediction<N>,
529        manifold: &mut ContactManifold<N>,
530    ) -> bool {
531        if let (Some(mesh1), Some(mesh2)) =
532            (g1.as_shape::<TriMesh<N>>(), g2.as_shape::<TriMesh<N>>())
533        {
534            // Find new collisions
535            let m12 = m1.inverse() * m2;
536            let m21 = m12.inverse();
537
538            // For transforming AABBs from mesh2 in the local space of mesh1.
539            let m12_abs_rot = m12.rotation.to_rotation_matrix().matrix().abs();
540
541            {
542                let mut visitor = AABBSetsInterferencesCollector::new(
543                    prediction.linear(),
544                    &m12,
545                    &m12_abs_rot,
546                    &mut self.interferences,
547                );
548                mesh1.bvh().visit_bvtt(mesh2.bvh(), &mut visitor);
549            }
550
551            let mut interferences = mem::replace(&mut self.interferences, Vec::new());
552            for id in interferences.drain(..) {
553                self.compute_faces_closest_points(
554                    &m12, &m21, m1, mesh1, id.0, proc1, m2, mesh2, id.1, proc2, prediction,
555                    manifold,
556                );
557            }
558            self.interferences = interferences;
559
560            true
561        } else {
562            false
563        }
564    }
565
566    fn init_manifold(&self) -> ContactManifold<N> {
567        let mut res = ContactManifold::new();
568        res.set_tracking_mode(ContactTrackingMode::FeatureBased);
569        res
570    }
571}