feat(physics): wire physx sdk into build
This commit is contained in:
48
engine/third_party/physx/source/physxextensions/src/tet/ExtBVH.cpp
vendored
Normal file
48
engine/third_party/physx/source/physxextensions/src/tet/ExtBVH.cpp
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
// 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.
|
||||
|
||||
#include "ExtBVH.h"
|
||||
#include "ExtUtilities.h"
|
||||
|
||||
using namespace physx;
|
||||
using namespace Ext;
|
||||
using namespace Gu;
|
||||
|
||||
void BVHDesc::query(const PxBounds3& bounds, PxArray<PxI32>& items)
|
||||
{
|
||||
items.clear();
|
||||
IntersectionCollectingTraversalController traversalController(bounds, items);
|
||||
traverseBVH(tree.begin(), traversalController, 0);
|
||||
}
|
||||
|
||||
void BVHBuilder::build(BVHDesc& bvh, const PxBounds3* items, PxI32 n)
|
||||
{
|
||||
AABBTreeBounds boxes;
|
||||
boxes.init(n);
|
||||
for (PxI32 i = 0; i < n; ++i)
|
||||
boxes.getBounds()[i] = items[i];
|
||||
Gu::buildAABBTree(n, boxes, bvh.tree);
|
||||
}
|
||||
53
engine/third_party/physx/source/physxextensions/src/tet/ExtBVH.h
vendored
Normal file
53
engine/third_party/physx/source/physxextensions/src/tet/ExtBVH.h
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
// 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.
|
||||
|
||||
|
||||
#ifndef EXT_BVH_H
|
||||
#define EXT_BVH_H
|
||||
|
||||
#include "foundation/PxBounds3.h"
|
||||
#include "foundation/PxArray.h"
|
||||
#include "GuAABBTreeNode.h"
|
||||
|
||||
namespace physx
|
||||
{
|
||||
namespace Ext
|
||||
{
|
||||
struct BVHDesc
|
||||
{
|
||||
PxArray<Gu::BVHNode> tree;
|
||||
void query(const PxBounds3& bounds, PxArray<PxI32>& items);
|
||||
};
|
||||
|
||||
class BVHBuilder
|
||||
{
|
||||
public:
|
||||
static void build(BVHDesc& bvh, const PxBounds3* items, PxI32 numItems);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
2382
engine/third_party/physx/source/physxextensions/src/tet/ExtDelaunayBoundaryInserter.cpp
vendored
Normal file
2382
engine/third_party/physx/source/physxextensions/src/tet/ExtDelaunayBoundaryInserter.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
85
engine/third_party/physx/source/physxextensions/src/tet/ExtDelaunayBoundaryInserter.h
vendored
Normal file
85
engine/third_party/physx/source/physxextensions/src/tet/ExtDelaunayBoundaryInserter.h
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
// 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.
|
||||
|
||||
#ifndef EXT_DELAUNAY_BOUNDARY_INSERTER_H
|
||||
#define EXT_DELAUNAY_BOUNDARY_INSERTER_H
|
||||
|
||||
#include "foundation/PxHashSet.h"
|
||||
#include "foundation/PxVec3.h"
|
||||
#include "foundation/PxMat33.h"
|
||||
#include "foundation/PxHashMap.h"
|
||||
#include "foundation/PxArray.h"
|
||||
#include "common/PxCoreUtilityTypes.h"
|
||||
#include "extensions/PxTriangleMeshAnalysisResult.h"
|
||||
#include "extensions/PxTetrahedronMeshAnalysisResult.h"
|
||||
|
||||
#include "ExtDelaunayTetrahedralizer.h"
|
||||
namespace physx
|
||||
{
|
||||
namespace Ext
|
||||
{
|
||||
//Generates a tetmesh that matches the specified triangle mesh. All triangle mesh points are present in the tetmesh, additional points on edges and faces might be present.
|
||||
void generateTetmesh(const PxBoundedData& inputPoints, const PxBoundedData& inputTriangles, const bool has16bitIndices, PxArray<PxVec3>& tetPoints, PxArray<PxU32>& finalTets);
|
||||
|
||||
//Generates a tetmesh that that matches the surface of the input tetmesh approximately but creats very regular shaped tetrahedra.
|
||||
void generateVoxelTetmesh(const PxBoundedData& inputPoints, const PxBoundedData& inputTets, PxU32 numVoxelsX, PxU32 numVoxelsY, PxU32 numVoxelsZ,
|
||||
PxArray<PxVec3>& voxelPoints, PxArray<PxU32>& voxelTets, PxI32* intputPointToOutputTetIndex, const PxU32* anchorNodeIndices = NULL, PxU32 numTetsPerVoxel = 5,
|
||||
bool avoidVoxelDuplication = false);
|
||||
|
||||
//Generates a tetmesh that that matches the surface of the input tetmesh approximately but creats very regular shaped tetrahedra.
|
||||
void generateVoxelTetmesh(const PxBoundedData& inputPoints, const PxBoundedData& inputTets, PxReal voxelEdgeLength,
|
||||
PxArray<PxVec3>& voxelPoints, PxArray<PxU32>& voxelTets, PxI32* intputPointToOutputTetIndex, const PxU32* anchorNodeIndices = NULL, PxU32 numTetsPerVoxel = 5,
|
||||
bool avoidVoxelDuplication = false);
|
||||
|
||||
//Generates a tetmesh that that matches the surface of the input tetmesh approximately but creats very regular shaped tetrahedra.
|
||||
void generateVoxelTetmesh(const PxBoundedData& inputPoints, const PxBoundedData& inputTets, PxU32 numVoxelsAlongLongestBoundingBoxAxis,
|
||||
PxArray<PxVec3>& voxelPoints, PxArray<PxU32>& voxelTets, PxI32* intputPointToOutputTetIndex, const PxU32* anchorNodeIndices = NULL, PxU32 numTetsPerVoxel = 5,
|
||||
bool avoidVoxelDuplication = false);
|
||||
|
||||
//Extracts the surface triangles from the specified tetrahedra
|
||||
void extractTetmeshSurface(const PxArray<PxI32>& tets, PxArray<PxI32>& triangles);
|
||||
|
||||
//Computes the lumped mass per vertex for the specified tetmesh
|
||||
void pointMasses(const PxArray<PxVec3>& tetVerts, const PxArray<PxU32>& tets, PxF32 density, PxArray<PxF32>& mass);
|
||||
|
||||
//Computes a rest pose matrix for every tetrahedron in the specified tetmesh
|
||||
void restPoses(const PxArray<PxVec3>& tetVerts, const PxArray<PxU32>& tets, PxArray<PxMat33>& restPoses);
|
||||
|
||||
//Computes a fiber direction for every tetrahedron in the specified tetmesh. Currently just returns dummy values.
|
||||
void tetFibers(const PxArray<PxVec3>& tetVerts, const PxArray<PxU32>& tets, PxArray<PxVec3>& tetFibers);
|
||||
|
||||
//Analyzes the triangle mesh to get a report about deficiencies. Some deficiencies can be handled by the tetmesher, others cannot.
|
||||
PxTriangleMeshAnalysisResults validateTriangleMesh(const PxBoundedData& points, const PxBoundedData& triangles, const bool has16BitIndices, const PxReal minVolumeThreshold = 1e-6f, const PxReal minTriangleAngleRadians = 10.0f*3.1415926535898f / 180.0f);
|
||||
|
||||
//Analyzes the tetrahedron mesh to get a report about deficiencies. Some deficiencies can be handled by the softbody cooker, others cannot.
|
||||
PxTetrahedronMeshAnalysisResults validateTetrahedronMesh(const PxBoundedData& points, const PxBoundedData& tetrahedra, const bool has16BitIndices, const PxReal minTetVolumeThreshold = 1e-8f);
|
||||
|
||||
PxU32 removeDisconnectedIslands(PxI32* finalTets, PxU32 numTets);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
2175
engine/third_party/physx/source/physxextensions/src/tet/ExtDelaunayTetrahedralizer.cpp
vendored
Normal file
2175
engine/third_party/physx/source/physxextensions/src/tet/ExtDelaunayTetrahedralizer.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
362
engine/third_party/physx/source/physxextensions/src/tet/ExtDelaunayTetrahedralizer.h
vendored
Normal file
362
engine/third_party/physx/source/physxextensions/src/tet/ExtDelaunayTetrahedralizer.h
vendored
Normal file
@@ -0,0 +1,362 @@
|
||||
// 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.
|
||||
|
||||
#ifndef EXT_DELAUNAY_TETRAHEDRALIZER_H
|
||||
#define EXT_DELAUNAY_TETRAHEDRALIZER_H
|
||||
|
||||
#include "foundation/PxArray.h"
|
||||
#include "foundation/PxVec3.h"
|
||||
#include "foundation/PxHashSet.h"
|
||||
#include "GuTetrahedron.h"
|
||||
#include "ExtVec3.h"
|
||||
|
||||
namespace physx
|
||||
{
|
||||
|
||||
namespace Ext
|
||||
{
|
||||
using Edge = PxPair<PxI32, PxI32>;
|
||||
using Tetrahedron = Gu::TetrahedronT<PxI32>;
|
||||
using Tetrahedron16 = Gu::TetrahedronT<PxI16>;
|
||||
|
||||
void buildNeighborhood(const PxArray<Tetrahedron>& tets, PxArray<PxI32>& result);
|
||||
void buildNeighborhood(const PxI32* tets, PxU32 numTets, PxArray<PxI32>& result);
|
||||
|
||||
PX_FORCE_INLINE PxF64 tetVolume(const PxVec3d& a, const PxVec3d& b, const PxVec3d& c, const PxVec3d& d)
|
||||
{
|
||||
return (-1.0 / 6.0) * (a - d).dot((b - d).cross(c - d));
|
||||
}
|
||||
|
||||
PX_FORCE_INLINE PxF64 tetVolume(const Tetrahedron& tet, const PxArray<PxVec3d>& points)
|
||||
{
|
||||
return tetVolume(points[tet[0]], points[tet[1]], points[tet[2]], points[tet[3]]);
|
||||
}
|
||||
|
||||
//Returns the intersection (set operation) of two sorted lists
|
||||
PX_FORCE_INLINE void intersectionOfSortedLists(const PxArray<PxI32>& sorted1, const PxArray<PxI32>& sorted2, PxArray<PxI32>& result)
|
||||
{
|
||||
PxU32 a = 0;
|
||||
PxU32 b = 0;
|
||||
result.clear();
|
||||
while (a < sorted1.size() && b < sorted2.size())
|
||||
{
|
||||
if (sorted1[a] == sorted2[b])
|
||||
{
|
||||
result.pushBack(sorted1[a]);
|
||||
++a;
|
||||
++b;
|
||||
}
|
||||
else if (sorted1[a] > sorted2[b])
|
||||
++b;
|
||||
else
|
||||
++a;
|
||||
}
|
||||
}
|
||||
|
||||
PX_FORCE_INLINE bool intersectionOfSortedListsContainsElements(const PxArray<PxI32>& sorted1, const PxArray<PxI32>& sorted2)
|
||||
{
|
||||
PxU32 a = 0;
|
||||
PxU32 b = 0;
|
||||
while (a < sorted1.size() && b < sorted2.size())
|
||||
{
|
||||
if (sorted1[a] == sorted2[b])
|
||||
return true;
|
||||
else if (sorted1[a] > sorted2[b])
|
||||
++b;
|
||||
else
|
||||
++a;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
class BaseTetAnalyzer
|
||||
{
|
||||
public:
|
||||
virtual PxF64 quality(const PxArray<PxI32> tetIndices) const = 0;
|
||||
|
||||
virtual PxF64 quality(const PxArray<Tetrahedron> tetrahedraToCheck) const = 0;
|
||||
|
||||
virtual bool improved(PxF64 previousQuality, PxF64 newQuality) const = 0;
|
||||
|
||||
virtual ~BaseTetAnalyzer() {}
|
||||
};
|
||||
|
||||
class MinimizeMaxAmipsEnergy : public BaseTetAnalyzer
|
||||
{
|
||||
const PxArray<PxVec3d>& points;
|
||||
const PxArray<Tetrahedron>& tetrahedra;
|
||||
|
||||
public:
|
||||
MinimizeMaxAmipsEnergy(const PxArray<PxVec3d>& points_, const PxArray<Tetrahedron>& tetrahedra_) : points(points_), tetrahedra(tetrahedra_)
|
||||
{}
|
||||
|
||||
PxF64 quality(const PxArray<PxI32> tetIndices) const;
|
||||
|
||||
PxF64 quality(const PxArray<Tetrahedron> tetrahedraToCheck) const;
|
||||
|
||||
bool improved(PxF64 previousQuality, PxF64 newQuality) const;
|
||||
|
||||
virtual ~MinimizeMaxAmipsEnergy() {}
|
||||
|
||||
private:
|
||||
PX_NOCOPY(MinimizeMaxAmipsEnergy)
|
||||
};
|
||||
|
||||
class MaximizeMinTetVolume : public BaseTetAnalyzer
|
||||
{
|
||||
const PxArray<PxVec3d>& points;
|
||||
const PxArray<Tetrahedron>& tetrahedra;
|
||||
|
||||
public:
|
||||
MaximizeMinTetVolume(const PxArray<PxVec3d>& points_, const PxArray<Tetrahedron>& tetrahedra_) : points(points_), tetrahedra(tetrahedra_)
|
||||
{}
|
||||
|
||||
PxF64 quality(const PxArray<PxI32> tetIndices) const;
|
||||
|
||||
PxF64 quality(const PxArray<Tetrahedron> tetrahedraToCheck) const;
|
||||
|
||||
bool improved(PxF64 previousQuality, PxF64 newQuality) const;
|
||||
|
||||
virtual ~MaximizeMinTetVolume() {}
|
||||
|
||||
private:
|
||||
PX_NOCOPY(MaximizeMinTetVolume)
|
||||
};
|
||||
|
||||
|
||||
|
||||
//Helper class to extract surface triangles from a tetmesh
|
||||
struct SortedTriangle
|
||||
{
|
||||
public:
|
||||
PxI32 A;
|
||||
PxI32 B;
|
||||
PxI32 C;
|
||||
bool Flipped;
|
||||
|
||||
PX_FORCE_INLINE SortedTriangle(PxI32 a, PxI32 b, PxI32 c)
|
||||
{
|
||||
A = a; B = b; C = c; Flipped = false;
|
||||
if (A > B) { PxSwap(A, B); Flipped = !Flipped; }
|
||||
if (B > C) { PxSwap(B, C); Flipped = !Flipped; }
|
||||
if (A > B) { PxSwap(A, B); Flipped = !Flipped; }
|
||||
}
|
||||
};
|
||||
|
||||
struct TriangleHash
|
||||
{
|
||||
PX_FORCE_INLINE std::size_t operator()(const SortedTriangle& k) const
|
||||
{
|
||||
return k.A ^ k.B ^ k.C;
|
||||
}
|
||||
|
||||
PX_FORCE_INLINE bool equal(const SortedTriangle& first, const SortedTriangle& second) const
|
||||
{
|
||||
return first.A == second.A && first.B == second.B && first.C == second.C;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct RecoverEdgeMemoryCache
|
||||
{
|
||||
PxArray<PxI32> facesStart;
|
||||
PxHashSet<PxI32> tetsDoneStart;
|
||||
PxArray<PxI32> resultStart;
|
||||
PxArray<PxI32> facesEnd;
|
||||
PxHashSet<PxI32> tetsDoneEnd;
|
||||
PxArray<PxI32> resultEnd;
|
||||
};
|
||||
|
||||
class StackMemory
|
||||
{
|
||||
public:
|
||||
|
||||
void clear()
|
||||
{
|
||||
faces.forceSize_Unsafe(0);
|
||||
hashSet.clear();
|
||||
}
|
||||
|
||||
PxArray<PxI32> faces;
|
||||
PxHashSet<PxI32> hashSet;
|
||||
|
||||
};
|
||||
|
||||
//Incremental delaunay tetrahedralizer
|
||||
class DelaunayTetrahedralizer
|
||||
{
|
||||
public:
|
||||
//The bounds specified must contain all points that will get inserted by calling insertPoints.
|
||||
DelaunayTetrahedralizer(const PxVec3d& min, const PxVec3d& max);
|
||||
|
||||
DelaunayTetrahedralizer(PxArray<PxVec3d>& points, PxArray<Tetrahedron>& tets);
|
||||
|
||||
void initialize(PxArray<PxVec3d>& points, PxArray<Tetrahedron>& tets);
|
||||
|
||||
//Inserts a bunch of new points into the tetrahedralization and keeps the delaunay condition satisfied. The new result will
|
||||
//get stored in the tetrahedra array. Points to insert must already be present in inPoints, the indices of the points to insert
|
||||
//can be controlled with start and end index (end index is exclusive, start index is inclusive)
|
||||
void insertPoints(const PxArray<PxVec3d>& inPoints, PxI32 start, PxI32 end, PxArray<Tetrahedron>& tetrahedra);
|
||||
|
||||
bool insertPoints(const PxArray<PxVec3d>& inPoints, PxI32 start, PxI32 end);
|
||||
|
||||
void exportTetrahedra(PxArray<Tetrahedron>& tetrahedra);
|
||||
|
||||
bool canCollapseEdge(PxI32 edgeVertexToKeep, PxI32 edgeVertexToRemove, PxF64 volumeChangeThreshold = 0.1, BaseTetAnalyzer* tetAnalyzer = NULL);
|
||||
bool canCollapseEdge(PxI32 edgeVertexToKeep, PxI32 edgeVertexToRemove, const PxArray<PxI32>& tetsConnectedToA, const PxArray<PxI32>& tetsConnectedToB,
|
||||
PxF64& qualityAfterCollapse, PxF64 volumeChangeThreshold = 0.1, BaseTetAnalyzer* tetAnalyzer = NULL);
|
||||
|
||||
void collapseEdge(PxI32 edgeVertexToKeep, PxI32 edgeVertexToRemove);
|
||||
|
||||
void collapseEdge(PxI32 edgeVertexAToKeep, PxI32 edgeVertexBToRemove, const PxArray<PxI32>& tetsConnectedToA, const PxArray<PxI32>& tetsConnectedToB);
|
||||
|
||||
void collectTetsConnectedToVertex(PxI32 vertexIndex, PxArray<PxI32>& tetIds);
|
||||
|
||||
void collectTetsConnectedToVertex(PxArray<PxI32>& faces, PxHashSet<PxI32>& tetsDone, PxI32 vertexIndex, PxArray<PxI32>& tetIds);
|
||||
|
||||
void collectTetsConnectedToEdge(PxI32 edgeStart, PxI32 edgeEnd, PxArray<PxI32>& tetIds);
|
||||
|
||||
PX_FORCE_INLINE const PxVec3d& point(PxI32 index) const { return centeredNormalizedPoints[index]; }
|
||||
|
||||
PX_FORCE_INLINE PxU32 numPoints() const { return centeredNormalizedPoints.size(); }
|
||||
|
||||
PX_FORCE_INLINE PxArray<PxVec3d>& points() { return centeredNormalizedPoints; }
|
||||
PX_FORCE_INLINE const PxArray<PxVec3d>& points() const { return centeredNormalizedPoints; }
|
||||
|
||||
PxU32 addPoint(const PxVec3d& p)
|
||||
{
|
||||
centeredNormalizedPoints.pushBack(p);
|
||||
vertexToTet.pushBack(-1);
|
||||
return centeredNormalizedPoints.size() - 1;
|
||||
}
|
||||
|
||||
PX_FORCE_INLINE const Tetrahedron& tetrahedron(PxI32 index) const { return result[index]; }
|
||||
|
||||
PX_FORCE_INLINE PxU32 numTetrahedra() const { return result.size(); }
|
||||
|
||||
PX_FORCE_INLINE PxArray<Tetrahedron>& tetrahedra() { return result; }
|
||||
PX_FORCE_INLINE const PxArray<Tetrahedron>& tetrahedra() const { return result; }
|
||||
|
||||
void copyInternalPointsTo(PxArray<PxVec3d>& points) { points = centeredNormalizedPoints; }
|
||||
|
||||
bool optimizeByFlipping(PxArray<PxI32>& affectedFaces, const BaseTetAnalyzer& qualityAnalyzer);
|
||||
|
||||
void insertPointIntoEdge(PxI32 newPointIndex, PxI32 edgeA, PxI32 edgeB, PxArray<PxI32>& affectedTets, BaseTetAnalyzer* qualityAnalyzer = NULL);
|
||||
|
||||
bool removeEdgeByFlip(PxI32 edgeA, PxI32 edgeB, PxArray<PxI32>& tetIndices, BaseTetAnalyzer* qualityAnalyzer = NULL);
|
||||
|
||||
void addLockedEdges(const PxArray<Gu::IndexedTriangleT<PxI32>>& triangles);
|
||||
|
||||
void addLockedTriangles(const PxArray<Gu::IndexedTriangleT<PxI32>>& triangles);
|
||||
|
||||
void clearLockedEdges() { lockedEdges.clear(); }
|
||||
|
||||
void clearLockedTriangles() { lockedTriangles.clear(); }
|
||||
|
||||
bool recoverEdgeByFlip(PxI32 eStart, PxI32 eEnd, RecoverEdgeMemoryCache& cache);
|
||||
|
||||
void generateTetmeshEnforcingEdges(const PxArray<PxVec3d>& trianglePoints, const PxArray<Gu::IndexedTriangleT<PxI32>>& triangles, PxArray<PxArray<PxI32>>& allEdges,
|
||||
PxArray<PxArray<PxI32>>& pointToOriginalTriangle,
|
||||
PxArray<PxVec3d>& points, PxArray<Tetrahedron>& finalTets);
|
||||
|
||||
private:
|
||||
PxArray<PxVec3d> centeredNormalizedPoints;
|
||||
PxArray<PxI32> neighbors;
|
||||
PxArray<PxI32> unusedTets;
|
||||
PxArray<PxI32> vertexToTet;
|
||||
PxArray<Tetrahedron> result;
|
||||
PxI32 numAdditionalPointsAtBeginning = 4;
|
||||
|
||||
PxHashSet<SortedTriangle, TriangleHash> lockedTriangles;
|
||||
PxHashSet<PxU64> lockedEdges;
|
||||
|
||||
StackMemory stackMemory;
|
||||
PX_NOCOPY(DelaunayTetrahedralizer)
|
||||
};
|
||||
|
||||
struct EdgeWithLength
|
||||
{
|
||||
PxI32 A;
|
||||
PxI32 B;
|
||||
PxF64 Length;
|
||||
|
||||
EdgeWithLength(PxI32 a_, PxI32 b_, PxF64 length_)
|
||||
{
|
||||
A = a_;
|
||||
B = b_;
|
||||
Length = length_;
|
||||
}
|
||||
};
|
||||
|
||||
PX_FORCE_INLINE bool operator <(const EdgeWithLength& lhs, const EdgeWithLength& rhs)
|
||||
{
|
||||
return lhs.Length < rhs.Length;
|
||||
}
|
||||
|
||||
struct SplitEdge
|
||||
{
|
||||
PxI32 A;
|
||||
PxI32 B;
|
||||
PxF64 Q;
|
||||
PxF64 L;
|
||||
bool InteriorEdge;
|
||||
|
||||
SplitEdge(PxI32 a, PxI32 b, PxF64 q, PxF64 l, bool interiorEdge)
|
||||
{
|
||||
A = a;
|
||||
B = b;
|
||||
Q = q;
|
||||
L = l;
|
||||
InteriorEdge = interiorEdge;
|
||||
}
|
||||
};
|
||||
|
||||
PX_FORCE_INLINE bool operator >(const SplitEdge& lhs, const SplitEdge& rhs)
|
||||
{
|
||||
if (lhs.Q == rhs.Q)
|
||||
return lhs.L > rhs.L;
|
||||
return lhs.Q > rhs.Q;
|
||||
}
|
||||
|
||||
|
||||
bool optimizeByCollapsing(DelaunayTetrahedralizer& del, const PxArray<EdgeWithLength>& edges,
|
||||
PxArray<PxArray<PxI32>>& pointToOriginalTriangle, PxI32 numFixPoints, BaseTetAnalyzer* qualityAnalyzer = NULL);
|
||||
|
||||
bool optimizeBySwapping(DelaunayTetrahedralizer& del, const PxArray<EdgeWithLength>& edges,
|
||||
const PxArray<PxArray<PxI32>>& pointToOriginalTriangle, BaseTetAnalyzer* qualityAnalyzer);
|
||||
|
||||
bool optimizeBySplitting(DelaunayTetrahedralizer& del, const PxArray<EdgeWithLength>& edges, const PxArray<PxArray<PxI32>>& pointToOriginalTriangle,
|
||||
PxI32 maxPointsToInsert = -1, bool sortByQuality = false, BaseTetAnalyzer* qualityAnalyzer = NULL, PxF64 qualityThreshold = 10);
|
||||
|
||||
//Modified tetmesh quality improvement implementation of the method described in https://cs.nyu.edu/~yixinhu/tetwild.pdf Section 3.2 Mesh Improvement
|
||||
void optimize(DelaunayTetrahedralizer& del, PxArray<PxArray<PxI32>>& pointToOriginalTriangle, PxI32 numFixPoints,
|
||||
PxArray<PxVec3d>& optimizedPoints, PxArray<Tetrahedron>& optimizedTets, PxI32 numPasses = 10);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
53
engine/third_party/physx/source/physxextensions/src/tet/ExtFastWindingNumber.cpp
vendored
Normal file
53
engine/third_party/physx/source/physxextensions/src/tet/ExtFastWindingNumber.cpp
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
// 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.
|
||||
|
||||
#include "ExtFastWindingNumber.h"
|
||||
#include "foundation/PxSort.h"
|
||||
|
||||
#include "foundation/PxMath.h"
|
||||
|
||||
#include "ExtUtilities.h"
|
||||
|
||||
//The following paper explains all the techniques used in this file
|
||||
//http://www.dgp.toronto.edu/projects/fast-winding-numbers/fast-winding-numbers-for-soups-and-clouds-siggraph-2018-barill-et-al.pdf
|
||||
|
||||
namespace physx
|
||||
{
|
||||
namespace Ext
|
||||
{
|
||||
PxF64 computeWindingNumber(const PxArray<Gu::BVHNode>& tree, const PxVec3d& q, PxF64 beta, const PxHashMap<PxU32, ClusterApproximationF64>& clusters,
|
||||
const PxArray<Triangle>& triangles, const PxArray<PxVec3d>& points)
|
||||
{
|
||||
return Gu::computeWindingNumber<PxF64, PxVec3d>(tree.begin(), q, beta, clusters, reinterpret_cast<const PxU32*>(triangles.begin()), points.begin());
|
||||
}
|
||||
|
||||
void precomputeClusterInformation(PxArray<Gu::BVHNode>& tree, const PxArray<Triangle>& triangles,
|
||||
const PxArray<PxVec3d>& points, PxHashMap<PxU32, ClusterApproximationF64>& result, PxI32 rootNodeIndex)
|
||||
{
|
||||
Gu::precomputeClusterInformation<PxF64, PxVec3d>(tree.begin(), reinterpret_cast<const PxU32*>(triangles.begin()), triangles.size(), points.begin(), result, rootNodeIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
53
engine/third_party/physx/source/physxextensions/src/tet/ExtFastWindingNumber.h
vendored
Normal file
53
engine/third_party/physx/source/physxextensions/src/tet/ExtFastWindingNumber.h
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
// 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.
|
||||
|
||||
#ifndef EXT_FAST_WINDING_NUMBER_H
|
||||
#define EXT_FAST_WINDING_NUMBER_H
|
||||
|
||||
|
||||
#include "ExtVec3.h"
|
||||
#include "GuWindingNumberT.h"
|
||||
|
||||
namespace physx
|
||||
{
|
||||
namespace Ext
|
||||
{
|
||||
using Triangle = Gu::IndexedTriangleT<PxI32>;
|
||||
using Triangle16 = Gu::IndexedTriangleT<PxI16>;
|
||||
|
||||
typedef Gu::ClusterApproximationT<PxF64, PxVec3d> ClusterApproximationF64;
|
||||
typedef Gu::SecondOrderClusterApproximationT<PxF64, PxVec3d> SecondOrderClusterApproximationF64;
|
||||
|
||||
PxF64 computeWindingNumber(const PxArray<Gu::BVHNode>& tree, const PxVec3d& q, PxF64 beta, const PxHashMap<PxU32, ClusterApproximationF64>& clusters,
|
||||
const PxArray<Triangle>& triangles, const PxArray<PxVec3d>& points);
|
||||
|
||||
void precomputeClusterInformation(PxArray<Gu::BVHNode>& tree, const PxArray<Triangle>& triangles,
|
||||
const PxArray<PxVec3d>& points, PxHashMap<PxU32, ClusterApproximationF64>& result, PxI32 rootNodeIndex = 0);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
192
engine/third_party/physx/source/physxextensions/src/tet/ExtInsideTester.cpp
vendored
Normal file
192
engine/third_party/physx/source/physxextensions/src/tet/ExtInsideTester.cpp
vendored
Normal file
@@ -0,0 +1,192 @@
|
||||
// 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.
|
||||
|
||||
#include "ExtInsideTester.h"
|
||||
#include "foundation/PxBounds3.h"
|
||||
|
||||
using namespace physx;
|
||||
using namespace Ext;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
void InsideTester::init(const PxVec3 *vertices, PxI32 numVertices, const PxI32 *triIndices, PxI32 numTris)
|
||||
{
|
||||
PxArray<PxI32> newIds(numVertices, -1);
|
||||
|
||||
mVertices.clear();
|
||||
mIndices.clear();
|
||||
|
||||
for (PxI32 i = 0; i < 3 * numTris; i++)
|
||||
{
|
||||
PxI32 id = triIndices[i];
|
||||
if (newIds[id] < 0)
|
||||
{
|
||||
newIds[id] = PxI32(mVertices.size());
|
||||
mVertices.pushBack(vertices[id]);
|
||||
}
|
||||
mIndices.pushBack(newIds[id]);
|
||||
}
|
||||
|
||||
mGrids[0].init(0, mVertices, mIndices);
|
||||
mGrids[1].init(1, mVertices, mIndices);
|
||||
mGrids[2].init(2, mVertices, mIndices);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
bool InsideTester::isInside(const PxVec3 &pos)
|
||||
{
|
||||
PxI32 vote = 0;
|
||||
vote += mGrids[0].numInside(pos, mVertices, mIndices);
|
||||
vote += mGrids[1].numInside(pos, mVertices, mIndices);
|
||||
vote += mGrids[2].numInside(pos, mVertices, mIndices);
|
||||
return (vote > 3);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
void InsideTester::Grid2d::init(PxI32 _dim0, const PxArray<PxVec3> &vertices, const PxArray<PxI32> &indices)
|
||||
{
|
||||
first.clear();
|
||||
tris.clear();
|
||||
next.clear();
|
||||
num1 = num2 = 0;
|
||||
|
||||
this->dim0 = _dim0;
|
||||
PxI32 dim1 = (_dim0 + 1) % 3;
|
||||
PxI32 dim2 = (_dim0 + 2) % 3;
|
||||
|
||||
PxI32 numTris = PxI32(indices.size()) / 3;
|
||||
if (numTris == 0)
|
||||
return;
|
||||
|
||||
PxBounds3 bounds, triBounds;
|
||||
bounds.setEmpty();
|
||||
PxReal avgSize = 0.0f;
|
||||
|
||||
for (PxI32 i = 0; i < numTris; i++)
|
||||
{
|
||||
triBounds.setEmpty();
|
||||
triBounds.include(vertices[indices[3 * i]]);
|
||||
triBounds.include(vertices[indices[3 * i + 1]]);
|
||||
triBounds.include(vertices[indices[3 * i + 2]]);
|
||||
triBounds.minimum[dim0] = 0.0f;
|
||||
triBounds.maximum[dim0] = 0.0f;
|
||||
avgSize += triBounds.getDimensions().magnitude();
|
||||
bounds.include(triBounds);
|
||||
}
|
||||
if (bounds.isEmpty())
|
||||
return;
|
||||
|
||||
avgSize /= PxReal(numTris);
|
||||
|
||||
orig = bounds.minimum;
|
||||
spacing = avgSize;
|
||||
|
||||
num1 = PxI32((bounds.maximum[dim1] - orig[dim1]) / spacing) + 2;
|
||||
num2 = PxI32((bounds.maximum[dim2] - orig[dim2]) / spacing) + 2;
|
||||
first.clear();
|
||||
first.resize(num1 * num2, -1);
|
||||
|
||||
for (PxI32 i = 0; i < numTris; i++)
|
||||
{
|
||||
triBounds.setEmpty();
|
||||
triBounds.include(vertices[indices[3 * i]] - orig);
|
||||
triBounds.include(vertices[indices[3 * i + 1]] - orig);
|
||||
triBounds.include(vertices[indices[3 * i + 2]] - orig);
|
||||
|
||||
PxI32 min1 = PxI32(triBounds.minimum[dim1] / spacing);
|
||||
PxI32 min2 = PxI32(triBounds.minimum[dim2] / spacing);
|
||||
PxI32 max1 = PxI32(triBounds.maximum[dim1] / spacing);
|
||||
PxI32 max2 = PxI32(triBounds.maximum[dim2] / spacing);
|
||||
for (PxI32 i1 = min1; i1 <= max1; i1++)
|
||||
{
|
||||
for (PxI32 i2 = min2; i2 <= max2; i2++)
|
||||
{
|
||||
PxI32 nr = i1 * num2 + i2;
|
||||
next.pushBack(first[nr]);
|
||||
first[nr] = PxI32(tris.size());
|
||||
tris.pushBack(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
PxI32 InsideTester::Grid2d::numInside(const PxVec3 &pos, const PxArray<PxVec3> &vertices, const PxArray<PxI32> &indices)
|
||||
{
|
||||
if (first.empty())
|
||||
return 0;
|
||||
|
||||
PxI32 dim1 = (dim0 + 1) % 3;
|
||||
PxI32 dim2 = (dim0 + 2) % 3;
|
||||
|
||||
PxReal r = 1e-5f;
|
||||
PxVec3 p = pos;
|
||||
|
||||
p[dim1] = pos[dim1] + rnd.rand(0.0f, r);
|
||||
p[dim2] = pos[dim2] + rnd.rand(0.0f, r);
|
||||
|
||||
PxI32 i1 = PxI32((p[dim1] - orig[dim1]) / spacing);
|
||||
PxI32 i2 = PxI32((p[dim2] - orig[dim2]) / spacing);
|
||||
if (i1 < 0 || i1 >= num1 || i2 < 0 || i2 >= num2)
|
||||
return false;
|
||||
|
||||
PxI32 count1 = 0;
|
||||
PxI32 count2 = 0;
|
||||
|
||||
PxI32 nr = first[i1 * num2 + i2];
|
||||
while (nr >= 0)
|
||||
{
|
||||
PxI32 triNr = tris[nr];
|
||||
nr = next[nr];
|
||||
|
||||
const PxVec3 &p0 = vertices[indices[3 * triNr]];
|
||||
const PxVec3 &p1 = vertices[indices[3 * triNr + 1]];
|
||||
const PxVec3 &p2 = vertices[indices[3 * triNr + 2]];
|
||||
|
||||
bool side0 = (p1 - p0).cross(p - p0)[dim0] > 0.0f;
|
||||
bool side1 = (p2 - p1).cross(p - p1)[dim0] > 0.0f;
|
||||
bool side2 = (p0 - p2).cross(p - p2)[dim0] > 0.0f;
|
||||
if (side0 != side1 || side1 != side2)
|
||||
continue;
|
||||
|
||||
// ray triangle intersection
|
||||
|
||||
PxVec3 n = (p1 - p0).cross(p2 - p0);
|
||||
if (n[dim0] == 0.0f)
|
||||
continue;
|
||||
PxReal t = (p0 - p).dot(n) / n[dim0];
|
||||
if (t > 0.0f)
|
||||
count1++;
|
||||
else if (t < 0.0f)
|
||||
count2++;
|
||||
}
|
||||
|
||||
PxI32 num = 0;
|
||||
if ((count1 % 2) == 1)
|
||||
num++;
|
||||
if ((count2 % 2) == 1)
|
||||
num++;
|
||||
return num;
|
||||
}
|
||||
76
engine/third_party/physx/source/physxextensions/src/tet/ExtInsideTester.h
vendored
Normal file
76
engine/third_party/physx/source/physxextensions/src/tet/ExtInsideTester.h
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
// 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 EXT_INSIDE_TESTER_H
|
||||
#define EXT_INSIDE_TESTER_H
|
||||
|
||||
// MM: tester whether a point is inside a triangle mesh
|
||||
// all faces are projected onto the 3 canonical planes and hashed
|
||||
// for fast ray mesh intersections
|
||||
|
||||
#include "foundation/PxVec3.h"
|
||||
#include "foundation/PxArray.h"
|
||||
#include "foundation/PxQuat.h"
|
||||
#include "CmRandom.h"
|
||||
|
||||
namespace physx
|
||||
{
|
||||
namespace Ext
|
||||
{
|
||||
|
||||
// ----------------------------------------------------------
|
||||
class InsideTester
|
||||
{
|
||||
public:
|
||||
void init(const PxVec3 *vertices, PxI32 numVertices, const PxI32 *triIndices, PxI32 numTris);
|
||||
bool isInside(const PxVec3& pos);
|
||||
|
||||
private:
|
||||
PxArray<PxVec3> mVertices;
|
||||
PxArray<PxI32> mIndices;
|
||||
|
||||
struct Grid2d
|
||||
{
|
||||
void init(PxI32 dim0, const PxArray<PxVec3> &vertices, const PxArray<PxI32> &indices);
|
||||
PxI32 numInside(const PxVec3&pos, const PxArray<PxVec3> &vertices, const PxArray<PxI32> &indices);
|
||||
PxI32 dim0;
|
||||
PxVec3 orig;
|
||||
PxI32 num1, num2;
|
||||
float spacing;
|
||||
PxArray<PxI32> first;
|
||||
PxArray<PxI32> tris;
|
||||
PxArray<int> next;
|
||||
|
||||
Cm::RandomR250 rnd = Cm::RandomR250(0);
|
||||
};
|
||||
Grid2d mGrids[3];
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
174
engine/third_party/physx/source/physxextensions/src/tet/ExtMarchingCubesTable.h
vendored
Normal file
174
engine/third_party/physx/source/physxextensions/src/tet/ExtMarchingCubesTable.h
vendored
Normal file
@@ -0,0 +1,174 @@
|
||||
// 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.
|
||||
|
||||
#ifndef EXT_MARCHING_CUBES_TABLE_H
|
||||
#define EXT_MARCHING_CUBES_TABLE_H
|
||||
|
||||
namespace physx
|
||||
{
|
||||
namespace Ext
|
||||
{
|
||||
|
||||
// point numbering
|
||||
|
||||
// 7-----------6
|
||||
// /| /|
|
||||
// / | / |
|
||||
// / | / |
|
||||
// 4-----------5 |
|
||||
// | | | |
|
||||
// | 3-------|---2
|
||||
// | / | /
|
||||
// | / | /
|
||||
// |/ |/
|
||||
// 0-----------1
|
||||
|
||||
// edge numbering
|
||||
|
||||
// *-----6-----*
|
||||
// /| /|
|
||||
// 7 | 5 |
|
||||
// / 11 / 10
|
||||
// *-----4-----* |
|
||||
// | | | |
|
||||
// | *-----2-|---*
|
||||
// 8 / 9 /
|
||||
// | 3 | 1
|
||||
// |/ |/
|
||||
// *-----0-----*
|
||||
|
||||
|
||||
// z
|
||||
// | y
|
||||
// | /
|
||||
// |/
|
||||
// 0---- x
|
||||
|
||||
int marchingCubeCorners[8][3] = { {0,0,0}, {1,0,0},{1,1,0},{0,1,0}, {0,0,1}, {1,0,1},{1,1,1},{0,1,1} };
|
||||
int marchingCubeEdges[12][2] = { {0,1},{1,2},{2,3},{3,0},{4,5},{5,6},{6,7},{7,4},{0,4},{1,5},{2,6},{3,7} };
|
||||
|
||||
int firstMarchingCubesId[257] = {
|
||||
0, 0, 3, 6, 12, 15, 21, 27, 36, 39, 45, 51, 60, 66, 75, 84, 90, 93, 99, 105, 114,
|
||||
120, 129, 138, 150, 156, 165, 174, 186, 195, 207, 219, 228, 231, 237, 243, 252, 258, 267, 276, 288,
|
||||
294, 303, 312, 324, 333, 345, 357, 366, 372, 381, 390, 396, 405, 417, 429, 438, 447, 459, 471, 480,
|
||||
492, 507, 522, 528, 531, 537, 543, 552, 558, 567, 576, 588, 594, 603, 612, 624, 633, 645, 657, 666,
|
||||
672, 681, 690, 702, 711, 723, 735, 750, 759, 771, 783, 798, 810, 825, 840, 852, 858, 867, 876, 888,
|
||||
897, 909, 915, 924, 933, 945, 957, 972, 984, 999, 1008, 1014, 1023, 1035, 1047, 1056, 1068, 1083, 1092, 1098,
|
||||
1110, 1125, 1140, 1152, 1167, 1173, 1185, 1188, 1191, 1197, 1203, 1212, 1218, 1227, 1236, 1248, 1254, 1263, 1272, 1284,
|
||||
1293, 1305, 1317, 1326, 1332, 1341, 1350, 1362, 1371, 1383, 1395, 1410, 1419, 1425, 1437, 1446, 1458, 1467, 1482, 1488,
|
||||
1494, 1503, 1512, 1524, 1533, 1545, 1557, 1572, 1581, 1593, 1605, 1620, 1632, 1647, 1662, 1674, 1683, 1695, 1707, 1716,
|
||||
1728, 1743, 1758, 1770, 1782, 1791, 1806, 1812, 1827, 1839, 1845, 1848, 1854, 1863, 1872, 1884, 1893, 1905, 1917, 1932,
|
||||
1941, 1953, 1965, 1980, 1986, 1995, 2004, 2010, 2019, 2031, 2043, 2058, 2070, 2085, 2100, 2106, 2118, 2127, 2142, 2154,
|
||||
2163, 2169, 2181, 2184, 2193, 2205, 2217, 2232, 2244, 2259, 2268, 2280, 2292, 2307, 2322, 2328, 2337, 2349, 2355, 2358,
|
||||
2364, 2373, 2382, 2388, 2397, 2409, 2415, 2418, 2427, 2433, 2445, 2448, 2454, 2457, 2460, 2460 };
|
||||
|
||||
int marchingCubesIds[2460] = {
|
||||
0, 8, 3, 0, 1, 9, 1, 8, 3, 9, 8, 1, 1, 2, 10, 0, 8, 3, 1, 2, 10, 9, 2, 10, 0, 2, 9, 2, 8, 3, 2,
|
||||
10, 8, 10, 9, 8, 3, 11, 2, 0, 11, 2, 8, 11, 0, 1, 9, 0, 2, 3, 11, 1, 11, 2, 1, 9, 11, 9, 8, 11, 3,
|
||||
10, 1, 11, 10, 3, 0, 10, 1, 0, 8, 10, 8, 11, 10, 3, 9, 0, 3, 11, 9, 11, 10, 9, 9, 8, 10, 10, 8, 11, 4,
|
||||
7, 8, 4, 3, 0, 7, 3, 4, 0, 1, 9, 8, 4, 7, 4, 1, 9, 4, 7, 1, 7, 3, 1, 1, 2, 10, 8, 4, 7, 3,
|
||||
4, 7, 3, 0, 4, 1, 2, 10, 9, 2, 10, 9, 0, 2, 8, 4, 7, 2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, 8,
|
||||
4, 7, 3, 11, 2, 11, 4, 7, 11, 2, 4, 2, 0, 4, 9, 0, 1, 8, 4, 7, 2, 3, 11, 4, 7, 11, 9, 4, 11, 9,
|
||||
11, 2, 9, 2, 1, 3, 10, 1, 3, 11, 10, 7, 8, 4, 1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, 4, 7, 8, 9,
|
||||
0, 11, 9, 11, 10, 11, 0, 3, 4, 7, 11, 4, 11, 9, 9, 11, 10, 9, 5, 4, 9, 5, 4, 0, 8, 3, 0, 5, 4, 1,
|
||||
5, 0, 8, 5, 4, 8, 3, 5, 3, 1, 5, 1, 2, 10, 9, 5, 4, 3, 0, 8, 1, 2, 10, 4, 9, 5, 5, 2, 10, 5,
|
||||
4, 2, 4, 0, 2, 2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, 9, 5, 4, 2, 3, 11, 0, 11, 2, 0, 8, 11, 4,
|
||||
9, 5, 0, 5, 4, 0, 1, 5, 2, 3, 11, 2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, 10, 3, 11, 10, 1, 3, 9,
|
||||
5, 4, 4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, 5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, 5, 4, 8, 5,
|
||||
8, 10, 10, 8, 11, 9, 7, 8, 5, 7, 9, 9, 3, 0, 9, 5, 3, 5, 7, 3, 0, 7, 8, 0, 1, 7, 1, 5, 7, 1,
|
||||
5, 3, 3, 5, 7, 9, 7, 8, 9, 5, 7, 10, 1, 2, 10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, 8, 0, 2, 8,
|
||||
2, 5, 8, 5, 7, 10, 5, 2, 2, 10, 5, 2, 5, 3, 3, 5, 7, 7, 9, 5, 7, 8, 9, 3, 11, 2, 9, 5, 7, 9,
|
||||
7, 2, 9, 2, 0, 2, 7, 11, 2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, 11, 2, 1, 11, 1, 7, 7, 1, 5, 9,
|
||||
5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, 5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, 11, 10, 0, 11,
|
||||
0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, 11, 10, 5, 7, 11, 5, 10, 6, 5, 0, 8, 3, 5, 10, 6, 9, 0, 1, 5,
|
||||
10, 6, 1, 8, 3, 1, 9, 8, 5, 10, 6, 1, 6, 5, 2, 6, 1, 1, 6, 5, 1, 2, 6, 3, 0, 8, 9, 6, 5, 9,
|
||||
0, 6, 0, 2, 6, 5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, 2, 3, 11, 10, 6, 5, 11, 0, 8, 11, 2, 0, 10,
|
||||
6, 5, 0, 1, 9, 2, 3, 11, 5, 10, 6, 5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, 6, 3, 11, 6, 5, 3, 5,
|
||||
1, 3, 0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, 3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, 6, 5, 9, 6,
|
||||
9, 11, 11, 9, 8, 5, 10, 6, 4, 7, 8, 4, 3, 0, 4, 7, 3, 6, 5, 10, 1, 9, 0, 5, 10, 6, 8, 4, 7, 10,
|
||||
6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, 6, 1, 2, 6, 5, 1, 4, 7, 8, 1, 2, 5, 5, 2, 6, 3, 0, 4, 3,
|
||||
4, 7, 8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, 7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, 3,
|
||||
11, 2, 7, 8, 4, 10, 6, 5, 5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, 0, 1, 9, 4, 7, 8, 2, 3, 11, 5,
|
||||
10, 6, 9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, 8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, 5,
|
||||
1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, 0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, 6,
|
||||
5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, 10, 4, 9, 6, 4, 10, 4, 10, 6, 4, 9, 10, 0, 8, 3, 10, 0, 1, 10,
|
||||
6, 0, 6, 4, 0, 8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, 1, 4, 9, 1, 2, 4, 2, 6, 4, 3, 0, 8, 1,
|
||||
2, 9, 2, 4, 9, 2, 6, 4, 0, 2, 4, 4, 2, 6, 8, 3, 2, 8, 2, 4, 4, 2, 6, 10, 4, 9, 10, 6, 4, 11,
|
||||
2, 3, 0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, 3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, 6, 4, 1, 6,
|
||||
1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, 9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, 8, 11, 1, 8, 1, 0, 11,
|
||||
6, 1, 9, 1, 4, 6, 4, 1, 3, 11, 6, 3, 6, 0, 0, 6, 4, 6, 4, 8, 11, 6, 8, 7, 10, 6, 7, 8, 10, 8,
|
||||
9, 10, 0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, 10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, 10, 6, 7, 10,
|
||||
7, 1, 1, 7, 3, 1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, 2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7,
|
||||
3, 9, 7, 8, 0, 7, 0, 6, 6, 0, 2, 7, 3, 2, 6, 7, 2, 2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, 2,
|
||||
0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, 1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, 11,
|
||||
2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, 8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, 0, 9, 1, 11,
|
||||
6, 7, 7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, 7, 11, 6, 7, 6, 11, 3, 0, 8, 11, 7, 6, 0, 1, 9, 11,
|
||||
7, 6, 8, 1, 9, 8, 3, 1, 11, 7, 6, 10, 1, 2, 6, 11, 7, 1, 2, 10, 3, 0, 8, 6, 11, 7, 2, 9, 0, 2,
|
||||
10, 9, 6, 11, 7, 6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, 7, 2, 3, 6, 2, 7, 7, 0, 8, 7, 6, 0, 6,
|
||||
2, 0, 2, 7, 6, 2, 3, 7, 0, 1, 9, 1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, 10, 7, 6, 10, 1, 7, 1,
|
||||
3, 7, 10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, 0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, 7, 6, 10, 7,
|
||||
10, 8, 8, 10, 9, 6, 8, 4, 11, 8, 6, 3, 6, 11, 3, 0, 6, 0, 4, 6, 8, 6, 11, 8, 4, 6, 9, 0, 1, 9,
|
||||
4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, 6, 8, 4, 6, 11, 8, 2, 10, 1, 1, 2, 10, 3, 0, 11, 0, 6, 11, 0,
|
||||
4, 6, 4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, 10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, 8,
|
||||
2, 3, 8, 4, 2, 4, 6, 2, 0, 4, 2, 4, 6, 2, 1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, 1, 9, 4, 1,
|
||||
4, 2, 2, 4, 6, 8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, 10, 1, 0, 10, 0, 6, 6, 0, 4, 4, 6, 3, 4,
|
||||
3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, 10, 9, 4, 6, 10, 4, 4, 9, 5, 7, 6, 11, 0, 8, 3, 4, 9, 5, 11,
|
||||
7, 6, 5, 0, 1, 5, 4, 0, 7, 6, 11, 11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, 9, 5, 4, 10, 1, 2, 7,
|
||||
6, 11, 6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, 7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, 3, 4, 8, 3,
|
||||
5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, 7, 2, 3, 7, 6, 2, 5, 4, 9, 9, 5, 4, 0, 8, 6, 0, 6, 2, 6,
|
||||
8, 7, 3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, 6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, 9,
|
||||
5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, 1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, 4, 0, 10, 4,
|
||||
10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, 7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, 6, 9, 5, 6, 11, 9, 11,
|
||||
8, 9, 3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, 0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, 6, 11, 3, 6,
|
||||
3, 5, 5, 3, 1, 1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, 0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1,
|
||||
2, 10, 11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, 6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, 5,
|
||||
8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, 9, 5, 6, 9, 6, 0, 0, 6, 2, 1, 5, 8, 1, 8, 0, 5, 6, 8, 3,
|
||||
8, 2, 6, 2, 8, 1, 5, 6, 2, 1, 6, 1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, 10, 1, 0, 10,
|
||||
0, 6, 9, 5, 0, 5, 6, 0, 0, 3, 8, 5, 6, 10, 10, 5, 6, 11, 5, 10, 7, 5, 11, 11, 5, 10, 11, 7, 5, 8,
|
||||
3, 0, 5, 11, 7, 5, 10, 11, 1, 9, 0, 10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, 11, 1, 2, 11, 7, 1, 7,
|
||||
5, 1, 0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, 9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, 7, 5, 2, 7,
|
||||
2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, 2, 5, 10, 2, 3, 5, 3, 7, 5, 8, 2, 0, 8, 5, 2, 8, 7, 5, 10,
|
||||
2, 5, 9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, 9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, 1,
|
||||
3, 5, 3, 7, 5, 0, 8, 7, 0, 7, 1, 1, 7, 5, 9, 0, 3, 9, 3, 5, 5, 3, 7, 9, 8, 7, 5, 9, 7, 5,
|
||||
8, 4, 5, 10, 8, 10, 11, 8, 5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, 0, 1, 9, 8, 4, 10, 8, 10, 11, 10,
|
||||
4, 5, 10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, 2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, 0,
|
||||
4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, 0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, 9,
|
||||
4, 5, 2, 11, 3, 2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, 5, 10, 2, 5, 2, 4, 4, 2, 0, 3, 10, 2, 3,
|
||||
5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, 5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, 8, 4, 5, 8, 5, 3, 3,
|
||||
5, 1, 0, 4, 5, 1, 0, 5, 8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, 9, 4, 5, 4, 11, 7, 4, 9, 11, 9,
|
||||
10, 11, 0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, 1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, 3, 1, 4, 3,
|
||||
4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, 4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, 9, 7, 4, 9, 11, 7, 9,
|
||||
1, 11, 2, 11, 1, 0, 8, 3, 11, 7, 4, 11, 4, 2, 2, 4, 0, 11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, 2,
|
||||
9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, 9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, 3, 7, 10, 3,
|
||||
10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, 1, 10, 2, 8, 7, 4, 4, 9, 1, 4, 1, 7, 7, 1, 3, 4, 9, 1, 4,
|
||||
1, 7, 0, 8, 1, 8, 7, 1, 4, 0, 3, 7, 4, 3, 4, 8, 7, 9, 10, 8, 10, 11, 8, 3, 0, 9, 3, 9, 11, 11,
|
||||
9, 10, 0, 1, 10, 0, 10, 8, 8, 10, 11, 3, 1, 10, 11, 3, 10, 1, 2, 11, 1, 11, 9, 9, 11, 8, 3, 0, 9, 3,
|
||||
9, 11, 1, 2, 9, 2, 11, 9, 0, 2, 11, 8, 0, 11, 3, 2, 11, 2, 3, 8, 2, 8, 10, 10, 8, 9, 9, 10, 2, 0,
|
||||
9, 2, 2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, 1, 10, 2, 1, 3, 8, 9, 1, 8, 0, 9, 1, 0, 3, 8 };
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
726
engine/third_party/physx/source/physxextensions/src/tet/ExtMeshSimplificator.cpp
vendored
Normal file
726
engine/third_party/physx/source/physxextensions/src/tet/ExtMeshSimplificator.cpp
vendored
Normal file
@@ -0,0 +1,726 @@
|
||||
// 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.
|
||||
|
||||
#include "ExtMeshSimplificator.h"
|
||||
#include "foundation/PxSort.h"
|
||||
|
||||
using namespace physx;
|
||||
using namespace Ext;
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
MeshSimplificator::MeshSimplificator()
|
||||
{
|
||||
currentVertMark = 0;
|
||||
numMeshTris = 0;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
void MeshSimplificator::findTriNeighbors()
|
||||
{
|
||||
PxI32 numTris = PxI32(triIds.size()) / 3;
|
||||
triNeighbors.clear();
|
||||
triNeighbors.resize(3 * numTris, -1);
|
||||
|
||||
struct Edge
|
||||
{
|
||||
PX_FORCE_INLINE void init(PxI32 _id0, PxI32 _id1, PxI32 _triNr, PxI32 _edgeNr)
|
||||
{
|
||||
this->id0 = PxMin(_id0, _id1);
|
||||
this->id1 = PxMax(_id0, _id1);
|
||||
this->triNr = _triNr;
|
||||
this->edgeNr = _edgeNr;
|
||||
}
|
||||
PX_FORCE_INLINE bool operator < (const Edge& e) const
|
||||
{
|
||||
return (id0 < e.id0 || (id0 == e.id0 && id1 < e.id1));
|
||||
}
|
||||
PX_FORCE_INLINE bool operator == (const Edge& e) const
|
||||
{
|
||||
return id0 == e.id0 && id1 == e.id1;
|
||||
}
|
||||
PxI32 id0, id1, triNr, edgeNr;
|
||||
};
|
||||
|
||||
PxArray<Edge> edges(PxI32(triIds.size()));
|
||||
|
||||
for (PxI32 i = 0; i < numTris; i++)
|
||||
{
|
||||
for (PxI32 j = 0; j < 3; j++)
|
||||
{
|
||||
PxI32 id0 = triIds[3 * i + j];
|
||||
PxI32 id1 = triIds[3 * i + (j + 1) % 3];
|
||||
edges[3 * i + j].init(id0, id1, i, j);
|
||||
}
|
||||
}
|
||||
PxSort(edges.begin(), edges.size());
|
||||
|
||||
PxI32 nr = 0;
|
||||
while (nr < PxI32(edges.size()))
|
||||
{
|
||||
Edge& e0 = edges[nr];
|
||||
nr++;
|
||||
while (nr < PxI32(edges.size()) && edges[nr] == e0)
|
||||
{
|
||||
Edge& e1 = edges[nr];
|
||||
triNeighbors[3 * e0.triNr + e0.edgeNr] = e1.triNr;
|
||||
triNeighbors[3 * e1.triNr + e1.edgeNr] = e0.triNr;
|
||||
nr++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
bool MeshSimplificator::getAdjTris(PxI32 triNr, PxI32 vertNr, PxI32& valence, bool& open, PxArray<PxI32>* tris) const
|
||||
{
|
||||
open = false;
|
||||
|
||||
if (tris)
|
||||
tris->clear();
|
||||
|
||||
PxI32 cnt = 0;
|
||||
valence = 0;
|
||||
PxI32 nr = triNr;
|
||||
|
||||
// counter clock
|
||||
do
|
||||
{
|
||||
if (tris)
|
||||
tris->pushBack(nr);
|
||||
valence++;
|
||||
if (triIds[3 * nr] == vertNr)
|
||||
nr = triNeighbors[3 * nr + 2];
|
||||
else if (triIds[3 * nr + 1] == vertNr)
|
||||
nr = triNeighbors[3 * nr];
|
||||
else
|
||||
nr = triNeighbors[3 * nr + 1];
|
||||
cnt++;
|
||||
}
|
||||
while (nr >= 0 && nr != triNr && cnt < 100);
|
||||
|
||||
if (cnt >= 100)
|
||||
{
|
||||
valence = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
cnt = 0;
|
||||
|
||||
if (nr < 0)
|
||||
{ // open: search clockwise too
|
||||
open = true;
|
||||
nr = triNr;
|
||||
do
|
||||
{
|
||||
if (nr != triNr)
|
||||
{
|
||||
if (tris)
|
||||
tris->pushBack(nr);
|
||||
valence++;
|
||||
}
|
||||
if (triIds[3 * nr] == vertNr)
|
||||
nr = triNeighbors[3 * nr];
|
||||
else if (triIds[3 * nr + 1] == vertNr)
|
||||
nr = triNeighbors[3 * nr + 1];
|
||||
else
|
||||
nr = triNeighbors[3 * nr + 2];
|
||||
cnt++;
|
||||
}
|
||||
while (nr >= 0 && nr != triNr && cnt < 100);
|
||||
|
||||
valence++; // num tris + 1 if open
|
||||
|
||||
if (cnt > 100)
|
||||
{
|
||||
valence = 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
bool MeshSimplificator::getAdjTris(PxI32 triNr, PxI32 vertNr, PxArray<PxI32>& tris) const
|
||||
{
|
||||
PxI32 valence;
|
||||
bool open;
|
||||
return getAdjTris(triNr, vertNr, valence, open, &tris);
|
||||
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
void MeshSimplificator::replaceNeighbor(PxI32 triNr, PxI32 oldNeighbor, PxI32 newNeighbor)
|
||||
{
|
||||
if (triNr < 0)
|
||||
return;
|
||||
for (PxI32 i = 0; i < 3; i++)
|
||||
{
|
||||
if (triNeighbors[3 * triNr + i] == oldNeighbor)
|
||||
triNeighbors[3 * triNr + i] = newNeighbor;
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
PxI32 MeshSimplificator::getEdgeId(PxI32 triNr, PxI32 edgeNr)
|
||||
{
|
||||
PxI32 n = triNeighbors[3 * triNr + edgeNr];
|
||||
if (n < 0 || triNr < n)
|
||||
return 3 * triNr + edgeNr;
|
||||
else
|
||||
{
|
||||
for (PxI32 i = 0; i < 3; i++)
|
||||
{
|
||||
if (triNeighbors[3 * n + i] == triNr)
|
||||
return 3 * n + i;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline PxVec3Ex MeshSimplificator::projectPoint(const PxVec3& p)
|
||||
{
|
||||
PxU32 triangleId;
|
||||
PxVec3 pos = projector->projectPoint(p, triangleId);
|
||||
return PxVec3Ex(pos, triangleId);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
PxVec3Ex MeshSimplificator::evalEdgeCost(PxI32 triNr, PxI32 edgeNr, PxReal &cost)
|
||||
{
|
||||
const PxI32 numSteps = 10;
|
||||
cost = -1.0f;
|
||||
PxI32 id0 = triIds[3 * triNr + edgeNr];
|
||||
PxI32 id1 = triIds[3 * triNr + (edgeNr + 1) % 3];
|
||||
|
||||
PxReal minCost = FLT_MAX;
|
||||
PxReal maxCost = -FLT_MAX;
|
||||
|
||||
Quadric q; q = quadrics[id0] + quadrics[id1];
|
||||
PxVec3Ex pos;
|
||||
|
||||
PxReal edgeLength = (vertices[id0].p - vertices[id1].p).magnitude();
|
||||
|
||||
for (PxI32 i = 0; i <= numSteps; i++)
|
||||
{
|
||||
float r = 1.0f / numSteps * i;
|
||||
pos.p = vertices[id0].p * (1.0f - r) + vertices[id1].p * r;
|
||||
if (projector)
|
||||
pos = projectPoint(pos.p);
|
||||
|
||||
float c = q.outerProduct(pos.p);
|
||||
c += edgeLengthCostWeight * edgeLength;
|
||||
if (cost < 0.0f || c < cost)
|
||||
{
|
||||
cost = c;
|
||||
}
|
||||
if (cost > maxCost)
|
||||
maxCost = cost;
|
||||
if (cost < minCost)
|
||||
minCost = cost;
|
||||
}
|
||||
|
||||
if (maxCost - minCost < flatnessDetectionThreshold)
|
||||
{
|
||||
float r = 0.5f;
|
||||
pos.p = vertices[id0].p * (1.0f - r) + vertices[id1].p * r;
|
||||
if (projector)
|
||||
pos = projectPoint(pos.p);
|
||||
cost = q.outerProduct(pos.p) + edgeLengthCostWeight * edgeLength;
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
bool MeshSimplificator::collapseEdge(PxI32 triNr, PxI32 edgeNr)
|
||||
{
|
||||
if (triIds[3 * triNr] == triIds[3 * triNr + 1]) // the triangle deleted
|
||||
return false;
|
||||
|
||||
PxI32 id0 = triIds[3 * triNr + edgeNr];
|
||||
PxI32 id1 = triIds[3 * triNr + (edgeNr + 1) % 3];
|
||||
PxI32 id2 = triIds[3 * triNr + (edgeNr + 2) % 3];
|
||||
PxI32 id3 = -1;
|
||||
|
||||
PxI32 n = triNeighbors[3 * triNr + edgeNr];
|
||||
PxI32 nEdgeNr = 0;
|
||||
if (n >= 0)
|
||||
{
|
||||
if (triNeighbors[3 * n] == triNr) nEdgeNr = 0;
|
||||
else if (triNeighbors[3 * n + 1] == triNr) nEdgeNr = 1;
|
||||
else if (triNeighbors[3 * n + 2] == triNr) nEdgeNr = 2;
|
||||
id3 = triIds[3 * n + (nEdgeNr + 2) % 3];
|
||||
}
|
||||
|
||||
// not legal if there exists id != id0,id1 with (id0,id) and (id1,id) edges
|
||||
// but (id,id0,id1) is not a triangle
|
||||
|
||||
bool OK = getAdjTris(triNr, id0, adjTris);
|
||||
currentVertMark++;
|
||||
for (PxI32 i = 0; i < PxI32(adjTris.size()); i++)
|
||||
{
|
||||
PxI32 adj = adjTris[i];
|
||||
for (PxI32 j = 0; j < 3; j++)
|
||||
vertMarks[triIds[3 * adj + j]] = currentVertMark;
|
||||
}
|
||||
OK = OK && getAdjTris(triNr, id1, adjTris);
|
||||
if (!OK)
|
||||
return false;
|
||||
for (PxI32 i = 0; i < PxI32(adjTris.size()); i++)
|
||||
{
|
||||
PxI32 adj = adjTris[i];
|
||||
for (PxI32 j = 0; j < 3; j++)
|
||||
{
|
||||
PxI32 id = triIds[3 * adj + j];
|
||||
if (vertMarks[id] == currentVertMark &&
|
||||
id != id0 && id != id1 && id != id2 && id != id3)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// new center pos
|
||||
|
||||
PxReal cost;
|
||||
PxVec3Ex newPos = evalEdgeCost(triNr, edgeNr, cost);
|
||||
//PxVec3 newPos = vertices[id0] * (1.0f - ratio) + vertices[id1] * ratio;
|
||||
|
||||
// any triangle flips?
|
||||
|
||||
for (PxI32 side = 0; side < 2; side++)
|
||||
{
|
||||
getAdjTris(triNr, side == 0 ? id0 : id1, adjTris);
|
||||
PxI32 other = side == 0 ? id1 : id0;
|
||||
for (PxU32 i = 0; i < adjTris.size(); i++)
|
||||
{
|
||||
PxI32 adj = adjTris[i];
|
||||
PxVec3 p[3], q[3];
|
||||
bool deleted = false;
|
||||
for (PxI32 j = 0; j < 3; j++)
|
||||
{
|
||||
PxI32 id = triIds[3 * adj + j];
|
||||
if (id == other)
|
||||
deleted = true;
|
||||
p[j] = vertices[id].p;
|
||||
q[j] = (id == id0 || id == id1) ? newPos.p : p[j];
|
||||
}
|
||||
if (!deleted)
|
||||
{
|
||||
PxVec3 n0 = (p[1] - p[0]).cross(p[2] - p[0]);
|
||||
PxVec3 n1 = (q[1] - q[0]).cross(q[2] - q[0]);
|
||||
if (n0.dot(n1) <= 0.0f)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// remove adjacent edges from heap
|
||||
|
||||
for (PxI32 side = 0; side < 2; side++)
|
||||
{
|
||||
PxI32 id = side == 0 ? id0 : id1;
|
||||
getAdjTris(triNr, id, adjTris);
|
||||
for (PxU32 i = 0; i < adjTris.size(); i++)
|
||||
{
|
||||
PxI32 adj = adjTris[i];
|
||||
for (PxI32 j = 0; j < 3; j++)
|
||||
{
|
||||
PxI32 adj0 = triIds[3 * adj + j];
|
||||
PxI32 adj1 = triIds[3 * adj + (j + 1) % 3];
|
||||
if (adj0 == id0 || adj0 == id1 || adj1 == id0 || adj1 == id1)
|
||||
{
|
||||
PxI32 edgeId = getEdgeId(adj, j);
|
||||
heap.remove(edgeId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// move vertex
|
||||
|
||||
if (id0 > id1) {
|
||||
int id = id0; id0 = id1; id1 = id;
|
||||
}
|
||||
|
||||
vertices[id0] = newPos;
|
||||
quadrics[id0] += quadrics[id1];
|
||||
|
||||
// collapse edge
|
||||
|
||||
getAdjTris(triNr, id1, adjTris);
|
||||
|
||||
for (PxU32 i = 0; i < adjTris.size(); i++)
|
||||
{
|
||||
PxI32 adj = adjTris[i];
|
||||
for (PxI32 j = 0; j < 3; j++) {
|
||||
PxI32& id = triIds[3 * adj + j];
|
||||
if (id == id1)
|
||||
id = id0;
|
||||
}
|
||||
}
|
||||
|
||||
simplificationMap[id1] = id0;
|
||||
|
||||
// mark triangles as deleted (duplicate indices, can still be rendered)
|
||||
|
||||
triIds[3 * triNr + 2] = triIds[3 * triNr + 1] = triIds[3 * triNr];
|
||||
numMeshTris--;
|
||||
if (n >= 0)
|
||||
{
|
||||
triIds[3 * n + 2] = triIds[3 * n + 1] = triIds[3 * n];
|
||||
numMeshTris--;
|
||||
}
|
||||
|
||||
// update neighbors
|
||||
|
||||
PxI32 right = triNeighbors[3 * triNr + (edgeNr + 1) % 3];
|
||||
PxI32 left = triNeighbors[3 * triNr + (edgeNr + 2) % 3];
|
||||
replaceNeighbor(right, triNr, left);
|
||||
replaceNeighbor(left, triNr, right);
|
||||
PxI32 startTriNr = PxMax(right, left);
|
||||
|
||||
if (n >= 0)
|
||||
{
|
||||
right = triNeighbors[3 * n + (nEdgeNr + 1) % 3];
|
||||
left = triNeighbors[3 * n + (nEdgeNr + 2) % 3];
|
||||
replaceNeighbor(right, n, left);
|
||||
replaceNeighbor(left, n, right);
|
||||
startTriNr = PxMax(startTriNr, PxMax(right, left));
|
||||
}
|
||||
|
||||
// add new edges to heap
|
||||
|
||||
if (startTriNr >= 0)
|
||||
{
|
||||
getAdjTris(startTriNr, id0, adjTris);
|
||||
for (PxU32 i = 0; i < adjTris.size(); i++)
|
||||
{
|
||||
PxI32 adj = adjTris[i];
|
||||
for (PxI32 j = 0; j < 3; j++)
|
||||
{
|
||||
PxI32 adj0 = triIds[3 * adj + j];
|
||||
PxI32 adj1 = triIds[3 * adj + (j + 1) % 3];
|
||||
if (adj0 == id0 || adj1 == id0)
|
||||
{
|
||||
evalEdgeCost(adj, j, cost);
|
||||
PxI32 id = getEdgeId(adj, j);
|
||||
|
||||
heap.insert(HeapElem(adj, j, cost), id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#if PX_LINUX
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wmisleading-indentation"
|
||||
#endif
|
||||
|
||||
static void minMax(const PxArray<PxVec3Ex>& points, PxVec3& min, PxVec3& max)
|
||||
{
|
||||
min = PxVec3(FLT_MAX, FLT_MAX, FLT_MAX);
|
||||
max = PxVec3(-FLT_MAX, -FLT_MAX, -FLT_MAX);
|
||||
|
||||
for (PxU32 i = 0; i < points.size(); ++i)
|
||||
{
|
||||
const PxVec3& p = points[i].p;
|
||||
if (p.x > max.x) max.x = p.x; if (p.y > max.y) max.y = p.y; if (p.z > max.z) max.z = p.z;
|
||||
if (p.x < min.x) min.x = p.x; if (p.y < min.y) min.y = p.y; if (p.z < min.z) min.z = p.z;
|
||||
}
|
||||
}
|
||||
|
||||
#if PX_LINUX
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
void MeshSimplificator::transformPointsToUnitBox(PxArray<PxVec3Ex>& points)
|
||||
{
|
||||
PxVec3 min, max;
|
||||
minMax(points, min, max);
|
||||
origin = min;
|
||||
PxVec3 size = max - min;
|
||||
|
||||
scaling = 1.0f / PxMax(PxMax(1e-6f, size.x), PxMax(size.y, size.z));
|
||||
|
||||
for (PxU32 i = 0; i < points.size(); ++i)
|
||||
points[i].p = (points[i].p - min) * scaling;
|
||||
}
|
||||
|
||||
void MeshSimplificator::transformPointsToOriginalPosition(PxArray<PxVec3>& points)
|
||||
{
|
||||
PxReal s = 1.0f / scaling;
|
||||
for (PxU32 i = 0; i < points.size(); ++i)
|
||||
points[i] = points[i] * s + origin;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
void MeshSimplificator::init(const PxSimpleTriangleMesh& inputMesh, PxReal edgeLengthCostWeight_,
|
||||
PxReal flatnessDetectionThreshold_, bool projectSimplifiedPointsOnInputMeshSurface)
|
||||
{
|
||||
edgeLengthCostWeight = edgeLengthCostWeight_;
|
||||
flatnessDetectionThreshold = flatnessDetectionThreshold_;
|
||||
|
||||
vertices.resize(inputMesh.points.count);
|
||||
for (PxU32 i = 0; i < inputMesh.points.count; i++)
|
||||
vertices[i] = PxVec3Ex(inputMesh.points.at<PxVec3>(i));
|
||||
|
||||
transformPointsToUnitBox(vertices);
|
||||
|
||||
PxI32 numIndices = 3 * inputMesh.triangles.count;
|
||||
triIds.resize(numIndices);
|
||||
|
||||
if (inputMesh.flags & PxMeshFlag::e16_BIT_INDICES)
|
||||
{
|
||||
for (PxI32 i = 0; i < numIndices; i++)
|
||||
triIds[i] = PxI32(inputMesh.triangles.at<PxU16>(i));
|
||||
}
|
||||
else
|
||||
{
|
||||
for (PxI32 i = 0; i < numIndices; i++)
|
||||
triIds[i] = PxI32(inputMesh.triangles.at<PxU32>(i));
|
||||
}
|
||||
|
||||
for (PxU32 i = 0; i < triIds.size(); i++)
|
||||
vertices[triIds[i]].i = i / 3;
|
||||
|
||||
if (projectSimplifiedPointsOnInputMeshSurface)
|
||||
{
|
||||
originalTriIds.resize(triIds.size());
|
||||
for (PxU32 i = 0; i < triIds.size(); ++i)
|
||||
originalTriIds[i] = triIds[i];
|
||||
scaledOriginalVertices.resize(inputMesh.points.count);
|
||||
for (PxU32 i = 0; i < inputMesh.points.count; i++)
|
||||
scaledOriginalVertices[i] = vertices[i].p;
|
||||
projector = Gu::PxCreatePointOntoTriangleMeshProjector(scaledOriginalVertices.begin(), originalTriIds.begin(), inputMesh.triangles.count);
|
||||
}
|
||||
else
|
||||
projector = NULL;
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
void MeshSimplificator::init(const PxArray<PxVec3> &inputVertices, const PxArray<PxU32> &inputTriIds, PxReal edgeLengthCostWeight_,
|
||||
PxReal flatnessDetectionThreshold_, bool projectSimplifiedPointsOnInputMeshSurface)
|
||||
{
|
||||
edgeLengthCostWeight = edgeLengthCostWeight_;
|
||||
flatnessDetectionThreshold = flatnessDetectionThreshold_;
|
||||
|
||||
vertices.resize(inputVertices.size());
|
||||
for (PxU32 i = 0; i < inputVertices.size(); i++)
|
||||
vertices[i] = PxVec3Ex(inputVertices[i]);
|
||||
|
||||
for (PxU32 i = 0; i < inputTriIds.size(); i++)
|
||||
vertices[inputTriIds[i]].i = i / 3;
|
||||
|
||||
transformPointsToUnitBox(vertices);
|
||||
|
||||
triIds.resize(inputTriIds.size());
|
||||
for (PxU32 i = 0; i < inputTriIds.size(); i++)
|
||||
triIds[i] = PxI32(inputTriIds[i]);
|
||||
|
||||
if (projectSimplifiedPointsOnInputMeshSurface)
|
||||
{
|
||||
scaledOriginalVertices.resize(inputVertices.size());
|
||||
for (PxU32 i = 0; i < inputVertices.size(); i++)
|
||||
scaledOriginalVertices[i] = vertices[i].p;
|
||||
projector = Gu::PxCreatePointOntoTriangleMeshProjector(scaledOriginalVertices.begin(), inputTriIds.begin(), inputTriIds.size() / 3);
|
||||
}
|
||||
else
|
||||
projector = NULL;
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
MeshSimplificator::~MeshSimplificator()
|
||||
{
|
||||
if (projector)
|
||||
{
|
||||
PX_RELEASE(projector)
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
void MeshSimplificator::init()
|
||||
{
|
||||
vertMarks.clear();
|
||||
vertMarks.resize(vertices.size(), 0);
|
||||
currentVertMark = 0;
|
||||
|
||||
findTriNeighbors();
|
||||
|
||||
// init vertex quadrics
|
||||
|
||||
quadrics.resize(vertices.size());
|
||||
for (PxU32 i = 0; i < vertices.size(); i++)
|
||||
quadrics[i].zero();
|
||||
|
||||
Quadric q;
|
||||
|
||||
PxI32 numTris = PxI32(triIds.size()) / 3;
|
||||
|
||||
for (PxI32 i = 0; i < numTris; i++)
|
||||
{
|
||||
PxI32 id0 = triIds[3 * i];
|
||||
PxI32 id1 = triIds[3 * i + 1];
|
||||
PxI32 id2 = triIds[3 * i + 2];
|
||||
q.setFromPlane(vertices[id0].p, vertices[id1].p, vertices[id2].p);
|
||||
quadrics[id0] += q;
|
||||
quadrics[id1] += q;
|
||||
quadrics[id2] += q;
|
||||
}
|
||||
|
||||
// init heap
|
||||
|
||||
heap.clear();
|
||||
|
||||
for (PxI32 i = 0; i < numTris; i++)
|
||||
{
|
||||
for (PxI32 j = 0; j < 3; j++)
|
||||
{
|
||||
PxI32 n = triNeighbors[3 * i + j];
|
||||
if (n < 0 || i < n)
|
||||
{
|
||||
PxReal cost;
|
||||
evalEdgeCost(i, j, cost);
|
||||
heap.insert(HeapElem(i, j, cost), getEdgeId(i, j));
|
||||
}
|
||||
}
|
||||
}
|
||||
numMeshTris = numTris;
|
||||
|
||||
// init simplification map
|
||||
|
||||
simplificationMap.resize(vertices.size());
|
||||
for (PxI32 i = 0; i < PxI32(vertices.size()); i++)
|
||||
simplificationMap[i] = i; // each vertex is a root
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
bool MeshSimplificator::step(PxF32 maximalEdgeLength)
|
||||
{
|
||||
int heapMinSize = 20;
|
||||
|
||||
if (heap.size() < heapMinSize)
|
||||
return false;
|
||||
|
||||
while (heap.size() > heapMinSize)
|
||||
{
|
||||
HeapElem e = heap.deleteMin();
|
||||
|
||||
PxI32 id0 = triIds[3 * e.triNr + e.edgeNr];
|
||||
PxI32 id1 = triIds[3 * e.triNr + (e.edgeNr + 1) % 3];
|
||||
PxF32 length = (vertices[id0].p - vertices[id1].p).magnitude();
|
||||
if (maximalEdgeLength == 0.0f || length < maximalEdgeLength)
|
||||
{
|
||||
collapseEdge(e.triNr, e.edgeNr);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
void MeshSimplificator::decimateByRatio(PxF32 relativeOutputMeshSize, PxF32 maximalEdgeLength)
|
||||
{
|
||||
relativeOutputMeshSize = PxClamp(relativeOutputMeshSize, 0.1f, 0.99f);
|
||||
PxI32 numSteps = PxI32(PxFloor(PxF32(heap.size()) * (1.0f - relativeOutputMeshSize)));
|
||||
for (PxI32 i = 0; i < numSteps; i++)
|
||||
{
|
||||
if (!step(maximalEdgeLength))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
void MeshSimplificator::decimateBySize(PxI32 targetTriangleCount, PxF32 maximalEdgeLength)
|
||||
{
|
||||
while (numMeshTris > targetTriangleCount)
|
||||
{
|
||||
if (!step(maximalEdgeLength))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
void MeshSimplificator::readBack(PxArray<PxVec3>& outVertices, PxArray<PxU32>& outTriIds, PxArray<PxU32> *vertexMap, PxArray<PxU32> *outputVertexToInputTriangle)
|
||||
{
|
||||
outVertices.clear();
|
||||
outTriIds.clear();
|
||||
PxArray<PxI32> idMap(vertices.size(), -1);
|
||||
|
||||
PxI32 numTris = PxI32(triIds.size()) / 3;
|
||||
|
||||
for (PxI32 i = 0; i < numTris; i++)
|
||||
{
|
||||
if (triIds[3 * i] == triIds[3 * i + 1]) // deleted
|
||||
continue;
|
||||
|
||||
for (PxI32 j = 0; j < 3; j++)
|
||||
{
|
||||
PxI32 id = triIds[3 * i + j];
|
||||
if (idMap[id] < 0)
|
||||
{
|
||||
idMap[id] = outVertices.size();
|
||||
outVertices.pushBack(vertices[id].p);
|
||||
if(outputVertexToInputTriangle && projector)
|
||||
outputVertexToInputTriangle->pushBack(vertices[id].i);
|
||||
}
|
||||
outTriIds.pushBack(PxU32(idMap[id]));
|
||||
}
|
||||
}
|
||||
|
||||
transformPointsToOriginalPosition(outVertices);
|
||||
|
||||
if (vertexMap)
|
||||
{
|
||||
for (PxU32 i = 0; i < simplificationMap.size(); ++i)
|
||||
{
|
||||
PxI32 id = i;
|
||||
while (id != simplificationMap[id])
|
||||
{
|
||||
id = simplificationMap[id];
|
||||
}
|
||||
const PxI32 finalLink = id;
|
||||
id = i;
|
||||
simplificationMap[i] = finalLink;
|
||||
while (id != simplificationMap[id])
|
||||
{
|
||||
PxI32 oldId = id;
|
||||
id = simplificationMap[id];
|
||||
simplificationMap[oldId] = finalLink;
|
||||
}
|
||||
}
|
||||
|
||||
vertexMap->resize(vertices.size());
|
||||
for (PxU32 i = 0; i < simplificationMap.size(); ++i)
|
||||
{
|
||||
PxI32 mapped = idMap[simplificationMap[i]];
|
||||
(*vertexMap)[i] = mapped;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
135
engine/third_party/physx/source/physxextensions/src/tet/ExtMeshSimplificator.h
vendored
Normal file
135
engine/third_party/physx/source/physxextensions/src/tet/ExtMeshSimplificator.h
vendored
Normal file
@@ -0,0 +1,135 @@
|
||||
// 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.
|
||||
|
||||
#ifndef EXT_MESH_SIMPLIFICATOR_H
|
||||
#define EXT_MESH_SIMPLIFICATOR_H
|
||||
|
||||
|
||||
#include "foundation/PxBounds3.h"
|
||||
#include "foundation/PxArray.h"
|
||||
#include "geometry/PxSimpleTriangleMesh.h"
|
||||
|
||||
#include "ExtQuadric.h"
|
||||
#include "ExtRandomAccessHeap.h"
|
||||
#include "GuSDF.h"
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
// MM: implementation of paper Garland and Heckbert: "Surface Simplification Using Quadric Error Metrics"
|
||||
|
||||
namespace physx
|
||||
{
|
||||
namespace Ext
|
||||
{
|
||||
struct PxVec3Ex
|
||||
{
|
||||
PxVec3 p;
|
||||
PxU32 i = 0xFFFFFFFF;
|
||||
|
||||
explicit PxVec3Ex() : p(0.0f), i(0xFFFFFFFF)
|
||||
{
|
||||
}
|
||||
|
||||
explicit PxVec3Ex(PxVec3 point, PxU32 sourceTriangleIndex = 0xFFFFFFFF) : p(point), i(sourceTriangleIndex)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class MeshSimplificator
|
||||
{
|
||||
public:
|
||||
|
||||
MeshSimplificator();
|
||||
|
||||
void init(const PxSimpleTriangleMesh& inputMesh, PxReal edgeLengthCostWeight_ = 1e-1f, PxReal flatnessDetectionThreshold_ = 1e-2f, bool projectSimplifiedPointsOnInputMeshSurface = false);
|
||||
void init(const PxArray<PxVec3> &vertices, const PxArray<PxU32> &triIds, PxReal edgeLengthCostWeight_ = 1e-1f, PxReal flatnessDetectionThreshold_ = 1e-2f, bool projectSimplifiedPointsOnInputMeshSurface = false);
|
||||
void decimateByRatio(PxF32 relativeOutputMeshSize = 0.5f, PxF32 maximalEdgeLength = 0.0f);
|
||||
void decimateBySize(PxI32 targetTriangleCount, PxF32 maximalEdgeLength = 0.0f);
|
||||
void readBack(PxArray<PxVec3>& vertices, PxArray<PxU32>& triIds, PxArray<PxU32> *vertexMap = NULL, PxArray<PxU32> *outputVertexToInputTriangle = NULL);
|
||||
|
||||
~MeshSimplificator();
|
||||
|
||||
private:
|
||||
PxArray<PxVec3Ex> vertices;
|
||||
PxArray<PxI32> triIds;
|
||||
PxArray<PxVec3> scaledOriginalVertices;
|
||||
PxArray<PxU32> originalTriIds;
|
||||
Gu::PxPointOntoTriangleMeshProjector* projector;
|
||||
|
||||
void init();
|
||||
bool step(PxF32 maximalEdgeLength);
|
||||
bool getAdjTris(PxI32 triNr, PxI32 vertNr, PxI32& valence, bool& open,
|
||||
PxArray<PxI32>* tris) const;
|
||||
bool getAdjTris(PxI32 triNr, PxI32 vertNr, PxArray<PxI32>& tris) const;
|
||||
|
||||
void replaceNeighbor(PxI32 triNr, PxI32 oldNeighbor, PxI32 newNeighbor);
|
||||
PxI32 getEdgeId(PxI32 triNr, PxI32 edgeNr);
|
||||
bool collapseEdge(PxI32 triNr, PxI32 edgeNr);
|
||||
PxVec3Ex evalEdgeCost(PxI32 triNr, PxI32 edgeNr, PxReal& costt);
|
||||
PxVec3Ex projectPoint(const PxVec3& p);
|
||||
void findTriNeighbors();
|
||||
|
||||
void transformPointsToUnitBox(PxArray<PxVec3Ex>& points);
|
||||
void transformPointsToOriginalPosition(PxArray<PxVec3>& points);
|
||||
|
||||
PxI32 numMeshTris;
|
||||
|
||||
PxArray<Quadric> quadrics;
|
||||
PxArray<PxI32> vertMarks;
|
||||
PxArray<PxI32> adjTris;
|
||||
PxI32 currentVertMark;
|
||||
PxArray<PxI32> triNeighbors;
|
||||
|
||||
//Scale input points into 0...1 unit-box
|
||||
PxReal scaling;
|
||||
PxVec3 origin;
|
||||
|
||||
PxReal edgeLengthCostWeight;
|
||||
PxReal flatnessDetectionThreshold;
|
||||
|
||||
PxArray<PxI32> simplificationMap;
|
||||
|
||||
struct HeapElem
|
||||
{
|
||||
HeapElem() : triNr(0), edgeNr(0), cost(0.0f) {}
|
||||
HeapElem(PxI32 triNr_, PxI32 edgeNr_, float cost_) :
|
||||
triNr(triNr_), edgeNr(edgeNr_), cost(cost_) {}
|
||||
|
||||
PxI32 triNr, edgeNr;
|
||||
float cost;
|
||||
|
||||
PX_FORCE_INLINE bool operator < (const HeapElem& e) const
|
||||
{
|
||||
return cost < e.cost;
|
||||
}
|
||||
};
|
||||
|
||||
RandomAccessHeap<HeapElem> heap;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
276
engine/third_party/physx/source/physxextensions/src/tet/ExtMultiList.h
vendored
Normal file
276
engine/third_party/physx/source/physxextensions/src/tet/ExtMultiList.h
vendored
Normal file
@@ -0,0 +1,276 @@
|
||||
// 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 EXT_MULTI_LIST_H
|
||||
#define EXT_MULTI_LIST_H
|
||||
|
||||
// MM: Multiple linked lists in a common array with a free list
|
||||
|
||||
#include "foundation/PxArray.h"
|
||||
|
||||
namespace physx
|
||||
{
|
||||
namespace Ext
|
||||
{
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
template <class T>
|
||||
class MultiList {
|
||||
public:
|
||||
MultiList(PxI32 maxId = 0) {
|
||||
firstFree = -1;
|
||||
if (maxId > 0)
|
||||
first.reserve(maxId + 1);
|
||||
}
|
||||
void reserve(int maxId) {
|
||||
first.reserve(maxId + 1);
|
||||
}
|
||||
void clear();
|
||||
PxI32 add(PxI32 id, const T &item);
|
||||
bool addUnique(PxI32 id, const T &item);
|
||||
bool exists(PxI32 id, const T &item) const;
|
||||
void remove(PxI32 id, const T &item);
|
||||
void removeAll(PxI32 id);
|
||||
PxI32 size(PxI32 id) const;
|
||||
PxI32 getPairNr(PxI32 id, const T &item) const;
|
||||
|
||||
void replace(PxI32 id, const T &before, const T &after);
|
||||
|
||||
void getItems(PxI32 id) const;
|
||||
mutable PxArray<T> queryItems;
|
||||
|
||||
void initIteration(PxI32 id, PxI32& iterator);
|
||||
bool iterate(T& item, PxI32& iterator);
|
||||
|
||||
void getPointers(PxI32 id);
|
||||
mutable PxArray<T*> queryPointers;
|
||||
|
||||
private:
|
||||
PxArray<PxI32> first;
|
||||
PxArray<T> items;
|
||||
PxArray<PxI32> next;
|
||||
PxI32 firstFree;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
template <class T>
|
||||
void MultiList<T>::clear()
|
||||
{
|
||||
first.clear();
|
||||
next.clear();
|
||||
items.clear();
|
||||
queryItems.clear();
|
||||
queryPointers.clear();
|
||||
firstFree = -1;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
template <class T>
|
||||
PxI32 MultiList<T>::add(PxI32 id, const T &item)
|
||||
{
|
||||
if (id >= PxI32(first.size()))
|
||||
first.resize(id + 1, -1);
|
||||
PxI32 pos = firstFree;
|
||||
if (pos >= 0)
|
||||
firstFree = next[firstFree];
|
||||
else
|
||||
{
|
||||
pos = PxI32(items.size());
|
||||
items.resize(items.size() + 1);
|
||||
next.resize(items.size() + 1);
|
||||
}
|
||||
next[pos] = first[id];
|
||||
first[id] = pos;
|
||||
items[pos] = item;
|
||||
return pos;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
template <class T>
|
||||
bool MultiList<T>::addUnique(PxI32 id, const T &item)
|
||||
{
|
||||
if (exists(id, item))
|
||||
return false;
|
||||
add(id, item);
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
template <class T>
|
||||
bool MultiList<T>::exists(PxI32 id, const T &item) const
|
||||
{
|
||||
return getPairNr(id, item) >= 0;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
template <class T>
|
||||
PxI32 MultiList<T>::size(PxI32 id) const
|
||||
{
|
||||
if (id >= PxI32(first.size()))
|
||||
return 0;
|
||||
|
||||
PxI32 num = 0;
|
||||
PxI32 nr = first[id];
|
||||
while (nr >= 0)
|
||||
{
|
||||
num++;
|
||||
nr = next[nr];
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
template <class T>
|
||||
PxI32 MultiList<T>::getPairNr(PxI32 id, const T &item) const
|
||||
{
|
||||
if (id < 0 || id >= PxI32(first.size()))
|
||||
return -1;
|
||||
PxI32 nr = first[id];
|
||||
while (nr >= 0)
|
||||
{
|
||||
if (items[nr] == item)
|
||||
return nr;
|
||||
nr = next[nr];
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
template <class T>
|
||||
void MultiList<T>::remove(PxI32 id, const T &itemNr)
|
||||
{
|
||||
PxI32 nr = first[id];
|
||||
PxI32 prev = -1;
|
||||
while (nr >= 0 && items[nr] != itemNr)
|
||||
{
|
||||
prev = nr;
|
||||
nr = next[nr];
|
||||
}
|
||||
if (nr < 0)
|
||||
return;
|
||||
if (prev >= 0)
|
||||
next[prev] = next[nr];
|
||||
else
|
||||
first[id] = next[nr];
|
||||
next[nr] = firstFree;
|
||||
firstFree = nr;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
template <class T>
|
||||
void MultiList<T>::replace(PxI32 id, const T &before, const T &after)
|
||||
{
|
||||
PxI32 nr = first[id];
|
||||
while (nr >= 0)
|
||||
{
|
||||
if (items[nr] == before)
|
||||
items[nr] = after;
|
||||
nr = next[nr];
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
template <class T>
|
||||
void MultiList<T>::removeAll(PxI32 id)
|
||||
{
|
||||
if (id >= PxI32(first.size()))
|
||||
return;
|
||||
|
||||
PxI32 nr = first[id];
|
||||
if (nr < 0)
|
||||
return;
|
||||
|
||||
PxI32 prev = -1;
|
||||
while (nr >= 0)
|
||||
{
|
||||
prev = nr;
|
||||
nr = next[nr];
|
||||
}
|
||||
next[prev] = firstFree;
|
||||
firstFree = first[id];
|
||||
first[id] = -1;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
template <class T>
|
||||
void MultiList<T>::getItems(PxI32 id) const
|
||||
{
|
||||
queryItems.clear();
|
||||
if (id >= PxI32(first.size()))
|
||||
return;
|
||||
PxI32 nr = first[id];
|
||||
while (nr >= 0)
|
||||
{
|
||||
queryItems.push_back(items[nr]);
|
||||
nr = next[nr];
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
template <class T>
|
||||
void MultiList<T>::initIteration(PxI32 id, PxI32& iterator)
|
||||
{
|
||||
if (id >= PxI32(first.size()))
|
||||
iterator = -1;
|
||||
else
|
||||
iterator = first[id];
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
template <class T>
|
||||
bool MultiList<T>::iterate(T& item, PxI32& iterator)
|
||||
{
|
||||
if (iterator >= 0)
|
||||
{
|
||||
item = items[iterator];
|
||||
iterator = next[iterator];
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
template <class T>
|
||||
void MultiList<T>::getPointers(PxI32 id)
|
||||
{
|
||||
queryPointers.clear();
|
||||
if (id >= PxI32(first.size()))
|
||||
return;
|
||||
PxI32 nr = first[id];
|
||||
while (nr >= 0)
|
||||
{
|
||||
queryPointers.push_back(&items[nr]);
|
||||
nr = next[nr];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
725
engine/third_party/physx/source/physxextensions/src/tet/ExtOctreeTetrahedralizer.cpp
vendored
Normal file
725
engine/third_party/physx/source/physxextensions/src/tet/ExtOctreeTetrahedralizer.cpp
vendored
Normal file
@@ -0,0 +1,725 @@
|
||||
// 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.
|
||||
|
||||
#include "ExtOctreeTetrahedralizer.h"
|
||||
#include "foundation/PxSort.h"
|
||||
#include "foundation/PxQuat.h"
|
||||
#include "CmRandom.h"
|
||||
|
||||
using namespace physx;
|
||||
using namespace Ext;
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
static const PxI32 childRelPos[8][3] = { {0,0,0}, {1,0,0},{0,1,0},{1,1,0}, {0,0,1}, {1,0,1},{0,1,1},{1,1,1} };
|
||||
|
||||
static const PxI32 cubeCorners[8][3] = { {0,0,0}, {1,0,0},{1,1,0},{0,1,0}, {0,0,1}, {1,0,1},{1,1,1},{0,1,1} };
|
||||
//static const PxI32 cubeEdges[12][2] = { {0,1}, {1,2},{2,3},{3,0}, {0,4},{1,5},{2,6},{3,7},{4,5},{5,6},{6,7},{7,4} };
|
||||
|
||||
static const PxI32 tetFaces[4][3] = { {2,1,0}, {0,1,3}, {1,2,3}, {2,0,3} };
|
||||
static const PxI32 cubeTets[6][4] = { {0,1,2,5}, {0,4,5,2}, {2,4,5,6}, {4,7,6,3}, {2,6,3,4}, {0,2,3,4} };
|
||||
|
||||
static const PxI32 cubeTetNeighbors[6][4] = { {-1,-1,-1,1}, {-1,5,2,0}, {1,4,-1,-1},
|
||||
{-1,-1,-1,4}, {-1,2,3,5}, {-1,1,4,-1} };
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
OctreeTetrahedralizer::OctreeTetrahedralizer()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
void OctreeTetrahedralizer::clearTets()
|
||||
{
|
||||
tetVerts.clear();
|
||||
tetIds.clear();
|
||||
firstFreeTet = -1;
|
||||
currentTetMark = 0;
|
||||
tetMarks.clear();
|
||||
tetNeighbors.clear();
|
||||
renderVerts.clear();
|
||||
renderTriIds.clear();
|
||||
firstABBVert = 0;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
void OctreeTetrahedralizer::clear()
|
||||
{
|
||||
surfaceVerts.clear();
|
||||
surfaceTriIds.clear();
|
||||
tetIds.clear();
|
||||
tetNeighbors.clear();
|
||||
cells.clear();
|
||||
clearTets();
|
||||
|
||||
prevClip = -1.0f;
|
||||
prevScale = -1.0f;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------
|
||||
void OctreeTetrahedralizer::createTree()
|
||||
{
|
||||
Bounds3 bounds;
|
||||
bounds.setEmpty();
|
||||
|
||||
for (PxI32 i = 0; i < PxI32(surfaceVerts.size()); i++)
|
||||
{
|
||||
const PxVec3& v = surfaceVerts[i];
|
||||
bounds.include(PxVec3d(PxF64(v.x), PxF64(v.y), PxF64(v.z)));
|
||||
}
|
||||
|
||||
bounds.expand(0.01);
|
||||
PxVec3d dims = bounds.getDimensions();
|
||||
PxF64 size = PxMax(dims.x, PxMax(dims.y, dims.z));
|
||||
|
||||
// create root
|
||||
cells.resize(1);
|
||||
cells.front().init();
|
||||
cells.front().orig = bounds.minimum;
|
||||
cells.front().size = size;
|
||||
|
||||
cells.front().depth = 0;
|
||||
|
||||
// insert vertices
|
||||
|
||||
PxI32 numVerts = PxI32(surfaceVerts.size());
|
||||
|
||||
for (PxI32 i = 0; i < numVerts; i++)
|
||||
{
|
||||
treeInsertVert(0, i);
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
|
||||
PxI32 OctreeTetrahedralizer::Cell::getChildNr(const PxVec3d& p)
|
||||
{
|
||||
if (firstChild < 0)
|
||||
return -1;
|
||||
PxI32 nr = 0;
|
||||
if (p.x > orig.x + 0.5 * size) nr |= 1;
|
||||
if (p.y > orig.y + 0.5 * size) nr |= 2;
|
||||
if (p.z > orig.z + 0.5 * size) nr |= 4;
|
||||
return firstChild + nr;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
|
||||
void OctreeTetrahedralizer::treeInsertVert(PxI32 cellNr, PxI32 vertNr)
|
||||
{
|
||||
// inner node
|
||||
|
||||
if (cells[cellNr].firstChild >= 0)
|
||||
{
|
||||
treeInsertVert(cells[cellNr].getChildNr(surfaceVerts[vertNr]), vertNr);
|
||||
return;
|
||||
}
|
||||
|
||||
// add
|
||||
|
||||
vertsOfCell.add(cellNr, vertNr);
|
||||
cells[cellNr].numVerts++;
|
||||
|
||||
if (cells[cellNr].numVerts <= maxVertsPerCell ||
|
||||
cells[cellNr].depth >= maxTreeDepth)
|
||||
return;
|
||||
|
||||
// split
|
||||
|
||||
PxI32 firstChild = cells.size();
|
||||
cells[cellNr].firstChild = firstChild;
|
||||
cells.resize(cells.size() + 8);
|
||||
|
||||
for (PxI32 i = 0; i < 8; i++)
|
||||
{
|
||||
Cell& child = cells[firstChild + i];
|
||||
child.init();
|
||||
child.depth = cells[cellNr].depth + 1;
|
||||
child.size = cells[cellNr].size * 0.5;
|
||||
child.orig = cells[cellNr].orig + PxVec3d(
|
||||
childRelPos[i][0] * child.size,
|
||||
childRelPos[i][1] * child.size,
|
||||
childRelPos[i][2] * child.size);
|
||||
}
|
||||
|
||||
PxI32 iterator, id;
|
||||
vertsOfCell.initIteration(cellNr, iterator);
|
||||
while (vertsOfCell.iterate(id, iterator))
|
||||
treeInsertVert(cells[cellNr].getChildNr(surfaceVerts[id]), id);
|
||||
|
||||
vertsOfCell.removeAll(cellNr);
|
||||
cells[cellNr].numVerts = 0;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------
|
||||
|
||||
static PxVec3d jitter(const PxVec3d& p, Cm::RandomR250& random)
|
||||
{
|
||||
PxF64 eps = 0.001;
|
||||
return PxVec3d(
|
||||
p.x - eps + 2.0 * eps * PxF64(random.rand(0.0f, 1.0f)),
|
||||
p.y - eps + 2.0 * eps * PxF64(random.rand(0.0f, 1.0f)),
|
||||
p.z - eps + 2.0 * eps * PxF64(random.rand(0.0f, 1.0f)));
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------
|
||||
|
||||
void OctreeTetrahedralizer::createTetVerts(bool includeOctreeNodes)
|
||||
{
|
||||
tetVerts.clear();
|
||||
|
||||
insideTester.init(surfaceVerts.begin(), PxI32(surfaceVerts.size()),
|
||||
surfaceTriIds.begin(), PxI32(surfaceTriIds.size()) / 3);
|
||||
|
||||
for (PxI32 i = 0; i < PxI32(surfaceVerts.size()); i++)
|
||||
{
|
||||
const PxVec3& v = surfaceVerts[i];
|
||||
tetVerts.pushBack(PxVec3d(PxF64(v.x), PxF64(v.y), PxF64(v.z)));
|
||||
}
|
||||
|
||||
if (includeOctreeNodes)
|
||||
{
|
||||
PxArray<PxVec3d> treeVerts;
|
||||
|
||||
for (PxI32 i = 0; i < PxI32(cells.size()); i++)
|
||||
{
|
||||
PxF64 s = cells[i].size;
|
||||
|
||||
for (PxI32 j = 0; j < 8; j++) {
|
||||
PxVec3d p = cells[i].orig + PxVec3d(
|
||||
s * cubeCorners[j][0],
|
||||
s * cubeCorners[j][1],
|
||||
s * cubeCorners[j][2]);
|
||||
treeVerts.pushBack(p);
|
||||
}
|
||||
}
|
||||
|
||||
// remove duplicates
|
||||
|
||||
PxF64 eps = 1e-8;
|
||||
|
||||
struct Ref
|
||||
{
|
||||
PxF64 d;
|
||||
PxI32 vertNr;
|
||||
bool operator < (const Ref& r) const
|
||||
{
|
||||
return d < r.d;
|
||||
}
|
||||
};
|
||||
|
||||
PxI32 numTreeVerts = PxI32(treeVerts.size());
|
||||
|
||||
PxArray<Ref> refs(numTreeVerts);
|
||||
for (PxI32 i = 0; i < numTreeVerts; i++)
|
||||
{
|
||||
PxVec3d& p = treeVerts[i];
|
||||
refs[i].d = p.x + 0.3 * p.y + 0.1 * p.z;
|
||||
refs[i].vertNr = i;
|
||||
}
|
||||
|
||||
PxSort(refs.begin(), refs.size());
|
||||
PxArray<bool> duplicate(numTreeVerts, false);
|
||||
|
||||
PxI32 nr = 0;
|
||||
Cm::RandomR250 random(0);
|
||||
while (nr < numTreeVerts)
|
||||
{
|
||||
Ref& r = refs[nr];
|
||||
nr++;
|
||||
if (duplicate[r.vertNr])
|
||||
continue;
|
||||
PxVec3d& p = treeVerts[r.vertNr];
|
||||
PxVec3d v = jitter(p, random);
|
||||
|
||||
if (insideTester.isInside(PxVec3(PxReal(v.x), PxReal(v.y), PxReal(v.z))))
|
||||
tetVerts.pushBack(jitter(p, random));
|
||||
|
||||
PxI32 i = nr;
|
||||
while (i < numTreeVerts && fabs(refs[i].d - r.d) < eps)
|
||||
{
|
||||
PxVec3d& q = treeVerts[refs[i].vertNr];
|
||||
if ((p - q).magnitude() < eps)
|
||||
duplicate[refs[i].vertNr] = true;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------
|
||||
PxVec3d OctreeTetrahedralizer::getTetCenter(PxI32 tetNr) const
|
||||
{
|
||||
return (tetVerts[tetIds[4 * tetNr]] +
|
||||
tetVerts[tetIds[4 * tetNr + 1]] +
|
||||
tetVerts[tetIds[4 * tetNr + 2]] +
|
||||
tetVerts[tetIds[4 * tetNr + 3]]) * 0.25;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------
|
||||
void OctreeTetrahedralizer::treeInsertTet(PxI32 tetNr)
|
||||
{
|
||||
PxVec3d center = getTetCenter(tetNr);
|
||||
|
||||
PxI32 cellNr = 0;
|
||||
while (cellNr >= 0)
|
||||
{
|
||||
Cell& c = cells[cellNr];
|
||||
if (c.closestTetNr < 0)
|
||||
c.closestTetNr = tetNr;
|
||||
else
|
||||
{
|
||||
PxVec3d cellCenter = c.orig + PxVec3d(c.size, c.size, c.size) * 0.5;
|
||||
PxVec3d closest = getTetCenter(c.closestTetNr);
|
||||
if ((cellCenter - center).magnitudeSquared() < (cellCenter - closest).magnitudeSquared())
|
||||
c.closestTetNr = tetNr;
|
||||
}
|
||||
cellNr = cells[cellNr].getChildNr(center);
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------
|
||||
void OctreeTetrahedralizer::treeRemoveTet(PxI32 tetNr)
|
||||
{
|
||||
PxVec3d center = getTetCenter(tetNr);
|
||||
|
||||
PxI32 cellNr = 0;
|
||||
while (cellNr >= 0)
|
||||
{
|
||||
Cell& c = cells[cellNr];
|
||||
if (c.closestTetNr == tetNr)
|
||||
c.closestTetNr = -1;
|
||||
cellNr = cells[cellNr].getChildNr(center);
|
||||
}
|
||||
}
|
||||
|
||||
static void resizeFast(PxArray<PxI32>& arr, PxU32 newSize, PxI32 value = 0)
|
||||
{
|
||||
if (newSize < arr.size())
|
||||
arr.removeRange(newSize, arr.size() - newSize);
|
||||
else
|
||||
{
|
||||
while (arr.size() < newSize)
|
||||
arr.pushBack(value);
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------
|
||||
PxI32 OctreeTetrahedralizer::getNewTetNr()
|
||||
{
|
||||
PxI32 newTetNr;
|
||||
if (firstFreeTet >= 0)
|
||||
{
|
||||
// take from free list
|
||||
newTetNr = firstFreeTet;
|
||||
firstFreeTet = tetIds[4 * firstFreeTet];
|
||||
}
|
||||
else
|
||||
{
|
||||
// append
|
||||
newTetNr = PxI32(tetIds.size()) / 4;
|
||||
resizeFast(tetIds, tetIds.size() + 4);
|
||||
resizeFast(tetMarks, newTetNr + 1, 0);
|
||||
resizeFast(tetNeighbors, tetIds.size(), -1);
|
||||
}
|
||||
return newTetNr;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------
|
||||
void OctreeTetrahedralizer::removeTetNr(PxI32 tetNr)
|
||||
{
|
||||
// add to free list
|
||||
tetIds[4 * tetNr] = firstFreeTet;
|
||||
tetIds[4 * tetNr + 1] = -1;
|
||||
tetIds[4 * tetNr + 2] = -1;
|
||||
tetIds[4 * tetNr + 3] = -1;
|
||||
firstFreeTet = tetNr;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------
|
||||
bool OctreeTetrahedralizer::findSurroundingTet(const PxVec3d& p, PxI32 startTetNr, PxI32& tetNr)
|
||||
{
|
||||
currentTetMark++;
|
||||
tetNr = startTetNr;
|
||||
|
||||
bool found = false;
|
||||
|
||||
while (!found)
|
||||
{
|
||||
if (tetNr < 0 || tetMarks[tetNr] == currentTetMark) // circular, something went wrong
|
||||
break;
|
||||
tetMarks[tetNr] = currentTetMark;
|
||||
|
||||
PxVec3d c = getTetCenter(tetNr);
|
||||
|
||||
PxI32* ids = &tetIds[4 * tetNr];
|
||||
PxF64 minT = DBL_MAX;
|
||||
PxI32 minFaceNr = -1;
|
||||
|
||||
for (PxI32 i = 0; i < 4; i++)
|
||||
{
|
||||
const PxVec3d& p0 = tetVerts[ids[tetFaces[i][0]]];
|
||||
const PxVec3d& p1 = tetVerts[ids[tetFaces[i][1]]];
|
||||
const PxVec3d& p2 = tetVerts[ids[tetFaces[i][2]]];
|
||||
|
||||
PxVec3d n = (p1 - p0).cross(p2 - p0);
|
||||
n = n.getNormalized();
|
||||
PxF64 hp = (p - p0).dot(n);
|
||||
PxF64 hc = (c - p0).dot(n);
|
||||
|
||||
PxF64 t = hp - hc;
|
||||
if (t == 0.0)
|
||||
continue;
|
||||
t = -hc / t; // time when c -> p hits the face
|
||||
if (t >= 0.0 && t < minT) { // in front and new min
|
||||
minT = t;
|
||||
minFaceNr = i;
|
||||
}
|
||||
}
|
||||
if (minT >= 1.0)
|
||||
found = true;
|
||||
else
|
||||
tetNr = tetNeighbors[4 * tetNr + minFaceNr];
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------
|
||||
bool OctreeTetrahedralizer::findSurroundingTet(const PxVec3d& p, PxI32& tetNr)
|
||||
{
|
||||
PxI32 startTet = 0;
|
||||
PxI32 cellNr = 0;
|
||||
while (cellNr >= 0)
|
||||
{
|
||||
if (cells[cellNr].closestTetNr >= 0)
|
||||
startTet = cells[cellNr].closestTetNr;
|
||||
cellNr = cells[cellNr].getChildNr(p);
|
||||
}
|
||||
|
||||
return findSurroundingTet(p, startTet, tetNr);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------
|
||||
static PxVec3d getCircumCenter(PxVec3d& p0, PxVec3d& p1, PxVec3d& p2, PxVec3d& p3)
|
||||
{
|
||||
PxVec3d b = p1 - p0;
|
||||
PxVec3d c = p2 - p0;
|
||||
PxVec3d d = p3 - p0;
|
||||
PxF64 det = 2.0 * (b.x*(c.y*d.z - c.z*d.y) - b.y*(c.x*d.z - c.z*d.x) + b.z*(c.x*d.y - c.y*d.x));
|
||||
if (det == 0.0)
|
||||
return p0;
|
||||
else
|
||||
{
|
||||
PxVec3d v = c.cross(d)*b.dot(b) + d.cross(b)*c.dot(c) + b.cross(c)*d.dot(d);
|
||||
v /= det;
|
||||
return p0 + v;
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------
|
||||
bool OctreeTetrahedralizer::meshInsertTetVert(PxI32 vertNr)
|
||||
{
|
||||
const PxVec3d& p = tetVerts[vertNr];
|
||||
PxI32 surroundingTetNr;
|
||||
if (!findSurroundingTet(p, surroundingTetNr))
|
||||
return false;
|
||||
|
||||
// find violating tets
|
||||
|
||||
violatingTets.clear();
|
||||
stack.clear();
|
||||
currentTetMark++;
|
||||
stack.pushBack(surroundingTetNr);
|
||||
|
||||
while (!stack.empty())
|
||||
{
|
||||
PxI32 tetNr = stack.back();
|
||||
stack.popBack();
|
||||
if (tetMarks[tetNr] == currentTetMark)
|
||||
continue;
|
||||
tetMarks[tetNr] = currentTetMark;
|
||||
violatingTets.pushBack(tetNr);
|
||||
|
||||
for (PxI32 i = 0; i < 4; i++)
|
||||
{
|
||||
PxI32 n = tetNeighbors[4 * tetNr + i];
|
||||
if (n < 0 || tetMarks[n] == currentTetMark)
|
||||
continue;
|
||||
|
||||
// Delaunay condition test
|
||||
PxI32* ids = &tetIds[4 * n];
|
||||
PxVec3d c = getCircumCenter(tetVerts[ids[0]], tetVerts[ids[1]], tetVerts[ids[2]], tetVerts[ids[3]]);
|
||||
|
||||
PxF64 r2 = (tetVerts[ids[0]] - c).magnitudeSquared();
|
||||
if ((p - c).magnitudeSquared() < r2)
|
||||
stack.pushBack(n);
|
||||
}
|
||||
}
|
||||
|
||||
// remove old tets, create new ones
|
||||
|
||||
edges.clear();
|
||||
Edge e;
|
||||
|
||||
for (PxI32 i = 0; i < PxI32(violatingTets.size()); i++)
|
||||
{
|
||||
PxI32 tetNr = violatingTets[i];
|
||||
|
||||
// copy information before we delete it
|
||||
PxI32 ids[4], ns[4];
|
||||
for (PxI32 j = 0; j < 4; j++) {
|
||||
ids[j] = tetIds[4 * tetNr + j];
|
||||
ns[j] = tetNeighbors[4 * tetNr + j];
|
||||
}
|
||||
|
||||
// delete the tetrahedron
|
||||
treeRemoveTet(tetNr);
|
||||
removeTetNr(tetNr);
|
||||
|
||||
// visit neighbors
|
||||
for (PxI32 j = 0; j < 4; j++) {
|
||||
PxI32 n = ns[j];
|
||||
if (n < 0 || tetMarks[n] != currentTetMark)
|
||||
{
|
||||
// no neighbor or neighbor is not-violating -> we are facing the border
|
||||
|
||||
// create new tetrahedron
|
||||
|
||||
PxI32 newTetNr = getNewTetNr();
|
||||
PxI32 id0 = ids[tetFaces[j][2]];
|
||||
PxI32 id1 = ids[tetFaces[j][1]];
|
||||
PxI32 id2 = ids[tetFaces[j][0]];
|
||||
|
||||
tetIds[4 * newTetNr] = id0;
|
||||
tetIds[4 * newTetNr + 1] = id1;
|
||||
tetIds[4 * newTetNr + 2] = id2;
|
||||
tetIds[4 * newTetNr + 3] = vertNr;
|
||||
|
||||
treeInsertTet(newTetNr);
|
||||
|
||||
tetNeighbors[4 * newTetNr] = n;
|
||||
|
||||
if (n >= 0)
|
||||
{
|
||||
for (PxI32 k = 0; k < 4; k++)
|
||||
{
|
||||
if (tetNeighbors[4 * n + k] == tetNr)
|
||||
tetNeighbors[4 * n + k] = newTetNr;
|
||||
}
|
||||
}
|
||||
|
||||
// will set the neighbors among the new tetrahedra later
|
||||
|
||||
tetNeighbors[4 * newTetNr + 1] = -1;
|
||||
tetNeighbors[4 * newTetNr + 2] = -1;
|
||||
tetNeighbors[4 * newTetNr + 3] = -1;
|
||||
|
||||
e.init(id0, id1, newTetNr, 1); edges.pushBack(e);
|
||||
e.init(id1, id2, newTetNr, 2); edges.pushBack(e);
|
||||
e.init(id2, id0, newTetNr, 3); edges.pushBack(e);
|
||||
}
|
||||
} // next neighbor
|
||||
} // next violating tetrahedron
|
||||
|
||||
// fix neighbors
|
||||
|
||||
PxSort(edges.begin(), edges.size());
|
||||
|
||||
PxI32 nr = 0;
|
||||
while (nr < PxI32(edges.size()))
|
||||
{
|
||||
Edge& e0 = edges[nr];
|
||||
nr++;
|
||||
if (nr < PxI32(edges.size()) && edges[nr] == e0)
|
||||
{
|
||||
Edge& e1 = edges[nr];
|
||||
|
||||
tetNeighbors[4 * e0.tetNr + e0.faceNr] = e1.tetNr;
|
||||
tetNeighbors[4 * e1.tetNr + e1.faceNr] = e0.tetNr;
|
||||
nr++;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------
|
||||
static PxF64 tetQuality(const PxVec3d& p0, const PxVec3d& p1, const PxVec3d& p2, const PxVec3d& p3)
|
||||
{
|
||||
PxVec3d d0 = p1 - p0;
|
||||
PxVec3d d1 = p2 - p0;
|
||||
PxVec3d d2 = p3 - p0;
|
||||
PxVec3d d3 = p2 - p1;
|
||||
PxVec3d d4 = p3 - p2;
|
||||
PxVec3d d5 = p1 - p3;
|
||||
|
||||
PxF64 s0 = d0.magnitudeSquared();
|
||||
PxF64 s1 = d1.magnitudeSquared();
|
||||
PxF64 s2 = d2.magnitudeSquared();
|
||||
PxF64 s3 = d3.magnitudeSquared();
|
||||
PxF64 s4 = d4.magnitudeSquared();
|
||||
PxF64 s5 = d5.magnitudeSquared();
|
||||
|
||||
PxF64 ms = (s0 + s1 + s2 + s3 + s4 + s5) / 6.0;
|
||||
PxF64 rms = sqrt(ms);
|
||||
|
||||
static const PxF64 s = 12.0 / sqrt(2.0);
|
||||
|
||||
PxF64 vol = d0.dot(d1.cross(d2)) / 6.0;
|
||||
return s * vol / (rms * rms * rms); // 1.0 for regular tetrahedron
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------
|
||||
void OctreeTetrahedralizer::pruneTets()
|
||||
{
|
||||
insideTester.init(surfaceVerts.begin(), PxI32(surfaceVerts.size()),
|
||||
surfaceTriIds.begin(), PxI32(surfaceTriIds.size()) / 3);
|
||||
|
||||
static PxF64 minQuality = 0.01;
|
||||
|
||||
PxI32 numTets = tetIds.size() / 4;
|
||||
|
||||
PxI32 num = 0;
|
||||
|
||||
for (PxI32 i = 0; i < numTets; i++)
|
||||
{
|
||||
bool remove = false;
|
||||
|
||||
PxI32* ids = &tetIds[4 * i];
|
||||
for (PxI32 j = 0; j < 4; j++)
|
||||
{
|
||||
if (ids[j] >= firstABBVert)
|
||||
remove = true;
|
||||
}
|
||||
|
||||
if (ids[0] < 0 || ids[1] < 0 || ids[2] < 0 || ids[3] < 0)
|
||||
remove = true;
|
||||
|
||||
if (!remove)
|
||||
{
|
||||
PxVec3d c = getTetCenter(i);
|
||||
if (!insideTester.isInside(PxVec3(PxReal(c.x), PxReal(c.y), PxReal(c.z))))
|
||||
remove = true;
|
||||
|
||||
if (tetQuality(tetVerts[ids[0]], tetVerts[ids[1]],
|
||||
tetVerts[ids[2]], tetVerts[ids[3]]) < minQuality)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (remove)
|
||||
continue;
|
||||
|
||||
for (PxI32 j = 0; j < 4; j++)
|
||||
tetIds[4 * num + j] = ids[j];
|
||||
num++;
|
||||
}
|
||||
|
||||
tetIds.resize(4 * num);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------
|
||||
void OctreeTetrahedralizer::createTetMesh(const PxArray<PxVec3>& verts, const PxArray<PxU32>& triIds,
|
||||
bool includeOctreeNodes, PxI32 _maxVertsPerCell, PxI32 _maxTreeDepth)
|
||||
{
|
||||
|
||||
this->surfaceVerts = verts;
|
||||
surfaceTriIds.resize(triIds.size());
|
||||
for (PxU32 i = 0; i < triIds.size(); i++)
|
||||
this->surfaceTriIds[i] = triIds[i];
|
||||
this->maxVertsPerCell = _maxVertsPerCell;
|
||||
this->maxTreeDepth = _maxTreeDepth;
|
||||
createTree();
|
||||
|
||||
clearTets();
|
||||
|
||||
if (cells.empty())
|
||||
return;
|
||||
|
||||
createTetVerts(includeOctreeNodes);
|
||||
if (tetVerts.empty())
|
||||
return;
|
||||
|
||||
for (PxI32 i = 0; i < PxI32(cells.size()); i++)
|
||||
cells[i].closestTetNr = -1;
|
||||
|
||||
// create aabb tets
|
||||
|
||||
Bounds3 bounds;
|
||||
bounds.setEmpty();
|
||||
for (PxI32 i = 0; i < PxI32(tetVerts.size()); i++)
|
||||
bounds.include(tetVerts[i]);
|
||||
bounds.expand(bounds.getDimensions().magnitude() * 0.1);
|
||||
|
||||
firstABBVert = PxI32(tetVerts.size());
|
||||
PxVec3d dims = bounds.getDimensions();
|
||||
|
||||
for (PxI32 i = 0; i < 8; i++)
|
||||
{
|
||||
tetVerts.pushBack(bounds.minimum + PxVec3d(
|
||||
cubeCorners[i][0] * dims.x,
|
||||
cubeCorners[i][1] * dims.y,
|
||||
cubeCorners[i][2] * dims.z));
|
||||
}
|
||||
|
||||
for (PxI32 i = 0; i < 6; i++)
|
||||
{
|
||||
for (PxI32 j = 0; j < 4; j++)
|
||||
{
|
||||
tetIds.pushBack(firstABBVert + cubeTets[i][j]);
|
||||
tetNeighbors.pushBack(cubeTetNeighbors[i][j]);
|
||||
}
|
||||
treeInsertTet(i);
|
||||
}
|
||||
|
||||
tetMarks.resize(6, 0);
|
||||
|
||||
for (PxI32 i = 0; i < firstABBVert; i++)
|
||||
{
|
||||
meshInsertTetVert(i);
|
||||
}
|
||||
|
||||
pruneTets();
|
||||
|
||||
renderTriIds.clear();
|
||||
renderVerts.clear();
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------
|
||||
void OctreeTetrahedralizer::readBack(PxArray<PxVec3> &outputTetVerts, PxArray<PxU32> &outputTetIds)
|
||||
{
|
||||
outputTetVerts.resize(tetVerts.size());
|
||||
|
||||
for (PxU32 i = 0; i < tetVerts.size(); i++)
|
||||
{
|
||||
PxVec3d &v = tetVerts[i];
|
||||
outputTetVerts[i] = PxVec3(PxReal(v.x), PxReal(v.y), PxReal(v.z));
|
||||
}
|
||||
|
||||
outputTetIds.resize(tetIds.size());
|
||||
|
||||
for (PxU32 i = 0; i < tetIds.size(); i++)
|
||||
outputTetIds[i] = PxU32(tetIds[i]);
|
||||
}
|
||||
169
engine/third_party/physx/source/physxextensions/src/tet/ExtOctreeTetrahedralizer.h
vendored
Normal file
169
engine/third_party/physx/source/physxextensions/src/tet/ExtOctreeTetrahedralizer.h
vendored
Normal file
@@ -0,0 +1,169 @@
|
||||
// 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 EXT_OCTREE_TETRAHEDRALIZER_H
|
||||
#define EXT_OCTREE_TETRAHEDRALIZER_H
|
||||
|
||||
#include "ExtMultiList.h"
|
||||
#include "ExtVec3.h"
|
||||
#include "foundation/PxVec3.h"
|
||||
#include "ExtInsideTester.h"
|
||||
|
||||
namespace physx
|
||||
{
|
||||
namespace Ext
|
||||
{
|
||||
|
||||
class InsideTester;
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
class OctreeTetrahedralizer
|
||||
{
|
||||
public:
|
||||
OctreeTetrahedralizer();
|
||||
|
||||
void clear();
|
||||
void createTetMesh(const PxArray<PxVec3> &verts, const PxArray<PxU32> &triIds,
|
||||
bool includeOctreeNodes = true, PxI32 maxVertsPerCell = 20, PxI32 maxTreeDepth = 5);
|
||||
|
||||
void readBack(PxArray<PxVec3> &tetVertices, PxArray<PxU32> &tetIndices);
|
||||
|
||||
private:
|
||||
// input mesh
|
||||
|
||||
PxArray<PxVec3> surfaceVerts;
|
||||
PxArray<PxI32> surfaceTriIds;
|
||||
|
||||
// octree
|
||||
|
||||
PxI32 maxVertsPerCell;
|
||||
PxI32 maxTreeDepth;
|
||||
|
||||
struct Cell
|
||||
{
|
||||
void init()
|
||||
{
|
||||
firstChild = -1;
|
||||
orig = PxVec3d(0.0, 0.0, 0.0);
|
||||
size = 0.0;
|
||||
numVerts = 0;
|
||||
closestTetNr = -1;
|
||||
depth = 0;
|
||||
}
|
||||
|
||||
PxI32 getChildNr(const PxVec3d& p);
|
||||
|
||||
PX_FORCE_INLINE PxI32 getChildNr(const PxVec3& p)
|
||||
{
|
||||
return getChildNr(PxVec3d(PxF64(p.x), PxF64(p.y), PxF64(p.z)));
|
||||
}
|
||||
|
||||
PxI32 firstChild;
|
||||
PxI32 firstCellVert;
|
||||
PxI32 firstCellTet;
|
||||
PxVec3d orig;
|
||||
double size;
|
||||
PxI32 numVerts;
|
||||
PxI32 closestTetNr;
|
||||
PxI32 depth;
|
||||
};
|
||||
|
||||
PxArray<Cell> cells;
|
||||
MultiList<PxI32> vertsOfCell;
|
||||
|
||||
// tet mesh
|
||||
|
||||
PxArray<PxVec3d> tetVerts;
|
||||
PxArray<PxI32> tetIds;
|
||||
PxArray<PxI32> tetNeighbors;
|
||||
PxArray<PxI32> tetMarks;
|
||||
PxI32 currentTetMark;
|
||||
PxArray<PxI32> stack;
|
||||
PxArray<PxI32> violatingTets;
|
||||
PxI32 firstABBVert;
|
||||
|
||||
struct Edge
|
||||
{
|
||||
|
||||
PxI32 id0, id1;
|
||||
PxI32 faceNr, tetNr;
|
||||
|
||||
void init(PxI32 _id0, PxI32 _id1, PxI32 _tetNr, PxI32 _faceNr)
|
||||
{
|
||||
this->id0 = _id0 < _id1 ? _id0 : _id1;
|
||||
this->id1 = _id0 > _id1 ? _id0 : _id1;
|
||||
this->tetNr = _tetNr;
|
||||
this->faceNr = _faceNr;
|
||||
}
|
||||
|
||||
PX_FORCE_INLINE bool operator < (Edge e) const
|
||||
{
|
||||
if (id0 < e.id0) return true;
|
||||
if (id0 > e.id0) return false;
|
||||
return id1 < e.id1;
|
||||
}
|
||||
|
||||
PX_FORCE_INLINE bool operator == (Edge e)
|
||||
{
|
||||
return id0 == e.id0 && id1 == e.id1;
|
||||
}
|
||||
};
|
||||
PxArray<Edge> edges;
|
||||
|
||||
void clearTets();
|
||||
void createTree();
|
||||
void treeInsertVert(PxI32 cellNr, PxI32 vertNr);
|
||||
void createTetVerts(bool includeOctreeNodes);
|
||||
|
||||
bool findSurroundingTet(const PxVec3d& p, PxI32 startTetNr, PxI32& tetNr);
|
||||
bool findSurroundingTet(const PxVec3d& p, PxI32& tetNr);
|
||||
void treeInsertTet(PxI32 tetNr);
|
||||
void treeRemoveTet(PxI32 tetNr);
|
||||
|
||||
PxI32 firstFreeTet;
|
||||
PxI32 getNewTetNr();
|
||||
void removeTetNr(PxI32 tetNr);
|
||||
|
||||
PxVec3d getTetCenter(PxI32 tetNr) const;
|
||||
bool meshInsertTetVert(PxI32 vertNr);
|
||||
|
||||
InsideTester insideTester;
|
||||
void pruneTets();
|
||||
|
||||
mutable float prevClip;
|
||||
mutable float prevScale;
|
||||
mutable PxArray<PxVec3> renderVerts;
|
||||
mutable PxArray<PxVec3> renderNormals;
|
||||
mutable PxArray<PxI32> renderTriIds;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
93
engine/third_party/physx/source/physxextensions/src/tet/ExtQuadric.h
vendored
Normal file
93
engine/third_party/physx/source/physxextensions/src/tet/ExtQuadric.h
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
// 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.
|
||||
|
||||
#ifndef EXT_QUADRIC_H
|
||||
#define EXT_QUADRIC_H
|
||||
|
||||
#include "foundation/PxVec3.h"
|
||||
|
||||
// MM: used in ExtMeshSimplificator
|
||||
// see paper Garland and Heckbert: "Surface Simplification Using Quadric Error Metrics"
|
||||
|
||||
namespace physx
|
||||
{
|
||||
namespace Ext
|
||||
{
|
||||
class Quadric {
|
||||
public:
|
||||
PX_FORCE_INLINE void zero()
|
||||
{
|
||||
a00 = 0.0f; a01 = 0.0f; a02 = 0.0f; a03 = 0.0f;
|
||||
a11 = 0.0f; a12 = 0.0f; a13 = 0.0f;
|
||||
a22 = 0.0f; a23 = 0.0f;
|
||||
a33 = 0.0f;
|
||||
}
|
||||
|
||||
PX_FORCE_INLINE void setFromPlane(const PxVec3& v0, const PxVec3& v1, const PxVec3& v2)
|
||||
{
|
||||
PxVec3 n = (v1 - v0).cross(v2 - v0); n.normalize();
|
||||
float d = -n.dot(v0);
|
||||
a00 = n.x * n.x; a01 = n.x * n.y; a02 = n.x * n.z; a03 = n.x * d;
|
||||
a11 = n.y * n.y; a12 = n.y * n.z; a13 = n.y * d;
|
||||
a22 = n.z * n.z; a23 = n.z * d;
|
||||
a33 = d * d;
|
||||
}
|
||||
|
||||
PX_FORCE_INLINE Quadric operator +(const Quadric& q) const
|
||||
{
|
||||
Quadric sum;
|
||||
sum.a00 = a00 + q.a00; sum.a01 = a01 + q.a01; sum.a02 = a02 + q.a02; sum.a03 = a03 + q.a03;
|
||||
sum.a11 = a11 + q.a11; sum.a12 = a12 + q.a12; sum.a13 = a13 + q.a13;
|
||||
sum.a22 = a22 + q.a22; sum.a23 = a23 + q.a23;
|
||||
sum.a33 = a33 + q.a33;
|
||||
return sum;
|
||||
}
|
||||
|
||||
void operator +=(const Quadric& q)
|
||||
{
|
||||
a00 += q.a00; a01 += q.a01; a02 += q.a02; a03 += q.a03;
|
||||
a11 += q.a11; a12 += q.a12; a13 += q.a13;
|
||||
a22 += q.a22; a23 += q.a23;
|
||||
a33 += q.a33;
|
||||
}
|
||||
|
||||
PxF32 outerProduct(const PxVec3& v)
|
||||
{
|
||||
return a00 * v.x * v.x + 2.0f * a01 * v.x * v.y + 2.0f * a02 * v.x * v.z + 2.0f * a03 * v.x +
|
||||
a11 * v.y * v.y + 2.0f * a12 * v.y * v.z + 2.0f * a13 * v.y +
|
||||
a22 * v.z * v.z + 2.0f * a23 * v.z + a33;
|
||||
}
|
||||
private:
|
||||
PxF32 a00, a01, a02, a03;
|
||||
PxF32 a11, a12, a13;
|
||||
PxF32 a22, a23;
|
||||
PxF32 a33;
|
||||
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
218
engine/third_party/physx/source/physxextensions/src/tet/ExtRandomAccessHeap.h
vendored
Normal file
218
engine/third_party/physx/source/physxextensions/src/tet/ExtRandomAccessHeap.h
vendored
Normal file
@@ -0,0 +1,218 @@
|
||||
// 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.
|
||||
|
||||
#ifndef EXT_RANDOM_ACCESS_HEAP_H
|
||||
#define EXT_RANDOM_ACCESS_HEAP_H
|
||||
|
||||
#include "foundation/PxArray.h"
|
||||
|
||||
// MM: heap which allows the modification of the priorities of entries stored anywhere in the tree
|
||||
// for this, every entry gets an id via which it can be accessed
|
||||
|
||||
// used in ExtMeshSimplificator to sort edges w.r.t. their error metric
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
namespace physx
|
||||
{
|
||||
namespace Ext
|
||||
{
|
||||
|
||||
template <class T> class RandomAccessHeap
|
||||
{
|
||||
public:
|
||||
RandomAccessHeap() {
|
||||
heap.resize(1); // dummy such that root is at 1 for faster parent child computation
|
||||
ids.resize(1);
|
||||
nextId = 0;
|
||||
}
|
||||
|
||||
void resizeFast(PxArray<PxI32>& arr, PxU32 newSize, PxI32 value = 0)
|
||||
{
|
||||
if (newSize < arr.size())
|
||||
arr.removeRange(newSize, arr.size() - newSize);
|
||||
else
|
||||
{
|
||||
while (arr.size() < newSize)
|
||||
arr.pushBack(value);
|
||||
}
|
||||
}
|
||||
|
||||
PxI32 insert(T elem, PxI32 id = -1) // id for ability to alter the entry later or to identify duplicates
|
||||
{
|
||||
if (id < 0) {
|
||||
id = nextId;
|
||||
nextId++;
|
||||
}
|
||||
else if (id >= nextId)
|
||||
nextId = id + 1;
|
||||
|
||||
if (id >= 0 && id < PxI32(posOfId.size()) && posOfId[id] >= 0)
|
||||
return id; // already in heap
|
||||
|
||||
heap.pushBack(elem);
|
||||
ids.pushBack(id);
|
||||
|
||||
if (id >= PxI32(posOfId.size()))
|
||||
resizeFast(posOfId, id + 1, -1);
|
||||
|
||||
posOfId[id] = heap.size() - 1;
|
||||
|
||||
percolate(PxI32(heap.size()) - 1);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
bool remove(PxI32 id)
|
||||
{
|
||||
PxI32 i = posOfId[id];
|
||||
if (i < 0)
|
||||
return false;
|
||||
|
||||
posOfId[id] = -1;
|
||||
T prev = heap[i];
|
||||
|
||||
heap[i] = heap.back();
|
||||
heap.popBack();
|
||||
ids[i] = ids.back();
|
||||
ids.popBack();
|
||||
|
||||
if (i < PxI32(heap.size()))
|
||||
{
|
||||
posOfId[ids[i]] = i;
|
||||
|
||||
if (heap.size() > 1)
|
||||
{
|
||||
if (heap[i] < prev)
|
||||
percolate(i);
|
||||
else
|
||||
siftDown(i);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
T deleteMin()
|
||||
{
|
||||
T min(-1, -1, 0.0f);
|
||||
|
||||
if (heap.size() > 1)
|
||||
{
|
||||
min = heap[1];
|
||||
posOfId[ids[1]] = -1;
|
||||
|
||||
heap[1] = heap.back();
|
||||
heap.popBack();
|
||||
ids[1] = ids.back();
|
||||
ids.popBack();
|
||||
posOfId[ids[1]] = 1;
|
||||
|
||||
siftDown(1);
|
||||
}
|
||||
|
||||
return min;
|
||||
}
|
||||
|
||||
void makeHeap(const PxArray<T> &elems) // O(n) instead of inserting one after the other O(n log n)
|
||||
{
|
||||
heap.resize(elems.size() + 1);
|
||||
ids.resize(elems.size() + 1);
|
||||
posOfId.resize(elems.size());
|
||||
|
||||
for (PxU32 i = 0; i < elems.size(); i++)
|
||||
{
|
||||
heap[i + 1] = elems[i];
|
||||
ids[i + 1] = i;
|
||||
posOfId[ids[i + 1]] = i + 1;
|
||||
}
|
||||
|
||||
PxI32 n = (heap.size() - 1) >> 1;
|
||||
for (PxI32 i = n; i >= 1; i--)
|
||||
siftDown(i);
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
heap.capacity() == 0 ? heap.resize(1) : heap.forceSize_Unsafe(1);
|
||||
ids.capacity() == 0 ? ids.resize(1) : ids.forceSize_Unsafe(1);
|
||||
posOfId.forceSize_Unsafe(0);
|
||||
nextId = 0;
|
||||
}
|
||||
PX_FORCE_INLINE PxI32 size() { return heap.size() - 1; }
|
||||
PX_FORCE_INLINE bool empty() { return heap.size() <= 1; }
|
||||
|
||||
private:
|
||||
void siftDown(PxI32 i)
|
||||
{
|
||||
PxI32 n = PxI32(heap.size()) - 1;
|
||||
PxI32 k = i;
|
||||
PxI32 j;
|
||||
do
|
||||
{
|
||||
j = k;
|
||||
if (2 * j < n && heap[2 * j] < heap[k])
|
||||
k = 2 * j;
|
||||
if (2 * j < n && heap[2 * j + 1] < heap[k])
|
||||
k = 2 * j + 1;
|
||||
T temp = heap[j]; heap[j] = heap[k]; heap[k] = temp;
|
||||
PxI32 id = ids[j]; ids[j] = ids[k]; ids[k] = id;
|
||||
|
||||
posOfId[ids[j]] = j;
|
||||
posOfId[ids[k]] = k;
|
||||
|
||||
}
|
||||
while (j != k);
|
||||
}
|
||||
|
||||
void percolate(PxI32 i)
|
||||
{
|
||||
PxI32 k = i;
|
||||
PxI32 j;
|
||||
do
|
||||
{
|
||||
j = k;
|
||||
if (j > 1 && !(heap[j >> 1] < heap[k]))
|
||||
k = j >> 1;
|
||||
T temp = heap[j]; heap[j] = heap[k]; heap[k] = temp;
|
||||
PxI32 id = ids[j]; ids[j] = ids[k]; ids[k] = id;
|
||||
|
||||
posOfId[ids[j]] = j;
|
||||
posOfId[ids[k]] = k;
|
||||
}
|
||||
while (j != k);
|
||||
}
|
||||
|
||||
PxArray<T> heap;
|
||||
|
||||
PxArray<PxI32> ids;
|
||||
PxArray<PxI32> posOfId;
|
||||
PxI32 nextId;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
656
engine/third_party/physx/source/physxextensions/src/tet/ExtRemesher.cpp
vendored
Normal file
656
engine/third_party/physx/source/physxextensions/src/tet/ExtRemesher.cpp
vendored
Normal file
@@ -0,0 +1,656 @@
|
||||
// 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.
|
||||
|
||||
#include "ExtRemesher.h"
|
||||
#include "ExtBVH.h"
|
||||
#include "ExtMarchingCubesTable.h"
|
||||
#include "foundation/PxFPU.h"
|
||||
#include "foundation/PxSort.h"
|
||||
#include "GuIntersectionTriangleBox.h"
|
||||
#include "GuBox.h"
|
||||
|
||||
using namespace physx;
|
||||
using namespace Ext;
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
void Remesher::clear()
|
||||
{
|
||||
cells.clear();
|
||||
vertices.clear();
|
||||
normals.clear();
|
||||
triIds.clear();
|
||||
triNeighbors.clear();
|
||||
}
|
||||
|
||||
#define HASH_SIZE 170111
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
PX_FORCE_INLINE static PxU32 hash(PxI32 xi, PxI32 yi, PxI32 zi)
|
||||
{
|
||||
PxU32 h = (xi * 92837111) ^ (yi * 689287499) ^ (zi * 283923481);
|
||||
return h % HASH_SIZE;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
void Remesher::addCell(PxI32 xi, PxI32 yi, PxI32 zi)
|
||||
{
|
||||
Cell c;
|
||||
c.init(xi, yi, zi);
|
||||
PxU32 h = hash(xi, yi, zi);
|
||||
c.next = firstCell[h];
|
||||
firstCell[h] = PxI32(cells.size());
|
||||
cells.pushBack(c);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
PxI32 Remesher::getCellNr(PxI32 xi, PxI32 yi, PxI32 zi) const
|
||||
{
|
||||
PxU32 h = hash(xi, yi, zi);
|
||||
PxI32 nr = firstCell[h];
|
||||
while (nr >= 0)
|
||||
{
|
||||
const Cell& c = cells[nr];
|
||||
if (c.xi == xi && c.yi == yi && c.zi == zi)
|
||||
return nr;
|
||||
nr = c.next;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
PX_FORCE_INLINE bool Remesher::cellExists(PxI32 xi, PxI32 yi, PxI32 zi) const
|
||||
{
|
||||
return getCellNr(xi, yi, zi) >= 0;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
void Remesher::remesh(const PxArray<PxVec3>& inputVerts, const PxArray<PxU32>& inputTriIds,
|
||||
PxU32 resolution, PxArray<PxU32> *vertexMap)
|
||||
{
|
||||
PX_SIMD_GUARD
|
||||
remesh(inputVerts.begin(), inputVerts.size(), inputTriIds.begin(), inputTriIds.size(), resolution, vertexMap);
|
||||
}
|
||||
|
||||
void Remesher::remesh(const PxVec3* inputVerts, PxU32 nbVertices, const PxU32* inputTriIds, PxU32 nbTriangleIndices, PxU32 resolution, PxArray<PxU32> *vertexMap)
|
||||
{
|
||||
clear();
|
||||
|
||||
PxBounds3 meshBounds;
|
||||
meshBounds.setEmpty();
|
||||
|
||||
for (PxU32 i = 0; i < nbVertices; i++)
|
||||
meshBounds.include(inputVerts[i]);
|
||||
|
||||
PxVec3 dims = meshBounds.getDimensions();
|
||||
|
||||
float spacing = PxMax(dims.x, PxMax(dims.y, dims.z)) / resolution;
|
||||
|
||||
meshBounds.fattenFast(3.0f * spacing);
|
||||
|
||||
PxU32 numTris = nbTriangleIndices / 3;
|
||||
PxBounds3 triBounds, cellBounds;
|
||||
Gu::BoxPadded box;
|
||||
box.rot = PxMat33(PxIdentity);
|
||||
|
||||
firstCell.clear();
|
||||
firstCell.resize(HASH_SIZE, -1);
|
||||
|
||||
// create sparse overlapping cells
|
||||
|
||||
for (PxU32 i = 0; i < numTris; i++)
|
||||
{
|
||||
const PxVec3& p0 = inputVerts[inputTriIds[3 * i]];
|
||||
const PxVec3& p1 = inputVerts[inputTriIds[3 * i + 1]];
|
||||
const PxVec3& p2 = inputVerts[inputTriIds[3 * i + 2]];
|
||||
|
||||
triBounds.setEmpty();
|
||||
triBounds.include(p0);
|
||||
triBounds.include(p1);
|
||||
triBounds.include(p2);
|
||||
|
||||
PxI32 x0 = PxI32(PxFloor((triBounds.minimum.x - meshBounds.minimum.x) / spacing));
|
||||
PxI32 y0 = PxI32(PxFloor((triBounds.minimum.y - meshBounds.minimum.y) / spacing));
|
||||
PxI32 z0 = PxI32(PxFloor((triBounds.minimum.z - meshBounds.minimum.z) / spacing));
|
||||
|
||||
PxI32 x1 = PxI32(PxFloor((triBounds.maximum.x - meshBounds.minimum.x) / spacing)) + 1;
|
||||
PxI32 y1 = PxI32(PxFloor((triBounds.maximum.y - meshBounds.minimum.y) / spacing)) + 1;
|
||||
PxI32 z1 = PxI32(PxFloor((triBounds.maximum.z - meshBounds.minimum.z) / spacing)) + 1;
|
||||
|
||||
for (PxI32 xi = x0; xi <= x1; xi++)
|
||||
{
|
||||
for (PxI32 yi = y0; yi <= y1; yi++)
|
||||
{
|
||||
for (PxI32 zi = z0; zi <= z1; zi++)
|
||||
{
|
||||
cellBounds.minimum.x = meshBounds.minimum.x + xi * spacing;
|
||||
cellBounds.minimum.y = meshBounds.minimum.y + yi * spacing;
|
||||
cellBounds.minimum.z = meshBounds.minimum.z + zi * spacing;
|
||||
cellBounds.maximum = cellBounds.minimum + PxVec3(spacing, spacing, spacing);
|
||||
cellBounds.fattenFast(1e-5f);
|
||||
|
||||
box.center = cellBounds.getCenter();
|
||||
box.extents = cellBounds.getExtents();
|
||||
|
||||
if (!Gu::intersectTriangleBox(box, p0, p1, p2))
|
||||
continue;
|
||||
|
||||
if (!cellExists(xi, yi, zi))
|
||||
addCell(xi, yi, zi);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// using marching cubes to create boundaries
|
||||
|
||||
vertices.clear();
|
||||
cellOfVertex.clear();
|
||||
triIds.clear();
|
||||
|
||||
PxI32 edgeVertId[12];
|
||||
PxVec3 cornerPos[8];
|
||||
int cornerVoxelNr[8];
|
||||
|
||||
for (PxI32 i = 0; i < PxI32(cells.size()); i++)
|
||||
{
|
||||
Cell& c = cells[i];
|
||||
|
||||
// we need to handle a 2 x 2 x 2 block of cells to cover the boundary
|
||||
|
||||
for (PxI32 dx = 0; dx < 2; dx++)
|
||||
{
|
||||
for (PxI32 dy = 0; dy < 2; dy++)
|
||||
{
|
||||
for (PxI32 dz = 0; dz < 2; dz++)
|
||||
{
|
||||
PxI32 xi = c.xi + dx;
|
||||
PxI32 yi = c.yi + dy;
|
||||
PxI32 zi = c.zi + dz;
|
||||
|
||||
// are we responsible for this cell?
|
||||
|
||||
PxI32 maxCellNr = i;
|
||||
|
||||
for (PxI32 rx = xi - 1; rx <= xi; rx++)
|
||||
for (PxI32 ry = yi - 1; ry <= yi; ry++)
|
||||
for (PxI32 rz = zi - 1; rz <= zi; rz++)
|
||||
maxCellNr = PxMax(maxCellNr, getCellNr(rx, ry, rz));
|
||||
|
||||
if (maxCellNr != i)
|
||||
continue;
|
||||
|
||||
PxI32 code = 0;
|
||||
for (PxI32 j = 0; j < 8; j++)
|
||||
{
|
||||
PxI32 mx = xi - 1 + marchingCubeCorners[j][0];
|
||||
PxI32 my = yi - 1 + marchingCubeCorners[j][1];
|
||||
PxI32 mz = zi - 1 + marchingCubeCorners[j][2];
|
||||
cornerVoxelNr[j] = getCellNr(mx, my, mz);
|
||||
|
||||
if (cornerVoxelNr[j] >= 0)
|
||||
code |= (1 << j);
|
||||
|
||||
cornerPos[j].x = meshBounds.minimum.x + (mx + 0.5f) * spacing;
|
||||
cornerPos[j].y = meshBounds.minimum.y + (my + 0.5f) * spacing;
|
||||
cornerPos[j].z = meshBounds.minimum.z + (mz + 0.5f) * spacing;
|
||||
}
|
||||
PxI32 first = firstMarchingCubesId[code];
|
||||
PxI32 num = (firstMarchingCubesId[code + 1] - first);
|
||||
|
||||
// create vertices and tris
|
||||
|
||||
for (PxI32 j = 0; j < 12; j++)
|
||||
edgeVertId[j] = -1;
|
||||
|
||||
for (PxI32 j = num - 1; j >= 0; j--)
|
||||
{
|
||||
PxI32 edgeId = marchingCubesIds[first + j];
|
||||
if (edgeVertId[edgeId] < 0)
|
||||
{
|
||||
PxI32 id0 = marchingCubeEdges[edgeId][0];
|
||||
PxI32 id1 = marchingCubeEdges[edgeId][1];
|
||||
PxVec3& p0 = cornerPos[id0];
|
||||
PxVec3& p1 = cornerPos[id1];
|
||||
edgeVertId[edgeId] = vertices.size();
|
||||
vertices.pushBack((p0 + p1) * 0.5f);
|
||||
cellOfVertex.pushBack(PxMax(cornerVoxelNr[id0], cornerVoxelNr[id1]));
|
||||
}
|
||||
triIds.pushBack(edgeVertId[edgeId]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
removeDuplicateVertices();
|
||||
pruneInternalSurfaces();
|
||||
|
||||
project(inputVerts, inputTriIds, nbTriangleIndices, 2.0f * spacing, 0.1f * spacing);
|
||||
|
||||
if (vertexMap)
|
||||
createVertexMap(inputVerts, nbVertices, meshBounds.minimum, spacing, *vertexMap);
|
||||
|
||||
computeNormals();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
void Remesher::removeDuplicateVertices()
|
||||
{
|
||||
PxF32 eps = 1e-5f;
|
||||
|
||||
struct Ref
|
||||
{
|
||||
PxF32 d;
|
||||
PxI32 id;
|
||||
bool operator < (const Ref& r) const
|
||||
{
|
||||
return d < r.d;
|
||||
}
|
||||
};
|
||||
|
||||
PxI32 numVerts = PxI32(vertices.size());
|
||||
|
||||
PxArray<Ref> refs(numVerts);
|
||||
for (PxI32 i = 0; i < numVerts; i++)
|
||||
{
|
||||
PxVec3& p = vertices[i];
|
||||
refs[i].d = p.x + 0.3f * p.y + 0.1f * p.z;
|
||||
refs[i].id = i;
|
||||
}
|
||||
|
||||
PxSort(refs.begin(), refs.size());
|
||||
|
||||
PxArray<PxI32> idMap(vertices.size(), -1);
|
||||
PxArray<PxVec3> oldVerts = vertices;
|
||||
PxArray<PxI32> oldCellOfVertex = cellOfVertex;
|
||||
vertices.clear();
|
||||
cellOfVertex.clear();
|
||||
|
||||
PxI32 nr = 0;
|
||||
while (nr < numVerts)
|
||||
{
|
||||
Ref& r = refs[nr];
|
||||
nr++;
|
||||
if (idMap[r.id] >= 0)
|
||||
continue;
|
||||
idMap[r.id] = vertices.size();
|
||||
vertices.pushBack(oldVerts[r.id]);
|
||||
cellOfVertex.pushBack(oldCellOfVertex[r.id]);
|
||||
|
||||
PxI32 i = nr;
|
||||
while (i < numVerts && fabsf(refs[i].d - r.d) < eps)
|
||||
{
|
||||
PxI32 id = refs[i].id;
|
||||
if ((oldVerts[r.id] - oldVerts[id]).magnitudeSquared() < eps * eps)
|
||||
idMap[id] = idMap[r.id];
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
for (PxI32 i = 0; i < PxI32(triIds.size()); i++)
|
||||
triIds[i] = idMap[triIds[i]];
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
void Remesher::findTriNeighbors()
|
||||
{
|
||||
PxI32 numTris = PxI32(triIds.size()) / 3;
|
||||
triNeighbors.clear();
|
||||
triNeighbors.resize(3 * numTris, -1);
|
||||
|
||||
struct Edge
|
||||
{
|
||||
void init(PxI32 _id0, PxI32 _id1, PxI32 _triNr, PxI32 _edgeNr)
|
||||
{
|
||||
this->id0 = PxMin(_id0, _id1);
|
||||
this->id1 = PxMax(_id0, _id1);
|
||||
this->triNr = _triNr;
|
||||
this->edgeNr = _edgeNr;
|
||||
}
|
||||
bool operator < (const Edge& e) const
|
||||
{
|
||||
if (id0 < e.id0) return true;
|
||||
if (id0 > e.id0) return false;
|
||||
return id1 < e.id1;
|
||||
}
|
||||
bool operator == (const Edge& e) const
|
||||
{
|
||||
return id0 == e.id0 && id1 == e.id1;
|
||||
}
|
||||
PxI32 id0, id1, triNr, edgeNr;
|
||||
};
|
||||
|
||||
PxArray<Edge> edges(triIds.size());
|
||||
|
||||
for (PxI32 i = 0; i < numTris; i++)
|
||||
{
|
||||
for (PxI32 j = 0; j < 3; j++) {
|
||||
PxI32 id0 = triIds[3 * i + j];
|
||||
PxI32 id1 = triIds[3 * i + (j + 1) % 3];
|
||||
edges[3 * i + j].init(id0, id1, i, j);
|
||||
}
|
||||
}
|
||||
PxSort(edges.begin(), edges.size());
|
||||
|
||||
PxI32 nr = 0;
|
||||
while (nr < PxI32(edges.size()))
|
||||
{
|
||||
Edge& e0 = edges[nr];
|
||||
nr++;
|
||||
while (nr < PxI32(edges.size()) && edges[nr] == e0) {
|
||||
Edge& e1 = edges[nr];
|
||||
triNeighbors[3 * e0.triNr + e0.edgeNr] = e1.triNr;
|
||||
triNeighbors[3 * e1.triNr + e1.edgeNr] = e0.triNr;
|
||||
nr++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
void Remesher::pruneInternalSurfaces()
|
||||
{
|
||||
// flood islands, if the enclosed volume is negative remove it
|
||||
|
||||
findTriNeighbors();
|
||||
|
||||
PxI32 numTris = PxI32(triIds.size()) / 3;
|
||||
|
||||
PxArray<PxI32> oldTriIds = triIds;
|
||||
triIds.clear();
|
||||
|
||||
PxArray<bool> visited(numTris, false);
|
||||
PxArray<PxI32> stack;
|
||||
|
||||
for (PxI32 i = 0; i < numTris; i++)
|
||||
{
|
||||
if (visited[i])
|
||||
continue;
|
||||
stack.clear();
|
||||
stack.pushBack(i);
|
||||
PxI32 islandStart = PxI32(triIds.size());
|
||||
|
||||
float vol = 0.0f;
|
||||
|
||||
while (!stack.empty())
|
||||
{
|
||||
PxI32 triNr = stack.back();
|
||||
stack.popBack();
|
||||
if (visited[triNr])
|
||||
continue;
|
||||
visited[triNr] = true;
|
||||
for (PxI32 j = 0; j < 3; j++)
|
||||
triIds.pushBack(oldTriIds[3 * triNr + j]);
|
||||
|
||||
const PxVec3& p0 = vertices[oldTriIds[3 * triNr]];
|
||||
const PxVec3& p1 = vertices[oldTriIds[3 * triNr + 1]];
|
||||
const PxVec3& p2 = vertices[oldTriIds[3 * triNr + 2]];
|
||||
vol += p0.cross(p1).dot(p2);
|
||||
|
||||
for (PxI32 j = 0; j < 3; j++)
|
||||
{
|
||||
PxI32 n = triNeighbors[3 * triNr + j];
|
||||
if (n >= 0 && !visited[n])
|
||||
stack.pushBack(n);
|
||||
}
|
||||
}
|
||||
|
||||
if (vol <= 0.0f)
|
||||
triIds.resize(islandStart);
|
||||
}
|
||||
|
||||
// remove unreferenced vertices
|
||||
|
||||
PxArray<PxI32> idMap(vertices.size(), -1);
|
||||
PxArray<PxVec3> oldVerts = vertices;
|
||||
PxArray<PxI32> oldCellOfVertex = cellOfVertex;
|
||||
vertices.clear();
|
||||
cellOfVertex.clear();
|
||||
|
||||
for (int i = 0; i < PxI32(triIds.size()); i++)
|
||||
{
|
||||
PxI32 id = triIds[i];
|
||||
if (idMap[id] < 0)
|
||||
{
|
||||
idMap[id] = vertices.size();
|
||||
vertices.pushBack(oldVerts[id]);
|
||||
cellOfVertex.pushBack(oldCellOfVertex[id]);
|
||||
}
|
||||
triIds[i] = idMap[id];
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------
|
||||
// PT: TODO: refactor with other implementation
|
||||
static void getClosestPointOnTriangle(const PxVec3& pos, const PxVec3& p0, const PxVec3& p1, const PxVec3& p2,
|
||||
PxVec3& closest, PxVec3& bary)
|
||||
{
|
||||
PxVec3 e0 = p1 - p0;
|
||||
PxVec3 e1 = p2 - p0;
|
||||
PxVec3 tmp = p0 - pos;
|
||||
|
||||
float a = e0.dot(e0);
|
||||
float b = e0.dot(e1);
|
||||
float c = e1.dot(e1);
|
||||
float d = e0.dot(tmp);
|
||||
float e = e1.dot(tmp);
|
||||
PxVec3 coords, clampedCoords;
|
||||
coords.x = b * e - c * d; // s * det
|
||||
coords.y = b * d - a * e; // t * det
|
||||
coords.z = a * c - b * b; // det
|
||||
|
||||
clampedCoords = PxVec3(PxZero);
|
||||
if (coords.x <= 0.0f)
|
||||
{
|
||||
if (c != 0.0f)
|
||||
clampedCoords.y = -e / c;
|
||||
}
|
||||
else if (coords.y <= 0.0f)
|
||||
{
|
||||
if (a != 0.0f)
|
||||
clampedCoords.x = -d / a;
|
||||
}
|
||||
else if (coords.x + coords.y > coords.z)
|
||||
{
|
||||
float denominator = a + c - b - b;
|
||||
float numerator = c + e - b - d;
|
||||
if (denominator != 0.0f)
|
||||
{
|
||||
clampedCoords.x = numerator / denominator;
|
||||
clampedCoords.y = 1.0f - clampedCoords.x;
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // all inside
|
||||
if (coords.z != 0.0f) {
|
||||
clampedCoords.x = coords.x / coords.z;
|
||||
clampedCoords.y = coords.y / coords.z;
|
||||
}
|
||||
}
|
||||
bary.y = PxMin(PxMax(clampedCoords.x, 0.0f), 1.0f);
|
||||
bary.z = PxMin(PxMax(clampedCoords.y, 0.0f), 1.0f);
|
||||
bary.x = 1.0f - bary.y - bary.z;
|
||||
closest = p0 * bary.x + p1 * bary.y + p2 * bary.z;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
void Remesher::project(const PxVec3* inputVerts, const PxU32* inputTriIds, PxU32 nbTriangleIndices,
|
||||
float searchDist, float surfaceDist)
|
||||
{
|
||||
// build a bvh for the input mesh
|
||||
|
||||
PxI32 numInputTris = PxI32(nbTriangleIndices) / 3;
|
||||
|
||||
if (numInputTris == 0)
|
||||
return;
|
||||
|
||||
bvhBounds.resize(numInputTris);
|
||||
bvhTris.clear();
|
||||
|
||||
for (PxI32 i = 0; i < numInputTris; i++) {
|
||||
PxBounds3& b = bvhBounds[i];
|
||||
b.setEmpty();
|
||||
b.include(inputVerts[inputTriIds[3 * i]]);
|
||||
b.include(inputVerts[inputTriIds[3 * i + 1]]);
|
||||
b.include(inputVerts[inputTriIds[3 * i + 2]]);
|
||||
}
|
||||
|
||||
BVHDesc bvh;
|
||||
BVHBuilder::build(bvh, &bvhBounds[0], bvhBounds.size());
|
||||
|
||||
// project vertices to closest point on surface
|
||||
|
||||
PxBounds3 pb;
|
||||
for (PxU32 i = 0; i < vertices.size(); i++)
|
||||
{
|
||||
PxVec3& p = vertices[i];
|
||||
pb.setEmpty();
|
||||
pb.include(p);
|
||||
pb.fattenFast(searchDist);
|
||||
bvh.query(pb, bvhTris);
|
||||
|
||||
float minDist2 = PX_MAX_F32;
|
||||
PxVec3 closest(PxZero);
|
||||
|
||||
for (PxU32 j = 0; j < bvhTris.size(); j++)
|
||||
{
|
||||
PxI32 triNr = bvhTris[j];
|
||||
const PxVec3& p0 = inputVerts[inputTriIds[3 * triNr]];
|
||||
const PxVec3& p1 = inputVerts[inputTriIds[3 * triNr + 1]];
|
||||
const PxVec3& p2 = inputVerts[inputTriIds[3 * triNr + 2]];
|
||||
PxVec3 c, bary;
|
||||
getClosestPointOnTriangle(p, p0, p1, p2, c, bary);
|
||||
float dist2 = (c - p).magnitudeSquared();
|
||||
if (dist2 < minDist2) {
|
||||
minDist2 = dist2;
|
||||
closest = c;
|
||||
}
|
||||
}
|
||||
|
||||
if (minDist2 < PX_MAX_F32) {
|
||||
PxVec3 n = p - closest;
|
||||
n.normalize();
|
||||
p = closest + n * surfaceDist;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static const int cellNeighbors[6][3] = { { -1,0,0 }, {1,0,0},{0,-1,0},{0,1,0},{0,0,-1},{0,0,1} };
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
void Remesher::createVertexMap(const PxVec3* inputVerts, PxU32 nbVertices, const PxVec3 &gridOrigin, PxF32 &gridSpacing,
|
||||
PxArray<PxU32> &vertexMap)
|
||||
{
|
||||
PxArray<PxI32> vertexOfCell(cells.size(), -1);
|
||||
PxArray<PxI32 > front[2];
|
||||
PxI32 frontNr = 0;
|
||||
|
||||
// compute inverse links
|
||||
|
||||
for (PxI32 i = 0; i < PxI32(vertices.size()); i++)
|
||||
{
|
||||
PxI32 cellNr = cellOfVertex[i];
|
||||
if (cellNr >= 0)
|
||||
{
|
||||
if (vertexOfCell[cellNr] < 0) {
|
||||
vertexOfCell[cellNr] = i;
|
||||
front[frontNr].pushBack(cellNr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// propagate cell->vertex links through the voxel mesh
|
||||
|
||||
while (!front[frontNr].empty())
|
||||
{
|
||||
front[1 - frontNr].clear();
|
||||
|
||||
for (PxI32 i = 0; i < PxI32(front[frontNr].size()); i++)
|
||||
{
|
||||
int cellNr = front[frontNr][i];
|
||||
Cell& c = cells[cellNr];
|
||||
for (PxI32 j = 0; j < 6; j++)
|
||||
{
|
||||
PxI32 n = getCellNr(c.xi + cellNeighbors[j][0],
|
||||
c.yi + cellNeighbors[j][1],
|
||||
c.zi + cellNeighbors[j][2]);
|
||||
if (n >= 0 && vertexOfCell[n] < 0) {
|
||||
vertexOfCell[n] = vertexOfCell[cellNr];
|
||||
front[1 - frontNr].pushBack(n);
|
||||
}
|
||||
}
|
||||
}
|
||||
frontNr = 1 - frontNr;
|
||||
}
|
||||
|
||||
// create the map
|
||||
|
||||
vertexMap.clear();
|
||||
vertexMap.resize(nbVertices, 0);
|
||||
|
||||
for (PxU32 i = 0; i < nbVertices; i++)
|
||||
{
|
||||
const PxVec3& p = inputVerts[i];
|
||||
PxI32 xi = PxI32(PxFloor((p.x - gridOrigin.x) / gridSpacing));
|
||||
PxI32 yi = PxI32(PxFloor((p.y - gridOrigin.y) / gridSpacing));
|
||||
PxI32 zi = PxI32(PxFloor((p.z - gridOrigin.z) / gridSpacing));
|
||||
|
||||
PxI32 cellNr = getCellNr(xi, yi, zi);
|
||||
vertexMap[i] = cellNr >= 0 ? vertexOfCell[cellNr] : 0;
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
void Remesher::computeNormals()
|
||||
{
|
||||
normals.clear();
|
||||
normals.resize(vertices.size(), PxVec3(PxZero));
|
||||
|
||||
for (PxI32 i = 0; i < PxI32(triIds.size()); i += 3)
|
||||
{
|
||||
PxI32* ids = &triIds[i];
|
||||
|
||||
PxVec3& p0 = vertices[ids[0]];
|
||||
PxVec3& p1 = vertices[ids[1]];
|
||||
PxVec3& p2 = vertices[ids[2]];
|
||||
PxVec3 n = (p1 - p0).cross(p2 - p0);
|
||||
normals[ids[0]] += n;
|
||||
normals[ids[1]] += n;
|
||||
normals[ids[2]] += n;
|
||||
}
|
||||
|
||||
for (PxI32 i = 0; i < PxI32(normals.size()); i++)
|
||||
normals[i].normalize();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
void Remesher::readBack(PxArray<PxVec3>& outputVertices, PxArray<PxU32>& outputTriIds)
|
||||
{
|
||||
outputVertices = vertices;
|
||||
outputTriIds.resize(triIds.size());
|
||||
for (PxU32 i = 0; i < triIds.size(); i++)
|
||||
outputTriIds[i] = PxU32(triIds[i]);
|
||||
}
|
||||
96
engine/third_party/physx/source/physxextensions/src/tet/ExtRemesher.h
vendored
Normal file
96
engine/third_party/physx/source/physxextensions/src/tet/ExtRemesher.h
vendored
Normal file
@@ -0,0 +1,96 @@
|
||||
// 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.
|
||||
|
||||
|
||||
#ifndef EXT_REMESHER_H
|
||||
#define EXT_REMESHER_H
|
||||
|
||||
#include "foundation/PxBounds3.h"
|
||||
#include "foundation/PxArray.h"
|
||||
|
||||
namespace physx
|
||||
{
|
||||
namespace Ext
|
||||
{
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
class Remesher {
|
||||
public:
|
||||
|
||||
Remesher() {}
|
||||
~Remesher() {}
|
||||
|
||||
void remesh(const PxVec3* verts, PxU32 nbVertices, const PxU32* triIds, PxU32 nbTriangleIndices, PxU32 resolution = 100, PxArray<PxU32> *vertexMap = nullptr);
|
||||
void remesh(const PxArray<PxVec3>& verts, const PxArray<PxU32>& triIds, PxU32 resolution = 100, PxArray<PxU32> *vertexMap = nullptr);
|
||||
|
||||
void clear();
|
||||
void readBack(PxArray<PxVec3>& vertices, PxArray<PxU32>& triIds);
|
||||
|
||||
private:
|
||||
PxArray<PxVec3> vertices;
|
||||
PxArray<PxI32> triIds;
|
||||
|
||||
void addCell(PxI32 xi, PxI32 yi, PxI32 zi);
|
||||
PxI32 getCellNr(PxI32 xi, PxI32 yi, PxI32 zi) const;
|
||||
bool cellExists(PxI32 xi, PxI32 yi, PxI32 zi) const;
|
||||
|
||||
void removeDuplicateVertices();
|
||||
void pruneInternalSurfaces();
|
||||
void computeNormals();
|
||||
void findTriNeighbors();
|
||||
|
||||
void project(const PxVec3* inputVerts, const PxU32* inputTriIds, PxU32 nbTriangleIndices,
|
||||
float searchDist, float surfaceDist);
|
||||
|
||||
void createVertexMap(const PxVec3* verts, PxU32 nbVertices, const PxVec3 &gridOrigin, PxF32 &gridSpacing,
|
||||
PxArray<PxU32> &vertexMap);
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
struct Cell
|
||||
{
|
||||
void init(PxI32 _xi, PxI32 _yi, PxI32 _zi) {
|
||||
this->xi = _xi; this->yi = _yi; this->zi = _zi;
|
||||
this->next = -1;
|
||||
}
|
||||
PxI32 xi, yi, zi;
|
||||
PxI32 next;
|
||||
};
|
||||
|
||||
PxArray<Cell> cells;
|
||||
PxArray<PxI32> firstCell;
|
||||
PxArray<PxVec3> normals;
|
||||
PxArray<PxI32> triNeighbors;
|
||||
PxArray<PxI32> cellOfVertex;
|
||||
|
||||
PxArray<PxBounds3> bvhBounds;
|
||||
PxArray<PxI32> bvhTris;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
806
engine/third_party/physx/source/physxextensions/src/tet/ExtTetSplitting.cpp
vendored
Normal file
806
engine/third_party/physx/source/physxextensions/src/tet/ExtTetSplitting.cpp
vendored
Normal file
@@ -0,0 +1,806 @@
|
||||
// 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.
|
||||
|
||||
#include "ExtTetSplitting.h"
|
||||
#include "ExtUtilities.h"
|
||||
#include "foundation/PxBasicTemplates.h"
|
||||
|
||||
using namespace physx;
|
||||
using namespace Ext;
|
||||
|
||||
//Last four bits are used
|
||||
//Last bit set stands for A
|
||||
//Second last bit stands for B
|
||||
//Third last bit stands for C
|
||||
//Fourth last bit stands for D
|
||||
|
||||
typedef PxU32 TetCorner;
|
||||
typedef PxU32 TetEdge;
|
||||
|
||||
static const TetCorner None = 0x00000000;
|
||||
static const TetCorner A = 0x00000001;
|
||||
static const TetCorner B = 0x00000002;
|
||||
static const TetCorner C = 0x00000004;
|
||||
static const TetCorner D = 0x00000008;
|
||||
|
||||
static const TetEdge AB = 0x00000003;
|
||||
static const TetEdge AC = 0x00000005;
|
||||
static const TetEdge AD = 0x00000009;
|
||||
static const TetEdge BC = 0x00000006;
|
||||
static const TetEdge BD = 0x0000000A;
|
||||
static const TetEdge CD = 0x0000000C;
|
||||
|
||||
static const TetCorner tetCorners[4] = { A, B, C, D };
|
||||
|
||||
namespace
|
||||
{
|
||||
//Specifies which edges of a tetrahedron should get a point inserted (=split)
|
||||
struct TetSubdivisionInfo
|
||||
{
|
||||
//If an index has value -1 then this means that this edge won't get split
|
||||
PxI32 ab;
|
||||
PxI32 ac;
|
||||
PxI32 ad;
|
||||
|
||||
PxI32 bc;
|
||||
PxI32 bd;
|
||||
|
||||
PxI32 cd;
|
||||
|
||||
Tetrahedron tet;
|
||||
PxI32 id;
|
||||
|
||||
PX_FORCE_INLINE TetSubdivisionInfo() : ab(-1), ac(-1), ad(-1), bc(-1), bd(-1), cd(-1), tet(Tetrahedron(-1, -1, -1, -1)), id(-1) {}
|
||||
|
||||
PX_FORCE_INLINE TetSubdivisionInfo(Tetrahedron tet_, PxI32 aBPointInsertIndex, PxI32 aCPointInsertIndex, PxI32 aDPointInsertIndex,
|
||||
PxI32 bCPointInsertIndex, PxI32 bDPointInsertIndex, PxI32 cDPointInsertIndex, PxI32 id_ = -1) :
|
||||
ab(aBPointInsertIndex), ac(aCPointInsertIndex), ad(aDPointInsertIndex), bc(bCPointInsertIndex), bd(bDPointInsertIndex), cd(cDPointInsertIndex), tet(tet_), id(id_)
|
||||
{ }
|
||||
|
||||
PX_FORCE_INLINE void swapInternal(TetCorner corner1, TetCorner corner2, TetCorner& cornerToProcess)
|
||||
{
|
||||
if (cornerToProcess == corner1)
|
||||
cornerToProcess = corner2;
|
||||
else if (cornerToProcess == corner2)
|
||||
cornerToProcess = corner1;
|
||||
}
|
||||
|
||||
// PT: this function seems unused
|
||||
//Helper method for sorting
|
||||
/*template<typename T>
|
||||
void swap(T& a, T& b)
|
||||
{
|
||||
T tmp = a;
|
||||
a = b;
|
||||
b = tmp;
|
||||
}*/
|
||||
|
||||
//Helper method for sorting
|
||||
bool swap(TetCorner corner1, TetCorner corner2, TetCorner& additionalCornerToProcess1, TetCorner& additionalCornerToProcess2)
|
||||
{
|
||||
swapInternal(corner1, corner2, additionalCornerToProcess1);
|
||||
swapInternal(corner1, corner2, additionalCornerToProcess2);
|
||||
return swap(corner1, corner2);
|
||||
}
|
||||
|
||||
//Helper method for sorting
|
||||
bool swap(TetCorner corner1, TetCorner corner2, TetCorner& additionalCornerToProcess)
|
||||
{
|
||||
swapInternal(corner1, corner2, additionalCornerToProcess);
|
||||
return swap(corner1, corner2);
|
||||
}
|
||||
|
||||
//Helper method for sorting
|
||||
bool swap(TetCorner corner1, TetCorner corner2)
|
||||
{
|
||||
if (corner1 == corner2)
|
||||
return false;
|
||||
|
||||
if (corner1 > corner2)
|
||||
{
|
||||
TetCorner tmp = corner1;
|
||||
corner1 = corner2;
|
||||
corner2 = tmp;
|
||||
}
|
||||
|
||||
if (corner1 == A && corner2 == B)
|
||||
{
|
||||
PxSwap(ad, bd);
|
||||
PxSwap(ac, bc);
|
||||
PxSwap(tet[0], tet[1]);
|
||||
}
|
||||
else if (corner1 == A && corner2 == C)
|
||||
{
|
||||
PxSwap(ad, cd);
|
||||
PxSwap(ab, bc);
|
||||
PxSwap(tet[0], tet[2]);
|
||||
}
|
||||
else if (corner1 == A && corner2 == D)
|
||||
{
|
||||
PxSwap(ac, cd);
|
||||
PxSwap(ab, bd);
|
||||
PxSwap(tet[0], tet[3]);
|
||||
}
|
||||
else if (corner1 == B && corner2 == C)
|
||||
{
|
||||
PxSwap(ac, ab);
|
||||
PxSwap(cd, bd);
|
||||
PxSwap(tet[1], tet[2]);
|
||||
}
|
||||
else if (corner1 == B && corner2 == D)
|
||||
{
|
||||
PxSwap(ab, ad);
|
||||
PxSwap(bc, cd);
|
||||
PxSwap(tet[1], tet[3]);
|
||||
}
|
||||
else if (corner1 == C && corner2 == D)
|
||||
{
|
||||
PxSwap(ac, ad);
|
||||
PxSwap(bc, bd);
|
||||
PxSwap(tet[2], tet[3]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//Allows to sort the information such that edges of neighboring tets have the same orientation
|
||||
PxI32 sort()
|
||||
{
|
||||
PxI32 counter = 0;
|
||||
if (tet[0] > tet[1]) { swap(A, B); ++counter; }
|
||||
if (tet[0] > tet[2]) { swap(A, C); ++counter; }
|
||||
if (tet[0] > tet[3]) { swap(A, D); ++counter; }
|
||||
|
||||
if (tet[1] > tet[2]) { swap(B, C); ++counter; }
|
||||
if (tet[1] > tet[3]) { swap(B, D); ++counter; }
|
||||
if (tet[2] > tet[3]) { swap(C, D); ++counter; }
|
||||
return counter;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
//Returns true if the edge is adjacent to the specified corner
|
||||
static PX_FORCE_INLINE bool edgeContainsCorner(TetEdge edge, TetCorner corner)
|
||||
{
|
||||
return (edge & corner) != 0;
|
||||
}
|
||||
|
||||
//Returns the common point of two edges, will be None if there is no common point
|
||||
static PX_FORCE_INLINE TetCorner getCommonPoint(TetEdge edge1, TetEdge edge2)
|
||||
{
|
||||
return edge1 & edge2;
|
||||
}
|
||||
|
||||
//Extracts the global indices from a tet given a local edge
|
||||
static Edge getTetEdge(const Tetrahedron& tet, TetEdge edge)
|
||||
{
|
||||
switch (edge)
|
||||
{
|
||||
case AB:
|
||||
return Edge(tet[0], tet[1]);
|
||||
case AC:
|
||||
return Edge(tet[0], tet[2]);
|
||||
case AD:
|
||||
return Edge(tet[0], tet[3]);
|
||||
case BC:
|
||||
return Edge(tet[1], tet[2]);
|
||||
case BD:
|
||||
return Edge(tet[1], tet[3]);
|
||||
case CD:
|
||||
return Edge(tet[2], tet[3]);
|
||||
}
|
||||
return Edge(-1, -1);
|
||||
}
|
||||
|
||||
static TetCorner getStart(TetEdge e)
|
||||
{
|
||||
switch (e)
|
||||
{
|
||||
case AB:
|
||||
return A;
|
||||
case AC:
|
||||
return A;
|
||||
case AD:
|
||||
return A;
|
||||
case BC:
|
||||
return B;
|
||||
case BD:
|
||||
return B;
|
||||
case CD:
|
||||
return C;
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
static TetCorner getEnd(TetEdge e)
|
||||
{
|
||||
switch (e)
|
||||
{
|
||||
case AB:
|
||||
return B;
|
||||
case AC:
|
||||
return C;
|
||||
case AD:
|
||||
return D;
|
||||
case BC:
|
||||
return C;
|
||||
case BD:
|
||||
return D;
|
||||
case CD:
|
||||
return D;
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
static PX_FORCE_INLINE TetEdge getOppositeEdge(TetEdge edge)
|
||||
{
|
||||
return 0x0000000F ^ edge;
|
||||
}
|
||||
|
||||
//Finds the index of the first instance of value in list
|
||||
static PxI32 getIndexOfFirstValue(PxI32 list[4], PxI32 value = 0, PxI32 startAt = 0)
|
||||
{
|
||||
for (PxI32 i = startAt; i < 4; ++i)
|
||||
if (list[i] == value)
|
||||
return i;
|
||||
|
||||
PX_ASSERT(false); // we should never reach this line
|
||||
return 0;
|
||||
}
|
||||
|
||||
//Counts how many times every corner is referenced by the specified set of edges - useful for corner classification
|
||||
static void getCornerAccessCounter(TetEdge edges[6], PxI32 edgesLength, PxI32 cornerAccessCounter[4])
|
||||
{
|
||||
for (PxI32 i = 0; i < 4; ++i)
|
||||
cornerAccessCounter[i] = 0;
|
||||
|
||||
for (PxI32 j = 0; j < edgesLength; ++j)
|
||||
{
|
||||
switch (edges[j])
|
||||
{
|
||||
case AB:
|
||||
++cornerAccessCounter[0];
|
||||
++cornerAccessCounter[1];
|
||||
break;
|
||||
case AC:
|
||||
++cornerAccessCounter[0];
|
||||
++cornerAccessCounter[2];
|
||||
break;
|
||||
case AD:
|
||||
++cornerAccessCounter[0];
|
||||
++cornerAccessCounter[3];
|
||||
break;
|
||||
case BC:
|
||||
++cornerAccessCounter[1];
|
||||
++cornerAccessCounter[2];
|
||||
break;
|
||||
case BD:
|
||||
++cornerAccessCounter[1];
|
||||
++cornerAccessCounter[3];
|
||||
break;
|
||||
case CD:
|
||||
++cornerAccessCounter[2];
|
||||
++cornerAccessCounter[3];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PT: using static exposed the fact that this function was not used. Expected?
|
||||
//Returns the tet's edge that does not contain corner1 and neither corner2
|
||||
/*static Edge getRemainingEdge(const Tetrahedron& tet, PxI32 corner1, PxI32 corner2)
|
||||
{
|
||||
PxI32 indexer = 0;
|
||||
Edge result(-1, -1);
|
||||
for (PxU32 i = 0; i < 4; ++i)
|
||||
{
|
||||
if (tet[i] != corner1 && tet[i] != corner2)
|
||||
{
|
||||
if (indexer == 0)
|
||||
result.first = tet[i];
|
||||
else if (indexer == 1)
|
||||
result.second = tet[i];
|
||||
++indexer;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}*/
|
||||
|
||||
static PX_FORCE_INLINE TetCorner getOtherCorner(TetEdge edge, TetCorner corner)
|
||||
{
|
||||
return edge ^ corner;
|
||||
}
|
||||
|
||||
static PX_FORCE_INLINE Tetrahedron flip(bool doFlip, Tetrahedron t)
|
||||
{
|
||||
if (doFlip) PxSwap(t[2], t[3]);
|
||||
return t;
|
||||
}
|
||||
|
||||
//Splits all tets according to the specification in tetSubdivisionInfos. The resulting mesh will be watertight if the tetSubdivisionInfos are specified such
|
||||
//that all tets sharing and edge will get the same point inserted on their corresponding edge
|
||||
static void split(PxArray<Tetrahedron>& tets, const PxArray<PxVec3d>& points, const PxArray<TetSubdivisionInfo>& tetSubdivisionInfos)
|
||||
{
|
||||
PxU32 originalNumTets = tets.size();
|
||||
for (PxU32 i = 0; i < originalNumTets; ++i)
|
||||
{
|
||||
TetSubdivisionInfo info = tetSubdivisionInfos[i];
|
||||
PxI32 counter = info.sort();
|
||||
|
||||
TetEdge splitEdges[6];
|
||||
PxI32 splitEdgesLength = 0;
|
||||
TetEdge nonSplitEdges[6];
|
||||
PxI32 nonSplitEdgesLength = 0;
|
||||
PxI32 insertionIndices[6];
|
||||
PxI32 insertionIndicesLength = 0;
|
||||
if (info.ab >= 0) { splitEdges[splitEdgesLength++] = AB; insertionIndices[insertionIndicesLength++] = info.ab; }
|
||||
else nonSplitEdges[nonSplitEdgesLength++] = AB;
|
||||
if (info.ac >= 0) { splitEdges[splitEdgesLength++] = AC; insertionIndices[insertionIndicesLength++] = info.ac; }
|
||||
else nonSplitEdges[nonSplitEdgesLength++] = AC;
|
||||
if (info.ad >= 0) { splitEdges[splitEdgesLength++] = AD; insertionIndices[insertionIndicesLength++] = info.ad; }
|
||||
else nonSplitEdges[nonSplitEdgesLength++] = AD;
|
||||
|
||||
if (info.bc >= 0) { splitEdges[splitEdgesLength++] = BC; insertionIndices[insertionIndicesLength++] = info.bc; }
|
||||
else nonSplitEdges[nonSplitEdgesLength++] = BC;
|
||||
if (info.bd >= 0) { splitEdges[splitEdgesLength++] = BD; insertionIndices[insertionIndicesLength++] = info.bd; }
|
||||
else nonSplitEdges[nonSplitEdgesLength++] = BD;
|
||||
|
||||
if (info.cd >= 0) { splitEdges[splitEdgesLength++] = CD; insertionIndices[insertionIndicesLength++] = info.cd; }
|
||||
else nonSplitEdges[nonSplitEdgesLength++] = CD;
|
||||
|
||||
//Depending on how many tet edges get a point inserted, a different topology results.
|
||||
//The created topology will make sure all neighboring tet faces will be tessellated identically to keep the mesh watertight
|
||||
switch (splitEdgesLength)
|
||||
{
|
||||
case 0:
|
||||
//Nothing to do here
|
||||
break;
|
||||
case 1:
|
||||
{
|
||||
PxI32 pointIndex = insertionIndices[0];
|
||||
Edge splitEdge = getTetEdge(info.tet, splitEdges[0]);
|
||||
|
||||
Edge oppositeEdge = getTetEdge(info.tet, getOppositeEdge(splitEdges[0]));
|
||||
|
||||
tets[i] = Tetrahedron(oppositeEdge.first, oppositeEdge.second, splitEdge.first, pointIndex);
|
||||
tets.pushBack(Tetrahedron(oppositeEdge.first, oppositeEdge.second, pointIndex, splitEdge.second));
|
||||
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
TetCorner corner = getCommonPoint(splitEdges[0], splitEdges[1]);
|
||||
if (corner != None)
|
||||
{
|
||||
//edges have a common point
|
||||
//Rearrange such that common corner is a and first edge is from a to b while second edge is from a to c
|
||||
TetCorner p1 = getOtherCorner(splitEdges[0], corner);
|
||||
TetCorner p2 = getOtherCorner(splitEdges[1], corner);
|
||||
if (info.swap(corner, A, p1, p2)) ++counter;
|
||||
if (info.swap(p1, B, p2)) ++counter;
|
||||
if (info.swap(p2, C)) ++counter;
|
||||
|
||||
if (info.tet[1] > info.tet[2])
|
||||
{
|
||||
if (info.swap(B, C)) ++counter;
|
||||
}
|
||||
|
||||
const bool f = counter % 2 == 1;
|
||||
|
||||
tets[i] = flip(f, Tetrahedron(info.tet[0], info.tet[3], info.ab, info.ac));
|
||||
tets.pushBack(flip(f, Tetrahedron(info.tet[3], info.tet[1], info.ab, info.ac)));
|
||||
tets.pushBack(flip(f, Tetrahedron(info.tet[3], info.tet[2], info.tet[1], info.ac)));
|
||||
}
|
||||
else
|
||||
{
|
||||
//Edges don't have a common point (opposite edges)
|
||||
TetEdge edge1 = splitEdges[0];
|
||||
//TetEdge edge2 = splitEdges[1];
|
||||
|
||||
//Permute the tetrahedron such that edge1 becomes the edge AB
|
||||
if (info.swap(getStart(edge1), A)) ++counter;
|
||||
if (info.swap(getEnd(edge1), B)) ++counter;
|
||||
|
||||
if (info.tet[0] > info.tet[1])
|
||||
{
|
||||
if (info.swap(A, B)) ++counter;
|
||||
}
|
||||
if (info.tet[2] > info.tet[3])
|
||||
{
|
||||
if (info.swap(C, D)) ++counter;
|
||||
}
|
||||
|
||||
const bool f = counter % 2 == 1;
|
||||
|
||||
tets[i] = flip(f, Tetrahedron(info.tet[0], info.ab, info.tet[2], info.cd));
|
||||
tets.pushBack(flip(f, Tetrahedron(info.tet[1], info.ab, info.cd, info.tet[2])));
|
||||
|
||||
tets.pushBack(flip(f, Tetrahedron(info.tet[0], info.ab, info.cd, info.tet[3])));
|
||||
tets.pushBack(flip(f, Tetrahedron(info.tet[1], info.ab, info.tet[3], info.cd)));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
//There are three sub cases called a, b and c
|
||||
TetCorner commonPoint01 = getCommonPoint(splitEdges[0], splitEdges[1]);
|
||||
TetCorner commonPoint02 = getCommonPoint(splitEdges[0], splitEdges[2]);
|
||||
TetCorner commonPoint12 = getCommonPoint(splitEdges[1], splitEdges[2]);
|
||||
if (commonPoint01 == None || commonPoint02 == None || commonPoint12 == None)
|
||||
{
|
||||
//The three edges form a non closed strip
|
||||
//The strip's end points are connected by a tet edge - map this edge such that it becomes edge AB
|
||||
//Then sort AB
|
||||
|
||||
PxI32 cnt[4];
|
||||
getCornerAccessCounter(splitEdges, splitEdgesLength, cnt);
|
||||
|
||||
PxI32 index = getIndexOfFirstValue(cnt, 1);
|
||||
TetCorner refStart = tetCorners[index];
|
||||
TetCorner refEnd = tetCorners[getIndexOfFirstValue(cnt, 1, index + 1)];
|
||||
|
||||
|
||||
TetCorner cornerToMapOntoC = None;
|
||||
if (edgeContainsCorner(splitEdges[0], refEnd))
|
||||
{
|
||||
cornerToMapOntoC = getOtherCorner(splitEdges[0], refEnd);
|
||||
}
|
||||
else if (edgeContainsCorner(splitEdges[1], refEnd))
|
||||
{
|
||||
cornerToMapOntoC = getOtherCorner(splitEdges[1], refEnd);
|
||||
}
|
||||
else if (edgeContainsCorner(splitEdges[2], refEnd))
|
||||
{
|
||||
cornerToMapOntoC = getOtherCorner(splitEdges[2], refEnd);
|
||||
}
|
||||
|
||||
if (info.swap(refStart, A, refEnd, cornerToMapOntoC)) ++counter;
|
||||
if (info.swap(refEnd, B, cornerToMapOntoC)) ++counter;
|
||||
if (info.swap(cornerToMapOntoC, C)) ++counter;
|
||||
|
||||
if (info.tet[0] > info.tet[1])
|
||||
{
|
||||
if (info.swap(A, B)) ++counter;
|
||||
if (info.swap(C, D)) ++counter;
|
||||
}
|
||||
|
||||
const bool f = counter % 2 == 1;
|
||||
|
||||
tets[i] = flip(f, Tetrahedron(info.tet[0], info.tet[1], info.bc, info.ad));
|
||||
|
||||
if (info.tet[0] > info.tet[2])
|
||||
{
|
||||
tets.pushBack(flip(f, Tetrahedron(info.tet[2], info.cd, info.ad, info.bc)));
|
||||
tets.pushBack(flip(f, Tetrahedron(info.tet[0], info.tet[2], info.ad, info.bc)));
|
||||
}
|
||||
else
|
||||
{
|
||||
tets.pushBack(flip(f, Tetrahedron(info.tet[0], info.tet[2], info.cd, info.bc)));
|
||||
tets.pushBack(flip(f, Tetrahedron(info.tet[0], info.ad, info.bc, info.cd)));
|
||||
}
|
||||
|
||||
if (info.tet[1] > info.tet[3])
|
||||
{
|
||||
tets.pushBack(flip(f, Tetrahedron(info.tet[3], info.bc, info.ad, info.cd)));
|
||||
tets.pushBack(flip(f, Tetrahedron(info.tet[3], info.tet[1], info.ad, info.bc)));
|
||||
}
|
||||
else
|
||||
{
|
||||
tets.pushBack(flip(f, Tetrahedron(info.tet[1], info.tet[3], info.cd, info.ad)));
|
||||
tets.pushBack(flip(f, Tetrahedron(info.tet[1], info.bc, info.ad, info.cd)));
|
||||
}
|
||||
}
|
||||
else if (edgeContainsCorner(splitEdges[2], commonPoint01))
|
||||
{
|
||||
//All three edges share one common point
|
||||
//Permute tetrahedron such that the common tip point is a
|
||||
if (info.swap(commonPoint01, A)) ++counter;
|
||||
//Sort the remaining values
|
||||
if (info.tet[1] > info.tet[2])
|
||||
{
|
||||
if (info.swap(B, C)) ++counter;
|
||||
}
|
||||
if (info.tet[2] > info.tet[3])
|
||||
{
|
||||
if (info.swap(C, D)) ++counter;
|
||||
}
|
||||
if (info.tet[1] > info.tet[2])
|
||||
{
|
||||
if (info.swap(B, C)) ++counter;
|
||||
}
|
||||
|
||||
const bool f = counter % 2 == 1;
|
||||
|
||||
tets[i] = flip(f, Tetrahedron(info.tet[0], info.ab, info.ac, info.ad));
|
||||
tets.pushBack(flip(f, Tetrahedron(info.tet[1], info.ab, info.ad, info.ac)));
|
||||
tets.pushBack(flip(f, Tetrahedron(info.tet[2], info.tet[1], info.ad, info.ac)));
|
||||
tets.pushBack(flip(f, Tetrahedron(info.tet[1], info.tet[2], info.ad, info.tet[3])));
|
||||
}
|
||||
else
|
||||
{
|
||||
//Edges form a triangle
|
||||
//Rearrange such that point opposite of triangle loop is point d
|
||||
//Triangle loop is a, b and c, make sure they're sorted
|
||||
|
||||
if (!(commonPoint01 == A || commonPoint02 == A || commonPoint12 == A))
|
||||
{
|
||||
if (info.swap(A, D)) ++counter;
|
||||
}
|
||||
else if (!(commonPoint01 == B || commonPoint02 == B || commonPoint12 == B))
|
||||
{
|
||||
if (info.swap(B, D)) ++counter;
|
||||
}
|
||||
else if (!(commonPoint01 == C || commonPoint02 == C || commonPoint12 == C))
|
||||
{
|
||||
if (info.swap(C, D)) ++counter;
|
||||
}
|
||||
else if (!(commonPoint01 == D || commonPoint02 == D || commonPoint12 == D))
|
||||
{
|
||||
if (info.swap(D, D)) ++counter;
|
||||
}
|
||||
|
||||
//Sort a,b and c
|
||||
if (info.tet[0] > info.tet[1])
|
||||
if (info.swap(A, B)) ++counter;
|
||||
if (info.tet[1] > info.tet[2])
|
||||
if (info.swap(B, C)) ++counter;
|
||||
if (info.tet[0] > info.tet[1])
|
||||
if (info.swap(A, B)) ++counter;
|
||||
|
||||
const bool f = counter % 2 == 1;
|
||||
|
||||
tets[i] = flip(f, Tetrahedron(info.tet[3], info.ab, info.ac, info.bc));
|
||||
tets.pushBack(flip(f, Tetrahedron(info.tet[3], info.ab, info.ac, info.tet[0])));
|
||||
tets.pushBack(flip(f, Tetrahedron(info.tet[3], info.ab, info.bc, info.tet[1])));
|
||||
tets.pushBack(flip(f, Tetrahedron(info.tet[3], info.ac, info.bc, info.tet[2])));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 4:
|
||||
{
|
||||
TetCorner commonPoint = getCommonPoint(nonSplitEdges[0], nonSplitEdges[1]);
|
||||
|
||||
if (commonPoint != None)
|
||||
{
|
||||
//Three edges form a triangle and the two edges that are not split share a common point
|
||||
TetCorner p1 = getOtherCorner(nonSplitEdges[0], commonPoint);
|
||||
TetCorner p2 = getOtherCorner(nonSplitEdges[1], commonPoint);
|
||||
|
||||
if (info.swap(commonPoint, A, p1, p2)) ++counter;
|
||||
|
||||
if (info.swap(p1, B, p2)) ++counter;
|
||||
if (info.swap(p2, C)) ++counter;
|
||||
|
||||
if (info.tet[1] > info.tet[2])
|
||||
if (info.swap(B, C)) ++counter;
|
||||
|
||||
const bool f = counter % 2 == 0;
|
||||
|
||||
//Tip
|
||||
tets[i] = flip(f, Tetrahedron(info.tet[3], info.ad, info.bd, info.cd));
|
||||
|
||||
//Center
|
||||
tets.pushBack(flip(f, Tetrahedron(info.bd, info.ad, info.bc, info.cd)));
|
||||
|
||||
if (info.tet[0] < info.tet[1] && info.tet[0] < info.tet[2])
|
||||
{
|
||||
tets.pushBack(flip(f, Tetrahedron(info.tet[0], info.tet[1], info.bd, info.bc)));
|
||||
tets.pushBack(flip(f, Tetrahedron(info.tet[0], info.tet[2], info.bc, info.cd)));
|
||||
tets.pushBack(flip(f, Tetrahedron(info.tet[0], info.bc, info.bd, info.ad)));
|
||||
tets.pushBack(flip(f, Tetrahedron(info.tet[0], info.bc, info.ad, info.cd)));
|
||||
}
|
||||
else if (info.tet[0] > info.tet[1] && info.tet[0] < info.tet[2])
|
||||
{
|
||||
tets.pushBack(flip(f, Tetrahedron(info.tet[0], info.tet[2], info.bc, info.cd)));
|
||||
tets.pushBack(flip(f, Tetrahedron(info.tet[0], info.bc, info.ad, info.cd)));
|
||||
tets.pushBack(flip(f, Tetrahedron(info.tet[0], info.tet[1], info.ad, info.bc)));
|
||||
tets.pushBack(flip(f, Tetrahedron(info.tet[1], info.bc, info.bd, info.ad)));
|
||||
}
|
||||
else
|
||||
{
|
||||
tets.pushBack(flip(f, Tetrahedron(info.tet[0], info.tet[1], info.ad, info.bc)));
|
||||
tets.pushBack(flip(f, Tetrahedron(info.tet[1], info.bc, info.bd, info.ad)));
|
||||
tets.pushBack(flip(f, Tetrahedron(info.tet[2], info.tet[0], info.ad, info.bc)));
|
||||
tets.pushBack(flip(f, Tetrahedron(info.tet[2], info.bc, info.ad, info.cd)));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//All four edges form a loop
|
||||
TetEdge edge1 = nonSplitEdges[0];
|
||||
|
||||
//Permute the tetrahedron such that edge1 becomes the edge AB
|
||||
TetCorner end = getEnd(edge1);
|
||||
if (info.swap(getStart(edge1), A, end)) ++counter;
|
||||
if (info.swap(end, B)) ++counter;
|
||||
|
||||
//Sort
|
||||
if (info.tet[0] > info.tet[1])
|
||||
if (info.swap(A, B)) ++counter;
|
||||
if (info.tet[2] > info.tet[3])
|
||||
if (info.swap(C, D)) ++counter;
|
||||
|
||||
const bool f = counter % 2 == 1;
|
||||
|
||||
tets[i] = flip(f, Tetrahedron(info.tet[0], info.tet[1], info.bc, info.bd));
|
||||
tets.pushBack(flip(f, Tetrahedron(info.tet[2], info.tet[3], info.ad, info.bd)));
|
||||
|
||||
PxF64 dist1 = (points[info.ad] - points[info.bc]).magnitudeSquared();
|
||||
PxF64 dist2 = (points[info.ac] - points[info.bd]).magnitudeSquared();
|
||||
|
||||
if (dist1 < dist2)
|
||||
{
|
||||
//Diagonal from AD to BC
|
||||
tets.pushBack(flip(f, Tetrahedron(info.tet[0], info.ad, info.bc, info.ac)));
|
||||
tets.pushBack(flip(f, Tetrahedron(info.tet[0], info.ad, info.bd, info.bc)));
|
||||
tets.pushBack(flip(f, Tetrahedron(info.tet[2], info.ad, info.ac, info.bc)));
|
||||
tets.pushBack(flip(f, Tetrahedron(info.tet[2], info.ad, info.bc, info.bd)));
|
||||
}
|
||||
else
|
||||
{
|
||||
//Diagonal from AC to BD
|
||||
tets.pushBack(flip(f, Tetrahedron(info.tet[0], info.ad, info.bd, info.ac)));
|
||||
tets.pushBack(flip(f, Tetrahedron(info.tet[0], info.ac, info.bd, info.bc)));
|
||||
tets.pushBack(flip(f, Tetrahedron(info.tet[2], info.bc, info.bd, info.ac)));
|
||||
tets.pushBack(flip(f, Tetrahedron(info.tet[2], info.bd, info.ad, info.ac)));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 5:
|
||||
{
|
||||
//There is only one edge that does not get split
|
||||
|
||||
//First create 2 small tetrahedra in every corner that is not an end point of the unsplit edge
|
||||
TetEdge nonSplitEdge;
|
||||
if (info.ab < 0)
|
||||
nonSplitEdge = AB;
|
||||
else if (info.ac < 0)
|
||||
nonSplitEdge = AC;
|
||||
else if (info.ad < 0)
|
||||
nonSplitEdge = AD;
|
||||
else if (info.bc < 0)
|
||||
nonSplitEdge = BC;
|
||||
else if (info.bd < 0)
|
||||
nonSplitEdge = BD;
|
||||
else //if (info.CDPointInsertIndex < 0)
|
||||
nonSplitEdge = CD;
|
||||
|
||||
TetCorner end = getEnd(nonSplitEdge);
|
||||
if (info.swap(getStart(nonSplitEdge), A, end)) ++counter;
|
||||
if (info.swap(end, B)) ++counter;
|
||||
|
||||
if (info.tet[0] > info.tet[1])
|
||||
if (info.swap(A, B)) ++counter;
|
||||
if (info.tet[2] > info.tet[3])
|
||||
if (info.swap(C, D)) ++counter;
|
||||
|
||||
const bool f = counter % 2 == 1;
|
||||
|
||||
//Two corner tets at corner C and corner D
|
||||
tets[i] = flip(f, Tetrahedron(info.tet[2], info.ac, info.bc, info.cd));
|
||||
tets.pushBack(flip(f, Tetrahedron(info.tet[3], info.ad, info.cd, info.bd)));
|
||||
|
||||
tets.pushBack(flip(f, Tetrahedron(info.tet[0], info.tet[1], info.bc, info.bd)));
|
||||
|
||||
|
||||
//There are two possible diagonals -> take the shorter
|
||||
PxF64 dist1 = (points[info.ac] - points[info.bd]).magnitudeSquared();
|
||||
PxF64 dist2 = (points[info.ad] - points[info.bc]).magnitudeSquared();
|
||||
if (dist1 < dist2)
|
||||
{
|
||||
//Diagonal from AC to BD
|
||||
tets.pushBack(flip(f, Tetrahedron(info.tet[0], info.ad, info.bd, info.ac)));
|
||||
tets.pushBack(flip(f, Tetrahedron(info.tet[0], info.ac, info.bd, info.bc)));
|
||||
//Tip pyramid
|
||||
tets.pushBack(flip(f, Tetrahedron(info.cd, info.bc, info.bd, info.ac)));
|
||||
tets.pushBack(flip(f, Tetrahedron(info.cd, info.bd, info.ad, info.ac)));
|
||||
}
|
||||
else
|
||||
{
|
||||
//Diagonal from AD to BC
|
||||
tets.pushBack(flip(f, Tetrahedron(info.tet[0], info.ac, info.ad, info.bc)));
|
||||
tets.pushBack(flip(f, Tetrahedron(info.tet[0], info.bd, info.bc, info.ad)));
|
||||
//Tip pyramid
|
||||
tets.pushBack(flip(f, Tetrahedron(info.cd, info.bc, info.ad, info.ac)));
|
||||
tets.pushBack(flip(f, Tetrahedron(info.cd, info.bd, info.ad, info.bc)));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 6:
|
||||
{
|
||||
//First create a small tetrahedron in every corner
|
||||
const bool f = counter % 2 == 1;
|
||||
if (f)
|
||||
info.swap(A, B);
|
||||
|
||||
tets[i] = Tetrahedron(info.tet[0], info.ab, info.ac, info.ad);
|
||||
tets.pushBack(Tetrahedron(info.tet[1], info.ab, info.bd, info.bc));
|
||||
tets.pushBack(Tetrahedron(info.tet[2], info.ac, info.bc, info.cd));
|
||||
tets.pushBack(Tetrahedron(info.tet[3], info.ad, info.cd, info.bd));
|
||||
|
||||
//Now fill the remaining octahedron in the middle
|
||||
//An octahedron can be constructed using 4 tetrahedra
|
||||
//There are three diagonal candidates -> pick the shortest diagonal
|
||||
PxF64 dist1 = (points[info.ab] - points[info.cd]).magnitudeSquared();
|
||||
PxF64 dist2 = (points[info.ac] - points[info.bd]).magnitudeSquared();
|
||||
PxF64 dist3 = (points[info.ad] - points[info.bc]).magnitudeSquared();
|
||||
|
||||
if (dist1 <= dist2 && dist1 <= dist3)
|
||||
{
|
||||
tets.pushBack(Tetrahedron(info.ab, info.cd, info.ad, info.bd));
|
||||
tets.pushBack(Tetrahedron(info.ab, info.cd, info.bd, info.bc));
|
||||
tets.pushBack(Tetrahedron(info.ab, info.cd, info.bc, info.ac));
|
||||
tets.pushBack(Tetrahedron(info.ab, info.cd, info.ac, info.ad));
|
||||
}
|
||||
else if (dist2 <= dist1 && dist2 <= dist3)
|
||||
{
|
||||
tets.pushBack(Tetrahedron(info.ac, info.bd, info.cd, info.ad));
|
||||
tets.pushBack(Tetrahedron(info.ac, info.bd, info.ad, info.ab));
|
||||
tets.pushBack(Tetrahedron(info.ac, info.bd, info.ab, info.bc));
|
||||
tets.pushBack(Tetrahedron(info.ac, info.bd, info.bc, info.cd));
|
||||
}
|
||||
else
|
||||
{
|
||||
tets.pushBack(Tetrahedron(info.ad, info.bc, info.bd, info.ab));
|
||||
tets.pushBack(Tetrahedron(info.ad, info.bc, info.cd, info.bd));
|
||||
tets.pushBack(Tetrahedron(info.ad, info.bc, info.ac, info.cd));
|
||||
tets.pushBack(Tetrahedron(info.ad, info.bc, info.ab, info.ac));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void physx::Ext::split(PxArray<Tetrahedron>& tets, const PxArray<PxVec3d>& points, const PxHashMap<PxU64, PxI32>& edgesToSplit)
|
||||
{
|
||||
PxArray<TetSubdivisionInfo> subdivisionInfos;
|
||||
subdivisionInfos.resize(tets.size());
|
||||
for (PxU32 i = 0; i < tets.size(); ++i)
|
||||
{
|
||||
const Tetrahedron& tet = tets[i];
|
||||
TetSubdivisionInfo info(tet, -1, -1, -1, -1, -1, -1, i);
|
||||
|
||||
if (const PxPair<const PxU64, PxI32>* ptr = edgesToSplit.find(key(tet[0], tet[1])))
|
||||
info.ab = ptr->second;
|
||||
if (const PxPair<const PxU64, PxI32>* ptr = edgesToSplit.find(key(tet[0], tet[2])))
|
||||
info.ac = ptr->second;
|
||||
if (const PxPair<const PxU64, PxI32>* ptr = edgesToSplit.find(key(tet[0], tet[3])))
|
||||
info.ad = ptr->second;
|
||||
|
||||
if (const PxPair<const PxU64, PxI32>* ptr = edgesToSplit.find(key(tet[1], tet[2])))
|
||||
info.bc = ptr->second;
|
||||
if (const PxPair<const PxU64, PxI32>* ptr = edgesToSplit.find(key(tet[1], tet[3])))
|
||||
info.bd = ptr->second;
|
||||
|
||||
if (const PxPair<const PxU64, PxI32>* ptr = edgesToSplit.find(key(tet[2], tet[3])))
|
||||
info.cd = ptr->second;
|
||||
|
||||
subdivisionInfos[i] = info;
|
||||
}
|
||||
|
||||
::split(tets, points, subdivisionInfos);
|
||||
}
|
||||
49
engine/third_party/physx/source/physxextensions/src/tet/ExtTetSplitting.h
vendored
Normal file
49
engine/third_party/physx/source/physxextensions/src/tet/ExtTetSplitting.h
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
// 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.
|
||||
|
||||
#ifndef EXT_TET_SPLITTING_H
|
||||
#define EXT_TET_SPLITTING_H
|
||||
|
||||
#include "ExtVec3.h"
|
||||
#include "foundation/PxArray.h"
|
||||
#include "foundation/PxHashMap.h"
|
||||
#include "GuTetrahedron.h"
|
||||
|
||||
namespace physx
|
||||
{
|
||||
namespace Ext
|
||||
{
|
||||
using Edge = PxPair<PxI32, PxI32>;
|
||||
using Tetrahedron = Gu::TetrahedronT<PxI32>;
|
||||
|
||||
//Splits all edges specified in edgesToSplit. The tets are modified in place. The poitns referenced by index in the key-value pari in
|
||||
//edgesToSplit must already pe present in the points array. This functions guarantees that the tetmesh will remain watertight.
|
||||
PX_C_EXPORT void PX_CALL_CONV split(PxArray<Tetrahedron>& tets, const PxArray<PxVec3d>& points, const PxHashMap<PxU64, PxI32>& edgesToSplit);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
25
engine/third_party/physx/source/physxextensions/src/tet/ExtTetTetraMesh.h
vendored
Normal file
25
engine/third_party/physx/source/physxextensions/src/tet/ExtTetTetraMesh.h
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
// 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.
|
||||
88
engine/third_party/physx/source/physxextensions/src/tet/ExtTetUnionFind.cpp
vendored
Normal file
88
engine/third_party/physx/source/physxextensions/src/tet/ExtTetUnionFind.cpp
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
// 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.
|
||||
|
||||
#include "ExtTetUnionFind.h"
|
||||
|
||||
using namespace physx;
|
||||
using namespace Ext;
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
void UnionFind::init(PxI32 numSets)
|
||||
{
|
||||
mEntries.resize(numSets);
|
||||
for (PxI32 i = 0; i < numSets; i++) {
|
||||
Entry &e = mEntries[i];
|
||||
e.parent = i;
|
||||
e.rank = 0;
|
||||
e.setNr = i;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
PxI32 UnionFind::find(PxI32 x)
|
||||
{
|
||||
if (mEntries[x].parent != x)
|
||||
mEntries[x].parent = find(mEntries[x].parent);
|
||||
return mEntries[x].parent;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
void UnionFind::makeSet(PxI32 x, PxI32 y)
|
||||
{
|
||||
PxI32 xroot = find(x);
|
||||
PxI32 yroot = find(y);
|
||||
if (xroot == yroot)
|
||||
return;
|
||||
if (mEntries[xroot].rank < mEntries[yroot].rank)
|
||||
mEntries[xroot].parent = yroot;
|
||||
else if (mEntries[xroot].rank > mEntries[yroot].rank)
|
||||
mEntries[yroot].parent = xroot;
|
||||
else {
|
||||
mEntries[yroot].parent = xroot;
|
||||
mEntries[xroot].rank++;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
PxI32 UnionFind::computeSetNrs()
|
||||
{
|
||||
PxArray<PxI32> oldToNew(mEntries.size(), -1);
|
||||
PxI32 numSets = 0;
|
||||
|
||||
for (PxU32 i = 0; i < mEntries.size(); i++) {
|
||||
PxI32 nr = find(i);
|
||||
if (oldToNew[nr] < 0)
|
||||
oldToNew[nr] = numSets++;
|
||||
mEntries[i].setNr = oldToNew[nr];
|
||||
}
|
||||
return numSets;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
PxI32 UnionFind::getSetNr(PxI32 x)
|
||||
{
|
||||
return mEntries[x].setNr;
|
||||
}
|
||||
59
engine/third_party/physx/source/physxextensions/src/tet/ExtTetUnionFind.h
vendored
Normal file
59
engine/third_party/physx/source/physxextensions/src/tet/ExtTetUnionFind.h
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
// 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.
|
||||
|
||||
#ifndef UNION_FIND_H
|
||||
#define UNION_FIND_H
|
||||
|
||||
#include "foundation/PxArray.h"
|
||||
|
||||
namespace physx
|
||||
{
|
||||
namespace Ext
|
||||
{
|
||||
class UnionFind {
|
||||
public:
|
||||
UnionFind() {}
|
||||
UnionFind(PxI32 numSets) { init(numSets); }
|
||||
|
||||
void init(PxI32 numSets);
|
||||
PxI32 find(PxI32 x);
|
||||
void makeSet(PxI32 x, PxI32 y);
|
||||
|
||||
PxI32 computeSetNrs();
|
||||
PxI32 getSetNr(PxI32 x);
|
||||
|
||||
private:
|
||||
struct Entry {
|
||||
PxI32 parent, rank;
|
||||
PxI32 setNr;
|
||||
};
|
||||
|
||||
PxArray<Entry> mEntries;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
58
engine/third_party/physx/source/physxextensions/src/tet/ExtUtilities.cpp
vendored
Normal file
58
engine/third_party/physx/source/physxextensions/src/tet/ExtUtilities.cpp
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
// 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.
|
||||
|
||||
#include "foundation/PxAssert.h"
|
||||
#include "ExtUtilities.h"
|
||||
#include "GuAABBTreeBuildStats.h"
|
||||
#include "foundation/PxFPU.h"
|
||||
|
||||
using namespace physx;
|
||||
using namespace Ext;
|
||||
using namespace Gu;
|
||||
|
||||
static PxVec3 toFloat(const PxVec3d& p)
|
||||
{
|
||||
return PxVec3(PxReal(p.x), PxReal(p.y), PxReal(p.z));
|
||||
}
|
||||
|
||||
void physx::Ext::buildTree(const PxU32* triangles, const PxU32 numTriangles, const PxVec3d* points, PxArray<Gu::BVHNode>& tree, PxF32 enlargement)
|
||||
{
|
||||
//Computes a bounding box for every triangle in triangles
|
||||
AABBTreeBounds boxes;
|
||||
boxes.init(numTriangles);
|
||||
for (PxU32 i = 0; i < numTriangles; ++i)
|
||||
{
|
||||
const PxU32* tri = &triangles[3 * i];
|
||||
PxBounds3 box = PxBounds3::empty();
|
||||
box.include(toFloat(points[tri[0]]));
|
||||
box.include(toFloat(points[tri[1]]));
|
||||
box.include(toFloat(points[tri[2]]));
|
||||
box.fattenFast(enlargement);
|
||||
boxes.getBounds()[i] = box;
|
||||
}
|
||||
|
||||
Gu::buildAABBTree(numTriangles, boxes, tree);
|
||||
}
|
||||
109
engine/third_party/physx/source/physxextensions/src/tet/ExtUtilities.h
vendored
Normal file
109
engine/third_party/physx/source/physxextensions/src/tet/ExtUtilities.h
vendored
Normal file
@@ -0,0 +1,109 @@
|
||||
// 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.
|
||||
|
||||
#ifndef EXT_TET_CPU_BVH_H
|
||||
#define EXT_TET_CPU_BVH_H
|
||||
|
||||
#include "foundation/PxArray.h"
|
||||
|
||||
#include "foundation/PxBounds3.h"
|
||||
|
||||
#include "GuBVH.h"
|
||||
#include "GuAABBTree.h"
|
||||
#include "GuAABBTreeNode.h"
|
||||
#include "GuAABBTreeBounds.h"
|
||||
#include "GuAABBTreeQuery.h"
|
||||
#include "GuTriangle.h"
|
||||
#include "ExtVec3.h"
|
||||
|
||||
namespace physx
|
||||
{
|
||||
namespace Ext
|
||||
{
|
||||
using Triangle = Gu::IndexedTriangleT<PxI32>;
|
||||
|
||||
//Creates an unique 64bit bit key out of two 32bit values, the key is order independent, useful as hash key for edges
|
||||
//Use this functions to compute the edge keys used in the edgesToSplit parameter of the split function below.
|
||||
PX_FORCE_INLINE PxU64 key(PxI32 a, PxI32 b)
|
||||
{
|
||||
if (a < b)
|
||||
return ((PxU64(a)) << 32) | (PxU64(b));
|
||||
else
|
||||
return ((PxU64(b)) << 32) | (PxU64(a));
|
||||
}
|
||||
|
||||
void buildTree(const PxU32* triangles, const PxU32 numTriangles, const PxVec3d* points, PxArray<Gu::BVHNode>& tree, PxF32 enlargement = 1e-4f);
|
||||
|
||||
//Builds a BVH from a set of triangles
|
||||
PX_FORCE_INLINE void buildTree(const PxArray<Triangle>& triangles, const PxArray<PxVec3d>& points, PxArray<Gu::BVHNode>& tree, PxF32 enlargement = 1e-4f)
|
||||
{
|
||||
buildTree(reinterpret_cast<const PxU32*>(triangles.begin()), triangles.size(), points.begin(), tree, enlargement);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void traverseBVH(const PxArray<Gu::BVHNode>& nodes, T& traversalController, PxI32 rootNodeIndex = 0)
|
||||
{
|
||||
traverseBVH(nodes.begin(), traversalController, rootNodeIndex);
|
||||
}
|
||||
|
||||
class IntersectionCollectingTraversalController
|
||||
{
|
||||
PxBounds3 box;
|
||||
PxArray<PxI32>& candidateTriangleIndices;
|
||||
|
||||
public:
|
||||
IntersectionCollectingTraversalController(PxArray<PxI32>& candidateTriangleIndices_) :
|
||||
box(PxBounds3::empty()), candidateTriangleIndices(candidateTriangleIndices_)
|
||||
{ }
|
||||
|
||||
IntersectionCollectingTraversalController(const PxBounds3& box_, PxArray<PxI32>& candidateTriangleIndices_) :
|
||||
box(box_), candidateTriangleIndices(candidateTriangleIndices_)
|
||||
{ }
|
||||
|
||||
void reset(const PxBounds3& box_)
|
||||
{
|
||||
box = box_;
|
||||
candidateTriangleIndices.clear();
|
||||
}
|
||||
|
||||
Gu::TraversalControl::Enum analyze(const Gu::BVHNode& node, PxI32)
|
||||
{
|
||||
if (node.isLeaf())
|
||||
{
|
||||
candidateTriangleIndices.pushBack(node.getPrimitiveIndex());
|
||||
return Gu::TraversalControl::eDontGoDeeper;
|
||||
}
|
||||
|
||||
if (node.mBV.intersects(box))
|
||||
return Gu::TraversalControl::eGoDeeper;
|
||||
return Gu::TraversalControl::eDontGoDeeper;
|
||||
}
|
||||
private:
|
||||
PX_NOCOPY(IntersectionCollectingTraversalController)
|
||||
};
|
||||
}
|
||||
}
|
||||
#endif
|
||||
70
engine/third_party/physx/source/physxextensions/src/tet/ExtVec3.h
vendored
Normal file
70
engine/third_party/physx/source/physxextensions/src/tet/ExtVec3.h
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
// 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.
|
||||
|
||||
#ifndef EXT_VEC3_H
|
||||
#define EXT_VEC3_H
|
||||
|
||||
#include "foundation/PxMath.h"
|
||||
#include "foundation/PxVec3.h"
|
||||
|
||||
namespace physx
|
||||
{
|
||||
namespace Ext
|
||||
{
|
||||
// ---------------------------------------------------------------------------------
|
||||
struct Bounds3 {
|
||||
Bounds3() {}
|
||||
Bounds3(const PxVec3d &min, const PxVec3d &max) : minimum(min), maximum(max) {}
|
||||
|
||||
void setEmpty() {
|
||||
minimum = PxVec3d(PX_MAX_F64, PX_MAX_F64, PX_MAX_F64);
|
||||
maximum = PxVec3d(-PX_MAX_F64, -PX_MAX_F64, -PX_MAX_F64);
|
||||
}
|
||||
|
||||
PxVec3d getDimensions() const {
|
||||
return maximum - minimum;
|
||||
}
|
||||
|
||||
void include(const PxVec3d &p) {
|
||||
minimum = minimum.minimum(p);
|
||||
maximum = maximum.maximum(p);
|
||||
}
|
||||
|
||||
void include(const Bounds3 &b) {
|
||||
minimum = minimum.minimum(b.minimum);
|
||||
maximum = maximum.maximum(b.maximum);
|
||||
}
|
||||
void expand(double d) {
|
||||
minimum -= PxVec3d(d, d, d);
|
||||
maximum += PxVec3d(d, d, d);
|
||||
}
|
||||
PxVec3d minimum, maximum;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
753
engine/third_party/physx/source/physxextensions/src/tet/ExtVoxelTetrahedralizer.cpp
vendored
Normal file
753
engine/third_party/physx/source/physxextensions/src/tet/ExtVoxelTetrahedralizer.cpp
vendored
Normal file
@@ -0,0 +1,753 @@
|
||||
// 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.
|
||||
|
||||
#include "ExtVoxelTetrahedralizer.h"
|
||||
#include "CmRandom.h"
|
||||
#include "ExtTetUnionFind.h"
|
||||
|
||||
using namespace physx;
|
||||
using namespace Ext;
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
static PxI32 cubeNeighbors[6][3] = { { -1,0,0 }, {1,0,0}, {0,-1,0}, {0,1,0}, {0,0,-1}, {0,0,1} };
|
||||
static const PxI32 cubeCorners[8][3] = { {0,0,0}, {1,0,0},{1,1,0},{0,1,0}, {0,0,1}, {1,0,1},{1,1,1},{0,1,1} };
|
||||
static const PxI32 cubeFaces[6][4] = { {0,3,7,4},{1,2,6,5},{0,1,5,4},{3,2,6,7},{0,1,2,3},{4,5,6,7} };
|
||||
static const PxI32 oppNeighbor[6] = { 1,0,3,2,5,4 };
|
||||
|
||||
static const PxI32 tetEdges[12][2] = { {0,1},{1,2},{2,0},{0,3},{1,3},{2,3}, {1,0},{2,1},{0,2},{3,0},{3,1},{3,2} };
|
||||
|
||||
static PxI32 cubeSixTets[6][4] = {
|
||||
{ 0, 4, 5, 7 },{ 1, 5, 6, 7 },{ 1, 0, 5, 7 },{ 1, 2, 3, 6 },{ 3, 1, 6, 7 },{ 0, 1, 3, 7 } };
|
||||
|
||||
static PxI32 cubeFiveTets[2][5][4] = {
|
||||
{ { 0, 1, 2, 5 },{ 0, 2, 3, 7 },{ 0, 5, 2, 7 },{ 0, 5, 7, 4 },{ 2, 7, 5, 6 } },
|
||||
{ { 1, 2, 3, 6 },{ 1, 3, 0, 4 },{ 1, 6, 3, 4 },{ 1, 6, 4, 5 },{ 3, 4, 6, 7 } },
|
||||
};
|
||||
|
||||
static PxI32 cubeSixSubdivTets[12][4] = {
|
||||
{0,4,5,8}, {0,5,1,8}, {3,2,6,8}, {3,6,7,8},
|
||||
{0,3,7,8}, {0,7,4,8}, {1,5,6,8}, {1,6,2,8},
|
||||
{0,1,3,8}, {1,2,3,8}, {5,4,7,8}, {5,7,6,8}
|
||||
};
|
||||
|
||||
static PxI32 cubeFiveSubdivTets[2][12][4] = {
|
||||
{
|
||||
{0,1,2,8}, {0,2,3,8}, {4,7,5,8}, {5,7,6,8},
|
||||
{0,7,4,8}, {0,3,7,8}, {1,5,2,8}, {2,5,6,8},
|
||||
{0,5,1,8}, {0,4,5,8}, {3,2,7,8}, {2,6,7,8}
|
||||
},
|
||||
{
|
||||
{0,1,3,8}, {1,2,3,8}, {4,7,6,8}, {4,6,5,8},
|
||||
{0,3,4,8}, {3,7,4,8}, {1,5,6,8}, {1,6,2,8},
|
||||
{0,4,1,8}, {1,4,5,8}, {3,2,6,8}, {3,6,7,8}
|
||||
}
|
||||
};
|
||||
|
||||
static const PxI32 volIdOrder[4][3] = { {1, 3, 2}, {0, 2, 3}, {0, 3, 1}, {0, 1, 2} };
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
static bool boxTriangleIntersection(
|
||||
PxVec3 p0, PxVec3 p1, PxVec3 p2, PxVec3 center, PxVec3 extents);
|
||||
static void getClosestPointOnTriangle(
|
||||
PxVec3 p1, PxVec3 p2, PxVec3 p3, PxVec3 p, PxVec3& closest, PxVec3& bary);
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
VoxelTetrahedralizer::VoxelTetrahedralizer()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
void VoxelTetrahedralizer::clear()
|
||||
{
|
||||
surfaceVerts.clear();
|
||||
surfaceTriIds.clear();
|
||||
surfaceBounds.setEmpty();
|
||||
|
||||
tetVerts.clear();
|
||||
origTetVerts.clear();
|
||||
isSurfaceVert.clear();
|
||||
targetVertPos.clear();
|
||||
|
||||
tetIds.clear();
|
||||
voxels.clear();
|
||||
gridOrigin = PxVec3(PxZero);
|
||||
gridSpacing = 0.0f;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------
|
||||
void VoxelTetrahedralizer::readBack(PxArray<PxVec3>& _tetVertices, PxArray<PxU32>& _tetIndices)
|
||||
{
|
||||
_tetVertices = tetVerts;
|
||||
_tetIndices.resize(tetIds.size());
|
||||
|
||||
for (PxU32 i = 0; i < tetIds.size(); i++)
|
||||
_tetIndices[i] = PxU32(tetIds[i]);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------
|
||||
void VoxelTetrahedralizer::createTetMesh(const PxArray<PxVec3>& verts, const PxArray<PxU32>& triIds,
|
||||
PxI32 resolution, PxI32 numRelaxationIters, PxF32 relMinTetVolume)
|
||||
{
|
||||
surfaceVerts = verts;
|
||||
surfaceTriIds.resize(triIds.size());
|
||||
for (PxU32 i = 0; i < triIds.size(); i++)
|
||||
surfaceTriIds[i] = triIds[i];
|
||||
|
||||
surfaceBounds.setEmpty();
|
||||
|
||||
for (PxU32 i = 0; i < surfaceVerts.size(); i++)
|
||||
surfaceBounds.include(surfaceVerts[i]);
|
||||
|
||||
buildBVH();
|
||||
|
||||
voxelize(resolution);
|
||||
|
||||
bool subdivBorder = true;
|
||||
int numTetsPerVoxel = 5; // or 6
|
||||
|
||||
createTets(subdivBorder, numTetsPerVoxel);
|
||||
|
||||
findTargetPositions(0.2f * gridSpacing);
|
||||
|
||||
relax(numRelaxationIters, relMinTetVolume);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------
|
||||
void VoxelTetrahedralizer::buildBVH()
|
||||
{
|
||||
PxI32 numTris = PxI32(surfaceTriIds.size()) / 3;
|
||||
|
||||
if (numTris == 0)
|
||||
return;
|
||||
|
||||
PxArray<PxBounds3> bvhBounds(numTris);
|
||||
|
||||
for (PxI32 i = 0; i < numTris; i++) {
|
||||
PxBounds3& b = bvhBounds[i];
|
||||
b.setEmpty();
|
||||
b.include(surfaceVerts[surfaceTriIds[3 * i]]);
|
||||
b.include(surfaceVerts[surfaceTriIds[3 * i + 1]]);
|
||||
b.include(surfaceVerts[surfaceTriIds[3 * i + 2]]);
|
||||
}
|
||||
|
||||
BVHBuilder::build(bvh, &bvhBounds[0], bvhBounds.size());
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------
|
||||
void VoxelTetrahedralizer::voxelize(PxU32 resolution)
|
||||
{
|
||||
tetIds.clear();
|
||||
tetVerts.clear();
|
||||
|
||||
PxBounds3 meshBounds;
|
||||
meshBounds.setEmpty();
|
||||
|
||||
for (PxU32 i = 0; i < surfaceVerts.size(); i++)
|
||||
meshBounds.include(surfaceVerts[i]);
|
||||
|
||||
gridSpacing = meshBounds.getDimensions().magnitude() / resolution;
|
||||
meshBounds.fattenSafe(gridSpacing);
|
||||
gridOrigin = meshBounds.minimum;
|
||||
|
||||
voxels.clear();
|
||||
|
||||
PxI32 numX = PxI32((meshBounds.maximum.x - meshBounds.minimum.x) / gridSpacing) + 1;
|
||||
PxI32 numY = PxI32((meshBounds.maximum.y - meshBounds.minimum.y) / gridSpacing) + 1;
|
||||
PxI32 numZ = PxI32((meshBounds.maximum.z - meshBounds.minimum.z) / gridSpacing) + 1;
|
||||
PxI32 numCells = numX * numY * numZ;
|
||||
|
||||
PxArray<PxI32> voxelOfCell(numCells, -1);
|
||||
PxBounds3 voxelBounds, faceBounds;
|
||||
|
||||
// create intersected voxels
|
||||
|
||||
for (PxI32 i = 0; i < numCells; i++) {
|
||||
PxI32 zi = i % numZ;
|
||||
PxI32 yi = (i / numZ) % numY;
|
||||
PxI32 xi = (i / numZ / numY);
|
||||
|
||||
voxelBounds.minimum = meshBounds.minimum + PxVec3(PxF32(xi), PxF32(yi), PxF32(zi)) * gridSpacing;
|
||||
voxelBounds.maximum = voxelBounds.minimum + PxVec3(gridSpacing);
|
||||
|
||||
bvh.query(voxelBounds, queryTris);
|
||||
|
||||
for (PxU32 j = 0; j < queryTris.size(); j++) {
|
||||
PxI32 triNr = queryTris[j];
|
||||
|
||||
const PxVec3& p0 = surfaceVerts[surfaceTriIds[3 * triNr]];
|
||||
const PxVec3& p1 = surfaceVerts[surfaceTriIds[3 * triNr + 1]];
|
||||
const PxVec3& p2 = surfaceVerts[surfaceTriIds[3 * triNr + 2]];
|
||||
|
||||
if (boxTriangleIntersection(p0, p1, p2, voxelBounds.getCenter(), voxelBounds.getExtents())) {
|
||||
// volume
|
||||
if (voxelOfCell[i] < 0) {
|
||||
voxelOfCell[i] = voxels.size();
|
||||
voxels.resize(voxels.size() + 1);
|
||||
voxels.back().init(xi, yi, zi);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// flood outside
|
||||
|
||||
PxArray<PxI32> stack;
|
||||
stack.pushBack(0);
|
||||
|
||||
while (!stack.empty()) {
|
||||
PxI32 nr = stack.back();
|
||||
stack.popBack();
|
||||
|
||||
if (voxelOfCell[nr] == -1) {
|
||||
voxelOfCell[nr] = -2; // outside
|
||||
|
||||
PxI32 z0 = nr % numZ;
|
||||
PxI32 y0 = (nr / numZ) % numY;
|
||||
PxI32 x0 = (nr / numZ / numY);
|
||||
|
||||
for (PxI32 i = 0; i < 6; i++) {
|
||||
PxI32 xi = x0 + cubeNeighbors[i][0];
|
||||
PxI32 yi = y0 + cubeNeighbors[i][1];
|
||||
PxI32 zi = z0 + cubeNeighbors[i][2];
|
||||
if (xi >= 0 && xi < numX && yi >= 0 && yi < numY && zi >= 0 && zi < numZ) {
|
||||
PxI32 adj = (xi * numY + yi) * numZ + zi;
|
||||
if (voxelOfCell[adj] == -1)
|
||||
stack.pushBack(adj);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// create voxels for the inside
|
||||
|
||||
for (PxI32 i = 0; i < numCells; i++) {
|
||||
if (voxelOfCell[i] == -1) {
|
||||
voxelOfCell[i] = voxels.size();
|
||||
voxels.resize(voxels.size() + 1);
|
||||
PxI32 zi = i % numZ;
|
||||
PxI32 yi = (i / numZ) % numY;
|
||||
PxI32 xi = (i / numZ / numY);
|
||||
voxels.back().init(xi, yi, zi);
|
||||
voxels.back().inner = true;
|
||||
}
|
||||
}
|
||||
|
||||
// find neighbors
|
||||
|
||||
for (PxU32 i = 0; i < voxels.size(); i++) {
|
||||
Voxel& v = voxels[i];
|
||||
|
||||
voxelBounds.minimum = meshBounds.minimum + PxVec3(PxF32(v.xi), PxF32(v.yi), PxF32(v.zi)) * gridSpacing;
|
||||
voxelBounds.maximum = voxelBounds.minimum + PxVec3(gridSpacing);
|
||||
|
||||
for (PxI32 j = 0; j < 6; j++) {
|
||||
|
||||
PxI32 xi = v.xi + cubeNeighbors[j][0];
|
||||
PxI32 yi = v.yi + cubeNeighbors[j][1];
|
||||
PxI32 zi = v.zi + cubeNeighbors[j][2];
|
||||
|
||||
if (xi < 0 || xi >= numX || yi < 0 || yi >= numY || zi < 0 || zi >= numZ)
|
||||
continue;
|
||||
|
||||
PxI32 neighbor = voxelOfCell[(xi * numY + yi) * numZ + zi];
|
||||
if (neighbor < 0)
|
||||
continue;
|
||||
|
||||
if (v.inner || voxels[neighbor].inner) {
|
||||
v.neighbors[j] = neighbor;
|
||||
continue;
|
||||
}
|
||||
|
||||
faceBounds = voxelBounds;
|
||||
PxF32 eps = 1e-4f;
|
||||
switch (j) {
|
||||
case 0: faceBounds.maximum.x = faceBounds.minimum.x + eps; break;
|
||||
case 1: faceBounds.minimum.x = faceBounds.maximum.x - eps; break;
|
||||
case 2: faceBounds.maximum.y = faceBounds.minimum.y + eps; break;
|
||||
case 3: faceBounds.minimum.y = faceBounds.maximum.y - eps; break;
|
||||
case 4: faceBounds.maximum.z = faceBounds.minimum.z + eps; break;
|
||||
case 5: faceBounds.minimum.z = faceBounds.maximum.z - eps; break;
|
||||
}
|
||||
bvh.query(faceBounds, queryTris);
|
||||
|
||||
bool intersected = false;
|
||||
|
||||
for (PxU32 k = 0; k < queryTris.size(); k++) {
|
||||
PxI32 triNr = queryTris[k];
|
||||
|
||||
const PxVec3& p0 = surfaceVerts[surfaceTriIds[3 * triNr]];
|
||||
const PxVec3& p1 = surfaceVerts[surfaceTriIds[3 * triNr + 1]];
|
||||
const PxVec3& p2 = surfaceVerts[surfaceTriIds[3 * triNr + 2]];
|
||||
|
||||
if (boxTriangleIntersection(p0, p1, p2, faceBounds.getCenter(), faceBounds.getExtents())) {
|
||||
intersected = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (intersected)
|
||||
v.neighbors[j] = neighbor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------
|
||||
void VoxelTetrahedralizer::createUniqueTetVertices()
|
||||
{
|
||||
// start with each voxel having its own vertices
|
||||
|
||||
PxArray<PxVec3> verts;
|
||||
for (PxU32 i = 0; i < voxels.size(); i++) {
|
||||
Voxel& v = voxels[i];
|
||||
|
||||
for (PxI32 j = 0; j < 8; j++) {
|
||||
v.ids[j] = verts.size();
|
||||
verts.pushBack(gridOrigin + PxVec3(
|
||||
PxF32(v.xi + cubeCorners[j][0]),
|
||||
PxF32(v.yi + cubeCorners[j][1]),
|
||||
PxF32(v.zi + cubeCorners[j][2])) * gridSpacing);
|
||||
}
|
||||
}
|
||||
|
||||
// unify vertices
|
||||
|
||||
UnionFind* u = new UnionFind();
|
||||
u->init(verts.size());
|
||||
|
||||
for (PxU32 i = 0; i < voxels.size(); i++) {
|
||||
Voxel& v0 = voxels[i];
|
||||
for (PxI32 j = 0; j < 6; j++) {
|
||||
PxI32 n = v0.neighbors[j];
|
||||
if (n < 0)
|
||||
continue;
|
||||
Voxel& v1 = voxels[n];
|
||||
|
||||
for (PxI32 k = 0; k < 4; k++) {
|
||||
PxI32 id0 = v0.ids[cubeFaces[j][k]];
|
||||
PxI32 id1 = v1.ids[cubeFaces[oppNeighbor[j]][k]];
|
||||
u->makeSet(id0, id1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
u->computeSetNrs();
|
||||
|
||||
tetVerts.clear();
|
||||
|
||||
for (PxU32 i = 0; i < voxels.size(); i++) {
|
||||
Voxel& v = voxels[i];
|
||||
|
||||
for (PxI32 j = 0; j < 8; j++) {
|
||||
PxI32 setNr = u->getSetNr(v.ids[j]);
|
||||
if (PxI32(tetVerts.size()) <= setNr)
|
||||
tetVerts.resize(setNr + 1, PxVec3(PxZero));
|
||||
tetVerts[setNr] = verts[v.ids[j]];
|
||||
v.ids[j] = setNr;
|
||||
}
|
||||
}
|
||||
|
||||
origTetVerts = tetVerts;
|
||||
|
||||
delete u;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
void VoxelTetrahedralizer::findTargetPositions(PxF32 surfaceDist)
|
||||
{
|
||||
targetVertPos = tetVerts;
|
||||
|
||||
for (PxU32 i = 0; i < voxels.size(); i++) {
|
||||
|
||||
Voxel& v = voxels[i];
|
||||
|
||||
PxBounds3 voxelBounds;
|
||||
voxelBounds.minimum = gridOrigin + PxVec3(PxF32(v.xi), PxF32(v.yi), PxF32(v.zi)) * gridSpacing;
|
||||
voxelBounds.maximum = voxelBounds.minimum + PxVec3(gridSpacing);
|
||||
voxelBounds.fattenFast(0.1f * gridSpacing);
|
||||
bvh.query(voxelBounds, queryTris);
|
||||
|
||||
for (PxI32 j = 0; j < 8; j++) {
|
||||
PxI32 id = v.ids[j];
|
||||
if (!isSurfaceVert[id])
|
||||
continue;
|
||||
|
||||
PxVec3& p = tetVerts[id];
|
||||
|
||||
PxF32 minDist2 = PX_MAX_F32;
|
||||
PxVec3 closest(PxZero);
|
||||
|
||||
for (PxU32 k = 0; k < queryTris.size(); k++) {
|
||||
|
||||
PxI32 triNr = queryTris[k];
|
||||
const PxVec3& p0 = surfaceVerts[surfaceTriIds[3 * triNr]];
|
||||
const PxVec3& p1 = surfaceVerts[surfaceTriIds[3 * triNr + 1]];
|
||||
const PxVec3& p2 = surfaceVerts[surfaceTriIds[3 * triNr + 2]];
|
||||
PxVec3 c, bary;
|
||||
getClosestPointOnTriangle(p0, p1, p2, p, c, bary);
|
||||
PxF32 dist2 = (c - p).magnitudeSquared();
|
||||
if (dist2 < minDist2) {
|
||||
minDist2 = dist2;
|
||||
closest = c;
|
||||
}
|
||||
}
|
||||
if (minDist2 < PX_MAX_F32) {
|
||||
PxVec3 n = p - closest;
|
||||
n.normalize();
|
||||
targetVertPos[id] = closest + n * surfaceDist;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------
|
||||
void VoxelTetrahedralizer::createTets(bool subdivBorder, PxU32 numTetsPerVoxel)
|
||||
{
|
||||
if (numTetsPerVoxel < 5 || numTetsPerVoxel > 6)
|
||||
return;
|
||||
|
||||
createUniqueTetVertices();
|
||||
|
||||
PxArray<Voxel> prevVoxels;
|
||||
|
||||
PxArray<PxI32> numVertVoxels(tetVerts.size(), 0);
|
||||
tetIds.clear();
|
||||
|
||||
for (PxU32 i = 0; i < voxels.size(); i++) {
|
||||
Voxel& v = voxels[i];
|
||||
for (PxI32 j = 0; j < 8; j++)
|
||||
numVertVoxels[v.ids[j]]++;
|
||||
|
||||
PxI32 parity = (v.xi + v.yi + v.zi) % 2;
|
||||
|
||||
if (v.inner || !subdivBorder) {
|
||||
if (numTetsPerVoxel == 6) {
|
||||
for (PxI32 j = 0; j < 6; j++) {
|
||||
tetIds.pushBack(v.ids[cubeSixTets[j][0]]);
|
||||
tetIds.pushBack(v.ids[cubeSixTets[j][1]]);
|
||||
tetIds.pushBack(v.ids[cubeSixTets[j][2]]);
|
||||
tetIds.pushBack(v.ids[cubeSixTets[j][3]]);
|
||||
}
|
||||
}
|
||||
else if (numTetsPerVoxel == 5) {
|
||||
for (PxI32 j = 0; j < 5; j++) {
|
||||
tetIds.pushBack(v.ids[cubeFiveTets[parity][j][0]]);
|
||||
tetIds.pushBack(v.ids[cubeFiveTets[parity][j][1]]);
|
||||
tetIds.pushBack(v.ids[cubeFiveTets[parity][j][2]]);
|
||||
tetIds.pushBack(v.ids[cubeFiveTets[parity][j][3]]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
PxVec3 p(PxZero);
|
||||
for (PxI32 j = 0; j < 8; j++)
|
||||
p += tetVerts[v.ids[j]];
|
||||
p /= 8.0;
|
||||
PxI32 newId = tetVerts.size();
|
||||
tetVerts.pushBack(p);
|
||||
origTetVerts.pushBack(p);
|
||||
numVertVoxels.pushBack(8);
|
||||
|
||||
for (PxI32 j = 0; j < 12; j++) {
|
||||
|
||||
const int* localIds;
|
||||
if (numTetsPerVoxel == 6)
|
||||
localIds = cubeSixSubdivTets[j];
|
||||
else
|
||||
localIds = cubeFiveSubdivTets[parity][j];
|
||||
|
||||
for (PxI32 k = 0; k < 4; k++) {
|
||||
PxI32 id = localIds[k] < 8 ? v.ids[localIds[k]] : newId;
|
||||
tetIds.pushBack(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
isSurfaceVert.resize(tetVerts.size(), false);
|
||||
for (PxU32 i = 0; i < tetVerts.size(); i++)
|
||||
isSurfaceVert[i] = numVertVoxels[i] < 8;
|
||||
|
||||
// randomize tets
|
||||
|
||||
PxU32 numTets = tetIds.size() / 4;
|
||||
|
||||
//for (PxU32 i = 0; i < numTets - 1; i++) {
|
||||
// PxI32 ri = i + rand() % (numTets - i);
|
||||
// for (PxI32 j = 0; j < 4; j++) {
|
||||
// PxI32 id = tetIds[4 * i + j]; tetIds[4 * i + j] = tetIds[4 * ri + j]; tetIds[4 * ri + j] = id;
|
||||
// }
|
||||
//}
|
||||
|
||||
// edges
|
||||
|
||||
MultiList<int> adjVerts;
|
||||
edgeIds.clear();
|
||||
|
||||
adjVerts.clear();
|
||||
adjVerts.reserve(tetVerts.size());
|
||||
|
||||
for (PxU32 i = 0; i < numTets; i++) {
|
||||
for (PxI32 j = 0; j < 6; j++) {
|
||||
PxI32 id0 = tetIds[4 * i + tetEdges[j][0]];
|
||||
PxI32 id1 = tetIds[4 * i + tetEdges[j][1]];
|
||||
|
||||
if (!adjVerts.exists(id0, id1)) {
|
||||
edgeIds.pushBack(id0);
|
||||
edgeIds.pushBack(id1);
|
||||
|
||||
adjVerts.addUnique(id0, id1);
|
||||
adjVerts.addUnique(id1, id0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------
|
||||
void VoxelTetrahedralizer::conserveVolume(PxF32 relMinVolume)
|
||||
{
|
||||
PxVec3 grads[4];
|
||||
PxU32 numTets = tetIds.size() / 4;
|
||||
|
||||
for (PxU32 i = 0; i < numTets; i++) {
|
||||
PxI32* ids = &tetIds[4 * i];
|
||||
|
||||
PxF32 w = 0.0f;
|
||||
|
||||
for (PxI32 j = 0; j < 4; j++) {
|
||||
PxI32 id0 = ids[volIdOrder[j][0]];
|
||||
PxI32 id1 = ids[volIdOrder[j][1]];
|
||||
PxI32 id2 = ids[volIdOrder[j][2]];
|
||||
|
||||
grads[j] = (tetVerts[id1] - tetVerts[id0]).cross(tetVerts[id2] - tetVerts[id0]);
|
||||
w += grads[j].magnitudeSquared();
|
||||
}
|
||||
|
||||
if (w == 0.0f)
|
||||
continue;
|
||||
|
||||
PxVec3& p0 = tetVerts[ids[0]];
|
||||
PxF32 V = (tetVerts[ids[1]] - p0).cross(tetVerts[ids[2]] - p0).dot(tetVerts[ids[3]] - p0);
|
||||
|
||||
PxVec3& origP0 = origTetVerts[ids[0]];
|
||||
PxF32 origV = (origTetVerts[ids[1]] - origP0).cross(origTetVerts[ids[2]] - origP0).dot(origTetVerts[ids[3]] - origP0);
|
||||
|
||||
PxF32 minV = relMinVolume * origV;
|
||||
|
||||
if (V < minV) {
|
||||
|
||||
PxF32 C = V - minV;
|
||||
PxF32 lambda = -C / w;
|
||||
|
||||
for (PxI32 j = 0; j < 4; j++) {
|
||||
tetVerts[ids[j]] += grads[j] * lambda;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
void VoxelTetrahedralizer::relax(PxI32 numIters, PxF32 relMinVolume)
|
||||
{
|
||||
const PxF32 targetScale = 0.3f;
|
||||
const PxF32 edgeScale = 0.3f;
|
||||
|
||||
for (PxI32 iter = 0; iter < numIters; iter++) {
|
||||
PxU32 numVerts = tetVerts.size();
|
||||
|
||||
for (PxU32 i = 0; i < numVerts; i++) {
|
||||
if (isSurfaceVert[i]) {
|
||||
PxVec3 offset = (targetVertPos[i] - tetVerts[i]) * targetScale;
|
||||
tetVerts[i] += offset;
|
||||
}
|
||||
}
|
||||
|
||||
for (PxU32 i = 0; i < edgeIds.size(); i += 2) {
|
||||
PxI32 id0 = edgeIds[i];
|
||||
PxI32 id1 = edgeIds[i + 1];
|
||||
PxF32 w0 = isSurfaceVert[id0] ? 0.0f : 1.0f;
|
||||
PxF32 w1 = isSurfaceVert[id1] ? 0.0f : 1.0f;
|
||||
PxF32 w = w0 + w1;
|
||||
if (w == 0.0f)
|
||||
continue;
|
||||
PxVec3& p0 = tetVerts[id0];
|
||||
PxVec3& p1 = tetVerts[id1];
|
||||
|
||||
PxVec3 e = (p1 - p0) * edgeScale;
|
||||
|
||||
if (w == 1.0f)
|
||||
e *= 0.5f;
|
||||
|
||||
p0 += w0 / w * e;
|
||||
p1 -= w1 / w * e;
|
||||
}
|
||||
conserveVolume(relMinVolume);
|
||||
}
|
||||
|
||||
PxI32 volIters = 2;
|
||||
|
||||
for (PxI32 volIter = 0; volIter < volIters; volIter++)
|
||||
conserveVolume(relMinVolume);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------
|
||||
static PxF32 max3(PxF32 f0, PxF32 f1, PxF32 f2) {
|
||||
return PxMax(f0, PxMax(f1, f2));
|
||||
}
|
||||
|
||||
static PxF32 min3(PxF32 f0, PxF32 f1, PxF32 f2) {
|
||||
return PxMin(f0, PxMin(f1, f2));
|
||||
}
|
||||
|
||||
static PxF32 minMax(PxF32 f0, PxF32 f1, PxF32 f2) {
|
||||
return PxMax(-max3(f0, f1, f2), min3(f0, f1, f2));
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------
|
||||
// PT: TODO: refactor with other SDK implementation
|
||||
static bool boxTriangleIntersection(
|
||||
PxVec3 p0, PxVec3 p1, PxVec3 p2, PxVec3 center, PxVec3 extents)
|
||||
{
|
||||
PxVec3 v0 = p0 - center, v1 = p1 - center, v2 = p2 - center;
|
||||
PxVec3 f0 = p1 - p0, f1 = p2 - p1, f2 = p0 - p2;
|
||||
PxF32 r;
|
||||
|
||||
PxVec3 n = f0.cross(f1);
|
||||
PxF32 d = n.dot(v0);
|
||||
r = extents.x * fabsf(n.x) + extents.y * fabsf(n.y) + extents.z * fabsf(n.z);
|
||||
if (d > r || d < -r)
|
||||
return false;
|
||||
|
||||
if (max3(v0.x, v1.x, v2.x) < -extents.x || min3(v0.x, v1.x, v2.x) > extents.x)
|
||||
return false;
|
||||
|
||||
if (max3(v0.y, v1.y, v2.y) < -extents.y || min3(v0.y, v1.y, v2.y) > extents.y)
|
||||
return false;
|
||||
|
||||
if (max3(v0.z, v1.z, v2.z) < -extents.z || min3(v0.z, v1.z, v2.z) > extents.z)
|
||||
return false;
|
||||
|
||||
PxVec3 a00(0.0f, -f0.z, f0.y);
|
||||
r = extents.y * fabsf(f0.z) + extents.z * fabsf(f0.y);
|
||||
if (minMax(v0.dot(a00), v1.dot(a00), v2.dot(a00)) > r)
|
||||
return false;
|
||||
|
||||
PxVec3 a01(0.0f, -f1.z, f1.y);
|
||||
r = extents.y * fabsf(f1.z) + extents.z * fabsf(f1.y);
|
||||
if (minMax(v0.dot(a01), v1.dot(a01), v2.dot(a01)) > r)
|
||||
return false;
|
||||
|
||||
PxVec3 a02(0.0f, -f2.z, f2.y);
|
||||
r = extents.y * fabsf(f2.z) + extents.z * fabsf(f2.y);
|
||||
if (minMax(v0.dot(a02), v1.dot(a02), v2.dot(a02)) > r)
|
||||
return false;
|
||||
|
||||
PxVec3 a10(f0.z, 0.0f, -f0.x);
|
||||
r = extents.x * fabsf(f0.z) + extents.z * fabsf(f0.x);
|
||||
if (minMax(v0.dot(a10), v1.dot(a10), v2.dot(a10)) > r)
|
||||
return false;
|
||||
|
||||
PxVec3 a11(f1.z, 0.0f, -f1.x);
|
||||
r = extents.x * fabsf(f1.z) + extents.z * fabsf(f1.x);
|
||||
if (minMax(v0.dot(a11), v1.dot(a11), v2.dot(a11)) > r)
|
||||
return false;
|
||||
|
||||
PxVec3 a12(f2.z, 0.0f, -f2.x);
|
||||
r = extents.x * fabsf(f2.z) + extents.z * fabsf(f2.x);
|
||||
if (minMax(v0.dot(a12), v1.dot(a12), v2.dot(a12)) > r)
|
||||
return false;
|
||||
|
||||
PxVec3 a20(-f0.y, f0.x, 0.0f);
|
||||
r = extents.x * fabsf(f0.y) + extents.y * fabsf(f0.x);
|
||||
if (minMax(v0.dot(a20), v1.dot(a20), v2.dot(a20)) > r)
|
||||
return false;
|
||||
|
||||
PxVec3 a21(-f1.y, f1.x, 0.0f);
|
||||
r = extents.x * fabsf(f1.y) + extents.y * fabsf(f1.x);
|
||||
if (minMax(v0.dot(a21), v1.dot(a21), v2.dot(a21)) > r)
|
||||
return false;
|
||||
|
||||
PxVec3 a22(-f2.y, f2.x, 0.0f);
|
||||
r = extents.x * fabsf(f2.y) + extents.y * fabsf(f2.x);
|
||||
if (minMax(v0.dot(a22), v1.dot(a22), v2.dot(a22)) > r)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------
|
||||
// PT: TODO: refactor with other implementation
|
||||
static void getClosestPointOnTriangle(
|
||||
PxVec3 p1, PxVec3 p2, PxVec3 p3, PxVec3 p, PxVec3& closest, PxVec3& bary)
|
||||
{
|
||||
PxVec3 e0 = p2 - p1;
|
||||
PxVec3 e1 = p3 - p1;
|
||||
PxVec3 tmp = p1 - p;
|
||||
|
||||
PxF32 a = e0.dot(e0);
|
||||
PxF32 b = e0.dot(e1);
|
||||
PxF32 c = e1.dot(e1);
|
||||
PxF32 d = e0.dot(tmp);
|
||||
PxF32 e = e1.dot(tmp);
|
||||
PxVec3 coords, clampedCoords;
|
||||
coords.x = b * e - c * d; // s * det
|
||||
coords.y = b * d - a * e; // t * det
|
||||
coords.z = a * c - b * b; // det
|
||||
|
||||
clampedCoords = PxVec3(0.0f, 0.0f, 0.0f);
|
||||
if (coords.x <= 0.0f) {
|
||||
if (c != 0.0f)
|
||||
clampedCoords.y = -e / c;
|
||||
}
|
||||
else if (coords.y <= 0.0f) {
|
||||
if (a != 0.0f)
|
||||
clampedCoords.x = -d / a;
|
||||
}
|
||||
else if (coords.x + coords.y > coords.z) {
|
||||
PxF32 denominator = a + c - b - b;
|
||||
PxF32 numerator = c + e - b - d;
|
||||
if (denominator != 0.0f) {
|
||||
clampedCoords.x = numerator / denominator;
|
||||
clampedCoords.y = 1.0f - clampedCoords.x;
|
||||
}
|
||||
}
|
||||
else { // all inside
|
||||
if (coords.z != 0.0f) {
|
||||
clampedCoords.x = coords.x / coords.z;
|
||||
clampedCoords.y = coords.y / coords.z;
|
||||
}
|
||||
}
|
||||
clampedCoords.x = PxMax(clampedCoords.x, 0.0f);
|
||||
clampedCoords.y = PxMax(clampedCoords.y, 0.0f);
|
||||
clampedCoords.x = PxMin(clampedCoords.x, 1.0f);
|
||||
clampedCoords.y = PxMin(clampedCoords.y, 1.0f);
|
||||
|
||||
closest = p1 + e0 * clampedCoords.x + e1 * clampedCoords.y;
|
||||
|
||||
bary.x = 1.0f - clampedCoords.x - clampedCoords.y;
|
||||
bary.y = clampedCoords.x;
|
||||
bary.z = clampedCoords.y;
|
||||
}
|
||||
116
engine/third_party/physx/source/physxextensions/src/tet/ExtVoxelTetrahedralizer.h
vendored
Normal file
116
engine/third_party/physx/source/physxextensions/src/tet/ExtVoxelTetrahedralizer.h
vendored
Normal file
@@ -0,0 +1,116 @@
|
||||
// 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 EXT_VOXEL_TETRAHEDRALIZER_H
|
||||
#define EXT_VOXEL_TETRAHEDRALIZER_H
|
||||
|
||||
#include "ExtMultiList.h"
|
||||
#include "ExtBVH.h"
|
||||
#include "foundation/PxVec3.h"
|
||||
#include "foundation/PxBounds3.h"
|
||||
|
||||
namespace physx
|
||||
{
|
||||
namespace Ext
|
||||
{
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
class VoxelTetrahedralizer
|
||||
{
|
||||
public:
|
||||
VoxelTetrahedralizer();
|
||||
|
||||
void clear();
|
||||
void createTetMesh(const PxArray<PxVec3>& verts, const PxArray<PxU32>& triIds,
|
||||
PxI32 resolution, PxI32 numRelaxationIters = 5, PxF32 relMinTetVolume = 0.05f);
|
||||
|
||||
void readBack(PxArray<PxVec3>& tetVertices, PxArray<PxU32>& tetIndices);
|
||||
|
||||
private:
|
||||
void voxelize(PxU32 resolution);
|
||||
void createTets(bool subdivBorder, PxU32 numTetsPerVoxel);
|
||||
void buildBVH();
|
||||
void createUniqueTetVertices();
|
||||
void findTargetPositions(PxF32 surfaceDist);
|
||||
void conserveVolume(PxF32 relMinVolume);
|
||||
void relax(PxI32 numIters, PxF32 relMinVolume);
|
||||
|
||||
// input mesh
|
||||
|
||||
PxArray<PxVec3> surfaceVerts;
|
||||
PxArray<PxI32> surfaceTriIds;
|
||||
PxBounds3 surfaceBounds;
|
||||
|
||||
// voxel grid
|
||||
|
||||
struct Voxel {
|
||||
void init(PxI32 _xi, PxI32 _yi, PxI32 _zi)
|
||||
{
|
||||
xi = _xi; yi = _yi; zi = _zi;
|
||||
for (PxI32 i = 0; i < 6; i++)
|
||||
neighbors[i] = -1;
|
||||
for (PxI32 i = 0; i < 8; i++)
|
||||
ids[i] = -1;
|
||||
parent = -1;
|
||||
inner = false;
|
||||
}
|
||||
bool isAt(PxI32 _xi, PxI32 _yi, PxI32 _zi) {
|
||||
return xi == _xi && yi == _yi && zi == _zi;
|
||||
}
|
||||
PxI32 xi, yi, zi;
|
||||
PxI32 neighbors[6];
|
||||
PxI32 parent;
|
||||
PxI32 ids[8];
|
||||
bool inner;
|
||||
};
|
||||
|
||||
PxVec3 gridOrigin;
|
||||
PxF32 gridSpacing;
|
||||
PxArray<Voxel> voxels;
|
||||
|
||||
BVHDesc bvh;
|
||||
|
||||
// tet mesh
|
||||
|
||||
PxArray<PxVec3> tetVerts;
|
||||
PxArray<PxVec3> origTetVerts;
|
||||
PxArray<PxI32> tetIds;
|
||||
|
||||
// relaxation
|
||||
|
||||
PxArray<bool> isSurfaceVert;
|
||||
PxArray<PxVec3> targetVertPos;
|
||||
PxArray<PxI32> queryTris;
|
||||
PxArray<PxI32> edgeIds;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user