Skip to content

Expose total normal impulses, improve contact impulse APIs#788

Merged
Jondolf merged 2 commits into
mainfrom
better-impulse-apis
Jul 22, 2025
Merged

Expose total normal impulses, improve contact impulse APIs#788
Jondolf merged 2 commits into
mainfrom
better-impulse-apis

Conversation

@Jondolf

@Jondolf Jondolf commented Jul 21, 2025

Copy link
Copy Markdown
Member

Objective

ContactPoint stores a normal_impulse. However, this is the clamped accumulated normal impulse from the last substep, used for warm starting the solver. It does not represent the total impulse applied across substeps and restitution, despite the ambiguous name.

As such, we are currently missing a proper way for users to determine how "strong" a contact is. Exposing this is important for numerous gameplay scenarios, like determining when a glass bottle or window should shatter, or how much damage a hit should deal.

Solution

Rename the current ContactPoint properties normal_impulse and tangent_impulse to warm_start_normal_impulse and warm_start_tangent_impulse, and expose a new normal_impulse that represents the total normal impulse applied across substeps and restitution.

Contact constraints still store a normal_impulse with the old semantics, but the max_normal_impulse has been renamed to total_normal_impulse, which contains the total normal impulse. Some of the solver code here has also been slightly modified to fix some problems and work with the new semantics.

The method APIs like ContactPair::total_normal_impulse have been updated accordingly. ContactPair::max_normal_impulse has also been split into ContactPair::max_normal_impulse (returns a vector) and ContactPair::max_normal_impulse_magnitude (returns a scalar).

Comparison to Box2D

Box2D has the same property, but there the warm starting impulse is stored on b2ManifoldPoint as normalImpulse, and the total normal impulse is stored as totalNormalImpulse. The former is intended to be more internal, and the latter is intended for gameplay purposes.

Using the more obvious name normalImpulse for the value intended for internals seems like an odd API choice to me, so I instead opted to make normal_impulse what users would expect to use, and give the less obvious / longer name to the warm starting impulse as warm_start_normal_impulse.


Migration Guide

ContactPoint::normal_impulse previously corresponded to the clamped accumulated normal impulse from the last substep, used for warm starting the contact solver. It did not represent the total impulse applied across substeps and restitution, despite the ambiguous name.

Now, ContactPoint::normal_impulse works like you would expect, and represents the total normal impulse applied at a contact point. Divide by the time step to get the corresponding force.

The old warm starting impulses are now stored as warm_start_normal_impulse and warm_start_tangent_impulse (previously tangent_impulse).

The method APIs such as total_normal_impulse, total_normal_impulse_magnitude, and max_normal_impulse have been updated accordingly to use the new normal_impulse. Additionally, ContactPair::max_normal_impulse has been split into ContactPair::max_normal_impulse (returns a vector) and ContactPair::max_normal_impulse_magnitude (returns a scalar).

The normal_force and tangent_force methods of ContactPoint have been removed.

ContactConstraintPoint::max_normal_impulse is now stored in the ContactNormalPart as total_impulse.

@Jondolf Jondolf added this to the 0.4 milestone Jul 21, 2025
@Jondolf Jondolf added C-Feature A new feature, making something new possible A-Collision Relates to the broad phase, narrow phase, colliders, or other collision functionality A-Dynamics Relates to rigid body dynamics: motion, mass, constraint solving, joints, CCD, and so on M-Migration-Guide A breaking change to Avian's public API that needs to be noted in a migration guide C-Usability A quality-of-life improvement that makes Avian easier to use labels Jul 21, 2025
@Jondolf Jondolf marked this pull request as ready for review July 21, 2025 21:23
@Jondolf Jondolf enabled auto-merge (squash) July 22, 2025 22:45
@Jondolf Jondolf merged commit 6b8676b into main Jul 22, 2025
6 checks passed
@Jondolf Jondolf deleted the better-impulse-apis branch July 22, 2025 22:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-Collision Relates to the broad phase, narrow phase, colliders, or other collision functionality A-Dynamics Relates to rigid body dynamics: motion, mass, constraint solving, joints, CCD, and so on C-Feature A new feature, making something new possible C-Usability A quality-of-life improvement that makes Avian easier to use M-Migration-Guide A breaking change to Avian's public API that needs to be noted in a migration guide

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant