Skip to content
Draft
Show file tree
Hide file tree
Changes from 42 commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
3e19e78
Deterministic contacts start to work for convex vs convex and convex …
nvtw Apr 10, 2026
e67a7a9
Determinism starts to work for meshes
nvtw Apr 10, 2026
f2d7647
Fix issues in contact pre-pruning
nvtw Apr 10, 2026
73849b1
Add more unit tests
nvtw Apr 10, 2026
50858c7
Ran ruff
nvtw Apr 10, 2026
ad5c0f7
Remove debug printf that slipped in
nvtw Apr 10, 2026
7623a82
Forgot to add new file
nvtw Apr 10, 2026
4f15e42
Ran ruff
nvtw Apr 10, 2026
f9ef6c4
Update the docs
nvtw Apr 10, 2026
c38b83e
Fix sort issue in contact determinism
nvtw Apr 10, 2026
92d9399
Fix hte docs
nvtw Apr 10, 2026
587be69
Fix the performance regression
nvtw Apr 10, 2026
ed6ee76
Implement more improvements
nvtw Apr 10, 2026
41ed6c8
Implement CodeRabbit comments
nvtw Apr 13, 2026
d2f7a09
Update the changelog and the docs
nvtw Apr 13, 2026
c7bc73c
Implement MR comments
nvtw Apr 13, 2026
5df8e2c
Merge branch 'main' into dev/tw3/deterministic_contacts
nvtw Apr 13, 2026
5e89322
Merge branch 'main' into dev/tw3/deterministic_contacts
nvtw Apr 13, 2026
ac66d07
Implement more MR comments
nvtw Apr 14, 2026
4d56f4f
Merge branch 'main' into dev/tw3/deterministic_contacts
nvtw Apr 14, 2026
45cfbf0
Implement more CodeRabbit comments
nvtw Apr 14, 2026
77b5331
Improve sorting
nvtw Apr 14, 2026
bb3ec2a
Improve the unit tests
nvtw Apr 14, 2026
c37a749
Attempt to fix some remaining non-determinism
nvtw Apr 14, 2026
f5f78e4
First attempt towards contact matching
nvtw Apr 14, 2026
a367f23
Reduce memory footprint
nvtw Apr 14, 2026
f6c61eb
Bugfix
nvtw Apr 14, 2026
975d973
Improve the test coverage
nvtw Apr 14, 2026
77882a0
Add more diagnostics to the tests.
nvtw Apr 14, 2026
3a8ed92
Disable prepruning for determinism experiments.
nvtw Apr 14, 2026
c639770
Attempt to fix CI
nvtw Apr 14, 2026
a0b1462
Remove unnecessary file
nvtw Apr 14, 2026
d10983c
Merge remote-tracking branch 'origin/dev/tw3/deterministic_contacts' …
nvtw Apr 14, 2026
1a5b370
Merge branch 'main' into dev/tw3/deterministic_contacts
nvtw Apr 14, 2026
94eef59
Merge remote-tracking branch 'refs/remotes/origin/dev/tw3/determinist…
nvtw Apr 14, 2026
3e833e9
Merge branch 'main' into dev/tw3/contact_matching
nvtw Apr 15, 2026
1370429
Update the changelog
nvtw Apr 15, 2026
ed4c6e1
Update the documentation
nvtw Apr 15, 2026
b531b49
Implement CodeRabbit comments
nvtw Apr 15, 2026
a0073fc
Merge branch 'main' into dev/tw3/contact_matching
nvtw Apr 15, 2026
e994d6b
Implement MR comments
nvtw Apr 17, 2026
631b7a3
Merge branch 'main' into dev/tw3/contact_matching
nvtw Apr 17, 2026
2c33eee
Implement CodeRabbit comments
nvtw Apr 17, 2026
cb52409
Implement more MR comments
nvtw Apr 17, 2026
1fc6ea7
Update docs
nvtw Apr 17, 2026
4379204
Fix CI
nvtw Apr 17, 2026
1295271
Merge branch 'main' into dev/tw3/contact_matching
nvtw Apr 17, 2026
6691056
Test better matching - not done yet
nvtw Apr 17, 2026
d8f89c7
Ensure that only one contact can claim a contact from the last frame
nvtw Apr 17, 2026
2c82a1d
Small performance improvement
nvtw Apr 17, 2026
287ce43
Merge branch 'main' into dev/tw3/contact_matching
nvtw Apr 20, 2026
cf4e985
Add sticky contact matching mode
nvtw Apr 21, 2026
99b0e2c
Merge branch 'main' into dev/tw3/contact_matching
nvtw Apr 21, 2026
bd6ed7a
Handle contact matching break distance evaluation in a more symmetric…
nvtw Apr 21, 2026
f1dca6e
Improve the docs
nvtw Apr 21, 2026
4fec7f5
Attempt to fix CI docs
nvtw Apr 21, 2026
dcd693b
Merge branch 'main' into dev/tw3/contact_matching
nvtw Apr 21, 2026
bfb4dc3
Contact matching experiments
nvtw Apr 21, 2026
ac5c049
Add more diagnostic output
nvtw Apr 21, 2026
49ce8bd
Revert "Add more diagnostic output"
nvtw Apr 21, 2026
743bac9
Revert "Contact matching experiments"
nvtw Apr 21, 2026
011d75c
Fix normal misalignment issue
nvtw Apr 21, 2026
e6f0542
Unit test fixes
nvtw Apr 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,11 @@
- Export `ViewerBase` from `newton.viewer` public API
- Add `custom_attributes` argument to `ModelBuilder.add_shape_convex_hull()`
- Add RJ45 plug-socket insertion example with SDF contacts, latch joint, and interactive gizmo
- Add `TRIANGLE_PRISM` support-function type for heightfield triangles, extruding 1 m along the heightfield's local -Z so GJK/MPR naturally resolves shapes on the back side
- Add `ViewerGL.log_scalar()` for live scalar time-series plots in the viewer
- Add `deterministic` flag to `CollisionPipeline` and `NarrowPhase` for GPU-thread-scheduling-independent contact ordering via radix sort and deterministic fingerprint tiebreaking in contact reduction
- Add `ViewerBase.log_arrows()` for arrow rendering (wide line + arrowhead) in the GL viewer with a dedicated geometry shader
- Add frame-to-frame contact matching via `CollisionPipeline(contact_matching=True)` with configurable position and normal thresholds. With `contact_report=True`, new and broken contact index lists are exposed as `contacts.rigid_contact_new_indices` / `rigid_contact_new_count` / `rigid_contact_broken_indices` / `rigid_contact_broken_count`
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

