Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions ALGORITHMS.md
Original file line number Diff line number Diff line change
Expand Up @@ -183,14 +183,18 @@ The center is the average of the points, weighted by their separation (as Box3D:
twice). µ is the static friction when the center slides slower than 1 cm/s, the dynamic friction otherwise.

### Restitution
Applied once after the substeps, for the contacts hitting faster than 1 m/s. The bounce impulse goes towards the
velocity `-e * vn_before` (Newton), and is at most `e` times the impulse which stopped the point, its normal impulse of
the step (Poisson's hypothesis, W. J. Stronge, Impact Mechanics):
Applied once after the substeps, for the contacts hitting faster than 1 m/s, once their compression is over (the
point doesn't approach anymore). The bounce impulse goes towards the velocity `-e * vn_impact` (Newton), and is at most
`e` times the impulse of the compression (Poisson's hypothesis, W. J. Stronge, Impact Mechanics):
````
λ = max(0, min(-m (vn + e * vn_before), e * λ_step))
λ = max(0, min(-m (vn + e * vn_impact), e * λ_compression))
````
An impact starting at the very end of a step is compressed over 2 steps: the point keeps its approach velocity and its
compression impulse (`ImpactVelocity`, `CompressionImpulse`, warm started like the impulses) and bounces at the end of
the second step, with the whole impulse. Bounced at the end of the first step, a ball dropped from 1 m at 60 Hz gave
back 10 % of its height instead of 92 % (`TestBounceRestitution`, at every rate).
Both are needed: a pile of balls bouncing with `e = 1` gains energy with Newton alone (411 J) or Poisson alone
(3523 J), not with both (`TestRestitutionNeverAddsEnergy`). The bounce uses the velocity before the step: a body not
(3523 J), not with both (`TestRestitutionNeverAddsEnergy`). The bounce uses the approach velocity of the impact: a body not
round and spinning fast can turn its point away before the end of the step, and bounce higher than it fell over
`e = 0.5` (see ARCHITECTURE.md, as documented by Jolt). Newton's law is the one of the game engines (Box2D, Jolt).

Expand Down
6 changes: 3 additions & 3 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,9 @@ restitution, continuous collision, islands), without allocation.
of contact never stops (no sleep).
- A capsule resting across a bump of a terrain can stay a few mm in the terrain: the contact of a triangle comes from
the feature of the body above the triangle, the middle of the capsule is missed.
- The restitution is applied once per step, with the velocity before the step: a body not round (box, capsule),
bouncy (`e` over 0.5) and spinning fast (20-50 rad/s) can bounce higher than it fell. Measured: up to +60 % of
energy at `e = 1`, never up to `e = 0.5`. Jolt documents the same limit.
- The restitution is applied once per step, with the approach velocity of the impact: a body not round (box,
capsule), bouncy (`e` over 0.5) and spinning fast (10-20 rad/s) can bounce higher than it fell. Measured at 60 Hz
with 8 substeps: up to +21 % of energy at `e = 1`, never up to `e = 0.5`. Jolt documents the same limit.
- No kinematic bodies (moving platforms): a body is static or dynamic.
- The continuous collision stops the fast bodies against the static bodies (and the bullets against all the bodies),
not the other pairs: 2 fast dynamic bodies rely on their speculative contacts (2 cm) and on the spring of the contact.
4 changes: 2 additions & 2 deletions PHYSICS_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ are twice as stiff.
- Higher values = less overlap under load (stacks), but it is capped at 1/8 of the substeps rate: `substeps / dt / 8`.
- Lower values = softer contacts.

With 12 substeps at 50 Hz, a stack of 10 boxes of 50 cm sinks by ~23 mm (Box2D v3.1: 30 mm): a contact sinks by
With 8 substeps at 60 Hz, a stack of 10 boxes of 50 cm sinks by ~32 mm (Box2D v3.1: 30 mm): a contact sinks by
(load / mass) g / (2π hertz)² under its load. A stiffer world sinks less, but a heavy body landing on a light one bounces
more.

Expand All @@ -138,7 +138,7 @@ travel during the step. Between 2 dynamic bodies, from 2 cm only: a fast body ca
spring of the contact pushes it out.
A fast body is also moved back to its first impact with a static body (continuous collision). Set `IsBullet` on a small
fast body (a projectile) to stop it on the dynamic bodies too.
A ball at 40 m/s does not go through a 4 cm wall at 50 Hz.
A ball at 40 m/s does not go through a 4 cm wall at 60 Hz (nor at 80 m/s).

### Sleep
The bodies touching each other form an island. An island resting for 0.5 s (all its bodies under 0.05 m/s and 0.05 rad/s)
Expand Down
18 changes: 10 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,27 +74,29 @@ end
- 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/`, 50 Hz, 12 substeps):
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 134 m) | stands (4.748 m) | 4.750 m |
| 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.654 m | 9.648 m |
| Bounce from 1 m, restitution 0.5 | 0.06 m | 0.24 m | 0.25 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 | 39/40 bodies differ | identical | identical |
| 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.41 / 1.94 / 8.8 ms | 0.03 / 0.27 / 1.28 ms | |
| 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
```

Both versions run 12 substeps at 50 Hz. A heavier scene, 500 boxes & spheres falling on each other (`BenchmarkWorldStep`),
takes ~5.5 ms per step on 1 worker, ~1.9 ms on 8 workers.
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,
Expand Down
Loading
Loading