aboutsummaryrefslogtreecommitdiff
path: root/NvBlast/sdk/extensions/authoring/source
diff options
context:
space:
mode:
authorBryan Galdrikian <[email protected]>2017-02-21 12:07:59 -0800
committerBryan Galdrikian <[email protected]>2017-02-21 12:07:59 -0800
commit446ce137c6823ba9eff273bdafdaf266287c7c98 (patch)
treed20aab3e2ed08d7b3ca71c2f40db6a93ea00c459 /NvBlast/sdk/extensions/authoring/source
downloadblast-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')
-rw-r--r--NvBlast/sdk/extensions/authoring/source/NvBlastExtApexSharedParts.cpp1004
-rw-r--r--NvBlast/sdk/extensions/authoring/source/NvBlastExtApexSharedParts.h51
-rw-r--r--NvBlast/sdk/extensions/authoring/source/NvBlastExtAuthoringAccelerator.cpp629
-rw-r--r--NvBlast/sdk/extensions/authoring/source/NvBlastExtAuthoringAccelerator.h147
-rw-r--r--NvBlast/sdk/extensions/authoring/source/NvBlastExtAuthoringBondGenerator.cpp991
-rw-r--r--NvBlast/sdk/extensions/authoring/source/NvBlastExtAuthoringBooleanTool.cpp1351
-rw-r--r--NvBlast/sdk/extensions/authoring/source/NvBlastExtAuthoringBooleanTool.h197
-rw-r--r--NvBlast/sdk/extensions/authoring/source/NvBlastExtAuthoringCollisionBuilder.cpp279
-rw-r--r--NvBlast/sdk/extensions/authoring/source/NvBlastExtAuthoringFractureTool.cpp1510
-rw-r--r--NvBlast/sdk/extensions/authoring/source/NvBlastExtAuthoringInternalCommon.h193
-rw-r--r--NvBlast/sdk/extensions/authoring/source/NvBlastExtAuthoringMesh.cpp558
-rw-r--r--NvBlast/sdk/extensions/authoring/source/NvBlastExtAuthoringPerlinNoise.h373
-rw-r--r--NvBlast/sdk/extensions/authoring/source/NvBlastExtAuthoringTriangulator.cpp1439
-rw-r--r--NvBlast/sdk/extensions/authoring/source/NvBlastExtAuthoringTriangulator.h261
-rw-r--r--NvBlast/sdk/extensions/authoring/source/NvBlastExtAuthoringVSA.h312
-rw-r--r--NvBlast/sdk/extensions/authoring/source/NvBlastExtTriangleProcessor.cpp355
-rw-r--r--NvBlast/sdk/extensions/authoring/source/NvBlastExtTriangleProcessor.h158
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, &params) == 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