feat(physics): wire physx sdk into build
This commit is contained in:
371
engine/third_party/physx/source/geomutils/src/contact/GuCollisionSDF.h
vendored
Normal file
371
engine/third_party/physx/source/geomutils/src/contact/GuCollisionSDF.h
vendored
Normal file
@@ -0,0 +1,371 @@
|
||||
// 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.
|
||||
|
||||
#ifndef GU_COLLISION_SDF_H
|
||||
#define GU_COLLISION_SDF_H
|
||||
|
||||
#include "GuSDF.h"
|
||||
#include "foundation/PxPreprocessor.h"
|
||||
|
||||
namespace physx
|
||||
{
|
||||
namespace Gu
|
||||
{
|
||||
|
||||
// SDF wrapper for collision computations
|
||||
// may be shared by CPU/GPU code in the future
|
||||
//
|
||||
// \detailed CollisionSDF wraps an `SDF` object, providing useful methods for SDF collision.
|
||||
//
|
||||
// Conventions
|
||||
// * The coarse or background SDF is always referred to as _coarse_. Its associated coordinates are called `cPos` and
|
||||
// are in units of `mSpacing*mSubgridSize`. The origin is normally at the first grid point.
|
||||
// * The fine SDF is always referred to as _fine_. Its associated coordinates are called `fPos` and are in units of
|
||||
// `mSpacing`. If the sdf is dense, `cPos` is equivalent to `fPos`. The origin is normally at the first grid point.
|
||||
// * Coordinates in the native space of the SDF are denoted `sPos` and are in native units.
|
||||
|
||||
template <int BytesPerSparsePixelT>
|
||||
PX_FORCE_INLINE PxReal decodeSample(PxReal subgridScalingFactor, PxReal subgridMinSdfValue, const PxU8* data, PxU32 index);
|
||||
|
||||
template <>
|
||||
PX_FORCE_INLINE PxReal decodeSample<1>(PxReal subgridScalingFactor, PxReal subgridMinSdfValue, const PxU8* data, PxU32 index)
|
||||
{
|
||||
return static_cast<PxReal>(data[index]) * subgridScalingFactor + subgridMinSdfValue;
|
||||
}
|
||||
|
||||
template <>
|
||||
PX_FORCE_INLINE PxReal decodeSample<2>(PxReal subgridScalingFactor, PxReal subgridMinSdfValue, const PxU8* data, PxU32 index)
|
||||
{
|
||||
const PxU16* ptr = reinterpret_cast<const PxU16*>(data);
|
||||
return static_cast<PxReal>(ptr[index]) * subgridScalingFactor + subgridMinSdfValue;
|
||||
}
|
||||
|
||||
template <>
|
||||
PX_FORCE_INLINE PxReal decodeSample<4>(PxReal /*unused*/, PxReal /*unused*/, const PxU8* data, PxU32 index)
|
||||
{
|
||||
//If 4 bytes per subgrid pixel are available, then normal floats are used. No need to
|
||||
//de-normalize integer values since the floats already contain real distance values
|
||||
const PxReal* ptr = reinterpret_cast<const PxReal*>(data);
|
||||
return ptr[index];
|
||||
}
|
||||
|
||||
struct CollisionSDF
|
||||
{
|
||||
CollisionSDF(const SDF& sdf): mSdf(sdf), mSdfBoxLower(sdf.mMeshLower), mFDims(sdf.mDims), mInvGridDx(1.0f / sdf.mSpacing),
|
||||
mInvSubgridSize(sdf.mSubgridSize ? 1.0f / sdf.mSubgridSize : 0), mIsDense(sdf.mSubgridSize == 0)
|
||||
{
|
||||
// assume that `mMeshLower` is also the location of the lowest grid point
|
||||
if (mIsDense)
|
||||
{
|
||||
mCDims = mFDims;
|
||||
mCSamples = mCDims;
|
||||
mSdfBoxUpper = sdf.mMeshLower + sdf.mSpacing * PxVec3(static_cast<PxReal>(mFDims.x-1), static_cast<PxReal>(mFDims.y-1), static_cast<PxReal>(mFDims.z-1));
|
||||
}
|
||||
else
|
||||
{
|
||||
mCDims = Dim3(mFDims.x / sdf.mSubgridSize, mFDims.y / sdf.mSubgridSize, mFDims.z / sdf.mSubgridSize);
|
||||
mCSamples = Dim3(mCDims.x + 1, mCDims.y + 1, mCDims.z + 1);
|
||||
mSdfBoxUpper = sdf.mMeshLower + sdf.mSpacing * PxVec3(mFDims);
|
||||
}
|
||||
if (mSdf.mBytesPerSparsePixel == 1)
|
||||
mSubgridScalingFactor = (1.0f / 255.0f) * (mSdf.mSubgridsMaxSdfValue - mSdf.mSubgridsMinSdfValue);
|
||||
else if (mSdf.mBytesPerSparsePixel == 2)
|
||||
mSubgridScalingFactor = (1.0f / 65535.0f) * (mSdf.mSubgridsMaxSdfValue - mSdf.mSubgridsMinSdfValue);
|
||||
|
||||
const PxU32 fW = mSdf.mSdfSubgrids3DTexBlockDim.x * (mSdf.mSubgridSize + 1),
|
||||
fH = mSdf.mSdfSubgrids3DTexBlockDim.y * (mSdf.mSubgridSize + 1);
|
||||
|
||||
mFStrideY = fW;
|
||||
mFStrideZ = fW*fH;
|
||||
|
||||
|
||||
}
|
||||
// clamp `fPos` to the grid on which `sdf` is defined
|
||||
PX_INLINE PxVec3 clampToFine(const PxVec3& fPos) const
|
||||
{
|
||||
if (!mIsDense)
|
||||
return fPos.maximum(PxVec3(0.0f)).minimum(PxVec3(mFDims));
|
||||
return fPos.maximum(PxVec3(0.5f)).minimum(PxVec3(mFDims) + PxVec3(0.5f));
|
||||
}
|
||||
|
||||
// clamp `sPos` to the grid on which `sdf` is defined
|
||||
PX_INLINE PxVec3 clampToBox(const PxVec3& sPos) const
|
||||
{
|
||||
if (mIsDense)
|
||||
return sPos.maximum(mSdfBoxLower+PxVec3(0.5f*mSdf.mSpacing)).minimum(mSdfBoxUpper+PxVec3(0.5f*mSdf.mSpacing));
|
||||
return sPos.maximum(mSdfBoxLower).minimum(mSdfBoxUpper);
|
||||
}
|
||||
|
||||
// Utility to convert from x/y/z indices to a linear index given the grid size (only width and height required)
|
||||
PX_FORCE_INLINE PX_CUDA_CALLABLE static PxU32 idx3D(PxU32 x, PxU32 y, PxU32 z, PxU32 width, PxU32 height)
|
||||
{
|
||||
return z * width * height + y * width + x;
|
||||
}
|
||||
|
||||
|
||||
static PX_INLINE PxReal TriLerpWithGradient(
|
||||
const PxReal f000,
|
||||
const PxReal f100,
|
||||
const PxReal f010,
|
||||
const PxReal f110,
|
||||
const PxReal f001,
|
||||
const PxReal f101,
|
||||
const PxReal f011,
|
||||
const PxReal f111,
|
||||
const PxReal tx,
|
||||
const PxReal ty,
|
||||
const PxReal tz,
|
||||
PxVec3* grad = NULL)
|
||||
{
|
||||
if (grad)
|
||||
{
|
||||
const PxReal a = f100 - f000;
|
||||
const PxReal b = f110 - f010;
|
||||
const PxReal c = f101 - f001;
|
||||
const PxReal d = f111 - f011;
|
||||
grad->x = a + (b - (a)) * ty + (c + (d - (c)) * ty - (a + (b - (a)) * ty)) * tz;
|
||||
grad->y = f010 + tx * (b) - (f000 + tx * (a)) + (f011 + tx * (d) - (f001 + tx * (c)) - (f010 + tx * (b) - (f000 + tx * (a)))) * tz;
|
||||
grad->z = f001 + tx * (c) + ty * (f011 + tx * (d) - (f001 + tx * (c))) - (f000 + tx * (a) + ty * (f010 + tx * (b) - (f000 + tx * (a))));
|
||||
}
|
||||
|
||||
return PxTriLerp(
|
||||
f000,
|
||||
f100,
|
||||
f010,
|
||||
f110,
|
||||
f001,
|
||||
f101,
|
||||
f011,
|
||||
f111,
|
||||
tx,
|
||||
ty,
|
||||
tz);
|
||||
}
|
||||
|
||||
template <int BytesPerSparsePixelT>
|
||||
PX_INLINE PxReal interpolateSubgrid (const PxU8* subgridBase, PxU32 baseIdx, PxReal x, PxReal y, PxReal z, PxVec3* gradient = NULL) const
|
||||
{
|
||||
PX_COMPILE_TIME_ASSERT(
|
||||
BytesPerSparsePixelT == 1 || BytesPerSparsePixelT == 2 || BytesPerSparsePixelT == 4);
|
||||
|
||||
return TriLerpWithGradient(
|
||||
decodeSample<BytesPerSparsePixelT>(mSubgridScalingFactor, mSdf.mSubgridsMinSdfValue, subgridBase, baseIdx ),
|
||||
decodeSample<BytesPerSparsePixelT>(mSubgridScalingFactor, mSdf.mSubgridsMinSdfValue, subgridBase, baseIdx+1 ),
|
||||
decodeSample<BytesPerSparsePixelT>(mSubgridScalingFactor, mSdf.mSubgridsMinSdfValue, subgridBase, baseIdx+mFStrideY ),
|
||||
decodeSample<BytesPerSparsePixelT>(mSubgridScalingFactor, mSdf.mSubgridsMinSdfValue, subgridBase, baseIdx+mFStrideY+1 ),
|
||||
decodeSample<BytesPerSparsePixelT>(mSubgridScalingFactor, mSdf.mSubgridsMinSdfValue, subgridBase, baseIdx+mFStrideZ ),
|
||||
decodeSample<BytesPerSparsePixelT>(mSubgridScalingFactor, mSdf.mSubgridsMinSdfValue, subgridBase, baseIdx+mFStrideZ+1 ),
|
||||
decodeSample<BytesPerSparsePixelT>(mSubgridScalingFactor, mSdf.mSubgridsMinSdfValue, subgridBase, baseIdx+mFStrideZ+mFStrideY ),
|
||||
decodeSample<BytesPerSparsePixelT>(mSubgridScalingFactor, mSdf.mSubgridsMinSdfValue, subgridBase, baseIdx+mFStrideZ+mFStrideY+1),
|
||||
x, y, z, gradient);
|
||||
}
|
||||
|
||||
// interpolate the values of `array` at position `fPos`
|
||||
// input vector `fPos` is in units of subgrid cells, with 0 corresponding to the subgrid origin
|
||||
PX_INLINE PxReal evaluateSubgrid(const PxU32 subgridInfo, const PxVec3& fPos, PxVec3* gradient = NULL) const
|
||||
{
|
||||
const PxU32 sgSamples = mSdf.mSubgridSize + 1;
|
||||
PX_ASSERT(fPos.x >= 0 && fPos.y >= 0 && fPos.z >= 0);
|
||||
PX_ASSERT(fPos.x < sgSamples && fPos.y < sgSamples && fPos.z < sgSamples);
|
||||
|
||||
// find the subgrid offset in memory
|
||||
PxU32 xSubgrid, ySubgrid, zSubgrid;
|
||||
SDF::decodeTriple(subgridInfo, xSubgrid, ySubgrid, zSubgrid);
|
||||
xSubgrid *= (mSdf.mSubgridSize + 1);
|
||||
ySubgrid *= (mSdf.mSubgridSize + 1);
|
||||
zSubgrid *= (mSdf.mSubgridSize + 1);
|
||||
|
||||
// reference subgrid point
|
||||
const PxU32 x = PxMin(static_cast<PxU32>(fPos.x), sgSamples - 2),
|
||||
y = PxMin(static_cast<PxU32>(fPos.y), sgSamples - 2),
|
||||
z = PxMin(static_cast<PxU32>(fPos.z), sgSamples - 2);
|
||||
|
||||
|
||||
// offset values by the subgrid memory offset
|
||||
const PxU32 xM = xSubgrid + x, yM = ySubgrid + y, zM = zSubgrid + z;
|
||||
|
||||
const PxU32 base = mFStrideZ * zM + mFStrideY * yM + xM;
|
||||
switch (mSdf.mBytesPerSparsePixel)
|
||||
{
|
||||
case 1:
|
||||
return interpolateSubgrid<1>(mSdf.mSubgridSdf, base, fPos.x - x, fPos.y - y, fPos.z - z, gradient);
|
||||
case 2:
|
||||
return interpolateSubgrid<2>(mSdf.mSubgridSdf, base, fPos.x - x, fPos.y - y, fPos.z - z, gradient);
|
||||
case 4:
|
||||
return interpolateSubgrid<4>(mSdf.mSubgridSdf, base, fPos.x - x, fPos.y - y, fPos.z - z, gradient);
|
||||
default: // never reached
|
||||
PX_ASSERT(0);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// interpolate the values of `array` at position `cPos`.
|
||||
// `cPos` must be >= 0 and < `cDims`
|
||||
PX_INLINE PxReal evaluateCoarse(const PxVec3& cPos, PxVec3* gradient = NULL) const
|
||||
{
|
||||
PX_ASSERT(cPos.x >= 0 && cPos.y >= 0 && cPos.z >= 0);
|
||||
PX_ASSERT(cPos.x < mCSamples.x && cPos.y < mCSamples.y && cPos.z < mCSamples.z);
|
||||
|
||||
// reference grid point
|
||||
const PxU32 x = PxMin(static_cast<PxU32>(cPos.x), mCSamples.x - 2),
|
||||
y = PxMin(static_cast<PxU32>(cPos.y), mCSamples.y - 2),
|
||||
z = PxMin(static_cast<PxU32>(cPos.z), mCSamples.z - 2);
|
||||
|
||||
const PxU32 w = mCSamples.x, h = mCSamples.y;
|
||||
const PxU32 cStrideY = w, cStrideZ = w*h; // Note that this is sample, not cell, stride
|
||||
const PxU32 base = cStrideZ * z + cStrideY * y + x;
|
||||
|
||||
return TriLerpWithGradient(
|
||||
mSdf.mSdf[base],
|
||||
mSdf.mSdf[base+1],
|
||||
mSdf.mSdf[base+cStrideY],
|
||||
mSdf.mSdf[base+cStrideY+1],
|
||||
mSdf.mSdf[base+cStrideZ],
|
||||
mSdf.mSdf[base+cStrideZ+1],
|
||||
mSdf.mSdf[base+cStrideZ+cStrideY],
|
||||
mSdf.mSdf[base+cStrideZ+cStrideY+1],
|
||||
cPos.x - x, cPos.y - y, cPos.z - z, gradient);
|
||||
}
|
||||
|
||||
// sample the SDF at `fPos`
|
||||
// input vector `fPos` is in units of (sub-) grid cells, with integer values representing nodes
|
||||
PX_INLINE PxReal sample(PxVec3 fPos, PxVec3* gradient = NULL) const
|
||||
{
|
||||
if (mIsDense)
|
||||
fPos -= PxVec3(0.5);
|
||||
PX_ASSERT(fPos.x >= 0 && fPos.y >= 0 && fPos.z >= 0);
|
||||
PX_ASSERT(fPos.x <= mFDims.x && fPos.y <= mFDims.y && fPos.z <= mFDims.z);
|
||||
if (mIsDense) // fPos = cPos
|
||||
return evaluateCoarse(fPos, gradient);
|
||||
|
||||
// coarse reference gridpoint index
|
||||
const Dim3 cBase(
|
||||
PxMin(static_cast<PxU32>(fPos.x * mInvSubgridSize), mCSamples.x - 2),
|
||||
PxMin(static_cast<PxU32>(fPos.y * mInvSubgridSize), mCSamples.y - 2),
|
||||
PxMin(static_cast<PxU32>(fPos.z * mInvSubgridSize), mCSamples.z - 2)
|
||||
);
|
||||
|
||||
const PxU32 i = idx3D(cBase.x, cBase.y, cBase.z, mCDims.x, mCDims.y);
|
||||
const PxU32 subgridInfo = mSdf.mSubgridStartSlots[i];
|
||||
|
||||
if (subgridInfo == 0xFFFFFFFF) // Evaluate (coarse) background of sparse SDF
|
||||
return evaluateCoarse((fPos * mInvSubgridSize).minimum(PxVec3(PxReal(mCSamples.x), PxReal(mCSamples.y), PxReal(mCSamples.z))), gradient);
|
||||
|
||||
// offset to subgrid origin
|
||||
PxVec3 fPosInSubgrid;
|
||||
fPosInSubgrid.x = PxMax(0.f, fPos.x - cBase.x * mSdf.mSubgridSize);
|
||||
fPosInSubgrid.y = PxMax(0.f, fPos.y - cBase.y * mSdf.mSubgridSize);
|
||||
fPosInSubgrid.z = PxMax(0.f, fPos.z - cBase.z * mSdf.mSubgridSize);
|
||||
|
||||
return evaluateSubgrid(subgridInfo, fPosInSubgrid, gradient);
|
||||
}
|
||||
|
||||
// evaluate & interpolate `sdf` (in `sdf`'s "vertex" space) at `sPos`
|
||||
// when outside the sdf grid, this should be considered an upper bound
|
||||
// TODO(CA): add more clamps or prove they're unnecessary
|
||||
inline PxReal dist(const PxVec3& sPos, PxVec3* gradient = NULL) const
|
||||
{
|
||||
// clamped to SDF support
|
||||
const PxVec3 boxPos = clampToBox(sPos);
|
||||
const PxVec3 diff = sPos - boxPos;
|
||||
const PxReal diffMag = diff.magnitude();
|
||||
const PxVec3 fPos = (boxPos - mSdfBoxLower) * mInvGridDx;
|
||||
const PxReal distance = sample(clampToFine(fPos), gradient) + diffMag; // division inaccuracy necessitates clamp
|
||||
if (gradient && diffMag > 0.0f)
|
||||
*gradient = diff; //A quite coarse approximation but it's only used if the sample point is outside of the sdf's bounding box
|
||||
return distance;
|
||||
}
|
||||
|
||||
// evaluate & interpolate `sdf` at `sPos` (in `sdf`'s "vertex" space), and compute its gradient
|
||||
inline PxVec3 grad(const PxVec3& sPos) const
|
||||
{
|
||||
// clamped to SDF support
|
||||
const PxVec3 boxPos = clampToBox(sPos);
|
||||
const PxVec3 fPos = (boxPos - mSdfBoxLower) * mInvGridDx;
|
||||
|
||||
PxVec3 gradient;
|
||||
|
||||
if ( fPos.x >= 1.0f && fPos.x <= mFDims.x - 2.0f &&
|
||||
fPos.y >= 1.0f && fPos.y <= mFDims.y - 2.0f &&
|
||||
fPos.z >= 1.0f && fPos.z <= mFDims.z - 2.0f)
|
||||
{
|
||||
gradient.x = sample(PxVec3(fPos.x+1, fPos.y, fPos.z)) - sample(PxVec3(fPos.x-1, fPos.y, fPos.z));
|
||||
gradient.y = sample(PxVec3(fPos.x, fPos.y+1, fPos.z)) - sample(PxVec3(fPos.x, fPos.y -1, fPos.z));
|
||||
gradient.z = sample(PxVec3(fPos.x, fPos.y, fPos.z+1)) - sample(PxVec3(fPos.x, fPos.y, fPos.z -1));
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
const PxReal h = mSdf.mSpacing;
|
||||
gradient.x = dist(PxVec3(sPos.x+h, sPos.y, sPos.z)) - dist(PxVec3(sPos.x-h, sPos.y, sPos.z));
|
||||
gradient.y = dist(PxVec3(sPos.x, sPos.y+h, sPos.z)) - dist(PxVec3(sPos.x, sPos.y-h, sPos.z));
|
||||
gradient.z = dist(PxVec3(sPos.x, sPos.y, sPos.z+h)) - dist(PxVec3(sPos.x, sPos.y, sPos.z-h));
|
||||
}
|
||||
gradient *= 0.5f / mSdf.mSpacing;
|
||||
|
||||
return gradient;
|
||||
}
|
||||
|
||||
// Estimate the value and gradient of `sdf` at `sPos`, using gradient information when `sPos` is
|
||||
// outside the SDF grid. Return `PX_MAX_F32` when the distance exceeds `cutoffDistance`
|
||||
PX_INLINE PxReal distUsingGradient(const PxVec3& sPos, PxVec3& gradient, const PxReal& cutoffDistance) const
|
||||
{
|
||||
// clamped to SDF support
|
||||
const PxVec3 boxPos = clampToBox(sPos);
|
||||
const PxVec3 diff = sPos - boxPos;
|
||||
|
||||
const PxVec3 fPos = (boxPos - mSdfBoxLower) * mInvGridDx;
|
||||
const PxReal dist = sample(clampToFine(fPos));
|
||||
|
||||
if (dist > cutoffDistance)
|
||||
return PX_MAX_F32;
|
||||
|
||||
gradient = grad(sPos);
|
||||
gradient = (gradient.getNormalized() * PxAbs(dist) + diff).getNormalized();
|
||||
return dist + gradient.dot(diff);
|
||||
}
|
||||
|
||||
// data members
|
||||
const SDF& mSdf;
|
||||
PxVec3 mSdfBoxLower, mSdfBoxUpper; // Positions of the first and last grid points
|
||||
Dim3 mCDims, mFDims; // background and high-resolution SDF dimensions in cells. Equal if dense.
|
||||
// fDims is equally divisible by subgridSize, which is also in cells, for sparse SDFs
|
||||
// the coarse grid has cDims+1 (cDims) samples per dimension for sparse (dense) SDFs
|
||||
// subgrids have subgridSize+1 samples per dimension
|
||||
Dim3 mCSamples; // Number of samples in each dimension. Equal to cDims for dense, and cDims + 1 for spares SDFs
|
||||
|
||||
PxReal mInvGridDx; // invSdfDx
|
||||
PxReal mInvSubgridSize; // fineToCoarse
|
||||
bool mIsDense;
|
||||
PxReal mSubgridScalingFactor; // purely for optimization
|
||||
PxU32 mFStrideY, mFStrideZ;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
695
engine/third_party/physx/source/geomutils/src/contact/GuContactBoxBox.cpp
vendored
Normal file
695
engine/third_party/physx/source/geomutils/src/contact/GuContactBoxBox.cpp
vendored
Normal file
@@ -0,0 +1,695 @@
|
||||
// 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 "GuContactMethodImpl.h"
|
||||
#include "CmMatrix34.h"
|
||||
#include "foundation/PxUtilities.h"
|
||||
|
||||
using namespace physx;
|
||||
using namespace Gu;
|
||||
using namespace Cm;
|
||||
|
||||
#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 PxMat34& transform0, const PxMat34& transform1, PxReal contactDistance);*/
|
||||
|
||||
static PxI32 doBoxBoxContactGeneration(PxContactBuffer& contactBuffer,
|
||||
const PxVec3& extents0, const PxVec3& extents1,
|
||||
PxU32& collisionData,
|
||||
const PxMat34& transform0, const PxMat34& transform1, PxReal contactDistance);
|
||||
|
||||
bool Gu::contactBoxBox(GU_CONTACT_METHOD_ARGS)
|
||||
{
|
||||
PX_UNUSED(renderOutput);
|
||||
|
||||
// Get actual shape data
|
||||
const PxBoxGeometry& shapeBox0 = checkedCast<PxBoxGeometry>(shape0);
|
||||
const PxBoxGeometry& shapeBox1 = checkedCast<PxBoxGeometry>(shape1);
|
||||
|
||||
PxU32 pd = PxU32(cache.mPairData);
|
||||
PxI32 Nb = doBoxBoxContactGeneration(contactBuffer,
|
||||
shapeBox0.halfExtents, shapeBox1.halfExtents,
|
||||
pd,
|
||||
Matrix34FromTransform(transform0), Matrix34FromTransform(transform1),
|
||||
params.mContactDistance);
|
||||
|
||||
cache.mPairData = PxTo8(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;
|
||||
}
|
||||
|
||||
// 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[],
|
||||
PxContactBuffer& contactBuffer, const PxVec3& contactNormal,
|
||||
PxReal y1, PxReal z1, const PxVec3& box2,
|
||||
const PxMat34& transform0, const PxMat34& transform1, PxReal contactDistance)
|
||||
{
|
||||
// PxI32 NbContacts=0;
|
||||
contactBuffer.reset();
|
||||
y1 += contactDistance;
|
||||
z1 += contactDistance;
|
||||
|
||||
const PxMat34 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(PxContactBuffer& contactBuffer,
|
||||
const PxVec3& extents0, const PxVec3& extents1,
|
||||
PxU32& collisionData,
|
||||
const PxMat34& transform0, const PxMat34& 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
|
||||
PxMat34 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);
|
||||
}
|
||||
}
|
||||
442
engine/third_party/physx/source/geomutils/src/contact/GuContactCapsuleBox.cpp
vendored
Normal file
442
engine/third_party/physx/source/geomutils/src/contact/GuContactCapsuleBox.cpp
vendored
Normal file
@@ -0,0 +1,442 @@
|
||||
// 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 "GuIntersectionRayBox.h"
|
||||
#include "GuDistanceSegmentBox.h"
|
||||
#include "GuInternal.h"
|
||||
#include "GuContactMethodImpl.h"
|
||||
#include "GuBoxConversion.h"
|
||||
|
||||
#include "foundation/PxUtilities.h"
|
||||
|
||||
using namespace physx;
|
||||
using namespace Gu;
|
||||
|
||||
/*namespace Gu
|
||||
{
|
||||
const PxU8* getBoxEdges();
|
||||
}*/
|
||||
|
||||
/////////
|
||||
/*#include "common/PxRenderOutput.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);
|
||||
|
||||
PxRenderOutput& 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();
|
||||
|
||||
RenderOutput& out = context.mRenderOutput;
|
||||
out << color << m << 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) PxSwap(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(!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( PxContactBuffer& contactBuffer,
|
||||
//
|
||||
const Segment& segment,
|
||||
PxReal radius,
|
||||
//
|
||||
const Box& worldBox,
|
||||
//
|
||||
const PxVec3& normal,
|
||||
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( PxContactBuffer& 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;
|
||||
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;
|
||||
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++];
|
||||
// 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( PxContactBuffer& contactBuffer,
|
||||
//
|
||||
const Segment& segment,
|
||||
PxReal radius,
|
||||
//
|
||||
const Box& worldBox,
|
||||
//
|
||||
const PxVec3& normal,
|
||||
PxReal contactDistance)
|
||||
{
|
||||
const PxU8* PX_RESTRICT Indices = getBoxEdges();
|
||||
|
||||
PxVec3 Pts[8];
|
||||
worldBox.computeBoxPoints(Pts);
|
||||
|
||||
PxVec3 s0 = segment.p0;
|
||||
PxVec3 s1 = segment.p1;
|
||||
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;
|
||||
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++];
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Gu::contactCapsuleBox(GU_CONTACT_METHOD_ARGS)
|
||||
{
|
||||
PX_UNUSED(renderOutput);
|
||||
PX_UNUSED(cache);
|
||||
|
||||
// Get actual shape data
|
||||
const PxCapsuleGeometry& shapeCapsule = checkedCast<PxCapsuleGeometry>(shape0);
|
||||
const PxBoxGeometry& shapeBox = checkedCast<PxBoxGeometry>(shape1);
|
||||
|
||||
// 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;
|
||||
}
|
||||
148
engine/third_party/physx/source/geomutils/src/contact/GuContactCapsuleCapsule.cpp
vendored
Normal file
148
engine/third_party/physx/source/geomutils/src/contact/GuContactCapsuleCapsule.cpp
vendored
Normal file
@@ -0,0 +1,148 @@
|
||||
// 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 "GuDistanceSegmentSegment.h"
|
||||
#include "GuContactMethodImpl.h"
|
||||
#include "GuInternal.h"
|
||||
|
||||
using namespace physx;
|
||||
|
||||
bool Gu::contactCapsuleCapsule(GU_CONTACT_METHOD_ARGS)
|
||||
{
|
||||
PX_UNUSED(renderOutput);
|
||||
PX_UNUSED(cache);
|
||||
|
||||
const PxCapsuleGeometry& capsuleGeom0 = checkedCast<PxCapsuleGeometry>(shape0);
|
||||
const PxCapsuleGeometry& capsuleGeom1 = checkedCast<PxCapsuleGeometry>(shape1);
|
||||
|
||||
// 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;
|
||||
}
|
||||
577
engine/third_party/physx/source/geomutils/src/contact/GuContactCapsuleConvex.cpp
vendored
Normal file
577
engine/third_party/physx/source/geomutils/src/contact/GuContactCapsuleConvex.cpp
vendored
Normal file
@@ -0,0 +1,577 @@
|
||||
// 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;
|
||||
}
|
||||
637
engine/third_party/physx/source/geomutils/src/contact/GuContactCapsuleMesh.cpp
vendored
Normal file
637
engine/third_party/physx/source/geomutils/src/contact/GuContactCapsuleMesh.cpp
vendored
Normal file
@@ -0,0 +1,637 @@
|
||||
// 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 "GuIntersectionEdgeEdge.h"
|
||||
#include "GuDistanceSegmentTriangle.h"
|
||||
#include "GuIntersectionRayTriangle.h"
|
||||
#include "GuIntersectionTriangleBox.h"
|
||||
#include "GuInternal.h"
|
||||
#include "GuContactMethodImpl.h"
|
||||
#include "GuFeatureCode.h"
|
||||
#include "GuMidphaseInterface.h"
|
||||
#include "GuEntityReport.h"
|
||||
#include "GuHeightFieldUtil.h"
|
||||
#include "GuConvexEdgeFlags.h"
|
||||
#include "GuBox.h"
|
||||
#include "CmMatrix34.h"
|
||||
|
||||
using namespace physx;
|
||||
using namespace Gu;
|
||||
using namespace Cm;
|
||||
|
||||
#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 "PxRenderOutput.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();
|
||||
|
||||
PxRenderOutput& out = context.mRenderOutput;
|
||||
out << color << m << PxRenderOutput::LINES << a << b;
|
||||
}
|
||||
static void gVisualizeTri(const PxVec3& a, const PxVec3& b, const PxVec3& c, PxcNpThreadContext& context, PxU32 color=0xffffff)
|
||||
{
|
||||
PxMat44 m = PxMat44::identity();
|
||||
|
||||
PxRenderOutput& out = context.mRenderOutput;
|
||||
out << color << m << PxRenderOutput::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) PxSwap(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[PxGetNextIndex3(i)];
|
||||
const PxVec3 edge = e0 - e1;
|
||||
|
||||
PxVec3 cross = capsuleAxis.cross(edge);
|
||||
if(!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 PxMat34& meshAbsPose, PxContactBuffer& 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);
|
||||
PxRenderOutput((PxRenderBufferImpl&)s->getRenderBuffer()) << PxRenderOutput::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 PxMat34& meshAbsPose, PxContactBuffer& 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;
|
||||
makeFatEdge(s0, s1, fatBoxEdgeCoeff);
|
||||
|
||||
for(PxU32 i=0;i<3;i++)
|
||||
{
|
||||
PxReal dist;
|
||||
PxVec3 ip;
|
||||
if(intersectEdgeEdge(triVerts[i], triVerts[PxGetNextIndex3(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);
|
||||
PxRenderOutput((PxRenderBufferImpl&)s->getRenderBuffer()) << PxRenderOutput::LINES << PxDebugColor::eARGB_BLUE // red
|
||||
<< ip << (ip + wn * 10.0f);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void PxcGenerateEEContacts2( const PxMat34& meshAbsPose, PxContactBuffer& 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;
|
||||
makeFatEdge(s0, s1, fatBoxEdgeCoeff);
|
||||
|
||||
for(PxU32 i=0;i<3;i++)
|
||||
{
|
||||
PxReal dist;
|
||||
PxVec3 ip;
|
||||
if(intersectEdgeEdge(triVerts[i], triVerts[PxGetNextIndex3(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);
|
||||
PxRenderOutput((PxRenderBufferImpl&)s->getRenderBuffer()) << PxRenderOutput::LINES << PxDebugColor::eARGB_BLUE // red
|
||||
<< ip << (ip + wn * 10.0f);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
struct CapsuleMeshContactGeneration
|
||||
{
|
||||
PxContactBuffer& mContactBuffer;
|
||||
const PxMat34 mMeshAbsPose;
|
||||
const Segment& mMeshCapsule;
|
||||
#ifdef USE_AABB_TRI_CULLING
|
||||
PxVec3p mBC;
|
||||
PxVec3p mBE;
|
||||
#endif
|
||||
PxReal mInflatedRadius;
|
||||
PxReal mContactDistance;
|
||||
PxReal mShapeCapsuleRadius;
|
||||
|
||||
CapsuleMeshContactGeneration(PxContactBuffer& contactBuffer, const PxTransform& transform1, const Segment& meshCapsule, PxReal inflatedRadius, PxReal contactDistance, PxReal shapeCapsuleRadius) :
|
||||
mContactBuffer (contactBuffer),
|
||||
mMeshAbsPose (Matrix34FromTransform(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 PxVec3p 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 PxTrianglePadded& tri, PxU8 extraData/*, const PxU32* vertInds*/)
|
||||
{
|
||||
#ifdef USE_AABB_TRI_CULLING
|
||||
#if VISUALIZE_CULLING_BOX
|
||||
{
|
||||
PxRenderOutput& out = context.mRenderOutput;
|
||||
PxTransform idt = PxTransform(PxIdentity);
|
||||
out << idt;
|
||||
out << 0xffffffff;
|
||||
out << PxDebugBox(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 = 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<PxGeomRaycastHit>
|
||||
{
|
||||
CapsuleMeshContactGeneration mGeneration;
|
||||
const TriangleMesh* mMeshData;
|
||||
|
||||
CapsuleMeshContactGenerationCallback_NoScale(
|
||||
PxContactBuffer& contactBuffer,
|
||||
const PxTransform& transform1, const Segment& meshCapsule,
|
||||
PxReal inflatedRadius, PxReal contactDistance,
|
||||
PxReal shapeCapsuleRadius, const TriangleMesh* meshData
|
||||
) :
|
||||
MeshHitCallback<PxGeomRaycastHit> (CallbackMode::eMULTIPLE),
|
||||
mGeneration (contactBuffer, transform1, meshCapsule, inflatedRadius, contactDistance, shapeCapsuleRadius),
|
||||
mMeshData (meshData)
|
||||
{
|
||||
PX_ASSERT(contactBuffer.count==0);
|
||||
}
|
||||
|
||||
virtual PxAgain processHit(
|
||||
const PxGeomRaycastHit& hit, const PxVec3& v0, const PxVec3& v1, const PxVec3& v2, PxReal&, const PxU32* /*vInds*/)
|
||||
{
|
||||
PxTrianglePadded tri;
|
||||
// PT: TODO: revisit this, avoid the copy
|
||||
tri.verts[0] = v0;
|
||||
tri.verts[1] = v1;
|
||||
tri.verts[2] = v2;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
private:
|
||||
CapsuleMeshContactGenerationCallback_NoScale& operator=(const CapsuleMeshContactGenerationCallback_NoScale&);
|
||||
};
|
||||
|
||||
struct CapsuleMeshContactGenerationCallback_Scale : CapsuleMeshContactGenerationCallback_NoScale
|
||||
{
|
||||
const FastVertex2ShapeScaling& mScaling;
|
||||
|
||||
CapsuleMeshContactGenerationCallback_Scale(
|
||||
PxContactBuffer& contactBuffer,
|
||||
const PxTransform& transform1, const Segment& meshCapsule,
|
||||
PxReal inflatedRadius, const FastVertex2ShapeScaling& scaling, PxReal contactDistance,
|
||||
PxReal shapeCapsuleRadius, const TriangleMesh* meshData
|
||||
) :
|
||||
CapsuleMeshContactGenerationCallback_NoScale(contactBuffer, transform1, meshCapsule, inflatedRadius, contactDistance, shapeCapsuleRadius, meshData),
|
||||
mScaling (scaling)
|
||||
{
|
||||
}
|
||||
|
||||
virtual PxAgain processHit(
|
||||
const PxGeomRaycastHit& hit, const PxVec3& v0, const PxVec3& v1, const PxVec3& v2, PxReal&, const PxU32* /*vInds*/)
|
||||
{
|
||||
PxTrianglePadded tri;
|
||||
getScaledVertices(tri.verts, v0, v1, v2, false, mScaling);
|
||||
|
||||
const PxU32 triangleIndex = hit.faceIndex;
|
||||
|
||||
//ML::set all the edges to be active, if the mExtraTrigData exist, we overwrite this flag
|
||||
PxU8 extraData = getConvexEdgeFlags(mMeshData->getExtraTrigData(), triangleIndex);
|
||||
|
||||
if(mScaling.flipsNormal())
|
||||
flipConvexEdgeFlags(extraData);
|
||||
|
||||
mGeneration.processTriangle(triangleIndex, tri, extraData);
|
||||
return true;
|
||||
}
|
||||
|
||||
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 = checkedCast<PxCapsuleGeometry>(shape0);
|
||||
const PxTriangleMeshGeometry& shapeMesh = checkedCast<PxTriangleMeshGeometry>(shape1);
|
||||
|
||||
const PxReal inflatedRadius = shapeCapsule.radius + params.mContactDistance; //AM: inflate!
|
||||
const Segment meshCapsule = computeLocalCapsule(transform0, transform1, shapeCapsule);
|
||||
|
||||
const TriangleMesh* meshData = _getMeshData(shapeMesh);
|
||||
|
||||
//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 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 : OverlapReport
|
||||
{
|
||||
CapsuleMeshContactGeneration mGeneration;
|
||||
const HeightFieldUtil& mHfUtil;
|
||||
const PxTransform& mTransform1;
|
||||
|
||||
CapsuleHeightfieldContactGenerationCallback(
|
||||
PxContactBuffer& contactBuffer,
|
||||
const PxTransform& transform1, const 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 reportTouchedTris(PxU32 nb, const PxU32* indices)
|
||||
{
|
||||
const PxU8 nextInd[] = {2,0,1};
|
||||
|
||||
while(nb--)
|
||||
{
|
||||
const PxU32 triangleIndex = *indices++;
|
||||
|
||||
PxU32 vertIndices[3];
|
||||
PxTrianglePadded 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 = checkedCast<PxCapsuleGeometry>(shape0);
|
||||
const PxHeightFieldGeometry& shapeMesh = checkedCast<PxHeightFieldGeometry>(shape1);
|
||||
|
||||
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 HeightFieldUtil hfUtil(shapeMesh);
|
||||
|
||||
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 AABB:
|
||||
|
||||
// PT: TODO: improve these bounds (see computeCapsuleBounds)
|
||||
hfUtil.overlapAABBTriangles(transform0, transform1, getLocalCapsuleBounds(inflatedRadius, shapeCapsule.halfHeight), callback);
|
||||
|
||||
return contactBuffer.count > 0;
|
||||
}
|
||||
1030
engine/third_party/physx/source/geomutils/src/contact/GuContactConvexConvex.cpp
vendored
Normal file
1030
engine/third_party/physx/source/geomutils/src/contact/GuContactConvexConvex.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
66
engine/third_party/physx/source/geomutils/src/contact/GuContactConvexCoreConvex.cpp
vendored
Normal file
66
engine/third_party/physx/source/geomutils/src/contact/GuContactConvexCoreConvex.cpp
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
// 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 "GuGJKPenetration.h"
|
||||
#include "GuEPA.h"
|
||||
#include "GuVecConvexHull.h"
|
||||
#include "GuVecConvexHullNoScale.h"
|
||||
#include "GuContactMethodImpl.h"
|
||||
#include "GuPCMShapeConvex.h"
|
||||
#include "GuPCMContactGen.h"
|
||||
#include "GuConvexGeometry.h"
|
||||
#include "GuConvexSupport.h"
|
||||
#include "GuRefGjkEpa.h"
|
||||
|
||||
using namespace physx;
|
||||
using namespace Gu;
|
||||
using namespace aos;
|
||||
|
||||
bool Gu::contactConvexCoreConvex(GU_CONTACT_METHOD_ARGS)
|
||||
{
|
||||
PX_UNUSED(cache);
|
||||
PX_UNUSED(renderOutput);
|
||||
|
||||
const PxVec3 shift = (transform0.p + transform1.p) * 0.5f;
|
||||
const PxTransform pose0(transform0.p - shift, transform0.q);
|
||||
const PxTransform pose1(transform1.p - shift, transform1.q);
|
||||
const PxReal contactDist = params.mContactDistance;
|
||||
|
||||
ConvexShape convex0; Gu::makeConvexShape(shape0, pose0, convex0);
|
||||
ConvexShape convex1; Gu::makeConvexShape(shape1, pose1, convex1);
|
||||
PX_ASSERT(convex0.isValid() && convex1.isValid());
|
||||
|
||||
PxVec3 normal, points[Gu::MAX_CONVEX_CONTACTS];
|
||||
PxReal dists[Gu::MAX_CONVEX_CONTACTS];
|
||||
if (PxU32 count = Gu::generateContacts(convex0, convex1, contactDist, normal, points, dists))
|
||||
for (PxU32 i = 0; i < count; ++i)
|
||||
contactBuffer.contact(points[i] + shift, normal, dists[i]);
|
||||
|
||||
return contactBuffer.count > 0;
|
||||
}
|
||||
435
engine/third_party/physx/source/geomutils/src/contact/GuContactConvexCoreMesh.cpp
vendored
Normal file
435
engine/third_party/physx/source/geomutils/src/contact/GuContactConvexCoreMesh.cpp
vendored
Normal file
@@ -0,0 +1,435 @@
|
||||
// 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 "GuContactPolygonPolygon.h"
|
||||
#include "GuContactMethodImpl.h"
|
||||
#include "GuMidphaseInterface.h"
|
||||
#include "GuHeightFieldUtil.h"
|
||||
#include "GuEntityReport.h"
|
||||
#include "GuBounds.h"
|
||||
#include "GuConvexGeometry.h"
|
||||
#include "GuConvexSupport.h"
|
||||
#include "GuContactReduction.h"
|
||||
#include <GuTriangleMesh.h>
|
||||
|
||||
using namespace physx;
|
||||
using namespace Gu;
|
||||
using namespace Cm;
|
||||
using namespace aos;
|
||||
using namespace intrinsics;
|
||||
|
||||
namespace
|
||||
{
|
||||
struct TriangleMeshTriangles
|
||||
{
|
||||
const TriangleMesh* data;
|
||||
const PxMeshScale& scale;
|
||||
|
||||
TriangleMeshTriangles(const TriangleMesh* _data, const PxMeshScale& _scale)
|
||||
:
|
||||
data(_data), scale(_scale)
|
||||
{}
|
||||
|
||||
void getVertexIndices(PxU32 triIndex, PxU32& i0, PxU32& i1, PxU32& i2) const
|
||||
{
|
||||
const void* tris = data->getTriangles();
|
||||
const bool ints16bit = data->has16BitIndices();
|
||||
getVertexRefs(triIndex, i0, i1, i2, tris, ints16bit);
|
||||
}
|
||||
|
||||
PxVec3 getVertex(PxU32 vertIndex) const
|
||||
{
|
||||
const PxVec3* verts = data->getVertices();
|
||||
return scale.transform(verts[vertIndex]);
|
||||
}
|
||||
|
||||
bool hasAdjacency() const
|
||||
{
|
||||
return data->getAdjacencies() != NULL;
|
||||
}
|
||||
|
||||
PxU32 getAdjacentTriIndex(PxU32 triIndex, PxU32 edgIndex) const
|
||||
{
|
||||
const PxU32* adjucent = data->getAdjacencies();
|
||||
return adjucent[triIndex * 3 + edgIndex];
|
||||
}
|
||||
};
|
||||
|
||||
struct HeightFieldTriangles
|
||||
{
|
||||
const HeightFieldUtil& hfUtil;
|
||||
|
||||
HeightFieldTriangles(const HeightFieldUtil& _hfUtil)
|
||||
:
|
||||
hfUtil(_hfUtil)
|
||||
{}
|
||||
|
||||
void getVertexIndices(PxU32 triIndex, PxU32& i0, PxU32& i1, PxU32& i2) const
|
||||
{
|
||||
hfUtil.mHeightField->getTriangleVertexIndices(triIndex, i0, i1, i2);
|
||||
}
|
||||
|
||||
PxVec3 getVertex(PxU32 vertIndex) const
|
||||
{
|
||||
PxVec3 v = hfUtil.mHeightField->getVertex(vertIndex);
|
||||
PxVec3 s(hfUtil.mHfGeom->rowScale, hfUtil.mHfGeom->heightScale, hfUtil.mHfGeom->columnScale);
|
||||
return PxVec3(v.x * s.x, v.y * s.y, v.z * s.z);
|
||||
}
|
||||
|
||||
bool hasAdjacency() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
PxU32 getAdjacentTriIndex(PxU32 triIndex, PxU32 edgIndex) const
|
||||
{
|
||||
PxU32 adjucent[3];
|
||||
hfUtil.mHeightField->getTriangleAdjacencyIndices(triIndex, 0, 0, 0, adjucent[0], adjucent[1], adjucent[2]);
|
||||
return adjucent[edgIndex];
|
||||
}
|
||||
};
|
||||
|
||||
PxVec3 computeBarycentric(const PxVec3& a, const PxVec3& b, const PxVec3& c, const PxVec3& p)
|
||||
{
|
||||
PxVec4 bary;
|
||||
PxComputeBarycentric(a, b, c, p, bary);
|
||||
//PxReal u = bary.x, v = bary.y, w = bary.z;
|
||||
//PX_ASSERT((a * u + b * v + c * w - p).magnitude() < 1e-3f); // VR: find out why this asserts sometimes
|
||||
return bary.getXYZ();
|
||||
}
|
||||
|
||||
template <typename TriangleSource>
|
||||
bool validateContact(const PxVec3& normal, const PxVec3& pointB, PxU32 triIndex, const TriangleSource& tris)
|
||||
{
|
||||
const PxReal eps = 1e-5f;
|
||||
|
||||
PxU32 i0, i1, i2;
|
||||
tris.getVertexIndices(triIndex, i0, i1, i2);
|
||||
|
||||
const PxVec3 v0 = tris.getVertex(i0),
|
||||
v1 = tris.getVertex(i1),
|
||||
v2 = tris.getVertex(i2);
|
||||
|
||||
const PxVec3 tn = (v1 - v0).cross(v2 - v0).getNormalized();
|
||||
// close enough to a face contact
|
||||
if (tn.dot(normal) > 0.99f)
|
||||
// better to accept
|
||||
return true;
|
||||
|
||||
const PxVec3 bc = computeBarycentric(v0, v1, v2, pointB);
|
||||
|
||||
// face contact
|
||||
if (bc.x > eps && bc.x < 1.0f - eps &&
|
||||
bc.y > eps && bc.y < 1.0f - eps &&
|
||||
bc.z > eps && bc.z < 1.0f - eps)
|
||||
// always accept
|
||||
return true;
|
||||
|
||||
// vertex contact
|
||||
if (bc.x > 1.0f - eps ||
|
||||
bc.y > 1.0f - eps ||
|
||||
bc.z > 1.0f - eps)
|
||||
{
|
||||
PxU32 vrtIndex = 0xffffffff;
|
||||
if (tris.hasAdjacency())
|
||||
{
|
||||
if (bc.x > 1.0f - eps)
|
||||
vrtIndex = 0;
|
||||
else if (bc.y > 1.0f - eps)
|
||||
vrtIndex = 1;
|
||||
else if (bc.z > 1.0f - eps)
|
||||
vrtIndex = 2;
|
||||
}
|
||||
|
||||
if (vrtIndex != 0xffffffff)
|
||||
{
|
||||
PxU32 ai[] = { i0, i1, i2 };
|
||||
PxU32 ai0 = ai[vrtIndex];
|
||||
PxU32 adjIndex = tris.getAdjacentTriIndex(triIndex, (vrtIndex + 2) % 3);
|
||||
while (adjIndex != triIndex && adjIndex != 0xffffffff)
|
||||
{
|
||||
// walking through the adjucent triangles surrounding the vertex and checking
|
||||
// if any other end of the edges sharing the vertex projects onto the contact
|
||||
// normal higher than the vertex itself.it'd meand that the contact normal is
|
||||
// out of the vertex's voronoi region.
|
||||
PxU32 bi[3]; tris.getVertexIndices(adjIndex, bi[0], bi[1], bi[2]);
|
||||
for (PxU32 i = 0; i < 3; ++i)
|
||||
{
|
||||
PxU32 bi0 = bi[i], bi1 = bi[(i + 1) % 3], bi2 = bi[(i + 2) % 3];
|
||||
if (bi1 == ai0)
|
||||
{
|
||||
const PxVec3 bv0 = tris.getVertex(bi0),
|
||||
bv1 = tris.getVertex(bi1),
|
||||
bv2 = tris.getVertex(bi2);
|
||||
const PxReal bd10 = normal.dot((bv0 - bv1).getNormalized()),
|
||||
bd12 = normal.dot((bv2 - bv1).getNormalized());
|
||||
|
||||
if (bd10 > eps || bd12 > eps)
|
||||
// the vertex is hidden by one of the adjacent
|
||||
// edges we can't collide with this vertex
|
||||
return false;
|
||||
|
||||
// next triangle to check
|
||||
adjIndex = tris.getAdjacentTriIndex(adjIndex, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// edge contact
|
||||
PxU32 edgIndex = 0xffffffff;
|
||||
if (tris.hasAdjacency())
|
||||
{
|
||||
if (bc.x < eps)
|
||||
edgIndex = 1;
|
||||
else if (bc.y < eps)
|
||||
edgIndex = 2;
|
||||
else if (bc.z < eps)
|
||||
edgIndex = 0;
|
||||
}
|
||||
|
||||
if (edgIndex != 0xffffffff)
|
||||
{
|
||||
PxU32 ai[] = { i0, i1, i2 };
|
||||
PxU32 ai0 = ai[edgIndex], ai1 = ai[(edgIndex + 1) % 3];
|
||||
PxU32 adjIndex = tris.getAdjacentTriIndex(triIndex, edgIndex);
|
||||
if (adjIndex != 0xffffffff)
|
||||
{
|
||||
// testing if the adjacent triangle's vertex opposite to this edge
|
||||
// projects onto the contact normal higher than the edge itself. it'd
|
||||
// mean that the normal is out of the edge's voronoi region.
|
||||
PxU32 bi[3]; tris.getVertexIndices(adjIndex, bi[0], bi[1], bi[2]);
|
||||
for (PxU32 i = 0; i < 3; ++i)
|
||||
{
|
||||
PxU32 bi0 = bi[i], bi1 = bi[(i + 1) % 3], bi2 = bi[(i + 2) % 3];
|
||||
if (bi0 == ai1 && bi1 == ai0)
|
||||
{
|
||||
const PxVec3 bv1 = tris.getVertex(bi1),
|
||||
bv2 = tris.getVertex(bi2);
|
||||
const PxReal bd12 = normal.dot((bv2 - bv1).getNormalized());
|
||||
|
||||
if (bd12 > eps)
|
||||
// the edge is hidden by the adjacent triangle
|
||||
// we can't collide with this edge
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool Gu::contactConvexCoreTrimesh(GU_CONTACT_METHOD_ARGS)
|
||||
{
|
||||
PX_UNUSED(cache);
|
||||
PX_UNUSED(renderOutput);
|
||||
|
||||
struct Callback : MeshHitCallback<PxGeomRaycastHit>
|
||||
{
|
||||
const Gu::ConvexShape& mConvex;
|
||||
const PxMeshScale& mScale;
|
||||
const TriangleMesh* mData;
|
||||
const PxReal mContactDist;
|
||||
const PxReal mTriMargin;
|
||||
const PxTransform& mTransform;
|
||||
Gu::Contact& mContact;
|
||||
|
||||
PxRenderOutput* mRenderOutput;
|
||||
|
||||
Callback(const Gu::ConvexShape& convex, const PxMeshScale& scale, const TriangleMesh* data, PxReal contactDist,
|
||||
PxReal triMargin, const PxTransform& transform, Gu::Contact& contact, PxRenderOutput* renderOutput)
|
||||
:
|
||||
MeshHitCallback<PxGeomRaycastHit>(CallbackMode::eMULTIPLE),
|
||||
mConvex(convex), mScale(scale), mData(data), mContactDist(contactDist),
|
||||
mTriMargin(triMargin), mTransform(transform), mContact(contact), mRenderOutput(renderOutput)
|
||||
{}
|
||||
|
||||
virtual PxAgain processHit(const PxGeomRaycastHit& hit, const PxVec3& v0, const PxVec3& v1, const PxVec3& v2,
|
||||
PxReal&, const PxU32*)
|
||||
{
|
||||
const PxVec3 verts[] = { v0, v1, v2 };
|
||||
|
||||
Gu::ConvexShape tri;
|
||||
tri.coreType = Gu::ConvexCore::Type::ePOINTS;
|
||||
tri.pose = PxTransform(PxIdentity);
|
||||
Gu::ConvexCore::PointsCore& core = *reinterpret_cast<Gu::ConvexCore::PointsCore*>(tri.coreData);
|
||||
core.points = verts;
|
||||
core.numPoints = 3;
|
||||
core.stride = sizeof(PxVec3);
|
||||
core.S = mScale.scale;
|
||||
core.R = mScale.rotation;
|
||||
tri.margin = mTriMargin;
|
||||
|
||||
const PxVec3 triNormal = (v1 - v0).cross(v2 - v0).getNormalized();
|
||||
|
||||
TriangleMeshTriangles triSource(mData, mScale);
|
||||
PxVec3 normal, points[Gu::MAX_CONVEX_CONTACTS];
|
||||
PxReal dists[Gu::MAX_CONVEX_CONTACTS];
|
||||
if (PxU32 count = Gu::generateContacts(mConvex, tri, mContactDist, triNormal, normal, points, dists))
|
||||
{
|
||||
const PxVec3 worldNormal = mTransform.rotate(normal);
|
||||
for (PxU32 i = 0; i < count; ++i)
|
||||
{
|
||||
PxVec3 pointB = points[i] - normal * dists[i];
|
||||
if (validateContact(normal, pointB, hit.faceIndex, triSource))
|
||||
{
|
||||
const PxVec3 worldPoint = mTransform.transform(points[i]);
|
||||
mContact.addPoint(worldPoint, worldNormal, dists[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
const PxConvexCoreGeometry& shapeConvex = checkedCast<PxConvexCoreGeometry>(shape0);
|
||||
const PxTriangleMeshGeometry& shapeMesh = checkedCast<PxTriangleMeshGeometry>(shape1);
|
||||
const TriangleMesh* meshData = _getMeshData(shapeMesh);
|
||||
|
||||
const PxTransform transform0in1 = transform1.transformInv(transform0);
|
||||
const PxBounds3 bounds = Gu::computeBounds(shapeConvex, PxTransform(PxIdentity));
|
||||
|
||||
Box queryBox;
|
||||
queryBox.extents = bounds.getExtents() + PxVec3(params.mContactDistance);
|
||||
queryBox.center = transform0in1.transform(bounds.getCenter());
|
||||
queryBox.rot = PxMat33(transform0in1.q);
|
||||
|
||||
PxReal triMargin = queryBox.extents.minElement() * 0.0001f;
|
||||
|
||||
const FastVertex2ShapeScaling meshScaling(shapeMesh.scale);
|
||||
meshScaling.transformQueryBounds(queryBox.center, queryBox.extents, queryBox.rot);
|
||||
|
||||
Gu::Contact contact;
|
||||
Gu::ConvexShape convex; Gu::makeConvexShape(shapeConvex, transform0in1, convex);
|
||||
Callback callback(convex, shapeMesh.scale, meshData, params.mContactDistance, triMargin, transform1, contact, renderOutput);
|
||||
|
||||
Midphase::intersectOBB(meshData, queryBox, callback, false);
|
||||
|
||||
for (PxU32 i = 0; i < contact.numPatches(); ++i)
|
||||
for (PxU32 j = 0; j < contact.numPatchPoints(i); ++j)
|
||||
contactBuffer.contact(contact.patchPoint(i, j).p, contact.patchNormal(i), contact.patchPoint(i, j).d);
|
||||
|
||||
return contactBuffer.count > 0;
|
||||
}
|
||||
|
||||
bool Gu::contactConvexCoreHeightfield(GU_CONTACT_METHOD_ARGS)
|
||||
{
|
||||
PX_UNUSED(cache);
|
||||
PX_UNUSED(renderOutput);
|
||||
|
||||
struct Callback : Gu::OverlapReport
|
||||
{
|
||||
const Gu::ConvexShape& mConvex;
|
||||
const HeightFieldUtil& mHfUtil;
|
||||
const PxReal mContactDist;
|
||||
const PxTransform& mTransform;
|
||||
Gu::Contact& mContact;
|
||||
|
||||
Callback(const Gu::ConvexShape& convex, const HeightFieldUtil& hfUtil, const PxReal contactDist,
|
||||
const PxTransform& transform, Gu::Contact& contact)
|
||||
:
|
||||
mConvex(convex), mHfUtil(hfUtil), mContactDist(contactDist), mTransform(transform), mContact(contact)
|
||||
{}
|
||||
|
||||
virtual bool reportTouchedTris(PxU32 numTris, const PxU32* triInds)
|
||||
{
|
||||
HeightFieldTriangles triSource(mHfUtil);
|
||||
|
||||
for (PxU32 t = 0; t < numTris; ++t)
|
||||
{
|
||||
PxU32 triIndex = triInds[t];
|
||||
|
||||
PxU32 vertInds[3];
|
||||
triSource.getVertexIndices(triIndex, vertInds[0], vertInds[1], vertInds[2]);
|
||||
PxVec3 verts[] = { triSource.getVertex(vertInds[0]),
|
||||
triSource.getVertex(vertInds[1]),
|
||||
triSource.getVertex(vertInds[2]) };
|
||||
|
||||
Gu::ConvexShape tri;
|
||||
tri.coreType = Gu::ConvexCore::Type::ePOINTS;
|
||||
tri.pose = PxTransform(PxIdentity);
|
||||
Gu::ConvexCore::PointsCore& core = *reinterpret_cast<Gu::ConvexCore::PointsCore*>(tri.coreData);
|
||||
core.points = verts;
|
||||
core.numPoints = 3;
|
||||
core.stride = sizeof(PxVec3);
|
||||
core.S = PxVec3(1);
|
||||
core.R = PxQuat(PxIdentity);
|
||||
tri.margin = 0.0f;
|
||||
|
||||
PxVec3 normal, points[Gu::MAX_CONVEX_CONTACTS];
|
||||
PxReal dists[Gu::MAX_CONVEX_CONTACTS];
|
||||
if (PxU32 count = Gu::generateContacts(mConvex, tri, mContactDist, normal, points, dists))
|
||||
{
|
||||
const PxVec3 worldNormal = mTransform.rotate(normal);
|
||||
for (PxU32 i = 0; i < count; ++i)
|
||||
{
|
||||
// VR: disabled for now - find out why it skips the tris it shouldn't
|
||||
//PxVec3 pointB = points[i] - normal * (dists[i] * 0.5f);
|
||||
//if (validateContact(normal, pointB, triIndex, triSource))
|
||||
{
|
||||
const PxVec3 worldPoint = mTransform.transform(points[i]);
|
||||
mContact.addPoint(worldPoint, worldNormal, dists[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
const PxConvexCoreGeometry& shapeConvex = checkedCast<PxConvexCoreGeometry>(shape0);
|
||||
const PxHeightFieldGeometry& shapeHeightfield = checkedCast<PxHeightFieldGeometry>(shape1);
|
||||
|
||||
const HeightFieldUtil hfUtil(shapeHeightfield);
|
||||
|
||||
const PxTransform transform0in1 = transform1.transformInv(transform0);
|
||||
PxBounds3 bounds = Gu::computeBounds(shapeConvex, PxTransform(PxIdentity));
|
||||
bounds.fattenFast(params.mContactDistance);
|
||||
|
||||
Gu::Contact contact;
|
||||
Gu::ConvexShape convex; Gu::makeConvexShape(shapeConvex, transform0in1, convex);
|
||||
Callback callback(convex, hfUtil, params.mContactDistance, transform1, contact);
|
||||
|
||||
hfUtil.overlapAABBTriangles0to1(transform0in1, bounds, callback);
|
||||
|
||||
for (PxU32 i = 0; i < contact.numPatches(); ++i)
|
||||
for (PxU32 j = 0; j < contact.numPatchPoints(i); ++j)
|
||||
contactBuffer.contact(contact.patchPoint(i, j).p, contact.patchNormal(i), contact.patchPoint(i, j).d);
|
||||
|
||||
return contactBuffer.count > 0;
|
||||
}
|
||||
1465
engine/third_party/physx/source/geomutils/src/contact/GuContactConvexMesh.cpp
vendored
Normal file
1465
engine/third_party/physx/source/geomutils/src/contact/GuContactConvexMesh.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
56
engine/third_party/physx/source/geomutils/src/contact/GuContactCustomGeometry.cpp
vendored
Normal file
56
engine/third_party/physx/source/geomutils/src/contact/GuContactCustomGeometry.cpp
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
// 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 "GuContactMethodImpl.h"
|
||||
|
||||
using namespace physx;
|
||||
|
||||
bool Gu::contactCustomGeometryGeometry(GU_CONTACT_METHOD_ARGS)
|
||||
{
|
||||
PX_UNUSED(renderOutput);
|
||||
PX_UNUSED(cache);
|
||||
|
||||
const PxCustomGeometry& customGeom = checkedCast<PxCustomGeometry>(shape0);
|
||||
const PxGeometry& otherGeom = shape1;
|
||||
|
||||
customGeom.callbacks->generateContacts(customGeom, otherGeom, transform0, transform1,
|
||||
params.mContactDistance, params.mMeshContactMargin, params.mToleranceLength,
|
||||
contactBuffer);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Gu::contactGeometryCustomGeometry(GU_CONTACT_METHOD_ARGS)
|
||||
{
|
||||
bool res = contactCustomGeometryGeometry(shape1, shape0, transform1, transform0, params, cache, contactBuffer, renderOutput);
|
||||
|
||||
for (PxU32 i = 0; i < contactBuffer.count; ++i)
|
||||
contactBuffer.contacts[i].normal = -contactBuffer.contacts[i].normal;
|
||||
|
||||
return res;
|
||||
}
|
||||
285
engine/third_party/physx/source/geomutils/src/contact/GuContactMeshMesh.cpp
vendored
Normal file
285
engine/third_party/physx/source/geomutils/src/contact/GuContactMeshMesh.cpp
vendored
Normal file
@@ -0,0 +1,285 @@
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
|
||||
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
|
||||
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
|
||||
|
||||
#include "foundation/PxArray.h"
|
||||
#include "foundation/PxAssert.h"
|
||||
#include "foundation/PxBounds3.h"
|
||||
#include "foundation/PxMat33.h"
|
||||
#include "foundation/PxMath.h"
|
||||
#include "foundation/PxPreprocessor.h"
|
||||
#include "foundation/PxSimpleTypes.h"
|
||||
#include "foundation/PxTransform.h"
|
||||
#include "foundation/PxVec3.h"
|
||||
#include "geometry/PxBoxGeometry.h"
|
||||
#include "geometry/PxMeshQuery.h"
|
||||
#include "geometry/PxTriangleMesh.h"
|
||||
#include "GuCollisionSDF.h"
|
||||
#include "GuTriangleMesh.h"
|
||||
#include "GuContactMeshMesh.h"
|
||||
#include "GuContactMethodImpl.h"
|
||||
#include "GuContactReduction.h"
|
||||
#include "GuMidphaseInterface.h"
|
||||
#include "GuTriangle.h"
|
||||
#include "GuTriangleRefinement.h"
|
||||
|
||||
using namespace physx;
|
||||
using namespace Gu;
|
||||
|
||||
using ContactReduction = SDFContactReduction<5, 10000, 32>;
|
||||
|
||||
const PxI32 maxRefinementLevel = 8;
|
||||
|
||||
struct TransformedTriangle
|
||||
{
|
||||
PxVec3 v0, v1, v2;
|
||||
PxI16 refinementLevel;
|
||||
PxU8 boundary; // information about boundaries; currently unused
|
||||
};
|
||||
|
||||
PX_FORCE_INLINE bool needsRefinement(const PxReal triRefThreshold, const TransformedTriangle& tri)
|
||||
{
|
||||
return (
|
||||
(tri.v0-tri.v1).magnitudeSquared() > triRefThreshold ||
|
||||
(tri.v1-tri.v2).magnitudeSquared() > triRefThreshold ||
|
||||
(tri.v2-tri.v0).magnitudeSquared() > triRefThreshold
|
||||
) && tri.refinementLevel < maxRefinementLevel;
|
||||
}
|
||||
|
||||
// Find contacts between an SDF and a triangle mesh and return the number of contacts generated
|
||||
PxU32 sdfMeshCollision (
|
||||
const PxTransform32& PX_RESTRICT tfSdf, const PxTriangleMeshGeometry& PX_RESTRICT sdfGeom,
|
||||
const PxTransform32& PX_RESTRICT tfMesh, const PxTriangleMeshGeometry& PX_RESTRICT meshGeom,
|
||||
ContactReduction& contactReducer, const PxReal totalContactDistance, bool flipContactNormals
|
||||
)
|
||||
{
|
||||
float min_separation = PX_MAX_REAL;
|
||||
const TriangleMesh& mesh = static_cast<const TriangleMesh&>(*meshGeom.triangleMesh);
|
||||
const TriangleMesh& sdfMesh = static_cast<const TriangleMesh&>(*sdfGeom.triangleMesh);
|
||||
|
||||
const PxMeshScale& sdfScale = sdfGeom.scale, & meshScale = meshGeom.scale;
|
||||
|
||||
const CollisionSDF& PX_RESTRICT sdf(sdfMesh.mSdfData);
|
||||
|
||||
const PxTransform meshToSdf = tfSdf.transformInv(tfMesh);
|
||||
|
||||
const PxMat33 sdfScaleMat = sdfScale.toMat33();
|
||||
PxBounds3 sdfBoundsAtWorldScale(sdfScaleMat.transform(sdf.mSdfBoxLower), sdfScaleMat.transform(sdf.mSdfBoxUpper));
|
||||
sdfBoundsAtWorldScale.fattenSafe(totalContactDistance);
|
||||
const PxTransform poseT(sdfBoundsAtWorldScale.getCenter());
|
||||
const PxBoxGeometry boxGeom(sdfBoundsAtWorldScale.getExtents());
|
||||
|
||||
const PxReal sdfDiagSq = (sdf.mSdfBoxUpper - sdf.mSdfBoxLower).magnitudeSquared();
|
||||
const PxReal div = 1.0f / 256.0f;
|
||||
const PxReal triRefThreshold = sdfDiagSq * div;
|
||||
const bool singleSdf = meshGeom.triangleMesh->getSDF() == NULL; // triangle subdivision if single SDF
|
||||
|
||||
const PxU32 MAX_INTERSECTIONS = 1024 * 32;
|
||||
PxArray<PxU32> overlappingTriangles;
|
||||
overlappingTriangles.resize(MAX_INTERSECTIONS); //TODO: Not ideal, dynamic allocation for every function call
|
||||
//PxU32 overlappingTriangles[MAX_INTERSECTIONS]; //TODO: Is this too much memory to allocate on the stack?
|
||||
|
||||
bool overflow = false;
|
||||
const PxU32 overlapCount = PxMeshQuery::findOverlapTriangleMesh(boxGeom, poseT, meshGeom, meshToSdf, overlappingTriangles.begin(), MAX_INTERSECTIONS, 0, overflow);
|
||||
PX_ASSERT(!overflow);
|
||||
|
||||
// we use cullScale to account for SDF scaling whenever distances are
|
||||
const PxReal cullScale = totalContactDistance / sdfScale.scale.minElement();
|
||||
|
||||
const PxVec3* PX_RESTRICT vertices = mesh.getVertices();
|
||||
const void* PX_RESTRICT tris = mesh.getTriangles();
|
||||
const bool has16BitIndices = mesh.getTriangleMeshFlags() & physx::PxTriangleMeshFlag::e16_BIT_INDICES;
|
||||
const PxU32 nbTris = overlapCount; // mesh.getNbTriangles();
|
||||
|
||||
|
||||
/* Transforms fused; unoptimized version:
|
||||
v0 = shape2Vertex(
|
||||
meshToSdf.transform(vertex2Shape(vertices[triIndices.mRef[0]], meshScale.scale, meshScale.rotation)),
|
||||
sdfScale.scale, sdfScale.rotation); */
|
||||
const PxMat33 sdfScaleIMat = sdfScale.getInverse().toMat33();
|
||||
const PxMat33 fusedRotScale = sdfScaleIMat * PxMat33Padded(meshToSdf.q) * meshScale.toMat33();
|
||||
const PxVec3 fusedTranslate = sdfScaleIMat * meshToSdf.p;
|
||||
|
||||
const PxMat33Padded tfSdfRotationMatrix(tfSdf.q);
|
||||
const PxMat33 pointToWorldR = tfSdfRotationMatrix * sdfScale.toMat33();
|
||||
const PxMat33 normalToWorld = tfSdfRotationMatrix * sdfScaleIMat;
|
||||
|
||||
|
||||
const PxU32 COLLISION_BUF_SIZE = 512;
|
||||
const PxU32 sudivBufSize = singleSdf ? maxRefinementLevel * 3 : 0; // Overhead for subdivision (pop one, push four)
|
||||
PX_ASSERT(sudivBufSize < COLLISION_BUF_SIZE/4); // ensure reasonable buffer size
|
||||
|
||||
|
||||
TransformedTriangle goodTriangles[COLLISION_BUF_SIZE];
|
||||
|
||||
PxU32 nbContacts = 0;
|
||||
for (PxU32 i = 0, allTrisProcessed = 0; !allTrisProcessed;)
|
||||
{
|
||||
// try to find `COLLISION_BUF_SIZE` triangles that cannot be culled immediately
|
||||
|
||||
PxU32 nbGoodTris = 0;
|
||||
// Every triangle that overlaps with the sdf's axis aligned bounding box is checked against the sdf to see if an intersection
|
||||
// can be ruled out. If an intersection can be ruled out, the triangle is not further processed. Since SDF data is accessed,
|
||||
// the check is more accurate (but still very fast) than a simple bounding box overlap test.
|
||||
// Performance measurements confirm that this pre-pruning loop actually increases performance significantly on some scenes
|
||||
for ( ; nbGoodTris < COLLISION_BUF_SIZE - sudivBufSize; ++i)
|
||||
{
|
||||
if (i == nbTris)
|
||||
{
|
||||
allTrisProcessed = true;
|
||||
break;
|
||||
}
|
||||
const PxU32 triIdx = overlappingTriangles[i];
|
||||
TransformedTriangle niceTri;
|
||||
|
||||
const Gu::IndexedTriangle32 triIndices = has16BitIndices ?
|
||||
getTriangleVertexIndices<PxU16>(tris, triIdx) :
|
||||
getTriangleVertexIndices<PxU32>(tris, triIdx);
|
||||
|
||||
niceTri.v0 = fusedTranslate + fusedRotScale * vertices[triIndices.mRef[0]];
|
||||
niceTri.v1 = fusedTranslate + fusedRotScale * vertices[triIndices.mRef[1]];
|
||||
niceTri.v2 = fusedTranslate + fusedRotScale * vertices[triIndices.mRef[2]];
|
||||
|
||||
if (singleSdf)
|
||||
niceTri.refinementLevel = 0;
|
||||
|
||||
// - triangles that are not culled are added to goodTriangles
|
||||
if (sdfTriangleSphericalCull(sdf, niceTri.v0, niceTri.v1, niceTri.v2, cullScale))
|
||||
goodTriangles[nbGoodTris++] = niceTri;
|
||||
}
|
||||
|
||||
// in promising triangles
|
||||
// - triangles are popped from goodTriangles and a contact generated or,
|
||||
// if subdivision is indicated, their children are pushed on top
|
||||
for (PxU32 goodTriEnd = nbGoodTris; goodTriEnd > 0;)
|
||||
{
|
||||
const TransformedTriangle tri = goodTriangles[--goodTriEnd]; // pop
|
||||
// decide on need for subdivision
|
||||
if (singleSdf && needsRefinement(triRefThreshold, tri))
|
||||
{
|
||||
for (int childIdx = 0; childIdx < 4; ++childIdx)
|
||||
{
|
||||
TransformedTriangle child = tri;
|
||||
Gu::getSubTriangle4(childIdx, child.v0, child.v1, child.v2);
|
||||
++child.refinementLevel;
|
||||
if (sdfTriangleSphericalCull(sdf, child.v0, child.v1, child.v2, cullScale))
|
||||
goodTriangles[goodTriEnd++] = child;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// generate contacts
|
||||
PxVec3 sdfPoint, contactDir;
|
||||
PxReal separation = sdfTriangleCollision(sdf, tri.v0, tri.v1, tri.v2, sdfPoint, contactDir, cullScale);
|
||||
min_separation = PxMin(min_separation, separation);
|
||||
|
||||
if (separation < cullScale)
|
||||
{
|
||||
const PxVec3 worldPoint = tfSdf.p + pointToWorldR * sdfPoint;
|
||||
contactDir = normalToWorld * contactDir;
|
||||
|
||||
const PxReal magSq = contactDir.magnitudeSquared();
|
||||
|
||||
// TODO(CA): handle case where only one mesh has an SDF and update heuristic once this is done
|
||||
if (magSq < 1e-6f) // ignore contacts with a bad/missing normal
|
||||
continue;
|
||||
const PxReal mag = PxRecipSqrt(magSq);
|
||||
|
||||
if (singleSdf && tri.refinementLevel)
|
||||
{
|
||||
const PxVec3 n = (tri.v1 - tri.v0).getNormalized().cross(tri.v2 - tri.v0).getNormalized();
|
||||
const PxVec3 sdfBoxCenter = 0.5f * (sdf.mSdfBoxUpper + sdf.mSdfBoxLower);
|
||||
const PxReal triangleNormalSign = -PxSign((sdfBoxCenter - tri.v0).dot(n));
|
||||
contactDir = normalToWorld * triangleNormalSign * n;
|
||||
contactDir.normalize();
|
||||
contactDir /= mag ;
|
||||
}
|
||||
separation *= mag;
|
||||
contactDir *= mag;
|
||||
const TinyContact contact{flipContactNormals ? -contactDir : contactDir, separation, worldPoint};
|
||||
contactReducer.addContact(contact);
|
||||
++nbContacts;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nbContacts;
|
||||
};
|
||||
|
||||
|
||||
bool Gu::contactMeshMesh(GU_CONTACT_METHOD_ARGS)
|
||||
{
|
||||
PX_UNUSED(renderOutput);
|
||||
PX_UNUSED(cache);
|
||||
|
||||
// Get meshes
|
||||
const PxTriangleMeshGeometry& geom0 = checkedCast<PxTriangleMeshGeometry>(shape0),
|
||||
& geom1 = checkedCast<PxTriangleMeshGeometry>(shape1);
|
||||
PX_ASSERT(geom0.triangleMesh != NULL && geom1.triangleMesh != NULL);
|
||||
|
||||
|
||||
PxU32 nbContacts = 0;
|
||||
const PxReal contactDistance = params.mContactDistance; // computed in `checkContactsMustBeGenerated`
|
||||
|
||||
const bool mesh0PreferProj = static_cast<const TriangleMesh&>(*geom0.triangleMesh).getPreferSDFProjection(),
|
||||
mesh1PreferProj = static_cast<const TriangleMesh&>(*geom1.triangleMesh).getPreferSDFProjection();
|
||||
const PxU32 n0 = geom0.triangleMesh->getNbVertices(), n1 = geom1.triangleMesh->getNbVertices();
|
||||
|
||||
// sdf0first: in first pass, treat mesh0 as an sdf and mesh1 as a mesh
|
||||
const bool sdf0first = ((!mesh0PreferProj && mesh1PreferProj) || (mesh0PreferProj == mesh1PreferProj && n1 < n0));
|
||||
|
||||
ContactReduction contactReducer;
|
||||
|
||||
const bool geom0HasSdf = geom0.triangleMesh->getSDF() != NULL,
|
||||
geom1HasSdf = geom1.triangleMesh->getSDF() != NULL;
|
||||
|
||||
PX_ASSERT(geom0HasSdf || geom1HasSdf); // require at least one SDF
|
||||
if (!geom0HasSdf && !geom1HasSdf)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(geom0HasSdf && geom1HasSdf))
|
||||
{
|
||||
if (geom0HasSdf)
|
||||
nbContacts += sdfMeshCollision(transform0, geom0, transform1, geom1, contactReducer, contactDistance, true);
|
||||
else
|
||||
nbContacts += sdfMeshCollision(transform1, geom1, transform0, geom0, contactReducer, contactDistance, false);
|
||||
}
|
||||
else if (sdf0first)
|
||||
{
|
||||
nbContacts += sdfMeshCollision(transform0, geom0, transform1, geom1, contactReducer, contactDistance, true);
|
||||
nbContacts += sdfMeshCollision(transform1, geom1, transform0, geom0, contactReducer, contactDistance, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
nbContacts += sdfMeshCollision(transform1, geom1, transform0, geom0, contactReducer, contactDistance, false);
|
||||
nbContacts += sdfMeshCollision(transform0, geom0, transform1, geom1, contactReducer, contactDistance, true);
|
||||
}
|
||||
|
||||
contactReducer.flushToContactBuffer(contactBuffer);
|
||||
return nbContacts != 0;
|
||||
}
|
||||
196
engine/third_party/physx/source/geomutils/src/contact/GuContactMeshMesh.h
vendored
Normal file
196
engine/third_party/physx/source/geomutils/src/contact/GuContactMeshMesh.h
vendored
Normal file
@@ -0,0 +1,196 @@
|
||||
// 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.
|
||||
|
||||
#ifndef GU_MESH_MESH_COLLISION_H
|
||||
#define GU_MESH_MESH_COLLISION_H
|
||||
|
||||
#include "foundation/PxAssert.h"
|
||||
#include "foundation/PxMath.h"
|
||||
#include "foundation/PxPreprocessor.h"
|
||||
#include "foundation/PxSimpleTypes.h"
|
||||
#include "foundation/PxVec3.h"
|
||||
#include "GuCollisionSDF.h"
|
||||
#include "GuDistancePointTriangle.h"
|
||||
#include "GuTriangle.h"
|
||||
|
||||
namespace physx
|
||||
{
|
||||
namespace Gu
|
||||
{
|
||||
|
||||
template <typename T>
|
||||
int argmin(const T& v0, const T& v1)
|
||||
{
|
||||
if (v0 < v1)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
int argmin(const T& v0, const T& v1, const T& v2, const T& v3)
|
||||
{
|
||||
const int ma = argmin(v0, v1), mb = argmin(v2, v3);
|
||||
if (!argmin((!ma ? v0 : v1),(!mb ? v2: v3)))
|
||||
return ma;
|
||||
return mb+2;
|
||||
}
|
||||
|
||||
// Based on one SDF evaluation at the centroid, can the circumsphere of the triangle
|
||||
// `v0`, `v1`, `v2` get closer to the surface given by `sdf` than `cutoffDistance`?
|
||||
static PX_INLINE bool sdfTriangleSphericalCull(
|
||||
const CollisionSDF& PX_RESTRICT sdf,
|
||||
const PxVec3& PX_RESTRICT v0, const PxVec3& PX_RESTRICT v1, const PxVec3& PX_RESTRICT v2,
|
||||
PxReal cutoffDistance)
|
||||
{
|
||||
const PxReal third = 1.0f / 3.0f;
|
||||
const PxVec3 centroid = (v0 + v1 + v2) * third;
|
||||
|
||||
const PxReal sphereRadiusSq = PxMax(
|
||||
(v0 - centroid).magnitudeSquared(),
|
||||
PxMax((v1 - centroid).magnitudeSquared(), (v2 - centroid).magnitudeSquared()));
|
||||
|
||||
const PxVec3 boxPos = sdf.clampToBox(centroid);
|
||||
const PxReal centroidToBoxSq = (centroid - boxPos).magnitudeSquared();
|
||||
// TODO(CA): consider minimum of SDF on box boundary, making this check tighter
|
||||
if (PxSqrt(centroidToBoxSq) > PxSqrt(sphereRadiusSq) + cutoffDistance)
|
||||
return false; //Early out without touching SDF data
|
||||
|
||||
const PxReal centroidSdf = sdf.dist(centroid);
|
||||
|
||||
return centroidSdf < PxSqrt(sphereRadiusSq) + cutoffDistance;
|
||||
}
|
||||
|
||||
|
||||
// Find maximum separation of an sdf and a triangle and find the contact point and normal separation is below `cutoffDistance`
|
||||
// Return the separation, or`PX\_MAX\_F32` if it exceeds `cutoffDistance`
|
||||
template <PxU32 TMaxLineSearchIters = 0, PxU32 TMaxPGDIterations = 32, bool TFastGrad = true>
|
||||
PX_INLINE PxReal sdfTriangleCollision(
|
||||
const CollisionSDF& PX_RESTRICT sdf,
|
||||
const PxVec3& PX_RESTRICT v0, const PxVec3& PX_RESTRICT v1, const PxVec3& PX_RESTRICT v2,
|
||||
PxVec3& point, PxVec3& dir, PxReal cutoffDistance)
|
||||
{
|
||||
const PxReal third = 1.0f / 3.0f;
|
||||
const PxVec3 centroid = (v0 + v1 + v2) * third;
|
||||
|
||||
// barycentric coordinates, corresponding to v0, v1, v2
|
||||
PxVec3 c(0.f);
|
||||
|
||||
// choose starting iterate
|
||||
const int start = argmin(sdf.dist(v0), sdf.dist(v1), sdf.dist(v2), sdf.dist(centroid));
|
||||
switch (start)
|
||||
{
|
||||
case 0:
|
||||
c.x = 1.f; break;
|
||||
case 1:
|
||||
c.y = 1.f; break;
|
||||
case 2:
|
||||
c.z = 1.f; break;
|
||||
case 3:
|
||||
c = PxVec3(third); break;
|
||||
default:
|
||||
PX_ASSERT(false);
|
||||
}
|
||||
|
||||
PxReal stepSize = 0.25;
|
||||
// we could also compute the gradient's lipschitz constant when baking!
|
||||
PxVec3 dfdp; // gradient w.r.t. p
|
||||
const PxReal toleranceSq = 1e-10f;
|
||||
|
||||
for (PxU32 i = 0; i < TMaxPGDIterations; ++i)
|
||||
{
|
||||
const PxVec3 p = c.x * v0 + c.y * v1 + c.z * v2;
|
||||
PxReal dist_old = 0;
|
||||
if (TFastGrad)
|
||||
dist_old = sdf.dist(p, &dfdp);
|
||||
else
|
||||
dfdp = sdf.grad(p);
|
||||
const PxReal dfdpMagSq = dfdp.magnitudeSquared();
|
||||
|
||||
if (dfdpMagSq == 0.0f)
|
||||
{
|
||||
// TODO(CA): consider expanding this into a stopping criterion
|
||||
// At a critical point. Take a small step away into an arbitrary direction
|
||||
dfdp = PxVec3(0.5718465865353257f, 0.7055450997557186f, 0.41856611625714474f);
|
||||
}
|
||||
else
|
||||
dfdp *= PxRecipSqrt(dfdpMagSq);
|
||||
|
||||
// Simple projected gradient descent
|
||||
const PxVec3 dfdc = PxVec3(dfdp.dot(v0-p), dfdp.dot(v1-p), dfdp.dot(v2-p));
|
||||
|
||||
const PxVec3 c_old = c;
|
||||
if (TMaxLineSearchIters) //Line Search is quite expensive since it increases the number of expensive calls to sdf.dist by approximately a factor of MAX_LINE_SEARCH_ITERATIONS
|
||||
{
|
||||
PxReal s = 1;
|
||||
if (!TFastGrad)
|
||||
dist_old = sdf.dist(p);
|
||||
c = closestPtPointBaryTriangle(c_old - s * dfdc);
|
||||
for (PxU32 ls_it = 0; ls_it < TMaxLineSearchIters; ++ls_it)
|
||||
{
|
||||
// restore barycentric coordinates
|
||||
const PxVec3 p_new = c.x * v0 + c.y * v1 + c.z * v2;
|
||||
if (sdf.dist(p_new) <= dist_old)
|
||||
{
|
||||
#if 0
|
||||
if (ls_it > 0)
|
||||
printf("%d: %d ls iterations\n", i, ls_it+1);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
s *= 0.5f;
|
||||
c = closestPtPointBaryTriangle(c_old - s * dfdc);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// take step and restore barycentric coordinates
|
||||
c = closestPtPointBaryTriangle(c - stepSize * dfdc);
|
||||
}
|
||||
// this detects a minimum found on the boundary
|
||||
if ((c - c_old).magnitudeSquared() < toleranceSq)
|
||||
break;
|
||||
stepSize *= 0.8f; // line search will get rid of this
|
||||
}
|
||||
|
||||
const PxVec3 p = c.x * v0 + c.y * v1 + c.z * v2;
|
||||
point = p;
|
||||
return sdf.distUsingGradient(p, dir, cutoffDistance);
|
||||
}
|
||||
|
||||
// Get the indices of the triangle at position `triIdx`.
|
||||
// `T` should be `PxU16` if the mesh has 16 bit indices, and `PxU32` otherwise
|
||||
template <typename T>
|
||||
PX_INLINE Gu::IndexedTriangle32 getTriangleVertexIndices(const void* triangles, PxU32 triIdx)
|
||||
{
|
||||
const T* trisCast = reinterpret_cast<const T*>(triangles);
|
||||
return {trisCast[triIdx*3], trisCast[triIdx*3+1], trisCast[triIdx*3+2]};
|
||||
}
|
||||
|
||||
} // namespace Gu
|
||||
} // namespace physx
|
||||
#endif
|
||||
208
engine/third_party/physx/source/geomutils/src/contact/GuContactMethodImpl.h
vendored
Normal file
208
engine/third_party/physx/source/geomutils/src/contact/GuContactMethodImpl.h
vendored
Normal file
@@ -0,0 +1,208 @@
|
||||
// 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.
|
||||
|
||||
#ifndef GU_CONTACTMETHODIMPL_H
|
||||
#define GU_CONTACTMETHODIMPL_H
|
||||
|
||||
#include "foundation/PxAssert.h"
|
||||
#include "common/PxPhysXCommonConfig.h"
|
||||
#include "collision/PxCollisionDefs.h"
|
||||
#include "GuGeometryChecks.h"
|
||||
|
||||
namespace physx
|
||||
{
|
||||
class PxGeometry;
|
||||
class PxRenderOutput;
|
||||
class PxContactBuffer;
|
||||
|
||||
namespace Gu
|
||||
{
|
||||
class PersistentContactManifold;
|
||||
class MultiplePersistentContactManifold;
|
||||
|
||||
struct NarrowPhaseParams
|
||||
{
|
||||
PX_FORCE_INLINE NarrowPhaseParams(PxReal contactDistance, PxReal meshContactMargin, PxReal toleranceLength) :
|
||||
mContactDistance(contactDistance),
|
||||
mMeshContactMargin(meshContactMargin),
|
||||
mToleranceLength(toleranceLength) {}
|
||||
|
||||
PxReal mContactDistance;
|
||||
const PxReal mMeshContactMargin; // PT: Margin used to generate mesh contacts. Temp & unclear, should be removed once GJK is default path.
|
||||
const PxReal mToleranceLength; // PT: copy of PxTolerancesScale::length
|
||||
};
|
||||
|
||||
enum ManifoldFlags
|
||||
{
|
||||
IS_MANIFOLD = (1<<0),
|
||||
IS_MULTI_MANIFOLD = (1<<1)
|
||||
};
|
||||
|
||||
struct Cache : public PxCache
|
||||
{
|
||||
Cache()
|
||||
{
|
||||
}
|
||||
|
||||
PX_FORCE_INLINE void setManifold(void* manifold)
|
||||
{
|
||||
PX_ASSERT((size_t(manifold) & 0xF) == 0);
|
||||
mCachedData = reinterpret_cast<PxU8*>(manifold);
|
||||
mManifoldFlags |= IS_MANIFOLD;
|
||||
}
|
||||
|
||||
PX_FORCE_INLINE void setMultiManifold(void* manifold)
|
||||
{
|
||||
PX_ASSERT((size_t(manifold) & 0xF) == 0);
|
||||
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);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
template<class Geom> PX_CUDA_CALLABLE PX_FORCE_INLINE const Geom& checkedCast(const PxGeometry& geom)
|
||||
{
|
||||
checkType<Geom>(geom);
|
||||
return static_cast<const Geom&>(geom);
|
||||
}
|
||||
|
||||
#define GU_CONTACT_METHOD_ARGS \
|
||||
const PxGeometry& shape0, \
|
||||
const PxGeometry& shape1, \
|
||||
const PxTransform32& transform0, \
|
||||
const PxTransform32& transform1, \
|
||||
const Gu::NarrowPhaseParams& params, \
|
||||
Gu::Cache& cache, \
|
||||
PxContactBuffer& contactBuffer, \
|
||||
PxRenderOutput* renderOutput
|
||||
|
||||
#define GU_CONTACT_METHOD_ARGS_UNUSED \
|
||||
const PxGeometry&, \
|
||||
const PxGeometry&, \
|
||||
const PxTransform32&, \
|
||||
const PxTransform32&, \
|
||||
const Gu::NarrowPhaseParams&, \
|
||||
Gu::Cache&, \
|
||||
PxContactBuffer&, \
|
||||
PxRenderOutput*
|
||||
|
||||
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 contactPlaneConvexCore(GU_CONTACT_METHOD_ARGS);
|
||||
PX_PHYSX_COMMON_API bool contactPlaneConvex(GU_CONTACT_METHOD_ARGS);
|
||||
PX_PHYSX_COMMON_API bool contactPlaneMesh(GU_CONTACT_METHOD_ARGS);
|
||||
PX_PHYSX_COMMON_API bool contactMeshMesh(GU_CONTACT_METHOD_ARGS);
|
||||
|
||||
PX_PHYSX_COMMON_API bool contactConvexCoreConvex(GU_CONTACT_METHOD_ARGS);
|
||||
PX_PHYSX_COMMON_API bool contactConvexCoreTrimesh(GU_CONTACT_METHOD_ARGS);
|
||||
PX_PHYSX_COMMON_API bool contactConvexCoreHeightfield(GU_CONTACT_METHOD_ARGS);
|
||||
|
||||
PX_PHYSX_COMMON_API bool contactCustomGeometryGeometry(GU_CONTACT_METHOD_ARGS);
|
||||
PX_PHYSX_COMMON_API bool contactGeometryCustomGeometry(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);
|
||||
|
||||
PX_PHYSX_COMMON_API bool pcmContactGeometryCustomGeometry(GU_CONTACT_METHOD_ARGS);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
122
engine/third_party/physx/source/geomutils/src/contact/GuContactPlaneBox.cpp
vendored
Normal file
122
engine/third_party/physx/source/geomutils/src/contact/GuContactPlaneBox.cpp
vendored
Normal file
@@ -0,0 +1,122 @@
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
|
||||
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
|
||||
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
|
||||
|
||||
#include "foundation/PxUnionCast.h"
|
||||
#include "geomutils/PxContactBuffer.h"
|
||||
#include "GuContactMethodImpl.h"
|
||||
#include "CmMatrix34.h"
|
||||
#include "foundation/PxUtilities.h"
|
||||
|
||||
using namespace physx;
|
||||
using namespace Cm;
|
||||
|
||||
bool Gu::contactPlaneBox(GU_CONTACT_METHOD_ARGS)
|
||||
{
|
||||
PX_UNUSED(renderOutput);
|
||||
PX_UNUSED(cache);
|
||||
PX_UNUSED(shape0);
|
||||
|
||||
// Get actual shape data
|
||||
//const PxPlaneGeometry& shapePlane = checkedCast<PxPlaneGeometry>(shape0);
|
||||
const PxBoxGeometry& shapeBox = checkedCast<PxBoxGeometry>(shape1);
|
||||
|
||||
const PxVec3 negPlaneNormal = -transform0.q.getBasisVector0();
|
||||
|
||||
//Make sure we have a normalized plane
|
||||
//PX_ASSERT(PxAbs(shape0.mNormal.magnitudeSquared() - 1.0f) < 0.000001f);
|
||||
|
||||
const Matrix34FromTransform boxMatrix(transform1);
|
||||
const Matrix34FromTransform 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;
|
||||
}
|
||||
74
engine/third_party/physx/source/geomutils/src/contact/GuContactPlaneCapsule.cpp
vendored
Normal file
74
engine/third_party/physx/source/geomutils/src/contact/GuContactPlaneCapsule.cpp
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
// 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 "GuContactMethodImpl.h"
|
||||
#include "GuInternal.h"
|
||||
#include "GuSegment.h"
|
||||
|
||||
using namespace physx;
|
||||
|
||||
bool Gu::contactPlaneCapsule(GU_CONTACT_METHOD_ARGS)
|
||||
{
|
||||
PX_UNUSED(renderOutput);
|
||||
PX_UNUSED(cache);
|
||||
PX_UNUSED(shape0);
|
||||
|
||||
// Get actual shape data
|
||||
//const PxPlaneGeometry& shapePlane = checkedCast<PxPlaneGeometry>(shape0);
|
||||
const PxCapsuleGeometry& shapeCapsule = checkedCast<PxCapsuleGeometry>(shape1);
|
||||
|
||||
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;
|
||||
}
|
||||
98
engine/third_party/physx/source/geomutils/src/contact/GuContactPlaneConvex.cpp
vendored
Normal file
98
engine/third_party/physx/source/geomutils/src/contact/GuContactPlaneConvex.cpp
vendored
Normal file
@@ -0,0 +1,98 @@
|
||||
// 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 "GuConvexMeshData.h"
|
||||
#include "GuContactMethodImpl.h"
|
||||
#include "GuConvexMesh.h"
|
||||
#include "CmScaling.h"
|
||||
#include "CmMatrix34.h"
|
||||
|
||||
using namespace physx;
|
||||
using namespace Cm;
|
||||
|
||||
bool Gu::contactPlaneConvex(GU_CONTACT_METHOD_ARGS)
|
||||
{
|
||||
PX_UNUSED(renderOutput);
|
||||
PX_UNUSED(cache);
|
||||
PX_UNUSED(shape0);
|
||||
|
||||
// Get actual shape data
|
||||
//const PxPlaneGeometry& shapePlane = checkedCast<PxPlaneGeometry>(shape0);
|
||||
const PxConvexMeshGeometry& shapeConvex = checkedCast<PxConvexMeshGeometry>(shape1);
|
||||
|
||||
const ConvexHullData* hullData = _getHullData(shapeConvex);
|
||||
const PxVec3* PX_RESTRICT hullVertices = hullData->getHullVertices();
|
||||
PxU32 numHullVertices = hullData->mNbHullVertices;
|
||||
// PxPrefetch128(hullVertices);
|
||||
|
||||
// Plane is implicitly <1,0,0> 0 in localspace
|
||||
const Matrix34FromTransform convexToPlane0 (transform0.transformInv(transform1));
|
||||
const PxMat33 convexToPlane_rot(convexToPlane0[0], convexToPlane0[1], convexToPlane0[2] );
|
||||
|
||||
bool idtScale = shapeConvex.scale.isIdentity();
|
||||
FastVertex2ShapeScaling convexScaling; // PT: TODO: remove default ctor
|
||||
if(!idtScale)
|
||||
convexScaling.init(shapeConvex.scale);
|
||||
|
||||
const PxMat34 convexToPlane(convexToPlane_rot * convexScaling.getVertex2ShapeSkew(), convexToPlane0[3]);
|
||||
|
||||
//convexToPlane = context.mVertex2ShapeSkew[1].getVertex2WorldSkew(convexToPlane);
|
||||
|
||||
const Matrix34FromTransform planeToW(transform0);
|
||||
|
||||
// This is rather brute-force
|
||||
|
||||
bool status = false;
|
||||
|
||||
const PxVec3 contactNormal = -planeToW.m.column0;
|
||||
|
||||
while(numHullVertices--)
|
||||
{
|
||||
const PxVec3& vertex = *hullVertices++;
|
||||
// if(numHullVertices)
|
||||
// PxPrefetch128(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;
|
||||
PxContactPoint* 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;
|
||||
}
|
||||
54
engine/third_party/physx/source/geomutils/src/contact/GuContactPlaneConvexCore.cpp
vendored
Normal file
54
engine/third_party/physx/source/geomutils/src/contact/GuContactPlaneConvexCore.cpp
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
// 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 "GuContactMethodImpl.h"
|
||||
#include "geometry/PxConvexCoreGeometry.h"
|
||||
#include "GuConvexGeometry.h"
|
||||
#include "GuConvexSupport.h"
|
||||
|
||||
using namespace physx;
|
||||
|
||||
bool Gu::contactPlaneConvexCore(GU_CONTACT_METHOD_ARGS)
|
||||
{
|
||||
PX_UNUSED(shape0);
|
||||
PX_UNUSED(renderOutput);
|
||||
PX_UNUSED(cache);
|
||||
|
||||
PxPlane plane0(transform0.p, transform0.q.getBasisVector0());
|
||||
Gu::ConvexShape convex1; Gu::makeConvexShape(shape1, transform1, convex1);
|
||||
PX_ASSERT(convex1.isValid());
|
||||
|
||||
PxVec3 normal, points[Gu::MAX_CONVEX_CONTACTS];
|
||||
PxReal dists[Gu::MAX_CONVEX_CONTACTS];
|
||||
if (PxU32 count = Gu::generateContacts(plane0, convex1, params.mContactDistance, normal, points, dists))
|
||||
for (PxU32 i = 0; i < count; ++i)
|
||||
contactBuffer.contact(points[i], normal, dists[i]);
|
||||
|
||||
return contactBuffer.count > 0;
|
||||
}
|
||||
159
engine/third_party/physx/source/geomutils/src/contact/GuContactPlaneMesh.cpp
vendored
Normal file
159
engine/third_party/physx/source/geomutils/src/contact/GuContactPlaneMesh.cpp
vendored
Normal file
@@ -0,0 +1,159 @@
|
||||
// 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 "CmMatrix34.h"
|
||||
#include "foundation/PxVec3.h"
|
||||
#include "CmScaling.h"
|
||||
#include "foundation/PxMat34.h"
|
||||
#include "foundation/PxArray.h"
|
||||
#include "foundation/PxAssert.h"
|
||||
#include "foundation/PxBitMap.h"
|
||||
#include "foundation/PxBounds3.h"
|
||||
#include "foundation/PxMat33.h"
|
||||
#include "foundation/PxSimpleTypes.h"
|
||||
#include "geometry/PxBoxGeometry.h"
|
||||
#include "geometry/PxMeshQuery.h"
|
||||
#include "geometry/PxTriangleMesh.h"
|
||||
#include "geomutils/PxContactBuffer.h"
|
||||
#include "GuTriangle.h"
|
||||
#include "geomutils/PxContactPoint.h"
|
||||
#include "foundation/PxPreprocessor.h"
|
||||
#include "foundation/PxTransform.h"
|
||||
#include "GuContactMethodImpl.h"
|
||||
#include "GuContactReduction.h"
|
||||
#include "GuMidphaseInterface.h"
|
||||
#include "PxContact.h"
|
||||
|
||||
using namespace physx;
|
||||
using namespace Cm;
|
||||
|
||||
using BufferedContactPatch = Gu::BufferedPatch<6, 64>;
|
||||
using ReducedContactPatch = Gu::TinyContactPatch<6>;
|
||||
|
||||
bool Gu::contactPlaneMesh(GU_CONTACT_METHOD_ARGS)
|
||||
{
|
||||
PX_UNUSED(renderOutput);
|
||||
PX_UNUSED(cache);
|
||||
PX_UNUSED(shape0);
|
||||
|
||||
// Get actual shape data
|
||||
const PxTriangleMeshGeometry& shapeMesh = checkedCast<PxTriangleMeshGeometry>(shape1);
|
||||
|
||||
// Plane is implicitly <1,0,0> 0 in localspace
|
||||
const PxVec3* PX_RESTRICT vertices = shapeMesh.triangleMesh->getVertices();
|
||||
const PxTransform meshToPlane0Trafo = transform0.transformInv(transform1);
|
||||
const Matrix34FromTransform meshToPlane0 (meshToPlane0Trafo);
|
||||
const PxMat33 meshToPlane_rot(meshToPlane0[0], meshToPlane0[1], meshToPlane0[2] );
|
||||
|
||||
const bool idtScale = shapeMesh.scale.isIdentity();
|
||||
FastVertex2ShapeScaling meshScaling;
|
||||
if(!idtScale)
|
||||
meshScaling.init(shapeMesh.scale);
|
||||
|
||||
const PxMat34 meshToPlane(meshToPlane_rot * meshScaling.getVertex2ShapeSkew(), meshToPlane0[3]);
|
||||
|
||||
//convexToPlane = context.mVertex2ShapeSkew[1].getVertex2WorldSkew(convexToPlane);
|
||||
|
||||
const Matrix34FromTransform planeToW(transform0);
|
||||
const PxVec3 contactNormal = -planeToW.m.column0;
|
||||
|
||||
BufferedContactPatch patch;
|
||||
patch.mRootNormal = contactNormal;
|
||||
|
||||
|
||||
const PxMeshScale& sdfScale = shapeMesh.scale;
|
||||
const PxMat33 sdfScaleMat = sdfScale.toMat33();
|
||||
|
||||
PxBounds3 meshBoundsAtWorldScale = shapeMesh.triangleMesh->getLocalBounds();
|
||||
meshBoundsAtWorldScale.minimum = sdfScaleMat.transform(meshBoundsAtWorldScale.minimum);
|
||||
meshBoundsAtWorldScale.maximum = sdfScaleMat.transform(meshBoundsAtWorldScale.maximum);
|
||||
|
||||
const PxReal radius = 0.5f*(meshBoundsAtWorldScale.maximum - meshBoundsAtWorldScale.minimum).magnitude();
|
||||
PxVec3 transformedBoundsCenter = meshToPlane0Trafo.transform(meshBoundsAtWorldScale.getCenter());
|
||||
transformedBoundsCenter.x = 0.0f; //In local plane space, this corresponds to a projection of a point onto the plane
|
||||
const PxBoxGeometry planeBoxMesh(params.mContactDistance, radius, radius);
|
||||
|
||||
const PxU32 MAX_INTERSECTIONS = 1024 * 32;
|
||||
PxArray<PxU32> overlappingTriangles;
|
||||
overlappingTriangles.resize(MAX_INTERSECTIONS); //TODO: Not ideal, dynamic allocation for every function call
|
||||
//PxU32 overlappingTriangles[MAX_INTERSECTIONS]; //TODO: Is this too much memory to allocate on the stack?
|
||||
|
||||
bool overflow = false;
|
||||
const PxU32 overlapCount = PxMeshQuery::findOverlapTriangleMesh(planeBoxMesh, PxTransform(transformedBoundsCenter), shapeMesh, meshToPlane0Trafo, overlappingTriangles.begin(), MAX_INTERSECTIONS, 0, overflow);
|
||||
PX_ASSERT(!overflow);
|
||||
|
||||
const bool has16BitIndices = shapeMesh.triangleMesh->getTriangleMeshFlags() & physx::PxTriangleMeshFlag::e16_BIT_INDICES;
|
||||
const void* PX_RESTRICT tris = shapeMesh.triangleMesh->getTriangles();
|
||||
|
||||
PxBitMap bitmap;
|
||||
bitmap.resize(shapeMesh.triangleMesh->getNbVertices(), false); //TODO: Not ideal, dynamic allocation for every function call
|
||||
|
||||
// FIXME: Make use of bvh to reduce number of checks here
|
||||
bool contact = false;
|
||||
for (PxU32 i = 0; i < overlapCount; ++i)
|
||||
{
|
||||
const PxU32 triIdx = overlappingTriangles[i];
|
||||
Gu::IndexedTriangle32 triIndices;
|
||||
getVertexRefs(triIdx, triIndices.mRef[0], triIndices.mRef[1], triIndices.mRef[2], tris, has16BitIndices);
|
||||
|
||||
for (PxU32 j = 0; j < 3; ++j)
|
||||
{
|
||||
const PxU32 vertexIndex = triIndices[j];
|
||||
if (bitmap.test(vertexIndex))
|
||||
continue;
|
||||
bitmap.set(vertexIndex);
|
||||
|
||||
const PxVec3& vertex = vertices[vertexIndex];
|
||||
const PxVec3 pointInPlane = meshToPlane.transform(vertex); //TODO: this multiply could be factored out!
|
||||
if (pointInPlane.x <= params.mContactDistance)
|
||||
{
|
||||
contact = true;
|
||||
patch.addContact(TinyContact(contactNormal, pointInPlane.x, planeToW.transform(pointInPlane)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (contact)
|
||||
{
|
||||
ReducedContactPatch reduced;
|
||||
patch.asTinyContactPatch(reduced);
|
||||
for (const TinyContact& tiny: reduced)
|
||||
{
|
||||
PxContactPoint* PX_RESTRICT pt = contactBuffer.contact();
|
||||
if(pt != NULL)
|
||||
{
|
||||
pt->normal = contactNormal;
|
||||
pt->point = tiny.mPoint;
|
||||
pt->separation = tiny.mSeparation;
|
||||
pt->internalFaceIndex1 = PXC_CONTACT_NO_FACE_INDEX;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return contact;
|
||||
}
|
||||
861
engine/third_party/physx/source/geomutils/src/contact/GuContactPolygonPolygon.cpp
vendored
Normal file
861
engine/third_party/physx/source/geomutils/src/contact/GuContactPolygonPolygon.cpp
vendored
Normal file
@@ -0,0 +1,861 @@
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
|
||||
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
|
||||
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
|
||||
|
||||
#include "foundation/PxMemory.h"
|
||||
#include "geomutils/PxContactBuffer.h"
|
||||
|
||||
#include "GuContactPolygonPolygon.h"
|
||||
#include "GuShapeConvex.h"
|
||||
#include "GuInternal.h"
|
||||
#include "foundation/PxAlloca.h"
|
||||
#include "foundation/PxFPU.h"
|
||||
|
||||
using namespace physx;
|
||||
using namespace Gu;
|
||||
|
||||
#define CONTACT_REDUCTION
|
||||
|
||||
/*
|
||||
void gVisualizeLocalLine(const PxVec3& a, const PxVec3& b, const PxMat34& m, PxsContactManager& manager) //temp debug
|
||||
{
|
||||
RenderOutput out = manager.getContext()->getRenderOutput();
|
||||
out << 0xffffff << m << RenderOutput::LINES << a << b;
|
||||
}
|
||||
*/
|
||||
|
||||
#ifdef CONTACT_REDUCTION
|
||||
static PX_FORCE_INLINE PxReal dot2D(const PxVec3& v0, const PxVec3& v1)
|
||||
{
|
||||
return v0.x * v1.x + v0.y * v1.y;
|
||||
}
|
||||
|
||||
static void ContactReductionAllIn( PxContactBuffer& contactBuffer, PxU32 nbExistingContacts, PxU32 numIn,
|
||||
const PxMat33& rotT,
|
||||
const PxVec3* PX_RESTRICT vertices, const PxU8* PX_RESTRICT indices)
|
||||
{
|
||||
// Number of contacts created by current call
|
||||
const PxU32 nbNewContacts = contactBuffer.count - nbExistingContacts;
|
||||
|
||||
if(nbNewContacts<=4)
|
||||
return; // no reduction for less than 4 verts
|
||||
|
||||
// We have 3 different numbers here:
|
||||
// - numVerts = number of vertices in the convex polygon we're dealing with
|
||||
// - numIn = number of those that were "inside" the other convex polygon (should be <= numVerts)
|
||||
// - contactBuffer.count = total number of contacts *from both polygons* (that's the catch here)
|
||||
// The fast path can only be chosen when the contact buffer contains all the verts from current polygon,
|
||||
// i.e. when contactBuffer.count == numIn == numVerts
|
||||
|
||||
PxContactPoint* PX_RESTRICT ctcs = contactBuffer.contacts + nbExistingContacts;
|
||||
|
||||
if(numIn == nbNewContacts)
|
||||
{
|
||||
// Codepath 1: all vertices generated a contact
|
||||
|
||||
PxReal deepestSeparation = ctcs[0].separation;
|
||||
PxU32 deepestIndex = 0;
|
||||
for(PxU32 i=1; i<nbNewContacts; ++i)
|
||||
{
|
||||
if(deepestSeparation > ctcs[i].separation)
|
||||
{
|
||||
deepestSeparation = ctcs[i].separation;
|
||||
deepestIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
PxU32 index = 0;
|
||||
const PxU32 step = (numIn<<16)>>2; // Fixed point math, don't use floats here please
|
||||
bool needsExtraPoint = true;
|
||||
for(PxU32 i=0;i<4;i++)
|
||||
{
|
||||
const PxU32 contactIndex = index>>16;
|
||||
ctcs[i] = ctcs[contactIndex];
|
||||
if(contactIndex==deepestIndex)
|
||||
needsExtraPoint = false;
|
||||
index += step;
|
||||
}
|
||||
|
||||
if(needsExtraPoint)
|
||||
{
|
||||
ctcs[4] = ctcs[deepestIndex];
|
||||
contactBuffer.count = nbExistingContacts + 5;
|
||||
}
|
||||
else
|
||||
{
|
||||
contactBuffer.count = nbExistingContacts + 4;
|
||||
}
|
||||
|
||||
/* PT: TODO: investigate why this one does not work
|
||||
PxU32 index = deepestIndex<<16;
|
||||
const PxU32 step = (numIn<<16)>>2; // Fixed point math, don't use floats here please
|
||||
for(PxU32 i=0;i<4;i++)
|
||||
{
|
||||
PxU32 contactIndex = index>>16;
|
||||
if(contactIndex>=numIn)
|
||||
contactIndex -= numIn;
|
||||
ctcs[i] = ctcs[contactIndex];
|
||||
index += step;
|
||||
}
|
||||
contactBuffer.count = nbExistingContacts + 4;*/
|
||||
}
|
||||
else
|
||||
{
|
||||
// Codepath 2: all vertices are "in" but only some of them generated a contact
|
||||
|
||||
// WARNING: this path doesn't work when the buffer contains vertices from both polys.
|
||||
|
||||
// TODO: precompute those axes
|
||||
const PxU32 nbAxes = 8;
|
||||
PxVec3 dirs[nbAxes];
|
||||
float angle = 0.0f;
|
||||
const float angleStep = PxDegToRad(180.0f/float(nbAxes));
|
||||
for(PxU32 i=0;i<nbAxes;i++)
|
||||
{
|
||||
dirs[i] = PxVec3(cosf(angle), sinf(angle), 0.0f);
|
||||
angle += angleStep;
|
||||
}
|
||||
|
||||
float dpmin[nbAxes];
|
||||
float dpmax[nbAxes];
|
||||
for(PxU32 i=0;i<nbAxes;i++)
|
||||
{
|
||||
dpmin[i] = PX_MAX_F32;
|
||||
dpmax[i] = -PX_MAX_F32;
|
||||
}
|
||||
|
||||
for(PxU32 i=0;i<nbNewContacts;i++)
|
||||
{
|
||||
const PxVec3& p = vertices[indices[i]];
|
||||
|
||||
// Transform to 2D
|
||||
const PxVec3 p2d = rotT.transform(p);
|
||||
|
||||
for(PxU32 j=0;j<nbAxes;j++)
|
||||
{
|
||||
const float dp = dot2D(dirs[j], p2d);
|
||||
dpmin[j] = physx::intrinsics::selectMin(dpmin[j], dp);
|
||||
dpmax[j] = physx::intrinsics::selectMax(dpmax[j], dp);
|
||||
}
|
||||
}
|
||||
|
||||
PxU32 bestAxis = 0;
|
||||
float maxVariance = dpmax[0] - dpmin[0];
|
||||
for(PxU32 i=1;i<nbAxes;i++)
|
||||
{
|
||||
const float variance = dpmax[i] - dpmin[i];
|
||||
if(variance>maxVariance)
|
||||
{
|
||||
maxVariance = variance;
|
||||
bestAxis = i;
|
||||
}
|
||||
}
|
||||
|
||||
const PxVec3 u = dirs[bestAxis];
|
||||
const PxVec3 v = PxVec3(-u.y, u.x, 0.0f);
|
||||
// PxVec3(1.0f, 0.0f, 0.0f) => PxVec3(0.0f, 1.0f, 0.0f)
|
||||
// PxVec3(0.0f, 1.0f, 0.0f) => PxVec3(-1.0f, 0.0f, 0.0f)
|
||||
// PxVec3(-1.0f, 1.0f, 0.0f) => PxVec3(-1.0f, -1.0f, 0.0f)
|
||||
// PxVec3(1.0f, 1.0f, 0.0f) => PxVec3(-1.0f, 1.0f, 0.0f)
|
||||
|
||||
float dpminu = PX_MAX_F32;
|
||||
float dpmaxu = -PX_MAX_F32;
|
||||
float dpminv = PX_MAX_F32;
|
||||
float dpmaxv = -PX_MAX_F32;
|
||||
PxU32 indexMinU = 0;
|
||||
PxU32 indexMaxU = 0;
|
||||
PxU32 indexMinV = 0;
|
||||
PxU32 indexMaxV = 0;
|
||||
|
||||
for(PxU32 i=0;i<nbNewContacts;i++)
|
||||
{
|
||||
const PxVec3& p = vertices[indices[i]];
|
||||
|
||||
// Transform to 2D
|
||||
const PxVec3 p2d = rotT.transform(p);
|
||||
|
||||
const float dpu = dot2D(u, p2d);
|
||||
const float dpv = dot2D(v, p2d);
|
||||
|
||||
if(dpu<dpminu)
|
||||
{
|
||||
dpminu=dpu;
|
||||
indexMinU = i;
|
||||
}
|
||||
if(dpu>dpmaxu)
|
||||
{
|
||||
dpmaxu=dpu;
|
||||
indexMaxU = i;
|
||||
}
|
||||
|
||||
if(dpv<dpminv)
|
||||
{
|
||||
dpminv=dpv;
|
||||
indexMinV = i;
|
||||
}
|
||||
if(dpv>dpmaxv)
|
||||
{
|
||||
dpmaxv=dpv;
|
||||
indexMaxV = i;
|
||||
}
|
||||
}
|
||||
|
||||
if(indexMaxU == indexMinU)
|
||||
indexMaxU = 0xffffffff;
|
||||
if(indexMinV == indexMinU || indexMinV == indexMaxU)
|
||||
indexMinV = 0xffffffff;
|
||||
if(indexMaxV == indexMinU || indexMaxV == indexMaxU || indexMaxV == indexMinV)
|
||||
indexMaxV = 0xffffffff;
|
||||
|
||||
PxU32 newCount = 0;
|
||||
for(PxU32 i=0;i<nbNewContacts;i++)
|
||||
{
|
||||
if( i==indexMinU
|
||||
|| i==indexMaxU
|
||||
|| i==indexMinV
|
||||
|| i==indexMaxV)
|
||||
{
|
||||
ctcs[newCount++] = ctcs[i];
|
||||
}
|
||||
}
|
||||
contactBuffer.count = nbExistingContacts + newCount;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// PT: please leave that function in the same translation unit as the calling code
|
||||
/*static*/ PxMat33 Gu::findRotationMatrixFromZ(const PxVec3& to)
|
||||
{
|
||||
PxMat33 result;
|
||||
|
||||
const PxReal e = to.z;
|
||||
const PxReal f = PxAbs(e);
|
||||
|
||||
if(f <= 0.9999f)
|
||||
{
|
||||
// PT: please keep the normal case first for PS3 branch prediction
|
||||
|
||||
// Normal case, to and from are not parallel or anti-parallel
|
||||
const PxVec3 v = cross001(to);
|
||||
const PxReal h = 1.0f/(1.0f + e); /* optimization by Gottfried Chen */
|
||||
const PxReal hvx = h * v.x;
|
||||
const PxReal hvz = h * v.z;
|
||||
const PxReal hvxy = hvx * v.y;
|
||||
const PxReal hvxz = hvx * v.z;
|
||||
const PxReal hvyz = hvz * v.y;
|
||||
|
||||
result(0,0) = e + hvx*v.x;
|
||||
result(0,1) = hvxy - v.z;
|
||||
result(0,2) = hvxz + v.y;
|
||||
|
||||
result(1,0) = hvxy + v.z;
|
||||
result(1,1) = e + h*v.y*v.y;
|
||||
result(1,2) = hvyz - v.x;
|
||||
|
||||
result(2,0) = hvxz - v.y;
|
||||
result(2,1) = hvyz + v.x;
|
||||
result(2,2) = e + hvz*v.z;
|
||||
}
|
||||
else
|
||||
{
|
||||
//Vectors almost parallel
|
||||
// PT: TODO: simplify code below
|
||||
PxVec3 from(0.0f, 0.0f, 1.0f);
|
||||
PxVec3 absFrom(0.0f, 0.0f, 1.0f);
|
||||
|
||||
if(absFrom.x < absFrom.y)
|
||||
{
|
||||
if(absFrom.x < absFrom.z)
|
||||
absFrom = PxVec3(1.0f, 0.0f, 0.0f);
|
||||
else
|
||||
absFrom = PxVec3(0.0f, 0.0f, 1.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(absFrom.y < absFrom.z)
|
||||
absFrom = PxVec3(0.0f, 1.0f, 0.0f);
|
||||
else
|
||||
absFrom = PxVec3(0.0f, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
PxVec3 u, v;
|
||||
u.x = absFrom.x - from.x; u.y = absFrom.y - from.y; u.z = absFrom.z - from.z;
|
||||
v.x = absFrom.x - to.x; v.y = absFrom.y - to.y; v.z = absFrom.z - to.z;
|
||||
|
||||
const PxReal c1 = 2.0f / u.dot(u);
|
||||
const PxReal c2 = 2.0f / v.dot(v);
|
||||
const PxReal c3 = c1 * c2 * u.dot(v);
|
||||
|
||||
for(unsigned int i = 0; i < 3; i++)
|
||||
{
|
||||
for(unsigned int j = 0; j < 3; j++)
|
||||
{
|
||||
result(i,j) = - c1*u[i]*u[j] - c2*v[i]*v[j] + c3*v[i]*u[j];
|
||||
}
|
||||
result(i,i) += 1.0f;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// PT: using this specialized version avoids doing an explicit transpose, which reduces LHS
|
||||
PX_FORCE_INLINE PxMat34 transformTranspose(const PxMat33& a, const PxMat34& b)
|
||||
{
|
||||
return PxMat34(a.transformTranspose(b.m.column0), a.transformTranspose(b.m.column1), a.transformTranspose(b.m.column2), a.transformTranspose(b.p));
|
||||
}
|
||||
|
||||
// Helper function to transform x/y coordinate of point.
|
||||
PX_FORCE_INLINE void transform2D(float& x, float& y, const PxVec3& src, const PxMat34& mat)
|
||||
{
|
||||
x = src.x * mat.m.column0.x + src.y * mat.m.column1.x + src.z * mat.m.column2.x + mat.p.x;
|
||||
y = src.x * mat.m.column0.y + src.y * mat.m.column1.y + src.z * mat.m.column2.y + mat.p.y;
|
||||
}
|
||||
|
||||
// Helper function to transform x/y coordinate of point. Use transposed matrix
|
||||
PX_FORCE_INLINE void transform2DT(float& x, float& y, const PxVec3& src, const PxMat33& mat)
|
||||
{
|
||||
x = mat.column0.dot(src);
|
||||
y = mat.column1.dot(src);
|
||||
}
|
||||
|
||||
// Helper function to transform z coordinate of point.
|
||||
PX_FORCE_INLINE PxReal transformZ(const PxVec3& src, const PxMat34& mat)
|
||||
{
|
||||
return src.x * mat.m.column0.z + src.y * mat.m.column1.z + src.z * mat.m.column2.z + mat.p.z;
|
||||
}
|
||||
|
||||
static void transformVertices( float& minX, float& minY,
|
||||
float& maxX, float& maxY,
|
||||
float* PX_RESTRICT verts2D,
|
||||
PxU32 nb, const PxVec3* PX_RESTRICT vertices, const PxU8* PX_RESTRICT indices, const PxMat33& RotT)
|
||||
{
|
||||
// PT: using local variables is important to reduce LHS.
|
||||
float lminX = FLT_MAX;
|
||||
float lminY = FLT_MAX;
|
||||
float lmaxX = -FLT_MAX;
|
||||
float lmaxY = -FLT_MAX;
|
||||
|
||||
// PT: project points, compute min & max at the same time
|
||||
for(PxU32 i=0; i<nb; i++)
|
||||
{
|
||||
float x,y;
|
||||
transform2DT(x, y, vertices[indices[i]], RotT);
|
||||
lminX = physx::intrinsics::selectMin(lminX, x);
|
||||
lminY = physx::intrinsics::selectMin(lminY, y);
|
||||
lmaxX = physx::intrinsics::selectMax(lmaxX, x);
|
||||
lmaxY = physx::intrinsics::selectMax(lmaxY, y);
|
||||
verts2D[i*2+0] = x;
|
||||
verts2D[i*2+1] = y;
|
||||
}
|
||||
|
||||
// DE702
|
||||
// Compute center of polygon
|
||||
const float cx = (lminX + lmaxX)*0.5f;
|
||||
const float cy = (lminY + lmaxY)*0.5f;
|
||||
// We'll scale the polygon by epsilon
|
||||
const float epsilon = 1.e-6f;
|
||||
// Adjust bounds to take care of scaling
|
||||
lminX -= epsilon;
|
||||
lminY -= epsilon;
|
||||
lmaxX += epsilon;
|
||||
lmaxY += epsilon;
|
||||
//~DE702
|
||||
|
||||
// PT: relocate polygon to positive quadrant
|
||||
for(PxU32 i=0; i<nb; i++)
|
||||
{
|
||||
const float x = verts2D[i*2+0];
|
||||
const float y = verts2D[i*2+1];
|
||||
|
||||
// PT: original code suffering from DE702 (relocation)
|
||||
// verts2D[i*2+0] = x - lminX;
|
||||
// verts2D[i*2+1] = y - lminY;
|
||||
|
||||
// PT: theoretically proper DE702 fix (relocation + scaling)
|
||||
const float dx = x - cx;
|
||||
const float dy = y - cy;
|
||||
// const float coeff = epsilon * physx::intrinsics::recipSqrt(dx*dx+dy*dy);
|
||||
// verts2D[i*2+0] = x - lminX + dx * coeff;
|
||||
// verts2D[i*2+1] = y - lminY + dy * coeff;
|
||||
|
||||
// PT: approximate but faster DE702 fix. We multiply by epsilon so this is good enough.
|
||||
verts2D[i*2+0] = x - lminX + physx::intrinsics::fsel(dx, epsilon, -epsilon);
|
||||
verts2D[i*2+1] = y - lminY + physx::intrinsics::fsel(dy, epsilon, -epsilon);
|
||||
}
|
||||
lmaxX -= lminX;
|
||||
lmaxY -= lminY;
|
||||
|
||||
minX = lminX;
|
||||
minY = lminY;
|
||||
maxX = lmaxX;
|
||||
maxY = lmaxY;
|
||||
}
|
||||
|
||||
//! Dedicated triangle version
|
||||
PX_FORCE_INLINE bool pointInTriangle2D( float px, float pz,
|
||||
float p0x, float p0z,
|
||||
float e10x, float e10z,
|
||||
float e20x, float e20z)
|
||||
{
|
||||
const float a = e10x*e10x + e10z*e10z;
|
||||
const float b = e10x*e20x + e10z*e20z;
|
||||
const float c = e20x*e20x + e20z*e20z;
|
||||
const float ac_bb = (a*c)-(b*b);
|
||||
|
||||
const float vpx = px - p0x;
|
||||
const float vpz = pz - p0z;
|
||||
|
||||
const float d = vpx*e10x + vpz*e10z;
|
||||
const float e = vpx*e20x + vpz*e20z;
|
||||
|
||||
const float x = (d*c) - (e*b);
|
||||
const float y = (e*a) - (d*b);
|
||||
const float z = x + y - ac_bb;
|
||||
|
||||
// Same as: if(x>0.0f && y>0.0f && z<0.0f) return TRUE;
|
||||
// else return FALSE;
|
||||
// return (( IR(z) & ~(IR(x)|IR(y)) ) & SIGN_BITMASK) != 0;
|
||||
if(x>0.0f && y>0.0f && z<0.0f) return true;
|
||||
else return false;
|
||||
}
|
||||
|
||||
|
||||
enum OutCode
|
||||
{
|
||||
OUT_XP = (1<<0),
|
||||
OUT_XN = (1<<1),
|
||||
OUT_YP = (1<<2),
|
||||
OUT_YN = (1<<3)
|
||||
};
|
||||
|
||||
static
|
||||
//PX_FORCE_INLINE
|
||||
bool PointInConvexPolygon2D_OutCodes(const float* PX_RESTRICT pgon2D, PxU32 numVerts, const PxReal tx, const PxReal ty, const PxReal maxX, const PxReal maxY, PxU8& outCodes)
|
||||
{
|
||||
PxU32 out = 0;
|
||||
if(tx<0.0f) out |= OUT_XN;
|
||||
if(ty<0.0f) out |= OUT_YN;
|
||||
if(tx>maxX) out |= OUT_XP;
|
||||
if(ty>maxY) out |= OUT_YP;
|
||||
outCodes = PxU8(out);
|
||||
if(out)
|
||||
return false;
|
||||
|
||||
if(numVerts==3)
|
||||
return pointInTriangle2D( tx, ty,
|
||||
pgon2D[0], pgon2D[1],
|
||||
pgon2D[2] - pgon2D[0],
|
||||
pgon2D[3] - pgon2D[1],
|
||||
pgon2D[4] - pgon2D[0],
|
||||
pgon2D[5] - pgon2D[1]);
|
||||
|
||||
#define X 0
|
||||
#define Y 1
|
||||
|
||||
const PxReal* PX_RESTRICT vtx0_ = pgon2D + (numVerts-1)*2;
|
||||
const PxReal* PX_RESTRICT vtx1_ = pgon2D;
|
||||
|
||||
const int* PX_RESTRICT ivtx0 = reinterpret_cast<const int*>(vtx0_);
|
||||
const int* PX_RESTRICT ivtx1 = reinterpret_cast<const int*>(vtx1_);
|
||||
//const int itx = (int&)tx;
|
||||
//const int ity = (int&)ty;
|
||||
// const int ity = PX_SIR(ty);
|
||||
const int* tmp = reinterpret_cast<const int*>(&ty);
|
||||
const int ity = *tmp;
|
||||
|
||||
// get test bit for above/below X axis
|
||||
int yflag0 = ivtx0[Y] >= ity;
|
||||
|
||||
int InsideFlag = 0;
|
||||
|
||||
while(numVerts--)
|
||||
{
|
||||
const int yflag1 = ivtx1[Y] >= ity;
|
||||
if(yflag0 != yflag1)
|
||||
{
|
||||
const PxReal* PX_RESTRICT vtx0 = reinterpret_cast<const PxReal*>(ivtx0);
|
||||
const PxReal* PX_RESTRICT vtx1 = reinterpret_cast<const PxReal*>(ivtx1);
|
||||
if( ((vtx1[Y]-ty) * (vtx0[X]-vtx1[X]) > (vtx1[X]-tx) * (vtx0[Y]-vtx1[Y])) == yflag1 )
|
||||
{
|
||||
if(InsideFlag == 1) return false;
|
||||
|
||||
InsideFlag++;
|
||||
}
|
||||
}
|
||||
yflag0 = yflag1;
|
||||
ivtx0 = ivtx1;
|
||||
ivtx1 += 2;
|
||||
}
|
||||
#undef X
|
||||
#undef Y
|
||||
|
||||
return InsideFlag & 1;
|
||||
}
|
||||
|
||||
// Helper function to detect contact between two edges
|
||||
PX_FORCE_INLINE bool EdgeEdgeContactSpecial(const PxVec3& v1, const PxPlane& plane,
|
||||
const PxVec3& p1, const PxVec3& p2, const PxVec3& dir, const PxVec3& p3, const PxVec3& p4,
|
||||
PxReal& dist, PxVec3& ip, unsigned int i, unsigned int j, float coeff)
|
||||
{
|
||||
const PxReal d3 = plane.distance(p3);
|
||||
PxReal temp = d3 * plane.distance(p4);
|
||||
if(temp > 0.0f)
|
||||
return false;
|
||||
|
||||
// if colliding edge (p3,p4) and plane are parallel return no collision
|
||||
const PxVec3 v2 = (p4-p3);
|
||||
temp = plane.n.dot(v2);
|
||||
if(temp == 0.0f) // ### epsilon would be better
|
||||
return false;
|
||||
|
||||
// compute intersection point of plane and colliding edge (p3,p4)
|
||||
ip = p3-v2*(d3/temp);
|
||||
|
||||
// compute distance of intersection from line (ip, -dir) to line (p1,p2)
|
||||
dist = (v1[i]*(ip[j]-p1[j])-v1[j]*(ip[i]-p1[i])) * coeff;
|
||||
if(dist < 0.0f)
|
||||
return false;
|
||||
|
||||
// compute intersection point on edge (p1,p2) line
|
||||
ip -= dist*dir;
|
||||
|
||||
// check if intersection point (ip) is between edge (p1,p2) vertices
|
||||
temp = (p1.x-ip.x)*(p2.x-ip.x)+(p1.y-ip.y)*(p2.y-ip.y)+(p1.z-ip.z)*(p2.z-ip.z);
|
||||
if(temp<0.0f)
|
||||
return true; // collision found
|
||||
|
||||
return false; //no collision
|
||||
}
|
||||
|
||||
//This one can also handle 2 vertex 'polygons' (useful for capsule surface segments) and can shift the results before contact generation.
|
||||
bool Gu::contactPolygonPolygonExt( PxU32 numVerts0, const PxVec3* vertices0, const PxU8* indices0, //polygon 0
|
||||
const PxMat34& world0, const PxPlane& localPlane0, //xform of polygon 0, plane of polygon
|
||||
const PxMat33& rotT0,
|
||||
//
|
||||
PxU32 numVerts1, const PxVec3* PX_RESTRICT vertices1, const PxU8* PX_RESTRICT indices1, //polygon 1
|
||||
const PxMat34& world1, const PxPlane& localPlane1, //xform of polygon 1, plane of polygon
|
||||
const PxMat33& rotT1,
|
||||
//
|
||||
const PxVec3& worldSepAxis, //world normal of separating plane - this is the world space normal of polygon0!!
|
||||
const PxMat34& transform0to1, const PxMat34& transform1to0, //transforms between polygons
|
||||
PxU32 /*polyIndex0*/, PxU32 polyIndex1, //feature indices for contact callback
|
||||
PxContactBuffer& contactBuffer,
|
||||
bool flipNormal, const PxVec3& posShift, PxReal sepShift) // shape order, result shift
|
||||
{
|
||||
const PxVec3 n = flipNormal ? -worldSepAxis : worldSepAxis;
|
||||
|
||||
PX_ASSERT(indices0 != NULL && indices1 != NULL);
|
||||
|
||||
// - optimize "from to" computation
|
||||
// - do the raycast case && EE tests in same space as 2D case...
|
||||
// - project all edges at the same time ?
|
||||
PxU32 NumIn = 0;
|
||||
bool status = false;
|
||||
|
||||
void* PX_RESTRICT stackMemory;
|
||||
{
|
||||
const PxU32 maxNumVert = PxMax(numVerts0, numVerts1);
|
||||
stackMemory = PxAlloca(maxNumVert * sizeof(PxVec3));
|
||||
}
|
||||
|
||||
const PxU32 size0 = numVerts0 * sizeof(bool);
|
||||
bool* PX_RESTRICT flags0 = reinterpret_cast<bool*>(PxAlloca(size0));
|
||||
PxU8* PX_RESTRICT outCodes0 = reinterpret_cast<PxU8*>(PxAlloca(size0));
|
||||
// PxMemZero(flags0, size0);
|
||||
// PxMemZero(outCodes0, size0);
|
||||
|
||||
const PxU32 size1 = numVerts1 * sizeof(bool);
|
||||
bool* PX_RESTRICT flags1 = reinterpret_cast<bool*>(PxAlloca(size1));
|
||||
PxU8* PX_RESTRICT outCodes1 = reinterpret_cast<PxU8*>(PxAlloca(size1));
|
||||
// PxMemZero(flags1, size1);
|
||||
// PxMemZero(outCodes1, size1);
|
||||
|
||||
#ifdef CONTACT_REDUCTION
|
||||
// We want to do contact reduction on newly created contacts, not on all the already existing ones...
|
||||
PxU32 nbExistingContacts = contactBuffer.count;
|
||||
PxU32 nbCurrentContacts=0;
|
||||
PxU8 indices[PxContactBuffer::MAX_CONTACTS];
|
||||
#endif
|
||||
|
||||
{
|
||||
//polygon 1
|
||||
float* PX_RESTRICT verts2D = NULL;
|
||||
float minX=0, minY=0;
|
||||
float maxX=0, maxY=0;
|
||||
|
||||
const PxVec3 localDir = -world1.rotateTranspose(worldSepAxis); //contactNormal in hull1 space
|
||||
//that's redundant, its equal to -localPlane1.d
|
||||
const PxMat34 t0to2D = transformTranspose(rotT1, transform0to1); //transform from hull0 to RotT
|
||||
|
||||
PxReal dn = localDir.dot(localPlane1.n); //if the contactNormal == +-(normal of poly0) is NOT orthogonal to poly1 ...this is just to protect the division below.
|
||||
|
||||
// PT: TODO: if "numVerts1>2" we may skip more
|
||||
if (numVerts1 > 2 //no need to test whether we're 'inside' ignore capsule segments and points
|
||||
// if(!(-1E-7 < dn && dn < 1E-7))
|
||||
&& dn >= 1E-7f) // PT: it should never be negative so this unique test is enough
|
||||
{
|
||||
dn = 1.0f / dn;
|
||||
const float ld1 = -localPlane1.d; // PT: unavoidable "int-to-float" LHS here, so we only want to read it once!
|
||||
|
||||
// Lazy-transform vertices
|
||||
if(!verts2D)
|
||||
{
|
||||
verts2D = reinterpret_cast<float*>(stackMemory);
|
||||
//Project points
|
||||
transformVertices(
|
||||
minX, minY,
|
||||
maxX, maxY,
|
||||
verts2D, numVerts1, vertices1, indices1, rotT1);
|
||||
}
|
||||
|
||||
for(PxU32 i=0; i < numVerts0; i++) //for all vertices of poly0
|
||||
{
|
||||
const PxVec3& p = vertices0[indices0[i]];
|
||||
const float p0_z = transformZ(p, t0to2D); //transform ith vertex of poly0 to RotT
|
||||
|
||||
const PxVec3 pIn1 = transform0to1.transform(p); //transform vertex to hull1 space, in which we have the poly1 vertices.
|
||||
|
||||
const PxReal dd = (p0_z - ld1) * dn; //(p0_z + localPlane1.d) is the depth of the vertex behind the triangle measured along the triangle's normal.
|
||||
//we convert this to being measured along the 'contact normal' using the division.
|
||||
|
||||
// if(dd < 0.0f) //if the penetrating vertex will have a penetration along the contact normal:
|
||||
// PX_ASSERT(dd <= 0.0f); // PT: dn is always positive, so dd is always negative
|
||||
{
|
||||
float px, py;
|
||||
transform2DT(px, py, pIn1 - dd*localDir, rotT1); //project vertex into poly1 plane along CONTACT NORMAL - not the polygon's normal.
|
||||
|
||||
const bool res = PointInConvexPolygon2D_OutCodes(verts2D, numVerts1, px-minX, py-minY, maxX, maxY, outCodes0[i]);
|
||||
flags0[i] = res;
|
||||
if(res)
|
||||
{
|
||||
NumIn++;
|
||||
|
||||
if(p0_z < ld1)
|
||||
{
|
||||
status = true; // PT: keep this first to avoid an LHS when leaving the function
|
||||
|
||||
PxContactPoint* PX_RESTRICT ctc = contactBuffer.contact();
|
||||
if(ctc)
|
||||
{
|
||||
#ifdef CONTACT_REDUCTION
|
||||
indices[nbCurrentContacts++] = indices0[i];
|
||||
#endif
|
||||
ctc->normal = n;
|
||||
ctc->point = world0.transform(p) + (flipNormal ? posShift : PxVec3(0.0f));
|
||||
ctc->separation = dd + sepShift;
|
||||
ctc->internalFaceIndex1 = polyIndex1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
PxMemZero(flags0, size0);
|
||||
PxMemZero(outCodes0, size0);
|
||||
}
|
||||
|
||||
if(NumIn == numVerts0)
|
||||
{
|
||||
//All vertices0 are inside polygon 1
|
||||
#ifdef CONTACT_REDUCTION
|
||||
ContactReductionAllIn(contactBuffer, nbExistingContacts, NumIn, rotT0, vertices0, indices);
|
||||
#endif
|
||||
return status;
|
||||
}
|
||||
|
||||
#ifdef CONTACT_REDUCTION
|
||||
ContactReductionAllIn(contactBuffer, nbExistingContacts, NumIn, rotT0, vertices0, indices);
|
||||
#endif
|
||||
|
||||
#ifdef CONTACT_REDUCTION
|
||||
nbExistingContacts = contactBuffer.count;
|
||||
nbCurrentContacts = 0;
|
||||
#endif
|
||||
NumIn = 0;
|
||||
verts2D = NULL;
|
||||
|
||||
//Polygon 0
|
||||
const PxMat34 t1to2D = transformTranspose(rotT0, transform1to0);
|
||||
|
||||
if (numVerts0 > 2) //no need to test whether we're 'inside' ignore capsule segments and points
|
||||
{
|
||||
const float ld0 = -localPlane0.d; // PT: unavoidable "int-to-float" LHS here, so we only want to read it once!
|
||||
|
||||
// Lazy-transform vertices
|
||||
if(!verts2D)
|
||||
{
|
||||
verts2D = reinterpret_cast<float*>(stackMemory);
|
||||
//Project vertices
|
||||
transformVertices(
|
||||
minX, minY,
|
||||
maxX, maxY,
|
||||
verts2D, numVerts0, vertices0, indices0, rotT0);
|
||||
}
|
||||
|
||||
for(PxU32 i=0; i < numVerts1; i++)
|
||||
{
|
||||
const PxVec3& p = vertices1[indices1[i]];
|
||||
|
||||
float px, py;
|
||||
transform2D(px, py, p, t1to2D);
|
||||
|
||||
const bool res = PointInConvexPolygon2D_OutCodes(verts2D, numVerts0, px-minX, py-minY, maxX, maxY, outCodes1[i]);
|
||||
flags1[i] = res;
|
||||
if(res)
|
||||
{
|
||||
NumIn++;
|
||||
|
||||
const float pz = transformZ(p, t1to2D);
|
||||
if(pz < ld0)
|
||||
{
|
||||
status = true; // PT: keep this first to avoid an LHS when leaving the function
|
||||
|
||||
// PT: in theory, with this contact point we should use "worldSepAxis" as a contact normal.
|
||||
// However we want to output the same normal for all contact points not to break friction
|
||||
// patches!!! In theory again, it should be exactly the same since the contact point at
|
||||
// time of impact is supposed to be the same on both bodies. In practice however, and with
|
||||
// a depth-based engine, this is not the case. So the contact point here is not exactly
|
||||
// right, but preserving the friction patch seems more important.
|
||||
|
||||
PxContactPoint* PX_RESTRICT ctc = contactBuffer.contact();
|
||||
if(ctc)
|
||||
{
|
||||
#ifdef CONTACT_REDUCTION
|
||||
indices[nbCurrentContacts++] = indices1[i];
|
||||
#endif
|
||||
ctc->normal = n;
|
||||
ctc->point = world1.transform(p) + (flipNormal ? PxVec3(0.0f) : posShift);
|
||||
ctc->separation = (pz - ld0) + sepShift;
|
||||
ctc->internalFaceIndex1 = polyIndex1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(NumIn == numVerts1)
|
||||
{
|
||||
//all vertices 1 are inside polygon 0
|
||||
#ifdef CONTACT_REDUCTION
|
||||
ContactReductionAllIn(contactBuffer, nbExistingContacts, NumIn, rotT1, vertices1, indices);
|
||||
#endif
|
||||
return status;
|
||||
}
|
||||
#ifdef CONTACT_REDUCTION
|
||||
ContactReductionAllIn(contactBuffer, nbExistingContacts, NumIn, rotT1, vertices1, indices);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
PxMemZero(flags1, size1);
|
||||
PxMemZero(outCodes1, size1);
|
||||
}
|
||||
}
|
||||
|
||||
//Edge/edge case
|
||||
//Calculation done in space 0
|
||||
PxVec3* PX_RESTRICT verts1in0 = reinterpret_cast<PxVec3*>(stackMemory);
|
||||
for(PxU32 i=0; i<numVerts1; i++)
|
||||
{
|
||||
verts1in0[i] = transform1to0.transform(vertices1[indices1[i]]);
|
||||
}
|
||||
|
||||
if (numVerts0 >= 2 && numVerts1 >= 2)//useless if one of them is degenerate.
|
||||
for(PxU32 j=0; j<numVerts1; j++)
|
||||
{
|
||||
PxU32 j1 = j+1;
|
||||
if(j1 >= numVerts1) j1 = 0;
|
||||
|
||||
// if(!(flags1[j] ^ flags1[j1]))
|
||||
// continue;
|
||||
if(flags1[j] && flags1[j1])
|
||||
continue;
|
||||
if(outCodes1[j]&outCodes1[j1])
|
||||
continue;
|
||||
|
||||
const PxVec3& p0 = verts1in0[j];
|
||||
const PxVec3& p1 = verts1in0[j1];
|
||||
|
||||
// gVisualizeLocalLine(vertices1[indices1[j]], vertices1[indices1[j1]], world1, callback.getManager());
|
||||
|
||||
const PxVec3 v1 = p1-p0;
|
||||
const PxVec3 planeNormal = v1.cross(localPlane0.n);
|
||||
const PxPlane plane(planeNormal, -(planeNormal.dot(p0)));
|
||||
|
||||
// find largest 2D plane projection
|
||||
PxU32 _i, _j;
|
||||
closestAxis(planeNormal, _i, _j);
|
||||
|
||||
const PxReal coeff = 1.0f / (v1[_i]*localPlane0.n[_j]-v1[_j]*localPlane0.n[_i]);
|
||||
|
||||
for(PxU32 i=0; i<numVerts0; i++)
|
||||
{
|
||||
PxU32 i1 = i+1;
|
||||
if(i1 >= numVerts0) i1 = 0;
|
||||
|
||||
// if(!(flags0[i] ^ flags0[i1]))
|
||||
// continue;
|
||||
if(flags0[i] && flags0[i1])
|
||||
continue;
|
||||
if(outCodes0[i]&outCodes0[i1])
|
||||
continue;
|
||||
|
||||
const PxVec3& p0b = vertices0[indices0[i]];
|
||||
const PxVec3& p1b = vertices0[indices0[i1]];
|
||||
|
||||
// gVisualizeLocalLine(p0b, p1b, world0, callback.getManager());
|
||||
|
||||
PxReal dist;
|
||||
PxVec3 p;
|
||||
|
||||
if(EdgeEdgeContactSpecial(v1, plane, p0, p1, localPlane0.n, p0b, p1b, dist, p, _i, _j, coeff))
|
||||
{
|
||||
status = true; // PT: keep this first to avoid an LHS when leaving the function
|
||||
/* p = world0.transform(p);
|
||||
|
||||
//contacts are generated on the edges of polygon 1
|
||||
//we only have to shift the position of polygon 1 if flipNormal is false, because
|
||||
//in this case convex 0 gets passed as polygon 1, and it is convex 0 that was shifted.
|
||||
if (!flipNormal)
|
||||
p += posShift;
|
||||
|
||||
contactBuffer.contact(p, n, -dist + sepShift, polyIndex0, polyIndex1, convexID);*/
|
||||
|
||||
PxContactPoint* PX_RESTRICT ctc = contactBuffer.contact();
|
||||
if(ctc)
|
||||
{
|
||||
ctc->normal = n;
|
||||
ctc->point = world0.transform(p) + (flipNormal ? PxVec3(0.0f) : posShift);
|
||||
ctc->separation = -dist + sepShift;
|
||||
ctc->internalFaceIndex1 = polyIndex1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
69
engine/third_party/physx/source/geomutils/src/contact/GuContactPolygonPolygon.h
vendored
Normal file
69
engine/third_party/physx/source/geomutils/src/contact/GuContactPolygonPolygon.h
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
// 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.
|
||||
|
||||
#ifndef GU_CONTACTPOLYGONPOLYGON_H
|
||||
#define GU_CONTACTPOLYGONPOLYGON_H
|
||||
|
||||
|
||||
#include "foundation/PxMat33.h"
|
||||
#include "foundation/PxMat34.h"
|
||||
#include "common/PxPhysXCommonConfig.h"
|
||||
|
||||
namespace physx
|
||||
{
|
||||
class PxContactBuffer;
|
||||
class PxPlane;
|
||||
|
||||
namespace Cm
|
||||
{
|
||||
class FastVertex2ShapeScaling;
|
||||
}
|
||||
|
||||
namespace Gu
|
||||
{
|
||||
|
||||
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 PxMat34& 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 PxMat34& world1, const PxPlane& localPlane1, //xform of polygon 1, plane of polygon
|
||||
const PxMat33& RotT1,
|
||||
|
||||
const PxVec3& worldSepAxis, //world normal of separating plane - this is the world space normal of polygon0!!
|
||||
const PxMat34& transform0to1, const PxMat34& transform1to0,//transforms between polygons
|
||||
PxU32 polyIndex0, PxU32 polyIndex1, //face indices for contact callback,
|
||||
PxContactBuffer& contactBuffer,
|
||||
bool flipNormal, const PxVec3& posShift, float sepShift
|
||||
); // shape order, post gen shift.
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
494
engine/third_party/physx/source/geomutils/src/contact/GuContactReduction.h
vendored
Normal file
494
engine/third_party/physx/source/geomutils/src/contact/GuContactReduction.h
vendored
Normal file
@@ -0,0 +1,494 @@
|
||||
// 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.
|
||||
|
||||
#ifndef GU_SDF_CONTACT_REDUCTION_H
|
||||
#define GU_SDF_CONTACT_REDUCTION_H
|
||||
|
||||
#include "foundation/PxArray.h"
|
||||
#include "foundation/PxAssert.h"
|
||||
#include "foundation/PxErrors.h"
|
||||
#include "foundation/PxFoundation.h"
|
||||
#include "foundation/PxPreprocessor.h"
|
||||
#include "foundation/PxSort.h"
|
||||
#include "foundation/PxSimpleTypes.h"
|
||||
#include "foundation/PxVec3.h"
|
||||
#include "geomutils/PxContactBuffer.h"
|
||||
#include "geomutils/PxContactPoint.h"
|
||||
#include "PxContact.h"
|
||||
|
||||
namespace physx
|
||||
{
|
||||
namespace Gu
|
||||
{
|
||||
|
||||
const float PATCH_ACCEPT_DOTP = 0.9995f;
|
||||
const float PATCH_REJECT_DOTP = 0.85f;
|
||||
const float PXS_SEPARATION_TOLERANCE = 0.001f;
|
||||
|
||||
// SDF contact reduction on the CPU takes place in three steps:
|
||||
// (1) assign a contact to an existing patch or create a new one
|
||||
// (2) once the patches grow too large, reduce them
|
||||
// (3) order patches by penetration depth and select a diverse set
|
||||
struct TinyContact
|
||||
{
|
||||
TinyContact() : mNormal(0.0f), mSeparation(0.0f), mPoint(0.0f) { }
|
||||
TinyContact(const PxVec3& normal, PxReal separation, const PxVec3& point) : mNormal(normal), mSeparation(separation), mPoint(point) {}
|
||||
PxVec3 mNormal;
|
||||
PxReal mSeparation;
|
||||
PxVec3 mPoint;
|
||||
};
|
||||
|
||||
template <PxU8 TMaxContactsPerPatch>
|
||||
struct TinyContactPatch
|
||||
{
|
||||
TinyContact mContacts[TMaxContactsPerPatch];
|
||||
PxU8 mNbContacts;
|
||||
const TinyContact* begin() const { return mContacts; }
|
||||
const TinyContact* end() const { return mContacts + mNbContacts; }
|
||||
|
||||
TinyContactPatch() : mNbContacts(0) { }
|
||||
};
|
||||
|
||||
PX_FORCE_INLINE static float square(float x) { return x * x; }
|
||||
|
||||
// proto-patch that may need to be reduced
|
||||
template <PxU8 TMaxContactsPerPatch, PxU32 TContactBufSz, bool planarize = true>
|
||||
class BufferedPatch
|
||||
{
|
||||
public:
|
||||
BufferedPatch() {}
|
||||
explicit BufferedPatch(const TinyContact& contact) : mRootNormal(contact.mNormal), mMinSeparation(contact.mSeparation)
|
||||
{
|
||||
addContact(contact);
|
||||
}
|
||||
|
||||
// copy, skipping unused elements
|
||||
BufferedPatch(const BufferedPatch& other) : mRootNormal(other.mRootNormal), mMinSeparation(other.mMinSeparation), mNbContacts(other.mNbContacts)
|
||||
{
|
||||
for (PxU32 i = 0; i < mNbContacts; ++i)
|
||||
{
|
||||
mPointX[i] = other.mPointX[i];
|
||||
mPointY[i] = other.mPointY[i];
|
||||
mPointZ[i] = other.mPointZ[i];
|
||||
|
||||
mNormalX[i] = other.mNormalX[i];
|
||||
mNormalY[i] = other.mNormalY[i];
|
||||
mNormalZ[i] = other.mNormalZ[i];
|
||||
|
||||
mSeparation[i] = other.mSeparation[i];
|
||||
}
|
||||
}
|
||||
|
||||
// add a contact
|
||||
PX_INLINE void addContact(const TinyContact& contact)
|
||||
{
|
||||
PX_ASSERT(mNbContacts < TContactBufSz);
|
||||
mPointX[mNbContacts] = contact.mPoint.x;
|
||||
mPointY[mNbContacts] = contact.mPoint.y;
|
||||
mPointZ[mNbContacts] = contact.mPoint.z;
|
||||
|
||||
mNormalX[mNbContacts] = contact.mNormal.x;
|
||||
mNormalY[mNbContacts] = contact.mNormal.y;
|
||||
mNormalZ[mNbContacts] = contact.mNormal.z;
|
||||
|
||||
mSeparation[mNbContacts] = contact.mSeparation;
|
||||
|
||||
// in case of <= 4 contacts per patch, the deepest one will not usually be conserved
|
||||
if (contact.mSeparation < mMinSeparation)
|
||||
mMinSeparation = contact.mSeparation;
|
||||
|
||||
++mNbContacts;
|
||||
if (mNbContacts == TContactBufSz)
|
||||
reduce();
|
||||
}
|
||||
|
||||
PX_INLINE void asTinyContactPatch(TinyContactPatch<TMaxContactsPerPatch>& patch)
|
||||
{
|
||||
reduce();
|
||||
patch.mNbContacts = static_cast<PxU8>(mNbContacts);
|
||||
|
||||
PX_ASSERT(mNbContacts <= TMaxContactsPerPatch);
|
||||
TinyContact* contacts = patch.mContacts;
|
||||
for (PxU32 contactIdx = 0; contactIdx < mNbContacts; ++contactIdx, ++contacts)
|
||||
{
|
||||
contacts->mPoint = PxVec3(mPointX[contactIdx], mPointY[contactIdx], mPointZ[contactIdx]);
|
||||
contacts->mNormal = PxVec3(mNormalX[contactIdx], mNormalY[contactIdx], mNormalZ[contactIdx]);
|
||||
contacts->mSeparation = mSeparation[contactIdx];
|
||||
}
|
||||
}
|
||||
|
||||
// store reduced contacts into `contacts`, returning the number of contacts
|
||||
PX_INLINE PxU32 getContacts(TinyContact* contacts)
|
||||
{
|
||||
reduce();
|
||||
PX_ASSERT(mNbContacts <= TMaxContactsPerPatch);
|
||||
for (PxU32 contactIdx = 0; contactIdx < mNbContacts; ++contactIdx, ++contacts)
|
||||
{
|
||||
contacts->mPoint = PxVec3(mPointX[contactIdx], mPointY[contactIdx], mPointZ[contactIdx]);
|
||||
contacts->mNormal = PxVec3(mNormalX[contactIdx], mNormalY[contactIdx], mNormalZ[contactIdx]);
|
||||
contacts->mSeparation = mSeparation[contactIdx];
|
||||
}
|
||||
return mNbContacts;
|
||||
}
|
||||
|
||||
PX_INLINE PxVec3 getPoint(PxU32 index) const
|
||||
{
|
||||
return PxVec3(mPointX[index], mPointY[index], mPointZ[index]);
|
||||
}
|
||||
|
||||
// reduce the contacts to `TMaxContactsPerPatch`.
|
||||
PX_INLINE void reduce()
|
||||
{
|
||||
if (mNbContacts <= TMaxContactsPerPatch)
|
||||
return;
|
||||
// P0: most extreme point
|
||||
float maxDistOrigSq = -1;
|
||||
PxU32 P0Idx = 0;
|
||||
PxVec3 P0;
|
||||
for (PxU32 i = 0; i < mNbContacts; ++i)
|
||||
{
|
||||
const PxVec3 p = getPoint(i);
|
||||
float distOrigSq = p.magnitudeSquared();
|
||||
if (planarize)
|
||||
distOrigSq -= square(p.dot(mRootNormal));
|
||||
if (distOrigSq > maxDistOrigSq)
|
||||
{
|
||||
maxDistOrigSq = distOrigSq;
|
||||
P0Idx = i;
|
||||
P0 = p;
|
||||
}
|
||||
}
|
||||
// P1: most distant point from P0
|
||||
float maxDistP0Sq = -1;
|
||||
PxU32 P1Idx = 0;
|
||||
PxVec3 P1;
|
||||
for (PxU32 i = 0; i < mNbContacts; ++i)
|
||||
{
|
||||
const PxVec3 p = getPoint(i);
|
||||
const PxVec3 v = p - P0;
|
||||
float distP0Sq = v.magnitudeSquared();
|
||||
if (planarize)
|
||||
distP0Sq -= square(v.dot(mRootNormal));
|
||||
if (distP0Sq > maxDistP0Sq)
|
||||
{
|
||||
maxDistP0Sq = distP0Sq;
|
||||
P1Idx = i;
|
||||
P1 = p;
|
||||
}
|
||||
}
|
||||
|
||||
if (P0Idx == P1Idx) // P0 == P1 => all points equal. keep only the first
|
||||
{
|
||||
// TODO(CA): account for differences in penetration? should not occur
|
||||
mNbContacts = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
PxVec3 P2, P3;
|
||||
// P2 & P3: most distant from P0-P1 segment in both directions
|
||||
const PxVec3 segNormal = (P0 - P1).cross(mRootNormal);
|
||||
float maxDistPos = -PX_MAX_REAL, maxDistNeg = PX_MAX_REAL;
|
||||
PxU32 P2Idx = PX_MAX_U32, P3Idx = PX_MAX_U32;
|
||||
for (PxU32 i = 0; i < mNbContacts; ++i)
|
||||
{
|
||||
if (i == P0Idx || i == P1Idx) // ensure that we have contacts distinct from P0/P1
|
||||
continue;
|
||||
const PxVec3 p = getPoint(i);
|
||||
const PxReal dist = (p - P0).dot(segNormal);
|
||||
if (dist > maxDistPos)
|
||||
{
|
||||
maxDistPos = dist;
|
||||
P2Idx = i;
|
||||
P2 = p;
|
||||
}
|
||||
if (dist <= maxDistNeg)
|
||||
{
|
||||
maxDistNeg = dist;
|
||||
P3Idx = i;
|
||||
P3 = p;
|
||||
}
|
||||
}
|
||||
// cluster the points
|
||||
PxReal anchorSeparation[TMaxContactsPerPatch];
|
||||
PxU32 anchorDeepestIdx[TMaxContactsPerPatch];
|
||||
const PxU32 anchorIndices[4] = {P0Idx, P1Idx, P2Idx, P3Idx};
|
||||
const PxVec3 anchorPoints[4] = {P0, P1, P2, P3};
|
||||
for(PxU32 i = 0; i < 4; ++i)
|
||||
{
|
||||
const PxU32 index = anchorIndices[i];
|
||||
anchorDeepestIdx[i] = index;
|
||||
anchorSeparation[i] = mSeparation[index] - PXS_SEPARATION_TOLERANCE;
|
||||
}
|
||||
for (PxU32 i = 0; i < mNbContacts; ++i)
|
||||
{
|
||||
const PxVec3 p = getPoint(i);
|
||||
PxReal dMin = PX_MAX_REAL;
|
||||
PxU32 anchorIdx = 0;
|
||||
for(PxU32 c = 0; c < 4; ++c) // assign to anchors
|
||||
{
|
||||
const PxReal dist = (anchorPoints[c] - p).magnitudeSquared();
|
||||
if(dist < dMin)
|
||||
{
|
||||
dMin = dist;
|
||||
anchorIdx = c;
|
||||
}
|
||||
}
|
||||
if(mSeparation[i] < anchorSeparation[anchorIdx]) // pick deepest
|
||||
{
|
||||
anchorDeepestIdx[anchorIdx] = i;
|
||||
anchorSeparation[anchorIdx] = mSeparation[i];
|
||||
}
|
||||
}
|
||||
PxU32 chosenPoints[TMaxContactsPerPatch] =
|
||||
{
|
||||
anchorDeepestIdx[0], anchorDeepestIdx[1], anchorDeepestIdx[2], anchorDeepestIdx[3]
|
||||
};
|
||||
|
||||
PxReal chosenSeparations[TMaxContactsPerPatch-4]; // only relevant for extra points
|
||||
for (PxU32 i = 0; i < TMaxContactsPerPatch-4; ++i)
|
||||
chosenSeparations[i] = PX_MAX_REAL;
|
||||
|
||||
for (PxU32 i = 0; i < mNbContacts; ++i)
|
||||
{
|
||||
bool alreadyChosen = false;
|
||||
for (PxU32 j = 0; j < 4; ++j)
|
||||
{
|
||||
if (i == chosenPoints[j])
|
||||
{
|
||||
alreadyChosen = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(alreadyChosen)
|
||||
continue;
|
||||
PxReal sep = mSeparation[i];
|
||||
for (PxU32 slotIdx = 4; slotIdx < TMaxContactsPerPatch; ++slotIdx)
|
||||
{
|
||||
if (sep < chosenSeparations[slotIdx-4])
|
||||
{
|
||||
// drop out largest contact
|
||||
for (PxU32 k = TMaxContactsPerPatch-1; k > slotIdx; --k)
|
||||
{
|
||||
chosenSeparations[k-4] = chosenSeparations[k-1-4];
|
||||
chosenPoints[k] = chosenPoints[k-1];
|
||||
}
|
||||
// assign to this slot
|
||||
chosenSeparations[slotIdx-4] = sep;
|
||||
chosenPoints[slotIdx] = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
float pointXNew[TMaxContactsPerPatch],
|
||||
pointYNew[TMaxContactsPerPatch],
|
||||
pointZNew[TMaxContactsPerPatch],
|
||||
normalXNew[TMaxContactsPerPatch],
|
||||
normalYNew[TMaxContactsPerPatch],
|
||||
normalZNew[TMaxContactsPerPatch],
|
||||
separationNew[TMaxContactsPerPatch];
|
||||
|
||||
for (PxU32 dst = 0; dst < TMaxContactsPerPatch; ++dst)
|
||||
{
|
||||
const PxU32 src = chosenPoints[dst];
|
||||
pointXNew[dst] = mPointX[src];
|
||||
pointYNew[dst] = mPointY[src];
|
||||
pointZNew[dst] = mPointZ[src];
|
||||
|
||||
normalXNew[dst] = mNormalX[src];
|
||||
normalYNew[dst] = mNormalY[src];
|
||||
normalZNew[dst] = mNormalZ[src];
|
||||
|
||||
separationNew[dst] = mSeparation[src];
|
||||
}
|
||||
for (PxU32 i = 0; i < TMaxContactsPerPatch; ++i)
|
||||
{
|
||||
mPointX[i] = pointXNew[i];
|
||||
mPointY[i] = pointYNew[i];
|
||||
mPointZ[i] = pointZNew[i];
|
||||
|
||||
mNormalX[i] = normalXNew[i];
|
||||
mNormalY[i] = normalYNew[i];
|
||||
mNormalZ[i] = normalZNew[i];
|
||||
|
||||
mSeparation[i] = separationNew[i];
|
||||
}
|
||||
mNbContacts = TMaxContactsPerPatch;
|
||||
}
|
||||
protected:
|
||||
|
||||
float mNormalX[TContactBufSz];
|
||||
float mNormalY[TContactBufSz];
|
||||
float mNormalZ[TContactBufSz];
|
||||
|
||||
float mSeparation[TContactBufSz];
|
||||
|
||||
float mPointX[TContactBufSz];
|
||||
float mPointY[TContactBufSz];
|
||||
float mPointZ[TContactBufSz];
|
||||
|
||||
public:
|
||||
PxVec3 mRootNormal;
|
||||
PxReal mMinSeparation = PX_MAX_REAL;
|
||||
protected:
|
||||
PxU32 mNbContacts = 0;
|
||||
PxU32 pad0;
|
||||
PxU32 pad1;
|
||||
PxU32 pad2;
|
||||
|
||||
PX_COMPILE_TIME_ASSERT((TContactBufSz * sizeof(float)) % 32 == 0);
|
||||
PX_COMPILE_TIME_ASSERT(TContactBufSz > TMaxContactsPerPatch);
|
||||
};
|
||||
|
||||
template <PxU8 TMaxContactsPerPatch, PxU32 TMaxPatches, PxU32 TPatchBufSz>
|
||||
class SDFContactReduction
|
||||
{
|
||||
public:
|
||||
using Patch = BufferedPatch<TMaxContactsPerPatch, TPatchBufSz>;
|
||||
using TinyPatch = TinyContactPatch<TMaxContactsPerPatch>;
|
||||
|
||||
// attempt to add a contact to one of the patches, adding a new one if necessary
|
||||
// return false if the contact was dropped
|
||||
PX_INLINE bool addContact(const TinyContact& contact)
|
||||
{
|
||||
++mNbContactsConsumed;
|
||||
for (Patch& patch: mPatchesBuffer)
|
||||
{
|
||||
const PxReal dot = patch.mRootNormal.dot(contact.mNormal);
|
||||
if (dot >= PATCH_ACCEPT_DOTP)
|
||||
return patch.addContact(contact), true;
|
||||
}
|
||||
mPatchesBuffer.pushBack(Patch(contact));
|
||||
// TODO(CA): make this more robust, taking into account the max number of surviving patches
|
||||
if (mPatchesBuffer.size() > mPatchCountLimit)
|
||||
cullPatches(1-(1-PATCH_REJECT_DOTP)/1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
struct PatchPenetrationPredicate
|
||||
{
|
||||
bool operator()(const Patch* a, const Patch* b) const { return a->mMinSeparation < b->mMinSeparation; }
|
||||
};
|
||||
|
||||
// cull the existing patches
|
||||
PX_INLINE void cullPatches(float rejectDotP)
|
||||
{
|
||||
PxArray<Patch*> patchesSorted;
|
||||
patchesSorted.reserve(mPatchesBuffer.size());
|
||||
for (Patch& patch: mPatchesBuffer)
|
||||
patchesSorted.pushBack(&patch);
|
||||
|
||||
PxSort(patchesSorted.begin(), mPatchesBuffer.size(), PatchPenetrationPredicate());
|
||||
|
||||
// drop patches that have > nbAllowedNeighbors neighbors that were selected
|
||||
// allowed neighbors = 0 seems to work best, still, 3 is worst, and >> 10 deactivates
|
||||
// isotropy
|
||||
const PxU32 nbAllowedNeighbors = 0;
|
||||
|
||||
PxArray<PxVec3> patchNormals;
|
||||
// Geometrical upper bound for the number of patches based on the area "reserved" for each patch
|
||||
const PxU32 nbPatchesBound = static_cast<PxU32>(PxFloor(1.0f/square(PxSin(0.25f * PxAcos(rejectDotP)))));
|
||||
patchNormals.reserve(PxMin(mPatchesBuffer.size(), nbPatchesBound));
|
||||
|
||||
mPatchesBufferTmp.clear();
|
||||
for (Patch* patch: patchesSorted)
|
||||
{
|
||||
PxU32 neighborPatches = 0;
|
||||
for (const PxVec3& rootNormal: patchNormals)
|
||||
if (rootNormal.dot(patch->mRootNormal) > rejectDotP)
|
||||
if (++neighborPatches > nbAllowedNeighbors)
|
||||
break;
|
||||
if (neighborPatches > nbAllowedNeighbors)
|
||||
continue;
|
||||
|
||||
// patch->reduce();
|
||||
patchNormals.pushBack(patch->mRootNormal);
|
||||
mPatchesBufferTmp.pushBack(*patch);
|
||||
}
|
||||
PxSwap(mPatchesBuffer, mPatchesBufferTmp);
|
||||
mPatchesBufferTmp.clear();
|
||||
}
|
||||
|
||||
// reduce each buffered patch, sort them, and create contacts
|
||||
PX_INLINE void flushContacts()
|
||||
{
|
||||
cullPatches(PATCH_REJECT_DOTP);
|
||||
for (Patch& patch: mPatchesBuffer)
|
||||
{
|
||||
TinyContactPatch<TMaxContactsPerPatch>& finalPatch = mPatchesFinal.pushBack({});
|
||||
patch.getContacts(finalPatch.mContacts);
|
||||
finalPatch.mNbContacts = patch.mNbContacts;
|
||||
}
|
||||
}
|
||||
|
||||
PX_INLINE PxU32 flushToContactBuffer(PxContactBuffer& contactBuffer)
|
||||
{
|
||||
cullPatches(PATCH_REJECT_DOTP);
|
||||
PxU32 nbContactsKept = 0;
|
||||
for (Patch& patch: mPatchesBuffer)
|
||||
{
|
||||
TinyPatch finalPatch;
|
||||
patch.asTinyContactPatch(finalPatch);
|
||||
for (PxU32 i = 0; i < finalPatch.mNbContacts; ++i)
|
||||
{
|
||||
const TinyContact& contact = finalPatch.mContacts[i];
|
||||
PxContactPoint* c = contactBuffer.contact();
|
||||
if (c == NULL)
|
||||
{
|
||||
#if PX_CHECKED
|
||||
PxGetFoundation().error(
|
||||
physx::PxErrorCode::eDEBUG_WARNING,
|
||||
PX_FL,
|
||||
"Dropping contacts in contact reduction due to full contact buffer.");
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
++nbContactsKept;
|
||||
c->normal = contact.mNormal;
|
||||
c->point = contact.mPoint;
|
||||
c->separation = contact.mSeparation;
|
||||
c->internalFaceIndex1 = PXC_CONTACT_NO_FACE_INDEX;
|
||||
}
|
||||
}
|
||||
|
||||
return nbContactsKept;
|
||||
}
|
||||
|
||||
protected:
|
||||
static const PxU32 mPatchCountLimit = 4000000/sizeof(Patch); // 4 MB limit on patch size
|
||||
PxArray<Patch> mPatchesBuffer; // store patches up to MaxPatches
|
||||
PxArray<Patch> mPatchesBufferTmp;
|
||||
PxArray<TinyPatch> mPatchesFinal; // store culled patches
|
||||
|
||||
PxU32 mNbContactsConsumed = 0;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
173
engine/third_party/physx/source/geomutils/src/contact/GuContactSphereBox.cpp
vendored
Normal file
173
engine/third_party/physx/source/geomutils/src/contact/GuContactSphereBox.cpp
vendored
Normal file
@@ -0,0 +1,173 @@
|
||||
// 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 "GuContactMethodImpl.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 PxTransform32& 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;
|
||||
}
|
||||
}
|
||||
|
||||
bool Gu::contactSphereBox(GU_CONTACT_METHOD_ARGS)
|
||||
{
|
||||
PX_UNUSED(renderOutput);
|
||||
PX_UNUSED(cache);
|
||||
|
||||
const PxSphereGeometry& sphereGeom = checkedCast<PxSphereGeometry>(shape0);
|
||||
const PxBoxGeometry& boxGeom = checkedCast<PxBoxGeometry>(shape1);
|
||||
|
||||
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;
|
||||
}
|
||||
76
engine/third_party/physx/source/geomutils/src/contact/GuContactSphereCapsule.cpp
vendored
Normal file
76
engine/third_party/physx/source/geomutils/src/contact/GuContactSphereCapsule.cpp
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
// 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 "GuDistancePointSegment.h"
|
||||
#include "GuContactMethodImpl.h"
|
||||
#include "GuInternal.h"
|
||||
|
||||
using namespace physx;
|
||||
|
||||
bool Gu::contactSphereCapsule(GU_CONTACT_METHOD_ARGS)
|
||||
{
|
||||
PX_UNUSED(renderOutput);
|
||||
PX_UNUSED(cache);
|
||||
|
||||
const PxSphereGeometry& sphereGeom = checkedCast<PxSphereGeometry>(shape0);
|
||||
const PxCapsuleGeometry& capsuleGeom = checkedCast<PxCapsuleGeometry>(shape1);
|
||||
|
||||
// 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;
|
||||
}
|
||||
621
engine/third_party/physx/source/geomutils/src/contact/GuContactSphereMesh.cpp
vendored
Normal file
621
engine/third_party/physx/source/geomutils/src/contact/GuContactSphereMesh.cpp
vendored
Normal file
@@ -0,0 +1,621 @@
|
||||
// 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;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
62
engine/third_party/physx/source/geomutils/src/contact/GuContactSpherePlane.cpp
vendored
Normal file
62
engine/third_party/physx/source/geomutils/src/contact/GuContactSpherePlane.cpp
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
// 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 "GuContactMethodImpl.h"
|
||||
|
||||
using namespace physx;
|
||||
|
||||
bool Gu::contactSpherePlane(GU_CONTACT_METHOD_ARGS)
|
||||
{
|
||||
PX_UNUSED(renderOutput);
|
||||
PX_UNUSED(cache);
|
||||
PX_UNUSED(shape1);
|
||||
|
||||
// Get actual shape data
|
||||
const PxSphereGeometry& shapeSphere = checkedCast<PxSphereGeometry>(shape0);
|
||||
//const PxPlaneGeometry& shapePlane = checkedCast<PxPlaneGeometry>(shape1);
|
||||
|
||||
//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;
|
||||
}
|
||||
62
engine/third_party/physx/source/geomutils/src/contact/GuContactSphereSphere.cpp
vendored
Normal file
62
engine/third_party/physx/source/geomutils/src/contact/GuContactSphereSphere.cpp
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
// 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 "GuContactMethodImpl.h"
|
||||
|
||||
using namespace physx;
|
||||
|
||||
bool Gu::contactSphereSphere(GU_CONTACT_METHOD_ARGS)
|
||||
{
|
||||
PX_UNUSED(renderOutput);
|
||||
PX_UNUSED(cache);
|
||||
|
||||
const PxSphereGeometry& sphereGeom0 = checkedCast<PxSphereGeometry>(shape0);
|
||||
const PxSphereGeometry& sphereGeom1 = checkedCast<PxSphereGeometry>(shape1);
|
||||
|
||||
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;
|
||||
}
|
||||
127
engine/third_party/physx/source/geomutils/src/contact/GuFeatureCode.cpp
vendored
Normal file
127
engine/third_party/physx/source/geomutils/src/contact/GuFeatureCode.cpp
vendored
Normal file
@@ -0,0 +1,127 @@
|
||||
// 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 "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;
|
||||
}
|
||||
|
||||
53
engine/third_party/physx/source/geomutils/src/contact/GuFeatureCode.h
vendored
Normal file
53
engine/third_party/physx/source/geomutils/src/contact/GuFeatureCode.h
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
// 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.
|
||||
|
||||
#ifndef GU_FEATURE_CODE_H
|
||||
#define GU_FEATURE_CODE_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
|
||||
Reference in New Issue
Block a user