PX_INLINE void computeFrictionTangents(const PxVec3& vrel,const PxVec3& unitNormal, PxVec3& t0, PxVec3& t1)
{
	PX_ASSERT(PxAbs(unitNormal.magnitude()-1)<1e-3f);

	t0 = vrel - unitNormal * unitNormal.dot(vrel);
	PxReal ll = t0.magnitudeSquared();

	if (ll > 0.1f)										//can set as low as 0.
	{
		t0 *= PxRecipSqrt(ll);
		t1 = unitNormal.cross(t0);
	}
	else
		Ps::normalToTangents(unitNormal, t0, t1);		//fallback
}
Ejemplo n.º 2
0
	/**
	\brief computes a oriented bounding box around the scaled basis.
	\param basis Input = skewed basis, Output = (normalized) orthogonal basis.
	\return Bounding box extent.
	*/
	PxVec3 optimizeBoundingBox(PxMat33& basis)
	{
		PxVec3* PX_RESTRICT vec = &basis[0];	// PT: don't copy vectors if not needed...

		// PT: since we store the magnitudes to memory, we can avoid the FCMPs afterwards
		PxVec3 magnitude( vec[0].magnitudeSquared(), vec[1].magnitudeSquared(), vec[2].magnitudeSquared() );

		// find indices sorted by magnitude
#ifdef PX_X360
		int i = (PxU32&)(magnitude[1]) > (PxU32&)(magnitude[ 0 ]) ? 1 :  0;
		int j = (PxU32&)(magnitude[2]) > (PxU32&)(magnitude[1-i]) ? 2 : 1-i;
#else
		int i = magnitude[1] > magnitude[ 0 ] ? 1 :  0;
		int j = magnitude[2] > magnitude[1-i] ? 2 : 1-i;
#endif
		const int k = 3 - i - j;
#ifdef PX_X360
		if((PxU32&)(magnitude[i]) < (PxU32&)(magnitude[j]))
#else
		if(magnitude[i] < magnitude[j])
#endif
			swap(i, j);

		// ortho-normalize basis

		PxReal invSqrt = PxRecipSqrt(magnitude[i]); 
		magnitude[i] *= invSqrt; vec[i] *= invSqrt; // normalize the first axis
		PxReal dotij = vec[i].dot(vec[j]);
		PxReal dotik = vec[i].dot(vec[k]);
		magnitude[i] += PxAbs(dotij) + PxAbs(dotik); // elongate the axis by projection of the other two
		vec[j] -= vec[i] * dotij; // orthogonize the two remaining axii relative to vec[i]
		vec[k] -= vec[i] * dotik;

		magnitude[j] = vec[j].normalize();
		PxReal dotjk = vec[j].dot(vec[k]);
		magnitude[j] += PxAbs(dotjk); // elongate the axis by projection of the other one
		vec[k] -= vec[j] * dotjk; // orthogonize vec[k] relative to vec[j]

		magnitude[k] = vec[k].normalize();

		return magnitude;
	}
