The Rapier testbed
The Python bindings come with their own testbed: a small physics sandbox written in Python, based on the Panda3D renderer. It is designed to easily open a window and render a simulation, and provides basic controls like play/pause, single-stepping, restarting the scene, switching between scenes, or showing the wireframe of the colliders with the debug-renderer. It runs the Python ports of most of the 3D demos of Rapier, which are also good examples of usage of the bindings. It is generally the quickest way of prototyping a scene, or of reproducing a problem before opening an issue.
Installation
The testbed is published as the rapier-testbed package, which depends on rapier3d, NumPy, and Panda3D:
pip install rapier-testbed
It can also be installed from the bindings/python/rapier-testbed folder of the
Rapier repository, e.g., to use it with bindings
built from source. From the root of the repository, and with your
virtual environment activated (--no-deps keeps the bindings you built instead of downloading them, and -e makes
your modifications of the testbed effective without reinstalling it):
pip install panda3d numpy
pip install --no-deps -e ./bindings/python/rapier-testbed
Running the demos
The demos are the modules of the rapier_testbed.examples3 package (e.g. rapier_testbed/examples3/domino3.py).
Running the rapier_testbed module lists them by category in the terminal, and asks which one to open. A demo can
also be opened directly by running its module:
python -m rapier_testbed # List the demos, and open the selected one.
python -m rapier_testbed.examples3.domino3 # Open the domino demo.
The bindings/python/examples_tour.py script of the repository opens every demo in turn, each of them being opened when the
window of the previous one is closed.
Always run the testbed with bindings built in release mode (see common mistakes): a debug build is up to 100 times slower.
Controls
Space: play/pause. Right arrow: execute one timestep.R: restart the demo.Tab: open the next demo (the digits1to9open the demo with this number).W: toggle the wireframe of the colliders.Esc: close the demo.- Left drag: rotate the camera around its target. Right drag: pan the camera. Mouse wheel: zoom.
- The time spent by the simulation and by the rendering of each frame, as well as the frame rate, are displayed at the top of the window.
Headless runs
If the PANDA_NO_WINDOW environment variable is set to 1, the testbed runs the demo for a fixed number of timesteps
without opening any window, which is useful to check that a demo runs without errors (e.g. in a CI):
PANDA_NO_WINDOW=1 python -m rapier_testbed.examples3.domino3
Writing a scene
A scene is one function building a physics world, and giving it to the
testbed with Testbed.set_world. Unlike the testbed of the Rust version of Rapier, the testbed owns the simulation
loop: it steps the world it is given at each frame (with its gravity, integration parameters, physics hooks, and event
handler), and calls the functions registered by the scene with Testbed.add_callback after each timestep, e.g., to
control the scene or to read the results of the simulation:
def bouncing_ball(testbed):
# The scene itself, built like in any other application.
world = rp.PhysicsWorld(gravity=(0.0, -9.81, 0.0))
world.add_collider(rp.Collider.cuboid(100.0, 0.1, 100.0))
ball_handle = world.add_body(
rp.RigidBody.dynamic(translation=(0.0, 10.0, 0.0)),
colliders=[rp.Collider.ball(0.5).restitution(0.7)],
)
# Hand the world to the testbed, and place the camera.
testbed.set_world(world)
testbed.look_at((10.0, 10.0, 10.0), (0.0, 0.0, 0.0))
# A function called after each timestep, e.g., to control the scene.
def print_altitude(testbed):
print("Ball altitude:", world.rigid_bodies[ball_handle].translation.y)
testbed.add_callback(print_altitude)
The scene is then registered with a category and a name, which makes it available in the list of the testbed together with the demos, and the testbed can be opened on it directly:
from rapier_testbed import register, run
# The scenes listed by the picker of the testbed, as (category, name) pairs.
register("Demos", "Bouncing ball", bouncing_ball)
if __name__ == "__main__":
# Open the testbed directly on this scene.
run(initial="Demos / Bouncing ball")
Instead of a world, Testbed.set_world also accepts the sets of a simulation (the rigid-body, collider,
impulse-joint, and multibody-joint sets, and optionally the soft-body set), which the testbed then steps with its own
pipeline and integration parameters, and with a gravity of along the axis. In this case, the gravity, the
physics hooks, and the
event handler of the simulation are given
with Testbed.set_gravity, Testbed.set_hooks, and Testbed.set_event_handler.