Contact and Collision
Contact setup has two separate layers:
- scene configuration controls the global algorithm (
contact/enable, activation distance, friction regularization, IPC pipeline, and broad phase); ContactTabularassigns pairwise material behavior (friction coefficient, contact resistance, and whether that pair may contact).
The optional config argument on ContactTabular.insert/default_model is a
reserved extension point. No per-model keys are currently defined, so it must
be {}; a non-empty object now raises an error instead of being ignored. The
same rule applies to the reserved SubsceneTabular.insert(..., config=...)
argument.
Keeping these layers separate is essential: setting contact/enable = 1 does
not choose material coefficients, and creating a contact element does not
label a mesh surface.
Make geometry contact-capable
For a tetrahedral, triangle, edge, or point SimplicialComplex, call
label_surface(mesh). It creates the is_surf attributes used by the global
surface and collision systems. For tetrahedral meshes,
label_triangle_orient(mesh) additionally records boundary orientation; this
is required for reliable surface export and downstream orientation-sensitive
operations.
ground(height, normal) creates an implicit half-plane and participates in
contact without surface labeling.
Geometry without an explicit contact element uses element 0, the default
element. Applying the default element explicitly is still recommended in
tutorial code because it makes the intended material assignment visible.
Default model
default_model(friction_rate, resistance, enable=True) defines the fallback
model for every contact-element pair without a specific row.
friction_rateis the Coulomb-style friction coefficient. Use>= 0; setcontact/friction/enable = 0to remove friction terms globally.resistanceis contact stiffness in Pa. Larger values enforce a stronger barrier but can worsen conditioning.enable=Falsedisables the default pair response.
If the application never calls default_model(...), the effective default
resistance is contact/adaptive/min_kappa (100 MPa by default). If it does set
a non-negative value, the backend clamps it into
[contact/adaptive/min_kappa, contact/adaptive/max_kappa] and warns when a
clamp occurs. A negative resistance is the explicit adaptive-kappa marker and
is intentionally not clamped.
Pairwise material table
Create named contact elements, apply them to geometries, then insert symmetric
pair rows. Pair lookup is order-independent; (rubber, steel) and
(steel, rubber) are the same row. A missing row falls back to the default
model.
from uipc.unit import GPa, MPa
table = scene.contact_tabular()
steel = table.default_element()
rubber = table.create("rubber")
table.default_model(0.3, 1.0 * GPa)
table.insert(steel, rubber, 0.8, 300.0 * MPa)
table.insert(rubber, rubber, 1.0, 100.0 * MPa)
steel.apply_to(steel_mesh)
rubber.apply_to(rubber_mesh)
To create a collision mask, insert the pair with enable=False. This is more
precise than disabling contact globally:
Activation distance and thickness
The activation distance d_hat is not a penetration allowance. IPC begins
building barrier response before primitives reach their geometric thickness.
For a rest-scene diagonal \(L\):
Shell/rod thickness is a geometry/material attribute and is combined with the
activation distance by collision primitives. Do not use a large d_hat as a
substitute for setting physical cloth or rod thickness.
Friction uses the analogous effective transition velocity:
IPC versus AL-IPC
contact/constitution = "ipc" is the default, broadly exercised pipeline.
"al-ipc" replaces it with the augmented-Lagrangian active-set pipeline and
activates the contact/al-ipc/* parameters. It is a pipeline choice, not a
per-material contact model, so one scene cannot mix IPC and AL-IPC pairs.
Fused-PCG CUDA graph replay is currently disabled automatically under AL-IPC. Start with IPC unless a method comparison or an AL-specific workflow requires the alternative.
Broad phase and sanity checks
The always-available broad phase is info_stackless_bvh (default).
stackless_bvh, linear_bvh, and the info_stackless_bvh_v0 comparison path
belong to the optional legacy-collision build component. They are present by
default, but a lean build can disable them with
UIPC_WITH_CUDA_LEGACY_COLLISION=OFF (CMake) or
cuda_legacy_collision=false (XMake); the scene schema then rejects those
names. This selector changes candidate generation, not contact material
behavior.
Leave sanity_check/enable = 1 while authoring scenes. Before engine
initialization it detects initial intersections and dangerously close
surfaces, respects the contact table's enabled masks, and prevents an invalid
world from starting. sanity_check/mode = "normal" also writes diagnostic
geometry for a failed check.
Common failure checklist
| Symptom | Check |
|---|---|
| Bodies pass through each other | contact/enable, label_surface, pair enable, initial intersection, and mesh units. |
| No friction | contact/friction/enable, pair friction_rate, and eps_velocity. |
| Unexpected material pair | Whether the intended ContactElement was applied before adding the geometry to the scene. Untagged geometry is default element 0. |
World invalid at init |
Keep sanity checks on and inspect the emitted diagnostic geometry/log. |
| Solver becomes very stiff | Check resistance units, d_hat, initial gaps, and whether an explicit resistance was clamped. |
| Scale-dependent behavior | Use the relative d_hat, velocity tolerance, and friction velocity controls consistently. |
The Rigid-Soft Coupling and Contact tutorial builds on these rules with a complete C++/Python material-table walkthrough.