diff options
| author | git perforce import user <a@b> | 2016-10-25 12:29:14 -0600 |
|---|---|---|
| committer | Sheikh Dawood Abdul Ajees <Sheikh Dawood Abdul Ajees> | 2016-10-25 18:56:37 -0500 |
| commit | 3dfe2108cfab31ba3ee5527e217d0d8e99a51162 (patch) | |
| tree | fa6485c169e50d7415a651bf838f5bcd0fd3bfbd /PhysX_3.4/Source/GeomUtils/src/contact/GuContactPolygonPolygon.cpp | |
| download | physx-3.4-3dfe2108cfab31ba3ee5527e217d0d8e99a51162.tar.xz physx-3.4-3dfe2108cfab31ba3ee5527e217d0d8e99a51162.zip | |
Initial commit:
PhysX 3.4.0 Update @ 21294896
APEX 1.4.0 Update @ 21275617
[CL 21300167]
Diffstat (limited to 'PhysX_3.4/Source/GeomUtils/src/contact/GuContactPolygonPolygon.cpp')
| -rw-r--r-- | PhysX_3.4/Source/GeomUtils/src/contact/GuContactPolygonPolygon.cpp | 861 |
1 files changed, 861 insertions, 0 deletions
diff --git a/PhysX_3.4/Source/GeomUtils/src/contact/GuContactPolygonPolygon.cpp b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactPolygonPolygon.cpp new file mode 100644 index 00000000..33190da8 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactPolygonPolygon.cpp @@ -0,0 +1,861 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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/PxMemory.h" +#include "GuContactPolygonPolygon.h" +#include "GuShapeConvex.h" +#include "GuContactBuffer.h" +#include "PsMathUtils.h" +#include "PsAllocator.h" +#include "PsFPU.h" + +using namespace physx; +using namespace Gu; + +#define CONTACT_REDUCTION + +/* +void gVisualizeLocalLine(const PxVec3& a, const PxVec3& b, const Cm::Matrix34& m, PxsContactManager& manager) //temp debug +{ + Cm::RenderOutput out = manager.getContext()->getRenderOutput(); + out << 0xffffff << m << Cm::RenderOutput::LINES << a << b; +} +*/ + +#ifdef CONTACT_REDUCTION +static PX_FORCE_INLINE PxReal dot2D(const PxVec3& v0, const PxVec3& v1) +{ + return v0.x * v1.x + v0.y * v1.y; +} + +static void ContactReductionAllIn( ContactBuffer& contactBuffer, PxU32 nbExistingContacts, PxU32 numIn, + const PxMat33& rotT, + const PxVec3* PX_RESTRICT vertices, const PxU8* PX_RESTRICT indices) +{ + // Number of contacts created by current call + const PxU32 nbNewContacts = contactBuffer.count - nbExistingContacts; + + if(nbNewContacts<=4) + return; // no reduction for less than 4 verts + + // We have 3 different numbers here: + // - numVerts = number of vertices in the convex polygon we're dealing with + // - numIn = number of those that were "inside" the other convex polygon (should be <= numVerts) + // - contactBuffer.count = total number of contacts *from both polygons* (that's the catch here) + // The fast path can only be chosen when the contact buffer contains all the verts from current polygon, + // i.e. when contactBuffer.count == numIn == numVerts + + Gu::ContactPoint* PX_RESTRICT ctcs = contactBuffer.contacts + nbExistingContacts; + + if(numIn == nbNewContacts) + { + // Codepath 1: all vertices generated a contact + + PxReal deepestSeparation = ctcs[0].separation; + PxU32 deepestIndex = 0; + for(PxU32 i=1; i<nbNewContacts; ++i) + { + if(deepestSeparation > ctcs[i].separation) + { + deepestSeparation = ctcs[i].separation; + deepestIndex = i; + } + } + + PxU32 index = 0; + const PxU32 step = (numIn<<16)>>2; // Fixed point math, don't use floats here please + bool needsExtraPoint = true; + for(PxU32 i=0;i<4;i++) + { + const PxU32 contactIndex = index>>16; + ctcs[i] = ctcs[contactIndex]; + if(contactIndex==deepestIndex) + needsExtraPoint = false; + index += step; + } + + if(needsExtraPoint) + { + ctcs[4] = ctcs[deepestIndex]; + contactBuffer.count = nbExistingContacts + 5; + } + else + { + contactBuffer.count = nbExistingContacts + 4; + } + +/* PT: TODO: investigate why this one does not work + PxU32 index = deepestIndex<<16; + const PxU32 step = (numIn<<16)>>2; // Fixed point math, don't use floats here please + for(PxU32 i=0;i<4;i++) + { + PxU32 contactIndex = index>>16; + if(contactIndex>=numIn) + contactIndex -= numIn; + ctcs[i] = ctcs[contactIndex]; + index += step; + } + contactBuffer.count = nbExistingContacts + 4;*/ + } + else + { + // Codepath 2: all vertices are "in" but only some of them generated a contact + + // WARNING: this path doesn't work when the buffer contains vertices from both polys. + + // TODO: precompute those axes + const PxU32 nbAxes = 8; + PxVec3 dirs[nbAxes]; + float angle = 0.0f; + const float angleStep = Ps::degToRad(180.0f/float(nbAxes)); + for(PxU32 i=0;i<nbAxes;i++) + { + dirs[i] = PxVec3(cosf(angle), sinf(angle), 0.0f); + angle += angleStep; + } + + float dpmin[nbAxes]; + float dpmax[nbAxes]; + for(PxU32 i=0;i<nbAxes;i++) + { + dpmin[i] = PX_MAX_F32; + dpmax[i] = -PX_MAX_F32; + } + + for(PxU32 i=0;i<nbNewContacts;i++) + { + const PxVec3& p = vertices[indices[i]]; + + // Transform to 2D + const PxVec3 p2d = rotT.transform(p); + + for(PxU32 j=0;j<nbAxes;j++) + { + const float dp = dot2D(dirs[j], p2d); + dpmin[j] = physx::intrinsics::selectMin(dpmin[j], dp); + dpmax[j] = physx::intrinsics::selectMax(dpmax[j], dp); + } + } + + PxU32 bestAxis = 0; + float maxVariance = dpmax[0] - dpmin[0]; + for(PxU32 i=1;i<nbAxes;i++) + { + const float variance = dpmax[i] - dpmin[i]; + if(variance>maxVariance) + { + maxVariance = variance; + bestAxis = i; + } + } + + const PxVec3 u = dirs[bestAxis]; + const PxVec3 v = PxVec3(-u.y, u.x, 0.0f); + // PxVec3(1.0f, 0.0f, 0.0f) => PxVec3(0.0f, 1.0f, 0.0f) + // PxVec3(0.0f, 1.0f, 0.0f) => PxVec3(-1.0f, 0.0f, 0.0f) + // PxVec3(-1.0f, 1.0f, 0.0f) => PxVec3(-1.0f, -1.0f, 0.0f) + // PxVec3(1.0f, 1.0f, 0.0f) => PxVec3(-1.0f, 1.0f, 0.0f) + + float dpminu = PX_MAX_F32; + float dpmaxu = -PX_MAX_F32; + float dpminv = PX_MAX_F32; + float dpmaxv = -PX_MAX_F32; + PxU32 indexMinU = 0; + PxU32 indexMaxU = 0; + PxU32 indexMinV = 0; + PxU32 indexMaxV = 0; + + for(PxU32 i=0;i<nbNewContacts;i++) + { + const PxVec3& p = vertices[indices[i]]; + + // Transform to 2D + const PxVec3 p2d = rotT.transform(p); + + const float dpu = dot2D(u, p2d); + const float dpv = dot2D(v, p2d); + + if(dpu<dpminu) + { + dpminu=dpu; + indexMinU = i; + } + if(dpu>dpmaxu) + { + dpmaxu=dpu; + indexMaxU = i; + } + + if(dpv<dpminv) + { + dpminv=dpv; + indexMinV = i; + } + if(dpv>dpmaxv) + { + dpmaxv=dpv; + indexMaxV = i; + } + } + + if(indexMaxU == indexMinU) + indexMaxU = 0xffffffff; + if(indexMinV == indexMinU || indexMinV == indexMaxU) + indexMinV = 0xffffffff; + if(indexMaxV == indexMinU || indexMaxV == indexMaxU || indexMaxV == indexMinV) + indexMaxV = 0xffffffff; + + PxU32 newCount = 0; + for(PxU32 i=0;i<nbNewContacts;i++) + { + if( i==indexMinU + || i==indexMaxU + || i==indexMinV + || i==indexMaxV) + { + ctcs[newCount++] = ctcs[i]; + } + } + contactBuffer.count = nbExistingContacts + newCount; + } +} +#endif + +// PT: please leave that function in the same translation unit as the calling code +/*static*/ PxMat33 Gu::findRotationMatrixFromZ(const PxVec3& to) +{ + PxMat33 result; + + const PxReal e = to.z; + const PxReal f = PxAbs(e); + + if(f <= 0.9999f) + { + // PT: please keep the normal case first for PS3 branch prediction + + // Normal case, to and from are not parallel or anti-parallel + const PxVec3 v = Ps::cross001(to); + const PxReal h = 1.0f/(1.0f + e); /* optimization by Gottfried Chen */ + const PxReal hvx = h * v.x; + const PxReal hvz = h * v.z; + const PxReal hvxy = hvx * v.y; + const PxReal hvxz = hvx * v.z; + const PxReal hvyz = hvz * v.y; + + result(0,0) = e + hvx*v.x; + result(0,1) = hvxy - v.z; + result(0,2) = hvxz + v.y; + + result(1,0) = hvxy + v.z; + result(1,1) = e + h*v.y*v.y; + result(1,2) = hvyz - v.x; + + result(2,0) = hvxz - v.y; + result(2,1) = hvyz + v.x; + result(2,2) = e + hvz*v.z; + } + else + { + //Vectors almost parallel + // PT: TODO: simplify code below + PxVec3 from(0.0f, 0.0f, 1.0f); + PxVec3 absFrom(0.0f, 0.0f, 1.0f); + + if(absFrom.x < absFrom.y) + { + if(absFrom.x < absFrom.z) + absFrom = PxVec3(1.0f, 0.0f, 0.0f); + else + absFrom = PxVec3(0.0f, 0.0f, 1.0f); + } + else + { + if(absFrom.y < absFrom.z) + absFrom = PxVec3(0.0f, 1.0f, 0.0f); + else + absFrom = PxVec3(0.0f, 0.0f, 1.0f); + } + + PxVec3 u, v; + u.x = absFrom.x - from.x; u.y = absFrom.y - from.y; u.z = absFrom.z - from.z; + v.x = absFrom.x - to.x; v.y = absFrom.y - to.y; v.z = absFrom.z - to.z; + + const PxReal c1 = 2.0f / u.dot(u); + const PxReal c2 = 2.0f / v.dot(v); + const PxReal c3 = c1 * c2 * u.dot(v); + + for(unsigned int i = 0; i < 3; i++) + { + for(unsigned int j = 0; j < 3; j++) + { + result(i,j) = - c1*u[i]*u[j] - c2*v[i]*v[j] + c3*v[i]*u[j]; + } + result(i,i) += 1.0f; + } + } + return result; +} + +// PT: using this specialized version avoids doing an explicit transpose, which reduces LHS +PX_FORCE_INLINE Cm::Matrix34 transformTranspose(const PxMat33& a, const Cm::Matrix34& b) +{ + return Cm::Matrix34(a.transformTranspose(b.m.column0), a.transformTranspose(b.m.column1), a.transformTranspose(b.m.column2), a.transformTranspose(b.p)); +} + +// Helper function to transform x/y coordinate of point. +PX_FORCE_INLINE void transform2D(float& x, float& y, const PxVec3& src, const Cm::Matrix34& mat) +{ + x = src.x * mat.m.column0.x + src.y * mat.m.column1.x + src.z * mat.m.column2.x + mat.p.x; + y = src.x * mat.m.column0.y + src.y * mat.m.column1.y + src.z * mat.m.column2.y + mat.p.y; +} + +// Helper function to transform x/y coordinate of point. Use transposed matrix +PX_FORCE_INLINE void transform2DT(float& x, float& y, const PxVec3& src, const PxMat33& mat) +{ + x = mat.column0.dot(src); + y = mat.column1.dot(src); +} + +// Helper function to transform z coordinate of point. +PX_FORCE_INLINE PxReal transformZ(const PxVec3& src, const Cm::Matrix34& mat) +{ + return src.x * mat.m.column0.z + src.y * mat.m.column1.z + src.z * mat.m.column2.z + mat.p.z; +} + +static void transformVertices( float& minX, float& minY, + float& maxX, float& maxY, + float* PX_RESTRICT verts2D, + PxU32 nb, const PxVec3* PX_RESTRICT vertices, const PxU8* PX_RESTRICT indices, const PxMat33& RotT) +{ + // PT: using local variables is important to reduce LHS. + float lminX = FLT_MAX; + float lminY = FLT_MAX; + float lmaxX = -FLT_MAX; + float lmaxY = -FLT_MAX; + + // PT: project points, compute min & max at the same time + for(PxU32 i=0; i<nb; i++) + { + float x,y; + transform2DT(x, y, vertices[indices[i]], RotT); + lminX = physx::intrinsics::selectMin(lminX, x); + lminY = physx::intrinsics::selectMin(lminY, y); + lmaxX = physx::intrinsics::selectMax(lmaxX, x); + lmaxY = physx::intrinsics::selectMax(lmaxY, y); + verts2D[i*2+0] = x; + verts2D[i*2+1] = y; + } + + // DE702 + // Compute center of polygon + const float cx = (lminX + lmaxX)*0.5f; + const float cy = (lminY + lmaxY)*0.5f; + // We'll scale the polygon by epsilon + const float epsilon = 1.e-6f; + // Adjust bounds to take care of scaling + lminX -= epsilon; + lminY -= epsilon; + lmaxX += epsilon; + lmaxY += epsilon; + //~DE702 + + // PT: relocate polygon to positive quadrant + for(PxU32 i=0; i<nb; i++) + { + const float x = verts2D[i*2+0]; + const float y = verts2D[i*2+1]; + + // PT: original code suffering from DE702 (relocation) +// verts2D[i*2+0] = x - lminX; +// verts2D[i*2+1] = y - lminY; + + // PT: theoretically proper DE702 fix (relocation + scaling) + const float dx = x - cx; + const float dy = y - cy; +// const float coeff = epsilon * physx::intrinsics::recipSqrt(dx*dx+dy*dy); +// verts2D[i*2+0] = x - lminX + dx * coeff; +// verts2D[i*2+1] = y - lminY + dy * coeff; + + // PT: approximate but faster DE702 fix. We multiply by epsilon so this is good enough. + verts2D[i*2+0] = x - lminX + physx::intrinsics::fsel(dx, epsilon, -epsilon); + verts2D[i*2+1] = y - lminY + physx::intrinsics::fsel(dy, epsilon, -epsilon); + } + lmaxX -= lminX; + lmaxY -= lminY; + + minX = lminX; + minY = lminY; + maxX = lmaxX; + maxY = lmaxY; +} + +//! Dedicated triangle version +PX_FORCE_INLINE bool pointInTriangle2D( float px, float pz, + float p0x, float p0z, + float e10x, float e10z, + float e20x, float e20z) +{ + const float a = e10x*e10x + e10z*e10z; + const float b = e10x*e20x + e10z*e20z; + const float c = e20x*e20x + e20z*e20z; + const float ac_bb = (a*c)-(b*b); + + const float vpx = px - p0x; + const float vpz = pz - p0z; + + const float d = vpx*e10x + vpz*e10z; + const float e = vpx*e20x + vpz*e20z; + + const float x = (d*c) - (e*b); + const float y = (e*a) - (d*b); + const float z = x + y - ac_bb; + + // Same as: if(x>0.0f && y>0.0f && z<0.0f) return TRUE; + // else return FALSE; +// return (( IR(z) & ~(IR(x)|IR(y)) ) & SIGN_BITMASK) != 0; + if(x>0.0f && y>0.0f && z<0.0f) return true; + else return false; +} + + + enum OutCode + { + OUT_XP = (1<<0), + OUT_XN = (1<<1), + OUT_YP = (1<<2), + OUT_YN = (1<<3) + }; + +static +//PX_FORCE_INLINE +bool PointInConvexPolygon2D_OutCodes(const float* PX_RESTRICT pgon2D, PxU32 numVerts, const PxReal tx, const PxReal ty, const PxReal maxX, const PxReal maxY, PxU8& outCodes) +{ + PxU32 out = 0; + if(tx<0.0f) out |= OUT_XN; + if(ty<0.0f) out |= OUT_YN; + if(tx>maxX) out |= OUT_XP; + if(ty>maxY) out |= OUT_YP; + outCodes = PxU8(out); + if(out) + return false; + + if(numVerts==3) + return pointInTriangle2D( tx, ty, + pgon2D[0], pgon2D[1], + pgon2D[2] - pgon2D[0], + pgon2D[3] - pgon2D[1], + pgon2D[4] - pgon2D[0], + pgon2D[5] - pgon2D[1]); + +#define X 0 +#define Y 1 + + const PxReal* PX_RESTRICT vtx0_ = pgon2D + (numVerts-1)*2; + const PxReal* PX_RESTRICT vtx1_ = pgon2D; + + const int* PX_RESTRICT ivtx0 = reinterpret_cast<const int*>(vtx0_); + const int* PX_RESTRICT ivtx1 = reinterpret_cast<const int*>(vtx1_); + //const int itx = (int&)tx; + //const int ity = (int&)ty; +// const int ity = PX_SIR(ty); + const int* tmp = reinterpret_cast<const int*>(&ty); + const int ity = *tmp; + + // get test bit for above/below X axis + int yflag0 = ivtx0[Y] >= ity; + + int InsideFlag = 0; + + while(numVerts--) + { + const int yflag1 = ivtx1[Y] >= ity; + if(yflag0 != yflag1) + { + const PxReal* PX_RESTRICT vtx0 = reinterpret_cast<const PxReal*>(ivtx0); + const PxReal* PX_RESTRICT vtx1 = reinterpret_cast<const PxReal*>(ivtx1); + if( ((vtx1[Y]-ty) * (vtx0[X]-vtx1[X]) > (vtx1[X]-tx) * (vtx0[Y]-vtx1[Y])) == yflag1 ) + { + if(InsideFlag == 1) return false; + + InsideFlag++; + } + } + yflag0 = yflag1; + ivtx0 = ivtx1; + ivtx1 += 2; + } +#undef X +#undef Y + + return InsideFlag & 1; +} + +// Helper function to detect contact between two edges +PX_FORCE_INLINE bool EdgeEdgeContactSpecial(const PxVec3& v1, const PxPlane& plane, + const PxVec3& p1, const PxVec3& p2, const PxVec3& dir, const PxVec3& p3, const PxVec3& p4, + PxReal& dist, PxVec3& ip, unsigned int i, unsigned int j, float coeff) +{ + const PxReal d3 = plane.distance(p3); + PxReal temp = d3 * plane.distance(p4); + if(temp > 0.0f) + return false; + + // if colliding edge (p3,p4) and plane are parallel return no collision + const PxVec3 v2 = (p4-p3); + temp = plane.n.dot(v2); + if(temp == 0.0f) // ### epsilon would be better + return false; + + // compute intersection point of plane and colliding edge (p3,p4) + ip = p3-v2*(d3/temp); + + // compute distance of intersection from line (ip, -dir) to line (p1,p2) + dist = (v1[i]*(ip[j]-p1[j])-v1[j]*(ip[i]-p1[i])) * coeff; + if(dist < 0.0f) + return false; + + // compute intersection point on edge (p1,p2) line + ip -= dist*dir; + + // check if intersection point (ip) is between edge (p1,p2) vertices + temp = (p1.x-ip.x)*(p2.x-ip.x)+(p1.y-ip.y)*(p2.y-ip.y)+(p1.z-ip.z)*(p2.z-ip.z); + if(temp<0.0f) + return true; // collision found + + return false; //no collision +} + +//This one can also handle 2 vertex 'polygons' (useful for capsule surface segments) and can shift the results before contact generation. +bool Gu::contactPolygonPolygonExt( PxU32 numVerts0, const PxVec3* vertices0, const PxU8* indices0, //polygon 0 + const Cm::Matrix34& world0, const PxPlane& localPlane0, //xform of polygon 0, plane of polygon + const PxMat33& rotT0, + // + PxU32 numVerts1, const PxVec3* PX_RESTRICT vertices1, const PxU8* PX_RESTRICT indices1, //polygon 1 + const Cm::Matrix34& world1, const PxPlane& localPlane1, //xform of polygon 1, plane of polygon + const PxMat33& rotT1, + // + const PxVec3& worldSepAxis, //world normal of separating plane - this is the world space normal of polygon0!! + const Cm::Matrix34& transform0to1, const Cm::Matrix34& transform1to0, //transforms between polygons + PxU32 /*polyIndex0*/, PxU32 polyIndex1, //feature indices for contact callback + ContactBuffer& contactBuffer, + bool flipNormal, const PxVec3& posShift, PxReal sepShift) // shape order, result shift +{ + const PxVec3 n = flipNormal ? -worldSepAxis : worldSepAxis; + + PX_ASSERT(indices0 != NULL && indices1 != NULL); + + // - optimize "from to" computation + // - do the raycast case && EE tests in same space as 2D case... + // - project all edges at the same time ? + PxU32 NumIn = 0; + bool status = false; + + void* PX_RESTRICT stackMemory; + { + const PxU32 maxNumVert = PxMax(numVerts0, numVerts1); + stackMemory = PxAlloca(maxNumVert * sizeof(PxVec3)); + } + + const PxU32 size0 = numVerts0 * sizeof(bool); + bool* PX_RESTRICT flags0 = reinterpret_cast<bool*>(PxAlloca(size0)); + PxU8* PX_RESTRICT outCodes0 = reinterpret_cast<PxU8*>(PxAlloca(size0)); +// Ps::memZero(flags0, size0); +// Ps::memZero(outCodes0, size0); + + const PxU32 size1 = numVerts1 * sizeof(bool); + bool* PX_RESTRICT flags1 = reinterpret_cast<bool*>(PxAlloca(size1)); + PxU8* PX_RESTRICT outCodes1 = reinterpret_cast<PxU8*>(PxAlloca(size1)); +// Ps::memZero(flags1, size1); +// Ps::memZero(outCodes1, size1); + +#ifdef CONTACT_REDUCTION + // We want to do contact reduction on newly created contacts, not on all the already existing ones... + PxU32 nbExistingContacts = contactBuffer.count; + PxU32 nbCurrentContacts=0; + PxU8 indices[ContactBuffer::MAX_CONTACTS]; +#endif + + { + //polygon 1 + float* PX_RESTRICT verts2D = NULL; + float minX=0, minY=0; + float maxX=0, maxY=0; + + const PxVec3 localDir = -world1.rotateTranspose(worldSepAxis); //contactNormal in hull1 space + //that's redundant, its equal to -localPlane1.d + const Cm::Matrix34 t0to2D = transformTranspose(rotT1, transform0to1); //transform from hull0 to RotT + + PxReal dn = localDir.dot(localPlane1.n); //if the contactNormal == +-(normal of poly0) is NOT orthogonal to poly1 ...this is just to protect the division below. + + // PT: TODO: if "numVerts1>2" we may skip more + if (numVerts1 > 2 //no need to test whether we're 'inside' ignore capsule segments and points +// if(!(-1E-7 < dn && dn < 1E-7)) + && dn >= 1E-7f) // PT: it should never be negative so this unique test is enough + { + dn = 1.0f / dn; + const float ld1 = -localPlane1.d; // PT: unavoidable "int-to-float" LHS here, so we only want to read it once! + + // Lazy-transform vertices + if(!verts2D) + { + verts2D = reinterpret_cast<float*>(stackMemory); + //Project points + transformVertices( + minX, minY, + maxX, maxY, + verts2D, numVerts1, vertices1, indices1, rotT1); + } + + for(PxU32 i=0; i < numVerts0; i++) //for all vertices of poly0 + { + const PxVec3& p = vertices0[indices0[i]]; + const float p0_z = transformZ(p, t0to2D); //transform ith vertex of poly0 to RotT + + const PxVec3 pIn1 = transform0to1.transform(p); //transform vertex to hull1 space, in which we have the poly1 vertices. + + const PxReal dd = (p0_z - ld1) * dn; //(p0_z + localPlane1.d) is the depth of the vertex behind the triangle measured along the triangle's normal. + //we convert this to being measured along the 'contact normal' using the division. + +// if(dd < 0.0f) //if the penetrating vertex will have a penetration along the contact normal: +// PX_ASSERT(dd <= 0.0f); // PT: dn is always positive, so dd is always negative + { + float px, py; + transform2DT(px, py, pIn1 - dd*localDir, rotT1); //project vertex into poly1 plane along CONTACT NORMAL - not the polygon's normal. + + const bool res = PointInConvexPolygon2D_OutCodes(verts2D, numVerts1, px-minX, py-minY, maxX, maxY, outCodes0[i]); + flags0[i] = res; + if(res) + { + NumIn++; + + if(p0_z < ld1) + { + status = true; // PT: keep this first to avoid an LHS when leaving the function + + Gu::ContactPoint* PX_RESTRICT ctc = contactBuffer.contact(); + if(ctc) + { +#ifdef CONTACT_REDUCTION + indices[nbCurrentContacts++] = indices0[i]; +#endif + ctc->normal = n; + ctc->point = world0.transform(p) + (flipNormal ? posShift : PxVec3(0.0f)); + ctc->separation = dd + sepShift; + ctc->internalFaceIndex1 = polyIndex1; + } + } + } + } + } + } + else + { + PxMemZero(flags0, size0); + PxMemZero(outCodes0, size0); + } + + if(NumIn == numVerts0) + { + //All vertices0 are inside polygon 1 +#ifdef CONTACT_REDUCTION + ContactReductionAllIn(contactBuffer, nbExistingContacts, NumIn, rotT0, vertices0, indices); +#endif + return status; + } + +#ifdef CONTACT_REDUCTION + ContactReductionAllIn(contactBuffer, nbExistingContacts, NumIn, rotT0, vertices0, indices); +#endif + +#ifdef CONTACT_REDUCTION + nbExistingContacts = contactBuffer.count; + nbCurrentContacts = 0; +#endif + NumIn = 0; + verts2D = NULL; + + //Polygon 0 + const Cm::Matrix34 t1to2D = transformTranspose(rotT0, transform1to0); + + if (numVerts0 > 2) //no need to test whether we're 'inside' ignore capsule segments and points + { + const float ld0 = -localPlane0.d; // PT: unavoidable "int-to-float" LHS here, so we only want to read it once! + + // Lazy-transform vertices + if(!verts2D) + { + verts2D = reinterpret_cast<float*>(stackMemory); + //Project vertices + transformVertices( + minX, minY, + maxX, maxY, + verts2D, numVerts0, vertices0, indices0, rotT0); + } + + for(PxU32 i=0; i < numVerts1; i++) + { + const PxVec3& p = vertices1[indices1[i]]; + + float px, py; + transform2D(px, py, p, t1to2D); + + const bool res = PointInConvexPolygon2D_OutCodes(verts2D, numVerts0, px-minX, py-minY, maxX, maxY, outCodes1[i]); + flags1[i] = res; + if(res) + { + NumIn++; + + const float pz = transformZ(p, t1to2D); + if(pz < ld0) + { + status = true; // PT: keep this first to avoid an LHS when leaving the function + + // PT: in theory, with this contact point we should use "worldSepAxis" as a contact normal. + // However we want to output the same normal for all contact points not to break friction + // patches!!! In theory again, it should be exactly the same since the contact point at + // time of impact is supposed to be the same on both bodies. In practice however, and with + // a depth-based engine, this is not the case. So the contact point here is not exactly + // right, but preserving the friction patch seems more important. + + Gu::ContactPoint* PX_RESTRICT ctc = contactBuffer.contact(); + if(ctc) + { +#ifdef CONTACT_REDUCTION + indices[nbCurrentContacts++] = indices1[i]; +#endif + ctc->normal = n; + ctc->point = world1.transform(p) + (flipNormal ? PxVec3(0.0f) : posShift); + ctc->separation = (pz - ld0) + sepShift; + ctc->internalFaceIndex1 = polyIndex1; + } + } + } + } + + if(NumIn == numVerts1) + { + //all vertices 1 are inside polygon 0 +#ifdef CONTACT_REDUCTION + ContactReductionAllIn(contactBuffer, nbExistingContacts, NumIn, rotT1, vertices1, indices); +#endif + return status; + } +#ifdef CONTACT_REDUCTION + ContactReductionAllIn(contactBuffer, nbExistingContacts, NumIn, rotT1, vertices1, indices); +#endif + } + else + { + PxMemZero(flags1, size1); + PxMemZero(outCodes1, size1); + } + } + + //Edge/edge case + //Calculation done in space 0 + PxVec3* PX_RESTRICT verts1in0 = reinterpret_cast<PxVec3*>(stackMemory); + for(PxU32 i=0; i<numVerts1; i++) + { + verts1in0[i] = transform1to0.transform(vertices1[indices1[i]]); + } + + if (numVerts0 >= 2 && numVerts1 >= 2)//useless if one of them is degenerate. + for(PxU32 j=0; j<numVerts1; j++) + { + PxU32 j1 = j+1; + if(j1 >= numVerts1) j1 = 0; + +// if(!(flags1[j] ^ flags1[j1])) +// continue; + if(flags1[j] && flags1[j1]) + continue; + if(outCodes1[j]&outCodes1[j1]) + continue; + + const PxVec3& p0 = verts1in0[j]; + const PxVec3& p1 = verts1in0[j1]; + +// gVisualizeLocalLine(vertices1[indices1[j]], vertices1[indices1[j1]], world1, callback.getManager()); + + const PxVec3 v1 = p1-p0; + const PxVec3 planeNormal = v1.cross(localPlane0.n); + const PxPlane plane(planeNormal, -(planeNormal.dot(p0))); + + // find largest 2D plane projection + PxU32 _i, _j; + Ps::closestAxis(planeNormal, _i, _j); + + const PxReal coeff = 1.0f / (v1[_i]*localPlane0.n[_j]-v1[_j]*localPlane0.n[_i]); + + for(PxU32 i=0; i<numVerts0; i++) + { + PxU32 i1 = i+1; + if(i1 >= numVerts0) i1 = 0; + +// if(!(flags0[i] ^ flags0[i1])) +// continue; + if(flags0[i] && flags0[i1]) + continue; + if(outCodes0[i]&outCodes0[i1]) + continue; + + const PxVec3& p0b = vertices0[indices0[i]]; + const PxVec3& p1b = vertices0[indices0[i1]]; + +// gVisualizeLocalLine(p0b, p1b, world0, callback.getManager()); + + PxReal dist; + PxVec3 p; + + if(EdgeEdgeContactSpecial(v1, plane, p0, p1, localPlane0.n, p0b, p1b, dist, p, _i, _j, coeff)) + { + status = true; // PT: keep this first to avoid an LHS when leaving the function +/* p = world0.transform(p); + + //contacts are generated on the edges of polygon 1 + //we only have to shift the position of polygon 1 if flipNormal is false, because + //in this case convex 0 gets passed as polygon 1, and it is convex 0 that was shifted. + if (!flipNormal) + p += posShift; + + contactBuffer.contact(p, n, -dist + sepShift, polyIndex0, polyIndex1, convexID);*/ + + Gu::ContactPoint* PX_RESTRICT ctc = contactBuffer.contact(); + if(ctc) + { + ctc->normal = n; + ctc->point = world0.transform(p) + (flipNormal ? PxVec3(0.0f) : posShift); + ctc->separation = -dist + sepShift; + ctc->internalFaceIndex1 = polyIndex1; + } + } + } + } + return status; +} |