Initial commit

This commit is contained in:
alexpete
2021-03-05 11:26:34 -08:00
commit a10351f38d
27091 changed files with 5521199 additions and 0 deletions
@@ -0,0 +1,104 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "EditorAssetImporter_precompiled.h"
#include <AssetBrowserContextProvider.h>
#include <AssetImporterPlugin.h>
#include <QtCore/QMimeData>
#include <QMenu>
#include <AzToolsFramework/AssetBrowser/AssetBrowserEntry.h>
#include <SceneAPI/SceneCore/Events/AssetImportRequest.h>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h> // for ebus events
#include <AzFramework/StringFunc/StringFunc.h>
namespace AZ
{
using namespace AzToolsFramework::AssetBrowser;
AssetBrowserContextProvider::AssetBrowserContextProvider()
{
BusConnect();
}
AssetBrowserContextProvider::~AssetBrowserContextProvider()
{
BusDisconnect();
}
bool AssetBrowserContextProvider::HandlesSource(const AzToolsFramework::AssetBrowser::SourceAssetBrowserEntry* entry) const
{
AZStd::unordered_set<AZStd::string> extensions;
EBUS_EVENT(AZ::SceneAPI::Events::AssetImportRequestBus, GetSupportedFileExtensions, extensions);
if (extensions.empty())
{
return false;
}
AZStd::string targetExtension = entry->GetExtension();
for (const AZStd::string& potentialExtension : extensions)
{
const char* extension = potentialExtension.c_str();
if (AzFramework::StringFunc::Equal(extension, targetExtension.c_str()))
{
return true;
}
}
return false;
}
void AssetBrowserContextProvider::AddSourceFileOpeners([[maybe_unused]] const char* fullSourceFileName, const AZ::Uuid& sourceUUID, AzToolsFramework::AssetBrowser::SourceFileOpenerList& openers)
{
using namespace AzToolsFramework;
if (const SourceAssetBrowserEntry* source = SourceAssetBrowserEntry::GetSourceByUuid(sourceUUID))
{
if (!HandlesSource(source))
{
return;
}
}
else
{
// its not something we can actually open if its not a source file at all
return;
}
openers.push_back({ "Lumberyard_FBX_Settings_Edit", "Edit Settings...", QIcon(), [](const char* fullSourceFileNameInCallback, const AZ::Uuid& /*sourceUUID*/)
{
AZStd::string sourceName(fullSourceFileNameInCallback); // because the below call absolutely requires a AZStd::string.
AssetImporterPlugin::GetInstance()->EditImportSettings(sourceName);
} });
}
AzToolsFramework::AssetBrowser::SourceFileDetails AssetBrowserContextProvider::GetSourceFileDetails(const char* fullSourceFileName)
{
AZStd::string extensionString;
if (AzFramework::StringFunc::Path::GetExtension(fullSourceFileName, extensionString, true))
{
// this does include the "." in the extension.
AZStd::unordered_set<AZStd::string> extensions;
EBUS_EVENT(AZ::SceneAPI::Events::AssetImportRequestBus, GetSupportedFileExtensions, extensions);
for (AZStd::string potentialExtension : extensions)
{
if (AzFramework::StringFunc::Equal(extensionString.c_str(), potentialExtension.c_str()))
{
return AzToolsFramework::AssetBrowser::SourceFileDetails("Editor/Icons/AssetBrowser/FBX_16.png");
}
}
}
return AzToolsFramework::AssetBrowser::SourceFileDetails();
}
}
@@ -0,0 +1,46 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzToolsFramework/AssetBrowser/AssetBrowserBus.h>
class QMimeData;
namespace AzToolsFramework
{
namespace AssetBrowser
{
class SourceAssetBrowserEntry;
class AssetBrowserEntry;
}
}
namespace AZ
{
class AssetBrowserContextProvider
: public AzToolsFramework::AssetBrowser::AssetBrowserInteractionNotificationBus::Handler
{
public:
AssetBrowserContextProvider();
~AssetBrowserContextProvider() override;
/////////////////////////////////////////////////////////////////////////////////////////////////////
// AzToolsFramework::AssetBrowser::AssetBrowserInteractionNotificationBus::Handler
void AddSourceFileOpeners(const char* fullSourceFileName, const AZ::Uuid& sourceUUID, AzToolsFramework::AssetBrowser::SourceFileOpenerList& openers) override;
AzToolsFramework::AssetBrowser::SourceFileDetails GetSourceFileDetails(const char* fullSourceFileName) override;
/////////////////////////////////////////////////////////////////////////////////////////////////////
protected:
bool HandlesSource(const AzToolsFramework::AssetBrowser::SourceAssetBrowserEntry* entry) const; // return true if we care about this kind of source file.
};
}
@@ -0,0 +1,17 @@
<RCC>
<qresource prefix="/Icons">
<file alias="Browse.png">../../../../Editor/Icons/PropertyEditor/Browse.png</file>
<file alias="Browse_On.png">../../../../Editor/Icons/PropertyEditor/Browse_on.png</file>
<file alias="DeleteRule.png">../../../../Editor/Icons/PropertyEditor/remove.png</file>
<file alias="DeleteGroup.png">../../../../Editor/Icons/PropertyEditor/remove.png</file>
<file alias="Error.png">../../../../Editor/Icons/PropertyEditor/error_icon.png</file>
<file alias="RefreshGroup.png">../../../../Editor/Icons/PropertyEditor/reset_icon.png</file>
<file alias="MeshSelectorBrowse.png">../../../../Editor/Icons/AssetImporter/mesh.png</file>
<file alias="MeshSelectorBrowse_On.png">../../../../Editor/Icons/AssetImporter/mesh_on.png</file>
<file alias="MeshTreeIcon.png">../../../../Editor/Icons/AssetImporter/mesh_on.png</file>
<file alias="GroupTreeIcon.png">../../../../Editor/Icons/AssetImporter/group_icon.png</file>
<file alias="CheckMark_Checked.png">../../../../Editor/Icons/checkmark_checked.png</file>
<file alias="CheckMark_Hover.png">../../../../Editor/Icons/checkmark_checked_hover.png</file>
<file alias="CheckMark_Unchecked_Hover.png">../../../../Editor/Icons/checkmark_unchecked_hover.png</file>
</qresource>
</RCC>
@@ -0,0 +1,118 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "EditorAssetImporter_precompiled.h"
#include <AssetImporterDocument.h>
#include <AzToolsFramework/Debug/TraceContext.h>
#include <SceneAPI/SceneCore/Export/MtlMaterialExporter.h>
#include <Util/PathUtil.h>
#include <GFxFramework/MaterialIO/IMaterial.h>
#include <SceneAPI/SceneCore/Containers/Views/PairIterator.h>
#include <SceneAPI/SceneCore/Containers/Views/FilterIterator.h>
#include <SceneAPI/SceneCore/DataTypes/DataTypeUtilities.h>
#include <SceneAPI/SceneCore/DataTypes/Rules/IMaterialRule.h>
#include <SceneAPI/SceneCore/DataTypes/Groups/IMeshGroup.h>
#include <SceneAPI/SceneCore/DataTypes/Groups/ISkeletonGroup.h>
#include <SceneAPI/SceneCore/DataTypes/Groups/ISkinGroup.h>
#include <SceneAPI/SceneCore/DataTypes/Groups/IAnimationGroup.h>
#include <SceneAPI/SceneCore/DataTypes/ManifestBase/ISceneNodeSelectionList.h>
#include <SceneAPI/SceneCore/Events/SceneSerializationBus.h>
#include <SceneAPI/SceneCore/Utilities/SceneGraphSelector.h>
#include <SceneAPI/SceneCore/Utilities/FileUtilities.h>
#include <SceneAPI/SceneCore/Utilities/Reporting.h>
#include <IEditor.h>
#include <ISourceControl.h>
#include <QFile>
#include <QWidget>
#include <QMessageBox>
#include <QPushButton>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <AzCore/std/string/conversions.h>
#include <AzCore/std/smart_ptr/make_shared.h>
#include <ActionOutput.h>
#include <AzToolsFramework/SourceControl/SourceControlAPI.h>
AssetImporterDocument::AssetImporterDocument()
{
}
bool AssetImporterDocument::LoadScene(const AZStd::string& sceneFullPath)
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Editor);
namespace SceneEvents = AZ::SceneAPI::Events;
SceneEvents::SceneSerializationBus::BroadcastResult(m_scene, &SceneEvents::SceneSerializationBus::Events::LoadScene, sceneFullPath, AZ::Uuid::CreateNull());
return !!m_scene;
}
void AssetImporterDocument::SaveScene(AZStd::shared_ptr<AZ::ActionOutput>& output, AZ::SaveCompleteCallback onSaveComplete)
{
if (!m_scene)
{
if (output)
{
output->AddError("No scene file was loaded.");
}
if (onSaveComplete)
{
onSaveComplete(false);
}
return;
}
m_saveRunner = AZStd::make_shared<AZ::AsyncSaveRunner>();
// Add a no-op saver to put the FBX into source control. The benefit of doing it this way
// rather than invoking the SourceControlCommands bus directly is that we enable ourselves
// to have a single callback point for fbx & the manifest.
auto fbxNoOpSaver = m_saveRunner->GenerateController();
fbxNoOpSaver->AddSaveOperation(m_scene->GetSourceFilename(), nullptr);
// Save the manifest
SaveManifest();
m_saveRunner->Run(output,
[this, onSaveComplete](bool success)
{
if (onSaveComplete)
{
onSaveComplete(success);
}
m_saveRunner = nullptr;
}, AZ::AsyncSaveRunner::ControllerOrder::Sequential);
}
void AssetImporterDocument::ClearScene()
{
m_scene.reset();
}
void AssetImporterDocument::SaveManifest()
{
// Create the save controller and add the save operation for the manifest job to it
AZStd::shared_ptr<AZ::SaveOperationController> saveController = m_saveRunner->GenerateController();
saveController->AddSaveOperation(m_scene->GetManifestFilename(),
[this](const AZStd::string& fullPath, const AZStd::shared_ptr<AZ::ActionOutput>& actionOutput) -> bool
{
AZ_UNUSED(actionOutput);
return m_scene->GetManifest().SaveToFile(fullPath.c_str());
});
}
AZStd::shared_ptr<AZ::SceneAPI::Containers::Scene>& AssetImporterDocument::GetScene()
{
return m_scene;
}
@@ -0,0 +1,69 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
/////////////////////////////////////////////////////////////////////////////
//
// Asset Importer Document hosts FBX back-end data storage and access,
// loading and saving APIs.
//
/////////////////////////////////////////////////////////////////////////////
#include <string>
#include <AzFramework/StringFunc/StringFunc.h>
#include <SceneAPI/SceneCore/Containers/Scene.h>
#include <AzCore/std/string/string.h>
#include <AzCore/std/containers/list.h>
#include <AzCore/std/string/string.h>
#include <AzCore/std/containers/map.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
#include <SaveUtilities/AsyncSaveRunner.h>
class QWidget;
namespace AZ
{
namespace SceneAPI
{
namespace DataTypes
{
class ISceneNodeGroup;
class IMeshGroup;
class ISkeletonGroup;
class ISkinGroup;
class IAnimationGroup;
class IMaterialRule;
class IActorGroup;
class IEFXMotionGroup;
}
}
}
class AssetImporterDocument
{
public:
AssetImporterDocument();
virtual ~AssetImporterDocument() = default;
bool LoadScene(const AZStd::string& sceneFullPath);
void SaveScene(AZStd::shared_ptr<AZ::ActionOutput>& output, AZ::SaveCompleteCallback onSaveComplete);
void ClearScene();
AZStd::shared_ptr<AZ::SceneAPI::Containers::Scene>& GetScene();
protected:
void SaveManifest();
AZStd::shared_ptr<AZ::SceneAPI::Containers::Scene> m_scene;
AZStd::shared_ptr<AZ::AsyncSaveRunner> m_saveRunner;
};
@@ -0,0 +1,98 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "EditorAssetImporter_precompiled.h"
#include <AssetImporterPlugin.h>
#include <AssetImporterWindow.h>
#include <QtViewPaneManager.h>
#include <SceneAPI/SceneCore/Utilities/Reporting.h>
#include <LyViewPaneNames.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
AssetImporterPlugin* AssetImporterPlugin::s_instance;
AssetImporterPlugin::AssetImporterPlugin(IEditor* editor)
: m_editor(editor)
, m_toolName(LyViewPane::SceneSettings)
, m_assetBrowserContextProvider()
, m_sceneSerializationHandler()
{
s_instance = this;
m_sceneUIModule = LoadSceneLibrary("SceneUI", true);
m_sceneSerializationHandler.Activate();
AzToolsFramework::ViewPaneOptions opt;
opt.isPreview = true;
opt.showInMenu = false; // this view pane is used to display scene settings, but the user never opens it directly through the Tools menu
opt.saveKeyName = "Scene Settings (PREVIEW)"; // user settings for this pane were originally saved with PREVIEW, so ensure that's how they are loaded as well, even after the PREVIEW is removed from the name
AzToolsFramework::RegisterViewPane<AssetImporterWindow>(m_toolName.c_str(), LyViewPane::CategoryTools, opt);
}
void AssetImporterPlugin::Release()
{
AzToolsFramework::UnregisterViewPane(m_toolName.c_str());
m_sceneSerializationHandler.Deactivate();
auto uninit = m_sceneUIModule->GetFunction<AZ::UninitializeDynamicModuleFunction>(AZ::UninitializeDynamicModuleFunctionName);
if (uninit)
{
(*uninit)();
}
m_sceneUIModule.reset();
}
AZStd::unique_ptr<AZ::DynamicModuleHandle> AssetImporterPlugin::LoadSceneLibrary(const char* name, bool explicitInit)
{
using ReflectFunc = void(*)(AZ::SerializeContext*);
AZStd::unique_ptr<AZ::DynamicModuleHandle> module = AZ::DynamicModuleHandle::Create(name);
if (module)
{
module->Load(false);
if (explicitInit)
{
// Explicitly initialize all modules. Because we're loading them twice (link time, and now-time), we need to explicitly uninit them.
auto init = module->GetFunction<AZ::InitializeDynamicModuleFunction>(AZ::InitializeDynamicModuleFunctionName);
if (init)
{
(*init)(AZ::Environment::GetInstance());
}
}
ReflectFunc reflect = module->GetFunction<ReflectFunc>("Reflect");
if (reflect)
{
(*reflect)(nullptr);
}
return module;
}
else
{
AZ_TracePrintf(AZ::SceneAPI::Utilities::ErrorWindow, "Failed to initialize library '%s'", name);
return nullptr;
}
}
void AssetImporterPlugin::EditImportSettings(const AZStd::string& sourceFilePath)
{
const QtViewPane* assetImporterPane = GetIEditor()->OpenView(m_toolName.c_str());
AssetImporterWindow* assetImporterWindow = qobject_cast<AssetImporterWindow*>(assetImporterPane->Widget());
if (!assetImporterWindow)
{
return;
}
assetImporterWindow->OpenFile(sourceFilePath);
}
@@ -0,0 +1,100 @@
#pragma once
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
/////////////////////////////////////////////////////////////////////////////
//
// Asset Importer Sandbox Plugin
//
/////////////////////////////////////////////////////////////////////////////
#include <IEditor.h>
#include <Include/IPlugin.h>
#include <AzCore/Module/DynamicModuleHandle.h>
#include <AssetBrowserContextProvider.h>
#include <SceneSerializationHandler.h>
class AssetImporterPlugin
: public IPlugin
{
// Create plugin instance, only accessible to CreatePluginInstance
// If you need instance, use static GetInstance()
friend IPlugin* ::CreatePluginInstance(PLUGIN_INIT_PARAM* initParam);
AssetImporterPlugin(IEditor* editor);
public:
// Get the singleton instance of the plugin
static AssetImporterPlugin* GetInstance()
{
return s_instance;
}
// Get the editor used to create this plugin
IEditor* GetIEditor()
{
return m_editor;
}
const string& GetToolName() const
{
return m_toolName;
}
/////////////////////////////////////////////////////////////////////////////
// IPlugin implementation
void Release() override;
void ShowAbout() override
{
}
const char* GetPluginGUID() override
{
return "{0abf28f2-ef56-4ac9-a459-175abb40d649}";
}
DWORD GetPluginVersion() override
{
return 1;
}
const char* GetPluginName() override
{
return "QtAssetImporter";
}
bool CanExitNow() override
{
return true;
}
void OnEditorNotify([[maybe_unused]] EEditorNotifyEvent aEventId) override
{
}
/////////////////////////////////////////////////////////////////////////////
void EditImportSettings(const AZStd::string& sourceFilePath);
private:
AZStd::unique_ptr<AZ::DynamicModuleHandle> LoadSceneLibrary(const char* name, bool explicitInit);
// Singleton instance
static AssetImporterPlugin* s_instance;
// Dependency DLL Handles
AZStd::unique_ptr<AZ::DynamicModuleHandle> m_sceneUIModule;
// The editor used to construct the plugin
IEditor* const m_editor;
// Tool name
string m_toolName;
// Context provider for the Asset Browser
AZ::AssetBrowserContextProvider m_assetBrowserContextProvider;
AZ::SceneSerializationHandler m_sceneSerializationHandler;
};
@@ -0,0 +1,557 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "EditorAssetImporter_precompiled.h"
#include <AssetImporterWindow.h>
#include <ui_AssetImporterWindow.h>
#include <AssetImporterPlugin.h>
#include <ImporterRootDisplay.h>
#include <QTimer>
#include <QFile>
#include <QFileDialog>
#include <QCloseEvent>
#include <QMessageBox>
#include <QDesktopServices>
#include <QUrl>
#include <QDockWidget>
#include <QLabel>
class IXMLDOMDocumentPtr; // Needed for settings.h
class CXTPDockingPaneLayout; // Needed for settings.h
#include <Settings.h>
#include <AzQtComponents/Components/StylesheetPreprocessor.h>
#include <AzToolsFramework/SourceControl/SourceControlAPI.h>
#include <AzCore/Component/ComponentApplicationBus.h>
#include <AzCore/IO/Path/Path.h>
#include <AzCore/std/functional.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzCore/std/string/conversions.h>
#include <Util/PathUtil.h>
#include <ActionOutput.h>
#include <SceneAPI/SceneUI/CommonWidgets/OverlayWidget.h>
#include <SceneAPI/SceneUI/CommonWidgets/ProcessingOverlayWidget.h>
#include <SceneAPI/SceneUI/Handlers/ProcessingHandlers/AsyncOperationProcessingHandler.h>
#include <SceneAPI/SceneUI/Handlers/ProcessingHandlers/ExportJobProcessingHandler.h>
#include <SceneAPI/SceneUI/SceneWidgets/ManifestWidget.h>
#include <SceneAPI/SceneUI/SceneWidgets/SceneGraphInspectWidget.h>
#include <SceneAPI/SceneCore/Events/AssetImportRequest.h>
#include <SceneAPI/SceneCore/Utilities/Reporting.h>
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}");
void MakeUserFriendlySourceAssetPath(QString& out, const QString& sourcePath)
{
char devAssetsRoot[AZ_MAX_PATH_LEN] = { 0 };
if (!gEnv->pFileIO->ResolvePath("@devroot@", devAssetsRoot, AZ_MAX_PATH_LEN))
{
out = sourcePath;
return;
}
AZStd::replace(devAssetsRoot, devAssetsRoot + AZ_MAX_PATH_LEN- 1, AZ_WRONG_FILESYSTEM_SEPARATOR, AZ_CORRECT_FILESYSTEM_SEPARATOR);
// Find if the sourcePathArray is a sub directory of the devAssets folder
// Keep reference to sourcePathArray long enough to use in PathView
QByteArray sourcePathArray = sourcePath.toUtf8();
AZ::IO::PathView sourcePathRootView(sourcePathArray.data());
AZ::IO::PathView devAssetsRootView(devAssetsRoot);
auto [sourcePathIter, devAssetsIter] = AZStd::mismatch(sourcePathRootView.begin(), sourcePathRootView.end(),
devAssetsRootView.begin(), devAssetsRootView.end());
// If the devAssets path iterator is not equal to the end, then there was a mismistch while comparing it
// against the source path indicating that the source path is not a sub-directory
if (devAssetsIter != devAssetsRootView.end())
{
out = sourcePath;
return;
}
int offset = aznumeric_cast<int>(strlen(devAssetsRoot));
if (sourcePath.at(offset) == AZ_CORRECT_FILESYSTEM_SEPARATOR)
{
++offset;
}
out = sourcePath.right(sourcePath.length() - offset);
}
AssetImporterWindow::AssetImporterWindow()
: AssetImporterWindow(nullptr)
{
}
AssetImporterWindow::AssetImporterWindow(QWidget* parent)
: QMainWindow(parent)
, ui(new Ui::AssetImporterWindow)
, m_assetImporterDocument(new AssetImporterDocument())
, m_serializeContext(nullptr)
, m_rootDisplay(nullptr)
, m_overlay(nullptr)
, m_isClosed(false)
, m_processingOverlayIndex(AZ::SceneAPI::UI::OverlayWidget::s_invalidOverlayIndex)
{
Init();
}
AssetImporterWindow::~AssetImporterWindow()
{
AZ_Assert(m_processingOverlayIndex == AZ::SceneAPI::UI::OverlayWidget::s_invalidOverlayIndex,
"Processing overlay (and potentially background thread) still active at destruction.");
AZ_Assert(!m_processingOverlay, "Processing overlay (and potentially background thread) still active at destruction.");
}
void AssetImporterWindow::OpenFile(const AZStd::string& filePath)
{
if (m_processingOverlay)
{
QMessageBox::warning(this, "In progress", "Please wait for the previous task to complete before opening a new file.");
return;
}
if (!m_overlay->CanClose())
{
QMessageBox::warning(this, "In progress", "Unable to close one or more windows at this time.");
return;
}
// Make sure we are not browsing *over* a current editing operation
if (!IsAllowedToChangeSourceFile())
{
// Issue will already have been reported to the user.
return;
}
if (!m_overlay->PopAllLayers())
{
QMessageBox::warning(this, "In progress", "Unable to close one or more windows at this time.");
return;
}
OpenFileInternal(filePath);
}
void AssetImporterWindow::closeEvent(QCloseEvent* ev)
{
if (m_isClosed)
{
return;
}
if (m_processingOverlay)
{
AZ_Assert(m_processingOverlayIndex != AZ::SceneAPI::UI::OverlayWidget::s_invalidOverlayIndex,
"Processing overlay present, but not the index in the overlay for it.");
if (m_processingOverlay->HasProcessingCompleted())
{
if (m_overlay->PopLayer(m_processingOverlayIndex))
{
m_processingOverlayIndex = AZ::SceneAPI::UI::OverlayWidget::s_invalidOverlayIndex;
m_processingOverlay.reset(nullptr);
}
else
{
QMessageBox::critical(this, "Processing In Progress", "Unable to close the result window at this time.",
QMessageBox::Ok, QMessageBox::Ok);
ev->ignore();
return;
}
}
else
{
QMessageBox::critical(this, "Processing In Progress", "Please wait until processing has completed to try again.",
QMessageBox::Ok, QMessageBox::Ok);
ev->ignore();
return;
}
}
if (!m_overlay->CanClose())
{
QMessageBox::critical(this, "Unable to close", "Unable to close one or more windows at this time.",
QMessageBox::Ok, QMessageBox::Ok);
ev->ignore();
return;
}
if (!IsAllowedToChangeSourceFile())
{
ev->ignore();
return;
}
ev->accept();
m_isClosed = true;
}
void AssetImporterWindow::Init()
{
// Serialization and reflection framework setup
EBUS_EVENT_RESULT(m_serializeContext, AZ::ComponentApplicationBus, GetSerializeContext);
AZ_Assert(m_serializeContext, "Serialization context not available");
// Load the style sheets
AzQtComponents::StylesheetPreprocessor styleSheetProcessor(nullptr);
AZStd::string mainWindowQSSPath = Path::GetEditingRootFolder() + "\\Editor\\Styles\\AssetImporterWindow.qss";
QFile mainWindowStyleSheetFile(mainWindowQSSPath.c_str());
if (mainWindowStyleSheetFile.open(QFile::ReadOnly))
{
setStyleSheet(styleSheetProcessor.ProcessStyleSheet(mainWindowStyleSheetFile.readAll()));
}
ui->setupUi(this);
if (!gSettings.enableSceneInspector)
{
ui->m_actionInspect->setVisible(false);
}
ResetMenuAccess(WindowState::InitialNothingLoaded);
// Setup the overlay system, and set the root to be the root display. The root display has the browse,
// the Import button & the cancel button, which are handled here by the window.
m_overlay.reset(aznew AZ::SceneAPI::UI::OverlayWidget(this));
m_rootDisplay.reset(aznew ImporterRootDisplay(m_serializeContext));
connect(m_rootDisplay.data(), &ImporterRootDisplay::UpdateClicked, this, &AssetImporterWindow::UpdateClicked);
connect(m_overlay.data(), &AZ::SceneAPI::UI::OverlayWidget::LayerAdded, this, &AssetImporterWindow::OverlayLayerAdded);
connect(m_overlay.data(), &AZ::SceneAPI::UI::OverlayWidget::LayerRemoved, this, &AssetImporterWindow::OverlayLayerRemoved);
m_overlay->SetRoot(m_rootDisplay.data());
ui->m_mainArea->layout()->addWidget(m_overlay.data());
// Filling the initial browse prompt text to be programmatically set from available extensions
AZStd::unordered_set<AZStd::string> extensions;
EBUS_EVENT(AZ::SceneAPI::Events::AssetImportRequestBus, GetSupportedFileExtensions, extensions);
AZ_Assert(!extensions.empty(), "No file extensions defined for assets.");
if (!extensions.empty())
{
for (AZStd::string& extension : extensions)
{
extension = extension.substr(1);
AZStd::to_upper(extension.begin(), extension.end());
}
AZStd::string joinedExtensions;
AzFramework::StringFunc::Join(joinedExtensions, extensions.begin(), extensions.end(), " or ");
AZStd::string firstLineText =
AZStd::string::format(
"%s files are available for use after placing them in any folder within your game project. "
"These files will automatically be processed and may be accessed via the Asset Browser. <a href=\"%s\">Learn more...</a>",
joinedExtensions.c_str(), s_documentationWebAddress);
ui->m_initialPromptFirstLine->setText(firstLineText.c_str());
AZStd::string secondLineText =
AZStd::string::format("To adjust the %s settings, right-click the file in the Asset Browser and select \"Edit Settings\" from the context menu.", joinedExtensions.c_str());
ui->m_initialPromptSecondLine->setText(secondLineText.c_str());
}
else
{
AZStd::string firstLineText =
AZStd::string::format(
"Files are available for use after placing them in any folder within your game project. "
"These files will automatically be processed and may be accessed via the Asset Browser. <a href=\"%s\">Learn more...</a>", s_documentationWebAddress);
ui->m_initialPromptFirstLine->setText(firstLineText.c_str());
AZStd::string secondLineText = "To adjust the settings, right-click the file in the Asset Browser and select \"Edit Settings\" from the context menu.";
ui->m_initialPromptSecondLine->setText(secondLineText.c_str());
// Hide the initial browse container so we can show the error (it will be shown again when the overlay pops)
ui->m_initialBrowseContainer->hide();
QMessageBox::critical(this, "No Extensions Detected",
"No importable file types were detected. This likely means an internal error has taken place which has broken the "
"registration of valid import types (e.g. FBX). This type of issue requires engineering support.");
}
}
void AssetImporterWindow::OpenFileInternal(const AZStd::string& filePath)
{
using namespace AZ::SceneAPI::SceneUI;
auto asyncLoadHandler = AZStd::make_shared<AZ::SceneAPI::SceneUI::AsyncOperationProcessingHandler>(
s_browseTag,
[this, filePath]()
{
m_assetImporterDocument->LoadScene(filePath);
},
[this]()
{
HandleAssetLoadingCompleted();
}, this);
m_processingOverlay.reset(new ProcessingOverlayWidget(m_overlay.data(), ProcessingOverlayWidget::Layout::Loading, s_browseTag));
m_processingOverlay->SetAndStartProcessingHandler(asyncLoadHandler);
m_processingOverlay->SetAutoCloseOnSuccess(true);
connect(m_processingOverlay.data(), &AZ::SceneAPI::SceneUI::ProcessingOverlayWidget::Closing, this, &AssetImporterWindow::ClearProcessingOverlay);
m_processingOverlayIndex = m_processingOverlay->PushToOverlay();
}
bool AssetImporterWindow::IsAllowedToChangeSourceFile()
{
if (!m_rootDisplay->HasUnsavedChanges())
{
return true;
}
QMessageBox messageBox(QMessageBox::Icon::NoIcon, "Unsaved changes",
"You have unsaved changes. Do you want to discard those changes?",
QMessageBox::StandardButton::Discard | QMessageBox::StandardButton::Cancel, this);
messageBox.exec();
QMessageBox::StandardButton choice = static_cast<QMessageBox::StandardButton>(messageBox.result());
return choice == QMessageBox::StandardButton::Discard;
}
void AssetImporterWindow::UpdateClicked()
{
using namespace AZ::SceneAPI::SceneUI;
// There are specific measures in place to block re-entry, applying asserts to be safe
if (m_processingOverlay)
{
AZ_Assert(!m_processingOverlay, "Attempted to update asset while processing is in progress.");
return;
}
m_processingOverlay.reset(new ProcessingOverlayWidget(m_overlay.data(), ProcessingOverlayWidget::Layout::Exporting, s_browseTag));
connect(m_processingOverlay.data(), &ProcessingOverlayWidget::Closing, this, &AssetImporterWindow::ClearProcessingOverlay);
m_processingOverlayIndex = m_processingOverlay->PushToOverlay();
// We need to block closing of the overlay until source control operations are complete
m_processingOverlay->BlockClosing();
m_processingOverlay->OnSetStatusMessage("Saving settings...");
bool isSourceControlActive = false;
{
using SCRequestBus = AzToolsFramework::SourceControlConnectionRequestBus;
SCRequestBus::BroadcastResult(isSourceControlActive, &SCRequestBus::Events::IsActive);
}
AZStd::shared_ptr<AZ::ActionOutput> output = AZStd::make_shared<AZ::ActionOutput>();
m_assetImporterDocument->SaveScene(output,
[output, this, isSourceControlActive](bool wasSuccessful)
{
if (output->HasAnyWarnings())
{
AZ_TracePrintf(AZ::SceneAPI::Utilities::WarningWindow, "%s", output->BuildWarningMessage().c_str());
}
if (output->HasAnyErrors())
{
AZ_TracePrintf(AZ::SceneAPI::Utilities::ErrorWindow, "%s", output->BuildErrorMessage().c_str());
}
if (wasSuccessful)
{
if (!isSourceControlActive)
{
AZ_TracePrintf(AZ::SceneAPI::Utilities::SuccessWindow, "Saving complete");
}
else
{
AZ_TracePrintf(AZ::SceneAPI::Utilities::SuccessWindow, "Saving & source control operations complete");
}
m_rootDisplay->HandleSaveWasSuccessful();
// Don't attach the job processor until all files are saved.
m_processingOverlay->SetAndStartProcessingHandler(AZStd::make_shared<ExportJobProcessingHandler>(s_browseTag, m_fullSourcePath));
}
else
{
// This kind of failure means that it's possible the jobs will never actually start,
// so we act like the processing is complete to make it so the user won't be stuck
// in the processing UI in that case.
m_processingOverlay->OnProcessingComplete();
}
// Blocking is only used for the period saving is happening. The ExportJobProcessingHandler will inform
// the overlay widget when the AP is done with processing, which will also block closing until done.
m_processingOverlay->UnblockClosing();
}
);
}
void AssetImporterWindow::OnSceneResetRequested()
{
using namespace AZ::SceneAPI::Events;
using namespace AZ::SceneAPI::SceneUI;
using namespace AZ::SceneAPI::Utilities;
auto asyncLoadHandler = AZStd::make_shared<AZ::SceneAPI::SceneUI::AsyncOperationProcessingHandler>(
s_browseTag,
[this]()
{
m_assetImporterDocument->GetScene()->GetManifest().Clear();
AZ::SceneAPI::Events::ProcessingResultCombiner result;
EBUS_EVENT_RESULT(result, AssetImportRequestBus, UpdateManifest, *m_assetImporterDocument->GetScene(),
AssetImportRequest::ManifestAction::ConstructDefault, AssetImportRequest::RequestingApplication::Editor);
// Specifically using success, because ignore would be an invalid case.
// Whenever we do construct default, it should always be done
if (result.GetResult() == ProcessingResult::Success)
{
AZ_TracePrintf(SuccessWindow, "Successfully reset the manifest.");
}
else
{
m_assetImporterDocument->ClearScene();
AZ_TracePrintf(ErrorWindow, "Manifest reset returned in '%s'",
result.GetResult() == ProcessingResult::Failure ? "Failure" : "Ignored");
}
},
[this]()
{
m_rootDisplay->HandleSceneWasReset(m_assetImporterDocument->GetScene());
}, this);
m_processingOverlay.reset(new ProcessingOverlayWidget(m_overlay.data(), ProcessingOverlayWidget::Layout::Resetting, s_browseTag));
m_processingOverlay->SetAndStartProcessingHandler(asyncLoadHandler);
m_processingOverlay->SetAutoCloseOnSuccess(true);
connect(m_processingOverlay.data(), &ProcessingOverlayWidget::Closing, this, &AssetImporterWindow::ClearProcessingOverlay);
m_processingOverlayIndex = m_processingOverlay->PushToOverlay();
}
void AssetImporterWindow::ResetMenuAccess(WindowState state)
{
if (state == WindowState::FileLoaded)
{
ui->m_actionResetSettings->setEnabled(true);
ui->m_actionInspect->setEnabled(true);
}
else
{
ui->m_actionResetSettings->setEnabled(false);
ui->m_actionInspect->setEnabled(false);
}
}
void AssetImporterWindow::OnOpenDocumentation()
{
QDesktopServices::openUrl(QString(s_documentationWebAddress));
}
void AssetImporterWindow::OnInspect()
{
AZ::SceneAPI::UI::OverlayWidgetButtonList buttons;
AZ::SceneAPI::UI::OverlayWidgetButton closeButton;
closeButton.m_text = "Close";
closeButton.m_triggersPop = true;
buttons.push_back(&closeButton);
QLabel* label = new QLabel("Please close the inspector to continue editing the settings.");
label->setWordWrap(true);
label->setAlignment(Qt::AlignCenter);
// make sure the inspector doesn't outlive the AssetImporterWindow, since we own the data it will be inspecting.
auto* theInspectWidget = aznew AZ::SceneAPI::UI::SceneGraphInspectWidget(*m_assetImporterDocument->GetScene());
QObject::connect(this, &QObject::destroyed, theInspectWidget, [theInspectWidget]() { theInspectWidget->window()->close(); } );
m_overlay->PushLayer(label, theInspectWidget, "Scene Inspector", buttons);
}
void AssetImporterWindow::OverlayLayerAdded()
{
setCursor(Qt::WaitCursor);
ResetMenuAccess(WindowState::OverlayShowing);
}
void AssetImporterWindow::OverlayLayerRemoved()
{
if (m_isClosed && !m_overlay->IsAtRoot())
{
return;
}
setCursor(Qt::ArrowCursor);
// Reset menu access
if (m_assetImporterDocument->GetScene())
{
ResetMenuAccess(WindowState::FileLoaded);
}
else
{
ResetMenuAccess(WindowState::InitialNothingLoaded);
ui->m_initialBrowseContainer->show();
m_rootDisplay->hide();
}
}
void AssetImporterWindow::SetTitle(const char* filePath)
{
QWidget* dock = parentWidget();
while (dock)
{
QDockWidget* converted = qobject_cast<QDockWidget*>(dock);
if (converted)
{
AZStd::string extension;
if (AzFramework::StringFunc::Path::GetExtension(filePath, extension, false))
{
extension[0] = toupper(extension[0]);
for (size_t i = 1; i < extension.size(); ++i)
{
extension[i] = tolower(extension[i]);
}
}
else
{
extension = "Scene";
}
AZStd::string fileName;
AzFramework::StringFunc::Path::GetFileName(filePath, fileName);
converted->setWindowTitle(QString("%1 Settings (PREVIEW) - %2").arg(extension.c_str(), fileName.c_str()));
break;
}
else
{
dock = dock->parentWidget();
}
}
}
void AssetImporterWindow::HandleAssetLoadingCompleted()
{
if (!m_assetImporterDocument->GetScene())
{
AZ_TracePrintf(AZ::SceneAPI::Utilities::ErrorWindow, "Failed to load scene.");
return;
}
m_fullSourcePath = m_assetImporterDocument->GetScene()->GetSourceFilename();
SetTitle(m_fullSourcePath.c_str());
QString userFriendlyFileName;
MakeUserFriendlySourceAssetPath(userFriendlyFileName, m_fullSourcePath.c_str());
m_rootDisplay->SetSceneDisplay(userFriendlyFileName, 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
ui->m_initialBrowseContainer->hide();
m_rootDisplay->show();
}
void AssetImporterWindow::ClearProcessingOverlay()
{
m_processingOverlayIndex = AZ::SceneAPI::UI::OverlayWidget::s_invalidOverlayIndex;
m_processingOverlay.reset(nullptr);
}
#include <moc_AssetImporterWindow.cpp>
@@ -0,0 +1,127 @@
#pragma once
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
/////////////////////////////////////////////////////////////////////////////
//
// Asset Importer Qt Interface
//
/////////////////////////////////////////////////////////////////////////////
#if !defined(Q_MOC_RUN)
#include <QMainWindow>
#include <AssetImporterDocument.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#endif
namespace AZStd
{
class thread;
}
namespace Ui
{
class AssetImporterWindow;
}
namespace AZ
{
class Module;
namespace SceneAPI
{
namespace UI
{
class OverlayWidget;
}
namespace SceneUI
{
class ProcessingOverlayWidget;
}
}
}
class ImporterRootDisplay;
class QCloseEvent;
class QMenu;
class QAction;
class AssetImporterWindow
: public QMainWindow
{
Q_OBJECT
public:
AssetImporterWindow();
explicit AssetImporterWindow(QWidget* parent);
~AssetImporterWindow();
// This implementation is required for unregister/register on "RegisterQtViewPane"
static const GUID& GetClassID()
{
// {c50c09d6-5bfa-4d49-8542-e350656ed1bc}
static const GUID guid = {
0xc50c09d6, 0x5bfa, 0x4d49, { 0x85, 0x42, 0xe3, 0x50, 0x65, 0x6e, 0xd1, 0xbc }
};
return guid;
}
void OpenFile(const AZStd::string& filePath);
void closeEvent(QCloseEvent* ev);
public slots:
void OnSceneResetRequested();
void OnOpenDocumentation();
void OnInspect();
private:
void Init();
void OpenFileInternal(const AZStd::string& filePath);
bool IsAllowedToChangeSourceFile();
enum class WindowState
{
InitialNothingLoaded,
FileLoaded,
OverlayShowing
};
void ResetMenuAccess(WindowState state);
void SetTitle(const char* filePath);
void HandleAssetLoadingCompleted();
void ClearProcessingOverlay();
private slots:
void UpdateClicked();
void OverlayLayerAdded();
void OverlayLayerRemoved();
private:
static const AZ::Uuid s_browseTag;
static const char* s_documentationWebAddress;
QScopedPointer<Ui::AssetImporterWindow> ui;
QScopedPointer<AssetImporterDocument> m_assetImporterDocument;
QScopedPointer<AZ::SceneAPI::UI::OverlayWidget> m_overlay;
AZ::SerializeContext* m_serializeContext;
AZStd::string m_fullSourcePath;
QScopedPointer<ImporterRootDisplay> m_rootDisplay;
bool m_isClosed;
int m_processingOverlayIndex;
QSharedPointer<AZ::SceneAPI::SceneUI::ProcessingOverlayWidget> m_processingOverlay;
};
@@ -0,0 +1,220 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AssetImporterWindow</class>
<widget class="QMainWindow" name="AssetImporterWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>320</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>400</width>
<height>320</height>
</size>
</property>
<property name="windowTitle">
<string>Scene Settings (PREVIEW)</string>
</property>
<widget class="QMenuBar" name="menuBar">
<widget class="QMenu" name="editMenu">
<property name="title">
<string>&amp;Edit</string>
</property>
<addaction name="m_actionResetSettings"/>
</widget>
<widget class="QMenu" name="helpMenu">
<property name="title">
<string>&amp;Help</string>
</property>
<addaction name="m_actionInspect"/>
<addaction name="separator"/>
<addaction name="actionOpenDocumentation"/>
</widget>
<addaction name="editMenu"/>
<addaction name="helpMenu"/>
</widget>
<widget class="QWidget" name="m_rootWidget">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QGridLayout" name="rootLayout">
<property name="sizeConstraint">
<enum>QLayout::SetMaximumSize</enum>
</property>
<!-- When an fbx is loaded -->
<item>
<widget class="QWidget" name="m_mainArea">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QGridLayout" name="mainAreaLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
</layout>
</widget>
</item>
<!-- When nothing is loaded -->
<item>
<widget class="QWidget" name="m_initialBrowseContainer">
<property name="leftMargin">
<number>20</number>
</property>
<property name="rightMargin">
<number>30</number>
</property>
<property name="topMargin">
<number>20</number>
</property>
<property name="bottomMargin">
<number>30</number>
</property>
<layout class="QGridLayout" name="initialBrowseLayout">
<!-- Initial Browse Prompt -->
<item row="0">
<widget class="QWidget">
<layout class="QGridLayout">
<!-- First Line -->
<item row="0">
<widget class="QLabel" name="m_initialPromptFirstLine">
<property name="openExternalLinks">
<bool>true</bool>
</property>
<property name="textFormat">
<enum>Qt::TextFormat::RichText</enum>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
</widget>
</item>
<!-- Fixed spacer: Height = 35-->
<item row="1">
<widget class="QWidget">
<property name="minimumHeight">
<number>35</number>
</property>
<property name="maximumHeight">
<number>35</number>
</property>
</widget>
</item>
<!-- Second Line -->
<item row="2">
<widget class="QLabel" name="m_initialPromptSecondLine">
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
<property name="topMargin">
<number>37</number>
</property>
</widget>
</item>
<!-- Spacer -->
<item row="3">
<spacer name="bottomSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>1</width>
<height>1</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<action name="m_actionResetSettings">
<property name="text">
<string>Reset settings...</string>
</property>
<property name="toolTip">
<string>Reset the settings for this file (note: you will have to update to commit)</string>
</property>
</action>
<action name="actionOpenDocumentation">
<property name="text">
<string>Documentation</string>
</property>
</action>
<action name="m_actionInspect">
<property name="text">
<string>Inspect...</string>
</property>
<property name="toolTip">
<string>Inspect the data loaded in greater detail.</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="0"/>
<resources>
<include location="AssetImporter.qrc"/>
</resources>
<connections>
<connection>
<sender>m_actionResetSettings</sender>
<signal>triggered()</signal>
<receiver>AssetImporterWindow</receiver>
<slot>OnSceneResetRequested()</slot>
</connection>
<connection>
<sender>m_actionInspect</sender>
<signal>triggered()</signal>
<receiver>AssetImporterWindow</receiver>
<slot>OnInspect()</slot>
</connection>
<connection>
<sender>actionOpenDocumentation</sender>
<signal>triggered()</signal>
<receiver>AssetImporterWindow</receiver>
<slot>OnOpenDocumentation()</slot>
</connection>
</connections>
</ui>
@@ -0,0 +1,46 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
if(NOT PAL_TRAIT_BUILD_HOST_TOOLS)
return()
endif()
ly_add_target(
NAME EditorAssetImporter MODULE
NAMESPACE Legacy
OUTPUT_SUBDIRECTORY EditorPlugins
AUTOMOC
AUTOUIC
FILES_CMAKE
editorassetimporter_files.cmake
COMPILE_DEFINITIONS
PRIVATE
PLUGIN_EXPORTS
INCLUDE_DIRECTORIES
PUBLIC
.
BUILD_DEPENDENCIES
PRIVATE
3rdParty::Qt::Core
3rdParty::Qt::Widgets
AZ::AzCore
AZ::AzToolsFramework
AZ::SceneCore
AZ::SceneUI
AZ::SceneData
AZ::GFxFramework
Legacy::CryCommon
Legacy::EditorLib
Legacy::EditorCommon
)
ly_add_dependencies(Editor EditorAssetImporter)
set_property(GLOBAL APPEND PROPERTY LY_EDITOR_PLUGINS $<TARGET_FILE_NAME:Legacy::EditorAssetImporter>)
@@ -0,0 +1,15 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include "EditorAssetImporter_precompiled.h"
@@ -0,0 +1,20 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/PlatformDef.h>
/////////////////////////////////////////////////////////////////////////////
// CryTek
/////////////////////////////////////////////////////////////////////////////
#include <platform.h>
@@ -0,0 +1,100 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "EditorAssetImporter_precompiled.h"
#include <ImporterRootDisplay.h>
#include <ui_ImporterRootDisplay.h>
#include <IEditor.h>
#include <SceneAPI/SceneCore/Containers/Scene.h>
#include <SceneAPI/SceneUI/CommonWidgets/OverlayWidget.h>
#include <SceneAPI/SceneUI/SceneWidgets/ManifestWidget.h>
#include <AzCore/Serialization/SerializeContext.h>
ImporterRootDisplay::ImporterRootDisplay(AZ::SerializeContext* serializeContext, QWidget* parent)
: QWidget(parent)
, ui(new Ui::ImporterRootDisplay())
, m_manifestWidget(new AZ::SceneAPI::UI::ManifestWidget(serializeContext))
, m_hasUnsavedChanges(false)
{
ui->setupUi(this);
ui->m_manifestWidgetAreaLayout->addWidget(m_manifestWidget.data());
ui->m_updateButton->setEnabled(false);
ui->m_updateButton->setProperty("class", "Primary");
connect(ui->m_updateButton, &QPushButton::clicked, this, &ImporterRootDisplay::UpdateClicked);
BusConnect();
}
ImporterRootDisplay::~ImporterRootDisplay()
{
BusDisconnect();
delete ui;
}
AZ::SceneAPI::UI::ManifestWidget* ImporterRootDisplay::GetManifestWidget()
{
return m_manifestWidget.data();
}
void ImporterRootDisplay::SetSceneDisplay(const QString& headerText, const AZStd::shared_ptr<AZ::SceneAPI::Containers::Scene>& scene)
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Editor);
if (!scene)
{
AZ_Assert(scene, "No scene provided to display.");
return;
}
ui->m_filePathText->setText(headerText);
HandleSceneWasReset(scene);
ui->m_updateButton->setEnabled(false);
m_hasUnsavedChanges = false;
}
void ImporterRootDisplay::HandleSceneWasReset(const AZStd::shared_ptr<AZ::SceneAPI::Containers::Scene>& scene)
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Editor);
// Don't accept updates while the widget is being filled in.
BusDisconnect();
m_manifestWidget->BuildFromScene(scene);
BusConnect();
}
void ImporterRootDisplay::HandleSaveWasSuccessful()
{
ui->m_updateButton->setEnabled(false);
m_hasUnsavedChanges = false;
}
bool ImporterRootDisplay::HasUnsavedChanges() const
{
return m_hasUnsavedChanges;
}
void ImporterRootDisplay::ObjectUpdated(const AZ::SceneAPI::Containers::Scene& scene, const AZ::SceneAPI::DataTypes::IManifestObject* /*target*/, void* /*sender*/)
{
if (m_manifestWidget)
{
if (&scene == m_manifestWidget->GetScene().get())
{
m_hasUnsavedChanges = true;
ui->m_updateButton->setEnabled(true);
}
}
}
#include <moc_ImporterRootDisplay.cpp>
@@ -0,0 +1,85 @@
#pragma once
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#if !defined(Q_MOC_RUN)
#include <QWidget>
#include <QScopedPointer>
#include <AzCore/std/smart_ptr/shared_ptr.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <SceneAPI/SceneCore/Events/ManifestMetaInfoBus.h>
#include <SceneAPI/SceneUI/SceneUIConfiguration.h>
#endif
class QAction;
class QMenu;
namespace AZ
{
class SerializeContext;
namespace SceneAPI
{
namespace Containers
{
class Scene;
}
namespace UI
{
class ManifestWidget;
}
}
}
namespace Ui
{
class ImporterRootDisplay;
}
class ImporterRootDisplay
: public QWidget
, public AZ::SceneAPI::Events::ManifestMetaInfoBus::Handler
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR(ImporterRootDisplay, AZ::SystemAllocator, 0)
ImporterRootDisplay(AZ::SerializeContext* serializeContext, QWidget* parent = nullptr);
~ImporterRootDisplay();
AZ::SceneAPI::UI::ManifestWidget* GetManifestWidget();
void SetSceneDisplay(const QString& headerText, const AZStd::shared_ptr<AZ::SceneAPI::Containers::Scene>& scene);
void HandleSceneWasReset(const AZStd::shared_ptr<AZ::SceneAPI::Containers::Scene>& scene);
void HandleSaveWasSuccessful();
bool HasUnsavedChanges() const;
signals:
void UpdateClicked();
private:
// ManifestMetaInfoBus
void ObjectUpdated(const AZ::SceneAPI::Containers::Scene& scene, const AZ::SceneAPI::DataTypes::IManifestObject* target, void* sender) override;
Ui::ImporterRootDisplay* ui;
QScopedPointer<AZ::SceneAPI::UI::ManifestWidget> m_manifestWidget;
bool m_hasUnsavedChanges;
};
@@ -0,0 +1,80 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ImporterRootDisplay</class>
<widget class="QWidget" name="ImporterRootDisplay">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>242</width>
<height>136</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QGridLayout" name="mainAreaLayout">
<property name="spacing">
<number>0</number>
</property>
<item row="0" column="0">
<layout class="QGridLayout" name="headerBrowseLayout">
<property name="leftMargin">
<number>6</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="m_filePathText">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>1</width>
<height>0</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">#m_filePathText { margin: 2px; color: grey; }</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="0">
<layout class="QHBoxLayout" name="m_actionControlLayout">
<property name="rightMargin">
<number>6</number>
</property>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="m_updateButton">
<property name="text">
<string>Update</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0">
<layout class="QVBoxLayout" name="m_manifestWidgetAreaLayout"/>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,54 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
/////////////////////////////////////////////////////////////////////////////
//
// Asset Importer Sandbox Plugin Instance Creation
//
/////////////////////////////////////////////////////////////////////////////
#include "EditorAssetImporter_precompiled.h"
#include "AssetImporterPlugin.h"
#if defined(AZ_PLATFORM_WINDOWS)
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif
#include <AzCore/Memory/SystemAllocator.h>
PLUGIN_API IPlugin* CreatePluginInstance(PLUGIN_INIT_PARAM* pInitParam)
{
IEditor* editor = pInitParam->pIEditorInterface;
SetIEditor(editor);
ISystem* system = pInitParam->pIEditorInterface->GetSystem();
ModuleInitISystem(system, "QtAssetImporter");
return new AssetImporterPlugin(editor);
}
#if !defined(AZ_MONOLITHIC_BUILD)
#if defined(AZ_PLATFORM_WINDOWS)
HINSTANCE g_hInstance = 0;
BOOL __stdcall DllMain(HINSTANCE hinstDLL, ULONG fdwReason, [[maybe_unused]] LPVOID lpvReserved)
{
if (fdwReason == DLL_PROCESS_ATTACH)
{
g_hInstance = hinstDLL;
}
return TRUE;
}
#endif
#endif // !defined(AZ_MONOLITHIC_BUILD)
@@ -0,0 +1,149 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "EditorAssetImporter_precompiled.h"
#include <AzCore/IO/SystemFile.h>
#include <AzCore/std/algorithm.h>
#include <AzCore/std/string/conversions.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <AzToolsFramework/Debug/TraceContext.h>
#include <SceneAPI/SceneCore/Events/AssetImportRequest.h>
#include <SceneAPI/SceneCore/Utilities/Reporting.h>
#include <SceneSerializationHandler.h>
namespace AZ
{
SceneSerializationHandler::~SceneSerializationHandler()
{
Deactivate();
}
void SceneSerializationHandler::Activate()
{
BusConnect();
}
void SceneSerializationHandler::Deactivate()
{
BusDisconnect();
}
AZStd::shared_ptr<SceneAPI::Containers::Scene> SceneSerializationHandler::LoadScene(
const AZStd::string& filePath, Uuid sceneSourceGuid)
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Editor);
namespace Utilities = AZ::SceneAPI::Utilities;
using AZ::SceneAPI::Events::AssetImportRequest;
CleanSceneMap();
AZ_TraceContext("File", filePath);
if (!IsValidExtension(filePath))
{
return nullptr;
}
AZStd::string cleanPath = filePath;
if (AzFramework::StringFunc::Path::IsRelative(filePath.c_str()))
{
const char* absolutePath = nullptr;
AzToolsFramework::AssetSystemRequestBus::BroadcastResult(absolutePath,
&AzToolsFramework::AssetSystemRequestBus::Events::GetAbsoluteDevRootFolderPath);
AZ_Assert(absolutePath, "Unable to retrieve the dev folder path");
AzFramework::StringFunc::Path::Join(absolutePath, cleanPath.c_str(), cleanPath);
}
else
{
// Normalizing is not needed if the path is relative as Join(...) will also normalize.
AzFramework::StringFunc::Path::Normalize(cleanPath);
}
auto sceneIt = m_scenes.find(cleanPath);
if (sceneIt != m_scenes.end())
{
AZStd::shared_ptr<SceneAPI::Containers::Scene> scene = sceneIt->second.lock();
if (scene)
{
return scene;
}
// There's a small window in between which the scene was closed after searching for
// it in the scene map. In this case continue and simply reload the scene.
}
if (!AZ::IO::SystemFile::Exists(cleanPath.c_str()))
{
AZ_TracePrintf(Utilities::ErrorWindow, "No file exists at given source path.");
return nullptr;
}
if (sceneSourceGuid.IsNull())
{
bool result = false;
AZ::Data::AssetInfo info;
AZStd::string watchFolder;
AzToolsFramework::AssetSystemRequestBus::BroadcastResult(result, &AzToolsFramework::AssetSystemRequestBus::Events::GetSourceInfoBySourcePath, cleanPath.c_str(), info, watchFolder);
if (!result)
{
AZ_TracePrintf(Utilities::ErrorWindow, "Failed to retrieve file info needed to determine the uuid of the source file.");
return nullptr;
}
sceneSourceGuid = info.m_assetId.m_guid;
}
AZStd::shared_ptr<SceneAPI::Containers::Scene> scene =
AssetImportRequest::LoadSceneFromVerifiedPath(cleanPath, sceneSourceGuid, AssetImportRequest::RequestingApplication::Editor);
if (!scene)
{
AZ_TracePrintf(Utilities::ErrorWindow, "Failed to load the requested scene.");
return nullptr;
}
m_scenes.emplace(AZStd::move(cleanPath), scene);
return scene;
}
bool SceneSerializationHandler::IsValidExtension(const AZStd::string& filePath) const
{
namespace Utilities = AZ::SceneAPI::Utilities;
if (AZ::SceneAPI::Events::AssetImportRequest::IsManifestExtension(filePath.c_str()))
{
AZ_TracePrintf(Utilities::ErrorWindow, "Provided path contains the manifest path, not the path to the source file.");
return false;
}
if (!AZ::SceneAPI::Events::AssetImportRequest::IsSceneFileExtension(filePath.c_str()))
{
AZ_TracePrintf(Utilities::ErrorWindow, "Provided path doesn't contain an extension supported by the SceneAPI.");
return false;
}
return true;
}
void SceneSerializationHandler::CleanSceneMap()
{
for (auto it = m_scenes.begin(); it != m_scenes.end(); )
{
if (it->second.expired())
{
it = m_scenes.erase(it);
}
else
{
++it;
}
}
}
} // namespace AZ
@@ -0,0 +1,41 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/std/containers/unordered_map.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
#include <AzCore/std/smart_ptr/weak_ptr.h>
#include <AzCore/std/string/string.h>
#include <SceneAPI/SceneCore/Events/SceneSerializationBus.h>
namespace AZ
{
class SceneSerializationHandler
: public SceneAPI::Events::SceneSerializationBus::Handler
{
public:
~SceneSerializationHandler() override;
void Activate();
void Deactivate();
AZStd::shared_ptr<SceneAPI::Containers::Scene> LoadScene(
const AZStd::string& sceneFilePath, Uuid sceneSourceGuid) override;
private:
bool IsValidExtension(const AZStd::string& filePath) const;
void CleanSceneMap();
AZStd::unordered_map<AZStd::string, AZStd::weak_ptr<SceneAPI::Containers::Scene>> m_scenes;
};
} // namespace AZ
@@ -0,0 +1,31 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
set(FILES
AssetImporterWindow.h
AssetImporterWindow.cpp
AssetImporterPlugin.h
AssetImporterPlugin.cpp
AssetImporterDocument.h
AssetImporterDocument.cpp
ImporterRootDisplay.h
ImporterRootDisplay.cpp
AssetBrowserContextProvider.h
AssetBrowserContextProvider.cpp
SceneSerializationHandler.h
SceneSerializationHandler.cpp
Main.cpp
EditorAssetImporter_precompiled.cpp
EditorAssetImporter_precompiled.h
AssetImporter.qrc
AssetImporterWindow.ui
ImporterRootDisplay.ui
)