merging latest to resolve auto-merge conflict

Signed-off-by: antonmic <56370189+antonmic@users.noreply.github.com>
monroegm-disable-blank-issue-2
antonmic 4 years ago
commit dcbcab68d2

@ -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)
{ {
@ -205,7 +127,7 @@ void CFileUtil::EditTextFile(const char* txtFile, int line, IFileUtil::ETextFile
{ {
QString file = txtFile; QString file = txtFile;
QString fullPathName = Path::GamePathToFullPath(file); QString fullPathName = Path::GamePathToFullPath(file);
ExtractFile(fullPathName); ExtractFile(fullPathName);
QString cmd(fullPathName); QString cmd(fullPathName);
#if defined (AZ_PLATFORM_WINDOWS) #if defined (AZ_PLATFORM_WINDOWS)
@ -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
{ {
// Normalize the token to prepare for CRC32 calculation AZ::u32 CalculateBranchTokenHash(AZStd::string_view engineRootPath)
AZStd::string normalized = appRootPath; {
// Normalize the token to prepare for CRC32 calculation
// Strip out any trailing path separators auto NormalizeEnginePath = [](const char element) -> char
AZ::StringFunc::Strip(normalized, AZ_CORRECT_FILESYSTEM_SEPARATOR_STRING AZ_WRONG_FILESYSTEM_SEPARATOR_STRING,false, false, true); {
// Substitute path separators with '_' and lower case
// Lower case always return element == AZ::IO::WindowsPathSeparator || element == AZ::IO::PosixPathSeparator
AZStd::to_lower(normalized.begin(), normalized.end()); ? '_' : static_cast<char>(std::tolower(element));
};
// 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 {}

@ -1060,26 +1060,21 @@ namespace UnitTest
/** /**
* UserSettingsComponent test * UserSettingsComponent test
*/ */
class UserSettingsTestApp class UserSettingsTestApp
: public ComponentApplication : public ComponentApplication
, 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

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

@ -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();
} }

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

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

@ -59,7 +59,9 @@ namespace O3DE::ProjectManager
signals: signals:
void StartGemDownload(const QString& gemName); void StartGemDownload(const QString& gemName);
void Done(const QString& gemName, bool success = true); void Done(const QString& gemName, bool success = true);
void GemDownloadProgress(int percentage); void GemDownloadAdded(const QString& gemName);
void GemDownloadRemoved(const QString& gemName);
void GemDownloadProgress(const QString& gemName, int percentage);
private: private:
DownloadWorker* m_worker; DownloadWorker* m_worker;

@ -30,6 +30,7 @@ namespace O3DE::ProjectManager
m_layout->setMargin(5); m_layout->setMargin(5);
m_layout->setAlignment(Qt::AlignTop); m_layout->setAlignment(Qt::AlignTop);
setLayout(m_layout); setLayout(m_layout);
setMinimumHeight(400);
QHBoxLayout* hLayout = new QHBoxLayout(); QHBoxLayout* hLayout = new QHBoxLayout();
@ -119,6 +120,12 @@ namespace O3DE::ProjectManager
setWindowFlags(Qt::FramelessWindowHint | Qt::Dialog); setWindowFlags(Qt::FramelessWindowHint | Qt::Dialog);
} }
CartOverlayWidget::~CartOverlayWidget()
{
// disconnect from all download controller signals
disconnect(m_downloadController, nullptr, this, nullptr);
}
void CartOverlayWidget::CreateGemSection(const QString& singularTitle, const QString& pluralTitle, GetTagIndicesCallback getTagIndices) void CartOverlayWidget::CreateGemSection(const QString& singularTitle, const QString& pluralTitle, GetTagIndicesCallback getTagIndices)
{ {
QWidget* widget = new QWidget(); QWidget* widget = new QWidget();
@ -162,13 +169,13 @@ namespace O3DE::ProjectManager
void CartOverlayWidget::CreateDownloadSection() void CartOverlayWidget::CreateDownloadSection()
{ {
QWidget* widget = new QWidget(); m_downloadSectionWidget = new QWidget();
widget->setFixedWidth(s_width); m_downloadSectionWidget->setFixedWidth(s_width);
m_layout->addWidget(widget); m_layout->addWidget(m_downloadSectionWidget);
QVBoxLayout* layout = new QVBoxLayout(); QVBoxLayout* layout = new QVBoxLayout();
layout->setAlignment(Qt::AlignTop); layout->setAlignment(Qt::AlignTop);
widget->setLayout(layout); m_downloadSectionWidget->setLayout(layout);
QLabel* titleLabel = new QLabel(); QLabel* titleLabel = new QLabel();
titleLabel->setObjectName("GemCatalogCartOverlaySectionLabel"); titleLabel->setObjectName("GemCatalogCartOverlaySectionLabel");
@ -187,88 +194,121 @@ namespace O3DE::ProjectManager
QLabel* processingQueueLabel = new QLabel("Processing Queue"); QLabel* processingQueueLabel = new QLabel("Processing Queue");
gemDownloadLayout->addWidget(processingQueueLabel); gemDownloadLayout->addWidget(processingQueueLabel);
QWidget* downloadingItemWidget = new QWidget(); m_downloadingListWidget = new QWidget();
downloadingItemWidget->setObjectName("GemCatalogCartOverlayGemDownloadBG"); m_downloadingListWidget->setObjectName("GemCatalogCartOverlayGemDownloadBG");
gemDownloadLayout->addWidget(downloadingItemWidget); gemDownloadLayout->addWidget(m_downloadingListWidget);
QVBoxLayout* downloadingItemLayout = new QVBoxLayout(); QVBoxLayout* downloadingItemLayout = new QVBoxLayout();
downloadingItemLayout->setAlignment(Qt::AlignTop); downloadingItemLayout->setAlignment(Qt::AlignTop);
downloadingItemWidget->setLayout(downloadingItemLayout); m_downloadingListWidget->setLayout(downloadingItemLayout);
auto update = [=](int downloadProgress) QLabel* downloadsInProgessLabel = new QLabel("");
downloadsInProgessLabel->setObjectName("NumDownloadsInProgressLabel");
downloadingItemLayout->addWidget(downloadsInProgessLabel);
if (m_downloadController->IsDownloadQueueEmpty())
{
m_downloadSectionWidget->hide();
}
else
{ {
if (m_downloadController->IsDownloadQueueEmpty()) // Setup gem download rows for gems that are already in the queue
const AZStd::vector<QString>& downloadQueue = m_downloadController->GetDownloadQueue();
for (const QString& gemName : downloadQueue)
{ {
widget->hide(); GemDownloadAdded(gemName);
} }
else }
{
widget->setUpdatesEnabled(false);
// remove items
QLayoutItem* layoutItem = nullptr;
while ((layoutItem = downloadingItemLayout->takeAt(0)) != nullptr)
{
if (layoutItem->layout())
{
// Gem info row
QLayoutItem* rowLayoutItem = nullptr;
while ((rowLayoutItem = layoutItem->layout()->takeAt(0)) != nullptr)
{
rowLayoutItem->widget()->deleteLater();
}
layoutItem->layout()->deleteLater();
}
if (layoutItem->widget())
{
layoutItem->widget()->deleteLater();
}
}
// Setup gem download rows
const AZStd::vector<QString>& downloadQueue = m_downloadController->GetDownloadQueue();
QLabel* downloadsInProgessLabel = new QLabel("");
downloadsInProgessLabel->setText(
QString("%1 %2").arg(downloadQueue.size()).arg(downloadQueue.size() == 1 ? tr("download in progress...") : tr("downloads in progress...")));
downloadingItemLayout->addWidget(downloadsInProgessLabel);
for (int downloadingGemNumber = 0; downloadingGemNumber < downloadQueue.size(); ++downloadingGemNumber)
{
QHBoxLayout* nameProgressLayout = new QHBoxLayout();
const QString& gemName = downloadQueue[downloadingGemNumber];
TagWidget* newTag = new TagWidget({gemName, gemName});
nameProgressLayout->addWidget(newTag);
QLabel* progress = new QLabel(downloadingGemNumber == 0? QString("%1%").arg(downloadProgress) : tr("Queued")); // connect to download controller data changed
nameProgressLayout->addWidget(progress); connect(m_downloadController, &DownloadController::GemDownloadAdded, this, &CartOverlayWidget::GemDownloadAdded);
connect(m_downloadController, &DownloadController::GemDownloadRemoved, this, &CartOverlayWidget::GemDownloadRemoved);
connect(m_downloadController, &DownloadController::GemDownloadProgress, this, &CartOverlayWidget::GemDownloadProgress);
connect(m_downloadController, &DownloadController::Done, this, &CartOverlayWidget::GemDownloadComplete);
}
QSpacerItem* spacer = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum); void CartOverlayWidget::GemDownloadAdded(const QString& gemName)
nameProgressLayout->addSpacerItem(spacer); {
// Containing widget for the current download item
QWidget* newGemDownloadWidget = new QWidget();
newGemDownloadWidget->setObjectName(gemName);
QVBoxLayout* downloadingGemLayout = new QVBoxLayout(newGemDownloadWidget);
newGemDownloadWidget->setLayout(downloadingGemLayout);
// Gem name, progress string, cancel
QHBoxLayout* nameProgressLayout = new QHBoxLayout(newGemDownloadWidget);
TagWidget* newTag = new TagWidget({gemName, gemName}, newGemDownloadWidget);
nameProgressLayout->addWidget(newTag);
QLabel* progress = new QLabel(tr("Queued"), newGemDownloadWidget);
progress->setObjectName("DownloadProgressLabel");
nameProgressLayout->addWidget(progress);
nameProgressLayout->addStretch();
QLabel* cancelText = new QLabel(tr("<a href=\"%1\">Cancel</a>").arg(gemName), newGemDownloadWidget);
cancelText->setTextInteractionFlags(Qt::LinksAccessibleByMouse);
connect(cancelText, &QLabel::linkActivated, this, &CartOverlayWidget::OnCancelDownloadActivated);
nameProgressLayout->addWidget(cancelText);
downloadingGemLayout->addLayout(nameProgressLayout);
// Progress bar
QProgressBar* downloadProgessBar = new QProgressBar(newGemDownloadWidget);
downloadProgessBar->setObjectName("DownloadProgressBar");
downloadingGemLayout->addWidget(downloadProgessBar);
downloadProgessBar->setValue(0);
m_downloadingListWidget->layout()->addWidget(newGemDownloadWidget);
const AZStd::vector<QString>& downloadQueue = m_downloadController->GetDownloadQueue();
QLabel* numDownloads = m_downloadingListWidget->findChild<QLabel*>("NumDownloadsInProgressLabel");
numDownloads->setText(QString("%1 %2")
.arg(downloadQueue.size())
.arg(downloadQueue.size() == 1 ? tr("download in progress...") : tr("downloads in progress...")));
m_downloadingListWidget->show();
}
QLabel* cancelText = new QLabel(QString("<a href=\"%1\">Cancel</a>").arg(gemName)); void CartOverlayWidget::GemDownloadRemoved(const QString& gemName)
cancelText->setTextInteractionFlags(Qt::LinksAccessibleByMouse); {
connect(cancelText, &QLabel::linkActivated, this, &CartOverlayWidget::OnCancelDownloadActivated); QWidget* gemToRemove = m_downloadingListWidget->findChild<QWidget*>(gemName);
nameProgressLayout->addWidget(cancelText); if (gemToRemove)
downloadingItemLayout->addLayout(nameProgressLayout); {
gemToRemove->deleteLater();
}
QProgressBar* downloadProgessBar = new QProgressBar(); if (m_downloadController->IsDownloadQueueEmpty())
downloadingItemLayout->addWidget(downloadProgessBar); {
downloadProgessBar->setValue(downloadingGemNumber == 0 ? downloadProgress : 0); m_downloadSectionWidget->hide();
} }
else
{
size_t downloadQueueSize = m_downloadController->GetDownloadQueue().size();
QLabel* numDownloads = m_downloadingListWidget->findChild<QLabel*>("NumDownloadsInProgressLabel");
numDownloads->setText(QString("%1 %2")
.arg(downloadQueueSize)
.arg(downloadQueueSize == 1 ? tr("download in progress...") : tr("downloads in progress...")));
}
}
widget->setUpdatesEnabled(true); void CartOverlayWidget::GemDownloadProgress(const QString& gemName, int percentage)
widget->show(); {
QWidget* gemToUpdate = m_downloadingListWidget->findChild<QWidget*>(gemName);
if (gemToUpdate)
{
QLabel* progressLabel = gemToUpdate->findChild<QLabel*>("DownloadProgressLabel");
if (progressLabel)
{
progressLabel->setText(QString("%1%").arg(percentage));
} }
}; QProgressBar* progressBar = gemToUpdate->findChild<QProgressBar*>("DownloadProgressBar");
if (progressBar)
{
progressBar->setValue(percentage);
}
}
}
auto downloadEnded = [=](const QString& /*gemName*/, bool /*success*/) void CartOverlayWidget::GemDownloadComplete(const QString& gemName, bool /*success*/)
{ {
update(0); // update the list to remove the gem that has finished GemDownloadRemoved(gemName); // update the list to remove the gem that has finished
};
// connect to download controller data changed
connect(m_downloadController, &DownloadController::GemDownloadProgress, this, update);
connect(m_downloadController, &DownloadController::Done, this, downloadEnded);
update(0);
} }
QVector<Tag> CartOverlayWidget::GetTagsFromModelIndices(const QVector<QModelIndex>& gems) const QVector<Tag> CartOverlayWidget::GetTagsFromModelIndices(const QVector<QModelIndex>& gems) const

@ -34,6 +34,13 @@ namespace O3DE::ProjectManager
public: public:
CartOverlayWidget(GemModel* gemModel, DownloadController* downloadController, QWidget* parent = nullptr); CartOverlayWidget(GemModel* gemModel, DownloadController* downloadController, QWidget* parent = nullptr);
~CartOverlayWidget();
public slots:
void GemDownloadAdded(const QString& gemName);
void GemDownloadRemoved(const QString& gemName);
void GemDownloadProgress(const QString& gemName, int percentage);
void GemDownloadComplete(const QString& gemName, bool success);
private: private:
QVector<Tag> GetTagsFromModelIndices(const QVector<QModelIndex>& gems) const; QVector<Tag> GetTagsFromModelIndices(const QVector<QModelIndex>& gems) const;
@ -47,6 +54,9 @@ namespace O3DE::ProjectManager
GemModel* m_gemModel = nullptr; GemModel* m_gemModel = nullptr;
DownloadController* m_downloadController = nullptr; DownloadController* m_downloadController = nullptr;
QWidget* m_downloadSectionWidget = nullptr;
QWidget* m_downloadingListWidget = nullptr;
inline constexpr static int s_width = 240; inline constexpr static int s_width = 240;
}; };

@ -90,6 +90,13 @@ namespace O3DE::ProjectManager
m_projectPath = 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();
@ -251,6 +258,7 @@ namespace O3DE::ProjectManager
if (added && GemModel::GetDownloadStatus(modelIndex) == GemInfo::DownloadStatus::NotDownloaded) if (added && GemModel::GetDownloadStatus(modelIndex) == GemInfo::DownloadStatus::NotDownloaded)
{ {
m_downloadController->AddGemDownload(GemModel::GetName(modelIndex)); m_downloadController->AddGemDownload(GemModel::GetName(modelIndex));
GemModel::SetDownloadStatus(*m_proxyModel, m_proxyModel->mapFromSource(modelIndex), GemInfo::DownloadStatus::Downloading);
} }
} }

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

@ -120,7 +120,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")));
m_mainWidget->adjustSize(); m_mainWidget->adjustSize();
m_mainWidget->show(); m_mainWidget->show();

@ -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)
{ {

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

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

@ -85,12 +85,12 @@ void ApplyIBL(Surface surface, inout LightingData lightingData)
if(useIbl) if(useIbl)
{ {
float iblExposureFactor = pow(2.0, SceneSrg::m_iblExposure); float globalIblExposure = pow(2.0, SceneSrg::m_iblExposure);
if(useDiffuseIbl) if(useDiffuseIbl)
{ {
float3 iblDiffuse = GetIblDiffuse(surface.normal, surface.albedo, lightingData.diffuseResponse); float3 iblDiffuse = GetIblDiffuse(surface.normal, surface.albedo, lightingData.diffuseResponse);
lightingData.diffuseLighting += (iblDiffuse * iblExposureFactor * lightingData.diffuseAmbientOcclusion); lightingData.diffuseLighting += (iblDiffuse * globalIblExposure * lightingData.diffuseAmbientOcclusion);
} }
if(useSpecularIbl) if(useSpecularIbl)
@ -116,7 +116,8 @@ void ApplyIBL(Surface surface, inout LightingData lightingData)
iblSpecular = iblSpecular * (1.0 - clearCoatResponse) * (1.0 - clearCoatResponse) + clearCoatIblSpecular; iblSpecular = iblSpecular * (1.0 - clearCoatResponse) * (1.0 - clearCoatResponse) + clearCoatIblSpecular;
} }
lightingData.specularLighting += (iblSpecular * iblExposureFactor); float exposure = ObjectSrg::m_reflectionProbeData.m_useReflectionProbe ? pow(2.0, ObjectSrg::m_reflectionProbeData.m_exposure) : globalIblExposure;
lightingData.specularLighting += (iblSpecular * exposure);
} }
} }
} }

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

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

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

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

