diff options
| author | git perforce import user <a@b> | 2016-10-25 12:29:14 -0600 |
|---|---|---|
| committer | Sheikh Dawood Abdul Ajees <Sheikh Dawood Abdul Ajees> | 2016-10-25 18:56:37 -0500 |
| commit | 3dfe2108cfab31ba3ee5527e217d0d8e99a51162 (patch) | |
| tree | fa6485c169e50d7415a651bf838f5bcd0fd3bfbd /APEX_1.4/module/destructible/src/DestructibleAssetImpl.cpp | |
| download | physx-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/DestructibleAssetImpl.cpp')
| -rw-r--r-- | APEX_1.4/module/destructible/src/DestructibleAssetImpl.cpp | 3780 |
1 files changed, 3780 insertions, 0 deletions
diff --git a/APEX_1.4/module/destructible/src/DestructibleAssetImpl.cpp b/APEX_1.4/module/destructible/src/DestructibleAssetImpl.cpp new file mode 100644 index 00000000..5939a084 --- /dev/null +++ b/APEX_1.4/module/destructible/src/DestructibleAssetImpl.cpp @@ -0,0 +1,3780 @@ +/* + * 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 "DestructibleAssetImpl.h" +#include "DestructibleActorProxy.h" +#include "DestructiblePreviewProxy.h" +#include "ModuleDestructibleImpl.h" +#include "PxCooking.h" +#include "PxPhysics.h" +#include <PxScene.h> +#include "ModulePerfScope.h" +#include "ApexUsingNamespace.h" +#include "RenderMeshAssetIntl.h" +#include "Cof44.h" +#include "nvparameterized/NvParamUtils.h" +#include "PsMemoryBuffer.h" +#if APEX_USE_PARTICLES +#include "EmitterAsset.h" +#else +static const char* EMITTER_AUTHORING_TYPE_NAME = "ApexEmitterAsset"; +#endif + +#include "../../../framework/include/autogen/RenderMeshAssetParameters.h" +#include "../../../framework/include/ApexRenderMeshAsset.h" + + +#include "ApexSharedSerialization.h" +#include "ApexRand.h" + +#include "ApexMerge.h" +#include "ApexFind.h" + +namespace nvidia +{ +namespace destructible +{ +using namespace physx; + +struct ChunkSortElement +{ + int32_t index; + int32_t parentIndex; + int32_t depth; +}; + +struct IndexSortedEdge +{ + IndexSortedEdge() {} + IndexSortedEdge(uint32_t _i0, uint32_t _i1, uint32_t _submeshIndex, const PxVec3& _triangleNormal) + { + if (_i0 <= _i1) + { + i0 = _i0; + i1 = _i1; + } + else + { + i0 = _i1; + i1 = _i0; + } + submeshIndex = _submeshIndex; + triangleNormal = _triangleNormal; + } + + uint32_t i0; + uint32_t i1; + uint32_t submeshIndex; + PxVec3 triangleNormal; +}; + +// We'll use this struct to store trim planes for each hull in each part +struct TrimPlane +{ + uint32_t partIndex; + uint32_t hullIndex; + PxPlane plane; + + struct LessThan + { + PX_INLINE bool operator()(const TrimPlane& x, const TrimPlane& y) const + { + return x.partIndex != y.partIndex ? (x.partIndex < y.partIndex) : (x.hullIndex < y.hullIndex); + } + }; +}; + +#ifndef WITHOUT_APEX_AUTHORING +static int compareChunkParents( + const void* A, + const void* B) +{ + ChunkSortElement& eA = *(ChunkSortElement*)A; + ChunkSortElement& eB = *(ChunkSortElement*)B; + + const int32_t depthDiff = eA.depth - eB.depth; + if (depthDiff) + { + return depthDiff; + } + + const int32_t parentDiff = eA.parentIndex - eB.parentIndex; + if (parentDiff) + { + return parentDiff; + } + + return eA.index - eB.index; // Keeps sort stable +} +#endif + +void DestructibleAssetImpl::setParameters(const DestructibleParameters& parameters) +{ + setParameters(parameters, mParams->destructibleParameters); + + NvParameterized::Handle handle(*mParams); + mParams->getParameterHandle("depthParameters", handle); + mParams->resizeArray(handle, (int32_t)parameters.depthParametersCount); + for (uint32_t i = 0; i < parameters.depthParametersCount; ++i) + { + DestructibleAssetParametersNS::DestructibleDepthParameters_Type& d = mParams->depthParameters.buf[i]; + const DestructibleDepthParameters& dparm = parameters.depthParameters[i]; + d.OVERRIDE_IMPACT_DAMAGE = (dparm.flags & DestructibleDepthParametersFlag::OVERRIDE_IMPACT_DAMAGE) ? true : false; + d.OVERRIDE_IMPACT_DAMAGE_VALUE = (dparm.flags & DestructibleDepthParametersFlag::OVERRIDE_IMPACT_DAMAGE_VALUE) ? true : false; + d.IGNORE_POSE_UPDATES = (dparm.flags & DestructibleDepthParametersFlag::IGNORE_POSE_UPDATES) ? true : false; + d.IGNORE_RAYCAST_CALLBACKS = (dparm.flags & DestructibleDepthParametersFlag::IGNORE_RAYCAST_CALLBACKS) ? true : false; + d.IGNORE_CONTACT_CALLBACKS = (dparm.flags & DestructibleDepthParametersFlag::IGNORE_CONTACT_CALLBACKS) ? true : false; + d.USER_FLAG_0 = (dparm.flags & DestructibleDepthParametersFlag::USER_FLAG_0) ? true : false; + d.USER_FLAG_1 = (dparm.flags & DestructibleDepthParametersFlag::USER_FLAG_1) ? true : false; + d.USER_FLAG_2 = (dparm.flags & DestructibleDepthParametersFlag::USER_FLAG_2) ? true : false; + d.USER_FLAG_3 = (dparm.flags & DestructibleDepthParametersFlag::USER_FLAG_3) ? true : false; + } +} + +void DestructibleAssetImpl::setParameters(const DestructibleParameters& parameters, DestructibleAssetParametersNS::DestructibleParameters_Type& destructibleParameters) +{ + destructibleParameters.damageCap = parameters.damageCap; + destructibleParameters.debrisDepth = parameters.debrisDepth; + destructibleParameters.debrisLifetimeMax = parameters.debrisLifetimeMax; + destructibleParameters.debrisLifetimeMin = parameters.debrisLifetimeMin; + destructibleParameters.debrisMaxSeparationMax = parameters.debrisMaxSeparationMax; + destructibleParameters.debrisMaxSeparationMin = parameters.debrisMaxSeparationMin; + destructibleParameters.debrisDestructionProbability = parameters.debrisDestructionProbability; + destructibleParameters.dynamicChunkDominanceGroup = parameters.dynamicChunksDominanceGroup; + destructibleParameters.dynamicChunksGroupsMask.useGroupsMask = parameters.useDynamicChunksGroupsMask; + destructibleParameters.dynamicChunksGroupsMask.bits0 = parameters.dynamicChunksFilterData.word0; + destructibleParameters.dynamicChunksGroupsMask.bits1 = parameters.dynamicChunksFilterData.word1; + destructibleParameters.dynamicChunksGroupsMask.bits2 = parameters.dynamicChunksFilterData.word2; + destructibleParameters.dynamicChunksGroupsMask.bits3 = parameters.dynamicChunksFilterData.word3; + destructibleParameters.supportStrength = parameters.supportStrength; + destructibleParameters.legacyChunkBoundsTestSetting = parameters.legacyChunkBoundsTestSetting; + destructibleParameters.legacyDamageRadiusSpreadSetting = parameters.legacyDamageRadiusSpreadSetting; + destructibleParameters.alwaysDrawScatterMesh = parameters.alwaysDrawScatterMesh; + + destructibleParameters.essentialDepth = parameters.essentialDepth; + destructibleParameters.flags.ACCUMULATE_DAMAGE = (parameters.flags & DestructibleParametersFlag::ACCUMULATE_DAMAGE) ? true : false; + destructibleParameters.flags.DEBRIS_TIMEOUT = (parameters.flags & DestructibleParametersFlag::DEBRIS_TIMEOUT) ? true : false; + destructibleParameters.flags.DEBRIS_MAX_SEPARATION = (parameters.flags & DestructibleParametersFlag::DEBRIS_MAX_SEPARATION) ? true : false; + destructibleParameters.flags.CRUMBLE_SMALLEST_CHUNKS = (parameters.flags & DestructibleParametersFlag::CRUMBLE_SMALLEST_CHUNKS) ? true : false; + destructibleParameters.flags.ACCURATE_RAYCASTS = (parameters.flags & DestructibleParametersFlag::ACCURATE_RAYCASTS) ? true : false; + destructibleParameters.flags.USE_VALID_BOUNDS = (parameters.flags & DestructibleParametersFlag::USE_VALID_BOUNDS) ? true : false; + destructibleParameters.flags.CRUMBLE_VIA_RUNTIME_FRACTURE = (parameters.flags & DestructibleParametersFlag::CRUMBLE_VIA_RUNTIME_FRACTURE) ? true : false; + destructibleParameters.forceToDamage = parameters.forceToDamage; + destructibleParameters.fractureImpulseScale = parameters.fractureImpulseScale; + destructibleParameters.damageDepthLimit = parameters.damageDepthLimit; + destructibleParameters.impactVelocityThreshold = parameters.impactVelocityThreshold; + destructibleParameters.maxChunkSpeed = parameters.maxChunkSpeed; + destructibleParameters.minimumFractureDepth = parameters.minimumFractureDepth; + destructibleParameters.impactDamageDefaultDepth = parameters.impactDamageDefaultDepth; + destructibleParameters.debrisDestructionProbability = parameters.debrisDestructionProbability; + destructibleParameters.validBounds = parameters.validBounds; + + // RT Fracture Parameters + destructibleParameters.runtimeFracture.sheetFracture = parameters.rtFractureParameters.sheetFracture; + destructibleParameters.runtimeFracture.depthLimit = parameters.rtFractureParameters.depthLimit; + destructibleParameters.runtimeFracture.destroyIfAtDepthLimit = parameters.rtFractureParameters.destroyIfAtDepthLimit; + destructibleParameters.runtimeFracture.minConvexSize = parameters.rtFractureParameters.minConvexSize; + destructibleParameters.runtimeFracture.impulseScale = parameters.rtFractureParameters.impulseScale; + destructibleParameters.runtimeFracture.glass.numSectors = parameters.rtFractureParameters.glass.numSectors; + destructibleParameters.runtimeFracture.glass.sectorRand = parameters.rtFractureParameters.glass.sectorRand; + destructibleParameters.runtimeFracture.glass.firstSegmentSize = parameters.rtFractureParameters.glass.firstSegmentSize; + destructibleParameters.runtimeFracture.glass.segmentScale = parameters.rtFractureParameters.glass.segmentScale; + destructibleParameters.runtimeFracture.glass.segmentRand = parameters.rtFractureParameters.glass.segmentRand; + destructibleParameters.runtimeFracture.attachment.posX = parameters.rtFractureParameters.attachment.posX; + destructibleParameters.runtimeFracture.attachment.negX = parameters.rtFractureParameters.attachment.negX; + destructibleParameters.runtimeFracture.attachment.posY = parameters.rtFractureParameters.attachment.posY; + destructibleParameters.runtimeFracture.attachment.negY = parameters.rtFractureParameters.attachment.negY; + destructibleParameters.runtimeFracture.attachment.posZ = parameters.rtFractureParameters.attachment.posZ; + destructibleParameters.runtimeFracture.attachment.negZ = parameters.rtFractureParameters.attachment.negZ; +} + +void DestructibleAssetImpl::setInitParameters(const DestructibleInitParameters& parameters) +{ + mParams->supportDepth = parameters.supportDepth; + mParams->formExtendedStructures = (parameters.flags & DestructibleInitParametersFlag::FORM_EXTENDED_STRUCTURES) ? true : false; + mParams->useAssetDefinedSupport = (parameters.flags & DestructibleInitParametersFlag::ASSET_DEFINED_SUPPORT) ? true : false; + mParams->useWorldSupport = (parameters.flags & DestructibleInitParametersFlag::WORLD_SUPPORT) ? true : false; +} + +DestructibleParameters DestructibleAssetImpl::getParameters() const +{ + return getParameters(mParams->destructibleParameters, &mParams->depthParameters); +} + +DestructibleParameters DestructibleAssetImpl::getParameters(const DestructibleAssetParametersNS::DestructibleParameters_Type& destructibleParameters, + const DestructibleAssetParametersNS::DestructibleDepthParameters_DynamicArray1D_Type* destructibleDepthParameters) +{ + DestructibleParameters parameters; + + parameters.damageCap = destructibleParameters.damageCap; + parameters.debrisDepth = destructibleParameters.debrisDepth; + parameters.debrisLifetimeMax = destructibleParameters.debrisLifetimeMax; + parameters.debrisLifetimeMin = destructibleParameters.debrisLifetimeMin; + parameters.debrisMaxSeparationMax = destructibleParameters.debrisMaxSeparationMax; + parameters.debrisMaxSeparationMin = destructibleParameters.debrisMaxSeparationMin; + parameters.dynamicChunksDominanceGroup = (uint8_t)destructibleParameters.dynamicChunkDominanceGroup; + parameters.useDynamicChunksGroupsMask = destructibleParameters.dynamicChunksGroupsMask.useGroupsMask; + parameters.dynamicChunksFilterData.word0 = destructibleParameters.dynamicChunksGroupsMask.bits0; + parameters.dynamicChunksFilterData.word1 = destructibleParameters.dynamicChunksGroupsMask.bits1; + parameters.dynamicChunksFilterData.word2 = destructibleParameters.dynamicChunksGroupsMask.bits2; + parameters.dynamicChunksFilterData.word3 = destructibleParameters.dynamicChunksGroupsMask.bits3; + parameters.essentialDepth = destructibleParameters.essentialDepth; + parameters.flags = 0; + parameters.alwaysDrawScatterMesh = destructibleParameters.alwaysDrawScatterMesh; + if (destructibleParameters.flags.ACCUMULATE_DAMAGE) + { + parameters.flags |= DestructibleParametersFlag::ACCUMULATE_DAMAGE; + } + if (destructibleParameters.flags.DEBRIS_TIMEOUT) + { + parameters.flags |= DestructibleParametersFlag::DEBRIS_TIMEOUT; + } + if (destructibleParameters.flags.DEBRIS_MAX_SEPARATION) + { + parameters.flags |= DestructibleParametersFlag::DEBRIS_MAX_SEPARATION; + } + if (destructibleParameters.flags.CRUMBLE_SMALLEST_CHUNKS) + { + parameters.flags |= DestructibleParametersFlag::CRUMBLE_SMALLEST_CHUNKS; + } + if (destructibleParameters.flags.ACCURATE_RAYCASTS) + { + parameters.flags |= DestructibleParametersFlag::ACCURATE_RAYCASTS; + } + if (destructibleParameters.flags.USE_VALID_BOUNDS) + { + parameters.flags |= DestructibleParametersFlag::USE_VALID_BOUNDS; + } + if (destructibleParameters.flags.CRUMBLE_VIA_RUNTIME_FRACTURE) + { + parameters.flags |= DestructibleParametersFlag::CRUMBLE_VIA_RUNTIME_FRACTURE; + } + parameters.forceToDamage = destructibleParameters.forceToDamage; + parameters.fractureImpulseScale = destructibleParameters.fractureImpulseScale; + parameters.damageDepthLimit = destructibleParameters.damageDepthLimit; + parameters.impactVelocityThreshold = destructibleParameters.impactVelocityThreshold; + parameters.maxChunkSpeed = destructibleParameters.maxChunkSpeed; + parameters.minimumFractureDepth = destructibleParameters.minimumFractureDepth; + parameters.impactDamageDefaultDepth = destructibleParameters.impactDamageDefaultDepth; + parameters.debrisDestructionProbability = destructibleParameters.debrisDestructionProbability; + parameters.validBounds = destructibleParameters.validBounds; + + if (destructibleDepthParameters) + { + //NvParameterized::Handle handle(*mParams); + //mParams->getParameterHandle("depthParameters", handle); + parameters.depthParametersCount = PxMin((uint32_t)DestructibleParameters::kDepthParametersCountMax, + (uint32_t)destructibleDepthParameters->arraySizes[0]); + for (int i = 0; i < (int)parameters.depthParametersCount; ++i) + { + DestructibleAssetParametersNS::DestructibleDepthParameters_Type& d = destructibleDepthParameters->buf[i]; + DestructibleDepthParameters& dparm = parameters.depthParameters[i]; + dparm.flags = 0; + if (d.OVERRIDE_IMPACT_DAMAGE) + { + dparm.flags |= DestructibleDepthParametersFlag::OVERRIDE_IMPACT_DAMAGE; + } + if (d.OVERRIDE_IMPACT_DAMAGE_VALUE) + { + dparm.flags |= DestructibleDepthParametersFlag::OVERRIDE_IMPACT_DAMAGE_VALUE; + } + if (d.IGNORE_POSE_UPDATES) + { + dparm.flags |= DestructibleDepthParametersFlag::IGNORE_POSE_UPDATES; + } + if (d.IGNORE_RAYCAST_CALLBACKS) + { + dparm.flags |= DestructibleDepthParametersFlag::IGNORE_RAYCAST_CALLBACKS; + } + if (d.IGNORE_CONTACT_CALLBACKS) + { + dparm.flags |= DestructibleDepthParametersFlag::IGNORE_CONTACT_CALLBACKS; + } + if (d.USER_FLAG_0) + { + dparm.flags |= DestructibleDepthParametersFlag::USER_FLAG_0; + } + if (d.USER_FLAG_1) + { + dparm.flags |= DestructibleDepthParametersFlag::USER_FLAG_1; + } + if (d.USER_FLAG_2) + { + dparm.flags |= DestructibleDepthParametersFlag::USER_FLAG_2; + } + if (d.USER_FLAG_3) + { + dparm.flags |= DestructibleDepthParametersFlag::USER_FLAG_3; + } + } + } + + // RT Fracture Parameters + parameters.rtFractureParameters.sheetFracture = destructibleParameters.runtimeFracture.sheetFracture; + parameters.rtFractureParameters.depthLimit = destructibleParameters.runtimeFracture.depthLimit; + parameters.rtFractureParameters.destroyIfAtDepthLimit = destructibleParameters.runtimeFracture.destroyIfAtDepthLimit; + parameters.rtFractureParameters.minConvexSize = destructibleParameters.runtimeFracture.minConvexSize; + parameters.rtFractureParameters.impulseScale = destructibleParameters.runtimeFracture.impulseScale; + parameters.rtFractureParameters.glass.numSectors = destructibleParameters.runtimeFracture.glass.numSectors; + parameters.rtFractureParameters.glass.sectorRand = destructibleParameters.runtimeFracture.glass.sectorRand; + parameters.rtFractureParameters.glass.firstSegmentSize = destructibleParameters.runtimeFracture.glass.firstSegmentSize; + parameters.rtFractureParameters.glass.segmentScale = destructibleParameters.runtimeFracture.glass.segmentScale; + parameters.rtFractureParameters.glass.segmentRand = destructibleParameters.runtimeFracture.glass.segmentRand; + parameters.rtFractureParameters.attachment.posX = destructibleParameters.runtimeFracture.attachment.posX; + parameters.rtFractureParameters.attachment.negX = destructibleParameters.runtimeFracture.attachment.negX; + parameters.rtFractureParameters.attachment.posY = destructibleParameters.runtimeFracture.attachment.posY; + parameters.rtFractureParameters.attachment.negY = destructibleParameters.runtimeFracture.attachment.negY; + parameters.rtFractureParameters.attachment.posZ = destructibleParameters.runtimeFracture.attachment.posZ; + parameters.rtFractureParameters.attachment.negZ = destructibleParameters.runtimeFracture.attachment.negZ; + parameters.supportStrength = destructibleParameters.supportStrength; + parameters.legacyChunkBoundsTestSetting = destructibleParameters.legacyChunkBoundsTestSetting; + parameters.legacyDamageRadiusSpreadSetting = destructibleParameters.legacyDamageRadiusSpreadSetting; + + return parameters; +} + +DestructibleInitParameters DestructibleAssetImpl::getInitParameters() const +{ + DestructibleInitParameters parameters; + + parameters.supportDepth = mParams->supportDepth; + parameters.flags = 0; + if (mParams->formExtendedStructures) + { + parameters.flags |= DestructibleInitParametersFlag::FORM_EXTENDED_STRUCTURES; + } + if (mParams->useAssetDefinedSupport) + { + parameters.flags |= DestructibleInitParametersFlag::ASSET_DEFINED_SUPPORT; + } + if (mParams->useWorldSupport) + { + parameters.flags |= DestructibleInitParametersFlag::WORLD_SUPPORT; + } + + return parameters; +} + +void DestructibleAssetImpl::setCrumbleEmitterName(const char* name) +{ + NvParameterized::Handle handle(*mParams); + mParams->getParameterHandle("crumbleEmitterName", handle); + mParams->setParamString(handle, name ? name : ""); +} + +const char* DestructibleAssetImpl::getCrumbleEmitterName() const +{ + const char* name = mParams->crumbleEmitterName; + return (name && *name) ? name : NULL; +} + +void DestructibleAssetImpl::setDustEmitterName(const char* name) +{ + NvParameterized::Handle handle(*mParams); + mParams->getParameterHandle("dustEmitterName", handle); + mParams->setParamString(handle, name ? name : ""); +} + +const char* DestructibleAssetImpl::getDustEmitterName() const +{ + const char* name = mParams->dustEmitterName; + return (name && *name) ? name : NULL; +} + +void DestructibleAssetImpl::setFracturePatternName(const char* name) +{ + NvParameterized::Handle handle(*mParams); + mParams->getParameterHandle("fracturePatternName", handle); + mParams->setParamString(handle, name ? name : ""); +} + +const char* DestructibleAssetImpl::getFracturePatternName() const +{ + // TODO: Add to asset params + const char* name = "";//mParams->fracturePatternName; + return (name && *name) ? name : NULL; +} + +void DestructibleAssetImpl::setChunkOverlapsCacheDepth(int32_t depth) +{ + chunkOverlapCacheDepth = depth; +} + +void DestructibleAssetImpl::calculateChunkDepthStarts() +{ + const uint32_t chunkCount = (uint32_t)mParams->chunks.arraySizes[0]; + + NvParameterized::Handle handle(*mParams); + mParams->getParameterHandle("firstChunkAtDepth", handle); + mParams->resizeArray(handle, (int32_t)mParams->depthCount + 1); + mParams->firstChunkAtDepth.buf[mParams->depthCount] = chunkCount; + + uint32_t stopIndex = 0; + for (uint32_t depth = 0; depth < mParams->depthCount; ++depth) + { + mParams->firstChunkAtDepth.buf[depth] = stopIndex; + while (stopIndex < chunkCount) + { + if (mParams->chunks.buf[stopIndex].depth != depth) + { + break; + } + ++stopIndex; + } + } +} + +CachedOverlapsNS::IntPair_DynamicArray1D_Type* DestructibleAssetImpl::getOverlapsAtDepth(uint32_t depth, bool create) const +{ + if (depth >= mParams->depthCount) + { + return NULL; + } + + int size = mParams->overlapsAtDepth.arraySizes[0]; + if (size <= (int)depth && !create) + { + return NULL; + } + + NvParameterized::Handle handle(*mParams); + mParams->getParameterHandle("overlapsAtDepth", handle); + + if (create) + { + mParams->resizeArray(handle, (int32_t)mParams->depthCount); + NvParameterized::Traits* traits = GetInternalApexSDK()->getParameterizedTraits(); + while (size < (int)mParams->depthCount) + { + CachedOverlaps* cachedOverlaps = DYNAMIC_CAST(CachedOverlaps*)(traits->createNvParameterized(CachedOverlaps::staticClassName())); + mParams->overlapsAtDepth.buf[size++] = cachedOverlaps; + cachedOverlaps->isCached = false; + } + } + + CachedOverlaps* cachedOverlapsAtDepth = DYNAMIC_CAST(CachedOverlaps*)(mParams->overlapsAtDepth.buf[depth]); + if (!cachedOverlapsAtDepth->isCached) + { + if (!create) + { + return NULL; + } + physx::Array<IntPair> overlaps; + calculateChunkOverlaps(overlaps, depth); + NvParameterized::Handle overlapsHandle(*cachedOverlapsAtDepth); + cachedOverlapsAtDepth->getParameterHandle("overlaps", overlapsHandle); + overlapsHandle.resizeArray(2*(int32_t)overlaps.size()); + for (uint32_t i = 0; i < overlaps.size(); ++i) + { + IntPair& pair = overlaps[i]; + + CachedOverlapsNS::IntPair_Type& ppair = cachedOverlapsAtDepth->overlaps.buf[2*i]; + ppair.i0 = pair.i0; + ppair.i1 = pair.i1; + + CachedOverlapsNS::IntPair_Type& ppairSymmetric = cachedOverlapsAtDepth->overlaps.buf[2*i+1]; + ppairSymmetric.i0 = pair.i1; + ppairSymmetric.i1 = pair.i0; + } + qsort(cachedOverlapsAtDepth->overlaps.buf, (uint32_t)cachedOverlapsAtDepth->overlaps.arraySizes[0], sizeof(IntPair), IntPair::compare); + + cachedOverlapsAtDepth->isCached = 1; + } + + return &cachedOverlapsAtDepth->overlaps; +} + +void DestructibleAssetImpl::calculateChunkOverlaps(physx::Array<IntPair>& overlaps, uint32_t depth) const +{ + const float padding = mParams->neighborPadding * (mParams->bounds.maximum - mParams->bounds.minimum).magnitude(); + + const PxTransform identityTM(PxIdentity); + const PxVec3 identityScale(1.0f); + + const uint32_t startIndex = mParams->firstChunkAtDepth.buf[depth]; + const uint32_t stopIndex = mParams->firstChunkAtDepth.buf[depth + 1]; + const uint32_t chunksAtDepth = stopIndex - startIndex; + + // Find AABB overlaps + physx::Array<BoundsRep> chunkBoundsReps; + chunkBoundsReps.reserve(chunksAtDepth); + for (uint32_t chunkIndex = startIndex; chunkIndex < stopIndex; ++chunkIndex) + { + BoundsRep& chunkBoundsRep = chunkBoundsReps.insert(); + chunkBoundsRep.aabb = getChunkActorLocalBounds(chunkIndex); + PX_ASSERT(!chunkBoundsRep.aabb.isEmpty()); + chunkBoundsRep.aabb.fattenFast(padding); + } + if (chunkBoundsReps.size() > 0) + { + boundsCalculateOverlaps(overlaps, Bounds3XYZ, &chunkBoundsReps[0], chunkBoundsReps.size(), sizeof(chunkBoundsReps[0])); + } + + // Now do detailed overlap test + uint32_t overlapCount = 0; + for (uint32_t overlapIndex = 0; overlapIndex < overlaps.size(); ++overlapIndex) + { + IntPair& AABBOverlap = overlaps[overlapIndex]; + AABBOverlap.i0 += startIndex; + AABBOverlap.i1 += startIndex; + if (chunksInProximity(*this, (uint16_t)AABBOverlap.i0, identityTM, identityScale, *this, (uint16_t)AABBOverlap.i1, identityTM, identityScale, 2 * padding)) + { + overlaps[overlapCount++] = AABBOverlap; + } + } + + overlaps.resize(overlapCount); +} + +void DestructibleAssetImpl::cacheChunkOverlapsUpToDepth(int32_t depth) +{ + if (mParams->depthCount < 1) + { + return; + } + + if (depth < 0) + { + depth = (int32_t)mParams->supportDepth; + } + + depth = PxMin(depth, (int32_t)mParams->depthCount - 1); + + for (uint32_t d = 0; d <= (uint32_t)depth; ++d) + { + getOverlapsAtDepth(d); + } + + for (uint32_t d = (uint32_t)depth + 1; d < (uint32_t)mParams->overlapsAtDepth.arraySizes[0]; ++d) + { + CachedOverlaps* cachedOverlaps = DYNAMIC_CAST(CachedOverlaps*)(mParams->overlapsAtDepth.buf[d]); + NvParameterized::Handle handle(*cachedOverlaps); + cachedOverlaps->getParameterHandle("overlaps", handle); + cachedOverlaps->resizeArray(handle, 0); + } +} + + +void DestructibleAssetImpl::clearChunkOverlaps(int32_t depth, bool keepCachedFlag) +{ + int32_t depthStart = (depth < 0) ? 0 : depth; + int32_t depthEnd = (depth < 0) ? mParams->overlapsAtDepth.arraySizes[0] : PxMin(depth+1, mParams->overlapsAtDepth.arraySizes[0]); + for (int32_t d = depthStart; d < depthEnd; ++d) + { + CachedOverlaps* cachedOverlaps = DYNAMIC_CAST(CachedOverlaps*)(mParams->overlapsAtDepth.buf[d]); + NvParameterized::Handle handle(*cachedOverlaps); + cachedOverlaps->getParameterHandle("overlaps", handle); + cachedOverlaps->resizeArray(handle, 0); + if (!keepCachedFlag) + { + cachedOverlaps->isCached = false; + } + } +} + + +void DestructibleAssetImpl::addChunkOverlaps(IntPair* supportGraphEdges, uint32_t supportGraphEdgeCount) +{ + if (supportGraphEdgeCount == 0) + return; + + uint32_t numChunks = (uint32_t)mParams->chunks.arraySizes[0]; + + Array< Array<IntPair> > overlapsAtDepth(mParams->depthCount); + + // store symmetric pairs at corresponding depth + for (uint32_t i = 0; i < supportGraphEdgeCount; ++i) + { + uint32_t chunkIndex0 = (uint32_t)supportGraphEdges[i].i0; + uint32_t chunkIndex1 = (uint32_t)supportGraphEdges[i].i1; + + if (chunkIndex0 >= numChunks || chunkIndex1 >= numChunks) + { + APEX_DEBUG_WARNING("Edge %i supportGraphEdges has indices (%i,%i), but only a total of %i chunks are provided. supportEdges will be ignored.", i, chunkIndex0, chunkIndex1, numChunks); + overlapsAtDepth.clear(); + break; + } + + const DestructibleAssetParametersNS::Chunk_Type& chunk0 = mParams->chunks.buf[chunkIndex0]; + const DestructibleAssetParametersNS::Chunk_Type& chunk1 = mParams->chunks.buf[chunkIndex1]; + + if (chunk0.depth != chunk1.depth) + { + APEX_DEBUG_WARNING("Support graph can only have edges between sibling chunks. supportEdges will be ignored."); + overlapsAtDepth.clear(); + break; + } + + PX_ASSERT(chunk0.depth < mParams->depthCount); + + IntPair pair; + pair.i0 = (int32_t)chunkIndex0; + pair.i1 = (int32_t)chunkIndex1; + overlapsAtDepth[chunk0.depth].pushBack(pair); + + pair.i0 = (int32_t)chunkIndex1; + pair.i1 = (int32_t)chunkIndex0; + overlapsAtDepth[chunk0.depth].pushBack(pair); + } + + if (overlapsAtDepth.size() == 0) + return; + + + // for each depth + for (uint32_t depth = 0; depth < overlapsAtDepth.size(); ++depth) + { + if (overlapsAtDepth[depth].size() > 0) + { + // sort overlaps pairs + qsort(&overlapsAtDepth[depth][0], overlapsAtDepth[depth].size(), sizeof(IntPair), IntPair::compare); + } + + if (overlapsAtDepth[depth].size() == 0) + continue; + + // resize parameterized array + uint32_t numCachedDepths = (uint32_t)mParams->overlapsAtDepth.arraySizes[0]; + if (depth >= numCachedDepths) + { + NvParameterized::Handle handle(*mParams); + mParams->getParameterHandle("overlapsAtDepth", handle); + mParams->resizeArray(handle, (int32_t)depth+1); + + NvParameterized::Traits* traits = GetInternalApexSDK()->getParameterizedTraits(); + for (uint32_t d = numCachedDepths; d < depth+1; ++d) + { + CachedOverlaps* cachedOverlaps = DYNAMIC_CAST(CachedOverlaps*)(traits->createNvParameterized(CachedOverlaps::staticClassName())); + mParams->overlapsAtDepth.buf[d] = cachedOverlaps; + cachedOverlaps->isCached = false; + } + } + + CachedOverlaps* cachedOverlapsAtDepth = DYNAMIC_CAST(CachedOverlaps*)(mParams->overlapsAtDepth.buf[depth]); + NvParameterized::Handle overlapsHandle(*cachedOverlapsAtDepth); + cachedOverlapsAtDepth->getParameterHandle("overlaps", overlapsHandle); + int32_t oldSize = cachedOverlapsAtDepth->overlaps.arraySizes[0]; + overlapsHandle.resizeArray(cachedOverlapsAtDepth->overlaps.arraySizes[0] + (int32_t)overlapsAtDepth[depth].size()); + + // merge new pairs into existing graph + bool ok = ApexMerge<IntPair>( (IntPair*)cachedOverlapsAtDepth->overlaps.buf, (uint32_t)oldSize, + &overlapsAtDepth[depth][0], overlapsAtDepth[depth].size(), + (IntPair*)cachedOverlapsAtDepth->overlaps.buf, (uint32_t)cachedOverlapsAtDepth->overlaps.arraySizes[0], + IntPair::compare); + + PX_UNUSED(ok); + PX_ASSERT(ok); + + // check for duplicates + if (cachedOverlapsAtDepth->overlaps.arraySizes[0] > 1) + { + Array<uint32_t> toRemove; + for (uint32_t j = 1; j < (uint32_t)cachedOverlapsAtDepth->overlaps.arraySizes[0]; ++j) + { + if (cachedOverlapsAtDepth->overlaps.buf[j].i1 == cachedOverlapsAtDepth->overlaps.buf[j-1].i1 && + cachedOverlapsAtDepth->overlaps.buf[j].i0 == cachedOverlapsAtDepth->overlaps.buf[j-1].i0) + { + toRemove.pushBack(j); + } + } + + // remove duplicates + toRemove.pushBack((uint32_t)cachedOverlapsAtDepth->overlaps.arraySizes[0]); // add guard + uint32_t shift = 0; + for (uint32_t j = 0; j < toRemove.size()-1; ++j) + { + ++shift; + for (uint32_t index = toRemove[j]+1; index < toRemove[j+1]; ++index) + { + cachedOverlapsAtDepth->overlaps.buf[index - shift] = cachedOverlapsAtDepth->overlaps.buf[index]; + } + } + NvParameterized::Handle overlapsHandle(*cachedOverlapsAtDepth); + cachedOverlapsAtDepth->getParameterHandle("overlaps", overlapsHandle); + overlapsHandle.resizeArray(cachedOverlapsAtDepth->overlaps.arraySizes[0] - (int32_t)shift); + } + + cachedOverlapsAtDepth->isCached = cachedOverlapsAtDepth->overlaps.arraySizes[0] > 0; + } +} + + +void DestructibleAssetImpl::removeChunkOverlaps(IntPair* supportGraphEdges, uint32_t numSupportGraphEdges, bool keepCachedFlagIfEmpty) +{ + Array< Array<uint32_t> > toRemoveAtDepth(mParams->depthCount); + for (uint32_t i = 0; i < numSupportGraphEdges; ++i) + { + CachedOverlapsNS::IntPair_Type& pair = (CachedOverlapsNS::IntPair_Type&)supportGraphEdges[i]; + + if (pair.i0 >= mParams->chunks.arraySizes[0] || pair.i1 >= mParams->chunks.arraySizes[0]) + continue; + + const DestructibleAssetParametersNS::Chunk_Type& chunk0 = mParams->chunks.buf[pair.i0]; + const DestructibleAssetParametersNS::Chunk_Type& chunk1 = mParams->chunks.buf[pair.i1]; + + if (chunk0.depth != chunk1.depth) + continue; + + if (chunk0.depth >= mParams->overlapsAtDepth.arraySizes[0]) + continue; + + if (chunk0.depth >= mParams->depthCount) + continue; + + CachedOverlaps* cachedOverlapsAtDepth = DYNAMIC_CAST(CachedOverlaps*)(mParams->overlapsAtDepth.buf[chunk0.depth]); + + // binary search for pair and add add to index to removal list + int32_t index = ApexFind(cachedOverlapsAtDepth->overlaps.buf, (uint32_t)cachedOverlapsAtDepth->overlaps.arraySizes[0], pair, IntPair::compare); + if (index != -1) + { + toRemoveAtDepth[chunk0.depth].pushBack((uint32_t)index); + } + + CachedOverlapsNS::IntPair_Type symmetricPair; + symmetricPair.i0 = pair.i1; + symmetricPair.i1 = pair.i0; + index = ApexFind(cachedOverlapsAtDepth->overlaps.buf, (uint32_t)cachedOverlapsAtDepth->overlaps.arraySizes[0], symmetricPair, IntPair::compare); + if (index != -1) + { + toRemoveAtDepth[chunk0.depth].pushBack((uint32_t)index); + } + } + + // go through removal list of each depth and shift remaining entries to overwrite the removed ones + for (uint32_t depth = 0; depth < toRemoveAtDepth.size(); ++depth) + { + CachedOverlaps* cachedOverlapsAtDepth = DYNAMIC_CAST(CachedOverlaps*)(mParams->overlapsAtDepth.buf[depth]); + toRemoveAtDepth[depth].pushBack((uint32_t)cachedOverlapsAtDepth->overlaps.arraySizes[0]); // add guard + uint32_t shift = 0; + for (uint32_t j = 1; j < toRemoveAtDepth[depth].size(); ++j) + { + ++shift; + for (uint32_t index = toRemoveAtDepth[depth][j-1]+1; index < toRemoveAtDepth[depth][j]; ++index) + { + cachedOverlapsAtDepth->overlaps.buf[index - shift] = cachedOverlapsAtDepth->overlaps.buf[index]; + } + } + + NvParameterized::Handle overlapsHandle(*cachedOverlapsAtDepth); + cachedOverlapsAtDepth->getParameterHandle("overlaps", overlapsHandle); + overlapsHandle.resizeArray(cachedOverlapsAtDepth->overlaps.arraySizes[0] - (int32_t)shift); + + if (!keepCachedFlagIfEmpty) + { + cachedOverlapsAtDepth->isCached = cachedOverlapsAtDepth->overlaps.arraySizes[0] > 0; + } + } +} + + +DestructibleAssetImpl::DestructibleAssetImpl(ModuleDestructibleImpl* inModule, DestructibleAsset* api, const char* name) : + mCrumbleAssetTracker(inModule->mSdk, EMITTER_AUTHORING_TYPE_NAME), + mDustAssetTracker(inModule->mSdk, EMITTER_AUTHORING_TYPE_NAME), + m_instancedChunkMeshCount(0) +{ + mApexDestructibleActorParams = 0; + init(); + + module = inModule; + mNxAssetApi = api; + mName = name; + + NvParameterized::Traits* traits = GetInternalApexSDK()->getParameterizedTraits(); + mParams = DYNAMIC_CAST(DestructibleAssetParameters*)(traits->createNvParameterized(DestructibleAssetParameters::staticClassName())); + mOwnsParams = mParams != NULL; + PX_ASSERT(mOwnsParams); +} + +DestructibleAssetImpl::DestructibleAssetImpl(ModuleDestructibleImpl* inModule, DestructibleAsset* api, NvParameterized::Interface* params, const char* name) + : mCrumbleAssetTracker(inModule->mSdk, EMITTER_AUTHORING_TYPE_NAME) + , mDustAssetTracker(inModule->mSdk, EMITTER_AUTHORING_TYPE_NAME) + , mRuntimeCookedConvexCount(0) + , m_instancedChunkMeshCount(0) +{ + mApexDestructibleActorParams = 0; + init(); + + module = inModule; + mNxAssetApi = api; + mName = name; + + mParams = DYNAMIC_CAST(DestructibleAssetParameters*)(params); + + // The pattern for NvParameterized assets is that the params pointer now belongs to the asset + mOwnsParams = true; + + // there's no deserialize, so init the ARMs + if (mParams->renderMeshAsset) + { + ApexSimpleString meshName = mName + ApexSimpleString("RenderMesh"); + module->mSdk->getInternalResourceProvider()->generateUniqueName(module->mSdk->getApexMeshNameSpace(), meshName); + + setRenderMeshAsset(static_cast<RenderMeshAsset*>(module->mSdk->createAsset(mParams->renderMeshAsset, meshName.c_str()))); + } + + // scatter meshes + bool scatterMeshAssetsValid = true; + physx::Array<RenderMeshAsset*> scatterMeshAssetArray((uint32_t)mParams->scatterMeshAssets.arraySizes[0]); + for (uint32_t i = 0; i < (uint32_t)mParams->scatterMeshAssets.arraySizes[0]; ++i) + { + if (i > 65535 || mParams->scatterMeshAssets.buf[i] == NULL) + { + scatterMeshAssetsValid = false; + break; + } + char suffix[20]; + sprintf(suffix, "ScatterMesh%d", i); + ApexSimpleString meshName = mName + ApexSimpleString(suffix); + module->mSdk->getInternalResourceProvider()->generateUniqueName(module->mSdk->getApexMeshNameSpace(), meshName); + scatterMeshAssetArray[i] = static_cast<RenderMeshAsset*>(module->mSdk->createAsset(mParams->scatterMeshAssets.buf[i], meshName.c_str())); + } + + bool success = false; + if (scatterMeshAssetsValid && mParams->scatterMeshAssets.arraySizes[0] > 0) + { + success = setScatterMeshAssets(&scatterMeshAssetArray[0], (uint32_t)mParams->scatterMeshAssets.arraySizes[0]); + } + if (!success) + { + for (uint32_t i = 0; i < scatterMeshAssetArray.size(); ++i) + { + if (scatterMeshAssetArray[i] != NULL) + { + scatterMeshAssetArray[i]->release(); + } + } + } + + bool hullWarningGiven = false; + + // Connect contained classes to referenced parameters + chunkConvexHulls.resize((uint32_t)mParams->chunkConvexHulls.arraySizes[0]); + for (uint32_t i = 0; i < chunkConvexHulls.size(); ++i) + { + chunkConvexHulls[i].init(mParams->chunkConvexHulls.buf[i]); + + // Fix convex hulls to account for adjacentFaces bug + if (chunkConvexHulls[i].mParams->adjacentFaces.arraySizes[0] != chunkConvexHulls[i].mParams->edges.arraySizes[0]) + { + chunkConvexHulls[i].buildFromPoints(chunkConvexHulls[i].mParams->vertices.buf, (uint32_t)chunkConvexHulls[i].mParams->vertices.arraySizes[0], + (uint32_t)chunkConvexHulls[i].mParams->vertices.elementSize); + if (!hullWarningGiven) + { + GetInternalApexSDK()->reportError(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, __FUNCTION__, + "Chunk convex hull data bad in asset %s, rebuilding. Asset should be re-exported.", name); + hullWarningGiven = true; + } + } + } + + m_currentInstanceBufferActorAllowance = mParams->initialDestructibleActorAllowanceForInstancing; + + physx::Array<uint16_t> tempPartToActorMap; + tempPartToActorMap.resize(renderMeshAsset->getPartCount(), 0xFFFF); + + m_instancedChunkMeshCount = 0; + + m_instancedChunkActorMap.resize((uint32_t)mParams->chunkInstanceInfo.arraySizes[0]); + for (uint32_t i = 0; i < (uint32_t)mParams->chunkInstanceInfo.arraySizes[0]; ++i) + { + uint16_t partIndex = mParams->chunkInstanceInfo.buf[i].partIndex; + if (tempPartToActorMap[partIndex] == 0xFFFF) + { + tempPartToActorMap[partIndex] = m_instancedChunkMeshCount++; + } + m_instancedChunkActorMap[i] = tempPartToActorMap[partIndex]; + } + + m_instancedChunkActorVisiblePart.resize(m_instancedChunkMeshCount); + for (uint32_t i = 0; i < (uint32_t)mParams->chunks.arraySizes[0]; ++i) + { + DestructibleAssetParametersNS::Chunk_Type& chunk = mParams->chunks.buf[i]; + if ((chunk.flags & DestructibleAssetImpl::Instanced) != 0) + { + uint16_t partIndex = mParams->chunkInstanceInfo.buf[chunk.meshPartIndex].partIndex; + m_instancedChunkActorVisiblePart[m_instancedChunkActorMap[chunk.meshPartIndex]] = partIndex; + } + } + + m_instancingRepresentativeActorIndex = -1; // not set + + reduceAccordingToLOD(); + + initializeAssetNameTable(); + + mStaticMaterialIDs.resize((uint32_t)mParams->staticMaterialNames.arraySizes[0]); + ResourceProviderIntl* resourceProvider = GetInternalApexSDK()->getInternalResourceProvider(); + ResID materialNS = GetInternalApexSDK()->getMaterialNameSpace(); + // Resolve material names using the NRP... + for (uint32_t i = 0; i < (uint32_t)mParams->staticMaterialNames.arraySizes[0]; ++i) + { + if (resourceProvider) + { + mStaticMaterialIDs[i] = resourceProvider->createResource(materialNS, mParams->staticMaterialNames.buf[i]); + } + else + { + mStaticMaterialIDs[i] = INVALID_RESOURCE_ID; + } + } +} + +DestructibleAssetImpl::~DestructibleAssetImpl() +{ + // Release named resources + ResourceProviderIntl* resourceProvider = GetInternalApexSDK()->getInternalResourceProvider(); + for (uint32_t i = 0 ; i < mStaticMaterialIDs.size() ; i++) + { + resourceProvider->releaseResource(mStaticMaterialIDs[i]); + } + + if (mParams != NULL && mOwnsParams) + { + mParams->destroy(); + } + mParams = NULL; + mOwnsParams = false; + + if (mApexDestructibleActorParams) + { + mApexDestructibleActorParams->destroy(); + mApexDestructibleActorParams = 0; + } + /* Assets that were forceloaded or loaded by actors will be automatically + * released by the ApexAssetTracker member destructors. + */ +} + +void DestructibleAssetImpl::init() +{ + module = NULL; + chunkOverlapCacheDepth = -1; + renderMeshAsset = NULL; + runtimeRenderMeshAsset = NULL; + mCollisionMeshes = NULL; + m_currentInstanceBufferActorAllowance = 0; + m_needsInstanceBufferDataResize = false; + m_needsInstanceBufferResize = false; + m_needsScatterMeshInstanceInfoCreation = false; +} + +uint32_t DestructibleAssetImpl::forceLoadAssets() +{ + uint32_t assetLoadedCount = 0; + + assetLoadedCount += mCrumbleAssetTracker.forceLoadAssets(); + assetLoadedCount += mDustAssetTracker.forceLoadAssets(); + + if (renderMeshAsset != NULL) + { + assetLoadedCount += renderMeshAsset->forceLoadAssets(); + } + + ResourceProviderIntl* nrp = GetInternalApexSDK()->getInternalResourceProvider(); + ResID materialNS = GetInternalApexSDK()->getMaterialNameSpace(); + for (uint32_t i = 0; i < mStaticMaterialIDs.size(); i++) + { + if (!nrp->checkResource(materialNS, mParams->staticMaterialNames.buf[i])) + { + /* we know for SURE that createResource() has already been called, so just getResource() */ + nrp->getResource(mStaticMaterialIDs[i]); + assetLoadedCount++; + } + } + + return assetLoadedCount; +} + +void DestructibleAssetImpl::initializeAssetNameTable() +{ + if (mParams->dustEmitterName && *mParams->dustEmitterName) + { + mDustAssetTracker.addAssetName(mParams->dustEmitterName, false); + } + + if (mParams->crumbleEmitterName && *mParams->crumbleEmitterName) + { + mCrumbleAssetTracker.addAssetName(mParams->crumbleEmitterName, false); + } +} + +void DestructibleAssetImpl::cleanup() +{ + // Release internal RenderMesh, preview instances, and authoring instance + + while (m_previewList.getSize()) + { + DestructiblePreviewProxy* proxy = DYNAMIC_CAST(DestructiblePreviewProxy*)(m_previewList.getResource(m_previewList.getSize() - 1)); + PX_ASSERT(proxy != NULL); + if (proxy == NULL) + { + m_previewList.remove(m_previewList.getSize() - 1); // To avoid an infinite loop + } + else + { + proxy->release(); + } + } + + m_previewList.clear(); + m_destructibleList.clear(); + + setRenderMeshAsset(NULL); + + // release chunk instance render resources + m_chunkInstanceBufferDataLock.lock(); + m_needsInstanceBufferResize = false; + m_chunkInstanceBufferData.clear(); + updateChunkInstanceRenderResources(false, NULL); + m_chunkInstanceBufferDataLock.unlock(); + + setScatterMeshAssets(NULL, 0); + + m_instancedChunkActorMap.resize(0); + m_instancedChunkActorVisiblePart.resize(0); + + if (module->mCachedData != NULL) + { + module->mCachedData->clearAssetCollisionSet(*this); + } +} + +void DestructibleAssetImpl::prepareForNewInstance() +{ + if (m_currentInstanceBufferActorAllowance < m_destructibleList.getSize() + 1) // Add 1 to predict new actor + { + // This loop should only be hit once + do + { + m_currentInstanceBufferActorAllowance = m_currentInstanceBufferActorAllowance > 0 ? 2*m_currentInstanceBufferActorAllowance : 1; + m_needsInstanceBufferDataResize = true; + } + while (m_currentInstanceBufferActorAllowance < m_destructibleList.getSize()); // Add 1 to predict new actor + } +} + + + +void DestructibleAssetImpl::resetInstanceData() +{ + PX_PROFILE_ZONE("DestructibleAsset::resetInstanceData", GetInternalApexSDK()->getContextId()); + + m_chunkInstanceBufferDataLock.lock(); + m_chunkInstanceBufferData.resize(m_instancedChunkMeshCount); + if (m_needsInstanceBufferDataResize) + { + // + // reserve the right amount of memory in the per chunk mesh arrays + // + for (uint32_t index = 0; index < m_instancedChunkMeshCount; ++index) + { + if (m_currentInstanceBufferActorAllowance > 0) + { + // Find out how many potential instances there are + uint32_t maxInstanceCount = 0; + for (int32_t i = 0; i < mParams->chunkInstanceInfo.arraySizes[0]; ++i) + { + if (mParams->chunkInstanceInfo.buf[i].partIndex == m_instancedChunkActorVisiblePart[index]) + { + maxInstanceCount += m_currentInstanceBufferActorAllowance; + } + } + + // Instance buffer data + m_chunkInstanceBufferData[index].reserve(maxInstanceCount); + } + + m_chunkInstanceBufferData[index].resize(0); + } + + m_needsInstanceBufferDataResize = false; + m_needsInstanceBufferResize = true; + } + else + { + for (uint32_t j = 0; j < m_chunkInstanceBufferData.size(); ++j) + { + m_chunkInstanceBufferData[j].resize(0); + } + } + m_chunkInstanceBufferDataLock.unlock(); + + + for (uint32_t j = 0; j < m_scatterMeshInstanceInfo.size(); ++j) + { + m_scatterMeshInstanceInfo[j].m_instanceBufferData.resize(0); + } + m_instancingRepresentativeActorIndex = -1; // not set +} + + +template <class ParamType> +DestructibleActor* createDestructibleActorImpl(ParamType& params, + DestructibleAssetImpl& destructibleAsset, + ResourceList& destructibleList, + DestructibleScene* destructibleScene) +{ + if (NULL == destructibleScene) + return NULL; + + destructibleAsset.prepareForNewInstance(); + + return PX_NEW(DestructibleActorProxy)(params, destructibleAsset, destructibleList, *destructibleScene); +} + +DestructibleActor* DestructibleAssetImpl::createDestructibleActorFromDeserializedState(NvParameterized::Interface* params, Scene& scene) +{ + PX_PROFILE_ZONE("DestructibleCreateActor", GetInternalApexSDK()->getContextId()); + + if (NULL == params || !isValidForActorCreation(*params, scene)) + return NULL; + + return createDestructibleActorImpl(params, *this, m_destructibleList, module->getDestructibleScene(scene)); +} + +DestructibleActor* DestructibleAssetImpl::createDestructibleActor(const NvParameterized::Interface& params, Scene& scene) +{ + PX_PROFILE_ZONE("DestructibleCreateActor", GetInternalApexSDK()->getContextId()); + + return createDestructibleActorImpl(params, *this, m_destructibleList, module->getDestructibleScene(scene)); +} + +void DestructibleAssetImpl::releaseDestructibleActor(DestructibleActor& nxactor) +{ + DestructibleActorProxy* proxy = DYNAMIC_CAST(DestructibleActorProxy*)(&nxactor); + proxy->destroy(); +} + +bool DestructibleAssetImpl::setRenderMeshAsset(RenderMeshAsset* newRenderMeshAsset) +{ + if (newRenderMeshAsset == renderMeshAsset) + { + return false; + } + + for (uint32_t i = 0; i < m_instancedChunkRenderMeshActors.size(); ++i) + { + if (m_instancedChunkRenderMeshActors[i] != NULL) + { + m_instancedChunkRenderMeshActors[i]->release(); + m_instancedChunkRenderMeshActors[i] = NULL; + } + } + + if (renderMeshAsset != NULL) + { + if(mOwnsParams && mParams != NULL) + { + // set isReferenced to false, so that the parameterized object + // for the render mesh asset is destroyed in renderMeshAsset->release + NvParameterized::ErrorType e; + if (mParams->renderMeshAsset != NULL) + { + NvParameterized::Handle h(*mParams->renderMeshAsset); + e = mParams->renderMeshAsset->getParameterHandle("isReferenced", h); + PX_ASSERT(e == NvParameterized::ERROR_NONE); + if (e == NvParameterized::ERROR_NONE) + { + h.setParamBool(false); + } + mParams->renderMeshAsset = NULL; + } + } + renderMeshAsset->release(); + } + + renderMeshAsset = newRenderMeshAsset; + if (renderMeshAsset != NULL) + { + mParams->renderMeshAsset = (NvParameterized::Interface*)renderMeshAsset->getAssetNvParameterized(); + NvParameterized::ErrorType e; + NvParameterized::Handle h(*mParams->renderMeshAsset); + e = mParams->renderMeshAsset->getParameterHandle("isReferenced", h); + PX_ASSERT(e == NvParameterized::ERROR_NONE); + if (e == NvParameterized::ERROR_NONE) + { + h.setParamBool(true); + } + + for (uint32_t i = 0; i < m_instancedChunkRenderMeshActors.size(); ++i) + { + // Create actor + RenderMeshActorDesc renderableMeshDesc; + renderableMeshDesc.maxInstanceCount = m_chunkInstanceBufferData[i].capacity(); + renderableMeshDesc.renderWithoutSkinning = true; + renderableMeshDesc.visible = false; + m_instancedChunkRenderMeshActors[i] = newRenderMeshAsset->createActor(renderableMeshDesc); + m_instancedChunkRenderMeshActors[i]->setInstanceBuffer(m_chunkInstanceBuffers[i]); + m_instancedChunkRenderMeshActors[i]->setVisibility(true, m_instancedChunkActorVisiblePart[i]); + m_instancedChunkRenderMeshActors[i]->setReleaseResourcesIfNothingToRender(false); + } + } + + return true; +} + +bool DestructibleAssetImpl::setScatterMeshAssets(RenderMeshAsset** scatterMeshAssetArray, uint32_t scatterMeshAssetArraySize) +{ + if (scatterMeshAssetArray == NULL && scatterMeshAssetArraySize > 0) + { + return false; + } + + for (uint32_t i = 0; i < scatterMeshAssetArraySize; ++i) + { + if (scatterMeshAssetArray[i] == NULL) + { + return false; + } + } + + // First clear instance information + m_scatterMeshInstanceInfo.resize(0); // Ensure we delete all instanced actors + m_scatterMeshInstanceInfo.resize(scatterMeshAssetArraySize); + + // Clear out scatter mesh assets, including parameterized data + for (int32_t i = 0; mParams && (i < mParams->scatterMeshAssets.arraySizes[0]); ++i) + { + if (mParams->scatterMeshAssets.buf[i] != NULL) + { + NvParameterized::ErrorType e; + NvParameterized::Handle h(*mParams->scatterMeshAssets.buf[i]); + e = mParams->scatterMeshAssets.buf[i]->getParameterHandle("isReferenced", h); + PX_ASSERT(e == NvParameterized::ERROR_NONE); + if (e == NvParameterized::ERROR_NONE) + { + h.setParamBool(false); + } + mParams->scatterMeshAssets.buf[i] = NULL; + } + } + + for (uint32_t i = 0; i < scatterMeshAssets.size(); ++i) + { + if (scatterMeshAssets[i] != NULL) + { + scatterMeshAssets[i]->release(); + scatterMeshAssets[i] = NULL; + } + } + + if (mParams != NULL) + { + scatterMeshAssets.resize(scatterMeshAssetArraySize, NULL); + NvParameterized::Handle h(*mParams, "scatterMeshAssets"); + h.resizeArray((int32_t)scatterMeshAssetArraySize); + + for (uint32_t i = 0; i < scatterMeshAssetArraySize; ++i) + { + // Create new asset + scatterMeshAssets[i] = scatterMeshAssetArray[i]; + mParams->scatterMeshAssets.buf[i] = (NvParameterized::Interface*)scatterMeshAssets[i]->getAssetNvParameterized(); + NvParameterized::ErrorType e; + NvParameterized::Handle h(*mParams->scatterMeshAssets.buf[i]); + e = mParams->scatterMeshAssets.buf[i]->getParameterHandle("isReferenced", h); + PX_ASSERT(e == NvParameterized::ERROR_NONE); + if (e == NvParameterized::ERROR_NONE) + { + h.setParamBool(true); + } + } + + m_needsScatterMeshInstanceInfoCreation = true; + } + + + return true; +} + +void DestructibleAssetImpl::createScatterMeshInstanceInfo() +{ + if (!m_needsScatterMeshInstanceInfoCreation) + return; + + m_needsScatterMeshInstanceInfoCreation = false; + + UserRenderResourceManager* rrm = GetInternalApexSDK()->getUserRenderResourceManager(); + + physx::Array<uint32_t> totalInstanceCounts(scatterMeshAssets.size(), 0); + for (int32_t i = 0; i < mParams->scatterMeshIndices.arraySizes[0]; ++i) + { + const uint8_t scatterMeshIndex = mParams->scatterMeshIndices.buf[i]; + if (scatterMeshIndex < scatterMeshAssets.size()) + { + ++totalInstanceCounts[scatterMeshIndex]; + } + } + + for (uint32_t i = 0; i < scatterMeshAssets.size(); ++i) + { + // Create instanced info + ScatterMeshInstanceInfo& info = m_scatterMeshInstanceInfo[i]; + RenderMeshActorDesc renderableMeshDesc; + renderableMeshDesc.maxInstanceCount = totalInstanceCounts[i]; + renderableMeshDesc.renderWithoutSkinning = true; + renderableMeshDesc.visible = true; + info.m_actor = scatterMeshAssets[i]->createActor(renderableMeshDesc); + + // Create instance buffer + info.m_instanceBuffer = NULL; + info.m_IBSize = totalInstanceCounts[i]; + if (totalInstanceCounts[i] > 0) + { + UserRenderInstanceBufferDesc instanceBufferDesc = getScatterMeshInstanceBufferDesc(); + instanceBufferDesc.maxInstances = totalInstanceCounts[i]; + info.m_instanceBuffer = rrm->createInstanceBuffer(instanceBufferDesc); + } + + // Instance buffer data + info.m_instanceBufferData.reset(); + info.m_instanceBufferData.reserve(totalInstanceCounts[i]); + + info.m_actor->setInstanceBuffer(info.m_instanceBuffer); + info.m_actor->setReleaseResourcesIfNothingToRender(false); + } +} + +UserRenderInstanceBufferDesc DestructibleAssetImpl::getScatterMeshInstanceBufferDesc() +{ + UserRenderInstanceBufferDesc instanceBufferDesc; + instanceBufferDesc.hint = RenderBufferHint::DYNAMIC; + instanceBufferDesc.semanticOffsets[RenderInstanceLayoutElement::POSITION_FLOAT3] = ScatterInstanceBufferDataElement::translationOffset(); + instanceBufferDesc.semanticOffsets[RenderInstanceLayoutElement::ROTATION_SCALE_FLOAT3x3] = ScatterInstanceBufferDataElement::scaledRotationOffset(); + instanceBufferDesc.semanticOffsets[RenderInstanceLayoutElement::DENSITY_FLOAT1] = ScatterInstanceBufferDataElement::alphaOffset(); + instanceBufferDesc.stride = sizeof(ScatterInstanceBufferDataElement); + + return instanceBufferDesc; +} + +void DestructibleAssetImpl::updateChunkInstanceRenderResources(bool rewriteBuffers, void* userRenderData) +{ + PX_PROFILE_ZONE("DestructibleAsset::updateChunkInstanceRenderResources", GetInternalApexSDK()->getContextId()); + + UserRenderResourceManager* rrm = GetInternalApexSDK()->getUserRenderResourceManager(); + + Mutex::ScopedLock scopeLock(m_chunkInstanceBufferDataLock); + + uint32_t oldCount = m_chunkInstanceBuffers.size(); + uint32_t count = m_chunkInstanceBufferData.size(); + + // + // release resources + // + // release all on resize for recreation lateron + uint32_t startIndexForRelease = m_needsInstanceBufferResize ? 0 : count; + for (uint32_t i = startIndexForRelease; i < oldCount; ++i) + { + if (m_instancedChunkRenderMeshActors[i] != NULL) + { + m_instancedChunkRenderMeshActors[i]->release(); + m_instancedChunkRenderMeshActors[i] = NULL; + } + if (m_chunkInstanceBuffers[i] != NULL) + { + rrm->releaseInstanceBuffer(*m_chunkInstanceBuffers[i]); + m_chunkInstanceBuffers[i] = NULL; + } + } + + // resize and init arrays + m_chunkInstanceBuffers.resize(count); + m_instancedChunkRenderMeshActors.resize(count); + for (uint32_t index = oldCount; index < count; ++index) + { + m_instancedChunkRenderMeshActors[index] = NULL; + m_chunkInstanceBuffers[index] = NULL; + } + + + // + // create resources when needed + // + + for (uint32_t index = 0; index < count; ++index) + { + // if m_chunkInstanceBufferData[index] contains any data there's an instance to render + if (m_chunkInstanceBuffers[index] == NULL && m_chunkInstanceBufferData[index].size() > 0) + { + // Find out how many potential instances there are + uint32_t maxInstanceCount = 0; + for (int32_t i = 0; i < mParams->chunkInstanceInfo.arraySizes[0]; ++i) + { + if (mParams->chunkInstanceInfo.buf[i].partIndex == m_instancedChunkActorVisiblePart[index]) + { + maxInstanceCount += m_currentInstanceBufferActorAllowance; + } + } + + // Create instance buffer + UserRenderInstanceBufferDesc instanceBufferDesc; + instanceBufferDesc.maxInstances = maxInstanceCount; + instanceBufferDesc.hint = RenderBufferHint::DYNAMIC; + instanceBufferDesc.semanticOffsets[RenderInstanceLayoutElement::POSITION_FLOAT3] = ChunkInstanceBufferDataElement::translationOffset(); + instanceBufferDesc.semanticOffsets[RenderInstanceLayoutElement::ROTATION_SCALE_FLOAT3x3] = ChunkInstanceBufferDataElement::scaledRotationOffset(); + instanceBufferDesc.semanticOffsets[RenderInstanceLayoutElement::UV_OFFSET_FLOAT2] = ChunkInstanceBufferDataElement::uvOffsetOffset(); + instanceBufferDesc.semanticOffsets[RenderInstanceLayoutElement::LOCAL_OFFSET_FLOAT3] = ChunkInstanceBufferDataElement::localOffsetOffset(); + instanceBufferDesc.stride = sizeof(ChunkInstanceBufferDataElement); + m_chunkInstanceBuffers[index] = rrm->createInstanceBuffer(instanceBufferDesc); + + // Create actor + if (renderMeshAsset != NULL) + { + PX_ASSERT(m_instancedChunkRenderMeshActors[index] == NULL); + + RenderMeshActorDesc renderableMeshDesc; + renderableMeshDesc.maxInstanceCount = maxInstanceCount; + renderableMeshDesc.renderWithoutSkinning = true; + renderableMeshDesc.visible = false; + m_instancedChunkRenderMeshActors[index] = renderMeshAsset->createActor(renderableMeshDesc); + m_instancedChunkRenderMeshActors[index]->setInstanceBuffer(m_chunkInstanceBuffers[index]); + m_instancedChunkRenderMeshActors[index]->setVisibility(true, m_instancedChunkActorVisiblePart[index]); + m_instancedChunkRenderMeshActors[index]->setReleaseResourcesIfNothingToRender(false); + } + } + + // + // update with new data + // + PX_ASSERT(index < m_chunkInstanceBufferData.size()); + RenderMeshActorIntl* renderMeshActor = (RenderMeshActorIntl*)m_instancedChunkRenderMeshActors[index]; + if (renderMeshActor != NULL) + { + RenderInstanceBufferData data; + const uint32_t instanceBufferSize = m_chunkInstanceBufferData[index].size(); + + if (instanceBufferSize > 0) + { + m_chunkInstanceBuffers[index]->writeBuffer(&m_chunkInstanceBufferData[index][0], 0, instanceBufferSize); + } + + renderMeshActor->setInstanceBufferRange(0, instanceBufferSize); + renderMeshActor->updateRenderResources(false, rewriteBuffers, userRenderData); + } + } + + m_needsInstanceBufferResize = false; +} + +bool DestructibleAssetImpl::setPlatformMaxDepth(PlatformTag platform, uint32_t maxDepth) +{ + bool isExistingPlatform = false; + for (Array<PlatformKeyValuePair>::Iterator iter = m_platformFractureDepthMap.begin(); iter != m_platformFractureDepthMap.end(); ++iter) + { + if (nvidia::strcmp(iter->key, platform) == 0) + { + isExistingPlatform = true; + iter->val = maxDepth; //overwrite if existing + break; + } + } + if (!isExistingPlatform) + { + m_platformFractureDepthMap.pushBack(PlatformKeyValuePair(platform, maxDepth)); + } + return maxDepth < mParams->depthCount - 1; //depthCount == 1 => unfractured mesh +} + +bool DestructibleAssetImpl::removePlatformMaxDepth(PlatformTag platform) +{ + bool isExistingPlatform = false; + for (uint32_t index = 0; index < m_platformFractureDepthMap.size(); ++index) + { + if (nvidia::strcmp(m_platformFractureDepthMap[index].key, platform) == 0) + { + isExistingPlatform = true; + m_platformFractureDepthMap.remove(index); //yikes! for lack of a map... + break; + } + } + return isExistingPlatform; +} + +bool DestructibleAssetImpl::setDepthCount(uint32_t targetDepthCount) const +{ + if (mParams->depthCount <= targetDepthCount) + { + return false; + } + + int newChunkCount = mParams->chunks.arraySizes[0]; + for (int i = newChunkCount; i--;) + { + if (mParams->chunks.buf[i].depth >= targetDepthCount) + { + newChunkCount = i; + } + else if (mParams->chunks.buf[i].depth == targetDepthCount - 1) + { + // These chunks must have no children + DestructibleAssetParametersNS::Chunk_Type& chunk = mParams->chunks.buf[i]; + chunk.numChildren = 0; + chunk.firstChildIndex = DestructibleAssetImpl::InvalidChunkIndex; + chunk.flags &= ~(uint16_t)DescendantUnfractureable; + } + else + { + break; + } + } + + NvParameterized::Handle handle(*mParams); + mParams->getParameterHandle("chunks", handle); + mParams->resizeArray(handle, newChunkCount); + + mParams->getParameterHandle("overlapsAtDepth", handle); + int size; + mParams->getArraySize(handle, size); + if ((int)targetDepthCount < size) + { + mParams->resizeArray(handle, (int32_t)targetDepthCount); + } + mParams->getParameterHandle("firstChunkAtDepth", handle); + mParams->resizeArray(handle, (int32_t)targetDepthCount + 1); + + mParams->depthCount = targetDepthCount; + + return true; +} + +bool DestructibleAssetImpl::prepareForPlatform(nvidia::apex::PlatformTag platform) const +{ + bool isExistingPlatform = false; + bool isDepthLimitChanged = false; + for (Array<PlatformKeyValuePair>::ConstIterator kIter = m_platformFractureDepthMap.begin(); kIter != m_platformFractureDepthMap.end(); ++kIter) + { + if (nvidia::strcmp(kIter->key, platform) == 0) + { + isExistingPlatform = true; + isDepthLimitChanged = setDepthCount(kIter->val + 1); //targetDepthCount == 1 => unfractured mesh + break; + } + } + //if (!isExistingPlatform) {/*keep all depths, behaviour by default*/} + return (isExistingPlatform & isDepthLimitChanged); +} + +void DestructibleAssetImpl::reduceAccordingToLOD() +{ + if (module == NULL) + { + return; + } + + const uint32_t targetDepthCount = mParams->originalDepthCount > module->m_maxChunkDepthOffset ? mParams->originalDepthCount - module->m_maxChunkDepthOffset : 1; + + setDepthCount(targetDepthCount); +} + +void DestructibleAssetImpl::getStats(DestructibleAssetStats& stats) const +{ + memset(&stats, 0, sizeof(DestructibleAssetStats)); + + if (renderMeshAsset) + { + renderMeshAsset->getStats(stats.renderMeshAssetStats); + } + + // BRG - to do - need a way of getting the serialized size + stats.totalBytes = 0; + + stats.chunkCount = (uint32_t)mParams->chunks.arraySizes[0]; + stats.chunkBytes = mParams->chunks.arraySizes[0] * sizeof(DestructibleAssetParametersNS::Chunk_Type); + + uint32_t maxEdgeCount = 0; + + for (uint16_t chunkIndex = 0; chunkIndex < getChunkCount(); ++chunkIndex) + { + for (uint32_t hullIndex = getChunkHullIndexStart(chunkIndex); hullIndex < getChunkHullIndexStop(chunkIndex); ++hullIndex) + { + const ConvexHullImpl& hullData = chunkConvexHulls[hullIndex]; + const uint32_t hullDataBytes = hullData.getVertexCount() * sizeof(PxVec3) + + hullData.getUniquePlaneNormalCount() * 4 * sizeof(float) + + hullData.getUniquePlaneNormalCount() * sizeof(float) + + hullData.getEdgeCount() * sizeof(uint32_t); + stats.chunkHullDataBytes += hullDataBytes; + stats.chunkBytes += hullDataBytes; + + // Get cooked convex mesh stats + uint32_t numVerts = 0; + uint32_t numPolys = 0; + const uint32_t cookedHullDataBytes = hullData.calculateCookedSizes(numVerts, numPolys, true); + stats.maxHullVertexCount = PxMax(stats.maxHullVertexCount, numVerts); + stats.maxHullFaceCount = PxMax(stats.maxHullFaceCount, numPolys); + const uint32_t numEdges = numVerts + numPolys - 2; + if (numEdges > maxEdgeCount) + { + maxEdgeCount = numEdges; + stats.chunkWithMaxEdgeCount = chunkIndex; + } + + stats.chunkBytes += cookedHullDataBytes; + } + } + + stats.runtimeCookedConvexCount = mRuntimeCookedConvexCount; + /* To do - count collision data in ApexScene! */ +} + +void DestructibleAssetImpl::applyTransformation(const PxMat44& transformation, float scale) +{ + /* For now, we'll just clear the current cached streams at scale. */ + /* transform and scale the PxConvexMesh(es) */ + if (mParams->collisionData) + { + APEX_DEBUG_WARNING("Cached collision data is already present, removing"); + mParams->collisionData->destroy(); + mParams->collisionData = NULL; +#if 0 + DestructibleAssetCollisionDataSet* cds = + DYNAMIC_CAST(DestructibleAssetCollisionDataSet*)(mParams->collisionData); + + for (int i = 0; i < cds->meshCookedCollisionStreamsAtScale.arraySizes[0]; ++i) + { + PX_ASSERT(cds->meshCookedCollisionStreamsAtScale.buf[i]); + + MeshCookedCollisionStreamsAtScale* meshStreamsAtScale = + DYNAMIC_CAST(MeshCookedCollisionStreamsAtScale*)(cds->meshCookedCollisionStreamsAtScale.buf[i]); + for (int j = 0; j < meshStreamsAtScale->meshCookedCollisionStreams.arraySizes[0]; ++j) + { + PX_ASSERT(meshStreamsAtScale->meshCookedCollisionStreams.buf[i]); + + MeshCookedCollisionStream* meshStreamParams = + DYNAMIC_CAST(MeshCookedCollisionStream*)(meshStreamsAtScale->meshCookedCollisionStreams.buf[i]); + + /* stream it into the physx sdk */ + nvidia::PsMemoryBuffer memStream(meshStreamParams->bytes.buf, meshStreamParams->bytes.arraySizes[0]); + memStream.setEndianMode(PxFileBuf::ENDIAN_NONE); + PxStreamFromFileBuf nvs(memStream); + PxConvexMesh* convexMesh = GetApexSDK()->getPhysXSDK()->createConvexMesh(nxs); + + PxConvexMeshDesc meshDesc; + convexMesh->saveToDesc(meshDesc); + meshDesc. + + uint32_t submeshCount = convexMesh->getSubmeshCount(); + (void)submeshCount; + } + } +#endif + } + + /* chunk surface normal */ + for (int i = 0; i < mParams->chunks.arraySizes[0]; ++i) + { + PX_ASSERT(mParams->chunks.buf); + + DestructibleAssetParametersNS::Chunk_Type* chunk = &(mParams->chunks.buf[i]); + + chunk->surfaceNormal = transformation.rotate(chunk->surfaceNormal); + } + + /* bounds */ + PX_ASSERT(!mParams->bounds.isEmpty()); + mParams->bounds.minimum *= scale; + mParams->bounds.maximum *= scale; + if (scale < 0.0f) + { + nvidia::swap(mParams->bounds.minimum, mParams->bounds.maximum); + } + mParams->bounds = PxBounds3::transformSafe(PxTransform(transformation), mParams->bounds); + + /* chunk convex hulls */ + for (int i = 0; i < mParams->chunkConvexHulls.arraySizes[0]; ++i) + { + PX_ASSERT(mParams->chunkConvexHulls.buf[i]); + ConvexHullImpl chunkHull; + chunkHull.mParams = DYNAMIC_CAST(ConvexHullParameters*)(mParams->chunkConvexHulls.buf[i]); + chunkHull.mOwnsParams = false; + chunkHull.applyTransformation(transformation, scale); + } + + /* render mesh (just apply to both the asset and author if they both exist) */ + if (renderMeshAsset) + { + reinterpret_cast<RenderMeshAssetIntl*>(renderMeshAsset)->applyTransformation(transformation, scale); + } + + /* scatter meshes */ + const PxMat33 m33(transformation.column0.getXYZ(), transformation.column1.getXYZ(), transformation.column2.getXYZ()); + for (int i = 0; i < mParams->scatterMeshTransforms.arraySizes[0]; ++i) + { + PxMat44& tm = mParams->scatterMeshTransforms.buf[i]; + PxMat33 tm33(tm.column0.getXYZ(), tm.column1.getXYZ(), tm.column2.getXYZ()); + + tm33 = m33*tm33; + tm33 *= scale; + const PxVec3 pos = transformation.getPosition() + scale*(m33*tm.getPosition()); + tm.column0 = PxVec4(tm33.column0, 0); + tm.column1 = PxVec4(tm33.column1, 0); + tm.column2 = PxVec4(tm33.column2, 0); + tm.setPosition(pos); + } + if (m33.getDeterminant() * scale < 0.0f) + { + for (uint32_t i = 0; i < scatterMeshAssets.size(); ++i) + { + RenderMeshAsset* scatterMesh = scatterMeshAssets[i]; + if (scatterMesh != NULL) + { + reinterpret_cast<RenderMeshAssetIntl*>(scatterMesh)->reverseWinding(); + } + } + } +} + +void DestructibleAssetImpl::applyTransformation(const PxMat44& transformation) +{ + /* For now, we'll just clear the current cached streams at scale. */ + /* transform and scale the PxConvexMesh(es) */ + if (mParams->collisionData) + { + APEX_DEBUG_WARNING("Cached collision data is already present, removing"); + mParams->collisionData->destroy(); + mParams->collisionData = NULL; + } + + Cof44 cofTM(transformation); + + /* chunk surface normal */ + for (int i = 0; i < mParams->chunks.arraySizes[0]; ++i) + { + PX_ASSERT(mParams->chunks.buf); + + DestructibleAssetParametersNS::Chunk_Type* chunk = &(mParams->chunks.buf[i]); + + chunk->surfaceNormal = cofTM.getBlock33().transform(chunk->surfaceNormal); + chunk->surfaceNormal.normalize(); + } + + /* bounds */ + PX_ASSERT(!mParams->bounds.isEmpty()); + mParams->bounds = PxBounds3::transformSafe(PxTransform(transformation), mParams->bounds); + + /* chunk convex hulls */ + for (int i = 0; i < mParams->chunkConvexHulls.arraySizes[0]; ++i) + { + PX_ASSERT(mParams->chunkConvexHulls.buf[i]); + ConvexHullImpl chunkHull; + chunkHull.mParams = DYNAMIC_CAST(ConvexHullParameters*)(mParams->chunkConvexHulls.buf[i]); + chunkHull.mOwnsParams = false; + chunkHull.applyTransformation(transformation); + } + + /* render mesh (just apply to both the asset and author if they both exist) */ + if (renderMeshAsset) + { + reinterpret_cast<RenderMeshAssetIntl*>(renderMeshAsset)->applyTransformation(transformation, 1.0f); // This transformation function properly handles non-uniform scales + } +} + +void DestructibleAssetImpl::traceSurfaceBoundary(physx::Array<PxVec3>& outPoints, uint16_t chunkIndex, const PxTransform& localToWorldRT, const PxVec3& scale, + float spacing, float jitter, float surfaceDistance, uint32_t maxPoints) +{ + outPoints.resize(0); + + // Removing this function's implementation for now + + PX_UNUSED(chunkIndex); + PX_UNUSED(localToWorldRT); + PX_UNUSED(scale); + PX_UNUSED(spacing); + PX_UNUSED(jitter); + PX_UNUSED(surfaceDistance); + PX_UNUSED(maxPoints); +} + + +bool DestructibleAssetImpl::rebuildCollisionGeometry(uint32_t partIndex, const DestructibleGeometryDesc& geometryDesc) +{ +#ifdef WITHOUT_APEX_AUTHORING + PX_UNUSED(partIndex); + PX_UNUSED(geometryDesc); + APEX_DEBUG_WARNING("DestructibleAsset::rebuildCollisionGeometry is not available in release builds."); + return false; +#else + NvParameterized::Handle handle(*mParams); + mParams->getParameterHandle("chunkConvexHulls", handle); + NvParameterized::Traits* traits = GetInternalApexSDK()->getParameterizedTraits(); + + const uint32_t startHullIndex = mParams->chunkConvexHullStartIndices.buf[partIndex]; + uint32_t hullCount = geometryDesc.convexHullCount; + const nvidia::ExplicitHierarchicalMesh::ConvexHull** convexHulls = geometryDesc.convexHulls; + physx::Array<PartConvexHullProxy*> collision; + if (hullCount == 0 && geometryDesc.collisionVolumeDesc != NULL) + { + physx::Array<PxVec3> vertices; + physx::Array<uint32_t> indices; + gatherPartMesh(vertices, indices, renderMeshAsset, partIndex); + buildCollisionGeometry(collision, *geometryDesc.collisionVolumeDesc, vertices.begin(), vertices.size(), sizeof(PxVec3), indices.begin(), indices.size()); + convexHulls = (const nvidia::ExplicitHierarchicalMesh::ConvexHull**)collision.begin(); + hullCount = collision.size(); + } + const uint32_t oldStopHullIndex = mParams->chunkConvexHullStartIndices.buf[partIndex + 1]; + const uint32_t oldHullCount = oldStopHullIndex - startHullIndex; + const uint32_t stopHullIndex = startHullIndex + hullCount; + const int32_t hullCountDelta = (int32_t)(hullCount - oldHullCount); + + // Adjust start indices + for (uint32_t i = partIndex+1; i < (uint32_t)mParams->chunkConvexHullStartIndices.arraySizes[0]; ++i) + { + mParams->chunkConvexHullStartIndices.buf[i] += hullCountDelta; + } + const uint32_t totalHullCount = (uint32_t)mParams->chunkConvexHullStartIndices.buf[mParams->chunkConvexHullStartIndices.arraySizes[0]-1]; + + // Eliminate hulls if the count has decreased + if (hullCountDelta < 0) + { + for (uint32_t index = stopHullIndex; index < totalHullCount; ++index) + { + chunkConvexHulls[index].mParams->copy( *chunkConvexHulls[index-hullCountDelta].mParams ); + } + } + + // Resize the hull arrays + if (hullCountDelta != 0) + { + mParams->resizeArray(handle, (int32_t)totalHullCount); + chunkConvexHulls.resize(totalHullCount); + } + + // Insert hulls if the count has increased + if (hullCountDelta > 0) + { + for (uint32_t index = totalHullCount; index-- > stopHullIndex;) + { + mParams->chunkConvexHulls.buf[index] = mParams->chunkConvexHulls.buf[index-hullCountDelta]; + chunkConvexHulls[index].init(mParams->chunkConvexHulls.buf[index]); + } + for (uint32_t index = oldStopHullIndex; index < stopHullIndex; ++index) + { + mParams->chunkConvexHulls.buf[index] = traits->createNvParameterized(ConvexHullParameters::staticClassName()); + chunkConvexHulls[index].init(mParams->chunkConvexHulls.buf[index]); + } + } + + if (hullCount > 0) + { + for (uint32_t hullNum = 0; hullNum < hullCount; ++hullNum) + { + ConvexHullImpl& chunkHullData = chunkConvexHulls[startHullIndex + hullNum]; + PartConvexHullProxy* convexHullProxy = (PartConvexHullProxy*)convexHulls[hullNum]; + chunkHullData.mParams->copy(*convexHullProxy->impl.mParams); + if (chunkHullData.isEmpty()) + { + chunkHullData.buildFromAABB(renderMeshAsset->getBounds(partIndex)); // \todo: need better way of simplifying + } + } + } + + return hullCount > 0; +#endif +} + + +// DestructibleAssetAuthoring +#ifndef WITHOUT_APEX_AUTHORING + + + +void gatherPartMesh(physx::Array<PxVec3>& vertices, + physx::Array<uint32_t>& indices, + const RenderMeshAsset* renderMeshAsset, + uint32_t partIndex) +{ + if (renderMeshAsset == NULL || partIndex >= renderMeshAsset->getPartCount()) + { + vertices.resize(0); + indices.resize(0); + return; + } + + + // Pre-count vertices and indices so we can allocate once + uint32_t vertexCount = 0; + uint32_t indexCount = 0; + for (uint32_t submeshIndex = 0; submeshIndex < renderMeshAsset->getSubmeshCount(); ++submeshIndex) + { + const RenderSubmesh& submesh = renderMeshAsset->getSubmesh(submeshIndex); + vertexCount += submesh.getVertexCount(partIndex); + indexCount += submesh.getIndexCount(partIndex); + } + + vertices.resize(vertexCount); + indices.resize(indexCount); + + vertexCount = 0; + indexCount = 0; + for (uint32_t submeshIndex = 0; submeshIndex < renderMeshAsset->getSubmeshCount(); ++submeshIndex) + { + const RenderSubmesh& submesh = renderMeshAsset->getSubmesh(submeshIndex); + if (submesh.getVertexCount(partIndex) > 0) + { + const VertexBuffer& vertexBuffer = submesh.getVertexBuffer(); + const VertexFormat& vertexFormat = vertexBuffer.getFormat(); + const int32_t bufferIndex = vertexFormat.getBufferIndexFromID(vertexFormat.getSemanticID(nvidia::RenderVertexSemantic::POSITION)); + if (bufferIndex >= 0) + { + vertexBuffer.getBufferData(&vertices[vertexCount], nvidia::RenderDataFormat::FLOAT3, sizeof(PxVec3), (uint32_t)bufferIndex, + submesh.getFirstVertexIndex(partIndex), submesh.getVertexCount(partIndex)); + } + else + { + memset(&vertices[vertexCount], 0, submesh.getVertexCount(partIndex)*sizeof(PxVec3)); + } + const uint32_t* partIndexBuffer = submesh.getIndexBuffer(partIndex); + const uint32_t partIndexCount = submesh.getIndexCount(partIndex); + for (uint32_t indexNum = 0; indexNum < partIndexCount; ++indexNum) + { + indices[indexCount++] = partIndexBuffer[indexNum] + vertexCount - submesh.getFirstVertexIndex(partIndex); + } + vertexCount += submesh.getVertexCount(partIndex); + } + } +} + + +/** + Private function - it's way too finicky and really only meant to serve the (public) trimCollisionVolumes function. + The chunkIndices array is expected to contain every member of an instanced set. That is, if a chunk indexed by some element of chunkIndices + references partIndex, then *every* chunk that references partIndex should be in the chunkIndices array. + Also, all chunks in chunkIndices are expected to be at the same depth. +*/ +void DestructibleAssetAuthoringImpl::trimCollisionGeometryInternal(const uint32_t* chunkIndices, uint32_t chunkIndexCount, const physx::Array<IntPair>& parentDepthOverlaps, uint32_t depth, float maxTrimFraction) +{ + /* + 1) Find overlaps between each chunk (hull to hull). + 2) For each overlap: + a) Create a trim plane for the overlapping hulls (one for each). Adjust the trim planes to respect maxTrimFraction. + 3) For each chunk: + a) For each hull in the chunk: + i) Add additional trim planes from all parent chunks' neighbors' hulls, and sibling's hulls. Adjust trim planes to respect maxTrimFraction. + 4) Intersect the trimmed hull(s) from (2) and (3) with the trim planes. This may cause some hulls to disappear. + 5) Recurse to children + */ + + // Create an epslion + PxBounds3 bounds = renderMeshAsset->getBounds(); + const float sizeScale = (bounds.minimum - bounds.maximum).magnitude(); + const float eps = 0.0001f * sizeScale; // \todo - expose? + + // 1) Find overlaps between each chunk (hull to hull). + const PxMat44 identityTM(PxVec4(1.0f)); + const PxVec3 identityScale(1.0f); + + // Find AABB overlaps + physx::Array<BoundsRep> chunkBoundsReps; + chunkBoundsReps.reserve(chunkIndexCount); + for (uint32_t chunkNum = 0; chunkNum < chunkIndexCount; ++chunkNum) + { + const uint32_t chunkIndex = chunkIndices[chunkNum]; + BoundsRep& chunkBoundsRep = chunkBoundsReps.insert(); + chunkBoundsRep.aabb = getChunkActorLocalBounds(chunkIndex); + } + physx::Array<IntPair> overlaps; + if (chunkBoundsReps.size() > 0) + { + boundsCalculateOverlaps(overlaps, Bounds3XYZ, &chunkBoundsReps[0], chunkBoundsReps.size(), sizeof(chunkBoundsReps[0])); + } + + // We'll store the trim planes here + physx::Array<TrimPlane> trimPlanes; + + // Now do detailed overlap test + for (uint32_t overlapIndex = 0; overlapIndex < overlaps.size(); ++overlapIndex) + { + IntPair& AABBOverlap = overlaps[overlapIndex]; + const uint32_t chunkIndex0 = chunkIndices[AABBOverlap.i0]; + const uint32_t chunkIndex1 = chunkIndices[AABBOverlap.i1]; + + // Offset chunks (in case they are instanced) + const PxTransform tm0(getChunkPositionOffset(chunkIndex0)); + const PxTransform tm1(getChunkPositionOffset(chunkIndex1)); + for (uint32_t hullIndex0 = getChunkHullIndexStart(chunkIndex0); hullIndex0 < getChunkHullIndexStop(chunkIndex0); ++hullIndex0) + { + TrimPlane trimPlane0; + trimPlane0.hullIndex = hullIndex0; + trimPlane0.partIndex = getPartIndex(chunkIndex0); + for (uint32_t hullIndex1 = getChunkHullIndexStart(chunkIndex1); hullIndex1 < getChunkHullIndexStop(chunkIndex1); ++hullIndex1) + { + TrimPlane trimPlane1; + trimPlane1.hullIndex = hullIndex1; + trimPlane1.partIndex = getPartIndex(chunkIndex1); + ConvexHullImpl::Separation separation; + if (ConvexHullImpl::hullsInProximity(chunkConvexHulls[hullIndex0], tm0, identityScale, chunkConvexHulls[hullIndex1], tm1, identityScale, 0.0f, &separation)) + { + // 2) For each overlap: + // a) Create a trim plane for the overlapping hulls (one for each). Adjust the trim planes to respect maxTrimFraction. + trimPlane0.plane = separation.plane; + trimPlane0.plane.d = PxMin(trimPlane0.plane.d, maxTrimFraction*(separation.max0-separation.min0) - separation.max0); // Bound clip distance + trimPlane0.plane.d += trimPlane0.plane.n.dot(tm0.p); // Transform back into part local space + trimPlanes.pushBack(trimPlane0); + trimPlane1.plane = PxPlane(-separation.plane.n, -separation.plane.d); + trimPlane1.plane.d = PxMin(trimPlane1.plane.d, maxTrimFraction*(separation.max1-separation.min1) + separation.min1); // Bound clip distance + trimPlane1.plane.d += trimPlane1.plane.n.dot(tm1.p); // Transform back into part local space + trimPlanes.pushBack(trimPlane1); + } + } + } + } + + // Get overlaps for this depth + physx::Array<IntPair> overlapsAtDepth; + calculateChunkOverlaps(overlapsAtDepth, depth); + + // 3) For each chunk: + for (uint32_t chunkNum = 0; chunkNum < chunkIndexCount; ++chunkNum) + { + const uint32_t chunkIndex = chunkIndices[chunkNum]; + const PxTransform tm0(getChunkPositionOffset(chunkIndex)); + const int32_t chunkParentIndex = mParams->chunks.buf[chunkIndex].parentIndex; + + for (int ancestorLevel = 0; ancestorLevel < 2; ++ancestorLevel) // First time iterate through uncles, second time through siblings + { + // Optimization opportunity: symmetrize, sort and index the overlap list + uint32_t overlapCount; + const IntPair* overlapArray; + if (ancestorLevel == 0) + { + overlapCount = parentDepthOverlaps.size(); + overlapArray = parentDepthOverlaps.begin(); + } + else + { + overlapCount = overlapsAtDepth.size(); + overlapArray = overlapsAtDepth.begin(); + } + for (uint32_t overlapNum = 0; overlapNum < overlapCount; ++overlapNum) + { + const IntPair& overlap = overlapArray[overlapNum]; + uint32_t otherChunkIndex; + const int32_t ignoreChunkIndex = ancestorLevel == 0 ? chunkParentIndex : (int32_t)chunkIndex; + if (overlap.i0 == ignoreChunkIndex) + { + otherChunkIndex = (uint32_t)overlap.i1; + } + else + if (overlap.i1 == ignoreChunkIndex) + { + otherChunkIndex = (uint32_t)overlap.i0; + } + else + { + continue; + } + // Make sure we're not trimming from a chunk already in our chunk list (we've handled that already) + bool alreadyConsidered = false; + if (ancestorLevel == 1) + { + for (uint32_t checkNum = 0; checkNum < chunkIndexCount; ++checkNum) + { + if (chunkIndices[checkNum] == otherChunkIndex) + { + alreadyConsidered = true; + break; + } + } + } + if (alreadyConsidered) + { + continue; + } + // Check other Chunk + const PxTransform tm1(getChunkPositionOffset(otherChunkIndex)); + // a) For each hull in the chunk: + for (uint32_t hullIndex0 = getChunkHullIndexStart(chunkIndex); hullIndex0 < getChunkHullIndexStop(chunkIndex); ++hullIndex0) + { + TrimPlane trimPlane; + trimPlane.hullIndex = hullIndex0; + trimPlane.partIndex = getPartIndex(chunkIndex); + for (uint32_t hullIndex1 = getChunkHullIndexStart(otherChunkIndex); hullIndex1 < getChunkHullIndexStop(otherChunkIndex); ++hullIndex1) + { + ConvexHullImpl::Separation separation; + if (ConvexHullImpl::hullsInProximity(chunkConvexHulls[hullIndex0], tm0, identityScale, chunkConvexHulls[hullIndex1], tm1, identityScale, 0.0f, &separation)) + { + if (PxMax(separation.min0 - separation.max1, separation.min1 - separation.max0) < -eps) + { + trimPlane.plane = separation.plane; + trimPlane.plane.d = -separation.min1; // Allow for other hull to intrude completely + trimPlane.plane.d = PxMin(trimPlane.plane.d, maxTrimFraction*(separation.max0-separation.min0) - separation.max0); // Bound clip distance + trimPlane.plane.d += trimPlane.plane.n.dot(tm0.p); // Transform back into part local space + trimPlanes.pushBack(trimPlane); + } + } + } + } + } + } + } + + // 4) Intersect the trimmed hull(s) from (2) and (3) with the trim planes. This may cause some hulls to disappear. + + // Sort by part, then by hull. We're going to get a little rough with these parts. + nvidia::sort<TrimPlane, TrimPlane::LessThan>(trimPlanes.begin(), trimPlanes.size(), TrimPlane::LessThan()); + + // Create a lookup into the array + physx::Array<uint32_t> trimPlaneLookup; + createIndexStartLookup(trimPlaneLookup, 0, renderMeshAsset->getPartCount(), trimPlanes.size() > 0 ? (int32_t*)&trimPlanes[0].partIndex : NULL, trimPlanes.size(), sizeof(TrimPlane)); + + // Trim + for (uint32_t partIndex = 0; partIndex < renderMeshAsset->getPartCount(); ++partIndex) + { + bool hullCountChanged = false; + for (uint32_t partTrimPlaneIndex = trimPlaneLookup[partIndex]; partTrimPlaneIndex < trimPlaneLookup[partIndex+1]; ++partTrimPlaneIndex) + { + TrimPlane& trimPlane = trimPlanes[partTrimPlaneIndex]; + ConvexHullImpl& hull = chunkConvexHulls[trimPlane.hullIndex]; + hull.intersectPlaneSide(trimPlane.plane); + if (hull.isEmpty()) + { + hullCountChanged = true; + } + } + if (hullCountChanged) + { + // Re-apply hulls that haven't disappeared + physx::Array<PartConvexHullProxy*> newHulls; + for (uint32_t hullIndex = mParams->chunkConvexHullStartIndices.buf[partIndex]; hullIndex < mParams->chunkConvexHullStartIndices.buf[partIndex+1]; ++hullIndex) + { + ConvexHullImpl& hull = chunkConvexHulls[hullIndex]; + if (!hull.isEmpty()) + { + PartConvexHullProxy* newHull = PX_NEW(PartConvexHullProxy); + newHulls.pushBack(newHull); + newHull->impl.init(hull.mParams); + } + } + DestructibleGeometryDesc geometryDesc; + CollisionVolumeDesc collisionVolumeDesc; + if (newHulls.size() > 0) + { + geometryDesc.convexHulls = (const nvidia::ExplicitHierarchicalMesh::ConvexHull**)newHulls.begin(); + geometryDesc.convexHullCount = newHulls.size(); + } + else + { + // We've lost all of the collision volume! Quite a shame... create a hull to replace it. + geometryDesc.collisionVolumeDesc = &collisionVolumeDesc; + collisionVolumeDesc.mHullMethod = ConvexHullMethod::WRAP_GRAPHICS_MESH; + } + rebuildCollisionGeometry(partIndex, geometryDesc); + for (uint32_t hullN = 0; hullN < newHulls.size(); ++hullN) + { + PX_DELETE(newHulls[hullN]); + } + } + } + + // 5) Recurse to children + + // Iterate through chunks and collect children + physx::Array<uint32_t> childChunkIndices; + for (uint32_t chunkNum = 0; chunkNum < chunkIndexCount; ++chunkNum) + { + const uint32_t chunkIndex = chunkIndices[chunkNum]; + const uint16_t firstChildIndex = mParams->chunks.buf[chunkIndex].firstChildIndex; + for (uint16_t childNum = 0; childNum < mParams->chunks.buf[chunkIndex].numChildren; ++childNum) + { + childChunkIndices.pushBack((uint32_t)(firstChildIndex + childNum)); + } + } + + // Recurse + if (childChunkIndices.size()) + { + trimCollisionGeometryInternal(childChunkIndices.begin(), childChunkIndices.size(), overlapsAtDepth, depth+1, maxTrimFraction); + } +} + +struct PartAndChunk +{ + uint32_t chunkIndex; + int32_t partIndex; + + struct LessThan + { + PX_INLINE bool operator()(const PartAndChunk& x, const PartAndChunk& y) const + { + return x.partIndex != y.partIndex ? (x.partIndex < y.partIndex) : (x.chunkIndex < y.chunkIndex); + } + }; +}; + +// Here's our chunk list element, with depth (for sorting) +struct ChunkAndDepth +{ + uint32_t chunkIndex; + int32_t depth; + + struct LessThan + { + PX_INLINE bool operator()(const ChunkAndDepth& x, const ChunkAndDepth& y) const + { + return x.depth != y.depth ? (x.depth < y.depth) : (x.chunkIndex < y.chunkIndex); + } + }; +}; + +void DestructibleAssetAuthoringImpl::trimCollisionGeometry(const uint32_t* partIndices, uint32_t partIndexCount, float maxTrimFraction) +{ + /* + 1) Create a list of chunks which reference each partIndex. (If there is instancing, there may be more than one chunk per part.) + 2) Sort by depth (stable sort) + 3) For each depth: + a) Collect a list of chunks at that depth, and call trimCollisionVolumesInternal. + */ + + // 1) Create a list of chunks which reference each partIndex. (If there is instancing, there may be more than one chunk per part.) + + // Fill array and sort + physx::Array<PartAndChunk> partAndChunkList; + partAndChunkList.resize(getChunkCount()); + for (uint32_t chunkIndex = 0; chunkIndex < getChunkCount(); ++chunkIndex) + { + partAndChunkList[chunkIndex].chunkIndex = chunkIndex; + partAndChunkList[chunkIndex].partIndex = (int32_t)getPartIndex(chunkIndex); + } + nvidia::sort<PartAndChunk, PartAndChunk::LessThan>(partAndChunkList.begin(), partAndChunkList.size(), PartAndChunk::LessThan()); + + // Create a lookup into the array + physx::Array<uint32_t> partAndChunkLookup; + createIndexStartLookup(partAndChunkLookup, 0, renderMeshAsset->getPartCount(), &partAndChunkList[0].partIndex, getChunkCount(), sizeof(PartAndChunk)); + + // 2) Sort by depth (stable sort) + + // Count how many chunks there will be + uint32_t chunkListSize = 0; + for (uint32_t partNum = 0; partNum < partIndexCount; ++partNum) + { + const uint32_t partIndex = partIndices[partNum]; + chunkListSize += partAndChunkLookup[partIndex+1] - partAndChunkLookup[partIndex]; + } + + // Fill and sort + physx::Array<ChunkAndDepth> chunkAndDepthList; + chunkAndDepthList.resize(chunkListSize); + uint32_t chunkNum = 0; + for (uint32_t partNum = 0; partNum < partIndexCount; ++partNum) + { + const uint32_t partIndex = partIndices[partNum]; + for (uint32_t partChunkNum = partAndChunkLookup[partIndex]; partChunkNum < partAndChunkLookup[partIndex+1]; ++partChunkNum, ++chunkNum) + { + const uint32_t chunkIndex = partAndChunkList[partChunkNum].chunkIndex; + chunkAndDepthList[chunkNum].chunkIndex = chunkIndex; + chunkAndDepthList[chunkNum].depth = mParams->chunks.buf[chunkIndex].depth; + } + } + nvidia::sort<ChunkAndDepth, ChunkAndDepth::LessThan>(chunkAndDepthList.begin(), chunkAndDepthList.size(), ChunkAndDepth::LessThan()); + + // And create a lookup into the array + physx::Array<uint32_t> chunkAndDepthLookup; + createIndexStartLookup(chunkAndDepthLookup, 0, mParams->depthCount, &chunkAndDepthList[0].depth, chunkListSize, sizeof(ChunkAndDepth)); + + // 3) For each depth: + for (uint32_t depth = 0; depth < mParams->depthCount; ++depth) + { + physx::Array<uint32_t> chunkIndexList; + chunkIndexList.resize(chunkAndDepthLookup[depth+1] - chunkAndDepthLookup[depth]); + if (chunkIndexList.size() == 0) + { + continue; + } + uint32_t chunkIndexListSize = 0; + for (uint32_t depthChunkNum = chunkAndDepthLookup[depth]; depthChunkNum < chunkAndDepthLookup[depth+1]; ++depthChunkNum) + { + chunkIndexList[chunkIndexListSize++] = chunkAndDepthList[depthChunkNum].chunkIndex; + } + PX_ASSERT(chunkIndexListSize == chunkIndexList.size()); + physx::Array<IntPair> overlaps; + if (depth > 0) + { + calculateChunkOverlaps(overlaps, depth-1); + } + trimCollisionGeometryInternal(chunkIndexList.begin(), chunkIndexList.size(), overlaps, depth, maxTrimFraction); + } +} + +void DestructibleAssetAuthoringImpl::setToolString(const char* toolString) +{ + if (mParams != NULL) + { + NvParameterized::Handle handle(*mParams, "comments"); + PX_ASSERT(handle.isValid()); + if (handle.isValid()) + { + PX_ASSERT(handle.parameterDefinition()->type() == NvParameterized::TYPE_STRING); + handle.setParamString(toolString); + } + } +} + +void DestructibleAssetAuthoringImpl::cookChunks(const DestructibleAssetCookingDesc& cookingDesc, bool cacheOverlaps, uint32_t* chunkIndexMapUser2Apex, uint32_t* chunkIndexMapApex2User, uint32_t chunkIndexMapCount) +{ + if (!cookingDesc.isValid()) + { + APEX_INVALID_PARAMETER("DestructibleAssetAuthoring::cookChunks: cookingDesc invalid."); + return; + } + + const uint32_t numChunks = cookingDesc.chunkDescCount; + const uint32_t numBehaviorGroups = cookingDesc.behaviorGroupDescCount; + const uint32_t numParts = renderMeshAsset->getPartCount(); + + if ((chunkIndexMapUser2Apex != NULL || chunkIndexMapApex2User != NULL) && chunkIndexMapCount < numChunks) + { + APEX_INVALID_PARAMETER("DestructibleAssetAuthoring::cookChunks: chunkIndexMap is not big enough."); + return; + } + + NvParameterized::Handle handle(*mParams); + + mParams->getParameterHandle("chunks", handle); + mParams->resizeArray(handle, (int32_t)numChunks); + mParams->depthCount = 0; + + // Create convex hulls + mParams->getParameterHandle("chunkConvexHulls", handle); + mParams->resizeArray(handle, 0); + chunkConvexHulls.reset(); + + mParams->getParameterHandle("chunkConvexHullStartIndices", handle); + mParams->resizeArray(handle, (int32_t)numParts + 1); + for (uint32_t partIndex = 0; partIndex <= numParts; ++partIndex) + { + mParams->chunkConvexHullStartIndices.buf[partIndex] = 0; // Initialize all to zero, so that the random-access rebuildCollisionGeometry does the right thing (below) + } + for (uint32_t partIndex = 0; partIndex < numParts; ++partIndex) + { + if (!rebuildCollisionGeometry(partIndex, cookingDesc.geometryDescs[partIndex])) + { + APEX_INVALID_PARAMETER("DestructibleAssetAuthoring::cookChunks: Could not find or generate collision hull for part."); + } + } + + // Sort - chunks must be in parent-sorted order + Array<ChunkSortElement> sortElements; + sortElements.resize(numChunks); + for (uint32_t i = 0; i < numChunks; ++i) + { + const DestructibleChunkDesc& chunkDesc = cookingDesc.chunkDescs[i]; + sortElements[i].index = (int32_t)i; + sortElements[i].parentIndex = chunkDesc.parentIndex; + sortElements[i].depth = 0; + int32_t parent = (int32_t)i; + uint32_t counter = 0; + while ((parent = cookingDesc.chunkDescs[parent].parentIndex) >= 0) + { + ++sortElements[i].depth; + if (++counter > numChunks) + { + APEX_INVALID_PARAMETER("DestructibleAssetAuthoring::cookChunks: loop found in cookingDesc parent indices. Cannot build an DestructibleAsset."); + return; + } + } + } + qsort(sortElements.begin(), numChunks, sizeof(ChunkSortElement), compareChunkParents); + + Array<uint32_t> ranks; + if (chunkIndexMapUser2Apex == NULL && numChunks > 0) + { + ranks.resize(numChunks); + chunkIndexMapUser2Apex = &ranks[0]; + } + + for (uint32_t i = 0; i < numChunks; ++i) + { + chunkIndexMapUser2Apex[sortElements[i].index] = i; + if (chunkIndexMapApex2User != NULL) + { + chunkIndexMapApex2User[i] = (uint32_t)sortElements[i].index; + } + } + + // Count instanced chunks and allocate instanced info array + uint32_t instancedChunkCount = 0; + for (uint32_t i = 0; i < numChunks; ++i) + { + const DestructibleChunkDesc& chunkDesc = cookingDesc.chunkDescs[sortElements[i].index]; + if (chunkDesc.useInstancedRendering) + { + ++instancedChunkCount; + } + } + + mParams->getParameterHandle("chunkInstanceInfo", handle); + mParams->resizeArray(handle, (int32_t)instancedChunkCount); + + mParams->getParameterHandle("scatterMeshIndices", handle); + mParams->resizeArray(handle, 0); + mParams->getParameterHandle("scatterMeshTransforms", handle); + mParams->resizeArray(handle, 0); + + const DestructibleChunkDesc defaultChunkDesc; + + instancedChunkCount = 0; // reset and use as cursor + + for (uint32_t i = 0; i < numChunks; ++i) + { + DestructibleAssetParametersNS::Chunk_Type& chunk = mParams->chunks.buf[i]; + const DestructibleChunkDesc& chunkDesc = cookingDesc.chunkDescs[sortElements[i].index]; + chunk.flags = 0; + if (chunkDesc.isSupportChunk) + { + chunk.flags |= SupportChunk; + } + if (chunkDesc.doNotFracture) + { + chunk.flags |= UnfracturableChunk; + } + if (chunkDesc.doNotDamage) + { + chunk.flags |= UndamageableChunk; + } + if (chunkDesc.doNotCrumble) + { + chunk.flags |= UncrumbleableChunk; + } +#if APEX_RUNTIME_FRACTURE + if (chunkDesc.runtimeFracture) + { + chunk.flags |= RuntimeFracturableChunk; + } +#endif + if (!chunkDesc.useInstancedRendering) + { + // Not instanced, meshPartIndex will be used to directly access the mesh part in the "normal" mesh + chunk.meshPartIndex = chunkDesc.meshIndex; + } + else + { + // Instanced, meshPartIndex will be used to access instance info + chunk.flags |= Instanced; + chunk.meshPartIndex = (uint16_t)instancedChunkCount++; + DestructibleAssetParametersNS::InstanceInfo_Type& instanceInfo = mParams->chunkInstanceInfo.buf[chunk.meshPartIndex]; + instanceInfo.partIndex = chunkDesc.meshIndex; + instanceInfo.chunkPositionOffset = chunkDesc.instancePositionOffset; + instanceInfo.chunkUVOffset = chunkDesc.instanceUVOffset; + } + if (sortElements[i].index == 0) + { + chunk.depth = 0; + chunk.parentIndex = DestructibleAssetImpl::InvalidChunkIndex; + } + else + { + chunk.parentIndex = (uint16_t)chunkIndexMapUser2Apex[(int16_t)chunkDesc.parentIndex]; + DestructibleAssetParametersNS::Chunk_Type& parent = mParams->chunks.buf[chunk.parentIndex]; + PX_ASSERT(chunk.parentIndex >= mParams->chunks.buf[i - 1].parentIndex || + mParams->chunks.buf[i - 1].parentIndex == DestructibleAssetImpl::InvalidChunkIndex); // Parent-sorted order + if (chunk.parentIndex != mParams->chunks.buf[i - 1].parentIndex) + { + parent.firstChildIndex = (uint16_t)i; + } + ++parent.numChildren; + chunk.depth = uint16_t(parent.depth + 1); + if ((parent.flags & SupportChunk) != 0) + { + chunk.flags |= SupportChunk; // All children of support chunks can be support chunks + } + if ((parent.flags & UnfracturableChunk) != 0) + { + chunk.flags |= UnfracturableChunk; // All children of unfracturable chunks are unfracturable + } +#if APEX_RUNTIME_FRACTURE + if ((parent.flags & RuntimeFracturableChunk) != 0) // Runtime fracturable chunks cannot have any children + { + PX_ALWAYS_ASSERT(); + } +#endif + } + if ((chunk.flags & UnfracturableChunk) != 0) + { + // All ancestors of unfracturable chunks must be unfracturable or note that they have an unfracturable descendant + uint16_t parentIndex = chunk.parentIndex; + while (parentIndex != DestructibleAssetImpl::InvalidChunkIndex) + { + DestructibleAssetParametersNS::Chunk_Type& parent = mParams->chunks.buf[parentIndex]; + if ((parent.flags & UnfracturableChunk) == 0) + { + parent.flags |= DescendantUnfractureable; + } + parentIndex = parent.parentIndex; + } + } + chunk.numChildren = 0; + chunk.firstChildIndex = DestructibleAssetImpl::InvalidChunkIndex; + mParams->depthCount = PxMax((uint16_t)mParams->depthCount, (uint16_t)(chunk.depth + 1)); + chunk.surfaceNormal = chunkDesc.surfaceNormal; + chunk.behaviorGroupIndex = chunkDesc.behaviorGroupIndex; + + // Default behavior is to take on the parent's behavior group + if (chunk.parentIndex != DestructibleAssetImpl::InvalidChunkIndex) + { + if (chunk.behaviorGroupIndex < 0) + { + chunk.behaviorGroupIndex = mParams->chunks.buf[chunk.parentIndex].behaviorGroupIndex; + } + } + + // Scatter mesh + const int32_t oldScatterMeshCount = mParams->scatterMeshIndices.arraySizes[0]; + chunk.firstScatterMesh = (uint16_t)oldScatterMeshCount; + chunk.scatterMeshCount = (uint16_t)chunkDesc.scatterMeshCount; + if (chunk.scatterMeshCount > 0) + { + mParams->getParameterHandle("scatterMeshIndices", handle); + mParams->resizeArray(handle, oldScatterMeshCount + (int32_t)chunk.scatterMeshCount); + handle.setParamU8Array(chunkDesc.scatterMeshIndices, (int32_t)chunk.scatterMeshCount, oldScatterMeshCount); + mParams->getParameterHandle("scatterMeshTransforms", handle); + mParams->resizeArray(handle, oldScatterMeshCount + (int32_t)chunk.scatterMeshCount); + for (uint16_t tmNum = 0; tmNum < chunk.scatterMeshCount; ++tmNum) + { + mParams->scatterMeshTransforms.buf[oldScatterMeshCount + tmNum] = chunkDesc.scatterMeshTransforms[tmNum]; + } + } + } + + mParams->getParameterHandle("behaviorGroups", handle); + mParams->resizeArray(handle, (int32_t)numBehaviorGroups); + + struct { + void operator() (const DestructibleBehaviorGroupDesc& behaviorGroupDesc, DestructibleAssetParameters* params, NvParameterized::Handle& elementHandle) + { + NvParameterized::Handle subElementHandle(*params); + + elementHandle.getChildHandle(params, "name", subElementHandle); + params->setParamString(subElementHandle, behaviorGroupDesc.name); + + elementHandle.getChildHandle(params, "damageThreshold", subElementHandle); + params->setParamF32(subElementHandle, behaviorGroupDesc.damageThreshold); + + elementHandle.getChildHandle(params, "damageToRadius", subElementHandle); + params->setParamF32(subElementHandle, behaviorGroupDesc.damageToRadius); + + elementHandle.getChildHandle(params, "damageSpread.minimumRadius", subElementHandle); + params->setParamF32(subElementHandle, behaviorGroupDesc.damageSpread.minimumRadius); + + elementHandle.getChildHandle(params, "damageSpread.radiusMultiplier", subElementHandle); + params->setParamF32(subElementHandle, behaviorGroupDesc.damageSpread.radiusMultiplier); + + elementHandle.getChildHandle(params, "damageSpread.falloffExponent", subElementHandle); + params->setParamF32(subElementHandle, behaviorGroupDesc.damageSpread.falloffExponent); + + elementHandle.getChildHandle(params, "damageColorSpread.minimumRadius", subElementHandle); + params->setParamF32(subElementHandle, behaviorGroupDesc.damageColorSpread.minimumRadius); + + elementHandle.getChildHandle(params, "damageColorSpread.radiusMultiplier", subElementHandle); + params->setParamF32(subElementHandle, behaviorGroupDesc.damageColorSpread.radiusMultiplier); + + elementHandle.getChildHandle(params, "damageColorSpread.falloffExponent", subElementHandle); + params->setParamF32(subElementHandle, behaviorGroupDesc.damageColorSpread.falloffExponent); + + elementHandle.getChildHandle(params, "damageColorChange", subElementHandle); + params->setParamVec4(subElementHandle, behaviorGroupDesc.damageColorChange); + + elementHandle.getChildHandle(params, "materialStrength", subElementHandle); + params->setParamF32(subElementHandle, behaviorGroupDesc.materialStrength); + + elementHandle.getChildHandle(params, "density", subElementHandle); + params->setParamF32(subElementHandle, behaviorGroupDesc.density); + + elementHandle.getChildHandle(params, "fadeOut", subElementHandle); + params->setParamF32(subElementHandle, behaviorGroupDesc.fadeOut); + + elementHandle.getChildHandle(params, "maxDepenetrationVelocity", subElementHandle); + params->setParamF32(subElementHandle, behaviorGroupDesc.maxDepenetrationVelocity); + + elementHandle.getChildHandle(params, "userData", subElementHandle); + params->setParamU64(subElementHandle, behaviorGroupDesc.userData); + } + } ConvertBehaviorGroupDesc; // local function definitions illegal, eh? + + const uint32_t bufferCount = 128; + char buffer[bufferCount] = {0}; + + for (uint32_t i = 0; i < numBehaviorGroups; ++i) + { + const DestructibleBehaviorGroupDesc& chunkDesc = cookingDesc.behaviorGroupDescs[i]; + NvParameterized::Handle elementHandle(*mParams); + + shdfnd::snprintf(buffer, bufferCount, "behaviorGroups[%d]", i); + mParams->getParameterHandle(buffer, elementHandle); + + ConvertBehaviorGroupDesc(chunkDesc, mParams, elementHandle); + } + + mParams->getParameterHandle("defaultBehaviorGroup", handle); + ConvertBehaviorGroupDesc(cookingDesc.defaultBehaviorGroupDesc, mParams, handle); + + mParams->RTFractureBehaviorGroup = cookingDesc.RTFractureBehaviorGroup; + + + // Fill in default destructible parameters up to depth + int oldDepthParametersSize = mParams->depthParameters.arraySizes[0]; + if (oldDepthParametersSize < (int)mParams->depthCount) + { + NvParameterized::Handle depthParametersHandle(*mParams); + mParams->getParameterHandle("depthParameters", depthParametersHandle); + mParams->resizeArray(depthParametersHandle, (int32_t)mParams->depthCount); + for (int i = oldDepthParametersSize; i < (int)mParams->depthCount; ++i) + { + DestructibleAssetParametersNS::DestructibleDepthParameters_Type& depthParameters = mParams->depthParameters.buf[i]; + depthParameters.OVERRIDE_IMPACT_DAMAGE = false; + depthParameters.OVERRIDE_IMPACT_DAMAGE_VALUE = false; + depthParameters.IGNORE_POSE_UPDATES = false; + depthParameters.IGNORE_RAYCAST_CALLBACKS = false; + depthParameters.IGNORE_CONTACT_CALLBACKS = false; + depthParameters.USER_FLAG_0 = false; + depthParameters.USER_FLAG_1 = false; + depthParameters.USER_FLAG_2 = false; + depthParameters.USER_FLAG_3 = false; + } + } + + // Build collision data and bounds + float skinWidth = 0.0025f; // Default value + if (GetApexSDK()->getCookingInterface()) + { + const PxCookingParams& cookingParams = GetApexSDK()->getCookingInterface()->getParams(); + skinWidth = cookingParams.skinWidth; + } + + mParams->bounds.setEmpty(); + for (uint32_t partIndex = 0; partIndex < numParts; ++partIndex) + { + const DestructibleGeometryDesc& geometryDesc = cookingDesc.geometryDescs[partIndex]; + const uint32_t startHullIndex = mParams->chunkConvexHullStartIndices.buf[partIndex]; + for (uint32_t hullIndex = 0; hullIndex < geometryDesc.convexHullCount; ++hullIndex) + { + ConvexHullImpl& chunkHullData = chunkConvexHulls[startHullIndex + hullIndex]; + PartConvexHullProxy* convexHullProxy = (PartConvexHullProxy*)geometryDesc.convexHulls[hullIndex]; + chunkHullData.mParams->copy(*convexHullProxy->impl.mParams); + if (chunkHullData.isEmpty()) + { + chunkHullData.buildFromAABB(renderMeshAsset->getBounds(partIndex)); // \todo: need better way of simplifying + } + } + } + + for (uint32_t chunkIndex = 0; chunkIndex < numChunks; ++chunkIndex) + { + DestructibleAssetParametersNS::Chunk_Type& chunk = mParams->chunks.buf[chunkIndex]; + PxBounds3 bounds = getChunkActorLocalBounds(chunkIndex); + mParams->bounds.include(bounds); + chunk.surfaceNormal = PxVec3(0.0f); + } + PX_ASSERT(!mParams->bounds.isEmpty()); + mParams->bounds.fattenFast(skinWidth); + + mParams->originalDepthCount = mParams->depthCount; + + calculateChunkDepthStarts(); + + if (cacheOverlaps) + { + cacheChunkOverlapsUpToDepth(chunkOverlapCacheDepth); + } + + Array<IntPair> supportGraphEdgesInternal(cookingDesc.supportGraphEdgeCount); + if (cookingDesc.supportGraphEdgeCount > 0) + { + for (uint32_t i = 0; i < cookingDesc.supportGraphEdgeCount; ++i) + { + supportGraphEdgesInternal[i].i0 = (int32_t)chunkIndexMapUser2Apex[(uint32_t)cookingDesc.supportGraphEdges[i].i0]; + supportGraphEdgesInternal[i].i1 = (int32_t)chunkIndexMapUser2Apex[(uint32_t)cookingDesc.supportGraphEdges[i].i1]; + } + + addChunkOverlaps(&supportGraphEdgesInternal[0], supportGraphEdgesInternal.size()); + } + + m_needsScatterMeshInstanceInfoCreation = true; +} + +void DestructibleAssetAuthoringImpl::serializeFractureToolState(PxFileBuf& stream, nvidia::ExplicitHierarchicalMesh::Embedding& embedding) const +{ + stream.storeDword((uint32_t)ApexStreamVersion::Current); + hMesh.serialize(stream, embedding); + hMeshCore.serialize(stream, embedding); + cutoutSet.serialize(stream); +} + +void DestructibleAssetAuthoringImpl::deserializeFractureToolState(PxFileBuf& stream, nvidia::ExplicitHierarchicalMesh::Embedding& embedding) +{ + const uint32_t version = stream.readDword(); + PX_UNUSED(version); // Initial version + + hMesh.deserialize(stream, embedding); + hMeshCore.deserialize(stream, embedding); + cutoutSet.deserialize(stream); +} +#endif + +NvParameterized::Interface* DestructibleAssetImpl::getDefaultActorDesc() +{ + NvParameterized::Interface* ret = 0; + + NvParameterized::Traits* traits = GetInternalApexSDK()->getParameterizedTraits(); + + // non-optimal. Should use a copy-constructor so this only gets built once. + if (!mApexDestructibleActorParams && traits) + { + mApexDestructibleActorParams = traits->createNvParameterized(DestructibleActorParam::staticClassName()); + } + + if (traits) + { + if (mApexDestructibleActorParams) + { + ret = mApexDestructibleActorParams; + DestructibleActorParam* p = static_cast<DestructibleActorParam*>(ret); + DestructibleActorParamNS::ParametersStruct* ps = static_cast<DestructibleActorParamNS::ParametersStruct*>(p); + + { + NvParameterized::Handle handle(*p); + if (p->getParameterHandle("crumbleEmitterName", handle) == NvParameterized::ERROR_NONE) + { + p->setParamString(handle, getCrumbleEmitterName()); + } + } + + { + NvParameterized::Handle handle(*p); + if (p->getParameterHandle("dustEmitterName", handle) == NvParameterized::ERROR_NONE) + { + p->setParamString(handle, getDustEmitterName()); + } + } + + DestructibleParameters destructibleParameters = getParameters(); + + ps->destructibleParameters.damageCap = destructibleParameters.damageCap; + ps->destructibleParameters.forceToDamage = destructibleParameters.forceToDamage; + ps->destructibleParameters.impactVelocityThreshold = destructibleParameters.impactVelocityThreshold; + ps->destructibleParameters.minimumFractureDepth = destructibleParameters.minimumFractureDepth; + ps->destructibleParameters.damageDepthLimit = destructibleParameters.damageDepthLimit; + ps->destructibleParameters.impactDamageDefaultDepth = destructibleParameters.impactDamageDefaultDepth; + ps->destructibleParameters.debrisDepth = destructibleParameters.debrisDepth; + ps->destructibleParameters.essentialDepth = destructibleParameters.essentialDepth; + ps->destructibleParameters.debrisLifetimeMin = destructibleParameters.debrisLifetimeMin; + ps->destructibleParameters.debrisLifetimeMax = destructibleParameters.debrisLifetimeMax; + ps->destructibleParameters.debrisMaxSeparationMin = destructibleParameters.debrisMaxSeparationMin; + ps->destructibleParameters.debrisMaxSeparationMax = destructibleParameters.debrisMaxSeparationMax; + ps->destructibleParameters.dynamicChunksGroupsMask.useGroupsMask = destructibleParameters.useDynamicChunksGroupsMask; + ps->destructibleParameters.debrisDestructionProbability = destructibleParameters.debrisDestructionProbability; + ps->destructibleParameters.dynamicChunkDominanceGroup = destructibleParameters.dynamicChunksDominanceGroup; + ps->destructibleParameters.dynamicChunksGroupsMask.bits0 = destructibleParameters.dynamicChunksFilterData.word0; + ps->destructibleParameters.dynamicChunksGroupsMask.bits1 = destructibleParameters.dynamicChunksFilterData.word1; + ps->destructibleParameters.dynamicChunksGroupsMask.bits2 = destructibleParameters.dynamicChunksFilterData.word2; + ps->destructibleParameters.dynamicChunksGroupsMask.bits3 = destructibleParameters.dynamicChunksFilterData.word3; + ps->destructibleParameters.supportStrength = destructibleParameters.supportStrength; + ps->destructibleParameters.legacyChunkBoundsTestSetting = destructibleParameters.legacyChunkBoundsTestSetting; + ps->destructibleParameters.legacyDamageRadiusSpreadSetting = destructibleParameters.legacyDamageRadiusSpreadSetting; + ps->destructibleParameters.validBounds = destructibleParameters.validBounds; + ps->destructibleParameters.maxChunkSpeed = destructibleParameters.maxChunkSpeed; + ps->destructibleParameters.flags.ACCUMULATE_DAMAGE = (destructibleParameters.flags & DestructibleParametersFlag::ACCUMULATE_DAMAGE) ? true : false; + ps->destructibleParameters.flags.DEBRIS_TIMEOUT = (destructibleParameters.flags & DestructibleParametersFlag::DEBRIS_TIMEOUT) ? true : false; + ps->destructibleParameters.flags.DEBRIS_MAX_SEPARATION = (destructibleParameters.flags & DestructibleParametersFlag::DEBRIS_MAX_SEPARATION) ? true : false; + ps->destructibleParameters.flags.CRUMBLE_SMALLEST_CHUNKS = (destructibleParameters.flags & DestructibleParametersFlag::CRUMBLE_SMALLEST_CHUNKS) ? true : false; + ps->destructibleParameters.flags.ACCURATE_RAYCASTS = (destructibleParameters.flags & DestructibleParametersFlag::ACCURATE_RAYCASTS) ? true : false; + ps->destructibleParameters.flags.USE_VALID_BOUNDS = (destructibleParameters.flags & DestructibleParametersFlag::USE_VALID_BOUNDS) ? true : false; + ps->destructibleParameters.flags.CRUMBLE_VIA_RUNTIME_FRACTURE = (destructibleParameters.flags & DestructibleParametersFlag::CRUMBLE_VIA_RUNTIME_FRACTURE) ? true : false; + + ps->supportDepth = mParams->supportDepth; + ps->formExtendedStructures = mParams->formExtendedStructures; + ps->useAssetDefinedSupport = mParams->useAssetDefinedSupport; + ps->useWorldSupport = mParams->useWorldSupport; + + // RT Fracture Parameters + ps->destructibleParameters.runtimeFracture.sheetFracture = destructibleParameters.rtFractureParameters.sheetFracture; + ps->destructibleParameters.runtimeFracture.depthLimit = destructibleParameters.rtFractureParameters.depthLimit; + ps->destructibleParameters.runtimeFracture.destroyIfAtDepthLimit = destructibleParameters.rtFractureParameters.destroyIfAtDepthLimit; + ps->destructibleParameters.runtimeFracture.minConvexSize = destructibleParameters.rtFractureParameters.minConvexSize; + ps->destructibleParameters.runtimeFracture.impulseScale = destructibleParameters.rtFractureParameters.impulseScale; + ps->destructibleParameters.runtimeFracture.glass.numSectors = destructibleParameters.rtFractureParameters.glass.numSectors; + ps->destructibleParameters.runtimeFracture.glass.sectorRand = destructibleParameters.rtFractureParameters.glass.sectorRand; + ps->destructibleParameters.runtimeFracture.glass.firstSegmentSize = destructibleParameters.rtFractureParameters.glass.firstSegmentSize; + ps->destructibleParameters.runtimeFracture.glass.segmentScale = destructibleParameters.rtFractureParameters.glass.segmentScale; + ps->destructibleParameters.runtimeFracture.glass.segmentRand = destructibleParameters.rtFractureParameters.glass.segmentRand; + ps->destructibleParameters.runtimeFracture.attachment.posX = destructibleParameters.rtFractureParameters.attachment.posX; + ps->destructibleParameters.runtimeFracture.attachment.negX = destructibleParameters.rtFractureParameters.attachment.negX; + ps->destructibleParameters.runtimeFracture.attachment.posY = destructibleParameters.rtFractureParameters.attachment.posY; + ps->destructibleParameters.runtimeFracture.attachment.negY = destructibleParameters.rtFractureParameters.attachment.negY; + ps->destructibleParameters.runtimeFracture.attachment.posZ = destructibleParameters.rtFractureParameters.attachment.posZ; + ps->destructibleParameters.runtimeFracture.attachment.negZ = destructibleParameters.rtFractureParameters.attachment.negZ; + + uint32_t depth = destructibleParameters.depthParametersCount; + if (depth > 0) + { + NvParameterized::Handle handle(*p); + if (p->getParameterHandle("depthParameters", handle) == NvParameterized::ERROR_NONE) + { + p->resizeArray(handle, (int32_t)depth); + for (uint32_t i = 0; i < depth; i++) + { + const DestructibleDepthParameters& dparm = destructibleParameters.depthParameters[i]; + ps->depthParameters.buf[i].OVERRIDE_IMPACT_DAMAGE = (dparm.flags & DestructibleDepthParametersFlag::OVERRIDE_IMPACT_DAMAGE) ? true : false; + ps->depthParameters.buf[i].OVERRIDE_IMPACT_DAMAGE_VALUE = (dparm.flags & DestructibleDepthParametersFlag::OVERRIDE_IMPACT_DAMAGE_VALUE) ? true : false; + ps->depthParameters.buf[i].IGNORE_POSE_UPDATES = (dparm.flags & DestructibleDepthParametersFlag::IGNORE_POSE_UPDATES) ? true : false; + ps->depthParameters.buf[i].IGNORE_RAYCAST_CALLBACKS = (dparm.flags & DestructibleDepthParametersFlag::IGNORE_RAYCAST_CALLBACKS) ? true : false; + ps->depthParameters.buf[i].IGNORE_CONTACT_CALLBACKS = (dparm.flags & DestructibleDepthParametersFlag::IGNORE_CONTACT_CALLBACKS) ? true : false; + ps->depthParameters.buf[i].USER_FLAG_0 = (dparm.flags & DestructibleDepthParametersFlag::USER_FLAG_0) ? true : false; + ps->depthParameters.buf[i].USER_FLAG_1 = (dparm.flags & DestructibleDepthParametersFlag::USER_FLAG_1) ? true : false; + ps->depthParameters.buf[i].USER_FLAG_2 = (dparm.flags & DestructibleDepthParametersFlag::USER_FLAG_2) ? true : false; + ps->depthParameters.buf[i].USER_FLAG_3 = (dparm.flags & DestructibleDepthParametersFlag::USER_FLAG_3) ? true : false; + } + } + } + + { + NvParameterized::Handle handle(*p); + if (p->getParameterHandle("behaviorGroups", handle) == NvParameterized::ERROR_NONE) + { + uint32_t behaviorGroupArraySize = (uint32_t)mParams->behaviorGroups.arraySizes[0]; + p->resizeArray(handle, (int32_t)behaviorGroupArraySize); + + struct { + void operator() (const DestructibleAssetParametersNS::BehaviorGroup_Type& src, + DestructibleActorParamNS::BehaviorGroup_Type& dest) + { + dest.damageThreshold = src.damageThreshold; + dest.damageToRadius = src.damageToRadius; + dest.damageSpread.minimumRadius = src.damageSpread.minimumRadius; + dest.damageSpread.radiusMultiplier = src.damageSpread.radiusMultiplier; + dest.damageSpread.falloffExponent = src.damageSpread.falloffExponent; + dest.materialStrength = src.materialStrength; + dest.density = src.density; + dest.fadeOut = src.fadeOut; + dest.maxDepenetrationVelocity = src.maxDepenetrationVelocity; + } + } ConvertBehaviorGroup; + + ConvertBehaviorGroup(mParams->defaultBehaviorGroup, ps->defaultBehaviorGroup); + for (uint32_t i = 0; i < behaviorGroupArraySize; i++) + { + ConvertBehaviorGroup(mParams->behaviorGroups.buf[i], + ps->behaviorGroups.buf[i]); + } + } + } + } + } + return ret; +} + +NvParameterized::Interface* DestructibleAssetImpl::getDefaultAssetPreviewDesc() +{ + NvParameterized::Interface* ret = NULL; + + if (module != NULL) + { + ret = module->getApexDestructiblePreviewParams(); + + if (ret != NULL) + { + ret->initDefaults(); + } + } + + return ret; +} + +bool DestructibleAssetImpl::isValidForActorCreation(const ::NvParameterized::Interface& params, Scene& /*apexScene*/) const +{ + return nvidia::strcmp(params.className(), DestructibleActorParam::staticClassName()) == 0 || + nvidia::strcmp(params.className(), DestructibleActorState::staticClassName()) == 0; +} + +Actor* DestructibleAssetImpl::createApexActor(const NvParameterized::Interface& params, Scene& apexScene) +{ + if (!isValidForActorCreation(params, apexScene)) + { + return NULL; + } + + return createDestructibleActor(params, apexScene); +} + +AssetPreview* DestructibleAssetImpl::createApexAssetPreview(const NvParameterized::Interface& params, AssetPreviewScene* /*previewScene*/) +{ + DestructiblePreviewProxy* proxy = NULL; + + proxy = PX_NEW(DestructiblePreviewProxy)(*this, m_previewList, ¶ms); + PX_ASSERT(proxy != NULL); + + return proxy; +} + +void DestructibleAssetImpl::appendActorTransforms(const PxMat44* transforms, uint32_t transformCount) + { + NvParameterized::Handle handle(*mParams); + mParams->getParameterHandle("actorTransforms", handle); + const int32_t oldSize = mParams->actorTransforms.arraySizes[0]; + mParams->resizeArray(handle, (int32_t)(oldSize + transformCount)); + mParams->setParamMat44Array(handle, transforms, (int32_t)transformCount, oldSize); + } + +void DestructibleAssetImpl::clearActorTransforms() +{ + NvParameterized::Handle handle(*mParams); + mParams->getParameterHandle("actorTransforms", handle); + mParams->resizeArray(handle, 0); +} + +bool DestructibleAssetImpl::chunksInProximity(const DestructibleAssetImpl& asset0, uint16_t chunkIndex0, const PxTransform& tm0, const PxVec3& scale0, + const DestructibleAssetImpl& asset1, uint16_t chunkIndex1, const PxTransform& tm1, const PxVec3& scale1, + float padding) +{ + PX_ASSERT(asset0.mParams != NULL); + PX_ASSERT(asset1.mParams != NULL); + + // Offset chunks (in case they are instanced) + PxTransform effectiveTM0(tm0); + effectiveTM0.p = tm0.p + tm0.rotate(scale0.multiply(asset0.getChunkPositionOffset(chunkIndex0))); + + PxTransform effectiveTM1(tm1); + effectiveTM1.p = tm1.p + tm1.rotate(scale1.multiply(asset1.getChunkPositionOffset(chunkIndex1))); + + for (uint32_t hullIndex0 = asset0.getChunkHullIndexStart(chunkIndex0); hullIndex0 < asset0.getChunkHullIndexStop(chunkIndex0); ++hullIndex0) + { + for (uint32_t hullIndex1 = asset1.getChunkHullIndexStart(chunkIndex1); hullIndex1 < asset1.getChunkHullIndexStop(chunkIndex1); ++hullIndex1) + { + if (ConvexHullImpl::hullsInProximity(asset0.chunkConvexHulls[hullIndex0], effectiveTM0, scale0, asset1.chunkConvexHulls[hullIndex1], effectiveTM1, scale1, padding)) + { + return true; + } + } + } + return false; +} + +bool DestructibleAssetImpl::chunkAndSphereInProximity(uint16_t chunkIndex, const PxTransform& chunkTM, const PxVec3& chunkScale, + const PxVec3& sphereWorldCenter, float sphereRadius, float padding, float* distance) +{ + // Offset chunk (in case it is instanced) + PxTransform effectiveTM = chunkTM; + effectiveTM.p = chunkTM.p + chunkTM.rotate(chunkScale.multiply(getChunkPositionOffset(chunkIndex))); + + ConvexHullImpl::Separation testSeparation; + ConvexHullImpl::Separation* testSeparationPtr = distance != NULL? &testSeparation : NULL; + bool result = false; + for (uint32_t hullIndex = getChunkHullIndexStart(chunkIndex); hullIndex < getChunkHullIndexStop(chunkIndex); ++hullIndex) + { + if (chunkConvexHulls[hullIndex].sphereInProximity(effectiveTM, chunkScale, sphereWorldCenter, sphereRadius, padding, testSeparationPtr)) + { + result = true; + if (distance != NULL) + { + const float testDistance = testSeparation.getDistance(); + if (testDistance < *distance) + { + *distance = testDistance; + } + } + } + } + return result; +} + + +/* + DestructibleAssetCollision +*/ + + +DestructibleAssetCollision::DestructibleAssetCollision() : + mAsset(NULL) +{ + NvParameterized::Traits* traits = GetInternalApexSDK()->getParameterizedTraits(); + mParams = DYNAMIC_CAST(DestructibleAssetCollisionDataSet*)(traits->createNvParameterized(DestructibleAssetCollisionDataSet::staticClassName())); + mOwnsParams = mParams != NULL; + PX_ASSERT(mOwnsParams); +} + +DestructibleAssetCollision::DestructibleAssetCollision(NvParameterized::Interface* params) : + mAsset(NULL) +{ + mParams = DYNAMIC_CAST(DestructibleAssetCollisionDataSet*)(params); + mOwnsParams = false; +} + +DestructibleAssetCollision::~DestructibleAssetCollision() +{ + resize(0); + + if (mParams != NULL && mOwnsParams) + { + mParams->destroy(); + } + mParams = NULL; + mOwnsParams = false; +} + +void DestructibleAssetCollision::setDestructibleAssetToCook(DestructibleAssetImpl* asset) +{ + if (asset == NULL || getAssetName() == NULL || nvidia::strcmp(asset->getName(), getAssetName())) + { + resize(0); + } + + mAsset = asset; + + NvParameterized::Handle handle(*mParams); + mParams->getParameterHandle("assetName", handle); + mParams->setParamString(handle, mAsset != NULL ? mAsset->getName() : ""); +} + +void DestructibleAssetCollision::resize(uint32_t hullCount) +{ + for (uint32_t i = 0; i < mConvexMeshContainer.size(); ++i) + { + physx::Array< PxConvexMesh* >& convexMeshSet = mConvexMeshContainer[i]; + for (uint32_t j = hullCount; j < convexMeshSet.size(); ++j) + { + if (convexMeshSet[j] != NULL) + { + convexMeshSet[j]->release(); + } + } + mConvexMeshContainer[i].resize(hullCount); + } + + NvParameterized::Traits* traits = GetInternalApexSDK()->getParameterizedTraits(); + for (int i = 0; i < mParams->meshCookedCollisionStreamsAtScale.arraySizes[0]; ++i) + { + MeshCookedCollisionStreamsAtScale* streamsAtScale = DYNAMIC_CAST(MeshCookedCollisionStreamsAtScale*)(mParams->meshCookedCollisionStreamsAtScale.buf[i]); + if (streamsAtScale == NULL) + { + streamsAtScale = DYNAMIC_CAST(MeshCookedCollisionStreamsAtScale*)(traits->createNvParameterized(MeshCookedCollisionStreamsAtScale::staticClassName())); + mParams->meshCookedCollisionStreamsAtScale.buf[i] = streamsAtScale; + } + NvParameterized::Handle handle(*streamsAtScale); + streamsAtScale->getParameterHandle("meshCookedCollisionStreams", handle); + + // resizing PxParam ref arrays doesn't call destroy(), do this here + int32_t currentArraySize = 0; + streamsAtScale->getArraySize(handle, currentArraySize); + for (int j = currentArraySize - 1; j >= (int32_t)hullCount; j--) + { + NvParameterized::Interface*& hullStream = streamsAtScale->meshCookedCollisionStreams.buf[j]; + if (hullStream != NULL) + { + hullStream->destroy(); + } + hullStream = NULL; + } + streamsAtScale->resizeArray(handle, (int32_t)hullCount); + } +} + +bool DestructibleAssetCollision::addScale(const PxVec3& scale) +{ + if (getScaleIndex(scale, kDefaultDestructibleAssetCollisionScaleTolerance) >= 0) + { + return false; // Scale already exists + } + + int scaleIndex = mParams->scales.arraySizes[0]; + + NvParameterized::Handle handle(*mParams); + mParams->getParameterHandle("scales", handle); + mParams->resizeArray(handle, scaleIndex + 1); + mParams->getParameterHandle("meshCookedCollisionStreamsAtScale", handle); + mParams->resizeArray(handle, scaleIndex + 1); + + mConvexMeshContainer.resize((uint32_t)scaleIndex + 1); + + mParams->scales.buf[(uint32_t)scaleIndex] = scale; + mParams->meshCookedCollisionStreamsAtScale.buf[(uint32_t)scaleIndex] = NULL; + mConvexMeshContainer[(uint32_t)scaleIndex].reset(); + + return true; +} + +bool DestructibleAssetCollision::cookAll() +{ + bool result = true; + for (int i = 0; i < mParams->scales.arraySizes[0]; ++i) + { + if (!cookScale(mParams->scales.buf[i])) + { + result = false; + } + } + + return result; +} + +bool DestructibleAssetCollision::cookScale(const PxVec3& scale) +{ + if (mAsset == NULL) + { + return false; + } + + const int32_t partCount = mAsset->mParams->chunkConvexHullStartIndices.arraySizes[0]; + if (partCount <= 0) + { + return false; + } + + const uint32_t hullCount = mAsset->mParams->chunkConvexHullStartIndices.buf[partCount-1]; + + bool result = true; + for (uint16_t i = 0; i < hullCount; ++i) + { + PxConvexMesh* convexMesh = getConvexMesh(i, scale); + if (convexMesh == NULL) + { + result = false; + } + } + + return result; +} + +PxConvexMesh* DestructibleAssetCollision::getConvexMesh(uint32_t hullIndex, const PxVec3& scale) +{ + int32_t scaleIndex = getScaleIndex(scale, kDefaultDestructibleAssetCollisionScaleTolerance); + + if (scaleIndex >= 0) + { + if (scaleIndex < (int32_t)mConvexMeshContainer.size()) + { + physx::Array<PxConvexMesh*>& convexMeshSet = mConvexMeshContainer[(uint32_t)scaleIndex]; + if (hullIndex < convexMeshSet.size()) + { + PxConvexMesh* convexMesh = convexMeshSet[hullIndex]; + if (convexMesh != NULL) + { + return convexMesh; + } + } + } + } + + NvParameterized::Handle handle(*mParams); + NvParameterized::Traits* traits = GetInternalApexSDK()->getParameterizedTraits(); + + MeshCookedCollisionStreamsAtScale* streamsAtScale; + if (scaleIndex < 0) + { + scaleIndex = mParams->scales.arraySizes[0]; + + mParams->getParameterHandle("scales", handle); + mParams->resizeArray(handle, scaleIndex + 1); + mParams->getParameterHandle("meshCookedCollisionStreamsAtScale", handle); + mParams->resizeArray(handle, scaleIndex + 1); + + mConvexMeshContainer.resize((uint32_t)scaleIndex + 1); + + mParams->scales.buf[(uint32_t)scaleIndex] = scale; + } + + if (mParams->meshCookedCollisionStreamsAtScale.buf[scaleIndex] != NULL) + { + streamsAtScale = DYNAMIC_CAST(MeshCookedCollisionStreamsAtScale*)(mParams->meshCookedCollisionStreamsAtScale.buf[scaleIndex]); + } + else + { + streamsAtScale = DYNAMIC_CAST(MeshCookedCollisionStreamsAtScale*)(traits->createNvParameterized(MeshCookedCollisionStreamsAtScale::staticClassName())); + mParams->meshCookedCollisionStreamsAtScale.buf[scaleIndex] = streamsAtScale; + } + + if (mAsset != NULL && (int)mAsset->chunkConvexHulls.size() != streamsAtScale->meshCookedCollisionStreams.arraySizes[0]) + { + NvParameterized::Handle streamsHandle(*streamsAtScale); + streamsAtScale->getParameterHandle("meshCookedCollisionStreams", streamsHandle); + streamsHandle.resizeArray((int32_t)mAsset->chunkConvexHulls.size()); + mConvexMeshContainer[(uint32_t)scaleIndex].resize(mAsset->chunkConvexHulls.size()); + } + + if ((int)hullIndex >= streamsAtScale->meshCookedCollisionStreams.arraySizes[0]) + { + return NULL; + } + + PxConvexMesh* convexMesh = NULL; + + MeshCookedCollisionStream* stream = DYNAMIC_CAST(MeshCookedCollisionStream*)(streamsAtScale->meshCookedCollisionStreams.buf[hullIndex]); + if (stream == NULL) + { + if (mAsset == NULL) + { + return NULL; + } + + stream = DYNAMIC_CAST(MeshCookedCollisionStream*)(traits->createNvParameterized(MeshCookedCollisionStream::staticClassName())); + streamsAtScale->meshCookedCollisionStreams.buf[hullIndex] = stream; + + PX_PROFILE_ZONE("DestructibleCookChunkCollisionMeshes", GetInternalApexSDK()->getContextId()); + + // Update the asset's stats with the number of cooked collision convex meshes + if (mAsset != NULL) + { + ++mAsset->mRuntimeCookedConvexCount; + } + + const ConvexHullImpl& hullData = mAsset->chunkConvexHulls[hullIndex]; + + if (hullData.getVertexCount() == 0) + { + return NULL; + } + + Array<PxVec3> scaledPoints; + scaledPoints.resize(hullData.getVertexCount()); + PxVec3 centroid(0.0f); + for (uint32_t i = 0; i < scaledPoints.size(); ++i) + { + scaledPoints[i] = hullData.getVertex(i); // Cook at unit scale first + centroid += scaledPoints[i]; + } + centroid *= 1.0f/(float)scaledPoints.size(); + for (uint32_t i = 0; i < scaledPoints.size(); ++i) + { + scaledPoints[i] -= centroid; + } + + nvidia::PsMemoryBuffer memStream; + memStream.setEndianMode(PxFileBuf::ENDIAN_NONE); + PxStreamFromFileBuf nvs(memStream); + physx::PxConvexMeshDesc meshDesc; + meshDesc.points.count = scaledPoints.size(); + meshDesc.points.data = scaledPoints.begin(); + meshDesc.points.stride = sizeof(PxVec3); + meshDesc.flags = physx::PxConvexFlag::eCOMPUTE_CONVEX; + const float skinWidth = GetApexSDK()->getCookingInterface() != NULL ? GetApexSDK()->getCookingInterface()->getParams().skinWidth : 0.0f; + if (skinWidth > 0.0f) + { + meshDesc.flags |= physx::PxConvexFlag::eINFLATE_CONVEX; + } + bool success = GetApexSDK()->getCookingInterface()->cookConvexMesh(meshDesc, nvs); + + // Now scale all the points, in case this array is used as-is (failure cases) + for (uint32_t i = 0; i < scaledPoints.size(); ++i) + { + scaledPoints[i] += centroid; + scaledPoints[i] = scaledPoints[i].multiply(scale); + } + + if (success) + { + PxStreamFromFileBuf nvs(memStream); + convexMesh = GetApexSDK()->getPhysXSDK()->createConvexMesh(nvs); + + // Examine the mass properties to make sure they're reasonable. + if (convexMesh != NULL) + { + float mass; + PxMat33 localInertia; + PxVec3 localCenterOfMass; + convexMesh->getMassInformation(mass, localInertia, localCenterOfMass); + PxMat33 massFrame; + const PxVec3 massLocalInertia = diagonalizeSymmetric(massFrame, localInertia); + success = (mass > 0 && massLocalInertia.x > 0 && massLocalInertia.y > 0 && massLocalInertia.z > 0); + if (success && massLocalInertia.maxElement() > 4000*massLocalInertia.minElement()) + { + convexMesh->release(); + convexMesh = NULL; + success = false; + } + } + else + { + success = false; + } + + if (success) + { + // Now scale the convex hull + memStream.reset(); + memStream.setEndianMode(PxFileBuf::ENDIAN_NONE); + PxStreamFromFileBuf nvs(memStream); + + const uint32_t numVerts = convexMesh->getNbVertices(); + const PxVec3* verts = convexMesh->getVertices(); + + scaledPoints.resize(numVerts); + for (uint32_t i = 0; i < numVerts; ++i) + { + scaledPoints[i] = (verts[i] + centroid).multiply(scale); + } + + // Unfortunately, we must build our own triangle buffer from the polygon buffer + uint32_t triangleCount = 0; + for (uint32_t i = 0; i < convexMesh->getNbPolygons(); ++i) + { + physx::PxHullPolygon polygon; + convexMesh->getPolygonData(i, polygon); + triangleCount += polygon.mNbVerts - 2; + } + const uint8_t* indexBuffer = (const uint8_t*)convexMesh->getIndexBuffer(); + Array<uint32_t> indices; + indices.reserve(triangleCount*3); + for (uint32_t i = 0; i < convexMesh->getNbPolygons(); ++i) + { + physx::PxHullPolygon polygon; + convexMesh->getPolygonData(i, polygon); + for (uint16_t j = 1; j < polygon.mNbVerts-1; ++j) + { + indices.pushBack((uint32_t)indexBuffer[polygon.mIndexBase]); + indices.pushBack((uint32_t)indexBuffer[polygon.mIndexBase+j]); + indices.pushBack((uint32_t)indexBuffer[polygon.mIndexBase+j+1]); + } + } + + physx::PxConvexMeshDesc meshDesc; + meshDesc.points.count = scaledPoints.size(); + meshDesc.points.data = scaledPoints.begin(); + meshDesc.points.stride = sizeof(PxVec3); + meshDesc.flags = physx::PxConvexFlag::eCOMPUTE_CONVEX; + success = GetApexSDK()->getCookingInterface()->cookConvexMesh(meshDesc, nvs); + + convexMesh->release(); + convexMesh = NULL; + + if (success) + { + convexMesh = GetApexSDK()->getPhysXSDK()->createConvexMesh(nvs); + } + + if (convexMesh == NULL) + { + success = false; + } + } + } + + if (!success) + { + convexMesh = NULL; + memStream.reset(); + memStream.setEndianMode(PxFileBuf::ENDIAN_NONE); + PxStreamFromFileBuf nvs(memStream); + // Just form bbox + PxBounds3 bounds; + bounds.setEmpty(); + for (uint32_t i = 0; i < scaledPoints.size(); ++i) + { + bounds.include(scaledPoints[i]); + } + PX_ASSERT(!bounds.isEmpty()); + bounds.fattenFast(PxMax(0.00001f, bounds.getExtents().magnitude()*0.001f)); + scaledPoints.resize(8); + for (uint32_t i = 0; i < 8; ++i) + { + scaledPoints[i] = PxVec3((i & 1) ? bounds.maximum.x : bounds.minimum.x, + (i & 2) ? bounds.maximum.y : bounds.minimum.y, + (i & 4) ? bounds.maximum.z : bounds.minimum.z); + } + meshDesc.points.data = scaledPoints.begin(); + meshDesc.points.count = 8; + if (!GetApexSDK()->getCookingInterface()->cookConvexMesh(meshDesc, nvs)) + { + memStream.reset(); + } + } + + { + NvParameterized::Handle bytesHandle(stream); + stream->getParameterHandle("bytes", bytesHandle); + stream->resizeArray(bytesHandle, (int32_t)memStream.getWriteBufferSize()); + stream->setParamU8Array(bytesHandle, memStream.getWriteBuffer(), (int32_t)memStream.getWriteBufferSize()); + } + } + + if (convexMesh == NULL) + { + nvidia::PsMemoryBuffer memStream(stream->bytes.buf, (uint32_t)stream->bytes.arraySizes[0]); + memStream.setEndianMode(PxFileBuf::ENDIAN_NONE); + PxStreamFromFileBuf nvs(memStream); + convexMesh = GetApexSDK()->getPhysXSDK()->createConvexMesh(nvs); + } + + // These resizes should not be required, fix it and remove them + if (mConvexMeshContainer.size() <= (uint32_t)scaleIndex) + { + APEX_DEBUG_WARNING("The asset's (%s) convex mesh container needed resizing, debug this", mAsset->getName()); + mConvexMeshContainer.resize((uint32_t)scaleIndex+1); + } + if (mConvexMeshContainer[(uint32_t)scaleIndex].size() <= (uint32_t)hullIndex) + { + APEX_DEBUG_WARNING("The asset's (%s) convex mesh container at scale index %d needed resizing, debug this", mAsset->getName(), scaleIndex); + mConvexMeshContainer[(uint32_t)scaleIndex].resize(hullIndex+1); + } + + mConvexMeshContainer[(uint32_t)scaleIndex][hullIndex] = convexMesh; + + return convexMesh; +} + +MeshCookedCollisionStreamsAtScale* DestructibleAssetCollision::getCollisionAtScale(const PxVec3& scale) +{ + cookScale(scale); + + const int32_t scaleIndex = getScaleIndex(scale, kDefaultDestructibleAssetCollisionScaleTolerance); + if (scaleIndex < 0) + { + return NULL; + } + + return DYNAMIC_CAST(MeshCookedCollisionStreamsAtScale*)(mParams->meshCookedCollisionStreamsAtScale.buf[scaleIndex]); +} + +physx::Array<PxConvexMesh*>* DestructibleAssetCollision::getConvexMeshesAtScale(const PxVec3& scale) +{ + cookScale(scale); + + const int32_t scaleIndex = getScaleIndex(scale, kDefaultDestructibleAssetCollisionScaleTolerance); + if (scaleIndex < 0) + { + return NULL; + } + + return &mConvexMeshContainer[(uint32_t)scaleIndex]; +} + +PxFileBuf& DestructibleAssetCollision::deserialize(PxFileBuf& stream, const char* assetName) +{ + // If there are any referenced meshes in ANY scales we're going to revoke this operation as not supported + for (uint32_t i=0; i<mConvexMeshContainer.size(); i++) + { + if (mConvexMeshContainer.getReferenceCount(i) > 0) + { + APEX_DEBUG_INFO("Cannot deserialize the cooked collision data cache for asset <%s> scaleIdx <%d> because it is in use by actors", getAssetName(), i); + return stream; + } + } + + mAsset = NULL; + mConvexMeshContainer.reset(); + + /*uint32_t version =*/ + stream.readDword(); // Eat version #, not used since this is the initial version + + ApexSimpleString name; + name.deserialize(stream); + NvParameterized::Handle handle(*mParams); + mParams->getParameterHandle("assetName", handle); + mParams->setParamString(handle, name.c_str()); + + if (assetName != NULL) + mParams->setParamString(handle, assetName); + + stream >> mParams->cookingPlatform; + stream >> mParams->cookingVersionNum; + + NvParameterized::Traits* traits = GetInternalApexSDK()->getParameterizedTraits(); + + int scaleCount = (int)stream.readDword(); + mParams->getParameterHandle("scales", handle); + mParams->resizeArray(handle, scaleCount); + for (int i = 0; i < scaleCount; ++i) + { + stream >> mParams->scales.buf[i]; + } + + int meshScaleCount = (int)stream.readDword(); + mParams->getParameterHandle("meshCookedCollisionStreamsAtScale", handle); + mParams->resizeArray(handle, meshScaleCount); + mConvexMeshContainer.resize((uint32_t)meshScaleCount); + for (uint32_t i = 0; i < (uint32_t)meshScaleCount; ++i) + { + MeshCookedCollisionStreamsAtScale* streamsAtScale = DYNAMIC_CAST(MeshCookedCollisionStreamsAtScale*)(mParams->meshCookedCollisionStreamsAtScale.buf[i]); + if (streamsAtScale == NULL) + { + streamsAtScale = DYNAMIC_CAST(MeshCookedCollisionStreamsAtScale*)(traits->createNvParameterized(MeshCookedCollisionStreamsAtScale::staticClassName())); + mParams->meshCookedCollisionStreamsAtScale.buf[i] = streamsAtScale; + } + + handle.setInterface(streamsAtScale); + physx::Array<PxConvexMesh*>& meshSet = mConvexMeshContainer[i]; + + uint32_t meshCount = stream.readDword(); + streamsAtScale->getParameterHandle("meshCookedCollisionStreams", handle); + streamsAtScale->resizeArray(handle, (int32_t)meshCount); + meshSet.resize(meshCount); + for (uint32_t j = 0; j < meshCount; ++j) + { + MeshCookedCollisionStream* collisionStream = DYNAMIC_CAST(MeshCookedCollisionStream*)(streamsAtScale->meshCookedCollisionStreams.buf[j]); + if (collisionStream == NULL) + { + collisionStream = DYNAMIC_CAST(MeshCookedCollisionStream*)(traits->createNvParameterized(MeshCookedCollisionStream::staticClassName())); + streamsAtScale->meshCookedCollisionStreams.buf[j] = collisionStream; + } + + handle.setInterface(collisionStream); + + int bufferSize = (int)stream.readDword(); + collisionStream->getParameterHandle("bytes", handle); + collisionStream->resizeArray(handle, bufferSize); + stream.read(collisionStream->bytes.buf, (uint32_t)bufferSize); + + nvidia::PsMemoryBuffer memStream(collisionStream->bytes.buf, (uint32_t)collisionStream->bytes.arraySizes[0]); + memStream.setEndianMode(PxFileBuf::ENDIAN_NONE); + PxStreamFromFileBuf nvs(memStream); + meshSet[j] = GetApexSDK()->getPhysXSDK()->createConvexMesh(nvs); + } + } + return stream; +} + +PxFileBuf& DestructibleAssetCollision::serialize(PxFileBuf& stream) const +{ +#ifndef WITHOUT_APEX_AUTHORING + stream << (uint32_t)Version::Current; + + ApexSimpleString name(mParams->assetName); + name.serialize(stream); + + stream << mParams->cookingPlatform; + stream << mParams->cookingVersionNum; + + stream.storeDword((uint32_t)mParams->scales.arraySizes[0]); + for (uint32_t i = 0; i < (uint32_t)mParams->scales.arraySizes[0]; ++i) + { + stream << mParams->scales.buf[i]; + } + + stream.storeDword((uint32_t)mParams->meshCookedCollisionStreamsAtScale.arraySizes[0]); + for (uint32_t i = 0; i < (uint32_t)mParams->meshCookedCollisionStreamsAtScale.arraySizes[0]; ++i) + { + MeshCookedCollisionStreamsAtScale* streamsAtScale = DYNAMIC_CAST(MeshCookedCollisionStreamsAtScale*)(mParams->meshCookedCollisionStreamsAtScale.buf[i]); + if (streamsAtScale == NULL) + { + stream.storeDword(0); + } + else + { + stream.storeDword((uint32_t)streamsAtScale->meshCookedCollisionStreams.arraySizes[0]); + for (uint32_t j = 0; j < (uint32_t)streamsAtScale->meshCookedCollisionStreams.arraySizes[0]; ++j) + { + MeshCookedCollisionStream* collisionStream = DYNAMIC_CAST(MeshCookedCollisionStream*)(streamsAtScale->meshCookedCollisionStreams.buf[j]); + if (collisionStream == NULL) + { + stream.storeDword(0); + } + else + { + stream.storeDword((uint32_t)collisionStream->bytes.arraySizes[0]); + stream.write(collisionStream->bytes.buf, (uint32_t)collisionStream->bytes.arraySizes[0]); + } + } + } + } +#endif // #ifndef WITHOUT_APEX_AUTHORING + return stream; +} + +bool DestructibleAssetCollision::platformAndVersionMatch() const +{ + const PxCookingParams& cookingParams = GetInternalApexSDK()->getCookingInterface()->getParams(); + const uint32_t presentCookingVersionNum = GetInternalApexSDK()->getCookingVersion(); + + return ((uint32_t) cookingParams.targetPlatform == mParams->cookingPlatform) && + ((presentCookingVersionNum & 0xFFFF0000) == (mParams->cookingVersionNum & 0xFFFF0000)); +} + +void DestructibleAssetCollision::setPlatformAndVersion() +{ + mParams->cookingPlatform = GetInternalApexSDK()->getCookingInterface()->getParams().targetPlatform; + mParams->cookingVersionNum = GetInternalApexSDK()->getCookingVersion(); +} + +uint32_t DestructibleAssetCollision::memorySize() const +{ + uint32_t size = 0; + + for (int i = 0; i < mParams->meshCookedCollisionStreamsAtScale.arraySizes[0]; ++i) + { + MeshCookedCollisionStreamsAtScale* streamsAtScale = DYNAMIC_CAST(MeshCookedCollisionStreamsAtScale*)(mParams->meshCookedCollisionStreamsAtScale.buf[i]); + if (streamsAtScale == NULL) + { + continue; + } + for (int j = 0; j < streamsAtScale->meshCookedCollisionStreams.arraySizes[0]; ++j) + { + MeshCookedCollisionStream* stream = DYNAMIC_CAST(MeshCookedCollisionStream*)(streamsAtScale->meshCookedCollisionStreams.buf[j]); + if (stream == NULL) + { + continue; + } + size += (uint32_t)stream->bytes.arraySizes[0]; + } + } + + return size; +} + +void DestructibleAssetCollision::clearUnreferencedSets() +{ + for (uint32_t i = 0; i < mConvexMeshContainer.size(); ++i) + { + if (mConvexMeshContainer.getReferenceCount(i) == 0) + { + MeshCookedCollisionStreamsAtScale* streamsAtScale = DYNAMIC_CAST(MeshCookedCollisionStreamsAtScale*)(mParams->meshCookedCollisionStreamsAtScale.buf[i]); + if (streamsAtScale) + { + NvParameterized::Handle handle(*streamsAtScale); + streamsAtScale->getParameterHandle("meshCookedCollisionStreams", handle); + streamsAtScale->resizeArray(handle, 0); + } + + // We need to NULL this pointer, otherwise we'll be accessing old data as a result of the reset below + DestructibleAssetImpl* asset = mAsset; + if (asset) + { + asset->mCollisionMeshes = NULL; + } + } + } + mConvexMeshContainer.reset(false); +} + +// Spit out warnings to the error stream for any referenced sets +void DestructibleAssetCollision::reportReferencedSets() +{ + for (uint32_t i = 0; i < mConvexMeshContainer.size(); ++i) + { + if (mConvexMeshContainer.getReferenceCount(i)) + { + APEX_DEBUG_WARNING("Clearing a referenced convex mesh container for asset: %s", mAsset); + } + } +} + +bool DestructibleAssetCollision::incReferenceCount(int scaleIndex) +{ + if (scaleIndex < 0 || scaleIndex >= (int)mConvexMeshContainer.size()) + { + return false; + } + + mConvexMeshContainer.incReferenceCount((uint32_t)scaleIndex); + + return true; +} + +bool DestructibleAssetCollision::decReferenceCount(int scaleIndex) +{ + if (scaleIndex < 0 || scaleIndex >= (int)mConvexMeshContainer.size()) + { + return false; + } + + return mConvexMeshContainer.decReferenceCount((uint32_t)scaleIndex); +} + +// The source 'collisionSet' is not const because it's list of PxConvexMesh pointers +// in 'mConvexMeshContainer' needs to be cleared so they aren't released in +// DestructibleAssetCollision::resize(0) +void DestructibleAssetCollision::merge(DestructibleAssetCollision& collisionSet) +{ + NvParameterized::Traits* traits = GetInternalApexSDK()->getParameterizedTraits(); + + // Prepare the convexMesh container for the collisionSet's meshes + if (mConvexMeshContainer.size() < collisionSet.mConvexMeshContainer.size()) + { + mConvexMeshContainer.resize(collisionSet.mConvexMeshContainer.size()); + } + + // Loop through scales contained in collisionSet + for (uint32_t i = 0; i < (uint32_t)collisionSet.mParams->scales.arraySizes[0]; ++i) + { + const PxVec3& scale = collisionSet.mParams->scales.buf[i]; + int scaleIndex = getScaleIndex(scale, kDefaultDestructibleAssetCollisionScaleTolerance); + if (scaleIndex < 0) + { + // Scale not found, add it to this set + addScale(scale); + scaleIndex = getScaleIndex(scale, kDefaultDestructibleAssetCollisionScaleTolerance); + if (scaleIndex < 0) + { + continue; // Failed to add scale + } + if (mParams->meshCookedCollisionStreamsAtScale.buf[scaleIndex] == NULL) // Create streams if we need them + { + mParams->meshCookedCollisionStreamsAtScale.buf[scaleIndex] = DYNAMIC_CAST(MeshCookedCollisionStreamsAtScale*)(traits->createNvParameterized(MeshCookedCollisionStreamsAtScale::staticClassName())); + } + mParams->meshCookedCollisionStreamsAtScale.buf[scaleIndex]->copy(*collisionSet.mParams->meshCookedCollisionStreamsAtScale.buf[i]); + } + else + { + MeshCookedCollisionStreamsAtScale* streamsAtScale = DYNAMIC_CAST(MeshCookedCollisionStreamsAtScale*)(mParams->meshCookedCollisionStreamsAtScale.buf[scaleIndex]); + PX_ASSERT(streamsAtScale != NULL); + if (streamsAtScale->meshCookedCollisionStreams.arraySizes[0] == 0) // Only merge if this scale is empty; we won't stomp any existing data + { + mParams->meshCookedCollisionStreamsAtScale.buf[scaleIndex]->copy(*collisionSet.mParams->meshCookedCollisionStreamsAtScale.buf[i]); + } + + // also copy the PxConvexMesh pointers (because the source collisionSet has them already at this point) + physx::Array<PxConvexMesh*>& srcConvexMeshSet = collisionSet.mConvexMeshContainer[i]; + + // make sure the destination list is present + bool convexMeshSetResized = false; + if (mConvexMeshContainer[i].size() < srcConvexMeshSet.size()) + { + convexMeshSetResized = true; + mConvexMeshContainer[i].resize(srcConvexMeshSet.size()); + } + + for (uint32_t j=0; j<srcConvexMeshSet.size(); j++) + { + // Only merge if we need PxConvexMesh pointers, otherwise just release these + // newly created PxConvexMesh after exiting this method + if(mConvexMeshContainer[i][j] == NULL || convexMeshSetResized) + { + mConvexMeshContainer[i][j] = srcConvexMeshSet[j]; + // This prevents DestructibleAssetCollision::resize() from releasing the convex mesh + srcConvexMeshSet[j] = NULL; + } + } + } + } +} + +int32_t DestructibleAssetCollision::getScaleIndex(const PxVec3& scale, float tolerance) const +{ + for (int i = 0; i < mParams->scales.arraySizes[0]; ++i) + { + const PxVec3& error = scale - mParams->scales.buf[i]; + if (PxAbs(error.x) <= tolerance && PxAbs(error.y) <= tolerance && PxAbs(error.z) <= tolerance) + { + return i; + } + } + + return -1; +} + +/** DestructibleAsset::ScatterMeshInstanceInfo **/ + +DestructibleAssetImpl::ScatterMeshInstanceInfo::~ScatterMeshInstanceInfo() +{ + if (m_actor != NULL) + { + m_actor->release(); + m_actor = NULL; + } + + if (m_instanceBuffer != NULL) + { + UserRenderResourceManager* rrm = GetInternalApexSDK()->getUserRenderResourceManager(); + rrm->releaseInstanceBuffer(*m_instanceBuffer); + m_instanceBuffer = NULL; + } +} + +} +} // end namespace nvidia + + |