// // 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-2018 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 "GuConvexUtilsInternal.h" #include "GuInternal.h" #include "GuContactPolygonPolygon.h" #include "GuConvexEdgeFlags.h" #include "GuSeparatingAxes.h" #include "GuContactMethodImpl.h" #include "GuContactBuffer.h" #include "GuMidphaseInterface.h" #include "GuConvexHelper.h" #include "GuTriangleCache.h" #include "GuHeightFieldUtil.h" #include "GuEntityReport.h" #include "GuGeometryUnion.h" #include "GuIntersectionTriangleBox.h" #include "CmUtils.h" #include "PsAllocator.h" #include "GuBox.h" #include "PsFPU.h" using namespace physx; using namespace Gu; using namespace shdfnd::aos; using namespace intrinsics; #define LOCAL_TOUCHED_TRIG_SIZE 192 //#define USE_TRIANGLE_NORMAL #define TEST_INTERNAL_OBJECTS static PX_FORCE_INLINE void projectTriangle(const PxVec3& localSpaceDirection, const PxVec3* PX_RESTRICT triangle, PxReal& min1, PxReal& max1) { const PxReal dp0 = triangle[0].dot(localSpaceDirection); const PxReal dp1 = triangle[1].dot(localSpaceDirection); min1 = selectMin(dp0, dp1); max1 = selectMax(dp0, dp1); const PxReal dp2 = triangle[2].dot(localSpaceDirection); min1 = selectMin(min1, dp2); max1 = selectMax(max1, dp2); } #ifdef TEST_INTERNAL_OBJECTS static PX_FORCE_INLINE void boxSupport(const float extents[3], const PxVec3& sv, float p[3]) { const PxU32* iextents = reinterpret_cast(extents); const PxU32* isv = reinterpret_cast(&sv); PxU32* ip = reinterpret_cast(p); ip[0] = iextents[0]|(isv[0]&PX_SIGN_BITMASK); ip[1] = iextents[1]|(isv[1]&PX_SIGN_BITMASK); ip[2] = iextents[2]|(isv[2]&PX_SIGN_BITMASK); } #if PX_DEBUG static const PxReal testInternalObjectsEpsilon = 1.0e-3f; #endif static PX_FORCE_INLINE bool testInternalObjects(const PxVec3& localAxis0, const PolygonalData& polyData0, const PxVec3* PX_RESTRICT triangleInHullSpace, float dmin) { PxReal min1, max1; projectTriangle(localAxis0, triangleInHullSpace, min1, max1); const float dp = polyData0.mCenter.dot(localAxis0); float p0[3]; boxSupport(polyData0.mInternal.mExtents, localAxis0, p0); const float Radius0 = p0[0]*localAxis0.x + p0[1]*localAxis0.y + p0[2]*localAxis0.z; const float bestRadius = selectMax(Radius0, polyData0.mInternal.mRadius); const PxReal min0 = dp - bestRadius; const PxReal max0 = dp + bestRadius; const PxReal d0 = max0 - min1; const PxReal d1 = max1 - min0; const float depth = selectMin(d0, d1); if(depth>dmin) return false; return true; } #endif static PX_FORCE_INLINE bool testNormal( const PxVec3& sepAxis, PxReal min0, PxReal max0, const PxVec3* PX_RESTRICT triangle, PxReal& depth, PxReal contactDistance) { PxReal min1, max1; projectTriangle(sepAxis, triangle, min1, max1); if(max0+contactDistance 0.0f) #else // ### this is dubious since the triangle center is likely to be very close to the hull, if not inside. Why not use the triangle normal? if(PL.distance(witness) < 0.0f) #endif continue; //backface culled *hullIndices++ = i; const PxVec3 sepAxis = m0to1.rotate(PL.n); const PxReal dp = sepAxis.dot(trans); PxReal d; if(!testNormal(sepAxis, P.getMin(vertices) + dp, P.getMax() + dp, triangle, d, contactDistance)) return false; if(d < dmin) { dmin = d; sep = sepAxis; id = i; } } } else { #ifndef USE_TRIANGLE_NORMAL //transform delta from hull0 shape into vertex space: const PxVec3 vertSpaceWitness = convexScaling % witness; #endif for(PxU32 i=0; i 0.0f) #else // ### this is dubious since the triangle center is likely to be very close to the hull, if not inside. Why not use the triangle normal? if(PL.distance(vertSpaceWitness) < 0.0f) #endif continue; //backface culled //normals transform by inverse transpose: (and transpose(skew) == skew as its symmetric) PxVec3 shapeSpaceNormal = convexScaling % PL.n; //renormalize: (Arr!) const PxReal magnitude = shapeSpaceNormal.normalize(); *hullIndices++ = i; const PxVec3 sepAxis = m0to1.rotate(shapeSpaceNormal); PxReal d; const PxReal dp = sepAxis.dot(trans); const float oneOverM = 1.0f / magnitude; if(!testNormal(sepAxis, P.getMin(vertices) * oneOverM + dp, P.getMax() * oneOverM + dp, triangle, d, contactDistance)) return false; if(d < dmin) { dmin = d; sep = sepAxis; id = i; } } } numHullIndices = PxU32(hullIndices - hullIndices_); } // Backup if(id == PX_INVALID_U32) { if(idtConvexScale) { for(PxU32 i=0; i=numVerts) j1 = 0; const PxU32 VRef0 = data[j]; const PxU32 VRef1 = data[j1]; if(edgeCulling(vertexSpacePlane, hullVerts[VRef0], hullVerts[VRef1], contactDistance)) { const PxVec3 currentHullEdge = m0to1.rotate(convexScaling * (hullVerts[VRef0] - hullVerts[VRef1])); //matrix mult is distributive! PxVec3 sepAxis = currentHullEdge.cross(currentPolyEdge); if(!Ps::isAlmostZero(sepAxis)) SA.addAxis(sepAxis.getNormalized()); } } } } dmin = PX_MAX_REAL; PxU32 numAxes = SA.getNumAxes(); const PxVec3* PX_RESTRICT axes = SA.getAxes(); #ifdef TEST_INTERNAL_OBJECTS PxVec3 triangleInHullSpace[3]; if(numAxes) { triangleInHullSpace[0] = m1to0.transform(triangle[0]); triangleInHullSpace[1] = m1to0.transform(triangle[1]); triangleInHullSpace[2] = m1to0.transform(triangle[2]); } #endif while(numAxes--) { const PxVec3& currentAxis = *axes++; #ifdef TEST_INTERNAL_OBJECTS const PxVec3 localAxis0 = m1to0.rotate(currentAxis); if(!testInternalObjects(localAxis0, polyData0, triangleInHullSpace, dmin)) { #if PX_DEBUG PxReal dtest; if(testSepAxis(currentAxis, polyData0, triangle, m0to1, convexScaling, dtest, contactDistance)) { PX_ASSERT(dtest + testInternalObjectsEpsilon*toleranceLength >= dmin); } #endif continue; } #endif PxReal d; if(!testSepAxis(currentAxis, polyData0, triangle, m0to1, convexScaling, d, contactDistance)) { return false; } if(d < dmin) { dmin = d; vec = currentAxis; } } return true; } static bool triangleConvexTest( const PolygonalData& polyData0, const PxU8 triFlags, PxU32 index, const PxVec3* PX_RESTRICT localPoints, const PxPlane& localPlane, const PxVec3& groupCenterHull, const Cm::Matrix34& world0, const Cm::Matrix34& world1, const Cm::Matrix34& m0to1, const Cm::Matrix34& m1to0, const Cm::FastVertex2ShapeScaling& convexScaling, PxReal contactDistance, PxReal toleranceLength, PxVec3& groupAxis, PxReal& groupMinDepth, bool& faceContact, bool idtConvexScale ) { PxU32 id0 = PX_INVALID_U32; PxReal dmin0 = PX_MAX_REAL; PxVec3 vec0; PxU32 numHullIndices = 0; PxU32* PX_RESTRICT const hullIndices = reinterpret_cast(PxAlloca(polyData0.mNbPolygons*sizeof(PxU32))); // PT: we test the hull normals first because they don't need any hull projection. If we can early exit thanks // to those, we completely avoid all hull projections. bool status = testFacesSepAxesBackface(polyData0, world0, world1, m0to1, groupCenterHull, localPoints, convexScaling, numHullIndices, hullIndices, dmin0, vec0, id0, contactDistance, idtConvexScale); if(!status) return false; groupAxis = PxVec3(0); groupMinDepth = PX_MAX_REAL; const PxReal eps = 0.0001f; // DE7748 //Test in mesh-space PxVec3 sepAxis; PxReal depth; { // Test triangle normal PxReal d; if(!testSepAxis(localPlane.n, polyData0, localPoints, m0to1, convexScaling, d, contactDistance)) return false; if(d& mDelayedContacts; Gu::CacheMap mEdgeCache; Gu::CacheMap mVertCache; const Cm::Matrix34 m0to1; const Cm::Matrix34 m1to0; PxVec3 mHullCenterMesh; PxVec3 mHullCenterWorld; const PolygonalData& mPolyData0; const Cm::Matrix34& mWorld0; const Cm::Matrix34& mWorld1; const Cm::FastVertex2ShapeScaling& mConvexScaling; PxReal mContactDistance; PxReal mToleranceLength; bool mIdtMeshScale, mIdtConvexScale; PxReal mCCDEpsilon; const PxTransform& mTransform0; const PxTransform& mTransform1; ContactBuffer& mContactBuffer; bool mAnyHits; ConvexMeshContactGeneration( Ps::InlineArray& delayedContacts, const PxTransform& t0to1, const PxTransform& t1to0, const PolygonalData& polyData0, const Cm::Matrix34& world0, const Cm::Matrix34& world1, const Cm::FastVertex2ShapeScaling& convexScaling, PxReal contactDistance, PxReal toleranceLength, bool idtConvexScale, PxReal cCCDEpsilon, const PxTransform& transform0, const PxTransform& transform1, ContactBuffer& contactBuffer ); void processTriangle(const PxVec3* verts, PxU32 triangleIndex, PxU8 triFlags, const PxU32* vertInds); void generateLastContacts(); bool generateContacts( const PxPlane& localPlane, const PxVec3* PX_RESTRICT localPoints, const PxVec3& triCenter, PxVec3& groupAxis, PxReal groupMinDepth, PxU32 index) const; private: ConvexMeshContactGeneration& operator=(const ConvexMeshContactGeneration&); }; // 17 entries. 1088/17 = 64 triangles in the local array struct SavedContactData { PxU32 mTriangleIndex; PxVec3 mVerts[3]; PxU32 mInds[3]; PxVec3 mGroupAxis; PxReal mGroupMinDepth; }; } ConvexMeshContactGeneration::ConvexMeshContactGeneration( Ps::InlineArray& delayedContacts, const PxTransform& t0to1, const PxTransform& t1to0, const PolygonalData& polyData0, const Cm::Matrix34& world0, const Cm::Matrix34& world1, const Cm::FastVertex2ShapeScaling& convexScaling, PxReal contactDistance, PxReal toleranceLength, bool idtConvexScale, PxReal cCCDEpsilon, const PxTransform& transform0, const PxTransform& transform1, ContactBuffer& contactBuffer ) : mDelayedContacts(delayedContacts), m0to1 (t0to1), m1to0 (t1to0), mPolyData0 (polyData0), mWorld0 (world0), mWorld1 (world1), mConvexScaling (convexScaling), mContactDistance(contactDistance), mToleranceLength(toleranceLength), mIdtConvexScale (idtConvexScale), mCCDEpsilon (cCCDEpsilon), mTransform0 (transform0), mTransform1 (transform1), mContactBuffer (contactBuffer) { delayedContacts.forceSize_Unsafe(0); mAnyHits = false; // Hull center in local space const PxVec3& hullCenterLocal = mPolyData0.mCenter; // Hull center in mesh space mHullCenterMesh = m0to1.transform(hullCenterLocal); // Hull center in world space mHullCenterWorld = mWorld0.transform(hullCenterLocal); } struct ConvexMeshContactGenerationCallback : MeshHitCallback { ConvexMeshContactGeneration mGeneration; const Cm::FastVertex2ShapeScaling& mMeshScaling; const PxU8* PX_RESTRICT mExtraTrigData; bool mIdtMeshScale; const TriangleMesh* mMeshData; const BoxPadded& mBox; ConvexMeshContactGenerationCallback( Ps::InlineArray& delayedContacts, const PxTransform& t0to1, const PxTransform& t1to0, const PolygonalData& polyData0, const Cm::Matrix34& world0, const Cm::Matrix34& world1, const TriangleMesh* meshData, const PxU8* PX_RESTRICT extraTrigData, const Cm::FastVertex2ShapeScaling& meshScaling, const Cm::FastVertex2ShapeScaling& convexScaling, PxReal contactDistance, PxReal toleranceLength, bool idtMeshScale, bool idtConvexScale, PxReal cCCDEpsilon, const PxTransform& transform0, const PxTransform& transform1, ContactBuffer& contactBuffer, const BoxPadded& box ) : MeshHitCallback (CallbackMode::eMULTIPLE), mGeneration (delayedContacts, t0to1, t1to0, polyData0, world0, world1, convexScaling, contactDistance, toleranceLength, idtConvexScale, cCCDEpsilon, transform0, transform1, contactBuffer), mMeshScaling (meshScaling), mExtraTrigData (extraTrigData), mIdtMeshScale (idtMeshScale), mMeshData (meshData), mBox (box) { } virtual PxAgain processHit( // all reported coords are in mesh local space including hit.position const PxRaycastHit& hit, const PxVec3& v0, const PxVec3& v1, const PxVec3& v2, PxReal&, const PxU32* vinds) { // PT: this one is safe because incoming vertices from midphase are always safe to V4Load (by design) // PT: TODO: is this test really needed? Not done in midphase already? if(!intersectTriangleBox(mBox, v0, v1, v2)) return true; PxVec3 verts[3]; getScaledVertices(verts, v0, v1, v2, mIdtMeshScale, mMeshScaling); const PxU32 triangleIndex = hit.faceIndex; const PxU8 extraData = getConvexEdgeFlags(mExtraTrigData, triangleIndex); mGeneration.processTriangle(verts, triangleIndex, extraData, vinds); return true; } protected: ConvexMeshContactGenerationCallback &operator=(const ConvexMeshContactGenerationCallback &); }; bool ConvexMeshContactGeneration::generateContacts( const PxPlane& localPlane, const PxVec3* PX_RESTRICT localPoints, const PxVec3& triCenter, PxVec3& groupAxis, PxReal groupMinDepth, PxU32 index) const { const PxVec3 worldGroupCenter = mWorld1.transform(triCenter); const PxVec3 deltaC = mHullCenterWorld - worldGroupCenter; if(deltaC.dot(groupAxis) < 0.0f) groupAxis = -groupAxis; const PxU32 id = (mPolyData0.mSelectClosestEdgeCB)(mPolyData0, mConvexScaling, mWorld0.rotateTranspose(-groupAxis)); const HullPolygonData& HP = mPolyData0.mPolygons[id]; PX_ALIGN(16, PxPlane) shapeSpacePlane0; if(mIdtConvexScale) V4StoreA(V4LoadU(&HP.mPlane.n.x), &shapeSpacePlane0.n.x); else mConvexScaling.transformPlaneToShapeSpace(HP.mPlane.n, HP.mPlane.d, shapeSpacePlane0.n, shapeSpacePlane0.d); const PxVec3 hullNormalWorld = mWorld0.rotate(shapeSpacePlane0.n); const PxReal d0 = PxAbs(hullNormalWorld.dot(groupAxis)); const PxVec3 triNormalWorld = mWorld1.rotate(localPlane.n); const PxReal d1 = PxAbs(triNormalWorld.dot(groupAxis)); const bool d0biggerd1 = d0 > d1; ////////////////////NEW DIST HANDLING////////////////////// //TODO: skip this if there is no dist involved! PxReal separation = - groupMinDepth; //convert to real distance. separation = fsel(separation, separation, 0.0f); //don't do anything when penetrating! //printf("\nseparation = %f", separation); PxReal contactGenPositionShift = separation + mCCDEpsilon; //if we're at a distance, shift so we're within penetration. PxVec3 contactGenPositionShiftVec = groupAxis * contactGenPositionShift; //shift one of the bodies this distance toward the other just for Pierre's contact generation. Then the bodies should be penetrating exactly by MIN_SEPARATION_FOR_PENALTY - ideal conditions for this contact generator. //note: for some reason this has to change sign! //this will make contact gen always generate contacts at about MSP. Shift them back to the true real distance, and then to a solver compliant distance given that //the solver converges to MSP penetration, while we want it to converge to 0 penetration. //to real distance: // PxReal polyPolySeparationShift = separation; //(+ or - depending on which way normal goes) //The system: We always shift convex 0 (arbitrary). If the contact is attached to convex 0 then we will need to shift the contact point, otherwise not. //TODO: make these overwrite orig location if its safe to do so. Cm::Matrix34 world0_(mWorld0); PxTransform transform0_(mTransform0); world0_.p -= contactGenPositionShiftVec; transform0_.p = world0_.p; //reset this too. PxTransform t0to1_ = mTransform1.transformInv(transform0_); PxTransform t1to0_ = transform0_.transformInv(mTransform1); Cm::Matrix34 m0to1_(t0to1_); Cm::Matrix34 m1to0_(t1to0_); PxVec3* scaledVertices0; PxU8* stackIndices0; GET_SCALEX_CONVEX(scaledVertices0, stackIndices0, mIdtConvexScale, HP.mNbVerts, mConvexScaling, mPolyData0.mVerts, mPolyData0.getPolygonVertexRefs(HP)) const PxU8 indices[3] = {0, 1, 2}; const PxMat33 RotT0 = findRotationMatrixFromZ(shapeSpacePlane0.n); const PxMat33 RotT1 = findRotationMatrixFromZ(localPlane.n); if(d0biggerd1) { if(contactPolygonPolygonExt( HP.mNbVerts, scaledVertices0, stackIndices0, world0_, shapeSpacePlane0, RotT0, 3, localPoints, indices, mWorld1, localPlane, RotT1, hullNormalWorld, m0to1_, m1to0_, PXC_CONTACT_NO_FACE_INDEX, index, mContactBuffer, true, contactGenPositionShiftVec, contactGenPositionShift)) { return true; } } else { if(contactPolygonPolygonExt( 3, localPoints, indices, mWorld1, localPlane, RotT1, HP.mNbVerts, scaledVertices0, stackIndices0, world0_, shapeSpacePlane0, RotT0, triNormalWorld, m1to0_, m0to1_, PXC_CONTACT_NO_FACE_INDEX, index, mContactBuffer, false, contactGenPositionShiftVec, contactGenPositionShift)) { return true; } } return false; } enum FeatureCode { FC_VERTEX0, FC_VERTEX1, FC_VERTEX2, FC_EDGE01, FC_EDGE12, FC_EDGE20, FC_FACE, FC_UNDEFINED }; static FeatureCode computeFeatureCode(const PxVec3& point, const PxVec3* verts) { const PxVec3& triangleOrigin = verts[0]; const PxVec3 triangleEdge0 = verts[1] - verts[0]; const PxVec3 triangleEdge1 = verts[2] - verts[0]; const PxVec3 kDiff = triangleOrigin - point; const PxReal fA00 = triangleEdge0.magnitudeSquared(); const PxReal fA01 = triangleEdge0.dot(triangleEdge1); const PxReal fA11 = triangleEdge1.magnitudeSquared(); const PxReal fB0 = kDiff.dot(triangleEdge0); const PxReal fB1 = kDiff.dot(triangleEdge1); const PxReal fDet = PxAbs(fA00*fA11 - fA01*fA01); const PxReal u = fA01*fB1-fA11*fB0; const PxReal v = fA01*fB0-fA00*fB1; FeatureCode fc = FC_UNDEFINED; if(u + v <= fDet) { if(u < 0.0f) { if(v < 0.0f) // region 4 { if(fB0 < 0.0f) { if(-fB0 >= fA00) fc = FC_VERTEX1; else fc = FC_EDGE01; } else { if(fB1 >= 0.0f) fc = FC_VERTEX0; else if(-fB1 >= fA11) fc = FC_VERTEX2; else fc = FC_EDGE20; } } else // region 3 { if(fB1 >= 0.0f) fc = FC_VERTEX0; else if(-fB1 >= fA11) fc = FC_VERTEX2; else fc = FC_EDGE20; } } else if(v < 0.0f) // region 5 { if(fB0 >= 0.0f) fc = FC_VERTEX0; else if(-fB0 >= fA00) fc = FC_VERTEX1; else fc = FC_EDGE01; } else // region 0 { // minimum at interior PxVec3 if(fDet==0.0f) fc = FC_VERTEX0; else fc = FC_FACE; } } else { PxReal fTmp0, fTmp1, fNumer, fDenom; if(u < 0.0f) // region 2 { fTmp0 = fA01 + fB0; fTmp1 = fA11 + fB1; if(fTmp1 > fTmp0) { fNumer = fTmp1 - fTmp0; fDenom = fA00-2.0f*fA01+fA11; if(fNumer >= fDenom) fc = FC_VERTEX1; else fc = FC_EDGE12; } else { if(fTmp1 <= 0.0f) fc = FC_VERTEX2; else if(fB1 >= 0.0f) fc = FC_VERTEX0; else fc = FC_EDGE20; } } else if(v < 0.0f) // region 6 { fTmp0 = fA01 + fB1; fTmp1 = fA00 + fB0; if(fTmp1 > fTmp0) { fNumer = fTmp1 - fTmp0; fDenom = fA00-2.0f*fA01+fA11; if(fNumer >= fDenom) fc = FC_VERTEX2; else fc = FC_EDGE12; } else { if(fTmp1 <= 0.0f) fc = FC_VERTEX1; else if(fB0 >= 0.0f) fc = FC_VERTEX0; else fc = FC_EDGE01; } } else // region 1 { fNumer = fA11 + fB1 - fA01 - fB0; if(fNumer <= 0.0f) { fc = FC_VERTEX2; } else { fDenom = fA00-2.0f*fA01+fA11; if(fNumer >= fDenom) fc = FC_VERTEX1; else fc = FC_EDGE12; } } } return fc; } //static bool validateVertex(PxU32 vref, const PxU32 count, const ContactPoint* PX_RESTRICT contacts, const TriangleMesh& meshData) //{ // PxU32 previous = 0xffffffff; // for(PxU32 i=0;itvref1) // Ps::swap(tvref0, tvref1); // // if(tvref0==vref0 && tvref1==vref1) // return false; // return true; //} //static bool validateEdge(PxU32 vref0, PxU32 vref1, const PxU32 count, const ContactPoint* PX_RESTRICT contacts, const TriangleMesh& meshData) //{ // if(vref0>vref1) // Ps::swap(vref0, vref1); // // PxU32 previous = 0xffffffff; // for(PxU32 i=0;ivref1) // Ps::swap(vref0, vref1); // // for(PxU32 i=0;i(mDelayedContacts.end()); mDelayedContacts.forceSize_Unsafe(newSize); cd->mTriangleIndex = triangleIndex; cd->mVerts[0] = verts[0]; cd->mVerts[1] = verts[1]; cd->mVerts[2] = verts[2]; cd->mInds[0] = vertInds[0]; cd->mInds[1] = vertInds[1]; cd->mInds[2] = vertInds[2]; cd->mGroupAxis = groupAxis; cd->mGroupMinDepth = groupMinDepth; } } void ConvexMeshContactGeneration::generateLastContacts() { // Process delayed contacts PxU32 nbEntries = mDelayedContacts.size(); if(nbEntries) { nbEntries /= sizeof(SavedContactData)/sizeof(PxU32); // PT: TODO: replicate this fix in sphere-vs-mesh ###FIX //const PxU32 count = mContactBuffer.count; //const ContactPoint* PX_RESTRICT contacts = mContactBuffer.contacts; const SavedContactData* PX_RESTRICT cd = reinterpret_cast(mDelayedContacts.begin()); for(PxU32 i=0;i delayedContacts; ConvexMeshContactGenerationCallback blockCallback( delayedContacts, t0to1, t1to0, polyData0, world0, world1, meshData, meshData->getExtraTrigData(), meshScaling, convexScaling, params.mContactDistance, params.mToleranceLength, idtMeshScale, idtConvexScale, params.mMeshContactMargin, transform0, transform1, contactBuffer, hullOBB ); Midphase::intersectOBB(meshData, hullOBB, blockCallback, false); blockCallback.mGeneration.generateLastContacts(); return blockCallback.mGeneration.mAnyHits; } ///////////// bool Gu::contactConvexMesh(GU_CONTACT_METHOD_ARGS) { PX_UNUSED(cache); PX_UNUSED(renderOutput); const PxTriangleMeshGeometryLL& shapeMesh = shape1.get(); const bool idtScaleMesh = shapeMesh.scale.isIdentity(); Cm::FastVertex2ShapeScaling meshScaling; if(!idtScaleMesh) meshScaling.init(shapeMesh.scale); Cm::FastVertex2ShapeScaling convexScaling; PxBounds3 hullAABB; PolygonalData polyData0; const bool idtScaleConvex = getConvexData(shape0, convexScaling, hullAABB, polyData0); return contactHullMesh2(polyData0, hullAABB, shapeMesh, transform0, transform1, params, contactBuffer, convexScaling, meshScaling, idtScaleConvex, idtScaleMesh); } bool Gu::contactBoxMesh(GU_CONTACT_METHOD_ARGS) { PX_UNUSED(cache); PX_UNUSED(renderOutput); const PxBoxGeometry& shapeBox = shape0.get(); const PxTriangleMeshGeometryLL& shapeMesh = shape1.get(); PolygonalData polyData0; PolygonalBox polyBox(shapeBox.halfExtents); polyBox.getPolygonalData(&polyData0); const PxBounds3 hullAABB(-shapeBox.halfExtents, shapeBox.halfExtents); const bool idtScaleMesh = shapeMesh.scale.isIdentity(); Cm::FastVertex2ShapeScaling meshScaling; if(!idtScaleMesh) meshScaling.init(shapeMesh.scale); Cm::FastVertex2ShapeScaling idtScaling; return contactHullMesh2(polyData0, hullAABB, shapeMesh, transform0, transform1, params, contactBuffer, idtScaling, meshScaling, true, idtScaleMesh); } ///////////// namespace { struct ConvexVsHeightfieldContactGenerationCallback : EntityReport { ConvexMeshContactGeneration mGeneration; HeightFieldUtil& mHfUtil; ConvexVsHeightfieldContactGenerationCallback( HeightFieldUtil& hfUtil, Ps::InlineArray& delayedContacts, const PxTransform& t0to1, const PxTransform& t1to0, const PolygonalData& polyData0, const Cm::Matrix34& world0, const Cm::Matrix34& world1, const Cm::FastVertex2ShapeScaling& convexScaling, PxReal contactDistance, PxReal toleranceLength, bool idtConvexScale, PxReal cCCDEpsilon, const PxTransform& transform0, const PxTransform& transform1, ContactBuffer& contactBuffer ) : mGeneration(delayedContacts, t0to1, t1to0, polyData0, world0, world1, convexScaling, contactDistance, toleranceLength, idtConvexScale, cCCDEpsilon, transform0, transform1, contactBuffer), mHfUtil(hfUtil) { } // PT: TODO: refactor/unify with similar code in other places virtual bool onEvent(PxU32 nb, PxU32* indices) { const PxU8 nextInd[] = {2,0,1}; while(nb--) { const PxU32 triangleIndex = *indices++; PxU32 vertIndices[3]; PxTriangle currentTriangle; // in world space PxU32 adjInds[3]; mHfUtil.getTriangle(mGeneration.mTransform1, currentTriangle, vertIndices, adjInds, triangleIndex, false, false); PxVec3 normal; currentTriangle.normal(normal); PxU8 triFlags = 0; //KS - temporary until we can calculate triFlags for HF for(PxU32 a = 0; a < 3; ++a) { if(adjInds[a] != 0xFFFFFFFF) { PxTriangle adjTri; mHfUtil.getTriangle(mGeneration.mTransform1, adjTri, NULL, NULL, adjInds[a], false, false); //We now compare the triangles to see if this edge is active PxVec3 adjNormal; adjTri.denormalizedNormal(adjNormal); PxU32 otherIndex = nextInd[a]; PxF32 projD = adjNormal.dot(currentTriangle.verts[otherIndex] - adjTri.verts[0]); if(projD < 0.f) { adjNormal.normalize(); PxF32 proj = adjNormal.dot(normal); if(proj < 0.999f) { triFlags |= 1 << (a+3); } } } else triFlags |= (1 << (a+3)); } mGeneration.processTriangle(currentTriangle.verts, triangleIndex, triFlags, vertIndices); } return true; } protected: ConvexVsHeightfieldContactGenerationCallback &operator=(const ConvexVsHeightfieldContactGenerationCallback &); }; } static bool contactHullHeightfield2(const PolygonalData& polyData0, const PxBounds3& hullAABB, const PxHeightFieldGeometry& shape1, const PxTransform& transform0, const PxTransform& transform1, const NarrowPhaseParams& params, ContactBuffer& contactBuffer, const Cm::FastVertex2ShapeScaling& convexScaling, bool idtConvexScale) { //We need to create a callback that fills triangles from the HF HeightFieldUtil hfUtil(shape1); const Cm::Matrix34 world0(transform0); const Cm::Matrix34 world1(transform1); //////////////////// // Compute relative transforms const PxTransform t0to1 = transform1.transformInv(transform0); const PxTransform t1to0 = transform0.transformInv(transform1); Ps::InlineArray delayedContacts; ConvexVsHeightfieldContactGenerationCallback blockCallback(hfUtil, delayedContacts, t0to1, t1to0, polyData0, world0, world1, convexScaling, params.mContactDistance, params.mToleranceLength, idtConvexScale, params.mMeshContactMargin, transform0, transform1, contactBuffer); hfUtil.overlapAABBTriangles(transform1, PxBounds3::transformFast(t0to1, hullAABB), 0, &blockCallback); blockCallback.mGeneration.generateLastContacts(); return blockCallback.mGeneration.mAnyHits; } bool Gu::contactConvexHeightfield(GU_CONTACT_METHOD_ARGS) { PX_UNUSED(cache); PX_UNUSED(renderOutput); //Create a triangle cache from the HF triangles and then feed triangles to NP mesh methods const physx::PxHeightFieldGeometryLL& shapeMesh = shape1.get(); Cm::FastVertex2ShapeScaling convexScaling; PxBounds3 hullAABB; PolygonalData polyData0; const bool idtScaleConvex = getConvexData(shape0, convexScaling, hullAABB, polyData0); const PxVec3 inflation(params.mContactDistance); hullAABB.minimum -= inflation; hullAABB.maximum += inflation; return contactHullHeightfield2(polyData0, hullAABB, shapeMesh, transform0, transform1, params, contactBuffer, convexScaling, idtScaleConvex); } bool Gu::contactBoxHeightfield(GU_CONTACT_METHOD_ARGS) { PX_UNUSED(cache); PX_UNUSED(renderOutput); //Create a triangle cache from the HF triangles and then feed triangles to NP mesh methods const physx::PxHeightFieldGeometryLL& shapeMesh = shape1.get(); const PxBoxGeometry& shapeBox = shape0.get(); PolygonalData polyData0; PolygonalBox polyBox(shapeBox.halfExtents); polyBox.getPolygonalData(&polyData0); const PxVec3 inflatedExtents = shapeBox.halfExtents + PxVec3(params.mContactDistance); const PxBounds3 hullAABB = PxBounds3(-inflatedExtents, inflatedExtents); const Cm::FastVertex2ShapeScaling idtScaling; return contactHullHeightfield2(polyData0, hullAABB, shapeMesh, transform0, transform1, params, contactBuffer, idtScaling, true); }