From de97ad6ade99afbd17a2eb5a124c2a0ef90fe4da Mon Sep 17 00:00:00 2001 From: Allen Jackson <23512001+jackalbe@users.noreply.github.com> Date: Tue, 15 Feb 2022 15:01:43 -0600 Subject: [PATCH] {lyn8578} adding UX for assinging a Python scene builder (#7551) * {lyn8578} adding UX for assinging a Python scene builder Signed-off-by: Allen Jackson <23512001+jackalbe@users.noreply.github.com> * improved the Reset and Assign script logic Signed-off-by: Allen Jackson <23512001+jackalbe@users.noreply.github.com> * GUI updates to highlight the scene script Signed-off-by: Allen Jackson <23512001+jackalbe@users.noreply.github.com> * save off the script file name instead of holding onto the rule update the header display name separate from setting the scene Signed-off-by: Allen Jackson <23512001+jackalbe@users.noreply.github.com> --- .../AssetImporterWindow.cpp | 114 +++++++++++++++++- .../EditorAssetImporter/AssetImporterWindow.h | 8 ++ .../AssetImporterWindow.ui | 22 +++- .../ImporterRootDisplay.cpp | 7 +- .../EditorAssetImporter/ImporterRootDisplay.h | 1 + .../ImporterRootDisplay.ui | 2 +- .../SceneData/Rules/ScriptProcessorRule.cpp | 10 -- .../SceneData/Rules/ScriptProcessorRule.h | 10 +- 8 files changed, 151 insertions(+), 23 deletions(-) diff --git a/Code/Editor/Plugins/EditorAssetImporter/AssetImporterWindow.cpp b/Code/Editor/Plugins/EditorAssetImporter/AssetImporterWindow.cpp index 0ff9467f33..f19acd4707 100644 --- a/Code/Editor/Plugins/EditorAssetImporter/AssetImporterWindow.cpp +++ b/Code/Editor/Plugins/EditorAssetImporter/AssetImporterWindow.cpp @@ -44,6 +44,9 @@ class CXTPDockingPaneLayout; // Needed for settings.h #include #include #include +#include +#include +#include const char* AssetImporterWindow::s_documentationWebAddress = "http://docs.aws.amazon.com/lumberyard/latest/userguide/char-fbx-importer.html"; const AZ::Uuid AssetImporterWindow::s_browseTag = AZ::Uuid::CreateString("{C240D2E1-BFD2-4FFA-BB5B-CC0FA389A5D3}"); @@ -198,7 +201,7 @@ void AssetImporterWindow::Init() // Filling the initial browse prompt text to be programmatically set from available extensions AZStd::unordered_set extensions; EBUS_EVENT(AZ::SceneAPI::Events::AssetImportRequestBus, GetSupportedFileExtensions, extensions); - AZ_Assert(!extensions.empty(), "No file extensions defined for assets."); + AZ_Error(AZ::SceneAPI::Utilities::ErrorWindow, !extensions.empty(), "No file extensions defined for assets."); if (!extensions.empty()) { for (AZStd::string& extension : extensions) @@ -252,6 +255,7 @@ void AssetImporterWindow::OpenFileInternal(const AZStd::string& filePath) [this, filePath]() { m_assetImporterDocument->LoadScene(filePath); + UpdateSceneDisplay({}); }, [this]() { @@ -290,6 +294,11 @@ void AssetImporterWindow::UpdateClicked() AZ_Assert(!m_processingOverlay, "Attempted to update asset while processing is in progress."); return; } + else if (!m_scriptProcessorRuleFilename.empty()) + { + AZ_TracePrintf(AZ::SceneAPI::Utilities::WarningWindow, "A script updates the manifest; will not save."); + return; + } m_processingOverlay.reset(new ProcessingOverlayWidget(m_overlay.data(), ProcessingOverlayWidget::Layout::Exporting, s_browseTag)); connect(m_processingOverlay.data(), &ProcessingOverlayWidget::Closing, this, &AssetImporterWindow::ClearProcessingOverlay); @@ -383,6 +392,18 @@ void AssetImporterWindow::OnSceneResetRequested() m_rootDisplay->HandleSceneWasReset(m_assetImporterDocument->GetScene()); }, this); + // reset the script rule from the .assetinfo file if it exists + if (!m_scriptProcessorRuleFilename.empty()) + { + m_scriptProcessorRuleFilename.clear(); + if (QFile::exists(m_assetImporterDocument->GetScene()->GetManifestFilename().c_str())) + { + QFile file(m_assetImporterDocument->GetScene()->GetManifestFilename().c_str()); + file.remove(); + } + } + UpdateSceneDisplay({}); + m_processingOverlay.reset(new ProcessingOverlayWidget(m_overlay.data(), ProcessingOverlayWidget::Layout::Resetting, s_browseTag)); m_processingOverlay->SetAndStartProcessingHandler(asyncLoadHandler); m_processingOverlay->SetAutoCloseOnSuccess(true); @@ -390,6 +411,51 @@ void AssetImporterWindow::OnSceneResetRequested() m_processingOverlayIndex = m_processingOverlay->PushToOverlay(); } +void AssetImporterWindow::OnAssignScript() +{ + using namespace AZ::SceneAPI; + using namespace AZ::SceneAPI::Events; + using namespace AZ::SceneAPI::SceneUI; + using namespace AZ::SceneAPI::Utilities; + + // use QFileDialog to select a Python script to embed into a scene manifest file + QString pyFilename = QFileDialog::getOpenFileName(this, + tr("Select scene builder Python script"), + Path::GetEditingGameDataFolder().c_str(), + tr("Python (*.py)")); + + if (pyFilename.isNull()) + { + return; + } + + // reset the script rule from the .assetinfo file if it exists + if (!m_scriptProcessorRuleFilename.empty()) + { + m_scriptProcessorRuleFilename.clear(); + if (QFile::exists(m_assetImporterDocument->GetScene()->GetManifestFilename().c_str())) + { + QFile file(m_assetImporterDocument->GetScene()->GetManifestFilename().c_str()); + file.remove(); + } + } + + // find the path relative to the project folder + pyFilename = Path::GetRelativePath(pyFilename, true); + + // create a script rule + auto scriptProcessorRule = AZStd::make_shared(); + scriptProcessorRule->SetScriptFilename(pyFilename.toUtf8().toStdString().c_str()); + + // add the script rule to the manifest & save off the scene manifest + Containers::SceneManifest sceneManifest; + sceneManifest.AddEntry(scriptProcessorRule); + if (sceneManifest.SaveToFile(m_assetImporterDocument->GetScene()->GetManifestFilename())) + { + OpenFile(m_assetImporterDocument->GetScene()->GetSourceFilename()); + } +} + void AssetImporterWindow::ResetMenuAccess(WindowState state) { if (state == WindowState::FileLoaded) @@ -480,7 +546,7 @@ void AssetImporterWindow::SetTitle(const char* filePath) } AZStd::string fileName; AzFramework::StringFunc::Path::GetFileName(filePath, fileName); - converted->setWindowTitle(QString("%1 Settings (PREVIEW) - %2").arg(extension.c_str(), fileName.c_str())); + converted->setWindowTitle(QString("%1 Settings - %2").arg(extension.c_str(), fileName.c_str())); break; } else @@ -490,6 +556,28 @@ void AssetImporterWindow::SetTitle(const char* filePath) } } +void AssetImporterWindow::UpdateSceneDisplay(const AZStd::shared_ptr scene) const +{ + AZ::IO::FixedMaxPath projectPath = AZ::Utils::GetProjectPath(); + AZ::IO::FixedMaxPath relativeSourcePath = AZ::IO::PathView(m_fullSourcePath).LexicallyProximate(projectPath); + auto sceneHeaderText = QString::fromUtf8(relativeSourcePath.c_str(), static_cast(relativeSourcePath.Native().size())); + if (!m_scriptProcessorRuleFilename.empty()) + { + sceneHeaderText.append("\n Assigned Python builder script (") + .append(m_scriptProcessorRuleFilename.c_str()) + .append(")"); + } + + if (scene) + { + m_rootDisplay->SetSceneDisplay(sceneHeaderText, scene); + } + else + { + m_rootDisplay->SetSceneHeaderText(sceneHeaderText); + } +} + void AssetImporterWindow::HandleAssetLoadingCompleted() { if (!m_assetImporterDocument->GetScene()) @@ -501,10 +589,24 @@ void AssetImporterWindow::HandleAssetLoadingCompleted() m_fullSourcePath = m_assetImporterDocument->GetScene()->GetSourceFilename(); SetTitle(m_fullSourcePath.c_str()); - AZ::IO::FixedMaxPath projectPath = AZ::Utils::GetProjectPath(); - AZ::IO::FixedMaxPath relativeSourcePath = AZ::IO::PathView(m_fullSourcePath).LexicallyProximate(projectPath); - auto userFriendlyFileName = QString::fromUtf8(relativeSourcePath.c_str(), static_cast(relativeSourcePath.Native().size())); - m_rootDisplay->SetSceneDisplay(userFriendlyFileName, m_assetImporterDocument->GetScene()); + using namespace AZ::SceneAPI; + m_scriptProcessorRuleFilename.clear(); + + // load up the source scene manifest file + Containers::SceneManifest sceneManifest; + if (sceneManifest.LoadFromFile(m_assetImporterDocument->GetScene()->GetManifestFilename())) + { + // check a Python script rule is in that source manifest + auto view = Containers::MakeDerivedFilterView(sceneManifest.GetValueStorage()); + if (!view.empty()) + { + // record the info about the rule in the class + const auto scriptProcessorRule = &*view.begin(); + m_scriptProcessorRuleFilename = scriptProcessorRule->GetScriptFilename(); + } + } + + UpdateSceneDisplay(m_assetImporterDocument->GetScene()); // Once we've browsed to something successfully, we need to hide the initial browse button layer and // show the main area where all the actual work takes place diff --git a/Code/Editor/Plugins/EditorAssetImporter/AssetImporterWindow.h b/Code/Editor/Plugins/EditorAssetImporter/AssetImporterWindow.h index 1c62b08c19..b4a46ca1f7 100644 --- a/Code/Editor/Plugins/EditorAssetImporter/AssetImporterWindow.h +++ b/Code/Editor/Plugins/EditorAssetImporter/AssetImporterWindow.h @@ -47,6 +47,10 @@ namespace AZ { class ProcessingOverlayWidget; } + namespace DataTypes + { + class IScriptProcessorRule; + } } } @@ -80,6 +84,7 @@ public: public slots: void OnSceneResetRequested(); + void OnAssignScript(); void OnOpenDocumentation(); void OnInspect(); @@ -105,6 +110,7 @@ private slots: void OverlayLayerAdded(); void OverlayLayerRemoved(); + void UpdateSceneDisplay(const AZStd::shared_ptr scene = {}) const; private: static const AZ::Uuid s_browseTag; @@ -122,4 +128,6 @@ private: int m_processingOverlayIndex; QSharedPointer m_processingOverlay; + + AZStd::string m_scriptProcessorRuleFilename; }; diff --git a/Code/Editor/Plugins/EditorAssetImporter/AssetImporterWindow.ui b/Code/Editor/Plugins/EditorAssetImporter/AssetImporterWindow.ui index d773d867e7..e279a7c6b9 100644 --- a/Code/Editor/Plugins/EditorAssetImporter/AssetImporterWindow.ui +++ b/Code/Editor/Plugins/EditorAssetImporter/AssetImporterWindow.ui @@ -25,7 +25,8 @@ &Edit - + + &Help @@ -49,7 +50,7 @@ QLayout::SetMaximumSize - + @@ -175,7 +176,16 @@ Reset the settings for this file (note: you will have to update to commit) - + + + + Assign Build Script... + + + Assign a Python build script that will override the scene's build rules + + + Documentation @@ -204,6 +214,12 @@ AssetImporterWindow OnSceneResetRequested() + + m_actionAssignScript + triggered() + AssetImporterWindow + OnAssignScript() + m_actionInspect triggered() diff --git a/Code/Editor/Plugins/EditorAssetImporter/ImporterRootDisplay.cpp b/Code/Editor/Plugins/EditorAssetImporter/ImporterRootDisplay.cpp index 2c00edbc39..359dfcec21 100644 --- a/Code/Editor/Plugins/EditorAssetImporter/ImporterRootDisplay.cpp +++ b/Code/Editor/Plugins/EditorAssetImporter/ImporterRootDisplay.cpp @@ -44,6 +44,11 @@ AZ::SceneAPI::UI::ManifestWidget* ImporterRootDisplay::GetManifestWidget() return m_manifestWidget.data(); } +void ImporterRootDisplay::SetSceneHeaderText(const QString& headerText) +{ + ui->m_filePathText->setText(headerText); +} + void ImporterRootDisplay::SetSceneDisplay(const QString& headerText, const AZStd::shared_ptr& scene) { AZ_PROFILE_FUNCTION(Editor); @@ -53,7 +58,7 @@ void ImporterRootDisplay::SetSceneDisplay(const QString& headerText, const AZStd return; } - ui->m_filePathText->setText(headerText); + SetSceneHeaderText(headerText); HandleSceneWasReset(scene); diff --git a/Code/Editor/Plugins/EditorAssetImporter/ImporterRootDisplay.h b/Code/Editor/Plugins/EditorAssetImporter/ImporterRootDisplay.h index 4e088ccb5c..03de717316 100644 --- a/Code/Editor/Plugins/EditorAssetImporter/ImporterRootDisplay.h +++ b/Code/Editor/Plugins/EditorAssetImporter/ImporterRootDisplay.h @@ -63,6 +63,7 @@ public: AZ::SceneAPI::UI::ManifestWidget* GetManifestWidget(); void SetSceneDisplay(const QString& headerText, const AZStd::shared_ptr& scene); + void SetSceneHeaderText(const QString& headerText); void HandleSceneWasReset(const AZStd::shared_ptr& scene); void HandleSaveWasSuccessful(); bool HasUnsavedChanges() const; diff --git a/Code/Editor/Plugins/EditorAssetImporter/ImporterRootDisplay.ui b/Code/Editor/Plugins/EditorAssetImporter/ImporterRootDisplay.ui index 2364b41f53..1cad17e19c 100644 --- a/Code/Editor/Plugins/EditorAssetImporter/ImporterRootDisplay.ui +++ b/Code/Editor/Plugins/EditorAssetImporter/ImporterRootDisplay.ui @@ -40,7 +40,7 @@ - #m_filePathText { margin: 2px; color: grey; } + #m_filePathText { margin: 2px; color: white; } Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter diff --git a/Code/Tools/SceneAPI/SceneData/Rules/ScriptProcessorRule.cpp b/Code/Tools/SceneAPI/SceneData/Rules/ScriptProcessorRule.cpp index 15c8c1b7f3..bf6ed19740 100644 --- a/Code/Tools/SceneAPI/SceneData/Rules/ScriptProcessorRule.cpp +++ b/Code/Tools/SceneAPI/SceneData/Rules/ScriptProcessorRule.cpp @@ -21,16 +21,6 @@ namespace AZ { namespace SceneData { - const AZStd::string& ScriptProcessorRule::GetScriptFilename() const - { - return m_scriptFilename; - } - - DataTypes::ScriptProcessorFallbackLogic ScriptProcessorRule::GetScriptProcessorFallbackLogic() const - { - return m_fallbackLogic; - } - void ScriptProcessorRule::Reflect(AZ::ReflectContext* context) { AZ::SerializeContext* serializeContext = azrtti_cast(context); diff --git a/Code/Tools/SceneAPI/SceneData/Rules/ScriptProcessorRule.h b/Code/Tools/SceneAPI/SceneData/Rules/ScriptProcessorRule.h index 80cb670f9f..e93bb97edb 100644 --- a/Code/Tools/SceneAPI/SceneData/Rules/ScriptProcessorRule.h +++ b/Code/Tools/SceneAPI/SceneData/Rules/ScriptProcessorRule.h @@ -28,14 +28,20 @@ namespace AZ ~ScriptProcessorRule() override = default; - const AZStd::string& GetScriptFilename() const override; + inline const AZStd::string& GetScriptFilename() const override + { + return m_scriptFilename; + } inline void SetScriptFilename(AZStd::string scriptFilename) { m_scriptFilename = AZStd::move(scriptFilename); } - DataTypes::ScriptProcessorFallbackLogic GetScriptProcessorFallbackLogic() const override; + inline DataTypes::ScriptProcessorFallbackLogic GetScriptProcessorFallbackLogic() const override + { + return m_fallbackLogic; + } static void Reflect(ReflectContext* context);