Add a physics engine (rapier3d) #110

Open
opened 2026-10-20 23:40:37 +00:00 by sermuns · 0 comments
Owner

The campus has no physics. Everything that moves is moved by hand: the player
walks with a per-building 2-D wall-distance predicate
(collision::can_walk_to), the vehicles are a bespoke speed-and-heading model
(vehicle::step_vehicle), and the things on the ground — benches, bins,
bollards, seesaws — are drawn but never collided. Every roll, swing,
tumble, tip, fall over and bounce is a special case somebody has to
write by hand, and none of them compose.

Rapier is the obvious candidate: a pure-Rust physics engine with no renderer, a
first-class WASM story, and the joint, character-controller and ray-cast
vehicle APIs that the missing features all need. This issue is the spine; the
features it unlocks are the issues that link back to it.

What it would buy

  • A real vehicle: wheels, suspension, grip and rollover, instead of the
    hand-rolled speed-and-heading model. #111
  • Objects that can be thrown, kicked and rolled: a football, a boule, a disc.
    #112
  • Playground toys as mechanisms: a seesaw on a hinge, a swing on a pendulum.
    #113
  • A body that can fall over instead of a body that slides along walls. #114
  • Doors that swing, and a building you can walk through properly. #115
  • Buoyancy: swimming, and things that float. #116

The costs, honestly

  • It contradicts a written decision. ARCHITECTURE.md says collision "is not
    a swept capsule" and is a per-building 2-D wall-distance predicate, because
    that keeps a walker on the right floor and never snaps a jump onto the slab
    above. Rapier's character controller works the other way: one shape, cast
    against the world. Getting the campus's storeys right means handing Rapier
    the same floor-aware geometry the predicate has today — as one collider per
    slab, or as collision groups — which is a bake change, not only a code
    change.
  • Two vector vocabularies. The project has one: glam, with Vec2/Vec3
    and the Enu/WorldPos newtypes. Rapier speaks nalgebra. The conversion
    lives at the edge of the physics module, but it exists, and every crossing
    pays for it.
  • The multiplayer split gets a third case. Players are client-authoritative
    and vehicles are server-authoritative today. A physics world both must agree
    on is something else: either the server owns every body and the 20 Hz pose
    carries the state, or each client simulates its own and the two drift.
  • Determinism is opt-in. Rapier is deterministic on one machine by default.
    Across machines it wants the enhanced-determinism feature, which cannot be
    combined with simd8.
  • WASM gets no threads. Rapier builds for wasm32-unknown-unknown and its
    own CI tests that target, but rayon falls back to one thread there, so the
    web build pays single-threaded cost.

Where it would live

Rapier is pure Rust and has no rendering dependency, so it belongs in
liuverse-world beside collision, vehicle and player — the crate whose
whole job is "the simulation, without the renderer", and whose tests are pure
in-memory. It goes into [workspace.dependencies] like everything else.

The static world is the hard part. Rapier wants colliders, and the campus is
baked as triangle soups with footprints and rings. Two ways in:

  • Trimesh colliders from the baked geometry. Honest, and enormous: the whole
    campus as one trimesh is a lot of triangles for a BVH to chew on, and it
    collides the interiors with the exteriors.
  • Analytic colliders from the bake. One cuboid per floor slab, one per
    interior wall ribbon, a height field for the terrain, a trimesh for the
    roofs. The bake already knows how to produce all four. It is also what
    makes the storeys work: a slab is a collider, so climbing a stair is a
    slope and jumping under the next floor still cannot snap onto it.

The second is the one that keeps the current behaviour, so it is the one to try
first. It grows the bake a collision layer, which is a format change and
therefore a rebake.

The smaller step, if this is too much

parry3d is the same author's collision library without the dynamics solver.
It gives ray casts, shape casts and overlap queries — enough for picking and
for a proper camera — at a fraction of the weight and with no authority
questions. It is the right first move if the goal is picking and not
simulation.

Related

  • #111 the vehicles on real wheels
  • #112 the thrown and rolled bodies
  • #113 the playground mechanisms
  • #114 the ragdoll
  • #115 the doors
  • #116 the water
  • parry3d is the same author as rapier3d, one layer down.
  • ARCHITECTURE.md's collision and vehicle decisions are the ones that change.
