Astra
July 19, 2026, 11:45am
1
Back when ES modules were still super secret beta stuff I made an integration of the beautiful Rapier physics engine into PlayCanvas as a reaction to finding several pretty severe bugs with the native Ammo engine, including memory leaks (see Changing default physics engine..? ).
Not sure if anyone shared it since ESM came out of beta, so I thought I’d do it here:
Rapier is very tightly optimized, has some extra useful features like a character controller, and is fully deterministic cross platforms! If you are planning on making a larger project or game with lots of scene changes or mesh colliders I’d very warmly recommend using Rapier instead of Ammo from the get-go.
3 Likes
Wagner
July 19, 2026, 12:30pm
2
You can port it as a kernel module, if you have the time.
This will be great news for the community.
main ← physics-backend-abstraction
opened 09:50PM - 06 Jul 26 UTC
## Description
Decouples the engine from ammo.js by introducing an internal phy… sics backend contract, as proposed in #2801: `PhysicsWorld`, `PhysicsBody` and `PhysicsJoint` base classes under `src/framework/physics/`, with all Ammo code ported behind them (`physics/ammo/`) and a no-op `NullPhysicsWorld` that lets the physics component lifecycle run in tests without a physics engine. Adding a new backend (Jolt, Rapier, a custom engine) is now a matter of implementing the three classes.
Closes #2801
**Zero public API change.** Component APIs, events and constants are untouched. Ammo is auto-selected exactly as before (`typeof Ammo` after WasmModule load) - now the only reference to the Ammo global outside `physics/ammo/`. Backends install through a single `@ignore`d seam, `RigidBodyComponentSystem#setPhysicsWorld()`; a public selection API (e.g. via `AppOptions`) is deferred until a second real backend lands. The unsupported native escape hatches keep returning raw Ammo objects (`rigidbody.body`, `dynamicsWorld`, `joint.constraint`, `collision.shape`), so `scripts/physics/vehicle.js` runs untouched.
Notes for reviewers:
- Shapes are opaque handles (the raw `btCollisionShape` for Ammo) created from typed descriptors; per-type creation inside the backend uses module-scope dispatch tables. Mesh sources expose vertex/index data through lazy accessors, so geometry cached per mesh id is never re-extracted.
- Contact reporting crosses the boundary via a listener (`onContactsBegin`/`onContactPair`/`onContactsEnd`) with one reused pair object; all pooling, collision maps and event firing stay in the system, and per-substep semantics (and the no-tick-callback fallback ordering) are preserved.
- Joint frames cross the boundary as scale-free `Mat4`s with X as the primary axis; the backend applies its native axis correction (bullet's hinge Z remap) in matrix space, keeping results identical to the previous single-sided computation.
- Behavior verified bit-exact against master with a real-Ammo A/B harness (~20 scenarios: falling/resting bodies, raycasts, contact/trigger event frame ordering and impulses, all 5 joint types, teleport interpolation from #8915, compound child moves, mesh/hull colliders, raw-vehicle escape hatches), plus all 7 physics examples in the browser and 26 new null-backend unit tests.
- Deliberate deviations, all internal: bt math temps are now per-world instead of cross-app statics (fixes a latent multi-app teardown bug); mesh scale-change rebuilds compare against the recorded build scale (removes redundant render-path rebuilds for non-unit scales); scene-settings gravity is applied without the Ammo-global gate; hinge motors use the last-stepped `fixedTimeStep`; the undocumented `system.createBody/destroyBody`, `collision.getCompoundChildShapeIndex` and `joint system getFixedBody` were removed (the world-anchor fixed body lives on the backend now).
Follow-ups: public backend-selection API, and flattening the `CollisionSystemImpl` class hierarchy (its `createPhysicalShape` methods are now one-liners).
## Checklist
- [x] I have read the [contributing guidelines](https://github.com/playcanvas/engine/blob/master/.github/CONTRIBUTING.md)
- [x] My code follows the project's coding standards
- [x] This PR focuses on a single change
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Astra
July 25, 2026, 1:12pm
3
OOoo, very neat!
Maybe I’ll have time in a month or two
Not gonna stop anyone else from doing it in the meantime though.
1 Like