赞
踩
目标:为了大量生成模型,我们把虚幻带有的方法迁移成函数,并去掉默认弹窗,以便代码调用
测试调用:
演示效果:
// Fill out your copyright notice in the Description page of Project Settings. #pragma once #include "CoreMinimal.h" #include "RawMesh.h" #include "Kismet/BlueprintFunctionLibrary.h" #include "TestFunctionLibrary.generated.h" struct FRawMeshTracker_Copy { FRawMeshTracker_Copy() : bValidColors(false) { FMemory::Memset(bValidTexCoords, 0); } bool bValidTexCoords[MAX_MESH_TEXTURE_COORDS]; bool bValidColors; }; UCLASS() class TESTEDITOR_API UTestFunctionLibrary : public UBlueprintFunctionLibrary { GENERATED_BODY() public: UFUNCTION(BlueprintCallable) static void ConvertActorMeshesToStaticMesh(const TArray<AActor*> InActors, const FString& PathString = FString(TEXT("/Game/Meshes/")), const FString& InMeshName = FString(TEXT("StaticMesh"))); UFUNCTION(BlueprintCallable) static void ConvertProceduralMeshToStaticMesh(UProceduralMeshComponent* ProcMeshComp, FString Path = FString(TEXT("/Game/Meshes/")), FString Name = FString(TEXT("ProcMesh")) ); static void GetSkinnedAndStaticMeshComponentsFromActors(const TArray<AActor*> InActors, TArray<UMeshComponent*>& OutMeshComponents); static bool IsValidSkinnedMeshComponent(USkinnedMeshComponent* InComponent); static bool IsValidStaticMeshComponent(UStaticMeshComponent* InComponent); template <typename ComponentType> static void ProcessMaterials(ComponentType* InComponent, const FString& InPackageName, TArray<UMaterialInterface*>& OutMaterials) { const int32 NumMaterials = InComponent->GetNumMaterials(); for (int32 MaterialIndex = 0; MaterialIndex < NumMaterials; MaterialIndex++) { UMaterialInterface* MaterialInterface = InComponent->GetMaterial(MaterialIndex); AddOrDuplicateMaterial(MaterialInterface, InPackageName, OutMaterials); } } static void AddOrDuplicateMaterial(UMaterialInterface* InMaterialInterface, const FString& InPackageName, TArray<UMaterialInterface*>& OutMaterials); static void SkinnedMeshToRawMeshes(USkinnedMeshComponent* InSkinnedMeshComponent, int32 InOverallMaxLODs, const FMatrix& InComponentToWorld, const FString& InPackageName, TArray<FRawMeshTracker_Copy>& OutRawMeshTrackers, TArray<FRawMesh>& OutRawMeshes, TArray<UMaterialInterface*>& OutMaterials); // Helper function for ConvertMeshesToStaticMesh static void StaticMeshToRawMeshes(UStaticMeshComponent* InStaticMeshComponent, int32 InOverallMaxLODs, const FMatrix& InComponentToWorld, const FString& InPackageName, TArray<FRawMeshTracker_Copy>& OutRawMeshTrackers, TArray<FRawMesh>& OutRawMeshes, TArray<UMaterialInterface*>& OutMaterials); static UStaticMesh* ConvertMeshesToStaticMesh(const TArray<UMeshComponent*>& InMeshComponents, const FTransform& InRootTransform, const FString& PathString = FString(TEXT("/Game/Meshes/")), const FString& InMeshName = FString(TEXT("StaticMesh")), const FString& InPackageName = FString()); };
// Fill out your copyright notice in the Description page of Project Settings. #include "TestFunctionLibrary.h" #include "Materials/MaterialInstanceDynamic.h" #include "AssetToolsModule.h" #include "ContentBrowserModule.h" #include "CustomMeshComponent.h" #include "Editor.h" #include "IContentBrowserSingleton.h" #include "MeshDescription.h" #include "MeshUtilities.h" #include "ProceduralMeshComponent.h" #include "ProceduralMeshConversion.h" #include "SkeletalRenderPublic.h" #include "AssetRegistry/AssetRegistryModule.h" #include "Components/CapsuleComponent.h" #include "Framework/Notifications/NotificationManager.h" #include "GameFramework/Character.h" #include "Rendering/SkeletalMeshRenderData.h" #include "Subsystems/AssetEditorSubsystem.h" #include "Widgets/Notifications/SNotificationList.h" #define LOCTEXT_NAMESPACE "UTestFunctionLibrary" void UTestFunctionLibrary::ConvertActorMeshesToStaticMesh(const TArray<AActor*> InActors, const FString& PathString, const FString& InMeshName) { IMeshUtilities& MeshUtilities = FModuleManager::Get().LoadModuleChecked<IMeshUtilities>("MeshUtilities"); TArray<UMeshComponent*> MeshComponents; GetSkinnedAndStaticMeshComponentsFromActors(InActors, MeshComponents); auto GetActorRootTransform = [](AActor* InActor) { FTransform RootTransform(FTransform::Identity); if (const ACharacter* Character = Cast<ACharacter>(InActor)) { RootTransform = Character->GetTransform(); RootTransform.SetLocation( RootTransform.GetLocation() - FVector( 0.0f, 0.0f, Character->GetCapsuleComponent()->GetScaledCapsuleHalfHeight())); } else { // otherwise just use the actor's origin RootTransform = InActor->GetTransform(); } return RootTransform; }; // now pick a root transform FTransform RootTransform(FTransform::Identity); if (InActors.Num() == 1) { RootTransform = GetActorRootTransform(InActors[0]); } else { // multiple actors use the average of their origins, with Z being the min of all origins. Rotation is identity for simplicity FVector Location(FVector::ZeroVector); float MinZ = FLT_MAX; for (AActor* Actor : InActors) { FTransform ActorTransform(GetActorRootTransform(Actor)); Location += ActorTransform.GetLocation(); MinZ = FMath::Min(ActorTransform.GetLocation().Z, MinZ); } Location /= (float)InActors.Num(); Location.Z = MinZ; RootTransform.SetLocation(Location); } UStaticMesh* StaticMesh = ConvertMeshesToStaticMesh(MeshComponents, RootTransform, PathString, InMeshName); // Also notify the content browser that the new assets exists if (StaticMesh != nullptr) { FContentBrowserModule& ContentBrowserModule = FModuleManager::Get().LoadModuleChecked< FContentBrowserModule>("ContentBrowser"); ContentBrowserModule.Get().SyncBrowserToAssets(TArray<UObject*>({StaticMesh}), true); } } void UTestFunctionLibrary::ConvertProceduralMeshToStaticMesh(UProceduralMeshComponent* ProcMeshComp, FString Path, FString Name) { if (ProcMeshComp != nullptr) { FString NewNameSuggestion = Name; FString PackageName = Path + NewNameSuggestion; FString Name; FAssetToolsModule& AssetToolsModule = FModuleManager::LoadModuleChecked<FAssetToolsModule>("AssetTools"); AssetToolsModule.Get().CreateUniqueAssetName(PackageName, TEXT(""), PackageName, Name); { FString UserPackageName = PackageName; FName MeshName(*FPackageName::GetLongPackageAssetName(UserPackageName)); // Check if the user inputed a valid asset name, if they did not, give it the generated default name if (MeshName == NAME_None) { // Use the defaults that were already generated. UserPackageName = PackageName; MeshName = *Name; } FMeshDescription MeshDescription = BuildMeshDescription(ProcMeshComp); // If we got some valid data. if (MeshDescription.Polygons().Num() > 0) { // Then find/create it. UPackage* Package = CreatePackage(*UserPackageName); check(Package); // Create StaticMesh object UStaticMesh* StaticMesh = NewObject<UStaticMesh>(Package, MeshName, RF_Public | RF_Standalone); StaticMesh->InitResources(); StaticMesh->SetLightingGuid(); // Add source to new StaticMesh FStaticMeshSourceModel& SrcModel = StaticMesh->AddSourceModel(); SrcModel.BuildSettings.bRecomputeNormals = false; SrcModel.BuildSettings.bRecomputeTangents = false; SrcModel.BuildSettings.bRemoveDegenerates = false; SrcModel.BuildSettings.bUseHighPrecisionTangentBasis = false; SrcModel.BuildSettings.bUseFullPrecisionUVs = false; SrcModel.BuildSettings.bGenerateLightmapUVs = true; SrcModel.BuildSettings.SrcLightmapIndex = 0; SrcModel.BuildSettings.DstLightmapIndex = 1; StaticMesh->CreateMeshDescription(0, MoveTemp(MeshDescription)); StaticMesh->CommitMeshDescription(0); SIMPLE COLLISION if (!ProcMeshComp->bUseComplexAsSimpleCollision) { StaticMesh->CreateBodySetup(); UBodySetup* NewBodySetup = StaticMesh->GetBodySetup(); NewBodySetup->BodySetupGuid = FGuid::NewGuid(); NewBodySetup->AggGeom.ConvexElems = ProcMeshComp->ProcMeshBodySetup->AggGeom.ConvexElems; NewBodySetup->bGenerateMirroredCollision = false; NewBodySetup->bDoubleSidedGeometry = true; NewBodySetup->CollisionTraceFlag = CTF_UseDefault; NewBodySetup->CreatePhysicsMeshes(); } MATERIALS TSet<UMaterialInterface*> UniqueMaterials; const int32 NumSections = ProcMeshComp->GetNumSections(); for (int32 SectionIdx = 0; SectionIdx < NumSections; SectionIdx++) { FProcMeshSection* ProcSection = ProcMeshComp->GetProcMeshSection(SectionIdx); UMaterialInterface* Material = ProcMeshComp->GetMaterial(SectionIdx); UniqueMaterials.Add(Material); } // Copy materials to new mesh for (auto* Material : UniqueMaterials) { StaticMesh->GetStaticMaterials().Add(FStaticMaterial(Material)); } //Set the Imported version before calling the build StaticMesh->ImportVersion = EImportStaticMeshVersion::LastVersion; // Build mesh from source StaticMesh->Build(false); StaticMesh->PostEditChange(); // Notify asset registry of new asset FAssetRegistryModule::AssetCreated(StaticMesh); } } } } void UTestFunctionLibrary::GetSkinnedAndStaticMeshComponentsFromActors(const TArray<AActor*> InActors, TArray<UMeshComponent*>& OutMeshComponents) { for (AActor* Actor : InActors) { // add all components from this actor TInlineComponentArray<UMeshComponent*> ActorComponents(Actor); for (UMeshComponent* ActorComponent : ActorComponents) { if (ActorComponent->IsA(USkinnedMeshComponent::StaticClass()) || ActorComponent->IsA( UStaticMeshComponent::StaticClass())) { OutMeshComponents.AddUnique(ActorComponent); } } // add all attached actors TArray<AActor*> AttachedActors; Actor->GetAttachedActors(AttachedActors); for (AActor* AttachedActor : AttachedActors) { TInlineComponentArray<UMeshComponent*> AttachedActorComponents(AttachedActor); for (UMeshComponent* AttachedActorComponent : AttachedActorComponents) { if (AttachedActorComponent->IsA(USkinnedMeshComponent::StaticClass()) || AttachedActorComponent-> IsA(UStaticMeshComponent::StaticClass())) { OutMeshComponents.AddUnique(AttachedActorComponent); } } } } } bool UTestFunctionLibrary::IsValidSkinnedMeshComponent(USkinnedMeshComponent* InComponent) { return InComponent && InComponent->MeshObject && InComponent->IsVisible(); } bool UTestFunctionLibrary::IsValidStaticMeshComponent(UStaticMeshComponent* InComponent) { return InComponent && InComponent->GetStaticMesh() && InComponent->GetStaticMesh()->GetRenderData() && InComponent->IsVisible(); } void UTestFunctionLibrary::AddOrDuplicateMaterial(UMaterialInterface* InMaterialInterface, const FString& InPackageName, TArray<UMaterialInterface*>& OutMaterials) { if (InMaterialInterface && !InMaterialInterface->GetOuter()->IsA<UPackage>()) { // Convert runtime material instances to new concrete material instances // Create new package FString OriginalMaterialName = InMaterialInterface->GetName(); FString MaterialPath = FPackageName::GetLongPackagePath(InPackageName) / OriginalMaterialName; FString MaterialName; FAssetToolsModule& AssetToolsModule = FModuleManager::LoadModuleChecked<FAssetToolsModule>("AssetTools"); AssetToolsModule.Get().CreateUniqueAssetName(MaterialPath, TEXT(""), MaterialPath, MaterialName); UPackage* MaterialPackage = CreatePackage(*MaterialPath); // Duplicate the object into the new package UMaterialInterface* NewMaterialInterface = DuplicateObject<UMaterialInterface>( InMaterialInterface, MaterialPackage, *MaterialName); NewMaterialInterface->SetFlags(RF_Public | RF_Standalone); if (UMaterialInstanceDynamic* MaterialInstanceDynamic = Cast< UMaterialInstanceDynamic>(NewMaterialInterface)) { UMaterialInstanceDynamic* OldMaterialInstanceDynamic = CastChecked<UMaterialInstanceDynamic>( InMaterialInterface); MaterialInstanceDynamic->K2_CopyMaterialInstanceParameters(OldMaterialInstanceDynamic); } NewMaterialInterface->MarkPackageDirty(); FAssetRegistryModule::AssetCreated(NewMaterialInterface); InMaterialInterface = NewMaterialInterface; } OutMaterials.Add(InMaterialInterface); } void UTestFunctionLibrary::SkinnedMeshToRawMeshes(USkinnedMeshComponent* InSkinnedMeshComponent, int32 InOverallMaxLODs, const FMatrix& InComponentToWorld, const FString& InPackageName, TArray<FRawMeshTracker_Copy>& OutRawMeshTrackers, TArray<FRawMesh>& OutRawMeshes, TArray<UMaterialInterface*>& OutMaterials) { const int32 BaseMaterialIndex = OutMaterials.Num(); // Export all LODs to raw meshes const int32 NumLODs = InSkinnedMeshComponent->GetNumLODs(); for (int32 OverallLODIndex = 0; OverallLODIndex < InOverallMaxLODs; OverallLODIndex++) { int32 LODIndexRead = FMath::Min(OverallLODIndex, NumLODs - 1); FRawMesh& RawMesh = OutRawMeshes[OverallLODIndex]; FRawMeshTracker_Copy& RawMeshTracker = OutRawMeshTrackers[OverallLODIndex]; const int32 BaseVertexIndex = RawMesh.VertexPositions.Num(); FSkeletalMeshLODInfo& SrcLODInfo = *(InSkinnedMeshComponent->SkeletalMesh->GetLODInfo(LODIndexRead)); // Get the CPU skinned verts for this LOD TArray<FFinalSkinVertex> FinalVertices; InSkinnedMeshComponent->GetCPUSkinnedVertices(FinalVertices, LODIndexRead); FSkeletalMeshRenderData& SkeletalMeshRenderData = InSkinnedMeshComponent->MeshObject-> GetSkeletalMeshRenderData(); FSkeletalMeshLODRenderData& LODData = SkeletalMeshRenderData.LODRenderData[LODIndexRead]; // Copy skinned vertex positions for (int32 VertIndex = 0; VertIndex < FinalVertices.Num(); ++VertIndex) { RawMesh.VertexPositions.Add(InComponentToWorld.TransformPosition(FinalVertices[VertIndex].Position)); } const uint32 NumTexCoords = FMath::Min(LODData.StaticVertexBuffers.StaticMeshVertexBuffer.GetNumTexCoords(), (uint32)MAX_MESH_TEXTURE_COORDS); const int32 NumSections = LODData.RenderSections.Num(); FRawStaticIndexBuffer16or32Interface& IndexBuffer = *LODData.MultiSizeIndexContainer.GetIndexBuffer(); for (int32 SectionIndex = 0; SectionIndex < NumSections; SectionIndex++) { const FSkelMeshRenderSection& SkelMeshSection = LODData.RenderSections[SectionIndex]; if (InSkinnedMeshComponent->IsMaterialSectionShown(SkelMeshSection.MaterialIndex, LODIndexRead)) { // Build 'wedge' info const int32 NumWedges = SkelMeshSection.NumTriangles * 3; for (int32 WedgeIndex = 0; WedgeIndex < NumWedges; WedgeIndex++) { const int32 VertexIndexForWedge = IndexBuffer.Get(SkelMeshSection.BaseIndex + WedgeIndex); RawMesh.WedgeIndices.Add(BaseVertexIndex + VertexIndexForWedge); const FFinalSkinVertex& SkinnedVertex = FinalVertices[VertexIndexForWedge]; const FVector TangentX = InComponentToWorld.TransformVector(SkinnedVertex.TangentX.ToFVector()); const FVector TangentZ = InComponentToWorld.TransformVector(SkinnedVertex.TangentZ.ToFVector()); const FVector4 UnpackedTangentZ = SkinnedVertex.TangentZ.ToFVector4(); const FVector TangentY = (TangentZ ^ TangentX).GetSafeNormal() * UnpackedTangentZ.W; RawMesh.WedgeTangentX.Add(TangentX); RawMesh.WedgeTangentY.Add(TangentY); RawMesh.WedgeTangentZ.Add(TangentZ); for (uint32 TexCoordIndex = 0; TexCoordIndex < MAX_MESH_TEXTURE_COORDS; TexCoordIndex++) { if (TexCoordIndex >= NumTexCoords) { RawMesh.WedgeTexCoords[TexCoordIndex].AddDefaulted(); } else { RawMesh.WedgeTexCoords[TexCoordIndex].Add( LODData.StaticVertexBuffers.StaticMeshVertexBuffer.GetVertexUV( VertexIndexForWedge, TexCoordIndex)); RawMeshTracker.bValidTexCoords[TexCoordIndex] = true; } } if (LODData.StaticVertexBuffers.ColorVertexBuffer.IsInitialized()) { RawMesh.WedgeColors.Add( LODData.StaticVertexBuffers.ColorVertexBuffer.VertexColor(VertexIndexForWedge)); RawMeshTracker.bValidColors = true; } else { RawMesh.WedgeColors.Add(FColor::White); } } int32 MaterialIndex = SkelMeshSection.MaterialIndex; // use the remapping of material indices if there is a valid value if (SrcLODInfo.LODMaterialMap.IsValidIndex(SectionIndex) && SrcLODInfo.LODMaterialMap[SectionIndex] != INDEX_NONE) { MaterialIndex = FMath::Clamp<int32>(SrcLODInfo.LODMaterialMap[SectionIndex], 0, InSkinnedMeshComponent->SkeletalMesh->GetMaterials().Num()); } // copy face info for (uint32 TriIndex = 0; TriIndex < SkelMeshSection.NumTriangles; TriIndex++) { RawMesh.FaceMaterialIndices.Add(BaseMaterialIndex + MaterialIndex); RawMesh.FaceSmoothingMasks.Add(0); // Assume this is ignored as bRecomputeNormals is false } } } } ProcessMaterials<USkinnedMeshComponent>(InSkinnedMeshComponent, InPackageName, OutMaterials); } void UTestFunctionLibrary::StaticMeshToRawMeshes(UStaticMeshComponent* InStaticMeshComponent, int32 InOverallMaxLODs, const FMatrix& InComponentToWorld, const FString& InPackageName, TArray<FRawMeshTracker_Copy>& OutRawMeshTrackers, TArray<FRawMesh>& OutRawMeshes, TArray<UMaterialInterface*>& OutMaterials) { const int32 BaseMaterialIndex = OutMaterials.Num(); const int32 NumLODs = InStaticMeshComponent->GetStaticMesh()->GetRenderData()->LODResources.Num(); for (int32 OverallLODIndex = 0; OverallLODIndex < InOverallMaxLODs; OverallLODIndex++) { int32 LODIndexRead = FMath::Min(OverallLODIndex, NumLODs - 1); FRawMesh& RawMesh = OutRawMeshes[OverallLODIndex]; FRawMeshTracker_Copy& RawMeshTracker = OutRawMeshTrackers[OverallLODIndex]; const FStaticMeshLODResources& LODResource = InStaticMeshComponent->GetStaticMesh()->GetRenderData()-> LODResources[LODIndexRead]; const int32 BaseVertexIndex = RawMesh.VertexPositions.Num(); for (int32 VertIndex = 0; VertIndex < LODResource.GetNumVertices(); ++VertIndex) { RawMesh.VertexPositions.Add(InComponentToWorld.TransformPosition( LODResource.VertexBuffers.PositionVertexBuffer.VertexPosition((uint32)VertIndex))); } const FIndexArrayView IndexArrayView = LODResource.IndexBuffer.GetArrayView(); const FStaticMeshVertexBuffer& StaticMeshVertexBuffer = LODResource.VertexBuffers.StaticMeshVertexBuffer; const int32 NumTexCoords = FMath::Min(StaticMeshVertexBuffer.GetNumTexCoords(), (uint32)MAX_MESH_TEXTURE_COORDS); const int32 NumSections = LODResource.Sections.Num(); for (int32 SectionIndex = 0; SectionIndex < NumSections; SectionIndex++) { const FStaticMeshSection& StaticMeshSection = LODResource.Sections[SectionIndex]; const int32 NumIndices = StaticMeshSection.NumTriangles * 3; for (int32 IndexIndex = 0; IndexIndex < NumIndices; IndexIndex++) { int32 Index = IndexArrayView[StaticMeshSection.FirstIndex + IndexIndex]; RawMesh.WedgeIndices.Add(BaseVertexIndex + Index); RawMesh.WedgeTangentX.Add( InComponentToWorld.TransformVector(StaticMeshVertexBuffer.VertexTangentX(Index))); RawMesh.WedgeTangentY.Add( InComponentToWorld.TransformVector(StaticMeshVertexBuffer.VertexTangentY(Index))); RawMesh.WedgeTangentZ.Add( InComponentToWorld.TransformVector(StaticMeshVertexBuffer.VertexTangentZ(Index))); for (int32 TexCoordIndex = 0; TexCoordIndex < MAX_MESH_TEXTURE_COORDS; TexCoordIndex++) { if (TexCoordIndex >= NumTexCoords) { RawMesh.WedgeTexCoords[TexCoordIndex].AddDefaulted(); } else { RawMesh.WedgeTexCoords[TexCoordIndex].Add( StaticMeshVertexBuffer.GetVertexUV(Index, TexCoordIndex)); RawMeshTracker.bValidTexCoords[TexCoordIndex] = true; } } if (LODResource.VertexBuffers.ColorVertexBuffer.IsInitialized()) { RawMesh.WedgeColors.Add(LODResource.VertexBuffers.ColorVertexBuffer.VertexColor(Index)); RawMeshTracker.bValidColors = true; } else { RawMesh.WedgeColors.Add(FColor::White); } } // copy face info for (uint32 TriIndex = 0; TriIndex < StaticMeshSection.NumTriangles; TriIndex++) { RawMesh.FaceMaterialIndices.Add(BaseMaterialIndex + StaticMeshSection.MaterialIndex); RawMesh.FaceSmoothingMasks.Add(0); // Assume this is ignored as bRecomputeNormals is false } } } ProcessMaterials<UStaticMeshComponent>(InStaticMeshComponent, InPackageName, OutMaterials); } UStaticMesh* UTestFunctionLibrary::ConvertMeshesToStaticMesh(const TArray<UMeshComponent*>& InMeshComponents, const FTransform& InRootTransform, const FString& PathString, const FString& InMeshName, const FString& InPackageName) { UStaticMesh* StaticMesh = nullptr; IMeshUtilities& MeshUtilities = FModuleManager::Get().LoadModuleChecked<IMeshUtilities>("MeshUtilities"); // Build a package name to use FString MeshName; FString PackageName; if (InPackageName.IsEmpty()) { FString NewNameSuggestion = InMeshName; FString PackageNameSuggestion = PathString + NewNameSuggestion; FString Name; FAssetToolsModule& AssetToolsModule = FModuleManager::LoadModuleChecked<FAssetToolsModule>("AssetTools"); AssetToolsModule.Get().CreateUniqueAssetName(PackageNameSuggestion, TEXT(""), PackageNameSuggestion, Name); // TSharedPtr<SDlgPickAssetPath> PickAssetPathWidget = // SNew(SDlgPickAssetPath) // .Title(LOCTEXT("ConvertToStaticMeshPickName", "Choose New StaticMesh Location")) // .DefaultAssetPath(FText::FromString(PackageNameSuggestion)); //if (PickAssetPathWidget->ShowModal() == EAppReturnType::Ok) { // Get the full name of where we want to create the mesh asset. PackageName = PackageNameSuggestion; //PickAssetPathWidget->GetFullAssetPath().ToString(); MeshName = FPackageName::GetLongPackageAssetName(PackageName); // Check if the user inputed a valid asset name, if they did not, give it the generated default name if (MeshName.IsEmpty()) { // Use the defaults that were already generated. PackageName = PackageNameSuggestion; MeshName = *Name; } } } else { PackageName = InPackageName; MeshName = *FPackageName::GetLongPackageAssetName(PackageName); } if (!PackageName.IsEmpty() && !MeshName.IsEmpty()) { TArray<FRawMesh> RawMeshes; TArray<UMaterialInterface*> Materials; TArray<FRawMeshTracker_Copy> RawMeshTrackers; FMatrix WorldToRoot = InRootTransform.ToMatrixWithScale().Inverse(); // first do a pass to determine the max LOD level we will be combining meshes into int32 OverallMaxLODs = 0; for (UMeshComponent* MeshComponent : InMeshComponents) { USkinnedMeshComponent* SkinnedMeshComponent = Cast<USkinnedMeshComponent>(MeshComponent); UStaticMeshComponent* StaticMeshComponent = Cast<UStaticMeshComponent>(MeshComponent); if (IsValidSkinnedMeshComponent(SkinnedMeshComponent)) { OverallMaxLODs = FMath::Max( SkinnedMeshComponent->MeshObject->GetSkeletalMeshRenderData().LODRenderData.Num(), OverallMaxLODs); } else if (IsValidStaticMeshComponent(StaticMeshComponent)) { OverallMaxLODs = FMath::Max( StaticMeshComponent->GetStaticMesh()->GetRenderData()->LODResources.Num(), OverallMaxLODs); } } // Resize raw meshes to accommodate the number of LODs we will need RawMeshes.SetNum(OverallMaxLODs); RawMeshTrackers.SetNum(OverallMaxLODs); // Export all visible components for (UMeshComponent* MeshComponent : InMeshComponents) { FMatrix ComponentToWorld = MeshComponent->GetComponentTransform().ToMatrixWithScale() * WorldToRoot; USkinnedMeshComponent* SkinnedMeshComponent = Cast<USkinnedMeshComponent>(MeshComponent); UStaticMeshComponent* StaticMeshComponent = Cast<UStaticMeshComponent>(MeshComponent); if (IsValidSkinnedMeshComponent(SkinnedMeshComponent)) { SkinnedMeshToRawMeshes(SkinnedMeshComponent, OverallMaxLODs, ComponentToWorld, PackageName, RawMeshTrackers, RawMeshes, Materials); } else if (IsValidStaticMeshComponent(StaticMeshComponent)) { StaticMeshToRawMeshes(StaticMeshComponent, OverallMaxLODs, ComponentToWorld, PackageName, RawMeshTrackers, RawMeshes, Materials); } } uint32 MaxInUseTextureCoordinate = 0; // scrub invalid vert color & tex coord data check(RawMeshes.Num() == RawMeshTrackers.Num()); for (int32 RawMeshIndex = 0; RawMeshIndex < RawMeshes.Num(); RawMeshIndex++) { if (!RawMeshTrackers[RawMeshIndex].bValidColors) { RawMeshes[RawMeshIndex].WedgeColors.Empty(); } for (uint32 TexCoordIndex = 0; TexCoordIndex < MAX_MESH_TEXTURE_COORDS; TexCoordIndex++) { if (!RawMeshTrackers[RawMeshIndex].bValidTexCoords[TexCoordIndex]) { RawMeshes[RawMeshIndex].WedgeTexCoords[TexCoordIndex].Empty(); } else { // Store first texture coordinate index not in use MaxInUseTextureCoordinate = FMath::Max(MaxInUseTextureCoordinate, TexCoordIndex); } } } // Check if we got some valid data. bool bValidData = false; for (FRawMesh& RawMesh : RawMeshes) { if (RawMesh.IsValidOrFixable()) { bValidData = true; break; } } if (bValidData) { // Then find/create it. UPackage* Package = CreatePackage(*PackageName); check(Package); // Create StaticMesh object StaticMesh = NewObject<UStaticMesh>(Package, *MeshName, RF_Public | RF_Standalone); StaticMesh->InitResources(); StaticMesh->SetLightingGuid(); // Determine which texture coordinate map should be used for storing/generating the lightmap UVs const uint32 LightMapIndex = FMath::Min(MaxInUseTextureCoordinate + 1, (uint32)MAX_MESH_TEXTURE_COORDS - 1); // Add source to new StaticMesh for (FRawMesh& RawMesh : RawMeshes) { if (RawMesh.IsValidOrFixable()) { FStaticMeshSourceModel& SrcModel = StaticMesh->AddSourceModel(); SrcModel.BuildSettings.bRecomputeNormals = false; SrcModel.BuildSettings.bRecomputeTangents = false; SrcModel.BuildSettings.bRemoveDegenerates = true; SrcModel.BuildSettings.bUseHighPrecisionTangentBasis = false; SrcModel.BuildSettings.bUseFullPrecisionUVs = false; SrcModel.BuildSettings.bGenerateLightmapUVs = true; SrcModel.BuildSettings.SrcLightmapIndex = 0; SrcModel.BuildSettings.DstLightmapIndex = LightMapIndex; SrcModel.SaveRawMesh(RawMesh); } } // Copy materials to new mesh for (UMaterialInterface* Material : Materials) { StaticMesh->GetStaticMaterials().Add(FStaticMaterial(Material)); } //Set the Imported version before calling the build StaticMesh->ImportVersion = EImportStaticMeshVersion::LastVersion; // Set light map coordinate index to match DstLightmapIndex StaticMesh->SetLightMapCoordinateIndex(LightMapIndex); // setup section info map for (int32 RawMeshLODIndex = 0; RawMeshLODIndex < RawMeshes.Num(); RawMeshLODIndex++) { const FRawMesh& RawMesh = RawMeshes[RawMeshLODIndex]; TArray<int32> UniqueMaterialIndices; for (int32 MaterialIndex : RawMesh.FaceMaterialIndices) { UniqueMaterialIndices.AddUnique(MaterialIndex); } int32 SectionIndex = 0; for (int32 UniqueMaterialIndex : UniqueMaterialIndices) { StaticMesh->GetSectionInfoMap().Set(RawMeshLODIndex, SectionIndex, FMeshSectionInfo(UniqueMaterialIndex)); SectionIndex++; } } StaticMesh->GetOriginalSectionInfoMap().CopyFrom(StaticMesh->GetSectionInfoMap()); // Build mesh from source StaticMesh->Build(false); StaticMesh->PostEditChange(); StaticMesh->MarkPackageDirty(); // Notify asset registry of new asset FAssetRegistryModule::AssetCreated(StaticMesh); // Display notification so users can quickly access the mesh if (GIsEditor) { FNotificationInfo Info(FText::Format( LOCTEXT("SkeletalMeshConverted", "Successfully Converted Mesh"), FText::FromString(StaticMesh->GetName()))); Info.ExpireDuration = 8.0f; Info.bUseLargeFont = false; Info.Hyperlink = FSimpleDelegate::CreateLambda([=]() { GEditor->GetEditorSubsystem<UAssetEditorSubsystem>()->OpenEditorForAssets(TArray<UObject*>({ StaticMesh })); }); Info.HyperlinkText = FText::Format( LOCTEXT("OpenNewAnimationHyperlink", "Open {0}"), FText::FromString(StaticMesh->GetName())); TSharedPtr<SNotificationItem> Notification = FSlateNotificationManager::Get().AddNotification(Info); if (Notification.IsValid()) { Notification->SetCompletionState(SNotificationItem::CS_Success); } } } } return StaticMesh; }
{
"CoreUObject",
"Engine",
"Slate",
"SlateCore",
"MeshUtilities",
"RawMesh",
"Slate",
"SlateCore",
"UnrealEd",
"CustomMeshComponent",
"ProceduralMeshComponent",
"MeshDescription"
}
最后根据情况调用下面俩个反射给蓝图的方法即可
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。