/** * 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(); }