// // 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-2019 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 PX_COLLISION_GRBTRIANGLEMESHCOOKING #define PX_COLLISION_GRBTRIANGLEMESHCOOKING #include "GuMeshData.h" #include "cooking/PxCooking.h" namespace physx { namespace Gu { } // TODO avoroshilov: remove duplicate definitions static const PxU32 BOUNDARY = 0xffffffff; static const PxU32 NONCONVEX_FLAG = 0x80000000; struct EdgeTriLookup { PxU32 edgeId0, edgeId1; PxU32 triId; bool operator < (const EdgeTriLookup& edge1) const { return edgeId0 < edge1.edgeId0 || (edgeId0 == edge1.edgeId0 && edgeId1 < edge1.edgeId1); } bool operator <=(const EdgeTriLookup& edge1) const { return edgeId0 < edge1.edgeId0 || (edgeId0 == edge1.edgeId0 && edgeId1 <= edge1.edgeId1); } }; static PxU32 binarySearch(const EdgeTriLookup* __restrict data, const PxU32 numElements, const EdgeTriLookup& value) { PxU32 left = 0; PxU32 right = numElements; while ((right - left) > 1) { PxU32 pos = (left + right) / 2; const EdgeTriLookup& element = data[pos]; if (element <= value) { left = pos; } else { right = pos; } } return left; } // slightly different behavior from collide2: boundary edges are filtered out static PxU32 findAdjacent(const PxVec3* triVertices, const PxVec3* triNormals, const uint3* triIndices, PxU32 nbTris, PxU32 i0, PxU32 i1, const PxPlane& plane, EdgeTriLookup* triLookups, PxU32 triangleIndex) { PxU32 result = BOUNDARY; PxReal bestCos = -FLT_MAX; EdgeTriLookup lookup; lookup.edgeId0 = PxMin(i0, i1); lookup.edgeId1 = PxMax(i0, i1); PxU32 startIndex = binarySearch(triLookups, nbTris * 3, lookup); for (PxU32 a = startIndex; a > 0; --a) { if (triLookups[a - 1].edgeId0 == lookup.edgeId0 && triLookups[a - 1].edgeId1 == lookup.edgeId1) startIndex = a - 1; else break; } for (PxU32 a = startIndex; a < nbTris * 3; ++a) { EdgeTriLookup& edgeTri = triLookups[a]; if (edgeTri.edgeId0 != lookup.edgeId0 || edgeTri.edgeId1 != lookup.edgeId1) break; if (edgeTri.triId == triangleIndex) continue; const uint3& triIdx = triIndices[edgeTri.triId]; PxU32 vIdx0 = triIdx.x; PxU32 vIdx1 = triIdx.y; PxU32 vIdx2 = triIdx.z; PxU32 other = vIdx0 + vIdx1 + vIdx2 - (i0 + i1); if (plane.distance(triVertices[other]) >= 0) return NONCONVEX_FLAG | edgeTri.triId; PxReal c = plane.n.dot(triNormals[edgeTri.triId]); if (c>bestCos) { bestCos = c; result = edgeTri.triId; } } return result; } static void buildAdjacencies(uint4* triAdjacencies, PxVec3* tempNormalsPerTri_prealloc, const PxVec3* triVertices, const uint3* triIndices, PxU32 nbTris) { //PxVec3 * triNormals = new PxVec3[nbTris]; EdgeTriLookup* edgeLookups = reinterpret_cast(PX_ALLOC(sizeof(EdgeTriLookup) * nbTris * 3, PX_DEBUG_EXP("edgeLookups"))); for (PxU32 i = 0; i < nbTris; i++) { const uint3& triIdx = triIndices[i]; PxU32 vIdx0 = triIdx.x; PxU32 vIdx1 = triIdx.y; PxU32 vIdx2 = triIdx.z; tempNormalsPerTri_prealloc[i] = (triVertices[vIdx1] - triVertices[vIdx0]).cross(triVertices[vIdx2] - triVertices[vIdx0]).getNormalized(); edgeLookups[i * 3].edgeId0 = PxMin(vIdx0, vIdx1); edgeLookups[i * 3].edgeId1 = PxMax(vIdx0, vIdx1); edgeLookups[i * 3].triId = i; edgeLookups[i * 3 + 1].edgeId0 = PxMin(vIdx1, vIdx2); edgeLookups[i * 3 + 1].edgeId1 = PxMax(vIdx1, vIdx2); edgeLookups[i * 3 + 1].triId = i; edgeLookups[i * 3 + 2].edgeId0 = PxMin(vIdx0, vIdx2); edgeLookups[i * 3 + 2].edgeId1 = PxMax(vIdx0, vIdx2); edgeLookups[i * 3 + 2].triId = i; } Ps::sort(edgeLookups, PxU32(nbTris * 3)); for (PxU32 i = 0; i < nbTris; i++) { const uint3& triIdx = triIndices[i]; PxU32 vIdx0 = triIdx.x; PxU32 vIdx1 = triIdx.y; PxU32 vIdx2 = triIdx.z; PxPlane triPlane(triVertices[vIdx0], tempNormalsPerTri_prealloc[i]); uint4 triAdjIdx; triAdjIdx.x = findAdjacent(triVertices, tempNormalsPerTri_prealloc, triIndices, nbTris, vIdx0, vIdx1, triPlane, edgeLookups, i); triAdjIdx.y = findAdjacent(triVertices, tempNormalsPerTri_prealloc, triIndices, nbTris, vIdx1, vIdx2, triPlane, edgeLookups, i); triAdjIdx.z = findAdjacent(triVertices, tempNormalsPerTri_prealloc, triIndices, nbTris, vIdx2, vIdx0, triPlane, edgeLookups, i); triAdjIdx.w = 0; triAdjacencies[i] = triAdjIdx; } PX_FREE(edgeLookups); } static bool isEdgeNonconvex(PxU32 adjEdgeIndex) { return (adjEdgeIndex != BOUNDARY) && (adjEdgeIndex & NONCONVEX_FLAG); } PX_INLINE void buildVertexConnection_p1(PxU32 * vertValency, PxU32 * vertNeighborStart, PxU32 & tempNumAdjVertices, const float4 * /*triVertices*/, const uint4 * triIndices, const uint4 * triAdjacencies, PxU32 nbVerts, PxU32 nbTris) { tempNumAdjVertices = 0; memset(vertValency, 0, nbVerts*sizeof(PxU32)); // Calculate max num of adjVerts for (PxU32 i = 0; i < nbTris; i++) { uint4 triIdx = triIndices[i]; PxU32 vi0 = triIdx.x; PxU32 vi1 = triIdx.y; PxU32 vi2 = triIdx.z; uint4 triAdjIdx = triAdjacencies[i]; PxU32 totalVertsAdded = 0; if (!isEdgeNonconvex(triAdjIdx.x)) { ++vertValency[vi0]; ++vertValency[vi1]; totalVertsAdded += 2; } if (!isEdgeNonconvex(triAdjIdx.y)) { ++vertValency[vi1]; ++vertValency[vi2]; totalVertsAdded += 2; } if (!isEdgeNonconvex(triAdjIdx.z)) { ++vertValency[vi2]; ++vertValency[vi0]; totalVertsAdded += 2; } tempNumAdjVertices += totalVertsAdded; } PxU32 offset = 0; for (PxU32 i = 0; i < nbVerts; i++) { vertNeighborStart[i] = offset; offset += vertValency[i]; } memset(vertValency, 0, nbVerts*sizeof(PxU32)); } PX_INLINE PxU32 buildVertexConnection_p2(PxU32 * vertValency, PxU32 * vertNeighborStart, PxU32 * vertNeighboringPairs_prealloc, PxU32 tempNumAdjVertices, const float4 * /*triVertices*/, const uint4 * triIndices, const uint4 * triAdjacencies, PxU32 /*nbVerts*/, PxU32 nbTris) { memset(vertNeighboringPairs_prealloc, 0xff, tempNumAdjVertices*2*sizeof(PxU32)); PxU32 newAdjVertsNum = 0; for (PxU32 i = 0; i < nbTris; i++) { uint4 triIdx = triIndices[i]; PxU32 vi[3] = { triIdx.x, triIdx.y, triIdx.z }; uint4 triAdjIdx = triAdjacencies[i]; PxU32 ta[3] = { triAdjIdx.x, triAdjIdx.y, triAdjIdx.z }; for (int tvi = 0; tvi < 3; ++tvi) { PxU32 curIdx = vi[tvi]; PxU32 nextIdx = vi[(tvi+1)%3]; if (!isEdgeNonconvex(ta[tvi])) { bool matchFound = false; for (PxU32 valIdx = vertNeighborStart[curIdx], valIdxEnd = vertNeighborStart[curIdx] + vertValency[curIdx]; valIdx < valIdxEnd; ++valIdx) { if (vertNeighboringPairs_prealloc[valIdx*2+1] == nextIdx) { matchFound = true; break; } } if (!matchFound) { PxU32 curPairIdx; curPairIdx = vertNeighborStart[curIdx] + vertValency[curIdx]; vertNeighboringPairs_prealloc[curPairIdx*2+0] = curIdx; vertNeighboringPairs_prealloc[curPairIdx*2+1] = nextIdx; ++vertValency[curIdx]; curPairIdx = vertNeighborStart[nextIdx] + vertValency[nextIdx]; vertNeighboringPairs_prealloc[curPairIdx*2+0] = nextIdx; vertNeighboringPairs_prealloc[curPairIdx*2+1] = curIdx; ++vertValency[nextIdx]; newAdjVertsNum += 2; } } } } return newAdjVertsNum; } PX_INLINE void buildVertexConnection_p3(PxU32 * vertNeighbors, PxU32 * /*vertValency*/, PxU32 * vertNeighborStart, PxU32 * vertNeighboringPairs_prealloc, PxU32 tempNumAdjVertices, PxU32 newNumAdjVertices, const float4 * /*triVertices*/, const uint4 * /*triIndices*/, const uint4 * /*triAdjacencies*/, PxU32 /*nbVerts*/, PxU32 /*nbTris*/) { PX_UNUSED(newNumAdjVertices); PxU32 prevVertex = 0xFFffFFff; PxU32 writingIndex = 0; for (PxU32 i = 0; i < tempNumAdjVertices; ++i) { PxU32 curPairIdx0 = vertNeighboringPairs_prealloc[i*2+0]; if (curPairIdx0 == 0xFFffFFff) { continue; } PxU32 curPairIdx1 = vertNeighboringPairs_prealloc[i*2+1]; vertNeighbors[writingIndex] = curPairIdx1; if (curPairIdx0 != prevVertex) { vertNeighborStart[curPairIdx0] = writingIndex; } prevVertex = curPairIdx0; ++writingIndex; } PX_ASSERT(writingIndex == newNumAdjVertices); } } // namespace physx #endif