x

Search in
Sort by:

Question Status:

Search help

  • Simple searches use one or more words. Separate the words with spaces (cat dog) to search cat,dog or both. Separate the words with plus signs (cat +dog) to search for items that may contain cat but must contain dog.
  • You can further refine your search on the search results page, where you can search by keywords, author, topic. These can be combined with each other. Examples
    • cat dog --matches anything with cat,dog or both
    • cat +dog --searches for cat +dog where dog is a mandatory term
    • cat -dog -- searches for cat excluding any result containing dog
    • [cats] —will restrict your search to results with topic named "cats"
    • [cats] [dogs] —will restrict your search to results with both topics, "cats", and "dogs"

Procedural mesh not saving all of its sections to static mesh

I wanted to be able to save out my procedural meshes as static ones so I copied the code from ProceduralMeshComponentDetails.cpp

 FReply FProceduralMeshComponentDetails::ClickedOnConvertToStaticMesh()
 {
     // Find first selected ProcMeshComp
     UProceduralMeshComponent* ProcMeshComp = GetFirstSelectedProcMeshComp();
     if (ProcMeshComp != nullptr)
     {
         FString NewNameSuggestion = FString(TEXT("ProcMesh"));
         FString PackageName = FString(TEXT("/Game/Meshes/")) + NewNameSuggestion;
         FString Name;
         FAssetToolsModule& AssetToolsModule = FModuleManager::LoadModuleChecked<FAssetToolsModule>("AssetTools");
         AssetToolsModule.Get().CreateUniqueAssetName(PackageName, TEXT(""), PackageName, Name);
 
         TSharedPtr<SDlgPickAssetPath> PickAssetPathWidget =
             SNew(SDlgPickAssetPath)
             .Title(LOCTEXT("ConvertToStaticMeshPickName", "Choose New StaticMesh Location"))
             .DefaultAssetPath(FText::FromString(PackageName));
 
         if (PickAssetPathWidget->ShowModal() == EAppReturnType::Ok)
         {
             // Get the full name of where we want to create the physics asset.
             FString UserPackageName = PickAssetPathWidget->GetFullAssetPath().ToString();
             FName MeshName(*FPackageName::GetLongPackageAssetName(UserPackageName));
 
             // Check if the user inputed a valid asset name, if they did not, give it the generated default name
             if (MeshName == NAME_None)
             {
                 // Use the defaults that were already generated.
                 UserPackageName = PackageName;
                 MeshName = *Name;
             }
 
             // Raw mesh data we are filling in
             FRawMesh RawMesh;
             // Materials to apply to new mesh
             TArray<UMaterialInterface*> MeshMaterials;
 
             const int32 NumSections = ProcMeshComp->GetNumSections();
             int32 VertexBase = 0;
             for (int32 SectionIdx = 0; SectionIdx < NumSections; SectionIdx++)
             {
                 FProcMeshSection* ProcSection = ProcMeshComp->GetProcMeshSection(SectionIdx);
 
                 // Copy verts
                 for (FProcMeshVertex& Vert : ProcSection->ProcVertexBuffer)
                 {
                     RawMesh.VertexPositions.Add(Vert.Position);
                 }
 
                 // Copy 'wedge' info
                 int32 NumIndices = ProcSection->ProcIndexBuffer.Num();
                 for (int32 IndexIdx=0; IndexIdx < NumIndices; IndexIdx++)
                 {
                     int32 Index = ProcSection->ProcIndexBuffer[IndexIdx];
 
                     RawMesh.WedgeIndices.Add(Index + VertexBase);
 
                     FProcMeshVertex& ProcVertex = ProcSection->ProcVertexBuffer[Index];
 
                     FVector TangentX = ProcVertex.Tangent.TangentX;
                     FVector TangentZ = ProcVertex.Normal;
                     FVector TangentY = (TangentX ^ TangentZ).GetSafeNormal() * (ProcVertex.Tangent.bFlipTangentY ? -1.f : 1.f);
 
                     RawMesh.WedgeTangentX.Add(TangentX);
                     RawMesh.WedgeTangentY.Add(TangentY);
                     RawMesh.WedgeTangentZ.Add(TangentZ);
 
                     RawMesh.WedgeTexCoords[0].Add(ProcVertex.UV0);
                     RawMesh.WedgeColors.Add(ProcVertex.Color);
                 }
 
                 // copy face info
                 int32 NumTris = NumIndices / 3;
                 for (int32 TriIdx=0; TriIdx < NumTris; TriIdx++)
                 {
                     RawMesh.FaceMaterialIndices.Add(SectionIdx);
                     RawMesh.FaceSmoothingMasks.Add(0); // Assume this is ignored as bRecomputeNormals is false
                 }
 
                 // Remember material
                 MeshMaterials.Add(ProcMeshComp->GetMaterial(SectionIdx));
 
                 // Update offset for creating one big index/vertex buffer
                 VertexBase += ProcSection->ProcVertexBuffer.Num();
             }
 
             // If we got some valid data.
             if (RawMesh.VertexPositions.Num() > 3 && RawMesh.WedgeIndices.Num() > 3)
             {
                 // Then find/create it.
                 UPackage* Package = CreatePackage(NULL, *UserPackageName);
                 check(Package);
 
                 // Create StaticMesh object
                 UStaticMesh* StaticMesh = NewObject<UStaticMesh>(Package, MeshName, RF_Public | RF_Standalone);
                 StaticMesh->InitResources();
 
                 StaticMesh->LightingGuid = FGuid::NewGuid();
 
                 // Add source to new StaticMesh
                 FStaticMeshSourceModel* SrcModel = new (StaticMesh->SourceModels) FStaticMeshSourceModel();
                 SrcModel->BuildSettings.bRecomputeNormals = false;
                 SrcModel->BuildSettings.bRecomputeTangents = false;
                 SrcModel->BuildSettings.bRemoveDegenerates = false;
                 SrcModel->BuildSettings.bUseHighPrecisionTangentBasis = false;
                 SrcModel->BuildSettings.bUseFullPrecisionUVs = false;
                 SrcModel->BuildSettings.bGenerateLightmapUVs = true;
                 SrcModel->BuildSettings.SrcLightmapIndex = 0;
                 SrcModel->BuildSettings.DstLightmapIndex = 1;
                 SrcModel->RawMeshBulkData->SaveRawMesh(RawMesh);
 
                 // Copy materials to new mesh
                 for (UMaterialInterface* Material : MeshMaterials)
                 {
                     StaticMesh->StaticMaterials.Add(FStaticMaterial(Material));
                 }
 
                 //Set the Imported version before calling the build
                 StaticMesh->ImportVersion = EImportStaticMeshVersion::LastVersion;
 
                 // Build mesh from source
                 StaticMesh->Build(false);
                 StaticMesh->PostEditChange();
 
                 // Notify asset registry of new asset
                 FAssetRegistryModule::AssetCreated(StaticMesh);
             }
         }
     }
 
     return FReply::Handled();
 }