@ -39,6 +39,8 @@ namespace AZ
bool IsCubeMapReferenced(const AZStd::string& relativePath) override; bool IsCubeMapReferenced(const AZStd::string& relativePath) override;
bool IsValidProbeHandle(const ReflectionProbeHandle& probe) const override { return (probe.get() != nullptr); } bool IsValidProbeHandle(const ReflectionProbeHandle& probe) const override { return (probe.get() != nullptr); }
void ShowProbeVisualization(const ReflectionProbeHandle& probe, bool showVisualization) override; void ShowProbeVisualization(const ReflectionProbeHandle& probe, bool showVisualization) override;
void SetRenderExposure(const ReflectionProbeHandle& probe, float renderExposure) override;
void SetBakeExposure(const ReflectionProbeHandle& probe, float bakeExposure) override;
// FeatureProcessor overrides // FeatureProcessor overrides
void Activate() override; void Activate() override;

@ -50,6 +50,8 @@ namespace AZ
virtual bool IsCubeMapReferenced(const AZStd::string& relativePath) = 0; virtual bool IsCubeMapReferenced(const AZStd::string& relativePath) = 0;
virtual bool IsValidProbeHandle(const ReflectionProbeHandle& probe) const = 0; virtual bool IsValidProbeHandle(const ReflectionProbeHandle& probe) const = 0;
virtual void ShowProbeVisualization(const ReflectionProbeHandle& probe, bool showVisualization) = 0; virtual void ShowProbeVisualization(const ReflectionProbeHandle& probe, bool showVisualization) = 0;
virtual void SetRenderExposure(const ReflectionProbeHandle& probe, float renderExposure) = 0;
virtual void SetBakeExposure(const ReflectionProbeHandle& probe, float bakeExposure) = 0;
}; };
} // namespace Render } // namespace Render
} // namespace AZ } // namespace AZ

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

