rapier-viewport-plugin
[Rapier 3D][rapier] physics as a [viewport-lib][vp] runtime plugin.
This is a small adapter crate. It does two things:
- Implements viewport-lib's
RuntimePlugintrait so rapier runs inside aViewportRuntimealongside any other plugin, on its fixed-timestep loop. - Keeps a
NodeId <-> Handlemap so you address bodies by the sameviewport_lib::NodeIdyou use for scene nodes everywhere else.
Everything else is rapier, used directly through its own builders. The crate re-exports rapier3d and parry3d at the crate root so you don't manage those dependencies separately. It does not wrap the rapier API: you build bodies and colliders with rapier's RigidBodyBuilder / ColliderBuilder and reach the rest of rapier through the with_* methods below.
[rapier]: https://github.com/dimforge/rapier
[vp]: https://github.com/grimandgreedy/viewport-lib
Using it
use ;
use ;
// 1. Create the handle and register bodies.
let mut rapier = new;
let floor_body = fixed.build;
let floor_collider = new.build;
rapier.add_body;
let ball_body = dynamic
.translation
.build;
let ball_collider = ball
.restitution
.active_events
.build;
rapier.add_body;
// 2. Split into the two RuntimePlugins the runtime needs. Keep `rapier`
// around to add/remove bodies later.
let = rapier.clone.into_plugins;
let mut runtime = new
.with_fixed_timestep
.with_plugin
.with_plugin;
// 3. Per frame: step the runtime, drain physics events.
let output = runtime.step;
for ev in output.events.
The two plugins do their work automatically:
RapierPreparePluginruns in thePreparephase and copies the scene transform of everyFixedorKinematicPositionbody into rapier. Move a kinematic node withscene.set_local_transform(...)and it pushes dynamic bodies in the next step.RapierSimulatePluginruns in theSimulatephase, callsPhysicsPipeline::step, writes every dynamic body's new transform back to the scene, and translates rapier'sCollisionEvents into NodeId-keyedRapierContactEvents on the runtime output bus.
Reaching the rest of rapier
The handle has a few lifecycle methods plus three ways to get at rapier's own types directly. Together they reach the entire rapier API.
| Access | Methods | Use when |
|---|---|---|
| Lifecycle | add_body, remove_body, clear_bodies, body_handle, collider_handle, into_plugins |
Registering bodies and looking up handles; the common path |
| Per body | `with_body_mut(node, | b |
| Full state | `with_state_mut( | s |
Apply an impulse:
rapier.with_body_mut;
Add a revolute joint, using body_handle to translate NodeIds to rapier
handles:
use *;
let h_parent = rapier.body_handle.unwrap;
let h_child = rapier.body_handle.unwrap;
rapier.with_state_mut;
Raycast through the broad phase:
use ;
let hit_node = rapier.with_state;
Examples
Eight examples cover the things you'd typically wire up:
| Example | What it shows |
|---|---|
falling_stack |
Dynamic bodies, gravity, a halfspace floor, transform writeback to the scene |
bouncing_spheres |
Restitution, multiple simultaneous contacts, draining RapierContactEvents from the runtime output |
kinematic_pusher |
Driving a KinematicPosition body's transform from input each frame; rapier's implicit velocity pushes dynamic balls |
ray_probe |
Camera-forward raycast through with_state and BroadPhaseBvh::as_query_pipeline. Click or space to mark the hit body |
joint_chain |
A 5-link revolute-joint chain: body_handle to translate NodeIds, with_state_mut to insert into ImpulseJointSet, with_body_mut to apply impulses |
sensor_trigger |
Trigger-volume detection: ColliderBuilder::sensor(true) for a ghost collider that emits events without contact forces |
collision_groups |
Layer-based collision filtering with InteractionGroups (32-bit membership / filter bitmasks) |
character_controller |
Keyboard-driven character using rapier's KinematicCharacterController::move_shape(...) through with_state. Walks around fixed box obstacles |
Run any of them:
cargo run --example falling_stack
cargo run --example bouncing_spheres
cargo run --example kinematic_pusher
cargo run --example ray_probe
cargo run --example joint_chain
cargo run --example sensor_trigger
cargo run --example collision_groups
cargo run --example character_controller
Not shown by an example yet
These rapier features work through the plugin (everything is reachable via with_state) but don't have a dedicated example yet:
- Other joint types (Fixed, Prismatic, Spherical, Rope/Distance, Generic 6-DOF, motors and limits)
DynamicRayCastVehicleController- Compound colliders,
Heightfield,TriMesh,ConvexHull - Continuous collision detection for tunnelling prevention
- Locked translation / rotation axes
- Continuous forces / torques (rather than the impulse path)
- Sleeping / islands inspection
PhysicsPipeline::countersfor step diagnostics
The patterns above generalise: joint variants follow joint_chain, vehicle and character follow character_controller, continuous forces follow the with_body_mut pattern in joint_chain's kick().
License
viewport-lib-wind is a part of the viewport-lib ecosystem.
GPL-3.0