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,用于记录部分关键信息可以应对部…...
接口测试中缓存处理策略
在接口测试中,缓存处理策略是一个关键环节,直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性,避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明: 一、缓存处理的核…...
【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器
一.自适应梯度算法Adagrad概述 Adagrad(Adaptive Gradient Algorithm)是一种自适应学习率的优化算法,由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率,适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...
React第五十七节 Router中RouterProvider使用详解及注意事项
前言 在 React Router v6.4 中,RouterProvider 是一个核心组件,用于提供基于数据路由(data routers)的新型路由方案。 它替代了传统的 <BrowserRouter>,支持更强大的数据加载和操作功能(如 loader 和…...
Day131 | 灵神 | 回溯算法 | 子集型 子集
Day131 | 灵神 | 回溯算法 | 子集型 子集 78.子集 78. 子集 - 力扣(LeetCode) 思路: 笔者写过很多次这道题了,不想写题解了,大家看灵神讲解吧 回溯算法套路①子集型回溯【基础算法精讲 14】_哔哩哔哩_bilibili 完…...
可靠性+灵活性:电力载波技术在楼宇自控中的核心价值
可靠性灵活性:电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中,电力载波技术(PLC)凭借其独特的优势,正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据,无需额外布…...
Linux简单的操作
ls ls 查看当前目录 ll 查看详细内容 ls -a 查看所有的内容 ls --help 查看方法文档 pwd pwd 查看当前路径 cd cd 转路径 cd .. 转上一级路径 cd 名 转换路径 …...
django filter 统计数量 按属性去重
在Django中,如果你想要根据某个属性对查询集进行去重并统计数量,你可以使用values()方法配合annotate()方法来实现。这里有两种常见的方法来完成这个需求: 方法1:使用annotate()和Count 假设你有一个模型Item,并且你想…...
华为OD机试-食堂供餐-二分法
import java.util.Arrays; import java.util.Scanner;public class DemoTest3 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseint a in.nextIn…...
HTML前端开发:JavaScript 常用事件详解
作为前端开发的核心,JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例: 1. onclick - 点击事件 当元素被单击时触发(左键点击) button.onclick function() {alert("按钮被点击了!&…...
安卓基础(aar)
重新设置java21的环境,临时设置 $env:JAVA_HOME "D:\Android Studio\jbr" 查看当前环境变量 JAVA_HOME 的值 echo $env:JAVA_HOME 构建ARR文件 ./gradlew :private-lib:assembleRelease 目录是这样的: MyApp/ ├── app/ …...
