Without friction, boxes slide forever and never tip or stack. Today you add a tangential friction impulse, clamped by the Coulomb limit, closing the resolution chapter.
Compute a tangential friction impulse that opposes sliding, clamped so it never exceeds mu times the normal impulse.
Friction acts along the tangent - the part of the relative velocity
perpendicular to the normal - and opposes sliding. Its magnitude is computed just like
the normal impulse, but along the tangent direction, then clamped by Coulomb’s
law: the friction impulse can be at most mu times the normal impulse. That clamp is
the whole character of dry friction - a resting box does not slide until you push harder
than friction can resist, and a sliding box is slowed by a force proportional to how
hard it presses down. Here the raw tangent impulse -3 exceeds the limit mu * j = 2,
so it is capped at -2.
Apply the friction impulse the same way as the normal one, splitting it by inverse mass (and, in the full resolver, by inverse inertia for the spin it induces). Combine the two bodies’ friction coefficients into one - a common choice is the square root of their product. With friction in place the resolution chapter is complete: contacts now stop approach, bounce with restitution, correct sinkage, induce spin, and resist sliding. Print a box sliding to a halt on the ground to see it all at once.
func FrictionImpulse(rv, n Vec2, jNormal, mu, totalInvMass float64) Vec2 {t := Sub(rv, Scale(n, Dot(rv, n))) // tangent directionif LengthSquared(t) == 0 { return Vec2{} }t = Normalize(t)jt := -Dot(rv, t) / totalInvMassjt = math.Max(-mu*jNormal, math.Min(mu*jNormal, jt)) // Coulomb clampreturn Scale(t, jt)}