@ -1181,10 +1181,13 @@ namespace AZ
AZ::RHI::ShaderInputConstantIndex useParallaxCorrectionConstantIndex = objectSrg->FindShaderInputConstantIndex(Name("m_reflectionProbeData.m_useParallaxCorrection")); AZ::RHI::ShaderInputConstantIndex useParallaxCorrectionConstantIndex = objectSrg->FindShaderInputConstantIndex(Name("m_reflectionProbeData.m_useParallaxCorrection"));
AZ_Error("ModelDataInstance", useParallaxCorrectionConstantIndex.IsValid(), "Failed to find ReflectionProbe constant index"); AZ_Error("ModelDataInstance", useParallaxCorrectionConstantIndex.IsValid(), "Failed to find ReflectionProbe constant index");
AZ::RHI::ShaderInputConstantIndex exposureConstantIndex = objectSrg->FindShaderInputConstantIndex(Name("m_reflectionProbeData.m_exposure"));
AZ_Error("MeshDataInstance", exposureConstantIndex.IsValid(), "Failed to find ReflectionProbe constant index");
// retrieve probe cubemap index // retrieve probe cubemap index
Name reflectionCubeMapImageName = Name("m_reflectionProbeCubeMap"); Name reflectionCubeMapImageName = Name("m_reflectionProbeCubeMap");
RHI::ShaderInputImageIndex reflectionCubeMapImageIndex = objectSrg->FindShaderInputImageIndex(reflectionCubeMapImageName); RHI::ShaderInputImageIndex reflectionCubeMapImageIndex = objectSrg->FindShaderInputImageIndex(reflectionCubeMapImageName);
AZ_Error("ModelDataInstance", reflectionCubeMapImageIndex.IsValid(), "Failed to find shader image index [%s]", reflectionCubeMapImageName.GetCStr()); AZ_Error("MeshDataInstance", reflectionCubeMapImageIndex.IsValid(), "Failed to find shader image index [%s]", reflectionCubeMapImageName.GetCStr());
// retrieve the list of probes that contain the centerpoint of the mesh // retrieve the list of probes that contain the centerpoint of the mesh
TransformServiceFeatureProcessor* transformServiceFeatureProcessor = m_scene->GetFeatureProcessor<TransformServiceFeatureProcessor>(); TransformServiceFeatureProcessor* transformServiceFeatureProcessor = m_scene->GetFeatureProcessor<TransformServiceFeatureProcessor>();
@ -1201,6 +1204,7 @@ namespace AZ
objectSrg->SetConstant(innerObbHalfLengthsConstantIndex, reflectionProbes[0]->GetInnerObbWs().GetHalfLengths()); objectSrg->SetConstant(innerObbHalfLengthsConstantIndex, reflectionProbes[0]->GetInnerObbWs().GetHalfLengths());
objectSrg->SetConstant(useReflectionProbeConstantIndex, true); objectSrg->SetConstant(useReflectionProbeConstantIndex, true);
objectSrg->SetConstant(useParallaxCorrectionConstantIndex, reflectionProbes[0]->GetUseParallaxCorrection()); objectSrg->SetConstant(useParallaxCorrectionConstantIndex, reflectionProbes[0]->GetUseParallaxCorrection());
objectSrg->SetConstant(exposureConstantIndex, reflectionProbes[0]->GetRenderExposure());
objectSrg->SetImage(reflectionCubeMapImageIndex, reflectionProbes[0]->GetCubeMapImage()); objectSrg->SetImage(reflectionCubeMapImageIndex, reflectionProbes[0]->GetCubeMapImage());
} }

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

