// This code contains NVIDIA Confidential Information and is disclosed to you // under a form of NVIDIA software license agreement provided separately to you. // // Notice // NVIDIA Corporation and its licensors retain all intellectual property and // proprietary rights in and to this software and 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. // // ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES // NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO // THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, // MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. // // Information and code furnished is believed to be accurate and reliable. // However, NVIDIA Corporation assumes no responsibility for the consequences of use of such // information or for any infringement of patents or other rights of third parties that may // result from its use. No license is granted by implication or otherwise under any patent // or patent rights of NVIDIA Corporation. Details are subject to change without notice. // This code supersedes and replaces all information previously supplied. // NVIDIA Corporation products are not authorized for use as critical // components in life support devices or systems without express written approval of // NVIDIA Corporation. // // Copyright (c) 2018 NVIDIA Corporation. All rights reserved. // 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 "NvBlastExtAuthoringMeshNoiser.h" #include "NvBlastExtAuthoringPerlinNoise.h" #include #include #include #include using namespace Nv::Blast; using namespace std; void MeshNoiser::computeFalloffAndNormals() { // Map newly created vertices according to positions computePositionedMapping(); mGeometryGraph.resize(mVertices.size()); for (uint32_t i = 0; i < mEdges.size(); ++i) { if (mTrMeshEdToTr[i].c == 0) { continue; } int32_t v1 = mPositionMappedVrt[mEdges[i].s]; int32_t v2 = mPositionMappedVrt[mEdges[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 que; for (uint32_t i = 0; i < mEdges.size(); ++i) { if (mTrMeshEdToTr[i].c != 0 && (mEdgeFlag[i] == EXTERNAL_EDGE || mEdgeFlag[i] == EXTERNAL_BORDER_EDGE)) { int32_t v1 = mPositionMappedVrt[mEdges[i].s]; int32_t v2 = mPositionMappedVrt[mEdges[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(NvcVec3& as, NvcVec3& ae, NvcVec3& bs, NvcVec3& 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 (toPxShared(bs - as).cross(toPxShared(ae - as))).magnitudeSquared() < 1e-6f && (toPxShared(be - as).cross(toPxShared(ae - as))).magnitudeSquared() < 1e-6f; } void MeshNoiser::computePositionedMapping() { std::map 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 MeshNoiser::relax(int32_t iteration, float factor, std::vector& vertices) { std::vector verticesTemp(vertices.size()); std::vector 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 = toPxShared(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 += toPxShared(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 = fromPxShared(verticesTemp[i]); mVerticesNormalsSmoothed[i] = normalsTemp[i].getNormalized(); } } } NV_FORCE_INLINE void markEdge(int32_t ui, int32_t ed, std::vector& shortMarkup, std::vector& lastOwner) { if (shortMarkup[ed] == MeshNoiser::NONE) { if (ui == 0) { shortMarkup[ed] = MeshNoiser::EXTERNAL_EDGE; } else { shortMarkup[ed] = MeshNoiser::INTERNAL_EDGE; } lastOwner[ed] = ui; } else { if (ui != 0) { if (shortMarkup[ed] == MeshNoiser::EXTERNAL_EDGE) { shortMarkup[ed] = MeshNoiser::EXTERNAL_BORDER_EDGE; } if ((shortMarkup[ed] == MeshNoiser::INTERNAL_EDGE) && ui != lastOwner[ed]) { shortMarkup[ed] = MeshNoiser::INTERNAL_BORDER_EDGE; } } else { if (shortMarkup[ed] != MeshNoiser::EXTERNAL_EDGE) { shortMarkup[ed] = MeshNoiser::EXTERNAL_BORDER_EDGE; } } } } void MeshNoiser::prebuildEdgeFlagArray() { mRestrictionFlag.clear(); mRestrictionFlag.resize(mVertices.size()); mEdgeFlag.clear(); mEdgeFlag.resize(mEdges.size(), NONE); std::map 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 mPositionEdgeMap; std::vector mPositionBasedEdges(mEdges.size()); for (uint32_t i = 0; i < mEdges.size(); ++i) { Edge tmp = { mPositionMappedVrt[mEdges[i].s], mPositionMappedVrt[mEdges[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 shortMarkup(mEdges.size(), NONE); std::vector lastOwner(mEdges.size(), 0); std::vector > edgeOverlap(mEdges.size()); for (auto it1 = mPositionEdgeMap.begin(); it1 != mPositionEdgeMap.end(); ++it1) { auto it2 = it1; it2++; for (; it2 != mPositionEdgeMap.end(); ++it2) { Edge& ed1 = mEdges[it1->second]; Edge& ed2 = mEdges[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 < mTriangles.size(); ++i) { int32_t ui = mTriangles[i].userData; int32_t ed = mPositionBasedEdges[findEdge({ mTriangles[i].ea, mTriangles[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({ mTriangles[i].ea, mTriangles[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({ mTriangles[i].eb, mTriangles[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 < mEdges.size(); ++i) { mEdgeFlag[i] = shortMarkup[mPositionBasedEdges[i]]; } for (uint32_t i = 0; i < mTriangles.size(); ++i) { if (mTriangles[i].userData != 0) continue; int32_t ed = findEdge({ mTriangles[i].ea, mTriangles[i].eb }); mEdgeFlag[ed] = EXTERNAL_EDGE; ed = findEdge({ mTriangles[i].ec, mTriangles[i].eb }); mEdgeFlag[ed] = EXTERNAL_EDGE; ed = findEdge({ mTriangles[i].ea, mTriangles[i].ec }); mEdgeFlag[ed] = EXTERNAL_EDGE; } } NV_FORCE_INLINE int32_t MeshNoiser::addVerticeIfNotExist(const Vertex& p) { auto it = mVertMap.find(p); if (it == mVertMap.end()) { mVertMap[p] = static_cast(mVertices.size()); mVertices.push_back(p); return static_cast(mVertices.size()) - 1; } else { return it->second; } } NV_FORCE_INLINE int32_t MeshNoiser::addEdge(const Edge& e) { Edge ed = e; if (ed.e < ed.s) std::swap(ed.s, ed.e); auto it = mEdgeMap.find(ed); if (it == mEdgeMap.end()) { mTrMeshEdToTr.push_back(EdgeToTriangles()); mEdgeMap[ed] = (int)mEdgeMap.size(); mEdges.push_back(ed); mEdgeFlag.push_back(INTERNAL_EDGE); return (int32_t)mEdges.size() - 1; } else { return it->second; } } NV_FORCE_INLINE int32_t MeshNoiser::findEdge(const Edge& e) { Edge ed = e; if (ed.e < ed.s) std::swap(ed.s, ed.e); auto it = mEdgeMap.find(ed); if (it == mEdgeMap.end()) { return -1; } else { return it->second; } } /** Weld input vertices, build edge and triangle buffers */ void MeshNoiser::setMesh(const vector& mesh) { uint32_t a, b, c; physx::PxBounds3 box; box.setEmpty(); for (uint32_t i = 0; i < mesh.size(); ++i) { const Triangle& tr = mesh[i]; a = addVerticeIfNotExist(tr.a); b = addVerticeIfNotExist(tr.b); c = addVerticeIfNotExist(tr.c); box.include(toPxShared(tr.a.p)); box.include(toPxShared(tr.b.p)); box.include(toPxShared(tr.c.p)); addEdge({ a, b }); addEdge({ b, c }); addEdge({ a, c }); mTriangles.push_back({a, b, c}); mTriangles.back().userData = tr.userData; mTriangles.back().materialId = tr.materialId; mTriangles.back().smoothingGroup = tr.smoothingGroup; } mOffset = box.getCenter(); mScale = max(box.getExtents(0), max(box.getExtents(1), box.getExtents(2))); float invScale = 1.0f / mScale; for (uint32_t i = 0; i < mVertices.size(); ++i) { mVertices[i].p = mVertices[i].p - fromPxShared(box.getCenter()); mVertices[i].p = mVertices[i].p * invScale; } } void MeshNoiser::tesselateInternalSurface(float maxLenIn) { if (mTriangles.empty()) { return; } updateEdgeTriangleInfo(); prebuildEdgeFlagArray(); mRestrictionFlag.resize(mVertices.size(), 0); for (uint32_t i = 0; i < mEdges.size(); ++i) { if (mEdgeFlag[i] == EXTERNAL_EDGE || mEdgeFlag[i] == EXTERNAL_BORDER_EDGE || mEdgeFlag[i] == INTERNAL_BORDER_EDGE) { mRestrictionFlag[mEdges[i].s] = 1; mRestrictionFlag[mEdges[i].e] = 1; } } float 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)mEdges.size(); for (uint32_t i = 0; i < oldSize; ++i) { if (mEdgeFlag[i] == EXTERNAL_EDGE || mEdgeFlag[i] == INTERNAL_BORDER_EDGE) { continue; } if (toPxShared(mVertices[mEdges[i].s].p - mVertices[mEdges[i].e].p).magnitudeSquared() < minD) { collapseEdge(i); } } oldSize = (uint32_t)mEdges.size(); updateEdgeTriangleInfo(); for (uint32_t i = 0; i < oldSize; ++i) { if (mEdgeFlag[i] == EXTERNAL_EDGE) { continue; } if (toPxShared(mVertices[mEdges[i].s].p - mVertices[mEdges[i].e].p).magnitudeSquared() > mlSq) { divideEdge(i); } } } computeFalloffAndNormals(); prebuildTesselatedTriangles(); isTesselated = true; } void MeshNoiser::updateEdgeTriangleInfo() { mTrMeshEdToTr.clear(); mTrMeshEdToTr.resize(mEdges.size()); for (uint32_t i = 0; i < mTriangles.size(); ++i) { TriangleIndexed& tr = mTriangles[i]; if (tr.ea == kNotValidVertexIndex) continue; int32_t ed = addEdge({ tr.ea, tr.eb }); mTrMeshEdToTr[ed].add(i); ed = addEdge({ tr.ea, tr.ec }); mTrMeshEdToTr[ed].add(i); ed = addEdge({ tr.ec, tr.eb }); mTrMeshEdToTr[ed].add(i); } } void MeshNoiser::updateVertEdgeInfo() { mVertexToTriangleMap.clear(); mVertexToTriangleMap.resize(mVertices.size()); for (uint32_t i = 0; i < mTriangles.size(); ++i) { TriangleIndexed& tr = mTriangles[i]; if (tr.ea == kNotValidVertexIndex) 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 < mEdges.size(); ++i) { if (mTrMeshEdToTr[i].c != 0) { mVertexValence[mEdges[i].s]++; mVertexValence[mEdges[i].e]++; } } } inline bool isContainEdge(const TriangleIndexed& t, uint32_t a, uint32_t b) { return (a == t.ea || a == t.eb || a == t.ec) && (b == t.ea || b == t.eb || b == t.ec); } void MeshNoiser::collapseEdge(int32_t id) { Edge cEdge = mEdges[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 connectedToBegin; std::set connectedToEnd; std::set neighboorTriangles; int32_t trWithEdge[2] = { -1, -1 }; int32_t cntr = 0; for (uint32_t i = 0; i < mVertexToTriangleMap[from].size(); ++i) { if (mTriangles[mVertexToTriangleMap[from][i]].ea == kNotValidVertexIndex) continue; if (neighboorTriangles.insert(mVertexToTriangleMap[from][i]).second && isContainEdge(mTriangles[mVertexToTriangleMap[from][i]] , from, to)) { trWithEdge[cntr] = mVertexToTriangleMap[from][i]; cntr++; } } for (uint32_t i = 0; i < mVertexToTriangleMap[to].size(); ++i) { if (mTriangles[mVertexToTriangleMap[to][i]].ea == kNotValidVertexIndex) continue; if (neighboorTriangles.insert(mVertexToTriangleMap[to][i]).second && isContainEdge(mTriangles[mVertexToTriangleMap[to][i]], from, to)) { trWithEdge[cntr] = mVertexToTriangleMap[to][i]; cntr++; } } if (cntr == 0) { return; } if (cntr > 2) { return; } for (uint32_t i : neighboorTriangles) { if (mTriangles[i].ea == from || mTriangles[i].eb == from || mTriangles[i].ec == from) { if (mTriangles[i].ea != to && mTriangles[i].ea != from) connectedToBegin.insert(mTriangles[i].ea); if (mTriangles[i].eb != to && mTriangles[i].eb != from) connectedToBegin.insert(mTriangles[i].eb); if (mTriangles[i].ec != to && mTriangles[i].ec != from) connectedToBegin.insert(mTriangles[i].ec); } if (mTriangles[i].ea == to || mTriangles[i].eb == to || mTriangles[i].ec == to) { if (mTriangles[i].ea != to && mTriangles[i].ea != from) connectedToEnd.insert(mTriangles[i].ea); if (mTriangles[i].eb != to && mTriangles[i].eb != from) connectedToEnd.insert(mTriangles[i].eb); if (mTriangles[i].ec != to && mTriangles[i].ec != from) connectedToEnd.insert(mTriangles[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 ((mTriangles[tr].ea == from || mTriangles[tr].eb == from || mTriangles[tr].ec == from) && (mTriangles[tr].ea == to || mTriangles[tr].eb == to || mTriangles[tr].ec == to) && (mTriangles[tr].ea == currV || mTriangles[tr].eb == currV || mTriangles[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 = mTriangles[i]; PxVec3 oldNormal = toPxShared(mVertices[tr.eb].p - mVertices[tr.ea].p).cross(toPxShared(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 = toPxShared(mVertices[tr.eb].p - mVertices[tr.ea].p).cross(toPxShared(mVertices[tr.ec].p - mVertices[tr.ea].p)); if (newNormal.magnitude() < 1e-8f) { canBeCollapsed = false; break; } if (oldNormal.dot(newNormal) < 0) { canBeCollapsed = false; break; } } mTriangles[trWithEdge[0]].ea = kNotValidVertexIndex; if (cntr == 2) mTriangles[trWithEdge[1]].ea = kNotValidVertexIndex; for (int32_t i : neighboorTriangles) { if (mTriangles[i].ea == kNotValidVertexIndex) continue; if (mTriangles[i].ea == from) { mTriangles[i].ea = to; mVertexToTriangleMap[from].clear(); mVertexToTriangleMap[to].push_back(i); } else if (mTriangles[i].eb == from) { mTriangles[i].eb = to; mVertexToTriangleMap[from].clear(); mVertexToTriangleMap[to].push_back(i); } else if (mTriangles[i].ec == from) { mTriangles[i].ec = to; mVertexToTriangleMap[from].clear(); mVertexToTriangleMap[to].push_back(i); } } } } void MeshNoiser::divideEdge(int32_t id) { if (mTrMeshEdToTr[id].c == 0) { return; } Edge cEdge = mEdges[id]; EdgeFlag snapRestriction = mEdgeFlag[id]; Vertex middle; uint32_t nv = kNotValidVertexIndex; for (int32_t t = 0; t < mTrMeshEdToTr[id].c; ++t) { int32_t oldTriangleIndex = mTrMeshEdToTr[id].tr[t]; TriangleIndexed tr = mTriangles[mTrMeshEdToTr[id].tr[t]]; if (tr.ea == kNotValidVertexIndex) { 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 == kNotValidVertexIndex) { 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 = addEdge({ pbf[p], nv }); uint32_t ind2 = addEdge({ nv, pbf[pnx] }); uint32_t ind3 = addEdge({ nv, pbf[opp] }); mEdgeFlag[ind1] = snapRestriction; mEdgeFlag[ind2] = snapRestriction; mEdgeFlag[ind3] = INTERNAL_EDGE; mTrMeshEdToTr[ind1].add(mTrMeshEdToTr[id].tr[t]); int32_t userInfo = mTriangles[mTrMeshEdToTr[id].tr[t]].userData; int32_t matId = mTriangles[mTrMeshEdToTr[id].tr[t]].materialId; int32_t smId = mTriangles[mTrMeshEdToTr[id].tr[t]].smoothingGroup; mTriangles[mTrMeshEdToTr[id].tr[t]] = {pbf[p], nv, pbf[opp]}; mTriangles[mTrMeshEdToTr[id].tr[t]].userData = userInfo; mTriangles[mTrMeshEdToTr[id].tr[t]].materialId = matId; mTriangles[mTrMeshEdToTr[id].tr[t]].smoothingGroup = smId; mTrMeshEdToTr[ind2].add((int32_t)mTriangles.size()); mTrMeshEdToTr[ind3].add((int32_t)mTrMeshEdToTr[id].tr[t]); mTrMeshEdToTr[ind3].add((int32_t)mTriangles.size()); mTriangles.push_back({nv, pbf[pnx], pbf[opp]}); mTriangles.back().userData = userInfo; mTriangles.back().materialId = matId; mTriangles.back().smoothingGroup = smId; int32_t ed1 = findEdge({ pbf[pnx], pbf[opp] }); mTrMeshEdToTr[ed1].replace(oldTriangleIndex, (int32_t)mTriangles.size() - 1); break; } } } } float falloffFunction(float x, float mx) { float t = (x) / (mx + 1e-6f); t = std::min(1.0f, t); return t * t; } void MeshNoiser::recalcNoiseDirs() { /** Compute normals direction to apply noise */ mVerticesNormalsSmoothed.resize(mVertices.size(), PxVec3(0, 0, 0)); for (uint32_t i = 0; i < mTriangles.size(); ++i) { if (mTriangles[i].ea == kNotValidVertexIndex) { continue; } TriangleIndexed& tr = mTriangles[i]; if (tr.userData == 0) continue; if (tr.userData < 0) mVerticesNormalsSmoothed[mPositionMappedVrt[tr.ea]] += toPxShared(mVertices[tr.ea].n).getNormalized(); else mVerticesNormalsSmoothed[mPositionMappedVrt[tr.ea]] -= toPxShared(mVertices[tr.ea].n).getNormalized(); if (tr.userData < 0) mVerticesNormalsSmoothed[mPositionMappedVrt[tr.eb]] += toPxShared(mVertices[tr.eb].n).getNormalized(); else mVerticesNormalsSmoothed[mPositionMappedVrt[tr.eb]] -= toPxShared(mVertices[tr.eb].n).getNormalized(); if (tr.userData < 0) mVerticesNormalsSmoothed[mPositionMappedVrt[tr.ec]] += toPxShared(mVertices[tr.ec].n).getNormalized(); else mVerticesNormalsSmoothed[mPositionMappedVrt[tr.ec]] -= toPxShared(mVertices[tr.ec].n).getNormalized(); } for (uint32_t i = 0; i < mVerticesNormalsSmoothed.size(); ++i) { mVerticesNormalsSmoothed[i] = mVerticesNormalsSmoothed[mPositionMappedVrt[i]]; mVerticesNormalsSmoothed[i].normalize(); } } void MeshNoiser::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 < mEdges.size(); ++i) { if (mTrMeshEdToTr[i].c != 0) { if (mEdgeFlag[i] == EXTERNAL_EDGE || mEdgeFlag[i] == EXTERNAL_BORDER_EDGE) { mRestrictionFlag[mEdges[i].e] = true; mRestrictionFlag[mEdges[i].s] = true; } } } std::vector 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(toPxShared(localVertices[i].p)); toPxShared(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 < mTriangles.size(); ++i) { if (mTriangles[i].ea == kNotValidVertexIndex) { continue; } TriangleIndexed& tr = mTriangles[i]; if (tr.userData == 0) continue; Triangle pTr(localVertices[tr.ea], localVertices[tr.eb], localVertices[tr.ec]); PxVec3 nrm = toPxShared(pTr.b.p - pTr.a.p).cross(toPxShared(pTr.c.p - pTr.a.p)).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 < mTriangles.size(); ++i) { if (mTriangles[i].ea == kNotValidVertexIndex) { continue; } TriangleIndexed& tr = mTriangles[i]; if (tr.userData == 0) continue; localVertices[tr.ea].n = fromPxShared(mVerticesNormalsSmoothed[mPositionMappedVrt[tr.ea]]); localVertices[tr.eb].n = fromPxShared(mVerticesNormalsSmoothed[mPositionMappedVrt[tr.eb]]); localVertices[tr.ec].n = fromPxShared(mVerticesNormalsSmoothed[mPositionMappedVrt[tr.ec]]); } mResultTriangles.clear(); for (uint32_t i = 0; i < mTriangles.size(); ++i) { if (mTriangles[i].ea == kNotValidVertexIndex) { continue; } mResultTriangles.push_back({ localVertices[mTriangles[i].ea], localVertices[mTriangles[i].eb], localVertices[mTriangles[i].ec], mTriangles[i].userData, mTriangles[i].materialId, mTriangles[i].smoothingGroup }); } } void MeshNoiser::prebuildTesselatedTriangles() { mResultTriangles.clear(); for (uint32_t i = 0; i < mVertices.size(); ++i) { mVertices[i].p = mVertices[i].p * mScale + fromPxShared(mOffset); } for (uint32_t i = 0; i < mTriangles.size(); ++i) { if (mTriangles[i].ea == kNotValidVertexIndex) { continue; } mResultTriangles.push_back({ mVertices[mTriangles[i].ea], mVertices[mTriangles[i].eb], mVertices[mTriangles[i].ec], mTriangles[i].userData, mTriangles[i].materialId, mTriangles[i].smoothingGroup }); } } std::vector MeshNoiser::getMesh() { return mResultTriangles; } void MeshNoiser::reset() { mVertices.clear(); mTriangles.clear(); mEdges.clear(); mVertMap.clear(); mEdgeMap.clear(); mResultTriangles.clear(); mRestrictionFlag.clear(); mEdgeFlag.clear(); mTrMeshEdToTr.clear(); mVertexValence.clear(); mVertexToTriangleMap.clear(); mVerticesDistances.clear(); mVerticesNormalsSmoothed.clear(); mPositionMappedVrt.clear(); mGeometryGraph.clear(); isTesselated = false; mOffset = PxVec3(0, 0, 0); mScale = 1.0f; }