soft_body_creation_and_insertion
A soft-body is described by a SoftBodyBuilderSoftBodyBuilder (wrapped by the SoftBody
component)SoftBodyDescR3SoftBodyDescSoftBodyBuilderclothTube)SoftBody class, e.g., SoftBody.cloth)
| Constructor | Dimension | Lattice |
|---|---|---|
rope | 2D, 3D | Structural and bending edges between the particles of a line. |
cloth, cloth_anisotropic, cloth_tube | 3D | Structural, shear and bending edges, with a triangle surface. |
grid | 2D | Triangle cells filling a rectangle. |
cuboid | 3D | Tetrahedral cells filling a box. |
polygon, disk | 2D | A closed boundary preserving its area. |
sphere | 3D | A closed surface preserving its volume, with dihedral bending constraints. |
trimesh, polyline | 2D, 3D (trimesh), 2D (polyline) | The vertices and edges of a mesh, held by shape matching. |
volumetric | 2D, 3D | Cells filling a closed mesh. |
| Constructor | Dimension | Lattice |
|---|---|---|
r3DefaultSoftBodyDesc, then r3SoftBodyDesc_SetParticles | 2D, 3D | No element at all: only the given particles. |
r3RopeSoftBodyDesc | 2D, 3D | Structural and bending edges between the particles of a line. |
r3ClothSoftBodyDesc, r3ClothAnisotropicSoftBodyDesc, r3ClothTubeSoftBodyDesc | 3D | Structural, shear and bending edges, with a triangle surface. |
r2GridSoftBodyDesc | 2D | Triangle cells filling a rectangle. |
r3CuboidSoftBodyDesc | 3D | Tetrahedral cells filling a box. |
r2PolygonSoftBodyDesc, r2DiskSoftBodyDesc | 2D | A closed boundary preserving its area. |
r3SphereSoftBodyDesc | 3D | A closed surface preserving its volume, with dihedral bending constraints. |
r3SoftBodyDesc_SetSurfaceMesh | 2D (segments), 3D (triangles) | The vertices and edges of a mesh, held by shape matching. |
r2SoftBodyDesc_SetTrimesh | 2D | The vertices and edges of a triangle mesh, held by shape matching. |
r3VolumetricSoftBodyDesc | 2D, 3D | Cells filling a closed mesh. |
The constructors return a description initialized with the default values of every other field, which can then be
modified before its insertion. The ones existing in a single dimension are only given with their prefix in that
dimension, e.g., there is no r3GridSoftBodyDesc.
| Constructor | Lattice |
|---|---|
SoftBodyBuilder(positions) | No element at all: only the given particles. |
SoftBody.rope | Structural and bending edges between the particles of a line. |
SoftBody.cloth, SoftBody.cloth_anisotropic, SoftBody.cloth_tube | Structural, shear and bending edges, with a triangle surface. |
SoftBody.cuboid | Tetrahedral cells filling a box. |
SoftBody.sphere | A closed surface preserving its volume, with dihedral bending constraints. |
SoftBody.trimesh | The vertices and edges of a triangle mesh, held by shape matching. |
SoftBody.volumetric, SoftBody.volumetric_with | Cells filling a closed mesh. |
Every constructor returns a SoftBodyBuilder, which setters return a modified copy of the
builder so they can be chained. These setters can also be given as keyword arguments of the constructors, e.g.,
SoftBody.rope(a, b, 20, particle_mass=0.1, pinned_particles=[0]).
The SoftBody component provides shortcuts for most of these constructors (SoftBody::rope,
SoftBody::cloth, SoftBody::cuboid, SoftBody::grid, SoftBody::volumetric, etc.), which positions are expressed in
the local frame of the entity: the cuboid, sphere, grid, and disk shortcuts are centered at its origin. The
other constructors are used with SoftBody::new(SoftBodyBuilder::...), and the methods of the builder are chained with
SoftBody::map, e.g., SoftBody::rope(a, b, 20).map(|b| b.particle_mass(0.1)).
The volumetric constructorr3VolumetricSoftBodyDesc constructorvolumetric_with: the size of the cells, how much the cover is smoothed and subdivided around the boundary in
3D, i.e., how closely it follows the mesh, and whether the surface alone is covered, leaving the interior
empty.R3VolumeMeshParameters, initialized by r3NewVolumeMeshParameters from the size of the cells: in 2D, min_angle is the minimum angle of the triangles, whereas in 3D, cover_smoothing and cover_subdivisions control how much the cover is smoothed and subdivided around the boundary, i.e., how closely it follows the mesh, and enclosure whether the surface alone is covered (1), leaving the interior empty. Note that the mesh is only filled by the insertion, which reports an error if the mesh isn't closed or encloses nothing at that cell size.volumetric_with, which takes a VolumeMeshParameters: the size of the cells (cell_size), how much the cover is smoothed (cover_smoothing) and subdivided (cover_subdivisions) around the boundary, i.e., how closely it follows the mesh, and whether the surface alone is covered (enclosure set to MeshEnclosure.CRUST), leaving the interior empty. Both constructors raise a MeshConversionError if the mesh isn't closed, or encloses nothing at that cell size.
- Example 2D
- Example 3D
// Fill a closed, counter-clockwise polyline with triangle cells of about 0.2 in size.
let vertices = vec![
Vector::new(-0.5, -0.25),
Vector::new(0.5, -0.25),
Vector::new(0.5, 0.25),
Vector::new(-0.5, 0.25),
];
let indices = vec![[0, 1], [1, 2], [2, 3], [3, 0]];
let block = SoftBodyBuilder::volumetric(&vertices, &indices, 0.2)
.expect("the polyline must be closed and enclose some area")
.translated(Vector::new(-3.0, 1.0));
let _block_handle = world.insert_soft_body(block);
// Fill a closed, outward-oriented triangle mesh with tetrahedral cells of about 0.2 in size.
let (vertices, indices) = Cuboid::new(Vector::new(0.5, 0.25, 0.25)).to_trimesh();
let block = SoftBodyBuilder::volumetric(&vertices, &indices, 0.2)
.expect("the mesh must be closed and enclose some volume")
.translated(Vector::new(-3.0, 1.0, 0.0));
let _block_handle = world.insert_soft_body(block);
- Example 2D
- Example 3D
// Fill a closed, counter-clockwise polyline (here a rectangle) with triangle cells of about
// 0.2 in size.
let boxVertices = new Float32Array([-0.5, -0.25, 0.5, -0.25, 0.5, 0.25, -0.5, 0.25]);
let boxIndices = new Uint32Array([0, 1, 1, 2, 2, 3, 3, 0]);
let blockDesc = RAPIER.SoftBodyDesc.volumetric(boxVertices, boxIndices, 0.2);
let block = world.createSoftBody(blockDesc.setTranslation({ x: -3.0, y: 1.0 }));
// Fill a closed, outward-oriented triangle mesh (here a box) with tetrahedral cells of
// about 0.2 in size.
let boxVertices = new Float32Array([
-0.5, -0.25, -0.25, 0.5, -0.25, -0.25, 0.5, 0.25, -0.25, -0.5, 0.25, -0.25,
-0.5, -0.25, 0.25, 0.5, -0.25, 0.25, 0.5, 0.25, 0.25, -0.5, 0.25, 0.25,
]);
let boxIndices = new Uint32Array([
0, 2, 1, 0, 3, 2, 4, 5, 6, 4, 6, 7, 0, 1, 5, 0, 5, 4,
3, 7, 6, 3, 6, 2, 0, 4, 7, 0, 7, 3, 1, 2, 6, 1, 6, 5,
]);
let blockDesc = RAPIER.SoftBodyDesc.volumetric(boxVertices, boxIndices, 0.2);
let block = world.createSoftBody(blockDesc.setTranslation({ x: -3.0, y: 1.0, z: 0.0 }));
- Example 2D
- Example 3D
// Fill a closed, counter-clockwise polyline with triangle cells of about 0.2 in size.
let vertices = vec![
Vec2::new(-0.5, -0.25),
Vec2::new(0.5, -0.25),
Vec2::new(0.5, 0.25),
Vec2::new(-0.5, 0.25),
];
let indices = vec![[0, 1], [1, 2], [2, 3], [3, 0]];
let block = SoftBody::volumetric(&vertices, &indices, 0.2)
.expect("the polyline must be closed and enclose some area");
commands.spawn((Transform::from_xyz(-3.0, 1.0, 0.0), block));
// Fill a closed, outward-oriented triangle mesh with tetrahedral cells of about 0.2 in size.
let (vertices, indices) = CuboidShape::new(Vec3::new(0.5, 0.25, 0.25)).to_trimesh();
let block = SoftBody::volumetric(&vertices, &indices, 0.2)
.expect("the mesh must be closed and enclose some volume");
commands.spawn((Transform::from_xyz(-3.0, 1.0, 0.0), block));
- Example 2D
- Example 3D
// Fill a closed, counter-clockwise polyline with triangle cells of about 0.2 in size.
const R2Vector vertices[] = {
r2Vector(-0.5, -0.25),
r2Vector(0.5, -0.25),
r2Vector(0.5, 0.25),
r2Vector(-0.5, 0.25),
};
const R2Edge indices[] = {{0, 1}, {1, 2}, {2, 3}, {3, 0}};
R2SoftBodyDesc block = r2VolumetricSoftBodyDesc((R2VectorView){vertices, 4}, (R2EdgeView){indices, 4},
r2NewVolumeMeshParameters(0.2));
block.translation = r2Vector(-3.0, 1.0);
// The polyline is only read during the insertion, which fails if it isn't closed.
R2SoftBodyHandle block_handle = r2InsertSoftBody(world, &block);
// Fill a closed, outward-oriented triangle mesh with tetrahedral cells of about 0.2 in size.
// The triangle mesh of a cuboid (the subdivision counts only matter for curved shapes).
R3SharedShape *cuboid = r3CuboidSharedShape(r3Vector(0.5, 0.25, 0.25));
R3TriMeshData *mesh = r3SharedShape_ToTrimesh(cuboid, 0, 0);
size_t num_vertices = r3TriMeshData_Vertices(mesh, NULL, 0);
size_t num_indices = r3TriMeshData_Indices(mesh, NULL, 0);
R3Vector *vertices = malloc(num_vertices * sizeof(R3Vector));
R3Triangle *triangles = malloc(num_indices * sizeof(uint32_t));
r3TriMeshData_Vertices(mesh, vertices, num_vertices);
r3TriMeshData_Indices(mesh, (uint32_t *)triangles, num_indices);
R3SoftBodyDesc block = r3VolumetricSoftBodyDesc((R3VectorView){vertices, num_vertices},
(R3TriangleView){triangles, num_indices / 3},
r3NewVolumeMeshParameters(0.2));
block.translation = r3Vector(-3.0, 1.0, 0.0);
// The mesh is only read during the insertion, which fails if it isn't closed.
R3SoftBodyHandle block_handle = r3InsertSoftBody(world, &block);
free(vertices);
free(triangles);
r3FreeTriMeshData(mesh);
r3FreeSharedShape(cuboid);
# Fill a closed, outward-oriented triangle mesh with tetrahedral cells of about 0.2 in size;
# this raises a `MeshConversionError` if the mesh isn't closed or encloses no volume.
vertices, indices = rp.Cuboid((0.5, 0.25, 0.25)).to_trimesh()
block = rp.SoftBody.volumetric(vertices, indices, 0.2).translated((-3.0, 1.0, 0.0))
block_handle = world.add_soft_body(block)
# The same, with the meshing parameters spelled out: the cover of the mesh is subdivided
# once around its boundary, then smoothed, so it follows the mesh more closely.
params = rp.VolumeMeshParameters(0.2, cover_subdivisions=1, cover_smoothing=4)
smooth_block = rp.SoftBody.volumetric_with(vertices, indices, params)
smooth_block_handle = world.add_soft_body(smooth_block.translated((-3.0, 2.0, 0.0)))
The pinned_particlessetPinnedParticlespinnedpinned_particlessoftnesssetSoftnessmaterial, e.g., the same softness for every constraint with r3UniformSoftBodyMaterialsoftnessparticle_masssetParticleMassparticleMassparticle_massmasssetMasstotalMassmassmassessetMassesmassesmassesparticle_radiussetParticleRadiusparticleRadiusparticle_radiusself_contactssetSelfContactsselfContactsself_contactslinear_dampingsetLinearDampinglinearDampinglinear_dampinggravity_scalesetGravityScalegravityScalegravity_scaleSoftBodyParticleSettings::dominance_groupsetDominanceGroupdominanceGroupSoftBodyParticleSettings.dominance_group, given with particle_settingscan_sleepsetCanSleepcanSleepcan_sleepPhysicsWorld::insert_soft_bodySoftBody componentWorld.createSoftBodyr3InsertSoftBodyPhysicsWorld.add_soft_body
- Example 2D
- Example 3D
// A world with a ground.
let mut world = PhysicsWorld::new();
world.insert_collider(ColliderBuilder::cuboid(10.0, 0.1), None);
// Builder for a rope of 20 particles between two points.
let _ = SoftBodyBuilder::rope(Vector::new(0.0, 3.0), Vector::new(2.0, 3.0), 20);
// Builder for a grid of `nx` by `ny` particles filled with triangle cells.
let _ = SoftBodyBuilder::grid(Vector::new(3.0, 1.0), Vector::new(1.0, 1.0), 6, 6);
// Builder for a disk: a ring of particles holding its area (a pressurized blob).
let _ = SoftBodyBuilder::disk(Vector::new(0.0, 3.0), 0.8, 24);
// Builder for a closed polygon of particles holding its area.
let _ = SoftBodyBuilder::polygon(vec![
Vector::new(5.0, 4.0),
Vector::new(7.0, 4.0),
Vector::new(7.0, 6.0),
Vector::new(5.0, 6.0),
]);
let n = 20;
let sheet = SoftBodyBuilder::grid(Vector::new(-3.0, 3.0), Vector::new(1.0, 1.0), n, n)
// Particles held in place.
.pinned_particles([0, (n - 1) as u32])
// A uniform softness (natural frequency in Hz, damping ratio) for every constraint.
.softness(SpringCoefficients::new(30.0, 1.0))
// The mass of each particle.
// Default: 1.0
.particle_mass(0.05)
// The thickness of the particles, for collisions.
// Default: 0.01
.particle_radius(0.05)
// The template of the body's colliders: its shape is replaced by the deformable surface.
.surface_collider(ColliderBuilder::ball(0.05).friction(0.8))
// Whether the body may fall asleep.
// Default: true
.can_sleep(true);
// Insert the soft body: this creates its hidden root rigid body and its colliders.
let sheet_handle = world.insert_soft_body(sheet);
// A world with a ground.
let mut world = PhysicsWorld::new();
world.insert_collider(ColliderBuilder::cuboid(10.0, 0.1, 10.0), None);
// Builder for a rope of 20 particles between two points.
let _ = SoftBodyBuilder::rope(Vector::new(0.0, 3.0, 0.0), Vector::new(2.0, 3.0, 0.0), 20);
// Builder for a cloth: `nu` by `nv` particles, particle `(i, j)` at `origin + i * du + j * dv`.
let _ = SoftBodyBuilder::cloth(
Vector::new(-1.0, 2.0, -1.0),
Vector::new(0.1, 0.0, 0.0),
Vector::new(0.0, 0.0, 0.1),
20,
20,
);
// Builder for a box of `nx * ny * nz` particles filled with tetrahedral cells.
let _ = SoftBodyBuilder::cuboid(Vector::new(3.0, 1.0, 0.0), Vector::splat(0.5), 4, 4, 4);
// Builder for a hollow sphere holding its volume (a balloon).
let _ = SoftBodyBuilder::sphere(Vector::new(0.0, 3.0, 3.0), 0.8, 2);
// Builder over raw particle positions; the elements are added by the setters.
let n = 20;
let cloth = SoftBodyBuilder::cloth(
Vector::new(-1.0, 2.0, -1.0),
Vector::new(0.1, 0.0, 0.0),
Vector::new(0.0, 0.0, 0.1),
n,
n,
)
// Particles held in place.
.pinned_particles([0, (n - 1) as u32, (n * (n - 1)) as u32, (n * n - 1) as u32])
// A uniform softness (natural frequency in Hz, damping ratio) for every constraint.
.softness(SpringCoefficients::new(30.0, 1.0))
// The mass of each particle.
// Default: 1.0
.particle_mass(0.05)
// The thickness of the particles, for collisions.
// Default: 0.01
.particle_radius(0.02)
// The template of the body's colliders: its shape is replaced by the deformable surface.
.surface_collider(ColliderBuilder::ball(0.05).friction(0.8))
// Whether the surface collides with itself.
// Default: false
.self_contacts(true)
// Whether the body may fall asleep.
// Default: true
.can_sleep(true);
// Insert the soft body: this creates its hidden root rigid body and its colliders.
let cloth_handle = world.insert_soft_body(cloth);
- Example 2D
- Example 3D
// The world that will contain our soft bodies.
let world = new RAPIER.World({ x: 0.0, y: -9.81 });
world.createCollider(RAPIER.ColliderDesc.cuboid(10.0, 0.1));
// Description of a rope of 20 particles between two points.
let example1 = RAPIER.SoftBodyDesc.rope({ x: 0.0, y: 3.0 }, { x: 2.0, y: 3.0 }, 20);
// Description of a grid of `nx` by `ny` particles filled with triangle cells.
let example2 = RAPIER.SoftBodyDesc.grid({ x: 3.0, y: 1.0 }, { x: 1.0, y: 1.0 }, 6, 6);
// Description of a disk: a ring of particles holding its area (a pressurized blob).
let example3 = RAPIER.SoftBodyDesc.disk({ x: 0.0, y: 3.0 }, 0.8, 24);
// Description of a closed polygon of particles holding its area.
let example4 = RAPIER.SoftBodyDesc.polygon([5.0, 4.0, 7.0, 4.0, 7.0, 6.0, 5.0, 6.0]);
// Description over raw particle positions; the elements are added by the setters.
let example5 = new RAPIER.SoftBodyDesc([0.0, 1.0, 1.0, 1.0]).setEdges([0, 1]);
let n = 20;
let sheetDesc = RAPIER.SoftBodyDesc.grid({ x: -3.0, y: 3.0 }, { x: 1.0, y: 1.0 }, n, n)
// Particles held in place.
.setPinnedParticles([0, n - 1])
// A uniform softness (natural frequency in Hz, damping ratio) for every constraint.
.setSoftness(30.0, 1.0)
// The mass of each particle.
// Default: 1.0
.setParticleMass(0.05)
// The thickness of the particles, for collisions.
// Default: 0.01
.setParticleRadius(0.05)
// The template of the body's colliders: its shape is replaced by the deformable surface.
.setSurfaceCollider(RAPIER.ColliderDesc.ball(0.05).setFriction(0.8))
// Whether the body may fall asleep.
// Default: true
.setCanSleep(true);
// Create the soft body: this creates its hidden root rigid body and its colliders.
let sheet = world.createSoftBody(sheetDesc);
// The integer handle of the soft body can be read from the `handle` field.
let sheetHandle = sheet.handle;
// The world that will contain our soft bodies.
let world = new RAPIER.World({ x: 0.0, y: -9.81, z: 0.0 });
world.createCollider(RAPIER.ColliderDesc.cuboid(10.0, 0.1, 10.0));
// Description of a rope of 20 particles between two points.
let example1 = RAPIER.SoftBodyDesc.rope({ x: 0.0, y: 3.0, z: 0.0 }, { x: 2.0, y: 3.0, z: 0.0 }, 20);
// Description of a cloth: `nu` by `nv` particles, particle `(i, j)` at `origin + i * du + j * dv`.
let example2 = RAPIER.SoftBodyDesc.cloth(
{ x: -1.0, y: 2.0, z: -1.0 }, { x: 0.1, y: 0.0, z: 0.0 }, { x: 0.0, y: 0.0, z: 0.1 }, 20, 20,
);
// Description of a box of `nx * ny * nz` particles filled with tetrahedral cells.
let example3 = RAPIER.SoftBodyDesc.cuboid({ x: 3.0, y: 1.0, z: 0.0 }, { x: 0.5, y: 0.5, z: 0.5 }, 4, 4, 4);
// Description of a hollow sphere holding its volume (a balloon).
let example4 = RAPIER.SoftBodyDesc.sphere({ x: 0.0, y: 3.0, z: 3.0 }, 0.8, 2);
// Description over raw particle positions; the elements are added by the setters.
let example5 = new RAPIER.SoftBodyDesc([0.0, 1.0, 0.0, 1.0, 1.0, 0.0]).setEdges([0, 1]);
let n = 20;
let clothDesc = RAPIER.SoftBodyDesc.cloth(
{ x: -1.0, y: 2.0, z: -1.0 }, { x: 0.1, y: 0.0, z: 0.0 }, { x: 0.0, y: 0.0, z: 0.1 }, n, n,
)
// Particles held in place.
.setPinnedParticles([0, n - 1, n * (n - 1), n * n - 1])
// A uniform softness (natural frequency in Hz, damping ratio) for every constraint.
.setSoftness(30.0, 1.0)
// The mass of each particle.
// Default: 1.0
.setParticleMass(0.05)
// The thickness of the particles, for collisions.
// Default: 0.01
.setParticleRadius(0.02)
// The template of the body's colliders: its shape is replaced by the deformable surface.
.setSurfaceCollider(RAPIER.ColliderDesc.ball(0.05).setFriction(0.8))
// Whether the surface collides with itself.
// Default: false
.setSelfContacts(true)
// Whether the body may fall asleep.
// Default: true
.setCanSleep(true);
// Create the soft body: this creates its hidden root rigid body and its colliders.
let cloth = world.createSoftBody(clothDesc);
// The integer handle of the soft body can be read from the `handle` field.
let clothHandle = cloth.handle;
- Example 2D
- Example 3D
// A ground.
commands.spawn((
Transform::from_xyz(0.0, -0.1, 0.0),
Collider::cuboid(10.0, 0.1),
));
// A rope of 20 particles between two points (in the local frame of the entity).
let _ = SoftBody::rope(Vec2::ZERO, Vec2::new(2.0, 0.0), 20);
// A grid of `nx` by `ny` particles filled with triangle cells, centered on the entity.
let _ = SoftBody::grid(Vec2::new(1.0, 1.0), 6, 6);
// A disk: a ring of particles holding its area (a pressurized blob), centered on the entity.
let _ = SoftBody::disk(0.8, 24);
// A closed polygon of particles holding its area.
let _ = SoftBody::polygon(vec![
Vec2::new(0.0, 0.0),
Vec2::new(2.0, 0.0),
Vec2::new(2.0, 2.0),
Vec2::new(0.0, 2.0),
]);
// Any constructor of the Rapier builder can be used too.
let _ = SoftBody::new(SoftBodyBuilder::grid(Vec2::ZERO, Vec2::new(1.0, 0.5), 6, 3));
let n = 20;
commands.spawn((
Sheet,
// The particles are placed by the transform of the entity when the soft-body is
// created. Then, the entity follows the center of mass of the particles.
Transform::from_xyz(-3.0, 3.0, 0.0),
SoftBody::grid(Vec2::new(1.0, 1.0), n, n).map(|builder| {
// Particles held in place.
builder
.pinned_particles([0, (n - 1) as u32])
// A uniform softness (frequency in Hz, damping ratio) for every constraint.
.softness(SpringCoefficients::new(30.0, 1.0))
// The mass of each particle.
// Default: 1.0
.particle_mass(0.05)
// The thickness of the particles, for collisions.
// Default: 0.01
.particle_radius(0.05)
// Whether the body may fall asleep.
// Default: true
.can_sleep(true)
}),
// The collider components of the entity configure the colliders of its surface.
Friction::coefficient(0.8),
// Render the soft-body with a mesh kept in sync with its particles.
SoftBodyMeshSync::default(),
MeshMaterial2d(materials.add(Color::srgb(0.8, 0.2, 0.2))),
));
// A ground.
commands.spawn((
Transform::from_xyz(0.0, -0.1, 0.0),
Collider::cuboid(10.0, 0.1, 10.0),
));
// A rope of 20 particles between two points (in the local frame of the entity).
let _ = SoftBody::rope(Vec3::ZERO, Vec3::new(2.0, 0.0, 0.0), 20);
// A cloth: `nu` by `nv` particles, particle `(i, j)` at `origin + i * du + j * dv`.
let _ = SoftBody::cloth(Vec3::ZERO, Vec3::X * 0.1, Vec3::Z * 0.1, 20, 20);
// A box of `nx * ny * nz` particles filled with tetrahedral cells, centered on the entity.
let _ = SoftBody::cuboid(Vec3::splat(0.5), 4, 4, 4);
// A hollow sphere holding its volume (a balloon), centered on the entity.
let _ = SoftBody::sphere(0.8, 2);
// Any constructor of the Rapier builder can be used too.
let _ = SoftBody::new(SoftBodyBuilder::cloth_tube(
Vec3::ZERO,
Vec3::Y,
0.3,
0.3,
12,
10,
));
let n = 20;
let corners = [0, (n - 1) as u32, (n * (n - 1)) as u32, (n * n - 1) as u32];
let cloth = commands
.spawn((
Cloth,
// The particles are placed by the transform of the entity when the soft-body is
// created. Then, the entity follows the center of mass of the particles.
Transform::from_xyz(-1.0, 2.0, -1.0),
SoftBody::cloth(Vec3::ZERO, Vec3::X * 0.1, Vec3::Z * 0.1, n, n).map(|builder| {
// Particles held in place.
builder
.pinned_particles(corners)
// A uniform softness (frequency in Hz, damping ratio) for every constraint.
.softness(SpringCoefficients::new(30.0, 1.0))
// The mass of each particle.
// Default: 1.0
.particle_mass(0.05)
// The thickness of the particles, for collisions.
// Default: 0.01
.particle_radius(0.02)
// Whether the surface collides with itself.
// Default: false
.self_contacts(true)
// Whether the body may fall asleep.
// Default: true
.can_sleep(true)
}),
// The collider components of the entity configure the colliders of its surface.
Friction::coefficient(0.8),
// Render the soft-body with a mesh kept in sync with its particles.
SoftBodyMeshSync::default(),
MeshMaterial3d(materials.add(StandardMaterial {
base_color: Color::srgb(0.8, 0.2, 0.2),
double_sided: true,
cull_mode: None,
..default()
})),
))
.id();
- Example 2D
- Example 3D
// A world with a ground.
R2World *world = r2NewWorld();
R2ColliderDesc ground = r2CuboidColliderDesc(r2Vector(10.0, 0.1));
r2InsertColliderWithoutParent(world, &ground);
// Description of a rope of 20 particles between two points.
R2SoftBodyDesc rope = r2RopeSoftBodyDesc(r2Vector(0.0, 3.0), r2Vector(2.0, 3.0), 20);
// Description of a grid of `nx` by `ny` particles filled with triangle cells.
R2SoftBodyDesc grid = r2GridSoftBodyDesc(r2Vector(3.0, 1.0), r2Vector(1.0, 1.0), 6, 6);
// Description of a disk: a ring of particles holding its area (a pressurized blob).
R2SoftBodyDesc disk = r2DiskSoftBodyDesc(r2Vector(0.0, 3.0), 0.8, 24);
// Description of a closed polygon of particles holding its area.
const R2Vector polygon_points[] = {
r2Vector(5.0, 4.0),
r2Vector(7.0, 4.0),
r2Vector(7.0, 6.0),
r2Vector(5.0, 6.0),
};
R2SoftBodyDesc polygon = r2PolygonSoftBodyDesc((R2VectorView){polygon_points, 4});
const uint32_t n = 20;
R2SoftBodyDesc sheet = r2GridSoftBodyDesc(r2Vector(-3.0, 3.0), r2Vector(1.0, 1.0), n, n);
// Particles held in place.
const uint32_t pinned[] = {0, n - 1};
sheet.pinned = (R2IndexView){pinned, 2};
// A uniform softness (natural frequency in Hz, damping ratio) for every constraint.
sheet.material = r2UniformSoftBodyMaterial((R2SpringCoefficients){30.0, 1.0});
// The mass of each particle.
// Default: 1.0
sheet.particleMass = 0.05;
// The thickness of the particles, for collisions.
// Default: disabled, i.e., the radius computed by the constructor.
sheet.particleRadius = (R2OptionalReal){1, 0.05};
// The template of the body's colliders: its shape is replaced by the deformable surface.
sheet.collider = r2BallColliderDesc(0.05);
sheet.collider.friction = 0.8;
// Whether the body may fall asleep.
// Default: 1
sheet.canSleep = 1;
// Insert the soft-body: this creates its hidden root rigid-body and its colliders.
R2SoftBodyHandle sheet_handle = r2InsertSoftBody(world, &sheet);
// A world with a ground.
R3World *world = r3NewWorld();
R3ColliderDesc ground = r3CuboidColliderDesc(r3Vector(10.0, 0.1, 10.0));
r3InsertColliderWithoutParent(world, &ground);
// Description of a rope of 20 particles between two points.
R3SoftBodyDesc rope = r3RopeSoftBodyDesc(r3Vector(0.0, 3.0, 0.0), r3Vector(2.0, 3.0, 0.0), 20);
// Description of a cloth: `nu` by `nv` particles, particle `(i, j)` at `origin + i * du + j * dv`.
const uint32_t n = 20;
R3SoftBodyDesc cloth =
r3ClothSoftBodyDesc(r3Vector(-1.0, 2.0, -1.0), r3Vector(0.1, 0.0, 0.0), r3Vector(0.0, 0.0, 0.1), n, n);
// Description of a box of `nx * ny * nz` particles filled with tetrahedral cells.
R3SoftBodyDesc box = r3CuboidSoftBodyDesc(r3Vector(3.0, 1.0, 0.0), r3Vector(0.5, 0.5, 0.5), 4, 4, 4);
// Description of a hollow sphere holding its volume (a balloon).
R3SoftBodyDesc balloon = r3SphereSoftBodyDesc(r3Vector(0.0, 3.0, 3.0), 0.8, 2);
// Any field of a description can be modified before its insertion.
// Particles held in place.
const uint32_t pinned[] = {0, n - 1, n * (n - 1), n * n - 1};
cloth.pinned = (R3IndexView){pinned, 4};
// A uniform softness (natural frequency in Hz, damping ratio) for every constraint.
cloth.material = r3UniformSoftBodyMaterial((R3SpringCoefficients){30.0, 1.0});
// The mass of each particle.
// Default: 1.0
cloth.particleMass = 0.05;
// The thickness of the particles, for collisions.
// Default: disabled, i.e., the radius computed by the constructor.
cloth.particleRadius = (R3OptionalReal){1, 0.02};
// The template of the body's colliders: its shape is replaced by the deformable surface.
cloth.collider = r3BallColliderDesc(0.05);
cloth.collider.friction = 0.8;
// Whether the surface collides with itself.
// Default: 0
cloth.selfContacts = 1;
// Whether the body may fall asleep.
// Default: 1
cloth.canSleep = 1;
// Insert the soft-body: this creates its hidden root rigid-body and its colliders.
R3SoftBodyHandle cloth_handle = r3InsertSoftBody(world, &cloth);
# A world with a ground.
world = rp.PhysicsWorld(gravity=(0.0, -9.81, 0.0))
world.add_collider(rp.Collider.cuboid(10.0, 0.1, 10.0))
# Builder for a rope of 20 particles between two points.
_ = rp.SoftBody.rope((0.0, 3.0, 0.0), (2.0, 3.0, 0.0), 20)
# Builder for a cloth: `nu` by `nv` particles, particle `(i, j)` at `origin + i * du + j * dv`.
_ = rp.SoftBody.cloth((-1.0, 2.0, -1.0), (0.1, 0.0, 0.0), (0.0, 0.0, 0.1), 20, 20)
# Builder for a box of `nx * ny * nz` particles filled with tetrahedral cells.
_ = rp.SoftBody.cuboid((3.0, 1.0, 0.0), (0.5, 0.5, 0.5), 4, 4, 4)
# Builder for a hollow sphere holding its volume (a balloon).
_ = rp.SoftBody.sphere((0.0, 3.0, 3.0), 0.8, 2)
# Builder over raw particle positions; the elements are added by the setters.
_ = rp.SoftBodyBuilder([(0.0, 3.0, 0.0), (1.0, 3.0, 0.0)]).edges([(0, 1)])
n = 20
cloth = (
rp.SoftBody.cloth((-1.0, 2.0, -1.0), (0.1, 0.0, 0.0), (0.0, 0.0, 0.1), n, n)
# Particles held in place.
.pinned_particles([0, n - 1, n * (n - 1), n * n - 1])
# A uniform softness (natural frequency in Hz, damping ratio) for every constraint.
.softness(rp.SpringCoefficients(30.0, 1.0))
# The mass of each particle.
# Default: 1.0
.particle_mass(0.05)
# The thickness of the particles, for collisions.
# Default: 0.01
.particle_radius(0.02)
# The template of the body's colliders: its shape is replaced by the deformable surface.
.surface_collider(rp.Collider.ball(0.05).friction(0.8))
# Whether the surface collides with itself.
# Default: False
.self_contacts(True)
# Whether the body may fall asleep.
# Default: True
.can_sleep(True)
)
# The setters can also be given as keyword arguments of the constructors.
_ = rp.SoftBody.rope((0.0, 3.0, 0.0), (2.0, 3.0, 0.0), 20, particle_mass=0.1, pinned_particles=[0])
# Insert the soft body: this creates its hidden root rigid body and its colliders.
cloth_handle = world.add_soft_body(cloth)
The collider given to SoftBodyBuilder::surface_colliderSoftBodyDesc.setSurfaceCollidercollider field of the descriptionSoftBodyBuilder.surface_colliderno_surface_collidersetNoSurfaceCollidercollisionEnabled set to 0no_surface_colliderFriction, Restitution, CollisionGroups, ActiveEvents, Sensor,
etc.) override the properties of that template, exactly like for a Collider: their later modifications are applied to
the colliders of the surface as well, and removing one of them restores the value of the template (the particle radius
for the ContactSkin).
Two appendappendr3SoftBodyDesc_SetAppended (each appended description keeps its own particles, masses, and elements, whereas every other setting comes from the description it is appended to)appendadd_edgesaddEdgesr3SoftBodyDesc_SetAddedEdgesadd_edges