soft_body_tearing
A soft-body can deform permanently in two ways:
- Plasticity changes the rest shape of the body, without any change of its topology: a metal sheet folding on impact, a piece of clay being modeled, the chassis of a car denting.
- Tearing changes its topology: pieces of the body physically disconnect from each other, e.g., a piece of fabric torn in two, or a jelly sliced by a blade.
Both are supported by the constraints solver. Note that with the constraints solver, the quality of the plastic deformations follows the convergence of the solver: more iterations result in more convincing permanent deformations.
Plasticity
Plasticity is configured by the material of the body, separately for its cells and for its edges:
- A cell strained past its plastic yield
(
plasticYield) absorbs the strain in excess into its rest shape, at the rate of its plastic creep (plasticCreep, per second), up to a total permanent deformation of its plastic max (plasticMax). This flow preserves the volume of the cell, and an inverted cell never flows. Note that this only applies to the elastic cells (theCorotationalandNeoHookeanmodels): theVolumecells never flow. - An edge strained past its edge plastic yield
(
edgePlasticYieldcompared to|length / rest_length - 1|) sees its rest length flow toward its current length at the rate of its edge plastic creep (edgePlasticCreep), up to a total permanent set of its edge plastic max (edgePlasticMax, as a fraction of its initial length). Its edge plastic flow (edgePlasticFlow, aSoftEdgePlasticFlow) selects whether that happens when it is squeezed, when it is stretched, or both.
A plastic deformation can be undone at any time
(SoftBody.resetPlasticity),
the particles springing back elastically from there. Note that the tear thresholds of the edges are always measured on
their initial length, not on their plastic one:
- Example 2D
- Example 3D
// The jelly has elastic (corotational) cells: the plasticity of `Volume` cells has no effect.
let plasticMaterial = jelly.material();
// Cells: the rest shape flows toward the current one past 5% strain, at a rate of 20 per
// second, up to a total permanent deformation of 50%.
plasticMaterial.plasticYield = 0.05;
plasticMaterial.plasticCreep = 20.0;
plasticMaterial.plasticMax = 0.5;
// Edges: the rest length flows past 10% strain, up to half the initial length, but only
// when squeezed (a dent stays, a stretch springs back).
plasticMaterial.edgePlasticYield = 0.1;
plasticMaterial.edgePlasticCreep = 10.0;
plasticMaterial.edgePlasticMax = 0.5;
plasticMaterial.edgePlasticFlow = RAPIER.SoftEdgePlasticFlow.Compression;
jelly.setMaterial(plasticMaterial);
// Every permanent deformation can be undone at once.
jelly.resetPlasticity();
// The jelly has elastic (corotational) cells: the plasticity of `Volume` cells has no effect.
let plasticMaterial = jelly.material();
// Cells: the rest shape flows toward the current one past 5% strain, at a rate of 20 per
// second, up to a total permanent deformation of 50%.
plasticMaterial.plasticYield = 0.05;
plasticMaterial.plasticCreep = 20.0;
plasticMaterial.plasticMax = 0.5;
// Edges: the rest length flows past 10% strain, up to half the initial length, but only
// when squeezed (a dent stays, a stretch springs back).
plasticMaterial.edgePlasticYield = 0.1;
plasticMaterial.edgePlasticCreep = 10.0;
plasticMaterial.edgePlasticMax = 0.5;
plasticMaterial.edgePlasticFlow = RAPIER.SoftEdgePlasticFlow.Compression;
jelly.setMaterial(plasticMaterial);
// Every permanent deformation can be undone at once.
jelly.resetPlasticity();
Tearing
Tearing is configured by the material of the body as well. An element tears at the end of the timestep during which its load goes beyond one of the two thresholds of the material:
- The tear strain (
tearStrain) applies to the edges (a fraction of their initial rest length) and to the elastic cells (their largest tensile strain). Note that volume cells never tear. - The tear force (
tearForce) applies to the edges only: an edge tears if its force along its direction exceeds it.
The other settings of the material shape how a tear propagates:
- The tear smoothing (
tearSmoothing) is the time constant (in seconds) over which the load of an element is smoothed before being tested, so that a single impact spike doesn't tear. - The interior strength (
interiorStrength) makes the undamaged interior elements (without any particle on the surface or on an earlier tear) that many times tougher, so that tears start from the surface or from an existing damage, and run inward. - The max tears per step (
maxTearsPerStep) bounds how many edges may tear during one step, the most loaded going first, which paces the cracks of a taut sheet (an edge loaded past twice its threshold always tears). - The min piece (
minPiece) is the smallest piece (in elements) a tear may split off, any tear leaving a smaller piece waiting until it doesn't.
Individual edges can be made tougher (or weaker, e.g., a perforation line) with their tear resistance, given to the
builder
(setEdgeTearResistance)
or by cluster:
- Example 2D
- Example 3D
let tearMaterial = sheet.material();
// An edge tears past 40% of stretch, or past a force of 50 along its direction.
tearMaterial.tearStrain = 0.4;
tearMaterial.tearForce = 50.0;
// The load is smoothed over 0.1 second, so a single impact spike doesn't tear.
tearMaterial.tearSmoothing = 0.1;
// Undamaged interior elements are twice as tough: tears start from the surface.
tearMaterial.interiorStrength = 2.0;
// A tear never splits off a piece smaller than 10 elements.
tearMaterial.minPiece = 10;
sheet.setMaterial(tearMaterial);
let tearMaterial = cloth.material();
// An edge tears past 40% of stretch, or past a force of 50 along its direction.
tearMaterial.tearStrain = 0.4;
tearMaterial.tearForce = 50.0;
// The load is smoothed over 0.1 second, so a single impact spike doesn't tear.
tearMaterial.tearSmoothing = 0.1;
// Undamaged interior elements are twice as tough: tears start from the surface.
tearMaterial.interiorStrength = 2.0;
// A tear never splits off a piece smaller than 10 elements.
tearMaterial.minPiece = 10;
cloth.setMaterial(tearMaterial);
A tear can also be requested explicitly, either edge by edge
(SoftBody.tearEdge, tearCell),
or all at once along a set of edges and through a set of cells
(World.tearSoftBody).
Finally, a body can be cut
(World.cutSoftBody)
along a blade, i.e., a segment in 2D or a triangle in 3D, which is the most convenient way of slicing a body with the
weapon of a player. Note that the cuts ignore the min piece threshold.
Tearing and cutting lose no material: the particles are duplicated along the tear instead of being removed, so the area (2D) or the volume (3D) of the body is preserved. The pieces a tear disconnects become soft-bodies of their own, which keep the material and the settings of the body they come from, the deformable meshes and the joints following the pieces they were attached to. Therefore the particles of the torn body are renumbered, and the returned event tells where each of them went:
- Example 2D
- Example 3D
// Elements tear on their own past the material's thresholds; a tear can also be requested.
sheet.tearEdge(10); // Applied at the end of the next step.
// Tear at once along edges and through cells; pieces the tear disconnects become soft
// bodies of their own.
let tear = world.tearSoftBody(sheet, [11, 12], []);
if (tear) {
console.log(tear.tornEdges().length / 2, "edges torn");
tear.free();
}
// Cut along a blade (a segment in 2D), without removing material.
let cut = world.cutSoftBody(sheet, [{ x: -3.0, y: -10.0 }, { x: -3.0, y: 10.0 }]);
if (cut) {
for (let i = 0; i < cut.numPieces(); ++i) {
let piece = world.getSoftBody(cut.pieceSoftBody(i));
console.log("piece", i, "has", piece.numParticles(), "particles");
}
// Where a particle of the torn body went.
let destination = cut.particleDestination(n * n - 1);
if (destination) {
console.log("particle", n * n - 1, "is now particle", destination.particle, "of", destination.softBody);
}
cut.free();
}
// Elements tear on their own past the material's thresholds; a tear can also be requested.
cloth.tearEdge(10); // Applied at the end of the next step.
// Tear at once along edges and through cells; pieces the tear disconnects become soft
// bodies of their own.
let tear = world.tearSoftBody(cloth, [11, 12], []);
if (tear) {
console.log(tear.tornEdges().length / 2, "edges torn");
tear.free();
}
// Cut along a blade (a triangle in 3D), without removing material.
let cut = world.cutSoftBody(cloth, [
{ x: -0.1, y: -10.0, z: -10.0 }, { x: -0.1, y: 10.0, z: 0.0 }, { x: -0.1, y: -10.0, z: 10.0 },
]);
if (cut) {
for (let i = 0; i < cut.numPieces(); ++i) {
let piece = world.getSoftBody(cut.pieceSoftBody(i));
console.log("piece", i, "has", piece.numParticles(), "particles");
}
// Where a particle of the torn body went.
let destination = cut.particleDestination(n * n - 1);
if (destination) {
console.log("particle", n * n - 1, "is now particle", destination.particle, "of", destination.softBody);
}
cut.free();
}
Tearing one edge with
SoftBody.tearEdge
only marks it: the tear is applied at the end of the next step, together with the tears the simulation generates itself.
The methods of the
World
tear and cut immediately, which is why they are the ones giving back an event.
Volume cells never tear. Therefore a body which cells use the Volume model
will only tear along its edges, and a material with a tear strain should be combined with the
Corotational or the NeoHookean
cell model if you expect it to be torn apart.