setWorldTransform vs setCenterOfMassTransform in Ammo.js

In Ammo.js (the Emscripten port of the Bullet Physics engine), positioning and moving rigid bodies requires a clear understanding of how the engine tracks spatial coordinates. While both setWorldTransform and setCenterOfMassTransform are used to define an object's position and rotation in 3D space, they target different components of a physics body. Choosing the wrong method can lead to unexpected physics behavior, visual jitter, or broken collision detection.

The Core Difference

The fundamental difference lies in which coordinate system you are manipulating and how the physics solver processes the change:

  • setWorldTransform sets the transform of the underlying collision object. It updates the spatial coordinates of the collision shape itself, but it does not update the physics solver's internal interpolation states or account for center-of-mass offsets.
  • setCenterOfMassTransform sets the transform of the rigid body's physical center of mass. It updates the collision shape's position relative to its center of mass, synchronizes the motion state interpolation transforms, and ensures the physics engine calculates gravity, inertia, and forces correctly from the new position.

Under the Hood: How Bullet Physics Sees It

To understand why this distinction matters, it helps to look at the relationship between rigid bodies, collision shapes, and the center of mass.

1. The Role of the Center of Mass

In Ammo.js, all dynamic physics calculations (such as torque, angular velocity, and gravity) are calculated relative to the rigid body's center of mass.

If you have a symmetrical shape (like a box or a sphere centered at [0, 0, 0]), the local origin of the shape and the center of mass are identical. However, for compound shapes or asymmetrical meshes, the center of mass may be offset from the shape's local origin.

  • setCenterOfMassTransform respects this offset, moving the physical center of mass to the designated coordinates and rendering the collision shape relative to it.
  • setWorldTransform ignores the physical center of mass calculations, which can result in forces (like gravity) acting on the wrong point of the object.

2. State Interpolation and Jitter

Ammo.js uses interpolation to smooth out the visual representation of objects between physics simulation ticks.

When you call setCenterOfMassTransform, Ammo.js updates both the current transform and the interpolation transform. When you call setWorldTransform on an active dynamic rigid body, the interpolation transform is left unchanged. This mismatch often causes the object to "jitter" or snap back to its previous position during the next physics frame.


When to Use Each Method

To ensure stable physics and smooth rendering, follow these usage guidelines:

Use setCenterOfMassTransform when:

  • Teleporting dynamic objects: If you need to manually move a dynamic rigid body (an object affected by gravity and forces) to a new position mid-simulation.
  • Initializing rigid bodies: Setting the starting position of active physical objects.
  • Applying manual offsets: Working with compound shapes where the visual mesh origin differs from the physical center of gravity.

Note: After calling setCenterOfMassTransform on an active body, you should also clear its forces and linear/angular velocities using clearForces(), setLinearVelocity(), and setAngularVelocity() to prevent leftover momentum from carrying over to the new position.

Use setWorldTransform when:

  • Moving kinematic objects: Kinematic bodies are driven manually by your code rather than the physics solver. Because they do not rely on dynamic forces or center-of-mass calculations, updating their position via setWorldTransform (often through a btMotionState) is standard practice.
  • Working with static colliders: Moving non-dynamic scenery, ground planes, or triggers that do not have physical mass or inertia.