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模块(最好是放Editor模块毕竟是编辑器代码)创建UBlueprintFunctionLibraryUTestFunctionLibrary.hUTestFunctionLibrary.cpp:.Build.cs 目标:为了大量…...
Qt中tableView控件的使用
tableView使用注意事项 tableView在使用时,从工具栏拖动到底层页面后,右键进行选择如下图所示: 此处需要注意的是,需要去修改属性,从UI上修改属性如下所示: 也可以通过代码修改属性: //将其设…...
【医学影像】LIDC-IDRI数据集的无痛制作
LIDC-IDRI数据集制作 0.下载0.0 链接汇总0.1 步骤 1.合成CT图reference 0.下载 0.0 链接汇总 LIDC-IDRI官方网址:https://www.cancerimagingarchive.net/nbia-search/?CollectionCriteriaLIDC-IDRINBIA Data Retriever 下载链接:https://wiki.canceri…...
MacOS开发环境搭建详解
搭建MacOS开发环境需要准备相应的软硬件,并遵循一系列步骤。以下是详细的步骤: 软硬件准备: MacOS电脑:确保你的电脑运行的是MacOS操作系统。Xcode软件:打开AppStore,搜索并安装Xcode。安装过程可能较长&…...
全量知识系统问题及SmartChat给出的答复 之2
Q6. 根据DDD的思想( 也就是借助 DDD的某个或某些实现),是否能按照这个想法给出程序设计和代码结构? 当使用领域驱动设计(DDD)的思想来设计程序和代码结构时,可以根据领域模型、领域服务、值对象、实体等概念来进行设计…...
嵌入式驱动学习第一周——vim的使用
前言 本篇博客学习使用vim,vim作为linux下的编辑器,学linux肯定是绕不开vim的,因为不确定对方环境中是否安装了编译器,但一定会有vim。 对于基本的使用只需要会打开文件,保存文件,编辑文件即可。 嵌入式驱动…...
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爬虫实战第二例【二】
零.前言: 本文章借鉴:Python爬虫实战(五):根据关键字爬取某度图片批量下载到本地(附上完整源码)_python爬虫下载图片-CSDN博客 大佬的文章里面有API的获取,在这里我就不赘述了。 一…...
Eclipse是如何创建web project项目的?
前面几篇描述先后描述了tomcat的目录结构和访问机制,以及Eclipse的项目类型和怎么调用jar包,还有java的main函数等,这些是一些基础问题,基础高清出来才更容易搞清楚后面要说的东西,也就是需求带动学习,后面…...
Excel的中高级用法
单元格格式,根据数值的正负分配不同的颜色和↑ ↓ 根据数值正负分配颜色 2-7 [蓝色]#,##0;[红色]-#,##0 分配颜色的基础上,根据正负加↑和↓ 2↑-7↓ 其实就是在上面颜色的代码基础上加个 向上的符号↑,或向下的符号↓ [蓝色]#,##0↑;[红色…...
【ArcGIS】基本概念-空间参考与变换
ArcGIS基本概念-空间参考与变换 1 空间参考与地图投影1.1 空间参考1.2 大地坐标系(地理坐标系)1.3 投影坐标系总结 2 投影变换预处理2.1 定义投影2.2 转换自定义地理(坐标)变换2.3 转换坐标记法 3 投影变换3.1 矢量数据的投影变换…...
Qt QWidget 简约美观的加载动画 第五季 - 小方块风格
给大家分享两个小方块风格的加载动画 😊 第五季来啦 😊 效果如下: 一个三个文件,可以直接编译运行 //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,开源代码实现见: https://github.com/rot256/research-we-kzg(Rust) 在该论文中,提供了一种…...
Spring Boot中实现列表数据导出为Excel文件
点击下载《Spring Boot中实现列表数据导出为Excel文件》 1. 前言 本文将详细介绍在Spring Boot框架中如何将列表数据导出为Excel文件。我们将通过Apache POI库来实现这一功能,并解释其背后的原理、提供完整的流程和步骤,以及带有详细注释的代码示例。最…...
华为ipv6 over ipv4 GRE隧道配置
思路: PC1访问PC2时,会先构造源ipv6为2001:1::2,目的IPV6为2001:2::2的ipv6报文,然后查看PC1的路由表,发送到R1,r1接收后,以目的IPV6地址2001:2::2查询IPV6路由表,出接口为tun0/0/0…...
项目解决方案:海外门店视频汇聚方案(全球性的连锁店、国外连锁店视频接入和汇聚方案)
目 录 一、概述 二、建设目标及需求 2.1 建设目标 2.2 需求描述 2.3 需求分析 三、建设方案设计 3.1 系统方案拓扑图 3.2 方案描述 3.3 服务器配置推荐 四、产品功能 4.1 资源管理平台 (1)用户权限管理 (2)…...
Java中的数据类型详解
引言 在Java编程中,数据类型是非常重要的概念,它定义了数据的类型和范围,帮助程序员有效地操作数据。Java的数据类型可以分为两大类:基本数据类型和引用数据类型。本文将详细介绍Java中的各种数据类型,并附上相应的代…...
ABBYY FineReader16文档转换、PDF管理与文档比较功能介绍
ABBYY FineReader 16作为一款OCR和PDF一体化程序,其强大的功能使得文档处理变得简单高效。在众多功能中,文档转换、PDF管理和文档比较这三大功能尤为突出,成为了众多企业和个人用户的首选工具。 ABBYY Finereader 16-安装包下载如下ÿ…...
导览系统厂家|景区电子导览|手绘地图|AR导览|语音导览系统
随着元宇宙、VR、AR等新技术的快速发展,旅游服务也更加多元化、智能化。景区导览系统作为旅游服务的重要组成部分,其形式更加多元化智能化。智能导览系统作为一种新的服务方式,能够为游客提供更加便捷的旅游服务和游览体验,也逐渐…...
oracle 如何使用脚本实现访问控制(无需额外插件)
随着这些年勒索病毒的爆发,各个企业对数据安全的要求越来越高,常见的办法有开启数据库审计,加数据库防火墙,网络限制等等;但是细粒度审计会消耗大量系统资源,第三方数据库防火墙一般是需要收费的;这里介绍我个人常用的四个db级别trigger,用于记录部分关键信息可以应对部…...
接口测试中缓存处理策略
在接口测试中,缓存处理策略是一个关键环节,直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性,避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明: 一、缓存处理的核…...
地震勘探——干扰波识别、井中地震时距曲线特点
目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波:可以用来解决所提出的地质任务的波;干扰波:所有妨碍辨认、追踪有效波的其他波。 地震勘探中,有效波和干扰波是相对的。例如,在反射波…...
DockerHub与私有镜像仓库在容器化中的应用与管理
哈喽,大家好,我是左手python! Docker Hub的应用与管理 Docker Hub的基本概念与使用方法 Docker Hub是Docker官方提供的一个公共镜像仓库,用户可以在其中找到各种操作系统、软件和应用的镜像。开发者可以通过Docker Hub轻松获取所…...
线程同步:确保多线程程序的安全与高效!
全文目录: 开篇语前序前言第一部分:线程同步的概念与问题1.1 线程同步的概念1.2 线程同步的问题1.3 线程同步的解决方案 第二部分:synchronized关键字的使用2.1 使用 synchronized修饰方法2.2 使用 synchronized修饰代码块 第三部分ÿ…...
大数据零基础学习day1之环境准备和大数据初步理解
学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 (1)设置网关 打开VMware虚拟机,点击编辑…...
Qwen3-Embedding-0.6B深度解析:多语言语义检索的轻量级利器
第一章 引言:语义表示的新时代挑战与Qwen3的破局之路 1.1 文本嵌入的核心价值与技术演进 在人工智能领域,文本嵌入技术如同连接自然语言与机器理解的“神经突触”——它将人类语言转化为计算机可计算的语义向量,支撑着搜索引擎、推荐系统、…...
python如何将word的doc另存为docx
将 DOCX 文件另存为 DOCX 格式(Python 实现) 在 Python 中,你可以使用 python-docx 库来操作 Word 文档。不过需要注意的是,.doc 是旧的 Word 格式,而 .docx 是新的基于 XML 的格式。python-docx 只能处理 .docx 格式…...
零基础设计模式——行为型模式 - 责任链模式
第四部分:行为型模式 - 责任链模式 (Chain of Responsibility Pattern) 欢迎来到行为型模式的学习!行为型模式关注对象之间的职责分配、算法封装和对象间的交互。我们将学习的第一个行为型模式是责任链模式。 核心思想:使多个对象都有机会处…...
成都鼎讯硬核科技!雷达目标与干扰模拟器,以卓越性能制胜电磁频谱战
在现代战争中,电磁频谱已成为继陆、海、空、天之后的 “第五维战场”,雷达作为电磁频谱领域的关键装备,其干扰与抗干扰能力的较量,直接影响着战争的胜负走向。由成都鼎讯科技匠心打造的雷达目标与干扰模拟器,凭借数字射…...
CMake控制VS2022项目文件分组
我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...
