// 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 }
// 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 ¢er, 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; }
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])); } } } }
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 ¢er, 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; }