Attach Rigid Body to Moving Soft Body in Ammo.js

This article explains how to dynamically attach a rigid body to a moving soft body mesh using the ammo.js physics library. You will learn the core concepts behind soft body anchors, how to identify the correct attachment point on a mesh at runtime, and the specific ammo.js API methods required to establish a stable, physical connection between the two bodies.

To connect a rigid body to a soft body dynamically in ammo.js, you must use anchors. Anchors bind a specific node (vertex) of the soft body to a rigid body. When the soft body moves or deforms, the attached rigid body moves with it, and vice versa.

Here is the step-by-step process to implement this programmatically.

Step 1: Find the Closest Soft Body Node

Before attaching the rigid body, you need to determine which vertex (node) of the soft body mesh should be the anchor point. Usually, this is the node closest to the rigid body’s current position.

function findClosestNode(softBody, position) {
    const nodes = softBody.get_m_nodes();
    const numNodes = nodes.size();
    let closestNodeIndex = -1;
    let minDistance = Infinity;

    for (let i = 0; i < numNodes; i++) {
        const node = nodes.at(i);
        const nodePos = node.get_m_x(); // Current position of the node
        
        const dx = nodePos.x() - position.x();
        const dy = nodePos.y() - position.y();
        const dz = nodePos.z() - position.z();
        const distance = Math.sqrt(dx * dx + dy * dy + dz * dz);

        if (distance < minDistance) {
            minDistance = distance;
            closestNodeIndex = i;
        }
    }

    return closestNodeIndex;
}

Step 2: Append the Anchor

Once you have the index of the closest node, use the appendAnchor method on the soft body instance to link it to the rigid body.

The appendAnchor method signature in ammo.js is: softBody.appendAnchor(nodeIndex, rigidBody, disableCollisionBetweenLinkedBodies, influence);

// Get the rigid body's current position
const rbTransform = rigidBody.getWorldTransform();
const rbOrigin = rbTransform.getOrigin();

// Find the closest node on the soft body
const nodeIndex = findClosestNode(softBody, rbOrigin);

if (nodeIndex !== -1) {
    // Dynamically attach the rigid body to the soft body node
    softBody.appendAnchor(nodeIndex, rigidBody, true, 1.0);
}

Step 3: Configure Rigid Body Mass and Activation

For the physics simulation to behave correctly after the attachment:

  1. Keep Both Active: Soft bodies and rigid bodies can “sleep” (deactivate) when they stop moving. Force both bodies to remain active so the physics solver continuously processes the joint.
  2. Adjust Mass: Ensure the rigid body’s mass is realistic compared to the soft body’s total mass to prevent extreme stretching or jittering.
// Force activation states
const ACTIVE_TAG = 1;
rigidBody.forceActivationState(ACTIVE_TAG);
softBody.activate();

How the Physics Solver Handles the Connection

Once appendAnchor is called, the Ammo.js constraint solver handles the link automatically during the physics world step. The rigid body will now inherit the velocity and position shifts of the soft body node, while the weight and inertia of the rigid body will pull back on the soft body mesh, creating a realistic, bidirectional physical interaction.