578 lines
18 KiB
C++
578 lines
18 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 "GuConvexMesh.h"
|
||
|
|
#include "GuConvexHelper.h"
|
||
|
|
#include "GuContactMethodImpl.h"
|
||
|
|
#include "GuVecConvexHull.h"
|
||
|
|
#include "GuVecCapsule.h"
|
||
|
|
#include "GuInternal.h"
|
||
|
|
#include "GuGJK.h"
|
||
|
|
#include "CmMatrix34.h"
|
||
|
|
|
||
|
|
using namespace physx;
|
||
|
|
using namespace Gu;
|
||
|
|
using namespace Cm;
|
||
|
|
|
||
|
|
///////////
|
||
|
|
// #include "PxRenderOutput.h"
|
||
|
|
// #include "PxsContext.h"
|
||
|
|
// static void gVisualizeLine(const PxVec3& a, const PxVec3& b, PxcNpThreadContext& context, PxU32 color=0xffffff)
|
||
|
|
// {
|
||
|
|
// PxMat44 m = PxMat44::identity();
|
||
|
|
//
|
||
|
|
// PxRenderOutput& out = context.mRenderOutput;
|
||
|
|
// out << color << m << 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 Segment& segment, PxReal radius,
|
||
|
|
const PolygonalData& polyData, const FastVertex2ShapeScaling& scaling,
|
||
|
|
const PxMat34& worldTM,
|
||
|
|
PxReal& depth)
|
||
|
|
{
|
||
|
|
// Project capsule
|
||
|
|
PxReal min0 = segment.p0.dot(axis);
|
||
|
|
PxReal max0 = segment.p1.dot(axis);
|
||
|
|
if(min0>max0) PxSwap(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 Segment& segment, PxReal radius,
|
||
|
|
const PolygonalData& polyData,
|
||
|
|
const 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 HullPolygonData* polys = polyData.mPolygons;
|
||
|
|
|
||
|
|
const Matrix34FromTransform worldTM(transform);
|
||
|
|
|
||
|
|
// Test normals
|
||
|
|
for(PxU32 i=0;i<nbPolys;i++)
|
||
|
|
{
|
||
|
|
const 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 HullPolygonData& poly = polys[i];
|
||
|
|
const PxPlane& vertSpacePlane = poly.mPlane;
|
||
|
|
|
||
|
|
const PxVec3 worldNormal = worldTM.rotate(vertSpacePlane.n);
|
||
|
|
|
||
|
|
PxVec3 Cross = CapsuleAxis.cross(worldNormal);
|
||
|
|
if(!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 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 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(PxContactBuffer& contactBuffer,
|
||
|
|
//
|
||
|
|
const PxTransform& convexPose,
|
||
|
|
const PolygonalData& polyData, // Convex data
|
||
|
|
const PxMeshScale& scale,
|
||
|
|
//
|
||
|
|
PxU32 nbPts,
|
||
|
|
const PxVec3* PX_RESTRICT points,
|
||
|
|
PxReal radius, // Capsule's radius
|
||
|
|
//
|
||
|
|
const PxVec3& normal,
|
||
|
|
PxReal contactDistance)
|
||
|
|
{
|
||
|
|
PX_ASSERT(PxAbs(normal.magnitudeSquared()-1)<1e-4f);
|
||
|
|
|
||
|
|
//scaling: transform the ray to vertex space
|
||
|
|
const PxMat34 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( PxContactBuffer& contactBuffer,
|
||
|
|
//
|
||
|
|
const Segment& segment,
|
||
|
|
PxReal radius,
|
||
|
|
PxReal contactDistance,
|
||
|
|
//
|
||
|
|
const PolygonalData& polyData,
|
||
|
|
const PxTransform& transform,
|
||
|
|
const FastVertex2ShapeScaling& scaling,
|
||
|
|
//
|
||
|
|
const PxVec3& normal)
|
||
|
|
{
|
||
|
|
PxU32 numPolygons = polyData.mNbPolygons;
|
||
|
|
const 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;
|
||
|
|
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;
|
||
|
|
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]);
|
||
|
|
// 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(PxContactBuffer& contactBuffer,
|
||
|
|
//
|
||
|
|
const Segment& segment,
|
||
|
|
PxReal radius,
|
||
|
|
//
|
||
|
|
const PxMat34& transform,
|
||
|
|
const PolygonalData& polyData,
|
||
|
|
const FastVertex2ShapeScaling& scaling,
|
||
|
|
//
|
||
|
|
const PxVec3& normal,
|
||
|
|
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;
|
||
|
|
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;
|
||
|
|
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 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++;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
bool Gu::contactCapsuleConvex(GU_CONTACT_METHOD_ARGS)
|
||
|
|
{
|
||
|
|
PX_UNUSED(renderOutput);
|
||
|
|
PX_UNUSED(cache);
|
||
|
|
|
||
|
|
// Get actual shape data
|
||
|
|
// PT: the capsule can be a sphere in this case so we do this special piece of code:
|
||
|
|
PxCapsuleGeometry shapeCapsule = static_cast<const PxCapsuleGeometry&>(shape0);
|
||
|
|
if(shape0.getType()==PxGeometryType::eSPHERE)
|
||
|
|
shapeCapsule.halfHeight = 0.0f;
|
||
|
|
const PxConvexMeshGeometry& shapeConvex = checkedCast<PxConvexMeshGeometry>(shape1);
|
||
|
|
|
||
|
|
PxVec3 onSegment, onConvex;
|
||
|
|
PxReal distance;
|
||
|
|
PxVec3 normal_;
|
||
|
|
{
|
||
|
|
const ConvexMesh* cm = static_cast<const ConvexMesh*>(shapeConvex.convexMesh);
|
||
|
|
|
||
|
|
using namespace aos;
|
||
|
|
Vec3V closA, closB, normalV;
|
||
|
|
GjkStatus status;
|
||
|
|
FloatV dist;
|
||
|
|
{
|
||
|
|
const Vec3V zeroV = V3Zero();
|
||
|
|
const 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 PxMatTransformV aToB(transform1.transformInv(transform0));
|
||
|
|
|
||
|
|
const ConvexHullV convexHull(hullData, zeroV, vScale, vQuat, shapeConvex.scale.isIdentity());
|
||
|
|
|
||
|
|
//transform capsule(a) into the local space of convexHull(b), treat capsule as segment
|
||
|
|
const CapsuleV capsule(aToB.p, aToB.rotate(V3Scale(V3UnitX(), capsuleHalfHeight)), FZero());
|
||
|
|
|
||
|
|
const LocalConvex<CapsuleV> convexA(capsule);
|
||
|
|
const 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;
|
||
|
|
|
||
|
|
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);
|
||
|
|
|
||
|
|
FastVertex2ShapeScaling convexScaling;
|
||
|
|
const bool idtConvexScale = shapeConvex.scale.isIdentity();
|
||
|
|
if(!idtConvexScale)
|
||
|
|
convexScaling.init(shapeConvex.scale);
|
||
|
|
|
||
|
|
PolygonalData polyData;
|
||
|
|
getPolygonalData_Convex(&polyData, _getHullData(shapeConvex), 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 Matrix34FromTransform 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;
|
||
|
|
}
|