void URuntimeMeshLibrary::CopyStaticMeshToRuntimeMesh(UStaticMeshComponent* StaticMeshComponent, int32 LODIndex, URuntimeMesh* RuntimeMesh, bool bCreateCollision) { SCOPE_CYCLE_COUNTER(STAT_RuntimeMeshLibrary_CopyStaticMeshToRuntimeMesh); if (StaticMeshComponent != nullptr && StaticMeshComponent->GetStaticMesh() != nullptr && RuntimeMesh != nullptr) { UStaticMesh* StaticMesh = StaticMeshComponent->GetStaticMesh(); RuntimeMesh->ClearAllMeshSections(); //// MESH DATA int32 NumSections = StaticMesh->GetNumSections(LODIndex); for (int32 SectionIndex = 0; SectionIndex < NumSections; SectionIndex++) { // Buffers for copying geom data TSharedPtr<FRuntimeMeshBuilder> MeshData = MakeRuntimeMeshBuilder<FRuntimeMeshTangents, FVector2DHalf, uint32>(); TArray<uint8> AdjacencyTrianglesData; TSharedPtr<FRuntimeMeshIndicesAccessor> AdjacencyTrianglesAccessor = MakeShared<FRuntimeMeshIndicesAccessor>(true, &AdjacencyTrianglesData); TArray<uint32> AdjacencyTriangles; AdjacencyTriangles.SetNum(AdjacencyTrianglesData.Num() / 4); FMemory::Memcpy(AdjacencyTriangles.GetData(), AdjacencyTrianglesData.GetData(), AdjacencyTrianglesData.Num()); // TODO: Make this smarter to setup a mesh section to match the static mesh vertex configuration // This will let it pick up the additional UVs or high pri normals/tangents/uvs // Get geom data from static mesh GetStaticMeshSection(StaticMesh, LODIndex, SectionIndex, MeshData, AdjacencyTrianglesAccessor); // Create RuntimeMesh RuntimeMesh->CreateMeshSection(SectionIndex, MeshData, bCreateCollision); RuntimeMesh->SetSectionTessellationTriangles(SectionIndex, AdjacencyTriangles); } //// SIMPLE COLLISION CopyCollisionFromStaticMesh(StaticMesh, RuntimeMesh); //// MATERIALS for (int32 MatIndex = 0; MatIndex < StaticMeshComponent->GetNumMaterials(); MatIndex++) { RuntimeMesh->SetSectionMaterial(MatIndex, StaticMeshComponent->GetMaterial(MatIndex)); } } }
EReimportResult::Type UReimportSpeedTreeFactory::Reimport(UObject* Obj) { #if WITH_SPEEDTREE UStaticMesh* Mesh = Cast<UStaticMesh>(Obj); if (!Mesh) { return EReimportResult::Failed; } const FString Filename = Mesh->AssetImportData->GetFirstFilename(); const FString FileExtension = FPaths::GetExtension(Filename); const bool bIsSRT = FCString::Stricmp(*FileExtension, TEXT("SRT")) == 0; if (!bIsSRT) { return EReimportResult::Failed; } if (!(Filename.Len())) { // Since this is a new system most static meshes don't have paths, so logging has been commented out //UE_LOG(LogEditorFactories, Warning, TEXT("-- cannot reimport: static mesh resource does not have path stored.")); return EReimportResult::Failed; } UE_LOG(LogEditorFactories, Log, TEXT("Performing atomic reimport of [%s]"), *Filename); if (UFactory::StaticImportObject(Mesh->GetClass(), Mesh->GetOuter(), *Mesh->GetName(), RF_Public | RF_Standalone, *Filename, NULL, this)) { UE_LOG(LogEditorFactories, Log, TEXT("-- imported successfully")); Mesh->AssetImportData->Update(Filename); // Mark the package dirty after the successful import Mesh->MarkPackageDirty(); return EReimportResult::Succeeded; } else { UE_LOG(LogEditorFactories, Warning, TEXT("-- import failed")); } #endif // #if WITH_SPEEDTREE return EReimportResult::Failed; }
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(); }
EReimportResult::Type UReimportFbxSceneFactory::ReimportStaticMesh(void* VoidFbxImporter, TSharedPtr<FFbxMeshInfo> MeshInfo) { UnFbx::FFbxImporter* FbxImporter = (UnFbx::FFbxImporter*)VoidFbxImporter; //Find the UObject associate with this MeshInfo UPackage* PkgExist = LoadPackage(nullptr, *(MeshInfo->GetImportPath()), LOAD_Verify | LOAD_NoWarn); if (PkgExist != nullptr) { PkgExist->FullyLoad(); } FString AssetName = MeshInfo->GetFullImportName(); UStaticMesh* Mesh = FindObjectSafe<UStaticMesh>(ANY_PACKAGE, *AssetName); if (Mesh == nullptr) { //We reimport only static mesh here FbxImporter->AddTokenizedErrorMessage(FTokenizedMessage::Create(EMessageSeverity::Error, FText::Format(FText::FromString("Reimport Mesh {0} fail, the original staicmesh in the content browser cannot be load."), FText::FromString(MeshInfo->GetImportPath()))), FName(TEXT("Reimport Fbx Scene"))); return EReimportResult::Failed; } //Copy default options to StaticMeshImportData SFbxSceneOptionWindow::CopyFbxOptionsToStaticMeshOptions(GlobalImportSettingsReference, SceneImportOptionsStaticMesh); SceneImportOptionsStaticMesh->FillStaticMeshInmportData(StaticMeshImportData, SceneImportOptions); UnFbx::FBXImportOptions* OverrideImportSettings = GetOptionsFromName(MeshInfo->OptionName); if (OverrideImportSettings != nullptr) { SFbxSceneOptionWindow::CopyFbxOptionsToFbxOptions(OverrideImportSettings, GlobalImportSettings); SFbxSceneOptionWindow::CopyFbxOptionsToStaticMeshOptions(OverrideImportSettings, SceneImportOptionsStaticMesh); } else { SFbxSceneOptionWindow::CopyFbxOptionsToFbxOptions(GlobalImportSettingsReference, GlobalImportSettings); SFbxSceneOptionWindow::CopyFbxOptionsToStaticMeshOptions(GlobalImportSettingsReference, SceneImportOptionsStaticMesh); } SceneImportOptionsStaticMesh->FillStaticMeshInmportData(StaticMeshImportData, SceneImportOptions); FbxImporter->ApplyTransformSettingsToFbxNode(FbxImporter->Scene->GetRootNode(), StaticMeshImportData); const TArray<UAssetUserData*>* UserData = Mesh->GetAssetUserDataArray(); TArray<UAssetUserData*> UserDataCopy; if (UserData) { for (int32 Idx = 0; Idx < UserData->Num(); Idx++) { UserDataCopy.Add((UAssetUserData*)StaticDuplicateObject((*UserData)[Idx], GetTransientPackage())); } } // preserve settings in navcollision subobject UNavCollision* NavCollision = Mesh->NavCollision ? (UNavCollision*)StaticDuplicateObject(Mesh->NavCollision, GetTransientPackage()) : nullptr; // preserve extended bound settings const FVector PositiveBoundsExtension = Mesh->PositiveBoundsExtension; const FVector NegativeBoundsExtension = Mesh->NegativeBoundsExtension; Mesh = FbxImporter->ReimportSceneStaticMesh(MeshInfo->UniqueId, Mesh, StaticMeshImportData); if (Mesh != nullptr) { //Put back the new mesh data since the reimport is putting back the original import data SceneImportOptionsStaticMesh->FillStaticMeshInmportData(StaticMeshImportData, SceneImportOptions); Mesh->AssetImportData = StaticMeshImportData; // Copy user data to newly created mesh for (int32 Idx = 0; Idx < UserDataCopy.Num(); Idx++) { UserDataCopy[Idx]->Rename(nullptr, Mesh, REN_DontCreateRedirectors | REN_DoNotDirty); Mesh->AddAssetUserData(UserDataCopy[Idx]); } if (NavCollision) { Mesh->NavCollision = NavCollision; NavCollision->Rename(nullptr, Mesh, REN_DontCreateRedirectors | REN_DoNotDirty); } // Restore bounds extension settings Mesh->PositiveBoundsExtension = PositiveBoundsExtension; Mesh->NegativeBoundsExtension = NegativeBoundsExtension; Mesh->AssetImportData->Update(FbxImportFileName); // Try to find the outer package so we can dirty it up if (Mesh->GetOutermost()) { Mesh->GetOutermost()->MarkPackageDirty(); } else { Mesh->MarkPackageDirty(); } AllNewAssets.Add(MeshInfo, Mesh); AssetToSyncContentBrowser.Add(Mesh); } else { return EReimportResult::Failed; } return EReimportResult::Succeeded; }
void UJanusExporterTool::Export() { TArray<UObject*> ObjectsToExport; FString Root = FString(ExportPath); // copy so we dont mess with the original reference FString Index = "<html>\n\t<head>\n\t\t<title>Unreal Export</title>\n\t</head>\n\t<body>\n\t\t<FireBoxRoom>\n\t\t\t<Assets>"; TArray<AActor*> ActorsExported; TArray<UStaticMesh*> StaticMeshesExp; TArray<FString> TexturesExp; TArray<FString> MaterialsExported; for (TObjectIterator<AActor> Itr; Itr; ++Itr) { AActor *Actor = *Itr; FString Name = Actor->GetName(); /*if (!Name.StartsWith("SM_Floor_R")) { continue; }*/ if (Actor->IsHiddenEd()) { continue; } ActorsExported.Add(Actor); TArray<UStaticMeshComponent*> StaticMeshes; Actor->GetComponents<UStaticMeshComponent>(StaticMeshes); for (int32 i = 0; i < StaticMeshes.Num(); i++) { UStaticMeshComponent* Component = StaticMeshes[i]; UStaticMesh *Mesh = Component->StaticMesh; if (!Mesh) { continue; } if (Component->LODData.Num() > 0) //if (false) { FStaticMeshComponentLODInfo* LODInfo = &Component->LODData[0]; FLightMap* LightMap = LODInfo->LightMap; FShadowMap* ShadowMap = LODInfo->ShadowMap; if (LightMap != NULL) { FLightMap2D* LightMap2D = LightMap->GetLightMap2D(); UTexture2D* Texture = LightMap2D->GetTexture(0); // 0 = HQ LightMap FString TexName = Texture->GetName(); if (TexturesExp.Contains(TexName)) { continue; } TexturesExp.Add(TexName); ExportPNG(Texture, Root); } if (ShadowMap != NULL) { FShadowMap2D* ShadowMap2D = ShadowMap->GetShadowMap2D(); UShadowMapTexture2D* ShadowTex = ShadowMap2D->GetTexture(); FString TexName = ShadowTex->GetName(); if (TexturesExp.Contains(TexName)) { continue; } TexturesExp.Add(TexName); ExportPNG(ShadowTex, Root); } } if (!StaticMeshesExp.Contains(Mesh)) { StaticMeshesExp.Add(Mesh); ExportFBX(Mesh, Root); } TArray<UMaterialInterface*> Materials = Component->GetMaterials(); for (int32 j = 0; j < Materials.Num(); j++) { UMaterialInterface* Material = Materials[j]; if (!Material) { continue; } FString MatName = Material->GetName(); if (MaterialsExported.Contains(MatName)) { continue; } MaterialsExported.Add(MatName); ExportMaterial(Root, Material, &TexturesExp); } } } // Models before textures so we can start showing the scene faster (textures take too long to load) for (int32 i = 0; i < StaticMeshesExp.Num(); i++) { UStaticMesh *mesh = StaticMeshesExp[i]; Index.Append("\n\t\t\t\t<AssetObject id=\"" + mesh->GetName() + "\" src=\"" + mesh->GetName() + ".fbx\" />"); } for (int32 i = 0; i < TexturesExp.Num(); i++) { FString Path = TexturesExp[i]; Index.Append("\n\t\t\t\t<AssetImage id=\"" + Path + "\" src=\"" + Path + ".png\" />"); } Index.Append("\n\t\t\t</Assets>\n\t\t\t<Room>"); for (int32 i = 0; i < ActorsExported.Num(); i++) { AActor *Actor = ActorsExported[i]; TArray<UStaticMeshComponent*> StaticMeshes; Actor->GetComponents<UStaticMeshComponent>(StaticMeshes); for (int32 i = 0; i < StaticMeshes.Num(); i++) { UStaticMeshComponent* Component = StaticMeshes[i]; UStaticMesh *Mesh = Component->StaticMesh; if (!Mesh) { continue; } FString ImageID = ""; TArray<UMaterialInterface*> Materials = Component->GetMaterials(); for (int32 j = 0; j < Materials.Num(); j++) { UMaterialInterface* Material = Materials[j]; if (!Material) { continue; } ImageID = Material->GetName() + "_BaseColor"; break; } if (ImageID == "") { Index.Append("\n\t\t\t\t<Object collision_id=\"" + Mesh->GetName() + "\" id=\"" + Mesh->GetName() + "\" lighting=\"true\" pos=\""); } else { Index.Append("\n\t\t\t\t<Object collision_id=\"" + Mesh->GetName() + "\" id=\"" + Mesh->GetName() + "\" image_id=\"" + ImageID + "\" lighting=\"true\" pos=\""); } FRotator Rot = Actor->GetActorRotation(); FVector XDir = Rot.RotateVector(FVector::RightVector); FVector YDir = Rot.RotateVector(FVector::UpVector); FVector ZDir = Rot.RotateVector(FVector::ForwardVector); FVector Pos = Actor->GetActorLocation() * UniformScale; FVector Sca = Actor->GetActorScale(); Pos = ChangeSpace(Pos); Sca = ChangeSpaceScalar(Sca) * UniformScale; XDir = ChangeSpace(XDir); YDir = ChangeSpace(YDir); ZDir = ChangeSpace(ZDir); FVector Sign = GetSignVector(Sca); Index.Append(FString::SanitizeFloat(Pos.X) + " " + FString::SanitizeFloat(Pos.Y) + " " + FString::SanitizeFloat(Pos.Z)); if (Sca.X < 0 || Sca.Y < 0 || Sca.Z < 0) { Index.Append("\" cull_face=\"front"); } Index.Append("\" scale=\""); Index.Append(FString::SanitizeFloat(Sca.X) + " " + FString::SanitizeFloat(Sca.Y) + " " + FString::SanitizeFloat(Sca.Z)); Index.Append("\" xdir=\""); Index.Append(FString::SanitizeFloat(XDir.X) + " " + FString::SanitizeFloat(XDir.Y) + " " + FString::SanitizeFloat(XDir.Z)); Index.Append("\" ydir=\""); Index.Append(FString::SanitizeFloat(YDir.X) + " " + FString::SanitizeFloat(YDir.Y) + " " + FString::SanitizeFloat(YDir.Z)); Index.Append("\" zdir=\""); Index.Append(FString::SanitizeFloat(ZDir.X) + " " + FString::SanitizeFloat(ZDir.Y) + " " + FString::SanitizeFloat(ZDir.Z)); Index.Append("\" />"); } } Index.Append("\n\t\t\t</Room>\n\t\t</FireBoxRoom>\n\t</body>\n</html>"); FString IndexPath = FString(ExportPath).Append("index.html"); FFileHelper::SaveStringToFile(Index, *IndexPath); }
static void StaticMeshToSlateRenderData(const UStaticMesh& DataSource, TArray<FSlateMeshVertex>& OutSlateVerts, TArray<uint32>& OutIndexes, FVector2D& OutExtentMin, FVector2D& OutExtentMax ) { OutExtentMin = FVector2D(FLT_MAX, FLT_MAX); OutExtentMax = FVector2D(-FLT_MAX, -FLT_MAX); const FStaticMeshLODResources& LOD = DataSource.RenderData->LODResources[0]; const int32 NumSections = LOD.Sections.Num(); if (NumSections > 1) { UE_LOG(LogUMG, Warning, TEXT("StaticMesh %s has %d sections. SMeshWidget expects a static mesh with 1 section."), *DataSource.GetName(), NumSections); } else { // Populate Vertex Data { const uint32 NumVerts = LOD.PositionVertexBuffer.GetNumVertices(); OutSlateVerts.Empty(); OutSlateVerts.Reserve(NumVerts); static const int32 MAX_SUPPORTED_UV_SETS = 6; const int32 TexCoordsPerVertex = LOD.GetNumTexCoords(); if (TexCoordsPerVertex > MAX_SUPPORTED_UV_SETS) { UE_LOG(LogStaticMesh, Warning, TEXT("[%s] has %d UV sets; slate vertex data supports at most %d"), *DataSource.GetName(), TexCoordsPerVertex, MAX_SUPPORTED_UV_SETS); } for (uint32 i = 0; i < NumVerts; ++i) { // Copy Position const FVector& Position = LOD.PositionVertexBuffer.VertexPosition(i); OutExtentMin.X = FMath::Min(Position.X, OutExtentMin.X); OutExtentMin.Y = FMath::Min(Position.Y, OutExtentMin.Y); OutExtentMax.X = FMath::Max(Position.X, OutExtentMax.X); OutExtentMax.Y = FMath::Max(Position.Y, OutExtentMax.Y); // Copy Color FColor Color = (LOD.ColorVertexBuffer.GetNumVertices() > 0) ? LOD.ColorVertexBuffer.VertexColor(i) : FColor::White; // Copy all the UVs that we have, and as many as we can fit. const FVector2D& UV0 = (TexCoordsPerVertex > 0) ? LOD.VertexBuffer.GetVertexUV(i, 0) : FVector2D(1, 1); const FVector2D& UV1 = (TexCoordsPerVertex > 1) ? LOD.VertexBuffer.GetVertexUV(i, 1) : FVector2D(1, 1); const FVector2D& UV2 = (TexCoordsPerVertex > 2) ? LOD.VertexBuffer.GetVertexUV(i, 2) : FVector2D(1, 1); const FVector2D& UV3 = (TexCoordsPerVertex > 3) ? LOD.VertexBuffer.GetVertexUV(i, 3) : FVector2D(1, 1); const FVector2D& UV4 = (TexCoordsPerVertex > 4) ? LOD.VertexBuffer.GetVertexUV(i, 4) : FVector2D(1, 1); const FVector2D& UV5 = (TexCoordsPerVertex > 5) ? LOD.VertexBuffer.GetVertexUV(i, 5) : FVector2D(1, 1); OutSlateVerts.Add(FSlateMeshVertex( FVector2D(Position.X, Position.Y), Color, UV0, UV1, UV2, UV3, UV4, UV5 )); } } // Populate Index data { FIndexArrayView SourceIndexes = LOD.IndexBuffer.GetArrayView(); const int32 NumIndexes = SourceIndexes.Num(); OutIndexes.Empty(); OutIndexes.Reserve(NumIndexes); for (int32 i = 0; i < NumIndexes; ++i) { OutIndexes.Add(SourceIndexes[i]); } // Sort the index buffer such that verts are drawn in Z-order. // Assume that all triangles are coplanar with Z == SomeValue. ensure(NumIndexes % 3 == 0); for (int32 a = 0; a < NumIndexes; a += 3) { for (int32 b = 0; b < NumIndexes; b += 3) { const float VertADepth = LOD.PositionVertexBuffer.VertexPosition(OutIndexes[a]).Z; const float VertBDepth = LOD.PositionVertexBuffer.VertexPosition(OutIndexes[b]).Z; if ( VertADepth < VertBDepth ) { // Swap the order in which triangles will be drawn Swap(OutIndexes[a + 0], OutIndexes[b + 0]); Swap(OutIndexes[a + 1], OutIndexes[b + 1]); Swap(OutIndexes[a + 2], OutIndexes[b + 2]); } } } } } }
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(); }