aboutsummaryrefslogtreecommitdiff
path: root/APEX_1.4/common/src/ApexIsoMesh.cpp
diff options
context:
space:
mode:
authorgit perforce import user <a@b>2016-10-25 12:29:14 -0600
committerSheikh Dawood Abdul Ajees <Sheikh Dawood Abdul Ajees>2016-10-25 18:56:37 -0500
commit3dfe2108cfab31ba3ee5527e217d0d8e99a51162 (patch)
treefa6485c169e50d7415a651bf838f5bcd0fd3bfbd /APEX_1.4/common/src/ApexIsoMesh.cpp
downloadphysx-3.4-3dfe2108cfab31ba3ee5527e217d0d8e99a51162.tar.xz
physx-3.4-3dfe2108cfab31ba3ee5527e217d0d8e99a51162.zip
Initial commit:
PhysX 3.4.0 Update @ 21294896 APEX 1.4.0 Update @ 21275617 [CL 21300167]
Diffstat (limited to 'APEX_1.4/common/src/ApexIsoMesh.cpp')
-rw-r--r--APEX_1.4/common/src/ApexIsoMesh.cpp658
1 files changed, 658 insertions, 0 deletions
diff --git a/APEX_1.4/common/src/ApexIsoMesh.cpp b/APEX_1.4/common/src/ApexIsoMesh.cpp
new file mode 100644
index 00000000..7700c9e2
--- /dev/null
+++ b/APEX_1.4/common/src/ApexIsoMesh.cpp
@@ -0,0 +1,658 @@
+/*
+ * Copyright (c) 2008-2015, NVIDIA CORPORATION. All rights reserved.
+ *
+ * NVIDIA CORPORATION and its licensors retain all intellectual property
+ * and proprietary rights in and to this software, related documentation
+ * and any modifications thereto. Any use, reproduction, disclosure or
+ * distribution of this software and related documentation without an express
+ * license agreement from NVIDIA CORPORATION is strictly prohibited.
+ */
+
+
+#include "ApexIsoMesh.h"
+#include "ApexCollision.h"
+#include "ApexMarchingCubes.h"
+#include "ApexSharedUtils.h"
+
+#include "ApexSDKIntl.h"
+
+#include "PsSort.h"
+
+namespace nvidia
+{
+namespace apex
+{
+
+ApexIsoMesh::ApexIsoMesh(uint32_t isoGridSubdivision, uint32_t keepNBiggestMeshes, bool discardInnerMeshes) :
+ mIsoGridSubdivision(isoGridSubdivision),
+ mKeepNBiggestMeshes(keepNBiggestMeshes),
+ mDiscardInnerMeshes(discardInnerMeshes),
+ mCellSize(0.0f),
+ mThickness(0.0f),
+ mOrigin(0.0f, 0.0f, 0.0f),
+ mNumX(0), mNumY(0), mNumZ(0),
+ mIsoValue(0.5f)
+{
+ if (mIsoGridSubdivision == 0)
+ {
+ APEX_INVALID_PARAMETER("isoGridSubdivision must be bigger than 0, setting it to 10");
+ mIsoGridSubdivision = 10;
+ }
+ mBound.setEmpty();
+}
+
+
+
+ApexIsoMesh::~ApexIsoMesh()
+{
+}
+
+
+
+void ApexIsoMesh::setBound(const PxBounds3& bound)
+{
+ mBound = bound;
+ mCellSize = (mBound.maximum - mBound.minimum).magnitude() / mIsoGridSubdivision;
+ if (mCellSize == 0.0f)
+ {
+ mCellSize = 1.0f;
+ }
+ mThickness = mCellSize * 1.5f;
+ PX_ASSERT(!mBound.isEmpty());
+ mBound.fattenFast(2.0f * mThickness);
+
+ mOrigin = mBound.minimum;
+ float invH = 1.0f / mCellSize;
+ mNumX = (int32_t)((mBound.maximum.x - mBound.minimum.x) * invH) + 1;
+ mNumY = (int32_t)((mBound.maximum.y - mBound.minimum.y) * invH) + 1;
+ mNumZ = (int32_t)((mBound.maximum.z - mBound.minimum.z) * invH) + 1;
+}
+
+
+
+void ApexIsoMesh::clear()
+{
+ mCellSize = 0.0f;
+ mThickness = 0.0f;
+ mOrigin = PxVec3(0.0f);
+ mNumX = mNumY = mNumZ = 0;
+ mBound.setEmpty();
+
+ clearTemp();
+
+ mIsoVertices.clear();
+ mIsoTriangles.clear();
+ mIsoEdges.clear();
+}
+
+
+
+void ApexIsoMesh::clearTemp()
+{
+ mGrid.clear();
+ mGrid.reset();
+}
+
+
+
+void ApexIsoMesh::addTriangle(const PxVec3& v0, const PxVec3& v1, const PxVec3& v2)
+{
+ int32_t num = mNumX * mNumY * mNumZ;
+ if (mGrid.size() != (uint32_t) num)
+ {
+ IsoCell cell;
+ cell.init();
+ mGrid.resize((uint32_t)num, cell);
+ }
+
+ PX_ASSERT(mThickness != 0.0f);
+
+ PxBounds3 bounds;
+ bounds.minimum = bounds.maximum = v0;
+ bounds.include(v1);
+ bounds.include(v2);
+ PX_ASSERT(!bounds.isEmpty());
+ bounds.fattenFast(mThickness);
+
+ float h = mCellSize;
+ float invH = 1.0f / h;
+ float invT = 1.0f / mThickness;
+
+ int32_t x0 = PxMax(0, (int32_t)((bounds.minimum.x - mOrigin.x) * invH));
+ int32_t x1 = PxMin(mNumX - 1, (int32_t)((bounds.maximum.x - mOrigin.x) * invH));
+ int32_t y0 = PxMax(0, (int32_t)((bounds.minimum.y - mOrigin.y) * invH));
+ int32_t y1 = PxMin(mNumY - 1, (int32_t)((bounds.maximum.y - mOrigin.y) * invH));
+ int32_t z0 = PxMax(0, (int32_t)((bounds.minimum.z - mOrigin.z) * invH));
+ int32_t z1 = PxMin(mNumZ - 1, (int32_t)((bounds.maximum.z - mOrigin.z) * invH));
+
+ for (int32_t xi = x0; xi <= x1; xi++)
+ {
+ PxVec3 pos;
+ pos.x = mOrigin.x + h * xi;
+ for (int32_t yi = y0; yi <= y1; yi++)
+ {
+ pos.y = mOrigin.y + h * yi;
+ for (int32_t zi = z0; zi <= z1; zi++)
+ {
+ pos.z = mOrigin.z + h * zi;
+ Triangle triangle(v0, v1, v2);
+ float dist = PxSqrt(APEX_pointTriangleSqrDst(triangle, pos));
+ if (dist > mThickness)
+ {
+ continue;
+ }
+
+ IsoCell& cell = cellAt(xi, yi, zi);
+ float density = 1.0f - dist * invT;
+ PX_ASSERT(density >= 0);
+ cell.density = PxMax(cell.density, density); ///< \todo, is this right?
+ }
+ }
+ }
+}
+
+
+
+bool ApexIsoMesh::update(IProgressListener* progressListener)
+{
+ HierarchicalProgressListener progress(100, progressListener);
+ progress.setSubtaskWork(90, "Generating first mesh");
+
+ if (generateMesh(&progress))
+ {
+
+ progress.completeSubtask();
+ progress.setSubtaskWork(5, "Finding Neighbors");
+
+ if (findNeighbors(&progress))
+ {
+
+ if (mKeepNBiggestMeshes > 0 || mDiscardInnerMeshes)
+ {
+ progress.completeSubtask();
+ progress.setSubtaskWork(3, "Removing layers");
+ removeLayers();
+ // removeSide(2);
+ }
+ progress.completeSubtask();
+ progress.setSubtaskWork(2, "Removing Triangles and Vertices");
+
+ removeTrisAndVerts();
+ }
+ }
+ progress.completeSubtask();
+
+ return true;
+}
+
+
+
+void ApexIsoMesh::getTriangle(uint32_t index, uint32_t& v0, uint32_t& v1, uint32_t& v2) const
+{
+ PX_ASSERT(index < mIsoTriangles.size());
+ const IsoTriangle& t = mIsoTriangles[index];
+ v0 = (uint32_t)t.vertexNr[0];
+ v1 = (uint32_t)t.vertexNr[1];
+ v2 = (uint32_t)t.vertexNr[2];
+}
+
+
+
+bool ApexIsoMesh::generateMesh(IProgressListener* progressListener)
+{
+ mIsoVertices.clear();
+
+ int xi, yi, zi;
+ float h = mCellSize;
+ PxVec3 pos, vPos;
+
+ uint32_t maximum = (uint32_t)(mNumX * mNumY * mNumZ);
+ uint32_t progressCounter = 0;
+
+ HierarchicalProgressListener progress(100, progressListener);
+ progress.setSubtaskWork(80, "Generating Vertices");
+
+ // generate vertices
+ for (xi = 0; xi < mNumX; xi++)
+ {
+ pos.x = mOrigin.x + h * xi;
+ for (yi = 0; yi < mNumY; yi++)
+ {
+ pos.y = mOrigin.y + h * yi;
+ for (zi = 0; zi < mNumZ; zi++)
+ {
+
+ if ((progressCounter++ & 0xff) == 0)
+ {
+ const int32_t curr = ((xi * mNumY) + yi) * mNumZ + zi;
+ const int32_t percent = 100 * curr / (int32_t)maximum;
+ progress.setProgress(percent);
+ }
+
+ pos.z = mOrigin.z + h * zi;
+ IsoCell& cell = cellAt(xi, yi, zi);
+ float d = cell.density;
+ if (xi < mNumX - 1)
+ {
+ float dx = cellAt(xi + 1, yi, zi).density;
+ if (interpolate(d, dx, pos, pos + PxVec3(h, 0.0f, 0.0f), vPos))
+ {
+ cell.vertNrX = (int32_t)mIsoVertices.size();
+ mIsoVertices.pushBack(vPos);
+ }
+ }
+ if (yi < mNumY - 1)
+ {
+ float dy = cellAt(xi, yi + 1, zi).density;
+ if (interpolate(d, dy, pos, pos + PxVec3(0.0f, h, 0.0f), vPos))
+ {
+ cell.vertNrY = (int32_t)mIsoVertices.size();
+ mIsoVertices.pushBack(vPos);
+ }
+ }
+ if (zi < mNumZ - 1)
+ {
+ float dz = cellAt(xi, yi, zi + 1).density;
+ if (interpolate(d, dz, pos, pos + PxVec3(0.0f, 0.0f, h), vPos))
+ {
+ cell.vertNrZ = (int32_t)mIsoVertices.size();
+ mIsoVertices.pushBack(vPos);
+ }
+ }
+ }
+ }
+ }
+
+ progress.completeSubtask();
+ progress.setSubtaskWork(20, "Generating Faces");
+ progressCounter = 0;
+
+ mIsoTriangles.clear();
+ // generate triangles
+ int edges[12];
+ IsoTriangle triangle;
+
+ for (xi = 0; xi < mNumX - 1; xi++)
+ {
+ pos.x = mOrigin.x + h * xi;
+ for (yi = 0; yi < mNumY - 1; yi++)
+ {
+ pos.y = mOrigin.y + h * yi;
+ for (zi = 0; zi < mNumZ - 1; zi++)
+ {
+
+ if ((progressCounter++ & 0xff) == 0)
+ {
+ const int32_t curr = ((xi * mNumY) + yi) * mNumZ + zi;
+ const int32_t percent = 100 * curr / (int32_t)maximum;
+ progress.setProgress(percent);
+ }
+ pos.z = mOrigin.z + h * zi;
+ int code = 0;
+ if (cellAt(xi, yi, zi).density > mIsoValue)
+ {
+ code |= 1;
+ }
+ if (cellAt(xi + 1, yi, zi).density > mIsoValue)
+ {
+ code |= 2;
+ }
+ if (cellAt(xi + 1, yi + 1, zi).density > mIsoValue)
+ {
+ code |= 4;
+ }
+ if (cellAt(xi, yi + 1, zi).density > mIsoValue)
+ {
+ code |= 8;
+ }
+ if (cellAt(xi, yi, zi + 1).density > mIsoValue)
+ {
+ code |= 16;
+ }
+ if (cellAt(xi + 1, yi, zi + 1).density > mIsoValue)
+ {
+ code |= 32;
+ }
+ if (cellAt(xi + 1, yi + 1, zi + 1).density > mIsoValue)
+ {
+ code |= 64;
+ }
+ if (cellAt(xi, yi + 1, zi + 1).density > mIsoValue)
+ {
+ code |= 128;
+ }
+ if (code == 0 || code == 255)
+ {
+ continue;
+ }
+
+ edges[ 0] = cellAt(xi, yi, zi).vertNrX;
+ edges[ 1] = cellAt(xi + 1, yi, zi).vertNrY;
+ edges[ 2] = cellAt(xi, yi + 1, zi).vertNrX;
+ edges[ 3] = cellAt(xi, yi, zi).vertNrY;
+
+ edges[ 4] = cellAt(xi, yi, zi + 1).vertNrX;
+ edges[ 5] = cellAt(xi + 1, yi, zi + 1).vertNrY;
+ edges[ 6] = cellAt(xi, yi + 1, zi + 1).vertNrX;
+ edges[ 7] = cellAt(xi, yi, zi + 1).vertNrY;
+
+ edges[ 8] = cellAt(xi, yi, zi).vertNrZ;
+ edges[ 9] = cellAt(xi + 1, yi, zi).vertNrZ;
+ edges[10] = cellAt(xi + 1, yi + 1, zi).vertNrZ;
+ edges[11] = cellAt(xi, yi + 1, zi).vertNrZ;
+
+ IsoCell& c = cellAt(xi, yi, zi);
+ c.firstTriangle = (int32_t)mIsoTriangles.size();
+
+ const int* v = MarchingCubes::triTable[code];
+ while (*v >= 0)
+ {
+ int v0 = edges[*v++];
+ PX_ASSERT(v0 >= 0);
+ int v1 = edges[*v++];
+ PX_ASSERT(v1 >= 0);
+ int v2 = edges[*v++];
+ PX_ASSERT(v2 >= 0);
+ triangle.set(v0, v1, v2, xi, yi, zi);
+ mIsoTriangles.pushBack(triangle);
+ c.numTriangles++;
+ }
+ }
+ }
+ }
+ progress.completeSubtask();
+
+ return true;
+}
+
+
+
+bool ApexIsoMesh::interpolate(float d0, float d1, const PxVec3& pos0, const PxVec3& pos1, PxVec3& pos)
+{
+ if ((d0 < mIsoValue && d1 < mIsoValue) || (d0 > mIsoValue && d1 > mIsoValue))
+ {
+ return false;
+ }
+
+ float s = (mIsoValue - d0) / (d1 - d0);
+ s = PxClamp(s, 0.01f, 0.99f); // safety not to produce vertices at the same location
+ pos = pos0 * (1.0f - s) + pos1 * s;
+ return true;
+}
+
+
+
+bool ApexIsoMesh::findNeighbors(IProgressListener* progress)
+{
+ mIsoEdges.clear();
+ IsoEdge edge;
+
+ for (int32_t i = 0; i < (int32_t)mIsoTriangles.size(); i++)
+ {
+ IsoTriangle& t = mIsoTriangles[(uint32_t)i];
+ edge.set(t.vertexNr[0], t.vertexNr[1], i);
+ mIsoEdges.pushBack(edge);
+ edge.set(t.vertexNr[1], t.vertexNr[2], i);
+ mIsoEdges.pushBack(edge);
+ edge.set(t.vertexNr[2], t.vertexNr[0], i);
+ mIsoEdges.pushBack(edge);
+ }
+
+ progress->setProgress(30);
+
+ shdfnd::sort(mIsoEdges.begin(), mIsoEdges.size(), IsoEdge());
+
+ progress->setProgress(60);
+
+ uint32_t i = 0;
+ while (i < mIsoEdges.size())
+ {
+ uint32_t j = i + 1;
+ while (j < mIsoEdges.size() && mIsoEdges[j] == mIsoEdges[i])
+ {
+ j++;
+ }
+ if (j > i + 1)
+ {
+ mIsoTriangles[(uint32_t)mIsoEdges[i ].triangleNr].addNeighbor(mIsoEdges[j - 1].triangleNr);
+ mIsoTriangles[(uint32_t)mIsoEdges[j - 1].triangleNr].addNeighbor(mIsoEdges[i ].triangleNr);
+ }
+ i = j;
+ }
+
+ progress->setProgress(100);
+ return true;
+}
+
+struct GroupAndSize
+{
+ int32_t group;
+ uint32_t size;
+ bool deleteMesh;
+
+ bool operator()(const GroupAndSize& a, const GroupAndSize& b) const
+ {
+ return a.size > b.size;
+ }
+};
+
+struct TimeAndDot
+{
+ float time;
+ float dot;
+
+ bool operator()(const TimeAndDot& a, const TimeAndDot& b) const
+ {
+ return a.time > b.time;
+ }
+};
+
+void ApexIsoMesh::removeLayers()
+{
+ // mark regions
+ nvidia::Array<GroupAndSize> triangleGroups;
+
+ for (uint32_t i = 0; i < mIsoTriangles.size(); i++)
+ {
+ if (mIsoTriangles[i].groupNr >= 0)
+ {
+ continue;
+ }
+
+ GroupAndSize gas;
+ gas.size = 0;
+ gas.group = (int32_t)triangleGroups.size();
+ gas.deleteMesh = false;
+
+ gas.size = floodFill(i, (uint32_t)gas.group);
+
+ triangleGroups.pushBack(gas);
+ }
+
+ if (triangleGroups.size() == 0)
+ {
+ return;
+ }
+
+ // clear regions
+ if (mDiscardInnerMeshes)
+ {
+ nvidia::Array<TimeAndDot> raycastHits;
+ for (uint32_t group = 0; group < triangleGroups.size(); group++)
+ {
+ // see if this group is an inner mesh
+ raycastHits.clear();
+
+ const int32_t groupNumber = triangleGroups[group].group;
+
+ uint32_t start = 0;
+ const uint32_t numTriangles = mIsoTriangles.size();
+ for (uint32_t i = 0; i < numTriangles; i++)
+ {
+ if (mIsoTriangles[i].groupNr == groupNumber)
+ {
+ start = i;
+ break;
+ }
+ }
+
+ // raycast from the start triangle
+ const PxVec3 rayOrig = (mIsoVertices[(uint32_t)mIsoTriangles[start].vertexNr[0]]
+ + mIsoVertices[(uint32_t)mIsoTriangles[start].vertexNr[1]]
+ + mIsoVertices[(uint32_t)mIsoTriangles[start].vertexNr[2]]) / 3.0f;
+ PxVec3 rayDir(1.0f, 1.0f, 1.0f);
+ rayDir.normalize(); // can really be anything.
+
+ // Find all ray hits
+ for (uint32_t i = start; i < numTriangles; i++)
+ {
+ if (mIsoTriangles[i].groupNr != groupNumber)
+ {
+ continue;
+ }
+
+ float t, u, v;
+ PxVec3 verts[3] =
+ {
+ mIsoVertices[(uint32_t)mIsoTriangles[i].vertexNr[0]],
+ mIsoVertices[(uint32_t)mIsoTriangles[i].vertexNr[1]],
+ mIsoVertices[(uint32_t)mIsoTriangles[i].vertexNr[2]],
+ };
+
+ if (APEX_RayTriangleIntersect(rayOrig, rayDir, verts[0], verts[1], verts[2], t, u, v))
+ {
+ TimeAndDot hit;
+ hit.time = t;
+
+ PxVec3 faceNormal = (verts[1] - verts[0]).cross(verts[2] - verts[0]);
+ faceNormal.normalize();
+
+ hit.dot = faceNormal.dot(rayDir);
+
+ raycastHits.pushBack(hit);
+ }
+ }
+
+ if (raycastHits.size() > 0)
+ {
+ shdfnd::sort(raycastHits.begin(), raycastHits.size(), TimeAndDot());
+
+ triangleGroups[group].deleteMesh = raycastHits[0].dot > 0.0f;
+ }
+ }
+ }
+
+ if (mKeepNBiggestMeshes > 0)
+ {
+ shdfnd::sort(triangleGroups.begin(), triangleGroups.size(), GroupAndSize());
+
+ for (uint32_t i = mKeepNBiggestMeshes; i < triangleGroups.size(); i++)
+ {
+ triangleGroups[i].deleteMesh = true;
+ }
+ }
+
+
+ for (uint32_t i = 0; i < triangleGroups.size(); i++)
+ {
+ if (!triangleGroups[i].deleteMesh)
+ {
+ continue;
+ }
+
+ const int32_t group = triangleGroups[i].group;
+
+ const uint32_t size = mIsoTriangles.size();
+ for (uint32_t j = 0; j < size; j++)
+ {
+ if (mIsoTriangles[j].groupNr == group)
+ {
+ mIsoTriangles[j].deleted = true;
+ }
+ }
+ }
+}
+
+
+
+uint32_t ApexIsoMesh::floodFill(uint32_t triangleNr, uint32_t groupNr)
+{
+ uint32_t numTriangles = 0;
+ nvidia::Array<uint32_t> queue;
+ queue.pushBack(triangleNr);
+
+ while (!queue.empty())
+ {
+ IsoTriangle& t = mIsoTriangles[queue.back()];
+ queue.popBack();
+ if (t.groupNr == int32_t(groupNr))
+ {
+ continue;
+ }
+
+ t.groupNr = (int32_t)groupNr;
+ numTriangles++;
+
+ int32_t adj = t.adjTriangles[0];
+ if (adj >= 0 && mIsoTriangles[(uint32_t)adj].groupNr < 0)
+ {
+ queue.pushBack((uint32_t)adj);
+ }
+ adj = t.adjTriangles[1];
+ if (adj >= 0 && mIsoTriangles[(uint32_t)adj].groupNr < 0)
+ {
+ queue.pushBack((uint32_t)adj);
+ }
+ adj = t.adjTriangles[2];
+ if (adj >= 0 && mIsoTriangles[(uint32_t)adj].groupNr < 0)
+ {
+ queue.pushBack((uint32_t)adj);
+ }
+ }
+
+ return numTriangles;
+}
+
+
+
+void ApexIsoMesh::removeTrisAndVerts()
+{
+ for (int32_t i = (int32_t)mIsoTriangles.size() - 1; i >= 0; i--)
+ {
+ if (mIsoTriangles[(uint32_t)i].deleted)
+ {
+ mIsoTriangles.replaceWithLast((uint32_t)i);
+ }
+ }
+
+ Array<int32_t> oldToNew;
+ Array<PxVec3> newVertices;
+
+ oldToNew.resize(mIsoVertices.size(), -1);
+
+ for (uint32_t i = 0; i < mIsoTriangles.size(); i++)
+ {
+ IsoTriangle& t = mIsoTriangles[i];
+ for (uint32_t j = 0; j < 3; j++)
+ {
+ uint32_t vNr = (uint32_t)t.vertexNr[j];
+ if (oldToNew[vNr] < 0)
+ {
+ oldToNew[vNr] = (int32_t)newVertices.size();
+ newVertices.pushBack(mIsoVertices[vNr]);
+ }
+ t.vertexNr[j] = oldToNew[vNr];
+ }
+ }
+
+ mIsoVertices.clear();
+ mIsoVertices.reserve(newVertices.size());
+
+ for (uint32_t i = 0; i < newVertices.size(); i++)
+ {
+ mIsoVertices.pushBack(newVertices[i]);
+ }
+}
+
+} // end namespace apex
+} // end namespace nvidia