225 lines
8.6 KiB
Plaintext
225 lines
8.6 KiB
Plaintext
// 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 __PARTICLE_COLLISION_CUH__
|
|
#define __PARTICLE_COLLISION_CUH__
|
|
|
|
#include "foundation/PxVec3.h"
|
|
#include "foundation/PxTransform.h"
|
|
#include "sphereCollision.cuh"
|
|
#include "deformableCollision.cuh"
|
|
#include "GuConvexSupport.h"
|
|
/**
|
|
* Retuns distance and normal information for a particle-box contact.
|
|
* If maxDist < PX_MAX_F32, then no result is provided if the distance is larger than maxDist.
|
|
*/
|
|
__device__ __forceinline__ static
|
|
bool contactPointBox(PxVec3& normal, PxReal& dist,
|
|
const PxVec3& pointPos, const PxTransform& boxToWorld, const PxVec3& halfBoxExtents, const PxReal maxDist)
|
|
{
|
|
PxVec3 relPointPos = boxToWorld.transformInv(pointPos);
|
|
|
|
const PxVec3 closestPos(
|
|
PxClamp(relPointPos.x, -halfBoxExtents.x, halfBoxExtents.x),
|
|
PxClamp(relPointPos.y, -halfBoxExtents.y, halfBoxExtents.y),
|
|
PxClamp(relPointPos.z, -halfBoxExtents.z, halfBoxExtents.z)
|
|
);
|
|
|
|
if (maxDist < PX_MAX_F32)
|
|
{
|
|
PxReal maxDistSq = maxDist*maxDist;
|
|
PxReal distSq = (relPointPos - closestPos).magnitudeSquared();
|
|
if (distSq > maxDistSq)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
//check whether the point is inside the aabb
|
|
const bool bX = halfBoxExtents.x >= PxAbs(relPointPos.x);
|
|
const bool bY = halfBoxExtents.y >= PxAbs(relPointPos.y);
|
|
const bool bZ = halfBoxExtents.z >= PxAbs(relPointPos.z);
|
|
|
|
const bool isInside = bX && bY && bZ;
|
|
if (isInside)
|
|
{
|
|
//dist from embedded point to box surface along 3 dimensions.
|
|
const PxVec3 distToSurface = halfBoxExtents - closestPos.abs();
|
|
|
|
//assume z is the smallest element of the distToSurface
|
|
normal = closestPos.z >= 0.f ? PxVec3(0.f, 0.f, 1.f) : PxVec3(0.f, 0.f, -1.f);
|
|
dist = -distToSurface.z;
|
|
//find smallest element of distToSurface
|
|
if (distToSurface.x <= distToSurface.y && distToSurface.x <= distToSurface.z)
|
|
{
|
|
normal = closestPos.x >= 0.f ? PxVec3(1.f, 0.f, 0.f) : PxVec3(-1.0f, 0.f, 0.f);
|
|
dist = -distToSurface.x;
|
|
}
|
|
else if (distToSurface.y <= distToSurface.z)
|
|
{
|
|
normal = closestPos.y >= 0.f ? PxVec3(0.f, 1.f, 0.f) : PxVec3(0.f, -1.f, 0.f);
|
|
dist = -distToSurface.y;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
normal = relPointPos - closestPos;
|
|
dist = normal.normalizeFast();
|
|
}
|
|
normal = boxToWorld.rotate(normal);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Retuns distance and normal information for a particle-sphere contact.
|
|
* If maxDist < PX_MAX_F32, then no result is provided if the distance is larger than maxDist.
|
|
*/
|
|
__device__ __forceinline__ static
|
|
bool contactPointSphere(PxVec3& normal, PxReal& dist,
|
|
const PxVec3& pointPos, const PxVec3& spherePos, const PxReal sphereRadius, const PxReal maxDist)
|
|
{
|
|
PxVec3 relPointPos = pointPos - spherePos;
|
|
const PxReal distCenterSq = relPointPos.magnitudeSquared();
|
|
if (maxDist < PX_MAX_F32)
|
|
{
|
|
const PxReal maxDistCenter = maxDist + sphereRadius;
|
|
const PxReal maxDistCenterSq = maxDistCenter*maxDistCenter;
|
|
if (distCenterSq > maxDistCenterSq)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
const PxReal distCenter = PxSqrt(distCenterSq);
|
|
normal = (distCenter > PX_EPS_REAL) ? relPointPos * (1.0f / distCenter) : PxVec3(1.0f, 0.0f, 0.0f);
|
|
dist = distCenter - sphereRadius;
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Retuns distance and normal information for a point-capsule contact.
|
|
* If maxDist < PX_MAX_F32, then no result is provided if the distance is larger than maxDist.
|
|
*/
|
|
__device__ __forceinline__ static
|
|
bool contactPointCapsule(PxVec3& normal, PxReal& dist,
|
|
const PxVec3& pointPos, const PxVec3& capsulePos, const PxVec3& capsuleDir, const PxReal capsuleRadius, const PxReal capsuleHalfHeight, const PxReal maxDist)
|
|
{
|
|
const PxVec3 capsuleAxisVec = capsuleDir * capsuleHalfHeight;
|
|
const PxVec3 relPointPos = pointPos - capsulePos;
|
|
PxReal t;
|
|
const PxReal distSegmentSq = distancePointSegmentSquared(-capsuleAxisVec, capsuleAxisVec, relPointPos, t);
|
|
if (maxDist < PX_MAX_F32)
|
|
{
|
|
const PxReal maxDistSegment = maxDist + capsuleRadius;
|
|
const PxReal maxDistSegmentSq = maxDistSegment * maxDistSegment;
|
|
if (distSegmentSq > maxDistSegmentSq)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
const PxVec3 distSegmentVec = relPointPos - capsuleAxisVec * (2.0f * t - 1.0f);
|
|
const PxReal distSegment = PxSqrt(distSegmentSq);
|
|
normal = (distSegment > PX_EPS_REAL) ? (distSegmentVec * (1.0f / distSegment)) : PxVec3(1.0f, 0.0f, 0.0f);
|
|
dist = distSegment - capsuleRadius;
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Combined speculative particle triangle cd. Returns normal and distance.
|
|
* If CCD is enabled two tests are performed. The first one decides if the triangle
|
|
* is overlapping with the contact volume (sphere), which is typically located at a
|
|
* predicted position of the particle, covering it's whole predicted path. The second
|
|
* test then establishes the correct distance and normal with respect to the actual particle
|
|
* position.
|
|
* If CCD is disabled, particlePos == cVolumePos, and cVolumeRadius is the standard contact
|
|
* distance.
|
|
*/
|
|
__device__ __forceinline__ static
|
|
bool particleTriangleTest(PxVec3& normal, PxReal& distance,
|
|
const PxVec3& particlePos, const PxVec3& cVolumePos, const PxReal cVolumeRadius,
|
|
const PxVec3& triV0, const PxVec3& triV1, const PxVec3& triV2, const bool enableCCD)
|
|
{
|
|
const PxVec3 triNormal = ((triV1 - triV0).cross(triV2 - triV0)).getNormalized();
|
|
const PxReal cVolumeRadiusSq = cVolumeRadius * cVolumeRadius;
|
|
|
|
//contact volume test
|
|
const PxReal cVolumePosPlaneDist = (cVolumePos - triV0).dot(triNormal);
|
|
PxVec3 closestPos;
|
|
if (cVolumePosPlaneDist < cVolumeRadius)
|
|
{
|
|
PxReal distSq = distancePointTriangleSquared(cVolumePos, triV0, triV1, triV2, closestPos);
|
|
if (distSq <= cVolumeRadiusSq)
|
|
{
|
|
//intersection test
|
|
//back face culling with respect to current particle position
|
|
const PxReal particlePosPlaneDist = (particlePos - triV0).dot(triNormal);
|
|
if (particlePosPlaneDist > 0.0f)
|
|
{
|
|
if (enableCCD)
|
|
{
|
|
//in case of CCD we replace the closest and distance from the contact volume with
|
|
//the corresponding from the current particle position
|
|
distSq = distancePointTriangleSquared(particlePos, triV0, triV1, triV2, closestPos);
|
|
}
|
|
//assumes particlePos == cVolumePos if enableCCD == false.
|
|
PxVec3 distVec = particlePos - closestPos;
|
|
normal = (distSq > 0.0f) ? distVec*PxRecipSqrt(distSq) : triNormal;
|
|
distance = PxSqrt(distSq);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Retuns distance and normal information for a particle-convexCore contact.
|
|
* If maxDist < PX_MAX_F32, then no result is provided if the distance is larger than maxDist.
|
|
*/
|
|
__device__ __forceinline__ static
|
|
bool particleConvexCore(PxVec3& normal, PxReal& dist, const PxVec3& particlePos,
|
|
const Gu::ConvexShape& convex, const PxReal maxDist)
|
|
{
|
|
Gu::ConvexShape particle;
|
|
particle.coreType = Gu::ConvexCore::Type::ePOINT;
|
|
particle.pose = PxTransform(particlePos);
|
|
particle.margin = 0;
|
|
|
|
PxVec3 point0, point1;
|
|
dist = Gu::RefGjkEpa::computeGjkDistance(particle, convex, particle.pose, convex.pose,
|
|
convex.margin + particle.margin + maxDist, point0, point1, normal);
|
|
|
|
if (dist < FLT_EPSILON)
|
|
dist = Gu::RefGjkEpa::computeEpaDepth(particle, convex, particle.pose, convex.pose,
|
|
point0, point1, normal);
|
|
|
|
if (dist > maxDist)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
#endif // __PARTICLE_COLLISION_CUH__ |