How Ammo.js Handles Soft Body Self-Collision
This article explores how the ammo.js physics engine—the
JavaScript port of the Bullet physics library—manages self-collision
within complex soft bodies. We will examine the underlying algorithms,
key configuration settings such as cluster-based collisions, and how
developers can enable and optimize this feature to prevent soft body
self-penetration.
The Challenge of Soft Body Self-Collision
In physics simulation, a soft body (such as cloth, rope, or a
deformable volume) is represented as a network of vertices (nodes)
connected by structural springs (links). By default,
ammo.js calculates collisions between the soft body and
external rigid bodies, but it does not check if the soft body is
colliding with itself. Without self-collision enabled, a folding piece
of cloth or a bending gel-like structure will pass through its own
geometry, destroying the realism of the simulation.
Resolving self-collision is computationally expensive because the engine must constantly monitor whether any part of the soft body’s deformable mesh is intersecting with another part of the same mesh.
The Core Mechanism: Clusters vs. Vertex-Face Detection
Ammo.js handles soft body self-collision primarily through two methods: Vertex-Face (VF) collisions and Cluster-Based collisions.
1. Vertex-Face (VF) Self-Collision
In this approach, the engine checks every vertex (node) against every triangular face of the soft body’s mesh. While highly accurate, this method is computationally demanding. For complex meshes with thousands of vertices, Vertex-Face self-collision can cause severe performance drops, making it impractical for real-time web applications.
2. Cluster-Based Self-Collision (Recommended)
To solve the performance bottleneck of VF collision, Bullet and
ammo.js utilize a technique called deformable
clusters.
- Decomposition: The engine automatically groups adjacent vertices of the soft body into localized, overlapping groups called “clusters.”
- Collision Detection: Instead of checking individual vertices, the engine treats these clusters as convex shapes. It performs collision detection between these clusters using GJK (Gilbert-Johnson-Keerthi) and EPA (Expanding Polytope Algorithm) distance algorithms.
- Self-Collision Resolution: When two clusters within the same soft body collide, the engine resolves the penetration by applying impulses to the vertices within those clusters, preventing the mesh from collapsing into itself.
How to Configure Self-Collision in Ammo.js
To enable self-collision in ammo.js, developers must
configure the soft body’s configuration object
(btSoftBodyConfig) and generate clusters.
Step 1: Set the Collision Flags
You must modify the soft body’s collision flags to instruct the
solver to look for soft-body-to-soft-body and self-collisions. This is
done using the m_cfg.collisions bitmask.
// Example flag configuration
const config = softBody.get_m_cfg();
// Clear existing flags and set cluster-to-cluster soft body collision
config.set_collisions(0x0001 | 0x0020); In the underlying Bullet API, the key flags for self-collision are: *
fCollision::CL_SS: Enables cluster-versus-cluster
collisions between soft bodies. * fCollision::CL_SELF:
Enables cluster-versus-cluster self-collisions within the same soft
body.
Step 2: Generate Clusters
If you choose cluster-based collision, you must generate the clusters
after creating the soft body mesh. This partition is built using the
generateClusters method.
// Generate 1024 clusters (or any number based on mesh complexity)
softBody.generateClusters(1024);Choosing the right number of clusters is a balancing act. Too few clusters will result in inaccurate self-collision (as the clusters will be too large and rigid), while too many clusters will decrease performance.
Step 3: Increase Solver Iterations
Because self-collision introduces complex constraints, you may need to increase the position solver iterations to ensure the body does not jitter or collapse under stress.
config.set_piterations(4); // Increase position solver iterations (default is 1)Performance Optimization
Because self-collision calculations scale poorly with mesh complexity, optimizing your implementation is essential for maintaining 60 FPS in WebGL environments:
- Use Low-Poly Proxies: Run the physics simulation on a simplified, low-polygon version of the soft body, and bind a high-resolution visual mesh to the low-poly physics mesh for rendering.
- Adjust Cluster Count dynamically: Match the number of generated clusters to the complexity of the mesh. A simple cape may only need 8 to 16 clusters, while a highly detailed cushion might require 256 or more.
- Optimize the Bounding Volume Hierarchy (BVH): Ammo.js uses a dynamic AABB tree to broadphase collision pairs. Ensure your world’s broadphase is configured efficiently to discard non-colliding cluster pairs quickly.