Water-tight rigid–soft contact

An SDF-based contact path so a deforming body cannot tunnel through a rigid shape between its vertices, with full integration into the VBD solver. Newton · SolverVBD · NVIDIA L40 · 2026-06.

3 passes
particle + soft-edge + soft-face contact generation
FACE + EDGE
contacts with no vertex required at the contact
flag off ⇒ identical
byte-for-byte legacy behaviour when disabled
two-way
soft side and rigid-body reaction in VBD

Results — gripping a soft mesh by a vertex-free feature

A parallel-jaw gripper (cube pads) closes on a soft mesh positioned so that no mesh vertex lies between the jaws — only the interior of a face or an edge does — then lifts and holds. Left = water-tight OFF reproduces main: the legacy per-particle path only tests vertices, finds none in the jaws, so the mesh slips through and drops. Right = water-tight ON: the mesh is caught by its face / edge and held in the air.

Soft triangle — gripped by its FACE

The triangle's three vertices are outside the jaws; only the face crosses the pinch. OFF: it falls. ON: a water-tight face contact holds it.

Soft 1×1 grid — gripped by its interior DIAGONAL EDGE

The grid's four corners are outside the jaws; the diagonal edge crosses the pinch. OFF: it falls. ON: a water-tight edge contact holds it (the sheet drapes over the grip).

What changed

The feature is purely additive and gated by enable_water_tight_rigid_soft_contact; with the flag off the contact set and solver results are byte-for-byte identical to before.

1 · Contact generation (SDF back-end)

2 · VBD integration

3 · Tests & examples

Gotcha worth noting: an analytic box face gives an ambiguous SDF normal when a soft triangle straddles it (the closest feature flips between the bottom and a side face), so the two-way grip tests use convex shapes (cube / sphere) where the normal is unique and stable.

SDF memory footprint

How much memory each shape's SDF actually uses, measured through the real finalize() path at the default settings (64³ max resolution, UINT16-quantised narrow band, 8³ subgrids). Analytic primitives store nothing; only meshes allocate a volume SDF.

ShapeSDF representationStored memory
Box, Sphere, Capsule, Cylinder, Cone, Ellipsoid, Planeclosed-form analytic (evaluated, not stored)0
Mesh — bunny (12,200 tris)texture volume SDF≈ 0.48 MB
Mesh — bear (3,968 tris)texture volume SDF≈ 0.30 MB

All analytic shapes report shape_sdf_index = -1 under water-tight — nothing is allocated; the contact path evaluates their closed-form SDF.

Mesh breakdown — the narrow-band subgrid dominates

Componentbunnybear
coarse texture (far field, float32)2.5 KB (9×9×8)1.3 KB (9×6×6)
subgrid texture (narrow band, uint16)488 KB (63³)307 KB (54³)
indirection slots (uint32)1.8 KB0.8 KB
total493 KB310 KB

SDF accuracy

Texture-SDF value vs. the exact mesh signed distance (winding-number query) at 300k points sampled within ±6 voxels of the surface (the contact-relevant band). The error is resolution-limited, so it is reported both absolutely and as a fraction of one voxel (≈ object extent / 64); the meshes are at their native USD scale.

MeshvoxelMAERMSEp99max
bunny26.7 mm1.02 mm (3.8% vox)1.86 mm (6.9%)7.8 mm (29%)22 mm (83%)
bear152.7 mm7.30 mm (4.8% vox)14.6 mm (9.6%)51 mm (33%)206 mm (135%)