Merge branch 'stabilization/2106' of https://github.com/aws-lumberyard/o3de into Network/olex/stab/fix_player_removal_on_disconnect
This commit is contained in:
@@ -1,38 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
# its licensors.
|
||||
#
|
||||
# For complete copyright and license terms please see the LICENSE at the root of this
|
||||
# distribution (the "License"). All use of this software is governed by the License,
|
||||
# or, if provided, by the license below or the license accompanying this file. Do not
|
||||
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
#
|
||||
import os
|
||||
|
||||
os.chdir('qml')
|
||||
startDir = os.getcwd()
|
||||
|
||||
# since it's a .exe file it will only work on windows, but we may as well
|
||||
# construct the path in a platform-independent way.
|
||||
lreleaseCmd = os.path.join(startDir, '..', '..', '..',
|
||||
'Code', 'SDKs', 'Qt', 'x64', 'bin', 'lrelease.exe ')
|
||||
|
||||
print(startDir)
|
||||
|
||||
# Korean, Japanese and Simplified Chinese
|
||||
targetLanguages = ['ko', 'ja', 'zh_CN']
|
||||
|
||||
for lang in targetLanguages:
|
||||
os.chdir(startDir)
|
||||
tgtLang = '-target-language ' + lang
|
||||
os.system(lreleaseCmd + 'this_' + lang + '.ts')
|
||||
|
||||
for fileName in os.listdir():
|
||||
if not fileName.endswith(".ts"):
|
||||
continue
|
||||
|
||||
os.system(lreleaseCmd + ' ' + fileName)
|
||||
|
||||
print(('Finished processing: ' + fileName))
|
||||
+1
-1
@@ -112,7 +112,7 @@ def remove_file(file_path: str) -> None:
|
||||
@pytest.mark.parametrize('project', ['AutomatedTesting'])
|
||||
@pytest.mark.parametrize('level', ['AWS/Metrics'])
|
||||
@pytest.mark.parametrize('feature_name', [AWS_METRICS_FEATURE_NAME])
|
||||
@pytest.mark.parametrize('resource_mappings_filename', ['aws_resource_mappings.json'])
|
||||
@pytest.mark.parametrize('resource_mappings_filename', ['default_aws_resource_mappings.json'])
|
||||
@pytest.mark.parametrize('profile_name', ['AWSAutomationTest'])
|
||||
@pytest.mark.parametrize('region_name', ['us-west-2'])
|
||||
@pytest.mark.parametrize('assume_role_arn', ['arn:aws:iam::645075835648:role/o3de-automation-tests'])
|
||||
|
||||
+1
-1
@@ -36,7 +36,7 @@ logger = logging.getLogger(__name__)
|
||||
@pytest.mark.usefixtures('cdk')
|
||||
@pytest.mark.parametrize('feature_name', [AWS_CLIENT_AUTH_FEATURE_NAME])
|
||||
@pytest.mark.usefixtures('resource_mappings')
|
||||
@pytest.mark.parametrize('resource_mappings_filename', ['aws_resource_mappings.json'])
|
||||
@pytest.mark.parametrize('resource_mappings_filename', ['default_aws_resource_mappings.json'])
|
||||
@pytest.mark.usefixtures('aws_utils')
|
||||
@pytest.mark.parametrize('region_name', ['us-west-2'])
|
||||
@pytest.mark.parametrize('assume_role_arn', ['arn:aws:iam::645075835648:role/o3de-automation-tests'])
|
||||
|
||||
@@ -35,7 +35,7 @@ logger = logging.getLogger(__name__)
|
||||
@pytest.mark.usefixtures('cdk')
|
||||
@pytest.mark.parametrize('feature_name', [AWS_CLIENT_AUTH_FEATURE_NAME])
|
||||
@pytest.mark.usefixtures('resource_mappings')
|
||||
@pytest.mark.parametrize('resource_mappings_filename', ['aws_resource_mappings.json'])
|
||||
@pytest.mark.parametrize('resource_mappings_filename', ['default_aws_resource_mappings.json'])
|
||||
@pytest.mark.usefixtures('aws_utils')
|
||||
@pytest.mark.parametrize('region_name', ['us-west-2'])
|
||||
@pytest.mark.parametrize('assume_role_arn', ['arn:aws:iam::645075835648:role/o3de-automation-tests'])
|
||||
|
||||
@@ -15,7 +15,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_
|
||||
TEST_SUITE main
|
||||
TEST_SERIAL
|
||||
PATH ${CMAKE_CURRENT_LIST_DIR}
|
||||
PYTEST_MARKS "not SUITE_sandbox and not SUITE_periodic and not SUITE_benchmark"
|
||||
PYTEST_MARKS "SUITE_main and not REQUIRES_gpu"
|
||||
TIMEOUT 1500
|
||||
RUNTIME_DEPENDENCIES
|
||||
Legacy::Editor
|
||||
@@ -30,7 +30,23 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_
|
||||
TEST_SUITE periodic
|
||||
TEST_SERIAL
|
||||
PATH ${CMAKE_CURRENT_LIST_DIR}
|
||||
PYTEST_MARKS "SUITE_periodic"
|
||||
PYTEST_MARKS "SUITE_periodic and not REQUIRES_gpu"
|
||||
TIMEOUT 1500
|
||||
RUNTIME_DEPENDENCIES
|
||||
Legacy::Editor
|
||||
AZ::AssetProcessor
|
||||
AutomatedTesting.Assets
|
||||
COMPONENT
|
||||
Editor
|
||||
)
|
||||
|
||||
ly_add_pytest(
|
||||
NAME AutomatedTesting::EditorTests_Main_GPU
|
||||
TEST_SUITE main
|
||||
TEST_SERIAL
|
||||
TEST_REQUIRES gpu
|
||||
PATH ${CMAKE_CURRENT_LIST_DIR}
|
||||
PYTEST_MARKS "SUITE_main and REQUIRES_gpu"
|
||||
TIMEOUT 1500
|
||||
RUNTIME_DEPENDENCIES
|
||||
Legacy::Editor
|
||||
|
||||
@@ -66,3 +66,35 @@ class TestBasicEditorWorkflows(object):
|
||||
timeout=log_monitor_timeout,
|
||||
auto_test_mode=False
|
||||
)
|
||||
|
||||
@pytest.mark.test_case_id("C6351273", "C6384955", "C16929880", "C15167490", "C15167491")
|
||||
@pytest.mark.SUITE_main
|
||||
@pytest.mark.REQUIRES_gpu
|
||||
def test_BasicEditorWorkflows_GPU_LevelEntityComponentCRUD(self, request, editor, level, launcher_platform):
|
||||
|
||||
# Skip test if running against Debug build
|
||||
if "debug" in internal_plugin.build_directory:
|
||||
pytest.skip("Does not execute against debug builds.")
|
||||
|
||||
expected_lines = [
|
||||
"Create and load new level: True",
|
||||
"New entity creation: True",
|
||||
"Create entity hierarchy: True",
|
||||
"Add component: True",
|
||||
"Component update: True",
|
||||
"Remove component: True",
|
||||
"Save and Export: True",
|
||||
"BasicEditorWorkflows_LevelEntityComponent: result=SUCCESS",
|
||||
]
|
||||
|
||||
hydra.launch_and_validate_results(
|
||||
request,
|
||||
test_directory,
|
||||
editor,
|
||||
"BasicEditorWorkflows_LevelEntityComponentCRUD.py",
|
||||
expected_lines,
|
||||
cfg_args=[level],
|
||||
timeout=log_monitor_timeout,
|
||||
auto_test_mode=False,
|
||||
null_renderer=False
|
||||
)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"AWSCore":
|
||||
{
|
||||
"ProfileName": "AWSAutomationTest",
|
||||
"ResourceMappingConfigFileName": "aws_resource_mappings.json"
|
||||
"ResourceMappingConfigFileName": "default_aws_resource_mappings.json"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -44,8 +44,7 @@ namespace AZ::IO
|
||||
//! make adjustments. For the most optimal performance align read buffers to the physicalSectorSize.
|
||||
u8 m_enableUnbufferedReads : 1;
|
||||
//! Globally enable file sharing. This allows files to used outside AZ::IO::Streamer, including other applications
|
||||
//! while in use by AZ::IO::Streamer. File sharing can negatively impact performance and is recommended for
|
||||
//! development only.
|
||||
//! while in use by AZ::IO::Streamer.
|
||||
u8 m_enableSharing : 1;
|
||||
//! If true, only information that's explicitly requested or issues are reported. If false, status information
|
||||
//! such as when drives are created and destroyed is reported as well.
|
||||
|
||||
+1
-1
@@ -312,7 +312,7 @@ namespace AZ::IO
|
||||
{
|
||||
if (reportHardware)
|
||||
{
|
||||
AZ_Printf("Streamer", "Skipping drive '%s' because to no paths make use of it.\n", driveIt);
|
||||
AZ_Printf("Streamer", "Skipping drive '%s' because no paths make use of it.\n", driveIt);
|
||||
}
|
||||
while (*driveIt++);
|
||||
continue;
|
||||
|
||||
+141
@@ -46,6 +46,7 @@ namespace AZ::IO
|
||||
options.m_hasSeekPenalty = HasSeekPenalty;
|
||||
options.m_enableUnbufferedReads = TestEnableUnbufferReads;
|
||||
options.m_enableSharing = TestEnableSharedReads;
|
||||
options.m_minimalReporting = true;
|
||||
|
||||
return StorageDriveWin({ "c:/" }, TestMaxFileHandles, TestMaxMetaDataEntries, TestPhysicalSectorSize,
|
||||
TestLogicalSectorSize, TestMaxIOChannels, TestOverCommit, options);
|
||||
@@ -151,6 +152,7 @@ namespace AZ::IO
|
||||
m_configurationOptions.m_hasSeekPenalty = HasSeekPenalty;
|
||||
m_configurationOptions.m_enableUnbufferedReads = TestEnableUnbufferReads;
|
||||
m_configurationOptions.m_enableSharing = TestEnableSharedReads;
|
||||
m_configurationOptions.m_minimalReporting = true;
|
||||
|
||||
m_storageDriveWin = AZStd::make_shared<AZ::IO::StorageDriveWin>(AZStd::vector<AZStd::string_view>{drive}, TestMaxFileHandles,
|
||||
TestMaxMetaDataEntries, TestPhysicalSectorSize, TestLogicalSectorSize, TestMaxIOChannels, overCommit, m_configurationOptions);
|
||||
@@ -1148,3 +1150,142 @@ namespace AZ::IO
|
||||
azfree(buffers[numRequests - 1]);
|
||||
}
|
||||
} // namespace AZ::IO
|
||||
|
||||
#if defined(HAVE_BENCHMARK)
|
||||
|
||||
#include <benchmark/benchmark.h>
|
||||
|
||||
namespace Benchmark
|
||||
{
|
||||
class StorageDriveWindowsFixture : public benchmark::Fixture
|
||||
{
|
||||
public:
|
||||
constexpr static char* TestFileName = "StreamerBenchmark.bin";
|
||||
constexpr static size_t FileSize = 64_mib;
|
||||
|
||||
void SetupStreamer(bool enableFileSharing)
|
||||
{
|
||||
using namespace AZ::IO;
|
||||
|
||||
m_fileIO = new UnitTest::TestFileIOBase();
|
||||
m_previousFileIO = AZ::IO::FileIOBase::GetInstance();
|
||||
AZ::IO::FileIOBase::SetInstance(nullptr);
|
||||
AZ::IO::FileIOBase::SetInstance(m_fileIO);
|
||||
|
||||
SystemFile file;
|
||||
file.Open(TestFileName, SystemFile::OpenMode::SF_OPEN_CREATE | SystemFile::OpenMode::SF_OPEN_READ_WRITE);
|
||||
AZStd::unique_ptr<char[]> buffer(new char[FileSize]);
|
||||
::memset(buffer.get(), 'c', FileSize);
|
||||
|
||||
file.Write(buffer.get(), FileSize);
|
||||
file.Close();
|
||||
|
||||
AZStd::optional<AZ::IO::FixedMaxPathString> absolutePath = AZ::Utils::ConvertToAbsolutePath(TestFileName);
|
||||
if (absolutePath.has_value())
|
||||
{
|
||||
AZStd::string drive;
|
||||
AZ::StringFunc::Path::GetDrive(absolutePath->c_str(), drive);
|
||||
|
||||
m_absolutePath = *absolutePath;
|
||||
|
||||
StorageDriveWin::ConstructionOptions options;
|
||||
options.m_hasSeekPenalty = false;
|
||||
options.m_enableUnbufferedReads = true; // Leave this on otherwise repeated loads will be using the Windows cache instead.
|
||||
options.m_enableSharing = enableFileSharing;
|
||||
options.m_minimalReporting = true;
|
||||
AZStd::shared_ptr<StreamStackEntry> storageDriveWin =
|
||||
AZStd::make_shared<StorageDriveWin>(AZStd::vector<AZStd::string_view>{ drive }, 32, 32, 4_kib, 512, 8, 0, options);
|
||||
|
||||
AZStd::unique_ptr<Scheduler> stack = AZStd::make_unique<Scheduler>(AZStd::move(storageDriveWin));
|
||||
m_streamer = aznew Streamer(AZStd::thread_desc{}, AZStd::move(stack));
|
||||
}
|
||||
}
|
||||
|
||||
void TearDown([[maybe_unused]] const ::benchmark::State& state) override
|
||||
{
|
||||
using namespace AZ::IO;
|
||||
|
||||
AZStd::string temp;
|
||||
m_absolutePath.swap(temp);
|
||||
|
||||
delete m_streamer;
|
||||
|
||||
SystemFile::Delete(TestFileName);
|
||||
|
||||
AZ::IO::FileIOBase::SetInstance(nullptr);
|
||||
AZ::IO::FileIOBase::SetInstance(m_previousFileIO);
|
||||
delete m_fileIO;
|
||||
}
|
||||
|
||||
void RepeatedlyReadFile(benchmark::State& state)
|
||||
{
|
||||
using namespace AZ::IO;
|
||||
using namespace AZStd::chrono;
|
||||
|
||||
AZStd::unique_ptr<char[]> buffer(new char[FileSize]);
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
AZStd::binary_semaphore waitForReads;
|
||||
AZStd::atomic<system_clock::time_point> end;
|
||||
auto callback = [&end, &waitForReads]([[maybe_unused]] FileRequestHandle request)
|
||||
{
|
||||
benchmark::DoNotOptimize(end = high_resolution_clock::now());
|
||||
waitForReads.release();
|
||||
};
|
||||
|
||||
FileRequestPtr request = m_streamer->Read(m_absolutePath, buffer.get(), state.range(0), state.range(0));
|
||||
m_streamer->SetRequestCompleteCallback(request, callback);
|
||||
|
||||
system_clock::time_point start;
|
||||
benchmark::DoNotOptimize(start = high_resolution_clock::now());
|
||||
m_streamer->QueueRequest(request);
|
||||
|
||||
waitForReads.try_acquire_for(AZStd::chrono::seconds(5));
|
||||
auto durationInSeconds = duration_cast<duration<double>>(end.load() - start);
|
||||
|
||||
state.SetIterationTime(durationInSeconds.count());
|
||||
|
||||
m_streamer->QueueRequest(m_streamer->FlushCaches());
|
||||
}
|
||||
}
|
||||
|
||||
AZStd::string m_absolutePath;
|
||||
AZ::IO::Streamer* m_streamer{};
|
||||
AZ::IO::FileIOBase* m_previousFileIO{};
|
||||
UnitTest::TestFileIOBase* m_fileIO{};
|
||||
};
|
||||
|
||||
BENCHMARK_DEFINE_F(StorageDriveWindowsFixture, ReadsBaseline)(benchmark::State& state)
|
||||
{
|
||||
constexpr bool EnableFileSharing = false;
|
||||
SetupStreamer(EnableFileSharing);
|
||||
RepeatedlyReadFile(state);
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(StorageDriveWindowsFixture, ReadsWithFileReadSharingEnabled)(benchmark::State& state)
|
||||
{
|
||||
using namespace AZ::IO;
|
||||
|
||||
constexpr bool EnableFileSharing = true;
|
||||
SetupStreamer(EnableFileSharing);
|
||||
RepeatedlyReadFile(state);
|
||||
}
|
||||
|
||||
// For these benchmarks the CPU stat doesn't provide useful information because it uses GetThreadTimes on Window but since the main
|
||||
// thread is mostly sleeping while waiting for the read on the Streamer thread to complete this will report values (close to) zero.
|
||||
|
||||
BENCHMARK_REGISTER_F(StorageDriveWindowsFixture, ReadsBaseline)
|
||||
->RangeMultiplier(8)
|
||||
->Range(1024, 64_mib)
|
||||
->UseManualTime()
|
||||
->Unit(benchmark::kMillisecond);
|
||||
|
||||
BENCHMARK_REGISTER_F(StorageDriveWindowsFixture, ReadsWithFileReadSharingEnabled)
|
||||
->RangeMultiplier(8)
|
||||
->Range(1024, 64_mib)
|
||||
->UseManualTime()
|
||||
->Unit(benchmark::kMillisecond);
|
||||
|
||||
} // namespace Benchmark
|
||||
#endif // HAVE_BENCHMARK
|
||||
|
||||
@@ -31,5 +31,6 @@
|
||||
<file>CarrotArrowDown.svg</file>
|
||||
<file>Summary.svg</file>
|
||||
<file>WindowClose.svg</file>
|
||||
<file>Warning.svg</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="24" height="24" fill="#444444"/>
|
||||
<rect x="10" y="6" width="4" height="15" fill="black"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 2L22 22H2L12 2ZM13 20V18H11V20H13ZM13 7H11V16.0862H13V7Z" fill="#F0C32D"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 333 B |
@@ -206,7 +206,11 @@ namespace O3DE::ProjectManager
|
||||
PythonBindingsInterface::Get()->AddProject(projectInfo.m_path);
|
||||
|
||||
#ifdef TEMPLATE_GEM_CONFIGURATION_ENABLED
|
||||
m_gemCatalogScreen->EnableDisableGemsForProject(projectInfo.m_path);
|
||||
if (!m_gemCatalogScreen->EnableDisableGemsForProject(projectInfo.m_path))
|
||||
{
|
||||
QMessageBox::critical(this, tr("Failed to configure gems"), tr("Failed to configure gems for template."));
|
||||
return;
|
||||
}
|
||||
#endif // TEMPLATE_GEM_CONFIGURATION_ENABLED
|
||||
|
||||
projectInfo.m_needsBuild = true;
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
#include <QLineEdit>
|
||||
#include <QMessageBox>
|
||||
#include <FormLineEditWidget.h>
|
||||
#include <FormBrowseEditWidget.h>
|
||||
#include <FormFolderBrowseEditWidget.h>
|
||||
#include <PythonBindingsInterface.h>
|
||||
#include <PathValidator.h>
|
||||
|
||||
@@ -46,28 +46,28 @@ namespace O3DE::ProjectManager
|
||||
m_engineVersion->lineEdit()->setReadOnly(true);
|
||||
layout->addWidget(m_engineVersion);
|
||||
|
||||
m_thirdParty = new FormBrowseEditWidget(tr("3rd Party Software Folder"), engineInfo.m_thirdPartyPath, this);
|
||||
m_thirdParty = new FormFolderBrowseEditWidget(tr("3rd Party Software Folder"), engineInfo.m_thirdPartyPath, this);
|
||||
m_thirdParty->lineEdit()->setValidator(new PathValidator(PathValidator::PathMode::ExistingFolder, this));
|
||||
m_thirdParty->lineEdit()->setReadOnly(true);
|
||||
m_thirdParty->setErrorLabelText(tr("Please provide a valid path to a folder that exists"));
|
||||
connect(m_thirdParty->lineEdit(), &QLineEdit::textChanged, this, &EngineSettingsScreen::OnTextChanged);
|
||||
layout->addWidget(m_thirdParty);
|
||||
|
||||
m_defaultProjects = new FormBrowseEditWidget(tr("Default Projects Folder"), engineInfo.m_defaultProjectsFolder, this);
|
||||
m_defaultProjects = new FormFolderBrowseEditWidget(tr("Default Projects Folder"), engineInfo.m_defaultProjectsFolder, this);
|
||||
m_defaultProjects->lineEdit()->setValidator(new PathValidator(PathValidator::PathMode::ExistingFolder, this));
|
||||
m_defaultProjects->lineEdit()->setReadOnly(true);
|
||||
m_defaultProjects->setErrorLabelText(tr("Please provide a valid path to a folder that exists"));
|
||||
connect(m_defaultProjects->lineEdit(), &QLineEdit::textChanged, this, &EngineSettingsScreen::OnTextChanged);
|
||||
layout->addWidget(m_defaultProjects);
|
||||
|
||||
m_defaultGems = new FormBrowseEditWidget(tr("Default Gems Folder"), engineInfo.m_defaultGemsFolder, this);
|
||||
m_defaultGems = new FormFolderBrowseEditWidget(tr("Default Gems Folder"), engineInfo.m_defaultGemsFolder, this);
|
||||
m_defaultGems->lineEdit()->setValidator(new PathValidator(PathValidator::PathMode::ExistingFolder, this));
|
||||
m_defaultGems->lineEdit()->setReadOnly(true);
|
||||
m_defaultGems->setErrorLabelText(tr("Please provide a valid path to a folder that exists"));
|
||||
connect(m_defaultGems->lineEdit(), &QLineEdit::textChanged, this, &EngineSettingsScreen::OnTextChanged);
|
||||
layout->addWidget(m_defaultGems);
|
||||
|
||||
m_defaultProjectTemplates = new FormBrowseEditWidget(tr("Default Project Templates Folder"), engineInfo.m_defaultTemplatesFolder, this);
|
||||
m_defaultProjectTemplates = new FormFolderBrowseEditWidget(tr("Default Project Templates Folder"), engineInfo.m_defaultTemplatesFolder, this);
|
||||
m_defaultProjectTemplates->lineEdit()->setValidator(new PathValidator(PathValidator::PathMode::ExistingFolder, this));
|
||||
m_defaultProjectTemplates->lineEdit()->setReadOnly(true);
|
||||
m_defaultProjectTemplates->setErrorLabelText(tr("Please provide a valid path to a folder that exists"));
|
||||
|
||||
@@ -11,13 +11,9 @@
|
||||
*/
|
||||
|
||||
#include <FormBrowseEditWidget.h>
|
||||
#include <AzQtComponents/Components/StyledLineEdit.h>
|
||||
|
||||
#include <QPushButton>
|
||||
#include <QHBoxLayout>
|
||||
#include <QFileDialog>
|
||||
#include <QLineEdit>
|
||||
#include <QStandardPaths>
|
||||
#include <QIcon>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
@@ -30,20 +26,4 @@ namespace O3DE::ProjectManager
|
||||
connect(browseButton, &QPushButton::pressed, this, &FormBrowseEditWidget::HandleBrowseButton);
|
||||
m_frameLayout->addWidget(browseButton);
|
||||
}
|
||||
|
||||
void FormBrowseEditWidget::HandleBrowseButton()
|
||||
{
|
||||
QString defaultPath = m_lineEdit->text();
|
||||
if (defaultPath.isEmpty())
|
||||
{
|
||||
defaultPath = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
|
||||
}
|
||||
|
||||
QString directory = QDir::toNativeSeparators(QFileDialog::getExistingDirectory(this, tr("Browse"), defaultPath));
|
||||
if (!directory.isEmpty())
|
||||
{
|
||||
m_lineEdit->setText(directory);
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace O3DE::ProjectManager
|
||||
explicit FormBrowseEditWidget(const QString& labelText, const QString& valueText = "", QWidget* parent = nullptr);
|
||||
~FormBrowseEditWidget() = default;
|
||||
|
||||
private slots:
|
||||
void HandleBrowseButton();
|
||||
protected slots:
|
||||
virtual void HandleBrowseButton() = 0;
|
||||
};
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <FormFolderBrowseEditWidget.h>
|
||||
#include <AzQtComponents/Components/StyledLineEdit.h>
|
||||
|
||||
#include <QFileDialog>
|
||||
#include <QLineEdit>
|
||||
#include <QStandardPaths>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
FormFolderBrowseEditWidget::FormFolderBrowseEditWidget(const QString& labelText, const QString& valueText, QWidget* parent)
|
||||
: FormBrowseEditWidget(labelText, valueText, parent)
|
||||
{
|
||||
}
|
||||
|
||||
void FormFolderBrowseEditWidget::HandleBrowseButton()
|
||||
{
|
||||
QString defaultPath = m_lineEdit->text();
|
||||
if (defaultPath.isEmpty())
|
||||
{
|
||||
defaultPath = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
|
||||
}
|
||||
|
||||
QString directory = QDir::toNativeSeparators(QFileDialog::getExistingDirectory(this, tr("Browse"), defaultPath));
|
||||
if (!directory.isEmpty())
|
||||
{
|
||||
m_lineEdit->setText(directory);
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace O3DE::ProjectManager
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(Q_MOC_RUN)
|
||||
#include <FormBrowseEditWidget.h>
|
||||
#endif
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
class FormFolderBrowseEditWidget
|
||||
: public FormBrowseEditWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit FormFolderBrowseEditWidget(const QString& labelText, const QString& valueText = "", QWidget* parent = nullptr);
|
||||
~FormFolderBrowseEditWidget() = default;
|
||||
|
||||
protected:
|
||||
void HandleBrowseButton() override;
|
||||
};
|
||||
} // namespace O3DE::ProjectManager
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <FormImageBrowseEditWidget.h>
|
||||
#include <AzQtComponents/Components/StyledLineEdit.h>
|
||||
|
||||
#include <QFileDialog>
|
||||
#include <QLineEdit>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
FormImageBrowseEditWidget::FormImageBrowseEditWidget(const QString& labelText, const QString& valueText, QWidget* parent)
|
||||
: FormBrowseEditWidget(labelText, valueText, parent)
|
||||
{
|
||||
}
|
||||
|
||||
void FormImageBrowseEditWidget::HandleBrowseButton()
|
||||
{
|
||||
QString file = QDir::toNativeSeparators(QFileDialog::getOpenFileName(
|
||||
this, tr("Select Image"), m_lineEdit->text(), tr("PNG (*.png)")));
|
||||
if (!file.isEmpty())
|
||||
{
|
||||
m_lineEdit->setText(file);
|
||||
}
|
||||
}
|
||||
} // namespace O3DE::ProjectManager
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(Q_MOC_RUN)
|
||||
#include <FormBrowseEditWidget.h>
|
||||
#endif
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
class FormImageBrowseEditWidget
|
||||
: public FormBrowseEditWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit FormImageBrowseEditWidget(const QString& labelText, const QString& valueText = "", QWidget* parent = nullptr);
|
||||
~FormImageBrowseEditWidget() = default;
|
||||
|
||||
protected:
|
||||
void HandleBrowseButton() override;
|
||||
};
|
||||
} // namespace O3DE::ProjectManager
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <PythonBindingsInterface.h>
|
||||
#include <GemCatalog/GemListHeaderWidget.h>
|
||||
#include <GemCatalog/GemSortFilterProxyModel.h>
|
||||
#include <GemCatalog/GemRequirementDialog.h>
|
||||
#include <QVBoxLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QPushButton>
|
||||
@@ -144,12 +145,23 @@ namespace O3DE::ProjectManager
|
||||
}
|
||||
}
|
||||
|
||||
void GemCatalogScreen::EnableDisableGemsForProject(const QString& projectPath)
|
||||
bool GemCatalogScreen::EnableDisableGemsForProject(const QString& projectPath)
|
||||
{
|
||||
IPythonBindings* pythonBindings = PythonBindingsInterface::Get();
|
||||
QVector<QModelIndex> toBeAdded = m_gemModel->GatherGemsToBeAdded();
|
||||
QVector<QModelIndex> toBeRemoved = m_gemModel->GatherGemsToBeRemoved();
|
||||
|
||||
if (m_gemModel->DoGemsToBeAddedHaveRequirements())
|
||||
{
|
||||
GemRequirementDialog* confirmRequirementsDialog = new GemRequirementDialog(m_gemModel, toBeAdded, this);
|
||||
confirmRequirementsDialog->exec();
|
||||
|
||||
if (confirmRequirementsDialog->GetButtonResult() != QDialogButtonBox::ApplyRole)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (const QModelIndex& modelIndex : toBeAdded)
|
||||
{
|
||||
const QString gemPath = GemModel::GetPath(modelIndex);
|
||||
@@ -158,6 +170,8 @@ namespace O3DE::ProjectManager
|
||||
{
|
||||
QMessageBox::critical(nullptr, "Operation failed",
|
||||
QString("Cannot add gem %1 to project.\n\nError:\n%2").arg(GemModel::GetName(modelIndex), result.GetError().c_str()));
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,8 +183,12 @@ namespace O3DE::ProjectManager
|
||||
{
|
||||
QMessageBox::critical(nullptr, "Operation failed",
|
||||
QString("Cannot remove gem %1 from project.\n\nError:\n%2").arg(GemModel::GetName(modelIndex), result.GetError().c_str()));
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ProjectManagerScreen GemCatalogScreen::GetScreenEnum()
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace O3DE::ProjectManager
|
||||
ProjectManagerScreen GetScreenEnum() override;
|
||||
|
||||
void ReinitForProject(const QString& projectPath, bool isNewProject);
|
||||
void EnableDisableGemsForProject(const QString& projectPath);
|
||||
bool EnableDisableGemsForProject(const QString& projectPath);
|
||||
|
||||
private:
|
||||
void FillModel(const QString& projectPath, bool isNewProject);
|
||||
|
||||
@@ -73,6 +73,7 @@ namespace O3DE::ProjectManager
|
||||
Platforms m_platforms;
|
||||
Types m_types; //! Asset and/or Code and/or Tool
|
||||
QStringList m_features;
|
||||
QString m_requirement;
|
||||
QString m_directoryLink;
|
||||
QString m_documentationLink;
|
||||
QString m_version = "Unknown Version";
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <QLabel>
|
||||
#include <QSpacerItem>
|
||||
#include <QVBoxLayout>
|
||||
#include <QIcon>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
@@ -70,6 +71,22 @@ namespace O3DE::ProjectManager
|
||||
m_directoryLinkLabel->SetUrl(m_model->GetDirectoryLink(modelIndex));
|
||||
m_documentationLinkLabel->SetUrl(m_model->GetDocLink(modelIndex));
|
||||
|
||||
if (m_model->HasRequirement(modelIndex))
|
||||
{
|
||||
m_reqirementsIconLabel->show();
|
||||
m_reqirementsTitleLabel->show();
|
||||
m_reqirementsTextLabel->show();
|
||||
|
||||
m_reqirementsTitleLabel->setText("Requirement");
|
||||
m_reqirementsTextLabel->setText(m_model->GetRequirement(modelIndex));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_reqirementsIconLabel->hide();
|
||||
m_reqirementsTitleLabel->hide();
|
||||
m_reqirementsTextLabel->hide();
|
||||
}
|
||||
|
||||
// Depending and conflicting gems
|
||||
m_dependingGems->Update("Depending Gems", "The following Gems will be automatically enabled with this Gem.", m_model->GetDependingGemNames(modelIndex));
|
||||
m_conflictingGems->Update("Conflicting Gems", "The following Gems will be automatically disabled with this Gem.", m_model->GetConflictingGemNames(modelIndex));
|
||||
@@ -134,6 +151,28 @@ namespace O3DE::ProjectManager
|
||||
|
||||
m_mainLayout->addSpacing(10);
|
||||
|
||||
// Requirements
|
||||
m_reqirementsTitleLabel = GemInspector::CreateStyledLabel(m_mainLayout, 16, s_headerColor);
|
||||
|
||||
QHBoxLayout* requrementsLayout = new QHBoxLayout();
|
||||
requrementsLayout->setAlignment(Qt::AlignTop);
|
||||
requrementsLayout->setMargin(0);
|
||||
requrementsLayout->setSpacing(0);
|
||||
|
||||
m_reqirementsIconLabel = new QLabel();
|
||||
m_reqirementsIconLabel->setPixmap(QIcon(":/Warning.svg").pixmap(24, 24));
|
||||
requrementsLayout->addWidget(m_reqirementsIconLabel);
|
||||
|
||||
m_reqirementsTextLabel = GemInspector::CreateStyledLabel(requrementsLayout, 10, s_textColor);
|
||||
m_reqirementsTextLabel->setWordWrap(true);
|
||||
|
||||
QSpacerItem* reqirementsSpacer = new QSpacerItem(0, 0, QSizePolicy::Expanding);
|
||||
requrementsLayout->addSpacerItem(reqirementsSpacer);
|
||||
|
||||
m_mainLayout->addLayout(requrementsLayout);
|
||||
|
||||
m_mainLayout->addSpacing(20);
|
||||
|
||||
// Depending and conflicting gems
|
||||
m_dependingGems = new GemsSubWidget();
|
||||
m_mainLayout->addWidget(m_dependingGems);
|
||||
|
||||
@@ -76,6 +76,11 @@ namespace O3DE::ProjectManager
|
||||
LinkLabel* m_directoryLinkLabel = nullptr;
|
||||
LinkLabel* m_documentationLinkLabel = nullptr;
|
||||
|
||||
// Requirements
|
||||
QLabel* m_reqirementsTitleLabel = nullptr;
|
||||
QLabel* m_reqirementsIconLabel = nullptr;
|
||||
QLabel* m_reqirementsTextLabel = nullptr;
|
||||
|
||||
// Depending and conflicting gems
|
||||
GemsSubWidget* m_dependingGems = nullptr;
|
||||
GemsSubWidget* m_conflictingGems = nullptr;
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
*/
|
||||
|
||||
#include <GemCatalog/GemItemDelegate.h>
|
||||
#include "GemModel.h"
|
||||
#include <GemCatalog/GemModel.h>
|
||||
#include <QEvent>
|
||||
#include <QPainter>
|
||||
#include <QMouseEvent>
|
||||
|
||||
@@ -62,7 +62,7 @@ namespace O3DE::ProjectManager
|
||||
inline constexpr static int s_buttonCircleRadius = s_buttonBorderRadius - 2;
|
||||
inline constexpr static qreal s_buttonFontSize = 10.0;
|
||||
|
||||
private:
|
||||
protected:
|
||||
void CalcRects(const QStyleOptionViewItem& option, QRect& outFullRect, QRect& outItemRect, QRect& outContentRect) const;
|
||||
QRect GetTextRect(QFont& font, const QString& text, qreal fontSize) const;
|
||||
QRect CalcButtonRect(const QRect& contentRect) const;
|
||||
@@ -71,6 +71,7 @@ namespace O3DE::ProjectManager
|
||||
|
||||
QAbstractItemModel* m_model = nullptr;
|
||||
|
||||
private:
|
||||
// Platform icons
|
||||
void AddPlatformIcon(GemInfo::Platform platform, const QString& iconPath);
|
||||
inline constexpr static int s_platformIconSize = 12;
|
||||
|
||||
@@ -10,11 +10,9 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "GemListView.h"
|
||||
#include "GemItemDelegate.h"
|
||||
#include <GemCatalog/GemListView.h>
|
||||
#include <GemCatalog/GemItemDelegate.h>
|
||||
#include <QStandardItemModel>
|
||||
#include <QDateTime>
|
||||
#include <QPalette>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
#pragma once
|
||||
|
||||
#if !defined(Q_MOC_RUN)
|
||||
#include "GemInfo.h"
|
||||
#include <QAbstractItemModel>
|
||||
#include <QItemSelectionModel>
|
||||
#include <QListView>
|
||||
|
||||
@@ -49,6 +49,7 @@ namespace O3DE::ProjectManager
|
||||
item->setData(gemInfo.m_binarySizeInKB, RoleBinarySize);
|
||||
item->setData(gemInfo.m_features, RoleFeatures);
|
||||
item->setData(gemInfo.m_path, RolePath);
|
||||
item->setData(gemInfo.m_requirement, RoleRequirement);
|
||||
|
||||
appendRow(item);
|
||||
|
||||
@@ -183,6 +184,11 @@ namespace O3DE::ProjectManager
|
||||
return modelIndex.data(RolePath).toString();
|
||||
}
|
||||
|
||||
QString GemModel::GetRequirement(const QModelIndex& modelIndex)
|
||||
{
|
||||
return modelIndex.data(RoleRequirement).toString();
|
||||
}
|
||||
|
||||
bool GemModel::IsAdded(const QModelIndex& modelIndex)
|
||||
{
|
||||
return modelIndex.data(RoleIsAdded).toBool();
|
||||
@@ -208,6 +214,24 @@ namespace O3DE::ProjectManager
|
||||
return (modelIndex.data(RoleWasPreviouslyAdded).toBool() && !modelIndex.data(RoleIsAdded).toBool());
|
||||
}
|
||||
|
||||
bool GemModel::HasRequirement(const QModelIndex& modelIndex)
|
||||
{
|
||||
return !modelIndex.data(RoleRequirement).toString().isEmpty();
|
||||
}
|
||||
|
||||
bool GemModel::DoGemsToBeAddedHaveRequirements() const
|
||||
{
|
||||
for (int row = 0; row < rowCount(); ++row)
|
||||
{
|
||||
const QModelIndex modelIndex = index(row, 0);
|
||||
if (NeedsToBeAdded(modelIndex) && HasRequirement(modelIndex))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
QVector<QModelIndex> GemModel::GatherGemsToBeAdded() const
|
||||
{
|
||||
QVector<QModelIndex> result;
|
||||
|
||||
@@ -53,12 +53,16 @@ namespace O3DE::ProjectManager
|
||||
static int GetBinarySizeInKB(const QModelIndex& modelIndex);
|
||||
static QStringList GetFeatures(const QModelIndex& modelIndex);
|
||||
static QString GetPath(const QModelIndex& modelIndex);
|
||||
static QString GetRequirement(const QModelIndex& modelIndex);
|
||||
|
||||
static bool IsAdded(const QModelIndex& modelIndex);
|
||||
static void SetIsAdded(QAbstractItemModel& model, const QModelIndex& modelIndex, bool isAdded);
|
||||
static void SetWasPreviouslyAdded(QAbstractItemModel& model, const QModelIndex& modelIndex, bool wasAdded);
|
||||
static bool NeedsToBeAdded(const QModelIndex& modelIndex);
|
||||
static bool NeedsToBeRemoved(const QModelIndex& modelIndex);
|
||||
static bool HasRequirement(const QModelIndex& modelIndex);
|
||||
|
||||
bool DoGemsToBeAddedHaveRequirements() const;
|
||||
|
||||
QVector<QModelIndex> GatherGemsToBeAdded() const;
|
||||
QVector<QModelIndex> GatherGemsToBeRemoved() const;
|
||||
@@ -84,7 +88,8 @@ namespace O3DE::ProjectManager
|
||||
RoleBinarySize,
|
||||
RoleFeatures,
|
||||
RoleTypes,
|
||||
RolePath
|
||||
RolePath,
|
||||
RoleRequirement
|
||||
};
|
||||
|
||||
QHash<QString, QModelIndex> m_nameToIndexMap;
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <GemCatalog/GemRequirementDelegate.h>
|
||||
#include <GemCatalog/GemModel.h>
|
||||
|
||||
#include <QPainter>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
GemRequirementDelegate::GemRequirementDelegate(QAbstractItemModel* model, QObject* parent)
|
||||
: GemItemDelegate(model, parent)
|
||||
{
|
||||
}
|
||||
|
||||
void GemRequirementDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& modelIndex) const
|
||||
{
|
||||
if (!modelIndex.isValid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QStyleOptionViewItem options(option);
|
||||
initStyleOption(&options, modelIndex);
|
||||
|
||||
painter->save();
|
||||
painter->setRenderHint(QPainter::Antialiasing);
|
||||
|
||||
QRect fullRect, itemRect, contentRect;
|
||||
CalcRects(options, fullRect, itemRect, contentRect);
|
||||
|
||||
QFont standardFont(options.font);
|
||||
standardFont.setPixelSize(s_fontSize);
|
||||
QFontMetrics standardFontMetrics(standardFont);
|
||||
|
||||
painter->save();
|
||||
painter->setClipping(true);
|
||||
painter->setClipRect(fullRect);
|
||||
painter->setFont(options.font);
|
||||
|
||||
// Draw background
|
||||
painter->fillRect(fullRect, m_backgroundColor);
|
||||
|
||||
// Draw item background
|
||||
const QColor itemBackgroundColor = m_itemBackgroundColor;
|
||||
painter->fillRect(itemRect, itemBackgroundColor);
|
||||
|
||||
// Gem name
|
||||
QString gemName = GemModel::GetName(modelIndex);
|
||||
QFont gemNameFont(options.font);
|
||||
const int firstColumnMaxTextWidth = s_summaryStartX - 30;
|
||||
gemName = QFontMetrics(gemNameFont).elidedText(gemName, Qt::TextElideMode::ElideRight, firstColumnMaxTextWidth);
|
||||
gemNameFont.setPixelSize(s_gemNameFontSize);
|
||||
gemNameFont.setBold(true);
|
||||
QRect gemNameRect = GetTextRect(gemNameFont, gemName, s_gemNameFontSize);
|
||||
gemNameRect.moveTo(contentRect.left(), contentRect.center().y() - s_gemNameFontSize);
|
||||
|
||||
painter->setFont(gemNameFont);
|
||||
painter->setPen(m_textColor);
|
||||
painter->drawText(gemNameRect, Qt::TextSingleLine, gemName);
|
||||
|
||||
// Gem requirement
|
||||
const QSize requirementSize = QSize(contentRect.width() - s_summaryStartX - s_itemMargins.right(), contentRect.height());
|
||||
const QRect requirementRect = QRect(QPoint(contentRect.left() + s_summaryStartX, contentRect.top()), requirementSize);
|
||||
|
||||
painter->setFont(standardFont);
|
||||
painter->setPen(m_textColor);
|
||||
|
||||
const QString requirement = GemModel::GetRequirement(modelIndex);
|
||||
painter->drawText(requirementRect, Qt::AlignLeft | Qt::TextWordWrap, requirement);
|
||||
|
||||
painter->restore();
|
||||
}
|
||||
|
||||
bool GemRequirementDelegate::editorEvent(
|
||||
[[maybe_unused]] QEvent* event,
|
||||
[[maybe_unused]] QAbstractItemModel* model,
|
||||
[[maybe_unused]] const QStyleOptionViewItem& option,
|
||||
[[maybe_unused]] const QModelIndex& modelIndex)
|
||||
{
|
||||
// Do nothing here
|
||||
return false;
|
||||
}
|
||||
} // namespace O3DE::ProjectManager
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(Q_MOC_RUN)
|
||||
#include <GemCatalog/GemItemDelegate.h>
|
||||
#endif
|
||||
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
class GemRequirementDelegate
|
||||
: public GemItemDelegate
|
||||
{
|
||||
Q_OBJECT // AUTOMOC
|
||||
|
||||
public:
|
||||
explicit GemRequirementDelegate(QAbstractItemModel* model, QObject* parent = nullptr);
|
||||
~GemRequirementDelegate() = default;
|
||||
|
||||
void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& modelIndex) const override;
|
||||
bool editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& modelIndex) override;
|
||||
|
||||
const QColor m_backgroundColor = QColor("#444444"); // Outside of the actual gem item
|
||||
const QColor m_itemBackgroundColor = QColor("#393939"); // Background color of the gem item
|
||||
};
|
||||
} // namespace O3DE::ProjectManager
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <GemCatalog/GemRequirementDialog.h>
|
||||
#include <GemCatalog/GemRequirementListView.h>
|
||||
#include <GemCatalog/GemRequirementFilterProxyModel.h>
|
||||
|
||||
#include <QVBoxLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QSpacerItem>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
GemRequirementDialog::GemRequirementDialog(GemModel* model, const QVector<QModelIndex>& gemsToAdd, QWidget* parent)
|
||||
: QDialog(parent)
|
||||
{
|
||||
setWindowTitle(tr("Manual setup is required"));
|
||||
setModal(true);
|
||||
|
||||
QVBoxLayout* vLayout = new QVBoxLayout();
|
||||
vLayout->setMargin(0);
|
||||
vLayout->setContentsMargins(25, 10, 25, 10);
|
||||
vLayout->setSizeConstraint(QLayout::SetFixedSize);
|
||||
setLayout(vLayout);
|
||||
|
||||
QHBoxLayout* instructionLayout = new QHBoxLayout();
|
||||
instructionLayout->setMargin(0);
|
||||
|
||||
QLabel* instructionIconLabel = new QLabel();
|
||||
instructionIconLabel->setPixmap(QIcon(":/Warning.svg").pixmap(32, 32));
|
||||
instructionLayout->addWidget(instructionIconLabel);
|
||||
|
||||
instructionLayout->addSpacing(10);
|
||||
|
||||
QLabel* instructionLabel = new QLabel(tr("The following Gem(s) require manual setup before the project can be built successfully."));
|
||||
instructionLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
|
||||
instructionLayout->addWidget(instructionLabel);
|
||||
|
||||
QSpacerItem* instructionSpacer = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum);
|
||||
instructionLayout->addSpacerItem(instructionSpacer);
|
||||
|
||||
vLayout->addLayout(instructionLayout);
|
||||
|
||||
vLayout->addSpacing(20);
|
||||
|
||||
GemRequirementFilterProxyModel* proxModel = new GemRequirementFilterProxyModel(model, gemsToAdd, this);
|
||||
|
||||
GemRequirementListView* m_gemListView = new GemRequirementListView(proxModel, proxModel->GetSelectionModel(), this);
|
||||
vLayout->addWidget(m_gemListView);
|
||||
|
||||
QDialogButtonBox* dialogButtons = new QDialogButtonBox();
|
||||
dialogButtons->setObjectName("footer");
|
||||
vLayout->addWidget(dialogButtons);
|
||||
|
||||
QPushButton* cancelButton = dialogButtons->addButton(tr("Cancel"), QDialogButtonBox::RejectRole);
|
||||
cancelButton->setProperty("secondary", true);
|
||||
QPushButton* continueButton = dialogButtons->addButton(tr("Continue"), QDialogButtonBox::ApplyRole);
|
||||
|
||||
connect(cancelButton, &QPushButton::clicked, this, &GemRequirementDialog::CancelButtonPressed);
|
||||
connect(continueButton, &QPushButton::clicked, this, &GemRequirementDialog::ContinueButtonPressed);
|
||||
}
|
||||
|
||||
QDialogButtonBox::ButtonRole GemRequirementDialog::GetButtonResult()
|
||||
{
|
||||
return m_buttonResult;
|
||||
}
|
||||
|
||||
void GemRequirementDialog::CancelButtonPressed()
|
||||
{
|
||||
m_buttonResult = QDialogButtonBox::RejectRole;
|
||||
close();
|
||||
}
|
||||
|
||||
void GemRequirementDialog::ContinueButtonPressed()
|
||||
{
|
||||
m_buttonResult = QDialogButtonBox::ApplyRole;
|
||||
close();
|
||||
}
|
||||
|
||||
} // namespace O3DE::ProjectManager
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(Q_MOC_RUN)
|
||||
#include <QDialog>
|
||||
|
||||
#include <QDialogButtonBox>
|
||||
#endif
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
QT_FORWARD_DECLARE_CLASS(GemModel)
|
||||
|
||||
class GemRequirementDialog
|
||||
: public QDialog
|
||||
{
|
||||
Q_OBJECT // AUTOMOC
|
||||
public:
|
||||
explicit GemRequirementDialog(GemModel* model, const QVector<QModelIndex>& gemsToAdd, QWidget *parent = nullptr);
|
||||
~GemRequirementDialog() = default;
|
||||
|
||||
QDialogButtonBox::ButtonRole GetButtonResult();
|
||||
|
||||
private:
|
||||
void CancelButtonPressed();
|
||||
void ContinueButtonPressed();
|
||||
|
||||
QDialogButtonBox::ButtonRole m_buttonResult = QDialogButtonBox::RejectRole;
|
||||
};
|
||||
} // namespace O3DE::ProjectManager
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <GemCatalog/GemRequirementFilterProxyModel.h>
|
||||
#include <GemCatalog/GemModel.h>
|
||||
|
||||
#include <QItemSelectionModel>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
GemRequirementFilterProxyModel::GemRequirementFilterProxyModel(GemModel* sourceModel, const QVector<QModelIndex>& addedGems, QObject* parent)
|
||||
: QSortFilterProxyModel(parent)
|
||||
, m_sourceModel(sourceModel)
|
||||
, m_addedGems(addedGems)
|
||||
{
|
||||
setSourceModel(sourceModel);
|
||||
m_selectionProxyModel = new AzQtComponents::SelectionProxyModel(sourceModel->GetSelectionModel(), this, parent);
|
||||
}
|
||||
|
||||
bool GemRequirementFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const
|
||||
{
|
||||
// Do not use sourceParent->child because an invalid parent does not produce valid children (which our index function does)
|
||||
QModelIndex sourceIndex = sourceModel()->index(sourceRow, 0, sourceParent);
|
||||
if (!sourceIndex.isValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_addedGems.contains(sourceIndex))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_sourceModel->HasRequirement(sourceIndex))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace O3DE::ProjectManager
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(Q_MOC_RUN)
|
||||
#include <AzQtComponents/Utilities/SelectionProxyModel.h>
|
||||
#include <QtCore/QSortFilterProxyModel>
|
||||
#endif
|
||||
|
||||
QT_FORWARD_DECLARE_CLASS(QItemSelectionModel)
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
QT_FORWARD_DECLARE_CLASS(GemModel)
|
||||
|
||||
class GemRequirementFilterProxyModel
|
||||
: public QSortFilterProxyModel
|
||||
{
|
||||
Q_OBJECT // AUTOMOC
|
||||
|
||||
public:
|
||||
GemRequirementFilterProxyModel(GemModel* sourceModel, const QVector<QModelIndex>& addedGems, QObject* parent = nullptr);
|
||||
|
||||
AzQtComponents::SelectionProxyModel* GetSelectionModel() const { return m_selectionProxyModel; }
|
||||
|
||||
bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const override;
|
||||
|
||||
private:
|
||||
GemModel* m_sourceModel = nullptr;
|
||||
AzQtComponents::SelectionProxyModel* m_selectionProxyModel = nullptr;
|
||||
|
||||
QVector<QModelIndex> m_addedGems;
|
||||
};
|
||||
} // namespace O3DE::ProjectManager
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <GemCatalog/GemRequirementListView.h>
|
||||
#include <GemCatalog/GemRequirementDelegate.h>
|
||||
#include <QStandardItemModel>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
GemRequirementListView::GemRequirementListView(QAbstractItemModel* model, QItemSelectionModel* selectionModel, QWidget* parent)
|
||||
: QListView(parent)
|
||||
{
|
||||
setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
|
||||
|
||||
setStyleSheet("background-color: #444444;");
|
||||
|
||||
setModel(model);
|
||||
setSelectionModel(selectionModel);
|
||||
setItemDelegate(new GemRequirementDelegate(model, this));
|
||||
}
|
||||
} // namespace O3DE::ProjectManager
|
||||
+17
-17
@@ -10,23 +10,23 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "./MeshMotionVectorCommon.azsli"
|
||||
#pragma once
|
||||
|
||||
struct VSInput
|
||||
#if !defined(Q_MOC_RUN)
|
||||
#include <QAbstractItemModel>
|
||||
#include <QItemSelectionModel>
|
||||
#include <QListView>
|
||||
#endif
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
float3 m_position : POSITION;
|
||||
};
|
||||
|
||||
VSOutput MainVS(VSInput IN)
|
||||
{
|
||||
VSOutput OUT;
|
||||
|
||||
OUT.m_worldPos = mul(SceneSrg::GetObjectToWorldMatrix(ObjectSrg::m_objectId), float4(IN.m_position, 1.0)).xyz;
|
||||
OUT.m_position = mul(ViewSrg::m_viewProjectionMatrix, float4(OUT.m_worldPos, 1.0));
|
||||
OUT.m_worldPosPrev = mul(SceneSrg::GetObjectToWorldMatrixPrev(ObjectSrg::m_objectId), float4(IN.m_position, 1.0)).xyz;
|
||||
|
||||
return OUT;
|
||||
}
|
||||
|
||||
|
||||
class GemRequirementListView
|
||||
: public QListView
|
||||
{
|
||||
Q_OBJECT // AUTOMOC
|
||||
|
||||
public:
|
||||
explicit GemRequirementListView(QAbstractItemModel* model, QItemSelectionModel* selectionModel, QWidget* parent = nullptr);
|
||||
~GemRequirementListView() = default;
|
||||
};
|
||||
} // namespace O3DE::ProjectManager
|
||||
@@ -12,8 +12,8 @@
|
||||
|
||||
#include <NewProjectSettingsScreen.h>
|
||||
#include <PythonBindingsInterface.h>
|
||||
#include <FormLineEditWidget.h>
|
||||
#include <FormBrowseEditWidget.h>
|
||||
#include <FormLineEditWidget.h>
|
||||
#include <TemplateButtonWidget.h>
|
||||
#include <PathValidator.h>
|
||||
#include <EngineInfo.h>
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
*/
|
||||
|
||||
#include <ProjectBuilder.h>
|
||||
#include <ProjectManagerDefs.h>
|
||||
#include <ProjectButtonWidget.h>
|
||||
#include <PythonBindingsInterface.h>
|
||||
|
||||
@@ -30,8 +31,6 @@ namespace O3DE::ProjectManager
|
||||
{
|
||||
// 10 Minutes
|
||||
constexpr int MaxBuildTimeMSecs = 600000;
|
||||
static const QString BuildPathPostfix = "windows_vs2019";
|
||||
static const QString ErrorLogPathPostfix = "CMakeFiles/CMakeProjectBuildError.log";
|
||||
|
||||
ProjectBuilderWorker::ProjectBuilderWorker(const ProjectInfo& projectInfo)
|
||||
: QObject()
|
||||
@@ -83,7 +82,7 @@ namespace O3DE::ProjectManager
|
||||
QStringList
|
||||
{
|
||||
"-B",
|
||||
QDir(m_projectInfo.m_path).filePath(BuildPathPostfix),
|
||||
QDir(m_projectInfo.m_path).filePath(ProjectBuildPathPostfix),
|
||||
"-S",
|
||||
m_projectInfo.m_path,
|
||||
"-G",
|
||||
@@ -123,7 +122,7 @@ namespace O3DE::ProjectManager
|
||||
QStringList
|
||||
{
|
||||
"--build",
|
||||
QDir(m_projectInfo.m_path).filePath(BuildPathPostfix),
|
||||
QDir(m_projectInfo.m_path).filePath(ProjectBuildPathPostfix),
|
||||
"--target",
|
||||
m_projectInfo.m_projectName + ".GameLauncher",
|
||||
"Editor",
|
||||
@@ -159,8 +158,8 @@ namespace O3DE::ProjectManager
|
||||
QString ProjectBuilderWorker::LogFilePath() const
|
||||
{
|
||||
QDir logFilePath(m_projectInfo.m_path);
|
||||
logFilePath.cd(BuildPathPostfix);
|
||||
return logFilePath.filePath(ErrorLogPathPostfix);
|
||||
logFilePath.cd(ProjectBuildPathPostfix);
|
||||
return logFilePath.filePath(ProjectBuildErrorLogPathPostfix);
|
||||
}
|
||||
|
||||
void ProjectBuilderWorker::WriteErrorLog(const QString& log)
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
*/
|
||||
|
||||
#include <ProjectButtonWidget.h>
|
||||
#include <ProjectManagerDefs.h>
|
||||
#include <AzQtComponents/Utilities/DesktopUtilities.h>
|
||||
|
||||
#include <QVBoxLayout>
|
||||
@@ -22,12 +23,11 @@
|
||||
#include <QMenu>
|
||||
#include <QSpacerItem>
|
||||
#include <QProgressBar>
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
inline constexpr static int s_projectImageWidth = 210;
|
||||
inline constexpr static int s_projectImageHeight = 280;
|
||||
|
||||
LabelButton::LabelButton(QWidget* parent)
|
||||
: QLabel(parent)
|
||||
{
|
||||
@@ -92,11 +92,6 @@ namespace O3DE::ProjectManager
|
||||
: QFrame(parent)
|
||||
, m_projectInfo(projectInfo)
|
||||
{
|
||||
if (m_projectInfo.m_imagePath.isEmpty())
|
||||
{
|
||||
m_projectInfo.m_imagePath = ":/DefaultProjectImage.png";
|
||||
}
|
||||
|
||||
BaseSetup();
|
||||
if (processing)
|
||||
{
|
||||
@@ -118,20 +113,25 @@ namespace O3DE::ProjectManager
|
||||
setLayout(vLayout);
|
||||
|
||||
m_projectImageLabel = new LabelButton(this);
|
||||
m_projectImageLabel->setFixedSize(s_projectImageWidth, s_projectImageHeight);
|
||||
m_projectImageLabel->setFixedSize(ProjectPreviewImageWidth, ProjectPreviewImageHeight);
|
||||
m_projectImageLabel->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
|
||||
connect(m_projectImageLabel, &LabelButton::triggered, [this]() { emit OpenProject(m_projectInfo.m_path); });
|
||||
vLayout->addWidget(m_projectImageLabel);
|
||||
|
||||
m_projectImageLabel->setPixmap(
|
||||
QPixmap(m_projectInfo.m_imagePath).scaled(m_projectImageLabel->size(), Qt::KeepAspectRatioByExpanding));
|
||||
QString projectPreviewPath = QDir(m_projectInfo.m_path).filePath(m_projectInfo.m_iconPath);
|
||||
QFileInfo doesPreviewExist(projectPreviewPath);
|
||||
if (!doesPreviewExist.exists() || !doesPreviewExist.isFile())
|
||||
{
|
||||
projectPreviewPath = ":/DefaultProjectImage.png";
|
||||
}
|
||||
m_projectImageLabel->setPixmap(QPixmap(projectPreviewPath).scaled(m_projectImageLabel->size(), Qt::KeepAspectRatioByExpanding));
|
||||
|
||||
m_projectFooter = new QFrame(this);
|
||||
QHBoxLayout* hLayout = new QHBoxLayout();
|
||||
hLayout->setContentsMargins(0, 0, 0, 0);
|
||||
m_projectFooter->setLayout(hLayout);
|
||||
{
|
||||
QLabel* projectNameLabel = new QLabel(m_projectInfo.m_displayName, this);
|
||||
QLabel* projectNameLabel = new QLabel(m_projectInfo.GetProjectDisplayName(), this);
|
||||
hLayout->addWidget(projectNameLabel);
|
||||
}
|
||||
|
||||
|
||||
@@ -10,33 +10,74 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ProjectInfo.h"
|
||||
#include <ProjectInfo.h>
|
||||
#include <ProjectManagerDefs.h>
|
||||
|
||||
#include <QDir>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
ProjectInfo::ProjectInfo(const QString& path, const QString& projectName, const QString& displayName,
|
||||
const QString& origin, const QString& summary, const QString& imagePath, const QString& backgroundImagePath,
|
||||
ProjectInfo::ProjectInfo(
|
||||
const QString& path,
|
||||
const QString& projectName,
|
||||
const QString& displayName,
|
||||
const QString& origin,
|
||||
const QString& summary,
|
||||
const QString& iconPath,
|
||||
const QString& newPreviewImagePath,
|
||||
const QString& newBackgroundImagePath,
|
||||
bool needsBuild)
|
||||
: m_path(path)
|
||||
, m_projectName(projectName)
|
||||
, m_displayName(displayName)
|
||||
, m_origin(origin)
|
||||
, m_summary(summary)
|
||||
, m_imagePath(imagePath)
|
||||
, m_backgroundImagePath(backgroundImagePath)
|
||||
, m_iconPath(iconPath)
|
||||
, m_newPreviewImagePath(newPreviewImagePath)
|
||||
, m_newBackgroundImagePath(newBackgroundImagePath)
|
||||
, m_needsBuild(needsBuild)
|
||||
{
|
||||
}
|
||||
|
||||
bool ProjectInfo::operator==(const ProjectInfo& rhs)
|
||||
bool ProjectInfo::operator==(const ProjectInfo& rhs) const
|
||||
{
|
||||
return m_path == rhs.m_path
|
||||
&& m_projectName == rhs.m_projectName
|
||||
&& m_imagePath == rhs.m_imagePath
|
||||
&& m_backgroundImagePath == rhs.m_backgroundImagePath;
|
||||
if (m_path != rhs.m_path)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (m_projectName != rhs.m_projectName)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (m_displayName != rhs.m_displayName)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (m_origin != rhs.m_origin)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (m_summary != rhs.m_summary)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (m_iconPath != rhs.m_iconPath)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (m_newPreviewImagePath != rhs.m_newPreviewImagePath)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (m_newBackgroundImagePath != rhs.m_newBackgroundImagePath)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ProjectInfo::operator!=(const ProjectInfo& rhs)
|
||||
bool ProjectInfo::operator!=(const ProjectInfo& rhs) const
|
||||
{
|
||||
return !operator==(rhs);
|
||||
}
|
||||
@@ -45,4 +86,16 @@ namespace O3DE::ProjectManager
|
||||
{
|
||||
return !m_path.isEmpty() && !m_projectName.isEmpty();
|
||||
}
|
||||
|
||||
const QString& ProjectInfo::GetProjectDisplayName() const
|
||||
{
|
||||
if (!m_displayName.isEmpty())
|
||||
{
|
||||
return m_displayName;
|
||||
}
|
||||
else
|
||||
{
|
||||
return m_projectName;
|
||||
}
|
||||
}
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -31,14 +31,16 @@ namespace O3DE::ProjectManager
|
||||
const QString& displayName,
|
||||
const QString& origin,
|
||||
const QString& summary,
|
||||
const QString& imagePath,
|
||||
const QString& backgroundImagePath,
|
||||
const QString& iconPath,
|
||||
const QString& newPreviewImagePath,
|
||||
const QString& newBackgroundImagePath,
|
||||
bool needsBuild);
|
||||
|
||||
bool operator==(const ProjectInfo& rhs);
|
||||
bool operator!=(const ProjectInfo& rhs);
|
||||
bool operator==(const ProjectInfo& rhs) const;
|
||||
bool operator!=(const ProjectInfo& rhs) const;
|
||||
|
||||
bool IsValid() const;
|
||||
const QString& GetProjectDisplayName() const;
|
||||
|
||||
// from o3de_manifest.json and o3de_projects.json
|
||||
QString m_path;
|
||||
@@ -48,14 +50,14 @@ namespace O3DE::ProjectManager
|
||||
QString m_displayName;
|
||||
QString m_origin;
|
||||
QString m_summary;
|
||||
QString m_iconPath;
|
||||
QStringList m_userTags;
|
||||
|
||||
// Used on projects home screen
|
||||
QString m_imagePath;
|
||||
QString m_backgroundImagePath;
|
||||
// Used as temp variable for replace images
|
||||
QString m_newPreviewImagePath;
|
||||
QString m_newBackgroundImagePath;
|
||||
|
||||
// Used in project creation
|
||||
|
||||
bool m_needsBuild = false; //! Does this project need to be built
|
||||
};
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
inline constexpr static int ProjectPreviewImageWidth = 210;
|
||||
inline constexpr static int ProjectPreviewImageHeight = 280;
|
||||
|
||||
static const QString ProjectBuildPathPostfix = "Windows_VS2019";
|
||||
static const QString ProjectBuildErrorLogPathPostfix = "CMakeFiles/CMakeProjectBuildError.log";
|
||||
static const QString ProjectPreviewImagePath = "preview.png";
|
||||
} // namespace O3DE::ProjectManager
|
||||
@@ -11,7 +11,7 @@
|
||||
*/
|
||||
|
||||
#include <ProjectSettingsScreen.h>
|
||||
#include <FormBrowseEditWidget.h>
|
||||
#include <FormFolderBrowseEditWidget.h>
|
||||
#include <FormLineEditWidget.h>
|
||||
#include <PathValidator.h>
|
||||
#include <PythonBindingsInterface.h>
|
||||
@@ -47,7 +47,7 @@ namespace O3DE::ProjectManager
|
||||
connect(m_projectName->lineEdit(), &QLineEdit::textChanged, this, &ProjectSettingsScreen::ValidateProjectName);
|
||||
m_verticalLayout->addWidget(m_projectName);
|
||||
|
||||
m_projectPath = new FormBrowseEditWidget(tr("Project Location"), "", this);
|
||||
m_projectPath = new FormFolderBrowseEditWidget(tr("Project Location"), "", this);
|
||||
m_projectPath->lineEdit()->setReadOnly(true);
|
||||
connect(m_projectPath->lineEdit(), &QLineEdit::textChanged, this, &ProjectSettingsScreen::Validate);
|
||||
m_verticalLayout->addWidget(m_projectPath);
|
||||
|
||||
@@ -32,9 +32,9 @@ namespace O3DE::ProjectManager
|
||||
~ProjectSettingsScreen() = default;
|
||||
ProjectManagerScreen GetScreenEnum() override;
|
||||
|
||||
ProjectInfo GetProjectInfo();
|
||||
virtual ProjectInfo GetProjectInfo();
|
||||
|
||||
bool Validate();
|
||||
virtual bool Validate();
|
||||
|
||||
protected slots:
|
||||
virtual bool ValidateProjectName();
|
||||
|
||||
@@ -29,11 +29,8 @@ namespace O3DE::ProjectManager
|
||||
if (!QDir(path).isEmpty())
|
||||
{
|
||||
QMessageBox::StandardButton warningResult = QMessageBox::warning(
|
||||
parent,
|
||||
QObject::tr("Overwrite Directory"),
|
||||
QObject::tr("Directory is not empty! Are you sure you want to overwrite it?"),
|
||||
QMessageBox::No | QMessageBox::Yes
|
||||
);
|
||||
parent, QObject::tr("Overwrite Directory"),
|
||||
QObject::tr("Directory is not empty! Are you sure you want to overwrite it?"), QMessageBox::No | QMessageBox::Yes);
|
||||
|
||||
if (warningResult != QMessageBox::Yes)
|
||||
{
|
||||
@@ -53,14 +50,13 @@ namespace O3DE::ProjectManager
|
||||
{
|
||||
if (ancestor == descendent)
|
||||
{
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
descendent.cdUp();
|
||||
}
|
||||
while (!descendent.isRoot());
|
||||
} while (!descendent.isRoot());
|
||||
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool CopyDirectory(const QString& origPath, const QString& newPath)
|
||||
@@ -138,7 +134,7 @@ namespace O3DE::ProjectManager
|
||||
bool CopyProject(const QString& origPath, const QString& newPath)
|
||||
{
|
||||
// Disallow copying from or into subdirectory
|
||||
if (!IsDirectoryDescedent(origPath, newPath) || !IsDirectoryDescedent(newPath, origPath))
|
||||
if (IsDirectoryDescedent(origPath, newPath) || IsDirectoryDescedent(newPath, origPath))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -173,20 +169,66 @@ namespace O3DE::ProjectManager
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MoveProject(const QString& origPath, const QString& newPath, QWidget* parent)
|
||||
bool MoveProject(QString origPath, QString newPath, QWidget* parent, bool ignoreRegister)
|
||||
{
|
||||
if (!WarnDirectoryOverwrite(newPath, parent) || !UnregisterProject(origPath))
|
||||
origPath = QDir::toNativeSeparators(origPath);
|
||||
newPath = QDir::toNativeSeparators(newPath);
|
||||
|
||||
if (!WarnDirectoryOverwrite(newPath, parent) || (!ignoreRegister && !UnregisterProject(origPath)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
QDir directory;
|
||||
if (directory.rename(origPath, newPath))
|
||||
QDir newDirectory(newPath);
|
||||
if (!newDirectory.removeRecursively())
|
||||
{
|
||||
return directory.rename(origPath, newPath);
|
||||
return false;
|
||||
}
|
||||
if (!newDirectory.rename(origPath, newPath))
|
||||
{
|
||||
// Likely failed because trying to move to another partition, try copying
|
||||
if (!CopyProject(origPath, newPath))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
DeleteProjectFiles(origPath, true);
|
||||
}
|
||||
|
||||
if (!RegisterProject(newPath))
|
||||
if (!ignoreRegister && !RegisterProject(newPath))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ReplaceFile(const QString& origFile, const QString& newFile, QWidget* parent, bool interactive)
|
||||
{
|
||||
QFileInfo original(origFile);
|
||||
if (original.exists())
|
||||
{
|
||||
if (interactive)
|
||||
{
|
||||
QMessageBox::StandardButton warningResult = QMessageBox::warning(
|
||||
parent,
|
||||
QObject::tr("Overwrite File?"),
|
||||
QObject::tr("Replacing this will overwrite the current file on disk. Are you sure?"),
|
||||
QMessageBox::No | QMessageBox::Yes);
|
||||
|
||||
if (warningResult == QMessageBox::No)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!QFile::remove(origFile))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!QFile::copy(newFile, origFile))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -24,7 +24,9 @@ namespace O3DE::ProjectManager
|
||||
bool CopyProjectDialog(const QString& origPath, QWidget* parent = nullptr);
|
||||
bool CopyProject(const QString& origPath, const QString& newPath);
|
||||
bool DeleteProjectFiles(const QString& path, bool force = false);
|
||||
bool MoveProject(const QString& origPath, const QString& newPath, QWidget* parent = nullptr);
|
||||
bool MoveProject(QString origPath, QString newPath, QWidget* parent = nullptr, bool ignoreRegister = false);
|
||||
|
||||
bool ReplaceFile(const QString& origFile, const QString& newFile, QWidget* parent = nullptr, bool interactive = true);
|
||||
|
||||
bool IsVS2019Installed();
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
#include <ProjectsScreen.h>
|
||||
|
||||
#include <ProjectManagerDefs.h>
|
||||
#include <ProjectButtonWidget.h>
|
||||
#include <PythonBindingsInterface.h>
|
||||
#include <ProjectUtils.h>
|
||||
@@ -35,7 +36,6 @@
|
||||
#include <QSpacerItem>
|
||||
#include <QListWidget>
|
||||
#include <QListWidgetItem>
|
||||
#include <QFileInfo>
|
||||
#include <QScrollArea>
|
||||
#include <QStackedWidget>
|
||||
#include <QFrame>
|
||||
@@ -218,16 +218,7 @@ namespace O3DE::ProjectManager
|
||||
|
||||
ProjectButton* ProjectsScreen::CreateProjectButton(ProjectInfo& project, QLayout* flowLayout, bool processing)
|
||||
{
|
||||
ProjectButton* projectButton;
|
||||
|
||||
QString projectPreviewPath = project.m_path + m_projectPreviewImagePath;
|
||||
QFileInfo doesPreviewExist(projectPreviewPath);
|
||||
if (doesPreviewExist.exists() && doesPreviewExist.isFile())
|
||||
{
|
||||
project.m_imagePath = projectPreviewPath;
|
||||
}
|
||||
|
||||
projectButton = new ProjectButton(project, this, processing);
|
||||
ProjectButton* projectButton = new ProjectButton(project, this, processing);
|
||||
|
||||
flowLayout->addWidget(projectButton);
|
||||
|
||||
@@ -438,7 +429,7 @@ namespace O3DE::ProjectManager
|
||||
{
|
||||
QMessageBox::information(this,
|
||||
tr("Project Should be rebuilt."),
|
||||
projectInfo.m_projectName + tr(" project likely needs to be rebuilt."));
|
||||
projectInfo.GetProjectDisplayName() + tr(" project likely needs to be rebuilt."));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -499,8 +490,8 @@ namespace O3DE::ProjectManager
|
||||
{
|
||||
QMessageBox::StandardButton buildProject = QMessageBox::information(
|
||||
this,
|
||||
tr("Building \"%1\"").arg(projectInfo.m_projectName),
|
||||
tr("Ready to build \"%1\"?").arg(projectInfo.m_projectName),
|
||||
tr("Building \"%1\"").arg(projectInfo.GetProjectDisplayName()),
|
||||
tr("Ready to build \"%1\"?").arg(projectInfo.GetProjectDisplayName()),
|
||||
QMessageBox::No | QMessageBox::Yes);
|
||||
|
||||
if (buildProject == QMessageBox::Yes)
|
||||
|
||||
@@ -80,8 +80,6 @@ namespace O3DE::ProjectManager
|
||||
QQueue<ProjectInfo> m_buildQueue;
|
||||
ProjectBuilderController* m_currentBuilder = nullptr;
|
||||
|
||||
const QString m_projectPreviewImagePath = "/preview.png";
|
||||
|
||||
inline constexpr static int s_contentMargins = 80;
|
||||
inline constexpr static int s_spacerSize = 20;
|
||||
};
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
#include <PythonBindings.h>
|
||||
|
||||
#include <ProjectManagerDefs.h>
|
||||
|
||||
// Qt defines slots, which interferes with the use here.
|
||||
#pragma push_macro("slots")
|
||||
@@ -660,6 +661,7 @@ namespace O3DE::ProjectManager
|
||||
gemInfo.m_displayName = Py_To_String_Optional(data, "DisplayName", gemInfo.m_name);
|
||||
gemInfo.m_summary = Py_To_String_Optional(data, "Summary", "");
|
||||
gemInfo.m_version = Py_To_String_Optional(data, "Version", "");
|
||||
gemInfo.m_requirement = Py_To_String_Optional(data, "Requirements", "");
|
||||
|
||||
if (data.contains("Tags"))
|
||||
{
|
||||
@@ -693,6 +695,7 @@ namespace O3DE::ProjectManager
|
||||
projectInfo.m_displayName = Py_To_String_Optional(projectData, "display_name", projectInfo.m_projectName);
|
||||
projectInfo.m_origin = Py_To_String_Optional(projectData, "origin", projectInfo.m_origin);
|
||||
projectInfo.m_summary = Py_To_String_Optional(projectData, "summary", projectInfo.m_summary);
|
||||
projectInfo.m_iconPath = Py_To_String_Optional(projectData, "icon", ProjectPreviewImagePath);
|
||||
if (projectData.contains("user_tags"))
|
||||
{
|
||||
for (auto tag : projectData["user_tags"])
|
||||
@@ -786,7 +789,7 @@ namespace O3DE::ProjectManager
|
||||
pybind11::str(projectInfo.m_origin.toStdString()), // new_origin
|
||||
pybind11::str(projectInfo.m_displayName.toStdString()), // new_display
|
||||
pybind11::str(projectInfo.m_summary.toStdString()), // new_summary
|
||||
pybind11::str(projectInfo.m_imagePath.toStdString()), // new_icon
|
||||
pybind11::str(projectInfo.m_iconPath.toStdString()), // new_icon
|
||||
pybind11::none(), // add_tags not used
|
||||
pybind11::none(), // remove_tags not used
|
||||
pybind11::list(pybind11::cast(newTags))); // replace_tags
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
*/
|
||||
|
||||
#include <GemCatalog/GemCatalogScreen.h>
|
||||
#include <ProjectManagerDefs.h>
|
||||
#include <PythonBindingsInterface.h>
|
||||
#include <ScreenHeaderWidget.h>
|
||||
#include <ScreensCtrl.h>
|
||||
@@ -24,6 +25,7 @@
|
||||
#include <QStackedWidget>
|
||||
#include <QTabWidget>
|
||||
#include <QVBoxLayout>
|
||||
#include <QDir>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
@@ -101,8 +103,11 @@ namespace O3DE::ProjectManager
|
||||
|
||||
void UpdateProjectCtrl::HandleGemsButton()
|
||||
{
|
||||
m_stack->setCurrentWidget(m_gemCatalogScreen);
|
||||
Update();
|
||||
if (UpdateProjectSettings(true))
|
||||
{
|
||||
m_stack->setCurrentWidget(m_gemCatalogScreen);
|
||||
Update();
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateProjectCtrl::HandleBackButton()
|
||||
@@ -114,7 +119,10 @@ namespace O3DE::ProjectManager
|
||||
}
|
||||
else
|
||||
{
|
||||
emit GotoPreviousScreenRequest();
|
||||
if (UpdateProjectSettings(true))
|
||||
{
|
||||
emit GotoPreviousScreenRequest();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,44 +132,19 @@ namespace O3DE::ProjectManager
|
||||
|
||||
if (m_stack->currentIndex() == ScreenOrder::Settings && m_updateSettingsScreen)
|
||||
{
|
||||
if (m_updateSettingsScreen)
|
||||
if (!UpdateProjectSettings())
|
||||
{
|
||||
if (!m_updateSettingsScreen->Validate())
|
||||
{
|
||||
QMessageBox::critical(this, tr("Invalid project settings"), tr("Invalid project settings"));
|
||||
return;
|
||||
}
|
||||
|
||||
ProjectInfo newProjectSettings = m_updateSettingsScreen->GetProjectInfo();
|
||||
|
||||
// Update project if settings changed
|
||||
if (m_projectInfo != newProjectSettings)
|
||||
{
|
||||
auto result = PythonBindingsInterface::Get()->UpdateProject(newProjectSettings);
|
||||
if (!result.IsSuccess())
|
||||
{
|
||||
QMessageBox::critical(this, tr("Project update failed"), tr(result.GetError().c_str()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if project path has changed and move it
|
||||
if (newProjectSettings.m_path != m_projectInfo.m_path)
|
||||
{
|
||||
if (!ProjectUtils::MoveProject(m_projectInfo.m_path, newProjectSettings.m_path))
|
||||
{
|
||||
QMessageBox::critical(this, tr("Project move failed"), tr("Failed to move project."));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
m_projectInfo = newProjectSettings;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (m_stack->currentIndex() == ScreenOrder::Gems && m_gemCatalogScreen)
|
||||
{
|
||||
// Enable or disable the gems that got adjusted in the gem catalog and apply them to the given project.
|
||||
m_gemCatalogScreen->EnableDisableGemsForProject(m_projectInfo.m_path);
|
||||
if (!m_gemCatalogScreen->EnableDisableGemsForProject(m_projectInfo.m_path))
|
||||
{
|
||||
QMessageBox::critical(this, tr("Failed to configure gems"), tr("Failed to configure gems for project."));
|
||||
return;
|
||||
}
|
||||
|
||||
shouldRebuild = true;
|
||||
}
|
||||
@@ -190,14 +173,15 @@ namespace O3DE::ProjectManager
|
||||
{
|
||||
if (m_stack->currentIndex() == ScreenOrder::Gems)
|
||||
{
|
||||
m_header->setTitle(QString(tr("Edit Project Settings: \"%1\"")).arg(m_projectInfo.m_projectName));
|
||||
|
||||
m_header->setTitle(QString(tr("Edit Project Settings: \"%1\"")).arg(m_projectInfo.GetProjectDisplayName()));
|
||||
m_header->setSubTitle(QString(tr("Configure Gems")));
|
||||
m_nextButton->setText(tr("Finalize"));
|
||||
m_nextButton->setText(tr("Save"));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_header->setTitle("");
|
||||
m_header->setSubTitle(QString(tr("Edit Project Settings: \"%1\"")).arg(m_projectInfo.m_projectName));
|
||||
m_header->setSubTitle(QString(tr("Edit Project Settings: \"%1\"")).arg(m_projectInfo.GetProjectDisplayName()));
|
||||
m_nextButton->setText(tr("Save"));
|
||||
}
|
||||
}
|
||||
@@ -207,4 +191,70 @@ namespace O3DE::ProjectManager
|
||||
m_updateSettingsScreen->SetProjectInfo(m_projectInfo);
|
||||
}
|
||||
|
||||
bool UpdateProjectCtrl::UpdateProjectSettings(bool shouldConfirm)
|
||||
{
|
||||
AZ_Assert(m_updateSettingsScreen, "Update settings screen is nullptr.")
|
||||
|
||||
ProjectInfo newProjectSettings = m_updateSettingsScreen->GetProjectInfo();
|
||||
|
||||
if (m_projectInfo != newProjectSettings)
|
||||
{
|
||||
if (shouldConfirm)
|
||||
{
|
||||
QMessageBox::StandardButton warningResult = QMessageBox::warning(
|
||||
this,
|
||||
QObject::tr("Unsaved Changes!"),
|
||||
QObject::tr("Would you like to save your changes to project settings?"),
|
||||
QMessageBox::No | QMessageBox::Yes
|
||||
);
|
||||
|
||||
if (warningResult == QMessageBox::No)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_updateSettingsScreen->Validate())
|
||||
{
|
||||
QMessageBox::critical(this, tr("Invalid project settings"), tr("Invalid project settings"));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Update project if settings changed
|
||||
{
|
||||
auto result = PythonBindingsInterface::Get()->UpdateProject(newProjectSettings);
|
||||
if (!result.IsSuccess())
|
||||
{
|
||||
QMessageBox::critical(this, tr("Project update failed"), tr(result.GetError().c_str()));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if project path has changed and move it
|
||||
if (newProjectSettings.m_path != m_projectInfo.m_path)
|
||||
{
|
||||
if (!ProjectUtils::MoveProject(m_projectInfo.m_path, newProjectSettings.m_path))
|
||||
{
|
||||
QMessageBox::critical(this, tr("Project move failed"), tr("Failed to move project."));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!newProjectSettings.m_newPreviewImagePath.isEmpty())
|
||||
{
|
||||
if (!ProjectUtils::ReplaceFile(
|
||||
QDir(newProjectSettings.m_path).filePath(newProjectSettings.m_iconPath), newProjectSettings.m_newPreviewImagePath))
|
||||
{
|
||||
QMessageBox::critical(this, tr("File replace failed"), tr("Failed to replace project preview image."));
|
||||
return false;
|
||||
}
|
||||
m_updateSettingsScreen->ResetProjectPreviewPath();
|
||||
}
|
||||
|
||||
m_projectInfo = newProjectSettings;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -46,6 +46,7 @@ namespace O3DE::ProjectManager
|
||||
private:
|
||||
void Update();
|
||||
void UpdateSettingsScreen();
|
||||
bool UpdateProjectSettings(bool shouldConfirm = false);
|
||||
|
||||
enum ScreenOrder
|
||||
{
|
||||
|
||||
@@ -11,17 +11,43 @@
|
||||
*/
|
||||
|
||||
#include <UpdateProjectSettingsScreen.h>
|
||||
#include <FormBrowseEditWidget.h>
|
||||
#include <ProjectManagerDefs.h>
|
||||
#include <FormImageBrowseEditWidget.h>
|
||||
#include <FormLineEditWidget.h>
|
||||
|
||||
#include <QVBoxLayout>
|
||||
#include <QLineEdit>
|
||||
#include <QDir>
|
||||
#include <QLabel>
|
||||
#include <QFileInfo>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
UpdateProjectSettingsScreen::UpdateProjectSettingsScreen(QWidget* parent)
|
||||
: ProjectSettingsScreen(parent)
|
||||
, m_userChangedPreview(false)
|
||||
{
|
||||
m_projectPreview = new FormImageBrowseEditWidget(tr("Project Preview"), "", this);
|
||||
m_projectPreview->lineEdit()->setReadOnly(true);
|
||||
connect(m_projectPreview->lineEdit(), &QLineEdit::textChanged, this, &ProjectSettingsScreen::Validate);
|
||||
connect(m_projectPreview->lineEdit(), &QLineEdit::textChanged, this, &UpdateProjectSettingsScreen::PreviewPathChanged);
|
||||
connect(m_projectPath->lineEdit(), &QLineEdit::textChanged, this, &UpdateProjectSettingsScreen::UpdateProjectPreviewPath);
|
||||
m_verticalLayout->addWidget(m_projectPreview);
|
||||
|
||||
QVBoxLayout* previewExtrasLayout = new QVBoxLayout(this);
|
||||
previewExtrasLayout->setAlignment(Qt::AlignLeft);
|
||||
previewExtrasLayout->setContentsMargins(50, 0, 0, 0);
|
||||
|
||||
QLabel* projectPreviewLabel = new QLabel(tr("Select an image (PNG). Minimum %1 x %2 pixels.")
|
||||
.arg(QString::number(ProjectPreviewImageWidth), QString::number(ProjectPreviewImageHeight)));
|
||||
previewExtrasLayout->addWidget(projectPreviewLabel);
|
||||
|
||||
m_projectPreviewImage = new QLabel(this);
|
||||
m_projectPreviewImage->setFixedSize(ProjectPreviewImageWidth, ProjectPreviewImageHeight);
|
||||
m_projectPreviewImage->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
|
||||
previewExtrasLayout->addWidget(m_projectPreviewImage);
|
||||
|
||||
m_verticalLayout->addLayout(previewExtrasLayout);
|
||||
}
|
||||
|
||||
ProjectManagerScreen UpdateProjectSettingsScreen::GetScreenEnum()
|
||||
@@ -29,10 +55,58 @@ namespace O3DE::ProjectManager
|
||||
return ProjectManagerScreen::UpdateProjectSettings;
|
||||
}
|
||||
|
||||
ProjectInfo UpdateProjectSettingsScreen::GetProjectInfo()
|
||||
{
|
||||
m_projectInfo.m_displayName = m_projectName->lineEdit()->text();
|
||||
m_projectInfo.m_path = m_projectPath->lineEdit()->text();
|
||||
|
||||
if (m_userChangedPreview)
|
||||
{
|
||||
m_projectInfo.m_iconPath = ProjectPreviewImagePath;
|
||||
m_projectInfo.m_newPreviewImagePath = m_projectPreview->lineEdit()->text();
|
||||
}
|
||||
return m_projectInfo;
|
||||
}
|
||||
|
||||
void UpdateProjectSettingsScreen::SetProjectInfo(const ProjectInfo& projectInfo)
|
||||
{
|
||||
m_projectName->lineEdit()->setText(projectInfo.m_projectName);
|
||||
m_projectInfo = projectInfo;
|
||||
|
||||
m_projectName->lineEdit()->setText(projectInfo.GetProjectDisplayName());
|
||||
|
||||
m_projectPath->lineEdit()->setText(projectInfo.m_path);
|
||||
UpdateProjectPreviewPath();
|
||||
}
|
||||
|
||||
void UpdateProjectSettingsScreen::UpdateProjectPreviewPath()
|
||||
{
|
||||
if (!m_userChangedPreview)
|
||||
{
|
||||
m_projectPreview->lineEdit()->setText(QDir(m_projectPath->lineEdit()->text()).filePath(m_projectInfo.m_iconPath));
|
||||
// Setting the text sets m_userChangedPreview to true
|
||||
// Set it back to false because it should only be true when changed by user
|
||||
m_userChangedPreview = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool UpdateProjectSettingsScreen::Validate()
|
||||
{
|
||||
return ProjectSettingsScreen::Validate() && ValidateProjectPreview();
|
||||
}
|
||||
|
||||
void UpdateProjectSettingsScreen::ResetProjectPreviewPath()
|
||||
{
|
||||
m_userChangedPreview = false;
|
||||
UpdateProjectPreviewPath();
|
||||
}
|
||||
|
||||
void UpdateProjectSettingsScreen::PreviewPathChanged()
|
||||
{
|
||||
m_userChangedPreview = true;
|
||||
|
||||
// Update with latest image
|
||||
m_projectPreviewImage->setPixmap(
|
||||
QPixmap(m_projectPreview->lineEdit()->text()).scaled(m_projectPreviewImage->size(), Qt::KeepAspectRatioByExpanding));
|
||||
}
|
||||
|
||||
bool UpdateProjectSettingsScreen::ValidateProjectPath()
|
||||
@@ -48,4 +122,39 @@ namespace O3DE::ProjectManager
|
||||
return projectPathIsValid;
|
||||
}
|
||||
|
||||
bool UpdateProjectSettingsScreen::ValidateProjectPreview()
|
||||
{
|
||||
bool projectPreviewIsValid = true;
|
||||
|
||||
if (m_projectPreview->lineEdit()->text().isEmpty())
|
||||
{
|
||||
projectPreviewIsValid = false;
|
||||
m_projectPreview->setErrorLabelText(tr("Please select a file."));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_userChangedPreview)
|
||||
{
|
||||
QFileInfo previewFile(m_projectPreview->lineEdit()->text());
|
||||
if (!previewFile.exists() || !previewFile.isFile())
|
||||
{
|
||||
projectPreviewIsValid = false;
|
||||
m_projectPreview->setErrorLabelText(tr("Please select a valid png file."));
|
||||
}
|
||||
else
|
||||
{
|
||||
QString fileType = previewFile.completeSuffix().toLower();
|
||||
if (fileType != "png")
|
||||
{
|
||||
projectPreviewIsValid = false;
|
||||
m_projectPreview->setErrorLabelText(tr("Please select a png image."));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_projectPreview->setErrorLabelVisible(!projectPreviewIsValid);
|
||||
return projectPreviewIsValid;
|
||||
}
|
||||
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
#include <ProjectSettingsScreen.h>
|
||||
#endif
|
||||
|
||||
QT_FORWARD_DECLARE_CLASS(QLabel)
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
class UpdateProjectSettingsScreen
|
||||
@@ -25,10 +27,26 @@ namespace O3DE::ProjectManager
|
||||
~UpdateProjectSettingsScreen() = default;
|
||||
ProjectManagerScreen GetScreenEnum() override;
|
||||
|
||||
ProjectInfo GetProjectInfo() override;
|
||||
void SetProjectInfo(const ProjectInfo& projectInfo);
|
||||
|
||||
bool Validate() override;
|
||||
|
||||
void ResetProjectPreviewPath();
|
||||
|
||||
public slots:
|
||||
void UpdateProjectPreviewPath();
|
||||
void PreviewPathChanged();
|
||||
|
||||
protected:
|
||||
bool ValidateProjectPath() override;
|
||||
virtual bool ValidateProjectPreview();
|
||||
|
||||
FormBrowseEditWidget* m_projectPreview;
|
||||
QLabel* m_projectPreviewImage;
|
||||
|
||||
ProjectInfo m_projectInfo;
|
||||
bool m_userChangedPreview; //! Did the user change the project preview path
|
||||
};
|
||||
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
set(FILES
|
||||
Source/Application.h
|
||||
Source/Application.cpp
|
||||
Source/ProjectManagerDefs.h
|
||||
Source/ScreenDefs.h
|
||||
Source/ScreenFactory.h
|
||||
Source/ScreenFactory.cpp
|
||||
@@ -25,6 +26,10 @@ set(FILES
|
||||
Source/FormLineEditWidget.cpp
|
||||
Source/FormBrowseEditWidget.h
|
||||
Source/FormBrowseEditWidget.cpp
|
||||
Source/FormFolderBrowseEditWidget.h
|
||||
Source/FormFolderBrowseEditWidget.cpp
|
||||
Source/FormImageBrowseEditWidget.h
|
||||
Source/FormImageBrowseEditWidget.cpp
|
||||
Source/PathValidator.h
|
||||
Source/PathValidator.cpp
|
||||
Source/ProjectManagerWindow.h
|
||||
@@ -82,6 +87,14 @@ set(FILES
|
||||
Source/GemCatalog/GemListHeaderWidget.cpp
|
||||
Source/GemCatalog/GemModel.h
|
||||
Source/GemCatalog/GemModel.cpp
|
||||
Source/GemCatalog/GemRequirementDialog.h
|
||||
Source/GemCatalog/GemRequirementDialog.cpp
|
||||
Source/GemCatalog/GemRequirementDelegate.h
|
||||
Source/GemCatalog/GemRequirementDelegate.cpp
|
||||
Source/GemCatalog/GemRequirementFilterProxyModel.h
|
||||
Source/GemCatalog/GemRequirementFilterProxyModel.cpp
|
||||
Source/GemCatalog/GemRequirementListView.h
|
||||
Source/GemCatalog/GemRequirementListView.cpp
|
||||
Source/GemCatalog/GemSortFilterProxyModel.h
|
||||
Source/GemCatalog/GemSortFilterProxyModel.cpp
|
||||
)
|
||||
|
||||
@@ -14,4 +14,5 @@ set(FILES
|
||||
Resources/ProjectManager.qss
|
||||
tests/ApplicationTests.cpp
|
||||
tests/main.cpp
|
||||
tests/UtilsTests.cpp
|
||||
)
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/UnitTest/TestTypes.h>
|
||||
#include <Application.h>
|
||||
#include <ProjectUtils.h>
|
||||
#include <ProjectManager_Test_Traits_Platform.h>
|
||||
|
||||
#include <QFile>
|
||||
#include <QTextStream>
|
||||
#include <QTimer>
|
||||
#include <QApplication>
|
||||
#include <QKeyEvent>
|
||||
#include <QDir>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
namespace ProjectUtils
|
||||
{
|
||||
class ProjectManagerUtilsTests
|
||||
: public ::UnitTest::ScopedAllocatorSetupFixture
|
||||
{
|
||||
public:
|
||||
ProjectManagerUtilsTests()
|
||||
{
|
||||
m_application = AZStd::make_unique<ProjectManager::Application>();
|
||||
m_application->Init(false);
|
||||
|
||||
QDir dir;
|
||||
dir.mkdir("ProjectA");
|
||||
dir.mkdir("ProjectB");
|
||||
|
||||
QFile origFile("ProjectA/origFile.txt");
|
||||
if (origFile.open(QIODevice::ReadWrite))
|
||||
{
|
||||
QTextStream stream(&origFile);
|
||||
stream << "orig" << Qt::endl;
|
||||
origFile.close();
|
||||
}
|
||||
|
||||
QFile replaceFile("ProjectA/replaceFile.txt");
|
||||
if (replaceFile.open(QIODevice::ReadWrite))
|
||||
{
|
||||
QTextStream stream(&replaceFile);
|
||||
stream << "replace" << Qt::endl;
|
||||
replaceFile.close();
|
||||
}
|
||||
}
|
||||
|
||||
~ProjectManagerUtilsTests()
|
||||
{
|
||||
QDir dirA("ProjectA");
|
||||
dirA.removeRecursively();
|
||||
|
||||
QDir dirB("ProjectB");
|
||||
dirB.removeRecursively();
|
||||
|
||||
m_application.reset();
|
||||
}
|
||||
|
||||
AZStd::unique_ptr<ProjectManager::Application> m_application;
|
||||
};
|
||||
|
||||
#if AZ_TRAIT_DISABLE_FAILED_PROJECT_MANAGER_TESTS
|
||||
TEST_F(ProjectManagerUtilsTests, DISABLED_MoveProject_Succeeds)
|
||||
#else
|
||||
TEST_F(ProjectManagerUtilsTests, MoveProject_Succeeds)
|
||||
#endif // !AZ_TRAIT_DISABLE_FAILED_PROJECT_MANAGER_TESTS
|
||||
{
|
||||
EXPECT_TRUE(MoveProject(
|
||||
QDir::currentPath() + QDir::separator() + "ProjectA",
|
||||
QDir::currentPath() + QDir::separator() + "ProjectB",
|
||||
nullptr, true));
|
||||
|
||||
QFileInfo origFile("ProjectA/origFile.txt");
|
||||
EXPECT_TRUE(!origFile.exists());
|
||||
|
||||
QFileInfo replaceFile("ProjectA/replaceFile.txt");
|
||||
EXPECT_TRUE(!replaceFile.exists());
|
||||
|
||||
QFileInfo origFileMoved("ProjectB/origFile.txt");
|
||||
EXPECT_TRUE(origFileMoved.exists() && origFileMoved.isFile());
|
||||
|
||||
QFileInfo replaceFileMoved("ProjectB/replaceFile.txt");
|
||||
EXPECT_TRUE(replaceFileMoved.exists() && replaceFileMoved.isFile());
|
||||
}
|
||||
|
||||
#if AZ_TRAIT_DISABLE_FAILED_PROJECT_MANAGER_TESTS
|
||||
TEST_F(ProjectManagerUtilsTests, DISABLED_ReplaceFile_Succeeds)
|
||||
#else
|
||||
TEST_F(ProjectManagerUtilsTests, ReplaceFile_Succeeds)
|
||||
#endif // !AZ_TRAIT_DISABLE_FAILED_PROJECT_MANAGER_TESTS
|
||||
{
|
||||
EXPECT_TRUE(ReplaceFile("ProjectA/origFile.txt", "ProjectA/replaceFile.txt", nullptr, false));
|
||||
|
||||
QFile origFile("ProjectA/origFile.txt");
|
||||
if (origFile.open(QIODevice::ReadOnly))
|
||||
{
|
||||
QTextStream stream(&origFile);
|
||||
QString line = stream.readLine();
|
||||
EXPECT_EQ(line, "replace");
|
||||
|
||||
origFile.close();
|
||||
}
|
||||
else
|
||||
{
|
||||
FAIL();
|
||||
}
|
||||
}
|
||||
} // namespace ProjectUtils
|
||||
} // namespace O3DE::ProjectManager
|
||||
@@ -146,12 +146,12 @@ namespace AWSClientAuth
|
||||
|
||||
void AWSCognitoAuthenticationProvider::DeviceCodeGrantSignInAsync()
|
||||
{
|
||||
AZ_Assert(true, "Not supported");
|
||||
AZ_Assert(false, "Not supported");
|
||||
}
|
||||
|
||||
void AWSCognitoAuthenticationProvider::DeviceCodeGrantConfirmSignInAsync()
|
||||
{
|
||||
AZ_Assert(true, "Not supported");
|
||||
AZ_Assert(false, "Not supported");
|
||||
}
|
||||
|
||||
void AWSCognitoAuthenticationProvider::RefreshTokensAsync()
|
||||
|
||||
@@ -53,7 +53,7 @@ namespace AWSClientAuth
|
||||
|
||||
if (!m_settingsRegistry->MergeSettingsFile(resolvedPath.data(), AZ::SettingsRegistryInterface::Format::JsonMergePatch))
|
||||
{
|
||||
AZ_Error("AuthenticationProviderManager", true, "Error merging settings registry for path: %s", resolvedPath.data());
|
||||
AZ_Error("AuthenticationProviderManager", false, "Error merging settings registry for path: %s", resolvedPath.data());
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -199,7 +199,7 @@ namespace AWSClientAuth
|
||||
{
|
||||
return enumValue.value();
|
||||
}
|
||||
AZ_Warning("AuthenticationProviderManager", true, "Incorrect string value for enum: %s", name.c_str());
|
||||
AZ_Warning("AuthenticationProviderManager", false, "Incorrect string value for enum: %s", name.c_str());
|
||||
return ProviderNameEnum::None;
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ namespace AWSClientAuth
|
||||
{
|
||||
if (!settingsRegistry.lock()->GetObject(m_settings.get(), azrtti_typeid(m_settings.get()), GoogleSettingsPath))
|
||||
{
|
||||
AZ_Warning("AWSCognitoAuthenticationProvider", true, "Failed to get Google settings object for path %s", GoogleSettingsPath);
|
||||
AZ_Warning("AWSCognitoAuthenticationProvider", false, "Failed to get Google settings object for path %s", GoogleSettingsPath);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -49,21 +49,21 @@ namespace AWSClientAuth
|
||||
{
|
||||
AZ_UNUSED(username);
|
||||
AZ_UNUSED(password);
|
||||
AZ_Assert(true, "Not supported");
|
||||
AZ_Assert(false, "Not supported");
|
||||
}
|
||||
|
||||
void GoogleAuthenticationProvider::PasswordGrantMultiFactorSignInAsync(const AZStd::string& username, const AZStd::string& password)
|
||||
{
|
||||
AZ_UNUSED(username);
|
||||
AZ_UNUSED(password);
|
||||
AZ_Assert(true, "Not supported");
|
||||
AZ_Assert(false, "Not supported");
|
||||
}
|
||||
|
||||
void GoogleAuthenticationProvider::PasswordGrantMultiFactorConfirmSignInAsync(const AZStd::string& username, const AZStd::string& confirmationCode)
|
||||
{
|
||||
AZ_UNUSED(username);
|
||||
AZ_UNUSED(confirmationCode);
|
||||
AZ_Assert(true, "Not supported");
|
||||
AZ_Assert(false, "Not supported");
|
||||
}
|
||||
|
||||
// Call Google authentication provider device code end point.
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace AWSClientAuth
|
||||
{
|
||||
if (!settingsRegistry.lock()->GetObject(m_settings.get(), azrtti_typeid(m_settings.get()), LwaSettingsPath))
|
||||
{
|
||||
AZ_Warning("AWSCognitoAuthenticationProvider", true, "Failed to get login with Amazon settings object for path %s", LwaSettingsPath);
|
||||
AZ_Warning("AWSCognitoAuthenticationProvider", false, "Failed to get login with Amazon settings object for path %s", LwaSettingsPath);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -48,21 +48,21 @@ namespace AWSClientAuth
|
||||
{
|
||||
AZ_UNUSED(username);
|
||||
AZ_UNUSED(password);
|
||||
AZ_Assert(true, "Not supported");
|
||||
AZ_Assert(false, "Not supported");
|
||||
}
|
||||
|
||||
void LWAAuthenticationProvider::PasswordGrantMultiFactorSignInAsync(const AZStd::string& username, const AZStd::string& password)
|
||||
{
|
||||
AZ_UNUSED(username);
|
||||
AZ_UNUSED(password);
|
||||
AZ_Assert(true, "Not supported");
|
||||
AZ_Assert(false, "Not supported");
|
||||
}
|
||||
|
||||
void LWAAuthenticationProvider::PasswordGrantMultiFactorConfirmSignInAsync(const AZStd::string& username, const AZStd::string& confirmationCode)
|
||||
{
|
||||
AZ_UNUSED(username);
|
||||
AZ_UNUSED(confirmationCode);
|
||||
AZ_Assert(true, "Not supported");
|
||||
AZ_Assert(false, "Not supported");
|
||||
}
|
||||
|
||||
// Call LWA authentication provider device code end point.
|
||||
|
||||
@@ -149,7 +149,7 @@ namespace AWSClientAuth
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Warning("AWSCognitoAuthorizationController", true, "No logins found. Fetching anonymous/unauthenticated credentials");
|
||||
AZ_Warning("AWSCognitoAuthorizationController", false, "No logins found. Fetching anonymous/unauthenticated credentials");
|
||||
}
|
||||
|
||||
AZ::JobContext* jobContext = nullptr;
|
||||
@@ -277,7 +277,7 @@ namespace AWSClientAuth
|
||||
// Check anonymous credentials as they are optional settings in Cognito Identity pool.
|
||||
if (!m_cognitoCachingAnonymousCredentialsProvider->GetAWSCredentials().IsEmpty())
|
||||
{
|
||||
AZ_Warning("AWSCognitoAuthorizationCredentialHandler", true, "No logins found. Using Anonymous credential provider");
|
||||
AZ_Warning("AWSCognitoAuthorizationCredentialHandler", false, "No logins found. Using Anonymous credential provider");
|
||||
return m_cognitoCachingAnonymousCredentialsProvider;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -257,5 +257,5 @@ TEST_F(AuthenticationProviderManagerScriptCanvasTest, Initialize_Fail_InvalidPat
|
||||
{
|
||||
AZ_TEST_START_TRACE_SUPPRESSION;
|
||||
ASSERT_FALSE(m_mockController->Initialize(m_enabledProviderNames, ""));
|
||||
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
|
||||
AZ_TEST_STOP_TRACE_SUPPRESSION(2);
|
||||
}
|
||||
|
||||
@@ -256,5 +256,5 @@ TEST_F(AuthenticationProviderManagerTest, Initialize_Fail_InvalidPath)
|
||||
{
|
||||
AZ_TEST_START_TRACE_SUPPRESSION;
|
||||
ASSERT_FALSE(m_mockController->Initialize(m_enabledProviderNames, ""));
|
||||
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
|
||||
AZ_TEST_STOP_TRACE_SUPPRESSION(2);
|
||||
}
|
||||
|
||||
@@ -51,6 +51,13 @@ To add additional dependencies, for example other CDK libraries, just add
|
||||
them to your requirements.txt file and rerun the `..\..\..\Lumberyard\python\pip.cmd install -r .\Gems\AWSClientAuth\cdk\requirements.txt`
|
||||
command.
|
||||
|
||||
|
||||
## Update Authorization Permissions
|
||||
To give permissions to call AWS resources, please update CognitoIdentityPoolRole class with correct policy statements.
|
||||
|
||||
An example IAM permission policy is provided to grant both authenticated and unauthenticated the permission to list S3 buckets in the project.
|
||||
However, it is expected that developers replace these permissions with those required by your users to use your resources.
|
||||
|
||||
## Useful commands
|
||||
|
||||
* `cdk ls` list all stacks in the app
|
||||
|
||||
@@ -53,14 +53,17 @@ class CognitoIdentityPoolRole:
|
||||
}
|
||||
}, assume_role_action='sts:AssumeRoleWithWebIdentity'))
|
||||
|
||||
# basic permissions
|
||||
# The above role is created for developers to add custom permissions that they need to provide authorized
|
||||
# clients. Developers should update the policy statements below to add their required permissions.
|
||||
# As an example s3:ListBuckets permissions are provided.
|
||||
# Note: There must be at least one policy statement here.
|
||||
stack_statement = iam.PolicyStatement(
|
||||
actions=[
|
||||
's3:ListBuckets'
|
||||
],
|
||||
effect=iam.Effect.ALLOW,
|
||||
resources=[
|
||||
'*'
|
||||
f'arn:aws:s3:::{project_name}/*'
|
||||
],
|
||||
sid=name_utils.format_aws_resource_sid(feature_name, project_name, iam.PolicyStatement.__name__)
|
||||
)
|
||||
|
||||
@@ -32,12 +32,18 @@ class CognitoUserPoolSMSRole:
|
||||
name_utils.format_aws_resource_id(feature_name, project_name, env, iam.Role.__name__),
|
||||
description='Role permissions used by Cognito user pool to send sms',
|
||||
assumed_by=iam.ServicePrincipal("cognito-idp.amazonaws.com"),
|
||||
# Deny all others and then allow only for the current sms role.
|
||||
inline_policies={
|
||||
'SNSRoleInlinePolicy':
|
||||
iam.PolicyDocument(
|
||||
statements=[
|
||||
# SMS role will be used by CognitoIDP tp allow to publish to SNS topic owned by CognitoIDP
|
||||
# team to push a sms.
|
||||
# Need to use * as the resource name used by CognitoIDP principal service is unknown.
|
||||
iam.PolicyStatement(
|
||||
actions=["sns:Publish"], resources=["*"]
|
||||
effect=iam.Effect.ALLOW,
|
||||
actions=['sns:Publish'],
|
||||
resources=['*']
|
||||
)
|
||||
]
|
||||
)
|
||||
|
||||
@@ -72,22 +72,43 @@ namespace AZ
|
||||
static constexpr uint32_t ShaderVariantJobVariantParam = 3;
|
||||
static constexpr uint32_t ShouldExitEarlyFromProcessJobParam = 4;
|
||||
|
||||
static void AddShaderAssetJobDependency(
|
||||
AssetBuilderSDK::JobDescriptor& jobDescriptor,
|
||||
const AssetBuilderSDK::PlatformInfo& platformInfo,
|
||||
const AZStd::string& shaderVariantListFilePath,
|
||||
const AZStd::string& shaderFilePath)
|
||||
//! Adds source file dependencies for every place a referenced file may appear, and detects if one of
|
||||
//! those possible paths resolves to the expected file.
|
||||
//! @param currentFilePath - the full path to the file being processed
|
||||
//! @param referencedParentPath - the path to a reference file, which may be relative to the @currentFilePath, or may be a full asset path.
|
||||
//! @param sourceFileDependencies - new source file dependencies will be added to this list
|
||||
//! @param foundSourceFile - if one of the source file dependencies is found, the highest priority one will be indicated here, otherwise this will be empty.
|
||||
//! @return true if the referenced file was found and @foundSourceFile was set
|
||||
bool LocateReferencedSourceFile(
|
||||
AZStd::string_view currentFilePath, AZStd::string_view referencedParentPath,
|
||||
AZStd::vector<AssetBuilderSDK::SourceFileDependency>& sourceFileDependencies,
|
||||
AZStd::string& foundSourceFile)
|
||||
{
|
||||
AZStd::vector<AZStd::string> possibleDependencies = AZ::RPI::AssetUtils::GetPossibleDepenencyPaths(shaderVariantListFilePath, shaderFilePath);
|
||||
foundSourceFile.clear();
|
||||
|
||||
bool found = false;
|
||||
|
||||
AZStd::vector<AZStd::string> possibleDependencies = RPI::AssetUtils::GetPossibleDepenencyPaths(currentFilePath, referencedParentPath);
|
||||
for (auto& file : possibleDependencies)
|
||||
{
|
||||
AssetBuilderSDK::JobDependency jobDependency;
|
||||
jobDependency.m_jobKey = ShaderAssetBuilder::ShaderAssetBuilderJobKey;
|
||||
jobDependency.m_platformIdentifier = platformInfo.m_identifier;
|
||||
jobDependency.m_type = AssetBuilderSDK::JobDependencyType::Order;
|
||||
jobDependency.m_sourceFile.m_sourceFileDependencyPath = file;
|
||||
jobDescriptor.m_jobDependencyList.push_back(jobDependency);
|
||||
AssetBuilderSDK::SourceFileDependency sourceFileDependency;
|
||||
sourceFileDependency.m_sourceFileDependencyPath = file;
|
||||
sourceFileDependencies.push_back(sourceFileDependency);
|
||||
|
||||
if (!found)
|
||||
{
|
||||
AZ::Data::AssetInfo sourceInfo;
|
||||
AZStd::string watchFolder;
|
||||
AzToolsFramework::AssetSystemRequestBus::BroadcastResult(found, &AzToolsFramework::AssetSystem::AssetSystemRequest::GetSourceInfoBySourcePath, file.c_str(), sourceInfo, watchFolder);
|
||||
|
||||
if (found)
|
||||
{
|
||||
foundSourceFile = file;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
//! Returns true if @sourceFileFullPath starts with a valid asset processor scan folder, false otherwise.
|
||||
@@ -334,6 +355,9 @@ namespace AZ
|
||||
response.m_result = AssetBuilderSDK::CreateJobsResultCode::Success;
|
||||
return;
|
||||
}
|
||||
|
||||
AZStd::string foundShaderFile;
|
||||
LocateReferencedSourceFile(variantListFullPath, shaderVariantList.m_shaderFilePath, response.m_sourceFileDependencyList, foundShaderFile);
|
||||
|
||||
for (const AssetBuilderSDK::PlatformInfo& info : request.m_enabledPlatforms)
|
||||
{
|
||||
@@ -349,8 +373,16 @@ namespace AZ
|
||||
|
||||
jobDescriptor.m_jobKey = GetShaderVariantTreeAssetJobKey();
|
||||
jobDescriptor.SetPlatformIdentifier(info.m_identifier.data());
|
||||
|
||||
AddShaderAssetJobDependency(jobDescriptor, info, variantListFullPath, shaderVariantList.m_shaderFilePath);
|
||||
|
||||
if (!foundShaderFile.empty())
|
||||
{
|
||||
AssetBuilderSDK::JobDependency jobDependency;
|
||||
jobDependency.m_jobKey = ShaderAssetBuilder::ShaderAssetBuilderJobKey;
|
||||
jobDependency.m_platformIdentifier = info.m_identifier;
|
||||
jobDependency.m_type = AssetBuilderSDK::JobDependencyType::Order;
|
||||
jobDependency.m_sourceFile.m_sourceFileDependencyPath = foundShaderFile;
|
||||
jobDescriptor.m_jobDependencyList.push_back(jobDependency);
|
||||
}
|
||||
|
||||
jobDescriptor.m_jobParameters.emplace(ShaderSourceFilePathJobParam, shaderSourceFileFullPath);
|
||||
|
||||
|
||||
@@ -203,6 +203,16 @@ namespace AZ
|
||||
// queue up AzslBuilder dependencies:
|
||||
for (RHI::ShaderPlatformInterface* shaderPlatformInterface : platformInterfaces)
|
||||
{
|
||||
const bool isAzsli = AzFramework::StringFunc::Path::IsExtension(fullPath.c_str(), "azsli");
|
||||
if (isAzsli)
|
||||
{
|
||||
auto skipCheck = ShaderBuilderUtility::ShouldSkipFileForSrgProcessing(SrgLayoutBuilderName, fullPath);
|
||||
if (skipCheck != ShaderBuilderUtility::SrgSkipFileResult::ContinueProcess)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
AddAzslBuilderJobDependency(jobDescriptor, info.m_identifier, shaderPlatformInterface->GetAPIName().GetCStr(), fullPath);
|
||||
}
|
||||
response.m_createJobOutputs.push_back(jobDescriptor);
|
||||
|
||||
@@ -1465,14 +1465,9 @@
|
||||
"file": "./EnhancedPBR_DepthPass_WithPS.shader",
|
||||
"tag": "DepthPass_WithPS"
|
||||
},
|
||||
// [GFX TODO][ATOM-4726] Use an "isSkinnedMesh" external material property and a functor that enables/disables the appropriate motion-vector shader
|
||||
{
|
||||
"file": "Shaders/MotionVector/StaticMeshMotionVector.shader",
|
||||
"tag": "StaticMeshMotionVector"
|
||||
},
|
||||
{
|
||||
"file": "Shaders/MotionVector/SkinnedMeshMotionVector.shader",
|
||||
"tag": "SkinnedMeshMotionVector"
|
||||
"file": "Shaders/MotionVector/MeshMotionVector.shader",
|
||||
"tag": "MeshMotionVector"
|
||||
},
|
||||
// Used by the light culling system to produce accurate depth bounds for this object when it uses blended transparency
|
||||
{
|
||||
@@ -1669,4 +1664,3 @@
|
||||
"UV1": "Unwrapped"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -980,14 +980,9 @@
|
||||
"file": "Shaders/Depth/DepthPass.shader",
|
||||
"tag": "DepthPass"
|
||||
},
|
||||
// [GFX TODO][ATOM-4726] Use an "isSkinnedMesh" external material property and a functor that enables/disables the appropriate motion-vector shader
|
||||
{
|
||||
"file": "Shaders/MotionVector/StaticMeshMotionVector.shader",
|
||||
"tag": "StaticMeshMotionVector"
|
||||
},
|
||||
{
|
||||
"file": "Shaders/MotionVector/SkinnedMeshMotionVector.shader",
|
||||
"tag": "SkinnedMeshMotionVector"
|
||||
"file": "Shaders/MotionVector/MeshMotionVector.shader",
|
||||
"tag": "MeshMotionVector"
|
||||
}
|
||||
],
|
||||
"functors": [
|
||||
|
||||
@@ -2632,14 +2632,9 @@
|
||||
"file": "./StandardMultilayerPBR_DepthPass_WithPS.shader",
|
||||
"tag": "DepthPass_WithPS"
|
||||
},
|
||||
// [GFX TODO][ATOM-4726] Use an "isSkinnedMesh" external material property and a functor that enables/disables the appropriate motion-vector shader
|
||||
{
|
||||
"file": "Shaders/MotionVector/StaticMeshMotionVector.shader",
|
||||
"tag": "StaticMeshMotionVector"
|
||||
},
|
||||
{
|
||||
"file": "Shaders/MotionVector/SkinnedMeshMotionVector.shader",
|
||||
"tag": "SkinnedMeshMotionVector"
|
||||
"file": "Shaders/MotionVector/MeshMotionVector.shader",
|
||||
"tag": "MeshMotionVector"
|
||||
}
|
||||
],
|
||||
"functors": [
|
||||
@@ -3103,4 +3098,3 @@
|
||||
"UV1": "Unwrapped"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1030,14 +1030,9 @@
|
||||
"file": "./StandardPBR_DepthPass_WithPS.shader",
|
||||
"tag": "DepthPass_WithPS"
|
||||
},
|
||||
// [GFX TODO][ATOM-4726] Use an "isSkinnedMesh" external material property and a functor that enables/disables the appropriate motion-vector shader
|
||||
{
|
||||
"file": "Shaders/MotionVector/StaticMeshMotionVector.shader",
|
||||
"tag": "StaticMeshMotionVector"
|
||||
},
|
||||
{
|
||||
"file": "Shaders/MotionVector/SkinnedMeshMotionVector.shader",
|
||||
"tag": "SkinnedMeshMotionVector"
|
||||
"file": "Shaders/MotionVector/MeshMotionVector.shader",
|
||||
"tag": "MeshMotionVector"
|
||||
},
|
||||
// Used by the light culling system to produce accurate depth bounds for this object when it uses blended transparency
|
||||
{
|
||||
@@ -1187,4 +1182,3 @@
|
||||
"UV1": "Unwrapped"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
@@ -427,6 +427,7 @@ float DirectionalLightShadow::SamplePcfBicubic()
|
||||
shadowCoord.y >= 0. && shadowCoord.y * size < size - PixelMargin &&
|
||||
shadowCoord.z < 1. - DepthMargin)
|
||||
{
|
||||
m_debugInfo.m_cascadeIndex = indexOfCascade;
|
||||
return SamplePcfBicubic(shadowCoord, indexOfCascade);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <scenesrg.srgi>
|
||||
#include <viewsrg.srgi>
|
||||
|
||||
#include <Atom/Features/PBR/DefaultObjectSrg.azsli>
|
||||
#include <Atom/RPI/ShaderResourceGroups/DefaultDrawSrg.azsli>
|
||||
|
||||
struct VSInput
|
||||
{
|
||||
float3 m_position : POSITION;
|
||||
|
||||
// This gets set automatically by the system at runtime only if it's available.
|
||||
// There is a soft naming convention that associates this with o_prevPosition_isBound, which will be set to true whenever m_optional_prevPosition is available.
|
||||
// (search "m_optional_" in ShaderVariantAssetBuilder for details on the naming convention).
|
||||
// [GFX TODO][ATOM-14475]: Come up with a more elegant way to associate the isBound flag with the input stream.
|
||||
// Vertex position of last frame to capture small scale motion due to vertex animation
|
||||
float3 m_optional_prevPosition : POSITIONT;
|
||||
};
|
||||
|
||||
struct VSOutput
|
||||
{
|
||||
float4 m_position : SV_Position;
|
||||
float3 m_worldPos : TEXCOORD0;
|
||||
float3 m_worldPosPrev: TEXCOORD1;
|
||||
};
|
||||
|
||||
struct PSOutput
|
||||
{
|
||||
float2 m_motion : SV_Target0;
|
||||
};
|
||||
|
||||
// Indicates whether the vertex input struct's "m_optional_prevPosition" is bound. If false, it is not safe to read from m_optional_prevPosition.
|
||||
// This option gets set automatically by the system at runtime; there is a soft naming convention that associates it with m_optional_prevPosition.
|
||||
// (search "m_optional_" in ShaderVariantAssetBuilder for details on the naming convention).
|
||||
// [GFX TODO][ATOM-14475]: Come up with a more elegant way to associate the isBound flag with the input stream.
|
||||
option bool o_prevPosition_isBound;
|
||||
|
||||
VSOutput MainVS(VSInput IN)
|
||||
{
|
||||
VSOutput OUT;
|
||||
|
||||
OUT.m_worldPos = mul(SceneSrg::GetObjectToWorldMatrix(ObjectSrg::m_objectId), float4(IN.m_position, 1.0)).xyz;
|
||||
OUT.m_position = mul(ViewSrg::m_viewProjectionMatrix, float4(OUT.m_worldPos, 1.0));
|
||||
|
||||
if (o_prevPosition_isBound)
|
||||
{
|
||||
OUT.m_worldPosPrev = mul(SceneSrg::GetObjectToWorldMatrixPrev(ObjectSrg::m_objectId), float4(IN.m_optional_prevPosition, 1.0)).xyz;
|
||||
}
|
||||
else
|
||||
{
|
||||
OUT.m_worldPosPrev = mul(SceneSrg::GetObjectToWorldMatrixPrev(ObjectSrg::m_objectId), float4(IN.m_position, 1.0)).xyz;
|
||||
}
|
||||
|
||||
return OUT;
|
||||
}
|
||||
|
||||
PSOutput MainPS(VSOutput IN)
|
||||
{
|
||||
PSOutput OUT;
|
||||
|
||||
// Current clip position
|
||||
float4 clipPos = mul(ViewSrg::m_viewProjectionMatrix, float4(IN.m_worldPos, 1.0));
|
||||
|
||||
// Reprojected last frame's clip position, for skinned mesh it also implies last key frame
|
||||
float4 clipPosPrev = mul(ViewSrg::m_viewProjectionPrevMatrix, float4(IN.m_worldPosPrev, 1.0));
|
||||
|
||||
float2 motion = (clipPos.xy / clipPos.w - clipPosPrev.xy / clipPosPrev.w) * 0.5;
|
||||
|
||||
OUT.m_motion = motion;
|
||||
|
||||
// Flip y to line up with uv coordinates
|
||||
OUT.m_motion.y = -OUT.m_motion.y;
|
||||
|
||||
return OUT;
|
||||
}
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"Source" : "StaticMeshMotionVector",
|
||||
"Source" : "MeshMotionVector",
|
||||
|
||||
"DepthStencilState" : {
|
||||
"Depth" : { "Enable" : true, "CompareFunc" : "GreaterEqual" }
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <scenesrg.srgi>
|
||||
#include <viewsrg.srgi>
|
||||
|
||||
#include <Atom/Features/PBR/DefaultObjectSrg.azsli>
|
||||
#include <Atom/RPI/ShaderResourceGroups/DefaultDrawSrg.azsli>
|
||||
|
||||
struct VSOutput
|
||||
{
|
||||
float4 m_position : SV_Position;
|
||||
float3 m_worldPos : TEXCOORD0;
|
||||
float3 m_worldPosPrev: TEXCOORD1;
|
||||
};
|
||||
|
||||
struct PSOutput
|
||||
{
|
||||
float2 m_motion : SV_Target0;
|
||||
};
|
||||
|
||||
PSOutput MainPS(VSOutput IN)
|
||||
{
|
||||
PSOutput OUT;
|
||||
|
||||
// Current clip position
|
||||
float4 clipPos = mul(ViewSrg::m_viewProjectionMatrix, float4(IN.m_worldPos, 1.0));
|
||||
|
||||
// Reprojected last frame's clip position, for skinned mesh it also implies last key frame
|
||||
float4 clipPosPrev = mul(ViewSrg::m_viewProjectionPrevMatrix, float4(IN.m_worldPosPrev, 1.0));
|
||||
|
||||
float2 motion = (clipPos.xy / clipPos.w - clipPosPrev.xy / clipPosPrev.w) * 0.5;
|
||||
|
||||
OUT.m_motion = motion;
|
||||
|
||||
// Flip y to line up with uv coordinates
|
||||
OUT.m_motion.y = -OUT.m_motion.y;
|
||||
|
||||
return OUT;
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "./MeshMotionVectorCommon.azsli"
|
||||
|
||||
struct VSInput
|
||||
{
|
||||
float3 m_position : POSITION;
|
||||
// Vertex position of last frame to capture small scale motion due to vertex animation
|
||||
float3 m_prevPosition : POSITIONT;
|
||||
};
|
||||
|
||||
VSOutput MainVS(VSInput IN)
|
||||
{
|
||||
VSOutput OUT;
|
||||
|
||||
OUT.m_worldPos = mul(SceneSrg::GetObjectToWorldMatrix(ObjectSrg::m_objectId), float4(IN.m_position, 1.0)).xyz;
|
||||
OUT.m_position = mul(ViewSrg::m_viewProjectionMatrix, float4(OUT.m_worldPos, 1.0));
|
||||
OUT.m_worldPosPrev = mul(SceneSrg::GetObjectToWorldMatrixPrev(ObjectSrg::m_objectId), float4(IN.m_prevPosition, 1.0)).xyz;
|
||||
|
||||
return OUT;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
{
|
||||
"Source" : "SkinnedMeshMotionVector",
|
||||
|
||||
"DepthStencilState" : {
|
||||
"Depth" : { "Enable" : true, "CompareFunc" : "GreaterEqual" }
|
||||
},
|
||||
|
||||
"DrawList" : "motion",
|
||||
|
||||
"ProgramSettings":
|
||||
{
|
||||
"EntryPoints":
|
||||
[
|
||||
{
|
||||
"name": "MainVS",
|
||||
"type": "Vertex"
|
||||
},
|
||||
{
|
||||
"name": "MainPS",
|
||||
"type": "Fragment"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -354,11 +354,8 @@ set(FILES
|
||||
Shaders/MorphTargets/MorphTargetSRG.azsli
|
||||
Shaders/MotionVector/CameraMotionVector.azsl
|
||||
Shaders/MotionVector/CameraMotionVector.shader
|
||||
Shaders/MotionVector/MeshMotionVectorCommon.azsli
|
||||
Shaders/MotionVector/SkinnedMeshMotionVector.azsl
|
||||
Shaders/MotionVector/SkinnedMeshMotionVector.shader
|
||||
Shaders/MotionVector/StaticMeshMotionVector.azsl
|
||||
Shaders/MotionVector/StaticMeshMotionVector.shader
|
||||
Shaders/MotionVector/MeshMotionVector.azsl
|
||||
Shaders/MotionVector/MeshMotionVector.shader
|
||||
Shaders/PostProcessing/AcesOutputTransformLut.azsl
|
||||
Shaders/PostProcessing/AcesOutputTransformLut.shader
|
||||
Shaders/PostProcessing/ApplyShaperLookupTable.azsl
|
||||
|
||||
@@ -72,7 +72,6 @@ namespace AZ
|
||||
void UpdateDrawPackets(bool forceUpdate = false);
|
||||
void BuildCullable();
|
||||
void UpdateCullBounds(const TransformServiceFeatureProcessor* transformService);
|
||||
void SelectMotionVectorShader(Data::Instance<RPI::Material> material);
|
||||
void UpdateObjectSrg();
|
||||
bool MaterialRequiresForwardPassIblSpecular(Data::Instance<RPI::Material> material) const;
|
||||
|
||||
|
||||
-1
@@ -32,7 +32,6 @@ namespace AZ
|
||||
using RequiresCloneCallback = AZStd::function<bool(const Data::Asset<RPI::ModelAsset>& modelAsset)>;
|
||||
|
||||
Data::Asset<RPI::ModelAsset> m_modelAsset;
|
||||
bool m_isSkinnedMeshWithMotion = false;
|
||||
bool m_isRayTracingEnabled = true;
|
||||
bool m_useForwardPassIblSpecular = false;
|
||||
RequiresCloneCallback m_requiresCloneCallback = {};
|
||||
|
||||
@@ -158,11 +158,6 @@ namespace AZ
|
||||
MeshHandle meshDataHandle = m_meshData.emplace();
|
||||
|
||||
meshDataHandle->m_descriptor = descriptor;
|
||||
|
||||
// Always disable ray tracing flag on skinned meshes
|
||||
// [GFX TODO][ATOM-13067] Enable raytracing on skinned meshes
|
||||
meshDataHandle->m_descriptor.m_isRayTracingEnabled &= !descriptor.m_isSkinnedMeshWithMotion;
|
||||
|
||||
meshDataHandle->m_scene = GetParentScene();
|
||||
meshDataHandle->m_materialAssignments = materials;
|
||||
meshDataHandle->m_objectId = m_transformService->ReserveObjectId();
|
||||
@@ -665,8 +660,6 @@ namespace AZ
|
||||
}
|
||||
}
|
||||
|
||||
SelectMotionVectorShader(material);
|
||||
|
||||
// setup the mesh draw packet
|
||||
RPI::MeshDrawPacket drawPacket(modelLod, meshIndex, material, m_shaderResourceGroup, materialAssignment.m_matModUvOverrides);
|
||||
|
||||
@@ -1091,29 +1084,6 @@ namespace AZ
|
||||
m_cullBoundsNeedsUpdate = false;
|
||||
}
|
||||
|
||||
void MeshDataInstance::SelectMotionVectorShader(Data::Instance<RPI::Material> material)
|
||||
{
|
||||
// Two motion vector shaders are defined in the material for static mesh (only animated by transform matrix)
|
||||
// and skinned mesh (per vertex animation) respectively, it's because they have different input signatures
|
||||
// (skinned mesh needs two streaming channels while static mesh only needs one) that cannot be addressed by shader option
|
||||
// itself. Therefore this function is used to pick one to use and disable the other one depending on the type of the mesh
|
||||
// so it won't cause errors due to missing input streaming channel.
|
||||
|
||||
//[GFX TODO][ATOM-4726] Replace this with a "isSkinnedMesh" external material property and a functor that enables/disables the appropriate shader
|
||||
for (auto& shaderItem : material->GetShaderCollection())
|
||||
{
|
||||
if (shaderItem.GetShaderAsset()->GetName() == Name{ "StaticMeshMotionVector" } && m_descriptor.m_isSkinnedMeshWithMotion)
|
||||
{
|
||||
shaderItem.SetEnabled(false);
|
||||
}
|
||||
|
||||
if (shaderItem.GetShaderAsset()->GetName() == Name{ "SkinnedMeshMotionVector" } && (!m_descriptor.m_isSkinnedMeshWithMotion))
|
||||
{
|
||||
shaderItem.SetEnabled(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MeshDataInstance::UpdateObjectSrg()
|
||||
{
|
||||
if (!m_shaderResourceGroup)
|
||||
|
||||
+6
-2
@@ -47,8 +47,12 @@ namespace AZ
|
||||
m_updateSrg = true;
|
||||
}
|
||||
|
||||
params.m_viewportState = RHI::Viewport(0, static_cast<float>(m_imageSize.m_width), 0, static_cast<float>(m_imageSize.m_height));
|
||||
params.m_scissorState = RHI::Scissor(0, 0, m_imageSize.m_width, m_imageSize.m_height);
|
||||
float inverseScale = 1.0f / m_outputScale;
|
||||
uint32_t outputWidth = m_imageSize.m_width * inverseScale;
|
||||
uint32_t outputHeight = m_imageSize.m_height * inverseScale;
|
||||
|
||||
params.m_viewportState = RHI::Viewport(0, static_cast<float>(outputWidth), 0, static_cast<float>(outputHeight));
|
||||
params.m_scissorState = RHI::Scissor(0, 0, outputWidth, outputHeight);
|
||||
|
||||
FullscreenTrianglePass::FrameBeginInternal(params);
|
||||
}
|
||||
|
||||
@@ -66,6 +66,7 @@ namespace AZ
|
||||
//! it's possible that b.json could be found in either MyGem/Assets/Foo/Bar/a.json or in MyGem/Assets/Bar/a.json.
|
||||
//! @param originatingSourceFilePath Path to a file that references referencedSourceFilePath. May be absolute or relative to asset-root.
|
||||
//! @param referencedSourceFilePath The referenced path as it appears in the originating file. May be relative to the originating file location or relative to asset-root.
|
||||
//! @return the list of possible paths, ordered from highest priority to lowest priority
|
||||
AZStd::vector<AZStd::string> GetPossibleDepenencyPaths(const AZStd::string& originatingSourceFilePath, const AZStd::string& referencedSourceFilePath);
|
||||
|
||||
// Definitions...
|
||||
|
||||
@@ -105,7 +105,6 @@ namespace AZ
|
||||
ChangeId GetCurrentChangeId() const;
|
||||
|
||||
//! Return the set of shaders to be run by this material.
|
||||
ShaderCollection& GetShaderCollection();
|
||||
const ShaderCollection& GetShaderCollection() const;
|
||||
|
||||
//! Attempts to set the value of a system-level shader option that is controlled by this material.
|
||||
|
||||
@@ -67,16 +67,41 @@ namespace AZ
|
||||
BusDisconnect();
|
||||
}
|
||||
|
||||
void AddPossibleJobDependencies(const char* jobKey, AZStd::string_view currentFilePath, AZStd::string_view referencedParentPath, AZStd::vector<AssetBuilderSDK::JobDependency>& jobDependencies)
|
||||
//! Adds all relevant dependencies for a referenced source file, considering that the path might be relative to the original file location or a full asset path.
|
||||
//! This will usually include multiple source dependencies and a single job dependency, but will include only source dependencies if the file is not found.
|
||||
//! Note the AssetBuilderSDK::JobDependency::m_platformIdentifier will not be set by this function. The calling code must set this value before passing back
|
||||
//! to the AssetBuilderSDK::CreateJobsResponse.
|
||||
void AddPossibleDependencies(
|
||||
AZStd::string_view currentFilePath, AZStd::string_view referencedParentPath,
|
||||
AZStd::vector<AssetBuilderSDK::SourceFileDependency>& sourceFileDependencies,
|
||||
const char* jobKey, AZStd::vector<AssetBuilderSDK::JobDependency>& jobDependencies)
|
||||
{
|
||||
AZStd::vector<AZStd::string> possibleDependencies = AssetUtils::GetPossibleDepenencyPaths(currentFilePath, referencedParentPath);
|
||||
bool dependencyFileFound = false;
|
||||
|
||||
AZStd::vector<AZStd::string> possibleDependencies = RPI::AssetUtils::GetPossibleDepenencyPaths(currentFilePath, referencedParentPath);
|
||||
for (auto& file : possibleDependencies)
|
||||
{
|
||||
AssetBuilderSDK::JobDependency jobDependency;
|
||||
jobDependency.m_jobKey = jobKey;
|
||||
jobDependency.m_type = AssetBuilderSDK::JobDependencyType::Order;
|
||||
jobDependency.m_sourceFile.m_sourceFileDependencyPath = file;
|
||||
jobDependencies.push_back(jobDependency);
|
||||
AssetBuilderSDK::SourceFileDependency sourceFileDependency;
|
||||
sourceFileDependency.m_sourceFileDependencyPath = file;
|
||||
sourceFileDependencies.push_back(sourceFileDependency);
|
||||
|
||||
// The first path found is the highest priority, and will have a job dependency, as this is the one
|
||||
// the builder will actually use
|
||||
if (!dependencyFileFound)
|
||||
{
|
||||
AZ::Data::AssetInfo sourceInfo;
|
||||
AZStd::string watchFolder;
|
||||
AzToolsFramework::AssetSystemRequestBus::BroadcastResult(dependencyFileFound, &AzToolsFramework::AssetSystem::AssetSystemRequest::GetSourceInfoBySourcePath, file.c_str(), sourceInfo, watchFolder);
|
||||
|
||||
if (dependencyFileFound)
|
||||
{
|
||||
AssetBuilderSDK::JobDependency jobDependency;
|
||||
jobDependency.m_jobKey = jobKey;
|
||||
jobDependency.m_type = AssetBuilderSDK::JobDependencyType::Order;
|
||||
jobDependency.m_sourceFile.m_sourceFileDependencyPath = file;
|
||||
jobDependencies.push_back(jobDependency);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,7 +148,7 @@ namespace AZ
|
||||
// We'll build up this one JobDescriptor and reuse it to register each of the platforms
|
||||
AssetBuilderSDK::JobDescriptor outputJobDescriptor;
|
||||
outputJobDescriptor.m_jobKey = JobKey;
|
||||
|
||||
|
||||
// Load the file so we can detect and report dependencies.
|
||||
// If the file is a .materialtype, report dependencies on the .shader files.
|
||||
// If the file is a .material, report a dependency on the .materialtype and parent .material file
|
||||
@@ -152,7 +177,9 @@ namespace AZ
|
||||
|
||||
for (auto& shader : materialTypeSourceData.GetValue().m_shaderCollection)
|
||||
{
|
||||
AddPossibleJobDependencies("Shader Asset", request.m_sourceFile, shader.m_shaderFilePath, outputJobDescriptor.m_jobDependencyList);
|
||||
AddPossibleDependencies(request.m_sourceFile, shader.m_shaderFilePath,
|
||||
response.m_sourceFileDependencyList, "Shader Asset",
|
||||
outputJobDescriptor.m_jobDependencyList);
|
||||
}
|
||||
|
||||
for (auto& functor : materialTypeSourceData.GetValue().m_materialFunctorSourceData)
|
||||
@@ -161,7 +188,9 @@ namespace AZ
|
||||
|
||||
for (const MaterialFunctorSourceData::AssetDependency& dependency : dependencies)
|
||||
{
|
||||
AddPossibleJobDependencies(dependency.m_jobKey.c_str(), request.m_sourceFile, dependency.m_sourceFilePath, outputJobDescriptor.m_jobDependencyList);
|
||||
AddPossibleDependencies(request.m_sourceFile, dependency.m_sourceFilePath,
|
||||
response.m_sourceFileDependencyList,
|
||||
dependency.m_jobKey.c_str(), outputJobDescriptor.m_jobDependencyList);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -196,7 +225,9 @@ namespace AZ
|
||||
|
||||
// Register dependency on the parent material source file so we can load it and use it's data to build this variant material.
|
||||
// Note, we don't need a direct dependency on the material type because the parent material will depend on it.
|
||||
AddPossibleJobDependencies(JobKey, request.m_sourceFile, parentMaterialPath, outputJobDescriptor.m_jobDependencyList);
|
||||
AddPossibleDependencies(request.m_sourceFile, parentMaterialPath,
|
||||
response.m_sourceFileDependencyList,
|
||||
JobKey, outputJobDescriptor.m_jobDependencyList);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -109,31 +109,16 @@ namespace AZ
|
||||
|
||||
AZStd::vector<AZStd::string> GetPossibleDepenencyPaths(const AZStd::string& originatingSourceFilePath, const AZStd::string& referencedSourceFilePath)
|
||||
{
|
||||
// We potentially add the parent dependency as both a direct path and a relative path rather than use AssetUtils::ResolvePathReference
|
||||
// because there is no guarantee that the Asset Processor has seen the parent file yet (which ResolvePathReference requires).
|
||||
// In that case, we have to add both possible locations because we don't know where it will show up.
|
||||
|
||||
AZStd::vector<AZStd::string> results;
|
||||
|
||||
// The first dependency we add is using the referencedSourceFilePath as a relative path. This gives relative paths priority over asset-root paths.
|
||||
// Use the referencedSourceFilePath as a relative path starting at originatingSourceFilePath
|
||||
AZStd::string combinedPath = originatingSourceFilePath;
|
||||
AzFramework::StringFunc::Path::StripFullName(combinedPath);
|
||||
AzFramework::StringFunc::Path::Join(combinedPath.c_str(), referencedSourceFilePath.c_str(), combinedPath);
|
||||
results.push_back(combinedPath);
|
||||
|
||||
// If the parent file exists at the relative path, then there is no need to report a dependency on the asset-root path.
|
||||
bool assetFound = false;
|
||||
AZ::Data::AssetInfo sourceInfo;
|
||||
AZStd::string watchFolder;
|
||||
AzToolsFramework::AssetSystemRequestBus::BroadcastResult(assetFound, &AzToolsFramework::AssetSystem::AssetSystemRequest::GetSourceInfoBySourcePath, combinedPath.c_str(), sourceInfo, watchFolder);
|
||||
|
||||
if (!assetFound)
|
||||
{
|
||||
// The parent file wasn't found at the relative path, so we need a dependency on the asset-root path in case the file
|
||||
// exists there. Note, we still keep the relative path dependency above because we don't know whether it's missing because
|
||||
// it doesn't exist, or just because the AP hasn't found it yet.
|
||||
results.push_back(referencedSourceFilePath);
|
||||
}
|
||||
// Use the referencedSourceFilePath as a standard asset path
|
||||
results.push_back(referencedSourceFilePath);
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
@@ -164,11 +164,6 @@ namespace AZ
|
||||
Data::AssetBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
ShaderCollection& Material::GetShaderCollection()
|
||||
{
|
||||
return m_shaderCollection;
|
||||
}
|
||||
|
||||
const ShaderCollection& Material::GetShaderCollection() const
|
||||
{
|
||||
return m_shaderCollection;
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
#include <AzCore/Asset/AssetCommon.h>
|
||||
#include <AzCore/Asset/AssetManagerBus.h>
|
||||
#include <AzCore/std/algorithm.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
@@ -181,19 +182,15 @@ namespace AZ
|
||||
|
||||
RHI::Size targetImageSize = outputAttachment->m_descriptor.m_image.m_size;
|
||||
|
||||
m_viewportState = params.m_viewportState;
|
||||
if (m_viewportState.IsNull())
|
||||
{
|
||||
// compute viewport from target attachment
|
||||
m_viewportState = RHI::Viewport(0, static_cast<float>(targetImageSize.m_width), 0, static_cast<float>(targetImageSize.m_height));
|
||||
}
|
||||
m_viewportState.m_minX = 0.0f;
|
||||
m_viewportState.m_minY = 0.0f;
|
||||
m_viewportState.m_maxX = AZStd::min(static_cast<uint32_t>(params.m_viewportState.m_maxX), targetImageSize.m_width);
|
||||
m_viewportState.m_maxY = AZStd::min(static_cast<uint32_t>(params.m_viewportState.m_maxY), targetImageSize.m_height);
|
||||
|
||||
m_scissorState = params.m_scissorState;
|
||||
if (m_scissorState.IsNull())
|
||||
{
|
||||
// compute scissor from target attachment
|
||||
m_scissorState = RHI::Scissor(0, 0, targetImageSize.m_width, targetImageSize.m_height);
|
||||
}
|
||||
m_scissorState.m_minX = 0.0f;
|
||||
m_scissorState.m_minY = 0.0f;
|
||||
m_scissorState.m_maxX = AZStd::min(static_cast<uint32_t>(params.m_scissorState.m_maxX), targetImageSize.m_width);
|
||||
m_scissorState.m_maxY = AZStd::min(static_cast<uint32_t>(params.m_scissorState.m_maxY), targetImageSize.m_height);
|
||||
|
||||
RenderPass::FrameBeginInternal(params);
|
||||
}
|
||||
|
||||
@@ -904,7 +904,7 @@ namespace AZ::AtomBridge
|
||||
{
|
||||
// Draw circle with single radius.
|
||||
const float step = DegToRad(10.0f);
|
||||
const float maxAngle = DegToRad(360.0f) + step;
|
||||
const float maxAngle = DegToRad(360.0f);
|
||||
SingleColorStaticSizeLineHelper<40> lines; // hard code 40 lines until DegToRad is constexpr.
|
||||
|
||||
AZ::Vector3 radiusV3 = AZ::Vector3(radius);
|
||||
@@ -1134,7 +1134,7 @@ namespace AZ::AtomBridge
|
||||
// This matches Cry behavior, the DrawWireSphere above may need modifying to use the same approach.
|
||||
// Draw 3 axis aligned circles
|
||||
const float step = DegToRad(10.0f);
|
||||
const float maxAngle = DegToRad(360.0f) + step;
|
||||
const float maxAngle = DegToRad(360.0f);
|
||||
SingleColorStaticSizeLineHelper<40*3> lines; // hard code to 40 lines * 3 circles until DegToRad is constexpr.
|
||||
|
||||
// Z Axis
|
||||
@@ -1160,8 +1160,8 @@ namespace AZ::AtomBridge
|
||||
// Draw 3 axis aligned circles
|
||||
const float stepAngle = DegToRad(11.25f);
|
||||
const float startAngle = DegToRad(0.0f);
|
||||
const float stopAngle = DegToRad(360.0f) + startAngle;
|
||||
SingleColorDynamicSizeLineHelper lines(2+static_cast<int>(360.0f/11.25f)); // num disk segments + 1 for azis line + 1 for spare
|
||||
const float stopAngle = DegToRad(360.0f);
|
||||
SingleColorDynamicSizeLineHelper lines(2 + static_cast<int>(360.0f / 11.25f)); // num disk segments + 1 for axis line + 1 for spare
|
||||
const AZ::Vector3 radiusV3 = AZ::Vector3(radius);
|
||||
CreateArbitraryAxisArc(
|
||||
lines,
|
||||
|
||||
@@ -332,6 +332,15 @@ namespace AZ::AtomBridge
|
||||
p0 = p1;
|
||||
++segmentIndex;
|
||||
}
|
||||
// Complete the arc by drawing the last bit
|
||||
sinCos.SetElement(circleAxis1, sinf(maxAngle));
|
||||
sinCos.SetElement(circleAxis2, cosf(maxAngle));
|
||||
p1 = position + radiusV3 * sinCos;
|
||||
p1 = ToWorldSpacePosition(p1);
|
||||
if (filterFunc(p0, p1, segmentIndex))
|
||||
{
|
||||
lines.AddLineSegment(p0, p1);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename LineStorageType>
|
||||
@@ -369,5 +378,13 @@ namespace AZ::AtomBridge
|
||||
p0 = p1;
|
||||
++segmentIndex;
|
||||
}
|
||||
// Complete the arc by drawing the last bit
|
||||
AZ::SinCos(maxAngle, sinVF, cosVF);
|
||||
p1 = position + radiusV3 * (cosVF * a + sinVF * b);
|
||||
p1 = ToWorldSpacePosition(p1);
|
||||
if (filterFunc(p0, p1, segmentIndex))
|
||||
{
|
||||
lines.AddLineSegment(p0, p1);
|
||||
}
|
||||
}
|
||||
} // namespace AZ::AtomBridge
|
||||
|
||||
@@ -628,7 +628,10 @@ namespace AZ
|
||||
{
|
||||
MeshHandleDescriptor meshDescriptor;
|
||||
meshDescriptor.m_modelAsset = m_skinnedMeshInstance->m_model->GetModelAsset();
|
||||
meshDescriptor.m_isSkinnedMeshWithMotion = true;
|
||||
|
||||
// [GFX TODO][ATOM-13067] Enable raytracing on skinned meshes
|
||||
meshDescriptor.m_isRayTracingEnabled = false;
|
||||
|
||||
m_meshHandle = AZStd::make_shared<MeshFeatureProcessorInterface::MeshHandle>(
|
||||
m_meshFeatureProcessor->AcquireMesh(meshDescriptor, materials));
|
||||
}
|
||||
|
||||
@@ -104,7 +104,7 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS)
|
||||
|
||||
ly_add_target_files(
|
||||
TARGETS LmbrCentral.Editor
|
||||
FILES ${QT_LRELEASE_EXECUTABLE}
|
||||
FILES ${lrelease_files}
|
||||
)
|
||||
|
||||
# by default, load the above "Gem::LmbrCentral.Editor" module in dev tools
|
||||
|
||||
@@ -17,3 +17,7 @@ add_custom_command(TARGET LmbrCentral.Editor POST_BUILD
|
||||
COMMENT "Patching lrelease..."
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
set(lrelease_files
|
||||
${QT_LRELEASE_EXECUTABLE}
|
||||
)
|
||||
|
||||
@@ -8,3 +8,7 @@
|
||||
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
#
|
||||
|
||||
set(lrelease_files
|
||||
${QT_LRELEASE_EXECUTABLE}
|
||||
)
|
||||
|
||||
@@ -8,3 +8,8 @@
|
||||
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
#
|
||||
|
||||
set(lrelease_files
|
||||
${QT_LRELEASE_EXECUTABLE}
|
||||
${QT_PATH}/bin/Qt5Core.dll # this is a dependency of lrelease. Even in debug we use the release version
|
||||
)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user