UObject* UFbxFactory::FactoryCreateBinary ( UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, const TCHAR* Type, const uint8*& Buffer, const uint8* BufferEnd, FFeedbackContext* Warn, bool& bOutOperationCanceled ) { if( bOperationCanceled ) { bOutOperationCanceled = true; FEditorDelegates::OnAssetPostImport.Broadcast(this, NULL); return NULL; } FEditorDelegates::OnAssetPreImport.Broadcast(this, Class, InParent, Name, Type); UObject* NewObject = NULL; if ( bDetectImportTypeOnImport ) { if ( !DetectImportType(UFactory::CurrentFilename) ) { // Failed to read the file info, fail the import FEditorDelegates::OnAssetPostImport.Broadcast(this, NULL); return NULL; } } // logger for all error/warnings // this one prints all messages that are stored in FFbxImporter UnFbx::FFbxImporter* FbxImporter = UnFbx::FFbxImporter::GetInstance(); UnFbx::FFbxLoggerSetter Logger(FbxImporter); EFBXImportType ForcedImportType = FBXIT_StaticMesh; bool bIsObjFormat = false; if( FString(Type).Equals(TEXT("obj"), ESearchCase::IgnoreCase ) ) { bIsObjFormat = true; } bool bShowImportDialog = bShowOption && !GIsAutomationTesting; bool bImportAll = false; UnFbx::FBXImportOptions* ImportOptions = GetImportOptions(FbxImporter, ImportUI, bShowImportDialog, InParent->GetPathName(), bOperationCanceled, bImportAll, bIsObjFormat, bIsObjFormat, ForcedImportType ); bOutOperationCanceled = bOperationCanceled; if( bImportAll ) { // If the user chose to import all, we don't show the dialog again and use the same settings for each object until importing another set of files bShowOption = false; } // For multiple files, use the same settings bDetectImportTypeOnImport = false; if (ImportOptions) { Warn->BeginSlowTask( NSLOCTEXT("FbxFactory", "BeginImportingFbxMeshTask", "Importing FBX mesh"), true ); if ( !FbxImporter->ImportFromFile( *UFactory::CurrentFilename, Type ) ) { // Log the error message and fail the import. Warn->Log(ELogVerbosity::Error, FbxImporter->GetErrorMessage() ); } else { // Log the import message and import the mesh. const TCHAR* errorMessage = FbxImporter->GetErrorMessage(); if (errorMessage[0] != '\0') { Warn->Log( errorMessage ); } FbxNode* RootNodeToImport = NULL; RootNodeToImport = FbxImporter->Scene->GetRootNode(); // For animation and static mesh we assume there is at lease one interesting node by default int32 InterestingNodeCount = 1; TArray< TArray<FbxNode*>* > SkelMeshArray; bool bImportStaticMeshLODs = ImportUI->StaticMeshImportData->bImportMeshLODs; bool bCombineMeshes = ImportUI->bCombineMeshes; if ( ImportUI->MeshTypeToImport == FBXIT_SkeletalMesh ) { FbxImporter->FillFbxSkelMeshArrayInScene(RootNodeToImport, SkelMeshArray, false); InterestingNodeCount = SkelMeshArray.Num(); } else if( ImportUI->MeshTypeToImport == FBXIT_StaticMesh ) { FbxImporter->ApplyTransformSettingsToFbxNode(RootNodeToImport, ImportUI->StaticMeshImportData); if( bCombineMeshes && !bImportStaticMeshLODs ) { // If Combine meshes and dont import mesh LODs, the interesting node count should be 1 so all the meshes are grouped together into one static mesh InterestingNodeCount = 1; } else { // count meshes in lod groups if we dont care about importing LODs bool bCountLODGroupMeshes = !bImportStaticMeshLODs; int32 NumLODGroups = 0; InterestingNodeCount = FbxImporter->GetFbxMeshCount(RootNodeToImport,bCountLODGroupMeshes,NumLODGroups); // if there were LODs in the file, do not combine meshes even if requested if( bImportStaticMeshLODs && bCombineMeshes ) { bCombineMeshes = NumLODGroups == 0; } } } if (InterestingNodeCount > 1) { // the option only works when there are only one asset ImportOptions->bUsedAsFullName = false; } const FString Filename( UFactory::CurrentFilename ); if (RootNodeToImport && InterestingNodeCount > 0) { int32 NodeIndex = 0; int32 ImportedMeshCount = 0; UStaticMesh* NewStaticMesh = NULL; if ( ImportUI->MeshTypeToImport == FBXIT_StaticMesh ) // static mesh { if (bCombineMeshes) { TArray<FbxNode*> FbxMeshArray; FbxImporter->FillFbxMeshArray(RootNodeToImport, FbxMeshArray, FbxImporter); if (FbxMeshArray.Num() > 0) { NewStaticMesh = FbxImporter->ImportStaticMeshAsSingle(InParent, FbxMeshArray, Name, Flags, ImportUI->StaticMeshImportData, NULL, 0); } ImportedMeshCount = NewStaticMesh ? 1 : 0; } else { TArray<UObject*> AllNewAssets; UObject* Object = RecursiveImportNode(FbxImporter,RootNodeToImport,InParent,Name,Flags,NodeIndex,InterestingNodeCount, AllNewAssets); NewStaticMesh = Cast<UStaticMesh>( Object ); // Make sure to notify the asset registry of all assets created other than the one returned, which will notify the asset registry automatically. for ( auto AssetIt = AllNewAssets.CreateConstIterator(); AssetIt; ++AssetIt ) { UObject* Asset = *AssetIt; if ( Asset != NewStaticMesh ) { FAssetRegistryModule::AssetCreated(Asset); Asset->MarkPackageDirty(); } } ImportedMeshCount = AllNewAssets.Num(); } // Importing static mesh sockets only works if one mesh is being imported if( ImportedMeshCount == 1 && NewStaticMesh ) { FbxImporter->ImportStaticMeshSockets( NewStaticMesh ); } NewObject = NewStaticMesh; } else if ( ImportUI->MeshTypeToImport == FBXIT_SkeletalMesh )// skeletal mesh { int32 TotalNumNodes = 0; for (int32 i = 0; i < SkelMeshArray.Num(); i++) { TArray<FbxNode*> NodeArray = *SkelMeshArray[i]; TotalNumNodes += NodeArray.Num(); // check if there is LODGroup for this skeletal mesh int32 MaxLODLevel = 1; for (int32 j = 0; j < NodeArray.Num(); j++) { FbxNode* Node = NodeArray[j]; if (Node->GetNodeAttribute() && Node->GetNodeAttribute()->GetAttributeType() == FbxNodeAttribute::eLODGroup) { // get max LODgroup level if (MaxLODLevel < Node->GetChildCount()) { MaxLODLevel = Node->GetChildCount(); } } } int32 LODIndex; bool bImportSkeletalMeshLODs = ImportUI->SkeletalMeshImportData->bImportMeshLODs; for (LODIndex = 0; LODIndex < MaxLODLevel; LODIndex++) { if ( !bImportSkeletalMeshLODs && LODIndex > 0) // not import LOD if UI option is OFF { break; } TArray<FbxNode*> SkelMeshNodeArray; for (int32 j = 0; j < NodeArray.Num(); j++) { FbxNode* Node = NodeArray[j]; if (Node->GetNodeAttribute() && Node->GetNodeAttribute()->GetAttributeType() == FbxNodeAttribute::eLODGroup) { if (Node->GetChildCount() > LODIndex) { SkelMeshNodeArray.Add(Node->GetChild(LODIndex)); } else // in less some LODGroups have less level, use the last level { SkelMeshNodeArray.Add(Node->GetChild(Node->GetChildCount() - 1)); } } else { SkelMeshNodeArray.Add(Node); } } if (LODIndex == 0 && SkelMeshNodeArray.Num() != 0) { FName OutputName = FbxImporter->MakeNameForMesh(Name.ToString(), SkelMeshNodeArray[0]); USkeletalMesh* NewMesh = FbxImporter->ImportSkeletalMesh( InParent, SkelMeshNodeArray, OutputName, Flags, ImportUI->SkeletalMeshImportData, &bOperationCanceled ); NewObject = NewMesh; if(bOperationCanceled) { // User cancelled, clean up and return FbxImporter->ReleaseScene(); Warn->EndSlowTask(); bOperationCanceled = true; return nullptr; } if ( NewMesh && ImportUI->bImportAnimations ) { // We need to remove all scaling from the root node before we set up animation data. // Othewise some of the global transform calculations will be incorrect. FbxImporter->RemoveTransformSettingsFromFbxNode(RootNodeToImport, ImportUI->SkeletalMeshImportData); FbxImporter->SetupAnimationDataFromMesh(NewMesh, InParent, SkelMeshNodeArray, ImportUI->AnimSequenceImportData, OutputName.ToString()); // Reapply the transforms for the rest of the import FbxImporter->ApplyTransformSettingsToFbxNode(RootNodeToImport, ImportUI->SkeletalMeshImportData); } } else if (NewObject) // the base skeletal mesh is imported successfully { USkeletalMesh* BaseSkeletalMesh = Cast<USkeletalMesh>(NewObject); FName LODObjectName = NAME_None; USkeletalMesh *LODObject = FbxImporter->ImportSkeletalMesh( GetTransientPackage(), SkelMeshNodeArray, LODObjectName, RF_NoFlags, ImportUI->SkeletalMeshImportData, &bOperationCanceled ); bool bImportSucceeded = !bOperationCanceled && FbxImporter->ImportSkeletalMeshLOD(LODObject, BaseSkeletalMesh, LODIndex, false); if (bImportSucceeded) { BaseSkeletalMesh->LODInfo[LODIndex].ScreenSize = 1.0f / (MaxLODLevel * LODIndex); } else { FbxImporter->AddTokenizedErrorMessage(FTokenizedMessage::Create(EMessageSeverity::Error, LOCTEXT("FailedToImport_SkeletalMeshLOD", "Failed to import Skeletal mesh LOD.")), FFbxErrors::SkeletalMesh_LOD_FailedToImport); } } // import morph target if ( NewObject && ImportUI->SkeletalMeshImportData->bImportMorphTargets) { // Disable material importing when importing morph targets uint32 bImportMaterials = ImportOptions->bImportMaterials; ImportOptions->bImportMaterials = 0; FbxImporter->ImportFbxMorphTarget(SkelMeshNodeArray, Cast<USkeletalMesh>(NewObject), InParent, LODIndex); ImportOptions->bImportMaterials = !!bImportMaterials; } } if (NewObject) { NodeIndex++; FFormatNamedArguments Args; Args.Add( TEXT("NodeIndex"), NodeIndex ); Args.Add( TEXT("ArrayLength"), SkelMeshArray.Num() ); GWarn->StatusUpdate( NodeIndex, SkelMeshArray.Num(), FText::Format( NSLOCTEXT("UnrealEd", "Importingf", "Importing ({NodeIndex} of {ArrayLength})"), Args ) ); } } for (int32 i = 0; i < SkelMeshArray.Num(); i++) { delete SkelMeshArray[i]; } // if total nodes we found is 0, we didn't find anything. if (TotalNumNodes == 0) { FbxImporter->AddTokenizedErrorMessage(FTokenizedMessage::Create(EMessageSeverity::Error, LOCTEXT("FailedToImport_NoMeshFoundOnRoot", "Could not find any valid mesh on the root hierarchy. If you have mesh in the sub hierarchy, please enable option of [Import Meshes In Bone Hierarchy] when import.")), FFbxErrors::SkeletalMesh_NoMeshFoundOnRoot); } } else if ( ImportUI->MeshTypeToImport == FBXIT_Animation )// animation { if (ImportOptions->SkeletonForAnimation) { // will return the last animation sequence that were added NewObject = UEditorEngine::ImportFbxAnimation( ImportOptions->SkeletonForAnimation, InParent, ImportUI->AnimSequenceImportData, *Filename, *Name.ToString(), true ); } } } else { if (RootNodeToImport == NULL) { FbxImporter->AddTokenizedErrorMessage(FTokenizedMessage::Create(EMessageSeverity::Error, LOCTEXT("FailedToImport_InvalidRoot", "Could not find root node.")), FFbxErrors::SkeletalMesh_InvalidRoot); } else if (ImportUI->MeshTypeToImport == FBXIT_SkeletalMesh) { FbxImporter->AddTokenizedErrorMessage(FTokenizedMessage::Create(EMessageSeverity::Error, LOCTEXT("FailedToImport_InvalidBone", "Failed to find any bone hierarchy. Try disabling the \"Import As Skeletal\" option to import as a rigid mesh. ")), FFbxErrors::SkeletalMesh_InvalidBone); } else { FbxImporter->AddTokenizedErrorMessage(FTokenizedMessage::Create(EMessageSeverity::Error, LOCTEXT("FailedToImport_InvalidNode", "Could not find any node.")), FFbxErrors::SkeletalMesh_InvalidNode); } } } if (NewObject == NULL) { FbxImporter->AddTokenizedErrorMessage(FTokenizedMessage::Create(EMessageSeverity::Error, LOCTEXT("FailedToImport_NoObject", "Import failed.")), FFbxErrors::Generic_ImportingNewObjectFailed); } FbxImporter->ReleaseScene(); Warn->EndSlowTask(); } FEditorDelegates::OnAssetPostImport.Broadcast(this, NewObject); return NewObject; }
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; }