当前位置: 首页 > news >正文

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方法即可

相关文章:

UE4c++ ConvertActorsToStaticMesh

UE4c ConvertActorsToStaticMesh ConvertActorsToStaticMesh UE4c ConvertActorsToStaticMesh创建Edior模块&#xff08;最好是放Editor模块毕竟是编辑器代码&#xff09;创建UBlueprintFunctionLibraryUTestFunctionLibrary.hUTestFunctionLibrary.cpp:.Build.cs 目标:为了大量…...

Qt中tableView控件的使用

tableView使用注意事项 tableView在使用时&#xff0c;从工具栏拖动到底层页面后&#xff0c;右键进行选择如下图所示&#xff1a; 此处需要注意的是&#xff0c;需要去修改属性&#xff0c;从UI上修改属性如下所示&#xff1a; 也可以通过代码修改属性&#xff1a; //将其设…...

【医学影像】LIDC-IDRI数据集的无痛制作

LIDC-IDRI数据集制作 0.下载0.0 链接汇总0.1 步骤 1.合成CT图reference 0.下载 0.0 链接汇总 LIDC-IDRI官方网址&#xff1a;https://www.cancerimagingarchive.net/nbia-search/?CollectionCriteriaLIDC-IDRINBIA Data Retriever 下载链接&#xff1a;https://wiki.canceri…...

MacOS开发环境搭建详解

搭建MacOS开发环境需要准备相应的软硬件&#xff0c;并遵循一系列步骤。以下是详细的步骤&#xff1a; 软硬件准备&#xff1a; MacOS电脑&#xff1a;确保你的电脑运行的是MacOS操作系统。Xcode软件&#xff1a;打开AppStore&#xff0c;搜索并安装Xcode。安装过程可能较长&…...

全量知识系统问题及SmartChat给出的答复 之2

Q6. 根据DDD的思想( 也就是借助 DDD的某个或某些实现)&#xff0c;是否能按照这个想法给出程序设计和代码结构&#xff1f; 当使用领域驱动设计&#xff08;DDD&#xff09;的思想来设计程序和代码结构时&#xff0c;可以根据领域模型、领域服务、值对象、实体等概念来进行设计…...

嵌入式驱动学习第一周——vim的使用

前言 本篇博客学习使用vim&#xff0c;vim作为linux下的编辑器&#xff0c;学linux肯定是绕不开vim的&#xff0c;因为不确定对方环境中是否安装了编译器&#xff0c;但一定会有vim。 对于基本的使用只需要会打开文件&#xff0c;保存文件&#xff0c;编辑文件即可。 嵌入式驱动…...

loop_list单向循环列表

#include "loop_list.h" //创建单向循环链表 loop_p create_head() { loop_p L(loop_p)malloc(sizeof(loop_list)); if(LNULL) { printf("create fail\n"); return NULL; } L->len 0; L->nextL; retur…...

Python爬虫实战第二例【二】

零.前言&#xff1a; 本文章借鉴&#xff1a;Python爬虫实战&#xff08;五&#xff09;&#xff1a;根据关键字爬取某度图片批量下载到本地&#xff08;附上完整源码&#xff09;_python爬虫下载图片-CSDN博客 大佬的文章里面有API的获取&#xff0c;在这里我就不赘述了。 一…...

Eclipse是如何创建web project项目的?

前面几篇描述先后描述了tomcat的目录结构和访问机制&#xff0c;以及Eclipse的项目类型和怎么调用jar包&#xff0c;还有java的main函数等&#xff0c;这些是一些基础问题&#xff0c;基础高清出来才更容易搞清楚后面要说的东西&#xff0c;也就是需求带动学习&#xff0c;后面…...

Excel的中高级用法

单元格格式&#xff0c;根据数值的正负分配不同的颜色和↑ ↓ 根据数值正负分配颜色 2-7 [蓝色]#,##0;[红色]-#,##0 分配颜色的基础上&#xff0c;根据正负加↑和↓ 2↑-7↓ 其实就是在上面颜色的代码基础上加个 向上的符号↑&#xff0c;或向下的符号↓ [蓝色]#,##0↑;[红色…...

【ArcGIS】基本概念-空间参考与变换

ArcGIS基本概念-空间参考与变换 1 空间参考与地图投影1.1 空间参考1.2 大地坐标系&#xff08;地理坐标系&#xff09;1.3 投影坐标系总结 2 投影变换预处理2.1 定义投影2.2 转换自定义地理&#xff08;坐标&#xff09;变换2.3 转换坐标记法 3 投影变换3.1 矢量数据的投影变换…...

Qt QWidget 简约美观的加载动画 第五季 - 小方块风格

给大家分享两个小方块风格的加载动画 &#x1f60a; 第五季来啦 &#x1f60a; 效果如下: 一个三个文件,可以直接编译运行 //main.cpp #include "LoadingAnimWidget.h" #include <QApplication> #include <QGridLayout> int main(int argc, char *arg…...

针对KZG承诺和高效laconic OT的extractable witness encryption

1. 引言 2024年以太坊基金会等成员论文 Extractable Witness Encryption for KZG Commitments and Efficient Laconic OT&#xff0c;开源代码实现见&#xff1a; https://github.com/rot256/research-we-kzg&#xff08;Rust&#xff09; 在该论文中&#xff0c;提供了一种…...

Spring Boot中实现列表数据导出为Excel文件

点击下载《Spring Boot中实现列表数据导出为Excel文件》 1. 前言 本文将详细介绍在Spring Boot框架中如何将列表数据导出为Excel文件。我们将通过Apache POI库来实现这一功能&#xff0c;并解释其背后的原理、提供完整的流程和步骤&#xff0c;以及带有详细注释的代码示例。最…...