@ -61,6 +61,7 @@ namespace AZ
RHI::ShaderInputNameIndex m_outerObbHalfLengthsRenderConstantIndex = "m_outerObbHalfLengths"; RHI::ShaderInputNameIndex m_outerObbHalfLengthsRenderConstantIndex = "m_outerObbHalfLengths";
RHI::ShaderInputNameIndex m_innerObbHalfLengthsRenderConstantIndex = "m_innerObbHalfLengths"; RHI::ShaderInputNameIndex m_innerObbHalfLengthsRenderConstantIndex = "m_innerObbHalfLengths";
RHI::ShaderInputNameIndex m_useParallaxCorrectionRenderConstantIndex = "m_useParallaxCorrection"; RHI::ShaderInputNameIndex m_useParallaxCorrectionRenderConstantIndex = "m_useParallaxCorrection";
RHI::ShaderInputNameIndex m_exposureConstantIndex = "m_exposure";
RHI::ShaderInputNameIndex m_reflectionCubeMapRenderImageIndex = "m_reflectionCubeMap"; RHI::ShaderInputNameIndex m_reflectionCubeMapRenderImageIndex = "m_reflectionCubeMap";
}; };
@ -106,6 +107,14 @@ namespace AZ
// enables or disables rendering of the visualization sphere // enables or disables rendering of the visualization sphere
void ShowVisualization(bool showVisualization); void ShowVisualization(bool showVisualization);
// the exposure to use when rendering meshes with this probe's cubemap
void SetRenderExposure(float renderExposure);
float GetRenderExposure() const { return m_renderExposure; }
// the exposure to use when baking the probe cubemap
void SetBakeExposure(float bakeExposure);
float GetBakeExposure() const { return m_bakeExposure; }
private: private:
AZ_DISABLE_COPY_MOVE(ReflectionProbe); AZ_DISABLE_COPY_MOVE(ReflectionProbe);
@ -157,6 +166,8 @@ namespace AZ
RHI::ConstPtr<RHI::DrawPacket> m_blendWeightDrawPacket; RHI::ConstPtr<RHI::DrawPacket> m_blendWeightDrawPacket;
RHI::ConstPtr<RHI::DrawPacket> m_renderOuterDrawPacket; RHI::ConstPtr<RHI::DrawPacket> m_renderOuterDrawPacket;
RHI::ConstPtr<RHI::DrawPacket> m_renderInnerDrawPacket; RHI::ConstPtr<RHI::DrawPacket> m_renderInnerDrawPacket;
float m_renderExposure = 0.0f;
float m_bakeExposure = 0.0f;
bool m_updateSrg = false; bool m_updateSrg = false;
const RHI::DrawItemSortKey InvalidSortKey = static_cast<RHI::DrawItemSortKey>(-1); const RHI::DrawItemSortKey InvalidSortKey = static_cast<RHI::DrawItemSortKey>(-1);
@ -169,8 +180,10 @@ namespace AZ
RPI::Ptr<RPI::EnvironmentCubeMapPass> m_environmentCubeMapPass = nullptr; RPI::Ptr<RPI::EnvironmentCubeMapPass> m_environmentCubeMapPass = nullptr;
RPI::RenderPipelineId m_environmentCubeMapPipelineId; RPI::RenderPipelineId m_environmentCubeMapPipelineId;
BuildCubeMapCallback m_callback; BuildCubeMapCallback m_callback;
RHI::ShaderInputNameIndex m_iblExposureConstantIndex = "m_iblExposure"; RHI::ShaderInputNameIndex m_globalIblExposureConstantIndex = "m_iblExposure";
float m_previousExposure = 0.0f; RHI::ShaderInputNameIndex m_skyBoxExposureConstantIndex = "m_cubemapExposure";
float m_previousGlobalIblExposure = 0.0f;
float m_previousSkyBoxExposure = 0.0f;
bool m_buildingCubeMap = false; bool m_buildingCubeMap = false;
}; };

