Merge branch 'stabilization/2110' into Prism/DeleteUpdateGemsUI

monroegm-disable-blank-issue-2
nggieber 4 years ago
commit 2913d72d17

@ -62164,7 +62164,7 @@ An Entity can be selected by using the pick button, or by dragging an Entity fro
<message id="HANDLER_TAGGLOBALNOTIFICATIONBUS_ONENTITYTAGADDED_OUTPUT0_NAME"> <message id="HANDLER_TAGGLOBALNOTIFICATIONBUS_ONENTITYTAGADDED_OUTPUT0_NAME">
<source>HANDLER_TAGGLOBALNOTIFICATIONBUS_ONENTITYTAGADDED_OUTPUT0_NAME</source> <source>HANDLER_TAGGLOBALNOTIFICATIONBUS_ONENTITYTAGADDED_OUTPUT0_NAME</source>
<comment>Simple Type: EntityID C++ Type: const EntityId&amp;</comment> <comment>Simple Type: EntityID C++ Type: const EntityId&amp;</comment>
<translation type="unfinished">Entity</translation> <translation type="unfinished">EntityID</translation>
</message> </message>
<message id="HANDLER_TAGGLOBALNOTIFICATIONBUS_ONENTITYTAGADDED_OUTPUT0_TOOLTIP"> <message id="HANDLER_TAGGLOBALNOTIFICATIONBUS_ONENTITYTAGADDED_OUTPUT0_TOOLTIP">
<source>HANDLER_TAGGLOBALNOTIFICATIONBUS_ONENTITYTAGADDED_OUTPUT0_TOOLTIP</source> <source>HANDLER_TAGGLOBALNOTIFICATIONBUS_ONENTITYTAGADDED_OUTPUT0_TOOLTIP</source>
@ -62202,7 +62202,7 @@ An Entity can be selected by using the pick button, or by dragging an Entity fro
<message id="HANDLER_TAGGLOBALNOTIFICATIONBUS_ONENTITYTAGREMOVED_OUTPUT0_NAME"> <message id="HANDLER_TAGGLOBALNOTIFICATIONBUS_ONENTITYTAGREMOVED_OUTPUT0_NAME">
<source>HANDLER_TAGGLOBALNOTIFICATIONBUS_ONENTITYTAGREMOVED_OUTPUT0_NAME</source> <source>HANDLER_TAGGLOBALNOTIFICATIONBUS_ONENTITYTAGREMOVED_OUTPUT0_NAME</source>
<comment>Simple Type: EntityID C++ Type: const EntityId&amp;</comment> <comment>Simple Type: EntityID C++ Type: const EntityId&amp;</comment>
<translation type="unfinished">Entity</translation> <translation type="unfinished">EntityId</translation>
</message> </message>
<message id="HANDLER_TAGGLOBALNOTIFICATIONBUS_ONENTITYTAGREMOVED_OUTPUT0_TOOLTIP"> <message id="HANDLER_TAGGLOBALNOTIFICATIONBUS_ONENTITYTAGREMOVED_OUTPUT0_TOOLTIP">
<source>HANDLER_TAGGLOBALNOTIFICATIONBUS_ONENTITYTAGREMOVED_OUTPUT0_TOOLTIP</source> <source>HANDLER_TAGGLOBALNOTIFICATIONBUS_ONENTITYTAGREMOVED_OUTPUT0_TOOLTIP</source>
@ -81852,7 +81852,7 @@ The element is removed from its current parent and added as a child of the new p
<message id="HANDLER_SPAWNERCOMPONENTNOTIFICATIONBUS_ONENTITYSPAWNED_OUTPUT1_NAME"> <message id="HANDLER_SPAWNERCOMPONENTNOTIFICATIONBUS_ONENTITYSPAWNED_OUTPUT1_NAME">
<source>HANDLER_SPAWNERCOMPONENTNOTIFICATIONBUS_ONENTITYSPAWNED_OUTPUT1_NAME</source> <source>HANDLER_SPAWNERCOMPONENTNOTIFICATIONBUS_ONENTITYSPAWNED_OUTPUT1_NAME</source>
<comment>Simple Type: EntityID C++ Type: const EntityId&amp;</comment> <comment>Simple Type: EntityID C++ Type: const EntityId&amp;</comment>
<translation type="unfinished">Entity</translation> <translation type="unfinished">EntityID</translation>
</message> </message>
<message id="HANDLER_SPAWNERCOMPONENTNOTIFICATIONBUS_ONENTITYSPAWNED_OUTPUT1_TOOLTIP"> <message id="HANDLER_SPAWNERCOMPONENTNOTIFICATIONBUS_ONENTITYSPAWNED_OUTPUT1_TOOLTIP">
<source>HANDLER_SPAWNERCOMPONENTNOTIFICATIONBUS_ONENTITYSPAWNED_OUTPUT1_TOOLTIP</source> <source>HANDLER_SPAWNERCOMPONENTNOTIFICATIONBUS_ONENTITYSPAWNED_OUTPUT1_TOOLTIP</source>
@ -89198,7 +89198,7 @@ The element is removed from its current parent and added as a child of the new p
<message id="HANDLER_ENTITYBUS_ONENTITYACTIVATED_OUTPUT0_NAME"> <message id="HANDLER_ENTITYBUS_ONENTITYACTIVATED_OUTPUT0_NAME">
<source>HANDLER_ENTITYBUS_ONENTITYACTIVATED_OUTPUT0_NAME</source> <source>HANDLER_ENTITYBUS_ONENTITYACTIVATED_OUTPUT0_NAME</source>
<comment>Simple Type: EntityID C++ Type: const EntityId&amp;</comment> <comment>Simple Type: EntityID C++ Type: const EntityId&amp;</comment>
<translation type="unfinished">Entity</translation> <translation type="unfinished">EntityID</translation>
</message> </message>
<message id="HANDLER_ENTITYBUS_ONENTITYACTIVATED_OUTPUT0_TOOLTIP"> <message id="HANDLER_ENTITYBUS_ONENTITYACTIVATED_OUTPUT0_TOOLTIP">
<source>HANDLER_ENTITYBUS_ONENTITYACTIVATED_OUTPUT0_TOOLTIP</source> <source>HANDLER_ENTITYBUS_ONENTITYACTIVATED_OUTPUT0_TOOLTIP</source>
@ -89236,7 +89236,7 @@ The element is removed from its current parent and added as a child of the new p
<message id="HANDLER_ENTITYBUS_ONENTITYDEACTIVATED_OUTPUT0_NAME"> <message id="HANDLER_ENTITYBUS_ONENTITYDEACTIVATED_OUTPUT0_NAME">
<source>HANDLER_ENTITYBUS_ONENTITYDEACTIVATED_OUTPUT0_NAME</source> <source>HANDLER_ENTITYBUS_ONENTITYDEACTIVATED_OUTPUT0_NAME</source>
<comment>Simple Type: EntityID C++ Type: const EntityId&amp;</comment> <comment>Simple Type: EntityID C++ Type: const EntityId&amp;</comment>
<translation type="unfinished">Entity</translation> <translation type="unfinished">EntityID</translation>
</message> </message>
<message id="HANDLER_ENTITYBUS_ONENTITYDEACTIVATED_OUTPUT0_TOOLTIP"> <message id="HANDLER_ENTITYBUS_ONENTITYDEACTIVATED_OUTPUT0_TOOLTIP">
<source>HANDLER_ENTITYBUS_ONENTITYDEACTIVATED_OUTPUT0_TOOLTIP</source> <source>HANDLER_ENTITYBUS_ONENTITYDEACTIVATED_OUTPUT0_TOOLTIP</source>

@ -6,6 +6,8 @@
* *
*/ */
#pragma once
#include <AzFramework/Input/Devices/Mouse/InputDeviceMouse.h> #include <AzFramework/Input/Devices/Mouse/InputDeviceMouse.h>
#include <AzFramework/XcbConnectionManager.h> #include <AzFramework/XcbConnectionManager.h>
#include <AzFramework/XcbEventHandler.h> #include <AzFramework/XcbEventHandler.h>

@ -20,7 +20,7 @@ AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option")
AZ_POP_DISABLE_WARNING AZ_POP_DISABLE_WARNING
AZ_CVAR( 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."); "Use the new AssetBrowser TableView for searching assets.");
namespace AzToolsFramework namespace AzToolsFramework
{ {

@ -606,6 +606,7 @@ namespace AzToolsFramework
AzToolsFramework::ComponentModeFramework::EditorComponentModeNotificationBus::Handler::BusConnect( AzToolsFramework::ComponentModeFramework::EditorComponentModeNotificationBus::Handler::BusConnect(
AzToolsFramework::GetEntityContextId()); AzToolsFramework::GetEntityContextId());
ViewportEditorModeNotificationsBus::Handler::BusConnect(GetEntityContextId());
} }
EntityPropertyEditor::~EntityPropertyEditor() EntityPropertyEditor::~EntityPropertyEditor()
@ -618,6 +619,7 @@ namespace AzToolsFramework
AZ::EntitySystemBus::Handler::BusDisconnect(); AZ::EntitySystemBus::Handler::BusDisconnect();
EditorEntityContextNotificationBus::Handler::BusDisconnect(); EditorEntityContextNotificationBus::Handler::BusDisconnect();
AzToolsFramework::ComponentModeFramework::EditorComponentModeNotificationBus::Handler::BusDisconnect(); AzToolsFramework::ComponentModeFramework::EditorComponentModeNotificationBus::Handler::BusDisconnect();
ViewportEditorModeNotificationsBus::Handler::BusDisconnect();
for (auto& entityId : m_overrideSelectedEntityIds) for (auto& entityId : m_overrideSelectedEntityIds)
{ {

@ -243,7 +243,7 @@ void AssetProcessorManagerTest::SetUp()
m_mockApplicationManager->BusConnect(); m_mockApplicationManager->BusConnect();
m_assetProcessorManager.reset(new AssetProcessorManager_Test(m_config.get())); m_assetProcessorManager.reset(new AssetProcessorManager_Test(m_config.get()));
m_assertAbsorber.Clear(); m_errorAbsorber->Clear();
m_isIdling = false; 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(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()); 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_errorAbsorber->m_numWarningsAbsorbed, 0);
ASSERT_EQ(m_assertAbsorber.m_numErrorsAbsorbed, 0); ASSERT_EQ(m_errorAbsorber->m_numErrorsAbsorbed, 0);
ASSERT_EQ(m_assertAbsorber.m_numAssertsAbsorbed, 0); ASSERT_EQ(m_errorAbsorber->m_numAssertsAbsorbed, 0);
} }
TEST_F(AssetProcessorManagerTest, WarningsAndErrorsReported_SuccessfullySavedToDatabase) 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_warningCount, 11);
ASSERT_EQ(response.m_jobList[0].m_errorCount, 22); ASSERT_EQ(response.m_jobList[0].m_errorCount, 22);
ASSERT_EQ(m_assertAbsorber.m_numWarningsAbsorbed, 0); ASSERT_EQ(m_errorAbsorber->m_numWarningsAbsorbed, 0);
ASSERT_EQ(m_assertAbsorber.m_numErrorsAbsorbed, 0); ASSERT_EQ(m_errorAbsorber->m_numErrorsAbsorbed, 0);
ASSERT_EQ(m_assertAbsorber.m_numAssertsAbsorbed, 0); ASSERT_EQ(m_errorAbsorber->m_numAssertsAbsorbed, 0);
} }
@ -1312,8 +1312,8 @@ void PathDependencyTest::SetUp()
void PathDependencyTest::TearDown() void PathDependencyTest::TearDown()
{ {
ASSERT_EQ(m_assertAbsorber.m_numAssertsAbsorbed, 0); ASSERT_EQ(m_errorAbsorber->m_numAssertsAbsorbed, 0);
ASSERT_EQ(m_assertAbsorber.m_numErrorsAbsorbed, 0); ASSERT_EQ(m_errorAbsorber->m_numErrorsAbsorbed, 0);
AssetProcessorManagerTest::TearDown(); AssetProcessorManagerTest::TearDown();
} }
@ -1617,7 +1617,7 @@ TEST_F(PathDependencyTest, AssetProcessed_Impl_SelfReferrentialProductDependency
mainFile.m_products.push_back(productAssetId); mainFile.m_products.push_back(productAssetId);
// tell the APM that the asset has been processed and allow it to bubble through its event queue: // 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); m_assetProcessorManager->AssetProcessed(jobDetails.m_jobEntry, processJobResponse);
ASSERT_TRUE(BlockUntilIdle(5000)); ASSERT_TRUE(BlockUntilIdle(5000));
@ -1627,8 +1627,8 @@ TEST_F(PathDependencyTest, AssetProcessed_Impl_SelfReferrentialProductDependency
ASSERT_TRUE(dependencyContainer.empty()); ASSERT_TRUE(dependencyContainer.empty());
// We are testing 2 different dependencies, so we should get 2 warnings // We are testing 2 different dependencies, so we should get 2 warnings
ASSERT_EQ(m_assertAbsorber.m_numWarningsAbsorbed, 2); ASSERT_EQ(m_errorAbsorber->m_numWarningsAbsorbed, 2);
m_assertAbsorber.Clear(); m_errorAbsorber->Clear();
} }
// This test shows the process of deferring resolution of a path dependency works. // 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 // Test asset PrimaryFile1 has 4 conflict dependencies
ASSERT_EQ(m_assertAbsorber.m_numErrorsAbsorbed, 4); ASSERT_EQ(m_errorAbsorber->m_numErrorsAbsorbed, 4);
m_assertAbsorber.Clear(); m_errorAbsorber->Clear();
} }
TEST_F(PathDependencyTest, WildcardDependencies_Deferred_ResolveCorrectly) TEST_F(PathDependencyTest, WildcardDependencies_Deferred_ResolveCorrectly)
@ -2093,8 +2093,8 @@ TEST_F(PathDependencyTest, WildcardDependencies_ExcludedPathDeferred_ResolveCorr
// Test asset PrimaryFile1 has 4 conflict dependencies // Test asset PrimaryFile1 has 4 conflict dependencies
// After test assets dep2 and dep3 are processed, // After test assets dep2 and dep3 are processed,
// another 2 errors will be raised because of the confliction // another 2 errors will be raised because of the confliction
ASSERT_EQ(m_assertAbsorber.m_numErrorsAbsorbed, 6); ASSERT_EQ(m_errorAbsorber->m_numErrorsAbsorbed, 6);
m_assertAbsorber.Clear(); m_errorAbsorber->Clear();
} }
void PathDependencyTest::RunWildcardTest(bool useCorrectDatabaseSeparator, AssetBuilderSDK::ProductPathDependencyType pathDependencyType, bool buildDependenciesFirst) void PathDependencyTest::RunWildcardTest(bool useCorrectDatabaseSeparator, AssetBuilderSDK::ProductPathDependencyType pathDependencyType, bool buildDependenciesFirst)

