Ammo.js btDefaultMotionState and Custom Motion States
This article explains the role of btDefaultMotionState
in Ammo.js, the JavaScript port of the Bullet physics engine. It covers
how this default class handles the synchronization between physics
simulations and 3D rendering engines, and provides clear guidelines on
when and why you should write a custom motion state to optimize your
WebGL projects.
What is the Purpose of btDefaultMotionState?
In Ammo.js, the physics simulation and the visual rendering engine (such as Three.js or Babylon.js) operate independently. While the physics world calculates collisions, gravity, and forces, the rendering engine needs to know where to draw the 3D meshes on the screen.
The primary purpose of a motion state is to act as a bridge between
these two systems. btDefaultMotionState is the default
implementation provided by Bullet/Ammo.js. Its responsibilities
include:
- Storing the Initial Transform: It holds the starting position and rotation (the “transform”) of a rigid body when it is first created.
- Providing Getters and Setters: It implements the
getWorldTransformandsetWorldTransformmethods, allowing the physics engine to query the initial position and update the motion state when forces act upon the rigid body.
While btDefaultMotionState successfully keeps track of a
body’s physical position, it does not automatically update your visual
3D meshes. If you use the default motion state, you must manually query
each rigid body’s transform in your main animation loop and copy those
values over to your corresponding 3D meshes.
When Should You Write a Custom Motion State?
Writing a custom motion state is a common best practice in WebGL game development. You should write a custom motion state in the following scenarios:
1. To Automate Graphics Synchronization (Performance and Clean Code)
Instead of manually looping through every rigid body in your render
loop to update visual positions, you can create a custom motion state.
By overriding the setWorldTransform method, you can write
code that directly updates your visual mesh’s position and rotation
whenever the physics engine updates.
Because Bullet only calls setWorldTransform for active,
moving bodies, this approach prevents unnecessary calculations for
static or sleeping objects, saving CPU cycles.
2. To Implement Frame Interpolation
Physics engines usually step at a fixed rate (e.g., 60 ticks per second), whereas rendering engines run at the variable refresh rate of the monitor (e.g., 90Hz, 120Hz, or 144Hz). This mismatch can cause visual jitter.
A custom motion state allows you to implement interpolation. By storing the previous and current physics transforms, you can calculate an intermediate position based on the render frame’s remainder time, resulting in ultra-smooth visual movement.
3. To Trigger Game Logic on Movement
If you need to execute specific code when an object moves—such as updating a bounding box, triggering a sound effect, or emitting particles based on velocity—a custom motion state provides a clean hook to trigger these events directly when the physics engine alters the object’s transform.