aboutsummaryrefslogtreecommitdiff
path: root/PhysX_3.4/Source/GeomUtils/src/contact
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
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')
-rw-r--r--PhysX_3.4/Source/GeomUtils/src/contact/GuContactBoxBox.cpp703
-rw-r--r--PhysX_3.4/Source/GeomUtils/src/contact/GuContactCapsuleBox.cpp457
-rw-r--r--PhysX_3.4/Source/GeomUtils/src/contact/GuContactCapsuleCapsule.cpp155
-rw-r--r--PhysX_3.4/Source/GeomUtils/src/contact/GuContactCapsuleConvex.cpp587
-rw-r--r--PhysX_3.4/Source/GeomUtils/src/contact/GuContactCapsuleMesh.cpp636
-rw-r--r--PhysX_3.4/Source/GeomUtils/src/contact/GuContactConvexConvex.cpp1033
-rw-r--r--PhysX_3.4/Source/GeomUtils/src/contact/GuContactConvexMesh.cpp1449
-rw-r--r--PhysX_3.4/Source/GeomUtils/src/contact/GuContactMethodImpl.h174
-rw-r--r--PhysX_3.4/Source/GeomUtils/src/contact/GuContactPlaneBox.cpp128
-rw-r--r--PhysX_3.4/Source/GeomUtils/src/contact/GuContactPlaneCapsule.cpp80
-rw-r--r--PhysX_3.4/Source/GeomUtils/src/contact/GuContactPlaneConvex.cpp101
-rw-r--r--PhysX_3.4/Source/GeomUtils/src/contact/GuContactPolygonPolygon.cpp861
-rw-r--r--PhysX_3.4/Source/GeomUtils/src/contact/GuContactPolygonPolygon.h69
-rw-r--r--PhysX_3.4/Source/GeomUtils/src/contact/GuContactSphereBox.cpp181
-rw-r--r--PhysX_3.4/Source/GeomUtils/src/contact/GuContactSphereCapsule.cpp82
-rw-r--r--PhysX_3.4/Source/GeomUtils/src/contact/GuContactSphereMesh.cpp615
-rw-r--r--PhysX_3.4/Source/GeomUtils/src/contact/GuContactSpherePlane.cpp68
-rw-r--r--PhysX_3.4/Source/GeomUtils/src/contact/GuContactSphereSphere.cpp68
-rw-r--r--PhysX_3.4/Source/GeomUtils/src/contact/GuFeatureCode.cpp128
-rw-r--r--PhysX_3.4/Source/GeomUtils/src/contact/GuFeatureCode.h56
-rw-r--r--PhysX_3.4/Source/GeomUtils/src/contact/GuLegacyContactBoxHeightField.cpp476
-rw-r--r--PhysX_3.4/Source/GeomUtils/src/contact/GuLegacyContactCapsuleHeightField.cpp279
-rw-r--r--PhysX_3.4/Source/GeomUtils/src/contact/GuLegacyContactConvexHeightField.cpp430
-rw-r--r--PhysX_3.4/Source/GeomUtils/src/contact/GuLegacyContactSphereHeightField.cpp290
-rw-r--r--PhysX_3.4/Source/GeomUtils/src/contact/GuLegacyTraceLineCallback.h160
25 files changed, 9266 insertions, 0 deletions
diff --git a/PhysX_3.4/Source/GeomUtils/src/contact/GuContactBoxBox.cpp b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactBoxBox.cpp
new file mode 100644
index 00000000..3b12a326
--- /dev/null
+++ b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactBoxBox.cpp
@@ -0,0 +1,703 @@
+// 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 "GuContactBuffer.h"
+#include "GuContactMethodImpl.h"
+#include "GuGeometryUnion.h"
+#include "CmMatrix34.h"
+#include "PsUtilities.h"
+
+using namespace physx;
+using namespace Gu;
+
+#define MAX_NB_CTCS 8 + 12*5 + 6*4
+#define ABS_GREATER(x, y) (PxAbs(x) > (y))
+#define ABS_SMALLER_EQUAL(x, y) (PxAbs(x) <= (y))
+//#define AIR(x) ((PxU32&)(x)&SIGN_BITMASK)
+//#define ABS_GREATER(x, y) (AIR(x) > IR(y))
+//#define ABS_SMALLER_EQUAL(x, y) (AIR(x) <= IR(y))
+
+#if PX_X86 && !PX_OSX
+
+ // Some float optimizations ported over from novodex.
+
+ //returns non zero if the value is negative.
+ #define PXC_IS_NEGATIVE(x) (((PxU32&)(x)) & 0x80000000)
+
+#else
+
+ //On most platforms using the integer rep is worse(produces LHSs) since the CPU has more registers.
+
+ //returns non zero if the value is negative.
+ #define PXC_IS_NEGATIVE(x) ((x) < 0.0f)
+
+#endif
+
+
+enum
+{
+ AXIS_A0, AXIS_A1, AXIS_A2,
+ AXIS_B0, AXIS_B1, AXIS_B2
+};
+
+struct VertexInfo
+{
+ PxVec3 pos;
+ bool penetrate;
+ bool area;
+};
+
+
+/*static PxI32 doBoxBoxContactGeneration(PxVec3 ctcPts[MAX_NB_CTCS], PxReal depths[MAX_NB_CTCS], PxVec3* ctcNrm,
+ const PxVec3& extents0, const PxVec3& extents1,
+ PxU32& collisionData,
+ const Cm::Matrix34& transform0, const Cm::Matrix34& transform1, PxReal contactDistance);*/
+
+static PxI32 doBoxBoxContactGeneration(ContactBuffer& contactBuffer,
+ const PxVec3& extents0, const PxVec3& extents1,
+ PxU32& collisionData,
+ const Cm::Matrix34& transform0, const Cm::Matrix34& transform1, PxReal contactDistance);
+
+namespace physx
+{
+
+namespace Gu
+{
+bool contactBoxBox(GU_CONTACT_METHOD_ARGS)
+{
+ PX_UNUSED(renderOutput);
+
+ // Get actual shape data
+ const PxBoxGeometry& shapeBox0 = shape0.get<const PxBoxGeometry>();
+ const PxBoxGeometry& shapeBox1 = shape1.get<const PxBoxGeometry>();
+
+ PxU32 pd = PxU32(cache.mPairData);
+ PxI32 Nb = doBoxBoxContactGeneration(contactBuffer,
+ shapeBox0.halfExtents, shapeBox1.halfExtents,
+ pd,
+ Cm::Matrix34(transform0), Cm::Matrix34(transform1),
+ params.mContactDistance);
+
+ cache.mPairData = Ps::to8(pd);
+
+ if(!Nb)
+ {
+ cache.mPairData = 0; // Mark as separated for temporal coherence
+ return false; // WARNING: the contact stream code below used to output stuff even for 0 contacts (!). Now we just return here.
+ }
+ return true;
+}
+}//Gu
+}//physx
+
+// face => 4 vertices of a face of the cube (i.e. a quad)
+static PX_FORCE_INLINE PxReal IsInYZ(const PxReal y, const PxReal z, const VertexInfo** PX_RESTRICT face)
+{
+ // Warning, indices have been remapped. We're now actually like this:
+ //
+ // 3+------+2
+ // | | |
+ // | *--|
+ // | (y,z)|
+ // 0+------+1
+ PxReal PreviousY = face[3]->pos.y;
+ PxReal PreviousZ = face[3]->pos.z;
+
+ // Loop through quad vertices
+ for(PxI32 i=0; i<4; i++)
+ {
+ const PxReal CurrentY = face[i]->pos.y;
+ const PxReal CurrentZ = face[i]->pos.z;
+
+ // |CurrentY - PreviousY y - PreviousY|
+ // |CurrentZ - PreviousZ z - PreviousZ|
+ // => similar to backface culling, check each one of the 4 triangles are consistent, in which case
+ // the point is within the parallelogram.
+ if((CurrentY - PreviousY)*(z - PreviousZ) - (CurrentZ - PreviousZ)*(y - PreviousY) >= 0.0f) return -1.0f;
+
+ PreviousY = CurrentY;
+ PreviousZ = CurrentZ;
+ }
+
+ PxReal x = face[0]->pos.x;
+ {
+ const PxReal ay = y - face[0]->pos.y;
+ const PxReal az = z - face[0]->pos.z;
+
+ PxVec3 b = face[1]->pos - face[0]->pos; // ### could be precomputed ?
+ x += b.x * (ay*b.y + az*b.z) / b.magnitudeSquared(); // ### could be precomputed ?
+
+ b = face[3]->pos - face[0]->pos; // ### could be precomputed ?
+ x += b.x * (ay*b.y + az*b.z) / b.magnitudeSquared(); // ### could be precomputed ?
+ }
+
+ return x;
+}
+
+// Test with respect to the quad defined by (0,-y1,-z1) and (0,y1,z1)
+// +------+ y1 y
+// | | |
+// | * | |
+// | | |
+// +------+ -y1 *-----z
+static PxI32 generateContacts(//PxVec3 ctcPts[], PxReal depths[],
+ ContactBuffer& contactBuffer, const PxVec3& contactNormal,
+ PxReal y1, PxReal z1, const PxVec3& box2,
+ const Cm::Matrix34& transform0, const Cm::Matrix34& transform1, PxReal contactDistance)
+{
+// PxI32 NbContacts=0;
+ contactBuffer.reset();
+ y1 += contactDistance;
+ z1 += contactDistance;
+
+ const Cm::Matrix34 trans1to0 = transform0.getInverseRT() * transform1;
+
+ VertexInfo vtx[8]; // The 8 cube vertices
+// PxI32 i;
+
+ // 6+------+7
+ // /| /|
+ // / | / |
+ // / 4+---/--+5
+ // 2+------+3 / y z
+ // | / | / | /
+ // |/ |/ |/
+ // 0+------+1 *---x
+
+ {
+ const PxVec3 ex = trans1to0.m.column0 * box2.x;
+ const PxVec3 ey = trans1to0.m.column1 * box2.y;
+ const PxVec3 ez = trans1to0.m.column2 * box2.z;
+
+ /*
+ vtx[0].pos = mat.pos - ex - ey - ez;
+ vtx[1].pos = mat.pos + ex - ey - ez;
+ vtx[2].pos = mat.pos - ex + ey - ez;
+ vtx[3].pos = mat.pos + ex + ey - ez;
+ vtx[4].pos = mat.pos - ex - ey + ez;
+ vtx[5].pos = mat.pos + ex - ey + ez;
+ vtx[6].pos = mat.pos - ex + ey + ez;
+ vtx[7].pos = mat.pos + ex + ey + ez;
+ */
+
+ // 12 vector ops = 12*3 = 36 FPU ops
+ vtx[0].pos = vtx[2].pos = vtx[4].pos = vtx[6].pos = trans1to0.p - ex;
+ vtx[1].pos = vtx[3].pos = vtx[5].pos = vtx[7].pos = trans1to0.p + ex;
+
+ PxVec3 e = ey+ez;
+ vtx[0].pos -= e;
+ vtx[1].pos -= e;
+ vtx[6].pos += e;
+ vtx[7].pos += e;
+
+ e = ey-ez;
+ vtx[2].pos += e;
+ vtx[3].pos += e;
+ vtx[4].pos -= e;
+ vtx[5].pos -= e;
+ }
+
+ // Create vertex info for 8 vertices
+ for(PxU32 i=0; i<8; i++)
+ {
+ // Vertex suivant
+ VertexInfo& p = vtx[i];
+ // test the point with respect to the x = 0 plane
+ // if(p.pos.x < 0)
+ if(p.pos.x < -contactDistance) //if(PXC_IS_NEGATIVE(p.pos.x))
+ {
+ p.area = false;
+ p.penetrate = false;
+ continue;
+ }
+
+ {
+ // we penetrated the quad plane
+ p.penetrate = true;
+ // test to see if we are in the quad
+ // PxAbs => thus we test Y with respect to -Y1 and +Y1 (same for Z)
+ // if(PxAbs(p->pos.y) <= y1 && PxAbs(p->pos.z) <= z1)
+ if(ABS_SMALLER_EQUAL(p.pos.y, y1) && ABS_SMALLER_EQUAL(p.pos.z, z1))
+ {
+ // the point is inside the quad
+ p.area=true;
+ // Since we are testing with respect to x = 0, the penetration is directly the x coordinate.
+// depths[NbContacts] = p.pos.x;
+
+ // We take the vertex as the impact point
+// ctcPts[NbContacts++] = p.pos;
+ contactBuffer.contact(p.pos, contactNormal, -p.pos.x);
+ }
+ else
+ {
+ p.area=false;
+ }
+ }
+ }
+
+ // Teste 12 edges on the quad
+ static const PxI32 indices[]={ 0,1, 1,3, 3,2, 2,0, 4,5, 5,7, 7,6, 6,4, 0,4, 1,5, 2,6, 3,7, };
+ const PxI32* runningLine = indices;
+ const PxI32* endLine = runningLine+24;
+ while(runningLine!=endLine)
+ {
+ // The two vertices of the current edge
+ const VertexInfo* p1 = &vtx[*runningLine++];
+ const VertexInfo* p2 = &vtx[*runningLine++];
+
+ // Penetrate|Area|Penetrate|Area => 16 cases
+
+ // We only take the edges that at least penetrated the quad's plane into account.
+ if(p1->penetrate || p2->penetrate)
+ // if(p1->penetrate + p2->penetrate) // One branch only
+ {
+ // If at least one of the two vertices is not in the quad...
+ if(!p1->area || !p2->area)
+ // if(!p1->area + !p2->area) // One branch only
+ {
+ // Test y
+ if(p1->pos.y > p2->pos.y) { const VertexInfo* tmp=p1; p1=p2; p2=tmp; }
+ // Impact on the +Y1 edge of the quad
+ if(p1->pos.y < +y1 && p2->pos.y >= +y1)
+ // => a point under Y1, the other above
+ {
+ // Case 1
+ PxReal a = (+y1 - p1->pos.y)/(p2->pos.y - p1->pos.y);
+ PxReal z = p1->pos.z + (p2->pos.z - p1->pos.z)*a;
+ if(PxAbs(z) <= z1)
+ {
+ PxReal x = p1->pos.x + (p2->pos.x - p1->pos.x)*a;
+ if(x+contactDistance>=0.0f)
+ {
+// depths[NbContacts] = x;
+// ctcPts[NbContacts++] = PxVec3(x, y1, z);
+ contactBuffer.contact(PxVec3(x, y1, z), contactNormal, -x);
+ }
+ }
+ }
+ // Impact on the edge -Y1 of the quad
+ if(p1->pos.y < -y1 && p2->pos.y >= -y1)
+ {
+ // Case 2
+ PxReal a = (-y1 - p1->pos.y)/(p2->pos.y - p1->pos.y);
+ PxReal z = p1->pos.z + (p2->pos.z - p1->pos.z)*a;
+ if(PxAbs(z) <= z1)
+ {
+ PxReal x = p1->pos.x + (p2->pos.x - p1->pos.x)*a;
+ if(x+contactDistance>=0.0f)
+ {
+// depths[NbContacts] = x;
+// ctcPts[NbContacts++] = PxVec3(x, -y1, z);
+ contactBuffer.contact(PxVec3(x, -y1, z), contactNormal, -x);
+ }
+ }
+ }
+
+ // Test z
+ if(p1->pos.z > p2->pos.z) { const VertexInfo* tmp=p1; p1=p2; p2=tmp; }
+ // Impact on the edge +Z1 of the quad
+ if(p1->pos.z < +z1 && p2->pos.z >= +z1)
+ {
+ // Case 3
+ PxReal a = (+z1 - p1->pos.z)/(p2->pos.z - p1->pos.z);
+ PxReal y = p1->pos.y + (p2->pos.y - p1->pos.y)*a;
+ if(PxAbs(y) <= y1)
+ {
+ PxReal x = p1->pos.x + (p2->pos.x - p1->pos.x)*a;
+ if(x+contactDistance>=0.0f)
+ {
+// depths[NbContacts] = x;
+// ctcPts[NbContacts++] = PxVec3(x, y, z1);
+ contactBuffer.contact(PxVec3(x, y, z1), contactNormal, -x);
+ }
+ }
+ }
+ // Impact on the edge -Z1 of the quad
+ if(p1->pos.z < -z1 && p2->pos.z >= -z1)
+ {
+ // Case 4
+ PxReal a = (-z1 - p1->pos.z)/(p2->pos.z - p1->pos.z);
+ PxReal y = p1->pos.y + (p2->pos.y - p1->pos.y)*a;
+ if(PxAbs(y) <= y1)
+ {
+ PxReal x = p1->pos.x + (p2->pos.x - p1->pos.x)*a;
+ if(x+contactDistance>=0.0f)
+ {
+// depths[NbContacts] = x;
+// ctcPts[NbContacts++] = PxVec3(x, y, -z1);
+ contactBuffer.contact(PxVec3(x, y, -z1), contactNormal, -x);
+ }
+ }
+ }
+ }
+
+ // The case where one point penetrates the plane, and the other is not in the quad.
+ if((!p1->penetrate && !p2->area) || (!p2->penetrate && !p1->area))
+ {
+ // Case 5
+ PxReal a = (-p1->pos.x)/(p2->pos.x - p1->pos.x);
+ PxReal y = p1->pos.y + (p2->pos.y - p1->pos.y)*a;
+ if(PxAbs(y) <= y1)
+ {
+ PxReal z = p1->pos.z + (p2->pos.z - p1->pos.z)*a;
+ if(PxAbs(z) <= z1)
+ {
+// depths[NbContacts] = 0;
+// ctcPts[NbContacts++] = PxVec3(0, y, z);
+ contactBuffer.contact(PxVec3(0, y, z), contactNormal, 0);
+ }
+ }
+ }
+
+ }
+ }
+
+ {
+ // 6 quads => 6 faces of the cube
+ static const PxI32 face[][4]={ {0,1,3,2}, {1,5,7,3}, {5,4,6,7}, {4,0,2,6}, {2,3,7,6}, {0,4,5,1} };
+ PxI32 addflg=0;
+ for(PxU32 i=0; i<6 && addflg!=0x0f; i++)
+ {
+ const PxI32* p = face[i];
+ const VertexInfo* q[4];
+ if((q[0]=&vtx[p[0]])->penetrate && (q[1]=&vtx[p[1]])->penetrate && (q[2]=&vtx[p[2]])->penetrate && (q[3]=&vtx[p[3]])->penetrate)
+ {
+ if(!q[0]->area || !q[1]->area || !q[2]->area || !q[3]->area)
+ {
+ if(!(addflg&1)) { PxReal x = IsInYZ(-y1, -z1, q); if(x>=0.0f) { addflg|=1; contactBuffer.contact(PxVec3(x, -y1, -z1), contactNormal, -x); /*depths[NbContacts]=x; ctcPts[NbContacts++] = PxVec3(x, -y1, -z1);*/ } }
+ if(!(addflg&2)) { PxReal x = IsInYZ(+y1, -z1, q); if(x>=0.0f) { addflg|=2; contactBuffer.contact(PxVec3(x, +y1, -z1), contactNormal, -x); /*depths[NbContacts]=x; ctcPts[NbContacts++] = PxVec3(x, +y1, -z1);*/ } }
+ if(!(addflg&4)) { PxReal x = IsInYZ(-y1, +z1, q); if(x>=0.0f) { addflg|=4; contactBuffer.contact(PxVec3(x, -y1, +z1), contactNormal, -x); /*depths[NbContacts]=x; ctcPts[NbContacts++] = PxVec3(x, -y1, +z1);*/ } }
+ if(!(addflg&8)) { PxReal x = IsInYZ(+y1, +z1, q); if(x>=0.0f) { addflg|=8; contactBuffer.contact(PxVec3(x, +y1, +z1), contactNormal, -x); /*depths[NbContacts]=x; ctcPts[NbContacts++] = PxVec3(x, +y1, +z1);*/ } }
+ }
+ }
+ }
+ }
+
+// for(i=0; i<NbContacts; i++)
+ for(PxU32 i=0; i<contactBuffer.count; i++)
+// ctcPts[i] = transform0.transform(ctcPts[i]); // local to world
+ contactBuffer.contacts[i].point = transform0.transform(contactBuffer.contacts[i].point); // local to world
+
+ //PX_ASSERT(NbContacts); //if this did not make contacts then something went wrong in theory, but even the old code without distances had this flaw!
+// return NbContacts;
+ return PxI32(contactBuffer.count);
+}
+
+//static PxI32 doBoxBoxContactGeneration(PxVec3 ctcPts[MAX_NB_CTCS], PxReal depths[MAX_NB_CTCS], PxVec3* ctcNrm,
+static PxI32 doBoxBoxContactGeneration(ContactBuffer& contactBuffer,
+ const PxVec3& extents0, const PxVec3& extents1,
+ PxU32& collisionData,
+ const Cm::Matrix34& transform0, const Cm::Matrix34& transform1, PxReal contactDistance)
+{
+ PxReal aafC[3][3]; // matrix C = A^T B, c_{ij} = Dot(A_i,B_j)
+ PxReal aafAbsC[3][3]; // |c_{ij}|
+ PxReal afAD[3]; // Dot(A_i,D)
+
+ PxReal d1[6];
+ PxReal overlap[6];
+
+ PxVec3 kD = transform1.p - transform0.p;
+
+ const PxVec3& axis00 = transform0.m.column0;
+ const PxVec3& axis01 = transform0.m.column1;
+ const PxVec3& axis02 = transform0.m.column2;
+ const PxVec3& axis10 = transform1.m.column0;
+ const PxVec3& axis11 = transform1.m.column1;
+ const PxVec3& axis12 = transform1.m.column2;
+
+ // Perform Class I tests
+
+ aafC[0][0] = axis00.dot(axis10);
+ aafC[0][1] = axis00.dot(axis11);
+ aafC[0][2] = axis00.dot(axis12);
+ afAD[0] = axis00.dot(kD);
+ aafAbsC[0][0] = 1e-6f + PxAbs(aafC[0][0]);
+ aafAbsC[0][1] = 1e-6f + PxAbs(aafC[0][1]);
+ aafAbsC[0][2] = 1e-6f + PxAbs(aafC[0][2]);
+ d1[AXIS_A0] = afAD[0];
+ PxReal d0 = extents0.x + extents1.x*aafAbsC[0][0] + extents1.y*aafAbsC[0][1] + extents1.z*aafAbsC[0][2];
+ overlap[AXIS_A0] = d0 - PxAbs(d1[AXIS_A0]) + contactDistance;
+ if(PXC_IS_NEGATIVE(overlap[AXIS_A0])) return 0;
+
+ aafC[1][0] = axis01.dot(axis10);
+ aafC[1][1] = axis01.dot(axis11);
+ aafC[1][2] = axis01.dot(axis12);
+ afAD[1] = axis01.dot(kD);
+ aafAbsC[1][0] = 1e-6f + PxAbs(aafC[1][0]);
+ aafAbsC[1][1] = 1e-6f + PxAbs(aafC[1][1]);
+ aafAbsC[1][2] = 1e-6f + PxAbs(aafC[1][2]);
+ d1[AXIS_A1] = afAD[1];
+ d0 = extents0.y + extents1.x*aafAbsC[1][0] + extents1.y*aafAbsC[1][1] + extents1.z*aafAbsC[1][2];
+ overlap[AXIS_A1] = d0 - PxAbs(d1[AXIS_A1]) + contactDistance;
+ if(PXC_IS_NEGATIVE(overlap[AXIS_A1])) return 0;
+
+ aafC[2][0] = axis02.dot(axis10);
+ aafC[2][1] = axis02.dot(axis11);
+ aafC[2][2] = axis02.dot(axis12);
+ afAD[2] = axis02.dot(kD);
+ aafAbsC[2][0] = 1e-6f + PxAbs(aafC[2][0]);
+ aafAbsC[2][1] = 1e-6f + PxAbs(aafC[2][1]);
+ aafAbsC[2][2] = 1e-6f + PxAbs(aafC[2][2]);
+ d1[AXIS_A2] = afAD[2];
+ d0 = extents0.z + extents1.x*aafAbsC[2][0] + extents1.y*aafAbsC[2][1] + extents1.z*aafAbsC[2][2];
+ overlap[AXIS_A2] = d0 - PxAbs(d1[AXIS_A2]) + contactDistance;
+ if(PXC_IS_NEGATIVE(overlap[AXIS_A2])) return 0;
+
+ // Perform Class II tests
+
+ d1[AXIS_B0] = axis10.dot(kD);
+ d0 = extents1.x + extents0.x*aafAbsC[0][0] + extents0.y*aafAbsC[1][0] + extents0.z*aafAbsC[2][0];
+ overlap[AXIS_B0] = d0 - PxAbs(d1[AXIS_B0]) + contactDistance;
+ if(PXC_IS_NEGATIVE(overlap[AXIS_B0])) return 0;
+
+ d1[AXIS_B1] = axis11.dot(kD);
+ d0 = extents1.y + extents0.x*aafAbsC[0][1] + extents0.y*aafAbsC[1][1] + extents0.z*aafAbsC[2][1];
+ overlap[AXIS_B1] = d0 - PxAbs(d1[AXIS_B1]) + contactDistance;
+ if(PXC_IS_NEGATIVE(overlap[AXIS_B1])) return 0;
+
+ d1[AXIS_B2] = axis12.dot(kD);
+ d0 = extents1.z + extents0.x*aafAbsC[0][2] + extents0.y*aafAbsC[1][2] + extents0.z*aafAbsC[2][2];
+ overlap[AXIS_B2] = d0 - PxAbs(d1[AXIS_B2]) + contactDistance;
+ if(PXC_IS_NEGATIVE(overlap[AXIS_B2])) return 0;
+
+ // Perform Class III tests - we don't need to store distances for those ones.
+ // We only test those axes when objects are likely to be separated, i.e. when they where previously non-colliding. For stacks, we'll have
+ // to do full contact generation anyway, and those tests are useless - so we skip them. This is similar to what I did in Opcode.
+ if(!collisionData) // separated or first run
+ {
+ PxReal d = afAD[2]*aafC[1][0] - afAD[1]*aafC[2][0];
+ d0 = contactDistance + extents0.y*aafAbsC[2][0] + extents0.z*aafAbsC[1][0] + extents1.y*aafAbsC[0][2] + extents1.z*aafAbsC[0][1];
+ if(ABS_GREATER(d, d0)) return 0;
+
+ d = afAD[2]*aafC[1][1] - afAD[1]*aafC[2][1];
+ d0 = contactDistance + extents0.y*aafAbsC[2][1] + extents0.z*aafAbsC[1][1] + extents1.x*aafAbsC[0][2] + extents1.z*aafAbsC[0][0];
+ if(ABS_GREATER(d, d0)) return 0;
+
+ d = afAD[2]*aafC[1][2] - afAD[1]*aafC[2][2];
+ d0 = contactDistance + extents0.y*aafAbsC[2][2] + extents0.z*aafAbsC[1][2] + extents1.x*aafAbsC[0][1] + extents1.y*aafAbsC[0][0];
+ if(ABS_GREATER(d, d0)) return 0;
+
+ d = afAD[0]*aafC[2][0] - afAD[2]*aafC[0][0];
+ d0 = contactDistance + extents0.x*aafAbsC[2][0] + extents0.z*aafAbsC[0][0] + extents1.y*aafAbsC[1][2] + extents1.z*aafAbsC[1][1];
+ if(ABS_GREATER(d, d0)) return 0;
+
+ d = afAD[0]*aafC[2][1] - afAD[2]*aafC[0][1];
+ d0 = contactDistance + extents0.x*aafAbsC[2][1] + extents0.z*aafAbsC[0][1] + extents1.x*aafAbsC[1][2] + extents1.z*aafAbsC[1][0];
+ if(ABS_GREATER(d, d0)) return 0;
+
+ d = afAD[0]*aafC[2][2] - afAD[2]*aafC[0][2];
+ d0 = contactDistance + extents0.x*aafAbsC[2][2] + extents0.z*aafAbsC[0][2] + extents1.x*aafAbsC[1][1] + extents1.y*aafAbsC[1][0];
+ if(ABS_GREATER(d, d0)) return 0;
+
+ d = afAD[1]*aafC[0][0] - afAD[0]*aafC[1][0];
+ d0 = contactDistance + extents0.x*aafAbsC[1][0] + extents0.y*aafAbsC[0][0] + extents1.y*aafAbsC[2][2] + extents1.z*aafAbsC[2][1];
+ if(ABS_GREATER(d, d0)) return 0;
+
+ d = afAD[1]*aafC[0][1] - afAD[0]*aafC[1][1];
+ d0 = contactDistance + extents0.x*aafAbsC[1][1] + extents0.y*aafAbsC[0][1] + extents1.x*aafAbsC[2][2] + extents1.z*aafAbsC[2][0];
+ if(ABS_GREATER(d, d0)) return 0;
+
+ d = afAD[1]*aafC[0][2] - afAD[0]*aafC[1][2];
+ d0 = contactDistance + extents0.x*aafAbsC[1][2] + extents0.y*aafAbsC[0][2] + extents1.x*aafAbsC[2][1] + extents1.y*aafAbsC[2][0];
+ if(ABS_GREATER(d, d0)) return 0;
+ }
+
+ /* djs - tempUserData can be zero when it gets here
+ - maybe if there was no previous axis?
+ - which causes stack corruption, and thence a crash, in .NET
+
+ PT: right! At first tempUserData wasn't ever supposed to be zero, but then I used that
+ value to mark separation of boxes, and forgot to update the code below. Now I think
+ the test is redundant with the one performed above, and the line could eventually
+ be merged in the previous block. I'll do that later when removing all the #defines.
+ */
+ // NB: the "16" here has nothing to do with MAX_NB_CTCS. Don't touch.
+ if(collisionData) // if initialized & not previously separated
+ overlap[collisionData-1] *= 0.999f; // Favorise previous axis .999 is too little.
+
+ PxReal minimum = PX_MAX_REAL;
+ PxI32 minIndex = 0;
+
+ for(PxU32 i=AXIS_A0; i<6; i++)
+ {
+ PxReal d = overlap[i];
+
+ if(d>=0.0f && d<minimum) { minimum=d; minIndex=PxI32(i); } // >=0 !! otherwise bug at sep = 0
+ }
+
+ collisionData = PxU32(minIndex + 1); // Leave "0" for separation
+
+#if PX_X86
+ const PxU32 sign = PXC_IS_NEGATIVE(d1[minIndex]);
+#else
+ const PxU32 sign = PxU32(PXC_IS_NEGATIVE(d1[minIndex]));
+#endif
+ Cm::Matrix34 trs;
+ PxVec3 ctcNrm;
+
+ switch(minIndex)
+ {
+ default:
+ return 0;
+
+ case AXIS_A0:
+// *ctcNrm = axis00;
+ if(sign)
+ {
+ ctcNrm = axis00;
+ trs.m = transform0.m;
+ trs.p = transform0.p - extents0.x*axis00;
+ }
+ else
+ {
+// *ctcNrm = -*ctcNrm;
+ ctcNrm = -axis00;
+
+ trs.m.column0 = -axis00;
+ trs.m.column1 = -axis01;
+ trs.m.column2 = axis02;
+ trs.p = transform0.p + extents0.x*axis00;
+ }
+// return generateContacts(ctcPts, depths, extents0.y, extents0.z, extents1, trs, transform1, contactDistance);
+ return generateContacts(contactBuffer, ctcNrm, extents0.y, extents0.z, extents1, trs, transform1, contactDistance);
+
+ case AXIS_A1:
+// *ctcNrm = axis01;
+ trs.m.column2 = axis00; // Factored out
+ if(sign)
+ {
+ ctcNrm = axis01;
+ trs.m.column0 = axis01;
+ trs.m.column1 = axis02;
+ trs.p = transform0.p - extents0.y*axis01;
+ }
+ else
+ {
+// *ctcNrm = -*ctcNrm;
+ ctcNrm = -axis01;
+
+ trs.m.column0 = -axis01;
+ trs.m.column1 = -axis02;
+ trs.p = transform0.p + extents0.y*axis01;
+ }
+// return generateContacts(ctcPts, depths, extents0.z, extents0.x, extents1, trs, transform1, contactDistance);
+ return generateContacts(contactBuffer, ctcNrm, extents0.z, extents0.x, extents1, trs, transform1, contactDistance);
+
+ case AXIS_A2:
+// *ctcNrm = axis02;
+ trs.m.column2 = axis01; // Factored out
+
+ if(sign)
+ {
+ ctcNrm = axis02;
+ trs.m.column0 = axis02;
+ trs.m.column1 = axis00;
+ trs.p = transform0.p - extents0.z*axis02;
+ }
+ else
+ {
+// *ctcNrm = -*ctcNrm;
+ ctcNrm = -axis02;
+
+ trs.m.column0 = -axis02;
+ trs.m.column1 = -axis00;
+ trs.p = transform0.p + extents0.z*axis02;
+ }
+// return generateContacts(ctcPts, depths, extents0.x, extents0.y, extents1, trs, transform1, contactDistance);
+ return generateContacts(contactBuffer, ctcNrm, extents0.x, extents0.y, extents1, trs, transform1, contactDistance);
+
+ case AXIS_B0:
+// *ctcNrm = axis10;
+ if(sign)
+ {
+ ctcNrm = axis10;
+ trs.m.column0 = -axis10;
+ trs.m.column1 = -axis11;
+ trs.m.column2 = axis12;
+ trs.p = transform1.p + extents1.x*axis10;
+ }
+ else
+ {
+// *ctcNrm = -*ctcNrm;
+ ctcNrm = -axis10;
+ trs.m = transform1.m;
+ trs.p = transform1.p - extents1.x*axis10;
+
+ }
+// return generateContacts(ctcPts, depths, extents1.y, extents1.z, extents0, trs, transform0, contactDistance);
+ return generateContacts(contactBuffer, ctcNrm, extents1.y, extents1.z, extents0, trs, transform0, contactDistance);
+
+ case AXIS_B1:
+// *ctcNrm = axis11;
+ trs.m.column2 = axis10; // Factored out
+ if(sign)
+ {
+ ctcNrm = axis11;
+ trs.m.column0 = -axis11;
+ trs.m.column1 = -axis12;
+ trs.p = transform1.p + extents1.y*axis11;
+ }
+ else
+ {
+// *ctcNrm = -*ctcNrm;
+ ctcNrm = -axis11;
+
+ trs.m.column0 = axis11;
+ trs.m.column1 = axis12;
+ trs.m.column2 = axis10;
+ trs.p = transform1.p - extents1.y*axis11;
+ }
+// return generateContacts(ctcPts, depths, extents1.z, extents1.x, extents0, trs, transform0, contactDistance);
+ return generateContacts(contactBuffer, ctcNrm, extents1.z, extents1.x, extents0, trs, transform0, contactDistance);
+
+ case AXIS_B2:
+// *ctcNrm = axis12;
+ trs.m.column2 = axis11; // Factored out
+
+ if(sign)
+ {
+ ctcNrm = axis12;
+ trs.m.column0 = -axis12;
+ trs.m.column1 = -axis10;
+ trs.p = transform1.p + extents1.z*axis12;
+ }
+ else
+ {
+// *ctcNrm = -*ctcNrm;
+ ctcNrm = -axis12;
+
+ trs.m.column0 = axis12;
+ trs.m.column1 = axis10;
+ trs.p = transform1.p - extents1.z*axis12;
+ }
+
+// return generateContacts(ctcPts, depths, extents1.x, extents1.y, extents0, trs, transform0, contactDistance);
+ return generateContacts(contactBuffer, ctcNrm, extents1.x, extents1.y, extents0, trs, transform0, contactDistance);
+ }
+}
diff --git a/PhysX_3.4/Source/GeomUtils/src/contact/GuContactCapsuleBox.cpp b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactCapsuleBox.cpp
new file mode 100644
index 00000000..75e737d4
--- /dev/null
+++ b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactCapsuleBox.cpp
@@ -0,0 +1,457 @@
+// 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 "GuContactBuffer.h"
+#include "GuIntersectionRayBox.h"
+#include "GuDistanceSegmentBox.h"
+#include "GuInternal.h"
+#include "GuContactMethodImpl.h"
+#include "PsMathUtils.h"
+#include "PsUtilities.h"
+#include "GuGeometryUnion.h"
+#include "GuBoxConversion.h"
+
+using namespace physx;
+using namespace Gu;
+
+/*namespace Gu
+{
+const PxU8* getBoxEdges();
+}*/
+
+/////////
+ /*#include "CmRenderOutput.h"
+ #include "PxsContext.h"
+ static void gVisualizeBox(const Box& box, PxcNpThreadContext& context, PxU32 color=0xffffff)
+ {
+ PxMat33 rot(box.base.column0, box.base.column1, box.base.column2);
+ PxMat44 m(rot, box.origin);
+
+ DebugBox db(box.extent);
+
+ Cm::RenderOutput& out = context.mRenderOutput;
+ out << color << m;
+ out << db;
+ }
+ static void gVisualizeLine(const PxVec3& a, const PxVec3& b, PxcNpThreadContext& context, PxU32 color=0xffffff)
+ {
+ PxMat44 m = PxMat44::identity();
+
+ Cm::RenderOutput& out = context.mRenderOutput;
+ out << color << m << Cm::RenderOutput::LINES << a << b;
+ }*/
+/////////
+
+
+static const PxReal fatBoxEdgeCoeff = 0.01f;
+
+static bool intersectEdgeEdgePreca(const PxVec3& p1, const PxVec3& p2, const PxVec3& v1, const PxPlane& plane, PxU32 i, PxU32 j, float coeff, const PxVec3& dir, const PxVec3& p3, const PxVec3& p4, PxReal& dist, PxVec3& ip)
+{
+ // if colliding edge (p3,p4) does not cross plane return no collision
+ // same as if p3 and p4 on same side of plane return 0
+ //
+ // Derivation:
+ // d3 = d(p3, P) = (p3 | plane.n) - plane.d; Reversed sign compared to Plane::Distance() because plane.d is negated.
+ // d4 = d(p4, P) = (p4 | plane.n) - plane.d; Reversed sign compared to Plane::Distance() because plane.d is negated.
+ // if d3 and d4 have the same sign, they're on the same side of the plane => no collision
+ // We test both sides at the same time by only testing Sign(d3 * d4).
+ // ### put that in the Plane class
+ // ### also check that code in the triangle class that might be similar
+ 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
+ PxVec3 v2 = p4 - p3;
+
+ temp = plane.n.dot(v2);
+ if(temp==0.0f) return false; // ### epsilon would be better
+
+ // 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
+}
+
+
+static bool GuTestAxis(const PxVec3& axis, const Segment& segment, PxReal radius, const Box& box, PxReal& depth)
+{
+ // Project capsule
+ PxReal min0 = segment.p0.dot(axis);
+ PxReal max0 = segment.p1.dot(axis);
+ if(min0>max0) Ps::swap(min0, max0);
+ min0 -= radius;
+ max0 += radius;
+
+ // Project box
+ PxReal Min1, Max1;
+ {
+ const PxReal BoxCen = box.center.dot(axis);
+ const PxReal BoxExt =
+ PxAbs(box.rot.column0.dot(axis)) * box.extents.x
+ + PxAbs(box.rot.column1.dot(axis)) * box.extents.y
+ + PxAbs(box.rot.column2.dot(axis)) * box.extents.z;
+
+ Min1 = BoxCen - BoxExt;
+ Max1 = BoxCen + BoxExt;
+ }
+
+ // Test projections
+ if(max0<Min1 || Max1<min0)
+ return false;
+
+ const PxReal d0 = max0 - Min1;
+ PX_ASSERT(d0>=0.0f);
+ const PxReal d1 = Max1 - min0;
+ PX_ASSERT(d1>=0.0f);
+ depth = physx::intrinsics::selectMin(d0, d1);
+ return true;
+}
+
+
+static bool GuCapsuleOBBOverlap3(const Segment& segment, PxReal radius, const Box& box, PxReal* t=NULL, PxVec3* pp=NULL)
+{
+ PxVec3 Sep(PxReal(0));
+ PxReal PenDepth = PX_MAX_REAL;
+
+ // Test normals
+ for(PxU32 i=0;i<3;i++)
+ {
+ PxReal d;
+ if(!GuTestAxis(box.rot[i], segment, radius, box, d))
+ return false;
+
+ if(d<PenDepth)
+ {
+ PenDepth = d;
+ Sep = box.rot[i];
+ }
+ }
+
+ // Test edges
+ PxVec3 CapsuleAxis(segment.p1 - segment.p0);
+ CapsuleAxis = CapsuleAxis.getNormalized();
+ for(PxU32 i=0;i<3;i++)
+ {
+ PxVec3 Cross = CapsuleAxis.cross(box.rot[i]);
+ if(!Ps::isAlmostZero(Cross))
+ {
+ Cross = Cross.getNormalized();
+ PxReal d;
+ if(!GuTestAxis(Cross, segment, radius, box, d))
+ return false;
+
+ if(d<PenDepth)
+ {
+ PenDepth = d;
+ Sep = Cross;
+ }
+ }
+ }
+
+ const PxVec3 Witness = segment.computeCenter() - box.center;
+
+ if(Sep.dot(Witness) < 0.0f)
+ Sep = -Sep;
+
+ if(t)
+ *t = PenDepth;
+ if(pp)
+ *pp = Sep;
+
+ return true;
+}
+
+
+static void GuGenerateVFContacts( ContactBuffer& contactBuffer,
+ //
+ const Segment& segment,
+ const PxReal radius,
+ //
+ const Box& worldBox,
+ //
+ const PxVec3& normal,
+ const PxReal contactDistance)
+{
+ const PxVec3 Max = worldBox.extents;
+ const PxVec3 Min = -worldBox.extents;
+
+ const PxVec3 tmp2 = - worldBox.rot.transformTranspose(normal);
+
+ const PxVec3* PX_RESTRICT Ptr = &segment.p0;
+ for(PxU32 i=0;i<2;i++)
+ {
+ const PxVec3& Pos = Ptr[i];
+
+ const PxVec3 tmp = worldBox.rot.transformTranspose(Pos - worldBox.center);
+ PxReal tnear, tfar;
+ int Res = intersectRayAABB(Min, Max, tmp, tmp2, tnear, tfar);
+
+ if(Res!=-1 && tnear < radius + contactDistance)
+ {
+ contactBuffer.contact(Pos - tnear * normal, normal, tnear - radius);
+ }
+ }
+}
+
+
+// PT: this looks similar to PxcGenerateEEContacts2 but it is mandatory to properly handle thin capsules.
+static void GuGenerateEEContacts( ContactBuffer& contactBuffer,
+ //
+ const Segment& segment,
+ const PxReal radius,
+ //
+ const Box& worldBox,
+ //
+ const PxVec3& normal)
+{
+ const PxU8* PX_RESTRICT Indices = getBoxEdges();
+
+ PxVec3 Pts[8];
+ worldBox.computeBoxPoints(Pts);
+
+ PxVec3 s0 = segment.p0;
+ PxVec3 s1 = segment.p1;
+ Ps::makeFatEdge(s0, s1, fatBoxEdgeCoeff);
+
+ // PT: precomputed part of edge-edge intersection test
+// const PxVec3 v1 = segment.p1 - segment.p0;
+ const PxVec3 v1 = s1 - s0;
+ PxPlane plane;
+ plane.n = v1.cross(normal);
+// plane.d = -(plane.normal|segment.p0);
+ plane.d = -(plane.n.dot(s0));
+
+ PxU32 ii,jj;
+ Ps::closestAxis(plane.n, ii, jj);
+
+ const float coeff = 1.0f /(v1[ii]*normal[jj]-v1[jj]*normal[ii]);
+
+
+ for(PxU32 i=0;i<12;i++)
+ {
+// PxVec3 p1 = Pts[*Indices++];
+// PxVec3 p2 = Pts[*Indices++];
+// Ps::makeFatEdge(p1, p2, fatBoxEdgeCoeff); // PT: TODO: make fat segment instead
+ const PxVec3& p1 = Pts[*Indices++];
+ const PxVec3& p2 = Pts[*Indices++];
+
+// PT: keep original code in case something goes wrong
+// PxReal dist;
+// PxVec3 ip;
+// if(intersectEdgeEdge(p1, p2, -normal, segment.p0, segment.p1, dist, ip))
+// contactBuffer.contact(ip, normal, - (radius + dist));
+
+ PxReal dist;
+ PxVec3 ip;
+
+
+ if(intersectEdgeEdgePreca(s0, s1, v1, plane, ii, jj, coeff, normal, p1, p2, dist, ip))
+// if(intersectEdgeEdgePreca(segment.p0, segment.p1, v1, plane, ii, jj, coeff, normal, p1, p2, dist, ip))
+ {
+ contactBuffer.contact(ip-normal*dist, normal, - (radius + dist));
+// if(contactBuffer.count==2) // PT: we only need 2 contacts to be stable
+// return;
+ }
+ }
+}
+
+
+static void GuGenerateEEContacts2( ContactBuffer& contactBuffer,
+ //
+ const Segment& segment,
+ const PxReal radius,
+ //
+ const Box& worldBox,
+ //
+ const PxVec3& normal,
+ const PxReal contactDistance)
+{
+ const PxU8* PX_RESTRICT Indices = getBoxEdges();
+
+ PxVec3 Pts[8];
+ worldBox.computeBoxPoints(Pts);
+
+ PxVec3 s0 = segment.p0;
+ PxVec3 s1 = segment.p1;
+ Ps::makeFatEdge(s0, s1, fatBoxEdgeCoeff);
+
+ // PT: precomputed part of edge-edge intersection test
+// const PxVec3 v1 = segment.p1 - segment.p0;
+ const PxVec3 v1 = s1 - s0;
+ PxPlane plane;
+ plane.n = -(v1.cross(normal));
+// plane.d = -(plane.normal|segment.p0);
+ plane.d = -(plane.n.dot(s0));
+
+ PxU32 ii,jj;
+ Ps::closestAxis(plane.n, ii, jj);
+
+ const float coeff = 1.0f /(v1[jj]*normal[ii]-v1[ii]*normal[jj]);
+
+ for(PxU32 i=0;i<12;i++)
+ {
+// PxVec3 p1 = Pts[*Indices++];
+// PxVec3 p2 = Pts[*Indices++];
+// Ps::makeFatEdge(p1, p2, fatBoxEdgeCoeff); // PT: TODO: make fat segment instead
+ const PxVec3& p1 = Pts[*Indices++];
+ const PxVec3& p2 = Pts[*Indices++];
+
+// PT: keep original code in case something goes wrong
+// PxReal dist;
+// PxVec3 ip;
+// bool contact = intersectEdgeEdge(p1, p2, normal, segment.p0, segment.p1, dist, ip);
+// if(contact && dist < radius + contactDistance)
+// contactBuffer.contact(ip, normal, dist - radius);
+
+ PxReal dist;
+ PxVec3 ip;
+// bool contact = intersectEdgeEdgePreca(segment.p0, segment.p1, v1, plane, ii, jj, coeff, -normal, p1, p2, dist, ip);
+ bool contact = intersectEdgeEdgePreca(s0, s1, v1, plane, ii, jj, coeff, -normal, p1, p2, dist, ip);
+ if(contact && dist < radius + contactDistance)
+ {
+ contactBuffer.contact(ip-normal*dist, normal, dist - radius);
+// if(contactBuffer.count==2) // PT: we only need 2 contacts to be stable
+// return;
+ }
+ }
+}
+
+namespace physx
+{
+namespace Gu
+{
+bool contactCapsuleBox(GU_CONTACT_METHOD_ARGS)
+{
+ PX_UNUSED(renderOutput);
+ PX_UNUSED(cache);
+
+ // Get actual shape data
+ const PxCapsuleGeometry& shapeCapsule = shape0.get<const PxCapsuleGeometry>();
+ const PxBoxGeometry& shapeBox = shape1.get<const PxBoxGeometry>();
+
+ // PT: TODO: move computations to local space
+
+ // Capsule data
+ Segment worldSegment;
+ getCapsuleSegment(transform0, shapeCapsule, worldSegment);
+ const PxReal inflatedRadius = shapeCapsule.radius + params.mContactDistance;
+
+ // Box data
+ Box worldBox;
+ buildFrom(worldBox, transform1.p, shapeBox.halfExtents, transform1.q);
+
+ // Collision detection
+ PxReal t;
+ PxVec3 onBox;
+ const PxReal squareDist = distanceSegmentBoxSquared(worldSegment.p0, worldSegment.p1, worldBox.center, worldBox.extents, worldBox.rot, &t, &onBox);
+
+ if(squareDist >= inflatedRadius*inflatedRadius)
+ return false;
+
+ PX_ASSERT(contactBuffer.count==0);
+
+ if(squareDist != 0.0f)
+ {
+ // PT: the capsule segment doesn't intersect the box => distance-based version
+ const PxVec3 onSegment = worldSegment.getPointAt(t);
+ onBox = worldBox.center + worldBox.rot.transform(onBox);
+
+ PxVec3 normal = onSegment - onBox;
+ PxReal normalLen = normal.magnitude();
+
+ if(normalLen > 0.0f)
+ {
+ normal *= 1.0f/normalLen;
+
+ // PT: generate VF contacts for segment's vertices vs box
+ GuGenerateVFContacts(contactBuffer, worldSegment, shapeCapsule.radius, worldBox, normal, params.mContactDistance);
+
+ // PT: early exit if we already have 2 stable contacts
+ if(contactBuffer.count==2)
+ return true;
+
+ // PT: else generate slower EE contacts
+ GuGenerateEEContacts2(contactBuffer, worldSegment, shapeCapsule.radius, worldBox, normal, params.mContactDistance);
+
+ // PT: run VF case for box-vertex-vs-capsule only if we don't have any contact yet
+ if(!contactBuffer.count)
+ contactBuffer.contact(onBox, normal, sqrtf(squareDist) - shapeCapsule.radius);
+ }
+ else
+ {
+ // On linux we encountered the following:
+ // For a case where a segment endpoint lies on the surface of a box, the squared distance between segment and box was tiny but still larger than 0.
+ // However, the computation of the normal length was exactly 0. In that case we should have switched to the penetration based version so we do it now
+ // instead.
+ goto PenetrationBasedCode;
+ }
+ }
+ else
+ {
+ PenetrationBasedCode:
+
+ // PT: the capsule segment intersects the box => penetration-based version
+
+ // PT: compute penetration vector (MTD)
+ PxVec3 sepAxis;
+ PxReal depth;
+ if(!GuCapsuleOBBOverlap3(worldSegment, shapeCapsule.radius, worldBox, &depth, &sepAxis)) return false;
+
+ // PT: generate VF contacts for segment's vertices vs box
+ GuGenerateVFContacts(contactBuffer, worldSegment, shapeCapsule.radius, worldBox, sepAxis, params.mContactDistance);
+
+ // PT: early exit if we already have 2 stable contacts
+ if(contactBuffer.count==2)
+ return true;
+
+ // PT: else generate slower EE contacts
+ GuGenerateEEContacts(contactBuffer, worldSegment, shapeCapsule.radius, worldBox, sepAxis);
+
+ if(!contactBuffer.count)
+ {
+ contactBuffer.contact(worldSegment.computeCenter(), sepAxis, -(shapeCapsule.radius + depth));
+ return true;
+ }
+ }
+ return true;
+}
+}//Gu
+}//physx
diff --git a/PhysX_3.4/Source/GeomUtils/src/contact/GuContactCapsuleCapsule.cpp b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactCapsuleCapsule.cpp
new file mode 100644
index 00000000..f2de1078
--- /dev/null
+++ b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactCapsuleCapsule.cpp
@@ -0,0 +1,155 @@
+// 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 "GuContactBuffer.h"
+#include "GuDistanceSegmentSegment.h"
+#include "GuContactMethodImpl.h"
+#include "GuInternal.h"
+#include "GuGeometryUnion.h"
+
+using namespace physx;
+
+namespace physx
+{
+namespace Gu
+{
+bool contactCapsuleCapsule(GU_CONTACT_METHOD_ARGS)
+{
+ PX_UNUSED(renderOutput);
+ PX_UNUSED(cache);
+
+ const PxCapsuleGeometry& capsuleGeom0 = shape0.get<const PxCapsuleGeometry>();
+ const PxCapsuleGeometry& capsuleGeom1 = shape1.get<const PxCapsuleGeometry>();
+
+ // PT: get capsules in local space
+ PxVec3 dir[2];
+ Segment segment[2];
+ {
+ const PxVec3 capsuleLocalSegment0 = getCapsuleHalfHeightVector(transform0, capsuleGeom0);
+ const PxVec3 capsuleLocalSegment1 = getCapsuleHalfHeightVector(transform1, capsuleGeom1);
+
+ const PxVec3 delta = transform1.p - transform0.p;
+ segment[0].p0 = capsuleLocalSegment0;
+ segment[0].p1 = -capsuleLocalSegment0;
+ dir[0] = -capsuleLocalSegment0*2.0f;
+ segment[1].p0 = capsuleLocalSegment1 + delta;
+ segment[1].p1 = -capsuleLocalSegment1 + delta;
+ dir[1] = -capsuleLocalSegment1*2.0f;
+ }
+
+ // PT: compute distance between capsules' segments
+ PxReal s,t;
+ const PxReal squareDist = distanceSegmentSegmentSquared(segment[0], segment[1], &s, &t);
+ const PxReal radiusSum = capsuleGeom0.radius + capsuleGeom1.radius;
+ const PxReal inflatedSum = radiusSum + params.mContactDistance;
+ const PxReal inflatedSumSquared = inflatedSum*inflatedSum;
+
+ if(squareDist >= inflatedSumSquared)
+ return false;
+
+ // PT: TODO: optimize this away
+ PxReal segLen[2];
+ segLen[0] = dir[0].magnitude();
+ segLen[1] = dir[1].magnitude();
+
+ if (segLen[0]) dir[0] *= 1.0f / segLen[0];
+ if (segLen[1]) dir[1] *= 1.0f / segLen[1];
+
+ if (PxAbs(dir[0].dot(dir[1])) > 0.9998f) //almost parallel, ca. 1 degree difference --> generate two contact points at ends
+ {
+ PxU32 numCons = 0;
+
+ PxReal segLenEps[2];
+ segLenEps[0] = segLen[0] * 0.001f;//0.1% error is ok.
+ segLenEps[1] = segLen[1] * 0.001f;
+
+ //project the two end points of each onto the axis of the other and take those 4 points.
+ //we could also generate a single normal at the single closest point, but this would be 'unstable'.
+
+ for (PxU32 destShapeIndex = 0; destShapeIndex < 2; destShapeIndex ++)
+ {
+ for (PxU32 startEnd = 0; startEnd < 2; startEnd ++)
+ {
+ const PxU32 srcShapeIndex = 1-destShapeIndex;
+ //project start/end of srcShapeIndex onto destShapeIndex.
+ PxVec3 pos[2];
+ pos[destShapeIndex] = startEnd ? segment[srcShapeIndex].p1 : segment[srcShapeIndex].p0;
+ const PxReal p = dir[destShapeIndex].dot(pos[destShapeIndex] - segment[destShapeIndex].p0);
+ if (p >= -segLenEps[destShapeIndex] && p <= (segLen[destShapeIndex] + segLenEps[destShapeIndex]))
+ {
+ pos[srcShapeIndex] = p * dir[destShapeIndex] + segment[destShapeIndex].p0;
+
+ PxVec3 normal = pos[1] - pos[0];
+
+ const PxReal normalLenSq = normal.magnitudeSquared();
+ if (normalLenSq > 1e-6f && normalLenSq < inflatedSumSquared)
+ {
+ const PxReal distance = PxSqrt(normalLenSq);
+ normal *= 1.0f/distance;
+ PxVec3 point = pos[1] - normal * (srcShapeIndex ? capsuleGeom1 : capsuleGeom0).radius;
+ point += transform0.p;
+ contactBuffer.contact(point, normal, distance - radiusSum);
+ numCons++;
+ }
+ }
+ }
+ }
+
+ if (numCons) //if we did not have contacts, then we may have the case where they are parallel, but are stacked end to end, in which case the old code will generate good contacts.
+ return true;
+ }
+
+ // Collision response
+ PxVec3 pos1 = segment[0].getPointAt(s);
+ PxVec3 pos2 = segment[1].getPointAt(t);
+
+ PxVec3 normal = pos1 - pos2;
+
+ const PxReal normalLenSq = normal.magnitudeSquared();
+ if (normalLenSq < 1e-6f)
+ {
+ // PT: TODO: revisit this. "FW" sounds old.
+ // Zero normal -> pick the direction of segment 0.
+ // Not always accurate but consistent with FW.
+ if (segLen[0] > 1e-6f)
+ normal = dir[0];
+ else
+ normal = PxVec3(1.0f, 0.0f, 0.0f);
+ }
+ else
+ {
+ normal *= PxRecipSqrt(normalLenSq);
+ }
+
+ pos1 += transform0.p;
+ contactBuffer.contact(pos1 - normal * capsuleGeom0.radius, normal, PxSqrt(squareDist) - radiusSum);
+ return true;
+}
+}//Gu
+}//physx
diff --git a/PhysX_3.4/Source/GeomUtils/src/contact/GuContactCapsuleConvex.cpp b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactCapsuleConvex.cpp
new file mode 100644
index 00000000..981b76ea
--- /dev/null
+++ b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactCapsuleConvex.cpp
@@ -0,0 +1,587 @@
+// 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 "GuConvexMesh.h"
+#include "GuConvexHelper.h"
+#include "GuContactBuffer.h"
+#include "GuContactMethodImpl.h"
+#include "GuVecConvexHull.h"
+#include "GuVecCapsule.h"
+#include "GuInternal.h"
+#include "GuGJK.h"
+#include "GuGeometryUnion.h"
+
+using namespace physx;
+using namespace Gu;
+
+///////////
+// #include "CmRenderOutput.h"
+// #include "PxsContext.h"
+// static void gVisualizeLine(const PxVec3& a, const PxVec3& b, PxcNpThreadContext& context, PxU32 color=0xffffff)
+// {
+// PxMat44 m = PxMat44::identity();
+//
+// Cm::RenderOutput& out = context.mRenderOutput;
+// out << color << m << Cm::RenderOutput::LINES << a << b;
+// }
+///////////
+
+static const PxReal fatConvexEdgeCoeff = 0.01f;
+
+static bool intersectEdgeEdgePreca(const PxVec3& p1, const PxVec3& p2, const PxVec3& v1, const PxPlane& plane, PxU32 i, PxU32 j, float coeff, const PxVec3& dir, const PxVec3& p3, const PxVec3& p4, PxReal& dist, PxVec3& ip, float limit)
+{
+ // if colliding edge (p3,p4) does not cross plane return no collision
+ // same as if p3 and p4 on same side of plane return 0
+ //
+ // Derivation:
+ // d3 = d(p3, P) = (p3 | plane.n) - plane.d; Reversed sign compared to Plane::Distance() because plane.d is negated.
+ // d4 = d(p4, P) = (p4 | plane.n) - plane.d; Reversed sign compared to Plane::Distance() because plane.d is negated.
+ // if d3 and d4 have the same sign, they're on the same side of the plane => no collision
+ // We test both sides at the same time by only testing Sign(d3 * d4).
+ // ### put that in the Plane class
+ // ### also check that code in the triangle class that might be similar
+ 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
+ PxVec3 v2 = p4 - p3;
+
+ temp = plane.n.dot(v2);
+ if(temp==0.0f)
+ return false; // ### epsilon would be better
+
+ // 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<limit)
+ 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
+}
+
+static bool GuTestAxis(const PxVec3& axis, const Gu::Segment& segment, PxReal radius,
+ const PolygonalData& polyData, const Cm::FastVertex2ShapeScaling& scaling,
+ const Cm::Matrix34& worldTM,
+ PxReal& depth)
+{
+ // Project capsule
+ PxReal min0 = segment.p0.dot(axis);
+ PxReal max0 = segment.p1.dot(axis);
+ if(min0>max0) Ps::swap(min0, max0);
+ min0 -= radius;
+ max0 += radius;
+
+ // Project convex
+ PxReal Min1, Max1;
+ (polyData.mProjectHull)(polyData, axis, worldTM, scaling, Min1, Max1);
+
+ // Test projections
+ if(max0<Min1 || Max1<min0)
+ return false;
+
+ const PxReal d0 = max0 - Min1;
+ PX_ASSERT(d0>=0.0f);
+ const PxReal d1 = Max1 - min0;
+ PX_ASSERT(d1>=0.0f);
+ depth = physx::intrinsics::selectMin(d0, d1);
+ return true;
+}
+
+static bool GuCapsuleConvexOverlap(const Gu::Segment& segment, PxReal radius,
+ const PolygonalData& polyData,
+ const Cm::FastVertex2ShapeScaling& scaling,
+ const PxTransform& transform,
+ PxReal* t, PxVec3* pp, bool isSphere)
+{
+ // TODO:
+ // - test normal & edge in same loop
+ // - local space
+ // - use precomputed face value
+ // - optimize projection
+
+ PxVec3 Sep(0,0,0);
+ PxReal PenDepth = PX_MAX_REAL;
+
+ PxU32 nbPolys = polyData.mNbPolygons;
+ const Gu::HullPolygonData* polys = polyData.mPolygons;
+
+ const Cm::Matrix34 worldTM(transform);
+
+ // Test normals
+ for(PxU32 i=0;i<nbPolys;i++)
+ {
+ const Gu::HullPolygonData& poly = polys[i];
+ const PxPlane& vertSpacePlane = poly.mPlane;
+
+ const PxVec3 worldNormal = worldTM.rotate(vertSpacePlane.n);
+
+ PxReal d;
+ if(!GuTestAxis(worldNormal, segment, radius, polyData, scaling, worldTM, d))
+ return false;
+
+ if(d<PenDepth)
+ {
+ PenDepth = d;
+ Sep = worldNormal;
+ }
+ }
+
+ // Test edges
+ if(!isSphere)
+ {
+ PxVec3 CapsuleAxis(segment.p1 - segment.p0);
+ CapsuleAxis = CapsuleAxis.getNormalized();
+ for(PxU32 i=0;i<nbPolys;i++)
+ {
+ const Gu::HullPolygonData& poly = polys[i];
+ const PxPlane& vertSpacePlane = poly.mPlane;
+
+ const PxVec3 worldNormal = worldTM.rotate(vertSpacePlane.n);
+
+ PxVec3 Cross = CapsuleAxis.cross(worldNormal);
+ if(!Ps::isAlmostZero(Cross))
+ {
+ Cross = Cross.getNormalized();
+ PxReal d;
+ if(!GuTestAxis(Cross, segment, radius, polyData, scaling, worldTM, d))
+ return false;
+
+ if(d<PenDepth)
+ {
+ PenDepth = d;
+ Sep = Cross;
+ }
+ }
+ }
+ }
+
+ const PxVec3 Witness = segment.computeCenter() - transform.transform(polyData.mCenter);
+
+ if(Sep.dot(Witness) < 0.0f)
+ Sep = -Sep;
+
+ if(t) *t = PenDepth;
+ if(pp) *pp = Sep;
+
+ return true;
+}
+
+
+
+
+
+static bool raycast_convexMesh2( const PolygonalData& polyData,
+ const PxVec3& vrayOrig, const PxVec3& vrayDir,
+ PxReal maxDist, PxF32& t)
+{
+ PxU32 nPolys = polyData.mNbPolygons;
+ const Gu::HullPolygonData* PX_RESTRICT polys = polyData.mPolygons;
+
+ /*
+ Purely convex planes based algorithm
+ Iterate all planes of convex, with following rules:
+ * determine of ray origin is inside them all or not.
+ * planes parallel to ray direction are immediate early out if we're on the outside side (plane normal is sep axis)
+ * else
+ - for all planes the ray direction "enters" from the front side, track the one furthest along the ray direction (A)
+ - for all planes the ray direction "exits" from the back side, track the one furthest along the negative ray direction (B)
+ if the ray origin is outside the convex and if along the ray, A comes before B, the directed line stabs the convex at A
+ */
+ PxReal latestEntry = -FLT_MAX;
+ PxReal earliestExit = FLT_MAX;
+
+ while(nPolys--)
+ {
+ const Gu::HullPolygonData& poly = *polys++;
+ const PxPlane& vertSpacePlane = poly.mPlane;
+
+ const PxReal distToPlane = vertSpacePlane.distance(vrayOrig);
+ const PxReal dn = vertSpacePlane.n.dot(vrayDir);
+ const PxReal distAlongRay = -distToPlane/dn;
+
+ if (dn > 1E-7f) //the ray direction "exits" from the back side
+ {
+ earliestExit = physx::intrinsics::selectMin(earliestExit, distAlongRay);
+ }
+ else if (dn < -1E-7f) //the ray direction "enters" from the front side
+ {
+/* if (distAlongRay > latestEntry)
+ {
+ latestEntry = distAlongRay;
+ }*/
+ latestEntry = physx::intrinsics::selectMax(latestEntry, distAlongRay);
+ }
+ else
+ {
+ //plane normal and ray dir are orthogonal
+ if(distToPlane > 0.0f)
+ return false; //a plane is parallel with ray -- and we're outside the ray -- we definitely miss the entire convex!
+ }
+ }
+
+ if(latestEntry < earliestExit && latestEntry != -FLT_MAX && latestEntry < maxDist-1e-5f)
+ {
+ t = latestEntry;
+ return true;
+ }
+ return false;
+}
+
+// PT: version based on Gu::raycast_convexMesh to handle scaling, but modified to make sure it works when ray starts inside the convex
+static void GuGenerateVFContacts2(ContactBuffer& contactBuffer,
+ //
+ const PxTransform& convexPose,
+ const PolygonalData& polyData, // Convex data
+ const PxMeshScale& scale,
+ //
+ PxU32 nbPts,
+ const PxVec3* PX_RESTRICT points,
+ const PxReal radius, // Capsule's radius
+ //
+ const PxVec3& normal,
+ const PxReal contactDistance)
+{
+ PX_ASSERT(PxAbs(normal.magnitudeSquared()-1)<1e-4f);
+
+ //scaling: transform the ray to vertex space
+ const Cm::Matrix34 world2vertexSkew = scale.getInverse() * convexPose.getInverse();
+
+ const PxVec3 vrayDir = world2vertexSkew.rotate( -normal );
+
+ const PxReal maxDist = contactDistance + radius;
+
+ for(PxU32 i=0;i<nbPts;i++)
+ {
+ const PxVec3& rayOrigin = points[i];
+
+ const PxVec3 vrayOrig = world2vertexSkew.transform( rayOrigin );
+ PxF32 t;
+ if(raycast_convexMesh2(polyData, vrayOrig, vrayDir, maxDist, t))
+ {
+ contactBuffer.contact(rayOrigin - t * normal, normal, t - radius);
+ }
+ }
+}
+
+static void GuGenerateEEContacts( ContactBuffer& contactBuffer,
+ //
+ const Gu::Segment& segment,
+ const PxReal radius,
+ const PxReal contactDistance,
+ //
+ const PolygonalData& polyData,
+ const PxTransform& transform,
+ const Cm::FastVertex2ShapeScaling& scaling,
+ //
+ const PxVec3& normal)
+{
+ PxU32 numPolygons = polyData.mNbPolygons;
+ const Gu::HullPolygonData* PX_RESTRICT polygons = polyData.mPolygons;
+ const PxU8* PX_RESTRICT vertexData = polyData.mPolygonVertexRefs;
+
+ ConvexEdge edges[512];
+ PxU32 nbEdges = findUniqueConvexEdges(512, edges, numPolygons, polygons, vertexData);
+
+ //
+ PxVec3 s0 = segment.p0;
+ PxVec3 s1 = segment.p1;
+ Ps::makeFatEdge(s0, s1, fatConvexEdgeCoeff);
+
+ // PT: precomputed part of edge-edge intersection test
+// const PxVec3 v1 = segment.p1 - segment.p0;
+ const PxVec3 v1 = s1 - s0;
+ PxPlane plane;
+ plane.n = v1.cross(normal);
+// plane.d = -(plane.normal|segment.p0);
+ plane.d = -(plane.n.dot(s0));
+
+ PxU32 ii,jj;
+ Ps::closestAxis(plane.n, ii, jj);
+
+ const float coeff = 1.0f /(v1[ii]*normal[jj]-v1[jj]*normal[ii]);
+
+ //
+
+ const PxVec3* PX_RESTRICT verts = polyData.mVerts;
+ for(PxU32 i=0;i<nbEdges;i++)
+ {
+ const PxU8 vi0 = edges[i].vref0;
+ const PxU8 vi1 = edges[i].vref1;
+
+// PxVec3 p1 = transform.transform(verts[vi0]);
+// PxVec3 p2 = transform.transform(verts[vi1]);
+// Ps::makeFatEdge(p1, p2, fatConvexEdgeCoeff); // PT: TODO: make fat segment instead
+ const PxVec3 p1 = transform.transform(scaling * verts[vi0]);
+ const PxVec3 p2 = transform.transform(scaling * verts[vi1]);
+
+ PxReal dist;
+ PxVec3 ip;
+// if(intersectEdgeEdgePreca(segment.p0, segment.p1, v1, plane, ii, jj, coeff, normal, p1, p2, dist, ip))
+// if(intersectEdgeEdgePreca(s0, s1, v1, plane, ii, jj, coeff, normal, p1, p2, dist, ip, -FLT_MAX))
+ if(intersectEdgeEdgePreca(s0, s1, v1, plane, ii, jj, coeff, normal, p1, p2, dist, ip, -radius-contactDistance))
+// if(intersectEdgeEdgePreca(s0, s1, v1, plane, ii, jj, coeff, normal, p1, p2, dist, ip, 0))
+ {
+ contactBuffer.contact(ip-normal*dist, normal, - (radius + dist));
+// if(contactBuffer.count==2) // PT: we only need 2 contacts to be stable
+// return;
+ }
+ }
+}
+
+static void GuGenerateEEContacts2b(ContactBuffer& contactBuffer,
+ //
+ const Gu::Segment& segment,
+ const PxReal radius,
+ //
+ const Cm::Matrix34& transform,
+ const PolygonalData& polyData,
+ const Cm::FastVertex2ShapeScaling& scaling,
+ //
+ const PxVec3& normal,
+ const PxReal contactDistance)
+{
+ // TODO:
+ // - local space
+
+ const PxVec3 localDir = transform.rotateTranspose(normal);
+ PxU32 polyIndex = (polyData.mSelectClosestEdgeCB)(polyData, scaling, localDir);
+
+ PxVec3 s0 = segment.p0;
+ PxVec3 s1 = segment.p1;
+ Ps::makeFatEdge(s0, s1, fatConvexEdgeCoeff);
+
+ // PT: precomputed part of edge-edge intersection test
+// const PxVec3 v1 = segment.p1 - segment.p0;
+ const PxVec3 v1 = s1 - s0;
+ PxPlane plane;
+ plane.n = -(v1.cross(normal));
+// plane.d = -(plane.normal|segment.p0);
+ plane.d = -(plane.n.dot(s0));
+
+ PxU32 ii,jj;
+ Ps::closestAxis(plane.n, ii, jj);
+
+ const float coeff = 1.0f /(v1[jj]*normal[ii]-v1[ii]*normal[jj]);
+ //
+
+ const PxVec3* PX_RESTRICT verts = polyData.mVerts;
+
+ const Gu::HullPolygonData& polygon = polyData.mPolygons[polyIndex];
+ const PxU8* PX_RESTRICT vRefBase = polyData.mPolygonVertexRefs + polygon.mVRef8;
+ PxU32 numEdges = polygon.mNbVerts;
+
+ PxU32 a = numEdges - 1;
+ PxU32 b = 0;
+ while(numEdges--)
+ {
+// const PxVec3 p1 = transform.transform(verts[vRefBase[a]]);
+// const PxVec3 p2 = transform.transform(verts[vRefBase[b]]);
+ const PxVec3 p1 = transform.transform(scaling * verts[vRefBase[a]]);
+ const PxVec3 p2 = transform.transform(scaling * verts[vRefBase[b]]);
+
+ PxReal dist;
+ PxVec3 ip;
+// bool contact = intersectEdgeEdgePreca(segment.p0, segment.p1, v1, plane, ii, jj, coeff, -normal, p1, p2, dist, ip);
+ bool contact = intersectEdgeEdgePreca(s0, s1, v1, plane, ii, jj, coeff, -normal, p1, p2, dist, ip, 0.0f);
+ if(contact && dist < radius + contactDistance)
+ {
+ contactBuffer.contact(ip-normal*dist, normal, dist - radius);
+// if(contactBuffer.count==2) // PT: we only need 2 contacts to be stable
+// return;
+ }
+
+ a = b;
+ b++;
+ }
+}
+
+namespace physx
+{
+namespace Gu
+{
+bool contactCapsuleConvex(GU_CONTACT_METHOD_ARGS)
+{
+ PX_UNUSED(renderOutput);
+ PX_UNUSED(cache);
+
+ // Get actual shape data
+ const PxCapsuleGeometry& shapeCapsule = shape0.get<const PxCapsuleGeometry>();
+ const PxConvexMeshGeometryLL& shapeConvex = shape1.get<const PxConvexMeshGeometryLL>();
+
+ PxVec3 onSegment, onConvex;
+ PxReal distance;
+ PxVec3 normal_;
+ {
+ Gu::ConvexMesh* cm = static_cast<Gu::ConvexMesh*>(shapeConvex.convexMesh);
+
+ using namespace Ps::aos;
+ Vec3V closA, closB, normalV;
+ GjkStatus status;
+ FloatV dist;
+ {
+
+ const Vec3V zeroV = V3Zero();
+ const Gu::ConvexHullData* hullData = &cm->getHull();
+
+ const FloatV capsuleHalfHeight = FLoad(shapeCapsule.halfHeight);
+
+ const Vec3V vScale = V3LoadU_SafeReadW(shapeConvex.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale
+ const QuatV vQuat = QuatVLoadU(&shapeConvex.scale.rotation.x);
+
+ const PsMatTransformV aToB(transform1.transformInv(transform0));
+
+ Gu::ConvexHullV convexHull(hullData, zeroV, vScale, vQuat, shapeConvex.scale.isIdentity());
+
+ //transform capsule(a) into the local space of convexHull(b), treat capsule as segment
+ Gu::CapsuleV capsule(aToB.p, aToB.rotate(V3Scale(V3UnitX(), capsuleHalfHeight)), FZero());
+
+
+ LocalConvex<CapsuleV> convexA(capsule);
+ LocalConvex<ConvexHullV> convexB(convexHull);
+ const Vec3V initialSearchDir = V3Sub(convexA.getCenter(), convexB.getCenter());
+
+ status = gjk<LocalConvex<CapsuleV>, LocalConvex<ConvexHullV> >(convexA, convexB, initialSearchDir, FMax(),closA, closB, normalV, dist);
+ }
+
+
+ if(status == GJK_CONTACT)
+ distance = 0.f;
+ else
+ {
+ //const FloatV sqDist = FMul(dist, dist);
+ V3StoreU(closB, onConvex);
+ FStore(dist, &distance);
+ V3StoreU(normalV, normal_);
+ onConvex = transform1.transform(onConvex);
+ normal_ = transform1.rotate(normal_);
+ }
+ }
+
+ const PxReal inflatedRadius = shapeCapsule.radius + params.mContactDistance;
+
+ if(distance >= inflatedRadius)
+ return false;
+
+ Gu::Segment worldSegment;
+ getCapsuleSegment(transform0, shapeCapsule, worldSegment);
+
+ const bool isSphere = worldSegment.p0 == worldSegment.p1;
+ const PxU32 nbPts = PxU32(isSphere ? 1 : 2);
+
+ PX_ASSERT(contactBuffer.count==0);
+
+ Cm::FastVertex2ShapeScaling convexScaling;
+ const bool idtConvexScale = shapeConvex.scale.isIdentity();
+ if(!idtConvexScale)
+ convexScaling.init(shapeConvex.scale);
+
+ PolygonalData polyData;
+ getPolygonalData_Convex(&polyData, shapeConvex.hullData, convexScaling);
+
+// if(0)
+ if(distance > 0.f)
+ {
+ // PT: the capsule segment doesn't intersect the convex => distance-based version
+ PxVec3 normal = -normal_;
+
+ // PT: generate VF contacts for segment's vertices vs convex
+ GuGenerateVFContacts2( contactBuffer,
+ transform1, polyData, shapeConvex.scale,
+ nbPts, &worldSegment.p0, shapeCapsule.radius,
+ normal, params.mContactDistance);
+
+ // PT: early exit if we already have 2 stable contacts
+ if(contactBuffer.count==2)
+ return true;
+
+ // PT: else generate slower EE contacts
+ if(!isSphere)
+ {
+ const Cm::Matrix34 worldTM(transform1);
+ GuGenerateEEContacts2b(contactBuffer, worldSegment, shapeCapsule.radius,
+ worldTM, polyData, convexScaling,
+ normal, params.mContactDistance);
+ }
+
+ // PT: run VF case for convex-vertex-vs-capsule only if we don't have any contact yet
+ if(!contactBuffer.count)
+ {
+// gVisualizeLine(onConvex, onConvex + normal, context, PxDebugColor::eARGB_RED);
+ //PxReal distance = PxSqrt(sqDistance);
+ contactBuffer.contact(onConvex, normal, distance - shapeCapsule.radius);
+ }
+ }
+ else
+ {
+ // PT: the capsule segment intersects the convex => penetration-based version
+//printf("Penetration-based:\n");
+
+ // PT: compute penetration vector (MTD)
+ PxVec3 SepAxis;
+ if(!GuCapsuleConvexOverlap(worldSegment, shapeCapsule.radius, polyData, convexScaling, transform1, NULL, &SepAxis, isSphere))
+ {
+//printf("- no overlap\n");
+ return false;
+ }
+
+ // PT: generate VF contacts for segment's vertices vs convex
+ GuGenerateVFContacts2( contactBuffer,
+ transform1, polyData, shapeConvex.scale,
+ nbPts, &worldSegment.p0, shapeCapsule.radius,
+ SepAxis, params.mContactDistance);
+
+ // PT: early exit if we already have 2 stable contacts
+//printf("- %d VF contacts\n", contactBuffer.count);
+ if(contactBuffer.count==2)
+ return true;
+
+ // PT: else generate slower EE contacts
+ if(!isSphere)
+ {
+ GuGenerateEEContacts(contactBuffer, worldSegment, shapeCapsule.radius, params.mContactDistance, polyData, transform1, convexScaling, SepAxis);
+//printf("- %d total contacts\n", contactBuffer.count);
+ }
+ }
+ return true;
+}
+
+}
+}
diff --git a/PhysX_3.4/Source/GeomUtils/src/contact/GuContactCapsuleMesh.cpp b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactCapsuleMesh.cpp
new file mode 100644
index 00000000..da282865
--- /dev/null
+++ b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactCapsuleMesh.cpp
@@ -0,0 +1,636 @@
+// 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 "GuIntersectionEdgeEdge.h"
+#include "GuDistanceSegmentTriangle.h"
+#include "GuIntersectionRayTriangle.h"
+#include "GuIntersectionTriangleBox.h"
+#include "GuInternal.h"
+#include "GuContactMethodImpl.h"
+#include "GuFeatureCode.h"
+#include "GuContactBuffer.h"
+#include "GuMidphaseInterface.h"
+#include "GuEntityReport.h"
+#include "GuHeightFieldUtil.h"
+#include "GuConvexEdgeFlags.h"
+#include "GuGeometryUnion.h"
+#include "GuSIMDHelpers.h"
+#include "GuBox.h"
+
+using namespace physx;
+using namespace Gu;
+
+#define DEBUG_RENDER_MESHCONTACTS 0
+
+#if DEBUG_RENDER_MESHCONTACTS
+#include "PxPhysics.h"
+#include "PxScene.h"
+#endif
+
+#define USE_AABB_TRI_CULLING
+
+//#define USE_CAPSULE_TRI_PROJ_CULLING
+//#define USE_CAPSULE_TRI_SAT_CULLING
+#define VISUALIZE_TOUCHED_TRIS 0
+#define VISUALIZE_CULLING_BOX 0
+
+#if VISUALIZE_TOUCHED_TRIS
+#include "CmRenderOutput.h"
+#include "PxsContactManager.h"
+#include "PxsContext.h"
+static void gVisualizeLine(const PxVec3& a, const PxVec3& b, PxcNpThreadContext& context, PxU32 color=0xffffff)
+{
+ PxMat44 m = PxMat44::identity();
+
+ Cm::RenderOutput& out = context.mRenderOutput;
+ out << color << m << Cm::RenderOutput::LINES << a << b;
+}
+static void gVisualizeTri(const PxVec3& a, const PxVec3& b, const PxVec3& c, PxcNpThreadContext& context, PxU32 color=0xffffff)
+{
+ PxMat44 m = PxMat44::identity();
+
+ Cm::RenderOutput& out = context.mRenderOutput;
+ out << color << m << Cm::RenderOutput::TRIANGLES << a << b << c;
+}
+
+static PxU32 gColors[8] = { 0xff0000ff, 0xff00ff00, 0xffff0000,
+ 0xff00ffff, 0xffff00ff, 0xffffff00,
+ 0xff000080, 0xff008000};
+#endif
+
+static const float fatBoxEdgeCoeff = 0.01f;
+
+static bool PxcTestAxis(const PxVec3& axis, const Segment& segment, PxReal radius,
+ const PxVec3* PX_RESTRICT triVerts, PxReal& depth)
+{
+ // Project capsule
+ PxReal min0 = segment.p0.dot(axis);
+ PxReal max0 = segment.p1.dot(axis);
+ if(min0>max0) Ps::swap(min0, max0);
+ min0 -= radius;
+ max0 += radius;
+
+ // Project triangle
+ float Min1, Max1;
+ {
+ Min1 = Max1 = triVerts[0].dot(axis);
+ const PxReal dp1 = triVerts[1].dot(axis);
+ Min1 = physx::intrinsics::selectMin(Min1, dp1);
+ Max1 = physx::intrinsics::selectMax(Max1, dp1);
+ const PxReal dp2 = triVerts[2].dot(axis);
+ Min1 = physx::intrinsics::selectMin(Min1, dp2);
+ Max1 = physx::intrinsics::selectMax(Max1, dp2);
+ }
+
+ // Test projections
+ if(max0<Min1 || Max1<min0)
+ return false;
+
+ const PxReal d0 = max0 - Min1;
+ PX_ASSERT(d0>=0.0f);
+ const PxReal d1 = Max1 - min0;
+ PX_ASSERT(d1>=0.0f);
+ depth = physx::intrinsics::selectMin(d0, d1);
+ return true;
+}
+
+PX_FORCE_INLINE static PxVec3 PxcComputeTriangleNormal(const PxVec3* PX_RESTRICT triVerts)
+{
+ return ((triVerts[0]-triVerts[1]).cross(triVerts[0]-triVerts[2])).getNormalized();
+}
+
+PX_FORCE_INLINE static PxVec3 PxcComputeTriangleCenter(const PxVec3* PX_RESTRICT triVerts)
+{
+ static const PxReal inv3 = 1.0f / 3.0f;
+ return (triVerts[0] + triVerts[1] + triVerts[2]) * inv3;
+}
+
+static bool PxcCapsuleTriOverlap3(PxU8 edgeFlags, const Segment& segment, PxReal radius, const PxVec3* PX_RESTRICT triVerts,
+ PxReal* PX_RESTRICT t=NULL, PxVec3* PX_RESTRICT pp=NULL)
+{
+ PxReal penDepth = PX_MAX_REAL;
+
+ // Test normal
+ PxVec3 sep = PxcComputeTriangleNormal(triVerts);
+ if(!PxcTestAxis(sep, segment, radius, triVerts, penDepth))
+ return false;
+
+ // Test edges
+ // ML:: use the active edge flag instead of the concave flag
+ const PxU32 activeEdgeFlag[] = {ETD_CONVEX_EDGE_01, ETD_CONVEX_EDGE_12, ETD_CONVEX_EDGE_20};
+ const PxVec3 capsuleAxis = (segment.p1 - segment.p0).getNormalized();
+ for(PxU32 i=0;i<3;i++)
+ {
+ //bool active =((edgeFlags & ignoreEdgeFlag[i]) == 0);
+
+ if(edgeFlags & activeEdgeFlag[i])
+ {
+
+ const PxVec3 e0 = triVerts[i];
+// const PxVec3 e1 = triVerts[(i+1)%3];
+ const PxVec3 e1 = triVerts[Ps::getNextIndex3(i)];
+ const PxVec3 edge = e0 - e1;
+
+ PxVec3 cross = capsuleAxis.cross(edge);
+ if(!Ps::isAlmostZero(cross))
+ {
+ cross = cross.getNormalized();
+ PxReal d;
+ if(!PxcTestAxis(cross, segment, radius, triVerts, d))
+ return false;
+ if(d<penDepth)
+ {
+ penDepth = d;
+ sep = cross;
+ }
+ }
+ }
+ }
+
+ const PxVec3 capsuleCenter = segment.computeCenter();
+ const PxVec3 triCenter = PxcComputeTriangleCenter(triVerts);
+ const PxVec3 witness = capsuleCenter - triCenter;
+
+ if(sep.dot(witness) < 0.0f)
+ sep = -sep;
+
+ if(t) *t = penDepth;
+ if(pp) *pp = sep;
+
+ return true;
+}
+
+static void PxcGenerateVFContacts( const Cm::Matrix34& meshAbsPose, ContactBuffer& contactBuffer, const Segment& segment,
+ const PxReal radius, const PxVec3* PX_RESTRICT triVerts, const PxVec3& normal,
+ PxU32 triangleIndex, PxReal contactDistance)
+{
+ const PxVec3* PX_RESTRICT Ptr = &segment.p0;
+ for(PxU32 i=0;i<2;i++)
+ {
+ const PxVec3& Pos = Ptr[i];
+ PxReal t,u,v;
+ if(intersectRayTriangleCulling(Pos, -normal, triVerts[0], triVerts[1], triVerts[2], t, u, v, 1e-3f) && t < radius + contactDistance)
+ {
+ const PxVec3 Hit = meshAbsPose.transform(Pos - t * normal);
+ const PxVec3 wn = meshAbsPose.rotate(normal);
+
+ contactBuffer.contact(Hit, wn, t-radius, triangleIndex);
+ #if DEBUG_RENDER_MESHCONTACTS
+ PxScene *s; PxGetPhysics().getScenes(&s, 1, 0);
+ Cm::RenderOutput((Cm::RenderBuffer&)s->getRenderBuffer()) << Cm::RenderOutput::LINES << PxDebugColor::eARGB_BLUE // red
+ << Hit << (Hit + wn * 10.0f);
+ #endif
+ }
+ }
+}
+
+// PT: PxcGenerateEEContacts2 uses a segment-triangle distance function, which breaks when the segment
+// intersects the triangle, in which case you need to switch to a penetration-depth computation.
+// If you don't do this thin capsules don't work.
+static void PxcGenerateEEContacts( const Cm::Matrix34& meshAbsPose, ContactBuffer& contactBuffer, const Segment& segment, const PxReal radius,
+ const PxVec3* PX_RESTRICT triVerts, const PxVec3& normal, PxU32 triangleIndex)
+{
+ PxVec3 s0 = segment.p0;
+ PxVec3 s1 = segment.p1;
+ Ps::makeFatEdge(s0, s1, fatBoxEdgeCoeff);
+
+ for(PxU32 i=0;i<3;i++)
+ {
+ PxReal dist;
+ PxVec3 ip;
+ if(intersectEdgeEdge(triVerts[i], triVerts[Ps::getNextIndex3(i)], -normal, s0, s1, dist, ip))
+ {
+ ip = meshAbsPose.transform(ip);
+ const PxVec3 wn = meshAbsPose.rotate(normal);
+
+ contactBuffer.contact(ip, wn, - (radius + dist), triangleIndex);
+ #if DEBUG_RENDER_MESHCONTACTS
+ PxScene *s; PxGetPhysics().getScenes(&s, 1, 0);
+ Cm::RenderOutput((Cm::RenderBuffer&)s->getRenderBuffer()) << Cm::RenderOutput::LINES << PxDebugColor::eARGB_BLUE // red
+ << ip << (ip + wn * 10.0f);
+ #endif
+ }
+ }
+}
+
+static void PxcGenerateEEContacts2( const Cm::Matrix34& meshAbsPose, ContactBuffer& contactBuffer, const Segment& segment, const PxReal radius,
+ const PxVec3* PX_RESTRICT triVerts, const PxVec3& normal, PxU32 triangleIndex, PxReal contactDistance)
+{
+ PxVec3 s0 = segment.p0;
+ PxVec3 s1 = segment.p1;
+ Ps::makeFatEdge(s0, s1, fatBoxEdgeCoeff);
+
+ for(PxU32 i=0;i<3;i++)
+ {
+ PxReal dist;
+ PxVec3 ip;
+ if(intersectEdgeEdge(triVerts[i], triVerts[Ps::getNextIndex3(i)], normal, s0, s1, dist, ip) && dist < radius+contactDistance)
+ {
+ ip = meshAbsPose.transform(ip);
+ const PxVec3 wn = meshAbsPose.rotate(normal);
+
+ contactBuffer.contact(ip, wn, dist - radius, triangleIndex);
+ #if DEBUG_RENDER_MESHCONTACTS
+ PxScene *s; PxGetPhysics().getScenes(&s, 1, 0);
+ Cm::RenderOutput((Cm::RenderBuffer&)s->getRenderBuffer()) << Cm::RenderOutput::LINES << PxDebugColor::eARGB_BLUE // red
+ << ip << (ip + wn * 10.0f);
+ #endif
+ }
+ }
+}
+
+namespace
+{
+struct CapsuleMeshContactGeneration
+{
+ ContactBuffer& mContactBuffer;
+ const Cm::Matrix34 mMeshAbsPose;
+ const Segment& mMeshCapsule;
+#ifdef USE_AABB_TRI_CULLING
+ Vec3p mBC;
+ Vec3p mBE;
+#endif
+ PxReal mInflatedRadius;
+ PxReal mContactDistance;
+ PxReal mShapeCapsuleRadius;
+
+ CapsuleMeshContactGeneration(ContactBuffer& contactBuffer, const PxTransform& transform1, const Segment& meshCapsule, PxReal inflatedRadius, PxReal contactDistance, PxReal shapeCapsuleRadius) :
+ mContactBuffer (contactBuffer),
+ mMeshAbsPose (Cm::Matrix34(transform1)),
+ mMeshCapsule (meshCapsule),
+ mInflatedRadius (inflatedRadius),
+ mContactDistance (contactDistance),
+ mShapeCapsuleRadius (shapeCapsuleRadius)
+ {
+ PX_ASSERT(contactBuffer.count==0);
+#ifdef USE_AABB_TRI_CULLING
+ mBC = (meshCapsule.p0 + meshCapsule.p1)*0.5f;
+ const Vec3p be = (meshCapsule.p0 - meshCapsule.p1)*0.5f;
+ mBE.x = fabsf(be.x) + inflatedRadius;
+ mBE.y = fabsf(be.y) + inflatedRadius;
+ mBE.z = fabsf(be.z) + inflatedRadius;
+#endif
+ }
+
+ void processTriangle(PxU32 triangleIndex, const TrianglePadded& tri, PxU8 extraData/*, const PxU32* vertInds*/)
+ {
+#ifdef USE_AABB_TRI_CULLING
+ #if VISUALIZE_CULLING_BOX
+ {
+ Cm::RenderOutput& out = context.mRenderOutput;
+ PxTransform idt = PxTransform(PxIdentity);
+ out << idt;
+ out << 0xffffffff;
+ out << Cm::DebugBox(mBC, mBE, true);
+ }
+ #endif
+#endif
+ const PxVec3& p0 = tri.verts[0];
+ const PxVec3& p1 = tri.verts[1];
+ const PxVec3& p2 = tri.verts[2];
+
+#ifdef USE_AABB_TRI_CULLING
+ // PT: this one is safe because triangle class is padded
+ // PT: TODO: is this test really needed? Not done in midphase already?
+ if(!intersectTriangleBox_Unsafe(mBC, mBE, p0, p1, p2))
+ return;
+#endif
+
+#ifdef USE_CAPSULE_TRI_PROJ_CULLING
+ PxVec3 triCenter = (p0 + p1 + p2)*0.33333333f;
+ PxVec3 delta = mBC - triCenter;
+
+ PxReal depth;
+ if(!PxcTestAxis(delta, mMeshCapsule, mInflatedRadius, tri.verts, depth))
+ return;
+#endif
+
+#if VISUALIZE_TOUCHED_TRIS
+ gVisualizeTri(p0, p1, p2, context, PxDebugColor::eARGB_RED);
+#endif
+
+#ifdef USE_CAPSULE_TRI_SAT_CULLING
+ PxVec3 SepAxis;
+ if(!PxcCapsuleTriOverlap3(extraData, mMeshCapsule, mInflatedRadius, tri.verts, NULL, &SepAxis))
+ return;
+#endif
+
+ PxReal t,u,v;
+ const PxVec3 p1_p0 = p1 - p0;
+ const PxVec3 p2_p0 = p2 - p0;
+ const PxReal squareDist = distanceSegmentTriangleSquared(mMeshCapsule, p0, p1_p0, p2_p0, &t, &u, &v);
+
+ // PT: do cheaper test first!
+ if(squareDist >= mInflatedRadius*mInflatedRadius)
+ return;
+
+ // PT: backface culling without the normalize
+ // PT: TODO: consider doing before the segment-triangle distance test if it's cheaper
+ const PxVec3 planeNormal = p1_p0.cross(p2_p0);
+ const PxF32 planeD = planeNormal.dot(p0); // PT: actually -d compared to PxcPlane
+ if(planeNormal.dot(mBC) < planeD)
+ return;
+
+ if(squareDist > 0.001f*0.001f)
+ {
+ // Contact information
+ PxVec3 normal;
+ if(selectNormal(extraData, u, v))
+ {
+ normal = planeNormal.getNormalized();
+ }
+ else
+ {
+ const PxVec3 pointOnTriangle = Ps::computeBarycentricPoint(p0, p1, p2, u, v);
+
+ const PxVec3 pointOnSegment = mMeshCapsule.getPointAt(t);
+ normal = pointOnSegment - pointOnTriangle;
+ const PxReal l = normal.magnitude();
+ if(l == 0.0f)
+ return;
+ normal = normal / l;
+ }
+
+ PxcGenerateEEContacts2(mMeshAbsPose, mContactBuffer, mMeshCapsule, mShapeCapsuleRadius, tri.verts, normal, triangleIndex, mContactDistance);
+ PxcGenerateVFContacts(mMeshAbsPose, mContactBuffer, mMeshCapsule, mShapeCapsuleRadius, tri.verts, normal, triangleIndex, mContactDistance);
+ }
+ else
+ {
+ PxVec3 SepAxis;
+ if(!PxcCapsuleTriOverlap3(extraData, mMeshCapsule, mInflatedRadius, tri.verts, NULL, &SepAxis))
+ return;
+
+ PxcGenerateEEContacts(mMeshAbsPose, mContactBuffer, mMeshCapsule, mShapeCapsuleRadius, tri.verts, SepAxis, triangleIndex);
+ PxcGenerateVFContacts(mMeshAbsPose, mContactBuffer, mMeshCapsule, mShapeCapsuleRadius, tri.verts, SepAxis, triangleIndex, mContactDistance);
+ }
+ }
+
+private:
+ CapsuleMeshContactGeneration& operator=(const CapsuleMeshContactGeneration&);
+};
+
+struct CapsuleMeshContactGenerationCallback_NoScale : MeshHitCallback<PxRaycastHit>
+{
+ CapsuleMeshContactGeneration mGeneration;
+ const TriangleMesh* mMeshData;
+
+ CapsuleMeshContactGenerationCallback_NoScale(
+ ContactBuffer& contactBuffer,
+ const PxTransform& transform1, const Segment& meshCapsule,
+ PxReal inflatedRadius, PxReal contactDistance,
+ PxReal shapeCapsuleRadius, const TriangleMesh* meshData
+ ) :
+ MeshHitCallback<PxRaycastHit> (CallbackMode::eMULTIPLE),
+ mGeneration (contactBuffer, transform1, meshCapsule, inflatedRadius, contactDistance, shapeCapsuleRadius),
+ mMeshData (meshData)
+ {
+ PX_ASSERT(contactBuffer.count==0);
+ }
+
+ PX_FORCE_INLINE PxAgain processTriangle(const PxRaycastHit& hit, const TrianglePadded& tri)
+ {
+ const PxU32 triangleIndex = hit.faceIndex;
+
+ //ML::set all the edges to be active, if the mExtraTrigData exist, we overwrite this flag
+ const PxU8 extraData = getConvexEdgeFlags(mMeshData->getExtraTrigData(), triangleIndex);
+ mGeneration.processTriangle(triangleIndex, tri, extraData);
+ return true;
+ }
+
+ virtual PxAgain processHit(
+ const PxRaycastHit& hit, const PxVec3& v0, const PxVec3& v1, const PxVec3& v2, PxReal&, const PxU32* /*vInds*/)
+ {
+ TrianglePadded tri;
+ // PT: TODO: revisit this, avoid the copy
+ tri.verts[0] = v0;
+ tri.verts[1] = v1;
+ tri.verts[2] = v2;
+ return processTriangle(hit, tri);
+ }
+
+private:
+ CapsuleMeshContactGenerationCallback_NoScale& operator=(const CapsuleMeshContactGenerationCallback_NoScale&);
+};
+
+struct CapsuleMeshContactGenerationCallback_Scale : CapsuleMeshContactGenerationCallback_NoScale
+{
+ const Cm::FastVertex2ShapeScaling& mScaling;
+
+ CapsuleMeshContactGenerationCallback_Scale(
+ ContactBuffer& contactBuffer,
+ const PxTransform& transform1, const Segment& meshCapsule,
+ PxReal inflatedRadius, const Cm::FastVertex2ShapeScaling& scaling, PxReal contactDistance,
+ PxReal shapeCapsuleRadius, const TriangleMesh* meshData
+ ) :
+ CapsuleMeshContactGenerationCallback_NoScale(contactBuffer, transform1, meshCapsule, inflatedRadius, contactDistance, shapeCapsuleRadius, meshData),
+ mScaling (scaling)
+ {
+ }
+
+ virtual PxAgain processHit(
+ const PxRaycastHit& hit, const PxVec3& v0, const PxVec3& v1, const PxVec3& v2, PxReal&, const PxU32* /*vInds*/)
+ {
+ TrianglePadded tri;
+ getScaledVertices(tri.verts, v0, v1, v2, false, mScaling);
+ return processTriangle(hit, tri);
+ }
+
+private:
+ CapsuleMeshContactGenerationCallback_Scale& operator=(const CapsuleMeshContactGenerationCallback_Scale&);
+};
+
+}
+
+// PT: computes local capsule without going to world-space
+static PX_FORCE_INLINE Segment computeLocalCapsule(const PxTransform& transform0, const PxTransform& transform1, const PxCapsuleGeometry& shapeCapsule)
+{
+ const PxVec3 halfHeight = getCapsuleHalfHeightVector(transform0, shapeCapsule);
+ const PxVec3 delta = transform1.p - transform0.p;
+ return Segment(
+ transform1.rotateInv(halfHeight - delta),
+ transform1.rotateInv(-halfHeight - delta));
+}
+
+bool Gu::contactCapsuleMesh(GU_CONTACT_METHOD_ARGS)
+{
+ PX_UNUSED(cache);
+ PX_UNUSED(renderOutput);
+
+ const PxCapsuleGeometry& shapeCapsule = shape0.get<const PxCapsuleGeometry>();
+ const PxTriangleMeshGeometryLL& shapeMesh = shape1.get<const PxTriangleMeshGeometryLL>();
+
+ const PxReal inflatedRadius = shapeCapsule.radius + params.mContactDistance; //AM: inflate!
+ const Segment meshCapsule = computeLocalCapsule(transform0, transform1, shapeCapsule);
+
+ const TriangleMesh* meshData = shapeMesh.meshData;
+
+ //bound the capsule in shape space by an OBB:
+ Box queryBox;
+ {
+ const Capsule queryCapsule(meshCapsule, inflatedRadius);
+ queryBox.create(queryCapsule);
+ }
+
+ if(shapeMesh.scale.isIdentity())
+ {
+ CapsuleMeshContactGenerationCallback_NoScale callback(contactBuffer, transform1, meshCapsule,
+ inflatedRadius, params.mContactDistance, shapeCapsule.radius, meshData);
+
+ // PT: TODO: switch to capsule query here
+ Midphase::intersectOBB(meshData, queryBox, callback, true);
+ }
+ else
+ {
+ const Cm::FastVertex2ShapeScaling meshScaling(shapeMesh.scale);
+
+ CapsuleMeshContactGenerationCallback_Scale callback(contactBuffer, transform1, meshCapsule,
+ inflatedRadius, meshScaling, params.mContactDistance, shapeCapsule.radius, meshData);
+
+ //switched from capsuleCollider to boxCollider so we can support nonuniformly scaled meshes by scaling the query region:
+
+ //apply the skew transform to the box:
+ meshScaling.transformQueryBounds(queryBox.center, queryBox.extents, queryBox.rot);
+
+ Midphase::intersectOBB(meshData, queryBox, callback, true);
+ }
+ return contactBuffer.count > 0;
+}
+
+namespace
+{
+struct CapsuleHeightfieldContactGenerationCallback : EntityReport<PxU32>
+{
+ CapsuleMeshContactGeneration mGeneration;
+ HeightFieldUtil& mHfUtil;
+ const PxTransform& mTransform1;
+
+ CapsuleHeightfieldContactGenerationCallback(
+ ContactBuffer& contactBuffer,
+ const PxTransform& transform1, HeightFieldUtil& hfUtil, const Segment& meshCapsule,
+ PxReal inflatedRadius, PxReal contactDistance, PxReal shapeCapsuleRadius
+ ) :
+ mGeneration (contactBuffer, transform1, meshCapsule, inflatedRadius, contactDistance, shapeCapsuleRadius),
+ mHfUtil (hfUtil),
+ mTransform1 (transform1)
+ {
+ PX_ASSERT(contactBuffer.count==0);
+ }
+
+ // PT: TODO: refactor/unify with similar code in other places
+ virtual bool onEvent(PxU32 nb, PxU32* indices)
+ {
+ const PxU8 nextInd[] = {2,0,1};
+
+ while(nb--)
+ {
+ const PxU32 triangleIndex = *indices++;
+
+ PxU32 vertIndices[3];
+ TrianglePadded currentTriangle; // in world space
+ PxU32 adjInds[3];
+ mHfUtil.getTriangle(mTransform1, currentTriangle, vertIndices, adjInds, triangleIndex, false, false);
+
+ PxVec3 normal;
+ currentTriangle.normal(normal);
+
+ PxU8 triFlags = 0; //KS - temporary until we can calculate triFlags for HF
+
+ for(PxU32 a = 0; a < 3; ++a)
+ {
+ if(adjInds[a] != 0xFFFFFFFF)
+ {
+ PxTriangle adjTri;
+ mHfUtil.getTriangle(mTransform1, adjTri, NULL, NULL, adjInds[a], false, false);
+ //We now compare the triangles to see if this edge is active
+
+ PxVec3 adjNormal;
+ adjTri.denormalizedNormal(adjNormal);
+ PxU32 otherIndex = nextInd[a];
+ PxF32 projD = adjNormal.dot(currentTriangle.verts[otherIndex] - adjTri.verts[0]);
+ if(projD < 0.f)
+ {
+ adjNormal.normalize();
+
+ PxF32 proj = adjNormal.dot(normal);
+
+ if(proj < 0.999f)
+ {
+ triFlags |= 1 << (a+3);
+ }
+ }
+ }
+ else
+ {
+ triFlags |= 1 << (a+3);
+ }
+ }
+
+ mGeneration.processTriangle(triangleIndex, currentTriangle, triFlags);
+ }
+ return true;
+ }
+
+private:
+ CapsuleHeightfieldContactGenerationCallback& operator=(const CapsuleHeightfieldContactGenerationCallback&);
+};
+}
+
+bool Gu::contactCapsuleHeightfield(GU_CONTACT_METHOD_ARGS)
+{
+ PX_UNUSED(cache);
+ PX_UNUSED(renderOutput);
+
+ const PxCapsuleGeometry& shapeCapsule = shape0.get<const PxCapsuleGeometry>();
+ const PxHeightFieldGeometryLL& shapeMesh = shape1.get<const PxHeightFieldGeometryLL>();
+
+ const PxReal inflatedRadius = shapeCapsule.radius + params.mContactDistance; //AM: inflate!
+ const Segment meshCapsule = computeLocalCapsule(transform0, transform1, shapeCapsule);
+
+ // We must be in local space to use the cache
+
+ const HeightField& hf = *static_cast<HeightField*>(shapeMesh.heightField);
+ HeightFieldUtil hfUtil(shapeMesh, hf);
+
+ CapsuleHeightfieldContactGenerationCallback callback(
+ contactBuffer, transform1, hfUtil, meshCapsule, inflatedRadius, params.mContactDistance, shapeCapsule.radius);
+
+ //switched from capsuleCollider to boxCollider so we can support nonuniformly scaled meshes by scaling the query region:
+
+ //bound the capsule in shape space by an OBB:
+
+ PxBounds3 bounds;
+ bounds.maximum = PxVec3(shapeCapsule.halfHeight + inflatedRadius, inflatedRadius, inflatedRadius);
+ bounds.minimum = -bounds.maximum;
+
+ bounds = PxBounds3::transformFast(transform1.transformInv(transform0), bounds);
+
+ hfUtil.overlapAABBTriangles(transform1, bounds, 0, &callback);
+
+ return contactBuffer.count > 0;
+}
diff --git a/PhysX_3.4/Source/GeomUtils/src/contact/GuContactConvexConvex.cpp b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactConvexConvex.cpp
new file mode 100644
index 00000000..898152ae
--- /dev/null
+++ b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactConvexConvex.cpp
@@ -0,0 +1,1033 @@
+// 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 "PsMathUtils.h"
+#include "GuContactPolygonPolygon.h"
+#include "GuGeometryUnion.h"
+#include "GuConvexHelper.h"
+#include "GuInternal.h"
+#include "GuSeparatingAxes.h"
+#include "GuContactMethodImpl.h"
+#include "GuContactBuffer.h"
+#include "PsFPU.h"
+
+// csigg: the single reference of gEnableOptims (below) has been
+// replaced with the actual value to prevent ios64 compiler crash.
+// static const int gEnableOptims = 1;
+#define CONVEX_CONVEX_ROUGH_FIRST_PASS
+#define TEST_INTERNAL_OBJECTS
+#ifdef TEST_INTERNAL_OBJECTS
+ #define USE_BOX_DATA
+#endif
+
+using namespace physx;
+using namespace Gu;
+using namespace shdfnd::aos;
+using namespace intrinsics;
+
+#ifdef TEST_INTERNAL_OBJECTS
+#ifdef USE_BOX_DATA
+PX_FORCE_INLINE void BoxSupport(const float extents[3], const PxVec3& sv, float p[3])
+{
+ const PxU32* iextents = reinterpret_cast<const PxU32*>(extents);
+ const PxU32* isv = reinterpret_cast<const PxU32*>(&sv);
+ PxU32* ip = reinterpret_cast<PxU32*>(p);
+
+ ip[0] = iextents[0]|(isv[0]&PX_SIGN_BITMASK);
+ ip[1] = iextents[1]|(isv[1]&PX_SIGN_BITMASK);
+ ip[2] = iextents[2]|(isv[2]&PX_SIGN_BITMASK);
+}
+#endif
+
+#if PX_DEBUG
+static const PxReal testInternalObjectsEpsilon = 1.0e-3f;
+#endif
+
+#ifdef DO_NOT_REMOVE
+ PX_FORCE_INLINE void testInternalObjects_( const PxVec3& delta_c, const PxVec3& axis,
+ const PolygonalData& polyData0, const PolygonalData& polyData1,
+ const Cm::Matrix34& tr0, const Cm::Matrix34& tr1,
+ float& dmin, float contactDistance)
+ {
+ {
+/* float projected0 = axis.dot(tr0.p);
+ float projected1 = axis.dot(tr1.p);
+ float min0 = projected0 - polyData0.mInternal.mRadius;
+ float max0 = projected0 + polyData0.mInternal.mRadius;
+ float min1 = projected1 - polyData1.mInternal.mRadius;
+ float max1 = projected1 + polyData1.mInternal.mRadius;
+*/
+ float MinMaxRadius = polyData0.mInternal.mRadius + polyData1.mInternal.mRadius;
+ PxVec3 delta = tr0.p - tr1.p;
+ const PxReal dp = axis.dot(delta);
+// const PxReal dp2 = axis.dot(delta_c);
+
+// const PxReal d0 = max0 - min1;
+// const PxReal d0 = projected0 + polyData0.mInternal.mRadius - projected1 + polyData1.mInternal.mRadius;
+// const PxReal d0 = projected0 - projected1 + polyData0.mInternal.mRadius + polyData1.mInternal.mRadius;
+// const PxReal d0 = projected0 - projected1 + MinMaxRadius;
+// const PxReal d0 = axis.dot(tr0.p) - axis.dot(tr1.p) + MinMaxRadius;
+// const PxReal d0 = axis.dot(tr0.p - tr1.p) + MinMaxRadius;
+// const PxReal d0 = MinMaxRadius + axis.dot(delta);
+ const PxReal d0 = MinMaxRadius + dp;
+
+// const PxReal d1 = max1 - min0;
+// const PxReal d1 = projected1 + polyData1.mInternal.mRadius - projected0 + polyData0.mInternal.mRadius;
+// const PxReal d1 = projected1 - projected0 + polyData1.mInternal.mRadius + polyData0.mInternal.mRadius;
+// const PxReal d1 = projected1 - projected0 + MinMaxRadius;
+// const PxReal d1 = axis.dot(tr1.p) - axis.dot(tr0.p) + MinMaxRadius;
+// const PxReal d1 = axis.dot(tr1.p - tr0.p) + MinMaxRadius;
+// const PxReal d1 = MinMaxRadius - axis.dot(delta);
+ const PxReal d1 = MinMaxRadius - dp;
+
+ dmin = selectMin(d0, d1);
+ return;
+ }
+
+ #ifdef USE_BOX_DATA
+ const PxVec3 localAxis0 = tr0.rotateTranspose(axis);
+ const PxVec3 localAxis1 = tr1.rotateTranspose(axis);
+
+ const float dp = delta_c.dot(axis);
+
+ float p0[3];
+ BoxSupport(polyData0.mInternal.mExtents, localAxis0, p0);
+ float p1[3];
+ BoxSupport(polyData1.mInternal.mExtents, localAxis1, p1);
+
+ const float Radius0 = p0[0]*localAxis0.x + p0[1]*localAxis0.y + p0[2]*localAxis0.z;
+ const float Radius1 = p1[0]*localAxis1.x + p1[1]*localAxis1.y + p1[2]*localAxis1.z;
+
+ const float MinRadius = selectMax(Radius0, polyData0.mInternal.mRadius);
+ const float MaxRadius = selectMax(Radius1, polyData1.mInternal.mRadius);
+ #else
+ const float dp = delta_c.dot(axis);
+ const float MinRadius = polyData0.mInternal.mRadius;
+ const float MaxRadius = polyData1.mInternal.mRadius;
+ #endif
+ const float MinMaxRadius = MaxRadius + MinRadius;
+ const float d0 = MinMaxRadius + dp;
+ const float d1 = MinMaxRadius - dp;
+
+ dmin = selectMin(d0, d1);
+ }
+#endif
+
+static PX_FORCE_INLINE bool testInternalObjects( const PxVec3& delta_c, const PxVec3& axis,
+ const PolygonalData& polyData0, const PolygonalData& polyData1,
+ const Cm::Matrix34& tr0, const Cm::Matrix34& tr1,
+ float dmin)
+{
+#ifdef USE_BOX_DATA
+ const PxVec3 localAxis0 = tr0.rotateTranspose(axis);
+ const PxVec3 localAxis1 = tr1.rotateTranspose(axis);
+
+ const float dp = delta_c.dot(axis);
+
+ float p0[3];
+ BoxSupport(polyData0.mInternal.mExtents, localAxis0, p0);
+ float p1[3];
+ BoxSupport(polyData1.mInternal.mExtents, localAxis1, p1);
+
+ const float Radius0 = p0[0]*localAxis0.x + p0[1]*localAxis0.y + p0[2]*localAxis0.z;
+ const float Radius1 = p1[0]*localAxis1.x + p1[1]*localAxis1.y + p1[2]*localAxis1.z;
+
+ const float MinRadius = selectMax(Radius0, polyData0.mInternal.mRadius);
+ const float MaxRadius = selectMax(Radius1, polyData1.mInternal.mRadius);
+#else
+ const float dp = delta_c.dot(axis);
+ const float MinRadius = polyData0.mInternal.mRadius;
+ const float MaxRadius = polyData1.mInternal.mRadius;
+#endif
+ const float MinMaxRadius = MaxRadius + MinRadius;
+ const float d0 = MinMaxRadius + dp;
+ const float d1 = MinMaxRadius - dp;
+
+ const float depth = selectMin(d0, d1);
+ if(depth>dmin)
+ return false;
+ return true;
+}
+#endif
+
+PX_FORCE_INLINE float PxcMultiplyAdd3x4(PxU32 i, const PxVec3& p0, const PxVec3& p1, const Cm::Matrix34& world)
+{
+ return (p1.x + p0.x) * world.m.column0[i] + (p1.y + p0.y) * world.m.column1[i] + (p1.z + p0.z) * world.m.column2[i] + 2.0f * world.p[i];
+}
+
+PX_FORCE_INLINE float PxcMultiplySub3x4(PxU32 i, const PxVec3& p0, const PxVec3& p1, const Cm::Matrix34& world)
+{
+ return (p1.x - p0.x) * world.m.column0[i] + (p1.y - p0.y) * world.m.column1[i] + (p1.z - p0.z) * world.m.column2[i];
+}
+
+static PX_FORCE_INLINE bool testNormal( const PxVec3& axis, PxReal min0, PxReal max0,
+ const PolygonalData& polyData1,
+ const Cm::Matrix34& m1to0,
+ const Cm::FastVertex2ShapeScaling& scaling1,
+ PxReal& depth, PxReal contactDistance)
+{
+ //The separating axis we want to test is a face normal of hull0
+ PxReal min1, max1;
+ (polyData1.mProjectHull)(polyData1, axis, m1to0, scaling1, min1, max1);
+
+ if(max0+contactDistance<min1 || max1+contactDistance<min0)
+ return false;
+
+ const PxReal d0 = max0 - min1;
+ const PxReal d1 = max1 - min0;
+ depth = selectMin(d0, d1);
+ return true;
+}
+
+static PX_FORCE_INLINE bool testSeparatingAxis( const PolygonalData& polyData0, const PolygonalData& polyData1,
+ const Cm::Matrix34& world0, const Cm::Matrix34& world1,
+ const Cm::FastVertex2ShapeScaling& scaling0, const Cm::FastVertex2ShapeScaling& scaling1,
+ const PxVec3& axis, PxReal& depth, PxReal contactDistance)
+{
+ PxReal min0, max0;
+ (polyData0.mProjectHull)(polyData0, axis, world0, scaling0, min0, max0);
+
+ return testNormal(axis, min0, max0, polyData1, world1, scaling1, depth, contactDistance);
+}
+
+static bool PxcSegmentAABBIntersect(const PxVec3& p0, const PxVec3& p1,
+ const PxVec3& minimum, const PxVec3& maximum,
+ const Cm::Matrix34& world)
+{
+ PxVec3 boxExtent, diff, dir;
+ PxReal fAWdU[3];
+
+ dir.x = PxcMultiplySub3x4(0, p0, p1, world);
+ boxExtent.x = maximum.x - minimum.x;
+ diff.x = (PxcMultiplyAdd3x4(0, p0, p1, world) - (maximum.x+minimum.x));
+ fAWdU[0] = PxAbs(dir.x);
+ if(PxAbs(diff.x)> boxExtent.x + fAWdU[0]) return false;
+
+ dir.y = PxcMultiplySub3x4(1, p0, p1, world);
+ boxExtent.y = maximum.y - minimum.y;
+ diff.y = (PxcMultiplyAdd3x4(1, p0, p1, world) - (maximum.y+minimum.y));
+ fAWdU[1] = PxAbs(dir.y);
+ if(PxAbs(diff.y)> boxExtent.y + fAWdU[1]) return false;
+
+ dir.z = PxcMultiplySub3x4(2, p0, p1, world);
+ boxExtent.z = maximum.z - minimum.z;
+ diff.z = (PxcMultiplyAdd3x4(2, p0, p1, world) - (maximum.z+minimum.z));
+ fAWdU[2] = PxAbs(dir.z);
+ if(PxAbs(diff.z)> boxExtent.z + fAWdU[2]) return false;
+
+ PxReal f;
+ f = dir.y * diff.z - dir.z*diff.y; if(PxAbs(f)>boxExtent.y*fAWdU[2] + boxExtent.z*fAWdU[1]) return false;
+ f = dir.z * diff.x - dir.x*diff.z; if(PxAbs(f)>boxExtent.x*fAWdU[2] + boxExtent.z*fAWdU[0]) return false;
+ f = dir.x * diff.y - dir.y*diff.x; if(PxAbs(f)>boxExtent.x*fAWdU[1] + boxExtent.y*fAWdU[0]) return false;
+ return true;
+}
+
+#define EXPERIMENT
+
+/*
+Edge culling can clearly be improved : in ConvexTest02 for example, edges don't even touch the other mesh sometimes.
+*/
+static void PxcFindSeparatingAxes( SeparatingAxes& sa, const PxU32* PX_RESTRICT indices, PxU32 numPolygons,
+ const PolygonalData& polyData,
+ const Cm::Matrix34& world0, const PxPlane& plane,
+ const Cm::Matrix34& m0to1, const PxBounds3& aabb, PxReal contactDistance,
+ const Cm::FastVertex2ShapeScaling& scaling)
+{
+// EdgeCache edgeCache; // PT: TODO: check this is actually useful
+ const PxVec3* PX_RESTRICT vertices = polyData.mVerts;
+ const Gu::HullPolygonData* PX_RESTRICT polygons = polyData.mPolygons;
+ const PxU8* PX_RESTRICT vrefsBase = polyData.mPolygonVertexRefs;
+
+ while(numPolygons--)
+ {
+ //Get current polygon
+ const Gu::HullPolygonData& P = polygons[*indices++];
+ const PxU8* PX_RESTRICT VData = vrefsBase + P.mVRef8;
+
+ // Loop through polygon vertices == polygon edges
+ PxU32 numVerts = P.mNbVerts;
+
+#ifdef EXPERIMENT
+ PxVec3 p0,p1;
+ PxU8 VRef0 = VData[0];
+ p0 = scaling * vertices[VRef0];
+ bool b0 = plane.distance(p0) <= contactDistance;
+#endif
+
+ for(PxU32 j = 0; j < numVerts; j++)
+ {
+ PxU32 j1 = j+1;
+ if(j1 >= numVerts) j1 = 0;
+
+#ifndef EXPERIMENT
+ PxU8 VRef0 = VData[j];
+#endif
+ PxU8 VRef1 = VData[j1];
+
+// Ps::order(VRef0, VRef1); //make sure edge (a,b) == edge (b,a)
+// if (edgeCache.isInCache(VRef0, VRef1))
+// continue;
+
+ //transform points //TODO: once this works we could transform plan instead, that is more efficient!!
+
+#ifdef EXPERIMENT
+ p1 = scaling * vertices[VRef1];
+#else
+ const PxVec3 p0 = scaling * vertices[VRef0];
+ const PxVec3 p1 = scaling * vertices[VRef1];
+#endif
+
+ // Cheap but effective culling!
+#ifdef EXPERIMENT
+ bool b1 = plane.distance(p1) <= contactDistance;
+ if(b0 || b1)
+#else
+ if(plane.signedDistanceHessianNormalForm(p0) <= contactDistance ||
+ plane.signedDistanceHessianNormalForm(p1) <= contactDistance)
+#endif
+ {
+ if(PxcSegmentAABBIntersect(p0, p1, aabb.minimum, aabb.maximum, m0to1))
+ {
+ // Create current edge. We're only interested in different edge directions so we normalize.
+ const PxVec3 currentEdge = world0.rotate(p0 - p1).getNormalized();
+ sa.addAxis(currentEdge);
+ }
+ }
+#ifdef EXPERIMENT
+ VRef0 = VRef1;
+ p0 = p1;
+ b0 = b1;
+#endif
+ }
+ }
+}
+
+static bool PxcTestFacesSepAxesBackface(const PolygonalData& polyData0, const PolygonalData& polyData1,
+ const Cm::Matrix34& world0, const Cm::Matrix34& world1,
+ const Cm::FastVertex2ShapeScaling& scaling0, const Cm::FastVertex2ShapeScaling& scaling1,
+ const Cm::Matrix34& m1to0, const PxVec3& delta,
+ PxReal& dmin, PxVec3& sep, PxU32& id, PxU32* PX_RESTRICT indices_, PxU32& numIndices,
+ PxReal contactDistance, float toleranceLength
+#ifdef TEST_INTERNAL_OBJECTS
+ , const PxVec3& worldDelta
+#endif
+ )
+{
+ PX_UNUSED(toleranceLength); // Only used in Debug
+ id = PX_INVALID_U32;
+ PxU32* indices = indices_;
+
+ const PxU32 num = polyData0.mNbPolygons;
+ const PxVec3* PX_RESTRICT vertices = polyData0.mVerts;
+ const Gu::HullPolygonData* PX_RESTRICT polygons = polyData0.mPolygons;
+
+ //transform delta from hull0 shape into vertex space:
+ const PxVec3 vertSpaceDelta = scaling0 % delta;
+
+ // PT: prefetch polygon data
+ {
+ const PxU32 dataSize = num*sizeof(Gu::HullPolygonData);
+ for(PxU32 offset=0; offset < dataSize; offset+=128)
+ Ps::prefetchLine(polygons, offset);
+ }
+
+ for(PxU32 i=0; i < num; i++)
+ {
+ const Gu::HullPolygonData& P = polygons[i];
+ const PxPlane& PL = P.mPlane;
+
+ // Do backface-culling
+ if(PL.n.dot(vertSpaceDelta) < 0.0f)
+ continue;
+
+ //normals transform by inverse transpose: (and transpose(skew) == skew as its symmetric)
+ PxVec3 shapeSpaceNormal = scaling0 % PL.n;
+ //renormalize: (Arr!)
+ const PxReal magnitude = shapeSpaceNormal.normalize(); // PT: We need to find a way to skip this normalize
+
+#ifdef TEST_INTERNAL_OBJECTS
+
+/*
+const PxVec3 worldNormal_ = world0.rotate(shapeSpaceNormal);
+PxReal d0;
+bool test0 = PxcTestSeparatingAxis(polyData0, polyData1, world0, world1, scaling0, scaling1, worldNormal_, d0, contactDistance);
+
+PxReal d1;
+const float invMagnitude0 = 1.0f / magnitude;
+bool test1 = PxcTestNormal(shapeSpaceNormal, P.getMin(vertices) * invMagnitude0, P.getMax() * invMagnitude0, polyData1, m1to0, scaling1, d1, contactDistance);
+
+PxReal d2;
+testInternalObjects_(worldDelta, worldNormal_, polyData0, polyData1, world0, world1, d2, contactDistance);
+*/
+
+ const PxVec3 worldNormal = world0.rotate(shapeSpaceNormal);
+ if(!testInternalObjects(worldDelta, worldNormal, polyData0, polyData1, world0, world1, dmin))
+ {
+ #if PX_DEBUG
+ PxReal d;
+ const float invMagnitude = 1.0f / magnitude;
+ if(testNormal(shapeSpaceNormal, P.getMin(vertices) * invMagnitude, P.getMax() * invMagnitude, polyData1, m1to0, scaling1, d, contactDistance)) //note how we scale scalars by skew magnitude change as we do plane d-s.
+ {
+ PX_ASSERT(d + testInternalObjectsEpsilon*toleranceLength >= dmin);
+ }
+ #endif
+ continue;
+ }
+#endif
+
+ *indices++ = i;
+
+ ////////////////////
+ /*
+ //gotta transform minimum and maximum from vertex to shape space!
+ //I think they transform like the 'd' of the plane, basically by magnitude division!
+ //unfortunately I am not certain of that, so let's convert them to a point in vertex space, transform that, and then convert back to a plane d.
+
+ //let's start by transforming the plane's d:
+ //a point on the plane:
+
+ PxVec3 vertSpaceDPoint = PL.normal * -PL.d;
+
+ //make sure this is on the plane:
+ PxReal distZero = PL.signedDistanceHessianNormalForm(vertSpaceDPoint); //should be zero
+
+ //transform:
+
+ PxVec3 shapeSpaceDPoint = cache.mVertex2ShapeSkew[skewIndex] * vertSpaceDPoint;
+
+ //make into a d offset again by projecting along the plane:
+ PxcPlane shapeSpacePlane(shapeSpaceNormal, shapeSpaceDPoint);
+
+ //see what D is!!
+
+
+ //NOTE: for boxes scale[0] is always id so this is all redundant. Hopefully for convex convex it will become useful!
+ */
+
+ ////////////////////
+
+ PxReal d;
+ const float invMagnitude = 1.0f / magnitude;
+ if(!testNormal(shapeSpaceNormal, P.getMin(vertices) * invMagnitude, P.getMax() * invMagnitude, polyData1, m1to0, scaling1, d, contactDistance)) //note how we scale scalars by skew magnitude change as we do plane d-s.
+ return false;
+
+ if(d < dmin)
+ {
+#ifdef TEST_INTERNAL_OBJECTS
+ sep = worldNormal;
+#else
+ sep = world0.rotate(shapeSpaceNormal);
+#endif
+ dmin = d;
+ id = i;
+ }
+ }
+
+ numIndices = PxU32(indices - indices_);
+
+ PX_ASSERT(id!=PX_INVALID_U32); //Should never happen with this version
+
+ return true;
+}
+
+// PT: isolating this piece of code allows us to better see what happens in PIX. For some reason it looks
+// like isolating it creates *less* LHS than before, but I don't understand why. There's still a lot of
+// bad stuff there anyway
+//PX_FORCE_INLINE
+static void prepareData(PxPlane& witnessPlane0, PxPlane& witnessPlane1,
+ PxBounds3& aabb0, PxBounds3& aabb1,
+ const PxBounds3& hullBounds0, const PxBounds3& hullBounds1,
+ const PxPlane& vertSpacePlane0, const PxPlane& vertSpacePlane1,
+ const Cm::FastVertex2ShapeScaling& scaling0, const Cm::FastVertex2ShapeScaling& scaling1,
+ const Cm::Matrix34& m0to1, const Cm::Matrix34& m1to0,
+ PxReal contactDistance
+ )
+{
+ scaling0.transformPlaneToShapeSpace(vertSpacePlane0.n, vertSpacePlane0.d, witnessPlane0.n, witnessPlane0.d);
+ scaling1.transformPlaneToShapeSpace(vertSpacePlane1.n, vertSpacePlane1.d, witnessPlane1.n, witnessPlane1.d);
+
+ //witnessPlane0 = witnessPlane0.getTransformed(m0to1);
+ //witnessPlane1 = witnessPlane1.getTransformed(m1to0);
+ const PxVec3 newN0 = m0to1.rotate(witnessPlane0.n);
+ witnessPlane0 = PxPlane(newN0, witnessPlane0.d - m0to1.p.dot(newN0));
+ const PxVec3 newN1 = m1to0.rotate(witnessPlane1.n);
+ witnessPlane1 = PxPlane(newN1, witnessPlane1.d - m1to0.p.dot(newN1));
+
+ aabb0 = hullBounds0;
+ aabb1 = hullBounds1;
+
+ //gotta inflate // PT: perfect LHS recipe here....
+ const PxVec3 inflate(contactDistance);
+ aabb0.minimum -= inflate;
+ aabb1.minimum -= inflate;
+ aabb0.maximum += inflate;
+ aabb1.maximum += inflate;
+}
+
+static bool PxcBruteForceOverlapBackface( const PxBounds3& hullBounds0, const PxBounds3& hullBounds1,
+ const PolygonalData& polyData0, const PolygonalData& polyData1,
+ const Cm::Matrix34& world0, const Cm::Matrix34& world1,
+ const Cm::FastVertex2ShapeScaling& scaling0, const Cm::FastVertex2ShapeScaling& scaling1,
+ const Cm::Matrix34& m0to1, const Cm::Matrix34& m1to0, const PxVec3& delta,
+ PxU32& id0, PxU32& id1,
+ PxReal& depth, PxVec3& sep, PxcSepAxisType& code, PxReal contactDistance, float toleranceLength)
+{
+ const PxVec3 localDelta0 = world0.rotateTranspose(delta);
+ PxU32* PX_RESTRICT indices0 = reinterpret_cast<PxU32*>(PxAlloca(polyData0.mNbPolygons*sizeof(PxU32)));
+
+ PxU32 numIndices0;
+ PxReal dmin0 = PX_MAX_REAL;
+ PxVec3 vec0;
+ if(!PxcTestFacesSepAxesBackface(polyData0, polyData1, world0, world1, scaling0, scaling1, m1to0, localDelta0, dmin0, vec0, id0, indices0, numIndices0, contactDistance, toleranceLength
+#ifdef TEST_INTERNAL_OBJECTS
+ , -delta
+#endif
+ ))
+ return false;
+
+ const PxVec3 localDelta1 = world1.rotateTranspose(delta);
+
+ PxU32* PX_RESTRICT indices1 = reinterpret_cast<PxU32*>(PxAlloca(polyData1.mNbPolygons*sizeof(PxU32)));
+
+ PxU32 numIndices1;
+ PxReal dmin1 = PX_MAX_REAL;
+ PxVec3 vec1;
+ if(!PxcTestFacesSepAxesBackface(polyData1, polyData0, world1, world0, scaling1, scaling0, m0to1, -localDelta1, dmin1, vec1, id1, indices1, numIndices1, contactDistance, toleranceLength
+#ifdef TEST_INTERNAL_OBJECTS
+ , delta
+#endif
+ ))
+ return false;
+
+ PxReal dmin = dmin0;
+ PxVec3 vec = vec0;
+ code = SA_NORMAL0;
+
+ if(dmin1 < dmin)
+ {
+ dmin = dmin1;
+ vec = vec1;
+ code = SA_NORMAL1;
+ }
+
+ PX_ASSERT(id0!=PX_INVALID_U32);
+ PX_ASSERT(id1!=PX_INVALID_U32);
+
+ // Brute-force find a separating axis
+ SeparatingAxes mSA0;
+ SeparatingAxes mSA1;
+ mSA0.reset();
+ mSA1.reset();
+ PxPlane witnessPlane0;
+ PxPlane witnessPlane1;
+ PxBounds3 aabb0, aabb1;
+
+ prepareData(witnessPlane0, witnessPlane1,
+ aabb0, aabb1,
+ hullBounds0, hullBounds1,
+ polyData0.mPolygons[id0].mPlane,
+ polyData1.mPolygons[id1].mPlane,
+ scaling0, scaling1,
+ m0to1, m1to0,
+ contactDistance);
+
+ // Find possibly separating axes
+ PxcFindSeparatingAxes(mSA0, indices0, numIndices0, polyData0, world0, witnessPlane1, m0to1, aabb1, contactDistance, scaling0);
+ PxcFindSeparatingAxes(mSA1, indices1, numIndices1, polyData1, world1, witnessPlane0, m1to0, aabb0, contactDistance, scaling1);
+// PxcFindSeparatingAxes(context.mSA0, &id0, 1, hull0, world0, witnessPlane1, m0to1, aabbMin1, aabbMax1);
+// PxcFindSeparatingAxes(context.mSA1, &id1, 1, hull1, world1, witnessPlane0, m1to0, aabbMin0, aabbMax0);
+
+ const PxU32 numEdges0 = mSA0.getNumAxes();
+ const PxVec3* PX_RESTRICT edges0 = mSA0.getAxes();
+
+ const PxU32 numEdges1 = mSA1.getNumAxes();
+ const PxVec3* PX_RESTRICT edges1 = mSA1.getAxes();
+
+ // Worst case = convex test 02 with big meshes: 23 & 23 edges => 23*23 = 529 tests!
+ // printf("%d - %d\n", NbEdges0, NbEdges1); // maximum = ~20 in test scenes.
+
+ for(PxU32 i=0; i < numEdges0; i++)
+ {
+ const PxVec3& edge0 = edges0[i];
+ for(PxU32 j=0; j < numEdges1; j++)
+ {
+ const PxVec3& edge1 = edges1[j];
+
+ PxVec3 sepAxis = edge0.cross(edge1);
+ if(!Ps::isAlmostZero(sepAxis))
+ {
+ sepAxis = sepAxis.getNormalized();
+
+#ifdef TEST_INTERNAL_OBJECTS
+ if(!testInternalObjects(-delta, sepAxis, polyData0, polyData1, world0, world1, dmin))
+ {
+ #if PX_DEBUG
+ PxReal d;
+ if(testSeparatingAxis(polyData0, polyData1, world0, world1, scaling0, scaling1, sepAxis, d, contactDistance))
+ {
+ PX_ASSERT(d + testInternalObjectsEpsilon*toleranceLength >= dmin);
+ }
+ #endif
+ continue;
+ }
+#endif
+ PxReal d;
+ if(!testSeparatingAxis(polyData0, polyData1, world0, world1, scaling0, scaling1, sepAxis, d, contactDistance))
+ return false;
+
+ if(d<dmin)
+ {
+ dmin = d;
+ vec = sepAxis;
+ code = SA_EE;
+ }
+ }
+ }
+ }
+
+ depth = dmin;
+ sep = vec;
+
+ return true;
+}
+
+static bool GuTestFacesSepAxesBackfaceRoughPass(
+ const PolygonalData& polyData0, const PolygonalData& polyData1,
+ const Cm::Matrix34& world0, const Cm::Matrix34& world1,
+ const Cm::FastVertex2ShapeScaling& scaling0, const Cm::FastVertex2ShapeScaling& scaling1,
+ const Cm::Matrix34& m1to0, const PxVec3& /*witness*/, const PxVec3& delta,
+ PxReal& dmin, PxVec3& sep, PxU32& id,
+ PxReal contactDistance, float toleranceLength
+#ifdef TEST_INTERNAL_OBJECTS
+ , const PxVec3& worldDelta
+#endif
+ )
+{
+ PX_UNUSED(toleranceLength); // Only used in Debug
+ id = PX_INVALID_U32;
+
+ const PxU32 num = polyData0.mNbPolygons;
+ const Gu::HullPolygonData* PX_RESTRICT polygons = polyData0.mPolygons;
+ const PxVec3* PX_RESTRICT vertices = polyData0.mVerts;
+
+ //transform delta from hull0 shape into vertex space:
+ const PxVec3 vertSpaceDelta = scaling0 % delta;
+
+ for(PxU32 i=0; i < num; i++)
+ {
+ const Gu::HullPolygonData& P = polygons[i];
+ const PxPlane& PL = P.mPlane;
+
+ if(PL.n.dot(vertSpaceDelta) < 0.0f)
+ continue; //backface-cull
+
+ PxVec3 shapeSpaceNormal = scaling0 % PL.n; //normals transform with inverse transpose
+ //renormalize: (Arr!)
+ const PxReal magnitude = shapeSpaceNormal.normalize(); // PT: We need to find a way to skip this normalize
+
+#ifdef TEST_INTERNAL_OBJECTS
+ const PxVec3 worldNormal = world0.rotate(shapeSpaceNormal);
+ if(!testInternalObjects(worldDelta, worldNormal, polyData0, polyData1, world0, world1, dmin))
+ {
+ #if PX_DEBUG
+ PxReal d;
+ const float invMagnitude = 1.0f / magnitude;
+ if(testNormal(shapeSpaceNormal, P.getMin(vertices) * invMagnitude, P.getMax() * invMagnitude, /*hull1,*/ polyData1, m1to0, scaling1, d, contactDistance)) //note how we scale scalars by skew magnitude change as we do plane d-s.
+ {
+ PX_ASSERT(d + testInternalObjectsEpsilon*toleranceLength >= dmin);
+ }
+ #endif
+ continue;
+ }
+#endif
+
+ PxReal d;
+ const float invMagnitude = 1.0f / magnitude;
+ if(!testNormal(shapeSpaceNormal, P.getMin(vertices) * invMagnitude, P.getMax() * invMagnitude, /*hull1,*/ polyData1, m1to0, scaling1, d, contactDistance)) //note how we scale scalars by skew magnitude change as we do plane d-s.
+ return false;
+
+ if(d < dmin)
+ {
+#ifdef TEST_INTERNAL_OBJECTS
+ sep = worldNormal;
+#else
+ sep = world0.rotate(shapeSpaceNormal);
+#endif
+ dmin = d;
+ id = i;
+ }
+ }
+
+ PX_ASSERT(id!=PX_INVALID_U32); //Should never happen with this version
+
+ return true;
+}
+
+static bool GuBruteForceOverlapBackfaceRoughPass( const PolygonalData& polyData0, const PolygonalData& polyData1,
+ const Cm::Matrix34& world0, const Cm::Matrix34& world1,
+ const Cm::FastVertex2ShapeScaling& scaling0, const Cm::FastVertex2ShapeScaling& scaling1,
+ const Cm::Matrix34& m0to1, const Cm::Matrix34& m1to0, const PxVec3& delta,
+ PxU32& id0, PxU32& id1,
+ PxReal& depth, PxVec3& sep, PxcSepAxisType& code, PxReal contactDistance, PxReal toleranceLength)
+{
+ PxReal dmin0 = PX_MAX_REAL;
+ PxReal dmin1 = PX_MAX_REAL;
+ PxVec3 vec0, vec1;
+
+ const PxVec3 localDelta0 = world0.rotateTranspose(delta);
+ const PxVec3 localCenter1in0 = m1to0.transform(polyData1.mCenter);
+ if(!GuTestFacesSepAxesBackfaceRoughPass(polyData0, polyData1, world0, world1, scaling0, scaling1, m1to0, localCenter1in0, localDelta0, dmin0, vec0, id0, contactDistance, toleranceLength
+#ifdef TEST_INTERNAL_OBJECTS
+ , -delta
+#endif
+ ))
+ return false;
+
+ const PxVec3 localDelta1 = world1.rotateTranspose(delta);
+ const PxVec3 localCenter0in1 = m0to1.transform(polyData0.mCenter);
+ if(!GuTestFacesSepAxesBackfaceRoughPass(polyData1, polyData0, world1, world0, scaling1, scaling0, m0to1, localCenter0in1, -localDelta1, dmin1, vec1, id1, contactDistance, toleranceLength
+#ifdef TEST_INTERNAL_OBJECTS
+ , delta
+#endif
+ ))
+ return false;
+
+ PxReal dmin = dmin0;
+ PxVec3 vec = vec0;
+ code = SA_NORMAL0;
+
+ if(dmin1 < dmin)
+ {
+ dmin = dmin1;
+ vec = vec1;
+ code = SA_NORMAL1;
+ }
+
+ PX_ASSERT(id0!=PX_INVALID_U32);
+ PX_ASSERT(id1!=PX_INVALID_U32);
+
+ depth = dmin;
+ sep = vec;
+
+ return true;
+}
+
+static bool GuContactHullHull( const PolygonalData& polyData0, const PolygonalData& polyData1,
+ const PxBounds3& hullBounds0, const PxBounds3& hullBounds1,
+ const PxTransform& transform0, const PxTransform& transform1,
+ const NarrowPhaseParams& params, ContactBuffer& contactBuffer,
+ const Cm::FastVertex2ShapeScaling& scaling0, const Cm::FastVertex2ShapeScaling& scaling1,
+ bool idtScale0, bool idtScale1);
+
+namespace physx
+{
+
+namespace Gu
+{
+// Box-convex contact generation
+// this can no longer share code with convex-convex because that case needs scaling for both shapes, while this only needs it for one, which may be significant perf wise.
+//
+// PT: duplicating the full convex-vs-convex codepath is a lot worse. Look at it this way: if scaling is really "significant perf wise" then we made the whole convex-convex
+// codepath significantly slower, and this is a lot more important than just box-vs-convex. The proper approach is to share the code and make sure scaling is NOT a perf hit.
+// PT: please leave this function in the same translation unit as PxcContactHullHull.
+
+bool contactBoxConvex(GU_CONTACT_METHOD_ARGS)
+{
+ PX_UNUSED(renderOutput);
+ PX_UNUSED(cache);
+
+ const PxBoxGeometry& shapeBox = shape0.get<const PxBoxGeometry>();
+
+ Cm::FastVertex2ShapeScaling idtScaling;
+
+ const PxBounds3 boxBounds(-shapeBox.halfExtents, shapeBox.halfExtents);
+
+ PolygonalData polyData0;
+ PolygonalBox polyBox(shapeBox.halfExtents);
+ polyBox.getPolygonalData(&polyData0);
+
+ ///////
+
+ Cm::FastVertex2ShapeScaling convexScaling;
+ PxBounds3 convexBounds;
+ PolygonalData polyData1;
+ const bool idtScale = getConvexData(shape1, convexScaling, convexBounds, polyData1);
+
+ return GuContactHullHull( polyData0, polyData1, boxBounds, convexBounds,
+ transform0, transform1, params, contactBuffer,
+ idtScaling, convexScaling, true, idtScale);
+}
+
+// PT: please leave this function in the same translation unit as PxcContactHullHull.
+bool contactConvexConvex(GU_CONTACT_METHOD_ARGS)
+{
+ PX_UNUSED(cache);
+ PX_UNUSED(renderOutput);
+
+ Cm::FastVertex2ShapeScaling scaling0, scaling1;
+ PxBounds3 convexBounds0, convexBounds1;
+ PolygonalData polyData0, polyData1;
+ const bool idtScale0 = getConvexData(shape0, scaling0, convexBounds0, polyData0);
+ const bool idtScale1 = getConvexData(shape1, scaling1, convexBounds1, polyData1);
+
+ return GuContactHullHull( polyData0, polyData1, convexBounds0, convexBounds1,
+ transform0, transform1, params, contactBuffer,
+ scaling0, scaling1, idtScale0, idtScale1);
+}
+}//Gu
+}//physx
+
+static bool GuContactHullHull( const PolygonalData& polyData0, const PolygonalData& polyData1,
+ const PxBounds3& hullBounds0, const PxBounds3& hullBounds1,
+ const PxTransform& transform0, const PxTransform& transform1,
+ const NarrowPhaseParams& params, ContactBuffer& contactBuffer,
+ const Cm::FastVertex2ShapeScaling& scaling0, const Cm::FastVertex2ShapeScaling& scaling1,
+ bool idtScale0, bool idtScale1)
+{
+ // Compute matrices
+ const Cm::Matrix34 world0(transform0);
+ const Cm::Matrix34 world1(transform1);
+
+ const PxVec3 worldCenter0 = world0.transform(polyData0.mCenter);
+ const PxVec3 worldCenter1 = world1.transform(polyData1.mCenter);
+
+ const PxVec3 deltaC = worldCenter1 - worldCenter0;
+
+ PxReal depth;
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ // Early-exit test: quickly discard obviously non-colliding cases.
+ // PT: we used to skip this when objects were touching, which provided a small speedup (saving 2 full hull projections).
+ // We may want to add this back at some point in the future, if possible.
+ if(true) //AM: now we definitely want to test the cached axis to get the depth value for the cached axis, even if we had a contact before.
+ {
+ if(!testSeparatingAxis(polyData0, polyData1, world0, world1, scaling0, scaling1, deltaC, depth, params.mContactDistance))
+ //there was no contact previously and we reject using the same prev. axis.
+ return false;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ // Compute relative transforms
+ PxTransform t0to1 = transform1.transformInv(transform0);
+ PxTransform t1to0 = transform0.transformInv(transform1);
+
+ PxU32 c0(0x7FFF);
+ PxU32 c1(0x7FFF);
+ PxVec3 worldNormal;
+
+ const Cm::Matrix34 m0to1(t0to1);
+ const Cm::Matrix34 m1to0(t1to0);
+
+#ifdef CONVEX_CONVEX_ROUGH_FIRST_PASS
+ // PT: it is a bad idea to skip the rough pass, for two reasons:
+ // 1) performance. The rough pass is obviously a lot faster.
+ // 2) stability. The "skipIt" optimization relies on the rough pass being present to catch the cases where it fails. If you disable the rough pass
+ // "skipIt" can skip the whole work, contacts get lost, and we never "try again" ==> explosions
+// bool TryRoughPass = (contactDistance == 0.0f); //Rough first pass doesn't work with dist based for some reason.
+ for(int TryRoughPass = 1 /*gEnableOptims*/; 0 <= TryRoughPass; --TryRoughPass)
+ {
+#endif
+
+ {
+ PxcSepAxisType code;
+ PxU32 id0, id1;
+ //PxReal depth;
+#ifdef CONVEX_CONVEX_ROUGH_FIRST_PASS
+ bool Status;
+ if(TryRoughPass)
+ {
+ Status = GuBruteForceOverlapBackfaceRoughPass(polyData0, polyData1, world0, world1, scaling0, scaling1, m0to1, m1to0, deltaC, id0, id1, depth, worldNormal, code, params.mContactDistance, params.mToleranceLength);
+ }
+ else
+ {
+ Status = PxcBruteForceOverlapBackface(
+// hull0, hull1,
+ hullBounds0, hullBounds1,
+ polyData0, polyData1, world0, world1, scaling0, scaling1, m0to1, m1to0, deltaC, id0, id1, depth, worldNormal, code, params.mContactDistance, params.mToleranceLength);
+ }
+
+ if(!Status)
+#else
+ if(!PxcBruteForceOverlapBackface(
+// hull0, hull1,
+ hullBounds0, hullBounds1,
+ polyData0, polyData1,
+ world0, world1, scaling0, scaling1, m0to1, m1to0, deltaC, id0, id1, depth, worldNormal, code, contactDistance))
+#endif
+ return false;
+
+ if(deltaC.dot(worldNormal) < 0.0f)
+ worldNormal = -worldNormal;
+
+/*
+worldNormal = -partialSep;
+depth = -partialDepth;
+code = SA_EE;
+*/
+
+ if(code==SA_NORMAL0)
+ {
+ c0 = id0;
+ c1 = (polyData1.mSelectClosestEdgeCB)(polyData1, scaling1, world1.rotateTranspose(-worldNormal));
+ }
+ else if(code==SA_NORMAL1)
+ {
+ c0 = (polyData0.mSelectClosestEdgeCB)(polyData0, scaling0, world0.rotateTranspose(worldNormal));
+ c1 = id1;
+ }
+ else if(code==SA_EE)
+ {
+ c0 = (polyData0.mSelectClosestEdgeCB)(polyData0, scaling0, world0.rotateTranspose(worldNormal));
+ c1 = (polyData1.mSelectClosestEdgeCB)(polyData1, scaling1, world1.rotateTranspose(-worldNormal));
+ }
+ }
+
+ const Gu::HullPolygonData& HP0 = polyData0.mPolygons[c0];
+ const Gu::HullPolygonData& HP1 = polyData1.mPolygons[c1];
+ // PT: prefetching those guys saves ~600.000 cycles in convex-convex benchmark
+ Ps::prefetchLine(&HP0.mPlane);
+ Ps::prefetchLine(&HP1.mPlane);
+
+ //ok, we have a new depth value. convert to real distance.
+// PxReal separation = -depth; //depth was either computed in initial cached-axis check, or better, when skipIt was false, in sep axis search.
+// if (separation < 0.0f)
+// separation = 0.0f; //we don't want to store penetration values.
+ const PxReal separation = fsel(depth, 0.0f, -depth);
+
+ PxVec3 worldNormal0;
+ PX_ALIGN(16, PxPlane) shapeSpacePlane0;
+ if(idtScale0)
+ {
+ V4StoreA(V4LoadU(&HP0.mPlane.n.x), &shapeSpacePlane0.n.x);
+ worldNormal0 = world0.rotate(HP0.mPlane.n);
+ }
+ else
+ {
+ scaling0.transformPlaneToShapeSpace(HP0.mPlane.n,HP0.mPlane.d,shapeSpacePlane0.n,shapeSpacePlane0.d);
+ worldNormal0 = world0.rotate(shapeSpacePlane0.n);
+ }
+
+ PxVec3 worldNormal1;
+ PX_ALIGN(16, PxPlane) shapeSpacePlane1;
+ if(idtScale1)
+ {
+ V4StoreA(V4LoadU(&HP1.mPlane.n.x), &shapeSpacePlane1.n.x);
+ worldNormal1 = world1.rotate(HP1.mPlane.n);
+ }
+ else
+ {
+ scaling1.transformPlaneToShapeSpace(HP1.mPlane.n,HP1.mPlane.d,shapeSpacePlane1.n,shapeSpacePlane1.d);
+ worldNormal1 = world1.rotate(shapeSpacePlane1.n);
+ }
+
+ Ps::IntBool flag;
+ {
+ const PxReal d0 = PxAbs(worldNormal0.dot(worldNormal));
+ const PxReal d1 = PxAbs(worldNormal1.dot(worldNormal));
+ bool f = d0>d1; //which face normal is the separating axis closest to.
+ flag = f;
+ }
+
+////////////////////NEW DIST HANDLING//////////////////////
+ PX_ASSERT(separation >= 0.0f); //be sure this got fetched somewhere in the chaos above.
+
+ const PxReal cCCDEpsilon = params.mMeshContactMargin;
+ const PxReal contactGenPositionShift = separation + cCCDEpsilon; //if we're at a distance, shift so we're in penetration.
+
+ const PxVec3 contactGenPositionShiftVec = worldNormal * -contactGenPositionShift; //shift one of the bodies this distance toward the other just for Pierre's contact generation. Then the bodies should be penetrating exactly by MIN_SEPARATION_FOR_PENALTY - ideal conditions for this contact generator.
+
+ //note: for some reason this has to change sign!
+
+ //this will make contact gen always generate contacts at about MSP. Shift them back to the true real distance, and then to a solver compliant distance given that
+ //the solver converges to MSP penetration, while we want it to converge to 0 penetration.
+ //to real distance:
+
+ //The system: We always shift convex 0 (arbitrary). If the contact is attached to convex 0 then we will need to shift the contact point, otherwise not.
+ const PxVec3 newp = world0.p - contactGenPositionShiftVec;
+ const Cm::Matrix34 world0_Tweaked(world0.m.column0, world0.m.column1, world0.m.column2, newp);
+ PxTransform shifted0(newp, transform0.q);
+
+ t0to1 = transform1.transformInv(shifted0);
+ t1to0 = shifted0.transformInv(transform1);
+ Cm::Matrix34 m0to1_Tweaked;
+ Cm::Matrix34 m1to0_Tweaked;
+ m0to1_Tweaked.set(t0to1.q); m0to1_Tweaked.p = t0to1.p;
+ m1to0_Tweaked.set(t1to0.q); m1to0_Tweaked.p = t1to0.p;
+
+//////////////////////////////////////////////////
+ //pretransform convex polygon if we have scaling!
+ PxVec3* scaledVertices0;
+ PxU8* stackIndices0;
+ GET_SCALEX_CONVEX(scaledVertices0, stackIndices0, idtScale0, HP0.mNbVerts, scaling0, polyData0.mVerts, polyData0.getPolygonVertexRefs(HP0))
+
+ //pretransform convex polygon if we have scaling!
+ PxVec3* scaledVertices1;
+ PxU8* stackIndices1;
+ GET_SCALEX_CONVEX(scaledVertices1, stackIndices1, idtScale1, HP1.mNbVerts, scaling1, polyData1.mVerts, polyData1.getPolygonVertexRefs(HP1))
+
+ // So we need to switch:
+ // - HP0, HP1
+ // - scaledVertices0, scaledVertices1
+ // - stackIndices0, stackIndices1
+ // - world0, world1
+ // - shapeSpacePlane0, shapeSpacePlane1
+ // - worldNormal0, worldNormal1
+ // - m0to1, m1to0
+ // - true, false
+ const PxMat33 RotT0 = findRotationMatrixFromZ(shapeSpacePlane0.n);
+ const PxMat33 RotT1 = findRotationMatrixFromZ(shapeSpacePlane1.n);
+
+ if(flag)
+ {
+ if(contactPolygonPolygonExt(HP0.mNbVerts, scaledVertices0, stackIndices0, world0_Tweaked, shapeSpacePlane0, RotT0,
+ HP1.mNbVerts, scaledVertices1, stackIndices1, world1, shapeSpacePlane1, RotT1,
+ worldNormal0, m0to1_Tweaked, m1to0_Tweaked,
+ PXC_CONTACT_NO_FACE_INDEX, PXC_CONTACT_NO_FACE_INDEX,
+ contactBuffer,
+ true, contactGenPositionShiftVec, contactGenPositionShift))
+ return true;
+ }
+ else
+ {
+ if(contactPolygonPolygonExt(HP1.mNbVerts, scaledVertices1, stackIndices1, world1, shapeSpacePlane1, RotT1,
+ HP0.mNbVerts, scaledVertices0, stackIndices0, world0_Tweaked, shapeSpacePlane0, RotT0,
+ worldNormal1, m1to0_Tweaked, m0to1_Tweaked,
+ PXC_CONTACT_NO_FACE_INDEX, PXC_CONTACT_NO_FACE_INDEX,
+ contactBuffer,
+ false, contactGenPositionShiftVec, contactGenPositionShift))
+ return true;
+ }
+
+#ifdef CONVEX_CONVEX_ROUGH_FIRST_PASS
+ }
+#endif
+ return false;
+}
diff --git a/PhysX_3.4/Source/GeomUtils/src/contact/GuContactConvexMesh.cpp b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactConvexMesh.cpp
new file mode 100644
index 00000000..58142f46
--- /dev/null
+++ b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactConvexMesh.cpp
@@ -0,0 +1,1449 @@
+// 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 "GuConvexUtilsInternal.h"
+#include "GuInternal.h"
+#include "GuContactPolygonPolygon.h"
+#include "GuConvexEdgeFlags.h"
+#include "GuSeparatingAxes.h"
+#include "GuContactMethodImpl.h"
+#include "GuContactBuffer.h"
+#include "GuMidphaseInterface.h"
+#include "GuConvexHelper.h"
+#include "GuTriangleCache.h"
+#include "GuHeightFieldUtil.h"
+#include "GuEntityReport.h"
+#include "GuGeometryUnion.h"
+#include "GuIntersectionTriangleBox.h"
+#include "CmUtils.h"
+#include "PsAllocator.h"
+#include "GuBox.h"
+#include "PsFPU.h"
+
+using namespace physx;
+using namespace Gu;
+using namespace shdfnd::aos;
+using namespace intrinsics;
+
+#define LOCAL_TOUCHED_TRIG_SIZE 192
+
+//#define USE_TRIANGLE_NORMAL
+#define TEST_INTERNAL_OBJECTS
+
+static PX_FORCE_INLINE void projectTriangle(const PxVec3& localSpaceDirection, const PxVec3* PX_RESTRICT triangle, PxReal& min1, PxReal& max1)
+{
+ const PxReal dp0 = triangle[0].dot(localSpaceDirection);
+ const PxReal dp1 = triangle[1].dot(localSpaceDirection);
+ min1 = selectMin(dp0, dp1);
+ max1 = selectMax(dp0, dp1);
+
+ const PxReal dp2 = triangle[2].dot(localSpaceDirection);
+ min1 = selectMin(min1, dp2);
+ max1 = selectMax(max1, dp2);
+}
+
+#ifdef TEST_INTERNAL_OBJECTS
+
+static PX_FORCE_INLINE void boxSupport(const float extents[3], const PxVec3& sv, float p[3])
+{
+ const PxU32* iextents = reinterpret_cast<const PxU32*>(extents);
+ const PxU32* isv = reinterpret_cast<const PxU32*>(&sv);
+ PxU32* ip = reinterpret_cast<PxU32*>(p);
+
+ ip[0] = iextents[0]|(isv[0]&PX_SIGN_BITMASK);
+ ip[1] = iextents[1]|(isv[1]&PX_SIGN_BITMASK);
+ ip[2] = iextents[2]|(isv[2]&PX_SIGN_BITMASK);
+}
+
+#if PX_DEBUG
+static const PxReal testInternalObjectsEpsilon = 1.0e-3f;
+#endif
+
+static PX_FORCE_INLINE bool testInternalObjects(const PxVec3& localAxis0,
+ const PolygonalData& polyData0,
+ const PxVec3* PX_RESTRICT triangleInHullSpace,
+ float dmin)
+{
+ PxReal min1, max1;
+ projectTriangle(localAxis0, triangleInHullSpace, min1, max1);
+
+ const float dp = polyData0.mCenter.dot(localAxis0);
+
+ float p0[3];
+ boxSupport(polyData0.mInternal.mExtents, localAxis0, p0);
+ const float Radius0 = p0[0]*localAxis0.x + p0[1]*localAxis0.y + p0[2]*localAxis0.z;
+ const float bestRadius = selectMax(Radius0, polyData0.mInternal.mRadius);
+ const PxReal min0 = dp - bestRadius;
+ const PxReal max0 = dp + bestRadius;
+
+ const PxReal d0 = max0 - min1;
+ const PxReal d1 = max1 - min0;
+
+ const float depth = selectMin(d0, d1);
+ if(depth>dmin)
+ return false;
+ return true;
+}
+#endif
+
+static PX_FORCE_INLINE bool testNormal( const PxVec3& sepAxis, PxReal min0, PxReal max0,
+ const PxVec3* PX_RESTRICT triangle,
+ PxReal& depth, PxReal contactDistance)
+{
+ PxReal min1, max1;
+ projectTriangle(sepAxis, triangle, min1, max1);
+
+ if(max0+contactDistance<min1 || max1+contactDistance<min0)
+ return false;
+
+ const PxReal d0 = max0 - min1;
+ const PxReal d1 = max1 - min0;
+ depth = selectMin(d0, d1);
+ return true;
+}
+
+static PX_FORCE_INLINE bool testSepAxis(const PxVec3& sepAxis,
+ const PolygonalData& polyData0,
+ const PxVec3* PX_RESTRICT triangle,
+ const Cm::Matrix34& m0to1,
+ const Cm::FastVertex2ShapeScaling& convexScaling,
+ PxReal& depth, PxReal contactDistance)
+{
+ PxReal min0, max0;
+ (polyData0.mProjectHull)(polyData0, sepAxis, m0to1, convexScaling, min0, max0);
+
+ PxReal min1, max1;
+ projectTriangle(sepAxis, triangle, min1, max1);
+
+ if(max0+contactDistance < min1 || max1+contactDistance < min0)
+ return false;
+
+ const PxReal d0 = max0 - min1;
+ const PxReal d1 = max1 - min0;
+ depth = selectMin(d0, d1);
+ return true;
+}
+
+static bool testFacesSepAxesBackface( const PolygonalData& polyData0,
+ const Cm::Matrix34& /*world0*/, const Cm::Matrix34& /*world1*/,
+ const Cm::Matrix34& m0to1, const PxVec3& witness,
+ const PxVec3* PX_RESTRICT triangle,
+ const Cm::FastVertex2ShapeScaling& convexScaling,
+ PxU32& numHullIndices,
+ PxU32* hullIndices_, PxReal& dmin, PxVec3& sep, PxU32& id, PxReal contactDistance,
+ bool idtConvexScale
+ )
+{
+ id = PX_INVALID_U32;
+
+ const PxU32 numHullPolys = polyData0.mNbPolygons;
+ const HullPolygonData* PX_RESTRICT polygons = polyData0.mPolygons;
+ const PxVec3* PX_RESTRICT vertices = polyData0.mVerts;
+ const PxVec3& trans = m0to1.p;
+ {
+ PxU32* hullIndices = hullIndices_;
+
+ // PT: when the center of one object is inside the other object (deep penetrations) this discards everything!
+ // PT: when this happens, the backup procedure is used to come up with good results anyway.
+ // PT: it's worth having a special codepath here for identity scales, to skip all the normalizes/division. Lot faster without.
+
+ if(idtConvexScale)
+ {
+ for(PxU32 i=0; i<numHullPolys; i++)
+ {
+ const HullPolygonData& P = polygons[i];
+ const PxPlane& PL = P.mPlane;
+
+#ifdef USE_TRIANGLE_NORMAL
+ if(PL.normal.dot(witness) > 0.0f)
+#else
+ // ### this is dubious since the triangle center is likely to be very close to the hull, if not inside. Why not use the triangle normal?
+ if(PL.distance(witness) < 0.0f)
+#endif
+ continue; //backface culled
+
+ *hullIndices++ = i;
+
+ const PxVec3 sepAxis = m0to1.rotate(PL.n);
+ const PxReal dp = sepAxis.dot(trans);
+
+ PxReal d;
+ if(!testNormal(sepAxis, P.getMin(vertices) + dp, P.getMax() + dp, triangle,
+ d, contactDistance))
+ return false;
+
+ if(d < dmin)
+ {
+ dmin = d;
+ sep = sepAxis;
+ id = i;
+ }
+ }
+ }
+ else
+ {
+#ifndef USE_TRIANGLE_NORMAL
+ //transform delta from hull0 shape into vertex space:
+ const PxVec3 vertSpaceWitness = convexScaling % witness;
+#endif
+ for(PxU32 i=0; i<numHullPolys; i++)
+ {
+ const HullPolygonData& P = polygons[i];
+ const PxPlane& PL = P.mPlane;
+
+#ifdef USE_TRIANGLE_NORMAL
+ if(PL.normal.dot(witness) > 0.0f)
+#else
+ // ### this is dubious since the triangle center is likely to be very close to the hull, if not inside. Why not use the triangle normal?
+ if(PL.distance(vertSpaceWitness) < 0.0f)
+#endif
+ continue; //backface culled
+
+ //normals transform by inverse transpose: (and transpose(skew) == skew as its symmetric)
+ PxVec3 shapeSpaceNormal = convexScaling % PL.n;
+ //renormalize: (Arr!)
+ const PxReal magnitude = shapeSpaceNormal.normalize();
+
+ *hullIndices++ = i;
+ const PxVec3 sepAxis = m0to1.rotate(shapeSpaceNormal);
+ PxReal d;
+ const PxReal dp = sepAxis.dot(trans);
+
+ const float oneOverM = 1.0f / magnitude;
+ if(!testNormal(sepAxis, P.getMin(vertices) * oneOverM + dp, P.getMax() * oneOverM + dp, triangle,
+ d, contactDistance))
+ return false;
+
+ if(d < dmin)
+ {
+ dmin = d;
+ sep = sepAxis;
+ id = i;
+ }
+ }
+ }
+ numHullIndices = PxU32(hullIndices - hullIndices_);
+ }
+
+ // Backup
+ if(id == PX_INVALID_U32)
+ {
+ if(idtConvexScale)
+ {
+ for(PxU32 i=0; i<numHullPolys; i++)
+ {
+ const HullPolygonData& P = polygons[i];
+ const PxPlane& PL = P.mPlane;
+
+ const PxVec3 sepAxis = m0to1.rotate(PL.n);
+ const PxReal dp = sepAxis.dot(trans);
+
+ PxReal d;
+ if(!testNormal(sepAxis, P.getMin(vertices) + dp, P.getMax() + dp, triangle,
+ d, contactDistance))
+ return false;
+
+ if(d < dmin)
+ {
+ dmin = d;
+ sep = sepAxis;
+ id = i;
+ }
+ hullIndices_[i] = i;
+ }
+ }
+ else
+ {
+ for(PxU32 i=0; i<numHullPolys; i++)
+ {
+ const HullPolygonData& P = polygons[i];
+ const PxPlane& PL = P.mPlane;
+
+ PxVec3 shapeSpaceNormal = convexScaling % PL.n;
+ //renormalize: (Arr!)
+ const PxReal magnitude = shapeSpaceNormal.normalize();
+
+ const PxVec3 sepAxis = m0to1.rotate(shapeSpaceNormal);
+
+ PxReal d;
+ const PxReal dp = sepAxis.dot(trans);
+
+ const float oneOverM = 1.0f / magnitude;
+ if(!testNormal(sepAxis, P.getMin(vertices) * oneOverM + dp, P.getMax() * oneOverM + dp, triangle,
+ d, contactDistance))
+ return false;
+
+ if(d < dmin)
+ {
+ dmin = d;
+ sep = sepAxis;
+ id = i;
+ }
+ hullIndices_[i] = i;
+ }
+ }
+ numHullIndices = numHullPolys;
+ }
+ return true;
+}
+
+static PX_FORCE_INLINE bool edgeCulling(const PxPlane& plane, const PxVec3& p0, const PxVec3& p1, PxReal contactDistance)
+{
+ return plane.distance(p0)<=contactDistance || plane.distance(p1)<=contactDistance;
+}
+
+static bool performEETests(
+ const PolygonalData& polyData0,
+ const PxU8 triFlags,
+ const Cm::Matrix34& m0to1, const Cm::Matrix34& m1to0,
+ const PxVec3* PX_RESTRICT triangle,
+ PxU32 numHullIndices, const PxU32* PX_RESTRICT hullIndices,
+ const PxPlane& localTriPlane,
+ const Cm::FastVertex2ShapeScaling& convexScaling,
+ PxVec3& vec, PxReal& dmin, PxReal contactDistance, PxReal toleranceLength,
+ PxU32 id0, PxU32 /*triangleIndex*/)
+{
+ PX_UNUSED(toleranceLength); // Only used in Debug
+
+ // Cull candidate triangle edges vs to hull plane
+ PxU32 nbTriangleAxes = 0;
+ PxVec3 triangleAxes[3];
+ {
+ const HullPolygonData& P = polyData0.mPolygons[id0];
+ const PxPlane& vertSpacePlane = P.mPlane;
+
+ const PxVec3 newN = m1to0.rotate(vertSpacePlane.n);
+ PxPlane hullWitness(convexScaling * newN, vertSpacePlane.d - m1to0.p.dot(newN)); //technically not a fully xformed plane, just use property of x|My == Mx|y for symmetric M.
+
+ if((triFlags & ETD_CONVEX_EDGE_01) && edgeCulling(hullWitness, triangle[0], triangle[1], contactDistance))
+ triangleAxes[nbTriangleAxes++] = (triangle[0] - triangle[1]);
+
+ if((triFlags & ETD_CONVEX_EDGE_12) && edgeCulling(hullWitness, triangle[1], triangle[2], contactDistance))
+ triangleAxes[nbTriangleAxes++] = (triangle[1] - triangle[2]);
+
+ if((triFlags & ETD_CONVEX_EDGE_20) && edgeCulling(hullWitness, triangle[2], triangle[0], contactDistance))
+ triangleAxes[nbTriangleAxes++] = (triangle[2] - triangle[0]);
+ }
+
+ //PxcPlane vertexSpacePlane = localTriPlane.getTransformed(m1to0);
+ //vertexSpacePlane.normal = convexScaling * vertexSpacePlane.normal; //technically not a fully xformed plane, just use property of x|My == Mx|y for symmetric M.
+ const PxVec3 newN = m1to0.rotate(localTriPlane.n);
+ PxPlane vertexSpacePlane(convexScaling * newN, localTriPlane.d - m1to0.p.dot(newN));
+
+ const PxVec3* PX_RESTRICT hullVerts = polyData0.mVerts;
+
+ SeparatingAxes SA;
+ SA.reset();
+
+ const PxU8* PX_RESTRICT vrefBase0 = polyData0.mPolygonVertexRefs;
+ const HullPolygonData* PX_RESTRICT polygons = polyData0.mPolygons;
+ while(numHullIndices--)
+ {
+ const HullPolygonData& P = polygons[*hullIndices++];
+ const PxU8* PX_RESTRICT data = vrefBase0 + P.mVRef8;
+
+ PxU32 numEdges = nbTriangleAxes;
+ const PxVec3* edges = triangleAxes;
+
+ // TODO: cheap edge culling as in convex/convex!
+ while(numEdges--)
+ {
+ const PxVec3& currentPolyEdge = *edges++;
+
+ // Loop through polygon vertices == polygon edges;
+ PxU32 numVerts = P.mNbVerts;
+ for(PxU32 j = 0; j < numVerts; j++)
+ {
+ PxU32 j1 = j+1;
+ if(j1>=numVerts) j1 = 0;
+
+ const PxU32 VRef0 = data[j];
+ const PxU32 VRef1 = data[j1];
+
+ if(edgeCulling(vertexSpacePlane, hullVerts[VRef0], hullVerts[VRef1], contactDistance))
+ {
+ const PxVec3 currentHullEdge = m0to1.rotate(convexScaling * (hullVerts[VRef0] - hullVerts[VRef1])); //matrix mult is distributive!
+
+ PxVec3 sepAxis = currentHullEdge.cross(currentPolyEdge);
+ if(!Ps::isAlmostZero(sepAxis))
+ SA.addAxis(sepAxis.getNormalized());
+ }
+ }
+ }
+ }
+
+ dmin = PX_MAX_REAL;
+ PxU32 numAxes = SA.getNumAxes();
+ const PxVec3* PX_RESTRICT axes = SA.getAxes();
+
+#ifdef TEST_INTERNAL_OBJECTS
+ PxVec3 triangleInHullSpace[3];
+ if(numAxes)
+ {
+ triangleInHullSpace[0] = m1to0.transform(triangle[0]);
+ triangleInHullSpace[1] = m1to0.transform(triangle[1]);
+ triangleInHullSpace[2] = m1to0.transform(triangle[2]);
+ }
+#endif
+
+ while(numAxes--)
+ {
+ const PxVec3& currentAxis = *axes++;
+
+#ifdef TEST_INTERNAL_OBJECTS
+ const PxVec3 localAxis0 = m1to0.rotate(currentAxis);
+ if(!testInternalObjects(localAxis0, polyData0, triangleInHullSpace, dmin))
+ {
+ #if PX_DEBUG
+ PxReal dtest;
+ if(testSepAxis(currentAxis, polyData0, triangle, m0to1, convexScaling, dtest, contactDistance))
+ {
+ PX_ASSERT(dtest + testInternalObjectsEpsilon*toleranceLength >= dmin);
+ }
+ #endif
+ continue;
+ }
+#endif
+
+ PxReal d;
+ if(!testSepAxis(currentAxis, polyData0, triangle,
+ m0to1, convexScaling, d, contactDistance))
+ {
+ return false;
+ }
+
+ if(d < dmin)
+ {
+ dmin = d;
+ vec = currentAxis;
+ }
+ }
+ return true;
+}
+
+static bool triangleConvexTest( const PolygonalData& polyData0,
+ const PxU8 triFlags,
+ PxU32 index, const PxVec3* PX_RESTRICT localPoints,
+ const PxPlane& localPlane,
+ const PxVec3& groupCenterHull,
+ const Cm::Matrix34& world0, const Cm::Matrix34& world1, const Cm::Matrix34& m0to1, const Cm::Matrix34& m1to0,
+ const Cm::FastVertex2ShapeScaling& convexScaling,
+ PxReal contactDistance, PxReal toleranceLength,
+ PxVec3& groupAxis, PxReal& groupMinDepth, bool& faceContact,
+ bool idtConvexScale
+ )
+{
+ PxU32 id0 = PX_INVALID_U32;
+ PxReal dmin0 = PX_MAX_REAL;
+ PxVec3 vec0;
+
+ PxU32 numHullIndices = 0;
+ PxU32* PX_RESTRICT const hullIndices = reinterpret_cast<PxU32*>(PxAlloca(polyData0.mNbPolygons*sizeof(PxU32)));
+
+ // PT: we test the hull normals first because they don't need any hull projection. If we can early exit thanks
+ // to those, we completely avoid all hull projections.
+ bool status = testFacesSepAxesBackface(polyData0, world0, world1, m0to1, groupCenterHull, localPoints,
+ convexScaling, numHullIndices, hullIndices, dmin0, vec0, id0, contactDistance, idtConvexScale);
+ if(!status)
+ return false;
+
+ groupAxis = PxVec3(0);
+ groupMinDepth = PX_MAX_REAL;
+
+ const PxReal eps = 0.0001f; // DE7748
+
+ //Test in mesh-space
+ PxVec3 sepAxis;
+ PxReal depth;
+
+ {
+ // Test triangle normal
+ PxReal d;
+ if(!testSepAxis(localPlane.n, polyData0, localPoints,
+ m0to1, convexScaling, d, contactDistance))
+ return false;
+
+ if(d<dmin0+eps)
+// if(d<dmin0)
+ {
+ depth = d;
+ sepAxis = localPlane.n;
+ faceContact = true;
+ }
+ else
+ {
+ depth = dmin0;
+ sepAxis = vec0;
+ faceContact = false;
+ }
+ }
+
+ if(depth < groupMinDepth)
+ {
+ groupMinDepth = depth;
+ groupAxis = world1.rotate(sepAxis);
+ }
+
+ if(!performEETests(polyData0, triFlags, m0to1, m1to0,
+ localPoints,
+ numHullIndices, hullIndices, localPlane,
+ convexScaling, sepAxis, depth, contactDistance, toleranceLength,
+ id0, index))
+ return false;
+
+ if(depth < groupMinDepth)
+ {
+ groupMinDepth = depth;
+ groupAxis = world1.rotate(sepAxis);
+ faceContact = false;
+ }
+
+ return true;
+}
+
+namespace
+{
+ struct ConvexMeshContactGeneration
+ {
+ Ps::InlineArray<PxU32,LOCAL_CONTACTS_SIZE>& mDelayedContacts;
+ Gu::CacheMap<Gu::CachedEdge, 128> mEdgeCache;
+ Gu::CacheMap<Gu::CachedVertex, 128> mVertCache;
+
+ const Cm::Matrix34 m0to1;
+ const Cm::Matrix34 m1to0;
+
+ PxVec3 mHullCenterMesh;
+ PxVec3 mHullCenterWorld;
+
+ const PolygonalData& mPolyData0;
+ const Cm::Matrix34& mWorld0;
+ const Cm::Matrix34& mWorld1;
+
+ const Cm::FastVertex2ShapeScaling& mConvexScaling;
+
+ PxReal mContactDistance;
+ PxReal mToleranceLength;
+ bool mIdtMeshScale, mIdtConvexScale;
+ PxReal mCCDEpsilon;
+ const PxTransform& mTransform0;
+ const PxTransform& mTransform1;
+ ContactBuffer& mContactBuffer;
+ bool mAnyHits;
+
+ ConvexMeshContactGeneration(
+ Ps::InlineArray<PxU32,LOCAL_CONTACTS_SIZE>& delayedContacts,
+ const PxTransform& t0to1, const PxTransform& t1to0,
+ const PolygonalData& polyData0, const Cm::Matrix34& world0, const Cm::Matrix34& world1,
+ const Cm::FastVertex2ShapeScaling& convexScaling,
+ PxReal contactDistance,
+ PxReal toleranceLength,
+ bool idtConvexScale,
+ PxReal cCCDEpsilon,
+ const PxTransform& transform0, const PxTransform& transform1,
+ ContactBuffer& contactBuffer
+ );
+
+ void processTriangle(const PxVec3* verts, PxU32 triangleIndex, PxU8 triFlags, const PxU32* vertInds);
+ void generateLastContacts();
+
+ bool generateContacts(
+ const PxPlane& localPlane,
+ const PxVec3* PX_RESTRICT localPoints,
+ const PxVec3& triCenter, PxVec3& groupAxis,
+ PxReal groupMinDepth, PxU32 index) const;
+
+ private:
+ ConvexMeshContactGeneration& operator=(const ConvexMeshContactGeneration&);
+ };
+
+// 17 entries. 1088/17 = 64 triangles in the local array
+struct SavedContactData
+{
+ PxU32 mTriangleIndex;
+ PxVec3 mVerts[3];
+ PxU32 mInds[3];
+ PxVec3 mGroupAxis;
+ PxReal mGroupMinDepth;
+};
+}
+
+ConvexMeshContactGeneration::ConvexMeshContactGeneration(
+ Ps::InlineArray<PxU32,LOCAL_CONTACTS_SIZE>& delayedContacts,
+ const PxTransform& t0to1, const PxTransform& t1to0,
+ const PolygonalData& polyData0, const Cm::Matrix34& world0, const Cm::Matrix34& world1,
+ const Cm::FastVertex2ShapeScaling& convexScaling,
+ PxReal contactDistance,
+ PxReal toleranceLength,
+ bool idtConvexScale,
+ PxReal cCCDEpsilon,
+ const PxTransform& transform0, const PxTransform& transform1,
+ ContactBuffer& contactBuffer
+) :
+ mDelayedContacts(delayedContacts),
+ m0to1 (t0to1),
+ m1to0 (t1to0),
+ mPolyData0 (polyData0),
+ mWorld0 (world0),
+ mWorld1 (world1),
+ mConvexScaling (convexScaling),
+ mContactDistance(contactDistance),
+ mToleranceLength(toleranceLength),
+ mIdtConvexScale (idtConvexScale),
+ mCCDEpsilon (cCCDEpsilon),
+ mTransform0 (transform0),
+ mTransform1 (transform1),
+ mContactBuffer (contactBuffer)
+{
+ delayedContacts.forceSize_Unsafe(0);
+ mAnyHits = false;
+
+ // Hull center in local space
+ const PxVec3& hullCenterLocal = mPolyData0.mCenter;
+ // Hull center in mesh space
+ mHullCenterMesh = m0to1.transform(hullCenterLocal);
+ // Hull center in world space
+ mHullCenterWorld = mWorld0.transform(hullCenterLocal);
+}
+
+struct ConvexMeshContactGenerationCallback : MeshHitCallback<PxRaycastHit>
+{
+ ConvexMeshContactGeneration mGeneration;
+ const Cm::FastVertex2ShapeScaling& mMeshScaling;
+ const PxU8* PX_RESTRICT mExtraTrigData;
+ bool mIdtMeshScale;
+ const TriangleMesh* mMeshData;
+ const BoxPadded& mBox;
+
+ ConvexMeshContactGenerationCallback(
+ Ps::InlineArray<PxU32,LOCAL_CONTACTS_SIZE>& delayedContacts,
+ const PxTransform& t0to1, const PxTransform& t1to0,
+ const PolygonalData& polyData0, const Cm::Matrix34& world0, const Cm::Matrix34& world1,
+ const TriangleMesh* meshData,
+ const PxU8* PX_RESTRICT extraTrigData,
+ const Cm::FastVertex2ShapeScaling& meshScaling,
+ const Cm::FastVertex2ShapeScaling& convexScaling,
+ PxReal contactDistance,
+ PxReal toleranceLength,
+ bool idtMeshScale, bool idtConvexScale,
+ PxReal cCCDEpsilon,
+ const PxTransform& transform0, const PxTransform& transform1,
+ ContactBuffer& contactBuffer,
+ const BoxPadded& box
+ ) :
+ MeshHitCallback<PxRaycastHit> (CallbackMode::eMULTIPLE),
+ mGeneration (delayedContacts, t0to1, t1to0, polyData0, world0, world1, convexScaling, contactDistance, toleranceLength, idtConvexScale, cCCDEpsilon, transform0, transform1, contactBuffer),
+ mMeshScaling (meshScaling),
+ mExtraTrigData (extraTrigData),
+ mIdtMeshScale (idtMeshScale),
+ mMeshData (meshData),
+ mBox (box)
+ {
+ }
+
+ virtual PxAgain processHit( // all reported coords are in mesh local space including hit.position
+ const PxRaycastHit& hit, const PxVec3& v0, const PxVec3& v1, const PxVec3& v2, PxReal&, const PxU32* vinds)
+ {
+ // PT: this one is safe because incoming vertices from midphase are always safe to V4Load (by design)
+ // PT: TODO: is this test really needed? Not done in midphase already?
+ if(!intersectTriangleBox(mBox, v0, v1, v2))
+ return true;
+
+ PxVec3 verts[3];
+ getScaledVertices(verts, v0, v1, v2, mIdtMeshScale, mMeshScaling);
+
+ const PxU32 triangleIndex = hit.faceIndex;
+
+ const PxU8 extraData = getConvexEdgeFlags(mExtraTrigData, triangleIndex);
+ mGeneration.processTriangle(verts, triangleIndex, extraData, vinds);
+ return true;
+ }
+
+protected:
+ ConvexMeshContactGenerationCallback &operator=(const ConvexMeshContactGenerationCallback &);
+};
+
+bool ConvexMeshContactGeneration::generateContacts(
+ const PxPlane& localPlane,
+ const PxVec3* PX_RESTRICT localPoints,
+ const PxVec3& triCenter, PxVec3& groupAxis,
+ PxReal groupMinDepth, PxU32 index) const
+{
+ const PxVec3 worldGroupCenter = mWorld1.transform(triCenter);
+ const PxVec3 deltaC = mHullCenterWorld - worldGroupCenter;
+ if(deltaC.dot(groupAxis) < 0.0f)
+ groupAxis = -groupAxis;
+
+ const PxU32 id = (mPolyData0.mSelectClosestEdgeCB)(mPolyData0, mConvexScaling, mWorld0.rotateTranspose(-groupAxis));
+
+ const HullPolygonData& HP = mPolyData0.mPolygons[id];
+ PX_ALIGN(16, PxPlane) shapeSpacePlane0;
+ if(mIdtConvexScale)
+ V4StoreA(V4LoadU(&HP.mPlane.n.x), &shapeSpacePlane0.n.x);
+ else
+ mConvexScaling.transformPlaneToShapeSpace(HP.mPlane.n, HP.mPlane.d, shapeSpacePlane0.n, shapeSpacePlane0.d);
+
+ const PxVec3 hullNormalWorld = mWorld0.rotate(shapeSpacePlane0.n);
+
+ const PxReal d0 = PxAbs(hullNormalWorld.dot(groupAxis));
+
+ const PxVec3 triNormalWorld = mWorld1.rotate(localPlane.n);
+ const PxReal d1 = PxAbs(triNormalWorld.dot(groupAxis));
+ const bool d0biggerd1 = d0 > d1;
+
+////////////////////NEW DIST HANDLING//////////////////////
+ //TODO: skip this if there is no dist involved!
+
+PxReal separation = - groupMinDepth; //convert to real distance.
+
+separation = fsel(separation, separation, 0.0f); //don't do anything when penetrating!
+
+//printf("\nseparation = %f", separation);
+
+PxReal contactGenPositionShift = separation + mCCDEpsilon; //if we're at a distance, shift so we're within penetration.
+
+PxVec3 contactGenPositionShiftVec = groupAxis * contactGenPositionShift; //shift one of the bodies this distance toward the other just for Pierre's contact generation. Then the bodies should be penetrating exactly by MIN_SEPARATION_FOR_PENALTY - ideal conditions for this contact generator.
+
+//note: for some reason this has to change sign!
+
+//this will make contact gen always generate contacts at about MSP. Shift them back to the true real distance, and then to a solver compliant distance given that
+//the solver converges to MSP penetration, while we want it to converge to 0 penetration.
+//to real distance:
+// PxReal polyPolySeparationShift = separation; //(+ or - depending on which way normal goes)
+
+//The system: We always shift convex 0 (arbitrary). If the contact is attached to convex 0 then we will need to shift the contact point, otherwise not.
+
+//TODO: make these overwrite orig location if its safe to do so.
+
+Cm::Matrix34 world0_(mWorld0);
+PxTransform transform0_(mTransform0);
+
+world0_.p -= contactGenPositionShiftVec;
+transform0_.p = world0_.p; //reset this too.
+
+PxTransform t0to1_ = mTransform1.transformInv(transform0_);
+PxTransform t1to0_ = transform0_.transformInv(mTransform1);
+Cm::Matrix34 m0to1_(t0to1_);
+Cm::Matrix34 m1to0_(t1to0_);
+
+ PxVec3* scaledVertices0;
+ PxU8* stackIndices0;
+ GET_SCALEX_CONVEX(scaledVertices0, stackIndices0, mIdtConvexScale, HP.mNbVerts, mConvexScaling, mPolyData0.mVerts, mPolyData0.getPolygonVertexRefs(HP))
+
+ const PxU8 indices[3] = {0, 1, 2};
+
+ const PxMat33 RotT0 = findRotationMatrixFromZ(shapeSpacePlane0.n);
+ const PxMat33 RotT1 = findRotationMatrixFromZ(localPlane.n);
+
+ if(d0biggerd1)
+ {
+ if(contactPolygonPolygonExt(
+ HP.mNbVerts, scaledVertices0, stackIndices0, world0_, shapeSpacePlane0,
+ RotT0,
+ 3, localPoints, indices, mWorld1, localPlane,
+ RotT1,
+ hullNormalWorld, m0to1_, m1to0_, PXC_CONTACT_NO_FACE_INDEX, index,
+ mContactBuffer,
+ true,
+ contactGenPositionShiftVec, contactGenPositionShift))
+ {
+ return true;
+ }
+ }
+ else
+ {
+ if(contactPolygonPolygonExt(
+ 3, localPoints, indices, mWorld1, localPlane,
+ RotT1,
+ HP.mNbVerts, scaledVertices0, stackIndices0, world0_, shapeSpacePlane0,
+ RotT0,
+ triNormalWorld, m1to0_, m0to1_, PXC_CONTACT_NO_FACE_INDEX, index,
+ mContactBuffer,
+ false,
+ contactGenPositionShiftVec, contactGenPositionShift))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+enum FeatureCode
+{
+ FC_VERTEX0,
+ FC_VERTEX1,
+ FC_VERTEX2,
+ FC_EDGE01,
+ FC_EDGE12,
+ FC_EDGE20,
+ FC_FACE,
+
+ FC_UNDEFINED
+};
+
+static FeatureCode computeFeatureCode(const PxVec3& point, const PxVec3* verts)
+{
+ const PxVec3& triangleOrigin = verts[0];
+ const PxVec3 triangleEdge0 = verts[1] - verts[0];
+ const PxVec3 triangleEdge1 = verts[2] - verts[0];
+
+ const PxVec3 kDiff = triangleOrigin - point;
+ const PxReal fA00 = triangleEdge0.magnitudeSquared();
+ const PxReal fA01 = triangleEdge0.dot(triangleEdge1);
+ const PxReal fA11 = triangleEdge1.magnitudeSquared();
+ const PxReal fB0 = kDiff.dot(triangleEdge0);
+ const PxReal fB1 = kDiff.dot(triangleEdge1);
+ const PxReal fDet = PxAbs(fA00*fA11 - fA01*fA01);
+ const PxReal u = fA01*fB1-fA11*fB0;
+ const PxReal v = fA01*fB0-fA00*fB1;
+
+ FeatureCode fc = FC_UNDEFINED;
+
+ if(u + v <= fDet)
+ {
+ if(u < 0.0f)
+ {
+ if(v < 0.0f) // region 4
+ {
+ if(fB0 < 0.0f)
+ {
+ if(-fB0 >= fA00)
+ fc = FC_VERTEX1;
+ else
+ fc = FC_EDGE01;
+ }
+ else
+ {
+ if(fB1 >= 0.0f)
+ fc = FC_VERTEX0;
+ else if(-fB1 >= fA11)
+ fc = FC_VERTEX2;
+ else
+ fc = FC_EDGE20;
+ }
+ }
+ else // region 3
+ {
+ if(fB1 >= 0.0f)
+ fc = FC_VERTEX0;
+ else if(-fB1 >= fA11)
+ fc = FC_VERTEX2;
+ else
+ fc = FC_EDGE20;
+ }
+ }
+ else if(v < 0.0f) // region 5
+ {
+ if(fB0 >= 0.0f)
+ fc = FC_VERTEX0;
+ else if(-fB0 >= fA00)
+ fc = FC_VERTEX1;
+ else
+ fc = FC_EDGE01;
+ }
+ else // region 0
+ {
+ // minimum at interior PxVec3
+ if(fDet==0.0f)
+ fc = FC_VERTEX0;
+ else
+ fc = FC_FACE;
+ }
+ }
+ else
+ {
+ PxReal fTmp0, fTmp1, fNumer, fDenom;
+
+ if(u < 0.0f) // region 2
+ {
+ fTmp0 = fA01 + fB0;
+ fTmp1 = fA11 + fB1;
+ if(fTmp1 > fTmp0)
+ {
+ fNumer = fTmp1 - fTmp0;
+ fDenom = fA00-2.0f*fA01+fA11;
+ if(fNumer >= fDenom)
+ fc = FC_VERTEX1;
+ else
+ fc = FC_EDGE12;
+ }
+ else
+ {
+ if(fTmp1 <= 0.0f)
+ fc = FC_VERTEX2;
+ else if(fB1 >= 0.0f)
+ fc = FC_VERTEX0;
+ else
+ fc = FC_EDGE20;
+ }
+ }
+ else if(v < 0.0f) // region 6
+ {
+ fTmp0 = fA01 + fB1;
+ fTmp1 = fA00 + fB0;
+ if(fTmp1 > fTmp0)
+ {
+ fNumer = fTmp1 - fTmp0;
+ fDenom = fA00-2.0f*fA01+fA11;
+ if(fNumer >= fDenom)
+ fc = FC_VERTEX2;
+ else
+ fc = FC_EDGE12;
+ }
+ else
+ {
+ if(fTmp1 <= 0.0f)
+ fc = FC_VERTEX1;
+ else if(fB0 >= 0.0f)
+ fc = FC_VERTEX0;
+ else
+ fc = FC_EDGE01;
+ }
+ }
+ else // region 1
+ {
+ fNumer = fA11 + fB1 - fA01 - fB0;
+ if(fNumer <= 0.0f)
+ {
+ fc = FC_VERTEX2;
+ }
+ else
+ {
+ fDenom = fA00-2.0f*fA01+fA11;
+ if(fNumer >= fDenom)
+ fc = FC_VERTEX1;
+ else
+ fc = FC_EDGE12;
+ }
+ }
+ }
+ return fc;
+}
+
+
+//static bool validateVertex(PxU32 vref, const PxU32 count, const ContactPoint* PX_RESTRICT contacts, const TriangleMesh& meshData)
+//{
+// PxU32 previous = 0xffffffff;
+// for(PxU32 i=0;i<count;i++)
+// {
+// if(contacts[i].internalFaceIndex1==previous)
+// continue;
+// previous = contacts[i].internalFaceIndex1;
+//
+// const TriangleIndices T(meshData, contacts[i].internalFaceIndex1);
+// if( T.mVRefs[0]==vref
+// || T.mVRefs[1]==vref
+// || T.mVRefs[2]==vref)
+// return false;
+// }
+// return true;
+//}
+
+
+//static PX_FORCE_INLINE bool testEdge(PxU32 vref0, PxU32 vref1, PxU32 tvref0, PxU32 tvref1)
+//{
+// if(tvref0>tvref1)
+// Ps::swap(tvref0, tvref1);
+//
+// if(tvref0==vref0 && tvref1==vref1)
+// return false;
+// return true;
+//}
+
+//static bool validateEdge(PxU32 vref0, PxU32 vref1, const PxU32 count, const ContactPoint* PX_RESTRICT contacts, const TriangleMesh& meshData)
+//{
+// if(vref0>vref1)
+// Ps::swap(vref0, vref1);
+//
+// PxU32 previous = 0xffffffff;
+// for(PxU32 i=0;i<count;i++)
+// {
+// if(contacts[i].internalFaceIndex1==previous)
+// continue;
+// previous = contacts[i].internalFaceIndex1;
+//
+// const TriangleIndices T(meshData, contacts[i].internalFaceIndex1);
+//
+///* if(T.mVRefs[0]==vref0 || T.mVRefs[0]==vref1)
+// return false;
+// if(T.mVRefs[1]==vref0 || T.mVRefs[1]==vref1)
+// return false;
+// if(T.mVRefs[2]==vref0 || T.mVRefs[2]==vref1)
+// return false;*/
+// // PT: wow, this was wrong??? ###FIX
+// if(!testEdge(vref0, vref1, T.mVRefs[0], T.mVRefs[1]))
+// return false;
+// if(!testEdge(vref0, vref1, T.mVRefs[1], T.mVRefs[2]))
+// return false;
+// if(!testEdge(vref0, vref1, T.mVRefs[2], T.mVRefs[0]))
+// return false;
+// }
+// return true;
+//}
+
+//static bool validateEdge(PxU32 vref0, PxU32 vref1, const PxU32* vertIndices, const PxU32 nbIndices)
+//{
+// if(vref0>vref1)
+// Ps::swap(vref0, vref1);
+//
+// for(PxU32 i=0;i<nbIndices;i+=3)
+// {
+// if(!testEdge(vref0, vref1, vertIndices[i+0], vertIndices[i+1]))
+// return false;
+// if(!testEdge(vref0, vref1, vertIndices[i+1], vertIndices[i+2]))
+// return false;
+// if(!testEdge(vref0, vref1, vertIndices[i+2], vertIndices[i+0]))
+// return false;
+// }
+// return true;
+//}
+
+//#endif
+
+void ConvexMeshContactGeneration::processTriangle(const PxVec3* verts, PxU32 triangleIndex, PxU8 triFlags, const PxU32* vertInds)
+{
+ const PxPlane localPlane(verts[0], verts[1], verts[2]);
+
+ // Backface culling
+ if(localPlane.distance(mHullCenterMesh)<0.0f)
+// if(localPlane.normal.dot(mHullCenterMesh - T.mVerts[0]) <= 0.0f)
+ return;
+
+ //////////////////////////////////////////////////////////////////////////
+
+ const PxVec3 triCenter = (verts[0] + verts[1] + verts[2])*(1.0f/3.0f);
+
+ // Group center in hull space
+#ifdef USE_TRIANGLE_NORMAL
+ const PxVec3 groupCenterHull = m1to0.rotate(localPlane.normal);
+#else
+ const PxVec3 groupCenterHull = m1to0.transform(triCenter);
+#endif
+ //////////////////////////////////////////////////////////////////////////
+
+ PxVec3 groupAxis;
+ PxReal groupMinDepth;
+ bool faceContact;
+ if(!triangleConvexTest( mPolyData0, triFlags,
+ triangleIndex, verts,
+ localPlane,
+ groupCenterHull,
+ mWorld0, mWorld1, m0to1, m1to0,
+ mConvexScaling,
+ mContactDistance, mToleranceLength,
+ groupAxis, groupMinDepth, faceContact,
+ mIdtConvexScale
+ ))
+ return;
+
+ if(faceContact)
+ {
+ // PT: generate face contacts immediately to save memory & avoid recomputing triangle data later
+ if(generateContacts(
+ localPlane,
+ verts,
+ triCenter, groupAxis,
+ groupMinDepth, triangleIndex))
+ {
+ mAnyHits = true;
+ mEdgeCache.addData(CachedEdge(vertInds[0], vertInds[1]));
+ mEdgeCache.addData(CachedEdge(vertInds[0], vertInds[2]));
+ mEdgeCache.addData(CachedEdge(vertInds[1], vertInds[2]));
+ mVertCache.addData(CachedVertex(vertInds[0]));
+ mVertCache.addData(CachedVertex(vertInds[1]));
+ mVertCache.addData(CachedVertex(vertInds[2]));
+ }
+ else
+ {
+ int stop=1;
+ (void)stop;
+ }
+ }
+ else
+ {
+ const PxU32 nb = sizeof(SavedContactData)/sizeof(PxU32);
+
+ // PT: no "pushBack" please (useless data copy + LHS)
+ PxU32 newSize = nb + mDelayedContacts.size();
+ mDelayedContacts.reserve(newSize);
+ SavedContactData* PX_RESTRICT cd = reinterpret_cast<SavedContactData*>(mDelayedContacts.end());
+ mDelayedContacts.forceSize_Unsafe(newSize);
+
+ cd->mTriangleIndex = triangleIndex;
+ cd->mVerts[0] = verts[0];
+ cd->mVerts[1] = verts[1];
+ cd->mVerts[2] = verts[2];
+ cd->mInds[0] = vertInds[0];
+ cd->mInds[1] = vertInds[1];
+ cd->mInds[2] = vertInds[2];
+ cd->mGroupAxis = groupAxis;
+ cd->mGroupMinDepth = groupMinDepth;
+ }
+}
+
+void ConvexMeshContactGeneration::generateLastContacts()
+{
+ // Process delayed contacts
+ PxU32 nbEntries = mDelayedContacts.size();
+ if(nbEntries)
+ {
+ nbEntries /= sizeof(SavedContactData)/sizeof(PxU32);
+
+ // PT: TODO: replicate this fix in sphere-vs-mesh ###FIX
+ //const PxU32 count = mContactBuffer.count;
+ //const ContactPoint* PX_RESTRICT contacts = mContactBuffer.contacts;
+
+ const SavedContactData* PX_RESTRICT cd = reinterpret_cast<const SavedContactData*>(mDelayedContacts.begin());
+ for(PxU32 i=0;i<nbEntries;i++)
+ {
+ const SavedContactData& currentContact = cd[i];
+
+ const PxU32 triangleIndex = currentContact.mTriangleIndex;
+
+ // PT: unfortunately we must recompute this triangle-data here.
+ // PT: TODO: find a way not to
+// const TriangleVertices T(*mMeshData, mMeshScaling, triangleIndex);
+// const TriangleIndices T(*mMeshData, triangleIndex);
+
+ const PxU32 ref0 = currentContact.mInds[0];
+ const PxU32 ref1 = currentContact.mInds[1];
+ const PxU32 ref2 = currentContact.mInds[2];
+
+ // PT: TODO: why bother with the feature code at all? Use edge cache directly?
+// const FeatureCode FC = computeFeatureCode(mHullCenterMesh, T.mVerts);
+ const FeatureCode FC = computeFeatureCode(mHullCenterMesh, currentContact.mVerts);
+
+ bool generateContact = false;
+ switch(FC)
+ {
+ // PT: trying the same as in sphere-mesh here
+ case FC_VERTEX0:
+ generateContact = !mVertCache.contains(CachedVertex(ref0));
+ break;
+ case FC_VERTEX1:
+ generateContact =!mVertCache.contains(CachedVertex(ref1));
+ break;
+ case FC_VERTEX2:
+ generateContact = !mVertCache.contains(CachedVertex(ref2));
+ break;
+ case FC_EDGE01:
+ generateContact = !mEdgeCache.contains(CachedEdge(ref0, ref1));
+ break;
+ case FC_EDGE12:
+ generateContact = !mEdgeCache.contains(CachedEdge(ref1, ref2));
+ break;
+ case FC_EDGE20:
+ generateContact = !mEdgeCache.contains(CachedEdge(ref0, ref2));
+ break;
+ case FC_FACE:
+ generateContact = true;
+ break;
+ case FC_UNDEFINED:
+ break;
+ };
+
+ if(!generateContact)
+ continue;
+
+// const PxcPlane localPlane(T.mVerts[0], T.mVerts[1], T.mVerts[2]);
+ const PxPlane localPlane(currentContact.mVerts[0], currentContact.mVerts[1], currentContact.mVerts[2]);
+// const PxVec3 triCenter = (T.mVerts[0] + T.mVerts[1] + T.mVerts[2])*(1.0f/3.0f);
+ const PxVec3 triCenter = (currentContact.mVerts[0] + currentContact.mVerts[1] + currentContact.mVerts[2])*(1.0f/3.0f);
+
+ PxVec3 groupAxis = currentContact.mGroupAxis;
+ if(generateContacts(
+ localPlane,
+// T.mVerts,
+ currentContact.mVerts,
+ triCenter, groupAxis,
+ currentContact.mGroupMinDepth, triangleIndex))
+ {
+ mAnyHits = true;
+
+ //We don't add the edges to the data - this is important because we don't want to reject triangles
+ //because we generated an edge contact with an adjacent triangle
+ /*mEdgeCache.addData(CachedEdge(ref0, ref1));
+ mEdgeCache.addData(CachedEdge(ref0, ref2));
+ mEdgeCache.addData(CachedEdge(ref1, ref2));
+ mVertCache.addData(CachedVertex(ref0));
+ mVertCache.addData(CachedVertex(ref1));
+ mVertCache.addData(CachedVertex(ref2));*/
+
+ }
+ }
+ }
+}
+
+/////////////
+
+static bool contactHullMesh2(const PolygonalData& polyData0, const PxBounds3& hullAABB, const PxTriangleMeshGeometryLL& shape1,
+ const PxTransform& transform0, const PxTransform& transform1,
+ const NarrowPhaseParams& params, ContactBuffer& contactBuffer,
+ const Cm::FastVertex2ShapeScaling& convexScaling, const Cm::FastVertex2ShapeScaling& meshScaling,
+ bool idtConvexScale, bool idtMeshScale)
+{
+ //Just a sanity-check in debug-mode
+ PX_ASSERT(shape1.getType() == PxGeometryType::eTRIANGLEMESH);
+ ////////////////////
+
+ // Compute matrices
+ const Cm::Matrix34 world0(transform0);
+ const Cm::Matrix34 world1(transform1);
+
+ // Compute relative transforms
+ const PxTransform t0to1 = transform1.transformInv(transform0);
+ const PxTransform t1to0 = transform0.transformInv(transform1);
+
+ BoxPadded hullOBB;
+ computeHullOBB(hullOBB, hullAABB, params.mContactDistance, world0, world1, meshScaling, idtMeshScale);
+
+ // Setup the collider
+ const TriangleMesh* PX_RESTRICT meshData = shape1.meshData;
+
+ Ps::InlineArray<PxU32,LOCAL_CONTACTS_SIZE> delayedContacts;
+
+ ConvexMeshContactGenerationCallback blockCallback(
+ delayedContacts,
+ t0to1, t1to0, polyData0, world0, world1, meshData, meshData->getExtraTrigData(), meshScaling,
+ convexScaling, params.mContactDistance, params.mToleranceLength,
+ idtMeshScale, idtConvexScale, params.mMeshContactMargin,
+ transform0, transform1,
+ contactBuffer, hullOBB
+ );
+
+ Midphase::intersectOBB(meshData, hullOBB, blockCallback, false);
+
+ blockCallback.mGeneration.generateLastContacts();
+
+ return blockCallback.mGeneration.mAnyHits;
+}
+
+/////////////
+
+bool Gu::contactConvexMesh(GU_CONTACT_METHOD_ARGS)
+{
+ PX_UNUSED(cache);
+ PX_UNUSED(renderOutput);
+
+ const PxTriangleMeshGeometryLL& shapeMesh = shape1.get<const PxTriangleMeshGeometryLL>();
+
+ const bool idtScaleMesh = shapeMesh.scale.isIdentity();
+
+ Cm::FastVertex2ShapeScaling meshScaling;
+ if(!idtScaleMesh)
+ meshScaling.init(shapeMesh.scale);
+
+ Cm::FastVertex2ShapeScaling convexScaling;
+ PxBounds3 hullAABB;
+ PolygonalData polyData0;
+ const bool idtScaleConvex = getConvexData(shape0, convexScaling, hullAABB, polyData0);
+
+ return contactHullMesh2(polyData0, hullAABB, shapeMesh, transform0, transform1, params, contactBuffer, convexScaling, meshScaling, idtScaleConvex, idtScaleMesh);
+}
+
+bool Gu::contactBoxMesh(GU_CONTACT_METHOD_ARGS)
+{
+ PX_UNUSED(cache);
+ PX_UNUSED(renderOutput);
+
+ const PxBoxGeometry& shapeBox = shape0.get<const PxBoxGeometry>();
+ const PxTriangleMeshGeometryLL& shapeMesh = shape1.get<const PxTriangleMeshGeometryLL>();
+
+ PolygonalData polyData0;
+ PolygonalBox polyBox(shapeBox.halfExtents);
+ polyBox.getPolygonalData(&polyData0);
+
+ const PxBounds3 hullAABB(-shapeBox.halfExtents, shapeBox.halfExtents);
+
+ const bool idtScaleMesh = shapeMesh.scale.isIdentity();
+
+ Cm::FastVertex2ShapeScaling meshScaling;
+ if(!idtScaleMesh)
+ meshScaling.init(shapeMesh.scale);
+
+ Cm::FastVertex2ShapeScaling idtScaling;
+ return contactHullMesh2(polyData0, hullAABB, shapeMesh, transform0, transform1, params, contactBuffer, idtScaling, meshScaling, true, idtScaleMesh);
+}
+
+/////////////
+
+namespace
+{
+struct ConvexVsHeightfieldContactGenerationCallback : EntityReport<PxU32>
+{
+ ConvexMeshContactGeneration mGeneration;
+ HeightFieldUtil& mHfUtil;
+
+ ConvexVsHeightfieldContactGenerationCallback(
+ HeightFieldUtil& hfUtil,
+ Ps::InlineArray<PxU32,LOCAL_CONTACTS_SIZE>& delayedContacts,
+ const PxTransform& t0to1, const PxTransform& t1to0,
+ const PolygonalData& polyData0, const Cm::Matrix34& world0, const Cm::Matrix34& world1,
+ const Cm::FastVertex2ShapeScaling& convexScaling,
+ PxReal contactDistance,
+ PxReal toleranceLength,
+ bool idtConvexScale,
+ PxReal cCCDEpsilon,
+ const PxTransform& transform0, const PxTransform& transform1,
+ ContactBuffer& contactBuffer
+ ) : mGeneration(delayedContacts, t0to1, t1to0, polyData0, world0, world1, convexScaling, contactDistance, toleranceLength, idtConvexScale, cCCDEpsilon, transform0,
+ transform1, contactBuffer),
+ mHfUtil(hfUtil)
+ {
+ }
+
+ // PT: TODO: refactor/unify with similar code in other places
+ virtual bool onEvent(PxU32 nb, PxU32* indices)
+ {
+ const PxU8 nextInd[] = {2,0,1};
+
+ while(nb--)
+ {
+ const PxU32 triangleIndex = *indices++;
+
+ PxU32 vertIndices[3];
+ PxTriangle currentTriangle; // in world space
+ PxU32 adjInds[3];
+ mHfUtil.getTriangle(mGeneration.mTransform1, currentTriangle, vertIndices, adjInds, triangleIndex, false, false);
+
+ PxVec3 normal;
+ currentTriangle.normal(normal);
+
+ PxU8 triFlags = 0; //KS - temporary until we can calculate triFlags for HF
+
+ for(PxU32 a = 0; a < 3; ++a)
+ {
+ if(adjInds[a] != 0xFFFFFFFF)
+ {
+ PxTriangle adjTri;
+ mHfUtil.getTriangle(mGeneration.mTransform1, adjTri, NULL, NULL, adjInds[a], false, false);
+ //We now compare the triangles to see if this edge is active
+
+ PxVec3 adjNormal;
+ adjTri.denormalizedNormal(adjNormal);
+ PxU32 otherIndex = nextInd[a];
+ PxF32 projD = adjNormal.dot(currentTriangle.verts[otherIndex] - adjTri.verts[0]);
+ if(projD < 0.f)
+ {
+ adjNormal.normalize();
+
+ PxF32 proj = adjNormal.dot(normal);
+
+ if(proj < 0.999f)
+ {
+ triFlags |= 1 << (a+3);
+ }
+ }
+ }
+ else
+ triFlags |= (1 << (a+3));
+ }
+ mGeneration.processTriangle(currentTriangle.verts, triangleIndex, triFlags, vertIndices);
+ }
+ return true;
+ }
+
+protected:
+ ConvexVsHeightfieldContactGenerationCallback &operator=(const ConvexVsHeightfieldContactGenerationCallback &);
+};
+}
+
+static bool contactHullHeightfield2(const PolygonalData& polyData0, const PxBounds3& hullAABB, const PxHeightFieldGeometry& shape1,
+ const PxTransform& transform0, const PxTransform& transform1,
+ const NarrowPhaseParams& params, ContactBuffer& contactBuffer,
+ const Cm::FastVertex2ShapeScaling& convexScaling,
+ bool idtConvexScale)
+{
+ //We need to create a callback that fills triangles from the HF
+
+ const HeightField& hf = *static_cast<HeightField*>(shape1.heightField);
+
+ HeightFieldUtil hfUtil(shape1, hf);
+
+ const Cm::Matrix34 world0(transform0);
+ const Cm::Matrix34 world1(transform1);
+
+ ////////////////////
+
+ // Compute relative transforms
+ const PxTransform t0to1 = transform1.transformInv(transform0);
+ const PxTransform t1to0 = transform0.transformInv(transform1);
+
+ Ps::InlineArray<PxU32, LOCAL_CONTACTS_SIZE> delayedContacts;
+
+ ConvexVsHeightfieldContactGenerationCallback blockCallback(hfUtil, delayedContacts, t0to1, t1to0, polyData0, world0, world1, convexScaling, params.mContactDistance, params.mToleranceLength,
+ idtConvexScale, params.mMeshContactMargin, transform0, transform1, contactBuffer);
+
+ hfUtil.overlapAABBTriangles(transform1, PxBounds3::transformFast(t0to1, hullAABB), 0, &blockCallback);
+
+ blockCallback.mGeneration.generateLastContacts();
+
+ return blockCallback.mGeneration.mAnyHits;
+}
+
+bool Gu::contactConvexHeightfield(GU_CONTACT_METHOD_ARGS)
+{
+ PX_UNUSED(cache);
+ PX_UNUSED(renderOutput);
+
+ //Create a triangle cache from the HF triangles and then feed triangles to NP mesh methods
+ const physx::PxHeightFieldGeometryLL& shapeMesh = shape1.get<const PxHeightFieldGeometryLL>();
+
+ Cm::FastVertex2ShapeScaling convexScaling;
+ PxBounds3 hullAABB;
+ PolygonalData polyData0;
+ const bool idtScaleConvex = getConvexData(shape0, convexScaling, hullAABB, polyData0);
+ const PxVec3 inflation(params.mContactDistance);
+ hullAABB.minimum -= inflation;
+ hullAABB.maximum += inflation;
+
+ return contactHullHeightfield2(polyData0, hullAABB, shapeMesh, transform0, transform1, params, contactBuffer, convexScaling, idtScaleConvex);
+}
+
+bool Gu::contactBoxHeightfield(GU_CONTACT_METHOD_ARGS)
+{
+ PX_UNUSED(cache);
+ PX_UNUSED(renderOutput);
+
+ //Create a triangle cache from the HF triangles and then feed triangles to NP mesh methods
+ const physx::PxHeightFieldGeometryLL& shapeMesh = shape1.get<const PxHeightFieldGeometryLL>();
+
+ const PxBoxGeometry& shapeBox = shape0.get<const PxBoxGeometry>();
+
+ PolygonalData polyData0;
+ PolygonalBox polyBox(shapeBox.halfExtents);
+ polyBox.getPolygonalData(&polyData0);
+
+ const PxVec3 inflatedExtents = shapeBox.halfExtents + PxVec3(params.mContactDistance);
+
+ const PxBounds3 hullAABB = PxBounds3(-inflatedExtents, inflatedExtents);
+
+ const Cm::FastVertex2ShapeScaling idtScaling;
+
+ return contactHullHeightfield2(polyData0, hullAABB, shapeMesh, transform0, transform1, params, contactBuffer, idtScaling, true);
+}
diff --git a/PhysX_3.4/Source/GeomUtils/src/contact/GuContactMethodImpl.h b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactMethodImpl.h
new file mode 100644
index 00000000..86101dc3
--- /dev/null
+++ b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactMethodImpl.h
@@ -0,0 +1,174 @@
+// 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.
+
+#ifndef GU_CONTACTMETHODIMPL_H
+#define GU_CONTACTMETHODIMPL_H
+
+#include "foundation/PxAssert.h"
+#include "PxPhysXCommonConfig.h"
+#include "CmPhysXCommon.h"
+#include "collision/PxCollisionDefs.h"
+
+namespace physx
+{
+namespace Cm
+{
+ class RenderOutput;
+}
+
+namespace Gu
+{
+ class GeometryUnion;
+ class ContactBuffer;
+ struct NarrowPhaseParams;
+ class PersistentContactManifold;
+ class MultiplePersistentContactManifold;
+
+ enum ManifoldFlags
+ {
+ IS_MANIFOLD = (1<<0),
+ IS_MULTI_MANIFOLD = (1<<1)
+ };
+
+ struct Cache : public PxCache
+ {
+ Cache()
+ {
+ }
+
+ PX_FORCE_INLINE void setManifold(void* manifold)
+ {
+ mCachedData = reinterpret_cast<PxU8*>(manifold);
+ mManifoldFlags |= IS_MANIFOLD;
+ }
+
+ PX_FORCE_INLINE void setMultiManifold(void* manifold)
+ {
+ mCachedData = reinterpret_cast<PxU8*>(manifold);
+ mManifoldFlags |= IS_MANIFOLD|IS_MULTI_MANIFOLD;
+ }
+
+ PX_FORCE_INLINE PxU8 isManifold() const
+ {
+ return PxU8(mManifoldFlags & IS_MANIFOLD);
+ }
+
+ PX_FORCE_INLINE PxU8 isMultiManifold() const
+ {
+ return PxU8(mManifoldFlags & IS_MULTI_MANIFOLD);
+ }
+
+ PX_FORCE_INLINE PersistentContactManifold& getManifold()
+ {
+ PX_ASSERT(isManifold());
+ PX_ASSERT(!isMultiManifold());
+ PX_ASSERT((uintptr_t(mCachedData) & 0xf) == 0);
+ return *reinterpret_cast<PersistentContactManifold*>(mCachedData);
+ }
+
+ PX_FORCE_INLINE MultiplePersistentContactManifold& getMultipleManifold()
+ {
+ PX_ASSERT(isManifold());
+ PX_ASSERT(isMultiManifold());
+ PX_ASSERT((uintptr_t(mCachedData) & 0xf) == 0);
+ return *reinterpret_cast<MultiplePersistentContactManifold*>(mCachedData);
+ }
+ };
+}
+
+#define GU_CONTACT_METHOD_ARGS \
+ const Gu::GeometryUnion& shape0, \
+ const Gu::GeometryUnion& shape1, \
+ const PxTransform& transform0, \
+ const PxTransform& transform1, \
+ const Gu::NarrowPhaseParams& params, \
+ Gu::Cache& cache, \
+ Gu::ContactBuffer& contactBuffer, \
+ Cm::RenderOutput* renderOutput
+
+namespace Gu
+{
+ PX_PHYSX_COMMON_API bool contactSphereSphere(GU_CONTACT_METHOD_ARGS);
+ PX_PHYSX_COMMON_API bool contactSphereCapsule(GU_CONTACT_METHOD_ARGS);
+ PX_PHYSX_COMMON_API bool contactSphereBox(GU_CONTACT_METHOD_ARGS);
+ PX_PHYSX_COMMON_API bool contactCapsuleCapsule(GU_CONTACT_METHOD_ARGS);
+ PX_PHYSX_COMMON_API bool contactCapsuleBox(GU_CONTACT_METHOD_ARGS);
+ PX_PHYSX_COMMON_API bool contactCapsuleConvex(GU_CONTACT_METHOD_ARGS);
+ PX_PHYSX_COMMON_API bool contactBoxBox(GU_CONTACT_METHOD_ARGS);
+ PX_PHYSX_COMMON_API bool contactBoxConvex(GU_CONTACT_METHOD_ARGS);
+ PX_PHYSX_COMMON_API bool contactConvexConvex(GU_CONTACT_METHOD_ARGS);
+
+ PX_PHYSX_COMMON_API bool contactSphereMesh(GU_CONTACT_METHOD_ARGS);
+ PX_PHYSX_COMMON_API bool contactCapsuleMesh(GU_CONTACT_METHOD_ARGS);
+ PX_PHYSX_COMMON_API bool contactBoxMesh(GU_CONTACT_METHOD_ARGS);
+ PX_PHYSX_COMMON_API bool contactConvexMesh(GU_CONTACT_METHOD_ARGS);
+
+ PX_PHYSX_COMMON_API bool contactSphereHeightfield(GU_CONTACT_METHOD_ARGS);
+ PX_PHYSX_COMMON_API bool contactCapsuleHeightfield(GU_CONTACT_METHOD_ARGS);
+ PX_PHYSX_COMMON_API bool contactBoxHeightfield(GU_CONTACT_METHOD_ARGS);
+ PX_PHYSX_COMMON_API bool contactConvexHeightfield(GU_CONTACT_METHOD_ARGS);
+
+ PX_PHYSX_COMMON_API bool contactSpherePlane(GU_CONTACT_METHOD_ARGS);
+ PX_PHYSX_COMMON_API bool contactPlaneBox(GU_CONTACT_METHOD_ARGS);
+ PX_PHYSX_COMMON_API bool contactPlaneCapsule(GU_CONTACT_METHOD_ARGS);
+ PX_PHYSX_COMMON_API bool contactPlaneConvex(GU_CONTACT_METHOD_ARGS);
+
+ //Legacy heightfield code path
+ PX_PHYSX_COMMON_API bool legacyContactSphereHeightfield(GU_CONTACT_METHOD_ARGS);
+ PX_PHYSX_COMMON_API bool legacyContactCapsuleHeightfield(GU_CONTACT_METHOD_ARGS);
+ PX_PHYSX_COMMON_API bool legacyContactBoxHeightfield(GU_CONTACT_METHOD_ARGS);
+ PX_PHYSX_COMMON_API bool legacyContactConvexHeightfield(GU_CONTACT_METHOD_ARGS);
+
+ PX_PHYSX_COMMON_API bool pcmContactSphereMesh(GU_CONTACT_METHOD_ARGS);
+ PX_PHYSX_COMMON_API bool pcmContactCapsuleMesh(GU_CONTACT_METHOD_ARGS);
+ PX_PHYSX_COMMON_API bool pcmContactBoxMesh(GU_CONTACT_METHOD_ARGS);
+ PX_PHYSX_COMMON_API bool pcmContactConvexMesh(GU_CONTACT_METHOD_ARGS);
+ PX_PHYSX_COMMON_API bool pcmContactSphereHeightField(GU_CONTACT_METHOD_ARGS);
+ PX_PHYSX_COMMON_API bool pcmContactCapsuleHeightField(GU_CONTACT_METHOD_ARGS);
+ PX_PHYSX_COMMON_API bool pcmContactBoxHeightField(GU_CONTACT_METHOD_ARGS);
+ PX_PHYSX_COMMON_API bool pcmContactConvexHeightField(GU_CONTACT_METHOD_ARGS);
+
+ PX_PHYSX_COMMON_API bool pcmContactPlaneCapsule(GU_CONTACT_METHOD_ARGS);
+ PX_PHYSX_COMMON_API bool pcmContactPlaneBox(GU_CONTACT_METHOD_ARGS);
+ PX_PHYSX_COMMON_API bool pcmContactPlaneConvex(GU_CONTACT_METHOD_ARGS);
+ PX_PHYSX_COMMON_API bool pcmContactSphereSphere(GU_CONTACT_METHOD_ARGS);
+ PX_PHYSX_COMMON_API bool pcmContactSpherePlane(GU_CONTACT_METHOD_ARGS);
+ PX_PHYSX_COMMON_API bool pcmContactSphereCapsule(GU_CONTACT_METHOD_ARGS);
+ PX_PHYSX_COMMON_API bool pcmContactSphereBox(GU_CONTACT_METHOD_ARGS);
+ PX_PHYSX_COMMON_API bool pcmContactSphereConvex(GU_CONTACT_METHOD_ARGS);
+ PX_PHYSX_COMMON_API bool pcmContactCapsuleCapsule(GU_CONTACT_METHOD_ARGS);
+ PX_PHYSX_COMMON_API bool pcmContactCapsuleBox(GU_CONTACT_METHOD_ARGS);
+ PX_PHYSX_COMMON_API bool pcmContactCapsuleConvex(GU_CONTACT_METHOD_ARGS);
+ PX_PHYSX_COMMON_API bool pcmContactBoxBox(GU_CONTACT_METHOD_ARGS);
+ PX_PHYSX_COMMON_API bool pcmContactBoxConvex(GU_CONTACT_METHOD_ARGS);
+ PX_PHYSX_COMMON_API bool pcmContactConvexConvex(GU_CONTACT_METHOD_ARGS);
+}
+}
+
+#endif
diff --git a/PhysX_3.4/Source/GeomUtils/src/contact/GuContactPlaneBox.cpp b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactPlaneBox.cpp
new file mode 100644
index 00000000..bd46b296
--- /dev/null
+++ b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactPlaneBox.cpp
@@ -0,0 +1,128 @@
+// 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 "GuContactMethodImpl.h"
+#include "GuContactBuffer.h"
+#include "GuGeometryUnion.h"
+#include "CmMatrix34.h"
+
+#include "PsUtilities.h"
+#include "foundation/PxUnionCast.h"
+
+namespace physx
+{
+namespace Gu
+{
+bool contactPlaneBox(GU_CONTACT_METHOD_ARGS)
+{
+ PX_UNUSED(renderOutput);
+ PX_UNUSED(cache);
+ PX_UNUSED(shape0);
+
+ // Get actual shape data
+ //const PxPlaneGeometry& shapePlane = shape.get<const PxPlaneGeometry>();
+ const PxBoxGeometry& shapeBox = shape1.get<const PxBoxGeometry>();
+
+ const PxVec3 negPlaneNormal = -transform0.q.getBasisVector0();
+
+ //Make sure we have a normalized plane
+ //PX_ASSERT(PxAbs(shape0.mNormal.magnitudeSquared() - 1.0f) < 0.000001f);
+
+ Cm::Matrix34 boxMatrix(transform1);
+ Cm::Matrix34 boxToPlane(transform0.transformInv(transform1));
+
+ PxVec3 point;
+
+ PX_ASSERT(contactBuffer.count==0);
+
+/* for(int vx=-1; vx<=1; vx+=2)
+ for(int vy=-1; vy<=1; vy+=2)
+ for(int vz=-1; vz<=1; vz+=2)
+ {
+ //point = boxToPlane.transform(PxVec3(shapeBox.halfExtents.x*vx, shapeBox.halfExtents.y*vy, shapeBox.halfExtents.z*vz));
+ //PxReal planeEq = point.x;
+ //Optimized a bit
+ point.set(shapeBox.halfExtents.x*vx, shapeBox.halfExtents.y*vy, shapeBox.halfExtents.z*vz);
+ const PxReal planeEq = boxToPlane.m.column0.x*point.x + boxToPlane.m.column1.x*point.y + boxToPlane.m.column2.x*point.z + boxToPlane.p.x;
+
+ if(planeEq <= contactDistance)
+ {
+ contactBuffer.contact(boxMatrix.transform(point), negPlaneNormal, planeEq);
+
+ //no point in making more than 4 contacts.
+ if (contactBuffer.count >= 6) //was: 4) actually, with strong interpenetration more than just the bottom surface goes through,
+ //and we want to find the *deepest* 4 vertices, really.
+ return true;
+ }
+ }*/
+
+ // PT: the above code is shock full of LHS/FCMPs. And there's no point in limiting the number of contacts to 6 when the max possible is 8.
+
+ const PxReal limit = params.mContactDistance - boxToPlane.p.x;
+ const PxReal dx = shapeBox.halfExtents.x;
+ const PxReal dy = shapeBox.halfExtents.y;
+ const PxReal dz = shapeBox.halfExtents.z;
+ const PxReal bxdx = boxToPlane.m.column0.x * dx;
+ const PxReal bxdy = boxToPlane.m.column1.x * dy;
+ const PxReal bxdz = boxToPlane.m.column2.x * dz;
+
+ PxReal depths[8];
+ depths[0] = bxdx + bxdy + bxdz - limit;
+ depths[1] = bxdx + bxdy - bxdz - limit;
+ depths[2] = bxdx - bxdy + bxdz - limit;
+ depths[3] = bxdx - bxdy - bxdz - limit;
+ depths[4] = - bxdx + bxdy + bxdz - limit;
+ depths[5] = - bxdx + bxdy - bxdz - limit;
+ depths[6] = - bxdx - bxdy + bxdz - limit;
+ depths[7] = - bxdx - bxdy - bxdz - limit;
+
+ //const PxU32* binary = reinterpret_cast<const PxU32*>(depths);
+ const PxU32* binary = PxUnionCast<PxU32*, PxF32*>(depths);
+
+ if(binary[0] & PX_SIGN_BITMASK)
+ contactBuffer.contact(boxMatrix.transform(PxVec3(dx, dy, dz)), negPlaneNormal, depths[0] + params.mContactDistance);
+ if(binary[1] & PX_SIGN_BITMASK)
+ contactBuffer.contact(boxMatrix.transform(PxVec3(dx, dy, -dz)), negPlaneNormal, depths[1] + params.mContactDistance);
+ if(binary[2] & PX_SIGN_BITMASK)
+ contactBuffer.contact(boxMatrix.transform(PxVec3(dx, -dy, dz)), negPlaneNormal, depths[2] + params.mContactDistance);
+ if(binary[3] & PX_SIGN_BITMASK)
+ contactBuffer.contact(boxMatrix.transform(PxVec3(dx, -dy, -dz)), negPlaneNormal, depths[3] + params.mContactDistance);
+ if(binary[4] & PX_SIGN_BITMASK)
+ contactBuffer.contact(boxMatrix.transform(PxVec3(-dx, dy, dz)), negPlaneNormal, depths[4] + params.mContactDistance);
+ if(binary[5] & PX_SIGN_BITMASK)
+ contactBuffer.contact(boxMatrix.transform(PxVec3(-dx, dy, -dz)), negPlaneNormal, depths[5] + params.mContactDistance);
+ if(binary[6] & PX_SIGN_BITMASK)
+ contactBuffer.contact(boxMatrix.transform(PxVec3(-dx, -dy, dz)), negPlaneNormal, depths[6] + params.mContactDistance);
+ if(binary[7] & PX_SIGN_BITMASK)
+ contactBuffer.contact(boxMatrix.transform(PxVec3(-dx, -dy, -dz)), negPlaneNormal, depths[7] + params.mContactDistance);
+
+ return contactBuffer.count > 0;
+}
+}//Gu
+}//physx
diff --git a/PhysX_3.4/Source/GeomUtils/src/contact/GuContactPlaneCapsule.cpp b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactPlaneCapsule.cpp
new file mode 100644
index 00000000..92a917a3
--- /dev/null
+++ b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactPlaneCapsule.cpp
@@ -0,0 +1,80 @@
+// 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 "GuContactMethodImpl.h"
+#include "GuContactBuffer.h"
+#include "GuInternal.h"
+#include "GuSegment.h"
+#include "GuGeometryUnion.h"
+
+namespace physx
+{
+namespace Gu
+{
+bool contactPlaneCapsule(GU_CONTACT_METHOD_ARGS)
+{
+ PX_UNUSED(renderOutput);
+ PX_UNUSED(cache);
+ PX_UNUSED(shape0);
+
+ // Get actual shape data
+ //const PxPlaneGeometry& shapePlane = shape.get<const PxPlaneGeometry>();
+ const PxCapsuleGeometry& shapeCapsule = shape1.get<const PxCapsuleGeometry>();
+
+ const PxTransform capsuleToPlane = transform0.transformInv(transform1);
+
+ //Capsule in plane space
+ Segment segment;
+ getCapsuleSegment(capsuleToPlane, shapeCapsule, segment);
+
+ const PxVec3 negPlaneNormal = transform0.q.getBasisVector0();
+
+ bool contact = false;
+
+ const PxReal separation0 = segment.p0.x - shapeCapsule.radius;
+ const PxReal separation1 = segment.p1.x - shapeCapsule.radius;
+ if(separation0 <= params.mContactDistance)
+ {
+ const PxVec3 temp(segment.p0.x - shapeCapsule.radius, segment.p0.y, segment.p0.z);
+ const PxVec3 point = transform0.transform(temp);
+ contactBuffer.contact(point, -negPlaneNormal, separation0);
+ contact = true;
+ }
+
+ if(separation1 <= params.mContactDistance)
+ {
+ const PxVec3 temp(segment.p1.x - shapeCapsule.radius, segment.p1.y, segment.p1.z);
+ const PxVec3 point = transform0.transform(temp);
+ contactBuffer.contact(point, -negPlaneNormal, separation1);
+ contact = true;
+ }
+ return contact;
+}
+}//Gu
+}//physx
diff --git a/PhysX_3.4/Source/GeomUtils/src/contact/GuContactPlaneConvex.cpp b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactPlaneConvex.cpp
new file mode 100644
index 00000000..682186c3
--- /dev/null
+++ b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactPlaneConvex.cpp
@@ -0,0 +1,101 @@
+// 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 "GuConvexMeshData.h"
+#include "GuContactMethodImpl.h"
+#include "GuContactBuffer.h"
+#include "GuGeometryUnion.h"
+#include "CmScaling.h"
+
+
+namespace physx
+{
+namespace Gu
+{
+bool contactPlaneConvex(GU_CONTACT_METHOD_ARGS)
+{
+ PX_UNUSED(renderOutput);
+ PX_UNUSED(cache);
+ PX_UNUSED(shape0);
+
+ // Get actual shape data
+ //const PxPlaneGeometry& shapePlane = shape.get<const PxPlaneGeometry>();
+ const PxConvexMeshGeometryLL& shapeConvex = shape1.get<const PxConvexMeshGeometryLL>();
+
+ const PxVec3* PX_RESTRICT hullVertices = shapeConvex.hullData->getHullVertices();
+ PxU32 numHullVertices = shapeConvex.hullData->mNbHullVertices;
+// Ps::prefetch128(hullVertices);
+
+ // Plane is implicitly <1,0,0> 0 in localspace
+ Cm::Matrix34 convexToPlane (transform0.transformInv(transform1));
+ PxMat33 convexToPlane_rot(convexToPlane[0], convexToPlane[1], convexToPlane[2] );
+
+ bool idtScale = shapeConvex.scale.isIdentity();
+ Cm::FastVertex2ShapeScaling convexScaling; // PT: TODO: remove default ctor
+ if(!idtScale)
+ convexScaling.init(shapeConvex.scale);
+
+ convexToPlane = Cm::Matrix34( convexToPlane_rot * convexScaling.getVertex2ShapeSkew(), convexToPlane[3] );
+
+ //convexToPlane = context.mVertex2ShapeSkew[1].getVertex2WorldSkew(convexToPlane);
+
+ const Cm::Matrix34 planeToW (transform0);
+
+ // This is rather brute-force
+
+ bool status = false;
+
+ const PxVec3 contactNormal = -planeToW.m.column0;
+
+ while(numHullVertices--)
+ {
+ const PxVec3& vertex = *hullVertices++;
+// if(numHullVertices)
+// Ps::prefetch128(hullVertices);
+
+ const PxVec3 pointInPlane = convexToPlane.transform(vertex); //TODO: this multiply could be factored out!
+ if(pointInPlane.x <= params.mContactDistance)
+ {
+// const PxVec3 pointInW = planeToW.transform(pointInPlane);
+// contactBuffer.contact(pointInW, -planeToW.m.column0, pointInPlane.x);
+ status = true;
+ Gu::ContactPoint* PX_RESTRICT pt = contactBuffer.contact();
+ if(pt)
+ {
+ pt->normal = contactNormal;
+ pt->point = planeToW.transform(pointInPlane);
+ pt->separation = pointInPlane.x;
+ pt->internalFaceIndex1 = PXC_CONTACT_NO_FACE_INDEX;
+ }
+ }
+ }
+ return status;
+}
+}//Gu
+}//physx
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;
+}
diff --git a/PhysX_3.4/Source/GeomUtils/src/contact/GuContactPolygonPolygon.h b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactPolygonPolygon.h
new file mode 100644
index 00000000..41f5bf6f
--- /dev/null
+++ b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactPolygonPolygon.h
@@ -0,0 +1,69 @@
+// 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.
+
+#ifndef GU_CONTACTPOLYGONPOLYGON_H
+#define GU_CONTACTPOLYGONPOLYGON_H
+
+#include "foundation/PxVec3.h"
+#include "CmPhysXCommon.h"
+#include "PxPhysXCommonConfig.h"
+
+namespace physx
+{
+
+namespace Cm
+{
+ class Matrix34;
+ class FastVertex2ShapeScaling;
+}
+
+namespace Gu
+{
+class ContactBuffer;
+
+PX_PHYSX_COMMON_API PxMat33 findRotationMatrixFromZ(const PxVec3& to);
+
+PX_PHYSX_COMMON_API bool 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* vertices1, const PxU8* 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, //face indices for contact callback,
+ ContactBuffer& contactBuffer,
+ bool flipNormal, const PxVec3& posShift, float sepShift
+ ); // shape order, post gen shift.
+}
+}
+
+#endif
diff --git a/PhysX_3.4/Source/GeomUtils/src/contact/GuContactSphereBox.cpp b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactSphereBox.cpp
new file mode 100644
index 00000000..58da6764
--- /dev/null
+++ b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactSphereBox.cpp
@@ -0,0 +1,181 @@
+// 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 "GuContactMethodImpl.h"
+#include "GuContactBuffer.h"
+#include "GuGeometryUnion.h"
+
+using namespace physx;
+
+//This version is ported 1:1 from novodex
+static PX_FORCE_INLINE bool ContactSphereBox(const PxVec3& sphereOrigin,
+ PxReal sphereRadius,
+ const PxVec3& boxExtents,
+// const PxcCachedTransforms& boxCacheTransform,
+ const PxTransform& boxTransform,
+ PxVec3& point,
+ PxVec3& normal,
+ PxReal& separation,
+ PxReal contactDistance)
+{
+// const PxTransform& boxTransform = boxCacheTransform.getShapeToWorld();
+
+ //returns true on contact
+ const PxVec3 delta = sphereOrigin - boxTransform.p; // s1.center - s2.center;
+ PxVec3 dRot = boxTransform.rotateInv(delta); //transform delta into OBB body coords.
+
+ //check if delta is outside ABB - and clip the vector to the ABB.
+ bool outside = false;
+
+ if (dRot.x < -boxExtents.x)
+ {
+ outside = true;
+ dRot.x = -boxExtents.x;
+ }
+ else if (dRot.x > boxExtents.x)
+ {
+ outside = true;
+ dRot.x = boxExtents.x;
+ }
+
+ if (dRot.y < -boxExtents.y)
+ {
+ outside = true;
+ dRot.y = -boxExtents.y;
+ }
+ else if (dRot.y > boxExtents.y)
+ {
+ outside = true;
+ dRot.y = boxExtents.y;
+ }
+
+ if (dRot.z < -boxExtents.z)
+ {
+ outside = true;
+ dRot.z =-boxExtents.z;
+ }
+ else if (dRot.z > boxExtents.z)
+ {
+ outside = true;
+ dRot.z = boxExtents.z;
+ }
+
+ if (outside) //if clipping was done, sphere center is outside of box.
+ {
+ point = boxTransform.rotate(dRot); //get clipped delta back in world coords.
+ normal = delta - point; //what we clipped away.
+ const PxReal lenSquared = normal.magnitudeSquared();
+ const PxReal inflatedDist = sphereRadius + contactDistance;
+ if (lenSquared > inflatedDist * inflatedDist)
+ return false; //disjoint
+
+ //normalize to make it into the normal:
+ separation = PxRecipSqrt(lenSquared);
+ normal *= separation;
+ separation *= lenSquared;
+ //any plane that touches the sphere is tangential, so a vector from contact point to sphere center defines normal.
+ //we could also use point here, which has same direction.
+ //this is either a faceFace or a vertexFace contact depending on whether the box's face or vertex collides, but we did not distinguish.
+ //We'll just use vertex face for now, this info isn't really being used anyway.
+ //contact point is point on surface of cube closest to sphere center.
+ point += boxTransform.p;
+ separation -= sphereRadius;
+ return true;
+ }
+ else
+ {
+ //center is in box, we definitely have a contact.
+ PxVec3 locNorm; //local coords contact normal
+
+ /*const*/ PxVec3 absdRot;
+ absdRot = PxVec3(PxAbs(dRot.x), PxAbs(dRot.y), PxAbs(dRot.z));
+ /*const*/ PxVec3 distToSurface = boxExtents - absdRot; //dist from embedded center to box surface along 3 dimensions.
+
+ //find smallest element of distToSurface
+ if (distToSurface.y < distToSurface.x)
+ {
+ if (distToSurface.y < distToSurface.z)
+ {
+ //y
+ locNorm = PxVec3(0.0f, dRot.y > 0.0f ? 1.0f : -1.0f, 0.0f);
+ separation = -distToSurface.y;
+ }
+ else
+ {
+ //z
+ locNorm = PxVec3(0.0f,0.0f, dRot.z > 0.0f ? 1.0f : -1.0f);
+ separation = -distToSurface.z;
+ }
+ }
+ else
+ {
+ if (distToSurface.x < distToSurface.z)
+ {
+ //x
+ locNorm = PxVec3(dRot.x > 0.0f ? 1.0f : -1.0f, 0.0f, 0.0f);
+ separation = -distToSurface.x;
+ }
+ else
+ {
+ //z
+ locNorm = PxVec3(0.0f,0.0f, dRot.z > 0.0f ? 1.0f : -1.0f);
+ separation = -distToSurface.z;
+ }
+ }
+ //separation so far is just the embedding of the center point; we still have to push out all of the radius.
+ point = sphereOrigin;
+ normal = boxTransform.rotate(locNorm);
+ separation -= sphereRadius;
+ return true;
+ }
+}
+
+namespace physx
+{
+namespace Gu
+{
+bool contactSphereBox(GU_CONTACT_METHOD_ARGS)
+{
+ PX_UNUSED(renderOutput);
+ PX_UNUSED(cache);
+
+ const PxSphereGeometry& sphereGeom = shape0.get<const PxSphereGeometry>();
+ const PxBoxGeometry& boxGeom = shape1.get<const PxBoxGeometry>();
+
+ PxVec3 normal;
+ PxVec3 point;
+ PxReal separation;
+ if(!ContactSphereBox(transform0.p, sphereGeom.radius, boxGeom.halfExtents, transform1, point, normal, separation, params.mContactDistance))
+ return false;
+
+ contactBuffer.contact(point, normal, separation);
+ return true;
+}
+}//Gu
+}//physx
diff --git a/PhysX_3.4/Source/GeomUtils/src/contact/GuContactSphereCapsule.cpp b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactSphereCapsule.cpp
new file mode 100644
index 00000000..24a97d6f
--- /dev/null
+++ b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactSphereCapsule.cpp
@@ -0,0 +1,82 @@
+// 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 "GuDistancePointSegment.h"
+#include "GuContactMethodImpl.h"
+#include "GuContactBuffer.h"
+#include "GuInternal.h"
+#include "GuGeometryUnion.h"
+
+namespace physx
+{
+namespace Gu
+{
+bool contactSphereCapsule(GU_CONTACT_METHOD_ARGS)
+{
+ PX_UNUSED(renderOutput);
+ PX_UNUSED(cache);
+
+ const PxSphereGeometry& sphereGeom = shape0.get<const PxSphereGeometry>();
+ const PxCapsuleGeometry& capsuleGeom = shape1.get<const PxCapsuleGeometry>();
+
+ // PT: get capsule in local space
+ const PxVec3 capsuleLocalSegment = getCapsuleHalfHeightVector(transform1, capsuleGeom);
+ const Segment localSegment(capsuleLocalSegment, -capsuleLocalSegment);
+
+ // PT: get sphere in capsule space
+ const PxVec3 sphereCenterInCapsuleSpace = transform0.p - transform1.p;
+
+ const PxReal radiusSum = sphereGeom.radius + capsuleGeom.radius;
+ const PxReal inflatedSum = radiusSum + params.mContactDistance;
+
+ // PT: compute distance between sphere center & capsule's segment
+ PxReal u;
+ const PxReal squareDist = distancePointSegmentSquared(localSegment, sphereCenterInCapsuleSpace, &u);
+ if(squareDist >= inflatedSum*inflatedSum)
+ return false;
+
+ // PT: compute contact normal
+ PxVec3 normal = sphereCenterInCapsuleSpace - localSegment.getPointAt(u);
+
+ // We do a *manual* normalization to check for singularity condition
+ const PxReal lenSq = normal.magnitudeSquared();
+ if(lenSq==0.0f)
+ normal = PxVec3(1.0f, 0.0f, 0.0f); // PT: zero normal => pick up random one
+ else
+ normal *= PxRecipSqrt(lenSq);
+
+ // PT: compute contact point
+ const PxVec3 point = sphereCenterInCapsuleSpace + transform1.p - normal * sphereGeom.radius;
+
+ // PT: output unique contact
+ contactBuffer.contact(point, normal, PxSqrt(squareDist) - radiusSum);
+ return true;
+}
+}//Gu
+}//physx
diff --git a/PhysX_3.4/Source/GeomUtils/src/contact/GuContactSphereMesh.cpp b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactSphereMesh.cpp
new file mode 100644
index 00000000..582b2df9
--- /dev/null
+++ b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactSphereMesh.cpp
@@ -0,0 +1,615 @@
+// 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 "GuDistancePointTriangle.h"
+#include "GuContactMethodImpl.h"
+#include "GuContactBuffer.h"
+#include "GuGeometryUnion.h"
+#include "GuFeatureCode.h"
+#include "GuMidphaseInterface.h"
+#include "GuEntityReport.h"
+#include "GuHeightFieldUtil.h"
+#include "GuBox.h"
+#include "PsSort.h"
+
+#include "CmRenderOutput.h"
+
+#define DEBUG_RENDER_MESHCONTACTS 0
+
+using namespace physx;
+using namespace Gu;
+
+static const bool gDrawTouchedTriangles = false;
+
+static void outputErrorMessage()
+{
+#if PX_CHECKED
+ Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "Dropping contacts in sphere vs mesh: exceeded limit of 64 ");
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+// PT: a customized version that also returns the feature code
+static PxVec3 closestPtPointTriangle(const PxVec3& p, const PxVec3& a, const PxVec3& b, const PxVec3& c, float& s, float& t, FeatureCode& fc)
+{
+ // Check if P in vertex region outside A
+ const PxVec3 ab = b - a;
+ const PxVec3 ac = c - a;
+ const PxVec3 ap = p - a;
+ const float d1 = ab.dot(ap);
+ const float d2 = ac.dot(ap);
+ if(d1<=0.0f && d2<=0.0f)
+ {
+ s = 0.0f;
+ t = 0.0f;
+ fc = FC_VERTEX0;
+ return a; // Barycentric coords 1,0,0
+ }
+
+ // Check if P in vertex region outside B
+ const PxVec3 bp = p - b;
+ const float d3 = ab.dot(bp);
+ const float d4 = ac.dot(bp);
+ if(d3>=0.0f && d4<=d3)
+ {
+ s = 1.0f;
+ t = 0.0f;
+ fc = FC_VERTEX1;
+ return b; // Barycentric coords 0,1,0
+ }
+
+ // Check if P in edge region of AB, if so return projection of P onto AB
+ const float vc = d1*d4 - d3*d2;
+ if(vc<=0.0f && d1>=0.0f && d3<=0.0f)
+ {
+ const float v = d1 / (d1 - d3);
+ s = v;
+ t = 0.0f;
+ fc = FC_EDGE01;
+ return a + v * ab; // barycentric coords (1-v, v, 0)
+ }
+
+ // Check if P in vertex region outside C
+ const PxVec3 cp = p - c;
+ const float d5 = ab.dot(cp);
+ const float d6 = ac.dot(cp);
+ if(d6>=0.0f && d5<=d6)
+ {
+ s = 0.0f;
+ t = 1.0f;
+ fc = FC_VERTEX2;
+ return c; // Barycentric coords 0,0,1
+ }
+
+ // Check if P in edge region of AC, if so return projection of P onto AC
+ const float vb = d5*d2 - d1*d6;
+ if(vb<=0.0f && d2>=0.0f && d6<=0.0f)
+ {
+ const float w = d2 / (d2 - d6);
+ s = 0.0f;
+ t = w;
+ fc = FC_EDGE20;
+ return a + w * ac; // barycentric coords (1-w, 0, w)
+ }
+
+ // Check if P in edge region of BC, if so return projection of P onto BC
+ const float va = d3*d6 - d5*d4;
+ if(va<=0.0f && (d4-d3)>=0.0f && (d5-d6)>=0.0f)
+ {
+ const float w = (d4-d3) / ((d4 - d3) + (d5-d6));
+ s = 1.0f-w;
+ t = w;
+ fc = FC_EDGE12;
+ return b + w * (c-b); // barycentric coords (0, 1-w, w)
+ }
+
+ // P inside face region. Compute Q through its barycentric coords (u,v,w)
+ const float denom = 1.0f / (va + vb + vc);
+ const float v = vb * denom;
+ const float w = vc * denom;
+ s = v;
+ t = w;
+ fc = FC_FACE;
+ return a + ab*v + ac*w;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+// PT: we use a separate structure to make sorting faster
+struct SortKey
+{
+ float mSquareDist;
+ PxU32 mIndex;
+
+ PX_FORCE_INLINE bool operator < (const SortKey& data) const
+ {
+ return mSquareDist < data.mSquareDist;
+ }
+};
+
+struct TriangleData
+{
+ PxVec3 mDelta;
+ FeatureCode mFC;
+ PxU32 mTriangleIndex;
+ PxU32 mVRef[3];
+};
+
+struct CachedTriangleIndices
+{
+ PxU32 mVRef[3];
+};
+
+static PX_FORCE_INLINE bool validateSquareDist(PxReal squareDist)
+{
+ return squareDist>0.0001f;
+}
+
+static bool validateEdge(PxU32 vref0, PxU32 vref1, const CachedTriangleIndices* cachedTris, PxU32 nbCachedTris)
+{
+ while(nbCachedTris--)
+ {
+ const CachedTriangleIndices& inds = *cachedTris++;
+ const PxU32 vi0 = inds.mVRef[0];
+ const PxU32 vi1 = inds.mVRef[1];
+ const PxU32 vi2 = inds.mVRef[2];
+
+ if(vi0==vref0)
+ {
+ if(vi1==vref1 || vi2==vref1)
+ return false;
+ }
+ else if(vi1==vref0)
+ {
+ if(vi0==vref1 || vi2==vref1)
+ return false;
+ }
+ else if(vi2==vref0)
+ {
+ if(vi1==vref1 || vi0==vref1)
+ return false;
+ }
+ }
+ return true;
+}
+
+static bool validateVertex(PxU32 vref, const CachedTriangleIndices* cachedTris, PxU32 nbCachedTris)
+{
+ while(nbCachedTris--)
+ {
+ const CachedTriangleIndices& inds = *cachedTris++;
+ if(inds.mVRef[0]==vref || inds.mVRef[1]==vref || inds.mVRef[2]==vref)
+ return false;
+ }
+ return true;
+}
+
+namespace
+{
+ class NullAllocator
+ {
+ public:
+ PX_FORCE_INLINE NullAllocator() { }
+ PX_FORCE_INLINE void* allocate(size_t, const char*, int) { return NULL; }
+ PX_FORCE_INLINE void deallocate(void*) { }
+ };
+
+struct SphereMeshContactGeneration
+{
+ const PxSphereGeometry& mShapeSphere;
+ const PxTransform& mTransform0;
+ const PxTransform& mTransform1;
+ ContactBuffer& mContactBuffer;
+ const PxVec3& mSphereCenterShape1Space;
+ PxF32 mInflatedRadius2;
+ PxU32 mNbDelayed;
+ TriangleData mSavedData[ContactBuffer::MAX_CONTACTS];
+ SortKey mSortKey[ContactBuffer::MAX_CONTACTS];
+ PxU32 mNbCachedTris;
+ CachedTriangleIndices mCachedTris[ContactBuffer::MAX_CONTACTS];
+ Cm::RenderOutput* mRenderOutput;
+
+ SphereMeshContactGeneration(const PxSphereGeometry& shapeSphere, const PxTransform& transform0, const PxTransform& transform1,
+ ContactBuffer& contactBuffer, const PxVec3& sphereCenterShape1Space, PxF32 inflatedRadius,
+ Cm::RenderOutput* renderOutput) :
+ mShapeSphere (shapeSphere),
+ mTransform0 (transform0),
+ mTransform1 (transform1),
+ mContactBuffer (contactBuffer),
+ mSphereCenterShape1Space (sphereCenterShape1Space),
+ mInflatedRadius2 (inflatedRadius*inflatedRadius),
+ mNbDelayed (0),
+ mNbCachedTris (0),
+ mRenderOutput (renderOutput)
+ {
+ }
+
+ PX_FORCE_INLINE void cacheTriangle(PxU32 ref0, PxU32 ref1, PxU32 ref2)
+ {
+ const PxU32 nb = mNbCachedTris++;
+ mCachedTris[nb].mVRef[0] = ref0;
+ mCachedTris[nb].mVRef[1] = ref1;
+ mCachedTris[nb].mVRef[2] = ref2;
+ }
+
+ PX_FORCE_INLINE void addContact(const PxVec3& d, PxReal squareDist, PxU32 triangleIndex)
+ {
+ float dist;
+ PxVec3 delta;
+ if(validateSquareDist(squareDist))
+ {
+ // PT: regular contact. Normalize 'delta'.
+ dist = PxSqrt(squareDist);
+ delta = d / dist;
+ }
+ else
+ {
+ // PT: singular contact: 'd' is the non-unit triangle's normal in this case.
+ dist = 0.0f;
+ delta = -d.getNormalized();
+ }
+
+ const PxVec3 worldNormal = -mTransform1.rotate(delta);
+
+ const PxVec3 localHit = mSphereCenterShape1Space + mShapeSphere.radius*delta;
+ const PxVec3 hit = mTransform1.transform(localHit);
+
+ if(!mContactBuffer.contact(hit, worldNormal, dist - mShapeSphere.radius, triangleIndex))
+ outputErrorMessage();
+ }
+
+ void processTriangle(PxU32 triangleIndex, const PxVec3& v0, const PxVec3& v1, const PxVec3& v2, const PxU32* vertInds)
+ {
+ // PT: compute closest point between sphere center and triangle
+ PxReal u, v;
+ FeatureCode fc;
+ const PxVec3 cp = closestPtPointTriangle(mSphereCenterShape1Space, v0, v1, v2, u, v, fc);
+
+ // PT: compute 'delta' vector between closest point and sphere center
+ const PxVec3 delta = cp - mSphereCenterShape1Space;
+ const PxReal squareDist = delta.magnitudeSquared();
+ if(squareDist >= mInflatedRadius2)
+ return;
+
+ // PT: backface culling without the normalize
+ // PT: TODO: consider doing before the pt-triangle distance test if it's cheaper
+ // PT: TODO: e0/e1 already computed in closestPtPointTriangle
+ const PxVec3 e0 = v1 - v0;
+ const PxVec3 e1 = v2 - v0;
+ const PxVec3 planeNormal = e0.cross(e1);
+ const PxF32 planeD = planeNormal.dot(v0); // PT: actually -d compared to PxcPlane
+ if(planeNormal.dot(mSphereCenterShape1Space) < planeD)
+ return;
+
+ // PT: for a regular contact, 'delta' is non-zero (and so is 'squareDist'). However when the sphere's center exactly touches
+ // the triangle, then both 'delta' and 'squareDist' become zero. This needs to be handled as a special case to avoid dividing
+ // by zero. We will use the triangle's normal as a contact normal in this special case.
+ //
+ // 'validateSquareDist' is called twice because there are conflicting goals here. We could call it once now and already
+ // compute the proper data for generating the contact. But this would mean doing a square-root and a division right here,
+ // even when the contact is not actually needed in the end. We could also call it only once in "addContact', but the plane's
+ // normal would not always be available (in case of delayed contacts), and thus it would need to be either recomputed (slower)
+ // or stored within 'TriangleData' (using more memory). Calling 'validateSquareDist' twice is a better option overall.
+ PxVec3 d;
+ if(validateSquareDist(squareDist))
+ d = delta;
+ else
+ d = planeNormal;
+
+ if(fc==FC_FACE)
+ {
+ addContact(d, squareDist, triangleIndex);
+
+ if(mNbCachedTris<ContactBuffer::MAX_CONTACTS)
+ cacheTriangle(vertInds[0], vertInds[1], vertInds[2]);
+ }
+ else
+ {
+ if(mNbDelayed<ContactBuffer::MAX_CONTACTS)
+ {
+ const PxU32 index = mNbDelayed++;
+ mSortKey[index].mSquareDist = squareDist;
+ mSortKey[index].mIndex = index;
+
+ TriangleData* saved = mSavedData + index;
+ saved->mDelta = d;
+ saved->mVRef[0] = vertInds[0];
+ saved->mVRef[1] = vertInds[1];
+ saved->mVRef[2] = vertInds[2];
+ saved->mFC = fc;
+ saved->mTriangleIndex = triangleIndex;
+ }
+ else outputErrorMessage();
+ }
+ }
+
+ void generateLastContacts()
+ {
+ const PxU32 count = mNbDelayed;
+ if(!count)
+ return;
+
+ Ps::sort(mSortKey, count, Ps::Less<SortKey>(), NullAllocator(), ContactBuffer::MAX_CONTACTS);
+
+ TriangleData* touchedTris = mSavedData;
+ for(PxU32 i=0;i<count;i++)
+ {
+ const TriangleData& data = touchedTris[mSortKey[i].mIndex];
+
+ const PxU32 ref0 = data.mVRef[0];
+ const PxU32 ref1 = data.mVRef[1];
+ const PxU32 ref2 = data.mVRef[2];
+
+ bool generateContact = false;
+
+ switch(data.mFC)
+ {
+ case FC_VERTEX0:
+ generateContact = ::validateVertex(ref0, mCachedTris, mNbCachedTris);
+ break;
+
+ case FC_VERTEX1:
+ generateContact = ::validateVertex(ref1, mCachedTris, mNbCachedTris);
+ break;
+
+ case FC_VERTEX2:
+ generateContact = ::validateVertex(ref2, mCachedTris, mNbCachedTris);
+ break;
+
+ case FC_EDGE01:
+ generateContact = ::validateEdge(ref0, ref1, mCachedTris, mNbCachedTris);
+ break;
+
+ case FC_EDGE12:
+ generateContact = ::validateEdge(ref1, ref2, mCachedTris, mNbCachedTris);
+ break;
+
+ case FC_EDGE20:
+ generateContact = ::validateEdge(ref0, ref2, mCachedTris, mNbCachedTris);
+ break;
+
+ case FC_FACE:
+ case FC_UNDEFINED:
+ PX_ASSERT(0); // PT: should not be possible
+ break;
+ };
+
+ if(generateContact)
+ addContact(data.mDelta, mSortKey[i].mSquareDist, data.mTriangleIndex);
+
+ if(mNbCachedTris<ContactBuffer::MAX_CONTACTS)
+ cacheTriangle(ref0, ref1, ref2);
+ else
+ outputErrorMessage();
+ }
+ }
+
+private:
+ SphereMeshContactGeneration& operator=(const SphereMeshContactGeneration&);
+};
+
+struct SphereMeshContactGenerationCallback_NoScale : MeshHitCallback<PxRaycastHit>
+{
+ SphereMeshContactGeneration mGeneration;
+ const TriangleMesh& mMeshData;
+
+ SphereMeshContactGenerationCallback_NoScale(const TriangleMesh& meshData, const PxSphereGeometry& shapeSphere,
+ const PxTransform& transform0, const PxTransform& transform1, ContactBuffer& contactBuffer,
+ const PxVec3& sphereCenterShape1Space, PxF32 inflatedRadius, Cm::RenderOutput* renderOutput
+ ) : MeshHitCallback<PxRaycastHit> (CallbackMode::eMULTIPLE),
+ mGeneration (shapeSphere, transform0, transform1, contactBuffer, sphereCenterShape1Space, inflatedRadius, renderOutput),
+ mMeshData (meshData)
+ {
+ }
+
+ virtual ~SphereMeshContactGenerationCallback_NoScale()
+ {
+ mGeneration.generateLastContacts();
+ }
+
+ virtual PxAgain processHit(
+ const PxRaycastHit& hit, const PxVec3& v0, const PxVec3& v1, const PxVec3& v2, PxReal&, const PxU32* vinds)
+ {
+ if(gDrawTouchedTriangles)
+ {
+ (*mGeneration.mRenderOutput) << 0xffffffff;
+ (*mGeneration.mRenderOutput) << PxMat44(PxIdentity);
+ const PxVec3 wp0 = mGeneration.mTransform1.transform(v0);
+ const PxVec3 wp1 = mGeneration.mTransform1.transform(v1);
+ const PxVec3 wp2 = mGeneration.mTransform1.transform(v2);
+ mGeneration.mRenderOutput->outputSegment(wp0, wp1);
+ mGeneration.mRenderOutput->outputSegment(wp1, wp2);
+ mGeneration.mRenderOutput->outputSegment(wp2, wp0);
+ }
+
+ mGeneration.processTriangle(hit.faceIndex, v0, v1, v2, vinds);
+ return true;
+ }
+
+protected:
+ SphereMeshContactGenerationCallback_NoScale &operator=(const SphereMeshContactGenerationCallback_NoScale &);
+};
+
+struct SphereMeshContactGenerationCallback_Scale : SphereMeshContactGenerationCallback_NoScale
+{
+ const Cm::FastVertex2ShapeScaling& mMeshScaling;
+
+ SphereMeshContactGenerationCallback_Scale(const TriangleMesh& meshData, const PxSphereGeometry& shapeSphere,
+ const PxTransform& transform0, const PxTransform& transform1, const Cm::FastVertex2ShapeScaling& meshScaling,
+ ContactBuffer& contactBuffer, const PxVec3& sphereCenterShape1Space, PxF32 inflatedRadius, Cm::RenderOutput* renderOutput
+ ) : SphereMeshContactGenerationCallback_NoScale(meshData, shapeSphere,
+ transform0, transform1, contactBuffer, sphereCenterShape1Space, inflatedRadius, renderOutput),
+ mMeshScaling (meshScaling)
+ {
+ }
+
+ virtual ~SphereMeshContactGenerationCallback_Scale() {}
+
+ virtual PxAgain processHit(const PxRaycastHit& hit, const PxVec3& v0, const PxVec3& v1, const PxVec3& v2, PxReal&, const PxU32* vinds)
+ {
+ PxVec3 verts[3];
+ getScaledVertices(verts, v0, v1, v2, false, mMeshScaling);
+
+ if(gDrawTouchedTriangles)
+ {
+ (*mGeneration.mRenderOutput) << 0xffffffff;
+ (*mGeneration.mRenderOutput) << PxMat44(PxIdentity);
+ const PxVec3 wp0 = mGeneration.mTransform1.transform(verts[0]);
+ const PxVec3 wp1 = mGeneration.mTransform1.transform(verts[1]);
+ const PxVec3 wp2 = mGeneration.mTransform1.transform(verts[2]);
+ mGeneration.mRenderOutput->outputSegment(wp0, wp1);
+ mGeneration.mRenderOutput->outputSegment(wp1, wp2);
+ mGeneration.mRenderOutput->outputSegment(wp2, wp0);
+ }
+
+ mGeneration.processTriangle(hit.faceIndex, verts[0], verts[1], verts[2], vinds);
+ return true;
+ }
+protected:
+ SphereMeshContactGenerationCallback_Scale &operator=(const SphereMeshContactGenerationCallback_Scale &);
+};
+
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool Gu::contactSphereMesh(GU_CONTACT_METHOD_ARGS)
+{
+ PX_UNUSED(cache);
+
+ const PxSphereGeometry& shapeSphere = shape0.get<const PxSphereGeometry>();
+ const PxTriangleMeshGeometryLL& shapeMesh = shape1.get<const PxTriangleMeshGeometryLL>();
+
+ // We must be in local space to use the cache
+ const PxVec3 sphereCenterInMeshSpace = transform1.transformInv(transform0.p);
+ const PxReal inflatedRadius = shapeSphere.radius + params.mContactDistance;
+ const TriangleMesh* meshData = shapeMesh.meshData;
+
+ // mesh scale is not baked into cached verts
+ if(shapeMesh.scale.isIdentity())
+ {
+ SphereMeshContactGenerationCallback_NoScale callback(
+ *meshData, shapeSphere, transform0, transform1,
+ contactBuffer, sphereCenterInMeshSpace, inflatedRadius, renderOutput);
+
+ // PT: TODO: switch to sphere query here
+ const Box obb(sphereCenterInMeshSpace, PxVec3(inflatedRadius), PxMat33(PxIdentity));
+ Midphase::intersectOBB(meshData, obb, callback, true);
+ }
+ else
+ {
+ const Cm::FastVertex2ShapeScaling meshScaling(shapeMesh.scale);
+
+ SphereMeshContactGenerationCallback_Scale callback(
+ *meshData, shapeSphere, transform0, transform1,
+ meshScaling, contactBuffer, sphereCenterInMeshSpace, inflatedRadius, renderOutput);
+
+ PxVec3 obbCenter = sphereCenterInMeshSpace;
+ PxVec3 obbExtents = PxVec3(inflatedRadius);
+ PxMat33 obbRot(PxIdentity);
+ meshScaling.transformQueryBounds(obbCenter, obbExtents, obbRot);
+
+ const Box obb(obbCenter, obbExtents, obbRot);
+
+ Midphase::intersectOBB(meshData, obb, callback, true);
+ }
+ return contactBuffer.count > 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+namespace
+{
+struct SphereHeightfieldContactGenerationCallback : EntityReport<PxU32>
+{
+ SphereMeshContactGeneration mGeneration;
+ HeightFieldUtil& mHfUtil;
+
+ SphereHeightfieldContactGenerationCallback(
+ HeightFieldUtil& hfUtil,
+ const PxSphereGeometry& shapeSphere,
+ const PxTransform& transform0,
+ const PxTransform& transform1,
+ ContactBuffer& contactBuffer,
+ const PxVec3& sphereCenterInMeshSpace,
+ PxF32 inflatedRadius,
+ Cm::RenderOutput* renderOutput
+ ) :
+ mGeneration (shapeSphere, transform0, transform1, contactBuffer, sphereCenterInMeshSpace, inflatedRadius, renderOutput),
+ mHfUtil (hfUtil)
+ {
+ }
+
+ // PT: TODO: refactor/unify with similar code in other places
+ virtual bool onEvent(PxU32 nb, PxU32* indices)
+ {
+ while(nb--)
+ {
+ const PxU32 triangleIndex = *indices++;
+ PxU32 vertIndices[3];
+ PxTriangle currentTriangle;
+ mHfUtil.getTriangle(mGeneration.mTransform1, currentTriangle, vertIndices, NULL, triangleIndex, false, false);
+
+ mGeneration.processTriangle(triangleIndex, currentTriangle.verts[0], currentTriangle.verts[1], currentTriangle.verts[2], vertIndices);
+ }
+ return true;
+ }
+protected:
+ SphereHeightfieldContactGenerationCallback &operator=(const SphereHeightfieldContactGenerationCallback &);
+};
+}
+
+bool Gu::contactSphereHeightfield(GU_CONTACT_METHOD_ARGS)
+{
+ PX_UNUSED(cache);
+ PX_UNUSED(renderOutput);
+
+ const PxSphereGeometry& shapeSphere = shape0.get<const PxSphereGeometry>();
+ const PxHeightFieldGeometryLL& shapeMesh = shape1.get<const PxHeightFieldGeometryLL>();
+ const HeightField& hf = *static_cast<HeightField*>(shapeMesh.heightField);
+
+ HeightFieldUtil hfUtil(shapeMesh, hf);
+
+ const PxVec3 sphereCenterInMeshSpace = transform1.transformInv(transform0.p);
+ const PxReal inflatedRadius = shapeSphere.radius + params.mContactDistance;
+ const PxVec3 inflatedRV3(inflatedRadius);
+
+ const PxBounds3 bounds(sphereCenterInMeshSpace - inflatedRV3, sphereCenterInMeshSpace + inflatedRV3);
+
+ SphereHeightfieldContactGenerationCallback blockCallback(hfUtil, shapeSphere, transform0, transform1, contactBuffer, sphereCenterInMeshSpace, inflatedRadius, renderOutput);
+
+ hfUtil.overlapAABBTriangles(transform1, bounds, 0, &blockCallback);
+
+ blockCallback.mGeneration.generateLastContacts();
+
+ return contactBuffer.count > 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
diff --git a/PhysX_3.4/Source/GeomUtils/src/contact/GuContactSpherePlane.cpp b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactSpherePlane.cpp
new file mode 100644
index 00000000..38446117
--- /dev/null
+++ b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactSpherePlane.cpp
@@ -0,0 +1,68 @@
+// 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 "GuContactBuffer.h"
+#include "GuContactMethodImpl.h"
+#include "GuGeometryUnion.h"
+
+namespace physx
+{
+namespace Gu
+{
+bool contactSpherePlane(GU_CONTACT_METHOD_ARGS)
+{
+ PX_UNUSED(renderOutput);
+ PX_UNUSED(cache);
+ PX_UNUSED(shape1);
+
+ // Get actual shape data
+ const PxSphereGeometry& shapeSphere = shape0.get<const PxSphereGeometry>();
+ //const PxPlaneGeometry& shapePlane = shape1.get<const PxPlaneGeometry>();
+
+ //Sphere in plane space
+ const PxVec3 sphere = transform1.transformInv(transform0.p);
+
+ //Make sure we have a normalized plane
+ //The plane is implicitly n=<1,0,0> d=0 (in plane-space)
+ //PX_ASSERT(PxAbs(shape1.mNormal.magnitudeSquared() - 1.0f) < 0.000001f);
+
+ //Separation
+ const PxReal separation = sphere.x - shapeSphere.radius;
+
+ if(separation<=params.mContactDistance)
+ {
+ const PxVec3 normal = transform1.q.getBasisVector0();
+ const PxVec3 point = transform0.p - normal * shapeSphere.radius;
+ contactBuffer.contact(point, normal, separation);
+ return true;
+ }
+ return false;
+}
+}//Gu
+}//physx
diff --git a/PhysX_3.4/Source/GeomUtils/src/contact/GuContactSphereSphere.cpp b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactSphereSphere.cpp
new file mode 100644
index 00000000..6146b1d2
--- /dev/null
+++ b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactSphereSphere.cpp
@@ -0,0 +1,68 @@
+// 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 "GuContactBuffer.h"
+#include "GuContactMethodImpl.h"
+#include "GuGeometryUnion.h"
+
+namespace physx
+{
+namespace Gu
+{
+bool contactSphereSphere(GU_CONTACT_METHOD_ARGS)
+{
+ PX_UNUSED(renderOutput);
+ PX_UNUSED(cache);
+
+ const PxSphereGeometry& sphereGeom0 = shape0.get<const PxSphereGeometry>();
+ const PxSphereGeometry& sphereGeom1 = shape1.get<const PxSphereGeometry>();
+
+ PxVec3 delta = transform0.p - transform1.p;
+
+ const PxReal distanceSq = delta.magnitudeSquared();
+ const PxReal radiusSum = sphereGeom0.radius + sphereGeom1.radius;
+ const PxReal inflatedSum = radiusSum + params.mContactDistance;
+ if(distanceSq >= inflatedSum*inflatedSum)
+ return false;
+
+ // We do a *manual* normalization to check for singularity condition
+ const PxReal magn = PxSqrt(distanceSq);
+ if(magn<=0.00001f)
+ delta = PxVec3(1.0f, 0.0f, 0.0f); // PT: spheres are exactly overlapping => can't create normal => pick up random one
+ else
+ delta *= 1.0f/magn;
+
+ // PT: TODO: why is this formula different from the original code?
+ const PxVec3 contact = delta * ((sphereGeom0.radius + magn - sphereGeom1.radius)*-0.5f) + transform0.p;
+
+ contactBuffer.contact(contact, delta, magn - radiusSum);
+ return true;
+}
+}//Gu
+}//physx
diff --git a/PhysX_3.4/Source/GeomUtils/src/contact/GuFeatureCode.cpp b/PhysX_3.4/Source/GeomUtils/src/contact/GuFeatureCode.cpp
new file mode 100644
index 00000000..f9bbd7e9
--- /dev/null
+++ b/PhysX_3.4/Source/GeomUtils/src/contact/GuFeatureCode.cpp
@@ -0,0 +1,128 @@
+// 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 "GuConvexEdgeFlags.h"
+#include "GuFeatureCode.h"
+
+using namespace physx;
+using namespace Gu;
+
+static FeatureCode computeFeatureCode(PxReal u, PxReal v)
+{
+ // Analysis
+ if(u==0.0f)
+ {
+ if(v==0.0f)
+ {
+ // Vertex 0
+ return FC_VERTEX0;
+ }
+ else if(v==1.0f)
+ {
+ // Vertex 2
+ return FC_VERTEX2;
+ }
+ else
+ {
+ // Edge 0-2
+ return FC_EDGE20;
+ }
+ }
+ else if(u==1.0f)
+ {
+ if(v==0.0f)
+ {
+ // Vertex 1
+ return FC_VERTEX1;
+ }
+ }
+ else
+ {
+ if(v==0.0f)
+ {
+ // Edge 0-1
+ return FC_EDGE01;
+ }
+ else
+ {
+ if((u+v)>=0.9999f)
+ {
+ // Edge 1-2
+ return FC_EDGE12;
+ }
+ else
+ {
+ // Face
+ return FC_FACE;
+ }
+ }
+ }
+ return FC_UNDEFINED;
+}
+
+
+bool Gu::selectNormal(PxU8 data, PxReal u, PxReal v)
+{
+ bool useFaceNormal = false;
+ const FeatureCode FC = computeFeatureCode(u, v);
+ switch(FC)
+ {
+ case FC_VERTEX0:
+ if(!(data & (Gu::ETD_CONVEX_EDGE_01|Gu::ETD_CONVEX_EDGE_20)))
+ useFaceNormal = true;
+ break;
+ case FC_VERTEX1:
+ if(!(data & (Gu::ETD_CONVEX_EDGE_01|Gu::ETD_CONVEX_EDGE_12)))
+ useFaceNormal = true;
+ break;
+ case FC_VERTEX2:
+ if(!(data & (Gu::ETD_CONVEX_EDGE_12|Gu::ETD_CONVEX_EDGE_20)))
+ useFaceNormal = true;
+ break;
+ case FC_EDGE01:
+ if(!(data & Gu::ETD_CONVEX_EDGE_01))
+ useFaceNormal = true;
+ break;
+ case FC_EDGE12:
+ if(!(data & Gu::ETD_CONVEX_EDGE_12))
+ useFaceNormal = true;
+ break;
+ case FC_EDGE20:
+ if(!(data & Gu::ETD_CONVEX_EDGE_20))
+ useFaceNormal = true;
+ break;
+ case FC_FACE:
+ useFaceNormal = true;
+ break;
+ case FC_UNDEFINED:
+ break;
+ };
+ return useFaceNormal;
+}
+
diff --git a/PhysX_3.4/Source/GeomUtils/src/contact/GuFeatureCode.h b/PhysX_3.4/Source/GeomUtils/src/contact/GuFeatureCode.h
new file mode 100644
index 00000000..541f2a67
--- /dev/null
+++ b/PhysX_3.4/Source/GeomUtils/src/contact/GuFeatureCode.h
@@ -0,0 +1,56 @@
+// 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.
+
+#ifndef GU_FEATURE_CODE_H
+#define GU_FEATURE_CODE_H
+
+#include "CmPhysXCommon.h"
+
+namespace physx
+{
+namespace Gu
+{
+ enum FeatureCode
+ {
+ FC_VERTEX0,
+ FC_VERTEX1,
+ FC_VERTEX2,
+ FC_EDGE01,
+ FC_EDGE12,
+ FC_EDGE20,
+ FC_FACE,
+
+ FC_UNDEFINED
+ };
+
+ bool selectNormal(PxU8 data, PxReal u, PxReal v);
+}
+}
+
+#endif
diff --git a/PhysX_3.4/Source/GeomUtils/src/contact/GuLegacyContactBoxHeightField.cpp b/PhysX_3.4/Source/GeomUtils/src/contact/GuLegacyContactBoxHeightField.cpp
new file mode 100644
index 00000000..8429fde3
--- /dev/null
+++ b/PhysX_3.4/Source/GeomUtils/src/contact/GuLegacyContactBoxHeightField.cpp
@@ -0,0 +1,476 @@
+// 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 "GuContactMethodImpl.h"
+#include "GuGeometryUnion.h"
+#include "GuHeightFieldUtil.h"
+#include "CmRenderBuffer.h"
+#include "GuContactBuffer.h"
+#include "GuContactMethodImpl.h"
+#include "GuLegacyTraceLineCallback.h"
+#include "PsFPU.h"
+
+#include "CmMatrix34.h"
+
+using namespace physx;
+
+#define DISTANCE_BASED_TEST
+
+/////////
+#if 0
+ #include "CmRenderOutput.h"
+ #include "PxsContext.h"
+ static void gVisualizeBox(const Gu::Box& box, PxcNpThreadContext& context, PxU32 color=0xffffff)
+ {
+ PxMat33 rot(box.rot.column0, box.rot.column1, box.rot.column2);
+ PxMat44 m(rot, box.center);
+
+ Cm::DebugBox db(box.extents);
+
+ Cm::RenderOutput& out = context.mRenderOutput;
+ out << color << m;
+ out << db;
+ }
+ static void gVisualizeLine(const PxVec3& a, const PxVec3& b, PxcNpThreadContext& context, PxU32 color=0xffffff)
+ {
+ PxMat44 m = PxMat44(PxIdentity);
+
+ Cm::RenderOutput& out = context.mRenderOutput;
+ out << color << m << Cm::RenderOutput::LINES << a << b;
+ }
+#endif
+/////////
+
+// ptchernev TODO: make sure these are ok before shipping
+static const bool gCompileBoxVertex = true;
+static const bool gCompileEdgeEdge = true;
+static const bool gCompileHeightFieldVertex = true;
+
+static const PxReal signs[24] =
+{
+ -1,-1,-1,
+ -1,-1, 1,
+ -1, 1,-1,
+ -1, 1, 1,
+ 1,-1,-1,
+ 1,-1, 1,
+ 1, 1,-1,
+ 1, 1, 1,
+};
+
+static const PxU8 edges[24] =
+{
+ 0,1,
+ 1,3,
+ 3,2,
+ 2,0,
+ 4,5,
+ 5,7,
+ 7,6,
+ 6,4,
+ 0,4,
+ 1,5,
+ 2,6,
+ 3,7,
+};
+
+static bool GuDepenetrateBox( const PxVec3& point,
+ const PxVec3& safeNormal,
+ const PxVec3& dimensions,
+ float /*contactDistance*/,
+ PxVec3& normal,
+ PxReal& distance)
+{
+ PxVec3 faceNormal(PxReal(0));
+ PxReal distance1 = -PX_MAX_REAL; // cant be more
+ PxReal distance2 = -PX_MAX_REAL; // cant be more
+ PxI32 poly1 = -1;
+ PxI32 poly2 = -2;
+
+ for (PxU32 poly = 0; poly < 6; poly++)
+ {
+ PxU32 dim = poly % 3;
+
+ PxReal sign = (poly > 2) ? -PxReal(1) : PxReal(1);
+
+ PxVec3 n(PxVec3(0));
+
+ n[dim] = sign;
+ PxReal proj = n[dim] * safeNormal[dim];
+ PxReal d = n[dim] * (point[dim] - sign * dimensions[dim]);
+
+#ifdef DISTANCE_BASED_TEST
+ // PT: I'm not really sure about contactDistance here
+ // AP: enabling this causes jitter in DE2740
+ //d -= contactDistance;
+#endif
+
+ if (d >= 0)
+ return false;
+
+ if (proj > 0)
+ {
+ if (d > distance1) // less penetration
+ {
+ distance1 = d;
+ faceNormal = n;
+ poly1 = PxI32(poly);
+ }
+
+ // distance2 / d = 1 / proj
+ PxReal tmp = d / proj;
+ if (tmp > distance2)
+ {
+ distance2 = tmp;
+ poly2 = PxI32(poly);
+ }
+ }
+ }
+
+ if (poly1 == poly2)
+ {
+ PX_ASSERT(faceNormal.magnitudeSquared() != 0.0f);
+ normal = faceNormal;
+ distance = -distance1;
+ }
+ else
+ {
+ normal = safeNormal;
+ distance = -distance2;
+ }
+
+ return true;
+}
+
+//Box-Heightfield and Convex-Heightfield do not support positive values for contactDistance,
+//and if in this case we would emit contacts normally, we'd cause things to jitter.
+//as a workaround we add contactDistance to the distance values that we emit in contacts.
+//this has the effect that the biasing will work exactly as if we had specified a legacy skinWidth of (contactDistance - restDistance)
+
+namespace physx
+{
+namespace Gu
+{
+bool legacyContactBoxHeightfield(GU_CONTACT_METHOD_ARGS)
+{
+ PX_UNUSED(renderOutput);
+ PX_UNUSED(cache);
+ //PXC_WARN_ONCE(contactDistance > 0.0f, "PxcContactBoxHeightField: Box-Heightfield does not support distance based contact generation! Ignoring contactOffset > 0!");
+
+ // Get actual shape data
+ const PxBoxGeometry& shapeBox = shape0.get<const PxBoxGeometry>();
+ const PxHeightFieldGeometryLL& hfGeom = shape1.get<const PxHeightFieldGeometryLL>();
+
+ const Gu::HeightField& hf = *static_cast<Gu::HeightField*>(hfGeom.heightField);
+ const Gu::HeightFieldUtil hfUtil(hfGeom, hf);
+
+ PX_ASSERT(contactBuffer.count==0);
+
+ Cm::Matrix34 boxShapeAbsPose(transform0);
+ Cm::Matrix34 hfShapeAbsPose(transform1);
+
+ const PxMat33& left = hfShapeAbsPose.m;
+ const PxMat33& right = boxShapeAbsPose.m;
+
+ Cm::Matrix34 boxShape2HfShape(left.getInverse()* right, left.getInverse()*(boxShapeAbsPose.p - hfShapeAbsPose.p));
+
+ // Get box vertices.
+ PxVec3 boxVertices[8];
+ PxVec3 boxVertexNormals[8];
+ for(PxU32 i=0; i<8; i++)
+ {
+ boxVertices[i] = PxVec3(shapeBox.halfExtents.x*signs[3*i], shapeBox.halfExtents.y*signs[3*i+1], shapeBox.halfExtents.z*signs[3*i+2]);
+
+ boxVertexNormals[i] = PxVec3(signs[3*i], signs[3*i+1], signs[3*i+2]);
+ boxVertexNormals[i].normalize();
+ }
+
+ // Transform box vertices to HeightFieldShape space.
+ PxVec3 boxVerticesInHfShape[8];
+ PxVec3 boxVertexNormalsInHfShape[8];
+ for(PxU32 i=0; i<8; i++)
+ {
+ boxVerticesInHfShape[i] = boxShape2HfShape.transform(boxVertices[i]);
+ boxVertexNormalsInHfShape[i] = boxShape2HfShape.rotate(boxVertexNormals[i]);
+ }
+
+ // bounds of box on HeightField.
+ PxVec3 aabbMin(boxVerticesInHfShape[0]);
+ PxVec3 aabbMax(boxVerticesInHfShape[0]);
+ for(PxU32 i=1; i<8; i++)
+ {
+ for(PxU32 dim = 0; dim < 3; dim++)
+ {
+ aabbMin[dim] = PxMin(aabbMin[dim], boxVerticesInHfShape[i][dim]);
+ aabbMax[dim] = PxMax(aabbMax[dim], boxVerticesInHfShape[i][dim]);
+ }
+ }
+
+ const bool thicknessNegOrNull = (hf.getThicknessFast() <= 0.0f); // PT: don't do this each time! FCMPs are slow.
+
+ // Compute the height field extreme over the bounds area.
+// PxReal hfExtreme = thicknessNegOrNull ? -PX_MAX_REAL : PX_MAX_REAL;
+ // PT: we already computed those!
+// const PxReal oneOverRowScale = 1.0f / shapeHeightField.rowScale;
+// const PxReal oneOverColumnScale = 1.0f / shapeHeightField.columnScale;
+ const PxReal oneOverRowScale = hfUtil.getOneOverRowScale();
+ const PxReal oneOverColumnScale = hfUtil.getOneOverColumnScale();
+
+ PxU32 minRow;
+ PxU32 maxRow;
+ PxU32 minColumn;
+ PxU32 maxColumn;
+
+ if (hfGeom.rowScale < 0)
+ {
+ minRow = hf.getMinRow(aabbMax.x * oneOverRowScale);
+ maxRow = hf.getMaxRow(aabbMin.x * oneOverRowScale);
+ }
+ else
+ {
+ minRow = hf.getMinRow(aabbMin.x * oneOverRowScale);
+ maxRow = hf.getMaxRow(aabbMax.x * oneOverRowScale);
+ }
+
+ if (hfGeom.columnScale < 0)
+ {
+ minColumn = hf.getMinColumn(aabbMax.z * oneOverColumnScale);
+ maxColumn = hf.getMaxColumn(aabbMin.z * oneOverColumnScale);
+ }
+ else
+ {
+ minColumn = hf.getMinColumn(aabbMin.z * oneOverColumnScale);
+ maxColumn = hf.getMaxColumn(aabbMax.z * oneOverColumnScale);
+ }
+
+ PxReal hfExtreme = hf.computeExtreme(minRow, maxRow, minColumn, maxColumn);
+
+ //hfExtreme *= hfShape.getHeightScale();
+ hfExtreme *= hfGeom.heightScale;
+
+ // Return if convex is on the wrong side of the extreme.
+ if (thicknessNegOrNull)
+ {
+ if (aabbMin.y > hfExtreme) return false;
+ }
+ else
+ {
+ if (aabbMax.y < hfExtreme) return false;
+ }
+
+ // Test box vertices.
+ if (gCompileBoxVertex)
+ {
+ for(PxU32 i=0; i<8; i++)
+ {
+ const int32_t* tmp = reinterpret_cast<const int32_t*>(&boxVertexNormalsInHfShape[i].y);
+ // PT: orientation culling
+ if(*tmp>0)
+// if(PX_SIR(boxVertexNormalsInHfShape[i].y)>0)
+ continue;
+
+ const PxVec3& boxVertexInHfShape = boxVerticesInHfShape[i];
+#if 0
+ PxVec3 pt = boxShapeAbsPose.transform(boxVertices[i]);
+ PxVec3 worldNormal = boxShapeAbsPose.rotate(boxVertexNormals[i]);
+ //gVisualizeLine(pt, pt+PxVec3(1.0f,0.0f,0.0f), context, PxDebugColor::eARGB_RED);
+ //gVisualizeLine(pt, pt+PxVec3(0.0f,1.0f,0.0f), context, PxDebugColor::eARGB_GREEN);
+ //gVisualizeLine(pt, pt+PxVec3(0.0f,0.0f,1.0f), context, PxDebugColor::eARGB_BLUE);
+ gVisualizeLine(pt, pt+worldNormal, context, PxDebugColor::eARGB_MAGENTA);
+#endif
+
+//////// SAME CODE AS IN CONVEX-HF
+ const bool insideExtreme =
+ thicknessNegOrNull ? (boxVertexInHfShape.y < hfExtreme + params.mContactDistance) : (boxVertexInHfShape.y > hfExtreme-params.mContactDistance);
+
+ //if (insideExtreme && hfShape.isShapePointOnHeightField(boxVertexInHfShape.x, boxVertexInHfShape.z))
+ if (insideExtreme && hfUtil.isShapePointOnHeightField(boxVertexInHfShape.x, boxVertexInHfShape.z))
+ {
+ //PxReal y = hfShape.getHeightAtShapePoint(boxVertexInHfShape.x, boxVertexInHfShape.z);
+// const PxReal y = hfUtil.getHeightAtShapePoint(boxVertexInHfShape.x, boxVertexInHfShape.z);
+
+ // PT: compute this once, reuse results (3 times!)
+ // PT: TODO: also reuse this in EE tests
+ PxReal fracX, fracZ;
+ const PxU32 vertexIndex = hfUtil.getHeightField().computeCellCoordinates(
+ boxVertexInHfShape.x * oneOverRowScale, boxVertexInHfShape.z * oneOverColumnScale, fracX, fracZ);
+
+ const PxReal y = hfUtil.getHeightAtShapePoint2(vertexIndex, fracX, fracZ);
+
+ const PxReal dy = boxVertexInHfShape.y - y;
+#ifdef DISTANCE_BASED_TEST
+ if (hf.isDeltaHeightInsideExtent(dy, params.mContactDistance))
+#else
+ if (hf.isDeltaHeightInsideExtent(dy))
+#endif
+ {
+ const PxU32 faceIndex = hfUtil.getFaceIndexAtShapePointNoTest2(vertexIndex, fracX, fracZ);
+ if (faceIndex != 0xffffffff)
+ {
+// PxcMaterialIndex material = hfShape.getTriangleMaterial(feature);
+ PxVec3 n;
+ //n = hfShape.getNormalAtShapePoint(boxVertexInHfShape.x, boxVertexInHfShape.z);
+// n = hfUtil.getNormalAtShapePoint(boxVertexInHfShape.x, boxVertexInHfShape.z);
+ n = hfUtil.getNormalAtShapePoint2(vertexIndex, fracX, fracZ);
+ n = n.getNormalized();
+
+ contactBuffer
+#ifdef DISTANCE_BASED_TEST
+ .contact(boxShapeAbsPose.transform(boxVertices[i]), hfShapeAbsPose.rotate(n), n.y*dy/* + contactDistance*/, faceIndex);
+#else
+ .contact(boxShapeAbsPose.transform(boxVertices[i]), hfShapeAbsPose.rotate(n), n.y*dy + contactDistance, feature);//add contactDistance to compensate for fact that we don't support dist based contacts! See comment at start of funct.
+#endif
+ }
+ }
+ }
+////////~SAME CODE AS IN CONVEX-HF
+ }
+ }
+
+ // Test box edges.
+ if (gCompileEdgeEdge)
+ {
+ // create helper class for the trace segment
+ GuContactHeightfieldTraceSegmentHelper traceSegmentHelper(hfUtil);
+
+ for(PxU32 i=0; i<12; i++)
+ {
+// PT: orientation culling
+//float worldNormalY = (boxVertexNormalsInHfShape[edges[2*i]].y + boxVertexNormalsInHfShape[edges[2*i+1]].y)*0.5f;
+float worldNormalY = boxVertexNormalsInHfShape[edges[2*i]].y + boxVertexNormalsInHfShape[edges[2*i+1]].y;
+if(worldNormalY>0.0f)
+ continue;
+
+
+ const PxVec3& v0 = boxVerticesInHfShape[edges[2*i]];
+ const PxVec3& v1 = boxVerticesInHfShape[edges[2*i+1]];
+
+#if 0
+ PxVec3 pt0 = boxShapeAbsPose.transform(boxVertices[edges[2*i]]);
+ PxVec3 pt1 = boxShapeAbsPose.transform(boxVertices[edges[2*i+1]]);
+
+ PxVec3 worldNormal0 = boxShapeAbsPose.rotate(boxVertexNormals[edges[2*i]]);
+ PxVec3 worldNormal1 = boxShapeAbsPose.rotate(boxVertexNormals[edges[2*i+1]]);
+
+ PxVec3 pt = (pt0 + pt1)*0.5f;
+ PxVec3 worldNormal = (worldNormal0 + worldNormal1)*0.5f;
+
+ gVisualizeLine(pt, pt+worldNormal, context, PxDebugColor::eARGB_CYAN);
+#endif
+
+
+
+ if (hf.getThicknessFast()) // PT: errr...? not the same test as in the convex code?
+ {
+ if ((v0.y > hfExtreme) && (v1.y > hfExtreme)) continue;
+ }
+ else
+ {
+ if ((v0.y < hfExtreme) && (v1.y < hfExtreme)) continue;
+ }
+ GuContactTraceSegmentCallback cb(v1 - v0,
+ contactBuffer,
+ hfShapeAbsPose, params.mContactDistance/*, context.mRenderOutput*/);
+
+ //context.mRenderOutput << PxVec3(1,0,0) << Gu::Debug::convertToPxMat44(transform1)
+ // << Cm::RenderOutput::LINES << v0+PxVec3(0.01f) << v1+PxVec3(0.01f);
+ //hfShape.traceSegment<PxcContactTraceSegmentCallback>(v0, v1, &cb);
+ traceSegmentHelper.traceSegment(v0, v1, &cb);
+ }
+ }
+
+ // Test HeightField vertices.
+ if (gCompileHeightFieldVertex)
+ {
+ // Iterate over all HeightField vertices inside the bounds.
+ for(PxU32 row = minRow; row <= maxRow; row++)
+ {
+ for(PxU32 column = minColumn; column <= maxColumn; column++)
+ {
+ const PxU32 vertexIndex = row * hf.getNbColumnsFast() + column;
+
+ //if (!hfShape.isCollisionVertex(vertexIndex)) continue;
+ if (!hfUtil.isCollisionVertex(vertexIndex, row, column)) continue;
+
+ // Check if hf vertex is inside the box.
+ //PxVec3 hfVertex;
+ //hfVertex.set(hfShape.getRowScale() * row, hfShape.getHeightScale() * hfShape.getHeight(vertexIndex), hfShape.getColumnScale() * column);
+// const PxVec3 hfVertex(shapeHeightField.rowScale * row, shapeHeightField.columnScale * hfShape.getHeight(vertexIndex), shapeHeightField.columnScale * column);
+ const PxVec3 hfVertex(hfGeom.rowScale * row, hfGeom.heightScale * hf.getHeight(vertexIndex), hfGeom.columnScale * column);
+
+ const PxVec3 hfVertexInBoxShape = boxShape2HfShape.transformTranspose(hfVertex);
+ if ((PxAbs(hfVertexInBoxShape.x) - shapeBox.halfExtents.x - params.mContactDistance < 0)
+ && (PxAbs(hfVertexInBoxShape.y) - shapeBox.halfExtents.y - params.mContactDistance < 0)
+ && (PxAbs(hfVertexInBoxShape.z) - shapeBox.halfExtents.z - params.mContactDistance < 0))
+ {
+
+ // ptchernev: should have done this in HeightFieldShape
+ // check if this really is a collision vertex
+ //PxVec3 hfVertexNormal = thicknessNegOrNull ? hfShape.getVertexNormal(vertexIndex) : -hfShape.getVertexNormal(vertexIndex);
+// PxVec3 hfVertexNormal = thicknessNegOrNull ? hfUtil.getVertexNormal(vertexIndex) : -hfUtil.getVertexNormal(vertexIndex);
+ const PxVec3 nrm = hfUtil.getVertexNormal(vertexIndex, row, column);
+ PxVec3 hfVertexNormal = thicknessNegOrNull ? nrm : -nrm;
+ hfVertexNormal = hfVertexNormal.getNormalized();
+ const PxVec3 hfVertexNormalInBoxShape = boxShape2HfShape.rotateTranspose(hfVertexNormal);
+ PxVec3 normal;
+ PxReal depth;
+ if (!GuDepenetrateBox(hfVertexInBoxShape, -hfVertexNormalInBoxShape, shapeBox.halfExtents, params.mContactDistance, normal, depth))
+ {
+ continue;
+ }
+
+// PxMat33 rot(boxShape2HfShape[0],boxShape2HfShape[1],boxShape2HfShape[2]);
+// PxVec3 normalInHfShape = rot * (-normal);
+ PxVec3 normalInHfShape = boxShape2HfShape.rotate(-normal);
+ //hfShape.clipShapeNormalToVertexVoronoi(normalInHfShape, vertexIndex);
+ hfUtil.clipShapeNormalToVertexVoronoi(normalInHfShape, vertexIndex, row, column);
+ if (normalInHfShape.dot(hfVertexNormal) < PX_EPS_REAL)
+ {
+ // hmm, I dont think this can happen
+ continue;
+ }
+ normalInHfShape = normalInHfShape.getNormalized();
+ const PxU32 faceIndex = hfUtil.getVertexFaceIndex(vertexIndex, row, column);
+ contactBuffer
+ .contact(hfShapeAbsPose.transform(hfVertex), hfShapeAbsPose.rotate(normalInHfShape),
+#ifdef DISTANCE_BASED_TEST
+ -depth,
+#else
+ -depth + contactDistance, //add contactDistance to compensate for fact that we don't support dist based contacts! See comment at start of funct.
+#endif
+ faceIndex);
+ }
+ }
+ }
+ }
+
+ return contactBuffer.count > 0;
+}
+}//Gu
+}//physx
diff --git a/PhysX_3.4/Source/GeomUtils/src/contact/GuLegacyContactCapsuleHeightField.cpp b/PhysX_3.4/Source/GeomUtils/src/contact/GuLegacyContactCapsuleHeightField.cpp
new file mode 100644
index 00000000..a21a281d
--- /dev/null
+++ b/PhysX_3.4/Source/GeomUtils/src/contact/GuLegacyContactCapsuleHeightField.cpp
@@ -0,0 +1,279 @@
+// 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 "GuDistancePointSegment.h"
+#include "GuDistanceSegmentSegment.h"
+#include "GuGeometryUnion.h"
+#include "GuHeightFieldData.h"
+#include "GuHeightFieldUtil.h"
+#include "GuContactMethodImpl.h"
+#include "GuContactBuffer.h"
+#include "GuContactMethodImpl.h"
+#include "GuInternal.h"
+
+#define DO_EDGE_EDGE 1
+#define DEBUG_HFNORMAL 0
+#define DEBUG_HFNORMALV 0
+#define DEBUG_RENDER_HFCONTACTS 0
+
+#if DEBUG_RENDER_HFCONTACTS
+#include "PxPhysics.h"
+#include "PxScene.h"
+#endif
+
+using namespace physx;
+using namespace Gu;
+
+bool GuContactSphereHeightFieldShared(GU_CONTACT_METHOD_ARGS, bool isCapsule);
+
+namespace physx
+{
+namespace Gu
+{
+bool legacyContactCapsuleHeightfield(GU_CONTACT_METHOD_ARGS)
+{
+ // Get actual shape data
+ const PxCapsuleGeometry& shapeCapsule = shape0.get<const PxCapsuleGeometry>();
+ const PxHeightFieldGeometryLL& hfGeom = shape1.get<const PxHeightFieldGeometryLL>();
+
+ const HeightField& hf = *static_cast<HeightField*>(hfGeom.heightField);
+ const HeightFieldUtil hfUtil(hfGeom, hf);
+
+ const PxReal radius = shapeCapsule.radius;
+ const PxReal inflatedRadius = shapeCapsule.radius + params.mContactDistance;
+ const PxReal radiusSquared = inflatedRadius * inflatedRadius;
+ const PxReal halfHeight = shapeCapsule.halfHeight;
+ const PxReal eps = PxReal(0.1)*radius;
+ const PxReal epsSqr = eps*eps;
+ const PxReal oneOverRowScale = hfUtil.getOneOverRowScale();
+ const PxReal oneOverColumnScale = hfUtil.getOneOverColumnScale();
+ const PxReal radiusOverRowScale = inflatedRadius * PxAbs(oneOverRowScale);
+ const PxReal radiusOverColumnScale = inflatedRadius * PxAbs(oneOverColumnScale);
+ const PxTransform capsuleShapeToHfShape = transform1.transformInv(transform0);
+
+ PxVec3 verticesInHfShape[2];
+ verticesInHfShape[0] = capsuleShapeToHfShape.transform(PxVec3(-halfHeight, 0, 0));
+ verticesInHfShape[1] = capsuleShapeToHfShape.transform(PxVec3(halfHeight, 0, 0));
+
+ PX_ASSERT(contactBuffer.count==0);
+ Gu::GeometryUnion u;
+ u.set(PxSphereGeometry(radius));
+ PxTransform ts0(transform1.transform(verticesInHfShape[0])), ts1(transform1.transform(verticesInHfShape[1]));
+ GuContactSphereHeightFieldShared(u, shape1, ts0, transform1, params, cache, contactBuffer, renderOutput, true);
+ GuContactSphereHeightFieldShared(u, shape1, ts1, transform1, params, cache, contactBuffer, renderOutput, true);
+
+ Segment worldCapsule;
+ worldCapsule.p0 = -getCapsuleHalfHeightVector(transform0, shapeCapsule);
+ worldCapsule.p1 = -worldCapsule.p0;
+ worldCapsule.p0 += transform0.p;
+ worldCapsule.p1 += transform0.p;
+
+ const Segment capsuleSegmentInHfShape(verticesInHfShape[0], verticesInHfShape[1]);
+
+ const PxU32 numCapsuleVertexContacts = contactBuffer.count; // remember how many contacts were stored as capsule vertex vs hf
+
+ // test capsule edges vs HF
+ PxVec3 v0h = hfUtil.shape2hfp(verticesInHfShape[0]), v1h = hfUtil.shape2hfp(verticesInHfShape[1]);
+ const PxU32 absMinRow = hf.getMinRow(PxMin(v0h.x - radiusOverRowScale, v1h.x - radiusOverRowScale));
+ const PxU32 absMaxRow = hf.getMaxRow(PxMax(v0h.x + radiusOverRowScale, v1h.x + radiusOverRowScale));
+ const PxU32 absMinCol = hf.getMinColumn(PxMin(v0h.z - radiusOverColumnScale, v1h.z - radiusOverColumnScale));
+ const PxU32 absMaxCol = hf.getMaxColumn(PxMax(v0h.z + radiusOverColumnScale, v1h.z + radiusOverColumnScale));
+ if (DO_EDGE_EDGE)
+ for(PxU32 row = absMinRow; row <= absMaxRow; row++)
+ {
+ for(PxU32 column = absMinCol; column <= absMaxCol; column++)
+ {
+ //PxU32 vertexIndex = row * hfShape.getNbColumnsFast() + column;
+ const PxU32 vertexIndex = row * hf.getNbColumnsFast() + column;
+ const PxU32 firstEdge = 3 * vertexIndex;
+
+ // omg I am sorry about this code but I can't find a simpler way:
+ // last column will only test edge 2
+ // last row will only test edge 0
+ // and most importantly last row and column will not go inside the for
+ const PxU32 minEi = PxU32((column == absMaxCol) ? 2 : 0);
+ const PxU32 maxEi = PxU32((row == absMaxRow) ? 1 : 3);
+ // perform capsule edge vs HF edge collision
+ for (PxU32 ei = minEi; ei < maxEi; ei++)
+ {
+ const PxU32 edgeIndex = firstEdge + ei;
+
+ PX_ASSERT(vertexIndex == edgeIndex / 3);
+ PX_ASSERT(row == vertexIndex / hf.getNbColumnsFast());
+ PX_ASSERT(column == vertexIndex % hf.getNbColumnsFast());
+
+ // look up the face indices adjacent to the current edge
+ PxU32 adjFaceIndices[2];
+ const PxU32 adjFaceCount = hf.getEdgeTriangleIndices(edgeIndex, adjFaceIndices);
+ bool doCollision = false;
+ if(adjFaceCount == 2)
+ {
+ doCollision = hf.getMaterialIndex0(adjFaceIndices[0] >> 1) != PxHeightFieldMaterial::eHOLE
+ || hf.getMaterialIndex1(adjFaceIndices[1] >> 1) != PxHeightFieldMaterial::eHOLE;
+ }
+ else if(adjFaceCount == 1)
+ {
+ doCollision = (hf.getMaterialIndex0(adjFaceIndices[0] >> 1) != PxHeightFieldMaterial::eHOLE);
+ }
+
+ if(doCollision)
+ {
+ PxVec3 origin;
+ PxVec3 direction;
+ hfUtil.getEdge(edgeIndex, vertexIndex, row, column, origin, direction);
+
+ PxReal s, t;
+ const PxReal ll = distanceSegmentSegmentSquared(
+ capsuleSegmentInHfShape.p0, capsuleSegmentInHfShape.computeDirection(), origin, direction, &s, &t);
+ if ((ll < radiusSquared) && (t >= 0) && (t <= 1))
+ {
+
+ // We only want to test the vertices for either rows or columns.
+ // In this case we have chosen rows (ei == 0).
+ if (ei != 0 && (t == 0 || t == 1))
+ continue;
+
+ const PxVec3 pointOnCapsuleInHfShape = capsuleSegmentInHfShape.getPointAt(s);
+ const PxVec3 pointOnEdge = origin + t * direction;
+ const PxVec3 d = pointOnCapsuleInHfShape - pointOnEdge;
+ //if (hfShape.isDeltaHeightOppositeExtent(d.y))
+ if (hf.isDeltaHeightOppositeExtent(d.y))
+ {
+
+ // Check if the current edge's normal is within any of it's 2 adjacent faces' Voronoi regions
+ // If it is, force the normal to that region's face normal
+ PxReal l;
+ PxVec3 n = hfUtil.computePointNormal(hfGeom.heightFieldFlags, d, transform1, ll, pointOnEdge.x, pointOnEdge.z, epsSqr, l);
+ PxVec3 localN = transform1.rotateInv(n);
+ for (PxU32 j = 0; j < adjFaceCount; j++)
+ {
+ const PxVec3 adjNormal = hfUtil.hf2shapen(hf.getTriangleNormalInternal(adjFaceIndices[j])).getNormalized();
+ PxU32 triCell = adjFaceIndices[j] >> 1;
+ PxU32 triRow = triCell/hf.getNbColumnsFast();
+ PxU32 triCol = triCell%hf.getNbColumnsFast();
+ PxVec3 tv0, tv1, tv2, tvc;
+ hf.getTriangleVertices(adjFaceIndices[j], triRow, triCol, tv0, tv1, tv2);
+ tvc = hfUtil.hf2shapep((tv0+tv1+tv2)/3.0f); // compute adjacent triangle center
+ PxVec3 perp = adjNormal.cross(direction).getNormalized(); // adj face normal cross edge dir
+ if (perp.dot(tvc-origin) < 0.0f) // make sure perp is pointing toward the center of the triangle
+ perp = -perp;
+ // perp is now a vector sticking out of the edge of the triangle (also the test edge) pointing toward the center
+ // perpendicular to the normal (in triangle plane)
+ if (perp.dot(localN) > 0.0f) // if the normal is in perp halfspace, clamp it to Voronoi region
+ {
+ n = transform1.rotate(adjNormal);
+ break;
+ }
+ }
+
+ const PxVec3 worldPoint = worldCapsule.getPointAt(s);
+ const PxVec3 p = worldPoint - n * radius;
+ PxU32 adjTri = adjFaceIndices[0];
+ if(adjFaceCount == 2)
+ {
+ const PxU16 m0 = hf.getMaterialIndex0(adjFaceIndices[0] >> 1);
+ if(m0 == PxHeightFieldMaterial::eHOLE)
+ adjTri = adjFaceIndices[1];
+ }
+ contactBuffer.contact(p, n, l-radius, adjTri);
+ #if DEBUG_HFNORMAL
+ printf("n=%.5f %.5f %.5f; d=%.5f\n", n.x, n.y, n.z, l-radius);
+ #if DEBUG_RENDER_HFCONTACTS
+ PxScene *s; PxGetPhysics().getScenes(&s, 1, 0);
+ Cm::RenderOutput((Cm::RenderBuffer&)s->getRenderBuffer()) << Cm::RenderOutput::LINES << PxDebugColor::eARGB_BLUE // red
+ << p << (p + n * 10.0f);
+ #endif
+ #endif
+ }
+ }
+ }
+ }
+
+ // also perform capsule edge vs HF vertex collision
+ if (hfUtil.isCollisionVertex(vertexIndex, row, column))
+ {
+ PxVec3 vertex(row * hfGeom.rowScale, hfGeom.heightScale * hf.getHeight(vertexIndex), column * hfGeom.columnScale);
+ PxReal s;
+ const PxReal ll = distancePointSegmentSquared(capsuleSegmentInHfShape, vertex, &s);
+ if (ll < radiusSquared)
+ {
+ const PxVec3 pointOnCapsuleInHfShape = capsuleSegmentInHfShape.getPointAt(s);
+ const PxVec3 d = pointOnCapsuleInHfShape - vertex;
+ //if (hfShape.isDeltaHeightOppositeExtent(d.y))
+ if (hf.isDeltaHeightOppositeExtent(d.y))
+ {
+ // we look through all prior capsule vertex vs HF face contacts and see
+ // if any of those share a face with hf_edge for the currently considered capsule_edge/hf_vertex contact
+ bool normalFromFace = false;
+ PxVec3 n;
+ PxReal l = 1.0f;
+ for (PxU32 iVertexContact = 0; iVertexContact < numCapsuleVertexContacts; iVertexContact++)
+ {
+ const ContactPoint& cp = contactBuffer.contacts[iVertexContact];
+ PxU32 vi0, vi1, vi2;
+ hf.getTriangleVertexIndices(cp.internalFaceIndex1, vi0, vi1, vi2);
+
+ const PxU32 vi = vertexIndex;
+ if ((cp.forInternalUse == 0) // if this is a face contact
+ && (vi == vi0 || vi == vi1 || vi == vi2)) // with one of the face's vertices matching this one
+ {
+ n = cp.normal; // then copy the normal from this contact
+ l = PxAbs(d.dot(n));
+ normalFromFace = true;
+ break;
+ }
+ }
+
+ if (!normalFromFace)
+ n = hfUtil.computePointNormal(hfGeom.heightFieldFlags, d, transform1, ll, vertex.x, vertex.z, epsSqr, l);
+
+ const PxVec3 worldPoint = worldCapsule.getPointAt(s);
+
+ const PxU32 faceIndex = hfUtil.getVertexFaceIndex(vertexIndex, row, column);
+
+ const PxVec3 p = worldPoint - n * radius;
+ contactBuffer.contact(p, n, l-radius, faceIndex);
+ #if DEBUG_HFNORMAL
+ printf("n=%.5f %.5f %.5f; d=%.5f\n", n.x, n.y, n.z, l-radius);
+ #if DEBUG_RENDER_HFCONTACTS
+ PxScene *s; PxGetPhysics().getScenes(&s, 1, 0);
+ Cm::RenderOutput((Cm::RenderBuffer&)s->getRenderBuffer()) << Cm::RenderOutput::LINES << PxDebugColor::eARGB_BLUE // red
+ << p << (p + n * 10.0f);
+ #endif
+ #endif
+ }
+ } // if ll < radiusSquared
+ } // if isCollisionVertex
+ } // forEach HF column intersecting with capsule edge AABB
+ } // forEach HF row intersecting with capsule edge AABB
+
+ return contactBuffer.count>0;
+}
+}//Gu
+}//physx
diff --git a/PhysX_3.4/Source/GeomUtils/src/contact/GuLegacyContactConvexHeightField.cpp b/PhysX_3.4/Source/GeomUtils/src/contact/GuLegacyContactConvexHeightField.cpp
new file mode 100644
index 00000000..4103714c
--- /dev/null
+++ b/PhysX_3.4/Source/GeomUtils/src/contact/GuLegacyContactConvexHeightField.cpp
@@ -0,0 +1,430 @@
+// 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 "GuLegacyTraceLineCallback.h"
+#include "GuConvexMeshData.h"
+#include "GuEdgeCache.h"
+#include "GuConvexHelper.h"
+#include "GuContactMethodImpl.h"
+#include "GuContactBuffer.h"
+
+using namespace physx;
+using namespace Gu;
+using namespace shdfnd::aos;
+
+#define DISTANCE_BASED_TEST
+
+// ptchernev TODO: make sure these are ok before shipping
+static const bool gCompileConvexVertex = true;
+static const bool gCompileEdgeEdge = true;
+static const bool gCompileHeightFieldVertex = true;
+
+/**
+\param point vertex tested for penetration (in local hull space)
+\param safeNormal if none of the faces are good this becomes the normal
+\param normal the direction to translate vertex to depenetrate
+\param distance the distance along normal to translate vertex to depenetrate
+*/
+static bool GuDepenetrateConvex( const PxVec3& point,
+ const PxVec3& safeNormal,
+ const Gu::ConvexHullData& hull,
+ float contactDistance,
+ PxVec3& normal,
+ PxReal& distance,
+ const Cm::FastVertex2ShapeScaling& scaling,
+ bool isConvexScaleIdentity)
+{
+ PxVec3 faceNormal(PxReal(0));
+ PxReal distance1 = -PX_MAX_REAL; // cant be more
+ PxReal distance2 = -PX_MAX_REAL; // cant be more
+ PxI32 poly1 = -1;
+ PxI32 poly2 = -2;
+
+// const Cm::FastVertex2ShapeScaling& scaling = context.mVertex2ShapeSkew[0];
+
+ for (PxU32 poly = 0; poly < hull.mNbPolygons; poly++)
+ {
+ PX_ALIGN(16, PxPlane) shapeSpacePlane;
+ if(isConvexScaleIdentity)
+ {
+ V4StoreA(V4LoadU(&hull.mPolygons[poly].mPlane.n.x), &shapeSpacePlane.n.x);
+ }
+ else
+ {
+ const PxPlane& vertSpacePlane = hull.mPolygons[poly].mPlane;
+ scaling.transformPlaneToShapeSpace(vertSpacePlane.n, vertSpacePlane.d, shapeSpacePlane.n, shapeSpacePlane.d);//transform plane into shape space
+ }
+
+#ifdef DISTANCE_BASED_TEST
+ // PT: I'm not really sure about contactDistance here
+ const PxReal d = shapeSpacePlane.distance(point) - contactDistance;
+#else
+ const PxReal d = shapeSpacePlane.distance(point);
+#endif
+
+ if (d >= 0)
+ {
+ // no penetration at all
+ return false;
+ }
+
+ //const PxVec3& n = plane.normal;
+ const PxReal proj = shapeSpacePlane.n.dot(safeNormal);
+ if (proj > 0)
+ {
+ if (d > distance1) // less penetration
+ {
+ distance1 = d;
+ faceNormal = shapeSpacePlane.n;
+ poly1 = PxI32(poly);
+ }
+
+ // distance2 / d = 1 / proj
+ const PxReal tmp = d / proj;
+ if (tmp > distance2)
+ {
+ distance2 = tmp;
+ poly2 = PxI32(poly);
+ }
+ }
+ }
+
+ if (poly1 == poly2)
+ {
+ PX_ASSERT(faceNormal.magnitudeSquared() != 0.0f);
+ normal = faceNormal;
+ distance = -distance1;
+ }
+ else
+ {
+ normal = safeNormal;
+ distance = -distance2;
+ }
+
+ return true;
+}
+
+//Box-Heightfield and Convex-Heightfield do not support positive values for contactDistance,
+//and if in this case we would emit contacts normally, we'd cause things to jitter.
+//as a workaround we add contactDistance to the distance values that we emit in contacts.
+//this has the effect that the biasing will work exactly as if we had specified a legacy skinWidth of (contactDistance - restDistance)
+
+#include "GuContactMethodImpl.h"
+
+namespace physx
+{
+namespace Gu
+{
+bool legacyContactConvexHeightfield(GU_CONTACT_METHOD_ARGS)
+{
+ PX_UNUSED(cache);
+ PX_UNUSED(renderOutput);
+
+#ifndef DISTANCE_BASED_TEST
+ PX_WARN_ONCE(contactDistance > 0.0f, "PxcContactConvexHeightField: Convex-Heightfield does not support distance based contact generation! Ignoring contactOffset > 0!");
+#endif
+
+ // Get actual shape data
+ const PxConvexMeshGeometryLL& shapeConvex = shape0.get<const PxConvexMeshGeometryLL>();
+ const PxHeightFieldGeometryLL& hfGeom = shape1.get<const PxHeightFieldGeometryLL>();
+
+ Cm::Matrix34 convexShapeAbsPose(transform0);
+ Cm::Matrix34 hfShapeAbsPose(transform1);
+
+ PX_ASSERT(contactBuffer.count==0);
+
+ const Gu::HeightField& hf = *static_cast<Gu::HeightField*>(hfGeom.heightField);
+ const Gu::HeightFieldUtil hfUtil(hfGeom, hf);
+
+ const bool isConvexScaleIdentity = shapeConvex.scale.isIdentity();
+ Cm::FastVertex2ShapeScaling convexScaling; // PT: TODO: remove default ctor
+ if(!isConvexScaleIdentity)
+ convexScaling.init(shapeConvex.scale);
+
+ const PxMat33& left = hfShapeAbsPose.m;
+ const PxMat33& right = convexShapeAbsPose.m;
+
+ Cm::Matrix34 convexShape2HfShape(left.getInverse()* right, left.getInverse()*(convexShapeAbsPose.p - hfShapeAbsPose.p));
+ Cm::Matrix34 convexVertex2World( right * convexScaling.getVertex2ShapeSkew(),convexShapeAbsPose.p );
+
+ // Allocate space for transformed vertices.
+ const Gu::ConvexHullData* PX_RESTRICT hull = shapeConvex.hullData;
+ PxVec3* PX_RESTRICT convexVerticesInHfShape = reinterpret_cast<PxVec3*>(PxAlloca(hull->mNbHullVertices*sizeof(PxVec3)));
+
+ // Transform vertices to height field shape
+ PxMat33 convexShape2HfShape_rot(convexShape2HfShape[0],convexShape2HfShape[1],convexShape2HfShape[2]);
+ Cm::Matrix34 convexVertices2HfShape(convexShape2HfShape_rot*convexScaling.getVertex2ShapeSkew(), convexShape2HfShape[3]);
+ const PxVec3* const PX_RESTRICT hullVerts = hull->getHullVertices();
+ for(PxU32 i = 0; i<hull->mNbHullVertices; i++)
+ convexVerticesInHfShape[i] = convexVertices2HfShape.transform(hullVerts[i]);
+
+ PxVec3 convexBoundsInHfShapeMin( PX_MAX_REAL, PX_MAX_REAL, PX_MAX_REAL);
+ PxVec3 convexBoundsInHfShapeMax(-PX_MAX_REAL, -PX_MAX_REAL, -PX_MAX_REAL);
+
+ // Compute bounds of convex in hf space
+ for(PxU32 i = 0; i<hull->mNbHullVertices; i++)
+ {
+ const PxVec3& v = convexVerticesInHfShape[i];
+
+ convexBoundsInHfShapeMin.x = PxMin(convexBoundsInHfShapeMin.x, v.x);
+ convexBoundsInHfShapeMin.y = PxMin(convexBoundsInHfShapeMin.y, v.y);
+ convexBoundsInHfShapeMin.z = PxMin(convexBoundsInHfShapeMin.z, v.z);
+
+ convexBoundsInHfShapeMax.x = PxMax(convexBoundsInHfShapeMax.x, v.x);
+ convexBoundsInHfShapeMax.y = PxMax(convexBoundsInHfShapeMax.y, v.y);
+ convexBoundsInHfShapeMax.z = PxMax(convexBoundsInHfShapeMax.z, v.z);
+ }
+
+ const bool thicknessNegOrNull = (hf.getThicknessFast() <= 0.0f); // PT: don't do this each time! FCMPs are slow.
+
+ // Compute the height field extreme over the bounds area.
+ const PxReal oneOverRowScale = hfUtil.getOneOverRowScale();
+ const PxReal oneOverColumnScale = hfUtil.getOneOverColumnScale();
+
+ PxU32 minRow;
+ PxU32 maxRow;
+ PxU32 minColumn;
+ PxU32 maxColumn;
+
+ minRow = hf.getMinRow(convexBoundsInHfShapeMin.x * oneOverRowScale);
+ maxRow = hf.getMaxRow(convexBoundsInHfShapeMax.x * oneOverRowScale);
+
+ minColumn = hf.getMinColumn(convexBoundsInHfShapeMin.z * oneOverColumnScale);
+ maxColumn = hf.getMaxColumn(convexBoundsInHfShapeMax.z * oneOverColumnScale);
+
+ PxReal hfExtreme = hf.computeExtreme(minRow, maxRow, minColumn, maxColumn);
+
+ hfExtreme *= hfGeom.heightScale;
+
+
+ // Return if convex is on the wrong side of the extreme.
+ if (thicknessNegOrNull)
+ {
+ if (convexBoundsInHfShapeMin.y > hfExtreme) return false;
+ }
+ else
+ {
+ if (convexBoundsInHfShapeMax.y < hfExtreme) return false;
+ }
+
+ // Test convex vertices
+ if (gCompileConvexVertex)
+ {
+ for(PxU32 i=0; i<hull->mNbHullVertices; i++)
+ {
+ const PxVec3& convexVertexInHfShape = convexVerticesInHfShape[i];
+
+ //////// SAME CODE AS IN BOX-HF
+ const bool insideExtreme = thicknessNegOrNull ? (convexVertexInHfShape.y < hfExtreme) : (convexVertexInHfShape.y > hfExtreme);
+
+ if (insideExtreme && hfUtil.isShapePointOnHeightField(convexVertexInHfShape.x, convexVertexInHfShape.z))
+ {
+ // PT: compute this once, reuse results (3 times!)
+ // PT: TODO: also reuse this in EE tests
+ PxReal fracX, fracZ;
+ const PxU32 vertexIndex = hfUtil.getHeightField().computeCellCoordinates(convexVertexInHfShape.x * oneOverRowScale, convexVertexInHfShape.z * oneOverColumnScale, fracX, fracZ);
+
+ const PxReal y = hfUtil.getHeightAtShapePoint2(vertexIndex, fracX, fracZ);
+
+ const PxReal dy = convexVertexInHfShape.y - y;
+ #ifdef DISTANCE_BASED_TEST
+ if (hf.isDeltaHeightInsideExtent(dy, params.mContactDistance/**2.0f*/))
+ #else
+ if (hf.isDeltaHeightInsideExtent(dy))
+ #endif
+ {
+ const PxU32 faceIndex = hfUtil.getFaceIndexAtShapePointNoTest2(vertexIndex, fracX, fracZ);
+ if (faceIndex != 0xffffffff)
+ {
+ PxVec3 n;
+ n = hfUtil.getNormalAtShapePoint2(vertexIndex, fracX, fracZ);
+ n = n.getNormalized();
+
+ contactBuffer
+ #ifdef DISTANCE_BASED_TEST
+ .contact(convexVertex2World.transform(hullVerts[i]), hfShapeAbsPose.rotate(n), n.y*dy/* - contactDistance*/, faceIndex);
+ #else
+ .contact(convexVertex2World.transform(hullVerts[i]), hfShapeAbsPose.rotate(n), n.y*dy + contactDistance, faceIndex);//add contactDistance to compensate for fact that we don't support dist based contacts! See comment at start of funct.
+ #endif
+ }
+ }
+ }
+ ////////~SAME CODE AS IN BOX-HF
+ }
+ }
+
+ // Test convex edges
+ if (gCompileEdgeEdge)
+ {
+ // create helper class for the trace segment
+ GuContactHeightfieldTraceSegmentHelper traceSegmentHelper(hfUtil);
+
+ if(1)
+ {
+ PxU32 numPolygons = hull->mNbPolygons;
+ const Gu::HullPolygonData* polygons = hull->mPolygons;
+ const PxU8* vertexData = hull->getVertexData8();
+
+ ConvexEdge edges[512];
+ PxU32 nbEdges = findUniqueConvexEdges(512, edges, numPolygons, polygons, vertexData);
+
+ for(PxU32 i=0;i<nbEdges;i++)
+ {
+ const PxVec3 convexNormalInHfShape = convexVertices2HfShape.rotate(edges[i].normal);
+ if(convexNormalInHfShape.y>0.0f)
+ continue;
+
+ const PxU8 vi0 = edges[i].vref0;
+ const PxU8 vi1 = edges[i].vref1;
+
+ const PxVec3& sv0 = convexVerticesInHfShape[vi0];
+ const PxVec3& sv1 = convexVerticesInHfShape[vi1];
+
+ if (thicknessNegOrNull)
+ {
+ if ((sv0.y > hfExtreme) && (sv1.y > hfExtreme)) continue;
+ }
+ else
+ {
+ if ((sv0.y < hfExtreme) && (sv1.y < hfExtreme)) continue;
+ }
+ GuContactTraceSegmentCallback cb(sv1 - sv0, contactBuffer, hfShapeAbsPose, params.mContactDistance);
+ traceSegmentHelper.traceSegment(sv0, sv1, &cb);
+ }
+ }
+ else
+ {
+ Gu::EdgeCache edgeCache;
+ PxU32 numPolygons = hull->mNbPolygons;
+ const Gu::HullPolygonData* polygons = hull->mPolygons;
+ const PxU8* vertexData = hull->getVertexData8();
+ while (numPolygons--)
+ {
+ const Gu::HullPolygonData& polygon = *polygons++;
+ const PxU8* vRefBase = vertexData + polygon.mVRef8;
+
+ PxU32 numEdges = polygon.mNbVerts;
+
+ PxU32 a = numEdges - 1;
+ PxU32 b = 0;
+ while(numEdges--)
+ {
+ PxU8 vi0 = vRefBase[a];
+ PxU8 vi1 = vRefBase[b];
+
+
+ if(vi1 < vi0)
+ {
+ PxU8 tmp = vi0;
+ vi0 = vi1;
+ vi1 = tmp;
+ }
+
+ a = b;
+ b++;
+
+ // avoid processing edges 2x if possible (this will typically have cache misses about 5% of the time
+ // leading to 5% redundant work).
+ if (edgeCache.isInCache(vi0, vi1))
+ continue;
+
+ const PxVec3& sv0 = convexVerticesInHfShape[vi0];
+ const PxVec3& sv1 = convexVerticesInHfShape[vi1];
+
+ if (thicknessNegOrNull)
+ {
+ if ((sv0.y > hfExtreme) && (sv1.y > hfExtreme))
+ continue;
+ }
+ else
+ {
+ if ((sv0.y < hfExtreme) && (sv1.y < hfExtreme))
+ continue;
+ }
+ GuContactTraceSegmentCallback cb(sv1 - sv0, contactBuffer, hfShapeAbsPose, params.mContactDistance);
+ traceSegmentHelper.traceSegment(sv0, sv1, &cb);
+ }
+ }
+ }
+ }
+
+ // Test height field vertices
+ if (gCompileHeightFieldVertex)
+ {
+ // Iterate over HeightField vertices inside the projected box bounds.
+ for(PxU32 row = minRow; row <= maxRow; row++)
+ {
+ for(PxU32 column = minColumn; column <= maxColumn; column++)
+ {
+ const PxU32 vertexIndex = row * hf.getNbColumnsFast() + column;
+
+ if (!hfUtil.isCollisionVertex(vertexIndex, row, column))
+ continue;
+
+ const PxVec3 hfVertex(hfGeom.rowScale * row, hfGeom.heightScale * hf.getHeight(vertexIndex), hfGeom.columnScale * column);
+
+ const PxVec3 nrm = hfUtil.getVertexNormal(vertexIndex, row, column);
+ PxVec3 hfVertexNormal = thicknessNegOrNull ? nrm : -nrm;
+ hfVertexNormal = hfVertexNormal.getNormalized();
+ const PxVec3 hfVertexNormalInConvexShape = convexShape2HfShape.rotateTranspose(hfVertexNormal);
+ PxVec3 hfVertexInConvexShape = convexShape2HfShape.transformTranspose(hfVertex);
+ PxReal depth;
+ PxVec3 normal;
+ if (!GuDepenetrateConvex(hfVertexInConvexShape, -hfVertexNormalInConvexShape, *hull, params.mContactDistance, normal, depth,
+ convexScaling,
+ isConvexScaleIdentity))
+ {
+ continue;
+ }
+ PxVec3 normalInHfShape = convexShape2HfShape.rotate(-normal);
+ hfUtil.clipShapeNormalToVertexVoronoi(normalInHfShape, vertexIndex, row, column);
+ if (normalInHfShape.dot(hfVertexNormal) < PX_EPS_REAL) // AP scaffold: verify this is impossible
+ continue;
+
+ normalInHfShape = normalInHfShape.getNormalized();
+ PxU32 faceIndex = hfUtil.getVertexFaceIndex(vertexIndex, row, column);
+ contactBuffer
+ .contact(hfShapeAbsPose.transform(hfVertex), hfShapeAbsPose.rotate(normalInHfShape),
+ #ifdef DISTANCE_BASED_TEST
+ -depth,
+ #else
+ //add contactDistance to compensate for fact that we don't support dist based contacts!
+ // See comment at start of funct.
+ -depth + contactDistance,
+ #endif
+ faceIndex);
+ }
+ }
+ }
+
+ return contactBuffer.count>0;
+}
+}//Gu
+}//physx
diff --git a/PhysX_3.4/Source/GeomUtils/src/contact/GuLegacyContactSphereHeightField.cpp b/PhysX_3.4/Source/GeomUtils/src/contact/GuLegacyContactSphereHeightField.cpp
new file mode 100644
index 00000000..5c9ba896
--- /dev/null
+++ b/PhysX_3.4/Source/GeomUtils/src/contact/GuLegacyContactSphereHeightField.cpp
@@ -0,0 +1,290 @@
+// 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 "GuGeometryUnion.h"
+#include "GuHeightFieldData.h"
+#include "GuHeightFieldUtil.h"
+#include "GuContactMethodImpl.h"
+#include "GuContactBuffer.h"
+
+#define DEBUG_RENDER_HFCONTACTS 0
+#if DEBUG_RENDER_HFCONTACTS
+#include "PxPhysics.h"
+#include "PxScene.h"
+#endif
+
+using namespace physx;
+using namespace Gu;
+
+// Sphere-heightfield contact generation
+
+// this code is shared between capsule vertices and sphere
+bool GuContactSphereHeightFieldShared(GU_CONTACT_METHOD_ARGS, bool isCapsule)
+{
+#if 1
+ PX_UNUSED(cache);
+ PX_UNUSED(renderOutput);
+
+ // Get actual shape data
+ const PxSphereGeometry& shapeSphere = shape0.get<const PxSphereGeometry>();
+ const PxHeightFieldGeometryLL& hfGeom = shape1.get<const PxHeightFieldGeometryLL>();
+
+ const Gu::HeightField& hf = *static_cast<Gu::HeightField*>(hfGeom.heightField);
+ const Gu::HeightFieldUtil hfUtil(hfGeom, hf);
+
+ const PxReal radius = shapeSphere.radius;
+ const PxReal eps = PxReal(0.1) * radius;
+
+ const PxVec3 sphereInHfShape = transform1.transformInv(transform0.p);
+
+ PX_ASSERT(isCapsule || contactBuffer.count==0);
+
+ const PxReal oneOverRowScale = hfUtil.getOneOverRowScale();
+ const PxReal oneOverColumnScale = hfUtil.getOneOverColumnScale();
+
+ // check if the sphere is below the HF surface
+ if (hfUtil.isShapePointOnHeightField(sphereInHfShape.x, sphereInHfShape.z))
+ {
+
+ PxReal fracX, fracZ;
+ const PxU32 vertexIndex = hfUtil.getHeightField().computeCellCoordinates(sphereInHfShape.x * oneOverRowScale, sphereInHfShape.z * oneOverColumnScale, fracX, fracZ);
+
+ // The sphere origin projects within the bounds of the heightfield in the X-Z plane
+// const PxReal sampleHeight = hfShape.getHeightAtShapePoint(sphereInHfShape.x, sphereInHfShape.z);
+ const PxReal sampleHeight = hfUtil.getHeightAtShapePoint2(vertexIndex, fracX, fracZ);
+
+ const PxReal deltaHeight = sphereInHfShape.y - sampleHeight;
+ //if (hfShape.isDeltaHeightInsideExtent(deltaHeight, eps))
+ if (hf.isDeltaHeightInsideExtent(deltaHeight, eps))
+ {
+ // The sphere origin is 'below' the heightfield surface
+ // Actually there is an epsilon involved to make sure the
+ // 'above' surface calculations can deliver a good normal
+ const PxU32 faceIndex = hfUtil.getFaceIndexAtShapePointNoTest2(vertexIndex, fracX, fracZ);
+ if (faceIndex != 0xffffffff)
+ {
+
+ //hfShape.getAbsPoseFast().M.getColumn(1, hfShapeUp);
+ const PxVec3 hfShapeUp = transform1.q.getBasisVector1();
+
+ if (hf.getThicknessFast() <= 0)
+ contactBuffer.contact(transform0.p, hfShapeUp, deltaHeight-radius, faceIndex);
+ else
+ contactBuffer.contact(transform0.p, -hfShapeUp, -deltaHeight-radius, faceIndex);
+
+ return true;
+ }
+
+ return false;
+ }
+
+ }
+
+ const PxReal epsSqr = eps * eps;
+
+ const PxReal inflatedRadius = radius + params.mContactDistance;
+ const PxReal inflatedRadiusSquared = inflatedRadius * inflatedRadius;
+
+ const PxVec3 sphereInHF = hfUtil.shape2hfp(sphereInHfShape);
+
+ const PxReal radiusOverRowScale = inflatedRadius * PxAbs(oneOverRowScale);
+ const PxReal radiusOverColumnScale = inflatedRadius * PxAbs(oneOverColumnScale);
+
+ const PxU32 minRow = hf.getMinRow(sphereInHF.x - radiusOverRowScale);
+ const PxU32 maxRow = hf.getMaxRow(sphereInHF.x + radiusOverRowScale);
+ const PxU32 minColumn = hf.getMinColumn(sphereInHF.z - radiusOverColumnScale);
+ const PxU32 maxColumn = hf.getMaxColumn(sphereInHF.z + radiusOverColumnScale);
+
+ // this assert is here because the following code depends on it for reasonable performance for high-count situations
+ PX_COMPILE_TIME_ASSERT(ContactBuffer::MAX_CONTACTS == 64);
+
+ const PxU32 nbColumns = hf.getNbColumnsFast();
+
+#define HFU Gu::HeightFieldUtil
+ PxU32 numFaceContacts = 0;
+ for (PxU32 i = 0; i<2; i++)
+ {
+ const bool facesOnly = (i == 0);
+ // first we go over faces-only meaning only contacts directly in Voronoi regions of faces
+ // at second pass we consider edges and vertices and clamp the normals to adjacent feature's normal
+ // if there was a prior contact. it is equivalent to clipping the normal to it's feature's Voronoi region
+
+ for (PxU32 r = minRow; r < maxRow; r++)
+ {
+ for (PxU32 c = minColumn; c < maxColumn; c++)
+ {
+
+ // x--x--x
+ // | x |
+ // x x x
+ // | x |
+ // x--x--x
+ PxVec3 closestPoints[11];
+ PxU32 closestFeatures[11];
+ PxU32 npcp = hfUtil.findClosestPointsOnCell(
+ r, c, sphereInHfShape, closestPoints, closestFeatures, facesOnly, !facesOnly, true);
+
+ for(PxU32 pi = 0; pi < npcp; pi++)
+ {
+ PX_ASSERT(closestFeatures[pi] != 0xffffffff);
+ const PxVec3 d = sphereInHfShape - closestPoints[pi];
+
+ if (hf.isDeltaHeightOppositeExtent(d.y)) // See if we are 'above' the heightfield
+ {
+ const PxReal dMagSq = d.magnitudeSquared();
+
+ if (dMagSq > inflatedRadiusSquared)
+ // Too far above
+ continue;
+
+ PxReal dMag = -1.0f; // dMag is sqrt(sMadSq) and comes up as a byproduct of other calculations in computePointNormal
+ PxVec3 n; // n is in world space, rotated by transform1
+ PxU32 featureType = HFU::getFeatureType(closestFeatures[pi]);
+ if (featureType == HFU::eEDGE)
+ {
+ PxU32 edgeIndex = HFU::getFeatureIndex(closestFeatures[pi]);
+ PxU32 adjFaceIndices[2];
+ const PxU32 adjFaceCount = hf.getEdgeTriangleIndices(edgeIndex, adjFaceIndices);
+ PxVec3 origin;
+ PxVec3 direction;
+ const PxU32 vertexIndex = edgeIndex / 3;
+ const PxU32 row = vertexIndex / nbColumns;
+ const PxU32 col = vertexIndex % nbColumns;
+ hfUtil.getEdge(edgeIndex, vertexIndex, row, col, origin, direction);
+ n = hfUtil.computePointNormal(
+ hfGeom.heightFieldFlags, d, transform1, dMagSq,
+ closestPoints[pi].x, closestPoints[pi].z, epsSqr, dMag);
+ PxVec3 localN = transform1.rotateInv(n);
+ // clamp the edge's normal to its Voronoi region
+ for (PxU32 j = 0; j < adjFaceCount; j++)
+ {
+ const PxVec3 adjNormal = hfUtil.hf2shapen(hf.getTriangleNormalInternal(adjFaceIndices[j])).getNormalized();
+ PxU32 triCell = adjFaceIndices[j] >> 1;
+ PxU32 triRow = triCell/hf.getNbColumnsFast();
+ PxU32 triCol = triCell%hf.getNbColumnsFast();
+ PxVec3 tv0, tv1, tv2, tvc;
+ hf.getTriangleVertices(adjFaceIndices[j], triRow, triCol, tv0, tv1, tv2);
+ tvc = hfUtil.hf2shapep((tv0+tv1+tv2)/3.0f); // compute adjacent triangle center
+ PxVec3 perp = adjNormal.cross(direction).getNormalized(); // adj face normal cross edge dir
+ if (perp.dot(tvc-origin) < 0.0f) // make sure perp is pointing toward the center of the triangle
+ perp = -perp;
+ // perp is now a vector sticking out of the edge of the triangle (also the test edge) pointing toward the center
+ // perpendicular to the normal (in triangle plane)
+ if (perp.dot(localN) > 0.0f) // if the normal is in perp halfspace, clamp it to Voronoi region
+ {
+ n = transform1.rotate(adjNormal);
+ break;
+ }
+ }
+ } else if(featureType == HFU::eVERTEX)
+ {
+ // AP: these contacts are rare so hopefully it's ok
+ const PxU32 bufferCount = contactBuffer.count;
+ const PxU32 vertIndex = HFU::getFeatureIndex(closestFeatures[pi]);
+ EdgeData adjEdges[8];
+ const PxU32 row = vertIndex / nbColumns;
+ const PxU32 col = vertIndex % nbColumns;
+ const PxU32 numAdjEdges = ::getVertexEdgeIndices(hf, vertIndex, row, col, adjEdges);
+ for (PxU32 iPrevEdgeContact = numFaceContacts; iPrevEdgeContact < bufferCount; iPrevEdgeContact++)
+ {
+ if (contactBuffer.contacts[iPrevEdgeContact].forInternalUse != HFU::eEDGE)
+ continue; // skip non-edge contacts (can be other vertex contacts)
+
+ for (PxU32 iAdjEdge = 0; iAdjEdge < numAdjEdges; iAdjEdge++)
+ // does adjacent edge index for this vertex match a previously encountered edge index?
+ if (adjEdges[iAdjEdge].edgeIndex == contactBuffer.contacts[iPrevEdgeContact].internalFaceIndex1)
+ {
+ // if so, clamp the normal for this vertex to that edge's normal
+ n = contactBuffer.contacts[iPrevEdgeContact].normal;
+ dMag = PxSqrt(dMagSq);
+ break;
+ }
+ }
+ }
+
+ if (dMag == -1.0f)
+ n = hfUtil.computePointNormal(hfGeom.heightFieldFlags, d, transform1,
+ dMagSq, closestPoints[pi].x, closestPoints[pi].z, epsSqr, dMag);
+
+ PxVec3 p = transform0.p - n * radius;
+ #if DEBUG_RENDER_HFCONTACTS
+ printf("n=%.5f %.5f %.5f; ", n.x, n.y, n.z);
+ if (n.y < 0.8f)
+ int a = 1;
+ PxScene *s; PxGetPhysics().getScenes(&s, 1, 0);
+ Cm::RenderOutput((Cm::RenderBuffer&)s->getRenderBuffer()) << Cm::RenderOutput::LINES << PxDebugColor::eARGB_BLUE // red
+ << p << (p + n * 10.0f);
+ #endif
+
+ // temporarily use the internalFaceIndex0 slot in the contact buffer for featureType
+ contactBuffer.contact(
+ p, n, dMag - radius, PxU16(featureType), HFU::getFeatureIndex(closestFeatures[pi]));
+ }
+ }
+ }
+ }
+ if (facesOnly)
+ numFaceContacts = contactBuffer.count;
+ } // for facesOnly = true to false
+
+ if (!isCapsule) // keep the face labels as internalFaceIndex0 if this function is called from inside capsule/HF function
+ for (PxU32 k = 0; k < numFaceContacts; k++)
+ contactBuffer.contacts[k].forInternalUse = 0xFFFF;
+
+ for (PxU32 k = numFaceContacts; k < contactBuffer.count; k++)
+ {
+ PX_ASSERT(contactBuffer.contacts[k].forInternalUse != HFU::eFACE);
+ PX_ASSERT(contactBuffer.contacts[k].forInternalUse <= HFU::eVERTEX);
+ PxU32 featureIndex = contactBuffer.contacts[k].internalFaceIndex1;
+ if (contactBuffer.contacts[k].forInternalUse == HFU::eEDGE)
+ contactBuffer.contacts[k].internalFaceIndex1 = hfUtil.getEdgeFaceIndex(featureIndex);
+ else if (contactBuffer.contacts[k].forInternalUse == HFU::eVERTEX)
+ {
+ PxU32 row = featureIndex/hf.getNbColumnsFast();
+ PxU32 col = featureIndex%hf.getNbColumnsFast();
+ contactBuffer.contacts[k].internalFaceIndex1 = hfUtil.getVertexFaceIndex(featureIndex, row, col);
+ }
+ }
+#undef HFU
+
+ return contactBuffer.count>0;
+#endif
+}
+
+namespace physx
+{
+namespace Gu
+{
+bool legacyContactSphereHeightfield(GU_CONTACT_METHOD_ARGS)
+{
+ return GuContactSphereHeightFieldShared(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput, false);
+}
+
+}//Gu
+}//physx
diff --git a/PhysX_3.4/Source/GeomUtils/src/contact/GuLegacyTraceLineCallback.h b/PhysX_3.4/Source/GeomUtils/src/contact/GuLegacyTraceLineCallback.h
new file mode 100644
index 00000000..d89cfb24
--- /dev/null
+++ b/PhysX_3.4/Source/GeomUtils/src/contact/GuLegacyTraceLineCallback.h
@@ -0,0 +1,160 @@
+// 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.
+
+
+#ifndef GU_CONTACTTRACESEGMENTCALLBACK_H
+#define GU_CONTACTTRACESEGMENTCALLBACK_H
+
+#include "CmMatrix34.h"
+#include "GuGeometryUnion.h"
+
+#include "GuHeightFieldUtil.h"
+#include "CmRenderOutput.h"
+#include "GuContactBuffer.h"
+
+namespace physx
+{
+namespace Gu
+{
+#define DISTANCE_BASED_TEST
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+struct GuContactTraceSegmentCallback
+{
+ PxVec3 mLine;
+ ContactBuffer& mContactBuffer;
+ Cm::Matrix34 mTransform;
+ PxReal mContactDistance;
+ PxU32 mPrevTriangleIndex; // currently only used to indicate first callback
+// Cm::RenderOutput& mRO;
+
+ PX_INLINE GuContactTraceSegmentCallback(
+ const PxVec3& line, Gu::ContactBuffer& contactBuffer,
+ Cm::Matrix34 transform, PxReal contactDistance
+ /*, Cm::RenderOutput& ro*/)
+ : mLine(line), mContactBuffer(contactBuffer), mTransform(transform),
+ mContactDistance(contactDistance), mPrevTriangleIndex(0xFFFFffff)//, mRO(ro)
+ {
+ }
+
+ bool onEvent(PxU32 , PxU32* )
+ {
+ return true;
+ }
+
+ PX_INLINE bool faceHit(const Gu::HeightFieldUtil& /*hfUtil*/, const PxVec3& /*hitPoint*/, PxU32 /*triangleIndex*/, PxReal, PxReal) { return true; }
+
+ // x,z is the point of projected face entry intercept in hf coords, rayHeight is at that same point
+ PX_INLINE bool underFaceHit(
+ const Gu::HeightFieldUtil& hfUtil, const PxVec3& triangleNormal,
+ const PxVec3& crossedEdge, PxF32 x, PxF32 z, PxF32 rayHeight, PxU32 triangleIndex)
+ {
+ if (mPrevTriangleIndex == 0xFFFFffff) // we only record under-edge contacts so we need at least 2 face hits to have the edge
+ {
+ mPrevTriangleIndex = triangleIndex;
+ //mPrevTriangleNormal = hfUtil.getTriangleNormal(triangleIndex);
+ return true;
+ }
+
+ const Gu::HeightField& hf = hfUtil.getHeightField();
+ PxF32 y = hfUtil.getHeightAtShapePoint(x, z); // TODO: optmization opportunity - this can be derived cheaply inside traceSegment
+ PxF32 dy = rayHeight - y;
+
+ if (!hf.isDeltaHeightInsideExtent(dy, mContactDistance))
+ return true;
+
+ // add contact
+ PxVec3 n = crossedEdge.cross(mLine);
+ if (n.y < 0) // Make sure cross product is facing correctly before clipping
+ n = -n;
+
+ if (n.y < 0) // degenerate case
+ return true;
+
+ const PxReal ll = n.magnitudeSquared();
+ if (ll > 0) // normalize
+ n *= PxRecipSqrt(ll);
+ else // degenerate case
+ return true;
+
+ // Scale delta height so it becomes the "penetration" along the normal
+ dy *= n.y;
+ if (hf.getThicknessFast() > 0)
+ {
+ n = -n;
+ dy = -dy;
+ }
+
+ // compute the contact point
+ const PxVec3 point(x, rayHeight, z);
+ //mRO << PxVec3(1,0,0) << Gu::Debug::convertToPxMat44(mTransform)
+ // << Cm::RenderOutput::LINES << point << point + triangleNormal;
+#ifdef DISTANCE_BASED_TEST
+ mContactBuffer.contact(
+ mTransform.transform(point), mTransform.rotate(triangleNormal), dy, triangleIndex);
+#else
+ // add gContactDistance to compensate for fact that we don't support dist based contacts in box/convex-hf!
+ // See comment at start of those functs.
+ mContactBuffer.contact(
+ mTransform.transform(point), mTransform.rotate(triangleNormal),
+ dy + mContactDistance, PXC_CONTACT_NO_FACE_INDEX, triangleIndex);
+#endif
+ mPrevTriangleIndex = triangleIndex;
+ //mPrevTriangleNormal = triangleNormal;
+ return true;
+ }
+
+private:
+ GuContactTraceSegmentCallback& operator=(const GuContactTraceSegmentCallback&);
+};
+
+class GuContactHeightfieldTraceSegmentHelper
+{
+ PX_NOCOPY(GuContactHeightfieldTraceSegmentHelper)
+public:
+ GuContactHeightfieldTraceSegmentHelper(const HeightFieldUtil& hfUtil)
+ : mHfUtil(hfUtil)
+ {
+ mHfUtil.computeLocalBounds(mLocalBounds);
+ }
+
+ PX_INLINE void traceSegment(const PxVec3& aP0, const PxVec3& aP1, GuContactTraceSegmentCallback* aCallback) const
+ {
+ mHfUtil.traceSegment<GuContactTraceSegmentCallback, true, false>(aP0, aP1 - aP0, 1.0f, aCallback, mLocalBounds, true, NULL);
+ }
+
+private:
+ const HeightFieldUtil& mHfUtil;
+ PxBounds3 mLocalBounds;
+};
+
+}//Gu
+}//physx
+
+#endif