PX_FORCE_INLINE void collideWithSphere(PxsParticleCollData& collData, const PxSphereGeometry& sphereShapeData,
									   PxReal proxRadius)
{
	PxVec3& oldPos = collData.localOldPos;
	PxVec3& newPos = collData.localNewPos;

	PxReal radius = sphereShapeData.radius;

	PxReal oldPosDist2 = oldPos.magnitudeSquared();
	PxReal radius2 = radius * radius;

	bool oldInSphere = (oldPosDist2 < radius2);

	if(oldInSphere)
	{
		// old position inside the skeleton
		// add ccd with time 0.0

		collData.localSurfaceNormal = oldPos;
		if (oldPosDist2 > 0.0f)
			collData.localSurfaceNormal *= PxRecipSqrt(oldPosDist2);
		else
			collData.localSurfaceNormal = PxVec3(0,1.0f,0);

		// Push particle to surface such that the distance to the surface is equal to the collision radius
		collData.localSurfacePos = collData.localSurfaceNormal * (radius + collData.restOffset);
		collData.ccTime = 0.0;
		collData.localFlags |= PXS_FLUID_COLL_FLAG_L_CC;
	}
	else
	{
		// old position is outside of the skeleton
		
		PxVec3 motion = newPos - oldPos;

		// Discriminant
		PxReal b = motion.dot(oldPos) * 2.0f;
		PxReal a2 = 2.0f * motion.magnitudeSquared();
		PxReal disc = (b*b) - (2.0f * a2 * (oldPosDist2 - radius2));

		bool intersection = disc > 0.0f;

		if ((!intersection) || (a2 == 0.0f))
		{
			// the ray does not intersect the sphere
			collideWithSphereNonContinuous(collData, newPos, radius, proxRadius);
		}
		else
		{
			// the ray intersects the sphere
			PxReal t = -(b + PxSqrt(disc)) / a2;	// Compute intersection point
			
			if (t < 0.0f || t > 1.0f)
			{
				// intersection point lies outside motion vector
				collideWithSphereNonContinuous(collData, newPos, radius, proxRadius);
			}
			else if(t < collData.ccTime)
			{
				// intersection point lies on sphere, add lcc
				//collData.localSurfacePos = oldPos + (motion * t);
				//collData.localSurfaceNormal = collData.localSurfacePos;
				//collData.localSurfaceNormal *= (1.0f / radius);
				//collData.localSurfacePos += (collData.localSurfaceNormal * collData.restOffset);
				PxVec3 relativeImpact = motion*t;
				collData.localSurfaceNormal = oldPos + relativeImpact;
				collData.localSurfaceNormal *= (1.0f / radius);		
				computeContinuousTargetPosition(collData.localSurfacePos, collData.localOldPos, relativeImpact, collData.localSurfaceNormal, collData.restOffset);
				
				collData.ccTime = t;
				collData.localFlags |= PXS_FLUID_COLL_FLAG_L_CC;
			}
		}
	}
}
	PX_FORCE_INLINE PxU32 collideWithMeshTriangle(PxVec3& surfaceNormal, PxVec3& surfacePos,
								  PxVec3& proxSurfaceNormal, PxVec3& proxSurfacePos,
								  PxReal& ccTime, PxReal& distOldToSurface,
								  const PxVec3& oldPos, const PxVec3& newPos,
								  const PxVec3& origin, const PxVec3& e0,
								  const PxVec3& e1, bool hasCC,
								  const PxReal& collRadius, const PxReal& proxRadius)
	{
		PxU32 flags = 0;

		PxReal collisionRadius2 = collRadius * collRadius;
		PxReal proximityRadius2 = proxRadius * proxRadius;

		PxVec3 motion = newPos - oldPos;

		// dc and proximity tests
		PxVec3 tmpV = origin - newPos;

		PxReal a = e0.dot(e0);
		PxReal b = e0.dot(e1);
		PxReal c = e1.dot(e1);
		PxReal d = e0.dot(tmpV);
		PxReal e = e1.dot(tmpV);
		PxVec3 coords;
		coords.x = b*e - c*d;	// s * det
		coords.y = b*d - a*e;	// t * det
		coords.z = a*c - b*b;	// det

		bool insideCase = false;
		PxVec3 clampedCoords(PxVec3(0));
		if (coords.x <= 0.0f) 
		{
			c = PxMax(c, FLT_MIN);
			clampedCoords.y = -e/c;
		}
		else if (coords.y <= 0.0f) 
		{
			a = PxMax(a, FLT_MIN);
			clampedCoords.x = -d/a;
		}
		else if (coords.x + coords.y > coords.z) 
		{
			PxReal denominator = a + c - b - b;
			PxReal numerator   = c + e - b - d;
			denominator = PxMax(denominator, FLT_MIN);
			clampedCoords.x = numerator / denominator;
			clampedCoords.y = 1.0f - clampedCoords.x;
		}
		else // all inside 
		{	
			PxReal tmpF = PxMax(coords.z, FLT_MIN);
			tmpF = 1.0f / tmpF;
			clampedCoords.x = coords.x * tmpF;
			clampedCoords.y = coords.y * tmpF;
			insideCase = true;
		}
		clampedCoords.x = PxMax(clampedCoords.x, 0.0f);
		clampedCoords.y = PxMax(clampedCoords.y, 0.0f);
		clampedCoords.x = PxMin(clampedCoords.x, 1.0f);
		clampedCoords.y = PxMin(clampedCoords.y, 1.0f);

		// Closest point to particle inside triangle
		PxVec3 closest = origin + e0 * clampedCoords.x + e1 * clampedCoords.y;

		PxVec3 triangleOffset = newPos - closest;
		PxReal triangleDistance2 = triangleOffset.magnitudeSquared();

		PxVec3 triangleNormal = e0.cross(e1);
		PxReal e0e1Span = triangleNormal.magnitude();
		
		bool isInFront = triangleOffset.dot(triangleNormal) > 0.0f;

		// MS: Possible optimzation
		/*
		if (isInFront && (triangleDistance2 >= proximityRadius2))
			return flags;
		*/

		bool isInProximity = insideCase && (triangleDistance2 < proximityRadius2) && isInFront;
		bool isInDiscrete = (triangleDistance2 < collisionRadius2) && isInFront;

		if (!hasCC)
		{
			// Only apply discrete and proximity collisions if no continuous collisions was detected so far (for any colliding shape)

			if (isInDiscrete)
			{
				if (triangleDistance2 > PXS_FLUID_COLL_TRI_DISTANCE)
				{
					surfaceNormal = triangleOffset * PxRecipSqrt(triangleDistance2);
				}
				else
				{
					surfaceNormal = triangleNormal * (1.0f / e0e1Span);
				}
				surfacePos = closest + (surfaceNormal * collRadius);
				flags |= PXS_FLUID_COLL_FLAG_L_DC;
			}
			
			if (isInProximity)
			{
				proxSurfaceNormal = triangleNormal * (1.0f / e0e1Span);
				proxSurfacePos = closest + (proxSurfaceNormal * collRadius);
				flags |= PXS_FLUID_COLL_FLAG_L_PROX;

				tmpV = (oldPos - origin); //this time it's not the newPosition offset.
				distOldToSurface = proxSurfaceNormal.dot(tmpV);	// Need to return the distance to decide which constraints should be thrown away
			}
		}

		if (!isInDiscrete && !isInProximity)
		{
			// cc test (let's try only executing this if no discrete coll, or proximity happend).
			tmpV = origin - oldPos; //this time it's not the newPosition offset.
			PxReal pDistN = triangleNormal.dot(tmpV);
			PxReal rLengthN = triangleNormal.dot(motion);

			if (pDistN > 0.0f || rLengthN >= pDistN) 
				return flags;

			//we are in the half closed interval [0.0f, 1.0)
			
			PxReal t = pDistN / rLengthN;
			PX_ASSERT((t >= 0.0f) && (t < 1.0f));

			PxVec3 relativePOSITION = (motion * t);
			PxVec3 testPoint = oldPos + relativePOSITION;

			// a,b,c and coords.z don't depend on test point -> still valid
			tmpV = origin - testPoint;
			d = e0.dot(tmpV);
			e = e1.dot(tmpV);
			coords.x = b*e - c*d;
			coords.y = b*d - a*e;

			//maybe we don't need this for rare case leaking on triangle boundaries? 
			PxReal eps = coords.z * PXS_FLUID_COLL_RAY_EPSILON_FACTOR;

			if ((coords.x >= -eps) && (coords.y >= -eps) && (coords.x + coords.y <= coords.z + eps)) 
			{
				PxReal invLengthN = (1.0f / e0e1Span);
				distOldToSurface = -pDistN * invLengthN;	// Need to return the distance to decide which constraints should be thrown away
				surfaceNormal = triangleNormal * invLengthN;
				//surfacePos = testPoint + (surfaceNormal * collRadius);
				computeContinuousTargetPosition(surfacePos, oldPos, relativePOSITION, surfaceNormal, collRadius);
				ccTime = t;
				flags |= PXS_FLUID_COLL_FLAG_L_CC;
			}
		}

		return flags;
	}
