How to convert a procedural mesh component into a static mesh component in ue4.27?

What’s wrong with the following code?

UStaticMesh* ProceduralmeshComponentConvertStaticmeshComponent(UProceduralMeshComponent* ProcMesh, const FString& AssetName, const FString& PackagePath)
{
    UStaticMesh* StaticMesh = nullptr;
#if WITH_EDITOR
    if (!ProcMesh || ProcMesh->GetNumSections() == 0)
    {
        UE_LOG(LogTemp, Error, TEXT("ProceduralMesh is invalid or has no sections."));
        return;
    }

    //Create StaticMesh
    FString FullPackageName = PackagePath + TEXT("/") + AssetName;
    UPackage* Package = CreatePackage(*FullPackageName);
    StaticMesh = NewObject<UStaticMesh>(Package, *AssetName, RF_Public | RF_Standalone);
    StaticMesh->InitResources();

    // 
    StaticMesh->AddSourceModel();
    FStaticMeshSourceModel& SrcModel = StaticMesh->GetSourceModel(0);
    SrcModel.BuildSettings.bRecomputeNormals = false;
    SrcModel.BuildSettings.bRecomputeTangents = false;
    SrcModel.BuildSettings.bUseMikkTSpace = true;

    FRawMesh RawMesh;

    // 
    FProcMeshSection* Section = ProcMesh->GetProcMeshSection(0);
    if (!Section) return;

    for (const FProcMeshVertex& Vertex : Section->ProcVertexBuffer)
    {
        RawMesh.VertexPositions.Add(Vertex.Position);
        RawMesh.WedgeTangentX.Add(Vertex.Tangent.TangentX);
        RawMesh.WedgeTangentY.Add(FVector(0, 0, 0)); // 可选
        RawMesh.WedgeTangentZ.Add(Vertex.Normal);
        RawMesh.WedgeTexCoords[0].Add(Vertex.UV0);
        RawMesh.WedgeColors.Add(Vertex.Color);
    }

    int32 NumTris = Section->ProcIndexBuffer.Num() / 3;
    for (int32 TriIdx = 0; TriIdx < NumTris; ++TriIdx)
    {
        for (int32 Corner = 0; Corner < 3; ++Corner)
        {
            int32 Index = Section->ProcIndexBuffer[TriIdx * 3 + Corner];
            RawMesh.WedgeIndices.Add(Index);
            RawMesh.FaceMaterialIndices.Add(0);
            RawMesh.FaceSmoothingMasks.Add(0);
        }
    }

    // 
    SrcModel.SaveRawMesh(RawMesh);

    // 
    UMaterialInterface* DefaultMat = ProcMesh->GetMaterial(0);
    StaticMesh->GetStaticMaterials().Add(FStaticMaterial(DefaultMat));

    // 
    StaticMesh->Build();
    StaticMesh->MarkPackageDirty();
    // 
    FAssetRegistryModule::AssetCreated(StaticMesh);
#endif
    return StaticMesh;
}

Hey @Mirro1871 ! how are you?

I’ve been checking this and there are some quick fixes you can test to make it work:

  1. Make sure your module’s Build.cs file includes:
PublicDependencyModuleNames.AddRange(new string[] { 
    "AssetRegistry", 
    "UnrealEd" 
});
  1. Here is the fixed code with some comments:
// Add this include at the top of your file if you didn't do it yet
#include "AssetRegistry/AssetRegistryModule.h"

UStaticMesh* ProceduralmeshComponentConvertStaticmeshComponent(UProceduralMeshComponent* ProcMesh, const FString& AssetName, const FString& PackagePath)
{
    UStaticMesh* StaticMesh = nullptr;
#if WITH_EDITOR
    if (!ProcMesh || ProcMesh->GetNumSections() == 0)
    {
        UE_LOG(LogTemp, Error, TEXT("ProceduralMesh is invalid or has no sections."));
        return nullptr; // FIXED: was "return;" - should return nullptr
    }

    // Create StaticMesh
    FString FullPackageName = PackagePath + TEXT("/") + AssetName;
    UPackage* Package = CreatePackage(*FullPackageName);
    StaticMesh = NewObject<UStaticMesh>(Package, *AssetName, RF_Public | RF_Standalone);
    StaticMesh->InitResources();

    StaticMesh->AddSourceModel();
    FStaticMeshSourceModel& SrcModel = StaticMesh->GetSourceModel(0);
    SrcModel.BuildSettings.bRecomputeNormals = false;
    SrcModel.BuildSettings.bRecomputeTangents = false;
    SrcModel.BuildSettings.bUseMikkTSpace = true;

    FRawMesh RawMesh;

    FProcMeshSection* Section = ProcMesh->GetProcMeshSection(0);
    if (!Section) 
    {
        return nullptr; // FIXED: was "return;" - should return nullptr
    }

    for (const FProcMeshVertex& Vertex : Section->ProcVertexBuffer)
    {
        RawMesh.VertexPositions.Add(Vertex.Position);
        RawMesh.WedgeTangentX.Add(Vertex.Tangent.TangentX);
        RawMesh.WedgeTangentY.Add(FVector(0, 0, 0)); // Optional - could calculate proper binormal
        RawMesh.WedgeTangentZ.Add(Vertex.Normal);
        RawMesh.WedgeTexCoords[0].Add(Vertex.UV0);
        RawMesh.WedgeColors.Add(Vertex.Color);
    }

    int32 NumTris = Section->ProcIndexBuffer.Num() / 3;
    for (int32 TriIdx = 0; TriIdx < NumTris; ++TriIdx)
    {
        for (int32 Corner = 0; Corner < 3; ++Corner)
        {
            int32 Index = Section->ProcIndexBuffer[TriIdx * 3 + Corner];
            RawMesh.WedgeIndices.Add(Index);
            RawMesh.FaceMaterialIndices.Add(0);
            RawMesh.FaceSmoothingMasks.Add(0);
        }
    }

    // Save raw mesh data
    SrcModel.SaveRawMesh(RawMesh);

    // Add material with null check
    UMaterialInterface* DefaultMat = ProcMesh->GetMaterial(0);
    if (DefaultMat) // ADDED: null check to prevent crash
    {
        StaticMesh->GetStaticMaterials().Add(FStaticMaterial(DefaultMat));
    }
    else
    {
        // Add a default material slot even if no material is assigned
        StaticMesh->GetStaticMaterials().Add(FStaticMaterial());
    }

    // Build and finalize
    StaticMesh->Build();
    StaticMesh->MarkPackageDirty();
    
    // Register with asset registry (UE5 method)
    FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
    AssetRegistryModule.AssetCreated(StaticMesh);
#endif
    return StaticMesh;
}

I’m also learning about this but I think this should fix it!

Let me know if this works!

Need to generate scene assets