Vehicle physics rewrite, Proving Ground + Vehicle Lab, surface maps, water overhaul, asset reorg #9
Loading…
Reference in a new issue
No description provided.
Delete branch "feature/rigid-body-vehicle-physics"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Vehicle physics — new pure-Go
engine/physicspackage replacing the per-frame legacy model for all vehicles except the on-foot human:Testing tools
Rendering
Assets
assets/reorganised intomaterials/,models/,decals/,skies/,docs/,library/with consistent<material>_<map>naming; 17 Poly Haven (CC0) wood sets added. Code, config and saves updated to the new paths. The assets folder itself is still gitignored except the README gifs.Testing
go build ./...,go vet ./...clean.go test ./engine/physics/ ./engine/texmaps/ ./engine/render/ ./engine/world/(35 vehicle-physics scenario tests: settling, acceleration, braking, handbrake on slope, skidpad, crashes, bike balance/slalom, boat float/planing, takeoff, cruise stability, helicopter hover/forward flight).🤖 Generated with Claude Code
heli spin 1. Car/bike drift when stopped: the tyre grip model (desiredForce = -velocity*gripRate) is purely velocity-proportional, so it has no true zero-velocity equilibrium — any tiny persistent disturbance (e.g. slightly asymmetric per-wheel suspension compression) settles into a small nonzero perpetual creep instead of actually stopping. Added Vehicle.ApplyGroundStiction, called from Car and the shared bike physics whenever not actively throttling/braking, which firmly cancels residual horizontal velocity below a small speed threshold. 2. Boat "shaking too much forwards" (porpoising): the buoyancy impulse is a pure spring — as the hull pitches, the submerged sample grid's centroid shifts and creates a restoring torque — with no angular damping counterpart, so nothing stopped it rocking indefinitely along its length. Added submersion-scaled pitch/roll rate damping, the same fix pattern as Car's suspension damper. 3. Airplane/Helicopter falling through the ground: unlike every other vehicle type, these have no ground-contact mechanism of their own (no wheels/suspension, no buoyancy) — the only thing stopping them sinking into terrain was the generic "fell through the world" safety net, deliberately set to a 5-unit margin on the assumption every rigid body would have its own primary ground contact. For aircraft that fallback *is* the only ground contact, so they sank the full 5 units on every landing before being caught. Tightened the margin to 0.3 units specifically for Airplane/Helicopter. 4. Helicopter spinning when turning: cyclic pitch/roll applied a constant undamped torque for as long as the stick was held, so holding A/D kept accelerating roll rate with nothing to cap it, eventually tumbling. Redesigned as a bounded spring+damper toward a tilt target proportional to input (like a real rotor disk, which tilts to an angle and holds there rather than spinning up indefinitely) — unifies with and replaces the separate auto-level mechanism. Needed two rounds of sign-checking: an isolated single-step check gave one answer, but that check turned out to be unreliable until CyclicDamping was raised enough to remove an underdamped oscillation in the full system — see the in-code comment for the general lesson (an isolated check is only trustworthy once the system it approximates is itself well-damped). New regression tests for all four: TestCarDoesNotDriftWhenStopped, TestBoatDoesNotPorpoise(UnderThrottle), and TestHelicopterCyclic{Roll,Pitch}StaysBounded. Co-Authored-By: Claude Sonnet 5 <[email protected]>Real, reported bug ("the helicopter is spinning to the side and won't stop"), confirmed synthetically rather than guessed at: sustained aggressive cyclic (Steer=1, Throttle=-1 for 2s — plausible if a player is fighting to correct an existing spin) tips the helicopter past level, and on release AngularVelocity grew for several seconds instead of decaying, settling into a persistent multi-rad/s oscillation. Root cause: pitchSin/rollSin (dot(Front/Right, worldUp), the cyclic spring's small-angle proxy for tilt) is not monotonic with the true rotation angle once the helicopter has genuinely tipped past ~90° — the spring's (target-actual) error term can be wrongly signed there and reinforce rotation instead of opposing it. Compounded by this helicopter's shape giving it a pitch moment of inertia *between* its roll and yaw moments (RecomputeInertiaTensorMinDims' small height dimension vs. its width/length) — the classic intermediate-axis ("tennis racket"/Dzhanibekov) instability for a tumbling rigid body. Verified empirically that just raising CyclicDamping only shrinks the resonant peak with diminishing returns rather than fixing it, since it's damping a structurally wrong error signal, not correcting it. Fix, in two parts: - Gate the cyclic spring's stiffness term off past CyclicValidSin, leaving only the always-correctly-signed rate-damping term active beyond it (a pure damper can never reinforce rotation, unlike a position-error spring built on an ambiguous proxy). MaxCyclicSin (0.35) never approaches this threshold during ordinary flight. - Add a large-angle-robust recovery torque (Up.Cross(worldUp), valid at any tilt unlike the small-angle proxy) that engages once actually tipped past RecoveryUpThresh. Sign verified with an isolated single-step check before use, not derived by hand, per this project's established discipline around rotation signs. Applied the same stiffness gate to Airplane's roll auto-level, which has the identical structural issue (confirmed via the same kind of synthetic hold-then-release trace) — milder there since its own damping term eventually wins out on its own, but genuinely improved. Net result on the reported scenario: peak angular speed after release dropped from unbounded/non-settling to 0.54 rad/s, settling to exact zero within ~2s. New tests cover the isolated torque sign, the full-system recovery, and confirm ordinary cyclic/aileron handling is unaffected.