UDestructibleMesh* ImportDestructibleMeshFromApexDestructibleAsset(UObject* InParent, NxDestructibleAsset& ApexDestructibleAsset, FName Name, EObjectFlags Flags, FSkeletalMeshImportData* OutData, EDestructibleImportOptions::Type Options) { // The APEX Destructible Asset contains an APEX Render Mesh Asset, get a pointer to this const physx::NxRenderMeshAsset* ApexRenderMesh = ApexDestructibleAsset.getRenderMeshAsset(); if (ApexRenderMesh == NULL) { return NULL; } // Number of submeshes (aka "elements" in Unreal) const physx::PxU32 SubmeshCount = ApexRenderMesh->getSubmeshCount(); if (SubmeshCount == 0) { return NULL; } // Make sure rendering is done - so we are not changing data being used by collision drawing. FlushRenderingCommands(); UDestructibleMesh* DestructibleMesh = FindObject<UDestructibleMesh>(InParent, *Name.ToString()); if (DestructibleMesh == NULL) { // Create the new UDestructibleMesh object if the one with the same name does not exist DestructibleMesh = NewObject<UDestructibleMesh>(InParent, Name, Flags); } if (!(Options & EDestructibleImportOptions::PreserveSettings)) { // Store the current file path and timestamp for re-import purposes // @todo AssetImportData make a data class for Apex destructible assets DestructibleMesh->AssetImportData = NewObject<UAssetImportData>(DestructibleMesh); DestructibleMesh->AssetImportData->Update(UFactory::CurrentFilename); DestructibleMesh->AssetImportData->bDirty = false; } DestructibleMesh->PreEditChange(NULL); // Build FractureSettings from ApexDestructibleAsset in case we want to re-fracture #if WITH_EDITORONLY_DATA DestructibleMesh->CreateFractureSettings(); DestructibleMesh->FractureSettings->BuildRootMeshFromApexDestructibleAsset(ApexDestructibleAsset, Options); // Fill materials DestructibleMesh->FractureSettings->Materials.Reset(DestructibleMesh->Materials.Num()); for (int32 MaterialIndex = 0; MaterialIndex < DestructibleMesh->Materials.Num(); ++MaterialIndex) { DestructibleMesh->FractureSettings->Materials.Insert(DestructibleMesh->Materials[MaterialIndex].MaterialInterface, MaterialIndex); } #endif // WITH_EDITORONLY_DATA if (!SetApexDestructibleAsset(*DestructibleMesh, ApexDestructibleAsset, OutData, Options)) { // should remove this destructible mesh. if not, this object causes a crash when ticking because it doesn't have proper rendering resources // @TODO : creates this destructible mesh after loading data completely DestructibleMesh->PostEditChange(); DestructibleMesh->ConditionalBeginDestroy(); return NULL; } return DestructibleMesh; }
void FDestructibleMeshEditorViewportClient::Fracture() { #if WITH_APEX UDestructibleMesh* DestructibleMesh = DestructibleMeshEditorPtr.Pin()->GetDestructibleMesh(); if (DestructibleMesh != NULL) { TComponentReregisterContext<USkinnedMeshComponent> ReregisterContext; DestructibleMesh->ReleaseResources(); DestructibleMesh->ReleaseResourcesFence.Wait(); if (DestructibleMesh->SourceStaticMesh) { DestructibleMesh->BuildFractureSettingsFromStaticMesh(DestructibleMesh->SourceStaticMesh); } else if (DestructibleMesh->ApexDestructibleAsset != NULL) { DestructibleMesh = ImportDestructibleMeshFromApexDestructibleAsset(DestructibleMesh->GetOuter(), *DestructibleMesh->ApexDestructibleAsset, DestructibleMesh->GetFName(), DestructibleMesh->GetFlags(), NULL, EDestructibleImportOptions::PreserveSettings); } DestructibleMesh->FractureSettings->CreateVoronoiSitesInRootMesh(); DestructibleMesh->FractureSettings->VoronoiSplitMesh(); BuildDestructibleMeshFromFractureSettings(*DestructibleMesh, NULL); } DestructibleMeshEditorPtr.Pin()->RefreshTool(); DestructibleMeshEditorPtr.Pin()->SetCurrentPreviewDepth(0xFFFFFFFF); // This will get clamped to the max depth #endif // WITH_APEX }
void FDestructibleMeshEditorViewportClient::RefreshFromStaticMesh() { #if WITH_APEX UDestructibleMesh* DestructibleMesh = DestructibleMeshEditorPtr.Pin()->GetDestructibleMesh(); DestructibleMesh->BuildFromStaticMesh(*DestructibleMesh->SourceStaticMesh); Fracture(); #endif // WITH_APEX }
UDestructibleMesh* ImportDestructibleMeshFromApexDestructibleAsset(UObject* InParent, NxDestructibleAsset& ApexDestructibleAsset, FName Name, EObjectFlags Flags, FSkeletalMeshImportData* OutData, EImportOptions::Type Options) { // The APEX Destructible Asset contains an APEX Render Mesh Asset, get a pointer to this const physx::NxRenderMeshAsset* ApexRenderMesh = ApexDestructibleAsset.getRenderMeshAsset(); if (ApexRenderMesh == NULL) { return NULL; } // Number of submeshes (aka "elements" in Unreal) const physx::PxU32 SubmeshCount = ApexRenderMesh->getSubmeshCount(); if (SubmeshCount == 0) { return NULL; } // Make sure rendering is done - so we are not changing data being used by collision drawing. FlushRenderingCommands(); UDestructibleMesh* DestructibleMesh = FindObject<UDestructibleMesh>(InParent, *Name.ToString()); if (DestructibleMesh == NULL) { // Create the new UDestructibleMesh object if the one with the same name does not exist DestructibleMesh = CastChecked<UDestructibleMesh>(StaticConstructObject(UDestructibleMesh::StaticClass(), InParent, Name, Flags)); } if (!(Options & EImportOptions::PreserveSettings)) { // Store the current file path and timestamp for re-import purposes // @todo AssetImportData make a data class for Apex destructible assets DestructibleMesh->AssetImportData = ConstructObject<UAssetImportData>(UAssetImportData::StaticClass(), DestructibleMesh); DestructibleMesh->AssetImportData->SourceFilePath = FReimportManager::SanitizeImportFilename(UFactory::CurrentFilename, DestructibleMesh); DestructibleMesh->AssetImportData->SourceFileTimestamp = IFileManager::Get().GetTimeStamp(*UFactory::CurrentFilename).ToString(); } DestructibleMesh->PreEditChange(NULL); // Build FractureSettings from ApexDestructibleAsset in case we want to re-fracture #if WITH_EDITORONLY_DATA DestructibleMesh->CreateFractureSettings(); DestructibleMesh->FractureSettings->BuildRootMeshFromApexDestructibleAsset(ApexDestructibleAsset, Options); // Fill materials DestructibleMesh->FractureSettings->Materials.Reset(DestructibleMesh->Materials.Num()); for (int32 MaterialIndex = 0; MaterialIndex < DestructibleMesh->Materials.Num(); ++MaterialIndex) { DestructibleMesh->FractureSettings->Materials.Insert(DestructibleMesh->Materials[MaterialIndex].MaterialInterface, MaterialIndex); } #endif // WITH_EDITORONLY_DATA if (!SetApexDestructibleAsset(*DestructibleMesh, ApexDestructibleAsset, OutData, Options)) { return NULL; } return DestructibleMesh; }
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 UDestructibleComponent::CreatePhysicsState() { // to avoid calling PrimitiveComponent, I'm just calling ActorComponent::CreatePhysicsState // @todo lh - fix me based on the discussion with Bryan G UActorComponent::CreatePhysicsState(); bPhysicsStateCreated = true; // What we want to do with BodySetup is simply use it to store a PhysicalMaterial, and possibly some other relevant fields. Set up pointers from the BodyInstance to the BodySetup and this component UBodySetup* BodySetup = GetBodySetup(); BodyInstance.OwnerComponent = this; BodyInstance.BodySetup = BodySetup; BodyInstance.InstanceBodyIndex = 0; #if WITH_APEX if( SkeletalMesh == NULL ) { return; } FPhysScene* PhysScene = World->GetPhysicsScene(); check(PhysScene); if( GApexModuleDestructible == NULL ) { UE_LOG(LogPhysics, Log, TEXT("UDestructibleComponent::CreatePhysicsState(): APEX must be enabled to init UDestructibleComponent physics.") ); return; } if( ApexDestructibleActor != NULL ) { UE_LOG(LogPhysics, Log, TEXT("UDestructibleComponent::CreatePhysicsState(): NxDestructibleActor already created.") ); return; } UDestructibleMesh* TheDestructibleMesh = GetDestructibleMesh(); if( TheDestructibleMesh == NULL || TheDestructibleMesh->ApexDestructibleAsset == NULL) { UE_LOG(LogPhysics, Log, TEXT("UDestructibleComponent::CreatePhysicsState(): No DestructibleMesh or missing ApexDestructibleAsset.") ); return; } int32 ChunkCount = TheDestructibleMesh->ApexDestructibleAsset->getChunkCount(); // Ensure the chunks start off invisible. RefreshBoneTransforms should make them visible. for (int32 ChunkIndex = 0; ChunkIndex < ChunkCount; ++ChunkIndex) { SetChunkVisible(ChunkIndex, false); } #if WITH_EDITOR if (GIsEditor && !World->IsGameWorld()) { // In the editor, only set the 0 chunk to be visible. if (TheDestructibleMesh->ApexDestructibleAsset->getChunkCount() > 0) { SetChunkVisible(0, true); } return; } #endif // WITH_EDITOR // Only create physics in the game if( !World->IsGameWorld() ) { return; } // Set template actor/body/shape properties // Find the PhysicalMaterial we need to apply to the physics bodies. UPhysicalMaterial* PhysMat = BodyInstance.GetSimplePhysicalMaterial(); // Get the default actor descriptor NxParameterized data from the asset NxParameterized::Interface* ActorParams = TheDestructibleMesh->GetDestructibleActorDesc(PhysMat); // Create PhysX transforms from ComponentToWorld const PxMat44 GlobalPose(PxMat33(U2PQuat(ComponentToWorld.GetRotation())), U2PVector(ComponentToWorld.GetTranslation())); const PxVec3 Scale = U2PVector(ComponentToWorld.GetScale3D()); // Set the transform in the actor descriptor verify( NxParameterized::setParamMat44(*ActorParams,"globalPose",GlobalPose) ); verify( NxParameterized::setParamVec3(*ActorParams,"scale",Scale) ); // Set the (initially) dynamic flag in the actor descriptor // See if we are 'static' verify( NxParameterized::setParamBool(*ActorParams,"dynamic", BodyInstance.bSimulatePhysics != false) ); // Set the sleep velocity frame decay constant (was sleepVelocitySmoothingFactor) - a new feature that should help sleeping in large piles verify( NxParameterized::setParamF32(*ActorParams,"sleepVelocityFrameDecayConstant", 20.0f) ); // Set up the shape desc template // Get collision channel and response PxFilterData PQueryFilterData, PSimFilterData; uint8 MoveChannel = GetCollisionObjectType(); FCollisionResponseContainer CollResponse; if(IsCollisionEnabled()) { // Only enable a collision response if collision is enabled CollResponse = GetCollisionResponseToChannels(); LargeChunkCollisionResponse.SetCollisionResponseContainer(CollResponse); SmallChunkCollisionResponse.SetCollisionResponseContainer(CollResponse); SmallChunkCollisionResponse.SetResponse(ECC_Pawn, ECR_Overlap); } else { // now since by default it will all block, if collision is disabled, we need to set to ignore MoveChannel = ECC_WorldStatic; CollResponse.SetAllChannels(ECR_Ignore); LargeChunkCollisionResponse.SetAllChannels(ECR_Ignore); SmallChunkCollisionResponse.SetAllChannels(ECR_Ignore); } const bool bEnableImpactDamage = IsImpactDamageEnabled(TheDestructibleMesh, 0); const bool bEnableContactModification = TheDestructibleMesh->DefaultDestructibleParameters.DamageParameters.bCustomImpactResistance && TheDestructibleMesh->DefaultDestructibleParameters.DamageParameters.ImpactResistance > 0.f; // Passing AssetInstanceID = 0 so we'll have self-collision AActor* Owner = GetOwner(); CreateShapeFilterData(MoveChannel, GetUniqueID(), CollResponse, 0, 0, PQueryFilterData, PSimFilterData, BodyInstance.bUseCCD, bEnableImpactDamage, false, bEnableContactModification); // Build filterData variations for complex and simple PSimFilterData.word3 |= EPDF_SimpleCollision | EPDF_ComplexCollision; PQueryFilterData.word3 |= EPDF_SimpleCollision | EPDF_ComplexCollision; // Set the filterData in the shape descriptor verify( NxParameterized::setParamU32(*ActorParams,"p3ShapeDescTemplate.simulationFilterData.word0", PSimFilterData.word0 ) ); verify( NxParameterized::setParamU32(*ActorParams,"p3ShapeDescTemplate.simulationFilterData.word1", PSimFilterData.word1 ) ); verify( NxParameterized::setParamU32(*ActorParams,"p3ShapeDescTemplate.simulationFilterData.word2", PSimFilterData.word2 ) ); verify( NxParameterized::setParamU32(*ActorParams,"p3ShapeDescTemplate.simulationFilterData.word3", PSimFilterData.word3 ) ); verify( NxParameterized::setParamU32(*ActorParams,"p3ShapeDescTemplate.queryFilterData.word0", PQueryFilterData.word0 ) ); verify( NxParameterized::setParamU32(*ActorParams,"p3ShapeDescTemplate.queryFilterData.word1", PQueryFilterData.word1 ) ); verify( NxParameterized::setParamU32(*ActorParams,"p3ShapeDescTemplate.queryFilterData.word2", PQueryFilterData.word2 ) ); verify( NxParameterized::setParamU32(*ActorParams,"p3ShapeDescTemplate.queryFilterData.word3", PQueryFilterData.word3 ) ); // Set the PhysX material in the shape descriptor PxMaterial* PMaterial = PhysMat->GetPhysXMaterial(); verify( NxParameterized::setParamU64(*ActorParams,"p3ShapeDescTemplate.material", (physx::PxU64)PMaterial) ); // Set the rest depth to match the skin width in the shape descriptor const physx::PxCookingParams& CookingParams = GApexSDK->getCookingInterface()->getParams(); verify( NxParameterized::setParamF32(*ActorParams,"p3ShapeDescTemplate.restOffset", -CookingParams.skinWidth) ); // Set the PhysX material in the actor descriptor verify( NxParameterized::setParamBool(*ActorParams,"p3ActorDescTemplate.flags.eDISABLE_GRAVITY",false) ); verify( NxParameterized::setParamBool(*ActorParams,"p3ActorDescTemplate.flags.eVISUALIZATION",true) ); // Set the PxActor's and PxShape's userData fields to this component's body instance verify( NxParameterized::setParamU64(*ActorParams,"p3ActorDescTemplate.userData", 0 ) ); // All shapes created by this DestructibleActor will have the userdata of the owning component. // We need this, as in some cases APEX is moving shapes accross actors ( ex. FormExtended structures ) verify( NxParameterized::setParamU64(*ActorParams,"p3ShapeDescTemplate.userData", (PxU64)&PhysxUserData ) ); // Set up the body desc template in the actor descriptor verify( NxParameterized::setParamF32(*ActorParams,"p3BodyDescTemplate.angularDamping", BodyInstance.AngularDamping ) ); verify( NxParameterized::setParamF32(*ActorParams,"p3BodyDescTemplate.linearDamping", BodyInstance.LinearDamping ) ); const PxTolerancesScale& PScale = GPhysXSDK->getTolerancesScale(); PxF32 SleepEnergyThreshold = 0.00005f*PScale.speed*PScale.speed; // 1/1000 Default, since the speed scale is quite high if (BodyInstance.SleepFamily == ESleepFamily::Sensitive) { SleepEnergyThreshold /= 20.0f; } verify( NxParameterized::setParamF32(*ActorParams,"p3BodyDescTemplate.sleepThreshold", SleepEnergyThreshold) ); // NxParameterized::setParamF32(*ActorParams,"bodyDescTemplate.sleepDamping", SleepDamping ); verify( NxParameterized::setParamF32(*ActorParams,"p3BodyDescTemplate.density", 0.001f*PhysMat->Density) ); // Convert from g/cm^3 to kg/cm^3 // Enable CCD if requested verify( NxParameterized::setParamBool(*ActorParams,"p3BodyDescTemplate.flags.eENABLE_CCD", BodyInstance.bUseCCD != 0) ); // Ask the actor to create chunk events, for more efficient visibility updates verify( NxParameterized::setParamBool(*ActorParams,"createChunkEvents", true) ); // Enable hard sleeping if requested verify( NxParameterized::setParamBool(*ActorParams,"useHardSleeping", bEnableHardSleeping) ); // Destructibles are always dynamic or kinematic, and therefore only go into one of the scenes const uint32 SceneType = BodyInstance.UseAsyncScene(PhysScene) ? PST_Async : PST_Sync; NxApexScene* ApexScene = PhysScene->GetApexScene(SceneType); PxScene* PScene = PhysScene->GetPhysXScene(SceneType); BodyInstance.SceneIndexSync = SceneType == PST_Sync ? PhysScene->PhysXSceneIndex[PST_Sync] : 0; BodyInstance.SceneIndexAsync = SceneType == PST_Async ? PhysScene->PhysXSceneIndex[PST_Async] : 0; check(ApexScene); ChunkInfos.Reset(ChunkCount); ChunkInfos.AddZeroed(ChunkCount); PhysxChunkUserData.Reset(ChunkCount); PhysxChunkUserData.AddZeroed(ChunkCount); // Create an APEX NxDestructibleActor from the Destructible asset and actor descriptor ApexDestructibleActor = static_cast<NxDestructibleActor*>(TheDestructibleMesh->ApexDestructibleAsset->createApexActor(*ActorParams, *ApexScene)); check(ApexDestructibleActor); // Make a backpointer to this component PhysxUserData = FPhysxUserData(this); ApexDestructibleActor->userData = &PhysxUserData; // Cache cooked collision data // BRGTODO : cook in asset ApexDestructibleActor->cacheModuleData(); // BRGTODO : Per-actor LOD setting // ApexDestructibleActor->forcePhysicalLod( DestructibleActor->LOD ); // Start asleep if requested PxRigidDynamic* PRootActor = ApexDestructibleActor->getChunkPhysXActor(0); // Put to sleep or wake up only if the component is physics-simulated if (PRootActor != NULL && BodyInstance.bSimulatePhysics) { SCOPED_SCENE_WRITE_LOCK(PScene); //Question, since apex is defer adding actors do we need to lock? Locking the async scene is expensive! PRootActor->setActorFlag(PxActorFlag::eDISABLE_GRAVITY, !BodyInstance.bEnableGravity); // Sleep/wake up as appropriate if (!BodyInstance.bStartAwake) { ApexDestructibleActor->setChunkPhysXActorAwakeState(0, false); } } UpdateBounds(); #endif // #if WITH_APEX }
void FDestructibleMeshEditorViewportClient::ImportFBXChunks() { // Get the FBX that we want to import TArray<FString> OpenFilenames; IDesktopPlatform* DesktopPlatform = FDesktopPlatformModule::Get(); bool bOpened = false; if (DesktopPlatform != NULL) { bOpened = DesktopPlatform->OpenFileDialog( NULL, NSLOCTEXT("UnrealEd", "ImportMatineeSequence", "Import UnrealMatinee Sequence").ToString(), *(FEditorDirectories::Get().GetLastDirectory(ELastDirectory::GENERIC_IMPORT)), TEXT(""), TEXT("FBX document|*.fbx"), EFileDialogFlags::None, OpenFilenames); } if (bOpened) { // Get the filename from dialog FString ImportFilename = OpenFilenames[0]; FString FileName = OpenFilenames[0]; FEditorDirectories::Get().SetLastDirectory(ELastDirectory::GENERIC_IMPORT, FPaths::GetPath(FileName)); // Save path as default for next time. const FString FileExtension = FPaths::GetExtension(FileName); const bool bIsFBX = FCString::Stricmp(*FileExtension, TEXT("FBX")) == 0; if (bIsFBX) { FlushRenderingCommands(); UnFbx::FFbxImporter* FFbxImporter = UnFbx::FFbxImporter::GetInstance(); if (FFbxImporter->ImportFromFile( *ImportFilename, FPaths::GetExtension( ImportFilename ) ) ) { TArray<FbxNode*> FbxMeshArray; FFbxImporter->FillFbxMeshArray(FFbxImporter->Scene->GetRootNode(), FbxMeshArray, FFbxImporter); UFbxStaticMeshImportData* ImportData = NewObject<UFbxStaticMeshImportData>(GetTransientPackage(), NAME_None, RF_NoFlags, NULL); TArray<UStaticMesh*> ChunkMeshes; for (int32 i=0; i < FbxMeshArray.Num(); ++i) { UStaticMesh* TempStaticMesh = NULL; TempStaticMesh = (UStaticMesh*)FFbxImporter->ImportStaticMesh(GetTransientPackage(), FbxMeshArray[i], NAME_None, RF_NoFlags, ImportData, 0); ChunkMeshes.Add(TempStaticMesh); } UDestructibleMesh* DestructibleMesh = DestructibleMeshEditorPtr.Pin()->GetDestructibleMesh(); if (DestructibleMesh) { DestructibleMesh->SetupChunksFromStaticMeshes(ChunkMeshes); } } FFbxImporter->ReleaseScene(); // Update the viewport DestructibleMeshEditorPtr.Pin()->RefreshTool(); DestructibleMeshEditorPtr.Pin()->SetCurrentPreviewDepth(0xFFFFFFFF); // This will get clamped to the max depth } else { // Invalid filename } } #if WITH_APEX #endif // WITH_APEX }