@ -283,6 +283,18 @@ namespace AZ
probe->ShowVisualization(showVisualization); probe->ShowVisualization(showVisualization);
} }
void ReflectionProbeFeatureProcessor::SetRenderExposure(const ReflectionProbeHandle& probe, float renderExposure)
{
AZ_Assert(probe.get(), "SetRenderExposure called with an invalid handle");
probe->SetRenderExposure(renderExposure);
}
void ReflectionProbeFeatureProcessor::SetBakeExposure(const ReflectionProbeHandle& probe, float bakeExposure)
{
AZ_Assert(probe.get(), "SetBakeExposure called with an invalid handle");
probe->SetBakeExposure(bakeExposure);
}
void ReflectionProbeFeatureProcessor::FindReflectionProbes(const Vector3& position, ReflectionProbeVector& reflectionProbes) void ReflectionProbeFeatureProcessor::FindReflectionProbes(const Vector3& position, ReflectionProbeVector& reflectionProbes)
{ {
reflectionProbes.clear(); reflectionProbes.clear();

@ -75,6 +75,9 @@ namespace AZ
//! Return True if the swap chain prefers exclusive full screen mode and a transition happened, false otherwise. //! Return True if the swap chain prefers exclusive full screen mode and a transition happened, false otherwise.
virtual bool SetExclusiveFullScreenState([[maybe_unused]]bool fullScreenState) { return false; } virtual bool SetExclusiveFullScreenState([[maybe_unused]]bool fullScreenState) { return false; }
//! Recreate the swapchain if it becomes invalid during presenting. This should happen at the end of the frame
//! due to images being used as attachments in the frame graph.
virtual void ProcessRecreation() {};
protected: protected:
SwapChain(); SwapChain();
@ -98,6 +101,14 @@ namespace AZ
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
//! Shutdown and clear all the images.
void ShutdownImages();
//! Initialized all the images.
ResultCode InitImages();
//! Flag indicating if swapchain recreation is needed at the end of the frame.
bool m_pendingRecreation = false;
private: private:
bool ValidateDescriptor(const SwapChainDescriptor& descriptor) const; bool ValidateDescriptor(const SwapChainDescriptor& descriptor) const;

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

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

@ -59,6 +59,19 @@ namespace AZ
m_swapChainBarrier.m_isValid = true; m_swapChainBarrier.m_isValid = true;
} }
void SwapChain::ProcessRecreation()
{
if (m_pendingRecreation)
{
ShutdownImages();
InvalidateNativeSwapChain();
CreateSwapchain();
InitImages();
m_pendingRecreation = false;
}
}
void SwapChain::SetVerticalSyncIntervalInternal(uint32_t previousVsyncInterval) void SwapChain::SetVerticalSyncIntervalInternal(uint32_t previousVsyncInterval)
{ {
if (GetDescriptor().m_verticalSyncInterval == 0 || previousVsyncInterval == 0) if (GetDescriptor().m_verticalSyncInterval == 0 || previousVsyncInterval == 0)
@ -231,8 +244,7 @@ namespace AZ
// VK_SUBOPTIMAL_KHR is treated as success, but we better update the surface info as well. // VK_SUBOPTIMAL_KHR is treated as success, but we better update the surface info as well.
if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR)
{ {
InvalidateNativeSwapChain(); m_pendingRecreation = true;
CreateSwapchain();
} }
else else
{ {
@ -246,18 +258,16 @@ namespace AZ
} }
}; };
m_presentationQueue->QueueCommand(AZStd::move(presentCommand));
uint32_t acquiredImageIndex = GetCurrentImageIndex(); uint32_t acquiredImageIndex = GetCurrentImageIndex();
RHI::ResultCode result = AcquireNewImage(&acquiredImageIndex); RHI::ResultCode result = AcquireNewImage(&acquiredImageIndex);
if (result == RHI::ResultCode::Fail) if (result == RHI::ResultCode::Fail)
{ {
InvalidateNativeSwapChain(); m_pendingRecreation = true;
CreateSwapchain();
return 0; return 0;
} }
else else
{ {
m_presentationQueue->QueueCommand(AZStd::move(presentCommand));
return acquiredImageIndex; return acquiredImageIndex;
} }
} }

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

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

