java-topology/defects/bullet3/patch/bullet3-0002-soft-body-collision-disabled-hashset.md

3.4 KiB
Raw Blame History

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.