How to Tear and Deform Soft Bodies in Ammo.js
Simulating realistic physics in web applications often requires advanced soft body dynamics, such as tearing cloth or permanently deforming squishy objects. This article provides a direct guide on how to implement tearing and plastic deformation on soft body meshes using the ammo.js physics library, detailing the configuration steps, key API methods, and code logic required to achieve these effects.
Part 1: Tearing a Soft Body Mesh
Tearing in ammo.js (a port of the Bullet Physics engine)
involves breaking the structural connections (links) between the nodes
(vertices) of a soft body when they undergo too much tension.
1. Enabling Native Tearing
Bullet has native support for tearing, primarily designed for cloth (patch) soft bodies. To enable this, you must configure the soft body’s configuration flags and set a tearing factor.
// Enable tearing flag in the soft body configuration
const sbConfig = softBody.get_m_cfg();
sbConfig.set_m_flags(sbConfig.get_m_flags() | 128); // 128 is typically the flag for tearing (f_Tearing)
// Set the tearing factor (m_dcf).
// A lower value makes the material tear more easily under stress.
sbConfig.set_m_dcf(0.5); 2. Manual Link Breaking (Forced Tearing)
Because the WebAssembly bindings of ammo.js can
sometimes limit direct access to internal tearing flags, manual tearing
is often the most reliable method. This involves iterating through the
links of the soft body, calculating the distance between linked nodes,
and deleting links that exceed a threshold.
function updateTearing(softBody, tearThreshold) {
const links = softBody.get_m_links();
const numLinks = links.size();
for (let i = numLinks - 1; i >= 0; i--) {
const link = links.at(i);
const node0 = link.get_m_n(0);
const node1 = link.get_m_n(1);
// Calculate current distance between nodes
const pos0 = node0.get_m_x();
const pos1 = node1.get_m_x();
const distance = pos0.distance(pos1);
// Compare against rest length (m_rl)
const restLength = link.get_m_rl();
if (distance > restLength * tearThreshold) {
// Remove the link to tear the mesh
links.erase(i);
// Force the soft body to rebuild its internal state
softBody.set_m_bUpdateRtCst(true);
}
}
}Part 2: Implementing Plastic (Permanent) Deformation
By default, soft bodies in ammo.js are purely
elastic—they will always attempt to return to their original shape
defined by the initial “rest length” (m_rl) of their links.
To make a soft body permanently deform (like clay or soft metal), you
must implement plasticity.
Plasticity is achieved by permanently modifying the rest length of the links when they are stretched beyond an elastic yield limit.
Step-by-Step Implementation
- Set the Yield Limit: Define how much a link can stretch before it permanently deforms (e.g., 10% beyond rest length).
- Set the Plasticity Rate: Define how quickly the material conforms to its new shape (plastic flow).
- Update Rest Lengths: After each physics step, check
all links and update their
m_rlvalues.
function applyPlasticDeformation(softBody, yieldLimit = 1.1, plasticityRate = 0.5) {
const links = softBody.get_m_links();
const numLinks = links.size();
for (let i = 0; i < numLinks; i++) {
const link = links.at(i);
const node0 = link.get_m_n(0);
const node1 = link.get_m_n(1);
// Calculate current distance between nodes
const pos0 = node0.get_m_x();
const pos1 = node1.get_m_x();
const currentLength = pos0.distance(pos1);
const restLength = link.get_m_rl();
// Check if the link has stretched past the elastic yield limit
if (currentLength > restLength * yieldLimit) {
// Calculate the permanent deformation amount
const deformation = currentLength - restLength;
// Update the rest length permanently
const newRestLength = restLength + (deformation * plasticityRate);
link.set_m_rl(newRestLength);
}
}
}Execution Loop
To make these effects active in your simulation, call these update functions inside your main animation loop directly after stepping the physics world:
function tick(deltaTime) {
// Step the physics world
physicsWorld.stepSimulation(deltaTime, 10);
// Apply tearing and deformation to your soft body
updateTearing(mySoftBody, 1.8); // Tears if stretched 180%
applyPlasticDeformation(mySoftBody, 1.1, 0.2); // Deforms plastically
// Render your scene
renderer.render(scene, camera);
requestAnimationFrame(tick);
}