PfxFloat pfxContactCapsuleCapsule(
	PfxVector3 &normal,PfxPoint3 &pointA,PfxPoint3 &pointB,
	void *shapeA,const PfxTransform3 &transformA,
	void *shapeB,const PfxTransform3 &transformB,
	PfxFloat distanceThreshold)
{
	PfxCapsule &capsuleA = *((PfxCapsule*)shapeA);
	PfxCapsule &capsuleB = *((PfxCapsule*)shapeB);

	PfxVector3 directionA = transformA.getUpper3x3().getCol0();
	PfxVector3 translationA = transformA.getTranslation();
	PfxVector3 directionB = transformB.getUpper3x3().getCol0();
	PfxVector3 translationB = transformB.getTranslation();

	// translation between centers

	PfxVector3 translation = translationB - translationA;

	// compute the closest points of the capsule line segments

	PfxVector3 ptsVector;           // the vector between the closest points
	PfxVector3 offsetA, offsetB;    // offsets from segment centers to their closest points
	PfxFloat tA, tB;              // parameters on line segment

	segmentsClosestPoints( ptsVector, offsetA, offsetB, tA, tB, translation,
						   directionA, capsuleA.m_halfLen, directionB, capsuleB.m_halfLen );

	PfxFloat distance = length(ptsVector) - capsuleA.m_radius - capsuleB.m_radius;

	if ( distance > distanceThreshold )
		return distance;

	// compute the contact normal

	segmentsNormal( normal, ptsVector );

	// compute points on capsules

	pointA = PfxPoint3( transpose(transformA.getUpper3x3()) * ( offsetA + normal * capsuleA.m_radius ) );
	pointB = PfxPoint3( transpose(transformB.getUpper3x3()) * ( offsetB - normal * capsuleB.m_radius ) );

	return distance;
}
PfxFloat pfxContactCapsuleSphere(
	PfxVector3 &normal,PfxPoint3 &pointA,PfxPoint3 &pointB,
	void *shapeA,const PfxTransform3 &transformA,
	void *shapeB,const PfxTransform3 &transformB,
	PfxFloat distanceThreshold)
{
	PfxCapsule &capsuleA = *((PfxCapsule*)shapeA);
	PfxSphere &sphereB = *((PfxSphere*)shapeB);

	PfxVector3 directionA = transformA.getUpper3x3().getCol0();
	PfxVector3 translationA = transformA.getTranslation();
	PfxVector3 translationB = transformB.getTranslation();

	// translation between centers of capsule and sphere

	PfxVector3 translation = translationB - translationA;

	// compute the closest point on the capsule line segment to the sphere center

	PfxVector3 ptsVector;
	PfxVector3 offsetA;
	PfxFloat tA;

	segmentPointClosestPoints( ptsVector, offsetA, tA, translation, directionA, capsuleA.m_halfLen );

	PfxFloat distance = length(ptsVector) - capsuleA.m_radius - sphereB.m_radius;

	if ( distance > distanceThreshold )
		return distance;

	// compute the contact normal

	segmentPointNormal( normal, ptsVector );

	// compute points on capsule and sphere

	pointA = PfxPoint3( transpose(transformA.getUpper3x3()) * ( offsetA + normal * capsuleA.m_radius ) );
	pointB = PfxPoint3( transpose(transformB.getUpper3x3()) * ( -normal * sphereB.m_radius ) );

	return distance;
}
PfxBool pfxIntersectRayBox(const PfxRayInput &ray,PfxRayOutput &out,const PfxBox &box,const PfxTransform3 &transform)
{
	// レイをBoxのローカル座標へ変換
	PfxTransform3 transformBox = orthoInverse(transform);
	PfxVector3 rayStartPosition = transformBox.getUpper3x3() * ray.m_startPosition + transformBox.getTranslation();
	PfxVector3 rayDirection = transformBox.getUpper3x3() * ray.m_direction;
	
	// 交差判定
	PfxFloat tmpVariable=0.0f;
	PfxVector3 tmpNormal(0.0f);
	if(pfxIntersectRayAABB(rayStartPosition,rayDirection,PfxVector3(0.0f),box.m_half,tmpVariable,tmpNormal)) {
		if(tmpVariable > 0.0f && tmpVariable < out.m_variable) {
			out.m_contactFlag = true;
			out.m_variable = tmpVariable;
			out.m_contactPoint = ray.m_startPosition + tmpVariable * ray.m_direction;
			out.m_contactNormal = transform.getUpper3x3() * tmpNormal;
			out.m_subData.m_type = PfxSubData::NONE;
			return true;
		}
	}
	
	return false;
}
static void convertToBullet(void)
{

	cleanupBullet();

	m_config = new btDefaultCollisionConfiguration();
	
	btHashedOverlappingPairCache* pairCache = new btHashedOverlappingPairCache();
	//m_broadphase = new btDbvtBroadphase();
	btLowLevelData* lowLevelData = new btLowLevelData;
	
	lowLevelData->m_maxContacts = NUM_CONTACTS;//8024;
	lowLevelData->m_contactIdPool = (int*) btAlignedAlloc(sizeof(int)*lowLevelData->m_maxContacts ,16);
	lowLevelData->m_contacts = (PfxContactManifold*) btAlignedAlloc(sizeof(PfxContactManifold)*lowLevelData->m_maxContacts,16);
	lowLevelData->m_maxPairs = lowLevelData->m_maxContacts;//??
	lowLevelData->m_pairsBuff[0] = (PfxBroadphasePair*) btAlignedAlloc(sizeof(PfxBroadphasePair)*lowLevelData->m_maxPairs,16);
	lowLevelData->m_pairsBuff[1] = (PfxBroadphasePair*) btAlignedAlloc(sizeof(PfxBroadphasePair)*lowLevelData->m_maxPairs,16);

#define USE_LL_BP
#ifdef USE_LL_BP
	btLowLevelBroadphase* llbp = new btLowLevelBroadphase(lowLevelData,pairCache);
	m_broadphase = llbp;
#else //USE_LL_BP
	m_broadphase = new btDbvtBroadphase();
#endif //USE_LL_BP

	//m_dispatcher = new btCollisionDispatcher(m_config);
	m_dispatcher = new btLowLevelCollisionDispatcher(lowLevelData,m_config,NUM_CONTACTS);
	
//#ifdef USE_PARALLEL_SOLVER
//	m_solver = new btSequentialImpulseConstraintSolver();
//#else
	//sThreadSupport = createSolverThreadSupport(4);
	//m_solver = new btLowLevelConstraintSolver(sThreadSupport);
	m_solver = new btLowLevelConstraintSolver2(lowLevelData);
//#endif //USE_PARALLEL_SOLVER

	//m_dynamicsWorld = new btDiscreteDynamicsWorld(m_dispatcher,m_broadphase,m_solver,m_config);
	m_dynamicsWorld = new btBulletPhysicsEffectsWorld(lowLevelData, m_dispatcher,llbp,m_solver,m_config,0);
	PEDebugDrawer* drawer = new PEDebugDrawer();
	drawer->setDebugMode(btIDebugDraw::DBG_DrawWireframe+btIDebugDraw::DBG_DrawAabb);

	m_dynamicsWorld->setDebugDrawer(drawer);
	
	m_dynamicsWorld ->getSolverInfo().m_splitImpulse = true;
	
	for(int i=0;i<physics_get_num_rigidbodies();i++) {

		btRigidBody* body = 0;
		btAlignedObjectArray<btCollisionShape*> shapes;
		btAlignedObjectArray<btTransform> childTtransforms;

		PfxRigidState &state = states[i];//physics_get_state(i);
		state.setUserData(0);

		
		const PfxCollidable &coll = physics_get_collidable(i);

		PfxTransform3 rbT(state.getOrientation(), state.getPosition());

		PfxShapeIterator itrShape(coll);
		for(int j=0;j<coll.getNumShapes();j++,++itrShape) {
			const PfxShape &shape = *itrShape;
			PfxTransform3 offsetT = shape.getOffsetTransform();
			PfxTransform3 worldT = rbT * offsetT;
			btTransform childTransform;
			childTransform.setIdentity();
			childTransform.setOrigin(getBtVector3(offsetT.getTranslation()));
			PfxQuat quat(offsetT.getUpper3x3());
			childTransform.setBasis(btMatrix3x3(getBtQuat(quat)));
			childTtransforms.push_back(childTransform);

			switch(shape.getType()) {
				case kPfxShapeSphere:
					{
					btSphereShape* sphere = new btSphereShape(shape.getSphere().m_radius);
					shapes.push_back(sphere);
					//render_sphere(	worldT,	Vectormath::Aos::Vector3(1,1,1),Vectormath::floatInVec(shape.getSphere().m_radius));
					}
				break;

				case kPfxShapeBox:
					{
					btBoxShape* box = new btBoxShape(getBtVector3(shape.getBox().m_half));
					shapes.push_back(box);
					}
					//render_box(		worldT,		Vectormath::Aos::Vector3(1,1,1),	shape.getBox().m_half);
				break;

				case kPfxShapeCapsule:
					shapes.push_back(new btCapsuleShapeX(shape.getCapsule().m_radius,2.f*shape.getCapsule().m_halfLen));
					//render_capsule(		worldT,		Vectormath::Aos::Vector3(1,1,1),	Vectormath::floatInVec(shape.getCapsule().m_radius),	Vectormath::floatInVec(shape.getCapsule().m_halfLen));
				break;

				case kPfxShapeCylinder:
					shapes.push_back(new btCylinderShapeX(btVector3(shape.getCylinder().m_halfLen,shape.getCylinder().m_radius,shape.getCylinder().m_radius)));
					//render_cylinder(	worldT,	Vectormath::Aos::Vector3(1,1,1),	Vectormath::floatInVec(shape.getCylinder().m_radius),Vectormath::floatInVec(shape.getCylinder().m_halfLen));
				break;

				case kPfxShapeConvexMesh:
					{
					const PfxConvexMesh* convex = shape.getConvexMesh();
					const btScalar* vertices = (const btScalar*)&convex->m_verts[0];
					btConvexHullShape* convexHull = new btConvexHullShape(vertices,convex->m_numVerts, sizeof(PfxVector3));
					shapes.push_back(convexHull);
					}
				break;

				case kPfxShapeLargeTriMesh:
					{
						const PfxLargeTriMesh* mesh = shape.getLargeTriMesh();
						btTriangleIndexVertexArray* meshInterface = new btTriangleIndexVertexArray();
						int i;
						for (i= 0; i < mesh->m_numIslands;i++)
						{
							PfxTriMesh* island = &mesh->m_islands[i];
							
						//mesh->m_islands
							//mesh->m_numIslands
							btIndexedMesh indexedMesh;
							indexedMesh.m_indexType = PHY_UCHAR;
							indexedMesh.m_numTriangles = island->m_numFacets;
							indexedMesh.m_triangleIndexBase = &island->m_facets[0].m_vertIds[0];
							indexedMesh.m_triangleIndexStride = sizeof(PfxFacet);
							indexedMesh.m_vertexBase = (const unsigned char*) &island->m_verts[0];
							indexedMesh.m_numVertices = island->m_numVerts;//unused
							indexedMesh.m_vertexStride = sizeof(PfxVector3);
							meshInterface->addIndexedMesh(indexedMesh,PHY_UCHAR);
						}
						
						btBvhTriangleMeshShape* trimesh = new btBvhTriangleMeshShape(meshInterface,true);
						shapes.push_back(trimesh);
					}
					
				break;

				default:
					{
						printf("unknown\n");
					}
				break;
			}
		}
	
		if(shapes.size()>0)
		{
			printf("shapes!\n");
			btCollisionShape* colShape = 0;
			if (shapes.size()==1 && childTtransforms[0].getOrigin().fuzzyZero())
//todo: also check orientation
			{
				colShape = shapes[0];
			}
			else
			{
				btCompoundShape* compound = new btCompoundShape();
				for (int i=0;i<shapes.size();i++)
				{
					compound->addChildShape(childTtransforms[i],shapes[i]);
				}
				colShape = compound;

			}
			
			btTransform worldTransform;
			worldTransform.setIdentity();
			worldTransform.setOrigin(getBtVector3(rbT.getTranslation()));
			PfxQuat quat(rbT.getUpper3x3());
			worldTransform.setBasis(btMatrix3x3(getBtQuat(quat)));
			
			btScalar mass = physics_get_body(i).getMass();
			btRigidBody* body = addRigidBody(colShape,mass,worldTransform);
			void* ptr = (void*)&state;
			body->setUserPointer(ptr);
			state.setUserData(body);
		}
		
	}

	//very basic joint conversion (only limits of PFX_JOINT_SWINGTWIST joint)
	for (int i=0;i<numJoints;i++)
	{
		PfxJoint& joint = joints[i];
		switch (joint.m_type)
		{
		case kPfxJointSwingtwist:
			{
				//btConeTwistConstraint* coneTwist = new btConeTwistConstraint(rbA,rbB,frameA,frameB);
				bool referenceA = true;
				btRigidBody* bodyA = (btRigidBody*)states[joint.m_rigidBodyIdA].getUserData();
				btRigidBody* bodyB = (btRigidBody*)states[joint.m_rigidBodyIdB].getUserData();
				if (bodyA&&bodyB)
				{
					btTransform frameA,frameB;
					frameA.setIdentity();
					frameB.setIdentity();
					frameA.setOrigin(getBtVector3(joint.m_anchorA));
					frameB.setOrigin(getBtVector3(joint.m_anchorB));
					bool referenceA = false;
					btGeneric6DofConstraint* dof6 = new btGeneric6DofConstraint(*bodyA,*bodyB,frameA,frameB,referenceA);
					
					for (int i=0;i<joint.m_numConstraints;i++)
					{
						switch (joint.m_constraints[i].m_lock)
						{
						case SCE_PFX_JOINT_LOCK_FIX:
							{
								dof6->setLimit(i,0,0);
								break;
							}
						case SCE_PFX_JOINT_LOCK_FREE:
							{
								dof6->setLimit(i,1,0);
								break;
							}
						case SCE_PFX_JOINT_LOCK_LIMIT:
							{
								//deal with the case where angular limits of Y-axis are free and/or beyond -PI/2 and PI/2
								if ((i==4) && ((joint.m_constraints[i].m_movableLowerLimit<-SIMD_PI/2)||(joint.m_constraints[i].m_movableUpperLimit>SIMD_PI/2)))
								{
									printf("error with joint limits, forcing DOF to fixed\n");
									dof6->setLimit(i,0,0);
								} else
								{
									dof6->setLimit(i,joint.m_constraints[i].m_movableLowerLimit,joint.m_constraints[i].m_movableUpperLimit);
								}
								
								break;
							}
						default:
							{
								printf("unknown joint lock state\n");
							}
						}

					}
					
					m_dynamicsWorld->addConstraint(dof6,true);
					
				} else
				{
					printf("error: missing bodies during joint conversion\n");
				}
				break;
			};
		case kPfxJointBall:
		case kPfxJointHinge:
		case kPfxJointSlider:
		case kPfxJointFix:
		case kPfxJointUniversal:
		case kPfxJointAnimation:
		default:
			{
				printf("unknown joint\n");
			}
		}
	}

	//create a large enough buffer. There is no method to pre-calculate the buffer size yet.
	int maxSerializeBufferSize = 1024*1024*5;
 
	btDefaultSerializer*	serializer = new btDefaultSerializer(maxSerializeBufferSize);
	m_dynamicsWorld->serialize(serializer);
 
	FILE* file = fopen("testFile.bullet","wb");
	fwrite(serializer->getBufferPointer(),serializer->getCurrentBufferSize(),1, file);
	fclose(file);

}
Beispiel #5
0
void graphics_from_physics(GLInstancingRenderer& renderer, bool syncTransformsOnly)
{

	int cubeShapeIndex  = -1;
	int strideInBytes = sizeof(float)*9;

	if (!syncTransformsOnly)
	{
		int numVertices = sizeof(cube_vertices)/strideInBytes;
		int numIndices = sizeof(cube_indices)/sizeof(int);
		cubeShapeIndex = renderer.registerShape(&cube_vertices[0],numVertices,cube_indices,numIndices);
	}


	int curGraphicsIndex=0;

	for(int i=0;i<physics_get_num_rigidbodies();i++) 
	{
		const PfxRigidState &state = physics_get_state(i);
		const PfxCollidable &coll = physics_get_collidable(i);
		const PfxRigidBody& body = physics_get_body(i);
	

		float color[4]={0,0,0,1};

		if (body.getMass()==0.f)
		{
			color[0]=1.f;
		} else
		{
			color[1]=1.f;
		}


		PfxTransform3 rbT(state.getOrientation(), state.getPosition());

		PfxShapeIterator itrShape(coll);
		for(int j=0;j<coll.getNumShapes();j++,++itrShape) {
			const PfxShape &shape = *itrShape;
			
	
			PfxTransform3 offsetT = shape.getOffsetTransform();
			PfxTransform3 worldT = rbT * offsetT;

			    PfxQuat ornWorld( worldT.getUpper3x3());

			switch(shape.getType()) {
				case kPfxShapeSphere:
					printf("render sphere\n");
				/*render_sphere(
					worldT,
					PfxVector3(1,1,1),
					PfxFloatInVec(shape.getSphere().m_radius));
					*/
				break;

				case kPfxShapeBox:
					{
				//	printf("render box\n");
					float cubeScaling[4] = {shape.getBox().m_half.getX(),shape.getBox().m_half.getY(),shape.getBox().m_half.getZ(),1};
					
					float rotOrn[4] = {ornWorld.getX(),ornWorld.getY(),ornWorld.getZ(),ornWorld.getW()};
					float position[4]={worldT.getTranslation().getX(),worldT.getTranslation().getY(),worldT.getTranslation().getZ(),0};
					if (!syncTransformsOnly)
					{
						renderer.registerGraphicsInstance(cubeShapeIndex,position,rotOrn,color,cubeScaling);
					}
					else
					{
						renderer.writeSingleInstanceTransformToCPU(position,rotOrn,curGraphicsIndex);
					}
					curGraphicsIndex++;
/*				render_box(
					worldT,
					PfxVector3(1,1,1),
					shape.getBox().m_half);
	*/
					break;
					}
				case kPfxShapeCapsule:

					printf("render_capsule\n");

					/*render_capsule(
					worldT,
					PfxVector3(1,1,1),
					PfxFloatInVec(shape.getCapsule().m_radius),
					PfxFloatInVec(shape.getCapsule().m_halfLen));
					*/
				break;

				case kPfxShapeCylinder:
					printf("render_cylinder\n");

/*				render_cylinder(
					worldT,
					PfxVector3(1,1,1),
					PfxFloatInVec(shape.getCylinder().m_radius),
					PfxFloatInVec(shape.getCylinder().m_halfLen));
					*/
				break;

				case kPfxShapeConvexMesh:
						printf("render_mesh\n");

					/*
				render_mesh(
					worldT,
					PfxVector3(1,1,1),
					convexMeshId);
					*/
				break;

				case kPfxShapeLargeTriMesh:
					printf("render_mesh\n");

					/*
				render_mesh(
					worldT,
					PfxVector3(1,1,1),
					landscapeMeshId);
					*/
				break;

				default:
				break;
			}
		}
	}


}
PfxInt32 pfxContactLargeTriMesh(
				PfxContactCache &contacts,
				const PfxLargeTriMesh *lmeshA,
				const PfxTransform3 &transformA,
				const PfxShape &shapeB,
				const PfxTransform3 &transformB,
				PfxFloat distanceThreshold)
{
	PfxTransform3 transformAB;
	PfxMatrix3 matrixAB;
	PfxVector3 offsetAB;
	
	// Bローカル→Aローカルへの変換
	transformAB = orthoInverse(transformA) * transformB;
	matrixAB = transformAB.getUpper3x3();
	offsetAB = transformAB.getTranslation();
	
	// -----------------------------------------------------
	// LargeTriMeshに含まれるTriMeshのAABBと凸体のAABBを判定し、
	// 交差するものを個別に衝突判定する。※LargeMesh座標系
	
	PfxVector3 shapeHalf(0.0f);
	PfxVector3 shapeCenter = offsetAB;
	

	switch(shapeB.getType()) {
		case kPfxShapeSphere:
		shapeHalf = PfxVector3(shapeB.getSphere().m_radius);
		break;
		
		case kPfxShapeCapsule:
		{
			PfxCapsule capsule = shapeB.getCapsule();
			shapeHalf = absPerElem(matrixAB) * PfxVector3(capsule.m_halfLen+capsule.m_radius,capsule.m_radius,capsule.m_radius);
		}
		break;
		
		case kPfxShapeCylinder:
		{
			PfxCylinder cylinder = shapeB.getCylinder();
			shapeHalf = absPerElem(matrixAB) * PfxVector3(cylinder.m_halfLen,cylinder.m_radius,cylinder.m_radius);
		}
		break;
		
		case kPfxShapeBox:
		shapeHalf = absPerElem(matrixAB) * shapeB.getBox().m_half;
		break;
		
		case kPfxShapeConvexMesh:
	shapeHalf = absPerElem(matrixAB) * shapeB.getConvexMesh()->m_half;
		break;
		
		default:
		break;
	}

	// -----------------------------------------------------
	// アイランドとの衝突判定

	PfxVecInt3 aabbMinL,aabbMaxL;
	lmeshA->getLocalPosition((shapeCenter-shapeHalf),(shapeCenter+shapeHalf),aabbMinL,aabbMaxL);
	
	PfxUInt32 numIslands = lmeshA->m_numIslands;

	{
	for(PfxUInt32 i=0;i<numIslands;i++) {
		// AABBチェック
		PfxAabb16 aabbB = lmeshA->m_aabbList[i];
		if(aabbMaxL.getX() < pfxGetXMin(aabbB) || aabbMinL.getX() > pfxGetXMax(aabbB)) continue;
		if(aabbMaxL.getY() < pfxGetYMin(aabbB) || aabbMinL.getY() > pfxGetYMax(aabbB)) continue;
		if(aabbMaxL.getZ() < pfxGetZMin(aabbB) || aabbMinL.getZ() > pfxGetZMax(aabbB)) continue;
		
		PfxTriMesh *island = &lmeshA->m_islands[i];

			// 衝突判定
			PfxContactCache localContacts;
			switch(shapeB.getType()) {
				case kPfxShapeSphere:
				pfxContactTriMeshSphere(localContacts,island,transformA,shapeB.getSphere(),transformB,distanceThreshold);
				break;
				
				case kPfxShapeCapsule:
				pfxContactTriMeshCapsule(localContacts,island,transformA,shapeB.getCapsule(),transformB,distanceThreshold);
				break;
				
				case kPfxShapeBox:
				pfxContactTriMeshBox(localContacts,island,transformA,shapeB.getBox(),transformB,distanceThreshold);
				break;
				
				case kPfxShapeCylinder:
				pfxContactTriMeshCylinder(localContacts,island,transformA,shapeB.getCylinder(),transformB,distanceThreshold);
				break;
				
				case kPfxShapeConvexMesh:
			pfxContactTriMeshConvex(localContacts,island,transformA,*shapeB.getConvexMesh(),transformB,distanceThreshold);
				break;
				
				default:
				break;
			}

			
			// 衝突点を追加
			for(int j=0;j<localContacts.getNumContacts();j++) {
				PfxSubData subData = localContacts.getSubData(j);
				subData.setIslandId(i);
				contacts.addContactPoint(
					localContacts.getDistance(j),
					localContacts.getNormal(j),
					localContacts.getLocalPointA(j),
					localContacts.getLocalPointB(j),
					subData);
			}
		}
	}


	return contacts.getNumContacts();
}
PfxBool pfxIntersectRayCapsule(const PfxRayInput &ray,PfxRayOutput &out,const PfxCapsule &capsule,const PfxTransform3 &transform)
{
	// レイをCapsuleのローカル座標へ変換
	PfxTransform3 transformCapsule = orthoInverse(transform);
	PfxVector3 startPosL = transformCapsule.getUpper3x3() * ray.m_startPosition + transformCapsule.getTranslation();
	PfxVector3 rayDirL = transformCapsule.getUpper3x3() * ray.m_direction;
	
	PfxFloat radSqr = capsule.m_radius * capsule.m_radius;

	// 始点がカプセルの内側にあるか判定
	{
		PfxFloat h = fabsf(startPosL[0]);
		if(h > capsule.m_halfLen) h = capsule.m_halfLen;
		PfxVector3 Px(out.m_variable,0,0);
		PfxFloat sqrLen = lengthSqr(startPosL-Px);
		if(sqrLen <= radSqr) return false;
	}

	// カプセルの胴体との交差判定
	do {
		PfxVector3 P(startPosL);
		PfxVector3 D(rayDirL);
		
		P[0] = 0.0f;
		D[0] = 0.0f;
		
		PfxFloat a = dot(D,D);
		PfxFloat b = dot(P,D);
		PfxFloat c = dot(P,P) - radSqr;
		
		PfxFloat d = b * b - a * c;
		
		if(d < 0.0f || fabs(a) < 0.00001f) return false;
		
		PfxFloat tt = ( -b - sqrtf(d) ) / a;
		
		if(tt < 0.0f)
			break;
		else if(tt > 1.0f)
			return false;
		
		if(tt < out.m_variable) {
			PfxVector3 cp = startPosL + tt * rayDirL;
			
			if(fabsf(cp[0]) <= capsule.m_halfLen) {
				out.m_contactFlag = true;
				out.m_variable = tt;
				out.m_contactPoint = PfxVector3(transform * PfxPoint3(cp));
				out.m_contactNormal = transform.getUpper3x3() * normalize(cp);
				out.m_subData.m_type = PfxSubData::NONE;
				return true;
			}
		}
	} while(0);
	
	// カプセルの両端にある球体との交差判定
	PfxFloat a = dot(rayDirL,rayDirL);
	if(fabs(a) < 0.00001f) return false;
	
	do {
		PfxVector3 center(capsule.m_halfLen,0.0f,0.0f);
		PfxVector3 v = startPosL - center;

		PfxFloat b = dot(v,rayDirL);
		PfxFloat c = dot(v,v) - radSqr;

		PfxFloat d = b * b - a * c;
		
		if(d < 0.0f) break;
		
		PfxFloat tt = ( -b - sqrtf(d) ) / a;
		
		if(tt < 0.0f || tt > 1.0f) break;
		
		if(tt < out.m_variable) {
			PfxVector3 cp = startPosL + tt * rayDirL;
			out.m_contactFlag = true;
			out.m_variable = tt;
			out.m_contactPoint = ray.m_startPosition + tt * ray.m_direction;
			out.m_contactNormal = transform.getUpper3x3() * normalize(cp-center);
			out.m_subData.m_type = PfxSubData::NONE;
			return true;
		}
	} while(0);
	
	{
		PfxVector3 center(-capsule.m_halfLen,0.0f,0.0f);
		PfxVector3 v = startPosL - center;

		PfxFloat b = dot(v,rayDirL);
		PfxFloat c = dot(v,v) - radSqr;

		PfxFloat d = b * b - a * c;
		
		if(d < 0.0f) return false;
		
		PfxFloat tt = ( -b - sqrtf(d) ) / a;
		
		if(tt < 0.0f || tt > 1.0f) return false;
		
		if(tt < out.m_variable) {
			PfxVector3 cp = startPosL + out.m_variable * rayDirL;
			out.m_contactFlag = true;
			out.m_variable = tt;
			out.m_contactPoint = ray.m_startPosition + tt * ray.m_direction;
			out.m_contactNormal = transform.getUpper3x3() * normalize(cp-center);
			out.m_subData.m_type = PfxSubData::NONE;
			return true;
		}
	}
	
	return false;
}
PfxBool pfxIntersectRayCylinder(const PfxRayInput &ray,PfxRayOutput &out,const PfxCylinder &cylinder,const PfxTransform3 &transform)
{
	// レイを円柱のローカル座標へ変換
	PfxTransform3 transformCapsule = orthoInverse(transform);
	PfxVector3 startPosL = transformCapsule.getUpper3x3() * ray.m_startPosition + transformCapsule.getTranslation();
	PfxVector3 rayDirL = transformCapsule.getUpper3x3() * ray.m_direction;
	
	PfxFloat radSqr = cylinder.m_radius * cylinder.m_radius;

	// 始点が円柱の内側にあるか判定
	{
		PfxFloat h = startPosL[0];
		if(-cylinder.m_halfLen <= h && h <= cylinder.m_halfLen) {
			PfxVector3 Px(h,0,0);
			PfxFloat sqrLen = lengthSqr(startPosL-Px);
			if(sqrLen <= radSqr) return false;
		}
	}

	// 円柱の胴体との交差判定
	do {
		PfxVector3 P(startPosL);
		PfxVector3 D(rayDirL);
		
		P[0] = 0.0f;
		D[0] = 0.0f;
		
		PfxFloat a = dot(D,D);
		PfxFloat b = dot(P,D);
		PfxFloat c = dot(P,P) - radSqr;
		
		PfxFloat d = b * b - a * c;
		
		if(d < 0.0f) return false; // レイは逸れている
		if(pfxAbsf(a) < 0.00001f) break; // レイがX軸に平行
		
		PfxFloat tt = ( -b - sqrtf(d) ) / a;
		
		if(tt < 0.0f || tt > 1.0f) break;
		
		if(tt < out.m_variable) {
			PfxVector3 cp = startPosL + tt * rayDirL;
			
			if(pfxAbsf(cp[0]) <= cylinder.m_halfLen) {
				out.m_contactFlag = true;
				out.m_variable = tt;
				out.m_contactPoint = PfxVector3(transform * PfxPoint3(cp));
				out.m_contactNormal = transform.getUpper3x3() * normalize(cp);
				out.m_subData.m_type = PfxSubData::NONE;
				return true;
			}
		}
	} while(0);
	
	// 円柱の両端にある平面との交差判定
	{
		if(pfxAbsf(rayDirL[0]) < 0.00001f) return false;
		
		PfxFloat t1 = ( cylinder.m_halfLen - startPosL[0] ) / rayDirL[0];
		PfxFloat t2 = ( - cylinder.m_halfLen - startPosL[0] ) / rayDirL[0];

		PfxFloat tt = SCE_PFX_MIN(t1,t2);
		
		if(tt < 0.0f || tt > 1.0f) return false;

		PfxVector3 p = startPosL + tt * rayDirL;
		p[0] = 0.0f;

		if(lengthSqr(p) < radSqr && tt < out.m_variable) {
			PfxVector3 cp = startPosL + tt * rayDirL;
			out.m_contactFlag = true;
			out.m_variable = tt;
			out.m_contactPoint = ray.m_startPosition + tt * ray.m_direction;
			out.m_contactNormal = transform.getUpper3x3() * ((cp[0]>0.0f)?PfxVector3(1.0,0.0,0.0):PfxVector3(-1.0,0.0,0.0));
			out.m_subData.m_type = PfxSubData::NONE;
			return true;
		}
	}
	
	return false;
}
PfxFloat pfxContactBoxCapsule(
	PfxVector3 &normal,PfxPoint3 &pointA,PfxPoint3 &pointB,
	void *shapeA,const PfxTransform3 &transformA,
	void *shapeB,const PfxTransform3 &transformB,
	PfxFloat distanceThreshold)
{
	PfxBox boxA = *((PfxBox*)shapeA);
	PfxCapsule capsuleB = *((PfxCapsule*)shapeB);

	PfxVector3 ident[3] = {
		PfxVector3(1.0,0.0,0.0),
		PfxVector3(0.0,1.0,0.0),
		PfxVector3(0.0,0.0,1.0),
	};

	// get capsule position and direction in box's coordinate system

	PfxMatrix3 matrixA = transformA.getUpper3x3();
	PfxMatrix3 matrixAinv = transpose(matrixA);

	PfxVector3 directionB = transformB.getUpper3x3().getCol0();
	PfxVector3 translationB = transformB.getTranslation();

	PfxVector3 capsDirection = matrixAinv * directionB;
	PfxVector3 absCapsDirection = absPerElem(capsDirection);
	PfxVector3 offsetAB = matrixAinv * (translationB - transformA.getTranslation());

	// find separating axis with largest gap between projections

	BoxCapsSepAxisType axisType;
	PfxVector3 axisA;
	PfxFloat maxGap;
	int faceDimA = 0, edgeDimA = 0;

	// face axes

	// can compute all the gaps at once with VU0

	PfxVector3 gapsA = absPerElem(offsetAB) - boxA.m_half - absCapsDirection * capsuleB.m_halfLen;

	AaxisTest( 0, X, true );
	AaxisTest( 1, Y, false );
	AaxisTest( 2, Z, false );

	// cross product axes

	// compute gaps on all cross product axes using some VU0 math.  suppose there's a tradeoff
	// between doing this with SIMD all at once or without SIMD in each cross product test, since
	// some test might exit early.

	PfxVector3 lsqrs, projOffset, projAhalf;

	PfxMatrix3 crossProdMat = crossMatrix(capsDirection) * PfxMatrix3::identity();
	PfxMatrix3 crossProdMatT = crossMatrix(-capsDirection) * PfxMatrix3::identity();

	lsqrs = mulPerElem( crossProdMatT.getCol0(), crossProdMatT.getCol0() ) +
			mulPerElem( crossProdMatT.getCol1(), crossProdMatT.getCol1() ) +
			mulPerElem( crossProdMatT.getCol2(), crossProdMatT.getCol2() );

	projOffset = crossProdMatT * offsetAB;
	projAhalf = absPerElem(crossProdMatT) * boxA.m_half;

	PfxVector3 gapsAxB = absPerElem(projOffset) - projAhalf;

	CrossAxisTest( 0, X );
	CrossAxisTest( 1, Y );
	CrossAxisTest( 2, Z );

	// make axis point from box center towards capsule center.

	if ( dot(axisA,offsetAB) < 0.0f )
		axisA = -axisA;

	// find the face on box whose normal best matches the separating axis. will use the entire
	// face only in degenerate cases.
	//
	// to make things simpler later, change the coordinate system so that the face normal is the z
	// direction.  if an edge cross product axis was chosen above, also align the box edge to the y
	// axis.  this saves the later tests from having to know which face was chosen.  changing the
	// coordinate system involves permuting vector elements, so construct a permutation matrix.
	// I believe this is a faster way to permute a bunch of vectors than using arrays.

	int dimA[3];

	if ( axisType == CROSS_AXIS ) {
		PfxVector3 absAxisA = PfxVector3(absPerElem(axisA));

		dimA[1] = edgeDimA;

		if ( edgeDimA == 0 ) {
			if ( absAxisA[1] > absAxisA[2] ) {
				dimA[0] = 2;
				dimA[2] = 1;
			} else                             {
				dimA[0] = 1;
				dimA[2] = 2;
			}
		} else if ( edgeDimA == 1 ) {
			if ( absAxisA[2] > absAxisA[0] ) {
				dimA[0] = 0;
				dimA[2] = 2;
			} else                             {
				dimA[0] = 2;
				dimA[2] = 0;
			}
		} else {
			if ( absAxisA[0] > absAxisA[1] ) {
				dimA[0] = 1;
				dimA[2] = 0;
			} else                             {
				dimA[0] = 0;
				dimA[2] = 1;
			}
		}
	} else {
		dimA[2] = faceDimA;
		dimA[0] = (faceDimA+1)%3;
		dimA[1] = (faceDimA+2)%3;
	}

	PfxMatrix3 aperm_col;

	aperm_col.setCol0(ident[dimA[0]]);
	aperm_col.setCol1(ident[dimA[1]]);
	aperm_col.setCol2(ident[dimA[2]]);

	PfxMatrix3 aperm_row = transpose(aperm_col);

	// permute vectors to be in face coordinate system.

	PfxVector3 offsetAB_perm = aperm_row * offsetAB;
	PfxVector3 halfA_perm = aperm_row * boxA.m_half;
	PfxVector3 signsA_perm = copySignPerElem(PfxVector3(1.0f), aperm_row * axisA);
	PfxVector3 scalesA_perm = mulPerElem( signsA_perm, halfA_perm );
	PfxVector3 capsDirection_perm = aperm_row * capsDirection;
	PfxFloat signB = (-dot(capsDirection,axisA) > 0.0f)? 1.0f : -1.0f;
	PfxFloat scaleB = signB * capsuleB.m_halfLen;

	// compute the vector between the center of the box face and the capsule center

	offsetAB_perm.setZ( offsetAB_perm.getZ() - scalesA_perm.getZ() );

	// if box and capsule overlap, this will separate them for finding points of penetration.

	if ( maxGap < 0.0f ) {
		offsetAB_perm -= aperm_row * axisA * maxGap * 1.01f;
	}

	// for each vertex/face or edge/edge pair of box face and line segment, find the closest
	// points.
	//
	// these points each have an associated feature (vertex, edge, or face).  if each
	// point is in the external Voronoi region of the other's feature, they are the
	// closest points of the objects, and the algorithm can exit.
	//
	// the feature pairs are arranged so that in the general case, the first test will
	// succeed.  degenerate cases (line segment parallel to face) may require up to all tests
	// in the worst case.
	//
	// if for some reason no case passes the Voronoi test, the features with the minimum
	// distance are returned.

	PfxVector3 closestPtsVec_perm;
	PfxPoint3 localPointA_perm;
	PfxFloat minDistSqr;
	PfxFloat segmentParamB;
	PfxBool done;

	localPointA_perm.setZ( scalesA_perm.getZ() );
	scalesA_perm.setZ(0.0f);

	PfxVector3 hA_perm( halfA_perm );

	int otherFaceDimA;

	if ( axisType == CROSS_AXIS ) {
		EdgeEdgeTests( done, minDistSqr, closestPtsVec_perm, localPointA_perm, segmentParamB,
					   otherFaceDimA,
					   hA_perm, capsuleB.m_halfLen, offsetAB_perm, capsDirection_perm, signsA_perm,
					   scalesA_perm, true );

		if ( !done ) {
			VertexBFaceATests( done, minDistSqr, closestPtsVec_perm, localPointA_perm, segmentParamB,
							   hA_perm, offsetAB_perm, capsDirection_perm, signB, scaleB, false );
		}
	} else {
		VertexBFaceATests( done, minDistSqr, closestPtsVec_perm, localPointA_perm, segmentParamB,
						   hA_perm, offsetAB_perm, capsDirection_perm, signB, scaleB, true );

		if ( !done ) {
			EdgeEdgeTests( done, minDistSqr, closestPtsVec_perm, localPointA_perm, segmentParamB,
						   otherFaceDimA,
						   hA_perm, capsuleB.m_halfLen, offsetAB_perm, capsDirection_perm, signsA_perm,
						   scalesA_perm, false );
		}
	}

	// compute normal

	PfxBool centerInside = ( signsA_perm.getZ() * closestPtsVec_perm.getZ() < 0.0f );

	if ( centerInside || ( minDistSqr < lenSqrTol ) ) {
		normal = matrixA * axisA;
	} else {
		PfxVector3 closestPtsVec = aperm_col * closestPtsVec_perm;
		normal = matrixA * ( closestPtsVec * (1.0f/sqrtf( minDistSqr )) );
	}

	// compute box point

	pointA = PfxPoint3( aperm_col * PfxVector3( localPointA_perm ) );

	// compute capsule point

	pointB = PfxPoint3( transpose(transformB.getUpper3x3()) * ( directionB * segmentParamB - normal * capsuleB.m_radius ) );

	if ( centerInside ) {
		return (-sqrtf( minDistSqr ) - capsuleB.m_radius);
	} else {
		return (sqrtf( minDistSqr ) - capsuleB.m_radius);
	}
}