UClass* FGraphNodeClassData::GetClass(bool bSilent) { UClass* RetClass = Class.Get(); if (RetClass == NULL && GeneratedClassPackage.Len()) { GWarn->BeginSlowTask(LOCTEXT("LoadPackage", "Loading Package..."), true); UPackage* Package = LoadPackage(NULL, *GeneratedClassPackage, LOAD_NoRedirects); if (Package) { Package->FullyLoad(); UObject* Object = FindObject<UObject>(Package, *AssetName); GWarn->EndSlowTask(); UBlueprint* BlueprintOb = Cast<UBlueprint>(Object); RetClass = BlueprintOb ? *BlueprintOb->GeneratedClass : Object ? Object->GetClass() : NULL; Class = RetClass; } else { GWarn->EndSlowTask(); if (!bSilent) { FMessageLog EditorErrors("EditorErrors"); EditorErrors.Error(LOCTEXT("PackageLoadFail", "Package Load Failed")); EditorErrors.Info(FText::FromString(GeneratedClassPackage)); EditorErrors.Notify(LOCTEXT("PackageLoadFail", "Package Load Failed")); } } } return RetClass; }
/** * Imports an object using a given factory * * @param ImportFactory - The factory to use to import the object * @param ObjectName - The name of the object to create * @param PackagePath - The full path of the package file to create * @param ImportPath - The path to the object to import */ UObject* ImportAssetUsingFactory(UFactory* ImportFactory, const FString& ObjectName, const FString& PackagePath, const FString& ImportPath) { UObject* ImportedAsset = NULL; UPackage* Pkg = CreatePackage(NULL, *PackagePath); if (Pkg) { // Make sure the destination package is loaded Pkg->FullyLoad(); UClass* ImportAssetType = ImportFactory->ResolveSupportedClass(); bool bDummy = false; //If we are a texture factory suppress some warning dialog that we don't want if (ImportFactory->IsA(UTextureFactory::StaticClass())) { UTextureFactory::SuppressImportOverwriteDialog(); } ImportedAsset = UFactory::StaticImportObject(ImportAssetType, Pkg, FName(*ObjectName), RF_Public | RF_Standalone, bDummy, *ImportPath, NULL, ImportFactory, NULL, GWarn, 0); if (ImportedAsset) { UE_LOG(LogAutomationEditorCommon, Display, TEXT("Imported %s"), *ImportPath); } else { UE_LOG(LogAutomationEditorCommon, Error, TEXT("Failed to import asset using factory %s!"), *ImportFactory->GetName()); } } else { UE_LOG(LogAutomationEditorCommon, Error, TEXT("Failed to create a package!")); } return ImportedAsset; }
bool UParticleSystemAuditCommandlet::ProcessParticleSystems() { FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry")); IAssetRegistry& AssetRegistry = AssetRegistryModule.Get(); AssetRegistry.SearchAllAssets(true); TArray<FAssetData> AssetList; AssetRegistry.GetAssetsByClass(UParticleSystem::StaticClass()->GetFName(), AssetList); double StartProcessParticleSystemsTime = FPlatformTime::Seconds(); // Find all level placed particle systems with: // - Single LOD level // - No fixed bounds // - LODLevel Mismatch // - Kismet referenced & auto-activate set // Iterate over the list and check each system for *no* lod // const FString DevelopersFolder = FPackageName::FilenameToLongPackageName(FPaths::GameDevelopersDir().LeftChop(1)); FString LastPackageName = TEXT(""); int32 PackageSwitches = 0; UPackage* CurrentPackage = NULL; for (const FAssetData& AssetIt : AssetList) { const FString PSysName = AssetIt.ObjectPath.ToString(); const FString PackageName = AssetIt.PackageName.ToString(); if ( PackageName.StartsWith(DevelopersFolder) ) { // Skip developer folders continue; } if (PackageName != LastPackageName) { UPackage* Package = ::LoadPackage(NULL, *PackageName, LOAD_None); if (Package != NULL) { LastPackageName = PackageName; Package->FullyLoad(); CurrentPackage = Package; } else { UE_LOG(LogParticleSystemAuditCommandlet, Warning, TEXT("Failed to load package %s processing %s"), *PackageName, *PSysName); CurrentPackage = NULL; } } const FString ShorterPSysName = AssetIt.AssetName.ToString(); UParticleSystem* PSys = FindObject<UParticleSystem>(CurrentPackage, *ShorterPSysName); if (PSys != NULL) { bool bInvalidLOD = false; bool bSingleLOD = false; bool bFoundEmitter = false; bool bMissingMaterial = false; bool bHasHighSpawnRateOrBurst = false; bool bHasRibbonTrailOrBeam = false; for (int32 EmitterIdx = 0; EmitterIdx < PSys->Emitters.Num(); EmitterIdx++) { UParticleEmitter* Emitter = PSys->Emitters[EmitterIdx]; if (Emitter != NULL) { if (Emitter->LODLevels.Num() == 0) { bInvalidLOD = true; } else if (Emitter->LODLevels.Num() == 1) { bSingleLOD = true; } bFoundEmitter = true; for (int32 LODIdx = 0; LODIdx < Emitter->LODLevels.Num(); LODIdx++) { UParticleLODLevel* LODLevel = Emitter->LODLevels[LODIdx]; if (LODLevel != NULL) { if (LODLevel->RequiredModule != NULL) { if (LODLevel->RequiredModule->Material == NULL) { bMissingMaterial = true; } } if (Cast<UParticleModuleTypeDataRibbon>(LODLevel->TypeDataModule) || Cast<UParticleModuleTypeDataBeam2>(LODLevel->TypeDataModule) || Cast<UParticleModuleTypeDataAnimTrail>(LODLevel->TypeDataModule)) { bHasRibbonTrailOrBeam = true; } for (int32 ModuleIdx = 0; ModuleIdx < LODLevel->Modules.Num(); ModuleIdx++) { UParticleModule* Module = LODLevel->Modules[ModuleIdx]; if (UParticleModuleSpawn* SpawnModule = Cast<UParticleModuleSpawn>(Module)) { if ( !bHasHighSpawnRateOrBurst ) { if ( UDistributionFloatConstant* ConstantDistribution = Cast<UDistributionFloatConstant>(SpawnModule->Rate.Distribution) ) { if ( ConstantDistribution->Constant > HighSpawnRateOrBurstThreshold ) { bHasHighSpawnRateOrBurst = true; } } for ( const FParticleBurst& Burst : SpawnModule->BurstList ) { if ( Burst.Count > HighSpawnRateOrBurstThreshold ) { bHasHighSpawnRateOrBurst = true; } } } } } } } } } // Note all PSystems w/ a high constant spawn rate or burst count... if ( bHasHighSpawnRateOrBurst ) { ParticleSystemsWithHighSpawnRateOrBurst.Add(PSys->GetPathName()); } // Note all PSystems w/ a far LOD distance... for ( float LODDistance : PSys->LODDistances ) { if (LODDistance > FarLODDistanceTheshold) { ParticleSystemsWithFarLODDistance.Add(PSys->GetPathName()); break; } } // Note all PSystems w/ no emitters... if (PSys->Emitters.Num() == 0) { ParticleSystemsWithNoEmitters.Add(PSys->GetPathName()); } // Note all missing material case PSystems... if (bMissingMaterial == true) { ParticleSystemsWithMissingMaterials.Add(PSys->GetPathName()); } // Note all 0 LOD case PSystems... if (bInvalidLOD == true) { ParticleSystemsWithNoLODs.Add(PSys->GetPathName()); } // Note all single LOD case PSystems... if (bSingleLOD == true) { ParticleSystemsWithSingleLOD.Add(PSys->GetPathName()); } // Note all non-fixed bound PSystems, unless there is a ribbon, trail, or beam emitter... if (PSys->bUseFixedRelativeBoundingBox == false && !bHasRibbonTrailOrBeam) { ParticleSystemsWithoutFixedBounds.Add(PSys->GetPathName()); } // Note all bOrientZAxisTowardCamera systems if (PSys->bOrientZAxisTowardCamera == true) { ParticleSystemsWithOrientZAxisTowardCamera.Add(PSys->GetPathName()); } if ((PSys->LODMethod == PARTICLESYSTEMLODMETHOD_Automatic) && (bInvalidLOD == false) && (bSingleLOD == false) && (PSys->LODDistanceCheckTime == 0.0f)) { ParticleSystemsWithBadLODCheckTimes.Add(PSys->GetPathName()); } if (LastPackageName.Len() > 0) { if (LastPackageName != PSys->GetOutermost()->GetName()) { LastPackageName = PSys->GetOutermost()->GetName(); PackageSwitches++; } } else { LastPackageName = PSys->GetOutermost()->GetName(); } if (PackageSwitches > 10) { ::CollectGarbage(RF_Native); PackageSwitches = 0; } } else { UE_LOG(LogParticleSystemAuditCommandlet, Warning, TEXT("Failed to load particle system %s"), *PSysName); } } // Probably don't need to do this, but just in case we have any 'hanging' packages // and more processing steps are added later, let's clean up everything... ::CollectGarbage(RF_Native); double ProcessParticleSystemsTime = FPlatformTime::Seconds() - StartProcessParticleSystemsTime; UE_LOG(LogParticleSystemAuditCommandlet, Log, TEXT("Took %5.3f seconds to process referenced particle systems..."), ProcessParticleSystemsTime); return true; }
FReply SPropertyEditorClass::OnDrop(const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent) { TSharedPtr<FClassDragDropOp> ClassOperation = DragDropEvent.GetOperationAs<FClassDragDropOp>(); if (ClassOperation.IsValid()) { // We can only drop one item into the combo box, so drop the first one. FString AssetName = ClassOperation->ClassesToDrop[0]->GetName(); // Set the property, it will be verified as valid. SendToObjects(AssetName); return FReply::Handled(); } TSharedPtr<FUnloadedClassDragDropOp> UnloadedClassOp = DragDropEvent.GetOperationAs<FUnloadedClassDragDropOp>(); if (UnloadedClassOp.IsValid()) { // Check if the asset is loaded, used to see if the context menu should be available bool bAllAssetWereLoaded = true; TArray<FClassPackageData>& AssetArray = *(UnloadedClassOp->AssetsToDrop.Get()); // We can only drop one item into the combo box, so drop the first one. FString& AssetName = AssetArray[0].AssetName; // Check to see if the asset can be found, otherwise load it. UObject* Object = FindObject<UObject>(NULL, *AssetName); if(Object == NULL) { // Check to see if the dropped asset was a blueprint const FString& PackageName = AssetArray[0].GeneratedPackageName; Object = FindObject<UObject>(NULL, *FString::Printf(TEXT("%s.%s"), *PackageName, *AssetName)); if(Object == NULL) { // Load the package. GWarn->BeginSlowTask(LOCTEXT("OnDrop_LoadPackage", "Fully Loading Package For Drop"), true, false); UPackage* Package = LoadPackage(NULL, *PackageName, LOAD_NoRedirects ); if(Package) { Package->FullyLoad(); } GWarn->EndSlowTask(); Object = FindObject<UObject>(Package, *AssetName); } if(Object->IsA(UBlueprint::StaticClass())) { // Get the default object from the generated class. Object = Cast<UBlueprint>(Object)->GeneratedClass->GetDefaultObject(); } } // Set the property, it will be verified as valid. SendToObjects(AssetName); return FReply::Handled(); } return FReply::Unhandled(); }
bool EngineUtils::FindOrLoadAssetsByPath(const FString& Path, TArray<UObject*>& OutAssets) { if ( !FPackageName::IsValidLongPackageName(Path, true) ) { return false; } // Convert the package path to a filename with no extension (directory) const FString FilePath = FPackageName::LongPackageNameToFilename(Path); // Gather the package files in that directory and subdirectories TArray<FString> Filenames; FPackageName::FindPackagesInDirectory(Filenames, FilePath); // Cull out map files for (int32 FilenameIdx = Filenames.Num() - 1; FilenameIdx >= 0; --FilenameIdx) { const FString Extension = FPaths::GetExtension(Filenames[FilenameIdx], true); if ( Extension == FPackageName::GetMapPackageExtension() ) { Filenames.RemoveAt(FilenameIdx); } } // Load packages or find existing ones and fully load them TSet<UPackage*> Packages; for (int32 FileIdx = 0; FileIdx < Filenames.Num(); ++FileIdx) { const FString& Filename = Filenames[FileIdx]; UPackage* Package = FindPackage(NULL, *FPackageName::FilenameToLongPackageName(Filename)); if (Package) { Package->FullyLoad(); } else { Package = LoadPackage(NULL, *Filename, LOAD_None); } if (Package) { Packages.Add(Package); } } // If any packages were successfully loaded, find all assets that were in the packages and add them to OutAssets if ( Packages.Num() > 0 ) { for (FObjectIterator ObjIt; ObjIt; ++ObjIt) { if ( Packages.Contains(ObjIt->GetOutermost()) && ObjIt->IsAsset() ) { OutAssets.Add(*ObjIt); } } } return true; }
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; }
UBlueprint *UReimportFbxSceneFactory::UpdateOriginalBluePrint(FString &BluePrintFullName, void* VoidNodeStatusMapPtr, TSharedPtr<FFbxSceneInfo> SceneInfoPtr, TSharedPtr<FFbxSceneInfo> SceneInfoOriginalPtr, TArray<FAssetData> &AssetDataToDelete) { if (!SceneInfoPtr.IsValid() || VoidNodeStatusMapPtr == nullptr || !SceneInfoOriginalPtr.IsValid() || BluePrintFullName.IsEmpty()) { return nullptr; } FbxSceneReimportStatusMapPtr NodeStatusMapPtr = (FbxSceneReimportStatusMapPtr)VoidNodeStatusMapPtr; //Find the BluePrint FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry"); FAssetData BlueprintAssetData = AssetRegistryModule.Get().GetAssetByObjectPath(FName(*(BluePrintFullName))); UPackage* PkgExist = LoadPackage(nullptr, *BlueprintAssetData.PackageName.ToString(), LOAD_Verify | LOAD_NoWarn); if (PkgExist == nullptr) { return nullptr; } //Load the package before searching the asset PkgExist->FullyLoad(); UBlueprint* BluePrint = FindObjectSafe<UBlueprint>(ANY_PACKAGE, *BluePrintFullName); if (BluePrint == nullptr) { return nullptr; } //Close all editor that edit this blueprint FAssetEditorManager::Get().CloseAllEditorsForAsset(BluePrint); //Set the import status for the next reimport for (TSharedPtr<FFbxNodeInfo> NodeInfo : SceneInfoPtr->HierarchyInfo) { if (!NodeStatusMapPtr->Contains(NodeInfo->NodeHierarchyPath)) continue; EFbxSceneReimportStatusFlags NodeStatus = *(NodeStatusMapPtr->Find(NodeInfo->NodeHierarchyPath)); NodeInfo->bImportNode = (NodeStatus & EFbxSceneReimportStatusFlags::ReimportAsset) != EFbxSceneReimportStatusFlags::None; } //Add back the component that was in delete state but no flag for reimport for (TSharedPtr<FFbxNodeInfo> OriginalNodeInfo : SceneInfoOriginalPtr->HierarchyInfo) { if (!NodeStatusMapPtr->Contains(OriginalNodeInfo->NodeHierarchyPath)) { continue; } EFbxSceneReimportStatusFlags NodeStatus = *(NodeStatusMapPtr->Find(OriginalNodeInfo->NodeHierarchyPath)); if (OriginalNodeInfo->bImportNode != true || (NodeStatus & EFbxSceneReimportStatusFlags::ReimportAsset) != EFbxSceneReimportStatusFlags::None) { continue; } //Clear the child OriginalNodeInfo->Childrens.Empty(); //hook the node to the new hierarchy parent bool bFoundParent = false; if (OriginalNodeInfo->ParentNodeInfo.IsValid()) { int32 InsertIndex = 0; for (TSharedPtr<FFbxNodeInfo> NodeInfo : SceneInfoPtr->HierarchyInfo) { InsertIndex++; if (NodeInfo->bImportNode && NodeInfo->NodeHierarchyPath.Compare(OriginalNodeInfo->ParentNodeInfo->NodeHierarchyPath) == 0) { OriginalNodeInfo->ParentNodeInfo = NodeInfo; NodeInfo->Childrens.Add(OriginalNodeInfo); SceneInfoPtr->HierarchyInfo.Insert(OriginalNodeInfo, InsertIndex); bFoundParent = true; break; } } } if (!bFoundParent) { //Insert after the root node OriginalNodeInfo->ParentNodeInfo = nullptr; SceneInfoPtr->HierarchyInfo.Insert(OriginalNodeInfo, 1); } } //Create a brand new actor with the correct component hierarchy then replace the existing blueprint //This function is using the bImportNode flag not the EFbxSceneReimportStatusFlags AActor *HierarchyActor = CreateActorComponentsHierarchy(SceneInfoPtr); if (HierarchyActor != nullptr) { //Modify the current blueprint to reflect the new actor //Clear all nodes by removing all root node TArray<USCS_Node*> BluePrintRootNodes = BluePrint->SimpleConstructionScript->GetRootNodes(); for(USCS_Node* RootNode : BluePrintRootNodes) { RemoveChildNodeRecursively(BluePrint->SimpleConstructionScript, RootNode); } //Create the new nodes from the hierarchy actor FKismetEditorUtilities::AddComponentsToBlueprint(BluePrint, HierarchyActor->GetInstanceComponents()); //Cleanup the temporary actor HierarchyActor->Destroy(); BluePrint->Modify(); BluePrint->PostEditChange(); AssetToSyncContentBrowser.Add(BluePrint); return BluePrint; } return nullptr; }
EReimportResult::Type UReimportFbxSceneFactory::Reimport(UObject* Obj) { ReimportData = GetFbxSceneImportData(Obj); if (!ReimportData) { return EReimportResult::Failed; } //We will call other factory store the filename value since UFactory::CurrentFilename is static FbxImportFileName = ReimportData->SourceFbxFile; UnFbx::FFbxImporter* FbxImporter = UnFbx::FFbxImporter::GetInstance(); UnFbx::FFbxLoggerSetter Logger(FbxImporter); GWarn->BeginSlowTask(NSLOCTEXT("FbxSceneReImportFactory", "BeginReImportingFbxSceneTask", "ReImporting FBX scene"), true); GlobalImportSettings = FbxImporter->GetImportOptions(); //Fill the original options for (auto kvp : ReimportData->NameOptionsMap) { if (kvp.Key.Compare(DefaultOptionName) == 0) { SFbxSceneOptionWindow::CopyFbxOptionsToFbxOptions(kvp.Value, GlobalImportSettings); NameOptionsMap.Add(kvp.Key, GlobalImportSettings); } else { NameOptionsMap.Add(kvp.Key, kvp.Value); } } //Always convert the scene GlobalImportSettings->bConvertScene = true; GlobalImportSettings->bImportScene = ReimportData->bImportScene; //Read the fbx and store the hierarchy's information so we can reuse it after importing all the model in the fbx file if (!FbxImporter->ImportFromFile(*FbxImportFileName, FPaths::GetExtension(FbxImportFileName))) { // Log the error message and fail the import. GWarn->Log(ELogVerbosity::Error, FbxImporter->GetErrorMessage()); FbxImporter->ReleaseScene(); FbxImporter = nullptr; GWarn->EndSlowTask(); return EReimportResult::Failed; } FString PackageName = ""; Obj->GetOutermost()->GetName(PackageName); Path = FPaths::GetPath(PackageName); UnFbx::FbxSceneInfo SceneInfo; //Read the scene and found all instance with their scene information. FbxImporter->GetSceneInfo(FbxImportFileName, SceneInfo); //Convert old structure to the new scene export structure TSharedPtr<FFbxSceneInfo> SceneInfoPtr = ConvertSceneInfo(&SceneInfo); //Get import material info ExtractMaterialInfo(FbxImporter, SceneInfoPtr); if (!ReimportData->bCreateFolderHierarchy) { for (TSharedPtr<FFbxMeshInfo> MeshInfo : SceneInfoPtr->MeshInfo) { FString AssetName = Path + TEXT("/") + MeshInfo->Name; MeshInfo->SetOriginalImportPath(AssetName); FString OriginalFullImportName = PackageTools::SanitizePackageName(AssetName); OriginalFullImportName = OriginalFullImportName + TEXT(".") + PackageTools::SanitizePackageName(MeshInfo->Name); MeshInfo->SetOriginalFullImportName(OriginalFullImportName); } } else { TSet<uint64> AssetPathDone; FString AssetPath = Path; for (TSharedPtr<FFbxNodeInfo> NodeInfo : SceneInfoPtr->HierarchyInfo) { //Iterate the hierarchy and build the original path RecursivelyCreateOriginalPath(FbxImporter, NodeInfo, AssetPath, AssetPathDone); } } FillSceneHierarchyPath(SceneInfoPtr); FbxSceneReimportStatusMap MeshStatusMap; FbxSceneReimportStatusMap NodeStatusMap; bool bCanReimportHierarchy = ReimportData->HierarchyType == (int32)EFBXSceneOptionsCreateHierarchyType::FBXSOCHT_CreateBlueprint && !ReimportData->BluePrintFullName.IsEmpty(); if (!GetFbxSceneReImportOptions(FbxImporter , SceneInfoPtr , ReimportData->SceneInfoSourceData , GlobalImportSettings , SceneImportOptions , SceneImportOptionsStaticMesh , NameOptionsMap , MeshStatusMap , NodeStatusMap , bCanReimportHierarchy , Path)) { //User cancel the scene import FbxImporter->ReleaseScene(); FbxImporter = nullptr; GlobalImportSettings = nullptr; GWarn->EndSlowTask(); return EReimportResult::Cancelled; } GlobalImportSettingsReference = new UnFbx::FBXImportOptions(); SFbxSceneOptionWindow::CopyFbxOptionsToFbxOptions(GlobalImportSettings, GlobalImportSettingsReference); //Overwrite the reimport asset data with the new data ReimportData->SceneInfoSourceData = SceneInfoPtr; ReimportData->SourceFbxFile = FPaths::ConvertRelativePathToFull(FbxImportFileName); ReimportData->bImportScene = GlobalImportSettingsReference->bImportScene; //Copy the options map ReimportData->NameOptionsMap.Reset(); for (auto kvp : NameOptionsMap) { ReimportData->NameOptionsMap.Add(kvp.Key, kvp.Value); } FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry"); TArray<FAssetData> AssetDataToDelete; for (TSharedPtr<FFbxMeshInfo> MeshInfo : SceneInfoPtr->MeshInfo) { //Delete all the delete asset if (!MeshStatusMap.Contains(MeshInfo->OriginalImportPath)) { continue; } EFbxSceneReimportStatusFlags MeshStatus = *(MeshStatusMap.Find(MeshInfo->OriginalImportPath)); if ((MeshStatus & EFbxSceneReimportStatusFlags::Removed) == EFbxSceneReimportStatusFlags::None || (MeshStatus & EFbxSceneReimportStatusFlags::ReimportAsset) == EFbxSceneReimportStatusFlags::None) { continue; } //Make sure we load all package that will be deleted UPackage* PkgExist = LoadPackage(nullptr, *(MeshInfo->GetImportPath()), LOAD_Verify | LOAD_NoWarn); if (PkgExist == nullptr) { continue; } PkgExist->FullyLoad(); //Find the asset AssetDataToDelete.Add(AssetRegistryModule.Get().GetAssetByObjectPath(FName(*(MeshInfo->GetFullImportName())))); } AllNewAssets.Empty(); AssetToSyncContentBrowser.Empty(); EReimportResult::Type ReimportResult = EReimportResult::Succeeded; //Reimport and add asset for (TSharedPtr<FFbxMeshInfo> MeshInfo : SceneInfoPtr->MeshInfo) { if (!MeshStatusMap.Contains(MeshInfo->OriginalImportPath)) { continue; } EFbxSceneReimportStatusFlags MeshStatus = *(MeshStatusMap.Find(MeshInfo->OriginalImportPath)); //Set the import status for the next reimport MeshInfo->bImportAttribute = (MeshStatus & EFbxSceneReimportStatusFlags::ReimportAsset) != EFbxSceneReimportStatusFlags::None; if ((MeshStatus & EFbxSceneReimportStatusFlags::Removed) != EFbxSceneReimportStatusFlags::None || (MeshStatus & EFbxSceneReimportStatusFlags::ReimportAsset) == EFbxSceneReimportStatusFlags::None) { continue; } if (((MeshStatus & EFbxSceneReimportStatusFlags::Same) != EFbxSceneReimportStatusFlags::None || (MeshStatus & EFbxSceneReimportStatusFlags::Added) != EFbxSceneReimportStatusFlags::None) && (MeshStatus & EFbxSceneReimportStatusFlags::FoundContentBrowserAsset) != EFbxSceneReimportStatusFlags::None) { //Reimport over the old asset if (!MeshInfo->bIsSkelMesh) { ReimportResult = ReimportStaticMesh(FbxImporter, MeshInfo); } else { //TODO reimport skeletal mesh } } else if ((MeshStatus & EFbxSceneReimportStatusFlags::Added) != EFbxSceneReimportStatusFlags::None || (MeshStatus & EFbxSceneReimportStatusFlags::Same) != EFbxSceneReimportStatusFlags::None) { //Create a package for this node //Get Parent hierarchy name to create new package path ReimportResult = ImportStaticMesh(FbxImporter, MeshInfo, SceneInfoPtr); } } //Put back the default option in the static mesh import data, so next import will have those last import option SFbxSceneOptionWindow::CopyFbxOptionsToFbxOptions(GlobalImportSettingsReference, GlobalImportSettings); SFbxSceneOptionWindow::CopyFbxOptionsToStaticMeshOptions(GlobalImportSettingsReference, SceneImportOptionsStaticMesh); SceneImportOptionsStaticMesh->FillStaticMeshInmportData(StaticMeshImportData, SceneImportOptions); StaticMeshImportData->SaveConfig(); //Update the blueprint UBlueprint *ReimportBlueprint = nullptr; if (bCanReimportHierarchy && GlobalImportSettingsReference->bImportScene) { ReimportBlueprint = UpdateOriginalBluePrint(ReimportData->BluePrintFullName, &NodeStatusMap, SceneInfoPtr, ReimportData->SceneInfoSourceData, AssetDataToDelete); } //Remove the deleted meshinfo node from the reimport data TArray<TSharedPtr<FFbxMeshInfo>> ToRemoveHierarchyNode; for (TSharedPtr<FFbxMeshInfo> MeshInfo : ReimportData->SceneInfoSourceData->MeshInfo) { EFbxSceneReimportStatusFlags MeshStatus = *(MeshStatusMap.Find(MeshInfo->OriginalImportPath)); if ((MeshStatus & EFbxSceneReimportStatusFlags::Removed) != EFbxSceneReimportStatusFlags::None) { ToRemoveHierarchyNode.Add(MeshInfo); } } for (TSharedPtr<FFbxMeshInfo> MeshInfo : ToRemoveHierarchyNode) { ReimportData->SceneInfoSourceData->MeshInfo.Remove(MeshInfo); } ReimportData->Modify(); ReimportData->PostEditChange(); //Make sure the content browser is in sync before we delete FContentBrowserModule& ContentBrowserModule = FModuleManager::Get().LoadModuleChecked<FContentBrowserModule>("ContentBrowser"); ContentBrowserModule.Get().SyncBrowserToAssets(AssetToSyncContentBrowser); if (AssetDataToDelete.Num() > 0) { bool AbortDelete = false; if (ReimportBlueprint != nullptr) { //Save the blueprint to avoid reference from the old blueprint FAssetData ReimportBlueprintAsset(ReimportBlueprint); TArray<UPackage*> Packages; Packages.Add(ReimportBlueprintAsset.GetPackage()); FEditorFileUtils::PromptForCheckoutAndSave(Packages, false, false); //Make sure the Asset registry is up to date after the save TArray<FString> Paths; Paths.Add(ReimportBlueprintAsset.PackagePath.ToString()); AssetRegistryModule.Get().ScanPathsSynchronous(Paths, true); } if (!AbortDelete) { //Delete the asset and use the normal dialog to make sure the user understand he will remove some content //The user can decide to cancel the delete or not. This will not interrupt the reimport process //The delete is done at the end because we want to remove the blueprint reference before deleting object ObjectTools::DeleteAssets(AssetDataToDelete, true); } } //Make sure the content browser is in sync ContentBrowserModule.Get().SyncBrowserToAssets(AssetToSyncContentBrowser); AllNewAssets.Empty(); GlobalImportSettings = nullptr; GlobalImportSettingsReference = nullptr; FbxImporter->ReleaseScene(); FbxImporter = nullptr; GWarn->EndSlowTask(); return EReimportResult::Succeeded; }
void FContentDirectoryMonitor::ProcessAdditions(const DirectoryWatcher::FTimeLimit& TimeLimit, TArray<UPackage*>& OutPackagesToSave, const TMap<FString, TArray<UFactory*>>& InFactoriesByExtension, FReimportFeedbackContext& Context) { bool bCancelled = false; for (int32 Index = 0; Index < AddedFiles.Num(); ++Index) { auto& Addition = AddedFiles[Index]; if (bCancelled) { // Just update the cache immediately if the user cancelled Cache.CompleteTransaction(MoveTemp(Addition)); Context.MainTask->EnterProgressFrame(); continue; } const FString FullFilename = Cache.GetDirectory() + Addition.Filename.Get(); FString NewAssetName = ObjectTools::SanitizeObjectName(FPaths::GetBaseFilename(FullFilename)); FString PackagePath = PackageTools::SanitizePackageName(MountedContentPath / FPaths::GetPath(Addition.Filename.Get()) / NewAssetName); // Don't create assets for new files if assets already exist for the filename auto ExistingReferences = Utils::FindAssetsPertainingToFile(*Registry, FullFilename); if (ExistingReferences.Num() != 0) { // Treat this as a modified file that will attempt to reimport it (if applicable). We don't update the progress for this item until it is processed by ProcessModifications ModifiedFiles.Add(MoveTemp(Addition)); continue; } // Move the progress on now that we know we're going to process the file Context.MainTask->EnterProgressFrame(); if (FPackageName::DoesPackageExist(*PackagePath)) { // Package already exists, so try and import over the top of it, if it doesn't already have a source file path TArray<FAssetData> Assets; if (Registry->GetAssetsByPackageName(*PackagePath, Assets) && Assets.Num() == 1) { if (UObject* ExistingAsset = Assets[0].GetAsset()) { // We're only eligible for reimport if the existing asset doesn't reference a source file already const bool bEligibleForReimport = !Utils::ExtractSourceFilePaths(ExistingAsset).ContainsByPredicate([&](const FString& In){ return !In.IsEmpty() && In == FullFilename; }); if (bEligibleForReimport) { ReimportAssetWithNewSource(ExistingAsset, FullFilename, OutPackagesToSave, Context); } } } } else { UPackage* NewPackage = CreatePackage(nullptr, *PackagePath); if ( !ensure(NewPackage) ) { Context.AddMessage(EMessageSeverity::Error, FText::Format(LOCTEXT("Error_FailedToCreateAsset", "Failed to create new asset ({0}) for file ({1})."), FText::FromString(NewAssetName), FText::FromString(FullFilename))); } else { Context.AddMessage(EMessageSeverity::Info, FText::Format(LOCTEXT("Info_CreatingNewAsset", "Importing new asset {0}."), FText::FromString(PackagePath))); // Make sure the destination package is loaded NewPackage->FullyLoad(); UObject* NewAsset = nullptr; // Find a relevant factory for this file // @todo import: gmp: show dialog in case of multiple matching factories const FString Ext = FPaths::GetExtension(Addition.Filename.Get(), false); auto* Factories = InFactoriesByExtension.Find(Ext); if (Factories && Factories->Num() != 0) { // Prefer a factory if it explicitly can import. UFactory::FactoryCanImport returns false by default, even if the factory supports the extension, so we can't use it directly. UFactory* const * PreferredFactory = Factories->FindByPredicate([&](UFactory* F){ return F->FactoryCanImport(FullFilename); }); if (PreferredFactory) { NewAsset = AttemptImport((*PreferredFactory)->GetClass(), NewPackage, *NewAssetName, bCancelled, FullFilename); } // If there was no preferred factory, just try them all until one succeeds else for (UFactory* Factory : *Factories) { NewAsset = AttemptImport(Factory->GetClass(), NewPackage, *NewAssetName, bCancelled, FullFilename); if (bCancelled || NewAsset) { break; } } } // If we didn't create an asset, unload and delete the package we just created if (!NewAsset) { TArray<UPackage*> Packages; Packages.Add(NewPackage); TGuardValue<bool> SuppressSlowTaskMessages(Context.bSuppressSlowTaskMessages, true); FText ErrorMessage; if (!PackageTools::UnloadPackages(Packages, ErrorMessage)) { Context.AddMessage(EMessageSeverity::Error, FText::Format(LOCTEXT("Error_UnloadingPackage", "There was an error unloading a package: {0}."), ErrorMessage)); } // Just add the message to the message log rather than add it to the UI // Factories may opt not to import the file, so we let them report errors if they do Context.GetMessageLog().Message(EMessageSeverity::Info, FText::Format(LOCTEXT("Info_FailedToImportAsset", "Failed to import file {0}."), FText::FromString(FullFilename))); } else if (!bCancelled) { FAssetRegistryModule::AssetCreated(NewAsset); GEditor->BroadcastObjectReimported(NewAsset); OutPackagesToSave.Add(NewPackage); } // Refresh the supported class. Some factories (e.g. FBX) only resolve their type after reading the file // ImportAssetType = Factory->ResolveSupportedClass(); // @todo: analytics? } } // Let the cache know that we've dealt with this change (it will be imported immediately) Cache.CompleteTransaction(MoveTemp(Addition)); if (!bCancelled && TimeLimit.Exceeded()) { // Remove the ones we've processed AddedFiles.RemoveAt(0, Index + 1); return; } } AddedFiles.Empty(); }