### Changed

Expand Down
95 changes: 95 additions & 0 deletions docs/concepts/collisions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1253,6 +1253,18 @@ and is consumed by the solver :meth:`~solvers.SolverBase.step` method for contac
- Contact normal, pointing from shape 0 toward shape 1 (world frame).
* - ``rigid_contact_margin0``, ``rigid_contact_margin1``
- Per-shape thickness: effective radius + margin (scalar).
* - ``rigid_contact_match_index``
- Per-contact frame-to-frame match result (int32). ``>= 0``: matched old
index, ``-1``: new, ``-2``: broken. Only allocated when
``contact_matching=True``. See :ref:`Contact Matching`.
* - ``rigid_contact_new_indices``, ``rigid_contact_new_count``
- Compact index list of new contacts in the current sorted buffer (where
``match_index < 0``). Only allocated when ``contact_report=True``.
See :ref:`Contact Reports`.
* - ``rigid_contact_broken_indices``, ``rigid_contact_broken_count``
- Compact index list of contacts from the previous frame that no current
contact matched. Only allocated when ``contact_report=True``.
See :ref:`Contact Reports`.

**Soft contacts (particle-shape):**

Expand Down Expand Up @@ -1669,6 +1681,89 @@ fully CUDA-graph-capturable.

Hydroelastic contacts are not yet covered by deterministic ordering.

.. _Contact Matching:

Contact Matching
----------------

Contact matching tracks contacts across frames, identifying which contacts
persist, which are new, and which have broken. Enable it with
``contact_matching=True`` on :class:`~CollisionPipeline` (this implies
``deterministic=True``):

.. testsetup:: contact-matching

import warp as wp
import newton

builder = newton.ModelBuilder()
builder.add_ground_plane()
body = builder.add_body(xform=wp.transform((0.0, 0.0, 2.0), wp.quat_identity()))
builder.add_shape_sphere(body, radius=0.5)
model = builder.finalize()
state = model.state()

.. testcode:: contact-matching

pipeline = newton.CollisionPipeline(
model,
contact_matching=True,
contact_matching_pos_threshold=0.02, # metres
contact_matching_normal_dot_threshold=0.9, # cos(~25°)
)
contacts = pipeline.contacts()

pipeline.collide(state, contacts)

# Per-contact match index (int32):
# >= 0 : index of the matched contact in the previous frame
# -1 : new contact (no match found)
# -2 : key matched but position/normal thresholds exceeded (broken)
match_idx = contacts.rigid_contact_match_index.numpy()

Each frame, the matcher binary-searches the current contacts against the
previous frame's sorted keys, then verifies candidates against a world-space
position distance threshold and a normal dot-product threshold. The sort key
encodes ``(shape_a, shape_b, sub_key)`` so only contacts between the same shape
pair are compared.

**Thresholds**

- ``contact_matching_pos_threshold`` — maximum world-space distance [m] a
contact may move between frames and still be considered the same contact.
- ``contact_matching_normal_dot_threshold`` — minimum dot product between old
and new contact normals. Below this the contact is reported as broken even
if the key and position match.

.. _Contact Reports:

Contact Reports
^^^^^^^^^^^^^^^

Pass ``contact_report=True`` to also collect compact index lists of new and
broken contacts each frame:

.. testcode:: contact-matching

pipeline = newton.CollisionPipeline(
model,
contact_matching=True,
contact_report=True,
)
contacts = pipeline.contacts()
pipeline.collide(state, contacts)

n_new = contacts.rigid_contact_new_count.numpy()[0]
new_indices = contacts.rigid_contact_new_indices.numpy()[:n_new]

n_broken = contacts.rigid_contact_broken_count.numpy()[0]
broken_indices = contacts.rigid_contact_broken_indices.numpy()[:n_broken]

``rigid_contact_new_indices`` holds indices into the current frame's sorted
contact buffer where ``match_index < 0``. ``rigid_contact_broken_indices``
holds indices into the *previous* frame's sorted buffer for contacts that no
current contact matched.
Comment thread
nvtw marked this conversation as resolved.
Outdated

.. _Performance:

Performance
Expand Down
Loading
Loading