Merge branch 'stabilization/2110' into Prism/DeleteUpdateGemsUI

monroegm-disable-blank-issue-2
nggieber 4 years ago
commit a253bfc2f5

@ -54,5 +54,6 @@ set(ENABLED_GEMS
AWSMetrics AWSMetrics
PrefabBuilder PrefabBuilder
AudioSystem AudioSystem
Terrain
Profiler Profiler
) )

@ -56,6 +56,9 @@ add_subdirectory(streaming)
## Smoke ## ## Smoke ##
add_subdirectory(smoke) add_subdirectory(smoke)
## Terrain ##
add_subdirectory(Terrain)
## AWS ## ## AWS ##
add_subdirectory(AWS) add_subdirectory(AWS)

@ -0,0 +1,24 @@
#
# Copyright (c) Contributors to the Open 3D Engine Project.
# For complete copyright and license terms please see the LICENSE at the root of this distribution.
#
# SPDX-License-Identifier: Apache-2.0 OR MIT
#
#
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
ly_add_pytest(
NAME AutomatedTesting::TerrainTests_Main
TEST_SUITE main
TEST_SERIAL
PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuite_Main.py
RUNTIME_DEPENDENCIES
Legacy::Editor
AZ::AssetProcessor
AutomatedTesting.Assets
COMPONENT
Terrain
)
endif()

@ -0,0 +1,90 @@
"""
Copyright (c) Contributors to the Open 3D Engine Project.
For complete copyright and license terms please see the LICENSE at the root of this distribution.
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
#fmt: off
class Tests():
create_test_entity = ("Entity created successfully", "Failed to create Entity")
add_axis_aligned_box_shape = ("Axis Aligned Box Shape component added", "Failed to add Axis Aligned Box Shape component")
add_terrain_collider = ("Terrain Physics Heightfield Collider component added", "Failed to add a Terrain Physics Heightfield Collider component")
box_dimensions_changed = ("Aabb dimensions changed successfully", "Failed change Aabb dimensions")
configuration_changed = ("Terrain size changed successfully", "Failed terrain size change")
no_errors_and_warnings_found = ("No errors and warnings found", "Found errors and warnings")
#fmt: on
def TerrainPhysicsCollider_ChangesSizeWithAxisAlignedBoxShapeChanges():
"""
Summary:
Test aspects of the TerrainHeightGradientList through the BehaviorContext and the Property Tree.
Test Steps:
Expected Behavior:
The Editor is stable there are no warnings or errors.
Test Steps:
1) Load the base level
2) Create test entity
3) Start the Tracer to catch any errors and warnings
4) Add the Axis Aligned Box Shape and Terrain Physics Heightfield Collider components
5) Change the Axis Aligned Box Shape dimensions
6) Check the Heightfield provider is returning the correct size
7) Verify there are no errors and warnings in the logs
:return: None
"""
from editor_python_test_tools.editor_entity_utils import EditorEntity
from editor_python_test_tools.utils import TestHelper as helper
from editor_python_test_tools.utils import Report, Tracer
import azlmbr.legacy.general as general
import azlmbr.physics as physics
import azlmbr.math as azmath
import azlmbr.bus as bus
import sys
import math
SET_BOX_X_SIZE = 5.0
SET_BOX_Y_SIZE = 6.0
EXPECTED_COLUMN_SIZE = SET_BOX_X_SIZE + 1
EXPECTED_ROW_SIZE = SET_BOX_Y_SIZE + 1
helper.init_idle()
# 1) Load the level
helper.open_level("", "Base")
# 2) Create test entity
test_entity = EditorEntity.create_editor_entity("TestEntity")
Report.result(Tests.create_test_entity, test_entity.id.IsValid())
# 3) Start the Tracer to catch any errors and warnings
with Tracer() as section_tracer:
# 4) Add the Axis Aligned Box Shape and Terrain Physics Heightfield Collider components
aaBoxShape_component = test_entity.add_component("Axis Aligned Box Shape")
Report.result(Tests.add_axis_aligned_box_shape, test_entity.has_component("Axis Aligned Box Shape"))
terrainPhysics_component = test_entity.add_component("Terrain Physics Heightfield Collider")
Report.result(Tests.add_terrain_collider, test_entity.has_component("Terrain Physics Heightfield Collider"))
# 5) Change the Axis Aligned Box Shape dimensions
aaBoxShape_component.set_component_property_value("Axis Aligned Box Shape|Box Configuration|Dimensions", azmath.Vector3(SET_BOX_X_SIZE, SET_BOX_Y_SIZE, 1.0))
add_check = aaBoxShape_component.get_component_property_value("Axis Aligned Box Shape|Box Configuration|Dimensions") == azmath.Vector3(SET_BOX_X_SIZE, SET_BOX_Y_SIZE, 1.0)
Report.result(Tests.box_dimensions_changed, add_check)
# 6) Check the Heightfield provider is returning the correct size
columns = physics.HeightfieldProviderRequestsBus(bus.Broadcast, "GetHeightfieldGridColumns")
rows = physics.HeightfieldProviderRequestsBus(bus.Broadcast, "GetHeightfieldGridRows")
Report.result(Tests.configuration_changed, math.isclose(columns, EXPECTED_COLUMN_SIZE) and math.isclose(rows, EXPECTED_ROW_SIZE))
helper.wait_for_condition(lambda: section_tracer.has_errors or section_tracer.has_asserts, 1.0)
for error_info in section_tracer.errors:
Report.info(f"Error: {error_info.filename} {error_info.function} | {error_info.message}")
for assert_info in section_tracer.asserts:
Report.info(f"Assert: {assert_info.filename} {assert_info.function} | {assert_info.message}")
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(TerrainPhysicsCollider_ChangesSizeWithAxisAlignedBoxShapeChanges)

@ -0,0 +1,25 @@
"""
Copyright (c) Contributors to the Open 3D Engine Project.
For complete copyright and license terms please see the LICENSE at the root of this distribution.
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
# This suite consists of all test cases that are passing and have been verified.
import pytest
import os
import sys
from ly_test_tools import LAUNCHERS
from ly_test_tools.o3de.editor_test import EditorTestSuite, EditorSingleTest
@pytest.mark.SUITE_main
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
@pytest.mark.parametrize("project", ["AutomatedTesting"])
class TestAutomation(EditorTestSuite):
#global_extra_cmdline_args=["--regset=/Amazon/Preferences/EnablePrefabSystem=true"]
class test_AxisAlignedBoxShape_ConfigurationWorks(EditorSingleTest):
from .EditorScripts import TerrainPhysicsCollider_ChangesSizeWithAxisAlignedBoxShapeChanges as test_module

@ -0,0 +1,6 @@
"""
Copyright (c) Contributors to the Open 3D Engine Project.
For complete copyright and license terms please see the LICENSE at the root of this distribution.
SPDX-License-Identifier: Apache-2.0 OR MIT
"""

