Skip to content

Latest commit

 

History

78 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Feather

GitHub go.mod Go version License Go Reference Go Report Card Tests Codecov GitHub Issues or Pull Requests GitHub Issues or Pull Requests

A Go physic library, based on the TGS Soft solver algorithm.

Shapes

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

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).

Why not XPBD anymore

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.

Constraints

  • 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)

GJK

Detects if two convex shapes overlap. With a margin, it also detects the shapes closer than the margin (speculative contacts).

EPA

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.

Tests & benchmarks

  • 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)

Sources

Acknowledgements

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

Contributing Guidelines

See how to contribute.

Licence

This project is distributed under the Apache 2.0 licence.

Releases

Packages

Contributors

Languages