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