diff --git a/Demos/FractureDemo/FractureDemo.cpp b/Demos/FractureDemo/FractureDemo.cpp index 32b3cf511..e2602de99 100644 --- a/Demos/FractureDemo/FractureDemo.cpp +++ b/Demos/FractureDemo/FractureDemo.cpp @@ -50,7 +50,7 @@ int sFrameNumber = 0; void FractureDemo::initPhysics() { - + setTexturing(true); setShadows(true); @@ -73,45 +73,37 @@ void FractureDemo::initPhysics() //m_dynamicsWorld = new btDiscreteDynamicsWorld(m_dispatcher,m_broadphase,m_solver,m_collisionConfiguration); - m_dynamicsWorld = new btFractureDynamicsWorld(m_dispatcher,m_broadphase,m_solver,m_collisionConfiguration); - - + btFractureDynamicsWorld* fractureWorld = new btFractureDynamicsWorld(m_dispatcher,m_broadphase,m_solver,m_collisionConfiguration); + m_dynamicsWorld = fractureWorld; + + m_dynamicsWorld->getDispatchInfo().m_convexMaxDistanceUseCPT = true; + m_ShootBoxInitialSpeed=100; //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->setGravity(btVector3(0,-10,0)); - ///create a few basic rigid bodies - btCollisionShape* groundShape = new btBoxShape(btVector3(btScalar(50.),btScalar(50.),btScalar(50.))); -// btCollisionShape* groundShape = new btStaticPlaneShape(btVector3(0,1,0),50); - - m_collisionShapes.push_back(groundShape); - - btTransform groundTransform; - groundTransform.setIdentity(); - groundTransform.setOrigin(btVector3(0,-50,0)); - - //We can also use DemoApplication::localCreateRigidBody, but for clarity it is provided here: { - btScalar mass(0.); - - //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) - groundShape->calculateLocalInertia(mass,localInertia); - - //using motionstate is recommended, it provides interpolation capabilities, and only synchronizes 'active' objects - btDefaultMotionState* myMotionState = new btDefaultMotionState(groundTransform); - btRigidBody::btRigidBodyConstructionInfo rbInfo(mass,myMotionState,groundShape,localInertia); - btRigidBody* body = new btRigidBody(rbInfo); - - //add the body to the dynamics world - m_dynamicsWorld->addRigidBody(body); + ///create a few basic rigid bodies + btCollisionShape* groundShape = new btBoxShape(btVector3(50,1,50)); + /// btCollisionShape* groundShape = new btStaticPlaneShape(btVector3(0,1,0),0); + m_collisionShapes.push_back(groundShape); + btTransform groundTransform; + groundTransform.setIdentity(); + groundTransform.setOrigin(btVector3(0,0,0)); + localCreateRigidBody(0.f,groundTransform,groundShape); } + { + ///create a few basic rigid bodies + btCollisionShape* shape = new btBoxShape(btVector3(1,1,1)); + m_collisionShapes.push_back(shape); + btTransform tr; + tr.setIdentity(); + tr.setOrigin(btVector3(5,2,0)); + localCreateRigidBody(0.f,tr,shape); + } + + { //create a few dynamic rigidbodies @@ -136,45 +128,52 @@ void FractureDemo::initPhysics() colShape->calculateLocalInertia(mass,localInertia); - int gNumObjects = 10; + int gNumObjects = 10; - for (int i=0;isetActivationState(ISLAND_SLEEPING); + //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); + body->setLinearVelocity(btVector3(0,-10,0)); - m_dynamicsWorld->addRigidBody(body); - body->setActivationState(ISLAND_SLEEPING); + m_dynamicsWorld->addRigidBody(body); - - } + } } - clientResetScene(); + + fractureWorld->stepSimulation(1./60.,0); + fractureWorld->glueCallback(); + } +void FractureDemo::clientResetScene() +{ + exitPhysics(); + initPhysics(); +} + + 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) { @@ -182,13 +181,13 @@ void FractureDemo::clientMoveAndDisplay() //optional but useful: debug drawing m_dynamicsWorld->debugDrawWorld(); } - - + + renderme(); showMessage(); - + glFlush(); swapBuffers(); @@ -203,12 +202,11 @@ void FractureDemo::showMessage() glDisable(GL_LIGHTING); glColor3f(0, 0, 0); char buf[124]; - - int lineWidth=350; + + int lineWidth=380; int xStart = m_glutScreenWidth - lineWidth; int yStart = 20; - glRasterPos3f(xStart, yStart, 0); btFractureDynamicsWorld* world = (btFractureDynamicsWorld*)m_dynamicsWorld; if (world->getFractureMode()) { @@ -217,12 +215,14 @@ void FractureDemo::showMessage() { sprintf(buf,"Glue mode"); } - GLDebugDrawString(xStart,20,buf); - yStart+=20; - glRasterPos3f(xStart, yStart, 0); + GLDebugDrawString(xStart,yStart,buf); sprintf(buf,"f to toggle fracture/glue mode"); yStart+=20; GLDebugDrawString(xStart,yStart,buf); + sprintf(buf,"space to restart, mouse to pick/shoot"); + yStart+=20; + GLDebugDrawString(xStart,yStart,buf); + resetPerspectiveProjection(); glEnable(GL_LIGHTING); } @@ -233,7 +233,7 @@ void FractureDemo::showMessage() void FractureDemo::displayCallback(void) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - + renderme(); showMessage(); @@ -246,15 +246,6 @@ void FractureDemo::displayCallback(void) { 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) { @@ -265,21 +256,10 @@ void FractureDemo::keyboardUpCallback(unsigned char key, int x, int y) } PlatformDemoApplication::keyboardUpCallback(key,x,y); - + } - - - - - - - - - - - void FractureDemo::shootBox(const btVector3& destination) { @@ -305,12 +285,12 @@ void FractureDemo::shootBox(const btVector3& destination) //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); @@ -324,14 +304,14 @@ void FractureDemo::shootBox(const btVector3& destination) body->setAngularVelocity(btVector3(0,0,0)); body->setCcdMotionThreshold(1.); body->setCcdSweptSphereRadius(0.2f); - + } } - + void FractureDemo::exitPhysics() { @@ -359,17 +339,23 @@ void FractureDemo::exitPhysics() delete shape; } + m_collisionShapes.clear(); + delete m_dynamicsWorld; - + m_dynamicsWorld=0; + delete m_solver; - + m_solver=0; + delete m_broadphase; - + m_broadphase=0; + delete m_dispatcher; + m_dispatcher=0; delete m_collisionConfiguration; + m_collisionConfiguration=0; - } diff --git a/Demos/FractureDemo/FractureDemo.h b/Demos/FractureDemo/FractureDemo.h index 81b7596e1..9e38f18c6 100644 --- a/Demos/FractureDemo/FractureDemo.h +++ b/Demos/FractureDemo/FractureDemo.h @@ -66,11 +66,11 @@ class FractureDemo : public PlatformDemoApplication virtual void clientMoveAndDisplay(); virtual void displayCallback(); - - virtual void keyboardCallback(unsigned char key, int x, int y); - + virtual void keyboardUpCallback(unsigned char key, int x, int y); + virtual void clientResetScene(); + static DemoApplication* Create() { FractureDemo* demo = new FractureDemo; diff --git a/Demos/FractureDemo/btFractureDynamicsWorld.cpp b/Demos/FractureDemo/btFractureDynamicsWorld.cpp index 50e9d2073..6a058a69a 100644 --- a/Demos/FractureDemo/btFractureDynamicsWorld.cpp +++ b/Demos/FractureDemo/btFractureDynamicsWorld.cpp @@ -7,17 +7,19 @@ btFractureDynamicsWorld::btFractureDynamicsWorld ( btDispatcher* dispatcher,btBroadphaseInterface* pairCache,btConstraintSolver* constraintSolver,btCollisionConfiguration* collisionConfiguration) :btDiscreteDynamicsWorld(dispatcher,pairCache,constraintSolver,collisionConfiguration), -m_fracturingMode(false) +m_fracturingMode(true) { } -void btFractureDynamicsWorld::glueCallback(btScalar timeStep) +void btFractureDynamicsWorld::glueCallback() { int numManifolds = getDispatcher()->getNumManifolds(); + ///first build the islands based on axis aligned bounding box overlap + btUnionFind unionFind; int index = 0; @@ -61,7 +63,7 @@ void btFractureDynamicsWorld::glueCallback(btScalar timeStep) btRigidBody* body0 = btRigidBody::upcast(colObj0); btRigidBody* body1 = btRigidBody::upcast(colObj1); - + if (!colObj0->isStaticOrKinematicObject() && !colObj1->isStaticOrKinematicObject()) { unionFind.unite(tag0, tag1); @@ -70,7 +72,7 @@ void btFractureDynamicsWorld::glueCallback(btScalar timeStep) - + numElem = unionFind.getNumElements(); @@ -82,9 +84,9 @@ void btFractureDynamicsWorld::glueCallback(btScalar timeStep) 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; @@ -102,7 +104,7 @@ void btFractureDynamicsWorld::glueCallback(btScalar timeStep) btAlignedObjectArray removedObjects; - //update the sleeping state for bodies, if all are sleeping + ///iterate over all islands for ( startIslandIndex=0;startIslandIndex=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 massArray; + + btAlignedObjectArray oldImpulses; + btAlignedObjectArray oldCenterOfMassesWS; + + oldImpulses.push_back(fracObj->getLinearVelocity()/1./fracObj->getInvMass()); + oldCenterOfMassesWS.push_back(fracObj->getCenterOfMassPosition()); + btScalar totalMass = 0.f; @@ -169,7 +177,7 @@ void btFractureDynamicsWorld::glueCallback(btScalar timeStep) for (idx=startIslandIndex;idxgetInvMass()) continue; - + + + oldImpulses.push_back(otherObject->getLinearVelocity()*(1.f/otherObject->getInvMass())); + oldCenterOfMassesWS.push_back(otherObject->getCenterOfMassPosition()); + removedObjects.push_back(otherObject); m_fractureBodies.remove((btFractureBody*)otherObject); @@ -208,7 +222,7 @@ void btFractureDynamicsWorld::glueCallback(btScalar timeStep) totalMass+=curMass; } - + btTransform shift; shift.setIdentity(); @@ -221,12 +235,22 @@ void btFractureDynamicsWorld::glueCallback(btScalar timeStep) 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;igetCenterOfMassPosition(); + const btVector3& imp = oldImpulses[i]; + newBody->applyImpulse(imp, rel_pos); + } + addRigidBody(newBody); } - + } //remove the objects from the world at the very end, @@ -255,24 +279,22 @@ struct btFracturePair 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 + // 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(solverInfo.m_timeStep); + fractureCallback(); } - -void btFractureDynamicsWorld::addNewBody(const btTransform& oldTransform,btScalar* masses, btCompoundShape* oldCompound) +btFractureBody* btFractureDynamicsWorld::addNewBody(const btTransform& oldTransform,btScalar* masses, btCompoundShape* oldCompound) { int i; - + btTransform shift; shift.setIdentity(); btVector3 localInertia; @@ -284,10 +306,11 @@ void btFractureDynamicsWorld::addNewBody(const btTransform& oldTransform,btScala 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) @@ -351,7 +374,7 @@ void btFractureDynamicsWorld::breakDisconnectedParts( btFractureBody* fracObj) } } numElem = unionFind.getNumElements(); - + index=0; for (int ai=0;aigetWorldTransform(),&masses[0],newCompound); + 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 -void btFractureDynamicsWorld::fractureCallback( btScalar timeStep) +void btFractureDynamicsWorld::fractureCallback( ) { btAlignedObjectArray sFracturePairs; if (!m_fracturingMode) { - glueCallback(timeStep); + glueCallback(); return; } @@ -429,28 +456,31 @@ void btFractureDynamicsWorld::fractureCallback( btScalar timeStep) 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; } + +// 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 < 10) + if (totalImpact < 40.f) continue; -// printf("strong impact\n"); + // printf("strong impact\n"); //@todo: add better logic to decide what parts to fracture @@ -479,8 +509,8 @@ void btFractureDynamicsWorld::fractureCallback( btScalar timeStep) btCollisionObject* colOb = (btCollisionObject*)manifold->getBody1(); btRigidBody* otherOb = btRigidBody::upcast(colOb); - // if (!otherOb->getInvMass()) - // continue; + // if (!otherOb->getInvMass()) + // continue; int pi=-1; @@ -504,16 +534,16 @@ void btFractureDynamicsWorld::fractureCallback( btScalar timeStep) 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; + // if (!otherOb->getInvMass()) + // continue; int pi=-1; @@ -549,7 +579,7 @@ void btFractureDynamicsWorld::fractureCallback( btScalar timeStep) { -// printf("fracturing\n"); + // printf("fracturing\n"); for (int i=0;igetCollisionShape()->isCompound()) { btTransform tr; @@ -585,7 +615,7 @@ void btFractureDynamicsWorld::fractureCallback( btScalar timeStep) { btConnection& connection = sFracturePairs[i].m_fracObj->m_connections[f]; if ( (connection.m_childIndex0 == pt.m_index0) || - (connection.m_childIndex1 == pt.m_index0) + (connection.m_childIndex1 == pt.m_index0) ) { connection.m_strength -= pt.m_appliedImpulse; @@ -603,7 +633,7 @@ void btFractureDynamicsWorld::fractureCallback( btScalar timeStep) { btConnection& connection = sFracturePairs[i].m_fracObj->m_connections[f]; if ( (connection.m_childIndex0 == pt.m_index1) || - (connection.m_childIndex1 == pt.m_index1) + (connection.m_childIndex1 == pt.m_index1) ) { connection.m_strength -= pt.m_appliedImpulse; diff --git a/Demos/FractureDemo/btFractureDynamicsWorld.h b/Demos/FractureDemo/btFractureDynamicsWorld.h index d6208436d..ad7e821a7 100644 --- a/Demos/FractureDemo/btFractureDynamicsWorld.h +++ b/Demos/FractureDemo/btFractureDynamicsWorld.h @@ -17,11 +17,7 @@ class btFractureDynamicsWorld : public btDiscreteDynamicsWorld bool m_fracturingMode; - void glueCallback(btScalar timeStep); - - void fractureCallback( btScalar timeStep); - - void addNewBody(const btTransform& oldTransform,btScalar* masses, btCompoundShape* oldCompound); + btFractureBody* addNewBody(const btTransform& oldTransform,btScalar* masses, btCompoundShape* oldCompound); void breakDisconnectedParts( btFractureBody* fracObj); @@ -42,6 +38,13 @@ public: } bool getFractureMode() const { return m_fracturingMode;} + + ///normally those callbacks are called internally by the 'solveConstraints' + void glueCallback(); + + ///normally those callbacks are called internally by the 'solveConstraints' + void fractureCallback(); + }; #endif //_BT_FRACTURE_DYNAMICS_WORLD_H diff --git a/src/BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.cpp b/src/BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.cpp index 0fc93cd37..ec0268f80 100644 --- a/src/BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.cpp +++ b/src/BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.cpp @@ -1115,12 +1115,13 @@ btScalar btSequentialImpulseConstraintSolver::solveGroupCacheFriendlyIterations( //should traverse the contacts random order... int iteration; { + solveGroupCacheFriendlySplitImpulseIterations(bodies ,numBodies,manifoldPtr, numManifolds,constraints,numConstraints,infoGlobal,debugDrawer,stackAlloc); + for ( iteration = 0;iteration