From 7115f60b91b5717d90f643fd692010905c7004db Mon Sep 17 00:00:00 2001 From: Bryan Galdrikian Date: Thu, 31 May 2018 11:36:08 -0700 Subject: Blast 1.1.3. See docs/release_notes.txt. --- .../authoring/source/NvBlastExtAuthoring.cpp | 1080 ++++++++++---------- 1 file changed, 537 insertions(+), 543 deletions(-) mode change 100644 => 100755 sdk/extensions/authoring/source/NvBlastExtAuthoring.cpp (limited to 'sdk/extensions/authoring/source/NvBlastExtAuthoring.cpp') diff --git a/sdk/extensions/authoring/source/NvBlastExtAuthoring.cpp b/sdk/extensions/authoring/source/NvBlastExtAuthoring.cpp old mode 100644 new mode 100755 index 156751f..6bc12bd --- a/sdk/extensions/authoring/source/NvBlastExtAuthoring.cpp +++ b/sdk/extensions/authoring/source/NvBlastExtAuthoring.cpp @@ -1,543 +1,537 @@ -// 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) 2016-2018 NVIDIA Corporation. All rights reserved. - -#include "NvBlastExtAuthoring.h" -#include "NvBlastExtAuthoringMeshImpl.h" -#include "NvBlastExtAuthoringMeshCleanerImpl.h" -#include "NvBlastExtAuthoringFractureToolImpl.h" -#include "NvBlastExtAuthoringCollisionBuilderImpl.h" -#include "NvBlastExtAuthoringBondGeneratorImpl.h" -#include "NvBlastExtAuthoringCutoutImpl.h" -#include "NvBlastTypes.h" -#include "NvBlastIndexFns.h" -#include "NvBlast.h" -#include "NvBlastGlobals.h" -#include "NvBlastExtPxAsset.h" -#include "NvBlastExtAssetUtils.h" - -#include -#include - -using namespace Nv::Blast; -using namespace physx; - -#define SAFE_ARRAY_NEW(T, x) ((x) > 0) ? reinterpret_cast(NVBLAST_ALLOC(sizeof(T) * (x))) : nullptr; -#define SAFE_ARRAY_DELETE(x) if (x != nullptr) {NVBLAST_FREE(x); x = nullptr;} - -Mesh* NvBlastExtAuthoringCreateMesh(const PxVec3* position, const PxVec3* normals, const PxVec2* uv, uint32_t verticesCount, const uint32_t* indices, uint32_t indicesCount) -{ - return new MeshImpl(position, normals, uv, verticesCount, indices, indicesCount); -} - -Mesh* NvBlastExtAuthoringCreateMeshFromFacets(const void* vertices, const void* edges, const void* facets, uint32_t verticesCount, uint32_t edgesCount, uint32_t facetsCount) -{ - return new MeshImpl((Vertex*)vertices, (Edge*)edges, (Facet*)facets, verticesCount, edgesCount, facetsCount); -} - -MeshCleaner* NvBlastExtAuthoringCreateMeshCleaner() -{ - return new MeshCleanerImpl; -} - -VoronoiSitesGenerator* NvBlastExtAuthoringCreateVoronoiSitesGenerator(Mesh* mesh, RandomGeneratorBase* rng) -{ - return new VoronoiSitesGeneratorImpl(mesh, rng); -} - -CutoutSet* NvBlastExtAuthoringCreateCutoutSet() -{ - return new CutoutSetImpl(); -} - -void NvBlastExtAuthoringBuildCutoutSet(CutoutSet& cutoutSet, const uint8_t* pixelBuffer, uint32_t bufferWidth, uint32_t bufferHeight, - float segmentationErrorThreshold, float snapThreshold, bool periodic, bool expandGaps) -{ - ::createCutoutSet(*(CutoutSetImpl*)&cutoutSet, pixelBuffer, bufferWidth, bufferHeight, segmentationErrorThreshold, snapThreshold, periodic, expandGaps); -} - -FractureTool* NvBlastExtAuthoringCreateFractureTool() -{ - return new FractureToolImpl; -} - -BlastBondGenerator* NvBlastExtAuthoringCreateBondGenerator(PxCooking* cooking, PxPhysicsInsertionCallback* insertionCallback) -{ - return new BlastBondGeneratorImpl(cooking, insertionCallback); -} - -ConvexMeshBuilder* NvBlastExtAuthoringCreateConvexMeshBuilder(PxCooking* cooking, PxPhysicsInsertionCallback* insertionCallback) -{ - return new ConvexMeshBuilderImpl(cooking, insertionCallback); -} - - -void NvBlastExtAuthoringTransformCollisionHullInPlace(CollisionHull* hull, const physx::PxVec3* scaling, const physx::PxQuat* rotation, const physx::PxVec3* translation) -{ - // Local copies of scaling (S), rotation (R), and translation (T) - physx::PxVec3 S = { 1, 1, 1 }; - physx::PxQuat R = { 0, 0, 0, 1 }; - physx::PxVec3 T = { 0, 0, 0 }; - physx::PxVec3 cofS = { 1, 1, 1 }; - float sgnDetS = 1; - - { - if (rotation) - { - R = *rotation; - } - - if (scaling) - { - S = *scaling; - cofS.x = S.y * S.z; - cofS.y = S.z * S.x; - cofS.z = S.x * S.y; - sgnDetS = (S.x * S.y * S.z < 0) ? -1 : 1; - } - - if (translation) - { - T = *translation; - } - } - - const uint32_t pointCount = hull->pointsCount; - for (uint32_t pi = 0; pi < pointCount; pi++) - { - physx::PxVec3& p = hull->points[pi]; - p = (R.rotate(p.multiply(S)) + T); - } - - const uint32_t planeCount = hull->polygonDataCount; - for (uint32_t pi = 0; pi < planeCount; pi++) - { - float* plane = hull->polygonData[pi].mPlane; - physx::PxPlane pxPlane(plane[0], plane[1], plane[2], plane[3]); - PxVec3 transformedNormal = sgnDetS*R.rotate(pxPlane.n.multiply(cofS)).getNormalized(); - PxVec3 transformedPt = R.rotate(pxPlane.pointInPlane().multiply(S)) + T; - - physx::PxPlane transformedPlane(transformedPt, transformedNormal); - plane[0] = transformedPlane.n[0]; - plane[1] = transformedPlane.n[1]; - plane[2] = transformedPlane.n[2]; - plane[3] = transformedPlane.d; - } -} - - -CollisionHull* NvBlastExtAuthoringTransformCollisionHull(const CollisionHull* hull, const physx::PxVec3* scaling, const physx::PxQuat* rotation, const physx::PxVec3* translation) -{ - CollisionHullImpl* ret = new CollisionHullImpl(*hull); - NvBlastExtAuthoringTransformCollisionHullInPlace(ret, scaling, rotation, translation); - return ret; -} - -void buildPhysicsChunks(ConvexMeshBuilder& collisionBuilder, AuthoringResult& result, const CollisionParams& params, uint32_t chunksToProcessCount = 0, uint32_t* chunksToProcess = nullptr) -{ - uint32_t chunkCount = (uint32_t)result.chunkCount; - if (params.maximumNumberOfHulls == 1) - { - result.collisionHullOffset = SAFE_ARRAY_NEW(uint32_t, chunkCount + 1); - result.collisionHullOffset[0] = 0; - result.collisionHull = SAFE_ARRAY_NEW(CollisionHull*, chunkCount); - result.physicsSubchunks = SAFE_ARRAY_NEW(ExtPxSubchunk, chunkCount); - result.physicsChunks = SAFE_ARRAY_NEW(ExtPxChunk, chunkCount); - for (uint32_t i = 0; i < chunkCount; ++i) - { - std::vector vertices; - for (uint32_t p = result.geometryOffset[i]; p < result.geometryOffset[i + 1]; ++p) - { - Nv::Blast::Triangle& tri = result.geometry[p]; - vertices.push_back(tri.a.p); - vertices.push_back(tri.b.p); - vertices.push_back(tri.c.p); - } - result.collisionHullOffset[i + 1] = result.collisionHullOffset[i] + 1; - result.collisionHull[i] = collisionBuilder.buildCollisionGeometry((uint32_t)vertices.size(), vertices.data()); - result.physicsSubchunks[i].transform = physx::PxTransform(physx::PxIdentity); - result.physicsSubchunks[i].geometry = physx::PxConvexMeshGeometry(collisionBuilder.buildConvexMesh(*result.collisionHull[i])); - - result.physicsChunks[i].isStatic = false; - result.physicsChunks[i].subchunkCount = 1; - result.physicsChunks[i].firstSubchunkIndex = i; - //outPhysicsChunks.get()[i].subchunks = &outPhysicsSubchunks[i]; - } - } - else - { - std::set chunkSet; - for (uint32_t c = 0; c < chunksToProcessCount; c++) - { - chunkSet.insert(chunksToProcess[c]); - } - std::vector > hulls(chunkCount); - int32_t totalHulls = 0; - for (uint32_t i = 0; i < chunkCount; ++i) - { - if (chunkSet.size() > 0 && chunkSet.find(i) == chunkSet.end()) - { - int32_t newHulls = result.collisionHullOffset[i + 1] - result.collisionHullOffset[i]; - int32_t off = result.collisionHullOffset[i]; - for (int32_t subhull = 0; subhull < newHulls; ++subhull) - { - hulls[i].push_back(result.collisionHull[off + subhull]); - } - totalHulls += newHulls; - continue; - } - - CollisionHull** tempHull; - - int32_t newHulls = collisionBuilder.buildMeshConvexDecomposition(result.geometry + result.geometryOffset[i], - result.geometryOffset[i + 1] - result.geometryOffset[i], params, tempHull); - totalHulls += newHulls; - for (int32_t h = 0; h < newHulls; ++h) - { - hulls[i].push_back(tempHull[h]); - } - SAFE_ARRAY_DELETE(tempHull); - } - - SAFE_ARRAY_DELETE(result.collisionHullOffset); - SAFE_ARRAY_DELETE(result.collisionHull); - SAFE_ARRAY_DELETE(result.physicsSubchunks); - SAFE_ARRAY_DELETE(result.physicsChunks); - - result.collisionHullOffset = SAFE_ARRAY_NEW(uint32_t, chunkCount + 1); - result.collisionHullOffset[0] = 0; - result.collisionHull = SAFE_ARRAY_NEW(CollisionHull*, totalHulls); - result.physicsSubchunks = SAFE_ARRAY_NEW(ExtPxSubchunk, totalHulls); - result.physicsChunks = SAFE_ARRAY_NEW(ExtPxChunk, chunkCount); - - int32_t firstSubchunk = 0; - for (uint32_t i = 0; i < chunkCount; ++i) - { - result.collisionHullOffset[i + 1] = result.collisionHullOffset[i] + hulls[i].size(); - int32_t off = result.collisionHullOffset[i]; - for (uint32_t subhull = 0; subhull < hulls[i].size(); ++subhull) - { - result.collisionHull[off + subhull] = hulls[i][subhull]; - result.physicsSubchunks[firstSubchunk + subhull].transform = physx::PxTransform(physx::PxIdentity); - result.physicsSubchunks[firstSubchunk + subhull].geometry = physx::PxConvexMeshGeometry(collisionBuilder.buildConvexMesh(*hulls[i][subhull])); - } - result.physicsChunks[i].isStatic = false; - result.physicsChunks[i].subchunkCount = static_cast(hulls[i].size()); - result.physicsChunks[i].firstSubchunkIndex = firstSubchunk; - firstSubchunk += result.physicsChunks[i].subchunkCount; - } - } -} - - -struct AuthoringResultImpl : public AuthoringResult -{ - void releaseCollisionHulls() override - { - if (collisionHull != nullptr) - { - for (uint32_t ch = 0; ch < collisionHullOffset[chunkCount]; ch++) - { - collisionHull[ch]->release(); - } - SAFE_ARRAY_DELETE(collisionHullOffset); - SAFE_ARRAY_DELETE(collisionHull); - } - } - - void release() override - { - releaseCollisionHulls(); - NVBLAST_FREE(asset); - SAFE_ARRAY_DELETE(assetToFractureChunkIdMap); - SAFE_ARRAY_DELETE(geometryOffset); - SAFE_ARRAY_DELETE(geometry); - SAFE_ARRAY_DELETE(chunkDescs); - SAFE_ARRAY_DELETE(bondDescs); - SAFE_ARRAY_DELETE(physicsChunks); - SAFE_ARRAY_DELETE(physicsSubchunks); - delete this; - } -}; - -AuthoringResult* NvBlastExtAuthoringProcessFracture(FractureTool& fTool, BlastBondGenerator& bondGenerator, ConvexMeshBuilder& collisionBuilder, const CollisionParams& collisionParam, int32_t defaultSupportDepth) -{ - fTool.finalizeFracturing(); - const uint32_t chunkCount = fTool.getChunkCount(); - if (chunkCount == 0) - { - return nullptr; - } - AuthoringResultImpl* ret = new AuthoringResultImpl; - if (ret == nullptr) - { - return nullptr; - } - AuthoringResult& aResult = *ret; - aResult.chunkCount = chunkCount; - - std::shared_ptr isSupport(new bool[chunkCount], [](bool* b) {delete[] b; }); - memset(isSupport.get(), 0, sizeof(bool) * chunkCount); - for (uint32_t i = 0; i < fTool.getChunkCount(); ++i) - { - if (defaultSupportDepth < 0 || fTool.getChunkDepth(fTool.getChunkId(i)) < defaultSupportDepth) - { - isSupport.get()[i] = fTool.getChunkInfo(i).isLeaf; - } - else if (fTool.getChunkDepth(fTool.getChunkId(i)) == defaultSupportDepth) - { - isSupport.get()[i] = true; - } - } - - BondGenerationConfig cnf; - cnf.bondMode = BondGenerationConfig::EXACT; - - //NvBlastChunkDesc>& chunkDescs = aResult.chunkDescs; - //std::shared_ptr& bondDescs = aResult.bondDescs; - const uint32_t bondCount = bondGenerator.buildDescFromInternalFracture(&fTool, isSupport.get(), aResult.bondDescs, aResult.chunkDescs); - aResult.bondCount = bondCount; - if (bondCount == 0) - { - aResult.bondDescs = nullptr; - } - - // order chunks, build map - std::vector chunkReorderInvMap; - { - std::vector chunkReorderMap(chunkCount); - std::vector scratch(chunkCount * sizeof(NvBlastChunkDesc)); - NvBlastEnsureAssetExactSupportCoverage(aResult.chunkDescs, chunkCount, scratch.data(), logLL); - NvBlastBuildAssetDescChunkReorderMap(chunkReorderMap.data(), aResult.chunkDescs, chunkCount, scratch.data(), logLL); - NvBlastApplyAssetDescChunkReorderMapInPlace(aResult.chunkDescs, chunkCount, aResult.bondDescs, bondCount, chunkReorderMap.data(), true, scratch.data(), logLL); - chunkReorderInvMap.resize(chunkReorderMap.size()); - Nv::Blast::invertMap(chunkReorderInvMap.data(), chunkReorderMap.data(), static_cast(chunkReorderMap.size())); - } - - // get result geometry - aResult.geometryOffset = SAFE_ARRAY_NEW(uint32_t, chunkCount + 1); - aResult.assetToFractureChunkIdMap = SAFE_ARRAY_NEW(uint32_t, chunkCount + 1); - aResult.geometryOffset[0] = 0; - std::vector chunkGeometry(chunkCount); - for (uint32_t i = 0; i < chunkCount; ++i) - { - uint32_t chunkIndex = chunkReorderInvMap[i]; - aResult.geometryOffset[i+1] = aResult.geometryOffset[i] + fTool.getBaseMesh(chunkIndex, chunkGeometry[i]); - aResult.assetToFractureChunkIdMap[i] = fTool.getChunkId(chunkIndex); - } - aResult.geometry = SAFE_ARRAY_NEW(Triangle, aResult.geometryOffset[chunkCount]); - for (uint32_t i = 0; i < chunkCount; ++i) - { - uint32_t trianglesCount = aResult.geometryOffset[i + 1] - aResult.geometryOffset[i]; - memcpy(aResult.geometry + aResult.geometryOffset[i], chunkGeometry[i], trianglesCount * sizeof(Nv::Blast::Triangle)); - delete chunkGeometry[i]; - chunkGeometry[i] = nullptr; - } - - float maxX = INT32_MIN; - float maxY = INT32_MIN; - float maxZ = INT32_MIN; - - float minX = INT32_MAX; - float minY = INT32_MAX; - float minZ = INT32_MAX; - - for (uint32_t i = 0; i < bondCount; i++) - { - NvBlastBondDesc& bondDesc = aResult.bondDescs[i]; - - minX = std::min(minX, bondDesc.bond.centroid[0]); - maxX = std::max(maxX, bondDesc.bond.centroid[0]); - - minY = std::min(minY, bondDesc.bond.centroid[1]); - maxY = std::max(maxY, bondDesc.bond.centroid[1]); - - minZ = std::min(minZ, bondDesc.bond.centroid[2]); - maxZ = std::max(maxZ, bondDesc.bond.centroid[2]); - } - - //std::cout << "Bond bounds: " << std::endl; - //std::cout << "MIN: " << minX << ", " << minY << ", " << minZ << std::endl; - //std::cout << "MAX: " << maxX << ", " << maxY << ", " << maxZ << std::endl; - - // prepare physics data (convexes) - buildPhysicsChunks(collisionBuilder, aResult, collisionParam); - - // set NvBlastChunk volume from Px geometry - for (uint32_t i = 0; i < chunkCount; i++) - { - float totalVolume = 0.f; - for (uint32_t k = 0; k < aResult.physicsChunks[i].subchunkCount; k++) - { - const auto& subChunk = aResult.physicsSubchunks[aResult.physicsChunks[i].firstSubchunkIndex + k]; - physx::PxVec3 localCenterOfMass; physx::PxMat33 intertia; float mass; - subChunk.geometry.convexMesh->getMassInformation(mass, intertia, localCenterOfMass); - const physx::PxVec3 scale = subChunk.geometry.scale.scale; - mass *= scale.x * scale.y * scale.z; - totalVolume += mass / 1.0f; // unit density - } - - aResult.chunkDescs[i].volume = totalVolume; - } - - // build and serialize ExtPhysicsAsset - NvBlastAssetDesc descriptor; - descriptor.bondCount = bondCount; - descriptor.bondDescs = aResult.bondDescs; - descriptor.chunkCount = chunkCount; - descriptor.chunkDescs = aResult.chunkDescs; - - std::vector scratch(static_cast(NvBlastGetRequiredScratchForCreateAsset(&descriptor, logLL))); - void* mem = NVBLAST_ALLOC(NvBlastGetAssetMemorySize(&descriptor, logLL)); - aResult.asset = NvBlastCreateAsset(mem, &descriptor, scratch.data(), logLL); - - //aResult.asset = std::shared_ptr(asset, [=](NvBlastAsset* asset) - //{ - // NVBLAST_FREE(asset); - //}); - - //std::cout << "Done" << std::endl; - ret->materialCount = 0; - ret->materialNames = nullptr; - return ret; -} - -uint32_t NvBlastExtAuthoringFindAssetConnectingBonds -( - const NvBlastAsset** components, - const physx::PxVec3* scales, - const physx::PxQuat* rotations, - const physx::PxVec3* translations, - const uint32_t** convexHullOffsets, - const CollisionHull*** chunkHulls, - uint32_t componentCount, - NvBlastExtAssetUtilsBondDesc*& newBondDescs, - float maxSeparation -) -{ - //We don't need to use any of the cooking related parts of this - BlastBondGeneratorImpl bondGenerator(nullptr, nullptr); - - std::vector componentChunkOffsets; - componentChunkOffsets.reserve(componentCount + 1); - componentChunkOffsets.push_back(0); - - std::vector combinedConvexHullOffsets; - std::vector combinedConvexHulls; - std::vector hullsToRelease; - combinedConvexHullOffsets.push_back(0); - - std::vector originalComponentIndex; - - const physx::PxVec3 identityScale(1); - - //Combine our hull lists into a single combined list for bondsFromPrefractured - for (uint32_t c = 0; c < componentCount; c++) - { - const uint32_t chunkCount = NvBlastAssetGetChunkCount(components[c], &logLL); - const physx::PxVec3* scale = scales ? scales + c : nullptr; - const physx::PxQuat* rotation = rotations ? rotations + c : nullptr; - const physx::PxVec3* translation = translations ? translations + c : nullptr; - - componentChunkOffsets.push_back(chunkCount + componentChunkOffsets.back()); - for (uint32_t chunk = 0; chunk < chunkCount; chunk++) - { - const uint32_t hullsStart = convexHullOffsets[c][chunk]; - const uint32_t hullsEnd = convexHullOffsets[c][chunk + 1]; - for (uint32_t hull = hullsStart; hull < hullsEnd; hull++) - { - if ((scale != nullptr && *scale != identityScale) || (rotation != nullptr && !rotation->isIdentity()) || (translation != nullptr && !translation->isZero())) - { - hullsToRelease.emplace_back(NvBlastExtAuthoringTransformCollisionHull(chunkHulls[c][hull], scale, rotation, translation)); - combinedConvexHulls.emplace_back(hullsToRelease.back()); - } - else - { - //No need to transform - combinedConvexHulls.emplace_back(chunkHulls[c][hull]); - } - } - combinedConvexHullOffsets.push_back((hullsEnd - hullsStart) + combinedConvexHullOffsets.back()); - originalComponentIndex.push_back(c); - } - } - const uint32_t totalChunkCount = componentChunkOffsets.back(); - //Can't use std::vector since we need a bool* later - std::unique_ptr isSupportChunk(new bool[totalChunkCount]); - for (uint32_t c = 0; c < componentCount; c++) - { - const uint32_t chunkCount = componentChunkOffsets[c + 1] - componentChunkOffsets[c]; - NvBlastSupportGraph supportGraph = NvBlastAssetGetSupportGraph(components[c], &logLL); - for (uint32_t chunk = 0; chunk < chunkCount; chunk++) - { - auto chunkIndiciesEnd = supportGraph.chunkIndices + supportGraph.nodeCount; - isSupportChunk[chunk + componentChunkOffsets[c]] = (std::find(supportGraph.chunkIndices, chunkIndiciesEnd, chunk) != chunkIndiciesEnd); - } - } - - //Find the bonds - NvBlastBondDesc* newBonds = nullptr; - const int32_t newBoundCount = bondGenerator.bondsFromPrefractured(totalChunkCount, combinedConvexHullOffsets.data(), combinedConvexHulls.data(), isSupportChunk.get(), originalComponentIndex.data(), newBonds, maxSeparation); - - //Convert the bonds back to per-component chunks - newBondDescs = SAFE_ARRAY_NEW(NvBlastExtAssetUtilsBondDesc, newBoundCount); - for (int32_t nb = 0; nb < newBoundCount; ++nb) - { - newBondDescs[nb].bond = newBonds[nb].bond; - for (uint32_t ci = 0; ci < 2; ++ci) - { - uint32_t absChunkIdx = newBonds[nb].chunkIndices[ci]; - uint32_t componentIdx = originalComponentIndex[absChunkIdx]; - newBondDescs[nb].componentIndices[ci] = componentIdx; - newBondDescs[nb].chunkIndices[ci] = absChunkIdx - componentChunkOffsets[componentIdx]; - } - } - //Don't need this anymore - NVBLAST_FREE(newBonds); - - for (CollisionHull* hull : hullsToRelease) - { - hull->release(); - } - - return newBoundCount; -} - - -void NvBlastExtAuthoringUpdateGraphicsMesh(Nv::Blast::FractureTool& fTool, Nv::Blast::AuthoringResult& aResult) -{ - uint32_t chunkCount = fTool.getChunkCount(); - for (uint32_t i = 0; i < chunkCount; ++i) - { - fTool.updateBaseMesh(fTool.getChunkIndex(aResult.assetToFractureChunkIdMap[i]), aResult.geometry + aResult.geometryOffset[i]); - } -} - -void NvBlastExtAuthoringBuildCollisionMeshes(Nv::Blast::AuthoringResult& ares, Nv::Blast::ConvexMeshBuilder& collisionBuilder, - const Nv::Blast::CollisionParams& collisionParam, uint32_t chunksToProcessCount, uint32_t* chunksToProcess) -{ - buildPhysicsChunks(collisionBuilder, ares, collisionParam, chunksToProcessCount, chunksToProcess); -} +// 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) 2016-2018 NVIDIA Corporation. All rights reserved. + +#include "NvBlastExtAuthoring.h" +#include "NvBlastExtAuthoringMeshImpl.h" +#include "NvBlastExtAuthoringMeshCleanerImpl.h" +#include "NvBlastExtAuthoringFractureToolImpl.h" +#include "NvBlastExtAuthoringCollisionBuilderImpl.h" +#include "NvBlastExtAuthoringBondGeneratorImpl.h" +#include "NvBlastExtAuthoringCutoutImpl.h" +#include "NvBlastTypes.h" +#include "NvBlastIndexFns.h" +#include "NvBlast.h" +#include "NvBlastGlobals.h" +#include "NvBlastExtPxAsset.h" +#include "NvBlastExtAssetUtils.h" + +#include +#include + +using namespace Nv::Blast; +using namespace physx; + +#define SAFE_ARRAY_NEW(T, x) ((x) > 0) ? reinterpret_cast(NVBLAST_ALLOC(sizeof(T) * (x))) : nullptr; +#define SAFE_ARRAY_DELETE(x) if (x != nullptr) {NVBLAST_FREE(x); x = nullptr;} + +Mesh* NvBlastExtAuthoringCreateMesh(const PxVec3* position, const PxVec3* normals, const PxVec2* uv, uint32_t verticesCount, const uint32_t* indices, uint32_t indicesCount) +{ + return new MeshImpl(position, normals, uv, verticesCount, indices, indicesCount); +} + +Mesh* NvBlastExtAuthoringCreateMeshFromFacets(const void* vertices, const void* edges, const void* facets, uint32_t verticesCount, uint32_t edgesCount, uint32_t facetsCount) +{ + return new MeshImpl((Vertex*)vertices, (Edge*)edges, (Facet*)facets, verticesCount, edgesCount, facetsCount); +} + +MeshCleaner* NvBlastExtAuthoringCreateMeshCleaner() +{ + return new MeshCleanerImpl; +} + +VoronoiSitesGenerator* NvBlastExtAuthoringCreateVoronoiSitesGenerator(Mesh* mesh, RandomGeneratorBase* rng) +{ + return new VoronoiSitesGeneratorImpl(mesh, rng); +} + +CutoutSet* NvBlastExtAuthoringCreateCutoutSet() +{ + return new CutoutSetImpl(); +} + +void NvBlastExtAuthoringBuildCutoutSet(CutoutSet& cutoutSet, const uint8_t* pixelBuffer, uint32_t bufferWidth, uint32_t bufferHeight, + float segmentationErrorThreshold, float snapThreshold, bool periodic, bool expandGaps) +{ + ::createCutoutSet(*(CutoutSetImpl*)&cutoutSet, pixelBuffer, bufferWidth, bufferHeight, segmentationErrorThreshold, snapThreshold, periodic, expandGaps); +} + +FractureTool* NvBlastExtAuthoringCreateFractureTool() +{ + return new FractureToolImpl; +} + +BlastBondGenerator* NvBlastExtAuthoringCreateBondGenerator(PxCooking* cooking, PxPhysicsInsertionCallback* insertionCallback) +{ + return new BlastBondGeneratorImpl(cooking, insertionCallback); +} + +ConvexMeshBuilder* NvBlastExtAuthoringCreateConvexMeshBuilder(PxCooking* cooking, PxPhysicsInsertionCallback* insertionCallback) +{ + return new ConvexMeshBuilderImpl(cooking, insertionCallback); +} + + +void NvBlastExtAuthoringTransformCollisionHullInPlace(CollisionHull* hull, const physx::PxVec3* scaling, const physx::PxQuat* rotation, const physx::PxVec3* translation) +{ + // Local copies of scaling (S), rotation (R), and translation (T) + physx::PxVec3 S = { 1, 1, 1 }; + physx::PxQuat R = { 0, 0, 0, 1 }; + physx::PxVec3 T = { 0, 0, 0 }; + physx::PxVec3 cofS = { 1, 1, 1 }; + float sgnDetS = 1; + + { + if (rotation) + { + R = *rotation; + } + + if (scaling) + { + S = *scaling; + cofS.x = S.y * S.z; + cofS.y = S.z * S.x; + cofS.z = S.x * S.y; + sgnDetS = (S.x * S.y * S.z < 0) ? -1 : 1; + } + + if (translation) + { + T = *translation; + } + } + + const uint32_t pointCount = hull->pointsCount; + for (uint32_t pi = 0; pi < pointCount; pi++) + { + physx::PxVec3& p = hull->points[pi]; + p = (R.rotate(p.multiply(S)) + T); + } + + const uint32_t planeCount = hull->polygonDataCount; + for (uint32_t pi = 0; pi < planeCount; pi++) + { + float* plane = hull->polygonData[pi].mPlane; + physx::PxPlane pxPlane(plane[0], plane[1], plane[2], plane[3]); + PxVec3 transformedNormal = sgnDetS*R.rotate(pxPlane.n.multiply(cofS)).getNormalized(); + PxVec3 transformedPt = R.rotate(pxPlane.pointInPlane().multiply(S)) + T; + + physx::PxPlane transformedPlane(transformedPt, transformedNormal); + plane[0] = transformedPlane.n[0]; + plane[1] = transformedPlane.n[1]; + plane[2] = transformedPlane.n[2]; + plane[3] = transformedPlane.d; + } +} + + +CollisionHull* NvBlastExtAuthoringTransformCollisionHull(const CollisionHull* hull, const physx::PxVec3* scaling, const physx::PxQuat* rotation, const physx::PxVec3* translation) +{ + CollisionHullImpl* ret = new CollisionHullImpl(*hull); + NvBlastExtAuthoringTransformCollisionHullInPlace(ret, scaling, rotation, translation); + return ret; +} + +void buildPhysicsChunks(ConvexMeshBuilder& collisionBuilder, AuthoringResult& result, const CollisionParams& params, uint32_t chunksToProcessCount = 0, uint32_t* chunksToProcess = nullptr) +{ + uint32_t chunkCount = (uint32_t)result.chunkCount; + if (params.maximumNumberOfHulls == 1) + { + result.collisionHullOffset = SAFE_ARRAY_NEW(uint32_t, chunkCount + 1); + result.collisionHullOffset[0] = 0; + result.collisionHull = SAFE_ARRAY_NEW(CollisionHull*, chunkCount); + result.physicsSubchunks = SAFE_ARRAY_NEW(ExtPxSubchunk, chunkCount); + result.physicsChunks = SAFE_ARRAY_NEW(ExtPxChunk, chunkCount); + for (uint32_t i = 0; i < chunkCount; ++i) + { + std::vector vertices; + for (uint32_t p = result.geometryOffset[i]; p < result.geometryOffset[i + 1]; ++p) + { + Nv::Blast::Triangle& tri = result.geometry[p]; + vertices.push_back(tri.a.p); + vertices.push_back(tri.b.p); + vertices.push_back(tri.c.p); + } + result.collisionHullOffset[i + 1] = result.collisionHullOffset[i] + 1; + result.collisionHull[i] = collisionBuilder.buildCollisionGeometry((uint32_t)vertices.size(), vertices.data()); + result.physicsSubchunks[i].transform = physx::PxTransform(physx::PxIdentity); + result.physicsSubchunks[i].geometry = physx::PxConvexMeshGeometry(collisionBuilder.buildConvexMesh(*result.collisionHull[i])); + + result.physicsChunks[i].isStatic = false; + result.physicsChunks[i].subchunkCount = 1; + result.physicsChunks[i].firstSubchunkIndex = i; + //outPhysicsChunks.get()[i].subchunks = &outPhysicsSubchunks[i]; + } + } + else + { + std::set chunkSet; + for (uint32_t c = 0; c < chunksToProcessCount; c++) + { + chunkSet.insert(chunksToProcess[c]); + } + std::vector > hulls(chunkCount); + int32_t totalHulls = 0; + for (uint32_t i = 0; i < chunkCount; ++i) + { + if (chunkSet.size() > 0 && chunkSet.find(i) == chunkSet.end()) + { + int32_t newHulls = result.collisionHullOffset[i + 1] - result.collisionHullOffset[i]; + int32_t off = result.collisionHullOffset[i]; + for (int32_t subhull = 0; subhull < newHulls; ++subhull) + { + hulls[i].push_back(result.collisionHull[off + subhull]); + } + totalHulls += newHulls; + continue; + } + + CollisionHull** tempHull; + + int32_t newHulls = collisionBuilder.buildMeshConvexDecomposition(result.geometry + result.geometryOffset[i], + result.geometryOffset[i + 1] - result.geometryOffset[i], params, tempHull); + totalHulls += newHulls; + for (int32_t h = 0; h < newHulls; ++h) + { + hulls[i].push_back(tempHull[h]); + } + SAFE_ARRAY_DELETE(tempHull); + } + + SAFE_ARRAY_DELETE(result.collisionHullOffset); + SAFE_ARRAY_DELETE(result.collisionHull); + SAFE_ARRAY_DELETE(result.physicsSubchunks); + SAFE_ARRAY_DELETE(result.physicsChunks); + + result.collisionHullOffset = SAFE_ARRAY_NEW(uint32_t, chunkCount + 1); + result.collisionHullOffset[0] = 0; + result.collisionHull = SAFE_ARRAY_NEW(CollisionHull*, totalHulls); + result.physicsSubchunks = SAFE_ARRAY_NEW(ExtPxSubchunk, totalHulls); + result.physicsChunks = SAFE_ARRAY_NEW(ExtPxChunk, chunkCount); + + int32_t firstSubchunk = 0; + for (uint32_t i = 0; i < chunkCount; ++i) + { + result.collisionHullOffset[i + 1] = result.collisionHullOffset[i] + hulls[i].size(); + int32_t off = result.collisionHullOffset[i]; + for (uint32_t subhull = 0; subhull < hulls[i].size(); ++subhull) + { + result.collisionHull[off + subhull] = hulls[i][subhull]; + result.physicsSubchunks[firstSubchunk + subhull].transform = physx::PxTransform(physx::PxIdentity); + result.physicsSubchunks[firstSubchunk + subhull].geometry = physx::PxConvexMeshGeometry(collisionBuilder.buildConvexMesh(*hulls[i][subhull])); + } + result.physicsChunks[i].isStatic = false; + result.physicsChunks[i].subchunkCount = static_cast(hulls[i].size()); + result.physicsChunks[i].firstSubchunkIndex = firstSubchunk; + firstSubchunk += result.physicsChunks[i].subchunkCount; + } + } +} + + +struct AuthoringResultImpl : public AuthoringResult +{ + void releaseCollisionHulls() override + { + if (collisionHull != nullptr) + { + for (uint32_t ch = 0; ch < collisionHullOffset[chunkCount]; ch++) + { + collisionHull[ch]->release(); + } + SAFE_ARRAY_DELETE(collisionHullOffset); + SAFE_ARRAY_DELETE(collisionHull); + } + } + + void release() override + { + releaseCollisionHulls(); + NVBLAST_FREE(asset); + SAFE_ARRAY_DELETE(assetToFractureChunkIdMap); + SAFE_ARRAY_DELETE(geometryOffset); + SAFE_ARRAY_DELETE(geometry); + SAFE_ARRAY_DELETE(chunkDescs); + SAFE_ARRAY_DELETE(bondDescs); + SAFE_ARRAY_DELETE(physicsChunks); + SAFE_ARRAY_DELETE(physicsSubchunks); + delete this; + } +}; + +AuthoringResult* NvBlastExtAuthoringProcessFracture(FractureTool& fTool, BlastBondGenerator& bondGenerator, ConvexMeshBuilder& collisionBuilder, const CollisionParams& collisionParam, int32_t defaultSupportDepth) +{ + fTool.finalizeFracturing(); + const uint32_t chunkCount = fTool.getChunkCount(); + if (chunkCount == 0) + { + return nullptr; + } + AuthoringResultImpl* ret = new AuthoringResultImpl; + if (ret == nullptr) + { + return nullptr; + } + AuthoringResult& aResult = *ret; + aResult.chunkCount = chunkCount; + + std::shared_ptr isSupport(new bool[chunkCount], [](bool* b) {delete[] b; }); + memset(isSupport.get(), 0, sizeof(bool) * chunkCount); + for (uint32_t i = 0; i < fTool.getChunkCount(); ++i) + { + if (defaultSupportDepth < 0 || fTool.getChunkDepth(fTool.getChunkId(i)) < defaultSupportDepth) + { + isSupport.get()[i] = fTool.getChunkInfo(i).isLeaf; + } + else if (fTool.getChunkDepth(fTool.getChunkId(i)) == defaultSupportDepth) + { + isSupport.get()[i] = true; + } + } + + BondGenerationConfig cnf; + cnf.bondMode = BondGenerationConfig::EXACT; + + const uint32_t bondCount = bondGenerator.buildDescFromInternalFracture(&fTool, isSupport.get(), aResult.bondDescs, aResult.chunkDescs); + aResult.bondCount = bondCount; + if (bondCount == 0) + { + aResult.bondDescs = nullptr; + } + + // order chunks, build map + std::vector chunkReorderInvMap; + { + std::vector chunkReorderMap(chunkCount); + std::vector scratch(chunkCount * sizeof(NvBlastChunkDesc)); + NvBlastEnsureAssetExactSupportCoverage(aResult.chunkDescs, chunkCount, scratch.data(), logLL); + NvBlastBuildAssetDescChunkReorderMap(chunkReorderMap.data(), aResult.chunkDescs, chunkCount, scratch.data(), logLL); + NvBlastApplyAssetDescChunkReorderMapInPlace(aResult.chunkDescs, chunkCount, aResult.bondDescs, bondCount, chunkReorderMap.data(), true, scratch.data(), logLL); + chunkReorderInvMap.resize(chunkReorderMap.size()); + Nv::Blast::invertMap(chunkReorderInvMap.data(), chunkReorderMap.data(), static_cast(chunkReorderMap.size())); + } + + // get result geometry + aResult.geometryOffset = SAFE_ARRAY_NEW(uint32_t, chunkCount + 1); + aResult.assetToFractureChunkIdMap = SAFE_ARRAY_NEW(uint32_t, chunkCount + 1); + aResult.geometryOffset[0] = 0; + std::vector chunkGeometry(chunkCount); + for (uint32_t i = 0; i < chunkCount; ++i) + { + uint32_t chunkIndex = chunkReorderInvMap[i]; + aResult.geometryOffset[i+1] = aResult.geometryOffset[i] + fTool.getBaseMesh(chunkIndex, chunkGeometry[i]); + aResult.assetToFractureChunkIdMap[i] = fTool.getChunkId(chunkIndex); + } + aResult.geometry = SAFE_ARRAY_NEW(Triangle, aResult.geometryOffset[chunkCount]); + for (uint32_t i = 0; i < chunkCount; ++i) + { + uint32_t trianglesCount = aResult.geometryOffset[i + 1] - aResult.geometryOffset[i]; + memcpy(aResult.geometry + aResult.geometryOffset[i], chunkGeometry[i], trianglesCount * sizeof(Nv::Blast::Triangle)); + delete chunkGeometry[i]; + chunkGeometry[i] = nullptr; + } + + float maxX = INT32_MIN; + float maxY = INT32_MIN; + float maxZ = INT32_MIN; + + float minX = INT32_MAX; + float minY = INT32_MAX; + float minZ = INT32_MAX; + + for (uint32_t i = 0; i < bondCount; i++) + { + NvBlastBondDesc& bondDesc = aResult.bondDescs[i]; + + minX = std::min(minX, bondDesc.bond.centroid[0]); + maxX = std::max(maxX, bondDesc.bond.centroid[0]); + + minY = std::min(minY, bondDesc.bond.centroid[1]); + maxY = std::max(maxY, bondDesc.bond.centroid[1]); + + minZ = std::min(minZ, bondDesc.bond.centroid[2]); + maxZ = std::max(maxZ, bondDesc.bond.centroid[2]); + } + + // prepare physics data (convexes) + buildPhysicsChunks(collisionBuilder, aResult, collisionParam); + + // set NvBlastChunk volume from Px geometry + for (uint32_t i = 0; i < chunkCount; i++) + { + float totalVolume = 0.f; + for (uint32_t k = 0; k < aResult.physicsChunks[i].subchunkCount; k++) + { + const auto& subChunk = aResult.physicsSubchunks[aResult.physicsChunks[i].firstSubchunkIndex + k]; + physx::PxVec3 localCenterOfMass; physx::PxMat33 intertia; float mass; + subChunk.geometry.convexMesh->getMassInformation(mass, intertia, localCenterOfMass); + const physx::PxVec3 scale = subChunk.geometry.scale.scale; + mass *= scale.x * scale.y * scale.z; + totalVolume += mass / 1.0f; // unit density + } + + aResult.chunkDescs[i].volume = totalVolume; + } + + // build and serialize ExtPhysicsAsset + NvBlastAssetDesc descriptor; + descriptor.bondCount = bondCount; + descriptor.bondDescs = aResult.bondDescs; + descriptor.chunkCount = chunkCount; + descriptor.chunkDescs = aResult.chunkDescs; + + std::vector scratch(static_cast(NvBlastGetRequiredScratchForCreateAsset(&descriptor, logLL))); + void* mem = NVBLAST_ALLOC(NvBlastGetAssetMemorySize(&descriptor, logLL)); + aResult.asset = NvBlastCreateAsset(mem, &descriptor, scratch.data(), logLL); + + //aResult.asset = std::shared_ptr(asset, [=](NvBlastAsset* asset) + //{ + // NVBLAST_FREE(asset); + //}); + + //std::cout << "Done" << std::endl; + ret->materialCount = 0; + ret->materialNames = nullptr; + return ret; +} + +uint32_t NvBlastExtAuthoringFindAssetConnectingBonds +( + const NvBlastAsset** components, + const physx::PxVec3* scales, + const physx::PxQuat* rotations, + const physx::PxVec3* translations, + const uint32_t** convexHullOffsets, + const CollisionHull*** chunkHulls, + uint32_t componentCount, + NvBlastExtAssetUtilsBondDesc*& newBondDescs, + float maxSeparation +) +{ + //We don't need to use any of the cooking related parts of this + BlastBondGeneratorImpl bondGenerator(nullptr, nullptr); + + std::vector componentChunkOffsets; + componentChunkOffsets.reserve(componentCount + 1); + componentChunkOffsets.push_back(0); + + std::vector combinedConvexHullOffsets; + std::vector combinedConvexHulls; + std::vector hullsToRelease; + combinedConvexHullOffsets.push_back(0); + + std::vector originalComponentIndex; + + const physx::PxVec3 identityScale(1); + + //Combine our hull lists into a single combined list for bondsFromPrefractured + for (uint32_t c = 0; c < componentCount; c++) + { + const uint32_t chunkCount = NvBlastAssetGetChunkCount(components[c], &logLL); + const physx::PxVec3* scale = scales ? scales + c : nullptr; + const physx::PxQuat* rotation = rotations ? rotations + c : nullptr; + const physx::PxVec3* translation = translations ? translations + c : nullptr; + + componentChunkOffsets.push_back(chunkCount + componentChunkOffsets.back()); + for (uint32_t chunk = 0; chunk < chunkCount; chunk++) + { + const uint32_t hullsStart = convexHullOffsets[c][chunk]; + const uint32_t hullsEnd = convexHullOffsets[c][chunk + 1]; + for (uint32_t hull = hullsStart; hull < hullsEnd; hull++) + { + if ((scale != nullptr && *scale != identityScale) || (rotation != nullptr && !rotation->isIdentity()) || (translation != nullptr && !translation->isZero())) + { + hullsToRelease.emplace_back(NvBlastExtAuthoringTransformCollisionHull(chunkHulls[c][hull], scale, rotation, translation)); + combinedConvexHulls.emplace_back(hullsToRelease.back()); + } + else + { + //No need to transform + combinedConvexHulls.emplace_back(chunkHulls[c][hull]); + } + } + combinedConvexHullOffsets.push_back((hullsEnd - hullsStart) + combinedConvexHullOffsets.back()); + originalComponentIndex.push_back(c); + } + } + const uint32_t totalChunkCount = componentChunkOffsets.back(); + //Can't use std::vector since we need a bool* later + std::unique_ptr isSupportChunk(new bool[totalChunkCount]); + for (uint32_t c = 0; c < componentCount; c++) + { + const uint32_t chunkCount = componentChunkOffsets[c + 1] - componentChunkOffsets[c]; + NvBlastSupportGraph supportGraph = NvBlastAssetGetSupportGraph(components[c], &logLL); + for (uint32_t chunk = 0; chunk < chunkCount; chunk++) + { + auto chunkIndiciesEnd = supportGraph.chunkIndices + supportGraph.nodeCount; + isSupportChunk[chunk + componentChunkOffsets[c]] = (std::find(supportGraph.chunkIndices, chunkIndiciesEnd, chunk) != chunkIndiciesEnd); + } + } + + //Find the bonds + NvBlastBondDesc* newBonds = nullptr; + const int32_t newBoundCount = bondGenerator.bondsFromPrefractured(totalChunkCount, combinedConvexHullOffsets.data(), combinedConvexHulls.data(), isSupportChunk.get(), originalComponentIndex.data(), newBonds, maxSeparation); + + //Convert the bonds back to per-component chunks + newBondDescs = SAFE_ARRAY_NEW(NvBlastExtAssetUtilsBondDesc, newBoundCount); + for (int32_t nb = 0; nb < newBoundCount; ++nb) + { + newBondDescs[nb].bond = newBonds[nb].bond; + for (uint32_t ci = 0; ci < 2; ++ci) + { + uint32_t absChunkIdx = newBonds[nb].chunkIndices[ci]; + uint32_t componentIdx = originalComponentIndex[absChunkIdx]; + newBondDescs[nb].componentIndices[ci] = componentIdx; + newBondDescs[nb].chunkIndices[ci] = absChunkIdx - componentChunkOffsets[componentIdx]; + } + } + //Don't need this anymore + NVBLAST_FREE(newBonds); + + for (CollisionHull* hull : hullsToRelease) + { + hull->release(); + } + + return newBoundCount; +} + + +void NvBlastExtAuthoringUpdateGraphicsMesh(Nv::Blast::FractureTool& fTool, Nv::Blast::AuthoringResult& aResult) +{ + uint32_t chunkCount = fTool.getChunkCount(); + for (uint32_t i = 0; i < chunkCount; ++i) + { + fTool.updateBaseMesh(fTool.getChunkIndex(aResult.assetToFractureChunkIdMap[i]), aResult.geometry + aResult.geometryOffset[i]); + } +} + +void NvBlastExtAuthoringBuildCollisionMeshes(Nv::Blast::AuthoringResult& ares, Nv::Blast::ConvexMeshBuilder& collisionBuilder, + const Nv::Blast::CollisionParams& collisionParam, uint32_t chunksToProcessCount, uint32_t* chunksToProcess) +{ + buildPhysicsChunks(collisionBuilder, ares, collisionParam, chunksToProcessCount, chunksToProcess); +} -- cgit v1.2.3