Physics · const

JOINT_DISTANCE

Joints: seven types, each solved with its own rows unrolled.

Explained in Your first game, Rigid bodies.

const JOINT_DISTANCE = 0
import { JOINT_DISTANCE } from '@driftengine/physics';

In depth

This file refused a configurable 6-DOF joint until 2026-08-27, and the refusal was narrower than it read. What it argued against was replacing the six unrolled types with one joint whose constraint set is decided at runtime — a branch per row in the hottest loop in the engine, and a megamorphic call site at the top of it. That argument still stands and nothing here has changed: the six keep their own arms, their own rows and their own numbers, and none of them gained a branch.

What arrived instead is a seventh type, which is exactly what that refusal named as its own cost — "the package has to grow a type" — so the branch a six-DOF joint pays for is paid by six-DOF joints and by nothing else. The stated reversal condition was three consumers wanting three different exotic joints; growing three more unrolled types is the outcome this answers more cheaply than it answers the question of whether three have asked.

JOINT_SIX_DOF solves six independent scalar rows, not two coupled 3x3 blocks, because a coupled solve has no meaning when a caller has made only some of the rows exist. Cost: an all-locked six-DOF joint is measurably softer than JOINT_FIXED at the same substep count, so reach for JOINT_FIXED when all six are locked. What would make this wrong: a caller wanting an all-locked six-DOF joint to be as stiff as a fixed one, and the honest answer there is to use the fixed one.

No angle appears anywhere. A swing limit compares a dot product against a stored cosine; a twist limit compares a quaternion component against the half-angle's sine. Math.acos and Math.atan2 are both on the banned list, and writing the limits this way from the start is cheaper than repairing them — the comparison is what the solver wanted anyway, since it clamps rather than measures.

A point-to-point constraint solves its full 3×3 effective mass rather than three sequential scalars. The matrix inverts in closed form, so one exact solve replaces three approximate ones that would each disturb the others. What it costs is an inversion per joint per iteration.

Joints are solved before contacts in each iteration, because a joint is a hard structural relationship and a contact is a transient one: letting contacts win produces a limb that pulls out of its socket when something lands on it.