// 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. #include "NvBlastExtAssetUtils.h" #include "NvBlast.h" #include "NvBlastIndexFns.h" #include "NvBlastMemory.h" #include "NvBlastGlobals.h" #include "math.h" using namespace Nv::Blast; /** Fill the chunk and bond descriptors from an asset. \param[out] chunkDescsWritten the number of chunk descriptors written to chunkDescs \param[out] bondDescsWritten the number of bond descriptors written to bondDescs \param[out] chunkDescs user-supplied buffer of NvBlastChunkDesc. Size must be at least NvBlastAssetGetChunkCount(asset, logFn) \param[out] bondDescs user-supplied buffer of NvBlastBondDesc. Size must be at least NvBlastAssetGetBondCount(asset, logFn) \param[in] asset asset from which to extract descriptors */ static void fillChunkAndBondDescriptorsFromAsset ( uint32_t& chunkDescsWritten, uint32_t& bondDescsWritten, NvBlastChunkDesc* chunkDescs, NvBlastBondDesc* bondDescs, const NvBlastAsset* asset ) { chunkDescsWritten = 0; bondDescsWritten = 0; // Chunk descs const uint32_t assetChunkCount = NvBlastAssetGetChunkCount(asset, logLL); const NvBlastChunk* assetChunk = NvBlastAssetGetChunks(asset, logLL); for (uint32_t i = 0; i < assetChunkCount; ++i, ++assetChunk) { NvBlastChunkDesc& chunkDesc = chunkDescs[chunkDescsWritten++]; memcpy(chunkDesc.centroid, assetChunk->centroid, sizeof(float) * 3); chunkDesc.volume = assetChunk->volume; chunkDesc.parentChunkIndex = assetChunk->parentChunkIndex; chunkDesc.flags = 0; // To be filled in below chunkDesc.userData = assetChunk->userData; } // Bond descs const uint32_t assetBondCount = NvBlastAssetGetBondCount(asset, logLL); const NvBlastBond* assetBond = NvBlastAssetGetBonds(asset, logLL); for (uint32_t i = 0; i < assetBondCount; ++i, ++assetBond) { NvBlastBondDesc& bondDesc = bondDescs[bondDescsWritten++]; memcpy(&bondDesc.bond, assetBond, sizeof(NvBlastBond)); } // Walk the graph and restore connection descriptors const NvBlastSupportGraph graph = NvBlastAssetGetSupportGraph(asset, logLL); for (uint32_t i = 0; i < graph.nodeCount; ++i) { const int32_t currentChunk = graph.chunkIndices[i]; if (isInvalidIndex(currentChunk)) { continue; } chunkDescs[currentChunk].flags |= NvBlastChunkDesc::SupportFlag; // Filling in chunk flags here for (uint32_t j = graph.adjacencyPartition[i]; j < graph.adjacencyPartition[i + 1]; ++j) { NvBlastBondDesc& bondDesc = bondDescs[graph.adjacentBondIndices[j]]; bondDesc.chunkIndices[0] = currentChunk; const uint32_t adjacentChunkIndex = graph.chunkIndices[graph.adjacentNodeIndices[j]]; bondDesc.chunkIndices[1] = adjacentChunkIndex; } } } /** Scale a 3-vector v in-place. \param[in,out] v The vector to scale. \param[in] s The scale. Represents the diagonal elements of a diagonal matrix. The result will be v <- s*v. */ static inline void scale(NvcVec3& v, const NvcVec3& s) { v.x *= s.x; v.y *= s.y; v.z *= s.z; } /** Rotate a 3-vector v in-place using a rotation represented by a quaternion q. \param[in,out] v The vector to rotate. \param[in] q The quaternion representation the rotation. The format of q is { x, y, z, w } where (x,y,z) is the vector part and w is the scalar part. The quaternion q MUST be normalized. */ static inline void rotate(NvcVec3& v, const NvcQuat& q) { const float vx = 2.0f * v.x; const float vy = 2.0f * v.y; const float vz = 2.0f * v.z; const float w2 = q.w * q.w - 0.5f; const float dot2 = (q.x * vx + q.y * vy + q.z * vz); v.x = vx * w2 + (q.y * vz - q.z * vy) * q.w + q.x * dot2; v.y = vy * w2 + (q.z * vx - q.x * vz) * q.w + q.y * dot2; v.z = vz * w2 + (q.x * vy - q.y * vx) * q.w + q.z * dot2; } /** Translate a 3-vector v in-place. \param[in,out] v The vector to translate. \param[in] t The translation. The result will be v <- v+t. */ static inline void translate(NvcVec3& v, const NvcVec3& t) { v.x += t.x; v.y += t.y; v.z += t.z; } NvBlastAsset* NvBlastExtAssetUtilsAddWorldBonds ( const NvBlastAsset* asset, const uint32_t* worldBoundChunks, uint32_t worldBoundChunkCount, const NvcVec3* bondDirections, const uint32_t* bondUserData ) { const uint32_t chunkCount = NvBlastAssetGetChunkCount(asset, logLL); const uint32_t oldBondCount = NvBlastAssetGetBondCount(asset, logLL); const uint32_t newBondCount = oldBondCount + worldBoundChunkCount; NvBlastChunkDesc* chunkDescs = static_cast(NVBLAST_ALLOC(chunkCount * sizeof(NvBlastChunkDesc))); NvBlastBondDesc* bondDescs = static_cast(NVBLAST_ALLOC(newBondCount * sizeof(NvBlastBondDesc))); // Create chunk descs uint32_t chunkDescsWritten; uint32_t bondDescsWritten; fillChunkAndBondDescriptorsFromAsset(chunkDescsWritten, bondDescsWritten, chunkDescs, bondDescs, asset); // Add world bonds uint32_t bondCount = oldBondCount; for (uint32_t i = 0; i < worldBoundChunkCount; i++) { NvBlastBondDesc& bondDesc = bondDescs[bondCount++]; const uint32_t chunkIndex = worldBoundChunks[i]; bondDesc.chunkIndices[0] = chunkIndex; bondDesc.chunkIndices[1] = invalidIndex(); memcpy(&bondDesc.bond.normal, bondDirections + i, sizeof(float) * 3); bondDesc.bond.area = 1.0f; // Should be set by user memcpy(&bondDesc.bond.centroid, chunkDescs[chunkIndex].centroid, sizeof(float) * 3); bondDesc.bond.userData = bondUserData != nullptr ? bondUserData[i] : 0; } // Create new asset NvBlastAssetDesc assetDesc; assetDesc.chunkCount = chunkCount; assetDesc.chunkDescs = chunkDescs; assetDesc.bondCount = bondCount; assetDesc.bondDescs = bondDescs; void* scratch = NVBLAST_ALLOC(NvBlastGetRequiredScratchForCreateAsset(&assetDesc, logLL)); NvBlastAsset* newAsset = NvBlastCreateAsset(NVBLAST_ALLOC(NvBlastGetAssetMemorySize(&assetDesc, logLL)), &assetDesc, scratch, logLL); // Free buffers NVBLAST_FREE(scratch); NVBLAST_FREE(bondDescs); NVBLAST_FREE(chunkDescs); return newAsset; } NvBlastAssetDesc NvBlastExtAssetUtilsMergeAssets ( const NvBlastAsset** components, const NvcVec3* scales, const NvcQuat* rotations, const NvcVec3* translations, uint32_t componentCount, const NvBlastExtAssetUtilsBondDesc* newBondDescs, uint32_t newBondCount, uint32_t* chunkIndexOffsets, uint32_t* chunkReorderMap, uint32_t chunkReorderMapSize ) { // Count the total number of chunks and bonds in the new asset uint32_t totalChunkCount = 0; uint32_t totalBondCount = newBondCount; for (uint32_t c = 0; c < componentCount; ++c) { totalChunkCount += NvBlastAssetGetChunkCount(components[c], logLL); totalBondCount += NvBlastAssetGetBondCount(components[c], logLL); } // Allocate space for chunk and bond descriptors NvBlastChunkDesc* chunkDescs = static_cast(NVBLAST_ALLOC(totalChunkCount * sizeof(NvBlastChunkDesc))); NvBlastBondDesc* bondDescs = static_cast(NVBLAST_ALLOC(totalBondCount * sizeof(NvBlastBondDesc))); // Create a list of chunk index offsets per component uint32_t* offsetStackAlloc = static_cast(NvBlastAlloca(componentCount * sizeof(uint32_t))); if (chunkIndexOffsets == nullptr) { chunkIndexOffsets = offsetStackAlloc; // Use local stack alloc if no array is provided } // Fill the chunk and bond descriptors from the components uint32_t chunkCount = 0; uint32_t bondCount = 0; for (uint32_t c = 0; c < componentCount; ++c) { chunkIndexOffsets[c] = chunkCount; uint32_t componentChunkCount; uint32_t componentBondCount; fillChunkAndBondDescriptorsFromAsset(componentChunkCount, componentBondCount, chunkDescs + chunkCount, bondDescs + bondCount, components[c]); // Fix chunks' parent indices for (uint32_t i = 0; i < componentChunkCount; ++i) { if (!isInvalidIndex(chunkDescs[chunkCount + i].parentChunkIndex)) { chunkDescs[chunkCount + i].parentChunkIndex += chunkCount; } } // Fix bonds' chunk indices for (uint32_t i = 0; i < componentBondCount; ++i) { NvBlastBondDesc& bondDesc = bondDescs[bondCount + i]; for (int j = 0; j < 2; ++j) { if (!isInvalidIndex(bondDesc.chunkIndices[j])) { bondDesc.chunkIndices[j] += chunkCount; } } } // Transform geometric data if (scales != nullptr) { const NvcVec3& S = scales[c]; NvcVec3 cofS = { S.y * S.z, S.z * S.x, S.x * S.y }; float absDetS = S.x * S.y * S.z; const float sgnDetS = absDetS < 0.0f ? -1.0f : 1.0f; absDetS *= sgnDetS; for (uint32_t i = 0; i < componentChunkCount; ++i) { scale(reinterpret_cast(chunkDescs[chunkCount + i].centroid), S); chunkDescs[chunkCount + i].volume *= absDetS; } for (uint32_t i = 0; i < componentBondCount; ++i) { NvBlastBond& bond = bondDescs[bondCount + i].bond; scale(reinterpret_cast(bond.normal), cofS); float renorm = sqrtf(bond.normal[0] * bond.normal[0] + bond.normal[1] * bond.normal[1] + bond.normal[2] * bond.normal[2]); bond.area *= renorm; if (renorm != 0) { renorm = sgnDetS / renorm; bond.normal[0] *= renorm; bond.normal[1] *= renorm; bond.normal[2] *= renorm; } scale(reinterpret_cast(bond.centroid), S); } } if (rotations != nullptr) { for (uint32_t i = 0; i < componentChunkCount; ++i) { rotate(reinterpret_cast(chunkDescs[chunkCount + i].centroid), rotations[c]); } for (uint32_t i = 0; i < componentBondCount; ++i) { NvBlastBond& bond = bondDescs[bondCount + i].bond; rotate(reinterpret_cast(bond.normal), rotations[c]); // Normal can be transformed this way since we aren't scaling rotate(reinterpret_cast(bond.centroid), rotations[c]); } } if (translations != nullptr) { for (uint32_t i = 0; i < componentChunkCount; ++i) { translate(reinterpret_cast(chunkDescs[chunkCount + i].centroid), translations[c]); } for (uint32_t i = 0; i < componentBondCount; ++i) { translate(reinterpret_cast(bondDescs[bondCount + i].bond.centroid), translations[c]); } } chunkCount += componentChunkCount; bondCount += componentBondCount; } // Fill the bond descriptors from the new bond descs for (uint32_t b = 0; b < newBondCount; ++b) { const NvBlastExtAssetUtilsBondDesc& newBondDesc = newBondDescs[b]; NvBlastBondDesc& bondDesc = bondDescs[bondCount++]; memcpy(&bondDesc.bond, &newBondDesc.bond, sizeof(NvBlastBond)); bondDesc.chunkIndices[0] = !isInvalidIndex(newBondDesc.chunkIndices[0]) ? newBondDesc.chunkIndices[0] + chunkIndexOffsets[newBondDesc.componentIndices[0]] : invalidIndex(); bondDesc.chunkIndices[1] = !isInvalidIndex(newBondDesc.chunkIndices[1]) ? newBondDesc.chunkIndices[1] + chunkIndexOffsets[newBondDesc.componentIndices[1]] : invalidIndex(); } // Create new asset desriptor NvBlastAssetDesc assetDesc; assetDesc.chunkCount = chunkCount; assetDesc.chunkDescs = chunkDescs; assetDesc.bondCount = bondCount; assetDesc.bondDescs = bondDescs; // Massage the descriptors so that they are valid for scratch creation void* scratch = NVBLAST_ALLOC(chunkCount * sizeof(NvBlastChunkDesc)); // Enough for NvBlastEnsureAssetExactSupportCoverage and NvBlastReorderAssetDescChunks NvBlastEnsureAssetExactSupportCoverage(chunkDescs, chunkCount, scratch, logLL); if (chunkReorderMapSize < chunkCount) { if (chunkReorderMap != nullptr) { // Chunk reorder map is not large enough. Fill it with invalid indices and don't use it. memset(chunkReorderMap, 0xFF, chunkReorderMapSize * sizeof(uint32_t)); NVBLAST_LOG_WARNING("NvBlastExtAssetUtilsMergeAssets: insufficient chunkReorderMap array passed in. NvBlastReorderAssetDescChunks will not be used."); } chunkReorderMap = nullptr; // Don't use } if (chunkReorderMap != nullptr) { NvBlastReorderAssetDescChunks(chunkDescs, chunkCount, bondDescs, bondCount, chunkReorderMap, true, scratch, logLL); } NVBLAST_FREE(scratch); return assetDesc; } /** Multiply a 3-vector v in-place by value. \param[in,out] v The vector to multiply. \param[in] m The 3x3 matrix. */ static inline void multiply(NvcVec3& v, float value) { v.x *= value; v.y *= value; v.z *= value; } /** Get Vec3 length */ static inline float length(const NvcVec3& p) { return sqrtf(p.x * p.x + p.y * p.y + p.z * p.z); } /** Transform a point in-place: scale, rotate, then translate \param[in,out] p The point to transform. \param[in] S The diagonal elements of a diagonal scale matrix. \param[in] R A quaternion representing the rotation. Must be normalized. \param[in] T The translation vector. */ static inline void transform(NvcVec3& p, const NvcVec3& S, const NvcQuat& R, const NvcVec3& T) { scale(p, S); rotate(p, R); translate(p, T); } /** Transform a vector in-place: scale, then rotate \param[in,out] v The vector to transform. \param[in] S The diagonal elements of a diagonal scale matrix. \param[in] R A quaternion representing the rotation. Must be normalized. */ static inline void transform(NvcVec3& v, const NvcVec3& S, const NvcQuat& R) { scale(v, S); rotate(v, R); } void NvBlastExtAssetTransformInPlace(NvBlastAsset* asset, const NvcVec3* scaling, const NvcQuat* rotation, const NvcVec3* translation) { // Local copies of scaling (S), rotation (R), and translation (T) NvcVec3 S = { 1, 1, 1 }; NvcQuat R = { 0, 0, 0, 1 }; NvcVec3 T = { 0, 0, 0 }; NvcVec3 cofS = { 1, 1, 1 }; float absDetS = 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; absDetS = S.x * S.y * S.z; sgnDetS = absDetS < 0.0f ? -1.0f : 1.0f; absDetS *= sgnDetS; } if (translation) { T = *translation; } } // Chunk descs const uint32_t assetChunkCount = NvBlastAssetGetChunkCount(asset, logLL); NvBlastChunk* assetChunk = const_cast(NvBlastAssetGetChunks(asset, logLL)); for (uint32_t i = 0; i < assetChunkCount; ++i, ++assetChunk) { transform(reinterpret_cast(assetChunk->centroid), S, R, T); assetChunk->volume *= absDetS; // Use |detS| to keep the volume positive } // Bond descs const uint32_t assetBondCount = NvBlastAssetGetBondCount(asset, logLL); NvBlastBond* assetBond = const_cast(NvBlastAssetGetBonds(asset, logLL)); for (uint32_t i = 0; i < assetBondCount; ++i, ++assetBond) { transform(reinterpret_cast(assetBond->centroid), S, R, T); NvcVec3& normal = reinterpret_cast(assetBond->normal); transform(normal, cofS, R); const float l = length(normal); assetBond->area *= l; multiply(normal, l > 0.f ? sgnDetS / l : 1.f); } }