Files
XCEngine/engine/third_party/physx/source/physxextensions/src/ExtDeformableVolumeExt.cpp

634 lines
29 KiB
C++

// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#include "extensions/PxDeformableVolumeExt.h"
#include "extensions/PxTetMakerExt.h"
#include "GuTetrahedronMesh.h"
#include "GuTetrahedronMeshUtils.h"
#include "cooking/PxCooking.h"
#include "PxPhysics.h"
#include "extensions/PxRemeshingExt.h"
#include "cudamanager/PxCudaContextManager.h"
#include "cudamanager/PxCudaContext.h"
#include "extensions/PxCudaHelpersExt.h"
using namespace physx;
using namespace Cm;
//Computes the volume of the simulation mesh defined as the sum of the volumes of all tetrahedra
static PxReal computeSimulationMeshVolume(PxDeformableVolume& deformableVolume, PxVec4* simPositions)
{
const PxU32 numTetsGM = deformableVolume.getSimulationMesh()->getNbTetrahedrons();
const PxU32* tetPtr32 = reinterpret_cast<const PxU32*>(deformableVolume.getSimulationMesh()->getTetrahedrons());
const PxU16* tetPtr16 = reinterpret_cast<const PxU16*>(deformableVolume.getSimulationMesh()->getTetrahedrons());
const bool sixteenBit = deformableVolume.getSimulationMesh()->getTetrahedronMeshFlags() & PxTetrahedronMeshFlag::e16_BIT_INDICES;
PxReal volume = 0;
for (PxU32 i = 0; i < numTetsGM; ++i)
{
PxVec4& x0 = simPositions[sixteenBit ? tetPtr16[4 * i] : tetPtr32[4 * i]];
PxVec4& x1 = simPositions[sixteenBit ? tetPtr16[4 * i + 1] : tetPtr32[4 * i + 1]];
PxVec4& x2 = simPositions[sixteenBit ? tetPtr16[4 * i + 2] : tetPtr32[4 * i + 2]];
PxVec4& x3 = simPositions[sixteenBit ? tetPtr16[4 * i + 3] : tetPtr32[4 * i + 3]];
const PxVec3 u1 = x1.getXYZ() - x0.getXYZ();
const PxVec3 u2 = x2.getXYZ() - x0.getXYZ();
const PxVec3 u3 = x3.getXYZ() - x0.getXYZ();
PxMat33 Q = PxMat33(u1, u2, u3);
const PxReal det = Q.getDeterminant();
volume += det;
}
volume /= 6.0f;
return volume;
}
//Recomputes the volume associated with a vertex. Every tetrahedron distributes a quarter of its volume to
//each vertex it is connected to. Finally the volume stored for every vertex is inverted.
static void updateNodeInverseVolumes(PxDeformableVolume& deformableVolume, PxVec4* simPositions)
{
const PxU32 numVertsGM = deformableVolume.getSimulationMesh()->getNbVertices();
const PxU32 numTetsGM = deformableVolume.getSimulationMesh()->getNbTetrahedrons();
const PxU32* tetPtr32 = reinterpret_cast<const PxU32*>(deformableVolume.getSimulationMesh()->getTetrahedrons());
const PxU16* tetPtr16 = reinterpret_cast<const PxU16*>(deformableVolume.getSimulationMesh()->getTetrahedrons());
const bool sixteenBit = deformableVolume.getSimulationMesh()->getTetrahedronMeshFlags() & PxTetrahedronMeshFlag::e16_BIT_INDICES;
for (PxU32 i = 0; i < numVertsGM; ++i)
simPositions[i].w = 0.0f;
for (PxU32 i = 0; i < numTetsGM; ++i)
{
PxVec4& x0 = simPositions[sixteenBit ? tetPtr16[4 * i] : tetPtr32[4 * i]];
PxVec4& x1 = simPositions[sixteenBit ? tetPtr16[4 * i + 1] : tetPtr32[4 * i + 1]];
PxVec4& x2 = simPositions[sixteenBit ? tetPtr16[4 * i + 2] : tetPtr32[4 * i + 2]];
PxVec4& x3 = simPositions[sixteenBit ? tetPtr16[4 * i + 3] : tetPtr32[4 * i + 3]];
const PxVec3 u1 = x1.getXYZ() - x0.getXYZ();
const PxVec3 u2 = x2.getXYZ() - x0.getXYZ();
const PxVec3 u3 = x3.getXYZ() - x0.getXYZ();
PxMat33 Q = PxMat33(u1, u2, u3);
//det should be positive
const PxReal det = Q.getDeterminant();
if (det <= 1.e-9f)
PxGetFoundation().error(PxErrorCode::eINVALID_PARAMETER, PX_FL, "updateNodeInverseVolumes(): tetrahedron is degenerate or inverted");
//Distribute one quarter of the volume to each vertex the tetrahedron is connected to
const PxReal volume = det / 6.0f;
x0.w += 0.25f * volume;
x1.w += 0.25f * volume;
x2.w += 0.25f * volume;
x3.w += 0.25f * volume;
}
//Invert the volumes stored per vertex and copy it to the velocity buffer
for (PxU32 i = 0; i < numVertsGM; ++i)
{
if (simPositions[i].w > 0)
{
simPositions[i].w = 1.0f / simPositions[i].w;
}
}
}
void PxDeformableVolumeExt::updateMass(PxDeformableVolume& deformableVolume, const PxReal density, const PxReal maxInvMassRatio, PxVec4* simPositionsPinned)
{
//Inverse volumes are recomputed to ensure that multiple subsequent calls of this method lead to correct results
updateNodeInverseVolumes(deformableVolume, simPositionsPinned);
const PxU32 numVertsGM = deformableVolume.getSimulationMesh()->getNbVertices();
PxReal minInvMass = PX_MAX_F32, maxInvMass = 0.f;
for (PxU32 i = 0; i < numVertsGM; ++i)
{
const PxVec4& vert = simPositionsPinned[i];
PxReal invMass = vert.w;
invMass = invMass / (density);
minInvMass = invMass > 0.f ? PxMin(invMass, minInvMass) : minInvMass;
maxInvMass = PxMax(invMass, maxInvMass);
simPositionsPinned[i] = PxVec4(vert.x, vert.y, vert.z, invMass);
}
if (minInvMass != PX_MAX_F32)
{
const PxReal ratio = maxInvMass / minInvMass;
if (ratio > maxInvMassRatio)
{
//Clamp the upper limit...
maxInvMass = minInvMass * maxInvMassRatio;
for (PxU32 i = 0; i < numVertsGM; ++i)
{
PxVec4& posInvMass = simPositionsPinned[i];
posInvMass.w = PxMin(posInvMass.w, maxInvMass);
}
}
}
}
void PxDeformableVolumeExt::setMass(PxDeformableVolume& deformableVolume, const PxReal mass, const PxReal maxInvMassRatio, PxVec4* simPositionsPinned)
{
//Compute the density such that density times volume is equal to the desired mass
updateMass(deformableVolume, mass / computeSimulationMeshVolume(deformableVolume, simPositionsPinned), maxInvMassRatio, simPositionsPinned);
}
void PxDeformableVolumeExt::transform(PxDeformableVolume& deformableVolume, const PxTransform& transform, const PxReal scale, PxVec4* simPositionsPinned, PxVec4* simVelocitiesPinned, PxVec4* collPositionsPinned, PxVec4* restPositionsPinned)
{
const PxU32 numVertsGM = deformableVolume.getSimulationMesh()->getNbVertices();
const PxU32 numVerts = deformableVolume.getCollisionMesh()->getNbVertices();
for (PxU32 i = 0; i < numVertsGM; ++i)
{
const PxVec4 tpInvMass = simPositionsPinned[i];
const PxReal invMass = tpInvMass.w;
PxVec3 vert = PxVec3(tpInvMass.x * scale, tpInvMass.y * scale, tpInvMass.z * scale);
//Transform the vertex position and keep the inverse mass
vert = transform.transform(vert);
simPositionsPinned[i] = PxVec4(vert.x, vert.y, vert.z, invMass);
PxVec4 vel = simVelocitiesPinned[i];
PxVec3 v = PxVec3(vel.x * scale, vel.y * scale, vel.z * scale);
//Velocities are translation invariant, therefore only the direction needs to get adjusted.
//The inverse mass is stored as well to optimize memory access on the GPU
v = transform.rotate(v);
simVelocitiesPinned[i] = PxVec4(v.x, v.y, v.z, invMass);
}
for (PxU32 i = 0; i < numVerts; ++i)
{
restPositionsPinned[i] = PxVec4(restPositionsPinned[i].x*scale, restPositionsPinned[i].y*scale, restPositionsPinned[i].z*scale, 1.f);
const PxVec4 tpInvMass = collPositionsPinned[i];
PxVec3 vert = PxVec3(tpInvMass.x * scale, tpInvMass.y * scale, tpInvMass.z * scale);
vert = transform.transform(vert);
collPositionsPinned[i] = PxVec4(vert.x, vert.y, vert.z, tpInvMass.w);
}
PxMat33* tetraRestPosesGM = static_cast<Gu::DeformableVolumeAuxData*>(deformableVolume.getDeformableVolumeAuxData())->getGridModelRestPosesFast(); // reinterpret_cast<PxMat33*>(simMeshData->deformableVolumeAuxData.getGridModelRestPosesFast());
const PxU32 nbTetraGM = deformableVolume.getSimulationMesh()->getNbTetrahedrons();
const PxReal invScale = 1.0f / scale;
for (PxU32 i = 0; i < nbTetraGM; ++i)
{
PxMat33& m = tetraRestPosesGM[i];
//Scale the rest pose
m.column0 = m.column0 * invScale;
m.column1 = m.column1 * invScale;
m.column2 = m.column2 * invScale;
//The rest pose is translation invariant, it only needs be rotated
PxVec3 row0 = transform.rotateInv(PxVec3(m.column0.x, m.column1.x, m.column2.x));
PxVec3 row1 = transform.rotateInv(PxVec3(m.column0.y, m.column1.y, m.column2.y));
PxVec3 row2 = transform.rotateInv(PxVec3(m.column0.z, m.column1.z, m.column2.z));
m.column0 = PxVec3(row0.x, row1.x, row2.x);
m.column1 = PxVec3(row0.y, row1.y, row2.y);
m.column2 = PxVec3(row0.z, row1.z, row2.z);
}
PxMat33* tetraRestPoses = static_cast<Gu::DeformableVolumeAuxData*>(deformableVolume.getDeformableVolumeAuxData())->getRestPosesFast(); // reinterpret_cast<PxMat33*>(simMeshData->deformableVolumeAuxData.getGridModelRestPosesFast());
const PxU32 nbTetra = deformableVolume.getCollisionMesh()->getNbTetrahedrons();
for (PxU32 i = 0; i < nbTetra; ++i)
{
PxMat33& m = tetraRestPoses[i];
//Scale the rest pose
m.column0 = m.column0 * invScale;
m.column1 = m.column1 * invScale;
m.column2 = m.column2 * invScale;
//The rest pose is translation invariant, it only needs be rotated
PxVec3 row0 = transform.rotateInv(PxVec3(m.column0.x, m.column1.x, m.column2.x));
PxVec3 row1 = transform.rotateInv(PxVec3(m.column0.y, m.column1.y, m.column2.y));
PxVec3 row2 = transform.rotateInv(PxVec3(m.column0.z, m.column1.z, m.column2.z));
m.column0 = PxVec3(row0.x, row1.x, row2.x);
m.column1 = PxVec3(row0.y, row1.y, row2.y);
m.column2 = PxVec3(row0.z, row1.z, row2.z);
}
}
void PxDeformableVolumeExt::updateEmbeddedCollisionMesh(PxDeformableVolume& deformableVolume, PxVec4* simPositionsPinned, PxVec4* collPositionsPinned)
{
Gu::DeformableVolumeAuxData* deformableVolumeAuxData = static_cast<Gu::DeformableVolumeAuxData*>(deformableVolume.getDeformableVolumeAuxData());
const PxU32* remapTable = deformableVolumeAuxData->mVertsRemapInGridModel;
PxReal* barycentricCoordinates = deformableVolumeAuxData->mVertsBarycentricInGridModel;
PxTetrahedronMesh* simMesh = deformableVolume.getSimulationMesh();
const void* tets = simMesh->getTetrahedrons();
const PxU32* tets32 = static_cast<const PxU32*>(tets);
const PxU16* tets16 = static_cast<const PxU16*>(tets);
bool sixteenBit = simMesh->getTetrahedronMeshFlags() & PxTetrahedronMeshFlag::e16_BIT_INDICES;
const PxU32 numVerts = deformableVolume.getCollisionMesh()->getNbVertices();
for (PxU32 i = 0; i < numVerts; ++i)
{
//The tetrahedra are ordered differently on the GPU, therefore the index must be taken from the remap table
const PxU32 tetrahedronIdx = remapTable[i];
const PxVec4 p0 = simPositionsPinned[sixteenBit ? tets16[4 * tetrahedronIdx] : tets32[4 * tetrahedronIdx]];
const PxVec4 p1 = simPositionsPinned[sixteenBit ? tets16[4 * tetrahedronIdx + 1] : tets32[4 * tetrahedronIdx + 1]];
const PxVec4 p2 = simPositionsPinned[sixteenBit ? tets16[4 * tetrahedronIdx + 2] : tets32[4 * tetrahedronIdx + 2]];
const PxVec4 p3 = simPositionsPinned[sixteenBit ? tets16[4 * tetrahedronIdx + 3] : tets32[4 * tetrahedronIdx + 3]];
const PxReal* barycentric = &barycentricCoordinates[4*i];
//Compute the embedded position as a weigted sum of vertices from the simulation mesh
//This ensures that all tranformations and scale changes applied to the simulation mesh get transferred
//to the collision mesh
collPositionsPinned[i] = p0 * barycentric[0] + p1 * barycentric[1] + p2 * barycentric[2] + p3 * barycentric[3];
collPositionsPinned[i].w = 1.0f;
}
}
void PxDeformableVolumeExt::copyToDevice(PxDeformableVolume& deformableVolume, PxDeformableVolumeDataFlags flags, PxVec4* simPositionsPinned, PxVec4* simVelocitiesPinned, PxVec4* collPositionsPinned, PxVec4* restPositionsPinned, CUstream stream)
{
//Updating the collision mesh's vertices ensures that simulation mesh and collision mesh are
//represented in the same coordinate system and the same scale
updateEmbeddedCollisionMesh(deformableVolume, simPositionsPinned, collPositionsPinned);
#if PX_SUPPORT_GPU_PHYSX
PxScopedCudaLock _lock(*deformableVolume.getCudaContextManager());
PxCudaContext* ctx = deformableVolume.getCudaContextManager()->getCudaContext();
if (flags & PxDeformableVolumeDataFlag::ePOSITION_INVMASS && collPositionsPinned)
ctx->memcpyHtoDAsync(reinterpret_cast<CUdeviceptr>(deformableVolume.getPositionInvMassBufferD()), collPositionsPinned, deformableVolume.getCollisionMesh()->getNbVertices() * sizeof(PxVec4), stream);
if (flags & PxDeformableVolumeDataFlag::eREST_POSITION_INVMASS && restPositionsPinned)
ctx->memcpyHtoDAsync(reinterpret_cast<CUdeviceptr>(deformableVolume.getRestPositionBufferD()), restPositionsPinned, deformableVolume.getCollisionMesh()->getNbVertices() * sizeof(PxVec4), stream);
if (flags & PxDeformableVolumeDataFlag::eSIM_POSITION_INVMASS && simPositionsPinned)
ctx->memcpyHtoDAsync(reinterpret_cast<CUdeviceptr>(deformableVolume.getSimPositionInvMassBufferD()), simPositionsPinned, deformableVolume.getSimulationMesh()->getNbVertices() * sizeof(PxVec4), stream);
if (flags & PxDeformableVolumeDataFlag::eSIM_VELOCITY && simVelocitiesPinned)
ctx->memcpyHtoDAsync(reinterpret_cast<CUdeviceptr>(deformableVolume.getSimVelocityBufferD()), simVelocitiesPinned, deformableVolume.getSimulationMesh()->getNbVertices() * sizeof(PxVec4), stream);
// we need to synchronize if the stream is the default argument.
if (stream == 0)
{
ctx->streamSynchronize(stream);
}
#else
PX_UNUSED(restPositionsPinned);
PX_UNUSED(simVelocitiesPinned);
PX_UNUSED(stream);
#endif
deformableVolume.markDirty(flags);
}
PxDeformableVolumeMesh* PxDeformableVolumeExt::createDeformableVolumeMesh(const PxCookingParams& params, const PxSimpleTriangleMesh& surfaceMesh, PxU32 numVoxelsAlongLongestAABBAxis, PxInsertionCallback& insertionCallback, const bool validate)
{
//Compute collision mesh
physx::PxArray<physx::PxVec3> collisionMeshVertices;
physx::PxArray<physx::PxU32> collisionMeshIndices;
if (!PxTetMaker::createConformingTetrahedronMesh(surfaceMesh, collisionMeshVertices, collisionMeshIndices, validate))
return NULL;
PxTetrahedronMeshDesc meshDesc(collisionMeshVertices, collisionMeshIndices);
//Compute simulation mesh
physx::PxArray<physx::PxI32> vertexToTet;
vertexToTet.resize(meshDesc.points.count);
physx::PxArray<physx::PxVec3> simulationMeshVertices;
physx::PxArray<physx::PxU32> simulationMeshIndices;
PxTetMaker::createVoxelTetrahedronMesh(meshDesc, numVoxelsAlongLongestAABBAxis, simulationMeshVertices, simulationMeshIndices, vertexToTet.begin());
PxTetrahedronMeshDesc simMeshDesc(simulationMeshVertices, simulationMeshIndices, PxTetrahedronMeshDesc::PxMeshFormat::eHEX_MESH);
PxDeformableVolumeSimulationDataDesc simDesc(vertexToTet);
physx::PxDeformableVolumeMesh* deformableVolumeMesh = PxCreateDeformableVolumeMesh(params, simMeshDesc, meshDesc, simDesc, insertionCallback);
return deformableVolumeMesh;
}
PxDeformableVolumeMesh* PxDeformableVolumeExt::createDeformableVolumeMeshNoVoxels(const PxCookingParams& params, const PxSimpleTriangleMesh& surfaceMesh, PxInsertionCallback& insertionCallback, PxReal maxWeightRatioInTet, const bool validate)
{
PxCookingParams p = params;
p.maxWeightRatioInTet = maxWeightRatioInTet;
physx::PxArray<physx::PxVec3> collisionMeshVertices;
physx::PxArray<physx::PxU32> collisionMeshIndices;
if (!PxTetMaker::createConformingTetrahedronMesh(surfaceMesh, collisionMeshVertices, collisionMeshIndices, validate))
return NULL;
PxTetrahedronMeshDesc meshDesc(collisionMeshVertices, collisionMeshIndices);
PxDeformableVolumeSimulationDataDesc simDesc;
physx::PxDeformableVolumeMesh* deformableVolumeMesh = PxCreateDeformableVolumeMesh(p, meshDesc, meshDesc, simDesc, insertionCallback);
return deformableVolumeMesh;
}
PxDeformableVolume* PxDeformableVolumeExt::createDeformableVolumeFromMesh(PxDeformableVolumeMesh* deformableVolumeMesh, const PxTransform& transform, const PxDeformableVolumeMaterial& material, PxCudaContextManager& cudaContextManager,
PxReal density, PxReal scale)
{
PxDeformableVolume* deformableVolume = PxGetPhysics().createDeformableVolume(cudaContextManager);
if (deformableVolume)
{
PxShapeFlags shapeFlags = PxShapeFlag::eVISUALIZATION | PxShapeFlag::eSCENE_QUERY_SHAPE | PxShapeFlag::eSIMULATION_SHAPE;
PxTetrahedronMeshGeometry geometry(deformableVolumeMesh->getCollisionMesh());
PxDeformableVolumeMaterial* materialPointer = const_cast<PxDeformableVolumeMaterial*>(&material);
PxShape* shape = PxGetPhysics().createShape(geometry, &materialPointer, 1, true, shapeFlags);
if (shape)
{
deformableVolume->attachShape(*shape);
}
deformableVolume->attachSimulationMesh(*deformableVolumeMesh->getSimulationMesh(), *deformableVolumeMesh->getDeformableVolumeAuxData());
PxVec4* simPositionInvMassPinned;
PxVec4* simVelocityPinned;
PxVec4* collPositionInvMassPinned;
PxVec4* restPositionPinned;
PxDeformableVolumeExt::allocateAndInitializeHostMirror(*deformableVolume, &cudaContextManager, simPositionInvMassPinned, simVelocityPinned, collPositionInvMassPinned, restPositionPinned);
const PxReal maxInvMassRatio = 50.f;
PxDeformableVolumeExt::transform(*deformableVolume, transform, scale, simPositionInvMassPinned, simVelocityPinned, collPositionInvMassPinned, restPositionPinned);
PxDeformableVolumeExt::updateMass(*deformableVolume, density, maxInvMassRatio, simPositionInvMassPinned);
PxDeformableVolumeExt::copyToDevice(*deformableVolume, PxDeformableVolumeDataFlag::eALL, simPositionInvMassPinned, simVelocityPinned, collPositionInvMassPinned, restPositionPinned);
#if PX_SUPPORT_GPU_PHYSX
PxCudaContextManager* mgr = &cudaContextManager;
PX_EXT_PINNED_MEMORY_FREE(*mgr, simPositionInvMassPinned);
PX_EXT_PINNED_MEMORY_FREE(*mgr, simVelocityPinned);
PX_EXT_PINNED_MEMORY_FREE(*mgr, collPositionInvMassPinned);
PX_EXT_PINNED_MEMORY_FREE(*mgr, restPositionPinned)
#endif
}
return deformableVolume;
}
PxDeformableVolume* PxDeformableVolumeExt::createDeformableVolumeBox(const PxTransform& transform, const PxVec3& boxDimensions, const PxDeformableVolumeMaterial& material,
PxCudaContextManager& cudaContextManager, PxReal maxEdgeLength, PxReal density, PxU32 numVoxelsAlongLongestAABBAxis, PxReal scale)
{
PxArray<PxVec3> triVerts;
triVerts.reserve(8);
triVerts.pushBack(PxVec3(0.5f, -0.5f, -0.5f).multiply(boxDimensions));
triVerts.pushBack(PxVec3(0.5f, -0.5f, 0.5f).multiply(boxDimensions));
triVerts.pushBack(PxVec3(-0.5f, -0.5f, 0.5f).multiply(boxDimensions));
triVerts.pushBack(PxVec3(-0.5f, -0.5f, -0.5f).multiply(boxDimensions));
triVerts.pushBack(PxVec3(0.5f, 0.5f, -0.5f).multiply(boxDimensions));
triVerts.pushBack(PxVec3(0.5f, 0.5f, 0.5f).multiply(boxDimensions));
triVerts.pushBack(PxVec3(-0.5f, 0.5f, 0.5f).multiply(boxDimensions));
triVerts.pushBack(PxVec3(-0.5f, 0.5f, -0.5f).multiply(boxDimensions));
PxArray<PxU32> triIndices;
triIndices.reserve(12 * 3);
triIndices.pushBack(1); triIndices.pushBack(2); triIndices.pushBack(3);
triIndices.pushBack(7); triIndices.pushBack(6); triIndices.pushBack(5);
triIndices.pushBack(4); triIndices.pushBack(5); triIndices.pushBack(1);
triIndices.pushBack(5); triIndices.pushBack(6); triIndices.pushBack(2);
triIndices.pushBack(2); triIndices.pushBack(6); triIndices.pushBack(7);
triIndices.pushBack(0); triIndices.pushBack(3); triIndices.pushBack(7);
triIndices.pushBack(0); triIndices.pushBack(1); triIndices.pushBack(3);
triIndices.pushBack(4); triIndices.pushBack(7); triIndices.pushBack(5);
triIndices.pushBack(0); triIndices.pushBack(4); triIndices.pushBack(1);
triIndices.pushBack(1); triIndices.pushBack(5); triIndices.pushBack(2);
triIndices.pushBack(3); triIndices.pushBack(2); triIndices.pushBack(7);
triIndices.pushBack(4); triIndices.pushBack(0); triIndices.pushBack(7);
if (maxEdgeLength > 0.0f)
PxRemeshingExt::limitMaxEdgeLength(triIndices, triVerts, maxEdgeLength, 3);
PxSimpleTriangleMesh surfaceMesh;
surfaceMesh.points.count = triVerts.size();
surfaceMesh.points.data = triVerts.begin();
surfaceMesh.triangles.count = triIndices.size() / 3;
surfaceMesh.triangles.data = triIndices.begin();
PxTolerancesScale tolerancesScale;
PxCookingParams params(tolerancesScale);
params.meshWeldTolerance = 0.001f;
params.meshPreprocessParams = PxMeshPreprocessingFlags(PxMeshPreprocessingFlag::eWELD_VERTICES);
params.buildTriangleAdjacencies = false;
params.buildGPUData = true;
params.midphaseDesc = PxMeshMidPhase::eBVH34;
PxDeformableVolumeMesh* deformableVolumeMesh = createDeformableVolumeMesh(params, surfaceMesh, numVoxelsAlongLongestAABBAxis, PxGetPhysics().getPhysicsInsertionCallback());
return createDeformableVolumeFromMesh(deformableVolumeMesh, transform, material, cudaContextManager, density, scale);
}
void PxDeformableVolumeExt::allocateAndInitializeHostMirror(PxDeformableVolume& deformableVolume, PxCudaContextManager* cudaContextManager, PxVec4*& simPositionInvMassPinned, PxVec4*& simVelocityPinned, PxVec4*& collPositionInvMassPinned, PxVec4*& restPositionPinned)
{
PX_ASSERT(deformableVolume.getCollisionMesh() != NULL);
PX_ASSERT(deformableVolume.getSimulationMesh() != NULL);
PxU32 nbCollVerts = deformableVolume.getCollisionMesh()->getNbVertices();
PxU32 nbSimVerts = deformableVolume.getSimulationMesh()->getNbVertices();
#if PX_SUPPORT_GPU_PHYSX
simPositionInvMassPinned = PX_EXT_PINNED_MEMORY_ALLOC(PxVec4, *cudaContextManager, nbSimVerts);
simVelocityPinned = PX_EXT_PINNED_MEMORY_ALLOC(PxVec4, *cudaContextManager, nbSimVerts);
collPositionInvMassPinned = PX_EXT_PINNED_MEMORY_ALLOC(PxVec4, *cudaContextManager, nbCollVerts);
restPositionPinned = PX_EXT_PINNED_MEMORY_ALLOC(PxVec4, *cudaContextManager, nbCollVerts);
#else
PX_UNUSED(cudaContextManager);
#endif
// write positionInvMass into CPU part.
const PxVec3* positions = deformableVolume.getCollisionMesh()->getVertices();
for (PxU32 i = 0; i < nbCollVerts; ++i)
{
PxVec3 vert = positions[i];
collPositionInvMassPinned[i] = PxVec4(vert, 1.f);
restPositionPinned[i] = PxVec4(vert, 1.f);
}
// write sim mesh part.
PxDeformableVolumeAuxData* s = deformableVolume.getDeformableVolumeAuxData();
PxReal* invMassGM = s->getGridModelInvMass();
const PxVec3* simPositions = deformableVolume.getSimulationMesh()->getVertices();
for (PxU32 i = 0; i < nbSimVerts; ++i)
{
PxReal invMass = invMassGM ? invMassGM[i] : 1.f;
simPositionInvMassPinned[i] = PxVec4(simPositions[i], invMass);
simVelocityPinned[i] = PxVec4(0.f, 0.f, 0.f, invMass);
}
}
struct InternalDeformableVolumeState
{
PxVec4* mVertices;
PxArray<PxReal> mInvMasses;
const PxU32* mTetrahedra;
PxArray<PxMat33> mInvRestPose;
PxArray<PxVec3> mPrevPos;
InternalDeformableVolumeState(const PxVec4* verticesOriginal, PxVec4* verticesDeformed, PxU32 nbVertices, const PxU32* tetrahedra, PxU32 nbTetraheda, const bool* vertexIsFixed) :
mVertices(verticesDeformed), mTetrahedra(tetrahedra)
{
PxReal density = 1.0f;
mInvMasses.resize(nbVertices, 0.0f);
mInvRestPose.resize(nbTetraheda, PxMat33(PxZero));
for (PxU32 i = 0; i < nbTetraheda; i++)
{
const PxU32* t = &mTetrahedra[4 * i];
const PxVec3 a = verticesOriginal[t[0]].getXYZ();
PxMat33 ir(verticesOriginal[t[1]].getXYZ() - a, verticesOriginal[t[2]].getXYZ() - a, verticesOriginal[t[3]].getXYZ() - a);
PxReal volume = ir.getDeterminant() / 6.0f;
if (volume > 1e-8f)
mInvRestPose[i] = ir.getInverse();
PxReal m = 0.25f * volume * density;
mInvMasses[t[0]] += m;
mInvMasses[t[1]] += m;
mInvMasses[t[2]] += m;
mInvMasses[t[3]] += m;
}
for (PxU32 i = 0; i < nbVertices; i++)
{
bool fixed = vertexIsFixed ? vertexIsFixed[i] : verticesOriginal[i].w == 0.0f;
if (mInvMasses[i] != 0.0f && !fixed)
mInvMasses[i] = 1.0f / mInvMasses[i];
else
mInvMasses[i] = 0.0f;
}
mPrevPos.resize(nbVertices);
for (PxU32 i = 0; i < mPrevPos.size(); ++i)
mPrevPos[i] = mVertices[i].getXYZ();
}
void applyDelta()
{
for (PxU32 i = 0; i < mPrevPos.size(); ++i)
{
PxVec3 delta = mVertices[i].getXYZ() - mPrevPos[i];
mPrevPos[i] = mVertices[i].getXYZ();
mVertices[i] += PxVec4(0.99f * delta, 0.0f);
}
}
PX_FORCE_INLINE void applyToElem(PxU32 elemNr, PxReal C, PxReal compliance, const PxVec3& g1, const PxVec3& g2, const PxVec3& g3, const PxVec4& invMasses)
{
if (C == 0.0f)
return;
const PxVec3 g0 = -g1 - g2 - g3;
const PxU32* t = &mTetrahedra[4 * elemNr];
const PxReal w = g0.magnitudeSquared() * invMasses.x + g1.magnitudeSquared() * invMasses.y + g2.magnitudeSquared() * invMasses.z + g3.magnitudeSquared() * invMasses.w;
if (w == 0.0f)
return;
const PxReal alpha = compliance;
const PxReal dlambda = -C / (w + alpha);
if (invMasses.x != 0.0f)
mVertices[t[0]] += PxVec4(g0 * dlambda * invMasses.x, 0.0f);
if (invMasses.y != 0.0f)
mVertices[t[1]] += PxVec4(g1 * dlambda * invMasses.y, 0.0f);
if (invMasses.z != 0.0f)
mVertices[t[2]] += PxVec4(g2 * dlambda * invMasses.z, 0.0f);
if (invMasses.w != 0.0f)
mVertices[t[3]] += PxVec4(g3 * dlambda * invMasses.w, 0.0f);
}
void solveElem(PxU32 elemNr)
{
const PxMat33& ir = mInvRestPose[elemNr];
if (ir == PxMat33(PxZero))
return;
const PxU32* tet = &mTetrahedra[4 * elemNr];
PxVec4 invMasses(mInvMasses[tet[0]], mInvMasses[tet[1]], mInvMasses[tet[2]], mInvMasses[tet[3]]);
PxMat33 P;
P.column0 = mVertices[tet[1]].getXYZ() - mVertices[tet[0]].getXYZ();
P.column1 = mVertices[tet[2]].getXYZ() - mVertices[tet[0]].getXYZ();
P.column2 = mVertices[tet[3]].getXYZ() - mVertices[tet[0]].getXYZ();
PxMat33 F = P * ir;
PxVec3 g1 = F.column0 * 2.0f * ir.column0.x + F.column1 * 2.0f * ir.column1.x + F.column2 * 2.0f * ir.column2.x;
PxVec3 g2 = F.column0 * 2.0f * ir.column0.y + F.column1 * 2.0f * ir.column1.y + F.column2 * 2.0f * ir.column2.y;
PxVec3 g3 = F.column0 * 2.0f * ir.column0.z + F.column1 * 2.0f * ir.column1.z + F.column2 * 2.0f * ir.column2.z;
PxReal C = F.column0.magnitudeSquared() + F.column1.magnitudeSquared() + F.column2.magnitudeSquared() - 3.0f;
applyToElem(elemNr, C, 0.0f, g1, g2, g3, invMasses);
P.column0 = mVertices[tet[1]].getXYZ() - mVertices[tet[0]].getXYZ();
P.column1 = mVertices[tet[2]].getXYZ() - mVertices[tet[0]].getXYZ();
P.column2 = mVertices[tet[3]].getXYZ() - mVertices[tet[0]].getXYZ();
F = P * ir;
PxMat33& dF = P; //Re-use memory, possible since P is not used anymore afterwards
dF.column0 = F.column1.cross(F.column2);
dF.column1 = F.column2.cross(F.column0);
dF.column2 = F.column0.cross(F.column1);
g1 = dF.column0 * ir.column0.x + dF.column1 * ir.column1.x + dF.column2 * ir.column2.x;
g2 = dF.column0 * ir.column0.y + dF.column1 * ir.column1.y + dF.column2 * ir.column2.y;
g3 = dF.column0 * ir.column0.z + dF.column1 * ir.column1.z + dF.column2 * ir.column2.z;
C = F.getDeterminant() - 1.0f;
applyToElem(elemNr, C, 0.0f, g1, g2, g3, invMasses);
}
};
void PxDeformableVolumeExt::relaxDeformableVolumeMesh(const PxVec4* verticesOriginal, PxVec4* verticesDeformed, PxU32 nbVertices, const PxU32* tetrahedra, PxU32 nbTetraheda, const bool* vertexIsFixed, PxU32 numIterations)
{
InternalDeformableVolumeState state(verticesOriginal, verticesDeformed, nbVertices, tetrahedra, nbTetraheda, vertexIsFixed);
for (PxU32 iter = 0; iter < numIterations; ++iter)
{
state.applyDelta();
for (PxU32 i = 0; i < nbTetraheda; ++i)
state.solveElem(i);
}
return;
}
void PxDeformableVolumeExt::convertCollisionToSimulationTet(PxDeformableVolume& deformableVolume, PxU32 tetId, const PxVec4& tetBarycentric, PxU32& outTetId, PxVec4& outTetBarycentric)
{
const PxTetrahedronMesh* simulationMesh = deformableVolume.getSimulationMesh();
const Gu::DeformableVolumeAuxData* deformableVolumeAuxData = static_cast<const Gu::DeformableVolumeAuxData*>(deformableVolume.getDeformableVolumeAuxData());
const Gu::BVTetrahedronMesh* collisionMesh = static_cast<const Gu::BVTetrahedronMesh*>(deformableVolume.getCollisionMesh());
// Sim mesh uses the same ordering for both CPU and GPU tet ids so we can use Gu::convertDeformableVolumeCollisionToSimMeshTets to convert from CPU collision tet id to CPU/GPU sim tet id.
Gu::convertDeformableVolumeCollisionToSimMeshTets(*simulationMesh, *deformableVolumeAuxData, *collisionMesh, tetId, tetBarycentric, outTetId, outTetBarycentric, false);
}