Then I rewrote it into a blueprint library function

 UStaticMesh* UWebEZBPFunctionLibrary::SaveProceduralWebMesh(UProceduralMeshComponent* ProcMesh)
 {
     //UStaticMesh = UStaticMesh::create
 
     // Find first selected ProcMeshComp
     UProceduralMeshComponent* ProcMeshComp = ProcMesh;
     if (ProcMeshComp != nullptr)
     {
         FString ActorName = ProcMesh->GetOwner()->GetName();
         FString LevelName = ProcMesh->GetWorld()->GetMapName();
         FString AssetName = FString(TEXT("SM_")) + LevelName + FString(TEXT("_") + ActorName);
         FString PathName = FString(TEXT("/Game/WebEZMeshes/"));
         FString PackageName = PathName + AssetName;
 
         // Raw mesh data we are filling in
         FRawMesh RawMesh;
         // Materials to apply to new mesh
         TArray<UMaterialInterface*> MeshMaterials;
 
         const int32 NumSections = ProcMeshComp->GetNumSections();
         int32 VertexBase = 0;
         for (int32 SectionIdx = 0; SectionIdx < NumSections; SectionIdx++)
         {
             FProcMeshSection* ProcSection = ProcMeshComp->GetProcMeshSection(SectionIdx);
 
             // Copy verts
             for (FProcMeshVertex& Vert : ProcSection->ProcVertexBuffer)
             {
                 RawMesh.VertexPositions.Add(Vert.Position);
             }
 
             // Copy 'wedge' info
             int32 NumIndices = ProcSection->ProcIndexBuffer.Num();
             for (int32 IndexIdx = 0; IndexIdx < NumIndices; IndexIdx++)
             {
                 int32 Index = ProcSection->ProcIndexBuffer[IndexIdx];
 
                 RawMesh.WedgeIndices.Add(Index + VertexBase);
 
                 FProcMeshVertex& ProcVertex = ProcSection->ProcVertexBuffer[Index];
 
                 FVector TangentX = ProcVertex.Tangent.TangentX;
                 FVector TangentZ = ProcVertex.Normal;
                 FVector TangentY = (TangentX ^ TangentZ).GetSafeNormal() * (ProcVertex.Tangent.bFlipTangentY ? -1.f : 1.f);
 
                 RawMesh.WedgeTangentX.Add(TangentX);
                 RawMesh.WedgeTangentY.Add(TangentY);
                 RawMesh.WedgeTangentZ.Add(TangentZ);
 
                 RawMesh.WedgeTexCoords[0].Add(ProcVertex.UV0);
                 RawMesh.WedgeColors.Add(ProcVertex.Color);
             }
 
             // copy face info
             int32 NumTris = NumIndices / 3;
             for (int32 TriIdx = 0; TriIdx < NumTris; TriIdx++)
             {
                 RawMesh.FaceMaterialIndices.Add(SectionIdx);
                 RawMesh.FaceSmoothingMasks.Add(0); // Assume this is ignored as bRecomputeNormals is false
             }
 
             // Remember material
             MeshMaterials.Add(ProcMeshComp->GetMaterial(SectionIdx));
 
             // Update offset for creating one big index/vertex buffer
             VertexBase += ProcSection->ProcVertexBuffer.Num();
 
             // If we got some valid data.
             if (RawMesh.VertexPositions.Num() > 3 && RawMesh.WedgeIndices.Num() > 3)
             {
                 // Then find/create it.
                 UPackage* Package = CreatePackage(NULL, *PackageName);
                 check(Package);
 
                 // Create StaticMesh object
                 UStaticMesh* StaticMesh = NewObject<UStaticMesh>(Package, FName(*AssetName), RF_Public | RF_Standalone);
                 StaticMesh->InitResources();
 
                 StaticMesh->LightingGuid = FGuid::NewGuid();
 
                 // Add source to new StaticMesh
                 FStaticMeshSourceModel* SrcModel = new (StaticMesh->SourceModels) FStaticMeshSourceModel();
                 SrcModel->BuildSettings.bRecomputeNormals = false;
                 SrcModel->BuildSettings.bRecomputeTangents = false;
                 SrcModel->BuildSettings.bRemoveDegenerates = false;
                 SrcModel->BuildSettings.bUseHighPrecisionTangentBasis = false;
                 SrcModel->BuildSettings.bUseFullPrecisionUVs = false;
                 SrcModel->BuildSettings.bGenerateLightmapUVs = true;
                 SrcModel->BuildSettings.SrcLightmapIndex = 0;
                 SrcModel->BuildSettings.DstLightmapIndex = 1;
                 SrcModel->RawMeshBulkData->SaveRawMesh(RawMesh);
 
                 // Copy materials to new mesh
                 for (UMaterialInterface* Material : MeshMaterials)
                 {
                     StaticMesh->StaticMaterials.Add(FStaticMaterial(Material));
                 }
 
                 //Set the Imported version before calling the build
                 StaticMesh->ImportVersion = EImportStaticMeshVersion::LastVersion;
 
                 // Build mesh from source
                 StaticMesh->Build(false);
                 StaticMesh->PostEditChange();
 
                 // Notify asset registry of new asset
                 FAssetRegistryModule::AssetCreated(StaticMesh);
 
                 return StaticMesh;
             }
         }
     }
     else
     {
         return nullptr;
     }
 }

