Files
XCEngine/engine/third_party/physx/source/gpunarrowphase/src/CUDA/particleSystemHFMidPhaseCG.cu

360 lines
14 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.
#include "foundation/PxBounds3.h"
#include "foundation/PxSimpleTypes.h"
#include "foundation/PxTransform.h"
#include "foundation/PxVec3.h"
#include "geometry/PxHeightFieldSample.h"
#include "PxNodeIndex.h"
#include "PxgContactManager.h"
#include "PxgConvexConvexShape.h"
#include "PxgParticleSystem.h"
#include "PxgParticleSystemCoreKernelIndices.h"
#include "PxsTransformCache.h"
#include <vector_types.h>
#include "PxgCommonDefines.h"
#include "heightfieldUtil.cuh"
#include "dataReadWriteHelper.cuh"
#include "gridCal.cuh"
#include "particleCollision.cuh"
#include "assert.h"
using namespace physx;
extern "C" __host__ void initNarrowphaseKernels12() {}
PX_ALIGN_PREFIX(16)
struct PsHeightfieldScratch
{
PxTransform heightfieldTransform;
PxgShape heightfieldShape;
PxgParticleContactInfo* PX_RESTRICT oneWayContactInfos;
PxNodeIndex* PX_RESTRICT oneWayContactNodeIndices;
PxU32* PX_RESTRICT oneWayContactCounts;
float4* currentPositions;
float4* predictedPositions;
PxNodeIndex rigidId; //heightfield
PxU32 transformCacheRef; //heightfield
PxU32 numParticles;
PxU32 particleSystemId;
PxReal contactDist;
PxReal restDist;
bool enableCCD;
}PX_ALIGN_SUFFIX(16);
__device__ static inline void psHeightfieldMidphaseCollision(
PsHeightfieldScratch* sScratch,
float4* PX_RESTRICT sPerParticleTempContacts,
const PxU32 numParticles,
const PxU32 particleIndex,
const bool isDiffuse,
const bool isTGS,
PxgParticleContactWriter& writer
)
{
const PxReal contactDist = sScratch->contactDist;
const PxReal restDist = sScratch->restDist;
PxVec3 currentPos = PxLoad3(sScratch->currentPositions[particleIndex]);
PxVec3 cVolumePos;
PxReal cVolumeRadius;
if (sScratch->enableCCD)
{
PxVec3 predictedPos = PxLoad3(sScratch->predictedPositions[particleIndex]);
cVolumeRadius = getParticleSpeculativeContactVolume(cVolumePos,
currentPos, predictedPos, contactDist, isDiffuse, isTGS);
}
else
{
cVolumePos = currentPos;
cVolumeRadius = contactDist;
}
const PxVec3 cVolumeRadius3(cVolumeRadius);
const PxVec3 min = cVolumePos - cVolumeRadius3;
const PxVec3 max = cVolumePos + cVolumeRadius3;
PxBounds3 worldBound(min, max);
PxTransform heightfieldTransform = sScratch->heightfieldTransform;
PxgShape heightfieldShape = sScratch->heightfieldShape;
const PxReal oneOverHeightScale = 1.f / heightfieldShape.scale.scale.y;
const PxReal oneOverRowScale = 1.f / PxAbs(heightfieldShape.scale.scale.x);
const PxReal oneOverlColScale = 1.f / PxAbs(heightfieldShape.scale.scale.z);
//bound is in world space, we need to transform the bound to the local space of height field
PxBounds3 localBound = PxBounds3::transformFast(heightfieldTransform.getInverse(), worldBound);
localBound.minimum.x *= oneOverRowScale;
localBound.minimum.y *= oneOverHeightScale;
localBound.minimum.z *= oneOverlColScale;
localBound.maximum.x *= oneOverRowScale;
localBound.maximum.y *= oneOverHeightScale;
localBound.maximum.z *= oneOverlColScale;
//row scale
if (heightfieldShape.scale.scale.x < 0.f)
{
//swap min and max row scale
const PxReal temp = localBound.minimum.x;
localBound.minimum.x = localBound.maximum.x;
localBound.maximum.x = temp;
}
//col scale
if (heightfieldShape.scale.scale.z < 0.f)
{
PxReal swap = localBound.minimum.z;
localBound.minimum.z = localBound.maximum.z;
localBound.maximum.z = swap;
}
PxU32* heightfieldData = reinterpret_cast<PxU32*>(heightfieldShape.hullOrMeshPtr);
const PxU32 nbRows = heightfieldData[0];
const PxU32 nbCols = heightfieldData[1];
PxHeightFieldSample* samples = reinterpret_cast<PxHeightFieldSample*>(&heightfieldData[2]);
if ((localBound.minimum.x > nbRows - 1) || (localBound.minimum.z > nbCols - 1)
|| (localBound.maximum.x < 0) || (localBound.maximum.z < 0))
{
return;
}
PxU32 minRow = getMinRow(localBound.minimum.x, nbRows);
PxU32 maxRow = getMaxRow(localBound.maximum.x, nbRows);
PxU32 minColumn = getMinColumn(localBound.minimum.z, nbCols);
PxU32 maxColumn = getMaxColumn(localBound.maximum.z, nbCols);
if ((2 * (maxColumn - minColumn) * (maxRow - minRow)) == 0)
{
return;
}
const PxVec3 cVolumePosShapeSpace = heightfieldTransform.transformInv(cVolumePos);
const PxVec3 particlePosShapeSpace = heightfieldTransform.transformInv(currentPos);
const PxReal miny = localBound.minimum.y;
const PxReal maxy = localBound.maximum.y;
const PxU32 columnSpan = maxColumn - minColumn;
//How many static contacts this particle already has
PxU32 contactCounts = 0;
//we have two materials corresponding to one vertexIndex, so each thread will deal with one of the materials
const PxU32 totalNumProcessed = (maxRow - minRow) * columnSpan * 2;
for (PxU32 i = 0; i < totalNumProcessed; ++i)
{
PxU32 triangleIdx = 0xFFffFFff;
const PxU32 index = i / 2;
const PxU32 vertexIndex = (minRow + index / columnSpan) * nbCols + (minColumn + index % columnSpan);
assert(isValidVertex(vertexIndex, nbRows, nbCols));
PxReal h0 = getHeight(vertexIndex, samples);
PxReal h1 = getHeight(vertexIndex + 1, samples);
PxReal h2 = getHeight(vertexIndex + nbCols, samples);
PxReal h3 = getHeight(vertexIndex + nbCols + 1, samples);
const bool con0 = maxy < h0 && maxy < h1 && maxy < h2 && maxy < h3;
const bool con1 = miny > h0 && miny > h1 && miny > h2 && miny > h3;
if (!(con0 || con1))
{
const PxHeightFieldSample& sample = getSample(vertexIndex, samples);
const bool isMaterial1 = (i & 1) ? 1 : 0;
PxU32 material = isMaterial1 ? sample.materialIndex1 : sample.materialIndex0;
if (material != PxHeightFieldMaterial::eHOLE)
{
triangleIdx = isMaterial1 ? ((vertexIndex << 1) + 1) : (vertexIndex << 1);
PxVec3 triV0, triV1, triV2;
getTriangle(triV0, triV1, triV2, NULL, triangleIdx, heightfieldShape.scale, nbRows, nbCols, samples);
PxVec3 normalShapeSpace;
PxReal distance;
bool intersect = particleTriangleTest(normalShapeSpace, distance,
particlePosShapeSpace, cVolumePosShapeSpace, cVolumeRadius, triV0, triV1, triV2, sScratch->enableCCD);
//this code still misses a logic from the triangle mesh near phase, which also selects the best contact
//if there are more than MaxStaticContactsPerMesh
if (intersect && contactCounts < PxgParticleContactInfo::MaxStaticContactsPerMesh)
{
PxVec3 normal = heightfieldTransform.rotate(normalShapeSpace);
sPerParticleTempContacts[contactCounts++] = make_float4(normal.x, normal.y, normal.z, distance - restDist);
}
}
}
}//end of totalNumProcessed
contactCounts = PxMin(contactCounts, PxgParticleContactInfo::MaxStaticContactsPerMesh);
PxU32 contactWritten = 0;
if (sScratch->rigidId.isStaticBody())
{
if (contactCounts)
{
PxU32 contactIndex = (PxU32)(atomicAdd((PxI32*)&sScratch->oneWayContactCounts[particleIndex], (PxI32)contactCounts));
PxU32 endIndex = PxMin(contactIndex + contactCounts, PxgParticleContactInfo::MaxStaticContactsPerParticle);
for (PxU32 i = contactIndex, src = 0; i < endIndex; i++, src++)
{
const PxU32 outputIndex = particleIndex + i * numParticles;
sScratch->oneWayContactInfos[outputIndex].mNormal_PenW = sPerParticleTempContacts[src];
sScratch->oneWayContactNodeIndices[outputIndex] = sScratch->rigidId;
}
}
contactWritten = PxMin(contactCounts, PxgParticleContactInfo::MaxStaticContactsPerParticle);
sScratch->oneWayContactCounts[particleIndex] = contactWritten;
}
PxU32 remaining = contactCounts - contactWritten;
if ((remaining > 0) && !isDiffuse)
{
PxU64 compressedParticleIndex = PxEncodeParticleIndex(sScratch->particleSystemId, particleIndex);
PxU32 contactStartIndex = atomicAdd(writer.numTotalContacts, remaining);
PxU32 contactEndIndex = contactStartIndex + remaining;
for (PxU32 contactIndex = contactStartIndex, src = 0; contactIndex < contactEndIndex; contactIndex++, src++)
{
float4 contact = sPerParticleTempContacts[src];
writer.writeContact(contactIndex, PxVec4(contact.x, contact.y, contact.z, contact.w), compressedParticleIndex, sScratch->rigidId);
}
}
}
extern "C" __global__ void ps_heightfieldCollisonLaunch(
const bool isTGS,
const PxReal toleranceLength,
const PxgContactManagerInput* PX_RESTRICT cmInputs,
const PxsCachedTransform* PX_RESTRICT transformCache,
const PxReal* PX_RESTRICT contactDistance,
const PxReal* PX_RESTRICT restDistances,
const PxgShape* PX_RESTRICT gpuShapes,
PxgParticleSystem* PX_RESTRICT particleSystems,
PxNodeIndex* PX_RESTRICT shapeToRigidRemapTable,
PxgParticleContactWriter writer
)
{
__shared__ __align__(16) PxU8 sData[sizeof(PsHeightfieldScratch)];
PsHeightfieldScratch& sHeightfieldScratch = reinterpret_cast<PsHeightfieldScratch&>(sData);
__shared__ float4 sTempContacts[PxgParticleSystemKernelBlockDim::PS_HEIGHTFIELD_COLLISION][PxgParticleContactInfo::MaxStaticContactsPerMesh];
unsigned int cmIdx = blockIdx.y;
const PxU32 warpIndex = threadIdx.x / WARP_SIZE;
const bool isDiffuseParticlesThread = blockIdx.z == 1;
if (warpIndex == 0)
{
const PxU32 threadIndexInWarp = threadIdx.x & (WARP_SIZE - 1);
PxgShape particleShape, heightfieldShape;
PxU32 particleCacheRef, heightfieldCacheRef;
LoadShapePairWarp<PxGeometryType::ePARTICLESYSTEM, PxGeometryType::eHEIGHTFIELD>(cmInputs, cmIdx, gpuShapes,
particleShape, particleCacheRef, heightfieldShape, heightfieldCacheRef);
PxsCachedTransform heightfieldTransformCache;
PxsCachedTransform_ReadWarp(heightfieldTransformCache, transformCache + heightfieldCacheRef);
const PxReal contactDist = contactDistance[particleCacheRef] + contactDistance[heightfieldCacheRef];
const PxNodeIndex rigidId = shapeToRigidRemapTable[heightfieldCacheRef];
const PxU32 particleSystemId = particleShape.particleOrSoftbodyId;
PxgParticleSystem& particleSystem = particleSystems[particleSystemId];
if (threadIndexInWarp == 0)
{
sHeightfieldScratch.heightfieldTransform = heightfieldTransformCache.transform;
sHeightfieldScratch.heightfieldShape = heightfieldShape;
sHeightfieldScratch.rigidId = rigidId;
sHeightfieldScratch.transformCacheRef = heightfieldCacheRef;
sHeightfieldScratch.contactDist = contactDist;
sHeightfieldScratch.restDist = restDistances[cmIdx];
sHeightfieldScratch.enableCCD = (particleSystem.mData.mFlags & PxParticleFlag::eENABLE_SPECULATIVE_CCD) > 0;
if (isDiffuseParticlesThread)
{
// If there is no diffuse particles or is not using PBD, disactivate collision handling from this kernel
const bool isDiffuseParticlesActive = (particleSystem.mCommonData.mMaxDiffuseParticles > 0);
sHeightfieldScratch.currentPositions = particleSystem.mDiffuseSortedOriginPos_LifeTime;
sHeightfieldScratch.predictedPositions = particleSystem.mDiffuseSortedPos_LifeTime;
sHeightfieldScratch.numParticles = (isDiffuseParticlesActive) ? *particleSystem.mNumDiffuseParticles : 0;
sHeightfieldScratch.oneWayContactInfos = particleSystem.mDiffuseOneWayContactInfos;
sHeightfieldScratch.oneWayContactNodeIndices = particleSystem.mDiffuseOneWayNodeIndex;
sHeightfieldScratch.oneWayContactCounts = particleSystem.mDiffuseOneWayContactCount;
sHeightfieldScratch.particleSystemId = particleSystemId;
}
else
{
sHeightfieldScratch.currentPositions = particleSystem.mSortedOriginPos_InvMass;
sHeightfieldScratch.predictedPositions = particleSystem.mSortedPositions_InvMass;
sHeightfieldScratch.numParticles = particleSystem.mCommonData.mNumParticles;
sHeightfieldScratch.oneWayContactInfos = particleSystem.mOneWayContactInfos;
sHeightfieldScratch.oneWayContactNodeIndices = particleSystem.mOneWayNodeIndex;
sHeightfieldScratch.oneWayContactCounts = particleSystem.mOneWayContactCount;
sHeightfieldScratch.particleSystemId = particleSystemId;
}
}
}
__syncthreads();
const PxU32 numParticles = sHeightfieldScratch.numParticles;
if (isDiffuseParticlesThread && numParticles == 0)
return;
for (PxU32 globalThreadIndex = blockIdx.x * blockDim.x + threadIdx.x; globalThreadIndex < numParticles; globalThreadIndex += gridDim.x * blockDim.x)
{
psHeightfieldMidphaseCollision(
&sHeightfieldScratch,
sTempContacts[threadIdx.x],
numParticles,
globalThreadIndex,
isDiffuseParticlesThread,
isTGS,
writer
);
}
}