aboutsummaryrefslogtreecommitdiff
path: root/test/src/unit/ActorTests.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'test/src/unit/ActorTests.cpp')
-rwxr-xr-x[-rw-r--r--]test/src/unit/ActorTests.cpp2236
1 files changed, 1118 insertions, 1118 deletions
diff --git a/test/src/unit/ActorTests.cpp b/test/src/unit/ActorTests.cpp
index d4de648..79bb1fc 100644..100755
--- a/test/src/unit/ActorTests.cpp
+++ b/test/src/unit/ActorTests.cpp
@@ -1,1118 +1,1118 @@
-// 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 "BlastBaseTest.h"
-#include "AssetGenerator.h"
-
-#include <map>
-#include <algorithm>
-
-#include "NvBlastActor.h"
-#include "NvBlastExtDamageShaders.h"
-
-
-static bool chooseRandomGraphNodes(uint32_t* g, uint32_t count, const Nv::Blast::Actor& actor)
-{
- const uint32_t graphNodeCount = actor.getGraphNodeCount();
-
- if (graphNodeCount < count)
- {
- return false;
- }
-
- std::vector<uint32_t> graphNodeIndices(graphNodeCount);
- uint32_t* index = graphNodeIndices.data();
- for (Nv::Blast::Actor::GraphNodeIt i = actor; (bool)i ; ++i)
- {
- *index++ = (uint32_t)i;
- }
- struct UserDataSorter
- {
- UserDataSorter(const Nv::Blast::Actor& actor) : m_asset(*actor.getAsset()) {}
-
- bool operator () (uint32_t i0, uint32_t i1) const
- {
- const uint32_t c0 = m_asset.m_graph.getChunkIndices()[i0];
- const uint32_t c1 = m_asset.m_graph.getChunkIndices()[i1];
- if (Nv::Blast::isInvalidIndex(c0) || Nv::Blast::isInvalidIndex(c1))
- {
- return c0 < c1;
- }
- return m_asset.getChunks()[c0].userData < m_asset.getChunks()[c1].userData;
- }
-
- const Nv::Blast::Asset& m_asset;
- } userDataSorter(actor);
- std::sort(graphNodeIndices.data(), graphNodeIndices.data() + graphNodeCount, userDataSorter);
-
-#if 0
- std::vector<uint32_t> descUserData(graphNodeCount);
- for (uint32_t i = 0; i < graphNodeCount; ++i)
- {
- descUserData[i] = actor.getAsset()->m_chunks[actor.getAsset()->m_graph.m_chunkIndices[graphNodeIndices[i]]].userData;
- }
-#endif
-
- uint32_t t = 0;
- uint32_t m = 0;
- for (uint32_t i = 0; i < graphNodeCount && m < count; ++i, ++t)
- {
- NVBLAST_ASSERT(t < graphNodeCount);
- if (t >= graphNodeCount)
- {
- break;
- }
- const float U = (float)rand()/RAND_MAX; // U is uniform random number in [0,1)
- if ((graphNodeCount - t)*U < count - m)
- {
- g[m++] = graphNodeIndices[i];
- }
- }
-
- return m == count;
-}
-
-
-static void blast(std::set<NvBlastActor*>& actorsToDamage, GeneratorAsset* testAsset, GeneratorAsset::Vec3 localPos, float minRadius, float maxRadius, float compressiveDamage)
-{
- std::vector<NvBlastChunkFractureData> chunkEvents; /* num lower-support chunks + bonds */
- std::vector<NvBlastBondFractureData> bondEvents; /* num lower-support chunks + bonds */
- chunkEvents.resize(testAsset->solverChunks.size());
- bondEvents.resize(testAsset->solverBonds.size());
-
-
- std::vector<char> splitScratch;
- std::vector<NvBlastActor*> newActorsBuffer(testAsset->solverChunks.size());
-
- NvBlastExtRadialDamageDesc damage = {
- compressiveDamage,
- { localPos.x, localPos.y, localPos.z },
- minRadius,
- maxRadius
- };
-
- NvBlastExtProgramParams programParams =
- {
- &damage,
- nullptr
- };
-
- NvBlastDamageProgram program = {
- NvBlastExtFalloffGraphShader,
- nullptr
- };
-
- size_t totalNewActorsCount = 0;
- for (std::set<NvBlastActor*>::iterator k = actorsToDamage.begin(); k != actorsToDamage.end();)
- {
- NvBlastActor* actor = *k;
- NvBlastFractureBuffers events = { static_cast<uint32_t>(bondEvents.size()), static_cast<uint32_t>(chunkEvents.size()), bondEvents.data(), chunkEvents.data() };
-
- NvBlastActorGenerateFracture(&events, actor, program, &programParams, nullptr, nullptr);
- NvBlastActorApplyFracture(&events, actor, &events, nullptr, nullptr);
- const bool isDamaged = NvBlastActorIsSplitRequired(actor, nullptr);
- bool removeActor = false;
-
- if (events.bondFractureCount + events.chunkFractureCount > 0)
- {
- NvBlastActorSplitEvent splitEvent;
- splitEvent.newActors = &newActorsBuffer.data()[totalNewActorsCount];
- uint32_t newActorSize = (uint32_t)(newActorsBuffer.size() - totalNewActorsCount);
-
- splitScratch.resize((size_t)NvBlastActorGetRequiredScratchForSplit(actor, nullptr));
- const size_t newActorsCount = NvBlastActorSplit(&splitEvent, actor, newActorSize, splitScratch.data(), nullptr, nullptr);
- EXPECT_TRUE(isDamaged || newActorsCount == 0);
- totalNewActorsCount += newActorsCount;
- removeActor = splitEvent.deletedActor != NULL;
- }
- else
- {
- EXPECT_FALSE(isDamaged);
- }
-
- if (removeActor)
- {
- k = actorsToDamage.erase(k);
- }
- else
- {
- ++k;
- }
- }
-
- for (size_t i = 0; i < totalNewActorsCount; ++i)
- {
- actorsToDamage.insert(newActorsBuffer[i]);
- }
-}
-
-
-template<int FailLevel, int Verbosity>
-class ActorTest : public BlastBaseTest<FailLevel, Verbosity>
-{
-public:
- ActorTest()
- {
-
- }
-
- static void messageLog(int type, const char* msg, const char* file, int line)
- {
- BlastBaseTest<FailLevel, Verbosity>::messageLog(type, msg, file, line);
- }
-
- static void* alloc(size_t size)
- {
- return BlastBaseTest<FailLevel, Verbosity>::alignedZeroedAlloc(size);
- }
-
- static void free(void* mem)
- {
- BlastBaseTest<FailLevel, Verbosity>::alignedFree(mem);
- }
-
- NvBlastAsset* buildAsset(const NvBlastAssetDesc& desc)
- {
- // fix desc if wrong order or missing coverage first
- NvBlastAssetDesc fixedDesc = desc;
- std::vector<NvBlastChunkDesc> chunkDescs(desc.chunkDescs, desc.chunkDescs + desc.chunkCount);
- std::vector<NvBlastBondDesc> bondDescs(desc.bondDescs, desc.bondDescs + desc.bondCount);
- std::vector<uint32_t> chunkReorderMap(desc.chunkCount);
- std::vector<char> scratch(desc.chunkCount * sizeof(NvBlastChunkDesc));
- NvBlastEnsureAssetExactSupportCoverage(chunkDescs.data(), fixedDesc.chunkCount, scratch.data(), messageLog);
- NvBlastReorderAssetDescChunks(chunkDescs.data(), fixedDesc.chunkCount, bondDescs.data(), fixedDesc.bondCount, chunkReorderMap.data(), true, scratch.data(), messageLog);
- fixedDesc.chunkDescs = chunkDescs.data();
- fixedDesc.bondDescs = bondDescs.empty() ? nullptr : bondDescs.data();
-
- // create asset
- m_scratch.resize((size_t)NvBlastGetRequiredScratchForCreateAsset(&fixedDesc, messageLog));
- void* mem = alloc(NvBlastGetAssetMemorySize(&fixedDesc, messageLog));
- NvBlastAsset* asset = NvBlastCreateAsset(mem, &fixedDesc, m_scratch.data(), messageLog);
- EXPECT_TRUE(asset != nullptr);
- return asset;
- }
-
- void buildAssets()
- {
- m_assets.resize(getAssetDescCount());
- for (uint32_t i = 0; i < m_assets.size(); ++i)
- {
- m_assets[i] = buildAsset(g_assetDescs[i]);
- }
- }
-
- NvBlastActor* instanceActor(const NvBlastAsset& asset)
- {
- NvBlastActorDesc actorDesc;
- actorDesc.initialBondHealths = actorDesc.initialSupportChunkHealths = nullptr;
- actorDesc.uniformInitialBondHealth = actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f;
- void* fmem = alloc(NvBlastAssetGetFamilyMemorySize(&asset, nullptr));
- NvBlastFamily* family = NvBlastAssetCreateFamily(fmem, &asset, nullptr);
- std::vector<char> scratch((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, messageLog));
- NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, scratch.data(), messageLog);
- EXPECT_TRUE(actor != nullptr);
- return actor;
- }
-
- void instanceActors()
- {
- m_actors.resize(m_assets.size());
- for (uint32_t i = 0; i < m_actors.size(); ++i)
- {
- m_actors[i] = instanceActor(*m_assets[i]);
- }
- }
-
- void releaseActors()
- {
- for (uint32_t i = 0; i < m_actors.size(); ++i)
- {
- NvBlastFamily* family = NvBlastActorGetFamily(m_actors[i], messageLog);
-
- const bool actorReleaseResult = NvBlastActorDeactivate(m_actors[i], messageLog);
- EXPECT_TRUE(actorReleaseResult);
-
- free(family);
- }
- }
-
- void destroyAssets()
- {
- for (uint32_t i = 0; i < m_assets.size(); ++i)
- {
- free(m_assets[i]);
- }
- }
-
- void instanceAndPartitionRecursively
- (
- const NvBlastAsset& asset,
- bool partitionToSubsupport,
- void (*preSplitTest)(const Nv::Blast::Actor&, NvBlastLog),
- void (*postSplitTest)(const std::vector<Nv::Blast::Actor*>&, uint32_t, uint32_t, bool)
- )
- {
- const Nv::Blast::Asset& solverAsset = *static_cast<const Nv::Blast::Asset*>(&asset);
-
- std::vector<Nv::Blast::Actor*> actors;
- std::vector<Nv::Blast::Actor*> buffer(NvBlastAssetGetChunkCount(&asset, messageLog));
-
- // Instance the first actor from the asset
- actors.push_back(static_cast<Nv::Blast::Actor*>(instanceActor(asset)));
-
- NvBlastFamily* family = NvBlastActorGetFamily(actors[0], messageLog);
-
- const uint32_t supportChunkCount = NvBlastAssetGetSupportChunkCount(&asset, messageLog);
- const uint32_t leafChunkCount = actors[0]->getAsset()->m_leafChunkCount;
-
- // Now randomly partition the actors in the array, and keep going until we're down to single support chunks
- bool canFracture = true;
-
- while (canFracture)
- {
- canFracture = false;
-
- for (uint32_t actorToPartition = 0; actorToPartition < actors.size(); ++actorToPartition)
- {
- Nv::Blast::Actor* a = (Nv::Blast::Actor*)actors[actorToPartition];
- if (a == nullptr)
- {
- continue;
- }
-
- m_scratch.reserve((size_t)NvBlastActorGetRequiredScratchForSplit(a, messageLog));
-
- if (preSplitTest)
- {
- preSplitTest(*a, nullptr);
- }
-
- const bool singleLowerSupportChunk = a->getGraphNodeCount() <= 1;
- uint32_t newActorCount = 0;
-
- for (int damageNum = 0; newActorCount < 2 && damageNum < 100; ++damageNum) // Avoid infinite loops
- {
- if (!singleLowerSupportChunk)
- {
- uint32_t g[2];
- chooseRandomGraphNodes(g, 2, *a);
- const uint32_t bondIndex = solverAsset.m_graph.findBond(g[0], g[1]);
- if (bondIndex != Nv::Blast::invalidIndex<uint32_t>())
- {
- a->damageBond(g[0], g[1], bondIndex, 100.0f);
- a->findIslands(m_scratch.data());
- }
- }
- else
- if (!partitionToSubsupport)
- {
- continue;
- }
-
- // Split actor
- newActorCount = a->partition((Nv::Blast::Actor**)&buffer[0], (uint32_t)buffer.size(), messageLog);
-
- if (newActorCount >= 2)
- {
- actors[actorToPartition] = nullptr;
- }
- }
-
- if (newActorCount > 1)
- {
- canFracture = true;
- }
-
- for (uint32_t i = 0; i < newActorCount; ++i)
- {
- actors.push_back(buffer[i]);
- buffer[i]->updateVisibleChunksFromGraphNodes();
- }
- }
- }
-
- if (postSplitTest)
- {
- postSplitTest(actors, leafChunkCount, supportChunkCount, partitionToSubsupport);
- }
-
- for (auto actor : actors)
- {
- if (actor)
- actor->release();
- }
-
- free(family);
- }
-
- static void recursivePartitionPostSplitTestCounts(const std::vector<Nv::Blast::Actor*>& actors, uint32_t leafChunkCount, uint32_t supportChunkCount, bool partitionToSubsupport)
- {
- // Test to see that all actors are split down to single support chunks
- uint32_t remainingActorCount = 0;
- for (uint32_t i = 0; i < actors.size(); ++i)
- {
- Nv::Blast::Actor* a = (Nv::Blast::Actor*)actors[i];
- if (a == nullptr)
- {
- continue;
- }
-
- ++remainingActorCount;
-
- NVBLAST_ASSERT(1 == a->getVisibleChunkCount() || a->isBoundToWorld());
- EXPECT_TRUE(1 == a->getVisibleChunkCount() || a->isBoundToWorld());
- if (!partitionToSubsupport)
- {
- EXPECT_EQ(1, a->getGraphNodeCount());
- }
-
- if (0 == a->getVisibleChunkCount())
- {
- EXPECT_TRUE(a->isBoundToWorld());
- EXPECT_EQ(1, a->getGraphNodeCount());
- EXPECT_EQ(a->getFamilyHeader()->m_asset->m_graph.m_nodeCount - 1, a->getFirstGraphNodeIndex());
- --remainingActorCount; // Do not count this as a remaining actor, to be compared with leaf or support chunk counts later
- }
-
- const bool actorReleaseResult = NvBlastActorDeactivate(actors[i], nullptr);
- EXPECT_TRUE(actorReleaseResult);
- }
-
- if (partitionToSubsupport)
- {
- EXPECT_EQ(leafChunkCount, remainingActorCount);
- }
- else
- {
- EXPECT_EQ(supportChunkCount, remainingActorCount);
- }
- }
-
- static void testActorVisibleChunks(const Nv::Blast::Actor& actor, NvBlastLog)
- {
- const Nv::Blast::Asset& asset = *actor.getAsset();
- const NvBlastChunk* chunks = asset.getChunks();
-
- if (actor.isSubSupportChunk())
- {
- EXPECT_EQ(1, actor.getVisibleChunkCount());
-
- const uint32_t firstVisibleChunkIndex = (uint32_t)Nv::Blast::Actor::VisibleChunkIt(actor);
-
- EXPECT_EQ(actor.getIndex() - asset.m_graph.m_nodeCount, firstVisibleChunkIndex - asset.m_firstSubsupportChunkIndex);
-
- // Make sure the visible chunk is subsupport
- // Array of support flags
- std::vector<bool> isSupport(asset.m_chunkCount, false);
- for (uint32_t i = 0; i < asset.m_graph.m_nodeCount; ++i)
- {
- const uint32_t chunkIndex = asset.m_graph.getChunkIndices()[i];
- if (!Nv::Blast::isInvalidIndex(chunkIndex))
- {
- isSupport[chunkIndex] = true;
- }
- }
-
- // Climb hierarchy to find support chunk
- uint32_t chunkIndex = firstVisibleChunkIndex;
- while (chunkIndex != Nv::Blast::invalidIndex<uint32_t>())
- {
- if (isSupport[chunkIndex])
- {
- break;
- }
- chunkIndex = chunks[chunkIndex].parentChunkIndex;
- }
-
- EXPECT_FALSE(Nv::Blast::isInvalidIndex(chunkIndex));
- }
- else
- {
- // Array of visibility flags
- std::vector<bool> isVisible(asset.m_chunkCount, false);
- for (Nv::Blast::Actor::VisibleChunkIt i = actor; (bool)i; ++i)
- {
- isVisible[(uint32_t)i] = true;
- }
-
- // Mark visible nodes representing graph chunks
- std::vector<bool> visibleChunkFound(asset.m_chunkCount, false);
-
- // Make sure every graph chunk is represented by a visible chunk, or represents the world
- for (Nv::Blast::Actor::GraphNodeIt i = actor; (bool)i; ++i)
- {
- const uint32_t graphNodeIndex = (uint32_t)i;
- uint32_t chunkIndex = asset.m_graph.getChunkIndices()[graphNodeIndex];
- // Climb hierarchy to find visible chunk
- while (chunkIndex != Nv::Blast::invalidIndex<uint32_t>())
- {
- // Check that chunk owners are accurate
- EXPECT_EQ(actor.getIndex(), actor.getFamilyHeader()->getChunkActorIndices()[chunkIndex]);
- if (isVisible[chunkIndex])
- {
- visibleChunkFound[chunkIndex] = true;
- break;
- }
- chunkIndex = chunks[chunkIndex].parentChunkIndex;
- }
- EXPECT_TRUE(!Nv::Blast::isInvalidIndex(chunkIndex) || (graphNodeIndex == asset.m_graph.m_nodeCount-1 && actor.isBoundToWorld()));
- }
-
- // Check that all visible chunks are accounted for
- for (uint32_t i = 0; i < asset.m_chunkCount; ++i)
- {
- EXPECT_EQ(visibleChunkFound[i], isVisible[i]);
- }
-
- // Make sure that, if all siblings are intact, they are invisible
- for (uint32_t i = 0; i < asset.m_chunkCount; ++i)
- {
- bool allIntact = true;
- bool noneVisible = true;
- if (chunks[i].firstChildIndex < asset.getUpperSupportChunkCount()) // Do not check subsupport
- {
- for (uint32_t j = chunks[i].firstChildIndex; j < chunks[i].childIndexStop; ++j)
- {
- allIntact = allIntact && actor.getFamilyHeader()->getChunkActorIndices()[j] == actor.getIndex();
- noneVisible = noneVisible && !isVisible[j];
- }
- EXPECT_TRUE(!allIntact || noneVisible);
- }
- }
- }
- }
-
- static void recursivePartitionPostSplitTestVisibleChunks(const std::vector<Nv::Blast::Actor*>& actors, uint32_t leafChunkCount, uint32_t supportChunkCount, bool partitionToSubsupport)
- {
- for (uint32_t i = 0; i < actors.size(); ++i)
- {
- Nv::Blast::Actor* a = (Nv::Blast::Actor*)actors[i];
- if (a == nullptr)
- {
- continue;
- }
-
- testActorVisibleChunks(*a, nullptr);
- }
- }
-
- void partitionActorsToSupportChunks
- (
- uint32_t assetDescCount,
- const NvBlastAssetDesc* assetDescs,
- void(*preSplitTest)(const Nv::Blast::Actor&, NvBlastLog),
- void(*postSplitTest)(const std::vector<Nv::Blast::Actor*>&, uint32_t, uint32_t, bool),
- bool partitionToSubsupport
- )
- {
- srand(0);
-
- for (uint32_t i = 0; i < assetDescCount; ++i)
- {
- // Create an asset
- NvBlastAsset* asset = buildAsset(assetDescs[i]);
-
- // Perform repeated partitioning
- instanceAndPartitionRecursively(*asset, partitionToSubsupport, preSplitTest, postSplitTest);
-
- // Free the asset
- free(asset);
- }
- }
-
- static void compareFamilies(const NvBlastFamily* family1, const NvBlastFamily* family2, size_t size, NvBlastLog logFn)
- {
- const char* block1 = reinterpret_cast<const char*>(family1);
- const char* block2 = reinterpret_cast<const char*>(family2);
-#if 0
- EXPECT_EQ(0, memcmp(block1, block2, size));
-#else
- bool diffFound = false;
- size_t startDiff = 0;
- for (size_t i = 0; i < size; ++i)
- {
- if (block1[i] != block2[i])
- {
- diffFound = true;
- startDiff = i;
- break;
- }
- }
- if (!diffFound)
- {
- return;
- }
- size_t endDiff = startDiff;
- for (size_t i = size; i--;)
- {
- if (block1[i] != block2[i])
- {
- endDiff = i;
- break;
- }
- }
- std::ostringstream msg;
- msg << "Block deserialization does not match current block in position range [" << startDiff << ", " << endDiff << "].";
- logFn(NvBlastMessage::Error, msg.str().c_str(), __FILE__, __LINE__);
-#endif
- }
-
- static void testActorBlockSerialize(std::vector<NvBlastActor*>& actors, NvBlastLog logFn)
- {
- if (actors.size())
- {
- const NvBlastFamily* family = NvBlastActorGetFamily(actors[0], logFn);
- const uint32_t size = NvBlastFamilyGetSize(family, logFn);
- s_storage.insert(s_storage.end(), (char*)family, (char*)family + size);
- }
- }
-
- static void testActorBlockDeserialize(std::vector<NvBlastActor*>& actors, NvBlastLog logFn)
- {
- if (actors.size())
- {
- EXPECT_LT(s_curr, s_storage.size());
- const NvBlastFamily* family = reinterpret_cast<NvBlastFamily*>(&s_storage[s_curr]);
- const uint32_t size = NvBlastFamilyGetSize(family, logFn);
- EXPECT_LE(s_curr + size, s_storage.size());
- s_curr += size;
- const NvBlastFamily* actorFamily = NvBlastActorGetFamily(actors[0], logFn);
- // Family may contain different assets pointers, copy into new family block and set the same asset before comparing
- Nv::Blast::Actor& a = *static_cast<Nv::Blast::Actor*>(actors[0]);
- const Nv::Blast::Asset* solverAsset = a.getAsset();
- std::vector<char> storageFamilyCopy((char*)family, (char*)family + size);
- NvBlastFamily* storageFamily = reinterpret_cast<NvBlastFamily*>(storageFamilyCopy.data());
- NvBlastFamilySetAsset(storageFamily, solverAsset, logFn);
- {
- const uint32_t actorCountExpected = NvBlastFamilyGetActorCount(storageFamily, logFn);
- std::vector<NvBlastActor*> blockActors(actorCountExpected);
- const uint32_t actorCountReturned = NvBlastFamilyGetActors(blockActors.data(), actorCountExpected, storageFamily, logFn);
- EXPECT_EQ(actorCountExpected, actorCountReturned);
- }
- compareFamilies(storageFamily, actorFamily, size, logFn);
- }
- }
-
- // Serialize all actors and then deserialize back into a new family in a random order, and compare with the original family
- static void testActorSerializationNewFamily(std::vector<NvBlastActor*>& actors, NvBlastLog logFn)
- {
- if (actors.size() == 0)
- {
- return;
- }
-
- Nv::Blast::Actor& a = *static_cast<Nv::Blast::Actor*>(actors[0]);
- const Nv::Blast::Asset* solverAsset = a.getAsset();
-
- const uint32_t serSizeBound = NvBlastAssetGetActorSerializationSizeUpperBound(solverAsset, logFn);
-
- std::vector< std::vector<char> > streams(actors.size());
- for (size_t i = 0; i < actors.size(); ++i)
- {
- const uint32_t serSize = NvBlastActorGetSerializationSize(actors[i], logFn);
- EXPECT_GE(serSizeBound, serSize);
- std::vector<char>& stream = streams[i];
- stream.resize(serSize);
- const uint32_t bytesWritten = NvBlastActorSerialize(stream.data(), serSize, actors[i], logFn);
- EXPECT_EQ(serSize, bytesWritten);
- }
-
- void* fmem = alloc(NvBlastAssetGetFamilyMemorySize(solverAsset, logFn));
- NvBlastFamily* newFamily = NvBlastAssetCreateFamily(fmem, solverAsset, logFn);
-
- std::vector<size_t> order(actors.size());
- for (size_t i = 0; i < order.size(); ++i)
- {
- order[i] = i;
- }
- std::random_shuffle(order.begin(), order.end());
-
- for (size_t i = 0; i < actors.size(); ++i)
- {
- NvBlastActor* newActor = NvBlastFamilyDeserializeActor(newFamily, streams[order[i]].data(), logFn);
- EXPECT_TRUE(newActor != nullptr);
- }
-
- const NvBlastFamily* oldFamily = NvBlastActorGetFamily(&a, logFn);
- compareFamilies(oldFamily, newFamily, NvBlastFamilyGetSize(oldFamily, logFn), logFn);
-
- free(newFamily);
- }
-
- // Copy the family and then serialize some subset of actors, deleting them afterwards.
- // Then, deserialize back into the block and compare the original and new families.
- static void testActorSerializationPartialBlock(std::vector<NvBlastActor*>& actors, NvBlastLog logFn)
- {
- if (actors.size() <= 1)
- {
- return;
- }
-
- Nv::Blast::Actor& a = *static_cast<Nv::Blast::Actor*>(actors[0]);
- const Nv::Blast::Asset* solverAsset = a.getAsset();
-
- const NvBlastFamily* oldFamily = NvBlastActorGetFamily(&a, logFn);
- const uint32_t size = NvBlastFamilyGetSize(oldFamily, logFn);
- std::vector<char> buffer((char*)oldFamily, (char*)oldFamily + size);
- NvBlastFamily* familyCopy = reinterpret_cast<NvBlastFamily*>(buffer.data());
-
- const uint32_t serCount = 1 + (rand() % actors.size() - 1);
-
- const uint32_t actorCount = NvBlastFamilyGetActorCount(familyCopy, logFn);
- std::vector<NvBlastActor*> actorsRemaining(actorCount);
- const uint32_t actorsInFamily = NvBlastFamilyGetActors(&actorsRemaining[0], actorCount, familyCopy, logFn);
- EXPECT_EQ(actorCount, actorsInFamily);
-
- const uint32_t serSizeBound = NvBlastAssetGetActorSerializationSizeUpperBound(solverAsset, logFn);
-
- std::vector< std::vector<char> > streams(serCount);
- for (uint32_t i = 0; i < serCount; ++i)
- {
- std::vector<char>& stream = streams[i];
- const uint32_t indexToStream = rand() % actorsRemaining.size();
- NvBlastActor* actorToStream = actorsRemaining[indexToStream];
- std::swap(actorsRemaining[indexToStream], actorsRemaining[actorsRemaining.size() - 1]);
- actorsRemaining.pop_back();
- const uint32_t serSize = NvBlastActorGetSerializationSize(actorToStream, logFn);
- EXPECT_GE(serSizeBound, serSize);
- stream.resize(serSize);
- const uint32_t bytesWritten = NvBlastActorSerialize(&stream[0], serSize, actorToStream, logFn);
- EXPECT_EQ(serSize, bytesWritten);
- NvBlastActorDeactivate(actorToStream, logFn);
- }
-
- for (uint32_t i = 0; i < serCount; ++i)
- {
- NvBlastActor* newActor = NvBlastFamilyDeserializeActor(familyCopy, streams[i].data(), logFn);
- EXPECT_TRUE(newActor != nullptr);
- }
-
- compareFamilies(oldFamily, familyCopy, size, logFn);
- }
-
- void damageLeafSupportActors
- (
- uint32_t assetCount,
- uint32_t familyCount,
- uint32_t damageCount,
- bool simple,
- void (*actorTest)(const Nv::Blast::Actor&, NvBlastLog),
- void (*postDamageTest)(std::vector<NvBlastActor*>&, NvBlastLog),
- CubeAssetGenerator::BondFlags bondFlags = CubeAssetGenerator::BondFlags::ALL_INTERNAL_BONDS
- )
- {
- const float relativeDamageRadius = simple ? 0.75f : 0.2f;
- const float compressiveDamage = 1.0f;
- const uint32_t minChunkCount = simple ? 9 : 100;
- const uint32_t maxChunkCount = simple ? 9 : 10000;
- const bool printActorCount = false;
-
- srand(0);
-
- std::cout << "Asset # (out of " << assetCount << "): ";
- for (uint32_t assetNum = 0; assetNum < assetCount; ++assetNum)
- {
- std::cout << assetNum + 1 << ".. ";
- CubeAssetGenerator::Settings settings;
- settings.extents = GeneratorAsset::Vec3(1, 1, 1);
- settings.bondFlags = bondFlags;
- CubeAssetGenerator::DepthInfo depthInfo;
- depthInfo.slicesPerAxis = GeneratorAsset::Vec3(1, 1, 1);
- depthInfo.flag = NvBlastChunkDesc::Flags::NoFlags;
- settings.depths.push_back(depthInfo);
- uint32_t chunkCount = 1;
- while (chunkCount < minChunkCount)
- {
- uint32_t chunkMul;
- do
- {
- depthInfo.slicesPerAxis = simple ? GeneratorAsset::Vec3(2, 2, 2) : GeneratorAsset::Vec3((float)(1 + rand() % 4), (float)(1 + rand() % 4), (float)(1 + rand() % 4));
- chunkMul = (uint32_t)(depthInfo.slicesPerAxis.x * depthInfo.slicesPerAxis.y * depthInfo.slicesPerAxis.z);
- } while (chunkMul == 1);
- if (chunkCount*chunkMul > maxChunkCount)
- {
- break;
- }
- chunkCount *= chunkMul;
- settings.depths.push_back(depthInfo);
- settings.extents = settings.extents * depthInfo.slicesPerAxis;
- }
- settings.depths.back().flag = NvBlastChunkDesc::SupportFlag; // Leaves are support
-
- // Make largest direction unit size
- settings.extents = settings.extents * (1.0f / std::max(settings.extents.x, std::max(settings.extents.y, settings.extents.z)));
-
- // Create asset
- GeneratorAsset testAsset;
- CubeAssetGenerator::generate(testAsset, settings);
-
- NvBlastAssetDesc desc;
- desc.chunkDescs = testAsset.solverChunks.data();
- desc.chunkCount = (uint32_t)testAsset.solverChunks.size();
- desc.bondDescs = testAsset.solverBonds.data();
- desc.bondCount = (uint32_t)testAsset.solverBonds.size();
- NvBlastAsset* asset = buildAsset(desc);
- NvBlastID assetID = NvBlastAssetGetID(asset, messageLog);
-
- // copy asset (for setAsset testing)
- const char* data = (const char*)asset;
- const uint32_t dataSize = NvBlastAssetGetSize(asset, messageLog);
- char* duplicateData = (char*)alloc(dataSize);
- memcpy(duplicateData, data, dataSize);
- NvBlastAsset* assetDuplicate = (NvBlastAsset*)duplicateData;
-
- // Generate families
- for (uint32_t familyNum = 0; familyNum < familyCount; ++familyNum)
- {
- // family
- void* fmem = alloc(NvBlastAssetGetFamilyMemorySize(asset, messageLog));
- NvBlastFamily* family = NvBlastAssetCreateFamily(fmem, asset, messageLog); // Using zeroingAlloc in case actorTest compares memory blocks
- NvBlastID id = NvBlastFamilyGetAssetID(family, messageLog);
- EXPECT_TRUE(!memcmp(&assetID, &id, sizeof(NvBlastID)));
- if (rand() % 2 == 0)
- {
- // replace asset with duplicate in half of cases to test setAsset
- NvBlastFamilySetAsset(family, assetDuplicate, messageLog);
- NvBlastID id2 = NvBlastFamilyGetAssetID(family, messageLog);
- EXPECT_TRUE(!memcmp(&assetID, &id2, sizeof(NvBlastID)));
- }
-
- // actor
- NvBlastActorDesc actorDesc;
- actorDesc.initialBondHealths = actorDesc.initialSupportChunkHealths = nullptr;
- actorDesc.uniformInitialBondHealth = actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f;
- m_scratch.resize((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, messageLog));
- NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, m_scratch.data(), messageLog);
- EXPECT_TRUE(actor != nullptr);
-
- // Generate damage
- std::set<NvBlastActor*> actors;
- actors.insert(actor);
- if (printActorCount) std::cout << "Actors: 1.. ";
- for (uint32_t damageNum = 0; damageNum < damageCount; ++damageNum)
- {
- GeneratorAsset::Vec3 localPos = settings.extents*GeneratorAsset::Vec3((float)rand() / RAND_MAX - 0.5f, (float)rand() / RAND_MAX - 0.5f, (float)rand() / RAND_MAX - 0.5f);
- blast(actors, &testAsset, localPos, relativeDamageRadius, relativeDamageRadius*1.2f, compressiveDamage);
- if (printActorCount) std::cout << actors.size() << ".. ";
- if (actors.size() > 0)
- {
- const NvBlastFamily* family = NvBlastActorGetFamily(*actors.begin(), messageLog);
- const uint32_t actorCount = NvBlastFamilyGetActorCount(family, messageLog);
- EXPECT_EQ((uint32_t)actors.size(), actorCount);
- if ((uint32_t)actors.size() == actorCount)
- {
- std::vector<NvBlastActor*> buffer1(actorCount);
- const uint32_t actorsWritten = NvBlastFamilyGetActors(&buffer1[0], actorCount, family, messageLog);
- EXPECT_EQ(actorsWritten, actorCount);
- std::vector<NvBlastActor*> buffer2(actors.begin(), actors.end());
- EXPECT_EQ(0, memcmp(&buffer1[0], buffer2.data(), actorCount*sizeof(NvBlastActor*)));
- }
- }
- // Test individual actors
- if (actorTest != nullptr)
- {
- for (std::set<NvBlastActor*>::iterator k = actors.begin(); k != actors.end(); ++k)
- {
- actorTest(*static_cast<Nv::Blast::Actor*>(*k), messageLog);
- }
- }
- }
- if (printActorCount) std::cout << "\n";
-
- // Test fractured actor set
- if (postDamageTest)
- {
- std::vector<NvBlastActor*> actorArray(actors.begin(), actors.end());
- postDamageTest(actorArray, messageLog);
- }
-
- // Release remaining actors
- for (std::set<NvBlastActor*>::iterator k = actors.begin(); k != actors.end(); ++k)
- {
- NvBlastActorDeactivate(*k, messageLog);
- }
- actors.clear();
-
- free(family);
- }
-
- // Release asset data
- free(asset);
- free(assetDuplicate);
- }
- std::cout << "done.\n";
- }
-
- std::vector<NvBlastAsset*> m_assets;
- std::vector<NvBlastActor*> m_actors;
- std::vector<char> m_scratch;
- static std::vector<char> s_storage;
-
- static size_t s_curr;
-};
-
-// Static values
-template<int FailLevel, int Verbosity>
-std::vector<char> ActorTest<FailLevel, Verbosity>::s_storage;
-
-template<int FailLevel, int Verbosity>
-size_t ActorTest<FailLevel, Verbosity>::s_curr;
-
-// Specializations
-typedef ActorTest<NvBlastMessage::Error, 1> ActorTestAllowWarnings;
-typedef ActorTest<NvBlastMessage::Warning, 1> ActorTestStrict;
-
-// Tests
-TEST_F(ActorTestStrict, InstanceActors)
-{
- // Build assets and instance actors
- buildAssets();
- instanceActors();
-
- // Release actors and destroy assets
- releaseActors();
- destroyAssets();
-}
-
-TEST_F(ActorTestAllowWarnings, ActorHealthInitialization)
-{
- // Test all assets
- std::vector<NvBlastAssetDesc> assetDescs;
- assetDescs.insert(assetDescs.end(), g_assetDescs, g_assetDescs + getAssetDescCount());
- assetDescs.insert(assetDescs.end(), g_assetDescsMissingCoverage, g_assetDescsMissingCoverage + getAssetDescMissingCoverageCount());
-
- struct TestMode
- {
- enum Enum
- {
- Uniform,
- Nonuniform,
-
- Count
- };
- };
-
- for (auto assetDesc : assetDescs)
- {
- NvBlastAsset* asset = buildAsset(assetDesc);
- EXPECT_TRUE(asset != nullptr);
-
- Nv::Blast::Asset& assetInt = static_cast<Nv::Blast::Asset&>(*asset);
-
- NvBlastSupportGraph graph = NvBlastAssetGetSupportGraph(asset, nullptr);
-
- std::vector<float> supportChunkHealths(graph.nodeCount);
- for (size_t i = 0; i < supportChunkHealths.size(); ++i)
- {
- supportChunkHealths[i] = 1.0f + (float)i;
- }
-
- std::vector<float> bondHealths(assetInt.getBondCount());
- for (size_t i = 0; i < bondHealths.size(); ++i)
- {
- bondHealths[i] = 1.5f + (float)i;
- }
-
- for (int chunkTestMode = 0; chunkTestMode < TestMode::Count; ++chunkTestMode)
- {
- for (int bondTestMode = 0; bondTestMode < TestMode::Count; ++bondTestMode)
- {
- NvBlastActorDesc actorDesc;
-
- switch (chunkTestMode)
- {
- default:
- case TestMode::Uniform:
- actorDesc.initialSupportChunkHealths = nullptr;
- actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f;
- break;
- case TestMode::Nonuniform:
- actorDesc.initialSupportChunkHealths = supportChunkHealths.data();
- break;
- }
-
- switch (bondTestMode)
- {
- default:
- case TestMode::Uniform:
- actorDesc.initialBondHealths = nullptr;
- actorDesc.uniformInitialBondHealth = 2.0f;
- break;
- case TestMode::Nonuniform:
- actorDesc.initialBondHealths = bondHealths.data();
- break;
- }
-
- void* fmem = alloc(NvBlastAssetGetFamilyMemorySize(asset, messageLog));
- NvBlastFamily* family = NvBlastAssetCreateFamily(fmem, asset, nullptr);
- std::vector<char> scratch((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, messageLog));
- NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, scratch.data(), messageLog);
- EXPECT_TRUE(actor != nullptr);
-
- Nv::Blast::Actor& actorInt = static_cast<Nv::Blast::Actor&>(*actor);
- Nv::Blast::FamilyHeader* header = actorInt.getFamilyHeader();
-
-
- for (uint32_t i = 0; i < graph.nodeCount; ++i)
- {
- const uint32_t supportChunkIndex = graph.chunkIndices[i];
- for (Nv::Blast::Asset::DepthFirstIt it(assetInt, supportChunkIndex); (bool)it; ++it)
- {
- const uint32_t chunkIndex = (uint32_t)it;
- const uint32_t lowerSupportIndex = assetInt.getContiguousLowerSupportIndex(chunkIndex);
- NVBLAST_ASSERT(lowerSupportIndex < assetInt.getLowerSupportChunkCount());
- const float health = header->getLowerSupportChunkHealths()[lowerSupportIndex];
- switch (chunkTestMode)
- {
- default:
- case TestMode::Uniform:
- EXPECT_EQ(1.0f, health);
- break;
- case TestMode::Nonuniform:
- EXPECT_EQ(supportChunkHealths[i], health);
- break;
- }
- }
- }
-
- for (uint32_t i = 0; i < assetInt.getBondCount(); ++i)
- {
- switch (bondTestMode)
- {
- default:
- case TestMode::Uniform:
- EXPECT_EQ(2.0f, header->getBondHealths()[i]);
- break;
- case TestMode::Nonuniform:
- EXPECT_EQ(bondHealths[i], header->getBondHealths()[i]);
- break;
- }
- }
-
- NvBlastActorDeactivate(actor, messageLog);
- free(family);
- }
- }
-
- free(asset);
- }
-}
-
-TEST_F(ActorTestStrict, PartitionActorsToSupportChunksTestCounts)
-{
- partitionActorsToSupportChunks(getAssetDescCount(), g_assetDescs, nullptr, recursivePartitionPostSplitTestCounts, false);
-}
-
-TEST_F(ActorTestAllowWarnings, PartitionActorsFromBadDescriptorsToSupportChunksTestCounts)
-{
- partitionActorsToSupportChunks(getAssetDescMissingCoverageCount(), g_assetDescsMissingCoverage, nullptr, recursivePartitionPostSplitTestCounts, false);
-}
-
-TEST_F(ActorTestStrict, PartitionActorsToLeafChunksTestCounts)
-{
- partitionActorsToSupportChunks(getAssetDescCount(), g_assetDescs, nullptr, recursivePartitionPostSplitTestCounts, true);
-}
-
-TEST_F(ActorTestAllowWarnings, PartitionActorsFromBadDescriptorsToLeafChunksTestCounts)
-{
- partitionActorsToSupportChunks(getAssetDescMissingCoverageCount(), g_assetDescsMissingCoverage, nullptr, recursivePartitionPostSplitTestCounts, true);
-}
-
-TEST_F(ActorTestStrict, PartitionActorsToSupportChunksTestVisibility)
-{
- partitionActorsToSupportChunks(getAssetDescCount(), g_assetDescs, testActorVisibleChunks, recursivePartitionPostSplitTestVisibleChunks, false);
-}
-
-TEST_F(ActorTestAllowWarnings, PartitionActorsFromBadDescriptorsToSupportChunksTestVisibility)
-{
- partitionActorsToSupportChunks(getAssetDescMissingCoverageCount(), g_assetDescsMissingCoverage, testActorVisibleChunks, recursivePartitionPostSplitTestVisibleChunks, false);
-}
-
-TEST_F(ActorTestStrict, PartitionActorsToLeafChunksTestVisibility)
-{
- partitionActorsToSupportChunks(getAssetDescCount(), g_assetDescs, testActorVisibleChunks, recursivePartitionPostSplitTestVisibleChunks, true);
-}
-
-TEST_F(ActorTestAllowWarnings, PartitionActorsFromBadDescriptorsToLeafChunksTestVisibility)
-{
- partitionActorsToSupportChunks(getAssetDescMissingCoverageCount(), g_assetDescsMissingCoverage, testActorVisibleChunks, recursivePartitionPostSplitTestVisibleChunks, true);
-}
-
-TEST_F(ActorTestStrict, DamageLeafSupportActorsTestVisibility)
-{
- damageLeafSupportActors(4, 4, 5, false, testActorVisibleChunks, nullptr);
-}
-
-TEST_F(ActorTestStrict, DamageLeafSupportActorTestBlockSerialization)
-{
- s_storage.resize(0);
- damageLeafSupportActors(4, 4, 5, false, nullptr, testActorBlockSerialize);
- s_curr = 0;
- damageLeafSupportActors(4, 4, 5, false, nullptr, testActorBlockDeserialize);
- s_storage.resize(0);
-}
-
-TEST_F(ActorTestStrict, DamageSimpleLeafSupportActorTestActorSerializationNewFamily)
-{
- damageLeafSupportActors(1, 1, 4, true, nullptr, testActorSerializationNewFamily);
-}
-
-TEST_F(ActorTestStrict, DamageSimpleLeafSupportActorTestActorSerializationPartialBlock)
-{
- damageLeafSupportActors(1, 1, 4, true, nullptr, testActorSerializationPartialBlock);
-}
-
-TEST_F(ActorTestStrict, DamageLeafSupportActorTestActorSerializationNewFamily)
-{
- damageLeafSupportActors(4, 4, 4, false, nullptr, testActorSerializationNewFamily);
-}
-
-TEST_F(ActorTestStrict, DamageLeafSupportActorTestActorSerializationPartialBlock)
-{
- damageLeafSupportActors(4, 4, 4, false, nullptr, testActorSerializationPartialBlock);
-}
-
-TEST_F(ActorTestStrict, DamageMultipleIslandLeafSupportActorsTestVisibility)
-{
- typedef CubeAssetGenerator::BondFlags BF;
- damageLeafSupportActors(4, 4, 5, false, testActorVisibleChunks, nullptr, BF::Y_BONDS | BF::Z_BONDS); // Only connect y-z plane islands
- damageLeafSupportActors(4, 4, 5, false, testActorVisibleChunks, nullptr, BF::Z_BONDS); // Only connect z-direction islands
- damageLeafSupportActors(4, 4, 5, false, testActorVisibleChunks, nullptr, BF::NO_BONDS); // All support chunks disconnected (single-chunk islands)
-}
-
-TEST_F(ActorTestStrict, DamageBoundToWorldLeafSupportActorsTestVisibility)
-{
- typedef CubeAssetGenerator::BondFlags BF;
- damageLeafSupportActors(4, 4, 5, false, testActorVisibleChunks, nullptr, BF::ALL_INTERNAL_BONDS | BF::X_MINUS_WORLD_BONDS);
- damageLeafSupportActors(4, 4, 5, false, testActorVisibleChunks, nullptr, BF::ALL_INTERNAL_BONDS | BF::Y_PLUS_WORLD_BONDS);
- damageLeafSupportActors(4, 4, 5, false, testActorVisibleChunks, nullptr, BF::ALL_INTERNAL_BONDS | BF::Z_MINUS_WORLD_BONDS);
- damageLeafSupportActors(4, 4, 5, false, testActorVisibleChunks, nullptr, BF::ALL_INTERNAL_BONDS | BF::X_PLUS_WORLD_BONDS | BF::Y_MINUS_WORLD_BONDS);
- damageLeafSupportActors(4, 4, 5, false, testActorVisibleChunks, nullptr, BF::ALL_INTERNAL_BONDS | BF::X_PLUS_WORLD_BONDS | BF::X_MINUS_WORLD_BONDS
- | BF::Y_PLUS_WORLD_BONDS | BF::Y_MINUS_WORLD_BONDS
- | BF::Z_PLUS_WORLD_BONDS | BF::Z_MINUS_WORLD_BONDS);
-}
+// 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 "BlastBaseTest.h"
+#include "AssetGenerator.h"
+
+#include <map>
+#include <algorithm>
+
+#include "NvBlastActor.h"
+#include "NvBlastExtDamageShaders.h"
+
+
+static bool chooseRandomGraphNodes(uint32_t* g, uint32_t count, const Nv::Blast::Actor& actor)
+{
+ const uint32_t graphNodeCount = actor.getGraphNodeCount();
+
+ if (graphNodeCount < count)
+ {
+ return false;
+ }
+
+ std::vector<uint32_t> graphNodeIndices(graphNodeCount);
+ uint32_t* index = graphNodeIndices.data();
+ for (Nv::Blast::Actor::GraphNodeIt i = actor; (bool)i ; ++i)
+ {
+ *index++ = (uint32_t)i;
+ }
+ struct UserDataSorter
+ {
+ UserDataSorter(const Nv::Blast::Actor& actor) : m_asset(*actor.getAsset()) {}
+
+ bool operator () (uint32_t i0, uint32_t i1) const
+ {
+ const uint32_t c0 = m_asset.m_graph.getChunkIndices()[i0];
+ const uint32_t c1 = m_asset.m_graph.getChunkIndices()[i1];
+ if (Nv::Blast::isInvalidIndex(c0) || Nv::Blast::isInvalidIndex(c1))
+ {
+ return c0 < c1;
+ }
+ return m_asset.getChunks()[c0].userData < m_asset.getChunks()[c1].userData;
+ }
+
+ const Nv::Blast::Asset& m_asset;
+ } userDataSorter(actor);
+ std::sort(graphNodeIndices.data(), graphNodeIndices.data() + graphNodeCount, userDataSorter);
+
+#if 0
+ std::vector<uint32_t> descUserData(graphNodeCount);
+ for (uint32_t i = 0; i < graphNodeCount; ++i)
+ {
+ descUserData[i] = actor.getAsset()->m_chunks[actor.getAsset()->m_graph.m_chunkIndices[graphNodeIndices[i]]].userData;
+ }
+#endif
+
+ uint32_t t = 0;
+ uint32_t m = 0;
+ for (uint32_t i = 0; i < graphNodeCount && m < count; ++i, ++t)
+ {
+ NVBLAST_ASSERT(t < graphNodeCount);
+ if (t >= graphNodeCount)
+ {
+ break;
+ }
+ const float U = (float)rand()/RAND_MAX; // U is uniform random number in [0,1)
+ if ((graphNodeCount - t)*U < count - m)
+ {
+ g[m++] = graphNodeIndices[i];
+ }
+ }
+
+ return m == count;
+}
+
+
+static void blast(std::set<NvBlastActor*>& actorsToDamage, GeneratorAsset* testAsset, GeneratorAsset::Vec3 localPos, float minRadius, float maxRadius, float compressiveDamage)
+{
+ std::vector<NvBlastChunkFractureData> chunkEvents; /* num lower-support chunks + bonds */
+ std::vector<NvBlastBondFractureData> bondEvents; /* num lower-support chunks + bonds */
+ chunkEvents.resize(testAsset->solverChunks.size());
+ bondEvents.resize(testAsset->solverBonds.size());
+
+
+ std::vector<char> splitScratch;
+ std::vector<NvBlastActor*> newActorsBuffer(testAsset->solverChunks.size());
+
+ NvBlastExtRadialDamageDesc damage = {
+ compressiveDamage,
+ { localPos.x, localPos.y, localPos.z },
+ minRadius,
+ maxRadius
+ };
+
+ NvBlastExtProgramParams programParams =
+ {
+ &damage,
+ nullptr
+ };
+
+ NvBlastDamageProgram program = {
+ NvBlastExtFalloffGraphShader,
+ nullptr
+ };
+
+ size_t totalNewActorsCount = 0;
+ for (std::set<NvBlastActor*>::iterator k = actorsToDamage.begin(); k != actorsToDamage.end();)
+ {
+ NvBlastActor* actor = *k;
+ NvBlastFractureBuffers events = { static_cast<uint32_t>(bondEvents.size()), static_cast<uint32_t>(chunkEvents.size()), bondEvents.data(), chunkEvents.data() };
+
+ NvBlastActorGenerateFracture(&events, actor, program, &programParams, nullptr, nullptr);
+ NvBlastActorApplyFracture(&events, actor, &events, nullptr, nullptr);
+ const bool isDamaged = NvBlastActorIsSplitRequired(actor, nullptr);
+ bool removeActor = false;
+
+ if (events.bondFractureCount + events.chunkFractureCount > 0)
+ {
+ NvBlastActorSplitEvent splitEvent;
+ splitEvent.newActors = &newActorsBuffer.data()[totalNewActorsCount];
+ uint32_t newActorSize = (uint32_t)(newActorsBuffer.size() - totalNewActorsCount);
+
+ splitScratch.resize((size_t)NvBlastActorGetRequiredScratchForSplit(actor, nullptr));
+ const size_t newActorsCount = NvBlastActorSplit(&splitEvent, actor, newActorSize, splitScratch.data(), nullptr, nullptr);
+ EXPECT_TRUE(isDamaged || newActorsCount == 0);
+ totalNewActorsCount += newActorsCount;
+ removeActor = splitEvent.deletedActor != NULL;
+ }
+ else
+ {
+ EXPECT_FALSE(isDamaged);
+ }
+
+ if (removeActor)
+ {
+ k = actorsToDamage.erase(k);
+ }
+ else
+ {
+ ++k;
+ }
+ }
+
+ for (size_t i = 0; i < totalNewActorsCount; ++i)
+ {
+ actorsToDamage.insert(newActorsBuffer[i]);
+ }
+}
+
+
+template<int FailLevel, int Verbosity>
+class ActorTest : public BlastBaseTest<FailLevel, Verbosity>
+{
+public:
+ ActorTest()
+ {
+
+ }
+
+ static void messageLog(int type, const char* msg, const char* file, int line)
+ {
+ BlastBaseTest<FailLevel, Verbosity>::messageLog(type, msg, file, line);
+ }
+
+ static void* alloc(size_t size)
+ {
+ return BlastBaseTest<FailLevel, Verbosity>::alignedZeroedAlloc(size);
+ }
+
+ static void free(void* mem)
+ {
+ BlastBaseTest<FailLevel, Verbosity>::alignedFree(mem);
+ }
+
+ NvBlastAsset* buildAsset(const NvBlastAssetDesc& desc)
+ {
+ // fix desc if wrong order or missing coverage first
+ NvBlastAssetDesc fixedDesc = desc;
+ std::vector<NvBlastChunkDesc> chunkDescs(desc.chunkDescs, desc.chunkDescs + desc.chunkCount);
+ std::vector<NvBlastBondDesc> bondDescs(desc.bondDescs, desc.bondDescs + desc.bondCount);
+ std::vector<uint32_t> chunkReorderMap(desc.chunkCount);
+ std::vector<char> scratch(desc.chunkCount * sizeof(NvBlastChunkDesc));
+ NvBlastEnsureAssetExactSupportCoverage(chunkDescs.data(), fixedDesc.chunkCount, scratch.data(), messageLog);
+ NvBlastReorderAssetDescChunks(chunkDescs.data(), fixedDesc.chunkCount, bondDescs.data(), fixedDesc.bondCount, chunkReorderMap.data(), true, scratch.data(), messageLog);
+ fixedDesc.chunkDescs = chunkDescs.data();
+ fixedDesc.bondDescs = bondDescs.empty() ? nullptr : bondDescs.data();
+
+ // create asset
+ m_scratch.resize((size_t)NvBlastGetRequiredScratchForCreateAsset(&fixedDesc, messageLog));
+ void* mem = alloc(NvBlastGetAssetMemorySize(&fixedDesc, messageLog));
+ NvBlastAsset* asset = NvBlastCreateAsset(mem, &fixedDesc, m_scratch.data(), messageLog);
+ EXPECT_TRUE(asset != nullptr);
+ return asset;
+ }
+
+ void buildAssets()
+ {
+ m_assets.resize(getAssetDescCount());
+ for (uint32_t i = 0; i < m_assets.size(); ++i)
+ {
+ m_assets[i] = buildAsset(g_assetDescs[i]);
+ }
+ }
+
+ NvBlastActor* instanceActor(const NvBlastAsset& asset)
+ {
+ NvBlastActorDesc actorDesc;
+ actorDesc.initialBondHealths = actorDesc.initialSupportChunkHealths = nullptr;
+ actorDesc.uniformInitialBondHealth = actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f;
+ void* fmem = alloc(NvBlastAssetGetFamilyMemorySize(&asset, nullptr));
+ NvBlastFamily* family = NvBlastAssetCreateFamily(fmem, &asset, nullptr);
+ std::vector<char> scratch((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, messageLog));
+ NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, scratch.data(), messageLog);
+ EXPECT_TRUE(actor != nullptr);
+ return actor;
+ }
+
+ void instanceActors()
+ {
+ m_actors.resize(m_assets.size());
+ for (uint32_t i = 0; i < m_actors.size(); ++i)
+ {
+ m_actors[i] = instanceActor(*m_assets[i]);
+ }
+ }
+
+ void releaseActors()
+ {
+ for (uint32_t i = 0; i < m_actors.size(); ++i)
+ {
+ NvBlastFamily* family = NvBlastActorGetFamily(m_actors[i], messageLog);
+
+ const bool actorReleaseResult = NvBlastActorDeactivate(m_actors[i], messageLog);
+ EXPECT_TRUE(actorReleaseResult);
+
+ free(family);
+ }
+ }
+
+ void destroyAssets()
+ {
+ for (uint32_t i = 0; i < m_assets.size(); ++i)
+ {
+ free(m_assets[i]);
+ }
+ }
+
+ void instanceAndPartitionRecursively
+ (
+ const NvBlastAsset& asset,
+ bool partitionToSubsupport,
+ void (*preSplitTest)(const Nv::Blast::Actor&, NvBlastLog),
+ void (*postSplitTest)(const std::vector<Nv::Blast::Actor*>&, uint32_t, uint32_t, bool)
+ )
+ {
+ const Nv::Blast::Asset& solverAsset = *static_cast<const Nv::Blast::Asset*>(&asset);
+
+ std::vector<Nv::Blast::Actor*> actors;
+ std::vector<Nv::Blast::Actor*> buffer(NvBlastAssetGetChunkCount(&asset, messageLog));
+
+ // Instance the first actor from the asset
+ actors.push_back(static_cast<Nv::Blast::Actor*>(instanceActor(asset)));
+
+ NvBlastFamily* family = NvBlastActorGetFamily(actors[0], messageLog);
+
+ const uint32_t supportChunkCount = NvBlastAssetGetSupportChunkCount(&asset, messageLog);
+ const uint32_t leafChunkCount = actors[0]->getAsset()->m_leafChunkCount;
+
+ // Now randomly partition the actors in the array, and keep going until we're down to single support chunks
+ bool canFracture = true;
+
+ while (canFracture)
+ {
+ canFracture = false;
+
+ for (uint32_t actorToPartition = 0; actorToPartition < actors.size(); ++actorToPartition)
+ {
+ Nv::Blast::Actor* a = (Nv::Blast::Actor*)actors[actorToPartition];
+ if (a == nullptr)
+ {
+ continue;
+ }
+
+ m_scratch.reserve((size_t)NvBlastActorGetRequiredScratchForSplit(a, messageLog));
+
+ if (preSplitTest)
+ {
+ preSplitTest(*a, nullptr);
+ }
+
+ const bool singleLowerSupportChunk = a->getGraphNodeCount() <= 1;
+ uint32_t newActorCount = 0;
+
+ for (int damageNum = 0; newActorCount < 2 && damageNum < 100; ++damageNum) // Avoid infinite loops
+ {
+ if (!singleLowerSupportChunk)
+ {
+ uint32_t g[2];
+ chooseRandomGraphNodes(g, 2, *a);
+ const uint32_t bondIndex = solverAsset.m_graph.findBond(g[0], g[1]);
+ if (bondIndex != Nv::Blast::invalidIndex<uint32_t>())
+ {
+ a->damageBond(g[0], g[1], bondIndex, 100.0f);
+ a->findIslands(m_scratch.data());
+ }
+ }
+ else
+ if (!partitionToSubsupport)
+ {
+ continue;
+ }
+
+ // Split actor
+ newActorCount = a->partition((Nv::Blast::Actor**)&buffer[0], (uint32_t)buffer.size(), messageLog);
+
+ if (newActorCount >= 2)
+ {
+ actors[actorToPartition] = nullptr;
+ }
+ }
+
+ if (newActorCount > 1)
+ {
+ canFracture = true;
+ }
+
+ for (uint32_t i = 0; i < newActorCount; ++i)
+ {
+ actors.push_back(buffer[i]);
+ buffer[i]->updateVisibleChunksFromGraphNodes();
+ }
+ }
+ }
+
+ if (postSplitTest)
+ {
+ postSplitTest(actors, leafChunkCount, supportChunkCount, partitionToSubsupport);
+ }
+
+ for (auto actor : actors)
+ {
+ if (actor)
+ actor->release();
+ }
+
+ free(family);
+ }
+
+ static void recursivePartitionPostSplitTestCounts(const std::vector<Nv::Blast::Actor*>& actors, uint32_t leafChunkCount, uint32_t supportChunkCount, bool partitionToSubsupport)
+ {
+ // Test to see that all actors are split down to single support chunks
+ uint32_t remainingActorCount = 0;
+ for (uint32_t i = 0; i < actors.size(); ++i)
+ {
+ Nv::Blast::Actor* a = (Nv::Blast::Actor*)actors[i];
+ if (a == nullptr)
+ {
+ continue;
+ }
+
+ ++remainingActorCount;
+
+ NVBLAST_ASSERT(1 == a->getVisibleChunkCount() || a->isBoundToWorld());
+ EXPECT_TRUE(1 == a->getVisibleChunkCount() || a->isBoundToWorld());
+ if (!partitionToSubsupport)
+ {
+ EXPECT_EQ(1, a->getGraphNodeCount());
+ }
+
+ if (0 == a->getVisibleChunkCount())
+ {
+ EXPECT_TRUE(a->isBoundToWorld());
+ EXPECT_EQ(1, a->getGraphNodeCount());
+ EXPECT_EQ(a->getFamilyHeader()->m_asset->m_graph.m_nodeCount - 1, a->getFirstGraphNodeIndex());
+ --remainingActorCount; // Do not count this as a remaining actor, to be compared with leaf or support chunk counts later
+ }
+
+ const bool actorReleaseResult = NvBlastActorDeactivate(actors[i], nullptr);
+ EXPECT_TRUE(actorReleaseResult);
+ }
+
+ if (partitionToSubsupport)
+ {
+ EXPECT_EQ(leafChunkCount, remainingActorCount);
+ }
+ else
+ {
+ EXPECT_EQ(supportChunkCount, remainingActorCount);
+ }
+ }
+
+ static void testActorVisibleChunks(const Nv::Blast::Actor& actor, NvBlastLog)
+ {
+ const Nv::Blast::Asset& asset = *actor.getAsset();
+ const NvBlastChunk* chunks = asset.getChunks();
+
+ if (actor.isSubSupportChunk())
+ {
+ EXPECT_EQ(1, actor.getVisibleChunkCount());
+
+ const uint32_t firstVisibleChunkIndex = (uint32_t)Nv::Blast::Actor::VisibleChunkIt(actor);
+
+ EXPECT_EQ(actor.getIndex() - asset.m_graph.m_nodeCount, firstVisibleChunkIndex - asset.m_firstSubsupportChunkIndex);
+
+ // Make sure the visible chunk is subsupport
+ // Array of support flags
+ std::vector<bool> isSupport(asset.m_chunkCount, false);
+ for (uint32_t i = 0; i < asset.m_graph.m_nodeCount; ++i)
+ {
+ const uint32_t chunkIndex = asset.m_graph.getChunkIndices()[i];
+ if (!Nv::Blast::isInvalidIndex(chunkIndex))
+ {
+ isSupport[chunkIndex] = true;
+ }
+ }
+
+ // Climb hierarchy to find support chunk
+ uint32_t chunkIndex = firstVisibleChunkIndex;
+ while (chunkIndex != Nv::Blast::invalidIndex<uint32_t>())
+ {
+ if (isSupport[chunkIndex])
+ {
+ break;
+ }
+ chunkIndex = chunks[chunkIndex].parentChunkIndex;
+ }
+
+ EXPECT_FALSE(Nv::Blast::isInvalidIndex(chunkIndex));
+ }
+ else
+ {
+ // Array of visibility flags
+ std::vector<bool> isVisible(asset.m_chunkCount, false);
+ for (Nv::Blast::Actor::VisibleChunkIt i = actor; (bool)i; ++i)
+ {
+ isVisible[(uint32_t)i] = true;
+ }
+
+ // Mark visible nodes representing graph chunks
+ std::vector<bool> visibleChunkFound(asset.m_chunkCount, false);
+
+ // Make sure every graph chunk is represented by a visible chunk, or represents the world
+ for (Nv::Blast::Actor::GraphNodeIt i = actor; (bool)i; ++i)
+ {
+ const uint32_t graphNodeIndex = (uint32_t)i;
+ uint32_t chunkIndex = asset.m_graph.getChunkIndices()[graphNodeIndex];
+ // Climb hierarchy to find visible chunk
+ while (chunkIndex != Nv::Blast::invalidIndex<uint32_t>())
+ {
+ // Check that chunk owners are accurate
+ EXPECT_EQ(actor.getIndex(), actor.getFamilyHeader()->getChunkActorIndices()[chunkIndex]);
+ if (isVisible[chunkIndex])
+ {
+ visibleChunkFound[chunkIndex] = true;
+ break;
+ }
+ chunkIndex = chunks[chunkIndex].parentChunkIndex;
+ }
+ EXPECT_TRUE(!Nv::Blast::isInvalidIndex(chunkIndex) || (graphNodeIndex == asset.m_graph.m_nodeCount-1 && actor.isBoundToWorld()));
+ }
+
+ // Check that all visible chunks are accounted for
+ for (uint32_t i = 0; i < asset.m_chunkCount; ++i)
+ {
+ EXPECT_EQ(visibleChunkFound[i], isVisible[i]);
+ }
+
+ // Make sure that, if all siblings are intact, they are invisible
+ for (uint32_t i = 0; i < asset.m_chunkCount; ++i)
+ {
+ bool allIntact = true;
+ bool noneVisible = true;
+ if (chunks[i].firstChildIndex < asset.getUpperSupportChunkCount()) // Do not check subsupport
+ {
+ for (uint32_t j = chunks[i].firstChildIndex; j < chunks[i].childIndexStop; ++j)
+ {
+ allIntact = allIntact && actor.getFamilyHeader()->getChunkActorIndices()[j] == actor.getIndex();
+ noneVisible = noneVisible && !isVisible[j];
+ }
+ EXPECT_TRUE(!allIntact || noneVisible);
+ }
+ }
+ }
+ }
+
+ static void recursivePartitionPostSplitTestVisibleChunks(const std::vector<Nv::Blast::Actor*>& actors, uint32_t leafChunkCount, uint32_t supportChunkCount, bool partitionToSubsupport)
+ {
+ for (uint32_t i = 0; i < actors.size(); ++i)
+ {
+ Nv::Blast::Actor* a = (Nv::Blast::Actor*)actors[i];
+ if (a == nullptr)
+ {
+ continue;
+ }
+
+ testActorVisibleChunks(*a, nullptr);
+ }
+ }
+
+ void partitionActorsToSupportChunks
+ (
+ uint32_t assetDescCount,
+ const NvBlastAssetDesc* assetDescs,
+ void(*preSplitTest)(const Nv::Blast::Actor&, NvBlastLog),
+ void(*postSplitTest)(const std::vector<Nv::Blast::Actor*>&, uint32_t, uint32_t, bool),
+ bool partitionToSubsupport
+ )
+ {
+ srand(0);
+
+ for (uint32_t i = 0; i < assetDescCount; ++i)
+ {
+ // Create an asset
+ NvBlastAsset* asset = buildAsset(assetDescs[i]);
+
+ // Perform repeated partitioning
+ instanceAndPartitionRecursively(*asset, partitionToSubsupport, preSplitTest, postSplitTest);
+
+ // Free the asset
+ free(asset);
+ }
+ }
+
+ static void compareFamilies(const NvBlastFamily* family1, const NvBlastFamily* family2, size_t size, NvBlastLog logFn)
+ {
+ const char* block1 = reinterpret_cast<const char*>(family1);
+ const char* block2 = reinterpret_cast<const char*>(family2);
+#if 0
+ EXPECT_EQ(0, memcmp(block1, block2, size));
+#else
+ bool diffFound = false;
+ size_t startDiff = 0;
+ for (size_t i = 0; i < size; ++i)
+ {
+ if (block1[i] != block2[i])
+ {
+ diffFound = true;
+ startDiff = i;
+ break;
+ }
+ }
+ if (!diffFound)
+ {
+ return;
+ }
+ size_t endDiff = startDiff;
+ for (size_t i = size; i--;)
+ {
+ if (block1[i] != block2[i])
+ {
+ endDiff = i;
+ break;
+ }
+ }
+ std::ostringstream msg;
+ msg << "Block deserialization does not match current block in position range [" << startDiff << ", " << endDiff << "].";
+ logFn(NvBlastMessage::Error, msg.str().c_str(), __FILE__, __LINE__);
+#endif
+ }
+
+ static void testActorBlockSerialize(std::vector<NvBlastActor*>& actors, NvBlastLog logFn)
+ {
+ if (actors.size())
+ {
+ const NvBlastFamily* family = NvBlastActorGetFamily(actors[0], logFn);
+ const uint32_t size = NvBlastFamilyGetSize(family, logFn);
+ s_storage.insert(s_storage.end(), (char*)family, (char*)family + size);
+ }
+ }
+
+ static void testActorBlockDeserialize(std::vector<NvBlastActor*>& actors, NvBlastLog logFn)
+ {
+ if (actors.size())
+ {
+ EXPECT_LT(s_curr, s_storage.size());
+ const NvBlastFamily* family = reinterpret_cast<NvBlastFamily*>(&s_storage[s_curr]);
+ const uint32_t size = NvBlastFamilyGetSize(family, logFn);
+ EXPECT_LE(s_curr + size, s_storage.size());
+ s_curr += size;
+ const NvBlastFamily* actorFamily = NvBlastActorGetFamily(actors[0], logFn);
+ // Family may contain different assets pointers, copy into new family block and set the same asset before comparing
+ Nv::Blast::Actor& a = *static_cast<Nv::Blast::Actor*>(actors[0]);
+ const Nv::Blast::Asset* solverAsset = a.getAsset();
+ std::vector<char> storageFamilyCopy((char*)family, (char*)family + size);
+ NvBlastFamily* storageFamily = reinterpret_cast<NvBlastFamily*>(storageFamilyCopy.data());
+ NvBlastFamilySetAsset(storageFamily, solverAsset, logFn);
+ {
+ const uint32_t actorCountExpected = NvBlastFamilyGetActorCount(storageFamily, logFn);
+ std::vector<NvBlastActor*> blockActors(actorCountExpected);
+ const uint32_t actorCountReturned = NvBlastFamilyGetActors(blockActors.data(), actorCountExpected, storageFamily, logFn);
+ EXPECT_EQ(actorCountExpected, actorCountReturned);
+ }
+ compareFamilies(storageFamily, actorFamily, size, logFn);
+ }
+ }
+
+ // Serialize all actors and then deserialize back into a new family in a random order, and compare with the original family
+ static void testActorSerializationNewFamily(std::vector<NvBlastActor*>& actors, NvBlastLog logFn)
+ {
+ if (actors.size() == 0)
+ {
+ return;
+ }
+
+ Nv::Blast::Actor& a = *static_cast<Nv::Blast::Actor*>(actors[0]);
+ const Nv::Blast::Asset* solverAsset = a.getAsset();
+
+ const uint32_t serSizeBound = NvBlastAssetGetActorSerializationSizeUpperBound(solverAsset, logFn);
+
+ std::vector< std::vector<char> > streams(actors.size());
+ for (size_t i = 0; i < actors.size(); ++i)
+ {
+ const uint32_t serSize = NvBlastActorGetSerializationSize(actors[i], logFn);
+ EXPECT_GE(serSizeBound, serSize);
+ std::vector<char>& stream = streams[i];
+ stream.resize(serSize);
+ const uint32_t bytesWritten = NvBlastActorSerialize(stream.data(), serSize, actors[i], logFn);
+ EXPECT_EQ(serSize, bytesWritten);
+ }
+
+ void* fmem = alloc(NvBlastAssetGetFamilyMemorySize(solverAsset, logFn));
+ NvBlastFamily* newFamily = NvBlastAssetCreateFamily(fmem, solverAsset, logFn);
+
+ std::vector<size_t> order(actors.size());
+ for (size_t i = 0; i < order.size(); ++i)
+ {
+ order[i] = i;
+ }
+ std::random_shuffle(order.begin(), order.end());
+
+ for (size_t i = 0; i < actors.size(); ++i)
+ {
+ NvBlastActor* newActor = NvBlastFamilyDeserializeActor(newFamily, streams[order[i]].data(), logFn);
+ EXPECT_TRUE(newActor != nullptr);
+ }
+
+ const NvBlastFamily* oldFamily = NvBlastActorGetFamily(&a, logFn);
+ compareFamilies(oldFamily, newFamily, NvBlastFamilyGetSize(oldFamily, logFn), logFn);
+
+ free(newFamily);
+ }
+
+ // Copy the family and then serialize some subset of actors, deleting them afterwards.
+ // Then, deserialize back into the block and compare the original and new families.
+ static void testActorSerializationPartialBlock(std::vector<NvBlastActor*>& actors, NvBlastLog logFn)
+ {
+ if (actors.size() <= 1)
+ {
+ return;
+ }
+
+ Nv::Blast::Actor& a = *static_cast<Nv::Blast::Actor*>(actors[0]);
+ const Nv::Blast::Asset* solverAsset = a.getAsset();
+
+ const NvBlastFamily* oldFamily = NvBlastActorGetFamily(&a, logFn);
+ const uint32_t size = NvBlastFamilyGetSize(oldFamily, logFn);
+ std::vector<char> buffer((char*)oldFamily, (char*)oldFamily + size);
+ NvBlastFamily* familyCopy = reinterpret_cast<NvBlastFamily*>(buffer.data());
+
+ const uint32_t serCount = 1 + (rand() % actors.size() - 1);
+
+ const uint32_t actorCount = NvBlastFamilyGetActorCount(familyCopy, logFn);
+ std::vector<NvBlastActor*> actorsRemaining(actorCount);
+ const uint32_t actorsInFamily = NvBlastFamilyGetActors(&actorsRemaining[0], actorCount, familyCopy, logFn);
+ EXPECT_EQ(actorCount, actorsInFamily);
+
+ const uint32_t serSizeBound = NvBlastAssetGetActorSerializationSizeUpperBound(solverAsset, logFn);
+
+ std::vector< std::vector<char> > streams(serCount);
+ for (uint32_t i = 0; i < serCount; ++i)
+ {
+ std::vector<char>& stream = streams[i];
+ const uint32_t indexToStream = rand() % actorsRemaining.size();
+ NvBlastActor* actorToStream = actorsRemaining[indexToStream];
+ std::swap(actorsRemaining[indexToStream], actorsRemaining[actorsRemaining.size() - 1]);
+ actorsRemaining.pop_back();
+ const uint32_t serSize = NvBlastActorGetSerializationSize(actorToStream, logFn);
+ EXPECT_GE(serSizeBound, serSize);
+ stream.resize(serSize);
+ const uint32_t bytesWritten = NvBlastActorSerialize(&stream[0], serSize, actorToStream, logFn);
+ EXPECT_EQ(serSize, bytesWritten);
+ NvBlastActorDeactivate(actorToStream, logFn);
+ }
+
+ for (uint32_t i = 0; i < serCount; ++i)
+ {
+ NvBlastActor* newActor = NvBlastFamilyDeserializeActor(familyCopy, streams[i].data(), logFn);
+ EXPECT_TRUE(newActor != nullptr);
+ }
+
+ compareFamilies(oldFamily, familyCopy, size, logFn);
+ }
+
+ void damageLeafSupportActors
+ (
+ uint32_t assetCount,
+ uint32_t familyCount,
+ uint32_t damageCount,
+ bool simple,
+ void (*actorTest)(const Nv::Blast::Actor&, NvBlastLog),
+ void (*postDamageTest)(std::vector<NvBlastActor*>&, NvBlastLog),
+ CubeAssetGenerator::BondFlags bondFlags = CubeAssetGenerator::BondFlags::ALL_INTERNAL_BONDS
+ )
+ {
+ const float relativeDamageRadius = simple ? 0.75f : 0.2f;
+ const float compressiveDamage = 1.0f;
+ const uint32_t minChunkCount = simple ? 9 : 100;
+ const uint32_t maxChunkCount = simple ? 9 : 10000;
+ const bool printActorCount = false;
+
+ srand(0);
+
+ std::cout << "Asset # (out of " << assetCount << "): ";
+ for (uint32_t assetNum = 0; assetNum < assetCount; ++assetNum)
+ {
+ std::cout << assetNum + 1 << ".. ";
+ CubeAssetGenerator::Settings settings;
+ settings.extents = GeneratorAsset::Vec3(1, 1, 1);
+ settings.bondFlags = bondFlags;
+ CubeAssetGenerator::DepthInfo depthInfo;
+ depthInfo.slicesPerAxis = GeneratorAsset::Vec3(1, 1, 1);
+ depthInfo.flag = NvBlastChunkDesc::Flags::NoFlags;
+ settings.depths.push_back(depthInfo);
+ uint32_t chunkCount = 1;
+ while (chunkCount < minChunkCount)
+ {
+ uint32_t chunkMul;
+ do
+ {
+ depthInfo.slicesPerAxis = simple ? GeneratorAsset::Vec3(2, 2, 2) : GeneratorAsset::Vec3((float)(1 + rand() % 4), (float)(1 + rand() % 4), (float)(1 + rand() % 4));
+ chunkMul = (uint32_t)(depthInfo.slicesPerAxis.x * depthInfo.slicesPerAxis.y * depthInfo.slicesPerAxis.z);
+ } while (chunkMul == 1);
+ if (chunkCount*chunkMul > maxChunkCount)
+ {
+ break;
+ }
+ chunkCount *= chunkMul;
+ settings.depths.push_back(depthInfo);
+ settings.extents = settings.extents * depthInfo.slicesPerAxis;
+ }
+ settings.depths.back().flag = NvBlastChunkDesc::SupportFlag; // Leaves are support
+
+ // Make largest direction unit size
+ settings.extents = settings.extents * (1.0f / std::max(settings.extents.x, std::max(settings.extents.y, settings.extents.z)));
+
+ // Create asset
+ GeneratorAsset testAsset;
+ CubeAssetGenerator::generate(testAsset, settings);
+
+ NvBlastAssetDesc desc;
+ desc.chunkDescs = testAsset.solverChunks.data();
+ desc.chunkCount = (uint32_t)testAsset.solverChunks.size();
+ desc.bondDescs = testAsset.solverBonds.data();
+ desc.bondCount = (uint32_t)testAsset.solverBonds.size();
+ NvBlastAsset* asset = buildAsset(desc);
+ NvBlastID assetID = NvBlastAssetGetID(asset, messageLog);
+
+ // copy asset (for setAsset testing)
+ const char* data = (const char*)asset;
+ const uint32_t dataSize = NvBlastAssetGetSize(asset, messageLog);
+ char* duplicateData = (char*)alloc(dataSize);
+ memcpy(duplicateData, data, dataSize);
+ NvBlastAsset* assetDuplicate = (NvBlastAsset*)duplicateData;
+
+ // Generate families
+ for (uint32_t familyNum = 0; familyNum < familyCount; ++familyNum)
+ {
+ // family
+ void* fmem = alloc(NvBlastAssetGetFamilyMemorySize(asset, messageLog));
+ NvBlastFamily* family = NvBlastAssetCreateFamily(fmem, asset, messageLog); // Using zeroingAlloc in case actorTest compares memory blocks
+ NvBlastID id = NvBlastFamilyGetAssetID(family, messageLog);
+ EXPECT_TRUE(!memcmp(&assetID, &id, sizeof(NvBlastID)));
+ if (rand() % 2 == 0)
+ {
+ // replace asset with duplicate in half of cases to test setAsset
+ NvBlastFamilySetAsset(family, assetDuplicate, messageLog);
+ NvBlastID id2 = NvBlastFamilyGetAssetID(family, messageLog);
+ EXPECT_TRUE(!memcmp(&assetID, &id2, sizeof(NvBlastID)));
+ }
+
+ // actor
+ NvBlastActorDesc actorDesc;
+ actorDesc.initialBondHealths = actorDesc.initialSupportChunkHealths = nullptr;
+ actorDesc.uniformInitialBondHealth = actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f;
+ m_scratch.resize((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, messageLog));
+ NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, m_scratch.data(), messageLog);
+ EXPECT_TRUE(actor != nullptr);
+
+ // Generate damage
+ std::set<NvBlastActor*> actors;
+ actors.insert(actor);
+ if (printActorCount) std::cout << "Actors: 1.. ";
+ for (uint32_t damageNum = 0; damageNum < damageCount; ++damageNum)
+ {
+ GeneratorAsset::Vec3 localPos = settings.extents*GeneratorAsset::Vec3((float)rand() / RAND_MAX - 0.5f, (float)rand() / RAND_MAX - 0.5f, (float)rand() / RAND_MAX - 0.5f);
+ blast(actors, &testAsset, localPos, relativeDamageRadius, relativeDamageRadius*1.2f, compressiveDamage);
+ if (printActorCount) std::cout << actors.size() << ".. ";
+ if (actors.size() > 0)
+ {
+ const NvBlastFamily* family = NvBlastActorGetFamily(*actors.begin(), messageLog);
+ const uint32_t actorCount = NvBlastFamilyGetActorCount(family, messageLog);
+ EXPECT_EQ((uint32_t)actors.size(), actorCount);
+ if ((uint32_t)actors.size() == actorCount)
+ {
+ std::vector<NvBlastActor*> buffer1(actorCount);
+ const uint32_t actorsWritten = NvBlastFamilyGetActors(&buffer1[0], actorCount, family, messageLog);
+ EXPECT_EQ(actorsWritten, actorCount);
+ std::vector<NvBlastActor*> buffer2(actors.begin(), actors.end());
+ EXPECT_EQ(0, memcmp(&buffer1[0], buffer2.data(), actorCount*sizeof(NvBlastActor*)));
+ }
+ }
+ // Test individual actors
+ if (actorTest != nullptr)
+ {
+ for (std::set<NvBlastActor*>::iterator k = actors.begin(); k != actors.end(); ++k)
+ {
+ actorTest(*static_cast<Nv::Blast::Actor*>(*k), messageLog);
+ }
+ }
+ }
+ if (printActorCount) std::cout << "\n";
+
+ // Test fractured actor set
+ if (postDamageTest)
+ {
+ std::vector<NvBlastActor*> actorArray(actors.begin(), actors.end());
+ postDamageTest(actorArray, messageLog);
+ }
+
+ // Release remaining actors
+ for (std::set<NvBlastActor*>::iterator k = actors.begin(); k != actors.end(); ++k)
+ {
+ NvBlastActorDeactivate(*k, messageLog);
+ }
+ actors.clear();
+
+ free(family);
+ }
+
+ // Release asset data
+ free(asset);
+ free(assetDuplicate);
+ }
+ std::cout << "done.\n";
+ }
+
+ std::vector<NvBlastAsset*> m_assets;
+ std::vector<NvBlastActor*> m_actors;
+ std::vector<char> m_scratch;
+ static std::vector<char> s_storage;
+
+ static size_t s_curr;
+};
+
+// Static values
+template<int FailLevel, int Verbosity>
+std::vector<char> ActorTest<FailLevel, Verbosity>::s_storage;
+
+template<int FailLevel, int Verbosity>
+size_t ActorTest<FailLevel, Verbosity>::s_curr;
+
+// Specializations
+typedef ActorTest<NvBlastMessage::Error, 1> ActorTestAllowWarnings;
+typedef ActorTest<NvBlastMessage::Warning, 1> ActorTestStrict;
+
+// Tests
+TEST_F(ActorTestStrict, InstanceActors)
+{
+ // Build assets and instance actors
+ buildAssets();
+ instanceActors();
+
+ // Release actors and destroy assets
+ releaseActors();
+ destroyAssets();
+}
+
+TEST_F(ActorTestAllowWarnings, ActorHealthInitialization)
+{
+ // Test all assets
+ std::vector<NvBlastAssetDesc> assetDescs;
+ assetDescs.insert(assetDescs.end(), g_assetDescs, g_assetDescs + getAssetDescCount());
+ assetDescs.insert(assetDescs.end(), g_assetDescsMissingCoverage, g_assetDescsMissingCoverage + getAssetDescMissingCoverageCount());
+
+ struct TestMode
+ {
+ enum Enum
+ {
+ Uniform,
+ Nonuniform,
+
+ Count
+ };
+ };
+
+ for (auto assetDesc : assetDescs)
+ {
+ NvBlastAsset* asset = buildAsset(assetDesc);
+ EXPECT_TRUE(asset != nullptr);
+
+ Nv::Blast::Asset& assetInt = static_cast<Nv::Blast::Asset&>(*asset);
+
+ NvBlastSupportGraph graph = NvBlastAssetGetSupportGraph(asset, nullptr);
+
+ std::vector<float> supportChunkHealths(graph.nodeCount);
+ for (size_t i = 0; i < supportChunkHealths.size(); ++i)
+ {
+ supportChunkHealths[i] = 1.0f + (float)i;
+ }
+
+ std::vector<float> bondHealths(assetInt.getBondCount());
+ for (size_t i = 0; i < bondHealths.size(); ++i)
+ {
+ bondHealths[i] = 1.5f + (float)i;
+ }
+
+ for (int chunkTestMode = 0; chunkTestMode < TestMode::Count; ++chunkTestMode)
+ {
+ for (int bondTestMode = 0; bondTestMode < TestMode::Count; ++bondTestMode)
+ {
+ NvBlastActorDesc actorDesc;
+
+ switch (chunkTestMode)
+ {
+ default:
+ case TestMode::Uniform:
+ actorDesc.initialSupportChunkHealths = nullptr;
+ actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f;
+ break;
+ case TestMode::Nonuniform:
+ actorDesc.initialSupportChunkHealths = supportChunkHealths.data();
+ break;
+ }
+
+ switch (bondTestMode)
+ {
+ default:
+ case TestMode::Uniform:
+ actorDesc.initialBondHealths = nullptr;
+ actorDesc.uniformInitialBondHealth = 2.0f;
+ break;
+ case TestMode::Nonuniform:
+ actorDesc.initialBondHealths = bondHealths.data();
+ break;
+ }
+
+ void* fmem = alloc(NvBlastAssetGetFamilyMemorySize(asset, messageLog));
+ NvBlastFamily* family = NvBlastAssetCreateFamily(fmem, asset, nullptr);
+ std::vector<char> scratch((size_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(family, messageLog));
+ NvBlastActor* actor = NvBlastFamilyCreateFirstActor(family, &actorDesc, scratch.data(), messageLog);
+ EXPECT_TRUE(actor != nullptr);
+
+ Nv::Blast::Actor& actorInt = static_cast<Nv::Blast::Actor&>(*actor);
+ Nv::Blast::FamilyHeader* header = actorInt.getFamilyHeader();
+
+
+ for (uint32_t i = 0; i < graph.nodeCount; ++i)
+ {
+ const uint32_t supportChunkIndex = graph.chunkIndices[i];
+ for (Nv::Blast::Asset::DepthFirstIt it(assetInt, supportChunkIndex); (bool)it; ++it)
+ {
+ const uint32_t chunkIndex = (uint32_t)it;
+ const uint32_t lowerSupportIndex = assetInt.getContiguousLowerSupportIndex(chunkIndex);
+ NVBLAST_ASSERT(lowerSupportIndex < assetInt.getLowerSupportChunkCount());
+ const float health = header->getLowerSupportChunkHealths()[lowerSupportIndex];
+ switch (chunkTestMode)
+ {
+ default:
+ case TestMode::Uniform:
+ EXPECT_EQ(1.0f, health);
+ break;
+ case TestMode::Nonuniform:
+ EXPECT_EQ(supportChunkHealths[i], health);
+ break;
+ }
+ }
+ }
+
+ for (uint32_t i = 0; i < assetInt.getBondCount(); ++i)
+ {
+ switch (bondTestMode)
+ {
+ default:
+ case TestMode::Uniform:
+ EXPECT_EQ(2.0f, header->getBondHealths()[i]);
+ break;
+ case TestMode::Nonuniform:
+ EXPECT_EQ(bondHealths[i], header->getBondHealths()[i]);
+ break;
+ }
+ }
+
+ NvBlastActorDeactivate(actor, messageLog);
+ free(family);
+ }
+ }
+
+ free(asset);
+ }
+}
+
+TEST_F(ActorTestStrict, PartitionActorsToSupportChunksTestCounts)
+{
+ partitionActorsToSupportChunks(getAssetDescCount(), g_assetDescs, nullptr, recursivePartitionPostSplitTestCounts, false);
+}
+
+TEST_F(ActorTestAllowWarnings, PartitionActorsFromBadDescriptorsToSupportChunksTestCounts)
+{
+ partitionActorsToSupportChunks(getAssetDescMissingCoverageCount(), g_assetDescsMissingCoverage, nullptr, recursivePartitionPostSplitTestCounts, false);
+}
+
+TEST_F(ActorTestStrict, PartitionActorsToLeafChunksTestCounts)
+{
+ partitionActorsToSupportChunks(getAssetDescCount(), g_assetDescs, nullptr, recursivePartitionPostSplitTestCounts, true);
+}
+
+TEST_F(ActorTestAllowWarnings, PartitionActorsFromBadDescriptorsToLeafChunksTestCounts)
+{
+ partitionActorsToSupportChunks(getAssetDescMissingCoverageCount(), g_assetDescsMissingCoverage, nullptr, recursivePartitionPostSplitTestCounts, true);
+}
+
+TEST_F(ActorTestStrict, PartitionActorsToSupportChunksTestVisibility)
+{
+ partitionActorsToSupportChunks(getAssetDescCount(), g_assetDescs, testActorVisibleChunks, recursivePartitionPostSplitTestVisibleChunks, false);
+}
+
+TEST_F(ActorTestAllowWarnings, PartitionActorsFromBadDescriptorsToSupportChunksTestVisibility)
+{
+ partitionActorsToSupportChunks(getAssetDescMissingCoverageCount(), g_assetDescsMissingCoverage, testActorVisibleChunks, recursivePartitionPostSplitTestVisibleChunks, false);
+}
+
+TEST_F(ActorTestStrict, PartitionActorsToLeafChunksTestVisibility)
+{
+ partitionActorsToSupportChunks(getAssetDescCount(), g_assetDescs, testActorVisibleChunks, recursivePartitionPostSplitTestVisibleChunks, true);
+}
+
+TEST_F(ActorTestAllowWarnings, PartitionActorsFromBadDescriptorsToLeafChunksTestVisibility)
+{
+ partitionActorsToSupportChunks(getAssetDescMissingCoverageCount(), g_assetDescsMissingCoverage, testActorVisibleChunks, recursivePartitionPostSplitTestVisibleChunks, true);
+}
+
+TEST_F(ActorTestStrict, DamageLeafSupportActorsTestVisibility)
+{
+ damageLeafSupportActors(4, 4, 5, false, testActorVisibleChunks, nullptr);
+}
+
+TEST_F(ActorTestStrict, DamageLeafSupportActorTestBlockSerialization)
+{
+ s_storage.resize(0);
+ damageLeafSupportActors(4, 4, 5, false, nullptr, testActorBlockSerialize);
+ s_curr = 0;
+ damageLeafSupportActors(4, 4, 5, false, nullptr, testActorBlockDeserialize);
+ s_storage.resize(0);
+}
+
+TEST_F(ActorTestStrict, DamageSimpleLeafSupportActorTestActorSerializationNewFamily)
+{
+ damageLeafSupportActors(1, 1, 4, true, nullptr, testActorSerializationNewFamily);
+}
+
+TEST_F(ActorTestStrict, DamageSimpleLeafSupportActorTestActorSerializationPartialBlock)
+{
+ damageLeafSupportActors(1, 1, 4, true, nullptr, testActorSerializationPartialBlock);
+}
+
+TEST_F(ActorTestStrict, DamageLeafSupportActorTestActorSerializationNewFamily)
+{
+ damageLeafSupportActors(4, 4, 4, false, nullptr, testActorSerializationNewFamily);
+}
+
+TEST_F(ActorTestStrict, DamageLeafSupportActorTestActorSerializationPartialBlock)
+{
+ damageLeafSupportActors(4, 4, 4, false, nullptr, testActorSerializationPartialBlock);
+}
+
+TEST_F(ActorTestStrict, DamageMultipleIslandLeafSupportActorsTestVisibility)
+{
+ typedef CubeAssetGenerator::BondFlags BF;
+ damageLeafSupportActors(4, 4, 5, false, testActorVisibleChunks, nullptr, BF::Y_BONDS | BF::Z_BONDS); // Only connect y-z plane islands
+ damageLeafSupportActors(4, 4, 5, false, testActorVisibleChunks, nullptr, BF::Z_BONDS); // Only connect z-direction islands
+ damageLeafSupportActors(4, 4, 5, false, testActorVisibleChunks, nullptr, BF::NO_BONDS); // All support chunks disconnected (single-chunk islands)
+}
+
+TEST_F(ActorTestStrict, DamageBoundToWorldLeafSupportActorsTestVisibility)
+{
+ typedef CubeAssetGenerator::BondFlags BF;
+ damageLeafSupportActors(4, 4, 5, false, testActorVisibleChunks, nullptr, BF::ALL_INTERNAL_BONDS | BF::X_MINUS_WORLD_BONDS);
+ damageLeafSupportActors(4, 4, 5, false, testActorVisibleChunks, nullptr, BF::ALL_INTERNAL_BONDS | BF::Y_PLUS_WORLD_BONDS);
+ damageLeafSupportActors(4, 4, 5, false, testActorVisibleChunks, nullptr, BF::ALL_INTERNAL_BONDS | BF::Z_MINUS_WORLD_BONDS);
+ damageLeafSupportActors(4, 4, 5, false, testActorVisibleChunks, nullptr, BF::ALL_INTERNAL_BONDS | BF::X_PLUS_WORLD_BONDS | BF::Y_MINUS_WORLD_BONDS);
+ damageLeafSupportActors(4, 4, 5, false, testActorVisibleChunks, nullptr, BF::ALL_INTERNAL_BONDS | BF::X_PLUS_WORLD_BONDS | BF::X_MINUS_WORLD_BONDS
+ | BF::Y_PLUS_WORLD_BONDS | BF::Y_MINUS_WORLD_BONDS
+ | BF::Z_PLUS_WORLD_BONDS | BF::Z_MINUS_WORLD_BONDS);
+}