// 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 #include #include #include #include "NvBlastExtTriangleProcessor.h" #include "NvBlastExtApexSharedParts.h" #include "NvBlastExtAuthoringInternalCommon.h" #include "NvBlastExtAuthoringTypes.h" #include #include #include #include #include #include #include #define SAFE_ARRAY_NEW(T, x) ((x) > 0) ? reinterpret_cast(NVBLAST_ALLOC(sizeof(T) * (x))) : nullptr; //#define DEBUG_OUTPUT #ifdef DEBUG_OUTPUT void saveGeometryToObj(std::vector& 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 intersectionBuffer; std::vector meshBuffer; #endif namespace Nv { namespace Blast { #define EPS_PLANE 0.0001f physx::PxVec3 getNormal(const Triangle& t) { return toPxShared(t.b.p - t.a.p).cross(toPxShared(t.c.p - t.a.p)); } bool planeComparer(const PlaneChunkIndexer& as, const PlaneChunkIndexer& bs) { const NvcPlane& a = as.plane; const NvcPlane& 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; }; inline physx::PxVec3 getVertex(const Triangle& t, uint32_t i) { return toPxShared((&t.a)[i].p); } void AddTtAnchorPoints(const Triangle* a, const Triangle* b, std::vector& points) { physx::PxVec3 na = getNormal(*a).getNormalized(); physx::PxVec3 nb = getNormal(*b).getNormalized(); physx::PxPlane pla(toPxShared(a->a.p), na); physx::PxPlane plb(toPxShared(b->a.p), nb); ProjectionDirections da = getProjectionDirection(na); ProjectionDirections db = getProjectionDirection(nb); TriangleProcessor prc; TrPrcTriangle2d ta(getProjectedPoint(toPxShared(a->a.p), da), getProjectedPoint(toPxShared(a->b.p), da), getProjectedPoint(toPxShared(a->c.p), da)); TrPrcTriangle2d tb(getProjectedPoint(toPxShared(b->a.p), db), getProjectedPoint(toPxShared(b->b.p), db), getProjectedPoint(toPxShared(b->c.p), db)); /** Compute */ for (uint32_t i = 0; i < 3; ++i) { physx::PxVec3 pt; if (getPlaneSegmentIntersection(pla, getVertex(*b, i), getVertex(*b, (i + 1) % 3), pt)) { physx::PxVec2 pt2 = getProjectedPoint(pt, da); if (prc.isPointInside(pt2, ta)) { points.push_back(pt); } } if (getPlaneSegmentIntersection(plb, getVertex(*a, i), getVertex(*a, (i + 1) % 3), pt)) { PxVec2 pt2 = getProjectedPoint(pt, db); if (prc.isPointInside(pt2, tb)) { points.push_back(pt); } } } } inline bool pointInsidePoly(const PxVec3& pt, const uint8_t* indices, uint16_t indexCount, const PxVec3* verts, const PxVec3& n) { int s = 0; for (uint16_t i = 0; i < indexCount; ++i) { const PxVec3 r0 = verts[indices[i]] - pt; const PxVec3 r1 = verts[indices[(i + 1) % indexCount]] - pt; const float cn = r0.cross(r1).dot(n); const int cns = cn >= 0 ? 1 : -1; if (!s) { s = cns; } if (cns * s < 0) { return false; } } return true; } void AddPpAnchorPoints(const uint8_t* indicesA, uint16_t indexCountA, const PxVec3* vertsA, const float planeA[4], const uint8_t* indicesB, uint16_t indexCountB, const PxVec3* vertsB, const float planeB[4], std::vector& points) { PxPlane pla(planeA[0], planeA[1], planeA[2], planeA[3]); PxPlane plb(planeB[0], planeB[1], planeB[2], planeB[3]); for (uint16_t iA = 0; iA < indexCountA; ++iA) { PxVec3 pt; if (getPlaneSegmentIntersection(plb, vertsA[indicesA[iA]], vertsA[indicesA[(iA + 1) % indexCountA]], pt)) { if (pointInsidePoly(pt, indicesB, indexCountB, vertsB, plb.n)) { points.push_back(pt); } } } for (uint16_t iB = 0; iB < indexCountA; ++iB) { PxVec3 pt; if (getPlaneSegmentIntersection(pla, vertsB[indicesB[iB]], vertsB[indicesA[(iB + 1) % indexCountB]], pt)) { if (pointInsidePoly(pt, indicesA, indexCountA, vertsA, pla.n)) { points.push_back(pt); } } } } float BlastBondGeneratorImpl::processWithMidplanes(TriangleProcessor* trProcessor, const Triangle* mA, uint32_t mavc, const Triangle* mB, uint32_t mbvc, const CollisionHull* hull1, const CollisionHull* hull2, const std::vector& hull1p, const std::vector& hull2p, PxVec3& normal, PxVec3& centroid, float maxRelSeparation) { PxBounds3 bounds; PxBounds3 aBounds; PxBounds3 bBounds; bounds.setEmpty(); aBounds.setEmpty(); bBounds.setEmpty(); PxVec3 chunk1Centroid(0, 0, 0); PxVec3 chunk2Centroid(0, 0, 0); /////////////////////////////////////////////////////////////////////////////////// if (hull1p.size() < 4 || hull2p.size() < 4) { return 0.0; } for (uint32_t i = 0; i < hull1p.size(); ++i) { chunk1Centroid += hull1p[i]; bounds.include(hull1p[i]); aBounds.include(hull1p[i]); } for (uint32_t i = 0; i < hull2p.size(); ++i) { chunk2Centroid += hull2p[i]; bounds.include(hull2p[i]); bBounds.include(hull2p[i]); } chunk1Centroid *= (1.0f / hull1p.size()); chunk2Centroid *= (1.0f / hull2p.size()); const float maxSeparation = maxRelSeparation * std::sqrt(std::max(aBounds.getExtents().magnitudeSquared(), bBounds.getExtents().magnitudeSquared())); Separation separation; if (!importerHullsInProximityApexFree(hull1p.size(), hull1p.data(), aBounds, PxTransform(PxIdentity), PxVec3(1, 1, 1), hull2p.size(), hull2p.data(), bBounds, PxTransform(PxIdentity), PxVec3(1, 1, 1), 2.0f * maxSeparation, &separation)) { return 0.0; } const bool have_geometry = (mA != nullptr && mB != nullptr) || (hull1 != nullptr && hull2 != nullptr); if (separation.getDistance() > 0 || !have_geometry) // If chunks don't intersect then use midplane to produce bond, // otherwise midplane can be wrong (only if we have geometry) { // Build first plane interface PxPlane midplane = separation.plane; if (!midplane.n.isFinite()) { return 0.0; } std::vector interfacePoints; float firstCentroidSide = (midplane.distance(chunk1Centroid) > 0) ? 1 : -1; float secondCentroidSide = (midplane.distance(chunk2Centroid) > 0) ? 1 : -1; for (uint32_t i = 0; i < hull1p.size(); ++i) { float dst = midplane.distance(hull1p[i]); if (dst * firstCentroidSide < maxSeparation) { interfacePoints.push_back(hull1p[i]); } } for (uint32_t i = 0; i < hull2p.size(); ++i) { float dst = midplane.distance(hull2p[i]); if (dst * secondCentroidSide < maxSeparation) { interfacePoints.push_back(hull2p[i]); } } std::vector 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; } else { float area = 0.0f; std::vector intersectionAnchors; if (mA != nullptr && mB != nullptr) // Use triangles { for (uint32_t i = 0; i < mavc; ++i) { for (uint32_t j = 0; j < mbvc; ++j) { AddTtAnchorPoints(mA + i, mB + j, intersectionAnchors); } } } else // Use hulls { for (uint32_t i1 = 0; i1 < hull1->polygonDataCount; ++i1) { HullPolygon& poly1 = hull1->polygonData[i1]; for (uint32_t i2 = 0; i2 < hull2->polygonDataCount; ++i2) { HullPolygon& poly2 = hull2->polygonData[i2]; AddPpAnchorPoints(reinterpret_cast(hull1->indices) + poly1.indexBase, poly1.vertexCount, toPxShared(hull1->points), poly1.plane, reinterpret_cast(hull2->indices) + poly2.indexBase, poly2.vertexCount, toPxShared(hull2->points), poly2.plane, intersectionAnchors); } } } PxVec3 lcoid(0, 0, 0); for (uint32_t i = 0; i < intersectionAnchors.size(); ++i) { lcoid += intersectionAnchors[i]; } lcoid *= (1.0f / intersectionAnchors.size()); centroid = lcoid; if (intersectionAnchors.size() < 2) { return 0; } PxVec3 dir1 = intersectionAnchors[0] - lcoid; PxVec3 dir2(0, 0, 0); float maxMagn = 0.0f; float maxDist = 0.0f; for (uint32_t j = 0; j < intersectionAnchors.size(); ++j) { float d = (intersectionAnchors[j] - lcoid).magnitude(); PxVec3 tempNormal = (intersectionAnchors[j] - lcoid).cross(dir1); maxDist = std::max(d, maxDist); if (tempNormal.magnitude() > maxMagn) { dir2 = tempNormal; } } normal = dir2.getNormalized(); area = (maxDist * maxDist) * 3.14f; // Compute area like circle area; return area; } } struct BondGenerationCandidate { PxVec3 point; bool end; uint32_t parentChunk; uint32_t parentComponent; BondGenerationCandidate(); BondGenerationCandidate(const PxVec3& p, bool isEnd, uint32_t pr, uint32_t c) : point(p), end(isEnd), parentChunk(pr), parentComponent(c){}; bool operator<(const BondGenerationCandidate& in) const { if (point.x < in.point.x) return true; if (point.x > in.point.x) return false; if (point.y < in.point.y) return true; if (point.y > in.point.y) return false; if (point.z < in.point.z) return true; if (point.z > in.point.z) return false; return end < in.end; }; }; int32_t BlastBondGeneratorImpl::createFullBondListAveraged(uint32_t meshCount, const uint32_t* geometryOffset, const Triangle* geometry, const CollisionHull** chunkHulls, const bool* supportFlags, const uint32_t* meshGroups, NvBlastBondDesc*& resultBondDescs, BondGenerationConfig conf, std::set >* pairNotToTest) { std::vector > chunksPoints(meshCount); std::vector bounds(meshCount); if (!chunkHulls) { for (uint32_t i = 0; i < meshCount; ++i) { bounds[i].setEmpty(); if (!supportFlags[i]) { continue; } uint32_t count = geometryOffset[i + 1] - geometryOffset[i]; for (uint32_t j = 0; j < count; ++j) { chunksPoints[i].push_back(geometry[geometryOffset[i] + j].a.p); chunksPoints[i].push_back(geometry[geometryOffset[i] + j].b.p); chunksPoints[i].push_back(geometry[geometryOffset[i] + j].c.p); bounds[i].include(toPxShared(geometry[geometryOffset[i] + j].a.p)); bounds[i].include(toPxShared(geometry[geometryOffset[i] + j].b.p)); bounds[i].include(toPxShared(geometry[geometryOffset[i] + j].c.p)); } } } std::vector > > hullPoints(meshCount); std::vector candidates; for (uint32_t chunk = 0; chunk < meshCount; ++chunk) { if (!supportFlags[chunk]) { continue; } PxBounds3 bnd(PxBounds3::empty()); CollisionHull* tempHullPtr = nullptr; uint32_t hullCountForMesh = 0; const CollisionHull** beginChunkHulls = nullptr; if (chunkHulls) { hullCountForMesh = geometryOffset[chunk + 1] - geometryOffset[chunk]; beginChunkHulls = chunkHulls + geometryOffset[chunk]; } else { // build a convex hull and store it in the temp slot tempHullPtr = mConvexMeshBuilder->buildCollisionGeometry(chunksPoints[chunk].size(), chunksPoints[chunk].data()); hullCountForMesh = 1; beginChunkHulls = const_cast(&tempHullPtr); } hullPoints[chunk].resize(hullCountForMesh); for (uint32_t hull = 0; hull < hullCountForMesh; ++hull) { auto& curHull = hullPoints[chunk][hull]; const uint32_t pointCount = beginChunkHulls[hull]->pointsCount; curHull.resize(pointCount); for (uint32_t i = 0; i < pointCount; ++i) { curHull[i] = toPxShared(beginChunkHulls[hull]->points[i]); bnd.include(curHull[i]); } } if (tempHullPtr) { mConvexMeshBuilder->releaseCollisionHull(tempHullPtr); } float minSide = bnd.getDimensions().abs().minElement(); if (minSide > 0.f) { float scaling = std::max(1.1f, conf.maxSeparation / (minSide)); bnd.scaleFast(scaling); } candidates.push_back( BondGenerationCandidate(bnd.minimum, false, chunk, meshGroups != nullptr ? meshGroups[chunk] : 0)); candidates.push_back( BondGenerationCandidate(bnd.maximum, true, chunk, meshGroups != nullptr ? meshGroups[chunk] : 0)); } std::sort(candidates.begin(), candidates.end()); std::set listOfActiveChunks; std::vector > possibleBondGraph(meshCount); for (uint32_t idx = 0; idx < candidates.size(); ++idx) { if (!candidates[idx].end) // If new candidate { for (uint32_t activeChunk : listOfActiveChunks) { if (meshGroups != nullptr && (meshGroups[activeChunk] == candidates[idx].parentComponent)) continue; // Don't connect components with itself. possibleBondGraph[activeChunk].push_back(candidates[idx].parentChunk); } listOfActiveChunks.insert(candidates[idx].parentChunk); } else { listOfActiveChunks.erase(candidates[idx].parentChunk); } } TriangleProcessor trProcessor; std::vector mResultBondDescs; for (uint32_t i = 0; i < meshCount; ++i) { const uint32_t ihullCount = hullPoints[i].size(); for (uint32_t tj = 0; tj < possibleBondGraph[i].size(); ++tj) { uint32_t j = possibleBondGraph[i][tj]; auto pr = (i < j) ? std::make_pair(i, j) : std::make_pair(j, i); if (pairNotToTest != nullptr && pairNotToTest->find(pr) != pairNotToTest->end()) { continue; // This chunks should not generate bonds. This is used for mixed generation with bondFrom } const uint32_t jhullCount = hullPoints[j].size(); for (uint32_t ihull = 0; ihull < ihullCount; ++ihull) { for (uint32_t jhull = 0; jhull < jhullCount; ++jhull) { PxVec3 normal; PxVec3 centroid; float area = processWithMidplanes( &trProcessor, geometry ? geometry + geometryOffset[i] : nullptr, geometryOffset[i + 1] - geometryOffset[i], geometry ? geometry + geometryOffset[j] : nullptr, geometryOffset[j + 1] - geometryOffset[j], geometry ? nullptr : chunkHulls[geometryOffset[i] + ihull], geometry ? nullptr : chunkHulls[geometryOffset[j] + jhull], hullPoints[i][ihull], hullPoints[j][jhull], normal, centroid, conf.maxSeparation); 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; uint32_t maxIndex = std::max(i, j); if ((bounds[maxIndex].getCenter() - centroid).dot(normal) < 0) { normal = -normal; } bDesc.bond.normal[0] = normal.x; bDesc.bond.normal[1] = normal.y; bDesc.bond.normal[2] = normal.z; mResultBondDescs.push_back(bDesc); } } } } } resultBondDescs = SAFE_ARRAY_NEW(NvBlastBondDesc, mResultBondDescs.size()); memcpy(resultBondDescs, mResultBondDescs.data(), sizeof(NvBlastBondDesc) * mResultBondDescs.size()); return mResultBondDescs.size(); } uint32_t isSamePlane(NvcPlane& a, NvcPlane& 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 BlastBondGeneratorImpl::createFullBondListExact(uint32_t meshCount, const uint32_t* geometryOffset, const Triangle* geometry, const bool* supportFlags, NvBlastBondDesc*& resultBondDescs, BondGenerationConfig conf) { std::vector planeTriangleMapping; NV_UNUSED(conf); for (uint32_t i = 0; i < meshCount; ++i) { if (!supportFlags[i]) { continue; } uint32_t count = geometryOffset[i + 1] - geometryOffset[i]; for (uint32_t j = 0; j < count; ++j) { #ifdef DEBUG_OUTPUT meshBuffer.push_back(geometry[geometryOffset[i] + j].a.p); meshBuffer.push_back(geometry[geometryOffset[i] + j].b.p); meshBuffer.push_back(geometry[geometryOffset[i] + j].c.p); #endif NvcPlane nPlane = fromPxShared(physx::PxPlane(toPxShared(geometry[geometryOffset[i] + j].a.p), toPxShared(geometry[geometryOffset[i] + j].b.p), toPxShared(geometry[geometryOffset[i] + j].c.p))); planeTriangleMapping.push_back({ (int32_t)i, (int32_t)j, nPlane }); } } std::sort(planeTriangleMapping.begin(), planeTriangleMapping.end(), planeComparer); return createFullBondListExactInternal(meshCount, geometryOffset, geometry, planeTriangleMapping, resultBondDescs); } void BlastBondGeneratorImpl::buildGeometryCache(uint32_t meshCount, const uint32_t* geometryOffset, const Triangle* geometry) { uint32_t geometryCount = geometryOffset[meshCount]; for (uint32_t i = 0; i < meshCount; i++) { mGeometryCache.push_back(std::vector()); uint32_t count = geometryOffset[i + 1] - geometryOffset[i]; mGeometryCache.back().resize(count); memcpy(mGeometryCache.back().data(), geometry + geometryOffset[i], sizeof(Triangle) * count); } mHullsPointsCache.resize(geometryCount); mBoundsCache.resize(geometryCount); mCHullCache.resize(geometryCount); for (uint32_t i = 0; i < mGeometryCache.size(); ++i) { for (uint32_t j = 0; j < mGeometryCache[i].size(); ++j) { NvcPlane nPlane = fromPxShared(physx::PxPlane(toPxShared(mGeometryCache[i][j].a.p), toPxShared(mGeometryCache[i][j].b.p), toPxShared(mGeometryCache[i][j].c.p))); mPlaneCache.push_back({ (int32_t)i, (int32_t)j, nPlane }); } } for (uint32_t ch = 0; ch < mGeometryCache.size(); ++ch) { std::vector 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; } mCHullCache[ch] = mConvexMeshBuilder->buildCollisionGeometry(chunksPoints.size(), chunksPoints.data()); mHullsPointsCache[ch].resize(mCHullCache[ch]->pointsCount); mBoundsCache[ch].setEmpty(); for (uint32_t i = 0; i < mCHullCache[ch]->pointsCount; ++i) { mHullsPointsCache[ch][i] = toPxShared(mCHullCache[ch]->points[i]); mBoundsCache[ch].include(mHullsPointsCache[ch][i]); } } } void BlastBondGeneratorImpl::resetGeometryCache() { mGeometryCache.clear(); mPlaneCache.clear(); mHullsPointsCache.clear(); for (auto h : mCHullCache) { mConvexMeshBuilder->releaseCollisionHull(h); } mCHullCache.clear(); mBoundsCache.clear(); } int32_t BlastBondGeneratorImpl::createFullBondListExactInternal(uint32_t meshCount, const uint32_t* geometryOffset, const Triangle* geometry, std::vector& planeTriangleMapping, NvBlastBondDesc*& resultBondDescs) { NV_UNUSED(meshCount); std::map, std::pair > bonds; TriangleProcessor trPrc; std::vector 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 = 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 = geometry[geometryOffset[mappedTr.chunkId] + mappedTr.trId]; PxPlane pln = toPxShared(mappedTr.plane); TrPrcTriangle trp(toPxShared(trl.a.p), toPxShared(trl.b.p), toPxShared(trl.c.p)); PxVec3 trCentroid = toPxShared(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 bondEndPoints = std::make_pair(mappedTr.chunkId, mappedTr2.chunkId); if (bondEndPoints.second < bondEndPoints.first) continue; std::pair 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 = geometry[geometryOffset[mappedTr2.chunkId] + mappedTr2.trId]; TrPrcTriangle trp2(toPxShared(trl2.a.p), toPxShared(trl2.b.p), toPxShared(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); } } } std::vector mResultBondDescs; 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 resultBondDescs = SAFE_ARRAY_NEW(NvBlastBondDesc, mResultBondDescs.size()); memcpy(resultBondDescs, mResultBondDescs.data(), sizeof(NvBlastBondDesc) * mResultBondDescs.size()); return mResultBondDescs.size(); } int32_t BlastBondGeneratorImpl::createBondForcedInternal(const std::vector& hull0, const std::vector& hull1, const CollisionHull& cHull0, const CollisionHull& cHull1, PxBounds3 bound0, PxBounds3 bound1, NvBlastBond& resultBond, float overlapping) { TriangleProcessor trProcessor; Separation separation; importerHullsInProximityApexFree(hull0.size(), hull0.data(), bound0, PxTransform(PxIdentity), PxVec3(1, 1, 1), hull1.size(), hull1.data(), bound1, PxTransform(PxIdentity), PxVec3(1, 1, 1), 0.000, &separation); if (std::isnan(separation.plane.d)) { importerHullsInProximityApexFree( hull0.size(), hull0.data(), bound0, PxTransform(PxVec3(0.000001f, 0.000001f, 0.000001f)), PxVec3(1, 1, 1), hull1.size(), hull1.data(), bound1, PxTransform(PxIdentity), PxVec3(1, 1, 1), 0.000, &separation); if (std::isnan(separation.plane.d)) { return 1; } } PxPlane pl = separation.plane; std::vector ifsPoints[2]; float dst[2][2]; dst[0][0] = 0; dst[0][1] = MAXIMUM_EXTENT; for (uint32_t p = 0; p < cHull0.pointsCount; ++p) { float d = pl.distance(toPxShared(cHull0.points[p])); 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.pointsCount; ++p) { float d = pl.distance(toPxShared(cHull0.points[p])); 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.polygonDataCount; ++i) { auto& pd = cHull0.polygonData[i]; PxVec3 result; for (uint32_t j = 0; j < pd.vertexCount; ++j) { uint32_t nxj = (j + 1) % pd.vertexCount; const uint32_t* ind = cHull0.indices; PxVec3 a = hull0[ind[j + pd.indexBase]] - pl.n * cvOffset[0]; PxVec3 b = hull0[ind[nxj + pd.indexBase]] - pl.n * cvOffset[0]; if (getPlaneSegmentIntersection(pl, a, b, result)) { ifsPoints[0].push_back(result); } } } for (uint32_t i = 0; i < cHull1.polygonDataCount; ++i) { auto& pd = cHull1.polygonData[i]; PxVec3 result; for (uint32_t j = 0; j < pd.vertexCount; ++j) { uint32_t nxj = (j + 1) % pd.vertexCount; const uint32_t* ind = cHull1.indices; PxVec3 a = hull1[ind[j + pd.indexBase]] - pl.n * cvOffset[1]; PxVec3 b = hull1[ind[nxj + pd.indexBase]] - pl.n * cvOffset[1]; if (getPlaneSegmentIntersection(pl, a, b, result)) { ifsPoints[1].push_back(result); } } } std::vector 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 BlastBondGeneratorImpl::buildDescFromInternalFracture(FractureTool* tool, const bool* chunkIsSupport, NvBlastBondDesc*& resultBondDescs, NvBlastChunkDesc*& resultChunkDescriptors) { uint32_t chunkCount = tool->getChunkCount(); std::vector trianglesCount(chunkCount); std::vector > trianglesBuffer; for (uint32_t i = 0; i < chunkCount; ++i) { Triangle* t; trianglesCount[i] = tool->getBaseMesh(i, t); trianglesBuffer.push_back(std::shared_ptr(t, [](Triangle* t) { delete[] t; })); } if (chunkCount == 0) { return 0; } resultChunkDescriptors = SAFE_ARRAY_NEW(NvBlastChunkDesc, trianglesBuffer.size()); std::vector bondDescriptors; resultChunkDescriptors[0].parentChunkIndex = UINT32_MAX; resultChunkDescriptors[0].userData = 0; resultChunkDescriptors[0].flags = NvBlastChunkDesc::NoFlags; { PxVec3 chunkCentroid(0, 0, 0); for (uint32_t tr = 0; tr < trianglesCount[0]; ++tr) { chunkCentroid += toPxShared(trianglesBuffer[0].get()[tr].a.p); chunkCentroid += toPxShared(trianglesBuffer[0].get()[tr].b.p); chunkCentroid += toPxShared(trianglesBuffer[0].get()[tr].c.p); } chunkCentroid *= (1.0f / (3 * trianglesCount[0])); resultChunkDescriptors[0].centroid[0] = chunkCentroid[0]; resultChunkDescriptors[0].centroid[1] = chunkCentroid[1]; resultChunkDescriptors[0].centroid[2] = chunkCentroid[2]; } bool hasApproximateBonding = false; for (uint32_t i = 1; i < chunkCount; ++i) { NvBlastChunkDesc& desc = resultChunkDescriptors[i]; desc.userData = tool->getChunkId(i); desc.parentChunkIndex = tool->getChunkIndex(tool->getChunkInfo(i).parent); desc.flags = NvBlastChunkDesc::NoFlags; hasApproximateBonding |= !!(tool->getChunkInfo(i).flags & ChunkInfo::APPROXIMATE_BONDING); if (chunkIsSupport[i]) { desc.flags = NvBlastChunkDesc::SupportFlag; } PxVec3 chunkCentroid(0, 0, 0); for (uint32_t tr = 0; tr < trianglesCount[i]; ++tr) { auto& trRef = trianglesBuffer[i].get()[tr]; chunkCentroid += toPxShared(trRef.a.p); chunkCentroid += toPxShared(trRef.b.p); chunkCentroid += toPxShared(trRef.c.p); int32_t id = trRef.userData; 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 * trianglesCount[i])); desc.centroid[0] = chunkCentroid[0]; desc.centroid[1] = chunkCentroid[1]; desc.centroid[2] = chunkCentroid[2]; } std::sort(bondDescriptors.begin(), bondDescriptors.end()); std::vector mResultBondDescs; if (!bondDescriptors.empty()) { int32_t chunkId, planeId; chunkId = bondDescriptors[0].m_chunkId; planeId = bondDescriptors[0].m_planeIndex; std::vector forwardChunks; std::vector 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) { if (chunkIsSupport[forwardChunks[fchunk].m_chunkId] == false) { continue; } for (uint32_t bchunk = 0; bchunk < backwardChunks.size(); ++bchunk) { if (weakBoundingBoxIntersection(forwardChunks[fchunk].m_bb, backwardChunks[bchunk].m_bb) == 0) { continue; } if (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++; auto& trRef = trianglesBuffer[chunkId].get()[bondDescriptors[i].triangleIndex]; PxVec3 n = getNormal(trRef); area += n.magnitude(); normal = n.getNormalized(); centroid += toPxShared(trRef.a.p); centroid += toPxShared(trRef.b.p); centroid += toPxShared(trRef.c.p); bb.include(toPxShared(trRef.a.p)); bb.include(toPxShared(trRef.b.p)); bb.include(toPxShared(trRef.c.p)); } } if (hasApproximateBonding) { std::vector chunkTriangles; std::vector chunkTrianglesOffsets; std::set > pairsAlreadyCreated; for (uint32_t i = 0; i < mResultBondDescs.size(); ++i) { auto pr = (mResultBondDescs[i].chunkIndices[0] < mResultBondDescs[i].chunkIndices[1]) ? std::make_pair(mResultBondDescs[i].chunkIndices[0], mResultBondDescs[i].chunkIndices[1]) : std::make_pair(mResultBondDescs[i].chunkIndices[1], mResultBondDescs[i].chunkIndices[0]); pairsAlreadyCreated.insert(pr); } const float EXPANSION = 0.01f; chunkTrianglesOffsets.push_back(0); for (uint32_t i = 0; i < chunkCount; ++i) { const float SCALE_FACTOR = 1.001f; NvcVec3 centroid = {resultChunkDescriptors[i].centroid[0], resultChunkDescriptors[i].centroid[1], resultChunkDescriptors[i].centroid[2]}; for (uint32_t k = 0; k < trianglesCount[i]; ++k) { chunkTriangles.push_back(trianglesBuffer[i].get()[k]); // inflate mesh a bit chunkTriangles.back().a.p = chunkTriangles.back().a.p + (chunkTriangles.back().a.p - centroid) * EXPANSION; chunkTriangles.back().b.p = chunkTriangles.back().b.p + (chunkTriangles.back().b.p - centroid) * EXPANSION; chunkTriangles.back().c.p = chunkTriangles.back().c.p + (chunkTriangles.back().c.p - centroid) * EXPANSION; } chunkTrianglesOffsets.push_back(chunkTriangles.size()); } NvBlastBondDesc* adsc; BondGenerationConfig cfg; cfg.bondMode = BondGenerationConfig::AVERAGE; cfg.maxSeparation = EXPANSION; uint32_t nbListSize = createFullBondListAveraged(chunkCount, chunkTrianglesOffsets.data(), chunkTriangles.data(), nullptr, chunkIsSupport, nullptr, adsc, cfg, &pairsAlreadyCreated); for (uint32_t i = 0; i < nbListSize; ++i) { mResultBondDescs.push_back(adsc[i]); } NVBLAST_FREE(adsc); } resultBondDescs = SAFE_ARRAY_NEW(NvBlastBondDesc, mResultBondDescs.size()); memcpy(resultBondDescs, mResultBondDescs.data(), sizeof(NvBlastBondDesc) * mResultBondDescs.size()); return mResultBondDescs.size(); } int32_t BlastBondGeneratorImpl::createBondBetweenMeshes(uint32_t meshCount, const uint32_t* geometryOffset, const Triangle* geometry, uint32_t overlapsCount, const uint32_t* overlapsA, const uint32_t* overlapsB, NvBlastBondDesc*& resultBond, BondGenerationConfig cfg) { if (cfg.bondMode == BondGenerationConfig::AVERAGE) { resetGeometryCache(); buildGeometryCache(meshCount, geometryOffset, geometry); } resultBond = SAFE_ARRAY_NEW(NvBlastBondDesc, overlapsCount); if (cfg.bondMode == BondGenerationConfig::EXACT) { for (uint32_t i = 0; i < overlapsCount; ++i) { NvBlastBondDesc& desc = resultBond[i]; desc.chunkIndices[0] = overlapsA[i]; desc.chunkIndices[1] = overlapsB[i]; uint32_t meshACount = geometryOffset[overlapsA[i] + 1] - geometryOffset[overlapsA[i]]; uint32_t meshBCount = geometryOffset[overlapsB[i] + 1] - geometryOffset[overlapsB[i]]; createBondBetweenMeshes(meshACount, geometry + geometryOffset[overlapsA[i]], meshBCount, geometry + geometryOffset[overlapsB[i]], desc.bond, cfg); } } else { for (uint32_t i = 0; i < overlapsCount; ++i) { NvBlastBondDesc& desc = resultBond[i]; desc.chunkIndices[0] = overlapsA[i]; desc.chunkIndices[1] = overlapsB[i]; createBondForcedInternal(mHullsPointsCache[overlapsA[i]], mHullsPointsCache[overlapsB[i]], *mCHullCache[overlapsA[i]], *mCHullCache[overlapsB[i]], mBoundsCache[overlapsA[i]], mBoundsCache[overlapsB[i]], desc.bond, 0.3f); } } return overlapsCount; } int32_t BlastBondGeneratorImpl::createBondBetweenMeshes(uint32_t meshACount, const Triangle* meshA, uint32_t meshBCount, const Triangle* meshB, NvBlastBond& resultBond, BondGenerationConfig conf) { float overlapping = 0.3; if (conf.bondMode == BondGenerationConfig::EXACT) { std::vector chunksOffsets = { 0, meshACount, meshACount + meshBCount }; std::vector chunks; chunks.resize(meshACount + meshBCount); memcpy(chunks.data(), meshA, sizeof(Triangle) * meshACount); memcpy(chunks.data() + meshACount, meshB, sizeof(Triangle) * meshBCount); std::shared_ptr isSupport(new bool[2]{ true, true }, [](bool* b) { delete[] b; }); NvBlastBondDesc* desc; uint32_t descSize = createFullBondListExact(2, chunksOffsets.data(), chunks.data(), isSupport.get(), desc, conf); if (descSize > 0) { resultBond = desc->bond; } else { memset(&resultBond, 0, sizeof(NvBlastBond)); return 1; } return 0; } std::vector chunksPoints1(meshACount * 3); std::vector chunksPoints2(meshBCount * 3); int32_t sp = 0; for (uint32_t i = 0; i < meshACount; ++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 < meshBCount; ++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 } CollisionHull* cHull[2]; cHull[0] = mConvexMeshBuilder->buildCollisionGeometry(chunksPoints1.size(), chunksPoints1.data()); cHull[1] = mConvexMeshBuilder->buildCollisionGeometry(chunksPoints2.size(), chunksPoints2.data()); std::vector hullPoints[2]; hullPoints[0].resize(cHull[0]->pointsCount); hullPoints[1].resize(cHull[1]->pointsCount); 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]->pointsCount; ++i) { hullPoints[cv][i] = toPxShared(cHull[cv]->points[i]); bb[cv].include(hullPoints[cv][i]); } } auto ret = createBondForcedInternal(hullPoints[0], hullPoints[1], *cHull[0], *cHull[1], bb[0], bb[1], resultBond, overlapping); mConvexMeshBuilder->releaseCollisionHull(cHull[0]); mConvexMeshBuilder->releaseCollisionHull(cHull[1]); return ret; } int32_t BlastBondGeneratorImpl::bondsFromPrefractured(uint32_t meshCount, const uint32_t* geometryCount, const Triangle* geometry, const bool* chunkIsSupport, NvBlastBondDesc*& resultBondDescs, BondGenerationConfig conf) { int32_t ret_val = 0; switch (conf.bondMode) { case BondGenerationConfig::AVERAGE: ret_val = createFullBondListAveraged(meshCount, geometryCount, geometry, nullptr, chunkIsSupport, nullptr, resultBondDescs, conf); break; case BondGenerationConfig::EXACT: ret_val = createFullBondListExact(meshCount, geometryCount, geometry, chunkIsSupport, resultBondDescs, conf); break; } return ret_val; } int32_t BlastBondGeneratorImpl::bondsFromPrefractured(uint32_t meshCount, const uint32_t* convexHullOffset, const CollisionHull** chunkHulls, const bool* chunkIsSupport, const uint32_t* meshGroups, NvBlastBondDesc*& resultBondDescs, float maxSeparation) { BondGenerationConfig conf; conf.maxSeparation = maxSeparation; conf.bondMode = BondGenerationConfig::AVERAGE; return createFullBondListAveraged(meshCount, convexHullOffset, nullptr, chunkHulls, chunkIsSupport, meshGroups, resultBondDescs, conf); } void BlastBondGeneratorImpl::release() { delete this; } } // namespace Blast } // namespace Nv