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")));
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());
};
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<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
{
"CoreUObject",
"Engine",
"Slate",
"SlateCore",
"MeshUtilities",
"RawMesh",
"Slate",
"SlateCore",
"UnrealEd",
"CustomMeshComponent",
"ProceduralMeshComponent",
"MeshDescription"
}
最后根据情况调用下面俩个反射给蓝图的方法即可