Example #1
2
/**
 * Creates a static mesh object from raw triangle data.
 */
UStaticMesh* CreateStaticMesh(struct FRawMesh& RawMesh,TArray<UMaterialInterface*>& Materials,UObject* InOuter,FName InName)
{
	// Create the UStaticMesh object.
	FStaticMeshComponentRecreateRenderStateContext RecreateRenderStateContext(FindObject<UStaticMesh>(InOuter,*InName.ToString()));
	UStaticMesh* StaticMesh = new(InOuter,InName,RF_Public|RF_Standalone) UStaticMesh(FPostConstructInitializeProperties());

	// Add one LOD for the base mesh
	FStaticMeshSourceModel* SrcModel = new(StaticMesh->SourceModels) FStaticMeshSourceModel();
	SrcModel->RawMeshBulkData->SaveRawMesh(RawMesh);
	StaticMesh->Materials = Materials;

	int32 NumSections = StaticMesh->Materials.Num();

	// Set up the SectionInfoMap to enable collision
	for (int32 SectionIdx = 0; SectionIdx < NumSections; ++SectionIdx)
	{
		FMeshSectionInfo Info = StaticMesh->SectionInfoMap.Get(0, SectionIdx);
		Info.MaterialIndex = SectionIdx;
		Info.bEnableCollision = true;
		StaticMesh->SectionInfoMap.Set(0, SectionIdx, Info);
	}

	StaticMesh->Build();
	StaticMesh->MarkPackageDirty();
	return StaticMesh;
}
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));
                }

                // Build mesh from source
                StaticMesh->Build(false);
                StaticMesh->PostEditChange();

                // Notify asset registry of new asset
                FAssetRegistryModule::AssetCreated(StaticMesh);
            }
        }
    }

    return FReply::Handled();
}
FReply FRuntimeMeshComponentDetails::ClickedOnConvertToStaticMesh()
{
 	// Find first selected RuntimeMeshComp
 	URuntimeMeshComponent* RuntimeMeshComp = GetFirstSelectedRuntimeMeshComp();
 	if (RuntimeMeshComp != nullptr)
 	{
 		FString NewNameSuggestion = FString(TEXT("RuntimeMeshComp"));
 		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;
 					
			// Unique Materials to apply to new mesh
#if ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION >= 14
			TArray<FStaticMaterial> Materials;
#else
			TArray<UMaterialInterface*> Materials;
#endif
 
			bool bUseHighPrecisionTangents = false;
			bool bUseFullPrecisionUVs = false;

 			const int32 NumSections = RuntimeMeshComp->GetNumSections();
 			int32 VertexBase = 0;
 			for (int32 SectionIdx = 0; SectionIdx < NumSections; SectionIdx++)
 			{
				const IRuntimeMeshVerticesBuilder* Vertices;
				const FRuntimeMeshIndicesBuilder* Indices;
				RuntimeMeshComp->GetSectionMesh(SectionIdx, Vertices, Indices);

				if (Vertices->HasHighPrecisionNormals())
				{
					bUseHighPrecisionTangents = true;
				}
				if (Vertices->HasHighPrecisionUVs())
				{
					bUseFullPrecisionUVs = true;
				}
 
 				// Copy verts
				Vertices->Seek(-1);
				while (Vertices->MoveNext() < Vertices->Length())
				{
					RawMesh.VertexPositions.Add(Vertices->GetPosition());
				}
 
 				// Copy 'wedge' info
				Indices->Seek(0);
				while (Indices->HasRemaining())
 				{
					int32 Index = Indices->ReadOne();
 
 					RawMesh.WedgeIndices.Add(Index + VertexBase);
 

					Vertices->Seek(Index);
 
					FVector TangentX = Vertices->GetTangent();
					FVector TangentZ = Vertices->GetNormal();
 					FVector TangentY = (TangentX ^ TangentZ).GetSafeNormal() * Vertices->GetNormal().W;
 
 					RawMesh.WedgeTangentX.Add(TangentX);
 					RawMesh.WedgeTangentY.Add(TangentY);
 					RawMesh.WedgeTangentZ.Add(TangentZ);
 
					for (int UVIndex = 0; UVIndex < 8; UVIndex++)
					{
						if (!Vertices->HasUVComponent(UVIndex))
						{
							break;
						}
						RawMesh.WedgeTexCoords[UVIndex].Add(Vertices->GetUV(UVIndex));
					}

 					RawMesh.WedgeColors.Add(Vertices->GetColor());
 				}
 
				// Find a material index for this section.
				UMaterialInterface* Material = RuntimeMeshComp->GetMaterial(SectionIdx);

#if ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION >= 14
				int32 MaterialIndex = Materials.AddUnique(FStaticMaterial(Material));
#else
				int32 MaterialIndex = Materials.AddUnique(Material);
#endif
				

 				// copy face info
 				int32 NumTris = Indices->Length() / 3;
 				for (int32 TriIdx=0; TriIdx < NumTris; TriIdx++)
 				{
					// Set the face material
					RawMesh.FaceMaterialIndices.Add(MaterialIndex);

 					RawMesh.FaceSmoothingMasks.Add(0); // Assume this is ignored as bRecomputeNormals is false
 				}
 
 				// Update offset for creating one big index/vertex buffer
				VertexBase += Vertices->Length();
 			}
 
 			// 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 = bUseHighPrecisionTangents;
				SrcModel->BuildSettings.bUseFullPrecisionUVs = bUseFullPrecisionUVs;
 				SrcModel->BuildSettings.bGenerateLightmapUVs = true;
 				SrcModel->BuildSettings.SrcLightmapIndex = 0;
 				SrcModel->BuildSettings.DstLightmapIndex = 1;
 				SrcModel->RawMeshBulkData->SaveRawMesh(RawMesh);
 
				// Set the materials used for this static mesh
#if ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION >= 14
				StaticMesh->StaticMaterials = Materials;
				int32 NumMaterials = StaticMesh->StaticMaterials.Num();
#else
				StaticMesh->Materials = Materials;
				int32 NumMaterials = StaticMesh->Materials.Num();
#endif

				// Set up the SectionInfoMap to enable collision
				for (int32 SectionIdx = 0; SectionIdx < NumMaterials; SectionIdx++)
				{
					FMeshSectionInfo Info = StaticMesh->SectionInfoMap.Get(0, SectionIdx);
					Info.MaterialIndex = SectionIdx;
					Info.bEnableCollision = true;
					StaticMesh->SectionInfoMap.Set(0, SectionIdx, Info);
				}

				// Configure body setup for working collision.
				StaticMesh->CreateBodySetup();
				StaticMesh->BodySetup->CollisionTraceFlag = CTF_UseComplexAsSimple;

 				// Build mesh from source
 				StaticMesh->Build(false);

				// Make package dirty.
				StaticMesh->MarkPackageDirty();

 				StaticMesh->PostEditChange();
 
 				// Notify asset registry of new asset
 				FAssetRegistryModule::AssetCreated(StaticMesh);
 			}
 		}
 	}

	return FReply::Handled();
}