aboutsummaryrefslogtreecommitdiff
path: root/APEX_1.4/module/destructible/src/DestructibleActorImpl.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/DestructibleActorImpl.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/DestructibleActorImpl.cpp')
-rw-r--r--APEX_1.4/module/destructible/src/DestructibleActorImpl.cpp3854
1 files changed, 3854 insertions, 0 deletions
diff --git a/APEX_1.4/module/destructible/src/DestructibleActorImpl.cpp b/APEX_1.4/module/destructible/src/DestructibleActorImpl.cpp
new file mode 100644
index 00000000..849dab58
--- /dev/null
+++ b/APEX_1.4/module/destructible/src/DestructibleActorImpl.cpp
@@ -0,0 +1,3854 @@
+/*
+ * 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 "ScopedPhysXLock.h"
+#include "ModuleDestructibleImpl.h"
+#include "DestructibleActorImpl.h"
+#include "DestructibleScene.h"
+#include "SceneIntl.h"
+#include "DestructibleStructure.h"
+#include "DestructibleActorProxy.h"
+#include "ModulePerfScope.h"
+
+#if APEX_USE_PARTICLES
+#include "EmitterAsset.h"
+#include "EmitterActor.h"
+#endif // APEX_USE_PARTICLES
+
+#include "PxMath.h"
+#include <PxMaterial.h>
+
+#include "PsString.h"
+
+#include "RenderMeshAssetIntl.h"
+
+#include "DestructibleActorUtils.h"
+
+namespace nvidia
+{
+namespace destructible
+{
+using namespace physx;
+
+///////////////////////////////////////////////////////////////////////////
+namespace
+{
+bool isPxActorSleeping(const physx::PxRigidDynamic & actor)
+{
+ PX_ASSERT(NULL != &actor);
+ SCOPED_PHYSX_LOCK_READ(actor.getScene());
+ return actor.isSleeping();
+}
+} // namespace nameless
+
+template<class type>
+PX_INLINE void combsort(type* a, uint32_t num)
+{
+ uint32_t gap = num;
+ bool swapped = false;
+ do
+ {
+ swapped = false;
+ gap = (gap * 10) / 13;
+ if (gap == 9 || gap == 10)
+ {
+ gap = 11;
+ }
+ else if (gap < 1)
+ {
+ gap = 1;
+ }
+ for (type* ai = a, *aend = a + (num - gap); ai < aend; ai++)
+ {
+ type* aj = ai + gap;
+ if (*ai > *aj)
+ {
+ swapped = true;
+ nvidia::swap(*ai, *aj);
+ }
+ }
+ }
+ while (gap > 1 || swapped);
+}
+
+static NvParameterized::Interface* createDefaultState(NvParameterized::Interface* params = NULL)
+{
+ NvParameterized::Interface* state = GetInternalApexSDK()->getParameterizedTraits()->createNvParameterized(DestructibleActorState::staticClassName());
+ PX_ASSERT(NULL != state);
+ if(state)
+ {
+ NvParameterized::Handle handle(*state);
+ VERIFY_PARAM(state->getParameterHandle("actorParameters", handle));
+ if (params)
+ handle.setParamRef(params);
+ else
+ handle.initParamRef(DestructibleActorParam::staticClassName());
+
+ VERIFY_PARAM(state->getParameterHandle("actorChunks", handle));
+ handle.initParamRef(DestructibleActorChunks::staticClassName());
+ }
+ return state;
+}
+
+DestructibleActorImpl::DestructibleActorImpl(DestructibleActor* _api, DestructibleAssetImpl& _asset, DestructibleScene& scene ) :
+ mState(NULL),
+ mParams(NULL),
+ mChunks(NULL),
+ mTM(PxMat44(PxIdentity)),
+ mRelTM(PxMat44(PxIdentity)),
+ mVisibleDynamicChunkShapeCount(0),
+ mEssentialVisibleDynamicChunkShapeCount(0),
+ mDestructibleScene(&scene),
+ mAPI(_api),
+ mFlags(0),
+ mInternalFlags(0),
+ mStructure(NULL),
+ mAsset(&_asset),
+ mID((uint32_t)InvalidID),
+ mLinearSize(0.0f),
+ mCrumbleEmitter(NULL),
+ mDustEmitter(NULL),
+ mCrumbleRenderVolume(NULL),
+ mDustRenderVolume(NULL),
+ mStartTime(scene.mElapsedTime),
+ mBenefit(0.0f),
+ mInitializedFromState(false),
+ mRenderable(NULL),
+ mDescOverrideSkinnedMaterialCount(0),
+ mDescOverrideSkinnedMaterials(NULL),
+ mDescOverrideStaticMaterialCount(0),
+ mPhysXActorBufferAcquired(false),
+ mInDeleteChunkMode(false),
+ mAwakeActorCount(0),
+ mActiveFrames(0),
+ mDamageEventReportIndex(0xFFFFFFFF)
+#if APEX_RUNTIME_FRACTURE
+ //,mRTActor((PX_NEW(DestructibleRTActor)(*this)))
+ ,mRTActor(NULL)
+#endif
+#if USE_DESTRUCTIBLE_RWLOCK
+ , mLock(NULL)
+#endif
+ , mWakeForEvent(false)
+{
+ // The client (DestructibleActorProxy) is responsible for calling initialize()
+}
+
+void DestructibleActorImpl::initialize(NvParameterized::Interface* p)
+{
+ // Given actor state
+ if (NULL != p && isType(p, DestructibleParameterizedType::State))
+ {
+ initializeFromState(p);
+ initializeCommon();
+ }
+ // Given actor params
+ else if(NULL != p && isType(p, DestructibleParameterizedType::Params))
+ {
+ initializeFromParams(p);
+ initializeCommon();
+ }
+ else
+ {
+ PX_ALWAYS_ASSERT();
+ APEX_INTERNAL_ERROR("Invalid destructible actor creation arguments.");
+ }
+}
+
+void DestructibleActorImpl::initializeFromState(NvParameterized::Interface* state)
+{
+ setState(state);
+ mInitializedFromState = true;
+}
+
+void DestructibleActorImpl::initializeFromParams(NvParameterized::Interface* params)
+{
+ setState(createDefaultState(params));
+ mInitializedFromState = false;
+ mState->enableCrumbleEmitter = APEX_USE_PARTICLES ? true : false;
+ mState->enableDustEmitter = APEX_USE_PARTICLES ? true : false;
+ mState->lod = PxMax(mAsset->getDepthCount(), (uint32_t)1) - 1;
+}
+
+void DestructibleActorImpl::initializeCommon()
+{
+ setInitialGlobalPose(getParams()->globalPose);
+
+ mFlags = (uint16_t)(getParams()->dynamic ? Dynamic : 0);
+
+ mDescOverrideSkinnedMaterialCount = (uint32_t)getParams()->overrideSkinnedMaterialNames.arraySizes[0];
+ mDescOverrideStaticMaterialCount = (uint32_t)getParams()->overrideStaticMaterialNames.arraySizes[0];
+ mDescOverrideSkinnedMaterials = (const char**)PX_ALLOC(sizeof(const char*)*(mDescOverrideSkinnedMaterialCount > 0 ? mDescOverrideSkinnedMaterialCount : 1), PX_DEBUG_EXP("DestructibleActor::initializeCommon_mDescOverrideSkinnedMaterials"));
+ for (uint32_t i = 0; i < mDescOverrideSkinnedMaterialCount; ++i)
+ {
+ mDescOverrideSkinnedMaterials[i] = getParams()->overrideSkinnedMaterialNames.buf[i].buf;
+ }
+ PX_ALLOCA(staticMaterialNames, const char*, mDescOverrideStaticMaterialCount > 0 ? mDescOverrideStaticMaterialCount : 1);
+ for (uint32_t i = 0; i < mDescOverrideStaticMaterialCount; ++i)
+ {
+ staticMaterialNames[i] = getParams()->overrideStaticMaterialNames.buf[i].buf;
+ }
+
+ DestructibleActorParam* p = getParams();
+ DestructibleActorParamNS::ParametersStruct* ps = static_cast<DestructibleActorParamNS::ParametersStruct*>(p);
+
+ PhysX3DescTemplateImpl p3Desc;
+ deserialize(p3Desc, ps->p3ActorDescTemplate, ps->p3BodyDescTemplate, ps->p3ShapeDescTemplate);
+ setPhysX3Template(&p3Desc);
+
+#if USE_DESTRUCTIBLE_RWLOCK
+ if (NULL == mLock)
+ {
+ mLock = (shdfnd::ReadWriteLock*)PX_ALLOC(sizeof(shdfnd::ReadWriteLock), PX_DEBUG_EXP("DestructibleActor::RWLock"));
+ PX_PLACEMENT_NEW(mLock, shdfnd::ReadWriteLock);
+ }
+#endif
+
+ PxVec3 extents = mAsset->getBounds().getExtents();
+ mLinearSize = PxMax(PxMax(extents.x*getScale().x, extents.y*getScale().y), extents.z*getScale().z);
+
+ setDestructibleParameters(getParams()->destructibleParameters, getParams()->depthParameters);
+
+ initializeActor();
+
+ if ((getDestructibleParameters().flags & nvidia::apex::DestructibleParametersFlag::CRUMBLE_VIA_RUNTIME_FRACTURE) != 0)
+ {
+ initializeRTActor();
+ }
+}
+
+float DestructibleActorImpl::getCrumbleParticleSpacing() const
+{
+ if (mParams->crumbleParticleSpacing > 0.0f)
+ {
+ return mParams->crumbleParticleSpacing;
+ }
+#if APEX_USE_PARTICLES
+ if (getCrumbleEmitter())
+ {
+ return 2 * getCrumbleEmitter()->getObjectRadius();
+ }
+#endif // APEX_USE_PARTICLES
+ return 0.0f;
+}
+
+float DestructibleActorImpl::getDustParticleSpacing() const
+{
+ if (mParams->dustParticleSpacing > 0.0f)
+ {
+ return mParams->dustParticleSpacing;
+ }
+#if APEX_USE
+ if (getDustEmitter())
+ {
+ return 2 * getDustEmitter()->getObjectRadius();
+ }
+#endif // APEX_USE_PARTICLES
+ return 0.0f;
+}
+
+void DestructibleActorImpl::setInitialGlobalPose(const PxMat44& pose)
+{
+ mTM = pose;
+ mOriginalBounds = mAsset->getBounds();
+ PxMat44 scaledTM = mTM;
+ scaledTM.scale(PxVec4(getScale(), 1.f));
+ PxBounds3Transform(mOriginalBounds, scaledTM);
+}
+
+void DestructibleActorImpl::setState(NvParameterized::Interface* state)
+{
+ if (mState != state)
+ {
+ // wrong name?
+ if (NULL != state && !isType(state, DestructibleParameterizedType::State))
+ {
+ APEX_INTERNAL_ERROR(
+ "The parameterized interface is of type <%s> instead of <%s>. "
+ "This object will be initialized by an empty one instead!",
+ state->className(),
+ DestructibleActorState::staticClassName());
+
+ state->destroy();
+ state = NULL;
+ }
+ else if (NULL != state)
+ {
+ //TODO: Verify parameters
+ }
+
+ // If no state was given, create the default state
+ if (NULL == state)
+ {
+ state = GetInternalApexSDK()->getParameterizedTraits()->createNvParameterized(DestructibleActorState::staticClassName());
+}
+ PX_ASSERT(state);
+
+ // Reset the state if it already exists
+ if (mState != NULL)
+ {
+ mState->setSerializationCallback(NULL);
+ PX_ASSERT(mDestructibleScene && "Expected destructible scene with existing actor state.");
+ reset();
+ }
+
+ mState = static_cast<DestructibleActorState*>(state);
+ mParams = NULL;
+ mChunks = NULL;
+
+ if (NULL != mState)
+ {
+ mState->setSerializationCallback(this, (void*)((intptr_t)DestructibleParameterizedType::State));
+
+ // Cache a handle to the actorParameters
+ NvParameterized::Handle handle(*mState);
+ NvParameterized::Interface* p = NULL;
+ mState->getParameterHandle("actorParameters", handle);
+ mState->getParamRef(handle, p);
+ if (NULL != p)
+ {
+ mParams = static_cast<DestructibleActorParam*>(p);
+ mParams->setSerializationCallback(this, (void*)((intptr_t)DestructibleParameterizedType::Params));
+ }
+ else
+ APEX_INTERNAL_ERROR("Invalid destructible actor state parameterization of actor params.");
+
+ // Cache a handle to the actorChunks
+ p = NULL;
+ mState->getParameterHandle("actorChunks", handle);
+ mState->getParamRef(handle, p);
+ if (NULL != p)
+ {
+ mChunks = static_cast<DestructibleActorChunks*>(p);
+ }
+ else
+ APEX_INTERNAL_ERROR("Invalid destructible actor state parameterization of actor params.");
+ }
+ }
+}
+
+void DestructibleActorImpl::createRenderable()
+{
+ RenderMeshActor* renderMeshActors[DestructibleActorMeshType::Count];
+ for (int meshN = 0; meshN < DestructibleActorMeshType::Count; ++meshN)
+ {
+ renderMeshActors[meshN] = NULL;
+ }
+
+ // There were three choices here as to the context in which to create this renderMeshActor.
+ // a) Use the global ApexSDK context
+ // b) Make the destructible actor itself an Context
+ // c) Use the destructible actor's context (mContext)
+ //
+ // The decision was made to use option A, the global context, and have the destrucible actor's destructor
+ // call the render mesh actor's release() method. Option B was too much overhead for a single sub-actor
+ // and option C would have introduced race conditions at context deletion time.
+ PX_ASSERT(mAsset->getRenderMeshAsset());
+ RenderMeshActorDesc renderableMeshDesc;
+ renderableMeshDesc.visible = false;
+ renderableMeshDesc.bufferVisibility = true;
+ renderableMeshDesc.indexBufferHint = RenderBufferHint::DYNAMIC;
+ renderableMeshDesc.keepVisibleBonesPacked = keepVisibleBonesPacked();
+ renderableMeshDesc.forceBoneIndexChannel = !renderableMeshDesc.keepVisibleBonesPacked;
+ renderableMeshDesc.overrideMaterials = mDescOverrideSkinnedMaterials;
+ renderableMeshDesc.overrideMaterialCount = mDescOverrideSkinnedMaterialCount;
+ renderableMeshDesc.keepPreviousFrameBoneBuffer = keepPreviousFrameBoneBuffer();
+
+ renderMeshActors[DestructibleActorMeshType::Skinned] = mAsset->getRenderMeshAsset()->createActor(renderableMeshDesc);
+
+ if (drawStaticChunksInSeparateMesh() && !(getFlags() & Dynamic))
+ {
+ // Create static render mesh
+ renderableMeshDesc.renderWithoutSkinning = true;
+ renderableMeshDesc.keepPreviousFrameBoneBuffer = false;
+ PX_ALLOCA(staticMaterialNames, const char*, PxMax(mAsset->mParams->staticMaterialNames.arraySizes[0], 1));
+ if (mDescOverrideStaticMaterialCount > 0)
+ {
+ // If static override materials are defined, use them
+ renderableMeshDesc.overrideMaterialCount = mDescOverrideStaticMaterialCount;
+ renderableMeshDesc.overrideMaterials = mDescOverrideStaticMaterials;
+ }
+ else
+ {
+ // Otherwise, use the static materials in the asset, if they're defined
+ renderableMeshDesc.overrideMaterialCount = (uint32_t)mAsset->mParams->staticMaterialNames.arraySizes[0];
+ renderableMeshDesc.overrideMaterials = (const char**)staticMaterialNames;
+ for (int i = 0; i < mAsset->mParams->staticMaterialNames.arraySizes[0]; ++i)
+ {
+ staticMaterialNames[i] = mAsset->mParams->staticMaterialNames.buf[i].buf;
+ }
+ }
+ renderMeshActors[DestructibleActorMeshType::Static] = mAsset->getRenderMeshAsset()->createActor(renderableMeshDesc);
+ }
+
+ //#if APEX_RUNTIME_FRACTURE
+ //mRenderable = PX_NEW(DestructibleRTRenderable)(renderMeshActors,mRTActor);
+ //#else
+ mRenderable = PX_NEW(DestructibleRenderableImpl)(renderMeshActors,getDestructibleAsset(),(int32_t)m_listIndex);
+ //#endif
+}
+
+void DestructibleActorImpl::initializeActor(void)
+{
+ if (!mParams->doNotCreateRenderable)
+ {
+ createRenderable();
+ }
+
+ mFirstChunkIndex = DestructibleAssetImpl::InvalidChunkIndex;
+
+ // Allow color channel replacement if requested
+ mUseDamageColoring = getParams()->defaultBehaviorGroup.damageColorChange.magnitudeSquared() != 0.0f;
+ for (int32_t behaviorGroupN = 0; !mUseDamageColoring && behaviorGroupN < getParams()->behaviorGroups.arraySizes[0]; ++behaviorGroupN)
+ {
+ mUseDamageColoring = getParams()->behaviorGroups.buf[behaviorGroupN].damageColorChange.magnitudeSquared() != 0.0f;
+ }
+ if (mUseDamageColoring)
+ {
+ RenderMeshAsset* rma = mAsset->getRenderMeshAsset();
+ mDamageColorArrays.resize(rma->getSubmeshCount());
+ for (uint32_t submeshIndex = 0; submeshIndex < rma->getSubmeshCount(); ++submeshIndex)
+ {
+ physx::Array<ColorRGBA>& damageColorArray = mDamageColorArrays[submeshIndex];
+ const RenderSubmesh& submesh = rma->getSubmesh(submeshIndex);
+ const VertexBuffer& vb = submesh.getVertexBuffer();
+ const VertexFormat& vf = vb.getFormat();
+ const int32_t colorBufferIndex = vf.getBufferIndexFromID(vf.getSemanticID(RenderVertexSemantic::COLOR));
+ // Fix asset on &damageColorArray[0]
+ if (vb.getVertexCount() != 0)
+ {
+ damageColorArray.resize(vb.getVertexCount());
+ intrinsics::memZero(&damageColorArray[0], sizeof(ColorRGBA)*damageColorArray.size()); // Default to zero in case this array doesn't exist in the asset
+ if (colorBufferIndex >= 0)
+ {
+ vb.getBufferData(&damageColorArray[0], RenderDataFormat::R8G8B8A8, 0, (uint32_t)colorBufferIndex, 0, vb.getVertexCount());
+ }
+ for (uint32_t typeN = 0; typeN < DestructibleActorMeshType::Count; ++typeN)
+ {
+ RenderMeshActorIntl* renderMeshActor = (RenderMeshActorIntl*)getRenderMeshActor((DestructibleActorMeshType::Enum)typeN);
+ if (renderMeshActor != NULL)
+ {
+ renderMeshActor->setStaticColorReplacement(submeshIndex, &damageColorArray[0]);
+ }
+ }
+ }
+ }
+ }
+
+ initializeEmitters();
+
+ mVisibleChunks.reserve(mAsset->getChunkCount());
+ mVisibleChunks.lockCapacity(true);
+
+ // This is done to handle old formats, and ensure we have a sufficient # of DepthParameters
+ while (mDestructibleParameters.depthParametersCount < mAsset->getDepthCount() && mDestructibleParameters.depthParametersCount < DestructibleParameters::kDepthParametersCountMax)
+ {
+ DestructibleDepthParameters& params = mDestructibleParameters.depthParameters[mDestructibleParameters.depthParametersCount];
+ if (mDestructibleParameters.depthParametersCount == 0)
+ {
+ // Set level 0 parameters to the more expensive settings
+ memset(&params, 0xFF, sizeof(params));
+ }
+ ++mDestructibleParameters.depthParametersCount;
+ }
+
+ // When we add ourselves to the ApexScene, it will call us back with setPhysXScene
+ if (!findSelfInContext(*mDestructibleScene->mApexScene->getApexContext()))
+ addSelfToContext(*mDestructibleScene->mApexScene->getApexContext());
+
+ // Add ourself to our DestructibleScene
+ if (!findSelfInContext(*DYNAMIC_CAST(ApexContext*)(mDestructibleScene)))
+ {
+ addSelfToContext(*DYNAMIC_CAST(ApexContext*)(mDestructibleScene));
+
+ if (mAsset->mParams->chunkInstanceInfo.arraySizes[0] || mAsset->mParams->scatterMeshAssets.arraySizes[0])
+ {
+ mDestructibleScene->addInstancedActor(this);
+ }
+ }
+
+ // Set a reasonable initial bounds until the first physics update
+ mRenderBounds = getOriginalBounds();
+ if (mRenderable != NULL)
+ {
+ mRenderable->setBounds(mRenderBounds);
+ }
+ mNonInstancedBounds = mRenderBounds;
+ mInstancedBounds.setEmpty();
+}
+
+void DestructibleActorImpl::initializeRTActor(void)
+{
+#if APEX_RUNTIME_FRACTURE
+ //mRTActor->initialize();
+ PX_DELETE(mRTActor);
+ mRTActor = (nvidia::fracture::Actor*)mDestructibleScene->getDestructibleRTScene()->createActor(this);
+#endif
+}
+
+void DestructibleActorImpl::initializeEmitters(void)
+{
+#if APEX_USE_PARTICLES
+ if (getCrumbleEmitterName() || mAsset->getCrumbleEmitterName())
+ {
+ const char* name;
+ if (getCrumbleEmitterName())
+ {
+ name = getCrumbleEmitterName();
+ mAsset->mCrumbleAssetTracker.addAssetName(name, false);
+ }
+ else
+ {
+ name = mAsset->getCrumbleEmitterName();
+ }
+ initCrumbleSystem(name);
+ }
+ {
+ const char* name;
+ if (getDustEmitterName())
+ {
+ name = getDustEmitterName();
+ mAsset->mDustAssetTracker.addAssetName(name, false);
+ }
+ else
+ {
+ name = mAsset->getDustEmitterName();
+ }
+ initDustSystem(name);
+ }
+#endif // APEX_USE_PARTICLES
+}
+
+void DestructibleActorImpl::setDestructibleParameters(const DestructibleActorParamNS::DestructibleParameters_Type& destructibleParameters,
+ const DestructibleActorParamNS::DestructibleDepthParameters_DynamicArray1D_Type& destructibleDepthParameters)
+{
+ DestructibleParameters parameters;
+ deserialize(destructibleParameters, destructibleDepthParameters, parameters);
+ setDestructibleParameters(parameters);
+}
+
+void DestructibleActorImpl::setDestructibleParameters(const DestructibleParameters& _destructibleParameters)
+{
+ // If essentialDepth changes, must re-count essential visible chunks
+ if(_destructibleParameters.essentialDepth != mDestructibleParameters.essentialDepth)
+ {
+ mEssentialVisibleDynamicChunkShapeCount = 0;
+ const uint16_t* chunkIndexPtr = mVisibleChunks.usedIndices();
+ const uint16_t* chunkIndexPtrStop = chunkIndexPtr + mVisibleChunks.usedCount();
+ while (chunkIndexPtr < chunkIndexPtrStop)
+ {
+ uint16_t chunkIndex = *chunkIndexPtr++;
+ if(getDynamic(chunkIndex))
+ {
+ if((uint32_t)getDestructibleAsset()->mParams->chunks.buf[chunkIndex].depth <= _destructibleParameters.essentialDepth)
+ {
+ mEssentialVisibleDynamicChunkShapeCount += getDestructibleAsset()->getChunkHullCount(chunkIndex);
+ }
+ }
+ }
+ }
+
+ // For now we keep a cached copy of the parameters
+ // At some point we may want to move completely into the state
+ mDestructibleParameters = _destructibleParameters;
+
+
+ getParams()->supportDepth = PxClamp(getSupportDepth(), (uint32_t)0, mAsset->getDepthCount() - 1);
+
+ // Make sure the depth params are filled completely
+ uint32_t oldSize = mDestructibleParameters.depthParametersCount;
+ uint32_t neededSize = mAsset->getDepthCount();
+ if (neededSize > DestructibleParameters::kDepthParametersCountMax)
+ {
+ neededSize = DestructibleParameters::kDepthParametersCountMax;
+ }
+ mDestructibleParameters.depthParametersCount = neededSize;
+ // Fill in remaining with asset defaults
+ for (uint32_t i = oldSize; i < neededSize; ++i)
+ {
+ mDestructibleParameters.depthParameters[i].setToDefault();
+ }
+}
+
+/* An (emitter) actor in our context has been released */
+void DestructibleActorImpl::removeActorAtIndex(uint32_t index)
+{
+#if APEX_USE_PARTICLES
+ if (mDustEmitter == mActorArray[index]->getActor())
+ {
+ mDustEmitter = 0;
+ }
+ if (mCrumbleEmitter == mActorArray[index]->getActor())
+ {
+ mCrumbleEmitter = 0;
+ }
+#endif // APEX_USE_PARTICLES
+
+ ApexContext::removeActorAtIndex(index);
+}
+
+
+void DestructibleActorImpl::wakeUp(void)
+{
+ PX_ASSERT(mDestructibleScene);
+ if (mDestructibleScene)
+ {
+ mDestructibleScene->addToAwakeList(*this);
+ }
+}
+
+void DestructibleActorImpl::putToSleep(void)
+{
+ PX_ASSERT(mDestructibleScene);
+ if (mDestructibleScene)
+ {
+ mDestructibleScene->removeFromAwakeList(*this);
+ }
+}
+
+// TODO: clean up mAwakeActorCount management with mUsingActiveTransforms
+// [APEX-671] mind that un/referencedByActor() also call these functions
+
+void DestructibleActorImpl::incrementWakeCount(void)
+{
+ if (!mAwakeActorCount)
+ {
+ wakeUp();
+ }
+ mAwakeActorCount++;
+}
+
+void DestructibleActorImpl::referencedByActor(PxRigidDynamic* actor)
+{
+ if (mReferencingActors.find(actor) == mReferencingActors.end())
+ {
+ // We need to check IS_SLEEPING instead of actor->isSleeping,
+ // because the state might have changed between the last callback and now.
+ // Here, the state of the last callback is needed.
+ PhysXObjectDescIntl* desc = (PhysXObjectDescIntl*)(GetInternalApexSDK()->getPhysXObjectInfo(actor));
+ if (mDestructibleScene != NULL && !mDestructibleScene->mUsingActiveTransforms && desc != NULL && !desc->getUserDefinedFlag(PhysXActorFlags::IS_SLEEPING))
+ {
+ incrementWakeCount();
+ }
+ mReferencingActors.pushBack(actor);
+ }
+}
+
+void DestructibleActorImpl::unreferencedByActor(PxRigidDynamic* actor)
+{
+ if (mReferencingActors.findAndReplaceWithLast(actor))
+ {
+ PhysXObjectDescIntl* desc = (PhysXObjectDescIntl*)(GetInternalApexSDK()->getPhysXObjectInfo(actor));
+ if (mDestructibleScene != NULL && !mDestructibleScene->mUsingActiveTransforms && desc != NULL && !desc->getUserDefinedFlag(PhysXActorFlags::IS_SLEEPING))
+ {
+ decrementWakeCount();
+ }
+ }
+}
+
+void DestructibleActorImpl::wakeForEvent()
+{
+ if (!mWakeForEvent)
+ {
+ mWakeForEvent = true;
+ incrementWakeCount();
+ }
+}
+
+void DestructibleActorImpl::resetWakeForEvent()
+{
+ if (mWakeForEvent)
+ {
+ mWakeForEvent = false;
+ decrementWakeCount();
+ }
+}
+
+void DestructibleActorImpl::decrementWakeCount(void)
+{
+ // this assert shouldn't happen, so if it does it means bad things, tell james.
+ // basically we keep a counter of the number of awake bodies in a destructible actor
+ // so that when its zero we know no updates are needed (normally updates are really expensive
+ // per-destructible). So, if wake count is 0 and its trying to decrement it means
+ // this value is completely out of sync and that can result in bad things (performance loss
+ // or behavior loss). So, don't remove this assert!
+
+ PX_ASSERT(mAwakeActorCount > 0);
+ if (mAwakeActorCount > 0)
+ {
+ mAwakeActorCount--;
+ if (!mAwakeActorCount)
+ {
+ putToSleep();
+ }
+ }
+}
+
+void DestructibleActorImpl::setChunkVisibility(uint16_t index, bool visibility)
+{
+ PX_ASSERT((int32_t)index < mAsset->mParams->chunks.arraySizes[0]);
+ if (visibility)
+ {
+ if(mVisibleChunks.use(index))
+ {
+ if (createChunkEvents())
+ {
+ mChunkEventBufferLock.lock();
+
+ if (mChunkEventBuffer.size() == 0 && mDestructibleScene->getModuleDestructible()->m_chunkStateEventCallbackSchedule != DestructibleCallbackSchedule::Disabled)
+ {
+ mStructure->dscene->mActorsWithChunkStateEvents.pushBack(this);
+ }
+
+ DestructibleChunkEvent& chunkEvent = mChunkEventBuffer.insert();
+ chunkEvent.chunkIndex = index;
+ chunkEvent.event = DestructibleChunkEvent::VisibilityChanged | DestructibleChunkEvent::ChunkVisible;
+
+ mChunkEventBufferLock.unlock();
+ }
+ if(getDynamic(index))
+ {
+ const uint32_t chunkShapeCount = getDestructibleAsset()->getChunkHullCount(index);
+ mVisibleDynamicChunkShapeCount += chunkShapeCount;
+ if((uint32_t)getDestructibleAsset()->mParams->chunks.buf[index].depth <= mDestructibleParameters.essentialDepth)
+ {
+ mEssentialVisibleDynamicChunkShapeCount += chunkShapeCount;
+ }
+ }
+ }
+ }
+ else
+ {
+ if(mVisibleChunks.free(index))
+ {
+ if(getDynamic(index))
+ {
+ const uint32_t chunkShapeCount = getDestructibleAsset()->getChunkHullCount(index);
+ mVisibleDynamicChunkShapeCount = mVisibleDynamicChunkShapeCount >= chunkShapeCount ? mVisibleDynamicChunkShapeCount - chunkShapeCount : 0;
+ if((uint32_t)getDestructibleAsset()->mParams->chunks.buf[index].depth <= mDestructibleParameters.essentialDepth)
+ {
+ mEssentialVisibleDynamicChunkShapeCount = mEssentialVisibleDynamicChunkShapeCount >= chunkShapeCount ? mEssentialVisibleDynamicChunkShapeCount - chunkShapeCount : 0;
+ }
+ }
+ if (createChunkEvents())
+ {
+ mChunkEventBufferLock.lock();
+
+ if (mChunkEventBuffer.size() == 0 && mDestructibleScene->getModuleDestructible()->m_chunkStateEventCallbackSchedule != DestructibleCallbackSchedule::Disabled)
+ {
+ mStructure->dscene->mActorsWithChunkStateEvents.pushBack(this);
+ }
+
+ DestructibleChunkEvent& chunkEvent = mChunkEventBuffer.insert();
+ chunkEvent.chunkIndex = index;
+ chunkEvent.event = DestructibleChunkEvent::VisibilityChanged;
+
+ mChunkEventBufferLock.unlock();
+ }
+ }
+ }
+ DestructibleAssetParametersNS::Chunk_Type& sourceChunk = mAsset->mParams->chunks.buf[index];
+ if ((sourceChunk.flags & DestructibleAssetImpl::Instanced) == 0)
+ {
+ // Not instanced - need to choose the static or dynamic mesh, and set visibility for the render mesh actor
+ const DestructibleActorMeshType::Enum typeN = (getDynamic(index) || !drawStaticChunksInSeparateMesh()) ?
+ DestructibleActorMeshType::Skinned : DestructibleActorMeshType::Static;
+ RenderMeshActor* rma = getRenderMeshActor(typeN);
+ if (rma != NULL)
+ {
+ RenderMeshActorIntl* rmi = static_cast<RenderMeshActorIntl*>(rma);
+ const bool visibilityChanged = rmi->setVisibility(visibility, sourceChunk.meshPartIndex);
+ if (keepPreviousFrameBoneBuffer() && visibilityChanged && visibility)
+ {
+ // Visibility changed from false to true. If we're keeping the previous frame bone buffer, be sure to seed the previous frame buffer
+ if (!mStructure->chunks[index + mFirstChunkIndex].isDestroyed())
+ {
+ rmi->setLastFrameTM(getChunkPose(index), getScale(), sourceChunk.meshPartIndex);
+ }
+ }
+ }
+ }
+}
+
+void DestructibleActorImpl::cacheModuleData() const
+{
+ PX_PROFILE_ZONE("DestructibleCacheChunkCookedCollisionMeshes", GetInternalApexSDK()->getContextId());
+
+ if (mAsset == NULL)
+ {
+ PX_ASSERT(!"cacheModuleData: asset is NULL.\n");
+ return;
+ }
+ // The DestructibleActor::cacheModuleData() method needs to avoid incrementing the ref count
+ bool incCacheRefCount = false;
+ physx::Array<PxConvexMesh*>* meshes = mStructure->dscene->mModule->mCachedData->getConvexMeshesForScale(*this->mAsset,
+ getDestructibleScene()->getModuleDestructible()->getChunkCollisionHullCookingScale(),
+ incCacheRefCount);
+ if (meshes == NULL)
+ {
+ PX_ASSERT(!"cacheModuleData: failed to create convex mesh cache for actor.\n");
+ return;
+ }
+}
+
+/* Called by Scene when the actor is added to the scene context or when the Scene's
+ * PxScene reference changes. PxScene can be NULL if the Scene is losing its PxScene reference.
+ */
+void DestructibleActorImpl::setPhysXScene(PxScene* pxScene)
+{
+ if (pxScene)
+ {
+ PX_ASSERT(mStructure == NULL);
+ mDestructibleScene->insertDestructibleActor(mAPI);
+ }
+ else
+ {
+ PX_ASSERT(mStructure != NULL);
+ removeSelfFromStructure();
+ }
+}
+
+PxScene* DestructibleActorImpl::getPhysXScene() const
+{
+ return mDestructibleScene->mPhysXScene;
+}
+
+void DestructibleActorImpl::removeSelfFromStructure()
+{
+ if (!mStructure)
+ {
+ return;
+ }
+
+ DestructibleStructure* oldStructure = mStructure;
+
+ mStructure->removeActor(this);
+
+ if (oldStructure->destructibles.size() == 0)
+ {
+ oldStructure->dscene->removeStructure(oldStructure);
+ }
+
+ for (uint32_t typeN = 0; typeN < DestructibleActorMeshType::Count; ++typeN)
+ {
+ RenderMeshActor* renderMeshActor = getRenderMeshActor((DestructibleActorMeshType::Enum)typeN);
+ if (renderMeshActor != NULL)
+ {
+ while (renderMeshActor->visiblePartCount() > 0)
+ {
+ renderMeshActor->setVisibility(false, (uint16_t)renderMeshActor->getVisibleParts()[renderMeshActor->visiblePartCount() - 1]);
+ }
+ }
+ }
+
+ mDestructibleScene->mTotalChunkCount -= mVisibleChunks.usedCount();
+
+ mVisibleChunks.clear(mAsset->getChunkCount());
+}
+
+void DestructibleActorImpl::removeSelfFromScene()
+{
+ if (mDestructibleScene == NULL)
+ {
+ return;
+ }
+
+ mDestructibleScene->mDestructibles.direct(mID) = NULL;
+ mDestructibleScene->mDestructibles.free(mID);
+
+ mID = (uint32_t)InvalidID;
+}
+
+DestructibleActorImpl::~DestructibleActorImpl()
+{
+#if APEX_RUNTIME_FRACTURE
+ PX_DELETE(mRTActor);
+ mRTActor = NULL;
+#endif
+}
+
+void DestructibleActorImpl::release()
+{
+ if (mInRelease)
+ {
+ return;
+ }
+ mInRelease = true;
+
+ mAsset->module->mCachedData->releaseReferencesToConvexMeshesForActor(*this);
+
+ // after this call, mAsset and all other members are freed and unusable
+ mAsset->releaseDestructibleActor(*mAPI);
+
+ // HC SVNT DRACONES
+}
+
+void DestructibleActorImpl::destroy()
+{
+ mDestructibleScene->removeReferencesToActor(*this);
+
+ if (NULL != mDescOverrideSkinnedMaterials)
+ {
+ PX_FREE_AND_RESET(mDescOverrideSkinnedMaterials);
+ }
+
+ //ApexActor::destroy();
+ mInRelease = true;
+ renderDataLock();
+ for (uint32_t i = 0 ; i < mContexts.size() ; i++)
+ {
+ ContextTrack& t = mContexts[i];
+ t.ctx->removeActorAtIndex(t.index);
+ }
+ mContexts.clear();
+ renderDataUnLock();
+
+ ApexResource::removeSelf();
+
+ // BRG - moving down here from the top of this function - mState seems to be needed
+ if (NULL != mState)
+ {
+ mState->destroy();
+ mState = NULL;
+ }
+
+#if APEX_RUNTIME_FRACTURE
+ //if (mRTActor != NULL)
+ //{
+ // mRTActor->notifyParentActorDestroyed();
+ //}
+#endif
+
+ if (mRenderable != NULL)
+ {
+ mRenderable->release();
+ mRenderable = NULL;
+ }
+
+#if APEX_USE_PARTICLES
+ if (mDustEmitter)
+ {
+ mDustEmitter->release();
+ mDustEmitter = NULL;
+ }
+
+ if (mCrumbleEmitter)
+ {
+ mCrumbleEmitter->release();
+ mCrumbleEmitter = NULL;
+ }
+#endif // APEX_USE_PARTICLES
+
+#if USE_DESTRUCTIBLE_RWLOCK
+ if (mLock)
+ {
+ mLock->~shdfnd::ReadWriteLock();
+ PX_FREE(mLock);
+ }
+ mLock = NULL;
+#endif
+
+ // acquire the buffer so we can properly release it
+ const DestructibleChunkEvent* tmpChunkEventBuffer=NULL;
+ uint32_t tmpChunkEventBufferSize = 0;
+ acquireChunkEventBuffer(tmpChunkEventBuffer, tmpChunkEventBufferSize);
+
+ releaseChunkEventBuffer();
+
+ releasePhysXActorBuffer();
+
+ removeSelfFromScene();
+
+ if(0 != mSyncParams.getUserActorID())
+ {
+ setSyncParams(0, 0, NULL, NULL);
+ }
+}
+
+void DestructibleActorImpl::reset()
+{
+ destroy();
+
+ mInRelease = false;
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ mState = NULL;
+ mParams = NULL;
+ mTM = PxMat44(PxIdentity);
+ mFlags = 0;
+ mInternalFlags = 0;
+ mStructure = NULL;
+ mID = (uint32_t)InvalidID;
+ mLinearSize = 0.0f;
+ mCrumbleEmitter = NULL;
+ mDustEmitter = NULL;
+ mCrumbleRenderVolume = NULL;
+ mDustRenderVolume = NULL;
+ mStartTime = mDestructibleScene->mElapsedTime;
+ mInitializedFromState = false;
+ mAwakeActorCount = 0;
+ mDescOverrideSkinnedMaterialCount = 0;
+ mDescOverrideStaticMaterialCount = 0;
+
+ mStaticRoots.clear();
+ mVisibleChunks.clear();
+
+ #if APEX_RUNTIME_FRACTURE
+ PX_DELETE(mRTActor);
+ mRTActor = NULL;
+ #endif
+}
+
+void DestructibleActorImpl::initializeChunk(uint32_t index, DestructibleStructure::Chunk& target) const
+{
+ const DestructibleAssetParametersNS::Chunk_Type& source = getDestructibleAsset()->mParams->chunks.buf[index];
+
+ // Derived parameters
+ target.destructibleID = getID();
+ target.reportID = (uint32_t)DestructibleScene::InvalidReportID;
+ target.indexInAsset = (uint16_t)index;
+ target.islandID = (uint32_t)DestructibleStructure::InvalidID;
+
+ // Serialized parameters
+ if (mInitializedFromState && (int32_t)index < mChunks->data.arraySizes[0])
+ {
+ deserializeChunkData(mChunks->data.buf[index], target);
+ if (target.visibleAncestorIndex != (int32_t)DestructibleStructure::InvalidChunkIndex)
+ target.visibleAncestorIndex += getFirstChunkIndex();
+ }
+ // Default parameters
+ else
+ {
+ target.state = uint8_t(isInitiallyDynamic() ? ChunkDynamic : 0);
+ target.flags = 0;
+ target.damage = 0;
+ target.localOffset = getScale().multiply(getDestructibleAsset()->getChunkPositionOffset(index));
+ const PxBounds3& bounds = getDestructibleAsset()->getChunkShapeLocalBounds(index);
+ PxVec3 center = bounds.getCenter();
+ PxVec3 extents = bounds.getExtents();
+ center = center.multiply(getScale());
+ extents = extents.multiply(getScale());
+ target.localSphereCenter = center;
+ target.localSphereRadius = extents.magnitude();
+ target.clearShapes();
+ target.controlledChunk = NULL;
+
+ if (source.numChildren == 0)
+ {
+ target.flags |= ChunkMissingChild;
+ }
+ }
+
+#if USE_CHUNK_RWLOCK
+ target.lock = (shdfnd::ReadWriteLock*)PX_ALLOC(sizeof(shdfnd::ReadWriteLock), PX_DEBUG_EXP("DestructibleActor::RWLock"));
+ PX_PLACEMENT_NEW(target.lock, shdfnd::ReadWriteLock);
+#endif
+
+ // I have not yet determined if this flag should be set for all actors,
+ // or only for those initialized purely from params
+ /*if (source.numChildren == 0)
+ {
+ target.flags |= ChunkMissingChild;
+ }*/
+}
+
+
+const DestructibleStructure::Chunk& DestructibleActorImpl::getChunk(uint32_t index) const
+{
+ PX_ASSERT(mStructure != NULL);
+ PX_ASSERT(index + mFirstChunkIndex < mStructure->chunks.size());
+ return mStructure->chunks[index + mFirstChunkIndex];
+}
+
+PxTransform DestructibleActorImpl::getChunkPose(uint32_t index) const
+{
+ SCOPED_PHYSX_LOCK_READ(mDestructibleScene->mApexScene);
+
+ PX_ASSERT(mStructure != NULL);
+ PX_ASSERT(index + mFirstChunkIndex < mStructure->chunks.size());
+ PX_ASSERT(!mStructure->chunks[index + mFirstChunkIndex].isDestroyed());
+ return mStructure->getChunkGlobalPose(mStructure->chunks[index + mFirstChunkIndex]);
+}
+
+PxTransform DestructibleActorImpl::getChunkTransform(uint32_t index) const
+{
+ PX_ASSERT(mStructure != NULL);
+ PX_ASSERT(index + mFirstChunkIndex < mStructure->chunks.size());
+ PX_ASSERT(!mStructure->chunks[index + mFirstChunkIndex].isDestroyed());
+ return mStructure->getChunkGlobalTransform(mStructure->chunks[index + mFirstChunkIndex]);
+}
+
+bool DestructibleActorImpl::getInitialChunkDestroyed(uint32_t index) const
+{
+ PX_ASSERT((int32_t)index < mChunks->data.arraySizes[0]);
+ return (int32_t)index < mChunks->data.arraySizes[0]
+ ? (mChunks->data.buf[index].shapesCount == 0 && mChunks->data.buf[index].visibleAncestorIndex == (int32_t)DestructibleStructure::InvalidChunkIndex)
+ : false;
+}
+
+bool DestructibleActorImpl::getInitialChunkDynamic(uint32_t index) const
+{
+ PX_ASSERT((int32_t)index < mChunks->data.arraySizes[0]);
+ return ((int32_t)index < mChunks->data.arraySizes[0])
+ ? ((mChunks->data.buf[index].state & ChunkDynamic) != 0) : true;
+}
+
+bool DestructibleActorImpl::getInitialChunkVisible(uint32_t index) const
+{
+ PX_ASSERT((index == 0 && mChunks->data.arraySizes[0] == 0) ||
+ (int32_t)index < mChunks->data.arraySizes[0]);
+ return ((int32_t)index < mChunks->data.arraySizes[0])
+ ? ((mChunks->data.buf[index].state & ChunkVisible) != 0) : true;
+}
+
+PxTransform DestructibleActorImpl::getInitialChunkGlobalPose(uint32_t index) const
+{
+ PX_ASSERT((int32_t)index < mChunks->data.arraySizes[0]);
+ return (int32_t)index < mChunks->data.arraySizes[0]
+ ? mChunks->data.buf[index].globalPose : PxTransform(PxIdentity);
+}
+
+PxTransform DestructibleActorImpl::getInitialChunkLocalPose(uint32_t index) const
+{
+ return PxTransform(getDestructibleAsset()->getChunkPositionOffset(index));
+}
+
+PxVec3 DestructibleActorImpl::getInitialChunkLinearVelocity(uint32_t index) const
+{
+ PX_ASSERT((int32_t)index < mChunks->data.arraySizes[0]);
+ return (int32_t)index < mChunks->data.arraySizes[0]
+ ? mChunks->data.buf[index].linearVelocity : PxVec3(0);
+}
+
+PxVec3 DestructibleActorImpl::getInitialChunkAngularVelocity(uint32_t index) const
+{
+ PX_ASSERT((int32_t)index < mChunks->data.arraySizes[0]);
+ return (int32_t)index < mChunks->data.arraySizes[0]
+ ? mChunks->data.buf[index].angularVelocity : PxVec3(0);
+}
+
+PxVec3 DestructibleActorImpl::getChunkLinearVelocity(uint32_t index) const
+{
+ PX_ASSERT(mStructure != NULL);
+ PX_ASSERT(index + mFirstChunkIndex < mStructure->chunks.size());
+ PX_ASSERT(!mStructure->chunks[index + mFirstChunkIndex].isDestroyed());
+ SCOPED_PHYSX_LOCK_READ(mDestructibleScene->mApexScene);
+ return getChunkActor(index)->getLinearVelocity();
+}
+
+PxVec3 DestructibleActorImpl::getChunkAngularVelocity(uint32_t index) const
+{
+ PX_ASSERT(mStructure != NULL);
+ PX_ASSERT(index + mFirstChunkIndex < mStructure->chunks.size());
+ PX_ASSERT(!mStructure->chunks[index + mFirstChunkIndex].isDestroyed());
+ SCOPED_PHYSX_LOCK_READ(mDestructibleScene->mApexScene);
+ return getChunkActor(index)->getAngularVelocity();
+}
+
+PxRigidDynamic* DestructibleActorImpl::getChunkActor(uint32_t index)
+{
+ PX_ASSERT(mStructure != NULL);
+ PX_ASSERT(index + mFirstChunkIndex < mStructure->chunks.size());
+ DestructibleStructure::Chunk& chunk = mStructure->chunks[index + mFirstChunkIndex];
+ return mStructure->getChunkActor(chunk);
+}
+
+const PxRigidDynamic* DestructibleActorImpl::getChunkActor(uint32_t index) const
+{
+ PX_ASSERT(mStructure != NULL);
+ PX_ASSERT(index + mFirstChunkIndex < mStructure->chunks.size());
+ DestructibleStructure::Chunk& chunk = mStructure->chunks[index + mFirstChunkIndex];
+ return mStructure->getChunkActor(chunk);
+}
+
+uint32_t DestructibleActorImpl::getChunkPhysXShapes(physx::PxShape**& shapes, uint32_t chunkIndex) const
+{
+ PX_ASSERT(mStructure != NULL);
+ PX_ASSERT(chunkIndex + mFirstChunkIndex < mStructure->chunks.size());
+ DestructibleStructure::Chunk& chunk = mStructure->chunks[chunkIndex + mFirstChunkIndex];
+ physx::Array<PxShape*>& shapeArray = mStructure->getChunkShapes(chunk);
+ shapes = shapeArray.size() ? &shapeArray[0] : NULL;
+ return shapeArray.size();
+}
+
+uint32_t DestructibleActorImpl::getChunkActorFlags(uint32_t index) const
+{
+ PX_ASSERT(mStructure != NULL);
+ PX_ASSERT(index + mFirstChunkIndex < mStructure->chunks.size());
+ DestructibleStructure::Chunk& chunk = mStructure->chunks[index + mFirstChunkIndex];
+ uint32_t flags = 0;
+ if (chunk.flags & ChunkWorldSupported)
+ {
+ flags |= DestructibleActorChunkFlags::ChunkIsWorldSupported;
+ }
+ return flags;
+}
+
+void DestructibleActorImpl::applyDamage(float damage, float momentum, const PxVec3& position, const PxVec3& direction, int32_t chunkIndex, void* damageUserData)
+{
+ DamageEvent& damageEvent = mStructure->dscene->getDamageWriteBuffer().pushBack();
+ damageEvent.destructibleID = mID;
+ damageEvent.damage = damage;
+ damageEvent.momentum = momentum;
+ damageEvent.position = position;
+ damageEvent.direction = direction;
+ damageEvent.radius = 0.0f;
+ damageEvent.chunkIndexInAsset = chunkIndex >= 0 ? chunkIndex : ModuleDestructibleConst::INVALID_CHUNK_INDEX;
+ damageEvent.flags = 0;
+ damageEvent.impactDamageActor = NULL;
+ damageEvent.appliedDamageUserData = damageUserData;
+}
+
+void DestructibleActorImpl::applyRadiusDamage(float damage, float momentum, const PxVec3& position, float radius, bool falloff, void* damageUserData)
+{
+ DamageEvent& damageEvent = mStructure->dscene->getDamageWriteBuffer().pushBack();
+ damageEvent.destructibleID = mID;
+ 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;
+ damageEvent.impactDamageActor = NULL;
+ damageEvent.appliedDamageUserData = damageUserData;
+ if (falloff)
+ {
+ damageEvent.flags |= DamageEvent::HasFalloff;
+ }
+}
+
+void DestructibleActorImpl::takeImpact(const PxVec3& force, const PxVec3& position, uint16_t chunkIndex, PxActor const* damageActor)
+{
+ if (chunkIndex >= (uint16_t)mAsset->mParams->chunks.arraySizes[0])
+ {
+ return;
+ }
+ DestructibleAssetParametersNS::Chunk_Type& source = mAsset->mParams->chunks.buf[chunkIndex];
+ if (!takesImpactDamageAtDepth(source.depth))
+ {
+ return;
+ }
+ DamageEvent& damageEvent = mStructure->dscene->getDamageWriteBuffer().pushBack();
+ damageEvent.direction = force;
+ damageEvent.destructibleID = mID;
+ const float magnitude = damageEvent.direction.normalize();
+ damageEvent.damage = magnitude * mDestructibleParameters.forceToDamage;
+ damageEvent.momentum = 0.0f;
+ damageEvent.position = position;
+ damageEvent.radius = 0.0f;
+ damageEvent.chunkIndexInAsset = chunkIndex;
+ damageEvent.flags = DamageEvent::IsFromImpact;
+ damageEvent.impactDamageActor = damageActor;
+ damageEvent.appliedDamageUserData = NULL;
+
+ if (mStructure->dscene->mModule->m_impactDamageReport != NULL)
+ {
+ ImpactDamageEventData& data = mStructure->dscene->mImpactDamageEventData.insert();
+ (DamageEventCoreData&)data = (DamageEventCoreData&)damageEvent; // Copy core data
+ data.destructible = mAPI;
+ data.direction = damageEvent.direction;
+ data.impactDamageActor = damageActor;
+ }
+}
+
+int32_t DestructibleActorImpl::pointOrOBBSweep(float& time, PxVec3& normal, const PxVec3& worldBoxCenter, const PxVec3& worldBoxExtents, const PxMat33& worldBoxRT, const PxVec3& pxWorldDisp,
+ DestructibleActorRaycastFlags::Enum flags, int32_t parentChunkIndex) const
+{
+ PX_PROFILE_ZONE("DestructibleActorPointOrOBBSweep", GetInternalApexSDK()->getContextId());
+
+ // use the scene lock to protect chunk data for
+ // multi-threaded obbSweep calls on destructible actors
+ // (different lock would be good, but mixing locks can cause deadlocks)
+ SCOPED_PHYSX_LOCK_READ(mDestructibleScene->mApexScene);
+
+ // TODO: the normal is not always output, for instance, when accurateRaycasts is false, can we generate a decent normal in this case?
+ const bool pointSweep = (worldBoxExtents.magnitudeSquared() == 0.0f);
+
+ const uint32_t dynamicStateFlags = ((uint32_t)flags)&DestructibleActorRaycastFlags::AllChunks;
+ if (dynamicStateFlags == 0)
+ {
+ return ModuleDestructibleConst::INVALID_CHUNK_INDEX;
+ }
+
+ const PxVec3 worldBoxAxes[3] = { worldBoxRT.column0, worldBoxRT.column1, worldBoxRT.column2 };
+ const PxVec3 worldDisp = pxWorldDisp;
+
+ // Project box along displacement direction, for the entire sweep
+ const float worldDisp2 = worldDisp.magnitudeSquared();
+ const bool sweep = (worldDisp2 != 0.0f);
+ const float recipWorldDisp2 = sweep ? 1.0f / worldDisp2 : 0.0f;
+ const float boxProjectedRadius = worldBoxExtents.x * PxAbs(worldDisp.dot(worldBoxAxes[0])) +
+ worldBoxExtents.y * PxAbs(worldDisp.dot(worldBoxAxes[1])) +
+ worldBoxExtents.z * PxAbs(worldDisp.dot(worldBoxAxes[2]));
+ const float boxProjectedCenter = worldDisp.dot(worldBoxCenter);
+ const float boxBSphereRadius = worldBoxExtents.magnitude(); // May reduce this by projecting the box along worldDisp
+
+ float boxSweptMax;
+ float minRayT;
+ float maxRayT;
+
+ const float boxProjectedMin = boxProjectedCenter - boxProjectedRadius;
+ const float boxProjectedMax = boxProjectedCenter + boxProjectedRadius;
+ if ((flags & DestructibleActorRaycastFlags::SegmentIntersect) != 0)
+ {
+ boxSweptMax = boxProjectedMax + worldDisp2;
+ minRayT = 0.0f;
+ maxRayT = 1.0f;
+ }
+ else
+ {
+ boxSweptMax = PX_MAX_F32;
+ minRayT = -PX_MAX_F32;
+ maxRayT = PX_MAX_F32;
+ }
+
+ PxVec3 rayorig;
+ PxVec3 raydir;
+
+// OverlapLineSegmentAABBCache segmentCache;
+ if (pointSweep)
+ {
+// computeOverlapLineSegmentAABBCache(segmentCache, worldDisplacement);
+ rayorig = worldBoxCenter;
+ raydir = worldDisp;
+ }
+
+ float minTime = PX_MAX_F32;
+ uint32_t totalVisibleChunkCount;
+ const uint16_t* visibleChunkIndices;
+ uint16_t dummyIndexArray = (uint16_t)parentChunkIndex;
+ if (parentChunkIndex == ModuleDestructibleConst::INVALID_CHUNK_INDEX)
+ {
+ totalVisibleChunkCount = mVisibleChunks.usedCount();
+ visibleChunkIndices = mVisibleChunks.usedIndices();
+ }
+ else
+ {
+ totalVisibleChunkCount = 1;
+ visibleChunkIndices = &dummyIndexArray;
+ }
+
+ IndexedReal* chunkProjectedMinima = (IndexedReal*)PxAlloca(totalVisibleChunkCount * sizeof(IndexedReal));
+ uint32_t* chunkIndices = (uint32_t*)PxAlloca(totalVisibleChunkCount * sizeof(uint32_t));
+ PxTransform* chunkTMs = (PxTransform*)PxAlloca(totalVisibleChunkCount * sizeof(PxTransform));
+
+#if USE_DESTRUCTIBLE_RWLOCK
+ mLock->lockReader();
+#endif
+ {
+ // Must find an intersecting visible chunk.
+ PX_PROFILE_ZONE("DestructibleRayCastFindVisibleChunk", GetInternalApexSDK()->getContextId());
+
+ // Find candidate chunks
+ uint32_t candidateChunkCount = 0;
+
+ for (uint32_t chunkNum = 0; chunkNum < totalVisibleChunkCount; ++chunkNum)
+ {
+ if (candidateChunkCount >= totalVisibleChunkCount)
+ {
+ PX_ALWAYS_ASSERT();
+ break;
+ }
+ const uint32_t chunkIndexInAsset = visibleChunkIndices[chunkNum];
+ DestructibleStructure::Chunk& chunk = mStructure->chunks[chunkIndexInAsset + mFirstChunkIndex];
+ DestructibleAssetParametersNS::Chunk_Type& chunkSource = mAsset->mParams->chunks.buf[chunkIndexInAsset];
+ if (mDestructibleParameters.depthParameters[chunkSource.depth].ignoresRaycastCallbacks())
+ {
+ continue;
+ }
+ if ((chunk.state & ChunkVisible) == 0)
+ {
+ continue; // In case a valid parentChunkIndex was passed in
+ }
+#if USE_CHUNK_RWLOCK
+ DestructibleStructure::ChunkScopedReadLock chunkReadLock(chunk);
+#endif
+ if (chunk.isDestroyed())
+ {
+ continue;
+ }
+
+ if (dynamicStateFlags != DestructibleActorRaycastFlags::AllChunks)
+ {
+ if ((chunk.state & ChunkDynamic)==0)
+ {
+ if ((flags & DestructibleActorRaycastFlags::StaticChunks) == 0)
+ {
+ continue;
+ }
+ }
+ else
+ {
+ if ((flags & DestructibleActorRaycastFlags::DynamicChunks) == 0)
+ {
+ continue;
+ }
+ }
+ }
+
+ PxTransform& tm = chunkTMs[candidateChunkCount];
+ tm = mStructure->getChunkGlobalPose(chunk); // Cache this off
+ IndexedReal& chunkProjectedMin = chunkProjectedMinima[candidateChunkCount];
+
+ if (sweep)
+ {
+ // Project chunk bounds along displacement direction
+ PxVec3 rM = tm.q.rotateInv(pxWorldDisp);
+ const float chunkProjectedCenter = rM.dot(chunk.localSphereCenter) + pxWorldDisp.dot(tm.p);
+ PxVec3 chunkLocalExtents = mAsset->getChunkShapeLocalBounds(chunkIndexInAsset).getExtents();
+ chunkLocalExtents = chunkLocalExtents.multiply(getScale());
+ const float chunkProjectedRadius = chunkLocalExtents.x * PxAbs(rM.x) +
+ chunkLocalExtents.y * PxAbs(rM.y) +
+ chunkLocalExtents.z * PxAbs(rM.z);
+ chunkProjectedMin.value = chunkProjectedCenter - chunkProjectedRadius;
+ if (boxProjectedMin >= chunkProjectedCenter + chunkProjectedRadius || boxSweptMax <= chunkProjectedMin.value)
+ {
+ // Beyond or before projected sweep
+ continue;
+ }
+
+ // Perform "corridor test"
+ const PxVec3 X = tm.transform(chunk.localSphereCenter) - worldBoxCenter;
+ const float sumRadius = boxBSphereRadius + chunk.localSphereRadius;
+ const float Xv = X.dot(pxWorldDisp);
+ if (worldDisp2 * (X.magnitudeSquared() - sumRadius * sumRadius) >= Xv * Xv)
+ {
+ // Outside of corridor
+ continue;
+ }
+ }
+ else
+ {
+ // Overlap test
+ const PxVec3 X = tm.transform(chunk.localSphereCenter) - worldBoxCenter;
+ const float sumRadius = boxBSphereRadius + chunk.localSphereRadius;
+ if (X.magnitudeSquared() >= sumRadius * sumRadius)
+ {
+ continue;
+ }
+ }
+
+ // We'll keep this one
+ chunkProjectedMin.index = candidateChunkCount;
+ chunkIndices[candidateChunkCount++] = chunkIndexInAsset;
+#if USE_CHUNK_RWLOCK
+ chunk.lock->lockReader(); // Add another read lock, be sure to unlock after use in next loop
+#endif
+ }
+
+ // Sort chunk bounds projected minima
+ if (sweep && candidateChunkCount > 1)
+ {
+ combsort(chunkProjectedMinima, candidateChunkCount);
+ }
+
+ uint32_t candidateChunkNum = 0;
+ for (; candidateChunkNum < candidateChunkCount; ++candidateChunkNum)
+ {
+ IndexedReal& indexedChunkMin = chunkProjectedMinima[candidateChunkNum];
+ if (sweep && minTime <= (indexedChunkMin.value - boxProjectedMax)*recipWorldDisp2)
+ {
+ // Early-out
+ break;
+ }
+ uint16_t chunkIndexInAsset = (uint16_t)chunkIndices[indexedChunkMin.index];
+#if USE_CHUNK_RWLOCK
+ DestructibleStructure::Chunk& chunk = mStructure->chunks[chunkIndexInAsset + mFirstChunkIndex];
+#endif
+ for (uint32_t hullIndex = mAsset->getChunkHullIndexStart(chunkIndexInAsset); hullIndex < mAsset->getChunkHullIndexStop(chunkIndexInAsset); ++hullIndex)
+ {
+ ConvexHullImpl& chunkSourceConvexHull = mAsset->chunkConvexHulls[hullIndex];
+ float in = minRayT;
+ float out = maxRayT;
+ PxVec3 n;
+ PxVec3 pxWorldBoxAxes[3];
+ pxWorldBoxAxes[0] = worldBoxAxes[0];
+ pxWorldBoxAxes[1] = worldBoxAxes[1];
+ pxWorldBoxAxes[2] = worldBoxAxes[2];
+ const bool hit = pointSweep ? (/* overlapLineSegmentAABBCached(rayorig, segmentCache, chunkSource.bounds) && */
+ chunkSourceConvexHull.rayCast(in, out, rayorig, raydir, chunkTMs[indexedChunkMin.index], getScale(), &n)) :
+ chunkSourceConvexHull.obbSweep(in, out, worldBoxCenter, worldBoxExtents, pxWorldBoxAxes, pxWorldDisp, chunkTMs[indexedChunkMin.index], getScale(), &n);
+ if (hit)
+ {
+ if (out > minRayT && in < minTime)
+ {
+ minTime = in;
+ normal = n;
+ parentChunkIndex = chunkIndexInAsset != (uint16_t)DestructibleAssetImpl::InvalidChunkIndex ? chunkIndexInAsset : (uint16_t)ModuleDestructibleConst::INVALID_CHUNK_INDEX;
+ }
+ }
+ }
+#if USE_CHUNK_RWLOCK
+ chunk.lock->unlockReader(); // Releasing lock from end of previous loop
+#endif
+ }
+
+#if USE_CHUNK_RWLOCK
+ // Release remaining locks
+ for (; candidateChunkNum < candidateChunkCount; ++candidateChunkNum)
+ {
+ IndexedReal& indexedChunkMin = chunkProjectedMinima[candidateChunkNum];
+ uint16_t chunkIndexInAsset = (uint16_t)chunkIndices[indexedChunkMin.index];
+ DestructibleStructure::Chunk& chunk = mStructure->chunks[chunkIndexInAsset + mFirstChunkIndex];
+ chunk.lock->unlockReader();
+ }
+#endif
+ }
+
+#if USE_DESTRUCTIBLE_RWLOCK
+ mLock->unlockReader();
+#endif
+
+ if (minTime != PX_MAX_F32)
+ {
+ time = minTime;
+ }
+
+ bool accurateRaycasts = (mDestructibleParameters.flags & DestructibleParametersFlag::ACCURATE_RAYCASTS) != 0;
+
+ if (((uint32_t)flags)&DestructibleActorRaycastFlags::ForceAccurateRaycastsOn)
+ {
+ accurateRaycasts = true;
+ }
+
+ if (((uint32_t)flags)&DestructibleActorRaycastFlags::ForceAccurateRaycastsOff)
+ {
+ accurateRaycasts = false;
+ }
+
+ if (!accurateRaycasts)
+ {
+ return parentChunkIndex;
+ }
+
+ int32_t chunkIndex = parentChunkIndex;
+
+ {
+ PX_PROFILE_ZONE("DestructibleRayCastFindDeepestChunk", GetInternalApexSDK()->getContextId());
+
+ SCOPED_PHYSX_LOCK_READ(mDestructibleScene->mApexScene);
+
+#if USE_DESTRUCTIBLE_RWLOCK
+ mLock->lockReader();
+#endif
+
+ while (parentChunkIndex != ModuleDestructibleConst::INVALID_CHUNK_INDEX)
+ {
+ DestructibleAssetParametersNS::Chunk_Type& source = mAsset->mParams->chunks.buf[parentChunkIndex];
+ float firstInTime = PX_MAX_F32;
+ parentChunkIndex = ModuleDestructibleConst::INVALID_CHUNK_INDEX;
+ const uint16_t childStop = source.numChildren;
+ if (childStop > 0 && mDestructibleParameters.depthParameters[source.depth + 1].ignoresRaycastCallbacks())
+ {
+ // Children ignore raycasts. We may stop here.
+ break;
+ }
+ for (uint16_t childNum = 0; childNum < childStop; ++childNum)
+ {
+ const uint16_t childIndexInAsset = uint16_t(source.firstChildIndex + childNum);
+ DestructibleStructure::Chunk& child = mStructure->chunks[childIndexInAsset + mFirstChunkIndex];
+#if USE_CHUNK_RWLOCK
+ DestructibleStructure::ChunkScopedReadLock chunkReadLock(child);
+#endif
+ if (!child.isDestroyed())
+ {
+ for (uint32_t hullIndex = mAsset->getChunkHullIndexStart(childIndexInAsset); hullIndex < mAsset->getChunkHullIndexStop(childIndexInAsset); ++hullIndex)
+ {
+ ConvexHullImpl& childSourceConvexHull = mAsset->chunkConvexHulls[hullIndex];
+ float in = minRayT;
+ float out = maxRayT;
+ PxVec3 n;
+ PxVec3 pxWorldBoxAxes[3];
+ pxWorldBoxAxes[0] = worldBoxAxes[0];
+ pxWorldBoxAxes[1] = worldBoxAxes[1];
+ pxWorldBoxAxes[2] = worldBoxAxes[2];
+ const bool hit = pointSweep ? (/* overlapLineSegmentAABBCached(rayorig, segmentCache, childSource.bounds) && */
+ childSourceConvexHull.rayCast(in, out, rayorig, raydir, mStructure->getChunkGlobalPose(child), getScale(), &n)) :
+ childSourceConvexHull.obbSweep(in, out, worldBoxCenter, worldBoxExtents, pxWorldBoxAxes, pxWorldDisp, mStructure->getChunkGlobalPose(child), getScale(), &n);
+ if (hit)
+ {
+ if (out > minRayT && in < firstInTime)
+ {
+ firstInTime = in;
+ normal = n;
+ parentChunkIndex = childIndexInAsset != (int32_t)DestructibleAssetImpl::InvalidChunkIndex ? childIndexInAsset : (int32_t)ModuleDestructibleConst::INVALID_CHUNK_INDEX;
+ if (child.state & ChunkVisible)
+ {
+ chunkIndex = parentChunkIndex;
+ }
+ }
+ }
+ }
+ }
+ }
+ if (firstInTime != PX_MAX_F32)
+ {
+ time = firstInTime;
+ }
+ }
+
+ }
+
+#if USE_DESTRUCTIBLE_RWLOCK
+ mLock->unlockReader();
+#endif
+
+ return chunkIndex;
+}
+
+int32_t DestructibleActorImpl::pointOrOBBSweepStatic(float& time, PxVec3& normal, const PxVec3& worldBoxCenter, const PxVec3& worldBoxExtents, const PxMat33& worldBoxRT, const PxVec3& pxWorldDisp,
+ DestructibleActorRaycastFlags::Enum flags, int32_t parentChunkIndex) const
+{
+ PX_PROFILE_ZONE("DestructibleActorPointOrOBBSweepStatic", GetInternalApexSDK()->getContextId());
+
+ PX_ASSERT((flags & DestructibleActorRaycastFlags::DynamicChunks) == 0);
+
+ // use the scene lock to protect chunk data for
+ // multi-threaded obbSweep calls on destructible actors
+ // (different lock would be good, but mixing locks can cause deadlocks)
+ SCOPED_PHYSX_LOCK_READ(mDestructibleScene->mApexScene);
+
+ // parentChunkIndex out of range
+ if (parentChunkIndex >= mAsset->mParams->chunks.arraySizes[0])
+ {
+ return ModuleDestructibleConst::INVALID_CHUNK_INDEX;
+ }
+
+ DestructibleAssetParametersNS::Chunk_Type* sourceChunks = mAsset->mParams->chunks.buf;
+
+ // parentChunkIndex is valid, but the chunk is invisible, beyond the LOD, or ignores raycasts
+ if (parentChunkIndex >= 0)
+ {
+ if ((getStructure()->chunks[parentChunkIndex + getFirstChunkIndex()].state & ChunkVisible) == 0)
+ {
+ return ModuleDestructibleConst::INVALID_CHUNK_INDEX;
+ }
+ }
+ else
+ {
+ // From now on, we treat parentChunkIndex < 0 as parentChunkIndex = 0 and iterate
+ parentChunkIndex = 0;
+ }
+
+ if (sourceChunks[parentChunkIndex].depth > (uint16_t)getLOD() || mDestructibleParameters.depthParameters[sourceChunks[parentChunkIndex].depth].ignoresRaycastCallbacks())
+ {
+ return ModuleDestructibleConst::INVALID_CHUNK_INDEX;
+ }
+
+ // TODO: the normal is not always output, for instance, when accurateRaycasts is false, can we generate a decent normal in this case?
+ const bool pointSweep = (worldBoxExtents.magnitudeSquared() == 0.0f);
+
+ const PxVec3 worldBoxAxes[3] = { worldBoxRT.column0, worldBoxRT.column1, worldBoxRT.column2 };
+ const PxVec3 worldDisp = pxWorldDisp;
+
+ float minRayT;
+ float maxRayT;
+ if ((flags & DestructibleActorRaycastFlags::SegmentIntersect) != 0)
+ {
+ minRayT = 0.0f;
+ maxRayT = 1.0f;
+ }
+ else
+ {
+ minRayT = -PX_MAX_F32;
+ maxRayT = PX_MAX_F32;
+ }
+
+ PxVec3 rayorig;
+ PxVec3 raydir;
+ if (pointSweep)
+ {
+ rayorig = worldBoxCenter;
+ raydir = worldDisp;
+ }
+
+ // Must find an intersecting visible chunk.
+
+ PxMat44 staticTM;
+ bool success = getGlobalPoseForStaticChunks(staticTM);
+ if (!success)
+ {
+ return ModuleDestructibleConst::INVALID_CHUNK_INDEX;
+ }
+ PxTransform chunkTM(staticTM); // We'll keep updating the position for this one
+
+ bool accurateRaycasts = (mDestructibleParameters.flags & DestructibleParametersFlag::ACCURATE_RAYCASTS) != 0;
+ if (((uint32_t)flags)&DestructibleActorRaycastFlags::ForceAccurateRaycastsOn)
+ {
+ accurateRaycasts = true;
+ }
+ if (((uint32_t)flags)&DestructibleActorRaycastFlags::ForceAccurateRaycastsOff)
+ {
+ accurateRaycasts = false;
+ }
+
+ int32_t chunkFoundIndex = ModuleDestructibleConst::INVALID_CHUNK_INDEX;
+ float minTime = PX_MAX_F32;
+ PxVec3 normalAtFirstHit(0.0f, 0.0f, 1.0f);
+
+ // Start with the parentChunkIndex. This stack is a stack of chunk ranges, stored LSW = low, MSW = high
+ physx::Array<uint32_t> stack;
+ stack.pushBack(((uint32_t)parentChunkIndex+1)<<16 | (uint32_t)parentChunkIndex);
+
+ while (stack.size() > 0)
+ {
+ const uint32_t range = stack.popBack();
+ uint32_t chunkIndex = (range&0xFFFF) + getFirstChunkIndex();
+ uint32_t chunkIndexStop = (range>>16) + getFirstChunkIndex();
+ const uint16_t depth = sourceChunks[range&0xFFFF].depth;
+ const bool atLimit = depth >= getLOD() || depth >= mAsset->mParams->depthCount - 1 || mDestructibleParameters.depthParameters[depth+1].ignoresRaycastCallbacks();
+
+ for (; chunkIndex < chunkIndexStop; ++chunkIndex)
+ {
+ const DestructibleStructure::Chunk& chunk = getStructure()->chunks[chunkIndex];
+ if ((chunk.state & ChunkDynamic) != 0)
+ {
+ continue;
+ }
+ chunkTM.p = staticTM.transform(chunk.localOffset);
+ const uint16_t chunkIndexInAsset = (uint16_t)(chunkIndex - getFirstChunkIndex());
+ for (uint32_t hullIndex = mAsset->getChunkHullIndexStart(chunkIndexInAsset); hullIndex < mAsset->getChunkHullIndexStop(chunkIndexInAsset); ++hullIndex)
+ {
+ ConvexHullImpl& chunkSourceConvexHull = mAsset->chunkConvexHulls[hullIndex];
+ float in = minRayT;
+ float out = maxRayT;
+ PxVec3 n;
+ bool hit;
+ if (pointSweep)
+ {
+ hit = chunkSourceConvexHull.rayCast(in, out, rayorig, raydir, chunkTM, getScale(), &n);
+ }
+ else
+ {
+ PxVec3 pxWorldBoxAxes[3];
+ pxWorldBoxAxes[0] = worldBoxAxes[0];
+ pxWorldBoxAxes[1] = worldBoxAxes[1];
+ pxWorldBoxAxes[2] = worldBoxAxes[2];
+ hit = chunkSourceConvexHull.obbSweep(in, out, worldBoxCenter, worldBoxExtents, pxWorldBoxAxes, pxWorldDisp, chunkTM, getScale(), &n);
+ }
+
+ if (hit)
+ {
+ if (out > 0.0f && in < minTime)
+ {
+ const uint32_t firstChildIndexInAsset = (uint32_t)sourceChunks[chunkIndexInAsset].firstChildIndex;
+ const uint32_t childCount = (uint32_t)sourceChunks[chunkIndexInAsset].numChildren;
+ const bool hasChildren = !atLimit && childCount > 0;
+
+ if (!accurateRaycasts)
+ {
+ if (chunk.state & ChunkVisible)
+ {
+ minTime = in;
+ normalAtFirstHit = n;
+ chunkFoundIndex = (int32_t)chunkIndex;
+ }
+ else
+ if (chunk.isDestroyed() && hasChildren)
+ {
+ stack.pushBack(((uint32_t)firstChildIndexInAsset+childCount)<<16 | (uint32_t)firstChildIndexInAsset);
+ }
+ }
+ else
+ {
+ if (!hasChildren)
+ {
+ minTime = in;
+ normalAtFirstHit = n;
+ chunkFoundIndex = (int32_t)chunkIndex;
+ }
+ else
+ {
+ stack.pushBack(((uint32_t)firstChildIndexInAsset+childCount)<<16 | (uint32_t)firstChildIndexInAsset);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (chunkFoundIndex == ModuleDestructibleConst::INVALID_CHUNK_INDEX || minTime == PX_MAX_F32)
+ {
+ return ModuleDestructibleConst::INVALID_CHUNK_INDEX;
+ }
+
+ time = minTime;
+ normal = normalAtFirstHit;
+
+ if (accurateRaycasts)
+ {
+ const DestructibleStructure::Chunk& chunkFound = getStructure()->chunks[(uint32_t)chunkFoundIndex];
+ if ((chunkFound.state & ChunkVisible) == 0)
+ {
+ chunkFoundIndex = chunkFound.visibleAncestorIndex;
+ }
+ }
+
+ return (int32_t)(chunkFoundIndex - getFirstChunkIndex());
+}
+
+void DestructibleActorImpl::applyDamage_immediate(DamageEvent& damageEvent)
+{
+ PX_PROFILE_ZONE("DestructibleApplyDamage_immediate", GetInternalApexSDK()->getContextId());
+
+ SCOPED_PHYSX_LOCK_READ(mDestructibleScene->mApexScene);
+
+ const float paddingFactor = 0.01f; // To do - expose ? This is necessary now that we're using exact bounds testing.
+ const float padding = paddingFactor * (getOriginalBounds().maximum - getOriginalBounds().minimum).magnitude();
+
+ const uint32_t rayCastFlags = mDestructibleScene->m_damageApplicationRaycastFlags & DestructibleActorRaycastFlags::AllChunks; // Mask against chunk flags
+
+ float time = 0;
+ PxVec3 fractureNormal(0.0f, 0.0f, 1.0f);
+
+ int32_t chunkIndexInAsset = damageEvent.chunkIndexInAsset >= 0 ? damageEvent.chunkIndexInAsset : ModuleDestructibleConst::INVALID_CHUNK_INDEX;
+
+ if (rayCastFlags != 0)
+ {
+ const PxVec3 worldRayOrig = damageEvent.position.isFinite() ? damageEvent.position : PxVec3(0.0f);
+ const PxVec3 worldRayDir = damageEvent.direction.isFinite() && !damageEvent.direction.isZero() ? damageEvent.direction : PxVec3(0.0f, 0.0f, 1.0f);
+ // TODO, even the direction isn't always normalized - PxVec3 fractureNormal(-worldRay.dir);
+ const int32_t actualHitChunk = rayCast(time, fractureNormal, worldRayOrig, worldRayDir, (DestructibleActorRaycastFlags::Enum)rayCastFlags, chunkIndexInAsset);
+ if (actualHitChunk != ModuleDestructibleConst::INVALID_CHUNK_INDEX)
+ {
+ chunkIndexInAsset = actualHitChunk;
+ }
+ }
+ else
+ if (mDestructibleParameters.fractureImpulseScale != 0.0f)
+ {
+ // Unfortunately, we need to do *some* kind of bounds check to get a good outward-pointing normal, if mDestructibleParameters.fractureImpulseScale != 0
+ // We'll make this as inexpensive as possible, and raycast against the depth 0 chunk
+ PxTransform tm(getInitialGlobalPose());
+ const PxVec3 pos = damageEvent.position.isFinite() ? damageEvent.position : PxVec3(0.0f);
+ const PxVec3 dir = damageEvent.direction.isFinite() ? damageEvent.direction : PxVec3(0.0f, 0.0f, 1.0f);
+ for (uint32_t hullIndex = mAsset->getChunkHullIndexStart(0); hullIndex < mAsset->getChunkHullIndexStop(0); ++hullIndex)
+ {
+ ConvexHullImpl& chunkSourceConvexHull = mAsset->chunkConvexHulls[hullIndex];
+ float in = -PX_MAX_F32;
+ float out = PX_MAX_F32;
+ float minTime = PX_MAX_F32;
+ PxVec3 n;
+ if (chunkSourceConvexHull.rayCast(in, out, pos, dir, tm, getScale(), &n))
+ {
+ if (in < minTime)
+ {
+ minTime = in;
+ fractureNormal = n;
+ }
+ }
+ }
+ }
+
+ PX_ASSERT(fractureNormal.isNormalized());
+
+ if (chunkIndexInAsset == ModuleDestructibleConst::INVALID_CHUNK_INDEX)
+ {
+ PX_ASSERT(0 == (DamageEvent::UseRadius & damageEvent.flags));
+ damageEvent.flags |= DamageEvent::Invalid;
+ return;
+ }
+
+ damageEvent.chunkIndexInAsset = chunkIndexInAsset;
+ damageEvent.position += damageEvent.direction * time;
+
+ float damage = damageEvent.damage;
+ if (mDestructibleParameters.damageCap > 0)
+ {
+ damage = PxMin(damage, mDestructibleParameters.damageCap);
+ }
+
+ DestructibleStructure::Chunk& chunk = mStructure->chunks[damageEvent.chunkIndexInAsset + mFirstChunkIndex];
+ if (!(chunk.state & ChunkVisible))
+ {
+ return;
+ }
+
+ // For probabilistic chunk deletion
+ uint32_t possibleDeleteChunks = 0;
+ float totalDeleteChunkRelativeDamage = 0.0f;
+
+ uint32_t totalFractureCount = 0;
+ PxRigidDynamic& actor = *mStructure->getChunkActor(chunk);
+ for (uint32_t i = 0; i < actor.getNbShapes(); ++i)
+ {
+ PxShape* shape;
+ actor.getShapes(&shape, 1, i);
+ DestructibleStructure::Chunk* chunk = mStructure->dscene->getChunk(shape);
+ if (chunk != NULL && chunk->isFirstShape(shape)) // BRG OPTIMIZE
+ {
+ PX_ASSERT(mStructure->dscene->mDestructibles.direct(chunk->destructibleID)->mStructure == mStructure);
+ if ((chunk->state & ChunkVisible) != 0)
+ {
+ // Damage coloring:
+ DestructibleActorImpl* destructible = mStructure->dscene->mDestructibles.direct(chunk->destructibleID);
+ if (destructible->useDamageColoring())
+ {
+ if (destructible->applyDamageColoring(chunk->indexInAsset, damageEvent.position, damage, 0.0f))
+ {
+ destructible->collectDamageColoring(chunk->indexInAsset, damageEvent.position, damage, 0.0f);
+ }
+ }
+
+ // specify the destructible asset because we're in a structure, it may not be this actor's asset
+ DestructibleAssetParametersNS::Chunk_Type& source = destructible->getDestructibleAsset()->mParams->chunks.buf[chunk->indexInAsset];
+
+ const uint16_t stopDepth = destructible->getParams()->destructibleParameters.damageDepthLimit < uint16_t(UINT16_MAX - source.depth) ?
+ uint16_t(destructible->getParams()->destructibleParameters.damageDepthLimit + source.depth) : static_cast<uint16_t>(UINT16_MAX);
+
+ totalFractureCount += mStructure->damageChunk(*chunk, damageEvent.position, damageEvent.direction, damageEvent.isFromImpact(), damage, 0.0f,
+ damageEvent.fractures, possibleDeleteChunks, totalDeleteChunkRelativeDamage, damageEvent.maxDepth, (uint32_t)source.depth, stopDepth, padding);
+ }
+ }
+ }
+
+ // For probabilistic chunk deletion
+ float deletionFactor = getDestructibleParameters().debrisDestructionProbability;
+ if (totalDeleteChunkRelativeDamage > 0.0f)
+ {
+ deletionFactor *= (float)possibleDeleteChunks/totalDeleteChunkRelativeDamage;
+ }
+
+ for (uint32_t depth = 0; depth <= damageEvent.maxDepth; ++depth)
+ {
+ physx::Array<FractureEvent>& fractureEventBuffer = damageEvent.fractures[depth];
+ for (uint32_t i = 0; i < fractureEventBuffer.size(); ++i)
+ {
+ FractureEvent& fractureEvent = fractureEventBuffer[i];
+ DestructibleActorImpl* destructible = mStructure->dscene->mDestructibles.direct(fractureEvent.destructibleID);
+ uint32_t affectedIndex = fractureEvent.chunkIndexInAsset + destructible->mFirstChunkIndex;
+ DestructibleStructure::Chunk& affectedChunk = mStructure->chunks[affectedIndex];
+ if (!affectedChunk.isDestroyed())
+ {
+ const float deletionProbability = deletionFactor*fractureEvent.deletionWeight;
+ if (deletionProbability > 0.0f && mStructure->dscene->mModule->mRandom.getUnit() < deletionProbability)
+ {
+ fractureEvent.flags |= FractureEvent::CrumbleChunk;
+ }
+
+ // Compute impulse
+ fractureEvent.impulse = mStructure->getChunkWorldCentroid(affectedChunk) - damageEvent.position;
+ fractureEvent.impulse.normalize();
+ fractureEvent.impulse *= fractureEvent.damageFraction * damageEvent.momentum;
+ fractureEvent.impulse += fractureNormal * mDestructibleParameters.fractureImpulseScale;
+ fractureEvent.position = damageEvent.position;
+ if (damageEvent.isFromImpact())
+ {
+ fractureEvent.flags |= FractureEvent::DamageFromImpact;
+ }
+ if (0 != (DamageEvent::SyncDirect & damageEvent.flags))
+ {
+ PX_ASSERT(0 == (FractureEvent::SyncDerived & fractureEvent.flags));
+ fractureEvent.flags |= FractureEvent::SyncDerived;
+ }
+ }
+ }
+ }
+}
+
+void DestructibleActorImpl::applyRadiusDamage_immediate(DamageEvent& damageEvent)
+{
+ PX_PROFILE_ZONE("DestructibleApplyRadiusDamage_immediate", GetInternalApexSDK()->getContextId());
+
+ SCOPED_PHYSX_LOCK_READ(mDestructibleScene->mApexScene);
+
+ const float damageCap = mDestructibleParameters.damageCap > 0 ? mDestructibleParameters.damageCap : PX_MAX_F32;
+
+ uint32_t totalFractureCount = 0;
+
+ const float paddingFactor = 0.01f; // To do - expose ? This is necessary now that we're using exact bounds testing.
+ const float padding = paddingFactor * (getOriginalBounds().maximum - getOriginalBounds().minimum).magnitude();
+
+ // For probabilistic chunk deletion
+ uint32_t possibleDeleteChunks = 0;
+ float totalDeleteChunkRelativeDamage = 0.0f;
+
+ const bool useLegacyDamageRadiusSpread = getUseLegacyDamageRadiusSpread();
+
+ // Should use scene query here
+ const uint16_t* chunkIndexPtr = mVisibleChunks.usedIndices();
+ const uint16_t* chunkIndexPtrStop = chunkIndexPtr + mVisibleChunks.usedCount();
+ bool needSaveDamageColor = false;
+ while (chunkIndexPtr < chunkIndexPtrStop)
+ {
+ uint16_t chunkIndex = *chunkIndexPtr++;
+ DestructibleStructure::Chunk& chunk = mStructure->chunks[chunkIndex + mFirstChunkIndex];
+
+ // Legacy behavior
+ float minRadius = 0.0f;
+ float maxRadius = damageEvent.radius;
+ float falloff = 1.0f;
+ const DestructibleActorParamNS::BehaviorGroup_Type& behaviorGroup = getBehaviorGroup(chunkIndex);
+ if (!useLegacyDamageRadiusSpread)
+ {
+ // New behavior
+ minRadius = behaviorGroup.damageSpread.minimumRadius;
+ maxRadius = minRadius + damageEvent.radius*behaviorGroup.damageSpread.radiusMultiplier;
+ falloff = behaviorGroup.damageSpread.falloffExponent;
+ }
+
+ // Damage coloring:
+ if (useDamageColoring())
+ {
+ if (applyDamageColoring(chunk.indexInAsset, damageEvent.position, damageEvent.damage, damageEvent.radius))
+ {
+ needSaveDamageColor = true;
+ }
+ }
+
+ const PxVec3 chunkCentroid = mStructure->getChunkWorldCentroid(chunk);
+ PxVec3 dir = chunkCentroid - damageEvent.position;
+ float dist = dir.normalize() - chunk.localSphereRadius;
+ if (dist < maxRadius)
+ {
+ float damageFraction = 1;
+ if (useLegacyDamageRadiusSpread)
+ {
+ dist = PxMax(dist, 0.0f);
+ if (falloff && damageEvent.radius > 0.0f)
+ {
+ damageFraction -= dist / damageEvent.radius;
+ }
+ }
+ const float effectiveDamage = PxMin(damageEvent.damage * damageFraction, damageCap);
+ DestructibleAssetParametersNS::Chunk_Type& source = mAsset->mParams->chunks.buf[chunk.indexInAsset];
+
+ const uint16_t stopDepth = getParams()->destructibleParameters.damageDepthLimit < uint16_t(UINT16_MAX - source.depth) ?
+ uint16_t(getParams()->destructibleParameters.damageDepthLimit + source.depth) : uint16_t(UINT16_MAX);
+
+ totalFractureCount += mStructure->damageChunk(chunk, damageEvent.position, damageEvent.direction, damageEvent.isFromImpact(), effectiveDamage, damageEvent.radius,
+ damageEvent.fractures, possibleDeleteChunks, totalDeleteChunkRelativeDamage, damageEvent.maxDepth, (uint32_t)source.depth, stopDepth, padding);
+ }
+ }
+
+ if (needSaveDamageColor)
+ {
+ collectDamageColoring(ModuleDestructibleConst::INVALID_CHUNK_INDEX, damageEvent.position, damageEvent.damage, damageEvent.radius);
+ }
+
+ // For probabilistic chunk deletion
+ float deletionFactor = getDestructibleParameters().debrisDestructionProbability;
+ if (totalDeleteChunkRelativeDamage > 0.0f)
+ {
+ deletionFactor *= (float)possibleDeleteChunks/totalDeleteChunkRelativeDamage;
+ }
+
+ for (uint32_t depth = 0; depth <= damageEvent.maxDepth; ++depth)
+ {
+ physx::Array<FractureEvent>& fractureEventBuffer = damageEvent.fractures[depth];
+ for (uint32_t i = 0; i < fractureEventBuffer.size(); ++i)
+ {
+ FractureEvent& fractureEvent = fractureEventBuffer[i];
+ DestructibleActorImpl* destructible = mStructure->dscene->mDestructibles.direct(fractureEvent.destructibleID);
+ uint32_t affectedIndex = fractureEvent.chunkIndexInAsset + destructible->mFirstChunkIndex;
+ DestructibleStructure::Chunk& affectedChunk = mStructure->chunks[affectedIndex];
+ if (!affectedChunk.isDestroyed())
+ {
+ const float deletionProbability = deletionFactor*fractureEvent.deletionWeight;
+ if (deletionProbability > 0.0f && mStructure->dscene->mModule->mRandom.getUnit() < deletionProbability)
+ {
+ fractureEvent.flags |= FractureEvent::CrumbleChunk;
+ }
+
+ fractureEvent.impulse = mStructure->getChunkWorldCentroid(affectedChunk) - damageEvent.position;
+ fractureEvent.impulse.normalize();
+ fractureEvent.impulse *= fractureEvent.damageFraction * damageEvent.momentum;
+
+ // Get outward normal for fractureImpulseScale, if this chunk is part of a larger island
+ PxVec3 fractureNormal(0.0f);
+ if (!mStructure->chunkIsSolitary(affectedChunk))
+ {
+ PxRigidDynamic& chunkActor = *mStructure->getChunkActor(affectedChunk);
+ // Search neighbors
+ DestructibleActorImpl* destructible = mStructure->dscene->mDestructibles.direct(affectedChunk.destructibleID);
+ const PxVec3 chunkPos = destructible->getChunkPose(affectedChunk.indexInAsset).p.multiply(destructible->getScale());
+ const uint32_t indexInStructure = affectedChunk.indexInAsset + destructible->getFirstChunkIndex();
+ for (uint32_t overlapN = mStructure->firstOverlapIndices[indexInStructure]; overlapN < mStructure->firstOverlapIndices[indexInStructure + 1]; ++overlapN)
+ {
+ DestructibleStructure::Chunk& overlapChunk = mStructure->chunks[mStructure->overlaps[overlapN]];
+ if (!overlapChunk.isDestroyed() && mStructure->getChunkActor(overlapChunk) == &chunkActor)
+ {
+ DestructibleActorImpl* overlapDestructible = mStructure->dscene->mDestructibles.direct(overlapChunk.destructibleID);
+ fractureNormal += chunkPos - overlapDestructible->getChunkPose(overlapChunk.indexInAsset).p.multiply(overlapDestructible->getScale());
+ }
+ }
+ if (fractureNormal.magnitudeSquared() != 0.0f)
+ {
+ fractureNormal.normalize();
+ fractureEvent.impulse += fractureNormal * mDestructibleParameters.fractureImpulseScale;
+ }
+ }
+
+ fractureEvent.position = damageEvent.position;
+ if (damageEvent.isFromImpact())
+ {
+ fractureEvent.flags |= FractureEvent::DamageFromImpact;
+ }
+ if (0 != (DamageEvent::SyncDirect & damageEvent.flags))
+ {
+ PX_ASSERT(0 == (FractureEvent::SyncDerived & fractureEvent.flags));
+ fractureEvent.flags |= FractureEvent::SyncDerived;
+ }
+ }
+ }
+ }
+}
+
+void DestructibleActorImpl::setSkinnedOverrideMaterial(uint32_t index, const char* overrideMaterialName)
+{
+ RenderMeshActor* rma = getRenderMeshActor(DestructibleActorMeshType::Skinned);
+ if (rma != NULL)
+ {
+ rma->setOverrideMaterial(index, overrideMaterialName);
+ }
+}
+
+void DestructibleActorImpl::setStaticOverrideMaterial(uint32_t index, const char* overrideMaterialName)
+{
+ RenderMeshActor* rma = getRenderMeshActor(DestructibleActorMeshType::Static);
+ if (rma != NULL)
+ {
+ rma->setOverrideMaterial(index, overrideMaterialName);
+ }
+}
+
+void DestructibleActorImpl::setRuntimeFracturePattern(const char* /*fracturePatternName*/)
+{
+ // TODO: Implement
+ /*
+ ResourceProviderIntl* nrp = GetInternalApexSDK()->getInternalResourceProvider();
+ if (nrp != NULL)
+ {
+ // do create before release, so we don't release the resource if the newID is the same as the old
+ ResID patternNS = GetInternalApexSDK()->getPatternNamespace();
+
+ ResID newID = nrp->createResource(patternNS, fracturePatternName);
+ nrp->releaseResource(mFracturePatternID);
+
+ mFracturePatternID = newID;
+ // TODO: Aquire resource for fracture pattern
+ }
+ */
+}
+
+void DestructibleActorImpl::updateRenderResources(bool rewriteBuffers, void* userRenderData)
+{
+
+ PX_PROFILE_ZONE("DestructibleUpdateRenderResources", GetInternalApexSDK()->getContextId());
+
+ //Should not be necessary anymore
+ //#if APEX_RUNTIME_FRACTURE
+ // {
+ // mRTActor->updateRenderResources(rewriteBuffers, userRenderData);
+ // }
+ //#endif
+
+ // Render non-instanced meshes if we own them
+ if (mRenderable != NULL && mRenderable->getReferenceCount() == 1)
+ {
+ mRenderable->updateRenderResources(rewriteBuffers, userRenderData);
+ }
+}
+
+void DestructibleActorImpl::dispatchRenderResources(UserRenderer& renderer)
+{
+ PX_PROFILE_ZONE("DestructibleDispatchRenderResources", GetInternalApexSDK()->getContextId());
+
+ // Dispatch non-instanced render resources if we own them
+ if (mRenderable != NULL && mRenderable->getReferenceCount() == 1)
+ {
+ mRenderable->dispatchRenderResources(renderer);
+ }
+
+ // Should not be necessary anymore
+ //#if APEX_RUNTIME_FRACTURE
+ // mRTActor->dispatchRenderResources(renderer);
+ //#endif
+}
+
+void DestructibleActorImpl::fillInstanceBuffers()
+{
+ if (mAsset->mParams->chunkInstanceInfo.arraySizes[0] == 0 && mAsset->mParams->scatterMeshIndices.arraySizes[0] == 0)
+ {
+ return; // No instancing for this asset
+ }
+
+ PX_PROFILE_ZONE("DestructibleActor::fillInstanceBuffers", GetInternalApexSDK()->getContextId());
+
+ mInstancedBounds.setEmpty();
+
+ DestructibleAssetParametersNS::Chunk_Type* sourceChunks = mAsset->mParams->chunks.buf;
+
+ Mutex::ScopedLock scopeLock(mAsset->m_chunkInstanceBufferDataLock);
+
+ const float scatterAlpha = PxClamp(2.0f-getLOD(), 0.0f, 1.0f);
+ const bool alwaysDrawScatterMesh = mAsset->getScatterMeshAlwaysDrawFlag();
+ // Iterate over all visible chunks
+ const uint16_t* indexPtr = mVisibleChunks.usedIndices();
+ const uint16_t* indexPtrStop = indexPtr + mVisibleChunks.usedCount();
+ DestructibleAssetParametersNS::InstanceInfo_Type* instanceDataArray = mAsset->mParams->chunkInstanceInfo.buf;
+ while (indexPtr < indexPtrStop)
+ {
+ const uint16_t index = *indexPtr++;
+ if (index < mAsset->getChunkCount())
+ {
+ DestructibleAssetParametersNS::Chunk_Type& sourceChunk = sourceChunks[index];
+ PxMat44 pose(getChunkPose(index));
+ const PxMat33 poseScaledRotation = PxMat33(getScale().x*pose.getBasis(0), getScale().y*pose.getBasis(1), getScale().z*pose.getBasis(2));
+
+ // Instanced chunks
+ if ((sourceChunk.flags & DestructibleAssetImpl::Instanced) != 0)
+ {
+ PX_ASSERT(sourceChunk.meshPartIndex < mAsset->mParams->chunkInstanceInfo.arraySizes[0]);
+ DestructibleAssetImpl::ChunkInstanceBufferDataElement instanceDataElement;
+ const DestructibleAssetParametersNS::InstanceInfo_Type& instanceData = instanceDataArray[sourceChunk.meshPartIndex];
+ const uint16_t instancedActorIndex = mAsset->m_instancedChunkActorMap[sourceChunk.meshPartIndex];
+ Array<DestructibleAssetImpl::ChunkInstanceBufferDataElement>& instanceBufferData = mAsset->m_chunkInstanceBufferData[instancedActorIndex];
+ instanceDataElement.translation = pose.getPosition();// + poseScaledRotation*instanceData.chunkPositionOffset;
+ instanceDataElement.scaledRotation = poseScaledRotation;
+ instanceDataElement.uvOffset = instanceData.chunkUVOffset;
+ instanceDataElement.localOffset = PxVec3(getScale().x*instanceData.chunkPositionOffset.x, getScale().y*instanceData.chunkPositionOffset.y, getScale().z*instanceData.chunkPositionOffset.z);
+
+ // there shouldn't be any allocation here because of the reserve in DestructibleAsset::resetInstanceData
+ PX_ASSERT(instanceBufferData.size() < instanceBufferData.capacity());
+ instanceBufferData.pushBack(instanceDataElement);
+
+ const PxBounds3& partBounds = mAsset->renderMeshAsset->getBounds(instanceData.partIndex);
+ // Transform bounds
+ PxVec3 center, extents;
+ center = partBounds.getCenter();
+ extents = partBounds.getExtents();
+ center = poseScaledRotation.transform(center) + instanceDataElement.translation;
+ extents = PxVec3(PxAbs(poseScaledRotation(0, 0) * extents.x) + PxAbs(poseScaledRotation(0, 1) * extents.y) + PxAbs(poseScaledRotation(0, 2) * extents.z),
+ PxAbs(poseScaledRotation(1, 0) * extents.x) + PxAbs(poseScaledRotation(1, 1) * extents.y) + PxAbs(poseScaledRotation(1, 2) * extents.z),
+ PxAbs(poseScaledRotation(2, 0) * extents.x) + PxAbs(poseScaledRotation(2, 1) * extents.y) + PxAbs(poseScaledRotation(2, 2) * extents.z));
+ mInstancedBounds.include(PxBounds3::centerExtents(center, extents));
+ }
+
+ // Scatter meshes
+ if (scatterAlpha > 0.0f || (sourceChunk.depth == 1 || alwaysDrawScatterMesh))
+ {
+ const uint16_t scatterMeshStop = uint16_t(sourceChunk.firstScatterMesh + sourceChunk.scatterMeshCount);
+ for (uint16_t scatterMeshNum = sourceChunk.firstScatterMesh; scatterMeshNum < scatterMeshStop; ++scatterMeshNum)
+ {
+ const physx::PxU8 scatterMeshIndex = mAsset->mParams->scatterMeshIndices.buf[scatterMeshNum];
+ const PxMat44& scatterMeshTransform = mAsset->mParams->scatterMeshTransforms.buf[scatterMeshNum];
+ DestructibleAssetImpl::ScatterMeshInstanceInfo& scatterMeshInstanceInfo = mAsset->m_scatterMeshInstanceInfo[scatterMeshIndex];
+ Array<DestructibleAssetImpl::ScatterInstanceBufferDataElement>& instanceBufferData = scatterMeshInstanceInfo.m_instanceBufferData;
+ DestructibleAssetImpl::ScatterInstanceBufferDataElement instanceDataElement;
+ instanceDataElement.translation = poseScaledRotation*scatterMeshTransform.getPosition() + pose.getPosition();// + poseScaledRotation*instanceData.chunkPositionOffset;
+ PxMat33 smTm33(scatterMeshTransform.column0.getXYZ(),
+ scatterMeshTransform.column1.getXYZ(),
+ scatterMeshTransform.column2.getXYZ());
+ instanceDataElement.scaledRotation = poseScaledRotation*smTm33;
+ instanceDataElement.alpha = scatterAlpha;
+ instanceBufferData.pushBack(instanceDataElement);
+ // Not updating bounds for scatter meshes
+ }
+ }
+ }
+ }
+
+ mRenderBounds = mNonInstancedBounds;
+ mRenderBounds.include(mInstancedBounds);
+ if (mRenderable != NULL)
+ {
+ mRenderable->setBounds(mRenderBounds);
+ }
+}
+
+void DestructibleActorImpl::setRelativeTMs()
+{
+ mRelTM = NULL != mStructure ? mTM*mStructure->getActorForStaticChunksPose().inverseRT() : PxMat44(PxIdentity);
+}
+
+/* Called during ApexScene::fetchResults(), after PhysXScene::fetchResults(). Sends new bone poses
+ * to ApexRenderMeshActor, then retrieves new world AABB.
+ */
+
+void DestructibleActorImpl::setRenderTMs(bool processChunkPoseForSyncing /*= false*/)
+{
+ mNonInstancedBounds.setEmpty();
+
+ if (mRenderable)
+ {
+ mRenderable->lockRenderResources();
+ }
+
+ RenderMeshActor* skinnedRMA = getRenderMeshActor(DestructibleActorMeshType::Skinned);
+
+ const bool syncWriteTM = processChunkPoseForSyncing && mSyncParams.isSyncFlagSet(DestructibleActorSyncFlags::CopyChunkTransform) && (NULL != mSyncParams.getChunkSyncState());
+ const bool canSyncReadTM = processChunkPoseForSyncing && mSyncParams.isSyncFlagSet(DestructibleActorSyncFlags::ReadChunkTransform);
+
+ if (skinnedRMA != NULL || syncWriteTM || canSyncReadTM)
+ {
+ if (skinnedRMA != NULL)
+ {
+ skinnedRMA->syncVisibility(false); // Using mRenderable->mRenderable->lockRenderResources() instead
+ }
+ // PX_ASSERT(asset->getRenderMeshAsset()->getPartCount() == asset->getChunkCount());
+
+ DestructibleAssetParametersNS::Chunk_Type* sourceChunks = mAsset->mParams->chunks.buf;
+
+ // Iterate over all visible chunks
+ const uint16_t* indexPtr = mVisibleChunks.usedIndices();
+ const uint16_t* indexPtrStop = indexPtr + mVisibleChunks.usedCount();
+
+ // TODO: Here we update all chunks although we practically know the chunks (active transforms) that have moved. Improve. [APEX-670]
+
+ while (indexPtr < indexPtrStop)
+ {
+ const uint16_t index = *indexPtr++;
+ if (index < mAsset->getChunkCount())
+ {
+ DestructibleAssetParametersNS::Chunk_Type& sourceChunk = sourceChunks[index];
+ DestructibleStructure::Chunk & chunk = mStructure->chunks[mFirstChunkIndex + index];
+ if (!chunk.isDestroyed() && (sourceChunk.flags & DestructibleAssetImpl::Instanced) == 0)
+ {
+ const bool syncReadTM = canSyncReadTM && (NULL != chunk.controlledChunk);
+ if (syncReadTM && !getDynamic(index))
+ {
+ setDynamic(index, true);
+ }
+
+ if (getDynamic(index) || !drawStaticChunksInSeparateMesh() || initializedFromState())
+ {
+ PxTransform chunkPose;
+ bool isChunkPoseInit = false;
+ if (processChunkPoseForSyncing)
+ {
+ if (syncWriteTM)
+ {
+ const DestructibleChunkSyncState & chunkSyncState = *(mSyncParams.getChunkSyncState());
+ PX_ASSERT(NULL != &chunkSyncState);
+ if(!chunkSyncState.disableTransformBuffering &&
+ (chunkSyncState.excludeSleepingChunks ? !isPxActorSleeping(*(mStructure->getChunkActor(chunk))) : true) &&
+ (chunkSyncState.chunkTransformCopyDepth >= sourceChunk.depth))
+ {
+ SCOPED_PHYSX_LOCK_READ(mDestructibleScene->mApexScene);
+
+ const PxTransform calculatedChunkPose = getChunkPose(index);
+ mSyncParams.pushCachedChunkTransform(CachedChunk(index, calculatedChunkPose));
+ chunkPose = calculatedChunkPose;
+ isChunkPoseInit = true;
+ }
+ }
+ if (syncReadTM)
+ {
+ const PxTransform controlledChunkPose(chunk.controlledChunk->chunkPosition, chunk.controlledChunk->chunkOrientation);
+ {
+ SCOPED_PHYSX_LOCK_WRITE(getDestructibleScene()->getApexScene());
+ setChunkPose(index, controlledChunkPose);
+ }
+ chunk.controlledChunk = NULL;
+ chunkPose = controlledChunkPose;
+ isChunkPoseInit = true;
+ }
+ if (!isChunkPoseInit)
+ {
+ SCOPED_PHYSX_LOCK_READ(mDestructibleScene->mApexScene);
+ chunkPose = getChunkPose(index);
+ isChunkPoseInit = true;
+ }
+ }
+ else
+ {
+ SCOPED_PHYSX_LOCK_READ(mDestructibleScene->mApexScene);
+ chunkPose = getChunkPose(index);
+ isChunkPoseInit = true;
+ }
+ PX_ASSERT(isChunkPoseInit);
+ if (skinnedRMA != NULL)
+ {
+ skinnedRMA->setTM(chunkPose, getScale(), sourceChunk.meshPartIndex);
+ }
+ }
+ }
+ }
+ }
+
+
+ if (skinnedRMA != NULL)
+ {
+ skinnedRMA->updateBounds();
+ mNonInstancedBounds.include(skinnedRMA->getBounds());
+ }
+ }
+
+ RenderMeshActor* staticRMA = getRenderMeshActor(DestructibleActorMeshType::Static);
+
+ // If a static mesh exists, set its (single) tm from the destructible's tm
+ if (staticRMA != NULL)
+ {
+ staticRMA->syncVisibility(false); // Using mRenderable->mRenderable->lockRenderResources() instead
+ PxMat44 pose;
+ if (!getGlobalPoseForStaticChunks(pose))
+ {
+ pose = PxMat44(PxIdentity); // Should not be rendered, but just in case we'll set pose to something sane.
+ }
+ staticRMA->setTM(pose, getScale());
+ staticRMA->updateBounds();
+ mNonInstancedBounds.include(staticRMA->getBounds());
+ }
+
+#if APEX_RUNTIME_FRACTURE
+ if(mRenderable)
+ {
+ mRenderable->getRTrenderable().updateRenderCache(mRTActor);
+ }
+#endif
+
+ if (mNonInstancedBounds.isEmpty()) // This can occur if we have no renderable
+ {
+ const uint16_t* indexPtr = mVisibleChunks.usedIndices();
+ const uint16_t* indexPtrStop = indexPtr + mVisibleChunks.usedCount();
+ while (indexPtr < indexPtrStop)
+ {
+ const uint16_t index = *indexPtr++;
+ if (index < mAsset->getChunkCount())
+ {
+ const PxMat44 pose(getChunkPose(index));
+ for (uint32_t hullIndex = mAsset->getChunkHullIndexStart(index); hullIndex < mAsset->getChunkHullIndexStop(index); ++hullIndex)
+ {
+ const ConvexHullImpl& chunkSourceConvexHull = mAsset->chunkConvexHulls[hullIndex];
+ PxBounds3 chunkConvexHullBounds = chunkSourceConvexHull.getBounds();
+ // Apply scale
+ chunkConvexHullBounds.minimum = chunkConvexHullBounds.minimum.multiply(getScale());
+ chunkConvexHullBounds.maximum = chunkConvexHullBounds.maximum.multiply(getScale());
+ // Apply rotation and translation
+ const PxVec3 extent = chunkConvexHullBounds.getExtents();
+ const PxVec3 newExtent(
+ PxAbs(pose.column0.x*extent.x) + PxAbs(pose.column1.x*extent.y) + PxAbs(pose.column2.x*extent.z),
+ PxAbs(pose.column0.y*extent.x) + PxAbs(pose.column1.y*extent.y) + PxAbs(pose.column2.y*extent.z),
+ PxAbs(pose.column0.z*extent.x) + PxAbs(pose.column1.z*extent.y) + PxAbs(pose.column2.z*extent.z));
+ const PxVec3 center = pose.transform(chunkConvexHullBounds.getCenter());
+ mNonInstancedBounds.include(PxBounds3(center - newExtent, center + newExtent));
+ }
+ }
+ }
+ }
+
+ mRenderBounds = mNonInstancedBounds;
+ mRenderBounds.include(mInstancedBounds);
+ if (mRenderable != NULL)
+ {
+ mRenderable->unlockRenderResources();
+ mRenderable->setBounds(mRenderBounds);
+ }
+}
+
+void DestructibleActorImpl::setActorObjDescFlags(PhysXObjectDescIntl* actorObjDesc, uint32_t depth) const
+{
+ const DestructibleDepthParameters& depthParameters = mDestructibleParameters.depthParameters[depth];
+ actorObjDesc->setIgnoreTransform(depthParameters.ignoresPoseUpdates());
+ actorObjDesc->setIgnoreRaycasts(depthParameters.ignoresRaycastCallbacks());
+ actorObjDesc->setIgnoreContacts(depthParameters.ignoresContactCallbacks());
+ for (uint32_t i = PhysXActorFlags::DEPTH_PARAM_USER_FLAG_0; i <= PhysXActorFlags::DEPTH_PARAM_USER_FLAG_3; ++i)
+ {
+ actorObjDesc->setUserDefinedFlag(i, depthParameters.hasUserFlagSet(i));
+ }
+ actorObjDesc->setUserDefinedFlag(PhysXActorFlags::CREATED_THIS_FRAME, true);
+ actorObjDesc->setUserDefinedFlag(PhysXActorFlags::IS_SLEEPING, true);
+}
+
+void DestructibleActorImpl::setGlobalPose(const PxMat44& pose)
+{
+ if (!isChunkDestroyed(0) && getDynamic(0))
+ {
+ setChunkPose(0, PxTransform(pose));
+ }
+ else
+ {
+ setGlobalPoseForStaticChunks(pose);
+ }
+}
+
+void DestructibleActorImpl::setGlobalPoseForStaticChunks(const PxMat44& pose)
+{
+ if (mStructure != NULL && mStructure->actorForStaticChunks != NULL)
+ {
+ const PxMat44 actorForStaticChunkPose = mRelTM.inverseRT() * pose;
+ {
+ SCOPED_PHYSX_LOCK_WRITE(mStructure->actorForStaticChunks->getScene());
+ mStructure->actorForStaticChunks->setKinematicTarget(PxTransform(actorForStaticChunkPose));
+ }
+ for (uint32_t meshType = 0; meshType < DestructibleActorMeshType::Count; ++meshType)
+ {
+ RenderMeshActor* renderMeshActor = getRenderMeshActor((DestructibleActorMeshType::Enum)meshType);
+ if (renderMeshActor != NULL)
+ {
+ renderMeshActor->updateBounds();
+ }
+ }
+
+ }
+
+ wakeForEvent();
+}
+
+bool DestructibleActorImpl::getGlobalPoseForStaticChunks(PxMat44& pose) const
+{
+ if (mStructure != NULL && mStructure->actorForStaticChunks != NULL)
+ {
+ if (NULL != mStructure->actorForStaticChunks)
+ pose = mRelTM * mStructure->getActorForStaticChunksPose();
+ else
+ pose = mTM;
+ return true;
+ }
+
+ return false;
+}
+
+void DestructibleActorImpl::setChunkPose(uint32_t index, PxTransform worldPose)
+{
+ PX_ASSERT(mStructure != NULL);
+ PX_ASSERT(index + mFirstChunkIndex < mStructure->chunks.size());
+ PX_ASSERT(!mStructure->chunks[index + mFirstChunkIndex].isDestroyed());
+ DestructibleStructure::Chunk & chunk = mStructure->chunks[index + mFirstChunkIndex];
+ PX_ASSERT(chunk.state & ChunkDynamic);
+ mStructure->setChunkGlobalPose(chunk, worldPose);
+}
+
+void DestructibleActorImpl::setLinearVelocity(const PxVec3& linearVelocity)
+{
+ // Only dynamic actors need their velocity set, and they'll all be in the skinned mesh
+ const uint16_t* indexPtr = getVisibleChunks();
+ const uint16_t* indexPtrStop = indexPtr + getNumVisibleChunks();
+ while (indexPtr < indexPtrStop)
+ {
+ DestructibleStructure::Chunk& chunk = mStructure->chunks[mFirstChunkIndex + *indexPtr++];
+ if (chunk.state & ChunkDynamic)
+ {
+ PxRigidDynamic* actor = mStructure->getChunkActor(chunk);
+ if (actor != NULL)
+ {
+ SCOPED_PHYSX_LOCK_WRITE(actor->getScene());
+ actor->setLinearVelocity(linearVelocity);
+ }
+ }
+ }
+}
+
+void DestructibleActorImpl::setAngularVelocity(const PxVec3& angularVelocity)
+{
+ // Only dynamic actors need their velocity set, and they'll all be in the skinned mesh
+ const uint16_t* indexPtr = getVisibleChunks();
+ const uint16_t* indexPtrStop = indexPtr + getNumVisibleChunks();
+ while (indexPtr < indexPtrStop)
+ {
+ DestructibleStructure::Chunk& chunk = mStructure->chunks[mFirstChunkIndex + *indexPtr++];
+ if (chunk.state & ChunkDynamic)
+ {
+ PxRigidDynamic* actor = mStructure->getChunkActor(chunk);
+ if (actor != NULL)
+ {
+ SCOPED_PHYSX_LOCK_WRITE(actor->getScene());
+ actor->setAngularVelocity(angularVelocity);
+ }
+ }
+ }
+}
+
+void DestructibleActorImpl::enableHardSleeping()
+{
+ mParams->useHardSleeping = true;
+}
+
+void DestructibleActorImpl::disableHardSleeping(bool wake)
+{
+ if (!useHardSleeping())
+ {
+ return; // Nothing to do
+ }
+
+ if (mStructure == NULL || mStructure->dscene == NULL || mStructure->dscene->mModule == NULL)
+ {
+ return; // Can't do anything
+ }
+
+ mParams->useHardSleeping = false;
+
+ Bank<DormantActorEntry, uint32_t>& dormantActors = mStructure->dscene->mDormantActors;
+ for (uint32_t dormantActorRank = dormantActors.usedCount(); dormantActorRank--;)
+ {
+ const uint32_t dormantActorIndex = dormantActors.usedIndices()[dormantActorRank];
+ DormantActorEntry& dormantActorEntry = dormantActors.direct(dormantActorIndex);
+ // Look at every destructible actor which contributes to this physx actor, and see if any use hard sleeping
+ bool keepDormant = false;
+ PhysXObjectDescIntl* actorObjDesc = (PhysXObjectDescIntl*)mStructure->dscene->mModule->mSdk->getPhysXObjectInfo(dormantActorEntry.actor);
+ if (actorObjDesc != NULL)
+ {
+ for (uint32_t i = 0; i < actorObjDesc->mApexActors.size(); ++i)
+ {
+ const DestructibleActor* dActor = static_cast<const DestructibleActor*>(actorObjDesc->mApexActors[i]);
+ if (dActor != NULL)
+ {
+ if (actorObjDesc->mApexActors[i]->getOwner()->getObjTypeID() == DestructibleAssetImpl::getAssetTypeID())
+ {
+ const DestructibleActorImpl& destructibleActor = static_cast<const DestructibleActorProxy*>(dActor)->impl;
+ if (destructibleActor.useHardSleeping())
+ {
+ keepDormant = true;
+ break;
+ }
+ }
+ }
+ }
+ }
+ // If none use hard sleeping, we will remove the physx actor from the dormant list
+ if (!keepDormant)
+ {
+ PxRigidDynamic* actor = dormantActorEntry.actor;
+ dormantActorEntry.actor = NULL;
+ dormantActors.free(dormantActorIndex);
+ {
+ SCOPED_PHYSX_LOCK_WRITE(actor->getScene());
+ actor->setRigidBodyFlag(physx::PxRigidBodyFlag::eKINEMATIC, false);
+ }
+ if (actorObjDesc != NULL)
+ {
+ actorObjDesc->userData = NULL;
+ mStructure->dscene->addActor(*actorObjDesc, *actor, dormantActorEntry.unscaledMass,
+ ((dormantActorEntry.flags & ActorFIFOEntry::IsDebris) != 0));
+ }
+ // Wake if requested
+ if (wake)
+ {
+ SCOPED_PHYSX_LOCK_WRITE(actor->getScene());
+ actor->wakeUp();
+ }
+ }
+ }
+}
+
+bool DestructibleActorImpl::setChunkPhysXActorAwakeState(uint32_t chunkIndex, bool awake)
+{
+ PxRigidDynamic* actor = getChunkActor(chunkIndex);
+ if (actor == NULL)
+ {
+ return false;
+ }
+
+ PxScene* scene = actor->getScene();
+ if (scene == NULL)
+ {
+ // defer
+ if (mDestructibleScene != NULL)
+ {
+ mDestructibleScene->addForceToAddActorsMap(actor, ActorForceAtPosition(PxVec3(0.0f), PxVec3(0.0f), physx::PxForceMode::eFORCE, awake, false));
+ return true;
+ }
+ return false;
+ }
+
+ // Actor has a scene, set sleep state now
+ if (awake)
+ {
+ actor->wakeUp();
+ }
+ else
+ {
+ ((PxRigidDynamic*)actor)->putToSleep();
+ }
+
+ return true;
+}
+
+bool DestructibleActorImpl::addForce(uint32_t chunkIndex, const PxVec3& force, physx::PxForceMode::Enum mode, const PxVec3* position, bool wakeup)
+{
+ PxRigidDynamic* actor = getChunkActor(chunkIndex);
+ if (actor == NULL)
+ {
+ return false;
+ }
+
+ if (actor->getScene() == NULL)
+ {
+ // defer
+ if (mDestructibleScene != NULL)
+ {
+ if (position)
+ {
+ mDestructibleScene->addForceToAddActorsMap(actor, ActorForceAtPosition(force, *position, mode, wakeup, true));
+ }
+ else
+ {
+ mDestructibleScene->addForceToAddActorsMap(actor, ActorForceAtPosition(force, PxVec3(0.0f), mode, wakeup, false));
+ }
+ return true;
+ }
+ return false;
+ }
+
+ // Actor has a scene, add force now
+ PxRigidBody* rigidBody = actor->is<PxRigidBody>();
+ if (rigidBody)
+ {
+ if (position)
+ {
+ PxRigidBodyExt::addForceAtPos(*rigidBody, force, *position, mode, wakeup);
+ }
+ else
+ {
+ rigidBody->addForce(force, mode, wakeup);
+ }
+ }
+
+ return true;
+}
+
+void DestructibleActorImpl::getLodRange(float& min, float& max, bool& intOnly) const
+{
+ min = 0.0f;
+ max = (float)(PxMax(mAsset->getDepthCount(), (uint32_t)1) - 1);
+ intOnly = true;
+}
+
+float DestructibleActorImpl::getActiveLod() const
+{
+ return (float)getLOD();
+}
+
+void DestructibleActorImpl::forceLod(float lod)
+{
+ if (lod < 0.0f)
+ {
+ mState->lod = PxMax(mAsset->getDepthCount(), (uint32_t)1) - 1;
+ mState->forceLod = false;
+ }
+ else
+ {
+ float min, max;
+ bool intOnly;
+ getLodRange(min, max, intOnly);
+ mState->lod = (uint32_t)PxClamp(lod, min, max);
+ mState->forceLod = true;
+ }
+}
+
+void DestructibleActorImpl::setDynamic(int32_t chunkIndex, bool immediate)
+{
+ const uint16_t* indexPtr = NULL;
+ const uint16_t* indexPtrStop = NULL;
+ uint16_t index;
+ if (chunkIndex == ModuleDestructibleConst::INVALID_CHUNK_INDEX)
+ {
+ indexPtr = getVisibleChunks();
+ indexPtrStop = indexPtr + getNumVisibleChunks();
+ }
+ else
+ if (chunkIndex >= 0 && chunkIndex < mAsset->mParams->chunks.arraySizes[0])
+ {
+ index = (uint16_t)chunkIndex;
+ indexPtr = &index;
+ indexPtrStop = indexPtr+1;
+ }
+
+ while (indexPtr < indexPtrStop)
+ {
+ const uint16_t index = *indexPtr++;
+ DestructibleStructure::Chunk& chunk = mStructure->chunks[mFirstChunkIndex + index];
+ if ((chunk.state & ChunkDynamic)==0)
+ {
+ FractureEvent stackEvent;
+ FractureEvent& fractureEvent = immediate ? stackEvent : mStructure->dscene->mFractureBuffer.pushBack();
+ fractureEvent.chunkIndexInAsset = chunk.indexInAsset;
+ fractureEvent.destructibleID = mID;
+ fractureEvent.impulse = PxVec3(0.0f);
+ fractureEvent.position = PxVec3(0.0f);
+ fractureEvent.flags = FractureEvent::Forced | FractureEvent::Silent;
+ if (immediate)
+ {
+ getStructure()->fractureChunk(fractureEvent);
+ }
+ }
+ }
+}
+
+void DestructibleActorImpl::getChunkVisibilities(uint8_t* visibilityArray, uint32_t visibilityArraySize) const
+{
+ if (visibilityArray == NULL || visibilityArraySize == 0)
+ {
+ return;
+ }
+
+ memset(visibilityArray, 0, visibilityArraySize);
+
+ const uint32_t visiblePartCount = mVisibleChunks.usedCount();
+ const uint16_t* visiblePartIndices = mVisibleChunks.usedIndices();
+ for (uint32_t i = 0; i < visiblePartCount; ++i)
+ {
+ const uint32_t index = visiblePartIndices[i];
+ if (index < visibilityArraySize)
+ {
+ visibilityArray[index] = 1;
+ }
+ }
+}
+
+bool DestructibleActorImpl::acquireChunkEventBuffer(const DestructibleChunkEvent*& buffer, uint32_t& bufferSize)
+{
+ mChunkEventBufferLock.lock();
+
+ buffer = mChunkEventBuffer.begin();
+ bufferSize = mChunkEventBuffer.size();
+ return true;
+}
+
+bool DestructibleActorImpl::releaseChunkEventBuffer(bool clearBuffer /* = true */)
+{
+ if (clearBuffer)
+ {
+ mChunkEventBuffer.reset();
+ // potentially O(n), but is O(1) (empty list) if chunk event callbacks aren't enabled. The chunk event callbacks
+ // and aquireChunkEventBuffer/releaseChunkEventBuffer mechanisms probably won't be used together.
+ mDestructibleScene->mActorsWithChunkStateEvents.findAndReplaceWithLast(this);
+ }
+
+ mChunkEventBufferLock.unlock();
+ return true;
+}
+
+// PhysX actor buffer API
+bool DestructibleActorImpl::acquirePhysXActorBuffer(physx::PxRigidDynamic**& buffer, uint32_t& bufferSize, uint32_t flags)
+{
+ mPhysXActorBufferLock.lock();
+
+ mPhysXActorBufferAcquired = true;
+ // Clear buffer, just in case
+ mPhysXActorBuffer.reset();
+
+ const bool eliminateRedundantActors = (flags & DestructiblePhysXActorQueryFlags::AllowRedundancy) == 0;
+
+ const bool onlyAllowActorsInScene = (flags & DestructiblePhysXActorQueryFlags::AllowActorsNotInScenes) == 0;
+
+ SCOPED_PHYSX_LOCK_READ(mDestructibleScene->mPhysXScene);
+
+ // Fill the actor buffer
+ for (uint32_t actorNum = 0; actorNum < mReferencingActors.size(); ++actorNum)
+ {
+ PxRigidDynamic* actor = mReferencingActors[actorNum];
+ if (actor == NULL || actor->getNbShapes() == 0
+ // don't return actors that have not yet been added to the scene
+ // to prevent errors in actor functions that require a scene
+ || ((actor->getScene() == NULL) && onlyAllowActorsInScene)
+ )
+ {
+ continue;
+ }
+
+ PxShape* firstShape;
+ actor->getShapes(&firstShape, 1, 0);
+ PhysXObjectDescIntl* objDesc = mStructure->dscene->mModule->mSdk->getGenericPhysXObjectInfo(firstShape);
+ if (objDesc == NULL)
+ {
+ continue;
+ }
+ const DestructibleStructure::Chunk& chunk = *(DestructibleStructure::Chunk*)objDesc->userData;
+ const uint32_t actorState = !(actor->getRigidBodyFlags() & physx::PxRigidBodyFlag::eKINEMATIC) ? DestructiblePhysXActorQueryFlags::Dynamic :
+ ((chunk.state & ChunkDynamic) == 0 ? DestructiblePhysXActorQueryFlags::Static : DestructiblePhysXActorQueryFlags::Dormant);
+ if ((actorState & flags) == 0)
+ {
+ continue; // Not a type of actor requested
+ }
+ if (eliminateRedundantActors && chunk.destructibleID != mID)
+ {
+ continue; // If eliminateRedundantActors is set, only take actors whose first shape belongs to a chunk from this destructible
+ }
+ mPhysXActorBuffer.pushBack((physx::PxRigidDynamic*)actor);
+ }
+
+ // Return buffer
+ buffer = mPhysXActorBuffer.begin();
+ bufferSize = mPhysXActorBuffer.size();
+ return true;
+}
+
+bool DestructibleActorImpl::releasePhysXActorBuffer()
+{
+ if(mPhysXActorBufferAcquired)
+ {
+ mPhysXActorBuffer.reset();
+ mPhysXActorBufferAcquired = false;
+ mPhysXActorBufferLock.unlock();
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+bool DestructibleActorImpl::recreateApexEmitter(DestructibleEmitterType::Enum type)
+{
+ bool ret = false;
+
+ switch (type)
+ {
+ case DestructibleEmitterType::Crumble:
+ if (getCrumbleEmitterName())
+ {
+ ret = initCrumbleSystem(getCrumbleEmitterName());
+ }
+ break;
+ case DestructibleEmitterType::Dust:
+ if (getDustEmitterName())
+ {
+ ret = initDustSystem(getDustEmitterName());
+ }
+ break;
+ default:
+ break;
+ }
+ return ret;
+}
+
+bool DestructibleActorImpl::initDustSystem(const char* name)
+{
+#if APEX_USE_PARTICLES
+
+ if (mDustEmitter)
+ {
+ return true;
+ }
+
+ // Set up dust system
+ if (name)
+ {
+ setDustEmitterName(name);
+ /* This destructible actor will hold a reference to its MeshParticleFactoryAsset */
+ Asset* tmpAsset = mAsset->mDustAssetTracker.getAssetFromName(name);
+ EmitterAsset* fasset = static_cast<EmitterAsset*>(tmpAsset);
+ if (fasset)
+ {
+ NvParameterized::Interface* descParams = fasset->getDefaultActorDesc();
+ PX_ASSERT(descParams);
+ if (descParams)
+ {
+ mDustEmitter = static_cast<EmitterActor*>(fasset->createApexActor(*descParams, *mDestructibleScene->mApexScene));
+ if (mDustEmitter)
+ {
+ ApexActor* aa = mAsset->module->mSdk->getApexActor(mDustEmitter);
+ if (aa)
+ {
+ aa->addSelfToContext(*this);
+ }
+ if (!mDustEmitter->isExplicitGeom())
+ {
+ APEX_INTERNAL_ERROR("Destructible actors need EmitterGeomExplicit emitters.");
+ }
+ if (mDustRenderVolume)
+ {
+ mDustEmitter->setPreferredRenderVolume(mDustRenderVolume);
+ }
+ mDustEmitter->startEmit(true);
+ }
+ }
+ }
+ }
+ else
+ {
+ mState->enableDustEmitter = false;
+ }
+#else
+ PX_UNUSED(name);
+#endif // APEX_USE_PARTICLES
+
+ return mDustEmitter ? true : false;
+}
+
+bool DestructibleActorImpl::initCrumbleSystem(const char* name)
+{
+#if APEX_USE_PARTICLES
+ // Set up crumble system
+ if (mCrumbleEmitter)
+ {
+ return true;
+ }
+ if (name)
+ {
+ setCrumbleEmitterName(name);
+ Asset* tmpAsset = mAsset->mCrumbleAssetTracker.getAssetFromName(name);
+ EmitterAsset* fasset = static_cast<EmitterAsset*>(tmpAsset);
+ if (fasset)
+ {
+ NvParameterized::Interface* descParams = fasset->getDefaultActorDesc();
+ PX_ASSERT(descParams);
+ if (descParams)
+ {
+ mCrumbleEmitter = static_cast<EmitterActor*>(fasset->createApexActor(*descParams, *mDestructibleScene->mApexScene));
+ if (mCrumbleEmitter)
+ {
+ ApexActor* aa = mAsset->module->mSdk->getApexActor(mCrumbleEmitter);
+ if (aa)
+ {
+ aa->addSelfToContext(*this);
+ }
+ if (!mCrumbleEmitter->isExplicitGeom())
+ {
+ APEX_INTERNAL_ERROR("Destructible actors need EmitterGeomExplicit emitters.");
+ }
+ if (mCrumbleRenderVolume)
+ {
+ mCrumbleEmitter->setPreferredRenderVolume(mCrumbleRenderVolume);
+ }
+ mCrumbleEmitter->startEmit(true);
+ }
+ }
+ }
+ }
+ else
+ {
+ mState->enableCrumbleEmitter = false;
+ }
+#else
+ PX_UNUSED(name);
+#endif // APEX_USE_PARTICLES
+
+ return mCrumbleEmitter ? true : false;
+}
+
+void DestructibleActorImpl::setCrumbleEmitterName(const char* name)
+{
+ // Avoid self-assignment
+ if (name == getCrumbleEmitterName())
+ return;
+ NvParameterized::Handle handle(*mParams);
+ mParams->getParameterHandle("crumbleEmitterName", handle);
+ mParams->setParamString(handle, name ? name : "");
+}
+
+const char* DestructibleActorImpl::getCrumbleEmitterName() const
+{
+ const char* name = (const char*)mParams->crumbleEmitterName;
+ return (name && *name) ? name : NULL;
+}
+
+void DestructibleActorImpl::setDustEmitterName(const char* name)
+{
+ // Avoid self-assignment
+ if (name == getDustEmitterName())
+ return;
+ NvParameterized::Handle handle(*mParams);
+ mParams->getParameterHandle("dustEmitterName", handle);
+ mParams->setParamString(handle, name ? name : "");
+}
+
+const char* DestructibleActorImpl::getDustEmitterName() const
+{
+ const char* name = (const char*)mParams->dustEmitterName;
+ return (name && *name) ? name : NULL;
+}
+
+
+void DestructibleActorImpl::setPreferredRenderVolume(RenderVolume* volume, DestructibleEmitterType::Enum type)
+{
+#if APEX_USE_PARTICLES
+ switch (type)
+ {
+ case DestructibleEmitterType::Crumble:
+ mCrumbleRenderVolume = volume;
+ if (mCrumbleEmitter)
+ {
+ mCrumbleEmitter->setPreferredRenderVolume(volume);
+ }
+ break;
+ case DestructibleEmitterType::Dust:
+ mDustRenderVolume = volume;
+ if (mDustEmitter)
+ {
+ mDustEmitter->setPreferredRenderVolume(volume);
+ }
+ break;
+ default:
+ break;
+ }
+#else
+ PX_UNUSED(volume);
+ PX_UNUSED(type);
+#endif // APEX_USE_PARTICLES
+}
+
+EmitterActor* DestructibleActorImpl::getApexEmitter(DestructibleEmitterType::Enum type)
+{
+ EmitterActor* ret = NULL;
+
+#if APEX_USE_PARTICLES
+ switch (type)
+ {
+ case DestructibleEmitterType::Crumble:
+ ret = mCrumbleEmitter;
+ break;
+ case DestructibleEmitterType::Dust:
+ ret = mDustEmitter;
+ break;
+ default:
+ break;
+ }
+#else
+ PX_UNUSED(type);
+#endif // APEX_USE_PARTICLES
+
+ return ret;
+}
+
+void DestructibleActorImpl::preSerialize(void* nvParameterizedType)
+{
+ DestructibleParameterizedType::Enum nxType = (DestructibleParameterizedType::Enum)((intptr_t)nvParameterizedType);
+ switch (nxType)
+ {
+ case DestructibleParameterizedType::State:
+ {
+ PX_ASSERT(mState->actorChunks);
+ serialize(*this, &getStructure()->chunks[0] + mFirstChunkIndex, getDestructibleAsset()->getChunkCount(), *mChunks);
+ break;
+ }
+ case DestructibleParameterizedType::Params:
+ {
+ PX_ASSERT(mParams);
+ PxMat44 globalPoseForStaticChunks;
+ if (!getGlobalPoseForStaticChunks(globalPoseForStaticChunks))
+ {
+ globalPoseForStaticChunks = getInitialGlobalPose(); // This will occur if there are no static chunks
+ }
+ PxTransform globalPose(globalPoseForStaticChunks);
+ globalPose.q.normalize();
+ mParams->globalPose = globalPose;
+ PX_ASSERT(mState->actorParameters);
+ serialize(mDestructibleParameters, *mState->actorParameters);
+ break;
+ }
+ default:
+ PX_ASSERT(0 && "Invalid destructible parameterized type");
+ break;
+ }
+}
+
+bool DestructibleActorImpl::applyDamageColoring(uint16_t indexInAsset, const PxVec3& position, float damage, float damageRadius)
+{
+ bool bRet = applyDamageColoringRecursive(indexInAsset, position, damage, damageRadius);
+
+ RenderMeshAsset* rma = mAsset->getRenderMeshAsset();
+ for (uint32_t submeshIndex = 0; submeshIndex < rma->getSubmeshCount(); ++submeshIndex)
+ {
+ physx::Array<ColorRGBA>& damageColorArray = mDamageColorArrays[submeshIndex];
+ // fix asset on &damageColorArray[0]
+ if (damageColorArray.size() != 0)
+ {
+ for (uint32_t typeN = 0; typeN < DestructibleActorMeshType::Count; ++typeN)
+ {
+ RenderMeshActorIntl* renderMeshActor = (RenderMeshActorIntl*)getRenderMeshActor((DestructibleActorMeshType::Enum)typeN);
+ if (renderMeshActor != NULL)
+ {
+ renderMeshActor->setStaticColorReplacement(submeshIndex, &damageColorArray[0]);
+ }
+ }
+ }
+ }
+
+ return bRet;
+}
+
+bool DestructibleActorImpl::applyDamageColoringRecursive(uint16_t indexInAsset, const PxVec3& position, float damage, float damageRadius)
+{
+ RenderMeshAsset* rma = mAsset->getRenderMeshAsset();
+ if (mDamageColorArrays.size() != rma->getSubmeshCount())
+ {
+ return false;
+ }
+
+ // Get behavior group
+ const DestructibleAssetParametersNS::Chunk_Type& source = getDestructibleAsset()->mParams->chunks.buf[indexInAsset];
+ const DestructibleActorParamNS::BehaviorGroup_Type& behaviorGroup = getBehaviorGroupImp(source.behaviorGroupIndex);
+
+ const float maxRadius = behaviorGroup.damageColorSpread.minimumRadius + damageRadius*behaviorGroup.damageColorSpread.radiusMultiplier;
+
+ const DestructibleStructure::Chunk& chunk = mStructure->chunks[indexInAsset + mFirstChunkIndex];
+ const PxVec3 disp = mStructure->getChunkWorldCentroid(chunk) - position;
+ const float dist = disp.magnitude();
+ if (dist > maxRadius + chunk.localSphereRadius)
+ {
+ return false; // Outside of max radius
+ }
+
+ bool bRet = false;
+
+// const float recipRadiusRange = maxRadius > behaviorGroup.damageColorSpread.minimumRadius ? 1.0f/(maxRadius - behaviorGroup.damageColorSpread.minimumRadius) : 0.0f;
+ const float recipRadiusRange = maxRadius > damageRadius ? 1.0f/(maxRadius - damageRadius) : 0.0f;
+
+ // Color scale multiplier based upon damage
+ const float colorScale = behaviorGroup.damageThreshold > 0.0f ? PxMin(1.0f, damage/behaviorGroup.damageThreshold) : 1.0f;
+
+ const uint32_t partIndex = mAsset->getPartIndex(indexInAsset);
+
+// const float minRadiusSquared = behaviorGroup.damageColorSpread.minimumRadius*behaviorGroup.damageColorSpread.minimumRadius;
+ const float minRadiusSquared = damageRadius*damageRadius;
+ const float maxRadiusSquared = maxRadius*maxRadius;
+
+ PxMat44 chunkGlobalPose(getChunkPose(indexInAsset));
+ chunkGlobalPose.scale(PxVec4(getScale(), 1.0f));
+
+ // Find all vertices and modify color
+ for (uint32_t submeshIndex = 0; submeshIndex < rma->getSubmeshCount(); ++submeshIndex)
+ {
+ const RenderSubmesh& submesh = rma->getSubmesh(submeshIndex);
+ physx::Array<ColorRGBA>& damageColorArray = mDamageColorArrays[submeshIndex];
+ const VertexBuffer& vb = submesh.getVertexBuffer();
+ PX_ASSERT(damageColorArray.size() == vb.getVertexCount());
+ if (damageColorArray.size() != vb.getVertexCount()) // Make sure we have the correct size color array
+ {
+ continue;
+ }
+ const VertexFormat& vf = vb.getFormat();
+ const int32_t positionBufferIndex = vf.getBufferIndexFromID(vf.getSemanticID(RenderVertexSemantic::POSITION));
+ if (positionBufferIndex >= 0) // Make sure we have a position buffer
+ {
+ RenderDataFormat::Enum positionBufferFormat = vf.getBufferFormat((uint32_t)positionBufferIndex);
+ PX_ASSERT(positionBufferFormat == RenderDataFormat::FLOAT3);
+ if (positionBufferFormat != RenderDataFormat::FLOAT3)
+ {
+ continue;
+ }
+ const PxVec3* positions = (const PxVec3*)vb.getBuffer((uint32_t)positionBufferIndex);
+ // Get the vertex range for the part associated with this chunk (note: this will _not_ work with instancing)
+ const uint32_t firstVertexIndex = submesh.getFirstVertexIndex(partIndex);
+ const uint32_t stopVertexIndex = firstVertexIndex + submesh.getVertexCount(partIndex);
+ for (uint32_t vertexIndex = firstVertexIndex; vertexIndex < stopVertexIndex; ++vertexIndex)
+ {
+ // Adjust the color scale based upon the envelope function
+ float colorChangeMultiplier = colorScale;
+ // Get the world vertex position
+ const PxVec3 worldVertexPosition = chunkGlobalPose.transform(positions[vertexIndex]);
+ const float radiusSquared = (position - worldVertexPosition).magnitudeSquared();
+ if (radiusSquared > maxRadiusSquared)
+ {
+ continue;
+ }
+ if (radiusSquared > minRadiusSquared)
+ {
+ colorChangeMultiplier *= PxPow((maxRadius - PxSqrt(radiusSquared))*recipRadiusRange, behaviorGroup.damageColorSpread.falloffExponent);
+ }
+ // Get the color, add the scaled color values, clamp, and set the new color
+ ColorRGBA& color = damageColorArray[vertexIndex];
+ PxVec4 newColor = PxVec4((float)color.r, (float)color.g, (float)color.b, (float)color.a) + colorChangeMultiplier*behaviorGroup.damageColorChange;
+ newColor = newColor.maximum(PxVec4(0.0f));
+ newColor = newColor.minimum(PxVec4(255.0f));
+ // save previous color
+ ColorRGBA preColor = color;
+ color.r = (uint8_t)(newColor[0] + 0.5f);
+ color.g = (uint8_t)(newColor[1] + 0.5f);
+ color.b = (uint8_t)(newColor[2] + 0.5f);
+ color.a = (uint8_t)(newColor[3] + 0.5f);
+
+ // Only save the static chunk now.
+ if ((chunk.state & ChunkDynamic) == 0)
+ {
+ // compare the previous color with the new color
+ bRet = preColor.r != color.r || preColor.g != color.g || preColor.b != color.b || preColor.a != color.a;
+ }
+ }
+ }
+ }
+
+ // Recurse to children
+ const uint32_t stopIndex = uint32_t(source.firstChildIndex + source.numChildren);
+ for (uint32_t childIndex = source.firstChildIndex; childIndex < stopIndex; ++childIndex)
+ {
+ bRet |= applyDamageColoringRecursive((uint16_t)childIndex, position, damage, damageRadius);
+ }
+
+ return bRet;
+}
+
+void DestructibleActorImpl::fillBehaviorGroupDesc(DestructibleBehaviorGroupDesc& behaviorGroupDesc, const DestructibleActorParamNS::BehaviorGroup_Type behaviorGroup) const
+{
+ behaviorGroupDesc.name = behaviorGroup.name;
+ behaviorGroupDesc.damageThreshold = behaviorGroup.damageThreshold;
+ behaviorGroupDesc.damageToRadius = behaviorGroup.damageToRadius;
+ behaviorGroupDesc.damageSpread.minimumRadius = behaviorGroup.damageSpread.minimumRadius;
+ behaviorGroupDesc.damageSpread.radiusMultiplier = behaviorGroup.damageSpread.radiusMultiplier;
+ behaviorGroupDesc.damageSpread.falloffExponent = behaviorGroup.damageSpread.falloffExponent;
+ behaviorGroupDesc.damageColorSpread.minimumRadius = behaviorGroup.damageColorSpread.minimumRadius;
+ behaviorGroupDesc.damageColorSpread.radiusMultiplier = behaviorGroup.damageColorSpread.radiusMultiplier;
+ behaviorGroupDesc.damageColorSpread.falloffExponent = behaviorGroup.damageColorSpread.falloffExponent;
+ behaviorGroupDesc.damageColorChange = behaviorGroup.damageColorChange;
+ behaviorGroupDesc.materialStrength = behaviorGroup.materialStrength;
+ behaviorGroupDesc.density = behaviorGroup.density;
+ behaviorGroupDesc.fadeOut = behaviorGroup.fadeOut;
+ behaviorGroupDesc.maxDepenetrationVelocity = behaviorGroup.maxDepenetrationVelocity;
+ behaviorGroupDesc.userData = behaviorGroup.userData;
+}
+
+void DestructibleActorImpl::spawnParticles(EmitterActor* emitter, UserChunkParticleReport* report, DestructibleStructure::Chunk& chunk, physx::Array<PxVec3>& positions, bool deriveVelocitiesFromChunk, const PxVec3* overrideVelocity)
+{
+#if APEX_USE_PARTICLES
+ uint32_t numParticles = positions.size();
+ if (numParticles == 0)
+ {
+ return;
+ }
+
+ EmitterGeomExplicit* geom = emitter != NULL ? emitter->isExplicitGeom() : NULL; // emitter may be NULL, since we may have a particle callback
+
+ if (geom == NULL && report == NULL)
+ {
+ APEX_INTERNAL_ERROR("DestructibleActor::spawnParticles requires an EmitterGeomExplicit emitter or a UserChunkParticleReport, or both.");
+ return;
+ }
+
+ if (overrideVelocity == NULL && deriveVelocitiesFromChunk)
+ {
+ physx::Array<PxVec3> volumeFillVel;
+ volumeFillVel.resize(numParticles);
+
+ // Use dynamic actor's current velocity
+ PxVec3 angVel;
+ PxVec3 linVel;
+ PxVec3 COM;
+ PxRigidDynamic* actor = mStructure->getChunkActor(chunk);
+ {
+ SCOPED_PHYSX_LOCK_READ(actor->getScene());
+ angVel = actor->getAngularVelocity();
+ linVel = actor->getLinearVelocity();
+ COM = actor->getGlobalPose().p;
+ }
+
+ for (uint32_t i = 0; i < numParticles; ++i)
+ {
+ volumeFillVel[i] = linVel + angVel.cross(positions[i] - COM);
+ PX_ASSERT(PxIsFinite(volumeFillVel[i].magnitude()));
+ }
+
+ if (geom != NULL)
+ {
+ geom->addParticleList(numParticles, &positions.front(), &volumeFillVel.front());
+ //emitter->emit(false);
+ }
+
+ if (report != NULL)
+ {
+ ChunkParticleReportData reportData;
+ reportData.positions = &positions.front();
+ reportData.positionCount = numParticles;
+ reportData.velocities = &volumeFillVel.front();
+ reportData.velocityCount = numParticles;
+ report->onParticleEmission(reportData);
+ }
+ }
+ else
+ {
+ PX_ASSERT(overrideVelocity == NULL || PxIsFinite(overrideVelocity->magnitude()));
+
+ if (geom != NULL)
+ {
+ const PxVec3 velocity = overrideVelocity != NULL ? *overrideVelocity : PxVec3(0.0f);
+ emitter->setVelocityLow(velocity);
+ emitter->setVelocityHigh(velocity);
+ geom->addParticleList(numParticles, &positions.front());
+ }
+
+ if (report != NULL)
+ {
+ ChunkParticleReportData reportData;
+ reportData.positions = &positions.front();
+ reportData.positionCount = numParticles;
+ reportData.velocities = overrideVelocity;
+ reportData.velocityCount = overrideVelocity != NULL ? 1u : 0u;
+ report->onParticleEmission(reportData);
+ }
+ }
+#else
+ PX_UNUSED(emitter);
+ PX_UNUSED(report);
+ PX_UNUSED(chunk);
+ PX_UNUSED(positions);
+ PX_UNUSED(deriveVelocitiesFromChunk);
+ PX_UNUSED(overrideVelocity);
+#endif // APEX_USE_PARTICLES
+}
+
+void DestructibleActorImpl::setDeleteFracturedChunks(bool inDeleteChunkMode)
+{
+ mInDeleteChunkMode = inDeleteChunkMode;
+}
+
+bool DestructibleActorImpl::setHitChunkTrackingParams(bool flushHistory, bool startTracking, uint32_t trackingDepth, bool trackAllChunks)
+{
+ bool validOperation = false;
+ if(getDestructibleAsset()->getDepthCount() >= trackingDepth)
+ {
+ hitChunkParams.cacheChunkHits = startTracking;
+ hitChunkParams.cacheAllChunks = trackAllChunks;
+ if(flushHistory && !hitChunkParams.hitChunkContainer.empty())
+ {
+ hitChunkParams.hitChunkContainer.clear();
+ damageColoringParams.damageEventCoreDataContainer.clear();
+ }
+ hitChunkParams.trackingDepth = trackingDepth;
+ validOperation = true;
+ }
+ return validOperation;
+}
+
+bool DestructibleActorImpl::getHitChunkHistory(const DestructibleHitChunk *& hitChunkContainer, uint32_t & hitChunkCount) const
+{
+ bool validOperation = false;
+ {
+ hitChunkContainer = !hitChunkParams.hitChunkContainer.empty() ? static_cast<const DestructibleHitChunk*>(&hitChunkParams.hitChunkContainer[0]) : NULL;
+ hitChunkCount = hitChunkParams.hitChunkContainer.size();
+ validOperation = true;
+ }
+ return validOperation;
+}
+
+bool DestructibleActorImpl::forceChunkHits(const DestructibleHitChunk * hitChunkContainer, uint32_t hitChunkCount, bool removeChunks /*= true*/, bool deferredEvent /*= false*/, PxVec3 damagePosition /*= PxVec3(0.0f)*/, PxVec3 damageDirection /*= PxVec3(0.0f)*/)
+{
+ bool validOperation = false;
+ PX_ASSERT(!((NULL == hitChunkContainer) ^ (0 == hitChunkCount)));
+ if(NULL != hitChunkContainer && 0 != hitChunkCount)
+ {
+ uint32_t manualFractureCount = 0;
+ for(uint32_t index = 0; index < hitChunkCount; ++index)
+ {
+ if(mStructure->chunks.size() > (hitChunkContainer[index].chunkIndex + mFirstChunkIndex))
+ {
+ hitChunkParams.manualFractureEventInstance.chunkIndexInAsset = hitChunkContainer[index].chunkIndex;
+ hitChunkParams.manualFractureEventInstance.destructibleID = mID;
+ hitChunkParams.manualFractureEventInstance.position = damagePosition;
+ hitChunkParams.manualFractureEventInstance.hitDirection = damageDirection;
+ hitChunkParams.manualFractureEventInstance.flags = (hitChunkContainer[index].hitChunkFlags | static_cast<uint32_t>(FractureEvent::Manual) | (removeChunks ? static_cast<uint32_t>(FractureEvent::DeleteChunk) : 0));
+ if(deferredEvent)
+ {
+ mDestructibleScene->mDeferredFractureBuffer.pushBack() = hitChunkParams.manualFractureEventInstance;
+ }
+ else
+ {
+ mDestructibleScene->mDeprioritisedFractureBuffer.pushBack() = hitChunkParams.manualFractureEventInstance;
+ }
+ ++manualFractureCount;
+ }
+ }
+ validOperation = (manualFractureCount == hitChunkCount);
+ }
+ return validOperation;
+}
+
+void DestructibleActorImpl::evaluateForHitChunkList(const FractureEvent & fractureEvent)
+{
+ PX_ASSERT(NULL != &fractureEvent);
+
+ // cache hit chunks for non-manual fractureEvents, if user set so
+ if(hitChunkParams.cacheChunkHits && (0 == (fractureEvent.flags & FractureEvent::Manual)))
+ {
+ // walk up the depth until we reach the user-specified tracking depth
+ int32_t chunkToUseIndexInAsset = static_cast<int32_t>(fractureEvent.chunkIndexInAsset);
+ PX_ASSERT(chunkToUseIndexInAsset >= 0);
+ while(ModuleDestructibleConst::INVALID_CHUNK_INDEX != chunkToUseIndexInAsset)
+ {
+ PX_ASSERT(chunkToUseIndexInAsset < static_cast<int32_t>(getDestructibleAsset()->getChunkCount()));
+ const DestructibleAssetParametersNS::Chunk_Type & source = getDestructibleAsset()->mParams->chunks.buf[chunkToUseIndexInAsset];
+ if(source.depth <= hitChunkParams.trackingDepth)
+ {
+ break;
+ }
+ chunkToUseIndexInAsset = static_cast<int32_t>(source.parentIndex);
+ }
+
+ // cache the chunk index at the user-specified tracking depth
+ if(ModuleDestructibleConst::INVALID_CHUNK_INDEX != chunkToUseIndexInAsset)
+ {
+ PX_ASSERT(chunkToUseIndexInAsset + getFirstChunkIndex() < getStructure()->chunks.size());
+ const DestructibleStructure::Chunk & chunkToUse = getStructure()->chunks[chunkToUseIndexInAsset + getFirstChunkIndex()];
+ if((!chunkToUse.isDestroyed()) && (0 == (FractureEvent::DamageFromImpact & fractureEvent.flags)) && (!hitChunkParams.cacheAllChunks ? ((chunkToUse.state & ChunkDynamic) == 0) : true))
+ {
+ // these flags are only used internally for fracture events coming in from the sync buffer, so we should exclude them
+ uint32_t hitChunkFlags = fractureEvent.flags;
+ hitChunkFlags &= ~FractureEvent::SyncDirect;
+ hitChunkFlags &= ~FractureEvent::SyncDerived;
+ hitChunkFlags &= ~FractureEvent::Manual;
+
+ // disallow crumbling and snapping as well
+ hitChunkFlags &= ~FractureEvent::CrumbleChunk;
+ hitChunkFlags &= ~FractureEvent::Snap;
+
+ // cache the chunk index
+ hitChunkParams.hitChunkContainer.pushBack(DestructibleActorImpl::CachedHitChunk(static_cast<uint32_t>(chunkToUseIndexInAsset), hitChunkFlags));
+ }
+ }
+ }
+}
+
+bool DestructibleActorImpl::getDamageColoringHistory(const DamageEventCoreData *& damageEventCoreDataContainer, uint32_t & damageEventCoreDataCount) const
+{
+ bool validOperation = false;
+ {
+ damageEventCoreDataContainer = !damageColoringParams.damageEventCoreDataContainer.empty() ? static_cast<const DamageEventCoreData*>(&damageColoringParams.damageEventCoreDataContainer[0]) : NULL;
+ damageEventCoreDataCount = damageColoringParams.damageEventCoreDataContainer.size();
+ validOperation = true;
+ }
+ return validOperation;
+
+}
+
+bool DestructibleActorImpl::forceDamageColoring(const DamageEventCoreData * damageEventCoreDataContainer, uint32_t damageEventCoreDataCount)
+{
+ bool validOperation = false;
+ PX_ASSERT(!((NULL == damageEventCoreDataContainer) ^ (0 == damageEventCoreDataCount)));
+ if (NULL != damageEventCoreDataContainer && 0 != damageEventCoreDataCount)
+ {
+ uint32_t manualDamageColorCount = 0;
+ for (uint32_t index = 0; index < damageEventCoreDataCount; ++index)
+ {
+ if (mStructure->chunks.size() > (damageEventCoreDataContainer[index].chunkIndexInAsset + mFirstChunkIndex))
+ {
+ damageColoringParams.damageEventCoreDataInstance.destructibleID = mID;
+ damageColoringParams.damageEventCoreDataInstance.chunkIndexInAsset = damageEventCoreDataContainer[index].chunkIndexInAsset;
+ damageColoringParams.damageEventCoreDataInstance.position = damageEventCoreDataContainer[index].position;
+ damageColoringParams.damageEventCoreDataInstance.damage = damageEventCoreDataContainer[index].damage;
+ damageColoringParams.damageEventCoreDataInstance.radius = damageEventCoreDataContainer[index].radius;
+
+ mDestructibleScene->mSyncDamageEventCoreDataBuffer.pushBack() = damageColoringParams.damageEventCoreDataInstance;
+
+ ++manualDamageColorCount;
+ }
+ }
+ validOperation = (manualDamageColorCount == damageEventCoreDataCount);
+ }
+ return validOperation;
+
+}
+
+void DestructibleActorImpl::collectDamageColoring(const int32_t indexInAsset, const PxVec3& position, const float damage, const float damageRadius)
+{
+ // only start cached the damage coloring when the flag set. see setHitChunkTrackingParams()
+ if (hitChunkParams.cacheChunkHits)
+ {
+ damageColoringParams.damageEventCoreDataContainer.pushBack(DestructibleActorImpl::CachedDamageEventCoreData(indexInAsset, position, damage, damageRadius));
+ }
+}
+
+void DestructibleActorImpl::applyDamageColoring_immediate(const int32_t indexInAsset, const PxVec3& position, const float damage, const float damageRadius)
+{
+ // Damage coloring:
+ if (useDamageColoring())
+ {
+ if (indexInAsset != ModuleDestructibleConst::INVALID_CHUNK_INDEX)
+ {
+ // Normal Damage - apply damage coloring directly
+ applyDamageColoring(static_cast<uint16_t>(indexInAsset), position, damage, damageRadius);
+ }
+ else
+ {
+ // Radius Damage - need to traverse all the visible chunks
+ const uint16_t* chunkIndexPtr = mVisibleChunks.usedIndices();
+ const uint16_t* chunkIndexPtrStop = chunkIndexPtr + mVisibleChunks.usedCount();
+
+ while (chunkIndexPtr < chunkIndexPtrStop)
+ {
+ uint16_t chunkIndex = *chunkIndexPtr++;
+ DestructibleStructure::Chunk& chunk = mStructure->chunks[chunkIndex + mFirstChunkIndex];
+
+ applyDamageColoring(chunk.indexInAsset, position, damage, damageRadius);
+ }
+ }
+ }
+}
+
+bool DestructibleActorImpl::getUseLegacyChunkBoundsTesting() const
+{
+ const int8_t setting = mParams->destructibleParameters.legacyChunkBoundsTestSetting;
+ if (setting < 0)
+ {
+ return mDestructibleScene->getModuleDestructible()->getUseLegacyChunkBoundsTesting();
+ }
+ return setting > 0;
+}
+
+bool DestructibleActorImpl::getUseLegacyDamageRadiusSpread() const
+{
+ const int8_t setting = mParams->destructibleParameters.legacyDamageRadiusSpreadSetting;
+ if (setting < 0)
+ {
+ return mDestructibleScene->getModuleDestructible()->getUseLegacyDamageRadiusSpread();
+ }
+ return setting > 0;
+}
+
+/*** DestructibleActor::SyncParams ***/
+bool DestructibleActorImpl::setSyncParams(uint32_t userActorID, uint32_t actorSyncFlags, const DestructibleActorSyncState * actorSyncState, const DestructibleChunkSyncState * chunkSyncState)
+{
+ bool validEntry = false;
+ PX_ASSERT(!mDestructibleScene->getSyncParams().lockSyncParams && "if this happens, theres more work to do!");
+ if(!mDestructibleScene->getSyncParams().lockSyncParams)
+ {
+ const bool validActorSyncFlags = static_cast<uint32_t>(DestructibleActorSyncFlags::Last) > actorSyncFlags;
+ const bool validActorSyncState = (NULL != actorSyncState) ? (mAsset->getDepthCount() >= actorSyncState->damageEventFilterDepth) && (mAsset->getDepthCount() >= actorSyncState->fractureEventFilterDepth) : true;
+ const bool validChunkSyncState = (NULL != chunkSyncState) ? (mAsset->getDepthCount() >= chunkSyncState->chunkTransformCopyDepth) : true;
+#if 0
+ //const bool validDepth = (mAsset->getDepthCount() >= chunkPositionCopyDepth) ? (chunkPositionCopyDepth >= chunkTransformCopyDepth) : false;
+#endif
+ if(validActorSyncFlags && validActorSyncState && validChunkSyncState)
+ {
+ //determine type of operation
+ const uint32_t presentUserActorID = mSyncParams.getUserActorID();
+ const bool addEntry = (0 != userActorID) && (0 == presentUserActorID);
+ const bool removeEntry = (0 == userActorID) && (0 != presentUserActorID);
+ const bool relocateEntry = (0 != userActorID) && (0 != presentUserActorID) && (userActorID != presentUserActorID);
+ const bool editEntry = (0 != userActorID) && (0 != presentUserActorID) && (userActorID == presentUserActorID);
+ const bool invalidEntry = (0 == userActorID) && (0 == presentUserActorID);
+ PX_ASSERT(addEntry ? (!removeEntry && !relocateEntry && !editEntry && !invalidEntry) : removeEntry ? (!relocateEntry && !editEntry && !invalidEntry) : relocateEntry ? (!editEntry && !invalidEntry) : editEntry? (!invalidEntry) : invalidEntry);
+
+ //attempt update scene and actor params
+ if(addEntry || removeEntry || relocateEntry || editEntry)
+ {
+ bool validUserActorID = false;
+ bool useUserArguments = false;
+ DestructibleScene::SyncParams & sceneParams = mDestructibleScene->getSyncParamsMutable();
+ DestructibleActorImpl * erasedEntry = NULL;
+ if(addEntry)
+ {
+ validUserActorID = sceneParams.setSyncActor(userActorID, this, erasedEntry);
+ PX_ASSERT(this != erasedEntry);
+ if(validUserActorID)
+ {
+ useUserArguments = true;
+ if(NULL != erasedEntry)
+ {
+ erasedEntry->getSyncParamsMutable().onReset();
+ erasedEntry = NULL;
+ }
+ }
+ }
+ else if(removeEntry)
+ {
+ validUserActorID = sceneParams.setSyncActor(presentUserActorID, NULL, erasedEntry);
+ PX_ASSERT((NULL != erasedEntry) && (this == erasedEntry));
+ if(validUserActorID)
+ {
+ mSyncParams.onReset();
+ }
+ }
+ else if(relocateEntry)
+ {
+ validUserActorID = sceneParams.setSyncActor(presentUserActorID, NULL, erasedEntry);
+ PX_ASSERT((NULL != erasedEntry) && (this == erasedEntry));
+ if(validUserActorID)
+ {
+ validUserActorID = sceneParams.setSyncActor(userActorID, this, erasedEntry);
+ PX_ASSERT(this != erasedEntry);
+ if(validUserActorID)
+ {
+ useUserArguments = true;
+ if(NULL != erasedEntry)
+ {
+ erasedEntry->getSyncParamsMutable().onReset();
+ erasedEntry = NULL;
+ }
+ }
+ }
+ }
+ else if(editEntry)
+ {
+ validUserActorID = true;
+ useUserArguments = true;
+ }
+ else
+ {
+ PX_ASSERT(!"!");
+ }
+ validEntry = validUserActorID && validActorSyncFlags && validActorSyncState && validChunkSyncState;
+ if(useUserArguments)
+ {
+ PX_ASSERT(validEntry);
+ mSyncParams.userActorID = userActorID;
+ mSyncParams.actorSyncFlags = actorSyncFlags;
+ if (actorSyncState)
+ {
+ mSyncParams.useActorSyncState = true;
+ mSyncParams.actorSyncState = *actorSyncState;
+ }
+ if (chunkSyncState)
+ {
+ mSyncParams.useChunkSyncState = true;
+ mSyncParams.chunkSyncState = *chunkSyncState;
+ }
+ }
+ }
+ else
+ {
+ PX_ASSERT(invalidEntry);
+ PX_UNUSED(invalidEntry);
+ PX_ASSERT(!"invalid use of function!");
+ }
+ }
+ }
+ return validEntry;
+}
+
+typedef DestructibleActorImpl::SyncParams SyncParams;
+
+SyncParams::SyncParams()
+ :userActorID(0)
+ ,actorSyncFlags(0)
+ ,useActorSyncState(false)
+ ,useChunkSyncState(false)
+{
+ PX_ASSERT(damageBufferIndices.empty());
+ PX_ASSERT(fractureBufferIndices.empty());
+ PX_ASSERT(cachedChunkTransforms.empty());
+}
+
+SyncParams::~SyncParams()
+{
+ PX_ASSERT(cachedChunkTransforms.empty());
+ PX_ASSERT(fractureBufferIndices.empty());
+ PX_ASSERT(damageBufferIndices.empty());
+ PX_ASSERT(!useChunkSyncState);
+ PX_ASSERT(!useActorSyncState);
+ PX_ASSERT(0 == actorSyncFlags);
+ PX_ASSERT(0 == userActorID);
+}
+
+uint32_t SyncParams::getUserActorID() const
+{
+ return userActorID;
+}
+
+bool SyncParams::isSyncFlagSet(DestructibleActorSyncFlags::Enum flag) const
+{
+ PX_ASSERT(0 != userActorID);
+ return (0 != (actorSyncFlags & static_cast<uint32_t>(flag)));
+}
+
+const DestructibleActorSyncState * SyncParams::getActorSyncState() const
+{
+ PX_ASSERT(useActorSyncState ? (isSyncFlagSet(DestructibleActorSyncFlags::CopyDamageEvents) || isSyncFlagSet(DestructibleActorSyncFlags::CopyFractureEvents)) : true);
+ return useActorSyncState ? &actorSyncState : NULL;
+}
+
+const DestructibleChunkSyncState * SyncParams::getChunkSyncState() const
+{
+ PX_ASSERT(useChunkSyncState ? isSyncFlagSet(DestructibleActorSyncFlags::CopyChunkTransform) : true);
+ return useChunkSyncState ? &chunkSyncState : NULL;
+}
+
+void SyncParams::pushDamageBufferIndex(uint32_t index)
+{
+ PX_ASSERT(isSyncFlagSet(DestructibleActorSyncFlags::CopyDamageEvents));
+ damageBufferIndices.pushBack(index);
+}
+
+void SyncParams::pushFractureBufferIndex(uint32_t index)
+{
+ PX_ASSERT(isSyncFlagSet(DestructibleActorSyncFlags::CopyFractureEvents));
+ fractureBufferIndices.pushBack(index);
+}
+
+void SyncParams::pushCachedChunkTransform(const CachedChunk & cachedChunk)
+{
+ PX_ASSERT(isSyncFlagSet(DestructibleActorSyncFlags::CopyChunkTransform));
+ cachedChunkTransforms.pushBack(cachedChunk);
+}
+
+const physx::Array<uint32_t> & SyncParams::getDamageBufferIndices() const
+{
+ return damageBufferIndices;
+}
+
+const physx::Array<uint32_t> & SyncParams::getFractureBufferIndices() const
+{
+ return fractureBufferIndices;
+}
+
+const physx::Array<CachedChunk> & SyncParams::getCachedChunkTransforms() const
+{
+ return cachedChunkTransforms;
+}
+
+template<> void SyncParams::clear<DamageEventUnit>()
+{
+ PX_ASSERT(!damageBufferIndices.empty() ? isSyncFlagSet(DestructibleActorSyncFlags::CopyDamageEvents) : true);
+ if(!damageBufferIndices.empty()) damageBufferIndices.clear();
+}
+
+template<> void SyncParams::clear<FractureEventUnit>()
+{
+ PX_ASSERT(!fractureBufferIndices.empty() ? isSyncFlagSet(DestructibleActorSyncFlags::CopyFractureEvents) : true);
+ if(!fractureBufferIndices.empty()) fractureBufferIndices.clear();
+}
+
+template<> void SyncParams::clear<ChunkTransformUnit>()
+{
+ PX_ASSERT(!cachedChunkTransforms.empty() ? isSyncFlagSet(DestructibleActorSyncFlags::CopyChunkTransform) : true);
+ if(!cachedChunkTransforms.empty()) cachedChunkTransforms.clear();
+}
+
+template<> uint32_t SyncParams::getCount<DamageEventUnit>() const
+{
+ PX_ASSERT(!damageBufferIndices.empty() ? isSyncFlagSet(DestructibleActorSyncFlags::CopyDamageEvents) : true);
+ return damageBufferIndices.size();
+}
+
+template<> uint32_t SyncParams::getCount<FractureEventUnit>() const
+{
+ PX_ASSERT(!fractureBufferIndices.empty() ? isSyncFlagSet(DestructibleActorSyncFlags::CopyFractureEvents) : true);
+ return fractureBufferIndices.size();
+}
+
+template<> uint32_t SyncParams::getCount<ChunkTransformUnit>() const
+{
+ PX_ASSERT(!cachedChunkTransforms.empty() ? isSyncFlagSet(DestructibleActorSyncFlags::CopyChunkTransform) : true);
+ return cachedChunkTransforms.size();
+}
+
+void SyncParams::onReset()
+{
+ userActorID = 0;
+ actorSyncFlags = 0;
+ useActorSyncState = false;
+ useChunkSyncState = false;
+ damageBufferIndices.clear();
+ fractureBufferIndices.clear();
+ cachedChunkTransforms.clear();
+}
+
+const SyncParams & DestructibleActorImpl::getSyncParams() const
+{
+ return mSyncParams;
+}
+
+SyncParams & DestructibleActorImpl::getSyncParamsMutable()
+{
+ return const_cast<SyncParams&>(getSyncParams());
+}
+
+// Renderable support:
+
+DestructibleRenderable* DestructibleActorImpl::acquireRenderableReference()
+{
+ return mRenderable ? mRenderable->incrementReferenceCount() : NULL;
+}
+
+RenderMeshActor* DestructibleActorImpl::getRenderMeshActor(DestructibleActorMeshType::Enum type) const
+{
+ return mRenderable ? mRenderable->getRenderMeshActor(type) : NULL;
+}
+
+}
+} // end namespace nvidia
+
+