839 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
		
		
			
		
	
	
			839 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
|  | #include "btInternalEdgeUtility.h"
 | ||
|  | 
 | ||
|  | #include "BulletCollision/CollisionShapes/btBvhTriangleMeshShape.h"
 | ||
|  | #include "BulletCollision/CollisionShapes/btScaledBvhTriangleMeshShape.h"
 | ||
|  | #include "BulletCollision/CollisionShapes/btTriangleShape.h"
 | ||
|  | #include "BulletCollision/CollisionDispatch/btCollisionObject.h"
 | ||
|  | #include "BulletCollision/NarrowPhaseCollision/btManifoldPoint.h"
 | ||
|  | #include "LinearMath/btIDebugDraw.h"
 | ||
|  | #include "BulletCollision/CollisionDispatch/btCollisionObjectWrapper.h"
 | ||
|  | 
 | ||
|  | //#define DEBUG_INTERNAL_EDGE
 | ||
|  | 
 | ||
|  | #ifdef DEBUG_INTERNAL_EDGE
 | ||
|  | #include <stdio.h>
 | ||
|  | #endif //DEBUG_INTERNAL_EDGE
 | ||
|  | 
 | ||
|  | 
 | ||
|  | #ifdef BT_INTERNAL_EDGE_DEBUG_DRAW
 | ||
|  | static btIDebugDraw* gDebugDrawer = 0; | ||
|  | 
 | ||
|  | void	btSetDebugDrawer(btIDebugDraw* debugDrawer) | ||
|  | { | ||
|  | 	gDebugDrawer = debugDrawer; | ||
|  | } | ||
|  | 
 | ||
|  | static void    btDebugDrawLine(const btVector3& from,const btVector3& to, const btVector3& color) | ||
|  | { | ||
|  | 	if (gDebugDrawer) | ||
|  | 		gDebugDrawer->drawLine(from,to,color); | ||
|  | } | ||
|  | #endif //BT_INTERNAL_EDGE_DEBUG_DRAW
 | ||
|  | 
 | ||
|  | 
 | ||
