macrosoft3dpinball/godot
m3ultra 2ca67bb28b Add Godot 4.7 physics testbed: Box3D/Jolt/GodotPhysics A/B pinball table
Playable table (hinge-motor flippers, plunger, bumper, drain) plus a headless
probe harness that measured, per engine: tunneling of a fast ball through a
1 cm wall with CCD off/on, and hinge-motor drive. Findings in godot/README.md:
Box3D never tunnels even with CCD off, but its hinge motor sign is inverted
vs Jolt/GodotPhysics, and Jolt blows through angular limits that Box3D holds.
Ships a from-source macOS arm64 libgodot-box3d.dylib (upstream has no
releases). Also gitignore the remaining Microsoft media at repo root.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-06 05:33:07 +10:00
..
probe Add Godot 4.7 physics testbed: Box3D/Jolt/GodotPhysics A/B pinball table 2026-08-06 05:33:07 +10:00
scenes Add Godot 4.7 physics testbed: Box3D/Jolt/GodotPhysics A/B pinball table 2026-08-06 05:33:07 +10:00
godot-box3d.gdextension Add Godot 4.7 physics testbed: Box3D/Jolt/GodotPhysics A/B pinball table 2026-08-06 05:33:07 +10:00
godot-box3d.gdextension.uid Add Godot 4.7 physics testbed: Box3D/Jolt/GodotPhysics A/B pinball table 2026-08-06 05:33:07 +10:00
project.godot Add Godot 4.7 physics testbed: Box3D/Jolt/GodotPhysics A/B pinball table 2026-08-06 05:33:07 +10:00
README.md Add Godot 4.7 physics testbed: Box3D/Jolt/GodotPhysics A/B pinball table 2026-08-06 05:33:07 +10:00
run_probe.sh Add Godot 4.7 physics testbed: Box3D/Jolt/GodotPhysics A/B pinball table 2026-08-06 05:33:07 +10:00

Godot physics testbed (Box3D / Jolt / GodotPhysics A/B)

A minimal playable pinball table for Godot 4.7 whose real job is comparing 3D physics backends on the things pinball stresses: fast-small-ball CCD, motorised hinge flippers, and joint limits. The backend is one project setting, so the same table runs under all three engines.

Run it

/Applications/Godot.app/Contents/MacOS/Godot --path godot

Keys: Z left flipper, / right flipper, Space plunger, R reset ball.

The project defaults to Box3D Physics (Extension) via godot-box3d (MIT). bin/ carries a macOS arm64 dylib built from source (upstream has no releases; build recipe below).

A/B probe harness

godot/run_probe.sh          # GODOT_BIN=... to point at a different binary

Writes override.cfg to switch physics/3d/physics_engine, runs probe/probe.tscn headless under each backend, and prints per-engine results (also saved to probe/results/, untracked). Two tests:

  1. Tunneling — ball fired at a 1 cm static wall at 5160 m/s, CCD off and on.
  2. Hinge motor — a motorised HingeJoint3D arm; a flipper is a motorised hinge.

There is also a table-level flipper check: PINBALL_AUTOTEST=1 godot --headless --path godot flips both real flippers for 0.5 s and prints the swing angles.

Results (2026-08-06, Godot 4.7.stable, M3 Ultra)

Tunneling — ball contained by the 1 cm wall?

speed m/s Godot ccd off Jolt ccd off Box3D ccd off all three, ccd on
5 tunneled* tunneled* contained contained
10 tunneled contained* contained contained
20 contained* contained* contained contained
40 contained* contained* contained contained
80 tunneled tunneled contained contained
160 tunneled tunneled contained contained

* With a 60 Hz tick, every speed here can step over a 1 cm wall; the discrete-engine "contained" rows are phase luck (2 m at 20 and 40 m/s lands a tick exactly on the wall — and both engines produce identical trajectories, so they get lucky identically). The signal is the pattern, not individual rows: Box3D never tunnels even with the CCD flag off (speculative contacts are always on), the other two need continuous_cd = true, and with it all three hold at 160 m/s.

Hinge motor (target 5 rad/s, 1 s):

engine reached rad/s moved verdict
GodotPhysics3D 5.0 73.5° (wrapped) works
Jolt 5.0 75.1° (wrapped) works
Box3D +5.0 73.5° (wrapped) works

Flipper autotest on the real table (motors at ±25 rad/s, limits 5°…+50°):

engine left swing right swing
Box3D +51.8° — stops at the 50° limit 51.8° — stops at limit
Jolt 106.6° — wrong way, blows ~100° past the limit +104.3° — same

Two real findings, half a second of headless runtime each:

  1. Box3D's hinge motor sign is inverted relative to GodotPhysics/Jolt (upstream drop-in-compatibility bug candidate for godot-box3d). The table's flipper signs are tuned for Box3D, the project default — flip FLIP_SPEED/REST_SPEED signs in scenes/table.gd to play under Jolt.
  2. With the motor overdriving into them, Box3D enforces the angular limits hard while Jolt lets this setup punch far through them.

Rebuilding the extension

git clone --recurse-submodules https://github.com/bearlikelion/godot-box3d
cd godot-box3d && cmake -B build -DCMAKE_BUILD_TYPE=Release && cmake --build build -j
cp bin/libgodot-box3d.dylib <here>/bin/

Known Box3D gaps that don't matter for pinball: no Generic6DOF/ConeTwist joints, no SoftBody3D, Area3D can't detect trimesh/heightmap bodies (the ball is a sphere; fine). Box3D also ignores Godot's hinge BIAS/LIMIT_SOFTNESS/LIMIT_RELAXATION params (warns at load; harmless here).