UE4c++ ConvertActorsToStaticMesh

UE4c++ ConvertActorsToStaticMesh

ConvertActorsToStaticMesh

    • UE4c++ ConvertActorsToStaticMesh
      • 创建Edior模块(最好是放Editor模块毕竟是编辑器代码)
      • 创建UBlueprintFunctionLibrary
        • UTestFunctionLibrary.h
        • UTestFunctionLibrary.cpp:
        • .Build.cs

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

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

演示效果:

请添加图片描述

创建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")));

	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());

};
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 "Editor.h"
#include "IContentBrowserSingleton.h"
#include "MeshUtilities.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::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;
	}
.Build.cs
        PrivateDependencyModuleNames.AddRange(
            new string[]
            {
                "CoreUObject",
                "Engine",
                "Slate",
                "SlateCore",
                "MeshUtilities",
                "RawMesh",
                "Slate",
                "SlateCore",
                "UnrealEd"
            }
        );

最终调用ConvertActorMeshesToStaticMesh方法即可

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/413648.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

uniapp android 原生插件开发-测试流程

前言 最近公司要求研究一下 uniapp 的 android 原生插件的开发&#xff0c;为以后的工作做准备。这篇文章记录一下自己的学习过程&#xff0c;也帮助一下有同样需求的同学们 : ) 一、下载安装Hbuilder X , Android studio&#xff08;相关的安装配置过程网上有很多&#xff0c;…

【Java EE初阶二十六】简单的表白墙(二)

2. 后端服务器部分 2.1 服务器分析 2.2 代码编写 2.2.2 前端发起一个ajax请求 2.2.3 服务器读取上述请求,并计算出响应 服务器需要使用 jackson 读取到前端这里的数据,并且进行解析&#xff1a; 代码运行图&#xff1a; 2.2.4 回到前端代码&#xff0c;处理服务器返回的响应…

【.NET Core】深入理解IO之File类

【.NET Core】深入理解IO之File类 文章目录 【.NET Core】深入理解IO之File类一、概述二、File类2.1 File.AppendAllLines方法2.2 File.AppendAllText方法2.3 File.Copy 方法2.4 File.Create 方法2.5 File.Decrypt(String) 方法2.6 File.Delete(String) 方法2.7 File.Move 方法…

Nginx+Tomcat实现动静分离

文章目录 一.动静分离的原理及架构1.1 动静分离是什么&#xff1f;1.2 动静分离的原理1.3 动静分离的架构组成 二.NginxTomcat实现动静分离2.1实验环境2.2所需软件环境2.3nginx服务的实现2.4配置动静分离 一.动静分离的原理及架构 1.1 动静分离是什么&#xff1f; 动静分离(S…

Android 15的新功能介绍

虽然谷歌已经发布了 Android 15 Preview 1&#xff0c;但这并不是完整的更新&#xff0c;因为该公司计划在后续的每月测试版中引入新功能。但这可能会让您思考&#xff0c;“Android 15 带来了哪些新功能&#xff1f;” 为了寻找答案&#xff0c;让我们深入了解 Android 15。 …

pr2024 Premiere Pro 2024 mac v24.2.1中文激活版

Premiere Pro 2024 for Mac是Adobe公司推出的一款强大的视频编辑软件&#xff0c;专为Mac操作系统优化。它提供了丰富的剪辑工具、特效和音频处理选项&#xff0c;帮助用户轻松创建专业级的影视作品。 软件下载&#xff1a;pr2024 Premiere Pro 2024 mac v24.2.1中文激活版 无论…

Linux yum安装pgsql出现Bad GPG signature错误

官方文档&#xff1a;https://www.postgresql.org/download/linux/redhat/ sudo yum install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm sudo yum install -y postgresql12-server sudo /usr/pgsql-12/bin/…

【架构笔记1】剃刀思维-如无必要,勿增实体

欢迎来到文思源想的架构空间&#xff0c;前段时间博主做了一个工作经历复盘&#xff0c;10年开发路&#xff0c;走了不少弯路&#xff0c;也算积累了不少软件开发、架构设计的经验和心得&#xff0c;确实有必要好好盘一盘&#xff0c;作为个人的总结&#xff0c;同时也留给有缘…

【QT+QGIS跨平台编译】之五十四:【QGIS_CORE跨平台编译】—【qgssqlstatementlexer.cpp生成】

