Simulate Zero Gravity in a Specific Ammo.js Region

This article explains how to create a localized zero-gravity zone within an Ammo.js physics simulation. You will learn how to define a specific boundary, detect when physics bodies enter this region, and manipulate their gravity properties dynamically to simulate weightlessness without affecting the rest of the physics world.

To achieve localized zero gravity in Ammo.js, you cannot rely on the global world gravity. Instead, you must dynamically override the gravity of individual rigid bodies when they enter a designated area and restore it when they leave.

Here is the step-by-step implementation process.

Step 1: Define the Zero-Gravity Boundary

First, define the spatial boundaries of your zero-gravity region. This can be done mathematically using an Axis-Aligned Bounding Box (AABB) for simple shapes, or by using a btGhostObject for complex sensor zones.

For a simple box region, define its minimum and maximum coordinates:

const zeroGravMin = new Ammo.btVector3( -10, 0, -10 );
const zeroGravMax = new Ammo.btVector3( 10, 10, 10 );

Step 2: Create a Detection Loop

In your main physics update loop, you must check the position of each active rigid body to determine if it is inside the zero-gravity boundaries.

function checkZeroGravityZone(dynamicsWorld, worldGravity) {
    const numCollisionObjects = dynamicsWorld.getCollisionObjectArray().size();

    for (let i = 0; i < numCollisionObjects; i++) {
        const obj = dynamicsWorld.getCollisionObjectArray().at(i);
        const body = Ammo.btRigidBody.prototype.upcast(obj);

        if (body && !body.isStaticOrKinematicObject()) {
            const transform = body.getWorldTransform();
            const origin = transform.getOrigin();

            const x = origin.x();
            const y = origin.y();
            const z = origin.z();

            // Check if the body is inside the bounding box
            const isInside = (
                x >= zeroGravMin.x() && x <= zeroGravMax.x() &&
                y >= zeroGravMin.y() && y <= zeroGravMax.y() &&
                z >= zeroGravMin.z() && z <= zeroGravMax.z()
            );

            if (isInside) {
                // Apply zero gravity
                body.setGravity(new Ammo.btVector3(0, 0, 0));
            } else {
                // Restore default world gravity
                body.setGravity(worldGravity);
            }
        }
    }
}

Step 3: Counteract Linear Damping (Optional)

When an object enters zero gravity, it should ideally coast indefinitely unless acted upon by an external force. By default, Ammo.js applies damping to simulate air resistance.

If you want realistic space-like zero gravity, reduce the linear damping of the body when it enters the zone, and restore it upon exit:

// Inside the zero-gravity check:
if (isInside) {
    body.setGravity(new Ammo.btVector3(0, 0, 0));
    body.setDamping(0.0, 0.0); // No linear or angular damping
} else {
    body.setGravity(worldGravity);
    body.setDamping(0.1, 0.1); // Restore default damping
}

Alternative Method: Applying an Opposing Force

If you prefer not to modify the gravity property of the rigid body directly, you can instead apply a continuous upward force that exactly counteracts the world’s gravity.

For a body of mass \(m\) in a world with gravity \(g\), apply an upward central force of \(F = m \cdot -g\) every physics step:

if (isInside) {
    const mass = 1.0 / body.getInvMass(); // Get mass of the body
    const counterForce = new Ammo.btVector3(0, mass * 9.8, 0); // Assuming world gravity is -9.8 on Y
    body.applyCentralForce(counterForce);
}

Using the setGravity method is generally preferred for performance and stability, as it prevents gravity calculations entirely for objects inside the designated region.