aboutsummaryrefslogtreecommitdiff
path: root/PhysX_3.4/Source/GeomUtils/src/contact/GuContactPolygonPolygon.cpp
diff options
context:
space:
mode:
authorgit perforce import user <a@b>2016-10-25 12:29:14 -0600
committerSheikh Dawood Abdul Ajees <Sheikh Dawood Abdul Ajees>2016-10-25 18:56:37 -0500
commit3dfe2108cfab31ba3ee5527e217d0d8e99a51162 (patch)
treefa6485c169e50d7415a651bf838f5bcd0fd3bfbd /PhysX_3.4/Source/GeomUtils/src/contact/GuContactPolygonPolygon.cpp
downloadphysx-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.cpp861
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;
+}