Contributing
June 20, 2026 ยท View on GitHub
- Extending Godot Physics
- How the Physics Server works with Godot Nodes
- Rapier Physics Server Serialization
- Rapier Physics Server Fluids
- How to build
- How to debug
- Hot to lint and format
If you just want to build locally, start from Chapter 5.
Extending Godot Physics
Godot let's any GDExtension create a new Physics Server if they call register_server on the PhysicsServer2DManager or PhysicsServer3DManager and on the callback return a new instance of their custom Physics Server that extends either PhysicsServer2DExtension or PhysicsServer3DExtension.
A physics server has to implement 3 classes:
- PhysicsServer2DExtension or PhysicsServer3DExtension
- PhysicsDirectBodyState2DExtension or PhysicsDirectBodyState3DExtension
- PhysicsDirectSpaceState2DExtension or PhysicsDirectSpaceState3DExtension
Note that these functions all have underscore in begining of function name. The flow for how Godot calls them is the function without underscore calls into th one with underscore, eg.:
body_create()
|
v
_body_create()
Godot can register multiple Physics Servers, and can decide at runtime which one to use.
Godot Rapier Physics implements these under the name like this:
- RapierPhysicsServer2D and RapierPhysicsServer3D
- RapierDirectBodyState2D and RapierDirectBodyState3D
- RapierDirectSpaceState2D and RapierDirectSpaceState3D
Code is reused as much as possible, by having each of these classes call into a generic class that does the implementation, and a few configuration flags to select dimensions, precision, etc.
The code written here is based on the Physics Server from Godot.
How the Physics Server works with Godot Nodes
The Physics Server is a singleton with a set of API's for bodies, areas, shapes and spaces. An example of this is body_create function. This function creates a body, and returns an RID. These RID's are resource id's for objects that are handled by the Physics Server.
Normally you wouldn't call these functions directly, but instead you would create a node that would call these functions. Eg. when you create a RigidBody2D node, this calls the following:
body_rid = body_create()
body_set_space(body_rid, space_rid)
body_set_state(body_rid, BODY_STATE_TRANSFORM, Transform2D())
...
Also, aside from communication from Godot -> Physics Server, there is also the reverse, where the Physics Server notifies Godot of updates (eg. collision events, etc.). For this, Godot first calls into the Physics Server's method, eg.:
These functions send a Callable to the Physics Server that it uses to call back into Godot and notify of certain changes.
Rapier Physics Server Serialization
The Rapier Physics Server, which is our custom implementation of Physics Server, hands out RID's for objects it has internally, similar to how Godot Physics Server does. The classes are named similar to how Godot naming works, but with Rapier instead of Godot, eg.:
RapierAreaRapierBodyRapierCollisionObjectBase- ...
The difference from Godot Physics Server is that resources are not using pointers, and as such are safe to be serialized.
However, there are some things that need to be reconstructed after saving and loading the state, namely:
CallableRIDinstance_idcanvas_instance_id
As described in the chapter above, the Physics Server creates some resources and gives Godot RID's that can be used to access them. Godot also gives some resources to the Physics Server, eg. Callable, that are used to notify Godot of updates (eg. collision events, etc.). Godot also gives to objects from Physics Server instance_id's and canvas_instance_id's in order to identify a node corresponds to a resource.
As such, when saving and loading, first one must recreate the Godot scene and make sure all the dependencies are created, and only later load the Physics Server state. Even if some of the things the Physics Server loads won't be the exact same, eg. RID's, Callable, etc. The simulation will work deterministic as internally it uses Rapier which is itself deterministic. Also the update order isn't based on these resources that Godot hands over.
Rapier Physics Server Fluids
Godot Rapier Physics also exposes new nodes, Fluid2D and Fluid3D. These nodes can set and get position, velocity and acceleration of particles used for fluid simulation. Internally it uses salva to create them.
How to build
- Prerequisites:
- Update dependencies to latest:
cargo update
- Build the project
# for 2d
cargo build --release --features="single-dim2"
# for 3d
cargo build --release --features="single-dim3"
- Copy the output to bin folder of the addon:
Eg. macOS
cp target/release/libgodot_rapier.dylib bin2d/addons/godot-rapier2d/bin/libgodot_rapier.macos.framework/libgodot_rapier.macos.dylib
Eg. Windows
cp target/release/godot_rapier.dll bin2d/addons/godot-rapier2d/bin/godot_rapier.windows.x86_64-pc-windows-msvc.dll
For the correct path to use inside the bin folder, look inside the bin2d/addons/godot-rapier2d.gdextension or the bin3d/addons/godot-rapier3d.gdextension.
Build script
Optionally, run the build script from scripts/build-dev-2d.sh or scripts/build-dev-3d.sh. (this also does linting)
Available features
For features, the following are available:
single-dim2single-dim3double-dim2double-dim3parallelsimd-stableenhanced-determinismserde-serialize
The single and double refer to the precision used in Godot.
The dim2 or dim3 refers to the rapier and salva versions used, and to what classes will be implemented for Godot and what Physics Server will be replaced(2d or 3d).
The parallel version doesn't work on web.
The enhanced-determinism usually slows down the simulation.
The serde-serialize feature enabled serialization methods for physics server.
How to debug
Run Godot from debugger inside VSCode
Click Run and Debug in VSCode on the left. Add a configuration to the launch.configurations similar to this (eg. below one is for macOS):
{
"name": "Launch",
"program": "path/to/godot/bin/godot.macos.editor.dev.arm64",
"type": "cppdbg",
"request": "launch",
"cwd": "${workspaceFolder:godot-rapier-2d}",
"osx": {
"MIMode": "lldb"
},
"args": [
"--path",
"path/to/project/folder",
"--debug-collisions",
"scene-name.tscn"
]
},
More on godot cli usage here.
How to lint
In order to make a PR that will get merged, you need to lint and format your code by running:
# Format
cargo fmt -- --config-path rustfmt.toml
# Run clippy for 2d only
cargo clippy --fix --allow-dirty --all-targets --features="single-dim2"
# Run clippy for 3d only
cargo clippy --fix --allow-dirty --all-targets --features="single-dim3"
The above runs formatting with cargo fmt and linting with cargo clippy. For clippy you can specify (and should run) both 2d and 3d feature sets.