@ -58,7 +58,6 @@ protected:
AZStd::unique_ptr<AssetProcessorManager_Test> m_assetProcessorManager; AZStd::unique_ptr<AssetProcessorManager_Test> m_assetProcessorManager;
AZStd::unique_ptr<AssetProcessor::MockApplicationManager> m_mockApplicationManager; AZStd::unique_ptr<AssetProcessor::MockApplicationManager> m_mockApplicationManager;
AZStd::unique_ptr<AssetProcessor::PlatformConfiguration> m_config; AZStd::unique_ptr<AssetProcessor::PlatformConfiguration> m_config;
UnitTestUtils::AssertAbsorber m_assertAbsorber; // absorb asserts/warnings/errors so that the unit test output is not cluttered
QString m_gameName; QString m_gameName;
QDir m_normalizedCacheRootDir; QDir m_normalizedCacheRootDir;
AZStd::atomic_bool m_isIdling; AZStd::atomic_bool m_isIdling;

@ -41,9 +41,11 @@ namespace O3DE::ProjectManager
void DownloadController::AddGemDownload(const QString& gemName) void DownloadController::AddGemDownload(const QString& gemName)
{ {
m_gemNames.push_back(gemName); m_gemNames.push_back(gemName);
emit GemDownloadAdded(gemName);
if (m_gemNames.size() == 1) if (m_gemNames.size() == 1)
{ {
m_worker->SetGemToDownload(m_gemNames[0], false); m_worker->SetGemToDownload(m_gemNames.front(), false);
m_workerThread.start(); m_workerThread.start();
} }
} }
@ -62,6 +64,7 @@ namespace O3DE::ProjectManager
else else
{ {
m_gemNames.erase(findResult); m_gemNames.erase(findResult);
emit GemDownloadRemoved(gemName);
} }
} }
} }
@ -69,7 +72,7 @@ namespace O3DE::ProjectManager
void DownloadController::UpdateUIProgress(int progress) void DownloadController::UpdateUIProgress(int progress)
{ {
m_lastProgress = progress; m_lastProgress = progress;
emit GemDownloadProgress(progress); emit GemDownloadProgress(m_gemNames.front(), progress);
} }
void DownloadController::HandleResults(const QString& result) void DownloadController::HandleResults(const QString& result)

@ -59,7 +59,9 @@ namespace O3DE::ProjectManager
signals: signals:
void StartGemDownload(const QString& gemName); void StartGemDownload(const QString& gemName);
void Done(const QString& gemName, bool success = true); 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: private:
DownloadWorker* m_worker; DownloadWorker* m_worker;

@ -30,6 +30,7 @@ namespace O3DE::ProjectManager
m_layout->setMargin(5); m_layout->setMargin(5);
m_layout->setAlignment(Qt::AlignTop); m_layout->setAlignment(Qt::AlignTop);
setLayout(m_layout); setLayout(m_layout);
setMinimumHeight(400);
QHBoxLayout* hLayout = new QHBoxLayout(); QHBoxLayout* hLayout = new QHBoxLayout();
@ -119,6 +120,12 @@ namespace O3DE::ProjectManager
setWindowFlags(Qt::FramelessWindowHint | Qt::Dialog); 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) void CartOverlayWidget::CreateGemSection(const QString& singularTitle, const QString& pluralTitle, GetTagIndicesCallback getTagIndices)
{ {
QWidget* widget = new QWidget(); QWidget* widget = new QWidget();
@ -162,13 +169,13 @@ namespace O3DE::ProjectManager
void CartOverlayWidget::CreateDownloadSection() void CartOverlayWidget::CreateDownloadSection()
{ {
QWidget* widget = new QWidget(); m_downloadSectionWidget = new QWidget();
widget->setFixedWidth(s_width); m_downloadSectionWidget->setFixedWidth(s_width);
m_layout->addWidget(widget); m_layout->addWidget(m_downloadSectionWidget);
QVBoxLayout* layout = new QVBoxLayout(); QVBoxLayout* layout = new QVBoxLayout();
layout->setAlignment(Qt::AlignTop); layout->setAlignment(Qt::AlignTop);
widget->setLayout(layout); m_downloadSectionWidget->setLayout(layout);
QLabel* titleLabel = new QLabel(); QLabel* titleLabel = new QLabel();
titleLabel->setObjectName("GemCatalogCartOverlaySectionLabel"); titleLabel->setObjectName("GemCatalogCartOverlaySectionLabel");
@ -187,88 +194,121 @@ namespace O3DE::ProjectManager
QLabel* processingQueueLabel = new QLabel("Processing Queue"); QLabel* processingQueueLabel = new QLabel("Processing Queue");
gemDownloadLayout->addWidget(processingQueueLabel); gemDownloadLayout->addWidget(processingQueueLabel);
QWidget* downloadingItemWidget = new QWidget(); m_downloadingListWidget = new QWidget();
downloadingItemWidget->setObjectName("GemCatalogCartOverlayGemDownloadBG"); m_downloadingListWidget->setObjectName("GemCatalogCartOverlayGemDownloadBG");
gemDownloadLayout->addWidget(downloadingItemWidget); gemDownloadLayout->addWidget(m_downloadingListWidget);
QVBoxLayout* downloadingItemLayout = new QVBoxLayout(); QVBoxLayout* downloadingItemLayout = new QVBoxLayout();
downloadingItemLayout->setAlignment(Qt::AlignTop); 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<QString>& 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<QString>& 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")); // connect to download controller data changed
nameProgressLayout->addWidget(progress); 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); void CartOverlayWidget::GemDownloadAdded(const QString& gemName)
nameProgressLayout->addSpacerItem(spacer); {
// 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("<a href=\"%1\">Cancel</a>").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<QString>& downloadQueue = m_downloadController->GetDownloadQueue();
QLabel* numDownloads = m_downloadingListWidget->findChild<QLabel*>("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("<a href=\"%1\">Cancel</a>").arg(gemName)); void CartOverlayWidget::GemDownloadRemoved(const QString& gemName)
cancelText->setTextInteractionFlags(Qt::LinksAccessibleByMouse); {
connect(cancelText, &QLabel::linkActivated, this, &CartOverlayWidget::OnCancelDownloadActivated); QWidget* gemToRemove = m_downloadingListWidget->findChild<QWidget*>(gemName);
nameProgressLayout->addWidget(cancelText); if (gemToRemove)
downloadingItemLayout->addLayout(nameProgressLayout); {
gemToRemove->deleteLater();
}
QProgressBar* downloadProgessBar = new QProgressBar(); if (m_downloadController->IsDownloadQueueEmpty())
downloadingItemLayout->addWidget(downloadProgessBar); {
downloadProgessBar->setValue(downloadingGemNumber == 0 ? downloadProgress : 0); m_downloadSectionWidget->hide();
} }
else
{
size_t downloadQueueSize = m_downloadController->GetDownloadQueue().size();
QLabel* numDownloads = m_downloadingListWidget->findChild<QLabel*>("NumDownloadsInProgressLabel");
numDownloads->setText(QString("%1 %2")
.arg(downloadQueueSize)
.arg(downloadQueueSize == 1 ? tr("download in progress...") : tr("downloads in progress...")));
}
}
widget->setUpdatesEnabled(true); void CartOverlayWidget::GemDownloadProgress(const QString& gemName, int percentage)
widget->show(); {
QWidget* gemToUpdate = m_downloadingListWidget->findChild<QWidget*>(gemName);
if (gemToUpdate)
{
QLabel* progressLabel = gemToUpdate->findChild<QLabel*>("DownloadProgressLabel");
if (progressLabel)
{
progressLabel->setText(QString("%1%").arg(percentage));
} }
}; QProgressBar* progressBar = gemToUpdate->findChild<QProgressBar*>("DownloadProgressBar");
if (progressBar)
{
progressBar->setValue(percentage);
}
}
}
auto downloadEnded = [=](const QString& /*gemName*/, bool /*success*/) void CartOverlayWidget::GemDownloadComplete(const QString& gemName, bool /*success*/)
{ {
update(0); // update the list to remove the gem that has finished GemDownloadRemoved(gemName); // 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);
} }
QVector<Tag> CartOverlayWidget::GetTagsFromModelIndices(const QVector<QModelIndex>& gems) const QVector<Tag> CartOverlayWidget::GetTagsFromModelIndices(const QVector<QModelIndex>& gems) const

@ -34,6 +34,13 @@ namespace O3DE::ProjectManager
public: public:
CartOverlayWidget(GemModel* gemModel, DownloadController* downloadController, QWidget* parent = nullptr); 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: private:
QVector<Tag> GetTagsFromModelIndices(const QVector<QModelIndex>& gems) const; QVector<Tag> GetTagsFromModelIndices(const QVector<QModelIndex>& gems) const;
@ -47,6 +54,9 @@ namespace O3DE::ProjectManager
GemModel* m_gemModel = nullptr; GemModel* m_gemModel = nullptr;
DownloadController* m_downloadController = nullptr; DownloadController* m_downloadController = nullptr;
QWidget* m_downloadSectionWidget = nullptr;
QWidget* m_downloadingListWidget = nullptr;
inline constexpr static int s_width = 240; inline constexpr static int s_width = 240;
}; };

@ -268,6 +268,7 @@ namespace O3DE::ProjectManager
if (added && GemModel::GetDownloadStatus(modelIndex) == GemInfo::DownloadStatus::NotDownloaded) if (added && GemModel::GetDownloadStatus(modelIndex) == GemInfo::DownloadStatus::NotDownloaded)
{ {
m_downloadController->AddGemDownload(GemModel::GetName(modelIndex)); m_downloadController->AddGemDownload(GemModel::GetName(modelIndex));
GemModel::SetDownloadStatus(*m_proxyModel, m_proxyModel->mapFromSource(modelIndex), GemInfo::DownloadStatus::Downloading);
} }
} }

@ -221,7 +221,6 @@ namespace O3DE::ProjectManager
ResetGemStatusFilter(); ResetGemStatusFilter();
ResetGemOriginFilter(); ResetGemOriginFilter();
ResetTypeFilter(); ResetTypeFilter();
ResetPlatformFilter();
ResetFeatureFilter(); ResetFeatureFilter();
} }

@ -37,6 +37,7 @@ ShaderResourceGroup ObjectSrg : SRG_PerObject
float m_padding; float m_padding;
bool m_useReflectionProbe; bool m_useReflectionProbe;
bool m_useParallaxCorrection; bool m_useParallaxCorrection;
float m_exposure;
}; };
ReflectionProbeData m_reflectionProbeData; ReflectionProbeData m_reflectionProbeData;

@ -85,12 +85,12 @@ void ApplyIBL(Surface surface, inout LightingData lightingData)
if(useIbl) if(useIbl)
{ {
float iblExposureFactor = pow(2.0, SceneSrg::m_iblExposure); float globalIblExposure = pow(2.0, SceneSrg::m_iblExposure);
if(useDiffuseIbl) if(useDiffuseIbl)
{ {
float3 iblDiffuse = GetIblDiffuse(surface.normal, surface.albedo, lightingData.diffuseResponse); float3 iblDiffuse = GetIblDiffuse(surface.normal, surface.albedo, lightingData.diffuseResponse);
lightingData.diffuseLighting += (iblDiffuse * iblExposureFactor * lightingData.diffuseAmbientOcclusion); lightingData.diffuseLighting += (iblDiffuse * globalIblExposure * lightingData.diffuseAmbientOcclusion);
} }
if(useSpecularIbl) if(useSpecularIbl)
@ -116,7 +116,8 @@ void ApplyIBL(Surface surface, inout LightingData lightingData)
iblSpecular = iblSpecular * (1.0 - clearCoatResponse) * (1.0 - clearCoatResponse) + clearCoatIblSpecular; 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);
} }
} }
} }

