soft_body_particles
The state of a soft-body is the state of its particles, which are identified by their index in the body. Their positions
and their velocities can be read (particle_position, particle_positions, particle_velocity,
particle_velocitiesparticlePosition, particlePositions, particleVelocity,
particleVelocitiesr3SoftBody_ParticlePosition, r3SoftBody_ParticlePositions, r3SoftBody_ParticleVelocity, r3SoftBody_ParticleVelocitiesparticle_position, particle_positions, particle_velocity, particle_velocitiesset_particle_position,
set_particle_velocitysetParticlePosition, setParticleVelocityr3SoftBody_SetParticlePosition, r3SoftBody_SetParticleVelocityset_particle_position, set_particle_velocityedges, cells, and boundaryedges, cells, and
boundaryr3SoftBody_Edges, r3SoftBody_Cells, and r3SoftBody_Boundaryedges, cells, and boundaryNULL buffer with a zero capacity only returns the length of the array. The elements are given as flat arrays of particle indices: 2 per edge, 3 (2D) or 4 (3D) per cell, and 2 (2D) or 3 (3D) per boundary element.(N, 3) for the positions and (B, 3) for the boundary triangles. All the positions (or velocities) are modified at once by assigning such an array to particle_positions (or particle_velocities). A snapshot of all the properties of a particle (its mass, its rest position, whether it is pinned, etc.) is given by particle.soft_body and soft_body_mut methods of the
physics context (see soft-bodies and entities). Note that the positions
are expressed in world-space, and not in the frame of the entity.
A particle can also be pinned (set_particle_pinnedSoftBodyPinnedParticles
componentsetParticlePinnedr3SoftBody_SetParticlePinnedset_particle_pinnedset_particle_kinematic_targetSoftBodyKinematicTargets
componentsetParticleKinematicTargetr3SoftBody_SetParticleKinematicTargetset_particle_kinematic_target
- Example 2D
- Example 3D
let soft_body = &mut world.soft_bodies[sheet_handle];
// Read the particles.
let position = soft_body.particle_position(0);
let velocity = soft_body.particle_velocity(0);
let positions: Vec<Vector> = soft_body.particle_positions().collect();
assert_eq!(positions.len(), soft_body.num_particles());
// Move a particle.
soft_body.set_particle_position(1, position + Vector::new(0.0, 0.1));
soft_body.set_particle_velocity(1, velocity);
// Pin (or release) a particle; a pinned particle can be driven like a kinematic body.
soft_body.set_particle_pinned(2, true);
soft_body.set_particle_kinematic_target(2, Vector::new(-3.5, 3.5));
// The elements: edges, cells and the boundary segments.
let num_edges = soft_body.edges().len();
let num_cells = soft_body.cells().len();
let boundary: &[[u32; 2]] = soft_body.boundary();
assert!(num_edges > 0 && num_cells > 0 && !boundary.is_empty());
let soft_body = &mut world.soft_bodies[cloth_handle];
// Read the particles.
let position = soft_body.particle_position(0);
let velocity = soft_body.particle_velocity(0);
let positions: Vec<Vector> = soft_body.particle_positions().collect();
assert_eq!(positions.len(), soft_body.num_particles());
// Move a particle.
soft_body.set_particle_position(1, position + Vector::new(0.0, 0.1, 0.0));
soft_body.set_particle_velocity(1, velocity);
// Pin (or release) a particle; a pinned particle can be driven like a kinematic body.
soft_body.set_particle_pinned(2, true);
soft_body.set_particle_kinematic_target(2, Vector::new(-1.0, 2.5, -0.8));
// The elements: edges, cells and the boundary triangles.
let num_edges = soft_body.edges().len();
let num_cells = soft_body.cells().len();
let boundary: &[[u32; 3]] = soft_body.boundary();
assert!(num_edges > 0 && num_cells == 0 && !boundary.is_empty());
- Example 2D
- Example 3D
// Read the particles.
let position = sheet.particlePosition(0);
let velocity = sheet.particleVelocity(0);
let positions: Float32Array = sheet.particlePositions(); // Two floats per particle.
console.log("The sheet has", sheet.numParticles(), "particles;", positions.length / 2);
// Move a particle.
sheet.setParticlePosition(1, { x: position.x, y: position.y + 0.1 });
sheet.setParticleVelocity(1, velocity);
// Pin (or release) a particle; a pinned particle can be driven like a kinematic body.
sheet.setParticlePinned(2, true);
sheet.setParticleKinematicTarget(2, { x: -3.5, y: 3.5 });
// The elements: edges (two indices each), cells (three) and the boundary segments (two).
let edges: Uint32Array = sheet.edges();
let cells: Uint32Array = sheet.cells();
let boundary: Uint32Array = sheet.boundary();
console.log(edges.length / 2, "edges,", cells.length / 3, "cells,", boundary.length / 2, "segments");
// Read the particles.
let position = cloth.particlePosition(0);
let velocity = cloth.particleVelocity(0);
let positions: Float32Array = cloth.particlePositions(); // Three floats per particle.
console.log("The cloth has", cloth.numParticles(), "particles;", positions.length / 3);
// Move a particle.
cloth.setParticlePosition(1, { x: position.x, y: position.y + 0.1, z: position.z });
cloth.setParticleVelocity(1, velocity);
// Pin (or release) a particle; a pinned particle can be driven like a kinematic body.
cloth.setParticlePinned(2, true);
cloth.setParticleKinematicTarget(2, { x: -1.0, y: 2.5, z: -0.8 });
// The elements: edges (two indices each), cells (four) and the boundary triangles (three).
let edges: Uint32Array = cloth.edges();
let cells: Uint32Array = cloth.cells();
let boundary: Uint32Array = cloth.boundary();
console.log(edges.length / 2, "edges,", cells.length / 4, "cells,", boundary.length / 3, "triangles");
The SoftBodyPinnedParticles component lists exactly the particles that are pinned: it replaces the
particles pinned by the builder, which are restored when the component is removed. The SoftBodyKinematicTargets
component gives world-space targets to some of the pinned particles: each time it changes, the listed particles are
moved to their target over the next step, then held there:
- Example 2D
- Example 3D
fn control_particles(
mut commands: Commands,
mut context: WriteRapierContext,
sheet: Single<Entity, With<Sheet>>,
) -> Result {
let mut context = context.single_mut()?;
let Some(soft_body) = context.soft_body_mut(*sheet) else {
return Ok(());
};
// Read the particles (in world-space).
let position = soft_body.particle_position(0);
let velocity = soft_body.particle_velocity(0);
let positions: Vec<Vec2> = soft_body.particle_positions().collect();
assert_eq!(positions.len(), soft_body.num_particles());
// Move a particle.
soft_body.set_particle_position(1, position + Vec2::new(0.0, 0.1));
soft_body.set_particle_velocity(1, velocity);
// The elements: edges, cells and the boundary segments.
let num_edges = soft_body.edges().len();
let num_cells = soft_body.cells().len();
let boundary: &[[u32; 2]] = soft_body.boundary();
assert!(num_edges > 0 && num_cells > 0 && !boundary.is_empty());
// Pin particles (exactly the listed ones), and drive the particle 2 kinematically.
commands.entity(*sheet).insert((
SoftBodyPinnedParticles(vec![0, 19, 2]),
SoftBodyKinematicTargets(vec![(2, Vec2::new(-3.5, 3.5))]),
));
Ok(())
}
fn control_particles(
mut commands: Commands,
mut context: WriteRapierContext,
cloth: Single<Entity, With<Cloth>>,
) -> Result {
let mut context = context.single_mut()?;
let Some(soft_body) = context.soft_body_mut(*cloth) else {
return Ok(());
};
// Read the particles (in world-space).
let position = soft_body.particle_position(0);
let velocity = soft_body.particle_velocity(0);
let positions: Vec<Vec3> = soft_body.particle_positions().collect();
assert_eq!(positions.len(), soft_body.num_particles());
// Move a particle.
soft_body.set_particle_position(1, position + Vec3::new(0.0, 0.1, 0.0));
soft_body.set_particle_velocity(1, velocity);
// The elements: edges, cells and the boundary triangles.
let num_edges = soft_body.edges().len();
let num_cells = soft_body.cells().len();
let boundary: &[[u32; 3]] = soft_body.boundary();
assert!(num_edges > 0 && num_cells == 0 && !boundary.is_empty());
// Pin particles (exactly the listed ones), and drive the particle 2 kinematically.
commands.entity(*cloth).insert((
SoftBodyPinnedParticles(vec![0, 19, 380, 399, 2]),
SoftBodyKinematicTargets(vec![(2, Vec3::new(-1.0, 2.5, -0.8))]),
));
Ok(())
}
- Example 2D
- Example 3D
// Read the particles.
R2Vector position = r2SoftBody_ParticlePosition(sheet_handle, 0);
R2Vector velocity = r2SoftBody_ParticleVelocity(sheet_handle, 0);
size_t num_particles = r2SoftBody_NumParticles(sheet_handle);
R2Vector *positions = malloc(num_particles * sizeof(R2Vector));
r2SoftBody_ParticlePositions(sheet_handle, positions, num_particles);
// Move a particle.
r2SoftBody_SetParticlePosition(sheet_handle, 1, r2VectorAdd(position, r2Vector(0.0, 0.1)));
r2SoftBody_SetParticleVelocity(sheet_handle, 1, velocity);
// Pin (or release) a particle; a pinned particle can be driven like a kinematic body.
r2SoftBody_SetParticlePinned(sheet_handle, 2, 1);
r2SoftBody_SetParticleKinematicTarget(sheet_handle, 2, r2Vector(-3.5, 3.5));
// The elements: edges, cells and the boundary segments, as flat arrays of particle indices
// (2, 3, and 2 indices per element). A NULL buffer with a zero capacity gives their length.
size_t num_edges = r2SoftBody_Edges(sheet_handle, NULL, 0) / 2;
size_t num_cells = r2SoftBody_Cells(sheet_handle, NULL, 0) / 3;
size_t boundary_len = r2SoftBody_Boundary(sheet_handle, NULL, 0);
uint32_t *boundary = malloc(boundary_len * sizeof(uint32_t));
r2SoftBody_Boundary(sheet_handle, boundary, boundary_len);
assert(num_edges > 0 && num_cells > 0 && boundary_len > 0);
free(positions);
free(boundary);
// Read the particles.
R3Vector position = r3SoftBody_ParticlePosition(cloth_handle, 0);
R3Vector velocity = r3SoftBody_ParticleVelocity(cloth_handle, 0);
size_t num_particles = r3SoftBody_NumParticles(cloth_handle);
R3Vector *positions = malloc(num_particles * sizeof(R3Vector));
r3SoftBody_ParticlePositions(cloth_handle, positions, num_particles);
// Move a particle.
r3SoftBody_SetParticlePosition(cloth_handle, 1, r3VectorAdd(position, r3Vector(0.0, 0.1, 0.0)));
r3SoftBody_SetParticleVelocity(cloth_handle, 1, velocity);
// Pin (or release) a particle; a pinned particle can be driven like a kinematic body.
r3SoftBody_SetParticlePinned(cloth_handle, 2, 1);
r3SoftBody_SetParticleKinematicTarget(cloth_handle, 2, r3Vector(-1.0, 2.5, -0.8));
// The elements: edges, cells and the boundary triangles, as flat arrays of particle indices
// (2, 4, and 3 indices per element). A NULL buffer with a zero capacity gives their length.
size_t num_edges = r3SoftBody_Edges(cloth_handle, NULL, 0) / 2;
size_t num_cells = r3SoftBody_Cells(cloth_handle, NULL, 0) / 4;
size_t boundary_len = r3SoftBody_Boundary(cloth_handle, NULL, 0);
uint32_t *boundary = malloc(boundary_len * sizeof(uint32_t));
r3SoftBody_Boundary(cloth_handle, boundary, boundary_len);
assert(num_edges > 0 && num_cells == 0 && boundary_len > 0);
free(positions);
free(boundary);
soft_body = world.soft_bodies[cloth_handle]
# Read the particles.
position = soft_body.particle_position(0)
velocity = soft_body.particle_velocity(0)
# All the positions (or velocities) at once, as an (N, 3) NumPy array.
positions = soft_body.particle_positions
assert positions.shape == (soft_body.num_particles, 3)
# Move a particle.
soft_body.set_particle_position(1, position + rp.Vec3(0.0, 0.1, 0.0))
soft_body.set_particle_velocity(1, velocity)
# Pin (or release) a particle; a pinned particle can be driven like a kinematic body.
soft_body.set_particle_pinned(2, True)
soft_body.set_particle_kinematic_target(2, (-1.0, 2.5, -0.8))
# The elements, as NumPy arrays of particle indices: edges, cells and the boundary triangles.
edges = soft_body.edges # Shape (E, 2).
cells = soft_body.cells # Shape (C, 4).
boundary = soft_body.boundary # Shape (B, 3).
assert len(edges) > 0 and len(cells) == 0 and len(boundary) > 0
Setting the position of a particle explicitly teleports it: no contact is taken into account along the way, so a particle can be moved inside of another object this way. Whenever the motion must be seen by the contacts and by the friction (to drag a piece of cloth, for example), it is recommended to pin the particle and to give it a kinematic target instead.
Controlling a region kinematically
A whole region of the body is controlled at once through a cluster covering it. Pinning
the cluster (set_cluster_pinnedSoftBodyClusterPinned
componentsetClusterPinnedr3SoftBody_SetClusterPinnedset_cluster_pinnedset_cluster_kinematic_targetSoftBodyClusterKinematicTarget component, a world-space
Transform which also pins the cluster when it is insertedsetClusterKinematicTargetr3SoftBody_SetClusterKinematicTargetset_cluster_kinematic_target
- Example 2D
- Example 3D
// Pin every particle of the cluster, then move it along a path: the cluster behaves like a
// kinematic rigid part dragging the rest of the body.
let jelly = &mut world.soft_bodies[jelly_handle];
jelly.set_cluster_pinned(cluster, true);
jelly.set_cluster_kinematic_target(cluster, Pose::from_translation(Vector::new(3.0, 2.5)));
// Release it: the cluster is simulated again.
jelly.set_cluster_pinned(cluster, false);
// Pin every particle of the cluster, then move it along a path: the cluster behaves like a
// kinematic rigid part dragging the rest of the body.
let jelly = &mut world.soft_bodies[jelly_handle];
jelly.set_cluster_pinned(cluster, true);
jelly.set_cluster_kinematic_target(
cluster,
Pose::from_translation(Vector::new(3.0, 2.0, 0.0)),
);
// Release it: the cluster is simulated again.
jelly.set_cluster_pinned(cluster, false);
- Example 2D
- Example 3D
// Pin every particle of the cluster, then move it along a path: the cluster behaves like a
// kinematic rigid part dragging the rest of the body.
jelly.setClusterPinned(cluster, true);
jelly.setClusterKinematicTarget(cluster, { x: 3.0, y: 2.5 }, 0.0);
// Release it: the cluster is simulated again.
jelly.setClusterPinned(cluster, false);
// Pin every particle of the cluster, then move it along a path: the cluster behaves like a
// kinematic rigid part dragging the rest of the body.
jelly.setClusterPinned(cluster, true);
jelly.setClusterKinematicTarget(cluster, { x: 3.0, y: 2.0, z: 0.0 }, { w: 1.0, x: 0.0, y: 0.0, z: 0.0 });
// Release it: the cluster is simulated again.
jelly.setClusterPinned(cluster, false);
- Example 2D
- Example 3D
fn drive_cluster(mut commands: Commands, cluster: Single<Entity, With<PlateCluster>>) {
// Pin every particle of the cluster (the target inserts `SoftBodyClusterPinned`), and move
// it to a world-space pose: the cluster behaves like a kinematic rigid part dragging the
// rest of the body.
commands
.entity(*cluster)
.insert(SoftBodyClusterKinematicTarget(Transform::from_xyz(
3.0, 2.5, 0.0,
)));
}
fn release_cluster(mut commands: Commands, cluster: Single<Entity, With<PlateCluster>>) {
// Release it: the cluster is simulated again.
commands
.entity(*cluster)
.remove::<(SoftBodyClusterKinematicTarget, SoftBodyClusterPinned)>();
}
fn drive_cluster(mut commands: Commands, cluster: Single<Entity, With<PlateCluster>>) {
// Pin every particle of the cluster (the target inserts `SoftBodyClusterPinned`), and move
// it to a world-space pose: the cluster behaves like a kinematic rigid part dragging the
// rest of the body.
commands
.entity(*cluster)
.insert(SoftBodyClusterKinematicTarget(Transform::from_xyz(
3.0, 2.0, 0.0,
)));
}
fn release_cluster(mut commands: Commands, cluster: Single<Entity, With<PlateCluster>>) {
// Release it: the cluster is simulated again.
commands
.entity(*cluster)
.remove::<(SoftBodyClusterKinematicTarget, SoftBodyClusterPinned)>();
}
- Example 2D
- Example 3D
// Pin every particle of the cluster, then move it along a path: the cluster behaves like a
// kinematic rigid part dragging the rest of the body.
r2SoftBody_SetClusterPinned(jelly_handle, cluster, 1);
r2SoftBody_SetClusterKinematicTarget(jelly_handle, cluster, r2TranslationPose(r2Vector(3.0, 2.5)));
// Release it: the cluster is simulated again.
r2SoftBody_SetClusterPinned(jelly_handle, cluster, 0);
// Pin every particle of the cluster, then move it along a path: the cluster behaves like a
// kinematic rigid part dragging the rest of the body.
r3SoftBody_SetClusterPinned(jelly_handle, cluster, 1);
r3SoftBody_SetClusterKinematicTarget(jelly_handle, cluster, r3TranslationPose(r3Vector(3.0, 2.0, 0.0)));
// Release it: the cluster is simulated again.
r3SoftBody_SetClusterPinned(jelly_handle, cluster, 0);
# Pin every particle of the cluster, then move it along a path: the cluster behaves like a
# kinematic rigid part dragging the rest of the body.
jelly = world.soft_bodies[jelly_handle]
jelly.set_cluster_pinned(cluster, True)
jelly.set_cluster_kinematic_target(cluster, rp.Isometry3(translation=(3.0, 2.0, 0.0)))
# Release it: the cluster is simulated again.
jelly.set_cluster_pinned(cluster, False)