// PT: TODO: use Gu::intersectLineTriangle
static bool raycastTriangle(
	PxVec3& hit, 
	PxReal& t,
	const PxVec3& rayOrigin,
	const PxVec3& rayDir,
	const PxVec3& p0, 
	const PxVec3& p1, 
	const PxVec3& p2)
{
	PxVec3 d1 = p1 - p0;
	PxVec3 d2 = p2 - p0;
	PxVec3 n = d1.cross(d2);
	t = rayDir.dot(n);
	if (t == 0.0f)
		return false;
	t = n.dot(p0 - rayOrigin) / t;
	if (t < 0.0f)
		return false;
	hit = rayOrigin + rayDir * t;
	PxVec3 d0 = p0 - hit;
	d1 = p1-hit;
	d2 = p2-hit;
	PxVec3 c = d0.cross(d1);
	if (c.dot(n) < 0.0f)
		return false;
	c = d1.cross(d2);
	if (c.dot(n) < 0.0f)
		return false;
	c = d2.cross(d0);
	if (c.dot(n) < 0.0f)
		return false;
	return true;
}
void SampleVehicleWayPoints::update(const PxTransform& playerTransform, const PxF32 timestep)
{
	//Increment the elapsed time
	mTimeElapsed+=timestep;

	//Work out the point on the crossing line of the next way-point that is closest to the player.
	const PxTransform& nextWayPoint=mWayPoints[mProgress+1];
	const PxVec3 v=nextWayPoint.p;
	const PxVec3 w=nextWayPoint.q.getBasisVector0();
	const PxVec3 p=playerTransform.p;
	const PxVec3 pv=p-v;
	const PxF32 t=pv.dot(w);

	//Test if the player's position is inside the width of the line crossing the next way-point.
	if(PxAbs(t) < LINEWIDTH)
	{
		//Now test if the shortest distance to the next crossing line is smaller than a threshold.
		const PxVec3 linePos=v+w*t;
		const PxVec3 diff=p-linePos;
		const PxF32 dist2=diff.magnitudeSquared();
		if(dist2<LINEDISTANCE2)
		{
			mProgress++;
		}
	}

	if(mProgress == mNumWayPoints-1)
	{
		mMinTimeElapsed=PxMin(mTimeElapsed, mMinTimeElapsed);
		mTimeElapsed=0;
		mProgress=0;
	}
}
// Based on GD Mag code, but now works correctly when origin is inside the sphere.
// This version has limited accuracy.
bool Gu::intersectRaySphereBasic(const PxVec3& origin, const PxVec3& dir, PxReal length, const PxVec3& center, PxReal radius, PxReal& dist, PxVec3* hit_pos)
{
	// get the offset vector
	const PxVec3 offset = center - origin;

	// get the distance along the ray to the center point of the sphere
	const PxReal ray_dist = dir.dot(offset);

	// get the squared distances
	const PxReal off2 = offset.dot(offset);
	const PxReal rad_2 = radius * radius;
	if(off2 <= rad_2)
	{
		// we're in the sphere
		if(hit_pos)
			*hit_pos	= origin;
		dist	= 0.0f;
		return true;
	}

	if(ray_dist <= 0 || (ray_dist - length) > radius)
	{
		// moving away from object or too far away
		return false;
	}

	// find hit distance squared
	const PxReal d = rad_2 - (off2 - ray_dist * ray_dist);
	if(d<0.0f)
	{
		// ray passes by sphere without hitting
		return false;
	}

	// get the distance along the ray
	dist = ray_dist - PxSqrt(d);
	if(dist > length)
	{
		// hit point beyond length
		return false;
	}

	// sort out the details
	if(hit_pos)
		*hit_pos = origin + dir * dist;
	return true;
}
static SPU_INLINE		int testAxis(	const PxTriangle& tri, const PxVec3& extents,
										const PxVec3& dir, const PxVec3& axis,
										bool& bValidMTD, float& tfirst, float& tlast)
