Scene loaders
Rapier provides companion crates converting some popular robotics formats into the
rigid-bodies, the colliders, and the joints they contain. Note that these crates are 3D only, and that they are
released separately from rapier3d itself.
These crates are integrated into bevy_rapier3d behind the urdf, mjcf, and
meshloader cargo features (see the bevy_rapier3d::loaders module). Instead of inserting anything into the physics
context directly, the loaders spawn regular entities with rigid-body, collider, and joint components. Therefore
the spawned objects can be modified, queried, or despawned like any other entity.
The scene loaders are not available in bevy_rapier2d.
URDF
The rapier3d-urdf crate loads the Unified Robot Description Format
used by the ROS community. A robot is read from a URDF file (or from a string), and is then spawned as entities either with impulse joints or with multibody joints.
Robotics generally care a lot about joint violation. Then inserting the model using multibody joints is strongly recommended since they guarantee the absence of joint violation by encoding the locked degrees of freedom into the equations of motion directly instead of relying on the constraints solver (which might not converge).
The robot is read as an UrdfModel, and spawned with spawn_urdf_robot. The UrdfSpawnOptions select the kind of
joints (impulse joints by default), the transform of the root entity every link is a child of, and the physics
context the robot is added to. Each link becomes a rigid-body entity (with an UrdfLinkId component), each of its
shapes a collider entity child of the link, and each joint an ImpulseJoint or a MultibodyJoint component on the
entity of its child link. The returned SpawnedUrdfRobot lists all these entities, and finds them by their URDF name:
// Read the robot, then spawn its links and joints as entities.
let model = UrdfModel::from_file("robot.urdf", UrdfLoaderOptions::default(), None)?;
let robot = spawn_urdf_robot(
&mut commands,
&model,
&UrdfSpawnOptions {
multibody: true,
multibody_options: UrdfMultibodyOptions::DISABLE_SELF_CONTACTS,
// URDF files are generally Z-up, whereas Bevy is Y-up.
root_transform: Transform::from_rotation(Quat::from_rotation_x(-FRAC_PI_2)),
..default()
},
);
println!("The robot has {} links.", robot.links.len());
let _elbow = robot.joints_by_name["elbow"];
The visual elements of the links are not rendered: they are given by the UrdfLinkVisuals component so you can
attach your own meshes to the link entities. On top of what rapier3d-urdf converts, the <dynamics> (damping and
friction) and <mimic> elements of the joints are applied to multibody joints (as MultibodyJointDamping,
MultibodyJointFriction, and MultibodyJointCouplings components), but they are ignored by impulse joints.
The
urdf feature of bevy_rapier3d also enables the loading of the .stl, .dae, and .obj meshes referenced by an
URDF file. Note that a joint inserted as a multibody joint is reset to its neutral position, i.e., all of its
coordinates are zero.
MJCF
The rapier3d-mjcf crate loads the MJCF XML format of MuJoCo.
We recommend browsing the MuJoCo Menagerie repository which
contains many MJCF models.
The model is read as an MjcfRobot, and spawned with spawn_mjcf_model. Unlike the URDF loader, its joints are
spawned as multibody joints by default (like in MuJoCo), which can be changed with MjcfSpawnOptions::multibody. The
returned SpawnedMjcfModel lists the spawned entities, and finds them by their MJCF name. Note that the gravity
declared by the model isn't applied automatically:
// Read the model, then spawn its bodies, joints, and actuators as entities.
let (robot, _model) = MjcfRobot::from_file("robot.xml", MjcfLoaderOptions::default())?;
let model = spawn_mjcf_model(
&mut commands,
&robot,
&MjcfSpawnOptions {
// MJCF files are Z-up, whereas Bevy is Y-up.
root_transform: Transform::from_rotation(Quat::from_rotation_x(-FRAC_PI_2)),
..default()
},
);
// The gravity of the model isn't applied automatically.
configurations.single_mut()?.gravity = model.gravity;
Two more elements of MJCF need your app to be set up accordingly. The contact rules of the model
(<contact><exclude>, and the friction of <contact><pair>) are registered in the MjcfContactFilters resource
and applied by the MjcfPhysicsHooks physics hooks (if you have
your own hooks, call MjcfContactFilters::filter_contact_pair and MjcfContactFilters::modify_solver_contacts from
them instead). Each actuator of the model is spawned as an entity with an MjcfActuator component, which drives the
motor of its joint once the MjcfPlugin is added:
App::new()
.add_plugins((
DefaultPlugins,
// The hooks apply the `<contact>` rules of the MJCF models.
RapierPhysicsPlugin::<MjcfPhysicsHooks>::default(),
// Applies the controls of the actuators to the joints they drive.
MjcfPlugin::default(),
))
The actuators are then controlled by setting their MjcfActuator::ctrl input:
fn drive_actuators(time: Res<Time>, mut actuators: Query<(&Name, &mut MjcfActuator)>) {
for (name, mut actuator) in actuators.iter_mut() {
if name.as_str() == "cart_motor" {
actuator.ctrl = time.elapsed_secs().sin();
}
}
}
Note that MJCF sometimes describes a whole simulation and not only a scene, therefore some of its elements have no equivalent in Rapier, and some others are approximated.
Meshes
The rapier3d-meshloader crate builds shapes from the usual mesh
files, which is what both the MJCF and URDF loaders use internally. It is useful on its own whenever the collision
geometry of a scene comes from an asset file rather than from primitive shapes. The formats it reads are selected by its
cargo features: stl, collada, and wavefront. Each mesh of the file becomes one shape, converted as selected by the MeshConverter:
// All the meshes of the file are combined into a single collider, each of them being
// converted here into its convex hull.
let collider = Collider::from_mesh_file("asset.obj", &MeshConverter::ConvexHull, Vec3::ONE)?;
commands.spawn((Transform::default(), RigidBody::Dynamic, collider));
Collider::from_mesh_file combines all the meshes of the file into a single collider (with a compound shape if there
are several of them). Use load_mesh_file_colliders instead to get one collider per mesh, together with its pose,
the mesh itself, and its material (which can be converted into a Bevy mesh for rendering with raw_mesh_to_bevy_mesh
if the to-bevy-mesh feature is enabled):
// Every mesh of the file becomes its own collider.
let parts = load_mesh_file_colliders("asset.obj", &MeshConverter::TriMesh, Vec3::ONE)?;
for part in parts {
commands.spawn((part.transform, part.collider));
}