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; }
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 }