From 74a65a62073df49bcf253998bc3978ae14d17fc2 Mon Sep 17 00:00:00 2001 From: "erwin.coumans" Date: Thu, 17 Mar 2011 00:41:39 +0000 Subject: [PATCH] cleaned up FractureDemo, still need to apply the velocity/impulses after the fracture took place --- Demos/FractureDemo/CMakeLists.txt | 8 + Demos/FractureDemo/FractureDemo.cpp | 1204 +++-------------- Demos/FractureDemo/btFractureBody.cpp | 138 ++ Demos/FractureDemo/btFractureBody.h | 78 ++ .../FractureDemo/btFractureDynamicsWorld.cpp | 636 +++++++++ Demos/FractureDemo/btFractureDynamicsWorld.h | 48 + 6 files changed, 1078 insertions(+), 1034 deletions(-) create mode 100644 Demos/FractureDemo/btFractureBody.cpp create mode 100644 Demos/FractureDemo/btFractureBody.h create mode 100644 Demos/FractureDemo/btFractureDynamicsWorld.cpp create mode 100644 Demos/FractureDemo/btFractureDynamicsWorld.h diff --git a/Demos/FractureDemo/CMakeLists.txt b/Demos/FractureDemo/CMakeLists.txt index 7534c0e6a..eae9acd9a 100644 --- a/Demos/FractureDemo/CMakeLists.txt +++ b/Demos/FractureDemo/CMakeLists.txt @@ -27,6 +27,10 @@ ADD_EXECUTABLE(AppFractureDemo main.cpp FractureDemo.cpp FractureDemo.h + btFractureBody.h + btFractureBody.cpp + btFractureDynamicsWorld.cpp + btFractureDynamicsWorld.h ${BULLET_PHYSICS_SOURCE_DIR}/msvc/bullet.rc ) ELSE() @@ -34,6 +38,10 @@ ELSE() main.cpp FractureDemo.cpp FractureDemo.h + btFractureBody.h + btFractureBody.cpp + btFractureDynamicsWorld.cpp + btFractureDynamicsWorld.h ) ENDIF() diff --git a/Demos/FractureDemo/FractureDemo.cpp b/Demos/FractureDemo/FractureDemo.cpp index 823da6cc1..32b3cf511 100644 --- a/Demos/FractureDemo/FractureDemo.cpp +++ b/Demos/FractureDemo/FractureDemo.cpp @@ -21,17 +21,6 @@ subject to the following restrictions: ///This is preliminary work - -//#define TEST_SERIALIZATION 1 - - -///create 125 (5x5x5) dynamic object -#define ARRAY_SIZE_X 10 -#define ARRAY_SIZE_Y 1 -#define ARRAY_SIZE_Z 10 - -//maximum number of objects (and allow user to shoot additional boxes) -#define MAX_PROXIES (ARRAY_SIZE_X*ARRAY_SIZE_Y*ARRAY_SIZE_Z + 1024) #define CUBE_HALF_EXTENTS 1.f #define EXTRA_HEIGHT 1.f ///scaling of the objects (0.1 = 20 centimeter boxes ) @@ -45,959 +34,15 @@ subject to the following restrictions: #include "GLDebugFont.h" ///btBulletDynamicsCommon.h is the main Bullet include file, contains most common include files. #include "btBulletDynamicsCommon.h" -#include "BulletCollision/CollisionDispatch/btUnionFind.h" -#ifdef TEST_SERIALIZATION -#include "LinearMath/btSerializer.h" -#endif //TEST_SERIALIZATION #include //printf debugging -bool sFracturingMode = false; -#define CUSTOM_FRACTURE_TYPE (btRigidBody::CO_USER_TYPE+1) + int sFrameNumber = 0; -void FractureDemo::clientMoveAndDisplay() -{ - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - //simple dynamics world doesn't handle fixed-time-stepping - float ms = getDeltaTimeMicroseconds(); - - ///step the simulation - if (m_dynamicsWorld) - { - m_dynamicsWorld->stepSimulation(ms / 1000000.f); - //optional but useful: debug drawing - m_dynamicsWorld->debugDrawWorld(); - } - - - - renderme(); - - showMessage(); - - glFlush(); - - swapBuffers(); - -} - -void FractureDemo::showMessage() -{ - if((getDebugMode() & btIDebugDraw::DBG_DrawText)) - { - setOrthographicProjection(); - glDisable(GL_LIGHTING); - glColor3f(0, 0, 0); - char buf[124]; - - int lineWidth=350; - int xStart = m_glutScreenWidth - lineWidth; - int yStart = 20; - - glRasterPos3f(xStart, yStart, 0); - if (sFracturingMode) - { - sprintf(buf,"Fracture mode"); - } else - { - sprintf(buf,"Glue mode"); - } - GLDebugDrawString(xStart,20,buf); - yStart+=20; - glRasterPos3f(xStart, yStart, 0); - sprintf(buf,"f to toggle fracture/glue mode"); - yStart+=20; - GLDebugDrawString(xStart,yStart,buf); - resetPerspectiveProjection(); - glEnable(GL_LIGHTING); - } - -} - - -void FractureDemo::displayCallback(void) { - - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - renderme(); - - showMessage(); - - //optional but useful: debug drawing to detect problems - if (m_dynamicsWorld) - m_dynamicsWorld->debugDrawWorld(); - - glFlush(); - swapBuffers(); -} - -void FractureDemo::keyboardCallback(unsigned char key, int x, int y) -{ - if (key=='f') - { - } else - { - PlatformDemoApplication::keyboardCallback(key,x,y); - } -} - -void FractureDemo::keyboardUpCallback(unsigned char key, int x, int y) -{ - if (key=='f') - { - sFracturingMode = !sFracturingMode; - } - - PlatformDemoApplication::keyboardUpCallback(key,x,y); - -} - - - -struct btConnection -{ - - btCollisionShape* m_childShape0; - btCollisionShape* m_childShape1; - int m_childIndex0; - int m_childIndex1; - btScalar m_strength; -}; - -class btFractureBody : public btRigidBody -{ - //connections -public: - - btDynamicsWorld* m_world; - btAlignedObjectArray m_masses; - btAlignedObjectArray m_connections; - - - - btFractureBody( const btRigidBodyConstructionInfo& constructionInfo, btDynamicsWorld* world) - :btRigidBody(constructionInfo), - m_world(world) - { - m_masses.push_back(constructionInfo.m_mass); - m_internalType=CUSTOM_FRACTURE_TYPE+CO_RIGID_BODY; - } - - - - ///btRigidBody constructor for backwards compatibility. - ///To specify friction (etc) during rigid body construction, please use the other constructor (using btRigidBodyConstructionInfo) - btFractureBody( btScalar mass, btMotionState* motionState, btCollisionShape* collisionShape, const btVector3& localInertia, btScalar* masses, int numMasses, btDynamicsWorld* world) - :btRigidBody(mass,motionState,collisionShape,localInertia), - m_world(world) - { - - for (int i=0;iisCompound()) - { - btCompoundShape* compound = (btCompoundShape*)getCollisionShape(); - for (int i=0;igetNumChildShapes();i++) - { - for (int j=i+1;jgetNumChildShapes();j++) - { - - struct MyContactResultCallback : public btCollisionWorld::ContactResultCallback - { - bool m_connected; - MyContactResultCallback() :m_connected(false) - { - } - virtual btScalar addSingleResult(btManifoldPoint& cp, const btCollisionObject* colObj0,int partId0,int index0,const btCollisionObject* colObj1,int partId1,int index1) - { - //@todo additional check on cp? - m_connected = true; - return 1.f; - } - }; - - MyContactResultCallback result; - - btCollisionObject obA; - obA.setWorldTransform(compound->getChildTransform(i)); - obA.setCollisionShape(compound->getChildShape(i)); - btCollisionObject obB; - obB.setWorldTransform(compound->getChildTransform(j)); - obB.setCollisionShape(compound->getChildShape(j)); - world->contactPairTest(&obA,&obB,result); - if (result.m_connected) - { - btConnection tmp; - tmp.m_childIndex0 = i; - tmp.m_childIndex1 = j; - tmp.m_childShape0 = compound->getChildShape(i); - tmp.m_childShape1 = compound->getChildShape(j); - tmp.m_strength = 1.f;//?? - m_connections.push_back(tmp); - } - } - } - } - - - } - - static btCompoundShape* shiftTransform(btCompoundShape* boxCompound,btScalar* masses,btTransform& shift, btVector3& principalInertia) - { - btTransform principal; - - boxCompound->calculatePrincipalAxisTransform(masses,principal,principalInertia); - - - ///create a new compound with world transform/center of mass properly aligned with the principal axis - - ///non-recursive compound shapes perform better - - #ifdef USE_RECURSIVE_COMPOUND - - btCompoundShape* newCompound = new btCompoundShape(); - newCompound->addChildShape(principal.inverse(),boxCompound); - newBoxCompound = newCompound; - //m_collisionShapes.push_back(newCompound); - - //btDefaultMotionState* myMotionState = new btDefaultMotionState(startTransform); - //btRigidBody::btRigidBodyConstructionInfo rbInfo(mass,myMotionState,newCompound,principalInertia); - - #else - #ifdef CHANGE_COMPOUND_INPLACE - newBoxCompound = boxCompound; - for (int i=0;igetNumChildShapes();i++) - { - btTransform newChildTransform = principal.inverse()*boxCompound->getChildTransform(i); - ///updateChildTransform is really slow, because it re-calculates the AABB each time. todo: add option to disable this update - boxCompound->updateChildTransform(i,newChildTransform); - } - bool isDynamic = (mass != 0.f); - btVector3 localInertia(0,0,0); - if (isDynamic) - boxCompound->calculateLocalInertia(mass,localInertia); - - #else - ///creation is faster using a new compound to store the shifted children - btCompoundShape* newBoxCompound = new btCompoundShape(); - for (int i=0;igetNumChildShapes();i++) - { - btTransform newChildTransform = principal.inverse()*boxCompound->getChildTransform(i); - ///updateChildTransform is really slow, because it re-calculates the AABB each time. todo: add option to disable this update - newBoxCompound->addChildShape(newChildTransform,boxCompound->getChildShape(i)); - } - - - - #endif - - #endif//USE_RECURSIVE_COMPOUND - - shift = principal; - return newBoxCompound; - } - - static btCompoundShape* shiftTransformDistributeMass(btCompoundShape* boxCompound,btScalar mass,btTransform& shift) - { - - btVector3 principalInertia; - - btScalar* masses = new btScalar[boxCompound->getNumChildShapes()]; - for (int j=0;jgetNumChildShapes();j++) - { - //evenly distribute mass - masses[j]=mass/boxCompound->getNumChildShapes(); - } - - return shiftTransform(boxCompound,masses,shift,principalInertia); - - } - - static bool collisionCallback(btManifoldPoint& cp, const btCollisionObject* colObj0,int partId0,int index0,const btCollisionObject* colObj1,int partId1,int index1); - -}; - -struct btFracturePair -{ - btFractureBody* m_fracObj; - btAlignedObjectArray m_contactManifolds; -}; - -btAlignedObjectArray sFractureBodies; -btAlignedObjectArray sFracturePairs; - - - - - -void addNewBody(const btTransform& oldTransform,btScalar* masses, btCompoundShape* oldCompound, btDynamicsWorld* world) -{ - int i; - - btTransform shift; - shift.setIdentity(); - btVector3 localInertia; - btCompoundShape* newCompound = btFractureBody::shiftTransform(oldCompound,masses,shift,localInertia); - btScalar totalMass = 0; - for (i=0;igetNumChildShapes();i++) - totalMass += masses[i]; - //newCompound->calculateLocalInertia(totalMass,localInertia); - - btFractureBody* newBody = new btFractureBody(totalMass,0,newCompound,localInertia, masses,newCompound->getNumChildShapes(), world); - newBody->recomputeConnectivity(world); - sFractureBodies.push_back(newBody); - - newBody->setCollisionFlags(newBody->getCollisionFlags()|btCollisionObject::CF_CUSTOM_MATERIAL_CALLBACK); - newBody->setWorldTransform(oldTransform*shift); - world->addRigidBody(newBody); -} - - -void breakDisconnectedParts( btFractureBody* fracObj, btDynamicsWorld* world) -{ - printf("breakingCheck\n"); - - if (!fracObj->getCollisionShape()->isCompound()) - return; - - btCompoundShape* compound = (btCompoundShape*)fracObj->getCollisionShape(); - int numChildren = compound->getNumChildShapes(); - - if (numChildren<=1) - return; - - //compute connectivity - btUnionFind unionFind; - - btAlignedObjectArray tags; - tags.resize(numChildren); - int i, index = 0; - for ( i=0;im_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 removedObjects; - - int numIslands = 0; - - for ( startIslandIndex=0;startIslandIndex masses; - - int idx; - for (idx=startIslandIndex;idxgetChildShape(i); - newCompound->addChildShape(compound->getChildTransform(i),compound->getChildShape(i)); - masses.push_back(fracObj->m_masses[i]); - numShapes++; - } - if (numShapes) - { - addNewBody(fracObj->getWorldTransform(),&masses[0],newCompound,world); - numIslands++; - } - } - - - - /* - - //fracture (split) disconnected parts - - int numElems = oldCompound->getNumChildShapes()/2; - btScalar* newMasses0 = (btScalar*) malloc(sizeof(btScalar)*numElems); - int mc = 0; - for (int c=0;cgetNumChildShapes()/2;c++,mc++) - { - newCompound0->addChildShape(oldCompound->getChildTransform(c),oldCompound->getChildShape(c)); - newMasses0[mc] = sFracturePairs[i].m_fracObj->m_masses[c]; - } - - - btCompoundShape* newCompound1 = new btCompoundShape(); - numElems = oldCompound->getNumChildShapes() - oldCompound->getNumChildShapes()/2; - mc = 0; - btScalar* newMasses1 = (btScalar*) malloc(sizeof(btScalar)*numElems); - for (int c=oldCompound->getNumChildShapes()/2;cgetNumChildShapes();c++,mc++) - { - newCompound1->addChildShape(oldCompound->getChildTransform(c),oldCompound->getChildShape(c)); - newMasses1[mc] = sFracturePairs[i].m_fracObj->m_masses[c]; - } - - addNewBody(sFracturePairs[i].m_fracObj->getWorldTransform(),newMasses1,newCompound1,world); - - */ - - world->removeRigidBody(fracObj); - sFractureBodies.remove(fracObj); - -} - - - - -void FractureDemo::shootBox(const btVector3& destination) -{ - - if (m_dynamicsWorld) - { - btScalar mass = 1.f; - btTransform startTransform; - startTransform.setIdentity(); - btVector3 camPos = getCameraPosition(); - startTransform.setOrigin(camPos); - - setShootBoxShape (); - - btAssert((!m_shootBoxShape || m_shootBoxShape->getShapeType() != INVALID_SHAPE_PROXYTYPE)); - - //rigidbody is dynamic if and only if mass is non zero, otherwise static - bool isDynamic = (mass != 0.f); - - btVector3 localInertia(0,0,0); - if (isDynamic) - m_shootBoxShape->calculateLocalInertia(mass,localInertia); - - //using motionstate is recommended, it provides interpolation capabilities, and only synchronizes 'active' objects - - btFractureBody* body = new btFractureBody(mass,0,m_shootBoxShape,localInertia,&mass,1,m_dynamicsWorld); - sFractureBodies.push_back(body); - body->setWorldTransform(startTransform); - - m_dynamicsWorld->addRigidBody(body); - - - body->setLinearFactor(btVector3(1,1,1)); - //body->setRestitution(1); - - btVector3 linVel(destination[0]-camPos[0],destination[1]-camPos[1],destination[2]-camPos[2]); - linVel.normalize(); - linVel*=m_ShootBoxInitialSpeed; - - body->getWorldTransform().setOrigin(camPos); - body->getWorldTransform().setRotation(btQuaternion(0,0,0,1)); - body->setLinearVelocity(linVel); - body->setAngularVelocity(btVector3(0,0,0)); - body->setCcdMotionThreshold(1.); - body->setCcdSweptSphereRadius(0.2f); - - } -} - -void glueCallback(btDynamicsWorld* world, btScalar timeStep) -{ - - int numManifolds = world->getDispatcher()->getNumManifolds(); - - btUnionFind unionFind; - - int index = 0; - { - - int i; - for (i=0;igetCollisionObjectArray().size(); i++) - { - btCollisionObject* collisionObject= world->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;igetDispatcher()->getManifoldByIndexInternal(i); - if (!manifold->getNumContacts()) - 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;aigetCollisionObjectArray().size();ai++) - { - btCollisionObject* collisionObject= world->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 removedObjects; - - //update the sleeping state for bodies, if all are sleeping - for ( startIslandIndex=0;startIslandIndexgetCollisionObjectArray()[i]; - if (colObj0->getInternalType()& CUSTOM_FRACTURE_TYPE) - { - fractureObjectIndex = i; - } - btRigidBody* otherObject = btRigidBody::upcast(colObj0); - if (!otherObject || !otherObject->getInvMass()) - continue; - numObjects++; - } - - - if (fractureObjectIndex>=0 && numObjects>1) - { - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - btFractureBody* fracObj = (btFractureBody*)world->getCollisionObjectArray()[fractureObjectIndex]; - - removedObjects.push_back(fracObj); - sFractureBodies.remove(fracObj); - - btAlignedObjectArray massArray; - 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;cgetNumChildShapes();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;idxgetCollisionObjectArray()[i]; - - btRigidBody* otherObject = btRigidBody::upcast(otherCollider); - if (!otherObject || !otherObject->getInvMass()) - continue; - - removedObjects.push_back(otherObject); - sFractureBodies.remove((btFractureBody*)otherObject); - - btScalar curMass = 1./otherObject->getInvMass(); - - - if (otherObject->getCollisionShape()->isCompound()) - { - btTransform tr; - btCompoundShape* oldCompound = (btCompoundShape*)otherObject->getCollisionShape(); - for (int c=0;cgetNumChildShapes();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,world); - newBody->recomputeConnectivity(world); - sFractureBodies.push_back(newBody); - newBody->setWorldTransform(fracObj->getWorldTransform()*shift); - world->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; - world->removeRigidBody(otherObject); - } - -} - - - - -void fractureCallback(btDynamicsWorld* world, btScalar timeStep) -{ - if (!sFracturingMode) - { - glueCallback(world,timeStep); - return; - } - - int numManifolds = world->getDispatcher()->getNumManifolds(); - - sFracturePairs.clear(); - - - for (int i=0;igetDispatcher()->getManifoldByIndexInternal(i); - if (!manifold->getNumContacts()) - continue; - - btScalar totalImpact = 0.f; - for (int p=0;pgetNumContacts();p++) - { - totalImpact += manifold->getContactPoint(p).m_appliedImpulse; - } - - static float maxImpact = 0; - if (totalImpact>maxImpact) - maxImpact = totalImpact; - - //some threshold otherwise resting contact would break objects after a while - if (totalImpact < 10) - 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 = sFractureBodies.findLinearSearch((btFractureBody*)manifold->getBody0()); - int f1 = sFractureBodies.findLinearSearch((btFractureBody*)manifold->getBody1()); - - if (f0 == f1 == sFractureBodies.size()) - continue; - - - if (f0getBody1(); - btRigidBody* otherOb = btRigidBody::upcast(colOb); - // if (!otherOb->getInvMass()) - // continue; - - int pi=-1; - - for (int p=0;pgetBody0(); - btRigidBody* otherOb = btRigidBody::upcast(colOb); - // if (!otherOb->getInvMass()) - // continue; - - - int pi=-1; - - for (int p=0;pgetCollisionShape()->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;jgetNumContacts();k++) - { - btManifoldPoint& pt = manifold->getContactPoint(k); - if (manifold->getBody0()==sFracturePairs[i].m_fracObj) - { - for (int f=0;fm_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;fm_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, world); - } - } - - } - - } - } - - sFracturePairs.clear(); - -} - +#include "btFractureBody.h" +#include "btFractureDynamicsWorld.h" @@ -1026,13 +71,15 @@ void FractureDemo::initPhysics() btSequentialImpulseConstraintSolver* sol = new btSequentialImpulseConstraintSolver; m_solver = sol; - m_dynamicsWorld = new btDiscreteDynamicsWorld(m_dispatcher,m_broadphase,m_solver,m_collisionConfiguration); + //m_dynamicsWorld = new btDiscreteDynamicsWorld(m_dispatcher,m_broadphase,m_solver,m_collisionConfiguration); + + m_dynamicsWorld = new btFractureDynamicsWorld(m_dispatcher,m_broadphase,m_solver,m_collisionConfiguration); + //m_splitImpulse removes the penetration resolution from the applied impulse, otherwise objects might fracture due to deep penetrations. m_dynamicsWorld->getSolverInfo().m_splitImpulse = true; - m_dynamicsWorld->setInternalTickCallback(fractureCallback,m_dynamicsWorld); m_dynamicsWorld->setGravity(btVector3(0,-10,0)); ///create a few basic rigid bodies @@ -1087,85 +134,22 @@ void FractureDemo::initPhysics() btVector3 localInertia(0,0,0); if (isDynamic) colShape->calculateLocalInertia(mass,localInertia); -#if 0 - float start_x = START_POS_X - ARRAY_SIZE_X/2; - float start_y = START_POS_Y; - float start_z = START_POS_Z - ARRAY_SIZE_Z/2; - for (int k=0;ksetWorldTransform(tr); - - body->setActivationState(ISLAND_SLEEPING); - - m_dynamicsWorld->addRigidBody(body); - body->setActivationState(ISLAND_SLEEPING); - - btVector3 pivot(0,0,0); - btVector3 axis(0,1,0); - //btHingeConstraint* hinge = new btHingeConstraint(*body,pivot,axis,true); - //hinge->setAngularOnly(true); - //m_dynamicsWorld->addConstraint(hinge); - - } - } - } -#else - - int gNumObjects = ARRAY_SIZE_X * ARRAY_SIZE_Y * ARRAY_SIZE_Z; + int gNumObjects = 10; for (int i=0;i3) - { - col=11; - row2 |=1; - } - - btVector3 pos(col*2*CUBE_HALF_EXTENTS + (row2%2)*CUBE_HALF_EXTENTS, - row*2*CUBE_HALF_EXTENTS+CUBE_HALF_EXTENTS+EXTRA_HEIGHT,0); - + btVector3 pos(i*2*CUBE_HALF_EXTENTS ,10,0); trans.setOrigin(pos); //using motionstate is recommended, it provides interpolation capabilities, and only synchronizes 'active' objects btDefaultMotionState* myMotionState = new btDefaultMotionState(trans); btRigidBody::btRigidBodyConstructionInfo rbInfo(mass,myMotionState,colShape,localInertia); btFractureBody* body = new btFractureBody(rbInfo, m_dynamicsWorld); - sFractureBodies.push_back(body); - btTransform tr; - tr.setIdentity(); - btQuaternion orn(btVector3(1,0,0),45); - tr.setRotation(orn); - body->setWorldTransform(tr); body->setActivationState(ISLAND_SLEEPING); @@ -1174,7 +158,7 @@ void FractureDemo::initPhysics() } -#endif + } @@ -1182,19 +166,171 @@ void FractureDemo::initPhysics() clientResetScene(); -#ifdef TEST_SERIALIZATION - //test serializing this +} - btDefaultSerializer* serializer = new btDefaultSerializer(); - m_dynamicsWorld->serialize(serializer); +void FractureDemo::clientMoveAndDisplay() +{ + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + //simple dynamics world doesn't handle fixed-time-stepping + float ms = getDeltaTimeMicroseconds(); + + ///step the simulation + if (m_dynamicsWorld) + { + m_dynamicsWorld->stepSimulation(ms / 1000000.f); + //optional but useful: debug drawing + m_dynamicsWorld->debugDrawWorld(); + } + - FILE* f2 = fopen("testFile.bullet","wb"); - fwrite(serializer->m_buffer,serializer->m_currentSize,1,f2); - fclose(f2); -#endif + renderme(); + + showMessage(); + + glFlush(); + + swapBuffers(); } + +void FractureDemo::showMessage() +{ + if((getDebugMode() & btIDebugDraw::DBG_DrawText)) + { + setOrthographicProjection(); + glDisable(GL_LIGHTING); + glColor3f(0, 0, 0); + char buf[124]; + + int lineWidth=350; + int xStart = m_glutScreenWidth - lineWidth; + int yStart = 20; + + glRasterPos3f(xStart, yStart, 0); + btFractureDynamicsWorld* world = (btFractureDynamicsWorld*)m_dynamicsWorld; + if (world->getFractureMode()) + { + sprintf(buf,"Fracture mode"); + } else + { + sprintf(buf,"Glue mode"); + } + GLDebugDrawString(xStart,20,buf); + yStart+=20; + glRasterPos3f(xStart, yStart, 0); + sprintf(buf,"f to toggle fracture/glue mode"); + yStart+=20; + GLDebugDrawString(xStart,yStart,buf); + resetPerspectiveProjection(); + glEnable(GL_LIGHTING); + } + +} + + +void FractureDemo::displayCallback(void) { + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + renderme(); + + showMessage(); + + //optional but useful: debug drawing to detect problems + if (m_dynamicsWorld) + m_dynamicsWorld->debugDrawWorld(); + + glFlush(); + swapBuffers(); +} + +void FractureDemo::keyboardCallback(unsigned char key, int x, int y) +{ + if (key=='f') + { + } else + { + PlatformDemoApplication::keyboardCallback(key,x,y); + } +} + +void FractureDemo::keyboardUpCallback(unsigned char key, int x, int y) +{ + if (key=='f') + { + btFractureDynamicsWorld* world = (btFractureDynamicsWorld*)m_dynamicsWorld; + world->setFractureMode(!world->getFractureMode()); + } + + PlatformDemoApplication::keyboardUpCallback(key,x,y); + +} + + + + + + + + + + + + + +void FractureDemo::shootBox(const btVector3& destination) +{ + + if (m_dynamicsWorld) + { + btScalar mass = 1.f; + btTransform startTransform; + startTransform.setIdentity(); + btVector3 camPos = getCameraPosition(); + startTransform.setOrigin(camPos); + + setShootBoxShape (); + + btAssert((!m_shootBoxShape || m_shootBoxShape->getShapeType() != INVALID_SHAPE_PROXYTYPE)); + + //rigidbody is dynamic if and only if mass is non zero, otherwise static + bool isDynamic = (mass != 0.f); + + btVector3 localInertia(0,0,0); + if (isDynamic) + m_shootBoxShape->calculateLocalInertia(mass,localInertia); + + //using motionstate is recommended, it provides interpolation capabilities, and only synchronizes 'active' objects + + btFractureBody* body = new btFractureBody(mass,0,m_shootBoxShape,localInertia,&mass,1,m_dynamicsWorld); + + body->setWorldTransform(startTransform); + + m_dynamicsWorld->addRigidBody(body); + + + body->setLinearFactor(btVector3(1,1,1)); + //body->setRestitution(1); + + btVector3 linVel(destination[0]-camPos[0],destination[1]-camPos[1],destination[2]-camPos[2]); + linVel.normalize(); + linVel*=m_ShootBoxInitialSpeed; + + body->getWorldTransform().setOrigin(camPos); + body->getWorldTransform().setRotation(btQuaternion(0,0,0,1)); + body->setLinearVelocity(linVel); + body->setAngularVelocity(btVector3(0,0,0)); + body->setCcdMotionThreshold(1.); + body->setCcdSweptSphereRadius(0.2f); + + } +} + + + + void FractureDemo::exitPhysics() diff --git a/Demos/FractureDemo/btFractureBody.cpp b/Demos/FractureDemo/btFractureBody.cpp new file mode 100644 index 000000000..e7513f647 --- /dev/null +++ b/Demos/FractureDemo/btFractureBody.cpp @@ -0,0 +1,138 @@ + +#include "btFractureBody.h" +#include "BulletCollision/CollisionDispatch/btCollisionWorld.h" +#include "BulletCollision/CollisionShapes/btCompoundShape.h" +#include "BulletDynamics/Dynamics/btDynamicsWorld.h" + + + +void btFractureBody::recomputeConnectivity(btCollisionWorld* world) +{ + m_connections.clear(); + //@todo use the AABB tree to avoid N^2 checks + + if (getCollisionShape()->isCompound()) + { + btCompoundShape* compound = (btCompoundShape*)getCollisionShape(); + for (int i=0;igetNumChildShapes();i++) + { + for (int j=i+1;jgetNumChildShapes();j++) + { + + struct MyContactResultCallback : public btCollisionWorld::ContactResultCallback + { + bool m_connected; + MyContactResultCallback() :m_connected(false) + { + } + virtual btScalar addSingleResult(btManifoldPoint& cp, const btCollisionObject* colObj0,int partId0,int index0,const btCollisionObject* colObj1,int partId1,int index1) + { + //@todo additional check on cp? + m_connected = true; + return 1.f; + } + }; + + MyContactResultCallback result; + + btCollisionObject obA; + obA.setWorldTransform(compound->getChildTransform(i)); + obA.setCollisionShape(compound->getChildShape(i)); + btCollisionObject obB; + obB.setWorldTransform(compound->getChildTransform(j)); + obB.setCollisionShape(compound->getChildShape(j)); + world->contactPairTest(&obA,&obB,result); + if (result.m_connected) + { + btConnection tmp; + tmp.m_childIndex0 = i; + tmp.m_childIndex1 = j; + tmp.m_childShape0 = compound->getChildShape(i); + tmp.m_childShape1 = compound->getChildShape(j); + tmp.m_strength = 1.f;//?? + m_connections.push_back(tmp); + } + } + } + } + + +} + +btCompoundShape* btFractureBody::shiftTransformDistributeMass(btCompoundShape* boxCompound,btScalar mass,btTransform& shift) +{ + + btVector3 principalInertia; + + btScalar* masses = new btScalar[boxCompound->getNumChildShapes()]; + for (int j=0;jgetNumChildShapes();j++) + { + //evenly distribute mass + masses[j]=mass/boxCompound->getNumChildShapes(); + } + + return shiftTransform(boxCompound,masses,shift,principalInertia); + +} + + +btCompoundShape* btFractureBody::shiftTransform(btCompoundShape* boxCompound,btScalar* masses,btTransform& shift, btVector3& principalInertia) +{ + btTransform principal; + + boxCompound->calculatePrincipalAxisTransform(masses,principal,principalInertia); + + + ///create a new compound with world transform/center of mass properly aligned with the principal axis + + ///non-recursive compound shapes perform better + +#ifdef USE_RECURSIVE_COMPOUND + + btCompoundShape* newCompound = new btCompoundShape(); + newCompound->addChildShape(principal.inverse(),boxCompound); + newBoxCompound = newCompound; + //m_collisionShapes.push_back(newCompound); + + //btDefaultMotionState* myMotionState = new btDefaultMotionState(startTransform); + //btRigidBody::btRigidBodyConstructionInfo rbInfo(mass,myMotionState,newCompound,principalInertia); + +#else +#ifdef CHANGE_COMPOUND_INPLACE + newBoxCompound = boxCompound; + for (int i=0;igetNumChildShapes();i++) + { + btTransform newChildTransform = principal.inverse()*boxCompound->getChildTransform(i); + ///updateChildTransform is really slow, because it re-calculates the AABB each time. todo: add option to disable this update + boxCompound->updateChildTransform(i,newChildTransform); + } + bool isDynamic = (mass != 0.f); + btVector3 localInertia(0,0,0); + if (isDynamic) + boxCompound->calculateLocalInertia(mass,localInertia); + +#else + ///creation is faster using a new compound to store the shifted children + btCompoundShape* newBoxCompound = new btCompoundShape(); + for (int i=0;igetNumChildShapes();i++) + { + btTransform newChildTransform = principal.inverse()*boxCompound->getChildTransform(i); + ///updateChildTransform is really slow, because it re-calculates the AABB each time. todo: add option to disable this update + newBoxCompound->addChildShape(newChildTransform,boxCompound->getChildShape(i)); + } + + + +#endif + +#endif//USE_RECURSIVE_COMPOUND + + shift = principal; + return newBoxCompound; +} + + + + + + diff --git a/Demos/FractureDemo/btFractureBody.h b/Demos/FractureDemo/btFractureBody.h new file mode 100644 index 000000000..a21d992db --- /dev/null +++ b/Demos/FractureDemo/btFractureBody.h @@ -0,0 +1,78 @@ + +#ifndef BT_FRACTURE_BODY +#define BT_FRACTURE_BODY + +class btCollisionShape; +class btDynamicsWorld; +class btCollisionWorld; +class btCompoundShape; +class btManifoldPoint; + +#include "LinearMath/btAlignedObjectArray.h" +#include "BulletDynamics/Dynamics/btRigidBody.h" + +#define CUSTOM_FRACTURE_TYPE (btRigidBody::CO_USER_TYPE+1) + + +struct btConnection +{ + + btCollisionShape* m_childShape0; + btCollisionShape* m_childShape1; + int m_childIndex0; + int m_childIndex1; + btScalar m_strength; +}; + +class btFractureBody : public btRigidBody +{ + //connections +public: + + btDynamicsWorld* m_world; + btAlignedObjectArray m_masses; + btAlignedObjectArray m_connections; + + + + btFractureBody( const btRigidBodyConstructionInfo& constructionInfo, btDynamicsWorld* world) + :btRigidBody(constructionInfo), + m_world(world) + { + m_masses.push_back(constructionInfo.m_mass); + m_internalType=CUSTOM_FRACTURE_TYPE+CO_RIGID_BODY; + } + + + + ///btRigidBody constructor for backwards compatibility. + ///To specify friction (etc) during rigid body construction, please use the other constructor (using btRigidBodyConstructionInfo) + btFractureBody( btScalar mass, btMotionState* motionState, btCollisionShape* collisionShape, const btVector3& localInertia, btScalar* masses, int numMasses, btDynamicsWorld* world) + :btRigidBody(mass,motionState,collisionShape,localInertia), + m_world(world) + { + + for (int i=0;igetNumManifolds(); + + btUnionFind unionFind; + + int index = 0; + { + + int i; + for (i=0;iisStaticOrKinematicObject()) + { + 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;igetManifoldByIndexInternal(i); + if (!manifold->getNumContacts()) + 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;aiisStaticOrKinematicObject()) + { + 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 removedObjects; + + //update the sleeping state for bodies, if all are sleeping + for ( startIslandIndex=0;startIslandIndexgetInternalType()& CUSTOM_FRACTURE_TYPE) + { + fractureObjectIndex = i; + } + btRigidBody* otherObject = btRigidBody::upcast(colObj0); + if (!otherObject || !otherObject->getInvMass()) + continue; + numObjects++; + } + + + if (fractureObjectIndex>=0 && numObjects>1) + { + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + btFractureBody* fracObj = (btFractureBody*)getCollisionObjectArray()[fractureObjectIndex]; + + removedObjects.push_back(fracObj); + m_fractureBodies.remove(fracObj); + + btAlignedObjectArray massArray; + 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;cgetNumChildShapes();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;idxgetInvMass()) + continue; + + 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;cgetNumChildShapes();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); + 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 m_contactManifolds; +}; + + + +void btFractureDynamicsWorld::solveConstraints(btContactSolverInfo& solverInfo) +{ + //todo: add some logic + +// save all velocities +// 1) that if a fracture object break +// 2) revert all velocties +// 3) apply impulses for the fracture bodies at the contact locations +// 4)and run the constaint solver again + + btDiscreteDynamicsWorld::solveConstraints(solverInfo); + + fractureCallback(solverInfo.m_timeStep); +} + + +void 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;igetNumChildShapes();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); +} + +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; + 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 tags; + tags.resize(numChildren); + int i, index = 0; + for ( i=0;im_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 removedObjects; + + int numIslands = 0; + + for ( startIslandIndex=0;startIslandIndex masses; + + int idx; + for (idx=startIslandIndex;idxgetChildShape(i); + newCompound->addChildShape(compound->getChildTransform(i),compound->getChildShape(i)); + masses.push_back(fracObj->m_masses[i]); + numShapes++; + } + if (numShapes) + { + addNewBody(fracObj->getWorldTransform(),&masses[0],newCompound); + numIslands++; + } + } + + + + + + removeRigidBody(fracObj);//should it also be removed from the array? + + +} + + + +void btFractureDynamicsWorld::fractureCallback( btScalar timeStep) +{ + + btAlignedObjectArray sFracturePairs; + + if (!m_fracturingMode) + { + glueCallback(timeStep); + return; + } + + int numManifolds = getDispatcher()->getNumManifolds(); + + sFracturePairs.clear(); + + + for (int i=0;igetManifoldByIndexInternal(i); + if (!manifold->getNumContacts()) + continue; + + btScalar totalImpact = 0.f; + for (int p=0;pgetNumContacts();p++) + { + totalImpact += manifold->getContactPoint(p).m_appliedImpulse; + } + + static float maxImpact = 0; + if (totalImpact>maxImpact) + maxImpact = totalImpact; + + //some threshold otherwise resting contact would break objects after a while + if (totalImpact < 10) + 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 (f0getBody1(); + btRigidBody* otherOb = btRigidBody::upcast(colOb); + // if (!otherOb->getInvMass()) + // continue; + + int pi=-1; + + for (int p=0;pgetBody0(); + btRigidBody* otherOb = btRigidBody::upcast(colOb); + // if (!otherOb->getInvMass()) + // continue; + + + int pi=-1; + + for (int p=0;pgetCollisionShape()->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;jgetNumContacts();k++) + { + btManifoldPoint& pt = manifold->getContactPoint(k); + if (manifold->getBody0()==sFracturePairs[i].m_fracObj) + { + for (int f=0;fm_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;fm_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(); + +} + diff --git a/Demos/FractureDemo/btFractureDynamicsWorld.h b/Demos/FractureDemo/btFractureDynamicsWorld.h new file mode 100644 index 000000000..d6208436d --- /dev/null +++ b/Demos/FractureDemo/btFractureDynamicsWorld.h @@ -0,0 +1,48 @@ +#ifndef _BT_FRACTURE_DYNAMICS_WORLD_H +#define _BT_FRACTURE_DYNAMICS_WORLD_H + +#include "BulletDynamics/Dynamics/btDiscreteDynamicsWorld.h" +#include "LinearMath/btAlignedObjectArray.h" + +class btFractureBody; +class btCompoundShape; +class btTransform; + + +///The btFractureDynamicsWorld class enabled basic glue and fracture of objects. +///If/once this implementation is stablized/tested we might merge it into btDiscreteDynamicsWorld and remove the class. +class btFractureDynamicsWorld : public btDiscreteDynamicsWorld +{ + btAlignedObjectArray m_fractureBodies; + + bool m_fracturingMode; + + void glueCallback(btScalar timeStep); + + void fractureCallback( btScalar timeStep); + + void addNewBody(const btTransform& oldTransform,btScalar* masses, btCompoundShape* oldCompound); + + void breakDisconnectedParts( btFractureBody* fracObj); + +public: + + btFractureDynamicsWorld ( btDispatcher* dispatcher,btBroadphaseInterface* pairCache,btConstraintSolver* constraintSolver,btCollisionConfiguration* collisionConfiguration); + + virtual void addRigidBody(btRigidBody* body); + + virtual void removeRigidBody(btRigidBody* body); + + void solveConstraints(btContactSolverInfo& solverInfo); + + ///either fracture or glue (!fracture) + void setFractureMode(bool fracture) + { + m_fracturingMode = fracture; + } + + bool getFractureMode() const { return m_fracturingMode;} +}; + +#endif //_BT_FRACTURE_DYNAMICS_WORLD_H +