bool PxcContactCapsuleCapsule(CONTACT_METHOD_ARGS)
{
	PX_UNUSED(npCache);

	// Get actual shape data
	const PxCapsuleGeometry& shapeCapsule0 = shape0.get<const PxCapsuleGeometry>();
	const PxCapsuleGeometry& shapeCapsule1 = shape1.get<const PxCapsuleGeometry>();

	// Capsule-capsule contact generation
	Gu::Segment segment[2];	

	segment[0].p0 = transform0.q.getBasisVector0() * shapeCapsule0.halfHeight;
	segment[0].p1 = -segment[0].p0;

	segment[1].p0 = transform1.q.getBasisVector0() * shapeCapsule1.halfHeight;
	segment[1].p1 = -segment[1].p0;

	PxVec3 delta = transform1.p - transform0.p;
	segment[1].p1 += delta;
	segment[1].p0 += delta;

	// Collision detection
	PxReal s,t;
#ifdef USE_NEW_VERSION
	PxReal squareDist = Gu::distanceSegmentSegmentSquared2(segment[0], segment[1], &s, &t);
#else
	PxReal squareDist = PxcDistanceSegmentSegmentSquaredOLD(segment[0].point0, segment[0].direction(), segment[1].point0, segment[1].direction(), &s, &t);
#endif

	const PxReal radiusSum = shapeCapsule0.radius + shapeCapsule1.radius;
	const PxReal inflatedSum = radiusSum + contactDistance;
	const PxReal inflatedSumSquared = inflatedSum*inflatedSum;

	if(squareDist < inflatedSumSquared)
	{
		PxVec3 dir[2];
		dir[0] = segment[0].computeDirection();
		dir[1] = segment[1].computeDirection();

		PxReal segLen[2];
		segLen[0] = dir[0].magnitude();
		segLen[1] = dir[1].magnitude();

		if (segLen[0]) dir[0] *= 1.0f / segLen[0];
		if (segLen[1]) dir[1] *= 1.0f / segLen[1];

		if (PxAbs(dir[0].dot(dir[1])) > 0.9998f)	//almost parallel, ca. 1 degree difference --> generate two contact points at ends
		{
			PxU32 numCons = 0;

			PxReal segLenEps[2];
			segLenEps[0] = segLen[0] * 0.001f;//0.1% error is ok.
			segLenEps[1] = segLen[1] * 0.001f;
			
			//project the two end points of each onto the axis of the other and take those 4 points.
			//we could also generate a single normal at the single closest point, but this would be 'unstable'.

			for (PxU32 destShapeIndex = 0; destShapeIndex < 2; destShapeIndex ++)
			{
				for (PxU32 startEnd = 0; startEnd < 2; startEnd ++)
				{
					const PxU32 srcShapeIndex = 1-destShapeIndex;
					//project start/end of srcShapeIndex onto destShapeIndex.
					PxVec3 pos[2];
					pos[destShapeIndex] = startEnd ? segment[srcShapeIndex].p1 : segment[srcShapeIndex].p0;
					const PxReal p = dir[destShapeIndex].dot(pos[destShapeIndex] - segment[destShapeIndex].p0);
					if (p >= -segLenEps[destShapeIndex] && p <= (segLen[destShapeIndex] + segLenEps[destShapeIndex]))
					{
						pos[srcShapeIndex] = p * dir[destShapeIndex] + segment[destShapeIndex].p0;

						PxVec3 normal = pos[1] - pos[0];
						
						const PxReal normalLenSq = normal.magnitudeSquared();
						if (normalLenSq > 1e-6 && normalLenSq < inflatedSumSquared)
						{
							const PxReal distance = PxSqrt(normalLenSq);
							normal *= 1.0f/distance;
							PxVec3 point = pos[1] - normal * (srcShapeIndex ? shapeCapsule1 : shapeCapsule0).radius;
							point += transform0.p;
							contactBuffer.contact(point, normal, distance - radiusSum);
							numCons++;
						}					
					}
				}
			}

			if (numCons)	//if we did not have contacts, then we may have the case where they are parallel, but are stacked end to end, in which case the old code will generate good contacts.
				return true;
		}

		// Collision response
		PxVec3 pos1 = segment[0].getPointAt(s);
		PxVec3 pos2 = segment[1].getPointAt(t);

		PxVec3 normal = pos1 - pos2;

		const PxReal normalLenSq = normal.magnitudeSquared();
		if (normalLenSq < 1e-6)
		{
			// Zero normal -> pick the direction of segment 0.
			// Not always accurate but consistent with FW.
			if (segLen[0] > 1e-6)
				normal = dir[0];
			else 
				normal = PxVec3(1.0f, 0.0f, 0.0f);
		}
		else
		{
			normal *= PxRecipSqrt(normalLenSq);
		}
	
		pos1 += transform0.p;
		contactBuffer.contact(pos1 - normal * shapeCapsule0.radius, normal, PxSqrt(squareDist) - radiusSum);
		return true;
	}
	return false;
}