@ -46,6 +46,7 @@ ShaderResourceGroup ObjectSrg : SRG_PerObject
float m_padding; float m_padding;
bool m_useReflectionProbe; bool m_useReflectionProbe;
bool m_useParallaxCorrection; bool m_useParallaxCorrection;
float m_exposure;
}; };
ReflectionProbeData m_reflectionProbeData; ReflectionProbeData m_reflectionProbeData;

@ -81,7 +81,7 @@ PSOutput MainPS(VSOutput IN, in uint sampleIndex : SV_SampleIndex)
} }
// apply exposure setting // apply exposure setting
specular *= pow(2.0, SceneSrg::m_iblExposure); specular *= pow(2.0, ObjectSrg::m_exposure);
PSOutput OUT; PSOutput OUT;
OUT.m_color = float4(specular, 1.0f); OUT.m_color = float4(specular, 1.0f);

@ -17,6 +17,7 @@ ShaderResourceGroup ObjectSrg : SRG_PerObject
float3 m_outerObbHalfLengths; float3 m_outerObbHalfLengths;
float3 m_innerObbHalfLengths; float3 m_innerObbHalfLengths;
bool m_useParallaxCorrection; bool m_useParallaxCorrection;
float m_exposure;
TextureCube m_reflectionCubeMap; TextureCube m_reflectionCubeMap;
float4x4 GetWorldMatrix() float4x4 GetWorldMatrix()

@ -104,7 +104,7 @@ PSOutput MainPS(VSOutput IN, in uint sampleIndex : SV_SampleIndex)
blendWeight /= max(1.0f, blendWeightAllProbes); blendWeight /= max(1.0f, blendWeightAllProbes);
// apply exposure setting // apply exposure setting
specular *= pow(2.0, SceneSrg::m_iblExposure); specular *= pow(2.0, ObjectSrg::m_exposure);
// apply blend weight for additive blending // apply blend weight for additive blending
specular *= blendWeight; specular *= blendWeight;

@ -39,6 +39,8 @@ namespace AZ
bool IsCubeMapReferenced(const AZStd::string& relativePath) override; bool IsCubeMapReferenced(const AZStd::string& relativePath) override;
bool IsValidProbeHandle(const ReflectionProbeHandle& probe) const override { return (probe.get() != nullptr); } bool IsValidProbeHandle(const ReflectionProbeHandle& probe) const override { return (probe.get() != nullptr); }
void ShowProbeVisualization(const ReflectionProbeHandle& probe, bool showVisualization) override; 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 // FeatureProcessor overrides
void Activate() override; void Activate() override;

@ -50,6 +50,8 @@ namespace AZ
virtual bool IsCubeMapReferenced(const AZStd::string& relativePath) = 0; virtual bool IsCubeMapReferenced(const AZStd::string& relativePath) = 0;
virtual bool IsValidProbeHandle(const ReflectionProbeHandle& probe) const = 0; virtual bool IsValidProbeHandle(const ReflectionProbeHandle& probe) const = 0;
virtual void ShowProbeVisualization(const ReflectionProbeHandle& probe, bool showVisualization) = 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 Render
} // namespace AZ } // namespace AZ

@ -1178,6 +1178,9 @@ namespace AZ
AZ::RHI::ShaderInputConstantIndex useParallaxCorrectionConstantIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name("m_reflectionProbeData.m_useParallaxCorrection")); AZ::RHI::ShaderInputConstantIndex useParallaxCorrectionConstantIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name("m_reflectionProbeData.m_useParallaxCorrection"));
AZ_Error("MeshDataInstance", useParallaxCorrectionConstantIndex.IsValid(), "Failed to find ReflectionProbe constant index"); 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 // retrieve probe cubemap index
Name reflectionCubeMapImageName = Name("m_reflectionProbeCubeMap"); Name reflectionCubeMapImageName = Name("m_reflectionProbeCubeMap");
RHI::ShaderInputImageIndex reflectionCubeMapImageIndex = m_shaderResourceGroup->FindShaderInputImageIndex(reflectionCubeMapImageName); RHI::ShaderInputImageIndex reflectionCubeMapImageIndex = m_shaderResourceGroup->FindShaderInputImageIndex(reflectionCubeMapImageName);
@ -1198,6 +1201,7 @@ namespace AZ
m_shaderResourceGroup->SetConstant(innerObbHalfLengthsConstantIndex, reflectionProbes[0]->GetInnerObbWs().GetHalfLengths()); m_shaderResourceGroup->SetConstant(innerObbHalfLengthsConstantIndex, reflectionProbes[0]->GetInnerObbWs().GetHalfLengths());
m_shaderResourceGroup->SetConstant(useReflectionProbeConstantIndex, true); m_shaderResourceGroup->SetConstant(useReflectionProbeConstantIndex, true);
m_shaderResourceGroup->SetConstant(useParallaxCorrectionConstantIndex, reflectionProbes[0]->GetUseParallaxCorrection()); m_shaderResourceGroup->SetConstant(useParallaxCorrectionConstantIndex, reflectionProbes[0]->GetUseParallaxCorrection());
m_shaderResourceGroup->SetConstant(exposureConstantIndex, reflectionProbes[0]->GetRenderExposure());
m_shaderResourceGroup->SetImage(reflectionCubeMapImageIndex, reflectionProbes[0]->GetCubeMapImage()); m_shaderResourceGroup->SetImage(reflectionCubeMapImageIndex, reflectionProbes[0]->GetCubeMapImage());
} }

@ -46,7 +46,11 @@ namespace AZ
void BlendColorGradingLutsPass::InitializeShaderVariant() 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, // 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. // and hence zero LUTs are blended resulting in an identity LUT.

@ -120,15 +120,17 @@ namespace AZ
m_scene->RemoveRenderPipeline(m_environmentCubeMapPipelineId); m_scene->RemoveRenderPipeline(m_environmentCubeMapPipelineId);
m_environmentCubeMapPass = nullptr; m_environmentCubeMapPass = nullptr;
// restore exposure // restore exposures
sceneSrg->SetConstant(m_iblExposureConstantIndex, m_previousExposure); sceneSrg->SetConstant(m_globalIblExposureConstantIndex, m_previousGlobalIblExposure);
sceneSrg->SetConstant(m_skyBoxExposureConstantIndex, m_previousSkyBoxExposure);
m_buildingCubeMap = false; m_buildingCubeMap = false;
} }
else else
{ {
// set exposure to 0.0 while baking the cubemap // set exposures to the user specified value while baking the cubemap
sceneSrg->SetConstant(m_iblExposureConstantIndex, 0.0f); 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_outerObbHalfLengthsRenderConstantIndex, m_outerObbWs.GetHalfLengths());
m_renderOuterSrg->SetConstant(m_reflectionRenderData->m_innerObbHalfLengthsRenderConstantIndex, m_innerObbWs.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_useParallaxCorrectionRenderConstantIndex, m_useParallaxCorrection);
m_renderOuterSrg->SetConstant(m_reflectionRenderData->m_exposureConstantIndex, m_renderExposure);
m_renderOuterSrg->SetImage(m_reflectionRenderData->m_reflectionCubeMapRenderImageIndex, m_cubeMapImage); m_renderOuterSrg->SetImage(m_reflectionRenderData->m_reflectionCubeMapRenderImageIndex, m_cubeMapImage);
m_renderOuterSrg->Compile(); 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_outerObbHalfLengthsRenderConstantIndex, m_outerObbWs.GetHalfLengths());
m_renderInnerSrg->SetConstant(m_reflectionRenderData->m_innerObbHalfLengthsRenderConstantIndex, m_innerObbWs.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_useParallaxCorrectionRenderConstantIndex, m_useParallaxCorrection);
m_renderInnerSrg->SetConstant(m_reflectionRenderData->m_exposureConstantIndex, m_renderExposure);
m_renderInnerSrg->SetImage(m_reflectionRenderData->m_reflectionCubeMapRenderImageIndex, m_cubeMapImage); m_renderInnerSrg->SetImage(m_reflectionRenderData->m_reflectionCubeMapRenderImageIndex, m_cubeMapImage);
m_renderInnerSrg->Compile(); m_renderInnerSrg->Compile();
@ -303,9 +307,10 @@ namespace AZ
const RPI::Ptr<RPI::ParentPass>& rootPass = environmentCubeMapPipeline->GetRootPass(); const RPI::Ptr<RPI::ParentPass>& rootPass = environmentCubeMapPipeline->GetRootPass();
rootPass->AddChild(m_environmentCubeMapPass); rootPass->AddChild(m_environmentCubeMapPass);
// store the current IBL exposure value // store the current IBL exposure values
Data::Instance<RPI::ShaderResourceGroup> sceneSrg = m_scene->GetShaderResourceGroup(); Data::Instance<RPI::ShaderResourceGroup> sceneSrg = m_scene->GetShaderResourceGroup();
m_previousExposure = sceneSrg->GetConstant<float>(m_iblExposureConstantIndex); m_previousGlobalIblExposure = sceneSrg->GetConstant<float>(m_globalIblExposureConstantIndex);
m_previousSkyBoxExposure = sceneSrg->GetConstant<float>(m_skyBoxExposureConstantIndex);
m_scene->AddRenderPipeline(environmentCubeMapPipeline); m_scene->AddRenderPipeline(environmentCubeMapPipeline);
} }
@ -326,6 +331,17 @@ namespace AZ
m_meshFeatureProcessor->SetVisible(m_visualizationMeshHandle, showVisualization); 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 RHI::DrawPacket* ReflectionProbe::BuildDrawPacket(
const Data::Instance<RPI::ShaderResourceGroup>& srg, const Data::Instance<RPI::ShaderResourceGroup>& srg,
const RPI::Ptr<RPI::PipelineStateForDraw>& pipelineState, const RPI::Ptr<RPI::PipelineStateForDraw>& pipelineState,

@ -61,6 +61,7 @@ namespace AZ
RHI::ShaderInputNameIndex m_outerObbHalfLengthsRenderConstantIndex = "m_outerObbHalfLengths"; RHI::ShaderInputNameIndex m_outerObbHalfLengthsRenderConstantIndex = "m_outerObbHalfLengths";
RHI::ShaderInputNameIndex m_innerObbHalfLengthsRenderConstantIndex = "m_innerObbHalfLengths"; RHI::ShaderInputNameIndex m_innerObbHalfLengthsRenderConstantIndex = "m_innerObbHalfLengths";
RHI::ShaderInputNameIndex m_useParallaxCorrectionRenderConstantIndex = "m_useParallaxCorrection"; RHI::ShaderInputNameIndex m_useParallaxCorrectionRenderConstantIndex = "m_useParallaxCorrection";
RHI::ShaderInputNameIndex m_exposureConstantIndex = "m_exposure";
RHI::ShaderInputNameIndex m_reflectionCubeMapRenderImageIndex = "m_reflectionCubeMap"; RHI::ShaderInputNameIndex m_reflectionCubeMapRenderImageIndex = "m_reflectionCubeMap";
}; };
@ -106,6 +107,14 @@ namespace AZ
// enables or disables rendering of the visualization sphere // enables or disables rendering of the visualization sphere
void ShowVisualization(bool showVisualization); 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: private:
AZ_DISABLE_COPY_MOVE(ReflectionProbe); AZ_DISABLE_COPY_MOVE(ReflectionProbe);
@ -157,6 +166,8 @@ namespace AZ
RHI::ConstPtr<RHI::DrawPacket> m_blendWeightDrawPacket; RHI::ConstPtr<RHI::DrawPacket> m_blendWeightDrawPacket;
RHI::ConstPtr<RHI::DrawPacket> m_renderOuterDrawPacket; RHI::ConstPtr<RHI::DrawPacket> m_renderOuterDrawPacket;
RHI::ConstPtr<RHI::DrawPacket> m_renderInnerDrawPacket; RHI::ConstPtr<RHI::DrawPacket> m_renderInnerDrawPacket;
float m_renderExposure = 0.0f;
float m_bakeExposure = 0.0f;
bool m_updateSrg = false; bool m_updateSrg = false;
const RHI::DrawItemSortKey InvalidSortKey = static_cast<RHI::DrawItemSortKey>(-1); const RHI::DrawItemSortKey InvalidSortKey = static_cast<RHI::DrawItemSortKey>(-1);
@ -169,8 +180,10 @@ namespace AZ
RPI::Ptr<RPI::EnvironmentCubeMapPass> m_environmentCubeMapPass = nullptr; RPI::Ptr<RPI::EnvironmentCubeMapPass> m_environmentCubeMapPass = nullptr;
RPI::RenderPipelineId m_environmentCubeMapPipelineId; RPI::RenderPipelineId m_environmentCubeMapPipelineId;
BuildCubeMapCallback m_callback; BuildCubeMapCallback m_callback;
RHI::ShaderInputNameIndex m_iblExposureConstantIndex = "m_iblExposure"; RHI::ShaderInputNameIndex m_globalIblExposureConstantIndex = "m_iblExposure";
float m_previousExposure = 0.0f; RHI::ShaderInputNameIndex m_skyBoxExposureConstantIndex = "m_cubemapExposure";
float m_previousGlobalIblExposure = 0.0f;
float m_previousSkyBoxExposure = 0.0f;
bool m_buildingCubeMap = false; bool m_buildingCubeMap = false;
}; };

