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

384 lines
14 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 "tet/ExtDelaunayBoundaryInserter.h"
#include "extensions/PxTetMakerExt.h"
#include "cooking/PxTetrahedronMeshDesc.h"
#include "geometry/PxTriangleMesh.h"
#include "tet/ExtMeshSimplificator.h"
#include "tet/ExtRemesher.h"
#include "tet/ExtOctreeTetrahedralizer.h"
#include "tet/ExtVoxelTetrahedralizer.h"
#include "foundation/PxMat33.h"
#include <stdio.h>
using namespace physx;
static PX_FORCE_INLINE PxReal computeTetrahedronVolume(const PxVec3& x0, const PxVec3& x1, const PxVec3& x2, const PxVec3& x3)
{
const PxVec3 u1 = x1 - x0;
const PxVec3 u2 = x2 - x0;
const PxVec3 u3 = x3 - x0;
PxMat33 edgeMatrix = PxMat33(u1, u2, u3);
const PxReal det = edgeMatrix.getDeterminant();
const PxReal volume = det / 6.0f;
return volume;
}
//Remove tets with small volume
static void removeSmallVolumeTetrahedra(PxArray<::physx::PxVec3>& vertices, PxArray<PxU32>& indices, PxReal volumeThreshold = 1e-8f)
{
uint32_t indexer = 0;
for (uint32_t i = 0; i < indices.size(); i += 4)
{
for (uint32_t j = 0; j < 4; ++j)
{
indices[indexer + j] = indices[i + j];
}
if (computeTetrahedronVolume(vertices[indices[i]], vertices[indices[i + 1]], vertices[indices[i + 2]], vertices[indices[i + 3]]) >= volumeThreshold)
{
indexer += 4;
}
}
if (indexer < indices.size())
{
indices.removeRange(indexer, indices.size() - indexer);
}
}
//Removes vertices not referenced by any tetrahedron and maps the tet's indices to match the compacted vertex list
static void removeUnusedVertices(PxArray<::physx::PxVec3>& vertices, PxArray<PxU32>& tets, PxU32 numPointsToKeepAtBeginning = 0)
{
PxArray<PxI32> compressorMap;
compressorMap.resize(vertices.size());
for (PxU32 i = 0; i < numPointsToKeepAtBeginning; ++i)
compressorMap[i] = 0;
for (PxU32 i = numPointsToKeepAtBeginning; i < compressorMap.size(); ++i)
compressorMap[i] = -1;
for (PxU32 i = 0; i < tets.size(); i += 4)
{
const PxU32* tet = &tets[i];
if (tet[0] == 0xFFFFFFFFu)
continue;
compressorMap[tet[0]] = 0;
compressorMap[tet[1]] = 0;
compressorMap[tet[2]] = 0;
compressorMap[tet[3]] = 0;
}
PxU32 indexer = 0;
for (PxU32 i = 0; i < compressorMap.size(); ++i)
{
if (compressorMap[i] >= 0)
{
compressorMap[i] = indexer;
vertices[indexer] = vertices[i];
indexer++;
}
}
for (PxU32 i = 0; i < tets.size(); i += 4)
{
PxU32* tet = &tets[i];
if (tet[0] == 0xFFFFFFFFu)
continue;
tet[0] = compressorMap[tet[0]];
tet[1] = compressorMap[tet[1]];
tet[2] = compressorMap[tet[2]];
tet[3] = compressorMap[tet[3]];
}
if (indexer < vertices.size())
vertices.removeRange(indexer, vertices.size() - indexer);
}
static PX_FORCE_INLINE PxU64 buildKey(PxI32 a, PxI32 b)
{
if (a < b)
return ((PxU64(a)) << 32) | (PxU64(b));
else
return ((PxU64(b)) << 32) | (PxU64(a));
}
static const PxI32 neighborEdgeList[3][2] = { { 0, 1 }, { 0, 2 }, { 1, 2 } };
static void buildTriangleNeighborhood(const PxI32* tris, PxU32 numTris, PxArray<PxI32>& result)
{
PxU32 l = 4 * numTris; //Waste one element in neighborhood info but allow bit shift access instead
result.clear();
result.resize(l, -1);
PxHashMap<PxU64, PxI32> faces;
for (PxU32 i = 0; i < numTris; ++i)
{
const PxI32* tri = &tris[3 * i];
if (tris[0] < 0)
continue;
for (PxI32 j = 0; j < 3; ++j)
{
PxU64 key = buildKey(tri[neighborEdgeList[j][0]], tri[neighborEdgeList[j][1]]);
if (const PxPair<const PxU64, PxI32>* ptr = faces.find(key))
{
if (ptr->second < 0)
{
//PX_ASSERT(false); //Invalid tetmesh since a face is shared by more than 2 tetrahedra
continue;
}
result[4 * i + j] = ptr->second;
result[ptr->second] = 4 * i + j;
faces[key] = -1;
}
else
faces.insert(key, 4 * i + j);
}
}
}
void PxTetMaker::detectTriangleIslands(const PxI32* triangles, PxU32 numTriangles, PxArray<PxU32>& islandIndexPerTriangle)
{
//Detect islands
PxArray<PxI32> neighborhood;
buildTriangleNeighborhood(triangles, numTriangles, neighborhood);
const PxU32 noIslandAssignedMarker = 0xFFFFFFFF;
islandIndexPerTriangle.resize(numTriangles, noIslandAssignedMarker);
PxU32 start = 0;
PxI32 color = -1;
PxArray<PxI32> stack;
while (true)
{
stack.clear();
while (start < islandIndexPerTriangle.size())
{
if (islandIndexPerTriangle[start] == noIslandAssignedMarker)
{
stack.pushBack(start);
++color;
islandIndexPerTriangle[start] = color;
break;
}
++start;
}
if (start == islandIndexPerTriangle.size())
break;
while (stack.size() > 0)
{
PxI32 id = stack.popBack();
for (PxI32 i = 0; i < 3; ++i)
{
PxI32 a = neighborhood[4 * id + i];
PxI32 tetId = a >> 2;
if (tetId >= 0 && islandIndexPerTriangle[tetId] == noIslandAssignedMarker)
{
stack.pushBack(tetId);
islandIndexPerTriangle[tetId] = color;
}
}
}
}
}
PxU32 PxTetMaker::findLargestIslandId(const PxU32* islandIndexPerTriangle, PxU32 numTriangles)
{
PxU32 numIslands = 0;
for (PxU32 i = 0; i < numTriangles; ++i)
numIslands = PxMax(numIslands, islandIndexPerTriangle[i]);
++numIslands;
PxArray<PxU32> numEntriesPerColor;
numEntriesPerColor.resize(numIslands, 0);
for (PxU32 i = 0; i < numTriangles; ++i)
numEntriesPerColor[islandIndexPerTriangle[i]] += 1;
PxU32 colorWithHighestTetCount = 0;
for (PxU32 i = 1; i < numEntriesPerColor.size(); ++i)
if (numEntriesPerColor[i] > numEntriesPerColor[colorWithHighestTetCount])
colorWithHighestTetCount = i;
return colorWithHighestTetCount;
}
bool PxTetMaker::createConformingTetrahedronMesh(const PxSimpleTriangleMesh& triangleMesh,
physx::PxArray<physx::PxVec3>& outVertices, physx::PxArray<physx::PxU32>& outTetIndices, const bool validate, PxReal volumeThreshold)
{
if (validate)
{
PxTriangleMeshAnalysisResults result = PxTetMaker::validateTriangleMesh(triangleMesh);
if (result & PxTriangleMeshAnalysisResult::eMESH_IS_INVALID)
{
PxGetFoundation().error(PxErrorCode::eDEBUG_INFO, PX_FL, "createConformingTetrahedronMesh(): Input triangle mesh is not suited to create a tetmesh due to deficiencies. Please call PxTetMaker::validateTriangleMesh(triangleMesh) for more details.");
return false;
}
}
Ext::generateTetmesh(triangleMesh.points, triangleMesh.triangles, triangleMesh.flags & PxMeshFlag::e16_BIT_INDICES, outVertices, outTetIndices);
if (volumeThreshold > 0.0f)
removeSmallVolumeTetrahedra(outVertices, outTetIndices, volumeThreshold);
PxU32 numRemoveAtEnd = Ext::removeDisconnectedIslands(reinterpret_cast<PxI32*>(outTetIndices.begin()), outTetIndices.size() / 4);
if (numRemoveAtEnd > 0)
outTetIndices.removeRange(outTetIndices.size() - 4 * numRemoveAtEnd, 4 * numRemoveAtEnd);
removeUnusedVertices(outVertices, outTetIndices, triangleMesh.points.count);
return true;
}
bool PxTetMaker::createVoxelTetrahedronMesh(const PxTetrahedronMeshDesc& tetMesh,
const PxU32 numVoxelsAlongLongestBoundingBoxAxis, physx::PxArray<physx::PxVec3>& outVertices, physx::PxArray<physx::PxU32>& outTetIndices,
PxI32* intputPointToOutputTetIndex, const PxU32* anchorNodeIndices, PxU32 numTetsPerVoxel)
{
//numTetsPerVoxel has only two valid values.
if (numTetsPerVoxel != 5 && numTetsPerVoxel != 6)
numTetsPerVoxel = 5;
Ext::generateVoxelTetmesh(tetMesh.points, tetMesh.tetrahedrons, numVoxelsAlongLongestBoundingBoxAxis, outVertices, outTetIndices, intputPointToOutputTetIndex, anchorNodeIndices, numTetsPerVoxel);
return true;
}
bool PxTetMaker::createVoxelTetrahedronMeshFromEdgeLength(const PxTetrahedronMeshDesc& tetMesh,
const PxReal voxelEdgeLength, physx::PxArray<physx::PxVec3>& outVertices, physx::PxArray<physx::PxU32>& outTetIndices,
PxI32* intputPointToOutputTetIndex, const PxU32* anchorNodeIndices, PxU32 numTetsPerVoxel)
{
//numTetsPerVoxel has only two valid values.
if (numTetsPerVoxel != 5 && numTetsPerVoxel != 6)
numTetsPerVoxel = 5;
Ext::generateVoxelTetmesh(tetMesh.points, tetMesh.tetrahedrons, voxelEdgeLength, outVertices, outTetIndices, intputPointToOutputTetIndex, anchorNodeIndices, numTetsPerVoxel);
return true;
}
PxTriangleMeshAnalysisResults PxTetMaker::validateTriangleMesh(const PxSimpleTriangleMesh& triangleMesh, const PxReal minVolumeThreshold, const PxReal minTriangleAngleRadians)
{
return Ext::validateTriangleMesh(triangleMesh.points, triangleMesh.triangles, triangleMesh.flags & PxMeshFlag::e16_BIT_INDICES, minVolumeThreshold, minTriangleAngleRadians);
}
PxTetrahedronMeshAnalysisResults PxTetMaker::validateTetrahedronMesh(const PxBoundedData& points, const PxBoundedData& tetrahedra, const PxReal minTetVolumeThreshold)
{
return Ext::validateTetrahedronMesh(points, tetrahedra, false, minTetVolumeThreshold);
}
void PxTetMaker::simplifyTriangleMesh(const PxArray<PxVec3>& inputVertices, const PxArray<PxU32>&inputIndices, int targetTriangleCount, PxF32 maximalEdgeLength,
PxArray<PxVec3>& outputVertices, PxArray<PxU32>& outputIndices,
PxArray<PxU32> *vertexMap, PxReal edgeLengthCostWeight, PxReal flatnessDetectionThreshold,
bool projectSimplifiedPointsOnInputMeshSurface, PxArray<PxU32>* outputVertexToInputTriangle, bool removeDisconnectedPatches)
{
Ext::MeshSimplificator ms;
PxArray<PxU32> indexMapToFullTriangleSet;
if (removeDisconnectedPatches)
{
PxU32 numTriangles = inputIndices.size() / 3;
PxArray<PxU32> islandIndexPerTriangle;
PxTetMaker::detectTriangleIslands(reinterpret_cast<const PxI32*>(inputIndices.begin()), numTriangles, islandIndexPerTriangle);
PxU32 biggestIslandIndex = PxTetMaker::findLargestIslandId(islandIndexPerTriangle.begin(), islandIndexPerTriangle.size());
PxArray<PxU32> connectedTriangleSet;
for (PxU32 i = 0; i < numTriangles; ++i)
{
if (islandIndexPerTriangle[i] == biggestIslandIndex)
{
for (PxU32 j = 0; j < 3; ++j)
connectedTriangleSet.pushBack(inputIndices[3 * i + j]);
indexMapToFullTriangleSet.pushBack(i);
}
}
ms.init(inputVertices, connectedTriangleSet, edgeLengthCostWeight, flatnessDetectionThreshold, projectSimplifiedPointsOnInputMeshSurface);
ms.decimateBySize(targetTriangleCount, maximalEdgeLength);
ms.readBack(outputVertices, outputIndices, vertexMap, outputVertexToInputTriangle);
}
else
{
ms.init(inputVertices, inputIndices, edgeLengthCostWeight, flatnessDetectionThreshold, projectSimplifiedPointsOnInputMeshSurface);
ms.decimateBySize(targetTriangleCount, maximalEdgeLength);
ms.readBack(outputVertices, outputIndices, vertexMap, outputVertexToInputTriangle);
}
if (removeDisconnectedPatches && projectSimplifiedPointsOnInputMeshSurface && outputVertexToInputTriangle)
{
for (PxU32 i = 0; i < outputVertexToInputTriangle->size(); ++i)
(*outputVertexToInputTriangle)[i] = indexMapToFullTriangleSet[(*outputVertexToInputTriangle)[i]];
}
}
void PxTetMaker::remeshTriangleMesh(const PxArray<PxVec3>& inputVertices, const PxArray<PxU32>&inputIndices, PxU32 gridResolution,
PxArray<PxVec3>& outputVertices, PxArray<PxU32>& outputIndices, PxArray<PxU32> *vertexMap)
{
Ext::Remesher rm;
rm.remesh(inputVertices, inputIndices, gridResolution, vertexMap);
rm.readBack(outputVertices, outputIndices);
}
void PxTetMaker::remeshTriangleMesh(const PxVec3* inputVertices, PxU32 nbVertices, const PxU32* inputIndices, PxU32 nbIndices, PxU32 gridResolution,
PxArray<PxVec3>& outputVertices, PxArray<PxU32>& outputIndices, PxArray<PxU32> *vertexMap)
{
Ext::Remesher rm;
rm.remesh(inputVertices, nbVertices, inputIndices, nbIndices, gridResolution, vertexMap);
rm.readBack(outputVertices, outputIndices);
}
void PxTetMaker::createTreeBasedTetrahedralMesh(const PxArray<PxVec3>& inputVertices, const PxArray<PxU32>&inputIndices,
bool useTreeNodes, PxArray<PxVec3>& outputVertices, PxArray<PxU32>& outputIndices, PxReal volumeThreshold)
{
Ext::OctreeTetrahedralizer ot;
ot.createTetMesh(inputVertices, inputIndices, useTreeNodes);
ot.readBack(outputVertices, outputIndices);
if (volumeThreshold > 0.0f)
removeSmallVolumeTetrahedra(outputVertices, outputIndices, volumeThreshold);
PxU32 numRemoveAtEnd = Ext::removeDisconnectedIslands(reinterpret_cast<PxI32*>(outputIndices.begin()), outputIndices.size() / 4);
if (numRemoveAtEnd > 0)
outputIndices.removeRange(outputIndices.size() - 4 * numRemoveAtEnd, 4 * numRemoveAtEnd);
removeUnusedVertices(outputVertices, outputIndices, inputVertices.size());
}
void PxTetMaker::createRelaxedVoxelTetrahedralMesh(const PxArray<PxVec3>& inputVertices, const PxArray<PxU32>&inputIndices,
PxArray<PxVec3>& outputVertices, PxArray<PxU32>& outputIndices,
PxI32 resolution, PxI32 numRelaxationIters, PxF32 relMinTetVolume)
{
Ext::VoxelTetrahedralizer vt;
vt.createTetMesh(inputVertices, inputIndices, resolution, numRelaxationIters, relMinTetVolume);
vt.readBack(outputVertices, outputIndices);
}