SOURCE / PINNED RELEASE
Made of little things.
Supersonic RC Revive
- Release
- 1ba42f1ca1d6…
- Author-recorded commit
- baecad10b1cd…
- License
- LICENSE
- Author’s source reference
- nostr://npub1ye5ptcxfyyxl5vjvdjar2ua3f0hynkjzpx552mu5snj3qmx5pzjscpknpr/wss%3A%2F%2Fgit.napplet.soy%2F/n-143146b0d6f
Archive hash verified: 90d22b206672eba4…. The source-to-build association is the author’s claim; it has not been independently rebuilt.
# Physics calibration against the original
The car physics is a line-by-line port of the Lingo (`car`, `wheel`, `havokManager`,
`drivingManager`; see `docs/original-game-spec.md` §2–3) on top of a Rapier-backed
stand-in for the Havok Xtra (`src/game/physics/havok.ts`). The Lingo is exact by
construction. The Xtra's behaviour is not documented accurately, so it was
**measured in the running original** and fitted.
## How the measurements were taken
`tools/sim/make_logger_wrapper.py` writes an instrumented copy of the wrapper movie.
The wrapper compiles `member("closeScript").text` into a movie script inside `game.dcr`.
The patched text adds an `on idle` logger. For every new Havok `simTime` it records:
- the rigid body: position, axis-angle rotation, linear and angular velocity,
`centerOfMass`, `mass`
- each wheel: `pOnGround`, `pSpringLength`, `pRotation`, `pLoad`, `pSpeed`
- the keys held and `pSkidding`
It flushes with `setPref` to `ssrcN.txt` in the projector's Prefs folder.
`tools/sim/wine-reference.sh` runs the original under Wine on a private Xvfb display
and drives it with `xdotool`. Then:
- `tools/sim/replay.mjs` feeds the logged per-frame keys, and the chunk sequence decoded
from each frame's simTime delta, into the port.
- `tools/sim/compare.py` and `tools/sim/fitwindow.py` diff the two trajectories.
For contacts, the logger can also run **scripted drop tests**. Set
`SSRC_EXPERIMENTS` to a Lingo list of `[simTime, position, axis, angleDeg, velocity]`
when building the wrapper. At each listed simTime the logger:
- drops the forces already queued for the next step;
- sets the car's state;
- writes an `EXP` marker line that also reports `havok.tolerance`.
Pass the same list as JSON to `tools/sim/replay.mjs` so the port gets the same
interventions. `tools/sim/inject.mjs` instead starts the port from any logged row,
for example just before a real landing, which isolates the contact response from
accumulated drift.
Two logging subtleties matter:
- Keys are sampled at `idle`, after that frame's update, so the replay shifts them
one frame later.
- Wine frame times jitter, so the replay reproduces each frame's chunk list instead of
assuming 25 ms frames.
## Findings
| Question | Answer | Evidence |
|---|---|---|
| `angularVelocity` units | **rad/s** (the reference says degrees) | In flight ω = 0.59 while the rotation angle advances 33.8°/s. Wheel `pSpeed` matches `v + ω × r` with ω in radians to 0.001 over 532 rotating frames. |
| Gravity | 77.24 units/s² (0.2 × 386.22) | Free-fall Δvz in the logs; `pLoad` at rest equals `579.33·(30 − L) + damping` exactly. |
| `centerOfMass` | (0, 0, −8.5): hull centroid (0, 0, 5) + shift (0, 0, −13.5) | Logged directly. |
| COM in the wheel lever | Added **unrotated** to `rb.position`, as the Lingo writes it | The rotated variant mismatches `pSpeed` by up to 59. |
| `applyForceAtPoint` point | Model space, rotated by the body transform | The unrotated-point hypothesis diverges about 20 s earlier on the turning run. |
| How queued forces act | **One impulse `F · t · 0.02417` at the start of `step(t, 7)`** | Resting vz = −1.21 ≈ −3·g·t/7 (the start-impulse signature). The equilibrium loads give 0.02417. The throttle-up curve matches (port +1% speed). |
| Inertia | Solid box of the 15×30×10 hull | Yaw-rate and roll-onset traces in turns match at scale 1.0; 0.7 is worse. |
| Torque scale | Same factor as the force | Airborne pitch rate after a ramp matches (0.59 rad/s). |
| Wheel rays and back faces | `modelsUnderRay` reports **front faces only** | Upside down, the original's wheel rays (which start just under the floor) find no ground. With back faces the port read full compression and flung the car. No level model is flagged double-sided. |
| Collision tolerance | `havok.tolerance` = **3.937** (0.1 m) | Logged. |
| Contact gap | Bodies rest **3.3** units apart | An upside-down car rests at z 344.0, against 340.7 for exact contact. The gap stops the chassis before the springs reach their 20-unit stop on hard landings, so the logged springs bottom out at about 21. |
| Collision callbacks | Fire for contacts within the tolerance; `car.collision` caps ω at 4 | Impacts in the logs show \|ω\| = 4.00 exactly. |
| Chassis friction | Behaves like the lower value, the scenery's 0.3 (car 10) | Landing replays: speed loss matches at 0.3. The geometric mean (1.73) scrubbed off about a third of the speed. |
| Solver | One velocity iteration | The −250 slam test rebounds at +28 in the original. Rapier's default four iterations gave +52 and a 20° pitch-up. |
## Agreement today
- **Rest:** spring 25.94 vs 25.92 in the original.
- **Straight acceleration:** speed within 1%; position within 9 units after 7 s.
- **Turning, braking, reversing:** yaw-rate traces nearly identical; position within
about 20 units after 20 s of manoeuvres.
- **Ramp jump:** take-off point and airborne pitch match.
- **Drop tests:** six scripted drops (flat, pitched, on the side, upside down, a −250
slam and a roof-edge roll). Mean height error is 0.06–0.4 units in the first five.
The roof-edge roll still turns over faster in the port: mean tilt error 19°, though
it ends upright like the original.
- **Real landings:** injecting the logged state just before three landings, the port
stays within 4–28 units of the original over 1.5 s with no rebound. Before these
fixes the error was 53–176 units, with launches.
- **Whole runs:** mean position error on log2 is 18.5 (was 33.2), with heading error
1.4° (was 11.9°). On log4 it is 328 (was 1246).
## Open items
- Inertia versus torque scale: the driving data only fixes their ratio. The roof-edge
roll prefers about 2× inertia, but other drops get worse, so it stays at 1×.
- Record reference runs at a steady 40 fps. Wine under Xvfb drops frames; the replay
handles it, but cleaner logs fit better.
