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://xiahunao.cn/news/2813892.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

React回顾

一、基础 1、使用babel解析 2、不直接使用jsx&#xff0c;jsx写起来很繁琐 3、jsx语法规则 4、函数式组件的使用 5、函数式组件渲染 6、类组件渲染 7、类组件中事件调用this指向问题 8、类组件不能直接改变状态 9、props接收数据类型限制 类型限制放到类组件内部&#xff0c;用…

045-WEB攻防-PHP应用SQL二次注入堆叠执行DNS带外功能点黑白盒条件

045-WEB攻防-PHP应用&SQL二次注入&堆叠执行&DNS带外&功能点&黑白盒条件 #知识点&#xff1a; 1、PHP-MYSQL-SQL注入-二次注入&利用条件 2、PHP-MYSQL-SQL注入-堆叠注入&利用条件 3、PHP-MYSQL-SQL注入-带外注入&利用条件 演示案例&#xff1a…

区块链智能合约开发

一.区块链的回顾 1.区块链 区块链实质上是一个去中心化、分布式的可进行交易的数据库或账本 特征: 去中心化&#xff1a;简单来说&#xff0c;在网络上一个或多个服务器瘫痪的情况下&#xff0c;应用或服务仍然能够持续地运行&#xff0c;这就是去中心化。服务和应用部署在…

一款开源.NET WPF界面库介绍

一款开源.NET WPF界面库介绍 这是一个WPF版的Layui前端UI样式库&#xff0c;该控件库参考了Web版本的LayUI风格&#xff0c;利用该控件库可以完成现代化UI客户端程序&#xff0c;让你的客户端看起来更加简洁丰富又不失美感 如何使用 步骤一 : 添加LayUI.Wpf Nuget包; Inst…

物业智能水电抄表管理系统

物业智能水电抄表管理系统是物业管理行业的关键技术之一&#xff0c;其结合了智能化、远程监控和数据分析等功能&#xff0c;为物业管理公司和业主提供了高效、精准的水电抄表管理解决方案。该系统具有多项优势&#xff0c;能够提升物业管理效率&#xff0c;降低成本&#xff0…

苍穹外卖-day12 - 工作台 - Apache POI - 导出运营数据Excel报表

课程内容 工作台 Apache POI 导出运营数据Excel报表 功能实现&#xff1a;工作台、数据导出 工作台效果图&#xff1a; 数据导出效果图&#xff1a; 在数据统计页面点击数据导出&#xff1a;生成Excel报表 1. 工作台 1.1 需求分析和设计 1.1.1 产品原型 工作台是系统运营…

当Web3叙事寒冬到来,游戏是否是冬日里的“一把火”?

出品&#xff5c;欧科云链研究院 作者&#xff5c;Jason Jiang 以太坊创始人Vitalik在2019年曾说&#xff1a;金融与游戏会是区块链最先落地的场景。 在DeFi金融创新驱动上个周期后&#xff0c;沉寂近两年的Web3游戏板块&#xff0c;如今似乎也在复苏。无论是频繁获得融资&a…

【推荐算法系列五】DeepFM 模型

文章目录 参考资料Sparse FeaturesDense EmbeddingsFM LayerHidden LayerOutput Units 优缺点DeepFM 的优点DeepFM 自身的缺点。 参考资料 DeepFM 中关于 整个发展过程&#xff0c; FM, PNN, wide&deep 的描述很给力。 所以FM在其中的含义就是low-order, deep 就是所谓的 …

IT廉连看——Uniapp——页面样式与布局

IT廉连看——Uniapp——页面样式与布局 目标&#xff1a; 了解样式与布局的规范 熟记px和rpx的区别 全局样式与index样式的区别 一、查看uniapp框架简介——尺寸单位 px尺寸单位的使用是贯穿始终的。 [IT廉连看] 二、尺寸单位——实操效果 1、打开Hbuilder X并进入in…

type may not be empty [type-empty]

原因是使用了规范commit信息的工具&#xff0c;你的提交信息不符合规范&#xff0c;所以被拒绝了 commit规范工具 commitlinthusky 解决方式一&#xff1a; 修改提交信息&#xff0c; 使其符合规范 git commit -m "feat: 新功能"使用Git Gui的使用以下格式写提交…

每日一题:最小生成树

