3.4 KiB
UNDF: UNDF-2026-000000604
UNDF: (pending)
bullet3-0002: btSoftRigidCollisionAlgorithm::processCollision — O(C×D) per frame linear scan
CWE-407 — Algorithmic Complexity
| Field | Value |
|---|---|
| ID | bullet3-0002 |
| Severity | MEDIUM |
| Ecosystem | bullet3 |
| Package | BulletSoftBody |
| File | src/BulletSoftBody/btSoftRigidCollisionAlgorithm.cpp:63, src/BulletSoftBody/btSoftBody.cpp:515 |
| Lines | 63 (algorithm); 515 (appendAnchor) |
| Complexity | O(C×D) per physics step where C = collision pairs, D = disabled objects |
| Hot path | per-collision-pair per physics step |
Defect
btSoftBody::m_collisionDisabledObjects is a
btAlignedObjectArray<const btCollisionObject*> (plain array). The soft-body
collision algorithm calls findLinearSearch on this array every time it
processes a collision pair between a soft body and a rigid body:
// btSoftRigidCollisionAlgorithm.cpp:63 — called every physics step per pair
void btSoftRigidCollisionAlgorithm::processCollision(...)
{
BT_PROFILE("btSoftRigidCollisionAlgorithm::processCollision");
btSoftBody* softBody = ...;
if (softBody->m_collisionDisabledObjects.findLinearSearch(
rigidCollisionObjectWrap->getCollisionObject())
== softBody->m_collisionDisabledObjects.size())
{
softBody->getSoftBodySolver()->processCollision(softBody, rigidCollisionObjectWrap);
}
}
m_collisionDisabledObjects grows when anchors are added with
disableCollisionBetweenLinkedBodies = true:
// btSoftBody.cpp:515
void btSoftBody::appendAnchor(int node, btRigidBody* body, const btVector3& localPivot,
bool disableCollisionBetweenLinkedBodies, btScalar influence)
{
if (disableCollisionBetweenLinkedBodies)
{
if (m_collisionDisabledObjects.findLinearSearch(body) == m_collisionDisabledObjects.size())
m_collisionDisabledObjects.push_back(body);
}
A cloth with A anchors to different rigid bodies has D ≤ A disabled objects. Every physics step, for each of C rigid bodies that overlap the soft body AABB, a linear scan of D objects is performed → O(C × D) per step. With a richly-anchored cloth (D=50) in a complex scene (C=100 overlapping rigid bodies), that is 5,000 operations per step at 60 Hz.
Fix
Replace m_collisionDisabledObjects with a pointer hash set.
// btSoftBody.h — change:
// btAlignedObjectArray<const class btCollisionObject*> m_collisionDisabledObjects;
// to:
#include "LinearMath/btHashMap.h"
btHashMap<btHashPtr, bool> m_collisionDisabledSet;
// btSoftBody.cpp — appendAnchor:
if (disableCollisionBetweenLinkedBodies)
{
btHashPtr key(body);
if (!m_collisionDisabledSet.find(key))
m_collisionDisabledSet.insert(key, true);
}
// btSoftRigidCollisionAlgorithm.cpp:
btHashPtr key(rigidCollisionObjectWrap->getCollisionObject());
if (!softBody->m_collisionDisabledSet.find(key))
{
softBody->getSoftBodySolver()->processCollision(softBody, rigidCollisionObjectWrap);
}
Speedup
| D (disabled objects) | C (overlapping rigids) | Before ops/step | After ops/step | Speedup |
|---|---|---|---|---|
| 10 | 20 | 200 | 20 | 10× |
| 50 | 100 | 5,000 | 100 | 50× |
| 100 | 200 | 20,000 | 200 | 100× |
Cloth and rope simulations with many anchor points in scenes with many rigid bodies are the primary beneficiaries.