aboutsummaryrefslogtreecommitdiff
path: root/APEX_1.4/module/destructible/src/DestructibleStructure.cpp
diff options
context:
space:
mode:
authorgit perforce import user <a@b>2016-10-25 12:29:14 -0600
committerSheikh Dawood Abdul Ajees <Sheikh Dawood Abdul Ajees>2016-10-25 18:56:37 -0500
commit3dfe2108cfab31ba3ee5527e217d0d8e99a51162 (patch)
treefa6485c169e50d7415a651bf838f5bcd0fd3bfbd /APEX_1.4/module/destructible/src/DestructibleStructure.cpp
downloadphysx-3.4-3dfe2108cfab31ba3ee5527e217d0d8e99a51162.tar.xz
physx-3.4-3dfe2108cfab31ba3ee5527e217d0d8e99a51162.zip
Initial commit:
PhysX 3.4.0 Update @ 21294896 APEX 1.4.0 Update @ 21275617 [CL 21300167]
Diffstat (limited to 'APEX_1.4/module/destructible/src/DestructibleStructure.cpp')
-rw-r--r--APEX_1.4/module/destructible/src/DestructibleStructure.cpp2414
1 files changed, 2414 insertions, 0 deletions
diff --git a/APEX_1.4/module/destructible/src/DestructibleStructure.cpp b/APEX_1.4/module/destructible/src/DestructibleStructure.cpp
new file mode 100644
index 00000000..4116948d
--- /dev/null
+++ b/APEX_1.4/module/destructible/src/DestructibleStructure.cpp
@@ -0,0 +1,2414 @@
+/*
+ * Copyright (c) 2008-2015, NVIDIA CORPORATION. All rights reserved.
+ *
+ * NVIDIA CORPORATION and its licensors retain all intellectual property
+ * and proprietary rights in and to this software, 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.
+ */
+
+
+#include "ApexDefs.h"
+#include "Apex.h"
+#include "DestructibleStructure.h"
+#include "DestructibleScene.h"
+#include "ModuleDestructibleImpl.h"
+#include "ModulePerfScope.h"
+#include "PsIOStream.h"
+#include <PxPhysics.h>
+#include <PxScene.h>
+#include "PxConvexMeshGeometry.h"
+
+#if APEX_USE_PARTICLES
+#include "EmitterActor.h"
+#include "EmitterGeoms.h"
+#endif
+
+#include "RenderDebugInterface.h"
+#include "DestructibleActorProxy.h"
+
+#include "DestructibleStructureStressSolver.h"
+
+#if APEX_RUNTIME_FRACTURE
+#include "SimScene.h"
+#endif
+
+#include "ApexMerge.h"
+
+namespace nvidia
+{
+namespace destructible
+{
+using namespace physx;
+
+// Local definitions
+
+#define REDUCE_DAMAGE_TO_CHILD_CHUNKS 1
+
+// DestructibleStructure methods
+
+DestructibleStructure::DestructibleStructure(DestructibleScene* inScene, uint32_t inID) :
+ dscene(inScene),
+ ID(inID),
+ supportDepthChunksNotExternallySupportedCount(0),
+ supportInvalid(false),
+ actorForStaticChunks(NULL),
+ stressSolver(NULL)
+{
+}
+
+DestructibleStructure::~DestructibleStructure()
+{
+ if(NULL != stressSolver)
+ {
+ PX_DELETE(stressSolver);
+ stressSolver = NULL;
+ dscene->setStressSolverTick(this, false);
+ }
+
+ for (uint32_t destructibleIndex = destructibles.size(); destructibleIndex--;)
+ {
+ DestructibleActorImpl* destructible = destructibles[destructibleIndex];
+ if (destructible)
+ {
+ destructible->setStructure(NULL);
+ dscene->mDestructibles.direct(destructible->getID()) = NULL;
+ dscene->mDestructibles.free(destructible->getID());
+ }
+ }
+
+ dscene->setStructureSupportRebuild(this, false);
+ dscene->setStructureUpdate(this, false);
+
+ for (uint32_t i = 0; i < chunks.size(); ++i)
+ {
+ Chunk& chunk = chunks[i];
+ if ((chunk.state & ChunkVisible) != 0)
+ {
+ physx::Array<PxShape*>& shapeArray = getChunkShapes(chunk);
+ for (uint32_t i = shapeArray.size(); i--;)
+ {
+ PxShape* shape = shapeArray[i];
+ dscene->mModule->mSdk->releaseObjectDesc(shape);
+ physx::PxRigidDynamic* actor = shape->getActor()->is<PxRigidDynamic>();
+ actor->detachShape(*shape);
+ if (dscene->mTotalChunkCount > 0)
+ {
+ --dscene->mTotalChunkCount;
+ }
+ SCOPED_PHYSX_LOCK_READ(actor->getScene());
+ if (actor->getNbShapes() == 0)
+ {
+ dscene->releasePhysXActor(*actor);
+ }
+ }
+ }
+ chunk.clearShapes();
+#if USE_CHUNK_RWLOCK
+ if (chunk.lock)
+ {
+ chunk.lock->~shdfnd::ReadWriteLock();
+ PX_FREE(chunk.lock);
+ }
+ chunk.lock = NULL;
+#endif
+ }
+
+ actorToIsland.clear();
+ islandToActor.clear();
+
+ dscene = NULL;
+ ID = (uint32_t)InvalidID;
+}
+
+bool DestructibleStructure::addActors(const physx::Array<DestructibleActorImpl*>& destructiblesToAdd)
+{
+ PX_PROFILE_ZONE("DestructibleAddActors", GetInternalApexSDK()->getContextId());
+
+ // use the scene lock to protect chunk data for
+ // multi-threaded obbSweep calls on destructible actors
+ // (different lock would be good, but mixing locks can cause deadlocks)
+ SCOPED_PHYSX_LOCK_WRITE(dscene->mApexScene);
+
+ const uint32_t oldDestructibleCount = destructibles.size();
+
+ const uint32_t destructibleCount = oldDestructibleCount + destructiblesToAdd.size();
+ destructibles.resize(destructibleCount);
+ uint32_t totalAddedChunkCount = 0;
+ for (uint32_t destructibleIndex = oldDestructibleCount; destructibleIndex < destructibleCount; ++destructibleIndex)
+ {
+ DestructibleActorImpl* destructible = destructiblesToAdd[destructibleIndex - oldDestructibleCount];
+ destructibles[destructibleIndex] = destructible;
+ totalAddedChunkCount += destructible->getDestructibleAsset()->getChunkCount();
+ }
+
+ const uint32_t oldChunkCount = chunks.size();
+ const uint32_t chunkCount = oldChunkCount + totalAddedChunkCount;
+ chunks.resize(chunkCount);
+
+ ApexSDKIntl* sdk = dscene->mModule->mSdk;
+
+ // Fix chunk pointers in shapes that may have been moved because of the resize() operation
+ for (uint32_t chunkIndex = 0; chunkIndex < oldChunkCount; ++chunkIndex)
+ {
+ Chunk& chunk = chunks[chunkIndex];
+ if ((chunk.state & ChunkVisible) != 0)
+ {
+ physx::Array<PxShape*>& shapeArray = getChunkShapes(chunk);
+ for (uint32_t i = shapeArray.size(); i--;)
+ {
+ PxShape* shape = shapeArray[i];
+ if (shape != NULL)
+ {
+ PhysXObjectDescIntl* objDesc = sdk->getGenericPhysXObjectInfo(shape);
+ if (objDesc != NULL)
+ {
+ objDesc->userData = &chunk;
+ }
+ }
+ }
+ }
+ }
+
+ uint32_t chunkIndex = oldChunkCount;
+ for (uint32_t destructibleIndex = oldDestructibleCount; destructibleIndex < destructibleCount; ++destructibleIndex)
+ {
+ DestructibleActorImpl* destructible = destructiblesToAdd[destructibleIndex - oldDestructibleCount];
+
+ const bool formsExtendedStructures = !destructible->isInitiallyDynamic() && destructible->formExtendedStructures();
+
+ if (!formsExtendedStructures)
+ {
+ PX_ASSERT(destructibleCount == 1); // All dynamic destructibles must be in their own structure
+ }
+
+ destructible->setStructure(this);
+ PX_VERIFY(dscene->mDestructibles.useNextFree(destructible->getIDRef()));
+ dscene->mDestructibles.direct(destructible->getID()) = destructible;
+
+ //PxVec3 destructibleScale = destructible->getScale();
+
+ destructible->setFirstChunkIndex(chunkIndex);
+ for (uint32_t i = 0; i < destructible->getDestructibleAsset()->getChunkCount(); ++i)
+ {
+ destructible->initializeChunk(i, chunks[chunkIndex++]);
+ }
+ // This should be done after all chunks are initialized, since it will traverse all chunks
+ Chunk& root = chunks[destructible->getFirstChunkIndex()];
+ PX_ASSERT(root.isDestroyed()); // ensure shape pointer is still NULL
+
+ // Create a new static actor if this is the first actor
+ if (destructibleIndex == 0)
+ {
+ // Disable visibility before creation
+ root.state &= ~(uint8_t)ChunkVisible;
+ PxRigidDynamic* rootActor = dscene->createRoot(root, PxTransform(destructible->getInitialGlobalPose()), destructible->isInitiallyDynamic());
+ if (!destructible->isInitiallyDynamic() || destructible->initializedFromState())
+ {
+ actorForStaticChunks = rootActor;
+ }
+ }
+ // Otherwise, append the new destructible actor's root chunk shapes to the existing root chunk's actor
+ // If the new destructible is initialized from state, defer chunk loading to ChunkLoadState
+ else if (!destructible->initializedFromState())
+ {
+ DestructibleActorImpl* firstDestructible = dscene->mDestructibles.direct(chunks[0].destructibleID);
+ PxTransform relTM(firstDestructible->getInitialGlobalPose().inverseRT() * destructible->getInitialGlobalPose());
+ if (actorForStaticChunks)
+ {
+ dscene->appendShapes(root, destructible->isInitiallyDynamic(), &relTM, actorForStaticChunks);
+ PhysXObjectDescIntl* objDesc = sdk->getGenericPhysXObjectInfo(actorForStaticChunks);
+ PX_ASSERT(objDesc->mApexActors.find(destructible->getAPI()) == objDesc->mApexActors.end());
+ objDesc->mApexActors.pushBack(destructible->getAPI());
+ destructible->referencedByActor(actorForStaticChunks);
+ }
+ else
+ {
+ // Disable visibility before creation
+ root.state &= ~(uint8_t)ChunkVisible;
+ PxRigidDynamic* rootActor = dscene->createRoot(root, PxTransform(destructible->getInitialGlobalPose()), destructible->isInitiallyDynamic());
+ actorForStaticChunks = rootActor;
+ }
+ }
+
+ destructible->setRelativeTMs();
+ if (destructible->initializedFromState())
+ {
+ DestructibleScene::ChunkLoadState chunkLoadState(*dscene);
+ dscene->forSubtree(root, chunkLoadState, false);
+ }
+
+ destructible->setChunkVisibility(0, destructible->getInitialChunkVisible(root.indexInAsset));
+ // init bounds
+ destructible->setRenderTMs();
+ }
+
+ dscene->setStructureSupportRebuild(this, true);
+
+ return true;
+}
+
+struct Ptr_LT
+{
+ PX_INLINE bool operator()(void* a, void* b) const
+ {
+ return a < b;
+ }
+};
+
+bool DestructibleStructure::removeActor(DestructibleActorImpl* destructibleToRemove)
+{
+ PX_PROFILE_ZONE("DestructibleRemoveActor", GetInternalApexSDK()->getContextId());
+
+ // use the scene lock to protect chunk data for
+ // multi-threaded obbSweep calls on destructible actors
+ // (different lock would be good, but mixing locks can cause deadlocks)
+ SCOPED_PHYSX_LOCK_WRITE(dscene->mApexScene);
+
+ if (destructibleToRemove->getStructure() != this)
+ {
+ return false; // Destructible not in structure
+ }
+
+ const uint32_t oldDestructibleCount = destructibles.size();
+
+ uint32_t destructibleIndex = 0;
+ for (; destructibleIndex < oldDestructibleCount; ++destructibleIndex)
+ {
+ if (destructibles[destructibleIndex] == destructibleToRemove)
+ {
+ break;
+ }
+ }
+
+ if (destructibleIndex == oldDestructibleCount)
+ {
+ return false; // Destructible not in structure
+ }
+
+ // Remove chunks (releases shapes) and ensure we dissociate from their actors.
+ // Note: if we're not in a structure, releasing shapes will always release the actors, which releases the actorObjDesc, thus dissociating us.
+ // But if we're in a structure, some other destructible might have shapes in the actor, and we won't get dissociated. This is why we must
+ // Go through the trouble of ensuring dissociation here.
+ physx::Array<void*> dissociatedActors;
+ uint32_t visibleChunkCount = destructibleToRemove->getNumVisibleChunks(); // Do it this way in case removeChunk doesn't work for some reason
+ while (visibleChunkCount--)
+ {
+ const uint16_t chunkIndex = destructibleToRemove->getVisibleChunks()[destructibleToRemove->getNumVisibleChunks()-1];
+ DestructibleStructure::Chunk& chunk = chunks[chunkIndex + destructibleToRemove->getFirstChunkIndex()];
+ PxRigidDynamic* actor = getChunkActor(chunk);
+ if (actor != NULL)
+ {
+ dissociatedActors.pushBack(actor);
+ }
+ removeChunk(chunk);
+ }
+
+ // Now sort the actor pointers so we only deal with each once
+ const uint32_t dissociatedActorCount = dissociatedActors.size();
+ if (dissociatedActorCount > 1)
+ {
+ nvidia::sort<void*, Ptr_LT>(&dissociatedActors[0], dissociatedActors.size(), Ptr_LT());
+ }
+
+ // Run through the sorted list
+ void* lastActor = NULL;
+ for (uint32_t actorNum = 0; actorNum < dissociatedActorCount; ++actorNum)
+ {
+ void* nextActor = dissociatedActors[actorNum];
+ if (nextActor != lastActor)
+ {
+ PhysXObjectDescIntl* actorObjDesc = dscene->mModule->mSdk->getGenericPhysXObjectInfo(nextActor);
+ if (actorObjDesc != NULL)
+ {
+ for (uint32_t i = actorObjDesc->mApexActors.size(); i--;)
+ {
+ if (actorObjDesc->mApexActors[i] == destructibleToRemove->getAPI())
+ {
+ actorObjDesc->mApexActors.replaceWithLast(i);
+ destructibleToRemove->unreferencedByActor((PxRigidDynamic*)nextActor);
+ }
+ }
+ if (actorObjDesc->mApexActors.size() == 0)
+ {
+ dscene->releasePhysXActor(*static_cast<PxRigidDynamic*>(nextActor));
+ }
+ }
+ lastActor = nextActor;
+ }
+ }
+
+ // Also check the actor for static chunks
+ if (actorForStaticChunks != NULL)
+ {
+ PhysXObjectDescIntl* actorObjDesc = dscene->mModule->mSdk->getGenericPhysXObjectInfo(actorForStaticChunks);
+ if (actorObjDesc != NULL)
+ {
+ for (uint32_t i = actorObjDesc->mApexActors.size(); i--;)
+ {
+ if (actorObjDesc->mApexActors[i] == destructibleToRemove->getAPI())
+ {
+ actorObjDesc->mApexActors.replaceWithLast(i);
+ destructibleToRemove->unreferencedByActor(actorForStaticChunks);
+ }
+ }
+ if (actorObjDesc->mApexActors.size() == 0)
+ {
+ dscene->releasePhysXActor(*static_cast<PxRigidDynamic*>(actorForStaticChunks));
+ }
+ }
+ }
+
+ const uint32_t destructibleToRemoveFirstChunkIndex = destructibleToRemove->getFirstChunkIndex();
+ const uint32_t destructibleToRemoveChunkCount = destructibleToRemove->getDestructibleAsset()->getChunkCount();
+ const uint32_t destructibleToRemoveChunkIndexStop = destructibleToRemoveFirstChunkIndex + destructibleToRemoveChunkCount;
+
+ destructibleToRemove->setStructure(NULL);
+
+ // Compact destructibles array
+ for (; destructibleIndex < oldDestructibleCount-1; ++destructibleIndex)
+ {
+ destructibles[destructibleIndex] = destructibles[destructibleIndex+1];
+ const uint32_t firstChunkIndex = destructibles[destructibleIndex]->getFirstChunkIndex();
+ PX_ASSERT(firstChunkIndex >= destructibleToRemoveChunkCount);
+ destructibles[destructibleIndex]->setFirstChunkIndex(firstChunkIndex - destructibleToRemoveChunkCount);
+ }
+ destructibles.resize(oldDestructibleCount-1);
+
+ // Compact chunks array
+ chunks.removeRange(destructibleToRemoveFirstChunkIndex, destructibleToRemoveChunkCount);
+ const uint32_t chunkCount = chunks.size();
+
+ ApexSDKIntl* sdk = dscene->mModule->mSdk;
+
+ // Fix chunk pointers in shapes that may have been moved because of the removeRange() operation
+ for (uint32_t chunkIndex = destructibleToRemoveFirstChunkIndex; chunkIndex < chunkCount; ++chunkIndex)
+ {
+ Chunk& chunk = chunks[chunkIndex];
+ if (chunk.visibleAncestorIndex != (int32_t)InvalidChunkIndex)
+ {
+ chunk.visibleAncestorIndex -= destructibleToRemoveChunkCount;
+ }
+ for (uint32_t i = chunk.shapes.size(); i--;)
+ {
+ PxShape* shape = chunk.shapes[i];
+ if (shape != NULL)
+ {
+ PhysXObjectDescIntl* objDesc = sdk->getGenericPhysXObjectInfo(shape);
+ if (objDesc != NULL)
+ {
+ objDesc->userData = &chunk;
+ }
+ }
+ }
+ }
+
+ // Remove obsolete support info
+ uint32_t newSupportChunkNum = 0;
+ PX_ASSERT(supportDepthChunksNotExternallySupportedCount <= supportDepthChunks.size());
+ for (uint32_t supportChunkNum = 0; supportChunkNum < supportDepthChunksNotExternallySupportedCount; ++supportChunkNum)
+ {
+ const uint32_t supportChunkIndex = supportDepthChunks[supportChunkNum];
+ if (supportChunkIndex < destructibleToRemoveFirstChunkIndex)
+ {
+ // This chunk index will not be affected
+ supportDepthChunks[newSupportChunkNum++] = supportChunkIndex;
+ }
+ else
+ if (supportChunkIndex >= destructibleToRemoveChunkIndexStop)
+ {
+ // This chunk index will be affected
+ supportDepthChunks[newSupportChunkNum++] = supportChunkIndex - destructibleToRemoveChunkCount;
+ }
+ }
+
+ const uint32_t newSupportDepthChunksNotExternallySupportedCount = newSupportChunkNum;
+
+ for (uint32_t supportChunkNum = supportDepthChunksNotExternallySupportedCount; supportChunkNum < supportDepthChunks.size(); ++supportChunkNum)
+ {
+ const uint32_t supportChunkIndex = supportDepthChunks[supportChunkNum];
+ if (supportChunkIndex < destructibleToRemoveFirstChunkIndex)
+ {
+ // This chunk index will not be affected
+ supportDepthChunks[newSupportChunkNum++] = supportChunkIndex;
+ }
+ else
+ if (supportChunkIndex >= destructibleToRemoveChunkIndexStop)
+ {
+ // This chunk index will be affected
+ supportDepthChunks[newSupportChunkNum++] = supportChunkIndex - destructibleToRemoveChunkCount;
+ }
+ }
+
+ supportDepthChunksNotExternallySupportedCount = newSupportDepthChunksNotExternallySupportedCount;
+ supportDepthChunks.resize(newSupportChunkNum);
+
+ dscene->setStructureSupportRebuild(this, true);
+
+ return true;
+}
+
+
+void DestructibleStructure::updateIslands()
+{
+ PX_PROFILE_ZONE("DestructibleStructureUpdateIslands", GetInternalApexSDK()->getContextId());
+
+ // islandToActor is used within a single frame to reconstruct deserialized islands.
+ // After that frame, there are no guarantees on island recreation within
+ // a single structure between multiple actors. Thus we reset it each frame.
+ if (islandToActor.size())
+ {
+ islandToActor.clear();
+ }
+
+ if (supportInvalid)
+ {
+ separateUnsupportedIslands();
+ }
+}
+
+
+void DestructibleStructure::tickStressSolver(float deltaTime)
+{
+ PX_PROFILE_ZONE("DestructibleStructureTickStressSolver", GetInternalApexSDK()->getContextId());
+
+ PX_ASSERT(stressSolver != NULL);
+ if (stressSolver == NULL)
+ return;
+
+ if(stressSolver->isPhysxBasedSim)
+ {
+ if(stressSolver->isCustomEnablesSimulating)
+ {
+ stressSolver->physcsBasedStressSolverTick();
+ }
+ }
+ else
+ {
+ stressSolver->onTick(deltaTime);
+ if (supportInvalid)
+ {
+ stressSolver->onResolve();
+ }
+ }
+}
+
+PX_INLINE uint32_t colorFromUInt(uint32_t x)
+{
+ x = (x % 26) + 1;
+ uint32_t rIndex = x / 9;
+ x -= rIndex * 9;
+ uint32_t gIndex = x / 3;
+ uint32_t bIndex = x - gIndex * 3;
+ uint32_t r = (rIndex << 7) - (uint32_t)(rIndex != 0);
+ uint32_t g = (gIndex << 7) - (uint32_t)(gIndex != 0);
+ uint32_t b = (bIndex << 7) - (uint32_t)(bIndex != 0);
+ return 0xFF000000 | r << 16 | g << 8 | b;
+}
+
+void DestructibleStructure::visualizeSupport(RenderDebugInterface* debugRender)
+{
+#ifdef WITHOUT_DEBUG_VISUALIZE
+ PX_UNUSED(debugRender);
+#else
+
+ // apparently if this method is called before an actor is ever stepped bad things can happen
+ if (firstOverlapIndices.size() == 0)
+ {
+ return;
+ }
+
+ for (uint32_t i = 0; i < supportDepthChunks.size(); ++i)
+ {
+ uint32_t chunkIndex = supportDepthChunks[i];
+ Chunk& chunk = chunks[chunkIndex];
+ PxRigidDynamic* chunkActor = dscene->chunkIntact(chunk);
+ if (chunkActor != NULL)
+ {
+ SCOPED_PHYSX_LOCK_READ(chunkActor->getScene());
+ PhysXObjectDesc* actorObjDesc = (PhysXObjectDesc*) dscene->mModule->mSdk->getPhysXObjectInfo(chunkActor);
+ uint32_t color = colorFromUInt((uint32_t)(-(intptr_t)actorObjDesc->userData));
+ RENDER_DEBUG_IFACE(debugRender)->setCurrentColor(color);
+ PxVec3 actorCentroid = (chunkActor->getGlobalPose() * PxTransform(chunk.localSphereCenter)).p;
+ const PxVec3 chunkCentroid = !chunk.isDestroyed() ? getChunkWorldCentroid(chunk) : actorCentroid;
+ const uint32_t firstOverlapIndex = firstOverlapIndices[chunkIndex];
+ const uint32_t stopOverlapIndex = firstOverlapIndices[chunkIndex + 1];
+ for (uint32_t j = firstOverlapIndex; j < stopOverlapIndex; ++j)
+ {
+ uint32_t overlapChunkIndex = overlaps[j];
+ if (overlapChunkIndex > chunkIndex)
+ {
+ Chunk& overlapChunk = chunks[overlapChunkIndex];
+ PxRigidDynamic* overlapChunkActor = dscene->chunkIntact(overlapChunk);
+ if (overlapChunkActor == chunkActor ||
+ (overlapChunkActor != NULL &&
+ chunkActor->getRigidBodyFlags() & physx::PxRigidBodyFlag::eKINEMATIC &&
+ overlapChunkActor->getRigidBodyFlags() & physx::PxRigidBodyFlag::eKINEMATIC))
+ {
+ PxVec3 actorCentroid = (overlapChunkActor->getGlobalPose() * PxTransform(overlapChunk.localSphereCenter)).p;
+ const PxVec3 overlapChunkCentroid = !overlapChunk.isDestroyed() ? getChunkWorldCentroid(overlapChunk) : actorCentroid;
+ // Draw line from chunkCentroid to overlapChunkCentroid
+ RENDER_DEBUG_IFACE(debugRender)->debugLine(chunkCentroid, overlapChunkCentroid);
+ }
+ }
+ }
+ if (chunk.flags & ChunkExternallySupported)
+ {
+ // Draw mark at chunkCentroid
+ DestructibleActorImpl* destructible = dscene->mDestructibles.direct(chunk.destructibleID);
+ if (destructible)
+ {
+ PxBounds3 bounds = destructible->getDestructibleAsset()->getChunkShapeLocalBounds(chunk.indexInAsset);
+ PxMat44 tm;
+ if (chunk.isDestroyed())
+ {
+ tm = PxMat44(chunkActor->getGlobalPose());
+ }
+ else
+ {
+ tm = PxMat44(getChunkGlobalPose(chunk));
+ }
+ tm.scale(PxVec4(destructible->getScale(), 1.f));
+ PxBounds3Transform(bounds, tm);
+ RENDER_DEBUG_IFACE(debugRender)->debugBound(bounds);
+ }
+ }
+ }
+ }
+#endif
+}
+
+uint32_t DestructibleStructure::damageChunk(Chunk& chunk, const PxVec3& position, const PxVec3& direction, bool fromImpact, float damage, float damageRadius,
+ physx::Array<FractureEvent> outputs[], uint32_t& possibleDeleteChunks, float& totalDeleteChunkRelativeDamage,
+ uint32_t& maxDepth, uint32_t depth, uint16_t stopDepth, float padding)
+{
+ if (depth > DamageEvent::MaxDepth || depth > (uint32_t)stopDepth)
+ {
+ return 0;
+ }
+
+ DestructibleActorImpl* destructible = dscene->mDestructibles.direct(chunk.destructibleID);
+
+ if (depth > destructible->getLOD())
+ {
+ return 0;
+ }
+
+ physx::Array<FractureEvent>& output = outputs[depth];
+
+ const DestructibleAssetParametersNS::Chunk_Type& source = destructible->getDestructibleAsset()->mParams->chunks.buf[chunk.indexInAsset];
+
+ if (source.flags & DestructibleAssetImpl::UnfracturableChunk)
+ {
+ return 0;
+ }
+
+ if (chunk.isDestroyed())
+ {
+ return 0;
+ }
+
+ const DestructibleParameters& destructibleParameters = destructible->getDestructibleParameters();
+
+ const uint32_t originalEventBufferSize = output.size();
+
+ const bool hasChildren = source.numChildren != 0 && depth < destructible->getLOD();
+
+ // Support legacy behavior
+ const bool useLegacyChunkOverlap = destructible->getUseLegacyChunkBoundsTesting();
+ const bool useLegacyDamageSpread = destructible->getUseLegacyDamageRadiusSpread();
+
+ // Get behavior group
+ const DestructibleActorParamNS::BehaviorGroup_Type& behaviorGroup = destructible->getBehaviorGroupImp(source.behaviorGroupIndex);
+
+ // Get radial info from behavior group
+ float minRadius = 0.0f;
+ float maxRadius = 0.0f;
+ float falloff = 1.0f;
+ if (!useLegacyDamageSpread)
+ {
+ // New behavior
+ minRadius = behaviorGroup.damageSpread.minimumRadius;
+ falloff = behaviorGroup.damageSpread.falloffExponent;
+
+ if (!fromImpact)
+ {
+ maxRadius = minRadius + damageRadius*behaviorGroup.damageSpread.radiusMultiplier;
+ }
+ else
+ {
+ maxRadius = behaviorGroup.damageToRadius;
+ if (behaviorGroup.damageThreshold > 0)
+ {
+ maxRadius *= damage / behaviorGroup.damageThreshold;
+ }
+ }
+ }
+ else
+ {
+ // Legacy behavior
+ if (damageRadius == 0.0f)
+ {
+ maxRadius = destructible->getLinearSize() * behaviorGroup.damageToRadius;
+ if (behaviorGroup.damageThreshold > 0)
+ {
+ maxRadius *= damage / behaviorGroup.damageThreshold;
+ }
+ }
+ else
+ {
+ maxRadius = damageRadius;
+ }
+ }
+
+ float overlapDistance = PX_MAX_F32;
+ if (!useLegacyChunkOverlap)
+ {
+ destructible->getDestructibleAsset()->chunkAndSphereInProximity(chunk.indexInAsset, destructible->getChunkPose(chunk.indexInAsset), destructible->getScale(), position, maxRadius, padding, &overlapDistance);
+ }
+ else
+ {
+ PxVec3 disp = getChunkWorldCentroid(chunk) - position;
+ float dist = disp.magnitude();
+ overlapDistance = dist - maxRadius;
+ padding = 0.0f; // Note we're overriding the function parameter here, as it didn't exist in the old (legacy) code
+ if (!hasChildren) // Test against bounding sphere for smallest chunks
+ {
+ overlapDistance -= chunk.localSphereRadius;
+ }
+ }
+
+ uint32_t fractureCount = 0;
+
+ const float recipRadiusRange = maxRadius > minRadius ? 1.0f/(maxRadius - minRadius) : 0.0f;
+
+ const bool canFracture =
+ source.depth >= (uint16_t)destructibleParameters.minimumFractureDepth && // At or deeper than minimum fracture depth
+ (source.flags & DestructibleAssetImpl::DescendantUnfractureable) == 0 && // Some descendant cannot be fractured
+ (source.flags & DestructibleAssetImpl::UndamageableChunk) == 0 && // This chunk is not to be fractured (though a descendant may be)
+ (useLegacyChunkOverlap || source.depth >= destructible->getSupportDepth()); // If we're using the new chunk overlap tests, we will only fracture at or deeper than the support depth
+
+ const bool canCrumble = (destructibleParameters.flags & DestructibleParametersFlag::CRUMBLE_SMALLEST_CHUNKS) != 0 && (source.flags & DestructibleAssetImpl::UncrumbleableChunk) == 0;
+
+ const bool forceCrumbleOrRTFracture =
+ source.depth < (uint16_t)destructibleParameters.minimumFractureDepth && // minimum fracture depth deeper than this chunk
+ !hasChildren && // leaf chunk
+ (canCrumble || (destructibleParameters.flags & DestructibleParametersFlag::CRUMBLE_VIA_RUNTIME_FRACTURE) != 0); // set to crumble or RT fracture
+
+ if (canFracture || forceCrumbleOrRTFracture)
+ {
+ uint32_t virtualEventFlag = 0;
+ for (uint32_t it = 0; it < 2; ++it) // Try once as a real event, then possibly as a virtual event
+ {
+ if (overlapDistance < padding) // Overlap distance can change
+ {
+ float damageFraction = 1;
+ if (!useLegacyDamageSpread)
+ {
+ const float radius = maxRadius + overlapDistance;
+ if (radius > minRadius && recipRadiusRange > 0.0f)
+ {
+ if (radius < maxRadius)
+ {
+ damageFraction = PxPow( (maxRadius - radius)*recipRadiusRange, falloff);
+ }
+ else
+ {
+ damageFraction = 0.0f;
+ }
+ }
+ }
+ else
+ {
+ if (falloff > 0.0f && maxRadius > 0.0f)
+ {
+ damageFraction -= PxMax((maxRadius + overlapDistance) / maxRadius, 0.0f);
+ }
+ }
+ const float oldChunkDamage = chunk.damage;
+ float effectiveDamage = damageFraction * damage;
+ chunk.damage += effectiveDamage;
+
+ if ((chunk.damage >= behaviorGroup.damageThreshold) || (fromImpact && (behaviorGroup.materialStrength > 0)) || forceCrumbleOrRTFracture)
+ {
+ chunk.damage = behaviorGroup.damageThreshold;
+ // Fracture
+#if REDUCE_DAMAGE_TO_CHILD_CHUNKS
+ if (it == 0)
+ {
+ damage -= chunk.damage - oldChunkDamage;
+ }
+#endif
+ // this is an alternative deletion path (instead of using the delete flag on the fracture event)
+ // this can allow us to skip some parts of damageChunk() and fractureChunk(), but it involves duplicating some code
+ // we are still relying on the delete flag for now
+ if(destructible->mInDeleteChunkMode && false)
+ {
+ removeChunk(chunks[chunk.indexInAsset]);
+ uint16_t parentIndex = source.parentIndex;
+
+ Chunk* rootChunk = getRootChunk(chunk);
+ const bool dynamic = rootChunk != NULL ? (rootChunk->state & ChunkDynamic) != 0 : false;
+ if(!dynamic)
+ {
+ Chunk* parent = (parentIndex != DestructibleAssetImpl::InvalidChunkIndex) ? &chunks[parentIndex + destructible->getFirstChunkIndex()] : NULL;
+ const DestructibleAssetParametersNS::Chunk_Type* parentSource = parent ? (destructible->getDestructibleAsset()->mParams->chunks.buf + parent->indexInAsset) : NULL;
+ const uint32_t startIndex = parentSource ? parentSource->firstChildIndex + destructible->getFirstChunkIndex() : chunk.indexInAsset /* + actor first index */;
+ const uint32_t stopIndex = parentSource ? startIndex + parentSource->numChildren : startIndex + 1;
+ // Walk children of current parent (siblings)
+ for (uint32_t childIndex = startIndex; childIndex < stopIndex; ++childIndex)
+ {
+ Chunk& child = chunks[childIndex];
+ if (child.isDestroyed())
+ {
+ continue;
+ }
+ bool belowSupportDepth = (child.flags & ChunkBelowSupportDepth) != 0;
+ if (&child != &chunk && !belowSupportDepth)
+ {
+ // this is a sibling, which we can keep because we're at a supported depth or above
+ if ((child.state & ChunkVisible) == 0)
+ {
+ // Make this chunk shape visible, attach to root bone
+ if (dscene->appendShapes(child, dynamic))
+ {
+ destructible->setChunkVisibility(child.indexInAsset, true);
+ }
+ }
+ }
+ }
+ }
+
+ while(parentIndex != DestructibleAssetImpl::InvalidChunkIndex)
+ {
+ Chunk * parent = &chunks[parentIndex + destructible->getFirstChunkIndex()];
+ if(parent->state & ChunkVisible)
+ {
+ removeChunk(*parent);
+ }
+ PX_ASSERT(parent->indexInAsset == parentIndex + destructible->getFirstChunkIndex());
+ parentIndex = destructible->getDestructibleAsset()->mParams->chunks.buf[parent->indexInAsset].parentIndex;
+ }
+ }
+ else
+ {
+ FractureEvent& fractureEvent = output.insert();
+ fractureEvent.position = position;
+ fractureEvent.impulse = PxVec3(0.0f);
+ fractureEvent.chunkIndexInAsset = chunk.indexInAsset;
+ fractureEvent.destructibleID = destructible->getID();
+ fractureEvent.flags = virtualEventFlag;
+ fractureEvent.damageFraction = damageFraction;
+ const bool normalCrumbleCondition = canCrumble && !hasChildren && damageFraction*damage >= behaviorGroup.damageThreshold;
+ if (it == 0 && (normalCrumbleCondition || forceCrumbleOrRTFracture))
+ {
+ // Crumble
+ fractureEvent.flags |= FractureEvent::CrumbleChunk;
+ }
+ fractureEvent.deletionWeight = 0.0f;
+ if (destructibleParameters.debrisDepth >= 0 && source.depth >= (uint16_t)destructibleParameters.debrisDepth)
+ {
+ ++possibleDeleteChunks;
+// const float relativeDamage = behaviorGroup.damageThreshold > 0.0f ? effectiveDamage/behaviorGroup.damageThreshold : 1.0f;
+ fractureEvent.deletionWeight = 1.0f;// - PxExp(-relativeDamage); // Flat probability for now
+ totalDeleteChunkRelativeDamage += fractureEvent.deletionWeight;
+ }
+ }
+ }
+ if (!(destructibleParameters.flags & DestructibleParametersFlag::ACCUMULATE_DAMAGE))
+ {
+ chunk.damage = 0;
+ }
+ if (it == 1)
+ {
+ chunk.damage = oldChunkDamage;
+ }
+ }
+
+ if (it == 1)
+ {
+ break;
+ }
+
+ fractureCount = output.size() - originalEventBufferSize;
+ if (!hasChildren || fractureCount > 0)
+ {
+ break;
+ }
+
+ if (useLegacyChunkOverlap)
+ {
+ // Try treating this as a childless chunk.
+ overlapDistance -= chunk.localSphereRadius;
+ }
+
+ // If we have bounding sphere overlap, mark it as a virtual fracture event
+ virtualEventFlag = FractureEvent::Virtual;
+ }
+ }
+ else
+ if (useLegacyChunkOverlap)
+ {
+ // Legacy system relied on this being done in the loop above (in the other branch of the conditional)
+ overlapDistance -= chunk.localSphereRadius;
+ }
+
+ if (fractureCount > 0)
+ {
+ maxDepth = PxMax(depth, maxDepth);
+ }
+
+ if (hasChildren && overlapDistance < padding && depth < (uint32_t)stopDepth)
+ {
+ // Recurse
+ const uint32_t startIndex = source.firstChildIndex + destructible->getFirstChunkIndex();
+ const uint32_t stopIndex = startIndex + source.numChildren;
+ for (uint32_t childIndex = startIndex; childIndex < stopIndex; ++childIndex)
+ {
+ fractureCount += damageChunk(chunks[childIndex], position, direction, fromImpact, damage, damageRadius, outputs,
+ possibleDeleteChunks, totalDeleteChunkRelativeDamage, maxDepth, depth + 1, stopDepth, padding);
+ }
+ }
+
+ return fractureCount;
+}
+
+
+/*
+ fractureChunk causes the chunk referenced in fractureEvent to become a dynamic, standalone chunk.
+
+ If the chunk was invisible because it was an ancestor of a visible chunk, then the ancestor will desroyed,
+ and appropriate descendants (including the chunk in question) will be made visible.
+
+ Any chunks which become visible that are at, or deeper than, the support depth will also become standalone
+ dynamic chunks.
+*/
+
+void DestructibleStructure::fractureChunk(const FractureEvent& fractureEvent)
+{
+ DestructibleActorImpl* destructible = dscene->mDestructibles.direct(fractureEvent.destructibleID);
+ if (!destructible)
+ {
+ return;
+ }
+
+ destructible->wakeForEvent();
+
+ // ===SyncParams===
+ if(0 != destructible->getSyncParams().getUserActorID())
+ {
+ destructible->evaluateForHitChunkList(fractureEvent);
+ }
+
+ Chunk& chunk = chunks[fractureEvent.chunkIndexInAsset + destructible->getFirstChunkIndex()];
+
+ const PxVec3& position = fractureEvent.position;
+ const PxVec3& impulse = fractureEvent.impulse;
+ if(fractureEvent.flags & FractureEvent::CrumbleChunk)
+ {
+ chunk.flags |= ChunkCrumbled;
+ }
+
+ const DestructibleAssetParametersNS::Chunk_Type& source = destructible->getDestructibleAsset()->mParams->chunks.buf[chunk.indexInAsset];
+
+ if ((fractureEvent.flags & FractureEvent::Forced) == 0 && (source.flags & DestructibleAssetImpl::UnfracturableChunk) != 0)
+ {
+ return;
+ }
+
+ if (chunk.isDestroyed())
+ {
+ return;
+ }
+
+#if APEX_RUNTIME_FRACTURE
+ // The runtime fracture flag pre-empts the crumble flag
+ if (source.flags & DestructibleAssetImpl::RuntimeFracturableChunk)
+ {
+ chunk.flags |= ChunkRuntime;
+ chunk.flags &= ~(uint8_t)ChunkCrumbled;
+ }
+#endif
+
+ bool loneDynamicShape = false;
+ if ((chunk.state & ChunkVisible) != 0 && chunkIsSolitary(chunk) && (chunk.state & ChunkDynamic) != 0)
+ {
+ loneDynamicShape = true;
+ }
+
+ uint16_t depth = source.depth;
+
+ PxRigidDynamic* actor = getChunkActor(chunk);
+ PX_ASSERT(actor != NULL);
+
+ SCOPED_PHYSX_LOCK_READ(actor->getScene());
+ bool isKinematic = actor->getRigidBodyFlags() & physx::PxRigidBodyFlag::eKINEMATIC;
+
+ if (loneDynamicShape)
+ {
+ if(0 == (fractureEvent.flags & FractureEvent::Manual)) // if it is a manual fractureEvent, we do not consider giving it an impulse nor to crumble it
+ {
+ if (isKinematic)
+ {
+ PhysXObjectDescIntl* actorObjDesc = (PhysXObjectDescIntl*)dscene->mModule->mSdk->getPhysXObjectInfo(actor);
+ if (actorObjDesc != NULL)
+ {
+ uintptr_t& cindex = (uintptr_t&)actorObjDesc->userData;
+ if (cindex != 0)
+ {
+ // Dormant actor - turn dynamic
+ const uint32_t dormantActorIndex = (uint32_t)~cindex;
+ DormantActorEntry& dormantActorEntry = dscene->mDormantActors.direct(dormantActorIndex);
+ dormantActorEntry.actor = NULL;
+ dscene->mDormantActors.free(dormantActorIndex);
+ cindex = (uintptr_t)0;
+ {
+ SCOPED_PHYSX_LOCK_WRITE(actor->getScene());
+ actor->setRigidBodyFlag(physx::PxRigidBodyFlag::eKINEMATIC, false);
+ dscene->addActor(*actorObjDesc, *actor, dormantActorEntry.unscaledMass,
+ ((dormantActorEntry.flags & ActorFIFOEntry::IsDebris) != 0));
+ actor->wakeUp();
+ }
+ isKinematic = false;
+ }
+ }
+ }
+
+ if (!isKinematic && impulse.magnitudeSquared() > 0.0f && ((chunk.flags & ChunkCrumbled) != 0 || (fractureEvent.flags & FractureEvent::DamageFromImpact) == 0))
+ {
+ addChunkImpluseForceAtPos(chunk, impulse, position);
+ }
+ if ((chunk.flags & ChunkCrumbled))
+ {
+ crumbleChunk(fractureEvent, chunk);
+ }
+#if APEX_RUNTIME_FRACTURE
+ else if ((chunk.flags & ChunkRuntime))
+ {
+ runtimeFractureChunk(fractureEvent, chunk);
+ }
+#endif
+ return;
+ }
+ }
+
+ Chunk* rootChunk = getRootChunk(chunk);
+ const bool dynamic = rootChunk != NULL ? (rootChunk->state & ChunkDynamic) != 0 : false;
+
+ uint16_t parentIndex = source.parentIndex;
+ uint32_t chunkIndex = chunk.indexInAsset + destructible->getFirstChunkIndex();
+
+ //Sync the shadow scene if using physics based stress solver
+ if(stressSolver&&stressSolver->isPhysxBasedSim)
+ {
+ stressSolver->removeChunkFromShadowScene(false,chunkIndex);
+ }
+ for (;;)
+ {
+ Chunk* parent = (parentIndex != DestructibleAssetImpl::InvalidChunkIndex) ? &chunks[parentIndex + destructible->getFirstChunkIndex()] : NULL;
+ const DestructibleAssetParametersNS::Chunk_Type* parentSource = parent ? (destructible->getDestructibleAsset()->mParams->chunks.buf + parent->indexInAsset) : NULL;
+ const uint32_t startIndex = parentSource ? parentSource->firstChildIndex + destructible->getFirstChunkIndex() : chunkIndex;
+ const uint32_t stopIndex = parentSource ? startIndex + parentSource->numChildren : startIndex + 1;
+ // Walk children of current parent (siblings)
+ for (uint32_t childIndex = startIndex; childIndex < stopIndex; ++childIndex)
+ {
+ Chunk& child = chunks[childIndex];
+ if (child.isDestroyed())
+ {
+ continue;
+ }
+
+ bool belowSupportDepth = (child.flags & ChunkBelowSupportDepth) != 0;
+
+ if (&child != &chunk && !belowSupportDepth)
+ {
+ // this is a sibling, which we can keep because we're at a supported depth or above
+ if ((child.state & ChunkVisible) == 0)
+ {
+ // Make this chunk shape visible, attach to root bone
+ dscene->appendShapes(child, dynamic);
+ destructible->setChunkVisibility(child.indexInAsset, true);
+ }
+ }
+ else if (child.flags & ChunkCrumbled)
+ {
+ // This chunk needs to be crumbled
+ if (isKinematic)
+ {
+ crumbleChunk(fractureEvent, child, &impulse);
+ }
+ else
+ {
+ if (impulse.magnitudeSquared() > 0.0f)
+ {
+ addChunkImpluseForceAtPos(child, impulse, position);
+ }
+ crumbleChunk(fractureEvent, child);
+ }
+ if (parent)
+ {
+ parent->flags |= ChunkMissingChild;
+ }
+ setSupportInvalid(true);
+ }
+#if APEX_RUNTIME_FRACTURE
+ else if (child.flags & ChunkRuntime)
+ {
+ runtimeFractureChunk(fractureEvent, child);
+ if (parent)
+ {
+ parent->flags |= ChunkMissingChild;
+ }
+ setSupportInvalid(true);
+ }
+#endif
+ else
+ {
+ // This is either the chunk or a sibling, which we are breaking off
+ if ((fractureEvent.flags & FractureEvent::Silent) == 0 && dscene->mModule->m_chunkReport != NULL)
+ {
+ dscene->createChunkReportDataForFractureEvent(fractureEvent, destructible, child);
+ }
+ destructible->setChunkVisibility(child.indexInAsset, false); // In case its mesh changes
+
+ // if the fractureEvent is manually generated, we remove the chunk instead of simulating it
+ if(0 != (fractureEvent.flags & FractureEvent::DeleteChunk))
+ {
+ removeChunk(child);
+ }
+ else
+ {
+ PX_ASSERT(0 == (fractureEvent.flags & FractureEvent::DeleteChunk));
+ PxRigidDynamic* oldActor = getChunkActor(child);
+ PxRigidDynamic* newActor = dscene->createRoot(child, getChunkGlobalPose(child), true);
+ if (newActor != NULL && oldActor == actor)
+ {
+ physx::PxRigidDynamic* newRigidDynamic = newActor->is<physx::PxRigidDynamic>();
+ PxVec3 childCenterOfMass = newRigidDynamic->getGlobalPose().transform(newRigidDynamic->getCMassLocalPose().p);
+ {
+ SCOPED_PHYSX_LOCK_WRITE(actor->getScene());
+ newActor->setLinearVelocity(physx::PxRigidBodyExt::getVelocityAtPos(*actor, childCenterOfMass));
+ newActor->setAngularVelocity(actor->getAngularVelocity());
+ }
+ }
+ }
+
+ PX_ASSERT(child.isDestroyed() == ((child.state & ChunkVisible) == 0));
+ if (!child.isDestroyed())
+ {
+ destructible->setChunkVisibility(child.indexInAsset, true);
+ if (&child == &chunk)
+ {
+ if (impulse.magnitudeSquared() > 0.0f)
+ {
+ // Bring position nearer to chunk for better effect
+ PxVec3 newPosition = position;
+ const PxVec3 c = getChunkWorldCentroid(child);
+ const PxVec3 disp = newPosition - c;
+ const float d2 = disp.magnitudeSquared();
+ if (d2 > child.localSphereRadius * child.localSphereRadius)
+ {
+ newPosition = c + disp * (disp.magnitudeSquared() * PxRecipSqrt(d2));
+ }
+ addChunkImpluseForceAtPos(child, impulse, newPosition);
+ }
+ }
+ addDust(child);
+ }
+ if (parent)
+ {
+ parent->flags |= ChunkMissingChild;
+ }
+ setSupportInvalid(true);
+ }
+ }
+ if (!parent)
+ {
+ break;
+ }
+ if (parent->state & ChunkVisible)
+ {
+ removeChunk(*parent);
+ }
+ else if (parent->isDestroyed())
+ {
+ break;
+ }
+ parent->clearShapes();
+ chunkIndex = parentIndex;
+ parentIndex = parentSource->parentIndex;
+ PX_ASSERT(depth > 0);
+ if (depth > 0)
+ {
+ --depth;
+ }
+ }
+
+ dscene->capDynamicActorCount();
+}
+
+void DestructibleStructure::crumbleChunk(const FractureEvent& fractureEvent, Chunk& chunk, const PxVec3* impulse)
+{
+ if (chunk.isDestroyed())
+ {
+ return;
+ }
+
+ DestructibleActorImpl* destructible = dscene->mDestructibles.direct(chunk.destructibleID);
+
+#if APEX_RUNTIME_FRACTURE
+ if ((destructible->getDestructibleParameters().flags & DestructibleParametersFlag::CRUMBLE_VIA_RUNTIME_FRACTURE) != 0)
+ {
+ runtimeFractureChunk(fractureEvent, chunk);
+ return;
+ }
+#else
+ PX_UNUSED(fractureEvent);
+#endif
+
+ dscene->getChunkReportData(chunk, ApexChunkFlag::DESTROYED_CRUMBLED);
+
+ PX_ASSERT(chunk.reportID < dscene->mChunkReportHandles.size() || chunk.reportID == DestructibleScene::InvalidReportID);
+ if (chunk.reportID < dscene->mChunkReportHandles.size())
+ {
+ IntPair& handle = dscene->mChunkReportHandles[chunk.reportID];
+ if ( (uint32_t)handle.i0 < dscene->mDamageEventReportData.size() )
+ {
+ ApexDamageEventReportDataImpl& DamageEventReport = dscene->mDamageEventReportData[(uint32_t)handle.i0];
+
+ DamageEventReport.impactDamageActor = fractureEvent.impactDamageActor;
+ DamageEventReport.appliedDamageUserData = fractureEvent.appliedDamageUserData;
+ DamageEventReport.hitPosition = fractureEvent.position;
+ DamageEventReport.hitDirection = fractureEvent.hitDirection;
+ }
+ }
+
+ EmitterActor* emitterToUse = destructible->getCrumbleEmitter() && destructible->isCrumbleEmitterEnabled() ? destructible->getCrumbleEmitter() : NULL;
+
+ const float crumbleParticleSpacing = destructible->getCrumbleParticleSpacing();
+
+ if (crumbleParticleSpacing > 0.0f && (emitterToUse != NULL || dscene->mModule->m_chunkCrumbleReport != NULL))
+ {
+ physx::Array<PxVec3> volumeFillPos;
+ /// \todo expose jitter
+ const float jitter = 0.25f;
+ for (uint32_t hullIndex = destructible->getDestructibleAsset()->getChunkHullIndexStart(chunk.indexInAsset); hullIndex < destructible->getDestructibleAsset()->getChunkHullIndexStop(chunk.indexInAsset); ++hullIndex)
+ {
+ destructible->getDestructibleAsset()->chunkConvexHulls[hullIndex].fill(volumeFillPos, getChunkGlobalPose(chunk), destructible->getScale(), destructible->getCrumbleParticleSpacing(), jitter, 250, true);
+ }
+ destructible->spawnParticles(emitterToUse, dscene->mModule->m_chunkCrumbleReport, chunk, volumeFillPos, true, impulse);
+ }
+
+ if (chunk.state & ChunkVisible)
+ {
+ removeChunk(chunk);
+ }
+}
+
+#if APEX_RUNTIME_FRACTURE
+void DestructibleStructure::runtimeFractureChunk(const FractureEvent& fractureEvent, Chunk& chunk)
+{
+ if (chunk.isDestroyed())
+ {
+ return;
+ }
+
+ DestructibleActorImpl* destructible = dscene->mDestructibles.direct(chunk.destructibleID);
+ PX_UNUSED(destructible);
+
+ if (destructible->getRTActor() != NULL)
+ {
+ destructible->getRTActor()->patternFracture(fractureEvent,true);
+ }
+
+ if (chunk.state & ChunkVisible)
+ {
+ removeChunk(chunk);
+ }
+}
+#endif
+
+void DestructibleStructure::addDust(Chunk& chunk)
+{
+ PX_UNUSED(chunk);
+#if 0 // Not implementing dust in 1.2.0
+ if (chunk.isDestroyed())
+ {
+ return;
+ }
+
+ DestructibleActor* destructible = dscene->mDestructibles.direct(chunk.destructibleID);
+
+ EmitterActor* emitterToUse = destructible->getDustEmitter() && destructible->getDustEmitter() ? destructible->getDustEmitter() : NULL;
+
+ const float dustParticleSpacing = destructible->getDustParticleSpacing();
+
+ if (dustParticleSpacing > 0.0f && (emitterToUse != NULL || dscene->mModule->m_chunkDustReport != NULL))
+ {
+ physx::Array<PxVec3> tracePos;
+ /// \todo expose jitter
+ const float jitter = 0.25f;
+ destructible->getAsset()->traceSurfaceBoundary(tracePos, chunk.indexInAsset, getChunkGlobalPose(chunk), destructible->getScale(), dustParticleSpacing, jitter, 0.5f * dustParticleSpacing, 250);
+ destructible->spawnParticles(emitterToUse, dscene->mModule->m_chunkDustReport, chunk, tracePos);
+ }
+#endif
+}
+
+void DestructibleStructure::removeChunk(Chunk& chunk)
+{
+ dscene->scheduleChunkShapesForDelete(chunk);
+ DestructibleActorImpl* destructible = dscene->mDestructibles.direct(chunk.destructibleID);
+
+ //Sync the shadow scene if using physics based stress solver
+ if(stressSolver&&stressSolver->isPhysxBasedSim)
+ {
+ uint32_t chunkIndex = chunk.indexInAsset + destructible->getFirstChunkIndex();
+ stressSolver->removeChunkFromShadowScene(false,chunkIndex);
+ }
+ destructible->setChunkVisibility(chunk.indexInAsset, false);
+ const DestructibleAssetParametersNS::Chunk_Type& source = destructible->getDestructibleAsset()->mParams->chunks.buf[chunk.indexInAsset];
+ if (source.parentIndex != DestructibleAssetImpl::InvalidChunkIndex)
+ {
+ Chunk& parent = chunks[source.parentIndex + destructible->getFirstChunkIndex()];
+ parent.flags |= ChunkMissingChild;
+ }
+ // Remove from static roots list (this no-ops if the chunk was not in the static roots list)
+ destructible->getStaticRoots().free(chunk.indexInAsset);
+ // No more static chunks, make sure this actor field is cleared
+ if (actorForStaticChunks != NULL)
+ {
+ SCOPED_PHYSX_LOCK_READ(actorForStaticChunks->getScene());
+ if (actorForStaticChunks->getNbShapes() == 0)
+ {
+ actorForStaticChunks = NULL;
+ }
+ }
+}
+
+struct ChunkIsland
+{
+ ChunkIsland() : actor(NULL), flags(0), actorIslandDataIndex(0) {}
+
+ physx::Array<uint32_t> indices;
+ PxRigidDynamic* actor;
+ uint32_t flags;
+ uint32_t actorIslandDataIndex;
+};
+
+struct ActorIslandData
+{
+ ActorIslandData() : islandCount(0), flags(0) {}
+
+ enum Flag
+ {
+ SubmmittedForMassRecalc = 0x1
+ };
+
+ uint32_t islandCount;
+ uint32_t flags;
+};
+
+void DestructibleStructure::separateUnsupportedIslands()
+{
+ // Find instances where an island broke away from an existing
+ // island, but instead of two new islands there should actually
+ // have been three (or more) because of discontinuities. Fix it.
+
+ PX_PROFILE_ZONE("DestructibleSeparateUnsupportedIslands", GetInternalApexSDK()->getContextId());
+ physx::Array<uint32_t> chunksRemaining = supportDepthChunks;
+ physx::Array<ChunkIsland> islands;
+
+ // Instead of sorting to find islands that share actors, will merely count.
+ // Since we expect the number of new islands to be small, we should be able
+ // to eliminate most of the work this way.
+ physx::Array<ActorIslandData> islandData(dscene->mActorFIFO.size() + dscene->mDormantActors.usedCount() + 1); // Max size needed
+
+ while (chunksRemaining.size())
+ {
+ uint32_t chunkIndex = chunksRemaining.back();
+ chunksRemaining.popBack();
+
+ Chunk& chunk = chunks[chunkIndex];
+
+ if ((chunk.state & ChunkTemp0) != 0)
+ {
+ continue;
+ }
+ PxRigidDynamic* actor = dscene->chunkIntact(chunk);
+ if (actor == NULL)
+ {
+ // we conveniently update and find recently broken links here since this loop is executed here
+ if(NULL != stressSolver)
+ {
+ if(!stressSolver->isPhysxBasedSim)
+ {
+ stressSolver->onUpdate(chunkIndex);
+ }
+ }
+
+ continue;
+ }
+ SCOPED_PHYSX_LOCK_READ(actor->getScene());
+ ChunkIsland& island = islands.insert();
+ island.indices.pushBack(chunkIndex);
+ island.actor = actor;
+ const bool actorStatic = (chunk.state & ChunkDynamic) == 0;
+ PhysXObjectDesc* actorObjDesc = (PhysXObjectDesc*) dscene->mModule->mSdk->getPhysXObjectInfo(island.actor);
+ int32_t index = -(int32_t)(intptr_t)actorObjDesc->userData;
+ if (index > 0 && actor->getRigidBodyFlags() & physx::PxRigidBodyFlag::eKINEMATIC)
+ {
+ index = int32_t(dscene->mDormantActors.getRank((uint32_t)index-1) + dscene->mActorFIFO.size()); // Get to the dormant portion of the array
+ }
+ PX_ASSERT(index >= 0 && index < (int32_t)islandData.size());
+
+ island.actorIslandDataIndex = (uint32_t)index;
+
+ ++islandData[(uint32_t)index].islandCount;
+ chunk.state |= ChunkTemp0;
+ for (uint32_t i = 0; i < island.indices.size(); ++i)
+ {
+ const uint32_t chunkIndex = island.indices[i];
+ Chunk& chunk = chunks[chunkIndex];
+ island.flags |= chunk.flags & ChunkExternallySupported;
+ DestructibleActorImpl* dactor = dscene->mDestructibles.direct(chunk.destructibleID);
+ const uint32_t visibleAssetIndex = chunk.visibleAncestorIndex >= 0 ? (uint32_t)chunk.visibleAncestorIndex - dactor->getFirstChunkIndex() : chunk.indexInAsset;
+ DestructibleAssetParametersNS::Chunk_Type& source = dactor->getDestructibleAsset()->mParams->chunks.buf[visibleAssetIndex];
+ if (source.flags & (DestructibleAssetImpl::UnfracturableChunk | DestructibleAssetImpl::DescendantUnfractureable))
+ {
+ island.flags |= ChunkExternallySupported;
+ }
+ const uint32_t firstOverlapIndex = firstOverlapIndices[chunkIndex];
+ const uint32_t stopOverlapIndex = firstOverlapIndices[chunkIndex + 1];
+ for (uint32_t j = firstOverlapIndex; j < stopOverlapIndex; ++j)
+ {
+ const uint32_t overlapChunkIndex = overlaps[j];
+ Chunk& overlapChunk = chunks[overlapChunkIndex];
+ if ((overlapChunk.state & ChunkTemp0) == 0)
+ {
+ PxRigidDynamic* overlapActor = dscene->chunkIntact(overlapChunk);
+ if (overlapActor != NULL)
+ {
+ if (overlapActor == actor ||
+ ((island.flags & ChunkExternallySupported) != 0 &&
+ actorStatic && (overlapChunk.state & ChunkDynamic) == 0))
+ {
+ island.indices.pushBack(overlapChunkIndex);
+ overlapChunk.state |= (uint32_t)ChunkTemp0;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ physx::Array<PxRigidDynamic*> dirtyActors; // will need mass properties recalculated
+
+ physx::Array<DestructibleActorImpl*> destructiblesInIsland;
+
+ for (uint32_t islandNum = 0; islandNum < islands.size(); ++islandNum)
+ {
+ ChunkIsland& island = islands[islandNum];
+
+ SCOPED_PHYSX_LOCK_READ(island.actor->getScene());
+ if (island.actor->getNbShapes() == 0)
+ {
+ // This can happen if this island's chunks have all been destroyed
+ continue;
+ }
+ PhysXObjectDescIntl* actorObjDesc = dscene->mModule->mSdk->getGenericPhysXObjectInfo(island.actor);
+ ActorIslandData& data = islandData[island.actorIslandDataIndex];
+ PX_ASSERT(data.islandCount > 0);
+ const bool dynamic = !(island.actor->getRigidBodyFlags() & physx::PxRigidBodyFlag::eKINEMATIC);
+ if (!(island.flags & ChunkExternallySupported) || dynamic)
+ {
+ if (data.islandCount > 1)
+ {
+ --data.islandCount;
+ createDynamicIsland(island.indices);
+ actorObjDesc = dscene->mModule->mSdk->getGenericPhysXObjectInfo(island.actor); // Need to get this again, since the pointer can change from createDynamicIsland()!
+ if ((data.flags & ActorIslandData::SubmmittedForMassRecalc) == 0)
+ {
+ dirtyActors.pushBack(island.actor);
+ data.flags |= ActorIslandData::SubmmittedForMassRecalc;
+ }
+ if (island.actorIslandDataIndex > 0 && island.actorIslandDataIndex <= (uint32_t)dscene->mActorFIFO.size())
+ {
+ dscene->mActorFIFO[island.actorIslandDataIndex-1].age = 0.0f; // Reset the age of the original actor. It will have a different set of chunks after this
+ }
+ }
+ else
+ {
+ if (!dynamic)
+ {
+ createDynamicIsland(island.indices);
+ actorObjDesc = dscene->mModule->mSdk->getGenericPhysXObjectInfo(island.actor); // Need to get this again, since the pointer can change from createDynamicIsland()!
+ // If we ever are able to simply clear the BF_KINEMATIC flag, remember to recalculate the mass properties here.
+ }
+ else
+ {
+ // Recalculate list of destructibles on this dynamic PxRigidDynamic island
+ destructiblesInIsland.reset();
+ for (uint32_t i = 0; i < actorObjDesc->mApexActors.size(); ++i)
+ {
+ const DestructibleActor* dActor = static_cast<const DestructibleActor*>(actorObjDesc->mApexActors[i]);
+ ((DestructibleActorProxy*)dActor)->impl.unreferencedByActor(island.actor);
+ }
+ actorObjDesc->mApexActors.reset();
+ bool isDebris = true; // Until proven otherwise
+ for (uint32_t i = 0; i < island.indices.size(); ++i)
+ {
+ const uint32_t chunkIndex = island.indices[i];
+ Chunk& chunk = chunks[chunkIndex];
+ DestructibleActorImpl* destructible = dscene->mDestructibles.direct(chunk.destructibleID);
+ const DestructibleParameters& parameters = destructible->getDestructibleParameters();
+ DestructibleAssetParametersNS::Chunk_Type& assetChunk = destructible->getDestructibleAsset()->mParams->chunks.buf[chunk.indexInAsset];
+ if (parameters.debrisDepth >= 0)
+ {
+ if (assetChunk.depth < (island.indices.size() == 1 ? parameters.debrisDepth : (parameters.debrisDepth + 1))) // If there is more than one chunk in an island, insist that all chunks must lie strictly _below_ the debris depth to be considered debris
+ {
+ isDebris = false;
+ }
+ }
+ else
+ {
+ isDebris = false;
+ }
+ if ((destructible->getInternalFlags() & DestructibleActorImpl::IslandMarker) == 0)
+ {
+ destructiblesInIsland.pushBack(destructible);
+ PX_ASSERT(actorObjDesc->mApexActors.find(destructible->getAPI()) == actorObjDesc->mApexActors.end());
+ actorObjDesc->mApexActors.pushBack(destructible->getAPI());
+ destructible->referencedByActor(island.actor);
+ destructible->getInternalFlags() |= DestructibleActorImpl::IslandMarker;
+ }
+ }
+ PX_ASSERT(island.actorIslandDataIndex > 0);
+ if (isDebris && island.actorIslandDataIndex > 0 && island.actorIslandDataIndex <= (uint32_t)dscene->mActorFIFO.size())
+ {
+ dscene->mActorFIFO[island.actorIslandDataIndex-1].flags |= ActorFIFOEntry::IsDebris;
+ }
+ for (uint32_t i = 0; i < actorObjDesc->mApexActors.size(); ++i)
+ {
+ destructiblesInIsland[i]->getInternalFlags() &= ~(uint16_t)DestructibleActorImpl::IslandMarker;
+ }
+ }
+ }
+ }
+
+ if (actorObjDesc->userData == 0) // initially static chunk
+ {
+ // Recalculate list of destructibles on the kinematic PxRigidDynamic island
+ for (uint32_t i = actorObjDesc->mApexActors.size(); i--;)
+ {
+ DestructibleActorProxy* proxy = const_cast<DestructibleActorProxy*>(static_cast<const DestructibleActorProxy*>(actorObjDesc->mApexActors[i]));
+ if (proxy->impl.getStaticRoots().usedCount() == 0)
+ {
+ proxy->impl.unreferencedByActor(island.actor);
+ actorObjDesc->mApexActors.replaceWithLast(i);
+ }
+ }
+ }
+
+ if (!dynamic)
+ {
+ if (actorObjDesc->mApexActors.size() == 0)
+ {
+ // if all destructible references have been removed
+ if (actorForStaticChunks == island.actor)
+ {
+ actorForStaticChunks = NULL;
+ }
+ }
+ else
+ {
+ dirtyActors.pushBack(island.actor);
+ }
+ }
+ }
+
+ for (uint32_t dirtyActorNum = 0; dirtyActorNum < dirtyActors.size(); ++dirtyActorNum)
+ {
+ PxRigidDynamic* actor = dirtyActors[dirtyActorNum];
+ SCOPED_PHYSX_LOCK_READ(actor->getScene());
+
+ const uint32_t nShapes = actor->getNbShapes();
+ if (nShapes > 0)
+ {
+ float mass = 0.0f;
+ for (uint32_t i = 0; i < nShapes; ++i)
+ {
+ PxShape* shape;
+ actor->getShapes(&shape, 1, i);
+ DestructibleStructure::Chunk* chunk = dscene->getChunk(shape);
+ if (chunk == NULL || !chunk->isFirstShape(shape)) // BRG OPTIMIZE
+ {
+ continue;
+ }
+ DestructibleActorImpl* dactor = dscene->mDestructibles.direct(chunk->destructibleID);
+ mass += dactor->getChunkMass(chunk->indexInAsset);
+ }
+ if (mass > 0.0f)
+ {
+ // Updating mass here, since it won't cause extra work for MOI calculator
+ SCOPED_PHYSX_LOCK_WRITE(actor->getScene());
+ physx::PxRigidBodyExt::setMassAndUpdateInertia(*actor, dscene->scaleMass(mass), NULL, true); // Ensures that we count shapes that have been flagged as non-simulation shapes
+ }
+ }
+ }
+
+ for (uint32_t supportChunkNum = 0; supportChunkNum < supportDepthChunks.size(); ++supportChunkNum)
+ {
+ chunks[supportDepthChunks[supportChunkNum]].state &= ~(uint32_t)ChunkTemp0;
+ }
+
+ setSupportInvalid(false);
+}
+
+void DestructibleStructure::createDynamicIsland(const physx::Array<uint32_t>& indices)
+{
+ PX_PROFILE_ZONE("DestructibleCreateDynamicIsland", GetInternalApexSDK()->getContextId());
+
+ if (indices.size() == 0)
+ {
+ return;
+ }
+
+ PxRigidDynamic* actor = dscene->chunkIntact(chunks[indices[0]]);
+ PX_ASSERT(actor != NULL);
+
+ SCOPED_PHYSX_LOCK_READ(actor->getScene());
+ SCOPED_PHYSX_LOCK_WRITE(actor->getScene());
+
+ physx::Array<Chunk*> removedChunks;
+ for (uint32_t i = 0; i < indices.size(); ++i)
+ {
+ Chunk& chunk = chunks[indices[i]];
+ if (!chunk.isDestroyed())
+ {
+ Chunk* rootChunk = getRootChunk(chunk);
+ if (rootChunk && (rootChunk->state & ChunkTemp1) == 0)
+ {
+ rootChunk->state |= (uint32_t)ChunkTemp1;
+ removedChunks.pushBack(rootChunk);
+ if(stressSolver && stressSolver->isPhysxBasedSim)
+ {
+ stressSolver->removeChunkFromIsland(indices[i]);
+ }
+ PxRigidDynamic* actor = getChunkActor(*rootChunk);
+ if (!(actor->getRigidBodyFlags() & physx::PxRigidBodyFlag::eKINEMATIC))
+ {
+ actor->wakeUp();
+ }
+ }
+ }
+ else
+ {
+ // This won't mark the found chunks with ChunkTemp1, but we should not
+ // have any duplicates from this search
+ DestructibleScene::CollectVisibleChunks chunkOp(removedChunks);
+ dscene->forSubtree(chunk, chunkOp);
+ }
+ }
+ if (removedChunks.size() == 0)
+ {
+ return;
+ }
+
+ // Eliminate chunks that are unfractureable.
+ for (uint32_t i = 0; i < removedChunks.size(); ++i)
+ {
+ Chunk& chunk = *removedChunks[i];
+ DestructibleActorImpl* destructible = dscene->mDestructibles.direct(chunk.destructibleID);
+ destructible->getStaticRoots().free(chunk.indexInAsset);
+ DestructibleAssetParametersNS::Chunk_Type& source = destructible->getDestructibleAsset()->mParams->chunks.buf[chunk.indexInAsset];
+ if ((source.flags & DestructibleAssetImpl::DescendantUnfractureable) != 0)
+ {
+ chunk.state &= ~(uint32_t)ChunkTemp1;
+ removedChunks[i] = NULL;
+ for (uint32_t j = 0; j < source.numChildren; ++j)
+ {
+ const uint32_t childIndex = destructible->getFirstChunkIndex() + source.firstChildIndex + j;
+ Chunk& child = chunks[childIndex];
+ removedChunks.pushBack(&child);
+ destructible->setChunkVisibility(child.indexInAsset, false); // In case its mesh changes
+ dscene->appendShapes(child, true);
+ destructible->setChunkVisibility(child.indexInAsset, true);
+ }
+ removeChunk(chunk);
+ }
+ if ((source.flags & DestructibleAssetImpl::UnfracturableChunk) != 0)
+ {
+ if (actor->getRigidBodyFlags() & physx::PxRigidBodyFlag::eKINEMATIC)
+ {
+ chunk.state &= ~(uint32_t)ChunkTemp1;
+ removedChunks[i] = NULL;
+ }
+ }
+ }
+
+ uint32_t convexShapeCount = 0;
+ uint32_t chunkCount = 0;
+ for (uint32_t i = 0; i < removedChunks.size(); ++i)
+ {
+ Chunk* chunk = removedChunks[i];
+ if (chunk == NULL)
+ {
+ continue;
+ }
+ DestructibleActorImpl* destructible = dscene->mDestructibles.direct(chunk->destructibleID);
+ removedChunks[chunkCount++] = chunk;
+
+ convexShapeCount += destructible->getDestructibleAsset()->getChunkHullCount(chunk->indexInAsset);
+ }
+ for (uint32_t i = chunkCount; i < removedChunks.size(); ++i)
+ {
+ removedChunks[i] = NULL;
+ }
+
+ PxRigidDynamic* newActor = NULL;
+
+ physx::Array<DestructibleActorImpl*> destructiblesInIsland;
+
+ float mass = 0.0f;
+
+ uint32_t* convexShapeCounts = (uint32_t*)PxAlloca(sizeof(uint32_t) * chunkCount);
+ PxConvexMeshGeometry* convexShapeDescs = (PxConvexMeshGeometry*)PxAlloca(sizeof(PxConvexMeshGeometry) * convexShapeCount);
+ uint32_t createdShapeCount = 0;
+ bool isDebris = true; // until proven otherwise
+ uint32_t minDepth = 0xFFFFFFFF;
+ bool takesImpactDamage = false;
+ float minContactReportThreshold = PX_MAX_F32;
+ PhysX3DescTemplateImpl physX3Template;
+
+ for (uint32_t i = 0; i < chunkCount; ++i)
+ {
+ Chunk* chunk = removedChunks[i];
+
+ DestructibleActorImpl* destructible = dscene->mDestructibles.direct(chunk->destructibleID);
+ destructible->getPhysX3Template(physX3Template);
+ if ((destructible->getInternalFlags() & DestructibleActorImpl::IslandMarker) == 0)
+ {
+ destructiblesInIsland.pushBack(destructible);
+ destructible->getInternalFlags() |= DestructibleActorImpl::IslandMarker;
+
+ if (!newActor)
+ {
+ newActor = dscene->mPhysXScene->getPhysics().createRigidDynamic(actor->getGlobalPose());
+
+ PX_ASSERT(newActor);
+ physX3Template.apply(newActor);
+
+ if (destructible->getDestructibleParameters().dynamicChunksDominanceGroup < 32)
+ {
+ newActor->setDominanceGroup(destructible->getDestructibleParameters().dynamicChunksDominanceGroup);
+ }
+
+ newActor->setActorFlag(PxActorFlag::eSEND_SLEEP_NOTIFIES, true);
+ const DestructibleActorParamNS::BehaviorGroup_Type& behaviorGroup = destructible->getBehaviorGroup(chunk->indexInAsset);
+ newActor->setMaxDepenetrationVelocity(behaviorGroup.maxDepenetrationVelocity);
+ }
+ }
+ DestructibleParameters& parameters = destructible->getDestructibleParameters();
+ DestructibleAssetParametersNS::Chunk_Type& assetChunk = destructible->getDestructibleAsset()->mParams->chunks.buf[chunk->indexInAsset];
+ if (parameters.debrisDepth >= 0)
+ {
+ if (assetChunk.depth < (chunkCount == 1 ? parameters.debrisDepth : (parameters.debrisDepth + 1))) // If there is more than one chunk in an island, insist that all chunks must lie strictly _below_ the debris depth to be considered debris
+ {
+ isDebris = false;
+ }
+ }
+ else
+ {
+ isDebris = false;
+ }
+ if (assetChunk.depth < minDepth)
+ {
+ minDepth = assetChunk.depth;
+ }
+ if (destructible->takesImpactDamageAtDepth(assetChunk.depth))
+ {
+ takesImpactDamage = true;
+ minContactReportThreshold = PxMin(minContactReportThreshold,
+ destructible->getContactReportThreshold(*chunk));
+ }
+
+ chunk->state &= ~(uint32_t)ChunkTemp1;
+ mass += destructible->getChunkMass(chunk->indexInAsset);
+
+ const uint32_t oldShapeCount = newActor->getNbShapes();
+ for (uint32_t hullIndex = destructible->getDestructibleAsset()->getChunkHullIndexStart(chunk->indexInAsset); hullIndex < destructible->getDestructibleAsset()->getChunkHullIndexStop(chunk->indexInAsset); ++hullIndex)
+ {
+ PxConvexMeshGeometry& convexShapeDesc = convexShapeDescs[createdShapeCount];
+ PX_PLACEMENT_NEW(&convexShapeDesc, PxConvexMeshGeometry);
+
+ // Need to get PxConvexMesh
+ // Shape(s):
+ PxTransform localPose = getChunkLocalPose(*chunk);
+
+ convexShapeDesc.convexMesh = destructible->getConvexMesh(hullIndex);
+ // Make sure we can get a collision mesh
+ if (!convexShapeDesc.convexMesh)
+ {
+ PX_ALWAYS_ASSERT();
+ return;
+ }
+ convexShapeDesc.scale.scale = destructible->getScale();
+ // Divide out the cooking scale to get the proper scaling
+ const PxVec3 cookingScale = dscene->getModuleDestructible()->getChunkCollisionHullCookingScale();
+ convexShapeDesc.scale.scale.x /= cookingScale.x;
+ convexShapeDesc.scale.scale.y /= cookingScale.y;
+ convexShapeDesc.scale.scale.z /= cookingScale.z;
+ if ((convexShapeDesc.scale.scale - PxVec3(1.0f)).abs().maxElement() < 10*PX_EPS_F32)
+ {
+ convexShapeDesc.scale.scale = PxVec3(1.0f);
+ }
+
+ PxShape* shape;
+
+ shape = newActor->createShape(convexShapeDesc,
+ physX3Template.materials.begin(),
+ static_cast<uint16_t>(physX3Template.materials.size()),
+ static_cast<physx::PxShapeFlags>(physX3Template.shapeFlags));
+ shape->setLocalPose(localPose);
+ physX3Template.apply(shape);
+
+ physx::PxPairFlags pairFlag = (physx::PxPairFlags)physX3Template.contactReportFlags;
+ if (takesImpactDamage)
+ {
+ pairFlag /* |= PxPairFlag::eNOTIFY_CONTACT_FORCES */
+ |= PxPairFlag::eNOTIFY_THRESHOLD_FORCE_PERSISTS
+ | PxPairFlag::eNOTIFY_THRESHOLD_FORCE_FOUND
+ | PxPairFlag::eNOTIFY_CONTACT_POINTS;
+ }
+
+ if (destructible->getBehaviorGroup(chunk->indexInAsset).materialStrength > 0.0f)
+ {
+ pairFlag |= PxPairFlag::eMODIFY_CONTACTS;
+ }
+
+ if (dscene->mApexScene->getApexPhysX3Interface())
+ dscene->mApexScene->getApexPhysX3Interface()->setContactReportFlags(shape, pairFlag, destructible->getAPI(), chunk->indexInAsset);
+
+
+ ++createdShapeCount;
+ }
+ dscene->scheduleChunkShapesForDelete(*chunk);
+ convexShapeCounts[i] = newActor->getNbShapes() - oldShapeCount;
+ }
+
+ if (destructiblesInIsland.size() == 0)
+ {
+ return;
+ }
+
+ /*
+ What if the different destructibles have different properties? Which to choose for the island? For now we'll just choose one.
+ */
+ DestructibleActorImpl* templateDestructible = destructiblesInIsland[0];
+
+ newActor->setRigidBodyFlag(PxRigidBodyFlag::eKINEMATIC, false);
+ if (takesImpactDamage)
+ {
+ // Set contact report threshold if the actor can take impact damage
+ newActor->setContactReportThreshold(minContactReportThreshold);
+ }
+ PxRigidBodyExt::setMassAndUpdateInertia(*newActor, dscene->scaleMass(mass));
+
+ {
+ dscene->mActorsToAddIndexMap[newActor] = dscene->mActorsToAdd.size();
+ dscene->mActorsToAdd.pushBack(newActor);
+
+ float initWakeCounter = dscene->mPhysXScene->getWakeCounterResetValue();
+ newActor->setWakeCounter(initWakeCounter);
+ }
+
+ // Use the templateDestructible to start the actorDesc, then add the other destructibles in the island
+ PhysXObjectDescIntl* actorObjDesc = dscene->mModule->mSdk->createObjectDesc(templateDestructible->getAPI(), newActor);
+ actorObjDesc->userData = 0;
+ templateDestructible->setActorObjDescFlags(actorObjDesc, minDepth);
+ templateDestructible->referencedByActor(newActor); // needs to be called after setActorObjDescFlags
+ // While we're at it, clear the marker flags here
+ templateDestructible->getInternalFlags() &= ~(uint16_t)DestructibleActorImpl::IslandMarker;
+ for (uint32_t i = 1; i < destructiblesInIsland.size(); ++i)
+ {
+ DestructibleActorImpl* destructibleInIsland = destructiblesInIsland[i];
+ PX_ASSERT(actorObjDesc->mApexActors.find(destructibleInIsland->getAPI()) == actorObjDesc->mApexActors.end());
+ actorObjDesc->mApexActors.pushBack(destructibleInIsland->getAPI());
+ destructibleInIsland->referencedByActor(newActor);
+ destructibleInIsland->getInternalFlags() &= ~(uint16_t)DestructibleActorImpl::IslandMarker;
+ }
+
+ dscene->addActor(*actorObjDesc, *newActor, mass, isDebris);
+
+ uint32_t newIslandID = newPxActorIslandReference(*removedChunks[0], *newActor);
+ uint32_t shapeStart = 0;
+ const uint32_t actorShapeCount = newActor->getNbShapes();
+ PX_ALLOCA(shapeArray, physx::PxShape*, actorShapeCount);
+ PxRigidActor* rigidActor = newActor->is<physx::PxRigidActor>();
+ rigidActor->getShapes(shapeArray, actorShapeCount);
+ for (uint32_t i = 0; i < chunkCount; ++i)
+ {
+ Chunk& chunk = *removedChunks[i];
+ chunk.islandID = newIslandID;
+ uint32_t shapeCount = convexShapeCounts[i];
+ PxShape* const* newShapes = shapeArray + shapeStart;
+ shapeStart += shapeCount;
+ DestructibleActorImpl* destructible = dscene->mDestructibles.direct(chunk.destructibleID);
+ PX_ASSERT(this == destructible->getStructure());
+ destructible->setChunkVisibility(chunk.indexInAsset, false);
+ chunk.state |= (uint32_t)ChunkVisible | (uint32_t)ChunkDynamic;
+ destructible->setChunkVisibility(chunk.indexInAsset, true);
+ chunk.setShapes(newShapes, shapeCount);
+ DestructibleScene::VisibleChunkSetDescendents chunkOp(chunk.indexInAsset+destructible->getFirstChunkIndex(), true);
+ dscene->forSubtree(chunk, chunkOp, true);
+ for (uint32_t j = 0; j < shapeCount; ++j)
+ {
+ PhysXObjectDescIntl* shapeObjDesc = dscene->mModule->mSdk->createObjectDesc(destructible->getAPI(), newShapes[j]);
+ shapeObjDesc->userData = &chunk;
+ }
+ }
+
+ if (newActor && dscene->getModuleDestructible()->m_destructiblePhysXActorReport != NULL)
+ {
+ dscene->getModuleDestructible()->m_destructiblePhysXActorReport->onPhysXActorCreate(*newActor);
+ }
+
+ //===SyncParams===
+ evaluateForHitChunkList(indices);
+}
+
+
+void DestructibleStructure::calculateExternalSupportChunks()
+{
+ const uint32_t chunkCount = chunks.size();
+ PX_UNUSED(chunkCount);
+ PX_PROFILE_ZONE("DestructibleStructureCalculateExternalSupportChunks", GetInternalApexSDK()->getContextId());
+
+ supportDepthChunks.resize(0);
+ supportDepthChunksNotExternallySupportedCount = 0;
+
+ // iterate all chunks, one destructible after the other
+ for (uint32_t i = 0; i < destructibles.size(); ++i)
+ {
+ DestructibleActorImpl* destructible = destructibles[i];
+ if (destructible == NULL)
+ continue;
+
+ DestructibleAssetImpl* destructibleAsset = destructible->getDestructibleAsset();
+ const float paddingFactor = destructibleAsset->mParams->neighborPadding;
+ const float padding = paddingFactor * (destructible->getOriginalBounds().maximum - destructible->getOriginalBounds().minimum).magnitude();
+ const DestructibleAssetParametersNS::Chunk_Type* destructibleAssetChunks = destructibleAsset->mParams->chunks.buf;
+
+ const uint32_t supportDepth = destructible->getSupportDepth();
+ const bool useAssetDefinedSupport = destructible->useAssetDefinedSupport();
+ const bool useWorldSupport = destructible->useWorldSupport();
+
+ const uint32_t firstChunkIndex = destructible->getFirstChunkIndex();
+ const uint32_t destructibleChunkCount = destructible->getChunkCount();
+ for (uint32_t chunkIndex = firstChunkIndex; chunkIndex < firstChunkIndex + destructibleChunkCount; ++chunkIndex)
+ {
+ Chunk& chunk = chunks[chunkIndex];
+ const DestructibleAssetParametersNS::Chunk_Type& source = destructibleAssetChunks[chunk.indexInAsset];
+
+ // init chunks flags (this is why all chunks need to be iterated)
+ chunk.flags &= ~((uint8_t)ChunkBelowSupportDepth | (uint8_t)ChunkExternallySupported | (uint8_t)ChunkWorldSupported);
+
+ if (source.depth > supportDepth)
+ {
+ chunk.flags |= ChunkBelowSupportDepth;
+ }
+ else if (source.depth == supportDepth)
+ {
+ // keep list of all support depth chunks
+ supportDepthChunks.pushBack(chunkIndex);
+
+ if (!destructible->isInitiallyDynamic())
+ {
+ // Only static destructibles can be externally supported
+ if (useAssetDefinedSupport)
+ {
+ if ((source.flags & DestructibleAssetImpl::SupportChunk) != 0)
+ {
+ chunk.flags |= ChunkExternallySupported;
+ }
+ }
+ if (useWorldSupport) // Test even if ChunkExternallySupported flag is already set, since we want to set the ChunkWorldSupported flag correctly
+ {
+ PhysX3DescTemplateImpl physX3Template;
+ destructible->getPhysX3Template(physX3Template);
+ for (uint32_t hullIndex = destructibleAsset->getChunkHullIndexStart(chunk.indexInAsset); hullIndex < destructibleAsset->getChunkHullIndexStop(chunk.indexInAsset); ++hullIndex)
+ {
+ PxTransform globalPose(destructible->getInitialGlobalPose());
+ globalPose.p = globalPose.p + globalPose.rotate(destructible->getScale().multiply(destructibleAsset->getChunkPositionOffset(chunk.indexInAsset)));
+ if (dscene->testWorldOverlap(destructibleAsset->chunkConvexHulls[hullIndex], globalPose, destructible->getScale(), padding, &physX3Template.queryFilterData))
+ {
+ chunk.flags |= ChunkExternallySupported | ChunkWorldSupported;
+ break;
+ }
+ }
+ }
+
+ if ( ((chunk.flags & ChunkExternallySupported) == 0) &&
+ supportDepthChunks.size() > supportDepthChunksNotExternallySupportedCount + 1)
+ {
+ // Will sort the supportDepthChunks so that externally supported ones are all at the end of the array
+ nvidia::swap(supportDepthChunks.back(), supportDepthChunks[supportDepthChunksNotExternallySupportedCount]);
+ ++supportDepthChunksNotExternallySupportedCount;
+ }
+ }
+ }
+ }
+ }
+}
+
+void DestructibleStructure::invalidateBounds(const PxBounds3* bounds, uint32_t boundsCount)
+{
+ for (uint32_t chunkNum = supportDepthChunks.size(); chunkNum-- > supportDepthChunksNotExternallySupportedCount;)
+ {
+ const uint32_t chunkIndex = supportDepthChunks[chunkNum];
+ Chunk& chunk = chunks[chunkIndex];
+ if (chunk.state & ChunkDynamic)
+ {
+ continue; // Only affect still-static chunks
+ }
+ DestructibleActorImpl* destructible = dscene->mDestructibles.direct(chunk.destructibleID);
+ const float paddingFactor = destructible->getDestructibleAsset()->mParams->neighborPadding;
+ const float padding = paddingFactor * (destructible->getOriginalBounds().maximum - destructible->getOriginalBounds().minimum).magnitude();
+ const DestructibleAssetParametersNS::Chunk_Type& source = destructible->getDestructibleAsset()->mParams->chunks.buf[chunk.indexInAsset];
+ if (destructible->useAssetDefinedSupport() && (source.flags & DestructibleAssetImpl::SupportChunk) != 0)
+ {
+ continue; // Asset-defined support. This is not affected by external actors.
+ }
+ PxBounds3 chunkBounds = destructible->getChunkBounds(chunk.indexInAsset);
+ chunkBounds.fattenFast(2.0f*padding);
+ bool affectedByInvalidBounds = false;
+ for (uint32_t boundsNum = 0; boundsNum < boundsCount; ++boundsNum)
+ {
+ if (bounds[boundsNum].intersects(chunkBounds))
+ {
+ affectedByInvalidBounds = true;
+ break;
+ }
+ }
+ if (!affectedByInvalidBounds)
+ {
+ continue;
+ }
+ bool isWorldSupported = false; // until proven otherwise
+ PhysX3DescTemplateImpl physX3Template;
+ destructible->getPhysX3Template(physX3Template);
+ for (uint32_t hullIndex = destructible->getDestructibleAsset()->getChunkHullIndexStart(chunk.indexInAsset); hullIndex < destructible->getDestructibleAsset()->getChunkHullIndexStop(chunk.indexInAsset); ++hullIndex)
+ {
+ PxTransform globalPose(destructible->getInitialGlobalPose());
+ globalPose.p = globalPose.p + globalPose.rotate(destructible->getScale().multiply(destructible->getDestructibleAsset()->getChunkPositionOffset(chunk.indexInAsset)));
+ if (dscene->testWorldOverlap(destructible->getDestructibleAsset()->chunkConvexHulls[hullIndex], globalPose, destructible->getScale(), padding, &physX3Template.queryFilterData))
+ {
+ isWorldSupported = true;
+ break;
+ }
+ }
+ if (!isWorldSupported)
+ {
+ chunk.flags &= ~(uint8_t)ChunkExternallySupported;
+ swap(supportDepthChunks[supportDepthChunksNotExternallySupportedCount++], supportDepthChunks[chunkNum++]);
+ supportInvalid = true;
+ }
+ }
+}
+
+void DestructibleStructure::buildSupportGraph()
+{
+ PX_PROFILE_ZONE("DestructibleStructureBuildSupportGraph", GetInternalApexSDK()->getContextId());
+
+ // First ensure external support data is up-to-date
+ calculateExternalSupportChunks();
+
+ // Now create graph
+
+ if (destructibles.size() > 1)
+ {
+ // First record cached overlaps within each destructible
+ physx::Array<IntPair> cachedOverlapPairs;
+ for (uint32_t i = 0; i < destructibles.size(); ++i)
+ {
+ DestructibleActorImpl* destructible = destructibles[i];
+ if (destructible != NULL)
+ {
+ DestructibleAssetImpl* asset = destructibles[i]->getDestructibleAsset();
+ PX_ASSERT(asset);
+ CachedOverlapsNS::IntPair_DynamicArray1D_Type* internalOverlaps = asset->getOverlapsAtDepth(destructible->getSupportDepth());
+ cachedOverlapPairs.reserve(cachedOverlapPairs.size() + internalOverlaps->arraySizes[0]);
+ for (int j = 0; j < internalOverlaps->arraySizes[0]; ++j)
+ {
+ CachedOverlapsNS::IntPair_Type& internalPair = internalOverlaps->buf[j];
+ IntPair& pair = cachedOverlapPairs.insert();
+ pair.i0 = internalPair.i0 + (int32_t)destructible->getFirstChunkIndex();
+ pair.i1 = internalPair.i1 + (int32_t)destructible->getFirstChunkIndex();
+ }
+ }
+ }
+
+ physx::Array<IntPair> overlapPairs;
+ uint32_t overlapCount = overlapPairs.size();
+
+ // Now find overlaps between destructibles. Start by creating a destructible overlap list
+ physx::Array<BoundsRep> destructibleBoundsReps;
+ destructibleBoundsReps.reserve(destructibles.size());
+ for (uint32_t i = 0; i < destructibles.size(); ++i)
+ {
+ DestructibleActorImpl* destructible = destructibles[i];
+ BoundsRep& boundsRep = destructibleBoundsReps.insert();
+ if (destructible != NULL)
+ {
+ boundsRep.aabb = destructible->getOriginalBounds();
+ }
+ else
+ {
+ boundsRep.type = 1; // Will not test 1-0 or 1-1 overlaps
+ }
+ const float paddingFactor = destructible->getDestructibleAsset()->mParams->neighborPadding;
+ const float padding = paddingFactor * (destructible->getOriginalBounds().maximum - destructible->getOriginalBounds().minimum).magnitude();
+ PX_ASSERT(!boundsRep.aabb.isEmpty());
+ boundsRep.aabb.fattenFast(padding);
+ }
+ physx::Array<IntPair> destructiblePairs;
+ {
+ PX_PROFILE_ZONE("DestructibleStructureBoundsCalculateOverlaps", GetInternalApexSDK()->getContextId());
+ BoundsInteractions interactions(false);
+ interactions.set(0, 0, true); // Only test 0-0 overlaps
+ PX_ASSERT(destructibleBoundsReps.size() > 1);
+ boundsCalculateOverlaps(destructiblePairs, Bounds3XYZ, &destructibleBoundsReps[0], destructibleBoundsReps.size(), sizeof(destructibleBoundsReps[0]), interactions);
+ }
+
+ // Test for chunk overlaps between overlapping destructibles
+ for (uint32_t overlapIndex = 0; overlapIndex < destructiblePairs.size(); ++overlapIndex)
+ {
+ const IntPair& destructibleOverlap = destructiblePairs[overlapIndex];
+
+ DestructibleActorImpl* destructible0 = destructibles[(uint32_t)destructibleOverlap.i0];
+ const uint32_t startChunkAssetIndex0 = destructible0->getDestructibleAsset()->mParams->firstChunkAtDepth.buf[destructible0->getSupportDepth()];
+ const uint32_t stopChunkAssetIndex0 = destructible0->getDestructibleAsset()->mParams->firstChunkAtDepth.buf[destructible0->getSupportDepth() + 1];
+ const float paddingFactor0 = destructible0->getDestructibleAsset()->mParams->neighborPadding;
+ const float padding0 = paddingFactor0 * (destructible0->getOriginalBounds().maximum - destructible0->getOriginalBounds().minimum).magnitude();
+
+ DestructibleActorImpl* destructible1 = destructibles[(uint32_t)destructibleOverlap.i1];
+ const uint32_t startChunkAssetIndex1 = destructible1->getDestructibleAsset()->mParams->firstChunkAtDepth.buf[destructible1->getSupportDepth()];
+ const uint32_t stopChunkAssetIndex1 = destructible1->getDestructibleAsset()->mParams->firstChunkAtDepth.buf[destructible1->getSupportDepth() + 1];
+ const float paddingFactor1 = destructible1->getDestructibleAsset()->mParams->neighborPadding;
+ const float padding1 = paddingFactor1 * (destructible1->getOriginalBounds().maximum - destructible1->getOriginalBounds().minimum).magnitude();
+
+ // Find AABB overlaps
+
+ // chunkBoundsReps contains bounds of all support chunks for both destructibles (first part destructible0, second part destructible1)
+ physx::Array<BoundsRep> chunkBoundsReps;
+ chunkBoundsReps.reserve((stopChunkAssetIndex0 - startChunkAssetIndex0) + (stopChunkAssetIndex1 - startChunkAssetIndex1));
+
+ // Destructible 0 bounds
+ for (uint32_t chunkAssetIndex = startChunkAssetIndex0; chunkAssetIndex < stopChunkAssetIndex0; ++chunkAssetIndex)
+ {
+ BoundsRep& bounds = chunkBoundsReps.insert();
+ PxMat44 scaledTM = destructible0->getInitialGlobalPose();
+ scaledTM.scale(PxVec4(destructible0->getScale(), 1.f));
+ bounds.aabb = destructible0->getDestructibleAsset()->getChunkActorLocalBounds(chunkAssetIndex);
+ PxBounds3Transform(bounds.aabb, scaledTM);
+ PX_ASSERT(!bounds.aabb.isEmpty());
+ bounds.aabb.fattenFast(padding0);
+ bounds.type = 0;
+ }
+
+ // Destructible 1 bounds
+ for (uint32_t chunkAssetIndex = startChunkAssetIndex1; chunkAssetIndex < stopChunkAssetIndex1; ++chunkAssetIndex)
+ {
+ BoundsRep& bounds = chunkBoundsReps.insert();
+ PxMat44 scaledTM = destructible1->getInitialGlobalPose();
+ scaledTM.scale(PxVec4(destructible1->getScale(), 1.f));
+ bounds.aabb = destructible1->getDestructibleAsset()->getChunkActorLocalBounds(chunkAssetIndex);
+ PxBounds3Transform(bounds.aabb, scaledTM);
+ PX_ASSERT(!bounds.aabb.isEmpty());
+ bounds.aabb.fattenFast(padding1);
+ bounds.type = 1;
+ }
+
+ {
+ PX_PROFILE_ZONE("DestructibleStructureBoundsCalculateOverlaps", GetInternalApexSDK()->getContextId());
+ BoundsInteractions interactions(false);
+ interactions.set(0, 1, true); // Only test 0-1 overlaps
+ if (chunkBoundsReps.size() > 0)
+ {
+ // this adds overlaps into overlapPairs with indices into the chunkBoundsReps array
+ boundsCalculateOverlaps(overlapPairs, Bounds3XYZ, &chunkBoundsReps[0], chunkBoundsReps.size(), sizeof(chunkBoundsReps[0]), interactions, true);
+ }
+ }
+
+ // Now do detailed overlap test
+ const bool performDetailedOverlapTest = destructible0->performDetailedOverlapTestForExtendedStructures() || destructible1->performDetailedOverlapTestForExtendedStructures();
+ {
+ const float padding = padding0 + padding1;
+
+ // upperOffset transforms from chunkBoundsReps indices to asset chunk indices of the second destructible
+ const int32_t upperOffset = int32_t(startChunkAssetIndex1 - (stopChunkAssetIndex0 - startChunkAssetIndex0));
+
+ PX_PROFILE_ZONE("DestructibleStructureDetailedOverlapTest", GetInternalApexSDK()->getContextId());
+ const uint32_t oldSize = overlapCount;
+ for (uint32_t overlapIndex = oldSize; overlapIndex < overlapPairs.size(); ++overlapIndex)
+ {
+ IntPair& AABBOverlap = overlapPairs[overlapIndex];
+ // The new overlaps will be indexed incorrectly. Fix the indexing.
+ if (AABBOverlap.i0 > AABBOverlap.i1)
+ {
+ // Ensures i0 corresponds to destructible0, i1 to destructible 1
+ // because lower chunkBoundsReps indices belong to destructible0
+ nvidia::swap(AABBOverlap.i0, AABBOverlap.i1);
+ }
+
+ // transform chunkBoundsReps indices to asset chunk indices
+ AABBOverlap.i0 += startChunkAssetIndex0;
+ AABBOverlap.i1 += upperOffset;
+ if (!performDetailedOverlapTest || DestructibleAssetImpl::chunksInProximity(*destructible0->getDestructibleAsset(), (uint16_t)AABBOverlap.i0,
+ PxTransform(destructible0->getInitialGlobalPose()), destructible0->getScale(), *destructible1->getDestructibleAsset(), (uint16_t)AABBOverlap.i1,
+ PxTransform(destructible1->getInitialGlobalPose()), destructible1->getScale(), padding))
+ {
+ // Record overlap with global indexing
+ IntPair& overlap = overlapPairs[overlapCount++];
+ overlap.i0 = AABBOverlap.i0 + (int32_t)destructible0->getFirstChunkIndex();
+ overlap.i1 = AABBOverlap.i1 + (int32_t)destructible1->getFirstChunkIndex();
+ }
+ }
+ }
+ overlapPairs.resize(overlapCount);
+ }
+
+ // We now have all chunk overlaps in the structure.
+ // Make 'overlapPairs' symmetric - i.e. if (A,B) is in the array, then so is (B,A).
+ overlapPairs.resize(2 * overlapCount);
+ for (uint32_t i = 0; i < overlapCount; ++i)
+ {
+ IntPair& overlap = overlapPairs[i];
+ IntPair& recipOverlap = overlapPairs[i + overlapCount];
+ recipOverlap.i0 = overlap.i1;
+ recipOverlap.i1 = overlap.i0;
+ }
+ overlapCount *= 2;
+
+ // Now sort overlapPairs by index0 and index1 (symmetric, so it doesn't matter which we sort by first)
+ qsort(overlapPairs.begin(), overlapCount, sizeof(IntPair), IntPair::compare);
+
+
+ // merge new overlaps with cached ones
+ physx::Array<IntPair> allOverlapPairs(overlapPairs.size() + cachedOverlapPairs.size());
+ ApexMerge(overlapPairs.begin(), overlapPairs.size(), cachedOverlapPairs.begin(), cachedOverlapPairs.size(), allOverlapPairs.begin(), allOverlapPairs.size(), IntPair::compare);
+ overlapCount = allOverlapPairs.size();
+
+ // Create the structure's chunk overlap list
+ createIndexStartLookup(firstOverlapIndices, 0, chunks.size(), &allOverlapPairs.begin()->i0, overlapCount, sizeof(IntPair));
+ overlaps.resize(overlapCount);
+ for (uint32_t overlapIndex = 0; overlapIndex < overlapCount; ++overlapIndex)
+ {
+ overlaps[overlapIndex] = (uint32_t)allOverlapPairs[overlapIndex].i1;
+ }
+ }
+ else if (destructibles.size() == 1 && destructibles[0] != NULL)
+ {
+ // fast path for case of structures with only 1 destructible
+
+ DestructibleAssetImpl* asset = destructibles[0]->getDestructibleAsset();
+ CachedOverlapsNS::IntPair_DynamicArray1D_Type* internalOverlaps = asset->getOverlapsAtDepth(destructibles[0]->getSupportDepth());
+
+ uint32_t numOverlaps = (uint32_t)internalOverlaps->arraySizes[0];
+
+ // Create the structure's chunk overlap list
+ createIndexStartLookup(firstOverlapIndices, 0, chunks.size(), &internalOverlaps->buf[0].i0, numOverlaps, sizeof(IntPair));
+
+ overlaps.resize(numOverlaps);
+ for (uint32_t overlapIndex = 0; overlapIndex < numOverlaps; ++overlapIndex)
+ {
+ overlaps[overlapIndex] = (uint32_t)internalOverlaps->buf[overlapIndex].i1;
+ }
+ }
+
+ // embed this call within DestructibleStructure, rather than exposing the StressSolver to other classes
+ postBuildSupportGraph();
+}
+
+void DestructibleStructure::postBuildSupportGraph()
+{
+ if(NULL != stressSolver)
+ {
+ PX_DELETE(stressSolver);
+ stressSolver = NULL;
+ dscene->setStressSolverTick(this, false);
+ }
+
+ {
+ // instantiate a stress solver if the user wants to use it
+ for(physx::Array<DestructibleActorImpl*>::ConstIterator kIter = destructibles.begin(); kIter != destructibles.end(); ++kIter)
+ {
+ const DestructibleActorImpl & currentDestructibleActor = *(*kIter);
+ PX_ASSERT(NULL != &currentDestructibleActor);
+ if(currentDestructibleActor.useStressSolver())
+ {
+ stressSolver = PX_NEW(DestructibleStructureStressSolver)(*this,currentDestructibleActor.getDestructibleParameters().supportStrength);
+ if (!stressSolver->isPhysxBasedSim || stressSolver->isCustomEnablesSimulating)
+ {
+ dscene->setStressSolverTick(this, true);
+ }
+ break;
+ }
+ }
+ }
+}
+
+void DestructibleStructure::evaluateForHitChunkList(const physx::Array<uint32_t> & chunkIndices) const
+{
+ PX_ASSERT(NULL != &chunkIndices);
+ FractureEvent fractureEvent;
+ ::memset(static_cast<void *>(&fractureEvent), 0x00, 1 * sizeof(FractureEvent));
+ for(physx::Array<uint32_t>::ConstIterator iter = chunkIndices.begin(); iter != chunkIndices.end(); ++iter)
+ {
+ DestructibleActorImpl & destructibleActor = *(dscene->mDestructibles.direct(chunks[*iter].destructibleID));
+ PX_ASSERT(NULL != &destructibleActor);
+ if(0 != destructibleActor.getSyncParams().getUserActorID())
+ {
+ fractureEvent.chunkIndexInAsset = *iter - destructibleActor.getFirstChunkIndex();
+ PX_ASSERT(fractureEvent.chunkIndexInAsset < destructibleActor.getChunkCount());
+ destructibleActor.evaluateForHitChunkList(fractureEvent);
+ }
+ }
+}
+
+void DestructibleStructure::addChunkImpluseForceAtPos(Chunk& chunk, const PxVec3& impulse, const PxVec3& position, bool wakeup)
+{
+ physx::Array<PxShape*>& shapes = getChunkShapes(chunk);
+ PX_ASSERT(!shapes.empty());
+ if (!shapes.empty())
+ {
+ // All shapes should have the same actor
+ PxRigidActor* actor = shapes[0]->getActor();
+ if (actor->getScene())
+ {
+ PxRigidDynamic* rigidDynamic = actor->is<physx::PxRigidDynamic>();
+ if (rigidDynamic && !(rigidDynamic->getRigidBodyFlags() & physx::PxRigidBodyFlag::eKINEMATIC))
+ {
+ PxRigidBodyExt::addForceAtPos(*actor->is<physx::PxRigidBody>(), impulse, position, PxForceMode::eIMPULSE, wakeup);
+ }
+ }
+ else // the actor hasn't been added to the scene yet, so store the forces to apply later.
+ {
+ ActorForceAtPosition forceToAdd(impulse, position, PxForceMode::eIMPULSE, wakeup, true);
+ dscene->addForceToAddActorsMap(actor, forceToAdd);
+ }
+ }
+}
+
+// http://en.wikipedia.org/wiki/Jenkins_hash_function
+// Fast and convenient hash for arbitrary multi-byte keys
+uint32_t jenkinsHash(const void *data, size_t sizeInBytes)
+{
+ uint32_t hash, i;
+ const char* key = reinterpret_cast<const char*>(data);
+ for (hash = i = 0; i < sizeInBytes; ++i)
+ {
+ hash += key[i];
+ hash += (hash << 10);
+ hash ^= (hash >> 6);
+ }
+ hash += (hash << 3);
+ hash ^= (hash >> 11);
+ hash += (hash << 15);
+
+ return hash;
+}
+
+uint32_t DestructibleStructure::newPxActorIslandReference(Chunk& chunk, PxRigidDynamic& nxActor)
+{
+ uint32_t islandID = jenkinsHash(&chunk, sizeof(Chunk));
+
+ // Prevent hash collisions
+ while (NULL != islandToActor.find(islandID))
+ {
+ ++islandID;
+ }
+
+ PX_ASSERT(islandID != InvalidID);
+
+ islandToActor[islandID] = &nxActor;
+ actorToIsland[&nxActor] = islandID;
+
+ dscene->setStructureUpdate(this, true); // islandToActor needs to be cleared in DestructibleStructure::tick
+
+ return islandID;
+}
+
+void DestructibleStructure::removePxActorIslandReferences(PxRigidDynamic& nxActor) const
+{
+ islandToActor.erase(actorToIsland[&nxActor]);
+ actorToIsland.erase(&nxActor);
+}
+
+PxRigidDynamic* DestructibleStructure::getChunkActor(Chunk& chunk)
+{
+ physx::Array<PxShape*>& shapes = getChunkShapes(chunk);
+ PxRigidDynamic* actor;
+ SCOPED_PHYSX_LOCK_READ(dscene->mApexScene);
+ actor = !shapes.empty() ? shapes[0]->getActor()->is<PxRigidDynamic>() : NULL;
+ return actor;
+}
+
+const PxRigidDynamic* DestructibleStructure::getChunkActor(const Chunk& chunk) const
+{
+ const physx::Array<PxShape*>& shapes = getChunkShapes(chunk);
+ PxRigidDynamic* actor;
+ SCOPED_PHYSX_LOCK_READ(dscene->mApexScene);
+ actor = !shapes.empty() ? shapes[0]->getActor()->is<PxRigidDynamic>() : NULL;
+ return actor;
+}
+
+PxTransform DestructibleStructure::getChunkLocalPose(const Chunk& chunk) const
+{
+ const physx::Array<PxShape*>* shapes;
+ PxVec3 offset;
+ if (chunk.visibleAncestorIndex == (int32_t)InvalidChunkIndex)
+ {
+ shapes = &chunk.shapes;
+ offset = PxVec3(0.0f);
+ }
+ else
+ {
+ shapes = &chunks[(uint32_t)chunk.visibleAncestorIndex].shapes;
+ offset = chunk.localOffset - chunks[(uint32_t)chunk.visibleAncestorIndex].localOffset;
+ }
+
+ PxTransform pose(PxIdentity);
+ if (!shapes->empty() && NULL != (*shapes)[0])
+ {
+ SCOPED_PHYSX_LOCK_READ(dscene->getModulePhysXScene());
+ // All shapes should have the same global pose
+ pose = (*shapes)[0]->getLocalPose();
+ }
+ pose.p += offset;
+ return pose;
+}
+
+PxTransform DestructibleStructure::getChunkActorPose(const Chunk& chunk) const
+{
+ const physx::Array<PxShape*>& shapes = getChunkShapes(chunk);
+ PxTransform pose(PxIdentity);
+ if (!shapes.empty() && NULL != shapes[0])
+ {
+ // All shapes should have the same actor
+ SCOPED_PHYSX_LOCK_READ(dscene->getModulePhysXScene());
+ pose = shapes[0]->getActor()->getGlobalPose();
+ }
+ return pose;
+}
+
+void DestructibleStructure::setSupportInvalid(bool supportIsInvalid)
+{
+ supportInvalid = supportIsInvalid;
+ dscene->setStructureUpdate(this, supportIsInvalid);
+}
+
+}
+} // end namespace nvidia
+