板子&#xff1a; 最小生成树【模板】最小生成树 - 洛谷 代码实现&#xff1a; 稠密图 #include<bits/stdc.h> using namespace std; const int N510,INF0x3f3f3f3f; int n,m; int g[N][N],dis[N]; bool st[N]; int prim(){memset(dis,0x3f,sizeof dis);int res0;for…

SpringBoot中时间对象区分及相关处理

目录 1 前言 2 常见时间对象的区分 2.2 LocalTime 2.3 LocalDateTime 3 控制类接收参数的细节 4 时间对象间的转化 1 前言 本文主要是目的是让大家能够区分Java中常见时间对象&#xff0c;并熟悉使用细节及它们间的转化。 2 常见时间对象的区分 ​​​​​2.1 LocalDa…

Aethir推出其首次去中心化AI节点售卖

Aethir&#xff0c;去中心化GPU云基础设施领导者&#xff0c;宣布其备受期待的节点销售。Aethir是一家企业级的以AI和游戏为重点的GPU即服务提供商。Aethir的去中心化云计算基础设施使GPU提供商能够与需要NVIDIA的H100芯片提供强大AI/ML任务支持的企业客户相连接。 此外&#x…

YOLOv6-Openvino和ONNXRuntime推理【CPU】

1 环境&#xff1a; CPU&#xff1a;i5-12500 Python&#xff1a;3.8.18 2 安装Openvino和ONNXRuntime 2.1 Openvino简介 Openvino是由Intel开发的专门用于优化和部署人工智能推理的半开源的工具包&#xff0c;主要用于对深度推理做优化。 Openvino内部集成了Opencv、Tens…

SQLPro Studio:数据库管理的革命性工具 mac版

SQLPro Studio是一款强大的数据库管理和开发工具&#xff0c;它旨在提供高效、便捷和安全的数据库操作体验。无论是数据库管理员、开发人员还是数据分析师&#xff0c;SQLPro Studio都能满足他们在数据库管理、查询、设计和维护方面的需求。 SQLPro Studio mac版软件获取 首先…

历史新知网:寄快递寄个电脑显示器要多少钱?

以下文字信息由&#xff08;新史知识网&#xff09;编辑整理发布。 让我们赶紧来看看吧&#xff01; 问题1&#xff1a;快递寄电脑显示器要多少钱&#xff1f; 此物有多重&#xff1f; 顺丰寄就可以了&#xff0c;但是必须是原包装的&#xff0c;不然不好寄。 问题2&#xff1…

爆火的1分钟声音克隆GPT-SoVITS项目 linux系统 ubuntu22.04安装2天踩坑教程

原项目地址&#xff1a;https://github.com/RVC-Boss/GPT-SoVITS 1分钟素材&#xff0c;最后出来的效果确实不错。 1. cuda环境安装 cuda环境准备 根据项目要求在cuda11.8和12.3都测试了通过。我这里是用cuda11.8 cuda11.8安装教程&#xff1a; ubuntu 22.04 cuda多版本和…

vscode——本地配置(C和C++)(1)

本地配置C和C&#xff08;1&#xff09; 什么是vscodevscode和visual studio的区别vscode的本地配置汉化 vscode配置C和C环境创建全局变量安装插件编写C或C程序生成task.json文件生成.exe文件 今天我们来看看一个开发工具——vscode。 什么是vscode 在正式了解vscode之前&…

2024年腾讯云4核8G12M配置的轻量服务器同时支持多大访问量?

腾讯云4核8G服务器支持多少人在线访问&#xff1f;支持25人同时访问。实际上程序效率不同支持人数在线人数不同&#xff0c;公网带宽也是影响4核8G服务器并发数的一大因素&#xff0c;假设公网带宽太小&#xff0c;流量直接卡在入口&#xff0c;4核8G配置的CPU内存也会造成计算…

CPU处理器NUMA架构简介

在实际工作中&#xff0c;经常遇到服务器是否开启NUMA、NUMA绑定几颗Core、跨NUMA节点访问的性能下降等等话题。那么NUMA作为非一致性内存访问的多处理器架构&#xff0c;在架构上有什么特性、与SMP架构有哪些不同&#xff0c;调优策略等&#xff0c;本文将作简要介绍。 1、CPU…