# 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:

```cpp
// 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`:

```cpp
// 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.

```cpp
// 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.