@ -32,7 +32,6 @@ namespace AZ
namespace RPI namespace RPI
{ {
class Scene; class Scene;
struct TickTimeInfo;
class ShaderResourceGroup; class ShaderResourceGroup;
class AnyAsset; class AnyAsset;
class WindowContext; class WindowContext;
@ -203,7 +202,7 @@ namespace AZ
void OnRemovedFromScene(Scene* scene); void OnRemovedFromScene(Scene* scene);
// Called when this pipeline is about to be rendered // Called when this pipeline is about to be rendered
void OnStartFrame(const TickTimeInfo& tick); void OnStartFrame(float time);
// Called when the rendering of current frame is finished. // Called when the rendering of current frame is finished.
void OnFrameEnd(); void OnFrameEnd();

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

@ -268,21 +268,23 @@ namespace AZ
AssetInitBus::Broadcast(&AssetInitBus::Events::PostLoadInit); AssetInitBus::Broadcast(&AssetInitBus::Events::PostLoadInit);
// Update tick time info m_currentSimulationTime = GetCurrentTime();
FillTickTimeInfo();
for (auto& scene : m_scenes) for (auto& scene : m_scenes)
{ {
scene->Simulate(m_tickTime, m_simulationJobPolicy); scene->Simulate(m_simulationJobPolicy, m_currentSimulationTime);
} }
} }
void RPISystem::FillTickTimeInfo() float RPISystem::GetCurrentTime()
{ {
AZ::TickRequestBus::BroadcastResult(m_tickTime.m_gameDeltaTime, &AZ::TickRequestBus::Events::GetTickDeltaTime); ScriptTimePoint timeAtCurrentTick;
ScriptTimePoint currentTime; AZ::TickRequestBus::BroadcastResult(timeAtCurrentTick, &AZ::TickRequestBus::Events::GetTimeAtCurrentTick);
AZ::TickRequestBus::BroadcastResult(currentTime, &AZ::TickRequestBus::Events::GetTimeAtCurrentTick);
m_tickTime.m_currentGameTime = static_cast<float>(currentTime.GetSeconds()); // We subtract the start time to maximize precision of the time value, since we will be converting it to a float.
double currentTime = timeAtCurrentTick.GetSeconds() - m_startTime.GetSeconds();
return aznumeric_cast<float>(currentTime);
} }
void RPISystem::RenderTick() void RPISystem::RenderTick()
@ -301,7 +303,7 @@ namespace AZ
// [GFX TODO] We may parallel scenes' prepare render. // [GFX TODO] We may parallel scenes' prepare render.
for (auto& scenePtr : m_scenes) for (auto& scenePtr : m_scenes)
{ {
scenePtr->PrepareRender(m_tickTime, m_prepareRenderJobPolicy); scenePtr->PrepareRender(m_prepareRenderJobPolicy, m_currentSimulationTime);
} }
m_rhiSystem.FrameUpdate( m_rhiSystem.FrameUpdate(

@ -375,7 +375,7 @@ namespace AZ
m_scene->RemoveRenderPipeline(m_nameId); m_scene->RemoveRenderPipeline(m_nameId);
} }
void RenderPipeline::OnStartFrame([[maybe_unused]] const TickTimeInfo& tick) void RenderPipeline::OnStartFrame([[maybe_unused]] float time)
{ {
AZ_PROFILE_SCOPE(RPI, "RenderPipeline: OnStartFrame"); AZ_PROFILE_SCOPE(RPI, "RenderPipeline: OnStartFrame");

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

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

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

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

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

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

Loading…
Cancel
Save