Merge branch 'main' into transform-float-scale

This commit is contained in:
greerdv
2021-05-25 19:21:32 +01:00
58 changed files with 1110 additions and 468 deletions
@@ -26,19 +26,23 @@ TEST_DIRECTORY = os.path.join(os.path.dirname(__file__), "atom_hydra_scripts")
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
@pytest.mark.parametrize("level", ["auto_test"])
class TestAtomEditorComponentsMain(object):
"""Holds tests for Atom components."""
@pytest.mark.test_case_id(
"C32078130", # Display Mapper
"C32078129", # Light
"C32078131", # Radius Weight Modifier
"C32078127", # PostFX Layer
"C32078125", # Physical Sky
"C32078115", # Global Skylight (IBL)
"C32078121", # Exposure Control
"C32078120", # Directional Light
"C32078119", # DepthOfField
"C32078118") # Decal (Atom)
def test_AtomEditorComponents_AddedToEntity(self, request, editor, level, workspace, project, launcher_platform):
"""
Please review the hydra script run by this test for more specific test info.
Tests the following Atom components and verifies all "expected_lines" appear in Editor.log:
1. Display Mapper
2. Light
3. Radius Weight Modifier
4. PostFX Layer
5. Physical Sky
6. Global Skylight (IBL)
7. Exposure Control
8. Directional Light
9. DepthOfField
10. Decal (Atom)
"""
cfg_args = [level]
expected_lines = [
@@ -81,9 +81,20 @@ namespace AzToolsFramework
m_ui->m_assetBrowserTreeViewWidget->SetName("AssetBrowserTreeView_" + name);
bool selectedAsset = false;
for (auto& assetId : selection.GetSelectedAssetIds())
{
m_ui->m_assetBrowserTreeViewWidget->SelectProduct(assetId);
if (assetId.IsValid())
{
selectedAsset = true;
m_ui->m_assetBrowserTreeViewWidget->SelectProduct(assetId);
}
}
if (!selectedAsset)
{
m_ui->m_assetBrowserTreeViewWidget->SelectFolder(selection.GetDefaultDirectory());
}
setWindowTitle(tr("Pick %1").arg(m_selection.GetTitle()));
@@ -93,6 +93,16 @@ namespace AzToolsFramework
m_selectedAssetIds.push_back(selectedAssetId);
}
void AssetSelectionModel::SetDefaultDirectory(AZStd::string_view defaultDirectory)
{
m_defaultDirectory = defaultDirectory;
}
AZStd::string_view AssetSelectionModel::GetDefaultDirectory() const
{
return m_defaultDirectory;
}
AZStd::vector<const AssetBrowserEntry*>& AssetSelectionModel::GetResults()
{
return m_results;
@@ -47,6 +47,9 @@ namespace AzToolsFramework
const AZStd::vector<AZ::Data::AssetId>& GetSelectedAssetIds() const;
void SetSelectedAssetIds(const AZStd::vector<AZ::Data::AssetId>& selectedAssetIds);
void SetSelectedAssetId(const AZ::Data::AssetId& selectedAssetId);
void SetDefaultDirectory(AZStd::string_view defaultDirectory);
AZStd::string_view GetDefaultDirectory() const;
AZStd::vector<const AssetBrowserEntry*>& GetResults();
const AssetBrowserEntry* GetResult();
@@ -72,6 +75,7 @@ namespace AzToolsFramework
AZStd::vector<AZ::Data::AssetId> m_selectedAssetIds;
AZStd::vector<const AssetBrowserEntry*> m_results;
AZStd::string m_defaultDirectory;
QString m_title;
};
@@ -14,6 +14,7 @@
#include <AzCore/Asset/AssetManagerBus.h>
#include <AzCore/Math/Crc.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/StringFunc/StringFunc.h>
#include <AzFramework/StringFunc/StringFunc.h>
@@ -270,7 +271,20 @@ namespace AzToolsFramework
return false;
}
bool AssetBrowserTreeView::SelectEntry(const QModelIndex& idxParent, const AZStd::vector<AZStd::string>& entries, const uint32_t entryPathIndex)
void AssetBrowserTreeView::SelectFolder(AZStd::string_view folderPath)
{
if (folderPath.size() == 0)
{
return;
}
AZStd::vector<AZStd::string> entries;
AZ::StringFunc::Tokenize(folderPath, entries, "/");
SelectEntry(QModelIndex(), entries, 0, true);
}
bool AssetBrowserTreeView::SelectEntry(const QModelIndex& idxParent, const AZStd::vector<AZStd::string>& entries, const uint32_t entryPathIndex, bool useDisplayName)
{
if (entries.empty())
{
@@ -285,30 +299,43 @@ namespace AzToolsFramework
auto rowIdx = model()->index(idx, 0, idxParent);
auto rowEntry = GetEntryFromIndex<AssetBrowserEntry>(rowIdx);
// Check if this entry name matches the query
if (rowEntry && AzFramework::StringFunc::Equal(entry.c_str(), rowEntry->GetName().c_str(), true))
if (rowEntry)
{
// Final entry found - set it as the selected element
if (entryPathIndex == entries.size() - 1)
{
selectionModel()->clear();
selectionModel()->select(rowIdx, QItemSelectionModel::Select);
setCurrentIndex(rowIdx);
return true;
}
// Check if this entry name matches the query
AZStd::string_view compareName = useDisplayName ? (const char*)(rowEntry->GetDisplayName().toUtf8()) : rowEntry->GetName().c_str();
// If this isn't the final entry, it needs to be a folder for the path to be valid (otherwise, early out)
if (rowEntry->GetEntryType() == AssetBrowserEntry::AssetEntryType::Folder)
if (AzFramework::StringFunc::Equal(entry.c_str(), compareName, true))
{
// Folder found - if the final entry is found, expand this folder so the final entry is viewable in the Asset Browser (otherwise, early out)
if (SelectEntry(rowIdx, entries, entryPathIndex + 1))
// Final entry found - set it as the selected element
if (entryPathIndex == entries.size() - 1)
{
expand(rowIdx);
if (rowEntry->GetEntryType() == AssetBrowserEntry::AssetEntryType::Folder)
{
// Expand the item itself if it is a folder
expand(rowIdx);
}
selectionModel()->clear();
selectionModel()->select(rowIdx, QItemSelectionModel::Select);
setCurrentIndex(rowIdx);
return true;
}
// If this isn't the final entry, it needs to be a folder for the path to be valid (otherwise, early out)
if (rowEntry->GetEntryType() == AssetBrowserEntry::AssetEntryType::Folder)
{
// Folder found - if the final entry is found, expand this folder so the final entry is viewable in the Asset
// Browser (otherwise, early out)
if (SelectEntry(rowIdx, entries, entryPathIndex + 1, useDisplayName))
{
expand(rowIdx);
return true;
}
}
return false;
}
return false;
}
}
@@ -60,6 +60,8 @@ namespace AzToolsFramework
AZStd::vector<AssetBrowserEntry*> GetSelectedAssets() const;
void SelectFolder(AZStd::string_view folderPath);
//////////////////////////////////////////////////////////////////////////
// AssetBrowserViewRequestBus
void SelectProduct(AZ::Data::AssetId assetID) override;
@@ -67,6 +69,7 @@ namespace AzToolsFramework
void ClearFilter() override;
void Update() override;
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
@@ -105,7 +108,7 @@ namespace AzToolsFramework
QString m_name;
bool SelectProduct(const QModelIndex& idxParent, AZ::Data::AssetId assetID);
bool SelectEntry(const QModelIndex& idxParent, const AZStd::vector<AZStd::string>& entryPathTokens, const uint32_t entryPathIndex = 0);
bool SelectEntry(const QModelIndex& idxParent, const AZStd::vector<AZStd::string>& entryPathTokens, const uint32_t entryPathIndex = 0, bool useDisplayName = false);
//! Grab one entry from the source thumbnail list and update it
void UpdateSCThumbnails();
@@ -769,6 +769,14 @@ namespace AzToolsFramework
// Request the AssetBrowser Dialog and set a type filter
AssetSelectionModel selection = GetAssetSelectionModel();
selection.SetSelectedAssetId(m_selectedAssetID);
AZStd::string defaultDirectory;
if (m_defaultDirectoryCallback)
{
m_defaultDirectoryCallback->Invoke(m_editNotifyTarget, defaultDirectory);
selection.SetDefaultDirectory(defaultDirectory);
}
AssetBrowserComponentRequestBus::Broadcast(&AssetBrowserComponentRequests::PickAssets, selection, parentWidget());
if (selection.IsValid())
{
@@ -1080,6 +1088,11 @@ namespace AzToolsFramework
m_editNotifyCallback = editNotifyCallback;
}
void PropertyAssetCtrl::SetDefaultDirectoryCallback(DefaultDirectoryCallbackType* callback)
{
m_defaultDirectoryCallback = callback;
}
void PropertyAssetCtrl::SetClearNotifyCallback(ClearCallbackType* clearNotifyCallback)
{
m_clearNotifyCallback = clearNotifyCallback;
@@ -1214,6 +1227,11 @@ namespace AzToolsFramework
GUI->SetTitle(title.c_str());
}
}
else if (attrib == AZ_CRC_CE("DefaultStartingDirectoryCallback"))
{
// This is assumed to be an Asset Browser path to a specific folder to be used as a default by the asset picker if provided
GUI->SetDefaultDirectoryCallback(azdynamic_cast<PropertyAssetCtrl::DefaultDirectoryCallbackType*>(attrValue->GetAttribute()));
}
else if (attrib == AZ_CRC("EditCallback", 0xb74f2ee1))
{
PropertyAssetCtrl::EditCallbackType* func = azdynamic_cast<PropertyAssetCtrl::EditCallbackType*>(attrValue->GetAttribute());
@@ -68,6 +68,7 @@ namespace AzToolsFramework
// This is meant to be used with the "EditCallback" Attribute
using EditCallbackType = AZ::Edit::AttributeFunction<void(const AZ::Data::AssetId&, const AZ::Data::AssetType&)>;
using ClearCallbackType = AZ::Edit::AttributeFunction<void()>;
using DefaultDirectoryCallbackType = AZ::Edit::AttributeFunction<void(AZStd::string&)>;
PropertyAssetCtrl(QWidget *pParent = NULL, QString optionalValidDragDropExtensions = QString());
virtual ~PropertyAssetCtrl();
@@ -119,6 +120,7 @@ namespace AzToolsFramework
EditCallbackType* m_editNotifyCallback = nullptr;
ClearCallbackType* m_clearNotifyCallback = nullptr;
QString m_optionalValidDragDropExtensions;
DefaultDirectoryCallbackType* m_defaultDirectoryCallback = nullptr;
//! The number of characters after which the autocompleter dropdown will be shown.
// Prevents showing too many options.
@@ -196,6 +198,7 @@ namespace AzToolsFramework
void SetEditNotifyTarget(void* editNotifyTarget);
void SetEditNotifyCallback(EditCallbackType* editNotifyCallback); // This is meant to be used with the "EditCallback" Attribute
void SetClearNotifyCallback(ClearCallbackType* clearNotifyCallback); // This is meant to be used with the "ClearNotify" Attribute
void SetDefaultDirectoryCallback(DefaultDirectoryCallbackType* callback); // This is meant to be used with the "DefaultStartingDirectoryCallback" Attribute
void SetEditButtonEnabled(bool enabled);
void SetEditButtonVisible(bool visible);
void SetEditButtonIcon(const QIcon& icon);
+2 -4
View File
@@ -58,13 +58,11 @@
#include "LevelFileDialog.h"
#include "StatObjBus.h"
// LmbrCentral
#include <ModernViewportCameraController.h>
#include <Atom/RPI.Public/ViewportContext.h>
#include <Atom/RPI.Public/ViewportContextBus.h>
#include <LmbrCentral/Rendering/EditorLightComponentBus.h> // for LmbrCentral::EditorLightComponentRequestBus
// LmbrCentral
#include <LmbrCentral/Rendering/EditorLightComponentBus.h> // for LmbrCentral::EditorLightComponentRequestBus
//#define PROFILE_LOADING_WITH_VTUNE
+2 -2
View File
@@ -53,6 +53,7 @@
// AtomToolsFramework
#include <AtomToolsFramework/Viewport/RenderViewportWidget.h>
#include <AtomToolsFramework/Viewport/ModularViewportCameraController.h>
// CryCommon
#include <CryCommon/HMDBus.h>
@@ -75,7 +76,6 @@
#include "EditorPreferencesPageGeneral.h"
#include "ViewportManipulatorController.h"
#include "LegacyViewportCameraController.h"
#include "ModernViewportCameraController.h"
#include "EditorViewportSettings.h"
#include "ViewPane.h"
@@ -1220,7 +1220,7 @@ void EditorViewportWidget::SetViewportId(int id)
{
AzFramework::ReloadCameraKeyBindings();
auto controller = AZStd::make_shared<SandboxEditor::ModernViewportCameraController>();
auto controller = AZStd::make_shared<AtomToolsFramework::ModularViewportCameraController>();
controller->SetCameraListBuilderCallback([](AzFramework::Cameras& cameras)
{
auto firstPersonRotateCamera = AZStd::make_shared<AzFramework::RotateCameraInput>(AzFramework::CameraFreeLookButton);
@@ -823,9 +823,6 @@ set(FILES
ViewportManipulatorController.h
LegacyViewportCameraController.cpp
LegacyViewportCameraController.h
ModernViewportCameraController.cpp
ModernViewportCameraController.h
ModernViewportCameraControllerRequestBus.h
RenderViewport.cpp
RenderViewport.h
TopRendererWnd.cpp
@@ -38,6 +38,7 @@ ly_add_target(
Gem::LmbrCentral
AZ::AtomCore
Gem::Atom_RPI.Public
Gem::AtomToolsFramework.Static
)
ly_add_dependencies(Editor ComponentEntityEditorPlugin)
@@ -66,8 +66,8 @@
#include <Atom/RPI.Public/ViewportContext.h>
#include <Atom/RPI.Public/ViewportContextBus.h>
#include <AtomToolsFramework/Viewport/ModularViewportCameraControllerRequestBus.h>
#include <ModernViewportCameraControllerRequestBus.h>
#include "Objects/ComponentEntityObject.h"
#include "ISourceControl.h"
@@ -1736,9 +1736,9 @@ void SandboxIntegrationManager::GoToEntitiesInViewports(const AzToolsFramework::
const AZ::Transform nextCameraTransform =
AZ::Transform::CreateLookAt(aabb.GetCenter() - (forward * distanceToTarget), aabb.GetCenter());
SandboxEditor::ModernViewportCameraControllerRequestBus::Event(
viewportContext->GetId(), &SandboxEditor::ModernViewportCameraControllerRequestBus::Events::InterpolateToTransform,
nextCameraTransform);
AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event(
viewportContext->GetId(),
&AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::InterpolateToTransform, nextCameraTransform);
}
}
}
@@ -224,7 +224,7 @@ void RCcontrollerTest_Simple::SubmitJob()
// This is a regresssion test to ensure the rccontroller can handle multiple jobs for the same file being completed before
// the APM has a chance to send OnFinishedProcesssingJob events
TEST_F(RCcontrollerTest_Simple, SameJobIsCompletedMultipleTimes_CompletesWithoutError)
TEST_F(RCcontrollerTest_Simple, DISABLED_SameJobIsCompletedMultipleTimes_CompletesWithoutError)
{
using namespace AssetProcessor;
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f82f22df64b93d4bec91e56b60efa3d5ce2915ce388a2dc627f1ab720678e3d5
size 334987
@@ -11,6 +11,9 @@
<file>iOS.svg</file>
<file>Linux.svg</file>
<file>macOS.svg</file>
<file>DefaultProjectImage.png</file>
<file>ArrowDownLine.svg</file>
<file>ArrowUpLine.svg</file>
<file>Backgrounds/FirstTimeBackgroundImage.jpg</file>
</qresource>
</RCC>
@@ -10,7 +10,7 @@
*
*/
#include <ProjectSettingsCtrl.h>
#include <CreateProjectCtrl.h>
#include <ScreensCtrl.h>
#include <PythonBindingsInterface.h>
#include <NewProjectSettingsScreen.h>
@@ -22,7 +22,7 @@
namespace O3DE::ProjectManager
{
ProjectSettingsCtrl::ProjectSettingsCtrl(QWidget* parent)
CreateProjectCtrl::CreateProjectCtrl(QWidget* parent)
: ScreenWidget(parent)
{
QVBoxLayout* vLayout = new QVBoxLayout();
@@ -34,11 +34,11 @@ namespace O3DE::ProjectManager
QDialogButtonBox* backNextButtons = new QDialogButtonBox();
vLayout->addWidget(backNextButtons);
m_backButton = backNextButtons->addButton("Back", QDialogButtonBox::RejectRole);
m_nextButton = backNextButtons->addButton("Next", QDialogButtonBox::ApplyRole);
m_backButton = backNextButtons->addButton(tr("Back"), QDialogButtonBox::RejectRole);
m_nextButton = backNextButtons->addButton(tr("Next"), QDialogButtonBox::ApplyRole);
connect(m_backButton, &QPushButton::pressed, this, &ProjectSettingsCtrl::HandleBackButton);
connect(m_nextButton, &QPushButton::pressed, this, &ProjectSettingsCtrl::HandleNextButton);
connect(m_backButton, &QPushButton::pressed, this, &CreateProjectCtrl::HandleBackButton);
connect(m_nextButton, &QPushButton::pressed, this, &CreateProjectCtrl::HandleNextButton);
m_screensOrder =
{
@@ -47,15 +47,16 @@ namespace O3DE::ProjectManager
};
m_screensCtrl->BuildScreens(m_screensOrder);
m_screensCtrl->ForceChangeToScreen(ProjectManagerScreen::NewProjectSettings, false);
UpdateNextButtonText();
}
ProjectManagerScreen ProjectSettingsCtrl::GetScreenEnum()
ProjectManagerScreen CreateProjectCtrl::GetScreenEnum()
{
return ProjectManagerScreen::NewProjectSettingsCore;
return ProjectManagerScreen::CreateProject;
}
void ProjectSettingsCtrl::HandleBackButton()
void CreateProjectCtrl::HandleBackButton()
{
if (!m_screensCtrl->GotoPreviousScreen())
{
@@ -66,7 +67,7 @@ namespace O3DE::ProjectManager
UpdateNextButtonText();
}
}
void ProjectSettingsCtrl::HandleNextButton()
void CreateProjectCtrl::HandleNextButton()
{
ScreenWidget* currentScreen = m_screensCtrl->GetCurrentScreen();
ProjectManagerScreen screenEnum = currentScreen->GetScreenEnum();
@@ -116,9 +117,14 @@ namespace O3DE::ProjectManager
}
}
void ProjectSettingsCtrl::UpdateNextButtonText()
void CreateProjectCtrl::UpdateNextButtonText()
{
m_nextButton->setText(m_screensCtrl->GetCurrentScreen()->GetNextButtonText());
QString nextButtonText = tr("Next");
if (m_screensCtrl->GetCurrentScreen()->GetScreenEnum() == ProjectManagerScreen::GemCatalog)
{
nextButtonText = tr("Create Project");
}
m_nextButton->setText(nextButtonText);
}
} // namespace O3DE::ProjectManager
@@ -21,12 +21,12 @@
namespace O3DE::ProjectManager
{
class ProjectSettingsCtrl
class CreateProjectCtrl
: public ScreenWidget
{
public:
explicit ProjectSettingsCtrl(QWidget* parent = nullptr);
~ProjectSettingsCtrl() = default;
explicit CreateProjectCtrl(QWidget* parent = nullptr);
~CreateProjectCtrl() = default;
ProjectManagerScreen GetScreenEnum() override;
protected slots:
@@ -21,13 +21,6 @@
namespace O3DE::ProjectManager
{
inline constexpr static int s_contentMargins = 80;
inline constexpr static int s_buttonSpacing = 30;
inline constexpr static int s_iconSize = 24;
inline constexpr static int s_spacerSize = 20;
inline constexpr static int s_boxButtonWidth = 210;
inline constexpr static int s_boxButtonHeight = 280;
FirstTimeUseScreen::FirstTimeUseScreen(QWidget* parent)
: ScreenWidget(parent)
{
@@ -79,8 +72,8 @@ namespace O3DE::ProjectManager
void FirstTimeUseScreen::HandleNewProjectButton()
{
emit ResetScreenRequest(ProjectManagerScreen::NewProjectSettingsCore);
emit ChangeScreenRequest(ProjectManagerScreen::NewProjectSettingsCore);
emit ResetScreenRequest(ProjectManagerScreen::CreateProject);
emit ChangeScreenRequest(ProjectManagerScreen::CreateProject);
}
void FirstTimeUseScreen::HandleAddProjectButton()
{
@@ -37,6 +37,13 @@ namespace O3DE::ProjectManager
QPushButton* m_createProjectButton;
QPushButton* m_addProjectButton;
inline constexpr static int s_contentMargins = 80;
inline constexpr static int s_buttonSpacing = 30;
inline constexpr static int s_iconSize = 24;
inline constexpr static int s_spacerSize = 20;
inline constexpr static int s_boxButtonWidth = 210;
inline constexpr static int s_boxButtonHeight = 280;
};
} // namespace O3DE::ProjectManager
@@ -170,9 +170,4 @@ namespace O3DE::ProjectManager
{
return ProjectManagerScreen::GemCatalog;
}
QString GemCatalogScreen::GetNextButtonText()
{
return "Create Project";
}
} // namespace O3DE::ProjectManager
@@ -28,7 +28,6 @@ namespace O3DE::ProjectManager
explicit GemCatalogScreen(QWidget* parent = nullptr);
~GemCatalogScreen() = default;
ProjectManagerScreen GetScreenEnum() override;
QString GetNextButtonText() override;
private:
QVector<GemInfo> GenerateTestData();
@@ -96,11 +96,6 @@ namespace O3DE::ProjectManager
return ProjectManagerScreen::NewProjectSettings;
}
QString NewProjectSettingsScreen::GetNextButtonText()
{
return tr("Next");
}
void NewProjectSettingsScreen::HandleBrowseButton()
{
QString defaultPath = m_projectPathLineEdit->text();
@@ -28,7 +28,6 @@ namespace O3DE::ProjectManager
explicit NewProjectSettingsScreen(QWidget* parent = nullptr);
~NewProjectSettingsScreen() = default;
ProjectManagerScreen GetScreenEnum() override;
QString GetNextButtonText() override;
ProjectInfo GetProjectInfo();
QString GetProjectTemplatePath();
@@ -0,0 +1,102 @@
/*
* 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 <ProjectButtonWidget.h>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QResizeEvent>
#include <QLabel>
#include <QPushButton>
#include <QPixmap>
#include <QMenu>
#include <QSpacerItem>
//#define SHOW_ALL_PROJECT_ACTIONS
namespace O3DE::ProjectManager
{
inline constexpr static int s_projectImageWidth = 210;
inline constexpr static int s_projectImageHeight = 280;
LabelButton::LabelButton(QWidget* parent)
: QLabel(parent)
{
}
void LabelButton::mousePressEvent([[maybe_unused]] QMouseEvent* event)
{
emit triggered();
}
ProjectButton::ProjectButton(const QString& projectName, QWidget* parent)
: QFrame(parent)
, m_projectName(projectName)
, m_projectImagePath(":/Resources/DefaultProjectImage.png")
{
Setup();
}
ProjectButton::ProjectButton(const QString& projectName, const QString& projectImage, QWidget* parent)
: QFrame(parent)
, m_projectName(projectName)
, m_projectImagePath(projectImage)
{
Setup();
}
void ProjectButton::Setup()
{
QVBoxLayout* vLayout = new QVBoxLayout();
vLayout->setSpacing(0);
vLayout->setContentsMargins(0, 0, 0, 0);
setLayout(vLayout);
m_projectImageLabel = new LabelButton(this);
m_projectImageLabel->setFixedSize(s_projectImageWidth, s_projectImageHeight);
vLayout->addWidget(m_projectImageLabel);
m_projectImageLabel->setPixmap(QPixmap(m_projectImagePath).scaled(m_projectImageLabel->size(), Qt::KeepAspectRatioByExpanding));
QMenu* newProjectMenu = new QMenu(this);
m_editProjectAction = newProjectMenu->addAction(tr("Edit Project Settings..."));
#ifdef SHOW_ALL_PROJECT_ACTIONS
m_editProjectGemsAction = newProjectMenu->addAction(tr("Cutomize Gems..."));
newProjectMenu->addSeparator();
m_copyProjectAction = newProjectMenu->addAction(tr("Duplicate"));
newProjectMenu->addSeparator();
m_removeProjectAction = newProjectMenu->addAction(tr("Remove from O3DE"));
m_deleteProjectAction = newProjectMenu->addAction(tr("Delete the Project"));
#endif
m_projectSettingsMenuButton = new QPushButton(this);
m_projectSettingsMenuButton->setText(m_projectName);
m_projectSettingsMenuButton->setMenu(newProjectMenu);
m_projectSettingsMenuButton->setFocusPolicy(Qt::FocusPolicy::NoFocus);
m_projectSettingsMenuButton->setStyleSheet("font-size: 14px; text-align:left;");
vLayout->addWidget(m_projectSettingsMenuButton);
setFixedSize(s_projectImageWidth, s_projectImageHeight + m_projectSettingsMenuButton->height());
connect(m_projectImageLabel, &LabelButton::triggered, [this]() { emit OpenProject(m_projectName); });
connect(m_editProjectAction, &QAction::triggered, [this]() { emit EditProject(m_projectName); });
#ifdef SHOW_ALL_PROJECT_ACTIONS
connect(m_editProjectGemsAction, &QAction::triggered, [this]() { emit EditProjectGems(m_projectName); });
connect(m_copyProjectAction, &QAction::triggered, [this]() { emit CopyProject(m_projectName); });
connect(m_removeProjectAction, &QAction::triggered, [this]() { emit RemoveProject(m_projectName); });
connect(m_deleteProjectAction, &QAction::triggered, [this]() { emit DeleteProject(m_projectName); });
#endif
}
} // namespace O3DE::ProjectManager
@@ -0,0 +1,73 @@
/*
* 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
#if !defined(Q_MOC_RUN)
#include <QFrame>
#include <QLabel>
#endif
QT_FORWARD_DECLARE_CLASS(QPixmap)
QT_FORWARD_DECLARE_CLASS(QPushButton)
QT_FORWARD_DECLARE_CLASS(QAction)
namespace O3DE::ProjectManager
{
class LabelButton
: public QLabel
{
Q_OBJECT // AUTOMOC
public:
explicit LabelButton(QWidget* parent = nullptr);
~LabelButton() = default;
signals:
void triggered();
public slots:
void mousePressEvent(QMouseEvent* event) override;
};
class ProjectButton
: public QFrame
{
Q_OBJECT // AUTOMOC
public:
explicit ProjectButton(const QString& projectName, QWidget* parent = nullptr);
explicit ProjectButton(const QString& projectName, const QString& projectImage, QWidget* parent = nullptr);
~ProjectButton() = default;
signals:
void OpenProject(const QString& projectName);
void EditProject(const QString& projectName);
void EditProjectGems(const QString& projectName);
void CopyProject(const QString& projectName);
void RemoveProject(const QString& projectName);
void DeleteProject(const QString& projectName);
private:
void Setup();
QString m_projectName;
QString m_projectImagePath;
LabelButton* m_projectImageLabel;
QPushButton* m_projectSettingsMenuButton;
QAction* m_editProjectAction;
QAction* m_editProjectGemsAction;
QAction* m_copyProjectAction;
QAction* m_removeProjectAction;
QAction* m_deleteProjectAction;
};
} // namespace O3DE::ProjectManager
@@ -50,9 +50,9 @@ namespace O3DE::ProjectManager
QVector<ProjectManagerScreen> screenEnums =
{
ProjectManagerScreen::FirstTimeUse,
ProjectManagerScreen::NewProjectSettingsCore,
ProjectManagerScreen::CreateProject,
ProjectManagerScreen::ProjectsHome,
ProjectManagerScreen::ProjectSettings,
ProjectManagerScreen::UpdateProject,
ProjectManagerScreen::EngineSettings
};
m_screensCtrl->BuildScreens(screenEnums);
@@ -30,6 +30,23 @@ namespace O3DE::ProjectManager
return ProjectManagerScreen::ProjectSettings;
}
ProjectInfo ProjectSettingsScreen::GetProjectInfo()
{
// Impl pending next PR
return ProjectInfo();
}
void ProjectSettingsScreen::SetProjectInfo()
{
// Impl pending next PR
}
bool ProjectSettingsScreen::Validate()
{
// Impl pending next PR
return true;
}
void ProjectSettingsScreen::HandleGemsButton()
{
emit ChangeScreenRequest(ProjectManagerScreen::GemCatalog);
@@ -13,6 +13,7 @@
#if !defined(Q_MOC_RUN)
#include <ScreenWidget.h>
#include <ProjectInfo.h>
#endif
namespace Ui
@@ -30,6 +31,11 @@ namespace O3DE::ProjectManager
~ProjectSettingsScreen() = default;
ProjectManagerScreen GetScreenEnum() override;
ProjectInfo GetProjectInfo();
void SetProjectInfo();
bool Validate();
protected slots:
void HandleGemsButton();
@@ -12,21 +12,103 @@
#include <ProjectsHomeScreen.h>
#include <Source/ui_ProjectsHomeScreen.h>
#include <ProjectButtonWidget.h>
#include <PythonBindingsInterface.h>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QGridLayout>
#include <QLabel>
#include <QPushButton>
#include <QMenu>
#include <QListView>
#include <QSpacerItem>
#include <QListWidget>
#include <QListWidgetItem>
#include <QFileInfo>
#include <QScrollArea>
namespace O3DE::ProjectManager
{
ProjectsHomeScreen::ProjectsHomeScreen(QWidget* parent)
: ScreenWidget(parent)
, m_ui(new Ui::ProjectsHomeClass())
{
m_ui->setupUi(this);
QVBoxLayout* vLayout = new QVBoxLayout();
setLayout(vLayout);
vLayout->setContentsMargins(s_contentMargins, s_contentMargins, s_contentMargins, s_contentMargins);
connect(m_ui->newProjectButton, &QPushButton::pressed, this, &ProjectsHomeScreen::HandleNewProjectButton);
connect(m_ui->addProjectButton, &QPushButton::pressed, this, &ProjectsHomeScreen::HandleAddProjectButton);
connect(m_ui->editProjectButton, &QPushButton::pressed, this, &ProjectsHomeScreen::HandleEditProjectButton);
QHBoxLayout* topLayout = new QHBoxLayout();
QLabel* titleLabel = new QLabel(this);
titleLabel->setText("My Projects");
titleLabel->setStyleSheet("font-size: 24px");
topLayout->addWidget(titleLabel);
QSpacerItem* topSpacer = new QSpacerItem(s_spacerSize, s_spacerSize, QSizePolicy::Expanding, QSizePolicy::Minimum);
topLayout->addItem(topSpacer);
QMenu* newProjectMenu = new QMenu(this);
m_createNewProjectAction = newProjectMenu->addAction("Create New Project");
m_addExistingProjectAction = newProjectMenu->addAction("Add Existing Project");
QPushButton* newProjectMenuButton = new QPushButton(this);
newProjectMenuButton->setText("New Project...");
newProjectMenuButton->setMenu(newProjectMenu);
newProjectMenuButton->setFixedWidth(s_newProjectButtonWidth);
newProjectMenuButton->setStyleSheet("font-size: 14px;");
topLayout->addWidget(newProjectMenuButton);
vLayout->addLayout(topLayout);
// Get all projects and create a horizontal scrolling list of them
auto projectsResult = PythonBindingsInterface::Get()->GetProjects();
if (projectsResult.IsSuccess() && !projectsResult.GetValue().isEmpty())
{
QScrollArea* projectsScrollArea = new QScrollArea(this);
QWidget* scrollWidget = new QWidget();
QGridLayout* projectGridLayout = new QGridLayout();
scrollWidget->setLayout(projectGridLayout);
projectsScrollArea->setWidget(scrollWidget);
projectsScrollArea->setWidgetResizable(true);
int gridIndex = 0;
for (auto project : projectsResult.GetValue())
{
ProjectButton* projectButton;
QString projectPreviewPath = project.m_path + m_projectPreviewImagePath;
QFileInfo doesPreviewExist(projectPreviewPath);
if (doesPreviewExist.exists() && doesPreviewExist.isFile())
{
projectButton = new ProjectButton(project.m_projectName, projectPreviewPath, this);
}
else
{
projectButton = new ProjectButton(project.m_projectName, this);
}
// Create rows of projects buttons s_projectButtonRowCount buttons wide
projectGridLayout->addWidget(projectButton, gridIndex / s_projectButtonRowCount, gridIndex % s_projectButtonRowCount);
connect(projectButton, &ProjectButton::OpenProject, this, &ProjectsHomeScreen::HandleOpenProject);
connect(projectButton, &ProjectButton::EditProject, this, &ProjectsHomeScreen::HandleEditProject);
#ifdef SHOW_ALL_PROJECT_ACTIONS
connect(projectButton, &ProjectButton::EditProjectGems, this, &ProjectsHomeScreen::HandleEditProjectGems);
connect(projectButton, &ProjectButton::CopyProject, this, &ProjectsHomeScreen::HandleCopyProject);
connect(projectButton, &ProjectButton::RemoveProject, this, &ProjectsHomeScreen::HandleRemoveProject);
connect(projectButton, &ProjectButton::DeleteProject, this, &ProjectsHomeScreen::HandleDeleteProject);
#endif
++gridIndex;
}
vLayout->addWidget(projectsScrollArea);
}
// Using border-image allows for scaling options background-image does not support
setStyleSheet("O3DE--ProjectManager--ScreenWidget { border-image: url(:/Backgrounds/FirstTimeBackgroundImage.jpg) repeat repeat; }");
connect(m_createNewProjectAction, &QAction::triggered, this, &ProjectsHomeScreen::HandleNewProjectButton);
connect(m_addExistingProjectAction, &QAction::triggered, this, &ProjectsHomeScreen::HandleAddProjectButton);
}
ProjectManagerScreen ProjectsHomeScreen::GetScreenEnum()
@@ -36,16 +118,41 @@ namespace O3DE::ProjectManager
void ProjectsHomeScreen::HandleNewProjectButton()
{
emit ResetScreenRequest(ProjectManagerScreen::NewProjectSettingsCore);
emit ChangeScreenRequest(ProjectManagerScreen::NewProjectSettingsCore);
emit ResetScreenRequest(ProjectManagerScreen::CreateProject);
emit ChangeScreenRequest(ProjectManagerScreen::CreateProject);
}
void ProjectsHomeScreen::HandleAddProjectButton()
{
// Do nothing for now
}
void ProjectsHomeScreen::HandleEditProjectButton()
void ProjectsHomeScreen::HandleOpenProject(const QString& projectPath)
{
emit ChangeScreenRequest(ProjectManagerScreen::ProjectSettings);
// Open the editor with this project open
emit NotifyCurrentProject(projectPath);
}
void ProjectsHomeScreen::HandleEditProject(const QString& projectPath)
{
emit NotifyCurrentProject(projectPath);
emit ResetScreenRequest(ProjectManagerScreen::UpdateProject);
emit ChangeScreenRequest(ProjectManagerScreen::UpdateProject);
}
void ProjectsHomeScreen::HandleEditProjectGems(const QString& projectPath)
{
emit NotifyCurrentProject(projectPath);
emit ChangeScreenRequest(ProjectManagerScreen::GemCatalog);
}
void ProjectsHomeScreen::HandleCopyProject([[maybe_unused]] const QString& projectPath)
{
// Open file dialog and choose location for copied project then register copy with O3DE
}
void ProjectsHomeScreen::HandleRemoveProject([[maybe_unused]] const QString& projectPath)
{
// Unregister Project from O3DE
}
void ProjectsHomeScreen::HandleDeleteProject([[maybe_unused]] const QString& projectPath)
{
// Remove project from 03DE and delete from disk
ProjectsHomeScreen::HandleRemoveProject(projectPath);
}
} // namespace O3DE::ProjectManager
@@ -15,11 +15,6 @@
#include <ScreenWidget.h>
#endif
namespace Ui
{
class ProjectsHomeClass;
}
namespace O3DE::ProjectManager
{
class ProjectsHomeScreen
@@ -34,10 +29,23 @@ namespace O3DE::ProjectManager
protected slots:
void HandleNewProjectButton();
void HandleAddProjectButton();
void HandleEditProjectButton();
void HandleOpenProject(const QString& projectPath);
void HandleEditProject(const QString& projectPath);
void HandleEditProjectGems(const QString& projectPath);
void HandleCopyProject(const QString& projectPath);
void HandleRemoveProject(const QString& projectPath);
void HandleDeleteProject(const QString& projectPath);
private:
QScopedPointer<Ui::ProjectsHomeClass> m_ui;
QAction* m_createNewProjectAction;
QAction* m_addExistingProjectAction;
const QString m_projectPreviewImagePath = "/preview.png";
inline constexpr static int s_contentMargins = 80;
inline constexpr static int s_spacerSize = 20;
inline constexpr static int s_projectButtonRowCount = 4;
inline constexpr static int s_newProjectButtonWidth = 156;
};
} // namespace O3DE::ProjectManager
@@ -1,137 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ProjectsHomeClass</class>
<widget class="QWidget" name="ProjectsHomeClass">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>826</width>
<height>585</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>My Projects</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="currentProjectButton">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="newProjectButton">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../Resources/ProjectManager.qrc">
<normaloff>:/Add.svg</normaloff>:/Add.svg</iconset>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="addProjectButton">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../Resources/ProjectManager.qrc">
<normaloff>:/Select_Folder.svg</normaloff>:/Select_Folder.svg</iconset>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QToolButton" name="editProjectButton">
<property name="text">
<string>Edit Project</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Open a Project</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources>
<include location="../Resources/ProjectManager.qrc"/>
</resources>
<connections/>
</ui>
@@ -540,7 +540,8 @@ namespace O3DE::ProjectManager
ProjectInfo PythonBindings::ProjectInfoFromPath(pybind11::handle path)
{
ProjectInfo projectInfo;
projectInfo.m_path = Py_To_String(path);
projectInfo.m_path = Py_To_String(path);
projectInfo.m_isNew = false;
auto projectData = m_registration.attr("get_project_data")(pybind11::none(), path);
if (pybind11::isinstance<pybind11::dict>(projectData))
@@ -18,10 +18,11 @@ namespace O3DE::ProjectManager
Invalid = -1,
Empty,
FirstTimeUse,
NewProjectSettingsCore,
CreateProject,
NewProjectSettings,
GemCatalog,
ProjectsHome,
UpdateProject,
ProjectSettings,
EngineSettings
};
@@ -12,7 +12,8 @@
#include <ScreenFactory.h>
#include <FirstTimeUseScreen.h>
#include <ProjectSettingsCtrl.h>
#include <CreateProjectCtrl.h>
#include <UpdateProjectCtrl.h>
#include <NewProjectSettingsScreen.h>
#include <GemCatalog/GemCatalogScreen.h>
#include <ProjectsHomeScreen.h>
@@ -30,8 +31,8 @@ namespace O3DE::ProjectManager
case (ProjectManagerScreen::FirstTimeUse):
newScreen = new FirstTimeUseScreen(parent);
break;
case (ProjectManagerScreen::NewProjectSettingsCore):
newScreen = new ProjectSettingsCtrl(parent);
case (ProjectManagerScreen::CreateProject):
newScreen = new CreateProjectCtrl(parent);
break;
case (ProjectManagerScreen::NewProjectSettings):
newScreen = new NewProjectSettingsScreen(parent);
@@ -42,6 +43,9 @@ namespace O3DE::ProjectManager
case (ProjectManagerScreen::ProjectsHome):
newScreen = new ProjectsHomeScreen(parent);
break;
case (ProjectManagerScreen::UpdateProject):
newScreen = new UpdateProjectCtrl(parent);
break;
case (ProjectManagerScreen::ProjectSettings):
newScreen = new ProjectSettingsScreen(parent);
break;
@@ -41,15 +41,12 @@ namespace O3DE::ProjectManager
{
return true;
}
virtual QString GetNextButtonText()
{
return "Next";
}
signals:
void ChangeScreenRequest(ProjectManagerScreen screen);
void GotoPreviousScreenRequest();
void ResetScreenRequest(ProjectManagerScreen screen);
void NotifyCurrentProject(const QString& projectPath);
};
} // namespace O3DE::ProjectManager
@@ -117,6 +117,7 @@ namespace O3DE::ProjectManager
connect(newScreen, &ScreenWidget::ChangeScreenRequest, this, &ScreensCtrl::ChangeToScreen);
connect(newScreen, &ScreenWidget::GotoPreviousScreenRequest, this, &ScreensCtrl::GotoPreviousScreen);
connect(newScreen, &ScreenWidget::ResetScreenRequest, this, &ScreensCtrl::ResetScreen);
connect(newScreen, &ScreenWidget::NotifyCurrentProject, this, &ScreensCtrl::NotifyCurrentProject);
}
void ScreensCtrl::ResetAllScreens()
@@ -35,6 +35,9 @@ namespace O3DE::ProjectManager
ScreenWidget* FindScreen(ProjectManagerScreen screen);
ScreenWidget* GetCurrentScreen();
signals:
void NotifyCurrentProject(const QString& projectPath);
public slots:
bool ChangeToScreen(ProjectManagerScreen screen);
bool ForceChangeToScreen(ProjectManagerScreen screen, bool addVisit = true);
@@ -0,0 +1,139 @@
/*
* 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 <UpdateProjectCtrl.h>
#include <ScreensCtrl.h>
#include <PythonBindingsInterface.h>
#include <ProjectSettingsScreen.h>
#include <QDialogButtonBox>
#include <QVBoxLayout>
#include <QPushButton>
#include <QMessageBox>
namespace O3DE::ProjectManager
{
UpdateProjectCtrl::UpdateProjectCtrl(QWidget* parent)
: ScreenWidget(parent)
{
QVBoxLayout* vLayout = new QVBoxLayout();
setLayout(vLayout);
m_screensCtrl = new ScreensCtrl();
vLayout->addWidget(m_screensCtrl);
QDialogButtonBox* backNextButtons = new QDialogButtonBox();
vLayout->addWidget(backNextButtons);
m_backButton = backNextButtons->addButton(tr("Back"), QDialogButtonBox::RejectRole);
m_nextButton = backNextButtons->addButton(tr("Next"), QDialogButtonBox::ApplyRole);
connect(m_backButton, &QPushButton::pressed, this, &UpdateProjectCtrl::HandleBackButton);
connect(m_nextButton, &QPushButton::pressed, this, &UpdateProjectCtrl::HandleNextButton);
connect(reinterpret_cast<ScreensCtrl*>(parent), &ScreensCtrl::NotifyCurrentProject, this, &UpdateProjectCtrl::UpdateCurrentProject);
m_screensOrder =
{
ProjectManagerScreen::ProjectSettings,
ProjectManagerScreen::GemCatalog
};
m_screensCtrl->BuildScreens(m_screensOrder);
m_screensCtrl->ForceChangeToScreen(ProjectManagerScreen::ProjectSettings, false);
UpdateNextButtonText();
}
ProjectManagerScreen UpdateProjectCtrl::GetScreenEnum()
{
return ProjectManagerScreen::UpdateProject;
}
void UpdateProjectCtrl::HandleBackButton()
{
if (!m_screensCtrl->GotoPreviousScreen())
{
emit GotoPreviousScreenRequest();
}
else
{
UpdateNextButtonText();
}
}
void UpdateProjectCtrl::HandleNextButton()
{
ScreenWidget* currentScreen = m_screensCtrl->GetCurrentScreen();
ProjectManagerScreen screenEnum = currentScreen->GetScreenEnum();
auto screenOrderIter = m_screensOrder.begin();
for (; screenOrderIter != m_screensOrder.end(); ++screenOrderIter)
{
if (*screenOrderIter == screenEnum)
{
++screenOrderIter;
break;
}
}
if (screenEnum == ProjectManagerScreen::ProjectSettings)
{
auto projectScreen = reinterpret_cast<ProjectSettingsScreen*>(currentScreen);
if (projectScreen)
{
if (!projectScreen->Validate())
{
QMessageBox::critical(this, tr("Invalid project settings"), tr("Invalid project settings"));
return;
}
m_projectInfo = projectScreen->GetProjectInfo();
}
}
if (screenOrderIter != m_screensOrder.end())
{
m_screensCtrl->ChangeToScreen(*screenOrderIter);
UpdateNextButtonText();
}
else
{
auto result = PythonBindingsInterface::Get()->UpdateProject(m_projectInfo);
if (result)
{
emit ChangeScreenRequest(ProjectManagerScreen::ProjectsHome);
}
else
{
QMessageBox::critical(this, tr("Project update failed"), tr("Failed to update project."));
}
}
}
void UpdateProjectCtrl::UpdateCurrentProject(const QString& projectPath)
{
auto projectResult = PythonBindingsInterface::Get()->GetProject(projectPath);
if (projectResult.IsSuccess())
{
m_projectInfo = projectResult.GetValue();
}
}
void UpdateProjectCtrl::UpdateNextButtonText()
{
QString nextButtonText = tr("Continue");
if (m_screensCtrl->GetCurrentScreen()->GetScreenEnum() == ProjectManagerScreen::GemCatalog)
{
nextButtonText = tr("Update Project");
}
m_nextButton->setText(nextButtonText);
}
} // namespace O3DE::ProjectManager
@@ -0,0 +1,51 @@
/*
* 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
#if !defined(Q_MOC_RUN)
#include "ProjectInfo.h"
#include <ScreenWidget.h>
#include <ScreensCtrl.h>
#include <QPushButton>
#endif
namespace O3DE::ProjectManager
{
class UpdateProjectCtrl
: public ScreenWidget
{
public:
explicit UpdateProjectCtrl(QWidget* parent = nullptr);
~UpdateProjectCtrl() = default;
ProjectManagerScreen GetScreenEnum() override;
protected slots:
void HandleBackButton();
void HandleNextButton();
void UpdateCurrentProject(const QString& projectPath);
private:
void UpdateNextButtonText();
ScreensCtrl* m_screensCtrl;
QPushButton* m_backButton;
QPushButton* m_nextButton;
QVector<ProjectManagerScreen> m_screensOrder;
ProjectInfo m_projectInfo;
ProjectManagerScreen m_screenEnum;
};
} // namespace O3DE::ProjectManager
@@ -41,16 +41,19 @@ set(FILES
Source/ProjectInfo.cpp
Source/NewProjectSettingsScreen.h
Source/NewProjectSettingsScreen.cpp
Source/ProjectSettingsCtrl.h
Source/ProjectSettingsCtrl.cpp
Source/CreateProjectCtrl.h
Source/CreateProjectCtrl.cpp
Source/UpdateProjectCtrl.h
Source/UpdateProjectCtrl.cpp
Source/ProjectsHomeScreen.h
Source/ProjectsHomeScreen.cpp
Source/ProjectsHomeScreen.ui
Source/ProjectSettingsScreen.h
Source/ProjectSettingsScreen.cpp
Source/ProjectSettingsScreen.ui
Source/EngineSettingsScreen.h
Source/EngineSettingsScreen.cpp
Source/ProjectButtonWidget.h
Source/ProjectButtonWidget.cpp
Source/LinkWidget.h
Source/LinkWidget.cpp
Source/TagWidget.h
@@ -41,6 +41,18 @@ namespace AZ
static AZ::SceneAPI::FbxSceneImporter::FbxImportRequestHandler* g_fbxImporter = nullptr;
static AZStd::vector<AZ::ComponentDescriptor*> g_componentDescriptors;
void Initialize()
{
// Currently it's still needed to explicitly create an instance of this instead of letting
// it be a normal component. This is because ResourceCompilerScene needs to return
// the list of available extensions before it can start the application.
if (!g_fbxImporter)
{
g_fbxImporter = aznew AZ::SceneAPI::FbxSceneImporter::FbxImportRequestHandler();
g_fbxImporter->Activate();
}
}
void Reflect(AZ::SerializeContext* /*context*/)
{
// Descriptor registration is done in Reflect instead of Initialize because the ResourceCompilerScene initializes the libraries before
@@ -52,7 +64,6 @@ namespace AZ
{
// Global importer and behavior
g_componentDescriptors.push_back(FbxSceneBuilder::FbxImporter::CreateDescriptor());
g_componentDescriptors.push_back(FbxSceneImporter::FbxImportRequestHandler::CreateDescriptor());
// Node and attribute importers
g_componentDescriptors.push_back(AssImpBitangentStreamImporter::CreateDescriptor());
@@ -114,11 +125,7 @@ namespace AZ
extern "C" AZ_DLL_EXPORT void InitializeDynamicModule(void* env)
{
AZ::Environment::Attach(static_cast<AZ::EnvironmentInstance>(env));
if (!AZ::SceneAPI::FbxSceneBuilder::g_fbxImporter)
{
AZ::SceneAPI::FbxSceneBuilder::g_fbxImporter = aznew AZ::SceneAPI::FbxSceneImporter::FbxImportRequestHandler();
AZ::SceneAPI::FbxSceneBuilder::g_fbxImporter->Activate();
}
AZ::SceneAPI::FbxSceneBuilder::Initialize();
}
extern "C" AZ_DLL_EXPORT void Reflect(AZ::SerializeContext* context)
{
@@ -10,16 +10,12 @@
*
*/
#include <AssetProcessor/AssetBuilderSDK/AssetBuilderSDK/AssetBuilderSDK.h>
#include <AzCore/Serialization/EditContextConstants.inl>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/Json/JsonSerialization.h>
#include <AzCore/Settings/SettingsRegistry.h>
#include <AzCore/StringFunc/StringFunc.h>
#include <SceneAPI/FbxSceneBuilder/FbxImportRequestHandler.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <SceneAPI/SceneCore/Containers/Scene.h>
#include <SceneAPI/SceneCore/Events/CallProcessorBus.h>
#include <SceneAPI/SceneCore/Events/ImportEventContext.h>
#include <SceneAPI/FbxSceneBuilder/FbxImportRequestHandler.h>
namespace AZ
{
@@ -27,23 +23,10 @@ namespace AZ
{
namespace FbxSceneImporter
{
void SceneImporterSettings::Reflect(AZ::ReflectContext* context)
{
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context); serializeContext)
{
serializeContext->Class<SceneImporterSettings>()
->Version(1)
->Field("SupportedFileTypeExtensions", &SceneImporterSettings::m_supportedFileTypeExtensions);
}
}
const char* FbxImportRequestHandler::s_extension = ".fbx";
void FbxImportRequestHandler::Activate()
{
if (auto* settingsRegistry = AZ::SettingsRegistry::Get())
{
settingsRegistry->GetObject(m_settings, "/O3DE/SceneAPI/AssetImporter");
}
BusConnect();
}
@@ -54,38 +37,21 @@ namespace AZ
void FbxImportRequestHandler::Reflect(ReflectContext* context)
{
SceneImporterSettings::Reflect(context);
SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<FbxImportRequestHandler, AZ::Component>()->Version(1)->Attribute(
AZ::Edit::Attributes::SystemComponentTags,
AZStd::vector<AZ::Crc32>({AssetBuilderSDK::ComponentTags::AssetBuilder}));
serializeContext->Class<FbxImportRequestHandler, SceneCore::BehaviorComponent>()->Version(1);
}
}
void FbxImportRequestHandler::GetSupportedFileExtensions(AZStd::unordered_set<AZStd::string>& extensions)
{
// It's unlikely an empty file extension list is intentional,
// so if it's empty, try reloading it from the registry.
if (m_settings.m_supportedFileTypeExtensions.empty())
{
if (auto* settingsRegistry = AZ::SettingsRegistry::Get())
{
settingsRegistry->GetObject(m_settings, "/O3DE/SceneAPI/AssetImporter");
}
}
extensions.insert(m_settings.m_supportedFileTypeExtensions.begin(), m_settings.m_supportedFileTypeExtensions.end());
extensions.insert(s_extension);
}
Events::LoadingResult FbxImportRequestHandler::LoadAsset(Containers::Scene& scene, const AZStd::string& path, const Uuid& guid, [[maybe_unused]] RequestingApplication requester)
{
AZStd::string extension;
StringFunc::Path::GetExtension(path.c_str(), extension);
if (!m_settings.m_supportedFileTypeExtensions.contains(extension))
if (!AzFramework::StringFunc::Path::IsExtension(path.c_str(), s_extension))
{
return Events::LoadingResult::Ignored;
}
@@ -107,11 +73,6 @@ namespace AZ
return Events::LoadingResult::AssetFailure;
}
}
void FbxImportRequestHandler::GetProvidedServices(ComponentDescriptor::DependencyArrayType& provided)
{
provided.emplace_back(AZ_CRC_CE("AssetImportRequestHandler"));
}
} // namespace Import
} // namespace SceneAPI
} // namespace AZ
@@ -21,21 +21,12 @@ namespace AZ
{
namespace FbxSceneImporter
{
struct SceneImporterSettings
{
AZ_TYPE_INFO(SceneImporterSettings, "{8BB6C7AD-BF99-44DC-9DA1-E7AD3F03DC10}");
static void Reflect(AZ::ReflectContext* context);
AZStd::unordered_set<AZStd::string> m_supportedFileTypeExtensions;
};
class FbxImportRequestHandler
: public AZ::Component
: public SceneCore::BehaviorComponent
, public Events::AssetImportRequestBus::Handler
{
public:
AZ_COMPONENT(FbxImportRequestHandler, "{9F4B189C-0A96-4F44-A5F0-E087FF1561F8}");
AZ_COMPONENT(FbxImportRequestHandler, "{9F4B189C-0A96-4F44-A5F0-E087FF1561F8}", SceneCore::BehaviorComponent);
~FbxImportRequestHandler() override = default;
@@ -47,13 +38,8 @@ namespace AZ
Events::LoadingResult LoadAsset(Containers::Scene& scene, const AZStd::string& path, const Uuid& guid,
RequestingApplication requester) override;
static void GetProvidedServices(ComponentDescriptor::DependencyArrayType& provided);
private:
SceneImporterSettings m_settings;
static constexpr const char* SettingsFilename = "AssetImporterSettings.json";
static const char* s_extension;
};
} // namespace FbxSceneImporter
} // namespace SceneAPI
@@ -97,6 +97,11 @@ namespace AZ
return m_configFilePath.c_str();
}
void Application::QueryApplicationType(AZ::ApplicationTypeQuery& appType) const
{
appType.m_maskValue = AZ::ApplicationTypeQuery::Masks::Tool;
}
void Application::SetSettingsRegistrySpecializations(AZ::SettingsRegistryInterface::Specializations& specializations)
{
AZ::ComponentApplication::SetSettingsRegistrySpecializations(specializations);
@@ -28,6 +28,7 @@ namespace AZ
const char* GetConfigFilePath() const;
AZ::ComponentTypeList GetRequiredSystemComponents() const override;
void QueryApplicationType(AZ::ApplicationTypeQuery& appType) const override;
protected:
void SetSettingsRegistrySpecializations(AZ::SettingsRegistryInterface::Specializations& specializations) override;
@@ -25,8 +25,11 @@
#include <AzCore/std/containers/unordered_set.h>
#include <AzCore/Utils/Utils.h>
#include <AzFramework/Archive/IArchive.h>
#include <AzFramework/Asset/AssetSystemBus.h>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <AzToolsFramework/Prefab/PrefabDomUtils.h>
#include <AzToolsFramework/Prefab/EditorPrefabComponent.h>
#include <AzToolsFramework/Prefab/PrefabPublicInterface.h>
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
#include <AzToolsFramework/Entity/PrefabEditorEntityOwnershipInterface.h>
#include <AzToolsFramework/ToolsComponents/TransformComponent.h>
@@ -36,7 +39,6 @@
// SliceConverter reads in a slice file (saved in an ObjectStream format), instantiates it, creates a prefab out of the data,
// and saves the prefab in a JSON format. This can be used for one-time migrations of slices or slice-based levels to prefabs.
// This converter is still in an early state. It can convert trivial slices, but it cannot handle nested slices yet.
//
// If the slice contains legacy data, it will print out warnings / errors about the data that couldn't be serialized.
// The prefab will be generated without that data.
@@ -70,6 +72,20 @@ namespace AZ
AZ_Error("Convert-Slice", false, "No json registration context found.");
return false;
}
// Connect to the Asset Processor so that we can get the correct source path to any nested slice references.
if (!ConnectToAssetProcessor())
{
AZ_Error("Convert-Slice", false, " Failed to connect to the Asset Processor.\n");
return false;
}
// Load the asset catalog so that we can find any nested assets successfully. We also need to tick the tick bus
// so that the OnCatalogLoaded event gets processed now, instead of during application shutdown.
AZ::Data::AssetCatalogRequestBus::Broadcast(
&AZ::Data::AssetCatalogRequestBus::Events::LoadCatalog, "@assets@/assetcatalog.xml");
application.Tick();
AZStd::string logggingScratchBuffer;
SetupLogging(logggingScratchBuffer, convertSettings.m_reporting, *commandLine);
@@ -80,83 +96,90 @@ namespace AZ
verifySettings.m_serializeContext = application.GetSerializeContext();
SetupLogging(logggingScratchBuffer, verifySettings.m_reporting, *commandLine);
auto archiveInterface = AZ::Interface<AZ::IO::IArchive>::Get();
// Find the Prefab System Component for use in creating and saving the prefab
AZ::Entity* systemEntity = application.FindEntity(AZ::SystemEntityId);
AZ_Assert(systemEntity != nullptr, "System entity doesn't exist.");
auto prefabSystemComponent = systemEntity->FindComponent<AzToolsFramework::Prefab::PrefabSystemComponent>();
AZ_Assert(prefabSystemComponent != nullptr, "Prefab System component doesn't exist");
bool result = true;
rapidjson::StringBuffer scratchBuffer;
// Loop through the list of requested files and convert them.
AZStd::vector<AZStd::string> fileList = Utilities::ReadFileListFromCommandLine(application, "files");
for (AZStd::string& filePath : fileList)
{
bool packOpened = false;
AZ::IO::Path outputPath = filePath;
outputPath.ReplaceExtension("prefab");
AZ_Printf("Convert-Slice", "------------------------------------------------------------------------------------------\n");
AZ_Printf("Convert-Slice", "Converting '%s' to '%s'\n", filePath.c_str(), outputPath.c_str());
AZ::IO::Path inputPath = filePath;
auto fileExtension = inputPath.Extension();
if (fileExtension == ".ly")
{
// Special case: for level files, we need to open the .ly zip file and convert the levelentities.editor_xml file
// inside of it. All the other files can be ignored as they are deprecated legacy system files that are no longer
// loaded with prefab-based levels.
packOpened = archiveInterface->OpenPack(filePath);
inputPath.ReplaceFilename("levelentities.editor_xml");
AZ_Warning("Convert-Slice", packOpened, " '%s' could not be opened as a pack file.\n", filePath.c_str());
}
else
{
AZ_Warning(
"Convert-Slice", (fileExtension == ".slice"),
" Warning: Only .ly and .slice files are supported, conversion of '%.*s' may not work.\n",
AZ_STRING_ARG(fileExtension.Native()));
}
auto callback = [prefabSystemComponent, &outputPath, isDryRun]
(void* classPtr, const Uuid& classId, [[maybe_unused]] SerializeContext* context)
{
if (classId != azrtti_typeid<AZ::Entity>())
{
AZ_Printf("Convert-Slice", " File not converted: Slice root is not an entity.\n");
return false;
}
AZ::Entity* rootEntity = reinterpret_cast<AZ::Entity*>(classPtr);
return ConvertSliceFile(prefabSystemComponent, outputPath, isDryRun, rootEntity);
};
if (!Utilities::InspectSerializedFile(inputPath.c_str(), convertSettings.m_serializeContext, callback))
{
AZ_Warning("Convert-Slice", false, "Failed to load '%s'. File may not contain an object stream.", inputPath.c_str());
result = false;
}
if (packOpened)
{
[[maybe_unused]] bool closeResult = archiveInterface->ClosePack(filePath);
AZ_Warning("Convert-Slice", closeResult, "Failed to close '%s'.", filePath.c_str());
}
AZ_Printf("Convert-Slice", "Finished converting '%s' to '%s'\n", filePath.c_str(), outputPath.c_str());
AZ_Printf("Convert-Slice", "------------------------------------------------------------------------------------------\n");
bool convertResult = ConvertSliceFile(convertSettings.m_serializeContext, filePath, isDryRun);
result = result && convertResult;
}
DisconnectFromAssetProcessor();
return result;
}
bool SliceConverter::ConvertSliceFile(
AzToolsFramework::Prefab::PrefabSystemComponent* prefabSystemComponent, AZ::IO::PathView outputPath, bool isDryRun,
AZ::Entity* rootEntity)
AZ::SerializeContext* serializeContext, const AZStd::string& slicePath, bool isDryRun)
{
bool result = true;
bool packOpened = false;
auto archiveInterface = AZ::Interface<AZ::IO::IArchive>::Get();
AZ::IO::Path outputPath = slicePath;
outputPath.ReplaceExtension("prefab");
AZ_Printf("Convert-Slice", "------------------------------------------------------------------------------------------\n");
AZ_Printf("Convert-Slice", "Converting '%s' to '%s'\n", slicePath.c_str(), outputPath.c_str());
AZ::IO::Path inputPath = slicePath;
auto fileExtension = inputPath.Extension();
if (fileExtension == ".ly")
{
// Special case: for level files, we need to open the .ly zip file and convert the levelentities.editor_xml file
// inside of it. All the other files can be ignored as they are deprecated legacy system files that are no longer
// loaded with prefab-based levels.
packOpened = archiveInterface->OpenPack(slicePath);
inputPath.ReplaceFilename("levelentities.editor_xml");
AZ_Warning("Convert-Slice", packOpened, " '%s' could not be opened as a pack file.\n", slicePath.c_str());
}
else
{
AZ_Warning(
"Convert-Slice", (fileExtension == ".slice"),
" Warning: Only .ly and .slice files are supported, conversion of '%.*s' may not work.\n",
AZ_STRING_ARG(fileExtension.Native()));
}
auto callback = [&outputPath, isDryRun](void* classPtr, const Uuid& classId, SerializeContext* context)
{
if (classId != azrtti_typeid<AZ::Entity>())
{
AZ_Printf("Convert-Slice", " File not converted: Slice root is not an entity.\n");
return false;
}
AZ::Entity* rootEntity = reinterpret_cast<AZ::Entity*>(classPtr);
return ConvertSliceToPrefab(context, outputPath, isDryRun, rootEntity);
};
// Read in the slice file and call the callback on completion to convert the read-in slice to a prefab.
if (!Utilities::InspectSerializedFile(inputPath.c_str(), serializeContext, callback))
{
AZ_Warning("Convert-Slice", false, "Failed to load '%s'. File may not contain an object stream.", inputPath.c_str());
result = false;
}
if (packOpened)
{
[[maybe_unused]] bool closeResult = archiveInterface->ClosePack(slicePath);
AZ_Warning("Convert-Slice", closeResult, "Failed to close '%s'.", slicePath.c_str());
}
AZ_Printf("Convert-Slice", "Finished converting '%s' to '%s'\n", slicePath.c_str(), outputPath.c_str());
AZ_Printf("Convert-Slice", "------------------------------------------------------------------------------------------\n");
return result;
}
bool SliceConverter::ConvertSliceToPrefab(
AZ::SerializeContext* serializeContext, AZ::IO::PathView outputPath, bool isDryRun, AZ::Entity* rootEntity)
{
auto prefabSystemComponentInterface = AZ::Interface<AzToolsFramework::Prefab::PrefabSystemComponentInterface>::Get();
// Find the slice from the root entity.
SliceComponent* sliceComponent = AZ::EntityUtils::FindFirstDerivedComponent<SliceComponent>(rootEntity);
if (sliceComponent == nullptr)
@@ -167,44 +190,21 @@ namespace AZ
// Get all of the entities from the slice.
SliceComponent::EntityList sliceEntities = sliceComponent->GetNewEntities();
if (sliceEntities.empty())
{
AZ_Printf("Convert-Slice", " File not converted: Slice entities could not be retrieved.\n");
return false;
}
AZ_Warning("Convert-Slice", sliceComponent->GetSlices().empty(), " Slice depends on other slices, this conversion will lose data.\n");
AZ_Printf("Convert-Slice", " Slice contains %zu entities.\n", sliceEntities.size());
// Create the Prefab with the entities from the slice
AZStd::unique_ptr<AzToolsFramework::Prefab::Instance> sourceInstance(
prefabSystemComponent->CreatePrefab(sliceEntities, {}, outputPath));
prefabSystemComponentInterface->CreatePrefab(sliceEntities, {}, outputPath));
// Dispatch events here, because prefab creation might trigger asset loads in rare circumstances.
AZ::Data::AssetManager::Instance().DispatchEvents();
// Set up the Prefab container entity to be a proper Editor entity. (This logic is normally triggered
// via an EditorRequests EBus in CreatePrefab, but the subsystem that listens for it isn't present in this tool.)
// Fix up the container entity to have the proper components and fix up the slice entities to have the proper hierarchy
// with the container as the top-most parent.
AzToolsFramework::Prefab::EntityOptionalReference container = sourceInstance->GetContainerEntity();
AzToolsFramework::EditorEntityContextRequestBus::Broadcast(
&AzToolsFramework::EditorEntityContextRequestBus::Events::AddRequiredComponents, container->get());
container->get().AddComponent(aznew AzToolsFramework::Prefab::EditorPrefabComponent());
// Reparent any root-level slice entities to the container entity.
for (auto entity : sliceEntities)
{
AzToolsFramework::Components::TransformComponent* transformComponent =
entity->FindComponent<AzToolsFramework::Components::TransformComponent>();
if (transformComponent)
{
if (!transformComponent->GetParentId().IsValid())
{
transformComponent->SetParent(container->get().GetId());
}
}
}
FixPrefabEntities(container->get(), sliceEntities);
auto templateId = sourceInstance->GetTemplateId();
if (templateId == AzToolsFramework::Prefab::InvalidTemplateId)
{
AZ_Printf("Convert-Slice", " Path error. Path could be invalid, or the prefab may not be loaded in this level.\n");
@@ -219,14 +219,27 @@ namespace AZ
AZ_Printf("Convert-Slice", " Failed to convert prefab instance data to a PrefabDom.\n");
return false;
}
prefabSystemComponent->UpdatePrefabTemplate(templateId, prefabDom);
prefabSystemComponentInterface->UpdatePrefabTemplate(templateId, prefabDom);
// Dispatch events here, because prefab serialization might trigger asset loads in rare circumstances.
AZ::Data::AssetManager::Instance().DispatchEvents();
// If this slice has nested slices, we need to loop through those, convert them to prefabs as well, and
// set up the new nesting relationships correctly.
const SliceComponent::SliceList& sliceList = sliceComponent->GetSlices();
AZ_Printf("Convert-Slice", " Slice contains %zu nested slices.\n", sliceList.size());
if (!sliceList.empty())
{
bool nestedSliceResult = ConvertNestedSlices(sliceComponent, sourceInstance.get(), serializeContext, isDryRun);
if (!nestedSliceResult)
{
return false;
}
}
if (isDryRun)
{
PrintPrefab(prefabDom, sourceInstance->GetTemplateSourcePath());
PrintPrefab(templateId);
return true;
}
else
@@ -235,8 +248,187 @@ namespace AZ
}
}
void SliceConverter::PrintPrefab(const AzToolsFramework::Prefab::PrefabDom& prefabDom, const AZ::IO::Path& templatePath)
void SliceConverter::FixPrefabEntities(AZ::Entity& containerEntity, SliceComponent::EntityList& sliceEntities)
{
// Set up the Prefab container entity to be a proper Editor entity. (This logic is normally triggered
// via an EditorRequests EBus in CreatePrefab, but the subsystem that listens for it isn't present in this tool.)
AzToolsFramework::EditorEntityContextRequestBus::Broadcast(
&AzToolsFramework::EditorEntityContextRequestBus::Events::AddRequiredComponents, containerEntity);
containerEntity.AddComponent(aznew AzToolsFramework::Prefab::EditorPrefabComponent());
// Reparent any root-level slice entities to the container entity.
for (auto entity : sliceEntities)
{
AzToolsFramework::Components::TransformComponent* transformComponent =
entity->FindComponent<AzToolsFramework::Components::TransformComponent>();
if (transformComponent)
{
if (!transformComponent->GetParentId().IsValid())
{
transformComponent->SetParent(containerEntity.GetId());
transformComponent->UpdateCachedWorldTransform();
}
}
}
}
bool SliceConverter::ConvertNestedSlices(
SliceComponent* sliceComponent, AzToolsFramework::Prefab::Instance* sourceInstance,
AZ::SerializeContext* serializeContext, bool isDryRun)
{
const SliceComponent::SliceList& sliceList = sliceComponent->GetSlices();
auto prefabSystemComponentInterface = AZ::Interface<AzToolsFramework::Prefab::PrefabSystemComponentInterface>::Get();
for (auto& slice : sliceList)
{
// Get the nested slice asset
auto sliceAsset = slice.GetSliceAsset();
sliceAsset.QueueLoad();
sliceAsset.BlockUntilLoadComplete();
// The slice list gives us asset IDs, and we need to get to the source path. So first we get the asset path from the ID,
// then we get the source path from the asset path.
AZStd::string processedAssetPath;
AZ::Data::AssetCatalogRequestBus::BroadcastResult(
processedAssetPath, &AZ::Data::AssetCatalogRequests::GetAssetPathById, sliceAsset.GetId());
AZStd::string assetPath;
AzToolsFramework::AssetSystemRequestBus::Broadcast(
&AzToolsFramework::AssetSystemRequestBus::Events::GetFullSourcePathFromRelativeProductPath,
processedAssetPath, assetPath);
if (assetPath.empty())
{
AZ_Warning("Convert-Slice", false,
" Source path for nested slice '%s' could not be found, slice not converted.", processedAssetPath.c_str());
return false;
}
// Now, convert the nested slice to a prefab.
bool nestedSliceResult = ConvertSliceFile(serializeContext, assetPath, isDryRun);
if (!nestedSliceResult)
{
AZ_Warning("Convert-Slice", nestedSliceResult, " Nested slice '%s' could not be converted.", assetPath.c_str());
return false;
}
// Load the prefab template for the newly-created nested prefab.
// To get the template, we need to take our absolute slice path and turn it into a project-relative prefab path.
AZ::IO::Path nestedPrefabPath = assetPath;
nestedPrefabPath.ReplaceExtension("prefab");
auto prefabLoaderInterface = AZ::Interface<AzToolsFramework::Prefab::PrefabLoaderInterface>::Get();
nestedPrefabPath = prefabLoaderInterface->GetRelativePathToProject(nestedPrefabPath);
AzToolsFramework::Prefab::TemplateId nestedTemplateId =
prefabSystemComponentInterface->GetTemplateIdFromFilePath(nestedPrefabPath);
AzToolsFramework::Prefab::TemplateReference nestedTemplate =
prefabSystemComponentInterface->FindTemplate(nestedTemplateId);
// For each slice instance of the nested slice, convert it to a nested prefab instance instead.
auto instances = slice.GetInstances();
AZ_Printf(
"Convert-Slice", " Attaching %zu instances of nested slice '%s'.\n", instances.size(),
nestedPrefabPath.Native().c_str());
for (auto& instance : instances)
{
bool instanceConvertResult = ConvertSliceInstance(instance, sliceAsset, nestedTemplate, sourceInstance);
if (!instanceConvertResult)
{
return false;
}
}
}
return true;
}
bool SliceConverter::ConvertSliceInstance(
[[maybe_unused]] AZ::SliceComponent::SliceInstance& instance,
[[maybe_unused]] AZ::Data::Asset<AZ::SliceAsset>& sliceAsset,
AzToolsFramework::Prefab::TemplateReference nestedTemplate,
AzToolsFramework::Prefab::Instance* topLevelInstance)
{
auto instanceToTemplateInterface = AZ::Interface<AzToolsFramework::Prefab::InstanceToTemplateInterface>::Get();
auto prefabSystemComponentInterface = AZ::Interface<AzToolsFramework::Prefab::PrefabSystemComponentInterface>::Get();
// Create a new unmodified prefab Instance for the nested slice instance.
auto nestedInstance = AZStd::make_unique<AzToolsFramework::Prefab::Instance>();
AzToolsFramework::Prefab::Instance::EntityList newEntities;
if (!AzToolsFramework::Prefab::PrefabDomUtils::LoadInstanceFromPrefabDom(
*nestedInstance, newEntities, nestedTemplate->get().GetPrefabDom()))
{
AZ_Error(
"Convert-Slice", false, " Failed to load and instantiate nested Prefab Template '%s'.",
nestedTemplate->get().GetFilePath().c_str());
return false;
}
// Get the DOM for the unmodified nested instance. This will be used later below for generating the correct patch
// to the top-level template DOM.
AzToolsFramework::Prefab::PrefabDom unmodifiedNestedInstanceDom;
instanceToTemplateInterface->GenerateDomForInstance(unmodifiedNestedInstanceDom, *(nestedInstance.get()));
// Currently, DataPatch conversions for nested slices aren't implemented, so all nested slice overrides will
// be lost.
AZ_Warning(
"Convert-Slice", false, " Nested slice instances will lose all of their override data during conversion.",
nestedTemplate->get().GetFilePath().c_str());
// Set the container entity of the nested prefab to have the top-level prefab as the parent.
// Once DataPatch conversions are supported, this will need to change to nest the prefab under the appropriate entity
// within the level.
auto containerEntity = nestedInstance->GetContainerEntity();
AzToolsFramework::Components::TransformComponent* transformComponent =
containerEntity->get().FindComponent<AzToolsFramework::Components::TransformComponent>();
if (transformComponent)
{
transformComponent->SetParent(topLevelInstance->GetContainerEntityId());
transformComponent->UpdateCachedWorldTransform();
}
// Add the nested instance itself to the top-level prefab. To do this, we need to add it to our top-level instance,
// create a patch out of it, and patch the top-level prefab template.
AzToolsFramework::Prefab::PrefabDom topLevelInstanceDomBefore;
instanceToTemplateInterface->GenerateDomForInstance(topLevelInstanceDomBefore, *topLevelInstance);
AzToolsFramework::Prefab::Instance& addedInstance = topLevelInstance->AddInstance(AZStd::move(nestedInstance));
AzToolsFramework::Prefab::PrefabDom topLevelInstanceDomAfter;
instanceToTemplateInterface->GenerateDomForInstance(topLevelInstanceDomAfter, *topLevelInstance);
AzToolsFramework::Prefab::PrefabDom addedInstancePatch;
instanceToTemplateInterface->GeneratePatch(addedInstancePatch, topLevelInstanceDomBefore, topLevelInstanceDomAfter);
instanceToTemplateInterface->PatchTemplate(addedInstancePatch, topLevelInstance->GetTemplateId());
// Get the DOM for the modified nested instance. Now that the data has been fixed up, and the instance has been added
// to the top-level instance, we've got all the changes we need to generate the correct patch.
AzToolsFramework::Prefab::PrefabDom modifiedNestedInstanceDom;
instanceToTemplateInterface->GenerateDomForInstance(modifiedNestedInstanceDom, addedInstance);
AzToolsFramework::Prefab::PrefabDom linkPatch;
instanceToTemplateInterface->GeneratePatch(linkPatch, unmodifiedNestedInstanceDom, modifiedNestedInstanceDom);
prefabSystemComponentInterface->CreateLink(
topLevelInstance->GetTemplateId(), addedInstance.GetTemplateId(), addedInstance.GetInstanceAlias(), linkPatch,
AzToolsFramework::Prefab::InvalidLinkId);
prefabSystemComponentInterface->PropagateTemplateChanges(topLevelInstance->GetTemplateId());
return true;
}
void SliceConverter::PrintPrefab(AzToolsFramework::Prefab::TemplateId templateId)
{
auto prefabSystemComponentInterface = AZ::Interface<AzToolsFramework::Prefab::PrefabSystemComponentInterface>::Get();
auto prefabTemplate = prefabSystemComponentInterface->FindTemplate(templateId);
auto& prefabDom = prefabTemplate->get().GetPrefabDom();
const AZ::IO::Path& templatePath = prefabTemplate->get().GetFilePath();
rapidjson::StringBuffer prefabBuffer;
rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(prefabBuffer);
prefabDom.Accept(writer);
@@ -260,5 +452,41 @@ namespace AZ
return true;
}
bool SliceConverter::ConnectToAssetProcessor()
{
AzFramework::AssetSystem::ConnectionSettings connectionSettings;
AzFramework::AssetSystem::ReadConnectionSettingsFromSettingsRegistry(connectionSettings);
connectionSettings.m_launchAssetProcessorOnFailedConnection = true;
connectionSettings.m_connectionDirection =
AzFramework::AssetSystem::ConnectionSettings::ConnectionDirection::ConnectToAssetProcessor;
connectionSettings.m_connectionIdentifier = AzFramework::AssetSystem::ConnectionIdentifiers::Editor;
connectionSettings.m_loggingCallback = [](AZStd::string_view logData)
{
AZ_Printf("Convert-Slice", "%.*s\n", AZ_STRING_ARG(logData));
};
bool connectedToAssetProcessor = false;
AzFramework::AssetSystemRequestBus::BroadcastResult(
connectedToAssetProcessor, &AzFramework::AssetSystemRequestBus::Events::EstablishAssetProcessorConnection,
connectionSettings);
return connectedToAssetProcessor;
}
void SliceConverter::DisconnectFromAssetProcessor()
{
AzFramework::AssetSystemRequestBus::Broadcast(
&AzFramework::AssetSystem::AssetSystemRequests::StartDisconnectingAssetProcessor);
// Wait for the disconnect to finish.
bool disconnected = false;
AzFramework::AssetSystemRequestBus::BroadcastResult(disconnected,
&AzFramework::AssetSystem::AssetSystemRequests::WaitUntilAssetProcessorDisconnected, AZStd::chrono::seconds(30));
AZ_Error("Convert-Slice", disconnected, "Asset Processor failed to disconnect successfully.");
}
} // namespace SerializeContextTools
} // namespace AZ
@@ -42,11 +42,20 @@ namespace AZ
static bool ConvertSliceFiles(Application& application);
private:
static bool ConnectToAssetProcessor();
static void DisconnectFromAssetProcessor();
static bool ConvertSliceFile(AzToolsFramework::Prefab::PrefabSystemComponent* prefabSystemComponent,
AZ::IO::PathView outputPath, bool isDryRun, AZ::Entity* rootEntity);
static void PrintPrefab(const AzToolsFramework::Prefab::PrefabDom& prefabDom, const AZ::IO::Path& templatePath);
static bool ConvertSliceFile(AZ::SerializeContext* serializeContext, const AZStd::string& slicePath, bool isDryRun);
static bool ConvertSliceToPrefab(
AZ::SerializeContext* serializeContext, AZ::IO::PathView outputPath, bool isDryRun, AZ::Entity* rootEntity);
static void FixPrefabEntities(AZ::Entity& containerEntity, SliceComponent::EntityList& sliceEntities);
static bool ConvertNestedSlices(
SliceComponent* sliceComponent, AzToolsFramework::Prefab::Instance* sourceInstance,
AZ::SerializeContext* serializeContext, bool isDryRun);
static bool SliceConverter::ConvertSliceInstance(
AZ::SliceComponent::SliceInstance& instance, AZ::Data::Asset<AZ::SliceAsset>& sliceAsset,
AzToolsFramework::Prefab::TemplateReference nestedTemplate, AzToolsFramework::Prefab::Instance* topLevelInstance);
static void PrintPrefab(AzToolsFramework::Prefab::TemplateId templateId);
static bool SavePrefab(AzToolsFramework::Prefab::TemplateId templateId);
};
} // namespace SerializeContextTools
@@ -12,17 +12,16 @@
#pragma once
#include <ModernViewportCameraControllerRequestBus.h>
#include <Atom/RPI.Public/ViewportContext.h>
#include <AtomToolsFramework/Viewport/ModularViewportCameraControllerRequestBus.h>
#include <AzFramework/Entity/EntityDebugDisplayBus.h>
#include <AzFramework/Viewport/CameraInput.h>
#include <AzFramework/Viewport/MultiViewportController.h>
namespace SandboxEditor
namespace AtomToolsFramework
{
class ModernViewportCameraControllerInstance;
class ModernViewportCameraController
class ModularViewportCameraController
: public AzFramework::MultiViewportController<
ModernViewportCameraControllerInstance, AzFramework::ViewportControllerPriority::DispatchToAllPriorities>
{
@@ -39,19 +38,19 @@ namespace SandboxEditor
};
class ModernViewportCameraControllerInstance final
: public AzFramework::MultiViewportControllerInstanceInterface<ModernViewportCameraController>,
public ModernViewportCameraControllerRequestBus::Handler,
: public AzFramework::MultiViewportControllerInstanceInterface<ModularViewportCameraController>,
public ModularViewportCameraControllerRequestBus::Handler,
private AzFramework::ViewportDebugDisplayEventBus::Handler
{
public:
explicit ModernViewportCameraControllerInstance(AzFramework::ViewportId viewportId, ModernViewportCameraController* controller);
explicit ModernViewportCameraControllerInstance(AzFramework::ViewportId viewportId, ModularViewportCameraController* controller);
~ModernViewportCameraControllerInstance() override;
// MultiViewportControllerInstanceInterface overrides ...
bool HandleInputChannelEvent(const AzFramework::ViewportControllerInputEvent& event) override;
void UpdateViewport(const AzFramework::ViewportControllerUpdateEvent& event) override;
// ModernViewportCameraControllerRequestBus overrides ...
// ModularViewportCameraControllerRequestBus overrides ...
void InterpolateToTransform(const AZ::Transform& worldFromLocal) override;
private:
@@ -76,4 +75,4 @@ namespace SandboxEditor
AZ::RPI::ViewportContext::MatrixChangedEvent::Handler m_cameraViewMatrixChangeHandler;
};
} // namespace SandboxEditor
} // namespace AtomToolsFramework
@@ -20,11 +20,11 @@ namespace AZ
class Transform;
}
namespace SandboxEditor
namespace AtomToolsFramework
{
//! Provides an interface to control the modern viewport camera controller from the Editor.
//! @note The bus is addressed by viewport id.
class ModernViewportCameraControllerRequests : public AZ::EBusTraits
class ModularViewportCameraControllerRequests : public AZ::EBusTraits
{
public:
using BusIdType = AzFramework::ViewportId;
@@ -35,8 +35,8 @@ namespace SandboxEditor
virtual void InterpolateToTransform(const AZ::Transform& worldFromLocal) = 0;
protected:
~ModernViewportCameraControllerRequests() = default;
~ModularViewportCameraControllerRequests() = default;
};
using ModernViewportCameraControllerRequestBus = AZ::EBus<ModernViewportCameraControllerRequests>;
} // namespace SandboxEditor
using ModularViewportCameraControllerRequestBus = AZ::EBus<ModularViewportCameraControllerRequests>;
} // namespace AtomToolsFramework
@@ -10,10 +10,9 @@
*
*/
#include "ModernViewportCameraController.h"
#include <Atom/RPI.Public/ViewportContext.h>
#include <Atom/RPI.Public/ViewportContextBus.h>
#include <AtomToolsFramework/Viewport/ModularViewportCameraController.h>
#include <AzCore/Console/IConsole.h>
#include <AzCore/Interface/Interface.h>
#include <AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h>
@@ -23,7 +22,7 @@
#include <AzFramework/Windowing/WindowBus.h>
#include <AzToolsFramework/Viewport/ViewportMessages.h>
namespace SandboxEditor
namespace AtomToolsFramework
{
// debug
void DrawPreviewAxis(AzFramework::DebugDisplayRequests& display, const AZ::Transform& transform, const float axisLength)
@@ -53,12 +52,12 @@ namespace SandboxEditor
return viewportContext;
}
void ModernViewportCameraController::SetCameraListBuilderCallback(const CameraListBuilder& builder)
void ModularViewportCameraController::SetCameraListBuilderCallback(const CameraListBuilder& builder)
{
m_cameraListBuilder = builder;
}
void ModernViewportCameraController::SetupCameras(AzFramework::Cameras& cameras)
void ModularViewportCameraController::SetupCameras(AzFramework::Cameras& cameras)
{
if (m_cameraListBuilder)
{
@@ -67,8 +66,8 @@ namespace SandboxEditor
}
ModernViewportCameraControllerInstance::ModernViewportCameraControllerInstance(
const AzFramework::ViewportId viewportId, ModernViewportCameraController* controller)
: MultiViewportControllerInstanceInterface<ModernViewportCameraController>(viewportId, controller)
const AzFramework::ViewportId viewportId, ModularViewportCameraController* controller)
: MultiViewportControllerInstanceInterface<ModularViewportCameraController>(viewportId, controller)
{
controller->SetupCameras(m_cameraSystem.m_cameras);
@@ -88,12 +87,12 @@ namespace SandboxEditor
}
AzFramework::ViewportDebugDisplayEventBus::Handler::BusConnect(AzToolsFramework::GetEntityContextId());
ModernViewportCameraControllerRequestBus::Handler::BusConnect(viewportId);
ModularViewportCameraControllerRequestBus::Handler::BusConnect(viewportId);
}
ModernViewportCameraControllerInstance::~ModernViewportCameraControllerInstance()
{
ModernViewportCameraControllerRequestBus::Handler::BusDisconnect();
ModularViewportCameraControllerRequestBus::Handler::BusDisconnect();
AzFramework::ViewportDebugDisplayEventBus::Handler::BusDisconnect();
}
@@ -182,4 +181,4 @@ namespace SandboxEditor
m_transformStart = m_camera.Transform();
m_transformEnd = worldFromLocal;
}
} // namespace SandboxEditor
} // namespace AtomToolsFramework
@@ -24,6 +24,8 @@ set(FILES
Include/AtomToolsFramework/Util/MaterialPropertyUtil.h
Include/AtomToolsFramework/Util/Util.h
Include/AtomToolsFramework/Viewport/RenderViewportWidget.h
Include/AtomToolsFramework/Viewport/ModularViewportCameraController.h
Include/AtomToolsFramework/Viewport/ModularViewportCameraControllerRequestBus.h
Source/Communication/LocalServer.cpp
Source/Communication/LocalSocket.cpp
Source/Debug/TraceRecorder.cpp
@@ -38,4 +40,5 @@ set(FILES
Source/Util/MaterialPropertyUtil.cpp
Source/Util/Util.cpp
Source/Viewport/RenderViewportWidget.cpp
Source/Viewport/ModularViewportCameraController.cpp
)
@@ -72,11 +72,6 @@ namespace SceneBuilder
m_sceneBuilder.BusDisconnect();
}
void BuilderPluginComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
{
required.emplace_back(AZ_CRC_CE("AssetImportRequestHandler"));
}
void BuilderPluginComponent::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
@@ -86,4 +81,5 @@ namespace SceneBuilder
->Attribute(AZ::Edit::Attributes::SystemComponentTags, AZStd::vector<AZ::Crc32>({ AssetBuilderSDK::ComponentTags::AssetBuilder }));
}
}
} // namespace SceneBuilder
@@ -32,8 +32,6 @@ namespace SceneBuilder
void Activate() override;
void Deactivate() override;
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required);
private:
SceneBuilderWorker m_sceneBuilder;
};
@@ -118,6 +118,8 @@ namespace SurfaceData
void EditorSurfaceDataSystemComponent::Deactivate()
{
m_surfaceTagNameAssets.clear();
AzFramework::AssetCatalogEventBus::Handler::BusDisconnect();
AzToolsFramework::Components::EditorComponentBase::Deactivate();
SurfaceDataTagProviderRequestBus::Handler::BusDisconnect();
@@ -9,6 +9,18 @@
},
"PythonAssetBuilder.Editor": {
"AutoLoad": false
},
"AWSCore.Editor": {
"AutoLoad": false
},
"AWSClientAuth": {
"AutoLoad": false
},
"AWSClientAuth.Editor": {
"AutoLoad": false
},
"AWSMetrics": {
"AutoLoad": false
}
}
}
-16
View File
@@ -1,16 +0,0 @@
{
"O3DE":
{
"SceneAPI":
{
"AssetImporter":
{
"SupportedFileTypeExtensions":
[
".fbx",
".stl"
]
}
}
}
}
+3 -3
View File
@@ -29,15 +29,15 @@ string(TOLOWER ${PROJECT_NAME} _project_name_lower)
set(CPACK_PACKAGE_FILE_NAME "${_project_name_lower}_${LY_VERSION_STRING}_installer")
set(DEFAULT_LICENSE_NAME "Apache-2.0")
set(DEFAULT_LICENSE_FILE "${CMAKE_SOURCE_DIR}/LICENSE.txt")
set(DEFAULT_LICENSE_FILE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE.txt")
set(CPACK_RESOURCE_FILE_LICENSE ${DEFAULT_LICENSE_FILE})
set(CPACK_LICENSE_URL ${LY_INSTALLER_LICENSE_URL})
set(CPACK_PACKAGE_INSTALL_DIRECTORY "${CPACK_PACKAGE_VENDOR}/${CPACK_PACKAGE_VERSION}")
# CMAKE_SOURCE_DIR doesn't equate to anything during execution of pre/post build scripts
set(CPACK_SOURCE_DIR ${CMAKE_SOURCE_DIR}/cmake)
# neither of the SOURCE_DIR variables equate to anything during execution of pre/post build scripts
set(CPACK_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
# attempt to apply platform specific settings
ly_get_absolute_pal_filename(pal_dir ${CPACK_SOURCE_DIR}/Platform/${PAL_HOST_PLATFORM_NAME})