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 ConvertMeshesToStaticMeshstatic 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 originRootTransform = InActor->GetTransform();}return RootTransform;};// now pick a root transformFTransform 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 simplicityFVector 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 existsif (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 actorTInlineComponentArray<UMeshComponent*> ActorComponents(Actor);for (UMeshComponent* ActorComponent : ActorComponents){if (ActorComponent->IsA(USkinnedMeshComponent::StaticClass()) || ActorComponent->IsA(UStaticMeshComponent::StaticClass())){OutMeshComponents.AddUnique(ActorComponent);}}// add all attached actorsTArray<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 packageFString 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 packageUMaterialInterface* 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 meshesconst 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 LODTArray<FFinalSkinVertex> FinalVertices;InSkinnedMeshComponent->GetCPUSkinnedVertices(FinalVertices, LODIndexRead);FSkeletalMeshRenderData& SkeletalMeshRenderData = InSkinnedMeshComponent->MeshObject->GetSkeletalMeshRenderData();FSkeletalMeshLODRenderData& LODData = SkeletalMeshRenderData.LODRenderData[LODIndexRead];// Copy skinned vertex positionsfor (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' infoconst 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 valueif (SrcLODInfo.LODMaterialMap.IsValidIndex(SectionIndex) && SrcLODInfo.LODMaterialMap[SectionIndex]!= INDEX_NONE){MaterialIndex = FMath::Clamp<int32>(SrcLODInfo.LODMaterialMap[SectionIndex], 0,InSkinnedMeshComponent->SkeletalMesh->GetMaterials().Num());}// copy face infofor (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 infofor (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 useFString 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 nameif (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 intoint32 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 needRawMeshes.SetNum(OverallMaxLODs);RawMeshTrackers.SetNum(OverallMaxLODs);// Export all visible componentsfor (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 datacheck(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 useMaxInUseTextureCoordinate = 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 objectStaticMesh = 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 UVsconst uint32 LightMapIndex = FMath::Min(MaxInUseTextureCoordinate + 1,(uint32)MAX_MESH_TEXTURE_COORDS - 1);// Add source to new StaticMeshfor (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 buildStaticMesh->ImportVersion = EImportStaticMeshVersion::LastVersion;// Set light map coordinate index to match DstLightmapIndexStaticMesh->SetLightMapCoordinateIndex(LightMapIndex);// setup section info mapfor (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 sourceStaticMesh->Build(false);StaticMesh->PostEditChange();StaticMesh->MarkPackageDirty();// Notify asset registry of new assetFAssetRegistryModule::AssetCreated(StaticMesh);// Display notification so users can quickly access the meshif (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方法即可

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

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

文章目录 一、Bison二、生成来源三、构建过程一、Bison GNU Bison 是一个通用的解析器生成器,它可以将注释的无上下文语法转换为使用 LALR (1) 解析表的确定性 LR 或广义 LR (GLR) 解析器。Bison 还可以生成 IELR (1) 或规范 LR (1) 解析表。一旦您熟练使用 Bison,您可以使用…

【严格递增】2972统计移除递增子数组的数目 II

作者推荐 动态规划的时间复杂度优化 本文涉及知识点 严格递增 子数组 LeetCode2972. 统计移除递增子数组的数目 II 给你一个下标从 0 开始的 正 整数数组 nums 。 如果 nums 的一个子数组满足&#xff1a;移除这个子数组后剩余元素 严格递增 &#xff0c;那么我们称这个子…

Vue+SpringBoot打造天然气工程业务管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块三、使用角色3.1 施工人员3.2 管理员 四、数据库设计4.1 用户表4.2 分公司表4.3 角色表4.4 数据字典表4.5 工程项目表4.6 使用材料表4.7 使用材料领用表4.8 整体E-R图 五、系统展示六、核心代码6.1 查询工程项目6.2 工程物资…

Qt RGB三色灯上位机

今天突发奇想做一个Qt RGB三色灯上位机&#xff0c;刚好手上有一个RGB三色灯和arduion开发板。 我是想实现一个颜色选择器界面然后鼠标点击颜色区域就可以发出rgb的值&#xff0c;然后把这个值通过串口线发送给arduion,arduion再解析出数据发送给RGB三色灯。 实现界面如下&…

express+mysql+vue,从零搭建一个商城管理系统3--user路由模块

提示&#xff1a;学习express&#xff0c;搭建管理系统 文章目录 前言一、新建routes文件夹二、新建routes/index.js和routes/user.js三、修改index.js四、修改routes/index.js五、修改routes/user.js六、启动项目预览总结 前言 需求&#xff1a;主要学习express&#xff0c;所…

maven的私服

什么是maven的私服就是把自己写的工具类共享给别人这样大家都能用到你写的工具类不用重复写提示效率 maven的上传与下载示意图 1.什么是发行版本&#xff1f;发行版本指定的是功能稳定可以共大家使用的版本 2.什么是快照版本&#xff1f;快照版本指定的是指正在开发的版本 3…

ChatGPT 正测试Android屏幕小组件;联想ThinkBook 推出透明笔记本电脑