However, I get a different result. When I compare directly from the same procedural mesh, it looks like my version is only saving two of the mesh's sections. Any ideas why?

Product Version: UE 4.14
Tags:
more ▼

asked Jan 19 '17 at 03:21 AM in C++ Programming

avatar image

BlackFangTech
210 23 33 58

avatar image VlecadGS Apr 19 '18 at 07:04 AM

Is there a tutorial for converting procedural mesh into static mesh in c++?

(comments are locked)
10|2000 characters needed characters left
Viewable by all users

2 answers: sort voted first

Oh. Its because I am returning from inside the section for loop.

more ▼

answered Jan 19 '17 at 03:26 AM

avatar image

BlackFangTech
210 23 33 58

avatar image fullnamefull Nov 08 '17 at 08:52 AM

I had the same issue, could you share your modified code? thx.

avatar image L34D3R Dec 12 '17 at 04:25 AM

@BlackRang666 That's awesome. i tried replacing your code in ProceduralMeshComponentDetails.cpp

But i got error as 'UWebEZBPFunctionLibrary': is not a class or namespace name. How do i do that ?

can it be possible by blueprint node? can you please share it?

avatar image Ryokath Feb 02 '18 at 08:03 PM

Just replace the "UWebEZBPFunctionLibrary" to the namespace of your C++ Blueprint Function Library class and this should be fixed.

(comments are locked)
10|2000 characters needed characters left
Viewable by all users

I guess you also had to use RawMesh.h to implement this function. Have you ever tried to package your game using this functionalities for the creation of static meshes?

more ▼

answered May 08 '19 at 03:01 PM

avatar image

JoseRoch
31 3 4

I have the same question ,cause RawMesh belongs to developer folders.

(comments are locked)
10|2000 characters needed characters left
Viewable by all users
Your answer
toggle preview:

Up to 5 attachments (including images) can be used with a maximum of 5.2 MB each and 5.2 MB total.

Follow this question

Once you sign in you will be able to subscribe for any updates here

Answers to this question