Files
bullet3/ObsoleteDemos/FractureDemo/btFractureDynamicsWorld.cpp
erwin coumans 69e5454d18 Add the old Bullet 2.x obsolete demos, and CMake buildsystem files, and gradually move them to newer Bullet 3.x structure
Use statically linked freeglut, instead of dynamic glut for the obsolete Bullet 2.x demos
Add the 'reset' method to b3GpuDynamicsWorld, and use it in the BasicGpuDemo (pretty slow in debug mode, use release mode)
Don't crash in btCollisionWorld, if there is no collision dispatcher
2013-12-19 12:40:59 -08:00

689 lines
18 KiB
C++

#include "btFractureDynamicsWorld.h"
#include "btFractureBody.h"
#include "BulletCollision/CollisionShapes/btCompoundShape.h"
#include "BulletCollision/CollisionDispatch/btUnionFind.h"
btFractureDynamicsWorld::btFractureDynamicsWorld ( btDispatcher* dispatcher,btBroadphaseInterface* pairCache,btConstraintSolver* constraintSolver,btCollisionConfiguration* collisionConfiguration)
:btDiscreteDynamicsWorld(dispatcher,pairCache,constraintSolver,collisionConfiguration),
m_fracturingMode(true)
{
}
void btFractureDynamicsWorld::glueCallback()
{
int numManifolds = getDispatcher()->getNumManifolds();
///first build the islands based on axis aligned bounding box overlap
btUnionFind unionFind;
int index = 0;
{
int i;
for (i=0;i<getCollisionObjectArray().size(); i++)
{
btCollisionObject* collisionObject= getCollisionObjectArray()[i];
// btRigidBody* body = btRigidBody::upcast(collisionObject);
//Adding filtering here
#ifdef STATIC_SIMULATION_ISLAND_OPTIMIZATION
if (!collisionObject->isStaticOrKinematicObject())
{
collisionObject->setIslandTag(index++);
} else
{
collisionObject->setIslandTag(-1);
}
#else
collisionObject->setIslandTag(i);
index=i+1;
#endif
}
}
unionFind.reset(index);
int numElem = unionFind.getNumElements();
for (int i=0;i<numManifolds;i++)
{
btPersistentManifold* manifold = getDispatcher()->getManifoldByIndexInternal(i);
if (!manifold->getNumContacts())
continue;
btScalar minDist = 1e30f;
for (int v=0;v<manifold->getNumContacts();v++)
{
minDist = btMin(minDist,manifold->getContactPoint(v).getDistance());
}
if (minDist>0.)
continue;
btCollisionObject* colObj0 = (btCollisionObject*)manifold->getBody0();
btCollisionObject* colObj1 = (btCollisionObject*)manifold->getBody1();
int tag0 = (colObj0)->getIslandTag();
int tag1 = (colObj1)->getIslandTag();
//btRigidBody* body0 = btRigidBody::upcast(colObj0);
//btRigidBody* body1 = btRigidBody::upcast(colObj1);
if (!colObj0->isStaticOrKinematicObject() && !colObj1->isStaticOrKinematicObject())
{
unionFind.unite(tag0, tag1);
}
}
numElem = unionFind.getNumElements();
index=0;
for (int ai=0;ai<getCollisionObjectArray().size();ai++)
{
btCollisionObject* collisionObject= getCollisionObjectArray()[ai];
if (!collisionObject->isStaticOrKinematicObject())
{
int tag = unionFind.find(index);
collisionObject->setIslandTag( tag);
//Set the correct object offset in Collision Object Array
#if STATIC_SIMULATION_ISLAND_OPTIMIZATION
unionFind.getElement(index).m_sz = ai;
#endif //STATIC_SIMULATION_ISLAND_OPTIMIZATION
index++;
}
}
unionFind.sortIslands();
int endIslandIndex=1;
int startIslandIndex;
btAlignedObjectArray<btCollisionObject*> removedObjects;
///iterate over all islands
for ( startIslandIndex=0;startIslandIndex<numElem;startIslandIndex = endIslandIndex)
{
int islandId = unionFind.getElement(startIslandIndex).m_id;
for (endIslandIndex = startIslandIndex+1;(endIslandIndex<numElem) && (unionFind.getElement(endIslandIndex).m_id == islandId);endIslandIndex++)
{
}
int fractureObjectIndex = -1;
int numObjects=0;
int idx;
for (idx=startIslandIndex;idx<endIslandIndex;idx++)
{
int i = unionFind.getElement(idx).m_sz;
btCollisionObject* colObj0 = getCollisionObjectArray()[i];
if (colObj0->getInternalType()& CUSTOM_FRACTURE_TYPE)
{
fractureObjectIndex = i;
}
btRigidBody* otherObject = btRigidBody::upcast(colObj0);
if (!otherObject || !otherObject->getInvMass())
continue;
numObjects++;
}
///Then for each island that contains at least two objects and one fracture object
if (fractureObjectIndex>=0 && numObjects>1)
{
btFractureBody* fracObj = (btFractureBody*)getCollisionObjectArray()[fractureObjectIndex];
///glueing objects means creating a new compound and removing the old objects
///delay the removal of old objects to avoid array indexing problems
removedObjects.push_back(fracObj);
m_fractureBodies.remove(fracObj);
btAlignedObjectArray<btScalar> massArray;
btAlignedObjectArray<btVector3> oldImpulses;
btAlignedObjectArray<btVector3> oldCenterOfMassesWS;
oldImpulses.push_back(fracObj->getLinearVelocity()/1./fracObj->getInvMass());
oldCenterOfMassesWS.push_back(fracObj->getCenterOfMassPosition());
btScalar totalMass = 0.f;
btCompoundShape* compound = new btCompoundShape();
if (fracObj->getCollisionShape()->isCompound())
{
btTransform tr;
tr.setIdentity();
btCompoundShape* oldCompound = (btCompoundShape*)fracObj->getCollisionShape();
for (int c=0;c<oldCompound->getNumChildShapes();c++)
{
compound->addChildShape(oldCompound->getChildTransform(c),oldCompound->getChildShape(c));
massArray.push_back(fracObj->m_masses[c]);
totalMass+=fracObj->m_masses[c];
}
} else
{
btTransform tr;
tr.setIdentity();
compound->addChildShape(tr,fracObj->getCollisionShape());
massArray.push_back(fracObj->m_masses[0]);
totalMass+=fracObj->m_masses[0];
}
for (idx=startIslandIndex;idx<endIslandIndex;idx++)
{
int i = unionFind.getElement(idx).m_sz;
if (i==fractureObjectIndex)
continue;
btCollisionObject* otherCollider = getCollisionObjectArray()[i];
btRigidBody* otherObject = btRigidBody::upcast(otherCollider);
//don't glue/merge with static objects right now, otherwise everything gets stuck to the ground
///todo: expose this as a callback
if (!otherObject || !otherObject->getInvMass())
continue;
oldImpulses.push_back(otherObject->getLinearVelocity()*(1.f/otherObject->getInvMass()));
oldCenterOfMassesWS.push_back(otherObject->getCenterOfMassPosition());
removedObjects.push_back(otherObject);
m_fractureBodies.remove((btFractureBody*)otherObject);
btScalar curMass = 1.f/otherObject->getInvMass();
if (otherObject->getCollisionShape()->isCompound())
{
btTransform tr;
btCompoundShape* oldCompound = (btCompoundShape*)otherObject->getCollisionShape();
for (int c=0;c<oldCompound->getNumChildShapes();c++)
{
tr = fracObj->getWorldTransform().inverseTimes(otherObject->getWorldTransform()*oldCompound->getChildTransform(c));
compound->addChildShape(tr,oldCompound->getChildShape(c));
massArray.push_back(curMass/(btScalar)oldCompound->getNumChildShapes());
}
} else
{
btTransform tr;
tr = fracObj->getWorldTransform().inverseTimes(otherObject->getWorldTransform());
compound->addChildShape(tr,otherObject->getCollisionShape());
massArray.push_back(curMass);
}
totalMass+=curMass;
}
btTransform shift;
shift.setIdentity();
btCompoundShape* newCompound = btFractureBody::shiftTransformDistributeMass(compound,totalMass,shift);
int numChildren = newCompound->getNumChildShapes();
btAssert(numChildren == massArray.size());
btVector3 localInertia;
newCompound->calculateLocalInertia(totalMass,localInertia);
btFractureBody* newBody = new btFractureBody(totalMass,0,newCompound,localInertia, &massArray[0], numChildren,this);
newBody->recomputeConnectivity(this);
newBody->setWorldTransform(fracObj->getWorldTransform()*shift);
//now the linear/angular velocity is still zero, apply the impulses
for (int i=0;i<oldImpulses.size();i++)
{
btVector3 rel_pos = oldCenterOfMassesWS[i]-newBody->getCenterOfMassPosition();
const btVector3& imp = oldImpulses[i];
newBody->applyImpulse(imp, rel_pos);
}
addRigidBody(newBody);
}
}
//remove the objects from the world at the very end,
//otherwise the island tags would not match the world collision object array indices anymore
while (removedObjects.size())
{
btCollisionObject* otherCollider = removedObjects[removedObjects.size()-1];
removedObjects.pop_back();
btRigidBody* otherObject = btRigidBody::upcast(otherCollider);
if (!otherObject || !otherObject->getInvMass())
continue;
removeRigidBody(otherObject);
}
}
struct btFracturePair
{
btFractureBody* m_fracObj;
btAlignedObjectArray<btPersistentManifold*> m_contactManifolds;
};
void btFractureDynamicsWorld::solveConstraints(btContactSolverInfo& solverInfo)
{
// todo: after fracture we should run the solver again for better realism
// for example
// save all velocities and if one or more objects fracture:
// 1) revert all velocties
// 2) apply impulses for the fracture bodies at the contact locations
// 3)and run the constaint solver again
btDiscreteDynamicsWorld::solveConstraints(solverInfo);
fractureCallback();
}
btFractureBody* btFractureDynamicsWorld::addNewBody(const btTransform& oldTransform,btScalar* masses, btCompoundShape* oldCompound)
{
int i;
btTransform shift;
shift.setIdentity();
btVector3 localInertia;
btCompoundShape* newCompound = btFractureBody::shiftTransform(oldCompound,masses,shift,localInertia);
btScalar totalMass = 0;
for (i=0;i<newCompound->getNumChildShapes();i++)
totalMass += masses[i];
//newCompound->calculateLocalInertia(totalMass,localInertia);
btFractureBody* newBody = new btFractureBody(totalMass,0,newCompound,localInertia, masses,newCompound->getNumChildShapes(), this);
newBody->recomputeConnectivity(this);
newBody->setCollisionFlags(newBody->getCollisionFlags()|btCollisionObject::CF_CUSTOM_MATERIAL_CALLBACK);
newBody->setWorldTransform(oldTransform*shift);
addRigidBody(newBody);
return newBody;
}
void btFractureDynamicsWorld::addRigidBody(btRigidBody* body)
{
if (body->getInternalType() & CUSTOM_FRACTURE_TYPE)
{
btFractureBody* fbody = (btFractureBody*)body;
m_fractureBodies.push_back(fbody);
}
btDiscreteDynamicsWorld::addRigidBody(body);
}
void btFractureDynamicsWorld::removeRigidBody(btRigidBody* body)
{
if (body->getInternalType() & CUSTOM_FRACTURE_TYPE)
{
btFractureBody* fbody = (btFractureBody*)body;
btAlignedObjectArray<btTypedConstraint*> tmpConstraints;
for (int i=0;i<fbody->getNumConstraintRefs();i++)
{
tmpConstraints.push_back(fbody->getConstraintRef(i));
}
//remove all constraints attached to this rigid body too
for (int i=0;i<tmpConstraints.size();i++)
btDiscreteDynamicsWorld::removeConstraint(tmpConstraints[i]);
m_fractureBodies.remove(fbody);
}
btDiscreteDynamicsWorld::removeRigidBody(body);
}
void btFractureDynamicsWorld::breakDisconnectedParts( btFractureBody* fracObj)
{
if (!fracObj->getCollisionShape()->isCompound())
return;
btCompoundShape* compound = (btCompoundShape*)fracObj->getCollisionShape();
int numChildren = compound->getNumChildShapes();
if (numChildren<=1)
return;
//compute connectivity
btUnionFind unionFind;
btAlignedObjectArray<int> tags;
tags.resize(numChildren);
int i, index = 0;
for ( i=0;i<numChildren;i++)
{
#ifdef STATIC_SIMULATION_ISLAND_OPTIMIZATION
tags[i] = index++;
#else
tags[i] = i;
index=i+1;
#endif
}
unionFind.reset(index);
int numElem = unionFind.getNumElements();
for (i=0;i<fracObj->m_connections.size();i++)
{
btConnection& connection = fracObj->m_connections[i];
if (connection.m_strength > 0.)
{
int tag0 = tags[connection.m_childIndex0];
int tag1 = tags[connection.m_childIndex1];
unionFind.unite(tag0, tag1);
}
}
numElem = unionFind.getNumElements();
index=0;
for (int ai=0;ai<numChildren;ai++)
{
int tag = unionFind.find(index);
tags[ai] = tag;
//Set the correct object offset in Collision Object Array
#if STATIC_SIMULATION_ISLAND_OPTIMIZATION
unionFind.getElement(index).m_sz = ai;
#endif //STATIC_SIMULATION_ISLAND_OPTIMIZATION
index++;
}
unionFind.sortIslands();
int endIslandIndex=1;
int startIslandIndex;
btAlignedObjectArray<btCollisionObject*> removedObjects;
int numIslands = 0;
for ( startIslandIndex=0;startIslandIndex<numElem;startIslandIndex = endIslandIndex)
{
int islandId = unionFind.getElement(startIslandIndex).m_id;
for (endIslandIndex = startIslandIndex+1;(endIslandIndex<numElem) && (unionFind.getElement(endIslandIndex).m_id == islandId);endIslandIndex++)
{
}
// int fractureObjectIndex = -1;
int numShapes=0;
btCompoundShape* newCompound = new btCompoundShape();
btAlignedObjectArray<btScalar> masses;
int idx;
for (idx=startIslandIndex;idx<endIslandIndex;idx++)
{
int i = unionFind.getElement(idx).m_sz;
// btCollisionShape* shape = compound->getChildShape(i);
newCompound->addChildShape(compound->getChildTransform(i),compound->getChildShape(i));
masses.push_back(fracObj->m_masses[i]);
numShapes++;
}
if (numShapes)
{
btFractureBody* newBody = addNewBody(fracObj->getWorldTransform(),&masses[0],newCompound);
newBody->setLinearVelocity(fracObj->getLinearVelocity());
newBody->setAngularVelocity(fracObj->getAngularVelocity());
numIslands++;
}
}
removeRigidBody(fracObj);//should it also be removed from the array?
}
#include <stdio.h>
void btFractureDynamicsWorld::fractureCallback( )
{
btAlignedObjectArray<btFracturePair> sFracturePairs;
if (!m_fracturingMode)
{
glueCallback();
return;
}
int numManifolds = getDispatcher()->getNumManifolds();
sFracturePairs.clear();
for (int i=0;i<numManifolds;i++)
{
btPersistentManifold* manifold = getDispatcher()->getManifoldByIndexInternal(i);
if (!manifold->getNumContacts())
continue;
btScalar totalImpact = 0.f;
for (int p=0;p<manifold->getNumContacts();p++)
{
totalImpact += manifold->getContactPoint(p).m_appliedImpulse;
}
// printf("totalImpact=%f\n",totalImpact);
static float maxImpact = 0;
if (totalImpact>maxImpact)
maxImpact = totalImpact;
//some threshold otherwise resting contact would break objects after a while
if (totalImpact < 40.f)
continue;
// printf("strong impact\n");
//@todo: add better logic to decide what parts to fracture
//For example use the idea from the SIGGRAPH talk about the fracture in the movie 2012:
//
//Breaking thresholds can be stored as connectivity information between child shapes in the fracture object
//
//You can calculate some "impact value" by simulating all the individual child shapes
//as rigid bodies, without constraints, running it in a separate simulation world
//(or by running the constraint solver without actually modifying the dynamics world)
//Then measure some "impact value" using the offset and applied impulse for each child shape
//weaken the connections based on this "impact value" and only break
//if this impact value exceeds the breaking threshold.
//you can propagate the weakening and breaking of connections using the connectivity information
int f0 = m_fractureBodies.findLinearSearch((btFractureBody*)manifold->getBody0());
int f1 = m_fractureBodies.findLinearSearch((btFractureBody*)manifold->getBody1());
if (f0 == f1 == m_fractureBodies.size())
continue;
if (f0<m_fractureBodies.size())
{
int j=f0;
btCollisionObject* colOb = (btCollisionObject*)manifold->getBody1();
// btRigidBody* otherOb = btRigidBody::upcast(colOb);
// if (!otherOb->getInvMass())
// continue;
int pi=-1;
for (int p=0;p<sFracturePairs.size();p++)
{
if (sFracturePairs[p].m_fracObj == m_fractureBodies[j])
{
pi = p; break;
}
}
if (pi<0)
{
btFracturePair p;
p.m_fracObj = m_fractureBodies[j];
p.m_contactManifolds.push_back(manifold);
sFracturePairs.push_back(p);
} else
{
btAssert(sFracturePairs[pi].m_contactManifolds.findLinearSearch(manifold)==sFracturePairs[pi].m_contactManifolds.size());
sFracturePairs[pi].m_contactManifolds.push_back(manifold);
}
}
if (f1 < m_fractureBodies.size())
{
int j=f1;
{
btCollisionObject* colOb = (btCollisionObject*)manifold->getBody0();
btRigidBody* otherOb = btRigidBody::upcast(colOb);
// if (!otherOb->getInvMass())
// continue;
int pi=-1;
for (int p=0;p<sFracturePairs.size();p++)
{
if (sFracturePairs[p].m_fracObj == m_fractureBodies[j])
{
pi = p; break;
}
}
if (pi<0)
{
btFracturePair p;
p.m_fracObj = m_fractureBodies[j];
p.m_contactManifolds.push_back( manifold);
sFracturePairs.push_back(p);
} else
{
btAssert(sFracturePairs[pi].m_contactManifolds.findLinearSearch(manifold)==sFracturePairs[pi].m_contactManifolds.size());
sFracturePairs[pi].m_contactManifolds.push_back(manifold);
}
}
}
//
}
//printf("m_fractureBodies size=%d\n",m_fractureBodies.size());
//printf("sFracturePairs size=%d\n",sFracturePairs.size());
if (!sFracturePairs.size())
return;
{
// printf("fracturing\n");
for (int i=0;i<sFracturePairs.size();i++)
{
//check impulse/displacement at impact
//weaken/break connections (and propagate breaking)
//compute connectivity of connected child shapes
if (sFracturePairs[i].m_fracObj->getCollisionShape()->isCompound())
{
btTransform tr;
tr.setIdentity();
btCompoundShape* oldCompound = (btCompoundShape*)sFracturePairs[i].m_fracObj->getCollisionShape();
if (oldCompound->getNumChildShapes()>1)
{
bool needsBreakingCheck = false;
//weaken/break the connections
//@todo: propagate along the connection graph
for (int j=0;j<sFracturePairs[i].m_contactManifolds.size();j++)
{
btPersistentManifold* manifold = sFracturePairs[i].m_contactManifolds[j];
for (int k=0;k<manifold->getNumContacts();k++)
{
btManifoldPoint& pt = manifold->getContactPoint(k);
if (manifold->getBody0()==sFracturePairs[i].m_fracObj)
{
for (int f=0;f<sFracturePairs[i].m_fracObj->m_connections.size();f++)
{
btConnection& connection = sFracturePairs[i].m_fracObj->m_connections[f];
if ( (connection.m_childIndex0 == pt.m_index0) ||
(connection.m_childIndex1 == pt.m_index0)
)
{
connection.m_strength -= pt.m_appliedImpulse;
if (connection.m_strength<0)
{
//remove or set to zero
connection.m_strength=0.f;
needsBreakingCheck = true;
}
}
}
} else
{
for (int f=0;f<sFracturePairs[i].m_fracObj->m_connections.size();f++)
{
btConnection& connection = sFracturePairs[i].m_fracObj->m_connections[f];
if ( (connection.m_childIndex0 == pt.m_index1) ||
(connection.m_childIndex1 == pt.m_index1)
)
{
connection.m_strength -= pt.m_appliedImpulse;
if (connection.m_strength<0)
{
//remove or set to zero
connection.m_strength=0.f;
needsBreakingCheck = true;
}
}
}
}
}
}
if (needsBreakingCheck)
{
breakDisconnectedParts(sFracturePairs[i].m_fracObj);
}
}
}
}
}
sFracturePairs.clear();
}