From cb9211097672d263169a8414f830416e203cef22 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 7 Dec 2023 10:25:45 +0100 Subject: [PATCH] Add `ModelPrefab` to imported model prefab for reimporting functionality --- .../Dedicated/ModelPrefabEditor.cs | 79 +++++++++++++++++++ .../Values/CustomValueContainer.cs | 1 - .../Editor/Modules/ContentImportingModule.cs | 44 ++++++----- .../AssetsImportingManager.cpp | 29 +++---- .../ContentImporters/AssetsImportingManager.h | 3 + .../Engine/ContentImporters/ImportModel.cpp | 22 +++++- Source/Engine/Level/SceneObjectsFactory.cpp | 8 +- .../{Components => Scripts}/MissingScript.h | 0 Source/Engine/Level/Scripts/ModelPrefab.h | 29 +++++++ 9 files changed, 179 insertions(+), 36 deletions(-) create mode 100644 Source/Editor/CustomEditors/Dedicated/ModelPrefabEditor.cs rename Source/Engine/Level/{Components => Scripts}/MissingScript.h (100%) create mode 100644 Source/Engine/Level/Scripts/ModelPrefab.h diff --git a/Source/Editor/CustomEditors/Dedicated/ModelPrefabEditor.cs b/Source/Editor/CustomEditors/Dedicated/ModelPrefabEditor.cs new file mode 100644 index 000000000..0a42b952e --- /dev/null +++ b/Source/Editor/CustomEditors/Dedicated/ModelPrefabEditor.cs @@ -0,0 +1,79 @@ +// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. + +using System; +using System.IO; +using FlaxEditor.Content; +using FlaxEditor.CustomEditors.Editors; +using FlaxEngine; +using FlaxEngine.GUI; +using FlaxEngine.Tools; + +namespace FlaxEditor.CustomEditors.Dedicated; + +/// +/// The missing script editor. +/// +[CustomEditor(typeof(ModelPrefab)), DefaultEditor] +public class ModelPrefabEditor : GenericEditor +{ + private Guid _prefabId; + private Button _reimportButton; + private string _importPath; + + /// + public override void Initialize(LayoutElementsContainer layout) + { + base.Initialize(layout); + + var modelPrefab = Values[0] as ModelPrefab; + if (modelPrefab == null) + return; + _prefabId = modelPrefab.PrefabID; + while (true) + { + var prefab = FlaxEngine.Content.Load(_prefabId); + if (prefab) + { + var prefabObjectId = modelPrefab.PrefabObjectID; + var prefabObject = prefab.GetDefaultInstance(ref prefabObjectId); + if (prefabObject.PrefabID == _prefabId) + break; + _prefabId = prefabObject.PrefabID; + } + } + + var button = layout.Button("Reimport", "Reimports the source asset as prefab."); + _reimportButton = button.Button; + _reimportButton.Clicked += OnReimport; + } + + private void OnReimport() + { + var prefab = FlaxEngine.Content.Load(_prefabId); + var modelPrefab = (ModelPrefab)Values[0]; + var importPath = modelPrefab.ImportPath; + var editor = Editor.Instance; + if (editor.ContentImporting.GetReimportPath("Model Prefab", ref importPath)) + return; + var folder = editor.ContentDatabase.Find(Path.GetDirectoryName(prefab.Path)) as ContentFolder; + if (folder == null) + return; + var importOptions = modelPrefab.ImportOptions; + importOptions.Type = ModelTool.ModelType.Prefab; + _importPath = importPath; + _reimportButton.Enabled = false; + editor.ContentImporting.ImportFileEnd += OnImportFileEnd; + editor.ContentImporting.Import(importPath, folder, true, importOptions); + } + + private void OnImportFileEnd(IFileEntryAction entry, bool failed) + { + if (entry.SourceUrl == _importPath) + { + // Restore button + _importPath = null; + _reimportButton.Enabled = true; + Editor.Instance.ContentImporting.ImportFileEnd -= OnImportFileEnd; + } + } +} diff --git a/Source/Editor/CustomEditors/Values/CustomValueContainer.cs b/Source/Editor/CustomEditors/Values/CustomValueContainer.cs index 9a09c4cc2..23b5832e5 100644 --- a/Source/Editor/CustomEditors/Values/CustomValueContainer.cs +++ b/Source/Editor/CustomEditors/Values/CustomValueContainer.cs @@ -73,7 +73,6 @@ namespace FlaxEditor.CustomEditors { if (instanceValues == null || instanceValues.Count != Count) throw new ArgumentException(); - for (int i = 0; i < Count; i++) { var v = instanceValues[i]; diff --git a/Source/Editor/Modules/ContentImportingModule.cs b/Source/Editor/Modules/ContentImportingModule.cs index 85dd50d35..587d3a6c6 100644 --- a/Source/Editor/Modules/ContentImportingModule.cs +++ b/Source/Editor/Modules/ContentImportingModule.cs @@ -126,29 +126,35 @@ namespace FlaxEditor.Modules { if (item != null && !item.GetImportPath(out string importPath)) { - // Check if input file is missing - if (!System.IO.File.Exists(importPath)) - { - Editor.LogWarning(string.Format("Cannot reimport asset \'{0}\'. File \'{1}\' does not exist.", item.Path, importPath)); - if (skipSettingsDialog) - return; - - // Ask user to select new file location - var title = string.Format("Please find missing \'{0}\' file for asset \'{1}\'", importPath, item.ShortName); - if (FileSystem.ShowOpenFileDialog(Editor.Windows.MainWindow, null, "All files (*.*)\0*.*\0", false, title, out var files)) - return; - if (files != null && files.Length > 0) - importPath = files[0]; - - // Validate file path again - if (!System.IO.File.Exists(importPath)) - return; - } - + if (GetReimportPath(item.ShortName, ref importPath, skipSettingsDialog)) + return; Import(importPath, item.Path, true, skipSettingsDialog, settings); } } + internal bool GetReimportPath(string contextName, ref string importPath, bool skipSettingsDialog = false) + { + // Check if input file is missing + if (!System.IO.File.Exists(importPath)) + { + Editor.LogWarning(string.Format("Cannot reimport asset \'{0}\'. File \'{1}\' does not exist.", contextName, importPath)); + if (skipSettingsDialog) + return true; + + // Ask user to select new file location + var title = string.Format("Please find missing \'{0}\' file for asset \'{1}\'", importPath, contextName); + if (FileSystem.ShowOpenFileDialog(Editor.Windows.MainWindow, null, "All files (*.*)\0*.*\0", false, title, out var files)) + return true; + if (files != null && files.Length > 0) + importPath = files[0]; + + // Validate file path again + if (!System.IO.File.Exists(importPath)) + return true; + } + return false; + } + /// /// Imports the specified files. /// diff --git a/Source/Engine/ContentImporters/AssetsImportingManager.cpp b/Source/Engine/ContentImporters/AssetsImportingManager.cpp index 4bad26599..bbefc687a 100644 --- a/Source/Engine/ContentImporters/AssetsImportingManager.cpp +++ b/Source/Engine/ContentImporters/AssetsImportingManager.cpp @@ -165,20 +165,7 @@ bool CreateAssetContext::AllocateChunk(int32 index) void CreateAssetContext::AddMeta(JsonWriter& writer) const { writer.JKEY("ImportPath"); - if (AssetsImportingManager::UseImportPathRelative && !FileSystem::IsRelative(InputPath) -#if PLATFORM_WINDOWS - // Import path from other drive should be stored as absolute on Windows to prevent issues - && InputPath.Length() > 2 && Globals::ProjectFolder.Length() > 2 && InputPath[0] == Globals::ProjectFolder[0] -#endif - ) - { - const String relativePath = FileSystem::ConvertAbsolutePathToRelative(Globals::ProjectFolder, InputPath); - writer.String(relativePath); - } - else - { - writer.String(InputPath); - } + writer.String(AssetsImportingManager::GetImportPath(InputPath)); writer.JKEY("ImportUsername"); writer.String(Platform::GetUserName()); } @@ -304,6 +291,20 @@ bool AssetsImportingManager::ImportIfEdited(const StringView& inputPath, const S return false; } +String AssetsImportingManager::GetImportPath(const String& path) +{ + if (UseImportPathRelative && !FileSystem::IsRelative(path) +#if PLATFORM_WINDOWS + // Import path from other drive should be stored as absolute on Windows to prevent issues + && path.Length() > 2 && Globals::ProjectFolder.Length() > 2 && path[0] == Globals::ProjectFolder[0] +#endif + ) + { + return FileSystem::ConvertAbsolutePathToRelative(Globals::ProjectFolder, path); + } + return path; +} + bool AssetsImportingManager::Create(const Function& callback, const StringView& inputPath, const StringView& outputPath, Guid& assetId, void* arg) { PROFILE_CPU(); diff --git a/Source/Engine/ContentImporters/AssetsImportingManager.h b/Source/Engine/ContentImporters/AssetsImportingManager.h index eb5058650..2b76298a6 100644 --- a/Source/Engine/ContentImporters/AssetsImportingManager.h +++ b/Source/Engine/ContentImporters/AssetsImportingManager.h @@ -236,6 +236,9 @@ public: return ImportIfEdited(inputPath, outputPath, id, arg); } + // Converts source files path into the relative format if enabled by the project settings. Result path can be stored in asset for reimports. + static String GetImportPath(const String& path); + private: static bool Create(const CreateAssetFunction& callback, const StringView& inputPath, const StringView& outputPath, Guid& assetId, void* arg); }; diff --git a/Source/Engine/ContentImporters/ImportModel.cpp b/Source/Engine/ContentImporters/ImportModel.cpp index be58be3ba..baac1defb 100644 --- a/Source/Engine/ContentImporters/ImportModel.cpp +++ b/Source/Engine/ContentImporters/ImportModel.cpp @@ -19,6 +19,7 @@ #include "Engine/Level/Actors/StaticModel.h" #include "Engine/Level/Prefabs/Prefab.h" #include "Engine/Level/Prefabs/PrefabManager.h" +#include "Engine/Level/Scripts/ModelPrefab.h" #include "Engine/Platform/FileSystem.h" #include "Engine/Utilities/RectPack.h" #include "Engine/Profiler/ProfilerCPU.h" @@ -699,7 +700,26 @@ CreateAssetResult ImportModel::CreatePrefab(CreateAssetContext& context, ModelDa } } ASSERT_LOW_LAYER(rootActor); - // TODO: add PrefabModel script for asset reimporting + { + // Add script with import options + auto* modelPrefabScript = New(); + modelPrefabScript->SetParent(rootActor); + modelPrefabScript->ImportPath = AssetsImportingManager::GetImportPath(context.InputPath); + modelPrefabScript->ImportOptions = options; + + // Link with existing prefab instance + if (prefab) + { + for (const auto& i : prefab->ObjectsCache) + { + if (i.Value->GetTypeHandle() == modelPrefabScript->GetTypeHandle()) + { + modelPrefabScript->LinkPrefab(i.Value->GetPrefabID(), i.Value->GetPrefabObjectID()); + break; + } + } + } + } // Create prefab instead of native asset bool failed; diff --git a/Source/Engine/Level/SceneObjectsFactory.cpp b/Source/Engine/Level/SceneObjectsFactory.cpp index 2ae947c85..df30257f9 100644 --- a/Source/Engine/Level/SceneObjectsFactory.cpp +++ b/Source/Engine/Level/SceneObjectsFactory.cpp @@ -16,8 +16,9 @@ #if !BUILD_RELEASE || USE_EDITOR #include "Engine/Level/Level.h" #include "Engine/Threading/Threading.h" -#include "Engine/Level/Components/MissingScript.h" +#include "Engine/Level/Scripts/MissingScript.h" #endif +#include "Engine/Level/Scripts/ModelPrefab.h" #if USE_EDITOR @@ -46,6 +47,11 @@ void MissingScript::SetReferenceScript(const ScriptingObjectReference