|  | static int	btGetHash(int partId, int triangleIndex) | ||
|  | { | ||
|  | 	int hash = (partId<<(31-MAX_NUM_PARTS_IN_BITS)) | triangleIndex; | ||
|  | 	return hash; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | static btScalar btGetAngle(const btVector3& edgeA, const btVector3& normalA,const btVector3& normalB) | ||
|  | { | ||
|  | 	const btVector3 refAxis0  = edgeA; | ||
|  | 	const btVector3 refAxis1  = normalA; | ||
|  | 	const btVector3 swingAxis = normalB; | ||
|  | 	btScalar angle = btAtan2(swingAxis.dot(refAxis0), swingAxis.dot(refAxis1)); | ||
|  | 	return  angle; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | struct btConnectivityProcessor : public btTriangleCallback | ||
|  | { | ||
|  | 	int				m_partIdA; | ||
|  | 	int				m_triangleIndexA; | ||
|  | 	btVector3*		m_triangleVerticesA; | ||
|  | 	btTriangleInfoMap*	m_triangleInfoMap; | ||
|  | 
 | ||
|  | 
 | ||
|  | 	virtual void processTriangle(btVector3* triangle, int partId, int triangleIndex) | ||
|  | 	{ | ||
|  | 		//skip self-collisions
 | ||
|  | 		if ((m_partIdA == partId) && (m_triangleIndexA == triangleIndex)) | ||
|  | 			return; | ||
|  | 
 | ||
|  | 		//skip duplicates (disabled for now)
 | ||
|  | 		//if ((m_partIdA <= partId) && (m_triangleIndexA <= triangleIndex))
 | ||
|  | 		//	return;
 | ||
|  | 
 | ||
|  | 		//search for shared vertices and edges
 | ||
|  | 		int numshared = 0; | ||
|  | 		int sharedVertsA[3]={-1,-1,-1}; | ||
|  | 		int sharedVertsB[3]={-1,-1,-1}; | ||
|  | 
 | ||
|  | 		///skip degenerate triangles
 | ||
|  | 		btScalar crossBSqr = ((triangle[1]-triangle[0]).cross(triangle[2]-triangle[0])).length2(); | ||
|  | 		if (crossBSqr < m_triangleInfoMap->m_equalVertexThreshold) | ||
|  | 			return; | ||
|  | 
 | ||
|  | 
 | ||
|  | 		btScalar crossASqr = ((m_triangleVerticesA[1]-m_triangleVerticesA[0]).cross(m_triangleVerticesA[2]-m_triangleVerticesA[0])).length2(); | ||
|  | 		///skip degenerate triangles
 | ||
|  | 		if (crossASqr< m_triangleInfoMap->m_equalVertexThreshold) | ||
|  | 			return; | ||
|  | 
 | ||
|  | #if 0
 | ||
|  | 		printf("triangle A[0]	=	(%f,%f,%f)\ntriangle A[1]	=	(%f,%f,%f)\ntriangle A[2]	=	(%f,%f,%f)\n", | ||
|  | 			m_triangleVerticesA[0].getX(),m_triangleVerticesA[0].getY(),m_triangleVerticesA[0].getZ(), | ||
|  | 			m_triangleVerticesA[1].getX(),m_triangleVerticesA[1].getY(),m_triangleVerticesA[1].getZ(), | ||
|  | 			m_triangleVerticesA[2].getX(),m_triangleVerticesA[2].getY(),m_triangleVerticesA[2].getZ()); | ||
|  | 
 | ||
|  | 		printf("partId=%d, triangleIndex=%d\n",partId,triangleIndex); | ||
|  | 		printf("triangle B[0]	=	(%f,%f,%f)\ntriangle B[1]	=	(%f,%f,%f)\ntriangle B[2]	=	(%f,%f,%f)\n", | ||
|  | 			triangle[0].getX(),triangle[0].getY(),triangle[0].getZ(), | ||
|  | 			triangle[1].getX(),triangle[1].getY(),triangle[1].getZ(), | ||
|  | 			triangle[2].getX(),triangle[2].getY(),triangle[2].getZ()); | ||
|  | #endif
 | ||
|  | 
 | ||
|  | 		for (int i=0;i<3;i++) | ||
|  | 		{ | ||
|  | 			for (int j=0;j<3;j++) | ||
|  | 			{ | ||
|  | 				if ( (m_triangleVerticesA[i]-triangle[j]).length2() < m_triangleInfoMap->m_equalVertexThreshold) | ||
|  | 				{ | ||
|  | 					sharedVertsA[numshared] = i; | ||
|  | 					sharedVertsB[numshared] = j; | ||
|  | 					numshared++; | ||
|  | 					///degenerate case
 | ||
|  | 					if(numshared >= 3) | ||
|  | 						return; | ||
|  | 				} | ||
|  | 			} | ||
|  | 			///degenerate case
 | ||
|  | 			if(numshared >= 3) | ||
|  | 				return; | ||
|  | 		} | ||
|  | 		switch (numshared) | ||
|  | 		{ | ||
|  | 		case 0: | ||
|  | 			{ | ||
|  | 				break; | ||
|  | 			} | ||
|  | 		case 1: | ||
|  | 			{ | ||
|  | 				//shared vertex
 | ||
|  | 				break; | ||
|  | 			} | ||
|  | 		case 2: | ||
|  | 			{ | ||
|  | 				//shared edge
 | ||
|  | 				//we need to make sure the edge is in the order V2V0 and not V0V2 so that the signs are correct
 | ||
|  | 				if (sharedVertsA[0] == 0 && sharedVertsA[1] == 2) | ||
|  | 				{ | ||
|  | 					sharedVertsA[0] = 2; | ||
|  | 					sharedVertsA[1] = 0; | ||
|  | 					int tmp = sharedVertsB[1]; | ||
|  | 					sharedVertsB[1] = sharedVertsB[0]; | ||
|  | 					sharedVertsB[0] = tmp; | ||
|  | 				} | ||
|  | 
 | ||
|  | 				int hash = btGetHash(m_partIdA,m_triangleIndexA); | ||
|  | 
 | ||
|  | 				btTriangleInfo* info = m_triangleInfoMap->find(hash); | ||
|  | 				if (!info) | ||
|  | 				{ | ||
|  | 					btTriangleInfo tmp; | ||
|  | 					m_triangleInfoMap->insert(hash,tmp); | ||
|  | 					info = m_triangleInfoMap->find(hash); | ||
|  | 				} | ||
|  | 
 | ||
|  | 				int sumvertsA = sharedVertsA[0]+sharedVertsA[1]; | ||
|  | 				int otherIndexA = 3-sumvertsA; | ||
|  | 
 | ||
|  | 				 | ||
|  | 				btVector3 edge(m_triangleVerticesA[sharedVertsA[1]]-m_triangleVerticesA[sharedVertsA[0]]); | ||
|  | 
 | ||
|  | 				btTriangleShape tA(m_triangleVerticesA[0],m_triangleVerticesA[1],m_triangleVerticesA[2]); | ||
|  | 				int otherIndexB = 3-(sharedVertsB[0]+sharedVertsB[1]); | ||
|  | 
 | ||
|  | 				btTriangleShape tB(triangle[sharedVertsB[1]],triangle[sharedVertsB[0]],triangle[otherIndexB]); | ||
|  | 				//btTriangleShape tB(triangle[0],triangle[1],triangle[2]);
 | ||
|  | 
 | ||
|  | 				btVector3 normalA; | ||
|  | 				btVector3 normalB; | ||
|  | 				tA.calcNormal(normalA); | ||
|  | 				tB.calcNormal(normalB); | ||
|  | 				edge.normalize(); | ||
|  | 				btVector3 edgeCrossA = edge.cross(normalA).normalize(); | ||
|  | 
 | ||
|  | 				{ | ||
|  | 					btVector3 tmp = m_triangleVerticesA[otherIndexA]-m_triangleVerticesA[sharedVertsA[0]]; | ||
|  | 					if (edgeCrossA.dot(tmp) < 0) | ||
|  | 					{ | ||
|  | 						edgeCrossA*=-1; | ||
|  | 					} | ||
|  | 				} | ||
|  | 
 | ||
|  | 				btVector3 edgeCrossB = edge.cross(normalB).normalize(); | ||
|  | 
 | ||
|  | 				{ | ||
|  | 					btVector3 tmp = triangle[otherIndexB]-triangle[sharedVertsB[0]]; | ||
|  | 					if (edgeCrossB.dot(tmp) < 0) | ||
|  | 					{ | ||
|  | 						edgeCrossB*=-1; | ||
|  | 					} | ||
|  | 				} | ||
|  | 
 | ||
|  | 				btScalar	angle2 = 0; | ||
|  | 				btScalar	ang4 = 0.f; | ||
|  | 
 | ||
|  | 
 | ||
|  | 				btVector3 calculatedEdge = edgeCrossA.cross(edgeCrossB); | ||
|  | 				btScalar len2 = calculatedEdge.length2(); | ||
|  | 
 | ||
|  | 				btScalar correctedAngle(0); | ||
|  | 				//btVector3 calculatedNormalB = normalA;
 | ||
|  | 				bool isConvex = false; | ||
|  | 
 | ||
|  | 				if (len2<m_triangleInfoMap->m_planarEpsilon) | ||
|  | 				{ | ||
|  | 					angle2 = 0.f; | ||
|  | 					ang4 = 0.f; | ||
|  | 				} else | ||
|  | 				{ | ||
|  | 
 | ||
|  | 					calculatedEdge.normalize(); | ||
|  | 					btVector3 calculatedNormalA = calculatedEdge.cross(edgeCrossA); | ||
|  | 					calculatedNormalA.normalize(); | ||
|  | 					angle2 = btGetAngle(calculatedNormalA,edgeCrossA,edgeCrossB); | ||
|  | 					ang4 = SIMD_PI-angle2; | ||
|  | 					btScalar dotA = normalA.dot(edgeCrossB); | ||
|  | 					///@todo: check if we need some epsilon, due to floating point imprecision
 | ||
|  | 					isConvex = (dotA<0.); | ||
|  | 
 | ||
|  | 					correctedAngle = isConvex ? ang4 : -ang4; | ||
|  | 				} | ||
|  | 
 | ||
|  | 				 | ||
|  | 
 | ||
|  | 				 | ||
|  | 							 | ||
|  | 				//alternatively use 
 | ||
|  | 				//btVector3 calculatedNormalB2 = quatRotate(orn,normalA);
 | ||
|  | 
 | ||
|  | 
 | ||
|  | 				switch (sumvertsA) | ||
|  | 				{ | ||
|  | 				case 1: | ||
|  | 					{ | ||
|  | 						btVector3 edge = m_triangleVerticesA[0]-m_triangleVerticesA[1]; | ||
|  | 						btQuaternion orn(edge,-correctedAngle); | ||
|  | 						btVector3 computedNormalB = quatRotate(orn,normalA); | ||
|  | 						btScalar bla = computedNormalB.dot(normalB); | ||
|  | 						if (bla<0) | ||
|  | 						{ | ||
|  | 							computedNormalB*=-1; | ||
|  | 							info->m_flags |= TRI_INFO_V0V1_SWAP_NORMALB; | ||
|  | 						} | ||
|  | #ifdef DEBUG_INTERNAL_EDGE
 | ||
|  | 						if ((computedNormalB-normalB).length()>0.0001) | ||
|  | 						{ | ||
|  | 							printf("warning: normals not identical\n"); | ||
|  | 						} | ||
|  | #endif//DEBUG_INTERNAL_EDGE
 | ||
|  | 
 | ||
|  | 						info->m_edgeV0V1Angle = -correctedAngle; | ||
|  | 
 | ||
|  | 						if (isConvex) | ||
|  | 							info->m_flags |= TRI_INFO_V0V1_CONVEX; | ||
|  | 						break; | ||
|  | 					} | ||
|  | 				case 2: | ||
|  | 					{ | ||
|  | 						btVector3 edge = m_triangleVerticesA[2]-m_triangleVerticesA[0]; | ||
|  | 						btQuaternion orn(edge,-correctedAngle); | ||
|  | 						btVector3 computedNormalB = quatRotate(orn,normalA); | ||
|  | 						if (computedNormalB.dot(normalB)<0) | ||
|  | 						{ | ||
|  | 							computedNormalB*=-1; | ||
|  | 							info->m_flags |= TRI_INFO_V2V0_SWAP_NORMALB; | ||
|  | 						} | ||
|  | 
 | ||
|  | #ifdef DEBUG_INTERNAL_EDGE
 | ||
|  | 						if ((computedNormalB-normalB).length()>0.0001) | ||
|  | 						{ | ||
|  | 							printf("warning: normals not identical\n"); | ||
|  | 						} | ||
|  | #endif //DEBUG_INTERNAL_EDGE
 | ||
|  | 						info->m_edgeV2V0Angle = -correctedAngle; | ||
|  | 						if (isConvex) | ||
|  | 							info->m_flags |= TRI_INFO_V2V0_CONVEX; | ||
|  | 						break;	 | ||
|  | 					} | ||
|  | 				case 3: | ||
|  | 					{ | ||
|  | 						btVector3 edge = m_triangleVerticesA[1]-m_triangleVerticesA[2]; | ||
|  | 						btQuaternion orn(edge,-correctedAngle); | ||
|  | 						btVector3 computedNormalB = quatRotate(orn,normalA); | ||
|  | 						if (computedNormalB.dot(normalB)<0) | ||
|  | 						{ | ||
|  | 							info->m_flags |= TRI_INFO_V1V2_SWAP_NORMALB; | ||
|  | 							computedNormalB*=-1; | ||
|  | 						} | ||
|  | #ifdef DEBUG_INTERNAL_EDGE
 | ||
|  | 						if ((computedNormalB-normalB).length()>0.0001) | ||
|  | 						{ | ||
|  | 							printf("warning: normals not identical\n"); | ||
|  | 						} | ||
|  | #endif //DEBUG_INTERNAL_EDGE
 | ||
|  | 						info->m_edgeV1V2Angle = -correctedAngle; | ||
|  | 
 | ||
|  | 						if (isConvex) | ||
|  | 							info->m_flags |= TRI_INFO_V1V2_CONVEX; | ||
|  | 						break; | ||
|  | 					} | ||
|  | 				} | ||
|  | 
 | ||
|  | 				break; | ||
|  | 			} | ||
|  | 		default: | ||
|  | 			{ | ||
|  | 				//				printf("warning: duplicate triangle\n");
 | ||
|  | 			} | ||
|  | 
 | ||
|  | 		} | ||
|  | 	} | ||
|  | }; | ||
|  | /////////////////////////////////////////////////////////
 | ||
|  | /////////////////////////////////////////////////////////
 | ||
|  | 
 | ||
|  | void btGenerateInternalEdgeInfo (btBvhTriangleMeshShape*trimeshShape, btTriangleInfoMap* triangleInfoMap) | ||
|  | { | ||
|  | 	//the user pointer shouldn't already be used for other purposes, we intend to store connectivity info there!
 | ||
|  | 	if (trimeshShape->getTriangleInfoMap()) | ||
|  | 		return; | ||
|  | 
 | ||
|  | 	trimeshShape->setTriangleInfoMap(triangleInfoMap); | ||
|  | 
 | ||
|  | 	btStridingMeshInterface* meshInterface = trimeshShape->getMeshInterface(); | ||
|  | 	const btVector3& meshScaling = meshInterface->getScaling(); | ||
|  | 
 | ||
|  | 	for (int partId = 0; partId< meshInterface->getNumSubParts();partId++) | ||
|  | 	{ | ||
|  | 		const unsigned char *vertexbase = 0; | ||
|  | 		int numverts = 0; | ||
|  | 		PHY_ScalarType type = PHY_INTEGER; | ||
|  | 		int stride = 0; | ||
|  | 		const unsigned char *indexbase = 0; | ||
|  | 		int indexstride = 0; | ||
|  | 		int numfaces = 0; | ||
|  | 		PHY_ScalarType indicestype = PHY_INTEGER; | ||
|  | 		//PHY_ScalarType indexType=0;
 | ||
|  | 
 | ||
|  | 		btVector3 triangleVerts[3]; | ||
|  | 		meshInterface->getLockedReadOnlyVertexIndexBase(&vertexbase,numverts,	type,stride,&indexbase,indexstride,numfaces,indicestype,partId); | ||
|  | 		btVector3 aabbMin,aabbMax; | ||
|  | 
 | ||
|  | 		for (int triangleIndex = 0 ; triangleIndex < numfaces;triangleIndex++) | ||
|  | 		{ | ||
|  | 			unsigned int* gfxbase = (unsigned int*)(indexbase+triangleIndex*indexstride); | ||
|  | 
 | ||
|  | 			for (int j=2;j>=0;j--) | ||
|  | 			{ | ||
|  | 
 | ||
|  | 				int graphicsindex = indicestype==PHY_SHORT?((unsigned short*)gfxbase)[j]:gfxbase[j]; | ||
|  | 				if (type == PHY_FLOAT) | ||
|  | 				{ | ||
|  | 					float* graphicsbase = (float*)(vertexbase+graphicsindex*stride); | ||
|  | 					triangleVerts[j] = btVector3( | ||
|  | 						graphicsbase[0]*meshScaling.getX(), | ||
|  | 						graphicsbase[1]*meshScaling.getY(), | ||
|  | 						graphicsbase[2]*meshScaling.getZ()); | ||
|  | 				} | ||
|  | 				else | ||
|  | 				{ | ||
|  | 					double* graphicsbase = (double*)(vertexbase+graphicsindex*stride); | ||
|  | 					triangleVerts[j] = btVector3( btScalar(graphicsbase[0]*meshScaling.getX()), btScalar(graphicsbase[1]*meshScaling.getY()), btScalar(graphicsbase[2]*meshScaling.getZ())); | ||
|  | 				} | ||
|  | 			} | ||
|  | 			aabbMin.setValue(btScalar(BT_LARGE_FLOAT),btScalar(BT_LARGE_FLOAT),btScalar(BT_LARGE_FLOAT)); | ||
|  | 			aabbMax.setValue(btScalar(-BT_LARGE_FLOAT),btScalar(-BT_LARGE_FLOAT),btScalar(-BT_LARGE_FLOAT));  | ||
|  | 			aabbMin.setMin(triangleVerts[0]); | ||
|  | 			aabbMax.setMax(triangleVerts[0]); | ||
|  | 			aabbMin.setMin(triangleVerts[1]); | ||
|  | 			aabbMax.setMax(triangleVerts[1]); | ||
|  | 			aabbMin.setMin(triangleVerts[2]); | ||
|  | 			aabbMax.setMax(triangleVerts[2]); | ||
|  | 
 | ||
|  | 			btConnectivityProcessor connectivityProcessor; | ||
|  | 			connectivityProcessor.m_partIdA = partId; | ||
|  | 			connectivityProcessor.m_triangleIndexA = triangleIndex; | ||
|  | 			connectivityProcessor.m_triangleVerticesA = &triangleVerts[0]; | ||
|  | 			connectivityProcessor.m_triangleInfoMap  = triangleInfoMap; | ||
|  | 
 | ||
|  | 			trimeshShape->processAllTriangles(&connectivityProcessor,aabbMin,aabbMax); | ||
|  | 		} | ||
|  | 
 | ||
|  | 	} | ||
|  | 
 | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | // Given a point and a line segment (defined by two points), compute the closest point
 | ||
|  | // in the line.  Cap the point at the endpoints of the line segment.
 | ||
|  | void btNearestPointInLineSegment(const btVector3 &point, const btVector3& line0, const btVector3& line1, btVector3& nearestPoint) | ||
|  | { | ||
|  | 	btVector3 lineDelta     = line1 - line0; | ||
|  | 
 | ||
|  | 	// Handle degenerate lines
 | ||
|  | 	if ( lineDelta.fuzzyZero()) | ||
|  | 	{ | ||
|  | 		nearestPoint = line0; | ||
|  | 	} | ||
|  | 	else | ||
|  | 	{ | ||
|  | 		btScalar delta = (point-line0).dot(lineDelta) / (lineDelta).dot(lineDelta); | ||
|  | 
 | ||
|  | 		// Clamp the point to conform to the segment's endpoints
 | ||
|  | 		if ( delta < 0 ) | ||
|  | 			delta = 0; | ||
|  | 		else if ( delta > 1 ) | ||
|  | 			delta = 1; | ||
|  | 
 | ||
|  | 		nearestPoint = line0 + lineDelta*delta; | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | bool	btClampNormal(const btVector3& edge,const btVector3& tri_normal_org,const btVector3& localContactNormalOnB, btScalar correctedEdgeAngle, btVector3 & clampedLocalNormal) | ||
|  | { | ||
|  | 	btVector3 tri_normal = tri_normal_org; | ||
|  | 	//we only have a local triangle normal, not a local contact normal -> only normal in world space...
 | ||
|  | 	//either compute the current angle all in local space, or all in world space
 | ||
|  | 
 | ||
|  | 	btVector3 edgeCross = edge.cross(tri_normal).normalize(); | ||
|  | 	btScalar curAngle = btGetAngle(edgeCross,tri_normal,localContactNormalOnB); | ||
|  | 
 | ||
|  | 	if (correctedEdgeAngle<0) | ||
|  | 	{ | ||
|  | 		if (curAngle < correctedEdgeAngle) | ||
|  | 		{ | ||
|  | 			btScalar diffAngle = correctedEdgeAngle-curAngle; | ||
|  | 			btQuaternion rotation(edge,diffAngle ); | ||
|  | 			clampedLocalNormal = btMatrix3x3(rotation)*localContactNormalOnB; | ||
|  | 			return true; | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if (correctedEdgeAngle>=0) | ||
|  | 	{ | ||
|  | 		if (curAngle > correctedEdgeAngle) | ||
|  | 		{ | ||
|  | 			btScalar diffAngle = correctedEdgeAngle-curAngle; | ||
|  | 			btQuaternion rotation(edge,diffAngle ); | ||
|  | 			clampedLocalNormal = btMatrix3x3(rotation)*localContactNormalOnB; | ||
|  | 			return true; | ||
|  | 		} | ||
|  | 	} | ||
|  | 	return false; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | /// Changes a btManifoldPoint collision normal to the normal from the mesh.
 | ||
|  | void btAdjustInternalEdgeContacts(btManifoldPoint& cp, const btCollisionObjectWrapper* colObj0Wrap,const btCollisionObjectWrapper* colObj1Wrap, int partId0, int index0, int normalAdjustFlags) | ||
|  | { | ||
|  | 	//btAssert(colObj0->getCollisionShape()->getShapeType() == TRIANGLE_SHAPE_PROXYTYPE);
 | ||
|  | 	if (colObj0Wrap->getCollisionShape()->getShapeType() != TRIANGLE_SHAPE_PROXYTYPE) | ||
|  | 		return; | ||
|  | 
 | ||
|  | 	btBvhTriangleMeshShape* trimesh = 0; | ||
|  | 	 | ||
|  | 	if( colObj0Wrap->getCollisionObject()->getCollisionShape()->getShapeType() == SCALED_TRIANGLE_MESH_SHAPE_PROXYTYPE ) | ||
|  | 	   trimesh = ((btScaledBvhTriangleMeshShape*)colObj0Wrap->getCollisionObject()->getCollisionShape())->getChildShape(); | ||
|  |    else	    | ||
|  | 	   trimesh = (btBvhTriangleMeshShape*)colObj0Wrap->getCollisionObject()->getCollisionShape(); | ||
|  | 	    | ||
|  |    	btTriangleInfoMap* triangleInfoMapPtr = (btTriangleInfoMap*) trimesh->getTriangleInfoMap(); | ||
|  | 	if (!triangleInfoMapPtr) | ||
|  | 		return; | ||
|  | 
 | ||
|  | 	int hash = btGetHash(partId0,index0); | ||
|  | 
 | ||
|  | 
 | ||
|  | 	btTriangleInfo* info = triangleInfoMapPtr->find(hash); | ||
|  | 	if (!info) | ||
|  | 		return; | ||
|  | 
 | ||
|  | 	btScalar frontFacing = (normalAdjustFlags & BT_TRIANGLE_CONVEX_BACKFACE_MODE)==0? 1.f : -1.f; | ||
|  | 	 | ||
|  | 	const btTriangleShape* tri_shape = static_cast<const btTriangleShape*>(colObj0Wrap->getCollisionShape()); | ||
|  | 	btVector3 v0,v1,v2; | ||
|  | 	tri_shape->getVertex(0,v0); | ||
|  | 	tri_shape->getVertex(1,v1); | ||
|  | 	tri_shape->getVertex(2,v2); | ||
|  | 
 | ||
|  | 	//btVector3 center = (v0+v1+v2)*btScalar(1./3.);
 | ||
|  | 
 | ||
|  | 	btVector3 red(1,0,0), green(0,1,0),blue(0,0,1),white(1,1,1),black(0,0,0); | ||
|  | 	btVector3 tri_normal; | ||
|  | 	tri_shape->calcNormal(tri_normal); | ||
|  | 
 | ||
|  | 	//btScalar dot = tri_normal.dot(cp.m_normalWorldOnB);
 | ||
|  | 	btVector3 nearest; | ||
|  | 	btNearestPointInLineSegment(cp.m_localPointB,v0,v1,nearest); | ||
|  | 
 | ||
|  | 	btVector3 contact = cp.m_localPointB; | ||
|  | #ifdef BT_INTERNAL_EDGE_DEBUG_DRAW
 | ||
|  | 	const btTransform& tr = colObj0->getWorldTransform(); | ||
|  | 	btDebugDrawLine(tr*nearest,tr*cp.m_localPointB,red); | ||
|  | #endif //BT_INTERNAL_EDGE_DEBUG_DRAW
 | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | 	bool isNearEdge = false; | ||
|  | 
 | ||
|  | 	int numConcaveEdgeHits = 0; | ||
|  | 	int numConvexEdgeHits = 0; | ||
|  | 
 | ||
|  | 	btVector3 localContactNormalOnB = colObj0Wrap->getWorldTransform().getBasis().transpose() * cp.m_normalWorldOnB; | ||
|  | 	localContactNormalOnB.normalize();//is this necessary?
 | ||
|  | 	 | ||
|  | 	// Get closest edge
 | ||
|  | 	int      bestedge=-1; | ||
|  | 	btScalar    disttobestedge=BT_LARGE_FLOAT; | ||
|  | 	//
 | ||
|  | 	// Edge 0 -> 1
 | ||
|  | 	if (btFabs(info->m_edgeV0V1Angle)< triangleInfoMapPtr->m_maxEdgeAngleThreshold) | ||
|  | 	{	 | ||
|  | 	   btVector3 nearest; | ||
|  | 	   btNearestPointInLineSegment( cp.m_localPointB, v0, v1, nearest ); | ||
|  | 	   btScalar     len=(contact-nearest).length(); | ||
|  | 	   //
 | ||
|  | 	   if( len < disttobestedge ) | ||
|  | 	   { | ||
|  | 	      bestedge=0; | ||
|  | 	      disttobestedge=len; | ||
|  |       }	       | ||
|  |    }	    | ||
|  | 	// Edge 1 -> 2
 | ||
|  | 	if (btFabs(info->m_edgeV1V2Angle)< triangleInfoMapPtr->m_maxEdgeAngleThreshold) | ||
|  | 	{	 | ||
|  | 	   btVector3 nearest; | ||
|  | 	   btNearestPointInLineSegment( cp.m_localPointB, v1, v2, nearest ); | ||
|  | 	   btScalar     len=(contact-nearest).length(); | ||
|  | 	   //
 | ||
|  | 	   if( len < disttobestedge ) | ||
|  | 	   { | ||
|  | 	      bestedge=1; | ||
|  | 	      disttobestedge=len; | ||
|  |       }	       | ||
|  |    }	    | ||
|  | 	// Edge 2 -> 0
 | ||
|  | 	if (btFabs(info->m_edgeV2V0Angle)< triangleInfoMapPtr->m_maxEdgeAngleThreshold) | ||
|  | 	{	 | ||
|  | 	   btVector3 nearest; | ||
|  | 	   btNearestPointInLineSegment( cp.m_localPointB, v2, v0, nearest ); | ||
|  | 	   btScalar     len=(contact-nearest).length(); | ||
|  | 	   //
 | ||
|  | 	   if( len < disttobestedge ) | ||
|  | 	   { | ||
|  | 	      bestedge=2; | ||
|  | 	      disttobestedge=len; | ||
|  |       }	       | ||
|  |    }   	      	 | ||
|  | 	 | ||
|  | #ifdef BT_INTERNAL_EDGE_DEBUG_DRAW
 | ||
|  |    btVector3 upfix=tri_normal * btVector3(0.1f,0.1f,0.1f); | ||
|  |    btDebugDrawLine(tr * v0 + upfix, tr * v1 + upfix, red ); | ||
|  | #endif   
 | ||
|  | 	if (btFabs(info->m_edgeV0V1Angle)< triangleInfoMapPtr->m_maxEdgeAngleThreshold) | ||
|  | 	{ | ||
|  | #ifdef BT_INTERNAL_EDGE_DEBUG_DRAW
 | ||
|  | 		btDebugDrawLine(tr*contact,tr*(contact+cp.m_normalWorldOnB*10),black); | ||
|  | #endif
 | ||
|  | 		btScalar len = (contact-nearest).length(); | ||
|  | 		if(len<triangleInfoMapPtr->m_edgeDistanceThreshold) | ||
|  | 		if( bestedge==0 ) | ||
|  | 		{ | ||
|  | 			btVector3 edge(v0-v1); | ||
|  | 			isNearEdge = true; | ||
|  | 
 | ||
|  | 			if (info->m_edgeV0V1Angle==btScalar(0)) | ||
|  | 			{ | ||
|  | 				numConcaveEdgeHits++; | ||
|  | 			} else | ||
|  | 			{ | ||
|  | 
 | ||
|  | 				bool isEdgeConvex = (info->m_flags & TRI_INFO_V0V1_CONVEX); | ||
|  | 				btScalar swapFactor = isEdgeConvex ? btScalar(1) : btScalar(-1); | ||
|  | 	#ifdef BT_INTERNAL_EDGE_DEBUG_DRAW
 | ||
|  | 				btDebugDrawLine(tr*nearest,tr*(nearest+swapFactor*tri_normal*10),white); | ||
|  | 	#endif //BT_INTERNAL_EDGE_DEBUG_DRAW
 | ||
|  | 
 | ||
|  | 				btVector3 nA = swapFactor * tri_normal; | ||
|  | 
 | ||
|  | 				btQuaternion orn(edge,info->m_edgeV0V1Angle); | ||
|  | 				btVector3 computedNormalB = quatRotate(orn,tri_normal); | ||
|  | 				if (info->m_flags & TRI_INFO_V0V1_SWAP_NORMALB) | ||
|  | 					computedNormalB*=-1; | ||
|  | 				btVector3 nB = swapFactor*computedNormalB; | ||
|  | 
 | ||
|  | 				btScalar	NdotA = localContactNormalOnB.dot(nA); | ||
|  | 				btScalar	NdotB = localContactNormalOnB.dot(nB); | ||
|  | 				bool backFacingNormal = (NdotA< triangleInfoMapPtr->m_convexEpsilon) && (NdotB<triangleInfoMapPtr->m_convexEpsilon); | ||
|  | 
 | ||
|  | #ifdef DEBUG_INTERNAL_EDGE
 | ||
|  | 				{ | ||
|  | 					 | ||
|  | 					btDebugDrawLine(cp.getPositionWorldOnB(),cp.getPositionWorldOnB()+tr.getBasis()*(nB*20),red); | ||
|  | 				} | ||
|  | #endif //DEBUG_INTERNAL_EDGE
 | ||
|  | 
 | ||
|  | 
 | ||
|  | 				if (backFacingNormal) | ||
|  | 				{ | ||
|  | 					numConcaveEdgeHits++; | ||
|  | 				} | ||
|  | 				else | ||
|  | 				{ | ||
|  | 					numConvexEdgeHits++; | ||
|  | 					btVector3 clampedLocalNormal; | ||
|  | 					bool isClamped = btClampNormal(edge,swapFactor*tri_normal,localContactNormalOnB, info->m_edgeV0V1Angle,clampedLocalNormal); | ||
|  | 					if (isClamped) | ||
|  | 					{ | ||
|  | 						if (((normalAdjustFlags & BT_TRIANGLE_CONVEX_DOUBLE_SIDED)!=0) || (clampedLocalNormal.dot(frontFacing*tri_normal)>0)) | ||
|  | 						{ | ||
|  | 							btVector3 newNormal = colObj0Wrap->getWorldTransform().getBasis() * clampedLocalNormal; | ||
|  | 							//					cp.m_distance1 = cp.m_distance1 * newNormal.dot(cp.m_normalWorldOnB);
 | ||
|  | 							cp.m_normalWorldOnB = newNormal; | ||
|  | 							// Reproject collision point along normal. (what about cp.m_distance1?)
 | ||
|  | 							cp.m_positionWorldOnB = cp.m_positionWorldOnA - cp.m_normalWorldOnB * cp.m_distance1; | ||
|  | 							cp.m_localPointB = colObj0Wrap->getWorldTransform().invXform(cp.m_positionWorldOnB); | ||
|  | 							 | ||
|  | 						} | ||
|  | 					} | ||
|  | 				} | ||
|  | 			} | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	btNearestPointInLineSegment(contact,v1,v2,nearest); | ||
|  | #ifdef BT_INTERNAL_EDGE_DEBUG_DRAW
 | ||
|  | 	btDebugDrawLine(tr*nearest,tr*cp.m_localPointB,green); | ||
|  | #endif //BT_INTERNAL_EDGE_DEBUG_DRAW
 | ||
|  | 
 | ||
|  | #ifdef BT_INTERNAL_EDGE_DEBUG_DRAW
 | ||
|  |    btDebugDrawLine(tr * v1 + upfix, tr * v2 + upfix , green ); | ||
|  | #endif   
 | ||
|  | 
 | ||
|  | 	if (btFabs(info->m_edgeV1V2Angle)< triangleInfoMapPtr->m_maxEdgeAngleThreshold) | ||
|  | 	{ | ||
|  | #ifdef BT_INTERNAL_EDGE_DEBUG_DRAW
 | ||
|  | 		btDebugDrawLine(tr*contact,tr*(contact+cp.m_normalWorldOnB*10),black); | ||
|  | #endif //BT_INTERNAL_EDGE_DEBUG_DRAW
 | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | 		btScalar len = (contact-nearest).length(); | ||
|  | 		if(len<triangleInfoMapPtr->m_edgeDistanceThreshold) | ||
|  | 		if( bestedge==1 ) | ||
|  | 		{ | ||
|  | 			isNearEdge = true; | ||
|  | #ifdef BT_INTERNAL_EDGE_DEBUG_DRAW
 | ||
|  | 			btDebugDrawLine(tr*nearest,tr*(nearest+tri_normal*10),white); | ||
|  | #endif //BT_INTERNAL_EDGE_DEBUG_DRAW
 | ||
|  | 
 | ||
|  | 			btVector3 edge(v1-v2); | ||
|  | 
 | ||
|  | 			isNearEdge = true; | ||
|  | 
 | ||
|  | 			if (info->m_edgeV1V2Angle == btScalar(0)) | ||
|  | 			{ | ||
|  | 				numConcaveEdgeHits++; | ||
|  | 			} else | ||
|  | 			{ | ||
|  | 				bool isEdgeConvex = (info->m_flags & TRI_INFO_V1V2_CONVEX)!=0; | ||
|  | 				btScalar swapFactor = isEdgeConvex ? btScalar(1) : btScalar(-1); | ||
|  | 	#ifdef BT_INTERNAL_EDGE_DEBUG_DRAW
 | ||
|  | 				btDebugDrawLine(tr*nearest,tr*(nearest+swapFactor*tri_normal*10),white); | ||
|  | 	#endif //BT_INTERNAL_EDGE_DEBUG_DRAW
 | ||
|  | 
 | ||
|  | 				btVector3 nA = swapFactor * tri_normal; | ||
|  | 				 | ||
|  | 				btQuaternion orn(edge,info->m_edgeV1V2Angle); | ||
|  | 				btVector3 computedNormalB = quatRotate(orn,tri_normal); | ||
|  | 				if (info->m_flags & TRI_INFO_V1V2_SWAP_NORMALB) | ||
|  | 					computedNormalB*=-1; | ||
|  | 				btVector3 nB = swapFactor*computedNormalB; | ||
|  | 
 | ||
|  | #ifdef DEBUG_INTERNAL_EDGE
 | ||
|  | 				{ | ||
|  | 					btDebugDrawLine(cp.getPositionWorldOnB(),cp.getPositionWorldOnB()+tr.getBasis()*(nB*20),red); | ||
|  | 				} | ||
|  | #endif //DEBUG_INTERNAL_EDGE
 | ||
|  | 
 | ||
|  | 
 | ||
|  | 				btScalar	NdotA = localContactNormalOnB.dot(nA); | ||
|  | 				btScalar	NdotB = localContactNormalOnB.dot(nB); | ||
|  | 				bool backFacingNormal = (NdotA< triangleInfoMapPtr->m_convexEpsilon) && (NdotB<triangleInfoMapPtr->m_convexEpsilon); | ||
|  | 
 | ||
|  | 				if (backFacingNormal) | ||
|  | 				{ | ||
|  | 					numConcaveEdgeHits++; | ||
|  | 				} | ||
|  | 				else | ||
|  | 				{ | ||
|  | 					numConvexEdgeHits++; | ||
|  | 					btVector3 localContactNormalOnB = colObj0Wrap->getWorldTransform().getBasis().transpose() * cp.m_normalWorldOnB; | ||
|  | 					btVector3 clampedLocalNormal; | ||
|  | 					bool isClamped = btClampNormal(edge,swapFactor*tri_normal,localContactNormalOnB, info->m_edgeV1V2Angle,clampedLocalNormal); | ||
|  | 					if (isClamped) | ||
|  | 					{ | ||
|  | 						if (((normalAdjustFlags & BT_TRIANGLE_CONVEX_DOUBLE_SIDED)!=0) || (clampedLocalNormal.dot(frontFacing*tri_normal)>0)) | ||
|  | 						{ | ||
|  | 							btVector3 newNormal = colObj0Wrap->getWorldTransform().getBasis() * clampedLocalNormal; | ||
|  | 							//					cp.m_distance1 = cp.m_distance1 * newNormal.dot(cp.m_normalWorldOnB);
 | ||
|  | 							cp.m_normalWorldOnB = newNormal; | ||
|  | 							// Reproject collision point along normal.
 | ||
|  | 							cp.m_positionWorldOnB = cp.m_positionWorldOnA - cp.m_normalWorldOnB * cp.m_distance1; | ||
|  | 							cp.m_localPointB = colObj0Wrap->getWorldTransform().invXform(cp.m_positionWorldOnB); | ||
|  | 						} | ||
|  | 					} | ||
|  | 				} | ||
|  | 			} | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	btNearestPointInLineSegment(contact,v2,v0,nearest); | ||
|  | #ifdef BT_INTERNAL_EDGE_DEBUG_DRAW
 | ||
|  | 	btDebugDrawLine(tr*nearest,tr*cp.m_localPointB,blue); | ||
|  | #endif //BT_INTERNAL_EDGE_DEBUG_DRAW
 | ||
|  | #ifdef BT_INTERNAL_EDGE_DEBUG_DRAW
 | ||
|  |    btDebugDrawLine(tr * v2 + upfix, tr * v0 + upfix , blue ); | ||
|  | #endif   
 | ||
|  | 
 | ||
|  | 	if (btFabs(info->m_edgeV2V0Angle)< triangleInfoMapPtr->m_maxEdgeAngleThreshold) | ||
|  | 	{ | ||
|  | 
 | ||
|  | #ifdef BT_INTERNAL_EDGE_DEBUG_DRAW
 | ||
|  | 		btDebugDrawLine(tr*contact,tr*(contact+cp.m_normalWorldOnB*10),black); | ||
|  | #endif //BT_INTERNAL_EDGE_DEBUG_DRAW
 | ||
|  | 
 | ||
|  | 		btScalar len = (contact-nearest).length(); | ||
|  | 		if(len<triangleInfoMapPtr->m_edgeDistanceThreshold) | ||
|  | 		if( bestedge==2 ) | ||
|  | 		{ | ||
|  | 			isNearEdge = true; | ||
|  | #ifdef BT_INTERNAL_EDGE_DEBUG_DRAW
 | ||
|  | 			btDebugDrawLine(tr*nearest,tr*(nearest+tri_normal*10),white); | ||
|  | #endif //BT_INTERNAL_EDGE_DEBUG_DRAW
 | ||
|  | 
 | ||
|  | 			btVector3 edge(v2-v0); | ||
|  | 
 | ||
|  | 			if (info->m_edgeV2V0Angle==btScalar(0)) | ||
|  | 			{ | ||
|  | 				numConcaveEdgeHits++; | ||
|  | 			} else | ||
|  | 			{ | ||
|  | 
 | ||
|  | 				bool isEdgeConvex = (info->m_flags & TRI_INFO_V2V0_CONVEX)!=0; | ||
|  | 				btScalar swapFactor = isEdgeConvex ? btScalar(1) : btScalar(-1); | ||
|  | 	#ifdef BT_INTERNAL_EDGE_DEBUG_DRAW
 | ||
|  | 				btDebugDrawLine(tr*nearest,tr*(nearest+swapFactor*tri_normal*10),white); | ||
|  | 	#endif //BT_INTERNAL_EDGE_DEBUG_DRAW
 | ||
|  | 
 | ||
|  | 				btVector3 nA = swapFactor * tri_normal; | ||
|  | 				btQuaternion orn(edge,info->m_edgeV2V0Angle); | ||
|  | 				btVector3 computedNormalB = quatRotate(orn,tri_normal); | ||
|  | 				if (info->m_flags & TRI_INFO_V2V0_SWAP_NORMALB) | ||
|  | 					computedNormalB*=-1; | ||
|  | 				btVector3 nB = swapFactor*computedNormalB; | ||
|  | 
 | ||
|  | #ifdef DEBUG_INTERNAL_EDGE
 | ||
|  | 				{ | ||
|  | 					btDebugDrawLine(cp.getPositionWorldOnB(),cp.getPositionWorldOnB()+tr.getBasis()*(nB*20),red); | ||
|  | 				} | ||
|  | #endif //DEBUG_INTERNAL_EDGE
 | ||
|  | 
 | ||
|  | 				btScalar	NdotA = localContactNormalOnB.dot(nA); | ||
|  | 				btScalar	NdotB = localContactNormalOnB.dot(nB); | ||
|  | 				bool backFacingNormal = (NdotA< triangleInfoMapPtr->m_convexEpsilon) && (NdotB<triangleInfoMapPtr->m_convexEpsilon); | ||
|  | 
 | ||
|  | 				if (backFacingNormal) | ||
|  | 				{ | ||
|  | 					numConcaveEdgeHits++; | ||
|  | 				} | ||
|  | 				else | ||
|  | 				{ | ||
|  | 					numConvexEdgeHits++; | ||
|  | 					//				printf("hitting convex edge\n");
 | ||
|  | 
 | ||
|  | 
 | ||
|  | 					btVector3 localContactNormalOnB = colObj0Wrap->getWorldTransform().getBasis().transpose() * cp.m_normalWorldOnB; | ||
|  | 					btVector3 clampedLocalNormal; | ||
|  | 					bool isClamped = btClampNormal(edge,swapFactor*tri_normal,localContactNormalOnB,info->m_edgeV2V0Angle,clampedLocalNormal); | ||
|  | 					if (isClamped) | ||
|  | 					{ | ||
|  | 						if (((normalAdjustFlags & BT_TRIANGLE_CONVEX_DOUBLE_SIDED)!=0) || (clampedLocalNormal.dot(frontFacing*tri_normal)>0)) | ||
|  | 						{ | ||
|  | 							btVector3 newNormal = colObj0Wrap->getWorldTransform().getBasis() * clampedLocalNormal; | ||
|  | 							//					cp.m_distance1 = cp.m_distance1 * newNormal.dot(cp.m_normalWorldOnB);
 | ||
|  | 							cp.m_normalWorldOnB = newNormal; | ||
|  | 							// Reproject collision point along normal.
 | ||
|  | 							cp.m_positionWorldOnB = cp.m_positionWorldOnA - cp.m_normalWorldOnB * cp.m_distance1; | ||
|  | 							cp.m_localPointB = colObj0Wrap->getWorldTransform().invXform(cp.m_positionWorldOnB); | ||
|  | 						} | ||
|  | 					} | ||
|  | 				}  | ||
|  | 			} | ||
|  | 			 | ||
|  | 
 | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | #ifdef DEBUG_INTERNAL_EDGE
 | ||
|  | 	{ | ||
|  | 		btVector3 color(0,1,1); | ||
|  | 		btDebugDrawLine(cp.getPositionWorldOnB(),cp.getPositionWorldOnB()+cp.m_normalWorldOnB*10,color); | ||
|  | 	} | ||
|  | #endif //DEBUG_INTERNAL_EDGE
 | ||
|  | 
 | ||
|  | 	if (isNearEdge) | ||
|  | 	{ | ||
|  | 
 | ||
|  | 		if (numConcaveEdgeHits>0) | ||
|  | 		{ | ||
|  | 			if ((normalAdjustFlags & BT_TRIANGLE_CONCAVE_DOUBLE_SIDED)!=0) | ||
|  | 			{ | ||
|  | 				//fix tri_normal so it pointing the same direction as the current local contact normal
 | ||
|  | 				if (tri_normal.dot(localContactNormalOnB) < 0) | ||
|  | 				{ | ||
|  | 					tri_normal *= -1; | ||
|  | 				} | ||
|  | 				cp.m_normalWorldOnB = colObj0Wrap->getWorldTransform().getBasis()*tri_normal; | ||
|  | 			} else | ||
|  | 			{ | ||
|  | 				btVector3 newNormal = tri_normal *frontFacing; | ||
|  | 				//if the tri_normal is pointing opposite direction as the current local contact normal, skip it
 | ||
|  | 				btScalar d = newNormal.dot(localContactNormalOnB) ; | ||
|  | 				if (d< 0) | ||
|  | 				{ | ||
|  | 					return; | ||
|  | 				} | ||
|  | 				//modify the normal to be the triangle normal (or backfacing normal)
 | ||
|  | 				cp.m_normalWorldOnB = colObj0Wrap->getWorldTransform().getBasis() *newNormal; | ||
|  | 			} | ||
|  | 						 | ||
|  | 			// Reproject collision point along normal.
 | ||
|  | 			cp.m_positionWorldOnB = cp.m_positionWorldOnA - cp.m_normalWorldOnB * cp.m_distance1; | ||
|  | 			cp.m_localPointB = colObj0Wrap->getWorldTransform().invXform(cp.m_positionWorldOnB); | ||
|  | 		} | ||
|  | 	} | ||
|  | } |