Create Anti-Gravity Fields with Ammo.js Trigger Volumes
This guide explains how to implement a custom anti-gravity field in a 3D environment using the Ammo.js physics engine. You will learn how to set up a ghost object as a trigger volume, detect when rigid bodies enter this zone, and apply a counter-gravity force to simulate weightlessness or upward lift.
1. Initialize the Ghost Pair Callback
Before creating a trigger volume (ghost object) in Ammo.js, you must
configure the physics world to calculate overlapping pairs for ghost
objects. Add the btGhostPairCallback to your dynamics world
during your physics initialization:
// Enable ghost object collision tracking in the physics world
const ghostCallback = new Ammo.btGhostPairCallback();
physicsWorld.getPairCache().getOverlappingPairCache().setInternalGhostPairCallback(ghostCallback);Without this callback, your trigger volume will not register when other rigid bodies enter its boundaries.
2. Create the Trigger Volume (Ghost Object)
A trigger volume in Ammo.js is represented by a
btGhostObject. It acts as a sensor zone that detects
collisions without physically blocking or colliding with other
objects.
// Create the ghost object
const triggerVolume = new Ammo.btGhostObject();
// Define the shape of the anti-gravity field (e.g., a 10x10x10 box)
const halfExtents = new Ammo.btVector3(5, 5, 5);
const triggerShape = new Ammo.btBoxShape(halfExtents);
triggerVolume.setCollisionShape(triggerShape);
// Position the trigger volume in the world
const triggerTransform = new Ammo.btTransform();
triggerTransform.setIdentity();
triggerTransform.setOrigin(new Ammo.btVector3(0, 5, 0)); // Placed at Y = 5
triggerVolume.setWorldTransform(triggerTransform);
// Set collision flags to make it a non-solid trigger zone
triggerVolume.setCollisionFlags(triggerVolume.getCollisionFlags() | 4); // 4 corresponds to CF_NO_CONTACT_RESPONSE
// Add the ghost object to the dynamics world
physicsWorld.addCollisionObject(triggerVolume, 2, -1); // 2: Collision group, -1: Collision mask3. Apply the Anti-Gravity Force in the Physics Loop
To make the anti-gravity field work, you must query the trigger volume during your application’s update loop. For every rigid body inside the volume, calculate and apply a force that counteracts the engine’s default gravity.
function updateAntiGravityField() {
const numOverlapping = triggerVolume.getNumOverlappingObjects();
for (let i = 0; i < numOverlapping; i++) {
const obj = triggerVolume.getOverlappingObject(i);
const body = Ammo.btRigidBody.prototype.upcast(obj);
// Ensure the object is a dynamic rigid body
if (body && !body.isStaticOrKinematicObject()) {
// Wake up the body so physics calculations continue to run
body.activate(true);
// Get body mass (mass = 1 / inverseMass)
const invMass = body.getInvMass();
if (invMass > 0) {
const mass = 1.0 / invMass;
const worldGravity = physicsWorld.getGravity();
// Calculate the force needed to negate gravity
// You can add an extra upward force (e.g., mass * 2) to lift objects slowly
const antiGravityForce = new Ammo.btVector3(
-worldGravity.x() * mass,
-worldGravity.y() * mass + (mass * 1.5), // Negates gravity and adds light upward lift
-worldGravity.z() * mass
);
// Apply the force directly to the center of the body
body.applyCentralForce(antiGravityForce);
}
}
}
}4. Run the Update Function
Call updateAntiGravityField() in your main render or
physics tick loop right before stepping the physics simulation:
function tick(deltaTime) {
// 1. Apply custom forces (including anti-gravity)
updateAntiGravityField();
// 2. Step the physics world
physicsWorld.stepSimulation(deltaTime, 10);
// 3. Render your scene...
}When dynamic objects enter the coordinates defined by the
triggerVolume, the custom force will continuously act upon
them. Once they drift out of the boundaries, normal gravity rules
instantly resume.