aboutsummaryrefslogtreecommitdiff
path: root/APEX_1.4/module/destructible/src/DestructibleScene.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/DestructibleScene.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/DestructibleScene.cpp')
-rw-r--r--APEX_1.4/module/destructible/src/DestructibleScene.cpp4078
1 files changed, 4078 insertions, 0 deletions
diff --git a/APEX_1.4/module/destructible/src/DestructibleScene.cpp b/APEX_1.4/module/destructible/src/DestructibleScene.cpp
new file mode 100644
index 00000000..4217e71f
--- /dev/null
+++ b/APEX_1.4/module/destructible/src/DestructibleScene.cpp
@@ -0,0 +1,4078 @@
+/*
+ * 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 "SceneIntl.h"
+#include "ModuleDestructibleImpl.h"
+#include "DestructibleScene.h"
+#include "DestructibleAssetImpl.h"
+#include "DestructibleActorImpl.h"
+#include "DestructibleActorProxy.h"
+#include "DestructibleActorJointProxy.h"
+#include "DestructibleStructureStressSolver.h"
+#if APEX_USE_PARTICLES
+#include "EmitterActor.h"
+#include "EmitterGeoms.h"
+#endif
+
+#include "PsArray.h"
+#include "PxScene.h"
+#include "PxConvexMeshDesc.h"
+#include "PxConvexMeshGeometry.h"
+#include "PxBoxGeometry.h"
+#include "PxSphereGeometry.h"
+
+#if APEX_RUNTIME_FRACTURE
+#include "SimScene.h"
+#include "Convex.h"
+#include "Compound.h"
+#include "Actor.h"
+#endif
+
+#include "ApexRand.h"
+#include "ModulePerfScope.h"
+#include "PsString.h"
+#include "PsTime.h"
+
+#include "RenderMeshAssetIntl.h"
+
+#include "Lock.h"
+#include "PxProfiler.h"
+
+#define USE_ACTIVE_TRANSFORMS_FOR_AWAKE_LIST 1
+
+namespace nvidia
+{
+namespace destructible
+{
+using namespace physx;
+
+namespace
+{
+ void unfortunateCompilerWorkaround(uint32_t)
+ {
+ }
+};
+
+PX_INLINE PxVec3 transformToScreenSpace(const PxVec3& pos, const float* viewProjectionMatrix)
+{
+ const float v[4] = { pos.x, pos.y, pos.z, 1.0f };
+
+ float u[4] = { 0, 0, 0, 0 };
+
+ const float* row = viewProjectionMatrix;
+ for (uint32_t i = 0; i < 4; ++i, ++row)
+ {
+ for (uint32_t j = 0; j < 4; ++j)
+ {
+ u[i] += row[j << 2] * v[j];
+ }
+ }
+
+ const float recipW = 1.0f / u[3];
+
+ return PxVec3(u[0] * recipW, u[1] * recipW, u[2] * recipW);
+}
+
+// Put an actor to sleep based upon input velocities.
+PX_INLINE void handleSleeping(PxRigidDynamic* actor, PxVec3 linearVelocity, PxVec3 angularVelocity)
+{
+ // In PhysX3, we only use an energy threshold.
+ if (actor == NULL)
+ {
+ return;
+ }
+ // Calculate kinetic energy
+ const float mass = actor->getMass();
+ const float linearKE = 0.5f*mass*linearVelocity.magnitudeSquared();
+ const PxTransform globalToMassT = actor->getCMassLocalPose().transform(actor->getGlobalPose());
+ const PxVec3 massSpaceAngularVelocity = globalToMassT.rotateInv(angularVelocity);
+ const float rotationalKE = 0.5f*massSpaceAngularVelocity.dot(actor->getMassSpaceInertiaTensor().multiply(massSpaceAngularVelocity));
+ const float totalKE = linearKE + rotationalKE;
+ // Put to sleep if below threshold
+ if (totalKE <= actor->getSleepThreshold()*mass)
+ {
+ actor->setLinearVelocity(PxVec3(0.0f));
+ actor->setAngularVelocity(PxVec3(0.0f));
+ actor->putToSleep();
+ }
+}
+
+/****************************
+* DestructibleUserNotify *
+*****************************/
+
+DestructibleUserNotify::DestructibleUserNotify(ModuleDestructibleImpl& module, DestructibleScene* destructibleScene) :
+mModule(module),
+mDestructibleScene(destructibleScene)
+{
+
+}
+void DestructibleUserNotify::onConstraintBreak(physx::PxConstraintInfo* constraints, uint32_t count)
+{
+ PX_UNUSED(constraints);
+ PX_UNUSED(count);
+}
+
+void DestructibleUserNotify::onWake(PxActor** actors, uint32_t count)
+{
+ if (mDestructibleScene->mUsingActiveTransforms) // The remaining code in this function only updates the destructible actor awake list when not using active transforms
+ {
+ return;
+ }
+
+ for (uint32_t i = 0; i < count; i++)
+ {
+ PxActor* actor = actors[i];
+ PhysXObjectDescIntl* desc = (PhysXObjectDescIntl*)(mModule.mSdk->getPhysXObjectInfo(actor));
+ if (desc != NULL)
+ {
+ if (desc->getUserDefinedFlag(PhysXActorFlags::CREATED_THIS_FRAME) || desc->getUserDefinedFlag(PhysXActorFlags::IS_SLEEPING))
+ {
+ // Only increase the counter in the first wake call, or
+ // when the state has changed since the last callback.
+ // IS_SLEEPING has to be checked, because with PhysX 3.3 we also
+ // receive a callback when the user calls putToSleep on an awake actor
+ // and the SDK wakes it up again.
+
+ // increase wake count on each referenced destructible
+ const uint32_t dActorCount = desc->mApexActors.size();
+ for (uint32_t i = 0; i < dActorCount; ++i)
+ {
+ const DestructibleActor* dActor = static_cast<const DestructibleActor*>(desc->mApexActors[i]);
+ if (dActor != NULL)
+ {
+ if (desc->mApexActors[i]->getOwner()->getObjTypeID() == DestructibleAssetImpl::getAssetTypeID())
+ {
+ DestructibleActorImpl& destructibleActor = const_cast<DestructibleActorImpl&>(static_cast<const DestructibleActorProxy*>(dActor)->impl);
+ destructibleActor.incrementWakeCount();
+ }
+ }
+ }
+
+ // update externally stored state of this physx actor
+ desc->setUserDefinedFlag(PhysXActorFlags::IS_SLEEPING, false);
+ }
+
+ desc->setUserDefinedFlag(PhysXActorFlags::CREATED_THIS_FRAME, false);
+ }
+ }
+}
+
+void DestructibleUserNotify::onSleep(PxActor** actors, uint32_t count)
+{
+ if (mDestructibleScene->mUsingActiveTransforms) // The remaining code in this function only updates the destructible actor awake list when not using active transforms
+ {
+ return;
+ }
+
+ for (uint32_t i = 0; i < count; i++)
+ {
+ PxActor* actor = actors[i];
+ PhysXObjectDescIntl* desc = (PhysXObjectDescIntl*)(mModule.mSdk->getPhysXObjectInfo(actor));
+ if (desc != NULL)
+ {
+ if (desc->getUserDefinedFlag(PhysXActorFlags::CREATED_THIS_FRAME))
+ {
+ // first sleep callback must be ignored, as it's not a state change, but initialization
+ desc->setUserDefinedFlag(PhysXActorFlags::CREATED_THIS_FRAME, false);
+ desc->setUserDefinedFlag(PhysXActorFlags::IS_SLEEPING, true);
+ continue;
+ }
+
+ if (!desc->getUserDefinedFlag(PhysXActorFlags::IS_SLEEPING))
+ {
+ // Only decrease the wake count, if the state has really changed to sleeping
+ // since the last callback
+ const uint32_t dActorCount = desc->mApexActors.size();
+ for (uint32_t i = 0; i < dActorCount; ++i)
+ {
+ const DestructibleActor* dActor = static_cast<const DestructibleActor*>(desc->mApexActors[i]);
+ if (dActor != NULL)
+ {
+ if (desc->mApexActors[i]->getOwner()->getObjTypeID() == DestructibleAssetImpl::getAssetTypeID())
+ {
+ DestructibleActorImpl& destructibleActor = const_cast<DestructibleActorImpl&>(static_cast<const DestructibleActorProxy*>(dActor)->impl);
+ destructibleActor.decrementWakeCount();
+ }
+ }
+ }
+
+ desc->setUserDefinedFlag(PhysXActorFlags::IS_SLEEPING, true);
+ }
+ }
+ }
+}
+
+
+void DestructibleUserNotify::onContact(const PxContactPairHeader& pairHeader, const PxContactPair* pairs, uint32_t nbPairs)
+{
+ if (pairHeader.flags & physx::PxContactPairHeaderFlag::eREMOVED_ACTOR_0 ||
+ pairHeader.flags & physx::PxContactPairHeaderFlag::eREMOVED_ACTOR_1 ||
+ pairHeader.actors[0] == NULL ||
+ pairHeader.actors[1] == NULL)
+ {
+ return;
+ }
+
+ ModuleDestructibleImpl* module = mDestructibleScene->mModule;
+ int moduleOwnsActor[2] =
+ {
+ (int)module->owns(pairHeader.actors[0]->is<physx::PxRigidActor>()),
+ (int)module->owns(pairHeader.actors[1]->is<physx::PxRigidActor>())
+ };
+
+ if (!(moduleOwnsActor[0] | moduleOwnsActor[1]))
+ {
+ return; // Neither is owned by the destruction module
+ }
+
+ for (uint32_t pairIdx = 0; pairIdx < nbPairs; pairIdx++)
+ {
+ const PxContactPair& currentPair = pairs[pairIdx];
+
+ if (currentPair.flags & physx::PxContactPairFlag::eREMOVED_SHAPE_0 ||
+ currentPair.flags & physx::PxContactPairFlag::eREMOVED_SHAPE_1 ||
+ currentPair.shapes[0] == NULL ||
+ currentPair.shapes[1] == NULL)
+ {
+ continue;
+ }
+
+ DestructibleActorImpl* destructibles[2] = {NULL, NULL};
+ DestructibleStructure::Chunk* chunks[2] = {NULL, NULL};
+ bool takesImpactDamage[2] = {false, false};
+ float minImpactVelocityThresholdsSq = PX_MAX_REAL;
+
+ for (int i = 0; i < 2; ++i)
+ {
+ PxShape* shape = currentPair.shapes[i];
+ if (moduleOwnsActor[i] && module->getDestructibleAndChunk(shape, NULL) == NULL)
+ {
+ chunks[i] = NULL;
+ }
+ else
+ {
+ chunks[i] = mDestructibleScene->getChunk(shape);
+ }
+ if (chunks[i] != NULL)
+ {
+ destructibles[i] = mDestructibleScene->mDestructibles.direct(chunks[i]->destructibleID);
+ PX_ASSERT(destructibles[i] != NULL);
+ if (destructibles[i] != NULL)
+ {
+ float ivts = destructibles[i]->getDestructibleParameters().impactVelocityThreshold;
+ ivts *= ivts;
+ if (ivts < minImpactVelocityThresholdsSq)
+ {
+ minImpactVelocityThresholdsSq = ivts;
+ }
+
+ int32_t depth = destructibles[i]->getDestructibleAsset()->getChunkDepth(chunks[i]->indexInAsset);
+ takesImpactDamage[i] = destructibles[i]->takesImpactDamageAtDepth((uint32_t)depth);
+ }
+ }
+ }
+ if (destructibles[0] == destructibles[1])
+ {
+ return; // No self-collision. To do: multiply by a self-collision factor instead?
+ }
+
+ if (!takesImpactDamage[0] && !takesImpactDamage[1])
+ {
+ return;
+ }
+
+ float masses[2];
+ {
+ for (int i = 0; i < 2; ++i)
+ {
+ masses[i] = 0;
+ PxRigidDynamic* rigidDynamic = pairHeader.actors[i]->is<physx::PxRigidDynamic>();
+ if (rigidDynamic)
+ {
+ SCOPED_PHYSX_LOCK_READ(rigidDynamic->getScene());
+ if (!(rigidDynamic->getRigidBodyFlags() & physx::PxRigidBodyFlag::eKINEMATIC))
+ {
+ masses[i] = rigidDynamic->getMass();
+ }
+ }
+ }
+ };
+
+ float reducedMass;
+ if (masses[0] == 0.0f)
+ {
+ reducedMass = masses[1];
+ }
+ else if (masses[1] == 0.0f)
+ {
+ reducedMass = masses[0];
+ }
+ else
+ {
+ reducedMass = masses[0] * masses[1] / (masses[0] + masses[1]);
+ }
+
+
+ PxVec3 destructibleForces[2] = {PxVec3(0.0f), PxVec3(0.0f)};
+ PxVec3 avgContactPosition = PxVec3(0.0f);
+ PxVec3 avgContactNormal = PxVec3(0.0f);
+ uint32_t numContacts = 0;
+
+#if USE_EXTRACT_CONTACTS
+#if PAIR_POINT_ALLOCS
+ PxContactPairPoint* pairPointBuffer = (PxContactPairPoint*)PX_ALLOC(currentPair.contactCount * sizeof(PxContactPairPoint), PX_DEBUG_EXP("PxContactPairPoints"));
+#else
+ mPairPointBuffer.reserve(currentPair.contactCount * sizeof(PxContactPairPoint));
+
+ // if this method isn't used, the operator[] method will fail because the actual size may be zero
+ mPairPointBuffer.forceSize_Unsafe(currentPair.contactCount * sizeof(PxContactPairPoint));
+ PxContactPairPoint* pairPointBuffer = currentPair.contactCount > 0 ? (PxContactPairPoint*)&(mPairPointBuffer[0]) : NULL;
+#endif
+ uint32_t numContactsInStream = pairPointBuffer != NULL ? currentPair.extractContacts(pairPointBuffer, currentPair.contactCount) : 0;
+#else
+ uint32_t numContactsInStream = currentPair.contactCount;
+ const PxContactPoint* contacts = reinterpret_cast<const PxContactPoint*>(currentPair.contactStream);
+#endif
+
+
+ for (uint32_t contactIdx = 0; contactIdx < numContactsInStream; contactIdx++)
+ {
+#if USE_EXTRACT_CONTACTS
+ PxContactPairPoint& currentPoint = pairPointBuffer[contactIdx];
+
+ const PxVec3& patchNormal = currentPoint.normal;
+ const PxVec3& position = currentPoint.position;
+#else
+ const PxContactPoint& cp = contacts[contactIdx];
+
+ const PxVec3& patchNormal = cp.normal;
+ const PxVec3& position = cp.point;
+#endif
+ PxVec3 velocities[2];
+ for (int i = 0; i < 2; ++i)
+ {
+ PxRigidBody* rigidBody = pairHeader.actors[i]->is<physx::PxRigidBody>();
+ if (rigidBody)
+ {
+ SCOPED_PHYSX_LOCK_READ(rigidBody->getScene());
+ velocities[i] = physx::PxRigidBodyExt::getVelocityAtPos(*rigidBody, position);
+ }
+ }
+
+ const PxVec3 velocityDelta = velocities[0] - velocities[1];
+ if (velocityDelta.magnitudeSquared() >= minImpactVelocityThresholdsSq || reducedMass == 0.0f) // If reduced mass == 0, this is kineamtic vs. kinematic. Generate damage.
+ {
+ for (int i = 0; i < 2; ++i)
+ {
+ DestructibleActorImpl* destructible = destructibles[i];
+ if (destructible)
+ {
+ // this is not really physically correct, but at least its deterministic...
+ destructibleForces[i] += (patchNormal * patchNormal.dot(velocityDelta)) * reducedMass * (i ? 1.0f : -1.0f);
+ }
+ }
+ avgContactPosition += position;
+ avgContactNormal += patchNormal;
+ numContacts++;
+ }
+ if (numContacts)
+ {
+ avgContactPosition /= (float)numContacts;
+ avgContactNormal.normalize();
+ for (uint32_t i = 0; i < 2; i++)
+ {
+ if (!takesImpactDamage[i])
+ continue;
+
+ const PxVec3 force = destructibleForces[i] / (float)numContacts;
+ DestructibleActorImpl* destructible = destructibles[i];
+ if (destructible != NULL)
+ {
+ if (!force.isZero())
+ {
+ destructible->takeImpact(force, avgContactPosition, chunks[i]->indexInAsset, pairHeader.actors[i ^ 1]);
+ }
+ else if (reducedMass == 0.0f) // Handle kineamtic vs. kinematic
+ {
+ const DestructibleActorParamNS::BehaviorGroup_Type& behaviorGroup = destructible->getBehaviorGroup(chunks[i]->indexInAsset);
+ destructible->applyDamage(2.0f*behaviorGroup.damageThreshold, 0.0f, avgContactPosition, (avgContactNormal * (i ? 1.0f : -1.0f)), chunks[i]->indexInAsset);
+ }
+ }
+ }
+ }
+ }
+
+#if PAIR_POINT_ALLOCS
+ if (pairPointBuffer != NULL)
+ {
+ PX_FREE(pairPointBuffer);
+ }
+#endif
+ }
+}
+
+
+void DestructibleUserNotify::onTrigger(PxTriggerPair* pairs, uint32_t count)
+{
+ PX_UNUSED(pairs);
+ PX_UNUSED(count);
+}
+
+
+void DestructibleUserNotify::onAdvance(const PxRigidBody*const* bodyBuffer, const PxTransform* poseBuffer, const PxU32 count)
+{
+ PX_UNUSED(bodyBuffer);
+ PX_UNUSED(poseBuffer);
+ PX_UNUSED(count);
+}
+
+
+/****************************
+* DestructibleContactModify *
+*****************************/
+
+void DestructibleContactModify::onContactModify(PxContactModifyPair* const pairs, uint32_t count)
+{
+ PX_PROFILE_ZONE("DestructibleOnContactConstraint", GetInternalApexSDK()->getContextId());
+
+ for (uint32_t iPair = 0; iPair < count; iPair++)
+ {
+ PxContactModifyPair& pair = pairs[iPair];
+
+ ModuleDestructibleImpl* module = destructibleScene->mModule;
+
+ int32_t chunkIndex0 = 0;
+ DestructibleActorProxy* proxy0 = static_cast<DestructibleActorProxy*>(module->getDestructibleAndChunk((PxShape*)pair.shape[0], &chunkIndex0));
+ int32_t chunkIndex1 = 0;
+ DestructibleActorProxy* proxy1 = static_cast<DestructibleActorProxy*>(module->getDestructibleAndChunk((PxShape*)pair.shape[1], &chunkIndex1));
+
+ const bool moduleOwnsActor[2] = {proxy0 != NULL, proxy1 != NULL};
+
+ if (moduleOwnsActor[0] == moduleOwnsActor[1])
+ {
+ continue; // Neither is owned by the destruction module, or both are
+ }
+
+ const int externalRBIndex = (int)(moduleOwnsActor[1] == 0);
+
+ destructibleScene->mApexScene->getPhysXScene()->lockRead();
+ const bool externalActorDynamic = pair.actor[externalRBIndex]->is<physx::PxRigidDynamic>() != NULL;
+ destructibleScene->mApexScene->getPhysXScene()->unlockRead();
+
+ if (!externalActorDynamic)
+ {
+ continue;
+ }
+
+ DestructibleActorProxy* proxy = externalRBIndex ? proxy0 : proxy1;
+ const float materialStrength = proxy->impl.getBehaviorGroup(uint32_t(externalRBIndex ? chunkIndex0 : chunkIndex1)).materialStrength;
+ if (materialStrength > 0.0f)
+ {
+ for (uint32_t contactIndex = 0; contactIndex < pair.contacts.size(); ++contactIndex)
+ {
+ pair.contacts.setMaxImpulse(contactIndex, materialStrength);
+ }
+ }
+ }
+}
+
+
+/****************************
+* ApexDamageEventReportDataImpl *
+*****************************/
+
+uint32_t
+ApexDamageEventReportDataImpl::addFractureEvent(const DestructibleStructure::Chunk& chunk, uint32_t flags)
+{
+ PX_ASSERT(m_destructible != NULL);
+ if (m_destructible == NULL)
+ {
+ return 0xFFFFFFFF;
+ }
+
+ // Find flags to see if we record this event
+ PX_ASSERT(!chunk.isDestroyed());
+ if (chunk.state & ChunkDynamic)
+ {
+ flags |= ApexChunkFlag::DYNAMIC;
+ }
+ if (chunk.flags & ChunkExternallySupported)
+ {
+ flags |= ApexChunkFlag::EXTERNALLY_SUPPORTED;
+ }
+ if (chunk.flags & ChunkWorldSupported)
+ {
+ flags |= ApexChunkFlag::WORLD_SUPPORTED;
+ }
+ if (chunk.flags & ChunkCrumbled)
+ {
+ flags |= ApexChunkFlag::DESTROYED_CRUMBLED;
+ }
+
+ // return invalid index if we don't record this event
+ if ((m_chunkReportBitMask & flags) == 0)
+ {
+ return 0xFFFFFFFF;
+ }
+
+ PX_ASSERT(m_destructible->getID() == chunk.destructibleID);
+ const DestructibleAssetParametersNS::Chunk_Type& source = m_destructible->getDestructibleAsset()->mParams->chunks.buf[chunk.indexInAsset];
+
+ PxBounds3 chunkWorldBoundsOnStack;
+ PxBounds3* chunkWorldBounds = &chunkWorldBoundsOnStack;
+
+ uint32_t fractureEventIndex = 0xFFFFFFFF;
+
+ if (source.depth <= m_chunkReportMaxFractureEventDepth)
+ {
+ fractureEventIndex = m_fractureEvents.size();
+ ChunkData& fractureEvent = m_fractureEvents.insert();
+ fractureEventList = &m_fractureEvents[0];
+ fractureEventListSize = m_fractureEvents.size();
+ fractureEvent.index = chunk.indexInAsset;
+ fractureEvent.depth = source.depth;
+ fractureEvent.damage = chunk.damage;
+ chunkWorldBounds = &fractureEvent.worldBounds; // Will be filled in below
+ fractureEvent.flags = flags;
+ }
+
+ // Adjust bounds to world coordinates
+ const PxVec3 scale = m_destructible->getScale();
+ const PxBounds3& bounds = m_destructible->getDestructibleAsset()->getChunkShapeLocalBounds(chunk.indexInAsset);
+ chunkWorldBounds->minimum = bounds.minimum.multiply(scale);
+ chunkWorldBounds->maximum = bounds.maximum.multiply(scale);
+ PxBounds3Transform(*chunkWorldBounds, PxMat44(m_destructible->getChunkPose(chunk.indexInAsset)));
+
+ worldBounds.include(*chunkWorldBounds);
+ minDepth = PxMin(minDepth, source.depth);
+ maxDepth = PxMax(maxDepth, source.depth);
+ ++totalNumberOfFractureEvents;
+
+ return fractureEventIndex;
+}
+
+void
+ApexDamageEventReportDataImpl::setDestructible(DestructibleActorImpl* inDestructible)
+{
+ m_destructible = inDestructible;
+ if (m_destructible != NULL)
+ {
+ m_chunkReportBitMask = m_destructible->getStructure()->dscene->mModule->m_chunkReportBitMask;
+ m_chunkReportMaxFractureEventDepth = m_destructible->getStructure()->dscene->mModule->m_chunkReportMaxFractureEventDepth;
+ destructible = m_destructible->getAPI();
+ minDepth = (uint16_t)(m_destructible->getDestructibleAsset()->mParams->depthCount > 0 ? m_destructible->getDestructibleAsset()->mParams->depthCount - 1 : 0);
+ maxDepth = 0;
+ }
+ else
+ {
+ clear();
+ }
+}
+
+void
+ApexDamageEventReportDataImpl::clearChunkReports()
+{
+ PX_ASSERT(m_destructible != NULL);
+ if (m_destructible == NULL)
+ {
+ return;
+ }
+
+ for (uint32_t i = 0; i < m_fractureEvents.size(); ++i)
+ {
+ ChunkData& fractureEvent = m_fractureEvents[i];
+ DestructibleStructure::Chunk& chunk = m_destructible->getStructure()->chunks[fractureEvent.index + m_destructible->getFirstChunkIndex()];
+ chunk.reportID = (uint32_t)DestructibleScene::InvalidReportID;
+ }
+
+ totalNumberOfFractureEvents = 0;
+}
+
+/********************
+* DestructibleScene *
+********************/
+
+class DestructibleBeforeTick : public PxTask, public UserAllocated
+{
+public:
+ DestructibleBeforeTick(DestructibleScene& scene) : mScene(&scene), mDeltaTime(0.0f) {}
+ const char* getName() const
+ {
+ return "DestructibleScene::BeforeTick";
+ }
+ void setDeltaTime(float deltaTime)
+ {
+ mDeltaTime = deltaTime;
+ }
+ void run()
+ {
+ mScene->tasked_beforeTick(mDeltaTime);
+ }
+ DestructibleScene* mScene;
+ float mDeltaTime;
+};
+
+static int comparePointers(const void* a, const void* b)
+{
+ return (uintptr_t)a == (uintptr_t)b ? 0 : ((uintptr_t)a < (uintptr_t)b ? -1 : 1);
+}
+
+static int compareOverlapHitShapePointers(const void* a, const void* b)
+{
+ const physx::PxOverlapHit* overlapA = (const physx::PxOverlapHit*)a;
+ const physx::PxOverlapHit* overlapB = (const physx::PxOverlapHit*)b;
+
+ return comparePointers(overlapA->shape, overlapB->shape);
+}
+
+DestructibleScene::DestructibleScene(ModuleDestructibleImpl& module, SceneIntl& scene, RenderDebugInterface* debugRender, ResourceList& list) :
+ mUserNotify(module, this),
+ mElapsedTime(0.0f),
+ mMassScale(1.0f),
+ mMassScaleInv(1.0f),
+ mScaledMassExponent(0.5f),
+ mScaledMassExponentInv(2.0f),
+ mPreviousVisibleDestructibleChunkCount(0),
+ mPreviousDynamicDestructibleChunkIslandCount(0),
+ mDynamicActorFIFONum(0),
+ mTotalChunkCount(0),
+ mFractureEventCount(0),
+ mDamageBufferWriteIndex(0),
+ mUsingActiveTransforms(false),
+ m_worldSupportPhysXScene(NULL),
+ m_damageApplicationRaycastFlags(nvidia::DestructibleActorRaycastFlags::StaticChunks),
+ mDebugRender(debugRender),
+ mRenderLockMode(RenderLockMode::PER_ACTOR_RENDER_LOCK),
+ mSyncParams(module.getSyncParams())
+{
+ list.add(*this);
+ mModule = &module;
+ mApexScene = &scene;
+ mPhysXScene = NULL;
+ mOverlapHits.resize(MAX_SHAPE_COUNT);
+ mApexScene->addModuleUserContactModify(mContactModify);
+ mContactModify.destructibleScene = this;
+ mNumFracturesProcessedThisFrame = 0;
+ mNumActorsCreatedThisFrame = 0;
+ mApexScene->addModuleUserNotifier(mUserNotify);
+
+#if APEX_RUNTIME_FRACTURE
+ mRTScene = NULL;
+#endif
+
+ mBeforeTickTask = PX_NEW(DestructibleBeforeTick)(*this);
+
+ /* Initialize reference to DestructibleDebugRenderParams */
+ {
+ READ_LOCK(*mApexScene);
+ mDebugRenderParams = DYNAMIC_CAST(DebugRenderParams*)(mApexScene->getDebugRenderParams());
+ }
+ PX_ASSERT(mDebugRenderParams);
+ NvParameterized::Handle handle(*mDebugRenderParams), memberHandle(*mDebugRenderParams);
+ int size;
+
+ if (mDebugRenderParams->getParameterHandle("moduleName", handle) == NvParameterized::ERROR_NONE)
+ {
+ handle.getArraySize(size, 0);
+ handle.resizeArray(size + 1);
+ if (handle.getChildHandle(size, memberHandle) == NvParameterized::ERROR_NONE)
+ {
+ memberHandle.initParamRef(DestructibleDebugRenderParams::staticClassName(), true);
+ }
+ }
+
+ /* Load reference to DestructibleDebugRenderParams */
+ NvParameterized::Interface* refPtr = NULL;
+ memberHandle.getParamRef(refPtr);
+ mDestructibleDebugRenderParams = DYNAMIC_CAST(DestructibleDebugRenderParams*)(refPtr);
+ PX_ASSERT(mDestructibleDebugRenderParams);
+
+ if (mModule->isInitialized())
+ {
+ // when scene is created after the module
+ initModuleSettings();
+ }
+ setMassScaling(mModule->m_massScale, mModule->m_scaledMassExponent);
+
+ createModuleStats();
+}
+
+void DestructibleScene::initModuleSettings()
+{
+ /* Initialize module defaults */
+ setMassScaling(mModule->m_massScale, mModule->m_scaledMassExponent);
+}
+
+DestructibleScene::~DestructibleScene()
+{
+#if APEX_RUNTIME_FRACTURE
+ PX_DELETE(mRTScene);
+#endif
+
+ delete(DestructibleBeforeTick*) mBeforeTickTask;
+
+ destroyModuleStats();
+}
+
+void DestructibleScene::destroy()
+{
+ removeAllActors();
+ reset();
+ PX_ASSERT(mAwakeActors.usedCount() == 0); // if there are actors left in here... thats very bad indeed.
+ mApexScene->removeModuleUserNotifier(mUserNotify);
+ mApexScene->removeModuleUserContactModify(mContactModify);
+ mApexScene->moduleReleased(*this);
+
+ // The order of user callbacks being modified needed to change due to a crash (DE9025)
+#if APEX_RUNTIME_FRACTURE
+ if(mRTScene)
+ {
+ mRTScene->restoreUserCallbacks();
+ }
+#endif
+
+ delete this;
+}
+
+void DestructibleScene::setModulePhysXScene(PxScene* pxScene)
+{
+ if (pxScene == mPhysXScene)
+ {
+ return;
+ }
+
+ mPhysXScene = pxScene;
+ mSceneClientIDs.reset();
+
+ if (pxScene)
+ {
+#if USE_ACTIVE_TRANSFORMS_FOR_AWAKE_LIST
+ SCOPED_PHYSX_LOCK_READ(mApexScene);
+ mUsingActiveTransforms = (mPhysXScene->getFlags() & PxSceneFlag::eENABLE_ACTIVETRANSFORMS);
+#endif
+ // Actors will make calls back to add themselves to structures
+ for (uint32_t i = 0 ; i < mActorArray.size() ; i++)
+ {
+ mActorArray[i]->setPhysXScene(pxScene);
+ }
+ }
+ else
+ {
+ mUsingActiveTransforms = false;
+
+ /* Release all destructible structures. TODO - this is not an optimal way to do this */
+ for (uint32_t i = 0 ; i < mActorArray.size() ; i++)
+ {
+ mActorArray[i]->setPhysXScene(0);
+ }
+
+ tasked_beforeTick(0.0f);
+ }
+}
+
+#if APEX_RUNTIME_FRACTURE
+::nvidia::fracture::SimScene* DestructibleScene::getDestructibleRTScene(bool create)
+{
+ if(mRTScene == NULL && create)
+ {
+ READ_LOCK(*mApexScene);
+ mRTScene = nvidia::fracture::SimScene::createSimScene(&mApexScene->getPhysXScene()->getPhysics(),GetApexSDK()->getCookingInterface(),mPhysXScene,0.02f,NULL,NULL);
+ mRTScene->clear();
+ }
+ return mRTScene;
+}
+#endif
+
+DestructibleActorJoint* DestructibleScene::createDestructibleActorJoint(const DestructibleActorJointDesc& destructibleActorJointDesc)
+{
+ return PX_NEW(DestructibleActorJointProxy)(destructibleActorJointDesc, *this, mDestructibleActorJointList);
+}
+
+PX_INLINE float square(float x)
+{
+ return x * x;
+}
+
+bool DestructibleScene::insertDestructibleActor(DestructibleActor* nxdestructible)
+{
+ DestructibleActorImpl* destructible = &((DestructibleActorProxy*)nxdestructible)->impl;
+ if (destructible->getStructure())
+ {
+ return false;
+ }
+
+ const DestructibleActorParam* p = destructible->getParams();
+ const physx::PxClientID clientID = p->p3ActorDescTemplate.ownerClient;
+ if (mSceneClientIDs.find(clientID) == mSceneClientIDs.end())
+ {
+ mSceneClientIDs.pushBack(clientID);
+ }
+
+ const float paddingFactor = destructible->getDestructibleAsset()->mParams->neighborPadding;
+
+ physx::Array<DestructibleStructure*> overlappedStructures;
+
+ const float padding = (destructible->getOriginalBounds().maximum - destructible->getOriginalBounds().minimum).magnitude() * paddingFactor;
+
+ const bool formsExtendedStructures = !destructible->isInitiallyDynamic() &&
+ destructible->formExtendedStructures();
+
+ uint32_t destructibleCount = 0;
+ DestructibleStructure* structureToUse = NULL;
+
+ if (formsExtendedStructures)
+ {
+ // Static actor
+ const PxBounds3& box = destructible->getOriginalBounds();
+
+ // Find structures that this actor touches
+ for (uint32_t structureNum = 0; structureNum < mStructures.usedCount(); ++structureNum)
+ {
+ DestructibleStructure* structure = mStructures.getUsed(structureNum);
+ if (structure->destructibles.size() == 0)
+ {
+ continue;
+ }
+ DestructibleActorImpl* firstDestructible = structure->destructibles[0];
+ if (firstDestructible->isInitiallyDynamic() || !firstDestructible->formExtendedStructures())
+ {
+ continue;
+ }
+ // TODO: Support structure selection by hashing structure when serializing actor
+ // We can hook up with this structure
+ for (uint32_t destructibleIndex = 0; destructibleIndex < structure->destructibles.size(); ++destructibleIndex)
+ {
+ DestructibleActorImpl* existingDestructible = structure->destructibles[destructibleIndex];
+ const PxBounds3& existingDestructibleBox = existingDestructible->getOriginalBounds();
+ if (box.intersects(existingDestructibleBox))
+ {
+ if (DestructibleAssetImpl::chunksInProximity(*destructible->getDestructibleAsset(), 0, PxTransform(destructible->getInitialGlobalPose()), destructible->getScale(),
+ *existingDestructible->getDestructibleAsset(), 0, PxTransform(existingDestructible->getInitialGlobalPose()), existingDestructible->getScale(), padding))
+ {
+ // Record individual actor touches for neighbor list
+ if (structureToUse == NULL || structureToUse->destructibles.size() < structure->destructibles.size())
+ {
+ structureToUse = structure;
+ }
+ overlappedStructures.pushBack(structure);
+ destructibleCount += structure->destructibles.size();
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ physx::Array<DestructibleActorImpl*> destructiblesToAdd;
+
+ if (structureToUse == NULL)
+ {
+ // Need to make a structure for this destructible
+ destructiblesToAdd.pushBack(destructible);
+ uint32_t structureID = UINT32_MAX;
+ if (!mStructures.useNextFree(structureID))
+ {
+ PX_ASSERT(!"Could not create a new structure ID.\n");
+ return false;
+ }
+ structureToUse = PX_NEW(DestructibleStructure)(this, structureID);
+ mStructures.direct(structureID) = structureToUse;
+ }
+ else
+ {
+ // We may re-use one of the overlappedStructures, and delete the rest
+ destructiblesToAdd.resize(destructibleCount - structureToUse->destructibles.size() + 1); // Exclude the ones in the structure already, include the one we're adding
+ uint32_t destructibleIndexOffset = 0;
+ for (uint32_t i = 0; i < overlappedStructures.size(); ++i)
+ {
+ DestructibleStructure* structure = overlappedStructures[i];
+ if (structure == structureToUse)
+ {
+ continue;
+ }
+ memcpy(&destructiblesToAdd[destructibleIndexOffset], structure->destructibles.begin(), structure->destructibles.size()*sizeof(DestructibleActorImpl*));
+ destructibleIndexOffset += structure->destructibles.size();
+ mStructures.free(structure->ID);
+ mStructures.direct(structure->ID) = NULL;
+ delete structure;
+ }
+ PX_ASSERT(destructibleIndexOffset == destructiblesToAdd.size() - 1);
+ // Finally, add our new destructible to the list
+ destructiblesToAdd[destructibleIndexOffset] = destructible;
+ }
+
+ return structureToUse->addActors(destructiblesToAdd);
+}
+
+void DestructibleScene::reset()
+{
+ //===SyncParams===
+ mDeprioritisedFractureBuffer.erase();
+
+ // Fracture buffer
+ mFractureBuffer.erase();
+
+ // Damage buffer
+ getDamageWriteBuffer().erase();
+ getDamageReadBuffer().erase();
+
+ // FIFO
+ mDynamicActorFIFONum = 0;
+ mTotalChunkCount = 0;
+ mActorFIFO.reset();
+
+ // Dormant list
+ mDormantActors.clear();
+
+ // Level-specific arrays
+ mStructureKillList.reset();
+ for (uint32_t actorKillIndex = 0; actorKillIndex < mActorKillList.size(); ++actorKillIndex)
+ {
+ PxRigidDynamic* actor = mActorKillList[actorKillIndex];
+ PX_ASSERT(actor);
+ if (actor)
+ {
+ releasePhysXActor(*actor);
+ }
+ }
+ mActorKillList.reset();
+ mDamageEventReportData.reset(); // DestructibleScene::reset should delete all DestructibleActors, too, so we shouldn't have bad DestructibleActor::mDamageEventReportIndex values floating around
+ mChunkReportHandles.reset();
+
+ // Structure container
+ for (uint32_t i = mStructures.usedCount(); i--;)
+ {
+ uint32_t index = mStructures.usedIndices()[i];
+ delete mStructures.direct(index);
+ mStructures.free(index);
+ }
+
+ mStructures.~Bank<DestructibleStructure*, uint32_t>();
+ PX_PLACEMENT_NEW(&mStructures, (Bank<DestructibleStructure*, uint32_t>))();
+
+ mStructureUpdateList.~Bank<DestructibleStructure*, uint32_t>();
+ PX_PLACEMENT_NEW(&mStructureUpdateList, (Bank<DestructibleStructure*, uint32_t>))();
+
+ mStructureSupportRebuildList.~Bank<DestructibleStructure*, uint32_t>();
+ PX_PLACEMENT_NEW(&mStructureSupportRebuildList, (Bank<DestructibleStructure*, uint32_t>))();
+
+ uint32_t apexActorKillIndex = mApexActorKillList.getSize();
+ while (apexActorKillIndex--)
+ {
+ DestructibleActorProxy* proxy = DYNAMIC_CAST(DestructibleActorProxy*)(mApexActorKillList.getResource(apexActorKillIndex));
+ PX_ASSERT(proxy);
+ delete proxy;
+ }
+
+ mChunkKillList.clear();
+
+ m_damageApplicationRaycastFlags = nvidia::DestructibleActorRaycastFlags::StaticChunks;
+}
+
+void DestructibleScene::resetEmitterActors()
+{
+# if APEX_USE_PARTICLES
+ for (uint32_t structureNum = 0; structureNum < mStructures.usedCount(); ++structureNum)
+ {
+ DestructibleStructure* structure = mStructures.getUsed(structureNum);
+ if (structure)
+ {
+ for (uint32_t destructibleIndex = 0; destructibleIndex < structure->destructibles.size(); ++destructibleIndex)
+ {
+ DestructibleActorImpl* destructible = structure->destructibles[destructibleIndex];
+ if (!destructible)
+ {
+ continue;
+ }
+
+ // stop all of the crumble and dust emitters in the destructible actors
+ if (destructible->getCrumbleEmitter() && destructible->getCrumbleEmitter()->isExplicitGeom())
+ {
+ destructible->getCrumbleEmitter()->isExplicitGeom()->resetParticleList();
+ }
+ if (destructible->getDustEmitter() && destructible->getDustEmitter()->isExplicitGeom())
+ {
+ destructible->getDustEmitter()->isExplicitGeom()->resetParticleList();
+ }
+ }
+ }
+ }
+#endif
+}
+
+void DestructibleScene::submitTasks(float elapsedTime, float /*substepSize*/, uint32_t /*numSubSteps*/)
+{
+ PX_PROFILE_ZONE("DestructibleScene/submitTasks", GetInternalApexSDK()->getContextId());
+ mFractureEventCount = 0;
+ PxTaskManager* tm;
+ {
+ tm = mApexScene->getTaskManager();
+ }
+ tm->submitNamedTask(mBeforeTickTask, mBeforeTickTask->getName());
+ mBeforeTickTask->setDeltaTime(elapsedTime);
+}
+
+void DestructibleScene::setTaskDependencies()
+{
+ PxTaskManager* tm;
+ {
+ tm = mApexScene->getTaskManager();
+ }
+ const PxTaskID physxTick = tm->getNamedTask(AST_PHYSX_SIMULATE);
+ mBeforeTickTask->finishBefore(physxTick);
+}
+
+class IRLess
+{
+public:
+ bool operator()(IndexedReal& ir1, IndexedReal& ir2) const
+ {
+ return ir1.value < ir2.value;
+ }
+};
+
+void DestructibleScene::tasked_beforeTick(float elapsedTime)
+{
+ SCOPED_PHYSX_LOCK_WRITE(mApexScene);
+
+ for (uint32_t i = 0; i < mStructureSupportRebuildList.usedCount(); ++i)
+ {
+ DestructibleStructure*& structure = mStructureSupportRebuildList.getUsed(i);
+ structure->buildSupportGraph();
+ structure = NULL; // This is only OK because we are calling clearFast after this. This allows setStructureSupportRebuild to operate without firing asserts.
+ }
+ mStructureSupportRebuildList.clearFast();
+
+ if (m_invalidBounds.size())
+ {
+ for (uint32_t i = 0; i < mStructures.usedCount(); ++i)
+ {
+ DestructibleStructure* structure = mStructures.getUsed(i);
+ if (structure != NULL)
+ {
+ structure->invalidateBounds(&m_invalidBounds[0], m_invalidBounds.size());
+ }
+ }
+ m_invalidBounds.clear();
+ }
+ processEventBuffers();
+
+
+ capDynamicActorCount();
+
+ for (uint32_t actorKillIndex = 0; actorKillIndex < mActorKillList.size(); ++actorKillIndex)
+ {
+ PxRigidDynamic* actor = mActorKillList[actorKillIndex];
+ PX_ASSERT(actor);
+ PhysXObjectDescIntl* actorObjDesc = mModule->mSdk->getGenericPhysXObjectInfo(actor);
+ if (actorObjDesc != NULL)
+ {
+ const uint32_t dActorCount = actorObjDesc->mApexActors.size();
+ for (uint32_t i = 0; i < dActorCount; ++i)
+ {
+ const DestructibleActor* dActor = static_cast<const DestructibleActor*>(actorObjDesc->mApexActors[i]);
+ if (dActor != NULL)
+ {
+ if (actorObjDesc->mApexActors[i]->getOwner()->getObjTypeID() == DestructibleAssetImpl::getAssetTypeID())
+ {
+ DestructibleActorImpl& destructibleActor = const_cast<DestructibleActorImpl&>(static_cast<const DestructibleActorProxy*>(dActor)->impl);
+ if (destructibleActor.getStructure() != NULL)
+ {
+ if (actor == destructibleActor.getStructure()->actorForStaticChunks)
+ {
+ destructibleActor.getStructure()->actorForStaticChunks = NULL;
+ }
+ }
+ }
+ }
+ }
+ actorObjDesc->mApexActors.clear();
+ releasePhysXActor(*actor);
+ }
+ }
+ mActorKillList.reset();
+
+ NvParameterized::Interface* iface;
+ {
+ iface = mApexScene->getDebugRenderParams();
+ }
+
+ mNumFracturesProcessedThisFrame = 0; //reset this counter
+ mNumActorsCreatedThisFrame = 0; //reset this counter
+
+ resetEmitterActors();
+
+ {
+ PX_PROFILE_ZONE("DestructibleRemoveChunksForBudget", GetInternalApexSDK()->getContextId());
+
+ // Remove chunks which need eliminating to keep budget
+ for (uint32_t i = 0; i < mChunkKillList.size(); ++i)
+ {
+ const IntPair& deadChunk = mChunkKillList[i];
+ DestructibleStructure* structure = mStructures.direct((uint32_t)deadChunk.i0);
+ if (structure != NULL)
+ {
+ DestructibleStructure::Chunk& chunk = structure->chunks[(uint32_t)deadChunk.i1];
+ if (!chunk.isDestroyed())
+ {
+ structure->removeChunk(chunk);
+ }
+ }
+ }
+ mChunkKillList.clear();
+ }
+
+ //===SyncParams=== prepare user's fracture event buffer, if available
+ UserFractureEventHandler * callback = NULL;
+ callback = mModule->getSyncParams().getUserFractureEventHandler();
+ const physx::Array<SyncParams::UserFractureEvent> * userSource = NULL;
+ if(NULL != callback)
+ {
+ mSyncParams.onPreProcessReadData(*callback, userSource);
+ }
+
+ //===SyncParams=== give the user the fracture event buffer. fracture event buffer must be fully populated and locked during this call
+ if(NULL != callback)
+ {
+ mSyncParams.onProcessWriteData(*callback, mFractureBuffer);
+ }
+
+ // Clear as much of the queue as we can
+ processFractureBuffer();
+ //===SyncParams=== process user's fracture events
+ processFractureBuffer(userSource);
+
+ // at this point all actors should have been created and added to the list for this tick
+ addActorsToScene();
+
+ //===SyncParams=== done with user's fracture event buffer, if available
+ if(NULL != callback)
+ {
+ mSyncParams.onPostProcessReadData(*callback);
+ }
+ callback = NULL;
+
+ // Process Damage coloring from forceDamageColoring()
+ processDamageColoringBuffer();
+
+ {
+ PX_PROFILE_ZONE("DestructibleKillStructures", GetInternalApexSDK()->getContextId());
+
+ for (uint32_t structureKillIndex = 0; structureKillIndex < mStructureKillList.size(); ++structureKillIndex)
+ {
+ DestructibleStructure* structure = mStructureKillList[structureKillIndex];
+ if (structure)
+ {
+ for (uint32_t destructibleIndex = structure->destructibles.size(); destructibleIndex--;)
+ {
+ DestructibleActorImpl*& destructible = structure->destructibles[destructibleIndex];
+ if (destructible)
+ {
+ destructible->setStructure(NULL);
+ mDestructibles.direct(destructible->getID()) = NULL;
+ mDestructibles.free(destructible->getID());
+ destructible = NULL;
+ }
+ }
+ }
+
+ setStructureUpdate(structure, false);
+
+ mStructures.free(structure->ID);
+ mStructures.direct(structure->ID) = NULL;
+ delete structure;
+ }
+ mStructureKillList.reset();
+ }
+
+ switch (getRenderLockMode())
+ {
+ case RenderLockMode::NO_RENDER_LOCK:
+ break;
+ case RenderLockMode::PER_MODULE_SCENE_RENDER_LOCK:
+ lockModuleSceneRenderLock();
+ break;
+ case RenderLockMode::PER_ACTOR_RENDER_LOCK:
+ default:
+ {
+ PX_PROFILE_ZONE("DestructibleBeforeTickLockRenderables", GetInternalApexSDK()->getContextId());
+ for (uint32_t i = 0; i < mActorArray.size(); ++i)
+ {
+ mActorArray[i]->renderDataLock();
+ }
+ }
+ break;
+ }
+
+ const uint32_t actorCount = mActorFIFO.size();
+
+ if (mDynamicActorFIFONum > 0 && (((mModule->m_dynamicActorFIFOMax > 0 && mDynamicActorFIFONum > mModule->m_dynamicActorFIFOMax) ||
+ (mModule->m_chunkFIFOMax > 0 && mTotalChunkCount > mModule->m_chunkFIFOMax)) && mModule->m_sortByBenefit))
+ {
+ if (mActorBenefitSortArray.size() < mActorFIFO.size())
+ {
+ mActorBenefitSortArray.resize(mActorFIFO.size());
+ }
+ for (uint32_t i = 0; i < mActorFIFO.size(); ++i)
+ {
+ IndexedReal& ir = mActorBenefitSortArray[i];
+ ActorFIFOEntry& entry = mActorFIFO[i];
+ ir.value = entry.benefitCache;
+ ir.index = i;
+ }
+ if (mActorFIFO.size() > 1)
+ {
+ shdfnd::sort(&mActorBenefitSortArray[0], mActorFIFO.size(), IRLess());
+ }
+ uint32_t sortIndex = 0;
+ if (mModule->m_dynamicActorFIFOMax > 0 && mDynamicActorFIFONum > mModule->m_dynamicActorFIFOMax)
+ {
+ while (sortIndex < mActorFIFO.size() && mDynamicActorFIFONum > mModule->m_dynamicActorFIFOMax)
+ {
+ IndexedReal& ir = mActorBenefitSortArray[sortIndex++];
+ ActorFIFOEntry& entry = mActorFIFO[ir.index];
+ if (entry.actor)
+ {
+ entry.flags |= ActorFIFOEntry::ForceLODRemove;
+ --mDynamicActorFIFONum;
+ }
+ }
+ }
+ uint32_t estTotalChunkCount = mTotalChunkCount; // This will get decremented again, in the FIFO loop below
+ if (mModule->m_chunkFIFOMax > 0 && estTotalChunkCount > mModule->m_chunkFIFOMax)
+ {
+ while (sortIndex < mActorFIFO.size() && estTotalChunkCount > mModule->m_chunkFIFOMax)
+ {
+ IndexedReal& ir = mActorBenefitSortArray[sortIndex++];
+ ActorFIFOEntry& entry = mActorFIFO[ir.index];
+ if (entry.actor)
+ {
+ entry.flags |= ActorFIFOEntry::ForceLODRemove;
+ const uint32_t chunkCount = entry.actor->getNbShapes();
+ estTotalChunkCount = estTotalChunkCount > chunkCount ? estTotalChunkCount - chunkCount : 0;
+ }
+ }
+ }
+ }
+
+ mDynamicActorFIFONum = 0;
+ for (uint32_t FIFOIndex = 0; FIFOIndex < actorCount; ++FIFOIndex)
+ {
+ ActorFIFOEntry& entry = mActorFIFO[FIFOIndex];
+ if (!entry.actor)
+ {
+ continue;
+ }
+ PhysXObjectDescIntl* actorObjDesc = mModule->mSdk->getGenericPhysXObjectInfo(entry.actor);
+ if (!actorObjDesc)
+ {
+ continue;
+ }
+
+ entry.benefitCache = 0.0f;
+ entry.age += elapsedTime;
+ uint32_t shapeCount;
+ if (actorObjDesc->userData != NULL && (shapeCount = entry.actor->getNbShapes()) != 0)
+ {
+ uint32_t reasonToDestroy = 0;
+ DestructibleActorImpl* destructible = NULL;
+ entry.maxSpeed = PX_MAX_F32;
+ float sleepVelocityFrameDecayConstant = 0.0f;
+ bool useHardSleeping = false;
+ for (uint32_t i = 0; i < actorObjDesc->mApexActors.size(); ++i)
+ {
+ DestructibleActorProxy* proxy = const_cast<DestructibleActorProxy*>(static_cast<const DestructibleActorProxy*>(actorObjDesc->mApexActors[i]));
+ if (proxy == NULL)
+ {
+ continue;
+ }
+ destructible = &proxy->impl;
+ const DestructibleParameters& parameters = destructible->getDestructibleParameters();
+ PxVec3 islandPos;
+ {
+ SCOPED_PHYSX_LOCK_READ(entry.actor->getScene());
+ islandPos = (entry.actor->getGlobalPose() * entry.actor->getCMassLocalPose()).p;
+ }
+ if (destructible->getParams()->deleteChunksLeavingUserDefinedBB)
+ {
+ const uint32_t bbc = mApexScene->getBoundingBoxCount();
+ for(uint32_t i = 0; i < bbc; ++i)
+ {
+ if(mApexScene->getBoundingBoxFlags(i) & UserBoundingBoxFlags::LEAVE)
+ {
+ if(!mApexScene->getBoundingBox(i).contains(islandPos))
+ {
+ reasonToDestroy = ApexChunkFlag::DESTROYED_LEFT_USER_BOUNDS;
+ break;
+ }
+ }
+ }
+ }
+ if (destructible->getParams()->deleteChunksEnteringUserDefinedBB)
+ {
+ const uint32_t bbc = mApexScene->getBoundingBoxCount();
+ for(uint32_t i = 0; i < bbc; ++i)
+ {
+ if(mApexScene->getBoundingBoxFlags(i) & UserBoundingBoxFlags::ENTER)
+ {
+ if(mApexScene->getBoundingBox(i).contains(islandPos))
+ {
+ reasonToDestroy = ApexChunkFlag::DESTROYED_ENTERED_USER_BOUNDS;
+ break;
+ }
+ }
+ }
+ }
+ if ((parameters.flags & DestructibleParametersFlag::USE_VALID_BOUNDS) != 0 &&
+ !parameters.validBounds.contains(islandPos - destructible->getInitialGlobalPose().getPosition()))
+ {
+ reasonToDestroy = ApexChunkFlag::DESTROYED_LEFT_VALID_BOUNDS;
+ }
+ else if ((entry.flags & ActorFIFOEntry::ForceLODRemove) != 0)
+ {
+ reasonToDestroy = ApexChunkFlag::DESTROYED_FIFO_FULL;
+ }
+ else if ((entry.flags & ActorFIFOEntry::IsDebris) != 0)
+ {
+ // Check if too old or too far
+ if ((parameters.flags & DestructibleParametersFlag::DEBRIS_TIMEOUT) != 0 &&
+ entry.age > (parameters.debrisLifetimeMax - parameters.debrisLifetimeMin)*mModule->m_maxChunkSeparationLOD + parameters.debrisLifetimeMin)
+ {
+ reasonToDestroy = ApexChunkFlag::DESTROYED_TIMED_OUT;
+ }
+ else if ((parameters.flags & DestructibleParametersFlag::DEBRIS_MAX_SEPARATION) != 0 &&
+ (entry.origin - islandPos).magnitudeSquared() >
+ square((parameters.debrisMaxSeparationMax - parameters.debrisMaxSeparationMin)*mModule->m_maxChunkSeparationLOD + parameters.debrisMaxSeparationMin))
+ {
+ reasonToDestroy = ApexChunkFlag::DESTROYED_EXCEEDED_MAX_DISTANCE;
+ }
+ }
+ if (reasonToDestroy)
+ {
+ destroyActorChunks(*entry.actor, reasonToDestroy); // places the actor on the kill list
+ entry.actor = NULL;
+ destructible->wakeForEvent();
+ break;
+ }
+ if (parameters.maxChunkSpeed > 0.0f)
+ {
+ entry.maxSpeed = PxMin(entry.maxSpeed, parameters.maxChunkSpeed);
+ }
+ sleepVelocityFrameDecayConstant = PxMax(sleepVelocityFrameDecayConstant, destructible->getSleepVelocityFrameDecayConstant());
+ useHardSleeping = useHardSleeping || destructible->useHardSleeping();
+ }
+ if (reasonToDestroy)
+ {
+ continue;
+ }
+ // Smooth velocities and see if the actor should be put to sleep
+ if (sleepVelocityFrameDecayConstant > 1.0f)
+ {
+ // Create smoothed velocities
+ const float sleepVelocitySmoothingFactor = 1.0f-1.0f/sleepVelocityFrameDecayConstant;
+ const float sleepVelocitySmoothingFactorComplement = 1.0f-sleepVelocitySmoothingFactor;
+ const PxVec3 currentLinearVelocity = entry.actor->getLinearVelocity();
+ const PxVec3 currentAngularVelocity = entry.actor->getAngularVelocity();
+ if (!entry.actor->isSleeping())
+ {
+ entry.averagedLinearVelocity = sleepVelocitySmoothingFactor*entry.averagedLinearVelocity + sleepVelocitySmoothingFactorComplement*currentLinearVelocity;
+ entry.averagedAngularVelocity = sleepVelocitySmoothingFactor*entry.averagedAngularVelocity + sleepVelocitySmoothingFactorComplement*currentAngularVelocity;
+ handleSleeping(entry.actor, entry.averagedLinearVelocity, entry.averagedAngularVelocity);
+ }
+ else
+ {
+ // Initialize smoothed velocity so that the actor may wake up again
+ entry.averagedLinearVelocity = mApexScene->getGravity();
+ entry.averagedAngularVelocity = PxVec3(0.0f, 0.0f, PxTwoPi);
+ }
+ }
+ // Cap the linear velocity here
+ if (entry.maxSpeed < PX_MAX_F32)
+ {
+ const PxVec3 chunkVel = entry.actor->getLinearVelocity();
+ const float chunkSpeed2 = chunkVel.magnitudeSquared();
+ if (chunkSpeed2 > entry.maxSpeed * entry.maxSpeed)
+ {
+ entry.actor->setLinearVelocity((entry.maxSpeed * RecipSqrt(chunkSpeed2))*chunkVel);
+ }
+ }
+ if (actorObjDesc->userData == NULL) // Signals that shapes have changed, need to recalculate the mass properties
+ {
+ PX_ASSERT(destructible != NULL);
+ }
+ if (destructible != NULL)
+ {
+ entry.benefitCache += destructible->getBenefit() * (float)shapeCount / (float)PxMax<uint32_t>(destructible->getVisibleDynamicChunkShapeCount(), 1);
+ }
+ if ((entry.flags & ActorFIFOEntry::MassUpdateNeeded) != 0)
+ {
+ PX_ASSERT(entry.unscaledMass > 0.0f);
+ if (entry.unscaledMass > 0.0f)
+ {
+ physx::PxRigidBodyExt::setMassAndUpdateInertia(*entry.actor, scaleMass(entry.unscaledMass), NULL, false);
+ }
+ entry.flags &= ~(uint32_t)ActorFIFOEntry::MassUpdateNeeded;
+ }
+ if (useHardSleeping && entry.actor->isSleeping())
+ {
+ bool isKinematic;
+ {
+ SCOPED_PHYSX_LOCK_READ(entry.actor->getScene());
+ isKinematic = (entry.actor->getRigidBodyFlags() & physx::PxRigidBodyFlag::eKINEMATIC);
+ }
+ if (!isKinematic)
+ {
+ {
+ entry.actor->setRigidBodyFlag(physx::PxRigidBodyFlag::eKINEMATIC, true);
+ }
+ uint32_t dormantID = 0;
+ if (mDormantActors.useNextFree(dormantID))
+ {
+ actorObjDesc->userData = (void*)~(uintptr_t)dormantID;
+ mDormantActors.direct(dormantID) = DormantActorEntry(entry.actor, entry.unscaledMass, entry.flags);
+ }
+ else
+ {
+ actorObjDesc->userData = NULL;
+ }
+ }
+ entry.actor = NULL;
+ continue;
+ }
+ if (mDynamicActorFIFONum != FIFOIndex)
+ {
+ actorObjDesc->userData = (void*)~(uintptr_t)mDynamicActorFIFONum;
+ mActorFIFO[mDynamicActorFIFONum] = entry;
+ }
+ ++mDynamicActorFIFONum;
+ }
+ else
+ {
+ schedulePxActorForDelete(*actorObjDesc);
+ entry.actor = NULL;
+ }
+ }
+ mActorFIFO.resize(mDynamicActorFIFONum);
+
+ mElapsedTime += elapsedTime;
+
+
+ for (uint32_t structureNum = mStructureUpdateList.usedCount(); structureNum--;)
+ {
+ DestructibleStructure* structure = mStructureUpdateList.getUsed(structureNum);
+ structure->updateIslands();
+ setStructureUpdate(structure, false);
+ }
+
+
+ for (uint32_t structureNum = mStressSolverTickList.usedCount(); structureNum--;)
+ {
+ DestructibleStructure* structure = mStressSolverTickList.getUsed(structureNum);
+ structure->tickStressSolver(elapsedTime);
+ }
+
+
+ switch (getRenderLockMode())
+ {
+ case RenderLockMode::NO_RENDER_LOCK:
+ break;
+ case RenderLockMode::PER_MODULE_SCENE_RENDER_LOCK:
+ unlockModuleSceneRenderLock();
+ break;
+ case RenderLockMode::PER_ACTOR_RENDER_LOCK:
+ default:
+ {
+ PX_PROFILE_ZONE("DestructibleBeforeTickLockRenderables", GetInternalApexSDK()->getContextId());
+ for (uint32_t i = 0; i < mActorArray.size(); ++i)
+ {
+ mActorArray[i]->renderDataUnLock();
+ }
+ }
+ break;
+ }
+
+ for (uint32_t jointIndex = mDestructibleActorJointList.getSize(); jointIndex--;)
+ {
+ DestructibleActorJointProxy* jointProxy =
+ DYNAMIC_CAST(DestructibleActorJointProxy*)(mDestructibleActorJointList.getResource(jointIndex));
+ PX_ASSERT(jointProxy != NULL);
+ if (jointProxy != NULL)
+ {
+ bool result = jointProxy->impl.updateJoint();
+ if (!result)
+ {
+ jointProxy->release();
+ }
+ }
+ }
+#if APEX_RUNTIME_FRACTURE
+ fracture::SimScene* simScene = getDestructibleRTScene(false);
+ if (simScene != NULL)
+ {
+ simScene->preSim(elapsedTime);
+ }
+#endif
+
+ if (mApexScene->isFinalStep())
+ {
+ if (mModule->m_chunkReport)
+ {
+ PX_PROFILE_ZONE("DestructibleChunkReport", GetInternalApexSDK()->getContextId());
+ if (mModule->m_chunkStateEventCallbackSchedule == DestructibleCallbackSchedule::BeforeTick)
+ {
+ for (uint32_t actorWithChunkStateEventIndex = mActorsWithChunkStateEvents.size(); actorWithChunkStateEventIndex--;)
+ {
+ ChunkStateEventData data;
+ DestructibleActorImpl* dactor = mActorsWithChunkStateEvents[actorWithChunkStateEventIndex];
+ data.destructible = dactor->getAPI();
+ if (dactor->acquireChunkEventBuffer(data.stateEventList, data.stateEventListSize))
+ {
+ mModule->m_chunkReport->onStateChangeNotify(data);
+ dactor->releaseChunkEventBuffer();
+ }
+ }
+ //mActorsWithChunkStateEvents.clear();
+ }
+ }
+ }
+
+ // Update mUsingActiveTransforms, in case the user has changed it (PhysX3 only)
+#if USE_ACTIVE_TRANSFORMS_FOR_AWAKE_LIST
+ SCOPED_PHYSX_LOCK_READ(mApexScene);
+ mUsingActiveTransforms = mPhysXScene != NULL && (mPhysXScene->getFlags() & PxSceneFlag::eENABLE_ACTIVETRANSFORMS);
+#endif
+}
+
+void DestructibleScene::setStructureSupportRebuild(DestructibleStructure* structure, bool rebuild)
+{
+ if (rebuild)
+ {
+ if (mStructureSupportRebuildList.use(structure->ID))
+ {
+ mStructureSupportRebuildList.direct(structure->ID) = structure;
+ }
+ else
+ {
+ PX_ASSERT(mStructureSupportRebuildList.direct(structure->ID) == structure);
+ }
+ }
+ else
+ {
+ if (mStructureSupportRebuildList.free(structure->ID))
+ {
+ mStructureSupportRebuildList.direct(structure->ID) = NULL;
+ }
+ else
+ {
+ PX_ASSERT(mStructureSupportRebuildList.direct(structure->ID) == NULL);
+ }
+ }
+}
+
+void DestructibleScene::setStructureUpdate(DestructibleStructure* structure, bool update)
+{
+ if (update)
+ {
+ if (mStructureUpdateList.use(structure->ID))
+ {
+ mStructureUpdateList.direct(structure->ID) = structure;
+ }
+ else
+ {
+ PX_ASSERT(mStructureUpdateList.direct(structure->ID) == structure);
+ }
+ }
+ else
+ {
+ if (mStructureUpdateList.free(structure->ID))
+ {
+ mStructureUpdateList.direct(structure->ID) = NULL;
+ }
+ else
+ {
+ PX_ASSERT(mStructureUpdateList.direct(structure->ID) == NULL);
+ }
+ }
+}
+
+
+void DestructibleScene::setStressSolverTick(DestructibleStructure* structure, bool update)
+{
+ if (update)
+ {
+ if (mStressSolverTickList.use(structure->ID))
+ {
+ mStressSolverTickList.direct(structure->ID) = structure;
+ }
+ else
+ {
+ PX_ASSERT(mStressSolverTickList.direct(structure->ID) == structure);
+ }
+ }
+ else
+ {
+ if (mStressSolverTickList.free(structure->ID))
+ {
+ mStressSolverTickList.direct(structure->ID) = NULL;
+ }
+ else
+ {
+ PX_ASSERT(mStressSolverTickList.direct(structure->ID) == NULL);
+ }
+ }
+}
+
+
+void DestructibleScene::fetchResults()
+{
+ PX_PROFILE_ZONE("DestructibleFetchResults", GetInternalApexSDK()->getContextId());
+
+ // beforeTick() should have deleted all of the PxActors. It should be safe now to delete the Apex actors
+ // since there should be no more PhysXObjectDesc which reference this actor.
+ uint32_t apexActorKillIndex = mApexActorKillList.getSize();
+ while (apexActorKillIndex--)
+ {
+ DestructibleActorProxy* proxy = DYNAMIC_CAST(DestructibleActorProxy*)(mApexActorKillList.getResource(apexActorKillIndex));
+ PX_ASSERT(proxy);
+ delete proxy;
+ }
+
+ {
+ PX_PROFILE_ZONE("DestructibleUpdateRenderMeshBonePoses", GetInternalApexSDK()->getContextId());
+
+ // Reset instanced mesh buffers
+ for (uint32_t i = 0; i < mInstancedActors.size(); ++i)
+ {
+ DestructibleActorImpl* actor = mInstancedActors[i];
+ if (actor->m_listIndex == 0)
+ {
+ actor->getDestructibleAsset()->resetInstanceData();
+ }
+ }
+
+ //===SyncParams=== prepare user's chunk motion buffer, if available
+ UserChunkMotionHandler * callback = NULL;
+ callback = mModule->getSyncParams().getUserChunkMotionHandler();
+ if(NULL != callback)
+ {
+ mSyncParams.onPreProcessReadData(*callback);
+ }
+
+ for (uint32_t i = 0; i < mInstancedActors.size(); ++i)
+ {
+ DestructibleActorImpl* actor = mInstancedActors[i];
+ actor->fillInstanceBuffers();
+ }
+
+ if (mUsingActiveTransforms)
+ {
+ // in AT mode, mAwakeActors is only used for temporarily storing actors that need update
+ // a frame history is kept to prevent wake/sleep event chatter
+ // TODO: update only the actually moving chunks. [APEX-670]
+ // with the current mechanism, all actor's chunks are updated regardless of active transforms
+
+ SCOPED_PHYSX_LOCK_READ(mPhysXScene);
+ for (uint32_t clientNum = 0; clientNum < mSceneClientIDs.size(); ++clientNum)
+ {
+ uint32_t transformCount = 0;
+ const physx::PxActiveTransform* activeTransforms = mPhysXScene->getActiveTransforms(transformCount, mSceneClientIDs[clientNum]);
+ for (uint32_t i = 0; i < transformCount; ++i)
+ {
+ PhysXObjectDescIntl* actorObjDesc = (PhysXObjectDescIntl*)mModule->mSdk->getPhysXObjectInfo(activeTransforms[i].actor);
+ if (actorObjDesc != NULL)
+ {
+ for (uint32_t j = 0; j < actorObjDesc->mApexActors.size(); ++j)
+ {
+ DestructibleActorProxy* dActor = (DestructibleActorProxy*)actorObjDesc->mApexActors[j];
+ if (dActor != NULL)
+ {
+ // ignore duplicate entries on purpose
+ if (dActor->impl.mActiveFrames == 0) // Hasn't been recorded as active yet
+ {
+ dActor->impl.incrementWakeCount();
+ }
+ dActor->impl.mActiveFrames |= 1; // Record as active this frame
+ }
+ }
+ }
+#if APEX_RUNTIME_FRACTURE
+ else
+ {
+ nvidia::fracture::SimScene* simScene = getDestructibleRTScene(false);
+ if (simScene != NULL)
+ {
+ physx::PxActor* actor = activeTransforms[i].actor;
+ if (actor != NULL)
+ {
+ physx::PxRigidDynamic* rigidDynamic = actor->is<physx::PxRigidDynamic>();
+ if (rigidDynamic != NULL)
+ {
+ if (rigidDynamic->getNbShapes() > 0)
+ {
+ physx::PxShape* firstShape;
+ if (rigidDynamic->getShapes(&firstShape, 1, 0) && firstShape != NULL)
+ {
+ nvidia::fracture::Convex* convex = (nvidia::fracture::Convex*)simScene->findConvexForShape(*firstShape);
+ if (simScene->owns(*firstShape))
+ {
+ nvidia::fracture::Compound* compound = (nvidia::fracture::Compound*)convex->getParent();
+ // ignore duplicate entries on purpose
+ if (compound->getDestructibleActor()->mActiveFrames == 0) // Hasn't been recorded as active yet
+ {
+ compound->getDestructibleActor()->incrementWakeCount();
+ }
+ compound->getDestructibleActor()->mActiveFrames |= 1; // Record as active this frame
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+#endif
+ }
+ }
+ }
+
+ // Iterate backwards through mAwakeActors, since an IndexBank used-list may have entries freed during iteration (as a result of actor->decrementWakeCount() or actor->resetWakeForEvent())
+ for (uint32_t awakeActorNum = mAwakeActors.usedCount(); awakeActorNum--;)
+ {
+ DestructibleActorImpl* actor = mDestructibles.direct(mAwakeActors.usedIndices()[awakeActorNum]);
+ if (mUsingActiveTransforms)
+ {
+ if (actor->mActiveFrames == 2) // Active last frame, not active this frame
+ {
+ actor->decrementWakeCount();
+ if (actor->mAwakeActorCount == 0)
+ {
+ actor->mActiveFrames = 0; // The result of the skipped shift & mask, below
+ continue;
+ }
+ }
+ actor->mActiveFrames = (actor->mActiveFrames << 1) & 3; // Shift up and erase history past last frame
+ }
+
+ updateActorPose(actor, callback);
+ actor->resetWakeForEvent();
+
+ if (actor->getNumVisibleChunks() == 0)
+ {
+ if (getModuleDestructible()->m_chunkReport != NULL)
+ {
+ if (getModuleDestructible()->m_chunkReport->releaseOnNoChunksVisible(actor->getAPI()))
+ {
+ destructibleActorKillList.pushBack(actor->getAPI());
+ }
+ }
+ }
+ }
+
+ //===SyncParams=== give the user the chunk motion buffer. chunk motion buffer must be fully populated and locked during this call
+ if(NULL != callback)
+ {
+ mSyncParams.onProcessWriteData(*callback);
+ }
+
+ //===SyncParams=== done with user's chunk event motion, if available
+ if(NULL != callback)
+ {
+ mSyncParams.onPostProcessReadData(*callback);
+ }
+ callback = NULL;
+
+ //===SyncParams=== allow user from changing sync params again. do this just after the last encounter of working with sync params in the program
+ PX_ASSERT(mSyncParams.lockSyncParams);
+ mSyncParams.lockSyncParams = false;
+
+#if 0
+ /* Update bone boses in the render mesh, update world bounds */
+ for (uint32_t i = 0 ; i < mAwakeActors.size() ; i++)
+ {
+ DestructibleActor* actor = DYNAMIC_CAST(DestructibleActor*)(mAwakeActors[ i ]);
+ if (actor->getStructure())
+ {
+ actor->setRenderTMs();
+ }
+ }
+#endif
+ }
+
+ if (mApexScene->isFinalStep())
+ {
+ if (mModule->m_chunkReport)
+ {
+ PX_PROFILE_ZONE("DestructibleChunkReport", GetInternalApexSDK()->getContextId());
+
+ // Chunk damage reports
+ for (uint32_t reportNum = 0; reportNum < mDamageEventReportData.size(); ++reportNum)
+ {
+ ApexDamageEventReportDataImpl& data = mDamageEventReportData[reportNum];
+ if (data.totalNumberOfFractureEvents > 0)
+ {
+ mModule->m_chunkReport->onDamageNotify(data);
+ }
+ data.clearChunkReports();
+ ((DestructibleActorProxy*)data.destructible)->impl.mDamageEventReportIndex = 0xFFFFFFFF;
+ }
+
+ // Chunk state notifies
+ if (mModule->m_chunkStateEventCallbackSchedule == DestructibleCallbackSchedule::FetchResults)
+ {
+ for (uint32_t actorWithChunkStateEventIndex = mActorsWithChunkStateEvents.size(); actorWithChunkStateEventIndex--;)
+ {
+ ChunkStateEventData data;
+ DestructibleActorImpl* dactor = mActorsWithChunkStateEvents[actorWithChunkStateEventIndex];
+ data.destructible = dactor->getAPI();
+ if (dactor->acquireChunkEventBuffer(data.stateEventList, data.stateEventListSize))
+ {
+ mModule->m_chunkReport->onStateChangeNotify(data);
+ dactor->releaseChunkEventBuffer();
+ }
+ }
+ //mActorsWithChunkStateEvents.clear();
+ }
+
+ // Destructible actor wake/sleep reports
+ if (mOnWakeActors.size())
+ {
+ mModule->m_chunkReport->onDestructibleWake(&mOnWakeActors[0], mOnWakeActors.size());
+ }
+ if (mOnSleepActors.size())
+ {
+ mModule->m_chunkReport->onDestructibleSleep(&mOnSleepActors[0], mOnSleepActors.size());
+ }
+ }
+ mDamageEventReportData.clear();
+ mChunkReportHandles.clear();
+ mOnWakeActors.clear();
+ mOnSleepActors.clear();
+
+ // Kill destructibles on death row
+ for (uint32_t i = 0; i < destructibleActorKillList.size(); ++i)
+ {
+ if (destructibleActorKillList[i] != NULL)
+ {
+ destructibleActorKillList[i]->release();
+ }
+ }
+ destructibleActorKillList.resize(0);
+ }
+
+ if (mModule->m_impactDamageReport)
+ {
+ if (mImpactDamageEventData.size() != 0)
+ {
+ mModule->m_impactDamageReport->onImpactDamageNotify(&mImpactDamageEventData[0], mImpactDamageEventData.size());
+ }
+ }
+ mImpactDamageEventData.clear();
+
+#if 0 //dead code
+ // here all scenes have finished beforeTick for sure, so we know all the PxActors have been deleted
+ // TODO: can that happen: beforeTick, releaseActor, releaseAsset, fetchResults ? that would still cause a problem.
+ mModule->releaseBufferedConvexMeshes();
+#endif
+
+#if APEX_RUNTIME_FRACTURE
+ fracture::SimScene* simScene = getDestructibleRTScene(false);
+ if (simScene != NULL)
+ {
+ simScene->postSim(PX_MAX_F32);
+ }
+#endif
+
+ swapDamageBuffers();
+}
+
+
+void DestructibleScene::visualize()
+{
+#ifdef WITHOUT_DEBUG_VISUALIZE
+#else
+ if (!mDestructibleDebugRenderParams->VISUALIZE_DESTRUCTIBLE_ACTOR)
+ {
+ return;
+ }
+
+ using RENDER_DEBUG::DebugColors;
+ using RENDER_DEBUG::DebugRenderState;
+ // save the rendering state
+ const physx::PxMat44& savedPose = *RENDER_DEBUG_IFACE(mDebugRender)->getPoseTyped();
+ RENDER_DEBUG_IFACE(mDebugRender)->setIdentityPose();
+ RENDER_DEBUG_IFACE(mDebugRender)->pushRenderState();
+ if (mDestructibleDebugRenderParams->VISUALIZE_DESTRUCTIBLE_BOUNDS > 0)
+ {
+ RENDER_DEBUG_IFACE(mDebugRender)->setCurrentColor(RENDER_DEBUG_IFACE(mDebugRender)->getDebugColor(DebugColors::Red));
+ for (uint32_t i = 0 ; i < mActorArray.size() ; i++)
+ {
+ DestructibleActorImpl* actor = DYNAMIC_CAST(DestructibleActorImpl*)(mActorArray[ i ]);
+ PxBounds3 bounds = actor->getBounds();
+ RENDER_DEBUG_IFACE(mDebugRender)->debugBound(bounds);
+ }
+ }
+
+ if (mDestructibleDebugRenderParams->VISUALIZE_DESTRUCTIBLE_SUPPORT > 0)
+ {
+ for (uint32_t structureNum = 0; structureNum < mStructures.usedCount(); ++structureNum)
+ {
+ DestructibleStructure* structure = mStructures.getUsed(structureNum);
+ if (structure)
+ {
+ structure->visualizeSupport(mDebugRender);
+ }
+ }
+ }
+
+ // debug visualization
+ for (uint32_t i = 0 ; i < mActorArray.size() ; i++)
+ {
+ DestructibleActorImpl* actor = DYNAMIC_CAST(DestructibleActorImpl*)(mActorArray[ i ]);
+ const uint16_t* visibleChunkIndexContainer = actor->getVisibleChunks();
+ const uint32_t visibleChunkIndexCount = actor->getNumVisibleChunks();
+ const PxVec3& eyePos = mApexScene->getEyePosition();
+
+ for (uint32_t chunkIndex = 0 ; chunkIndex < visibleChunkIndexCount; ++chunkIndex)
+ {
+ PxMat44 chunkPose(actor->getChunkPose(visibleChunkIndexContainer[chunkIndex]));
+ float disToEye = (-eyePos + chunkPose.getPosition()).magnitude();
+
+ if (visibleChunkIndexCount == 1 && visibleChunkIndexContainer[0] == 0)
+ {
+ // visualize actor pose
+ if (mDestructibleDebugRenderParams->VISUALIZE_DESTRUCTIBLE_ACTOR_POSE &&
+ disToEye < mDestructibleDebugRenderParams->THRESHOLD_DISTANCE_DESTRUCTIBLE_ACTOR_POSE)
+ {
+ RENDER_DEBUG_IFACE(mDebugRender)->debugAxes(chunkPose, 1, 1);
+ }
+
+ // visualize actor name
+ if (mDestructibleDebugRenderParams->VISUALIZE_DESTRUCTIBLE_ACTOR_NAME &&
+ disToEye < mDestructibleDebugRenderParams->THRESHOLD_DISTANCE_DESTRUCTIBLE_ACTOR_NAME)
+ {
+ float shiftDistance = actor->getBounds().getExtents().magnitude();
+ PxVec3 shiftDirection = -1 * mApexScene->getViewMatrix(0).column1.getXYZ();
+ PxVec3 textLocation = chunkPose.getPosition() + (shiftDistance * shiftDirection);
+
+ RENDER_DEBUG_IFACE(mDebugRender)->addToCurrentState(DebugRenderState::CameraFacing);
+ RENDER_DEBUG_IFACE(mDebugRender)->debugText(textLocation, "Destructible");
+ RENDER_DEBUG_IFACE(mDebugRender)->removeFromCurrentState(DebugRenderState::CameraFacing);
+ }
+ }
+ else
+ {
+ // visualize actor pose (fragmented)
+ if (mDestructibleDebugRenderParams->VISUALIZE_DESTRUCTIBLE_FRAGMENT_POSE &&
+ disToEye < mDestructibleDebugRenderParams->THRESHOLD_DISTANCE_DESTRUCTIBLE_FRAGMENT_POSE)
+ {
+#if 0
+ RENDER_DEBUG_IFACE(mDebugRender)->debugAxes(chunkPose, 0.5, 0.7);
+#endif //lionel: todo: chunk pose is incorrect
+ }
+ }
+ }
+ }
+
+ // visualize render mesh actor
+ for (uint32_t i = 0 ; i < mActorArray.size() ; i++)
+ {
+ DestructibleActorImpl* currentActor = NULL;
+ currentActor = DYNAMIC_CAST(DestructibleActorImpl*)(mActorArray[i]);
+ PX_ASSERT(currentActor != NULL);
+
+ for (uint32_t meshTypeIndex = 0; meshTypeIndex < DestructibleActorMeshType::Count; ++meshTypeIndex)
+ {
+ const RenderMeshActorIntl* currentMeshActor = NULL;
+ currentMeshActor = static_cast<RenderMeshActorIntl*>(const_cast<RenderMeshActor*>(currentActor->getRenderMeshActor(static_cast<DestructibleActorMeshType::Enum>(meshTypeIndex)))); //lionel: const_cast bad!
+ if (NULL == currentMeshActor)
+ {
+ continue;
+ }
+ PX_ASSERT(mDebugRender != NULL);
+ currentMeshActor->visualize(*mDebugRender, mDebugRenderParams);
+ }
+
+ // Instancing
+ if (currentActor->m_listIndex == 0)
+ {
+ for (uint32_t j = 0; j < currentActor->getDestructibleAsset()->m_instancedChunkRenderMeshActors.size(); ++j)
+ {
+ PX_ASSERT(j < currentActor->getDestructibleAsset()->m_chunkInstanceBufferData.size());
+ if (currentActor->getDestructibleAsset()->m_instancedChunkRenderMeshActors[j] != NULL && currentActor->getDestructibleAsset()->m_chunkInstanceBufferData[j].size() > 0)
+ {
+ uint32_t count = currentActor->getDestructibleAsset()->m_chunkInstanceBufferData[j].size();
+ PxMat33* scaledRotations = &currentActor->getDestructibleAsset()->m_chunkInstanceBufferData[j][0].scaledRotation;
+ PxVec3* translations = &currentActor->getDestructibleAsset()->m_chunkInstanceBufferData[j][0].translation;
+
+ const RenderMeshActorIntl* currentMeshActor = NULL;
+ currentMeshActor = static_cast<RenderMeshActorIntl*>(const_cast<RenderMeshActor*>(currentActor->getDestructibleAsset()->m_instancedChunkRenderMeshActors[j]));
+ if (NULL == currentMeshActor)
+ {
+ continue;
+ }
+ PX_ASSERT(mDebugRender != NULL);
+ currentMeshActor->visualize(*mDebugRender, mDebugRenderParams, scaledRotations, translations, sizeof(DestructibleAssetImpl::ChunkInstanceBufferDataElement), count);
+ }
+ }
+ }
+ }
+
+ // restore the rendering state
+ RENDER_DEBUG_IFACE(mDebugRender)->setPose(savedPose);
+ RENDER_DEBUG_IFACE(mDebugRender)->popRenderState();
+#endif
+}
+
+// Private interface, used by Destructible* classes
+
+DestructibleActor* DestructibleScene::getDestructibleAndChunk(const PxShape* shape, int32_t* chunkIndex) const
+{
+ if (chunkIndex)
+ {
+ *chunkIndex = ModuleDestructibleConst::INVALID_CHUNK_INDEX;
+ }
+
+ if (!mModule->owns(shape->getActor()))
+ {
+ return NULL;
+ }
+
+ DestructibleStructure::Chunk* chunk = getChunk((const PxShape*)shape);
+ if (chunk == NULL)
+ {
+ return NULL;
+ }
+
+ DestructibleActorImpl* destructible = mDestructibles.direct(chunk->destructibleID);
+
+ if (destructible != NULL)
+ {
+ if (chunkIndex)
+ {
+ *chunkIndex = (int32_t)chunk->indexInAsset;
+ }
+ return destructible->getAPI();
+ }
+
+ return NULL;
+}
+
+bool DestructibleScene::removeStructure(DestructibleStructure* structure, bool immediate)
+{
+ if (structure)
+ {
+ if (!immediate)
+ {
+ mStructureKillList.pushBack(structure);
+ }
+ else
+ {
+ for (uint32_t destructibleIndex = structure->destructibles.size(); destructibleIndex--;)
+ {
+ DestructibleActorImpl*& destructible = structure->destructibles[destructibleIndex];
+ if (destructible)
+ {
+ destructible->setStructure(NULL);
+ mDestructibles.direct(destructible->getID()) = NULL;
+ mDestructibles.free(destructible->getID());
+ destructible = NULL;
+ }
+ }
+
+ setStructureUpdate(structure, false);
+
+ mStructures.free(structure->ID);
+ mStructures.direct(structure->ID) = NULL;
+ delete structure;
+ }
+ return true;
+ }
+
+ return false;
+}
+
+void DestructibleScene::addActor(PhysXObjectDescIntl& desc, PxRigidDynamic& actor, float unscaledMass, bool isDebris)
+{
+ uintptr_t& cindex = (uintptr_t&)desc.userData;
+
+ SCOPED_PHYSX_LOCK_READ(actor.getScene());
+
+ if (actor.getRigidBodyFlags() & physx::PxRigidBodyFlag::eKINEMATIC && cindex != 0)
+ {
+ // Remove from dormant list
+ const uint32_t index = (uint32_t)~cindex;
+ if (mDormantActors.free(index))
+ {
+ mDormantActors.direct(index).actor = NULL;
+ }
+ cindex = 0;
+ }
+
+ if (cindex != 0)
+ {
+ PX_ASSERT(!"Attempting to add an actor twice.\n");
+ return;
+ }
+
+ // Add to FIFO
+ cindex = ~(uintptr_t)mActorFIFO.size();
+ uint32_t entryFlags = 0;
+ if (isDebris)
+ {
+ entryFlags |= ActorFIFOEntry::IsDebris;
+ }
+ mActorFIFO.pushBack(ActorFIFOEntry(&actor, unscaledMass, entryFlags));
+
+ // Initialize smoothed velocity so as not to get immediate sleeping
+ ActorFIFOEntry& fifoEntry = mActorFIFO.back();
+ fifoEntry.averagedLinearVelocity = mApexScene->getGravity();
+ fifoEntry.averagedAngularVelocity = PxVec3(0.0f, 0.0f, PxTwoPi);
+
+ ++mDynamicActorFIFONum;
+}
+
+void DestructibleScene::capDynamicActorCount()
+{
+ if ((mModule->m_dynamicActorFIFOMax > 0 || mModule->m_chunkFIFOMax > 0) && !mModule->m_sortByBenefit)
+ {
+ while (mDynamicActorFIFONum > 0 && ((mModule->m_dynamicActorFIFOMax > 0 && mDynamicActorFIFONum > mModule->m_dynamicActorFIFOMax) ||
+ (mModule->m_chunkFIFOMax > 0 && mTotalChunkCount > mModule->m_chunkFIFOMax)))
+ {
+ ActorFIFOEntry& entry = mActorFIFO[mActorFIFO.size() - mDynamicActorFIFONum--];
+ if (entry.actor != NULL)
+ {
+ destroyActorChunks(*entry.actor, ApexChunkFlag::DESTROYED_FIFO_FULL);
+ entry.actor = NULL;
+ }
+ }
+ }
+}
+
+void DestructibleScene::removeReferencesToActor(DestructibleActorImpl& destructible)
+{
+ if (destructible.getStructure() == NULL)
+ {
+ return;
+ }
+
+ mApexScene->lockRead(__FILE__, __LINE__);
+
+ // Remove from FIFO or dormant list
+ const uint16_t* chunkIndexPtr = destructible.getVisibleChunks();
+ const uint16_t* chunkIndexPtrStop = chunkIndexPtr + destructible.getNumVisibleChunks();
+ while (chunkIndexPtr < chunkIndexPtrStop)
+ {
+ uint16_t chunkIndex = *chunkIndexPtr++;
+ DestructibleStructure::Chunk& chunk = destructible.getStructure()->chunks[chunkIndex + destructible.getFirstChunkIndex()];
+
+ PxRigidDynamic* actor = destructible.getStructure()->getChunkActor(chunk);
+ if (actor == NULL)
+ {
+ continue;
+ }
+
+ if ((chunk.state & ChunkDynamic) != 0)
+ {
+ const PhysXObjectDesc* desc = mModule->mSdk->getPhysXObjectInfo(actor);
+ PX_ASSERT(desc != NULL);
+
+ uint32_t index = ~(uint32_t)(uintptr_t)desc->userData;
+
+ if (!(actor->getRigidBodyFlags() & physx::PxRigidBodyFlag::eKINEMATIC))
+ {
+ // find the entry in the fifo
+ if (index < mDynamicActorFIFONum)
+ {
+ mActorFIFO[index].actor = NULL;
+ }
+ }
+ else
+ {
+ // find the actor in the dormant list
+ if (mDormantActors.free(index))
+ {
+ mDormantActors.direct(index).actor = NULL;
+ }
+ }
+ }
+ }
+ mApexScene->unlockRead();
+
+ destructible.setPhysXScene(NULL);
+
+ destructible.removeSelfFromStructure();
+
+ uint32_t referencingActorCount;
+ PxRigidDynamic** referencingActors = NULL;
+ while((referencingActors = destructible.getReferencingActors(referencingActorCount)) != NULL)
+ {
+ PxRigidDynamic* actor = referencingActors[referencingActorCount - 1];
+ PhysXObjectDescIntl* actorObjDesc = (PhysXObjectDescIntl*)mModule->mSdk->getPhysXObjectInfo(actor);
+ if (actorObjDesc != NULL)
+ {
+ for (uint32_t i = actorObjDesc->mApexActors.size(); i--;)
+ {
+ if (actorObjDesc->mApexActors[i] == destructible.getAPI())
+ {
+ actorObjDesc->mApexActors.replaceWithLast(i);
+ }
+ }
+ }
+ destructible.unreferencedByActor(actor);
+ }
+
+ // Remove from kill list
+ for (uint32_t killListIndex = mActorKillList.size(); killListIndex--;)
+ {
+ bool removeActorFromList = false;
+ PxRigidDynamic* killListActor = mActorKillList[killListIndex];
+ if (killListActor != NULL)
+ {
+ PhysXObjectDescIntl* actorObjDesc = (PhysXObjectDescIntl*)mModule->mSdk->getPhysXObjectInfo(killListActor);
+ if (actorObjDesc != NULL)
+ {
+ for (uint32_t i = actorObjDesc->mApexActors.size(); i--;)
+ {
+ if (actorObjDesc->mApexActors[i] == destructible.getAPI())
+ {
+ actorObjDesc->mApexActors.replaceWithLast(i);
+ destructible.unreferencedByActor(killListActor);
+ }
+ }
+ if (actorObjDesc->mApexActors.size() == 0)
+ {
+ releasePhysXActor(*killListActor);
+ removeActorFromList = true;
+ }
+ }
+ else
+ {
+ removeActorFromList = true;
+ }
+ }
+ else
+ {
+ removeActorFromList = true;
+ }
+
+ if (removeActorFromList)
+ {
+ mActorKillList.replaceWithLast(killListIndex);
+ }
+ }
+
+ PX_ASSERT(mDestructibles.usedCount() == 0 ? mActorKillList.size() == 0 : true);
+
+ // Remove from damage and fracture buffers
+ for (RingBuffer<DamageEvent>::It i(getDamageReadBuffer()); i; ++i)
+ {
+ DamageEvent& e = *i;
+ if (e.destructibleID == destructible.getID())
+ {
+ e.flags |= (uint32_t)DamageEvent::Invalid;
+ //e.destructibleID = (uint32_t)DestructibleStructure::InvalidID;
+ }
+ }
+
+ for (RingBuffer<DamageEvent>::It i(getDamageWriteBuffer()); i; ++i)
+ {
+ DamageEvent& e = *i;
+ if (e.destructibleID == destructible.getID())
+ {
+ e.flags |= (uint32_t)DamageEvent::Invalid;
+ //e.destructibleID = (uint32_t)DestructibleStructure::InvalidID;
+ }
+ }
+
+ for (RingBuffer<FractureEvent>::It i(mFractureBuffer); i; ++i)
+ {
+ FractureEvent& e = *i;
+ if (e.destructibleID == destructible.getID() && e.chunkIndexInAsset < destructible.getDestructibleAsset()->getChunkCount())
+ {
+ e.flags |= (uint32_t)FractureEvent::Invalid;
+ //e.destructibleID = (uint32_t)DestructibleStructure::InvalidID;
+ }
+ }
+
+ for (RingBuffer<FractureEvent>::It i(mDeprioritisedFractureBuffer); i; ++i)
+ {
+ FractureEvent& e = *i;
+ if (e.destructibleID == destructible.getID() && e.chunkIndexInAsset < destructible.getDestructibleAsset()->getChunkCount())
+ {
+ e.flags |= (uint32_t)FractureEvent::Invalid;
+ //e.destructibleID = (uint32_t)DestructibleStructure::InvalidID;
+ }
+ }
+
+ // Remove from scene awake list
+ mAwakeActors.free(destructible.getID());
+
+ // Remove from instanced actors list, if it's in one
+ if (destructible.getDestructibleAsset()->mParams->chunkInstanceInfo.arraySizes[0] || destructible.getDestructibleAsset()->mParams->scatterMeshAssets.arraySizes[0])
+ {
+ for (uint32_t i = 0; i < mInstancedActors.size(); ++i)
+ {
+ if (mInstancedActors[i] == &destructible)
+ {
+ mInstancedActors.replaceWithLast(i);
+ break;
+ }
+ }
+ }
+
+ // Remove from damage report list
+ for (uint32_t damageReportIndex = 0; damageReportIndex < mDamageEventReportData.size(); ++damageReportIndex)
+ {
+ ApexDamageEventReportDataImpl& damageReport = mDamageEventReportData[damageReportIndex];
+ if (damageReport.destructible == destructible.getAPI())
+ {
+ damageReport.destructible = NULL;
+ damageReport.clearChunkReports();
+ }
+ }
+
+ // Remove from chunk state event list
+ for (uint32_t actorWithChunkStateEventIndex = mActorsWithChunkStateEvents.size(); actorWithChunkStateEventIndex--;)
+ {
+ if (mActorsWithChunkStateEvents[actorWithChunkStateEventIndex] == &destructible)
+ {
+ mActorsWithChunkStateEvents.replaceWithLast(actorWithChunkStateEventIndex);
+ }
+ }
+}
+
+bool DestructibleScene::destroyActorChunks(PxRigidDynamic& actor, uint32_t chunkFlag)
+{
+ PhysXObjectDescIntl* actorObjDesc = (PhysXObjectDescIntl*) mModule->mSdk->getPhysXObjectInfo(&actor);
+ if (!actorObjDesc)
+ {
+ return false;
+ }
+ uintptr_t& cindex = (uintptr_t&)actorObjDesc->userData;
+ if (cindex == 0)
+ {
+ return false;
+ }
+
+ SCOPED_PHYSX_LOCK_READ(actor.getScene());
+
+ // First collect a list of invariant destructible and chunk IDs
+ physx::Array<IntPair> chunks;
+ for (uint32_t i = actor.getNbShapes(); i--;)
+ {
+ PxShape* shape;
+ actor.getShapes(&shape, 1, i);
+ DestructibleStructure::Chunk* chunk = getChunk(shape);
+ PX_ASSERT(chunk != NULL);
+ if (chunk != NULL && chunk->isFirstShape(shape)) // BRG OPTIMIZE
+ {
+ getChunkReportData(*chunk, chunkFlag);
+ if (chunk->state & ChunkVisible)
+ {
+ IntPair chunkIDs;
+ chunkIDs.set((int32_t)chunk->destructibleID, (int32_t)chunk->indexInAsset);
+ chunks.pushBack(chunkIDs);
+ }
+ }
+ }
+
+ // Now release the list of chunks
+ for (uint32_t i = 0; i < chunks.size(); ++i)
+ {
+ const IntPair& chunkIDs = chunks[i];
+ DestructibleActorImpl* dactor = mDestructibles.direct((uint32_t)chunkIDs.i0);
+ if (dactor != NULL && dactor->getStructure() != NULL)
+ {
+ dactor->getStructure()->removeChunk(dactor->getStructure()->chunks[dactor->getFirstChunkIndex()+(uint16_t)chunkIDs.i1]);
+ dactor->getStructure()->removePxActorIslandReferences(actor);
+ if (dactor->getStructure()->actorForStaticChunks == &actor)
+ {
+ dactor->getStructure()->actorForStaticChunks = NULL;
+ }
+ }
+ }
+
+ for (uint32_t i = actorObjDesc->mApexActors.size(); i--;)
+ {
+ const DestructibleActor* dActor = static_cast<const DestructibleActor*>(actorObjDesc->mApexActors[i]);
+ if (dActor != NULL)
+ {
+ ((DestructibleActorProxy*)dActor)->impl.unreferencedByActor(&actor);
+ }
+ }
+
+ if (cindex != 0) // Destroying all associated chunks should have set this to 0, above
+ {
+ if (actor.getRigidBodyFlags() & physx::PxRigidBodyFlag::eKINEMATIC)
+ {
+ // Dormant actor
+ const uint32_t index = (uint32_t)~cindex;
+ if (mDormantActors.free(index))
+ {
+ mDormantActors.direct(index).actor = NULL;
+ }
+ }
+ else
+ {
+ mActorFIFO[(uint32_t)~cindex].actor = NULL;
+ cindex = 0;
+ }
+ }
+
+ schedulePxActorForDelete(*actorObjDesc);
+ return true;
+}
+
+void DestructibleScene::releasePhysXActor(PxRigidDynamic& actor)
+{
+ PX_PROFILE_ZONE("DestructibleScene::releasePhysXActor", GetInternalApexSDK()->getContextId());
+
+ const HashMap<physx::PxActor*, uint32_t>::Entry* entry = mActorsToAddIndexMap.find(&actor);
+ if (entry != NULL)
+ {
+ uint32_t index = entry->second;
+ mActorsToAdd.replaceWithLast(index);
+ if (index < mActorsToAdd.size())
+ {
+ mActorsToAddIndexMap[mActorsToAdd[index]] = index;
+ }
+ mActorsToAddIndexMap.erase(entry->first);
+ }
+
+ // handle the case where an actor was given a force but then immediately released (not sure this is possible)
+ mForcesToAddToActorsMap.erase(&actor);
+
+ {
+ SCOPED_PHYSX_LOCK_READ(actor.getScene());
+ for (uint32_t shapeN = 0; shapeN < actor.getNbShapes(); ++shapeN)
+ {
+ PxShape* shape;
+ actor.getShapes(&shape, 1, shapeN);
+ PhysXObjectDescIntl* shapeObjDesc = (PhysXObjectDescIntl*)mModule->mSdk->getPhysXObjectInfo(shape);
+ if (shapeObjDesc != NULL)
+ {
+ DestructibleStructure::Chunk* chunk = (DestructibleStructure::Chunk*)shapeObjDesc->userData;
+ if (chunk != NULL)
+ {
+ // safety net if for any reason the chunk has not been disassociated from the shape
+ PX_ALWAYS_ASSERT();
+ chunk->clearShapes();
+ }
+
+ mModule->mSdk->releaseObjectDesc(shape);
+ }
+ }
+ }
+
+ for ( uint32_t damageReportIndex = 0; damageReportIndex < mDamageEventReportData.size(); ++damageReportIndex )
+ {
+ ApexDamageEventReportDataImpl& damageReport = mDamageEventReportData[damageReportIndex];
+
+ if ( damageReport.impactDamageActor == &actor )
+ {
+ damageReport.impactDamageActor = NULL;
+ }
+ }
+
+ if (getModuleDestructible()->m_destructiblePhysXActorReport != NULL)
+ {
+ getModuleDestructible()->m_destructiblePhysXActorReport->onPhysXActorRelease(actor);
+ }
+
+
+ // make sure the actor is not referenced by any destructible
+ const PhysXObjectDescIntl* desc = static_cast<const PhysXObjectDescIntl*>(GetApexSDK()->getPhysXObjectInfo(&actor));
+ if (desc != NULL)
+ {
+ const uint32_t dActorCount = desc->mApexActors.size();
+ for (uint32_t i = 0; i < dActorCount; ++i)
+ {
+ const DestructibleActor* dActor = static_cast<const DestructibleActor*>(desc->mApexActors[i]);
+ if (dActor != NULL)
+ {
+ if (desc->mApexActors[i]->getOwner()->getObjTypeID() == DestructibleAssetImpl::getAssetTypeID())
+ {
+ DestructibleActorImpl& destructibleActor = const_cast<DestructibleActorImpl&>(static_cast<const DestructibleActorProxy*>(dActor)->impl);
+ destructibleActor.unreferencedByActor(&actor);
+ }
+ }
+ }
+ }
+
+
+ mModule->mSdk->releaseObjectDesc(&actor);
+ SCOPED_PHYSX_LOCK_WRITE(actor.getScene());
+ actor.release();
+}
+
+bool DestructibleScene::scheduleChunkShapesForDelete(DestructibleStructure::Chunk& chunk)
+{
+ DestructibleActorImpl* destructible = mDestructibles.direct(chunk.destructibleID);
+
+ if ((chunk.state & ChunkVisible) == 0)
+ {
+// PX_ASSERT(!"Cannot schedule shape for release from invisible chunk.\n");
+ chunk.clearShapes();
+ ChunkClearDescendents chunkOp(int32_t(chunk.indexInAsset + destructible->getFirstChunkIndex()));
+ forSubtree(chunk, chunkOp, true);
+ return true;
+ }
+
+ if (chunk.isDestroyed())
+ {
+ return false;
+ }
+
+ PxRigidDynamic* actor = destructible->getStructure()->getChunkActor(chunk);
+ SCOPED_PHYSX_LOCK_READ(actor->getScene());
+
+ PhysXObjectDescIntl* actorObjDesc = (PhysXObjectDescIntl*)mModule->mSdk->getPhysXObjectInfo(actor);
+ if (!actorObjDesc)
+ {
+ PX_ASSERT(!"Cannot schedule actor for release that is not owned by APEX.\n");
+ return false;
+ }
+ bool owned = false;
+ for (uint32_t i = 0; i < actorObjDesc->mApexActors.size(); ++i)
+ {
+ if (actorObjDesc->mApexActors[i] && actorObjDesc->mApexActors[i]->getOwner()->getObjTypeID() == DestructibleAssetImpl::mAssetTypeID)
+ {
+ owned = true;
+ break;
+ }
+ }
+ if (!owned && actorObjDesc->mApexActors.size())
+ {
+ PX_ASSERT(!"Cannot schedule actor for release that is not owned by this module.\n");
+ return false;
+ }
+
+#if USE_DESTRUCTIBLE_RWLOCK
+ DestructibleScopedWriteLock destructibleWriteLock(*destructible);
+#endif
+
+ if (getRenderLockMode() == RenderLockMode::PER_ACTOR_RENDER_LOCK)
+ {
+ destructible->renderDataLock();
+ }
+
+#if USE_CHUNK_RWLOCK
+ DestructibleStructure::ChunkScopedWriteLock chunkWriteLock(chunk);
+#endif
+
+ if (mTotalChunkCount > 0)
+ {
+ --mTotalChunkCount;
+ }
+
+ physx::Array<PxShape*>& shapes = destructible->getStructure()->getChunkShapes(chunk);
+ for (uint32_t i = 0; i < shapes.size(); ++i)
+ {
+ PxShape* shape = shapes[i];
+
+ const PhysXObjectDescIntl* shapeObjDesc = (const PhysXObjectDescIntl*)mModule->mSdk->getPhysXObjectInfo(shape);
+ if (!shapeObjDesc)
+ {
+ PX_ASSERT(!"Cannot schedule object for release that is not owned by APEX.\n");
+ continue;
+ }
+
+ mModule->mSdk->releaseObjectDesc(shape);
+ SCOPED_PHYSX_LOCK_WRITE(mPhysXScene);
+ ((physx::PxRigidActor*)actor)->detachShape(*shape);
+ }
+
+ if (mTotalChunkCount > 0)
+ {
+ --mTotalChunkCount;
+ }
+
+ if (actor->getNbShapes() == 0)
+ {
+ PhysXObjectDescIntl* actorObjDesc = (PhysXObjectDescIntl*)mModule->mSdk->getPhysXObjectInfo(actor);
+ if (actorObjDesc != NULL)
+ {
+ for (uint32_t i = actorObjDesc->mApexActors.size(); i--;)
+ {
+ const DestructibleActor* dActor = static_cast<const DestructibleActor*>(actorObjDesc->mApexActors[i]);
+ actorObjDesc->mApexActors.replaceWithLast(i);
+ ((DestructibleActorProxy*)dActor)->impl.unreferencedByActor(actor);
+ }
+ }
+ destructible->unreferencedByActor(actor);
+
+ if (actorObjDesc != NULL)
+ {
+ uintptr_t& cindex = (uintptr_t&)actorObjDesc->userData;
+ if (actor->getRigidBodyFlags() & physx::PxRigidBodyFlag::eKINEMATIC)
+ {
+ if (cindex != 0)
+ {
+ // Dormant actor
+ const uint32_t index = (uint32_t)~cindex;
+ if (mDormantActors.free(index))
+ {
+ mDormantActors.direct(index).actor = NULL;
+ }
+ }
+ destructible->getStructure()->removePxActorIslandReferences(*actor);
+ if (destructible->getStructure()->actorForStaticChunks == actor)
+ {
+ destructible->getStructure()->actorForStaticChunks = NULL;
+ }
+ schedulePxActorForDelete(*actorObjDesc);
+ }
+ else// if(0 != (uintptr_t&)actorObjDesc->userData)
+ {
+ PX_ASSERT(cindex != 0);
+ PX_ASSERT(mActorFIFO[(uint32_t)~cindex].actor == NULL || mActorFIFO[(uint32_t)~cindex].actor == actor);
+ cindex = 0;
+ }
+ }
+ }
+ else if (!(actor->getRigidBodyFlags() & physx::PxRigidBodyFlag::eKINEMATIC))
+ {
+ if (actor->getNbShapes() > 0)
+ {
+ const uintptr_t cindex = (uintptr_t)actorObjDesc->userData;
+ if (cindex != 0)
+ {
+ PX_ASSERT(mActorFIFO[(uint32_t)~cindex].actor == NULL || mActorFIFO[(uint32_t)~cindex].actor == actor);
+ ActorFIFOEntry& FIFOEntry = mActorFIFO[(uint32_t)~cindex];
+ FIFOEntry.unscaledMass -= destructible->getChunkMass(chunk.indexInAsset);
+ if (FIFOEntry.unscaledMass <= 0.0f)
+ {
+ FIFOEntry.unscaledMass = 1.0f; // This should only occur if the last shape is deleted. In this case, the mass won't matter.
+ }
+ FIFOEntry.flags |= ActorFIFOEntry::MassUpdateNeeded;
+ }
+ }
+ }
+ chunk.clearShapes();
+ ChunkClearDescendents chunkOp(int32_t(chunk.indexInAsset + destructible->getFirstChunkIndex()));
+ forSubtree(chunk, chunkOp, true);
+
+ chunk.state &= ~(uint32_t)ChunkVisible;
+
+ if (getRenderLockMode() == RenderLockMode::PER_ACTOR_RENDER_LOCK)
+ {
+ destructible->renderDataUnLock();
+ }
+
+ return true;
+}
+
+bool DestructibleScene::schedulePxActorForDelete(PhysXObjectDescIntl& actorDesc)
+{
+ bool inScene = false;
+ bool isKinematic = false;
+ if (mPhysXScene)
+ {
+ mPhysXScene->lockRead();
+ inScene = ((PxRigidDynamic*)actorDesc.mPhysXObject)->getScene() != NULL;
+ isKinematic = ((PxRigidDynamic*)actorDesc.mPhysXObject)->getRigidBodyFlags() & physx::PxRigidBodyFlag::eKINEMATIC;
+ mPhysXScene->unlockRead();
+ }
+ if (inScene && !isKinematic)
+ {
+ mPhysXScene->lockWrite();
+ ((PxRigidDynamic*)actorDesc.mPhysXObject)->wakeUp();
+ mPhysXScene->unlockWrite();
+ }
+ PX_ASSERT(mActorKillList.find((PxRigidDynamic*)actorDesc.mPhysXObject) == mActorKillList.end());
+ mActorKillList.pushBack((PxRigidDynamic*)actorDesc.mPhysXObject);
+ return true;
+}
+
+void DestructibleScene::applyRadiusDamage(float damage, float momentum, const PxVec3& position, float radius, bool falloff)
+{
+ // Apply scene-based damage actor-based damage. Those will split off islands.
+ DamageEvent& damageEvent = getDamageWriteBuffer().pushFront();
+ damageEvent.damage = damage;
+ damageEvent.momentum = momentum;
+ damageEvent.position = position;
+ damageEvent.direction = PxVec3(0.0f); // not used
+ damageEvent.radius = radius;
+ damageEvent.chunkIndexInAsset = ModuleDestructibleConst::INVALID_CHUNK_INDEX;
+ damageEvent.flags = DamageEvent::UseRadius;
+ if (falloff)
+ {
+ damageEvent.flags |= DamageEvent::HasFalloff;
+ }
+}
+
+
+bool DestructibleScene::isActorCreationRateExceeded()
+{
+ return mNumActorsCreatedThisFrame >= mModule->m_maxActorsCreateablePerFrame;
+}
+
+bool DestructibleScene::isFractureBufferProcessRateExceeded()
+{
+ return mNumFracturesProcessedThisFrame >= mModule->m_maxFracturesProcessedPerFrame;
+}
+
+void DestructibleScene::addToAwakeList(DestructibleActorImpl& actor)
+{
+ if (mAwakeActors.use(actor.getID()))
+ {
+ if (getModuleDestructible()->m_chunkReport != NULL) // Only use the list if there's a report
+ {
+ mOnWakeActors.pushBack(actor.getAPI());
+ }
+ return;
+ }
+
+ APEX_INTERNAL_ERROR("Destructible actor already present in awake actors list");
+}
+
+void DestructibleScene::removeFromAwakeList(DestructibleActorImpl& actor)
+{
+
+ if (mAwakeActors.free(actor.getID()))
+ {
+ if (getModuleDestructible()->m_chunkReport != NULL) // Only use the list if there's a report
+ {
+ mOnSleepActors.pushBack(actor.getAPI());
+ }
+ return;
+ }
+
+ APEX_INTERNAL_ERROR("Destructible actor not found in awake actors list, size %d", mAwakeActors.usedCount());
+}
+
+bool DestructibleScene::setMassScaling(float massScale, float scaledMassExponent)
+{
+ if (massScale > 0.0f && scaledMassExponent > 0.0f && scaledMassExponent <= 1.0f)
+ {
+ mMassScale = massScale;
+ mMassScaleInv = 1.0f / mMassScale;
+ mScaledMassExponent = scaledMassExponent;
+ mScaledMassExponentInv = 1.0f / scaledMassExponent;
+ return true;
+ }
+
+ return false;
+}
+
+void DestructibleScene::invalidateBounds(const PxBounds3* bounds, uint32_t boundsCount)
+{
+ PX_ASSERT(bounds);
+ m_invalidBounds.reserve(m_invalidBounds.size() + boundsCount);
+ for (uint32_t i = 0; i < boundsCount; ++i)
+ {
+ m_invalidBounds.pushBack(bounds[i]);
+ }
+}
+
+void DestructibleScene::setDamageApplicationRaycastFlags(nvidia::DestructibleActorRaycastFlags::Enum flags)
+{
+ m_damageApplicationRaycastFlags = flags;
+}
+
+
+void DestructibleScene::processFractureBuffer()
+{
+ PX_PROFILE_ZONE("DestructibleProcessFractureBuffer", GetInternalApexSDK()->getContextId());
+
+ // We need to use the render lock because the last frame's TMs are accessed in this method and in URR
+ bool freeSceneRenderLock = false;
+ if (mFractureBuffer.size() > 0 && getRenderLockMode() == RenderLockMode::PER_MODULE_SCENE_RENDER_LOCK)
+ {
+ lockModuleSceneRenderLock();
+ freeSceneRenderLock = true;
+ }
+
+ DestructibleActorImpl* lastDestructible = NULL;
+ for (;
+ (mFractureBuffer.size() > 0) && (!isFractureBufferProcessRateExceeded() && !isActorCreationRateExceeded());
+ mFractureBuffer.popFront())
+ {
+ FractureEvent& fractureEvent = mFractureBuffer.front();
+ PX_ASSERT(0 == (FractureEvent::SyncDirect & fractureEvent.flags) && (0 == (FractureEvent::SyncDerived & fractureEvent.flags)) && (0 == (FractureEvent::Manual & fractureEvent.flags)));
+
+ if (fractureEvent.flags & FractureEvent::Invalid)
+ {
+ continue;
+ }
+
+ PX_ASSERT(fractureEvent.destructibleID < mDestructibles.capacity());
+ DestructibleActorImpl* destructible = mDestructibles.direct(fractureEvent.destructibleID);
+ if (destructible && destructible->getStructure())
+ {
+ if (destructible->mDamageEventReportIndex == 0xffffffff && mModule->m_chunkReport != NULL)
+ {
+ destructible->mDamageEventReportIndex = mDamageEventReportData.size();
+ ApexDamageEventReportDataImpl& damageEventReport = mDamageEventReportData.insert();
+ damageEventReport.setDestructible(destructible);
+ damageEventReport.hitDirection = fractureEvent.hitDirection;
+ damageEventReport.impactDamageActor = fractureEvent.impactDamageActor;
+ damageEventReport.hitPosition = fractureEvent.position;
+ damageEventReport.appliedDamageUserData = fractureEvent.appliedDamageUserData;
+ }
+ PX_ASSERT(mModule->m_chunkReport == NULL || destructible->mDamageEventReportIndex < mDamageEventReportData.size());
+
+ // Avoid cycling lock/unlock render lock on the same actor
+ if (getRenderLockMode() == RenderLockMode::PER_ACTOR_RENDER_LOCK &&
+ destructible != lastDestructible)
+ {
+ if (lastDestructible != NULL)
+ {
+ lastDestructible->renderDataUnLock();
+ }
+ destructible->renderDataLock();
+ }
+
+ destructible->getStructure()->fractureChunk(fractureEvent);
+ ++mNumFracturesProcessedThisFrame;
+ lastDestructible = destructible;
+ }
+ }
+
+ if (freeSceneRenderLock &&
+ getRenderLockMode() == RenderLockMode::PER_MODULE_SCENE_RENDER_LOCK)
+ {
+ unlockModuleSceneRenderLock();
+ }
+ else if(lastDestructible != NULL &&
+ getRenderLockMode() == RenderLockMode::PER_ACTOR_RENDER_LOCK)
+ {
+ lastDestructible->renderDataUnLock();
+ }
+}
+
+
+void DestructibleScene::processFractureBuffer(const physx::Array<SyncParams::UserFractureEvent> * userSource)
+{
+ // if we still have pending local fractures, we push any new incoming fractures into the deprioritised fracture buffer
+ if(mFractureBuffer.size() > 0)
+ {
+ if(NULL != userSource)
+ {
+ for(physx::Array<SyncParams::UserFractureEvent>::ConstIterator iter = userSource->begin(); iter != userSource->end(); ++iter)
+ {
+ FractureEvent & fractureEvent = mSyncParams.interpret(*iter);
+ PX_ASSERT(mSyncParams.assertUserFractureEventOk(fractureEvent, *this));
+ PX_ASSERT(0 == (FractureEvent::SyncDirect & fractureEvent.flags));
+ fractureEvent.flags |= FractureEvent::SyncDirect;
+ mDeprioritisedFractureBuffer.pushBack() = fractureEvent;
+ }
+ }
+ }
+ // process deprioritised fractures only if local fractures have been processed, and any new incoming fractures only after clearing the backlog
+ else
+ {
+ // process fractures from the sync buffer
+#define DEPRIORITISED_CONDITION (mDeprioritisedFractureBuffer.size() > 0)
+ PX_ASSERT(0 == mFractureBuffer.size());
+ bool processingDeprioritised = true;
+ const uint32_t userSourceCount = (NULL != userSource) ? userSource->size() : 0;
+ uint32_t userSourceIndex = 0;
+ for (;
+ (DEPRIORITISED_CONDITION || (userSourceIndex < userSourceCount)) && (!isFractureBufferProcessRateExceeded() && !isActorCreationRateExceeded());
+ processingDeprioritised ? mDeprioritisedFractureBuffer.popFront() : unfortunateCompilerWorkaround(++userSourceIndex))
+ {
+ processingDeprioritised = DEPRIORITISED_CONDITION;
+#undef DEPRIORITISED_CONDITION
+ FractureEvent & fractureEvent = processingDeprioritised ? mDeprioritisedFractureBuffer.front() : mSyncParams.interpret((*userSource)[userSourceIndex]);
+ PX_ASSERT(!processingDeprioritised ? mSyncParams.assertUserFractureEventOk(fractureEvent, *this) :true);
+ if(!processingDeprioritised)
+ {
+ PX_ASSERT(0 == (FractureEvent::SyncDirect & fractureEvent.flags));
+ fractureEvent.flags |= FractureEvent::SyncDirect;
+ }
+
+ const bool usingEditFeature = false;
+ if(usingEditFeature)
+ {
+ if(!processingDeprioritised)
+ {
+ const DestructibleActorImpl::SyncParams & actorParams = mDestructibles.direct(fractureEvent.destructibleID)->getSyncParams();
+ mSyncParams.interceptEdit(fractureEvent, actorParams);
+ }
+ }
+
+ if(0 != (FractureEvent::Invalid & fractureEvent.flags))
+ {
+ continue;
+ }
+
+ PX_ASSERT(fractureEvent.destructibleID < mDestructibles.capacity());
+ DestructibleActorImpl * destructible = mDestructibles.direct(fractureEvent.destructibleID);
+ if(NULL != destructible && NULL != destructible->getStructure())
+ {
+ // set damage event report data and fracture event chunk report data
+ if(0xFFFFFFFF == destructible->mDamageEventReportIndex && NULL != mModule->m_chunkReport)
+ {
+ destructible->mDamageEventReportIndex = mDamageEventReportData.size();
+ ApexDamageEventReportDataImpl & damageEventReport = mDamageEventReportData.insert();
+ damageEventReport.setDestructible(destructible);
+ damageEventReport.hitDirection = fractureEvent.hitDirection;
+ damageEventReport.appliedDamageUserData = fractureEvent.appliedDamageUserData;
+ }
+
+ // perform the fracturing
+ destructible->getStructure()->fractureChunk(fractureEvent);
+ ++mNumFracturesProcessedThisFrame;
+ }
+ }
+
+ // if we still have any incoming fractures unprocessed, we push them into the deprioritised fracture buffer
+ if(NULL != userSource ? userSourceIndex < userSourceCount : false)
+ {
+ for(; userSourceIndex < userSourceCount; ++userSourceIndex)
+ {
+ FractureEvent & fractureEvent = mSyncParams.interpret((*userSource)[userSourceIndex]);
+ PX_ASSERT(mSyncParams.assertUserFractureEventOk(fractureEvent, *this));
+ PX_ASSERT(0 == (FractureEvent::SyncDirect & fractureEvent.flags));
+ fractureEvent.flags |= FractureEvent::SyncDirect;
+ mDeprioritisedFractureBuffer.pushBack() = fractureEvent;
+ }
+ }
+ }
+
+ // Move fracture event from Deferred fracture buffer to Deprioritised fracture buffer
+ if(mDeferredFractureBuffer.size() > 0)
+ {
+ mDeprioritisedFractureBuffer.reserve(mDeprioritisedFractureBuffer.size() + mDeferredFractureBuffer.size());
+ do
+ {
+ mDeprioritisedFractureBuffer.pushBack() = mDeferredFractureBuffer.front();
+ mDeferredFractureBuffer.popFront();
+ }
+ while(mDeferredFractureBuffer.size() > 0);
+ }
+}
+
+void DestructibleScene::processDamageColoringBuffer()
+{
+ for (; (mSyncDamageEventCoreDataBuffer.size() > 0) && (!isFractureBufferProcessRateExceeded() && !isActorCreationRateExceeded()); mSyncDamageEventCoreDataBuffer.popFront())
+ {
+ SyncDamageEventCoreDataParams& syncDamageEventCoreDataParams = mSyncDamageEventCoreDataBuffer.front();
+
+ PX_ASSERT(syncDamageEventCoreDataParams.destructibleID < mDestructibles.capacity());
+ DestructibleActorImpl * destructible = mDestructibles.direct(syncDamageEventCoreDataParams.destructibleID);
+ if (NULL != destructible && destructible->useDamageColoring())
+ {
+ destructible->applyDamageColoring_immediate( syncDamageEventCoreDataParams.chunkIndexInAsset,
+ syncDamageEventCoreDataParams.position,
+ syncDamageEventCoreDataParams.damage,
+ syncDamageEventCoreDataParams.radius );
+ }
+ }
+}
+
+
+void DestructibleScene::calculateDamageBufferCostProfiles(nvidia::RingBuffer<DamageEvent> & subjectDamageBuffer)
+{
+ physx::Array<uint8_t*> trail;
+ physx::Array<uint8_t> undo;
+
+ for (RingBuffer<DamageEvent>::It i(subjectDamageBuffer); i; ++i)
+ {
+ DamageEvent& e = *i;
+
+ if (e.flags & DamageEvent::Invalid)
+ {
+ continue;
+ }
+
+ // Calculate costs and benefits for this event
+ float realCost = 0;
+ float realBenefit = 0;
+ for (uint32_t depth = 0; depth <= e.getMaxDepth(); ++depth)
+ {
+ float depthCost = realCost; // Cost & benefit at depth include only real events at lower depths
+ float depthBenefit = realBenefit;
+ physx::Array<FractureEvent>& fractures = e.fractures[depth];
+ for (uint32_t j = 0; j < fractures.size(); ++j)
+ {
+ FractureEvent& fractureEvent = fractures[j];
+ float eventCost = 0;
+ float eventBenefit = 0;
+ uint32_t oldTrailSize = trail.size();
+ calculatePotentialCostAndBenefit(eventCost, eventBenefit, trail, undo, fractureEvent);
+ depthCost += eventCost; // Cost & benefit at depth include virtual events at this depth
+ depthBenefit += eventBenefit;
+ if ((fractureEvent.flags & FractureEvent::Virtual) == 0)
+ {
+ // Add real cost
+ realCost += eventCost;
+ realBenefit += eventBenefit;
+ }
+ else
+ {
+ // Undo chunk temporary state changes from virtual event
+ uint8_t** trailMark = trail.begin() + oldTrailSize;
+ uint8_t** trailEnd = trail.end();
+ uint8_t* undoEnd = undo.end();
+ while (trailEnd-- > trailMark)
+ {
+ PX_ASSERT(undoEnd > undo.begin());
+ *(*trailEnd) = (uint8_t)(((**trailEnd)&~(uint8_t)ChunkTempMask) | (*--undoEnd));
+ }
+ trail.resize((uint32_t)(trailMark - trail.begin()));
+ }
+ undo.reset();
+ }
+ e.cost[depth] = depthCost;
+ e.benefit[depth] = depthBenefit;
+ }
+
+ e.processDepth = e.getMaxDepth();
+
+ // Cover trail
+ for (uint8_t** statePtr = trail.begin(); statePtr < trail.end(); ++statePtr)
+ {
+ *(*statePtr) &= ~(uint8_t)ChunkTempMask;
+ }
+ trail.reset();
+ }
+}
+
+
+void DestructibleScene::processEventBuffers()
+{
+ //===SyncParams=== prevent user from changing sync params during this phase. do this just before the first encounter of working with sync params in the program
+ //PX_ASSERT(!mSyncParams.lockSyncParams);
+ mSyncParams.lockSyncParams = true;
+
+ //===SyncParams=== prepare user's damage event buffer, if available
+ UserDamageEventHandler * callback = NULL;
+ callback = mModule->getSyncParams().getUserDamageEventHandler();
+ const physx::Array<SyncParams::UserDamageEvent> * userSource = NULL;
+ if(NULL != callback)
+ {
+ mSyncParams.onPreProcessReadData(*callback, userSource);
+ }
+
+ //process damage buffer's damageEvents
+ nvidia::RingBuffer<DamageEvent> userDamageBuffer;
+ const nvidia::RingBuffer<DamageEvent> * const prePopulateAddress = &userDamageBuffer;
+ PX_ASSERT(0 == userDamageBuffer.size());
+ generateFractureProfilesInDamageBuffer(userDamageBuffer, userSource);
+ PX_ASSERT((NULL != userSource) ? (0 != userDamageBuffer.size()) : (0 == userDamageBuffer.size()));
+ userSource = NULL;
+ const nvidia::RingBuffer<DamageEvent> * const postPopulateAddress = &userDamageBuffer;
+ PX_ASSERT(prePopulateAddress == postPopulateAddress);
+ PX_UNUSED(prePopulateAddress);
+ PX_UNUSED(postPopulateAddress);
+
+ calculateDamageBufferCostProfiles(getDamageReadBuffer());
+
+ //===SyncParams===
+ if(0 != userDamageBuffer.size())
+ {
+ calculateDamageBufferCostProfiles(userDamageBuffer);
+ }
+
+ //===SyncParams=== give the user the damage event buffer. damage event buffer must be fully populated and locked during this call
+ if(NULL != callback)
+ {
+ mSyncParams.onProcessWriteData(*callback, getDamageReadBuffer());
+ }
+
+ //pop damage buffer, push fracture buffer. note that local fracture buffer may be 'contaminated' with non-local fractureEvents, which are derivatives of the user's damageEvents
+ fillFractureBufferFromDamage((0 != userDamageBuffer.size()) ? &userDamageBuffer : NULL);
+ userDamageBuffer.clear();
+
+ //===SyncParams=== done with user's damage event buffer, if available
+ if(NULL != callback)
+ {
+ mSyncParams.onPostProcessReadData(*callback);
+ }
+ callback = NULL;
+}
+
+void DestructibleScene::generateFractureProfilesInDamageBuffer(nvidia::RingBuffer<DamageEvent> & userDamageBuffer, const physx::Array<SyncParams::UserDamageEvent> * userSource /*= NULL*/)
+{
+
+#define LOCAL_CONDITION (eventN < getDamageReadBuffer().size())
+ bool processingLocal = true;
+ for (uint32_t eventN = 0 , userEventN = 0, userEventCount = (NULL != userSource) ? userSource->size() : 0;
+ LOCAL_CONDITION || (userEventN < userEventCount);
+ processingLocal ? ++eventN : ++userEventN)
+ {
+ processingLocal = LOCAL_CONDITION;
+#undef LOCAL_CONDITION
+ //===SyncParams===
+ DamageEvent& damageEvent = processingLocal ? getDamageReadBuffer()[eventN] : mSyncParams.interpret((*userSource)[userEventN]);
+ PX_ASSERT(!processingLocal ? mSyncParams.assertUserDamageEventOk(damageEvent, *this) : true);
+ const bool usingEditFeature = false;
+ if(usingEditFeature)
+ {
+ if(!processingLocal)
+ {
+ const DestructibleActorImpl::SyncParams & actorParams = mDestructibles.direct(damageEvent.destructibleID)->getSyncParams();
+ mSyncParams.interceptEdit(damageEvent, actorParams);
+ }
+ }
+
+ for (uint32_t i = DamageEvent::MaxDepth + 1; i--;)
+ {
+ damageEvent.fractures[i].reset();
+ }
+
+ if (damageEvent.flags & DamageEvent::Invalid)
+ {
+ continue;
+ }
+
+#if APEX_RUNTIME_FRACTURE
+ if ((damageEvent.flags & DamageEvent::IsFromImpact) == 0)
+ {
+ // TODO: Remove this, and properly process the damage events
+ for (uint32_t i = 0; i < mDestructibles.usedCount(); ++i)
+ {
+ nvidia::fracture::Actor* rtActor = mDestructibles.getUsed(i)->getRTActor();
+ if (rtActor != NULL && rtActor->patternFracture(damageEvent))
+ {
+ break;
+ }
+ }
+ }
+#endif
+
+ if (damageEvent.destructibleID == (uint32_t)DestructibleActorImpl::InvalidID)
+ {
+ // Scene-based damage. Must find destructibles.
+ PxSphereGeometry sphere(damageEvent.radius);
+
+ PxOverlapBuffer ovBuffer(&mOverlapHits[0], MAX_SHAPE_COUNT);
+ mPhysXScene->lockRead();
+ mPhysXScene->overlap(sphere, PxTransform(damageEvent.position), ovBuffer);
+ uint32_t nbHits = ovBuffer.getNbAnyHits();
+ //nbHits = nbHits >= 0 ? nbHits : MAX_SHAPE_COUNT; //Ivan: it is always true and should be removed
+
+ if (nbHits)
+ {
+ qsort(&mOverlapHits[0], nbHits, sizeof(PxOverlapHit), compareOverlapHitShapePointers);
+ DestructibleActorImpl* lastDestructible = NULL;
+ for (uint32_t i = 0; i < nbHits; ++i)
+ {
+ DestructibleActorImpl* overlapDestructible = NULL;
+ DestructibleStructure::Chunk* chunk = getChunk((const PxShape*)mOverlapHits[i].shape);
+ if (chunk != NULL)
+ {
+ overlapDestructible = mDestructibles.direct(chunk->destructibleID);
+ }
+
+ if (overlapDestructible != lastDestructible)
+ {
+ if (overlapDestructible->getStructure() != NULL)
+ {
+ // Expand damage buffer
+ DamageEvent& newEvent = processingLocal ? getDamageReadBuffer().pushBack() : userDamageBuffer.pushBack();
+ newEvent = processingLocal ? getDamageReadBuffer()[eventN] : userDamageBuffer[userEventN]; // Need to use indexed access again; damageEvent may now be invalid if the buffer resized
+ newEvent.destructibleID = overlapDestructible->getID();
+ const uint32_t maxLOD = overlapDestructible->getDestructibleAsset()->getDepthCount() > 0 ? overlapDestructible->getDestructibleAsset()->getDepthCount() - 1 : 0;
+ newEvent.minDepth = PxMin(overlapDestructible->getLOD(), maxLOD);
+ newEvent.maxDepth = damageEvent.minDepth;
+ }
+ lastDestructible = overlapDestructible;
+ }
+ }
+ }
+
+ mPhysXScene->unlockRead();
+ }
+ else
+ {
+ // Actor-based damage
+ DestructibleActorImpl* destructible = mDestructibles.direct(damageEvent.destructibleID);
+ if (destructible == NULL)
+ {
+ continue;
+ }
+
+ if (getRenderLockMode() == RenderLockMode::PER_ACTOR_RENDER_LOCK)
+ {
+ destructible->renderDataLock();
+ }
+
+ if(!processingLocal)
+ {
+ PX_ASSERT(0 == (DamageEvent::SyncDirect & damageEvent.flags));
+ damageEvent.flags |= DamageEvent::SyncDirect;
+ }
+ const uint32_t maxLOD = destructible->getDestructibleAsset()->getDepthCount() > 0 ? destructible->getDestructibleAsset()->getDepthCount() - 1 : 0;
+ damageEvent.minDepth = PxMin(destructible->getLOD(), maxLOD);
+ damageEvent.maxDepth = damageEvent.minDepth;
+ if ((damageEvent.flags & DamageEvent::UseRadius) == 0)
+ {
+ destructible->applyDamage_immediate(damageEvent);
+ }
+ else
+ {
+ destructible->applyRadiusDamage_immediate(damageEvent);
+ }
+ if (getRenderLockMode() == RenderLockMode::PER_ACTOR_RENDER_LOCK)
+ {
+ destructible->renderDataUnLock();
+ }
+ }
+
+ //===SyncParams===
+ if(!processingLocal)
+ {
+ PX_ASSERT(0 != (DamageEvent::SyncDirect & damageEvent.flags));
+ userDamageBuffer.pushBack() = damageEvent;
+ }
+ }
+}
+
+void DestructibleScene::fillFractureBufferFromDamage(nvidia::RingBuffer<DamageEvent> * userDamageBuffer /*= NULL*/)
+{
+#define LOCAL_CONDITION (getDamageReadBuffer().size() > 0)
+ bool processingLocal = true;
+ for (uint32_t userEventN = 0, userEventCount = (NULL != userDamageBuffer) ? userDamageBuffer->size() : 0;
+ LOCAL_CONDITION || (userEventN < userEventCount);
+ processingLocal ? getDamageReadBuffer().popFront() : unfortunateCompilerWorkaround(++userEventN))
+ {
+ processingLocal = LOCAL_CONDITION;
+#undef LOCAL_CONDITION
+ //===SyncParams===
+ DamageEvent& damageEvent = processingLocal ? getDamageReadBuffer().front() : (*userDamageBuffer)[userEventN];
+
+ if (damageEvent.flags & DamageEvent::Invalid)
+ {
+ continue;
+ }
+ for (uint32_t depth = 0; depth <= damageEvent.getProcessDepth(); ++depth)
+ {
+ const bool atProcessDepth = depth == damageEvent.getProcessDepth();
+ physx::Array<FractureEvent>& buffer = damageEvent.fractures[depth];
+ for (uint32_t i = 0; i < buffer.size(); ++i)
+ {
+ FractureEvent& fractureEvent = buffer[i];
+ if (atProcessDepth || (fractureEvent.flags & FractureEvent::Virtual) == 0)
+ {
+ fractureEvent.hitDirection = damageEvent.direction;
+ fractureEvent.impactDamageActor = damageEvent.impactDamageActor;
+ fractureEvent.position = damageEvent.position;
+ fractureEvent.appliedDamageUserData = damageEvent.appliedDamageUserData;
+
+ //===SyncParams===
+ PX_ASSERT(0 == (FractureEvent::DeleteChunk & fractureEvent.flags));
+ if(mDestructibles.direct(damageEvent.destructibleID)->mInDeleteChunkMode)
+ {
+ fractureEvent.flags |= FractureEvent::DeleteChunk;
+ }
+
+ if(!processingLocal)
+ {
+ PX_ASSERT(0 != (FractureEvent::SyncDerived & fractureEvent.flags));
+ mDeprioritisedFractureBuffer.pushBack() = fractureEvent;
+ }
+ else
+ {
+ PX_ASSERT(0 == (FractureEvent::SyncDirect & fractureEvent.flags) && (0 == (FractureEvent::SyncDerived & fractureEvent.flags)) && (0 == (FractureEvent::Manual & fractureEvent.flags)));
+ mFractureBuffer.pushBack() = fractureEvent;
+ }
+ }
+ }
+ }
+ }
+}
+
+static StatsInfo DestructionStatsData[] =
+{
+ {"VisibleDestructibleChunkCount", StatDataType::INT, {{0}} },
+ {"DynamicDestructibleChunkIslandCount", StatDataType::INT, {{0}} },
+ {"NumberOfShapes", StatDataType::INT, {{0}} },
+ {"NumberOfAwakeShapes", StatDataType::INT, {{0}} },
+ {"RbThroughput(Mpair/sec)", StatDataType::FLOAT, {{0}} },
+};
+
+void DestructibleScene::createModuleStats(void)
+{
+ mModuleSceneStats.numApexStats = NumberOfStats;
+ mModuleSceneStats.ApexStatsInfoPtr = (StatsInfo*)PX_ALLOC(sizeof(StatsInfo) * NumberOfStats, PX_DEBUG_EXP("StatsInfo"));
+
+ for (uint32_t i = 0; i < NumberOfStats; i++)
+ {
+ mModuleSceneStats.ApexStatsInfoPtr[i] = DestructionStatsData[i];
+ }
+}
+
+void DestructibleScene::destroyModuleStats(void)
+{
+ mModuleSceneStats.numApexStats = 0;
+ if (mModuleSceneStats.ApexStatsInfoPtr)
+ {
+ PX_FREE_AND_RESET(mModuleSceneStats.ApexStatsInfoPtr);
+ }
+}
+
+void DestructibleScene::setStatValue(int32_t index, StatValue dataVal)
+{
+ if (mModuleSceneStats.ApexStatsInfoPtr)
+ {
+ mModuleSceneStats.ApexStatsInfoPtr[index].StatCurrentValue = dataVal;
+ }
+}
+
+SceneStats* DestructibleScene::getStats()
+{
+ PX_PROFILE_ZONE("DestructibleScene::getStats", GetInternalApexSDK()->getContextId());
+ { // CPU/GPU agnositic stats
+ unsigned totalVisibleChunkCount = 0;
+ unsigned totalDynamicIslandCount = 0;
+ for (unsigned i = 0; i < mStructures.usedCount(); ++i)
+ {
+ DestructibleStructure* s = mStructures.getUsed(i);
+ for (unsigned j = 0; j < s->destructibles.size(); ++j)
+ {
+ DestructibleActorImpl* a = s->destructibles[j];
+ const unsigned visibleChunkCount = a->getNumVisibleChunks();
+ totalVisibleChunkCount += visibleChunkCount;
+ physx::PxRigidDynamic** buffer;
+ unsigned bufferSize;
+ if (a->acquirePhysXActorBuffer(buffer, bufferSize, DestructiblePhysXActorQueryFlags::Dynamic))
+ {
+ totalDynamicIslandCount += bufferSize;
+ a->releasePhysXActorBuffer();
+ }
+ }
+ }
+
+#if 0 // Issue warnings if these don't align with updated values
+ if (mTotalChunkCount != totalVisibleChunkCount)
+ {
+ APEX_DEBUG_WARNING("mTotalChunkCount = %d, actual total visible chunk count = %d.\n", mTotalChunkCount, totalVisibleChunkCount);
+ }
+ if (mDynamicActorFIFONum != totalDynamicIslandCount)
+ {
+ APEX_DEBUG_WARNING("mDynamicActorFIFONum = %d, actual total dynamic chunk island count = %d.\n", mDynamicActorFIFONum, totalDynamicIslandCount);
+ }
+ if (mModule->m_chunkFIFOMax > 0 && mTotalChunkCount > mModule->m_chunkFIFOMax)
+ {
+ APEX_DEBUG_WARNING("mTotalChunkCount = %d, mModule->m_chunkFIFOMax = %d.\n", mTotalChunkCount, mModule->m_chunkFIFOMax);
+ }
+ if (mModule->m_dynamicActorFIFOMax > 0 && mDynamicActorFIFONum > mModule->m_dynamicActorFIFOMax)
+ {
+ APEX_DEBUG_WARNING("mDynamicActorFIFONum = %d, mModule->m_dynamicActorFIFOMax = %d.\n", mDynamicActorFIFONum, mModule->m_dynamicActorFIFOMax);
+ }
+#endif
+
+ StatValue dataVal;
+
+ dataVal.Int = (int)totalVisibleChunkCount - mPreviousVisibleDestructibleChunkCount;
+ mPreviousVisibleDestructibleChunkCount = (int)totalVisibleChunkCount;
+ setStatValue(VisibleDestructibleChunkCount, dataVal);
+
+ dataVal.Int = (int)totalDynamicIslandCount - mPreviousDynamicDestructibleChunkIslandCount;
+ mPreviousDynamicDestructibleChunkIslandCount = (int)totalDynamicIslandCount;
+ setStatValue(DynamicDestructibleChunkIslandCount, dataVal);
+ }
+
+ {
+ StatValue dataVal;
+ dataVal.Int = 0;
+ setStatValue(NumberOfShapes, dataVal);
+ setStatValue(NumberOfAwakeShapes, dataVal);
+ setStatValue(RbThroughput, dataVal);
+ }
+
+ return &mModuleSceneStats;
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+bool DestructibleScene::ChunkLoadState::execute(DestructibleStructure* structure, DestructibleStructure::Chunk& chunk)
+{
+ DestructibleScene& scene = *structure->dscene;
+ DestructibleActorImpl& destructible = *scene.mDestructibles.direct(chunk.destructibleID);
+
+ const uint16_t chunkIndex = chunk.indexInAsset;
+ const uint16_t parentIndex = destructible.getDestructibleAsset()->getChunkParentIndex(chunkIndex);
+ const bool chunkDynamic = destructible.getInitialChunkDynamic(chunkIndex);
+ const bool chunkVisible = destructible.getInitialChunkVisible(chunkIndex);
+ const bool chunkDestroyed = destructible.getInitialChunkDestroyed(chunkIndex);
+ bool chunkRecurse = true;
+
+ // Destroy the chunk if necessary
+ if (chunkDestroyed && (!chunk.isDestroyed() || chunkVisible))
+ {
+ structure->removeChunk(chunk);
+ structure->setSupportInvalid(true);
+ chunkRecurse = true;
+ }
+ // Create the chunk actor or append the chunk shapes to the island's actor
+ else if (chunkVisible && chunk.getShapeCount() == 0)
+ {
+ PxRigidDynamic* chunkActor = NULL;
+ bool chunkActorCreated = false;
+
+ // Visible chunks not in an island require their own PxActor
+ if (chunk.islandID == (uint32_t)DestructibleStructure::InvalidID)
+ {
+ chunk.state &= ~(uint8_t)ChunkVisible;
+ chunkActor = scene.createRoot(chunk, destructible.getInitialChunkGlobalPose(chunkIndex), chunkDynamic, NULL, false);
+ chunkActorCreated = true;
+ }
+ else
+ {
+ // Check if the island actor has already been created
+ PxRigidDynamic* actor = NULL;
+ if (!chunkDynamic && structure->actorForStaticChunks != NULL)
+ {
+ actor = structure->actorForStaticChunks;
+ chunk.islandID = structure->actorToIsland[actor];
+ }
+ else
+ {
+ actor = structure->islandToActor[chunk.islandID];
+ }
+ if (NULL == actor)
+ {
+ // Create the island's actor
+ chunk.state &= ~(uint8_t)ChunkVisible;
+ actor = scene.createRoot(chunk, destructible.getInitialChunkGlobalPose(chunkIndex), chunkDynamic, NULL, false);
+ structure->islandToActor[chunk.islandID] = actor;
+ scene.setStructureUpdate(structure, true); // islandToActor needs to be cleared in DestructibleStructure::tick
+ if (!chunkDynamic)
+ {
+ structure->actorForStaticChunks = actor;
+ }
+ chunkActorCreated = true;
+ }
+ else
+ {
+ // Append the chunk shapes to the existing island actor
+ SCOPED_PHYSX_LOCK_READ(actor->getScene());
+ PxTransform relTM = actor->getGlobalPose().transformInv(destructible.getInitialChunkGlobalPose(chunkIndex));
+
+ chunk.state &= ~(uint8_t)ChunkVisible;
+ if (scene.appendShapes(chunk, chunkDynamic, &relTM, actor))
+ {
+ PhysXObjectDescIntl* objDesc = scene.mModule->mSdk->getGenericPhysXObjectInfo(actor);
+ if(objDesc->mApexActors.find(destructible.getAPI()) == objDesc->mApexActors.end())
+ {
+ objDesc->mApexActors.pushBack(destructible.getAPI());
+ destructible.referencedByActor(actor);
+ }
+ }
+ }
+ chunkActor = actor;
+ }
+
+ PX_ASSERT(!chunk.isDestroyed() && NULL != chunkActor);
+ if (!chunk.isDestroyed() && NULL != chunkActor)
+ {
+ // BRG - must decouple this now since we have "dormant" kinematic chunks
+ // which are chunks that have been freed but turned kinematic for "hard" sleeping
+// PX_ASSERT(chunkDynamic != chunkActor->readBodyFlag(BF_KINEMATIC));
+
+ SCOPED_PHYSX_LOCK_WRITE(scene.mApexScene);
+ SCOPED_PHYSX_LOCK_READ(scene.mApexScene);
+
+ destructible.setChunkVisibility(chunkIndex, true);
+
+ // Only set shape local poses on newly created actors (appendShapes properly positions otherwise)
+ if (chunkActorCreated)
+ {
+ for (uint32_t i = 0; i < chunk.getShapeCount(); ++i)
+ {
+ chunk.getShape(i)->setLocalPose(destructible.getInitialChunkLocalPose(chunkIndex));
+ }
+ }
+
+ // Initialize velocities for newly created dynamic actors
+ if (chunkActorCreated && chunkDynamic)
+ {
+ chunkActor->setLinearVelocity( destructible.getInitialChunkLinearVelocity( chunkIndex));
+ chunkActor->setAngularVelocity(destructible.getInitialChunkAngularVelocity(chunkIndex));
+ }
+
+ // Don't recurse visible chunks
+ chunkRecurse = false;
+ if (parentIndex != DestructibleAssetImpl::InvalidChunkIndex)
+ {
+ structure->chunks[parentIndex].flags |= ChunkMissingChild;
+ }
+ }
+ structure->setSupportInvalid(true);
+ }
+
+ return chunkRecurse;
+}
+
+
+////////////////////////////////////////////////////////////////
+// PhysX3 dependent methods
+////////////////////////////////////////////////////////////////
+
+PxRigidDynamic* DestructibleScene::createRoot(DestructibleStructure::Chunk& chunk, const PxTransform& pose, bool dynamic, PxTransform* relTM, bool fromInitialData)
+{
+ // apan2 need verify 3.0 (createRoot)
+ PX_PROFILE_ZONE("DestructibleCreateRoot", GetInternalApexSDK()->getContextId());
+
+ DestructibleActorImpl* destructible = mDestructibles.direct(chunk.destructibleID);
+
+ PxRigidDynamic* actor = NULL;
+ if (!chunk.isDestroyed())
+ {
+ actor = destructible->getStructure()->getChunkActor(chunk);
+ }
+
+ if ((chunk.state & ChunkVisible) != 0)
+ {
+ SCOPED_PHYSX_LOCK_READ(actor->getScene());
+ PhysXObjectDesc* actorObjDesc = (PhysXObjectDesc*)mModule->mSdk->getPhysXObjectInfo(actor);
+ PX_ASSERT(actor && actorObjDesc && actor->getNbShapes() >= 1);
+ if (destructible->getStructure()->chunkIsSolitary(chunk))
+ {
+ bool actorIsDynamic = (chunk.state & ChunkDynamic) != 0;
+ if (dynamic == actorIsDynamic)
+ {
+ return actor; // nothing to do
+ }
+ if (!dynamic)
+ {
+ SCOPED_PHYSX_LOCK_WRITE(actor->getScene());
+ actor->setRigidBodyFlag(PxRigidBodyFlag::eKINEMATIC, true);
+ actorObjDesc->userData = NULL;
+ return actor;
+ }
+#if 0 // XXX \todo: why doesn't this work?
+ else
+ {
+ actor->clearBodyFlag(BF_KINEMATIC);
+ addActor(*actorDesc, *actor);
+ return;
+ }
+#endif
+ }
+ scheduleChunkShapesForDelete(chunk);
+ }
+
+ const uint32_t firstHullIndex = destructible->getDestructibleAsset()->getChunkHullIndexStart(chunk.indexInAsset);
+ const uint32_t hullCount = destructible->getDestructibleAsset()->getChunkHullIndexStop(chunk.indexInAsset) - firstHullIndex;
+ PxConvexMeshGeometry* convexShapeDescs = (PxConvexMeshGeometry*)PxAlloca(sizeof(PxConvexMeshGeometry) * hullCount);
+
+ DestructibleAssetParametersNS::Chunk_Type* source = destructible->getDestructibleAsset()->mParams->chunks.buf + chunk.indexInAsset;
+
+ // Whether or not this chunk can be damaged by an impact
+ const bool takesImpactDamage = destructible->takesImpactDamageAtDepth(source->depth);
+
+ // Get user-defined descriptors, modify as needed
+
+ // Create a new actor
+ PhysX3DescTemplateImpl physX3Template;
+ destructible->getPhysX3Template(physX3Template);
+
+ const DestructibleActorParamNS::BehaviorGroup_Type& behaviorGroup = destructible->getBehaviorGroup(chunk.indexInAsset);
+
+ PxRigidDynamic* newActor = NULL;
+ PxTransform poseActor(pose);
+ if (!fromInitialData)
+ poseActor.p -= poseActor.rotate(destructible->getDestructibleAsset()->getChunkPositionOffset(chunk.indexInAsset));
+
+ poseActor.q.normalize();
+
+ newActor = mPhysXScene->getPhysics().createRigidDynamic(poseActor);
+
+ PX_ASSERT(newActor && "creating actor failed");
+ if (!newActor)
+ return NULL;
+
+ physX3Template.apply(newActor);
+
+ if (dynamic && destructible->getDestructibleParameters().dynamicChunksDominanceGroup < 32)
+ {
+ newActor->setDominanceGroup(destructible->getDestructibleParameters().dynamicChunksDominanceGroup);
+ }
+
+ newActor->setActorFlag(PxActorFlag::eSEND_SLEEP_NOTIFIES, true);
+ newActor->setMaxDepenetrationVelocity(behaviorGroup.maxDepenetrationVelocity);
+
+ // Shape(s):
+ for (uint32_t hullIndex = 0; hullIndex < hullCount; ++hullIndex)
+ {
+ PxConvexMeshGeometry& convexShapeDesc = convexShapeDescs[hullIndex];
+ PX_PLACEMENT_NEW(&convexShapeDesc, PxConvexMeshGeometry);
+ convexShapeDesc.convexMesh = destructible->getConvexMesh(hullIndex + firstHullIndex);
+ PX_ASSERT(convexShapeDesc.convexMesh != NULL);
+ convexShapeDesc.scale.scale = destructible->getScale();
+ // Divide out the cooking scale to get the proper scaling
+ const PxVec3 cookingScale = 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;
+
+ PxTransform localPose;
+ if (relTM != NULL)
+ {
+ localPose = *relTM;
+ localPose.p += destructible->getScale().multiply(destructible->getDestructibleAsset()->getChunkPositionOffset(chunk.indexInAsset));
+ }
+ else
+ {
+ localPose = PxTransform(destructible->getDestructibleAsset()->getChunkPositionOffset(chunk.indexInAsset));
+ }
+
+ shape = newActor->createShape(convexShapeDesc,
+ physX3Template.materials.begin(),
+ static_cast<uint16_t>(physX3Template.materials.size()),
+ static_cast<physx::PxShapeFlags>(physX3Template.shapeFlags));
+ PX_ASSERT(shape);
+ if (!shape)
+ {
+ APEX_INTERNAL_ERROR("Failed to create the PhysX shape.");
+ return NULL;
+ }
+ shape->setLocalPose(localPose);
+ physX3Template.apply(shape);
+
+ if (behaviorGroup.groupsMask.useGroupsMask)
+ {
+ physx::PxFilterData filterData(behaviorGroup.groupsMask.bits0,
+ behaviorGroup.groupsMask.bits1,
+ behaviorGroup.groupsMask.bits2,
+ behaviorGroup.groupsMask.bits3);
+ shape->setSimulationFilterData(filterData);
+ shape->setQueryFilterData(filterData);
+ }
+ else if (dynamic && destructible->getDestructibleParameters().useDynamicChunksGroupsMask)
+ {
+ // Override the filter data
+ shape->setSimulationFilterData(destructible->getDestructibleParameters().dynamicChunksFilterData);
+ shape->setQueryFilterData(destructible->getDestructibleParameters().dynamicChunksFilterData);
+ }
+
+ // apan2
+ 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 (behaviorGroup.materialStrength > 0.0f)
+ {
+ pairFlag |= PxPairFlag::eMODIFY_CONTACTS;
+ }
+
+ if (mApexScene->getApexPhysX3Interface())
+ mApexScene->getApexPhysX3Interface()->setContactReportFlags(shape, pairFlag, destructible->getAPI(), chunk.indexInAsset);
+
+ }
+
+ // Calculate mass
+ const float actorMass = destructible->getChunkMass(chunk.indexInAsset);
+ const float scaledMass = scaleMass(actorMass);
+
+ if (!dynamic)
+ {
+ // Make kinematic if the chunk is not dynamic
+ newActor->setRigidBodyFlag(PxRigidBodyFlag::eKINEMATIC, true);
+ }
+ else
+ {
+ newActor->setRigidBodyFlag(PxRigidBodyFlag::eKINEMATIC, false);
+ }
+
+ if (takesImpactDamage)
+ {
+ // Set contact report threshold if the actor can take impact damage
+ newActor->setContactReportThreshold(destructible->getContactReportThreshold(behaviorGroup));
+ }
+
+ // Actor:
+ if (takesImpactDamage && behaviorGroup.materialStrength > 0.0f)
+ {
+ newActor->setContactReportThreshold(behaviorGroup.materialStrength);
+ }
+
+ if (newActor)
+ {
+ // apan2
+ // bodyDesc.apply(newActor);
+ PxRigidBodyExt::setMassAndUpdateInertia(*newActor, scaledMass);
+ newActor->setAngularDamping(0.05f);
+
+ ++mNumActorsCreatedThisFrame;
+ ++mTotalChunkCount;
+
+ PhysXObjectDescIntl* actorObjDesc = mModule->mSdk->createObjectDesc(destructible->getAPI(), newActor);
+ actorObjDesc->userData = 0;
+ destructible->setActorObjDescFlags(actorObjDesc, source->depth);
+
+ if (!(newActor->getRigidBodyFlags() & PxRigidBodyFlag::eKINEMATIC))
+ {
+ bool isDebris = destructible->getDestructibleParameters().debrisDepth >= 0
+ && source->depth >= destructible->getDestructibleParameters().debrisDepth;
+ addActor(*actorObjDesc, *newActor, actorMass, isDebris);
+ }
+
+ // Set the PxShape for the chunk and all descendants
+ const uint32_t shapeCount = newActor->getNbShapes();
+ PX_ALLOCA(shapeArray, physx::PxShape*, shapeCount);
+ newActor->getShapes(shapeArray, shapeCount);
+
+ chunk.setShapes(shapeArray, shapeCount);
+ chunk.state |= (uint32_t)ChunkVisible;
+ if (dynamic)
+ {
+ chunk.state |= (uint32_t)ChunkDynamic;
+ // New visible dynamic chunk, add to visible dynamic chunk shape counts
+ destructible->increaseVisibleDynamicChunkShapeCount(hullCount);
+ if (source->depth <= destructible->getDestructibleParameters().essentialDepth)
+ {
+ destructible->increaseEssentialVisibleDynamicChunkShapeCount(hullCount);
+ }
+ }
+
+ // Create and store an island ID for the root actor
+ if (actor != NULL || chunk.islandID == DestructibleStructure::InvalidID)
+ {
+ chunk.islandID = destructible->getStructure()->newPxActorIslandReference(chunk, *newActor);
+ }
+
+ VisibleChunkSetDescendents chunkOp(chunk.indexInAsset + destructible->getFirstChunkIndex(), dynamic);
+ forSubtree(chunk, chunkOp, true);
+
+ physx::Array<PxShape*>& shapes = destructible->getStructure()->getChunkShapes(chunk);
+ for (uint32_t i = shapes.size(); i--;)
+ {
+ PxShape* shape = shapes[i];
+ PhysXObjectDescIntl* shapeObjDesc = mModule->mSdk->createObjectDesc(destructible->getAPI(), shape);
+ shapeObjDesc->userData = &chunk;
+ }
+
+ {
+ mActorsToAddIndexMap[newActor] = mActorsToAdd.size();
+ mActorsToAdd.pushBack(newActor);
+
+ // TODO do we really need this?
+ SCOPED_PHYSX_LOCK_WRITE(mPhysXScene);
+ if (!(newActor->getRigidBodyFlags() & physx::PxRigidBodyFlag::eKINEMATIC)) // cannot call 'wake-up' on kinematic bodies
+ {
+ float initWakeCounter = mPhysXScene->getWakeCounterResetValue();
+ newActor->setWakeCounter(initWakeCounter);
+ }
+ }
+
+ destructible->referencedByActor(newActor); // Must be done after adding to scene, since sleeping is checked
+ }
+
+ // Add to static root list if static
+ if (!dynamic)
+ {
+ destructible->getStaticRoots().use(chunk.indexInAsset);
+ }
+
+ if (newActor && getModuleDestructible()->m_destructiblePhysXActorReport != NULL)
+ {
+ getModuleDestructible()->m_destructiblePhysXActorReport->onPhysXActorCreate(*newActor);
+ }
+
+ return newActor;
+}
+
+void DestructibleScene::addForceToAddActorsMap(physx::PxActor* actor, const ActorForceAtPosition& force)
+{
+ mForcesToAddToActorsMap[actor] = force;
+}
+
+void DestructibleScene::addActorsToScene()
+{
+ PX_PROFILE_ZONE("DestructibleAddActorsToScene", GetInternalApexSDK()->getContextId());
+
+ if (mActorsToAdd.empty() || mPhysXScene == NULL)
+ return;
+
+ {
+ SCOPED_PHYSX_LOCK_WRITE(mPhysXScene);
+ mPhysXScene->addActors(&mActorsToAdd[0], mActorsToAdd.size());
+
+ {
+ PX_PROFILE_ZONE("DestructibleAddForcesToNewSceneActors", GetInternalApexSDK()->getContextId());
+ for (HashMap<physx::PxActor*, ActorForceAtPosition>::Iterator iter = mForcesToAddToActorsMap.getIterator(); !iter.done(); iter++)
+ {
+ PxActor* const actor = iter->first;
+ ActorForceAtPosition& forceToAdd = iter->second;
+ if (actor->getScene())
+ {
+ PxRigidDynamic* rigidDynamic = actor->is<physx::PxRigidDynamic>();
+ if (rigidDynamic && !(rigidDynamic->getRigidBodyFlags() & physx::PxRigidBodyFlag::eKINEMATIC))
+ {
+ if (!forceToAdd.force.isZero())
+ {
+ PxRigidBody* rigidBody = actor->is<physx::PxRigidBody>();
+ if (rigidBody)
+ {
+ if (forceToAdd.usePosition)
+ {
+ PxRigidBodyExt::addForceAtPos(*rigidBody, forceToAdd.force, forceToAdd.pos, forceToAdd.mode, forceToAdd.wakeup);
+ }
+ else
+ {
+ rigidBody->addForce(forceToAdd.force, forceToAdd.mode, forceToAdd.wakeup);
+ }
+ }
+ }
+ else
+ {
+ // No force, but we will apply the wakeup flag
+ if (forceToAdd.wakeup)
+ {
+ rigidDynamic->wakeUp();
+ }
+ else
+ {
+ rigidDynamic->putToSleep();
+ }
+ }
+ }
+ }
+ }
+ mForcesToAddToActorsMap.clear();
+ }
+ }
+
+ mActorsToAdd.clear();
+ mActorsToAddIndexMap.clear();
+}
+
+
+bool DestructibleScene::appendShapes(DestructibleStructure::Chunk& chunk, bool dynamic, PxTransform* relTM, PxRigidDynamic* actor)
+{
+ // PX_PROFILE_ZONE(DestructibleAppendShape, GetInternalApexSDK()->getContextId());
+
+ if (chunk.state & ChunkVisible)
+ {
+ return true;
+ }
+
+ if (chunk.isDestroyed() && actor == NULL)
+ {
+ return false;
+ }
+
+ DestructibleActorImpl* destructible = mDestructibles.direct(chunk.destructibleID);
+
+ if (actor == NULL)
+ {
+ actor = destructible->getStructure()->getChunkActor(chunk);
+ relTM = NULL;
+ }
+
+ SCOPED_PHYSX_LOCK_READ(actor->getScene());
+ const uint32_t oldActorShapeCount = actor->getNbShapes();
+
+ PhysX3DescTemplateImpl physX3Template;
+
+ destructible->getPhysX3Template(physX3Template);
+
+ DestructibleAssetParametersNS::Chunk_Type* source = destructible->getDestructibleAsset()->mParams->chunks.buf + chunk.indexInAsset;
+ const bool takesImpactDamage = destructible->takesImpactDamageAtDepth(source->depth);
+
+ // Shape(s):
+ for (uint32_t hullIndex = destructible->getDestructibleAsset()->getChunkHullIndexStart(chunk.indexInAsset); hullIndex < destructible->getDestructibleAsset()->getChunkHullIndexStop(chunk.indexInAsset); ++hullIndex)
+ {
+ PxConvexMeshGeometry convexShapeDesc;
+ convexShapeDesc.convexMesh = destructible->getConvexMesh(hullIndex);
+ // Make sure we can get a collision mesh
+ if (!convexShapeDesc.convexMesh)
+ {
+ return false;
+ }
+ convexShapeDesc.scale.scale = destructible->getScale();
+ // Divide out the cooking scale to get the proper scaling
+ const PxVec3 cookingScale = 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);
+ }
+
+ PxTransform localPose;
+ if (relTM != NULL)
+ {
+ localPose = *relTM;
+ localPose.p += destructible->getScale().multiply(destructible->getDestructibleAsset()->getChunkPositionOffset(chunk.indexInAsset));
+ }
+ else
+ {
+ localPose = destructible->getStructure()->getChunkLocalPose(chunk);
+ }
+
+ PxShape* newShape = NULL;
+
+ {
+ {
+ SCOPED_PHYSX_LOCK_WRITE(actor->getScene());
+ newShape = actor->createShape(convexShapeDesc,
+ physX3Template.materials.begin(),
+ static_cast<uint16_t>(physX3Template.materials.size()),
+ static_cast<physx::PxShapeFlags>(physX3Template.shapeFlags));
+ newShape->setLocalPose(localPose);
+
+ PX_ASSERT(newShape);
+ if (!newShape)
+ {
+ APEX_INTERNAL_ERROR("Failed to create the PhysX shape.");
+ return false;
+ }
+ physX3Template.apply(newShape);
+ }
+
+ const DestructibleActorParamNS::BehaviorGroup_Type& behaviorGroup = destructible->getBehaviorGroup(chunk.indexInAsset);
+ if (behaviorGroup.groupsMask.useGroupsMask)
+ {
+ physx::PxFilterData filterData(behaviorGroup.groupsMask.bits0,
+ behaviorGroup.groupsMask.bits1,
+ behaviorGroup.groupsMask.bits2,
+ behaviorGroup.groupsMask.bits3);
+ newShape->setSimulationFilterData(filterData);
+ newShape->setQueryFilterData(filterData);
+ }
+ else if (dynamic && destructible->getDestructibleParameters().useDynamicChunksGroupsMask)
+ {
+ // Override the filter data
+ newShape->setSimulationFilterData(destructible->getDestructibleParameters().dynamicChunksFilterData);
+ newShape->setQueryFilterData(destructible->getDestructibleParameters().dynamicChunksFilterData);
+ }
+
+ 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 (behaviorGroup.materialStrength > 0.0f)
+ {
+ pairFlag |= PxPairFlag::eMODIFY_CONTACTS;
+ }
+
+ if (mApexScene->getApexPhysX3Interface())
+ mApexScene->getApexPhysX3Interface()->setContactReportFlags(newShape, pairFlag, destructible->getAPI(), chunk.indexInAsset);
+ }
+
+ PhysXObjectDescIntl* shapeObjDesc = mModule->mSdk->createObjectDesc(destructible->getAPI(), newShape);
+ shapeObjDesc->userData = &chunk;
+ }
+
+ ++mTotalChunkCount;
+ chunk.state |= (uint8_t)ChunkVisible;
+ if (dynamic)
+ {
+ chunk.state |= (uint8_t)ChunkDynamic;
+ // New visible dynamic chunk, add to visible dynamic chunk shape counts
+ const uint32_t hullCount = destructible->getDestructibleAsset()->getChunkHullCount(chunk.indexInAsset);
+ destructible->increaseVisibleDynamicChunkShapeCount(hullCount);
+ if (destructible->getDestructibleAsset()->mParams->chunks.buf[chunk.indexInAsset].depth <= destructible->getDestructibleParameters().essentialDepth)
+ {
+ destructible->increaseEssentialVisibleDynamicChunkShapeCount(hullCount);
+ }
+ }
+
+ // Retrieve the island ID associated with the parent PxActor
+ chunk.islandID = destructible->getStructure()->actorToIsland[actor];
+
+ const uint32_t shapeCount = actor->getNbShapes();
+ PX_ALLOCA(shapeArray, physx::PxShape*, shapeCount);
+ {
+ actor->getShapes(shapeArray, shapeCount);
+ }
+
+ chunk.setShapes(shapeArray + oldActorShapeCount, shapeCount - oldActorShapeCount);
+ VisibleChunkSetDescendents chunkOp(chunk.indexInAsset + destructible->getFirstChunkIndex(), dynamic);
+ forSubtree(chunk, chunkOp, true);
+
+ // Update the mass
+ {
+ PhysXObjectDescIntl* actorObjDesc = (PhysXObjectDescIntl*)mModule->mSdk->getPhysXObjectInfo(actor);
+ const uintptr_t cindex = (uintptr_t)actorObjDesc->userData;
+ if (cindex != 0)
+ {
+ // In the FIFO, trigger mass update
+ PX_ASSERT(mActorFIFO[(uint32_t)~cindex].actor == actor);
+ ActorFIFOEntry& FIFOEntry = mActorFIFO[(uint32_t)~cindex];
+ FIFOEntry.unscaledMass += destructible->getChunkMass(chunk.indexInAsset);
+ bool isKinematic = false;
+ {
+ isKinematic = actor->getRigidBodyFlags() & physx::PxRigidBodyFlag::eKINEMATIC;
+ }
+ if (!isKinematic)
+ {
+ FIFOEntry.flags |= ActorFIFOEntry::MassUpdateNeeded;
+ }
+ }
+ }
+
+ if ((chunk.state & ChunkDynamic) == 0)
+ {
+ destructible->getStaticRoots().use(chunk.indexInAsset);
+ }
+
+ return true;
+}
+
+bool DestructibleScene::testWorldOverlap(const ConvexHullImpl& convexHull, const PxTransform& tm, const PxVec3& scale, float padding, const PxFilterData* groupsMask)
+{
+ physx::PxScene* physxSceneToUse = m_worldSupportPhysXScene != NULL ? m_worldSupportPhysXScene : mPhysXScene;
+ if (physxSceneToUse == NULL)
+ {
+ return false;
+ }
+
+ PxMat44 scaledTM(tm);
+ scaledTM.scale(PxVec4(scale, 1.f));
+ PxBounds3 worldBounds = convexHull.getBounds();
+ PxBounds3Transform(worldBounds, scaledTM);
+ PX_ASSERT(!worldBounds.isEmpty());
+ worldBounds.fattenFast(padding);
+
+ PxBoxGeometry box(worldBounds.getExtents());
+ PxQueryFilterData filterData;
+ filterData.flags = PxQueryFlag::eSTATIC;
+ if (groupsMask)
+ filterData.data = *groupsMask;
+ SCOPED_PHYSX_LOCK_READ(physxSceneToUse);
+ PxOverlapBuffer ovBuffer(&mOverlapHits[0], MAX_SHAPE_COUNT);
+ physxSceneToUse->overlap(box, PxTransform(worldBounds.getCenter()), ovBuffer, filterData, NULL);
+ uint32_t nbHit = ovBuffer.getNbAnyHits();
+ //nbHit = nbHit >= 0 ? nbHit : MAX_SHAPE_COUNT; //Ivan: it is always true and should be removed
+ for (uint32_t i = 0; i < nbHit; i++)
+ {
+ if (convexHull.intersects(*mOverlapHits[i].shape, tm, scale, padding))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+}
+} // end namespace nvidia
+