@ -0,0 +1,146 @@
"""
Copyright (c) Contributors to the Open 3D Engine Project.
For complete copyright and license terms please see the LICENSE at the root of this distribution.
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
class Tests:
entities_sorted = (
"Entities sorted in the expected order",
"Entities sorted in an incorrect order",
)
def EntityOutliner_EntityOrdering():
"""
Summary:
Verify that manual entity ordering in the entity outliner works and is stable.
Expected Behavior:
Several entities are created, some are manually ordered, and their order
is maintained, even when new entities are added.
Test Steps:
1) Open the empty Prefab Base level
2) Add 5 entities to the outliner
3) Move "Entity1" to the top of the order
4) Move "Entity4" to the bottom of the order
5) Add another new entity, ensure the rest of the order is unchanged
"""
import editor_python_test_tools.pyside_utils as pyside_utils
import azlmbr.legacy.general as general
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
from PySide2 import QtCore, QtWidgets, QtGui, QtTest
# Grab the Editor, Entity Outliner, and Outliner Model
editor_window = pyside_utils.get_editor_main_window()
entity_outliner = pyside_utils.find_child_by_hierarchy(
editor_window, ..., "EntityOutlinerWidgetUI", ..., "m_objectTree"
)
entity_outliner_model = entity_outliner.model()
# Get the outliner index for the root prefab container entity
def get_root_prefab_container_index():
return entity_outliner_model.index(0, 0)
# Get the outliner index for the top level entity of a given name
def index_for_name(name):
root_index = get_root_prefab_container_index()
for row in range(entity_outliner_model.rowCount(root_index)):
row_index = entity_outliner_model.index(row, 0, root_index)
if row_index.data() == name:
return row_index
return None
# Validate that the outliner top level entity order matches the expected order
def verify_entities_sorted(expected_order):
actual_order = []
root_index = get_root_prefab_container_index()
for row in range(entity_outliner_model.rowCount(root_index)):
row_index = entity_outliner_model.index(row, 0, root_index)
actual_order.append(row_index.data())
sorted_correctly = actual_order == expected_order
Report.result(Tests.entities_sorted, sorted_correctly)
if not sorted_correctly:
print(f"Expected entity order: {expected_order}")
print(f"Actual entity order: {actual_order}")
# Creates an entity from the outliner context menu
def create_entity():
pyside_utils.trigger_context_menu_entry(
entity_outliner, "Create entity", index=get_root_prefab_container_index()
)
# Wait a tick after entity creation to let events process
general.idle_wait(0.0)
# Moves an entity (wrapped by move_entity_before and move_entity_after)
def _move_entity(source_name, target_name, move_after=False):
source_index = index_for_name(source_name)
target_index = index_for_name(target_name)
target_row = target_index.row()
if move_after:
target_row += 1
# Generate MIME data and directly inject it into the model instead of
# generating mouse click operations, as it's more reliable and we're
# testing the underlying drag & drop logic as opposed to Qt's mouse
# handling here
mime_data = entity_outliner_model.mimeData([source_index])
entity_outliner_model.dropMimeData(
mime_data, QtCore.Qt.MoveAction, target_row, 0, target_index.parent()
)
QtWidgets.QApplication.processEvents()
# Move an entity before another entity in the order by dragging the source above the target
move_entity_before = lambda source_name, target_name: _move_entity(
source_name, target_name, move_after=False
)
# Move an entity after another entity in the order by dragging the source beloew the target
move_entity_after = lambda source_name, target_name: _move_entity(
source_name, target_name, move_after=True
)
expected_order = []
# 1) Open the empty Prefab Base level
helper.init_idle()
helper.open_level("Prefab", "Base")
# 2) Add 5 entities to the outliner
ENTITIES_TO_ADD = 5
for i in range(ENTITIES_TO_ADD):
create_entity()
# Our new entity should be given a name with a number automatically
new_entity = f"Entity{i+1}"
# The new entity should be added to the top of its parent entity
expected_order = [new_entity] + expected_order
verify_entities_sorted(expected_order)
# 3) Move "Entity1" to the top of the order
move_entity_before("Entity1", "Entity5")
expected_order = ["Entity1", "Entity5", "Entity4", "Entity3", "Entity2"]
verify_entities_sorted(expected_order)
# 4) Move "Entity4" to the bottom of the order
move_entity_after("Entity4", "Entity2")
expected_order = ["Entity1", "Entity5", "Entity3", "Entity2", "Entity4"]
verify_entities_sorted(expected_order)
# 5) Add another new entity, ensure the rest of the order is unchanged
create_entity()
expected_order = ["Entity6", "Entity1", "Entity5", "Entity3", "Entity2", "Entity4"]
verify_entities_sorted(expected_order)
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(EntityOutliner_EntityOrdering)

@ -41,3 +41,15 @@ class TestAutomation(TestAutomationBase):
from .EditorScripts import BasicEditorWorkflows_LevelEntityComponentCRUD as test_module from .EditorScripts import BasicEditorWorkflows_LevelEntityComponentCRUD as test_module
self._run_test(request, workspace, editor, test_module, batch_mode=False, autotest_mode=False, self._run_test(request, workspace, editor, test_module, batch_mode=False, autotest_mode=False,
use_null_renderer=False) use_null_renderer=False)
def test_EntityOutlienr_EntityOrdering(self, request, workspace, editor, launcher_platform):
from .EditorScripts import EntityOutliner_EntityOrdering as test_module
self._run_test(
request,
workspace,
editor,
test_module,
batch_mode=False,
autotest_mode=True,
extra_cmdline_args=["--regset=/Amazon/Preferences/EnablePrefabSystem=true"]
)

@ -28,4 +28,4 @@ class TestAutomation(TestAutomationBase):
from . import Editor_NewExistingLevels_Works as test_module from . import Editor_NewExistingLevels_Works as test_module
self._run_test(request, workspace, editor, test_module) self._run_test(request, workspace, editor, test_module, extra_cmdline_args=["--regset=/Amazon/Preferences/EnablePrefabSystem=false"])

@ -0,0 +1,53 @@
{
"ContainerEntity": {
"Id": "Entity_[1146574390643]",
"Name": "Level",
"Components": {
"Component_[10641544592923449938]": {
"$type": "EditorInspectorComponent",
"Id": 10641544592923449938
},
"Component_[12039882709170782873]": {
"$type": "EditorOnlyEntityComponent",
"Id": 12039882709170782873
},
"Component_[12265484671603697631]": {
"$type": "EditorPendingCompositionComponent",
"Id": 12265484671603697631
},
"Component_[14126657869720434043]": {
"$type": "EditorEntitySortComponent",
"Id": 14126657869720434043
},
"Component_[15230859088967841193]": {
"$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
"Id": 15230859088967841193,
"Parent Entity": ""
},
"Component_[16239496886950819870]": {
"$type": "EditorDisabledCompositionComponent",
"Id": 16239496886950819870
},
"Component_[5688118765544765547]": {
"$type": "EditorEntityIconComponent",
"Id": 5688118765544765547
},
"Component_[6545738857812235305]": {
"$type": "SelectionComponent",
"Id": 6545738857812235305
},
"Component_[7247035804068349658]": {
"$type": "EditorPrefabComponent",
"Id": 7247035804068349658
},
"Component_[9307224322037797205]": {
"$type": "EditorLockComponent",
"Id": 9307224322037797205
},
"Component_[9562516168917670048]": {
"$type": "EditorVisibilityComponent",
"Id": 9562516168917670048
}
}
}
}

@ -3974,9 +3974,8 @@ void CCryEditApp::OpenLUAEditor(const char* files)
} }
} }
const char* engineRoot = nullptr; AZ::IO::FixedMaxPathString engineRoot = AZ::Utils::GetEnginePath();
AzFramework::ApplicationRequests::Bus::BroadcastResult(engineRoot, &AzFramework::ApplicationRequests::GetEngineRoot); AZ_Assert(!engineRoot.empty(), "Unable to query Engine Path");
AZ_Assert(engineRoot != nullptr, "Unable to communicate to AzFramework::ApplicationRequests::Bus");
AZStd::string_view exePath; AZStd::string_view exePath;
AZ::ComponentApplicationBus::BroadcastResult(exePath, &AZ::ComponentApplicationRequests::GetExecutableFolder); AZ::ComponentApplicationBus::BroadcastResult(exePath, &AZ::ComponentApplicationRequests::GetExecutableFolder);
@ -3995,7 +3994,7 @@ void CCryEditApp::OpenLUAEditor(const char* files)
#endif #endif
"%s", argumentQuoteString, aznumeric_cast<int>(exePath.size()), exePath.data(), argumentQuoteString); "%s", argumentQuoteString, aznumeric_cast<int>(exePath.size()), exePath.data(), argumentQuoteString);
AZStd::string processArgs = AZStd::string::format("%s -engine-path \"%s\"", args.c_str(), engineRoot); AZStd::string processArgs = AZStd::string::format("%s -engine-path \"%s\"", args.c_str(), engineRoot.c_str());
StartProcessDetached(process.c_str(), processArgs.c_str()); StartProcessDetached(process.c_str(), processArgs.c_str());
} }

@ -114,9 +114,7 @@ struct IFileUtil
virtual void ShowInExplorer(const QString& path) = 0; virtual void ShowInExplorer(const QString& path) = 0;
virtual bool CompileLuaFile(const char* luaFilename) = 0;
virtual bool ExtractFile(QString& file, bool bMsgBoxAskForExtraction = true, const char* pDestinationFilename = nullptr) = 0; virtual bool ExtractFile(QString& file, bool bMsgBoxAskForExtraction = true, const char* pDestinationFilename = nullptr) = 0;
virtual void EditTextFile(const char* txtFile, int line = 0, ETextFileType fileType = FILE_TYPE_SCRIPT) = 0;
virtual void EditTextureFile(const char* txtureFile, bool bUseGameFolder) = 0; virtual void EditTextureFile(const char* txtureFile, bool bUseGameFolder) = 0;
//! dcc filename calculation and extraction sub-routines //! dcc filename calculation and extraction sub-routines

@ -23,9 +23,9 @@
// AzCore // AzCore
#include <AzCore/Component/TickBus.h> #include <AzCore/Component/TickBus.h>
#include <AzCore/Settings/SettingsRegistryMergeUtils.h> #include <AzCore/Settings/SettingsRegistryMergeUtils.h>
#include <AzCore/Utils/Utils.h>
// AzFramework // AzFramework
#include <AzFramework/API/ApplicationAPI.h>
// AzQtComponents // AzQtComponents
#include <AzQtComponents/Utilities/DesktopUtilities.h> #include <AzQtComponents/Utilities/DesktopUtilities.h>
@ -62,84 +62,6 @@ CAutoRestorePrimaryCDRoot::~CAutoRestorePrimaryCDRoot()
QDir::setCurrent(GetIEditor()->GetPrimaryCDFolder()); QDir::setCurrent(GetIEditor()->GetPrimaryCDFolder());
} }
bool CFileUtil::CompileLuaFile(const char* luaFilename)
{
QString luaFile = luaFilename;
if (luaFile.isEmpty())
{
return false;
}
// Check if this file is in Archive.
{
CCryFile file;
if (file.Open(luaFilename, "rb"))
{
// Check if in pack.
if (file.IsInPak())
{
return true;
}
}
}
luaFile = Path::GamePathToFullPath(luaFilename);
// First try compiling script and see if it have any errors.
QString LuaCompiler;
QString CompilerOutput;
// Create the filepath of the lua compiler
QString szExeFileName = qApp->applicationFilePath();
QString exePath = Path::GetPath(szExeFileName);
#if defined(AZ_PLATFORM_WINDOWS)
const char* luaCompiler = "LuaCompiler.exe";
#else
const char* luaCompiler = "lua";
#endif
LuaCompiler = Path::AddPathSlash(exePath) + luaCompiler + " ";
AZStd::string path = luaFile.toUtf8().data();
EBUS_EVENT(AzFramework::ApplicationRequests::Bus, NormalizePath, path);
QString finalPath = path.c_str();
finalPath = "\"" + finalPath + "\"";
// Add the name of the Lua file
QString cmdLine = LuaCompiler + finalPath;
// Execute the compiler and capture the output
if (!GetIEditor()->ExecuteConsoleApp(cmdLine, CompilerOutput))
{
QMessageBox::critical(QApplication::activeWindow(), QString(), QObject::tr("Error while executing '%1', make sure the file is in" \
" your Primary CD folder !").arg(luaCompiler));
return false;
}
// Check return string
if (!CompilerOutput.isEmpty())
{
// Errors while compiling file.
// Show output from Lua compiler
if (QMessageBox::critical(QApplication::activeWindow(), QObject::tr("Lua Compiler"),
QObject::tr("Error output from Lua compiler:\r\n%1\r\nDo you want to edit the file ?").arg(CompilerOutput), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes)
{
int line = 0;
int index = CompilerOutput.indexOf("at line");
if (index >= 0)
{
azsscanf(CompilerOutput.mid(index).toUtf8().data(), "at line %d", &line);
}
// Open the Lua file for editing
EditTextFile(luaFile.toUtf8().data(), line);
}
return false;
}
return true;
}
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
bool CFileUtil::ExtractFile(QString& file, bool bMsgBoxAskForExtraction, const char* pDestinationFilename) bool CFileUtil::ExtractFile(QString& file, bool bMsgBoxAskForExtraction, const char* pDestinationFilename)
{ {
@ -301,64 +223,6 @@ void CFileUtil::EditTextureFile(const char* textureFile, [[maybe_unused]] bool b
} }
} }
//////////////////////////////////////////////////////////////////////////
bool CFileUtil::EditMayaFile(const char* filepath, const bool bExtractFromPak, const bool bUseGameFolder)
{
QString dosFilepath = PathUtil::ToDosPath(filepath).c_str();
if (bExtractFromPak)
{
ExtractFile(dosFilepath);
}
if (bUseGameFolder)
{
const QString sGameFolder = Path::GetEditingGameDataFolder().c_str();
int nLength = sGameFolder.toUtf8().count();
if (azstrnicmp(filepath, sGameFolder.toUtf8().data(), nLength) != 0)
{
dosFilepath = sGameFolder + '\\' + filepath;
}
dosFilepath = PathUtil::ToDosPath(dosFilepath.toUtf8().data()).c_str();
}
const char* engineRoot;
EBUS_EVENT_RESULT(engineRoot, AzFramework::ApplicationRequests::Bus, GetEngineRoot);
const QString fullPath = QString(engineRoot) + '\\' + dosFilepath;
if (gSettings.animEditor.isEmpty())
{
AzQtComponents::ShowFileOnDesktop(fullPath);
}
else
{
if (!QProcess::startDetached(gSettings.animEditor, { fullPath }))
{
CryMessageBox("Can't open the file. You can specify a source editor in Sandbox Preferences or create an association in Windows.", "Cannot open file!", MB_OK | MB_ICONERROR);
}
}
return true;
}
//////////////////////////////////////////////////////////////////////////
bool CFileUtil::EditFile(const char* filePath, const bool bExtrackFromPak, const bool bUseGameFolder)
{
QString extension = filePath;
extension.remove(0, extension.lastIndexOf('.'));
if (extension.compare(".ma") == 0)
{
return EditMayaFile(filePath, bExtrackFromPak, bUseGameFolder);
}
else if ((extension.compare(".bspace") == 0) || (extension.compare(".comb") == 0))
{
EditTextFile(filePath, 0, IFileUtil::FILE_TYPE_BSPACE);
return true;
}
return false;
}
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
bool CFileUtil::CalculateDccFilename(const QString& assetFilename, QString& dccFilename) bool CFileUtil::CalculateDccFilename(const QString& assetFilename, QString& dccFilename)

@ -25,14 +25,9 @@ public:
static void ShowInExplorer(const QString& path); static void ShowInExplorer(const QString& path);
// Try to compile the given lua file: returns true if compilation succeeded, false on failure.
static bool CompileLuaFile(const char* luaFilename);
static bool ExtractFile(QString& file, bool bMsgBoxAskForExtraction = true, const char* pDestinationFilename = nullptr); static bool ExtractFile(QString& file, bool bMsgBoxAskForExtraction = true, const char* pDestinationFilename = nullptr);
static void EditTextFile(const char* txtFile, int line = 0, IFileUtil::ETextFileType fileType = IFileUtil::FILE_TYPE_SCRIPT); static void EditTextFile(const char* txtFile, int line = 0, IFileUtil::ETextFileType fileType = IFileUtil::FILE_TYPE_SCRIPT);
static void EditTextureFile(const char* txtureFile, bool bUseGameFolder); static void EditTextureFile(const char* txtureFile, bool bUseGameFolder);
static bool EditMayaFile(const char* mayaFile, const bool bExtractFromPak, const bool bUseGameFolder);
static bool EditFile(const char* filePath, const bool bExtrackFromPak, const bool bUseGameFolder);
//! dcc filename calculation and extraction sub-routines //! dcc filename calculation and extraction sub-routines
static bool CalculateDccFilename(const QString& assetFilename, QString& dccFilename); static bool CalculateDccFilename(const QString& assetFilename, QString& dccFilename);

@ -20,21 +20,11 @@ void CFileUtil_impl::ShowInExplorer(const QString& path)
CFileUtil::ShowInExplorer(path); CFileUtil::ShowInExplorer(path);
} }
bool CFileUtil_impl::CompileLuaFile(const char* luaFilename)
{
return CFileUtil::CompileLuaFile(luaFilename);
}
bool CFileUtil_impl::ExtractFile(QString& file, bool bMsgBoxAskForExtraction, const char* pDestinationFilename) bool CFileUtil_impl::ExtractFile(QString& file, bool bMsgBoxAskForExtraction, const char* pDestinationFilename)
{ {
return CFileUtil::ExtractFile(file, bMsgBoxAskForExtraction, pDestinationFilename); return CFileUtil::ExtractFile(file, bMsgBoxAskForExtraction, pDestinationFilename);
} }
void CFileUtil_impl::EditTextFile(const char* txtFile, int line, ETextFileType fileType)
{
CFileUtil::EditTextFile(txtFile, line, fileType);
}
void CFileUtil_impl::EditTextureFile(const char* txtureFile, bool bUseGameFolder) void CFileUtil_impl::EditTextureFile(const char* txtureFile, bool bUseGameFolder)
{ {
CFileUtil::EditTextureFile(txtureFile, bUseGameFolder); CFileUtil::EditTextureFile(txtureFile, bUseGameFolder);

@ -36,9 +36,7 @@ public:
void ShowInExplorer(const QString& path) override; void ShowInExplorer(const QString& path) override;
bool CompileLuaFile(const char* luaFilename) override;
bool ExtractFile(QString& file, bool bMsgBoxAskForExtraction = true, const char* pDestinationFilename = nullptr) override; bool ExtractFile(QString& file, bool bMsgBoxAskForExtraction = true, const char* pDestinationFilename = nullptr) override;
void EditTextFile(const char* txtFile, int line = 0, ETextFileType fileType = FILE_TYPE_SCRIPT) override;
void EditTextureFile(const char* txtureFile, bool bUseGameFolder) override; void EditTextureFile(const char* txtureFile, bool bUseGameFolder) override;
//! dcc filename calculation and extraction sub-routines //! dcc filename calculation and extraction sub-routines

@ -14,7 +14,6 @@
#include <AzCore/Settings/SettingsRegistryMergeUtils.h> #include <AzCore/Settings/SettingsRegistryMergeUtils.h>
#include <AzCore/Utils/Utils.h> #include <AzCore/Utils/Utils.h>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h> // for ebus events #include <AzToolsFramework/API/EditorAssetSystemAPI.h> // for ebus events
#include <AzFramework/API/ApplicationAPI.h>
#include <AzCore/std/string/conversions.h> #include <AzCore/std/string/conversions.h>
#include <AzFramework/IO/LocalFileIO.h> #include <AzFramework/IO/LocalFileIO.h>
@ -175,9 +174,8 @@ namespace Path
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
QString GetEngineRootPath() QString GetEngineRootPath()
{ {
const char* engineRoot; const AZ::IO::FixedMaxPathString engineRoot = AZ::Utils::GetEnginePath();
EBUS_EVENT_RESULT(engineRoot, AzFramework::ApplicationRequests::Bus, GetEngineRoot); return QString::fromUtf8(engineRoot.c_str(), static_cast<int>(engineRoot.size()));
return QString(engineRoot);
} }
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////

@ -25,8 +25,6 @@
#include <AzCore/Utils/Utils.h> #include <AzCore/Utils/Utils.h>
// AzFramework
#include <AzFramework/API/ApplicationAPI.h>
// AzToolsFramework // AzToolsFramework
#include <AzToolsFramework/UI/UICore/WidgetHelpers.h> #include <AzToolsFramework/UI/UICore/WidgetHelpers.h>
@ -173,9 +171,6 @@ void WelcomeScreenDialog::SetRecentFileList(RecentFileList* pList)
m_pRecentList = pList; m_pRecentList = pList;
const char* engineRoot;
EBUS_EVENT_RESULT(engineRoot, AzFramework::ApplicationRequests::Bus, GetEngineRoot);
auto projectPath = AZ::Utils::GetProjectPath(); auto projectPath = AZ::Utils::GetProjectPath();
QString gamePath{projectPath.c_str()}; QString gamePath{projectPath.c_str()};
Path::ConvertSlashToBackSlash(gamePath); Path::ConvertSlashToBackSlash(gamePath);

@ -485,16 +485,6 @@ namespace AZ
constexpr bool executeRegDumpCommands = false; constexpr bool executeRegDumpCommands = false;
SettingsRegistryMergeUtils::MergeSettingsToRegistry_CommandLine(*m_settingsRegistry, m_commandLine, executeRegDumpCommands); SettingsRegistryMergeUtils::MergeSettingsToRegistry_CommandLine(*m_settingsRegistry, m_commandLine, executeRegDumpCommands);
// Query for the Executable Path using OS specific functions
CalculateExecutablePath();
// Determine the path to the engine
CalculateEngineRoot();
// If the current platform returns an engaged optional from Utils::GetDefaultAppRootPath(), that is used
// for the application root.
CalculateAppRoot();
SettingsRegistryMergeUtils::MergeSettingsToRegistry_O3deUserRegistry(*m_settingsRegistry, AZ_TRAIT_OS_PLATFORM_CODENAME, {}); SettingsRegistryMergeUtils::MergeSettingsToRegistry_O3deUserRegistry(*m_settingsRegistry, AZ_TRAIT_OS_PLATFORM_CODENAME, {});
SettingsRegistryMergeUtils::MergeSettingsToRegistry_CommandLine(*m_settingsRegistry, m_commandLine, executeRegDumpCommands); SettingsRegistryMergeUtils::MergeSettingsToRegistry_CommandLine(*m_settingsRegistry, m_commandLine, executeRegDumpCommands);
SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*m_settingsRegistry); SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*m_settingsRegistry);
@ -614,7 +604,8 @@ namespace AZ
{ {
AZ_Assert(!m_isStarted, "Component application already started!"); AZ_Assert(!m_isStarted, "Component application already started!");
if (m_engineRoot.empty()) using Type = AZ::SettingsRegistryInterface::Type;
if (m_settingsRegistry->GetType(SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder) == Type::NoType)
{ {
ReportBadEngineRoot(); ReportBadEngineRoot();
return nullptr; return nullptr;
@ -1180,6 +1171,24 @@ namespace AZ
return ReflectionEnvironment::GetReflectionManager() ? ReflectionEnvironment::GetReflectionManager()->GetReflectContext<JsonRegistrationContext>() : nullptr; return ReflectionEnvironment::GetReflectionManager() ? ReflectionEnvironment::GetReflectionManager()->GetReflectContext<JsonRegistrationContext>() : nullptr;
} }
/// Returns the path to the engine.
const char* ComponentApplication::GetEngineRoot() const
{
static IO::FixedMaxPathString engineRoot;
engineRoot.clear();
m_settingsRegistry->Get(engineRoot, SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder);
return engineRoot.c_str();
}
const char* ComponentApplication::GetExecutableFolder() const
{
static IO::FixedMaxPathString exeFolder;
exeFolder.clear();
m_settingsRegistry->Get(exeFolder, SettingsRegistryMergeUtils::FilePathKey_BinaryFolder);
return exeFolder.c_str();
}
//========================================================================= //=========================================================================
// CreateReflectionManager // CreateReflectionManager
//========================================================================= //=========================================================================
@ -1485,27 +1494,6 @@ namespace AZ
} }
} }
//=========================================================================
// CalculateExecutablePath
//=========================================================================
void ComponentApplication::CalculateExecutablePath()
{
m_exeDirectory = Utils::GetExecutableDirectory();
}
void ComponentApplication::CalculateAppRoot()
{
if (AZStd::optional<AZ::StringFunc::Path::FixedString> appRootPath = Utils::GetDefaultAppRootPath(); appRootPath)
{
m_appRoot = AZStd::move(*appRootPath);
}
}
void ComponentApplication::CalculateEngineRoot()
{
m_engineRoot = AZ::SettingsRegistryMergeUtils::FindEngineRoot(*m_settingsRegistry).Native();
}
void ComponentApplication::ResolveModulePath([[maybe_unused]] AZ::OSString& modulePath) void ComponentApplication::ResolveModulePath([[maybe_unused]] AZ::OSString& modulePath)
{ {
// No special parsing of the Module Path is done by the Component Application anymore // No special parsing of the Module Path is done by the Component Application anymore

@ -221,13 +221,10 @@ namespace AZ
BehaviorContext* GetBehaviorContext() override; BehaviorContext* GetBehaviorContext() override;
/// Returns the json registration context that has been registered with the app, if there is one. /// Returns the json registration context that has been registered with the app, if there is one.
JsonRegistrationContext* GetJsonRegistrationContext() override; JsonRegistrationContext* GetJsonRegistrationContext() override;
/// Returns the working root folder that has been registered with the app, if there is one.
/// It's expected that derived applications will implement an application root.
const char* GetAppRoot() const override { return m_appRoot.c_str(); }
/// Returns the path to the engine. /// Returns the path to the engine.
const char* GetEngineRoot() const override { return m_engineRoot.c_str(); } const char* GetEngineRoot() const override;
/// Returns the path to the folder the executable is in. /// Returns the path to the folder the executable is in.
const char* GetExecutableFolder() const override { return m_exeDirectory.c_str(); } const char* GetExecutableFolder() const override;
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
/// TickRequestBus /// TickRequestBus
@ -352,15 +349,6 @@ namespace AZ
/// Adds system components requested by modules and the application to the system entity. /// Adds system components requested by modules and the application to the system entity.
void AddRequiredSystemComponents(AZ::Entity* systemEntity); void AddRequiredSystemComponents(AZ::Entity* systemEntity);
/// Calculates the directory the application executable comes from.
void CalculateExecutablePath();
/// Calculates the root directory of the engine.
void CalculateEngineRoot();
/// Deprecated: The term "AppRoot" has no meaning
void CalculateAppRoot();
template<typename Iterator> template<typename Iterator>
static void NormalizePath(Iterator begin, Iterator end, bool doLowercase = true) static void NormalizePath(Iterator begin, Iterator end, bool doLowercase = true)
{ {
@ -388,9 +376,6 @@ namespace AZ
void* m_fixedMemoryBlock{ nullptr }; //!< Pointer to the memory block allocator, so we can free it OnDestroy. void* m_fixedMemoryBlock{ nullptr }; //!< Pointer to the memory block allocator, so we can free it OnDestroy.
IAllocatorAllocate* m_osAllocator{ nullptr }; IAllocatorAllocate* m_osAllocator{ nullptr };
EntitySetType m_entities; EntitySetType m_entities;
AZ::IO::FixedMaxPath m_exeDirectory;
AZ::IO::FixedMaxPath m_engineRoot;
AZ::IO::FixedMaxPath m_appRoot;
AZ::SettingsRegistryInterface::NotifyEventHandler m_projectPathChangedHandler; AZ::SettingsRegistryInterface::NotifyEventHandler m_projectPathChangedHandler;
AZ::SettingsRegistryInterface::NotifyEventHandler m_projectNameChangedHandler; AZ::SettingsRegistryInterface::NotifyEventHandler m_projectNameChangedHandler;

@ -175,10 +175,6 @@ namespace AZ
//! the serializers used by the best-effort json serialization. //! the serializers used by the best-effort json serialization.
virtual class JsonRegistrationContext* GetJsonRegistrationContext() = 0; virtual class JsonRegistrationContext* GetJsonRegistrationContext() = 0;
//! Gets the name of the working root folder that was registered with the app.
//! @return a pointer to the name of the app's root folder, if a root folder was registered.
virtual const char* GetAppRoot() const = 0;
//! Gets the path of the working engine folder that the app is a part of. //! Gets the path of the working engine folder that the app is a part of.
//! @return a pointer to the engine path. //! @return a pointer to the engine path.
virtual const char* GetEngineRoot() const = 0; virtual const char* GetEngineRoot() const = 0;

@ -266,7 +266,8 @@ namespace AZ::SettingsRegistryMergeUtils
// Step 3 locate the project root and attempt to find the engine root using the registered engine // Step 3 locate the project root and attempt to find the engine root using the registered engine
// for the project in the project.json file // for the project in the project.json file
AZ::IO::FixedMaxPath projectRoot = FindProjectRoot(settingsRegistry); AZ::IO::FixedMaxPath projectRoot;
settingsRegistry.Get(projectRoot.Native(), FilePathKey_ProjectPath);
if (projectRoot.empty()) if (projectRoot.empty())
{ {
return {}; return {};
@ -668,7 +669,7 @@ namespace AZ::SettingsRegistryMergeUtils
// NOTE: We make the project-path in the BootstrapSettingsRootKey absolute first // NOTE: We make the project-path in the BootstrapSettingsRootKey absolute first
AZ::IO::FixedMaxPath projectPath = FindProjectRoot(registry); AZ::IO::FixedMaxPath projectPath = FindProjectRoot(registry);
if (constexpr auto projectPathKey = FixedValueString(BootstrapSettingsRootKey) + "/project_path"; if ([[maybe_unused]] constexpr auto projectPathKey = FixedValueString(BootstrapSettingsRootKey) + "/project_path";
!projectPath.empty()) !projectPath.empty())
{ {
if (projectPath.IsRelative()) if (projectPath.IsRelative())
@ -693,6 +694,7 @@ namespace AZ::SettingsRegistryMergeUtils
R"(Project path isn't set in the Settings Registry at "%.*s".)" R"(Project path isn't set in the Settings Registry at "%.*s".)"
" Project-related filepaths will be set relative to the executable directory\n", " Project-related filepaths will be set relative to the executable directory\n",
AZ_STRING_ARG(projectPathKey)); AZ_STRING_ARG(projectPathKey));
projectPath = exePath;
registry.Set(FilePathKey_ProjectPath, exePath.Native()); registry.Set(FilePathKey_ProjectPath, exePath.Native());
} }

@ -1427,26 +1427,36 @@ namespace AZ
namespace AssetPath namespace AssetPath
{ {
void CalculateBranchToken(const AZStd::string& appRootPath, AZStd::string& token) namespace Internal
{
AZ::u32 CalculateBranchTokenHash(AZStd::string_view engineRootPath)
{ {
// Normalize the token to prepare for CRC32 calculation // Normalize the token to prepare for CRC32 calculation
AZStd::string normalized = appRootPath; auto NormalizeEnginePath = [](const char element) -> char
{
// Strip out any trailing path separators // Substitute path separators with '_' and lower case
AZ::StringFunc::Strip(normalized, AZ_CORRECT_FILESYSTEM_SEPARATOR_STRING AZ_WRONG_FILESYSTEM_SEPARATOR_STRING,false, false, true); return element == AZ::IO::WindowsPathSeparator || element == AZ::IO::PosixPathSeparator
? '_' : static_cast<char>(std::tolower(element));
// Lower case always };
AZStd::to_lower(normalized.begin(), normalized.end());
// Substitute path separators with '_' // Trim off trailing path separators
AZStd::replace(normalized.begin(), normalized.end(), '\\', '_'); engineRootPath = RStrip(engineRootPath, AZ_CORRECT_AND_WRONG_FILESYSTEM_SEPARATOR);
AZStd::replace(normalized.begin(), normalized.end(), '/', '_'); AZ::IO::FixedMaxPathString enginePath;
AZStd::transform(engineRootPath.begin(), engineRootPath.end(),
AZStd::back_inserter(enginePath), AZStd::move(NormalizeEnginePath));
// Perform the CRC32 calculation // Perform the CRC32 calculation
const AZ::Crc32 branchTokenCrc(normalized.c_str(), normalized.size(), true); constexpr bool forceLowercase = true;
char branchToken[12]; return static_cast<AZ::u32>(AZ::Crc32(enginePath.c_str(), enginePath.size(), forceLowercase));
azsnprintf(branchToken, AZ_ARRAY_SIZE(branchToken), "0x%08X", static_cast<AZ::u32>(branchTokenCrc)); }
token = AZStd::string(branchToken); }
void CalculateBranchToken(AZStd::string_view engineRootPath, AZStd::string& token)
{
token = AZStd::string::format("0x%08X", Internal::CalculateBranchTokenHash(engineRootPath));
}
void CalculateBranchToken(AZStd::string_view engineRootPath, AZ::IO::FixedMaxPathString& token)
{
token = AZ::IO::FixedMaxPathString::format("0x%08X", Internal::CalculateBranchTokenHash(engineRootPath));
} }
} }

@ -485,10 +485,11 @@ namespace AZ
//! CalculateBranchToken //! CalculateBranchToken
/*! Calculate the branch token that is used for asset processor connection negotiations /*! Calculate the branch token that is used for asset processor connection negotiations
* *
* \param appRootPath - The absolute path of the app root to base the token calculation on * \param engineRootPath - The absolute path to the engine root to base the token calculation on
* \param token - The result of the branch token calculation * \param token - The result of the branch token calculation
*/ */
void CalculateBranchToken(const AZStd::string& appRootPath, AZStd::string& token); void CalculateBranchToken(AZStd::string_view engineRootPath, AZStd::string& token);
void CalculateBranchToken(AZStd::string_view engineRootPath, AZ::IO::FixedMaxPathString& token);
} }
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////

@ -41,7 +41,6 @@ namespace UnitTest
MOCK_METHOD0(GetSerializeContext, AZ::SerializeContext* ()); MOCK_METHOD0(GetSerializeContext, AZ::SerializeContext* ());
MOCK_METHOD0(GetJsonRegistrationContext, AZ::JsonRegistrationContext* ()); MOCK_METHOD0(GetJsonRegistrationContext, AZ::JsonRegistrationContext* ());
MOCK_METHOD0(GetBehaviorContext, AZ::BehaviorContext* ()); MOCK_METHOD0(GetBehaviorContext, AZ::BehaviorContext* ());
MOCK_CONST_METHOD0(GetAppRoot, const char* ());
MOCK_CONST_METHOD0(GetEngineRoot, const char* ()); MOCK_CONST_METHOD0(GetEngineRoot, const char* ());
MOCK_CONST_METHOD0(GetExecutableFolder, const char* ()); MOCK_CONST_METHOD0(GetExecutableFolder, const char* ());
MOCK_CONST_METHOD1(QueryApplicationType, void(AZ::ApplicationTypeQuery&)); MOCK_CONST_METHOD1(QueryApplicationType, void(AZ::ApplicationTypeQuery&));

@ -59,7 +59,6 @@ namespace UnitTest
AZ::SerializeContext* GetSerializeContext() override { return nullptr; } AZ::SerializeContext* GetSerializeContext() override { return nullptr; }
AZ::BehaviorContext* GetBehaviorContext() override { return m_behaviorContext; } AZ::BehaviorContext* GetBehaviorContext() override { return m_behaviorContext; }
AZ::JsonRegistrationContext* GetJsonRegistrationContext() override { return nullptr; } AZ::JsonRegistrationContext* GetJsonRegistrationContext() override { return nullptr; }
const char* GetAppRoot() const override { return nullptr; }
const char* GetEngineRoot() const override { return nullptr; } const char* GetEngineRoot() const override { return nullptr; }
const char* GetExecutableFolder() const override { return nullptr; } const char* GetExecutableFolder() const override { return nullptr; }
void EnumerateEntities(const EntityCallback& /*callback*/) override {} void EnumerateEntities(const EntityCallback& /*callback*/) override {}

@ -1065,21 +1065,16 @@ namespace UnitTest
, public UserSettingsFileLocatorBus::Handler , public UserSettingsFileLocatorBus::Handler
{ {
public: public:
void SetExecutableFolder(const char* path)
{
m_exeDirectory = path;
}
AZStd::string ResolveFilePath(u32 providerId) override AZStd::string ResolveFilePath(u32 providerId) override
{ {
AZStd::string filePath; AZStd::string filePath;
if (providerId == UserSettings::CT_GLOBAL) if (providerId == UserSettings::CT_GLOBAL)
{ {
filePath = (m_exeDirectory / "GlobalUserSettings.xml").String(); filePath = (AZ::IO::Path(GetTestFolderPath()) / "GlobalUserSettings.xml").Native();
} }
else if (providerId == UserSettings::CT_LOCAL) else if (providerId == UserSettings::CT_LOCAL)
{ {
filePath = (m_exeDirectory / "LocalUserSettings.xml").String(); filePath = (AZ::IO::Path(GetTestFolderPath()) / "LocalUserSettings.xml").Native();
} }
return filePath; return filePath;
} }
@ -1117,7 +1112,6 @@ namespace UnitTest
ComponentApplication::Descriptor appDesc; ComponentApplication::Descriptor appDesc;
appDesc.m_memoryBlocksByteSize = 10 * 1024 * 1024; appDesc.m_memoryBlocksByteSize = 10 * 1024 * 1024;
Entity* systemEntity = app.Create(appDesc); Entity* systemEntity = app.Create(appDesc);
app.SetExecutableFolder(GetTestFolderPath().c_str());
app.UserSettingsFileLocatorBus::Handler::BusConnect(); app.UserSettingsFileLocatorBus::Handler::BusConnect();
// Make sure user settings file does not exist at this point // Make sure user settings file does not exist at this point

@ -1240,7 +1240,6 @@ namespace UnitTest
SerializeContext* GetSerializeContext() override { return m_serializeContext.get(); } SerializeContext* GetSerializeContext() override { return m_serializeContext.get(); }
BehaviorContext* GetBehaviorContext() override { return nullptr; } BehaviorContext* GetBehaviorContext() override { return nullptr; }
JsonRegistrationContext* GetJsonRegistrationContext() override { return nullptr; } JsonRegistrationContext* GetJsonRegistrationContext() override { return nullptr; }
const char* GetAppRoot() const override { return nullptr; }
const char* GetEngineRoot() const override { return nullptr; } const char* GetEngineRoot() const override { return nullptr; }
const char* GetExecutableFolder() const override { return nullptr; } const char* GetExecutableFolder() const override { return nullptr; }
void EnumerateEntities(const EntityCallback& /*callback*/) override {} void EnumerateEntities(const EntityCallback& /*callback*/) override {}

@ -67,12 +67,6 @@ namespace AzFramework
/// Make path relative to the provided root. /// Make path relative to the provided root.
virtual void MakePathRelative(AZStd::string& /*fullPath*/, const char* /*rootPath*/) {} virtual void MakePathRelative(AZStd::string& /*fullPath*/, const char* /*rootPath*/) {}
/// Gets the engine root path where the modules for the current engine are located.
virtual const char* GetEngineRoot() const { return nullptr; }
/// Retrieves the app root path for the application.
virtual const char* GetAppRoot() const { return nullptr; }
/// Get the Command Line arguments passed in. /// Get the Command Line arguments passed in.
virtual const CommandLine* GetCommandLine() { return nullptr; } virtual const CommandLine* GetCommandLine() { return nullptr; }

@ -69,6 +69,7 @@
#include <AzCore/Console/Console.h> #include <AzCore/Console/Console.h>
#include <AzFramework/Viewport/ViewportBus.h> #include <AzFramework/Viewport/ViewportBus.h>
#include <GridMate/Memory.h> #include <GridMate/Memory.h>
#include <AzFramework/Physics/HeightfieldProviderBus.h>
#include "Application.h" #include "Application.h"
#include <AzFramework/AzFrameworkModule.h> #include <AzFramework/AzFrameworkModule.h>
@ -224,13 +225,6 @@ namespace AzFramework
} }
} }
void Application::PreModuleLoad()
{
SetRootPath(RootPathType::EngineRoot, m_engineRoot.c_str());
AZ_TracePrintf(s_azFrameworkWarningWindow, "Engine Path: %s\n", m_engineRoot.c_str());
}
void Application::Stop() void Application::Stop()
{ {
if (m_isStarted) if (m_isStarted)
@ -318,6 +312,8 @@ namespace AzFramework
AzFramework::SurfaceData::SurfaceTagWeight::Reflect(context); AzFramework::SurfaceData::SurfaceTagWeight::Reflect(context);
AzFramework::SurfaceData::SurfacePoint::Reflect(context); AzFramework::SurfaceData::SurfacePoint::Reflect(context);
AzFramework::Terrain::TerrainDataRequests::Reflect(context); AzFramework::Terrain::TerrainDataRequests::Reflect(context);
Physics::HeightfieldProviderRequests::Reflect(context);
Physics::HeightMaterialPoint::Reflect(context);
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context)) if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{ {
@ -394,11 +390,6 @@ namespace AzFramework
outModules.emplace_back(aznew AzFrameworkModule()); outModules.emplace_back(aznew AzFrameworkModule());
} }
const char* Application::GetAppRoot() const
{
return m_appRoot.c_str();
}
const char* Application::GetCurrentConfigurationName() const const char* Application::GetCurrentConfigurationName() const
{ {
#if defined(_RELEASE) #if defined(_RELEASE)
@ -434,19 +425,19 @@ namespace AzFramework
void Application::ResolveEnginePath(AZStd::string& engineRelativePath) const void Application::ResolveEnginePath(AZStd::string& engineRelativePath) const
{ {
AZ::IO::FixedMaxPath fullPath = m_engineRoot / engineRelativePath; auto fullPath = AZ::IO::FixedMaxPath(GetEngineRoot()) / engineRelativePath;
engineRelativePath = fullPath.String(); engineRelativePath = fullPath.String();
} }
void Application::CalculateBranchTokenForEngineRoot(AZStd::string& token) const void Application::CalculateBranchTokenForEngineRoot(AZStd::string& token) const
{ {
AzFramework::StringFunc::AssetPath::CalculateBranchToken(m_engineRoot.String(), token); AZ::StringFunc::AssetPath::CalculateBranchToken(GetEngineRoot(), token);
} }
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
void Application::MakePathRootRelative(AZStd::string& fullPath) void Application::MakePathRootRelative(AZStd::string& fullPath)
{ {
MakePathRelative(fullPath, m_engineRoot.c_str()); MakePathRelative(fullPath, GetEngineRoot());
} }
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
@ -582,30 +573,6 @@ namespace AzFramework
} }
} }
void Application::SetRootPath(RootPathType type, const char* source)
{
[[maybe_unused]] const size_t sourceLen = strlen(source);
// Copy the source path to the intended root path and correct the path separators as well
switch (type)
{
case RootPathType::AppRoot:
{
AZ_Assert(sourceLen < m_appRoot.Native().max_size(), "String overflow for App Root: %s", source);
m_appRoot = AZ::IO::PathView(source).LexicallyNormal();
}
break;
case RootPathType::EngineRoot:
{
AZ_Assert(sourceLen < m_engineRoot.Native().max_size(), "String overflow for Engine Root: %s", source);
m_engineRoot = AZ::IO::PathView(source).LexicallyNormal();
}
break;
default:
AZ_Assert(false, "Invalid RootPathType (%d)", static_cast<int>(type));
}
}
struct DeprecatedAliasesKeyVisitor struct DeprecatedAliasesKeyVisitor
: AZ::SettingsRegistryInterface::Visitor : AZ::SettingsRegistryInterface::Visitor
{ {

@ -95,8 +95,6 @@ namespace AzFramework
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
//! ApplicationRequests::Bus::Handler //! ApplicationRequests::Bus::Handler
const char* GetEngineRoot() const override { return m_engineRoot.c_str(); }
const char* GetAppRoot() const override;
void ResolveEnginePath(AZStd::string& engineRelativePath) const override; void ResolveEnginePath(AZStd::string& engineRelativePath) const override;
void CalculateBranchTokenForEngineRoot(AZStd::string& token) const override; void CalculateBranchTokenForEngineRoot(AZStd::string& token) const override;
bool IsPrefabSystemEnabled() const override; bool IsPrefabSystemEnabled() const override;
@ -146,8 +144,6 @@ namespace AzFramework
*/ */
void SetFileIOAliases(); void SetFileIOAliases();
void PreModuleLoad() override;
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
//! AZ::ComponentApplication //! AZ::ComponentApplication
void RegisterCoreComponents() override; void RegisterCoreComponents() override;
@ -182,12 +178,6 @@ namespace AzFramework
bool m_exitMainLoopRequested = false; bool m_exitMainLoopRequested = false;
enum class RootPathType
{
AppRoot,
EngineRoot
};
void SetRootPath(RootPathType type, const char* source);
}; };
} // namespace AzFramework } // namespace AzFramework

@ -0,0 +1,46 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "HeightfieldProviderBus.h"
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/Math/Transform.h>
#include <AzCore/Serialization/SerializeContext.h>
namespace Physics
{
void HeightfieldProviderRequests::Reflect(AZ::ReflectContext* context)
{
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->EBus<Physics::HeightfieldProviderRequestsBus>("HeightfieldProviderRequestsBus")
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
->Attribute(AZ::Script::Attributes::Module, "physics")
->Attribute(AZ::Script::Attributes::Category, "PhysX")
->Event("GetHeightfieldGridSpacing", &Physics::HeightfieldProviderRequestsBus::Events::GetHeightfieldGridSpacing)
->Event("GetHeightfieldAabb", &Physics::HeightfieldProviderRequestsBus::Events::GetHeightfieldAabb)
->Event("GetHeightfieldTransform", &Physics::HeightfieldProviderRequestsBus::Events::GetHeightfieldTransform)
->Event("GetMaterialList", &Physics::HeightfieldProviderRequestsBus::Events::GetMaterialList)
->Event("GetHeights", &Physics::HeightfieldProviderRequestsBus::Events::GetHeights)
->Event("GetHeightsAndMaterials", &Physics::HeightfieldProviderRequestsBus::Events::GetHeightsAndMaterials)
->Event("GetHeightfieldMinHeight", &Physics::HeightfieldProviderRequestsBus::Events::GetHeightfieldMinHeight)
->Event("GetHeightfieldMaxHeight", &Physics::HeightfieldProviderRequestsBus::Events::GetHeightfieldMaxHeight)
->Event("GetHeightfieldGridColumns", &Physics::HeightfieldProviderRequestsBus::Events::GetHeightfieldGridColumns)
->Event("GetHeightfieldGridRows", &Physics::HeightfieldProviderRequestsBus::Events::GetHeightfieldGridRows)
;
}
}
void HeightMaterialPoint::Reflect(AZ::ReflectContext* context)
{
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->Class<Physics::HeightMaterialPoint>()->Attribute(AZ::Script::Attributes::Category, "Physics");
}
}
} // namespace Physics

@ -26,10 +26,25 @@ namespace Physics
struct HeightMaterialPoint struct HeightMaterialPoint
{ {
HeightMaterialPoint(
float height = 0.0f, QuadMeshType type = QuadMeshType::SubdivideUpperLeftToBottomRight, uint8_t index = 0)
: m_height(height)
, m_quadMeshType(type)
, m_materialIndex(index)
, m_padding(0)
{
}
virtual ~HeightMaterialPoint() = default;
static void Reflect(AZ::ReflectContext* context);
AZ_RTTI(HeightMaterialPoint, "{DF167ED4-24E6-4F7B-8AB7-42622F7DBAD3}");
float m_height{ 0.0f }; //!< Holds the height of this point in the heightfield relative to the heightfield entity location. float m_height{ 0.0f }; //!< Holds the height of this point in the heightfield relative to the heightfield entity location.
QuadMeshType m_quadMeshType{ QuadMeshType::SubdivideUpperLeftToBottomRight }; //!< By default, create two triangles like this |\|, where this point is in the upper left corner. QuadMeshType m_quadMeshType{ QuadMeshType::SubdivideUpperLeftToBottomRight }; //!< By default, create two triangles like this |\|, where this point is in the upper left corner.
uint8_t m_materialIndex{ 0 }; //!< The surface material index for the upper left corner of this quad. uint8_t m_materialIndex{ 0 }; //!< The surface material index for the upper left corner of this quad.
uint16_t m_padding{ 0 }; //!< available for future use. uint16_t m_padding{ 0 }; //!< available for future use.
}; };
//! An interface to provide heightfield values. //! An interface to provide heightfield values.
@ -37,6 +52,8 @@ namespace Physics
: public AZ::ComponentBus : public AZ::ComponentBus
{ {
public: public:
static void Reflect(AZ::ReflectContext* context);
//! Returns the distance between each height in the map. //! Returns the distance between each height in the map.
//! @return Vector containing Column Spacing, Rows Spacing. //! @return Vector containing Column Spacing, Rows Spacing.
virtual AZ::Vector2 GetHeightfieldGridSpacing() const = 0; virtual AZ::Vector2 GetHeightfieldGridSpacing() const = 0;
@ -46,11 +63,27 @@ namespace Physics
//! @param numRows contains the size of the grid in the y direction. //! @param numRows contains the size of the grid in the y direction.
virtual void GetHeightfieldGridSize(int32_t& numColumns, int32_t& numRows) const = 0; virtual void GetHeightfieldGridSize(int32_t& numColumns, int32_t& numRows) const = 0;
//! Returns the height field gridsize columns.
//! @return the size of the grid in the x direction.
virtual int32_t GetHeightfieldGridColumns() const = 0;
//! Returns the height field gridsize rows.
//! @return the size of the grid in the y direction.
virtual int32_t GetHeightfieldGridRows() const = 0;
//! Returns the height field min and max height bounds. //! Returns the height field min and max height bounds.
//! @param minHeightBounds contains the minimum height that the heightfield can contain. //! @param minHeightBounds contains the minimum height that the heightfield can contain.
//! @param maxHeightBounds contains the maximum height that the heightfield can contain. //! @param maxHeightBounds contains the maximum height that the heightfield can contain.
virtual void GetHeightfieldHeightBounds(float& minHeightBounds, float& maxHeightBounds) const = 0; virtual void GetHeightfieldHeightBounds(float& minHeightBounds, float& maxHeightBounds) const = 0;
//! Returns the height field min height bounds.
//! @return the minimum height that the heightfield can contain.
virtual float GetHeightfieldMinHeight() const = 0;
//! Returns the height field max height bounds.
//! @return the maximum height that the heightfield can contain.
virtual float GetHeightfieldMaxHeight() const = 0;
//! Returns the AABB of the heightfield. //! Returns the AABB of the heightfield.
//! This is provided separately from the shape AABB because the heightfield might choose to modify the AABB bounds. //! This is provided separately from the shape AABB because the heightfield might choose to modify the AABB bounds.
//! @return AABB of the heightfield. //! @return AABB of the heightfield.

@ -360,6 +360,11 @@ namespace Physics
->Field("MaterialId", &Physics::MaterialId::m_id) ->Field("MaterialId", &Physics::MaterialId::m_id)
; ;
} }
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->Class<Physics::MaterialId>()->Attribute(AZ::Script::Attributes::Category, "Physics");
}
} }
MaterialId MaterialId::Create() MaterialId MaterialId::Create()

@ -229,6 +229,7 @@ set(FILES
Physics/Configuration/SystemConfiguration.h Physics/Configuration/SystemConfiguration.h
Physics/Configuration/SystemConfiguration.cpp Physics/Configuration/SystemConfiguration.cpp
Physics/HeightfieldProviderBus.h Physics/HeightfieldProviderBus.h
Physics/HeightfieldProviderBus.cpp
Physics/SimulatedBodies/RigidBody.h Physics/SimulatedBodies/RigidBody.h
Physics/SimulatedBodies/RigidBody.cpp Physics/SimulatedBodies/RigidBody.cpp
Physics/SimulatedBodies/StaticRigidBody.h Physics/SimulatedBodies/StaticRigidBody.h

@ -27,7 +27,6 @@ namespace AzQtComponents
setProperty("HasNoWindowDecorations", true); setProperty("HasNoWindowDecorations", true);
setAttribute(Qt::WA_ShowWithoutActivating); setAttribute(Qt::WA_ShowWithoutActivating);
setAttribute(Qt::WA_DeleteOnClose);
m_borderRadius = toastConfiguration.m_borderRadius; m_borderRadius = toastConfiguration.m_borderRadius;
if (m_borderRadius > 0) if (m_borderRadius > 0)

@ -31,7 +31,6 @@ namespace AzQtComponents
{ {
Q_OBJECT Q_OBJECT
public: public:
AZ_CLASS_ALLOCATOR(ToastNotification, AZ::SystemAllocator, 0);
ToastNotification(QWidget* parent, const ToastConfiguration& toastConfiguration); ToastNotification(QWidget* parent, const ToastConfiguration& toastConfiguration);
virtual ~ToastNotification(); virtual ~ToastNotification();
@ -73,7 +72,7 @@ namespace AzQtComponents
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
AZStd::chrono::milliseconds m_fadeDuration; AZStd::chrono::milliseconds m_fadeDuration;
AZStd::unique_ptr<Ui::ToastNotification> m_ui; QScopedPointer<Ui::ToastNotification> m_ui;
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
}; };
} // namespace AzQtComponents } // namespace AzQtComponents

@ -27,7 +27,6 @@ namespace AzQtComponents
class AZ_QT_COMPONENTS_API ToastConfiguration class AZ_QT_COMPONENTS_API ToastConfiguration
{ {
public: public:
AZ_CLASS_ALLOCATOR(ToastConfiguration, AZ::SystemAllocator, 0);
ToastConfiguration(ToastType toastType, const QString& title, const QString& description); ToastConfiguration(ToastType toastType, const QString& title, const QString& description);
bool m_closeOnClick = true; bool m_closeOnClick = true;

@ -14,7 +14,7 @@
#include <AzCore/Module/DynamicModuleHandle.h> #include <AzCore/Module/DynamicModuleHandle.h>
#include <AzCore/Settings/SettingsRegistryMergeUtils.h> #include <AzCore/Settings/SettingsRegistryMergeUtils.h>
#include <AzCore/StringFunc/StringFunc.h> #include <AzCore/StringFunc/StringFunc.h>
#include <AzFramework/API/ApplicationAPI.h> #include <AzCore/Utils/Utils.h>
#include <AzFramework/IO/LocalFileIO.h> #include <AzFramework/IO/LocalFileIO.h>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h> #include <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <AzToolsFramework/Asset/AssetUtils.h> #include <AzToolsFramework/Asset/AssetUtils.h>
@ -205,7 +205,7 @@ namespace AzToolsFramework::AssetUtils
return platformConfigFilePathsAdded; return platformConfigFilePathsAdded;
} }
AZStd::vector<AZ::IO::Path> GetConfigFiles(AZStd::string_view engineRoot, AZStd::string_view assetRoot, AZStd::string_view projectPath, AZStd::vector<AZ::IO::Path> GetConfigFiles(AZStd::string_view engineRoot, AZStd::string_view projectPath,
bool addPlatformConfigs, bool addGemsConfigs, AZ::SettingsRegistryInterface* settingsRegistry) bool addPlatformConfigs, bool addGemsConfigs, AZ::SettingsRegistryInterface* settingsRegistry)
{ {
constexpr const char* AssetProcessorGamePlatformConfigFileName = "AssetProcessorGamePlatformConfig.ini"; constexpr const char* AssetProcessorGamePlatformConfigFileName = "AssetProcessorGamePlatformConfig.ini";
@ -232,14 +232,13 @@ namespace AzToolsFramework::AssetUtils
Internal::AddGemConfigFiles(gemInfoList, configFiles); Internal::AddGemConfigFiles(gemInfoList, configFiles);
} }
AZ::IO::Path assetRootDir(assetRoot); AZ::IO::Path projectRoot(projectPath);
assetRootDir /= projectPath;
AZ::IO::Path projectConfigFile = assetRootDir / AssetProcessorGamePlatformConfigFileName; AZ::IO::Path projectConfigFile = projectRoot / AssetProcessorGamePlatformConfigFileName;
configFiles.push_back(projectConfigFile); configFiles.push_back(projectConfigFile);
// Add a file entry for the Project AssetProcessor setreg file // Add a file entry for the Project AssetProcessor setreg file
projectConfigFile = assetRootDir / AssetProcessorGamePlatformConfigSetreg; projectConfigFile = projectRoot / AssetProcessorGamePlatformConfigSetreg;
configFiles.push_back(projectConfigFile); configFiles.push_back(projectConfigFile);
return configFiles; return configFiles;
@ -251,10 +250,10 @@ namespace AzToolsFramework::AssetUtils
AZStd::vector<AZStd::string> tokens; AZStd::vector<AZStd::string> tokens;
AZ::StringFunc::Tokenize(relPathFromRoot.c_str(), tokens, AZ_CORRECT_FILESYSTEM_SEPARATOR_STRING); AZ::StringFunc::Tokenize(relPathFromRoot.c_str(), tokens, AZ_CORRECT_FILESYSTEM_SEPARATOR_STRING);
AZStd::string validatedPath; AZ::IO::FixedMaxPath validatedPath;
if (rootPath.empty()) if (rootPath.empty())
{ {
AzFramework::ApplicationRequests::Bus::BroadcastResult(validatedPath, &AzFramework::ApplicationRequests::GetEngineRoot); validatedPath = AZ::Utils::GetEnginePath();
} }
else else
{ {
@ -299,10 +298,7 @@ namespace AzToolsFramework::AssetUtils
break; break;
} }
AZStd::string absoluteFilePath; validatedPath /= element; // go one step deeper.
AZ::StringFunc::Path::ConstructFull(validatedPath.c_str(), element.c_str(), absoluteFilePath);
validatedPath = absoluteFilePath; // go one step deeper.
} }
if (success) if (success)

@ -40,7 +40,7 @@ namespace AzToolsFramework::AssetUtils
//! Also note that if the project has any "game project gems", then those will also be inserted last, //! Also note that if the project has any "game project gems", then those will also be inserted last,
//! and thus have a higher priority than the root or non - project gems. //! and thus have a higher priority than the root or non - project gems.
//! Also note that the game project could be in a different location to the engine therefore we need the assetRoot param. //! Also note that the game project could be in a different location to the engine therefore we need the assetRoot param.
AZStd::vector<AZ::IO::Path> GetConfigFiles(AZStd::string_view engineRoot, AZStd::string_view assetRoot, AZStd::string_view projectPath, AZStd::vector<AZ::IO::Path> GetConfigFiles(AZStd::string_view engineRoot, AZStd::string_view projectPath,
bool addPlatformConfigs = true, bool addGemsConfigs = true, AZ::SettingsRegistryInterface* settingsRegistry = nullptr); bool addPlatformConfigs = true, bool addGemsConfigs = true, AZ::SettingsRegistryInterface* settingsRegistry = nullptr);
//! A utility function which checks the given path starting at the root and updates the relative path to be the actual case correct path. //! A utility function which checks the given path starting at the root and updates the relative path to be the actual case correct path.

@ -9,11 +9,11 @@
#include <AzCore/EBus/Results.h> #include <AzCore/EBus/Results.h>
#include <AzCore/std/string/string.h> #include <AzCore/std/string/string.h>
#include <AzCore/std/containers/vector.h> #include <AzCore/std/containers/vector.h>
#include <AzCore/Utils/Utils.h>
#include <AzFramework/StringFunc/StringFunc.h> #include <AzFramework/StringFunc/StringFunc.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserBus.h> #include <AzToolsFramework/AssetBrowser/AssetBrowserBus.h>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h> #include <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <AzToolsFramework/AssetBrowser/Thumbnails/SourceThumbnail.h> #include <AzToolsFramework/AssetBrowser/Thumbnails/SourceThumbnail.h>
#include <AzFramework/API/ApplicationAPI.h>
#include <QString> #include <QString>
namespace AzToolsFramework namespace AzToolsFramework
@ -113,11 +113,9 @@ namespace AzToolsFramework
if (iconPathToUse.isEmpty()) if (iconPathToUse.isEmpty())
{ {
const char* engineRoot = nullptr; AZ::IO::FixedMaxPath engineRoot = AZ::Utils::GetEnginePath();
AzFramework::ApplicationRequests::Bus::BroadcastResult(engineRoot, &AzFramework::ApplicationRequests::GetEngineRoot); AZ_Assert(!engineRoot.empty(), "Engine Root not initialized");
AZ_Assert(engineRoot, "Engine Root not initialized"); iconPathToUse = (engineRoot / DefaultFileIconPath).c_str();
AZStd::string iconPath = AZStd::string::format("%s%s", engineRoot, DefaultFileIconPath);
iconPathToUse = iconPath.c_str();
} }
m_pixmap.load(iconPathToUse); m_pixmap.load(iconPathToUse);

@ -15,6 +15,7 @@
#include <AzCore/std/sort.h> #include <AzCore/std/sort.h>
#include <AzCore/RTTI/AttributeReader.h> #include <AzCore/RTTI/AttributeReader.h>
#include <AzCore/Serialization/SerializeContext.h> #include <AzCore/Serialization/SerializeContext.h>
#include <AzFramework/API/ApplicationAPI.h>
#include <AzToolsFramework/Commands/EntityStateCommand.h> #include <AzToolsFramework/Commands/EntityStateCommand.h>
#include <AzToolsFramework/ContainerEntity/ContainerEntityInterface.h> #include <AzToolsFramework/ContainerEntity/ContainerEntityInterface.h>
#include <AzToolsFramework/Entity/EditorEntityInfoBus.h> #include <AzToolsFramework/Entity/EditorEntityInfoBus.h>
@ -468,6 +469,18 @@ namespace AzToolsFramework
EntityIdList children; EntityIdList children;
EditorEntityInfoRequestBus::EventResult(children, parentId, &EditorEntityInfoRequestBus::Events::GetChildren); EditorEntityInfoRequestBus::EventResult(children, parentId, &EditorEntityInfoRequestBus::Events::GetChildren);
// If Prefabs are enabled, don't check the order for an invalid parent, just return its children (i.e. the root container entity)
// There will currently always be one root container entity, so there's no order to retrieve
if (!parentId.IsValid())
{
bool isPrefabEnabled = false;
AzFramework::ApplicationRequests::Bus::BroadcastResult(isPrefabEnabled, &AzFramework::ApplicationRequests::IsPrefabSystemEnabled);
if (isPrefabEnabled)
{
return children;
}
}
EntityIdList entityChildOrder; EntityIdList entityChildOrder;
AZ::EntityId sortEntityId = GetEntityIdForSortInfo(parentId); AZ::EntityId sortEntityId = GetEntityIdForSortInfo(parentId);
EditorEntitySortRequestBus::EventResult(entityChildOrder, sortEntityId, &EditorEntitySortRequestBus::Events::GetChildEntityOrderArray); EditorEntitySortRequestBus::EventResult(entityChildOrder, sortEntityId, &EditorEntitySortRequestBus::Events::GetChildEntityOrderArray);

@ -11,6 +11,8 @@
#include <AzCore/Debug/Profiler.h> #include <AzCore/Debug/Profiler.h>
#include <AzCore/Serialization/EditContext.h> #include <AzCore/Serialization/EditContext.h>
#include <AzCore/std/sort.h> #include <AzCore/std/sort.h>
#include <AzFramework/API/ApplicationAPI.h>
#include <AzToolsFramework/Prefab/PrefabPublicInterface.h>
static_assert(sizeof(AZ::u64) == sizeof(AZ::EntityId), "We use AZ::EntityId for Persistent ID, which is a u64 under the hood. These must be the same size otherwise the persistent id will have to be rewritten"); static_assert(sizeof(AZ::u64) == sizeof(AZ::EntityId), "We use AZ::EntityId for Persistent ID, which is a u64 under the hood. These must be the same size otherwise the persistent id will have to be rewritten");
@ -144,6 +146,12 @@ namespace AzToolsFramework
bool EditorEntitySortComponent::AddChildEntityInternal(const AZ::EntityId& entityId, bool addToBack, EntityOrderArray::iterator insertPosition) bool EditorEntitySortComponent::AddChildEntityInternal(const AZ::EntityId& entityId, bool addToBack, EntityOrderArray::iterator insertPosition)
{ {
AZ_PROFILE_FUNCTION(AzToolsFramework); AZ_PROFILE_FUNCTION(AzToolsFramework);
if (m_ignoreIncomingOrderChanges)
{
return true;
}
auto entityItr = m_childEntityOrderCache.find(entityId); auto entityItr = m_childEntityOrderCache.find(entityId);
if (entityItr == m_childEntityOrderCache.end()) if (entityItr == m_childEntityOrderCache.end())
{ {
@ -198,6 +206,12 @@ namespace AzToolsFramework
bool EditorEntitySortComponent::RemoveChildEntity(const AZ::EntityId& entityId) bool EditorEntitySortComponent::RemoveChildEntity(const AZ::EntityId& entityId)
{ {
AZ_PROFILE_FUNCTION(AzToolsFramework); AZ_PROFILE_FUNCTION(AzToolsFramework);
if (m_ignoreIncomingOrderChanges)
{
return true;
}
auto entityItr = m_childEntityOrderCache.find(entityId); auto entityItr = m_childEntityOrderCache.find(entityId);
if (entityItr != m_childEntityOrderCache.end()) if (entityItr != m_childEntityOrderCache.end())
{ {
@ -250,11 +264,30 @@ namespace AzToolsFramework
} }
} }
void EditorEntitySortComponent::OnPrefabInstancePropagationBegin()
{
m_ignoreIncomingOrderChanges = true;
}
void EditorEntitySortComponent::OnPrefabInstancePropagationEnd()
{
m_ignoreIncomingOrderChanges = false;
}
void EditorEntitySortComponent::MarkDirtyAndSendChangedEvent() void EditorEntitySortComponent::MarkDirtyAndSendChangedEvent()
{ {
// mark the order as dirty before sending the ChildEntityOrderArrayUpdated event in order for PrepareSave to be properly handled in the case // mark the order as dirty before sending the ChildEntityOrderArrayUpdated event in order for PrepareSave to be properly handled in the case
// one of the event listeners needs to build the InstanceDataHierarchy // one of the event listeners needs to build the InstanceDataHierarchy
m_entityOrderIsDirty = true; m_entityOrderIsDirty = true;
// Force an immediate update for prefabs, which won't receive PrepareSave
bool isPrefabEnabled = false;
AzFramework::ApplicationRequests::Bus::BroadcastResult(
isPrefabEnabled, &AzFramework::ApplicationRequests::IsPrefabSystemEnabled);
if (isPrefabEnabled)
{
PrepareSave();
}
EditorEntitySortNotificationBus::Event(GetEntityId(), &EditorEntitySortNotificationBus::Events::ChildEntityOrderArrayUpdated); EditorEntitySortNotificationBus::Event(GetEntityId(), &EditorEntitySortNotificationBus::Events::ChildEntityOrderArrayUpdated);
} }
@ -264,10 +297,20 @@ namespace AzToolsFramework
// This is a special case for certain EditorComponents only! // This is a special case for certain EditorComponents only!
EditorEntitySortRequestBus::Handler::BusConnect(GetEntityId()); EditorEntitySortRequestBus::Handler::BusConnect(GetEntityId());
EditorEntityContextNotificationBus::Handler::BusConnect(); EditorEntityContextNotificationBus::Handler::BusConnect();
AzToolsFramework::Prefab::PrefabPublicNotificationBus::Handler::BusConnect();
} }
void EditorEntitySortComponent::Activate() void EditorEntitySortComponent::Activate()
{ {
// Run the post-serialize handler if prefabs are enabled because PostLoad won't be called automatically
bool isPrefabEnabled = false;
AzFramework::ApplicationRequests::Bus::BroadcastResult(
isPrefabEnabled, &AzFramework::ApplicationRequests::IsPrefabSystemEnabled);
if (isPrefabEnabled)
{
PostLoad();
}
// Send out that the order for our entity is now updated // Send out that the order for our entity is now updated
EditorEntitySortNotificationBus::Event(GetEntityId(), &EditorEntitySortNotificationBus::Events::ChildEntityOrderArrayUpdated); EditorEntitySortNotificationBus::Event(GetEntityId(), &EditorEntitySortNotificationBus::Events::ChildEntityOrderArrayUpdated);
} }

@ -10,6 +10,7 @@
#include "EditorEntitySortBus.h" #include "EditorEntitySortBus.h"
#include <AzToolsFramework/ToolsComponents/EditorComponentBase.h> #include <AzToolsFramework/ToolsComponents/EditorComponentBase.h>
#include <AzToolsFramework/Entity/EditorEntityContextBus.h> #include <AzToolsFramework/Entity/EditorEntityContextBus.h>
#include <AzToolsFramework/Prefab/PrefabPublicNotificationBus.h>
#include <AzCore/Serialization/SerializeContext.h> #include <AzCore/Serialization/SerializeContext.h>
namespace AzToolsFramework namespace AzToolsFramework
@ -20,6 +21,7 @@ namespace AzToolsFramework
: public AzToolsFramework::Components::EditorComponentBase : public AzToolsFramework::Components::EditorComponentBase
, public EditorEntitySortRequestBus::Handler , public EditorEntitySortRequestBus::Handler
, public EditorEntityContextNotificationBus::Handler , public EditorEntityContextNotificationBus::Handler
, public AzToolsFramework::Prefab::PrefabPublicNotificationBus::Handler
{ {
public: public:
AZ_COMPONENT(EditorEntitySortComponent, "{6EA1E03D-68B2-466D-97F7-83998C8C27F0}", EditorComponentBase); AZ_COMPONENT(EditorEntitySortComponent, "{6EA1E03D-68B2-466D-97F7-83998C8C27F0}", EditorComponentBase);
@ -45,6 +47,9 @@ namespace AzToolsFramework
// EditorEntityContextNotificationBus::Handler // EditorEntityContextNotificationBus::Handler
void OnEntityStreamLoadSuccess() override; void OnEntityStreamLoadSuccess() override;
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
void OnPrefabInstancePropagationBegin() override;
void OnPrefabInstancePropagationEnd() override;
private: private:
void MarkDirtyAndSendChangedEvent(); void MarkDirtyAndSendChangedEvent();
bool AddChildEntityInternal(const AZ::EntityId& entityId, bool addToBack, EntityOrderArray::iterator insertPosition); bool AddChildEntityInternal(const AZ::EntityId& entityId, bool addToBack, EntityOrderArray::iterator insertPosition);
@ -106,6 +111,7 @@ namespace AzToolsFramework
EntityOrderCache m_childEntityOrderCache; ///< The map of entity id to index for quick look up EntityOrderCache m_childEntityOrderCache; ///< The map of entity id to index for quick look up
bool m_entityOrderIsDirty = true; ///< This flag indicates our stored serialization order data is out of date and must be rebuilt before serialization occurs bool m_entityOrderIsDirty = true; ///< This flag indicates our stored serialization order data is out of date and must be rebuilt before serialization occurs
bool m_ignoreIncomingOrderChanges = false; ///< This is set when prefab propagation occurs so that non-authored order changes can be ignored
}; };
} }
} // namespace AzToolsFramework } // namespace AzToolsFramework

@ -1144,6 +1144,10 @@ namespace AzToolsFramework
AZ::EntityId firstEntityIdToDelete = entityIdsNoFocusContainer[0]; AZ::EntityId firstEntityIdToDelete = entityIdsNoFocusContainer[0];
InstanceOptionalReference commonOwningInstance = GetOwnerInstanceByEntityId(firstEntityIdToDelete); InstanceOptionalReference commonOwningInstance = GetOwnerInstanceByEntityId(firstEntityIdToDelete);
if (!commonOwningInstance.has_value())
{
return AZ::Failure(AZStd::string("Cannot delete entities belonging to an invalid instance"));
}
// If the first entity id is a container entity id, then we need to mark its parent as the common owning instance because you // If the first entity id is a container entity id, then we need to mark its parent as the common owning instance because you
// cannot delete an instance from itself. // cannot delete an instance from itself.

@ -6,10 +6,10 @@
* *
*/ */
#include <AzFramework/StringFunc/StringFunc.h> #include <AzCore/StringFunc/StringFunc.h>
#include <AzCore/Utils/Utils.h>
#include <AzToolsFramework/Thumbnails/SourceControlThumbnail.h> #include <AzToolsFramework/Thumbnails/SourceControlThumbnail.h>
#include <AzToolsFramework/SourceControl/SourceControlAPI.h> #include <AzToolsFramework/SourceControl/SourceControlAPI.h>
#include <AzFramework/API/ApplicationAPI.h>
namespace AzToolsFramework namespace AzToolsFramework
{ {
@ -68,12 +68,12 @@ namespace AzToolsFramework
SourceControlThumbnail::SourceControlThumbnail(SharedThumbnailKey key) SourceControlThumbnail::SourceControlThumbnail(SharedThumbnailKey key)
: Thumbnail(key) : Thumbnail(key)
{ {
const char* engineRoot = nullptr; AZ::IO::FixedMaxPath engineRoot = AZ::Utils::GetEnginePath();
AzFramework::ApplicationRequests::Bus::BroadcastResult(engineRoot, &AzFramework::ApplicationRequests::GetEngineRoot); AZ_Assert(!engineRoot.empty(), "Engine Root not initialized");
AZ_Assert(engineRoot, "Engine Root not initialized");
m_writableIconPath = (engineRoot / WRITABLE_ICON_PATH).String();
m_nonWritableIconPath = (engineRoot / NONWRITABLE_ICON_PATH).String();
AzFramework::StringFunc::Path::Join(engineRoot, WRITABLE_ICON_PATH, m_writableIconPath);
AzFramework::StringFunc::Path::Join(engineRoot, NONWRITABLE_ICON_PATH, m_nonWritableIconPath);
BusConnect(); BusConnect();
} }
@ -90,8 +90,8 @@ namespace AzToolsFramework
AZ_Assert(sourceControlKey, "Incorrect key type, excpected SourceControlThumbnailKey"); AZ_Assert(sourceControlKey, "Incorrect key type, excpected SourceControlThumbnailKey");
AZStd::string myFileName(sourceControlKey->GetFileName()); AZStd::string myFileName(sourceControlKey->GetFileName());
AzFramework::StringFunc::Path::Normalize(myFileName); AZ::StringFunc::Path::Normalize(myFileName);
if (AzFramework::StringFunc::Equal(myFileName.c_str(), filename)) if (AZ::StringFunc::Equal(myFileName.c_str(), filename))
{ {
Update(); Update();
} }

@ -129,7 +129,7 @@ namespace AzToolsFramework
ToastId ToastNotificationsView::CreateToastNotification(const AzQtComponents::ToastConfiguration& toastConfiguration) ToastId ToastNotificationsView::CreateToastNotification(const AzQtComponents::ToastConfiguration& toastConfiguration)
{ {
AzQtComponents::ToastNotification* notification = aznew AzQtComponents::ToastNotification(parentWidget(), toastConfiguration); AzQtComponents::ToastNotification* notification = new AzQtComponents::ToastNotification(this, toastConfiguration);
ToastId toastId = AZ::Entity::MakeId(); ToastId toastId = AZ::Entity::MakeId();
m_notifications[toastId] = notification; m_notifications[toastId] = notification;

@ -1116,7 +1116,6 @@ namespace UnitTest
SerializeContext* GetSerializeContext() override { return m_serializeContext.get(); } SerializeContext* GetSerializeContext() override { return m_serializeContext.get(); }
BehaviorContext* GetBehaviorContext() override { return nullptr; } BehaviorContext* GetBehaviorContext() override { return nullptr; }
JsonRegistrationContext* GetJsonRegistrationContext() override { return nullptr; } JsonRegistrationContext* GetJsonRegistrationContext() override { return nullptr; }
const char* GetAppRoot() const override { return nullptr; }
const char* GetEngineRoot() const override { return nullptr; } const char* GetEngineRoot() const override { return nullptr; }
const char* GetExecutableFolder() const override { return nullptr; } const char* GetExecutableFolder() const override { return nullptr; }
void EnumerateEntities(const EntityCallback& /*callback*/) override {} void EnumerateEntities(const EntityCallback& /*callback*/) override {}

@ -36,11 +36,6 @@ namespace UnitTest
: public ComponentApplication : public ComponentApplication
{ {
public: public:
void SetExecutableFolder(const char* path)
{
m_exeDirectory = path;
}
void SetSettingsRegistrySpecializations(SettingsRegistryInterface::Specializations& specializations) override void SetSettingsRegistrySpecializations(SettingsRegistryInterface::Specializations& specializations) override
{ {
ComponentApplication::SetSettingsRegistrySpecializations(specializations); ComponentApplication::SetSettingsRegistrySpecializations(specializations);

@ -346,9 +346,7 @@ namespace AssetBundler
} }
// Determine the enabled platforms // Determine the enabled platforms
const char* appRoot = nullptr; m_enabledPlatforms = GetEnabledPlatformFlags(GetEngineRoot(), AZStd::string_view(AZ::Utils::GetProjectPath()));
AzFramework::ApplicationRequests::Bus::BroadcastResult(appRoot, &AzFramework::ApplicationRequests::GetAppRoot);
m_enabledPlatforms = GetEnabledPlatformFlags(GetEngineRoot(), appRoot, AZ::Utils::GetProjectPath().c_str());
// Determine which Gems are enabled for the current project // Determine which Gems are enabled for the current project
if (!AzFramework::GetGemsInfo(m_gemInfoList, *m_settingsRegistry)) if (!AzFramework::GetGemsInfo(m_gemInfoList, *m_settingsRegistry))

@ -1401,7 +1401,6 @@ namespace AssetBundler
// If no platform was specified, defaulting to platforms specified in the asset processor config files // If no platform was specified, defaulting to platforms specified in the asset processor config files
AzFramework::PlatformFlags platformFlags = GetEnabledPlatformFlags( AzFramework::PlatformFlags platformFlags = GetEnabledPlatformFlags(
AZStd::string_view{ AZ::Utils::GetEnginePath() },
AZStd::string_view{ AZ::Utils::GetEnginePath() }, AZStd::string_view{ AZ::Utils::GetEnginePath() },
AZStd::string_view{ AZ::Utils::GetProjectPath() }); AZStd::string_view{ AZ::Utils::GetProjectPath() });
[[maybe_unused]] auto platformsString = AzFramework::PlatformHelper::GetCommaSeparatedPlatformList(platformFlags); [[maybe_unused]] auto platformsString = AzFramework::PlatformHelper::GetCommaSeparatedPlatformList(platformFlags);

@ -377,7 +377,6 @@ namespace AssetBundler
AzFramework::PlatformFlags GetEnabledPlatformFlags( AzFramework::PlatformFlags GetEnabledPlatformFlags(
AZStd::string_view engineRoot, AZStd::string_view engineRoot,
AZStd::string_view assetRoot,
AZStd::string_view projectPath) AZStd::string_view projectPath)
{ {
auto settingsRegistry = AZ::SettingsRegistry::Get(); auto settingsRegistry = AZ::SettingsRegistry::Get();
@ -387,7 +386,7 @@ namespace AssetBundler
return AzFramework::PlatformFlags::Platform_NONE; return AzFramework::PlatformFlags::Platform_NONE;
} }
auto configFiles = AzToolsFramework::AssetUtils::GetConfigFiles(engineRoot, assetRoot, projectPath, true, true, settingsRegistry); auto configFiles = AzToolsFramework::AssetUtils::GetConfigFiles(engineRoot, projectPath, true, true, settingsRegistry);
auto enabledPlatformList = AzToolsFramework::AssetUtils::GetEnabledPlatforms(*settingsRegistry, configFiles); auto enabledPlatformList = AzToolsFramework::AssetUtils::GetEnabledPlatforms(*settingsRegistry, configFiles);
AzFramework::PlatformFlags platformFlags = AzFramework::PlatformFlags::Platform_NONE; AzFramework::PlatformFlags platformFlags = AzFramework::PlatformFlags::Platform_NONE;
for (const auto& enabledPlatform : enabledPlatformList) for (const auto& enabledPlatform : enabledPlatformList)

@ -221,7 +221,6 @@ namespace AssetBundler
//! Please note that the game project could be in a different location to the engine therefore we need the assetRoot param. //! Please note that the game project could be in a different location to the engine therefore we need the assetRoot param.
AzFramework::PlatformFlags GetEnabledPlatformFlags( AzFramework::PlatformFlags GetEnabledPlatformFlags(
AZStd::string_view enginePath, AZStd::string_view enginePath,
AZStd::string_view assetRoot,
AZStd::string_view projectPath); AZStd::string_view projectPath);
QJsonObject ReadJson(const AZStd::string& filePath); QJsonObject ReadJson(const AZStd::string& filePath);

@ -67,7 +67,7 @@ namespace AssetBundler
void NormalizePathKeepCase(AZStd::string& /*path*/) override {} void NormalizePathKeepCase(AZStd::string& /*path*/) override {}
void CalculateBranchTokenForEngineRoot(AZStd::string& /*token*/) const override {} void CalculateBranchTokenForEngineRoot(AZStd::string& /*token*/) const override {}
const char* GetEngineRoot() const override const char* GetTempDir() const
{ {
return m_tempDir->GetDirectory(); return m_tempDir->GetDirectory();
} }
@ -83,7 +83,7 @@ namespace AssetBundler
TEST_F(MockUtilsTest, DISABLED_TestFilePath_StartsWithAFileSeparator_Valid) TEST_F(MockUtilsTest, DISABLED_TestFilePath_StartsWithAFileSeparator_Valid)
{ {
AZ::IO::Path relFilePath = "Foo/foo.xml"; AZ::IO::Path relFilePath = "Foo/foo.xml";
AZ::IO::Path absoluteFilePath = AZ::IO::PathView(GetEngineRoot()).RootPath(); AZ::IO::Path absoluteFilePath = AZ::IO::PathView(GetTempDir()).RootPath();
absoluteFilePath /= relFilePath; absoluteFilePath /= relFilePath;
absoluteFilePath = absoluteFilePath.LexicallyNormal(); absoluteFilePath = absoluteFilePath.LexicallyNormal();
@ -95,7 +95,7 @@ namespace AssetBundler
TEST_F(MockUtilsTest, TestFilePath_RelativePath_Valid) TEST_F(MockUtilsTest, TestFilePath_RelativePath_Valid)
{ {
AZ::IO::Path relFilePath = "Foo\\foo.xml"; AZ::IO::Path relFilePath = "Foo\\foo.xml";
AZ::IO::Path absoluteFilePath = (AZ::IO::Path(GetEngineRoot()) / relFilePath).LexicallyNormal(); AZ::IO::Path absoluteFilePath = (AZ::IO::Path(GetTempDir()) / relFilePath).LexicallyNormal();
FilePath filePath(relFilePath.Native()); FilePath filePath(relFilePath.Native());
EXPECT_EQ(AZ::IO::PathView{ filePath.AbsolutePath() }, absoluteFilePath); EXPECT_EQ(AZ::IO::PathView{ filePath.AbsolutePath() }, absoluteFilePath);
} }
@ -107,8 +107,8 @@ namespace AssetBundler
AZ::IO::Path relFilePath = "Foo\\Foo.xml"; AZ::IO::Path relFilePath = "Foo\\Foo.xml";
AZ::IO::Path wrongCaseRelFilePath = "Foo\\foo.xml"; AZ::IO::Path wrongCaseRelFilePath = "Foo\\foo.xml";
AZ::IO::Path correctAbsoluteFilePath = (AZ::IO::Path(GetEngineRoot()) / relFilePath).LexicallyNormal(); AZ::IO::Path correctAbsoluteFilePath = (AZ::IO::Path(GetTempDir()) / relFilePath).LexicallyNormal();
AZ::IO::Path wrongCaseAbsoluteFilePath = (AZ::IO::Path(GetEngineRoot()) / wrongCaseRelFilePath).LexicallyNormal(); AZ::IO::Path wrongCaseAbsoluteFilePath = (AZ::IO::Path(GetTempDir()) / wrongCaseRelFilePath).LexicallyNormal();
AZ::IO::HandleType fileHandle = AZ::IO::InvalidHandle; AZ::IO::HandleType fileHandle = AZ::IO::InvalidHandle;
AZ::IO::FileIOBase::GetInstance()->Open(correctAbsoluteFilePath.c_str(), AZ::IO::OpenMode::ModeWrite | AZ::IO::OpenMode::ModeCreatePath, fileHandle); AZ::IO::FileIOBase::GetInstance()->Open(correctAbsoluteFilePath.c_str(), AZ::IO::OpenMode::ModeWrite | AZ::IO::OpenMode::ModeCreatePath, fileHandle);
@ -121,7 +121,7 @@ namespace AssetBundler
TEST_F(MockUtilsTest, TestFilePath_NoFileExists_NoError_valid) TEST_F(MockUtilsTest, TestFilePath_NoFileExists_NoError_valid)
{ {
AZ::IO::Path relFilePath = "Foo\\Foo.xml"; AZ::IO::Path relFilePath = "Foo\\Foo.xml";
AZ::IO::Path absoluteFilePath = (AZ::IO::Path(GetEngineRoot()) / relFilePath).LexicallyNormal(); AZ::IO::Path absoluteFilePath = (AZ::IO::Path(GetTempDir()) / relFilePath).LexicallyNormal();
FilePath filePath(absoluteFilePath.Native(), true, false); FilePath filePath(absoluteFilePath.Native(), true, false);
EXPECT_TRUE(filePath.IsValid()); EXPECT_TRUE(filePath.IsValid());
@ -132,8 +132,8 @@ namespace AssetBundler
{ {
AZStd::string relFilePath = "Foo\\Foo.xml"; AZStd::string relFilePath = "Foo\\Foo.xml";
AZStd::string wrongCaseRelFilePath = "Foo\\foo.xml"; AZStd::string wrongCaseRelFilePath = "Foo\\foo.xml";
AZ::IO::Path correctAbsoluteFilePath = (AZ::IO::Path(GetEngineRoot()) / relFilePath).LexicallyNormal(); AZ::IO::Path correctAbsoluteFilePath = (AZ::IO::Path(GetTempDir()) / relFilePath).LexicallyNormal();
AZ::IO::Path wrongCaseAbsoluteFilePath = (AZ::IO::Path(GetEngineRoot()) / wrongCaseRelFilePath).LexicallyNormal(); AZ::IO::Path wrongCaseAbsoluteFilePath = (AZ::IO::Path(GetTempDir()) / wrongCaseRelFilePath).LexicallyNormal();
AZ::IO::HandleType fileHandle = AZ::IO::InvalidHandle; AZ::IO::HandleType fileHandle = AZ::IO::InvalidHandle;
AZ::IO::FileIOBase::GetInstance()->Open(correctAbsoluteFilePath.c_str(), AZ::IO::OpenMode::ModeWrite | AZ::IO::OpenMode::ModeCreatePath, fileHandle); AZ::IO::FileIOBase::GetInstance()->Open(correctAbsoluteFilePath.c_str(), AZ::IO::OpenMode::ModeWrite | AZ::IO::OpenMode::ModeCreatePath, fileHandle);

@ -16,6 +16,7 @@
#include <AzCore/Settings/SettingsRegistryImpl.h> #include <AzCore/Settings/SettingsRegistryImpl.h>
#include <AzCore/Settings/SettingsRegistryMergeUtils.h> #include <AzCore/Settings/SettingsRegistryMergeUtils.h>
#include <AzCore/UserSettings/UserSettingsComponent.h> #include <AzCore/UserSettings/UserSettingsComponent.h>
#include <AzCore/Utils/Utils.h>
#include <source/utils/utils.h> #include <source/utils/utils.h>
#include <source/utils/applicationManager.h> #include <source/utils/applicationManager.h>
@ -84,10 +85,9 @@ namespace AssetBundler
// in the unit tests. // in the unit tests.
AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize); AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize);
const char* engineRoot = nullptr; AZ::IO::FixedMaxPath engineRoot = AZ::Utils::GetEnginePath();
AzFramework::ApplicationRequests::Bus::BroadcastResult(engineRoot, &AzFramework::ApplicationRequests::GetEngineRoot); ASSERT_TRUE(!engineRoot.empty()) << "Unable to locate engine root.\n";
ASSERT_TRUE(engineRoot) << "Unable to locate engine root.\n"; m_data->m_testEngineRoot = (engineRoot / RelativeTestFolder).String();
AzFramework::StringFunc::Path::Join(engineRoot, RelativeTestFolder, m_data->m_testEngineRoot);
m_data->m_localFileIO = aznew AZ::IO::LocalFileIO(); m_data->m_localFileIO = aznew AZ::IO::LocalFileIO();
m_data->m_priorFileIO = AZ::IO::FileIOBase::GetInstance(); m_data->m_priorFileIO = AZ::IO::FileIOBase::GetInstance();
@ -150,7 +150,8 @@ namespace AssetBundler
EXPECT_EQ(0, gemsNameMap.size()); EXPECT_EQ(0, gemsNameMap.size());
AzFramework::PlatformFlags platformFlags = GetEnabledPlatformFlags(m_data->m_testEngineRoot.c_str(), m_data->m_testEngineRoot.c_str(), DummyProjectName); const auto testProjectPath = AZ::IO::Path(m_data->m_testEngineRoot) / DummyProjectName;
AzFramework::PlatformFlags platformFlags = GetEnabledPlatformFlags(m_data->m_testEngineRoot, testProjectPath.Native());
AzFramework::PlatformFlags hostPlatformFlag = AzFramework::PlatformHelper::GetPlatformFlag(AzToolsFramework::AssetSystem::GetHostAssetPlatform()); AzFramework::PlatformFlags hostPlatformFlag = AzFramework::PlatformHelper::GetPlatformFlag(AzToolsFramework::AssetSystem::GetHostAssetPlatform());
AzFramework::PlatformFlags expectedFlags = AzFramework::PlatformFlags::Platform_ANDROID | AzFramework::PlatformFlags::Platform_IOS | AzFramework::PlatformFlags::Platform_PROVO | hostPlatformFlag; AzFramework::PlatformFlags expectedFlags = AzFramework::PlatformFlags::Platform_ANDROID | AzFramework::PlatformFlags::Platform_IOS | AzFramework::PlatformFlags::Platform_PROVO | hostPlatformFlag;
ASSERT_EQ(platformFlags, expectedFlags); ASSERT_EQ(platformFlags, expectedFlags);

@ -52,11 +52,12 @@ TEST_F(PlatformConfigurationUnitTests, TestFailReadConfigFile_BadPlatform)
using namespace AssetProcessor; using namespace AssetProcessor;
const auto testExeFolder = AZ::IO::FileIOBase::GetInstance()->ResolvePath(TestAppRoot); const auto testExeFolder = AZ::IO::FileIOBase::GetInstance()->ResolvePath(TestAppRoot);
const AZ::IO::FixedMaxPath projectPath = (*testExeFolder) / EmptyDummyProjectName;
auto configRoot = AZ::IO::FileIOBase::GetInstance()->ResolvePath("@exefolder@/testdata/config_broken_badplatform"); auto configRoot = AZ::IO::FileIOBase::GetInstance()->ResolvePath("@exefolder@/testdata/config_broken_badplatform");
ASSERT_TRUE(configRoot); ASSERT_TRUE(configRoot);
UnitTestPlatformConfiguration config; UnitTestPlatformConfiguration config;
m_absorber.Clear(); m_absorber.Clear();
ASSERT_FALSE(config.InitializeFromConfigFiles(configRoot->c_str(), testExeFolder->c_str(), EmptyDummyProjectName, false, false)); ASSERT_FALSE(config.InitializeFromConfigFiles(configRoot->c_str(), testExeFolder->c_str(), projectPath.c_str(), false, false));
ASSERT_GT(m_absorber.m_numErrorsAbsorbed, 0); ASSERT_GT(m_absorber.m_numErrorsAbsorbed, 0);
} }
@ -67,11 +68,12 @@ TEST_F(PlatformConfigurationUnitTests, TestFailReadConfigFile_NoPlatform)
using namespace AssetProcessor; using namespace AssetProcessor;
const auto testExeFolder = AZ::IO::FileIOBase::GetInstance()->ResolvePath(TestAppRoot); const auto testExeFolder = AZ::IO::FileIOBase::GetInstance()->ResolvePath(TestAppRoot);
const AZ::IO::FixedMaxPath projectPath = (*testExeFolder) / EmptyDummyProjectName;
auto configRoot = AZ::IO::FileIOBase::GetInstance()->ResolvePath("@exefolder@/testdata/config_broken_noplatform"); auto configRoot = AZ::IO::FileIOBase::GetInstance()->ResolvePath("@exefolder@/testdata/config_broken_noplatform");
ASSERT_TRUE(configRoot); ASSERT_TRUE(configRoot);
UnitTestPlatformConfiguration config; UnitTestPlatformConfiguration config;
m_absorber.Clear(); m_absorber.Clear();
ASSERT_FALSE(config.InitializeFromConfigFiles(configRoot->c_str(), testExeFolder->c_str(), EmptyDummyProjectName, false, false)); ASSERT_FALSE(config.InitializeFromConfigFiles(configRoot->c_str(), testExeFolder->c_str(), projectPath.c_str(), false, false));
ASSERT_GT(m_absorber.m_numErrorsAbsorbed, 0); ASSERT_GT(m_absorber.m_numErrorsAbsorbed, 0);
} }
@ -81,11 +83,12 @@ TEST_F(PlatformConfigurationUnitTests, TestFailReadConfigFile_NoScanFolders)
using namespace AssetProcessor; using namespace AssetProcessor;
const auto testExeFolder = AZ::IO::FileIOBase::GetInstance()->ResolvePath(TestAppRoot); const auto testExeFolder = AZ::IO::FileIOBase::GetInstance()->ResolvePath(TestAppRoot);
const AZ::IO::FixedMaxPath projectPath = (*testExeFolder) / EmptyDummyProjectName;
auto configRoot = AZ::IO::FileIOBase::GetInstance()->ResolvePath("@exefolder@/testdata/config_broken_noscans"); auto configRoot = AZ::IO::FileIOBase::GetInstance()->ResolvePath("@exefolder@/testdata/config_broken_noscans");
ASSERT_TRUE(configRoot); ASSERT_TRUE(configRoot);
UnitTestPlatformConfiguration config; UnitTestPlatformConfiguration config;
m_absorber.Clear(); m_absorber.Clear();
ASSERT_FALSE(config.InitializeFromConfigFiles(configRoot->c_str(), testExeFolder->c_str(), EmptyDummyProjectName, false, false)); ASSERT_FALSE(config.InitializeFromConfigFiles(configRoot->c_str(), testExeFolder->c_str(), projectPath.c_str(), false, false));
ASSERT_GT(m_absorber.m_numErrorsAbsorbed, 0); ASSERT_GT(m_absorber.m_numErrorsAbsorbed, 0);
} }
@ -95,11 +98,12 @@ TEST_F(PlatformConfigurationUnitTests, TestFailReadConfigFile_BrokenRecognizers)
using namespace AssetProcessor; using namespace AssetProcessor;
const auto testExeFolder = AZ::IO::FileIOBase::GetInstance()->ResolvePath(TestAppRoot); const auto testExeFolder = AZ::IO::FileIOBase::GetInstance()->ResolvePath(TestAppRoot);
const AZ::IO::FixedMaxPath projectPath = (*testExeFolder) / EmptyDummyProjectName;
auto configRoot = AZ::IO::FileIOBase::GetInstance()->ResolvePath("@exefolder@/testdata/config_broken_recognizers"); auto configRoot = AZ::IO::FileIOBase::GetInstance()->ResolvePath("@exefolder@/testdata/config_broken_recognizers");
ASSERT_TRUE(configRoot); ASSERT_TRUE(configRoot);
UnitTestPlatformConfiguration config; UnitTestPlatformConfiguration config;
m_absorber.Clear(); m_absorber.Clear();
ASSERT_FALSE(config.InitializeFromConfigFiles(configRoot->c_str(), testExeFolder->c_str(), EmptyDummyProjectName, false, false)); ASSERT_FALSE(config.InitializeFromConfigFiles(configRoot->c_str(), testExeFolder->c_str(), projectPath.c_str(), false, false));
ASSERT_GT(m_absorber.m_numErrorsAbsorbed, 0); ASSERT_GT(m_absorber.m_numErrorsAbsorbed, 0);
} }
@ -109,11 +113,12 @@ TEST_F(PlatformConfigurationUnitTests, TestFailReadConfigFile_Regular_Platforms)
using namespace AssetProcessor; using namespace AssetProcessor;
const auto testExeFolder = AZ::IO::FileIOBase::GetInstance()->ResolvePath(TestAppRoot); const auto testExeFolder = AZ::IO::FileIOBase::GetInstance()->ResolvePath(TestAppRoot);
const AZ::IO::FixedMaxPath projectPath = (*testExeFolder) / EmptyDummyProjectName;
auto configRoot = AZ::IO::FileIOBase::GetInstance()->ResolvePath("@exefolder@/testdata/config_regular"); auto configRoot = AZ::IO::FileIOBase::GetInstance()->ResolvePath("@exefolder@/testdata/config_regular");
ASSERT_TRUE(configRoot); ASSERT_TRUE(configRoot);
UnitTestPlatformConfiguration config; UnitTestPlatformConfiguration config;
m_absorber.Clear(); m_absorber.Clear();
ASSERT_TRUE(config.InitializeFromConfigFiles(configRoot->c_str(), testExeFolder->c_str(), EmptyDummyProjectName, false, false)); ASSERT_TRUE(config.InitializeFromConfigFiles(configRoot->c_str(), testExeFolder->c_str(), projectPath.c_str(), false, false));
ASSERT_EQ(m_absorber.m_numErrorsAbsorbed, 0); ASSERT_EQ(m_absorber.m_numErrorsAbsorbed, 0);
// verify the data. // verify the data.
@ -322,12 +327,13 @@ TEST_F(PlatformConfigurationUnitTests, TestFailReadConfigFile_RegularScanfolder)
using namespace AssetProcessor; using namespace AssetProcessor;
const auto testExeFolder = AZ::IO::FileIOBase::GetInstance()->ResolvePath(TestAppRoot); const auto testExeFolder = AZ::IO::FileIOBase::GetInstance()->ResolvePath(TestAppRoot);
const AZ::IO::FixedMaxPath projectPath = (*testExeFolder) / EmptyDummyProjectName;
auto configRoot = AZ::IO::FileIOBase::GetInstance()->ResolvePath("@exefolder@/testdata/config_regular"); auto configRoot = AZ::IO::FileIOBase::GetInstance()->ResolvePath("@exefolder@/testdata/config_regular");
ASSERT_TRUE(configRoot); ASSERT_TRUE(configRoot);
UnitTestPlatformConfiguration config; UnitTestPlatformConfiguration config;
m_absorber.Clear(); m_absorber.Clear();
AssetUtilities::ComputeProjectName(EmptyDummyProjectName, true); AssetUtilities::ComputeProjectName(EmptyDummyProjectName, true);
ASSERT_TRUE(config.InitializeFromConfigFiles(configRoot->c_str(), testExeFolder->c_str(), EmptyDummyProjectName, false, false)); ASSERT_TRUE(config.InitializeFromConfigFiles(configRoot->c_str(), testExeFolder->c_str(), projectPath.c_str(), false, false));
ASSERT_EQ(m_absorber.m_numErrorsAbsorbed, 0); ASSERT_EQ(m_absorber.m_numErrorsAbsorbed, 0);
ASSERT_EQ(config.GetScanFolderCount(), 3); // the two, and then the one that has the same data as prior but different identifier. ASSERT_EQ(config.GetScanFolderCount(), 3); // the two, and then the one that has the same data as prior but different identifier.
@ -356,11 +362,12 @@ TEST_F(PlatformConfigurationUnitTests, TestFailReadConfigFile_RegularScanfolderP
using namespace AssetProcessor; using namespace AssetProcessor;
const auto testExeFolder = AZ::IO::FileIOBase::GetInstance()->ResolvePath(TestAppRoot); const auto testExeFolder = AZ::IO::FileIOBase::GetInstance()->ResolvePath(TestAppRoot);
const AZ::IO::FixedMaxPath projectPath = (*testExeFolder) / EmptyDummyProjectName;
auto configRoot = AZ::IO::FileIOBase::GetInstance()->ResolvePath("@exefolder@/testdata/config_regular_platform_scanfolder"); auto configRoot = AZ::IO::FileIOBase::GetInstance()->ResolvePath("@exefolder@/testdata/config_regular_platform_scanfolder");
ASSERT_TRUE(configRoot); ASSERT_TRUE(configRoot);
UnitTestPlatformConfiguration config; UnitTestPlatformConfiguration config;
m_absorber.Clear(); m_absorber.Clear();
ASSERT_TRUE(config.InitializeFromConfigFiles(configRoot->c_str(), testExeFolder->c_str(), EmptyDummyProjectName, false, false)); ASSERT_TRUE(config.InitializeFromConfigFiles(configRoot->c_str(), testExeFolder->c_str(), projectPath.c_str(), false, false));
ASSERT_EQ(m_absorber.m_numErrorsAbsorbed, 0); ASSERT_EQ(m_absorber.m_numErrorsAbsorbed, 0);
ASSERT_EQ(config.GetScanFolderCount(), 5); ASSERT_EQ(config.GetScanFolderCount(), 5);
@ -402,11 +409,12 @@ TEST_F(PlatformConfigurationUnitTests, TestFailReadConfigFile_RegularExcludes)
using namespace AssetProcessor; using namespace AssetProcessor;
const auto testExeFolder = AZ::IO::FileIOBase::GetInstance()->ResolvePath(TestAppRoot); const auto testExeFolder = AZ::IO::FileIOBase::GetInstance()->ResolvePath(TestAppRoot);
const AZ::IO::FixedMaxPath projectPath = (*testExeFolder) / EmptyDummyProjectName;
auto configRoot = AZ::IO::FileIOBase::GetInstance()->ResolvePath("@exefolder@/testdata/config_regular"); auto configRoot = AZ::IO::FileIOBase::GetInstance()->ResolvePath("@exefolder@/testdata/config_regular");
ASSERT_TRUE(configRoot); ASSERT_TRUE(configRoot);
UnitTestPlatformConfiguration config; UnitTestPlatformConfiguration config;
m_absorber.Clear(); m_absorber.Clear();
ASSERT_TRUE(config.InitializeFromConfigFiles(configRoot->c_str(), testExeFolder->c_str(), EmptyDummyProjectName, false, false)); ASSERT_TRUE(config.InitializeFromConfigFiles(configRoot->c_str(), testExeFolder->c_str(), projectPath.c_str(), false, false));
ASSERT_EQ(m_absorber.m_numErrorsAbsorbed, 0); ASSERT_EQ(m_absorber.m_numErrorsAbsorbed, 0);
ASSERT_TRUE(config.IsFileExcluded("blahblah/$tmp_01.test")); ASSERT_TRUE(config.IsFileExcluded("blahblah/$tmp_01.test"));
@ -427,11 +435,12 @@ TEST_F(PlatformConfigurationUnitTests, TestFailReadConfigFile_Recognizers)
#endif #endif
const auto testExeFolder = AZ::IO::FileIOBase::GetInstance()->ResolvePath(TestAppRoot); const auto testExeFolder = AZ::IO::FileIOBase::GetInstance()->ResolvePath(TestAppRoot);
const AZ::IO::FixedMaxPath projectPath = (*testExeFolder) / EmptyDummyProjectName;
auto configRoot = AZ::IO::FileIOBase::GetInstance()->ResolvePath("@exefolder@/testdata/config_regular"); auto configRoot = AZ::IO::FileIOBase::GetInstance()->ResolvePath("@exefolder@/testdata/config_regular");
ASSERT_TRUE(configRoot); ASSERT_TRUE(configRoot);
UnitTestPlatformConfiguration config; UnitTestPlatformConfiguration config;
m_absorber.Clear(); m_absorber.Clear();
ASSERT_TRUE(config.InitializeFromConfigFiles(configRoot->c_str(), testExeFolder->c_str(), EmptyDummyProjectName, false, false)); ASSERT_TRUE(config.InitializeFromConfigFiles(configRoot->c_str(), testExeFolder->c_str(), projectPath.c_str(), false, false));
ASSERT_EQ(m_absorber.m_numErrorsAbsorbed, 0); ASSERT_EQ(m_absorber.m_numErrorsAbsorbed, 0);
const AssetProcessor::RecognizerContainer& recogs = config.GetAssetRecognizerContainer(); const AssetProcessor::RecognizerContainer& recogs = config.GetAssetRecognizerContainer();
@ -518,12 +527,13 @@ TEST_F(PlatformConfigurationUnitTests, TestFailReadConfigFile_Overrides)
using namespace AzToolsFramework::AssetSystem; using namespace AzToolsFramework::AssetSystem;
using namespace AssetProcessor; using namespace AssetProcessor;
const auto testExeFolder = AZ::IO::FileIOBase::GetInstance()->ResolvePath(TestAppRoot); const auto testExeFolder = AZ::IO::FileIOBase::GetInstance()->ResolvePath(TestAppRoot);
const AZ::IO::FixedMaxPath projectPath = (*testExeFolder) / DummyProjectName;
auto configRoot = AZ::IO::FileIOBase::GetInstance()->ResolvePath("@exefolder@/testdata/config_regular"); auto configRoot = AZ::IO::FileIOBase::GetInstance()->ResolvePath("@exefolder@/testdata/config_regular");
ASSERT_TRUE(configRoot); ASSERT_TRUE(configRoot);
UnitTestPlatformConfiguration config; UnitTestPlatformConfiguration config;
m_absorber.Clear(); m_absorber.Clear();
ASSERT_TRUE(config.InitializeFromConfigFiles(configRoot->c_str(), testExeFolder->c_str(), DummyProjectName, false, false)); ASSERT_TRUE(config.InitializeFromConfigFiles(configRoot->c_str(), testExeFolder->c_str(), projectPath.c_str(), false, false));
ASSERT_EQ(m_absorber.m_numErrorsAbsorbed, 0); ASSERT_EQ(m_absorber.m_numErrorsAbsorbed, 0);
const AssetProcessor::RecognizerContainer& recogs = config.GetAssetRecognizerContainer(); const AssetProcessor::RecognizerContainer& recogs = config.GetAssetRecognizerContainer();
@ -625,11 +635,12 @@ TEST_F(PlatformConfigurationUnitTests, ReadCheckServer_FromConfig_Valid)
using namespace AssetProcessor; using namespace AssetProcessor;
const auto testExeFolder = AZ::IO::FileIOBase::GetInstance()->ResolvePath(TestAppRoot); const auto testExeFolder = AZ::IO::FileIOBase::GetInstance()->ResolvePath(TestAppRoot);
const AZ::IO::FixedMaxPath projectPath = (*testExeFolder) / EmptyDummyProjectName;
auto configRoot = AZ::IO::FileIOBase::GetInstance()->ResolvePath("@exefolder@/testdata/config_regular"); auto configRoot = AZ::IO::FileIOBase::GetInstance()->ResolvePath("@exefolder@/testdata/config_regular");
ASSERT_TRUE(configRoot); ASSERT_TRUE(configRoot);
UnitTestPlatformConfiguration config; UnitTestPlatformConfiguration config;
m_absorber.Clear(); m_absorber.Clear();
ASSERT_TRUE(config.InitializeFromConfigFiles(configRoot->c_str(), testExeFolder->c_str(), EmptyDummyProjectName, false, false)); ASSERT_TRUE(config.InitializeFromConfigFiles(configRoot->c_str(), testExeFolder->c_str(), projectPath.c_str(), false, false));
ASSERT_EQ(m_absorber.m_numErrorsAbsorbed, 0); ASSERT_EQ(m_absorber.m_numErrorsAbsorbed, 0);
const AssetProcessor::RecognizerContainer& recogs = config.GetAssetRecognizerContainer(); const AssetProcessor::RecognizerContainer& recogs = config.GetAssetRecognizerContainer();
@ -674,11 +685,12 @@ TEST_F(PlatformConfigurationUnitTests, Test_MetaFileTypes_AssetImporterExtension
using namespace AssetProcessor; using namespace AssetProcessor;
const auto testExeFolder = AZ::IO::FileIOBase::GetInstance()->ResolvePath(TestAppRoot); const auto testExeFolder = AZ::IO::FileIOBase::GetInstance()->ResolvePath(TestAppRoot);
const AZ::IO::FixedMaxPath projectPath = (*testExeFolder) / EmptyDummyProjectName;
auto configRoot = AZ::IO::FileIOBase::GetInstance()->ResolvePath("@exefolder@/testdata/config_metadata"); auto configRoot = AZ::IO::FileIOBase::GetInstance()->ResolvePath("@exefolder@/testdata/config_metadata");
ASSERT_TRUE(configRoot); ASSERT_TRUE(configRoot);
UnitTestPlatformConfiguration config; UnitTestPlatformConfiguration config;
m_absorber.Clear(); m_absorber.Clear();
ASSERT_FALSE(config.InitializeFromConfigFiles(configRoot->c_str(), testExeFolder->c_str(), EmptyDummyProjectName, false, false)); ASSERT_FALSE(config.InitializeFromConfigFiles(configRoot->c_str(), testExeFolder->c_str(), projectPath.c_str(), false, false));
ASSERT_GT(m_absorber.m_numErrorsAbsorbed, 0); ASSERT_GT(m_absorber.m_numErrorsAbsorbed, 0);
ASSERT_TRUE(config.MetaDataFileTypesCount() == 2); ASSERT_TRUE(config.MetaDataFileTypesCount() == 2);

@ -749,7 +749,7 @@ namespace AssetProcessor
} }
AZStd::vector<AZ::IO::Path> configFiles = AzToolsFramework::AssetUtils::GetConfigFiles(absoluteSystemRoot.toUtf8().constData(), AZStd::vector<AZ::IO::Path> configFiles = AzToolsFramework::AssetUtils::GetConfigFiles(absoluteSystemRoot.toUtf8().constData(),
absoluteAssetRoot.toUtf8().constData(), projectPath.toUtf8().constData(), projectPath.toUtf8().constData(),
addPlatformConfigs, addGemsConfigs && !noGemScanFolders, settingsRegistry); addPlatformConfigs, addGemsConfigs && !noGemScanFolders, settingsRegistry);
// First Merge all Engine, Gem and Project specific AssetProcessor*Config.setreg/.inifiles // First Merge all Engine, Gem and Project specific AssetProcessor*Config.setreg/.inifiles

@ -13,6 +13,7 @@
#include <ScreenHeaderWidget.h> #include <ScreenHeaderWidget.h>
#include <GemCatalog/GemModel.h> #include <GemCatalog/GemModel.h>
#include <GemCatalog/GemCatalogScreen.h> #include <GemCatalog/GemCatalogScreen.h>
#include <GemRepo/GemRepoScreen.h>
#include <ProjectUtils.h> #include <ProjectUtils.h>
#include <QDialogButtonBox> #include <QDialogButtonBox>
@ -47,9 +48,14 @@ namespace O3DE::ProjectManager
m_gemCatalogScreen = new GemCatalogScreen(this); m_gemCatalogScreen = new GemCatalogScreen(this);
m_stack->addWidget(m_gemCatalogScreen); m_stack->addWidget(m_gemCatalogScreen);
m_gemRepoScreen = new GemRepoScreen(this);
m_stack->addWidget(m_gemRepoScreen);
vLayout->addWidget(m_stack); vLayout->addWidget(m_stack);
connect(m_gemCatalogScreen, &ScreenWidget::ChangeScreenRequest, this, &CreateProjectCtrl::OnChangeScreenRequest); connect(m_gemCatalogScreen, &ScreenWidget::ChangeScreenRequest, this, &CreateProjectCtrl::OnChangeScreenRequest);
connect(m_gemRepoScreen, &GemRepoScreen::OnRefresh, m_gemCatalogScreen, &GemCatalogScreen::Refresh);
// When there are multiple project templates present, we re-gather the gems when changing the selected the project template. // When there are multiple project templates present, we re-gather the gems when changing the selected the project template.
connect(m_newProjectSettingsScreen, &NewProjectSettingsScreen::OnTemplateSelectionChanged, this, [=](int oldIndex, [[maybe_unused]] int newIndex) connect(m_newProjectSettingsScreen, &NewProjectSettingsScreen::OnTemplateSelectionChanged, this, [=](int oldIndex, [[maybe_unused]] int newIndex)
@ -89,6 +95,9 @@ namespace O3DE::ProjectManager
buttons->setObjectName("footer"); buttons->setObjectName("footer");
vLayout->addWidget(buttons); vLayout->addWidget(buttons);
m_primaryButton = buttons->addButton(tr("Create Project"), QDialogButtonBox::ApplyRole);
connect(m_primaryButton, &QPushButton::clicked, this, &CreateProjectCtrl::HandlePrimaryButton);
#ifdef TEMPLATE_GEM_CONFIGURATION_ENABLED #ifdef TEMPLATE_GEM_CONFIGURATION_ENABLED
connect(m_newProjectSettingsScreen, &ScreenWidget::ChangeScreenRequest, this, &CreateProjectCtrl::OnChangeScreenRequest); connect(m_newProjectSettingsScreen, &ScreenWidget::ChangeScreenRequest, this, &CreateProjectCtrl::OnChangeScreenRequest);
@ -100,8 +109,6 @@ namespace O3DE::ProjectManager
Update(); Update();
#endif // TEMPLATE_GEM_CONFIGURATION_ENABLED #endif // TEMPLATE_GEM_CONFIGURATION_ENABLED
m_primaryButton = buttons->addButton(tr("Create Project"), QDialogButtonBox::ApplyRole);
connect(m_primaryButton, &QPushButton::clicked, this, &CreateProjectCtrl::HandlePrimaryButton);
setLayout(vLayout); setLayout(vLayout);
} }
@ -122,6 +129,9 @@ namespace O3DE::ProjectManager
// Gather the enabled gems from the default project template when starting the create new project workflow. // Gather the enabled gems from the default project template when starting the create new project workflow.
ReinitGemCatalogForSelectedTemplate(); ReinitGemCatalogForSelectedTemplate();
// make sure the gem repo has the latest details
m_gemRepoScreen->Reinit();
} }
void CreateProjectCtrl::HandleBackButton() void CreateProjectCtrl::HandleBackButton()
@ -160,12 +170,21 @@ namespace O3DE::ProjectManager
{ {
m_header->setSubTitle(tr("Configure project with Gems")); m_header->setSubTitle(tr("Configure project with Gems"));
m_secondaryButton->setVisible(false); m_secondaryButton->setVisible(false);
m_primaryButton->setVisible(true);
}
else if (m_stack->currentWidget() == m_gemRepoScreen)
{
m_header->setSubTitle(tr("Gem Repositories"));
m_secondaryButton->setVisible(true);
m_secondaryButton->setText(tr("Back"));
m_primaryButton->setVisible(false);
} }
else else
{ {
m_header->setSubTitle(tr("Enter Project Details")); m_header->setSubTitle(tr("Enter Project Details"));
m_secondaryButton->setVisible(true); m_secondaryButton->setVisible(true);
m_secondaryButton->setText(tr("Configure Gems")); m_secondaryButton->setText(tr("Configure Gems"));
m_primaryButton->setVisible(true);
} }
} }
@ -175,6 +194,10 @@ namespace O3DE::ProjectManager
{ {
HandleSecondaryButton(); HandleSecondaryButton();
} }
else if (screen == ProjectManagerScreen::GemRepos)
{
NextScreen();
}
else else
{ {
emit ChangeScreenRequest(screen); emit ChangeScreenRequest(screen);
@ -230,6 +253,12 @@ namespace O3DE::ProjectManager
{ {
if (m_newProjectSettingsScreen->Validate()) if (m_newProjectSettingsScreen->Validate())
{ {
if (!m_gemCatalogScreen->GetDownloadController()->IsDownloadQueueEmpty())
{
QMessageBox::critical(this, tr("Gems downloading"), tr("You must wait for gems to finish downloading before continuing."));
return;
}
ProjectInfo projectInfo = m_newProjectSettingsScreen->GetProjectInfo(); ProjectInfo projectInfo = m_newProjectSettingsScreen->GetProjectInfo();
QString projectTemplatePath = m_newProjectSettingsScreen->GetProjectTemplatePath(); QString projectTemplatePath = m_newProjectSettingsScreen->GetProjectTemplatePath();

@ -23,6 +23,7 @@ namespace O3DE::ProjectManager
QT_FORWARD_DECLARE_CLASS(ScreenHeader) QT_FORWARD_DECLARE_CLASS(ScreenHeader)
QT_FORWARD_DECLARE_CLASS(NewProjectSettingsScreen) QT_FORWARD_DECLARE_CLASS(NewProjectSettingsScreen)
QT_FORWARD_DECLARE_CLASS(GemCatalogScreen) QT_FORWARD_DECLARE_CLASS(GemCatalogScreen)
QT_FORWARD_DECLARE_CLASS(GemRepoScreen)
class CreateProjectCtrl class CreateProjectCtrl
: public ScreenWidget : public ScreenWidget
@ -64,6 +65,7 @@ namespace O3DE::ProjectManager
NewProjectSettingsScreen* m_newProjectSettingsScreen = nullptr; NewProjectSettingsScreen* m_newProjectSettingsScreen = nullptr;
GemCatalogScreen* m_gemCatalogScreen = nullptr; GemCatalogScreen* m_gemCatalogScreen = nullptr;
GemRepoScreen* m_gemRepoScreen = nullptr;
}; };
} // namespace O3DE::ProjectManager } // namespace O3DE::ProjectManager

@ -82,12 +82,13 @@ namespace O3DE::ProjectManager
succeeded = false; succeeded = false;
} }
QString gemName = m_gemNames.front();
m_gemNames.erase(m_gemNames.begin()); m_gemNames.erase(m_gemNames.begin());
emit Done(succeeded); emit Done(gemName, succeeded);
if (!m_gemNames.empty()) if (!m_gemNames.empty())
{ {
emit StartGemDownload(m_gemNames[0]); emit StartGemDownload(m_gemNames.front());
} }
else else
{ {

@ -58,7 +58,7 @@ namespace O3DE::ProjectManager
signals: signals:
void StartGemDownload(const QString& gemName); void StartGemDownload(const QString& gemName);
void Done(bool success = true); void Done(const QString& gemName, bool success = true);
void GemDownloadProgress(int percentage); void GemDownloadProgress(int percentage);
private: private:

@ -39,6 +39,10 @@ namespace O3DE::ProjectManager
m_tabWidget->addTab(m_engineSettingsScreen, tr("General")); m_tabWidget->addTab(m_engineSettingsScreen, tr("General"));
m_tabWidget->addTab(m_gemRepoScreen, tr("Gem Repositories")); m_tabWidget->addTab(m_gemRepoScreen, tr("Gem Repositories"));
// when tab changes, notify the current screen so it can refresh
connect(m_tabWidget, &QTabWidget::currentChanged, this, &EngineScreenCtrl::TabChanged);
topBarHLayout->addWidget(m_tabWidget); topBarHLayout->addWidget(m_tabWidget);
vLayout->addWidget(topBarFrameWidget); vLayout->addWidget(topBarFrameWidget);
@ -46,6 +50,11 @@ namespace O3DE::ProjectManager
setLayout(vLayout); setLayout(vLayout);
} }
void EngineScreenCtrl::TabChanged([[maybe_unused]] int index)
{
NotifyCurrentScreen();
}
ProjectManagerScreen EngineScreenCtrl::GetScreenEnum() ProjectManagerScreen EngineScreenCtrl::GetScreenEnum()
{ {
return ProjectManagerScreen::UpdateProject; return ProjectManagerScreen::UpdateProject;
@ -71,6 +80,15 @@ namespace O3DE::ProjectManager
return false; return false;
} }
void EngineScreenCtrl::NotifyCurrentScreen()
{
ScreenWidget* screen = reinterpret_cast<ScreenWidget*>(m_tabWidget->currentWidget());
if (screen)
{
screen->NotifyCurrentScreen();
}
}
void EngineScreenCtrl::GoToScreen(ProjectManagerScreen screen) void EngineScreenCtrl::GoToScreen(ProjectManagerScreen screen)
{ {
if (screen == m_engineSettingsScreen->GetScreenEnum()) if (screen == m_engineSettingsScreen->GetScreenEnum())

@ -30,6 +30,10 @@ namespace O3DE::ProjectManager
bool IsTab() override; bool IsTab() override;
bool ContainsScreen(ProjectManagerScreen screen) override; bool ContainsScreen(ProjectManagerScreen screen) override;
void GoToScreen(ProjectManagerScreen screen) override; void GoToScreen(ProjectManagerScreen screen) override;
void NotifyCurrentScreen() override;
public slots:
void TabChanged(int index);
QTabWidget* m_tabWidget = nullptr; QTabWidget* m_tabWidget = nullptr;
EngineSettingsScreen* m_engineSettingsScreen = nullptr; EngineSettingsScreen* m_engineSettingsScreen = nullptr;

@ -145,7 +145,7 @@ namespace O3DE::ProjectManager
} }
else else
{ {
tagContainer->Update(ConvertFromModelIndices(tagIndices)); tagContainer->Update(GetTagsFromModelIndices(tagIndices));
label->setText(QString("%1 %2").arg(tagIndices.size()).arg(tagIndices.size() == 1 ? singularTitle : pluralTitle)); label->setText(QString("%1 %2").arg(tagIndices.size()).arg(tagIndices.size() == 1 ? singularTitle : pluralTitle));
widget->show(); widget->show();
} }
@ -234,17 +234,23 @@ namespace O3DE::ProjectManager
for (int downloadingGemNumber = 0; downloadingGemNumber < downloadQueue.size(); ++downloadingGemNumber) for (int downloadingGemNumber = 0; downloadingGemNumber < downloadQueue.size(); ++downloadingGemNumber)
{ {
QHBoxLayout* nameProgressLayout = new QHBoxLayout(); QHBoxLayout* nameProgressLayout = new QHBoxLayout();
TagWidget* newTag = new TagWidget(downloadQueue[downloadingGemNumber]);
const QString& gemName = downloadQueue[downloadingGemNumber];
TagWidget* newTag = new TagWidget({gemName, gemName});
nameProgressLayout->addWidget(newTag); nameProgressLayout->addWidget(newTag);
QLabel* progress = new QLabel(downloadingGemNumber == 0? QString("%1%").arg(downloadProgress) : tr("Queued")); QLabel* progress = new QLabel(downloadingGemNumber == 0? QString("%1%").arg(downloadProgress) : tr("Queued"));
nameProgressLayout->addWidget(progress); nameProgressLayout->addWidget(progress);
QSpacerItem* spacer = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum); QSpacerItem* spacer = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum);
nameProgressLayout->addSpacerItem(spacer); nameProgressLayout->addSpacerItem(spacer);
QLabel* cancelText = new QLabel(QString("<a href=\"%1\">Cancel</a>").arg(downloadQueue[downloadingGemNumber]));
QLabel* cancelText = new QLabel(QString("<a href=\"%1\">Cancel</a>").arg(gemName));
cancelText->setTextInteractionFlags(Qt::LinksAccessibleByMouse); cancelText->setTextInteractionFlags(Qt::LinksAccessibleByMouse);
connect(cancelText, &QLabel::linkActivated, this, &CartOverlayWidget::OnCancelDownloadActivated); connect(cancelText, &QLabel::linkActivated, this, &CartOverlayWidget::OnCancelDownloadActivated);
nameProgressLayout->addWidget(cancelText); nameProgressLayout->addWidget(cancelText);
downloadingItemLayout->addLayout(nameProgressLayout); downloadingItemLayout->addLayout(nameProgressLayout);
QProgressBar* downloadProgessBar = new QProgressBar(); QProgressBar* downloadProgessBar = new QProgressBar();
downloadingItemLayout->addWidget(downloadProgessBar); downloadingItemLayout->addWidget(downloadProgessBar);
downloadProgessBar->setValue(downloadingGemNumber == 0 ? downloadProgress : 0); downloadProgessBar->setValue(downloadingGemNumber == 0 ? downloadProgress : 0);
@ -255,7 +261,7 @@ namespace O3DE::ProjectManager
} }
}; };
auto downloadEnded = [=](bool /*success*/) auto downloadEnded = [=](const QString& /*gemName*/, bool /*success*/)
{ {
update(0); // update the list to remove the gem that has finished update(0); // update the list to remove the gem that has finished
}; };
@ -265,15 +271,15 @@ namespace O3DE::ProjectManager
update(0); update(0);
} }
QStringList CartOverlayWidget::ConvertFromModelIndices(const QVector<QModelIndex>& gems) const QVector<Tag> CartOverlayWidget::GetTagsFromModelIndices(const QVector<QModelIndex>& gems) const
{ {
QStringList gemNames; QVector<Tag> tags;
gemNames.reserve(gems.size()); tags.reserve(gems.size());
for (const QModelIndex& modelIndex : gems) for (const QModelIndex& modelIndex : gems)
{ {
gemNames.push_back(GemModel::GetDisplayName(modelIndex)); tags.push_back({ GemModel::GetDisplayName(modelIndex), GemModel::GetName(modelIndex) });
} }
return gemNames; return tags;
} }
CartButton::CartButton(GemModel* gemModel, DownloadController* downloadController, QWidget* parent) CartButton::CartButton(GemModel* gemModel, DownloadController* downloadController, QWidget* parent)

@ -36,7 +36,7 @@ namespace O3DE::ProjectManager
CartOverlayWidget(GemModel* gemModel, DownloadController* downloadController, QWidget* parent = nullptr); CartOverlayWidget(GemModel* gemModel, DownloadController* downloadController, QWidget* parent = nullptr);
private: private:
QStringList ConvertFromModelIndices(const QVector<QModelIndex>& gems) const; QVector<Tag> GetTagsFromModelIndices(const QVector<QModelIndex>& gems) const;
using GetTagIndicesCallback = AZStd::function<QVector<QModelIndex>()>; using GetTagIndicesCallback = AZStd::function<QVector<QModelIndex>()>;
void CreateGemSection(const QString& singularTitle, const QString& pluralTitle, GetTagIndicesCallback getTagIndices); void CreateGemSection(const QString& singularTitle, const QString& pluralTitle, GetTagIndicesCallback getTagIndices);

@ -26,6 +26,7 @@
#include <QStandardPaths> #include <QStandardPaths>
#include <QFileDialog> #include <QFileDialog>
#include <QMessageBox> #include <QMessageBox>
#include <QHash>
namespace O3DE::ProjectManager namespace O3DE::ProjectManager
{ {
@ -35,6 +36,9 @@ namespace O3DE::ProjectManager
m_gemModel = new GemModel(this); m_gemModel = new GemModel(this);
m_proxyModel = new GemSortFilterProxyModel(m_gemModel, this); m_proxyModel = new GemSortFilterProxyModel(m_gemModel, this);
// default to sort by gem name
m_proxyModel->setSortRole(GemModel::RoleName);
QVBoxLayout* vLayout = new QVBoxLayout(); QVBoxLayout* vLayout = new QVBoxLayout();
vLayout->setMargin(0); vLayout->setMargin(0);
vLayout->setSpacing(0); vLayout->setSpacing(0);
@ -48,6 +52,7 @@ namespace O3DE::ProjectManager
connect(m_gemModel, &GemModel::gemStatusChanged, this, &GemCatalogScreen::OnGemStatusChanged); connect(m_gemModel, &GemModel::gemStatusChanged, this, &GemCatalogScreen::OnGemStatusChanged);
connect(m_headerWidget, &GemCatalogHeaderWidget::OpenGemsRepo, this, &GemCatalogScreen::HandleOpenGemRepo); connect(m_headerWidget, &GemCatalogHeaderWidget::OpenGemsRepo, this, &GemCatalogScreen::HandleOpenGemRepo);
connect(m_headerWidget, &GemCatalogHeaderWidget::AddGem, this, &GemCatalogScreen::OnAddGemClicked); connect(m_headerWidget, &GemCatalogHeaderWidget::AddGem, this, &GemCatalogScreen::OnAddGemClicked);
connect(m_downloadController, &DownloadController::Done, this, &GemCatalogScreen::OnGemDownloadResult);
QHBoxLayout* hLayout = new QHBoxLayout(); QHBoxLayout* hLayout = new QHBoxLayout();
hLayout->setMargin(0); hLayout->setMargin(0);
@ -57,7 +62,7 @@ namespace O3DE::ProjectManager
m_gemInspector = new GemInspector(m_gemModel, this); m_gemInspector = new GemInspector(m_gemModel, this);
m_gemInspector->setFixedWidth(240); m_gemInspector->setFixedWidth(240);
connect(m_gemInspector, &GemInspector::TagClicked, this, &GemCatalogScreen::SelectGem); connect(m_gemInspector, &GemInspector::TagClicked, [=](const Tag& tag) { SelectGem(tag.id); });
connect(m_gemInspector, &GemInspector::UpdateGem, this, &GemCatalogScreen::UpdateGem); connect(m_gemInspector, &GemInspector::UpdateGem, this, &GemCatalogScreen::UpdateGem);
connect(m_gemInspector, &GemInspector::UninstallGem, this, &GemCatalogScreen::UninstallGem); connect(m_gemInspector, &GemInspector::UninstallGem, this, &GemCatalogScreen::UninstallGem);
@ -87,11 +92,20 @@ namespace O3DE::ProjectManager
void GemCatalogScreen::ReinitForProject(const QString& projectPath) void GemCatalogScreen::ReinitForProject(const QString& projectPath)
{ {
m_projectPath = projectPath;
m_gemModel->Clear(); m_gemModel->Clear();
m_gemsToRegisterWithProject.clear(); m_gemsToRegisterWithProject.clear();
if (m_filterWidget)
{
// disconnect so we don't update the status filter for every gem we add
disconnect(m_gemModel, &GemModel::dataChanged, m_filterWidget, &GemFilterWidget::ResetGemStatusFilter);
}
FillModel(projectPath); FillModel(projectPath);
m_proxyModel->ResetFilters(); m_proxyModel->ResetFilters();
m_proxyModel->sort(/*column=*/0);
if (m_filterWidget) if (m_filterWidget)
{ {
@ -109,8 +123,7 @@ namespace O3DE::ProjectManager
// Select the first entry after everything got correctly sized // Select the first entry after everything got correctly sized
QTimer::singleShot(200, [=]{ QTimer::singleShot(200, [=]{
QModelIndex firstModelIndex = m_gemModel->index(0, 0); // m_gemListView->model()->index(0,0); QModelIndex firstModelIndex = m_gemModel->index(0, 0);
//m_gemListView->selectionModel()->select(firstModelIndex, QItemSelectionModel::ClearAndSelect);
m_gemModel->GetSelectionModel()->select(firstModelIndex, QItemSelectionModel::ClearAndSelect); m_gemModel->GetSelectionModel()->select(firstModelIndex, QItemSelectionModel::ClearAndSelect);
}); });
} }
@ -150,11 +163,80 @@ namespace O3DE::ProjectManager
{ {
m_gemModel->AddGem(gemInfoResult.GetValue<GemInfo>()); m_gemModel->AddGem(gemInfoResult.GetValue<GemInfo>());
m_gemModel->UpdateGemDependencies(); m_gemModel->UpdateGemDependencies();
m_proxyModel->sort(/*column=*/0);
} }
} }
} }
} }
void GemCatalogScreen::Refresh()
{
QHash<QString, GemInfo> gemInfoHash;
// create a hash with the gem name as key
const AZ::Outcome<QVector<GemInfo>, AZStd::string>& allGemInfosResult = PythonBindingsInterface::Get()->GetAllGemInfos(m_projectPath);
if (allGemInfosResult.IsSuccess())
{
const QVector<GemInfo>& gemInfos = allGemInfosResult.GetValue();
for (const GemInfo& gemInfo : gemInfos)
{
gemInfoHash.insert(gemInfo.m_name, gemInfo);
}
}
// add all the gem repos into the hash
const AZ::Outcome<QVector<GemInfo>, AZStd::string>& allRepoGemInfosResult = PythonBindingsInterface::Get()->GetAllGemRepoGemsInfos();
if (allRepoGemInfosResult.IsSuccess())
{
const QVector<GemInfo>& allRepoGemInfos = allRepoGemInfosResult.GetValue();
for (const GemInfo& gemInfo : allRepoGemInfos)
{
if (!gemInfoHash.contains(gemInfo.m_name))
{
gemInfoHash.insert(gemInfo.m_name, gemInfo);
}
}
}
// remove gems from the model that no longer exist in the hash and are not project dependencies
int i = 0;
while (i < m_gemModel->rowCount())
{
QModelIndex index = m_gemModel->index(i,0);
QString gemName = m_gemModel->GetName(index);
const bool gemFound = gemInfoHash.contains(gemName);
if (!gemFound && !m_gemModel->IsAdded(index) && !m_gemModel->IsAddedDependency(index))
{
m_gemModel->removeRow(i);
}
else
{
if (!gemFound && (m_gemModel->IsAdded(index) || m_gemModel->IsAddedDependency(index)))
{
const QString error = tr("Gem %1 was removed or unregistered, but is still used by the project.").arg(gemName);
AZ_Warning("Project Manager", false, error.toUtf8().constData());
QMessageBox::warning(this, tr("Gem not found"), error.toUtf8().constData());
}
gemInfoHash.remove(gemName);
i++;
}
}
// add all gems remaining in the hash that were not removed
for(auto iter = gemInfoHash.begin(); iter != gemInfoHash.end(); ++iter)
{
m_gemModel->AddGem(iter.value());
}
m_gemModel->UpdateGemDependencies();
m_proxyModel->sort(/*column=*/0);
// temporary, until we can refresh filter counts
m_proxyModel->ResetFilters();
m_filterWidget->ResetAllFilters();
}
void GemCatalogScreen::OnGemStatusChanged(const QString& gemName, uint32_t numChangedDependencies) void GemCatalogScreen::OnGemStatusChanged(const QString& gemName, uint32_t numChangedDependencies)
{ {
if (m_notificationsEnabled) if (m_notificationsEnabled)
@ -262,20 +344,22 @@ namespace O3DE::ProjectManager
void GemCatalogScreen::FillModel(const QString& projectPath) void GemCatalogScreen::FillModel(const QString& projectPath)
{ {
AZ::Outcome<QVector<GemInfo>, AZStd::string> allGemInfosResult = PythonBindingsInterface::Get()->GetAllGemInfos(projectPath); m_projectPath = projectPath;
const AZ::Outcome<QVector<GemInfo>, AZStd::string>& allGemInfosResult = PythonBindingsInterface::Get()->GetAllGemInfos(projectPath);
if (allGemInfosResult.IsSuccess()) if (allGemInfosResult.IsSuccess())
{ {
// Add all available gems to the model. // Add all available gems to the model.
const QVector<GemInfo> allGemInfos = allGemInfosResult.GetValue(); const QVector<GemInfo>& allGemInfos = allGemInfosResult.GetValue();
for (const GemInfo& gemInfo : allGemInfos) for (const GemInfo& gemInfo : allGemInfos)
{ {
m_gemModel->AddGem(gemInfo); m_gemModel->AddGem(gemInfo);
} }
AZ::Outcome<QVector<GemInfo>, AZStd::string> allRepoGemInfosResult = PythonBindingsInterface::Get()->GetAllGemRepoGemsInfos(); const AZ::Outcome<QVector<GemInfo>, AZStd::string>& allRepoGemInfosResult = PythonBindingsInterface::Get()->GetAllGemRepoGemsInfos();
if (allRepoGemInfosResult.IsSuccess()) if (allRepoGemInfosResult.IsSuccess())
{ {
const QVector<GemInfo> allRepoGemInfos = allRepoGemInfosResult.GetValue(); const QVector<GemInfo>& allRepoGemInfos = allRepoGemInfosResult.GetValue();
for (const GemInfo& gemInfo : allRepoGemInfos) for (const GemInfo& gemInfo : allRepoGemInfos)
{ {
// do not add gems that have already been downloaded // do not add gems that have already been downloaded
@ -294,10 +378,10 @@ namespace O3DE::ProjectManager
m_notificationsEnabled = false; m_notificationsEnabled = false;
// Gather enabled gems for the given project. // Gather enabled gems for the given project.
auto enabledGemNamesResult = PythonBindingsInterface::Get()->GetEnabledGemNames(projectPath); const auto& enabledGemNamesResult = PythonBindingsInterface::Get()->GetEnabledGemNames(projectPath);
if (enabledGemNamesResult.IsSuccess()) if (enabledGemNamesResult.IsSuccess())
{ {
const QVector<AZStd::string> enabledGemNames = enabledGemNamesResult.GetValue(); const QVector<AZStd::string>& enabledGemNames = enabledGemNamesResult.GetValue();
for (const AZStd::string& enabledGemName : enabledGemNames) for (const AZStd::string& enabledGemName : enabledGemNames)
{ {
const QModelIndex modelIndex = m_gemModel->FindIndexByNameString(enabledGemName.c_str()); const QModelIndex modelIndex = m_gemModel->FindIndexByNameString(enabledGemName.c_str());
@ -357,12 +441,24 @@ namespace O3DE::ProjectManager
for (const QModelIndex& modelIndex : toBeAdded) for (const QModelIndex& modelIndex : toBeAdded)
{ {
const QString gemPath = GemModel::GetPath(modelIndex); const QString& gemPath = GemModel::GetPath(modelIndex);
// make sure any remote gems we added were downloaded successfully
if (GemModel::GetGemOrigin(modelIndex) == GemInfo::Remote && GemModel::GetDownloadStatus(modelIndex) != GemInfo::Downloaded)
{
QMessageBox::critical(
nullptr, "Cannot add gem that isn't downloaded",
tr("Cannot add gem %1 to project because it isn't downloaded yet or failed to download.")
.arg(GemModel::GetDisplayName(modelIndex)));
return EnableDisableGemsResult::Failed;
}
const AZ::Outcome<void, AZStd::string> result = pythonBindings->AddGemToProject(gemPath, projectPath); const AZ::Outcome<void, AZStd::string> result = pythonBindings->AddGemToProject(gemPath, projectPath);
if (!result.IsSuccess()) if (!result.IsSuccess())
{ {
QMessageBox::critical(nullptr, "Operation failed", QMessageBox::critical(nullptr, "Failed to add gem to project",
QString("Cannot add gem %1 to project.\n\nError:\n%2").arg(GemModel::GetDisplayName(modelIndex), result.GetError().c_str())); tr("Cannot add gem %1 to project.<br><br>Error:<br>%2").arg(GemModel::GetDisplayName(modelIndex), result.GetError().c_str()));
return EnableDisableGemsResult::Failed; return EnableDisableGemsResult::Failed;
} }
@ -380,8 +476,8 @@ namespace O3DE::ProjectManager
const AZ::Outcome<void, AZStd::string> result = pythonBindings->RemoveGemFromProject(gemPath, projectPath); const AZ::Outcome<void, AZStd::string> result = pythonBindings->RemoveGemFromProject(gemPath, projectPath);
if (!result.IsSuccess()) if (!result.IsSuccess())
{ {
QMessageBox::critical(nullptr, "Operation failed", QMessageBox::critical(nullptr, "Failed to remove gem from project",
QString("Cannot remove gem %1 from project.\n\nError:\n%2").arg(GemModel::GetDisplayName(modelIndex), result.GetError().c_str())); tr("Cannot remove gem %1 from project.<br><br>Error:<br>%2").arg(GemModel::GetDisplayName(modelIndex), result.GetError().c_str()));
return EnableDisableGemsResult::Failed; return EnableDisableGemsResult::Failed;
} }
@ -392,23 +488,35 @@ namespace O3DE::ProjectManager
void GemCatalogScreen::HandleOpenGemRepo() void GemCatalogScreen::HandleOpenGemRepo()
{ {
QVector<QModelIndex> gemsToBeAdded = m_gemModel->GatherGemsToBeAdded(true); emit ChangeScreenRequest(ProjectManagerScreen::GemRepos);
QVector<QModelIndex> gemsToBeRemoved = m_gemModel->GatherGemsToBeRemoved(true); }
if (!gemsToBeAdded.empty() || !gemsToBeRemoved.empty()) void GemCatalogScreen::OnGemDownloadResult(const QString& gemName, bool succeeded)
{ {
QMessageBox::StandardButton warningResult = QMessageBox::warning( if (succeeded)
nullptr, "Pending Changes", {
"There are some unsaved changes to the gem selection,<br> they will be lost if you change screens.<br> Are you sure?", // refresh the information for downloaded gems
QMessageBox::No | QMessageBox::Yes); const AZ::Outcome<QVector<GemInfo>, AZStd::string>& allGemInfosResult = PythonBindingsInterface::Get()->GetAllGemInfos(m_projectPath);
if (allGemInfosResult.IsSuccess())
if (warningResult != QMessageBox::Yes) {
// we should find the gem name now in all gem infos
for (const GemInfo& gemInfo : allGemInfosResult.GetValue())
{ {
if (gemInfo.m_name == gemName)
{
QModelIndex index = m_gemModel->FindIndexByNameString(gemName);
if (index.isValid())
{
m_gemModel->setData(index, GemInfo::Downloaded, GemModel::RoleDownloadStatus);
m_gemModel->setData(index, gemInfo.m_path, GemModel::RolePath);
m_gemModel->setData(index, gemInfo.m_path, GemModel::RoleDirectoryLink);
}
return; return;
} }
} }
}
emit ChangeScreenRequest(ProjectManagerScreen::GemRepos); }
} }
ProjectManagerScreen GemCatalogScreen::GetScreenEnum() ProjectManagerScreen GemCatalogScreen::GetScreenEnum()

@ -49,6 +49,8 @@ namespace O3DE::ProjectManager
void OnGemStatusChanged(const QString& gemName, uint32_t numChangedDependencies); void OnGemStatusChanged(const QString& gemName, uint32_t numChangedDependencies);
void OnAddGemClicked(); void OnAddGemClicked();
void SelectGem(const QString& gemName); void SelectGem(const QString& gemName);
void OnGemDownloadResult(const QString& gemName, bool succeeded = true);
void Refresh();
void UpdateGem(const QModelIndex& modelIndex); void UpdateGem(const QModelIndex& modelIndex);
void UninstallGem(const QModelIndex& modelIndex); void UninstallGem(const QModelIndex& modelIndex);
@ -64,7 +66,6 @@ namespace O3DE::ProjectManager
private: private:
void FillModel(const QString& projectPath); void FillModel(const QString& projectPath);
QModelIndex GetCurrentlySelectedGem();
AZStd::unique_ptr<AzToolsFramework::ToastNotificationsView> m_notificationsView; AZStd::unique_ptr<AzToolsFramework::ToastNotificationsView> m_notificationsView;
@ -78,5 +79,6 @@ namespace O3DE::ProjectManager
DownloadController* m_downloadController = nullptr; DownloadController* m_downloadController = nullptr;
bool m_notificationsEnabled = true; bool m_notificationsEnabled = true;
QSet<QString> m_gemsToRegisterWithProject; QSet<QString> m_gemsToRegisterWithProject;
QString m_projectPath;
}; };
} // namespace O3DE::ProjectManager } // namespace O3DE::ProjectManager

@ -109,10 +109,10 @@ namespace O3DE::ProjectManager
} }
// Depending gems // Depending gems
QStringList dependingGems = m_model->GetDependingGemNames(modelIndex); const QVector<Tag>& dependingGemTags = m_model->GetDependingGemTags(modelIndex);
if (!dependingGems.isEmpty()) if (!dependingGemTags.isEmpty())
{ {
m_dependingGems->Update(tr("Depending Gems"), tr("The following Gems will be automatically enabled with this Gem."), dependingGems); m_dependingGems->Update(tr("Depending Gems"), tr("The following Gems will be automatically enabled with this Gem."), dependingGemTags);
m_dependingGems->show(); m_dependingGems->show();
} }
else else
@ -123,7 +123,8 @@ namespace O3DE::ProjectManager
// Additional information // Additional information
m_versionLabel->setText(tr("Gem Version: %1").arg(m_model->GetVersion(modelIndex))); m_versionLabel->setText(tr("Gem Version: %1").arg(m_model->GetVersion(modelIndex)));
m_lastUpdatedLabel->setText(tr("Last Updated: %1").arg(m_model->GetLastUpdated(modelIndex))); m_lastUpdatedLabel->setText(tr("Last Updated: %1").arg(m_model->GetLastUpdated(modelIndex)));
m_binarySizeLabel->setText(tr("Binary Size: %1 KB").arg(m_model->GetBinarySizeInKB(modelIndex))); const int binarySize = m_model->GetBinarySizeInKB(modelIndex);
m_binarySizeLabel->setText(tr("Binary Size: %1").arg(binarySize ? tr("%1 KB").arg(binarySize) : tr("Unknown")));
// Update and Uninstall buttons // Update and Uninstall buttons
if (m_model->GetGemOrigin(modelIndex) == GemInfo::Remote && m_model->GetDownloadStatus(modelIndex) == GemInfo::Downloaded) if (m_model->GetGemOrigin(modelIndex) == GemInfo::Remote && m_model->GetDownloadStatus(modelIndex) == GemInfo::Downloaded)

@ -45,7 +45,7 @@ namespace O3DE::ProjectManager
inline constexpr static const char* s_textColor = "#DDDDDD"; inline constexpr static const char* s_textColor = "#DDDDDD";
signals: signals:
void TagClicked(const QString& tag); void TagClicked(const Tag& tag);
void UpdateGem(const QModelIndex& modelIndex); void UpdateGem(const QModelIndex& modelIndex);
void UninstallGem(const QModelIndex& modelIndex); void UninstallGem(const QModelIndex& modelIndex);

@ -18,6 +18,7 @@ namespace O3DE::ProjectManager
: QStandardItemModel(parent) : QStandardItemModel(parent)
{ {
m_selectionModel = new QItemSelectionModel(this, parent); m_selectionModel = new QItemSelectionModel(this, parent);
connect(this, &QAbstractItemModel::rowsAboutToBeRemoved, this, &GemModel::OnRowsAboutToBeRemoved);
} }
QItemSelectionModel* GemModel::GetSelectionModel() const QItemSelectionModel* GemModel::GetSelectionModel() const
@ -64,7 +65,6 @@ namespace O3DE::ProjectManager
appendRow(item); appendRow(item);
const QModelIndex modelIndex = index(rowCount()-1, 0); const QModelIndex modelIndex = index(rowCount()-1, 0);
m_nameToIndexMap[gemInfo.m_displayName] = modelIndex;
m_nameToIndexMap[gemInfo.m_name] = modelIndex; m_nameToIndexMap[gemInfo.m_name] = modelIndex;
} }
@ -177,18 +177,6 @@ namespace O3DE::ProjectManager
return {}; return {};
} }
void GemModel::FindGemDisplayNamesByNameStrings(QStringList& inOutGemNames)
{
for (QString& name : inOutGemNames)
{
QModelIndex modelIndex = FindIndexByNameString(name);
if (modelIndex.isValid())
{
name = GetDisplayName(modelIndex);
}
}
}
QStringList GemModel::GetDependingGems(const QModelIndex& modelIndex) QStringList GemModel::GetDependingGems(const QModelIndex& modelIndex)
{ {
return modelIndex.data(RoleDependingGems).toStringList(); return modelIndex.data(RoleDependingGems).toStringList();
@ -208,16 +196,23 @@ namespace O3DE::ProjectManager
} }
} }
QStringList GemModel::GetDependingGemNames(const QModelIndex& modelIndex) QVector<Tag> GemModel::GetDependingGemTags(const QModelIndex& modelIndex)
{
QVector<Tag> tags;
QStringList dependingGemNames = GetDependingGems(modelIndex);
tags.reserve(dependingGemNames.size());
for (QString& gemName : dependingGemNames)
{ {
QStringList result = GetDependingGems(modelIndex); const QModelIndex& dependingIndex = FindIndexByNameString(gemName);
if (result.isEmpty()) if (dependingIndex.isValid())
{ {
return {}; tags.push_back({ GetDisplayName(dependingIndex), GetName(dependingIndex) });
}
} }
FindGemDisplayNamesByNameStrings(result); return tags;
return result;
} }
QString GemModel::GetVersion(const QModelIndex& modelIndex) QString GemModel::GetVersion(const QModelIndex& modelIndex)
@ -372,6 +367,16 @@ namespace O3DE::ProjectManager
gemModel->emit gemStatusChanged(gemName, numChangedDependencies); gemModel->emit gemStatusChanged(gemName, numChangedDependencies);
} }
void GemModel::OnRowsAboutToBeRemoved(const QModelIndex& parent, int first, int last)
{
for (int i = first; i <= last; ++i)
{
QModelIndex modelIndex = index(i, 0, parent);
const QString& gemName = GetName(modelIndex);
m_nameToIndexMap.remove(gemName);
}
}
void GemModel::SetIsAddedDependency(QAbstractItemModel& model, const QModelIndex& modelIndex, bool isAdded) void GemModel::SetIsAddedDependency(QAbstractItemModel& model, const QModelIndex& modelIndex, bool isAdded)
{ {
model.setData(modelIndex, isAdded, RoleIsAddedDependency); model.setData(modelIndex, isAdded, RoleIsAddedDependency);

@ -10,6 +10,7 @@
#if !defined(Q_MOC_RUN) #if !defined(Q_MOC_RUN)
#include <GemCatalog/GemInfo.h> #include <GemCatalog/GemInfo.h>
#include <TagWidget.h>
#include <QAbstractItemModel> #include <QAbstractItemModel>
#include <QStandardItemModel> #include <QStandardItemModel>
#include <QItemSelectionModel> #include <QItemSelectionModel>
@ -26,12 +27,39 @@ namespace O3DE::ProjectManager
explicit GemModel(QObject* parent = nullptr); explicit GemModel(QObject* parent = nullptr);
QItemSelectionModel* GetSelectionModel() const; QItemSelectionModel* GetSelectionModel() const;
enum UserRole
{
RoleName = Qt::UserRole,
RoleDisplayName,
RoleCreator,
RoleGemOrigin,
RolePlatforms,
RoleSummary,
RoleWasPreviouslyAdded,
RoleWasPreviouslyAddedDependency,
RoleIsAdded,
RoleIsAddedDependency,
RoleDirectoryLink,
RoleDocLink,
RoleDependingGems,
RoleVersion,
RoleLastUpdated,
RoleBinarySize,
RoleFeatures,
RoleTypes,
RolePath,
RoleRequirement,
RoleDownloadStatus,
RoleLicenseText,
RoleLicenseLink
};
void AddGem(const GemInfo& gemInfo); void AddGem(const GemInfo& gemInfo);
void Clear(); void Clear();
void UpdateGemDependencies(); void UpdateGemDependencies();
QModelIndex FindIndexByNameString(const QString& nameString) const; QModelIndex FindIndexByNameString(const QString& nameString) const;
QStringList GetDependingGemNames(const QModelIndex& modelIndex); QVector<Tag> GetDependingGemTags(const QModelIndex& modelIndex);
bool HasDependentGems(const QModelIndex& modelIndex) const; bool HasDependentGems(const QModelIndex& modelIndex) const;
static QString GetName(const QModelIndex& modelIndex); static QString GetName(const QModelIndex& modelIndex);
@ -82,38 +110,13 @@ namespace O3DE::ProjectManager
signals: signals:
void gemStatusChanged(const QString& gemName, uint32_t numChangedDependencies); void gemStatusChanged(const QString& gemName, uint32_t numChangedDependencies);
protected slots:
void OnRowsAboutToBeRemoved(const QModelIndex& parent, int first, int last);
private: private:
void FindGemDisplayNamesByNameStrings(QStringList& inOutGemNames);
void GetAllDependingGems(const QModelIndex& modelIndex, QSet<QModelIndex>& inOutGems); void GetAllDependingGems(const QModelIndex& modelIndex, QSet<QModelIndex>& inOutGems);
QStringList GetDependingGems(const QModelIndex& modelIndex); QStringList GetDependingGems(const QModelIndex& modelIndex);
enum UserRole
{
RoleName = Qt::UserRole,
RoleDisplayName,
RoleCreator,
RoleGemOrigin,
RolePlatforms,
RoleSummary,
RoleWasPreviouslyAdded,
RoleWasPreviouslyAddedDependency,
RoleIsAdded,
RoleIsAddedDependency,
RoleDirectoryLink,
RoleDocLink,
RoleDependingGems,
RoleVersion,
RoleLastUpdated,
RoleBinarySize,
RoleFeatures,
RoleTypes,
RolePath,
RoleRequirement,
RoleDownloadStatus,
RoleLicenseText,
RoleLicenseLink
};
QHash<QString, QModelIndex> m_nameToIndexMap; QHash<QString, QModelIndex> m_nameToIndexMap;
QItemSelectionModel* m_selectionModel = nullptr; QItemSelectionModel* m_selectionModel = nullptr;
QHash<QString, QSet<QModelIndex>> m_gemDependencyMap; QHash<QString, QSet<QModelIndex>> m_gemDependencyMap;

@ -86,7 +86,7 @@ namespace O3DE::ProjectManager
} }
// Included Gems // Included Gems
m_includedGems->Update(tr("Included Gems"), "", m_model->GetIncludedGemNames(modelIndex)); m_includedGems->Update(tr("Included Gems"), "", m_model->GetIncludedGemTags(modelIndex));
m_mainWidget->adjustSize(); m_mainWidget->adjustSize();
m_mainWidget->show(); m_mainWidget->show();

@ -103,17 +103,17 @@ namespace O3DE::ProjectManager
return modelIndex.data(RoleIncludedGems).toStringList(); return modelIndex.data(RoleIncludedGems).toStringList();
} }
QStringList GemRepoModel::GetIncludedGemNames(const QModelIndex& modelIndex) QVector<Tag> GemRepoModel::GetIncludedGemTags(const QModelIndex& modelIndex)
{ {
QStringList gemNames; QVector<Tag> tags;
QVector<GemInfo> gemInfos = GetIncludedGemInfos(modelIndex); const QVector<GemInfo>& gemInfos = GetIncludedGemInfos(modelIndex);
tags.reserve(gemInfos.size());
for (const GemInfo& gemInfo : gemInfos) for (const GemInfo& gemInfo : gemInfos)
{ {
gemNames.append(gemInfo.m_displayName); tags.append({ gemInfo.m_displayName, gemInfo.m_name });
} }
return gemNames; return tags;
} }
QVector<GemInfo> GemRepoModel::GetIncludedGemInfos(const QModelIndex& modelIndex) QVector<GemInfo> GemRepoModel::GetIncludedGemInfos(const QModelIndex& modelIndex)

@ -40,7 +40,7 @@ namespace O3DE::ProjectManager
static QString GetPath(const QModelIndex& modelIndex); static QString GetPath(const QModelIndex& modelIndex);
static QStringList GetIncludedGemPaths(const QModelIndex& modelIndex); static QStringList GetIncludedGemPaths(const QModelIndex& modelIndex);
static QStringList GetIncludedGemNames(const QModelIndex& modelIndex); static QVector<Tag> GetIncludedGemTags(const QModelIndex& modelIndex);
static QVector<GemInfo> GetIncludedGemInfos(const QModelIndex& modelIndex); static QVector<GemInfo> GetIncludedGemInfos(const QModelIndex& modelIndex);
static bool IsEnabled(const QModelIndex& modelIndex); static bool IsEnabled(const QModelIndex& modelIndex);

@ -52,6 +52,11 @@ namespace O3DE::ProjectManager
Reinit(); Reinit();
} }
void GemRepoScreen::NotifyCurrentScreen()
{
Reinit();
}
void GemRepoScreen::Reinit() void GemRepoScreen::Reinit()
{ {
m_gemRepoModel->clear(); m_gemRepoModel->clear();
@ -91,6 +96,7 @@ namespace O3DE::ProjectManager
if (addGemRepoResult) if (addGemRepoResult)
{ {
Reinit(); Reinit();
emit OnRefresh();
} }
else else
{ {
@ -116,6 +122,7 @@ namespace O3DE::ProjectManager
if (removeGemRepoResult) if (removeGemRepoResult)
{ {
Reinit(); Reinit();
emit OnRefresh();
} }
else else
{ {
@ -130,6 +137,7 @@ namespace O3DE::ProjectManager
{ {
bool refreshResult = PythonBindingsInterface::Get()->RefreshAllGemRepos(); bool refreshResult = PythonBindingsInterface::Get()->RefreshAllGemRepos();
Reinit(); Reinit();
emit OnRefresh();
if (!refreshResult) if (!refreshResult)
{ {
@ -146,6 +154,7 @@ namespace O3DE::ProjectManager
if (refreshResult.IsSuccess()) if (refreshResult.IsSuccess())
{ {
Reinit(); Reinit();
emit OnRefresh();
} }
else else
{ {

@ -28,6 +28,7 @@ namespace O3DE::ProjectManager
class GemRepoScreen class GemRepoScreen
: public ScreenWidget : public ScreenWidget
{ {
Q_OBJECT
public: public:
explicit GemRepoScreen(QWidget* parent = nullptr); explicit GemRepoScreen(QWidget* parent = nullptr);
~GemRepoScreen() = default; ~GemRepoScreen() = default;
@ -37,12 +38,18 @@ namespace O3DE::ProjectManager
GemRepoModel* GetGemRepoModel() const { return m_gemRepoModel; } GemRepoModel* GetGemRepoModel() const { return m_gemRepoModel; }
void NotifyCurrentScreen() override;
signals:
void OnRefresh();
public slots: public slots:
void HandleAddRepoButton(); void HandleAddRepoButton();
void HandleRemoveRepoButton(const QModelIndex& modelIndex); void HandleRemoveRepoButton(const QModelIndex& modelIndex);
void HandleRefreshAllButton(); void HandleRefreshAllButton();
void HandleRefreshRepoButton(const QModelIndex& modelIndex); void HandleRefreshRepoButton(const QModelIndex& modelIndex);
private: private:
void FillModel(); void FillModel();
QFrame* CreateNoReposContent(); QFrame* CreateNoReposContent();

@ -33,14 +33,14 @@ namespace O3DE::ProjectManager
m_layout->addWidget(m_textLabel); m_layout->addWidget(m_textLabel);
m_tagWidget = new TagContainerWidget(); m_tagWidget = new TagContainerWidget();
connect(m_tagWidget, &TagContainerWidget::TagClicked, this, [=](const QString& tag){ emit TagClicked(tag); }); connect(m_tagWidget, &TagContainerWidget::TagClicked, this, [=](const Tag& tag){ emit TagClicked(tag); });
m_layout->addWidget(m_tagWidget); m_layout->addWidget(m_tagWidget);
} }
void GemsSubWidget::Update(const QString& title, const QString& text, const QStringList& gemNames) void GemsSubWidget::Update(const QString& title, const QString& text, const QVector<Tag>& tags)
{ {
m_titleLabel->setText(title); m_titleLabel->setText(title);
m_textLabel->setText(text); m_textLabel->setText(text);
m_tagWidget->Update(gemNames); m_tagWidget->Update(tags);
} }
} // namespace O3DE::ProjectManager } // namespace O3DE::ProjectManager

@ -26,10 +26,10 @@ namespace O3DE::ProjectManager
public: public:
GemsSubWidget(QWidget* parent = nullptr); GemsSubWidget(QWidget* parent = nullptr);
void Update(const QString& title, const QString& text, const QStringList& gemNames); void Update(const QString& title, const QString& text, const QVector<Tag>& tags);
signals: signals:
void TagClicked(const QString& tag); void TagClicked(const Tag& tag);
private: private:
QLabel* m_titleLabel = nullptr; QLabel* m_titleLabel = nullptr;

@ -53,6 +53,8 @@ namespace Platform
#define Py_To_String(obj) pybind11::str(obj).cast<std::string>().c_str() #define Py_To_String(obj) pybind11::str(obj).cast<std::string>().c_str()
#define Py_To_String_Optional(dict, key, default_string) dict.contains(key) ? Py_To_String(dict[key]) : default_string #define Py_To_String_Optional(dict, key, default_string) dict.contains(key) ? Py_To_String(dict[key]) : default_string
#define Py_To_Int(obj) obj.cast<int>()
#define Py_To_Int_Optional(dict, key, default_int) dict.contains(key) ? Py_To_Int(dict[key]) : default_int
#define QString_To_Py_String(value) pybind11::str(value.toStdString()) #define QString_To_Py_String(value) pybind11::str(value.toStdString())
#define QString_To_Py_Path(value) m_pathlib.attr("Path")(value.toStdString()) #define QString_To_Py_Path(value) m_pathlib.attr("Path")(value.toStdString())
@ -705,7 +707,9 @@ namespace O3DE::ProjectManager
// optional // optional
gemInfo.m_displayName = Py_To_String_Optional(data, "display_name", gemInfo.m_name); gemInfo.m_displayName = Py_To_String_Optional(data, "display_name", gemInfo.m_name);
gemInfo.m_summary = Py_To_String_Optional(data, "summary", ""); gemInfo.m_summary = Py_To_String_Optional(data, "summary", "");
gemInfo.m_version = ""; gemInfo.m_version = Py_To_String_Optional(data, "version", gemInfo.m_version);
gemInfo.m_lastUpdatedDate = Py_To_String_Optional(data, "last_updated", gemInfo.m_lastUpdatedDate);
gemInfo.m_binarySizeInKB = Py_To_Int_Optional(data, "binary_size", gemInfo.m_binarySizeInKB);
gemInfo.m_requirement = Py_To_String_Optional(data, "requirements", ""); gemInfo.m_requirement = Py_To_String_Optional(data, "requirements", "");
gemInfo.m_creator = Py_To_String_Optional(data, "origin", ""); gemInfo.m_creator = Py_To_String_Optional(data, "origin", "");
gemInfo.m_documentationLink = Py_To_String_Optional(data, "documentation_url", ""); gemInfo.m_documentationLink = Py_To_String_Optional(data, "documentation_url", "");
@ -1175,6 +1179,7 @@ namespace O3DE::ProjectManager
QString_To_Py_String(gemName), // gem name QString_To_Py_String(gemName), // gem name
pybind11::none(), // destination path pybind11::none(), // destination path
false, // skip auto register false, // skip auto register
false, // force
pybind11::cpp_function( pybind11::cpp_function(
[this, gemProgressCallback](int progress) [this, gemProgressCallback](int progress)
{ {

@ -12,15 +12,16 @@
namespace O3DE::ProjectManager namespace O3DE::ProjectManager
{ {
TagWidget::TagWidget(const QString& text, QWidget* parent) TagWidget::TagWidget(const Tag& tag, QWidget* parent)
: QLabel(text, parent) : QLabel(tag.text, parent)
, m_tag(tag)
{ {
setObjectName("TagWidget"); setObjectName("TagWidget");
} }
void TagWidget::mousePressEvent([[maybe_unused]] QMouseEvent* event) void TagWidget::mousePressEvent([[maybe_unused]] QMouseEvent* event)
{ {
emit(TagClicked(text())); emit TagClicked(m_tag);
} }
TagContainerWidget::TagContainerWidget(QWidget* parent) TagContainerWidget::TagContainerWidget(QWidget* parent)
@ -39,20 +40,34 @@ namespace O3DE::ProjectManager
void TagContainerWidget::Update(const QStringList& tags) void TagContainerWidget::Update(const QStringList& tags)
{ {
FlowLayout* flowLayout = static_cast<FlowLayout*>(layout()); Clear();
// remove old tags foreach (const QString& tag, tags)
QLayoutItem* layoutItem = nullptr;
while ((layoutItem = layout()->takeAt(0)) != nullptr)
{ {
layoutItem->widget()->deleteLater(); TagWidget* tagWidget = new TagWidget({tag, tag});
connect(tagWidget, &TagWidget::TagClicked, this, [=](const Tag& clickedTag){ emit TagClicked(clickedTag); });
layout()->addWidget(tagWidget);
}
} }
foreach (const QString& tag, tags) void TagContainerWidget::Update(const QVector<Tag>& tags)
{
Clear();
foreach (const Tag& tag, tags)
{ {
TagWidget* tagWidget = new TagWidget(tag); TagWidget* tagWidget = new TagWidget(tag);
connect(tagWidget, &TagWidget::TagClicked, this, [=](const QString& tag){ emit TagClicked(tag); }); connect(tagWidget, &TagWidget::TagClicked, this, [=](const Tag& clickedTag){ emit TagClicked(clickedTag); });
flowLayout->addWidget(tagWidget); layout()->addWidget(tagWidget);
}
}
void TagContainerWidget::Clear()
{
QLayoutItem* layoutItem = nullptr;
while ((layoutItem = layout()->takeAt(0)) != nullptr)
{
layoutItem->widget()->deleteLater();
} }
} }
} // namespace O3DE::ProjectManager } // namespace O3DE::ProjectManager

@ -10,12 +10,19 @@
#if !defined(Q_MOC_RUN) #if !defined(Q_MOC_RUN)
#include <QLabel> #include <QLabel>
#include <QStringList>
#include <QWidget> #include <QWidget>
#include <QVector>
#include <QStringList>
#endif #endif
namespace O3DE::ProjectManager namespace O3DE::ProjectManager
{ {
struct Tag
{
QString text;
QString id;
};
// Single tag // Single tag
class TagWidget class TagWidget
: public QLabel : public QLabel
@ -23,14 +30,17 @@ namespace O3DE::ProjectManager
Q_OBJECT // AUTOMOC Q_OBJECT // AUTOMOC
public: public:
explicit TagWidget(const QString& text, QWidget* parent = nullptr); explicit TagWidget(const Tag& id, QWidget* parent = nullptr);
~TagWidget() = default; ~TagWidget() = default;
signals: signals:
void TagClicked(const QString& tag); void TagClicked(const Tag& tag);
protected: protected:
void mousePressEvent(QMouseEvent* event) override; void mousePressEvent(QMouseEvent* event) override;
private:
Tag m_tag;
}; };
// Widget containing multiple tags, automatically wrapping based on the size // Widget containing multiple tags, automatically wrapping based on the size
@ -43,9 +53,13 @@ namespace O3DE::ProjectManager
explicit TagContainerWidget(QWidget* parent = nullptr); explicit TagContainerWidget(QWidget* parent = nullptr);
~TagContainerWidget() = default; ~TagContainerWidget() = default;
void Update(const QVector<Tag>& tags);
void Update(const QStringList& tags); void Update(const QStringList& tags);
signals: signals:
void TagClicked(const QString& tag); void TagClicked(const Tag& tag);
private:
void Clear();
}; };
} // namespace O3DE::ProjectManager } // namespace O3DE::ProjectManager

@ -7,6 +7,7 @@
*/ */
#include <GemCatalog/GemCatalogScreen.h> #include <GemCatalog/GemCatalogScreen.h>
#include <GemRepo/GemRepoScreen.h>
#include <ProjectManagerDefs.h> #include <ProjectManagerDefs.h>
#include <PythonBindingsInterface.h> #include <PythonBindingsInterface.h>
#include <ScreenHeaderWidget.h> #include <ScreenHeaderWidget.h>
@ -39,10 +40,10 @@ namespace O3DE::ProjectManager
m_updateSettingsScreen = new UpdateProjectSettingsScreen(); m_updateSettingsScreen = new UpdateProjectSettingsScreen();
m_gemCatalogScreen = new GemCatalogScreen(); m_gemCatalogScreen = new GemCatalogScreen();
m_gemRepoScreen = new GemRepoScreen(this);
connect(m_gemCatalogScreen, &ScreenWidget::ChangeScreenRequest, this, [this](ProjectManagerScreen screen){ connect(m_gemCatalogScreen, &ScreenWidget::ChangeScreenRequest, this, &UpdateProjectCtrl::OnChangeScreenRequest);
emit ChangeScreenRequest(screen); connect(m_gemRepoScreen, &GemRepoScreen::OnRefresh, m_gemCatalogScreen, &GemCatalogScreen::Refresh);
});
m_stack = new QStackedWidget(this); m_stack = new QStackedWidget(this);
m_stack->setObjectName("body"); m_stack->setObjectName("body");
@ -69,6 +70,7 @@ namespace O3DE::ProjectManager
m_stack->addWidget(topBarFrameWidget); m_stack->addWidget(topBarFrameWidget);
m_stack->addWidget(m_gemCatalogScreen); m_stack->addWidget(m_gemCatalogScreen);
m_stack->addWidget(m_gemRepoScreen);
QDialogButtonBox* backNextButtons = new QDialogButtonBox(); QDialogButtonBox* backNextButtons = new QDialogButtonBox();
backNextButtons->setObjectName("footer"); backNextButtons->setObjectName("footer");
@ -100,6 +102,22 @@ namespace O3DE::ProjectManager
// Gather the available gems that will be shown in the gem catalog. // Gather the available gems that will be shown in the gem catalog.
m_gemCatalogScreen->ReinitForProject(m_projectInfo.m_path); m_gemCatalogScreen->ReinitForProject(m_projectInfo.m_path);
// make sure the gem repo has the latest repo details
m_gemRepoScreen->Reinit();
}
void UpdateProjectCtrl::OnChangeScreenRequest(ProjectManagerScreen screen)
{
if (screen == ProjectManagerScreen::GemRepos)
{
m_stack->setCurrentWidget(m_gemRepoScreen);
Update();
}
else
{
emit ChangeScreenRequest(screen);
}
} }
void UpdateProjectCtrl::HandleGemsButton() void UpdateProjectCtrl::HandleGemsButton()
@ -145,6 +163,7 @@ namespace O3DE::ProjectManager
QMessageBox::critical(this, tr("Gems downloading"), tr("You must wait for gems to finish downloading before continuing.")); QMessageBox::critical(this, tr("Gems downloading"), tr("You must wait for gems to finish downloading before continuing."));
return; return;
} }
// Enable or disable the gems that got adjusted in the gem catalog and apply them to the given project. // Enable or disable the gems that got adjusted in the gem catalog and apply them to the given project.
const GemCatalogScreen::EnableDisableGemsResult result = m_gemCatalogScreen->EnableDisableGemsForProject(m_projectInfo.m_path); const GemCatalogScreen::EnableDisableGemsResult result = m_gemCatalogScreen->EnableDisableGemsForProject(m_projectInfo.m_path);
if (result == GemCatalogScreen::EnableDisableGemsResult::Failed) if (result == GemCatalogScreen::EnableDisableGemsResult::Failed)
@ -181,18 +200,26 @@ namespace O3DE::ProjectManager
void UpdateProjectCtrl::Update() void UpdateProjectCtrl::Update()
{ {
if (m_stack->currentIndex() == ScreenOrder::Gems) if (m_stack->currentIndex() == ScreenOrder::GemRepos)
{
m_header->setTitle(QString(tr("Edit Project Settings: \"%1\"")).arg(m_projectInfo.GetProjectDisplayName()));
m_header->setSubTitle(QString(tr("Gem Repositories")));
m_nextButton->setVisible(false);
}
else if (m_stack->currentIndex() == ScreenOrder::Gems)
{ {
m_header->setTitle(QString(tr("Edit Project Settings: \"%1\"")).arg(m_projectInfo.GetProjectDisplayName())); m_header->setTitle(QString(tr("Edit Project Settings: \"%1\"")).arg(m_projectInfo.GetProjectDisplayName()));
m_header->setSubTitle(QString(tr("Configure Gems"))); m_header->setSubTitle(QString(tr("Configure Gems")));
m_nextButton->setText(tr("Save")); m_nextButton->setText(tr("Save"));
m_nextButton->setVisible(true);
} }
else else
{ {
m_header->setTitle(""); m_header->setTitle("");
m_header->setSubTitle(QString(tr("Edit Project Settings: \"%1\"")).arg(m_projectInfo.GetProjectDisplayName())); m_header->setSubTitle(QString(tr("Edit Project Settings: \"%1\"")).arg(m_projectInfo.GetProjectDisplayName()));
m_nextButton->setText(tr("Save")); m_nextButton->setText(tr("Save"));
m_nextButton->setVisible(true);
} }
} }

@ -22,9 +22,11 @@ namespace O3DE::ProjectManager
QT_FORWARD_DECLARE_CLASS(ScreenHeader) QT_FORWARD_DECLARE_CLASS(ScreenHeader)
QT_FORWARD_DECLARE_CLASS(UpdateProjectSettingsScreen) QT_FORWARD_DECLARE_CLASS(UpdateProjectSettingsScreen)
QT_FORWARD_DECLARE_CLASS(GemCatalogScreen) QT_FORWARD_DECLARE_CLASS(GemCatalogScreen)
QT_FORWARD_DECLARE_CLASS(GemRepoScreen)
class UpdateProjectCtrl : public ScreenWidget class UpdateProjectCtrl : public ScreenWidget
{ {
Q_OBJECT
public: public:
explicit UpdateProjectCtrl(QWidget* parent = nullptr); explicit UpdateProjectCtrl(QWidget* parent = nullptr);
~UpdateProjectCtrl() = default; ~UpdateProjectCtrl() = default;
@ -37,6 +39,7 @@ namespace O3DE::ProjectManager
void HandleBackButton(); void HandleBackButton();
void HandleNextButton(); void HandleNextButton();
void HandleGemsButton(); void HandleGemsButton();
void OnChangeScreenRequest(ProjectManagerScreen screen);
void UpdateCurrentProject(const QString& projectPath); void UpdateCurrentProject(const QString& projectPath);
private: private:
@ -47,13 +50,15 @@ namespace O3DE::ProjectManager
enum ScreenOrder enum ScreenOrder
{ {
Settings, Settings,
Gems Gems,
GemRepos
}; };
ScreenHeader* m_header = nullptr; ScreenHeader* m_header = nullptr;
QStackedWidget* m_stack = nullptr; QStackedWidget* m_stack = nullptr;
UpdateProjectSettingsScreen* m_updateSettingsScreen = nullptr; UpdateProjectSettingsScreen* m_updateSettingsScreen = nullptr;
GemCatalogScreen* m_gemCatalogScreen = nullptr; GemCatalogScreen* m_gemCatalogScreen = nullptr;
GemRepoScreen* m_gemRepoScreen = nullptr;
QPushButton* m_backButton = nullptr; QPushButton* m_backButton = nullptr;
QPushButton* m_nextButton = nullptr; QPushButton* m_nextButton = nullptr;

@ -39,7 +39,6 @@ namespace PythonBindingsExample
AzToolsFramework::EditorPythonConsoleNotificationBus::Handler::BusConnect(); AzToolsFramework::EditorPythonConsoleNotificationBus::Handler::BusConnect();
// prepare the Python binding gem(s) // prepare the Python binding gem(s)
CalculateExecutablePath();
Start(Descriptor()); Start(Descriptor());
AZ::SerializeContext* context; AZ::SerializeContext* context;

@ -368,7 +368,6 @@ namespace AZ::SceneAPI::Containers
MOCK_METHOD0(GetSerializeContext, AZ::SerializeContext* ()); MOCK_METHOD0(GetSerializeContext, AZ::SerializeContext* ());
MOCK_METHOD0(GetJsonRegistrationContext, AZ::JsonRegistrationContext* ()); MOCK_METHOD0(GetJsonRegistrationContext, AZ::JsonRegistrationContext* ());
MOCK_METHOD0(GetBehaviorContext, AZ::BehaviorContext* ()); MOCK_METHOD0(GetBehaviorContext, AZ::BehaviorContext* ());
MOCK_CONST_METHOD0(GetAppRoot, const char*());
MOCK_CONST_METHOD0(GetEngineRoot, const char*()); MOCK_CONST_METHOD0(GetEngineRoot, const char*());
MOCK_CONST_METHOD0(GetExecutableFolder, const char* ()); MOCK_CONST_METHOD0(GetExecutableFolder, const char* ());
MOCK_CONST_METHOD1(QueryApplicationType, void(AZ::ApplicationTypeQuery&)); MOCK_CONST_METHOD1(QueryApplicationType, void(AZ::ApplicationTypeQuery&));

@ -202,8 +202,6 @@ namespace AZ
bool skipSystem = commandLine->HasSwitch("skipsystem"); bool skipSystem = commandLine->HasSwitch("skipsystem");
bool isDryRun = commandLine->HasSwitch("dryrun"); bool isDryRun = commandLine->HasSwitch("dryrun");
const char* appRoot = const_cast<const Application&>(application).GetAppRoot();
PathDocumentContainer documents; PathDocumentContainer documents;
bool result = true; bool result = true;
const AZStd::string& filePath = application.GetConfigFilePath(); const AZStd::string& filePath = application.GetConfigFilePath();
@ -230,7 +228,7 @@ namespace AZ
} }
auto callback = auto callback =
[&result, skipGems, skipSystem, &configurationName, sourceGameFolder, &appRoot, &documents, &convertSettings, &verifySettings] [&result, skipGems, skipSystem, &configurationName, sourceGameFolder, &documents, &convertSettings, &verifySettings]
(void* classPtr, const Uuid& classId, SerializeContext* context) (void* classPtr, const Uuid& classId, SerializeContext* context)
{ {
if (classId == azrtti_typeid<AZ::ComponentApplication::Descriptor>()) if (classId == azrtti_typeid<AZ::ComponentApplication::Descriptor>())
@ -238,7 +236,7 @@ namespace AZ
if (!skipSystem) if (!skipSystem)
{ {
result = ConvertSystemSettings(documents, *reinterpret_cast<AZ::ComponentApplication::Descriptor*>(classPtr), result = ConvertSystemSettings(documents, *reinterpret_cast<AZ::ComponentApplication::Descriptor*>(classPtr),
configurationName, sourceGameFolder, appRoot) && result; configurationName, sourceGameFolder) && result;
} }
// Cleanup the Serialized Element to allow any classes within the element's hierarchy to delete // Cleanup the Serialized Element to allow any classes within the element's hierarchy to delete
@ -443,7 +441,7 @@ namespace AZ
} }
bool Converter::ConvertSystemSettings(PathDocumentContainer& documents, const ComponentApplication::Descriptor& descriptor, bool Converter::ConvertSystemSettings(PathDocumentContainer& documents, const ComponentApplication::Descriptor& descriptor,
const AZStd::string& configurationName, const AZ::IO::PathView& projectFolder, [[maybe_unused]] const AZStd::string& applicationRoot) const AZStd::string& configurationName, const AZ::IO::PathView& projectFolder)
{ {
AZ::IO::FixedMaxPath memoryFilePath{ projectFolder }; AZ::IO::FixedMaxPath memoryFilePath{ projectFolder };
memoryFilePath /= "Registry"; memoryFilePath /= "Registry";

@ -43,7 +43,7 @@ namespace AZ
using PathDocumentContainer = AZStd::vector<PathDocumentPair>; using PathDocumentContainer = AZStd::vector<PathDocumentPair>;
static bool ConvertSystemSettings(PathDocumentContainer& documents, const ComponentApplication::Descriptor& descriptor, static bool ConvertSystemSettings(PathDocumentContainer& documents, const ComponentApplication::Descriptor& descriptor,
const AZStd::string& configurationName, const AZ::IO::PathView& projectFolder, const AZStd::string& applicationRoot); const AZStd::string& configurationName, const AZ::IO::PathView& projectFolder);
static bool ConvertSystemComponents(PathDocumentContainer& documents, const Entity& entity, static bool ConvertSystemComponents(PathDocumentContainer& documents, const Entity& entity,
const AZStd::string& configurationName, const AZ::IO::PathView& projectFolder, const AZStd::string& configurationName, const AZ::IO::PathView& projectFolder,
const JsonSerializerSettings& convertSettings, const JsonDeserializerSettings& verifySettings); const JsonSerializerSettings& convertSettings, const JsonDeserializerSettings& verifySettings);

@ -608,7 +608,6 @@ namespace AWSClientAuthUnitTest
AZ::Entity* FindEntity(const AZ::EntityId&) override { return nullptr; } AZ::Entity* FindEntity(const AZ::EntityId&) override { return nullptr; }
AZ::BehaviorContext* GetBehaviorContext() override { return nullptr; } AZ::BehaviorContext* GetBehaviorContext() override { return nullptr; }
const char* GetExecutableFolder() const override { return nullptr; } const char* GetExecutableFolder() const override { return nullptr; }
const char* GetAppRoot() const override { return nullptr; }
const char* GetEngineRoot() const override { return nullptr; } const char* GetEngineRoot() const override { return nullptr; }
void EnumerateEntities(const EntityCallback& /*callback*/) override {} void EnumerateEntities(const EntityCallback& /*callback*/) override {}
void QueryApplicationType(AZ::ApplicationTypeQuery& /*appType*/) const override {} void QueryApplicationType(AZ::ApplicationTypeQuery& /*appType*/) const override {}

@ -439,13 +439,6 @@ namespace AssetValidation
bool GetDefaultSeedListFiles(AZStd::vector<AZStd::string>& defaultSeedListFiles) bool GetDefaultSeedListFiles(AZStd::vector<AZStd::string>& defaultSeedListFiles)
{ {
const char* engineRoot = nullptr;
AzFramework::ApplicationRequests::Bus::BroadcastResult(engineRoot, &AzFramework::ApplicationRequests::GetEngineRoot);
const char* appRoot = nullptr;
AzFramework::ApplicationRequests::Bus::BroadcastResult(appRoot, &AzFramework::ApplicationRequests::GetAppRoot);
auto settingsRegistry = AZ::SettingsRegistry::Get(); auto settingsRegistry = AZ::SettingsRegistry::Get();
AZ::SettingsRegistryInterface::FixedValueString gameFolder; AZ::SettingsRegistryInterface::FixedValueString gameFolder;
auto projectKey = AZ::SettingsRegistryInterface::FixedValueString::format("%s/project_path", AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey); auto projectKey = AZ::SettingsRegistryInterface::FixedValueString::format("%s/project_path", AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey);
@ -509,30 +502,28 @@ namespace AssetValidation
AZ::Outcome<AzFramework::AssetSeedList, AZStd::string> AssetValidationSystemComponent::LoadSeedList(const char* seedPath, AZStd::string& seedListPath) AZ::Outcome<AzFramework::AssetSeedList, AZStd::string> AssetValidationSystemComponent::LoadSeedList(const char* seedPath, AZStd::string& seedListPath)
{ {
AZStd::string absoluteSeedPath = seedPath; AZ::IO::Path absoluteSeedPath = seedPath;
if (AZ::StringFunc::Path::IsRelative(seedPath)) if (AZ::StringFunc::Path::IsRelative(seedPath))
{ {
const char* appRoot = nullptr; AZ::IO::FixedMaxPath engineRoot = AZ::Utils::GetEnginePath();
AzFramework::ApplicationRequests::Bus::BroadcastResult(appRoot, &AzFramework::ApplicationRequests::GetEngineRoot);
if (!appRoot) if (engineRoot.empty())
{ {
return AZ::Failure(AZStd::string("Couldn't get engine root")); return AZ::Failure(AZStd::string("Couldn't get engine root"));
} }
absoluteSeedPath = AZStd::string::format("%s/%s", appRoot, seedPath); absoluteSeedPath = (engineRoot / seedPath).String();
} }
AzFramework::StringFunc::Path::Normalize(absoluteSeedPath);
AzFramework::AssetSeedList seedList; AzFramework::AssetSeedList seedList;
if (!AZ::Utils::LoadObjectFromFileInPlace(absoluteSeedPath, seedList)) if (!AZ::Utils::LoadObjectFromFileInPlace(absoluteSeedPath.Native(), seedList))
{ {
return AZ::Failure(AZStd::string::format("Failed to load seed list %s", absoluteSeedPath.c_str())); return AZ::Failure(AZStd::string::format("Failed to load seed list %s", absoluteSeedPath.c_str()));
} }
seedListPath = absoluteSeedPath; seedListPath = AZStd::move(absoluteSeedPath.Native());
return AZ::Success(seedList); return AZ::Success(seedList);
} }

@ -150,13 +150,13 @@ struct AssetValidationTest
auto projectPathKey = AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) auto projectPathKey = AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey)
+ "/project_path"; + "/project_path";
m_registry.Set(projectPathKey, (AZ::IO::FixedMaxPath(GetEngineRoot()) / "AutomatedTesting").Native()); m_registry.Set(projectPathKey, (AZ::IO::FixedMaxPath(m_tempDir.GetDirectory()) / "AutomatedTesting").Native());
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(m_registry); AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(m_registry);
// Set the engine root to the temporary directory and re-update the runtime file paths // Set the engine root to the temporary directory and re-update the runtime file paths
auto enginePathKey = AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) auto enginePathKey = AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey)
+ "/engine_path"; + "/engine_path";
m_registry.Set(enginePathKey, GetEngineRoot()); m_registry.Set(enginePathKey, m_tempDir.GetDirectory());
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(m_registry); AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(m_registry);
} }
} }
@ -176,11 +176,6 @@ struct AssetValidationTest
AZ_Assert(false, "Not implemented"); AZ_Assert(false, "Not implemented");
} }
const char* GetEngineRoot() const override
{
return m_tempDir.GetDirectory();
}
void SetUp() override void SetUp() override
{ {
using namespace ::testing; using namespace ::testing;

@ -111,7 +111,6 @@ namespace UnitTest
AZ::SerializeContext* GetSerializeContext() override { return m_context.get(); } AZ::SerializeContext* GetSerializeContext() override { return m_context.get(); }
AZ::BehaviorContext* GetBehaviorContext() override { return nullptr; } AZ::BehaviorContext* GetBehaviorContext() override { return nullptr; }
AZ::JsonRegistrationContext* GetJsonRegistrationContext() override { return m_jsonRegistrationContext.get(); } AZ::JsonRegistrationContext* GetJsonRegistrationContext() override { return m_jsonRegistrationContext.get(); }
const char* GetAppRoot() const override { return nullptr; }
const char* GetEngineRoot() const override { return nullptr; } const char* GetEngineRoot() const override { return nullptr; }
const char* GetExecutableFolder() const override { return nullptr; } const char* GetExecutableFolder() const override { return nullptr; }
void EnumerateEntities(const AZ::ComponentApplicationRequests::EntityCallback& /*callback*/) override {} void EnumerateEntities(const AZ::ComponentApplicationRequests::EntityCallback& /*callback*/) override {}

@ -50,8 +50,13 @@ float3 GetBackLighting(Surface surface, LightingData lightingData, float3 lightI
// Thin object mode, using thin-film assumption proposed by Jimenez J. et al, 2010, "Real-Time Realistic Skin Translucency" // Thin object mode, using thin-film assumption proposed by Jimenez J. et al, 2010, "Real-Time Realistic Skin Translucency"
// http://www.iryoku.com/translucency/downloads/Real-Time-Realistic-Skin-Translucency.pdf // http://www.iryoku.com/translucency/downloads/Real-Time-Realistic-Skin-Translucency.pdf
result = shadowRatio ? float3(0.0, 0.0, 0.0) : TransmissionKernel(surface.transmission.thickness * transmissionParams.w, rcp(transmissionParams.xyz)) * float litRatio = 1.0 - shadowRatio;
saturate(dot(-surface.normal, dirToLight)) * lightIntensity * shadowRatio; if (litRatio)
{
result = TransmissionKernel(surface.transmission.thickness * transmissionParams.w, rcp(transmissionParams.xyz)) *
saturate(dot(-surface.normal, dirToLight)) * lightIntensity * litRatio;
}
break; break;
} }

@ -13,6 +13,7 @@
#include <AzCore/Component/TransformBus.h> #include <AzCore/Component/TransformBus.h>
#include <AzFramework/Components/CameraBus.h> #include <AzFramework/Components/CameraBus.h>
#include <AzCore/std/containers/compressed_pair.h> #include <AzCore/std/containers/compressed_pair.h>
#include <AzCore/Utils/Utils.h>
#include <AzFramework/API/ApplicationAPI.h> #include <AzFramework/API/ApplicationAPI.h>
#include <luxcore/luxcore.h> #include <luxcore/luxcore.h>
@ -295,14 +296,12 @@ namespace AZ
} }
// Run luxcoreui.exe // Run luxcoreui.exe
AZStd::string luxCoreExeFullPath; AZ::IO::FixedMaxPath luxCoreExeFullPath = AZ::Utils::GetEnginePath();
AzFramework::ApplicationRequests::Bus::BroadcastResult(luxCoreExeFullPath, &AzFramework::ApplicationRequests::GetAppRoot); luxCoreExeFullPath /= AZ_TRAIT_LUXCORE_EXEPATH;
luxCoreExeFullPath = luxCoreExeFullPath + AZ_TRAIT_LUXCORE_EXEPATH;
AzFramework::StringFunc::Path::Normalize(luxCoreExeFullPath);
AZStd::string commandLine = "-o " + AZStd::string(resolvedPath) + "/render.cfg"; AZStd::string commandLine = "-o " + AZStd::string(resolvedPath) + "/render.cfg";
LuxCoreUI::LaunchLuxCoreUI(luxCoreExeFullPath, commandLine); LuxCoreUI::LaunchLuxCoreUI(luxCoreExeFullPath.String(), commandLine);
} }
} }
} }

@ -46,7 +46,6 @@ namespace UnitTest
bool DeleteEntity(const AZ::EntityId&) override { return false; } bool DeleteEntity(const AZ::EntityId&) override { return false; }
AZ::Entity* FindEntity(const AZ::EntityId&) override { return nullptr; } AZ::Entity* FindEntity(const AZ::EntityId&) override { return nullptr; }
AZ::BehaviorContext* GetBehaviorContext() override { return nullptr; } AZ::BehaviorContext* GetBehaviorContext() override { return nullptr; }
const char* GetAppRoot() const override { return nullptr; }
const char* GetEngineRoot() const override { return nullptr; } const char* GetEngineRoot() const override { return nullptr; }
const char* GetExecutableFolder() const override { return nullptr; } const char* GetExecutableFolder() const override { return nullptr; }
void EnumerateEntities(const EntityCallback& /*callback*/) override {} void EnumerateEntities(const EntityCallback& /*callback*/) override {}

@ -44,7 +44,6 @@ namespace UnitTest
AZ::Entity* FindEntity(const AZ::EntityId&) override { return nullptr; } AZ::Entity* FindEntity(const AZ::EntityId&) override { return nullptr; }
AZ::BehaviorContext* GetBehaviorContext() override { return nullptr; } AZ::BehaviorContext* GetBehaviorContext() override { return nullptr; }
AZ::JsonRegistrationContext* GetJsonRegistrationContext() override { return nullptr; } AZ::JsonRegistrationContext* GetJsonRegistrationContext() override { return nullptr; }
const char* GetAppRoot() const override { return nullptr; }
const char* GetEngineRoot() const override { return nullptr; } const char* GetEngineRoot() const override { return nullptr; }
const char* GetExecutableFolder() const override { return nullptr; } const char* GetExecutableFolder() const override { return nullptr; }
void EnumerateEntities(const EntityCallback& /*callback*/) override {} void EnumerateEntities(const EntityCallback& /*callback*/) override {}

@ -0,0 +1,29 @@
{
"description": "",
"parentMaterial": "",
"materialType": "Materials/Types/EnhancedPBR.materialtype",
"materialTypeVersion": 4,
"properties": {
"baseColor": {
"color": [
0.027664607390761375,
0.1926604062318802,
0.013916227966547012,
1.0
]
},
"general": {
"doubleSided": true
},
"subsurfaceScattering": {
"thickness": 0.20000000298023224,
"transmissionMode": "ThinObject",
"transmissionTint": [
0.009140154346823692,
0.19806210696697235,
0.01095597818493843,
1.0
]
}
}
}

@ -43,7 +43,7 @@ namespace AtomToolsFramework
m_propertyEditor->Setup(context, instanceNotificationHandler, false); m_propertyEditor->Setup(context, instanceNotificationHandler, false);
m_propertyEditor->AddInstance(instance, instanceClassId, nullptr, instanceToCompare); m_propertyEditor->AddInstance(instance, instanceClassId, nullptr, instanceToCompare);
m_propertyEditor->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); m_propertyEditor->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
m_propertyEditor->InvalidateAll(); m_propertyEditor->QueueInvalidation(AzToolsFramework::PropertyModificationRefreshLevel::Refresh_EntireTree);
m_layout->addWidget(m_propertyEditor); m_layout->addWidget(m_propertyEditor);
setLayout(m_layout); setLayout(m_layout);

@ -133,29 +133,12 @@ namespace AtomToolsFramework
bool LaunchTool(const QString& baseName, const QString& extension, const QStringList& arguments) bool LaunchTool(const QString& baseName, const QString& extension, const QStringList& arguments)
{ {
const char* engineRoot = nullptr; AZ::IO::FixedMaxPath engineRoot = AZ::Utils::GetEnginePath();
AzFramework::ApplicationRequests::Bus::BroadcastResult(engineRoot, &AzFramework::ApplicationRequests::GetEngineRoot); AZ_Assert(!engineRoot.empty(), "Cannot query Engine Path");
AZ_Assert(engineRoot != nullptr, "AzFramework::ApplicationRequests::GetEngineRoot failed");
char binFolderName[AZ_MAX_PATH_LEN] = {}; AZ::IO::FixedMaxPath launchPath = AZ::IO::FixedMaxPath(AZ::Utils::GetExecutableDirectory())
AZ::Utils::GetExecutablePathReturnType ret = AZ::Utils::GetExecutablePath(binFolderName, AZ_MAX_PATH_LEN); / (baseName + extension).toUtf8().constData();
// If it contains the filename, zero out the last path separator character... return QProcess::startDetached(launchPath.c_str(), arguments, engineRoot.c_str());
if (ret.m_pathIncludesFilename)
{
char* lastSlash = strrchr(binFolderName, AZ_CORRECT_FILESYSTEM_SEPARATOR);
if (lastSlash)
{
*lastSlash = '\0';
}
}
const QString path = QString("%1%2%3%4")
.arg(binFolderName)
.arg(AZ_CORRECT_FILESYSTEM_SEPARATOR_STRING)
.arg(baseName)
.arg(extension);
return QProcess::startDetached(path, arguments, engineRoot);
} }
} }

@ -19,11 +19,11 @@
#include <Atom/Utils/DdsFile.h> #include <Atom/Utils/DdsFile.h>
#include <AzFramework/API/ApplicationAPI.h>
#include <AzCore/IO/GenericStreams.h> #include <AzCore/IO/GenericStreams.h>
#include <AzCore/IO/SystemFile.h> #include <AzCore/IO/SystemFile.h>
#include <AzCore/IO/FileIO.h> #include <AzCore/IO/FileIO.h>
#include <AzCore/Utils/Utils.h>
#ifndef SCRIPTABLE_IMGUI #ifndef SCRIPTABLE_IMGUI
#define Scriptable_ImGui ImGui #define Scriptable_ImGui ImGui
@ -334,11 +334,10 @@ namespace AZ::Render
if (m_engineRoot.empty()) if (m_engineRoot.empty())
{ {
const char* engineRoot = nullptr; AZ::IO::FixedMaxPathString engineRoot = AZ::Utils::GetEnginePath();
AzFramework::ApplicationRequests::Bus::BroadcastResult(engineRoot, &AzFramework::ApplicationRequests::GetEngineRoot); if (!engineRoot.empty())
if (engineRoot)
{ {
m_engineRoot = AZStd::string(engineRoot); m_engineRoot = AZStd::string_view(engineRoot);
} }
} }

@ -1,4 +1,6 @@
@echo off @echo off
:: Keep changes local
SETLOCAL enableDelayedExpansion
REM REM
REM Copyright (c) Contributors to the Open 3D Engine Project REM Copyright (c) Contributors to the Open 3D Engine Project
@ -13,7 +15,7 @@ REM
:: Puts you in the CMD within the dev environment :: Puts you in the CMD within the dev environment
:: Set up window :: Set up window
TITLE O3DE Asset Gem Cmd TITLE O3DE DCC Scripting Interface Cmd
:: Use obvious color to prevent confusion (Grey with Yellow Text) :: Use obvious color to prevent confusion (Grey with Yellow Text)
COLOR 8E COLOR 8E
@ -21,15 +23,12 @@ COLOR 8E
cd %~dp0 cd %~dp0
PUSHD %~dp0 PUSHD %~dp0
:: Keep changes local
SETLOCAL enableDelayedExpansion
CALL %~dp0\Project_Env.bat CALL %~dp0\Project_Env.bat
echo. echo.
echo _____________________________________________________________________ echo _____________________________________________________________________
echo. echo.
echo ~ O3DE Asset Gem CMD ... echo ~ O3DE %O3DE_PROJECT% Asset Gem CMD ...
echo _____________________________________________________________________ echo _____________________________________________________________________
echo. echo.

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save