feat: add Möller 1997 boolean tri-tri fast path with test coverage#846
Closed
facontidavide wants to merge 1 commit into
Closed
feat: add Möller 1997 boolean tri-tri fast path with test coverage#846facontidavide wants to merge 1 commit into
facontidavide wants to merge 1 commit into
Conversation
Adds a fast-path triangle-triangle intersection test for boolean-only mesh collision queries (CollisionRequestFlag::NO_REQUEST and friends), based on Möller 1997. The implementation lives in include/coal/internal/tri_tri_overlap.h as a header-only template and returns a coplanar flag so the caller can fall back to GJK on coplanar or near-coplanar inputs (where Möller's 6-vertex separating-axis test isn't reliable). The fast path is enabled in MeshCollisionTraversalNode::leafCollides when the request only needs a boolean answer. It transforms each triangle's three vertices to world space, runs Möller, and on a non-coplanar overlap reports the contact. Coplanar pairs fall through to the existing GJK/EPA path. Test coverage: - test/tri_tri_overlap_fuzz.cpp: 50000 random triangle pairs cross- checked against a GJK oracle. 0 disagreements on non-coplanar inputs. - test/tri_tri_overlap_edge_cases.cpp: 10 named degenerate cases that the random fuzz never hits — coplanar overlapping/disjoint, identical triangles, shared-vertex / shared-edge boundary, zero-area triangle, collinear vertices, near-coplanar disjoint/overlapping, deep interpenetration. All 10 pass. On the polso/gen4 mesh-vs-mesh boolean motion-validation workload this is the largest single perf win in the branch (-14% to -34% per joint).
Contributor
|
See #858 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
include/coal/internal/tri_tri_overlap.h. Returns a coplanar flag so the caller can fall back to GJK on coplanar / near-coplanar inputs (where Möller's SAT isn't reliable).use_moller_fastpath_) onMeshCollisionTraversalNodekeeps the per-leaf check to a singleif.gjk-solver-hoist)This PR's leaf path is conceptually downstream of #843 (GJK hoist). The Möller fallback path (coplanar pairs) currently constructs a local
GJKSolverper leaf — once #843 lands, a follow-up rebase will switch this PR's fallback to use the hoisted member solversolver_for an additional small speedup on coplanar pairs. The change is mechanical and has been verified on the original branch where both commits live together.This PR can be merged before #843 (with the local-solver fallback on the coplanar path) or after #843 (with a trivial rebase that switches fallback to
solver_).Implementation notes
include/coal/internal/tri_tri_overlap.h: header-only templatetriTriOverlap(P1, P2, P3, Q1, Q2, Q3, &coplanar)returning bool. Inputs are world-spaceVec3striples; transformation happens inleafCollidesbefore the call.MeshCollisionTraversalNode::leafCollides: whenuse_moller_fastpath_is true, transforms both triangles into world frame, callstriTriOverlap, and either records a synthetic contact (positions are placeholders since the user opted out of contact info) or returns immediately on coplanar fallback.use_moller_fastpath_is precomputed at construction time as!request.enable_contact && !request.enable_distance_lower_bound && request.security_margin == 0. Computing it per-leaf would be wasteful; the request is fixed for a traversal.Performance impact
The fast path is the largest single perf contributor for boolean motion-validation workloads (the workload the original commit targeted). On the synthetic benchmark — which uses default
CollisionRequestwithenable_contact=true— the fast path never fires (the eligibility gate is false), so the synthetic Δ is within noise.Methodology
taskset -c 4), turbo locked.cmake --build build -j12 -DCMAKE_BUILD_TYPE=Releasein isolated worktrees; separatelibcoal.soper variant. Both base and variant compiled with stock upstream flags.coal-test-benchmarktotalFor the actual fast-path numbers: original commit measurements on
polso/gen4mesh-mesh boolean motion-validation (a real-world workload outside this repo) showed -14 % to -34 % per joint — by a wide margin the largest single optimisation in the parent branch.Correctness gate: full
ctestsuite passes including the two new tri-tri tests.Tests
test/tri_tri_overlap_fuzz.cpp: 50,000 random triangle pairs cross-checked against a GJK oracle. 0 disagreements on non-coplanar inputs. This is the strongest single-test regression gate in the parent branch.test/tri_tri_overlap_edge_cases.cpp: 10 named degenerate / corner-case pairs that the random fuzz never hits — coplanar-overlapping, coplanar-disjoint, identical triangles, shared-vertex / shared-edge boundary, zero-area triangle, collinear vertices, near-coplanar disjoint and overlapping, deep interpenetration. All 10 pass.Risk & regression analysis
sqrDistLowerBound = 0, which is the honest "no info" answer for a request that opted out of distance lower bound. Callers that need a tighter bound must enablerequest.enable_distance_lower_bound, which disables the fast path (eligibility gate becomes false) — by design.enable_contact == falseis the gating condition, butnum_max_contactsmay still be > 0), the syntheticContactuses midpoint position and zero normal. This is consistent with "user opted out of contact info"; the fast-path check ensures the placeholder is only registered when the user has setenable_contact=false.