368 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			368 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
| Bullet Continuous Collision Detection and Physics Library
 | |
| Copyright (c) 2003-2006 Erwin Coumans  http://continuousphysics.com/Bullet/
 | |
| 
 | |
| This software is provided 'as-is', without any express or implied warranty.
 | |
| In no event will the authors be held liable for any damages arising from the use of this software.
 | |
| Permission is granted to anyone to use this software for any purpose, 
 | |
| including commercial applications, and to alter it and redistribute it freely, 
 | |
| subject to the following restrictions:
 | |
| 
 | |
| 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
 | |
| 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
 | |
| 3. This notice may not be removed or altered from any source distribution.
 | |
| */
 | |
| 
 | |
| 
 | |
| #include "btSoftMultiBodyDynamicsWorld.h"
 | |
| #include "LinearMath/btQuickprof.h"
 | |
| 
 | |
| //softbody & helpers
 | |
| #include "BulletSoftBody/btSoftBody.h"
 | |
| #include "BulletSoftBody/btSoftBodyHelpers.h"
 | |
| #include "BulletSoftBody/btSoftBodySolvers.h"
 | |
| #include "BulletSoftBody/btDefaultSoftBodySolver.h"
 | |
| #include "LinearMath/btSerializer.h"
 | |
| 
 | |
| 
 | |
| btSoftMultiBodyDynamicsWorld::btSoftMultiBodyDynamicsWorld(
 | |
| 	btDispatcher* dispatcher,
 | |
| 	btBroadphaseInterface* pairCache,
 | |
| 	btMultiBodyConstraintSolver* constraintSolver,
 | |
| 	btCollisionConfiguration* collisionConfiguration,
 | |
| 	btSoftBodySolver *softBodySolver ) : 
 | |
| 		btMultiBodyDynamicsWorld(dispatcher,pairCache,constraintSolver,collisionConfiguration),
 | |
|         m_softBodySolver( softBodySolver ),
 | |
|         m_ownsSolver(false)
 | |
| {
 | |
| 	if( !m_softBodySolver )
 | |
| 	{
 | |
| 		void* ptr = btAlignedAlloc(sizeof(btDefaultSoftBodySolver),16);
 | |
| 		m_softBodySolver = new(ptr) btDefaultSoftBodySolver();
 | |
| 		m_ownsSolver = true;
 | |
| 	}
 | |
| 
 | |
| 	m_drawFlags			=	fDrawFlags::Std;
 | |
| 	m_drawNodeTree		=	true;
 | |
| 	m_drawFaceTree		=	false;
 | |
| 	m_drawClusterTree	=	false;
 | |
| 	m_sbi.m_broadphase = pairCache;
 | |
| 	m_sbi.m_dispatcher = dispatcher;
 | |
| 	m_sbi.m_sparsesdf.Initialize();
 | |
| 	m_sbi.m_sparsesdf.Reset();
 | |
| 
 | |
| 	m_sbi.air_density		=	(btScalar)1.2;
 | |
| 	m_sbi.water_density	=	0;
 | |
| 	m_sbi.water_offset		=	0;
 | |
| 	m_sbi.water_normal		=	btVector3(0,0,0);
 | |
| 	m_sbi.m_gravity.setValue(0,-10,0);
 | |
| 
 | |
| 	m_sbi.m_sparsesdf.Initialize();
 | |
| 
 | |
| 
 | |
| }
 | |
| 
 | |
| btSoftMultiBodyDynamicsWorld::~btSoftMultiBodyDynamicsWorld()
 | |