The campus has no physics. Everything that moves is moved by hand: the player walks with a per-building 2-D wall-distance predicate (`collision::can_walk_to`), the vehicles are a bespoke speed-and-heading model (`vehicle::step_vehicle`), and the things on the ground — benches, bins, bollards, seesaws — are drawn but never collided. Every *roll*, *swing*, *tumble*, *tip*, *fall over* and *bounce* is a special case somebody has to write by hand, and none of them compose. Rapier is the obvious candidate: a pure-Rust physics engine with no renderer, a first-class WASM story, and the joint, character-controller and ray-cast vehicle APIs that the missing features all need. This issue is the spine; the features it unlocks are the issues that link back to it. **What it would buy** - A real vehicle: wheels, suspension, grip and rollover, instead of the hand-rolled speed-and-heading model. #111 - Objects that can be thrown, kicked and rolled: a football, a boule, a disc. #112 - Playground toys as mechanisms: a seesaw on a hinge, a swing on a pendulum. #113 - A body that can fall over instead of a body that slides along walls. #114 - Doors that swing, and a building you can walk through properly. #115 - Buoyancy: swimming, and things that float. #116 **The costs, honestly** - *It contradicts a written decision.* ARCHITECTURE.md says collision "is not a swept capsule" and is a per-building 2-D wall-distance predicate, because that keeps a walker on the right floor and never snaps a jump onto the slab above. Rapier's character controller works the other way: one shape, cast against the world. Getting the campus's storeys right means handing Rapier the same floor-aware geometry the predicate has today — as one collider per slab, or as collision groups — which is a bake change, not only a code change. - *Two vector vocabularies.* The project has one: `glam`, with `Vec2`/`Vec3` and the `Enu`/`WorldPos` newtypes. Rapier speaks `nalgebra`. The conversion lives at the edge of the physics module, but it exists, and every crossing pays for it. - *The multiplayer split gets a third case.* Players are client-authoritative and vehicles are server-authoritative today. A physics world both must agree on is something else: either the server owns every body and the 20 Hz pose carries the state, or each client simulates its own and the two drift. - *Determinism is opt-in.* Rapier is deterministic on one machine by default. Across machines it wants the `enhanced-determinism` feature, which cannot be combined with `simd8`. - *WASM gets no threads.* Rapier builds for `wasm32-unknown-unknown` and its own CI tests that target, but rayon falls back to one thread there, so the web build pays single-threaded cost. **Where it would live** Rapier is pure Rust and has no rendering dependency, so it belongs in `liuverse-world` beside `collision`, `vehicle` and `player` — the crate whose whole job is "the simulation, without the renderer", and whose tests are pure in-memory. It goes into `[workspace.dependencies]` like everything else. The static world is the hard part. Rapier wants colliders, and the campus is baked as triangle soups with footprints and rings. Two ways in: - *Trimesh colliders from the baked geometry.* Honest, and enormous: the whole campus as one trimesh is a lot of triangles for a BVH to chew on, and it collides the interiors with the exteriors. - *Analytic colliders from the bake.* One cuboid per floor slab, one per interior wall ribbon, a height field for the terrain, a trimesh for the roofs. The bake already knows how to produce all four. It is also what makes the storeys work: a slab is a collider, so climbing a stair is a slope and jumping under the next floor still cannot snap onto it. The second is the one that keeps the current behaviour, so it is the one to try first. It grows the bake a collision layer, which is a format change and therefore a rebake. **The smaller step, if this is too much** `parry3d` is the same author's collision library without the dynamics solver. It gives ray casts, shape casts and overlap queries — enough for picking and for a proper camera — at a fraction of the weight and with no authority questions. It is the right first move if the goal is *picking* and not *simulation*. **Related** - #111 the vehicles on real wheels - #112 the thrown and rolled bodies - #113 the playground mechanisms - #114 the ragdoll - #115 the doors - #116 the water - `parry3d` is the same author as `rapier3d`, one layer down. - ARCHITECTURE.md's collision and vehicle decisions are the ones that change.
sermuns added this to the Play milestone 2026-10-25 02:22:27 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Reference
elsie/liuverse#110
No description provided.