Skip to content

fix: give grid nodes lying on a trimesh surface a collision - #11

Merged
sebcrozet merged 1 commit into
dimforge:mainfrom
MichaelBradetLegris:fix/collide-on-surface-nodes
Nov 2, 2026
Merged

sebcrozet merged 1 commit into
dimforge:mainfrom
MichaelBradetLegris:fix/collide-on-surface-nodes

Conversation

@MichaelBradetLegris

Copy link
Copy Markdown
Contributor

Problem

collide() builds the contact normal from proj.point - point and skips a collider when that distance is zero (dist != 0.0). A grid node lying exactly on a triangle-mesh surface therefore gets no collision, and no boundary condition is applied to it. This affects grid_update and any custom kernel that calls collide().

With a flat mesh floor that lies on a grid plane (for example a heightfield floor at an elevation that is a multiple of the cell width), the whole layer of nodes on the floor is left free. Particles just above it take part of their velocity from those nodes, and once disturbed they sink through the floor.

Fix

  • TriMesh.project_local_point also returns the pseudo-normal it already computes for its inside test. The return type is now Tuple<ProjectionResult, Vect, bool>; collide() is its only caller in slosh.
  • For points within 1.0e-4 cell widths of a trimesh surface, collide() uses that normal instead of the direction to the projection, which is zero or rounding noise there. Other shape types behave as before.

Testing

We hit this in a blast simulation built on slosh 0.7.0. Its floor is a heightfield converted to an oriented trimesh, with the boundary condition applied per grid node through collide(). The setup: floor at a multiple of the cell width, 1.25 m cells, 1 particle per cell. Same input and same build, with and without this change:

Bottom-row particles that end more than 0.1 m below the floor Without With
Inside the blast 8,121 of 13,421 0
10+ m from the blast 10,033 of 15,463 0

The same scene shifted half a cell against the grid, so that no node lies on the floor, is unchanged by the patch.

That run used this edit applied to v0.7.0. On this branch I compiled solver/grid_update.slang to WGSL with slangc (DIM=3) but have not run a simulation.

We pin slosh 0.7.0, so a 0.7.x patch release with this fix would let us ship it without moving to 0.9.

collide() derives the contact normal from the vector between a grid node
and its projection on the collider, and skipped the collider when that
vector had zero length. A node lying exactly on a triangle-mesh surface
therefore got no boundary condition at all. When a flat mesh floor lies on
a grid plane, that whole layer of nodes is left free and particles sink
through the floor.

TriMesh.project_local_point now also returns the pseudo-normal it already
computes for its inside test, and collide() uses it for points within
1e-4 cell widths of the surface. Other shape types are unchanged.
@sebcrozet
sebcrozet merged commit 4595724 into dimforge:main Nov 2, 2026
5 checks passed
@sebcrozet

Copy link
Copy Markdown
Member

Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants