diff --git a/Assets/Editor/Translation/scriptcanvas_en_us.ts b/Assets/Editor/Translation/scriptcanvas_en_us.ts
index 7b8361c879..6d436b7caf 100644
--- a/Assets/Editor/Translation/scriptcanvas_en_us.ts
+++ b/Assets/Editor/Translation/scriptcanvas_en_us.ts
@@ -62164,7 +62164,7 @@ An Entity can be selected by using the pick button, or by dragging an Entity fro
HANDLER_TAGGLOBALNOTIFICATIONBUS_ONENTITYTAGADDED_OUTPUT0_NAME
Simple Type: EntityID C++ Type: const EntityId&
- Entity
+ EntityID
HANDLER_TAGGLOBALNOTIFICATIONBUS_ONENTITYTAGADDED_OUTPUT0_TOOLTIP
@@ -62202,7 +62202,7 @@ An Entity can be selected by using the pick button, or by dragging an Entity fro
HANDLER_TAGGLOBALNOTIFICATIONBUS_ONENTITYTAGREMOVED_OUTPUT0_NAME
Simple Type: EntityID C++ Type: const EntityId&
- Entity
+ EntityId
HANDLER_TAGGLOBALNOTIFICATIONBUS_ONENTITYTAGREMOVED_OUTPUT0_TOOLTIP
@@ -81852,7 +81852,7 @@ The element is removed from its current parent and added as a child of the new p
HANDLER_SPAWNERCOMPONENTNOTIFICATIONBUS_ONENTITYSPAWNED_OUTPUT1_NAME
Simple Type: EntityID C++ Type: const EntityId&
- Entity
+ EntityID
HANDLER_SPAWNERCOMPONENTNOTIFICATIONBUS_ONENTITYSPAWNED_OUTPUT1_TOOLTIP
@@ -89198,7 +89198,7 @@ The element is removed from its current parent and added as a child of the new p
HANDLER_ENTITYBUS_ONENTITYACTIVATED_OUTPUT0_NAME
Simple Type: EntityID C++ Type: const EntityId&
- Entity
+ EntityID
HANDLER_ENTITYBUS_ONENTITYACTIVATED_OUTPUT0_TOOLTIP
@@ -89236,7 +89236,7 @@ The element is removed from its current parent and added as a child of the new p
HANDLER_ENTITYBUS_ONENTITYDEACTIVATED_OUTPUT0_NAME
Simple Type: EntityID C++ Type: const EntityId&
- Entity
+ EntityID
HANDLER_ENTITYBUS_ONENTITYDEACTIVATED_OUTPUT0_TOOLTIP
diff --git a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceMouse.h b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceMouse.h
index a69a8a9ec5..f5b805a7a5 100644
--- a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceMouse.h
+++ b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceMouse.h
@@ -6,6 +6,8 @@
*
*/
+#pragma once
+
#include
#include
#include
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp
index acf935e6dc..c6772ea2d7 100644
--- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp
@@ -20,7 +20,7 @@ AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option")
AZ_POP_DISABLE_WARNING
AZ_CVAR(
- bool, ed_useNewAssetBrowserTableView, false, nullptr, AZ::ConsoleFunctorFlags::Null,
+ bool, ed_useNewAssetBrowserTableView, true, nullptr, AZ::ConsoleFunctorFlags::Null,
"Use the new AssetBrowser TableView for searching assets.");
namespace AzToolsFramework
{
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.cpp
index 9ffe8021e3..a449fa0055 100644
--- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.cpp
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.cpp
@@ -606,6 +606,7 @@ namespace AzToolsFramework
AzToolsFramework::ComponentModeFramework::EditorComponentModeNotificationBus::Handler::BusConnect(
AzToolsFramework::GetEntityContextId());
+ ViewportEditorModeNotificationsBus::Handler::BusConnect(GetEntityContextId());
}
EntityPropertyEditor::~EntityPropertyEditor()
@@ -618,7 +619,8 @@ namespace AzToolsFramework
AZ::EntitySystemBus::Handler::BusDisconnect();
EditorEntityContextNotificationBus::Handler::BusDisconnect();
AzToolsFramework::ComponentModeFramework::EditorComponentModeNotificationBus::Handler::BusDisconnect();
-
+ ViewportEditorModeNotificationsBus::Handler::BusDisconnect();
+
for (auto& entityId : m_overrideSelectedEntityIds)
{
DisconnectFromEntityBuses(entityId);
diff --git a/Code/Tools/AssetProcessor/native/tests/assetmanager/AssetProcessorManagerTest.cpp b/Code/Tools/AssetProcessor/native/tests/assetmanager/AssetProcessorManagerTest.cpp
index f8d758e092..1a6063cff0 100644
--- a/Code/Tools/AssetProcessor/native/tests/assetmanager/AssetProcessorManagerTest.cpp
+++ b/Code/Tools/AssetProcessor/native/tests/assetmanager/AssetProcessorManagerTest.cpp
@@ -243,7 +243,7 @@ void AssetProcessorManagerTest::SetUp()
m_mockApplicationManager->BusConnect();
m_assetProcessorManager.reset(new AssetProcessorManager_Test(m_config.get()));
- m_assertAbsorber.Clear();
+ m_errorAbsorber->Clear();
m_isIdling = false;
@@ -334,9 +334,9 @@ TEST_F(AssetProcessorManagerTest, UnitTestForGettingJobInfoBySourceUUIDSuccess)
EXPECT_STRCASEEQ(relFileName.toUtf8().data(), response.m_jobList[0].m_sourceFile.c_str());
EXPECT_STRCASEEQ(tempPath.filePath("subfolder1").toUtf8().data(), response.m_jobList[0].m_watchFolder.c_str());
- ASSERT_EQ(m_assertAbsorber.m_numWarningsAbsorbed, 0);
- ASSERT_EQ(m_assertAbsorber.m_numErrorsAbsorbed, 0);
- ASSERT_EQ(m_assertAbsorber.m_numAssertsAbsorbed, 0);
+ ASSERT_EQ(m_errorAbsorber->m_numWarningsAbsorbed, 0);
+ ASSERT_EQ(m_errorAbsorber->m_numErrorsAbsorbed, 0);
+ ASSERT_EQ(m_errorAbsorber->m_numAssertsAbsorbed, 0);
}
TEST_F(AssetProcessorManagerTest, WarningsAndErrorsReported_SuccessfullySavedToDatabase)
@@ -388,9 +388,9 @@ TEST_F(AssetProcessorManagerTest, WarningsAndErrorsReported_SuccessfullySavedToD
ASSERT_EQ(response.m_jobList[0].m_warningCount, 11);
ASSERT_EQ(response.m_jobList[0].m_errorCount, 22);
- ASSERT_EQ(m_assertAbsorber.m_numWarningsAbsorbed, 0);
- ASSERT_EQ(m_assertAbsorber.m_numErrorsAbsorbed, 0);
- ASSERT_EQ(m_assertAbsorber.m_numAssertsAbsorbed, 0);
+ ASSERT_EQ(m_errorAbsorber->m_numWarningsAbsorbed, 0);
+ ASSERT_EQ(m_errorAbsorber->m_numErrorsAbsorbed, 0);
+ ASSERT_EQ(m_errorAbsorber->m_numAssertsAbsorbed, 0);
}
@@ -1312,8 +1312,8 @@ void PathDependencyTest::SetUp()
void PathDependencyTest::TearDown()
{
- ASSERT_EQ(m_assertAbsorber.m_numAssertsAbsorbed, 0);
- ASSERT_EQ(m_assertAbsorber.m_numErrorsAbsorbed, 0);
+ ASSERT_EQ(m_errorAbsorber->m_numAssertsAbsorbed, 0);
+ ASSERT_EQ(m_errorAbsorber->m_numErrorsAbsorbed, 0);
AssetProcessorManagerTest::TearDown();
}
@@ -1617,7 +1617,7 @@ TEST_F(PathDependencyTest, AssetProcessed_Impl_SelfReferrentialProductDependency
mainFile.m_products.push_back(productAssetId);
// tell the APM that the asset has been processed and allow it to bubble through its event queue:
- m_assertAbsorber.Clear();
+ m_errorAbsorber->Clear();
m_assetProcessorManager->AssetProcessed(jobDetails.m_jobEntry, processJobResponse);
ASSERT_TRUE(BlockUntilIdle(5000));
@@ -1627,8 +1627,8 @@ TEST_F(PathDependencyTest, AssetProcessed_Impl_SelfReferrentialProductDependency
ASSERT_TRUE(dependencyContainer.empty());
// We are testing 2 different dependencies, so we should get 2 warnings
- ASSERT_EQ(m_assertAbsorber.m_numWarningsAbsorbed, 2);
- m_assertAbsorber.Clear();
+ ASSERT_EQ(m_errorAbsorber->m_numWarningsAbsorbed, 2);
+ m_errorAbsorber->Clear();
}
// This test shows the process of deferring resolution of a path dependency works.
@@ -1945,8 +1945,8 @@ TEST_F(PathDependencyTest, WildcardDependencies_ExcludePathsExisting_ResolveCorr
);
// Test asset PrimaryFile1 has 4 conflict dependencies
- ASSERT_EQ(m_assertAbsorber.m_numErrorsAbsorbed, 4);
- m_assertAbsorber.Clear();
+ ASSERT_EQ(m_errorAbsorber->m_numErrorsAbsorbed, 4);
+ m_errorAbsorber->Clear();
}
TEST_F(PathDependencyTest, WildcardDependencies_Deferred_ResolveCorrectly)
@@ -2093,8 +2093,8 @@ TEST_F(PathDependencyTest, WildcardDependencies_ExcludedPathDeferred_ResolveCorr
// Test asset PrimaryFile1 has 4 conflict dependencies
// After test assets dep2 and dep3 are processed,
// another 2 errors will be raised because of the confliction
- ASSERT_EQ(m_assertAbsorber.m_numErrorsAbsorbed, 6);
- m_assertAbsorber.Clear();
+ ASSERT_EQ(m_errorAbsorber->m_numErrorsAbsorbed, 6);
+ m_errorAbsorber->Clear();
}
void PathDependencyTest::RunWildcardTest(bool useCorrectDatabaseSeparator, AssetBuilderSDK::ProductPathDependencyType pathDependencyType, bool buildDependenciesFirst)
diff --git a/Code/Tools/AssetProcessor/native/tests/assetmanager/AssetProcessorManagerTest.h b/Code/Tools/AssetProcessor/native/tests/assetmanager/AssetProcessorManagerTest.h
index 3443a4c519..2f0121485e 100644
--- a/Code/Tools/AssetProcessor/native/tests/assetmanager/AssetProcessorManagerTest.h
+++ b/Code/Tools/AssetProcessor/native/tests/assetmanager/AssetProcessorManagerTest.h
@@ -58,7 +58,6 @@ protected:
AZStd::unique_ptr m_assetProcessorManager;
AZStd::unique_ptr m_mockApplicationManager;
AZStd::unique_ptr m_config;
- UnitTestUtils::AssertAbsorber m_assertAbsorber; // absorb asserts/warnings/errors so that the unit test output is not cluttered
QString m_gameName;
QDir m_normalizedCacheRootDir;
AZStd::atomic_bool m_isIdling;
diff --git a/Code/Tools/ProjectManager/Source/DownloadController.cpp b/Code/Tools/ProjectManager/Source/DownloadController.cpp
index d30e1bbc7c..6326b2fc11 100644
--- a/Code/Tools/ProjectManager/Source/DownloadController.cpp
+++ b/Code/Tools/ProjectManager/Source/DownloadController.cpp
@@ -41,9 +41,11 @@ namespace O3DE::ProjectManager
void DownloadController::AddGemDownload(const QString& gemName)
{
m_gemNames.push_back(gemName);
+ emit GemDownloadAdded(gemName);
+
if (m_gemNames.size() == 1)
{
- m_worker->SetGemToDownload(m_gemNames[0], false);
+ m_worker->SetGemToDownload(m_gemNames.front(), false);
m_workerThread.start();
}
}
@@ -62,6 +64,7 @@ namespace O3DE::ProjectManager
else
{
m_gemNames.erase(findResult);
+ emit GemDownloadRemoved(gemName);
}
}
}
@@ -69,7 +72,7 @@ namespace O3DE::ProjectManager
void DownloadController::UpdateUIProgress(int progress)
{
m_lastProgress = progress;
- emit GemDownloadProgress(progress);
+ emit GemDownloadProgress(m_gemNames.front(), progress);
}
void DownloadController::HandleResults(const QString& result)
diff --git a/Code/Tools/ProjectManager/Source/DownloadController.h b/Code/Tools/ProjectManager/Source/DownloadController.h
index 5b2d230379..0bf0ae473c 100644
--- a/Code/Tools/ProjectManager/Source/DownloadController.h
+++ b/Code/Tools/ProjectManager/Source/DownloadController.h
@@ -59,7 +59,9 @@ namespace O3DE::ProjectManager
signals:
void StartGemDownload(const QString& gemName);
void Done(const QString& gemName, bool success = true);
- void GemDownloadProgress(int percentage);
+ void GemDownloadAdded(const QString& gemName);
+ void GemDownloadRemoved(const QString& gemName);
+ void GemDownloadProgress(const QString& gemName, int percentage);
private:
DownloadWorker* m_worker;
diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp
index def02ea9ba..a05d32a76e 100644
--- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp
+++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp
@@ -30,6 +30,7 @@ namespace O3DE::ProjectManager
m_layout->setMargin(5);
m_layout->setAlignment(Qt::AlignTop);
setLayout(m_layout);
+ setMinimumHeight(400);
QHBoxLayout* hLayout = new QHBoxLayout();
@@ -119,6 +120,12 @@ namespace O3DE::ProjectManager
setWindowFlags(Qt::FramelessWindowHint | Qt::Dialog);
}
+ CartOverlayWidget::~CartOverlayWidget()
+ {
+ // disconnect from all download controller signals
+ disconnect(m_downloadController, nullptr, this, nullptr);
+ }
+
void CartOverlayWidget::CreateGemSection(const QString& singularTitle, const QString& pluralTitle, GetTagIndicesCallback getTagIndices)
{
QWidget* widget = new QWidget();
@@ -162,13 +169,13 @@ namespace O3DE::ProjectManager
void CartOverlayWidget::CreateDownloadSection()
{
- QWidget* widget = new QWidget();
- widget->setFixedWidth(s_width);
- m_layout->addWidget(widget);
+ m_downloadSectionWidget = new QWidget();
+ m_downloadSectionWidget->setFixedWidth(s_width);
+ m_layout->addWidget(m_downloadSectionWidget);
QVBoxLayout* layout = new QVBoxLayout();
layout->setAlignment(Qt::AlignTop);
- widget->setLayout(layout);
+ m_downloadSectionWidget->setLayout(layout);
QLabel* titleLabel = new QLabel();
titleLabel->setObjectName("GemCatalogCartOverlaySectionLabel");
@@ -187,88 +194,121 @@ namespace O3DE::ProjectManager
QLabel* processingQueueLabel = new QLabel("Processing Queue");
gemDownloadLayout->addWidget(processingQueueLabel);
- QWidget* downloadingItemWidget = new QWidget();
- downloadingItemWidget->setObjectName("GemCatalogCartOverlayGemDownloadBG");
- gemDownloadLayout->addWidget(downloadingItemWidget);
+ m_downloadingListWidget = new QWidget();
+ m_downloadingListWidget->setObjectName("GemCatalogCartOverlayGemDownloadBG");
+ gemDownloadLayout->addWidget(m_downloadingListWidget);
QVBoxLayout* downloadingItemLayout = new QVBoxLayout();
downloadingItemLayout->setAlignment(Qt::AlignTop);
- downloadingItemWidget->setLayout(downloadingItemLayout);
+ m_downloadingListWidget->setLayout(downloadingItemLayout);
- auto update = [=](int downloadProgress)
+ QLabel* downloadsInProgessLabel = new QLabel("");
+ downloadsInProgessLabel->setObjectName("NumDownloadsInProgressLabel");
+ downloadingItemLayout->addWidget(downloadsInProgessLabel);
+
+ if (m_downloadController->IsDownloadQueueEmpty())
+ {
+ m_downloadSectionWidget->hide();
+ }
+ else
{
- if (m_downloadController->IsDownloadQueueEmpty())
+ // Setup gem download rows for gems that are already in the queue
+ const AZStd::vector& downloadQueue = m_downloadController->GetDownloadQueue();
+
+ for (const QString& gemName : downloadQueue)
{
- widget->hide();
+ GemDownloadAdded(gemName);
}
- else
- {
- widget->setUpdatesEnabled(false);
- // remove items
- QLayoutItem* layoutItem = nullptr;
- while ((layoutItem = downloadingItemLayout->takeAt(0)) != nullptr)
- {
- if (layoutItem->layout())
- {
- // Gem info row
- QLayoutItem* rowLayoutItem = nullptr;
- while ((rowLayoutItem = layoutItem->layout()->takeAt(0)) != nullptr)
- {
- rowLayoutItem->widget()->deleteLater();
- }
- layoutItem->layout()->deleteLater();
- }
- if (layoutItem->widget())
- {
- layoutItem->widget()->deleteLater();
- }
- }
-
- // Setup gem download rows
- const AZStd::vector& downloadQueue = m_downloadController->GetDownloadQueue();
-
- QLabel* downloadsInProgessLabel = new QLabel("");
- downloadsInProgessLabel->setText(
- QString("%1 %2").arg(downloadQueue.size()).arg(downloadQueue.size() == 1 ? tr("download in progress...") : tr("downloads in progress...")));
- downloadingItemLayout->addWidget(downloadsInProgessLabel);
-
- for (int downloadingGemNumber = 0; downloadingGemNumber < downloadQueue.size(); ++downloadingGemNumber)
- {
- QHBoxLayout* nameProgressLayout = new QHBoxLayout();
-
- const QString& gemName = downloadQueue[downloadingGemNumber];
- TagWidget* newTag = new TagWidget({gemName, gemName});
- nameProgressLayout->addWidget(newTag);
+ }
- QLabel* progress = new QLabel(downloadingGemNumber == 0? QString("%1%").arg(downloadProgress) : tr("Queued"));
- nameProgressLayout->addWidget(progress);
+ // connect to download controller data changed
+ connect(m_downloadController, &DownloadController::GemDownloadAdded, this, &CartOverlayWidget::GemDownloadAdded);
+ connect(m_downloadController, &DownloadController::GemDownloadRemoved, this, &CartOverlayWidget::GemDownloadRemoved);
+ connect(m_downloadController, &DownloadController::GemDownloadProgress, this, &CartOverlayWidget::GemDownloadProgress);
+ connect(m_downloadController, &DownloadController::Done, this, &CartOverlayWidget::GemDownloadComplete);
+ }
- QSpacerItem* spacer = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum);
- nameProgressLayout->addSpacerItem(spacer);
+ void CartOverlayWidget::GemDownloadAdded(const QString& gemName)
+ {
+ // Containing widget for the current download item
+ QWidget* newGemDownloadWidget = new QWidget();
+ newGemDownloadWidget->setObjectName(gemName);
+ QVBoxLayout* downloadingGemLayout = new QVBoxLayout(newGemDownloadWidget);
+ newGemDownloadWidget->setLayout(downloadingGemLayout);
+
+ // Gem name, progress string, cancel
+ QHBoxLayout* nameProgressLayout = new QHBoxLayout(newGemDownloadWidget);
+ TagWidget* newTag = new TagWidget({gemName, gemName}, newGemDownloadWidget);
+ nameProgressLayout->addWidget(newTag);
+ QLabel* progress = new QLabel(tr("Queued"), newGemDownloadWidget);
+ progress->setObjectName("DownloadProgressLabel");
+ nameProgressLayout->addWidget(progress);
+ nameProgressLayout->addStretch();
+ QLabel* cancelText = new QLabel(tr("Cancel").arg(gemName), newGemDownloadWidget);
+ cancelText->setTextInteractionFlags(Qt::LinksAccessibleByMouse);
+ connect(cancelText, &QLabel::linkActivated, this, &CartOverlayWidget::OnCancelDownloadActivated);
+ nameProgressLayout->addWidget(cancelText);
+ downloadingGemLayout->addLayout(nameProgressLayout);
+
+ // Progress bar
+ QProgressBar* downloadProgessBar = new QProgressBar(newGemDownloadWidget);
+ downloadProgessBar->setObjectName("DownloadProgressBar");
+ downloadingGemLayout->addWidget(downloadProgessBar);
+ downloadProgessBar->setValue(0);
+
+ m_downloadingListWidget->layout()->addWidget(newGemDownloadWidget);
+
+ const AZStd::vector& downloadQueue = m_downloadController->GetDownloadQueue();
+ QLabel* numDownloads = m_downloadingListWidget->findChild("NumDownloadsInProgressLabel");
+ numDownloads->setText(QString("%1 %2")
+ .arg(downloadQueue.size())
+ .arg(downloadQueue.size() == 1 ? tr("download in progress...") : tr("downloads in progress...")));
+
+ m_downloadingListWidget->show();
+ }
- QLabel* cancelText = new QLabel(QString("Cancel").arg(gemName));
- cancelText->setTextInteractionFlags(Qt::LinksAccessibleByMouse);
- connect(cancelText, &QLabel::linkActivated, this, &CartOverlayWidget::OnCancelDownloadActivated);
- nameProgressLayout->addWidget(cancelText);
- downloadingItemLayout->addLayout(nameProgressLayout);
+ void CartOverlayWidget::GemDownloadRemoved(const QString& gemName)
+ {
+ QWidget* gemToRemove = m_downloadingListWidget->findChild(gemName);
+ if (gemToRemove)
+ {
+ gemToRemove->deleteLater();
+ }
- QProgressBar* downloadProgessBar = new QProgressBar();
- downloadingItemLayout->addWidget(downloadProgessBar);
- downloadProgessBar->setValue(downloadingGemNumber == 0 ? downloadProgress : 0);
- }
+ if (m_downloadController->IsDownloadQueueEmpty())
+ {
+ m_downloadSectionWidget->hide();
+ }
+ else
+ {
+ size_t downloadQueueSize = m_downloadController->GetDownloadQueue().size();
+ QLabel* numDownloads = m_downloadingListWidget->findChild("NumDownloadsInProgressLabel");
+ numDownloads->setText(QString("%1 %2")
+ .arg(downloadQueueSize)
+ .arg(downloadQueueSize == 1 ? tr("download in progress...") : tr("downloads in progress...")));
+ }
+ }
- widget->setUpdatesEnabled(true);
- widget->show();
+ void CartOverlayWidget::GemDownloadProgress(const QString& gemName, int percentage)
+ {
+ QWidget* gemToUpdate = m_downloadingListWidget->findChild(gemName);
+ if (gemToUpdate)
+ {
+ QLabel* progressLabel = gemToUpdate->findChild("DownloadProgressLabel");
+ if (progressLabel)
+ {
+ progressLabel->setText(QString("%1%").arg(percentage));
}
- };
+ QProgressBar* progressBar = gemToUpdate->findChild("DownloadProgressBar");
+ if (progressBar)
+ {
+ progressBar->setValue(percentage);
+ }
+ }
+ }
- auto downloadEnded = [=](const QString& /*gemName*/, bool /*success*/)
- {
- update(0); // update the list to remove the gem that has finished
- };
- // connect to download controller data changed
- connect(m_downloadController, &DownloadController::GemDownloadProgress, this, update);
- connect(m_downloadController, &DownloadController::Done, this, downloadEnded);
- update(0);
+ void CartOverlayWidget::GemDownloadComplete(const QString& gemName, bool /*success*/)
+ {
+ GemDownloadRemoved(gemName); // update the list to remove the gem that has finished
}
QVector CartOverlayWidget::GetTagsFromModelIndices(const QVector& gems) const
diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.h
index a0ed7c70f9..fb833b85ca 100644
--- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.h
+++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.h
@@ -34,6 +34,13 @@ namespace O3DE::ProjectManager
public:
CartOverlayWidget(GemModel* gemModel, DownloadController* downloadController, QWidget* parent = nullptr);
+ ~CartOverlayWidget();
+
+ public slots:
+ void GemDownloadAdded(const QString& gemName);
+ void GemDownloadRemoved(const QString& gemName);
+ void GemDownloadProgress(const QString& gemName, int percentage);
+ void GemDownloadComplete(const QString& gemName, bool success);
private:
QVector GetTagsFromModelIndices(const QVector& gems) const;
@@ -47,6 +54,9 @@ namespace O3DE::ProjectManager
GemModel* m_gemModel = nullptr;
DownloadController* m_downloadController = nullptr;
+ QWidget* m_downloadSectionWidget = nullptr;
+ QWidget* m_downloadingListWidget = nullptr;
+
inline constexpr static int s_width = 240;
};
diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp
index 6758010cf5..3a513fdc1d 100644
--- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp
+++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp
@@ -268,6 +268,7 @@ namespace O3DE::ProjectManager
if (added && GemModel::GetDownloadStatus(modelIndex) == GemInfo::DownloadStatus::NotDownloaded)
{
m_downloadController->AddGemDownload(GemModel::GetName(modelIndex));
+ GemModel::SetDownloadStatus(*m_proxyModel, m_proxyModel->mapFromSource(modelIndex), GemInfo::DownloadStatus::Downloading);
}
}
diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterWidget.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterWidget.cpp
index b608445d0f..acea6ce378 100644
--- a/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterWidget.cpp
+++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterWidget.cpp
@@ -221,7 +221,6 @@ namespace O3DE::ProjectManager
ResetGemStatusFilter();
ResetGemOriginFilter();
ResetTypeFilter();
- ResetPlatformFilter();
ResetFeatureFilter();
}
diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/DefaultObjectSrg.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/DefaultObjectSrg.azsli
index 10526597cb..9f40e7ab8f 100644
--- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/DefaultObjectSrg.azsli
+++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/DefaultObjectSrg.azsli
@@ -37,6 +37,7 @@ ShaderResourceGroup ObjectSrg : SRG_PerObject
float m_padding;
bool m_useReflectionProbe;
bool m_useParallaxCorrection;
+ float m_exposure;
};
ReflectionProbeData m_reflectionProbeData;
diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/Ibl.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/Ibl.azsli
index d33a307dfe..3254b8e4ed 100644
--- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/Ibl.azsli
+++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/Ibl.azsli
@@ -85,12 +85,12 @@ void ApplyIBL(Surface surface, inout LightingData lightingData)
if(useIbl)
{
- float iblExposureFactor = pow(2.0, SceneSrg::m_iblExposure);
+ float globalIblExposure = pow(2.0, SceneSrg::m_iblExposure);
if(useDiffuseIbl)
{
float3 iblDiffuse = GetIblDiffuse(surface.normal, surface.albedo, lightingData.diffuseResponse);
- lightingData.diffuseLighting += (iblDiffuse * iblExposureFactor * lightingData.diffuseAmbientOcclusion);
+ lightingData.diffuseLighting += (iblDiffuse * globalIblExposure * lightingData.diffuseAmbientOcclusion);
}
if(useSpecularIbl)
@@ -116,7 +116,8 @@ void ApplyIBL(Surface surface, inout LightingData lightingData)
iblSpecular = iblSpecular * (1.0 - clearCoatResponse) * (1.0 - clearCoatResponse) + clearCoatIblSpecular;
}
- lightingData.specularLighting += (iblSpecular * iblExposureFactor);
+ float exposure = ObjectSrg::m_reflectionProbeData.m_useReflectionProbe ? pow(2.0, ObjectSrg::m_reflectionProbeData.m_exposure) : globalIblExposure;
+ lightingData.specularLighting += (iblSpecular * exposure);
}
}
}
diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Skin/SkinObjectSrg.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Skin/SkinObjectSrg.azsli
index d0766c295d..99a32629ef 100644
--- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Skin/SkinObjectSrg.azsli
+++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Skin/SkinObjectSrg.azsli
@@ -46,6 +46,7 @@ ShaderResourceGroup ObjectSrg : SRG_PerObject
float m_padding;
bool m_useReflectionProbe;
bool m_useParallaxCorrection;
+ float m_exposure;
};
ReflectionProbeData m_reflectionProbeData;
diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeRenderInner.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeRenderInner.azsl
index a0734caf02..e9333a4694 100644
--- a/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeRenderInner.azsl
+++ b/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeRenderInner.azsl
@@ -81,7 +81,7 @@ PSOutput MainPS(VSOutput IN, in uint sampleIndex : SV_SampleIndex)
}
// apply exposure setting
- specular *= pow(2.0, SceneSrg::m_iblExposure);
+ specular *= pow(2.0, ObjectSrg::m_exposure);
PSOutput OUT;
OUT.m_color = float4(specular, 1.0f);
diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeRenderObjectSrg.azsli b/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeRenderObjectSrg.azsli
index 8151ed2fd5..366dc691ed 100644
--- a/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeRenderObjectSrg.azsli
+++ b/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeRenderObjectSrg.azsli
@@ -17,6 +17,7 @@ ShaderResourceGroup ObjectSrg : SRG_PerObject
float3 m_outerObbHalfLengths;
float3 m_innerObbHalfLengths;
bool m_useParallaxCorrection;
+ float m_exposure;
TextureCube m_reflectionCubeMap;
float4x4 GetWorldMatrix()
diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeRenderOuter.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeRenderOuter.azsl
index ac97172f1f..138d29398e 100644
--- a/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeRenderOuter.azsl
+++ b/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeRenderOuter.azsl
@@ -104,7 +104,7 @@ PSOutput MainPS(VSOutput IN, in uint sampleIndex : SV_SampleIndex)
blendWeight /= max(1.0f, blendWeightAllProbes);
// apply exposure setting
- specular *= pow(2.0, SceneSrg::m_iblExposure);
+ specular *= pow(2.0, ObjectSrg::m_exposure);
// apply blend weight for additive blending
specular *= blendWeight;
diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/ReflectionProbe/ReflectionProbeFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/ReflectionProbe/ReflectionProbeFeatureProcessor.h
index 5efd235a67..ded36f5496 100644
--- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/ReflectionProbe/ReflectionProbeFeatureProcessor.h
+++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/ReflectionProbe/ReflectionProbeFeatureProcessor.h
@@ -39,6 +39,8 @@ namespace AZ
bool IsCubeMapReferenced(const AZStd::string& relativePath) override;
bool IsValidProbeHandle(const ReflectionProbeHandle& probe) const override { return (probe.get() != nullptr); }
void ShowProbeVisualization(const ReflectionProbeHandle& probe, bool showVisualization) override;
+ void SetRenderExposure(const ReflectionProbeHandle& probe, float renderExposure) override;
+ void SetBakeExposure(const ReflectionProbeHandle& probe, float bakeExposure) override;
// FeatureProcessor overrides
void Activate() override;
diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/ReflectionProbe/ReflectionProbeFeatureProcessorInterface.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/ReflectionProbe/ReflectionProbeFeatureProcessorInterface.h
index 4eb2130b1b..80c92281ea 100644
--- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/ReflectionProbe/ReflectionProbeFeatureProcessorInterface.h
+++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/ReflectionProbe/ReflectionProbeFeatureProcessorInterface.h
@@ -50,6 +50,8 @@ namespace AZ
virtual bool IsCubeMapReferenced(const AZStd::string& relativePath) = 0;
virtual bool IsValidProbeHandle(const ReflectionProbeHandle& probe) const = 0;
virtual void ShowProbeVisualization(const ReflectionProbeHandle& probe, bool showVisualization) = 0;
+ virtual void SetRenderExposure(const ReflectionProbeHandle& probe, float renderExposure) = 0;
+ virtual void SetBakeExposure(const ReflectionProbeHandle& probe, float bakeExposure) = 0;
};
} // namespace Render
} // namespace AZ
diff --git a/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp
index c7fb19bc5d..99f01ea630 100644
--- a/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp
+++ b/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp
@@ -1178,6 +1178,9 @@ namespace AZ
AZ::RHI::ShaderInputConstantIndex useParallaxCorrectionConstantIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name("m_reflectionProbeData.m_useParallaxCorrection"));
AZ_Error("MeshDataInstance", useParallaxCorrectionConstantIndex.IsValid(), "Failed to find ReflectionProbe constant index");
+ AZ::RHI::ShaderInputConstantIndex exposureConstantIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name("m_reflectionProbeData.m_exposure"));
+ AZ_Error("MeshDataInstance", exposureConstantIndex.IsValid(), "Failed to find ReflectionProbe constant index");
+
// retrieve probe cubemap index
Name reflectionCubeMapImageName = Name("m_reflectionProbeCubeMap");
RHI::ShaderInputImageIndex reflectionCubeMapImageIndex = m_shaderResourceGroup->FindShaderInputImageIndex(reflectionCubeMapImageName);
@@ -1198,6 +1201,7 @@ namespace AZ
m_shaderResourceGroup->SetConstant(innerObbHalfLengthsConstantIndex, reflectionProbes[0]->GetInnerObbWs().GetHalfLengths());
m_shaderResourceGroup->SetConstant(useReflectionProbeConstantIndex, true);
m_shaderResourceGroup->SetConstant(useParallaxCorrectionConstantIndex, reflectionProbes[0]->GetUseParallaxCorrection());
+ m_shaderResourceGroup->SetConstant(exposureConstantIndex, reflectionProbes[0]->GetRenderExposure());
m_shaderResourceGroup->SetImage(reflectionCubeMapImageIndex, reflectionProbes[0]->GetCubeMapImage());
}
diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/BlendColorGradingLutsPass.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/BlendColorGradingLutsPass.cpp
index f74837bd9a..ca93898d5e 100644
--- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/BlendColorGradingLutsPass.cpp
+++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/BlendColorGradingLutsPass.cpp
@@ -46,7 +46,11 @@ namespace AZ
void BlendColorGradingLutsPass::InitializeShaderVariant()
{
- AZ_Assert(m_shader != nullptr, "BlendColorGradingLutsPass %s has a null shader when calling InitializeShaderVariant.", GetPathName().GetCStr());
+ if (m_shader == nullptr)
+ {
+ AZ_Assert(false, "BlendColorGradingLutsPass %s has a null shader when calling InitializeShaderVariant.", GetPathName().GetCStr());
+ return;
+ }
// Total variations is MaxBlendLuts plus one for the fallback case that none of the LUTs are found,
// and hence zero LUTs are blended resulting in an identity LUT.
diff --git a/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.cpp b/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.cpp
index e86d91d387..4ac5782b04 100644
--- a/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.cpp
+++ b/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.cpp
@@ -120,15 +120,17 @@ namespace AZ
m_scene->RemoveRenderPipeline(m_environmentCubeMapPipelineId);
m_environmentCubeMapPass = nullptr;
- // restore exposure
- sceneSrg->SetConstant(m_iblExposureConstantIndex, m_previousExposure);
+ // restore exposures
+ sceneSrg->SetConstant(m_globalIblExposureConstantIndex, m_previousGlobalIblExposure);
+ sceneSrg->SetConstant(m_skyBoxExposureConstantIndex, m_previousSkyBoxExposure);
m_buildingCubeMap = false;
}
else
{
- // set exposure to 0.0 while baking the cubemap
- sceneSrg->SetConstant(m_iblExposureConstantIndex, 0.0f);
+ // set exposures to the user specified value while baking the cubemap
+ sceneSrg->SetConstant(m_globalIblExposureConstantIndex, m_bakeExposure);
+ sceneSrg->SetConstant(m_skyBoxExposureConstantIndex, m_bakeExposure);
}
}
@@ -162,6 +164,7 @@ namespace AZ
m_renderOuterSrg->SetConstant(m_reflectionRenderData->m_outerObbHalfLengthsRenderConstantIndex, m_outerObbWs.GetHalfLengths());
m_renderOuterSrg->SetConstant(m_reflectionRenderData->m_innerObbHalfLengthsRenderConstantIndex, m_innerObbWs.GetHalfLengths());
m_renderOuterSrg->SetConstant(m_reflectionRenderData->m_useParallaxCorrectionRenderConstantIndex, m_useParallaxCorrection);
+ m_renderOuterSrg->SetConstant(m_reflectionRenderData->m_exposureConstantIndex, m_renderExposure);
m_renderOuterSrg->SetImage(m_reflectionRenderData->m_reflectionCubeMapRenderImageIndex, m_cubeMapImage);
m_renderOuterSrg->Compile();
@@ -172,6 +175,7 @@ namespace AZ
m_renderInnerSrg->SetConstant(m_reflectionRenderData->m_outerObbHalfLengthsRenderConstantIndex, m_outerObbWs.GetHalfLengths());
m_renderInnerSrg->SetConstant(m_reflectionRenderData->m_innerObbHalfLengthsRenderConstantIndex, m_innerObbWs.GetHalfLengths());
m_renderInnerSrg->SetConstant(m_reflectionRenderData->m_useParallaxCorrectionRenderConstantIndex, m_useParallaxCorrection);
+ m_renderInnerSrg->SetConstant(m_reflectionRenderData->m_exposureConstantIndex, m_renderExposure);
m_renderInnerSrg->SetImage(m_reflectionRenderData->m_reflectionCubeMapRenderImageIndex, m_cubeMapImage);
m_renderInnerSrg->Compile();
@@ -303,9 +307,10 @@ namespace AZ
const RPI::Ptr& rootPass = environmentCubeMapPipeline->GetRootPass();
rootPass->AddChild(m_environmentCubeMapPass);
- // store the current IBL exposure value
+ // store the current IBL exposure values
Data::Instance sceneSrg = m_scene->GetShaderResourceGroup();
- m_previousExposure = sceneSrg->GetConstant(m_iblExposureConstantIndex);
+ m_previousGlobalIblExposure = sceneSrg->GetConstant(m_globalIblExposureConstantIndex);
+ m_previousSkyBoxExposure = sceneSrg->GetConstant(m_skyBoxExposureConstantIndex);
m_scene->AddRenderPipeline(environmentCubeMapPipeline);
}
@@ -326,6 +331,17 @@ namespace AZ
m_meshFeatureProcessor->SetVisible(m_visualizationMeshHandle, showVisualization);
}
+ void ReflectionProbe::SetRenderExposure(float renderExposure)
+ {
+ m_renderExposure = renderExposure;
+ m_updateSrg = true;
+ }
+
+ void ReflectionProbe::SetBakeExposure(float bakeExposure)
+ {
+ m_bakeExposure = bakeExposure;
+ }
+
const RHI::DrawPacket* ReflectionProbe::BuildDrawPacket(
const Data::Instance& srg,
const RPI::Ptr& pipelineState,
diff --git a/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.h b/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.h
index bee304c5b9..17ef54367b 100644
--- a/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.h
+++ b/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.h
@@ -61,6 +61,7 @@ namespace AZ
RHI::ShaderInputNameIndex m_outerObbHalfLengthsRenderConstantIndex = "m_outerObbHalfLengths";
RHI::ShaderInputNameIndex m_innerObbHalfLengthsRenderConstantIndex = "m_innerObbHalfLengths";
RHI::ShaderInputNameIndex m_useParallaxCorrectionRenderConstantIndex = "m_useParallaxCorrection";
+ RHI::ShaderInputNameIndex m_exposureConstantIndex = "m_exposure";
RHI::ShaderInputNameIndex m_reflectionCubeMapRenderImageIndex = "m_reflectionCubeMap";
};
@@ -106,6 +107,14 @@ namespace AZ
// enables or disables rendering of the visualization sphere
void ShowVisualization(bool showVisualization);
+ // the exposure to use when rendering meshes with this probe's cubemap
+ void SetRenderExposure(float renderExposure);
+ float GetRenderExposure() const { return m_renderExposure; }
+
+ // the exposure to use when baking the probe cubemap
+ void SetBakeExposure(float bakeExposure);
+ float GetBakeExposure() const { return m_bakeExposure; }
+
private:
AZ_DISABLE_COPY_MOVE(ReflectionProbe);
@@ -157,6 +166,8 @@ namespace AZ
RHI::ConstPtr m_blendWeightDrawPacket;
RHI::ConstPtr m_renderOuterDrawPacket;
RHI::ConstPtr m_renderInnerDrawPacket;
+ float m_renderExposure = 0.0f;
+ float m_bakeExposure = 0.0f;
bool m_updateSrg = false;
const RHI::DrawItemSortKey InvalidSortKey = static_cast(-1);
@@ -169,8 +180,10 @@ namespace AZ
RPI::Ptr m_environmentCubeMapPass = nullptr;
RPI::RenderPipelineId m_environmentCubeMapPipelineId;
BuildCubeMapCallback m_callback;
- RHI::ShaderInputNameIndex m_iblExposureConstantIndex = "m_iblExposure";
- float m_previousExposure = 0.0f;
+ RHI::ShaderInputNameIndex m_globalIblExposureConstantIndex = "m_iblExposure";
+ RHI::ShaderInputNameIndex m_skyBoxExposureConstantIndex = "m_cubemapExposure";
+ float m_previousGlobalIblExposure = 0.0f;
+ float m_previousSkyBoxExposure = 0.0f;
bool m_buildingCubeMap = false;
};
diff --git a/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbeFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbeFeatureProcessor.cpp
index 52d089ae0d..e9038858ad 100644
--- a/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbeFeatureProcessor.cpp
+++ b/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbeFeatureProcessor.cpp
@@ -283,6 +283,18 @@ namespace AZ
probe->ShowVisualization(showVisualization);
}
+ void ReflectionProbeFeatureProcessor::SetRenderExposure(const ReflectionProbeHandle& probe, float renderExposure)
+ {
+ AZ_Assert(probe.get(), "SetRenderExposure called with an invalid handle");
+ probe->SetRenderExposure(renderExposure);
+ }
+
+ void ReflectionProbeFeatureProcessor::SetBakeExposure(const ReflectionProbeHandle& probe, float bakeExposure)
+ {
+ AZ_Assert(probe.get(), "SetBakeExposure called with an invalid handle");
+ probe->SetBakeExposure(bakeExposure);
+ }
+
void ReflectionProbeFeatureProcessor::FindReflectionProbes(const Vector3& position, ReflectionProbeVector& reflectionProbes)
{
reflectionProbes.clear();
@@ -431,7 +443,12 @@ namespace AZ
{
// load shader
shader = RPI::LoadCriticalShader(filePath);
- AZ_Error("ReflectionProbeFeatureProcessor", shader, "Failed to find asset for shader [%s]", filePath);
+
+ if (shader == nullptr)
+ {
+ AZ_Error("ReflectionProbeFeatureProcessor", false, "Failed to find asset for shader [%s]", filePath);
+ return;
+ }
// store drawlist tag
drawListTag = shader->GetDrawListTag();
diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI/SwapChain.h b/Gems/Atom/RHI/Code/Include/Atom/RHI/SwapChain.h
index 97ac3baa90..abad1fb263 100644
--- a/Gems/Atom/RHI/Code/Include/Atom/RHI/SwapChain.h
+++ b/Gems/Atom/RHI/Code/Include/Atom/RHI/SwapChain.h
@@ -75,6 +75,9 @@ namespace AZ
//! Return True if the swap chain prefers exclusive full screen mode and a transition happened, false otherwise.
virtual bool SetExclusiveFullScreenState([[maybe_unused]]bool fullScreenState) { return false; }
+ //! Recreate the swapchain if it becomes invalid during presenting. This should happen at the end of the frame
+ //! due to images being used as attachments in the frame graph.
+ virtual void ProcessRecreation() {};
protected:
SwapChain();
@@ -98,6 +101,14 @@ namespace AZ
//////////////////////////////////////////////////////////////////////////
+ //! Shutdown and clear all the images.
+ void ShutdownImages();
+
+ //! Initialized all the images.
+ ResultCode InitImages();
+
+ //! Flag indicating if swapchain recreation is needed at the end of the frame.
+ bool m_pendingRecreation = false;
private:
bool ValidateDescriptor(const SwapChainDescriptor& descriptor) const;
diff --git a/Gems/Atom/RHI/Code/Source/RHI/FrameGraphAttachmentDatabase.cpp b/Gems/Atom/RHI/Code/Source/RHI/FrameGraphAttachmentDatabase.cpp
index 6bac2b8c7d..388277ff59 100644
--- a/Gems/Atom/RHI/Code/Source/RHI/FrameGraphAttachmentDatabase.cpp
+++ b/Gems/Atom/RHI/Code/Source/RHI/FrameGraphAttachmentDatabase.cpp
@@ -134,7 +134,6 @@ namespace AZ
m_scopeAttachmentLookup.clear();
m_imageAttachments.clear();
m_bufferAttachments.clear();
- m_swapChainAttachments.clear();
m_importedImageAttachments.clear();
m_importedBufferAttachments.clear();
m_transientImageAttachments.clear();
@@ -153,6 +152,13 @@ namespace AZ
delete attachment;
}
m_attachments.clear();
+
+ for (auto swapchainAttachment : m_swapChainAttachments)
+ {
+ swapchainAttachment->GetSwapChain()->ProcessRecreation();
+ }
+
+ m_swapChainAttachments.clear();
}
ImageDescriptor FrameGraphAttachmentDatabase::GetImageDescriptor(const AttachmentId& attachmentId) const
diff --git a/Gems/Atom/RHI/Code/Source/RHI/SwapChain.cpp b/Gems/Atom/RHI/Code/Source/RHI/SwapChain.cpp
index ff1f0e69a6..074eedf1b6 100644
--- a/Gems/Atom/RHI/Code/Source/RHI/SwapChain.cpp
+++ b/Gems/Atom/RHI/Code/Source/RHI/SwapChain.cpp
@@ -58,43 +58,68 @@ namespace AZ
// Overwrite descriptor dimensions with the native ones (the ones assigned by the platform) returned by InitInternal.
m_descriptor.m_dimensions = nativeDimensions;
- m_images.reserve(m_descriptor.m_dimensions.m_imageCount);
+ resultCode = InitImages();
+ }
- for (uint32_t imageIdx = 0; imageIdx < m_descriptor.m_dimensions.m_imageCount; ++imageIdx)
- {
- m_images.emplace_back(RHI::Factory::Get().CreateImage());
- }
+ return resultCode;
+ }
- InitImageRequest request;
+ void SwapChain::ShutdownImages()
+ {
+ // Shutdown existing set of images.
+ uint32_t imageSize = aznumeric_cast(m_images.size());
+ for (uint32_t imageIdx = 0; imageIdx < imageSize; ++imageIdx)
+ {
+ m_images[imageIdx]->Shutdown();
+ }
- RHI::ImageDescriptor& imageDescriptor = request.m_descriptor;
- imageDescriptor.m_dimension = RHI::ImageDimension::Image2D;
- imageDescriptor.m_bindFlags = RHI::ImageBindFlags::Color;
- imageDescriptor.m_size.m_width = m_descriptor.m_dimensions.m_imageWidth;
- imageDescriptor.m_size.m_height = m_descriptor.m_dimensions.m_imageHeight;
- imageDescriptor.m_format = m_descriptor.m_dimensions.m_imageFormat;
+ m_images.clear();
+ }
- for (uint32_t imageIdx = 0; imageIdx < m_descriptor.m_dimensions.m_imageCount; ++imageIdx)
- {
- request.m_image = m_images[imageIdx].get();
- request.m_imageIndex = imageIdx;
+ ResultCode SwapChain::InitImages()
+ {
+ ResultCode resultCode = ResultCode::Success;
+
+ m_images.reserve(m_descriptor.m_dimensions.m_imageCount);
+
+ // If the new display mode has more buffers, add them.
+ for (uint32_t i = 0; i < m_descriptor.m_dimensions.m_imageCount; ++i)
+ {
+ m_images.emplace_back(RHI::Factory::Get().CreateImage());
+ }
+
+ InitImageRequest request;
+
+ RHI::ImageDescriptor& imageDescriptor = request.m_descriptor;
+ imageDescriptor.m_dimension = RHI::ImageDimension::Image2D;
+ imageDescriptor.m_bindFlags = RHI::ImageBindFlags::Color;
+ imageDescriptor.m_size.m_width = m_descriptor.m_dimensions.m_imageWidth;
+ imageDescriptor.m_size.m_height = m_descriptor.m_dimensions.m_imageHeight;
+ imageDescriptor.m_format = m_descriptor.m_dimensions.m_imageFormat;
- resultCode = ImagePoolBase::InitImage(
- request.m_image,
- imageDescriptor,
- [this, &request]()
+ for (uint32_t imageIdx = 0; imageIdx < m_descriptor.m_dimensions.m_imageCount; ++imageIdx)
+ {
+ request.m_image = m_images[imageIdx].get();
+ request.m_imageIndex = imageIdx;
+
+ resultCode = ImagePoolBase::InitImage(
+ request.m_image, imageDescriptor,
+ [this, &request]()
{
return InitImageInternal(request);
});
- if (resultCode != ResultCode::Success)
- {
- Shutdown();
- break;
- }
+ if (resultCode != ResultCode::Success)
+ {
+ AZ_Error("Swapchain", false, "Failed to initialize images.");
+ Shutdown();
+ break;
}
}
+ // Reset the current index back to 0 so we match the platform swap chain.
+ m_currentImageIndex = 0;
+
return resultCode;
}
@@ -105,63 +130,15 @@ namespace AZ
}
ResultCode SwapChain::Resize(const RHI::SwapChainDimensions& dimensions)
- {
- // Shutdown existing set of images.
- for (uint32_t imageIdx = 0; imageIdx < GetImageCount(); ++imageIdx)
- {
- m_images[imageIdx]->Shutdown();
- }
+ {
+ ShutdownImages();
SwapChainDimensions nativeDimensions = dimensions;
ResultCode resultCode = ResizeInternal(dimensions, &nativeDimensions);
if (resultCode == ResultCode::Success)
{
m_descriptor.m_dimensions = nativeDimensions;
- m_images.reserve(m_descriptor.m_dimensions.m_imageCount);
-
- // If the new display mode has more buffers, add them.
- while (m_images.size() < static_cast(m_descriptor.m_dimensions.m_imageCount))
- {
- m_images.emplace_back(RHI::Factory::Get().CreateImage());
- }
-
- // If it has fewer, trim down.
- while (m_images.size() > static_cast(m_descriptor.m_dimensions.m_imageCount))
- {
- m_images.pop_back();
- }
-
- InitImageRequest request;
-
- RHI::ImageDescriptor& imageDescriptor = request.m_descriptor;
- imageDescriptor.m_dimension = RHI::ImageDimension::Image2D;
- imageDescriptor.m_bindFlags = RHI::ImageBindFlags::Color;
- imageDescriptor.m_size.m_width = m_descriptor.m_dimensions.m_imageWidth;
- imageDescriptor.m_size.m_height = m_descriptor.m_dimensions.m_imageHeight;
- imageDescriptor.m_format = m_descriptor.m_dimensions.m_imageFormat;
-
- for (uint32_t imageIdx = 0; imageIdx < GetImageCount(); ++imageIdx)
- {
- request.m_image = m_images[imageIdx].get();
- request.m_imageIndex = imageIdx;
-
- resultCode = ImagePoolBase::InitImage(
- request.m_image,
- imageDescriptor,
- [this, &request]()
- {
- return InitImageInternal(request);
- });
-
- if (resultCode != ResultCode::Success)
- {
- Shutdown();
- break;
- }
- }
-
- // Reset the current index back to 0 so we match the platform swap chain.
- m_currentImageIndex = 0;
+ resultCode = InitImages();
}
return resultCode;
@@ -188,7 +165,7 @@ namespace AZ
uint32_t SwapChain::GetImageCount() const
{
- return static_cast(m_images.size());
+ return aznumeric_cast(m_images.size());
}
uint32_t SwapChain::GetCurrentImageIndex() const
@@ -209,8 +186,18 @@ namespace AZ
void SwapChain::Present()
{
AZ_TRACE_METHOD();
- m_currentImageIndex = PresentInternal();
- AZ_Assert(m_currentImageIndex < m_images.size(), "Invalid image index");
+ // Due to swapchain recreation, the images are refreshed.
+ // There is no need to present swapchain for this frame.
+ const uint32_t imageCount = aznumeric_cast(m_images.size());
+ if (imageCount == 0)
+ {
+ return;
+ }
+ else
+ {
+ m_currentImageIndex = PresentInternal();
+ AZ_Assert(m_currentImageIndex < imageCount, "Invalid image index");
+ }
}
}
}
diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/SwapChain.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/SwapChain.cpp
index bef2b154e1..19c88ec34f 100644
--- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/SwapChain.cpp
+++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/SwapChain.cpp
@@ -59,6 +59,19 @@ namespace AZ
m_swapChainBarrier.m_isValid = true;
}
+ void SwapChain::ProcessRecreation()
+ {
+ if (m_pendingRecreation)
+ {
+ ShutdownImages();
+ InvalidateNativeSwapChain();
+ CreateSwapchain();
+ InitImages();
+
+ m_pendingRecreation = false;
+ }
+ }
+
void SwapChain::SetVerticalSyncIntervalInternal(uint32_t previousVsyncInterval)
{
if (GetDescriptor().m_verticalSyncInterval == 0 || previousVsyncInterval == 0)
@@ -231,8 +244,7 @@ namespace AZ
// VK_SUBOPTIMAL_KHR is treated as success, but we better update the surface info as well.
if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR)
{
- InvalidateNativeSwapChain();
- CreateSwapchain();
+ m_pendingRecreation = true;
}
else
{
@@ -246,18 +258,16 @@ namespace AZ
}
};
- m_presentationQueue->QueueCommand(AZStd::move(presentCommand));
-
uint32_t acquiredImageIndex = GetCurrentImageIndex();
RHI::ResultCode result = AcquireNewImage(&acquiredImageIndex);
if (result == RHI::ResultCode::Fail)
{
- InvalidateNativeSwapChain();
- CreateSwapchain();
+ m_pendingRecreation = true;
return 0;
}
else
{
+ m_presentationQueue->QueueCommand(AZStd::move(presentCommand));
return acquiredImageIndex;
}
}
diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/SwapChain.h b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/SwapChain.h
index ee2ff3c207..68abc97b2d 100644
--- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/SwapChain.h
+++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/SwapChain.h
@@ -51,6 +51,7 @@ namespace AZ
void QueueBarrier(const VkPipelineStageFlags src, const VkPipelineStageFlags dst, const VkImageMemoryBarrier& imageBarrier);
+ void ProcessRecreation() override;
private:
SwapChain() = default;
diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialSourceData.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialSourceData.h
index a67477f061..53d3072370 100644
--- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialSourceData.h
+++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialSourceData.h
@@ -31,6 +31,7 @@ namespace AZ
static constexpr const char UvGroupName[] = "uvSets";
class MaterialAsset;
+ class MaterialAssetCreator;
//! This is a simple data structure for serializing in/out material source files.
class MaterialSourceData final
@@ -78,15 +79,33 @@ namespace AZ
//! Creates a MaterialAsset from the MaterialSourceData content.
//! @param assetId ID for the MaterialAsset
- //! @param materialSourceFilePath Indicates the path of the .material file that the MaterialSourceData represents. Used for resolving file-relative paths.
+ //! @param materialSourceFilePath Indicates the path of the .material file that the MaterialSourceData represents. Used for
+ //! resolving file-relative paths.
//! @param elevateWarnings Indicates whether to treat warnings as errors
//! @param includeMaterialPropertyNames Indicates whether to save material property names into the material asset file
Outcome> CreateMaterialAsset(
Data::AssetId assetId,
AZStd::string_view materialSourceFilePath = "",
bool elevateWarnings = true,
- bool includeMaterialPropertyNames = true
- ) const;
+ bool includeMaterialPropertyNames = true) const;
+
+ //! Creates a MaterialAsset from the MaterialSourceData content.
+ //! @param assetId ID for the MaterialAsset
+ //! @param materialSourceFilePath Indicates the path of the .material file that the MaterialSourceData represents. Used for
+ //! resolving file-relative paths.
+ //! @param elevateWarnings Indicates whether to treat warnings as errors
+ //! @param includeMaterialPropertyNames Indicates whether to save material property names into the material asset file
+ //! @param sourceDependencies if not null, will be populated with a set of all of the loaded material and material type paths
+ Outcome> CreateMaterialAssetFromSourceData(
+ Data::AssetId assetId,
+ AZStd::string_view materialSourceFilePath = "",
+ bool elevateWarnings = true,
+ bool includeMaterialPropertyNames = true,
+ AZStd::unordered_set* sourceDependencies = nullptr) const;
+
+ private:
+ void ApplyPropertiesToAssetCreator(
+ AZ::RPI::MaterialAssetCreator& materialAssetCreator, const AZStd::string_view& materialSourceFilePath) const;
};
} // namespace RPI
} // namespace AZ
diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/RPISystem.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/RPISystem.h
index 92370c5a82..9138c0b418 100644
--- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/RPISystem.h
+++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/RPISystem.h
@@ -97,8 +97,7 @@ namespace AZ
// SystemTickBus::OnTick
void OnSystemTick() override;
- // Fill system time and game time information for simulation or rendering
- void FillTickTimeInfo();
+ float GetCurrentTime();
// The set of core asset handlers registered by the system.
AZStd::vector> m_assetHandlers;
@@ -124,7 +123,8 @@ namespace AZ
// The job policy used for feature processor's rendering prepare
RHI::JobPolicy m_prepareRenderJobPolicy = RHI::JobPolicy::Parallel;
- TickTimeInfo m_tickTime;
+ ScriptTimePoint m_startTime;
+ float m_currentSimulationTime = 0.0f;
RPISystemDescriptor m_descriptor;
diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/RenderPipeline.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/RenderPipeline.h
index fcf812cc38..6cba0c774f 100644
--- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/RenderPipeline.h
+++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/RenderPipeline.h
@@ -32,7 +32,6 @@ namespace AZ
namespace RPI
{
class Scene;
- struct TickTimeInfo;
class ShaderResourceGroup;
class AnyAsset;
class WindowContext;
@@ -203,7 +202,7 @@ namespace AZ
void OnRemovedFromScene(Scene* scene);
// Called when this pipeline is about to be rendered
- void OnStartFrame(const TickTimeInfo& tick);
+ void OnStartFrame(float time);
// Called when the rendering of current frame is finished.
void OnFrameEnd();
diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Scene.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Scene.h
index f86383101a..1411a8d996 100644
--- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Scene.h
+++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Scene.h
@@ -48,14 +48,6 @@ namespace AZ
// Callback function to modify values of a ShaderResourceGroup
using ShaderResourceGroupCallback = AZStd::function;
- //! A structure for ticks which contains system time and game time.
- struct TickTimeInfo
- {
- float m_currentGameTime;
- float m_gameDeltaTime = 0;
- };
-
-
class Scene final
: public SceneRequestBus::Handler
{
@@ -179,12 +171,14 @@ namespace AZ
// Cpu simulation which runs all active FeatureProcessor Simulate() functions.
// @param jobPolicy if it's JobPolicy::Parallel, the function will spawn a job thread for each FeatureProcessor's simulation.
- void Simulate(const TickTimeInfo& tickInfo, RHI::JobPolicy jobPolicy);
+ // @param simulationTime the number of seconds since the application started
+ void Simulate(RHI::JobPolicy jobPolicy, float simulationTime);
// Collect DrawPackets from FeatureProcessors
// @param jobPolicy if it's JobPolicy::Parallel, the function will spawn a job thread for each FeatureProcessor's
// PrepareRender.
- void PrepareRender(const TickTimeInfo& tickInfo, RHI::JobPolicy jobPolicy);
+ // @param simulationTime the number of seconds since the application started; this is the same time value that was passed to Simulate()
+ void PrepareRender(RHI::JobPolicy jobPolicy, float simulationTime);
// Function called when the current frame is finished rendering.
void OnFrameEnd();
@@ -267,6 +261,7 @@ namespace AZ
// Registry which allocates draw filter tag for RenderPipeline
RHI::Ptr m_drawFilterTagRegistry;
+ RHI::ShaderInputConstantIndex m_timeInputIndex;
float m_simulationTime;
};
diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/AssetCreator.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/AssetCreator.h
index abdbe9cdce..79d43d0b8d 100644
--- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/AssetCreator.h
+++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/AssetCreator.h
@@ -118,7 +118,7 @@ namespace AZ
ResetIssueCounts(); // Because the asset creator can be used multiple times
- m_asset = Data::AssetManager::Instance().CreateAsset(assetId, AZ::Data::AssetLoadBehavior::PreLoad);
+ m_asset = Data::Asset(assetId, aznew AssetDataT, AZ::Data::AssetLoadBehavior::PreLoad);
m_beginCalled = true;
if (!m_asset)
@@ -138,6 +138,7 @@ namespace AZ
}
else
{
+ Data::AssetManager::Instance().AssignAssetData(m_asset);
result = AZStd::move(m_asset);
success = true;
}
diff --git a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/LuaMaterialFunctorSourceData.cpp b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/LuaMaterialFunctorSourceData.cpp
index b230953f8c..14ef3bb17d 100644
--- a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/LuaMaterialFunctorSourceData.cpp
+++ b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/LuaMaterialFunctorSourceData.cpp
@@ -137,7 +137,10 @@ namespace AZ
}
else if (!m_luaSourceFile.empty())
{
- auto loadOutcome = RPI::AssetUtils::LoadAsset(materialTypeSourceFilePath, m_luaSourceFile);
+ // The sub ID for script assets must be explicit.
+ // LUA source files output a compiled as well as an uncompiled asset, sub Ids of 1 and 2.
+ auto loadOutcome =
+ RPI::AssetUtils::LoadAsset(materialTypeSourceFilePath, m_luaSourceFile, ScriptAsset::CompiledAssetSubId);
if (!loadOutcome)
{
AZ_Error("LuaMaterialFunctorSourceData", false, "Could not load script file '%s'", m_luaSourceFile.c_str());
diff --git a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceData.cpp b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceData.cpp
index 1467b017d5..d6308ec655 100644
--- a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceData.cpp
+++ b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceData.cpp
@@ -17,6 +17,7 @@
#include
#include
#include
+#include
#include
#include
@@ -126,7 +127,8 @@ namespace AZ
return changesWereApplied ? ApplyVersionUpdatesResult::UpdatesApplied : ApplyVersionUpdatesResult::NoUpdates;
}
- Outcome > MaterialSourceData::CreateMaterialAsset(Data::AssetId assetId, AZStd::string_view materialSourceFilePath, bool elevateWarnings, bool includeMaterialPropertyNames) const
+ Outcome> MaterialSourceData::CreateMaterialAsset(
+ Data::AssetId assetId, AZStd::string_view materialSourceFilePath, bool elevateWarnings, bool includeMaterialPropertyNames) const
{
MaterialAssetCreator materialAssetCreator;
materialAssetCreator.SetElevateWarnings(elevateWarnings);
@@ -172,6 +174,128 @@ namespace AZ
materialAssetCreator.Begin(assetId, *parentMaterialAsset.GetValue().Get(), includeMaterialPropertyNames);
}
+ ApplyPropertiesToAssetCreator(materialAssetCreator, materialSourceFilePath);
+
+ Data::Asset material;
+ if (materialAssetCreator.End(material))
+ {
+ return Success(material);
+ }
+ else
+ {
+ return Failure();
+ }
+ }
+
+ Outcome> MaterialSourceData::CreateMaterialAssetFromSourceData(
+ Data::AssetId assetId,
+ AZStd::string_view materialSourceFilePath,
+ bool elevateWarnings,
+ bool includeMaterialPropertyNames,
+ AZStd::unordered_set* sourceDependencies) const
+ {
+ const auto materialTypeSourcePath = AssetUtils::ResolvePathReference(materialSourceFilePath, m_materialType);
+ const auto materialTypeAssetId = AssetUtils::MakeAssetId(materialTypeSourcePath, 0);
+ if (!materialTypeAssetId.IsSuccess())
+ {
+ AZ_Error("MaterialSourceData", false, "Failed to create material type asset ID: '%s'.", materialTypeSourcePath.c_str());
+ return Failure();
+ }
+
+ MaterialTypeSourceData materialTypeSourceData;
+ if (!AZ::RPI::JsonUtils::LoadObjectFromFile(materialTypeSourcePath, materialTypeSourceData))
+ {
+ AZ_Error("MaterialSourceData", false, "Failed to load MaterialTypeSourceData: '%s'.", materialTypeSourcePath.c_str());
+ return Failure();
+ }
+
+ materialTypeSourceData.ResolveUvEnums();
+
+ const auto materialTypeAsset =
+ materialTypeSourceData.CreateMaterialTypeAsset(materialTypeAssetId.GetValue(), materialTypeSourcePath, elevateWarnings);
+ if (!materialTypeAsset.IsSuccess())
+ {
+ AZ_Error("MaterialSourceData", false, "Failed to create material type asset from source data: '%s'.", materialTypeSourcePath.c_str());
+ return Failure();
+ }
+
+ // Track all of the material and material type assets loaded while trying to create a material asset from source data. This will
+ // be used for evaluating circular dependencies and returned for external monitoring or other use.
+ AZStd::unordered_set dependencies;
+ dependencies.insert(materialSourceFilePath);
+ dependencies.insert(materialTypeSourcePath);
+
+ // Load and build a stack of MaterialSourceData from all of the parent materials in the hierarchy. Properties from the source
+ // data will be applied in reverse to the asset creator.
+ AZStd::vector parentSourceDataStack;
+
+ AZStd::string parentSourceRelPath = m_parentMaterial;
+ AZStd::string parentSourceAbsPath = AssetUtils::ResolvePathReference(materialSourceFilePath, parentSourceRelPath);
+ while (!parentSourceRelPath.empty())
+ {
+ if (!dependencies.insert(parentSourceAbsPath).second)
+ {
+ AZ_Error("MaterialSourceData", false, "Detected circular dependency between materials: '%s' and '%s'.", materialSourceFilePath.data(), parentSourceAbsPath.c_str());
+ return Failure();
+ }
+
+ MaterialSourceData parentSourceData;
+ if (!AZ::RPI::JsonUtils::LoadObjectFromFile(parentSourceAbsPath, parentSourceData))
+ {
+ AZ_Error("MaterialSourceData", false, "Failed to load MaterialSourceData for parent material: '%s'.", parentSourceAbsPath.c_str());
+ return Failure();
+ }
+
+ // Make sure that all materials in the hierarchy share the same material type
+ const auto parentTypeAssetId = AssetUtils::MakeAssetId(parentSourceAbsPath, parentSourceData.m_materialType, 0);
+ if (!parentTypeAssetId)
+ {
+ AZ_Error("MaterialSourceData", false, "Parent material asset ID wasn't found: '%s'.", parentSourceAbsPath.c_str());
+ return Failure();
+ }
+
+ if (parentTypeAssetId.GetValue() != materialTypeAssetId.GetValue())
+ {
+ AZ_Error("MaterialSourceData", false, "This material and its parent material do not share the same material type.");
+ return Failure();
+ }
+
+ // Get the location of the next parent material and push the source data onto the stack
+ parentSourceRelPath = parentSourceData.m_parentMaterial;
+ parentSourceAbsPath = AssetUtils::ResolvePathReference(parentSourceAbsPath, parentSourceRelPath);
+ parentSourceDataStack.emplace_back(AZStd::move(parentSourceData));
+ }
+
+ // Create the material asset from all the previously loaded source data
+ MaterialAssetCreator materialAssetCreator;
+ materialAssetCreator.SetElevateWarnings(elevateWarnings);
+ materialAssetCreator.Begin(assetId, *materialTypeAsset.GetValue().Get(), includeMaterialPropertyNames);
+
+ while (!parentSourceDataStack.empty())
+ {
+ parentSourceDataStack.back().ApplyPropertiesToAssetCreator(materialAssetCreator, materialSourceFilePath);
+ parentSourceDataStack.pop_back();
+ }
+
+ ApplyPropertiesToAssetCreator(materialAssetCreator, materialSourceFilePath);
+
+ Data::Asset material;
+ if (materialAssetCreator.End(material))
+ {
+ if (sourceDependencies)
+ {
+ sourceDependencies->insert(dependencies.begin(), dependencies.end());
+ }
+
+ return Success(material);
+ }
+
+ return Failure();
+ }
+
+ void MaterialSourceData::ApplyPropertiesToAssetCreator(
+ AZ::RPI::MaterialAssetCreator& materialAssetCreator, const AZStd::string_view& materialSourceFilePath) const
+ {
for (auto& group : m_properties)
{
for (auto& property : group.second)
@@ -183,43 +307,49 @@ namespace AZ
}
else
{
- MaterialPropertyIndex propertyIndex = materialAssetCreator.m_materialPropertiesLayout->FindPropertyIndex(propertyId.GetFullName());
+ MaterialPropertyIndex propertyIndex =
+ materialAssetCreator.m_materialPropertiesLayout->FindPropertyIndex(propertyId.GetFullName());
if (propertyIndex.IsValid())
{
- const MaterialPropertyDescriptor* propertyDescriptor = materialAssetCreator.m_materialPropertiesLayout->GetPropertyDescriptor(propertyIndex);
+ const MaterialPropertyDescriptor* propertyDescriptor =
+ materialAssetCreator.m_materialPropertiesLayout->GetPropertyDescriptor(propertyIndex);
switch (propertyDescriptor->GetDataType())
{
case MaterialPropertyDataType::Image:
- {
- Outcome> imageAssetResult = MaterialUtils::GetImageAssetReference(materialSourceFilePath, property.second.m_value.GetValue());
-
- if (imageAssetResult.IsSuccess())
- {
- auto& imageAsset = imageAssetResult.GetValue();
- // Load referenced images when load material
- imageAsset.SetAutoLoadBehavior(Data::AssetLoadBehavior::PreLoad);
- materialAssetCreator.SetPropertyValue(propertyId.GetFullName(), imageAsset);
- }
- else
{
- materialAssetCreator.ReportError("Material property '%s': Could not find the image '%s'", propertyId.GetFullName().GetCStr(), property.second.m_value.GetValue().data());
+ Outcome> imageAssetResult = MaterialUtils::GetImageAssetReference(
+ materialSourceFilePath, property.second.m_value.GetValue());
+
+ if (imageAssetResult.IsSuccess())
+ {
+ auto& imageAsset = imageAssetResult.GetValue();
+ // Load referenced images when load material
+ imageAsset.SetAutoLoadBehavior(Data::AssetLoadBehavior::PreLoad);
+ materialAssetCreator.SetPropertyValue(propertyId.GetFullName(), imageAsset);
+ }
+ else
+ {
+ materialAssetCreator.ReportError(
+ "Material property '%s': Could not find the image '%s'", propertyId.GetFullName().GetCStr(),
+ property.second.m_value.GetValue().data());
+ }
}
- }
- break;
+ break;
case MaterialPropertyDataType::Enum:
- {
- AZ::Name enumName = AZ::Name(property.second.m_value.GetValue());
- uint32_t enumValue = propertyDescriptor->GetEnumValue(enumName);
- if (enumValue == MaterialPropertyDescriptor::InvalidEnumValue)
{
- materialAssetCreator.ReportError("Enum value '%s' couldn't be found in the 'enumValues' list", enumName.GetCStr());
+ AZ::Name enumName = AZ::Name(property.second.m_value.GetValue());
+ uint32_t enumValue = propertyDescriptor->GetEnumValue(enumName);
+ if (enumValue == MaterialPropertyDescriptor::InvalidEnumValue)
+ {
+ materialAssetCreator.ReportError(
+ "Enum value '%s' couldn't be found in the 'enumValues' list", enumName.GetCStr());
+ }
+ else
+ {
+ materialAssetCreator.SetPropertyValue(propertyId.GetFullName(), enumValue);
+ }
}
- else
- {
- materialAssetCreator.SetPropertyValue(propertyId.GetFullName(), enumValue);
- }
- }
- break;
+ break;
default:
materialAssetCreator.SetPropertyValue(propertyId.GetFullName(), property.second.m_value);
break;
@@ -227,21 +357,12 @@ namespace AZ
}
else
{
- materialAssetCreator.ReportWarning("Can not find property id '%s' in MaterialPropertyLayout", propertyId.GetFullName().GetStringView().data());
+ materialAssetCreator.ReportWarning(
+ "Can not find property id '%s' in MaterialPropertyLayout", propertyId.GetFullName().GetStringView().data());
}
}
}
}
-
- Data::Asset material;
- if (materialAssetCreator.End(material))
- {
- return Success(material);
- }
- else
- {
- return Failure();
- }
}
} // namespace RPI
diff --git a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialTypeSourceData.cpp b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialTypeSourceData.cpp
index 08f57c7cd3..396ba71e14 100644
--- a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialTypeSourceData.cpp
+++ b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialTypeSourceData.cpp
@@ -393,11 +393,12 @@ namespace AZ
for (const ShaderVariantReferenceData& shaderRef : m_shaderCollection)
{
const auto& shaderFile = shaderRef.m_shaderFilePath;
- const auto& shaderAsset = AssetUtils::LoadAsset(materialTypeSourceFilePath, shaderFile, 0);
+ auto shaderAssetResult = AssetUtils::LoadAsset(materialTypeSourceFilePath, shaderFile, 0);
- if (shaderAsset)
+ if (shaderAssetResult)
{
- auto optionsLayout = shaderAsset.GetValue()->GetShaderOptionGroupLayout();
+ auto shaderAsset = shaderAssetResult.GetValue();
+ auto optionsLayout = shaderAsset->GetShaderOptionGroupLayout();
ShaderOptionGroup options{ optionsLayout };
for (auto& iter : shaderRef.m_shaderOptionValues)
{
@@ -408,12 +409,11 @@ namespace AZ
}
materialTypeAssetCreator.AddShader(
- shaderAsset.GetValue(), options.GetShaderVariantId(),
- shaderRef.m_shaderTag.IsEmpty() ? Uuid::CreateRandom().ToString() : shaderRef.m_shaderTag
- );
+ shaderAsset, options.GetShaderVariantId(),
+ shaderRef.m_shaderTag.IsEmpty() ? Uuid::CreateRandom().ToString() : shaderRef.m_shaderTag);
// Gather UV names
- const ShaderInputContract& shaderInputContract = shaderAsset.GetValue()->GetInputContract();
+ const ShaderInputContract& shaderInputContract = shaderAsset->GetInputContract();
for (const ShaderInputContract::StreamChannelInfo& channel : shaderInputContract.m_streamChannels)
{
const RHI::ShaderSemantic& semantic = channel.m_semantic;
@@ -493,15 +493,19 @@ namespace AZ
{
case MaterialPropertyDataType::Image:
{
- Outcome> imageAssetResult = MaterialUtils::GetImageAssetReference(materialTypeSourceFilePath, property.m_value.GetValue());
+ auto imageAssetResult = MaterialUtils::GetImageAssetReference(
+ materialTypeSourceFilePath, property.m_value.GetValue());
- if (imageAssetResult.IsSuccess())
+ if (imageAssetResult)
{
- materialTypeAssetCreator.SetPropertyValue(propertyId.GetFullName(), imageAssetResult.GetValue());
+ auto imageAsset = imageAssetResult.GetValue();
+ materialTypeAssetCreator.SetPropertyValue(propertyId.GetFullName(), imageAsset);
}
else
{
- materialTypeAssetCreator.ReportError("Material property '%s': Could not find the image '%s'", propertyId.GetFullName().GetCStr(), property.m_value.GetValue().data());
+ materialTypeAssetCreator.ReportError(
+ "Material property '%s': Could not find the image '%s'", propertyId.GetFullName().GetCStr(),
+ property.m_value.GetValue().data());
}
}
break;
diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/FullscreenTrianglePass.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/FullscreenTrianglePass.cpp
index 40dce7d138..828966f377 100644
--- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/FullscreenTrianglePass.cpp
+++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/FullscreenTrianglePass.cpp
@@ -136,6 +136,12 @@ namespace AZ
RHI::DrawLinear draw = RHI::DrawLinear();
draw.m_vertexCount = 3;
+ if (m_shader == nullptr)
+ {
+ AZ_Error("PassSystem", false, "[FullscreenTrianglePass]: Shader not loaded!");
+ return;
+ }
+
RHI::PipelineStateDescriptorForDraw pipelineStateDescriptor;
// [GFX TODO][ATOM-872] The pass should be able to drive the shader variant
diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/RPISystem.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/RPISystem.cpp
index 943966c13b..2b32503838 100644
--- a/Gems/Atom/RPI/Code/Source/RPI.Public/RPISystem.cpp
+++ b/Gems/Atom/RPI/Code/Source/RPI.Public/RPISystem.cpp
@@ -268,21 +268,23 @@ namespace AZ
AssetInitBus::Broadcast(&AssetInitBus::Events::PostLoadInit);
- // Update tick time info
- FillTickTimeInfo();
+ m_currentSimulationTime = GetCurrentTime();
for (auto& scene : m_scenes)
{
- scene->Simulate(m_tickTime, m_simulationJobPolicy);
+ scene->Simulate(m_simulationJobPolicy, m_currentSimulationTime);
}
}
- void RPISystem::FillTickTimeInfo()
+ float RPISystem::GetCurrentTime()
{
- AZ::TickRequestBus::BroadcastResult(m_tickTime.m_gameDeltaTime, &AZ::TickRequestBus::Events::GetTickDeltaTime);
- ScriptTimePoint currentTime;
- AZ::TickRequestBus::BroadcastResult(currentTime, &AZ::TickRequestBus::Events::GetTimeAtCurrentTick);
- m_tickTime.m_currentGameTime = static_cast(currentTime.GetSeconds());
+ ScriptTimePoint timeAtCurrentTick;
+ AZ::TickRequestBus::BroadcastResult(timeAtCurrentTick, &AZ::TickRequestBus::Events::GetTimeAtCurrentTick);
+
+ // We subtract the start time to maximize precision of the time value, since we will be converting it to a float.
+ double currentTime = timeAtCurrentTick.GetSeconds() - m_startTime.GetSeconds();
+
+ return aznumeric_cast(currentTime);
}
void RPISystem::RenderTick()
@@ -301,7 +303,7 @@ namespace AZ
// [GFX TODO] We may parallel scenes' prepare render.
for (auto& scenePtr : m_scenes)
{
- scenePtr->PrepareRender(m_tickTime, m_prepareRenderJobPolicy);
+ scenePtr->PrepareRender(m_prepareRenderJobPolicy, m_currentSimulationTime);
}
m_rhiSystem.FrameUpdate(
diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/RenderPipeline.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/RenderPipeline.cpp
index 6378a249a4..8d4303f187 100644
--- a/Gems/Atom/RPI/Code/Source/RPI.Public/RenderPipeline.cpp
+++ b/Gems/Atom/RPI/Code/Source/RPI.Public/RenderPipeline.cpp
@@ -375,7 +375,7 @@ namespace AZ
m_scene->RemoveRenderPipeline(m_nameId);
}
- void RenderPipeline::OnStartFrame([[maybe_unused]] const TickTimeInfo& tick)
+ void RenderPipeline::OnStartFrame([[maybe_unused]] float time)
{
AZ_PROFILE_SCOPE(RPI, "RenderPipeline: OnStartFrame");
diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp
index 41fefda656..b27e3518e1 100644
--- a/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp
+++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp
@@ -44,6 +44,9 @@ namespace AZ
{
auto shaderAsset = RPISystemInterface::Get()->GetCommonShaderAssetForSrgs();
scene->m_srg = ShaderResourceGroup::Create(shaderAsset, sceneSrgLayout->GetName());
+
+ // Set value for constants defined in SceneTimeSrg.azsli
+ scene->m_timeInputIndex = scene->m_srg->FindShaderInputConstantIndex(Name{ "m_time" });
}
scene->m_name = sceneDescriptor.m_nameId;
@@ -410,11 +413,11 @@ namespace AZ
//[GFX TODO]: the completion job should start here
}
- void Scene::Simulate([[maybe_unused]] const TickTimeInfo& tickInfo, RHI::JobPolicy jobPolicy)
+ void Scene::Simulate(RHI::JobPolicy jobPolicy, float simulationTime)
{
AZ_PROFILE_SCOPE(RPI, "Scene: Simulate");
- m_simulationTime = tickInfo.m_currentGameTime;
+ m_simulationTime = simulationTime;
// If previous simulation job wasn't done, wait for it to finish.
if (m_taskGraphActive)
@@ -483,11 +486,9 @@ namespace AZ
{
if (m_srg)
{
- // Set value for constants defined in SceneTimeSrg.azsli
- RHI::ShaderInputConstantIndex timeIndex = m_srg->FindShaderInputConstantIndex(Name{ "m_time" });
- if (timeIndex.IsValid())
+ if (m_timeInputIndex.IsValid())
{
- m_srg->SetConstant(timeIndex, m_simulationTime);
+ m_srg->SetConstant(m_timeInputIndex, m_simulationTime);
}
// signal any handlers to update values for their partial scene srg
@@ -620,7 +621,7 @@ namespace AZ
WaitAndCleanCompletionJob(finalizeDrawListsCompletion);
}
- void Scene::PrepareRender(const TickTimeInfo& tickInfo, RHI::JobPolicy jobPolicy)
+ void Scene::PrepareRender(RHI::JobPolicy jobPolicy, float simulationTime)
{
AZ_PROFILE_SCOPE(RPI, "Scene: PrepareRender");
@@ -644,7 +645,7 @@ namespace AZ
if (pipeline->NeedsRender())
{
activePipelines.push_back(pipeline);
- pipeline->OnStartFrame(tickInfo);
+ pipeline->OnStartFrame(simulationTime);
}
}
}
diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialTypeAsset.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialTypeAsset.cpp
index 76634201eb..48654d7769 100644
--- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialTypeAsset.cpp
+++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialTypeAsset.cpp
@@ -188,6 +188,10 @@ namespace AZ
void MaterialTypeAsset::SetReady()
{
m_status = AssetStatus::Ready;
+
+ // If this was created dynamically using MaterialTypeAssetCreator (which is what calls SetReady()),
+ // we need to connect to the AssetBus for reloads.
+ PostLoadInit();
}
bool MaterialTypeAsset::PostLoadInit()
diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/ShaderCollection.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/ShaderCollection.cpp
index 87076891dd..16813cb6b7 100644
--- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/ShaderCollection.cpp
+++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/ShaderCollection.cpp
@@ -126,8 +126,8 @@ namespace AZ
}
ShaderCollection::Item::Item()
+ : m_renderStatesOverlay(RHI::GetInvalidRenderStates())
{
- m_renderStatesOverlay = RHI::GetInvalidRenderStates();
}
ShaderCollection::Item& ShaderCollection::operator[](size_t i)
@@ -156,7 +156,8 @@ namespace AZ
}
ShaderCollection::Item::Item(const Data::Asset& shaderAsset, const AZ::Name& shaderTag, ShaderVariantId variantId)
- : m_shaderAsset(shaderAsset)
+ : m_renderStatesOverlay(RHI::GetInvalidRenderStates())
+ , m_shaderAsset(shaderAsset)
, m_shaderVariantId(variantId)
, m_shaderTag(shaderTag)
, m_shaderOptionGroup(shaderAsset->GetShaderOptionGroupLayout(), variantId)
@@ -164,7 +165,8 @@ namespace AZ
}
ShaderCollection::Item::Item(Data::Asset&& shaderAsset, const AZ::Name& shaderTag, ShaderVariantId variantId)
- : m_shaderAsset(AZStd::move(shaderAsset))
+ : m_renderStatesOverlay(RHI::GetInvalidRenderStates())
+ , m_shaderAsset(AZStd::move(shaderAsset))
, m_shaderVariantId(variantId)
, m_shaderTag(shaderTag)
, m_shaderOptionGroup(shaderAsset->GetShaderOptionGroupLayout(), variantId)
diff --git a/Gems/Atom/TestData/TestData/Materials/SkinTestCases/002_wrinkle_regression_test.material b/Gems/Atom/TestData/TestData/Materials/SkinTestCases/002_wrinkle_regression_test.material
index 400044d29f..78f597b14f 100644
--- a/Gems/Atom/TestData/TestData/Materials/SkinTestCases/002_wrinkle_regression_test.material
+++ b/Gems/Atom/TestData/TestData/Materials/SkinTestCases/002_wrinkle_regression_test.material
@@ -33,7 +33,7 @@
},
"subsurfaceScattering": {
"enableSubsurfaceScattering": true,
- "influenceMap": "Objects/Lucy/Lucy_thickness.tif",
+ "influenceMap": "TestData/Textures/checker8x8_gray_512.png",
"scatterDistance": 15.0,
"subsurfaceScatterFactor": 0.4300000071525574,
"thicknessMap": "Objects/Lucy/Lucy_thickness.tif",
@@ -47,8 +47,7 @@
0.3182879388332367,
0.16388189792633058,
1.0
- ],
- "useInfluenceMap": false
+ ]
},
"wrinkleLayers": {
"baseColorMap1": "TestData/Textures/cc0/Lava004_1K_Color.jpg",
@@ -61,4 +60,4 @@
"normalMap2": "TestData/Textures/TextureHaven/4k_castle_brick_02_red/4k_castle_brick_02_red_normal.png"
}
}
-}
+}
\ No newline at end of file
diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Document/AtomToolsDocumentSystemComponent.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Document/AtomToolsDocumentSystemComponent.cpp
index 5652e9fe23..00de2a7c4c 100644
--- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Document/AtomToolsDocumentSystemComponent.cpp
+++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Document/AtomToolsDocumentSystemComponent.cpp
@@ -159,7 +159,7 @@ namespace AtomToolsFramework
void AtomToolsDocumentSystemComponent::OnDocumentExternallyModified(const AZ::Uuid& documentId)
{
- m_documentIdsToReopen.insert(documentId);
+ m_documentIdsWithExternalChanges.insert(documentId);
if (!AZ::TickBus::Handler::BusIsConnected())
{
AZ::TickBus::Handler::BusConnect();
@@ -168,7 +168,7 @@ namespace AtomToolsFramework
void AtomToolsDocumentSystemComponent::OnDocumentDependencyModified(const AZ::Uuid& documentId)
{
- m_documentIdsToReopen.insert(documentId);
+ m_documentIdsWithDependencyChanges.insert(documentId);
if (!AZ::TickBus::Handler::BusIsConnected())
{
AZ::TickBus::Handler::BusConnect();
@@ -177,7 +177,7 @@ namespace AtomToolsFramework
void AtomToolsDocumentSystemComponent::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
{
- for (const AZ::Uuid& documentId : m_documentIdsToReopen)
+ for (const AZ::Uuid& documentId : m_documentIdsWithExternalChanges)
{
AZStd::string documentPath;
AtomToolsDocumentRequestBus::EventResult(documentPath, documentId, &AtomToolsDocumentRequestBus::Events::GetAbsolutePath);
@@ -191,6 +191,8 @@ namespace AtomToolsFramework
continue;
}
+ m_documentIdsWithDependencyChanges.erase(documentId);
+
AtomToolsFramework::TraceRecorder traceRecorder(m_maxMessageBoxLineCount);
bool openResult = false;
@@ -204,7 +206,7 @@ namespace AtomToolsFramework
}
}
- for (const AZ::Uuid& documentId : m_documentIdsToReopen)
+ for (const AZ::Uuid& documentId : m_documentIdsWithDependencyChanges)
{
AZStd::string documentPath;
AtomToolsDocumentRequestBus::EventResult(documentPath, documentId, &AtomToolsDocumentRequestBus::Events::GetAbsolutePath);
@@ -231,8 +233,8 @@ namespace AtomToolsFramework
}
}
- m_documentIdsToReopen.clear();
- m_documentIdsToReopen.clear();
+ m_documentIdsWithDependencyChanges.clear();
+ m_documentIdsWithExternalChanges.clear();
AZ::TickBus::Handler::BusDisconnect();
}
diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Document/AtomToolsDocumentSystemComponent.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Document/AtomToolsDocumentSystemComponent.h
index 9c556a07e7..a0f5eb085d 100644
--- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Document/AtomToolsDocumentSystemComponent.h
+++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Document/AtomToolsDocumentSystemComponent.h
@@ -85,8 +85,8 @@ namespace AtomToolsFramework
AZStd::intrusive_ptr m_settings;
AZStd::function m_documentCreator;
AZStd::unordered_map> m_documentMap;
- AZStd::unordered_set m_documentIdsToRebuild;
- AZStd::unordered_set m_documentIdsToReopen;
+ AZStd::unordered_set m_documentIdsWithExternalChanges;
+ AZStd::unordered_set m_documentIdsWithDependencyChanges;
const size_t m_maxMessageBoxLineCount = 15;
};
} // namespace AtomToolsFramework
diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.cpp
index 98af749261..ee58cbea3e 100644
--- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.cpp
+++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.cpp
@@ -567,26 +567,26 @@ namespace MaterialEditor
}
}
- void MaterialDocument::SourceFileChanged(AZStd::string relativePath, AZStd::string scanFolder, AZ::Uuid sourceUUID)
+ void MaterialDocument::SourceFileChanged(AZStd::string relativePath, AZStd::string scanFolder, [[maybe_unused]] AZ::Uuid sourceUUID)
{
- if (m_sourceAssetId.m_guid == sourceUUID)
+ auto sourcePath = AZ::RPI::AssetUtils::ResolvePathReference(scanFolder, relativePath);
+
+ if (m_absolutePath == sourcePath)
{
// ignore notifications caused by saving the open document
if (!m_saveTriggeredInternally)
{
AZ_TracePrintf("MaterialDocument", "Material document changed externally: '%s'.\n", m_absolutePath.c_str());
- AtomToolsFramework::AtomToolsDocumentNotificationBus::Broadcast(&AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentExternallyModified, m_id);
+ AtomToolsFramework::AtomToolsDocumentNotificationBus::Broadcast(
+ &AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentExternallyModified, m_id);
}
m_saveTriggeredInternally = false;
}
- }
-
- void MaterialDocument::OnAssetReloaded(AZ::Data::Asset asset)
- {
- if (m_dependentAssetIds.find(asset->GetId()) != m_dependentAssetIds.end())
+ else if (m_sourceDependencies.find(sourcePath) != m_sourceDependencies.end())
{
AZ_TracePrintf("MaterialDocument", "Material document dependency changed: '%s'.\n", m_absolutePath.c_str());
- AtomToolsFramework::AtomToolsDocumentNotificationBus::Broadcast(&AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentDependencyModified, m_id);
+ AtomToolsFramework::AtomToolsDocumentNotificationBus::Broadcast(
+ &AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentDependencyModified, m_id);
}
}
@@ -655,7 +655,6 @@ namespace MaterialEditor
return false;
}
- m_sourceAssetId = sourceAssetInfo.m_assetId;
m_relativePath = sourceAssetInfo.m_relativePath;
if (!AzFramework::StringFunc::Path::Normalize(m_relativePath))
{
@@ -722,14 +721,15 @@ namespace MaterialEditor
// we can create the asset dynamically from the source data.
// Long term, the material document should not be concerned with assets at all. The viewport window should be the
// only thing concerned with assets or instances.
- auto createResult = m_materialSourceData.CreateMaterialAsset(Uuid::CreateRandom(), m_absolutePath, true);
- if (!createResult)
+ auto materialAssetResult =
+ m_materialSourceData.CreateMaterialAssetFromSourceData(Uuid::CreateRandom(), m_absolutePath, true, true, &m_sourceDependencies);
+ if (!materialAssetResult)
{
AZ_Error("MaterialDocument", false, "Material asset could not be created from source data: '%s'.", m_absolutePath.c_str());
return false;
}
- m_materialAsset = createResult.GetValue();
+ m_materialAsset = materialAssetResult.GetValue();
if (!m_materialAsset.IsReady())
{
AZ_Error("MaterialDocument", false, "Material asset is not ready: '%s'.", m_absolutePath.c_str());
@@ -743,28 +743,35 @@ namespace MaterialEditor
return false;
}
- // track material type asset to notify when dependencies change
- m_dependentAssetIds.insert(materialTypeAsset->GetId());
- AZ::Data::AssetBus::MultiHandler::BusConnect(materialTypeAsset->GetId());
-
AZStd::array_view parentPropertyValues = materialTypeAsset->GetDefaultPropertyValues();
AZ::Data::Asset parentMaterialAsset;
if (!m_materialSourceData.m_parentMaterial.empty())
{
- // There is a parent for this material
- auto parentMaterialResult = AssetUtils::LoadAsset(m_absolutePath, m_materialSourceData.m_parentMaterial);
- if (!parentMaterialResult)
+ AZ::RPI::MaterialSourceData parentMaterialSourceData;
+ const auto parentMaterialFilePath = AssetUtils::ResolvePathReference(m_absolutePath, m_materialSourceData.m_parentMaterial);
+ if (!AZ::RPI::JsonUtils::LoadObjectFromFile(parentMaterialFilePath, parentMaterialSourceData))
{
- AZ_Error("MaterialDocument", false, "Parent material asset could not be loaded: '%s'.", m_materialSourceData.m_parentMaterial.c_str());
+ AZ_Error("MaterialDocument", false, "Material parent source data could not be loaded for: '%s'.", parentMaterialFilePath.c_str());
return false;
}
- parentMaterialAsset = parentMaterialResult.GetValue();
- parentPropertyValues = parentMaterialAsset->GetPropertyValues();
+ const auto parentMaterialAssetIdResult = AssetUtils::MakeAssetId(parentMaterialFilePath, 0);
+ if (!parentMaterialAssetIdResult)
+ {
+ AZ_Error("MaterialDocument", false, "Material parent asset ID could not be created: '%s'.", parentMaterialFilePath.c_str());
+ return false;
+ }
- // track parent material asset to notify when dependencies change
- m_dependentAssetIds.insert(parentMaterialAsset->GetId());
- AZ::Data::AssetBus::MultiHandler::BusConnect(parentMaterialAsset->GetId());
+ auto parentMaterialAssetResult = parentMaterialSourceData.CreateMaterialAssetFromSourceData(
+ parentMaterialAssetIdResult.GetValue(), parentMaterialFilePath, true, true);
+ if (!parentMaterialAssetResult)
+ {
+ AZ_Error("MaterialDocument", false, "Material parent asset could not be created from source data: '%s'.", parentMaterialFilePath.c_str());
+ return false;
+ }
+
+ parentMaterialAsset = parentMaterialAssetResult.GetValue();
+ parentPropertyValues = parentMaterialAsset->GetPropertyValues();
}
// Creating a material from a material asset will fail if a texture is referenced but not loaded
@@ -913,15 +920,13 @@ namespace MaterialEditor
void MaterialDocument::Clear()
{
AZ::TickBus::Handler::BusDisconnect();
- AZ::Data::AssetBus::MultiHandler::BusDisconnect();
AzToolsFramework::AssetSystemBus::Handler::BusDisconnect();
m_materialAsset = {};
m_materialInstance = {};
m_absolutePath.clear();
m_relativePath.clear();
- m_sourceAssetId = {};
- m_dependentAssetIds.clear();
+ m_sourceDependencies.clear();
m_saveTriggeredInternally = {};
m_compilePending = {};
m_properties.clear();
diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.h b/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.h
index 03997a2a91..452111f99a 100644
--- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.h
+++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.h
@@ -29,7 +29,6 @@ namespace MaterialEditor
: public AtomToolsFramework::AtomToolsDocument
, public MaterialDocumentRequestBus::Handler
, private AZ::TickBus::Handler
- , private AZ::Data::AssetBus::MultiHandler
, private AzToolsFramework::AssetSystemBus::Handler
{
public:
@@ -105,11 +104,6 @@ namespace MaterialEditor
void SourceFileChanged(AZStd::string relativePath, AZStd::string scanFolder, AZ::Uuid sourceUUID) override;
//////////////////////////////////////////////////////////////////////////
- //////////////////////////////////////////////////////////////////////////
- // AZ::Data::AssetBus::Router overrides...
- void OnAssetReloaded(AZ::Data::Asset asset) override;
- //////////////////////////////////////////////////////////////////////////
-
bool SavePropertiesToSourceData(AZ::RPI::MaterialSourceData& sourceData, PropertyFilterFunction propertyFilter) const;
bool OpenInternal(AZStd::string_view loadPath);
@@ -137,11 +131,8 @@ namespace MaterialEditor
// Material instance being edited
AZ::Data::Instance m_materialInstance;
- // Asset used to open document
- AZ::Data::AssetId m_sourceAssetId;
-
// Set of assets that can trigger a document reload
- AZStd::unordered_set m_dependentAssetIds;
+ AZStd::unordered_set m_sourceDependencies;
// Track if document saved itself last to skip external modification notification
bool m_saveTriggeredInternally = false;
diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/EditorReflectionProbeComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/EditorReflectionProbeComponent.cpp
index ae5c930096..578c6e9300 100644
--- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/EditorReflectionProbeComponent.cpp
+++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/EditorReflectionProbeComponent.cpp
@@ -40,6 +40,7 @@ namespace AZ
->Field("bakedCubeMapQualityLevel", &EditorReflectionProbeComponent::m_bakedCubeMapQualityLevel)
->Field("bakedCubeMapRelativePath", &EditorReflectionProbeComponent::m_bakedCubeMapRelativePath)
->Field("authoredCubeMapAsset", &EditorReflectionProbeComponent::m_authoredCubeMapAsset)
+ ->Field("bakeExposure", &EditorReflectionProbeComponent::m_bakeExposure)
;
if (AZ::EditContext* editContext = serializeContext->GetEditContext())
@@ -62,6 +63,13 @@ namespace AZ
->Attribute(AZ::Edit::Attributes::ButtonText, "Bake Reflection Probe")
->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorReflectionProbeComponent::BakeReflectionProbe)
->Attribute(AZ::Edit::Attributes::Visibility, &EditorReflectionProbeComponent::GetBakedCubemapVisibilitySetting)
+ ->DataElement(AZ::Edit::UIHandlers::Slider, &EditorReflectionProbeComponent::m_bakeExposure, "Bake Exposure", "Exposure to use when baking the cubemap")
+ ->Attribute(AZ::Edit::Attributes::SoftMin, -16.0f)
+ ->Attribute(AZ::Edit::Attributes::SoftMax, 16.0f)
+ ->Attribute(AZ::Edit::Attributes::Min, -20.0f)
+ ->Attribute(AZ::Edit::Attributes::Max, 20.0f)
+ ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorReflectionProbeComponent::OnBakeExposureChanged)
+ ->Attribute(AZ::Edit::Attributes::Visibility, &EditorReflectionProbeComponent::GetBakedCubemapVisibilitySetting)
->ClassElement(AZ::Edit::ClassElements::Group, "Cubemap")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->DataElement(AZ::Edit::UIHandlers::Default, &EditorReflectionProbeComponent::m_useBakedCubemap, "Use Baked Cubemap", "Selects between a cubemap that captures the environment at location in the scene or a preauthored cubemap")
@@ -111,6 +119,11 @@ namespace AZ
->Attribute(AZ::Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly)
->DataElement(AZ::Edit::UIHandlers::CheckBox, &ReflectionProbeComponentConfig::m_showVisualization, "Show Visualization", "Show the reflection probe visualization sphere")
->Attribute(AZ::Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly)
+ ->DataElement(AZ::Edit::UIHandlers::Slider, &ReflectionProbeComponentConfig::m_renderExposure, "Exposure", "Exposure to use when rendering meshes with the cubemap")
+ ->Attribute(AZ::Edit::Attributes::SoftMin, -5.0f)
+ ->Attribute(AZ::Edit::Attributes::SoftMax, 5.0f)
+ ->Attribute(AZ::Edit::Attributes::Min, -20.0f)
+ ->Attribute(AZ::Edit::Attributes::Max, 20.0f)
;
}
}
@@ -275,6 +288,13 @@ namespace AZ
return AZ::Edit::PropertyRefreshLevels::None;
}
+ AZ::u32 EditorReflectionProbeComponent::OnBakeExposureChanged()
+ {
+ m_controller.SetBakeExposure(m_bakeExposure);
+
+ return AZ::Edit::PropertyRefreshLevels::None;
+ }
+
AZ::u32 EditorReflectionProbeComponent::GetBakedCubemapVisibilitySetting()
{
// controls specific to baked cubemaps call this to determine their visibility
diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/EditorReflectionProbeComponent.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/EditorReflectionProbeComponent.h
index 441da19e78..3cd017fd18 100644
--- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/EditorReflectionProbeComponent.h
+++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/EditorReflectionProbeComponent.h
@@ -55,6 +55,7 @@ namespace AZ
// change notifications
AZ::u32 OnUseBakedCubemapChanged();
AZ::u32 OnAuthoredCubemapChanged();
+ AZ::u32 OnBakeExposureChanged();
// retrieves visibility for baked or authored cubemap controls
AZ::u32 GetBakedCubemapVisibilitySetting();
@@ -77,6 +78,7 @@ namespace AZ
AZStd::string m_bakedCubeMapRelativePath;
Data::Asset m_bakedCubeMapAsset;
Data::Asset m_authoredCubeMapAsset;
+ float m_bakeExposure = 0.0f;
// flag indicating if a cubemap bake is currently in progress
AZStd::atomic_bool m_bakeInProgress = false;
diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/ReflectionProbeComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/ReflectionProbeComponentController.cpp
index 4022dfda9b..017f6c9cdf 100644
--- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/ReflectionProbeComponentController.cpp
+++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/ReflectionProbeComponentController.cpp
@@ -35,7 +35,7 @@ namespace AZ
if (auto* serializeContext = azrtti_cast(context))
{
serializeContext->Class()
- ->Version(0)
+ ->Version(1)
->Field("OuterHeight", &ReflectionProbeComponentConfig::m_outerHeight)
->Field("OuterLength", &ReflectionProbeComponentConfig::m_outerLength)
->Field("OuterWidth", &ReflectionProbeComponentConfig::m_outerWidth)
@@ -49,7 +49,9 @@ namespace AZ
->Field("AuthoredCubeMapAsset", &ReflectionProbeComponentConfig::m_authoredCubeMapAsset)
->Field("EntityId", &ReflectionProbeComponentConfig::m_entityId)
->Field("UseParallaxCorrection", &ReflectionProbeComponentConfig::m_useParallaxCorrection)
- ->Field("ShowVisualization", &ReflectionProbeComponentConfig::m_showVisualization);
+ ->Field("ShowVisualization", &ReflectionProbeComponentConfig::m_showVisualization)
+ ->Field("RenderExposure", &ReflectionProbeComponentConfig::m_renderExposure)
+ ->Field("BakeExposure", &ReflectionProbeComponentConfig::m_bakeExposure);
}
}
@@ -157,6 +159,9 @@ namespace AZ
cubeMapAsset.QueueLoad();
Data::AssetBus::MultiHandler::BusConnect(cubeMapAsset.GetId());
}
+
+ // set cubemap render exposure
+ m_featureProcessor->SetRenderExposure(m_handle, m_configuration.m_renderExposure);
}
void ReflectionProbeComponentController::Deactivate()
@@ -284,6 +289,16 @@ namespace AZ
m_configuration.m_innerHeight = AZStd::min(m_configuration.m_innerHeight, m_configuration.m_outerHeight);
}
+ void ReflectionProbeComponentController::SetBakeExposure(float bakeExposure)
+ {
+ if (!m_featureProcessor)
+ {
+ return;
+ }
+
+ m_featureProcessor->SetBakeExposure(m_handle, bakeExposure);
+ }
+
void ReflectionProbeComponentController::BakeReflectionProbe(BuildCubeMapCallback callback, const AZStd::string& relativePath)
{
if (!m_featureProcessor)
diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/ReflectionProbeComponentController.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/ReflectionProbeComponentController.h
index 18e13f023b..ad7d9f7f53 100644
--- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/ReflectionProbeComponentController.h
+++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/ReflectionProbeComponentController.h
@@ -68,6 +68,9 @@ namespace AZ
Data::Asset m_bakedCubeMapAsset;
Data::Asset m_authoredCubeMapAsset;
AZ::u64 m_entityId{ EntityId::InvalidEntityId };
+
+ float m_renderExposure = 0.0f;
+ float m_bakeExposure = 0.0f;
};
class ReflectionProbeComponentController final
@@ -99,6 +102,9 @@ namespace AZ
// returns the outer extent Aabb for this reflection
AZ::Aabb GetAabb() const;
+ // set the exposure to use when baking the cubemap
+ void SetBakeExposure(float bakeExposure);
+
// initiate the reflection probe bake, invokes callback when complete
void BakeReflectionProbe(BuildCubeMapCallback callback, const AZStd::string& relativePath);
diff --git a/Gems/LyShine/Code/Editor/Animation/UiAnimViewDialog.cpp b/Gems/LyShine/Code/Editor/Animation/UiAnimViewDialog.cpp
index e6c2dda74c..9ad9a50aa5 100644
--- a/Gems/LyShine/Code/Editor/Animation/UiAnimViewDialog.cpp
+++ b/Gems/LyShine/Code/Editor/Animation/UiAnimViewDialog.cpp
@@ -257,6 +257,7 @@ BOOL CUiAnimViewDialog::OnInitDialog()
m_wndSplitter->addWidget(m_wndDopeSheet);
m_wndSplitter->setStretchFactor(0, 1);
m_wndSplitter->setStretchFactor(1, 10);
+ m_wndSplitter->setChildrenCollapsible(false);
l->addWidget(m_wndSplitter);
w->setLayout(l);
setCentralWidget(w);
@@ -283,6 +284,11 @@ BOOL CUiAnimViewDialog::OnInitDialog()
m_wndCurveEditorDock->setVisible(false);
m_wndCurveEditorDock->setEnabled(false);
+ // In order to prevent the track editor view from collapsing and becoming invisible, we use the
+ // minimum size of the curve editor for the track editor as well. Since both editors use the same
+ // view widget in the UI animation editor when not in 'Both' mode, the sizes can be identical.
+ m_wndDopeSheet->setMinimumSize(m_wndCurveEditor->minimumSizeHint());
+
InitSequences();
m_lazyInitDone = false;
diff --git a/Gems/Terrain/Assets/Shaders/Terrain/TerrainCommon.azsli b/Gems/Terrain/Assets/Shaders/Terrain/TerrainCommon.azsli
index cd680f721a..52d0e0a6cc 100644
--- a/Gems/Terrain/Assets/Shaders/Terrain/TerrainCommon.azsli
+++ b/Gems/Terrain/Assets/Shaders/Terrain/TerrainCommon.azsli
@@ -56,6 +56,7 @@ ShaderResourceGroup ObjectSrg : SRG_PerObject
float m_padding;
bool m_useReflectionProbe;
bool m_useParallaxCorrection;
+ float m_exposure;
};
ReflectionProbeData m_reflectionProbeData;
diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/Components/TerrainSurfaceMaterialsListComponent.cpp b/Gems/Terrain/Code/Source/TerrainRenderer/Components/TerrainSurfaceMaterialsListComponent.cpp
index 271919070c..1dbcbd1120 100644
--- a/Gems/Terrain/Code/Source/TerrainRenderer/Components/TerrainSurfaceMaterialsListComponent.cpp
+++ b/Gems/Terrain/Code/Source/TerrainRenderer/Components/TerrainSurfaceMaterialsListComponent.cpp
@@ -74,7 +74,7 @@ namespace Terrain
->DataElement(
AZ::Edit::UIHandlers::Default, &TerrainSurfaceMaterialsListConfig::m_surfaceMaterials,
- "Gradient to Material Mappings", "Maps surfaces to materials.");
+ "Material Mappings", "Maps surfaces to materials.");
}
}
}
@@ -123,7 +123,7 @@ namespace Terrain
{
surfaceMaterialMapping.m_active = false;
surfaceMaterialMapping.m_materialAsset.QueueLoad();
- AZ::Data::AssetBus::Handler::BusConnect(surfaceMaterialMapping.m_materialAsset.GetId());
+ AZ::Data::AssetBus::MultiHandler::BusConnect(surfaceMaterialMapping.m_materialAsset.GetId());
}
}
}
@@ -136,7 +136,7 @@ namespace Terrain
{
if (surfaceMaterialMapping.m_materialAsset.GetId().IsValid())
{
- AZ::Data::AssetBus::Handler::BusDisconnect(surfaceMaterialMapping.m_materialAsset.GetId());
+ AZ::Data::AssetBus::MultiHandler::BusDisconnect(surfaceMaterialMapping.m_materialAsset.GetId());
surfaceMaterialMapping.m_materialAsset.Release();
surfaceMaterialMapping.m_materialInstance.reset();
surfaceMaterialMapping.m_activeMaterialAssetId = AZ::Data::AssetId();
@@ -202,7 +202,7 @@ namespace Terrain
// Don't disconnect from the AssetBus if this material is mapped more than once.
if (CountMaterialIDInstances(surfaceMaterialMapping.m_activeMaterialAssetId) == 1)
{
- AZ::Data::AssetBus::Handler::BusDisconnect(surfaceMaterialMapping.m_activeMaterialAssetId);
+ AZ::Data::AssetBus::MultiHandler::BusDisconnect(surfaceMaterialMapping.m_activeMaterialAssetId);
}
surfaceMaterialMapping.m_activeMaterialAssetId = AZ::Data::AssetId();
@@ -238,7 +238,7 @@ namespace Terrain
// All materials have been deactivated, stop listening for requests and notifications.
m_cachedAabb = AZ::Aabb::CreateNull();
LmbrCentral::ShapeComponentNotificationsBus::Handler::BusDisconnect();
- TerrainAreaMaterialRequestBus::Handler::BusConnect(GetEntityId());
+ TerrainAreaMaterialRequestBus::Handler::BusDisconnect();
}
}
diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/Components/TerrainSurfaceMaterialsListComponent.h b/Gems/Terrain/Code/Source/TerrainRenderer/Components/TerrainSurfaceMaterialsListComponent.h
index 7c36033c41..72eee5f68e 100644
--- a/Gems/Terrain/Code/Source/TerrainRenderer/Components/TerrainSurfaceMaterialsListComponent.h
+++ b/Gems/Terrain/Code/Source/TerrainRenderer/Components/TerrainSurfaceMaterialsListComponent.h
@@ -53,7 +53,7 @@ namespace Terrain
class TerrainSurfaceMaterialsListComponent
: public AZ::Component
, private TerrainAreaMaterialRequestBus::Handler
- , private AZ::Data::AssetBus::Handler
+ , private AZ::Data::AssetBus::MultiHandler
, private LmbrCentral::ShapeComponentNotificationsBus::Handler
{
public:
diff --git a/Gems/Terrain/Code/Tests/LayerSpawnerTests.cpp b/Gems/Terrain/Code/Tests/LayerSpawnerTests.cpp
index 3778ba860f..9de441eecf 100644
--- a/Gems/Terrain/Code/Tests/LayerSpawnerTests.cpp
+++ b/Gems/Terrain/Code/Tests/LayerSpawnerTests.cpp
@@ -6,15 +6,13 @@
*
*/
+#include
+
#include
#include
#include
-#include
-
#include
-#include
-#include
#include
#include
@@ -23,21 +21,12 @@ using ::testing::NiceMock;
using ::testing::AtLeast;
using ::testing::_;
-using ::testing::NiceMock;
-using ::testing::AtLeast;
-using ::testing::_;
-
class LayerSpawnerComponentTest
: public ::testing::Test
{
protected:
AZ::ComponentApplication m_app;
- AZStd::unique_ptr m_entity;
- Terrain::TerrainLayerSpawnerComponent* m_layerSpawnerComponent;
- UnitTest::MockAxisAlignedBoxShapeComponent* m_shapeComponent;
- AZStd::unique_ptr> m_terrainSystem;
-
void SetUp() override
{
AZ::ComponentApplication::Descriptor appDesc;
@@ -50,78 +39,86 @@ protected:
void TearDown() override
{
- m_entity.reset();
- m_terrainSystem.reset();
m_app.Destroy();
}
- void CreateEntity()
+ AZStd::unique_ptr CreateEntity()
{
- m_entity = AZStd::make_unique();
- m_entity->Init();
+ auto entity = AZStd::make_unique();
+ entity->Init();
- ASSERT_TRUE(m_entity);
- }
-
- void AddLayerSpawnerAndShapeComponentToEntity()
- {
- AddLayerSpawnerAndShapeComponentToEntity(Terrain::TerrainLayerSpawnerConfig());
+ return entity;
}
- void AddLayerSpawnerAndShapeComponentToEntity(const Terrain::TerrainLayerSpawnerConfig& config)
+ Terrain::TerrainLayerSpawnerComponent* AddLayerSpawnerToEntity(AZ::Entity* entity, const Terrain::TerrainLayerSpawnerConfig& config)
{
- m_layerSpawnerComponent = m_entity->CreateComponent(config);
- m_app.RegisterComponentDescriptor(m_layerSpawnerComponent->CreateDescriptor());
+ auto layerSpawnerComponent = entity->CreateComponent(config);
+ m_app.RegisterComponentDescriptor(layerSpawnerComponent->CreateDescriptor());
- m_shapeComponent = m_entity->CreateComponent();
- m_app.RegisterComponentDescriptor(m_shapeComponent->CreateDescriptor());
-
- ASSERT_TRUE(m_layerSpawnerComponent);
- ASSERT_TRUE(m_shapeComponent);
+ return layerSpawnerComponent;
}
- void CreateMockTerrainSystem()
+ UnitTest::MockAxisAlignedBoxShapeComponent* AddShapeComponentToEntity(AZ::Entity* entity)
{
- m_terrainSystem = AZStd::make_unique>();
+ UnitTest::MockAxisAlignedBoxShapeComponent* shapeComponent = entity->CreateComponent();
+ m_app.RegisterComponentDescriptor(shapeComponent->CreateDescriptor());
+
+ return shapeComponent;
}
};
-TEST_F(LayerSpawnerComponentTest, ActivatEntityActivateSuccess)
+TEST_F(LayerSpawnerComponentTest, ActivateEntityWithoutShapeFails)
+{
+ auto entity = CreateEntity();
+
+ AddLayerSpawnerToEntity(entity.get(), Terrain::TerrainLayerSpawnerConfig());
+
+ const AZ::Entity::DependencySortOutcome sortOutcome = entity->EvaluateDependenciesGetDetails();
+ EXPECT_FALSE(sortOutcome.IsSuccess());
+
+ entity.reset();
+}
+
+TEST_F(LayerSpawnerComponentTest, ActivateEntityActivateSuccess)
{
- CreateEntity();
- AddLayerSpawnerAndShapeComponentToEntity();
+ auto entity = CreateEntity();
+
+ AddLayerSpawnerToEntity(entity.get(), Terrain::TerrainLayerSpawnerConfig());
+ AddShapeComponentToEntity(entity.get());
- m_entity->Activate();
- EXPECT_EQ(m_entity->GetState(), AZ::Entity::State::Active);
-
- m_entity->Deactivate();
+ entity->Activate();
+ EXPECT_EQ(entity->GetState(), AZ::Entity::State::Active);
+
+ entity.reset();
}
TEST_F(LayerSpawnerComponentTest, LayerSpawnerDefaultValuesCorrect)
{
- CreateEntity();
- AddLayerSpawnerAndShapeComponentToEntity();
+ auto entity = CreateEntity();
+ AddLayerSpawnerToEntity(entity.get(), Terrain::TerrainLayerSpawnerConfig());
+ AddShapeComponentToEntity(entity.get());
- m_entity->Activate();
+ entity->Activate();
AZ::u32 priority = 999, layer = 999;
- Terrain::TerrainSpawnerRequestBus::Event(m_entity->GetId(), &Terrain::TerrainSpawnerRequestBus::Events::GetPriority, layer, priority);
+ Terrain::TerrainSpawnerRequestBus::Event(entity->GetId(), &Terrain::TerrainSpawnerRequestBus::Events::GetPriority, layer, priority);
EXPECT_EQ(0, priority);
EXPECT_EQ(1, layer);
bool useGroundPlane = false;
- Terrain::TerrainSpawnerRequestBus::EventResult(useGroundPlane, m_entity->GetId(), &Terrain::TerrainSpawnerRequestBus::Events::GetUseGroundPlane);
+ Terrain::TerrainSpawnerRequestBus::EventResult(
+ useGroundPlane, entity->GetId(), &Terrain::TerrainSpawnerRequestBus::Events::GetUseGroundPlane);
EXPECT_TRUE(useGroundPlane);
- m_entity->Deactivate();
+ entity.reset();
}
TEST_F(LayerSpawnerComponentTest, LayerSpawnerConfigValuesCorrect)
{
- CreateEntity();
+ auto entity = CreateEntity();
constexpr static AZ::u32 testPriority = 15;
constexpr static AZ::u32 testLayer = 0;
@@ -131,12 +128,13 @@ TEST_F(LayerSpawnerComponentTest, LayerSpawnerConfigValuesCorrect)
config.m_priority = testPriority;
config.m_useGroundPlane = false;
- AddLayerSpawnerAndShapeComponentToEntity(config);
+ AddLayerSpawnerToEntity(entity.get(), config);
+ AddShapeComponentToEntity(entity.get());
- m_entity->Activate();
+ entity->Activate();
AZ::u32 priority = 999, layer = 999;
- Terrain::TerrainSpawnerRequestBus::Event(m_entity->GetId(), &Terrain::TerrainSpawnerRequestBus::Events::GetPriority, layer, priority);
+ Terrain::TerrainSpawnerRequestBus::Event(entity->GetId(), &Terrain::TerrainSpawnerRequestBus::Events::GetPriority, layer, priority);
EXPECT_EQ(testPriority, priority);
EXPECT_EQ(testLayer, layer);
@@ -144,82 +142,86 @@ TEST_F(LayerSpawnerComponentTest, LayerSpawnerConfigValuesCorrect)
bool useGroundPlane = true;
Terrain::TerrainSpawnerRequestBus::EventResult(
- useGroundPlane, m_entity->GetId(), &Terrain::TerrainSpawnerRequestBus::Events::GetUseGroundPlane);
+ useGroundPlane, entity->GetId(), &Terrain::TerrainSpawnerRequestBus::Events::GetUseGroundPlane);
EXPECT_FALSE(useGroundPlane);
- m_entity->Deactivate();
+ entity.reset();
}
TEST_F(LayerSpawnerComponentTest, LayerSpawnerRegisterAreaUpdatesTerrainSystem)
{
- CreateEntity();
+ auto entity = CreateEntity();
- CreateMockTerrainSystem();
+ NiceMock terrainSystem;
// The Activate call should register the area.
- EXPECT_CALL(*m_terrainSystem, RegisterArea(_)).Times(1);
+ EXPECT_CALL(terrainSystem, RegisterArea(_)).Times(1);
- AddLayerSpawnerAndShapeComponentToEntity();
+ AddLayerSpawnerToEntity(entity.get(), Terrain::TerrainLayerSpawnerConfig());
+ AddShapeComponentToEntity(entity.get());
- m_entity->Activate();
+ entity->Activate();
- m_entity->Deactivate();
+ entity.reset();
}
TEST_F(LayerSpawnerComponentTest, LayerSpawnerUnregisterAreaUpdatesTerrainSystem)
{
- CreateEntity();
+ auto entity = CreateEntity();
- CreateMockTerrainSystem();
+ NiceMock terrainSystem;
// The Deactivate call should unregister the area.
- EXPECT_CALL(*m_terrainSystem, UnregisterArea(_)).Times(1);
+ EXPECT_CALL(terrainSystem, UnregisterArea(_)).Times(1);
- AddLayerSpawnerAndShapeComponentToEntity();
+ AddLayerSpawnerToEntity(entity.get(), Terrain::TerrainLayerSpawnerConfig());
+ AddShapeComponentToEntity(entity.get());
- m_entity->Activate();
+ entity->Activate();
- m_entity->Deactivate();
+ entity.reset();
}
TEST_F(LayerSpawnerComponentTest, LayerSpawnerTransformChangedUpdatesTerrainSystem)
{
- CreateEntity();
+ auto entity = CreateEntity();
- CreateMockTerrainSystem();
+ NiceMock terrainSystem;
// The TransformChanged call should refresh the area.
- EXPECT_CALL(*m_terrainSystem, RefreshArea(_, _)).Times(1);
+ EXPECT_CALL(terrainSystem, RefreshArea(_, _)).Times(1);
- AddLayerSpawnerAndShapeComponentToEntity();
+ AddLayerSpawnerToEntity(entity.get(), Terrain::TerrainLayerSpawnerConfig());
+ AddShapeComponentToEntity(entity.get());
- m_entity->Activate();
+ entity->Activate();
// The component gets transform change notifications via the shape bus.
LmbrCentral::ShapeComponentNotificationsBus::Event(
- m_entity->GetId(), &LmbrCentral::ShapeComponentNotificationsBus::Events::OnShapeChanged,
+ entity->GetId(), &LmbrCentral::ShapeComponentNotificationsBus::Events::OnShapeChanged,
LmbrCentral::ShapeComponentNotifications::ShapeChangeReasons::TransformChanged);
- m_entity->Deactivate();
+ entity.reset();
}
TEST_F(LayerSpawnerComponentTest, LayerSpawnerShapeChangedUpdatesTerrainSystem)
{
- CreateEntity();
+ auto entity = CreateEntity();
- CreateMockTerrainSystem();
+ NiceMock terrainSystem;
// The ShapeChanged call should refresh the area.
- EXPECT_CALL(*m_terrainSystem, RefreshArea(_, _)).Times(1);
+ EXPECT_CALL(terrainSystem, RefreshArea(_, _)).Times(1);
- AddLayerSpawnerAndShapeComponentToEntity();
+ AddLayerSpawnerToEntity(entity.get(), Terrain::TerrainLayerSpawnerConfig());
+ AddShapeComponentToEntity(entity.get());
- m_entity->Activate();
+ entity->Activate();
- LmbrCentral::ShapeComponentNotificationsBus::Event(
- m_entity->GetId(), &LmbrCentral::ShapeComponentNotificationsBus::Events::OnShapeChanged,
+ LmbrCentral::ShapeComponentNotificationsBus::Event(
+ entity->GetId(), &LmbrCentral::ShapeComponentNotificationsBus::Events::OnShapeChanged,
LmbrCentral::ShapeComponentNotifications::ShapeChangeReasons::ShapeChanged);
- m_entity->Deactivate();
+ entity.reset();
}
diff --git a/Registry/Platform/Mac/bootstrap_overrides.setreg b/Registry/Platform/Mac/bootstrap_overrides.setreg
new file mode 100644
index 0000000000..4e1ca76724
--- /dev/null
+++ b/Registry/Platform/Mac/bootstrap_overrides.setreg
@@ -0,0 +1,12 @@
+{
+ "Amazon": {
+ "AzCore": {
+ "Bootstrap": {
+ // The first time an application is launched on MacOS, each
+ // dynamic library is inspected by the OS before being loaded.
+ // This can take a while on some Macs.
+ "launch_ap_timeout": 300
+ }
+ }
+ }
+}
diff --git a/Templates/PythonToolGem/Template/Code/CMakeLists.txt b/Templates/PythonToolGem/Template/Code/CMakeLists.txt
index a6044e717b..b90f2d7703 100644
--- a/Templates/PythonToolGem/Template/Code/CMakeLists.txt
+++ b/Templates/PythonToolGem/Template/Code/CMakeLists.txt
@@ -53,6 +53,8 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS)
BUILD_DEPENDENCIES
PUBLIC
Gem::${Name}.Editor.Static
+ RUNTIME_DEPENDENCIES
+ Gem::QtForPython.Editor
)
# By default, we will specify that the above target ${Name} would be used by
diff --git a/Templates/PythonToolGem/Template/gem.json b/Templates/PythonToolGem/Template/gem.json
index d4ff637bee..84f5b65a3e 100644
--- a/Templates/PythonToolGem/Template/gem.json
+++ b/Templates/PythonToolGem/Template/gem.json
@@ -13,5 +13,8 @@
"${Name}"
],
"icon_path": "preview.png",
- "requirements": ""
+ "requirements": "",
+ "dependencies": [
+ "QtForPython"
+ ]
}
diff --git a/scripts/build/Jenkins/Jenkinsfile b/scripts/build/Jenkins/Jenkinsfile
index 1388366661..beb4a21620 100644
--- a/scripts/build/Jenkins/Jenkinsfile
+++ b/scripts/build/Jenkins/Jenkinsfile
@@ -16,7 +16,7 @@ EMPTY_JSON = readJSON text: '{}'
ENGINE_REPOSITORY_NAME = 'o3de'
// Branches with build snapshots
-BUILD_SNAPSHOTS = ['development', 'stabilization/2106']
+BUILD_SNAPSHOTS = ['development', 'stabilization/2110']
// Build snapshots with empty snapshot (for use with 'SNAPSHOT' pipeline paramater)
BUILD_SNAPSHOTS_WITH_EMPTY = BUILD_SNAPSHOTS + ''
@@ -102,6 +102,10 @@ def IsJobEnabled(branchName, buildTypeMap, pipelineName, platformName) {
}
}
+def IsAPLogUpload(branchName, jobName) {
+ return !IsPullRequest(branchName) && jobName.toLowerCase().contains('asset') && env.AP_LOGS_S3_BUCKET
+}
+
def GetRunningPipelineName(JENKINS_JOB_NAME) {
// If the job name has an underscore
def job_parts = JENKINS_JOB_NAME.tokenize('/')[0].tokenize('_')
@@ -267,7 +271,7 @@ def CheckoutRepo(boolean disableSubmodules = false) {
palRm('commitdate')
}
-def HandleDriveMount(String snapshot, String repositoryName, String projectName, String pipeline, String branchName, String platform, String buildType, String workspace, boolean recreateVolume = false) {
+def HandleDriveMount(String snapshot, String repositoryName, String projectName, String pipeline, String branchName, String platform, String buildType, String workspace, boolean recreateVolume = false) {
unstash name: 'incremental_build_script'
def pythonCmd = ''
@@ -277,9 +281,7 @@ def HandleDriveMount(String snapshot, String repositoryName, String projectName,
if(recreateVolume) {
palSh("${pythonCmd} ${INCREMENTAL_BUILD_SCRIPT_PATH} --action delete --repository_name ${repositoryName} --project ${projectName} --pipeline ${pipeline} --branch ${branchName} --platform ${platform} --build_type ${buildType}", 'Deleting volume', winSlashReplacement=false)
}
- timeout(5) {
- palSh("${pythonCmd} ${INCREMENTAL_BUILD_SCRIPT_PATH} --action mount --snapshot ${snapshot} --repository_name ${repositoryName} --project ${projectName} --pipeline ${pipeline} --branch ${branchName} --platform ${platform} --build_type ${buildType}", 'Mounting volume', winSlashReplacement=false)
- }
+ palSh("${pythonCmd} ${INCREMENTAL_BUILD_SCRIPT_PATH} --action mount --snapshot ${snapshot} --repository_name ${repositoryName} --project ${projectName} --pipeline ${pipeline} --branch ${branchName} --platform ${platform} --build_type ${buildType}", 'Mounting volume', winSlashReplacement=false)
if(env.IS_UNIX) {
sh label: 'Setting volume\'s ownership',
@@ -431,6 +433,27 @@ def ExportTestScreenshots(Map options, String branchName, String platformName, S
}
}
+def UploadAPLogs(Map options, String branchName, String platformName, String jobName, String workspace, Map params) {
+ dir("${workspace}/${ENGINE_REPOSITORY_NAME}") {
+ projects = params.CMAKE_LY_PROJECTS.split(",")
+ projects.each{ project ->
+ def apLogsPath = "${project}/user/log"
+ def s3UploadScriptPath = "scripts/build/tools/upload_to_s3.py"
+ if(env.IS_UNIX) {
+ pythonPath = "${options.PYTHON_DIR}/python.sh"
+ }
+ else {
+ pythonPath = "${options.PYTHON_DIR}/python.cmd"
+ }
+ def command = "${pythonPath} -u ${s3UploadScriptPath} --base_dir ${apLogsPath} " +
+ "--file_regex \".*\" --bucket ${env.AP_LOGS_S3_BUCKET} " +
+ "--search_subdirectories True --key_prefix ${env.JENKINS_JOB_NAME}/${branchName}/${env.BUILD_NUMBER}/${platformName}/${jobName} " +
+ '--extra_args {\\"ACL\\":\\"bucket-owner-full-control\\"}'
+ palSh(command, "Uploading AP logs for job ${jobName} for branch ${branchName}", false)
+ }
+ }
+ }
+
def PostBuildCommonSteps(String workspace, boolean mount = true) {
echo 'Starting post-build common steps...'
@@ -494,6 +517,14 @@ def CreateExportTestScreenshotsStage(Map pipelineConfig, String branchName, Stri
}
}
+def CreateUploadAPLogsStage(Map pipelineConfig, String branchName, String platformName, String jobName, String workspace, Map params) {
+ return {
+ stage("${jobName}_upload_ap_logs") {
+ UploadAPLogs(pipelineConfig, branchName, platformName, jobName, workspace, params)
+ }
+ }
+}
+
def CreateTeardownStage(Map environmentVars) {
return {
stage('Teardown') {
@@ -518,9 +549,11 @@ def CreateSingleNode(Map pipelineConfig, def platform, def build_job, Map envVar
CreateSetupStage(pipelineConfig, snapshot, repositoryName, projectName, pipelineName, branchName, platform.key, build_job.key, envVars, onlyMountEBSVolume).call()
if(build_job.value.steps) { //this is a pipe with many steps so create all the build stages
+ pipelineEnvVars = GetBuildEnvVars(platform.value.PIPELINE_ENV ?: EMPTY_JSON, build_job.value.PIPELINE_ENV ?: EMPTY_JSON, pipelineName)
build_job.value.steps.each { build_step ->
build_job_name = build_step
- envVars = GetBuildEnvVars(platform.value.PIPELINE_ENV ?: EMPTY_JSON, platform.value.build_types[build_step].PIPELINE_ENV ?: EMPTY_JSON, pipelineName)
+ // This addition of maps makes it that the right operand will override entries if they overlap with the left operand
+ envVars = pipelineEnvVars + GetBuildEnvVars(platform.value.PIPELINE_ENV ?: EMPTY_JSON, platform.value.build_types[build_step].PIPELINE_ENV ?: EMPTY_JSON, pipelineName)
try {
CreateBuildStage(pipelineConfig, platform.key, build_step, envVars).call()
}
@@ -543,6 +576,9 @@ def CreateSingleNode(Map pipelineConfig, def platform, def build_job, Map envVar
error "Node disconnected during build: ${e}" // Error raised to retry stage on a new node
}
}
+ if (IsAPLogUpload(branchName, build_job_name)) {
+ CreateUploadAPLogsStage(pipelineConfig, branchName, platform.key, build_job_name, envVars['WORKSPACE'], platform.value.build_types[build_job_name].PARAMETERS).call()
+ }
// All other errors will be raised outside the retry block
currentResult = envVars['ON_FAILURE_MARK'] ?: 'FAILURE'
currentException = e.toString()
@@ -770,6 +806,7 @@ try {
platform.value.build_types.each { build_job ->
if (IsJobEnabled(branchName, build_job, pipelineName, platform.key)) { // User can filter jobs, jobs are tagged by pipeline
def envVars = GetBuildEnvVars(platform.value.PIPELINE_ENV ?: EMPTY_JSON, build_job.value.PIPELINE_ENV ?: EMPTY_JSON, pipelineName)
+ envVars['JENKINS_JOB_NAME'] = env.JOB_NAME // Save original Jenkins job name to JENKINS_JOB_NAME
envVars['JOB_NAME'] = "${branchName}_${platform.key}_${build_job.key}" // backwards compatibility, some scripts rely on this
someBuildHappened = true