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

1466 lines
44 KiB
C++

// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#include "geomutils/PxContactBuffer.h"
#include "GuConvexUtilsInternal.h"
#include "GuInternal.h"
#include "GuContactPolygonPolygon.h"
#include "GuConvexEdgeFlags.h"
#include "GuSeparatingAxes.h"
#include "GuContactMethodImpl.h"
#include "GuMidphaseInterface.h"
#include "GuConvexHelper.h"
#include "GuTriangleCache.h"
#include "GuHeightFieldUtil.h"
#include "GuEntityReport.h"
#include "GuIntersectionTriangleBox.h"
#include "GuBox.h"
#include "CmUtils.h"
#include "foundation/PxAlloca.h"
#include "foundation/PxFPU.h"
#include "CmMatrix34.h"
using namespace physx;
using namespace Gu;
using namespace Cm;
using namespace aos;
using namespace intrinsics;
//sizeof(SavedContactData)/sizeof(PxU32) = 17, 1088/17 = 64 triangles in the local array
#define LOCAL_CONTACTS_SIZE 1088
#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.mInternalExtents.x, localAxis0, p0);
const float Radius0 = p0[0]*localAxis0.x + p0[1]*localAxis0.y + p0[2]*localAxis0.z;
const float bestRadius = selectMax(Radius0, polyData0.mInternal.mInternalRadius);
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 PxMat34& m0to1,
const 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 PxMat34& /*world0*/, const PxMat34& /*world1*/,
const PxMat34& m0to1, const PxVec3& witness,
const PxVec3* PX_RESTRICT triangle,
const 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 PxMat34& m0to1, const PxMat34& m1to0,
const PxVec3* PX_RESTRICT triangle,
PxU32 numHullIndices, const PxU32* PX_RESTRICT hullIndices,
const PxPlane& localTriPlane,
const 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(!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 PxMat34& world0, const PxMat34& world1, const PxMat34& m0to1, const PxMat34& m1to0,
const 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
{
PxInlineArray<PxU32,LOCAL_CONTACTS_SIZE>& mDelayedContacts;
CacheMap<CachedEdge, 128> mEdgeCache;
CacheMap<CachedVertex, 128> mVertCache;
const Matrix34FromTransform m0to1;
const Matrix34FromTransform m1to0;
PxVec3 mHullCenterMesh;
PxVec3 mHullCenterWorld;
const PolygonalData& mPolyData0;
const PxMat34& mWorld0;
const PxMat34& mWorld1;
const FastVertex2ShapeScaling& mConvexScaling;
PxReal mContactDistance;
PxReal mToleranceLength;
bool mIdtMeshScale, mIdtConvexScale;
PxReal mCCDEpsilon;
const PxTransform& mTransform0;
const PxTransform& mTransform1;
PxContactBuffer& mContactBuffer;
bool mAnyHits;
ConvexMeshContactGeneration(
PxInlineArray<PxU32,LOCAL_CONTACTS_SIZE>& delayedContacts,
const PxTransform& t0to1, const PxTransform& t1to0,
const PolygonalData& polyData0, const PxMat34& world0, const PxMat34& world1,
const FastVertex2ShapeScaling& convexScaling,
PxReal contactDistance,
PxReal toleranceLength,
bool idtConvexScale,
PxReal cCCDEpsilon,
const PxTransform& transform0, const PxTransform& transform1,
PxContactBuffer& 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; // 1
PxVec3 mVerts[3]; // 10
PxU32 mInds[3]; // 13
PxVec3 mGroupAxis; // 16
PxReal mGroupMinDepth; // 17
};
}
ConvexMeshContactGeneration::ConvexMeshContactGeneration(
PxInlineArray<PxU32,LOCAL_CONTACTS_SIZE>& delayedContacts,
const PxTransform& t0to1, const PxTransform& t1to0,
const PolygonalData& polyData0, const PxMat34& world0, const PxMat34& world1,
const FastVertex2ShapeScaling& convexScaling,
PxReal contactDistance,
PxReal toleranceLength,
bool idtConvexScale,
PxReal cCCDEpsilon,
const PxTransform& transform0, const PxTransform& transform1,
PxContactBuffer& 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<PxGeomRaycastHit>
{
ConvexMeshContactGeneration mGeneration;
const FastVertex2ShapeScaling& mMeshScaling;
const PxU8* PX_RESTRICT mExtraTrigData;
bool mIdtMeshScale;
const TriangleMesh* mMeshData;
const BoxPadded& mBox;
ConvexMeshContactGenerationCallback(
PxInlineArray<PxU32,LOCAL_CONTACTS_SIZE>& delayedContacts,
const PxTransform& t0to1, const PxTransform& t1to0,
const PolygonalData& polyData0, const PxMat34& world0, const PxMat34& world1,
const TriangleMesh* meshData,
const PxU8* PX_RESTRICT extraTrigData,
const FastVertex2ShapeScaling& meshScaling,
const FastVertex2ShapeScaling& convexScaling,
PxReal contactDistance,
PxReal toleranceLength,
bool idtMeshScale, bool idtConvexScale,
PxReal cCCDEpsilon,
const PxTransform& transform0, const PxTransform& transform1,
PxContactBuffer& contactBuffer,
const BoxPadded& box
) :
MeshHitCallback<PxGeomRaycastHit> (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 PxGeomRaycastHit& 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;
PxU8 extraData = getConvexEdgeFlags(mExtraTrigData, triangleIndex);
const PxU32* vertexIndices = vinds;
PxU32 localStorage[3];
if(mMeshScaling.flipsNormal())
{
flipConvexEdgeFlags(extraData);
localStorage[0] = vinds[0];
localStorage[1] = vinds[2];
localStorage[2] = vinds[1];
vertexIndices = localStorage;
}
mGeneration.processTriangle(verts, triangleIndex, extraData, vertexIndices);
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.
PxMat34 world0_(mWorld0);
PxTransform transform0_(mTransform0);
world0_.p -= contactGenPositionShiftVec;
transform0_.p = world0_.p; //reset this too.
const PxTransform t0to1_ = mTransform1.transformInv(transform0_);
const PxTransform t1to0_ = transform0_.transformInv(mTransform1);
const Matrix34FromTransform m0to1_(t0to1_);
const Matrix34FromTransform 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)
// PxSwap(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)
// PxSwap(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)
// PxSwap(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 PxTriangleMeshGeometry& shape1,
const PxTransform& transform0, const PxTransform& transform1,
const NarrowPhaseParams& params, PxContactBuffer& contactBuffer,
const FastVertex2ShapeScaling& convexScaling, const FastVertex2ShapeScaling& meshScaling,
bool idtConvexScale, bool idtMeshScale)
{
//Just a sanity-check in debug-mode
PX_ASSERT(shape1.getType() == PxGeometryType::eTRIANGLEMESH);
////////////////////
// Compute matrices
const Matrix34FromTransform world0(transform0);
const Matrix34FromTransform 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 = _getMeshData(shape1);
PxInlineArray<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 PxConvexMeshGeometry& shapeConvex = checkedCast<PxConvexMeshGeometry>(shape0);
const PxTriangleMeshGeometry& shapeMesh = checkedCast<PxTriangleMeshGeometry>(shape1);
const bool idtScaleMesh = shapeMesh.scale.isIdentity();
FastVertex2ShapeScaling meshScaling;
if(!idtScaleMesh)
meshScaling.init(shapeMesh.scale);
FastVertex2ShapeScaling convexScaling;
PxBounds3 hullAABB;
PolygonalData polyData0;
const bool idtScaleConvex = getConvexData(shapeConvex, 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 = checkedCast<PxBoxGeometry>(shape0);
const PxTriangleMeshGeometry& shapeMesh = checkedCast<PxTriangleMeshGeometry>(shape1);
PolygonalData polyData0;
PolygonalBox polyBox(shapeBox.halfExtents);
polyBox.getPolygonalData(&polyData0);
const PxBounds3 hullAABB(-shapeBox.halfExtents, shapeBox.halfExtents);
const bool idtScaleMesh = shapeMesh.scale.isIdentity();
FastVertex2ShapeScaling meshScaling;
if(!idtScaleMesh)
meshScaling.init(shapeMesh.scale);
FastVertex2ShapeScaling idtScaling;
return contactHullMesh2(polyData0, hullAABB, shapeMesh, transform0, transform1, params, contactBuffer, idtScaling, meshScaling, true, idtScaleMesh);
}
/////////////
namespace
{
struct ConvexVsHeightfieldContactGenerationCallback : OverlapReport
{
ConvexMeshContactGeneration mGeneration;
HeightFieldUtil& mHfUtil;
ConvexVsHeightfieldContactGenerationCallback(
HeightFieldUtil& hfUtil,
PxInlineArray<PxU32,LOCAL_CONTACTS_SIZE>& delayedContacts,
const PxTransform& t0to1, const PxTransform& t1to0,
const PolygonalData& polyData0, const PxMat34& world0, const PxMat34& world1,
const FastVertex2ShapeScaling& convexScaling,
PxReal contactDistance,
PxReal toleranceLength,
bool idtConvexScale,
PxReal cCCDEpsilon,
const PxTransform& transform0, const PxTransform& transform1,
PxContactBuffer& 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 reportTouchedTris(PxU32 nb, const 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, PxContactBuffer& contactBuffer,
const FastVertex2ShapeScaling& convexScaling,
bool idtConvexScale)
{
//We need to create a callback that fills triangles from the HF
HeightFieldUtil hfUtil(shape1);
const Matrix34FromTransform world0(transform0);
const Matrix34FromTransform world1(transform1);
////////////////////
// Compute relative transforms
const PxTransform t0to1 = transform1.transformInv(transform0);
const PxTransform t1to0 = transform0.transformInv(transform1);
PxInlineArray<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.overlapAABBTriangles0to1(t0to1, hullAABB, 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 PxConvexMeshGeometry& shapeConvex = checkedCast<PxConvexMeshGeometry>(shape0);
const PxHeightFieldGeometry& shapeMesh = checkedCast<PxHeightFieldGeometry>(shape1);
FastVertex2ShapeScaling convexScaling;
PxBounds3 hullAABB;
PolygonalData polyData0;
const bool idtScaleConvex = getConvexData(shapeConvex, 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 PxHeightFieldGeometry& shapeMesh = checkedCast<PxHeightFieldGeometry>(shape1);
const PxBoxGeometry& shapeBox = checkedCast<PxBoxGeometry>(shape0);
PolygonalData polyData0;
PolygonalBox polyBox(shapeBox.halfExtents);
polyBox.getPolygonalData(&polyData0);
const PxVec3 inflatedExtents = shapeBox.halfExtents + PxVec3(params.mContactDistance);
const PxBounds3 hullAABB = PxBounds3(-inflatedExtents, inflatedExtents);
const FastVertex2ShapeScaling idtScaling;
return contactHullHeightfield2(polyData0, hullAABB, shapeMesh, transform0, transform1, params, contactBuffer, idtScaling, true);
}