Using Hydroelastic Contact Models (SDF) for Soft Contact in Newton
Newton’s hydroelastic contact model uses Signed Distance Fields (SDFs) to distribute contact forces over a surface patch, enabling realistic soft-body interactions with smoother force gradients than traditional point-contact methods.
Newton is an open-source physics engine that implements advanced hydroelastic contact models for simulating compliant, surface-based interactions. By representing colliding shapes with Signed Distance Fields (SDFs), Newton’s hydroelastic pipeline distributes forces across a contact patch rather than at discrete points, providing more realistic soft contact behavior for robotics and deformable body simulation.
How Hydroelastic SDF Contact Works in Newton
Unlike point-contact models that resolve collisions at single points, hydroelastic contacts model the intersection volume between two SDF-represented shapes. The HydroelasticSDF class in newton/_src/geometry/sdf_hydroelastic.py (lines 70-78) orchestrates a five-stage pipeline that converts overlapping SDF volumes into distributed contact forces.
The Five-Stage Collision Pipeline
The hydroelastic pipeline processes collisions through distinct computational stages, each implemented as specialized kernels in sdf_hydroelastic.py:
-
Broadphase – An OBB intersection test culls non-overlapping shape pairs using
broadphase_collision_pairs_countandbroadphase_collision_pairs_scatter(lines 880-910). -
Octree Refinement – Hierarchical subdivision (8×8×8 → 4×4×4 → 2×2×2 → voxels) locates iso-voxels where the zero-isosurface between two SDFs exists via
count_iso_voxels_blockandscatter_iso_subblock(lines 1010-1080). -
Marching Cubes – The pipeline extracts contact-surface triangles from each iso-voxel using
mc_iterate_voxel_verticesand related helper kernels (lines 1190-1240). -
Contact Generation –
generate_contacts_kernel(created in_get_generate_contacts_kerneland invoked from_generate_contacts) computes contact centroids, normals, penetration depths, and areas from the triangles. -
Contact Reduction – Optional binning reduces contacts to a representative set per shape pair using
HydroelasticContactReduction(seenewton/_src/geometry/contact_reduction_hydroelastic.py).
Configuring Hydroelastic SDF Contacts
HydroelasticSDF.Config Parameters
The HydroelasticSDF.Config dataclass in newton/_src/geometry/sdf_hydroelastic.py (lines 122-150) controls pipeline behavior:
reduce_contacts– WhenTrue, contacts are reduced via a spatial hashtable; setFalsefor full contact resolution useful for debugging.pre_prune_contacts– Enables fast local-first face compaction that reduces global hashtable traffic.buffer_fraction,buffer_mult_*– Scale pre-allocated buffers for broadphase, octree refinement, and contact storage.output_contact_surface– Generates aContactSurfaceDataobject containing triangle vertices for visualization whenTrue.anchor_contact– Adds an anchor contact at the patch centroid to improve moment balance during reduction.margin_contact_area– Small area used for non-penetrating contacts at the contact margin.
Enabling Hydroelastic on Shapes
To use hydroelastic contacts, you must set shape flags and generate SDFs:
- Set Shape Flags – Apply
ShapeFlags.HYDROELASTICto shapes using the SDF model:
shape_idx = builder.add_shape_mesh(...)
builder.shape_flags[shape_idx] |= newton.ShapeFlags.HYDROELASTIC
- Generate SDFs – Meshes require an associated SDF built with appropriate resolution:
mesh.build_sdf(
max_resolution=64,
narrow_band_range=(-gap, gap),
margin=gap,
)
For scaled meshes, ensure the SDF is scale-baked (rescale to unit scale before building) as demonstrated in newton/examples/robot/example_robot_panda_hydro.py (lines 94-103).
- Configure Pipeline – Pass
HydroelasticSDF.ConfigtoCollisionPipeline:
hydro_cfg = HydroelasticSDF.Config(
reduce_contacts=True,
output_contact_surface=False,
buffer_fraction=1.0,
)
pipeline = newton.CollisionPipeline(
model,
rigid_contact_max=6000,
sdf_hydroelastic_config=hydro_cfg,
)
Practical Implementation Examples
Minimal Hydroelastic Model Setup
This complete example creates a soft box with hydroelastic contacts:
import newton, warp as wp
from newton.geometry import HydroelasticSDF
# Create a simple box mesh and bake an SDF
box = newton.Mesh.create_box(0.05, 0.05, 0.05)
box.build_sdf(max_resolution=64, narrow_band_range=(-0.01, 0.01), margin=0.01)
builder = newton.ModelBuilder()
shape_cfg = newton.ModelBuilder.ShapeConfig(is_hydroelastic=True, gap=0.01)
builder.default_shape_cfg = shape_cfg
body = builder.add_body()
builder.add_shape_mesh(body=body, mesh=box)
model = builder.finalize(device=wp.get_device())
hydro_cfg = HydroelasticSDF.Config(
reduce_contacts=False,
output_contact_surface=True,
)
pipeline = newton.CollisionPipeline(
model,
sdf_hydroelastic_config=hydro_cfg,
rigid_contact_max=2000,
)
# Run one collision step
state = model.state()
contacts = pipeline.contacts()
pipeline.collide(state, contacts)
# Visualise contact surface (if using Newton viewer)
if pipeline.hydroelastic_sdf:
surface = pipeline.hydroelastic_sdf.get_contact_surface()
print("Contact triangles:", surface.max_num_face_contacts)
Enabling Hydroelastic Contacts on URDF Robots
Selectively enable soft contacts on specific URDF links (e.g., robot finger pads):
import newton, warp as wp
from newton.geometry import HydroelasticSDF
import copy
# Base shape config for all URDF parts (hydroelastic disabled by default)
base_cfg = newton.ModelBuilder.ShapeConfig(
kh=1e11,
sdf_max_resolution=64,
is_hydroelastic=False,
sdf_narrow_band_range=(-0.01, 0.01),
gap=0.01,
)
builder = newton.ModelBuilder()
builder.default_shape_cfg = base_cfg
# Import a robot URDF; visual meshes become colliders
builder.add_urdf(
newton.utils.download_asset("franka_emika_panda") / "urdf/fr3_franka_hand.urdf",
parse_visuals_as_colliders=True,
)
# Enable hydroelastic only on the finger pads
finger_shape_cfg = copy.deepcopy(base_cfg)
finger_shape_cfg.is_hydroelastic = True
for idx, body_idx in enumerate(builder.shape_body):
if builder.shape_type[idx] == newton.GeoType.MESH:
# Assume we know which shapes correspond to the pads
if body_idx in {left_finger_idx, right_finger_idx}:
mesh = builder.shape_source[idx]
mesh.build_sdf(max_resolution=64, narrow_band_range=(-0.01, 0.01), margin=0.01)
builder.shape_flags[idx] |= newton.ShapeFlags.HYDROELASTIC
model = builder.finalize(device=wp.get_device())
pipeline = newton.CollisionPipeline(
model,
sdf_hydroelastic_config=HydroelasticSDF.Config(
reduce_contacts=True,
anchor_contact=True,
),
)
Running a Full Simulation with Soft Contacts
Execute a complete simulation loop using the hydroelastic pipeline:
import newton, warp as wp
from newton.geometry import HydroelasticSDF
# Build scene (see build_stacked_cubes_scene in tests for full details)
model, solver, state0, state1, ctrl, pipeline, _, _ = build_stacked_cubes_scene(
device=wp.get_device(),
solver_fn=lambda m: newton.solvers.SolverXPBD(m, iterations=10),
shape_type=newton.tests.ShapeType.PRIMITIVE,
reduce_contacts=True,
sdf_hydroelastic_config=HydroelasticSDF.Config(
output_contact_surface=False,
reduce_contacts=True,
anchor_contact=True,
),
)
contacts = pipeline.contacts()
for step in range(300):
pipeline.collide(state0, contacts)
solver.step(state0, state1, ctrl, contacts, 1.0 / 60.0)
state0, state1 = state1, state0
# Optional: visualise or log contact forces here
Visualizing Hydroelastic Contact Surfaces
When debugging or analyzing soft contacts, you can extract the actual contact surface geometry. Setting output_contact_surface=True in the configuration generates a ContactSurfaceData object containing the triangle vertices of the contact patch.
The viewer extracts these vertex buffers and renders them as wireframes (see viewer/_src/viewer/viewer.py line ~530). Here is how to capture and visualize the contact surface:
hydro_cfg = HydroelasticSDF.Config(
output_contact_surface=True,
reduce_contacts=False,
)
pipeline = newton.CollisionPipeline(model, sdf_hydroelastic_config=hydro_cfg)
# After a collision step:
surface = pipeline.hydroelastic_sdf.get_contact_surface()
if surface:
viewer.add_mesh(
vertices=surface.contact_surface_point,
faces=surface.face_contact_count, # each 3 vertices form a triangle
color=(0.2, 0.7, 1.0, 0.5),
)
Key Source Files and References
Understanding the hydroelastic implementation requires familiarity with these specific source locations:
-
newton/_src/geometry/sdf_hydroelastic.py– Core hydroelastic implementation containing theHydroelasticSDFclass (lines 70-78), theConfigdataclass (lines 122-150), and all pipeline kernels includingbroadphase_collision_pairs_count,count_iso_voxels_block, andmc_iterate_voxel_vertices. -
newton/_src/geometry/contact_reduction_hydroelastic.py– ImplementsHydroelasticContactReductionfor spatial hashing and contact patch simplification. -
newton/examples/robot/example_robot_panda_hydro.py– End-to-end example demonstrating SDF generation for scaled meshes (lines 94-103), shape flag configuration, and collision pipeline setup for a Franka Panda arm. -
newton/tests/test_hydroelastic.py– Comprehensive test suite featuringbuild_stacked_cubes_scene(lines 82-115) for validating hydroelastic contacts against positional and rotational error thresholds using both MuJoCo and XPBD solvers.
Summary
-
Hydroelastic contact models in Newton use SDFs to represent colliding geometry, enabling distributed force calculation across contact patches rather than discrete points.
-
The five-stage pipeline (Broadphase → Octree Refinement → Marching Cubes → Contact Generation → Contact Reduction) processes collisions through specialized kernels in
sdf_hydroelastic.py. -
Configuration via
HydroelasticSDF.Configcontrols contact reduction, buffer sizing, and surface output, while shape flags (ShapeFlags.HYDROELASTIC) andmesh.build_sdf()enable the model on specific geometries. -
Practical implementation follows the pattern: model construction → SDF baking → flagging → pipeline configuration → simulation, with full examples available in the test suite and robot examples.
Frequently Asked Questions
What is the difference between hydroelastic contacts and standard rigid contacts in Newton?
Standard rigid contacts use point-based collision detection where forces are applied at discrete contact points, often resulting in jittery or unstable behavior with soft materials. Hydroelastic contacts model the actual intersection volume between shapes using Signed Distance Fields, distributing forces across the entire contact patch. This produces smoother force gradients and more realistic compliance, particularly important for grasping and soft-body simulation.
How do I generate an SDF for my mesh to use with hydroelastic contacts?
You must call build_sdf() on your mesh object before finalizing the model, specifying the resolution and narrow band range appropriate for your object scale. For example: mesh.build_sdf(max_resolution=64, narrow_band_range=(-0.01, 0.01), margin=0.01). If your mesh is scaled non-uniformly, you should scale-bake it first (rescale to unit scale before building the SDF) as demonstrated in newton/examples/robot/example_robot_panda_hydro.py lines 94-103.
Why are my hydroelastic contacts not generating any contact forces?
First, verify that you have set the ShapeFlags.HYDROELASTIC flag on your shape: builder.shape_flags[shape_idx] |= newton.ShapeFlags.HYDROELASTIC. Second, ensure you passed a valid HydroelasticSDF.Config to the CollisionPipeline via the sdf_hydroelastic_config parameter. Third, confirm that your SDF was built with sufficient resolution and that the narrow_band_range covers the expected penetration depths. Without proper SDF generation or configuration flags, the pipeline will not process hydroelastic collisions.
Can I visualize the actual contact surface patches in Newton?
Yes, set output_contact_surface=True in your HydroelasticSDF.Config. After a collision step, access the surface data via pipeline.hydroelastic_sdf.get_contact_surface(), which returns a ContactSurfaceData object containing contact_surface_point vertices and face_contact_count indices. You can render these triangles in Newton’s viewer using viewer.add_mesh() with the extracted vertices and faces, typically drawn as wireframes to visualize the contact patch geometry.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →