diff options
| author | Bryan Galdrikian <[email protected]> | 2017-02-21 12:07:59 -0800 |
|---|---|---|
| committer | Bryan Galdrikian <[email protected]> | 2017-02-21 12:07:59 -0800 |
| commit | 446ce137c6823ba9eff273bdafdaf266287c7c98 (patch) | |
| tree | d20aab3e2ed08d7b3ca71c2f40db6a93ea00c459 /NvBlast/sdk/extensions/authoring/source | |
| download | blast-1.0.0-beta.tar.xz blast-1.0.0-beta.zip | |
first commitv1.0.0-beta
Diffstat (limited to 'NvBlast/sdk/extensions/authoring/source')
17 files changed, 9808 insertions, 0 deletions
diff --git a/NvBlast/sdk/extensions/authoring/source/NvBlastExtApexSharedParts.cpp b/NvBlast/sdk/extensions/authoring/source/NvBlastExtApexSharedParts.cpp new file mode 100644 index 0000000..73c59b8 --- /dev/null +++ b/NvBlast/sdk/extensions/authoring/source/NvBlastExtApexSharedParts.cpp @@ -0,0 +1,1004 @@ +/* +* Copyright (c) 2016-2017, 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 "NvBlastExtApexSharedParts.h" + +#include "PxMat44.h" +#include "PxBounds3.h" +#include "PxFoundation.h" +#include "PxPhysics.h" +#include "PsVecMath.h" +#include <vector> + +using namespace physx; +using namespace physx::shdfnd::aos; + + +namespace Nv +{ +namespace Blast +{ + +PX_NOALIAS PX_FORCE_INLINE BoolV PointOutsideOfPlane4(const Vec3VArg _a, const Vec3VArg _b, const Vec3VArg _c, const Vec3VArg _d) +{ + // this is not 0 because of the following scenario: + // All the points lie on the same plane and the plane goes through the origin (0,0,0). + // On the Wii U, the math below has the problem that when point A gets projected on the + // plane cumputed by A, B, C, the distance to the plane might not be 0 for the mentioned + // scenario but a small positive or negative value. This can lead to the wrong boolean + // results. Using a small negative value as threshold is more conservative but safer. + const Vec4V zero = V4Load(-1e-6); + + const Vec3V ab = V3Sub(_b, _a); + const Vec3V ac = V3Sub(_c, _a); + const Vec3V ad = V3Sub(_d, _a); + const Vec3V bd = V3Sub(_d, _b); + const Vec3V bc = V3Sub(_c, _b); + + const Vec3V v0 = V3Cross(ab, ac); + const Vec3V v1 = V3Cross(ac, ad); + const Vec3V v2 = V3Cross(ad, ab); + const Vec3V v3 = V3Cross(bd, bc); + + const FloatV signa0 = V3Dot(v0, _a); + const FloatV signa1 = V3Dot(v1, _a); + const FloatV signa2 = V3Dot(v2, _a); + const FloatV signd3 = V3Dot(v3, _a); + + const FloatV signd0 = V3Dot(v0, _d); + const FloatV signd1 = V3Dot(v1, _b); + const FloatV signd2 = V3Dot(v2, _c); + const FloatV signa3 = V3Dot(v3, _b); + + const Vec4V signa = V4Merge(signa0, signa1, signa2, signa3); + const Vec4V signd = V4Merge(signd0, signd1, signd2, signd3); + return V4IsGrtrOrEq(V4Mul(signa, signd), zero);//same side, outside of the plane +} + +PX_NOALIAS PX_FORCE_INLINE Vec3V closestPtPointSegment(const Vec3VArg a, const Vec3VArg b) +{ + const FloatV zero = FZero(); + const FloatV one = FOne(); + + //Test degenerated case + const Vec3V ab = V3Sub(b, a); + const FloatV denom = V3Dot(ab, ab); + const Vec3V ap = V3Neg(a);//V3Sub(origin, a); + const FloatV nom = V3Dot(ap, ab); + const BoolV con = FIsEq(denom, zero); + const FloatV tValue = FClamp(FDiv(nom, denom), zero, one); + const FloatV t = FSel(con, zero, tValue); + + return V3Sel(con, a, V3ScaleAdd(ab, t, a)); +} + +PX_NOALIAS PX_FORCE_INLINE Vec3V closestPtPointSegment(const Vec3VArg Q0, const Vec3VArg Q1, const Vec3VArg A0, const Vec3VArg A1, + const Vec3VArg B0, const Vec3VArg B1, PxU32& size, Vec3V& closestA, Vec3V& closestB) +{ + const Vec3V a = Q0; + const Vec3V b = Q1; + + const BoolV bTrue = BTTTT(); + const FloatV zero = FZero(); + const FloatV one = FOne(); + + //Test degenerated case + const Vec3V ab = V3Sub(b, a); + const FloatV denom = V3Dot(ab, ab); + const Vec3V ap = V3Neg(a);//V3Sub(origin, a); + const FloatV nom = V3Dot(ap, ab); + const BoolV con = FIsEq(denom, zero); + + if (BAllEq(con, bTrue)) + { + size = 1; + closestA = A0; + closestB = B0; + return Q0; + } + + const Vec3V v = V3Sub(A1, A0); + const Vec3V w = V3Sub(B1, B0); + const FloatV tValue = FClamp(FDiv(nom, denom), zero, one); + const FloatV t = FSel(con, zero, tValue); + + const Vec3V tempClosestA = V3ScaleAdd(v, t, A0); + const Vec3V tempClosestB = V3ScaleAdd(w, t, B0); + closestA = tempClosestA; + closestB = tempClosestB; + return V3Sub(tempClosestA, tempClosestB); +} + +PX_NOALIAS Vec3V closestPtPointSegmentTesselation(const Vec3VArg Q0, const Vec3VArg Q1, const Vec3VArg A0, const Vec3VArg A1, + const Vec3VArg B0, const Vec3VArg B1, PxU32& size, Vec3V& closestA, Vec3V& closestB) +{ + const FloatV half = FHalf(); + + const FloatV targetSegmentLengthSq = FLoad(10000.f);//100 unit + + Vec3V q0 = Q0; + Vec3V q1 = Q1; + Vec3V a0 = A0; + Vec3V a1 = A1; + Vec3V b0 = B0; + Vec3V b1 = B1; + + for (;;) + { + const Vec3V midPoint = V3Scale(V3Add(q0, q1), half); + const Vec3V midA = V3Scale(V3Add(a0, a1), half); + const Vec3V midB = V3Scale(V3Add(b0, b1), half); + + const Vec3V v = V3Sub(midPoint, q0); + const FloatV sqV = V3Dot(v, v); + if (FAllGrtr(targetSegmentLengthSq, sqV)) + break; + //split the segment into half + const Vec3V tClos0 = closestPtPointSegment(q0, midPoint); + const FloatV sqDist0 = V3Dot(tClos0, tClos0); + + const Vec3V tClos1 = closestPtPointSegment(q1, midPoint); + const FloatV sqDist1 = V3Dot(tClos1, tClos1); + //const BoolV con = FIsGrtr(sqDist0, sqDist1); + if (FAllGrtr(sqDist0, sqDist1)) + { + //segment [m, q1] + q0 = midPoint; + a0 = midA; + b0 = midB; + } + else + { + //segment [q0, m] + q1 = midPoint; + a1 = midA; + b1 = midB; + } + + } + + return closestPtPointSegment(q0, q1, a0, a1, b0, b1, size, closestA, closestB); +} + +PX_NOALIAS Vec3V closestPtPointTriangleTesselation(const Vec3V* PX_RESTRICT Q, const Vec3V* PX_RESTRICT A, const Vec3V* PX_RESTRICT B, const PxU32* PX_RESTRICT indices, PxU32& size, Vec3V& closestA, Vec3V& closestB) +{ + size = 3; + const FloatV zero = FZero(); + const FloatV eps = FEps(); + const FloatV half = FHalf(); + const BoolV bTrue = BTTTT(); + const FloatV four = FLoad(4.f); + const FloatV sixty = FLoad(100.f); + + const PxU32 ind0 = indices[0]; + const PxU32 ind1 = indices[1]; + const PxU32 ind2 = indices[2]; + + const Vec3V a = Q[ind0]; + const Vec3V b = Q[ind1]; + const Vec3V c = Q[ind2]; + + Vec3V ab_ = V3Sub(b, a); + Vec3V ac_ = V3Sub(c, a); + Vec3V bc_ = V3Sub(b, c); + + const FloatV dac_ = V3Dot(ac_, ac_); + const FloatV dbc_ = V3Dot(bc_, bc_); + if (FAllGrtrOrEq(eps, FMin(dac_, dbc_))) + { + //degenerate + size = 2; + return closestPtPointSegment(Q[ind0], Q[ind1], A[ind0], A[ind1], B[ind0], B[ind1], size, closestA, closestB); + } + + Vec3V ap = V3Neg(a); + Vec3V bp = V3Neg(b); + Vec3V cp = V3Neg(c); + + FloatV d1 = V3Dot(ab_, ap); // snom + FloatV d2 = V3Dot(ac_, ap); // tnom + FloatV d3 = V3Dot(ab_, bp); // -sdenom + FloatV d4 = V3Dot(ac_, bp); // unom = d4 - d3 + FloatV d5 = V3Dot(ab_, cp); // udenom = d5 - d6 + FloatV d6 = V3Dot(ac_, cp); // -tdenom + /* FloatV unom = FSub(d4, d3); + FloatV udenom = FSub(d5, d6);*/ + + FloatV va = FNegScaleSub(d5, d4, FMul(d3, d6));//edge region of BC + FloatV vb = FNegScaleSub(d1, d6, FMul(d5, d2));//edge region of AC + FloatV vc = FNegScaleSub(d3, d2, FMul(d1, d4));//edge region of AB + + //check if p in vertex region outside a + const BoolV con00 = FIsGrtrOrEq(zero, d1); // snom <= 0 + const BoolV con01 = FIsGrtrOrEq(zero, d2); // tnom <= 0 + const BoolV con0 = BAnd(con00, con01); // vertex region a + if (BAllEq(con0, bTrue)) + { + //size = 1; + closestA = A[ind0]; + closestB = B[ind0]; + return Q[ind0]; + } + + //check if p in vertex region outside b + const BoolV con10 = FIsGrtrOrEq(d3, zero); + const BoolV con11 = FIsGrtrOrEq(d3, d4); + const BoolV con1 = BAnd(con10, con11); // vertex region b + if (BAllEq(con1, bTrue)) + { + /*size = 1; + indices[0] = ind1;*/ + closestA = A[ind1]; + closestB = B[ind1]; + return Q[ind1]; + } + + + //check if p in vertex region outside of c + const BoolV con20 = FIsGrtrOrEq(d6, zero); + const BoolV con21 = FIsGrtrOrEq(d6, d5); + const BoolV con2 = BAnd(con20, con21); // vertex region c + if (BAllEq(con2, bTrue)) + { + closestA = A[ind2]; + closestB = B[ind2]; + return Q[ind2]; + } + + //check if p in edge region of AB + const BoolV con30 = FIsGrtrOrEq(zero, vc); + const BoolV con31 = FIsGrtrOrEq(d1, zero); + const BoolV con32 = FIsGrtrOrEq(zero, d3); + const BoolV con3 = BAnd(con30, BAnd(con31, con32)); + + if (BAllEq(con3, bTrue)) + { + //size = 2; + //p in edge region of AB, split AB + return closestPtPointSegmentTesselation(Q[ind0], Q[ind1], A[ind0], A[ind1], B[ind0], B[ind1], size, closestA, closestB); + } + + //check if p in edge region of BC + const BoolV con40 = FIsGrtrOrEq(zero, va); + const BoolV con41 = FIsGrtrOrEq(d4, d3); + const BoolV con42 = FIsGrtrOrEq(d5, d6); + const BoolV con4 = BAnd(con40, BAnd(con41, con42)); + + if (BAllEq(con4, bTrue)) + { + //p in edge region of BC, split BC + return closestPtPointSegmentTesselation(Q[ind1], Q[ind2], A[ind1], A[ind2], B[ind1], B[ind2], size, closestA, closestB); + } + + //check if p in edge region of AC + const BoolV con50 = FIsGrtrOrEq(zero, vb); + const BoolV con51 = FIsGrtrOrEq(d2, zero); + const BoolV con52 = FIsGrtrOrEq(zero, d6); + const BoolV con5 = BAnd(con50, BAnd(con51, con52)); + + if (BAllEq(con5, bTrue)) + { + //p in edge region of AC, split AC + return closestPtPointSegmentTesselation(Q[ind0], Q[ind2], A[ind0], A[ind2], B[ind0], B[ind2], size, closestA, closestB); + } + + size = 3; + + Vec3V q0 = Q[ind0]; + Vec3V q1 = Q[ind1]; + Vec3V q2 = Q[ind2]; + Vec3V a0 = A[ind0]; + Vec3V a1 = A[ind1]; + Vec3V a2 = A[ind2]; + Vec3V b0 = B[ind0]; + Vec3V b1 = B[ind1]; + Vec3V b2 = B[ind2]; + + for (;;) + { + + const Vec3V ab = V3Sub(q1, q0); + const Vec3V ac = V3Sub(q2, q0); + const Vec3V bc = V3Sub(q2, q1); + + const FloatV dab = V3Dot(ab, ab); + const FloatV dac = V3Dot(ac, ac); + const FloatV dbc = V3Dot(bc, bc); + + const FloatV fMax = FMax(dab, FMax(dac, dbc)); + const FloatV fMin = FMin(dab, FMin(dac, dbc)); + + const Vec3V w = V3Cross(ab, ac); + + const FloatV area = V3Length(w); + const FloatV ratio = FDiv(FSqrt(fMax), FSqrt(fMin)); + if (FAllGrtr(four, ratio) && FAllGrtr(sixty, area)) + break; + + //calculate the triangle normal + const Vec3V triNormal = V3Normalize(w); + + PX_ASSERT(V3AllEq(triNormal, V3Zero()) == 0); + + + //split the longest edge + if (FAllGrtrOrEq(dab, dac) && FAllGrtrOrEq(dab, dbc)) + { + //split edge q0q1 + const Vec3V midPoint = V3Scale(V3Add(q0, q1), half); + const Vec3V midA = V3Scale(V3Add(a0, a1), half); + const Vec3V midB = V3Scale(V3Add(b0, b1), half); + + const Vec3V v = V3Sub(midPoint, q2); + const Vec3V n = V3Normalize(V3Cross(v, triNormal)); + + const FloatV d = FNeg(V3Dot(n, midPoint)); + const FloatV dp = FAdd(V3Dot(n, q0), d); + const FloatV sum = FMul(d, dp); + + if (FAllGrtr(sum, zero)) + { + //q0 and origin at the same side, split triangle[q0, m, q2] + q1 = midPoint; + a1 = midA; + b1 = midB; + } + else + { + //q1 and origin at the same side, split triangle[m, q1, q2] + q0 = midPoint; + a0 = midA; + b0 = midB; + } + + } + else if (FAllGrtrOrEq(dac, dbc)) + { + //split edge q0q2 + const Vec3V midPoint = V3Scale(V3Add(q0, q2), half); + const Vec3V midA = V3Scale(V3Add(a0, a2), half); + const Vec3V midB = V3Scale(V3Add(b0, b2), half); + + const Vec3V v = V3Sub(midPoint, q1); + const Vec3V n = V3Normalize(V3Cross(v, triNormal)); + + const FloatV d = FNeg(V3Dot(n, midPoint)); + const FloatV dp = FAdd(V3Dot(n, q0), d); + const FloatV sum = FMul(d, dp); + + if (FAllGrtr(sum, zero)) + { + //q0 and origin at the same side, split triangle[q0, q1, m] + q2 = midPoint; + a2 = midA; + b2 = midB; + } + else + { + //q2 and origin at the same side, split triangle[m, q1, q2] + q0 = midPoint; + a0 = midA; + b0 = midB; + } + } + else + { + //split edge q1q2 + const Vec3V midPoint = V3Scale(V3Add(q1, q2), half); + const Vec3V midA = V3Scale(V3Add(a1, a2), half); + const Vec3V midB = V3Scale(V3Add(b1, b2), half); + + const Vec3V v = V3Sub(midPoint, q0); + const Vec3V n = V3Normalize(V3Cross(v, triNormal)); + + const FloatV d = FNeg(V3Dot(n, midPoint)); + const FloatV dp = FAdd(V3Dot(n, q1), d); + const FloatV sum = FMul(d, dp); + + if (FAllGrtr(sum, zero)) + { + //q1 and origin at the same side, split triangle[q0, q1, m] + q2 = midPoint; + a2 = midA; + b2 = midB; + } + else + { + //q2 and origin at the same side, split triangle[q0, m, q2] + q1 = midPoint; + a1 = midA; + b1 = midB; + } + + + } + } + + //P must project inside face region. Compute Q using Barycentric coordinates + ab_ = V3Sub(q1, q0); + ac_ = V3Sub(q2, q0); + ap = V3Neg(q0); + bp = V3Neg(q1); + cp = V3Neg(q2); + + d1 = V3Dot(ab_, ap); // snom + d2 = V3Dot(ac_, ap); // tnom + d3 = V3Dot(ab_, bp); // -sdenom + d4 = V3Dot(ac_, bp); // unom = d4 - d3 + d5 = V3Dot(ab_, cp); // udenom = d5 - d6 + d6 = V3Dot(ac_, cp); // -tdenom + + va = FNegScaleSub(d5, d4, FMul(d3, d6));//edge region of BC + vb = FNegScaleSub(d1, d6, FMul(d5, d2));//edge region of AC + vc = FNegScaleSub(d3, d2, FMul(d1, d4));//edge region of AB + + const FloatV toRecipD = FAdd(va, FAdd(vb, vc)); + const FloatV denom = FRecip(toRecipD);//V4GetW(recipTmp); + const Vec3V v0 = V3Sub(a1, a0); + const Vec3V v1 = V3Sub(a2, a0); + const Vec3V w0 = V3Sub(b1, b0); + const Vec3V w1 = V3Sub(b2, b0); + + const FloatV t = FMul(vb, denom); + const FloatV w = FMul(vc, denom); + const Vec3V vA1 = V3Scale(v1, w); + const Vec3V vB1 = V3Scale(w1, w); + const Vec3V tempClosestA = V3Add(a0, V3ScaleAdd(v0, t, vA1)); + const Vec3V tempClosestB = V3Add(b0, V3ScaleAdd(w0, t, vB1)); + closestA = tempClosestA; + closestB = tempClosestB; + return V3Sub(tempClosestA, tempClosestB); +} + +PX_NOALIAS Vec3V closestPtPointTetrahedronTesselation(Vec3V* PX_RESTRICT Q, Vec3V* PX_RESTRICT A, Vec3V* PX_RESTRICT B, PxU32& size, Vec3V& closestA, Vec3V& closestB) +{ + const FloatV eps = FEps(); + const Vec3V zeroV = V3Zero(); + PxU32 tempSize = size; + + FloatV bestSqDist = FLoad(PX_MAX_REAL); + const Vec3V a = Q[0]; + const Vec3V b = Q[1]; + const Vec3V c = Q[2]; + const Vec3V d = Q[3]; + const BoolV bTrue = BTTTT(); + const BoolV bFalse = BFFFF(); + + //degenerated + const Vec3V ad = V3Sub(d, a); + const Vec3V bd = V3Sub(d, b); + const Vec3V cd = V3Sub(d, c); + const FloatV dad = V3Dot(ad, ad); + const FloatV dbd = V3Dot(bd, bd); + const FloatV dcd = V3Dot(cd, cd); + const FloatV fMin = FMin(dad, FMin(dbd, dcd)); + if (FAllGrtr(eps, fMin)) + { + size = 3; + PxU32 tempIndices[] = { 0, 1, 2 }; + return closestPtPointTriangleTesselation(Q, A, B, tempIndices, size, closestA, closestB); + } + + Vec3V _Q[] = { Q[0], Q[1], Q[2], Q[3] }; + Vec3V _A[] = { A[0], A[1], A[2], A[3] }; + Vec3V _B[] = { B[0], B[1], B[2], B[3] }; + + PxU32 indices[3] = { 0, 1, 2 }; + + const BoolV bIsOutside4 = PointOutsideOfPlane4(a, b, c, d); + + if (BAllEq(bIsOutside4, bFalse)) + { + //origin is inside the tetrahedron, we are done + return zeroV; + } + + Vec3V result = zeroV; + Vec3V tempClosestA, tempClosestB; + + if (BAllEq(BGetX(bIsOutside4), bTrue)) + { + + PxU32 tempIndices[] = { 0, 1, 2 }; + PxU32 _size = 3; + + result = closestPtPointTriangleTesselation(_Q, _A, _B, tempIndices, _size, tempClosestA, tempClosestB); + + const FloatV sqDist = V3Dot(result, result); + bestSqDist = sqDist; + + indices[0] = tempIndices[0]; + indices[1] = tempIndices[1]; + indices[2] = tempIndices[2]; + + tempSize = _size; + closestA = tempClosestA; + closestB = tempClosestB; + } + + if (BAllEq(BGetY(bIsOutside4), bTrue)) + { + + PxU32 tempIndices[] = { 0, 2, 3 }; + + PxU32 _size = 3; + + const Vec3V q = closestPtPointTriangleTesselation(_Q, _A, _B, tempIndices, _size, tempClosestA, tempClosestB); + + const FloatV sqDist = V3Dot(q, q); + const BoolV con = FIsGrtr(bestSqDist, sqDist); + if (BAllEq(con, bTrue)) + { + result = q; + bestSqDist = sqDist; + indices[0] = tempIndices[0]; + indices[1] = tempIndices[1]; + indices[2] = tempIndices[2]; + + tempSize = _size; + closestA = tempClosestA; + closestB = tempClosestB; + } + } + + if (BAllEq(BGetZ(bIsOutside4), bTrue)) + { + + PxU32 tempIndices[] = { 0, 3, 1 }; + PxU32 _size = 3; + + const Vec3V q = closestPtPointTriangleTesselation(_Q, _A, _B, tempIndices, _size, tempClosestA, tempClosestB); + + const FloatV sqDist = V3Dot(q, q); + const BoolV con = FIsGrtr(bestSqDist, sqDist); + if (BAllEq(con, bTrue)) + { + result = q; + bestSqDist = sqDist; + indices[0] = tempIndices[0]; + indices[1] = tempIndices[1]; + indices[2] = tempIndices[2]; + tempSize = _size; + closestA = tempClosestA; + closestB = tempClosestB; + } + + } + + if (BAllEq(BGetW(bIsOutside4), bTrue)) + { + + PxU32 tempIndices[] = { 1, 3, 2 }; + PxU32 _size = 3; + + const Vec3V q = closestPtPointTriangleTesselation(_Q, _A, _B, tempIndices, _size, tempClosestA, tempClosestB); + + const FloatV sqDist = V3Dot(q, q); + const BoolV con = FIsGrtr(bestSqDist, sqDist); + + if (BAllEq(con, bTrue)) + { + result = q; + bestSqDist = sqDist; + + indices[0] = tempIndices[0]; + indices[1] = tempIndices[1]; + indices[2] = tempIndices[2]; + + tempSize = _size; + closestA = tempClosestA; + closestB = tempClosestB; + } + } + + A[0] = _A[indices[0]]; A[1] = _A[indices[1]]; A[2] = _A[indices[2]]; + B[0] = _B[indices[0]]; B[1] = _B[indices[1]]; B[2] = _B[indices[2]]; + Q[0] = _Q[indices[0]]; Q[1] = _Q[indices[1]]; Q[2] = _Q[indices[2]]; + + + size = tempSize; + return result; +} + +PX_NOALIAS PX_FORCE_INLINE Vec3V doTesselation(Vec3V* PX_RESTRICT Q, Vec3V* PX_RESTRICT A, Vec3V* PX_RESTRICT B, + const Vec3VArg support, const Vec3VArg supportA, const Vec3VArg supportB, PxU32& size, Vec3V& closestA, Vec3V& closestB) +{ + switch (size) + { + case 1: + { + closestA = supportA; + closestB = supportB; + return support; + } + case 2: + { + return closestPtPointSegmentTesselation(Q[0], support, A[0], supportA, B[0], supportB, size, closestA, closestB); + } + case 3: + { + + PxU32 tempIndices[3] = { 0, 1, 2 }; + return closestPtPointTriangleTesselation(Q, A, B, tempIndices, size, closestA, closestB); + } + case 4: + { + return closestPtPointTetrahedronTesselation(Q, A, B, size, closestA, closestB); + } + default: + PX_ASSERT(0); + } + return support; +} + + + + +enum Status +{ + STATUS_NON_INTERSECT, + STATUS_CONTACT, + STATUS_DEGENERATE, +}; + +struct Output +{ + /// Get the normal to push apart in direction from A to B + PX_FORCE_INLINE Vec3V getNormal() const { return V3Normalize(V3Sub(mClosestB, mClosestA)); } + Vec3V mClosestA; ///< Closest point on A + Vec3V mClosestB; ///< Closest point on B + FloatV mDistSq; +}; + +struct ConvexV +{ + void calcExtent(const Vec3V& dir, PxF32& minOut, PxF32& maxOut) const + { + // Expand + const Vec4V x = Vec4V_From_FloatV(V3GetX(dir)); + const Vec4V y = Vec4V_From_FloatV(V3GetY(dir)); + const Vec4V z = Vec4V_From_FloatV(V3GetZ(dir)); + + const Vec4V* src = mAovVertices; + const Vec4V* end = src + mNumAovVertices * 3; + + // Do first step + Vec4V max = V4MulAdd(x, src[0], V4MulAdd(y, src[1], V4Mul(z, src[2]))); + Vec4V min = max; + src += 3; + // Do the rest + for (; src < end; src += 3) + { + const Vec4V dot = V4MulAdd(x, src[0], V4MulAdd(y, src[1], V4Mul(z, src[2]))); + max = V4Max(dot, max); + min = V4Min(dot, min); + } + FStore(V4ExtractMax(max), &maxOut); + FStore(V4ExtractMin(min), &minOut); + } + Vec3V calcSupport(const Vec3V& dir) const + { + // Expand + const Vec4V x = Vec4V_From_FloatV(V3GetX(dir)); + const Vec4V y = Vec4V_From_FloatV(V3GetY(dir)); + const Vec4V z = Vec4V_From_FloatV(V3GetZ(dir)); + + PX_ALIGN(16, static const PxF32 index4const[]) = { 0.0f, 1.0f, 2.0f, 3.0f }; + Vec4V index4 = *(const Vec4V*)index4const; + PX_ALIGN(16, static const PxF32 delta4const[]) = { 4.0f, 4.0f, 4.0f, 4.0f }; + const Vec4V delta4 = *(const Vec4V*)delta4const; + + const Vec4V* src = mAovVertices; + const Vec4V* end = src + mNumAovVertices * 3; + + // Do first step + Vec4V max = V4MulAdd(x, src[0], V4MulAdd(y, src[1], V4Mul(z, src[2]))); + Vec4V maxIndex = index4; + index4 = V4Add(index4, delta4); + src += 3; + // Do the rest + for (; src < end; src += 3) + { + const Vec4V dot = V4MulAdd(x, src[0], V4MulAdd(y, src[1], V4Mul(z, src[2]))); + const BoolV cmp = V4IsGrtr(dot, max); + max = V4Max(dot, max); + maxIndex = V4Sel(cmp, index4, maxIndex); + index4 = V4Add(index4, delta4); + } + Vec4V horiMax = Vec4V_From_FloatV(V4ExtractMax(max)); + PxU32 mask = BGetBitMask(V4IsEq(horiMax, max)); + const PxU32 simdIndex = (0x12131210 >> (mask + mask)) & PxU32(3); + + /// NOTE! Could be load hit store + /// Would be better to have all simd. + PX_ALIGN(16, PxF32 f[4]); + V4StoreA(maxIndex, f); + PxU32 index = PxU32(PxI32(f[simdIndex])); + + const Vec4V* aovIndex = (mAovVertices + (index >> 2) * 3); + const PxF32* aovOffset = ((const PxF32*)aovIndex) + (index & 3); + + return Vec3V_From_Vec4V(V4LoadXYZW(aovOffset[0], aovOffset[4], aovOffset[8], 1.0f)); + } + + const Vec4V* mAovVertices; ///< Vertices storex x,x,x,x, y,y,y,y, z,z,z,z + PxU32 mNumAovVertices; ///< Number of groups of 4 of vertices +}; + +Status Collide(const Vec3V& initialDir, const ConvexV& convexA, const Mat34V& bToA, const ConvexV& convexB, Output& out) +{ + Vec3V Q[4]; + Vec3V A[4]; + Vec3V B[4]; + + Mat33V aToB = M34Trnsps33(bToA); + + PxU32 size = 0; + + const Vec3V zeroV = V3Zero(); + const BoolV bTrue = BTTTT(); + + //Vec3V v = V3UnitX(); + Vec3V v = V3Sel(FIsGrtr(V3Dot(initialDir, initialDir), FZero()), initialDir, V3UnitX()); + + //const FloatV minMargin = zero; + //const FloatV eps2 = FMul(minMargin, FLoad(0.01f)); + //FloatV eps2 = zero; + FloatV eps2 = FLoad(1e-6f); + const FloatV epsRel = FLoad(0.000225f); + + Vec3V closA(zeroV), closB(zeroV); + FloatV sDist = FMax(); + FloatV minDist = sDist; + Vec3V closAA = zeroV; + Vec3V closBB = zeroV; + + BoolV bNotTerminated = bTrue; + BoolV bCon = bTrue; + + do + { + minDist = sDist; + closAA = closA; + closBB = closB; + + PxU32 index = size++; + PX_ASSERT(index < 4); + + const Vec3V supportA = convexA.calcSupport(V3Neg(v)); + const Vec3V supportB = M34MulV3(bToA, convexB.calcSupport(M33MulV3(aToB, v))); + const Vec3V support = Vec3V_From_Vec4V(Vec4V_From_Vec3V(V3Sub(supportA, supportB))); + + A[index] = supportA; + B[index] = supportB; + Q[index] = support; + + const FloatV signDist = V3Dot(v, support); + const FloatV tmp0 = FSub(sDist, signDist); + if (FAllGrtr(FMul(epsRel, sDist), tmp0)) + { + out.mClosestA = closA; + out.mClosestB = closB; + out.mDistSq = sDist; + return STATUS_NON_INTERSECT; + } + + //calculate the closest point between two convex hull + v = doTesselation(Q, A, B, support, supportA, supportB, size, closA, closB); + sDist = V3Dot(v, v); + bCon = FIsGrtr(minDist, sDist); + + bNotTerminated = BAnd(FIsGrtr(sDist, eps2), bCon); + } while (BAllEq(bNotTerminated, bTrue)); + + out.mClosestA = V3Sel(bCon, closA, closAA); + out.mClosestB = V3Sel(bCon, closB, closBB); + out.mDistSq = FSel(bCon, sDist, minDist); + return Status(BAllEq(bCon, bTrue) == 1 ? STATUS_CONTACT : STATUS_DEGENERATE); +} + +static void _calcSeparation(const ConvexV& convexA, const physx::PxTransform& aToWorldIn, const Mat34V& bToA, ConvexV& convexB, Output& out, Separation& sep) +{ + + Mat33V aToB = M34Trnsps33(bToA); + Vec3V normalA = out.getNormal(); + + convexA.calcExtent(normalA, sep.min0, sep.max0); + Vec3V normalB = M33MulV3(aToB, normalA); + convexB.calcExtent(normalB, sep.min1, sep.max1); + + { + // Offset the min max taking into account transform + // Distance of origin from B's space in As space in direction of the normal in As space should fix it... + PxF32 fix; + FStore(V3Dot(bToA.col3, normalA), &fix); + sep.min1 += fix; + sep.max1 += fix; + } + + // Looks like it's the plane at the midpoint + Vec3V center = V3Scale(V3Add(out.mClosestA, out.mClosestB), FLoad(0.5f)); + // Transform to world space + Mat34V aToWorld; + *(PxMat44*)&aToWorld = aToWorldIn; + // Put the normal in world space + Vec3V worldCenter = M34MulV3(aToWorld, center); + Vec3V worldNormal = M34Mul33V3(aToWorld, normalA); + + FloatV dist = V3Dot(worldNormal, worldCenter); + V3StoreU(worldNormal, sep.plane.n); + FStore(dist, &sep.plane.d); + sep.plane.d = -sep.plane.d; +} + +static void _arrayVec3ToVec4(const PxVec3* src, Vec4V* dst, PxU32 num) +{ + const PxU32 num4 = num >> 2; + for (PxU32 i = 0; i < num4; i++, dst += 3, src += 4) + { + Vec3V v0 = V3LoadU(&src[0].x); + Vec3V v1 = V3LoadU(&src[1].x); + Vec3V v2 = V3LoadU(&src[2].x); + Vec3V v3 = V3LoadU(&src[3].x); + // Transpose + V4Transpose(v0, v1, v2, v3); + // Save + dst[0] = v0; + dst[1] = v1; + dst[2] = v2; + } + const PxU32 remain = num & 3; + if (remain) + { + Vec3V work[4]; + PxU32 i = 0; + for (; i < remain; i++) work[i] = V3LoadU(&src[i].x); + for (; i < 4; i++) work[i] = work[remain - 1]; + V4Transpose(work[0], work[1], work[2], work[3]); + dst[0] = work[0]; + dst[1] = work[1]; + dst[2] = work[2]; + } +} + + +static void _arrayVec3ToVec4(const PxVec3* src, const Vec3V& scale, Vec4V* dst, PxU32 num) +{ + // If no scale - use the faster version + if (V3AllEq(scale, V3One())) + { + return _arrayVec3ToVec4(src, dst, num); + } + + const PxU32 num4 = num >> 2; + for (PxU32 i = 0; i < num4; i++, dst += 3, src += 4) + { + Vec3V v0 = V3Mul(scale, V3LoadU(&src[0].x)); + Vec3V v1 = V3Mul(scale, V3LoadU(&src[1].x)); + Vec3V v2 = V3Mul(scale, V3LoadU(&src[2].x)); + Vec3V v3 = V3Mul(scale, V3LoadU(&src[3].x)); + // Transpose + V4Transpose(v0, v1, v2, v3); + // Save + dst[0] = v0; + dst[1] = v1; + dst[2] = v2; + } + const PxU32 remain = num & 3; + if (remain) + { + Vec3V work[4]; + PxU32 i = 0; + for (; i < remain; i++) work[i] = V3Mul(scale, V3LoadU(&src[i].x)); + for (; i < 4; i++) work[i] = work[remain - 1]; + V4Transpose(work[0], work[1], work[2], work[3]); + dst[0] = work[0]; + dst[1] = work[1]; + dst[2] = work[2]; + } +} + + +bool importerHullsInProximityApexFree(const std::vector<PxVec3>& hull0, PxBounds3& hull0Bounds, const physx::PxTransform& localToWorldRT0In, const physx::PxVec3& scale0In, + const std::vector<PxVec3>& hull1, PxBounds3& hull1Bounds, const physx::PxTransform& localToWorldRT1In, const physx::PxVec3& scale1In, + physx::PxF32 maxDistance, Separation* separation) +{ + + + const PxU32 numVerts0 = static_cast<PxU32>(hull0.size()); + const PxU32 numVerts1 = static_cast<PxU32>(hull1.size()); + const PxU32 numAov0 = (numVerts0 + 3) >> 2; + const PxU32 numAov1 = (numVerts1 + 3) >> 2; + Vec4V* verts0 = (Vec4V*)alloca((numAov0 + numAov1) * sizeof(Vec4V) * 3); + + // Make sure it's aligned + PX_ASSERT((size_t(verts0) & 0xf) == 0); + + Vec4V* verts1 = verts0 + (numAov0 * 3); + + const Vec3V scale0 = V3LoadU(&scale0In.x); + const Vec3V scale1 = V3LoadU(&scale1In.x); + std::vector<PxVec3> vert0(numVerts0); + for (uint32_t i = 0; i < numVerts0; ++i) + { + vert0[i] = hull0[i]; + } + std::vector<PxVec3> vert1(numVerts1); + for (uint32_t i = 0; i < numVerts1; ++i) + { + vert1[i] = hull1[i]; + } + + _arrayVec3ToVec4(&vert0[0], scale0, verts0, numVerts0); + _arrayVec3ToVec4(&vert1[0], scale1, verts1, numVerts1); + + const PxTransform trans1To0 = localToWorldRT0In.transformInv(localToWorldRT1In); + + // Load into simd mat + Mat34V bToA; + *(PxMat44*)&bToA = trans1To0; + (*(PxMat44*)&bToA).column3.w = 0.0f; // AOS wants the 4th component of Vec3V to be 0 to work properly + + ConvexV convexA; + ConvexV convexB; + + convexA.mNumAovVertices = numAov0; + convexA.mAovVertices = verts0; + + convexB.mNumAovVertices = numAov1; + convexB.mAovVertices = verts1; + + // Take the origin of B in As space as the inital direction as it is 'the difference in transform origins B-A in A's space' + // Should be a good first guess + const Vec3V initialDir = bToA.col3; + Output output; + Status status = Collide(initialDir, convexA, bToA, convexB, output); + + if (status == STATUS_DEGENERATE) + { + // Calculate the tolerance from the extents + const PxVec3 extents0 = hull0Bounds.getExtents(); + const PxVec3 extents1 = hull1Bounds.getExtents(); + + const FloatV tolerance0 = V3ExtractMin(V3Mul(V3LoadU(&extents0.x), scale0)); + const FloatV tolerance1 = V3ExtractMin(V3Mul(V3LoadU(&extents1.x), scale1)); + + const FloatV tolerance = FMul(FAdd(tolerance0, tolerance1), FLoad(0.01f)); + const FloatV sqTolerance = FMul(tolerance, tolerance); + + status = FAllGrtr(sqTolerance, output.mDistSq) ? STATUS_CONTACT : STATUS_NON_INTERSECT; + } + + switch (status) + { + case STATUS_CONTACT: + { + if (separation) + { + _calcSeparation(convexA, localToWorldRT0In, bToA, convexB, output, *separation); + } + return true; + } + default: + case STATUS_NON_INTERSECT: + { + if (separation) + { + _calcSeparation(convexA, localToWorldRT0In, bToA, convexB, output, *separation); + } + PxF32 val; + FStore(output.mDistSq, &val); + return val < (maxDistance * maxDistance); + } + } +} + +} // namespace Blast +} // namespace Nv diff --git a/NvBlast/sdk/extensions/authoring/source/NvBlastExtApexSharedParts.h b/NvBlast/sdk/extensions/authoring/source/NvBlastExtApexSharedParts.h new file mode 100644 index 0000000..68e0412 --- /dev/null +++ b/NvBlast/sdk/extensions/authoring/source/NvBlastExtApexSharedParts.h @@ -0,0 +1,51 @@ +/* +* Copyright (c) 2016-2017, 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. +*/ + +#ifndef NVBLASTEXTAPEXSHAREDPARTS_H +#define NVBLASTEXTAPEXSHAREDPARTS_H + +#include "NvBlast.h" +#include <vector> +#include <PxPlane.h> +namespace physx +{ + class PxVec3; + class PxTransform; + class PxBounds3; +} + +namespace Nv +{ +namespace Blast +{ + +struct Separation +{ + physx::PxPlane plane; + float min0, max0, min1, max1; + + float getDistance() + { + return physx::PxMax(min0 - max1, min1 - max0); + } +}; + +/** + Function to compute midplane between two convex hulls. Is copied from APEX. +*/ +bool importerHullsInProximityApexFree(const std::vector<physx::PxVec3>& hull0, physx::PxBounds3& hull0Bounds, const physx::PxTransform& localToWorldRT0In, const physx::PxVec3& scale0In, + const std::vector<physx::PxVec3>& hull1, physx::PxBounds3& hull1Bounds, const physx::PxTransform& localToWorldRT1In, const physx::PxVec3& scale1In, + physx::PxF32 maxDistance, Separation* separation); + +} // namespace Blast +} // namespace Nv + + +#endif // NVBLASTEXTAPEXSHAREDPARTS_H diff --git a/NvBlast/sdk/extensions/authoring/source/NvBlastExtAuthoringAccelerator.cpp b/NvBlast/sdk/extensions/authoring/source/NvBlastExtAuthoringAccelerator.cpp new file mode 100644 index 0000000..075bce9 --- /dev/null +++ b/NvBlast/sdk/extensions/authoring/source/NvBlastExtAuthoringAccelerator.cpp @@ -0,0 +1,629 @@ +/* +* Copyright (c) 2016-2017, 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 "NvBlastExtAuthoringAccelerator.h" +#include "NvBlastExtAuthoringMesh.h" +#include "NvBlastExtAuthoringInternalCommon.h" + + +using namespace physx; + + +namespace Nv +{ +namespace Blast +{ + +DummyAccelerator::DummyAccelerator(int32_t count) :count(count) +{ + current = 0; +} +void DummyAccelerator::setState(Vertex* pos, Edge* ed, Facet& fc) +{ + current = 0; + (void)pos; + (void)ed; + (void)fc; +} +void DummyAccelerator::setState(const physx::PxVec3& point) { + current = 0; + (void)point; +} +int32_t DummyAccelerator::getNextFacet() +{ + if (current < count) + { + ++current; + return current - 1; + } + else + return -1; +} + + + +BBoxBasedAccelerator::BBoxBasedAccelerator(Mesh* mesh, int32_t resolution) : mResolution(resolution), alreadyGotValue(1) +{ + mBounds = mesh->getBoundingBox(); + mSpatialMap.resize(resolution * resolution * resolution); + mCells.resize(resolution * resolution * resolution); + int32_t currentCell = 0; + PxVec3 incr = (mBounds.maximum - mBounds.minimum) * (1.0f / mResolution); + for (int32_t z = 0; z < resolution; ++z) + { + for (int32_t y = 0; y < resolution; ++y) + { + for (int32_t x = 0; x < resolution; ++x) + { + mCells[currentCell].minimum.x = mBounds.minimum.x + x * incr.x; + mCells[currentCell].minimum.y = mBounds.minimum.y + y * incr.y; + mCells[currentCell].minimum.z = mBounds.minimum.z + z * incr.z; + + mCells[currentCell].maximum.x = mBounds.minimum.x + (x + 1) * incr.x; + mCells[currentCell].maximum.y = mBounds.minimum.y + (y + 1) * incr.y; + mCells[currentCell].maximum.z = mBounds.minimum.z + (z + 1) * incr.z; + + ++currentCell; + } + } + } + + buildAccelStructure(mesh->getVertices(), mesh->getEdges(), mesh->getFacetsBuffer(), mesh->getFacetCount()); +} + + +BBoxBasedAccelerator::~BBoxBasedAccelerator() +{ + mResolution = 0; + mBounds.setEmpty(); + mSpatialMap.clear(); + mCells.clear(); +} + +int32_t BBoxBasedAccelerator::getNextFacet() +{ + int32_t facetId = -1; + + while (mIteratorCell != -1) + { + if (mIteratorFacet >= (int32_t)mSpatialMap[mIteratorCell].size()) + { + if (!cellList.empty()) + { + mIteratorCell = cellList.back(); + cellList.pop_back(); + mIteratorFacet = 0; + } + else + { + mIteratorCell = -1; + break; + } + } + if (alreadyGotFlag[mSpatialMap[mIteratorCell][mIteratorFacet]] != alreadyGotValue) + { + facetId = mSpatialMap[mIteratorCell][mIteratorFacet]; + mIteratorFacet++; + break; + } + else + { + mIteratorFacet++; + } + } + if (facetId != -1) + { + alreadyGotFlag[facetId] = alreadyGotValue; + } + return facetId; +} +void BBoxBasedAccelerator::setState(Vertex* pos, Edge* ed, Facet& fc) +{ + alreadyGotValue++; + mIteratorCell = -1; + mIteratorFacet = -1; + cellList.clear(); + facetBox.setEmpty(); + Edge* edge = ed + fc.firstEdgeNumber; + uint32_t count = fc.edgesCount; + for (uint32_t ec = 0; ec < count; ++ec) + { + facetBox.include(pos[edge->s].p); + facetBox.include(pos[edge->e].p); + edge++; + } + for (uint32_t i = 0; i < mCells.size(); ++i) + { + if (testCellPolygonIntersection(i, facetBox)) + { + if (!mSpatialMap[i].empty()) + cellList.push_back(i); + } + } + if (!cellList.empty()) + { + mIteratorFacet = 0; + mIteratorCell = cellList.back(); + cellList.pop_back(); + } +} + + +void BBoxBasedAccelerator::setState(const PxVec3& p) +{ + alreadyGotValue++; + mIteratorCell = -1; + mIteratorFacet = -1; + cellList.clear(); + int32_t perSlice = mResolution * mResolution; + for (uint32_t i = 0; i < mCells.size(); ++i) + { + if (mCells[i].contains(p)) + { + int32_t xyCellId = i % perSlice; + for (int32_t zCell = 0; zCell < mResolution; ++zCell) + { + int32_t cell = zCell * perSlice + xyCellId; + if (!mSpatialMap[cell].empty()) + cellList.push_back(cell); + } + } + } + if (!cellList.empty()) + { + mIteratorFacet = 0; + mIteratorCell = cellList.back(); + cellList.pop_back(); + } +} + + +bool BBoxBasedAccelerator::testCellPolygonIntersection(int32_t cellId, PxBounds3& facetBB) +{ + if (weakBoundingBoxIntersection(mCells[cellId], facetBB)) + { + return true; + } + else + return false; +} + +void BBoxBasedAccelerator::buildAccelStructure(Vertex* pos, Edge* edges, Facet* fc, int32_t facetCount) +{ + for (int32_t facet = 0; facet < facetCount; ++facet) + { + PxBounds3 bBox; + bBox.setEmpty(); + Edge* edge = &edges[0] + fc->firstEdgeNumber; + int32_t count = fc->edgesCount; + for (int32_t ec = 0; ec < count; ++ec) + { + bBox.include(pos[edge->s].p); + bBox.include(pos[edge->e].p); + edge++; + } + + for (uint32_t i = 0; i < mCells.size(); ++i) + { + if (testCellPolygonIntersection(i, bBox)) + { + mSpatialMap[i].push_back(facet); + } + } + fc++; + } + alreadyGotFlag.resize(facetCount, 0); + cellList.resize(mCells.size()); +} + +int32_t testEdgeAgainstCube(PxVec3& p1, PxVec3& p2) +{ + PxVec3 vec = p2 - p1; + PxVec3 vecSigns; + for (int32_t i = 0; i < 3; ++i) + { + vecSigns[i] = (vec[i] < 0) ? -1 : 1; + } + for (int32_t i = 0; i < 3; ++i) + { + if (p1[i] * vecSigns[i] > 0.5f) return 0; + if (p2[i] * vecSigns[i] < -0.5f) return 0; + } + + for (int32_t i = 0; i < 3; ++i) + { + int32_t ip1 = (i + 1) % 3; + int32_t ip2 = (i + 2) % 3; + + float vl1 = vec[ip2] * p1[ip1] - vec[ip1] * p1[ip2]; + float vl2 = 0.5f * (vec[ip2] * vecSigns[ip1] + vec[ip1] * vecSigns[ip2]); + if (vl1 * vl1 > vl2 * vl2) + { + return 0; + } + } + return 1; +} + +NV_INLINE int32_t isInSegm(float a, float b, float c) +{ + return (b >= c) - (a >= c); +} + +NV_INLINE int32_t edgeIsAbovePoint(PxVec2& p1, PxVec2& p2, PxVec2& p) +{ + int32_t direction = isInSegm(p1.x, p2.x, p.x); + if (direction != 0) + { + if (isInSegm(p1.y, p2.y, p.y)) + { + if (direction * (p.x - p1.x) * (p2.y - p1.y) >= direction * (p.y - p1.y) * (p2.x - p1.x)) + { + return direction; + } + } + else + { + if (p1.y > p.y) + return direction; + } + } + return 0; +} + +int32_t pointInPolygon(PxVec3* vertices, PxVec3& diagPoint, int32_t edgeCount, PxVec3& normal) +{ + std::vector<PxVec2> projectedVertices(edgeCount * 2); + ProjectionDirections pDir = getProjectionDirection(normal); + PxVec2 projectedDiagPoint = getProjectedPoint(diagPoint, pDir); + PxVec2* saveVert = projectedVertices.data(); + PxVec3* p = vertices; + for (int32_t i = 0; i < edgeCount * 2; ++i) + { + *saveVert = getProjectedPoint(*p, pDir); + ++saveVert; + ++p; + } + int32_t counter = 0; + PxVec2* v = projectedVertices.data(); + for (int32_t i = 0; i < edgeCount; ++i) + { + PxVec2& p1 = *v; + PxVec2& p2 = *(v + 1); + counter += edgeIsAbovePoint(p1, p2, projectedDiagPoint); + v += 2; + } + return counter != 0; +} + + + +int32_t testFacetUnitCubeIntersectionInternal(PxVec3* vertices,PxVec3& facetNormal, int32_t edgeCount) +{ + PxVec3* pnt_p = vertices; + for (int32_t i = 0; i < edgeCount; ++i) + { + if (testEdgeAgainstCube(*pnt_p, *(pnt_p + 1)) == 1) + { + return 1; + } + pnt_p += 2; + } + + PxVec3 cubeDiag(0, 0, 0); + for (int32_t i = 0; i < 3; ++i) + cubeDiag[i] = (facetNormal[i] < 0) ? -1 : 1; + float t = vertices->dot(facetNormal) / (cubeDiag.dot(facetNormal)); + if (t > 0.5 || t < -0.5) + return 0; + + PxVec3 intersPoint = cubeDiag * t; + int trs = pointInPolygon(vertices, intersPoint, edgeCount, facetNormal); + return trs; +} + +enum TrivialFlags +{ + HAS_POINT_BELOW_HIGH_X = ~(1 << 0), + HAS_POINT_ABOVE_LOW_X = ~(1 << 1), + + HAS_POINT_BELOW_HIGH_Y = ~(1 << 2), + HAS_POINT_ABOVE_LOW_Y = ~(1 << 3), + + HAS_POINT_BELOW_HIGH_Z = ~(1 << 4), + HAS_POINT_ABOVE_LOW_Z = ~(1 << 5), + + + + ALL_ONE = (1 << 6) - 1 +}; + + + + + +int32_t testFacetUnitCubeIntersection(Vertex* vertices, Edge* edges, Facet& fc, PxBounds3 cube, float fattening) +{ + Edge* ed = edges + fc.firstEdgeNumber; + int32_t trivialFlags = ALL_ONE; + cube.fattenFast(fattening); + for (uint32_t i = 0; i < fc.edgesCount; ++i) + { + { + PxVec3& p = vertices[ed->s].p; + if (cube.contains(p)) + return 1; + if (p.x < cube.getCenter().x + 0.5) + trivialFlags &= HAS_POINT_BELOW_HIGH_X; + if (p.x > cube.getCenter().x - 0.5) + trivialFlags &= HAS_POINT_ABOVE_LOW_X; + + if (p.y < cube.getCenter().y + 0.5) + trivialFlags &= HAS_POINT_BELOW_HIGH_Y; + if (p.y > cube.getCenter().y - 0.5) + trivialFlags &= HAS_POINT_ABOVE_LOW_Y; + + if (p.z < cube.getCenter().z + 0.5) + trivialFlags &= HAS_POINT_BELOW_HIGH_Z; + if (p.z > cube.getCenter().z - 0.5) + trivialFlags &= HAS_POINT_ABOVE_LOW_Z; + } + { + PxVec3& p = vertices[ed->e].p; + if (cube.contains(p)) + return 1; + if (p.x < cube.getCenter().x + 0.5) + trivialFlags &= HAS_POINT_BELOW_HIGH_X; + if (p.x > cube.getCenter().x - 0.5) + trivialFlags &= HAS_POINT_ABOVE_LOW_X; + + if (p.y < cube.getCenter().y + 0.5) + trivialFlags &= HAS_POINT_BELOW_HIGH_Y; + if (p.y > cube.getCenter().y - 0.5) + trivialFlags &= HAS_POINT_ABOVE_LOW_Y; + + if (p.z < cube.getCenter().z + 0.5) + trivialFlags &= HAS_POINT_BELOW_HIGH_Z; + if (p.z > cube.getCenter().z - 0.5) + trivialFlags &= HAS_POINT_ABOVE_LOW_Z; + } + + ++ed; + } + if (trivialFlags != 0) + { + return 0; + } + std::vector<PxVec3> verticesRescaled(fc.edgesCount * 2); + + int32_t vrt = 0; + ed = edges + fc.firstEdgeNumber; + PxVec3 offset = cube.getCenter(); + PxVec3 normal(1, 1, 1); + + /** + Compute normal + */ + PxVec3& v1 = vertices[ed->s].p; + PxVec3* v2 = nullptr; + PxVec3* v3 = nullptr; + + for (uint32_t i = 0; i < fc.edgesCount; ++i) + { + if (v1 != vertices[ed->s].p) + { + v2 = &vertices[ed->s].p; + break; + } + if (v1 != vertices[ed->e].p) + { + v2 = &vertices[ed->e].p; + break; + } + ed++; + } + ed = edges + fc.firstEdgeNumber; + for (uint32_t i = 0; i < fc.edgesCount; ++i) + { + if (v1 != vertices[ed->s].p && *v2 != vertices[ed->s].p) + { + v3 = &vertices[ed->s].p; + break; + } + if (v1 != vertices[ed->e].p && *v2 != vertices[ed->e].p) + { + v3 = &vertices[ed->e].p; + break; + } + ed++; + } + ed = edges + fc.firstEdgeNumber; + if (v2 != nullptr && v3 != nullptr) + { + normal = (*v2 - v1).cross(*v3 - v1); + } + else + { + return true; // If cant find normal, assume it intersects box. + } + + + normal.normalize(); + + PxVec3 rescale(.5f / (cube.getExtents().x), .5f / (cube.getExtents().y), 0.5f / (cube.getExtents().z)); + for (uint32_t i = 0; i < fc.edgesCount; ++i) + { + verticesRescaled[vrt] = vertices[ed->s].p - offset; + verticesRescaled[vrt].x *= rescale.x; + verticesRescaled[vrt].y *= rescale.y; + verticesRescaled[vrt].z *= rescale.z; + ++vrt; + verticesRescaled[vrt] = vertices[ed->e].p - offset; + verticesRescaled[vrt].x *= rescale.x; + verticesRescaled[vrt].y *= rescale.y; + verticesRescaled[vrt].z *= rescale.z; + ++ed; + ++vrt; + } + return testFacetUnitCubeIntersectionInternal(verticesRescaled.data(), normal, fc.edgesCount); +} + + +IntersectionTestingAccelerator::IntersectionTestingAccelerator(Mesh* in, int32_t resolution) +{ + + + alreadyGotFlag.resize(in->getFacetCount(), 0); + alreadyGotValue = 0; + mResolution = resolution; + + float cubeSize = 1.0f / resolution; + PxVec3 cubeMinimal(-0.5, -0.5, -0.5); + PxVec3 extents(cubeSize, cubeSize, cubeSize); + mCubes.resize(mResolution * mResolution * mResolution); + mSpatialMap.resize(mCubes.size()); + int32_t cubeId = 0; + + // Build unit cube partition + for (int32_t i = 0; i < mResolution; ++i) + { + cubeMinimal.y = -0.5; + cubeMinimal.z = -0.5; + for (int32_t j = 0; j < mResolution; ++j) + { + cubeMinimal.z = -0.5; + for (int32_t k = 0; k < mResolution; ++k) + { + mCubes[cubeId].minimum = cubeMinimal; + mCubes[cubeId].maximum = cubeMinimal + extents; + cubeMinimal.z += cubeSize; + ++cubeId; + } + cubeMinimal.y += cubeSize; + } + cubeMinimal.x += cubeSize; + } + + + for (uint32_t i = 0; i < in->getFacetCount(); ++i) + { + for (uint32_t c = 0; c < mCubes.size(); ++c) + { + if (testFacetUnitCubeIntersection(in->getVertices(), in->getEdges(), *in->getFacet(i), mCubes[c], 0.001)) + { + mSpatialMap[c].push_back(i); + } + } + } +} + + +int32_t IntersectionTestingAccelerator::getNextFacet() +{ + int32_t facetId = -1; + + while (mIteratorCell != -1) + { + if (mIteratorFacet >= (int32_t)mSpatialMap[mIteratorCell].size()) + { + if (!cellList.empty()) + { + mIteratorCell = cellList.back(); + cellList.pop_back(); + mIteratorFacet = 0; + } + else + { + mIteratorCell = -1; + break; + } + } + if (alreadyGotFlag[mSpatialMap[mIteratorCell][mIteratorFacet]] != alreadyGotValue) + { + facetId = mSpatialMap[mIteratorCell][mIteratorFacet]; + mIteratorFacet++; + break; + } + else + { + mIteratorFacet++; + } + } + if (facetId != -1) + { + alreadyGotFlag[facetId] = alreadyGotValue; + } + return facetId; +} + +void IntersectionTestingAccelerator::setState(Vertex* pos, Edge* ed, Facet& fc) +{ + alreadyGotValue++; + mIteratorCell = -1; + mIteratorFacet = -1; + cellList.clear(); + PxBounds3 bigBox(PxVec3(-0.5, -0.5, -0.5), PxVec3(0.5, 0.5, 0.5)); + if (!testFacetUnitCubeIntersection(pos, ed, fc, bigBox, 0.001f)) + { + return; + } + for (uint32_t i = 0; i < mCubes.size(); ++i) + { + if (testFacetUnitCubeIntersection(pos, ed, fc, mCubes[i], 0.001f)) + { + if (!mSpatialMap[i].empty()) + cellList.push_back(i); + } + } + if (!cellList.empty()) + { + mIteratorFacet = 0; + mIteratorCell = cellList.back(); + cellList.pop_back(); + } +} + +void IntersectionTestingAccelerator::setState(const PxVec3& p) +{ + alreadyGotValue++; + mIteratorCell = -1; + mIteratorFacet = -1; + cellList.clear(); + + + for (uint32_t i = 0; i < mCubes.size(); ++i) + { + PxBounds3 tmp = mCubes[i]; + tmp.fattenFast(0.001); + if (tmp.contains(p)) + { + int32_t xyCellId = (((int)((float)i / mResolution)) * mResolution); + for (int32_t zCell = 0; zCell < mResolution; ++zCell) + { + int32_t cell = zCell + xyCellId; + if (!mSpatialMap[cell].empty()) + { + cellList.push_back(cell); + } + + } + } + } + if (!cellList.empty()) + { + mIteratorFacet = 0; + mIteratorCell = cellList.back(); + cellList.pop_back(); + } +} + + +} // namespace Blast +} // namespace Nv diff --git a/NvBlast/sdk/extensions/authoring/source/NvBlastExtAuthoringAccelerator.h b/NvBlast/sdk/extensions/authoring/source/NvBlastExtAuthoringAccelerator.h new file mode 100644 index 0000000..8284cd7 --- /dev/null +++ b/NvBlast/sdk/extensions/authoring/source/NvBlastExtAuthoringAccelerator.h @@ -0,0 +1,147 @@ +/* +* Copyright (c) 2016-2017, 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. +*/ + +#ifndef NVBLASTEXTAUTHORINGACCELERATOR_H +#define NVBLASTEXTAUTHORINGACCELERATOR_H + +#include <set> +#include <vector> +#include "NvBlastExtAuthoringTypes.h" + + +namespace Nv +{ +namespace Blast +{ + +class Mesh; + + +/** + Acceleration structure interface. +*/ +class SpatialAccelerator +{ +public: + /** + Set state of accelerator to return all facets which possibly can intersect given facet. + \param[in] pos Vertex buffer + \param[in] ed Edge buffer + \param[in] fc Facet which should be tested. + */ + virtual void setState(Vertex* pos, Edge* ed, Facet& fc) = 0; + /** + Set state of accelerator to return all facets which possibly can cover given point. Needed for testing whether point is inside mesh. + \param[in] point Point which should be tested. + */ + virtual void setState(const physx::PxVec3& point) = 0; + /** + Recieve next facet for setted state. + \return Next facet index, or -1 if no facets left. + */ + virtual int32_t getNextFacet() = 0; + + virtual ~SpatialAccelerator() {}; +}; + + +/** + Dummy accelerator iterates through all facets of mesh. +*/ +class DummyAccelerator : public SpatialAccelerator +{ +public: + /** + \param[in] count Mesh facets count for which accelerator should be built. + */ + DummyAccelerator(int32_t count); + virtual void setState(Vertex* pos, Edge* ed, Facet& fc); + virtual void setState(const physx::PxVec3& point); + virtual int32_t getNextFacet(); + +private: + int32_t count; + int32_t current; +}; + +/** + Accelerator which builds map from 3d grid to initial mesh facets. + To find all facets which possibly intersect given one, it return all facets which are pointed by grid cells, which intersects with bounding box of given facet. + To find all facets which possibly cover given point, all facets which are pointed by cells in column which contains given point are returned. +*/ +class BBoxBasedAccelerator : public SpatialAccelerator +{ +public: + /** + \param[in] mesh Mesh for which acceleration structure should be built. + \param[in] resolution Resolution on 3d grid. + */ + BBoxBasedAccelerator(Mesh* mesh, int32_t resolution); + virtual ~BBoxBasedAccelerator(); + int32_t getNextFacet(); + void setState(Vertex* pos, Edge* ed, Facet& fc); + void setState(const physx::PxVec3& p); +private: + + bool testCellPolygonIntersection(int32_t cellId, physx::PxBounds3& facetBB); + void buildAccelStructure(Vertex* pos, Edge* edges, Facet* fc, int32_t facetCount); + + int32_t mResolution; + physx::PxBounds3 mBounds; + physx::PxBounds3 facetBox; + std::vector< std::vector<int32_t> > mSpatialMap; + std::vector<physx::PxBounds3> mCells; + + + // Iterator data + std::vector<uint32_t> alreadyGotFlag; + uint32_t alreadyGotValue; + std::vector<int32_t> cellList; + int32_t mIteratorCell; + int32_t mIteratorFacet; +}; + + + +/** + Accelerator which builds map from 3d grid to initial mesh facets. + To find all facets which possibly intersect given one, it return all facets which are pointed by grid cells, which are intersected by given facet. + To find all facets which possibly cover given point, all facets which are pointed by cells in column which contains given point are returned. + + In difference with BBoxBasedAccelerator this accelerator computes actual intersection of cube with polygon. It is more precise and omits much more intersections but slower. +*/ + +class IntersectionTestingAccelerator : public SpatialAccelerator +{ +public: + IntersectionTestingAccelerator(Mesh* mesh, int32_t resolution); + int32_t getNextFacet(); + void setState(Vertex* pos, Edge* ed, Facet& fc); + void setState(const physx::PxVec3& p); + + +private: + std::vector< std::vector<int32_t> > mSpatialMap; + std::vector<physx::PxBounds3> mCubes; + int32_t mResolution; + + // Iterator data + std::vector<uint32_t> alreadyGotFlag; + uint32_t alreadyGotValue; + std::vector<int32_t> cellList; + int32_t mIteratorCell; + int32_t mIteratorFacet; +}; + +} // namespace Blast +} // namsepace Nv + + +#endif // ifndef NVBLASTEXTAUTHORINGACCELERATOR_H diff --git a/NvBlast/sdk/extensions/authoring/source/NvBlastExtAuthoringBondGenerator.cpp b/NvBlast/sdk/extensions/authoring/source/NvBlastExtAuthoringBondGenerator.cpp new file mode 100644 index 0000000..b2c3883 --- /dev/null +++ b/NvBlast/sdk/extensions/authoring/source/NvBlastExtAuthoringBondGenerator.cpp @@ -0,0 +1,991 @@ +/* +* Copyright (c) 2017, 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. +*/ + +// This warning arises when using some stl containers with older versions of VC +// c:\program files (x86)\microsoft visual studio 12.0\vc\include\xtree(1826): warning C4702: unreachable code +#include "NvPreprocessor.h" +#if NV_VC && NV_VC < 14 +#pragma warning(disable : 4702) +#endif + +#include <NvBlastExtAuthoringBondGenerator.h> +#include <NvBlastTypes.h> +#include <NvBlast.h> +#include "NvBlastExtTriangleProcessor.h" +#include "NvBlastExtApexSharedParts.h" +#include "NvBlastExtAuthoringCollisionBuilder.h" +#include "NvBlastExtAuthoringInternalCommon.h" +#include <vector> +#include <map> +#include <PxPlane.h> +#include <algorithm> +#include <cmath> + +using physx::PxVec3; +using physx::PxBounds3; + +//#define DEBUG_OUTPUT +#ifdef DEBUG_OUTPUT + +void saveGeometryToObj(std::vector<PxVec3>& triangles, const char* filepath) +{ + + FILE* outStream = fopen(filepath, "w"); + + for (uint32_t i = 0; i < triangles.size(); ++i) + { + fprintf(outStream, "v %lf %lf %lf\n", triangles[i].x, triangles[i].y, triangles[i].z); + ++i; + fprintf(outStream, "v %lf %lf %lf\n", triangles[i].x, triangles[i].y, triangles[i].z); + ++i; + fprintf(outStream, "v %lf %lf %lf\n", triangles[i].x, triangles[i].y, triangles[i].z); + } + for (uint32_t i = 0; i < triangles.size() / 3; ++i) + { + PxVec3 normal = (triangles[3 * i + 2] - triangles[3 * i]).cross((triangles[3 * i + 1] - triangles[3 * i])).getNormalized(); + fprintf(outStream, "vn %lf %lf %lf\n", normal.x, normal.y, normal.z); + fprintf(outStream, "vn %lf %lf %lf\n", normal.x, normal.y, normal.z); + fprintf(outStream, "vn %lf %lf %lf\n", normal.x, normal.y, normal.z); + } + int indx = 1; + for (uint32_t i = 0; i < triangles.size() / 3; ++i) + { + fprintf(outStream, "f %d//%d ", indx, indx); + indx++; + fprintf(outStream, "%d//%d ", indx, indx); + indx++; + fprintf(outStream, "%d//%d \n", indx, indx); + indx++; + } + + fclose(outStream); + +} + + +std::vector<PxVec3> intersectionBuffer; +std::vector<PxVec3> meshBuffer; +#endif + +namespace Nv +{ + namespace Blast + { + + #define EPS_PLANE 0.0001f + + bool planeComparer(const PlaneChunkIndexer& as, const PlaneChunkIndexer& bs) + { + const PxPlane& a = as.plane; + const PxPlane& b = bs.plane; + + if (a.d + EPS_PLANE < b.d) return true; + if (a.d - EPS_PLANE > b.d) return false; + if (a.n.x + EPS_PLANE < b.n.x) return true; + if (a.n.x - EPS_PLANE > b.n.x) return false; + if (a.n.y + EPS_PLANE < b.n.y) return true; + if (a.n.y - EPS_PLANE > b.n.y) return false; + return a.n.z + EPS_PLANE < b.n.z; + } + + + struct Bond + { + int32_t m_chunkId; + int32_t m_planeIndex; + int32_t triangleIndex; + + bool operator<(const Bond& inp) const + { + if (abs(m_planeIndex) == abs(inp.m_planeIndex)) + { + return m_chunkId < inp.m_chunkId; + } + else + { + return abs(m_planeIndex) < abs(inp.m_planeIndex); + } + } + }; + + + struct BondInfo + { + float area; + physx::PxBounds3 m_bb; + physx::PxVec3 centroid; + physx::PxVec3 normal; + int32_t m_chunkId; + }; + + + float BlastBondGenerator::processWithMidplanes(TriangleProcessor* trProcessor, const std::vector<PxVec3>& chunk1Points, const std::vector<PxVec3>& chunk2Points, + const std::vector<PxVec3>& hull1p, const std::vector<PxVec3>& hull2p, PxVec3& normal, PxVec3& centroid) + { + PxBounds3 bounds; + PxBounds3 aBounds; + PxBounds3 bBounds; + bounds.setEmpty(); + aBounds.setEmpty(); + bBounds.setEmpty(); + + PxVec3 chunk1Centroid(0, 0, 0); + PxVec3 chunk2Centroid(0, 0, 0); + + /////////////////////////////////////////////////////////////////////////////////// + if (chunk1Points.size() < 4 || chunk2Points.size() < 4) + { + return 0.0; + } + + for (uint32_t i = 0; i < chunk1Points.size(); ++i) + { + chunk1Centroid += chunk1Points[i]; + bounds.include(chunk1Points[i]); + aBounds.include(chunk1Points[i]); + } + for (uint32_t i = 0; i < chunk2Points.size(); ++i) + { + chunk2Centroid += chunk2Points[i]; + bounds.include(chunk2Points[i]); + bBounds.include(chunk2Points[i]); + } + + + chunk1Centroid *= (1.0f / chunk1Points.size()); + chunk2Centroid *= (1.0f / chunk2Points.size()); + + Separation separation; + if (!importerHullsInProximityApexFree(hull1p, aBounds, PxTransform(PxIdentity), PxVec3(1, 1, 1), hull2p, bBounds, PxTransform(PxIdentity), PxVec3(1, 1, 1), 0.000, &separation)) + { + return 0.0; + } + + // Build first plane interface + PxPlane midplane = separation.plane; + if (!midplane.n.isFinite()) + { + return 0.0; + } + std::vector<PxVec3> interfacePoints; + + float firstCentroidSide = midplane.distance(chunk1Centroid); + float secondCentroidSide = midplane.distance(chunk2Centroid); + + for (uint32_t i = 0; i < chunk1Points.size(); ++i) + { + float dst = midplane.distance(chunk1Points[i]); + if (dst * firstCentroidSide < 0) + { + interfacePoints.push_back(chunk1Points[i]); + } + } + + for (uint32_t i = 0; i < chunk2Points.size(); ++i) + { + float dst = midplane.distance(chunk2Points[i]); + if (dst * secondCentroidSide < 0) + { + interfacePoints.push_back(chunk2Points[i]); + } + } + std::vector<PxVec3> convexHull; + trProcessor->buildConvexHull(interfacePoints, convexHull, midplane.n); + float area = 0; + PxVec3 centroidLocal(0, 0, 0); + if (convexHull.size() < 3) + { + return 0.0; + } + for (uint32_t i = 0; i < convexHull.size() - 1; ++i) + { + centroidLocal += convexHull[i]; + area += (convexHull[i] - convexHull[0]).cross((convexHull[i + 1] - convexHull[0])).magnitude(); + } + centroidLocal += convexHull.back(); + centroidLocal *= (1.0f / convexHull.size()); + float direction = midplane.n.dot(chunk2Centroid - chunk1Centroid); + if (direction < 0) + { + normal = -1.0f * normal; + } + normal = midplane.n; + centroid = centroidLocal; + return area * 0.5f; + } + + + int32_t BlastBondGenerator::bondsFromPrefractured(const std::vector<std::vector<Triangle>>& geometry, const std::vector<bool>& chunkIsSupport, std::vector<NvBlastBondDesc>& resultBondDescs, BondGenerationConfig conf) + { + int32_t ret_val = 0; + switch (conf.bondMode) + { + case BondGenerationConfig::AVERAGE: + ret_val = createFullBondListAveraged(geometry, chunkIsSupport, resultBondDescs, conf); + break; + case BondGenerationConfig::EXACT: + ret_val = createFullBondListExact(geometry, chunkIsSupport, resultBondDescs, conf); + break; + } + return ret_val; + } + + int32_t BlastBondGenerator::createFullBondListAveraged(const std::vector<std::vector<Triangle>>& chunksGeometry, const std::vector<bool>& supportFlags, std::vector<NvBlastBondDesc>& mResultBondDescs, BondGenerationConfig conf) + { + NV_UNUSED(conf); + + std::vector<std::vector<PxVec3> > chunksPoints(chunksGeometry.size()); + + for (uint32_t i = 0; i < chunksGeometry.size(); ++i) + { + if (!supportFlags[i]) + { + continue; + } + for (uint32_t j = 0; j < chunksGeometry[i].size(); ++j) + { + chunksPoints[i].push_back(chunksGeometry[i][j].a.p); + chunksPoints[i].push_back(chunksGeometry[i][j].b.p); + chunksPoints[i].push_back(chunksGeometry[i][j].c.p); + } + } + + Nv::Blast::ConvexMeshBuilder builder(mPxCooking, mPxInsertionCallback); + + std::vector<CollisionHull> cHulls(chunksGeometry.size()); + + for (uint32_t i = 0; i < chunksGeometry.size(); ++i) + { + if (!supportFlags[i]) + { + continue; + } + builder.buildCollisionGeometry(chunksPoints[i], cHulls[i]); + } + + std::vector<std::vector<PxVec3> > hullPoints(cHulls.size()); + + for (uint32_t chunk = 0; chunk < cHulls.size(); ++chunk) + { + if (!supportFlags[chunk]) + { + continue; + } + + hullPoints[chunk].resize(cHulls[chunk].points.size()); + for (uint32_t i = 0; i < cHulls[chunk].points.size(); ++i) + { + hullPoints[chunk][i].x = cHulls[chunk].points[i].x; + hullPoints[chunk][i].y = cHulls[chunk].points[i].y; + hullPoints[chunk][i].z = cHulls[chunk].points[i].z; + } + } + + TriangleProcessor trProcessor; + + for (uint32_t i = 0; i < chunksGeometry.size(); ++i) + { + if (!supportFlags[i]) + { + continue; + } + for (uint32_t j = i + 1; j < chunksGeometry.size(); ++j) + { + if (!supportFlags[i]) + { + continue; + } + PxVec3 normal; + PxVec3 centroid; + + float area = processWithMidplanes(&trProcessor, chunksPoints[i], chunksPoints[j], hullPoints[i], hullPoints[j], normal, centroid); + + if (area > 0) + { + NvBlastBondDesc bDesc; + bDesc.chunkIndices[0] = i; + bDesc.chunkIndices[1] = j; + bDesc.bond.area = area; + bDesc.bond.centroid[0] = centroid.x; + bDesc.bond.centroid[1] = centroid.y; + bDesc.bond.centroid[2] = centroid.z; + + bDesc.bond.normal[0] = normal.x; + bDesc.bond.normal[1] = normal.y; + bDesc.bond.normal[2] = normal.z; + + + mResultBondDescs.push_back(bDesc); + } + + } + } + + return 0; + } + + uint32_t isSamePlane(PxPlane& a, PxPlane& b) + { + if (PxAbs(a.d - b.d) > EPS_PLANE) return 0; + if (PxAbs(a.n.x - b.n.x) > EPS_PLANE) return 0; + if (PxAbs(a.n.y - b.n.y) > EPS_PLANE) return 0; + if (PxAbs(a.n.z - b.n.z) > EPS_PLANE) return 0; + return 1; + } + + int32_t BlastBondGenerator::createFullBondListExact(const std::vector<std::vector<Triangle>>& chunksGeometry, const std::vector<bool>& supportFlags, std::vector<NvBlastBondDesc>& mResultBondDescs, BondGenerationConfig conf) + { + std::vector < PlaneChunkIndexer > planeTriangleMapping; + NV_UNUSED(conf); + for (uint32_t i = 0; i < chunksGeometry.size(); ++i) + { + if (!supportFlags[i]) + { + continue; + } + for (uint32_t j = 0; j < chunksGeometry[i].size(); ++j) + { +#ifdef DEBUG_OUTPUT + meshBuffer.push_back(chunksGeometry[i][j].a.p ); + meshBuffer.push_back(chunksGeometry[i][j].b.p); + meshBuffer.push_back(chunksGeometry[i][j].c.p ); +#endif + + PxPlane nPlane = PxPlane(chunksGeometry[i][j].a.p, chunksGeometry[i][j].b.p, chunksGeometry[i][j].c.p); + planeTriangleMapping.push_back(PlaneChunkIndexer(i, j, nPlane)); + } + } + + std::sort(planeTriangleMapping.begin(), planeTriangleMapping.end(), planeComparer); + return createFullBondListExactInternal(chunksGeometry, planeTriangleMapping, mResultBondDescs); + } + + void BlastBondGenerator::buildGeometryCache(const std::vector<std::vector<Triangle> >& geometry) + { + mGeometryCache = geometry; + mHullsPointsCache.resize(geometry.size()); + mBoundsCache.resize(geometry.size()); + mCHullCache.resize(geometry.size()); + for (uint32_t i = 0; i < mGeometryCache.size(); ++i) + { + for (uint32_t j = 0; j < mGeometryCache[i].size(); ++j) + { + + PxPlane nPlane = PxPlane(mGeometryCache[i][j].a.p, mGeometryCache[i][j].b.p, mGeometryCache[i][j].c.p); + mPlaneCache.push_back(PlaneChunkIndexer(i, j, nPlane)); + } + } + + for (uint32_t ch = 0; ch < mGeometryCache.size(); ++ch) + { + std::vector<PxVec3> chunksPoints(mGeometryCache[ch].size() * 3); + + int32_t sp = 0; + for (uint32_t i = 0; i < mGeometryCache[ch].size(); ++i) + { + chunksPoints[sp++] = mGeometryCache[ch][i].a.p; + chunksPoints[sp++] = mGeometryCache[ch][i].b.p; + chunksPoints[sp++] = mGeometryCache[ch][i].c.p; + } + + Nv::Blast::ConvexMeshBuilder builder(mPxCooking, mPxInsertionCallback); + + CollisionHull& cHull = mCHullCache[ch]; + + builder.buildCollisionGeometry(chunksPoints, cHull); + + mHullsPointsCache[ch].resize(cHull.points.size()); + + mBoundsCache[ch].setEmpty(); + for (uint32_t i = 0; i < cHull.points.size(); ++i) + { + mHullsPointsCache[ch][i].x = cHull.points[i].x; + mHullsPointsCache[ch][i].y = cHull.points[i].y; + mHullsPointsCache[ch][i].z = cHull.points[i].z; + mBoundsCache[ch].include(mHullsPointsCache[ch][i]); + } + } + } + + void BlastBondGenerator::resetGeometryCache() + { + mGeometryCache.clear(); + mPlaneCache.clear(); + mHullsPointsCache.clear(); + mCHullCache.clear(); + mBoundsCache.clear(); + } + + int32_t BlastBondGenerator::createFullBondListExactInternal(const std::vector<std::vector<Triangle>>& chunksGeometry, std::vector < PlaneChunkIndexer >& planeTriangleMapping, std::vector<NvBlastBondDesc>& mResultBondDescs) + { + std::map<std::pair<int32_t, int32_t>, std::pair<NvBlastBondDesc, int32_t> > bonds; + + TriangleProcessor trPrc; + std::vector<PxVec3> intersectionBufferLocal; + + NvBlastBondDesc cleanBond; + memset(&cleanBond, 0, sizeof(NvBlastBondDesc)); + for (uint32_t tIndex = 0; tIndex < planeTriangleMapping.size(); ++tIndex) + { + + PlaneChunkIndexer opp = planeTriangleMapping[tIndex]; + + opp.plane.d *= -1; + opp.plane.n *= -1; + + uint32_t startIndex = (uint32_t)(std::lower_bound(planeTriangleMapping.begin(), planeTriangleMapping.end(), opp, planeComparer) - planeTriangleMapping.begin()); + uint32_t endIndex = (uint32_t)(std::upper_bound(planeTriangleMapping.begin(), planeTriangleMapping.end(), opp, planeComparer) - planeTriangleMapping.begin()); + // uint32_t startIndex = 0; + // uint32_t endIndex = (uint32_t)planeTriangleMapping.size(); + + PlaneChunkIndexer& mappedTr = planeTriangleMapping[tIndex]; + const Triangle& trl = chunksGeometry[mappedTr.chunkId][mappedTr.trId]; + PxPlane pln = mappedTr.plane; + TrPrcTriangle trp(trl.a.p, trl.b.p, trl.c.p); + PxVec3 trCentroid = (trl.a.p + trl.b.p + trl.c.p) * (1.0f / 3.0f); + trp.points[0] -= trCentroid; + trp.points[1] -= trCentroid; + trp.points[2] -= trCentroid; + ProjectionDirections pDir = getProjectionDirection(pln.n); + TrPrcTriangle2d trp2d; + trp2d.points[0] = getProjectedPointWithWinding(trp.points[0], pDir); + trp2d.points[1] = getProjectedPointWithWinding(trp.points[1], pDir); + trp2d.points[2] = getProjectedPointWithWinding(trp.points[2], pDir); + + for (uint32_t i = startIndex; i <= endIndex && i < planeTriangleMapping.size(); ++i) + { + PlaneChunkIndexer& mappedTr2 = planeTriangleMapping[i]; + if (mappedTr2.trId == opp.chunkId) + { + continue; + } + + if (!isSamePlane(opp.plane, mappedTr2.plane)) + { + continue; + } + + if (mappedTr.chunkId == mappedTr2.chunkId) + { + continue; + } + std::pair<int32_t, int32_t> bondEndPoints = std::make_pair(mappedTr.chunkId, mappedTr2.chunkId); + if (bondEndPoints.second < bondEndPoints.first) continue; + std::pair<int32_t, int32_t> bondEndPointsSwapped = std::make_pair(mappedTr2.chunkId, mappedTr.chunkId); + if (bonds.find(bondEndPoints) == bonds.end() && bonds.find(bondEndPointsSwapped) != bonds.end()) + { + continue; // We do not need account interface surface twice + } + if (bonds.find(bondEndPoints) == bonds.end()) + { + bonds[bondEndPoints].second = 0; + bonds[bondEndPoints].first = cleanBond; + bonds[bondEndPoints].first.chunkIndices[0] = bondEndPoints.first; + bonds[bondEndPoints].first.chunkIndices[1] = bondEndPoints.second; + bonds[bondEndPoints].first.bond.normal[0] = pln.n[0]; + bonds[bondEndPoints].first.bond.normal[1] = pln.n[1]; + bonds[bondEndPoints].first.bond.normal[2] = pln.n[2]; + } + + const Triangle& trl2 = chunksGeometry[mappedTr2.chunkId][mappedTr2.trId]; + + TrPrcTriangle trp2(trl2.a.p, trl2.b.p, trl2.c.p); + + intersectionBufferLocal.clear(); + intersectionBufferLocal.reserve(32); + trPrc.getTriangleIntersection(trp, trp2d, trp2, trCentroid, intersectionBufferLocal, pln.n); + PxVec3 centroidPoint(0, 0, 0); + int32_t collectedVerticesCount = 0; + float area = 0; + if (intersectionBufferLocal.size() >= 3) + { +#ifdef DEBUG_OUTPUT + for (uint32_t p = 1; p < intersectionBufferLocal.size() - 1; ++p) + { + intersectionBuffer.push_back(intersectionBufferLocal[0]); + intersectionBuffer.push_back(intersectionBufferLocal[p]); + intersectionBuffer.push_back(intersectionBufferLocal[p + 1]); + } +#endif + centroidPoint = intersectionBufferLocal[0] + intersectionBufferLocal.back(); + collectedVerticesCount = 2; + + for (uint32_t j = 1; j < intersectionBufferLocal.size() - 1; ++j) + { + ++collectedVerticesCount; + centroidPoint += intersectionBufferLocal[j]; + area += (intersectionBufferLocal[j + 1] - intersectionBufferLocal[0]).cross(intersectionBufferLocal[j] - intersectionBufferLocal[0]).magnitude(); + } + } + if (area > 0.00001f) + { + bonds[bondEndPoints].second += collectedVerticesCount; + + bonds[bondEndPoints].first.bond.area += area * 0.5f; + bonds[bondEndPoints].first.bond.centroid[0] += (centroidPoint.x); + bonds[bondEndPoints].first.bond.centroid[1] += (centroidPoint.y); + bonds[bondEndPoints].first.bond.centroid[2] += (centroidPoint.z); + } + } + } + + for (auto it : bonds) + { + if (it.second.first.bond.area > 0) + { + float mlt = 1.0f / (it.second.second); + it.second.first.bond.centroid[0] *= mlt; + it.second.first.bond.centroid[1] *= mlt; + it.second.first.bond.centroid[2] *= mlt; + + mResultBondDescs.push_back(it.second.first); + } + + } +#ifdef DEBUG_OUTPUT + saveGeometryToObj(meshBuffer, "Mesh.obj"); + saveGeometryToObj(intersectionBuffer, "inter.obj"); +#endif + return 0; + } + + int32_t BlastBondGenerator::createBondForcedInternal(const std::vector<PxVec3>& hull0, const std::vector<PxVec3>& hull1, + const CollisionHull& cHull0,const CollisionHull& cHull1, + PxBounds3 bound0, PxBounds3 bound1, NvBlastBond& resultBond, float overlapping) + { + + TriangleProcessor trProcessor; + Separation separation; + importerHullsInProximityApexFree(hull0, bound0, PxTransform(PxIdentity), PxVec3(1, 1, 1), hull1, bound1, PxTransform(PxIdentity), PxVec3(1, 1, 1), 0.000, &separation); + + if (std::isnan(separation.plane.d)) + { + importerHullsInProximityApexFree(hull0, bound0, PxTransform(PxVec3(0.000001f, 0.000001f, 0.000001f)), PxVec3(1, 1, 1), hull1, bound1, PxTransform(PxIdentity), PxVec3(1, 1, 1), 0.000, &separation); + if (std::isnan(separation.plane.d)) + { + return 1; + } + } + + PxPlane pl = separation.plane; + std::vector<PxVec3> ifsPoints[2]; + + float dst[2][2]; + + dst[0][0] = 0; + dst[0][1] = MAXIMUM_EXTENT; + for (uint32_t p = 0; p < cHull0.points.size(); ++p) + { + float d = pl.distance(PxVec3(cHull0.points[p].x, cHull0.points[p].y, cHull0.points[p].z)); + if (PxAbs(d) > PxAbs(dst[0][0])) + { + dst[0][0] = d; + } + if (PxAbs(d) < PxAbs(dst[0][1])) + { + dst[0][1] = d; + } + } + + dst[1][0] = 0; + dst[1][1] = MAXIMUM_EXTENT; + for (uint32_t p = 0; p < cHull1.points.size(); ++p) + { + float d = pl.distance(PxVec3(cHull1.points[p].x, cHull1.points[p].y, cHull1.points[p].z)); + if (PxAbs(d) > PxAbs(dst[1][0])) + { + dst[1][0] = d; + } + if (PxAbs(d) < PxAbs(dst[1][1])) + { + dst[1][1] = d; + } + } + + + float cvOffset[2] = { dst[0][1] + (dst[0][0] - dst[0][1]) * overlapping, dst[1][1] + (dst[1][0] - dst[1][1]) * overlapping }; + + for (uint32_t i = 0; i < cHull0.polygonData.size(); ++i) + { + uint32_t offset = cHull0.polygonData[i].mIndexBase; + PxVec3 result; + for (uint32_t j = 0; j < cHull0.polygonData[i].mNbVerts; ++j) + { + uint32_t nxj = (j + 1) % cHull0.polygonData[i].mNbVerts; + const uint32_t* ind = &cHull0.indices[0]; + PxVec3 a = hull0[ind[j + offset]] - pl.n * cvOffset[0]; + PxVec3 b = hull0[ind[nxj + offset]] - pl.n * cvOffset[0]; + + if (getPlaneSegmentIntersection(pl, a, b, result)) + { + ifsPoints[0].push_back(result); + } + } + } + + for (uint32_t i = 0; i < cHull1.polygonData.size(); ++i) + { + uint32_t offset = cHull1.polygonData[i].mIndexBase; + PxVec3 result; + for (uint32_t j = 0; j < cHull1.polygonData[i].mNbVerts; ++j) + { + uint32_t nxj = (j + 1) % cHull1.polygonData[i].mNbVerts; + const uint32_t* ind = &cHull1.indices[0]; + PxVec3 a = hull1[ind[j + offset]] - pl.n * cvOffset[1]; + PxVec3 b = hull1[ind[nxj + offset]] - pl.n * cvOffset[1]; + + if (getPlaneSegmentIntersection(pl, a, b, result)) + { + ifsPoints[1].push_back(result); + } + } + } + + + std::vector<PxVec3> convexes[2]; + + trProcessor.buildConvexHull(ifsPoints[0], convexes[0], pl.n); + trProcessor.buildConvexHull(ifsPoints[1], convexes[1], pl.n); + + float areas[2] = { 0, 0 }; + PxVec3 centroids[2] = { PxVec3(0, 0, 0), PxVec3(0, 0, 0) }; + + for (uint32_t cv = 0; cv < 2; ++cv) + { + if (convexes[cv].size() == 0) + { + continue; + } + centroids[cv] = convexes[cv][0] + convexes[cv].back(); + for (uint32_t i = 1; i < convexes[cv].size() - 1; ++i) + { + centroids[cv] += convexes[cv][i]; + areas[cv] += (convexes[cv][i + 1] - convexes[cv][0]).cross(convexes[cv][i] - convexes[cv][0]).magnitude(); +#ifdef DEBUG_OUTPUT + intersectionBuffer.push_back(convexes[cv][0]); + intersectionBuffer.push_back(convexes[cv][i]); + intersectionBuffer.push_back(convexes[cv][i + 1]); +#endif + + } + centroids[cv] *= (1.0f / convexes[cv].size()); + areas[cv] = PxAbs(areas[cv]); + } + + resultBond.area = (areas[0] + areas[1]) * 0.5f; + resultBond.centroid[0] = (centroids[0][0] + centroids[1][0]) * 0.5f; + resultBond.centroid[1] = (centroids[0][1] + centroids[1][1]) * 0.5f; + resultBond.centroid[2] = (centroids[0][2] + centroids[1][2]) * 0.5f; + resultBond.normal[0] = pl.n[0]; + resultBond.normal[1] = pl.n[1]; + resultBond.normal[2] = pl.n[2]; + +#ifdef DEBUG_OUTPUT + saveGeometryToObj(meshBuffer, "ArbitMeshes.obj"); + saveGeometryToObj(intersectionBuffer, "inter.obj"); +#endif + + + return 0; + } + + + int32_t BlastBondGenerator::buildDescFromInternalFracture(FractureTool* tool, const std::vector<bool>& chunkIsSupport, std::vector<NvBlastBondDesc>& mResultBondDescs, std::vector<NvBlastChunkDesc>& mResultChunkDescriptors) + { + const std::vector<ChunkInfo>& chunkData = tool->getChunkList(); + std::vector<std::vector<Triangle> > trianglesBuffer(chunkData.size()); + + for (uint32_t i = 0; i < trianglesBuffer.size(); ++i) + { + tool->getBaseMesh(i, trianglesBuffer[i]); + } + + if (chunkData.empty() || trianglesBuffer.empty()) + { + return 1; + } + mResultChunkDescriptors.resize(trianglesBuffer.size()); + std::vector<Bond> bondDescriptors; + mResultChunkDescriptors[0].parentChunkIndex = UINT32_MAX; + mResultChunkDescriptors[0].userData = 0; + + { + PxVec3 chunkCentroid(0, 0, 0); + for (uint32_t tr = 0; tr < trianglesBuffer[0].size(); ++tr) + { + chunkCentroid += trianglesBuffer[0][tr].a.p; + chunkCentroid += trianglesBuffer[0][tr].b.p; + chunkCentroid += trianglesBuffer[0][tr].c.p; + } + chunkCentroid *= (1.0f / (3 * trianglesBuffer[0].size())); + mResultChunkDescriptors[0].centroid[0] = chunkCentroid[0]; + mResultChunkDescriptors[0].centroid[1] = chunkCentroid[1]; + mResultChunkDescriptors[0].centroid[2] = chunkCentroid[2]; + } + + for (uint32_t i = 1; i < chunkData.size(); ++i) + { + + mResultChunkDescriptors[i].userData = i; + mResultChunkDescriptors[i].parentChunkIndex = tool->getChunkIndex(chunkData[i].parent); + if (chunkIsSupport[i]) + mResultChunkDescriptors[i].flags = NvBlastChunkDesc::SupportFlag; + PxVec3 chunkCentroid(0, 0, 0); + for (uint32_t tr = 0; tr < trianglesBuffer[i].size(); ++tr) + { + chunkCentroid += trianglesBuffer[i][tr].a.p; + chunkCentroid += trianglesBuffer[i][tr].b.p; + chunkCentroid += trianglesBuffer[i][tr].c.p; + + Triangle& trRef = trianglesBuffer[i][tr]; + int32_t id = trRef.userInfo; + if (id == 0) + continue; + bondDescriptors.push_back(Bond()); + Bond& bond = bondDescriptors.back(); + bond.m_chunkId = i; + bond.m_planeIndex = id; + bond.triangleIndex = tr; + } + chunkCentroid *= (1.0f / (3 * trianglesBuffer[i].size())); + mResultChunkDescriptors[i].centroid[0] = chunkCentroid[0]; + mResultChunkDescriptors[i].centroid[1] = chunkCentroid[1]; + mResultChunkDescriptors[i].centroid[2] = chunkCentroid[2]; + } + std::sort(bondDescriptors.begin(), bondDescriptors.end()); + if (bondDescriptors.empty()) + { + return 0; + } + int32_t chunkId, planeId; + chunkId = bondDescriptors[0].m_chunkId; + planeId = bondDescriptors[0].m_planeIndex; + std::vector<BondInfo> forwardChunks; + std::vector<BondInfo> backwardChunks; + + float area = 0; + PxVec3 normal(0, 0, 0); + PxVec3 centroid(0, 0, 0); + int32_t collected = 0; + PxBounds3 bb = PxBounds3::empty(); + + chunkId = -1; + planeId = bondDescriptors[0].m_planeIndex; + for (uint32_t i = 0; i <= bondDescriptors.size(); ++i) + { + if (i == bondDescriptors.size() || (chunkId != bondDescriptors[i].m_chunkId || abs(planeId) != abs(bondDescriptors[i].m_planeIndex))) + { + if (chunkId != -1) + { + if (bondDescriptors[i - 1].m_planeIndex > 0) { + forwardChunks.push_back(BondInfo()); + forwardChunks.back().area = area; + forwardChunks.back().normal = normal; + forwardChunks.back().centroid = centroid * (1.0f / 3.0f / collected); + forwardChunks.back().m_chunkId = chunkId; + forwardChunks.back().m_bb = bb; + + } + else + { + backwardChunks.push_back(BondInfo()); + backwardChunks.back().area = area; + backwardChunks.back().normal = normal; + backwardChunks.back().centroid = centroid * (1.0f / 3.0f / collected); + backwardChunks.back().m_chunkId = chunkId; + backwardChunks.back().m_bb = bb; + } + } + bb.setEmpty(); + collected = 0; + area = 0; + normal = PxVec3(0, 0, 0); + centroid = PxVec3(0, 0, 0); + if (i != bondDescriptors.size()) + chunkId = bondDescriptors[i].m_chunkId; + } + if (i == bondDescriptors.size() || abs(planeId) != abs(bondDescriptors[i].m_planeIndex)) + { + for (uint32_t fchunk = 0; fchunk < forwardChunks.size(); ++fchunk) + { + for (uint32_t bchunk = 0; bchunk < backwardChunks.size(); ++bchunk) + { + if (weakBoundingBoxIntersection(forwardChunks[fchunk].m_bb, backwardChunks[bchunk].m_bb) == 0) + { + continue; + } + if (chunkIsSupport[forwardChunks[fchunk].m_chunkId] == false || chunkIsSupport[backwardChunks[bchunk].m_chunkId] == false) + { + continue; + } + mResultBondDescs.push_back(NvBlastBondDesc()); + mResultBondDescs.back().bond.area = std::min(forwardChunks[fchunk].area, backwardChunks[bchunk].area); + mResultBondDescs.back().bond.normal[0] = forwardChunks[fchunk].normal.x; + mResultBondDescs.back().bond.normal[1] = forwardChunks[fchunk].normal.y; + mResultBondDescs.back().bond.normal[2] = forwardChunks[fchunk].normal.z; + + mResultBondDescs.back().bond.centroid[0] = (forwardChunks[fchunk].centroid.x + backwardChunks[bchunk].centroid.x ) * 0.5; + mResultBondDescs.back().bond.centroid[1] = (forwardChunks[fchunk].centroid.y + backwardChunks[bchunk].centroid.y) * 0.5; + mResultBondDescs.back().bond.centroid[2] = (forwardChunks[fchunk].centroid.z + backwardChunks[bchunk].centroid.z) * 0.5; + + + mResultBondDescs.back().chunkIndices[0] = forwardChunks[fchunk].m_chunkId; + mResultBondDescs.back().chunkIndices[1] = backwardChunks[bchunk].m_chunkId; + } + } + forwardChunks.clear(); + backwardChunks.clear(); + if (i != bondDescriptors.size()) + { + planeId = bondDescriptors[i].m_planeIndex; + } + else + { + break; + } + } + + collected++; + int32_t tr = bondDescriptors[i].triangleIndex; + PxVec3 n = trianglesBuffer[chunkId][tr].getNormal(); + area += n.magnitude(); + normal = n.getNormalized(); + centroid += trianglesBuffer[chunkId][tr].a.p; + centroid += trianglesBuffer[chunkId][tr].b.p; + centroid += trianglesBuffer[chunkId][tr].c.p; + + bb.include(trianglesBuffer[chunkId][tr].a.p); + bb.include(trianglesBuffer[chunkId][tr].b.p); + bb.include(trianglesBuffer[chunkId][tr].c.p); + } + + return 0; + } + + int32_t BlastBondGenerator::createBondBetweenMeshes(const std::vector<std::vector<Triangle> >& geometry, std::vector<NvBlastBondDesc>& resultBond,const std::vector<std::pair<uint32_t, uint32_t> >& overlaps, BondGenerationConfig cfg) + { + if (cfg.bondMode == BondGenerationConfig::AVERAGE) + { + resetGeometryCache(); + buildGeometryCache(geometry); + } + resultBond.clear(); + resultBond.resize(overlaps.size()); + + if (cfg.bondMode == BondGenerationConfig::EXACT) + { + for (uint32_t i = 0; i < overlaps.size(); ++i) + { + resultBond[i].chunkIndices[0] = overlaps[i].first; + resultBond[i].chunkIndices[1] = overlaps[i].second; + createBondBetweenMeshes(geometry[overlaps[i].first], geometry[overlaps[i].second], resultBond[i].bond, cfg); + } + } + else + { + for (uint32_t i = 0; i < overlaps.size(); ++i) + { + resultBond[i].chunkIndices[0] = overlaps[i].first; + resultBond[i].chunkIndices[1] = overlaps[i].second; + createBondForcedInternal(mHullsPointsCache[overlaps[i].first], mHullsPointsCache[overlaps[i].second], mCHullCache[overlaps[i].first], mCHullCache[overlaps[i].second], + mBoundsCache[overlaps[i].first], mBoundsCache[overlaps[i].second], resultBond[i].bond, 0.3f); + } + } + + return 0; + } + + + int32_t BlastBondGenerator::createBondBetweenMeshes(const std::vector<Triangle>& meshA, const std::vector<Triangle>& meshB, NvBlastBond& resultBond, BondGenerationConfig conf) + { + float overlapping = 0.3; + if (conf.bondMode == BondGenerationConfig::EXACT) + { + std::vector<std::vector<Triangle> > chunks; + chunks.push_back(meshA); + chunks.push_back(meshB); + std::vector<bool> isSupport(2, true); + std::vector<NvBlastBondDesc> desc; + createFullBondListExact(chunks, isSupport, desc, conf); + if (desc.size() > 0) + { + resultBond = desc.back().bond; + } + else + { + return 1; + } + return 0; + } + + std::vector<PxVec3> chunksPoints1(meshA.size() * 3); + std::vector<PxVec3> chunksPoints2(meshB.size() * 3); + + int32_t sp = 0; + for (uint32_t i = 0; i < meshA.size(); ++i) + { + chunksPoints1[sp++] = meshA[i].a.p; + chunksPoints1[sp++] = meshA[i].b.p; + chunksPoints1[sp++] = meshA[i].c.p; +#ifdef DEBUG_OUTPUT + meshBuffer.push_back(meshA[i].a.p); + meshBuffer.push_back(meshA[i].b.p); + meshBuffer.push_back(meshA[i].c.p); +#endif + + + } + sp = 0; + for (uint32_t i = 0; i < meshB.size(); ++i) + { + chunksPoints2[sp++] = meshB[i].a.p; + chunksPoints2[sp++] = meshB[i].b.p; + chunksPoints2[sp++] = meshB[i].c.p; +#ifdef DEBUG_OUTPUT + meshBuffer.push_back(meshB[i].a.p); + meshBuffer.push_back(meshB[i].b.p); + meshBuffer.push_back(meshB[i].c.p); +#endif + } + + + Nv::Blast::ConvexMeshBuilder builder(mPxCooking, mPxInsertionCallback); + + CollisionHull cHull[2]; + + builder.buildCollisionGeometry(chunksPoints1, cHull[0]); + builder.buildCollisionGeometry(chunksPoints2, cHull[1]); + + std::vector<PxVec3> hullPoints[2]; + hullPoints[0].resize(cHull[0].points.size()); + hullPoints[1].resize(cHull[1].points.size()); + + + PxBounds3 bb[2]; + bb[0].setEmpty(); + bb[1].setEmpty(); + + for (uint32_t cv = 0; cv < 2; ++cv) + { + for (uint32_t i = 0; i < cHull[cv].points.size(); ++i) + { + hullPoints[cv][i].x = cHull[cv].points[i].x; + hullPoints[cv][i].y = cHull[cv].points[i].y; + hullPoints[cv][i].z = cHull[cv].points[i].z; + bb[cv].include(hullPoints[cv][i]); + } + } + return createBondForcedInternal(hullPoints[0], hullPoints[1], cHull[0], cHull[1], bb[0], bb[1], resultBond, overlapping); + } + + + + } +} diff --git a/NvBlast/sdk/extensions/authoring/source/NvBlastExtAuthoringBooleanTool.cpp b/NvBlast/sdk/extensions/authoring/source/NvBlastExtAuthoringBooleanTool.cpp new file mode 100644 index 0000000..b5030d7 --- /dev/null +++ b/NvBlast/sdk/extensions/authoring/source/NvBlastExtAuthoringBooleanTool.cpp @@ -0,0 +1,1351 @@ +/* +* Copyright (c) 2016-2017, 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 "NvBlastExtAuthoringBooleanTool.h" +#include "NvBlastExtAuthoringMesh.h" +#include "NvBlastExtAuthoringAccelerator.h" + +#include <math.h> +#include <set> +#include <algorithm> + +using physx::PxVec3; +using physx::PxVec2; +using physx::PxBounds3; + + +namespace Nv +{ +namespace Blast +{ + +/* Linear interpolation of vectors */ + +NV_FORCE_INLINE void vec3Lerp(const PxVec3& a, const PxVec3& b, PxVec3& out, float t) +{ + out.x = (b.x - a.x) * t + a.x; + out.y = (b.y - a.y) * t + a.y; + out.z = (b.z - a.z) * t + a.z; +} + +NV_FORCE_INLINE void vec2Lerp(const PxVec2& a, const PxVec2& b, PxVec2& out, float t) +{ + out.x = (b.x - a.x) * t + a.x; + out.y = (b.y - a.y) * t + a.y; +} + + +NV_FORCE_INLINE int32_t BooleanEvaluator::addIfNotExist(Vertex& p) +{ + mVerticesAggregate.push_back(p); + return static_cast<int32_t>(mVerticesAggregate.size()) - 1; +} + +NV_FORCE_INLINE void BooleanEvaluator::addEdgeIfValid(EdgeWithParent& ed) +{ + mEdgeAggregate.push_back(ed); +} + +/** +Vertex level shadowing functions +*/ +NV_FORCE_INLINE int32_t vertexShadowing(const PxVec3& a, const PxVec3& b) +{ + return (b.x >= a.x) ? 1 : 0; +} +/** +Vertex-edge status functions +*/ +NV_FORCE_INLINE int32_t veStatus01(const PxVec3& sEdge, const PxVec3& eEdge, const PxVec3& p) +{ + return vertexShadowing(p, eEdge) - vertexShadowing(p, sEdge); +} + +NV_FORCE_INLINE int32_t veStatus10(const PxVec3& sEdge, const PxVec3& eEdge, const PxVec3& p) +{ + return -vertexShadowing(eEdge, p) + vertexShadowing(sEdge, p); +} + +/** +Vertex-edge shadowing functions +*/ +int32_t shadowing01(const Vertex& sEdge, const Vertex& eEdge, const PxVec3& p, Vertex& onEdgePoint, bool& hasOnEdge) +{ + int32_t winding = veStatus01(sEdge.p, eEdge.p, p); + if (winding != 0) + { + float t = (p.x - sEdge.p.x) / (eEdge.p.x - sEdge.p.x); + if (t >= 1) + { + onEdgePoint = eEdge; + } + else if (t <= 0) + { + onEdgePoint = sEdge; + } + else + { + vec3Lerp(sEdge.p, eEdge.p, onEdgePoint.p, t); + vec3Lerp(sEdge.n, eEdge.n, onEdgePoint.n, t); + vec2Lerp(sEdge.uv[0], eEdge.uv[0], onEdgePoint.uv[0], t); + } + hasOnEdge = true; + if (onEdgePoint.p.y >= p.y) + { + return winding; + } + } + else + { + hasOnEdge = false; + } + return 0; +} +int32_t shadowing10(const Vertex& sEdge, const Vertex& eEdge, const PxVec3& p, Vertex& onEdgePoint, bool& hasOnEdge) +{ + int32_t winding = veStatus10(sEdge.p, eEdge.p, p); + if (winding != 0) + { + float t = (p.x - sEdge.p.x) / (eEdge.p.x - sEdge.p.x); + if (t >= 1) + { + onEdgePoint = eEdge; + } + else if (t <= 0) + { + onEdgePoint = sEdge; + } + else + { + vec3Lerp(sEdge.p, eEdge.p, onEdgePoint.p, t); + vec3Lerp(sEdge.n, eEdge.n, onEdgePoint.n, t); + vec2Lerp(sEdge.uv[0], eEdge.uv[0], onEdgePoint.uv[0], t); + } + hasOnEdge = true; + if (onEdgePoint.p.y < p.y) + { + return winding; + } + } + else + { + hasOnEdge = false; + } + return 0; +} + +int32_t shadowing01(const PxVec3& sEdge, const PxVec3& eEdge, const PxVec3& p) +{ + int32_t winding = veStatus01(sEdge, eEdge, p); + if (winding != 0) + { + float t = ((p.x - sEdge.x) / (eEdge.x - sEdge.x)); + PxVec3 onEdgePoint; + if (t >= 1) + onEdgePoint = eEdge; + else if (t <= 0) + onEdgePoint = sEdge; + else + vec3Lerp(sEdge, eEdge, onEdgePoint, t); + if (onEdgePoint.y >= p.y) + { + return winding; + } + } + return 0; +} + +int32_t shadowing10(const PxVec3& sEdge, const PxVec3& eEdge, const PxVec3& p) +{ + int32_t winding = veStatus10(sEdge, eEdge, p); + if (winding != 0) + { + float t = ((p.x - sEdge.x) / (eEdge.x - sEdge.x)); + PxVec3 onEdgePoint; + if (t >= 1) + onEdgePoint = eEdge; + else if (t <= 0) + onEdgePoint = sEdge; + else + vec3Lerp(sEdge, eEdge, onEdgePoint, t); + if (onEdgePoint.y < p.y) + { + return winding; + } + } + return 0; +} + +/** +Vertex-facet shadowing functions +*/ + +int32_t vfStatus02(const PxVec3& p, const Vertex* points, const Edge* edges, int32_t edgesCount, Vertex& out1, Vertex& out2) +{ + int32_t val = 0; + Vertex pnt; + bool hasOnEdge = false; + for (int32_t i = 0; i < edgesCount; ++i) + { + val -= shadowing01(points[edges->s], points[edges->e], p, pnt, hasOnEdge); + if (hasOnEdge != 0) + { + out2 = out1; + out1 = pnt; + } + ++edges; + } + return val; +} + + +int32_t shadowing02(const PxVec3& p, const Vertex* points, const Edge* edges, int edgesCount, bool& hasOnFacetPoint, Vertex& onFacetPoint) +{ + Vertex p1, p2; + int32_t stat = vfStatus02(p, points, edges, edgesCount, p1, p2); + float z = 0; + hasOnFacetPoint = false; + if (stat != 0) + { + PxVec3 vc = p2.p - p1.p; + float t = 0; + t = (abs(vc.x) > abs(vc.y)) ? (p.x - p1.p.x) / vc.x : (p.y - p1.p.y) / vc.y; + t = (t < 0) ? 0 : t; + t = (t > 1) ? 1 : t; + z = t * vc.z + p1.p.z; + + hasOnFacetPoint = true; + onFacetPoint.p.x = p.x; + onFacetPoint.p.y = p.y; + onFacetPoint.p.z = z; + + vec2Lerp(p1.uv[0], p2.uv[0], onFacetPoint.uv[0], t); + vec3Lerp(p1.n, p2.n, onFacetPoint.n, t); + + if (z >= p.z) + { + return stat; + } + } + return 0; +} + +int32_t vfStatus20(const PxVec3& p, const Vertex* points, const Edge* edges, int32_t edgesCount, Vertex& out1, Vertex& out2) +{ + int32_t val = 0; + Vertex pnt; + bool hasOnEdge = false; + for (int32_t i = 0; i < edgesCount; ++i) + { + val += shadowing10(points[edges->s], points[edges->e], p, pnt, hasOnEdge); + if (hasOnEdge != 0) + { + out2 = out1; + out1 = pnt; + } + ++edges; + } + return val; +} + +int32_t shadowing20(const PxVec3& p, const Vertex* points, const Edge* edges, int edgesCount, bool& hasOnFacetPoint, Vertex& onFacetPoint) +{ + Vertex p1, p2; + int32_t stat = vfStatus20(p, points, edges, edgesCount, p1, p2); + hasOnFacetPoint = false; + if (stat != 0) + { + PxVec3 vc = p2.p - p1.p; + float t = 0; + t = (abs(vc.x) > abs(vc.y)) ? (p.x - p1.p.x) / vc.x : (p.y - p1.p.y) / vc.y; + t = (t < 0) ? 0 : t; + t = (t > 1) ? 1 : t; + + hasOnFacetPoint = true; + onFacetPoint.p.x = p.x; + onFacetPoint.p.y = p.y; + + onFacetPoint.p.z = t * vc.z + p1.p.z; + + vec2Lerp(p1.uv[0], p2.uv[0], onFacetPoint.uv[0], t); + vec3Lerp(p1.n, p2.n, onFacetPoint.n, t); + + if (onFacetPoint.p.z < p.z) + { + return stat; + } + } + return 0; +} + + +NV_FORCE_INLINE int32_t edgesCrossCheck(const PxVec3& eAs, const PxVec3& eAe, const PxVec3& eBs, const PxVec3& eBe) +{ + return shadowing01(eBs, eBe, eAe) - shadowing01(eBs, eBe, eAs) + shadowing10(eAs, eAe, eBe) - shadowing10(eAs, eAe, eBs); +} + +int32_t edgesIntersection(const Vertex& eAs, const Vertex& eAe, const Vertex& eBs, const Vertex& eBe, Vertex& intersectionA, Vertex& intersectionB, bool& hasPoints) +{ + int32_t status = edgesCrossCheck(eAs.p, eAe.p, eBs.p, eBe.p); + hasPoints = false; + if (status == 0) + return 0; + Vertex tempPoint; + + Vertex bShadowingPair[2]; + Vertex aShadowingPair[2]; + bool hasOnEdge = false; + int32_t shadowingType = shadowing10(eAs, eAe, eBs.p, tempPoint, hasOnEdge); + + bool aShadowing = false; + bool bShadowing = false; + + + if (shadowingType == 0 && hasOnEdge) + { + aShadowing = true; + aShadowingPair[0] = eBs; + aShadowingPair[1] = tempPoint; + } + else + { + if (shadowingType == 1 || shadowingType == -1) + { + bShadowing = true; + bShadowingPair[0] = eBs; + bShadowingPair[1] = tempPoint; + } + } + + shadowingType = shadowing10(eAs, eAe, eBe.p, tempPoint, hasOnEdge); + + if (shadowingType == 0 && !aShadowing && hasOnEdge) + { + aShadowing = true; + aShadowingPair[0] = eBe; + aShadowingPair[1] = tempPoint; + } + else + { + if ((shadowingType == 1 || shadowingType == -1) && !bShadowing) + { + bShadowing = true; + bShadowingPair[0] = eBe; + bShadowingPair[1] = tempPoint; + } + } + shadowingType = shadowing01(eBs, eBe, eAe.p, tempPoint, hasOnEdge); + + if (shadowingType == 0 && !aShadowing && hasOnEdge) + { + aShadowing = true; + aShadowingPair[1] = eAe; + aShadowingPair[0] = tempPoint; + } + else + { + if ((shadowingType == 1 || shadowingType == -1) && !bShadowing) + { + bShadowing = true; + bShadowingPair[1] = eAe; + bShadowingPair[0] = tempPoint; + } + } + + shadowingType = shadowing01(eBs, eBe, eAs.p, tempPoint, hasOnEdge); + + if (shadowingType == 0 && !aShadowing && hasOnEdge) + { + aShadowing = true; + aShadowingPair[1] = eAs; + aShadowingPair[0] = tempPoint; + } + else + { + if ((shadowingType == 1 || shadowingType == -1) && !bShadowing) + { + bShadowing = true; + bShadowingPair[1] = eAs; + bShadowingPair[0] = tempPoint; + } + } + float deltaPlus = bShadowingPair[0].p.y - bShadowingPair[1].p.y; + float deltaMinus = aShadowingPair[0].p.y - aShadowingPair[1].p.y; + float div = 0; + if (deltaPlus > 0) + div = deltaPlus / (deltaPlus - deltaMinus); + else + div = 0; + + intersectionA.p = bShadowingPair[1].p - div * (bShadowingPair[1].p - aShadowingPair[1].p); + intersectionA.n = bShadowingPair[1].n - div * (bShadowingPair[1].n - aShadowingPair[1].n); + intersectionA.uv[0] = bShadowingPair[1].uv[0] - (bShadowingPair[1].uv[0] - aShadowingPair[1].uv[0]) * div; + intersectionB.p = intersectionA.p; + intersectionB.p.z = bShadowingPair[0].p.z - div * (bShadowingPair[0].p.z - aShadowingPair[0].p.z); + intersectionB.n = bShadowingPair[0].n - div * (bShadowingPair[0].n - aShadowingPair[0].n); + intersectionB.uv[0] = bShadowingPair[0].uv[0] - (bShadowingPair[0].uv[0] - aShadowingPair[0].uv[0]) * div; + + hasPoints = true; + return status; +} + +NV_FORCE_INLINE int32_t edgeEdgeShadowing(const Vertex& eAs, const Vertex& eAe, const Vertex& eBs, const Vertex& eBe, Vertex& intersectionA, Vertex& intersectionB, bool& hasPoints) +{ + int32_t status = edgesIntersection(eAs, eAe, eBs, eBe, intersectionA, intersectionB, hasPoints); + if (intersectionB.p.z >= intersectionA.p.z) + { + return status; + } + return 0; +} + +int32_t edgeFacetIntersection12(const Vertex& edSt, const Vertex& edEnd, const Vertex* points, const Edge* edges, int edgesCount, Vertex& intersectionA, Vertex& intersectionB) +{ + int32_t status = 0; + Vertex p1, p2; + Vertex bShadowingPair[2]; + Vertex aShadowingPair[2]; + bool hasPoint = false; + int32_t shadowingType = shadowing02(edEnd.p, points, edges, edgesCount, hasPoint, p1); + status -= shadowingType; + bool aShadowing = false; + bool bShadowing = false; + + if (shadowingType == 0 && hasPoint) + { + aShadowing = true; + aShadowingPair[0] = p1; + aShadowingPair[1] = edEnd; + } + else + { + if (shadowingType == 1 || shadowingType == -1) + { + bShadowing = true; + bShadowingPair[0] = p1; + bShadowingPair[1] = edEnd; + } + } + + shadowingType = shadowing02(edSt.p, points, edges, edgesCount, hasPoint, p1); + status += shadowingType; + if (shadowingType == 0 && !aShadowing && hasPoint) + { + aShadowing = true; + aShadowingPair[0] = p1; + aShadowingPair[1] = edSt; + } + else + { + if ((shadowingType == 1 || shadowingType == -1) && !bShadowing) + { + bShadowing = true; + bShadowingPair[0] = p1; + bShadowingPair[1] = edSt; + } + } + + for (int32_t ed = 0; ed < edgesCount; ++ed) + { + shadowingType = edgeEdgeShadowing(edSt, edEnd, points[edges[ed].s], points[edges[ed].e], p1, p2, hasPoint); + status -= shadowingType; + if (shadowingType == 0 && !aShadowing && hasPoint) + { + aShadowing = true; + aShadowingPair[0] = p2; + aShadowingPair[1] = p1; + } + else + { + if ((shadowingType == 1 || shadowingType == -1) && !bShadowing) + { + bShadowing = true; + bShadowingPair[0] = p2; + bShadowingPair[1] = p1; + } + } + } + if (status == 0) + { + return 0; + } + if (!bShadowing || !aShadowing) + { + return 0; + } + float deltaPlus = bShadowingPair[0].p.z - bShadowingPair[1].p.z; + float div = 0; + if (deltaPlus != 0) + { + float deltaMinus = aShadowingPair[0].p.z - aShadowingPair[1].p.z; + div = deltaPlus / (deltaPlus - deltaMinus); + } + intersectionA.p = bShadowingPair[1].p - div * (bShadowingPair[1].p - aShadowingPair[1].p); + intersectionA.n = bShadowingPair[1].n - div * (bShadowingPair[1].n - aShadowingPair[1].n); + intersectionA.uv[0] = bShadowingPair[1].uv[0] - (bShadowingPair[1].uv[0] - aShadowingPair[1].uv[0]) * div; + + intersectionB.p = intersectionA.p; + intersectionB.n = bShadowingPair[0].n - div * (bShadowingPair[0].n - aShadowingPair[0].n); + intersectionB.uv[0] = bShadowingPair[0].uv[0] - (bShadowingPair[0].uv[0] - aShadowingPair[0].uv[0]) * div; + + + return status; +} + + +int32_t edgeFacetIntersection21(const Vertex& edSt, const Vertex& edEnd, const Vertex* points, const Edge* edges, int edgesCount, Vertex& intersectionA, Vertex& intersectionB) +{ + int32_t status = 0; + Vertex p1, p2; + + Vertex bShadowingPair[2]; + Vertex aShadowingPair[2]; + bool hasPoint = false; + int32_t shadowingType = shadowing20(edEnd.p, points, edges, edgesCount, hasPoint, p1); + status = shadowingType; + bool aShadowing = false; + bool bShadowing = false; + if (shadowingType == 0 && hasPoint) + { + aShadowing = true; + aShadowingPair[0] = edEnd; + aShadowingPair[1] = p1; + } + else + { + if (shadowingType == 1 || shadowingType == -1) + { + bShadowing = true; + bShadowingPair[0] = edEnd; + bShadowingPair[1] = p1; + } + } + + shadowingType = shadowing20(edSt.p, points, edges, edgesCount, hasPoint, p1); + status -= shadowingType; + if (shadowingType == 0 && !aShadowing && hasPoint) + { + aShadowing = true; + aShadowingPair[0] = edSt; + aShadowingPair[1] = p1; + } + else + { + if ((shadowingType == 1 || shadowingType == -1) && !bShadowing) + { + bShadowing = true; + bShadowingPair[0] = edSt; + bShadowingPair[1] = p1; + } + } + + for (int32_t ed = 0; ed < edgesCount; ++ed) + { + shadowingType = edgeEdgeShadowing(points[edges[ed].s], points[edges[ed].e], edSt, edEnd, p1, p2, hasPoint); + status -= shadowingType; + if (shadowingType == 0) + { + if (!aShadowing && hasPoint) + { + aShadowing = true; + aShadowingPair[0] = p2; + aShadowingPair[1] = p1; + } + } + else + { + if ((shadowingType == 1 || shadowingType == -1) && !bShadowing) + { + bShadowing = true; + bShadowingPair[0] = p2; + bShadowingPair[1] = p1; + } + } + } + if (status == 0) + { + return 0; + } + if (!bShadowing || !aShadowing) + { + return 0; + } + float deltaPlus = bShadowingPair[0].p.z - bShadowingPair[1].p.z; + float div = 0; + if (deltaPlus != 0) + { + float deltaMinus = aShadowingPair[0].p.z - aShadowingPair[1].p.z; + div = deltaPlus / (deltaPlus - deltaMinus); + } + intersectionA.p = bShadowingPair[1].p - div * (bShadowingPair[1].p - aShadowingPair[1].p); + intersectionA.n = bShadowingPair[1].n - div * (bShadowingPair[1].n - aShadowingPair[1].n); + intersectionA.uv[0] = bShadowingPair[1].uv[0] - (bShadowingPair[1].uv[0] - aShadowingPair[1].uv[0]) * div; + + intersectionB.p = intersectionA.p; + intersectionB.n = bShadowingPair[0].n - div * (bShadowingPair[0].n - aShadowingPair[0].n); + intersectionB.uv[0] = bShadowingPair[0].uv[0] - (bShadowingPair[0].uv[0] - aShadowingPair[0].uv[0]) * div; + + return status; +} + +int32_t BooleanEvaluator::vertexMeshStatus03(const PxVec3& p, Mesh* mesh) +{ + int32_t status = 0; + Vertex pnt; + bool hasPoint = false; + mAcceleratorB->setState(p); + int32_t facet = mAcceleratorB->getNextFacet(); + while (facet != -1) + { + Edge* ed = mesh->getEdges() + mesh->getFacet(facet)->firstEdgeNumber; + status += shadowing02(p, mesh->getVertices(), ed, mesh->getFacet(facet)->edgesCount, hasPoint, pnt); + facet = mAcceleratorB->getNextFacet(); + } + + //for (int32_t facet = 0; facet < mesh->getFacetCount(); ++facet) + //{ + // Edge* ed = mesh->getEdges() + mesh->getFacet(facet)->firstEdgeNumber; + // status += shadowing02(p, mesh->getVertices(), ed, mesh->getFacet(facet)->edgesCount, hasPoint, pnt); + //} + + return status; +} + +int32_t BooleanEvaluator::vertexMeshStatus30(const PxVec3& p, Mesh* mesh) +{ + int32_t status = 0; + bool hasPoints = false; + Vertex point; + mAcceleratorA->setState(p); + int32_t facet = mAcceleratorA->getNextFacet(); + while ( facet != -1) + { + Edge* ed = mesh->getEdges() + mesh->getFacet(facet)->firstEdgeNumber; + status -= shadowing20(p, mesh->getVertices(), ed, mesh->getFacet(facet)->edgesCount, hasPoints, point); + facet = mAcceleratorA->getNextFacet(); + } + + //for (int32_t facet = 0; facet < mesh->getFacetCount(); ++facet) + //{ + // Edge* ed = mesh->getEdges() + mesh->getFacet(facet)->firstEdgeNumber; + // status -= shadowing20(p, mesh->getVertices(), ed, mesh->getFacet(facet)->edgesCount, hasPoints, point); + //} + return status; +} + +NV_FORCE_INLINE int32_t inclusionValue03(BooleanConf& conf, int32_t xValue) +{ + return conf.ca + conf.ci * xValue; +} + +NV_FORCE_INLINE int32_t inclusionValueEdgeFace(BooleanConf& conf, int32_t xValue) +{ + return conf.ci * xValue; +} + +NV_FORCE_INLINE int32_t inclusionValue30(BooleanConf& conf, int32_t xValue) +{ + return conf.cb + conf.ci * xValue; +} + +struct VertexComparator +{ + VertexComparator(PxVec3 base = PxVec3()) : basePoint(base) {}; + PxVec3 basePoint; + bool operator()(const Vertex& a, const Vertex& b) + { + return (b.p - a.p).dot(basePoint) > 0.0; + } +}; + +struct VertexPairComparator +{ + VertexPairComparator(PxVec3 base = PxVec3()) : basePoint(base) {}; + PxVec3 basePoint; + bool operator()(const std::pair<Vertex, Vertex>& a, const std::pair<Vertex, Vertex>& b) + { + return (b.first.p - a.first.p).dot(basePoint) > 0.0; + } +}; + +int32_t BooleanEvaluator::isPointContainedInMesh(Mesh* msh, const PxVec3& point) +{ + if (msh == nullptr) + { + return 0; + } + DummyAccelerator dmAccel(msh->getFacetCount()); + mAcceleratorA = &dmAccel; + return vertexMeshStatus30(point, msh); + +} + +int32_t BooleanEvaluator::isPointContainedInMesh(Mesh* msh, SpatialAccelerator* spAccel, const PxVec3& point) +{ + if (msh == nullptr) + { + return 0; + } + mAcceleratorA = spAccel; + return vertexMeshStatus30(point, msh); +} + + +bool shouldSwap(const PxVec3& a, const PxVec3& b) +{ + if (a.x < b.x) return false; + if (a.x > b.x) return true; + + if (a.y < b.y) return false; + if (a.y > b.y) return true; + + if (a.z < b.z) return false; + if (a.z > b.z) return true; + + return false; +} + +void BooleanEvaluator::buildFaceFaceIntersections(BooleanConf mode) +{ + int32_t statusValue = 0; + int32_t inclusionValue = 0; + + std::vector<std::pair<Vertex, Vertex> > retainedStarts; + std::vector<std::pair<Vertex, Vertex>> retainedEnds; + VertexPairComparator comp; + + Vertex newPointA; + Vertex newPointB; + + Vertex* meshAPoints = mMeshA->getVertices(); + Vertex* meshBPoints = mMeshB->getVertices(); + EdgeWithParent newEdge; + mEdgeFacetIntersectionData12.clear(); + mEdgeFacetIntersectionData21.clear(); + + mEdgeFacetIntersectionData12.resize(mMeshA->getFacetCount()); + mEdgeFacetIntersectionData21.resize(mMeshB->getFacetCount()); + + for (uint32_t facetB = 0; facetB < mMeshB->getFacetCount(); ++facetB) + { + mAcceleratorA->setState(mMeshB->getVertices(), mMeshB->getEdges(), *mMeshB->getFacet(facetB)); + int32_t facetA = mAcceleratorA->getNextFacet(); + while (facetA != -1) + { + Edge* facetBEdges = mMeshB->getEdges() + mMeshB->getFacet(facetB)->firstEdgeNumber; + Edge* facetAEdges = mMeshA->getEdges() + mMeshA->getFacet(facetA)->firstEdgeNumber; + Edge* fbe = facetBEdges; + Edge* fae = facetAEdges; + retainedStarts.clear(); + retainedEnds.clear(); + PxVec3 compositeEndPoint(0, 0, 0); + PxVec3 compositeStartPoint(0, 0, 0); + uint32_t facetAEdgeCount = mMeshA->getFacet(facetA)->edgesCount; + uint32_t facetBEdgeCount = mMeshB->getFacet(facetB)->edgesCount; + int32_t ic = 0; + for (uint32_t i = 0; i < facetAEdgeCount; ++i) + { + if (shouldSwap(meshAPoints[fae->e].p, meshAPoints[fae->s].p)) + { + statusValue = -edgeFacetIntersection12(meshAPoints[fae->e], meshAPoints[fae->s], mMeshB->getVertices(), facetBEdges, facetBEdgeCount, newPointA, newPointB); + } + else + { + statusValue = edgeFacetIntersection12(meshAPoints[fae->s], meshAPoints[fae->e], mMeshB->getVertices(), facetBEdges, facetBEdgeCount, newPointA, newPointB); + } + inclusionValue = -inclusionValueEdgeFace(mode, statusValue); + if (inclusionValue > 0) + { + for (ic = 0; ic < inclusionValue; ++ic) + { + retainedEnds.push_back(std::make_pair(newPointA, newPointB)); + compositeEndPoint += newPointA.p; + } + mEdgeFacetIntersectionData12[facetA].push_back(EdgeFacetIntersectionData(i, statusValue, newPointA)); + } + if (inclusionValue < 0) + { + for (ic = 0; ic < -inclusionValue; ++ic) + { + retainedStarts.push_back(std::make_pair(newPointA, newPointB)); + compositeStartPoint += newPointA.p; + } + mEdgeFacetIntersectionData12[facetA].push_back(EdgeFacetIntersectionData(i, statusValue, newPointA)); + } + fae++; + } + for (uint32_t i = 0; i < facetBEdgeCount; ++i) + { + if (shouldSwap(meshBPoints[fbe->e].p, meshBPoints[fbe->s].p)) + { + statusValue = -edgeFacetIntersection21(meshBPoints[(fbe)->e], meshBPoints[(fbe)->s], mMeshA->getVertices(), facetAEdges, facetAEdgeCount, newPointA, newPointB); + } + else + { + statusValue = edgeFacetIntersection21(meshBPoints[(fbe)->s], meshBPoints[(fbe)->e], mMeshA->getVertices(), facetAEdges, facetAEdgeCount, newPointA, newPointB); + } + inclusionValue = inclusionValueEdgeFace(mode, statusValue); + if (inclusionValue > 0) + { + for (ic = 0; ic < inclusionValue; ++ic) + { + retainedEnds.push_back(std::make_pair(newPointA, newPointB)); + compositeEndPoint += newPointB.p; + } + mEdgeFacetIntersectionData21[facetB].push_back(EdgeFacetIntersectionData( i, statusValue, newPointB)); + } + if (inclusionValue < 0) + { + for (ic = 0; ic < -inclusionValue; ++ic) + { + retainedStarts.push_back(std::make_pair(newPointA, newPointB)); + compositeStartPoint += newPointB.p; + } + mEdgeFacetIntersectionData21[facetB].push_back(EdgeFacetIntersectionData(i, statusValue, newPointB)); + } + fbe++; + } + if (retainedStarts.size() != retainedEnds.size()) + { + NVBLAST_LOG_ERROR(mLoggingCallback, "Not equal number of starting and ending vertices! Probably input mesh has open edges."); + return; + } + if (retainedStarts.size() > 1) + { + comp.basePoint = compositeEndPoint - compositeStartPoint; + std::sort(retainedStarts.begin(), retainedStarts.end(), comp); + std::sort(retainedEnds.begin(), retainedEnds.end(), comp); + } + for (uint32_t rv = 0; rv < retainedStarts.size(); ++rv) + { + newEdge.s = addIfNotExist(retainedStarts[rv].first); + newEdge.e = addIfNotExist(retainedEnds[rv].first); + newEdge.parent = facetA; + addEdgeIfValid(newEdge); + newEdge.parent = facetB + mMeshA->getFacetCount(); + newEdge.e = addIfNotExist(retainedStarts[rv].second); + newEdge.s = addIfNotExist(retainedEnds[rv].second); + addEdgeIfValid(newEdge); + } + facetA = mAcceleratorA->getNextFacet(); + } // while (*iter != -1) + + } // for (uint32_t facetB = 0; facetB < mMeshB->getFacetCount(); ++facetB) + + + +} + + +void BooleanEvaluator::buildFastFaceFaceIntersection(BooleanConf mode) +{ + int32_t statusValue = 0; + int32_t inclusionValue = 0; + + std::vector<std::pair<Vertex, Vertex> > retainedStarts; + std::vector<std::pair<Vertex, Vertex>> retainedEnds; + VertexPairComparator comp; + + Vertex newPointA; + Vertex newPointB; + + Vertex* meshAPoints = mMeshA->getVertices(); + EdgeWithParent newEdge; + + mEdgeFacetIntersectionData12.clear(); + mEdgeFacetIntersectionData21.clear(); + + mEdgeFacetIntersectionData12.resize(mMeshA->getFacetCount()); + mEdgeFacetIntersectionData21.resize(mMeshB->getFacetCount()); + + for (uint32_t facetA = 0; facetA < mMeshA->getFacetCount(); ++facetA) + { + Edge* facetAEdges = mMeshA->getEdges() + mMeshA->getFacet(facetA)->firstEdgeNumber; + int32_t facetB = 0; + Edge* facetBEdges = mMeshB->getEdges() + mMeshB->getFacet(facetB)->firstEdgeNumber; + Edge* fae = facetAEdges; + retainedStarts.clear(); + retainedEnds.clear(); + PxVec3 compositeEndPoint(0, 0, 0); + PxVec3 compositeStartPoint(0, 0, 0); + uint32_t facetAEdgeCount = mMeshA->getFacet(facetA)->edgesCount; + uint32_t facetBEdgeCount = mMeshB->getFacet(facetB)->edgesCount; + int32_t ic = 0; + for (uint32_t i = 0; i < facetAEdgeCount; ++i) + { + if (shouldSwap(meshAPoints[fae->e].p, meshAPoints[fae->s].p)) + { + statusValue = -edgeFacetIntersection12(meshAPoints[fae->e], meshAPoints[fae->s], mMeshB->getVertices(), facetBEdges, facetBEdgeCount, newPointA, newPointB); + } + else + { + statusValue = edgeFacetIntersection12(meshAPoints[fae->s], meshAPoints[fae->e], mMeshB->getVertices(), facetBEdges, facetBEdgeCount, newPointA, newPointB); + } + inclusionValue = -inclusionValueEdgeFace(mode, statusValue); + if (inclusionValue > 0) + { + for (ic = 0; ic < inclusionValue; ++ic) + { + retainedEnds.push_back(std::make_pair(newPointA, newPointB)); + compositeEndPoint += newPointA.p; + } + mEdgeFacetIntersectionData12[facetA].push_back(EdgeFacetIntersectionData(i, statusValue, newPointA)); + } + if (inclusionValue < 0) + { + for (ic = 0; ic < -inclusionValue; ++ic) + { + retainedStarts.push_back(std::make_pair(newPointA, newPointB)); + compositeStartPoint += newPointA.p; + } + mEdgeFacetIntersectionData12[facetA].push_back(EdgeFacetIntersectionData(i, statusValue, newPointA)); + } + fae++; + } + if (retainedStarts.size() != retainedEnds.size()) + { + NVBLAST_LOG_ERROR(mLoggingCallback, "Not equal number of starting and ending vertices! Probably input mesh has open edges."); + return; + } + if (retainedStarts.size() > 1) + { + comp.basePoint = compositeEndPoint - compositeStartPoint; + std::sort(retainedStarts.begin(), retainedStarts.end(), comp); + std::sort(retainedEnds.begin(), retainedEnds.end(), comp); + } + for (uint32_t rv = 0; rv < retainedStarts.size(); ++rv) + { + newEdge.s = addIfNotExist(retainedStarts[rv].first); + newEdge.e = addIfNotExist(retainedEnds[rv].first); + newEdge.parent = facetA; + addEdgeIfValid(newEdge); + newEdge.parent = facetB + mMeshA->getFacetCount(); + newEdge.e = addIfNotExist(retainedStarts[rv].second); + newEdge.s = addIfNotExist(retainedEnds[rv].second); + addEdgeIfValid(newEdge); + } + } + +} + + + +void BooleanEvaluator::collectRetainedPartsFromA(BooleanConf mode) +{ + + int32_t statusValue = 0; + int32_t inclusionValue = 0; + Vertex* vertices = mMeshA->getVertices(); + Vertex newPoint; + VertexComparator comp; + PxBounds3& bMeshBoudning = mMeshB->getBoundingBox(); + Edge* facetEdges = mMeshA->getEdges(); + std::vector<Vertex> retainedStartVertices; + std::vector<Vertex> retainedEndVertices; + retainedStartVertices.reserve(255); + retainedEndVertices.reserve(255); + int32_t ic = 0; + for (uint32_t facetId = 0; facetId < mMeshA->getFacetCount(); ++facetId) + { + retainedStartVertices.clear(); + retainedEndVertices.clear(); + for (uint32_t i = 0; i < mMeshA->getFacet(facetId)->edgesCount; ++i) + { + PxVec3 compositeEndPoint(0, 0, 0); + PxVec3 compositeStartPoint(0, 0, 0); + + int32_t lastPos = static_cast<int32_t>(retainedEndVertices.size()); + /* Test start and end point of edge against mesh */ + if (bMeshBoudning.contains(vertices[facetEdges->s].p)) + { + statusValue = vertexMeshStatus03(vertices[facetEdges->s].p, mMeshB); + } + else + { + statusValue = 0; + } + inclusionValue = -inclusionValue03(mode, statusValue); + + if (inclusionValue > 0) + { + for (ic = 0; ic < inclusionValue; ++ic) + { + retainedEndVertices.push_back(vertices[facetEdges->s]); + compositeEndPoint += vertices[facetEdges->s].p; + } + } + else + { + if (inclusionValue < 0) + { + for (ic = 0; ic < -inclusionValue; ++ic) + { + retainedStartVertices.push_back(vertices[facetEdges->s]); + compositeStartPoint += vertices[facetEdges->s].p; + } + } + } + + if (bMeshBoudning.contains(vertices[facetEdges->e].p)) + { + statusValue = vertexMeshStatus03(vertices[facetEdges->e].p, mMeshB); + } + else + { + statusValue = 0; + } + inclusionValue = inclusionValue03(mode, statusValue); + if (inclusionValue > 0) + { + for (ic = 0; ic < inclusionValue; ++ic) + { + retainedEndVertices.push_back(vertices[facetEdges->e]); + compositeEndPoint += vertices[facetEdges->e].p; + } + } + else + { + if (inclusionValue < 0) + { + for (ic = 0; ic < -inclusionValue; ++ic) + { + retainedStartVertices.push_back(vertices[facetEdges->e]); + compositeStartPoint += vertices[facetEdges->e].p; + } + } + } + /* Test edge intersection with mesh*/ + for (uint32_t intrs = 0; intrs < mEdgeFacetIntersectionData12[facetId].size(); ++intrs) + { + EdgeFacetIntersectionData& intr = mEdgeFacetIntersectionData12[facetId][intrs]; + if (intr.edId != (int32_t)i) + continue; + newPoint = intr.intersectionPoint; + inclusionValue = inclusionValueEdgeFace(mode, intr.intersectionType); + + if (inclusionValue > 0) + { + for (ic = 0; ic < inclusionValue; ++ic) + { + retainedEndVertices.push_back(newPoint); + compositeEndPoint += newPoint.p; + } + } + else + { + if (inclusionValue < 0) + { + for (ic = 0; ic < -inclusionValue; ++ic) + { + retainedStartVertices.push_back(newPoint); + compositeStartPoint += newPoint.p; + } + } + } + } + facetEdges++; + if (retainedStartVertices.size() != retainedEndVertices.size()) + { + NVBLAST_LOG_ERROR(mLoggingCallback, "Not equal number of starting and ending vertices! Probably input mesh has open edges."); + return; + } + if (retainedEndVertices.size() > 1) + { + comp.basePoint = compositeEndPoint - compositeStartPoint; + std::sort(retainedStartVertices.begin() + lastPos, retainedStartVertices.end(), comp); + std::sort(retainedEndVertices.begin() + lastPos, retainedEndVertices.end(), comp); + } + } + + + EdgeWithParent newEdge; + for (uint32_t rv = 0; rv < retainedStartVertices.size(); ++rv) + { + newEdge.s = addIfNotExist(retainedStartVertices[rv]); + newEdge.e = addIfNotExist(retainedEndVertices[rv]); + newEdge.parent = facetId; + addEdgeIfValid(newEdge); + } + } + + return; +} + +void BooleanEvaluator::collectRetainedPartsFromB(BooleanConf mode) +{ + int32_t statusValue = 0; + int32_t inclusionValue = 0; + Vertex* vertices = mMeshB->getVertices(); + Vertex newPoint; + VertexComparator comp; + PxBounds3& aMeshBoudning = mMeshA->getBoundingBox(); + Edge* facetEdges = mMeshB->getEdges(); + std::vector<Vertex> retainedStartVertices; + std::vector<Vertex> retainedEndVertices; + retainedStartVertices.reserve(255); + retainedEndVertices.reserve(255); + int32_t ic = 0; + for (uint32_t facetId = 0; facetId < mMeshB->getFacetCount(); ++facetId) + { + retainedStartVertices.clear(); + retainedEndVertices.clear(); + for (uint32_t i = 0; i < mMeshB->getFacet(facetId)->edgesCount; ++i) + { + PxVec3 compositeEndPoint(0, 0, 0); + PxVec3 compositeStartPoint(0, 0, 0); + int32_t lastPos = static_cast<int32_t>(retainedEndVertices.size()); + if (aMeshBoudning.contains(vertices[facetEdges->s].p)) + { + statusValue = vertexMeshStatus30(vertices[facetEdges->s].p, mMeshA); + } + else + { + statusValue = 0; + } + inclusionValue = -inclusionValue30(mode, statusValue); + + if (inclusionValue > 0) + { + for (ic = 0; ic < inclusionValue; ++ic) + { + retainedEndVertices.push_back(vertices[facetEdges->s]); + compositeEndPoint += vertices[facetEdges->s].p; + } + + } + else + { + if (inclusionValue < 0) + { + for (ic = 0; ic < -inclusionValue; ++ic) + { + retainedStartVertices.push_back(vertices[facetEdges->s]); + compositeStartPoint += vertices[facetEdges->s].p; + } + + } + } + + if (aMeshBoudning.contains(vertices[facetEdges->e].p)) + { + statusValue = vertexMeshStatus30(vertices[facetEdges->e].p, mMeshA); + } + else + { + statusValue = 0; + } + inclusionValue = inclusionValue30(mode, statusValue); + if (inclusionValue > 0) + { + for (ic = 0; ic < inclusionValue; ++ic) + { + retainedEndVertices.push_back(vertices[facetEdges->e]); + compositeEndPoint += vertices[facetEdges->e].p; + } + + } + else + { + if (inclusionValue < 0) + { + for (ic = 0; ic < -inclusionValue; ++ic) + { + retainedStartVertices.push_back(vertices[facetEdges->e]); + compositeStartPoint += vertices[facetEdges->e].p; + } + + } + } + for (uint32_t intrs = 0; intrs < mEdgeFacetIntersectionData21[facetId].size(); ++intrs) + { + EdgeFacetIntersectionData& intr = mEdgeFacetIntersectionData21[facetId][intrs]; + if (intr.edId != (int32_t)i) + continue; + newPoint = intr.intersectionPoint; + inclusionValue = inclusionValueEdgeFace(mode, intr.intersectionType); + + if (inclusionValue > 0) + { + for (ic = 0; ic < inclusionValue; ++ic) + { + retainedEndVertices.push_back(newPoint); + compositeEndPoint += newPoint.p; + } + } + else + { + if (inclusionValue < 0) + { + for (ic = 0; ic < -inclusionValue; ++ic) + { + retainedStartVertices.push_back(newPoint); + compositeStartPoint += newPoint.p; + } + } + } + } + facetEdges++; + if (retainedStartVertices.size() != retainedEndVertices.size()) + { + NVBLAST_LOG_ERROR(mLoggingCallback, "Not equal number of starting and ending vertices! Probably input mesh has open edges."); + return; + } + if (retainedEndVertices.size() - lastPos > 1) + { + comp.basePoint = compositeEndPoint - compositeStartPoint; + std::sort(retainedStartVertices.begin() + lastPos, retainedStartVertices.end(), comp); + std::sort(retainedEndVertices.begin() + lastPos, retainedEndVertices.end(), comp); + } + } + EdgeWithParent newEdge; + for (uint32_t rv = 0; rv < retainedStartVertices.size(); ++rv) + { + newEdge.s = addIfNotExist(retainedStartVertices[rv]); + newEdge.e = addIfNotExist(retainedEndVertices[rv]); + newEdge.parent = facetId + mMeshA->getFacetCount(); + addEdgeIfValid(newEdge); + } + } + return; +} + +bool EdgeWithParentSortComp(const EdgeWithParent& a, const EdgeWithParent& b) +{ + return a.parent < b.parent; +} + + +void BooleanEvaluator::performBoolean(Mesh* meshA, Mesh* meshB, SpatialAccelerator* spAccelA, SpatialAccelerator* spAccelB, BooleanConf mode) +{ + reset(); + mMeshA = meshA; + mMeshB = meshB; + mAcceleratorA = spAccelA; + mAcceleratorB = spAccelB; + buildFaceFaceIntersections(mode); + collectRetainedPartsFromA(mode); + collectRetainedPartsFromB(mode); + mAcceleratorA = nullptr; + mAcceleratorB = nullptr; +} + +void BooleanEvaluator::performBoolean(Mesh* meshA, Mesh* meshB, BooleanConf mode) +{ + reset(); + mMeshA = meshA; + mMeshB = meshB; + DummyAccelerator ac = DummyAccelerator(mMeshA->getFacetCount()); + DummyAccelerator bc = DummyAccelerator(mMeshB->getFacetCount()); + performBoolean(meshA, meshB, &ac, &bc, mode); +} + + +void BooleanEvaluator::performFastCutting(Mesh* meshA, Mesh* meshB, SpatialAccelerator* spAccelA, SpatialAccelerator* spAccelB, BooleanConf mode) +{ + reset(); + mMeshA = meshA; + mMeshB = meshB; + mAcceleratorA = spAccelA; + mAcceleratorB = spAccelB; + buildFastFaceFaceIntersection(mode); + collectRetainedPartsFromA(mode); + mAcceleratorA = nullptr; + mAcceleratorB = nullptr; +} + +void BooleanEvaluator::performFastCutting(Mesh* meshA, Mesh* meshB, BooleanConf mode) +{ + reset(); + mMeshA = meshA; + mMeshB = meshB; + DummyAccelerator ac = DummyAccelerator(mMeshA->getFacetCount()); + DummyAccelerator bc = DummyAccelerator(mMeshB->getFacetCount()); + performFastCutting(meshA, meshB, &ac, &bc, mode); +} + + + + +BooleanEvaluator::BooleanEvaluator(NvBlastLog loggingCallback) +{ + mMeshA = nullptr; + mMeshB = nullptr; + mAcceleratorA = nullptr; + mAcceleratorB = nullptr; + mLoggingCallback = loggingCallback; +} +BooleanEvaluator::~BooleanEvaluator() +{ + reset(); +} + + + +Mesh* BooleanEvaluator::createNewMesh() +{ + if (mEdgeAggregate.size() == 0) + { + return nullptr; + } + std::sort(mEdgeAggregate.begin(), mEdgeAggregate.end(), EdgeWithParentSortComp); + std::vector<Facet> newFacets; + std::vector<Edge> newEdges(mEdgeAggregate.size()); + int32_t lastPos = 0; + int32_t lastParent = mEdgeAggregate[0].parent; + uint32_t collected = 0; + int32_t userData = 0; + for (uint32_t i = 0; i < mEdgeAggregate.size(); ++i) + { + if (mEdgeAggregate[i].parent != lastParent) + { + if (lastParent < (int32_t)mMeshA->getFacetCount()) + { + userData = mMeshA->getFacet(lastParent)->userData; + } + else + { + userData = mMeshB->getFacet(lastParent - mMeshA->getFacetCount())->userData; + } + newFacets.push_back(Facet(lastPos, collected, userData)); + lastPos = i; + lastParent = mEdgeAggregate[i].parent; + collected = 0; + } + collected++; + newEdges[i].s = mEdgeAggregate[i].s; + newEdges[i].e = mEdgeAggregate[i].e; + } + int32_t pr = lastParent - mMeshA->getFacetCount(); + if (lastParent < (int32_t)mMeshA->getFacetCount()) + { + userData = mMeshA->getFacet(lastParent)->userData; + } + else + { + userData = mMeshB->getFacet(pr)->userData; + } + newFacets.push_back(Facet(lastPos, collected, userData)); + return new Mesh(&mVerticesAggregate[0], &newEdges[0], &newFacets[0], static_cast<uint32_t>(mVerticesAggregate.size()), static_cast<uint32_t>(mEdgeAggregate.size()), static_cast<uint32_t>(newFacets.size())); +} + +void BooleanEvaluator::reset() +{ + mMeshA = nullptr; + mMeshB = nullptr; + mAcceleratorA = nullptr; + mAcceleratorB = nullptr; + mEdgeAggregate.clear(); + mVerticesAggregate.clear(); + mEdgeFacetIntersectionData12.clear(); + mEdgeFacetIntersectionData21.clear(); +} + +} // namespace Blast +} // namespace Nv diff --git a/NvBlast/sdk/extensions/authoring/source/NvBlastExtAuthoringBooleanTool.h b/NvBlast/sdk/extensions/authoring/source/NvBlastExtAuthoringBooleanTool.h new file mode 100644 index 0000000..0b0b73a --- /dev/null +++ b/NvBlast/sdk/extensions/authoring/source/NvBlastExtAuthoringBooleanTool.h @@ -0,0 +1,197 @@ +/* +* Copyright (c) 2016-2017, 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. +*/ + +#ifndef NVBLASTEXTAUTHORINGBOOLEANTOOL_H +#define NVBLASTEXTAUTHORINGBOOLEANTOOL_H + +#include "NvBlastExtAuthoringTypes.h" +#include "NvBlastExtAuthoringInternalCommon.h" +#include <vector> +#include <map> +#include "NvBlastTypes.h" + + +namespace Nv +{ +namespace Blast +{ + +class Mesh; + +/** + Boolean tool config, used to perform different operations: UNION, INTERSECTION, DIFFERENCE +*/ +struct BooleanConf +{ + int32_t ca, cb, ci; + BooleanConf(int32_t a, int32_t b, int32_t c) : ca(a), cb(b), ci(c) + { + } +}; + + +namespace BooleanConfigurations +{ + /** + Creates boolean tool configuration to perform intersection of meshes A and B. + */ +inline BooleanConf BOOLEAN_INTERSECION() +{ + return BooleanConf(0, 0, 1); +} + +/** + Creates boolean tool configuration to perform union of meshes A and B. +*/ +inline BooleanConf BOOLEAN_UNION() +{ + return BooleanConf(1, 1, -1); +} +/** + Creates boolean tool configuration to perform difference of meshes(A - B). +*/ +inline BooleanConf BOOLEAN_DIFFERENCE() +{ + return BooleanConf(1, 0, -1); +} +} + +/** + Structure which holds information about intersection facet with edge. +*/ +struct EdgeFacetIntersectionData +{ + int32_t edId; + int32_t intersectionType; + Vertex intersectionPoint; + EdgeFacetIntersectionData(int32_t edId, int32_t intersType, Vertex& inters) : edId(edId), intersectionType(intersType), intersectionPoint(inters) + { } + EdgeFacetIntersectionData(int32_t edId) : edId(edId) + { } + bool operator<(const EdgeFacetIntersectionData& b) const + { + return edId < b.edId; + } +}; + + +class SpatialAccelerator; + +/** + Tool for performing boolean operations on polygonal meshes. + Tool supports only closed meshes. Performing boolean on meshes with holes can lead to unexpected behavior, e.g. holes in result geometry. +*/ +class BooleanEvaluator +{ + +public: + BooleanEvaluator(NvBlastLog logCallback = nullptr); + ~BooleanEvaluator(); + + /** + Perform boolean operation on two polygonal meshes (A and B). + \param[in] meshA Mesh A + \param[in] meshB Mesh B + \param[in] spAccelA Acceleration structure for mesh A + \param[in] spAccelB Acceleration structure for mesh B + \param[in] mode Boolean operation type + */ + void performBoolean(Mesh* meshA, Mesh* meshB, SpatialAccelerator* spAccelA, SpatialAccelerator* spAccelB, BooleanConf mode); + + /** + Perform boolean operation on two polygonal meshes (A and B). + \param[in] meshA Mesh A + \param[in] meshB Mesh B + \param[in] mode Boolean operation type + */ + void performBoolean(Mesh* meshA, Mesh* meshB, BooleanConf mode); + + /** + Perform cutting of mesh with some large box, which represents cutting plane. This method skips part of intersetion computations, so + should be used ONLY with cutting box, received from getBigBox(...) method from NvBlastExtAuthoringMesh.h. For cutting use only BOOLEAN_INTERSECTION or BOOLEAN_DIFFERENCE mode. + \param[in] meshA Mesh A + \param[in] meshB Cutting box + \param[in] spAccelA Acceleration structure for mesh A + \param[in] spAccelB Acceleration structure for cutting box + \param[in] mode Boolean operation type + */ + void performFastCutting(Mesh* meshA, Mesh* meshB, SpatialAccelerator* spAccelA, SpatialAccelerator* spAccelB, BooleanConf mode); + + /** + Perform cutting of mesh with some large box, which represents cutting plane. This method skips part of intersetion computations, so + should be used ONLY with cutting box, received from getBigBox(...) method from NvBlastExtAuthoringMesh.h. For cutting use only BOOLEAN_INTERSECTION or BOOLEAN_DIFFERENCE mode. + \param[in] meshA Mesh A + \param[in] meshB Cutting box + \param[in] mode Boolean operation type + */ + void performFastCutting(Mesh* meshA, Mesh* meshB, BooleanConf mode); + + /** + Test whether point contained in mesh. + \param[in] mesh Mesh geometry + \param[in] point Point which should be tested + \return not 0 if point is inside of mesh + */ + int32_t isPointContainedInMesh(Mesh* mesh, const physx::PxVec3& point); + /** + Test whether point contained in mesh. + \param[in] mesh Mesh geometry + \param[in] spAccel Acceleration structure for mesh + \param[in] point Point which should be tested + \return not 0 if point is inside of mesh + */ + int32_t isPointContainedInMesh(Mesh* mesh, SpatialAccelerator* spAccel, const physx::PxVec3& point); + + + /** + Generates result polygon mesh after performing boolean operation. + \return If not nullptr - result mesh geometry. + */ + Mesh* createNewMesh(); + + /** + Reset tool state. + */ + void reset(); + +private: + + void buildFaceFaceIntersections(BooleanConf); + void buildFastFaceFaceIntersection(BooleanConf); + void collectRetainedPartsFromA(BooleanConf mode); + void collectRetainedPartsFromB(BooleanConf mode); + + int32_t addIfNotExist(Vertex& p); + void addEdgeIfValid(EdgeWithParent& ed); +private: + + int32_t vertexMeshStatus03(const physx::PxVec3& p, Mesh* mesh); + int32_t vertexMeshStatus30(const physx::PxVec3& p, Mesh* mesh); + + Mesh* mMeshA; + Mesh* mMeshB; + + SpatialAccelerator* mAcceleratorA; + SpatialAccelerator* mAcceleratorB; + + std::vector<EdgeWithParent> mEdgeAggregate; + std::vector<Vertex> mVerticesAggregate; + + std::vector<std::vector<EdgeFacetIntersectionData> > mEdgeFacetIntersectionData12; + std::vector<std::vector<EdgeFacetIntersectionData> > mEdgeFacetIntersectionData21; + + NvBlastLog mLoggingCallback; +}; + +} // namespace Blast +} // namespace Nv + + +#endif // ifndef NVBLASTEXTAUTHORINGBOOLEANTOOL_H diff --git a/NvBlast/sdk/extensions/authoring/source/NvBlastExtAuthoringCollisionBuilder.cpp b/NvBlast/sdk/extensions/authoring/source/NvBlastExtAuthoringCollisionBuilder.cpp new file mode 100644 index 0000000..becdce9 --- /dev/null +++ b/NvBlast/sdk/extensions/authoring/source/NvBlastExtAuthoringCollisionBuilder.cpp @@ -0,0 +1,279 @@ +/* +* Copyright (c) 2016-2017, 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 "NvBlastExtAuthoringCollisionBuilder.h" +#include <PxConvexMesh.h> +#include <PxVec3.h> +#include <PxBounds3.h> +#include "PxPhysics.h" +#include "cooking/PxCooking.h" +#include <NvBlastExtApexSharedParts.h> +#include <NvBlastExtAuthoringInternalCommon.h> + +#include <NvBlastExtAuthoringBooleanTool.h> +#include <NvBlastExtAuthoringMesh.h> + +using namespace physx; + +namespace Nv +{ +namespace Blast +{ + +void ConvexMeshBuilder::buildCollisionGeometry(const std::vector<PxVec3>& vData, CollisionHull& output) +{ + std::vector<physx::PxVec3> vertexData = vData; + + PxConvexMeshDesc convexMeshDescr; + PxConvexMesh* resultConvexMesh; + PxBounds3 bounds; + // Scale chunk to unit cube size, to avoid numerical errors + bounds.setEmpty(); + for (uint32_t i = 0; i < vertexData.size(); ++i) + { + bounds.include(vertexData[i]); + } + PxVec3 bbCenter = bounds.getCenter(); + float scale = PxMax(PxAbs(bounds.getExtents(0)), PxMax(PxAbs(bounds.getExtents(1)), PxAbs(bounds.getExtents(2)))); + for (uint32_t i = 0; i < vertexData.size(); ++i) + { + vertexData[i] = vertexData[i] - bbCenter; + vertexData[i] *= (1.0f / scale); + } + bounds.setEmpty(); + for (uint32_t i = 0; i < vertexData.size(); ++i) + { + bounds.include(vertexData[i]); + } + convexMeshDescr.points.data = vertexData.data(); + convexMeshDescr.points.stride = sizeof(PxVec3); + convexMeshDescr.points.count = (uint32_t)vertexData.size(); + convexMeshDescr.flags = PxConvexFlag::eCOMPUTE_CONVEX; + resultConvexMesh = mCooking->createConvexMesh(convexMeshDescr, *mInsertionCallback); + if (!resultConvexMesh) + { + vertexData.clear(); + vertexData.push_back(bounds.minimum); + vertexData.push_back(PxVec3(bounds.minimum.x, bounds.maximum.y, bounds.minimum.z)); + vertexData.push_back(PxVec3(bounds.maximum.x, bounds.maximum.y, bounds.minimum.z)); + vertexData.push_back(PxVec3(bounds.maximum.x, bounds.minimum.y, bounds.minimum.z)); + vertexData.push_back(PxVec3(bounds.minimum.x, bounds.minimum.y, bounds.maximum.z)); + vertexData.push_back(PxVec3(bounds.minimum.x, bounds.maximum.y, bounds.maximum.z)); + vertexData.push_back(PxVec3(bounds.maximum.x, bounds.maximum.y, bounds.maximum.z)); + vertexData.push_back(PxVec3(bounds.maximum.x, bounds.minimum.y, bounds.maximum.z)); + convexMeshDescr.points.data = vertexData.data(); + convexMeshDescr.points.count = (uint32_t)vertexData.size(); + resultConvexMesh = mCooking->createConvexMesh(convexMeshDescr, *mInsertionCallback); + } + output.polygonData.resize(resultConvexMesh->getNbPolygons()); + output.points.resize(resultConvexMesh->getNbVertices()); + int32_t indicesCount = 0; + PxHullPolygon hPoly; + for (uint32_t i = 0; i < resultConvexMesh->getNbPolygons(); ++i) + { + CollisionHull::HullPolygon& pd = output.polygonData[i]; + resultConvexMesh->getPolygonData(i, hPoly); + pd.mIndexBase = hPoly.mIndexBase; + pd.mNbVerts = hPoly.mNbVerts; + pd.mPlane[0] = hPoly.mPlane[0]; + pd.mPlane[1] = hPoly.mPlane[1]; + pd.mPlane[2] = hPoly.mPlane[2]; + pd.mPlane[3] = hPoly.mPlane[3]; + + pd.mPlane[0] /= scale; + pd.mPlane[1] /= scale; + pd.mPlane[2] /= scale; + pd.mPlane[3] -= (pd.mPlane[0] * bbCenter.x + pd.mPlane[1] * bbCenter.y + pd.mPlane[2] * bbCenter.z); + float length = sqrt(pd.mPlane[0] * pd.mPlane[0] + pd.mPlane[1] * pd.mPlane[1] + pd.mPlane[2] * pd.mPlane[2]); + pd.mPlane[0] /= length; + pd.mPlane[1] /= length; + pd.mPlane[2] /= length; + pd.mPlane[3] /= length; + indicesCount = PxMax(indicesCount, pd.mIndexBase + pd.mNbVerts); + } + output.indices.resize(indicesCount); + for (uint32_t i = 0; i < resultConvexMesh->getNbVertices(); ++i) + { + PxVec3 p = resultConvexMesh->getVertices()[i] * scale + bbCenter; + output.points[i] = p; + } + for (int32_t i = 0; i < indicesCount; ++i) + { + output.indices[i] = resultConvexMesh->getIndexBuffer()[i]; + } + resultConvexMesh->release(); +} + +void ConvexMeshBuilder::trimCollisionGeometry(std::vector<CollisionHull>& in, const std::vector<uint32_t>& chunkDepth) +{ + std::vector<std::vector<PxPlane> > chunkMidplanes(in.size()); + std::vector<PxVec3> centers(in.size()); + std::vector<PxBounds3> hullsBounds(in.size()); + for (uint32_t i = 0; i < in.size(); ++i) + { + hullsBounds[i].setEmpty(); + centers[i] = PxVec3(0, 0, 0); + for (uint32_t p = 0; p < in[i].points.size(); ++p) + { + centers[i] += in[i].points[p]; + hullsBounds[i].include(in[i].points[p]); + } + centers[i] = hullsBounds[i].getCenter(); + } + + Separation params; + for (uint32_t hull = 0; hull < in.size(); ++hull) + { + for (uint32_t hull2 = hull + 1; hull2 < in.size(); ++hull2) + { + if (chunkDepth[hull] != chunkDepth[hull2]) + { + continue; + } + if (importerHullsInProximityApexFree(in[hull].points, hullsBounds[hull], PxTransform(PxIdentity), PxVec3(1, 1, 1), + in[hull2].points, hullsBounds[hull2], PxTransform(PxIdentity), PxVec3(1, 1, 1), 0.0, ¶ms) == false) + { + continue; + } + PxVec3 c1 = centers[hull]; + PxVec3 c2 = centers[hull2]; + float d = FLT_MAX; + PxVec3 n1; + PxVec3 n2; + for (uint32_t p = 0; p < in[hull].points.size(); ++p) + { + float ld = (in[hull].points[p] - c2).magnitude(); + if (ld < d) + { + n1 = in[hull].points[p]; + d = ld; + } + } + d = FLT_MAX; + for (uint32_t p = 0; p < in[hull2].points.size(); ++p) + { + float ld = (in[hull2].points[p] - c1).magnitude(); + if (ld < d) + { + n2 = in[hull2].points[p]; + d = ld; + } + } + + PxVec3 dir = c2 - c1; + + PxPlane pl = PxPlane((n1 + n2) * 0.5, dir.getNormalized()); + chunkMidplanes[hull].push_back(pl); + PxPlane pl2 = PxPlane((n1 + n2) * 0.5, -dir.getNormalized()); + chunkMidplanes[hull2].push_back(pl2); + } + } + std::vector<PxVec3> hPoints; + for (uint32_t i = 0; i < in.size(); ++i) + { + std::vector<Facet> facets; + std::vector<Vertex> vertices; + std::vector<Edge> edges; + for (uint32_t fc = 0; fc < in[i].polygonData.size(); ++fc) + { + Facet nFc; + nFc.firstEdgeNumber = edges.size(); + uint32_t n = in[i].polygonData[fc].mNbVerts; + for (uint32_t ed = 0; ed < n; ++ed) + { + uint32_t vr1 = in[i].indices[(ed) + in[i].polygonData[fc].mIndexBase]; + uint32_t vr2 = in[i].indices[(ed + 1) % n + in[i].polygonData[fc].mIndexBase]; + edges.push_back(Edge(vr1, vr2)); + } + nFc.edgesCount = n; + facets.push_back(nFc); + } + vertices.resize(in[i].points.size()); + for (uint32_t vr = 0; vr < in[i].points.size(); ++vr) + { + vertices[vr].p = in[i].points[vr]; + } + Mesh* hullMesh = new Mesh(vertices.data(), edges.data(), facets.data(), vertices.size(), edges.size(), facets.size()); + BooleanEvaluator evl; + Mesh* cuttingMesh = getCuttingBox(PxVec3(0, 0, 0), PxVec3(0, 0, 1), 40, 0); + for (uint32_t p = 0; p < chunkMidplanes[i].size(); ++p) + { + PxPlane& pl = chunkMidplanes[i][p]; + setCuttingBox(pl.pointInPlane(), pl.n.getNormalized(), cuttingMesh, 60, 0); + evl.performFastCutting(hullMesh, cuttingMesh, BooleanConfigurations::BOOLEAN_DIFFERENCE()); + Mesh* result = evl.createNewMesh(); + if (result == nullptr) + { + break; + } + delete hullMesh; + hullMesh = result; + } + delete cuttingMesh; + if (hullMesh == nullptr) + { + continue; + } + hPoints.clear(); + hPoints.resize(hullMesh->getVerticesCount()); + for (uint32_t v = 0; v < hullMesh->getVerticesCount(); ++v) + { + hPoints[v] = hullMesh->getVertices()[v].p; + } + delete hullMesh; + buildCollisionGeometry(hPoints, in[i]); + } +} + + +PxConvexMesh* ConvexMeshBuilder::buildConvexMesh(std::vector<PxVec3>& vertexData) +{ + CollisionHull hull; + buildCollisionGeometry(vertexData, hull); + + PxConvexMeshDesc convexMeshDescr; + convexMeshDescr.indices.data = hull.indices.data(); + convexMeshDescr.indices.count = (uint32_t)hull.indices.size(); + convexMeshDescr.indices.stride = sizeof(uint32_t); + + convexMeshDescr.points.data = hull.points.data(); + convexMeshDescr.points.count = (uint32_t)hull.points.size(); + convexMeshDescr.points.stride = sizeof(PxVec3); + + convexMeshDescr.polygons.data = hull.polygonData.data(); + convexMeshDescr.polygons.count = (uint32_t)hull.polygonData.size(); + convexMeshDescr.polygons.stride = sizeof(PxHullPolygon); + + PxConvexMesh* convexMesh = mCooking->createConvexMesh(convexMeshDescr, *mInsertionCallback); + return convexMesh; +} + +PxConvexMesh* ConvexMeshBuilder::buildConvexMesh(CollisionHull& hull) +{ + PxConvexMeshDesc convexMeshDescr; + convexMeshDescr.indices.data = hull.indices.data(); + convexMeshDescr.indices.count = (uint32_t)hull.indices.size(); + convexMeshDescr.indices.stride = sizeof(uint32_t); + + convexMeshDescr.points.data = hull.points.data(); + convexMeshDescr.points.count = (uint32_t)hull.points.size(); + convexMeshDescr.points.stride = sizeof(PxVec3); + + convexMeshDescr.polygons.data = hull.polygonData.data(); + convexMeshDescr.polygons.count = (uint32_t)hull.polygonData.size(); + convexMeshDescr.polygons.stride = sizeof(PxHullPolygon); + + PxConvexMesh* convexMesh = mCooking->createConvexMesh(convexMeshDescr, *mInsertionCallback); + return convexMesh; +} + + +} // namespace Blast +} // namespace Nv diff --git a/NvBlast/sdk/extensions/authoring/source/NvBlastExtAuthoringFractureTool.cpp b/NvBlast/sdk/extensions/authoring/source/NvBlastExtAuthoringFractureTool.cpp new file mode 100644 index 0000000..48830fe --- /dev/null +++ b/NvBlast/sdk/extensions/authoring/source/NvBlastExtAuthoringFractureTool.cpp @@ -0,0 +1,1510 @@ +/* +* Copyright (c) 2016-2017, 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 "NvBlastExtAuthoringFractureTool.h" +// This warning arises when using some stl containers with older versions of VC +// c:\program files (x86)\microsoft visual studio 12.0\vc\include\xtree(1826): warning C4702: unreachable code +#if NV_VC && NV_VC < 14 +#pragma warning(disable : 4702) +#endif +#include <queue> +#include <vector> +#include "NvBlastExtAuthoringVSA.h" +#include <float.h> +#include "NvBlastExtAuthoringTriangulator.h" +#include "NvBlastExtAuthoringBooleanTool.h" +#include "NvBlastExtAuthoringAccelerator.h" +#include "NvBlast.h" +#include "NvBlastExtAuthoringPerlinNoise.h" +#include <NvBlastAssert.h> +using namespace physx; + +#define DEFAULT_BB_ACCELARATOR_RES 10 + +namespace Nv +{ +namespace Blast +{ + +struct Halfspace_partitioning : public VSA::VS3D_Halfspace_Set +{ + std::vector<physx::PxPlane> planes; + VSA::real farthest_halfspace(VSA::real plane[4], const VSA::real point[4]) + { + float biggest_d = -FLT_MAX; + for (uint32_t i = 0; i < planes.size(); ++i) + { + float d = planes[i].n.x * point[0] + planes[i].n.y * point[1] + planes[i].n.z * point[2] + planes[i].d * point[3]; + if (d > biggest_d) + { + biggest_d = d; + plane[0] = planes[i].n.x; + plane[1] = planes[i].n.y; + plane[2] = planes[i].n.z; + plane[3] = planes[i].d; + } + } + return biggest_d; + }; +}; + + +void findCellBasePlanes(const std::vector<PxVec3>& sites, std::vector<std::vector<int32_t> >& neighboors) +{ + Halfspace_partitioning prt; + std::vector<physx::PxPlane>& planes = prt.planes; + neighboors.resize(sites.size()); + for (uint32_t cellId = 0; cellId + 1 < sites.size(); ++cellId) + { + planes.clear(); + planes.resize(sites.size() - 1 - cellId); + std::vector<PxVec3> midpoints(sites.size() - 1); + int32_t collected = 0; + + for (uint32_t i = cellId + 1; i < sites.size(); ++i) + { + PxVec3 midpoint = 0.5 * (sites[i] + sites[cellId]); + PxVec3 direction = (sites[i] - sites[cellId]).getNormalized(); + planes[collected].n = direction; + planes[collected].d = -direction.dot(midpoint); + midpoints[collected] = midpoint; + ++collected; + } + for (uint32_t i = 0; i < planes.size(); ++i) + { + planes[i].n = -planes[i].n; + planes[i].d = -planes[i].d; + + if (VSA::vs3d_test(prt)) + { + neighboors[cellId].push_back(i + cellId + 1); + neighboors[i + cellId + 1].push_back(cellId); + }; + planes[i].n = -planes[i].n; + planes[i].d = -planes[i].d; + } + } +} + + +#define SITE_BOX_SIZE 4 +#define CUTTING_BOX_SIZE 40 + +Mesh* getCellMesh(BooleanEvaluator& eval, int32_t planeIndexerOffset, int32_t cellId, const std::vector<PxVec3>& sites, std::vector < std::vector<int32_t> >& neighboors) +{ + Mesh* cell = getBigBox(sites[cellId], SITE_BOX_SIZE); + Mesh* cuttingMesh = getCuttingBox(PxVec3(0, 0, 0), PxVec3(1, 1, 1), CUTTING_BOX_SIZE, 0); + + for (uint32_t i = 0; i < neighboors[cellId].size(); ++i) + { + int32_t nCell = neighboors[cellId][i]; + PxVec3 midpoint = 0.5 * (sites[nCell] + sites[cellId]); + PxVec3 direction = (sites[nCell] - sites[cellId]).getNormalized(); + int32_t planeIndex = static_cast<int32_t>(sites.size()) * std::min(cellId, nCell) + std::max(cellId, nCell) + planeIndexerOffset; + if (nCell < cellId) + planeIndex = -planeIndex; + setCuttingBox(midpoint, -direction, cuttingMesh, CUTTING_BOX_SIZE, planeIndex); + eval.performFastCutting(cell, cuttingMesh, BooleanConfigurations::BOOLEAN_INTERSECION()); + Mesh* newCell = eval.createNewMesh(); + delete cell; + cell = newCell; + if (cell == nullptr) + break; + } + return cell; +} + + +bool blastBondComparator(const NvBlastBondDesc& a, const NvBlastBondDesc& b) +{ + if (a.chunkIndices[0] == b.chunkIndices[0]) + return a.chunkIndices[1] < b.chunkIndices[1]; + else + return a.chunkIndices[0] < b.chunkIndices[0]; +} + + +#define MAX_VORONOI_ATTEMPT_NUMBER 450 + +VoronoiSitesGenerator::VoronoiSitesGenerator(Mesh* mesh, RandomGeneratorBase* rnd) +{ + mMesh = mesh; + mRnd = rnd; + mAccelerator = new BBoxBasedAccelerator(mMesh, DEFAULT_BB_ACCELARATOR_RES); + mStencil = nullptr; +} + +void VoronoiSitesGenerator::setBaseMesh(Mesh* m) +{ + mGeneratedSites.clear(); + delete mAccelerator; + mMesh = m; + mAccelerator = new BBoxBasedAccelerator(mMesh, DEFAULT_BB_ACCELARATOR_RES); +} + +VoronoiSitesGenerator::~VoronoiSitesGenerator() +{ + delete mAccelerator; + mAccelerator = nullptr; +} + + +void VoronoiSitesGenerator::setStencil(Mesh* stencil) +{ + mStencil = stencil; +} + + +void VoronoiSitesGenerator::clearStencil() +{ + mStencil = nullptr; +} + + +void VoronoiSitesGenerator::uniformlyGenerateSitesInMesh(const uint32_t sitesCount) +{ + BooleanEvaluator voronoiMeshEval(nullptr); + PxVec3 mn = mMesh->getBoundingBox().minimum; + PxVec3 mx = mMesh->getBoundingBox().maximum; + PxVec3 vc = mx - mn; + uint32_t attemptNumber = 0; + uint32_t generatedSites = 0; + while (generatedSites < sitesCount && attemptNumber < MAX_VORONOI_ATTEMPT_NUMBER) + { + float rn1 = mRnd->getRandomValue() * vc.x; + float rn2 = mRnd->getRandomValue() * vc.y; + float rn3 = mRnd->getRandomValue() * vc.z; + if (voronoiMeshEval.isPointContainedInMesh(mMesh, PxVec3(rn1, rn2, rn3) + mn) && (mStencil == nullptr + || voronoiMeshEval.isPointContainedInMesh(mStencil, PxVec3(rn1, rn2, rn3) + mn))) + { + generatedSites++; + mGeneratedSites.push_back(PxVec3(rn1, rn2, rn3) + mn); + attemptNumber = 0; + } + else + { + attemptNumber++; + if (attemptNumber > MAX_VORONOI_ATTEMPT_NUMBER) + break; + } + } +} + + +void VoronoiSitesGenerator::clusteredSitesGeneration(const uint32_t numberOfClusters, const uint32_t sitesPerCluster, float clusterRadius) +{ + BooleanEvaluator voronoiMeshEval(nullptr); + PxVec3 mn = mMesh->getBoundingBox().minimum; + PxVec3 mx = mMesh->getBoundingBox().maximum; + PxVec3 middle = (mx + mn) * 0.5; + PxVec3 vc = (mx - mn) * 0.5; + uint32_t attemptNumber = 0; + uint32_t generatedSites = 0; + std::vector<PxVec3> tempPoints; + while (generatedSites < numberOfClusters) + { + float rn1 = mRnd->getRandomValue() * 2 - 1; + float rn2 = mRnd->getRandomValue() * 2 - 1; + float rn3 = mRnd->getRandomValue() * 2 - 1; + PxVec3 p = PxVec3(middle.x + rn1 * vc.x, middle.y + rn2 * vc.y, middle.z + rn3 * vc.z); + + if (voronoiMeshEval.isPointContainedInMesh(mMesh, p) && (mStencil == nullptr + || voronoiMeshEval.isPointContainedInMesh(mStencil, p))) + { + generatedSites++; + tempPoints.push_back(p); + attemptNumber = 0; + } + else + { + attemptNumber++; + if (attemptNumber > MAX_VORONOI_ATTEMPT_NUMBER) + break; + } + } + int32_t totalCount = 0; + for (; tempPoints.size() > 0; tempPoints.pop_back()) + { + uint32_t unif = sitesPerCluster; + generatedSites = 0; + while (generatedSites < unif) + { + PxVec3 p = tempPoints.back() + PxVec3(mRnd->getRandomValue() * 2 - 1, mRnd->getRandomValue() * 2 - 1, mRnd->getRandomValue() * 2 - 1).getNormalized() * (mRnd->getRandomValue() + 0.001f) * clusterRadius; + if (voronoiMeshEval.isPointContainedInMesh(mMesh, p) && (mStencil == nullptr + || voronoiMeshEval.isPointContainedInMesh(mStencil, p))) + { + totalCount++; + generatedSites++; + mGeneratedSites.push_back(p); + attemptNumber = 0; + } + else + { + attemptNumber++; + if (attemptNumber > MAX_VORONOI_ATTEMPT_NUMBER) + break; + } + } + + } + +} + + +#define IN_SPHERE_ATTEMPT_NUMBER 20 + +void VoronoiSitesGenerator::addSite(const physx::PxVec3& site) +{ + mGeneratedSites.push_back(site); +} + + +void VoronoiSitesGenerator::generateInSphere(const uint32_t count, const float radius, const physx::PxVec3& center) +{ + BooleanEvaluator voronoiMeshEval(nullptr); + uint32_t attemptNumber = 0; + uint32_t generatedSites = 0; + std::vector<PxVec3> tempPoints; + + while (generatedSites < count && attemptNumber < MAX_VORONOI_ATTEMPT_NUMBER) + { + float rn1 = mRnd->getRandomValue() * radius; + float rn2 = mRnd->getRandomValue() * radius; + float rn3 = mRnd->getRandomValue() * radius; + if (voronoiMeshEval.isPointContainedInMesh(mMesh, PxVec3(rn1, rn2, rn3) + center) && (mStencil == nullptr + || voronoiMeshEval.isPointContainedInMesh(mStencil, PxVec3(rn1, rn2, rn3) + center))) + { + generatedSites++; + mGeneratedSites.push_back(PxVec3(rn1, rn2, rn3) + center); + attemptNumber = 0; + } + else + { + attemptNumber++; + if (attemptNumber > MAX_VORONOI_ATTEMPT_NUMBER) + break; + } + } +} + + +void VoronoiSitesGenerator::deleteInSphere(const float radius, const physx::PxVec3& center, float deleteProbability) +{ + float r2 = radius * radius; + for (uint32_t i = 0; i < mGeneratedSites.size(); ++i) + { + if ((mGeneratedSites[i] - center).magnitudeSquared() < r2 && mRnd->getRandomValue() <= deleteProbability) + { + std::swap(mGeneratedSites[i], mGeneratedSites.back()); + mGeneratedSites.pop_back(); + --i; + } + } +} + + +void VoronoiSitesGenerator::radialPattern(const physx::PxVec3& center, const physx::PxVec3& normal, float radius, int32_t angularSteps, int32_t radialSteps, float angleOffset, float variability) +{ +// mGeneratedSites.push_back(center); + physx::PxVec3 t1, t2; + if (abs(normal.z) < 0.9) + { + t1 = normal.cross(PxVec3(0, 0, 1)); + } + else + { + t1 = normal.cross(PxVec3(1, 0, 0)); + } + t2 = t1.cross(normal); + t1.normalize(); + t2.normalize(); + + float radStep = radius / radialSteps; + int32_t cCr = 0; + + float angleStep = PxPi * 2 / angularSteps; + for (float cRadius = radStep; cRadius < radius; cRadius += radStep) + { + float cAngle = angleOffset * cCr; + for (int32_t i = 0; i < angularSteps; ++i) + { + float angVars = mRnd->getRandomValue() * variability + (1.0f - 0.5f * variability); + float radVars = mRnd->getRandomValue() * variability + (1.0f - 0.5f * variability); + + PxVec3 nPos = (PxCos(cAngle * angVars) * t1 + PxSin(cAngle * angVars) * t2) * cRadius * radVars + center; + mGeneratedSites.push_back(nPos); + cAngle += angleStep; + } + ++cCr; + } +} + + +std::vector<PxVec3>& VoronoiSitesGenerator::getVoronoiSites() +{ + return mGeneratedSites; +} + + +int32_t FractureTool::voronoiFracturing(uint32_t chunkId, const std::vector<physx::PxVec3>& cellPointsIn, bool replaceChunk) +{ + if (chunkId == 0 && replaceChunk) + { + return 1; + } + + int32_t chunkIndex = getChunkIndex(chunkId); + if (chunkIndex == -1 || cellPointsIn.size() < 2) + { + return 1; + } + if (!mChunkData[chunkIndex].isLeaf) + { + deleteAllChildsOfChunk(chunkId); + } + chunkIndex = getChunkIndex(chunkId); + + Mesh* mesh = mChunkData[chunkIndex].meshData; + + std::vector<PxVec3> cellPoints(cellPointsIn.size()); + for (uint32_t i = 0; i < cellPointsIn.size(); ++i) + { + cellPoints[i] = (cellPointsIn[i] - mOffset) * (1.0f / mScaleFactor); + } + + /** + Prebuild accelerator structure + */ + BooleanEvaluator eval(mLoggingCallback); + BooleanEvaluator voronoiMeshEval(mLoggingCallback); + + BBoxBasedAccelerator spAccel = BBoxBasedAccelerator(mesh, DEFAULT_BB_ACCELARATOR_RES); + + std::vector<std::vector<int32_t> > neighboors; + findCellBasePlanes(cellPoints, neighboors); + + /** + Fracture + */ + int32_t parentChunk = replaceChunk ? mChunkData[chunkIndex].parent : chunkId; + std::vector<uint32_t> newlyCreatedChunksIds; + for (uint32_t i = 0; i < cellPoints.size(); ++i) + { + Mesh* cell = getCellMesh(eval, mPlaneIndexerOffset, i, cellPoints, neighboors); + + if (cell == nullptr) + { + continue; + } + DummyAccelerator dmAccel(cell->getFacetCount()); + voronoiMeshEval.performBoolean(mesh, cell, &spAccel, &dmAccel, BooleanConfigurations::BOOLEAN_INTERSECION()); + Mesh* resultMesh = voronoiMeshEval.createNewMesh(); + if (resultMesh) + { + mChunkData.push_back(ChunkInfo()); + mChunkData.back().isLeaf = true; + mChunkData.back().meshData = resultMesh; + mChunkData.back().parent = parentChunk; + mChunkData.back().chunkId = mChunkIdCounter++; + newlyCreatedChunksIds.push_back(mChunkData.back().chunkId); + } + eval.reset(); + delete cell; + } + mChunkData[chunkIndex].isLeaf = false; + if (replaceChunk) + { + eraseChunk(chunkId); + } + mPlaneIndexerOffset += static_cast<int32_t>(cellPoints.size() * cellPoints.size()); + + + if (mRemoveIslands) + { + for (auto chunkToCheck : newlyCreatedChunksIds) + { + islandDetectionAndRemoving(chunkToCheck); + } + } + return 0; +} + +Mesh FractureTool::getChunkMesh(int32_t chunkId) +{ + Mesh temp = *mChunkData[getChunkIndex(chunkId)].meshData; + for (uint32_t i = 0; i < temp.getVerticesCount(); ++i) + { + temp.getVertices()[i].p = temp.getVertices()[i].p * mScaleFactor + mOffset; + } + temp.recalculateBoundingBox(); + + return temp; +} + + +int32_t FractureTool::voronoiFracturing(uint32_t chunkId, const std::vector<physx::PxVec3>& cellPointsIn, const physx::PxVec3& scale, bool replaceChunk) +{ + if (chunkId == 0 && replaceChunk) + { + return 1; + } + + int32_t chunkIndex = getChunkIndex(chunkId); + if (chunkIndex == -1 || cellPointsIn.size() < 2) + { + return 1; + } + if (!mChunkData[chunkIndex].isLeaf) + { + deleteAllChildsOfChunk(chunkId); + } + chunkIndex = getChunkIndex(chunkId); + + Mesh* mesh = mChunkData[chunkIndex].meshData; + + std::vector<PxVec3> cellPoints(cellPointsIn.size()); + for (uint32_t i = 0; i < cellPointsIn.size(); ++i) + { + cellPoints[i] = (cellPointsIn[i] - mOffset) * (1.0f / mScaleFactor); + + cellPoints[i].x *= (1.0f / scale.x); + cellPoints[i].y *= (1.0f / scale.y); + cellPoints[i].z *= (1.0f / scale.z); + } + + /** + Prebuild accelerator structure + */ + BooleanEvaluator eval(mLoggingCallback); + BooleanEvaluator voronoiMeshEval(mLoggingCallback); + + BBoxBasedAccelerator spAccel = BBoxBasedAccelerator(mesh, DEFAULT_BB_ACCELARATOR_RES); + + std::vector<std::vector<int32_t> > neighboors; + findCellBasePlanes(cellPoints, neighboors); + + /** + Fracture + */ + int32_t parentChunk = replaceChunk ? mChunkData[chunkIndex].parent : chunkId; + std::vector<uint32_t> newlyCreatedChunksIds; + + for (uint32_t i = 0; i < cellPoints.size(); ++i) + { + Mesh* cell = getCellMesh(eval, mPlaneIndexerOffset, i, cellPoints, neighboors); + + if (cell == nullptr) + { + continue; + } + + for (uint32_t v = 0; v < cell->getVerticesCount(); ++v) + { + cell->getVertices()[v].p.x *= scale.x; + cell->getVertices()[v].p.y *= scale.y; + cell->getVertices()[v].p.z *= scale.z; + } + cell->recalculateBoundingBox(); + DummyAccelerator dmAccel(cell->getFacetCount()); + voronoiMeshEval.performBoolean(mesh, cell, &spAccel, &dmAccel, BooleanConfigurations::BOOLEAN_INTERSECION()); + Mesh* resultMesh = voronoiMeshEval.createNewMesh(); + if (resultMesh) + { + mChunkData.push_back(ChunkInfo()); + mChunkData.back().isLeaf = true; + mChunkData.back().meshData = resultMesh; + mChunkData.back().parent = parentChunk; + mChunkData.back().chunkId = mChunkIdCounter++; + newlyCreatedChunksIds.push_back(mChunkData.back().chunkId); + } + eval.reset(); + delete cell; + } + mChunkData[chunkIndex].isLeaf = false; + if (replaceChunk) + { + eraseChunk(chunkId); + } + mPlaneIndexerOffset += static_cast<int32_t>(cellPoints.size() * cellPoints.size()); + + if (mRemoveIslands) + { + for (auto chunkToCheck : newlyCreatedChunksIds) + { + islandDetectionAndRemoving(chunkToCheck); + } + } + + return 0; +} + + +int32_t FractureTool::slicing(uint32_t chunkId, SlicingConfiguration conf, bool replaceChunk, RandomGeneratorBase* rnd) +{ + if (conf.noiseAmplitude != 0) + { + return slicingNoisy(chunkId, conf, replaceChunk, rnd); + } + + if (replaceChunk && chunkId == 0) + { + return 1; + } + + int32_t chunkIndex = getChunkIndex(chunkId); + if (chunkIndex == -1) + { + return 1; + } + if (!mChunkData[chunkIndex].isLeaf) + { + deleteAllChildsOfChunk(chunkId); + } + chunkIndex = getChunkIndex(chunkId); + + + Mesh* mesh = new Mesh(*mChunkData[chunkIndex].meshData); + + BooleanEvaluator bTool(mLoggingCallback); + + int32_t x_slices = conf.x_slices; + int32_t y_slices = conf.y_slices; + int32_t z_slices = conf.z_slices; + + const PxBounds3 sourceBBox = mesh->getBoundingBox(); + + PxVec3 center = PxVec3(mesh->getBoundingBox().minimum.x, 0, 0); + + + float x_offset = (sourceBBox.maximum.x - sourceBBox.minimum.x) * (1.0f / (x_slices + 1)); + float y_offset = (sourceBBox.maximum.y - sourceBBox.minimum.y) * (1.0f / (y_slices + 1)); + float z_offset = (sourceBBox.maximum.z - sourceBBox.minimum.z) * (1.0f / (z_slices + 1)); + + center.x += x_offset; + + PxVec3 dir(1, 0, 0); + + Mesh* slBox = getCuttingBox(center, dir, 20, 0); + + ChunkInfo ch; + ch.isLeaf = true; + ch.parent = replaceChunk ? mChunkData[chunkIndex].parent : chunkId; + std::vector<ChunkInfo> xSlicedChunks; + std::vector<ChunkInfo> ySlicedChunks; + std::vector<uint32_t> newlyCreatedChunksIds; + /** + Slice along x direction + */ + for (int32_t slice = 0; slice < x_slices; ++slice) + { + PxVec3 randVect = PxVec3(2 * rnd->getRandomValue() - 1, 2 * rnd->getRandomValue() - 1, 2 * rnd->getRandomValue() - 1); + PxVec3 lDir = dir + randVect * conf.angle_variations; + + setCuttingBox(center, lDir, slBox, 20, mPlaneIndexerOffset); + bTool.performFastCutting(mesh, slBox, BooleanConfigurations::BOOLEAN_DIFFERENCE()); + ch.meshData = bTool.createNewMesh(); + + if (ch.meshData != 0) + { + xSlicedChunks.push_back(ch); + } + inverseNormalAndSetIndices(slBox, -mPlaneIndexerOffset); + ++mPlaneIndexerOffset; + bTool.performFastCutting(mesh, slBox, BooleanConfigurations::BOOLEAN_INTERSECION()); + Mesh* result = bTool.createNewMesh(); + delete mesh; + mesh = result; + if (mesh == nullptr) + { + break; + } + center.x += x_offset + (rnd->getRandomValue()) * conf.offset_variations * x_offset; + } + if (mesh != 0) + { + ch.meshData = mesh; + xSlicedChunks.push_back(ch); + } + + + for (uint32_t chunk = 0; chunk < xSlicedChunks.size(); ++chunk) + { + center = PxVec3(0, sourceBBox.minimum.y, 0); + center.y += y_offset; + dir = PxVec3(0, 1, 0); + mesh = xSlicedChunks[chunk].meshData; + + for (int32_t slice = 0; slice < y_slices; ++slice) + { + PxVec3 randVect = PxVec3(2 * rnd->getRandomValue() - 1, 2 * rnd->getRandomValue() - 1, 2 * rnd->getRandomValue() - 1); + PxVec3 lDir = dir + randVect * conf.angle_variations; + + + setCuttingBox(center, lDir, slBox, 20, mPlaneIndexerOffset); + bTool.performFastCutting(mesh, slBox, BooleanConfigurations::BOOLEAN_DIFFERENCE()); + ch.meshData = bTool.createNewMesh(); + if (ch.meshData != 0) + { + ySlicedChunks.push_back(ch); + } + inverseNormalAndSetIndices(slBox, -mPlaneIndexerOffset); + ++mPlaneIndexerOffset; + bTool.performFastCutting(mesh, slBox, BooleanConfigurations::BOOLEAN_INTERSECION()); + Mesh* result = bTool.createNewMesh(); + delete mesh; + mesh = result; + if (mesh == nullptr) + { + break; + } + center.y += y_offset + (rnd->getRandomValue()) * conf.offset_variations * y_offset; + } + if (mesh != 0) + { + ch.meshData = mesh; + ySlicedChunks.push_back(ch); + } + } + + + for (uint32_t chunk = 0; chunk < ySlicedChunks.size(); ++chunk) + { + center = PxVec3(0, 0, sourceBBox.minimum.z); + center.z += z_offset; + dir = PxVec3(0, 0, 1); + mesh = ySlicedChunks[chunk].meshData; + + for (int32_t slice = 0; slice < z_slices; ++slice) + { + PxVec3 randVect = PxVec3(2 * rnd->getRandomValue() - 1, 2 * rnd->getRandomValue() - 1, 2 * rnd->getRandomValue() - 1); + PxVec3 lDir = dir + randVect * conf.angle_variations; + setCuttingBox(center, lDir, slBox, 20, mPlaneIndexerOffset); + bTool.performFastCutting(mesh, slBox, BooleanConfigurations::BOOLEAN_DIFFERENCE()); + ch.meshData = bTool.createNewMesh(); + if (ch.meshData != 0) + { + ch.chunkId = mChunkIdCounter++; + newlyCreatedChunksIds.push_back(ch.chunkId); + mChunkData.push_back(ch); + } + inverseNormalAndSetIndices(slBox, -mPlaneIndexerOffset); + ++mPlaneIndexerOffset; + bTool.performFastCutting(mesh, slBox, BooleanConfigurations::BOOLEAN_INTERSECION()); + Mesh* result = bTool.createNewMesh(); + delete mesh; + mesh = result; + if (mesh == nullptr) + { + break; + } + center.z += z_offset + (rnd->getRandomValue()) * conf.offset_variations * z_offset; + } + if (mesh != 0) + { + ch.chunkId = mChunkIdCounter++; + ch.meshData = mesh; + mChunkData.push_back(ch); + newlyCreatedChunksIds.push_back(ch.chunkId); + } + } + + + delete slBox; + + mChunkData[chunkIndex].isLeaf = false; + if (replaceChunk) + { + eraseChunk(chunkId); + } + + if (mRemoveIslands) + { + for (auto chunkToCheck : newlyCreatedChunksIds) + { + islandDetectionAndRemoving(chunkToCheck); + } + } + + return 0; +} + +int32_t FractureTool::slicingNoisy(uint32_t chunkId, SlicingConfiguration conf, bool replaceChunk, RandomGeneratorBase* rnd) +{ + if (replaceChunk && chunkId == 0) + { + return 1; + } + + int32_t chunkIndex = getChunkIndex(chunkId); + if (chunkIndex == -1) + { + return 1; + } + if (!mChunkData[chunkIndex].isLeaf) + { + deleteAllChildsOfChunk(chunkId); + } + chunkIndex = getChunkIndex(chunkId); + + + Mesh* mesh = new Mesh(*mChunkData[chunkIndex].meshData); + + BooleanEvaluator bTool(mLoggingCallback); + + int32_t x_slices = conf.x_slices; + int32_t y_slices = conf.y_slices; + int32_t z_slices = conf.z_slices; + + const PxBounds3 sourceBBox = mesh->getBoundingBox(); + + PxVec3 center = PxVec3(mesh->getBoundingBox().minimum.x, 0, 0); + + + float x_offset = (sourceBBox.maximum.x - sourceBBox.minimum.x) * (1.0f / (x_slices + 1)); + float y_offset = (sourceBBox.maximum.y - sourceBBox.minimum.y) * (1.0f / (y_slices + 1)); + float z_offset = (sourceBBox.maximum.z - sourceBBox.minimum.z) * (1.0f / (z_slices + 1)); + + center.x += x_offset; + + PxVec3 dir(1, 0, 0); + + Mesh* slBox = nullptr; + + ChunkInfo ch; + ch.isLeaf = true; + ch.parent = replaceChunk ? mChunkData[chunkIndex].parent : chunkId; + std::vector<ChunkInfo> xSlicedChunks; + std::vector<ChunkInfo> ySlicedChunks; + std::vector<uint32_t> newlyCreatedChunksIds; + float noisyPartSize = 1.8f; + int32_t acceleratorRes = 5; + /** + Slice along x direction + */ + for (int32_t slice = 0; slice < x_slices; ++slice) + { + PxVec3 randVect = PxVec3(2 * rnd->getRandomValue() - 1, 2 * rnd->getRandomValue() - 1, 2 * rnd->getRandomValue() - 1); + PxVec3 lDir = dir + randVect * conf.angle_variations; + slBox = getNoisyCuttingBoxPair(center, lDir, 40, noisyPartSize, conf.surfaceResolution, mPlaneIndexerOffset, conf.noiseAmplitude, conf.noiseFrequency, conf.noiseOctaveNumber, rnd->getRandomValue()); + // DummyAccelerator accel(mesh->getFacetCount()); + IntersectionTestingAccelerator accel(mesh, acceleratorRes); + DummyAccelerator dummy(slBox->getFacetCount()); + bTool.performBoolean(mesh, slBox, &accel, &dummy, BooleanConfigurations::BOOLEAN_DIFFERENCE()); + ch.meshData = bTool.createNewMesh(); + if (ch.meshData != 0) + { + xSlicedChunks.push_back(ch); + } + inverseNormalAndSetIndices(slBox, -mPlaneIndexerOffset); + ++mPlaneIndexerOffset; + bTool.performBoolean(mesh, slBox, &accel, &dummy, BooleanConfigurations::BOOLEAN_INTERSECION()); + Mesh* result = bTool.createNewMesh(); + delete slBox; + delete mesh; + mesh = result; + if (mesh == nullptr) + { + break; + } + center.x += x_offset + (rnd->getRandomValue()) * conf.offset_variations * x_offset; + } + if (mesh != 0) + { + ch.meshData = mesh; + xSlicedChunks.push_back(ch); + } + slBox = getCuttingBox(center, dir, 20, 0); + uint32_t slicedChunkSize = xSlicedChunks.size(); + for (uint32_t chunk = 0; chunk < slicedChunkSize; ++chunk) + { + center = PxVec3(0, sourceBBox.minimum.y, 0); + center.y += y_offset; + dir = PxVec3(0, 1, 0); + mesh = xSlicedChunks[chunk].meshData; + + for (int32_t slice = 0; slice < y_slices; ++slice) + { + PxVec3 randVect = PxVec3(2 * rnd->getRandomValue() - 1, 2 * rnd->getRandomValue() - 1, 2 * rnd->getRandomValue() - 1); + PxVec3 lDir = dir + randVect * conf.angle_variations; + + slBox = getNoisyCuttingBoxPair(center, lDir, 40, noisyPartSize, conf.surfaceResolution, mPlaneIndexerOffset, conf.noiseAmplitude, conf.noiseFrequency, conf.noiseOctaveNumber, rnd->getRandomValue()); + // DummyAccelerator accel(mesh->getFacetCount()); + IntersectionTestingAccelerator accel(mesh, acceleratorRes); + DummyAccelerator dummy(slBox->getFacetCount()); + bTool.performBoolean(mesh, slBox, &accel, &dummy, BooleanConfigurations::BOOLEAN_DIFFERENCE()); + ch.meshData = bTool.createNewMesh(); + if (ch.meshData != 0) + { + ySlicedChunks.push_back(ch); + } + inverseNormalAndSetIndices(slBox, -mPlaneIndexerOffset); + ++mPlaneIndexerOffset; + bTool.performBoolean(mesh, slBox, &accel, &dummy, BooleanConfigurations::BOOLEAN_INTERSECION()); + Mesh* result = bTool.createNewMesh(); + delete slBox; + delete mesh; + mesh = result; + if (mesh == nullptr) + { + break; + } + center.y += y_offset + (rnd->getRandomValue()) * conf.offset_variations * y_offset; + } + if (mesh != 0) + { + ch.meshData = mesh; + ySlicedChunks.push_back(ch); + } + } + + for (uint32_t chunk = 0; chunk < ySlicedChunks.size(); ++chunk) + { + center = PxVec3(0, 0, sourceBBox.minimum.z); + center.z += z_offset; + dir = PxVec3(0, 0, 1); + mesh = ySlicedChunks[chunk].meshData; + + for (int32_t slice = 0; slice < z_slices; ++slice) + { + PxVec3 randVect = PxVec3(2 * rnd->getRandomValue() - 1, 2 * rnd->getRandomValue() - 1, 2 * rnd->getRandomValue() - 1); + PxVec3 lDir = dir + randVect * conf.angle_variations; + slBox = getNoisyCuttingBoxPair(center, lDir, 40, noisyPartSize, conf.surfaceResolution, mPlaneIndexerOffset, conf.noiseAmplitude, conf.noiseFrequency, conf.noiseOctaveNumber, rnd->getRandomValue()); + // DummyAccelerator accel(mesh->getFacetCount()); + IntersectionTestingAccelerator accel(mesh, acceleratorRes); + DummyAccelerator dummy(slBox->getFacetCount()); + bTool.performBoolean(mesh, slBox, &accel, &dummy, BooleanConfigurations::BOOLEAN_DIFFERENCE()); + ch.meshData = bTool.createNewMesh(); + if (ch.meshData != 0) + { + ch.chunkId = mChunkIdCounter++; + mChunkData.push_back(ch); + newlyCreatedChunksIds.push_back(ch.chunkId); + } + inverseNormalAndSetIndices(slBox, -mPlaneIndexerOffset); + ++mPlaneIndexerOffset; + bTool.performBoolean(mesh, slBox, &accel, &dummy, BooleanConfigurations::BOOLEAN_INTERSECION()); + Mesh* result = bTool.createNewMesh(); + delete mesh; + delete slBox; + mesh = result; + if (mesh == nullptr) + { + break; + } + center.z += z_offset + (rnd->getRandomValue()) * conf.offset_variations * z_offset; + } + if (mesh != 0) + { + ch.chunkId = mChunkIdCounter++; + ch.meshData = mesh; + mChunkData.push_back(ch); + newlyCreatedChunksIds.push_back(ch.chunkId); + } + } + +// delete slBox; + + mChunkData[chunkIndex].isLeaf = false; + if (replaceChunk) + { + eraseChunk(chunkId); + } + + if (mRemoveIslands) + { + for (auto chunkToCheck : newlyCreatedChunksIds) + { + islandDetectionAndRemoving(chunkToCheck); + } + } + + return 0; +} + + + +int32_t FractureTool::getChunkIndex(int32_t chunkId) +{ + for (uint32_t i = 0; i < mChunkData.size(); ++i) + { + if (mChunkData[i].chunkId == chunkId) + { + return i; + } + } + return -1; +} + +int32_t FractureTool::getChunkDepth(int32_t chunkId) +{ + int32_t chunkIndex = getChunkIndex(chunkId); + if (chunkIndex == -1) + { + return -1; + } + + int32_t depth = 0; + + while (mChunkData[chunkIndex].parent != -1) + { + ++depth; + chunkIndex = getChunkIndex(mChunkData[chunkIndex].parent); + } + return depth; +} + +std::vector<int32_t> FractureTool::getChunksIdAtDepth(uint32_t depth) +{ + std::vector<int32_t> chunkIds; + + for (uint32_t i = 0; i < mChunkData.size(); ++i) + { + if (getChunkDepth(mChunkData[i].chunkId) == (int32_t)depth) + { + chunkIds.push_back(mChunkData[i].chunkId); + } + } + return chunkIds; +} + + +void FractureTool::getTransformation(PxVec3& offset, float& scale) +{ + offset = mOffset; + scale = mScaleFactor; +} + +void FractureTool::setSourceMesh(Mesh* mesh) +{ + if (mesh == nullptr) + { + return; + } + reset(); + + mChunkData.resize(1); + mChunkData[0].meshData = new Mesh(*mesh); + mChunkData[0].parent = -1; + mChunkData[0].isLeaf = true; + mChunkData[0].chunkId = mChunkIdCounter++; + mesh = mChunkData[0].meshData; + + /** + Move to origin and scale to unit cube + */ + + mOffset = (mesh->getBoundingBox().maximum + mesh->getBoundingBox().minimum) * 0.5f; + PxVec3 bbSizes = (mesh->getBoundingBox().maximum - mesh->getBoundingBox().minimum); + + mScaleFactor = std::max(bbSizes.x, std::max(bbSizes.y, bbSizes.z)); + + Vertex* verticesBuffer = mesh->getVertices(); + for (uint32_t i = 0; i < mesh->getVerticesCount(); ++i) + { + verticesBuffer[i].p = (verticesBuffer[i].p - mOffset) * (1.0f / mScaleFactor); + } + + mesh->getBoundingBox().minimum = (mesh->getBoundingBox().minimum - mOffset) * (1.0f / mScaleFactor); + mesh->getBoundingBox().maximum = (mesh->getBoundingBox().maximum - mOffset) * (1.0f / mScaleFactor); + + + for (uint32_t i = 0; i < mesh->getFacetCount(); ++i) + { + mesh->getFacet(i)->userData = 0; // Mark facet as initial boundary facet + } +} + + +void FractureTool::reset() +{ + mChunkPostprocessors.clear(); + for (uint32_t i = 0; i < mChunkData.size(); ++i) + { + delete mChunkData[i].meshData; + } + mChunkData.clear(); + mPlaneIndexerOffset = 1; + mChunkIdCounter = 0; +} + + + + +bool FractureTool::isAncestorForChunk(int32_t ancestorId, int32_t chunkId) +{ + if (ancestorId == chunkId) + { + return false; + } + while (chunkId != -1) + { + if (ancestorId == chunkId) + { + return true; + } + chunkId = getChunkIndex(chunkId); + if (chunkId == -1) + { + return false; + } + chunkId = mChunkData[chunkId].parent; + } + return false; +} + +void FractureTool::eraseChunk(int32_t chunkId) +{ + deleteAllChildsOfChunk(chunkId); + int32_t index = getChunkIndex(chunkId); + if (index != -1) + { + delete mChunkData[index].meshData; + std::swap(mChunkData.back(), mChunkData[index]); + mChunkData.pop_back(); + } +} + + +void FractureTool::deleteAllChildsOfChunk(int32_t chunkId) +{ + std::vector<int32_t> chunkToDelete; + for (uint32_t i = 0; i < mChunkData.size(); ++i) + { + if (isAncestorForChunk(chunkId, mChunkData[i].chunkId)) + { + chunkToDelete.push_back(i); + } + } + for (int32_t i = (int32_t)chunkToDelete.size() - 1; i >= 0; --i) + { + int32_t m = chunkToDelete[i]; + delete mChunkData[m].meshData; + std::swap(mChunkData.back(), mChunkData[m]); + mChunkData.pop_back(); + } +} + +void FractureTool::finalizeFracturing() +{ + for (uint32_t i = 0; i < mChunkPostprocessors.size(); ++i) + { + delete mChunkPostprocessors[i]; + } + mChunkPostprocessors.resize(mChunkData.size()); + for (uint32_t i = 0; i < mChunkPostprocessors.size(); ++i) + { + mChunkPostprocessors[i] = new ChunkPostProcessor(); + } + + for (uint32_t i = 0; i < mChunkPostprocessors.size(); ++i) + { + mChunkPostprocessors[i]->triangulate(mChunkData[i].meshData); + } + std::vector<int32_t> badOnes; + for (uint32_t i = 0; i < mChunkPostprocessors.size(); ++i) + { + if (mChunkPostprocessors[i]->getBaseMesh().empty()) + { + badOnes.push_back(i); + } + } + for (int32_t i = (int32_t)badOnes.size() - 1; i >= 0; --i) + { + int32_t chunkId = mChunkData[badOnes[i]].chunkId; + for (uint32_t j = 0; j < mChunkData.size(); ++j) + { + if (mChunkData[j].parent == chunkId) + mChunkData[j].parent = mChunkData[badOnes[i]].parent; + } + std::swap(mChunkPostprocessors[badOnes[i]], mChunkPostprocessors.back()); + mChunkPostprocessors.pop_back(); + std::swap(mChunkData[badOnes[i]], mChunkData.back()); + mChunkData.pop_back(); + } + +} + +const std::vector<ChunkInfo>& FractureTool::getChunkList() +{ + return mChunkData; +} + +void FractureTool::getBaseMesh(int32_t chunkIndex, std::vector<Triangle>& output) +{ + NVBLAST_ASSERT(mChunkPostprocessors.size() > 0); + if (mChunkPostprocessors.size() == 0) + { + return; // finalizeFracturing() should be called before getting mesh! + } + output = mChunkPostprocessors[chunkIndex]->getBaseMesh(); + + /* Scale mesh back */ + + for (uint32_t i = 0; i < output.size(); ++i) + { + output[i].a.p = output[i].a.p * mScaleFactor + mOffset; + output[i].b.p = output[i].b.p * mScaleFactor + mOffset; + output[i].c.p = output[i].c.p * mScaleFactor + mOffset; + } +} + +void FractureTool::getNoisedMesh(int32_t chunkIndex, std::vector<Triangle>& output) +{ + NVBLAST_ASSERT(mChunkPostprocessors.size() > 0); + if (mChunkPostprocessors.size() == 0) + { + return; // finalizeFracturing() should be called before getting mesh! + } + + if (mChunkData[chunkIndex].chunkId == 0) + { + output = mChunkPostprocessors[chunkIndex]->getBaseMesh(); + } + else + { + output = mChunkPostprocessors[chunkIndex]->getNoisyMesh(); + } + + for (uint32_t i = 0; i < output.size(); ++i) + { + output[i].a.p = output[i].a.p * mScaleFactor + mOffset; + output[i].b.p = output[i].b.p * mScaleFactor + mOffset; + output[i].c.p = output[i].c.p * mScaleFactor + mOffset; + } +} + +void FractureTool::tesselate(float averateEdgeLength) +{ + NVBLAST_ASSERT(mChunkPostprocessors.size() > 0); + if (mChunkPostprocessors.size() == 0) + { + return; // finalizeFracturing() should be called before tesselation! + } + + for (uint32_t i = 0; i < mChunkPostprocessors.size(); ++i) + { + if (mChunkData[i].chunkId == 0) // skip source mesh + { + continue; + } + mChunkPostprocessors[i]->tesselateInternalSurface(averateEdgeLength / mScaleFactor); + } +} + +void FractureTool::applyNoise(float amplitude, float frequency, int32_t octaves, float falloff, int32_t relaxIterations, float relaxFactor, int32_t seed) +{ + octaves = octaves <= 0 ? 1 : octaves; + if (mChunkPostprocessors.empty()) + { + return; + } + SimplexNoise noise(amplitude / mScaleFactor, frequency * mScaleFactor, octaves, seed); + for (uint32_t i = 0; i < mChunkPostprocessors.size(); ++i) + { + if (mChunkData[i].chunkId == 0) // skip source mesh + { + continue; + } + mChunkPostprocessors[i]->applyNoise(noise, falloff, relaxIterations, relaxFactor); + } +} + +float getVolume(std::vector<Triangle>& triangles) +{ + float volume = 0.0f; + + for (uint32_t i = 0; i < triangles.size(); ++i) + { + PxVec3& a = triangles[i].a.p; + PxVec3& b = triangles[i].b.p; + PxVec3& c = triangles[i].c.p; + volume += (a.x * b.y * c.z - a.x * b.z * c.y - a.y * b.x * c.z + a.y * b.z * c.x + a.z * b.x * c.y - a.z * b.y * c.x); + } + return (1.0f / 6.0f) * PxAbs(volume); +} + +float FractureTool::getMeshOverlap(Mesh& meshA, Mesh& meshB) +{ + BooleanEvaluator bTool; + bTool.performBoolean(&meshA, &meshB, BooleanConfigurations::BOOLEAN_INTERSECION()); + Mesh* result = bTool.createNewMesh(); + if (result == nullptr) + { + return 0.0f; + } + + ChunkPostProcessor postProcessor; + postProcessor.triangulate(&meshA); + + float baseVolume = getVolume(postProcessor.getBaseMesh()); + if (baseVolume == 0) + { + return 0.0f; + } + postProcessor.triangulate(result); + float intrsVolume = getVolume(postProcessor.getBaseMesh()); + + delete result; + + return intrsVolume / baseVolume; +} + +void weldVertices(std::map<Vertex, uint32_t, VrtComp>& vertexMapping, std::vector<Vertex>& vertexBuffer, std::vector<uint32_t>& indexBuffer, std::vector<Triangle>& trb) +{ + for (uint32_t i = 0; i < trb.size(); ++i) + { + auto it = vertexMapping.find(trb[i].a); + if (it == vertexMapping.end()) + { + indexBuffer.push_back(static_cast<uint32_t>(vertexBuffer.size())); + vertexMapping[trb[i].a] = static_cast<uint32_t>(vertexBuffer.size()); + vertexBuffer.push_back(trb[i].a); + } + else + { + indexBuffer.push_back(it->second); + } + it = vertexMapping.find(trb[i].b); + if (it == vertexMapping.end()) + { + indexBuffer.push_back(static_cast<uint32_t>(vertexBuffer.size())); + vertexMapping[trb[i].b] = static_cast<uint32_t>(vertexBuffer.size()); + vertexBuffer.push_back(trb[i].b); + } + else + { + indexBuffer.push_back(it->second); + } + it = vertexMapping.find(trb[i].c); + if (it == vertexMapping.end()) + { + indexBuffer.push_back(static_cast<uint32_t>(vertexBuffer.size())); + vertexMapping[trb[i].c] = static_cast<uint32_t>(vertexBuffer.size()); + vertexBuffer.push_back(trb[i].c); + } + else + { + indexBuffer.push_back(it->second); + } + } + +} + +void FractureTool::setRemoveIslands(bool isRemoveIslands) +{ + mRemoveIslands = isRemoveIslands; +} + +int32_t FractureTool::islandDetectionAndRemoving(int32_t chunkId) +{ + if (chunkId == 0) + { + return 0; + } + int32_t chunkIndex = getChunkIndex(chunkId); + ChunkPostProcessor prc; + prc.triangulate(mChunkData[chunkIndex].meshData); + + Mesh* chunk = mChunkData[chunkIndex].meshData; + + std::vector<uint32_t>& mapping = prc.getBaseMapping(); + std::vector<TriangleIndexed>& trs = prc.getBaseMeshIndexed(); + + std::vector<std::vector<uint32_t> > graph(prc.getWeldedVerticesCount()); + std::vector<int32_t>& pm = prc.getPositionedMapping(); + if (pm.size() == 0) + { + return 0; + } + + /** + Chunk graph + */ + for (uint32_t i = 0; i < trs.size(); ++i) + { + graph[pm[trs[i].ea]].push_back(pm[trs[i].eb]); + graph[pm[trs[i].ea]].push_back(pm[trs[i].ec]); + + graph[pm[trs[i].ec]].push_back(pm[trs[i].eb]); + graph[pm[trs[i].ec]].push_back(pm[trs[i].ea]); + + graph[pm[trs[i].eb]].push_back(pm[trs[i].ea]); + graph[pm[trs[i].eb]].push_back(pm[trs[i].ec]); + } + for (uint32_t i = 0; i < chunk->getEdgesCount(); ++i) + { + int v1 = chunk->getEdges()[i].s; + int v2 = chunk->getEdges()[i].e; + + v1 = pm[mapping[v1]]; + v2 = pm[mapping[v2]]; + + graph[v1].push_back(v2); + graph[v2].push_back(v1); + + } + + + /** + Walk graph, mark components + */ + + std::vector<int32_t> comps(prc.getWeldedVerticesCount(), -1); + std::queue<uint32_t> que; + int32_t cComp = 0; + + for (uint32_t i = 0; i < prc.getWeldedVerticesCount(); ++i) + { + int32_t to = pm[i]; + if (comps[to] != -1) continue; + que.push(to); + comps[to] = cComp; + + while (!que.empty()) + { + int32_t c = que.front(); + que.pop(); + + for (uint32_t j = 0; j < graph[c].size(); ++j) + { + if (comps[graph[c][j]] == -1) + { + que.push(graph[c][j]); + comps[graph[c][j]] = cComp; + } + } + } + cComp++; + } + for (uint32_t i = 0; i < prc.getWeldedVerticesCount(); ++i) + { + int32_t to = pm[i]; + comps[i] = comps[to]; + } + std::vector<uint32_t> longComps(chunk->getVerticesCount()); + for (uint32_t i = 0; i < chunk->getVerticesCount(); ++i) + { + int32_t to = mapping[i]; + longComps[i] = comps[to]; + } + + if (cComp > 1) + { + std::vector<std::vector<Vertex> > compVertices(cComp); + std::vector<std::vector<Facet> > compFacets(cComp); + std::vector<std::vector<Edge> > compEdges(cComp); + + + std::vector<uint32_t> compVertexMapping(chunk->getVerticesCount(), 0); + Vertex* vrts = chunk->getVertices(); + for (uint32_t v = 0; v < chunk->getVerticesCount(); ++v) + { + int32_t vComp = comps[mapping[v]]; + compVertexMapping[v] = static_cast<uint32_t>(compVertices[vComp].size()); + compVertices[vComp].push_back(vrts[v]); + } + + Facet* fcb = chunk->getFacetsBuffer(); + Edge* edb = chunk->getEdges(); + + for (uint32_t fc = 0; fc < chunk->getFacetCount(); ++fc) + { + std::vector<uint32_t> edgesPerComp(cComp, 0); + for (uint32_t ep = fcb[fc].firstEdgeNumber; ep < fcb[fc].firstEdgeNumber + fcb[fc].edgesCount; ++ep) + { + int32_t vComp = comps[mapping[edb[ep].s]]; + edgesPerComp[vComp]++; + compEdges[vComp].push_back(Edge(compVertexMapping[edb[ep].s], compVertexMapping[edb[ep].e])); + } + for (int32_t c = 0; c < cComp; ++c) + { + if (edgesPerComp[c] == 0) + { + continue; + } + compFacets[c].push_back(*chunk->getFacet(fc)); + compFacets[c].back().edgesCount = edgesPerComp[c]; + compFacets[c].back().firstEdgeNumber = static_cast<int32_t>(compEdges[c].size()) - edgesPerComp[c]; + } + } + + delete mChunkData[chunkIndex].meshData; + mChunkData[chunkIndex].meshData = new Mesh(compVertices[0].data(), compEdges[0].data(), compFacets[0].data(), static_cast<uint32_t>(compVertices[0].size()), + static_cast<uint32_t>(compEdges[0].size()), static_cast<uint32_t>(compFacets[0].size()));; + for (int32_t i = 1; i < cComp; ++i) + { + mChunkData.push_back(ChunkInfo(mChunkData[chunkIndex])); + mChunkData.back().chunkId = mChunkIdCounter++; + mChunkData.back().meshData = new Mesh(compVertices[i].data(), compEdges[i].data(), compFacets[i].data(), static_cast<uint32_t>(compVertices[i].size()), + static_cast<uint32_t>(compEdges[i].size()), static_cast<uint32_t>(compFacets[i].size())); + } + + return cComp; + } + return 0; +} + +void FractureTool::getBufferedBaseMeshes(std::vector<Vertex>& vertexBuffer, std::vector<std::vector<uint32_t> >& indexBuffer) +{ + std::map<Vertex, uint32_t, VrtComp> vertexMapping; + vertexBuffer.clear(); + indexBuffer.clear(); + indexBuffer.resize(mChunkPostprocessors.size()); + + for (uint32_t ch = 0; ch < mChunkPostprocessors.size(); ++ch) + { + std::vector<Triangle>& trb = mChunkPostprocessors[ch]->getBaseMesh(); + weldVertices(vertexMapping, vertexBuffer, indexBuffer[ch], trb); + } + for (uint32_t i = 0; i < vertexBuffer.size(); ++i) + { + vertexBuffer[i].p = vertexBuffer[i].p * mScaleFactor + mOffset; + } +} + +int32_t FractureTool::getChunkId(int32_t chunkIndex) +{ + if (chunkIndex < 0 || static_cast<uint32_t>(chunkIndex) >= mChunkData.size()) + { + return -1; + } + return mChunkData[chunkIndex].chunkId; +} + +void FractureTool::getBufferedNoiseMeshes(std::vector<Vertex>& vertexBuffer, std::vector<std::vector<uint32_t> >& indexBuffer) +{ + std::map<Vertex, uint32_t, VrtComp> vertexMapping; + vertexBuffer.clear(); + indexBuffer.clear(); + indexBuffer.resize(mChunkPostprocessors.size()); + + for (uint32_t ch = 0; ch < mChunkPostprocessors.size(); ++ch) + { + if (ch == 0) + { + std::vector<Triangle>& trb = mChunkPostprocessors[ch]->getBaseMesh(); + weldVertices(vertexMapping, vertexBuffer, indexBuffer[ch], trb); + } + else + { + std::vector<Triangle>& trb = mChunkPostprocessors[ch]->getNoisyMesh(); + weldVertices(vertexMapping, vertexBuffer, indexBuffer[ch], trb); + } + } + for (uint32_t i = 0; i < vertexBuffer.size(); ++i) + { + vertexBuffer[i].p = vertexBuffer[i].p * mScaleFactor + mOffset; + } +} + + +} // namespace Blast +} // namespace Nv + diff --git a/NvBlast/sdk/extensions/authoring/source/NvBlastExtAuthoringInternalCommon.h b/NvBlast/sdk/extensions/authoring/source/NvBlastExtAuthoringInternalCommon.h new file mode 100644 index 0000000..b8fb20e --- /dev/null +++ b/NvBlast/sdk/extensions/authoring/source/NvBlastExtAuthoringInternalCommon.h @@ -0,0 +1,193 @@ +/* +* Copyright (c) 2017, 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. +*/ + +#ifndef NVBLASTINTERNALCOMMON_H +#define NVBLASTINTERNALCOMMON_H +#include "NvBlastExtAuthoringTypes.h" + +using namespace physx; + +namespace Nv +{ +namespace Blast +{ + +/** +Edge representation with index of parent facet +*/ +struct EdgeWithParent +{ + int32_t s, e; // Starting and ending vertices + int32_t parent; // Parent facet index + EdgeWithParent() : s(0), e(0), parent(0) {} + EdgeWithParent(int32_t s, int32_t e, int32_t p) : s(s), e(e), parent(p) {} +}; + + +/** +Comparator for sorting edges according to parent facet number. +*/ +struct EdgeComparator +{ + bool operator()(const EdgeWithParent& a, const EdgeWithParent& b) const + { + if (a.parent == b.parent) + { + if (a.s == b.s) + { + return a.e < b.e; + } + else + { + return a.s < b.s; + } + } + else + { + return a.parent < b.parent; + } + } +}; + + +/** +Vertex projection direction flag. +*/ +enum ProjectionDirections +{ + YZ_PLANE = 1 << 1, + XY_PLANE = 1 << 2, + ZX_PLANE = 1 << 3, + + OPPOSITE_WINDING = 1 << 4 +}; + +/** +Computes best direction to project points. +*/ +NV_FORCE_INLINE ProjectionDirections getProjectionDirection(const physx::PxVec3& normal) +{ + float maxv = std::max(abs(normal.x), std::max(abs(normal.y), abs(normal.z))); + ProjectionDirections retVal; + if (maxv == abs(normal.x)) + { + retVal = YZ_PLANE; + if (normal.x < 0) retVal = (ProjectionDirections)((int)retVal | (int)OPPOSITE_WINDING); + return retVal; + } + if (maxv == abs(normal.y)) + { + retVal = ZX_PLANE; + if (normal.y > 0) retVal = (ProjectionDirections)((int)retVal | (int)OPPOSITE_WINDING); + return retVal; + } + retVal = XY_PLANE; + if (normal.z < 0) retVal = (ProjectionDirections)((int)retVal | (int)OPPOSITE_WINDING); + return retVal; +} + + +/** +Computes point projected on given axis aligned plane. +*/ +NV_FORCE_INLINE physx::PxVec2 getProjectedPoint(const physx::PxVec3& point, ProjectionDirections dir) +{ + if (dir & YZ_PLANE) + { + return physx::PxVec2(point.y, point.z); + } + if (dir & ZX_PLANE) + { + return physx::PxVec2(point.x, point.z); + } + return physx::PxVec2(point.x, point.y); +} + +/** +Computes point projected on given axis aligned plane, this method is polygon-winding aware. +*/ +NV_FORCE_INLINE physx::PxVec2 getProjectedPointWithWinding(const physx::PxVec3& point, ProjectionDirections dir) +{ + if (dir & YZ_PLANE) + { + if (dir & OPPOSITE_WINDING) + { + return physx::PxVec2(point.z, point.y); + } + else + return physx::PxVec2(point.y, point.z); + } + if (dir & ZX_PLANE) + { + if (dir & OPPOSITE_WINDING) + { + return physx::PxVec2(point.z, point.x); + } + return physx::PxVec2(point.x, point.z); + } + if (dir & OPPOSITE_WINDING) + { + return physx::PxVec2(point.y, point.x); + } + return physx::PxVec2(point.x, point.y); +} + + + +#define MAXIMUM_EXTENT 1000 * 1000 * 1000 +#define BBOX_TEST_EPS 1e-5f + +/** +Test fattened bounding box intersetion. +*/ +NV_INLINE bool weakBoundingBoxIntersection(const physx::PxBounds3& aBox, const physx::PxBounds3& bBox) +{ + if (std::max(aBox.minimum.x, bBox.minimum.x) > std::min(aBox.maximum.x, bBox.maximum.x) + BBOX_TEST_EPS) + return false; + if (std::max(aBox.minimum.y, bBox.minimum.y) > std::min(aBox.maximum.y, bBox.maximum.y) + BBOX_TEST_EPS) + return false; + if (std::max(aBox.minimum.z, bBox.minimum.z) > std::min(aBox.maximum.z, bBox.maximum.z) + BBOX_TEST_EPS) + return false; + return true; +} + + + +/** +Test segment vs plane intersection. If segment intersects the plane true is returned. Point of intersection is written into 'result'. +*/ +NV_INLINE bool getPlaneSegmentIntersection(const PxPlane& pl, const PxVec3& a, const PxVec3& b, PxVec3& result) +{ + float div = (b - a).dot(pl.n); + if (PxAbs(div) < 0.0001f) + { + if (pl.contains(a)) + { + result = a; + return true; + } + else + { + return false; + } + } + float t = (-a.dot(pl.n) - pl.d) / div; + if (t < 0.0f || t > 1.0f) + { + return false; + } + result = (b - a) * t + a; + return true; +} + +} // namespace Blast +} // namespace Nv + +#endif
\ No newline at end of file diff --git a/NvBlast/sdk/extensions/authoring/source/NvBlastExtAuthoringMesh.cpp b/NvBlast/sdk/extensions/authoring/source/NvBlastExtAuthoringMesh.cpp new file mode 100644 index 0000000..a25d2fe --- /dev/null +++ b/NvBlast/sdk/extensions/authoring/source/NvBlastExtAuthoringMesh.cpp @@ -0,0 +1,558 @@ +/* +* Copyright (c) 2016-2017, 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. +*/ + +#define _CRT_SECURE_NO_WARNINGS + +#include "NvBlastExtAuthoringMesh.h" +#include "NvBlastExtAuthoringTypes.h" +#include <string.h> +#include "NvBlastExtAuthoringPerlinNoise.h" + +using physx::PxVec2; +using physx::PxVec3; +using physx::PxBounds3; + +namespace Nv +{ +namespace Blast +{ + +Mesh::Mesh(PxVec3* position, PxVec3* normals, PxVec2* uv, uint32_t verticesCount, uint32_t* indices, uint32_t indicesCount) +{ + + mVertices.resize(verticesCount); + for (uint32_t i = 0; i < mVertices.size(); ++i) + { + mVertices[i].p = position[i]; + } + if (normals != 0) + { + for (uint32_t i = 0; i < mVertices.size(); ++i) + { + mVertices[i].n = normals[i]; + } + + } + else + { + for (uint32_t i = 0; i < mVertices.size(); ++i) + { + mVertices[i].n = PxVec3(0, 0, 0); + } + } + if (uv != 0) + { + for (uint32_t i = 0; i < mVertices.size(); ++i) + { + mVertices[i].uv[0] = uv[i]; + } + } + else + { + for (uint32_t i = 0; i < mVertices.size(); ++i) + { + mVertices[i].uv[0] = PxVec2(0, 0); + } + } + mEdges.resize(indicesCount); + mFacets.resize(indicesCount / 3); + mBounds.setEmpty(); + for (uint32_t i = 0; i < verticesCount; ++i) + { + mBounds.include(mVertices[i].p); + } + int32_t facetId = 0; + for (uint32_t i = 0; i < indicesCount; i += 3) + { + mEdges[i].s = indices[i]; + mEdges[i].e = indices[i + 1]; + + mEdges[i + 1].s = indices[i + 1]; + mEdges[i + 1].e = indices[i + 2]; + + mEdges[i + 2].s = indices[i + 2]; + mEdges[i + 2].e = indices[i]; + mFacets[facetId].firstEdgeNumber = i; + mFacets[facetId].edgesCount = 3; + facetId++; + } +} + +Mesh::Mesh(Vertex* vertices, Edge* edges, Facet* facets, uint32_t posCount, uint32_t edgesCount, uint32_t facetsCount) +{ + mVertices.resize(posCount); + mEdges.resize(edgesCount); + mFacets.resize(facetsCount); + + memcpy(mVertices.data(), vertices, sizeof(Vertex) * posCount); + memcpy(mEdges.data(), edges, sizeof(Edge) * edgesCount); + memcpy(mFacets.data(), facets, sizeof(Facet) * facetsCount); + mBounds.setEmpty(); + for (uint32_t i = 0; i < posCount; ++i) + { + mBounds.include(mVertices[i].p); + } +} + +float Mesh::getMeshVolume() +{ + /** + Check if mesh boundary consist only of triangles + */ + for (uint32_t i = 0; i < mFacets.size(); ++i) + { + if (mFacets[i].edgesCount != 3) + { + return 0.0f; + } + } + + float volume = 0; + for (uint32_t i = 0; i < mFacets.size(); ++i) + { + int32_t offset = mFacets[i].firstEdgeNumber; + PxVec3& a = mVertices[mEdges[offset].s].p; + PxVec3& b = mVertices[mEdges[offset + 1].s].p; + PxVec3& c = mVertices[mEdges[offset + 2].s].p; + + volume += (a.x * b.y * c.z - a.x * b.z * c.y - a.y * b.x * c.z + a.y * b.z * c.x + a.z * b.x * c.y - a.z * b.y * c.x); + } + return (1.0f / 6.0f) * abs(volume); +} + + +uint32_t Mesh::getFacetCount() +{ + return static_cast<uint32_t>(mFacets.size()); +} + +Vertex* Mesh::getVertices() +{ + return mVertices.data(); +} + +Edge* Mesh::getEdges() +{ + return mEdges.data(); +} + +uint32_t Mesh::getEdgesCount() +{ + return static_cast<uint32_t>(mEdges.size()); +} +uint32_t Mesh::getVerticesCount() +{ + return static_cast<uint32_t>(mVertices.size()); +} +Facet* Mesh::getFacetsBuffer() +{ + return mFacets.data(); +} +Facet* Mesh::getFacet(int32_t facet) +{ + return &mFacets[facet]; +} + + +Mesh::~Mesh() +{ +} + +PxBounds3& Mesh::getBoundingBox() +{ + return mBounds; +} + +void Mesh::recalculateBoundingBox() +{ + mBounds.setEmpty(); + for (uint32_t i = 0; i < mVertices.size(); ++i) + { + mBounds.include(mVertices[i].p); + } +} + + + +void getTangents(PxVec3& normal, PxVec3& t1, PxVec3& t2) +{ + + if (abs(normal.z) < 0.9) + { + t1 = normal.cross(PxVec3(0, 0, 1)); + } + else + { + t1 = normal.cross(PxVec3(1, 0, 0)); + } + t2 = t1.cross(normal); +} + +Mesh* getCuttingBox(const PxVec3& point, const PxVec3& normal, float size, int32_t id) +{ + PxVec3 lNormal = normal.getNormalized(); + PxVec3 t1, t2; + getTangents(lNormal, t1, t2); + + std::vector<Vertex> positions(8); + positions[0].p = point + (t1 + t2) * size; + positions[1].p = point + (t2 - t1) * size; + + positions[2].p = point + (-t1 - t2) * size; + positions[3].p = point + (t1 - t2) * size; + + + positions[4].p = point + (t1 + t2 + lNormal) * size; + positions[5].p = point + (t2 - t1 + lNormal) * size; + + positions[6].p = point + (-t1 - t2 + lNormal) * size; + positions[7].p = point + (t1 - t2 + lNormal) * size; + + positions[0].n = -lNormal; + positions[1].n = -lNormal; + + positions[2].n = -lNormal; + positions[3].n = -lNormal; + + + positions[4].n = -lNormal; + positions[5].n = -lNormal; + + positions[6].n = -lNormal; + positions[7].n = -lNormal; + + positions[0].uv[0] = PxVec2(0, 0); + positions[1].uv[0] = PxVec2(10, 0); + + positions[2].uv[0] = PxVec2(10, 10); + positions[3].uv[0] = PxVec2(0, 10); + + + positions[4].uv[0] = PxVec2(0, 0); + positions[5].uv[0] = PxVec2(10, 0); + + positions[6].uv[0] = PxVec2(10, 10); + positions[7].uv[0] = PxVec2(0, 10); + + + std::vector<Edge> edges; + std::vector<Facet> facets; + + edges.push_back(Edge(0, 1)); + edges.push_back(Edge(1, 2)); + edges.push_back(Edge(2, 3)); + edges.push_back(Edge(3, 0)); + facets.push_back(Facet(0, 4, id)); + + + edges.push_back(Edge(0, 3)); + edges.push_back(Edge(3, 7)); + edges.push_back(Edge(7, 4)); + edges.push_back(Edge(4, 0)); + facets.push_back(Facet(4, 4, id)); + + edges.push_back(Edge(3, 2)); + edges.push_back(Edge(2, 6)); + edges.push_back(Edge(6, 7)); + edges.push_back(Edge(7, 3)); + facets.push_back(Facet(8, 4, id)); + + edges.push_back(Edge(5, 6)); + edges.push_back(Edge(6, 2)); + edges.push_back(Edge(2, 1)); + edges.push_back(Edge(1, 5)); + facets.push_back(Facet(12, 4, id)); + + edges.push_back(Edge(4, 5)); + edges.push_back(Edge(5, 1)); + edges.push_back(Edge(1, 0)); + edges.push_back(Edge(0, 4)); + facets.push_back(Facet(16, 4, id)); + + edges.push_back(Edge(4, 7)); + edges.push_back(Edge(7, 6)); + edges.push_back(Edge(6, 5)); + edges.push_back(Edge(5, 4)); + facets.push_back(Facet(20, 4, id)); + return new Mesh(positions.data(), edges.data(), facets.data(), static_cast<uint32_t>(positions.size()), static_cast<uint32_t>(edges.size()), static_cast<uint32_t>(facets.size())); +} + +void inverseNormalAndSetIndices(Mesh* mesh, int32_t id) +{ + for (uint32_t i = 0; i < mesh->getVerticesCount(); ++i) + { + mesh->getVertices()[i].n *= -1.0f; + } + for (uint32_t i = 0; i < mesh->getFacetCount(); ++i) + { + mesh->getFacet(i)->userData = id; + } + +} + +void setCuttingBox(const PxVec3& point, const PxVec3& normal, Mesh* mesh, float size, int32_t id) +{ + PxVec3 t1, t2; + PxVec3 lNormal = normal.getNormalized(); + getTangents(lNormal, t1, t2); + + Vertex* positions = mesh->getVertices(); + positions[0].p = point + (t1 + t2) * size; + positions[1].p = point + (t2 - t1) * size; + + positions[2].p = point + (-t1 - t2) * size; + positions[3].p = point + (t1 - t2) * size; + + + positions[4].p = point + (t1 + t2 + lNormal) * size; + positions[5].p = point + (t2 - t1 + lNormal) * size; + + positions[6].p = point + (-t1 - t2 + lNormal) * size; + positions[7].p = point + (t1 - t2 + lNormal) * size; + + positions[0].n = -lNormal; + positions[1].n = -lNormal; + + positions[2].n = -lNormal; + positions[3].n = -lNormal; + + + positions[4].n = -lNormal; + positions[5].n = -lNormal; + + positions[6].n = -lNormal; + positions[7].n = -lNormal; + + for (uint32_t i = 0; i < mesh->getFacetCount(); ++i) + { + mesh->getFacet(i)->userData = id; + } + mesh->recalculateBoundingBox(); +} + +bool Mesh::isValid() +{ + return mVertices.size() > 0 && mEdges.size() > 0 && mFacets.size() > 0; +} + +Mesh* getNoisyCuttingBoxPair(const physx::PxVec3& point, const physx::PxVec3& normal, float size, float jaggedPlaneSize, uint32_t resolution, int32_t id, float amplitude, float frequency, int32_t octaves, int32_t seed) +{ + SimplexNoise nEval(amplitude, frequency, octaves, seed); + PxVec3 t1, t2; + PxVec3 lNormal = normal.getNormalized(); + getTangents(lNormal, t1, t2); + + std::vector<Vertex> vertices ((resolution + 1) * (resolution + 1) + 12); + PxVec3 cPosit = point + (t1 + t2) * jaggedPlaneSize; + PxVec3 t1d = -t1 * 2.0f * jaggedPlaneSize / resolution; + PxVec3 t2d = -t2 * 2.0f * jaggedPlaneSize / resolution; + + int32_t vrtId = 0; + for (uint32_t i = 0; i < resolution + 1; ++i) + { + PxVec3 lcPosit = cPosit; + for (uint32_t j = 0; j < resolution + 1; ++j) + { + vertices[vrtId].p = lcPosit; + lcPosit += t1d; + vrtId++; + } + cPosit += t2d; + } + + + for (uint32_t i = 1; i < resolution; ++i) + { + for (uint32_t j = 1; j < resolution; ++j) + { + PxVec3& pnt = vertices[i * (resolution + 1) + j].p; + pnt += lNormal * nEval.sample(pnt); + } + } + + std::vector<Edge> edges; + std::vector<Facet> facets; + for (uint32_t i = 0; i < resolution; ++i) + { + for (uint32_t j = 0; j < resolution; ++j) + { + uint32_t start = edges.size(); + edges.push_back(Edge(i * (resolution + 1) + j, i * (resolution + 1) + j + 1)); + edges.push_back(Edge(i * (resolution + 1) + j + 1, (i + 1) * (resolution + 1) + j + 1)); + edges.push_back(Edge((i + 1) * (resolution + 1) + j + 1, (i + 1) * (resolution + 1) + j)); + edges.push_back(Edge((i + 1) * (resolution + 1) + j, i * (resolution + 1) + j)); + facets.push_back(Facet(start, 4, id)); + } + } + uint32_t offset = (resolution + 1) * (resolution + 1); + + vertices[0 + offset].p = point + (t1 + t2) * size; + vertices[1 + offset].p = point + (t2 - t1) * size; + + vertices[2 + offset].p = point + (-t1 - t2) * size; + vertices[3 + offset].p = point + (t1 - t2) * size; + + vertices[8 + offset].p = point + (t1 + t2) * jaggedPlaneSize; + vertices[9 + offset].p = point + (t2 - t1) * jaggedPlaneSize; + + vertices[10 + offset].p = point + (-t1 - t2) * jaggedPlaneSize; + vertices[11 + offset].p = point + (t1 - t2) * jaggedPlaneSize; + + + vertices[4 + offset].p = point + (t1 + t2 + lNormal) * size; + vertices[5 + offset].p = point + (t2 - t1 + lNormal) * size; + + vertices[6 + offset].p = point + (-t1 - t2 + lNormal) * size; + vertices[7 + offset].p = point + (t1 - t2 + lNormal) * size; + + for (uint32_t i = 1; i < resolution; ++i) + { + for (uint32_t j = 1; j < resolution; ++j) + { + PxVec3 v1 = vertices[(resolution + 1) * (i + 1) + j].p - vertices[(resolution + 1) * i + j].p; + PxVec3 v2 = vertices[(resolution + 1) * (i) + j + 1].p - vertices[(resolution + 1) * i + j].p; + PxVec3 v3 = vertices[(resolution + 1) * (i - 1) + j].p - vertices[(resolution + 1) * i + j].p; + PxVec3 v4 = vertices[(resolution + 1) * (i) + j - 1].p - vertices[(resolution + 1) * i + j].p; + + vertices[(resolution + 1) * i + j].n = v1.cross(v2) + v2.cross(v3) + v3.cross(v4) + v4.cross(v1); + vertices[(resolution + 1) * i + j].n.normalize(); + } + } + + int32_t edgeOffset = edges.size(); + edges.push_back(Edge(0 + offset, 1 + offset)); + edges.push_back(Edge(1 + offset, 2 + offset)); + edges.push_back(Edge(2 + offset, 3 + offset)); + edges.push_back(Edge(3 + offset, 0 + offset)); + + edges.push_back(Edge(11 + offset, 10 + offset)); + edges.push_back(Edge(10 + offset, 9 + offset)); + edges.push_back(Edge(9 + offset, 8 + offset)); + edges.push_back(Edge(8 + offset, 11 + offset)); + + facets.push_back(Facet(edgeOffset, 8, id)); + + + + edges.push_back(Edge(0 + offset, 3 + offset)); + edges.push_back(Edge(3 + offset, 7 + offset)); + edges.push_back(Edge(7 + offset, 4 + offset)); + edges.push_back(Edge(4 + offset, 0 + offset)); + facets.push_back(Facet(8 + edgeOffset, 4, id)); + + edges.push_back(Edge(3 + offset, 2 + offset)); + edges.push_back(Edge(2 + offset, 6 + offset)); + edges.push_back(Edge(6 + offset, 7 + offset)); + edges.push_back(Edge(7 + offset, 3 + offset)); + facets.push_back(Facet(12 + edgeOffset, 4, id)); + + edges.push_back(Edge(5 + offset, 6 + offset)); + edges.push_back(Edge(6 + offset, 2 + offset)); + edges.push_back(Edge(2 + offset, 1 + offset)); + edges.push_back(Edge(1 + offset, 5 + offset)); + facets.push_back(Facet(16 + edgeOffset, 4, id)); + + edges.push_back(Edge(4 + offset, 5 + offset)); + edges.push_back(Edge(5 + offset, 1 + offset)); + edges.push_back(Edge(1 + offset, 0 + offset)); + edges.push_back(Edge(0 + offset, 4 + offset)); + facets.push_back(Facet(20 + edgeOffset, 4, id)); + + edges.push_back(Edge(4 + offset, 7 + offset)); + edges.push_back(Edge(7 + offset, 6 + offset)); + edges.push_back(Edge(6 + offset, 5 + offset)); + edges.push_back(Edge(5 + offset, 4 + offset)); + facets.push_back(Facet(24 + edgeOffset, 4, id)); + + // + return new Mesh(vertices.data(), edges.data(), facets.data(), vertices.size(), edges.size(), facets.size()); +} + +Mesh* getBigBox(const PxVec3& point, float size) +{ + PxVec3 normal(0, 0, 1); + normal.normalize(); + PxVec3 t1, t2; + getTangents(normal, t1, t2); + + std::vector<Vertex> positions(8); + positions[0].p = point + (t1 + t2 - normal) * size; + positions[1].p = point + (t2 - t1 - normal) * size; + + positions[2].p = point + (-t1 - t2 - normal) * size; + positions[3].p = point + (t1 - t2 - normal) * size; + + + positions[4].p = point + (t1 + t2 + normal) * size; + positions[5].p = point + (t2 - t1 + normal) * size; + + positions[6].p = point + (-t1 - t2 + normal) * size; + positions[7].p = point + (t1 - t2 + normal) * size; + + positions[0].uv[0] = PxVec2(0, 0); + positions[1].uv[0] = PxVec2(10, 0); + + positions[2].uv[0] = PxVec2(10, 10); + positions[3].uv[0] = PxVec2(0, 10); + + + positions[4].uv[0] = PxVec2(0, 0); + positions[5].uv[0] = PxVec2(10, 0); + + positions[6].uv[0] = PxVec2(10, 10); + positions[7].uv[0] = PxVec2(0, 10); + + + std::vector<Edge> edges; + std::vector<Facet> facets; + + edges.push_back(Edge(0, 1)); + edges.push_back(Edge(1, 2)); + edges.push_back(Edge(2, 3)); + edges.push_back(Edge(3, 0)); + facets.push_back(Facet(0, 4)); + + + edges.push_back(Edge(0, 3)); + edges.push_back(Edge(3, 7)); + edges.push_back(Edge(7, 4)); + edges.push_back(Edge(4, 0)); + facets.push_back(Facet(4, 4)); + + edges.push_back(Edge(3, 2)); + edges.push_back(Edge(2, 6)); + edges.push_back(Edge(6, 7)); + edges.push_back(Edge(7, 3)); + facets.push_back(Facet(8, 4)); + + edges.push_back(Edge(5, 6)); + edges.push_back(Edge(6, 2)); + edges.push_back(Edge(2, 1)); + edges.push_back(Edge(1, 5)); + facets.push_back(Facet(12, 4)); + + edges.push_back(Edge(4, 5)); + edges.push_back(Edge(5, 1)); + edges.push_back(Edge(1, 0)); + edges.push_back(Edge(0, 4)); + facets.push_back(Facet(16, 4)); + + edges.push_back(Edge(4, 7)); + edges.push_back(Edge(7, 6)); + edges.push_back(Edge(6, 5)); + edges.push_back(Edge(5, 4)); + facets.push_back(Facet(20, 4)); + for (int i = 0; i < 8; ++i) + positions[i].n = PxVec3(0, 0, 0); + return new Mesh(positions.data(), edges.data(), facets.data(), static_cast<uint32_t>(positions.size()), static_cast<uint32_t>(edges.size()), static_cast<uint32_t>(facets.size())); +} + +} // namespace Blast +} // namespace Nv diff --git a/NvBlast/sdk/extensions/authoring/source/NvBlastExtAuthoringPerlinNoise.h b/NvBlast/sdk/extensions/authoring/source/NvBlastExtAuthoringPerlinNoise.h new file mode 100644 index 0000000..95308c2 --- /dev/null +++ b/NvBlast/sdk/extensions/authoring/source/NvBlastExtAuthoringPerlinNoise.h @@ -0,0 +1,373 @@ +/* +* Copyright (c) 2017, 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. +*/ + +#ifndef NVBLASTEXTAUTHORINGPERLINNOISE_H +#define NVBLASTEXTAUTHORINGPERLINNOISE_H + + +#include <NvBlastExtAuthoringTypes.h> + +#include <PxVec4.h> +#include <PxVec3.h> + +#define PERLIN_NOISE_SAMPLE_TABLE 512 +using physx::PxVec3; +namespace Nv +{ +namespace Blast +{ + +/*********** + Noise generation routines, copied from Apex. +*/ + + +NV_INLINE float at3(const float& rx, const float& ry, const float& rz, const PxVec3 q) +{ + return rx * q[0] + ry * q[1] + rz * q[2]; +} + +NV_INLINE float fade(float t) { return t * t * t * (t * (t * 6.0f - 15.0f) + 10.0f); } + +NV_INLINE float lerp(float t, float a, float b) { return a + t * (b - a); } + +NV_INLINE void setup(int i, PxVec3 point, float& t, int& b0, int& b1, float& r0, float& r1) +{ + t = point[i] + (0x1000); + b0 = ((int)t) & (PERLIN_NOISE_SAMPLE_TABLE - 1); + b1 = (b0 + 1) & (PERLIN_NOISE_SAMPLE_TABLE - 1); + r0 = t - (int)t; + r1 = r0 - 1.0f; +} + + +NV_INLINE float noiseSample(PxVec3 point, int* p, PxVec3* g) +{ + int bx0, bx1, by0, by1, bz0, bz1, b00, b10, b01, b11; + float rx0, rx1, ry0, ry1, rz0, rz1, sy, sz, a, b, c, d, t, u, v; + PxVec3 q; + int i, j; + + setup(0, point, t, bx0, bx1, rx0, rx1); + setup(1, point, t, by0, by1, ry0, ry1); + setup(2, point, t, bz0, bz1, rz0, rz1); + + i = p[bx0]; + j = p[bx1]; + + b00 = p[i + by0]; + b10 = p[j + by0]; + b01 = p[i + by1]; + b11 = p[j + by1]; + + t = fade(rx0); + sy = fade(ry0); + sz = fade(rz0); + + q = g[b00 + bz0]; u = at3(rx0, ry0, rz0, q); + q = g[b10 + bz0]; v = at3(rx1, ry0, rz0, q); + a = lerp(t, u, v); + + q = g[b01 + bz0]; u = at3(rx0, ry1, rz0, q); + q = g[b11 + bz0]; v = at3(rx1, ry1, rz0, q); + b = lerp(t, u, v); + + c = lerp(sy, a, b); + + q = g[b00 + bz1]; u = at3(rx0, ry0, rz1, q); + q = g[b10 + bz1]; v = at3(rx1, ry0, rz1, q); + a = lerp(t, u, v); + + q = g[b01 + bz1]; u = at3(rx0, ry1, rz1, q); + q = g[b11 + bz1]; v = at3(rx1, ry1, rz1, q); + b = lerp(t, u, v); + + d = lerp(sy, a, b); + + return lerp(sz, c, d); +} + +/** + Perlin Noise generation tool +*/ +class PerlinNoise +{ +public: + /** + \param[in] rnd Random value generator + \param[in] octaves Number of noise octaves + \param[in] frequency Frequency of noise + \param[in] amplitude Amplitude of noise + */ + PerlinNoise(Nv::Blast::RandomGeneratorBase* rnd, int octaves = 1, float frequency = 1., float amplitude = 1.) + : mRnd(rnd), + mOctaves(octaves), + mFrequency(frequency), + mAmplitude(amplitude), + mbInit(false) + { + + } + + /* + Reset state of noise generator + \param[in] octaves Number of noise octaves + \param[in] frequency Frequency of noise + \param[in] amplitude Amplitude of noise + */ + void reset(int octaves = 1, float frequency = 1.f, float amplitude = 1.f) + { + mOctaves = octaves; + mFrequency = frequency; + mAmplitude = amplitude; + init(); + } + + /** + Get Perlin Noise value at given point + */ + float sample(const physx::PxVec3& point) + { + return perlinNoise(point); + } + +private: + PerlinNoise& operator=(const PerlinNoise&); + + float perlinNoise(physx::PxVec3 point) + { + if (!mbInit) + init(); + + const int octaves = mOctaves; + const float frequency = mFrequency; + float amplitude = mAmplitude; + float result = 0.0f; + + point *= frequency; + + for (int i = 0; i < octaves; ++i) + { + PxVec3 lpnt; + lpnt[0] = point.x; + lpnt[1] = point.y; + lpnt[2] = point.z; + result += (noiseSample(lpnt, p, g)) * amplitude; + point *= 2.0f; + amplitude *= 0.5f; + } + return result; + } + + void init(void) + { + mbInit = true; + + unsigned i, j; + int k; + + for (i = 0; i < (unsigned)PERLIN_NOISE_SAMPLE_TABLE; i++) + { + p[i] = (int)i; + for (j = 0; j < 3; ++j) + g[i][j] = mRnd->getRandomValue(); + g[i].normalize(); + } + + while (--i) + { + k = p[i]; + j = static_cast<uint32_t>(mRnd->getRandomValue() * PERLIN_NOISE_SAMPLE_TABLE); + p[i] = p[j]; + p[j] = k; + } + + for (i = 0; i < PERLIN_NOISE_SAMPLE_TABLE + 2; ++i) + { + p[(unsigned)PERLIN_NOISE_SAMPLE_TABLE + i] = p[i]; + for (j = 0; j < 3; ++j) + g[(unsigned)PERLIN_NOISE_SAMPLE_TABLE + i][j] = g[i][j]; + } + + } + + Nv::Blast::RandomGeneratorBase* mRnd; + int mOctaves; + float mFrequency; + float mAmplitude; + + // Permutation vector + int p[(unsigned)(PERLIN_NOISE_SAMPLE_TABLE + PERLIN_NOISE_SAMPLE_TABLE + 2)]; + // Gradient vector + PxVec3 g[(unsigned)(PERLIN_NOISE_SAMPLE_TABLE + PERLIN_NOISE_SAMPLE_TABLE + 2)]; + + bool mbInit; +}; + + +/** + Simplex noise generation tool +*/ +class SimplexNoise +{ + + int32_t mOctaves; + float mAmplitude; + float mFrequency; + int32_t mSeed; + + static const int X_NOISE_GEN = 1619; + static const int Y_NOISE_GEN = 31337; + static const int Z_NOISE_GEN = 6971; + static const int W_NOISE_GEN = 1999; + static const int SEED_NOISE_GEN = 1013; + static const int SHIFT_NOISE_GEN = 8; + + NV_INLINE int fastfloor(float x) + { + return (x >= 0) ? (int)x : (int)(x - 1); + } + + SimplexNoise& operator=(const SimplexNoise&) + { + return *this; + } + +public: + /** + \param[in] ampl Amplitude of noise + \param[in] freq Frequency of noise + \param[in] octaves Number of noise octaves + \param[in] seed Random seed value + */ + SimplexNoise(float ampl, float freq, int32_t octaves, int32_t seed) : mOctaves(octaves), mAmplitude(ampl), mFrequency(freq), mSeed(seed) {}; + // 4D simplex noise + // returns: (x,y,z) = noise grad, w = noise value + + /** + Evaluate noise at given 4d-point + \param[in] x x coordinate of point + \param[in] y y coordinate of point + \param[in] z z coordinate of point + \param[in] w w coordinate of point + \param[in] seed Random seed value + \return Noise valued vector (x,y,z) and scalar (w) + */ + physx::PxVec4 eval4D(float x, float y, float z, float w, int seed) + { + // The skewing and unskewing factors are hairy again for the 4D case + const float F4 = (physx::PxSqrt(5.0f) - 1.0f) / 4.0f; + const float G4 = (5.0f - physx::PxSqrt(5.0f)) / 20.0f; + // Skew the (x,y,z,w) space to determine which cell of 24 simplices we're in + float s = (x + y + z + w) * F4; // Factor for 4D skewing + int ix = fastfloor(x + s); + int iy = fastfloor(y + s); + int iz = fastfloor(z + s); + int iw = fastfloor(w + s); + float tu = (ix + iy + iz + iw) * G4; // Factor for 4D unskewing + // Unskew the cell origin back to (x,y,z,w) space + float x0 = x - (ix - tu); // The x,y,z,w distances from the cell origin + float y0 = y - (iy - tu); + float z0 = z - (iz - tu); + float w0 = w - (iw - tu); + + int c = (x0 > y0) ? (1 << 0) : (1 << 2); + c += (x0 > z0) ? (1 << 0) : (1 << 4); + c += (x0 > w0) ? (1 << 0) : (1 << 6); + c += (y0 > z0) ? (1 << 2) : (1 << 4); + c += (y0 > w0) ? (1 << 2) : (1 << 6); + c += (z0 > w0) ? (1 << 4) : (1 << 6); + + physx::PxVec4 res; + res.setZero(); + + // Calculate the contribution from the five corners + for (int p = 4; p >= 0; --p) + { + int ixp = ((c >> 0) & 3) >= p ? 1 : 0; + int iyp = ((c >> 2) & 3) >= p ? 1 : 0; + int izp = ((c >> 4) & 3) >= p ? 1 : 0; + int iwp = ((c >> 6) & 3) >= p ? 1 : 0; + + float xp = x0 - ixp + (4 - p) * G4; + float yp = y0 - iyp + (4 - p) * G4; + float zp = z0 - izp + (4 - p) * G4; + float wp = w0 - iwp + (4 - p) * G4; + + float t = 0.6f - xp * xp - yp * yp - zp * zp - wp * wp; + if (t > 0) + { + //get index + int gradIndex = int(( + X_NOISE_GEN * (ix + ixp) + + Y_NOISE_GEN * (iy + iyp) + + Z_NOISE_GEN * (iz + izp) + + W_NOISE_GEN * (iw + iwp) + + SEED_NOISE_GEN * seed) + & 0xffffffff); + gradIndex ^= (gradIndex >> SHIFT_NOISE_GEN); + gradIndex &= 31; + + physx::PxVec4 g; + { + const int h = gradIndex; + const int hs = 2 - (h >> 4); + const int h1 = (h >> 3); + g.x = (h1 == 0) ? 0.0f : ((h & 4) ? -1.0f : 1.0f); + g.y = (h1 == 1) ? 0.0f : ((h & (hs << 1)) ? -1.0f : 1.0f); + g.z = (h1 == 2) ? 0.0f : ((h & hs) ? -1.0f : 1.0f); + g.w = (h1 == 3) ? 0.0f : ((h & 1) ? -1.0f : 1.0f); + } + float gdot = (g.x * xp + g.y * yp + g.z * zp + g.w * wp); + + float t2 = t * t; + float t3 = t2 * t; + float t4 = t3 * t; + + float dt4gdot = 8 * t3 * gdot; + + res.x += t4 * g.x - dt4gdot * xp; + res.y += t4 * g.y - dt4gdot * yp; + res.z += t4 * g.z - dt4gdot * zp; + res.w += t4 * gdot; + } + } + // scale the result to cover the range [-1,1] + res *= 27; + return res; + } + + /** + Evaluate noise at given 3d-point + \param[in] p Point in which noise will be evaluated + \return Noise value at given point + */ + float sample(physx::PxVec3 p) + { + p *= mFrequency; + float result = 0.0f; + float alpha = 1; + for (int32_t i = 1; i <= mOctaves; ++i) + { + result += eval4D(p.x * i, p.y * i, p.z * i, i * 5.0f, mSeed).w * alpha; + alpha *= 0.45; + } + return result * mAmplitude; + } + +}; + + + } // Blast namespace +} // Nv namespace + + + +#endif
\ No newline at end of file diff --git a/NvBlast/sdk/extensions/authoring/source/NvBlastExtAuthoringTriangulator.cpp b/NvBlast/sdk/extensions/authoring/source/NvBlastExtAuthoringTriangulator.cpp new file mode 100644 index 0000000..0b7187f --- /dev/null +++ b/NvBlast/sdk/extensions/authoring/source/NvBlastExtAuthoringTriangulator.cpp @@ -0,0 +1,1439 @@ +/* +* Copyright (c) 2016-2017, 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. +*/ + +// This warning arises when using some stl containers with older versions of VC +// c:\program files (x86)\microsoft visual studio 12.0\vc\include\xtree(1826): warning C4702: unreachable code +#include "NvPreprocessor.h" +#if NV_VC && NV_VC < 14 +#pragma warning(disable : 4702) +#endif +#include "NvBlastExtAuthoringTriangulator.h" +#include "NvBlastExtAuthoringMesh.h" +#include "NvBlastExtAuthoringTypes.h" +#include <math.h> +#include "NvPreprocessor.h" +#include <algorithm> +#include <vector> +#include <set> +#include "NvBlastExtAuthoringBooleanTool.h" +#include <queue> +#include "NvBlastExtAuthoringPerlinNoise.h" +#include <NvBlastAssert.h> + +using physx::PxVec3; +using physx::PxVec2; + +#define VEC_COMPARISON_OFFSET 1e-5f +#define TWO_VERTICES_THRESHOLD 1e-7 + +namespace Nv +{ +namespace Blast +{ + +bool VrtComp::operator()(const Vertex& a, const Vertex& b) const +{ + if (a.p.x + VEC_COMPARISON_OFFSET < b.p.x) return true; + if (a.p.x - VEC_COMPARISON_OFFSET > b.p.x) return false; + if (a.p.y + VEC_COMPARISON_OFFSET < b.p.y) return true; + if (a.p.y - VEC_COMPARISON_OFFSET > b.p.y) return false; + if (a.p.z + VEC_COMPARISON_OFFSET < b.p.z) return true; + if (a.p.z - VEC_COMPARISON_OFFSET > b.p.z) return false; + + if (a.n.x + 1e-3 < b.n.x) return true; + if (a.n.x - 1e-3 > b.n.x) return false; + if (a.n.y + 1e-3 < b.n.y) return true; + if (a.n.y - 1e-3 > b.n.y) return false; + if (a.n.z + 1e-3 < b.n.z) return true; + if (a.n.z - 1e-3 > b.n.z) return false; + + + if (a.uv[0].x + 1e-3 < b.uv[0].x) return true; + if (a.uv[0].x - 1e-3 > b.uv[0].x) return false; + if (a.uv[0].y + 1e-3 < b.uv[0].y) return true; + return false; +} + +bool VrtPositionComparator::operator()(const PxVec3& a, const PxVec3& b) const +{ + if (a.x + VEC_COMPARISON_OFFSET < b.x) return true; + if (a.x - VEC_COMPARISON_OFFSET > b.x) return false; + if (a.y + VEC_COMPARISON_OFFSET < b.y) return true; + if (a.y - VEC_COMPARISON_OFFSET > b.y) return false; + if (a.z + VEC_COMPARISON_OFFSET < b.z) return true; + if (a.z - VEC_COMPARISON_OFFSET > b.z) return false; + return false; +} + +NV_FORCE_INLINE bool compareTwoVertices(const PxVec3& a, const PxVec3& b) +{ + return std::abs(b.x - a.x) < TWO_VERTICES_THRESHOLD && std::abs(b.y - a.y) < TWO_VERTICES_THRESHOLD && std::abs(b.z - a.z) < TWO_VERTICES_THRESHOLD; +} +NV_FORCE_INLINE bool compareTwoVertices(const PxVec2& a, const PxVec2& b) +{ + return std::abs(b.x - a.x) < TWO_VERTICES_THRESHOLD && std::abs(b.y - a.y) < TWO_VERTICES_THRESHOLD; +} + +NV_FORCE_INLINE float getRotation(const PxVec2& a, const PxVec2& b) +{ + return a.x * b.y - a.y * b.x; +} + +inline bool pointInside(PxVec2 a, PxVec2 b, PxVec2 c, PxVec2 pnt) +{ + if (compareTwoVertices(a, pnt) || compareTwoVertices(b, pnt) || compareTwoVertices(c, pnt)) + { + return false; + } + float v1 = (getRotation((b - a), (pnt - a))); + float v2 = (getRotation((c - b), (pnt - b))); + float v3 = (getRotation((a - c), (pnt - c))); + + return (v1 >= 0.0f && v2 >= 0.0f && v3 >= 0.0f) || + (v1 <= 0.0f && v2 <= 0.0f && v3 <= 0.0f); + +} +void ChunkPostProcessor::triangulatePolygonWithEarClipping(std::vector<uint32_t>& inputPolygon, Vertex* vert, ProjectionDirections dir) +{ + // return; + //for (uint32_t i = 0; i < inputPolygon.size(); ++i) + //{ + // mBaseMeshTriangles.push_back(TriangleIndexed(inputPolygon[i], inputPolygon[i], inputPolygon[(i + 1) % inputPolygon.size()])); + //} + //return; + int32_t vCount = static_cast<int32_t>(inputPolygon.size()); + + if (vCount < 3) + { + return; + } + for (int32_t curr = 0; curr < vCount && vCount > 2; ++curr) + { + int32_t prev = (curr == 0) ? vCount - 1 : curr - 1; + int32_t next = (curr == vCount - 1) ? 0 : curr + 1; + + Vertex cV = vert[inputPolygon[curr]]; + Vertex nV = vert[inputPolygon[prev]]; + Vertex pV = vert[inputPolygon[next]]; + + PxVec2 cVp = getProjectedPoint(cV.p, dir); + PxVec2 nVp = getProjectedPoint(nV.p, dir); + PxVec2 pVp = getProjectedPoint(pV.p, dir); + + // Check wheather curr is ear-tip + float rot = getRotation((pVp - nVp).getNormalized(), (cVp - nVp).getNormalized()); + if (!(dir & OPPOSITE_WINDING)) rot = -rot; + if (rot > 0.0001) + { + bool good = true; + for (int vrt = 0; vrt < vCount; ++vrt) + { + if (vrt == curr || vrt == prev || vrt == next) continue; + if (pointInside(cVp, nVp, pVp, getProjectedPoint(vert[inputPolygon[vrt]].p, dir))) + { + good = false; + break; + } + } + if (good) + { + addEdgeTr(Edge(inputPolygon[curr], inputPolygon[prev])); + addEdgeTr(Edge(inputPolygon[next], inputPolygon[prev])); + addEdgeTr(Edge(inputPolygon[curr], inputPolygon[next])); + + mBaseMeshTriangles.push_back(TriangleIndexed(inputPolygon[curr], inputPolygon[prev], inputPolygon[next])); + vCount--; + inputPolygon.erase(inputPolygon.begin() + curr); + curr = -1; + } + } + } +} + + + +struct LoopInfo +{ + LoopInfo() + { + used = false; + } + PxVec3 normal; + float area; + int32_t index; + bool used; + bool operator<(const LoopInfo& b) const + { + return area < b.area; + } +}; + +int32_t unitePolygons(std::vector<uint32_t>& externalLoop, std::vector<uint32_t>& internalLoop, Vertex* vrx, ProjectionDirections dir) +{ + if (externalLoop.size() < 3 || internalLoop.size() < 3) + return 1; + /** + Find point with maximum x-coordinate + */ + float x_max = -MAXIMUM_EXTENT; + int32_t mIndex = -1; + for (uint32_t i = 0; i < internalLoop.size(); ++i) + { + float nx = getProjectedPoint(vrx[internalLoop[i]].p, dir).x; + if (nx > x_max) + { + mIndex = i; + x_max = nx; + } + } + if (mIndex == -1) + { + return 1; + } + + /** + Search for base point on external loop + */ + float minX = MAXIMUM_EXTENT; + int32_t vrtIndex = -1; + bool isFromBuffer = 0; + PxVec2 holePoint = getProjectedPoint(vrx[internalLoop[mIndex]].p, dir); + PxVec2 computedPoint; + for (uint32_t i = 0; i < externalLoop.size(); ++i) + { + int32_t nx = (i + 1) % externalLoop.size(); + PxVec2 pnt1 = getProjectedPoint(vrx[externalLoop[i]].p, dir); + PxVec2 pnt2 = getProjectedPoint(vrx[externalLoop[nx]].p, dir); + if (pnt1.x < x_max && pnt2.x < x_max) + { + continue; + } + PxVec2 vc = pnt2 - pnt1; + if (vc.y == 0 && pnt1.y == holePoint.y) + { + if (pnt1.x < minX && pnt1.x < pnt2.x && pnt1.x > x_max) + { + minX = pnt1.x; + vrtIndex = i; + isFromBuffer = true; + } + if (pnt2.x < minX && pnt2.x < pnt1.x && pnt2.x > x_max) + { + minX = pnt2.x; + vrtIndex = nx; + isFromBuffer = true; + } + } + else + { + float t = (holePoint.y - pnt1.y) / vc.y; + if (t <= 1 && t >= 0) + { + PxVec2 tempPoint = vc * t + pnt1; + if (tempPoint.x < minX && tempPoint.x > x_max) + { + minX = tempPoint.x; + vrtIndex = i; + isFromBuffer = false; + computedPoint = tempPoint; + } + } + } + } + if (vrtIndex == -1) + { + // std::cout << "Triangulation: base vertex for inner loop is not found..." << std::endl; + return 1; + } + int32_t bridgePoint = -1; + float bestAngle = 100; + if (!isFromBuffer) + { + PxVec2 ex1 = getProjectedPoint(vrx[externalLoop[vrtIndex]].p, dir); + PxVec2 ex2 = getProjectedPoint(vrx[externalLoop[(vrtIndex + 1) % externalLoop.size()]].p, dir); + + if (ex1.x > ex2.x) + { + vrtIndex = (vrtIndex + 1) % externalLoop.size(); + ex1 = ex2; + } + /* Check if some point is inside triangle */ + bool notFound = true; + for (int32_t i = 0; i < (int32_t)externalLoop.size(); ++i) + { + PxVec2 tempPoint = getProjectedPoint(vrx[externalLoop[i]].p, dir); + if (pointInside(holePoint, ex1, computedPoint, tempPoint)) + { + notFound = false; + PxVec2 cVp = getProjectedPoint(vrx[externalLoop[i]].p, dir); + PxVec2 pVp = getProjectedPoint(vrx[externalLoop[(i - 1 + externalLoop.size()) % externalLoop.size()]].p, dir); + PxVec2 nVp = getProjectedPoint(vrx[externalLoop[(i + 1) % externalLoop.size()]].p, dir); + float rt = getRotation((cVp - pVp).getNormalized(), (nVp - pVp).getNormalized()); + if ((dir & OPPOSITE_WINDING)) rt = -rt; + if (rt < 0.000001) + continue; + float tempAngle = PxVec2(1, 0).dot((tempPoint - holePoint).getNormalized()); + if (bestAngle < tempAngle) + { + bestAngle = tempAngle; + bridgePoint = i; + } + } + } + if (notFound) + { + bridgePoint = vrtIndex; + } + if (bridgePoint == -1) + { + // std::cout << "Triangulation: bridge vertex for inner loop is not found..." << std::endl; + return 1; + } + } + else + { + bridgePoint = vrtIndex; + } + std::vector<uint32_t> temporal; + + for (int32_t i = 0; i <= bridgePoint; ++i) + { + temporal.push_back(externalLoop[i]); + } + temporal.push_back(internalLoop[mIndex]); + for (int32_t i = (mIndex + 1) % internalLoop.size(); i != mIndex; i = (i + 1) % internalLoop.size()) + { + temporal.push_back(internalLoop[i]); + } + temporal.push_back(internalLoop[mIndex]); + for (uint32_t i = bridgePoint; i < externalLoop.size(); ++i) + { + temporal.push_back(externalLoop[i]); + } + externalLoop = temporal; + return 0; +} + +void ChunkPostProcessor::buildPolygonAndTriangulate(std::vector<Edge>& edges, Vertex* vertices, int32_t userData) +{ + std::vector<std::vector<uint32_t> > serializedLoops; + + std::set<int> visitedVertices; + std::vector<int> used(edges.size(), 0); + uint32_t collected = 0; + + std::vector<int> edgesIds; + /** + Add first edge to polygon + */ + edgesIds.push_back(0); + visitedVertices.insert(edges[0].s); + visitedVertices.insert(edges[0].e); + used[0] = true; + collected = 1; + uint32_t lastEdge = 0; + bool successfullPass = false; + for (; collected < edges.size();) + { + successfullPass = false; + for (uint32_t p = 0; p < edges.size(); ++p) + { + if (used[p] == 0 && edges[p].s == edges[lastEdge].e) + { + successfullPass = true; + collected++; + used[p] = true; + edgesIds.push_back(p); + lastEdge = p; + if (visitedVertices.find(edges[p].e) != visitedVertices.end()) // if we formed loop, detach it and triangulate + { + serializedLoops.push_back(std::vector<uint32_t>()); + std::vector<uint32_t>& serializedPositions = serializedLoops.back(); + while (edgesIds.size() > 0) + { + serializedPositions.push_back(edges[edgesIds.back()].s); + visitedVertices.erase(edges[edgesIds.back()].s); + if (edges[edgesIds.back()].s == edges[p].e) + { + edgesIds.pop_back(); + break; + } + edgesIds.pop_back(); + } + if (edgesIds.size() > 0) + { + lastEdge = edgesIds.back(); + } + else + { + for (uint32_t t = 0; t < edges.size(); ++t) + { + if (used[t] == 0) + { + edgesIds.push_back(t); + visitedVertices.insert(edges[t].s); + visitedVertices.insert(edges[t].e); + used[t] = true; + collected++; + lastEdge = t; + break; + } + } + } + } + else + { + visitedVertices.insert(edges[p].e); + } + } + } + if (!successfullPass) + { + break; + } + } + + std::vector<LoopInfo> loopsInfo(serializedLoops.size()); + // Compute normal to whole polygon, and areas of loops + PxVec3 wholeFacetNormal(0, 0, 0); + for (uint32_t loop = 0; loop < serializedLoops.size(); ++loop) + { + PxVec3 loopNormal(0, 0, 0); + std::vector<uint32_t>& pos = serializedLoops[loop]; + for (uint32_t vrt = 1; vrt + 1 < serializedLoops[loop].size(); ++vrt) + { + loopNormal += (vertices[pos[vrt]].p - vertices[pos[0]].p).cross(vertices[pos[vrt + 1]].p - vertices[pos[0]].p); + } + loopsInfo[loop].area = loopNormal.magnitude(); + loopsInfo[loop].normal = loopNormal; + loopsInfo[loop].index = loop; + wholeFacetNormal += loopNormal; + } + + // Change areas signs according to winding direction + for (uint32_t loop = 0; loop < serializedLoops.size(); ++loop) + { + if (wholeFacetNormal.dot(loopsInfo[loop].normal) < 0) + { + loopsInfo[loop].area = -loopsInfo[loop].area; + } + } + ProjectionDirections dir = getProjectionDirection(wholeFacetNormal); + std::sort(loopsInfo.begin(), loopsInfo.end()); + + std::vector<PxVec3> tempPositions; + int32_t oldSize = static_cast<int32_t>(mBaseMeshTriangles.size()); + for (uint32_t extPoly = 0; extPoly < loopsInfo.size(); ++extPoly) + { + if (loopsInfo[extPoly].area < 0) + { + continue; // Polygon with negative area is hole + } + int32_t baseLoop = loopsInfo[extPoly].index; + for (uint32_t intPoly = 0; intPoly < loopsInfo.size(); ++intPoly) + { + if (loopsInfo[intPoly].area > 0 || loopsInfo[intPoly].used || abs(loopsInfo[intPoly].area) > loopsInfo[extPoly].area) + { + continue; + } + int32_t holeLoop = loopsInfo[intPoly].index; + + if (!unitePolygons(serializedLoops[baseLoop], serializedLoops[holeLoop], vertices, dir)) + { + loopsInfo[intPoly].used = true; + }; + } + triangulatePolygonWithEarClipping(serializedLoops[baseLoop],vertices, dir); + } + for (uint32_t i = oldSize; i < mBaseMeshTriangles.size(); ++i) + { + mBaseMeshTriangles[i].userInfo = userData; + } +} + +NV_FORCE_INLINE int32_t ChunkPostProcessor::addVerticeIfNotExist(Vertex& p) +{ + auto it = mVertMap.find(p); + if (it == mVertMap.end()) + { + mVertMap[p] = static_cast<int32_t>(mVertices.size()); + mVertices.push_back(p); + return static_cast<int32_t>(mVertices.size()) - 1; + } + else + { + return it->second; + } +} + +NV_FORCE_INLINE void ChunkPostProcessor::addEdgeIfValid(EdgeWithParent& ed) +{ + if (ed.s == ed.e) + return; + EdgeWithParent opposite(ed.e, ed.s, ed.parent); + auto it = mEdgeMap.find(opposite); + if (it == mEdgeMap.end()) + { + mEdgeMap[ed] = static_cast<int32_t>(mBaseMeshEdges.size()); + mBaseMeshEdges.push_back(ed); + } + else + { + if (mBaseMeshEdges[it->second].s == NOT_VALID_VERTEX) + { + mBaseMeshEdges[it->second].s = ed.s; + mBaseMeshEdges[it->second].e = ed.e; + } + mBaseMeshEdges[it->second].s = NOT_VALID_VERTEX; + } +} + + + +void ChunkPostProcessor::prepare(Mesh* mesh) +{ + Edge* ed = mesh->getEdges(); + Vertex* vr = mesh->getVertices(); + mBaseMapping.resize(mesh->getVerticesCount()); + for (uint32_t i = 0; i < mesh->getFacetCount(); ++i) + { + Facet* fc = mesh->getFacet(i); + for (uint32_t j = fc->firstEdgeNumber; j < fc->firstEdgeNumber + fc->edgesCount; ++j) + { + int32_t a = addVerticeIfNotExist(vr[ed[j].s]); + int32_t b = addVerticeIfNotExist(vr[ed[j].e]); + mBaseMapping[ed[j].s] = a; + mBaseMapping[ed[j].e] = b; + EdgeWithParent e(a, b, i); + addEdgeIfValid(e); + } + } + std::vector<EdgeWithParent> temp; + temp.reserve(mBaseMeshEdges.size()); + for (uint32_t i = 0; i < mBaseMeshEdges.size(); ++i) + { + if (mBaseMeshEdges[i].s != NOT_VALID_VERTEX) + { + temp.push_back(mBaseMeshEdges[i]); + } + } + +} + +void ChunkPostProcessor::reset() +{ + isTesselated = false; + mVertices.clear(); + mBaseMeshEdges.clear(); + mVertMap.clear(); + mEdgeMap.clear(); + mTrMeshEdgeMap.clear(); + mTrMeshEdges.clear(); + mTrMeshEdToTr.clear(); + mBaseMeshTriangles.clear(); + mEdgeFlag.clear(); + mVertexValence.clear(); + mRestrictionFlag.clear(); + mVerticesDistances.clear(); + mVerticesNormalsSmoothed.clear(); + + mBaseMeshResultTriangles.clear(); + mTesselatedMeshResultTriangles.clear(); + mTesselatedMeshTriangles.clear(); +} + +void ChunkPostProcessor::triangulate(Mesh* mesh) +{ + reset(); + if (mesh == nullptr || !mesh->isValid()) + { + return; + } + prepare(mesh); + if (mBaseMeshEdges.empty()) + { + return; + } + std::vector<Edge> temp; + int32_t fP = mBaseMeshEdges[0].parent; + for (uint32_t i = 0; i < mBaseMeshEdges.size(); ++i) + { + if (fP != mBaseMeshEdges[i].parent) + { + if (temp.empty() == false) + { + buildPolygonAndTriangulate(temp, &mVertices[0], mesh->getFacet(fP)->userData); + } + temp.clear(); + fP = mBaseMeshEdges[i].parent; + } + temp.push_back(Edge(mBaseMeshEdges[i].s, mBaseMeshEdges[i].e)); + } + buildPolygonAndTriangulate(temp, &mVertices[0], mesh->getFacet(fP)->userData); + + /* Build final triangles */ + + mBaseMeshResultTriangles.clear(); + for (uint32_t i = 0; i < mBaseMeshTriangles.size(); ++i) + { + if (mBaseMeshTriangles[i].ea == NOT_VALID_VERTEX) + { + continue; + } + mBaseMeshResultTriangles.push_back(Triangle(mVertices[mBaseMeshTriangles[i].ea], mVertices[mBaseMeshTriangles[i].eb], mVertices[mBaseMeshTriangles[i].ec])); + mBaseMeshResultTriangles.back().userInfo = mBaseMeshTriangles[i].userInfo; + } + + computePositionedMapping(); +} + +void ChunkPostProcessor::prebuildTesselatedTriangles() +{ + mTesselatedMeshResultTriangles.clear(); + for (uint32_t i = 0; i < mTesselatedMeshTriangles.size(); ++i) + { + if (mTesselatedMeshTriangles[i].ea == NOT_VALID_VERTEX) + { + continue; + } + mTesselatedMeshResultTriangles.push_back(Triangle(mVertices[mTesselatedMeshTriangles[i].ea], mVertices[mTesselatedMeshTriangles[i].eb], mVertices[mTesselatedMeshTriangles[i].ec])); + mTesselatedMeshResultTriangles.back().userInfo = mTesselatedMeshTriangles[i].userInfo; + } + +} + + +int32_t ChunkPostProcessor::addEdgeTr(const Edge& e) +{ + Edge ed = e; + if (ed.e < ed.s) std::swap(ed.s, ed.e); + auto it = mTrMeshEdgeMap.find(ed); + if (it == mTrMeshEdgeMap.end()) + { + mTrMeshEdToTr.push_back(EdgeToTriangles()); + mTrMeshEdgeMap[ed] = (int)mTrMeshEdToTr.size() - 1; + mTrMeshEdges.push_back(ed); + mEdgeFlag.push_back(INTERNAL_EDGE); + return (int32_t)mTrMeshEdToTr.size() - 1; + } + else + { + return it->second; + } +} + +int32_t ChunkPostProcessor::findEdge(const Edge& e) +{ + Edge ed = e; + if (ed.e < ed.s) std::swap(ed.s, ed.e); + auto it = mTrMeshEdgeMap.find(ed); + if (it == mTrMeshEdgeMap.end()) + { + return -1; + } + return it->second; +} + +void ChunkPostProcessor::updateEdgeTriangleInfo() +{ + mTrMeshEdToTr.clear(); + mTrMeshEdToTr.resize(mTrMeshEdges.size()); + for (uint32_t i = 0; i < mTesselatedMeshTriangles.size(); ++i) + { + TriangleIndexed& tr = mTesselatedMeshTriangles[i]; + if (tr.ea == NOT_VALID_VERTEX) + continue; + int32_t ed = addEdgeTr(Edge(tr.ea, tr.eb)); + mTrMeshEdToTr[ed].add(i); + ed = addEdgeTr(Edge(tr.ea, tr.ec)); + mTrMeshEdToTr[ed].add(i); + ed = addEdgeTr(Edge(tr.ec, tr.eb)); + mTrMeshEdToTr[ed].add(i); + } +} + +void ChunkPostProcessor::updateVertEdgeInfo() +{ + mVertexToTriangleMap.clear(); + mVertexToTriangleMap.resize(mVertices.size()); + for (uint32_t i = 0; i < mTesselatedMeshTriangles.size(); ++i) + { + TriangleIndexed& tr = mTesselatedMeshTriangles[i]; + if (tr.ea == NOT_VALID_VERTEX) continue; + mVertexToTriangleMap[tr.ea].push_back(i); + mVertexToTriangleMap[tr.eb].push_back(i); + mVertexToTriangleMap[tr.ec].push_back(i); + } + mVertexValence.clear(); + mVertexValence.resize(mVertices.size(), 0); + + for (uint32_t i = 0; i < mTrMeshEdges.size(); ++i) + { + if (mTrMeshEdToTr[i].c != 0) + { + mVertexValence[mTrMeshEdges[i].s]++; + mVertexValence[mTrMeshEdges[i].e]++; + } + } +} + + +void ChunkPostProcessor::collapseEdge(int32_t id) +{ + Edge cEdge = mTrMeshEdges[id]; + uint32_t from = cEdge.s; + uint32_t to = cEdge.e; + + + if (mRestrictionFlag[from] && mRestrictionFlag[to]) + { + return; + } + + if (mVertexValence[from] > mVertexValence[to]) + { + std::swap(from, to); + } + + if (mRestrictionFlag[from]) + { + std::swap(from, to); + } + + std::set<int32_t> connectedToBegin; + std::set<int32_t> connectedToEnd; + std::set<int32_t> neighboorTriangles; + + int32_t trWithEdge[2] = {-1, -1}; + int32_t cntr = 0; + for (uint32_t i = 0; i < mVertexToTriangleMap[from].size(); ++i) + { + if (mTesselatedMeshTriangles[mVertexToTriangleMap[from][i]].ea == NOT_VALID_VERTEX) + continue; + if (neighboorTriangles.insert(mVertexToTriangleMap[from][i]).second && mTesselatedMeshTriangles[mVertexToTriangleMap[from][i]].isContainEdge(from, to)) + { + trWithEdge[cntr] = mVertexToTriangleMap[from][i]; + cntr++; + } + } + for (uint32_t i = 0; i < mVertexToTriangleMap[to].size(); ++i) + { + if (mTesselatedMeshTriangles[mVertexToTriangleMap[to][i]].ea == NOT_VALID_VERTEX) + continue; + if (neighboorTriangles.insert(mVertexToTriangleMap[to][i]).second && mTesselatedMeshTriangles[mVertexToTriangleMap[to][i]].isContainEdge(from, to)) + { + trWithEdge[cntr] = mVertexToTriangleMap[to][i]; + cntr++; + } + } + + if (cntr == 0) + { + return; + } + if (cntr > 2) + { + return; + } + + for (uint32_t i: neighboorTriangles) + { + if (mTesselatedMeshTriangles[i].ea == from || mTesselatedMeshTriangles[i].eb == from || mTesselatedMeshTriangles[i].ec == from) + { + if (mTesselatedMeshTriangles[i].ea != to && mTesselatedMeshTriangles[i].ea != from) + connectedToBegin.insert(mTesselatedMeshTriangles[i].ea); + if (mTesselatedMeshTriangles[i].eb != to && mTesselatedMeshTriangles[i].eb != from) + connectedToBegin.insert(mTesselatedMeshTriangles[i].eb); + if (mTesselatedMeshTriangles[i].ec != to && mTesselatedMeshTriangles[i].ec != from) + connectedToBegin.insert(mTesselatedMeshTriangles[i].ec); + } + + if (mTesselatedMeshTriangles[i].ea == to || mTesselatedMeshTriangles[i].eb == to || mTesselatedMeshTriangles[i].ec == to) + { + if (mTesselatedMeshTriangles[i].ea != to && mTesselatedMeshTriangles[i].ea != from) + connectedToEnd.insert(mTesselatedMeshTriangles[i].ea); + if (mTesselatedMeshTriangles[i].eb != to && mTesselatedMeshTriangles[i].eb != from) + connectedToEnd.insert(mTesselatedMeshTriangles[i].eb); + if (mTesselatedMeshTriangles[i].ec != to && mTesselatedMeshTriangles[i].ec != from) + connectedToEnd.insert(mTesselatedMeshTriangles[i].ec); + } + } + bool canBeCollapsed = true; + for (auto it = connectedToBegin.begin(); it != connectedToBegin.end(); ++it) + { + uint32_t currV = *it; + if (connectedToEnd.find(currV) == connectedToEnd.end()) + continue; + bool found = false; + for (int32_t tr : neighboorTriangles) + { + if ((mTesselatedMeshTriangles[tr].ea == from || mTesselatedMeshTriangles[tr].eb == from || mTesselatedMeshTriangles[tr].ec == from) && + (mTesselatedMeshTriangles[tr].ea == to || mTesselatedMeshTriangles[tr].eb == to || mTesselatedMeshTriangles[tr].ec == to) && + (mTesselatedMeshTriangles[tr].ea == currV || mTesselatedMeshTriangles[tr].eb == currV || mTesselatedMeshTriangles[tr].ec == currV)) + { + found = true; + break; + } + } + if (!found) + { + canBeCollapsed = false; + break; + } + } + + if (canBeCollapsed) + { + for (int32_t i : neighboorTriangles) + { + if (trWithEdge[0] == i) continue; + if (cntr == 2 && trWithEdge[1] == i) continue; + TriangleIndexed tr = mTesselatedMeshTriangles[i]; + PxVec3 oldNormal = (mVertices[tr.eb].p - mVertices[tr.ea].p).cross(mVertices[tr.ec].p - mVertices[tr.ea].p); + + if (tr.ea == from) + { + tr.ea = to; + } + else + if (tr.eb == from) + { + tr.eb = to; + } + else + if (tr.ec == from) + { + tr.ec = to; + } + PxVec3 newNormal = (mVertices[tr.eb].p - mVertices[tr.ea].p).cross(mVertices[tr.ec].p - mVertices[tr.ea].p); + if (newNormal.magnitude() < 1e-8f) + { + canBeCollapsed = false; + break; + } + if (oldNormal.dot(newNormal) < 0) + { + canBeCollapsed = false; + break; + } + } + } + + if (canBeCollapsed) + { + mTesselatedMeshTriangles[trWithEdge[0]].ea = NOT_VALID_VERTEX; + if (cntr == 2)mTesselatedMeshTriangles[trWithEdge[1]].ea = NOT_VALID_VERTEX; + + for (int32_t i : neighboorTriangles) + { + if (mTesselatedMeshTriangles[i].ea == NOT_VALID_VERTEX) + continue; + if (mTesselatedMeshTriangles[i].ea == from) + { + mTesselatedMeshTriangles[i].ea = to; + mVertexToTriangleMap[from].clear(); + mVertexToTriangleMap[to].push_back(i); + } + else + if (mTesselatedMeshTriangles[i].eb == from) + { + mTesselatedMeshTriangles[i].eb = to; + mVertexToTriangleMap[from].clear(); + mVertexToTriangleMap[to].push_back(i); + } + else + if (mTesselatedMeshTriangles[i].ec == from) + { + mTesselatedMeshTriangles[i].ec = to; + mVertexToTriangleMap[from].clear(); + mVertexToTriangleMap[to].push_back(i); + } + } + } +} + + +void ChunkPostProcessor::divideEdge(int32_t id) +{ + + if (mTrMeshEdToTr[id].c == 0 ) + { + return; + } + + Edge cEdge = mTrMeshEdges[id]; + EdgeFlag snapRestriction = mEdgeFlag[id]; + Vertex middle; + uint32_t nv = NOT_VALID_VERTEX; + for (int32_t t = 0; t < mTrMeshEdToTr[id].c; ++t) + { + int32_t oldTriangleIndex = mTrMeshEdToTr[id].tr[t]; + TriangleIndexed tr = mTesselatedMeshTriangles[mTrMeshEdToTr[id].tr[t]]; + + if (tr.ea == NOT_VALID_VERTEX) + { + continue; + } + + uint32_t pbf[3]; + pbf[0] = tr.ea; + pbf[1] = tr.eb; + pbf[2] = tr.ec; + for (int32_t p = 0; p < 3; ++p) + { + int32_t pnx = (p + 1) % 3; + int32_t opp = (p + 2) % 3; + + if ((pbf[p] == cEdge.s && pbf[pnx] == cEdge.e) || (pbf[p] == cEdge.e && pbf[pnx] == cEdge.s)) + { + if (nv == NOT_VALID_VERTEX) + { + middle.p = (mVertices[pbf[p]].p + mVertices[pbf[pnx]].p) * 0.5f; + middle.n = (mVertices[pbf[p]].n + mVertices[pbf[pnx]].n) * 0.5f; + middle.uv[0] = (mVertices[pbf[p]].uv[0] + mVertices[pbf[pnx]].uv[0]) * 0.5f; + + nv = (uint32_t)mVertices.size(); + mVertices.push_back(middle); + } + if (nv < mRestrictionFlag.size()) + { + mRestrictionFlag[nv] = ((snapRestriction == EXTERNAL_BORDER_EDGE) || (snapRestriction == INTERNAL_BORDER_EDGE)); + } + else + { + mRestrictionFlag.push_back((snapRestriction == EXTERNAL_BORDER_EDGE) || (snapRestriction == INTERNAL_BORDER_EDGE)); + } + + uint32_t ind1 = addEdgeTr(Edge(pbf[p], nv)); + uint32_t ind2 = addEdgeTr(Edge(nv, pbf[pnx])); + uint32_t ind3 = addEdgeTr(Edge(nv, pbf[opp])); + + + mEdgeFlag[ind1] = snapRestriction; + mEdgeFlag[ind2] = snapRestriction; + mEdgeFlag[ind3] = INTERNAL_EDGE; + + mTrMeshEdToTr[ind1].add(mTrMeshEdToTr[id].tr[t]); + int32_t userInfo = mTesselatedMeshTriangles[mTrMeshEdToTr[id].tr[t]].userInfo; + mTesselatedMeshTriangles[mTrMeshEdToTr[id].tr[t]] = TriangleIndexed(pbf[p], nv, pbf[opp]); + mTesselatedMeshTriangles[mTrMeshEdToTr[id].tr[t]].userInfo = userInfo; + mTrMeshEdToTr[ind2].add((int32_t)mTesselatedMeshTriangles.size()); + mTrMeshEdToTr[ind3].add((int32_t)mTrMeshEdToTr[id].tr[t]); + mTrMeshEdToTr[ind3].add((int32_t)mTesselatedMeshTriangles.size()); + mTesselatedMeshTriangles.push_back(TriangleIndexed(nv,pbf[pnx], pbf[opp])); + mTesselatedMeshTriangles.back().userInfo = userInfo; + int32_t ed1 = findEdge(Edge(pbf[pnx], pbf[opp])); + mTrMeshEdToTr[ed1].replace(oldTriangleIndex, (int32_t)mTesselatedMeshTriangles.size() - 1); + break; + } + } + } +} + + +NV_FORCE_INLINE void markEdge(int32_t ui, int32_t ed, std::vector<ChunkPostProcessor::EdgeFlag>& shortMarkup, std::vector<int32_t>& lastOwner) +{ + if (shortMarkup[ed] == ChunkPostProcessor::NONE) + { + if (ui == 0) + { + shortMarkup[ed] = ChunkPostProcessor::EXTERNAL_EDGE; + } + else + { + shortMarkup[ed] = ChunkPostProcessor::INTERNAL_EDGE; + } + lastOwner[ed] = ui; + } + else + { + if (ui != 0) + { + if (shortMarkup[ed] == ChunkPostProcessor::EXTERNAL_EDGE) + { + shortMarkup[ed] = ChunkPostProcessor::EXTERNAL_BORDER_EDGE; + } + if ((shortMarkup[ed] == ChunkPostProcessor::INTERNAL_EDGE) && ui != lastOwner[ed]) + { + shortMarkup[ed] = ChunkPostProcessor::INTERNAL_BORDER_EDGE; + } + } + else + { + if (shortMarkup[ed] != ChunkPostProcessor::EXTERNAL_EDGE) + { + shortMarkup[ed] = ChunkPostProcessor::EXTERNAL_BORDER_EDGE; + } + } + } +} + +float falloffFunction(float x, float mx) +{ + float t = (x) / (mx + 1e-6f); + t = std::min(1.0f, t); + return t * t; +} + +void ChunkPostProcessor::recalcNoiseDirs() +{ + /** + Compute normals direction to apply noise + */ + mVerticesNormalsSmoothed.resize(mVertices.size(), PxVec3(0, 0, 0)); + for (uint32_t i = 0; i < mTesselatedMeshTriangles.size(); ++i) + { + if (mTesselatedMeshTriangles[i].ea == NOT_VALID_VERTEX) + { + continue; + } + TriangleIndexed& tr = mTesselatedMeshTriangles[i]; + if (tr.userInfo == 0) continue; + + if (tr.userInfo < 0) + mVerticesNormalsSmoothed[mPositionMappedVrt[tr.ea]] += mVertices[tr.ea].n.getNormalized(); + else + mVerticesNormalsSmoothed[mPositionMappedVrt[tr.ea]] -= mVertices[tr.ea].n.getNormalized(); + + if (tr.userInfo < 0) + mVerticesNormalsSmoothed[mPositionMappedVrt[tr.eb]] += mVertices[tr.eb].n.getNormalized(); + else + mVerticesNormalsSmoothed[mPositionMappedVrt[tr.eb]] -= mVertices[tr.eb].n.getNormalized(); + + if (tr.userInfo < 0) + mVerticesNormalsSmoothed[mPositionMappedVrt[tr.ec]] += mVertices[tr.ec].n.getNormalized(); + else + mVerticesNormalsSmoothed[mPositionMappedVrt[tr.ec]] -= mVertices[tr.ec].n.getNormalized(); + + } + for (uint32_t i = 0; i < mVerticesNormalsSmoothed.size(); ++i) + { + + mVerticesNormalsSmoothed[i] = mVerticesNormalsSmoothed[mPositionMappedVrt[i]]; + mVerticesNormalsSmoothed[i].normalize(); + } +} + + + +void ChunkPostProcessor::applyNoise(SimplexNoise& noise, float falloff, int32_t relaxIterations, float relaxFactor) +{ + NVBLAST_ASSERT(isTesselated); + if (isTesselated == false) + { + return; + } + mRestrictionFlag.clear(); + mRestrictionFlag.resize(mVertices.size(), false); + + for (uint32_t i = 0; i < mTrMeshEdges.size(); ++i) + { + if (mTrMeshEdToTr[i].c != 0) + { + if (mEdgeFlag[i] == EXTERNAL_EDGE || mEdgeFlag[i] == EXTERNAL_BORDER_EDGE) + { + mRestrictionFlag[mTrMeshEdges[i].e] = true; + mRestrictionFlag[mTrMeshEdges[i].s] = true; + } + } + } + std::vector<Vertex> localVertices = mVertices; + + recalcNoiseDirs(); + + relax(relaxIterations, relaxFactor, localVertices); + + + /** + Apply noise + */ + for (uint32_t i = 0; i < localVertices.size(); ++i) + { + + if (!mRestrictionFlag[i]) + { + + float d = noise.sample(localVertices[i].p); + localVertices[i].p += (falloffFunction(mVerticesDistances[i], falloff)) * mVerticesNormalsSmoothed[i] * d; + } + } + + + /* Recalculate smoothed normals*/ + mVerticesNormalsSmoothed.assign(mVerticesNormalsSmoothed.size(), PxVec3(0, 0, 0)); + for (uint32_t i = 0; i < mTesselatedMeshTriangles.size(); ++i) + { + if (mTesselatedMeshTriangles[i].ea == NOT_VALID_VERTEX) + { + continue; + } + TriangleIndexed& tr = mTesselatedMeshTriangles[i]; + if (tr.userInfo == 0) continue; + + Triangle pTr(localVertices[tr.ea], localVertices[tr.eb], localVertices[tr.ec]); + PxVec3 nrm = pTr.getNormal().getNormalized(); + + mVerticesNormalsSmoothed[mPositionMappedVrt[tr.ea]] += nrm; + mVerticesNormalsSmoothed[mPositionMappedVrt[tr.eb]] += nrm; + mVerticesNormalsSmoothed[mPositionMappedVrt[tr.ec]] += nrm; + } + for (uint32_t i = 0; i < mVerticesNormalsSmoothed.size(); ++i) + { + mVerticesNormalsSmoothed[i] = mVerticesNormalsSmoothed[mPositionMappedVrt[i]]; + mVerticesNormalsSmoothed[i].normalize(); + } + for (uint32_t i = 0; i < mTesselatedMeshTriangles.size(); ++i) + { + if (mTesselatedMeshTriangles[i].ea == NOT_VALID_VERTEX) + { + continue; + } + TriangleIndexed& tr = mTesselatedMeshTriangles[i]; + if (tr.userInfo == 0) continue; + + localVertices[tr.ea].n = mVerticesNormalsSmoothed[mPositionMappedVrt[tr.ea]]; + localVertices[tr.eb].n = mVerticesNormalsSmoothed[mPositionMappedVrt[tr.eb]]; + localVertices[tr.ec].n = mVerticesNormalsSmoothed[mPositionMappedVrt[tr.ec]]; + } + + mTesselatedMeshResultTriangles.clear(); + for (uint32_t i = 0; i < mTesselatedMeshTriangles.size(); ++i) + { + if (mTesselatedMeshTriangles[i].ea == NOT_VALID_VERTEX) + { + continue; + } + mTesselatedMeshResultTriangles.push_back(Triangle(localVertices[mTesselatedMeshTriangles[i].ea], localVertices[mTesselatedMeshTriangles[i].eb], localVertices[mTesselatedMeshTriangles[i].ec])); + mTesselatedMeshResultTriangles.back().userInfo = mTesselatedMeshTriangles[i].userInfo; + } + + +} + + +void ChunkPostProcessor::computePositionedMapping() +{ + std::map<PxVec3, int32_t, VrtPositionComparator> mPosMap; + mPositionMappedVrt.clear(); + mPositionMappedVrt.resize(mVertices.size()); + + for (uint32_t i = 0; i < mVertices.size(); ++i) + { + auto it = mPosMap.find(mVertices[i].p); + + if (it == mPosMap.end()) + { + mPosMap[mVertices[i].p] = i; + mPositionMappedVrt[i] = i; + } + else + { + mPositionMappedVrt[i] = it->second; + } + } +} + +void ChunkPostProcessor::computeFalloffAndNormals() +{ + // Map newly created vertices according to positions + + computePositionedMapping(); + + mGeometryGraph.resize(mVertices.size()); + for (uint32_t i = 0; i < mTrMeshEdges.size(); ++i) + { + if (mTrMeshEdToTr[i].c == 0) + { + continue; + } + int32_t v1 = mPositionMappedVrt[mTrMeshEdges[i].s]; + int32_t v2 = mPositionMappedVrt[mTrMeshEdges[i].e]; + + if (std::find(mGeometryGraph[v1].begin(), mGeometryGraph[v1].end(), v2) == mGeometryGraph[v1].end()) + mGeometryGraph[v1].push_back(v2); + if (std::find(mGeometryGraph[v2].begin(), mGeometryGraph[v2].end(), v1) == mGeometryGraph[v2].end()) + mGeometryGraph[v2].push_back(v1); + } + mVerticesDistances.clear(); + mVerticesDistances.resize(mVertices.size(), 10000.0f); + + std::queue<int32_t> que; + + for (uint32_t i = 0; i < mTrMeshEdges.size(); ++i) + { + if (mTrMeshEdToTr[i].c != 0 && (mEdgeFlag[i] == EXTERNAL_EDGE || mEdgeFlag[i] == EXTERNAL_BORDER_EDGE)) + { + int32_t v1 = mPositionMappedVrt[mTrMeshEdges[i].s]; + int32_t v2 = mPositionMappedVrt[mTrMeshEdges[i].e]; + mVerticesDistances[v1] = 0.0f; + mVerticesDistances[v2] = 0.0f; + que.push(v1); + que.push(v2); + } + } + while (!que.empty()) + { + int32_t curr = que.front(); + que.pop(); + + for (uint32_t i = 0; i < mGeometryGraph[curr].size(); ++i) + { + int32_t to = mGeometryGraph[curr][i]; + float d = mVerticesDistances[curr] + 0.1f;// (mVertices[to].p - mVertices[curr].p).magnitudeSquared(); + if (d < mVerticesDistances[to]) + { + mVerticesDistances[to] = d; + que.push(to); + } + } + } + + for (uint32_t i = 0; i < mVerticesDistances.size(); ++i) + { + int32_t from = mPositionMappedVrt[i]; + mVerticesDistances[i] = mVerticesDistances[from]; + } +} + +bool edgeOverlapTest(PxVec3& as, PxVec3& ae, PxVec3& bs, PxVec3& be) +{ + //return false; + if (std::max(std::min(as.x, ae.x), std::min(bs.x, be.x)) > std::min(std::max(as.x, ae.x), std::max(bs.x, be.x))) return false; + if (std::max(std::min(as.y, ae.y), std::min(bs.y, be.y)) > std::min(std::max(as.y, ae.y), std::max(bs.y, be.y))) return false; + if (std::max(std::min(as.z, ae.z), std::min(bs.z, be.z)) > std::min(std::max(as.z, ae.z), std::max(bs.z, be.z))) return false; + + return ((bs - as).cross(ae - as)).magnitudeSquared() < 1e-12f && ((be - as).cross(ae - as)).magnitudeSquared() < 1e-12f; +} + +void ChunkPostProcessor::relax(int32_t iteration, float factor, std::vector<Vertex>& vertices) +{ + std::vector<PxVec3> verticesTemp(vertices.size()); + std::vector<PxVec3> normalsTemp(vertices.size()); + for (int32_t iter = 0; iter < iteration; ++iter) + { + for (uint32_t i = 0; i < vertices.size(); ++i) + { + if (mRestrictionFlag[i]) + { + continue; + } + PxVec3 cps = vertices[i].p; + PxVec3 cns = mVerticesNormalsSmoothed[i]; + PxVec3 averaged(0, 0, 0); + PxVec3 averagedNormal(0, 0, 0); + + for (uint32_t p = 0; p < mGeometryGraph[mPositionMappedVrt[i]].size(); ++p) + { + int32_t to = mGeometryGraph[mPositionMappedVrt[i]][p]; + averaged += vertices[to].p; + averagedNormal += mVerticesNormalsSmoothed[to]; + + } + averaged *= (1.0f / mGeometryGraph[mPositionMappedVrt[i]].size()); + averagedNormal *= (1.0f / mGeometryGraph[mPositionMappedVrt[i]].size()); + verticesTemp[i] = cps + (averaged - cps) * factor; + normalsTemp[i] = cns * (1.0f - factor) + averagedNormal * factor; + } + for (uint32_t i = 0; i < vertices.size(); ++i) + { + if (mRestrictionFlag[i]) + { + continue; + } + vertices[i].p = verticesTemp[i]; + mVerticesNormalsSmoothed[i] = normalsTemp[i].getNormalized(); + + } + } + +} + +void ChunkPostProcessor::prebuildEdgeFlagArray() +{ + mRestrictionFlag.clear(); + mRestrictionFlag.resize(mVertices.size()); + mEdgeFlag.clear(); + mEdgeFlag.resize(mTrMeshEdges.size(), NONE); + + std::map<PxVec3, int32_t, VrtPositionComparator> mPosMap; + mPositionMappedVrt.clear(); + mPositionMappedVrt.resize(mVertices.size(), 0); + + for (uint32_t i = 0; i < mVertices.size(); ++i) + { + auto it = mPosMap.find(mVertices[i].p); + + if (it == mPosMap.end()) + { + mPosMap[mVertices[i].p] = i; + mPositionMappedVrt[i] = i; + } + else + { + mPositionMappedVrt[i] = it->second; + } + } + + std::map<Edge, int32_t> mPositionEdgeMap; + std::vector<int32_t> mPositionBasedEdges(mTrMeshEdges.size()); + + + for (uint32_t i = 0; i < mTrMeshEdges.size(); ++i) + { + Edge tmp = Edge(mPositionMappedVrt[mTrMeshEdges[i].s], mPositionMappedVrt[mTrMeshEdges[i].e]); + if (tmp.e < tmp.s) std::swap(tmp.e, tmp.s); + auto it = mPositionEdgeMap.find(tmp); + if (it == mPositionEdgeMap.end()) + { + mPositionEdgeMap[tmp] = i; + mPositionBasedEdges[i] = i; + } + else + { + mPositionBasedEdges[i] = it->second; + } + } + + std::vector<EdgeFlag> shortMarkup(mTrMeshEdges.size(), NONE); + std::vector<int32_t> lastOwner(mTrMeshEdges.size(), 0); + + std::vector<std::vector<int32_t> > edgeOverlap(mTrMeshEdges.size()); + for (auto it1 = mPositionEdgeMap.begin(); it1 != mPositionEdgeMap.end(); ++it1) + { + auto it2 = it1; + it2++; + for (; it2 != mPositionEdgeMap.end(); ++it2) + { + Edge& ed1 = mTrMeshEdges[it1->second]; + Edge& ed2 = mTrMeshEdges[it2->second]; + + if (edgeOverlapTest(mVertices[ed1.s].p, mVertices[ed1.e].p, mVertices[ed2.s].p, mVertices[ed2.e].p)) + { + edgeOverlap[it1->second].push_back(it2->second); + } + } + } + + for (uint32_t i = 0; i < mTesselatedMeshTriangles.size(); ++i) + { + int32_t ui = mTesselatedMeshTriangles[i].userInfo; + int32_t ed = mPositionBasedEdges[findEdge(Edge(mTesselatedMeshTriangles[i].ea, mTesselatedMeshTriangles[i].eb))]; + + + markEdge(ui, ed, shortMarkup, lastOwner); + for (uint32_t ov = 0; ov < edgeOverlap[ed].size(); ++ov) + { + markEdge(ui, edgeOverlap[ed][ov], shortMarkup, lastOwner); + } + + ed = mPositionBasedEdges[findEdge(Edge(mTesselatedMeshTriangles[i].ea, mTesselatedMeshTriangles[i].ec))]; + markEdge(ui, ed, shortMarkup, lastOwner); + for (uint32_t ov = 0; ov < edgeOverlap[ed].size(); ++ov) + { + markEdge(ui, edgeOverlap[ed][ov], shortMarkup, lastOwner); + } + + ed = mPositionBasedEdges[findEdge(Edge(mTesselatedMeshTriangles[i].eb, mTesselatedMeshTriangles[i].ec))]; + markEdge(ui, ed, shortMarkup, lastOwner); + for (uint32_t ov = 0; ov < edgeOverlap[ed].size(); ++ov) + { + markEdge(ui, edgeOverlap[ed][ov], shortMarkup, lastOwner); + } + + } + + for (uint32_t i = 0; i < mTrMeshEdges.size(); ++i) + { + mEdgeFlag[i] = shortMarkup[mPositionBasedEdges[i]]; + } + + for (uint32_t i = 0; i < mTesselatedMeshTriangles.size(); ++i) + { + if (mTesselatedMeshTriangles[i].userInfo != 0) continue; + + int32_t ed = findEdge(Edge(mTesselatedMeshTriangles[i].ea, mTesselatedMeshTriangles[i].eb)); + mEdgeFlag[ed] = EXTERNAL_EDGE; + ed = findEdge(Edge(mTesselatedMeshTriangles[i].ec, mTesselatedMeshTriangles[i].eb)); + mEdgeFlag[ed] = EXTERNAL_EDGE; + ed = findEdge(Edge(mTesselatedMeshTriangles[i].ea, mTesselatedMeshTriangles[i].ec)); + mEdgeFlag[ed] = EXTERNAL_EDGE; + } +} + + + +void ChunkPostProcessor::tesselateInternalSurface(float maxLenIn) +{ + mTesselatedMeshTriangles = mBaseMeshTriangles; + if (mTesselatedMeshTriangles.empty()) + { + return; + } + + updateEdgeTriangleInfo(); + prebuildEdgeFlagArray(); + mRestrictionFlag.resize(mVertices.size(), 0); + for (uint32_t i = 0; i < mTrMeshEdges.size(); ++i) + { + if (mEdgeFlag[i] == EXTERNAL_EDGE || mEdgeFlag[i] == EXTERNAL_BORDER_EDGE || mEdgeFlag[i] == INTERNAL_BORDER_EDGE) + { + mRestrictionFlag[mTrMeshEdges[i].s] = 1; + mRestrictionFlag[mTrMeshEdges[i].e] = 1; + } + } + + + float maxLen = std::max(0.1f, maxLenIn); + while (maxLen > maxLenIn) + { + float mlSq = maxLen * maxLen; + float minD = maxLen * 0.5f; + minD = minD * minD; + + for (int32_t iter = 0; iter < 15; ++iter) + { + updateVertEdgeInfo(); + uint32_t oldSize = (uint32_t)mTrMeshEdges.size(); + for (uint32_t i = 0; i < oldSize; ++i) + { + if (mEdgeFlag[i] == EXTERNAL_EDGE || mEdgeFlag[i] == INTERNAL_BORDER_EDGE) + { + continue; + } + if ((mVertices[mTrMeshEdges[i].s].p - mVertices[mTrMeshEdges[i].e].p).magnitudeSquared() < minD) + { + collapseEdge(i); + } + } + + oldSize = (uint32_t)mTrMeshEdges.size(); + updateEdgeTriangleInfo(); + for (uint32_t i = 0; i < oldSize; ++i) + { + if (mEdgeFlag[i] == EXTERNAL_EDGE) + { + continue; + } + if ((mVertices[mTrMeshEdges[i].s].p - mVertices[mTrMeshEdges[i].e].p).magnitudeSquared() > mlSq) + { + divideEdge(i); + } + } + } + maxLen *= 0.3; + maxLen = std::max(maxLen, maxLenIn); + } + computeFalloffAndNormals(); + prebuildTesselatedTriangles(); + isTesselated = true; +} + +} // namespace Blast +} // namespace Nv
\ No newline at end of file diff --git a/NvBlast/sdk/extensions/authoring/source/NvBlastExtAuthoringTriangulator.h b/NvBlast/sdk/extensions/authoring/source/NvBlastExtAuthoringTriangulator.h new file mode 100644 index 0000000..83942f4 --- /dev/null +++ b/NvBlast/sdk/extensions/authoring/source/NvBlastExtAuthoringTriangulator.h @@ -0,0 +1,261 @@ +/* +* Copyright (c) 2016-2017, 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. +*/ + +#ifndef NVBLASTEXTAUTHORINGTRIANGULATOR_H +#define NVBLASTEXTAUTHORINGTRIANGULATOR_H + + +#include <vector> +#include <map> +#include "NvBlastExtAuthoringTypes.h" +#include "NvBlastExtAuthoringMesh.h" +#include "NvBlastExtAuthoringInternalCommon.h" + +namespace Nv +{ +namespace Blast +{ +class SimplexNoise; + +/** + Vertex comparator for vertex welding. +*/ +struct VrtComp +{ + bool operator()(const Vertex& a, const Vertex& b) const; +}; + +/** + Vertex comparator for vertex welding (not accounts normal and uv parameters of vertice). +*/ +struct VrtPositionComparator +{ + bool operator()(const physx::PxVec3& a, const physx::PxVec3& b) const; +}; + +/** + Structure used on tesselation stage. Maps edge to two neighboor triangles +*/ +struct EdgeToTriangles +{ + int32_t tr[2]; + int32_t c; + EdgeToTriangles() + { + c = 0; + } + /** + Add triangle to edge. Should not be called more than twice for one edge!!!!. + */ + void add(int32_t t) + { + tr[c] = t; + ++c; + } + /** + Replaces mapping from one triangle to another. + */ + void replace(int32_t from, int32_t to) + { + if (tr[0] == from) + { + tr[0] = to; + } + else + { + if (c == 2 && tr[1] == from) + { + tr[1] = to; + } + } + } + /** + Get triangle which is mapped by this edge and which index is different than provided. + */ + int32_t getNot(int32_t id) + { + if (tr[0] != id) + { + return tr[0]; + } + if (c == 2 && tr[1] != id) + { + return tr[1]; + } + return -1; + } + +}; + + +/** + Tool for doing all post processing steps of authoring. +*/ +class ChunkPostProcessor +{ +public: + /** + Edge flags + */ + enum EdgeFlag{ INTERNAL_EDGE, EXTERNAL_BORDER_EDGE, INTERNAL_BORDER_EDGE, EXTERNAL_EDGE, NONE }; + + /** + Triangulates provided mesh and saves result internally. Uses Ear-clipping algorithm. + \param[in] mesh Mesh for triangulation + */ + void triangulate(Mesh* mesh); + + /** + \return Return array of triangles of base mesh. + */ + std::vector<Triangle>& getBaseMesh() + { + return mBaseMeshResultTriangles; + } + + /** + \return Return array of TriangleIndexed of base mesh. Each TriangleIndexed contains index of corresponding vertex in internal vertex buffer. + */ + std::vector<TriangleIndexed>& getBaseMeshIndexed() + { + return mBaseMeshTriangles; + } + + + /** + \return Return mapping from vertices of input Mesh to internal vertices buffer. Used for island detection. + */ + std::vector<uint32_t>& getBaseMapping() + { + return mBaseMapping; + }; + /** + \return Return mapping from vertices of input Mesh to internal vertices buffer, only positions are accounted. Used for island detection. + */ + std::vector<int32_t>& getPositionedMapping() + { + return mPositionMappedVrt; + }; + + /** + \return Return internal vertex buffer size. Vertices internally are welded with some threshold. + */ + uint32_t getWeldedVerticesCount() + { + return static_cast<uint32_t>(mVertices.size()); + } + /** + Tesselate internal surface. + \param[in] maxLen - maximal length of edge on internal surface. + */ + void tesselateInternalSurface(float maxLen); + + /** + Apply noise to internal surface. Must be called only after tesselation!!! + \param[in] noise - noise generator + \param[in] falloff - damping of noise around of external surface + \param[in] relaxIterations - number of smoothing iterations before applying noise + \param[in] relaxFactor - amount of smooting before applying noise. + */ + void applyNoise(SimplexNoise& noise, float falloff, int32_t relaxIterations, float relaxFactor); + + /** + \return Return array of noised mesh triangles. + */ + std::vector<Triangle>& getNoisyMesh() + { + return mTesselatedMeshResultTriangles; + }; + + /** + Removes all information about mesh triangulation, tesselation, etc. + */ + void reset(); + +private: + + + + void collapseEdge(int32_t id); + void divideEdge(int32_t id); + void updateVertEdgeInfo(); + void updateEdgeTriangleInfo(); + + int32_t addVerticeIfNotExist(Vertex& p); + void addEdgeIfValid(EdgeWithParent& ed); + + /* Data used before triangulation to build polygon loops*/ + + std::vector<Vertex> mVertices; + std::vector<EdgeWithParent> mBaseMeshEdges; + std::map<Vertex, int32_t, VrtComp> mVertMap; + std::map<EdgeWithParent, int32_t, EdgeComparator> mEdgeMap; + std::vector<uint32_t> mBaseMapping; + + + + /** + Unite all almost similar vertices, update edges according to this changes + */ + void prepare(Mesh* mesh); + + /* ------------------------------------------------------------ */ + + /* Triangulation and tesselation stage data */ + bool isTesselated; + + std::map<Edge, int32_t> mTrMeshEdgeMap; + std::vector<Edge> mTrMeshEdges; + std::vector<EdgeToTriangles> mTrMeshEdToTr; + std::vector<int32_t> mVertexValence; + std::vector<std::vector<int32_t> > mVertexToTriangleMap; + + std::vector<bool> mRestrictionFlag; + std::vector<EdgeFlag> mEdgeFlag; + + std::vector<float> mVerticesDistances; + std::vector<physx::PxVec3> mVerticesNormalsSmoothed; + std::vector<int32_t> mPositionMappedVrt; + std::vector<std::vector<int32_t> > mGeometryGraph; + + + int32_t addEdgeTr(const Edge& ed); + int32_t findEdge(const Edge& e); + + void prebuildEdgeFlagArray(); + void computePositionedMapping(); + + void computeFalloffAndNormals(); + + + + void triangulatePolygonWithEarClipping(std::vector<uint32_t>& inputPolygon, Vertex* vert, ProjectionDirections dir); + void buildPolygonAndTriangulate(std::vector<Edge>& edges, Vertex* vertices, int32_t userData); + + void relax(int32_t iterations, float factor, std::vector<Vertex>& vertices); + void recalcNoiseDirs(); + + std::vector<TriangleIndexed> mBaseMeshTriangles; + std::vector<TriangleIndexed> mTesselatedMeshTriangles; + + /** + Final triangles + */ + void prebuildTesselatedTriangles(); + + std::vector<Triangle> mBaseMeshResultTriangles; + std::vector<Triangle> mTesselatedMeshResultTriangles; +}; + +} // namespace Blast +} // namespace Nv + + +#endif // ifndef NVBLASTEXTAUTHORINGTRIANGULATOR_H diff --git a/NvBlast/sdk/extensions/authoring/source/NvBlastExtAuthoringVSA.h b/NvBlast/sdk/extensions/authoring/source/NvBlastExtAuthoringVSA.h new file mode 100644 index 0000000..fd0c9c9 --- /dev/null +++ b/NvBlast/sdk/extensions/authoring/source/NvBlastExtAuthoringVSA.h @@ -0,0 +1,312 @@ +/* +* Copyright (c) 2016-2017, 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. +*/ + +#ifndef NVBLASTEXTAUTHORINGVSA_H +#define NVBLASTEXTAUTHORINGVSA_H + +namespace Nv +{ +namespace Blast +{ + +/* + This code copied from APEX GSA +*/ + +namespace VSA +{ +typedef float real; + +struct VS3D_Halfspace_Set +{ + virtual real farthest_halfspace(real plane[4], const real point[4]) = 0; +}; + + +// Simple types and operations for internal calculations +struct Vec3 { real x, y, z; }; // 3-vector +inline Vec3 vec3(real x, real y, real z) { Vec3 r; r.x = x; r.y = y; r.z = z; return r; } // vector builder +inline Vec3 operator + (const Vec3& a, const Vec3& b) { return vec3(a.x + b.x, a.y + b.y, a.z + b.z); } // vector addition +inline Vec3 operator * (real s, const Vec3& v) { return vec3(s*v.x, s*v.y, s*v.z); } // scalar multiplication +inline real operator | (const Vec3& a, const Vec3& b) { return a.x*b.x + a.y*b.y + a.z*b.z; } // dot product +inline Vec3 operator ^ (const Vec3& a, const Vec3& b) { return vec3(a.y*b.z - b.y*a.z, a.z*b.x - b.z*a.x, a.x*b.y - b.x*a.y); } // cross product + +struct Vec4 { Vec3 v; real w; }; // 4-vector split into 3-vector and scalar parts +inline Vec4 vec4(const Vec3& v, real w) { Vec4 r; r.v = v; r.w = w; return r; } // vector builder +inline real operator | (const Vec4& a, const Vec4& b) { return (a.v | b.v) + a.w*b.w; } // dot product + +// More accurate perpendicular +inline Vec3 perp(const Vec3& a, const Vec3& b) +{ + Vec3 c = a^b; // Cross-product gives perpendicular +#if VS3D_HIGH_ACCURACY || REAL_DOUBLE + const real c2 = c | c; + if (c2 != 0) c = c + (1 / c2)*((a | c)*(c^b) + (b | c)*(a^c)); // Improvement to (a b)^T(c) = (0) +#endif + return c; +} + +// Square +inline real sq(real x) { return x*x; } + +// Returns index of the extremal element in a three-element set {e0, e1, e2} based upon comparisons c_ij. The extremal index m is such that c_mn is true, or e_m == e_n, for all n. +inline int ext_index(int c_10, int c_21, int c_20) { return c_10 << c_21 | (c_21&c_20) << 1; } + +// Returns index (0, 1, or 2) of minimum argument +inline int index_of_min(real x0, real x1, real x2) { return ext_index((int)(x1 < x0), (int)(x2 < x1), (int)(x2 < x0)); } + +// Compare fractions with positive deominators. Returns a_num*sqrt(a_rden2) > b_num*sqrt(b_rden2) +inline bool frac_gt(real a_num, real a_rden2, real b_num, real b_rden2) +{ + const bool a_num_neg = a_num < 0; + const bool b_num_neg = b_num < 0; + return a_num_neg != b_num_neg ? b_num_neg : ((a_num*a_num*a_rden2 > b_num*b_num*b_rden2) != a_num_neg); +} + +// Returns index (0, 1, or 2) of maximum fraction with positive deominators +inline int index_of_max_frac(real x0_num, real x0_rden2, real x1_num, real x1_rden2, real x2_num, real x2_rden2) +{ + return ext_index((int)frac_gt(x1_num, x1_rden2, x0_num, x0_rden2), (int)frac_gt(x2_num, x2_rden2, x1_num, x1_rden2), (int)frac_gt(x2_num, x2_rden2, x0_num, x0_rden2)); +} + +// Compare values given their signs and squares. Returns a > b. a2 and b2 may have any constant offset applied to them. +inline bool sgn_sq_gt(real sgn_a, real a2, real sgn_b, real b2) { return sgn_a*sgn_b < 0 ? (sgn_b < 0) : ((a2 > b2) != (sgn_a < 0)); } + +// Returns index (0, 1, or 2) of maximum value given their signs and squares. sq_x0, sq_x1, and sq_x2 may have any constant offset applied to them. +inline int index_of_max_sgn_sq(real sgn_x0, real sq_x0, real sgn_x1, real sq_x1, real sgn_x2, real sq_x2) +{ + return ext_index((int)sgn_sq_gt(sgn_x1, sq_x1, sgn_x0, sq_x0), (int)sgn_sq_gt(sgn_x2, sq_x2, sgn_x1, sq_x1), (int)sgn_sq_gt(sgn_x2, sq_x2, sgn_x0, sq_x0)); +} + +// Project 2D (homogeneous) vector onto 2D half-space boundary +inline void project2D(Vec3& r, const Vec3& plane, real delta, real recip_n2, real eps2) +{ + r = r + (-delta*recip_n2)*vec3(plane.x, plane.y, 0); + r = r + (-(r | plane)*recip_n2)*vec3(plane.x, plane.y, 0); // Second projection for increased accuracy + if ((r | r) > eps2) return; + r = (-plane.z*recip_n2)*vec3(plane.x, plane.y, 0); + r.z = 1; +} + + +// Update function for vs3d_test +static bool vs3d_update(Vec4& p, Vec4 S[4], int& plane_count, const Vec4& q, real eps2) +{ + // h plane is the last plane + const Vec4& h = S[plane_count - 1]; + + // Handle plane_count == 1 specially (optimization; this could be commented out) + if (plane_count == 1) + { + // Solution is objective projected onto h plane + p = q; + p.v = p.v + -(p | h)*h.v; + if ((p | p) <= eps2) p = vec4(-h.w*h.v, 1); // If p == 0 then q is a direction vector, any point in h is a support point + return true; + } + + // Create basis in the h plane + const int min_i = index_of_min(h.v.x*h.v.x, h.v.y*h.v.y, h.v.z*h.v.z); + const Vec3 y = h.v^vec3((real)(min_i == 0), (real)(min_i == 1), (real)(min_i == 2)); + const Vec3 x = y^h.v; + + // Use reduced vector r instead of p + Vec3 r = { x | q.v, y | q.v, q.w*(y | y) }; // (x|x) = (y|y) = square of plane basis scale + + // If r == 0 (within epsilon), then it is a direction vector, and we have a bounded solution + if ((r | r) <= eps2) r.z = 1; + + // Create plane equations in the h plane. These will not be normalized in general. + int N = 0; // Plane count in h subspace + Vec3 R[3]; // Planes in h subspace + real recip_n2[3]; // Plane normal vector reciprocal lengths squared + real delta[3]; // Signed distance of objective to the planes + int index[3]; // Keep track of original plane indices + for (int i = 0; i < plane_count - 1; ++i) + { + const Vec3& vi = S[i].v; + const real cos_theta = h.v | vi; + R[N] = vec3(x | vi, y | vi, S[i].w - h.w*cos_theta); + index[N] = i; + const real n2 = R[N].x*R[N].x + R[N].y*R[N].y; + if (n2 >= eps2) + { + const real lin_norm = (real)1.5 - (real)0.5*n2; // 1st-order approximation to 1/sqrt(n2) expanded about n2 = 1 + R[N] = lin_norm*R[N]; // We don't need normalized plane equations, but rescaling (even with an approximate normalization) gives better numerical behavior + recip_n2[N] = 1 / (R[N].x*R[N].x + R[N].y*R[N].y); + delta[N] = r | R[N]; + ++N; // Keep this plane + } + else if (cos_theta < 0) return false; // Parallel cases are redundant and rejected, anti-parallel cases are 1D voids + } + + // Now work with the N-sized R array of half-spaces in the h plane + switch (N) + { + case 1: one_plane : + if (delta[0] < 0) N = 0; // S[0] is redundant, eliminate it + else project2D(r, R[0], delta[0], recip_n2[0], eps2); + break; + case 2: two_planes : + if (delta[0] < 0 && delta[1] < 0) N = 0; // S[0] and S[1] are redundant, eliminate them + else + { + const int max_d_index = (int)frac_gt(delta[1], recip_n2[1], delta[0], recip_n2[0]); + project2D(r, R[max_d_index], delta[max_d_index], recip_n2[max_d_index], eps2); + const int min_d_index = max_d_index ^ 1; + const real new_delta_min = r | R[min_d_index]; + if (new_delta_min < 0) + { + index[0] = index[max_d_index]; + N = 1; // S[min_d_index] is redundant, eliminate it + } + else + { + // Set r to the intersection of R[0] and R[1] and keep both + r = perp(R[0], R[1]); + if (r.z*r.z*recip_n2[0] * recip_n2[1] < eps2) + { + if (R[0].x*R[1].x + R[0].y*R[1].y < 0) return false; // 2D void found + goto one_plane; + } + r = (1 / r.z)*r; // We could just as well multiply r by sgn(r.z); we just need to ensure r.z > 0 + } + } + break; + case 3: + if (delta[0] < 0 && delta[1] < 0 && delta[2] < 0) N = 0; // S[0], S[1], and S[2] are redundant, eliminate them + else + { + const Vec3 row_x = { R[0].x, R[1].x, R[2].x }; + const Vec3 row_y = { R[0].y, R[1].y, R[2].y }; + const Vec3 row_w = { R[0].z, R[1].z, R[2].z }; + const Vec3 cof_w = perp(row_x, row_y); + const bool detR_pos = (row_w | cof_w) > 0; + const int nrw_sgn0 = cof_w.x*cof_w.x*recip_n2[1] * recip_n2[2] < eps2 ? 0 : (((int)((cof_w.x > 0) == detR_pos) << 1) - 1); + const int nrw_sgn1 = cof_w.y*cof_w.y*recip_n2[2] * recip_n2[0] < eps2 ? 0 : (((int)((cof_w.y > 0) == detR_pos) << 1) - 1); + const int nrw_sgn2 = cof_w.z*cof_w.z*recip_n2[0] * recip_n2[1] < eps2 ? 0 : (((int)((cof_w.z > 0) == detR_pos) << 1) - 1); + + if ((nrw_sgn0 | nrw_sgn1 | nrw_sgn2) >= 0) return false; // 3D void found + + const int positive_width_count = ((nrw_sgn0 >> 1) & 1) + ((nrw_sgn1 >> 1) & 1) + ((nrw_sgn2 >> 1) & 1); + if (positive_width_count == 1) + { + // A single positive width results from a redundant plane. Eliminate it and peform N = 2 calculation. + const int pos_width_index = ((nrw_sgn1 >> 1) & 1) | (nrw_sgn2 & 2); // Calculates which index corresponds to the positive-width side + R[pos_width_index] = R[2]; + recip_n2[pos_width_index] = recip_n2[2]; + delta[pos_width_index] = delta[2]; + index[pos_width_index] = index[2]; + N = 2; + goto two_planes; + } + + // Find the max dot product of r and R[i]/|R_normal[i]|. For numerical accuracy when the angle between r and the i^{th} plane normal is small, we take some care below: + const int max_d_index = r.z != 0 + ? index_of_max_frac(delta[0], recip_n2[0], delta[1], recip_n2[1], delta[2], recip_n2[2]) // displacement term resolves small-angle ambiguity, just use dot product + : index_of_max_sgn_sq(delta[0], -sq(r.x*R[0].y - r.y*R[0].x)*recip_n2[0], delta[1], -sq(r.x*R[1].y - r.y*R[1].x)*recip_n2[1], delta[2], -sq(r.x*R[2].y - r.y*R[2].x)*recip_n2[2]); // No displacement term. Use wedge product to find the sine of the angle. + + // Project r onto max-d plane + project2D(r, R[max_d_index], delta[max_d_index], recip_n2[max_d_index], eps2); + N = 1; // Unless we use a vertex in the loop below + const int index_max = index[max_d_index]; + + // The number of finite widths should be >= 2. If not, it should be 0, but in any case it implies three parallel lines in the plane, which we should not have here. + // If we do have three parallel lines (# of finite widths < 2), we've picked the line corresponding to the half-plane farthest from r, which is correct. + const int finite_width_count = (nrw_sgn0 & 1) + (nrw_sgn1 & 1) + (nrw_sgn2 & 1); + if (finite_width_count >= 2) + { + const int i_remaining[2] = { (1 << max_d_index) & 3, (3 >> max_d_index) ^ 1 }; // = {(max_d_index+1)%3, (max_d_index+2)%3} + const int i_select = (int)frac_gt(delta[i_remaining[1]], recip_n2[i_remaining[1]], delta[i_remaining[0]], recip_n2[i_remaining[0]]); // Select the greater of the remaining dot products + for (int i = 0; i < 2; ++i) + { + const int j = i_remaining[i_select^i]; // i = 0 => the next-greatest, i = 1 => the least + if ((r | R[j]) >= 0) + { + r = perp(R[max_d_index], R[j]); + r = (1 / r.z)*r; // We could just as well multiply r by sgn(r.z); we just need to ensure r.z > 0 + index[1] = index[j]; + N = 2; + break; + } + } + } + + index[0] = index_max; + } + break; + } + + // Transform r back to 3D space + p = vec4(r.x*x + r.y*y + (-r.z*h.w)*h.v, r.z); + + // Pack S array with kept planes + if (N < 2 || index[1] != 0) { for (int i = 0; i < N; ++i) S[i] = S[index[i]]; } // Safe to copy columns in order + else { const Vec4 temp = S[0]; S[0] = S[index[0]]; S[1] = temp; } // Otherwise use temp storage to avoid overwrite + S[N] = h; + plane_count = N + 1; + + return true; +} + + +// Performs the VS algorithm for D = 3 +inline int vs3d_test(VS3D_Halfspace_Set& halfspace_set, real* q = nullptr) +{ + // Objective = q if it is not NULL, otherwise it is the origin represented in homogeneous coordinates + const Vec4 objective = q ? (q[3] != 0 ? vec4((1 / q[3])*vec3(q[0], q[1], q[2]), 1) : *(Vec4*)q) : vec4(vec3(0, 0, 0), 1); + + // Tolerance for 3D void simplex algorithm + const real eps_f = (real)1 / (sizeof(real) == 4 ? (1L << 23) : (1LL << 52)); // Floating-point epsilon +#if VS3D_HIGH_ACCURACY || REAL_DOUBLE + const real eps = 8 * eps_f; +#else + const real eps = 80 * eps_f; +#endif + const real eps2 = eps*eps; // Using epsilon squared + + // Maximum allowed iterations of main loop. If exceeded, error code is returned + const int max_iteration_count = 50; + + // State + Vec4 S[4]; // Up to 4 planes + int plane_count = 0; // Number of valid planes + Vec4 p = objective; // Test point, initialized to objective + + // Default result, changed to valid result if found in loop below + int result = -1; + + // Iterate until a stopping condition is met or the maximum number of iterations is reached + for (int i = 0; result < 0 && i < max_iteration_count; ++i) + { + Vec4& plane = S[plane_count++]; + real delta = halfspace_set.farthest_halfspace(&plane.v.x, &p.v.x); +#if VS3D_UNNORMALIZED_PLANE_HANDLING != 0 + const real recip_norm = vs3d_recip_sqrt(plane.v | plane.v); + plane = vec4(recip_norm*plane.v, recip_norm*plane.w); + delta *= recip_norm; +#endif + if (delta <= 0 || delta*delta <= eps2*(p | p)) result = 1; // Intersection found + else if (!vs3d_update(p, S, plane_count, objective, eps2)) result = 0; // Void simplex found + } + + // If q is given, fill it with the solution (normalize p.w if it is not zero) + if (q) *(Vec4*)q = (p.w != 0) ? vec4((1 / p.w)*p.v, 1) : p; + + return result; +} + +} // namespace VSA + +} // namespace Blast +} // namespace Nv + + +#endif // ifndef NVBLASTEXTAUTHORINGVSA_H diff --git a/NvBlast/sdk/extensions/authoring/source/NvBlastExtTriangleProcessor.cpp b/NvBlast/sdk/extensions/authoring/source/NvBlastExtTriangleProcessor.cpp new file mode 100644 index 0000000..3c3e540 --- /dev/null +++ b/NvBlast/sdk/extensions/authoring/source/NvBlastExtTriangleProcessor.cpp @@ -0,0 +1,355 @@ +/* +* Copyright (c) 2016-2017, 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 "NvBlastExtTriangleProcessor.h" +#include "NvBlastExtAuthoringInternalCommon.h" +#define COLLIN_EPS 1e-4f +#define V_COMP_EPS 1e-5f + +using namespace physx; + +namespace Nv +{ +namespace Blast +{ +/** + Segments bounding box interseciton test +*/ +bool boundingRectangleIntersection(const PxVec2& s1, const PxVec2& e1, const PxVec2& s2, const PxVec2& e2) +{ + // sl1/sl2 is always left bottom end of rectangle + // se1/el2 is always right top end of rectangle + + PxF32 sl1, sl2, el1, el2; + if (s1.x < e1.x) + { + sl1 = s1.x; + el1 = e1.x; + } + else + { + el1 = s1.x; + sl1 = e1.x; + } + + if (s2.x < e2.x) + { + sl2 = s2.x; + el2 = e2.x; + } + else + { + el2 = s2.x; + sl2 = e2.x; + } + if (PxMax(sl1, sl2) > PxMin(el1, el2)) + return false; + + if (s1.y < e1.y) + { + sl1 = s1.y; + el1 = e1.y; + } + else + { + el1 = s1.y; + sl1 = e1.y; + } + + if (s2.y < e2.y) + { + sl2 = s2.y; + el2 = e2.y; + } + else + { + el2 = s2.y; + sl2 = e2.y; + } + if (PxMax(sl1, sl2) > PxMin(el1, el2)) + return false; + + return true; +} + +inline PxF32 getRotation(PxVec2 a, PxVec2 b) +{ + return a.x * b.y - a.y * b.x; +} + +inline PxF32 getParameter(const PxVec2& a, const PxVec2& b, const PxVec2& point) +{ + return (point - a).magnitude() / (b - a).magnitude(); +} +inline PxVec3 lerp3D(const PxVec3& a, const PxVec3& b, const PxF32 t) +{ + return (b - a) * t + a; +} + + + +struct Line2D +{ + PxVec2 normal; + PxF32 c; + Line2D(PxVec2 vec, PxVec2 point) + { + normal.x = vec.y; + normal.y = -vec.x; + c = -normal.dot(point); + } +}; + + +uint32_t TriangleProcessor::getSegmentIntersection(const PxVec2& s1, const PxVec2& e1, const PxVec2& s2, const PxVec2& e2, PxF32& t1) +{ + if (!boundingRectangleIntersection(s1, e1, s2, e2)) + return 0; + + PxVec2 vec1 = e1 - s1; + PxVec2 vec2 = e2 - s2; + PxF32 det1 = getRotation(vec1, vec2); + if (PxAbs(det1) < COLLIN_EPS) + { + return 0; + } + Line2D lineA(vec1, s1); + Line2D lineB(vec2, s2); + PxVec2 fInt; + + PxF32 detX = lineA.normal.y * lineB.c - lineA.c * lineB.normal.y; + PxF32 detY = lineA.c * lineB.normal.x - lineB.c * lineA.normal.x; + PxF32 x = detX / det1; + PxF32 y = detY / det1; + + if (x + V_COMP_EPS >= PxMax(PxMin(s1.x, e1.x), PxMin(s2.x, e2.x)) && + x - V_COMP_EPS <= PxMin(PxMax(s1.x, e1.x), PxMax(s2.x, e2.x)) && + y + V_COMP_EPS >= PxMax(PxMin(s1.y, e1.y), PxMin(s2.y, e2.y)) && + y - V_COMP_EPS <= PxMin(PxMax(s1.y, e1.y), PxMax(s2.y, e2.y))) + { + fInt.x = x; + fInt.y = y; + t1 = getParameter(s1, e1, fInt); + return 1; + } + + return 0; +} + +struct cwComparer +{ + PxVec3 basePoint; + PxVec3 normal; + cwComparer(PxVec3 basePointIn, PxVec3 norm) + { + basePoint = basePointIn; + normal = norm; + }; + bool operator()(const PxVec3& a, const PxVec3& b) + { + PxVec3 norm = (a - basePoint).cross(b - basePoint); + return normal.dot(norm) > 0; + } +}; + +bool vec3Comparer(const PxVec3& a, const PxVec3& b) +{ + if (a.x + V_COMP_EPS < b.x) return true; + if (a.x - V_COMP_EPS > b.x) return false; + if (a.y + V_COMP_EPS < b.y) return true; + if (a.y - V_COMP_EPS > b.y) return false; + if (a.z + V_COMP_EPS < b.z) return true; + return false; +} + +void TriangleProcessor::sortToCCW(std::vector<PxVec3>& points, PxVec3& normal) +{ + std::sort(points.begin(), points.end(), vec3Comparer); + int lastUnique = 0; + for (uint32_t i = 1; i < points.size(); ++i) + { + PxVec3 df = points[i] - points[lastUnique]; + if (df.x > V_COMP_EPS || df.y > V_COMP_EPS || df.z > V_COMP_EPS) + { + points[++lastUnique] = points[i]; + } + } + points.resize(lastUnique + 1); + if (points.size() > 2) + { + cwComparer compr(points[0], normal); + std::sort(points.begin() + 1, points.end(), compr); + } +} + + + +void TriangleProcessor::buildConvexHull(std::vector<PxVec3>& points, std::vector<PxVec3>& convexHull,const PxVec3& normal) +{ + + std::sort(points.begin(), points.end(), vec3Comparer); + int lastUnique = 0; + for (uint32_t i = 1; i < points.size(); ++i) + { + PxVec3 df = points[i] - points[lastUnique]; + if (df.x > V_COMP_EPS || df.y > V_COMP_EPS || df.z > V_COMP_EPS) + { + points[++lastUnique] = points[i]; + } + } + points.resize(lastUnique + 1); + if (points.size() > 2) + { + cwComparer compr(points[0], normal); + std::sort(points.begin() + 1, points.end(), compr); + } + if (points.size() < 3) + return; + convexHull.push_back(points[0]); + convexHull.push_back(points[1]); + ProjectionDirections projectionDirection = getProjectionDirection(normal); + for (uint32_t i = 2; i < points.size(); ++i) + { + PxVec2 pnt = getProjectedPointWithWinding(points[i], projectionDirection); + PxVec2 vec = pnt - getProjectedPointWithWinding(convexHull.back(), projectionDirection); + if (vec.x < V_COMP_EPS && vec.y < V_COMP_EPS) + { + continue; + } + if (getRotation(vec, getProjectedPointWithWinding(convexHull.back(), projectionDirection) - getProjectedPointWithWinding(convexHull[convexHull.size() - 2], projectionDirection)) < 0) + { + convexHull.push_back(points[i]); + } + else + { + while (convexHull.size() > 1 && getRotation(vec, getProjectedPointWithWinding(convexHull.back(), projectionDirection) - getProjectedPointWithWinding(convexHull[convexHull.size() - 2], projectionDirection)) > 0) + { + convexHull.pop_back(); + vec = pnt - getProjectedPointWithWinding(convexHull.back(), projectionDirection); + } + convexHull.push_back(points[i]); + } + } +} + + +uint32_t TriangleProcessor::getTriangleIntersection(TrPrcTriangle& a, TrPrcTriangle2d& aProjected, TrPrcTriangle &b, PxVec3& centroid, std::vector<PxVec3>& intersectionBuffer, PxVec3 normal) +{ + + b.points[0] -= centroid; + b.points[1] -= centroid; + b.points[2] -= centroid; + + ProjectionDirections prjDir = getProjectionDirection(normal); + + TrPrcTriangle2d bProjected; + bProjected.points[0] = getProjectedPointWithWinding(b.points[0], prjDir); + bProjected.points[1] = getProjectedPointWithWinding(b.points[1], prjDir); + bProjected.points[2] = getProjectedPointWithWinding(b.points[2], prjDir); + + + if (!triangleBoundingBoxIntersection(aProjected, bProjected)) return 0; + + //* Check triangle A against points of B *// + for (int i = 0; i < 3; ++i) + { + if (isPointInside(bProjected.points[i], aProjected)) + { + intersectionBuffer.push_back(b.points[i]); + } + } + //* Check triangle B against points of A *// + for (int i = 0; i < 3; ++i) + { + if (isPointInside(aProjected.points[i], bProjected)) + { + intersectionBuffer.push_back(a.points[i]); + } + } + + //* Check edges intersection *// + float param = 0; + for (int i = 0; i < 3; ++i) + { + for (int j = 0; j < 3; ++j) + { + if (getSegmentIntersection(aProjected.points[i], aProjected.points[(i + 1) % 3], bProjected.points[j], bProjected.points[(j + 1) % 3], param)) + { + intersectionBuffer.push_back(lerp3D(a.points[i], a.points[(i + 1) % 3], param)); + } + } + } + + if (intersectionBuffer.size() == 0) + return 0; + + // Intersection between two triangles is convex, but points should be reordered to construct right polygon // + std::vector<PxVec3> intrs; + buildConvexHull(intersectionBuffer, intrs, normal); + intersectionBuffer = intrs; + + // Return all points back from origin // + for (uint32_t i = 0; i < intersectionBuffer.size(); ++i) + { + intersectionBuffer[i] += centroid; + } + return 1; +} + + + +bool TriangleProcessor::triangleBoundingBoxIntersection(TrPrcTriangle2d& a, TrPrcTriangle2d& b) +{ + float fb = std::min(a.points[0].x, std::min(a.points[1].x, a.points[2].x)); + float fe = std::max(a.points[0].x, std::max(a.points[1].x, a.points[2].x)); + + float sb = std::min(b.points[0].x, std::min(b.points[1].x, b.points[2].x)); + float se = std::max(b.points[0].x, std::max(b.points[1].x, b.points[2].x)); + + if (std::min(fe, se) + V_COMP_EPS < std::max(fb, sb)) return 0; + + fb = std::min(a.points[0].y, std::min(a.points[1].y, a.points[2].y)); + fe = std::max(a.points[0].y, std::max(a.points[1].y, a.points[2].y)); + + sb = std::min(b.points[0].y, std::min(b.points[1].y, b.points[2].y)); + se = std::max(b.points[0].y, std::max(b.points[1].y, b.points[2].y)); + if (std::min(fe, se) + V_COMP_EPS < std::max(fb, sb)) return 0; + return 1; +} + + +uint32_t TriangleProcessor::isPointInside(const PxVec2& point, const TrPrcTriangle2d& triangle) +{ + PxF32 av = getRotation(point - triangle.points[0], triangle.points[1] - triangle.points[0]); + PxF32 bv = getRotation(point - triangle.points[1], triangle.points[2] - triangle.points[1]); + PxF32 cv = getRotation(point - triangle.points[2], triangle.points[0] - triangle.points[2]); + + + if (PxAbs(av) < COLLIN_EPS) av = 0; + if (PxAbs(bv) < COLLIN_EPS) bv = 0; + if (PxAbs(cv) < COLLIN_EPS) cv = 0; + + if (av >= 0 && bv >= 0 && cv >= 0) + { + if (av == 0 || bv == 0 || cv == 0) + return 2; + return 1; + } + if (av <= 0 && bv <= 0 && cv <= 0) + { + if (av == 0 || bv == 0 || cv == 0) + return 2; + return 1; + } + return 0; +} + +} // namespace Blast +} // namespace Nv diff --git a/NvBlast/sdk/extensions/authoring/source/NvBlastExtTriangleProcessor.h b/NvBlast/sdk/extensions/authoring/source/NvBlastExtTriangleProcessor.h new file mode 100644 index 0000000..db9f682 --- /dev/null +++ b/NvBlast/sdk/extensions/authoring/source/NvBlastExtTriangleProcessor.h @@ -0,0 +1,158 @@ +/* +* Copyright (c) 2016-2017, 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. +*/ + +#ifndef NVBLASTEXTTRIANGLEPROCESSOR_H +#define NVBLASTEXTTRIANGLEPROCESSOR_H + +#include <PxPhysicsAPI.h> +#include <vector> +#include <algorithm> + +using namespace physx; + + +namespace Nv +{ +namespace Blast +{ + +/** + Triangle processor internal triangle representation. Contains only vertex positions. +*/ +struct TrPrcTriangle +{ + PxVec3 points[3]; + TrPrcTriangle(PxVec3 a = PxVec3(0.0f), PxVec3 b = PxVec3(0.0f), PxVec3 c = PxVec3(0.0f)) + { + points[0] = a; + points[1] = b; + points[2] = c; + } + + TrPrcTriangle& operator=(const TrPrcTriangle& b) + { + points[0] = b.points[0]; + points[1] = b.points[1]; + points[2] = b.points[2]; + return *this; + } + + TrPrcTriangle(const TrPrcTriangle& b) + { + points[0] = b.points[0]; + points[1] = b.points[1]; + points[2] = b.points[2]; + } + PxVec3 getNormal() const + { + return (points[1] - points[0]).cross(points[2] - points[0]); + } +}; + +/** + Triangle processor internal 2D triangle representation. Contains only vertex positions. +*/ +struct TrPrcTriangle2d +{ + PxVec2 points[3]; + TrPrcTriangle2d(PxVec2 a = PxVec2(0.0f), PxVec2 b = PxVec2(0.0f), PxVec2 c = PxVec2(0.0f)) + { + points[0] = a; + points[1] = b; + points[2] = c; + } + + TrPrcTriangle2d operator=(const TrPrcTriangle2d& b) + { + points[0] = b.points[0]; + points[1] = b.points[1]; + points[2] = b.points[2]; + return *this; + } + + TrPrcTriangle2d(const TrPrcTriangle2d& b) + { + points[0] = b.points[0]; + points[1] = b.points[1]; + points[2] = b.points[2]; + } +}; + +class TriangleProcessor +{ +public: + + + TriangleProcessor() + {}; + ~TriangleProcessor() + { + } + + + /** + Build intersection between two triangles + \param[in] a First triangle (A) + \param[in] aProjected Projected triangle A + \param[in] b Second triangle (B) + \param[in] centroid Centroid of first triangle (A) + \param[out] intersectionBuffer Result intersection polygon + \param[in] normal Normal vector to triangle (Common for both A and B). + \return 1 - if if intersection is found. + */ + uint32_t getTriangleIntersection(TrPrcTriangle& a, TrPrcTriangle2d& aProjected, TrPrcTriangle &b, PxVec3& centroid, std::vector<PxVec3>& intersectionBuffer, PxVec3 normal); + + /** + Test whether BB of triangles intersect. + \param[in] a First triangle (A) + \param[in] b Second triangle (B) + \return true - if intersect + */ + bool triangleBoundingBoxIntersection(TrPrcTriangle2d& a, TrPrcTriangle2d& b); + + + /** + Test whether point is inside of triangle. + \param[in] point Point coordinates in 2d space. + \param[in] triangle Triangle in 2d space. + \return 1 - if inside, 2 if on edge, 0 if neither inside nor edge. + */ + uint32_t isPointInside(const PxVec2& point, const TrPrcTriangle2d& triangle); + + /** + Segment intersection point + \param[in] s1 Segment-1 start point + \param[in] e1 Segment-1 end point + \param[in] s2 Segment-2 start point + \param[in] e2 Segment-2 end point + \param[out] t1 Intersection point parameter relatively to Segment-1, lies in [0.0, 1.0] range. + \return 0 if there is no intersections, 1 - if intersection is found. + */ + uint32_t getSegmentIntersection(const PxVec2& s1, const PxVec2& e1, const PxVec2& s2, const PxVec2& e2, PxF32& t1); + + /** + Sort vertices of polygon in CCW-order + */ + void sortToCCW(std::vector<PxVec3>& points, PxVec3& normal); + + /** + Builds convex polygon for given set of points. Points should be coplanar. + \param[in] points Input array of points + \param[out] convexHull Output polygon + \param[in] normal Normal vector to polygon. + */ + void buildConvexHull(std::vector<PxVec3>& points, std::vector<PxVec3>& convexHull, const PxVec3& normal); +}; + +} // namespace Blast +} // namespace Nv + + +#endif // NVBLASTEXTTRIANGLEPROCESSOR_H |