diff options
Diffstat (limited to 'NvBlast/test/src/unit/APITests.cpp')
| -rw-r--r-- | NvBlast/test/src/unit/APITests.cpp | 1354 |
1 files changed, 1354 insertions, 0 deletions
diff --git a/NvBlast/test/src/unit/APITests.cpp b/NvBlast/test/src/unit/APITests.cpp new file mode 100644 index 0000000..c2a4a04 --- /dev/null +++ b/NvBlast/test/src/unit/APITests.cpp @@ -0,0 +1,1354 @@ +#include "BlastBaseTest.h" +#include "NvBlastIndexFns.h" +#include "NvBlastExtDamageShaders.h" + +#include <algorithm> + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Utils / Tests Common +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +using namespace Nv::Blast; + +class APITest : public BlastBaseTest < NvBlastMessage::Error, 1 > +{ +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Tests +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +TEST_F(APITest, Basic) +{ + // create asset + const NvBlastAssetDesc& assetDesc = g_assetDescs[0]; + + std::vector<char> scratch((size_t)NvBlastGetRequiredScratchForCreateAsset(&assetDesc, messageLog)); + void* amem = alloc(NvBlastGetAssetMemorySize(&assetDesc, messageLog)); + NvBlastAsset* asset = NvBlastCreateAsset(amem, &assetDesc, scratch.data(), messageLog); + EXPECT_TRUE(asset != nullptr); + + // create actor + NvBlastActorDesc actorDesc; + actorDesc.initialBondHealths = actorDesc.initialSupportChunkHealths = nullptr; + actorDesc.uniformInitialBondHealth = actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f; + void* fmem = alloc(NvBlastAssetGetFamilyMemorySize(asset, messageLog)); + NvBlastFamily* family = NvBlastAssetCreateFamily(fmem, asset, messageLog); + scratch.resize((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, messageLog)); + NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, scratch.data(), messageLog); + EXPECT_TRUE(actor != nullptr); + + NvBlastExtRadialDamageDesc damage[] = { + { + 10.0f, // compressive + { 0.0f, 0.0f, 0.0f }, // position + 4.0f, // min radius - maximum damage + 6.0f // max radius - zero damage + } // linear falloff + }; + + NvBlastBondFractureData outFracture[12]; /*num lower-support chunks + bonds?*/ + + NvBlastFractureBuffers events; + events.bondFractureCount = 12; + events.bondFractures = outFracture; + events.chunkFractureCount = 0; + events.chunkFractures = nullptr; + + NvBlastProgramParams programParams; + programParams.damageDescCount = 1; + programParams.damageDescBuffer = &damage; + + NvBlastDamageProgram program = { + NvBlastExtFalloffGraphShader, + nullptr + }; + + NvBlastActorGenerateFracture(&events, actor, program, &programParams, messageLog, nullptr); + NvBlastActorApplyFracture(&events, actor, &events, messageLog, nullptr); + EXPECT_EQ(12, events.bondFractureCount); + + NvBlastActor* newActors[8]; /* num lower-support chunks? plus space for deletedActor */ + NvBlastActorSplitEvent result; + result.deletedActor = nullptr; + result.newActors = newActors; + scratch.resize((size_t)NvBlastActorGetRequiredScratchForSplit(actor, messageLog)); + size_t newActorsCount = NvBlastActorSplit(&result, actor, 8, scratch.data(), messageLog, nullptr); + EXPECT_EQ(8, newActorsCount); + EXPECT_EQ(true, result.deletedActor == actor); + + for (uint32_t i = 0; i < newActorsCount; ++i) + { + const bool actorReleaseResult = NvBlastActorDeactivate(result.newActors[i], messageLog); + EXPECT_TRUE(actorReleaseResult); + } + free(family); + free(asset); +} + +TEST_F(APITest, DamageBondsCompressive) +{ + const size_t bondsCount = 6; + + const NvBlastChunkDesc c_chunks[8] = + { + // centroid volume parent idx flags ID + { {0.0f, 0.0f, 0.0f}, 0.0f, UINT32_MAX, NvBlastChunkDesc::NoFlags, 0 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 1 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 2 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 3 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 4 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 5 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 6 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 7 } + }; + + const NvBlastBondDesc c_bonds[bondsCount] = + { + { { 1, 2 }, { { -1.0f, 0.0f, 0.0f }, 1.0f, { 1.0f, 2.0f, 0.0f }, 0 } }, + { { 2, 3 }, { { -1.0f, 0.0f, 0.0f }, 1.0f, { -1.0f, 2.0f, 0.0f }, 0 } }, + { { 3, 4 }, { { 0.0f, -1.0f, 0.0f }, 1.0f, { -2.0f, 1.0f, 0.0f }, 0 } }, + { { 4, 5 }, { { 0.0f, -1.0f, 0.0f }, 1.0f, { -2.0f, -1.0f, 0.0f }, 0 } }, + { { 5, 6 }, { { 1.0f, 0.0f, 0.0f }, 1.0f, { -1.0f, -2.0f, 0.0f }, 0 } }, + { { 6, 7 }, { { 1.0f, 0.0f, 0.0f }, 1.0f, { 1.0f, -2.0f, 0.0f }, 0 } } + }; + + // create asset + const NvBlastAssetDesc assetDesc = { 8, c_chunks, bondsCount, c_bonds }; + + std::vector<char> scratch((size_t)NvBlastGetRequiredScratchForCreateAsset(&assetDesc, messageLog)); + void* amem = alloc(NvBlastGetAssetMemorySize(&assetDesc, messageLog)); + NvBlastAsset* asset = NvBlastCreateAsset(amem, &assetDesc, scratch.data(), messageLog); + EXPECT_TRUE(asset != nullptr); + + // create actor + NvBlastActorDesc actorDesc; + actorDesc.initialBondHealths = actorDesc.initialSupportChunkHealths = nullptr; + actorDesc.uniformInitialBondHealth = actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f; + void* fmem = alloc(NvBlastAssetGetFamilyMemorySize(asset, messageLog)); + NvBlastFamily* family = NvBlastAssetCreateFamily(fmem, asset, messageLog); + scratch.resize((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, messageLog)); + NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, scratch.data(), messageLog); + EXPECT_TRUE(actor != nullptr); + + // get graph nodes check + std::vector<uint32_t> graphNodeIndices; + graphNodeIndices.resize(NvBlastActorGetGraphNodeCount(actor, nullptr)); + uint32_t graphNodesCount = NvBlastActorGetGraphNodeIndices(graphNodeIndices.data(), (uint32_t)graphNodeIndices.size(), actor, nullptr); + EXPECT_EQ(graphNodesCount, 7); + + NvBlastExtRadialDamageDesc damage = { + 1.0f, // compressive + { 4.0f, 2.0f, 0.0f }, // position + 4.0f, // min radius - maximum damage + 6.0f // max radius - zero damage + }; // linear falloff + + NvBlastBondFractureData outCommands[bondsCount] = { + { UINT32_MAX, UINT32_MAX, UINT32_MAX, 0 }, + { UINT32_MAX, UINT32_MAX, UINT32_MAX, 0 }, + { UINT32_MAX, UINT32_MAX, UINT32_MAX, 0 }, + { UINT32_MAX, UINT32_MAX, UINT32_MAX, 0 }, + { UINT32_MAX, UINT32_MAX, UINT32_MAX, 0 }, + { UINT32_MAX, UINT32_MAX, UINT32_MAX, 0 }, + }; + + NvBlastFractureBuffers commands = { + 6, 0, outCommands, nullptr + }; + + NvBlastProgramParams programParams; + programParams.damageDescCount = 1; + programParams.damageDescBuffer = &damage; + + NvBlastDamageProgram program = { + NvBlastExtFalloffGraphShader, + nullptr + }; + + NvBlastActorGenerateFracture(&commands, actor, program, &programParams, messageLog, nullptr); + + ASSERT_EQ(3, commands.bondFractureCount); + ASSERT_EQ(0, commands.chunkFractureCount); + + // node indices in _graph_ chunks + NvBlastBondFractureData expectedCommand[] = { + { 0, 0, 1, 1.0f }, + { 0, 1, 2, 0.5f }, + { 0, 5, 6, 0.5f } + }; + + for (int i = 0; i < 3; i++) + { + EXPECT_EQ(expectedCommand[i].nodeIndex0, outCommands[i].nodeIndex0); + EXPECT_EQ(expectedCommand[i].nodeIndex1, outCommands[i].nodeIndex1); + EXPECT_EQ(expectedCommand[i].health, outCommands[i].health); + } + + const bool actorReleaseResult = NvBlastActorDeactivate(actor, messageLog); + EXPECT_TRUE(actorReleaseResult); + free(family); + free(asset); +} + +TEST_F(APITest, DirectFractureKillsChunk) +{ + // 1--2 + // | | + // 3--4 <-- kill 4 + + const NvBlastChunkDesc c_chunks[9] = + { + // centroid volume parent idx flags ID + { {0.0f, 0.0f, 0.0f}, 0.0f, UINT32_MAX, NvBlastChunkDesc::NoFlags, 0 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 1 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 2 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 3 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 4 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 4, NvBlastChunkDesc::NoFlags, 5 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 4, NvBlastChunkDesc::NoFlags, 6 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 4, NvBlastChunkDesc::NoFlags, 7 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 4, NvBlastChunkDesc::NoFlags, 8 }, + }; + + const NvBlastBondDesc c_bonds[4] = + { + { { 1, 2 }, { { 1.0f, 0.0f, 0.0f }, 1.0f, { 0.0f, +1.0f, 0.0f }, 0 } }, + { { 3, 4 }, { { 1.0f, 0.0f, 0.0f }, 1.0f, { 0.0f, -1.0f, 0.0f }, 0 } }, + { { 1, 3 }, { { 0.0f, -1.0f, 0.0f }, 1.0f, { -1.0f, 0.0f, 0.0f }, 0 } }, + { { 2, 4 }, { { 0.0f, -1.0f, 0.0f }, 1.0f, { +1.0f, 0.0f, 0.0f }, 0 } }, + }; + + NvBlastAssetDesc assetDesc; + assetDesc.chunkCount = 9; + assetDesc.chunkDescs = c_chunks; + assetDesc.bondCount = 4; + assetDesc.bondDescs = c_bonds; + + // create asset + std::vector<char> scratch((size_t)NvBlastGetRequiredScratchForCreateAsset(&assetDesc, messageLog)); + void* amem = alloc(NvBlastGetAssetMemorySize(&assetDesc, messageLog)); + NvBlastAsset* asset = NvBlastCreateAsset(amem, &assetDesc, scratch.data(), messageLog); + EXPECT_TRUE(asset != nullptr); + + // create actor + NvBlastActorDesc actorDesc; + actorDesc.initialBondHealths = actorDesc.initialSupportChunkHealths = nullptr; + actorDesc.uniformInitialBondHealth = actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f; + void* fmem = alloc(NvBlastAssetGetFamilyMemorySize(asset, messageLog)); + NvBlastFamily* family = NvBlastAssetCreateFamily(fmem, asset, messageLog); + scratch.resize((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, messageLog)); + NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, scratch.data(), messageLog); + EXPECT_TRUE(actor != nullptr); + + NvBlastChunkFractureData fractureCmd; + fractureCmd.chunkIndex = 4; + fractureCmd.health = 1.0f; + + NvBlastFractureBuffers commands = { 0, 1, nullptr, &fractureCmd }; + + NvBlastChunkFractureData fractureEvt; + NvBlastFractureBuffers events = { 0, 1, nullptr, &fractureEvt }; + + NvBlastActorApplyFracture(&events, actor, &commands, messageLog, nullptr); + EXPECT_EQ(1, events.chunkFractureCount); + + scratch.resize((size_t)NvBlastActorGetRequiredScratchForSplit(actor, messageLog)); + std::vector<NvBlastActor*> newActors(NvBlastActorGetMaxActorCountForSplit(actor, messageLog)); + NvBlastActorSplitEvent result; + result.deletedActor = nullptr; + result.newActors = newActors.data(); + size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast<uint32_t>(newActors.size()), scratch.data(), messageLog, nullptr); + newActors.resize(newActorsCount); + + EXPECT_EQ(5, newActorsCount); + EXPECT_EQ(actor, result.deletedActor); + // check newActors contain original actor + EXPECT_TRUE(std::any_of(newActors.begin(), newActors.end(), [&](const NvBlastActor* a) { return actor == a; })); + + for (uint32_t i = 0; i < newActorsCount; ++i) + { + const bool actorReleaseResult = NvBlastActorDeactivate(result.newActors[i], messageLog); + EXPECT_TRUE(actorReleaseResult); + } + free(family); + free(asset); +} + +TEST_F(APITest, DirectFractureKillsIslandRootChunk) +{ + // 1--2 <-- kill 1 + // | | + // 3--4 + + const NvBlastChunkDesc c_chunks[9] = + { + // centroid volume parent idx flags ID + { {0.0f, 0.0f, 0.0f}, 0.0f, UINT32_MAX, NvBlastChunkDesc::NoFlags, 0 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 1 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 2 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 3 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 4 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 1, NvBlastChunkDesc::NoFlags, 5 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 1, NvBlastChunkDesc::NoFlags, 6 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 1, NvBlastChunkDesc::NoFlags, 7 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 1, NvBlastChunkDesc::NoFlags, 8 }, + }; + + const NvBlastBondDesc c_bonds[4] = + { + { { 1, 2 }, { { 1.0f, 0.0f, 0.0f }, 1.0f, { 0.0f, +1.0f, 0.0f }, 0 } }, + { { 3, 4 }, { { 1.0f, 0.0f, 0.0f }, 1.0f, { 0.0f, -1.0f, 0.0f }, 0 } }, + { { 1, 3 }, { { 0.0f, -1.0f, 0.0f }, 1.0f, { -1.0f, 0.0f, 0.0f }, 0 } }, + { { 2, 4 }, { { 0.0f, -1.0f, 0.0f }, 1.0f, { +1.0f, 0.0f, 0.0f }, 0 } }, + }; + + NvBlastAssetDesc assetDesc; + assetDesc.chunkCount = 9; + assetDesc.chunkDescs = c_chunks; + assetDesc.bondCount = 4; + assetDesc.bondDescs = c_bonds; + + // create asset + std::vector<char> scratch((size_t)NvBlastGetRequiredScratchForCreateAsset(&assetDesc, messageLog)); + void* amem = alloc(NvBlastGetAssetMemorySize(&assetDesc, messageLog)); + NvBlastAsset* asset = NvBlastCreateAsset(amem, &assetDesc, scratch.data(), messageLog); + EXPECT_TRUE(asset != nullptr); + + // create actor + NvBlastActorDesc actorDesc; + actorDesc.initialBondHealths = actorDesc.initialSupportChunkHealths = nullptr; + actorDesc.uniformInitialBondHealth = actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f; + void* fmem = alloc(NvBlastAssetGetFamilyMemorySize(asset, messageLog)); + NvBlastFamily* family = NvBlastAssetCreateFamily(fmem, asset, messageLog); + scratch.resize((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, messageLog)); + NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, scratch.data(), messageLog); + EXPECT_TRUE(actor != nullptr); + + NvBlastChunkFractureData fractureCmd; + fractureCmd.chunkIndex = 1; + fractureCmd.health = 1.0f; + + NvBlastFractureBuffers commands = { 0, 1, nullptr, &fractureCmd }; + + NvBlastChunkFractureData fractureEvt; + NvBlastFractureBuffers events = { 0, 1, nullptr, &fractureEvt }; + + NvBlastActorApplyFracture(&events, actor, &commands, messageLog, nullptr); + EXPECT_EQ(1, events.chunkFractureCount); + + scratch.resize((size_t)NvBlastActorGetRequiredScratchForSplit(actor, messageLog)); + std::vector<NvBlastActor*> newActors(NvBlastActorGetMaxActorCountForSplit(actor, messageLog)); + NvBlastActorSplitEvent result; + result.deletedActor = nullptr; + result.newActors = newActors.data(); + size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast<uint32_t>(newActors.size()), scratch.data(), messageLog, nullptr); + newActors.resize(newActorsCount); + + EXPECT_EQ(5, newActorsCount); + EXPECT_EQ(actor, result.deletedActor); + // check if newActors don't contain original actor + EXPECT_TRUE(!std::any_of(newActors.begin(), newActors.end(), [&](const NvBlastActor* a) { return actor == a; })); + + for (uint32_t i = 0; i < newActorsCount; ++i) + { + const bool actorReleaseResult = NvBlastActorDeactivate(result.newActors[i], messageLog); + EXPECT_TRUE(actorReleaseResult); + } + free(family); + free(asset); +} + +TEST_F(APITest, SubsupportFracture) +{ + const NvBlastAssetDesc& assetDesc = g_assetDescs[1]; // cube with subsupport + + // create asset with chunk map + std::vector<char> scratch((size_t)NvBlastGetRequiredScratchForCreateAsset(&assetDesc, messageLog)); + void* amem = alloc(NvBlastGetAssetMemorySize(&assetDesc, messageLog)); + NvBlastAsset* asset = NvBlastCreateAsset(amem, &assetDesc, scratch.data(), messageLog); + EXPECT_TRUE(asset != nullptr); + + // create actor + NvBlastActorDesc actorDesc; + actorDesc.initialBondHealths = actorDesc.initialSupportChunkHealths = nullptr; + actorDesc.uniformInitialBondHealth = actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f; + void* fmem = alloc(NvBlastAssetGetFamilyMemorySize(asset, messageLog)); + NvBlastFamily* family = NvBlastAssetCreateFamily(fmem, asset, messageLog); + scratch.resize((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, messageLog)); + NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, scratch.data(), messageLog); + EXPECT_TRUE(actor != nullptr); + + // first set of fracture commands + NvBlastChunkFractureData f1 = { 0, 1, 2.0f }; + NvBlastChunkFractureData f3 = { 0, 3, 0.5f }; + NvBlastChunkFractureData f5 = { 0, 5, 1.0f }; + NvBlastChunkFractureData f7 = { 0, 7, 1.0f }; + + std::vector<NvBlastChunkFractureData> chunkFractureData; + chunkFractureData.reserve(assetDesc.chunkCount); + chunkFractureData.push_back(f1); + chunkFractureData.push_back(f3); + chunkFractureData.push_back(f5); + chunkFractureData.push_back(f7); + ASSERT_EQ(assetDesc.chunkCount, chunkFractureData.capacity()); + ASSERT_EQ(4, chunkFractureData.size()); + + NvBlastFractureBuffers target = { 0, static_cast<uint32_t>(chunkFractureData.capacity()), nullptr, chunkFractureData.data() }; + { + NvBlastFractureBuffers events = target; + NvBlastFractureBuffers commands = { 0, static_cast<uint32_t>(chunkFractureData.size()), nullptr, chunkFractureData.data() }; + NvBlastActorApplyFracture(&events, actor, &commands, messageLog, nullptr); + ASSERT_EQ(4 + 8, events.chunkFractureCount); // all requested chunks take damage, and the children of one of them + } + + // re-apply same set of commands + chunkFractureData.clear(); + chunkFractureData.reserve(assetDesc.chunkCount); + chunkFractureData.push_back(f1); + chunkFractureData.push_back(f3); + chunkFractureData.push_back(f5); + chunkFractureData.push_back(f7); + ASSERT_EQ(assetDesc.chunkCount, chunkFractureData.capacity()); + ASSERT_EQ(4, chunkFractureData.size()); + + { + NvBlastFractureBuffers events = target; + NvBlastFractureBuffers commands = { 0, static_cast<uint32_t>(chunkFractureData.size()), nullptr, chunkFractureData.data() }; + NvBlastActorApplyFracture(&events, actor, &commands, messageLog, nullptr); + ASSERT_EQ(1, events.chunkFractureCount); // f3 has broken the chunk + } + + // fracture all support chunks + // the chunks from the previous fractures must not be reported again (since they are all broken already) + NvBlastChunkFractureData f2 = { 0, 2, 2.0f }; // will damage chunk and children + NvBlastChunkFractureData f4 = { 0, 4, 0.5f }; // will damage chunk without creating children on split + NvBlastChunkFractureData f6 = { 0, 6, 2.0f }; // will damage chunk and children + NvBlastChunkFractureData f8 = { 0, 8, 1.0f }; // will damage chunk + + chunkFractureData.clear(); + chunkFractureData.reserve(assetDesc.chunkCount); + chunkFractureData.push_back(f1); + chunkFractureData.push_back(f2); + chunkFractureData.push_back(f3); + chunkFractureData.push_back(f4); + chunkFractureData.push_back(f5); + chunkFractureData.push_back(f6); + chunkFractureData.push_back(f7); + chunkFractureData.push_back(f8); + ASSERT_EQ(assetDesc.chunkCount, chunkFractureData.capacity()); + ASSERT_EQ(8, chunkFractureData.size()); + + NvBlastFractureBuffers events = target; + { + NvBlastFractureBuffers commands = { 0, static_cast<uint32_t>(chunkFractureData.size()), nullptr, chunkFractureData.data() }; + NvBlastActorApplyFracture(&events, actor, &commands, messageLog, nullptr); + ASSERT_EQ(4 + 8 + 8, events.chunkFractureCount); // the new fracture commands all apply, plus two of them damage their children too + } + + for (size_t i = 0; i < events.chunkFractureCount; i++) + { + const uint32_t chunkIndex = events.chunkFractures[i].chunkIndex; + + ASSERT_TRUE(chunkIndex != 1); + ASSERT_TRUE(chunkIndex != 3); + ASSERT_TRUE(chunkIndex != 5); + ASSERT_TRUE(chunkIndex != 7); + + // literal values come from g_cube2ChunkDescs + bool isInSupportRange = chunkIndex <= 8 && chunkIndex >= 1; + bool isChildOfTwo = chunkIndex <= 24 && chunkIndex >= 17; + bool isChildOfSix = chunkIndex <= 56 && chunkIndex >= 49; + + ASSERT_TRUE(isInSupportRange || isChildOfTwo || isChildOfSix); + } + + scratch.resize((size_t)NvBlastActorGetRequiredScratchForSplit(actor, messageLog)); + std::vector<NvBlastActor*> newActors(NvBlastActorGetMaxActorCountForSplit(actor, messageLog)); + NvBlastActorSplitEvent result; + result.deletedActor = nullptr; + result.newActors = newActors.data(); + size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast<uint32_t>(newActors.size()), scratch.data(), messageLog, nullptr); + newActors.resize(newActorsCount); + + EXPECT_EQ(64 - 8 + 1, newActorsCount); + for (uint32_t i = 0; i < newActorsCount; ++i) + { + const bool actorReleaseResult = NvBlastActorDeactivate(result.newActors[i], messageLog); + EXPECT_TRUE(actorReleaseResult); + } + free(family); + free(asset); +} + +static bool hasWarned = false; +static void myLog(int type, const char* msg, const char* file, int line) +{ + BlastBaseTest<-1, 0>::messageLog(type, msg, file, line); + hasWarned = true; +} +#define EXPECT_WARNING EXPECT_TRUE(hasWarned); hasWarned=false; +#define EXPECT_NO_WARNING EXPECT_FALSE(hasWarned); hasWarned=false; + +TEST_F(APITest, FractureNoEvents) +{ + static const uint32_t GUARD = 0xb1a57; + + const uint32_t chunksCount = 17; + const NvBlastChunkDesc c_chunks[chunksCount] = + { + // centroid volume parent idx flags ID + { {0.0f, 0.0f, 0.0f}, 0.0f, UINT32_MAX, NvBlastChunkDesc::NoFlags, 0 }, + + { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 1 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 2 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 3 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 4 }, + + { {0.0f, 0.0f, 0.0f}, 0.0f, 1, NvBlastChunkDesc::NoFlags, 5 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 1, NvBlastChunkDesc::NoFlags, 6 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 2, NvBlastChunkDesc::NoFlags, 7 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 2, NvBlastChunkDesc::NoFlags, 8 }, + + { {0.0f, 0.0f, 0.0f}, 0.0f, 5, NvBlastChunkDesc::NoFlags, 9 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 5, NvBlastChunkDesc::NoFlags, 10 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 6, NvBlastChunkDesc::NoFlags, 11 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 6, NvBlastChunkDesc::NoFlags, 12 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 7, NvBlastChunkDesc::NoFlags, 13 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 7, NvBlastChunkDesc::NoFlags, 14 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 8, NvBlastChunkDesc::NoFlags, 15 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 8, NvBlastChunkDesc::NoFlags, 16 }, + }; + + const NvBlastBondDesc c_bonds[3] = + { + // chunks, normal, area, centroid, userdata + { { 1, 2 }, { { 1.0f, 0.0f, 0.0f }, 1.0f, { 1.0f, 0.0f, 0.0f }, 0 } }, + { { 2, 3 }, { { 1.0f, 0.0f, 0.0f }, 1.0f, { 2.0f, 0.0f, 0.0f }, 0 } }, + { { 3, 4 }, { { 1.0f, 0.0f, 0.0f }, 1.0f, { 3.0f, 0.0f, 0.0f }, 0 } }, + }; + + NvBlastAssetDesc assetDesc = { chunksCount, c_chunks, 3, c_bonds }; + + // create asset with chunk map + std::vector<char> scratch((size_t)NvBlastGetRequiredScratchForCreateAsset(&assetDesc, myLog)); + void* amem = alloc(NvBlastGetAssetMemorySize(&assetDesc, myLog)); + NvBlastAsset* asset = NvBlastCreateAsset(amem, &assetDesc, scratch.data(), myLog); + EXPECT_TRUE(asset != nullptr); + + // create actor + NvBlastActorDesc actorDesc; + actorDesc.initialBondHealths = actorDesc.initialSupportChunkHealths = nullptr; + actorDesc.uniformInitialBondHealth = actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f; + void* fmem = alloc(NvBlastAssetGetFamilyMemorySize(asset, myLog)); + NvBlastFamily* family = NvBlastAssetCreateFamily(fmem, asset, myLog); + scratch.resize((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, myLog)); + NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, scratch.data(), myLog); + EXPECT_TRUE(actor != nullptr); + + std::vector<NvBlastChunkFractureData> cfData; + cfData.resize(0 + 1); + cfData[cfData.size() - 1].userdata = GUARD; + std::vector<NvBlastBondFractureData> bfData; + + NvBlastChunkFractureData command[] = + { + { 0, 1, 10.0f }, + { 0, 2, 10.0f }, + }; + + NvBlastFractureBuffers commands = { 0, 2, nullptr, command }; + NvBlastActorApplyFracture(nullptr, actor, &commands, myLog, nullptr); + + EXPECT_NO_WARNING; // events can be null + EXPECT_EQ(GUARD, cfData[cfData.size() - 1].userdata); + + scratch.resize((size_t)NvBlastActorGetRequiredScratchForSplit(actor, myLog)); + std::vector<NvBlastActor*> newActors(NvBlastActorGetMaxActorCountForSplit(actor, myLog)); + NvBlastActorSplitEvent result; + result.deletedActor = nullptr; + result.newActors = newActors.data(); + size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast<uint32_t>(newActors.size()), scratch.data(), myLog, nullptr); + newActors.resize(newActorsCount); + + EXPECT_EQ(9, newActorsCount); + for (uint32_t i = 0; i < newActorsCount; ++i) + { + const bool actorReleaseResult = NvBlastActorDeactivate(result.newActors[i], myLog); + EXPECT_TRUE(actorReleaseResult); + } + + free(family); + free(asset); + + EXPECT_NO_WARNING; +} + +TEST_F(APITest, FractureBufferLimits) +{ + static const uint32_t GUARD = 0xb1a57; + + const uint32_t chunksCount = 17; + const NvBlastChunkDesc c_chunks[chunksCount] = + { + // centroid volume parent idx flags ID + { {0.0f, 0.0f, 0.0f}, 0.0f, UINT32_MAX, NvBlastChunkDesc::NoFlags, 0 }, + + { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 1 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 2 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 3 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 4 }, + + { {0.0f, 0.0f, 0.0f}, 0.0f, 1, NvBlastChunkDesc::NoFlags, 5 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 1, NvBlastChunkDesc::NoFlags, 6 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 2, NvBlastChunkDesc::NoFlags, 7 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 2, NvBlastChunkDesc::NoFlags, 8 }, + + { {0.0f, 0.0f, 0.0f}, 0.0f, 5, NvBlastChunkDesc::NoFlags, 9 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 5, NvBlastChunkDesc::NoFlags, 10 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 6, NvBlastChunkDesc::NoFlags, 11 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 6, NvBlastChunkDesc::NoFlags, 12 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 7, NvBlastChunkDesc::NoFlags, 13 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 7, NvBlastChunkDesc::NoFlags, 14 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 8, NvBlastChunkDesc::NoFlags, 15 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 8, NvBlastChunkDesc::NoFlags, 16 }, + }; + + const NvBlastBondDesc c_bonds[3] = + { + // chunks, normal, area, centroid, userdata + { { 1, 2 }, { { 1.0f, 0.0f, 0.0f }, 1.0f, { 1.0f, 0.0f, 0.0f }, 0 } }, + { { 2, 3 }, { { 1.0f, 0.0f, 0.0f }, 1.0f, { 2.0f, 0.0f, 0.0f }, 0 } }, + { { 3, 4 }, { { 1.0f, 0.0f, 0.0f }, 1.0f, { 3.0f, 0.0f, 0.0f }, 0 } }, + }; + + NvBlastAssetDesc assetDesc = { chunksCount, c_chunks, 3, c_bonds }; + { + // create asset with chunk map + std::vector<char> scratch((size_t)NvBlastGetRequiredScratchForCreateAsset(&assetDesc, myLog)); + void* amem = alloc(NvBlastGetAssetMemorySize(&assetDesc, myLog)); + NvBlastAsset* asset = NvBlastCreateAsset(amem, &assetDesc, scratch.data(), myLog); + EXPECT_TRUE(asset != nullptr); + + for (uint32_t i = 0; i < 14; i++) + { + // create actor + NvBlastActorDesc actorDesc; + actorDesc.initialBondHealths = actorDesc.initialSupportChunkHealths = nullptr; + actorDesc.uniformInitialBondHealth = actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f; + void* fmem = alloc(NvBlastAssetGetFamilyMemorySize(asset, myLog)); + NvBlastFamily* family = NvBlastAssetCreateFamily(fmem, asset, myLog); + scratch.resize((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, myLog)); + NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, scratch.data(), myLog); + EXPECT_TRUE(actor != nullptr); + + std::vector<NvBlastChunkFractureData> cfData; + cfData.resize(i + 1); + cfData[cfData.size() - 1].userdata = GUARD; + std::vector<NvBlastBondFractureData> bfData; + + NvBlastChunkFractureData command[] = + { + { 0, 1, 10.0f }, + { 0, 2, 10.0f }, + }; + + NvBlastFractureBuffers commands = { 0, 2, nullptr, command }; + NvBlastFractureBuffers events = { static_cast<uint32_t>(bfData.size()), static_cast<uint32_t>(cfData.size()) - 1, bfData.data(), cfData.data() }; + + NvBlastActorApplyFracture(&events, actor, &commands, myLog, nullptr); + + EXPECT_WARNING; + EXPECT_EQ(GUARD, cfData[cfData.size() - 1].userdata); + EXPECT_EQ(i, events.chunkFractureCount); + for (uint32_t i = 0; i < events.chunkFractureCount; i++) + { + EXPECT_EQ(events.chunkFractures[i].chunkIndex, events.chunkFractures[i].userdata); + } + + EXPECT_TRUE(NvBlastActorDeactivate(actor, myLog)); + free(family); + } + + { + // create actor + NvBlastActorDesc actorDesc; + actorDesc.initialBondHealths = actorDesc.initialSupportChunkHealths = nullptr; + actorDesc.uniformInitialBondHealth = actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f; + void* fmem = alloc(NvBlastAssetGetFamilyMemorySize(asset, myLog)); + NvBlastFamily* family = NvBlastAssetCreateFamily(fmem, asset, myLog); + scratch.resize((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, myLog)); + NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, scratch.data(), myLog); + EXPECT_TRUE(actor != nullptr); + + std::vector<NvBlastChunkFractureData> cfData; + cfData.resize(14 + 1); + cfData[cfData.size() - 1].userdata = GUARD; + std::vector<NvBlastBondFractureData> bfData; + + NvBlastChunkFractureData command[] = + { + { 0, 1, 10.0f }, + { 0, 2, 10.0f }, + }; + + NvBlastFractureBuffers commands = { 0, 2, nullptr, command }; + NvBlastFractureBuffers events = { static_cast<uint32_t>(bfData.size()), static_cast<uint32_t>(cfData.size()) - 1, bfData.data(), cfData.data() }; + + NvBlastActorApplyFracture(&events, actor, &commands, myLog, nullptr); + + EXPECT_NO_WARNING; + EXPECT_EQ(14, events.chunkFractureCount); + for (uint32_t i = 0; i < events.chunkFractureCount; i++) + { + EXPECT_EQ(events.chunkFractures[i].chunkIndex, events.chunkFractures[i].userdata); + } + ASSERT_EQ(GUARD, cfData[cfData.size() - 1].userdata); + + scratch.resize((size_t)NvBlastActorGetRequiredScratchForSplit(actor, myLog)); + std::vector<NvBlastActor*> newActors(NvBlastActorGetMaxActorCountForSplit(actor, myLog)); + NvBlastActorSplitEvent result; + result.deletedActor = nullptr; + result.newActors = newActors.data(); + size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast<uint32_t>(newActors.size()), scratch.data(), myLog, nullptr); + newActors.resize(newActorsCount); + + EXPECT_EQ(9, newActorsCount); + for (uint32_t i = 0; i < newActorsCount; ++i) + { + const bool actorReleaseResult = NvBlastActorDeactivate(result.newActors[i], myLog); + EXPECT_TRUE(actorReleaseResult); + } + + free(family); + } + + free(asset); + } + EXPECT_NO_WARNING; +} + +TEST_F(APITest, FractureBufferLimitsInSitu) +{ + static const uint32_t GUARD = 0xb1a57; + + const uint32_t chunksCount = 17; + const NvBlastChunkDesc c_chunks[chunksCount] = + { + // cenroid volume parent idx flags ID + { {0.0f, 0.0f, 0.0f}, 0.0f, UINT32_MAX, NvBlastChunkDesc::NoFlags, 0 }, + + { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 1 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 2 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 3 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 4 }, + + { {0.0f, 0.0f, 0.0f}, 0.0f, 1, NvBlastChunkDesc::NoFlags, 5 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 1, NvBlastChunkDesc::NoFlags, 6 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 2, NvBlastChunkDesc::NoFlags, 7 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 2, NvBlastChunkDesc::NoFlags, 8 }, + + { {0.0f, 0.0f, 0.0f}, 0.0f, 5, NvBlastChunkDesc::NoFlags, 9 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 5, NvBlastChunkDesc::NoFlags, 10 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 6, NvBlastChunkDesc::NoFlags, 11 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 6, NvBlastChunkDesc::NoFlags, 12 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 7, NvBlastChunkDesc::NoFlags, 13 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 7, NvBlastChunkDesc::NoFlags, 14 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 8, NvBlastChunkDesc::NoFlags, 15 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 8, NvBlastChunkDesc::NoFlags, 16 }, + }; + + const NvBlastBondDesc c_bonds[3] = + { + // chunks, normal, area, centroid, userdata + { { 1, 2 }, { { 1.0f, 0.0f, 0.0f }, 1.0f, { 1.0f, 0.0f, 0.0f }, 0 } }, + { { 2, 3 }, { { 1.0f, 0.0f, 0.0f }, 1.0f, { 2.0f, 0.0f, 0.0f }, 0 } }, + { { 3, 4 }, { { 1.0f, 0.0f, 0.0f }, 1.0f, { 3.0f, 0.0f, 0.0f }, 0 } }, + }; + + NvBlastAssetDesc assetDesc = { chunksCount, c_chunks, 3, c_bonds }; + { + // create asset with chunk map + std::vector<char> scratch((size_t)NvBlastGetRequiredScratchForCreateAsset(&assetDesc, myLog)); + void* amem = alloc(NvBlastGetAssetMemorySize(&assetDesc, myLog)); + NvBlastAsset* asset = NvBlastCreateAsset(amem, &assetDesc, scratch.data(), myLog); + EXPECT_TRUE(asset != nullptr); + + for (uint32_t i = 0; i < 14 - 2; i++) + { + // create actor + NvBlastActorDesc actorDesc; + actorDesc.initialBondHealths = actorDesc.initialSupportChunkHealths = nullptr; + actorDesc.uniformInitialBondHealth = actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f; + void* fmem = alloc(NvBlastAssetGetFamilyMemorySize(asset, myLog)); + NvBlastFamily* family = NvBlastAssetCreateFamily(fmem, asset, myLog); + scratch.resize((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, myLog)); + NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, scratch.data(), myLog); + EXPECT_TRUE(actor != nullptr); + + std::vector<NvBlastChunkFractureData> cfData; + cfData.resize(2 + i + 1); + + std::vector<NvBlastBondFractureData> bfData; + + cfData[0].userdata = 0; + cfData[0].chunkIndex = 1; + cfData[0].health = 10.0f; + + cfData[1].userdata = 0; + cfData[1].chunkIndex = 2; + cfData[1].health = 10.0f; + + cfData[2 + i].userdata = GUARD; + + NvBlastFractureBuffers commands = { 0, 2, nullptr, cfData.data() }; + NvBlastFractureBuffers events = { static_cast<uint32_t>(bfData.size()), static_cast<uint32_t>(cfData.size()) - 1, bfData.data(), cfData.data() }; + + NvBlastActorApplyFracture(&events, actor, &commands, myLog, nullptr); + + EXPECT_WARNING; + EXPECT_EQ(GUARD, cfData[cfData.size() - 1].userdata); + EXPECT_EQ(2 + i, events.chunkFractureCount); + for (uint32_t i = 0; i < events.chunkFractureCount; i++) + { + EXPECT_EQ(events.chunkFractures[i].chunkIndex, events.chunkFractures[i].userdata); + } + + EXPECT_TRUE(NvBlastActorDeactivate(actor, myLog)); + + free(family); + } + + { + // create actor + NvBlastActorDesc actorDesc; + actorDesc.initialBondHealths = actorDesc.initialSupportChunkHealths = nullptr; + actorDesc.uniformInitialBondHealth = actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f; + void* fmem = alloc(NvBlastAssetGetFamilyMemorySize(asset, myLog)); + NvBlastFamily* family = NvBlastAssetCreateFamily(fmem, asset, myLog); + scratch.resize((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, myLog)); + NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, scratch.data(), myLog); + EXPECT_TRUE(actor != nullptr); + + std::vector<NvBlastChunkFractureData> cfData; + cfData.resize(14 + 1); + cfData[cfData.size() - 1].userdata = GUARD; + std::vector<NvBlastBondFractureData> bfData; + + cfData[0].userdata = 0; + cfData[0].chunkIndex = 1; + cfData[0].health = 10.0f; + + cfData[1].userdata = 0; + cfData[1].chunkIndex = 2; + cfData[1].health = 10.0f; + + cfData[14].userdata = GUARD; + + NvBlastFractureBuffers commands = { 0, 2, nullptr, cfData.data() }; + NvBlastFractureBuffers events = { static_cast<uint32_t>(bfData.size()), static_cast<uint32_t>(cfData.size()) - 1, bfData.data(), cfData.data() }; + + NvBlastActorApplyFracture(&events, actor, &commands, myLog, nullptr); + + EXPECT_NO_WARNING; + EXPECT_EQ(14, events.chunkFractureCount); + for (uint32_t i = 0; i < events.chunkFractureCount; i++) + { + EXPECT_EQ(events.chunkFractures[i].chunkIndex, events.chunkFractures[i].userdata); + } + ASSERT_EQ(GUARD, cfData[cfData.size() - 1].userdata); + + scratch.resize((size_t)NvBlastActorGetRequiredScratchForSplit(actor, myLog)); + std::vector<NvBlastActor*> newActors(NvBlastActorGetMaxActorCountForSplit(actor, myLog)); + NvBlastActorSplitEvent result; + result.deletedActor = nullptr; + result.newActors = newActors.data(); + size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast<uint32_t>(newActors.size()), scratch.data(), myLog, nullptr); + newActors.resize(newActorsCount); + + EXPECT_EQ(9, newActorsCount); + for (uint32_t i = 0; i < newActorsCount; ++i) + { + const bool actorReleaseResult = NvBlastActorDeactivate(result.newActors[i], myLog); + EXPECT_TRUE(actorReleaseResult); + } + free(family); + } + + free(asset); + } + EXPECT_NO_WARNING; +} + +#if 0 +TEST(APITest, UserChunkMap) +{ + for (int i = 0; i < 2; ++i) + { + // Choose descriptor list + const NvBlastAssetDesc* descs = nullptr; + size_t size = 0; + switch (i) + { + case 0: + descs = g_assetDescs; + size = sizeof(g_assetDescs) / sizeof(g_assetDescs[0]); + break; + case 1: + descs = g_assetDescsMissingCoverage; + size = sizeof(g_assetDescsMissingCoverage) / sizeof(g_assetDescsMissingCoverage[0]); + break; + default: + continue; + } + + // Iterate over list + for (size_t j = 0; j < size; ++j) + { + // Create asset + const NvBlastAssetDesc* desc = descs + j; + std::vector<char> scratch((size_t)NvBlastGetRequiredScratchForCreateAsset(desc)); + std::vector<uint32_t> chunkMap(desc->chunkCount); + NvBlastAsset* asset = NvBlastCreateAsset(&chunkMap[0], desc, alignedAlloc<malloc>, scratch.data(), nullptr); + EXPECT_TRUE(asset); + + // Test map + Nv::Blast::Asset& a = static_cast<Nv::Blast::Asset&>(asset); + uint32_t supportChunkCount = 0; + uint32_t subsupportChunkCount = 0; + for (uint32_t i = 0; i < desc->chunkCount; ++i) + { + const uint32_t map = chunkMap[i]; + if (Nv::Blast::isInvalidIndex(map)) + { + continue; + } + else if (map < a.m_firstSubsupportChunkIndex) + { + EXPECT_LT(map, asset.m_graph.m_nodeCount); + ++supportChunkCount; + } + else + { + EXPECT_LT(map, asset.m_chunkCount); + EXPECT_GE(map, asset.m_graph.m_nodeCount); + ++subsupportChunkCount; + } + } + EXPECT_EQ(supportChunkCount, asset.m_graph.m_nodeCount); + EXPECT_EQ(subsupportChunkCount, a.getLowerSupportChunkCount() - asset.m_graph.m_nodeCount); + + // Release asset + NvBlastAssetRelease(asset, free, nullptr); + } + } +} +#endif + +TEST_F(APITest, NoBondsSausage) +{ + // create asset + const NvBlastChunkDesc c_chunks[4] = + { + // centroid volume parent idx flags ID + { { 0.0f, 0.0f, 0.0f }, 0.0f, UINT32_MAX, NvBlastChunkDesc::NoFlags, 0 }, + { { 0.0f, 0.0f, 0.0f }, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 1 }, + { { 0.0f, 0.0f, 0.0f }, 0.0f, 1, NvBlastChunkDesc::NoFlags, 2 }, + { { 0.0f, 0.0f, 0.0f }, 0.0f, 2, NvBlastChunkDesc::NoFlags, 3 } + }; + + NvBlastAssetDesc assetDesc; + assetDesc.chunkCount = 4; + assetDesc.chunkDescs = c_chunks; + assetDesc.bondCount = 0; + assetDesc.bondDescs = nullptr; + + std::vector<char> scratch((size_t)NvBlastGetRequiredScratchForCreateAsset(&assetDesc, messageLog)); + void* amem = alloc(NvBlastGetAssetMemorySize(&assetDesc, messageLog)); + NvBlastAsset* asset = NvBlastCreateAsset(amem, &assetDesc, scratch.data(), messageLog); + const NvBlastChunk* chunks = NvBlastAssetGetChunks(asset, messageLog); + EXPECT_TRUE(asset != nullptr); + + // create actor + NvBlastActorDesc actorDesc; + actorDesc.initialBondHealths = actorDesc.initialSupportChunkHealths = nullptr; + actorDesc.uniformInitialBondHealth = actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f; + void* fmem = alloc(NvBlastAssetGetFamilyMemorySize(asset, messageLog)); + NvBlastFamily* family = NvBlastAssetCreateFamily(fmem, asset, messageLog); + scratch.resize((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, messageLog)); + NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, scratch.data(), messageLog); + EXPECT_TRUE(actor != nullptr); + + // check visible chunk + { + EXPECT_EQ(NvBlastActorGetVisibleChunkCount(actor, messageLog), 1); + uint32_t chunkIndex; + NvBlastActorGetVisibleChunkIndices(&chunkIndex, 1, actor, messageLog); + EXPECT_EQ(chunks[chunkIndex].userData, 0); + } + + // damage + NvBlastExtRadialDamageDesc damage[] = { + { + 10.0f, // compressive + { 0.0f, 0.0f, 0.0f }, // position + 4.0f, // min radius - maximum damage + 6.0f // max radius - zero damage + } // linear falloff + }; + + NvBlastBondFractureData outBondFracture[2]; + NvBlastChunkFractureData outChunkFracture[2]; + + NvBlastFractureBuffers events; + events.bondFractureCount = 2; + events.bondFractures = outBondFracture; + events.chunkFractureCount = 2; + events.chunkFractures = outChunkFracture; + + NvBlastProgramParams programParams; + programParams.damageDescCount = 1; + programParams.damageDescBuffer = &damage; + + NvBlastDamageProgram program = { + NvBlastExtFalloffGraphShader, + NvBlastExtFalloffSubgraphShader + }; + + NvBlastActorGenerateFracture(&events, actor, program, &programParams, messageLog, nullptr); + NvBlastActorApplyFracture(&events, actor, &events, messageLog, nullptr); + EXPECT_EQ(0, events.bondFractureCount); + EXPECT_EQ(1, events.chunkFractureCount); + + // split + NvBlastActor* newActors[8]; /* num lower-support chunks? plus space for deletedActor */ + NvBlastActorSplitEvent result; + result.deletedActor = nullptr; + result.newActors = newActors; + scratch.resize((size_t)NvBlastActorGetRequiredScratchForSplit(actor, messageLog)); + size_t newActorsCount = NvBlastActorSplit(&result, actor, 8, scratch.data(), messageLog, nullptr); + EXPECT_EQ(1, newActorsCount); + EXPECT_EQ(true, result.deletedActor == actor); + + // check visible chunk + { + EXPECT_EQ(NvBlastActorGetVisibleChunkCount(result.newActors[0], messageLog), 1); + uint32_t chunkIndex; + NvBlastActorGetVisibleChunkIndices(&chunkIndex, 1, result.newActors[0], messageLog); + EXPECT_EQ(chunks[chunkIndex].userData, 3); + } + + // release all + for (uint32_t i = 0; i < newActorsCount; ++i) + { + const bool actorReleaseResult = NvBlastActorDeactivate(result.newActors[i], messageLog); + EXPECT_TRUE(actorReleaseResult); + } + + free(family); + free(asset); +} + +TEST_F(APITest, SplitOnlyWhenNecessary) +{ + static const uint32_t GUARD = 0xb1a57; + + const uint32_t chunksCount = 17; + const NvBlastChunkDesc c_chunks[chunksCount] = + { + // centroid volume parent idx flags ID + { { 0.0f, 0.0f, 0.0f }, 0.0f, UINT32_MAX, NvBlastChunkDesc::NoFlags, 0 }, + + { { 0.0f, 0.0f, 0.0f }, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 1 }, + { { 0.0f, 0.0f, 0.0f }, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 2 }, + { { 0.0f, 0.0f, 0.0f }, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 3 }, + { { 0.0f, 0.0f, 0.0f }, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 4 }, + + { { 0.0f, 0.0f, 0.0f }, 0.0f, 1, NvBlastChunkDesc::NoFlags, 5 }, + { { 0.0f, 0.0f, 0.0f }, 0.0f, 1, NvBlastChunkDesc::NoFlags, 6 }, + { { 0.0f, 0.0f, 0.0f }, 0.0f, 2, NvBlastChunkDesc::NoFlags, 7 }, + { { 0.0f, 0.0f, 0.0f }, 0.0f, 2, NvBlastChunkDesc::NoFlags, 8 }, + + { { 0.0f, 0.0f, 0.0f }, 0.0f, 5, NvBlastChunkDesc::NoFlags, 9 }, + { { 0.0f, 0.0f, 0.0f }, 0.0f, 5, NvBlastChunkDesc::NoFlags, 10 }, + { { 0.0f, 0.0f, 0.0f }, 0.0f, 6, NvBlastChunkDesc::NoFlags, 11 }, + { { 0.0f, 0.0f, 0.0f }, 0.0f, 6, NvBlastChunkDesc::NoFlags, 12 }, + { { 0.0f, 0.0f, 0.0f }, 0.0f, 7, NvBlastChunkDesc::NoFlags, 13 }, + { { 0.0f, 0.0f, 0.0f }, 0.0f, 7, NvBlastChunkDesc::NoFlags, 14 }, + { { 0.0f, 0.0f, 0.0f }, 0.0f, 8, NvBlastChunkDesc::NoFlags, 15 }, + { { 0.0f, 0.0f, 0.0f }, 0.0f, 8, NvBlastChunkDesc::NoFlags, 16 }, + }; + + const NvBlastBondDesc c_bonds[4] = + { + // chunks, normal, area, centroid, userdata + { { 1, 2 }, { { 1.0f, 0.0f, 0.0f }, 1.0f, { 1.0f, 0.0f, 0.0f }, 0 } }, + { { 2, 3 }, { { 1.0f, 0.0f, 0.0f }, 1.0f, { 1.0f, 0.0f, 0.0f }, 0 } }, + { { 3, 4 }, { { 1.0f, 0.0f, 0.0f }, 1.0f, { 1.0f, 0.0f, 0.0f }, 0 } }, + { { 1, 3 }, { { 1.0f, 0.0f, 0.0f }, 1.0f, { 1.0f, 0.0f, 0.0f }, 0 } } + }; + + NvBlastAssetDesc assetDesc = { chunksCount, c_chunks, 4, c_bonds }; + + // create asset with chunk map + std::vector<char> scratch((size_t)NvBlastGetRequiredScratchForCreateAsset(&assetDesc, myLog)); + void* amem = alloc(NvBlastGetAssetMemorySize(&assetDesc, myLog)); + NvBlastAsset* asset = NvBlastCreateAsset(amem, &assetDesc, scratch.data(), myLog); + EXPECT_TRUE(asset != nullptr); + + // create actor + NvBlastActorDesc actorDesc; + actorDesc.initialBondHealths = actorDesc.initialSupportChunkHealths = nullptr; + actorDesc.uniformInitialBondHealth = actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f; + void* fmem = alloc(NvBlastAssetGetFamilyMemorySize(asset, myLog)); + NvBlastFamily* family = NvBlastAssetCreateFamily(fmem, asset, myLog); + scratch.resize((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, myLog)); + NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, scratch.data(), myLog); + EXPECT_TRUE(actor != nullptr); + + + // damage health only (expect no split) + { + NvBlastBondFractureData command[] = + { + { 0, 0, 1, 0.99f }, + { 0, 1, 2, 0.50f }, + { 0, 2, 3, 0.01f } + }; + + NvBlastFractureBuffers commands = { 3, 0, command, nullptr }; + NvBlastActorApplyFracture(nullptr, actor, &commands, myLog, nullptr); + + scratch.resize((size_t)NvBlastActorGetRequiredScratchForSplit(actor, myLog)); + std::vector<NvBlastActor*> newActors(NvBlastActorGetMaxActorCountForSplit(actor, myLog)); + NvBlastActorSplitEvent result; + result.deletedActor = nullptr; + result.newActors = newActors.data(); + size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast<uint32_t>(newActors.size()), scratch.data(), myLog, nullptr); + + EXPECT_EQ(0, newActorsCount); + EXPECT_EQ(nullptr, result.deletedActor); + + EXPECT_EQ(1, NvBlastActorGetVisibleChunkCount(actor, myLog)); + uint32_t chunkIndex; + NvBlastActorGetVisibleChunkIndices(&chunkIndex, 1, actor, myLog); + EXPECT_EQ(0, chunkIndex); + } + + // break 1 bond (expect no split) + { + NvBlastBondFractureData command[] = + { + { 0, 0, 2, 10.0f }, + }; + + NvBlastFractureBuffers commands = { 1, 0, command, nullptr }; + NvBlastActorApplyFracture(nullptr, actor, &commands, myLog, nullptr); + + scratch.resize((size_t)NvBlastActorGetRequiredScratchForSplit(actor, myLog)); + std::vector<NvBlastActor*> newActors(NvBlastActorGetMaxActorCountForSplit(actor, myLog)); + NvBlastActorSplitEvent result; + result.deletedActor = nullptr; + result.newActors = newActors.data(); + size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast<uint32_t>(newActors.size()), scratch.data(), myLog, nullptr); + + EXPECT_EQ(0, newActorsCount); + EXPECT_EQ(nullptr, result.deletedActor); + + EXPECT_EQ(1, NvBlastActorGetVisibleChunkCount(actor, myLog)); + uint32_t chunkIndex; + NvBlastActorGetVisibleChunkIndices(&chunkIndex, 1, actor, myLog); + EXPECT_EQ(0, chunkIndex); + } + + // split in 4 + std::vector<NvBlastActor*> actors; + { + NvBlastBondFractureData command[] = + { + { 0, 0, 1, 10.0f }, + { 0, 1, 2, 10.0f }, + { 0, 2, 3, 10.0f } + }; + + NvBlastFractureBuffers commands = { 3, 0, command, nullptr }; + NvBlastActorApplyFracture(nullptr, actor, &commands, myLog, nullptr); + + scratch.resize((size_t)NvBlastActorGetRequiredScratchForSplit(actor, myLog)); + std::vector<NvBlastActor*> newActors(NvBlastActorGetMaxActorCountForSplit(actor, myLog)); + NvBlastActorSplitEvent result; + result.deletedActor = nullptr; + result.newActors = newActors.data(); + size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast<uint32_t>(newActors.size()), scratch.data(), myLog, nullptr); + + EXPECT_EQ(4, newActorsCount); + EXPECT_EQ(actor, result.deletedActor); + + actors.insert(actors.begin(), result.newActors, result.newActors + newActorsCount); + } + + // damage chunk's health only (expect no split) + { + for (NvBlastActor* actor : actors) + { + uint32_t chunkToDamage; + NvBlastActorGetVisibleChunkIndices(&chunkToDamage, 1, actor, myLog); + + NvBlastChunkFractureData command[] = + { + { 0, chunkToDamage, 0.9f }, + }; + + NvBlastFractureBuffers commands = { 0, 1, nullptr, command }; + NvBlastActorApplyFracture(nullptr, actor, &commands, myLog, nullptr); + + scratch.resize((size_t)NvBlastActorGetRequiredScratchForSplit(actor, myLog)); + std::vector<NvBlastActor*> newActors(NvBlastActorGetMaxActorCountForSplit(actor, myLog)); + NvBlastActorSplitEvent result; + result.deletedActor = nullptr; + result.newActors = newActors.data(); + size_t newActorsCount = NvBlastActorSplit(&result, actor, static_cast<uint32_t>(newActors.size()), scratch.data(), myLog, nullptr); + + EXPECT_EQ(0, newActorsCount); + EXPECT_EQ(nullptr, result.deletedActor); + + EXPECT_EQ(1, NvBlastActorGetVisibleChunkCount(actor, myLog)); + uint32_t chunkIndex; + NvBlastActorGetVisibleChunkIndices(&chunkIndex, 1, actor, myLog); + EXPECT_EQ(chunkToDamage, chunkIndex); + } + } + + for (NvBlastActor* actor : actors) + { + NvBlastActorDeactivate(actor, myLog); + } + + free(family); + free(asset); + + EXPECT_NO_WARNING; +} + +#if NV_WINDOWS_FAMILY +#include <windows.h> +TEST_F(APITest,CExportsNoNameMangling) +{ + + // + // tests the lib-link-free approach using unmangled names (extern "C") + // + +#if NV_WIN32 +#if NV_DEBUG + const char* dllName = "NvBlastDebug_x86.dll"; +#elif NV_CHECKED + const char* dllName = "NvBlastChecked_x86.dll"; +#elif NV_PROFILE + const char* dllName = "NvBlastProfile_x86.dll"; +#else + const char* dllName = "NvBlast_x86.dll"; +#endif +#elif NV_WIN64 +#if NV_DEBUG + const char* dllName = "NvBlastDebug_x64.dll"; +#elif NV_CHECKED + const char* dllName = "NvBlastChecked_x64.dll"; +#elif NV_PROFILE + const char* dllName = "NvBlastProfile_x64.dll"; +#else + const char* dllName = "NvBlast_x64.dll"; +#endif +#endif + + HMODULE dllHandle = LoadLibrary(TEXT(dllName)); + DWORD error = GetLastError(); + ASSERT_TRUE(dllHandle != nullptr); + + + // Asset functions + typedef size_t(*NvBlastGetRequiredScratchForCreateAsset)(const NvBlastAssetDesc* desc); + typedef size_t(*NvBlastGetAssetMemorySize)(const NvBlastAssetDesc* desc); + typedef NvBlastAsset*(*NvBlastCreateAsset)(void* mem, const NvBlastAssetDesc* desc, void* scratch, NvBlastLog logFn); + + NvBlastGetRequiredScratchForCreateAsset assetCreateRequiredScratch = (NvBlastGetRequiredScratchForCreateAsset)GetProcAddress(dllHandle, TEXT("NvBlastGetRequiredScratchForCreateAsset")); + ASSERT_TRUE(assetCreateRequiredScratch != nullptr); + + NvBlastGetAssetMemorySize assetGetMemorySize = (NvBlastGetAssetMemorySize)GetProcAddress(dllHandle, TEXT("NvBlastGetAssetMemorySize")); + ASSERT_TRUE(assetGetMemorySize != nullptr); + + NvBlastCreateAsset assetCreate = (NvBlastCreateAsset)GetProcAddress(dllHandle, TEXT("NvBlastCreateAsset")); + ASSERT_TRUE(assetCreate != nullptr); + + // Family functions + typedef NvBlastFamily* (*NvBlastAssetCreateFamily)(void* mem, const NvBlastAsset* asset, NvBlastLog logFn); + typedef size_t(*NVBLASTASSETGETFAMILYMEMORYSIZE)(const NvBlastAsset* asset); + + NVBLASTASSETGETFAMILYMEMORYSIZE familyGetMemorySize = (NVBLASTASSETGETFAMILYMEMORYSIZE)GetProcAddress(dllHandle, TEXT("NvBlastAssetGetFamilyMemorySize")); + ASSERT_TRUE(familyGetMemorySize != nullptr); + + NvBlastAssetCreateFamily familyCreate = (NvBlastAssetCreateFamily)GetProcAddress(dllHandle, TEXT("NvBlastAssetCreateFamily")); + ASSERT_TRUE(familyCreate != nullptr); + + + // Actor functions + typedef size_t(*NvBlastFamilyGetRequiredScratchForCreateFirstActor)(const NvBlastFamily* family); + typedef NvBlastActor* (*NvBlastFamilyCreateFirstActor)(NvBlastFamily* family, const NvBlastActorDesc* desc, void* scratch, NvBlastLog logFn); + typedef bool(*NVBLASTACTORDEACTIVATE)(NvBlastActor* actor); + + NvBlastFamilyGetRequiredScratchForCreateFirstActor actorcreaterequiredscratch = (NvBlastFamilyGetRequiredScratchForCreateFirstActor)GetProcAddress(dllHandle, TEXT("NvBlastFamilyGetRequiredScratchForCreateFirstActor")); + ASSERT_TRUE(actorcreaterequiredscratch != nullptr); + + NvBlastFamilyCreateFirstActor actorCreate = (NvBlastFamilyCreateFirstActor)GetProcAddress(dllHandle, TEXT("NvBlastFamilyCreateFirstActor")); + ASSERT_TRUE(actorCreate != nullptr); + + NVBLASTACTORDEACTIVATE actorRelease = (NVBLASTACTORDEACTIVATE)GetProcAddress(dllHandle, TEXT("NvBlastActorDeactivate")); + ASSERT_TRUE(actorRelease != nullptr); + + + const NvBlastChunkDesc c_chunks[] = + { + // centroid volume parent idx flags ID + { {0.0f, 0.0f, 0.0f}, 0.0f, UINT32_MAX, NvBlastChunkDesc::NoFlags, 0 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 0 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 0 }, + { {0.0f, 0.0f, 0.0f}, 0.0f, 0, NvBlastChunkDesc::SupportFlag, 0 }, + }; + + NvBlastAssetDesc assetDesc; + assetDesc.bondCount = 0; + assetDesc.bondDescs = nullptr; + assetDesc.chunkCount = 4; + assetDesc.chunkDescs = c_chunks; + + NvBlastAsset* asset; + { + size_t requiredsize = assetCreateRequiredScratch(&assetDesc); + std::vector<char>scratch(requiredsize); + void* mem = alloc(assetGetMemorySize(&assetDesc)); + asset = assetCreate(mem, &assetDesc, scratch.data(), myLog); + ASSERT_TRUE(asset != nullptr); + } + + void* fmem = alloc(familyGetMemorySize(asset)); + NvBlastFamily* family = familyCreate(fmem, asset, myLog); + + { + NvBlastActorDesc actorD; + actorD.initialBondHealths = actorD.initialSupportChunkHealths = nullptr; + actorD.uniformInitialBondHealth = actorD.uniformInitialLowerSupportChunkHealth = 1.0f; + + size_t requiredsize = actorcreaterequiredscratch(family); + std::vector<char>scratch(requiredsize); + NvBlastActor* actor = actorCreate(family, &actorD, scratch.data(), myLog); + ASSERT_TRUE(actor != nullptr); + + ASSERT_TRUE(actorRelease(actor)); + } + + free(family); + free(asset); + + EXPECT_NO_WARNING; +} +#endif |