| {
 | |
| 	if (m_ownsSolver)
 | |
| 	{
 | |
| 		m_softBodySolver->~btSoftBodySolver();
 | |
| 		btAlignedFree(m_softBodySolver);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void	btSoftMultiBodyDynamicsWorld::predictUnconstraintMotion(btScalar timeStep)
 | |
| {
 | |
| 	btDiscreteDynamicsWorld::predictUnconstraintMotion( timeStep );
 | |
| 	{
 | |
| 		BT_PROFILE("predictUnconstraintMotionSoftBody");
 | |
| 		m_softBodySolver->predictMotion( float(timeStep) );
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void	btSoftMultiBodyDynamicsWorld::internalSingleStepSimulation( btScalar timeStep )
 | |
| {
 | |
| 
 | |
| 	// Let the solver grab the soft bodies and if necessary optimize for it
 | |
| 	m_softBodySolver->optimize( getSoftBodyArray() );
 | |
| 
 | |
| 	if( !m_softBodySolver->checkInitialized() )
 | |
| 	{
 | |
| 		btAssert( "Solver initialization failed\n" );
 | |
| 	}
 | |
| 
 | |
| 	btDiscreteDynamicsWorld::internalSingleStepSimulation( timeStep );
 | |
| 
 | |
| 	///solve soft bodies constraints
 | |
| 	solveSoftBodiesConstraints( timeStep );
 | |
| 
 | |
| 	//self collisions
 | |
| 	for ( int i=0;i<m_softBodies.size();i++)
 | |
| 	{
 | |
| 		btSoftBody*	psb=(btSoftBody*)m_softBodies[i];
 | |
| 		psb->defaultCollisionHandler(psb);
 | |
| 	}
 | |
| 
 | |
| 	///update soft bodies
 | |
| 	m_softBodySolver->updateSoftBodies( );
 | |
| 	
 | |
| 	// End solver-wise simulation step
 | |
| 	// ///////////////////////////////
 | |
| 
 | |
| }
 | |
| 
 | |
| void	btSoftMultiBodyDynamicsWorld::solveSoftBodiesConstraints( btScalar timeStep )
 | |
| {
 | |
| 	BT_PROFILE("solveSoftConstraints");
 | |
| 
 | |
| 	if(m_softBodies.size())
 | |
| 	{
 | |
| 		btSoftBody::solveClusters(m_softBodies);
 | |
| 	}
 | |
| 
 | |
| 	// Solve constraints solver-wise
 | |
| 	m_softBodySolver->solveConstraints( timeStep * m_softBodySolver->getTimeScale() );
 | |
| 
 | |
| }
 | |
| 
 | |
| void	btSoftMultiBodyDynamicsWorld::addSoftBody(btSoftBody* body, int collisionFilterGroup, int collisionFilterMask)
 | |
| {
 | |
| 	m_softBodies.push_back(body);
 | |
| 
 | |
| 	// Set the soft body solver that will deal with this body
 | |
| 	// to be the world's solver
 | |
| 	body->setSoftBodySolver( m_softBodySolver );
 | |
| 
 | |
| 	btCollisionWorld::addCollisionObject(body,
 | |
| 		collisionFilterGroup,
 | |
| 		collisionFilterMask);
 | |
| 
 | |
| }
 | |
| 
 | |
| void	btSoftMultiBodyDynamicsWorld::removeSoftBody(btSoftBody* body)
 | |
| {
 | |
| 	m_softBodies.remove(body);
 | |
| 
 | |
| 	btCollisionWorld::removeCollisionObject(body);
 | |
| }
 | |
| 
 | |
| void	btSoftMultiBodyDynamicsWorld::removeCollisionObject(btCollisionObject* collisionObject)
 | |
| {
 | |
| 	btSoftBody* body = btSoftBody::upcast(collisionObject);
 | |
| 	if (body)
 | |
| 		removeSoftBody(body);
 | |
| 	else
 | |
| 		btDiscreteDynamicsWorld::removeCollisionObject(collisionObject);
 | |
| }
 | |
| 
 | |
| void	btSoftMultiBodyDynamicsWorld::debugDrawWorld()
 | |
| {
 | |
| 	btDiscreteDynamicsWorld::debugDrawWorld();
 | |
| 
 | |
| 	if (getDebugDrawer())
 | |
| 	{
 | |
| 		int i;
 | |
| 		for (  i=0;i<this->m_softBodies.size();i++)
 | |
| 		{
 | |
| 			btSoftBody*	psb=(btSoftBody*)this->m_softBodies[i];
 | |
| 			if (getDebugDrawer() && (getDebugDrawer()->getDebugMode() & (btIDebugDraw::DBG_DrawWireframe)))
 | |
| 			{
 | |
| 				btSoftBodyHelpers::DrawFrame(psb,m_debugDrawer);
 | |
| 				btSoftBodyHelpers::Draw(psb,m_debugDrawer,m_drawFlags);
 | |
| 			}
 | |
| 			
 | |
| 			if (m_debugDrawer && (m_debugDrawer->getDebugMode() & btIDebugDraw::DBG_DrawAabb))
 | |
| 			{
 | |
| 				if(m_drawNodeTree)		btSoftBodyHelpers::DrawNodeTree(psb,m_debugDrawer);
 | |
| 				if(m_drawFaceTree)		btSoftBodyHelpers::DrawFaceTree(psb,m_debugDrawer);
 | |
| 				if(m_drawClusterTree)	btSoftBodyHelpers::DrawClusterTree(psb,m_debugDrawer);
 | |
| 			}
 | |
| 		}		
 | |
| 	}	
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| struct btSoftSingleRayCallback : public btBroadphaseRayCallback
 | |
| {
 | |
| 	btVector3	m_rayFromWorld;
 | |
| 	btVector3	m_rayToWorld;
 | |
| 	btTransform	m_rayFromTrans;
 | |
| 	btTransform	m_rayToTrans;
 | |
| 	btVector3	m_hitNormal;
 | |
| 
 | |
| 	const btSoftMultiBodyDynamicsWorld*	m_world;
 | |
| 	btCollisionWorld::RayResultCallback&	m_resultCallback;
 | |
| 
 | |
| 	btSoftSingleRayCallback(const btVector3& rayFromWorld,const btVector3& rayToWorld,const btSoftMultiBodyDynamicsWorld* world,btCollisionWorld::RayResultCallback& resultCallback)
 | |
| 	:m_rayFromWorld(rayFromWorld),
 | |
| 	m_rayToWorld(rayToWorld),
 | |
| 	m_world(world),
 | |
| 	m_resultCallback(resultCallback)
 | |
| 	{
 | |
| 		m_rayFromTrans.setIdentity();
 | |
| 		m_rayFromTrans.setOrigin(m_rayFromWorld);
 | |
| 		m_rayToTrans.setIdentity();
 | |
| 		m_rayToTrans.setOrigin(m_rayToWorld);
 | |
| 
 | |
| 		btVector3 rayDir = (rayToWorld-rayFromWorld);
 | |
| 
 | |
| 		rayDir.normalize ();
 | |
| 		///what about division by zero? --> just set rayDirection[i] to INF/1e30
 | |
| 		m_rayDirectionInverse[0] = rayDir[0] == btScalar(0.0) ? btScalar(1e30) : btScalar(1.0) / rayDir[0];
 | |
| 		m_rayDirectionInverse[1] = rayDir[1] == btScalar(0.0) ? btScalar(1e30) : btScalar(1.0) / rayDir[1];
 | |
| 		m_rayDirectionInverse[2] = rayDir[2] == btScalar(0.0) ? btScalar(1e30) : btScalar(1.0) / rayDir[2];
 | |
| 		m_signs[0] = m_rayDirectionInverse[0] < 0.0;
 | |
| 		m_signs[1] = m_rayDirectionInverse[1] < 0.0;
 | |
| 		m_signs[2] = m_rayDirectionInverse[2] < 0.0;
 | |
| 
 | |
| 		m_lambda_max = rayDir.dot(m_rayToWorld-m_rayFromWorld);
 | |
| 
 | |
| 	}
 | |
| 
 | |
| 	
 | |
| 
 | |
| 	virtual bool	process(const btBroadphaseProxy* proxy)
 | |
| 	{
 | |
| 		///terminate further ray tests, once the closestHitFraction reached zero
 | |
| 		if (m_resultCallback.m_closestHitFraction == btScalar(0.f))
 | |
| 			return false;
 | |
| 
 | |
| 		btCollisionObject*	collisionObject = (btCollisionObject*)proxy->m_clientObject;
 | |
| 
 | |
| 		//only perform raycast if filterMask matches
 | |
| 		if(m_resultCallback.needsCollision(collisionObject->getBroadphaseHandle())) 
 | |
| 		{
 | |
| 			//RigidcollisionObject* collisionObject = ctrl->GetRigidcollisionObject();
 | |
| 			//btVector3 collisionObjectAabbMin,collisionObjectAabbMax;
 | |
| #if 0
 | |
| #ifdef RECALCULATE_AABB
 | |
| 			btVector3 collisionObjectAabbMin,collisionObjectAabbMax;
 | |
| 			collisionObject->getCollisionShape()->getAabb(collisionObject->getWorldTransform(),collisionObjectAabbMin,collisionObjectAabbMax);
 | |
| #else
 | |
| 			//getBroadphase()->getAabb(collisionObject->getBroadphaseHandle(),collisionObjectAabbMin,collisionObjectAabbMax);
 | |
| 			const btVector3& collisionObjectAabbMin = collisionObject->getBroadphaseHandle()->m_aabbMin;
 | |
| 			const btVector3& collisionObjectAabbMax = collisionObject->getBroadphaseHandle()->m_aabbMax;
 | |
| #endif
 | |
| #endif
 | |
| 			//btScalar hitLambda = m_resultCallback.m_closestHitFraction;
 | |
| 			//culling already done by broadphase
 | |
| 			//if (btRayAabb(m_rayFromWorld,m_rayToWorld,collisionObjectAabbMin,collisionObjectAabbMax,hitLambda,m_hitNormal))
 | |
| 			{
 | |
| 				m_world->rayTestSingle(m_rayFromTrans,m_rayToTrans,
 | |
| 					collisionObject,
 | |
| 						collisionObject->getCollisionShape(),
 | |
| 						collisionObject->getWorldTransform(),
 | |
| 						m_resultCallback);
 | |
| 			}
 | |
| 		}
 | |
| 		return true;
 | |
| 	}
 | |
| };
 | |
| 
 | |
| void	btSoftMultiBodyDynamicsWorld::rayTest(const btVector3& rayFromWorld, const btVector3& rayToWorld, RayResultCallback& resultCallback) const
 | |
| {
 | |
| 	BT_PROFILE("rayTest");
 | |
| 	/// use the broadphase to accelerate the search for objects, based on their aabb
 | |
| 	/// and for each object with ray-aabb overlap, perform an exact ray test
 | |
| 	btSoftSingleRayCallback rayCB(rayFromWorld,rayToWorld,this,resultCallback);
 | |
| 
 | |
| #ifndef USE_BRUTEFORCE_RAYBROADPHASE
 | |
| 	m_broadphasePairCache->rayTest(rayFromWorld,rayToWorld,rayCB);
 | |
| #else
 | |
| 	for (int i=0;i<this->getNumCollisionObjects();i++)
 | |
| 	{
 | |
| 		rayCB.process(m_collisionObjects[i]->getBroadphaseHandle());
 | |
| 	}	
 | |
| #endif //USE_BRUTEFORCE_RAYBROADPHASE
 | |
| 
 | |
| }
 | |
| 
 | |
| 
 | |
| void	btSoftMultiBodyDynamicsWorld::rayTestSingle(const btTransform& rayFromTrans,const btTransform& rayToTrans,
 | |
| 					  btCollisionObject* collisionObject,
 | |
| 					  const btCollisionShape* collisionShape,
 | |
| 					  const btTransform& colObjWorldTransform,
 | |
| 					  RayResultCallback& resultCallback)
 | |
| {
 | |
| 	if (collisionShape->isSoftBody()) {
 | |
| 		btSoftBody* softBody = btSoftBody::upcast(collisionObject);
 | |
| 		if (softBody) {
 | |
| 			btSoftBody::sRayCast softResult;
 | |
| 			if (softBody->rayTest(rayFromTrans.getOrigin(), rayToTrans.getOrigin(), softResult)) 
 | |
| 			{
 | |
| 				
 | |
| 				if (softResult.fraction<= resultCallback.m_closestHitFraction)
 | |
| 				{
 | |
| 
 | |
| 					btCollisionWorld::LocalShapeInfo shapeInfo;
 | |
| 					shapeInfo.m_shapePart = 0;
 | |
| 					shapeInfo.m_triangleIndex = softResult.index;
 | |
| 					// get the normal
 | |
| 					btVector3 rayDir = rayToTrans.getOrigin() - rayFromTrans.getOrigin();
 | |
| 					btVector3 normal=-rayDir;
 | |
| 					normal.normalize();
 | |
| 
 | |
| 					if (softResult.feature == btSoftBody::eFeature::Face)
 | |
| 					{
 | |
| 						normal = softBody->m_faces[softResult.index].m_normal;
 | |
| 						if (normal.dot(rayDir) > 0) {
 | |
| 							// normal always point toward origin of the ray
 | |
| 							normal = -normal;
 | |
| 						}
 | |
| 					}
 | |
| 	
 | |
| 					btCollisionWorld::LocalRayResult rayResult
 | |
| 						(collisionObject,
 | |
| 						 &shapeInfo,
 | |
| 						 normal,
 | |
| 						 softResult.fraction);
 | |
| 					bool	normalInWorldSpace = true;
 | |
| 					resultCallback.addSingleResult(rayResult,normalInWorldSpace);
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	} 
 | |
| 	else {
 | |
| 		btCollisionWorld::rayTestSingle(rayFromTrans,rayToTrans,collisionObject,collisionShape,colObjWorldTransform,resultCallback);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 
 | |
| void	btSoftMultiBodyDynamicsWorld::serializeSoftBodies(btSerializer* serializer)
 | |
| {
 | |
| 	int i;
 | |
| 	//serialize all collision objects
 | |
| 	for (i=0;i<m_collisionObjects.size();i++)
 | |
| 	{
 | |
| 		btCollisionObject* colObj = m_collisionObjects[i];
 | |
| 		if (colObj->getInternalType() & btCollisionObject::CO_SOFT_BODY)
 | |
| 		{
 | |
| 			int len = colObj->calculateSerializeBufferSize();
 | |
| 			btChunk* chunk = serializer->allocate(len,1);
 | |
| 			const char* structType = colObj->serialize(chunk->m_oldPtr, serializer);
 | |
| 			serializer->finalizeChunk(chunk,structType,BT_SOFTBODY_CODE,colObj);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| }
 | |
| 
 | |
| void	btSoftMultiBodyDynamicsWorld::serialize(btSerializer* serializer)
 | |
| {
 | |
| 
 | |
| 	serializer->startSerialization();
 | |
| 
 | |
| 	serializeDynamicsWorldInfo( serializer);
 | |
| 
 | |
| 	serializeSoftBodies(serializer);
 | |
| 
 | |
| 	serializeRigidBodies(serializer);
 | |
| 
 | |
| 	serializeCollisionObjects(serializer);
 | |
| 
 | |
| 	serializer->finishSerialization();
 | |
| }
 | |
| 
 | |
| 
 |