• UE4c++ ConvertActorsToStaticMesh & ConvertProceduralMeshToStaticMesh


    目标:为了大量生成模型,我们把虚幻带有的方法迁移成函数,并去掉默认弹窗,以便代码调用
    在这里插入图片描述
    测试调用:
    在这里插入图片描述

    在这里插入图片描述
    在这里插入图片描述

    演示效果:

    请添加图片描述

    创建Edior模块(最好是放Editor模块毕竟是编辑器代码)

    创建蓝图函数UBlueprintFunctionLibrary

    UTestFunctionLibrary.h

    // 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());
    };
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83

    UTestFunctionLibrary.cpp:

    // 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 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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302
    • 303
    • 304
    • 305
    • 306
    • 307
    • 308
    • 309
    • 310
    • 311
    • 312
    • 313
    • 314
    • 315
    • 316
    • 317
    • 318
    • 319
    • 320
    • 321
    • 322
    • 323
    • 324
    • 325
    • 326
    • 327
    • 328
    • 329
    • 330
    • 331
    • 332
    • 333
    • 334
    • 335
    • 336
    • 337
    • 338
    • 339
    • 340
    • 341
    • 342
    • 343
    • 344
    • 345
    • 346
    • 347
    • 348
    • 349
    • 350
    • 351
    • 352
    • 353
    • 354
    • 355
    • 356
    • 357
    • 358
    • 359
    • 360
    • 361
    • 362
    • 363
    • 364
    • 365
    • 366
    • 367
    • 368
    • 369
    • 370
    • 371
    • 372
    • 373
    • 374
    • 375
    • 376
    • 377
    • 378
    • 379
    • 380
    • 381
    • 382
    • 383
    • 384
    • 385
    • 386
    • 387
    • 388
    • 389
    • 390
    • 391
    • 392
    • 393
    • 394
    • 395
    • 396
    • 397
    • 398
    • 399
    • 400
    • 401
    • 402
    • 403
    • 404
    • 405
    • 406
    • 407
    • 408
    • 409
    • 410
    • 411
    • 412
    • 413
    • 414
    • 415
    • 416
    • 417
    • 418
    • 419
    • 420
    • 421
    • 422
    • 423
    • 424
    • 425
    • 426
    • 427
    • 428
    • 429
    • 430
    • 431
    • 432
    • 433
    • 434
    • 435
    • 436
    • 437
    • 438
    • 439
    • 440
    • 441
    • 442
    • 443
    • 444
    • 445
    • 446
    • 447
    • 448
    • 449
    • 450
    • 451
    • 452
    • 453
    • 454
    • 455
    • 456
    • 457
    • 458
    • 459
    • 460
    • 461
    • 462
    • 463
    • 464
    • 465
    • 466
    • 467
    • 468
    • 469
    • 470
    • 471
    • 472
    • 473
    • 474
    • 475
    • 476
    • 477
    • 478
    • 479
    • 480
    • 481
    • 482
    • 483
    • 484
    • 485
    • 486
    • 487
    • 488
    • 489
    • 490
    • 491
    • 492
    • 493
    • 494
    • 495
    • 496
    • 497
    • 498
    • 499
    • 500
    • 501
    • 502
    • 503
    • 504
    • 505
    • 506
    • 507
    • 508
    • 509
    • 510
    • 511
    • 512
    • 513
    • 514
    • 515
    • 516
    • 517
    • 518
    • 519
    • 520
    • 521
    • 522
    • 523
    • 524
    • 525
    • 526
    • 527
    • 528
    • 529
    • 530
    • 531
    • 532
    • 533
    • 534
    • 535
    • 536
    • 537
    • 538
    • 539
    • 540
    • 541
    • 542
    • 543
    • 544
    • 545
    • 546
    • 547
    • 548
    • 549
    • 550
    • 551
    • 552
    • 553
    • 554
    • 555
    • 556
    • 557
    • 558
    • 559
    • 560
    • 561
    • 562
    • 563
    • 564
    • 565
    • 566
    • 567
    • 568
    • 569
    • 570
    • 571
    • 572
    • 573
    • 574
    • 575
    • 576
    • 577
    • 578
    • 579
    • 580
    • 581
    • 582
    • 583
    • 584
    • 585
    • 586
    • 587
    • 588
    • 589
    • 590
    • 591
    • 592
    • 593
    • 594
    • 595
    • 596
    • 597
    • 598
    • 599
    • 600
    • 601
    • 602
    • 603
    • 604
    • 605
    • 606
    • 607
    • 608
    • 609
    • 610
    • 611
    • 612
    • 613
    • 614
    • 615
    • 616
    • 617
    • 618
    • 619
    • 620
    • 621
    • 622
    • 623
    • 624
    • 625
    • 626
    • 627
    • 628
    • 629
    • 630
    • 631
    • 632
    • 633
    • 634
    • 635
    • 636
    • 637
    • 638
    • 639
    • 640
    • 641
    • 642
    • 643
    • 644
    • 645
    • 646
    • 647
    • 648
    • 649
    • 650
    • 651
    • 652
    • 653
    • 654
    • 655
    • 656
    • 657
    • 658
    • 659
    • 660
    • 661
    • 662
    • 663
    • 664
    • 665
    • 666
    • 667
    • 668
    • 669
    • 670
    • 671
    • 672
    • 673
    • 674
    • 675
    • 676
    • 677
    • 678
    • 679
    • 680
    • 681
    • 682
    • 683
    • 684
    • 685
    • 686
    • 687
    • 688
    • 689
    • 690
    • 691
    • 692
    • 693
    • 694
    • 695
    • 696
    • 697
    • 698
    • 699
    • 700
    • 701
    • 702
    • 703
    • 704

    .Build.cs

                {
                    "CoreUObject",
                    "Engine",
                    "Slate",
                    "SlateCore",
                    "MeshUtilities",
                    "RawMesh",
                    "Slate",
                    "SlateCore",
                    "UnrealEd",
                    "CustomMeshComponent", 
                    "ProceduralMeshComponent", 
                    "MeshDescription"
                }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    最后根据情况调用下面俩个反射给蓝图的方法即可
    最终调用ConvertActorMeshesToStaticMesh方法即可

  • 相关阅读:
    C++设计模式-更新中
    MyBatis Plus整合Redis实现分布式二级缓存
    判断两个IP地址(ipv4)是否在同一个网段
    基于java中的springboot框架实现经方药食两用服务平台项目演示【内附项目源码+论文说明】
    MySQL text 能存多少个字符
    【NVMe2.0b 14-4】Directive Send/Receive command
    【docker快速部署微服务若依管理系统(RuoYi-Cloud)】
    数码相机raw照片编辑Capture One Pro中文
    java计算机毕业设计开放式实验室预约系统源码+mysql数据库+系统+lw文档+部署
    使用EasyPoi导出word并转换为pdf
  • 原文地址:https://blog.csdn.net/weixin_56946623/article/details/136327878