Skip to main content

soft_body_sets

A soft-body is created by inserting a SoftBody component on an entity. This component only wraps the Rapier SoftBodyBuilder of the body, and it is read only once: the plugin creates the Rapier soft-body during the next physics update, then inserts its RapierSoftBodyHandle on the entity, as well as a SoftBodyState component updated after each step with the (mass-weighted) center of mass of the body, whether it is sleeping, its number of particles, etc. Therefore modifying the SoftBody component afterwards has no effect: the body is controlled at runtime by the other soft-body components (SoftBodyMaterial, SoftBodyPinnedParticles, etc.) described in the next sections. Adding the SoftBodyDisabled component disables the soft-body until it is removed, and despawning the entity (or removing its SoftBody component) removes the soft-body from the simulation.

The positions of the particles given to the builder are expressed in the local frame of the entity: they are transformed by its GlobalTransform when the soft-body is created. After that, the particles live in world-space, and the plugin drives the Transform of the entity: it follows the pose of the root body of the soft-body, i.e., its translation follows the centroid of the free particles, and its rotation is the one best fitting the particles onto their rest shape (the identity when the soft-body is created), whereas its scale is kept. The colliders of the children of the soft-body entity are attached to the root body, so these children follow the body as a whole. Whatever must follow a specific part of the body is better attached to a cluster entity.

The soft-body entity also stands for the rigid-body and the colliders the engine creates for it:

  • It is mapped to the root body of the soft-body, so it can be used like any rigid-body entity by the impulse joints.
  • The colliders of its surface carry the bits of the entity in their user-data, so the collision events, the contact pairs, and the scene queries report the soft-body entity itself as the collider entity.
  • Its collider components (Friction, Restitution, CollisionGroups, SolverGroups, ActiveEvents, ActiveHooks, ActiveCollisionTypes, ContactForceEventThreshold, ContactSkin, and Sensor) configure the colliders of its surface exactly like for a Collider: they are applied when the soft-body is created and whenever they change, and removing one of them restores the value of the collider template of the builder. These colliders are given by RapierRigidBodySet::soft_body_colliders.

Finally, the Rapier soft-bodies are stored in the soft_bodies set of the RapierRigidBodySet component of the physics context, next to the rigid-bodies. The Rapier soft-body type is re-exported as RapierSoftBody (and its material as RapierSoftBodyMaterial) to avoid any confusion with the components. They are accessed with the ReadRapierContext and WriteRapierContext system parameters, which provide shortcuts for the most common operations: soft_body gives the Rapier soft-body of an entity, soft_body_entity gives the entity of a soft-body handle, soft_body_particle_positions and soft_body_center_of_mass read its state, and soft_body_mut gives mutable access to the soft-body of an entity:

fn read_soft_bodies(context: ReadRapierContext, sheet: Single<Entity, With<Sheet>>) -> Result {
let context = context.single()?;
// The Rapier soft-bodies live in the `RapierRigidBodySet` of the context, together with the
// map from their entity to their handle (also given by their `RapierSoftBodyHandle`).
let Some(handle) = context.rigidbody_set.entity2soft_body().get(&*sheet) else {
return Ok(()); // Not created yet.
};
let soft_body: &RapierSoftBody = &context.rigidbody_set.soft_bodies[*handle];
// Shortcuts are provided for the most common operations.
assert_eq!(context.soft_body_entity(*handle), Some(*sheet));
assert_eq!(
context.soft_body_particle_positions(*sheet).unwrap().len(),
soft_body.num_particles()
);
let _center = context.soft_body_center_of_mass(*sheet);
// The colliders of its surface, configured by its collider components.
let surface_colliders = context
.rigidbody_set
.soft_body_colliders(&context.colliders.colliders, *sheet)
.unwrap_or_default();
for handle in surface_colliders {
let _friction = context.colliders.colliders[handle].friction();
}
Ok(())
}
warning

The properties covered by a component should be modified through that component rather than through soft_body_mut. The components are applied again whenever they change, so, e.g., a material set with RapierSoftBody::set_material is replaced by the SoftBodyMaterial of the entity the next time this component is modified.

info

The SoftBodyMeshSync component (from the to-bevy-mesh feature, enabled by default) renders the soft-body with a mesh kept in sync with its particles. The plugin generates the mesh and inserts it as a Mesh3d (3D) or Mesh2d (2D) component, updates its vertices after each step, and rebuilds it whenever the topology of the body changes, e.g., after a tear. Its vertices are expressed in the frame of the Transform of the entity, and the material must be added by you, as shown in the creation example. In 3D, the mesh is the skin of the body if it has one, else its surface (or its boundary triangles), or a line list of its edges for a body without any surface, e.g., a rope. In 2D, it is made of its cells, or of its boundary segments (or of its edges). The meshes of the deformable colliders bound to the body are never part of it.