@ -283,6 +283,18 @@ namespace AZ
probe->ShowVisualization(showVisualization); 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) void ReflectionProbeFeatureProcessor::FindReflectionProbes(const Vector3& position, ReflectionProbeVector& reflectionProbes)
{ {
reflectionProbes.clear(); reflectionProbes.clear();
@ -431,7 +443,12 @@ namespace AZ
{ {
// load shader // load shader
shader = RPI::LoadCriticalShader(filePath); 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 // store drawlist tag
drawListTag = shader->GetDrawListTag(); drawListTag = shader->GetDrawListTag();

@ -75,6 +75,9 @@ namespace AZ
//! Return True if the swap chain prefers exclusive full screen mode and a transition happened, false otherwise. //! 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; } 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: protected:
SwapChain(); 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: private:
bool ValidateDescriptor(const SwapChainDescriptor& descriptor) const; bool ValidateDescriptor(const SwapChainDescriptor& descriptor) const;

@ -134,7 +134,6 @@ namespace AZ
m_scopeAttachmentLookup.clear(); m_scopeAttachmentLookup.clear();
m_imageAttachments.clear(); m_imageAttachments.clear();
m_bufferAttachments.clear(); m_bufferAttachments.clear();
m_swapChainAttachments.clear();
m_importedImageAttachments.clear(); m_importedImageAttachments.clear();
m_importedBufferAttachments.clear(); m_importedBufferAttachments.clear();
m_transientImageAttachments.clear(); m_transientImageAttachments.clear();
@ -153,6 +152,13 @@ namespace AZ
delete attachment; delete attachment;
} }
m_attachments.clear(); m_attachments.clear();
for (auto swapchainAttachment : m_swapChainAttachments)
{
swapchainAttachment->GetSwapChain()->ProcessRecreation();
}
m_swapChainAttachments.clear();
} }
ImageDescriptor FrameGraphAttachmentDatabase::GetImageDescriptor(const AttachmentId& attachmentId) const ImageDescriptor FrameGraphAttachmentDatabase::GetImageDescriptor(const AttachmentId& attachmentId) const

@ -58,43 +58,68 @@ namespace AZ
// Overwrite descriptor dimensions with the native ones (the ones assigned by the platform) returned by InitInternal. // Overwrite descriptor dimensions with the native ones (the ones assigned by the platform) returned by InitInternal.
m_descriptor.m_dimensions = nativeDimensions; 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) return resultCode;
{ }
m_images.emplace_back(RHI::Factory::Get().CreateImage());
}
InitImageRequest request; void SwapChain::ShutdownImages()
{
// Shutdown existing set of images.
uint32_t imageSize = aznumeric_cast<uint32_t>(m_images.size());
for (uint32_t imageIdx = 0; imageIdx < imageSize; ++imageIdx)
{
m_images[imageIdx]->Shutdown();
}
RHI::ImageDescriptor& imageDescriptor = request.m_descriptor; m_images.clear();
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 < m_descriptor.m_dimensions.m_imageCount; ++imageIdx) ResultCode SwapChain::InitImages()
{ {
request.m_image = m_images[imageIdx].get(); ResultCode resultCode = ResultCode::Success;
request.m_imageIndex = imageIdx;
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( for (uint32_t imageIdx = 0; imageIdx < m_descriptor.m_dimensions.m_imageCount; ++imageIdx)
request.m_image, {
imageDescriptor, request.m_image = m_images[imageIdx].get();
[this, &request]() request.m_imageIndex = imageIdx;
resultCode = ImagePoolBase::InitImage(
request.m_image, imageDescriptor,
[this, &request]()
{ {
return InitImageInternal(request); return InitImageInternal(request);
}); });
if (resultCode != ResultCode::Success) if (resultCode != ResultCode::Success)
{ {
Shutdown(); AZ_Error("Swapchain", false, "Failed to initialize images.");
break; Shutdown();
} break;
} }
} }
// Reset the current index back to 0 so we match the platform swap chain.
m_currentImageIndex = 0;
return resultCode; return resultCode;
} }
@ -106,62 +131,14 @@ namespace AZ
ResultCode SwapChain::Resize(const RHI::SwapChainDimensions& dimensions) ResultCode SwapChain::Resize(const RHI::SwapChainDimensions& dimensions)
{ {
// Shutdown existing set of images. ShutdownImages();
for (uint32_t imageIdx = 0; imageIdx < GetImageCount(); ++imageIdx)
{
m_images[imageIdx]->Shutdown();
}
SwapChainDimensions nativeDimensions = dimensions; SwapChainDimensions nativeDimensions = dimensions;
ResultCode resultCode = ResizeInternal(dimensions, &nativeDimensions); ResultCode resultCode = ResizeInternal(dimensions, &nativeDimensions);
if (resultCode == ResultCode::Success) if (resultCode == ResultCode::Success)
{ {
m_descriptor.m_dimensions = nativeDimensions; m_descriptor.m_dimensions = nativeDimensions;
m_images.reserve(m_descriptor.m_dimensions.m_imageCount); resultCode = InitImages();
// If the new display mode has more buffers, add them.
while (m_images.size() < static_cast<size_t>(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<size_t>(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;
} }
return resultCode; return resultCode;
@ -188,7 +165,7 @@ namespace AZ
uint32_t SwapChain::GetImageCount() const uint32_t SwapChain::GetImageCount() const
{ {
return static_cast<uint32_t>(m_images.size()); return aznumeric_cast<uint32_t>(m_images.size());
} }
uint32_t SwapChain::GetCurrentImageIndex() const uint32_t SwapChain::GetCurrentImageIndex() const
@ -209,8 +186,18 @@ namespace AZ
void SwapChain::Present() void SwapChain::Present()
{ {
AZ_TRACE_METHOD(); AZ_TRACE_METHOD();
m_currentImageIndex = PresentInternal(); // Due to swapchain recreation, the images are refreshed.
AZ_Assert(m_currentImageIndex < m_images.size(), "Invalid image index"); // There is no need to present swapchain for this frame.
const uint32_t imageCount = aznumeric_cast<uint32_t>(m_images.size());
if (imageCount == 0)
{
return;
}
else
{
m_currentImageIndex = PresentInternal();
AZ_Assert(m_currentImageIndex < imageCount, "Invalid image index");
}
} }
} }
} }

@ -59,6 +59,19 @@ namespace AZ
m_swapChainBarrier.m_isValid = true; m_swapChainBarrier.m_isValid = true;
} }
void SwapChain::ProcessRecreation()
{
if (m_pendingRecreation)
{
ShutdownImages();
InvalidateNativeSwapChain();
CreateSwapchain();
InitImages();
m_pendingRecreation = false;
}
}
void SwapChain::SetVerticalSyncIntervalInternal(uint32_t previousVsyncInterval) void SwapChain::SetVerticalSyncIntervalInternal(uint32_t previousVsyncInterval)
{ {
if (GetDescriptor().m_verticalSyncInterval == 0 || previousVsyncInterval == 0) 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. // 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) if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR)
{ {
InvalidateNativeSwapChain(); m_pendingRecreation = true;
CreateSwapchain();
} }
else else
{ {
@ -246,18 +258,16 @@ namespace AZ
} }
}; };
m_presentationQueue->QueueCommand(AZStd::move(presentCommand));
uint32_t acquiredImageIndex = GetCurrentImageIndex(); uint32_t acquiredImageIndex = GetCurrentImageIndex();
RHI::ResultCode result = AcquireNewImage(&acquiredImageIndex); RHI::ResultCode result = AcquireNewImage(&acquiredImageIndex);
if (result == RHI::ResultCode::Fail) if (result == RHI::ResultCode::Fail)
{ {
InvalidateNativeSwapChain(); m_pendingRecreation = true;
CreateSwapchain();
return 0; return 0;
} }
else else
{ {
m_presentationQueue->QueueCommand(AZStd::move(presentCommand));
return acquiredImageIndex; return acquiredImageIndex;
} }
} }

@ -51,6 +51,7 @@ namespace AZ
void QueueBarrier(const VkPipelineStageFlags src, const VkPipelineStageFlags dst, const VkImageMemoryBarrier& imageBarrier); void QueueBarrier(const VkPipelineStageFlags src, const VkPipelineStageFlags dst, const VkImageMemoryBarrier& imageBarrier);
void ProcessRecreation() override;
private: private:
SwapChain() = default; SwapChain() = default;

@ -31,6 +31,7 @@ namespace AZ
static constexpr const char UvGroupName[] = "uvSets"; static constexpr const char UvGroupName[] = "uvSets";
class MaterialAsset; class MaterialAsset;
class MaterialAssetCreator;
//! This is a simple data structure for serializing in/out material source files. //! This is a simple data structure for serializing in/out material source files.
class MaterialSourceData final class MaterialSourceData final
@ -78,15 +79,33 @@ namespace AZ
//! Creates a MaterialAsset from the MaterialSourceData content. //! Creates a MaterialAsset from the MaterialSourceData content.
//! @param assetId ID for the MaterialAsset //! @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 elevateWarnings Indicates whether to treat warnings as errors
//! @param includeMaterialPropertyNames Indicates whether to save material property names into the material asset file //! @param includeMaterialPropertyNames Indicates whether to save material property names into the material asset file
Outcome<Data::Asset<MaterialAsset>> CreateMaterialAsset( Outcome<Data::Asset<MaterialAsset>> CreateMaterialAsset(
Data::AssetId assetId, Data::AssetId assetId,
AZStd::string_view materialSourceFilePath = "", AZStd::string_view materialSourceFilePath = "",
bool elevateWarnings = true, bool elevateWarnings = true,
bool includeMaterialPropertyNames = true bool includeMaterialPropertyNames = true) const;
) 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<Data::Asset<MaterialAsset>> CreateMaterialAssetFromSourceData(
Data::AssetId assetId,
AZStd::string_view materialSourceFilePath = "",
bool elevateWarnings = true,
bool includeMaterialPropertyNames = true,
AZStd::unordered_set<AZStd::string>* sourceDependencies = nullptr) const;
private:
void ApplyPropertiesToAssetCreator(
AZ::RPI::MaterialAssetCreator& materialAssetCreator, const AZStd::string_view& materialSourceFilePath) const;
}; };
} // namespace RPI } // namespace RPI
} // namespace AZ } // namespace AZ

@ -97,8 +97,7 @@ namespace AZ
// SystemTickBus::OnTick // SystemTickBus::OnTick
void OnSystemTick() override; void OnSystemTick() override;
// Fill system time and game time information for simulation or rendering float GetCurrentTime();
void FillTickTimeInfo();
// The set of core asset handlers registered by the system. // The set of core asset handlers registered by the system.
AZStd::vector<AZStd::unique_ptr<Data::AssetHandler>> m_assetHandlers; AZStd::vector<AZStd::unique_ptr<Data::AssetHandler>> m_assetHandlers;
@ -124,7 +123,8 @@ namespace AZ
// The job policy used for feature processor's rendering prepare // The job policy used for feature processor's rendering prepare
RHI::JobPolicy m_prepareRenderJobPolicy = RHI::JobPolicy::Parallel; RHI::JobPolicy m_prepareRenderJobPolicy = RHI::JobPolicy::Parallel;
TickTimeInfo m_tickTime; ScriptTimePoint m_startTime;
float m_currentSimulationTime = 0.0f;
RPISystemDescriptor m_descriptor; RPISystemDescriptor m_descriptor;

@ -32,7 +32,6 @@ namespace AZ
namespace RPI namespace RPI
{ {
class Scene; class Scene;
struct TickTimeInfo;
class ShaderResourceGroup; class ShaderResourceGroup;
class AnyAsset; class AnyAsset;
class WindowContext; class WindowContext;
@ -203,7 +202,7 @@ namespace AZ
void OnRemovedFromScene(Scene* scene); void OnRemovedFromScene(Scene* scene);
// Called when this pipeline is about to be rendered // 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. // Called when the rendering of current frame is finished.
void OnFrameEnd(); void OnFrameEnd();

@ -48,14 +48,6 @@ namespace AZ
// Callback function to modify values of a ShaderResourceGroup // Callback function to modify values of a ShaderResourceGroup
using ShaderResourceGroupCallback = AZStd::function<void(ShaderResourceGroup*)>; using ShaderResourceGroupCallback = AZStd::function<void(ShaderResourceGroup*)>;
//! A structure for ticks which contains system time and game time.
struct TickTimeInfo
{
float m_currentGameTime;
float m_gameDeltaTime = 0;
};
class Scene final class Scene final
: public SceneRequestBus::Handler : public SceneRequestBus::Handler
{ {
@ -179,12 +171,14 @@ namespace AZ
// Cpu simulation which runs all active FeatureProcessor Simulate() functions. // 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. // @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 // Collect DrawPackets from FeatureProcessors
// @param jobPolicy if it's JobPolicy::Parallel, the function will spawn a job thread for each FeatureProcessor's // @param jobPolicy if it's JobPolicy::Parallel, the function will spawn a job thread for each FeatureProcessor's
// PrepareRender. // 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. // Function called when the current frame is finished rendering.
void OnFrameEnd(); void OnFrameEnd();
@ -267,6 +261,7 @@ namespace AZ
// Registry which allocates draw filter tag for RenderPipeline // Registry which allocates draw filter tag for RenderPipeline
RHI::Ptr<RHI::DrawFilterTagRegistry> m_drawFilterTagRegistry; RHI::Ptr<RHI::DrawFilterTagRegistry> m_drawFilterTagRegistry;
RHI::ShaderInputConstantIndex m_timeInputIndex;
float m_simulationTime; float m_simulationTime;
}; };

@ -118,7 +118,7 @@ namespace AZ
ResetIssueCounts(); // Because the asset creator can be used multiple times ResetIssueCounts(); // Because the asset creator can be used multiple times
m_asset = Data::AssetManager::Instance().CreateAsset<AssetDataT>(assetId, AZ::Data::AssetLoadBehavior::PreLoad); m_asset = Data::Asset<AssetDataT>(assetId, aznew AssetDataT, AZ::Data::AssetLoadBehavior::PreLoad);
m_beginCalled = true; m_beginCalled = true;
if (!m_asset) if (!m_asset)
@ -138,6 +138,7 @@ namespace AZ
} }
else else
{ {
Data::AssetManager::Instance().AssignAssetData(m_asset);
result = AZStd::move(m_asset); result = AZStd::move(m_asset);
success = true; success = true;
} }

@ -137,7 +137,10 @@ namespace AZ
} }
else if (!m_luaSourceFile.empty()) else if (!m_luaSourceFile.empty())
{ {
auto loadOutcome = RPI::AssetUtils::LoadAsset<ScriptAsset>(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<ScriptAsset>(materialTypeSourceFilePath, m_luaSourceFile, ScriptAsset::CompiledAssetSubId);
if (!loadOutcome) if (!loadOutcome)
{ {
AZ_Error("LuaMaterialFunctorSourceData", false, "Could not load script file '%s'", m_luaSourceFile.c_str()); AZ_Error("LuaMaterialFunctorSourceData", false, "Could not load script file '%s'", m_luaSourceFile.c_str());

@ -17,6 +17,7 @@
#include <Atom/RPI.Edit/Common/AssetUtils.h> #include <Atom/RPI.Edit/Common/AssetUtils.h>
#include <Atom/RPI.Edit/Common/JsonFileLoadContext.h> #include <Atom/RPI.Edit/Common/JsonFileLoadContext.h>
#include <Atom/RPI.Edit/Common/JsonReportingHelper.h> #include <Atom/RPI.Edit/Common/JsonReportingHelper.h>
#include <Atom/RPI.Edit/Common/JsonUtils.h>
#include <Atom/RPI.Reflect/Material/MaterialAssetCreator.h> #include <Atom/RPI.Reflect/Material/MaterialAssetCreator.h>
#include <Atom/RPI.Reflect/Material/MaterialPropertiesLayout.h> #include <Atom/RPI.Reflect/Material/MaterialPropertiesLayout.h>
@ -126,7 +127,8 @@ namespace AZ
return changesWereApplied ? ApplyVersionUpdatesResult::UpdatesApplied : ApplyVersionUpdatesResult::NoUpdates; return changesWereApplied ? ApplyVersionUpdatesResult::UpdatesApplied : ApplyVersionUpdatesResult::NoUpdates;
} }
Outcome<Data::Asset<MaterialAsset> > MaterialSourceData::CreateMaterialAsset(Data::AssetId assetId, AZStd::string_view materialSourceFilePath, bool elevateWarnings, bool includeMaterialPropertyNames) const Outcome<Data::Asset<MaterialAsset>> MaterialSourceData::CreateMaterialAsset(
Data::AssetId assetId, AZStd::string_view materialSourceFilePath, bool elevateWarnings, bool includeMaterialPropertyNames) const
{ {
MaterialAssetCreator materialAssetCreator; MaterialAssetCreator materialAssetCreator;
materialAssetCreator.SetElevateWarnings(elevateWarnings); materialAssetCreator.SetElevateWarnings(elevateWarnings);
@ -172,6 +174,128 @@ namespace AZ
materialAssetCreator.Begin(assetId, *parentMaterialAsset.GetValue().Get(), includeMaterialPropertyNames); materialAssetCreator.Begin(assetId, *parentMaterialAsset.GetValue().Get(), includeMaterialPropertyNames);
} }
ApplyPropertiesToAssetCreator(materialAssetCreator, materialSourceFilePath);
Data::Asset<MaterialAsset> material;
if (materialAssetCreator.End(material))
{
return Success(material);
}
else
{
return Failure();
}
}
Outcome<Data::Asset<MaterialAsset>> MaterialSourceData::CreateMaterialAssetFromSourceData(
Data::AssetId assetId,
AZStd::string_view materialSourceFilePath,
bool elevateWarnings,
bool includeMaterialPropertyNames,
AZStd::unordered_set<AZStd::string>* 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<AZStd::string> 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<MaterialSourceData> 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<MaterialAsset> 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& group : m_properties)
{ {
for (auto& property : group.second) for (auto& property : group.second)
@ -183,43 +307,49 @@ namespace AZ
} }
else else
{ {
MaterialPropertyIndex propertyIndex = materialAssetCreator.m_materialPropertiesLayout->FindPropertyIndex(propertyId.GetFullName()); MaterialPropertyIndex propertyIndex =
materialAssetCreator.m_materialPropertiesLayout->FindPropertyIndex(propertyId.GetFullName());
if (propertyIndex.IsValid()) if (propertyIndex.IsValid())
{ {
const MaterialPropertyDescriptor* propertyDescriptor = materialAssetCreator.m_materialPropertiesLayout->GetPropertyDescriptor(propertyIndex); const MaterialPropertyDescriptor* propertyDescriptor =
materialAssetCreator.m_materialPropertiesLayout->GetPropertyDescriptor(propertyIndex);
switch (propertyDescriptor->GetDataType()) switch (propertyDescriptor->GetDataType())
{ {
case MaterialPropertyDataType::Image: case MaterialPropertyDataType::Image:
{
Outcome<Data::Asset<ImageAsset>> imageAssetResult = MaterialUtils::GetImageAssetReference(materialSourceFilePath, property.second.m_value.GetValue<AZStd::string>());
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<AZStd::string>().data()); Outcome<Data::Asset<ImageAsset>> imageAssetResult = MaterialUtils::GetImageAssetReference(
materialSourceFilePath, property.second.m_value.GetValue<AZStd::string>());
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<AZStd::string>().data());
}
} }
} break;
break;
case MaterialPropertyDataType::Enum: case MaterialPropertyDataType::Enum:
{
AZ::Name enumName = AZ::Name(property.second.m_value.GetValue<AZStd::string>());
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); AZ::Name enumName = AZ::Name(property.second.m_value.GetValue<AZStd::string>());
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);
}
} }
} break;
break;
default: default:
materialAssetCreator.SetPropertyValue(propertyId.GetFullName(), property.second.m_value); materialAssetCreator.SetPropertyValue(propertyId.GetFullName(), property.second.m_value);
break; break;
@ -227,21 +357,12 @@ namespace AZ
} }
else 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<MaterialAsset> material;
if (materialAssetCreator.End(material))
{
return Success(material);
}
else
{
return Failure();
}
} }
} // namespace RPI } // namespace RPI