▶ ChatGPT 测试屏幕小组件 近日 ChatGPT 正在测试 Android 平台上的屏幕小组件&#xff0c;类似于手机中的悬浮窗&#xff0c;按住 Android 手机主屏幕上的空白位置就可以调出 ChatGPT 的部件菜单。 菜单中提供了许多选项&#xff0c;包括文本、语音和视频查询的快捷方式&…

第102讲:MySQL多实例与Mycat分布式读写分离的架构实践

文章目录 1.Mycat读写分离分布式架构规划2.在两台服务器中搭建八个MySQL实例2.1.安装MySQL软件2.2.创建每个MySQL实例的数据目录并初始化2.3.准备每个实例的配置文件2.4.准备每个实例的启动脚本2.6启动每台机器的MySQL多实例2.7.为每个MySQL实例设置密码2.8.查看每个MySQL实例的…

蓝桥杯第十四届电子类单片机组决赛程序设计

目录 前言 单片机资源数据包_2023&#xff08;点击下载&#xff09; 一、第十四届比赛题目 1.比赛题目 2.题目解读 1&#xff09;任务要求 2&#xff09;注意事项 二、显示功能实现 1.关于高位为0时数码管熄灭功能的实现 2.关于显示小数位的处理 3.关于“校准值”的…

Nginx反向代理ip透传与负载均衡

前言 上篇介绍了nginx服务器单向代理和动静分离等相关内容&#xff0c;可参考Nginx重写功能和反向代理-CSDN博客&#xff0c;这里就ip透传和负载均衡对nginx反向代理做进一步了解。 目录 一、客户端ip透传 1. 概述 2. 一级代理 2.1 图示 2.2 操作过程 3. 二级代理 3.…

使用ffmpeg压缩视频

一、到ffmpeg官网下载文件包&#xff1a; Download FFmpeg 下载后找到 bin 下的3个exe文件&#xff0c;复制到自己本机的某个目录下, 如&#xff1a; 二、使用命令行压缩&#xff1a; ffmpeg -i input.mp4 -c:v libx265 -crf 28 -y output.mp4 这条命令使用 FFmpeg 工具对输…

机器学习——线性代数中矩阵和向量的基本介绍

矩阵和向量的基本概念 矩阵的基本概念&#xff08;这里不多说&#xff0c;应该都知道&#xff09; 而向量就是一个特殊的矩阵&#xff0c;即向量只有一列&#xff0c;是个n*1的矩阵 注&#xff1a;一般矩阵用大写字母表示&#xff0c;向量用小写字母表示 矩阵的加减运算 两个…

前端架构: 脚手架命令行交互核心实现之inquirer和readline的应用教程

命令行交互核心实现 核心目标&#xff1a;实现命令行行交互&#xff0c;如List命令行的交互呢比命令行的渲难度要更大&#xff0c;因为它涉及的技术点会会更多它涉及以下技术点 键盘输入的一个监听 (这里通过 readline来实现)计算命令行窗口的尺寸清屏光标的移动输出流的静默 …

关于年化收益率的思考

近期&#xff0c;对于投资的年化收益率有一些思考&#xff0c;想着将这些思考整理一下&#xff0c;顺便也就记录在这里。 1. 计算方式 年化收益率常见的计算有三种&#xff1a;算数平均&#xff0c;几何平均&#xff0c;IRR。 1.1 算术平均 算数平均用于度量产品的回报率&a…

MetaGPT 1 安装与配置踩坑实录

安装 与 配置直接参考这里就行&#xff1a;Hugging Muti Agent&#xff08;二月学习&#xff09; - 飞书云文档 (feishu.cn) 这里按照教程安装的是metagpt 0.6.6 &#xff0c;经过跟0.7.0对比&#xff0c;个人认为0.7对其他llm接入可能更好&#xff0c;文档也更清晰。 0.6.6的…

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

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

安全运营中心(SOC)综合指南

什么是安全运营中心&#xff08;SOC&#xff09; 安全运营中心&#xff0c;也称为信息安全运营中心 &#xff08;ISOC&#xff09;&#xff0c;是结构良好的网络安全战略的核心。安全运营中心是一个集中式枢纽&#xff0c;无论是在组织内部还是外包&#xff0c;都致力于对整个…

云计算时代的运维: 职业发展方向与岗位选择

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua&#xff0c;在这里我会分享我的知识和经验。&#x…

Nginx网络服务五-----rewrite和反向代理

1.rewrite 1.1rewrite指令 通过正则表达式的匹配来改变URI&#xff0c;可以同时存在一个或多个指令&#xff0c;按照顺序依次对URI进行匹配&#xff0c;rewrite主要是针对用户请求的URL或者是URI做具体处理 官方文档&#xff1a; https://nginx.org/en/docs/http/ngx_http_r…

自动驾驶消息传输机制-LCM

需要用到LCM消息通讯&#xff0c;遂研究下。 这里写目录标题 1 LCM简介2. LCM源码分析3 LCM C教程与实例3.1 安装配置及介绍3.2 创建类型定义3.3 初始化LCM3.4 发布publish一个消息3.5 订阅和接收一个消息3.6 LCM进程间通讯3.7 注意事项&#xff1f;3.7.1 当数据结构定义的是数…