华为ipv6 over ipv4 GRE隧道配置

思路&#xff1a; PC1访问PC2时&#xff0c;会先构造源ipv6为2001:1::2&#xff0c;目的IPV6为2001:2::2的ipv6报文&#xff0c;然后查看PC1的路由表&#xff0c;发送到R1&#xff0c;r1接收后&#xff0c;以目的IPV6地址2001:2::2查询IPV6路由表&#xff0c;出接口为tun0/0/0…...

项目解决方案:海外门店视频汇聚方案(全球性的连锁店、国外连锁店视频接入和汇聚方案)

目 录 一、概述 二、建设目标及需求 2.1 建设目标 2.2 需求描述 2.3 需求分析 三、建设方案设计 3.1 系统方案拓扑图 3.2 方案描述 3.3 服务器配置推荐 四、产品功能 4.1 资源管理平台 &#xff08;1&#xff09;用户权限管理 &#xff08;2&#xff09…...

Java中的数据类型详解

引言 在Java编程中&#xff0c;数据类型是非常重要的概念&#xff0c;它定义了数据的类型和范围&#xff0c;帮助程序员有效地操作数据。Java的数据类型可以分为两大类&#xff1a;基本数据类型和引用数据类型。本文将详细介绍Java中的各种数据类型&#xff0c;并附上相应的代…...

ABBYY FineReader16文档转换、PDF管理与文档比较功能介绍

ABBYY FineReader 16作为一款OCR和PDF一体化程序&#xff0c;其强大的功能使得文档处理变得简单高效。在众多功能中&#xff0c;文档转换、PDF管理和文档比较这三大功能尤为突出&#xff0c;成为了众多企业和个人用户的首选工具。 ABBYY Finereader 16-安装包下载如下&#xff…...

导览系统厂家|景区电子导览|手绘地图|AR导览|语音导览系统

随着元宇宙、VR、AR等新技术的快速发展&#xff0c;旅游服务也更加多元化、智能化。景区导览系统作为旅游服务的重要组成部分&#xff0c;其形式更加多元化智能化。智能导览系统作为一种新的服务方式&#xff0c;能够为游客提供更加便捷的旅游服务和游览体验&#xff0c;也逐渐…...

oracle 如何使用脚本实现访问控制(无需额外插件)

随着这些年勒索病毒的爆发,各个企业对数据安全的要求越来越高,常见的办法有开启数据库审计,加数据库防火墙,网络限制等等;但是细粒度审计会消耗大量系统资源,第三方数据库防火墙一般是需要收费的;这里介绍我个人常用的四个db级别trigger,用于记录部分关键信息可以应对部…...

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…...

地震勘探——干扰波识别、井中地震时距曲线特点

目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波&#xff1a;可以用来解决所提出的地质任务的波&#xff1b;干扰波&#xff1a;所有妨碍辨认、追踪有效波的其他波。 地震勘探中&#xff0c;有效波和干扰波是相对的。例如&#xff0c;在反射波…...

DockerHub与私有镜像仓库在容器化中的应用与管理

哈喽&#xff0c;大家好&#xff0c;我是左手python&#xff01; Docker Hub的应用与管理 Docker Hub的基本概念与使用方法 Docker Hub是Docker官方提供的一个公共镜像仓库&#xff0c;用户可以在其中找到各种操作系统、软件和应用的镜像。开发者可以通过Docker Hub轻松获取所…...

线程同步:确保多线程程序的安全与高效!

全文目录&#xff1a; 开篇语前序前言第一部分&#xff1a;线程同步的概念与问题1.1 线程同步的概念1.2 线程同步的问题1.3 线程同步的解决方案 第二部分&#xff1a;synchronized关键字的使用2.1 使用 synchronized修饰方法2.2 使用 synchronized修饰代码块 第三部分&#xff…...

大数据零基础学习day1之环境准备和大数据初步理解

学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 &#xff08;1&#xff09;设置网关 打开VMware虚拟机&#xff0c;点击编辑…...

Qwen3-Embedding-0.6B深度解析:多语言语义检索的轻量级利器

第一章 引言&#xff1a;语义表示的新时代挑战与Qwen3的破局之路 1.1 文本嵌入的核心价值与技术演进 在人工智能领域&#xff0c;文本嵌入技术如同连接自然语言与机器理解的“神经突触”——它将人类语言转化为计算机可计算的语义向量&#xff0c;支撑着搜索引擎、推荐系统、…...

python如何将word的doc另存为docx

将 DOCX 文件另存为 DOCX 格式&#xff08;Python 实现&#xff09; 在 Python 中&#xff0c;你可以使用 python-docx 库来操作 Word 文档。不过需要注意的是&#xff0c;.doc 是旧的 Word 格式&#xff0c;而 .docx 是新的基于 XML 的格式。python-docx 只能处理 .docx 格式…...

零基础设计模式——行为型模式 - 责任链模式

第四部分&#xff1a;行为型模式 - 责任链模式 (Chain of Responsibility Pattern) 欢迎来到行为型模式的学习&#xff01;行为型模式关注对象之间的职责分配、算法封装和对象间的交互。我们将学习的第一个行为型模式是责任链模式。 核心思想&#xff1a;使多个对象都有机会处…...

成都鼎讯硬核科技!雷达目标与干扰模拟器,以卓越性能制胜电磁频谱战

在现代战争中&#xff0c;电磁频谱已成为继陆、海、空、天之后的 “第五维战场”&#xff0c;雷达作为电磁频谱领域的关键装备&#xff0c;其干扰与抗干扰能力的较量&#xff0c;直接影响着战争的胜负走向。由成都鼎讯科技匠心打造的雷达目标与干扰模拟器&#xff0c;凭借数字射…...

CMake控制VS2022项目文件分组

我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...