@ -393,11 +393,12 @@ namespace AZ
for (const ShaderVariantReferenceData& shaderRef : m_shaderCollection) for (const ShaderVariantReferenceData& shaderRef : m_shaderCollection)
{ {
const auto& shaderFile = shaderRef.m_shaderFilePath; const auto& shaderFile = shaderRef.m_shaderFilePath;
const auto& shaderAsset = AssetUtils::LoadAsset<ShaderAsset>(materialTypeSourceFilePath, shaderFile, 0); auto shaderAssetResult = AssetUtils::LoadAsset<ShaderAsset>(materialTypeSourceFilePath, shaderFile, 0);
if (shaderAsset) if (shaderAssetResult)
{ {
auto optionsLayout = shaderAsset.GetValue()->GetShaderOptionGroupLayout(); auto shaderAsset = shaderAssetResult.GetValue();
auto optionsLayout = shaderAsset->GetShaderOptionGroupLayout();
ShaderOptionGroup options{ optionsLayout }; ShaderOptionGroup options{ optionsLayout };
for (auto& iter : shaderRef.m_shaderOptionValues) for (auto& iter : shaderRef.m_shaderOptionValues)
{ {
@ -408,12 +409,11 @@ namespace AZ
} }
materialTypeAssetCreator.AddShader( materialTypeAssetCreator.AddShader(
shaderAsset.GetValue(), options.GetShaderVariantId(), shaderAsset, options.GetShaderVariantId(),
shaderRef.m_shaderTag.IsEmpty() ? Uuid::CreateRandom().ToString<AZ::Name>() : shaderRef.m_shaderTag shaderRef.m_shaderTag.IsEmpty() ? Uuid::CreateRandom().ToString<AZ::Name>() : shaderRef.m_shaderTag);
);
// Gather UV names // Gather UV names
const ShaderInputContract& shaderInputContract = shaderAsset.GetValue()->GetInputContract(); const ShaderInputContract& shaderInputContract = shaderAsset->GetInputContract();
for (const ShaderInputContract::StreamChannelInfo& channel : shaderInputContract.m_streamChannels) for (const ShaderInputContract::StreamChannelInfo& channel : shaderInputContract.m_streamChannels)
{ {
const RHI::ShaderSemantic& semantic = channel.m_semantic; const RHI::ShaderSemantic& semantic = channel.m_semantic;
@ -493,15 +493,19 @@ namespace AZ
{ {
case MaterialPropertyDataType::Image: case MaterialPropertyDataType::Image:
{ {
Outcome<Data::Asset<ImageAsset>> imageAssetResult = MaterialUtils::GetImageAssetReference(materialTypeSourceFilePath, property.m_value.GetValue<AZStd::string>()); auto imageAssetResult = MaterialUtils::GetImageAssetReference(
materialTypeSourceFilePath, property.m_value.GetValue<AZStd::string>());
if (imageAssetResult.IsSuccess()) if (imageAssetResult)
{ {
materialTypeAssetCreator.SetPropertyValue(propertyId.GetFullName(), imageAssetResult.GetValue()); auto imageAsset = imageAssetResult.GetValue();
materialTypeAssetCreator.SetPropertyValue(propertyId.GetFullName(), imageAsset);
} }
else else
{ {
materialTypeAssetCreator.ReportError("Material property '%s': Could not find the image '%s'", propertyId.GetFullName().GetCStr(), property.m_value.GetValue<AZStd::string>().data()); materialTypeAssetCreator.ReportError(
"Material property '%s': Could not find the image '%s'", propertyId.GetFullName().GetCStr(),
property.m_value.GetValue<AZStd::string>().data());
} }
} }
break; break;

@ -136,6 +136,12 @@ namespace AZ
RHI::DrawLinear draw = RHI::DrawLinear(); RHI::DrawLinear draw = RHI::DrawLinear();
draw.m_vertexCount = 3; draw.m_vertexCount = 3;
if (m_shader == nullptr)
{
AZ_Error("PassSystem", false, "[FullscreenTrianglePass]: Shader not loaded!");
return;
}
RHI::PipelineStateDescriptorForDraw pipelineStateDescriptor; RHI::PipelineStateDescriptorForDraw pipelineStateDescriptor;
// [GFX TODO][ATOM-872] The pass should be able to drive the shader variant // [GFX TODO][ATOM-872] The pass should be able to drive the shader variant

@ -268,21 +268,23 @@ namespace AZ
AssetInitBus::Broadcast(&AssetInitBus::Events::PostLoadInit); AssetInitBus::Broadcast(&AssetInitBus::Events::PostLoadInit);
// Update tick time info m_currentSimulationTime = GetCurrentTime();
FillTickTimeInfo();
for (auto& scene : m_scenes) 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 timeAtCurrentTick;
ScriptTimePoint currentTime; AZ::TickRequestBus::BroadcastResult(timeAtCurrentTick, &AZ::TickRequestBus::Events::GetTimeAtCurrentTick);
AZ::TickRequestBus::BroadcastResult(currentTime, &AZ::TickRequestBus::Events::GetTimeAtCurrentTick);
m_tickTime.m_currentGameTime = static_cast<float>(currentTime.GetSeconds()); // 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<float>(currentTime);
} }
void RPISystem::RenderTick() void RPISystem::RenderTick()
@ -301,7 +303,7 @@ namespace AZ
// [GFX TODO] We may parallel scenes' prepare render. // [GFX TODO] We may parallel scenes' prepare render.
for (auto& scenePtr : m_scenes) for (auto& scenePtr : m_scenes)
{ {
scenePtr->PrepareRender(m_tickTime, m_prepareRenderJobPolicy); scenePtr->PrepareRender(m_prepareRenderJobPolicy, m_currentSimulationTime);
} }
m_rhiSystem.FrameUpdate( m_rhiSystem.FrameUpdate(

@ -375,7 +375,7 @@ namespace AZ
m_scene->RemoveRenderPipeline(m_nameId); 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"); AZ_PROFILE_SCOPE(RPI, "RenderPipeline: OnStartFrame");

@ -44,6 +44,9 @@ namespace AZ
{ {
auto shaderAsset = RPISystemInterface::Get()->GetCommonShaderAssetForSrgs(); auto shaderAsset = RPISystemInterface::Get()->GetCommonShaderAssetForSrgs();
scene->m_srg = ShaderResourceGroup::Create(shaderAsset, sceneSrgLayout->GetName()); 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; scene->m_name = sceneDescriptor.m_nameId;
@ -410,11 +413,11 @@ namespace AZ
//[GFX TODO]: the completion job should start here //[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"); 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 previous simulation job wasn't done, wait for it to finish.
if (m_taskGraphActive) if (m_taskGraphActive)
@ -483,11 +486,9 @@ namespace AZ
{ {
if (m_srg) if (m_srg)
{ {
// Set value for constants defined in SceneTimeSrg.azsli if (m_timeInputIndex.IsValid())
RHI::ShaderInputConstantIndex timeIndex = m_srg->FindShaderInputConstantIndex(Name{ "m_time" });
if (timeIndex.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 // signal any handlers to update values for their partial scene srg
@ -620,7 +621,7 @@ namespace AZ
WaitAndCleanCompletionJob(finalizeDrawListsCompletion); 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"); AZ_PROFILE_SCOPE(RPI, "Scene: PrepareRender");
@ -644,7 +645,7 @@ namespace AZ
if (pipeline->NeedsRender()) if (pipeline->NeedsRender())
{ {
activePipelines.push_back(pipeline); activePipelines.push_back(pipeline);
pipeline->OnStartFrame(tickInfo); pipeline->OnStartFrame(simulationTime);
} }
} }
} }

@ -188,6 +188,10 @@ namespace AZ
void MaterialTypeAsset::SetReady() void MaterialTypeAsset::SetReady()
{ {
m_status = AssetStatus::Ready; 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() bool MaterialTypeAsset::PostLoadInit()

@ -126,8 +126,8 @@ namespace AZ
} }
ShaderCollection::Item::Item() ShaderCollection::Item::Item()
: m_renderStatesOverlay(RHI::GetInvalidRenderStates())
{ {
m_renderStatesOverlay = RHI::GetInvalidRenderStates();
} }
ShaderCollection::Item& ShaderCollection::operator[](size_t i) ShaderCollection::Item& ShaderCollection::operator[](size_t i)
@ -156,7 +156,8 @@ namespace AZ
} }
ShaderCollection::Item::Item(const Data::Asset<ShaderAsset>& shaderAsset, const AZ::Name& shaderTag, ShaderVariantId variantId) ShaderCollection::Item::Item(const Data::Asset<ShaderAsset>& shaderAsset, const AZ::Name& shaderTag, ShaderVariantId variantId)
: m_shaderAsset(shaderAsset) : m_renderStatesOverlay(RHI::GetInvalidRenderStates())
, m_shaderAsset(shaderAsset)
, m_shaderVariantId(variantId) , m_shaderVariantId(variantId)
, m_shaderTag(shaderTag) , m_shaderTag(shaderTag)
, m_shaderOptionGroup(shaderAsset->GetShaderOptionGroupLayout(), variantId) , m_shaderOptionGroup(shaderAsset->GetShaderOptionGroupLayout(), variantId)
@ -164,7 +165,8 @@ namespace AZ
} }
ShaderCollection::Item::Item(Data::Asset<ShaderAsset>&& shaderAsset, const AZ::Name& shaderTag, ShaderVariantId variantId) ShaderCollection::Item::Item(Data::Asset<ShaderAsset>&& 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_shaderVariantId(variantId)
, m_shaderTag(shaderTag) , m_shaderTag(shaderTag)
, m_shaderOptionGroup(shaderAsset->GetShaderOptionGroupLayout(), variantId) , m_shaderOptionGroup(shaderAsset->GetShaderOptionGroupLayout(), variantId)

@ -33,7 +33,7 @@
}, },
"subsurfaceScattering": { "subsurfaceScattering": {
"enableSubsurfaceScattering": true, "enableSubsurfaceScattering": true,
"influenceMap": "Objects/Lucy/Lucy_thickness.tif", "influenceMap": "TestData/Textures/checker8x8_gray_512.png",
"scatterDistance": 15.0, "scatterDistance": 15.0,
"subsurfaceScatterFactor": 0.4300000071525574, "subsurfaceScatterFactor": 0.4300000071525574,
"thicknessMap": "Objects/Lucy/Lucy_thickness.tif", "thicknessMap": "Objects/Lucy/Lucy_thickness.tif",
@ -47,8 +47,7 @@
0.3182879388332367, 0.3182879388332367,
0.16388189792633058, 0.16388189792633058,
1.0 1.0
], ]
"useInfluenceMap": false
}, },
"wrinkleLayers": { "wrinkleLayers": {
"baseColorMap1": "TestData/Textures/cc0/Lava004_1K_Color.jpg", "baseColorMap1": "TestData/Textures/cc0/Lava004_1K_Color.jpg",

@ -159,7 +159,7 @@ namespace AtomToolsFramework
void AtomToolsDocumentSystemComponent::OnDocumentExternallyModified(const AZ::Uuid& documentId) void AtomToolsDocumentSystemComponent::OnDocumentExternallyModified(const AZ::Uuid& documentId)
{ {
m_documentIdsToReopen.insert(documentId); m_documentIdsWithExternalChanges.insert(documentId);
if (!AZ::TickBus::Handler::BusIsConnected()) if (!AZ::TickBus::Handler::BusIsConnected())
{ {
AZ::TickBus::Handler::BusConnect(); AZ::TickBus::Handler::BusConnect();
@ -168,7 +168,7 @@ namespace AtomToolsFramework
void AtomToolsDocumentSystemComponent::OnDocumentDependencyModified(const AZ::Uuid& documentId) void AtomToolsDocumentSystemComponent::OnDocumentDependencyModified(const AZ::Uuid& documentId)
{ {
m_documentIdsToReopen.insert(documentId); m_documentIdsWithDependencyChanges.insert(documentId);
if (!AZ::TickBus::Handler::BusIsConnected()) if (!AZ::TickBus::Handler::BusIsConnected())
{ {
AZ::TickBus::Handler::BusConnect(); AZ::TickBus::Handler::BusConnect();
@ -177,7 +177,7 @@ namespace AtomToolsFramework
void AtomToolsDocumentSystemComponent::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time) 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; AZStd::string documentPath;
AtomToolsDocumentRequestBus::EventResult(documentPath, documentId, &AtomToolsDocumentRequestBus::Events::GetAbsolutePath); AtomToolsDocumentRequestBus::EventResult(documentPath, documentId, &AtomToolsDocumentRequestBus::Events::GetAbsolutePath);
@ -191,6 +191,8 @@ namespace AtomToolsFramework
continue; continue;
} }
m_documentIdsWithDependencyChanges.erase(documentId);
AtomToolsFramework::TraceRecorder traceRecorder(m_maxMessageBoxLineCount); AtomToolsFramework::TraceRecorder traceRecorder(m_maxMessageBoxLineCount);
bool openResult = false; 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; AZStd::string documentPath;
AtomToolsDocumentRequestBus::EventResult(documentPath, documentId, &AtomToolsDocumentRequestBus::Events::GetAbsolutePath); AtomToolsDocumentRequestBus::EventResult(documentPath, documentId, &AtomToolsDocumentRequestBus::Events::GetAbsolutePath);
@ -231,8 +233,8 @@ namespace AtomToolsFramework
} }
} }
m_documentIdsToReopen.clear(); m_documentIdsWithDependencyChanges.clear();
m_documentIdsToReopen.clear(); m_documentIdsWithExternalChanges.clear();
AZ::TickBus::Handler::BusDisconnect(); AZ::TickBus::Handler::BusDisconnect();
} }

@ -85,8 +85,8 @@ namespace AtomToolsFramework
AZStd::intrusive_ptr<AtomToolsDocumentSystemSettings> m_settings; AZStd::intrusive_ptr<AtomToolsDocumentSystemSettings> m_settings;
AZStd::function<AtomToolsDocument*()> m_documentCreator; AZStd::function<AtomToolsDocument*()> m_documentCreator;
AZStd::unordered_map<AZ::Uuid, AZStd::shared_ptr<AtomToolsDocument>> m_documentMap; AZStd::unordered_map<AZ::Uuid, AZStd::shared_ptr<AtomToolsDocument>> m_documentMap;
AZStd::unordered_set<AZ::Uuid> m_documentIdsToRebuild; AZStd::unordered_set<AZ::Uuid> m_documentIdsWithExternalChanges;
AZStd::unordered_set<AZ::Uuid> m_documentIdsToReopen; AZStd::unordered_set<AZ::Uuid> m_documentIdsWithDependencyChanges;
const size_t m_maxMessageBoxLineCount = 15; const size_t m_maxMessageBoxLineCount = 15;
}; };
} // namespace AtomToolsFramework } // namespace AtomToolsFramework

@ -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 // ignore notifications caused by saving the open document
if (!m_saveTriggeredInternally) if (!m_saveTriggeredInternally)
{ {
AZ_TracePrintf("MaterialDocument", "Material document changed externally: '%s'.\n", m_absolutePath.c_str()); 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; m_saveTriggeredInternally = false;
} }
} else if (m_sourceDependencies.find(sourcePath) != m_sourceDependencies.end())
void MaterialDocument::OnAssetReloaded(AZ::Data::Asset<AZ::Data::AssetData> asset)
{
if (m_dependentAssetIds.find(asset->GetId()) != m_dependentAssetIds.end())
{ {
AZ_TracePrintf("MaterialDocument", "Material document dependency changed: '%s'.\n", m_absolutePath.c_str()); 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; return false;
} }
m_sourceAssetId = sourceAssetInfo.m_assetId;
m_relativePath = sourceAssetInfo.m_relativePath; m_relativePath = sourceAssetInfo.m_relativePath;
if (!AzFramework::StringFunc::Path::Normalize(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. // 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 // 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. // only thing concerned with assets or instances.
auto createResult = m_materialSourceData.CreateMaterialAsset(Uuid::CreateRandom(), m_absolutePath, true); auto materialAssetResult =
if (!createResult) 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()); AZ_Error("MaterialDocument", false, "Material asset could not be created from source data: '%s'.", m_absolutePath.c_str());
return false; return false;
} }
m_materialAsset = createResult.GetValue(); m_materialAsset = materialAssetResult.GetValue();
if (!m_materialAsset.IsReady()) if (!m_materialAsset.IsReady())
{ {
AZ_Error("MaterialDocument", false, "Material asset is not ready: '%s'.", m_absolutePath.c_str()); AZ_Error("MaterialDocument", false, "Material asset is not ready: '%s'.", m_absolutePath.c_str());
@ -743,28 +743,35 @@ namespace MaterialEditor
return false; 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<AZ::RPI::MaterialPropertyValue> parentPropertyValues = materialTypeAsset->GetDefaultPropertyValues(); AZStd::array_view<AZ::RPI::MaterialPropertyValue> parentPropertyValues = materialTypeAsset->GetDefaultPropertyValues();
AZ::Data::Asset<MaterialAsset> parentMaterialAsset; AZ::Data::Asset<MaterialAsset> parentMaterialAsset;
if (!m_materialSourceData.m_parentMaterial.empty()) if (!m_materialSourceData.m_parentMaterial.empty())
{ {
// There is a parent for this material AZ::RPI::MaterialSourceData parentMaterialSourceData;
auto parentMaterialResult = AssetUtils::LoadAsset<MaterialAsset>(m_absolutePath, m_materialSourceData.m_parentMaterial); const auto parentMaterialFilePath = AssetUtils::ResolvePathReference(m_absolutePath, m_materialSourceData.m_parentMaterial);
if (!parentMaterialResult) 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; return false;
} }
parentMaterialAsset = parentMaterialResult.GetValue(); const auto parentMaterialAssetIdResult = AssetUtils::MakeAssetId(parentMaterialFilePath, 0);
parentPropertyValues = parentMaterialAsset->GetPropertyValues(); 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 auto parentMaterialAssetResult = parentMaterialSourceData.CreateMaterialAssetFromSourceData(
m_dependentAssetIds.insert(parentMaterialAsset->GetId()); parentMaterialAssetIdResult.GetValue(), parentMaterialFilePath, true, true);
AZ::Data::AssetBus::MultiHandler::BusConnect(parentMaterialAsset->GetId()); 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 // 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() void MaterialDocument::Clear()
{ {
AZ::TickBus::Handler::BusDisconnect(); AZ::TickBus::Handler::BusDisconnect();
AZ::Data::AssetBus::MultiHandler::BusDisconnect();
AzToolsFramework::AssetSystemBus::Handler::BusDisconnect(); AzToolsFramework::AssetSystemBus::Handler::BusDisconnect();
m_materialAsset = {}; m_materialAsset = {};
m_materialInstance = {}; m_materialInstance = {};
m_absolutePath.clear(); m_absolutePath.clear();
m_relativePath.clear(); m_relativePath.clear();
m_sourceAssetId = {}; m_sourceDependencies.clear();
m_dependentAssetIds.clear();
m_saveTriggeredInternally = {}; m_saveTriggeredInternally = {};
m_compilePending = {}; m_compilePending = {};
m_properties.clear(); m_properties.clear();

@ -29,7 +29,6 @@ namespace MaterialEditor
: public AtomToolsFramework::AtomToolsDocument : public AtomToolsFramework::AtomToolsDocument
, public MaterialDocumentRequestBus::Handler , public MaterialDocumentRequestBus::Handler
, private AZ::TickBus::Handler , private AZ::TickBus::Handler
, private AZ::Data::AssetBus::MultiHandler
, private AzToolsFramework::AssetSystemBus::Handler , private AzToolsFramework::AssetSystemBus::Handler
{ {
public: public:
@ -105,11 +104,6 @@ namespace MaterialEditor
void SourceFileChanged(AZStd::string relativePath, AZStd::string scanFolder, AZ::Uuid sourceUUID) override; void SourceFileChanged(AZStd::string relativePath, AZStd::string scanFolder, AZ::Uuid sourceUUID) override;
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// AZ::Data::AssetBus::Router overrides...
void OnAssetReloaded(AZ::Data::Asset<AZ::Data::AssetData> asset) override;
//////////////////////////////////////////////////////////////////////////
bool SavePropertiesToSourceData(AZ::RPI::MaterialSourceData& sourceData, PropertyFilterFunction propertyFilter) const; bool SavePropertiesToSourceData(AZ::RPI::MaterialSourceData& sourceData, PropertyFilterFunction propertyFilter) const;
bool OpenInternal(AZStd::string_view loadPath); bool OpenInternal(AZStd::string_view loadPath);
@ -137,11 +131,8 @@ namespace MaterialEditor
// Material instance being edited // Material instance being edited
AZ::Data::Instance<AZ::RPI::Material> m_materialInstance; AZ::Data::Instance<AZ::RPI::Material> m_materialInstance;
// Asset used to open document
AZ::Data::AssetId m_sourceAssetId;
// Set of assets that can trigger a document reload // Set of assets that can trigger a document reload
AZStd::unordered_set<AZ::Data::AssetId> m_dependentAssetIds; AZStd::unordered_set<AZStd::string> m_sourceDependencies;
// Track if document saved itself last to skip external modification notification // Track if document saved itself last to skip external modification notification
bool m_saveTriggeredInternally = false; bool m_saveTriggeredInternally = false;

@ -40,6 +40,7 @@ namespace AZ
->Field("bakedCubeMapQualityLevel", &EditorReflectionProbeComponent::m_bakedCubeMapQualityLevel) ->Field("bakedCubeMapQualityLevel", &EditorReflectionProbeComponent::m_bakedCubeMapQualityLevel)
->Field("bakedCubeMapRelativePath", &EditorReflectionProbeComponent::m_bakedCubeMapRelativePath) ->Field("bakedCubeMapRelativePath", &EditorReflectionProbeComponent::m_bakedCubeMapRelativePath)
->Field("authoredCubeMapAsset", &EditorReflectionProbeComponent::m_authoredCubeMapAsset) ->Field("authoredCubeMapAsset", &EditorReflectionProbeComponent::m_authoredCubeMapAsset)
->Field("bakeExposure", &EditorReflectionProbeComponent::m_bakeExposure)
; ;
if (AZ::EditContext* editContext = serializeContext->GetEditContext()) if (AZ::EditContext* editContext = serializeContext->GetEditContext())
@ -62,6 +63,13 @@ namespace AZ
->Attribute(AZ::Edit::Attributes::ButtonText, "Bake Reflection Probe") ->Attribute(AZ::Edit::Attributes::ButtonText, "Bake Reflection Probe")
->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorReflectionProbeComponent::BakeReflectionProbe) ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorReflectionProbeComponent::BakeReflectionProbe)
->Attribute(AZ::Edit::Attributes::Visibility, &EditorReflectionProbeComponent::GetBakedCubemapVisibilitySetting) ->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") ->ClassElement(AZ::Edit::ClassElements::Group, "Cubemap")
->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->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") ->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) ->Attribute(AZ::Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly)
->DataElement(AZ::Edit::UIHandlers::CheckBox, &ReflectionProbeComponentConfig::m_showVisualization, "Show Visualization", "Show the reflection probe visualization sphere") ->DataElement(AZ::Edit::UIHandlers::CheckBox, &ReflectionProbeComponentConfig::m_showVisualization, "Show Visualization", "Show the reflection probe visualization sphere")
->Attribute(AZ::Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) ->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; return AZ::Edit::PropertyRefreshLevels::None;
} }
AZ::u32 EditorReflectionProbeComponent::OnBakeExposureChanged()
{
m_controller.SetBakeExposure(m_bakeExposure);
return AZ::Edit::PropertyRefreshLevels::None;
}
AZ::u32 EditorReflectionProbeComponent::GetBakedCubemapVisibilitySetting() AZ::u32 EditorReflectionProbeComponent::GetBakedCubemapVisibilitySetting()
{ {
// controls specific to baked cubemaps call this to determine their visibility // controls specific to baked cubemaps call this to determine their visibility

@ -55,6 +55,7 @@ namespace AZ
// change notifications // change notifications
AZ::u32 OnUseBakedCubemapChanged(); AZ::u32 OnUseBakedCubemapChanged();
AZ::u32 OnAuthoredCubemapChanged(); AZ::u32 OnAuthoredCubemapChanged();
AZ::u32 OnBakeExposureChanged();
// retrieves visibility for baked or authored cubemap controls // retrieves visibility for baked or authored cubemap controls
AZ::u32 GetBakedCubemapVisibilitySetting(); AZ::u32 GetBakedCubemapVisibilitySetting();
@ -77,6 +78,7 @@ namespace AZ
AZStd::string m_bakedCubeMapRelativePath; AZStd::string m_bakedCubeMapRelativePath;
Data::Asset<RPI::StreamingImageAsset> m_bakedCubeMapAsset; Data::Asset<RPI::StreamingImageAsset> m_bakedCubeMapAsset;
Data::Asset<RPI::StreamingImageAsset> m_authoredCubeMapAsset; Data::Asset<RPI::StreamingImageAsset> m_authoredCubeMapAsset;
float m_bakeExposure = 0.0f;
// flag indicating if a cubemap bake is currently in progress // flag indicating if a cubemap bake is currently in progress
AZStd::atomic_bool m_bakeInProgress = false; AZStd::atomic_bool m_bakeInProgress = false;

@ -35,7 +35,7 @@ namespace AZ
if (auto* serializeContext = azrtti_cast<SerializeContext*>(context)) if (auto* serializeContext = azrtti_cast<SerializeContext*>(context))
{ {
serializeContext->Class<ReflectionProbeComponentConfig>() serializeContext->Class<ReflectionProbeComponentConfig>()
->Version(0) ->Version(1)
->Field("OuterHeight", &ReflectionProbeComponentConfig::m_outerHeight) ->Field("OuterHeight", &ReflectionProbeComponentConfig::m_outerHeight)
->Field("OuterLength", &ReflectionProbeComponentConfig::m_outerLength) ->Field("OuterLength", &ReflectionProbeComponentConfig::m_outerLength)
->Field("OuterWidth", &ReflectionProbeComponentConfig::m_outerWidth) ->Field("OuterWidth", &ReflectionProbeComponentConfig::m_outerWidth)
@ -49,7 +49,9 @@ namespace AZ
->Field("AuthoredCubeMapAsset", &ReflectionProbeComponentConfig::m_authoredCubeMapAsset) ->Field("AuthoredCubeMapAsset", &ReflectionProbeComponentConfig::m_authoredCubeMapAsset)
->Field("EntityId", &ReflectionProbeComponentConfig::m_entityId) ->Field("EntityId", &ReflectionProbeComponentConfig::m_entityId)
->Field("UseParallaxCorrection", &ReflectionProbeComponentConfig::m_useParallaxCorrection) ->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(); cubeMapAsset.QueueLoad();
Data::AssetBus::MultiHandler::BusConnect(cubeMapAsset.GetId()); Data::AssetBus::MultiHandler::BusConnect(cubeMapAsset.GetId());
} }
// set cubemap render exposure
m_featureProcessor->SetRenderExposure(m_handle, m_configuration.m_renderExposure);
} }
void ReflectionProbeComponentController::Deactivate() void ReflectionProbeComponentController::Deactivate()
@ -284,6 +289,16 @@ namespace AZ
m_configuration.m_innerHeight = AZStd::min(m_configuration.m_innerHeight, m_configuration.m_outerHeight); 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) void ReflectionProbeComponentController::BakeReflectionProbe(BuildCubeMapCallback callback, const AZStd::string& relativePath)
{ {
if (!m_featureProcessor) if (!m_featureProcessor)

@ -68,6 +68,9 @@ namespace AZ
Data::Asset<RPI::StreamingImageAsset> m_bakedCubeMapAsset; Data::Asset<RPI::StreamingImageAsset> m_bakedCubeMapAsset;
Data::Asset<RPI::StreamingImageAsset> m_authoredCubeMapAsset; Data::Asset<RPI::StreamingImageAsset> m_authoredCubeMapAsset;
AZ::u64 m_entityId{ EntityId::InvalidEntityId }; AZ::u64 m_entityId{ EntityId::InvalidEntityId };
float m_renderExposure = 0.0f;
float m_bakeExposure = 0.0f;
}; };
class ReflectionProbeComponentController final class ReflectionProbeComponentController final
@ -99,6 +102,9 @@ namespace AZ
// returns the outer extent Aabb for this reflection // returns the outer extent Aabb for this reflection
AZ::Aabb GetAabb() const; 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 // initiate the reflection probe bake, invokes callback when complete
void BakeReflectionProbe(BuildCubeMapCallback callback, const AZStd::string& relativePath); void BakeReflectionProbe(BuildCubeMapCallback callback, const AZStd::string& relativePath);

@ -257,6 +257,7 @@ BOOL CUiAnimViewDialog::OnInitDialog()
m_wndSplitter->addWidget(m_wndDopeSheet); m_wndSplitter->addWidget(m_wndDopeSheet);
m_wndSplitter->setStretchFactor(0, 1); m_wndSplitter->setStretchFactor(0, 1);
m_wndSplitter->setStretchFactor(1, 10); m_wndSplitter->setStretchFactor(1, 10);
m_wndSplitter->setChildrenCollapsible(false);
l->addWidget(m_wndSplitter); l->addWidget(m_wndSplitter);
w->setLayout(l); w->setLayout(l);
setCentralWidget(w); setCentralWidget(w);
@ -283,6 +284,11 @@ BOOL CUiAnimViewDialog::OnInitDialog()
m_wndCurveEditorDock->setVisible(false); m_wndCurveEditorDock->setVisible(false);
m_wndCurveEditorDock->setEnabled(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(); InitSequences();
m_lazyInitDone = false; m_lazyInitDone = false;

@ -56,6 +56,7 @@ ShaderResourceGroup ObjectSrg : SRG_PerObject
float m_padding; float m_padding;
bool m_useReflectionProbe; bool m_useReflectionProbe;
bool m_useParallaxCorrection; bool m_useParallaxCorrection;
float m_exposure;
}; };
ReflectionProbeData m_reflectionProbeData; ReflectionProbeData m_reflectionProbeData;

@ -74,7 +74,7 @@ namespace Terrain
->DataElement( ->DataElement(
AZ::Edit::UIHandlers::Default, &TerrainSurfaceMaterialsListConfig::m_surfaceMaterials, 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_active = false;
surfaceMaterialMapping.m_materialAsset.QueueLoad(); 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()) 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_materialAsset.Release();
surfaceMaterialMapping.m_materialInstance.reset(); surfaceMaterialMapping.m_materialInstance.reset();
surfaceMaterialMapping.m_activeMaterialAssetId = AZ::Data::AssetId(); 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. // Don't disconnect from the AssetBus if this material is mapped more than once.
if (CountMaterialIDInstances(surfaceMaterialMapping.m_activeMaterialAssetId) == 1) 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(); surfaceMaterialMapping.m_activeMaterialAssetId = AZ::Data::AssetId();
@ -238,7 +238,7 @@ namespace Terrain
// All materials have been deactivated, stop listening for requests and notifications. // All materials have been deactivated, stop listening for requests and notifications.
m_cachedAabb = AZ::Aabb::CreateNull(); m_cachedAabb = AZ::Aabb::CreateNull();
LmbrCentral::ShapeComponentNotificationsBus::Handler::BusDisconnect(); LmbrCentral::ShapeComponentNotificationsBus::Handler::BusDisconnect();
TerrainAreaMaterialRequestBus::Handler::BusConnect(GetEntityId()); TerrainAreaMaterialRequestBus::Handler::BusDisconnect();
} }
} }

