///////// FApexPhysX3Interface ////////////////////////////////// void FApexPhysX3Interface::setContactReportFlags(physx::PxShape* PShape, physx::PxPairFlags PFlags, apex::DestructibleActor* actor, PxU16 actorChunkIndex) { UDestructibleComponent* DestructibleComponent = Cast<UDestructibleComponent>(FPhysxUserData::Get<UPrimitiveComponent>(PShape->userData)); check(DestructibleComponent); DestructibleComponent->Pair(actorChunkIndex, PShape); }
void UDestructibleComponent::UpdateDestructibleChunkTM(const TArray<const PxRigidActor*>& ActiveActors) { //We want to consolidate the transforms so that we update each destructible component once by passing it an array of chunks to update. //This helps avoid a lot of duplicated work like marking render dirty, computing inverse world component, etc... TMap<UDestructibleComponent*, TArray<FUpdateChunksInfo> > ComponentUpdateMapping; //prepare map to update destructible components TArray<PxShape*> Shapes; for (const PxRigidActor* RigidActor : ActiveActors) { if (const FDestructibleChunkInfo* DestructibleChunkInfo = FPhysxUserData::Get<FDestructibleChunkInfo>(RigidActor->userData)) { if (GApexModuleDestructible->owns(RigidActor) && DestructibleChunkInfo->OwningComponent.IsValid()) { Shapes.AddUninitialized(RigidActor->getNbShapes()); int32 NumShapes = RigidActor->getShapes(Shapes.GetData(), Shapes.Num()); for (int32 ShapeIdx = 0; ShapeIdx < Shapes.Num(); ++ShapeIdx) { PxShape* Shape = Shapes[ShapeIdx]; int32 ChunkIndex; if (NxDestructibleActor* DestructibleActor = GApexModuleDestructible->getDestructibleAndChunk(Shape, &ChunkIndex)) { const physx::PxMat44 ChunkPoseRT = DestructibleActor->getChunkPose(ChunkIndex); const physx::PxTransform Transform(ChunkPoseRT); if (UDestructibleComponent* DestructibleComponent = Cast<UDestructibleComponent>(FPhysxUserData::Get<UPrimitiveComponent>(DestructibleActor->userData))) { if (DestructibleComponent->IsRegistered()) { TArray<FUpdateChunksInfo>& UpdateInfos = ComponentUpdateMapping.FindOrAdd(DestructibleComponent); FUpdateChunksInfo* UpdateInfo = new (UpdateInfos)FUpdateChunksInfo(ChunkIndex, P2UTransform(Transform)); } } } } Shapes.Empty(Shapes.Num()); //we want to keep largest capacity array to avoid reallocs } } } //update each component for (auto It = ComponentUpdateMapping.CreateIterator(); It; ++It) { UDestructibleComponent* DestructibleComponent = It.Key(); TArray<FUpdateChunksInfo>& UpdateInfos = It.Value(); if (DestructibleComponent->IsFracturedOrInitiallyStatic()) { DestructibleComponent->SetChunksWorldTM(UpdateInfos); } else { //if we haven't fractured it must mean that we're simulating a destructible and so we should update our ComponentToWorld based on the single rigid body DestructibleComponent->SyncComponentToRBPhysics(); } } }
void FApexChunkReport::onStateChangeNotify(const apex::ChunkStateEventData& visibilityEvent) { UDestructibleComponent* DestructibleComponent = Cast<UDestructibleComponent>(FPhysxUserData::Get<UPrimitiveComponent>(visibilityEvent.destructible->userData)); check(DestructibleComponent); if (DestructibleComponent->IsPendingKill()) //don't notify if object is being destroyed { return; } DestructibleComponent->OnVisibilityEvent(visibilityEvent); }
void FApexChunkReport::onDamageNotify(const apex::DamageEventReportData& damageEvent) { UDestructibleComponent* DestructibleComponent = Cast<UDestructibleComponent>(FPhysxUserData::Get<UPrimitiveComponent>(damageEvent.destructible->userData)); check(DestructibleComponent); if (DestructibleComponent->IsPendingKill()) //don't notify if object is being destroyed { return; } DestructibleComponent->GetWorld()->GetPhysicsScene()->AddPendingDamageEvent(DestructibleComponent, damageEvent); }
void FPhysScene::DispatchPhysNotifications() { SCOPE_CYCLE_COUNTER(STAT_PhysicsEventTime); //Collision notification { // Let the game-specific PhysicsCollisionHandler process any physics collisions that took place if (OwningWorld != NULL && OwningWorld->PhysicsCollisionHandler != NULL) { OwningWorld->PhysicsCollisionHandler->HandlePhysicsCollisions(PendingCollisionNotifies); } // Fire any collision notifies in the queue. for (int32 i = 0; i<PendingCollisionNotifies.Num(); i++) { FCollisionNotifyInfo& NotifyInfo = PendingCollisionNotifies[i]; if (NotifyInfo.RigidCollisionData.ContactInfos.Num() > 0) { if (NotifyInfo.bCallEvent0 && NotifyInfo.IsValidForNotify() && NotifyInfo.Info0.Actor.IsValid()) { NotifyInfo.Info0.Actor->DispatchPhysicsCollisionHit(NotifyInfo.Info0, NotifyInfo.Info1, NotifyInfo.RigidCollisionData); } // Need to check IsValidForNotify again in case first call broke something. if (NotifyInfo.bCallEvent1 && NotifyInfo.IsValidForNotify() && NotifyInfo.Info1.Actor.IsValid()) { NotifyInfo.RigidCollisionData.SwapContactOrders(); NotifyInfo.Info1.Actor->DispatchPhysicsCollisionHit(NotifyInfo.Info1, NotifyInfo.Info0, NotifyInfo.RigidCollisionData); } } } PendingCollisionNotifies.Empty(); } #if WITH_SUBSTEPPING #if WITH_APEX //TODO: Queue is always empty for now - waiting for support from NVIDIA //Destructible notification { for (int32 i = 0; i < DestructibleDamageEventQueue.Num(); ++i) { const NxApexDamageEventReportData & damageEvent = DestructibleDamageEventQueue[i]; UDestructibleComponent* DestructibleComponent = Cast<UDestructibleComponent>(FPhysxUserData::Get<UPrimitiveComponent>(damageEvent.destructible->userData)); DestructibleComponent->OnDamageEvent(damageEvent); } DestructibleDamageEventQueue.Empty(); } #endif #endif }
void FDestructibleMeshEditorViewportClient::Draw( const FSceneView* View,FPrimitiveDrawInterface* PDI ) { FEditorViewportClient::Draw(View, PDI); #if WITH_APEX const bool DrawChunkMarker = true; UDestructibleComponent* Comp = PreviewDestructibleComp.Get(); if (Comp) { if (Comp->DestructibleMesh != NULL && Comp->DestructibleMesh->FractureSettings != NULL) { if (Comp->DestructibleMesh->ApexDestructibleAsset != NULL) { NxDestructibleAsset* Asset = Comp->DestructibleMesh->ApexDestructibleAsset; const NxRenderMeshAsset* RenderMesh = Asset->getRenderMeshAsset(); for (uint32 i=0; i < Asset->getChunkCount(); ++i) { int32 PartIdx = Asset->getPartIndex(i); int32 BoneIdx = i+1; if ( SelectedChunkIndices.Contains(i) ) { PxBounds3 PBounds = RenderMesh->getBounds(PartIdx); FVector Center = P2UVector(PBounds.getCenter()) + Comp->GetBoneLocation(Comp->GetBoneName(BoneIdx)); FVector Extent = P2UVector(PBounds.getExtents()); FBox Bounds(Center - Extent, Center + Extent); DrawWireBox(PDI, Bounds, FColor::Blue, SDPG_World); } } } } } #endif // WITH_APEX }
/** Set info in the HitResult (Actor, Component, PhysMaterial, BoneName, Item) based on the supplied shape and face index */ static void SetHitResultFromShapeAndFaceIndex(const PxShape* PShape, const PxRigidActor* PActor, const uint32 FaceIndex, FHitResult& OutResult, bool bReturnPhysMat) { const FBodyInstance* BodyInst = FPhysxUserData::Get<FBodyInstance>(PActor->userData); FDestructibleChunkInfo* ChunkInfo = FPhysxUserData::Get<FDestructibleChunkInfo>(PShape->userData); UPrimitiveComponent* PrimComp = FPhysxUserData::Get<UPrimitiveComponent>(PShape->userData); if(BodyInst) { BodyInst = BodyInst->GetOriginalBodyInstance(PShape); } if (ChunkInfo == NULL) { ChunkInfo = FPhysxUserData::Get<FDestructibleChunkInfo>(PActor->userData); } UPrimitiveComponent* OwningComponent = ChunkInfo != NULL ? ChunkInfo->OwningComponent.Get() : NULL; if (OwningComponent == NULL && BodyInst != NULL) { OwningComponent = BodyInst->OwnerComponent.Get(); } // If the shape has a different parent component, we take that one instead of the ChunkInfo. This can happen in some // cases where APEX moves shapes internally to another actor ( ex. FormExtended structures ) if (PrimComp != NULL && OwningComponent != PrimComp) { OwningComponent = PrimComp; } OutResult.PhysMaterial = NULL; bool bReturnBody = false; // Grab actor/component if( OwningComponent ) { OutResult.Actor = OwningComponent->GetOwner(); OutResult.Component = OwningComponent; if (bReturnPhysMat) { // This function returns the single material in all cases other than trimesh or heightfield if(PxMaterial* PxMat = PShape->getMaterialFromInternalFaceIndex(FaceIndex)) { OutResult.PhysMaterial = FPhysxUserData::Get<UPhysicalMaterial>(PxMat->userData); } } bReturnBody = OwningComponent->bMultiBodyOverlap; } // For destructibles give the ChunkInfo-Index as Item if (bReturnBody && ChunkInfo) { OutResult.Item = ChunkInfo->ChunkIndex; UDestructibleComponent* DMComp = Cast<UDestructibleComponent>(OwningComponent); OutResult.BoneName = DMComp->GetBoneName(UDestructibleComponent::ChunkIdxToBoneIdx(ChunkInfo->ChunkIndex)); } // If BodyInstance and not destructible, give BodyIndex as Item else if (BodyInst) { OutResult.Item = BodyInst->InstanceBodyIndex; const UBodySetup* BodySetup = BodyInst->BodySetup.Get(); if (BodySetup) { OutResult.BoneName = BodySetup->BoneName; } } else { // invalid index OutResult.Item = INDEX_NONE; OutResult.BoneName = NAME_None; } OutResult.FaceIndex = INDEX_NONE; }
bool SetApexDestructibleAsset(UDestructibleMesh& DestructibleMesh, NxDestructibleAsset& ApexDestructibleAsset, FSkeletalMeshImportData* OutData, EDestructibleImportOptions::Type Options) { DestructibleMesh.PreEditChange(NULL); ExistingDestMeshData * ExistDestMeshDataPtr = SaveExistingDestMeshData(&DestructibleMesh); // The asset is going away, which will destroy any actors created from it. We must destroy the physics state of any destructible mesh components before we release the asset. for(TObjectIterator<UDestructibleComponent> It; It; ++It) { UDestructibleComponent* DestructibleComponent = *It; if(DestructibleComponent->SkeletalMesh == &DestructibleMesh && DestructibleComponent->IsPhysicsStateCreated()) { DestructibleComponent->DestroyPhysicsState(); } } // Release old NxDestructibleAsset if it exists if (DestructibleMesh.ApexDestructibleAsset != NULL && DestructibleMesh.ApexDestructibleAsset != &ApexDestructibleAsset) { GPhysCommandHandler->DeferredRelease(DestructibleMesh.ApexDestructibleAsset); } // BRGTODO - need to remove the render data from the ApexDestructibleAsset, no longer need it // Removing const cast ... we'll have to make it non-const anyway when we modify it DestructibleMesh.ApexDestructibleAsset = &ApexDestructibleAsset; if ( !(Options&EDestructibleImportOptions::PreserveSettings) ) { // Resize the depth parameters array to the appropriate size DestructibleMesh.DefaultDestructibleParameters.DepthParameters.Init(FDestructibleDepthParameters(), ApexDestructibleAsset.getDepthCount()); // Resize the fracture effects array to the appropriate size DestructibleMesh.FractureEffects.AddZeroed(ApexDestructibleAsset.getDepthCount()); // Load the UnrealEd-editable parameters from the destructible asset DestructibleMesh.LoadDefaultDestructibleParametersFromApexAsset(); } // Create body setup for the destructible mesh DestructibleMesh.CreateBodySetup(); #if 0 // BRGTODO // warning for missing smoothing group info CheckSmoothingInfo(FbxMesh); #endif FSkeletalMeshImportData TempData; // Fill with data from buffer FSkeletalMeshImportData* SkelMeshImportDataPtr = &TempData; if( OutData ) { SkelMeshImportDataPtr = OutData; } // Get all material names here ImportMaterialsForSkelMesh(*SkelMeshImportDataPtr, ApexDestructibleAsset); // Import animation hierarchy, although this is trivial for an Apex Destructible Asset CreateBones(*SkelMeshImportDataPtr, ApexDestructibleAsset); // Import graphics data bool bHaveNormals, bHaveTangents; if (!FillSkelMeshImporterFromApexDestructibleAsset(*SkelMeshImportDataPtr, ApexDestructibleAsset, bHaveNormals, bHaveTangents)) { return false; } #if 0 // BRGTODO - what is this? if( SkelMeshImportDataPtr->Materials.Num() == FbxMatList.Num() ) { // reorder material according to "SKinXX" in material name SetMaterialSkinXXOrder(*SkelMeshImportDataPtr, FbxMatList ); } #endif #if 0 // BRGTODO - what is this? if( ImportOptions->bSplitNonMatchingTriangles ) { DoUnSmoothVerts(*SkelMeshImportDataPtr); } #endif // process materials from import data ProcessImportMeshMaterials( DestructibleMesh.Materials,*SkelMeshImportDataPtr ); // process reference skeleton from import data int32 SkeletalDepth=0; if(!ProcessImportMeshSkeleton(DestructibleMesh.RefSkeleton, SkeletalDepth, *SkelMeshImportDataPtr)) { return false; } UE_LOG(LogApexDestructibleAssetImport, Warning, TEXT("Bones digested - %i Depth of hierarchy - %i"), DestructibleMesh.RefSkeleton.GetNum(), SkeletalDepth); // process bone influences from import data ProcessImportMeshInfluences(*SkelMeshImportDataPtr); FSkeletalMeshResource& DestructibleMeshResource = *DestructibleMesh.GetImportedResource(); check(DestructibleMeshResource.LODModels.Num() == 0); DestructibleMeshResource.LODModels.Empty(); new(DestructibleMeshResource.LODModels)FStaticLODModel(); DestructibleMesh.LODInfo.Empty(); DestructibleMesh.LODInfo.AddZeroed(); DestructibleMesh.LODInfo[0].LODHysteresis = 0.02f; // Create initial bounding box based on expanded version of reference pose for meshes without physics assets. Can be overridden by artist. FBox BoundingBox(SkelMeshImportDataPtr->Points.GetData(), SkelMeshImportDataPtr->Points.Num()); DestructibleMesh.Bounds= FBoxSphereBounds(BoundingBox); // Store whether or not this mesh has vertex colors DestructibleMesh.bHasVertexColors = SkelMeshImportDataPtr->bHasVertexColors; FStaticLODModel& LODModel = DestructibleMeshResource.LODModels[0]; // Pass the number of texture coordinate sets to the LODModel. Ensure there is at least one UV coord LODModel.NumTexCoords = FMath::Max<uint32>(1,SkelMeshImportDataPtr->NumTexCoords); // if( bCreateRenderData ) // We always create render data { // copy vertex data needed to generate skinning streams for LOD TArray<FVector> LODPoints; TArray<FMeshWedge> LODWedges; TArray<FMeshFace> LODFaces; TArray<FVertInfluence> LODInfluences; TArray<int32> LODPointToRawMap; SkelMeshImportDataPtr->CopyLODImportData(LODPoints,LODWedges,LODFaces,LODInfluences,LODPointToRawMap); IMeshUtilities& MeshUtilities = FModuleManager::Get().LoadModuleChecked<IMeshUtilities>("MeshUtilities"); // Create actual rendering data. if (!MeshUtilities.BuildSkeletalMesh(DestructibleMeshResource.LODModels[0], DestructibleMesh.RefSkeleton, LODInfluences,LODWedges,LODFaces,LODPoints,LODPointToRawMap,false,!bHaveNormals,!bHaveTangents)) { DestructibleMesh.MarkPendingKill(); return false; } // Presize the per-section shadow casting array with the number of sections in the imported LOD. const int32 NumSections = LODModel.Sections.Num(); for ( int32 SectionIndex = 0 ; SectionIndex < NumSections ; ++SectionIndex ) { DestructibleMesh.LODInfo[0].TriangleSortSettings.AddZeroed(); } if (ExistDestMeshDataPtr) { RestoreExistingDestMeshData(ExistDestMeshDataPtr, &DestructibleMesh); delete ExistDestMeshDataPtr; ExistDestMeshDataPtr = NULL; } DestructibleMesh.CalculateInvRefMatrices(); DestructibleMesh.PostEditChange(); DestructibleMesh.MarkPackageDirty(); #if 0 // BRGTODO : Check, we don't need this, do we? // We have to go and fix any AnimSetMeshLinkup objects that refer to this skeletal mesh, as the reference skeleton has changed. for(TObjectIterator<UAnimSet> It;It;++It) { UAnimSet* AnimSet = *It; // Get DestructibleMesh path name FName SkelMeshName = FName( *DestructibleMesh.GetPathName() ); // See if we have already cached this Skeletal Mesh. const int32* IndexPtr = AnimSet->SkelMesh2LinkupCache.Find( SkelMeshName ); if( IndexPtr ) { AnimSet->LinkupCache( *IndexPtr ).BuildLinkup( &DestructibleMesh, AnimSet ); } } #endif // Now iterate over all skeletal mesh components re-initialising them. for(TObjectIterator<UDestructibleComponent> It; It; ++It) { UDestructibleComponent* DestructibleComponent = *It; if(DestructibleComponent->SkeletalMesh == &DestructibleMesh) { FComponentReregisterContext ReregisterContext(DestructibleComponent); } } } #if INVERT_Y_AND_V // Apply transformation for Y inversion const physx::PxMat44 MirrorY = physx::PxMat44(physx::PxVec4(1.0f, -1.0f, 1.0f, 1.0f)); #if !USE_TEMPORARY_TRANSFORMATION_FUNCTION ApexDestructibleAsset.applyTransformation(MirrorY, 1.0f); #else ApplyTransformationToApexDestructibleAsset( ApexDestructibleAsset, MirrorY ); #endif #endif return true; }
void FDestructibleMeshEditorViewportClient::ProcessClick( class FSceneView& View, class HHitProxy* HitProxy, FKey Key, EInputEvent Event, uint32 HitX, uint32 HitY ) { #if WITH_APEX bool bKeepSelection = Viewport->KeyState(EKeys::LeftControl) || Viewport->KeyState(EKeys::RightControl); bool bSelectionChanged = false; if (Key == EKeys::LeftMouseButton && Event == EInputEvent::IE_Released) { UDestructibleComponent* Comp = PreviewDestructibleComp.Get(); NxDestructibleAsset* Asset = Comp->DestructibleMesh->ApexDestructibleAsset; const NxRenderMeshAsset* RenderMesh = Asset->getRenderMeshAsset(); FVector2D ScreenPos(HitX, HitY); FVector ClickOrigin, ViewDir; View.DeprojectFVector2D(ScreenPos, ClickOrigin, ViewDir); float NearestHitDistance = FLT_MAX; int32 ClickedChunk = -1; for (uint32 i=0; i < Asset->getChunkCount(); ++i) { int32 PartIdx = Asset->getPartIndex(i); int32 BoneIdx = i+1; if (!Comp->IsBoneHidden(BoneIdx)) { PxBounds3 PBounds = RenderMesh->getBounds(PartIdx); FVector Center = P2UVector(PBounds.getCenter()) + Comp->GetBoneLocation(Comp->GetBoneName(BoneIdx)); FVector Extent = P2UVector(PBounds.getExtents()); FBox Bounds(Center - Extent, Center + Extent); FVector HitLoc, HitNorm; float HitTime; if (FMath::LineExtentBoxIntersection(Bounds, ClickOrigin, ClickOrigin + ViewDir * 1000.0f, FVector(0,0,0), HitLoc, HitNorm, HitTime)) { float dist = (HitLoc - ClickOrigin).SizeSquared(); if (dist < NearestHitDistance) { NearestHitDistance = dist; ClickedChunk = i; } } } } if (ClickedChunk >= 0) { int32 Idx = SelectedChunkIndices.Find(ClickedChunk); if (Idx < 0) { if (!bKeepSelection) { SelectedChunkIndices.Empty(); } SelectedChunkIndices.Add(ClickedChunk); bSelectionChanged = true; } else { SelectedChunkIndices.RemoveAt(Idx); bSelectionChanged = true; } } else if (!bKeepSelection) { SelectedChunkIndices.Empty(); bSelectionChanged = true; } } if (bSelectionChanged) { UpdateChunkSelection(SelectedChunkIndices); } #endif // WITH_APEX }