// 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 "NvBlastActor.h" #include "NvBlastFamilyGraph.h" #include "NvBlastChunkHierarchy.h" #include "NvBlastIndexFns.h" #include "NvBlastDLink.h" #include "NvBlastGeometry.h" #include "NvBlastTime.h" #include #include namespace Nv { namespace Blast { //////// Actor static methods //////// size_t Actor::createRequiredScratch(const NvBlastFamily* family) { #if NVBLASTLL_CHECK_PARAMS if (family == nullptr || reinterpret_cast(family)->m_asset == nullptr) { NVBLAST_ALWAYS_ASSERT(); return 0; } #endif const Asset& solverAsset = *reinterpret_cast(family)->m_asset; return FamilyGraph::findIslandsRequiredScratch(solverAsset.m_graph.m_nodeCount); } Actor* Actor::create(NvBlastFamily* family, const NvBlastActorDesc* desc, void* scratch, NvBlastLog logFn) { NVBLASTLL_CHECK(family != nullptr, logFn, "Actor::create: NULL family pointer input.", return nullptr); NVBLASTLL_CHECK(reinterpret_cast(family)->m_asset != nullptr, logFn, "Actor::create: family has NULL asset.", return nullptr); NVBLASTLL_CHECK(reinterpret_cast(family)->m_asset->m_graph.m_nodeCount != 0, logFn, "Actor::create: family's asset has no support chunks.", return nullptr); NVBLASTLL_CHECK(desc != nullptr, logFn, "Actor::create: NULL desc pointer input.", return nullptr); NVBLASTLL_CHECK(scratch != nullptr, logFn, "Actor::create: NULL scratch input.", return nullptr); FamilyHeader* header = reinterpret_cast(family); if (header->m_actorCount > 0) { NVBLASTLL_LOG_ERROR(logFn, "Actor::create: input family is not empty."); return nullptr; } const Asset& solverAsset = *static_cast(header->m_asset); const SupportGraph& graph = solverAsset.m_graph; // Lower support chunk healths - initialize float* lowerSupportChunkHealths = header->getLowerSupportChunkHealths(); if (desc->initialSupportChunkHealths != nullptr) // Health array given { const uint32_t* supportChunkIndices = graph.getChunkIndices(); for (uint32_t supportChunkNum = 0; supportChunkNum < graph.m_nodeCount; ++supportChunkNum) { const float initialHealth = desc->initialSupportChunkHealths[supportChunkNum]; for (Asset::DepthFirstIt i(solverAsset, supportChunkIndices[supportChunkNum]); (bool)i; ++i) { lowerSupportChunkHealths[solverAsset.getContiguousLowerSupportIndex((uint32_t)i)] = initialHealth; } } } else // Use uniform initialization { const uint32_t lowerSupportChunkCount = solverAsset.getLowerSupportChunkCount(); for (uint32_t i = 0; i < lowerSupportChunkCount; ++i) { lowerSupportChunkHealths[i] = desc->uniformInitialLowerSupportChunkHealth; } } // Bond healths - initialize const uint32_t bondCount = solverAsset.getBondCount(); float* bondHealths = header->getBondHealths(); if (desc->initialBondHealths != nullptr) // Health array given { memcpy(bondHealths, desc->initialBondHealths, bondCount * sizeof(float)); } else // Use uniform initialization { for (uint32_t bondNum = 0; bondNum < bondCount; ++bondNum) { bondHealths[bondNum] = desc->uniformInitialBondHealth; } } // Get first actor - NOTE: we don't send an event for this! May need to do so for consistency. Actor* actor = header->borrowActor(0); // Using actor[0] // Fill in actor fields actor->m_firstGraphNodeIndex = 0; actor->m_graphNodeCount = graph.m_nodeCount; actor->m_leafChunkCount = solverAsset.m_leafChunkCount; // Graph node index links - initialize to chain uint32_t* graphNodeLinks = header->getGraphNodeIndexLinks(); for (uint32_t i = 0; i < graph.m_nodeCount - 1; ++i) { graphNodeLinks[i] = i + 1; } graphNodeLinks[graph.m_nodeCount - 1] = invalidIndex(); // Update visible chunks (we assume that all chunks belong to one actor at the beginning) actor->updateVisibleChunksFromGraphNodes(); // Initialize instance graph with this actor header->getFamilyGraph()->initialize(actor->getIndex(), &graph); // Call findIslands to set up the internal instance graph data header->getFamilyGraph()->findIslands(actor->getIndex(), scratch, &graph); return actor; } //////// Actor member methods //////// uint32_t Actor::damageBond(uint32_t nodeIndex0, uint32_t nodeIndex1, float healthDamage) { const uint32_t bondIndex = getGraph()->findBond(nodeIndex0, nodeIndex1); damageBond(nodeIndex0, nodeIndex1, bondIndex, healthDamage); return bondIndex; } void Actor::damageBond(uint32_t nodeIndex0, uint32_t nodeIndex1, uint32_t bondIndex, float healthDamage) { if (bondIndex == invalidIndex()) { NVBLAST_ALWAYS_ASSERT(); return; } float* bondHealths = getBondHealths(); if (bondHealths[bondIndex] > 0 && healthDamage > 0.0f) { // Subtract health bondHealths[bondIndex] -= healthDamage; // Was removed? if (bondHealths[bondIndex] <= 0) { // Notify graph that bond was removed getFamilyGraph()->notifyEdgeRemoved(getIndex(), nodeIndex0, nodeIndex1, bondIndex, getGraph()); bondHealths[bondIndex] = 0; // Doing this for single-actor serialization consistency; should not actually be necessary } } } uint32_t Actor::damageBond(const NvBlastBondFractureData& cmd) { NVBLAST_ASSERT(!isInvalidIndex(cmd.nodeIndex1)); return damageBond(cmd.nodeIndex0, cmd.nodeIndex1, cmd.health); } void Actor::generateFracture(NvBlastFractureBuffers* commandBuffers, const NvBlastDamageProgram& program, const void* programParams, NvBlastLog logFn, NvBlastTimers* timers) const { NVBLASTLL_CHECK(commandBuffers != nullptr, logFn, "Actor::generateFracture: NULL commandBuffers pointer input.", return); NVBLASTLL_CHECK(isValid(commandBuffers), logFn, "NvBlastActorGenerateFracture: commandBuffers memory is NULL but size is > 0.", commandBuffers->bondFractureCount = 0; commandBuffers->chunkFractureCount = 0; return); #if NVBLASTLL_CHECK_PARAMS if (commandBuffers->bondFractureCount == 0 && commandBuffers->chunkFractureCount == 0) { NVBLASTLL_LOG_WARNING(logFn, "NvBlastActorGenerateFracture: commandBuffers do not provide any space."); return; } #endif #if NV_PROFILE Time time; #else NV_UNUSED(timers); #endif const SupportGraph* graph = getGraph(); const uint32_t graphNodeCount = getGraphNodeCount(); if (graphNodeCount > 1 && program.graphShaderFunction != nullptr) { const NvBlastGraphShaderActor shaderActor = { getIndex(), getGraphNodeCount(), graph->m_nodeCount, getFirstGraphNodeIndex(), getGraphNodeIndexLinks(), graph->getChunkIndices(), graph->getAdjacencyPartition(), graph->getAdjacentNodeIndices(), graph->getAdjacentBondIndices(), getBonds(), getChunks(), getBondHealths(), getLowerSupportChunkHealths(), getFamilyHeader()->getFamilyGraph()->getIslandIds() }; program.graphShaderFunction(commandBuffers, &shaderActor, programParams); } else if (graphNodeCount <= 1 && program.subgraphShaderFunction != nullptr) { const NvBlastSubgraphShaderActor shaderActor = { // The conditional (visible vs. support chunk) is needed because we allow single-child chunk chains // This makes it possible that an actor with a single support chunk will have a different visible chunk (ancestor of the support chunk) graphNodeCount == 1 ? graph->getChunkIndices()[getFirstGraphNodeIndex()] : getFirstVisibleChunkIndex(), getChunks() }; program.subgraphShaderFunction(commandBuffers, &shaderActor, programParams); } else { commandBuffers->bondFractureCount = 0; commandBuffers->chunkFractureCount = 0; } #if NV_PROFILE if (timers != nullptr) { timers->material += time.getElapsedTicks(); } #endif } size_t Actor::splitRequiredScratch() const { // Scratch is reused, just need the max of these two values return std::max(m_graphNodeCount * sizeof(uint32_t), static_cast(FamilyGraph::findIslandsRequiredScratch(getGraph()->m_nodeCount))); } uint32_t Actor::split(NvBlastActorSplitEvent* result, uint32_t newActorsMaxCount, void* scratch, NvBlastLog logFn, NvBlastTimers* timers) { NVBLASTLL_CHECK(result != nullptr, logFn, "Actor::split: NULL result pointer input.", return 0); NVBLASTLL_CHECK(newActorsMaxCount > 0 && result->newActors != nullptr, logFn, "NvBlastActorSplit: no space for results provided.", return 0); NVBLASTLL_CHECK(scratch != nullptr, logFn, "Actor::split: NULL scratch pointer input.", return 0); #if NV_PROFILE Time time; #else NV_UNUSED(timers); #endif Actor** newActors = reinterpret_cast(result->newActors); uint32_t actorsCount = 0; if (getGraphNodeCount() <= 1) { uint32_t chunkHealthIndex = isSingleSupportChunk() ? getIndex() : getFirstVisibleChunkIndex() - getFirstSubsupportChunkIndex() + getGraph()->m_nodeCount; float* chunkHealths = getLowerSupportChunkHealths(); if (chunkHealths[chunkHealthIndex] <= 0.0f) { actorsCount = partitionSingleLowerSupportChunk(newActors, newActorsMaxCount, logFn); for (uint32_t i = 0; i < actorsCount; ++i) { Actor* newActor = newActors[i]; uint32_t firstVisible = newActor->getFirstVisibleChunkIndex(); uint32_t firstSub = newActor->getFirstSubsupportChunkIndex(); uint32_t nodeCount = newActor->getGraph()->m_nodeCount; uint32_t newActorIndex = newActor->getIndex(); uint32_t healthIndex = newActor->isSubSupportChunk() ? firstVisible - firstSub + nodeCount : newActorIndex; if (chunkHealths[healthIndex] <= 0.0f) { uint32_t brittleActors = newActors[i]->partitionSingleLowerSupportChunk(&newActors[actorsCount], newActorsMaxCount - actorsCount, logFn); actorsCount += brittleActors; if (brittleActors > 0) { actorsCount--; newActors[i] = newActors[actorsCount]; i--; } } } } #if NV_PROFILE if (timers != nullptr) { timers->partition += time.getElapsedTicks(); } #endif } else { findIslands(scratch); #if NV_PROFILE if (timers != nullptr) { timers->island += time.getElapsedTicks(); } #endif // Reuse scratch for node list uint32_t* graphNodeIndexList = reinterpret_cast(scratch); // Get the family header FamilyHeader* header = getFamilyHeader(); NVBLAST_ASSERT(header != nullptr); // If m_actorEntryDataIndex is valid, this should be too // Record nodes in this actor before splitting const uint32_t* graphNodeIndexLinks = header->getGraphNodeIndexLinks(); // Get the links for the graph nodes uint32_t graphNodeIndexCount = 0; for (uint32_t graphNodeIndex = m_firstGraphNodeIndex; !isInvalidIndex(graphNodeIndex); graphNodeIndex = graphNodeIndexLinks[graphNodeIndex]) { if (graphNodeIndexCount >= m_graphNodeCount) { // Safety, splitRequiredScratch() only guarantees m_graphNodeCount elements. In any case, this condition shouldn't happen. NVBLAST_ASSERT(graphNodeIndexCount < m_graphNodeCount); break; } graphNodeIndexList[graphNodeIndexCount++] = graphNodeIndex; } actorsCount = partitionMultipleGraphNodes(newActors, newActorsMaxCount, logFn); if (actorsCount > 1) { #if NV_PROFILE if (timers != nullptr) { timers->partition += time.getElapsedTicks(); } #endif // Get various pointers and values to iterate const Asset* asset = getAsset(); Actor* actors = header->getActors(); IndexDLink* visibleChunkIndexLinks = header->getVisibleChunkIndexLinks(); uint32_t* chunkActorIndices = header->getChunkActorIndices(); const SupportGraph& graph = asset->m_graph; const uint32_t* graphChunkIndices = graph.getChunkIndices(); const NvBlastChunk* chunks = asset->getChunks(); const uint32_t upperSupportChunkCount = asset->getUpperSupportChunkCount(); const uint32_t* familyGraphIslandIDs = header->getFamilyGraph()->getIslandIds(); // Iterate over all graph nodes and update visible chunk lists for (uint32_t graphNodeNum = 0; graphNodeNum < graphNodeIndexCount; ++graphNodeNum) { const uint32_t graphNodeIndex = graphNodeIndexList[graphNodeNum]; const uint32_t supportChunkIndex = graphChunkIndices[graphNodeIndex]; if (!isInvalidIndex(supportChunkIndex)) // Invalid if this is the world chunk { updateVisibleChunksFromSupportChunk(actors, visibleChunkIndexLinks, chunkActorIndices, familyGraphIslandIDs[graphNodeIndex], graphChunkIndices[graphNodeIndex], chunks, upperSupportChunkCount); } } // Remove actors with no visible chunks - this can happen if we've split such that the world node is by itself uint32_t actualActorsCount = 0; for (uint32_t i = 0; i < actorsCount; ++i) { newActors[actualActorsCount] = newActors[i]; if (newActors[actualActorsCount]->getVisibleChunkCount() > 0) { ++actualActorsCount; } else { header->returnActor(*newActors[actualActorsCount]); } } actorsCount = actualActorsCount; #if NV_PROFILE if (timers != nullptr) { timers->visibility += time.getElapsedTicks(); } #endif for (uint32_t i = 0; i < actorsCount; ++i) { Actor* newActor = newActors[i]; float* chunkHealths = newActor->getLowerSupportChunkHealths(); uint32_t firstVisible = newActor->getFirstVisibleChunkIndex(); uint32_t firstSub = newActor->getFirstSubsupportChunkIndex(); uint32_t nodeCount = newActor->getGraph()->m_nodeCount; uint32_t newActorIndex = newActor->getIndex(); uint32_t healthIndex = newActor->isSubSupportChunk() ? firstVisible - firstSub + nodeCount : newActorIndex; if (newActors[i]->getGraphNodeCount() <= 1) { // this relies on visibility updated, subsupport actors only have m_firstVisibleChunkIndex to identify the chunk if (chunkHealths[healthIndex] <= 0.0f) { uint32_t brittleActors = newActors[i]->partitionSingleLowerSupportChunk(&newActors[actorsCount], newActorsMaxCount - actorsCount, logFn); actorsCount += brittleActors; if (brittleActors > 0) { actorsCount--; newActors[i] = newActors[actorsCount]; i--; } } } } #if NV_PROFILE if (timers != nullptr) { timers->partition += time.getElapsedTicks(); } #endif } else { actorsCount = 0; } } result->deletedActor = actorsCount == 0 ? nullptr : this; return actorsCount; } uint32_t Actor::findIslands(void* scratch) { return getFamilyHeader()->getFamilyGraph()->findIslands(getIndex(), scratch, &getAsset()->m_graph); } uint32_t Actor::partitionMultipleGraphNodes(Actor** newActors, uint32_t newActorsSize, NvBlastLog logFn) { NVBLAST_ASSERT(newActorsSize == 0 || newActors != nullptr); // Check for single subsupport chunk, no partitioning if (m_graphNodeCount <= 1) { NVBLASTLL_LOG_WARNING(logFn, "Nv::Blast::Actor::partitionMultipleGraphNodes: actor is a single lower-support chunk, and cannot be partitioned by this function."); return 0; } FamilyHeader* header = getFamilyHeader(); NVBLAST_ASSERT(header != nullptr); // If m_actorEntryDataIndex is valid, this should be too // Get the links for the graph nodes uint32_t* graphNodeIndexLinks = header->getGraphNodeIndexLinks(); // Get the graph chunk indices and leaf chunk counts const Asset* asset = getAsset(); const uint32_t* graphChunkIndices = asset->m_graph.getChunkIndices(); const uint32_t* subtreeLeafChunkCounts = asset->getSubtreeLeafChunkCounts(); // Distribute graph nodes to new actors uint32_t newActorCount = 0; const uint32_t thisActorIndex = getIndex(); m_leafChunkCount = 0; const uint32_t* islandIDs = header->getFamilyGraph()->getIslandIds(); uint32_t lastGraphNodeIndex = invalidIndex(); uint32_t nextGraphNodeIndex = invalidIndex(); bool overflow = false; for (uint32_t graphNodeIndex = m_firstGraphNodeIndex; !isInvalidIndex(graphNodeIndex); graphNodeIndex = nextGraphNodeIndex) { nextGraphNodeIndex = graphNodeIndexLinks[graphNodeIndex]; const uint32_t islandID = islandIDs[graphNodeIndex]; if (islandID == thisActorIndex) { const uint32_t graphChunkIndex = graphChunkIndices[graphNodeIndex]; if (!isInvalidIndex(graphChunkIndex)) // Invalid if this is the world chunk { m_leafChunkCount += subtreeLeafChunkCounts[graphChunkIndex]; } lastGraphNodeIndex = graphNodeIndex; continue; // Leave the chunk in this actor } // Remove link from this actor if (isInvalidIndex(lastGraphNodeIndex)) { m_firstGraphNodeIndex = nextGraphNodeIndex; } else { graphNodeIndexLinks[lastGraphNodeIndex] = nextGraphNodeIndex; } graphNodeIndexLinks[graphNodeIndex] = invalidIndex(); --m_graphNodeCount; // See if the chunk had been removed if (islandID == invalidIndex()) { continue; } // Get new actor if the islandID is valid Actor* newActor = header->borrowActor(islandID); // Check new actor to see if we're adding the first chunk if (isInvalidIndex(newActor->m_firstGraphNodeIndex)) { // See if we can fit it in the output list if (newActorCount < newActorsSize) { newActors[newActorCount++] = newActor; } else { overflow = true; } } // Put link in new actor graphNodeIndexLinks[graphNodeIndex] = newActor->m_firstGraphNodeIndex; newActor->m_firstGraphNodeIndex = graphNodeIndex; ++newActor->m_graphNodeCount; // Add to the actor's leaf chunk count const uint32_t graphChunkIndex = graphChunkIndices[graphNodeIndex]; if (!isInvalidIndex(graphChunkIndex)) // Invalid if this is the world chunk { newActor->m_leafChunkCount += subtreeLeafChunkCounts[graphChunkIndex]; } } if (m_graphNodeCount > 0) { // There are still chunks in this actor. See if we can fit this in the output list. if (newActorCount < newActorsSize) { newActors[newActorCount++] = this; } else { overflow = true; } } else { // No more chunks; release this actor. release(); } if (overflow) { NVBLASTLL_LOG_WARNING(logFn, "Nv::Blast::Actor::partitionMultipleGraphNodes: input newActors array could not hold all actors generated."); } return newActorCount; } uint32_t Actor::partitionSingleLowerSupportChunk(Actor** newActors, uint32_t newActorsSize, NvBlastLog logFn) { NVBLAST_ASSERT(newActorsSize == 0 || newActors != nullptr); // Ensure this is a single subsupport chunk, no partitioning if (m_graphNodeCount > 1) { NVBLASTLL_LOG_WARNING(logFn, "Nv::Blast::Actor::partitionSingleLowerSupportChunk: actor is not a single lower-support chunk, and cannot be partitioned by this function."); return 0; } FamilyHeader* header = getFamilyHeader(); // The conditional (visible vs. support chunk) is needed because we allow single-child chunk chains // This makes it possible that an actor with a single support chunk will have a different visible chunk (ancestor of the support chunk) const uint32_t chunkIndex = m_graphNodeCount == 0 ? m_firstVisibleChunkIndex : getGraph()->getChunkIndices()[m_firstGraphNodeIndex]; if (isInvalidIndex(chunkIndex)) { return 0; // This actor has no chunks; only a graph node representing the world } NVBLAST_ASSERT(isInvalidIndex(header->getVisibleChunkIndexLinks()[chunkIndex].m_adj[1])); const NvBlastChunk& chunk = header->m_asset->getChunks()[chunkIndex]; uint32_t childCount = chunk.childIndexStop - chunk.firstChildIndex; // Warn if we cannot fit all child chunks in the output list if (childCount > newActorsSize) { NVBLASTLL_LOG_WARNING(logFn, "Nv::Blast::Actor::partitionSingleLowerSupportChunk: input newActors array will not hold all actors generated."); childCount = newActorsSize; } // Return if no chunks will be created. if (childCount == 0) { return 0; } // Activate a new actor for every child chunk const Asset* asset = getAsset(); const NvBlastChunk* chunks = asset->getChunks(); const uint32_t firstChildIndex = chunks[chunkIndex].firstChildIndex; for (uint32_t i = 0; i < childCount; ++i) { const uint32_t childIndex = firstChildIndex + i; NVBLAST_ASSERT(childIndex >= asset->m_firstSubsupportChunkIndex); const uint32_t actorIndex = asset->m_graph.m_nodeCount + (childIndex - asset->m_firstSubsupportChunkIndex); NVBLAST_ASSERT(!header->isActorActive(actorIndex)); newActors[i] = header->borrowActor(actorIndex); newActors[i]->m_firstVisibleChunkIndex = childIndex; newActors[i]->m_visibleChunkCount = 1; newActors[i]->m_leafChunkCount = asset->getSubtreeLeafChunkCounts()[childIndex]; } // Release this actor release(); return childCount; } void Actor::updateVisibleChunksFromGraphNodes() { // Only apply this to upper-support chunk actors if (m_graphNodeCount == 0) { return; } const Asset* asset = getAsset(); const uint32_t thisActorIndex = getIndex(); // Get various arrays FamilyHeader* header = getFamilyHeader(); Actor* actors = header->getActors(); IndexDLink* visibleChunkIndexLinks = header->getVisibleChunkIndexLinks(); uint32_t* chunkActorIndices = header->getChunkActorIndices(); const SupportGraph& graph = asset->m_graph; const uint32_t* graphChunkIndices = graph.getChunkIndices(); const NvBlastChunk* chunks = asset->getChunks(); const uint32_t upperSupportChunkCount = asset->getUpperSupportChunkCount(); // Iterate over all graph nodes and update visible chunk list const uint32_t* graphNodeIndexLinks = header->getGraphNodeIndexLinks(); for (uint32_t graphNodeIndex = m_firstGraphNodeIndex; !isInvalidIndex(graphNodeIndex); graphNodeIndex = graphNodeIndexLinks[graphNodeIndex]) { const uint32_t supportChunkIndex = graphChunkIndices[graphNodeIndex]; if (!isInvalidIndex(supportChunkIndex)) // Invalid if this is the world chunk { updateVisibleChunksFromSupportChunk(actors, visibleChunkIndexLinks, chunkActorIndices, thisActorIndex, graphChunkIndices[graphNodeIndex], chunks, upperSupportChunkCount); } } } } // namespace Blast } // namespace Nv // API implementation extern "C" { NvBlastActor* NvBlastFamilyCreateFirstActor(NvBlastFamily* family, const NvBlastActorDesc* desc, void* scratch, NvBlastLog logFn) { NVBLASTLL_CHECK(family != nullptr, logFn, "NvBlastFamilyCreateFirstActor: NULL family input.", return nullptr); NVBLASTLL_CHECK(desc != nullptr, logFn, "NvBlastFamilyCreateFirstActor: NULL desc input.", return nullptr); NVBLASTLL_CHECK(scratch != nullptr, logFn, "NvBlastFamilyCreateFirstActor: NULL scratch input.", return nullptr); return Nv::Blast::Actor::create(family, desc, scratch, logFn); } size_t NvBlastFamilyGetRequiredScratchForCreateFirstActor(const NvBlastFamily* family, NvBlastLog logFn) { NVBLASTLL_CHECK(family != nullptr, logFn, "NvBlastFamilyGetRequiredScratchForCreateFirstActor: NULL family input.", return 0); NVBLASTLL_CHECK(reinterpret_cast(family)->m_asset != nullptr, logFn, "NvBlastFamilyGetRequiredScratchForCreateFirstActor: family has NULL asset.", return 0); return Nv::Blast::Actor::createRequiredScratch(family); } bool NvBlastActorDeactivate(NvBlastActor* actor, NvBlastLog logFn) { NVBLASTLL_CHECK(actor != nullptr, logFn, "NvBlastActorDeactivate: NULL actor input.", return false); Nv::Blast::Actor& a = *static_cast(actor); if (!a.isActive()) { NVBLASTLL_LOG_WARNING(logFn, "NvBlastActorDeactivate: inactive actor input."); } return a.release(); } uint32_t NvBlastActorGetVisibleChunkCount(const NvBlastActor* actor, NvBlastLog logFn) { NVBLASTLL_CHECK(actor != nullptr, logFn, "NvBlastActorGetVisibleChunkCount: NULL actor input.", return 0); const Nv::Blast::Actor& a = *static_cast(actor); if (!a.isActive()) { NVBLASTLL_LOG_ERROR(logFn, "NvBlastActorGetVisibleChunkCount: inactive actor input."); return 0; } return a.getVisibleChunkCount(); } uint32_t NvBlastActorGetVisibleChunkIndices(uint32_t* visibleChunkIndices, uint32_t visibleChunkIndicesSize, const NvBlastActor* actor, NvBlastLog logFn) { NVBLASTLL_CHECK(visibleChunkIndices != nullptr, logFn, "NvBlastActorGetVisibleChunkIndices: NULL visibleChunkIndices pointer input.", return 0); NVBLASTLL_CHECK(actor != nullptr, logFn, "NvBlastActorGetVisibleChunkIndices: NULL actor pointer input.", return 0); const Nv::Blast::Actor& a = *static_cast(actor); if (!a.isActive()) { NVBLASTLL_LOG_ERROR(logFn, "NvBlastActorGetVisibleChunkIndices: inactive actor pointer input."); return 0; } // Iterate through visible chunk list and write to supplied array uint32_t indexCount = 0; for (Nv::Blast::Actor::VisibleChunkIt i = a; indexCount < visibleChunkIndicesSize && (bool)i; ++i) { visibleChunkIndices[indexCount++] = (uint32_t)i; } return indexCount; } uint32_t NvBlastActorGetGraphNodeCount(const NvBlastActor* actor, NvBlastLog logFn) { NVBLASTLL_CHECK(actor != nullptr, logFn, "NvBlastActorGetGraphNodeCount: NULL actor pointer input.", return 0); const Nv::Blast::Actor& a = *static_cast(actor); if (!a.isActive()) { NVBLASTLL_LOG_ERROR(logFn, "NvBlastActorGetGraphNodeCount: inactive actor pointer input."); return 0; } return a.getGraphNodeCount(); } uint32_t NvBlastActorGetGraphNodeIndices(uint32_t* graphNodeIndices, uint32_t graphNodeIndicesSize, const NvBlastActor* actor, NvBlastLog logFn) { NVBLASTLL_CHECK(graphNodeIndices != nullptr, logFn, "NvBlastActorGetGraphNodeIndices: NULL graphNodeIndices pointer input.", return 0); NVBLASTLL_CHECK(actor != nullptr, logFn, "NvBlastActorGetGraphNodeIndices: NULL actor pointer input.", return 0); const Nv::Blast::Actor& a = *static_cast(actor); if (!a.isActive()) { NVBLASTLL_LOG_ERROR(logFn, "NvBlastActorGetGraphNodeIndices: inactive actor pointer input."); return 0; } // Iterate through graph node list and write to supplied array const uint32_t* graphChunkIndices = a.getAsset()->m_graph.getChunkIndices(); uint32_t indexCount = 0; for (Nv::Blast::Actor::GraphNodeIt i = a; indexCount < graphNodeIndicesSize && (bool)i; ++i) { const uint32_t graphNodeIndex = (uint32_t)i; if (!Nv::Blast::isInvalidIndex(graphChunkIndices[graphNodeIndex])) { graphNodeIndices[indexCount++] = graphNodeIndex; } } return indexCount; } const float* NvBlastActorGetBondHealths(const NvBlastActor* actor, NvBlastLog logFn) { NVBLASTLL_CHECK(actor != nullptr, logFn, "NvBlastActorGetBondHealths: NULL actor pointer input.", return nullptr); const Nv::Blast::Actor& a = *static_cast(actor); if (!a.isActive()) { NVBLASTLL_LOG_ERROR(logFn, "NvBlastActorGetBondHealths: inactive actor pointer input."); return nullptr; } return a.getFamilyHeader()->getBondHealths(); } NvBlastFamily* NvBlastActorGetFamily(const NvBlastActor* actor, NvBlastLog logFn) { NVBLASTLL_CHECK(actor != nullptr, logFn, "NvBlastActorGetFamily: NULL actor pointer input.", return nullptr); const Nv::Blast::Actor& a = *static_cast(actor); if (!a.isActive()) { NVBLASTLL_LOG_ERROR(logFn, "NvBlastActorGetFamily: inactive actor pointer input."); return nullptr; } return reinterpret_cast(a.getFamilyHeader()); } uint32_t NvBlastActorGetIndex(const NvBlastActor* actor, NvBlastLog logFn) { NVBLASTLL_CHECK(actor != nullptr, logFn, "NvBlastActorGetIndex: NULL actor pointer input.", return Nv::Blast::invalidIndex()); const Nv::Blast::Actor& a = *static_cast(actor); if (!a.isActive()) { NVBLASTLL_LOG_ERROR(logFn, "NvBlastActorGetIndex: actor is not active."); return Nv::Blast::invalidIndex(); } return a.getIndex(); } void NvBlastActorGenerateFracture ( NvBlastFractureBuffers* commandBuffers, const NvBlastActor* actor, const NvBlastDamageProgram program, const void* programParams, NvBlastLog logFn, NvBlastTimers* timers ) { NVBLASTLL_CHECK(commandBuffers != nullptr, logFn, "NvBlastActorGenerateFracture: NULL commandBuffers pointer input.", return); NVBLASTLL_CHECK(actor != nullptr, logFn, "NvBlastActorGenerateFracture: NULL actor pointer input.", return); const Nv::Blast::Actor& a = *static_cast(actor); if (!a.isActive()) { NVBLASTLL_LOG_ERROR(logFn, "NvBlastActorGenerateFracture: actor is not active."); commandBuffers->bondFractureCount = 0; commandBuffers->chunkFractureCount = 0; return; } a.generateFracture(commandBuffers, program, programParams, logFn, timers); } void NvBlastActorApplyFracture ( NvBlastFractureBuffers* eventBuffers, NvBlastActor* actor, const NvBlastFractureBuffers* commands, NvBlastLog logFn, NvBlastTimers* timers ) { NVBLASTLL_CHECK(actor != nullptr, logFn, "NvBlastActorApplyFracture: NULL actor pointer input.", return); NVBLASTLL_CHECK(commands != nullptr, logFn, "NvBlastActorApplyFracture: NULL commands pointer input.", return); NVBLASTLL_CHECK(Nv::Blast::isValid(commands), logFn, "NvBlastActorApplyFracture: commands memory is NULL but size is > 0.", return); Nv::Blast::Actor& a = *static_cast(actor); if (!a.isActive()) { NVBLASTLL_LOG_ERROR(logFn, "NvBlastActorApplyFracture: actor is not active."); if (eventBuffers != nullptr) { eventBuffers->bondFractureCount = 0; eventBuffers->chunkFractureCount = 0; } return; } a.getFamilyHeader()->applyFracture(eventBuffers, commands, &a, logFn, timers); } size_t NvBlastActorGetRequiredScratchForSplit(const NvBlastActor* actor, NvBlastLog logFn) { NVBLASTLL_CHECK(actor != nullptr, logFn, "NvBlastActorGetRequiredScratchForSplit: NULL actor input.", return 0); const Nv::Blast::Actor& a = *static_cast(actor); if (!a.isActive()) { NVBLASTLL_LOG_ERROR(logFn, "NvBlastActorGetRequiredScratchForSplit: actor is not active."); return 0; } return a.splitRequiredScratch(); } uint32_t NvBlastActorGetMaxActorCountForSplit(const NvBlastActor* actor, NvBlastLog logFn) { NVBLASTLL_CHECK(actor != nullptr, logFn, "NvBlastActorGetMaxActorCountForSplit: NULL actor input.", return 0); const Nv::Blast::Actor& a = *static_cast(actor); if (!a.isActive()) { NVBLASTLL_LOG_ERROR(logFn, "NvBlastActorGetMaxActorCountForSplit: actor is not active."); return 0; } return a.getLeafChunkCount() + 1; // GWD-167 workaround (+1) } uint32_t NvBlastActorSplit ( NvBlastActorSplitEvent* result, NvBlastActor* actor, uint32_t newActorsMaxCount, void* scratch, NvBlastLog logFn, NvBlastTimers* timers ) { NVBLASTLL_CHECK(result != nullptr, logFn, "NvBlastActorSplit: NULL result pointer input.", return 0); NVBLASTLL_CHECK(newActorsMaxCount > 0 && result->newActors != nullptr, logFn, "NvBlastActorSplit: no space for results provided.", return 0); NVBLASTLL_CHECK(actor != nullptr, logFn, "NvBlastActorSplit: NULL actor pointer input.", return 0); NVBLASTLL_CHECK(scratch != nullptr, logFn, "NvBlastActorSplit: NULL scratch pointer input.", return 0); Nv::Blast::Actor& a = *static_cast(actor); if (!a.isActive()) { NVBLASTLL_LOG_ERROR(logFn, "NvBlastActorGetIndex: actor is not active."); return 0; } return a.split(result, newActorsMaxCount, scratch, logFn, timers); } bool NvBlastActorCanFracture(const NvBlastActor* actor, NvBlastLog logFn) { NVBLASTLL_CHECK(actor != nullptr, logFn, "NvBlastActorCanFracture: NULL actor input.", return false); const Nv::Blast::Actor& a = *static_cast(actor); if (!a.isActive()) { NVBLASTLL_LOG_ERROR(logFn, "NvBlastActorCanFracture: actor is not active."); return false; } bool canFracture = true; uint32_t graphNodeCount = a.getGraphNodeCount(); if (graphNodeCount < 2) { uint32_t chunkHealthIndex = graphNodeCount == 0 ? a.getFirstVisibleChunkIndex() - a.getFirstSubsupportChunkIndex() + a.getGraph()->m_nodeCount : a.getFirstGraphNodeIndex(); canFracture = (a.getLowerSupportChunkHealths()[chunkHealthIndex] > 0.0f); } return canFracture; } bool NvBlastActorIsBoundToWorld(const NvBlastActor* actor, NvBlastLog logFn) { NVBLASTLL_CHECK(actor != nullptr, logFn, "NvBlastActorIsBoundToWorld: NULL actor input.", return false); return static_cast(actor)->isBoundToWorld(); } bool NvBlastActorIsSplitRequired(const NvBlastActor* actor, NvBlastLog logFn) { NVBLASTLL_CHECK(actor != nullptr, logFn, "NvBlastActorIsSplitRequired: NULL actor input.", return false); const Nv::Blast::Actor& a = *static_cast(actor); if (!a.isActive()) { NVBLASTLL_LOG_ERROR(logFn, "NvBlastActorIsSplitRequired: actor is not active."); return false; } return a.isSplitRequired(); } } // extern "C"