// // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions // are met: // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // * Neither the name of NVIDIA CORPORATION nor the names of its // contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY // OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. #include "foundation/PxPreprocessor.h" #include "PsVecMath.h" #include "DyArticulationContactPrep.h" #include "DySolverConstraintDesc.h" #include "DySolverConstraint1D.h" #include "DyArticulationHelper.h" #include "PxcNpWorkUnit.h" #include "PxsMaterialManager.h" #include "PxsMaterialCombiner.h" #include "DyCorrelationBuffer.h" #include "DySolverConstraintExtShared.h" #include "DyConstraintPrep.h" using namespace physx::Gu; namespace physx { namespace Dy { // constraint-gen only, since these use getVelocity methods // which aren't valid during the solver phase //PX_INLINE void computeFrictionTangents(const Ps::aos::Vec3V& vrel,const Ps::aos::Vec3V& unitNormal, Ps::aos::Vec3V& t0, Ps::aos::Vec3V& t1) //{ // using namespace Ps::aos; // //PX_ASSERT(PxAbs(unitNormal.magnitude()-1)<1e-3f); // // t0 = V3Sub(vrel, V3Scale(unitNormal, V3Dot(unitNormal, vrel))); // const FloatV ll = V3Dot(t0, t0); // // if (FAllGrtr(ll, FLoad(1e10f))) //can set as low as 0. // { // t0 = V3Scale(t0, FRsqrt(ll)); // t1 = V3Cross(unitNormal, t0); // } // else // Ps::normalToTangents(unitNormal, t0, t1); //fallback //} PxReal SolverExtBody::projectVelocity(const PxVec3& linear, const PxVec3& angular) const { if(mLinkIndex == PxSolverConstraintDesc::NO_LINK) return mBodyData->projectVelocity(linear, angular); else { const Cm::SpatialVectorV velocity = mArticulation->getLinkVelocity(mLinkIndex); const FloatV fv = velocity.dot(Cm::SpatialVector(linear, angular)); PxF32 f; FStore(fv, &f); return f; /*PxF32 f; FStore(getVelocity(*mFsData)[mLinkIndex].dot(Cm::SpatialVector(linear, angular)), &f); return f;*/ } } Ps::aos::FloatV SolverExtBody::projectVelocity(const Ps::aos::Vec3V& linear, const Ps::aos::Vec3V& angular) const { if (mLinkIndex == PxSolverConstraintDesc::NO_LINK) { return V3SumElems(V3Add(V3Mul(V3LoadA(mBodyData->linearVelocity), linear), V3Mul(V3LoadA(mBodyData->angularVelocity), angular))); } else { const Cm::SpatialVectorV velocity = mArticulation->getLinkVelocity(mLinkIndex); return velocity.dot(Cm::SpatialVectorV(linear, angular)); } } PxVec3 SolverExtBody::getLinVel() const { if(mLinkIndex == PxSolverConstraintDesc::NO_LINK) return mBodyData->linearVelocity; else { const Vec3V linear = mArticulation->getLinkVelocity(mLinkIndex).linear; PxVec3 result; V3StoreU(linear, result); /*PxVec3 result; V3StoreU(getVelocity(*mFsData)[mLinkIndex].linear, result);*/ return result; } } PxVec3 SolverExtBody::getAngVel() const { if(mLinkIndex == PxSolverConstraintDesc::NO_LINK) return mBodyData->angularVelocity; else { const Vec3V angular = mArticulation->getLinkVelocity(mLinkIndex).angular; PxVec3 result; V3StoreU(angular, result); /*PxVec3 result; V3StoreU(getVelocity(*mFsData)[mLinkIndex].angular, result);*/ return result; } } Ps::aos::Vec3V SolverExtBody::getLinVelV() const { if (mLinkIndex == PxSolverConstraintDesc::NO_LINK) return V3LoadA(mBodyData->linearVelocity); else { return mArticulation->getLinkVelocity(mLinkIndex).linear; } } Vec3V SolverExtBody::getAngVelV() const { if (mLinkIndex == PxSolverConstraintDesc::NO_LINK) return V3LoadA(mBodyData->angularVelocity); else { return mArticulation->getLinkVelocity(mLinkIndex).angular; } } Cm::SpatialVectorV SolverExtBody::getVelocity() const { if(mLinkIndex == PxSolverConstraintDesc::NO_LINK) return Cm::SpatialVectorV(V3LoadA(mBodyData->linearVelocity), V3LoadA(mBodyData->angularVelocity)); else return mArticulation->getLinkVelocity(mLinkIndex); } Cm::SpatialVector createImpulseResponseVector(const PxVec3& linear, const PxVec3& angular, const SolverExtBody& body) { if(body.mLinkIndex == PxSolverConstraintDesc::NO_LINK) return Cm::SpatialVector(linear, body.mBodyData->sqrtInvInertia * angular); return Cm::SpatialVector(linear, angular); } Cm::SpatialVectorV createImpulseResponseVector(const Ps::aos::Vec3V& linear, const Ps::aos::Vec3V& angular, const SolverExtBody& body) { if (body.mLinkIndex == PxSolverConstraintDesc::NO_LINK) { return Cm::SpatialVectorV(linear, M33MulV3(M33Load(body.mBodyData->sqrtInvInertia), angular)); } return Cm::SpatialVectorV(linear, angular); } PxReal getImpulseResponse(const SolverExtBody& b0, const Cm::SpatialVector& impulse0, Cm::SpatialVector& deltaV0, PxReal dom0, PxReal angDom0, const SolverExtBody& b1, const Cm::SpatialVector& impulse1, Cm::SpatialVector& deltaV1, PxReal dom1, PxReal angDom1, Cm::SpatialVectorF* Z, bool allowSelfCollision) { PxReal response; allowSelfCollision = false; // right now self-collision with contacts crashes the solver //KS - knocked this out to save some space on SPU if(allowSelfCollision && b0.mLinkIndex!=PxSolverConstraintDesc::NO_LINK && b0.mArticulation == b1.mArticulation && 0) { /*ArticulationHelper::getImpulseSelfResponse(*b0.mFsData,b0.mLinkIndex, impulse0, deltaV0, b1.mLinkIndex, impulse1, deltaV1);*/ b0.mArticulation->getImpulseSelfResponse(b0.mLinkIndex, b1.mLinkIndex, Z, impulse0.scale(dom0, angDom0), impulse1.scale(dom1, angDom1), deltaV0, deltaV1); response = impulse0.dot(deltaV0) + impulse1.dot(deltaV1); } else { if(b0.mLinkIndex == PxSolverConstraintDesc::NO_LINK) { deltaV0.linear = impulse0.linear * b0.mBodyData->invMass * dom0; deltaV0.angular = impulse0.angular * angDom0; } else { b0.mArticulation->getImpulseResponse(b0.mLinkIndex, Z, impulse0.scale(dom0, angDom0), deltaV0); } response = impulse0.dot(deltaV0); if(b1.mLinkIndex == PxSolverConstraintDesc::NO_LINK) { deltaV1.linear = impulse1.linear * b1.mBodyData->invMass * dom1; deltaV1.angular = impulse1.angular * angDom1; } else { b1.mArticulation->getImpulseResponse( b1.mLinkIndex, Z, impulse1.scale(dom1, angDom1), deltaV1); } response += impulse1.dot(deltaV1); } return response; } FloatV getImpulseResponse(const SolverExtBody& b0, const Cm::SpatialVectorV& impulse0, Cm::SpatialVectorV& deltaV0, const FloatV& dom0, const FloatV& angDom0, const SolverExtBody& b1, const Cm::SpatialVectorV& impulse1, Cm::SpatialVectorV& deltaV1, const FloatV& dom1, const FloatV& angDom1, Cm::SpatialVectorV* Z, bool /*allowSelfCollision*/) { Vec3V response; { if (b0.mLinkIndex == PxSolverConstraintDesc::NO_LINK) { deltaV0.linear = V3Scale(impulse0.linear, FMul(FLoad(b0.mBodyData->invMass), dom0)); deltaV0.angular = V3Scale(impulse0.angular, angDom0); } else { b0.mArticulation->getImpulseResponse(b0.mLinkIndex, Z, impulse0.scale(dom0, angDom0), deltaV0); } response = V3Add(V3Mul(impulse0.linear, deltaV0.linear), V3Mul(impulse0.angular, deltaV0.angular)); if (b1.mLinkIndex == PxSolverConstraintDesc::NO_LINK) { deltaV1.linear = V3Scale(impulse1.linear, FMul(FLoad(b1.mBodyData->invMass), dom1)); deltaV1.angular = V3Scale(impulse1.angular, angDom1); } else { b1.mArticulation->getImpulseResponse(b1.mLinkIndex, Z, impulse1.scale(dom1, angDom1), deltaV1); } response = V3Add(response, V3Add(V3Mul(impulse1.linear, deltaV1.linear), V3Mul(impulse1.angular, deltaV1.angular))); } return V3SumElems(response); } void setupFinalizeExtSolverContacts( const ContactPoint* buffer, const CorrelationBuffer& c, const PxTransform& bodyFrame0, const PxTransform& bodyFrame1, PxU8* workspace, const SolverExtBody& b0, const SolverExtBody& b1, const PxReal invDtF32, PxReal bounceThresholdF32, PxReal invMassScale0, PxReal invInertiaScale0, PxReal invMassScale1, PxReal invInertiaScale1, const PxReal restDist, PxU8* frictionDataPtr, PxReal ccdMaxContactDist, Cm::SpatialVectorF* Z) { // 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 bool haveFriction = PX_IR(n.staticFriction) > 0 || PX_IR(n.dynamicFriction) > 0;*/ const FloatV ccdMaxSeparation = FLoad(ccdMaxContactDist); PxU8* PX_RESTRICT ptr = workspace; const FloatV zero = FZero(); const PxF32 maxPenBias0 = b0.mLinkIndex == PxSolverConstraintDesc::NO_LINK ? b0.mBodyData->penBiasClamp : b0.mArticulation->getLinkMaxPenBias(b0.mLinkIndex); const PxF32 maxPenBias1 = b1.mLinkIndex == PxSolverConstraintDesc::NO_LINK ? b1.mBodyData->penBiasClamp : b1.mArticulation->getLinkMaxPenBias(b1.mLinkIndex); const FloatV maxPenBias = FLoad(PxMax(maxPenBias0, maxPenBias1)); const Vec3V frame0p = V3LoadU(bodyFrame0.p); const Vec3V frame1p = V3LoadU(bodyFrame1.p); const Cm::SpatialVectorV vel0 = b0.getVelocity(); const Cm::SpatialVectorV vel1 = b1.getVelocity(); const FloatV d0 = FLoad(invMassScale0); const FloatV d1 = FLoad(invMassScale1); const FloatV angD0 = FLoad(invInertiaScale0); const FloatV angD1 = FLoad(invInertiaScale1); Vec4V staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W = V4Zero(); staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W=V4SetZ(staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W, d0); staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W=V4SetW(staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W, d1); const FloatV restDistance = FLoad(restDist); PxU32 frictionPatchWritebackAddrIndex = 0; PxU32 contactWritebackCount = 0; Ps::prefetchLine(c.contactID); Ps::prefetchLine(c.contactID, 128); const FloatV invDt = FLoad(invDtF32); const FloatV p8 = FLoad(0.8f); const FloatV bounceThreshold = FLoad(bounceThresholdF32); const FloatV invDtp8 = FMul(invDt, p8); PxU8 flags = 0; for(PxU32 i=0;irestitution; const PxReal coefficient = (contactBase0->materialFlags & PxMaterialFlag::eIMPROVED_PATCH_FRICTION && frictionPatch.anchorCount == 2) ? 0.5f : 1.f; const PxReal staticFriction = contactBase0->staticFriction * coefficient; const PxReal dynamicFriction = contactBase0->dynamicFriction * coefficient; const bool disableStrongFriction = !!(contactBase0->materialFlags & PxMaterialFlag::eDISABLE_FRICTION); staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W=V4SetX(staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W, FLoad(staticFriction)); staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W=V4SetY(staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W, FLoad(dynamicFriction)); SolverContactHeader* PX_RESTRICT header = reinterpret_cast(ptr); ptr += sizeof(SolverContactHeader); Ps::prefetchLine(ptr + 128); Ps::prefetchLine(ptr + 256); Ps::prefetchLine(ptr + 384); const bool haveFriction = (disableStrongFriction == 0) ;//PX_IR(n.staticFriction) > 0 || PX_IR(n.dynamicFriction) > 0; header->numNormalConstr = Ps::to8(contactCount); header->numFrictionConstr = Ps::to8(haveFriction ? frictionPatch.anchorCount*2 : 0); header->type = Ps::to8(DY_SC_TYPE_EXT_CONTACT); header->flags = flags; const FloatV restitution = FLoad(combinedRestitution); header->staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W = staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W; header->angDom0 = invInertiaScale0; header->angDom1 = invInertiaScale1; const PxU32 pointStride = sizeof(SolverContactPointExt); const PxU32 frictionStride = sizeof(SolverContactFrictionExt); const Vec3V normal = V3LoadU(buffer[c.contactPatches[c.correlationListHeads[i]].start].normal); FloatV accumImpulse = FZero(); 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 + c.contactPatches[patch].start; PxU8* p = ptr; for(PxU32 j=0;j(p); p += pointStride; accumImpulse = FAdd(accumImpulse, setupExtSolverContact(b0, b1, d0, d1, angD0, angD1, frame0p, frame1p, normal, invDt, invDtp8, restDistance, maxPenBias, restitution, bounceThreshold, contact, *solverContact, ccdMaxSeparation, Z, vel0, vel1)); } ptr = p; } contactWritebackCount += contactCount; accumImpulse = FDiv(accumImpulse, FLoad(PxF32(contactCount))); header->normal_minAppliedImpulseForFrictionW = V4SetW(Vec4V_From_Vec3V(normal), accumImpulse); PxF32* forceBuffer = reinterpret_cast(ptr); PxMemZero(forceBuffer, sizeof(PxF32) * contactCount); ptr += sizeof(PxF32) * ((contactCount + 3) & (~3)); header->broken = 0; if(haveFriction) { //const Vec3V normal = Vec3V_From_PxVec3(buffer.contacts[c.contactPatches[c.correlationListHeads[i]].start].normal); //Vec3V normalS = V3LoadA(buffer[c.contactPatches[c.correlationListHeads[i]].start].normal); const Vec3V linVrel = V3Sub(vel0.linear, vel1.linear); //const Vec3V normal = Vec3V_From_PxVec3_Aligned(buffer.contacts[c.contactPatches[c.correlationListHeads[i]].start].normal); const FloatV orthoThreshold = FLoad(0.70710678f); const FloatV p1 = FLoad(0.0001f); // fallback: normal.cross((1,0,0)) or normal.cross((0,0,1)) const FloatV normalX = V3GetX(normal); const FloatV normalY = V3GetY(normal); const FloatV normalZ = V3GetZ(normal); Vec3V t0Fallback1 = V3Merge(zero, FNeg(normalZ), normalY); Vec3V t0Fallback2 = V3Merge(FNeg(normalY), normalX, zero); Vec3V t0Fallback = V3Sel(FIsGrtr(orthoThreshold, FAbs(normalX)), t0Fallback1, t0Fallback2); Vec3V t0 = V3Sub(linVrel, V3Scale(normal, V3Dot(normal, linVrel))); t0 = V3Sel(FIsGrtr(V3LengthSq(t0), p1), t0, t0Fallback); t0 = V3Normalize(t0); const VecCrossV t0Cross = V3PrepareCross(t0); const Vec3V t1 = V3Cross(normal, t0Cross); const VecCrossV t1Cross = V3PrepareCross(t1); //We want to set the writeBack ptr to point to the broken flag of the friction patch. //On spu we have a slight problem here because the friction patch array is //in local store rather than in main memory. The good news is that the address of the friction //patch array in main memory is stored in the work unit. These two addresses will be equal //except on spu where one is local store memory and the other is the effective address in main memory. //Using the value stored in the work unit guarantees that the main memory address is used on all platforms. PxU8* PX_RESTRICT writeback = frictionDataPtr + frictionPatchWritebackAddrIndex*sizeof(FrictionPatch); header->frictionBrokenWritebackByte = writeback; for(PxU32 j = 0; j < frictionPatch.anchorCount; j++) { SolverContactFrictionExt* PX_RESTRICT f0 = reinterpret_cast(ptr); ptr += frictionStride; SolverContactFrictionExt* PX_RESTRICT f1 = reinterpret_cast(ptr); ptr += frictionStride; Vec3V ra = V3LoadU(bodyFrame0.q.rotate(frictionPatch.body0Anchors[j])); Vec3V rb = V3LoadU(bodyFrame1.q.rotate(frictionPatch.body1Anchors[j])); Vec3V error = V3Sub(V3Add(ra, frame0p), V3Add(rb, frame1p)); { const Vec3V raXn = V3Cross(ra, t0Cross); const Vec3V rbXn = V3Cross(rb, t0Cross); Cm::SpatialVectorV deltaV0, deltaV1; const Cm::SpatialVectorV resp0 = createImpulseResponseVector(t0, raXn, b0); const Cm::SpatialVectorV resp1 = createImpulseResponseVector(V3Neg(t0), V3Neg(rbXn), b1); FloatV resp = getImpulseResponse(b0, resp0, deltaV0, d0, angD0, b1, resp1, deltaV1, d1, angD1, reinterpret_cast(Z)); const FloatV velMultiplier = FSel(FIsGrtr(resp, FLoad(DY_ARTICULATION_MIN_RESPONSE)), FDiv(p8, resp), zero); const PxU32 index = c.contactPatches[c.correlationListHeads[i]].start; FloatV targetVel = V3Dot(V3LoadA(buffer[index].targetVel), t0); if(b0.mLinkIndex == PxSolverConstraintDesc::NO_LINK) targetVel = FSub(targetVel, b0.projectVelocity(t0, raXn)); else if(b1.mLinkIndex == PxSolverConstraintDesc::NO_LINK) targetVel = FAdd(targetVel, b1.projectVelocity(t0, rbXn)); f0->normalXYZ_appliedForceW = V4SetW(t0, zero); f0->raXnXYZ_velMultiplierW = V4SetW(Vec4V_From_Vec3V(resp0.angular), velMultiplier); f0->rbXnXYZ_biasW = V4SetW(V4Neg(Vec4V_From_Vec3V(resp1.angular)), FMul(V3Dot(t0, error), invDt)); f0->linDeltaVA = deltaV0.linear; f0->angDeltaVA = deltaV0.angular; f0->linDeltaVB = deltaV1.linear; f0->angDeltaVB = deltaV1.angular; FStore(targetVel, &f0->targetVel); } { const Vec3V raXn = V3Cross(ra, t1Cross); const Vec3V rbXn = V3Cross(rb, t1Cross); Cm::SpatialVectorV deltaV0, deltaV1; const Cm::SpatialVectorV resp0 = createImpulseResponseVector(t1, raXn, b0); const Cm::SpatialVectorV resp1 = createImpulseResponseVector(V3Neg(t1), V3Neg(rbXn), b1); FloatV resp = getImpulseResponse(b0, resp0, deltaV0, d0, angD0, b1, resp1, deltaV1, d1, angD1, reinterpret_cast(Z)); //const FloatV velMultiplier = FSel(FIsGrtr(resp, FLoad(DY_ARTICULATION_MIN_RESPONSE)), FMul(p8, FRecip(resp)), zero); const FloatV velMultiplier = FSel(FIsGrtr(resp, FLoad(DY_ARTICULATION_MIN_RESPONSE)), FDiv(p8, resp), zero); const PxU32 index = c.contactPatches[c.correlationListHeads[i]].start; FloatV targetVel = V3Dot(V3LoadA(buffer[index].targetVel), t1); if(b0.mLinkIndex == PxSolverConstraintDesc::NO_LINK) targetVel = FSub(targetVel, b0.projectVelocity(t1, raXn)); else if(b1.mLinkIndex == PxSolverConstraintDesc::NO_LINK) targetVel = FAdd(targetVel, b1.projectVelocity(t1, rbXn)); f1->normalXYZ_appliedForceW = V4SetW(t1, zero); f1->raXnXYZ_velMultiplierW = V4SetW(Vec4V_From_Vec3V(resp0.angular), velMultiplier); f1->rbXnXYZ_biasW = V4SetW(V4Neg(Vec4V_From_Vec3V(resp1.angular)), FMul(V3Dot(t1, error), invDt)); f1->linDeltaVA = deltaV0.linear; f1->angDeltaVA = deltaV0.angular; f1->linDeltaVB = deltaV1.linear; f1->angDeltaVB = deltaV1.angular; FStore(targetVel, &f1->targetVel); } } } frictionPatchWritebackAddrIndex++; } } } }