/** * 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; }
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); bool OutCanceled = false; if (ImportObject(Mesh->GetClass(), Mesh->GetOuter(), *Mesh->GetName(), RF_Public | RF_Standalone, Filename, nullptr, OutCanceled) != nullptr) { UE_LOG(LogEditorFactories, Log, TEXT("-- imported successfully")); Mesh->AssetImportData->Update(Filename); Mesh->MarkPackageDirty(); return EReimportResult::Succeeded; } else if (OutCanceled) { UE_LOG(LogEditorFactories, Warning, TEXT("-- import canceled")); } else { UE_LOG(LogEditorFactories, Warning, TEXT("-- import failed")); } #endif // #if WITH_SPEEDTREE return EReimportResult::Failed; }
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; }
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(); }