Skip to content

removeBody leaves rigid-body constraints pointing at the deleted body #4808

Description

@peachtree0222

Environment

  • OS: Ubuntu 24.04.5 LTS, Linux 7.0.0-28-generic x86_64
  • Compiler: GCC 11.5.0
  • Python: 3.12.3
  • Source: bulletphysics/bullet3 master at 63c4d67e337017f9d8b298c900e9aabdb69296e7
  • PyBullet: built locally from that exact checkout

Description

Removing a maximal-coordinate rigid body does not remove btTypedConstraint objects or m_userConstraints entries that reference it. The multibody branch performs this cleanup, but the rigid-body branch deletes the body after a //todo: clear all other remaining data... comment.

Expected behavior: removeBody removes constraints whose endpoint is the removed body, just as the multibody path does.

Actual behavior: stepping immediately after removal exits with status 139. If a replacement rigid body is allocated first, the retained fixed constraint silently binds the replacement: a body created at x=5 moves to x=4.5 and its peer moves from x=1 to x=1.5. Removing the constraint before the body is a clean control and leaves both positions unchanged.

Source:

bool PhysicsServerCommandProcessor::processRemoveBodyCommand(const struct SharedMemoryCommand& clientCmd, struct SharedMemoryStatus& serverStatusOut, char* bufferServerToClient, int bufferSizeInBytes)
{
bool hasStatus = true;
SharedMemoryStatus& serverCmd = serverStatusOut;
serverCmd.m_type = CMD_REMOVE_BODY_FAILED;
serverCmd.m_removeObjectArgs.m_numBodies = 0;
serverCmd.m_removeObjectArgs.m_numUserConstraints = 0;
m_data->m_guiHelper->setVisualizerFlag(COV_ENABLE_SYNC_RENDERING_INTERNAL, 0);
for (int i = 0; i < clientCmd.m_removeObjectArgs.m_numBodies; i++)
{
int bodyUniqueId = clientCmd.m_removeObjectArgs.m_bodyUniqueIds[i];
InternalBodyHandle* bodyHandle = m_data->m_bodyHandles.getHandle(bodyUniqueId);
if (bodyHandle)
{
#ifndef USE_DISCRETE_DYNAMICS_WORLD
if (bodyHandle->m_multiBody)
{
serverCmd.m_removeObjectArgs.m_bodyUniqueIds[serverCmd.m_removeObjectArgs.m_numBodies++] = bodyUniqueId;
if (m_data->m_pickingMultiBodyPoint2Point && m_data->m_pickingMultiBodyPoint2Point->getMultiBodyA() == bodyHandle->m_multiBody)
{
//memory will be deleted in the code that follows
m_data->m_pickingMultiBodyPoint2Point = 0;
}
//also remove user constraints...
for (int i = m_data->m_dynamicsWorld->getNumMultiBodyConstraints() - 1; i >= 0; i--)
{
btMultiBodyConstraint* mbc = m_data->m_dynamicsWorld->getMultiBodyConstraint(i);
if ((mbc->getMultiBodyA() == bodyHandle->m_multiBody) || (mbc->getMultiBodyB() == bodyHandle->m_multiBody))
{
m_data->m_dynamicsWorld->removeMultiBodyConstraint(mbc);
//also remove user constraint and submit it as removed
for (int c = m_data->m_userConstraints.size() - 1; c >= 0; c--)
{
InteralUserConstraintData* userConstraintPtr = m_data->m_userConstraints.getAtIndex(c);
int userConstraintKey = m_data->m_userConstraints.getKeyAtIndex(c).getUid1();
if (userConstraintPtr->m_mbConstraint == mbc)
{
m_data->m_userConstraints.remove(userConstraintKey);
serverCmd.m_removeObjectArgs.m_userConstraintUniqueIds[serverCmd.m_removeObjectArgs.m_numUserConstraints++] = userConstraintKey;
}
}
delete mbc;
}
}
if (bodyHandle->m_multiBody->getBaseCollider())
{
if (m_data->m_pluginManager.getRenderInterface())
{
m_data->m_pluginManager.getRenderInterface()->removeVisualShape(bodyHandle->m_multiBody->getBaseCollider()->getUserIndex3());
}
m_data->m_dynamicsWorld->removeCollisionObject(bodyHandle->m_multiBody->getBaseCollider());
int graphicsIndex = bodyHandle->m_multiBody->getBaseCollider()->getUserIndex();
m_data->m_guiHelper->removeGraphicsInstance(graphicsIndex);
delete bodyHandle->m_multiBody->getBaseCollider();
}
for (int link = 0; link < bodyHandle->m_multiBody->getNumLinks(); link++)
{
btCollisionObject* colObj = bodyHandle->m_multiBody->getLink(link).m_collider;
if (colObj)
{
if (m_data->m_pluginManager.getRenderInterface())
{
m_data->m_pluginManager.getRenderInterface()->removeVisualShape(bodyHandle->m_multiBody->getLink(link).m_collider->getUserIndex3());
}
m_data->m_dynamicsWorld->removeCollisionObject(bodyHandle->m_multiBody->getLink(link).m_collider);
int graphicsIndex = bodyHandle->m_multiBody->getLink(link).m_collider->getUserIndex();
m_data->m_guiHelper->removeGraphicsInstance(graphicsIndex);
delete colObj;
}
}
int numCollisionObjects = m_data->m_dynamicsWorld->getNumCollisionObjects();
m_data->m_dynamicsWorld->removeMultiBody(bodyHandle->m_multiBody);
numCollisionObjects = m_data->m_dynamicsWorld->getNumCollisionObjects();
delete bodyHandle->m_multiBody;
bodyHandle->m_multiBody = 0;
serverCmd.m_type = CMD_REMOVE_BODY_COMPLETED;
}
#endif
if (bodyHandle->m_rigidBody)
{
if (m_data->m_pluginManager.getRenderInterface())
{
m_data->m_pluginManager.getRenderInterface()->removeVisualShape(bodyHandle->m_rigidBody->getUserIndex3());
}
serverCmd.m_removeObjectArgs.m_bodyUniqueIds[serverCmd.m_removeObjectArgs.m_numBodies++] = bodyUniqueId;
if (m_data->m_pickedConstraint && m_data->m_pickedBody == bodyHandle->m_rigidBody)
{
m_data->m_pickedConstraint = 0;
m_data->m_pickedBody = 0;
}
//todo: clear all other remaining data...
m_data->m_dynamicsWorld->removeRigidBody(bodyHandle->m_rigidBody);
int graphicsInstance = bodyHandle->m_rigidBody->getUserIndex2();
m_data->m_guiHelper->removeGraphicsInstance(graphicsInstance);
delete bodyHandle->m_rigidBody;
bodyHandle->m_rigidBody = 0;
serverCmd.m_type = CMD_REMOVE_BODY_COMPLETED;
}

Reproduction package:

rigid-body-removal-constraint-cleanup-poc.zip

Steps to reproduce

  1. Build PyBullet from the commit above using poc/README.md.
  2. Run the control, dangling, and reuse modes in separate processes.
  3. Compare the exit statuses, constraint counts, and positions with poc/observed-output.txt.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions