How Ammo.js Calculates Drag and Lift on Cloth
This article explains how the JavaScript physics engine ammo.js—a direct port of the Bullet Physics SDK—calculates aerodynamic drag and lift forces on soft body cloth simulations. It breaks down the underlying mathematical models, the configuration settings required to enable these forces, and how face-level aerodynamic calculations are distributed across a 3D mesh’s vertices.
The Underlying Physics Framework
Because ammo.js is a direct compiled port of the C++ Bullet Physics
library, its soft body simulation mechanics are identical to Bullet’s
btSoftBody class. In ammo.js, cloth is represented as a
mass-spring system or a finite element mesh consisting of vertices
(nodes) and triangular faces (links/elements).
Aerodynamic forces like drag and lift are calculated per triangular face rather than per vertex. This is because aerodynamic forces depend on the surface area exposed to the medium (air or wind) and the orientation of that surface relative to the wind flow.
Enabling Aerodynamics in Ammo.js
By default, aerodynamic forces are not applied to soft bodies to save
processing power. To enable them, you must configure the soft body’s
configuration object (btSoftBody::Config).
- Air Density (
kAHR): This parameter represents the density of the medium (air). - Drag Coefficient (
kDG): This controls how much the cloth resists motion through the air. - Lift Coefficient (
kLF): This controls the lifting force generated perpendicular to the air flow. - Aerodynamic Model (
aerofringeoreAeroModel): You must choose an aerodynamic model. The most common models for cloth are:V_TwoSided: Calculates basic aerodynamic forces on both sides of the triangles without lift.V_TwoSidedLiftDrag: Calculates both drag and lift on both sides of the triangular faces.
Step-by-Step Force Calculation
Once aerodynamics are enabled and a wind velocity vector is defined in the physics world, ammo.js performs the following calculations during each physics simulation step:
1. Calculate Relative Velocity
For every triangular face in the soft body mesh, ammo.js calculates the average velocity of its three vertices (\(\vec{v}_{face}\)). It then determines the relative velocity (\(\vec{v}_{rel}\)) between the wind velocity (\(\vec{v}_{wind}\)) and the face’s movement:
\[\vec{v}_{rel} = \vec{v}_{wind} - \vec{v}_{face}\]
2. Determine Face Normal and Area
The engine calculates the surface normal vector (\(\vec{n}\)) of the triangle to determine its orientation. It also calculates the surface area (\(A\)) of the triangle. The projected area facing the wind flow is determined by the dot product of the relative velocity and the surface normal:
\[\cos(\theta) = \frac{\vec{v}_{rel} \cdot \vec{n}}{|\vec{v}_{rel}|}\]
This step determines the “angle of attack.” If the wind hits the cloth face-on, the drag force is maximized. If the wind blows parallel to the face, the drag force approaches zero.
3. Compute Drag Force
Drag acts in the direction of the relative wind velocity, opposing the motion of the cloth. Ammo.js computes the drag force vector (\(\vec{F}_{drag}\)) using a simplified aerodynamic drag equation:
\[\vec{F}_{drag} = C_{drag} \cdot A \cdot (\vec{v}_{rel} \cdot \vec{n}) \cdot \vec{v}_{rel}\]
Here, \(C_{drag}\) is influenced by
the user-defined configuration coefficient (kDG) and the
air density (kAHR). The force is scaled by the relative
velocity and the projected area.
4. Compute Lift Force
Lift acts perpendicular to the relative velocity, pushing the cloth
upward or sideways. Ammo.js calculates the lift force vector (\(\vec{F}_{lift}\)) using the lift
coefficient (kLF):
\[\vec{F}_{lift} = C_{lift} \cdot A \cdot (\vec{v}_{rel} \cdot \vec{n}) \cdot (\vec{v}_{rel} \times \vec{n}) \times \vec{v}_{rel}\]
This cross-product mathematical formulation ensures that lift is generated in a direction perpendicular to the incoming air stream, causing the cloth to flutter or sail.
5. Distribute Forces to Vertices
Once the net aerodynamic force (\(\vec{F}_{total} = \vec{F}_{drag} + \vec{F}_{lift}\)) is calculated for a single triangular face, ammo.js distributes this force vector equally among the three vertices that define that triangle:
\[\vec{F}_{vertex} = \frac{\vec{F}_{total}}{3}\]
These accumulated forces are added to the external force accumulators of the respective nodes. During the integration step of the physics solver, these forces alter the velocities and positions of the vertices, resulting in realistic behaviors like flags waving in the wind or parachutes inflating as they fall.