# UNDF: UNDF-2026-000000603 # UNDF: (leave blank) --- a/src/BulletCollision/CollisionDispatch/btGhostObject.h +++ b/src/BulletCollision/CollisionDispatch/btGhostObject.h @@ -19,6 +19,7 @@ #include "btCollisionObject.h" #include "BulletCollision/BroadphaseCollision/btOverlappingPairCallback.h" #include "LinearMath/btAlignedAllocator.h" +#include "LinearMath/btHashMap.h" #include "BulletCollision/BroadphaseCollision/btOverlappingPairCache.h" #include "btCollisionWorld.h" @@ -36,6 +37,12 @@ btGhostObject : public btCollisionObject protected: btAlignedObjectArray m_overlappingObjects; + // O(1) membership index: maps btCollisionObject* pointer -> index in m_overlappingObjects. + // Eliminates O(N) findLinearSearch on every broadphase add/remove callback, + // turning the per-tick pair-update loop from O(N²) to O(N). CWE-407. + btHashMap m_overlappingObjectsIndex; + public: btGhostObject(); --- a/src/BulletCollision/CollisionDispatch/btGhostObject.cpp +++ b/src/BulletCollision/CollisionDispatch/btGhostObject.cpp @@ -32,10 +32,12 @@ void btGhostObject::addOverlappingObjectInternal(btBroadphaseProxy* otherProxy, { btCollisionObject* otherObject = (btCollisionObject*)otherProxy->m_clientObject; btAssert(otherObject); - ///if this linearSearch becomes too slow (too many overlapping objects) we should add a more appropriate data structure - int index = m_overlappingObjects.findLinearSearch(otherObject); - if (index == m_overlappingObjects.size()) - { - //not found + // O(1) hash lookup replaces O(N) findLinearSearch. CWE-407. + if (m_overlappingObjectsIndex.find(btHashPtr(otherObject)) == NULL) + { + int index = m_overlappingObjects.size(); m_overlappingObjects.push_back(otherObject); + m_overlappingObjectsIndex.insert(btHashPtr(otherObject), index); } } @@ -44,11 +46,16 @@ void btGhostObject::removeOverlappingObjectInternal(btBroadphaseProxy* otherProx btCollisionObject* otherObject = (btCollisionObject*)otherProxy->m_clientObject; btAssert(otherObject); - int index = m_overlappingObjects.findLinearSearch(otherObject); - if (index < m_overlappingObjects.size()) + int* indexPtr = m_overlappingObjectsIndex.find(btHashPtr(otherObject)); + if (indexPtr != NULL) { - m_overlappingObjects[index] = m_overlappingObjects[m_overlappingObjects.size() - 1]; + int index = *indexPtr; + int lastIndex = m_overlappingObjects.size() - 1; + if (index != lastIndex) + { + // Swap with last; update the moved element's index entry. + btCollisionObject* movedObject = m_overlappingObjects[lastIndex]; + m_overlappingObjects[index] = movedObject; + m_overlappingObjectsIndex.insert(btHashPtr(movedObject), index); + } m_overlappingObjects.pop_back(); + m_overlappingObjectsIndex.remove(btHashPtr(otherObject)); } } @@ -68,9 +75,12 @@ void btPairCachingGhostObject::addOverlappingObjectInternal(btBroadphaseProxy* o btCollisionObject* otherObject = (btCollisionObject*)otherProxy->m_clientObject; btAssert(otherObject); - int index = m_overlappingObjects.findLinearSearch(otherObject); - if (index == m_overlappingObjects.size()) - { + if (m_overlappingObjectsIndex.find(btHashPtr(otherObject)) == NULL) + { + int index = m_overlappingObjects.size(); m_overlappingObjects.push_back(otherObject); + m_overlappingObjectsIndex.insert(btHashPtr(otherObject), index); m_hashPairCache->addOverlappingPair(actualThisProxy, otherProxy); } } @@ -82,11 +92,19 @@ void btPairCachingGhostObject::removeOverlappingObjectInternal(btBroadphaseProxy btAssert(actualThisProxy); btAssert(otherObject); - int index = m_overlappingObjects.findLinearSearch(otherObject); - if (index < m_overlappingObjects.size()) + int* indexPtr = m_overlappingObjectsIndex.find(btHashPtr(otherObject)); + if (indexPtr != NULL) { - m_overlappingObjects[index] = m_overlappingObjects[m_overlappingObjects.size() - 1]; + int index = *indexPtr; + int lastIndex = m_overlappingObjects.size() - 1; + if (index != lastIndex) + { + btCollisionObject* movedObject = m_overlappingObjects[lastIndex]; + m_overlappingObjects[index] = movedObject; + m_overlappingObjectsIndex.insert(btHashPtr(movedObject), index); + } m_overlappingObjects.pop_back(); + m_overlappingObjectsIndex.remove(btHashPtr(otherObject)); m_hashPairCache->removeOverlappingPair(actualThisProxy, otherProxy, dispatcher); } }