A Go physic library, based on the TGS Soft solver algorithm.
All shapes live in the actor package and implement actor.ShapeInterface.
| Shape | Definition | Narrow phase |
|---|---|---|
Sphere |
Radius |
analytic against planes, spheres and capsules; its center against the other shapes (GJK distance + radius) |
Box |
HalfExtents |
analytic against planes; GJK/EPA otherwise |
Plane |
Normal, Distance (static only) |
analytic |
Capsule |
HalfHeight, Radius, axis along local Y |
analytic against planes, spheres and capsules; its segment against the other shapes (GJK distance + radius) |
Heightfield |
a grid of heights (static only), 2 triangles per cell | GJK/EPA against each triangle under the body |
body := actor.NewRigidBody(
actor.Transform{Position: mgl64.Vec3{0, 1, 0}, Rotation: mgl64.QuatIdent()},
&actor.Capsule{HalfHeight: 0.6, Radius: 0.3},
actor.BodyTypeDynamic,
1000, // density
)
body.Material.StaticFriction = 0.6
body.Material.DynamicFriction = 0.5
world.AddBody(body)
body.AddForce(mgl64.Vec3{10, 0, 0}) // in N, during the next step
world.Step(1.0 / 60.0)TGS Soft (or "Soft Step") is the solver of Box2D v3, described by Erin Catto in Solver2D. It is made of substeps, soft constraints, warm starting and relaxation:
while simulating do
contacts ← CollectContacts(); // once per step, with speculative contacts
h ← Δt/numSubsteps;
PrepareContacts(contacts); // anchors, effective masses, previous impulses
for numSubsteps do
for n bodies do
v ← v + h*(g + f_ext/m);
ω ← ω + h*I⁻¹(τ_ext - ω × Iω);
end
WarmStart(contacts); // apply the impulses of the previous substep
Push(contacts); // soft constraint: remove the overlap
for n bodies do
x ← x + h*v;
q ← q + h/2 * ω*q;
end
Relax(contacts); // rigid constraint + friction, removes the energy of the soft constraint
end
ApplyRestitution(contacts);
StoreImpulses(contacts); // warm start of the next step
end
- The contacts are computed only once per step: during the substeps, the separation of each contact point is updated from the motion of both bodies.
- The soft constraint is a spring + damper, set with a frequency (
World.ContactHertz, 30 Hz by default, as Box2D v3.1) and a damping ratio. - Contacts exist before the bodies touch (speculative contacts), so fast bodies don't go through thin walls.
- Friction follows Coulomb's law: static friction when the contact sticks, dynamic friction when it slides.
- The simulation is deterministic: same result bit for bit, whatever the number of
Workers. - The solver is parallel: the contacts are split into colors (graph coloring), the contacts of a color don't share any body.
- A step doesn't allocate memory (after the first steps).
- The bodies touching each other sleep and wake up together (islands).
Up to v0.2.0, Feather used a simplified XPBD solver. The same scenes (bench/), each version at its own setting:
TGS Soft at 60 Hz with 8 substeps (the setting of Feather for the games), v0.2.0 at 50 Hz with 12 substeps (the
setting AkmonEngine ran it with; v0.2.0 has no default):
| Scene | v0.2.0 (XPBD) | TGS Soft | Expected |
|---|---|---|---|
| Pyramid of 55 boxes, 3 s | explodes (top box at 93 m) | stands (4.744 m) | 4.750 m |
| Box on a 20° slope, µ = 0.6 | slides 9.9 m | 0 m | 0 m |
| Box on a 35° slope, µ = 0.3 | slides 16.9 m | 9.657 m | 9.648 m |
| Bounce from 1 m, restitution 0.5 | 0.06 m | 0.23 m | 0.25 m |
| 10 N during 1 s on 32.7 kg | 15279 m/s | 0.306 m/s | 0.306 m/s |
| Same scene, run twice | 38/40 bodies differ | identical | identical |
| EPA sphere-box normal (p99) | 2.7° | 0.03° | 0° |
| Step, 10 / 100 / 500 bodies resting on the ground (one layer of boxes & spheres), 1 worker | 0.39 / 1.82 / 8.3 ms | 0.017 / 0.11 / 0.57 ms |
cd bench
go run . # current version
go run -tags v020 -modfile=go.v020.mod . # v0.2.0
A heavier scene, 500 boxes & spheres falling on each other (BenchmarkWorldStep, 60 Hz, 8 substeps), takes ~2.1 ms
per step on 1 worker, ~0.8 ms on 8 workers; 2000 bodies awake, 6.5 ms and 2 ms.
- Contact: generated when a collision is detected between two rigid bodies, up to 4 points (manifold), with friction, rolling resistance and restitution.
- Distance: fixed length, a range [min, max] (a rope), or a spring. Usage: ropes, chains, springs
- Ball (ball and socket): the anchors stay together, with an optional elliptic cone for the swing and a range for the twist, and an optional drive towards a target rotation. Usage: ragdolls, physical bones, tails
- Hinge: rotation around one axis only, with an optional angle range, motor and spring. Usage: doors, wheels, knees
- Fixed: the position and the rotation of the 2 bodies are frozen together
- Configurable: each of the 6 axes is locked, limited or free, with optional drives. Usage: sliders, shoulders, vehicles, anything the other joints don't cover
hinge := feather.NewHingeJoint(frame, door, mgl64.Vec3{0, 1, 0}, mgl64.Vec3{0, 1, 0}) // anchor, axis (world space)
hinge.EnableLimit = true
hinge.LowerAngle, hinge.UpperAngle = -math.Pi/2, math.Pi/2
world.AddJoint(hinge)Detects if two convex shapes overlap. With a margin, it also detects the shapes closer than the margin (speculative contacts).
Computes the penetration depth, the normal and the witness points. The contact points are then clipped between the faces of both shapes (Sutherland-Hodgman), each point with its own separation.
See ALGORITHMS.md, ARCHITECTURE.md and the physics guide.
- Minimal scenes (
scenes_test.go): one mechanism each (a box landing on a corner, a capsule spinning like a top, a sphere in a V...), with a bound derived from the engine (LinearSlop), never from a measure. - Invariants (
invariants_test.go): 60 random scenes checked at every step: finite values, unit quaternions, 1 and 8 workers giving the same bits, no body in a plane, no energy gained, momentum & angular momentum kept in free flight. - Scenes of Solver2D (
bench/scenes): the samples of Erin Catto's Solver2D in 3D, each checked, and compared to Box2D v3.1 on the same scenes (the reference: Feather must do at least as well; the known gaps are followed by #821). - Regressions (
bench/): 6 scenes (piles, pyramid, joint chain, rain on a terrain) and the scenes of Solver2D against a committed reference: fingerprint, quality and speed per phase (World.Profile).
go test ./...
cd bench && go run . -check # exit 1 on a regression
cd bench && go run . -update # after a wanted change
cd bench && go test ./... # the scenes of Solver2D
cd bench && go run . -scenes # the scenes at full size (-compare: with v0.2.0)
- https://box2d.org/posts/2024/02/solver2d/
- https://github.com/erincatto/box2d (v3)
- https://box2d.org/files/ErinCatto_SoftConstraints_GDC2011.pdf
- https://box2d.org/files/ErinCatto_NumericalMethods_GDC2015.pdf (gyroscopic torque)
- https://github.com/bepu/bepuphysics2
- https://cse442-17f.github.io/Gilbert-Johnson-Keerthi-Distance-Algorithm/
- https://winter.dev/articles/epa-algorithm
- Christer Ericson, Real-Time Collision Detection (2004)
- https://github.com/jrouwe/JoltPhysics (active edges, contact patches, body pair cache)
- W. J. Stronge, Impact Mechanics (2000): Poisson's hypothesis for the restitution
- Brian Mirtich, Impulse-based Dynamic Simulation of Rigid Body Systems (1996): conservative advancement
- Solver2D, the samples of the reference scenes: https://github.com/erincatto/solver2d (MIT)
- PhysX speculative CCD & Unity "Continuous Speculative": https://nvidia-omniverse.github.io/PhysX/physx/5.4.1/docs/AdvancedCollisionDetection.html
Feather is written from the publications and the documentation of these projects:
- Box2D, by Erin Catto: the TGS Soft solver (Solver2D, Soft Constraints), the graph coloring, the continuous collision
- Jolt Physics, by Jorrit Rouwe: the active edges of the terrains, the contact patches, the body pair cache
See how to contribute.
This project is distributed under the Apache 2.0 licence.