@ -53,7 +53,7 @@ namespace Terrain
class TerrainSurfaceMaterialsListComponent class TerrainSurfaceMaterialsListComponent
: public AZ::Component : public AZ::Component
, private TerrainAreaMaterialRequestBus::Handler , private TerrainAreaMaterialRequestBus::Handler
, private AZ::Data::AssetBus::Handler , private AZ::Data::AssetBus::MultiHandler
, private LmbrCentral::ShapeComponentNotificationsBus::Handler , private LmbrCentral::ShapeComponentNotificationsBus::Handler
{ {
public: public:

@ -6,15 +6,13 @@
* *
*/ */
#include <AzTest/AzTest.h>
#include <AzCore/Component/ComponentApplication.h> #include <AzCore/Component/ComponentApplication.h>
#include <AzCore/Component/TransformBus.h> #include <AzCore/Component/TransformBus.h>
#include <AzCore/Memory/MemoryComponent.h> #include <AzCore/Memory/MemoryComponent.h>
#include <AzFramework/Terrain/TerrainDataRequestBus.h>
#include <Components/TerrainLayerSpawnerComponent.h> #include <Components/TerrainLayerSpawnerComponent.h>
#include <LmbrCentral/Shape/BoxShapeComponentBus.h>
#include <AzTest/AzTest.h>
#include <Terrain/MockTerrain.h> #include <Terrain/MockTerrain.h>
#include <MockAxisAlignedBoxShapeComponent.h> #include <MockAxisAlignedBoxShapeComponent.h>
@ -23,21 +21,12 @@ using ::testing::NiceMock;
using ::testing::AtLeast; using ::testing::AtLeast;
using ::testing::_; using ::testing::_;
using ::testing::NiceMock;
using ::testing::AtLeast;
using ::testing::_;
class LayerSpawnerComponentTest class LayerSpawnerComponentTest
: public ::testing::Test : public ::testing::Test
{ {
protected: protected:
AZ::ComponentApplication m_app; AZ::ComponentApplication m_app;
AZStd::unique_ptr<AZ::Entity> m_entity;
Terrain::TerrainLayerSpawnerComponent* m_layerSpawnerComponent;
UnitTest::MockAxisAlignedBoxShapeComponent* m_shapeComponent;
AZStd::unique_ptr<NiceMock<UnitTest::MockTerrainSystemService>> m_terrainSystem;
void SetUp() override void SetUp() override
{ {
AZ::ComponentApplication::Descriptor appDesc; AZ::ComponentApplication::Descriptor appDesc;
@ -50,78 +39,86 @@ protected:
void TearDown() override void TearDown() override
{ {
m_entity.reset();
m_terrainSystem.reset();
m_app.Destroy(); m_app.Destroy();
} }
void CreateEntity() AZStd::unique_ptr<AZ::Entity> CreateEntity()
{ {
m_entity = AZStd::make_unique<AZ::Entity>(); auto entity = AZStd::make_unique<AZ::Entity>();
m_entity->Init(); entity->Init();
ASSERT_TRUE(m_entity); return entity;
}
void AddLayerSpawnerAndShapeComponentToEntity()
{
AddLayerSpawnerAndShapeComponentToEntity(Terrain::TerrainLayerSpawnerConfig());
} }
void AddLayerSpawnerAndShapeComponentToEntity(const Terrain::TerrainLayerSpawnerConfig& config) Terrain::TerrainLayerSpawnerComponent* AddLayerSpawnerToEntity(AZ::Entity* entity, const Terrain::TerrainLayerSpawnerConfig& config)
{ {
m_layerSpawnerComponent = m_entity->CreateComponent<Terrain::TerrainLayerSpawnerComponent>(config); auto layerSpawnerComponent = entity->CreateComponent<Terrain::TerrainLayerSpawnerComponent>(config);
m_app.RegisterComponentDescriptor(m_layerSpawnerComponent->CreateDescriptor()); m_app.RegisterComponentDescriptor(layerSpawnerComponent->CreateDescriptor());
m_shapeComponent = m_entity->CreateComponent<UnitTest::MockAxisAlignedBoxShapeComponent>(); return layerSpawnerComponent;
m_app.RegisterComponentDescriptor(m_shapeComponent->CreateDescriptor());
ASSERT_TRUE(m_layerSpawnerComponent);
ASSERT_TRUE(m_shapeComponent);
} }
void CreateMockTerrainSystem() UnitTest::MockAxisAlignedBoxShapeComponent* AddShapeComponentToEntity(AZ::Entity* entity)
{ {
m_terrainSystem = AZStd::make_unique<NiceMock<UnitTest::MockTerrainSystemService>>(); UnitTest::MockAxisAlignedBoxShapeComponent* shapeComponent = entity->CreateComponent<UnitTest::MockAxisAlignedBoxShapeComponent>();
m_app.RegisterComponentDescriptor(shapeComponent->CreateDescriptor());
return shapeComponent;
} }
}; };
TEST_F(LayerSpawnerComponentTest, ActivatEntityActivateSuccess) TEST_F(LayerSpawnerComponentTest, ActivateEntityWithoutShapeFails)
{ {
CreateEntity(); auto entity = CreateEntity();
AddLayerSpawnerAndShapeComponentToEntity();
AddLayerSpawnerToEntity(entity.get(), Terrain::TerrainLayerSpawnerConfig());
m_entity->Activate(); const AZ::Entity::DependencySortOutcome sortOutcome = entity->EvaluateDependenciesGetDetails();
EXPECT_EQ(m_entity->GetState(), AZ::Entity::State::Active); EXPECT_FALSE(sortOutcome.IsSuccess());
m_entity->Deactivate(); entity.reset();
}
TEST_F(LayerSpawnerComponentTest, ActivateEntityActivateSuccess)
{
auto entity = CreateEntity();
AddLayerSpawnerToEntity(entity.get(), Terrain::TerrainLayerSpawnerConfig());
AddShapeComponentToEntity(entity.get());
entity->Activate();
EXPECT_EQ(entity->GetState(), AZ::Entity::State::Active);
entity.reset();
} }
TEST_F(LayerSpawnerComponentTest, LayerSpawnerDefaultValuesCorrect) TEST_F(LayerSpawnerComponentTest, LayerSpawnerDefaultValuesCorrect)
{ {
CreateEntity(); auto entity = CreateEntity();
AddLayerSpawnerAndShapeComponentToEntity(); AddLayerSpawnerToEntity(entity.get(), Terrain::TerrainLayerSpawnerConfig());
AddShapeComponentToEntity(entity.get());
m_entity->Activate(); entity->Activate();
AZ::u32 priority = 999, layer = 999; 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(0, priority);
EXPECT_EQ(1, layer); EXPECT_EQ(1, layer);
bool useGroundPlane = false; 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); EXPECT_TRUE(useGroundPlane);
m_entity->Deactivate(); entity.reset();
} }
TEST_F(LayerSpawnerComponentTest, LayerSpawnerConfigValuesCorrect) TEST_F(LayerSpawnerComponentTest, LayerSpawnerConfigValuesCorrect)
{ {
CreateEntity(); auto entity = CreateEntity();
constexpr static AZ::u32 testPriority = 15; constexpr static AZ::u32 testPriority = 15;
constexpr static AZ::u32 testLayer = 0; constexpr static AZ::u32 testLayer = 0;
@ -131,12 +128,13 @@ TEST_F(LayerSpawnerComponentTest, LayerSpawnerConfigValuesCorrect)
config.m_priority = testPriority; config.m_priority = testPriority;
config.m_useGroundPlane = false; config.m_useGroundPlane = false;
AddLayerSpawnerAndShapeComponentToEntity(config); AddLayerSpawnerToEntity(entity.get(), config);
AddShapeComponentToEntity(entity.get());
m_entity->Activate(); entity->Activate();
AZ::u32 priority = 999, layer = 999; 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(testPriority, priority);
EXPECT_EQ(testLayer, layer); EXPECT_EQ(testLayer, layer);
@ -144,82 +142,86 @@ TEST_F(LayerSpawnerComponentTest, LayerSpawnerConfigValuesCorrect)
bool useGroundPlane = true; bool useGroundPlane = true;
Terrain::TerrainSpawnerRequestBus::EventResult( Terrain::TerrainSpawnerRequestBus::EventResult(
useGroundPlane, m_entity->GetId(), &Terrain::TerrainSpawnerRequestBus::Events::GetUseGroundPlane); useGroundPlane, entity->GetId(), &Terrain::TerrainSpawnerRequestBus::Events::GetUseGroundPlane);
EXPECT_FALSE(useGroundPlane); EXPECT_FALSE(useGroundPlane);
m_entity->Deactivate(); entity.reset();
} }
TEST_F(LayerSpawnerComponentTest, LayerSpawnerRegisterAreaUpdatesTerrainSystem) TEST_F(LayerSpawnerComponentTest, LayerSpawnerRegisterAreaUpdatesTerrainSystem)
{ {
CreateEntity(); auto entity = CreateEntity();
CreateMockTerrainSystem(); NiceMock<UnitTest::MockTerrainSystemService> terrainSystem;
// The Activate call should register the area. // 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) TEST_F(LayerSpawnerComponentTest, LayerSpawnerUnregisterAreaUpdatesTerrainSystem)
{ {
CreateEntity(); auto entity = CreateEntity();
CreateMockTerrainSystem(); NiceMock<UnitTest::MockTerrainSystemService> terrainSystem;
// The Deactivate call should unregister the area. // 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) TEST_F(LayerSpawnerComponentTest, LayerSpawnerTransformChangedUpdatesTerrainSystem)
{ {
CreateEntity(); auto entity = CreateEntity();
CreateMockTerrainSystem(); NiceMock<UnitTest::MockTerrainSystemService> terrainSystem;
// The TransformChanged call should refresh the area. // 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. // The component gets transform change notifications via the shape bus.
LmbrCentral::ShapeComponentNotificationsBus::Event( LmbrCentral::ShapeComponentNotificationsBus::Event(
m_entity->GetId(), &LmbrCentral::ShapeComponentNotificationsBus::Events::OnShapeChanged, entity->GetId(), &LmbrCentral::ShapeComponentNotificationsBus::Events::OnShapeChanged,
LmbrCentral::ShapeComponentNotifications::ShapeChangeReasons::TransformChanged); LmbrCentral::ShapeComponentNotifications::ShapeChangeReasons::TransformChanged);
m_entity->Deactivate(); entity.reset();
} }
TEST_F(LayerSpawnerComponentTest, LayerSpawnerShapeChangedUpdatesTerrainSystem) TEST_F(LayerSpawnerComponentTest, LayerSpawnerShapeChangedUpdatesTerrainSystem)
{ {
CreateEntity(); auto entity = CreateEntity();
CreateMockTerrainSystem(); NiceMock<UnitTest::MockTerrainSystemService> terrainSystem;
// The ShapeChanged call should refresh the area. // 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( LmbrCentral::ShapeComponentNotificationsBus::Event(
m_entity->GetId(), &LmbrCentral::ShapeComponentNotificationsBus::Events::OnShapeChanged, entity->GetId(), &LmbrCentral::ShapeComponentNotificationsBus::Events::OnShapeChanged,
LmbrCentral::ShapeComponentNotifications::ShapeChangeReasons::ShapeChanged); LmbrCentral::ShapeComponentNotifications::ShapeChangeReasons::ShapeChanged);
m_entity->Deactivate(); entity.reset();
} }

@ -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
}
}
}
}

@ -53,6 +53,8 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS)
BUILD_DEPENDENCIES BUILD_DEPENDENCIES
PUBLIC PUBLIC
Gem::${Name}.Editor.Static Gem::${Name}.Editor.Static
RUNTIME_DEPENDENCIES
Gem::QtForPython.Editor
) )
# By default, we will specify that the above target ${Name} would be used by # By default, we will specify that the above target ${Name} would be used by

