// // 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 "PsIntrinsics.h" #include "GuDistanceSegmentTriangle.h" #include "GuDistanceSegmentTriangleSIMD.h" #include "GuDistancePointTriangle.h" #include "GuDistancePointTriangleSIMD.h" #include "GuDistanceSegmentSegment.h" #include "GuDistanceSegmentSegmentSIMD.h" #include "GuBarycentricCoordinates.h" using namespace physx; using namespace Gu; // ptchernev: // The Magic Software code uses a relative error test for parallel case. // The Novodex code does not presumably as an optimization. // Since the Novodex code is working in the trunk I see no reason // to reintroduce the relative error test here. // PT: this might just be because the relative error test has been added // after we grabbed the code. I don't remember making this change. A good // idea would be NOT to refactor Magic's code, to easily grab updated // versions from the website............................................. // ptchernev: // The code has been modified to use a relative error test since the absolute // test would break down for small geometries. (TTP 4021) static PX_FORCE_INLINE void updateClosestHit( PxReal fSqrDist0, PxReal fR0, PxReal fS0, PxReal fT0, PxReal& fSqrDist, PxReal& fR, PxReal& fS, PxReal& fT) { if(fSqrDist0 < fSqrDist) { fSqrDist = fSqrDist0; fR = fR0; fS = fS0; fT = fT0; } } PxReal Gu::distanceSegmentTriangleSquared( const PxVec3& origin, const PxVec3& dir, const PxVec3& p0, const PxVec3& triEdge0, const PxVec3& triEdge1, PxReal* t, PxReal* u, PxReal* v) { const PxReal fA00 = dir.magnitudeSquared(); if(fA00 < 1e-6f*1e-6f) { if(t) *t = 0.0f; return distancePointTriangleSquared(origin, p0, triEdge0, triEdge1, u, v); } const PxVec3 kDiff = p0 - origin; const PxReal fA01 = -(dir.dot(triEdge0)); const PxReal fA02 = -(dir.dot(triEdge1)); const PxReal fA11 = triEdge0.magnitudeSquared(); const PxReal fA12 = triEdge0.dot(triEdge1); const PxReal fA22 = triEdge1.dot(triEdge1); const PxReal fB0 = -(kDiff.dot(dir)); const PxReal fB1 = kDiff.dot(triEdge0); const PxReal fB2 = kDiff.dot(triEdge1); const PxReal fCof00 = fA11*fA22-fA12*fA12; const PxReal fCof01 = fA02*fA12-fA01*fA22; const PxReal fCof02 = fA01*fA12-fA02*fA11; const PxReal fDet = fA00*fCof00+fA01*fCof01+fA02*fCof02; PxReal fSqrDist, fSqrDist0, fR, fS, fT, fR0, fS0, fT0; // Set up for a relative error test on the angle between ray direction // and triangle normal to determine parallel/nonparallel status. const PxVec3 kNormal = triEdge0.cross(triEdge1); const PxReal fDot = kNormal.dot(dir); if(fDot*fDot >= 1e-6f*dir.magnitudeSquared()*kNormal.magnitudeSquared()) { const PxReal fCof11 = fA00*fA22-fA02*fA02; const PxReal fCof12 = fA02*fA01-fA00*fA12; const PxReal fCof22 = fA00*fA11-fA01*fA01; const PxReal fInvDet = fDet == 0.0f ? 0.0f : 1.0f/fDet; const PxReal fRhs0 = -fB0*fInvDet; const PxReal fRhs1 = -fB1*fInvDet; const PxReal fRhs2 = -fB2*fInvDet; fR = fCof00*fRhs0+fCof01*fRhs1+fCof02*fRhs2; fS = fCof01*fRhs0+fCof11*fRhs1+fCof12*fRhs2; fT = fCof02*fRhs0+fCof12*fRhs1+fCof22*fRhs2; if(fR < 0.0f) { if(fS+fT <= 1.0f) { if(fS < 0.0f) { if(fT < 0.0f) // region 4m { // minimum on face s=0 or t=0 or r=0 fSqrDist = distanceSegmentSegmentSquared(origin, dir, p0, triEdge1, &fR, &fT); fS = 0.0f; fSqrDist0 = distanceSegmentSegmentSquared(origin, dir, p0, triEdge0, &fR0, &fS0); fT0 = 0.0f; updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); } else // region 3m { // minimum on face s=0 or r=0 fSqrDist = distanceSegmentSegmentSquared(origin, dir, p0, triEdge1, &fR, &fT); fS = 0.0f; } fSqrDist0 = distancePointTriangleSquared(origin, p0, triEdge0, triEdge1, &fS0, &fT0); fR0 = 0.0f; updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); } else if(fT < 0.0f) // region 5m { // minimum on face t=0 or r=0 fSqrDist = distanceSegmentSegmentSquared(origin, dir, p0, triEdge0, &fR, &fS); fT = 0.0f; fSqrDist0 = distancePointTriangleSquared(origin, p0, triEdge0, triEdge1, &fS0, &fT0); fR0 = 0.0f; updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); } else // region 0m { // minimum on face r=0 fSqrDist = distancePointTriangleSquared(origin, p0, triEdge0, triEdge1, &fS, &fT); fR = 0.0f; } } else { if(fS < 0.0f) // region 2m { // minimum on face s=0 or s+t=1 or r=0 fSqrDist = distanceSegmentSegmentSquared(origin, dir, p0, triEdge1, &fR, &fT); fS = 0.0f; const PxVec3 kTriSegOrig = p0+triEdge0; const PxVec3 kTriSegDir = triEdge1-triEdge0; fSqrDist0 = distanceSegmentSegmentSquared(origin, dir, kTriSegOrig, kTriSegDir, &fR0, &fT0); fS0 = 1.0f-fT0; updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); } else if(fT < 0.0f) // region 6m { // minimum on face t=0 or s+t=1 or r=0 fSqrDist = distanceSegmentSegmentSquared(origin, dir, p0, triEdge0, &fR, &fS); fT = 0.0f; const PxVec3 kTriSegOrig = p0+triEdge0; const PxVec3 kTriSegDir = triEdge1-triEdge0; fSqrDist0 = distanceSegmentSegmentSquared(origin, dir, kTriSegOrig, kTriSegDir, &fR0, &fT0); fS0 = 1.0f-fT0; updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); } else // region 1m { // minimum on face s+t=1 or r=0 const PxVec3 kTriSegOrig = p0+triEdge0; const PxVec3 kTriSegDir = triEdge1-triEdge0; fSqrDist = distanceSegmentSegmentSquared(origin, dir, kTriSegOrig, kTriSegDir, &fR, &fT); fS = 1.0f-fT; } fSqrDist0 = distancePointTriangleSquared(origin, p0, triEdge0, triEdge1, &fS0, &fT0); fR0 = 0.0f; updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); } } else if(fR <= 1.0f) { if(fS+fT <= 1.0f) { if(fS < 0.0f) { if(fT < 0.0f) // region 4 { // minimum on face s=0 or t=0 fSqrDist = distanceSegmentSegmentSquared(origin, dir, p0, triEdge1, &fR, &fT); fS = 0.0f; fSqrDist0 = distanceSegmentSegmentSquared(origin, dir, p0, triEdge0, &fR0, &fS0); fT0 = 0.0f; updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); } else // region 3 { // minimum on face s=0 fSqrDist = distanceSegmentSegmentSquared(origin, dir, p0, triEdge1, &fR, &fT); fS = 0.0f; } } else if(fT < 0.0f) // region 5 { // minimum on face t=0 fSqrDist = distanceSegmentSegmentSquared(origin, dir, p0, triEdge0, &fR, &fS); fT = 0.0f; } else // region 0 { // global minimum is interior, done fSqrDist = fR*(fA00*fR+fA01*fS+fA02*fT+2.0f*fB0) +fS*(fA01*fR+fA11*fS+fA12*fT+2.0f*fB1) +fT*(fA02*fR+fA12*fS+fA22*fT+2.0f*fB2) +kDiff.magnitudeSquared(); } } else { if(fS < 0.0f) // region 2 { // minimum on face s=0 or s+t=1 fSqrDist = distanceSegmentSegmentSquared(origin, dir, p0, triEdge1, &fR, &fT); fS = 0.0f; const PxVec3 kTriSegOrig = p0+triEdge0; const PxVec3 kTriSegDir = triEdge1-triEdge0; fSqrDist0 = distanceSegmentSegmentSquared(origin, dir, kTriSegOrig, kTriSegDir, &fR0, &fT0); fS0 = 1.0f-fT0; updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); } else if(fT < 0.0f) // region 6 { // minimum on face t=0 or s+t=1 fSqrDist = distanceSegmentSegmentSquared(origin, dir, p0, triEdge0, &fR, &fS); fT = 0.0f; const PxVec3 kTriSegOrig = p0+triEdge0; const PxVec3 kTriSegDir = triEdge1-triEdge0; fSqrDist0 = distanceSegmentSegmentSquared(origin, dir, kTriSegOrig, kTriSegDir, &fR0, &fT0); fS0 = 1.0f-fT0; updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); } else // region 1 { // minimum on face s+t=1 const PxVec3 kTriSegOrig = p0+triEdge0; const PxVec3 kTriSegDir = triEdge1-triEdge0; fSqrDist = distanceSegmentSegmentSquared(origin, dir, kTriSegOrig, kTriSegDir, &fR, &fT); fS = 1.0f-fT; } } } else // fR > 1 { if(fS+fT <= 1.0f) { if(fS < 0.0f) { if(fT < 0.0f) // region 4p { // minimum on face s=0 or t=0 or r=1 fSqrDist = distanceSegmentSegmentSquared(origin, dir, p0, triEdge1, &fR, &fT); fS = 0.0f; fSqrDist0 = distanceSegmentSegmentSquared(origin, dir, p0, triEdge0, &fR0, &fS0); fT0 = 0.0f; updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); } else // region 3p { // minimum on face s=0 or r=1 fSqrDist = distanceSegmentSegmentSquared(origin, dir, p0, triEdge1, &fR, &fT); fS = 0.0f; } const PxVec3 kPt = origin+dir; fSqrDist0 = distancePointTriangleSquared(kPt, p0, triEdge0, triEdge1, &fS0, &fT0); fR0 = 1.0f; updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); } else if(fT < 0.0f) // region 5p { // minimum on face t=0 or r=1 fSqrDist = distanceSegmentSegmentSquared(origin, dir, p0, triEdge0, &fR, &fS); fT = 0.0f; const PxVec3 kPt = origin+dir; fSqrDist0 = distancePointTriangleSquared(kPt, p0, triEdge0, triEdge1, &fS0, &fT0); fR0 = 1.0f; updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); } else // region 0p { // minimum face on r=1 const PxVec3 kPt = origin+dir; fSqrDist = distancePointTriangleSquared(kPt, p0, triEdge0, triEdge1, &fS, &fT); fR = 1.0f; } } else { if(fS < 0.0f) // region 2p { // minimum on face s=0 or s+t=1 or r=1 fSqrDist = distanceSegmentSegmentSquared(origin, dir, p0, triEdge1, &fR, &fT); fS = 0.0f; const PxVec3 kTriSegOrig = p0+triEdge0; const PxVec3 kTriSegDir = triEdge1-triEdge0; fSqrDist0 = distanceSegmentSegmentSquared(origin, dir, kTriSegOrig, kTriSegDir, &fR0, &fT0); fS0 = 1.0f-fT0; updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); } else if(fT < 0.0f) // region 6p { // minimum on face t=0 or s+t=1 or r=1 fSqrDist = distanceSegmentSegmentSquared(origin, dir, p0, triEdge0, &fR, &fS); fT = 0.0f; const PxVec3 kTriSegOrig = p0+triEdge0; const PxVec3 kTriSegDir = triEdge1-triEdge0; fSqrDist0 = distanceSegmentSegmentSquared(origin, dir, kTriSegOrig, kTriSegDir, &fR0, &fT0); fS0 = 1.0f-fT0; updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); } else // region 1p { // minimum on face s+t=1 or r=1 const PxVec3 kTriSegOrig = p0+triEdge0; const PxVec3 kTriSegDir = triEdge1-triEdge0; fSqrDist = distanceSegmentSegmentSquared(origin, dir, kTriSegOrig, kTriSegDir, &fR, &fT); fS = 1.0f-fT; } const PxVec3 kPt = origin+dir; fSqrDist0 = distancePointTriangleSquared(kPt, p0, triEdge0, triEdge1, &fS0, &fT0); fR0 = 1.0f; updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); } } } else { // segment and triangle are parallel fSqrDist = distanceSegmentSegmentSquared(origin, dir, p0, triEdge0, &fR, &fS); fT = 0.0f; fSqrDist0 = distanceSegmentSegmentSquared(origin, dir, p0, triEdge1, &fR0, &fT0); fS0 = 0.0f; updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); const PxVec3 kTriSegOrig = p0+triEdge0; const PxVec3 kTriSegDir = triEdge1 - triEdge0; fSqrDist0 = distanceSegmentSegmentSquared(origin, dir, kTriSegOrig, kTriSegDir, &fR0, &fT0); fS0 = 1.0f-fT0; updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); fSqrDist0 = distancePointTriangleSquared(origin, p0, triEdge0, triEdge1, &fS0, &fT0); fR0 = 0.0f; updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); const PxVec3 kPt = origin+dir; fSqrDist0 = distancePointTriangleSquared(kPt, p0, triEdge0, triEdge1, &fS0, &fT0); fR0 = 1.0f; updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); } if(t) *t = fR; if(u) *u = fS; if(v) *v = fT; // account for numerical round-off error return physx::intrinsics::selectMax(0.0f, fSqrDist); } /* closest0 is the closest point on segment pq closest1 is the closest point on triangle abc */ Ps::aos::FloatV Gu::distanceSegmentTriangleSquared( const Ps::aos::Vec3VArg p, const Ps::aos::Vec3VArg q, const Ps::aos::Vec3VArg a, const Ps::aos::Vec3VArg b, const Ps::aos::Vec3VArg c, Ps::aos::Vec3V& closest0, Ps::aos::Vec3V& closest1) { using namespace Ps::aos; const FloatV zero = FZero(); //const FloatV one = FOne(); //const FloatV parallelTolerance = FloatV_From_F32(PX_PARALLEL_TOLERANCE); const Vec3V pq = V3Sub(q, p); const Vec3V ab = V3Sub(b, a); const Vec3V ac = V3Sub(c, a); const Vec3V bc = V3Sub(c, b); const Vec3V ap = V3Sub(p, a); const Vec3V aq = V3Sub(q, a); //This is used to calculate the barycentric coordinate const FloatV d00 = V3Dot(ab,ab); const FloatV d01 = V3Dot(ab, ac); const FloatV d11 = V3Dot(ac, ac); const FloatV tDenom = FSub(FMul(d00, d11), FMul(d01, d01)); const FloatV bdenom = FSel(FIsGrtr(tDenom, zero), FRecip(tDenom), zero); const Vec3V n =V3Normalize(V3Cross(ab, ac)); // normalize vector //compute the closest point of p and triangle plane abc const FloatV dist3 = V3Dot(ap, n); const FloatV sqDist3 = FMul(dist3, dist3); //compute the closest point of q and triangle plane abc const FloatV dist4 = V3Dot(aq, n); const FloatV sqDist4 = FMul(dist4, dist4); const FloatV dMul = FMul(dist3, dist4); const BoolV con = FIsGrtr(zero, dMul); // intersect with the plane if(BAllEqTTTT(con)) { //compute the intersect point const FloatV nom = FNeg(V3Dot(n, ap)); const FloatV denom = FRecip(V3Dot(n, pq)); const FloatV t = FMul(nom, denom); const Vec3V ip = V3ScaleAdd(pq, t, p);//V3Add(p, V3Scale(pq, t)); const Vec3V v2 = V3Sub(ip, a); const FloatV d20 = V3Dot(v2, ab); const FloatV d21 = V3Dot(v2, ac); const FloatV v0 = FMul(FSub(FMul(d11, d20), FMul(d01, d21)), bdenom); const FloatV w0 = FMul(FSub(FMul(d00, d21), FMul(d01, d20)), bdenom); const BoolV con0 = isValidTriangleBarycentricCoord(v0, w0); if(BAllEqTTTT(con0)) { closest0 = closest1 = ip; return zero; } } Vec4V t40, t41; const Vec4V sqDist44 = distanceSegmentSegmentSquared4(p,pq,a,ab, b,bc, a,ac, a,ab, t40, t41); const FloatV t00 = V4GetX(t40); const FloatV t10 = V4GetY(t40); const FloatV t20 = V4GetZ(t40); const FloatV t01 = V4GetX(t41); const FloatV t11 = V4GetY(t41); const FloatV t21 = V4GetZ(t41); const FloatV sqDist0(V4GetX(sqDist44)); const FloatV sqDist1(V4GetY(sqDist44)); const FloatV sqDist2(V4GetZ(sqDist44)); const Vec3V closestP00 = V3ScaleAdd(pq, t00, p); const Vec3V closestP01 = V3ScaleAdd(ab, t01, a); const Vec3V closestP10 = V3ScaleAdd(pq, t10, p); const Vec3V closestP11 = V3ScaleAdd(bc, t11, b); const Vec3V closestP20 = V3ScaleAdd(pq, t20, p); const Vec3V closestP21 = V3ScaleAdd(ac, t21, a); //Get the closest point of all edges const BoolV con20 = FIsGrtr(sqDist1, sqDist0); const BoolV con21 = FIsGrtr(sqDist2, sqDist0); const BoolV con2 = BAnd(con20,con21); const BoolV con30 = FIsGrtrOrEq(sqDist0, sqDist1); const BoolV con31 = FIsGrtr(sqDist2, sqDist1); const BoolV con3 = BAnd(con30, con31); const FloatV sqDistPE = FSel(con2, sqDist0, FSel(con3, sqDist1, sqDist2)); //const FloatV tValue = FSel(con2, t00, FSel(con3, t10, t20)); const Vec3V closestPE0 = V3Sel(con2, closestP00, V3Sel(con3, closestP10, closestP20)); // closestP on segment const Vec3V closestPE1 = V3Sel(con2, closestP01, V3Sel(con3, closestP11, closestP21)); // closestP on triangle const Vec3V closestP31 = V3NegScaleSub(n, dist3, p);//V3Sub(p, V3Scale(n, dist3)); const Vec3V closestP30 = p; //Compute the barycentric coordinate for project point of q const Vec3V pV20 = V3Sub(closestP31, a); const FloatV pD20 = V3Dot(pV20, ab); const FloatV pD21 = V3Dot(pV20, ac); const FloatV v0 = FMul(FSub(FMul(d11, pD20), FMul(d01, pD21)), bdenom); const FloatV w0 = FMul(FSub(FMul(d00, pD21), FMul(d01, pD20)), bdenom); //check closestP3 is inside the triangle const BoolV con0 = isValidTriangleBarycentricCoord(v0, w0); const Vec3V closestP41 = V3NegScaleSub(n, dist4, q);// V3Sub(q, V3Scale(n, dist4)); const Vec3V closestP40 = q; //Compute the barycentric coordinate for project point of q const Vec3V qV20 = V3Sub(closestP41, a); const FloatV qD20 = V3Dot(qV20, ab); const FloatV qD21 = V3Dot(qV20, ac); const FloatV v1 = FMul(FSub(FMul(d11, qD20), FMul(d01, qD21)), bdenom); const FloatV w1 = FMul(FSub(FMul(d00, qD21), FMul(d01, qD20)), bdenom); const BoolV con1 = isValidTriangleBarycentricCoord(v1, w1); /* p is interior point but not q */ const BoolV d0 = FIsGrtr(sqDistPE, sqDist3); const Vec3V c00 = V3Sel(d0, closestP30, closestPE0); const Vec3V c01 = V3Sel(d0, closestP31, closestPE1); /* q is interior point but not p */ const BoolV d1 = FIsGrtr(sqDistPE, sqDist4); const Vec3V c10 = V3Sel(d1, closestP40, closestPE0); const Vec3V c11 = V3Sel(d1, closestP41, closestPE1); /* p and q are interior point */ const BoolV d2 = FIsGrtr(sqDist4, sqDist3); const Vec3V c20 = V3Sel(d2, closestP30, closestP40); const Vec3V c21 = V3Sel(d2, closestP31, closestP41); const BoolV cond2 = BAnd(con0, con1); const Vec3V closestP0 = V3Sel(cond2, c20, V3Sel(con0, c00, V3Sel(con1, c10, closestPE0))); const Vec3V closestP1 = V3Sel(cond2, c21, V3Sel(con0, c01, V3Sel(con1, c11, closestPE1))); const Vec3V vv = V3Sub(closestP1, closestP0); closest0 = closestP0; closest1 = closestP1; return V3Dot(vv, vv); }