Physics · type
FrictionModel
How the two tangent rows are bounded against each other.
Explained in Your first game, Rigid bodies.
type FrictionModel = 'box' | 'elliptical'import type { FrictionModel } from '@driftengine/physics';In depth
box is two independent clamps and is what this solver has always done. Each axis is bounded
at mu * N on its own, so along the diagonal between them the pair reaches sqrt(2) * mu * N —
forty-one per cent more friction than the same surface offers along an axis. The tangent basis is
constructed from the contact normal, so which headings are cheap is an artefact of that
construction rather than a fact about the world, and a box measurably slides further in some
directions than others.
elliptical couples them and friction becomes isotropic, which is what it physically is. It
is a correctness fix and it is not the default, because every stacking result moves under it:
fingerprintBodies, both baselines, and any golden fingerprint or stored replay a consumer has
kept. A production engine does not silently invalidate those. What would reverse
that: a major version, where the honest thing is to make the ellipse the default and say so in
the porting guide.
One coefficient, not two, and that is a decision rather than an omission. Anisotropic
friction is the natural API for an ellipse — different mu along each tangent — and it needs a
tangent frame anchored to a material, which this solver does not have: prepareContact builds
the tangents from the normal, so they rotate arbitrarily as a contact moves and a per-axis
coefficient would mean something different from one tick to the next. That is a silent wrong
answer, so the ellipse here is a circle. What would reverse it: a tangent frame carried by the
body, which is a larger change than the cone; vehicle.ts already needs that and answers it its
own way, with a grip table rather than a coefficient.