622 lines
20 KiB
C++
622 lines
20 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 "common/PxRenderOutput.h"
|
|
#include "GuDistancePointTriangle.h"
|
|
#include "GuContactMethodImpl.h"
|
|
#include "GuFeatureCode.h"
|
|
#include "GuMidphaseInterface.h"
|
|
#include "GuEntityReport.h"
|
|
#include "GuHeightFieldUtil.h"
|
|
#include "GuBox.h"
|
|
|
|
#include "foundation/PxSort.h"
|
|
|
|
#define DEBUG_RENDER_MESHCONTACTS 0
|
|
|
|
using namespace physx;
|
|
using namespace Gu;
|
|
|
|
static const bool gDrawTouchedTriangles = false;
|
|
|
|
static void outputErrorMessage()
|
|
{
|
|
#if PX_CHECKED
|
|
PxGetFoundation().error(PxErrorCode::eINTERNAL_ERROR, PX_FL, "Dropping contacts in sphere vs mesh: exceeded limit of 256 ");
|
|
#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;
|
|
PxContactBuffer& mContactBuffer;
|
|
const PxVec3& mSphereCenterShape1Space;
|
|
PxF32 mInflatedRadius2;
|
|
PxU32 mNbDelayed;
|
|
TriangleData mSavedData[PxContactBuffer::MAX_CONTACTS];
|
|
SortKey mSortKey[PxContactBuffer::MAX_CONTACTS];
|
|
PxU32 mNbCachedTris;
|
|
CachedTriangleIndices mCachedTris[PxContactBuffer::MAX_CONTACTS];
|
|
PxRenderOutput* mRenderOutput;
|
|
|
|
SphereMeshContactGeneration(const PxSphereGeometry& shapeSphere, const PxTransform& transform0, const PxTransform& transform1,
|
|
PxContactBuffer& contactBuffer, const PxVec3& sphereCenterShape1Space, PxF32 inflatedRadius,
|
|
PxRenderOutput* 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<PxContactBuffer::MAX_CONTACTS)
|
|
cacheTriangle(vertInds[0], vertInds[1], vertInds[2]);
|
|
}
|
|
else
|
|
{
|
|
if(mNbDelayed<PxContactBuffer::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;
|
|
|
|
PxSort(mSortKey, count, PxLess<SortKey>(), NullAllocator(), PxContactBuffer::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<PxContactBuffer::MAX_CONTACTS)
|
|
cacheTriangle(ref0, ref1, ref2);
|
|
else
|
|
outputErrorMessage();
|
|
}
|
|
}
|
|
|
|
private:
|
|
SphereMeshContactGeneration& operator=(const SphereMeshContactGeneration&);
|
|
};
|
|
|
|
struct SphereMeshContactGenerationCallback_NoScale : MeshHitCallback<PxGeomRaycastHit>
|
|
{
|
|
SphereMeshContactGeneration mGeneration;
|
|
const TriangleMesh& mMeshData;
|
|
|
|
SphereMeshContactGenerationCallback_NoScale(const TriangleMesh& meshData, const PxSphereGeometry& shapeSphere,
|
|
const PxTransform& transform0, const PxTransform& transform1, PxContactBuffer& contactBuffer,
|
|
const PxVec3& sphereCenterShape1Space, PxF32 inflatedRadius, PxRenderOutput* renderOutput
|
|
) : MeshHitCallback<PxGeomRaycastHit> (CallbackMode::eMULTIPLE),
|
|
mGeneration (shapeSphere, transform0, transform1, contactBuffer, sphereCenterShape1Space, inflatedRadius, renderOutput),
|
|
mMeshData (meshData)
|
|
{
|
|
}
|
|
|
|
virtual ~SphereMeshContactGenerationCallback_NoScale()
|
|
{
|
|
mGeneration.generateLastContacts();
|
|
}
|
|
|
|
virtual PxAgain processHit(
|
|
const PxGeomRaycastHit& 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,
|
|
PxContactBuffer& contactBuffer, const PxVec3& sphereCenterShape1Space, PxF32 inflatedRadius, PxRenderOutput* renderOutput
|
|
) : SphereMeshContactGenerationCallback_NoScale(meshData, shapeSphere,
|
|
transform0, transform1, contactBuffer, sphereCenterShape1Space, inflatedRadius, renderOutput),
|
|
mMeshScaling (meshScaling)
|
|
{
|
|
}
|
|
|
|
virtual ~SphereMeshContactGenerationCallback_Scale() {}
|
|
|
|
virtual PxAgain processHit(const PxGeomRaycastHit& hit, const PxVec3& v0, const PxVec3& v1, const PxVec3& v2, PxReal&, const PxU32* vinds)
|
|
{
|
|
PxVec3 verts[3];
|
|
getScaledVertices(verts, v0, v1, v2, false, mMeshScaling);
|
|
|
|
const PxU32* vertexIndices = vinds;
|
|
PxU32 localStorage[3];
|
|
if(mMeshScaling.flipsNormal())
|
|
{
|
|
localStorage[0] = vinds[0];
|
|
localStorage[1] = vinds[2];
|
|
localStorage[2] = vinds[1];
|
|
vertexIndices = localStorage;
|
|
}
|
|
|
|
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], vertexIndices);
|
|
return true;
|
|
}
|
|
protected:
|
|
SphereMeshContactGenerationCallback_Scale &operator=(const SphereMeshContactGenerationCallback_Scale &);
|
|
};
|
|
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
bool Gu::contactSphereMesh(GU_CONTACT_METHOD_ARGS)
|
|
{
|
|
PX_UNUSED(cache);
|
|
|
|
const PxSphereGeometry& shapeSphere = checkedCast<PxSphereGeometry>(shape0);
|
|
const PxTriangleMeshGeometry& shapeMesh = checkedCast<PxTriangleMeshGeometry>(shape1);
|
|
|
|
// 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 = _getMeshData(shapeMesh);
|
|
|
|
// 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 : OverlapReport
|
|
{
|
|
SphereMeshContactGeneration mGeneration;
|
|
HeightFieldUtil& mHfUtil;
|
|
|
|
SphereHeightfieldContactGenerationCallback(
|
|
HeightFieldUtil& hfUtil,
|
|
const PxSphereGeometry& shapeSphere,
|
|
const PxTransform& transform0,
|
|
const PxTransform& transform1,
|
|
PxContactBuffer& contactBuffer,
|
|
const PxVec3& sphereCenterInMeshSpace,
|
|
PxF32 inflatedRadius,
|
|
PxRenderOutput* renderOutput
|
|
) :
|
|
mGeneration (shapeSphere, transform0, transform1, contactBuffer, sphereCenterInMeshSpace, inflatedRadius, renderOutput),
|
|
mHfUtil (hfUtil)
|
|
{
|
|
}
|
|
|
|
// PT: TODO: refactor/unify with similar code in other places
|
|
virtual bool reportTouchedTris(PxU32 nb, const 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 = checkedCast<PxSphereGeometry>(shape0);
|
|
const PxHeightFieldGeometry& shapeMesh = checkedCast<PxHeightFieldGeometry>(shape1);
|
|
|
|
HeightFieldUtil hfUtil(shapeMesh);
|
|
|
|
const PxReal inflatedRadius = shapeSphere.radius + params.mContactDistance;
|
|
|
|
PxBounds3 localBounds;
|
|
const PxVec3 localSphereCenter = getLocalSphereData(localBounds, transform0, transform1, inflatedRadius);
|
|
|
|
SphereHeightfieldContactGenerationCallback blockCallback(hfUtil, shapeSphere, transform0, transform1, contactBuffer, localSphereCenter, inflatedRadius, renderOutput);
|
|
|
|
hfUtil.overlapAABBTriangles(localBounds, blockCallback);
|
|
|
|
blockCallback.mGeneration.generateLastContacts();
|
|
|
|
return contactBuffer.count > 0;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|