#endif
{
	const float d0t = tri.verts[0].dot(axis);
	const float d1t = tri.verts[1].dot(axis);
	const float d2t = tri.verts[2].dot(axis);

	float TriMin = physx::intrinsics::selectMin(d0t, d1t);
	float TriMax = physx::intrinsics::selectMax(d0t, d1t);
	TriMin = physx::intrinsics::selectMin(TriMin, d2t);
	TriMax = physx::intrinsics::selectMax(TriMax, d2t);

	////////

	const float BoxExt = physx::intrinsics::abs(axis.x)*extents.x + physx::intrinsics::abs(axis.y)*extents.y + physx::intrinsics::abs(axis.z)*extents.z;
	TEST_OVERLAP

	const float v = dir.dot(axis);
	if(physx::intrinsics::abs(v) < 1.0E-6f)
#ifdef _XBOX
//		return float(bIntersect);
		return bIntersect;
#else
		return bIntersect;
#endif
	const float oneOverV = -1.0f / v;

//	float t0 = d0 * oneOverV;
//	float t1 = d1 * oneOverV;
//	if(t0 > t1)	TSwap(t0, t1);
	const float t0_ = d0 * oneOverV;
	const float t1_ = d1 * oneOverV;
	float t0 = physx::intrinsics::selectMin(t0_, t1_);
	float t1 = physx::intrinsics::selectMax(t0_, t1_);

#ifdef _XBOX
	const float cndt0 = physx::intrinsics::fsel(tlast - t0, 1.0f, 0.0f);
	const float cndt1 = physx::intrinsics::fsel(t1 - tfirst, 1.0f, 0.0f);
#else
	if(t0 > tlast)	return false;
	if(t1 < tfirst)	return false;
#endif

//	if(t1 < tlast)	tlast = t1;
	tlast = physx::intrinsics::selectMin(t1, tlast);

//	if(t0 > tfirst)	tfirst = t0;
	tfirst = physx::intrinsics::selectMax(t0, tfirst);

#ifdef _XBOX
//	return int(cndt0*cndt1);
	return cndt0*cndt1;
#else
	return true;
#endif
}
Example #5
0
// Logic to determine if vehicle needs to be reset and does so if needed
void Vehicle::resetIfNeeded()
{
    PxVec3 up(0, 1, 0);
    PxVec3 vehUp = actor->getGlobalPose().rotate(up);

    PxF32 cos = vehUp.dot(up);
    PxVec3 vel = ((PxRigidDynamic*)actor)->getLinearVelocity();
    if (cos <= 0.707f && vel.x == 0 && vel.y == 0 && vel.z == 0)
    {
        PxVec3 forw(0, 0, 1);
        PxVec3 vehForw = actor->getGlobalPose().rotate(forw);
        PxVec3 vehLeft = PxTransform(PxVec3(0), PxQuat(-PxPi/2, PxVec3(0, 1, 0))).rotate(vehForw);

        PxF32 angleRad = (vehLeft.dot(forw) < 0) ? -acos(vehForw.dot(forw)) : acos(vehForw.dot(forw));
        PxTransform cur = actor->getGlobalPose();
        actor->setGlobalPose(PxTransform(PxVec3(cur.p.x, cur.p.y + 0.5f, cur.p.z), PxQuat(angleRad, up)));
    }
}
// -------------------------------------------------------------------------------------
void PolygonTriangulator::importPoints(const PxVec3 *points, int numPoints, const int *indices, PxVec3 *planeNormal, bool &isConvex)
{
	// find projection 3d -> 2d;
	PxVec3 n;

	isConvex = true;

	if (planeNormal) 
		n = *planeNormal;
	else {
		PX_ASSERT(numPoints >= 3);
		n = PxVec3(0.0f, 0.0f, 0.0f);

		for (int i = 1; i < numPoints-1; i++) {
			int i0 = 0;
			int i1 = i;
			int i2 = i+1;
			if (indices) {
				i0 = indices[i0];
				i1 = indices[i1];
				i2 = indices[i2];
			}
			const PxVec3 &p0 = points[i0];
			const PxVec3 &p1 = points[i1];
			const PxVec3 &p2 = points[i2];
			PxVec3 ni = (p1-p0).cross(p2-p0);
			if (i > 1 && ni.dot(n) < 0.0f)
				isConvex = false;
			n += ni;
		}
	}

	n.normalize();
	PxVec3 t0,t1;

	if (fabs(n.x) < fabs(n.y) && fabs(n.x) < fabs(n.z))
		t0 = PxVec3(1.0f, 0.0f, 0.0f);
	else if (fabs(n.y) < fabs(n.z))
		t0 = PxVec3(0.0f, 1.0f, 0.0f);
	else
		t0 = PxVec3(0.0f, 0.0f, 1.0f);
	t1 = n.cross(t0);
	t1.normalize();
	t0 = t1.cross(n);
	
	mPoints.resize((uint32_t)numPoints);
	if (indices == NULL) {
		for (uint32_t i = 0; i < (uint32_t)numPoints; i++) 
			mPoints[i] = PxVec3(points[i].dot(t0), points[i].dot(t1), 0.0f);
	}
	else {
		for (uint32_t i = 0; i < (uint32_t)numPoints; i++) {
			const PxVec3 &p = points[(uint32_t)indices[i]];
			mPoints[i] = PxVec3(p.dot(t0), p.dot(t1), 0.0f);
		}
	}
}
static bool sphereSphereSweep(	const PxReal ra, //radius of sphere A
								const PxVec3& A0, //previous position of sphere A
								const PxVec3& A1, //current position of sphere A
								const PxReal rb, //radius of sphere B
								const PxVec3& B0, //previous position of sphere B
								const PxVec3& B1, //current position of sphere B
								PxReal& u0, //normalized time of first collision
								PxReal& u1 //normalized time of second collision
								)
{
	const PxVec3 va = A1 - A0;
	const PxVec3 vb = B1 - B0;
	const PxVec3 AB = B0 - A0;
	const PxVec3 vab = vb - va;	// relative velocity (in normalized time)
	const PxReal rab = ra + rb;

	const PxReal a = vab.dot(vab);		//u*u coefficient
	const PxReal b = 2.0f*(vab.dot(AB));	//u coefficient

	const PxReal c = (AB.dot(AB)) - rab*rab;	//constant term

	//check if they're currently overlapping
	if(c<=0.0f || a==0.0f)
	{
		u0 = 0.0f;
		u1 = 0.0f;
		return true;
	}

	//check if they hit each other during the frame
	if(quadraticFormula(a, b, c, u0, u1))
	{
		if(u0>u1)
			Ps::swap(u0, u1);

		// u0<u1
//		if(u0<0.0f || u1>1.0f)	return false;
		if(u1<0.0f || u0>1.0f)	return false;

		return true;
	}
	return false;
}
bool Character::faceToward(const PxVec3& targetDir, PxReal angleLimitPerFrame)
{
	PxVec3 oldDir = mCharacterPose.q.rotate(PxVec3(0,0,1));
	PxVec3 up(0,1,0);
	PxVec3 newDir = PxVec3(targetDir.x, 0, targetDir.z).getNormalized();
	PxVec3 right = -1.0f * oldDir.cross(up);

	PxReal cos = newDir.dot(oldDir);
	PxReal sin = newDir.dot(right);
	PxReal angle = atan2(sin, cos);

	PxReal limit = angleLimitPerFrame * (PxPi / 180.0f);
	if (angle > limit) angle = limit;
	else if (angle < -limit) angle = -limit;

	PxQuat qdel(angle, up);

	mCharacterPose.q = qdel * mCharacterPose.q;

	return true;
}
// PT: SAT-based version, in box space
// AP: uninlining on SPU doesn't help
int Gu::triBoxSweepTestBoxSpace(const PxTriangle& tri, const PxVec3& extents, const PxVec3& dir, const PxVec3& oneOverDir, float tmax, float& toi, bool doBackfaceCulling)
{
	// Create triangle normal
	PxVec3 triNormal;
	tri.denormalizedNormal(triNormal);

	// Backface culling
	if(doBackfaceCulling && (triNormal.dot(dir)) >= 0.0f)	// ">=" is important !
		return 0;

	// The SAT test will properly detect initial overlaps, no need for extra tests
	return testSeparationAxes(tri, extents, triNormal, dir, oneOverDir, tmax, toi);
}
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
}
// PT: modified version calls the previous function, but moves the ray origin closer to the sphere. The test accuracy is
// greatly improved as a result. This is an idea proposed on the GD-Algorithms list by Eddie Edwards.
// See: http://www.codercorner.com/blog/?p=321
bool Gu::intersectRaySphere(const PxVec3& origin, const PxVec3& dir, PxReal length, const PxVec3& center, PxReal radius, PxReal& dist, PxVec3* hit_pos)
{
	const PxVec3 x = origin - center;
	PxReal l = PxSqrt(x.dot(x)) - radius - 10.0f;

//	if(l<0.0f)
//		l=0.0f;
	l = physx::intrinsics::selectMax(l, 0.0f);

	bool status = intersectRaySphereBasic(origin + l*dir, dir, length - l, center, radius, dist, hit_pos);
	if(status)
	{
//		dist += l/length;
		dist += l;
	}
	return status;
}
static PX_FORCE_INLINE Ps::IntBool planeBoxOverlap(const PxVec3& normal, PxReal d, const PxVec3& maxbox)
{
	PxVec3 vmin,vmax;

	if (normal.x>0.0f)
	{
		vmin.x = -maxbox.x;
		vmax.x = maxbox.x;
	}
	else
	{
		vmin.x = maxbox.x;
		vmax.x = -maxbox.x;
	}

	if (normal.y>0.0f)
	{
		vmin.y = -maxbox.y;
		vmax.y = maxbox.y;
	}
	else
	{
		vmin.y = maxbox.y;
		vmax.y = -maxbox.y;
	}

	if (normal.z>0.0f)
	{
		vmin.z = -maxbox.z;
		vmax.z = maxbox.z;
	}
	else
	{
		vmin.z = maxbox.z;
		vmax.z = -maxbox.z;
	}

	if( normal.dot(vmin) + d >  0.0f) return Ps::IntFalse;
	if( normal.dot(vmax) + d >= 0.0f) return Ps::IntTrue;
	return Ps::IntFalse;
}
// AP: function version to reduce SPU code size
void sweepCapsuleTrianglesOutputTri2(
	const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, const PxVec3& d, PxTriangle* extrudedTris,
	PxU32& nbExtrudedTris, PxVec3* extrudedTrisNormals
	)
{
	PxTriangle& t = extrudedTris[nbExtrudedTris];
	t.verts[0] = p0;
	t.verts[1] = p1;
	t.verts[2] = p2;
	PxVec3 nrm;
	t.denormalizedNormal(nrm);
	if(nrm.dot(d)>0.0f)
	{
		PxVec3 tmp = t.verts[1];
		t.verts[1] = t.verts[2];
		t.verts[2] = tmp;
		nrm = -nrm;
	}
	extrudedTrisNormals[nbExtrudedTris] = nrm;
	nbExtrudedTris++;
}
/**
 @brief generate triangle index buffer
 @date 2014-01-30
*/
void SampleRenderer::GenerateTriangleFrom3Vector( void *positions, void *normals, PxU32 stride, 
	const PxVec3 &center, const vector<PxU16> &triangle, OUT vector<PxU16> &outTriangle )
{
	const PxVec3 p0 = *(PxVec3*)(((PxU8*)positions) + (stride * triangle[ 0]));
	const PxVec3 p1 = *(PxVec3*)(((PxU8*)positions) + (stride * triangle[ 1]));
	const PxVec3 p2 = *(PxVec3*)(((PxU8*)positions) + (stride * triangle[ 2]));

	PxVec3 faceCenter = (p0 + p1 + p2) / 3.f;
	PxVec3 n = faceCenter - center;
	n.normalize();

	// ccw check
	//
	//            p0
	//          /   \
	//        /        \
	//    p2  ------  p1
	//

	PxVec3 v10 = p1 - p0;
	v10.normalize();
	PxVec3 v20 = p2 - p0;
	v20.normalize();

	PxVec3 crossV = v10.cross(v20);
	crossV.normalize();

	if (n.dot(crossV) >= 0)
	{
		outTriangle.push_back( triangle[ 0] );
		outTriangle.push_back( triangle[ 2] );
		outTriangle.push_back( triangle[ 1] );
	}
	else
	{
		outTriangle.push_back( triangle[ 0] );
		outTriangle.push_back( triangle[ 1] );
		outTriangle.push_back( triangle[ 2] );
	}
}
static PxQuat rotationArc(const PxVec3& v0, const PxVec3& v1, bool& res)
{
	PxVec3 _v0 = v0;
	PxVec3 _v1 = v1;
	_v0.normalize();
	_v1.normalize();

	float s = sqrtf((1.0f + (v0.dot(v1))) * 2.0f);
	if(s<0.001f)
	{
		res = false;
		return PxQuat::createIdentity();
	}

	PxVec3 p = (_v0.cross(_v1)) / s;
	float w = s * 0.5f;
	PxQuat q(p.x, p.y, p.z, w);
	q.normalize();

	res = true;
	return q;
}
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;
			}
		}
	}
}
void SampleVehicle_CameraController::update(const PxReal dtime, const PxRigidDynamic* actor, PxScene& scene)
{
	PxTransform carChassisTransfm;
	if(mLockOnFocusVehTransform)
	{
		carChassisTransfm = actor->getGlobalPose();
		mLastFocusVehTransform = carChassisTransfm;
	}
	else
	{
		carChassisTransfm = mLastFocusVehTransform;
	}

	PxF32 camDist = 15.0f;
	PxF32 cameraYRotExtra = 0.0f;

	PxVec3 velocity = mLastCarPos-carChassisTransfm.p;

	if (mCameraInit)
	{
		//Work out the forward and sideways directions.
		PxVec3 unitZ(0,0,1);
		PxVec3 carDirection = carChassisTransfm.q.rotate(unitZ);
		PxVec3 unitX(1,0,0);
		PxVec3 carSideDirection = carChassisTransfm.q.rotate(unitX);

		//Acceleration (note that is not scaled by time).
		PxVec3 acclVec = mLastCarVelocity-velocity;

		//Higher forward accelerations allow the car to speed away from the camera.
		PxF32 acclZ = carDirection.dot(acclVec);
		camDist = PxMax(camDist+acclZ*400.0f, 5.0f);

		//Higher sideways accelerations allow the car's rotation to speed away from the camera's rotation.
		PxF32 acclX = carSideDirection.dot(acclVec);
		cameraYRotExtra = -acclX*10.0f;
		//At very small sideways speeds the camera greatly amplifies any numeric error in the body and leads to a slight jitter.
		//Scale cameraYRotExtra by a value in range (0,1) for side speeds in range (0.1,1.0) and by zero for side speeds less than 0.1.
		PxFixedSizeLookupTable<4> table;
		table.addPair(0.0f, 0.0f);
		table.addPair(0.1f*dtime, 0);
		table.addPair(1.0f*dtime, 1);
		PxF32 velX = carSideDirection.dot(velocity);
		cameraYRotExtra *= table.getYVal(PxAbs(velX));
	}

	mCameraRotateAngleY+=mRotateInputY*mMaxCameraRotateSpeed*dtime;
	mCameraRotateAngleY=physx::intrinsics::fsel(mCameraRotateAngleY-10*PxPi, mCameraRotateAngleY-10*PxPi, physx::intrinsics::fsel(-mCameraRotateAngleY-10*PxPi, mCameraRotateAngleY + 10*PxPi, mCameraRotateAngleY));
	mCameraRotateAngleZ+=mRotateInputZ*mMaxCameraRotateSpeed*dtime;
	mCameraRotateAngleZ=PxClamp(mCameraRotateAngleZ,-PxPi*0.05f,PxPi*0.45f);

	PxVec3 cameraDir=PxVec3(0,0,1)*PxCos(mCameraRotateAngleY+cameraYRotExtra) + PxVec3(1,0,0)*PxSin(mCameraRotateAngleY+cameraYRotExtra);

	cameraDir=cameraDir*PxCos(mCameraRotateAngleZ)-PxVec3(0,1,0)*PxSin(mCameraRotateAngleZ);

	const PxVec3 direction = carChassisTransfm.q.rotate(cameraDir);
	PxVec3 target = carChassisTransfm.p;
	target.y+=0.5f;

	PxRaycastHit hit;
	PxSceneQueryFilterData filterData(PxSceneQueryFilterFlag::eSTATIC);
	scene.raycastSingle(target, -direction, camDist, PxSceneQueryFlag::eDISTANCE, hit, filterData, NULL, NULL);
	if (hit.shape != NULL)
	{
		camDist = hit.distance-0.25f;
	}

	camDist = PxMax(5.0f, PxMin(camDist, 50.0f));

	PxVec3 position = target-direction*camDist;

	if (mCameraInit)
	{
		dampVec3(mCameraPos, position, dtime);
		dampVec3(mCameraTargetPos, target, dtime);
	}

	mCameraPos = position;
	mCameraTargetPos = target;
	mCameraInit = true;

	mLastCarVelocity = velocity;
	mLastCarPos = carChassisTransfm.p;
}
//! Compute the smallest distance from the (infinite) line to the box.
static PxReal distanceLineBoxSquared(const PxVec3& lineOrigin, const PxVec3& lineDirection,
								  const PxVec3& boxOrigin, const PxVec3& boxExtent, const PxMat33& boxBase,
								  PxReal* lineParam,
								  PxVec3* boxParam)
{
	const PxVec3& axis0 = boxBase.column0;
	const PxVec3& axis1 = boxBase.column1;
	const PxVec3& axis2 = boxBase.column2;
	
	// compute coordinates of line in box coordinate system
	const PxVec3 diff = lineOrigin - boxOrigin;
	PxVec3 pnt(diff.dot(axis0), diff.dot(axis1), diff.dot(axis2));
	PxVec3 dir(lineDirection.dot(axis0), lineDirection.dot(axis1), lineDirection.dot(axis2));

	// Apply reflections so that direction vector has nonnegative components.
	bool reflect[3];
	for(unsigned int i=0;i<3;i++)
	{
		if(dir[i]<0.0f)
		{
			pnt[i] = -pnt[i];
			dir[i] = -dir[i];
			reflect[i] = true;
		}
		else
		{
			reflect[i] = false;
		}
	}

	PxReal sqrDistance = 0.0f;

	if(dir.x>0.0f)
	{
		if(dir.y>0.0f)
		{
			if(dir.z>0.0f)	caseNoZeros(pnt, dir, boxExtent, lineParam, sqrDistance);		// (+,+,+)
			else			case0(0, 1, 2, pnt, dir, boxExtent, lineParam, sqrDistance);	// (+,+,0)
		}
		else
		{
			if(dir.z>0.0f)	case0(0, 2, 1, pnt, dir, boxExtent, lineParam, sqrDistance);	// (+,0,+)
			else			case00(0, 1, 2, pnt, dir, boxExtent, lineParam, sqrDistance);	// (+,0,0)
		}
	}
	else
	{
		if(dir.y>0.0f)
		{
			if(dir.z>0.0f)	case0(1, 2, 0, pnt, dir, boxExtent, lineParam, sqrDistance);	// (0,+,+)
			else			case00(1, 0, 2, pnt, dir, boxExtent, lineParam, sqrDistance);	// (0,+,0)
		}
		else
		{
			if(dir.z>0.0f)	case00(2, 0, 1, pnt, dir, boxExtent, lineParam, sqrDistance);	// (0,0,+)
			else
			{
				case000(pnt, boxExtent, sqrDistance);										// (0,0,0)
				if(lineParam)
					*lineParam = 0.0f;
			}
		}
	}

	if(boxParam)
	{
		// undo reflections
		for(unsigned int i=0;i<3;i++)
		{
			if(reflect[i])
				pnt[i] = -pnt[i];
		}

		*boxParam = pnt;
	}

	return sqrDistance;
}
Example #19
0
bool Gu::intersectOBBOBB(const PxVec3& e0, const PxVec3& c0, const PxMat33& r0, const PxVec3& e1, const PxVec3& c1, const PxMat33& r1, bool full_test)
{
	// Translation, in parent frame
	const PxVec3 v = c1 - c0;
	// Translation, in A's frame
	const PxVec3 T(v.dot(r0[0]), v.dot(r0[1]), v.dot(r0[2]));

	// B's basis with respect to A's local frame
	PxReal R[3][3];
	PxReal FR[3][3];
	PxReal ra, rb, t;

	// Calculate rotation matrix
	for(PxU32 i=0;i<3;i++)
	{
		for(PxU32 k=0;k<3;k++)
		{
			R[i][k] = r0[i].dot(r1[k]);
			FR[i][k] = 1e-6f + PxAbs(R[i][k]);	// Precompute fabs matrix
		}
	}

	// A's basis vectors
	for(PxU32 i=0;i<3;i++)
	{
		ra = e0[i];

		rb = e1[0]*FR[i][0] + e1[1]*FR[i][1] + e1[2]*FR[i][2];

		t = PxAbs(T[i]);

		if(t > ra + rb)		return false;
	}

	// B's basis vectors
	for(PxU32 k=0;k<3;k++)
	{
		ra = e0[0]*FR[0][k] + e0[1]*FR[1][k] + e0[2]*FR[2][k];

		rb = e1[k];

		t = PxAbs(T[0]*R[0][k] + T[1]*R[1][k] + T[2]*R[2][k]);

		if( t > ra + rb )	return false;
	}

	if(full_test)
	{
		//9 cross products

		//L = A0 x B0
		ra	= e0[1]*FR[2][0] + e0[2]*FR[1][0];
		rb	= e1[1]*FR[0][2] + e1[2]*FR[0][1];
		t	= PxAbs(T[2]*R[1][0] - T[1]*R[2][0]);
		if(t > ra + rb)	return false;

		//L = A0 x B1
		ra	= e0[1]*FR[2][1] + e0[2]*FR[1][1];
		rb	= e1[0]*FR[0][2] + e1[2]*FR[0][0];
		t	= PxAbs(T[2]*R[1][1] - T[1]*R[2][1]);
		if(t > ra + rb)	return false;

		//L = A0 x B2
		ra	= e0[1]*FR[2][2] + e0[2]*FR[1][2];
		rb	= e1[0]*FR[0][1] + e1[1]*FR[0][0];
		t	= PxAbs(T[2]*R[1][2] - T[1]*R[2][2]);
		if(t > ra + rb)	return false;

		//L = A1 x B0
		ra	= e0[0]*FR[2][0] + e0[2]*FR[0][0];
		rb	= e1[1]*FR[1][2] + e1[2]*FR[1][1];
		t	= PxAbs(T[0]*R[2][0] - T[2]*R[0][0]);
		if(t > ra + rb)	return false;

		//L = A1 x B1
		ra	= e0[0]*FR[2][1] + e0[2]*FR[0][1];
		rb	= e1[0]*FR[1][2] + e1[2]*FR[1][0];
		t	= PxAbs(T[0]*R[2][1] - T[2]*R[0][1]);
		if(t > ra + rb)	return false;

		//L = A1 x B2
		ra	= e0[0]*FR[2][2] + e0[2]*FR[0][2];
		rb	= e1[0]*FR[1][1] + e1[1]*FR[1][0];
		t	= PxAbs(T[0]*R[2][2] - T[2]*R[0][2]);
		if(t > ra + rb)	return false;

		//L = A2 x B0
		ra	= e0[0]*FR[1][0] + e0[1]*FR[0][0];
		rb	= e1[1]*FR[2][2] + e1[2]*FR[2][1];
		t	= PxAbs(T[1]*R[0][0] - T[0]*R[1][0]);
		if(t > ra + rb)	return false;

		//L = A2 x B1
		ra	= e0[0]*FR[1][1] + e0[1]*FR[0][1];
		rb	= e1[0] *FR[2][2] + e1[2]*FR[2][0];
		t	= PxAbs(T[1]*R[0][1] - T[0]*R[1][1]);
		if(t > ra + rb)	return false;

		//L = A2 x B2
		ra	= e0[0]*FR[1][2] + e0[1]*FR[0][2];
		rb	= e1[0]*FR[2][1] + e1[1]*FR[2][0];
		t	= PxAbs(T[1]*R[0][2] - T[0]*R[1][2]);
		if(t > ra + rb)	return false;
	}
	return true;
}
bool setupFinalizeExtSolverContactsCoulomb(
						    const ContactBuffer& buffer,
							const CorrelationBuffer& c,
							const PxTransform& bodyFrame0,
							const PxTransform& bodyFrame1,
							PxU8* workspace,
							PxReal invDt,
							PxReal bounceThresholdF32,
							const SolverExtBody& b0,
							const SolverExtBody& b1,
							PxU32 frictionCountPerPoint,
							PxReal invMassScale0, PxReal invInertiaScale0, 
							PxReal invMassScale1, PxReal invInertiaScale1,
							PxReal restDist,
							PxReal ccdMaxDistance)	
{
	// NOTE II: the friction patches are sparse (some of them have no contact patches, and
	// therefore did not get written back to the cache) but the patch addresses are dense,
	// corresponding to valid patches

	const FloatV ccdMaxSeparation = FLoad(ccdMaxDistance);

	PxU8* PX_RESTRICT ptr = workspace;

	//KS - TODO - this should all be done in SIMD to avoid LHS
	const PxF32 maxPenBias0 = b0.mLinkIndex == PxSolverConstraintDesc::NO_LINK ? b0.mBodyData->penBiasClamp : getMaxPenBias(*b0.mFsData)[b0.mLinkIndex];
	const PxF32 maxPenBias1 = b1.mLinkIndex == PxSolverConstraintDesc::NO_LINK ? b1.mBodyData->penBiasClamp : getMaxPenBias(*b1.mFsData)[b1.mLinkIndex];

	const FloatV maxPenBias = FLoad(PxMax(maxPenBias0, maxPenBias1)/invDt);

	const FloatV restDistance = FLoad(restDist); 
	const FloatV bounceThreshold = FLoad(bounceThresholdF32);

	const FloatV invDtV = FLoad(invDt);
	const FloatV pt8 = FLoad(0.8f);

	const FloatV invDtp8 = FMul(invDtV, pt8);

	Ps::prefetchLine(c.contactID);
	Ps::prefetchLine(c.contactID, 128);

	const PxU32 frictionPatchCount = c.frictionPatchCount;

	const PxU32 pointStride = sizeof(SolverContactPointExt);
	const PxU32 frictionStride = sizeof(SolverContactFrictionExt);
	const PxU8 pointHeaderType = DY_SC_TYPE_EXT_CONTACT;
	const PxU8 frictionHeaderType = DY_SC_TYPE_EXT_FRICTION;

	PxReal d0 = invMassScale0;
	PxReal d1 = invMassScale1;
	PxReal angD0 = invInertiaScale0;
	PxReal angD1 = invInertiaScale1;

	PxU8 flags = 0;

	for(PxU32 i=0;i< frictionPatchCount;i++)
	{
		const PxU32 contactCount = c.frictionPatchContactCounts[i];
		if(contactCount == 0)
			continue;

		const Gu::ContactPoint* contactBase0 = buffer.contacts + c.contactPatches[c.correlationListHeads[i]].start;

		const Vec3V normalV = Ps::aos::V3LoadA(contactBase0->normal);
		const Vec3V normal = V3LoadA(contactBase0->normal);

		const PxReal combinedRestitution = contactBase0->restitution;
	
		
		SolverContactCoulombHeader* PX_RESTRICT header = reinterpret_cast<SolverContactCoulombHeader*>(ptr);
		ptr += sizeof(SolverContactCoulombHeader);

		Ps::prefetchLine(ptr, 128);
		Ps::prefetchLine(ptr, 256);
		Ps::prefetchLine(ptr, 384);

		const FloatV restitution = FLoad(combinedRestitution);


		header->numNormalConstr		= PxU8(contactCount);
		header->type				= pointHeaderType;
		//header->setRestitution(combinedRestitution);

		header->setDominance0(d0);
		header->setDominance1(d1);
		header->angDom0 = angD0;
		header->angDom1 = angD1;
		header->flags = flags;
		
		header->setNormal(normalV);
		
		for(PxU32 patch=c.correlationListHeads[i]; 
			patch!=CorrelationBuffer::LIST_END; 
			patch = c.contactPatches[patch].next)
		{
			const PxU32 count = c.contactPatches[patch].count;
			const Gu::ContactPoint* contactBase = buffer.contacts + c.contactPatches[patch].start;
				
			PxU8* p = ptr;
			for(PxU32 j=0;j<count;j++)
			{
				const Gu::ContactPoint& contact = contactBase[j];

				SolverContactPointExt* PX_RESTRICT solverContact = reinterpret_cast<SolverContactPointExt*>(p);
				p += pointStride;

				setupExtSolverContact(b0, b1, d0, d1, angD0, angD1, bodyFrame0, bodyFrame1, normal, invDtV, invDtp8, restDistance, maxPenBias, restitution,
					bounceThreshold, contact, *solverContact, ccdMaxSeparation);
			}			
			ptr = p;
		}
	}

	//construct all the frictions

	PxU8* PX_RESTRICT ptr2 = workspace;

	const PxF32 orthoThreshold = 0.70710678f;
	const PxF32 eps = 0.00001f;
	bool hasFriction = false;

	for(PxU32 i=0;i< frictionPatchCount;i++)
	{
		const PxU32 contactCount = c.frictionPatchContactCounts[i];
		if(contactCount == 0)
			continue;

		SolverContactCoulombHeader* header = reinterpret_cast<SolverContactCoulombHeader*>(ptr2); 
		header->frictionOffset = PxU16(ptr - ptr2);
		ptr2 += sizeof(SolverContactCoulombHeader) + header->numNormalConstr * pointStride;

		const Gu::ContactPoint* contactBase0 = buffer.contacts + c.contactPatches[c.correlationListHeads[i]].start;

		PxVec3 normal = contactBase0->normal;

		const PxReal staticFriction = contactBase0->staticFriction;
		const bool disableStrongFriction = !!(contactBase0->materialFlags & PxMaterialFlag::eDISABLE_FRICTION);
		const bool haveFriction = (disableStrongFriction == 0);
	
		SolverFrictionHeader* frictionHeader = reinterpret_cast<SolverFrictionHeader*>(ptr);
		frictionHeader->numNormalConstr = Ps::to8(c.frictionPatchContactCounts[i]);
		frictionHeader->numFrictionConstr = Ps::to8(haveFriction ? c.frictionPatchContactCounts[i] * frictionCountPerPoint : 0);
		frictionHeader->flags = flags;
		ptr += sizeof(SolverFrictionHeader);
		PxF32* forceBuffer = reinterpret_cast<PxF32*>(ptr);
		ptr += frictionHeader->getAppliedForcePaddingSize(c.frictionPatchContactCounts[i]);
		PxMemZero(forceBuffer, sizeof(PxF32) * c.frictionPatchContactCounts[i]);
		Ps::prefetchLine(ptr, 128);
		Ps::prefetchLine(ptr, 256);
		Ps::prefetchLine(ptr, 384);


		const PxVec3 t0Fallback1(0.f, -normal.z, normal.y);
		const PxVec3 t0Fallback2(-normal.y, normal.x, 0.f) ;
		const PxVec3 tFallback1 = orthoThreshold > PxAbs(normal.x) ? t0Fallback1 : t0Fallback2;
		const PxVec3 vrel = b0.getLinVel() - b1.getLinVel();
		const PxVec3 t0_ = vrel - normal * (normal.dot(vrel));
		const PxReal sqDist = t0_.dot(t0_);
		const PxVec3 tDir0 = (sqDist > eps ? t0_: tFallback1).getNormalized();
		const PxVec3 tDir1 = tDir0.cross(normal);
		PxVec3 tFallback[2] = {tDir0, tDir1};

		PxU32 ind = 0;

		if(haveFriction)
		{
			hasFriction = true;
			frictionHeader->setStaticFriction(staticFriction);
			frictionHeader->invMass0D0 = d0;
			frictionHeader->invMass1D1 = d1;
			frictionHeader->angDom0 = angD0;
			frictionHeader->angDom1 = angD1;
			frictionHeader->type			= frictionHeaderType;
			
			PxU32 totalPatchContactCount = 0;
		
			for(PxU32 patch=c.correlationListHeads[i]; 
				patch!=CorrelationBuffer::LIST_END; 
				patch = c.contactPatches[patch].next)
			{
				const PxU32 count = c.contactPatches[patch].count;
				const PxU32 start = c.contactPatches[patch].start;
				const Gu::ContactPoint* contactBase = buffer.contacts + start;
					
				PxU8* p = ptr;

				for(PxU32 j =0; j < count; j++)
				{
					const Gu::ContactPoint& contact = contactBase[j];
					const PxVec3 ra = contact.point - bodyFrame0.p;
					const PxVec3 rb = contact.point - bodyFrame1.p;
						
					const PxVec3 targetVel = contact.targetVel;
					const PxVec3 pVRa = b0.getLinVel() + b0.getAngVel().cross(ra);
					const PxVec3 pVRb = b1.getLinVel() + b1.getAngVel().cross(rb);
					//const PxVec3 vrel = pVRa - pVRb;

					for(PxU32 k = 0; k < frictionCountPerPoint; ++k)
					{
						SolverContactFrictionExt* PX_RESTRICT f0 = reinterpret_cast<SolverContactFrictionExt*>(p);
						p += frictionStride;

						PxVec3 t0 = tFallback[ind];
						ind = 1 - ind;
						PxVec3 raXn = ra.cross(t0); 
						PxVec3 rbXn = rb.cross(t0); 
						Cm::SpatialVector deltaV0, deltaV1;

						const Cm::SpatialVector resp0 = createImpulseResponseVector(t0, raXn, b0);
						const Cm::SpatialVector resp1 = createImpulseResponseVector(-t0, -rbXn, b1);

						PxReal unitResponse = getImpulseResponse(b0, resp0, deltaV0, d0, angD0,
																 b1, resp1, deltaV1, d1, angD1);

						PxReal tv = targetVel.dot(t0);
						if(b0.mLinkIndex == PxSolverConstraintDesc::NO_LINK)
							tv += pVRa.dot(t0);
						else if(b1.mLinkIndex == PxSolverConstraintDesc::NO_LINK)
							tv -= pVRb.dot(t0);


						f0->setVelMultiplier(FLoad(unitResponse>0.0f ? 1.f/unitResponse : 0.0f));
						f0->setRaXn(resp0.angular);
						f0->setRbXn(-resp1.angular);
						f0->targetVel = tv;
						f0->setNormal(t0);
						f0->setAppliedForce(0.0f);
						f0->linDeltaVA = V3LoadA(deltaV0.linear);
						f0->angDeltaVA = V3LoadA(deltaV0.angular);
						f0->linDeltaVB = V3LoadA(deltaV1.linear);
						f0->angDeltaVB = V3LoadA(deltaV1.angular);
					}					
				}

				totalPatchContactCount += c.contactPatches[patch].count;
				
				ptr = p;	
			}
		}
	}
	//PX_ASSERT(ptr - workspace == n.solverConstraintSize);
	return hasFriction;
}
// this code is shared between capsule vertices and sphere
bool GuContactSphereHeightFieldShared(GU_CONTACT_METHOD_ARGS, bool isCapsule)
{
#if 1
	PX_UNUSED(cache);
	PX_UNUSED(renderOutput);

	// Get actual shape data
	const PxSphereGeometry& shapeSphere = shape0.get<const PxSphereGeometry>();
	const PxHeightFieldGeometryLL& hfGeom = shape1.get<const PxHeightFieldGeometryLL>();

	const Gu::HeightField& hf = *static_cast<Gu::HeightField*>(hfGeom.heightField);
	const Gu::HeightFieldUtil hfUtil(hfGeom, hf);

	const PxReal radius = shapeSphere.radius;
	const PxReal eps = PxReal(0.1) * radius;

	const PxVec3 sphereInHfShape = transform1.transformInv(transform0.p);

	PX_ASSERT(isCapsule || contactBuffer.count==0);

	const PxReal oneOverRowScale = hfUtil.getOneOverRowScale();
	const PxReal oneOverColumnScale = hfUtil.getOneOverColumnScale();

	// check if the sphere is below the HF surface
	if (hfUtil.isShapePointOnHeightField(sphereInHfShape.x, sphereInHfShape.z))
	{

		PxReal fracX, fracZ;
		const PxU32 vertexIndex = hfUtil.getHeightField().computeCellCoordinates(sphereInHfShape.x * oneOverRowScale, sphereInHfShape.z * oneOverColumnScale, fracX, fracZ);

		// The sphere origin projects within the bounds of the heightfield in the X-Z plane
//		const PxReal sampleHeight = hfShape.getHeightAtShapePoint(sphereInHfShape.x, sphereInHfShape.z);
		const PxReal sampleHeight = hfUtil.getHeightAtShapePoint2(vertexIndex, fracX, fracZ);
		
		const PxReal deltaHeight = sphereInHfShape.y - sampleHeight;
		//if (hfShape.isDeltaHeightInsideExtent(deltaHeight, eps))
		if (hf.isDeltaHeightInsideExtent(deltaHeight, eps))
		{
			// The sphere origin is 'below' the heightfield surface
			// Actually there is an epsilon involved to make sure the
			// 'above' surface calculations can deliver a good normal
			const PxU32 faceIndex = hfUtil.getFaceIndexAtShapePointNoTest2(vertexIndex, fracX, fracZ);
			if (faceIndex != 0xffffffff)
			{

				//hfShape.getAbsPoseFast().M.getColumn(1, hfShapeUp);
				const PxVec3 hfShapeUp = transform1.q.getBasisVector1();

				if (hf.getThicknessFast() <= 0)
					contactBuffer.contact(transform0.p,  hfShapeUp,  deltaHeight-radius, faceIndex);
				else
					contactBuffer.contact(transform0.p, -hfShapeUp, -deltaHeight-radius, faceIndex);

				return true;
			}

			return false;
		}

	}

	const PxReal epsSqr = eps * eps;

	const PxReal inflatedRadius = radius + params.mContactDistance;
	const PxReal inflatedRadiusSquared = inflatedRadius * inflatedRadius;

	const PxVec3 sphereInHF = hfUtil.shape2hfp(sphereInHfShape);

	const PxReal radiusOverRowScale = inflatedRadius * PxAbs(oneOverRowScale);
	const PxReal radiusOverColumnScale = inflatedRadius * PxAbs(oneOverColumnScale);

	const PxU32 minRow = hf.getMinRow(sphereInHF.x - radiusOverRowScale);
	const PxU32 maxRow = hf.getMaxRow(sphereInHF.x + radiusOverRowScale);
	const PxU32 minColumn = hf.getMinColumn(sphereInHF.z - radiusOverColumnScale);
	const PxU32 maxColumn = hf.getMaxColumn(sphereInHF.z + radiusOverColumnScale);

	// this assert is here because the following code depends on it for reasonable performance for high-count situations
	PX_COMPILE_TIME_ASSERT(ContactBuffer::MAX_CONTACTS == 64);

	const PxU32 nbColumns = hf.getNbColumnsFast();

#define HFU Gu::HeightFieldUtil
	PxU32 numFaceContacts = 0;
	for (PxU32 i = 0; i<2; i++)
	{
		const bool facesOnly = (i == 0);
		// first we go over faces-only meaning only contacts directly in Voronoi regions of faces
		// at second pass we consider edges and vertices and clamp the normals to adjacent feature's normal
		// if there was a prior contact. it is equivalent to clipping the normal to it's feature's Voronoi region

		for (PxU32 r = minRow; r < maxRow; r++) 
		{
			for (PxU32 c = minColumn; c < maxColumn; c++) 
			{

				// x--x--x
				// | x   |
				// x  x  x
				// |   x |
				// x--x--x
				PxVec3 closestPoints[11];
				PxU32 closestFeatures[11];
				PxU32 npcp = hfUtil.findClosestPointsOnCell(
					r, c, sphereInHfShape, closestPoints, closestFeatures, facesOnly, !facesOnly, true);

				for(PxU32 pi = 0; pi < npcp; pi++)
				{
					PX_ASSERT(closestFeatures[pi] != 0xffffffff);
					const PxVec3 d = sphereInHfShape - closestPoints[pi];

					if (hf.isDeltaHeightOppositeExtent(d.y)) // See if we are 'above' the heightfield
					{
						const PxReal dMagSq = d.magnitudeSquared();

						if (dMagSq > inflatedRadiusSquared) 
							// Too far above
							continue;

						PxReal dMag = -1.0f; // dMag is sqrt(sMadSq) and comes up as a byproduct of other calculations in computePointNormal
						PxVec3 n; // n is in world space, rotated by transform1
						PxU32 featureType = HFU::getFeatureType(closestFeatures[pi]);
						if (featureType == HFU::eEDGE)
						{
							PxU32 edgeIndex = HFU::getFeatureIndex(closestFeatures[pi]);
							PxU32 adjFaceIndices[2];
							const PxU32 adjFaceCount = hf.getEdgeTriangleIndices(edgeIndex, adjFaceIndices);
							PxVec3 origin;
							PxVec3 direction;
							const PxU32 vertexIndex = edgeIndex / 3;
							const PxU32 row			= vertexIndex / nbColumns;
							const PxU32 col			= vertexIndex % nbColumns;
							hfUtil.getEdge(edgeIndex, vertexIndex, row, col, origin, direction);
							n = hfUtil.computePointNormal(
									hfGeom.heightFieldFlags, d, transform1, dMagSq,
									closestPoints[pi].x, closestPoints[pi].z, epsSqr, dMag);
							PxVec3 localN = transform1.rotateInv(n);
							// clamp the edge's normal to its Voronoi region
							for (PxU32 j = 0; j < adjFaceCount; j++)
							{
								const PxVec3 adjNormal = hfUtil.hf2shapen(hf.getTriangleNormalInternal(adjFaceIndices[j])).getNormalized();
								PxU32 triCell = adjFaceIndices[j] >> 1;
								PxU32 triRow = triCell/hf.getNbColumnsFast();
								PxU32 triCol = triCell%hf.getNbColumnsFast();
								PxVec3 tv0, tv1, tv2, tvc;
								hf.getTriangleVertices(adjFaceIndices[j], triRow, triCol, tv0, tv1, tv2);
								tvc = hfUtil.hf2shapep((tv0+tv1+tv2)/3.0f); // compute adjacent triangle center
								PxVec3 perp = adjNormal.cross(direction).getNormalized(); // adj face normal cross edge dir
								if (perp.dot(tvc-origin) < 0.0f) // make sure perp is pointing toward the center of the triangle
									perp = -perp;
								// perp is now a vector sticking out of the edge of the triangle (also the test edge) pointing toward the center
								// perpendicular to the normal (in triangle plane)
								if (perp.dot(localN) > 0.0f) // if the normal is in perp halfspace, clamp it to Voronoi region
								{
									n = transform1.rotate(adjNormal);
									break;
								}
							}
						} else if(featureType == HFU::eVERTEX)
						{
							// AP: these contacts are rare so hopefully it's ok
							const PxU32 bufferCount = contactBuffer.count;
							const PxU32 vertIndex = HFU::getFeatureIndex(closestFeatures[pi]);
							EdgeData adjEdges[8];
							const PxU32 row = vertIndex / nbColumns;
							const PxU32 col = vertIndex % nbColumns;
							const PxU32 numAdjEdges = ::getVertexEdgeIndices(hf, vertIndex, row, col, adjEdges);
							for (PxU32 iPrevEdgeContact = numFaceContacts; iPrevEdgeContact < bufferCount; iPrevEdgeContact++)
							{
								if (contactBuffer.contacts[iPrevEdgeContact].forInternalUse != HFU::eEDGE)
									continue; // skip non-edge contacts (can be other vertex contacts)

								for (PxU32 iAdjEdge = 0; iAdjEdge < numAdjEdges; iAdjEdge++)
									// does adjacent edge index for this vertex match a previously encountered edge index?
									if (adjEdges[iAdjEdge].edgeIndex == contactBuffer.contacts[iPrevEdgeContact].internalFaceIndex1)
									{
										// if so, clamp the normal for this vertex to that edge's normal
										n = contactBuffer.contacts[iPrevEdgeContact].normal;
										dMag = PxSqrt(dMagSq);
										break;
									}
							}
						}

						if (dMag == -1.0f)
							n = hfUtil.computePointNormal(hfGeom.heightFieldFlags, d, transform1,
								dMagSq, closestPoints[pi].x, closestPoints[pi].z, epsSqr, dMag);

						PxVec3 p = transform0.p - n * radius;
							#if DEBUG_RENDER_HFCONTACTS
							printf("n=%.5f %.5f %.5f;  ", n.x, n.y, n.z);
							if (n.y < 0.8f)
								int a = 1;
							PxScene *s; PxGetPhysics().getScenes(&s, 1, 0);
							Cm::RenderOutput((Cm::RenderBuffer&)s->getRenderBuffer()) << Cm::RenderOutput::LINES << PxDebugColor::eARGB_BLUE // red
								<< p << (p + n * 10.0f);
							#endif

						// temporarily use the internalFaceIndex0 slot in the contact buffer for featureType
	 					contactBuffer.contact(
							p, n, dMag - radius, PxU16(featureType), HFU::getFeatureIndex(closestFeatures[pi]));	
					}
				}
			}
		}
Example #22
0
PxVec3 RandomOrthogonalVector(PxVec3 normal)
{
    PxVec3 random = CreateRandomVector(10);

    return random - (normal * random.dot(normal));
}
static PX_FORCE_INLINE int testSeparationAxes(	const PxTriangle& tri, const PxVec3& extents,
												const PxVec3& normal, const PxVec3& dir, const PxVec3& oneOverDir, float tmax, float& tcoll)
{
#ifdef _XBOX
	float bValidMTD = 1.0f;
#else
	bool bValidMTD = true;
#endif
	tcoll = tmax;
	float tfirst = -FLT_MAX;
	float tlast  = FLT_MAX;

	// Triangle normal
#ifdef _XBOX
	if(testAxis(tri, extents, dir, normal, bValidMTD, tfirst, tlast)==0.0f)
#else
	if(!testAxis(tri, extents, dir, normal, bValidMTD, tfirst, tlast))
#endif
		return 0;

	// Box normals
#ifdef _XBOX
	if(testAxisXYZ(tri, extents, dir, oneOverDir.x, bValidMTD, tfirst, tlast, 0)==0.0f)
		return 0;
	if(testAxisXYZ(tri, extents, dir, oneOverDir.y, bValidMTD, tfirst, tlast, 1)==0.0f)
		return 0;
	if(testAxisXYZ(tri, extents, dir, oneOverDir.z, bValidMTD, tfirst, tlast, 2)==0.0f)
		return 0;
#else
	if(!testAxisXYZ(tri, extents, dir, oneOverDir.x, bValidMTD, tfirst, tlast, 0))
		return 0;
	if(!testAxisXYZ(tri, extents, dir, oneOverDir.y, bValidMTD, tfirst, tlast, 1))
		return 0;
	if(!testAxisXYZ(tri, extents, dir, oneOverDir.z, bValidMTD, tfirst, tlast, 2))
		return 0;
#endif
	// Edges
	for(PxU32 i=0; i<3; i++)
	{
		int ip1 = int(i+1);
		if(i>=2)	ip1 = 0;
		const PxVec3 TriEdge = tri.verts[ip1] - tri.verts[i];

#ifdef _XBOX
		{
			const PxVec3 Sep = Ps::cross100(TriEdge);
			if((Sep.dot(Sep))>=1.0E-6f && testAxis(tri, extents, dir, Sep, bValidMTD, tfirst, tlast)==0.0f)
				return 0;
		}
		{
			const PxVec3 Sep = Ps::cross010(TriEdge);
			if((Sep.dot(Sep))>=1.0E-6f && testAxis(tri, extents, dir, Sep, bValidMTD, tfirst, tlast)==0.0f)
				return 0;
		}
		{
			const PxVec3 Sep = Ps::cross001(TriEdge);
			if((Sep.dot(Sep))>=1.0E-6f && testAxis(tri, extents, dir, Sep, bValidMTD, tfirst, tlast)==0.0f)
				return 0;
		}
#else
		{
			const PxVec3 Sep = Ps::cross100(TriEdge);
			if((Sep.dot(Sep))>=1.0E-6f && !testAxis(tri, extents, dir, Sep, bValidMTD, tfirst, tlast))
				return 0;
		}
		{
			const PxVec3 Sep = Ps::cross010(TriEdge);
			if((Sep.dot(Sep))>=1.0E-6f && !testAxis(tri, extents, dir, Sep, bValidMTD, tfirst, tlast))
				return 0;
		}
		{
			const PxVec3 Sep = Ps::cross001(TriEdge);
			if((Sep.dot(Sep))>=1.0E-6f && !testAxis(tri, extents, dir, Sep, bValidMTD, tfirst, tlast))
				return 0;
		}
#endif
	}

	if(tfirst > tmax || tlast < 0.0f)	return 0;
	if(tfirst <= 0.0f)
	{
#ifdef _XBOX
		if(bValidMTD==0.0f)	return 0;
#else
		if(!bValidMTD)	return 0;
#endif
		tcoll = 0.0f;
	}
	else tcoll = tfirst;

	return 1;
}
bool PxFabricCookerImpl::cook(const PxClothMeshDesc& desc, PxVec3 gravity, bool useGeodesicTether)
{	
	if(!desc.isValid())
	{
		shdfnd::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, 
			"PxFabricCookerImpl::cook: desc.isValid() failed!");
		return false;
	}

	gravity = gravity.getNormalized();

	mNumParticles = desc.points.count;

	// assemble points
	shdfnd::Array<PxVec4> particles;
	particles.reserve(mNumParticles);
	PxStrideIterator<const PxVec3> pIt((const PxVec3*)desc.points.data, desc.points.stride);
	PxStrideIterator<const PxReal> wIt((const PxReal*)desc.invMasses.data, desc.invMasses.stride);
	for(PxU32 i=0; i<mNumParticles; ++i)
		particles.pushBack(PxVec4(*pIt++, wIt.ptr() ? *wIt++ : 1.0f));

	// build adjacent vertex list
	shdfnd::Array<PxU32> valency(mNumParticles+1, 0);
	shdfnd::Array<PxU32> adjacencies;
	if(desc.flags & PxMeshFlag::e16_BIT_INDICES)
		gatherAdjacencies<PxU16>(valency, adjacencies, desc.triangles, desc.quads);
	else
		gatherAdjacencies<PxU32>(valency, adjacencies, desc.triangles, desc.quads);

	// build unique neighbors from adjacencies
	shdfnd::Array<PxU32> mark(valency.size(), 0);
	shdfnd::Array<PxU32> neighbors; neighbors.reserve(adjacencies.size());
	for(PxU32 i=1, j=0; i<valency.size(); ++i)
	{
		for(; j<valency[i]; ++j)
		{
			PxU32 k = adjacencies[j];
			if(mark[k] != i)
			{
				mark[k] = i;
				neighbors.pushBack(k);
			}
		}
		valency[i] = neighbors.size();
	}

	// build map of unique edges and classify
	shdfnd::HashMap<Pair, Edge> edges;
	for(PxU32 i=0; i<mNumParticles; ++i)
	{
		PxReal wi = particles[i].w;
		// iterate all neighbors
		PxU32 jlast = valency[i+1];
		for(PxU32 j=valency[i]; j<jlast; ++j)
		{
			// add 1-ring edge
			PxU32 m = neighbors[j];
			if(wi + particles[m].w > 0.0f)
				edges[Pair(PxMin(i, m), PxMax(i, m))].classify();

			// iterate all neighbors of neighbor
			PxU32 klast = valency[m+1];
			for(PxU32 k=valency[m]; k<klast; ++k)
			{
				PxU32 n = neighbors[k];
				if(n != i && wi + particles[n].w > 0.0f)
				{
					// add 2-ring edge
					edges[Pair(PxMin(i, n), PxMax(i, n))].classify(
						particles[i], particles[m], particles[n]);
				}
			}
		}
	}

	// copy classified edges to constraints array
	// build histogram of constraints per vertex
	shdfnd::Array<Entry> constraints; 	
	constraints.reserve(edges.size());
	valency.resize(0); valency.resize(mNumParticles+1, 0);

	const PxReal sqrtHalf = PxSqrt(0.4f);
	for(shdfnd::HashMap<Pair, Edge>::Iterator eIt = edges.getIterator(); !eIt.done(); ++eIt)
	{
		const Edge& edge = eIt->second;
		const Pair& pair = eIt->first;
		if((edge.mStretching + edge.mBending + edge.mShearing) > 0.0f)
		{	
			PxClothFabricPhaseType::Enum type = PxClothFabricPhaseType::eINVALID;
			if(edge.mBending > PxMax(edge.mStretching, edge.mShearing))
				type = PxClothFabricPhaseType::eBENDING;
			else if(edge.mShearing > PxMax(edge.mStretching, edge.mBending))
				type = PxClothFabricPhaseType::eSHEARING;
			else 
			{
				PxVec4 diff = particles[pair.first]-particles[pair.second];
				PxReal dot = gravity.dot(reinterpret_cast<const PxVec3&>(diff).getNormalized());
				type = fabsf(dot) < sqrtHalf ? PxClothFabricPhaseType::eHORIZONTAL : PxClothFabricPhaseType::eVERTICAL;
			}
			++valency[pair.first];
			++valency[pair.second];
			constraints.pushBack(Entry(pair, type));
		}
	} 

	prefixSum(valency.begin(), valency.end(), valency.begin());

	PxU32 numConstraints = constraints.size();

	// build adjacent constraint list
	adjacencies.resize(0); adjacencies.resize(valency.back(), 0);
	for(PxU32 i=0; i<numConstraints; ++i)
	{
		adjacencies[--valency[constraints[i].first.first]] = i;
		adjacencies[--valency[constraints[i].first.second]] = i;
	}
	
	shdfnd::Array<PxU32>::ConstIterator aFirst = adjacencies.begin();
	shdfnd::Array<PxU32> colors(numConstraints, numConstraints); // constraint -> color, initialily not colored
	mark.resize(0); mark.resize(numConstraints+1, PX_MAX_U32); // color -> constraint index
	shdfnd::Array<PxU32> adjColorCount(numConstraints, 0); // # of neighbors that are already colored

	shdfnd::Array<ConstraintGraphColorCount> constraintHeap; 
	constraintHeap.reserve(numConstraints); // set of constraints to color (added in edge distance order)

	// Do graph coloring based on edge distance.
	// For each constraint, we add its uncolored neighbors to the heap
	// ,and we pick the constraint with most colored neighbors from the heap.
	while (1)
	{
		PxU32 constraint = 0;
		while ( (constraint < numConstraints) && (colors[constraint] != numConstraints))
			constraint++; // start with the first uncolored constraint
	
		if (constraint >= numConstraints)
			break;

		constraintHeap.clear();
		pushHeap(constraintHeap, ConstraintGraphColorCount((int)constraint, (int)adjColorCount[constraint]));
		PxClothFabricPhaseType::Enum type = constraints[constraint].second;
		
		while (!constraintHeap.empty())
		{		
			ConstraintGraphColorCount heapItem = popHeap(constraintHeap);
			constraint = heapItem.constraint;
			if (colors[constraint] != numConstraints)
				continue; // skip if already colored 

			const Pair& pair = constraints[constraint].first;			
			for(PxU32 j=0; j<2; ++j)
			{
				PxU32 index = j ? pair.first : pair.second;
				if(particles[index].w == 0.0f)
					continue; // don't mark adjacent particles if attached

				for(shdfnd::Array<PxU32>::ConstIterator aIt = aFirst + valency[index], aEnd = aFirst + valency[index+1]; aIt != aEnd; ++aIt)
				{				
					PxU32 adjacentConstraint = *aIt;
					if ((constraints[adjacentConstraint].second != type) || (adjacentConstraint == constraint))
						continue;

					mark[colors[adjacentConstraint]] = constraint; 
					++adjColorCount[adjacentConstraint];
					pushHeap(constraintHeap, ConstraintGraphColorCount((int)adjacentConstraint, (int)adjColorCount[adjacentConstraint]));
				}
			}

			// find smallest color with matching type
			PxU32 color = 0;
			while((color < mPhases.size() && mPhases[color].phaseType != type) || mark[color] == constraint)
				++color;

			// create a new color set
			if(color == mPhases.size())
			{
				PxClothFabricPhase phase(type, mPhases.size());
				mPhases.pushBack(phase);
				mSets.pushBack(0);
			}

			colors[constraint] = color;
			++mSets[color];
		} 
	}

#if 0 // PX_DEBUG
	printf("set[%u] = ", mSets.size());
	for(PxU32 i=0; i<mSets.size(); ++i)
		printf("%u ", mSets[i]);
#endif

	prefixSum(mSets.begin(), mSets.end(), mSets.begin());

#if 0 // PX_DEBUG
	printf(" = %u\n", mSets.back());
#endif

	// write indices and rest lengths
	// convert mSets to exclusive sum
	PxU32 back = mSets.back();
	mSets.pushBack(back);
	mIndices.resize(numConstraints*2);
	mRestvalues.resize(numConstraints);
	for(PxU32 i=0; i<numConstraints; ++i)
	{
		PxU32 first = constraints[i].first.first;
		PxU32 second = constraints[i].first.second;

		PxU32 index = --mSets[colors[i]];

		mIndices[2*index  ] = first;
		mIndices[2*index+1] = second;

		PxVec4 diff = particles[second] - particles[first];
		mRestvalues[index] = reinterpret_cast<
			const PxVec3&>(diff).magnitude();
	} 
	
	// reorder constraints and rest values for more efficient cache access (linear)
	shdfnd::Array<PxU32> newIndices(mIndices.size());
	shdfnd::Array<PxF32> newRestValues(mRestvalues.size());

	// sort each constraint set in vertex order
	for (PxU32 i=0; i < mSets.size()-1; ++i)
	{
		// create a re-ordering list
		shdfnd::Array<PxU32> reorder(mSets[i+1]-mSets[i]);

		for (PxU32 r=0; r < reorder.size(); ++r)
			reorder[r] = r;

		const PxU32 indicesOffset = mSets[i]*2;
		const PxU32 restOffset = mSets[i];

		ConstraintSorter predicate(&mIndices[indicesOffset]);
		shdfnd::sort(&reorder[0], reorder.size(), predicate);
		
		for (PxU32 r=0; r < reorder.size(); ++r)
		{
			newIndices[indicesOffset + r*2] = mIndices[indicesOffset + reorder[r]*2];
			newIndices[indicesOffset + r*2+1] = mIndices[indicesOffset + reorder[r]*2+1];
			newRestValues[restOffset + r] = mRestvalues[restOffset + reorder[r]];
		}
	}

	mIndices = newIndices;
	mRestvalues = newRestValues;

	PX_ASSERT(mIndices.size() == mRestvalues.size()*2);
	PX_ASSERT(mRestvalues.size() == mSets.back());

#if 0 // PX_DEBUG
	for (PxU32 i = 1; i < mSets.size(); i++)
	{
		PxClothFabricPhase phase = mPhases[i-1];
		printf("%d : type %d, size %d\n", 
			i-1, phase.phaseType, mSets[i] - mSets[i-1]);
	}
#endif

	if (useGeodesicTether)
	{
		PxClothGeodesicTetherCooker tetherCooker(desc);
		if (tetherCooker.getCookerStatus() == 0)
		{
			PxU32 numTethersPerParticle = tetherCooker.getNbTethersPerParticle();
			PxU32 tetherSize = mNumParticles * numTethersPerParticle;
			mTetherAnchors.resize(tetherSize);
			mTetherLengths.resize(tetherSize);
			tetherCooker.getTetherData(mTetherAnchors.begin(), mTetherLengths.begin());
		}
		else
			useGeodesicTether = false;
	}

	if (!useGeodesicTether)
	{
		PxClothSimpleTetherCooker tetherCooker(desc);
		mTetherAnchors.resize(mNumParticles);
		mTetherLengths.resize(mNumParticles);
		tetherCooker.getTetherData(mTetherAnchors.begin(), mTetherLengths.begin());
	}

	return true;
}
/**
 @brief 
 @date 2014-01-04
*/
void RendererCompositionShape::GenerateTriangleFrom4Vector( void *positions, PxU32 positionStride, 
	void *normals, PxU32 normalStride, PxU32 startVtxIdx, PxU16 *indices, PxU32 startIndexIdx,
	const PxVec3 &center, PxVec3 v0, PxVec3 v1, PxVec3 v2, PxVec3 v3 )
{
	// test cw
	{
		PxVec3 n0 = v2 - v0;
		n0.normalize();
		PxVec3 n1 = v3 - v0;
		n1.normalize();
		PxVec3 n = n0.cross(n1);
		n.normalize();

		PxVec3 faceCenter = v0 + v2 + v3;
		faceCenter /= 3.f;
		PxVec3 cn = center - faceCenter;
		cn.normalize();
		const float d = n.dot(cn);
		if (d >= 0)
		{ // cw
		}
		else
		{ // ccw
			// switching
			PxVec3 tmp = v3;
			v3 = v2;
			v2 = tmp;
		}
	}

	// triangle 1
	{
		PxU32 face1VtxIdx = startVtxIdx;

		PxVec3 n0 = v1 - v0;
		n0.normalize();
		PxVec3 n1 = v2 - v0;
		n1.normalize();
		PxVec3 n = n0.cross(n1);
		n.normalize();
		PxVec3 faceCenter = v0 + v1 + v2;
		faceCenter /= 3.f;
		PxVec3 cn = center - faceCenter;
		cn.normalize();

		const float d = n.dot(cn);
		if (d >= 0)
		{ // cw
			*(PxVec3*)(((PxU8*)positions) + (positionStride * startVtxIdx++)) = v0;
			*(PxVec3*)(((PxU8*)positions) + (positionStride * startVtxIdx++)) = v1;
			*(PxVec3*)(((PxU8*)positions) + (positionStride * startVtxIdx++)) = v2;
			*(PxVec3*)(((PxU8*)normals) + (normalStride * face1VtxIdx)) = -n;
			*(PxVec3*)(((PxU8*)normals) + (normalStride * (face1VtxIdx+1))) = -n;
			*(PxVec3*)(((PxU8*)normals) + (normalStride * (face1VtxIdx+2))) = -n;
		}
		else
		{ // ccw
			*(PxVec3*)(((PxU8*)positions) + (positionStride * startVtxIdx++)) = v0;
			*(PxVec3*)(((PxU8*)positions) + (positionStride * startVtxIdx++)) = v2;
			*(PxVec3*)(((PxU8*)positions) + (positionStride * startVtxIdx++)) = v1;
			*(PxVec3*)(((PxU8*)normals) + (normalStride * face1VtxIdx)) = n;
			*(PxVec3*)(((PxU8*)normals) + (normalStride * (face1VtxIdx+1))) = n;
			*(PxVec3*)(((PxU8*)normals) + (normalStride * (face1VtxIdx+2))) = n;
		}

		indices[ startIndexIdx++] = face1VtxIdx;
		indices[ startIndexIdx++] = face1VtxIdx+1;
		indices[ startIndexIdx++] = face1VtxIdx+2;
	}


	// triangle2
	{
		PxU32 face2VtxIdx = startVtxIdx;

		PxVec3 n0 = v2 - v0;
		n0.normalize();
		PxVec3 n1 = v3 - v0;
		n1.normalize();
		PxVec3 n = n0.cross(n1);
		n.normalize();
		PxVec3 faceCenter = v0 + v2 + v3;
		faceCenter /= 3.f;
		PxVec3 cn = center - faceCenter;
		cn.normalize();

		const float d = n.dot(cn);
		if (d >= 0)
		{ // cw
			*(PxVec3*)(((PxU8*)positions) + (positionStride * startVtxIdx++)) = v0;
			*(PxVec3*)(((PxU8*)positions) + (positionStride * startVtxIdx++)) = v2;
			*(PxVec3*)(((PxU8*)positions) + (positionStride * startVtxIdx++)) = v3;
			*(PxVec3*)(((PxU8*)normals) + (normalStride * face2VtxIdx)) = -n;
			*(PxVec3*)(((PxU8*)normals) + (normalStride * (face2VtxIdx+1))) = -n;
			*(PxVec3*)(((PxU8*)normals) + (normalStride * (face2VtxIdx+2))) = -n;
		}
		else
		{ // ccw
			*(PxVec3*)(((PxU8*)positions) + (positionStride * startVtxIdx++)) = v0;
			*(PxVec3*)(((PxU8*)positions) + (positionStride * startVtxIdx++)) = v3;
			*(PxVec3*)(((PxU8*)positions) + (positionStride * startVtxIdx++)) = v2;
			*(PxVec3*)(((PxU8*)normals) + (normalStride * face2VtxIdx)) = n;
			*(PxVec3*)(((PxU8*)normals) + (normalStride * (face2VtxIdx+1))) = n;
			*(PxVec3*)(((PxU8*)normals) + (normalStride * (face2VtxIdx+2))) = n;
		}

		indices[ startIndexIdx++] = face2VtxIdx;
		indices[ startIndexIdx++] = face2VtxIdx+1;
		indices[ startIndexIdx++] = face2VtxIdx+2;
	}
}
void SampleCustomGravityCameraController::update(Camera& camera, PxReal dtime)
{
	const PxExtendedVec3& currentPos = mCCT.getPosition();
	const PxVec3 curPos = toVec3(currentPos);

	// Compute up vector for current CCT position
	PxVec3 upVector;
	mBase.mPlanet.getUpVector(upVector, curPos);
	PX_ASSERT(upVector.isFinite());

	// Update CCT
	if(!mBase.isPaused())
	{
		if(1)
		{
			bool recordPos = true;
			const PxU32 currentSize = mNbRecords;
			if(currentSize)
			{
				const PxVec3 lastPos = mHistory[currentSize-1];
//				const float limit = 0.1f;
				const float limit = 0.5f;
				if((curPos - lastPos).magnitude()<limit)
					recordPos = false;
			}
			if(recordPos)
			{
				if(mNbRecords==POS_HISTORY_LIMIT)
				{
					for(PxU32 i=1;i<mNbRecords;i++)
						mHistory[i-1] = mHistory[i];
					mNbRecords--;
				}
				mHistory[mNbRecords++] = curPos;
			}
		}

		// Subtract off the 'up' component of the view direction to get our forward motion vector.
		PxVec3 viewDir = camera.getViewDir();
		PxVec3 forward = (viewDir - upVector * upVector.dot(viewDir)).getNormalized();
	
//		PxVec3 forward = mForward;

		// Compute "right" vector
		PxVec3 right = forward.cross(upVector);
		right.normalize();
//		PxVec3 right = mRightV;

		PxVec3 targetKeyDisplacement(0);
		if(mFwd)	targetKeyDisplacement += forward;
		if(mBwd)	targetKeyDisplacement -= forward;

		if(mRight)	targetKeyDisplacement += right;
		if(mLeft)	targetKeyDisplacement -= right;

		targetKeyDisplacement *= mKeyShiftDown ? mRunningSpeed : mWalkingSpeed;
//		targetKeyDisplacement += PxVec3(0,-9.81,0);
		targetKeyDisplacement *= dtime;

		PxVec3 targetPadDisplacement(0);
		targetPadDisplacement += forward * mGamepadForwardInc * mRunningSpeed;
		targetPadDisplacement += right * mGamepadLateralInc * mRunningSpeed;
//		targetPadDisplacement += PxVec3(0,-9.81,0);
		targetPadDisplacement *= dtime;


		const PxF32 heightDelta = gJump.getHeight(dtime);
//		printf("%f\n", heightDelta);
		PxVec3 upDisp = upVector;
		if(heightDelta!=0.0f)
			upDisp *= heightDelta;
		else
			upDisp *= -9.81f * dtime;
		const PxVec3 disp = targetKeyDisplacement + targetPadDisplacement + upDisp;

//upDisp.normalize();
//printf("%f | %f | %f\n", upDisp.x, upDisp.y, upDisp.z);
//		printf("%f | %f | %f\n", targetKeyDisplacement.x, targetKeyDisplacement.y, targetKeyDisplacement.z);
//		printf("%f | %f | %f\n\n", targetPadDisplacement.x, targetPadDisplacement.y, targetPadDisplacement.z);

		mCCT.setUpDirection(upVector);
		const PxU32 flags = mCCT.move(disp, 0.001f, dtime, PxControllerFilters());
		if(flags & PxControllerFlag::eCOLLISION_DOWN)
		{
			gJump.stopJump();
//			printf("Stop jump\n");
		}
	}

	// Update camera
	if(1)
	{
		mTargetYaw		+= mGamepadYawInc * dtime;
		mTargetPitch	+= mGamepadPitchInc * dtime;

		// Clamp pitch
//		if(mTargetPitch<mPitchMin)	mTargetPitch = mPitchMin;
//		if(mTargetPitch>mPitchMax)	mTargetPitch = mPitchMax;
	}

	if(1)
	{
		PxVec3 up = upVector;

		PxQuat localPitchQ(mTargetPitch, PxVec3(1.0f, 0.0f, 0.0f));
		PX_ASSERT(localPitchQ.isSane());
		PxMat33 localPitchM(localPitchQ);

		const PxVec3 upRef(0.0f, 1.0f, 0.0f);

		PxQuat localYawQ(mTargetYaw, upRef);
		PX_ASSERT(localYawQ.isSane());
		PxMat33 localYawM(localYawQ);

			bool res;
			PxQuat localToWorldQ = rotationArc(upRef, up, res);
			static PxQuat memory(0,0,0,1);
			if(!res)
			{
				localToWorldQ = memory;
			}
			else
			{
				memory = localToWorldQ;
			}
			PX_ASSERT(localToWorldQ.isSane());
			PxMat33 localToWorld(localToWorldQ);
		

			static PxVec3 previousUp(0.0f, 1.0f, 0.0f);
			static PxQuat incLocalToWorldQ(0.0f, 0.0f, 0.0f, 1.0f);
			PxQuat incQ = rotationArc(previousUp, up, res);
			PX_ASSERT(incQ.isSane());
//			incLocalToWorldQ = incLocalToWorldQ * incQ;
			incLocalToWorldQ = incQ * incLocalToWorldQ;
			PX_ASSERT(incLocalToWorldQ.isSane());
			incLocalToWorldQ.normalize();
			PxMat33 incLocalToWorldM(incLocalToWorldQ);
			localToWorld = incLocalToWorldM;
			previousUp = up;

mTest = localToWorld;
//mTest = localToWorld * localYawM;

//		PxMat33 rot = localYawM * localToWorld;
		PxMat33 rot = localToWorld * localYawM * localPitchM;
//		PxMat33 rot = localToWorld * localYawM;
		PX_ASSERT(rot.column0.isFinite());
		PX_ASSERT(rot.column1.isFinite());
		PX_ASSERT(rot.column2.isFinite());

		////

		PxMat44 view(rot.column0, rot.column1, rot.column2, PxVec3(0));

		mForward = -rot.column2;
		mRightV = rot.column0;

		camera.setView(PxTransform(view));
		PxVec3 viewDir = camera.getViewDir();
		PX_ASSERT(viewDir.isFinite());

		////

		PxRigidActor* characterActor = mCCT.getActor();
		
		PxShape* shape;
		characterActor->getShapes(&shape,1);

		PxCapsuleGeometry geom;
		shape->getCapsuleGeometry(geom);

		up *= geom.halfHeight+geom.radius;

		const PxVec3 headPos = curPos + up;
		const float distanceToTarget = 10.0f;
//		const float distanceToTarget = 20.0f;
//		const float distanceToTarget = 5.0f;
//		const PxVec3 camPos = headPos - viewDir*distanceToTarget;
		const PxVec3 camPos = headPos - mForward*distanceToTarget;// + up * 20.0f;
//		view.t = camPos;
		view.column3 = PxVec4(camPos,0);
//		camera.setView(view);
		camera.setView(PxTransform(view));
		mTarget = headPos;
	}

	if(0)
	{
		PxControllerState cctState;
		mCCT.getState(cctState);
		printf("\nCCT state:\n");
		printf("delta:             %.02f | %.02f | %.02f\n", cctState.deltaXP.x, cctState.deltaXP.y, cctState.deltaXP.z);
		printf("touchedShape:      %p\n", cctState.touchedShape);
		printf("touchedObstacle:   %p\n", cctState.touchedObstacle);
		printf("standOnAnotherCCT: %d\n", cctState.standOnAnotherCCT);
		printf("standOnObstacle:   %d\n", cctState.standOnObstacle);
		printf("isMovingUp:        %d\n", cctState.isMovingUp);
		printf("collisionFlags:    %d\n", cctState.collisionFlags);
	}

}
// PT: ray-capsule intersection code, originally from the old Magic Software library.
PxU32 Gu::intersectRayCapsuleInternal(const PxVec3& origin, const PxVec3& dir, const PxVec3& p0, const PxVec3& p1, float radius, PxReal s[2])
{
	// set up quadratic Q(t) = a*t^2 + 2*b*t + c
	PxVec3 kW = p1 - p0;
	const float fWLength = kW.magnitude();
	if(fWLength!=0.0f)
		kW /= fWLength;

	// PT: if the capsule is in fact a sphere, switch back to dedicated sphere code.
	// This is not just an optimization, the rest of the code fails otherwise.
	if(fWLength<=1e-6f)
	{
		const float d0 = (origin - p0).magnitudeSquared();
		const float d1 = (origin - p1).magnitudeSquared();
		const float approxLength = (PxMax(d0, d1) + radius)*2.0f;
		return PxU32(Gu::intersectRaySphere(origin, dir, approxLength, p0, radius, s[0]));
	}

	// generate orthonormal basis
	PxVec3 kU(0.0f);

	if (fWLength > 0.0f)
	{
		PxReal fInvLength;
		if ( PxAbs(kW.x) >= PxAbs(kW.y) )
		{
			// W.x or W.z is the largest magnitude component, swap them
			fInvLength = PxRecipSqrt(kW.x*kW.x + kW.z*kW.z);
			kU.x = -kW.z*fInvLength;
			kU.y = 0.0f;
			kU.z = kW.x*fInvLength;
		}
		else
		{
			// W.y or W.z is the largest magnitude component, swap them
			fInvLength = PxRecipSqrt(kW.y*kW.y + kW.z*kW.z);
			kU.x = 0.0f;
			kU.y = kW.z*fInvLength;
			kU.z = -kW.y*fInvLength;
		}
	}

	PxVec3 kV = kW.cross(kU);
	kV.normalize();	// PT: fixed november, 24, 2004. This is a bug in Magic.

	// compute intersection

	PxVec3 kD(kU.dot(dir), kV.dot(dir), kW.dot(dir));
	const float fDLength = kD.magnitude();
	const float fInvDLength = fDLength!=0.0f ? 1.0f/fDLength : 0.0f;
	kD *= fInvDLength;

	const PxVec3 kDiff = origin - p0;
	const PxVec3 kP(kU.dot(kDiff), kV.dot(kDiff), kW.dot(kDiff));
	const PxReal fRadiusSqr = radius*radius;

	// Is the velocity parallel to the capsule direction? (or zero)
	if ( PxAbs(kD.z) >= 1.0f - PX_EPS_REAL || fDLength < PX_EPS_REAL )
	{
		const float fAxisDir = dir.dot(kW);

		const PxReal fDiscr = fRadiusSqr - kP.x*kP.x - kP.y*kP.y;
		if ( fAxisDir < 0 && fDiscr >= 0.0f )
		{
			// Velocity anti-parallel to the capsule direction
			const PxReal fRoot = PxSqrt(fDiscr);
			s[0] = (kP.z + fRoot)*fInvDLength;
			s[1] = -(fWLength - kP.z + fRoot)*fInvDLength;
			return 2;
		}
		else if ( fAxisDir > 0  && fDiscr >= 0.0f )
		{
			// Velocity parallel to the capsule direction
			const PxReal fRoot = PxSqrt(fDiscr);
			s[0] = -(kP.z + fRoot)*fInvDLength;
			s[1] = (fWLength - kP.z + fRoot)*fInvDLength;
			return 2;
		}
		else
		{
			// sphere heading wrong direction, or no velocity at all
			return 0;
		}   
	}

	// test intersection with infinite cylinder
	PxReal fA = kD.x*kD.x + kD.y*kD.y;
	PxReal fB = kP.x*kD.x + kP.y*kD.y;
	PxReal fC = kP.x*kP.x + kP.y*kP.y - fRadiusSqr;
	PxReal fDiscr = fB*fB - fA*fC;
	if ( fDiscr < 0.0f )
	{
		// line does not intersect infinite cylinder
		return 0;
	}

	PxU32 iQuantity = 0;

	if ( fDiscr > 0.0f )
	{
		// line intersects infinite cylinder in two places
		const PxReal fRoot = PxSqrt(fDiscr);
		const PxReal fInv = 1.0f/fA;
		PxReal fT = (-fB - fRoot)*fInv;
		PxReal fTmp = kP.z + fT*kD.z;
		const float epsilon = 1e-3f;	// PT: see TA35174
		if ( fTmp >= -epsilon && fTmp <= fWLength+epsilon )
			s[iQuantity++] = fT*fInvDLength;

		fT = (-fB + fRoot)*fInv;
		fTmp = kP.z + fT*kD.z;
		if ( fTmp >= -epsilon && fTmp <= fWLength+epsilon )
			s[iQuantity++] = fT*fInvDLength;

		if ( iQuantity == 2 )
		{
			// line intersects capsule wall in two places
			return 2;
		}
	}
	else
	{
		// line is tangent to infinite cylinder
		const PxReal fT = -fB/fA;
		const PxReal fTmp = kP.z + fT*kD.z;
		if ( 0.0f <= fTmp && fTmp <= fWLength )
		{
			s[0] = fT*fInvDLength;
			return 1;
		}
	}

	// test intersection with bottom hemisphere
	// fA = 1
	fB += kP.z*kD.z;
	fC += kP.z*kP.z;
	fDiscr = fB*fB - fC;
	if ( fDiscr > 0.0f )
	{
		const PxReal fRoot = PxSqrt(fDiscr);
		PxReal fT = -fB - fRoot;
		PxReal fTmp = kP.z + fT*kD.z;
		if ( fTmp <= 0.0f )
		{
			s[iQuantity++] = fT*fInvDLength;
			if ( iQuantity == 2 )
				return 2;
		}

		fT = -fB + fRoot;
		fTmp = kP.z + fT*kD.z;
		if ( fTmp <= 0.0f )
		{
			s[iQuantity++] = fT*fInvDLength;
			if ( iQuantity == 2 )
				return 2;
		}
	}
	else if ( fDiscr == 0.0f )
	{
		const PxReal fT = -fB;
		const PxReal fTmp = kP.z + fT*kD.z;
		if ( fTmp <= 0.0f )
		{
			s[iQuantity++] = fT*fInvDLength;
			if ( iQuantity == 2 )
				return 2;
		}
	}

	// test intersection with top hemisphere
	// fA = 1
	fB -= kD.z*fWLength;
	fC += fWLength*(fWLength - 2.0f*kP.z);

	fDiscr = fB*fB - fC;
	if ( fDiscr > 0.0f )
	{
		const PxReal fRoot = PxSqrt(fDiscr);
		PxReal fT = -fB - fRoot;
		PxReal fTmp = kP.z + fT*kD.z;
		if ( fTmp >= fWLength )
		{
			s[iQuantity++] = fT*fInvDLength;
			if ( iQuantity == 2 )
				return 2;
		}

		fT = -fB + fRoot;
		fTmp = kP.z + fT*kD.z;
		if ( fTmp >= fWLength )
		{
			s[iQuantity++] = fT*fInvDLength;
			if ( iQuantity == 2 )
				return 2;
		}
	}
	else if ( fDiscr == 0.0f )
	{
		const PxReal fT = -fB;
		const PxReal fTmp = kP.z + fT*kD.z;
		if ( fTmp >= fWLength )
		{
			s[iQuantity++] = fT*fInvDLength;
			if ( iQuantity == 2 )
				return 2;
		}
	}
	return iQuantity;
}
///////////////////////////////////////////////////////////////////////////////
// compute intersection of a ray from a source vertex in direction toward parent
int PxClothGeodesicTetherCookerImpl::computeVertexIntersection(PxU32 parent, PxU32 src, PathIntersection &path)
{
	if (src == parent)
	{
		path = PathIntersection(true, src, 0.0);
		return 0;
	}

	float maxdot = -1.0f;
	int closestVert = -1;

	// gradient is toward the parent vertex
	PxVec3 g = (mVertices[parent] - mVertices[src]).getNormalized();

	// for every triangle incident on this vertex, we intersect against opposite edge of the triangle
	PxU32 sfirst = mFirstVertTriAdj[src];
	PxU32 slast = (src < ((PxU32)mNumParticles-1)) ? mFirstVertTriAdj[src+1] : (PxU32)mVertTriAdjs.size();
	for (PxU32 adj = sfirst; adj < slast; adj++)
	{
		PxU32 tid = mVertTriAdjs[adj];
		
		PxU32 i0 = mIndices[tid*3];
		PxU32 i1 = mIndices[tid*3+1];
		PxU32 i2 = mIndices[tid*3+2];

		int eid = 0;
		if (i0 == src) eid = 1;
		else if (i1 == src) eid = 2;
		else if (i2 == src) eid = 0;
		else continue; // error

		// reshuffle so that src is located at i2
		i0 = mIndices[tid*3 + eid];
		i1 = mIndices[tid*3 + (eid+1)%3];
		i2 = src;

		PxVec3 p0 = mVertices[i0];
		PxVec3 p1 = mVertices[i1];
		PxVec3 p2 = mVertices[i2];

		// check if we hit source immediately from this triangle
		if (i0 == parent)
		{
			path = PathIntersection(true, parent, (p0 - p2).magnitude());
			return 1;
		}

		if (i1 == parent)
		{
			path = PathIntersection(true, parent, (p1 - p2).magnitude());
			return 1;
		}

		// ray direction is the gradient projected on the plane of this triangle
		PxVec3 n = ((p0 - p2).cross(p1 - p2)).getNormalized();
		PxVec3 d = (g - g.dot(n) * n).getNormalized();

		// find intersection of ray (p2, d) against the edge (p0,p1)
		float s, t;
		bool result = intersectRayEdge(p2, d, p0, p1, s, t);
		if (result == false)
			continue;

		// t should be positive, otherwise we just hit the triangle in opposite direction, so ignore
		const float eps = 1e-5;
		if (t > -eps)
		{		
			PxVec3 ip; // intersection point
			if (( s > -eps ) && (s < (1.0f + eps)))
			{				
				// if intersection point is too close to each vertex, we record a vertex intersection
				if ( ( s < eps) || (s > (1.0f-eps)))
				{
					path.vertOrTriangle = true;
					path.index = (s < eps) ? i0 : i1;
					path.distance = (p2 - mVertices[path.index]).magnitude();				
				}
				else // found an edge instersection
				{
					ip = p0 + s * (p1 - p0);
					path = PathIntersection(false, tid*3 + eid, (p2 - ip).magnitude(), s);					
				}
				return 1;
			}
		}

		// for fall back (see below)
		PxVec3 d0 = (p0 - p2).getNormalized();
		PxVec3 d1 = (p1 - p2).getNormalized();
		float d0dotg = d0.dot(d);
		float d1dotg = d1.dot(d);

		if (d0dotg > maxdot)
		{
			closestVert = (int)i0;
			maxdot = d0dotg;
		}
		if (d1dotg > maxdot)
		{
			closestVert = (int)i1;
			maxdot = d1dotg;
		}
	} // end for (PxU32 adj = sfirst...

	// Fall back to use greedy (Dijkstra-like) path selection. 
	// This happens as triangles are curved and we may not find intersection on any triangle.
	// In this case, we choose a vertex closest to the gradient direction.
	if (closestVert > 0)
	{
		path = PathIntersection(true, (PxU32)closestVert, (mVertices[src] - mVertices[(PxU32)closestVert]).magnitude());
		return 1;
	}

	// Error, (possibly dangling vertex)
	return -1;
}
bool setupFinalizeExtSolverConstraintsCoulomb(PxcNpWorkUnit& n,
						    const ContactBuffer& buffer,
							const PxcCorrelationBufferCoulomb& c,
							const PxTransform& bodyFrame0,
							const PxTransform& bodyFrame1,
							bool /*perPointFriction*/,
							PxU8* workspace,
							PxReal invDt,
							PxReal bounceThreshold,
							PxsSolverExtBody& b0,
							PxsSolverExtBody& b1,
							PxU32 frictionCountPerPoint,
							PxReal invMassScale0, PxReal invInertiaScale0, 
							PxReal invMassScale1, PxReal invInertiaScale1)	
{
	// NOTE II: the friction patches are sparse (some of them have no contact patches, and
	// therefore did not get written back to the cache) but the patch addresses are dense,
	// corresponding to valid patches

	PxU8* PX_RESTRICT ptr = workspace;
	const FloatV zero=FZero();

	//KS - TODO - this should all be done in SIMD to avoid LHS
	const PxF32 maxPenBias0 = b0.mLinkIndex == PxcSolverConstraintDesc::NO_LINK ? b0.mBodyData->penBiasClamp : getMaxPenBias(*b0.mFsData)[b0.mLinkIndex];
	const PxF32 maxPenBias1 = b1.mLinkIndex == PxcSolverConstraintDesc::NO_LINK ? b1.mBodyData->penBiasClamp : getMaxPenBias(*b1.mFsData)[b0.mLinkIndex];

	const FloatV maxPen = FLoad(PxMax(maxPenBias0, maxPenBias1)/invDt);

	const FloatV restDistance = FLoad(n.restDistance); 

	Ps::prefetchLine(c.contactID);
	Ps::prefetchLine(c.contactID, 128);

	bool useExtContacts = (n.flags & (PxcNpWorkUnitFlag::eARTICULATION_BODY0|PxcNpWorkUnitFlag::eARTICULATION_BODY1))!=0;

	const PxU32 frictionPatchCount = c.frictionPatchCount;
	const bool staticBody = ((n.flags & PxcNpWorkUnitFlag::eDYNAMIC_BODY1) == 0);

	const PxU32 pointStride = useExtContacts ? sizeof(PxcSolverContactExt) : sizeof(PxcSolverContact);
	const PxU32 frictionStride = useExtContacts ? sizeof(PxcSolverFrictionExt) : sizeof(PxcSolverFriction);
	const PxU8 pointHeaderType = Ps::to8(useExtContacts ? PXS_SC_TYPE_EXT_CONTACT : (staticBody ? PXS_SC_TYPE_STATIC_CONTACT : PXS_SC_TYPE_RB_CONTACT));
	const PxU8 frictionHeaderType = Ps::to8(useExtContacts ? PXS_SC_TYPE_EXT_FRICTION : (staticBody ? PXS_SC_TYPE_STATIC_FRICTION : PXS_SC_TYPE_FRICTION));

	PxReal d0 = n.dominance0 * invMassScale0;
	PxReal d1 = n.dominance1 * invMassScale1;
	PxReal angD0 = n.dominance0 * invInertiaScale0;
	PxReal angD1 = n.dominance1 * invInertiaScale1;

	for(PxU32 i=0;i< frictionPatchCount;i++)
	{
		const PxU32 contactCount = c.frictionPatchContactCounts[i];
		if(contactCount == 0)
			continue;

		const Gu::ContactPoint* contactBase0 = buffer.contacts + c.contactPatches[c.correlationListHeads[i]].start;

		const PxcFrictionPatchCoulomb& frictionPatch = c.frictionPatches[i];

		const Vec3V normalV = Ps::aos::V3LoadU(frictionPatch.normal);
		const PxVec3 normal = frictionPatch.normal;

		const PxReal combinedRestitution = contactBase0->restitution;
	
		
		PxcSolverContactCoulombHeader* PX_RESTRICT header = reinterpret_cast<PxcSolverContactCoulombHeader*>(ptr);
		ptr += sizeof(PxcSolverContactCoulombHeader);

		Ps::prefetchLine(ptr, 128);
		Ps::prefetchLine(ptr, 256);
		Ps::prefetchLine(ptr, 384);


		header->numNormalConstr		= (PxU8)contactCount;
		header->type				= pointHeaderType;
		header->setRestitution(combinedRestitution);

		header->setDominance0(d0);
		header->setDominance1(d1);
		header->angDom0 = angD0;
		header->angDom1 = angD1;
		
		header->setNormal(normalV);
		
		for(PxU32 patch=c.correlationListHeads[i]; 
			patch!=PxcCorrelationBuffer::LIST_END; 
			patch = c.contactPatches[patch].next)
		{
			const PxU32 count = c.contactPatches[patch].count;
			const Gu::ContactPoint* contactBase = buffer.contacts + c.contactPatches[patch].start;
				
			PxU8* p = ptr;
			for(PxU32 j=0;j<count;j++)
			{
				const Gu::ContactPoint& contact = contactBase[j];

				PxcSolverContactExt* PX_RESTRICT solverContact = reinterpret_cast<PxcSolverContactExt*>(p);
				p += pointStride;

				const FloatV separation = FLoad(contact.separation);

				PxVec3 ra = contact.point - bodyFrame0.p; 
				PxVec3 rb = contact.point - bodyFrame1.p; 

				Vec3V targetVel = V3LoadU(contact.targetVel);
				const FloatV maxImpulse = FLoad(contact.maxImpulse);

				solverContact->scaledBiasX_targetVelocityY_maxImpulseZ = V3Merge(FMax(maxPen, FSub(separation, restDistance)), V3Dot(normalV,targetVel), maxImpulse);

				//TODO - should we do cross only in vector land and then store. Could cause a LHS but probably no worse than
				//what we already have (probably has a LHS from converting from vector to scalar above)
				const PxVec3 raXn = ra.cross(normal);
				const PxVec3 rbXn = rb.cross(normal);

				Cm::SpatialVector deltaV0, deltaV1;

				PxReal unitResponse = getImpulseResponse(b0, Cm::SpatialVector(normal, raXn), deltaV0, d0, angD0,
														 b1, Cm::SpatialVector(-normal, -rbXn), deltaV1, d1, angD1);

				const PxReal vrel = b0.projectVelocity(normal, raXn)
								  - b1.projectVelocity(normal, rbXn);

				solverContact->raXnXYZ_appliedForceW = V4SetW(Vec4V_From_Vec3V(V3LoadU(raXn)), zero);
				solverContact->rbXnXYZ_velMultiplierW = V4SetW(Vec4V_From_Vec3V(V3LoadU(rbXn)), zero);

				completeContactPoint(*solverContact, unitResponse, vrel, invDt, header->restitution, bounceThreshold);

				solverContact->setDeltaVA(deltaV0.linear, deltaV0.angular);
				solverContact->setDeltaVB(deltaV1.linear, deltaV1.angular);


			}			
			ptr = p;
		}
	}

	//construct all the frictions

	PxU8* PX_RESTRICT ptr2 = workspace;

	const PxF32 orthoThreshold = 0.70710678f;
	const PxF32 eps = 0.00001f;
	bool hasFriction = false;

	for(PxU32 i=0;i< frictionPatchCount;i++)
	{
		const PxU32 contactCount = c.frictionPatchContactCounts[i];
		if(contactCount == 0)
			continue;

		PxcSolverContactCoulombHeader* header = reinterpret_cast<PxcSolverContactCoulombHeader*>(ptr2); 
		header->frictionOffset = PxU16(ptr - ptr2);
		ptr2 += sizeof(PxcSolverContactCoulombHeader) + header->numNormalConstr * pointStride;

		PxVec3 normal = c.frictionPatches[i].normal;

		const Gu::ContactPoint* contactBase0 = buffer.contacts + c.contactPatches[c.correlationListHeads[i]].start;

		const PxReal staticFriction = contactBase0->staticFriction;
		const PxU32 disableStrongFriction = contactBase0->internalFaceIndex1 & PxMaterialFlag::eDISABLE_FRICTION;
		const bool haveFriction = (disableStrongFriction == 0);
	
		PxcSolverFrictionHeader* frictionHeader = (PxcSolverFrictionHeader*)ptr;
		frictionHeader->numNormalConstr = Ps::to8(c.frictionPatchContactCounts[i]);
		frictionHeader->numFrictionConstr = Ps::to8(haveFriction ? c.frictionPatches[i].numConstraints : 0);
		ptr += sizeof(PxcSolverFrictionHeader);
		ptr += frictionHeader->getAppliedForcePaddingSize(c.frictionPatchContactCounts[i]);
		Ps::prefetchLine(ptr, 128);
		Ps::prefetchLine(ptr, 256);
		Ps::prefetchLine(ptr, 384);


		const PxVec3 t0Fallback1(0.f, -normal.z, normal.y);
		const PxVec3 t0Fallback2(-normal.y, normal.x, 0.f) ;
		const PxVec3 tFallback1 = orthoThreshold > PxAbs(normal.x) ? t0Fallback1 : t0Fallback2;
		const PxVec3 vrel = b0.getLinVel() - b1.getLinVel();
		const PxVec3 t0_ = vrel - normal * (normal.dot(vrel));
		const PxReal sqDist = t0_.dot(t0_);
		const PxVec3 tDir0 = (sqDist > eps ? t0_: tFallback1).getNormalized();
		const PxVec3 tDir1 = tDir0.cross(normal);
		PxVec3 tFallback[2] = {tDir0, tDir1};

		PxU32 ind = 0;

		if(haveFriction)
		{
			hasFriction = true;
			frictionHeader->setStaticFriction(staticFriction);
			frictionHeader->setDominance0(n.dominance0);
			frictionHeader->setDominance1(n.dominance1);
			frictionHeader->angDom0 = angD0;
			frictionHeader->angDom1 = angD1;
			frictionHeader->type			= frictionHeaderType;
			
			PxU32 totalPatchContactCount = 0;
		
			for(PxU32 patch=c.correlationListHeads[i]; 
				patch!=PxcCorrelationBuffer::LIST_END; 
				patch = c.contactPatches[patch].next)
			{
				const PxU32 count = c.contactPatches[patch].count;
				const PxU32 start = c.contactPatches[patch].start;
				const Gu::ContactPoint* contactBase = buffer.contacts + start;
					
				PxU8* p = ptr;

				

				for(PxU32 j =0; j < count; j++)
				{
					const PxU32 contactId = totalPatchContactCount + j;
					const Gu::ContactPoint& contact = contactBase[j];
					const PxVec3 ra = contact.point - bodyFrame0.p;
					const PxVec3 rb = contact.point - bodyFrame1.p;
					
					for(PxU32 k = 0; k < frictionCountPerPoint; ++k)
					{
						PxcSolverFrictionExt* PX_RESTRICT f0 = reinterpret_cast<PxcSolverFrictionExt*>(p);
						p += frictionStride;
						f0->contactIndex = contactId;

						PxVec3 t0 = tFallback[ind];
						ind = 1 - ind;
						PxVec3 raXn = ra.cross(t0); 
						PxVec3 rbXn = rb.cross(t0); 
						Cm::SpatialVector deltaV0, deltaV1;
						PxReal unitResponse = getImpulseResponse(b0, Cm::SpatialVector(t0, raXn), deltaV0, d0, angD0,
																 b1, Cm::SpatialVector(-t0, -rbXn), deltaV1, d1, angD1);

						f0->setVelMultiplier(FLoad(unitResponse>0.0f ? 1.f/unitResponse : 0.0f));
						f0->setRaXn(raXn);
						f0->setRbXn(rbXn);
						f0->setNormal(t0);
						f0->setAppliedForce(0.0f);
						f0->setDeltaVA(deltaV0.linear, deltaV0.angular);
						f0->setDeltaVB(deltaV1.linear, deltaV1.angular);
					}					
				}

				totalPatchContactCount += c.contactPatches[patch].count;
				
				ptr = p;	
			}
		}
	}
	//PX_ASSERT(ptr - workspace == n.solverConstraintSize);
	return hasFriction;
}
///////////////////////////////////////////////////////////////////////////////
// compute intersection of a ray from a source vertex in direction toward parent
int PxClothGeodesicTetherCookerImpl::computeEdgeIntersection(PxU32 parent, PxU32 edge, float in_s, PathIntersection &path)
{
	int tid = (int)edge / 3;
	int eid = (int)edge % 3;

	PxU32 e0 = mIndices[PxU32(tid*3 + eid)];
	PxU32 e1 = mIndices[PxU32(tid*3 + (eid+1)%3)];

	PxVec3 v0 = mVertices[e0];
	PxVec3 v1 = mVertices[e1];

	PxVec3 v = v0 + in_s * (v1 - v0);
	PxVec3 g = mVertices[parent] - v;

	PxU32 triNbr = mTriNeighbors[edge];

	if (triNbr == PxU32(-1)) // boundary edge
	{
		float dir = g.dot(v1-v0);
		PxU32 vid = (dir > 0) ? e1 : e0;
		path = PathIntersection(true, vid, (mVertices[vid] - v).magnitude());
		return 1;
	}

	PxU32 i0 = mIndices[triNbr*3];
	PxU32 i1 = mIndices[triNbr*3+1];
	PxU32 i2 = mIndices[triNbr*3+2];

	// vertex is sorted s.t i0,i1 contains the edge point
	if ( checkEdge((int)i0, (int)i1, (int)e0, (int)e1)) {
		eid = 0;
	}
	else if ( checkEdge((int)i1, (int)i2, (int)e0, (int)e1)) {
		eid = 1;
		PxU32 tmp = i2;
		i2 = i0;
		i0 = i1;
		i1 = tmp;
	}
	else if ( checkEdge((int)i2, (int)i0, (int)e0, (int)e1)) 
	{
		eid = 2;
		PxU32 tmp = i0;
		i0 = i2;
		i2 = i1;
		i1 = tmp;
	}

	// we hit the parent
	if (i2 == parent)
	{
		path = PathIntersection(true, i2, (mVertices[i2] - v).magnitude());
		return 1;
	}

	PxVec3 p0 = mVertices[i0];
	PxVec3 p1 = mVertices[i1];
	PxVec3 p2 = mVertices[i2];

	// project gradient vector on the plane of the triangle
	PxVec3 n = ((p0 - p2).cross(p1 - p2)).getNormalized();
	g = (g - g.dot(n) * n).getNormalized();

	float s = 0.0f, t = 0.0f;
	const float eps = 1e-5;
	PxVec3 ip;

	// intersect against edge form p2 to p0
	if (intersectRayEdge(v, g, p2, p0, s, t) && ( s >= -eps) && ( s <= (1.0f+eps) ) && (t > -eps))
	{
		if ( ( s < eps) || (s > (1.0f-eps)))
		{
			path.vertOrTriangle = true;
			path.index = (s < eps) ? i2 : i0;
			path.distance = (mVertices[path.index] - v).magnitude();
		}
		else
		{
			ip = p2 + s * (p0 - p2);
			path = PathIntersection(false, triNbr*3 + (eid + 2) % 3, (ip - v).magnitude(), s);
			
		}

		return 1;
	}

	// intersect against edge form p1 to p2
	if (intersectRayEdge(v, g, p1, p2, s, t) && ( s >= -eps) && ( s <= (1.0f+eps) ) && (t > -eps))
	{
		if ( ( s < eps) || (s > (1.0f-eps)))
		{
			path.vertOrTriangle = true;
			path.index = (s < eps) ? i1 : i2;
			path.distance = (mVertices[path.index] - v).magnitude();
		}
		else
		{
			ip = p1 + s * (p2 - p1);
			path = PathIntersection(false, triNbr*3 + (eid + 1) % 3, (ip - v).magnitude(), s);
		}
		
		return 1;
	}

	// fallback to pick closer vertex when no edges intersect
	float dir = g.dot(v1-v0);
	path.vertOrTriangle = true;
	path.index = (dir > 0) ? e1 : e0;
	path.distance = (mVertices[path.index] - v).magnitude();

	return 1;
}