@ -13,5 +13,8 @@
"${Name}" "${Name}"
], ],
"icon_path": "preview.png", "icon_path": "preview.png",
"requirements": "" "requirements": "",
"dependencies": [
"QtForPython"
]
} }

@ -16,7 +16,7 @@ EMPTY_JSON = readJSON text: '{}'
ENGINE_REPOSITORY_NAME = 'o3de' ENGINE_REPOSITORY_NAME = 'o3de'
// Branches with build snapshots // 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 snapshot (for use with 'SNAPSHOT' pipeline paramater)
BUILD_SNAPSHOTS_WITH_EMPTY = BUILD_SNAPSHOTS + '' 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) { def GetRunningPipelineName(JENKINS_JOB_NAME) {
// If the job name has an underscore // If the job name has an underscore
def job_parts = JENKINS_JOB_NAME.tokenize('/')[0].tokenize('_') def job_parts = JENKINS_JOB_NAME.tokenize('/')[0].tokenize('_')
@ -277,9 +281,7 @@ def HandleDriveMount(String snapshot, String repositoryName, String projectName,
if(recreateVolume) { 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) 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) { if(env.IS_UNIX) {
sh label: 'Setting volume\'s ownership', 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) { def PostBuildCommonSteps(String workspace, boolean mount = true) {
echo 'Starting post-build common steps...' 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) { def CreateTeardownStage(Map environmentVars) {
return { return {
stage('Teardown') { 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() 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 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.value.steps.each { build_step ->
build_job_name = 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 { try {
CreateBuildStage(pipelineConfig, platform.key, build_step, envVars).call() 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 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 // All other errors will be raised outside the retry block
currentResult = envVars['ON_FAILURE_MARK'] ?: 'FAILURE' currentResult = envVars['ON_FAILURE_MARK'] ?: 'FAILURE'
currentException = e.toString() currentException = e.toString()
@ -770,6 +806,7 @@ try {
platform.value.build_types.each { build_job -> platform.value.build_types.each { build_job ->
if (IsJobEnabled(branchName, build_job, pipelineName, platform.key)) { // User can filter jobs, jobs are tagged by pipeline 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) 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 envVars['JOB_NAME'] = "${branchName}_${platform.key}_${build_job.key}" // backwards compatibility, some scripts rely on this
someBuildHappened = true someBuildHappened = true

Loading…
Cancel
Save