Revision control

+ // 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-2026 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);
+ }