文章目录 一、Flex二、生成来源三、构建过程一、Flex Flex (fast lexical analyser generator) 是 Lex 的另一个替代品。它经常和自由软件 Bison 语法分析器生成器 一起使用。Flex 最初由 Vern Paxson 于 1987 年用 C 语言写成。 “flex 是一个生成扫描器的工具,能够识别文本中…

leetcode:134.加油站

解题思路&#xff1a;需要注意开始时的编号&#xff0c;有的可以走一圈&#xff0c;有的走不了 模拟过程&#xff1a;for循环主要是用来模拟线性的过程&#xff0c;而在这里它是环状的&#xff1b; 可以用暴力解法&#xff0c;但是在这里我用贪心来解决。 常见疑惑&#xff1…

阿里云国际云解析DNS如何开启/关闭流量分析?

流量分析服务会涉及产生日志费用&#xff0c;所以开通内网DNS解析服务后&#xff0c;默认不会主动开启流量分析&#xff0c;需要您手动开启流量分析。对于未开启流量分析的用户&#xff0c;进入界面会提示您展示的都是模拟数据&#xff0c;您可以点击开启流量分析服务&#xff…

nvm下载node指定版本后npm不存在

一&#xff0c;项目背景 接手一个老的项目&#xff0c;需要使用旧的node版本&#xff0c;使用nvm下载12.11.0版本后发现npm命令不存在。 二&#xff0c;原因 查找资料发现是8.11以上版本的node版本对应的npm都没法自动安装&#xff0c;需要自己到npm官网( https://registry.…

《TCP/IP详解 卷一》第9章 广播和本地组播

目录 9.1 引言 9.2 广播 9.2.1 使用广播地址 9.2.2 发送广播数据报 9.3 组播 9.3.1 将组播IP地址转换为组播MAC地址 9.3.2 例子 9.3.3 发送组播数据报 9.3.4 接收组播数据报 9.3.5 主机地址过滤 9.4 IGMP协议和MLD协议 9.4.1 组成员的IGMP和MLD处理 9.4.2 组播路由…

Linux--查看网络性能指标

一、性能指标有哪些&#xff1f; 带宽&#xff0c;表示链路的最大传输速率&#xff0c;单位是 b/s &#xff08;比特 / 秒&#xff09;&#xff0c;带宽越大&#xff0c;其传输能力就越强。延时&#xff0c;表示请求数据包发送后&#xff0c;收到对端响应&#xff0c;所需要的…

【iOS ARKit】网络传输 ARWorldMap

ARKit 可以利用 ARWorldMap 在应用中断后进行状态恢复、继续AR 进程。一个用户也可以将ARWorldMap 发送给其他用户&#xff0c;当其他用户接收并加载 ARWorldMap 后&#xff0c;就可以在相同的物理环境看到同样的虚拟元素&#xff0c;达到共享 AR体验的目的。 在ARKit 中&#…

【c语言】内存函数

欢迎关注个人主页&#xff1a;逸狼 创造不易&#xff0c;可以点点赞吗~ 如有错误&#xff0c;欢迎指出~ 目录 memcpy函数的使用和模拟实现 memcpy函数的使用 memcpy函数的模拟实现 memmove的使用和模拟实现 memmove的使用 memmove的模拟实现 memset函数的使用 memcmp函数…

java中容器继承体系

首先上图 源码解析 打开Collection接口源码&#xff0c;能够看到Collection接口是继承了Iterable接口。 public interface Collection<E> extends Iterable<E> { /** * ...... */ } 以下是Iterable接口源码及注释 /** * Implementing this inte…

【UE 材质】水晶材质

效果 步骤 1. 先在Quixel Bridge上下载冰纹理 2. 新建一个材质&#xff0c;这里命名为“M_Ice”并打开&#xff0c;添加如下纹理采样节点 继续添加如下节点 此时效果如下&#xff1a; 可以看到此时的材质颜色比较浅&#xff0c;如果希望颜色深一点可以继续添加如下节点 此时效…

Java学习--学生管理系统(残破版)

代码 Main.java import java.util.ArrayList; import java.util.Scanner;public class Main {public static void main(String[] args) {ArrayList<Student> list new ArrayList<>();loop:while (true) {System.out.println("-----欢迎来到阿宝院校学生管理系…

OSI模型

OSI模型 TCP/IP参考模型 TCP/IP常见协议 应用层 FTP&#xff08;用于文件的下载和上传&#xff0c;采用C/S结构&#xff09; Telnet&#xff08;用于远程登陆服务&#xff09; DNS&#xff08;域名解析&#xff09; HTTP&#xff08;接收和发布Html页面&#xff09; 传输层…