Files
XCEngine/engine/third_party/physx/source/geomutils/src/contact/GuContactPolygonPolygon.cpp

862 lines
26 KiB
C++
Raw Normal View History

// 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-2025 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 "geomutils/PxContactBuffer.h"
#include "GuContactPolygonPolygon.h"
#include "GuShapeConvex.h"
#include "GuInternal.h"
#include "foundation/PxAlloca.h"
#include "foundation/PxFPU.h"
using namespace physx;
using namespace Gu;
#define CONTACT_REDUCTION
/*
void gVisualizeLocalLine(const PxVec3& a, const PxVec3& b, const PxMat34& m, PxsContactManager& manager) //temp debug
{
RenderOutput out = manager.getContext()->getRenderOutput();
out << 0xffffff << m << 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( PxContactBuffer& 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
PxContactPoint* 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 = PxDegToRad(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 = 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 PxMat34 transformTranspose(const PxMat33& a, const PxMat34& b)
{
return PxMat34(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 PxMat34& 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 PxMat34& 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 PxMat34& 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 PxMat34& 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 PxMat34& transform0to1, const PxMat34& transform1to0, //transforms between polygons
PxU32 /*polyIndex0*/, PxU32 polyIndex1, //feature indices for contact callback
PxContactBuffer& 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));
// PxMemZero(flags0, size0);
// PxMemZero(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));
// PxMemZero(flags1, size1);
// PxMemZero(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[PxContactBuffer::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 PxMat34 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
PxContactPoint* 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 PxMat34 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.
PxContactPoint* 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;
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);*/
PxContactPoint* 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;
}