726 lines
18 KiB
C++
726 lines
18 KiB
C++
// Redistribution and use in source and binary forms, with or without
|
|
// modification, are permitted provided that the following conditions
|
|
// are met:
|
|
// * Redistributions of source code must retain the above copyright
|
|
// notice, this list of conditions and the following disclaimer.
|
|
// * Redistributions in binary form must reproduce the above copyright
|
|
// notice, this list of conditions and the following disclaimer in the
|
|
// documentation and/or other materials provided with the distribution.
|
|
// * Neither the name of NVIDIA CORPORATION nor the names of its
|
|
// contributors may be used to endorse or promote products derived
|
|
// from this software without specific prior written permission.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
|
|
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
|
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
|
// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
//
|
|
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
|
|
|
|
#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]);
|
|
}
|