diff --git a/AutomatedTesting/Gem/Code/enabled_gems.cmake b/AutomatedTesting/Gem/Code/enabled_gems.cmake index f653a54505..7d33a65bc7 100644 --- a/AutomatedTesting/Gem/Code/enabled_gems.cmake +++ b/AutomatedTesting/Gem/Code/enabled_gems.cmake @@ -54,5 +54,6 @@ set(ENABLED_GEMS AWSMetrics PrefabBuilder AudioSystem + Terrain Profiler ) diff --git a/AutomatedTesting/Gem/PythonTests/CMakeLists.txt b/AutomatedTesting/Gem/PythonTests/CMakeLists.txt index fd3222ba83..d2b7075e22 100644 --- a/AutomatedTesting/Gem/PythonTests/CMakeLists.txt +++ b/AutomatedTesting/Gem/PythonTests/CMakeLists.txt @@ -56,6 +56,9 @@ add_subdirectory(streaming) ## Smoke ## add_subdirectory(smoke) +## Terrain ## +add_subdirectory(Terrain) + ## AWS ## add_subdirectory(AWS) diff --git a/AutomatedTesting/Gem/PythonTests/NvCloth/tests/NvCloth_AddClothSimulationToActor.py b/AutomatedTesting/Gem/PythonTests/NvCloth/tests/NvCloth_AddClothSimulationToActor.py index 357067b176..e4150f7af8 100644 --- a/AutomatedTesting/Gem/PythonTests/NvCloth/tests/NvCloth_AddClothSimulationToActor.py +++ b/AutomatedTesting/Gem/PythonTests/NvCloth/tests/NvCloth_AddClothSimulationToActor.py @@ -50,6 +50,7 @@ def NvCloth_AddClothSimulationToActor(): # Constants FRAMES_IN_GAME_MODE = 200 + CLOTH_GEM_ERROR_WARNING_LIST = ["Cloth", "NvCloth", "ClothComponentMesh", "ActorClothSkinning", "ActorClothSkinning", "TangentSpaceHelper", "MeshAssetHelper", "ActorAssetHelper", "ClothDebugDisplay"] helper.init_idle() # 1) Load the level @@ -64,14 +65,16 @@ def NvCloth_AddClothSimulationToActor(): general.idle_wait_frames(FRAMES_IN_GAME_MODE) # 5) Verify there are no errors and warnings in the logs - success_condition = not (section_tracer.has_errors or section_tracer.has_warnings) - Report.result(Tests.no_errors_and_warnings_found, success_condition) - if not success_condition: - if section_tracer.has_warnings: - Report.info(f"Warnings found: {section_tracer.warnings}") - if section_tracer.has_errors: - Report.info(f"Errors found: {section_tracer.errors}") - Report.failure(Tests.no_errors_and_warnings_found) + has_errors_or_warnings = False + for error_msg in section_tracer.errors: + if error_msg.window in CLOTH_GEM_ERROR_WARNING_LIST: + has_errors_or_warnings = True + Report.info(f"Cloth error found: {error_msg}") + for warning_msg in section_tracer.warnings: + if warning_msg.window in CLOTH_GEM_ERROR_WARNING_LIST: + has_errors_or_warnings = True + Report.info(f"Cloth warning found: {warning_msg}") + Report.result(Tests.no_errors_and_warnings_found, not has_errors_or_warnings) # 6) Exit game mode helper.exit_game_mode(Tests.exit_game_mode) diff --git a/AutomatedTesting/Gem/PythonTests/NvCloth/tests/NvCloth_AddClothSimulationToMesh.py b/AutomatedTesting/Gem/PythonTests/NvCloth/tests/NvCloth_AddClothSimulationToMesh.py index 0f9d8448f7..707f745cea 100644 --- a/AutomatedTesting/Gem/PythonTests/NvCloth/tests/NvCloth_AddClothSimulationToMesh.py +++ b/AutomatedTesting/Gem/PythonTests/NvCloth/tests/NvCloth_AddClothSimulationToMesh.py @@ -50,6 +50,7 @@ def NvCloth_AddClothSimulationToMesh(): # Constants FRAMES_IN_GAME_MODE = 200 + CLOTH_GEM_ERROR_WARNING_LIST = ["Cloth", "NvCloth", "ClothComponentMesh", "ActorClothSkinning", "ActorClothSkinning", "TangentSpaceHelper", "MeshAssetHelper", "ActorAssetHelper", "ClothDebugDisplay"] helper.init_idle() # 1) Load the level @@ -64,14 +65,16 @@ def NvCloth_AddClothSimulationToMesh(): general.idle_wait_frames(FRAMES_IN_GAME_MODE) # 5) Verify there are no errors and warnings in the logs - success_condition = not (section_tracer.has_errors or section_tracer.has_warnings) - Report.result(Tests.no_errors_and_warnings_found, success_condition) - if not success_condition: - if section_tracer.has_warnings: - Report.info(f"Warnings found: {section_tracer.warnings}") - if section_tracer.has_errors: - Report.info(f"Errors found: {section_tracer.errors}") - Report.failure(Tests.no_errors_and_warnings_found) + has_errors_or_warnings = False + for error_msg in section_tracer.errors: + if error_msg.window in CLOTH_GEM_ERROR_WARNING_LIST: + has_errors_or_warnings = True + Report.info(f"Cloth error found: {error_msg}") + for warning_msg in section_tracer.warnings: + if warning_msg.window in CLOTH_GEM_ERROR_WARNING_LIST: + has_errors_or_warnings = True + Report.info(f"Cloth warning found: {warning_msg}") + Report.result(Tests.no_errors_and_warnings_found, not has_errors_or_warnings) # 6) Exit game mode helper.exit_game_mode(Tests.exit_game_mode) diff --git a/AutomatedTesting/Gem/PythonTests/Terrain/CMakeLists.txt b/AutomatedTesting/Gem/PythonTests/Terrain/CMakeLists.txt new file mode 100644 index 0000000000..9f8ba06829 --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/Terrain/CMakeLists.txt @@ -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() diff --git a/AutomatedTesting/Gem/PythonTests/Terrain/EditorScripts/TerrainPhysicsCollider_ChangesSizeWithAxisAlignedBoxShapeChanges.py b/AutomatedTesting/Gem/PythonTests/Terrain/EditorScripts/TerrainPhysicsCollider_ChangesSizeWithAxisAlignedBoxShapeChanges.py new file mode 100644 index 0000000000..aba506ea20 --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/Terrain/EditorScripts/TerrainPhysicsCollider_ChangesSizeWithAxisAlignedBoxShapeChanges.py @@ -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) diff --git a/AutomatedTesting/Gem/PythonTests/Terrain/TestSuite_Main.py b/AutomatedTesting/Gem/PythonTests/Terrain/TestSuite_Main.py new file mode 100644 index 0000000000..620d84d7db --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/Terrain/TestSuite_Main.py @@ -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 diff --git a/AutomatedTesting/Gem/PythonTests/Terrain/__init__.py b/AutomatedTesting/Gem/PythonTests/Terrain/__init__.py new file mode 100644 index 0000000000..f5193b300e --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/Terrain/__init__.py @@ -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 +""" diff --git a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/EntityOutliner_EntityOrdering.py b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/EntityOutliner_EntityOrdering.py new file mode 100644 index 0000000000..e4575d4d17 --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/EntityOutliner_EntityOrdering.py @@ -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) diff --git a/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Main.py b/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Main.py index 26b254ae71..49069569eb 100644 --- a/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Main.py +++ b/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Main.py @@ -41,3 +41,15 @@ class TestAutomation(TestAutomationBase): from .EditorScripts import BasicEditorWorkflows_LevelEntityComponentCRUD as test_module self._run_test(request, workspace, editor, test_module, batch_mode=False, autotest_mode=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"] + ) diff --git a/AutomatedTesting/Gem/PythonTests/smoke/test_Editor_NewExistingLevels_Works.py b/AutomatedTesting/Gem/PythonTests/smoke/test_Editor_NewExistingLevels_Works.py index 6f654b9107..72e548615c 100644 --- a/AutomatedTesting/Gem/PythonTests/smoke/test_Editor_NewExistingLevels_Works.py +++ b/AutomatedTesting/Gem/PythonTests/smoke/test_Editor_NewExistingLevels_Works.py @@ -28,4 +28,4 @@ class TestAutomation(TestAutomationBase): 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"]) diff --git a/AutomatedTesting/Levels/Base/Base.prefab b/AutomatedTesting/Levels/Base/Base.prefab new file mode 100644 index 0000000000..f7e42e7731 --- /dev/null +++ b/AutomatedTesting/Levels/Base/Base.prefab @@ -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 + } + } + } +} \ No newline at end of file diff --git a/Code/Editor/CryEdit.cpp b/Code/Editor/CryEdit.cpp index 5cffedd774..0ea22e8ce3 100644 --- a/Code/Editor/CryEdit.cpp +++ b/Code/Editor/CryEdit.cpp @@ -3974,9 +3974,8 @@ void CCryEditApp::OpenLUAEditor(const char* files) } } - const char* engineRoot = nullptr; - AzFramework::ApplicationRequests::Bus::BroadcastResult(engineRoot, &AzFramework::ApplicationRequests::GetEngineRoot); - AZ_Assert(engineRoot != nullptr, "Unable to communicate to AzFramework::ApplicationRequests::Bus"); + AZ::IO::FixedMaxPathString engineRoot = AZ::Utils::GetEnginePath(); + AZ_Assert(!engineRoot.empty(), "Unable to query Engine Path"); AZStd::string_view exePath; AZ::ComponentApplicationBus::BroadcastResult(exePath, &AZ::ComponentApplicationRequests::GetExecutableFolder); @@ -3995,7 +3994,7 @@ void CCryEditApp::OpenLUAEditor(const char* files) #endif "%s", argumentQuoteString, aznumeric_cast(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()); } diff --git a/Code/Editor/EditorModularViewportCameraComposer.cpp b/Code/Editor/EditorModularViewportCameraComposer.cpp index ce4a0a2e33..72fd37605a 100644 --- a/Code/Editor/EditorModularViewportCameraComposer.cpp +++ b/Code/Editor/EditorModularViewportCameraComposer.cpp @@ -13,9 +13,32 @@ #include #include #include +#include #include #include +AZ_CVAR( + bool, + ed_cameraPinDefaultOrbit, + true, + nullptr, + AZ::ConsoleFunctorFlags::Null, + "Sets whether the default orbit point moves with the camera or not"); +AZ_CVAR( + bool, + ed_cameraDefaultOrbitAxesOrtho, + true, + nullptr, + AZ::ConsoleFunctorFlags::Null, + "Sets whether to draw the default orbit point as orthographic or not"); +AZ_CVAR( + float, + ed_cameraDefaultOrbitFadeDuration, + 0.5f, + nullptr, + AZ::ConsoleFunctorFlags::Null, + "Sets how long the default orbit point should take to appear and disappear"); + namespace SandboxEditor { static AzFramework::TranslateCameraInputChannelIds BuildTranslateCameraInputChannelIds() @@ -174,7 +197,7 @@ namespace SandboxEditor return SandboxEditor::CameraScrollSpeed(); }; - const auto pivotFn = [] + const auto pivotFn = []() -> AZStd::optional { // use the manipulator transform as the pivot point AZStd::optional entityPivot; @@ -187,8 +210,7 @@ namespace SandboxEditor return entityPivot->GetTranslation(); } - // otherwise just use the identity - return AZ::Vector3::CreateZero(); + return AZStd::nullopt; }; m_firstPersonFocusCamera = @@ -199,9 +221,26 @@ namespace SandboxEditor m_orbitCamera = AZStd::make_shared(SandboxEditor::CameraOrbitChannelId()); m_orbitCamera->SetPivotFn( - [pivotFn]([[maybe_unused]] const AZ::Vector3& position, [[maybe_unused]] const AZ::Vector3& direction) + [this, pivotFn](const AZ::Vector3& position, const AZ::Vector3& direction) { - return pivotFn(); + // return the pivot + if (auto pivot = pivotFn()) + { + return pivot.value(); + } + + // start ticking and drawing (for the default pivot) + AZ::TickBus::Handler::BusConnect(); + AzFramework::ViewportDebugDisplayEventBus::Handler::BusConnect(AzToolsFramework::GetEntityContextId()); + + m_defaultOrbiting = true; + // calculate the default orbit point + if (!ed_cameraPinDefaultOrbit || m_orbitCamera->Beginning()) + { + m_defaultOrbitPoint = position + direction * SandboxEditor::CameraDefaultOrbitDistance(); + } + + return m_defaultOrbitPoint; }); m_orbitRotateCamera = AZStd::make_shared(SandboxEditor::CameraOrbitLookChannelId()); @@ -306,4 +345,67 @@ namespace SandboxEditor m_viewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::ClearReferenceFrame); } } + + void EditorModularViewportCameraComposer::OnTick(const float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time) + { + const float delta = [duration = &ed_cameraDefaultOrbitFadeDuration, deltaTime] { + if (*duration == 0.0f) { + return 1.0f; + } + return deltaTime / *duration; + }(); + + if (m_defaultOrbiting) + { + m_defaultOrbitOpacity = AZStd::min(m_defaultOrbitOpacity + delta, 1.0f); + } + else + { + m_defaultOrbitOpacity = AZStd::max(m_defaultOrbitOpacity - delta, 0.0f); + if (m_defaultOrbitOpacity == 0.0f) + { + AZ::TickBus::Handler::BusDisconnect(); + AzFramework::ViewportDebugDisplayEventBus::Handler::BusDisconnect(); + } + } + + m_defaultOrbiting = false; + } + + static void DrawTransformAxis( + AzFramework::DebugDisplayRequests& display, + const AzFramework::CameraState& cameraState, + const AZ::Vector3& pivot, + const float axisLength, + const float alpha) + { + const int prevState = display.GetState(); + + display.DepthWriteOff(); + display.DepthTestOff(); + display.CullOff(); + + const float orthoScale = + ed_cameraDefaultOrbitAxesOrtho ? AzToolsFramework::CalculateScreenToWorldMultiplier(pivot, cameraState) : 1.0f; + + display.SetColor(AZ::Color::CreateFromVector3AndFloat(AZ::Colors::Red.GetAsVector3(), alpha)); + display.DrawLine(pivot, pivot + AZ::Vector3::CreateAxisX() * axisLength * orthoScale); + display.SetColor(AZ::Color::CreateFromVector3AndFloat(AZ::Colors::LawnGreen.GetAsVector3(), alpha)); + display.DrawLine(pivot, pivot + AZ::Vector3::CreateAxisY() * axisLength * orthoScale); + display.SetColor(AZ::Color::CreateFromVector3AndFloat(AZ::Colors::Blue.GetAsVector3(), alpha)); + display.DrawLine(pivot, pivot + AZ::Vector3::CreateAxisZ() * axisLength * orthoScale); + + display.DepthWriteOn(); + display.DepthTestOn(); + display.CullOn(); + + display.SetState(prevState); + } + + void EditorModularViewportCameraComposer::DisplayViewport( + [[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) + { + DrawTransformAxis( + debugDisplay, AzToolsFramework::GetCameraState(viewportInfo.m_viewportId), m_defaultOrbitPoint, 1.0f, m_defaultOrbitOpacity); + } } // namespace SandboxEditor diff --git a/Code/Editor/EditorModularViewportCameraComposer.h b/Code/Editor/EditorModularViewportCameraComposer.h index 9cfd6f3554..6cd5df533c 100644 --- a/Code/Editor/EditorModularViewportCameraComposer.h +++ b/Code/Editor/EditorModularViewportCameraComposer.h @@ -9,6 +9,8 @@ #pragma once #include +#include +#include #include #include #include @@ -20,6 +22,8 @@ namespace SandboxEditor class EditorModularViewportCameraComposer : private EditorModularViewportCameraComposerNotificationBus::Handler , private Camera::EditorCameraNotificationBus::Handler + , private AzFramework::ViewportDebugDisplayEventBus::Handler + , private AZ::TickBus::Handler { public: SANDBOX_API explicit EditorModularViewportCameraComposer(AzFramework::ViewportId viewportId); @@ -29,6 +33,12 @@ namespace SandboxEditor SANDBOX_API AZStd::shared_ptr CreateModularViewportCameraController(); private: + // AzFramework::ViewportDebugDisplayEventBus overrides ... + void DisplayViewport(const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) override; + + // AZ::TickBus overrides ... + void OnTick(float deltaTime, AZ::ScriptTimePoint time) override; + //! Setup all internal camera inputs. void SetupCameras(); @@ -52,5 +62,9 @@ namespace SandboxEditor AZStd::shared_ptr m_orbitFocusCamera; AzFramework::ViewportId m_viewportId; + + float m_defaultOrbitOpacity = 0.0f; //!< The default orbit axes opacity (to fade in and out). + AZ::Vector3 m_defaultOrbitPoint = AZ::Vector3::CreateZero(); //!< The orbit point to use when no entity is selected. + bool m_defaultOrbiting = false; //!< Is the camera default orbiting (orbiting when there's no selected entity). }; } // namespace SandboxEditor diff --git a/Code/Editor/EditorPreferencesPageViewportCamera.cpp b/Code/Editor/EditorPreferencesPageViewportCamera.cpp index 2f6e6b1b9d..c81eac0414 100644 --- a/Code/Editor/EditorPreferencesPageViewportCamera.cpp +++ b/Code/Editor/EditorPreferencesPageViewportCamera.cpp @@ -61,7 +61,7 @@ static AZStd::vector GetEditorInputNames() void CEditorPreferencesPage_ViewportCamera::Reflect(AZ::SerializeContext& serialize) { serialize.Class() - ->Version(3) + ->Version(4) ->Field("TranslateSpeed", &CameraMovementSettings::m_translateSpeed) ->Field("RotateSpeed", &CameraMovementSettings::m_rotateSpeed) ->Field("BoostMultiplier", &CameraMovementSettings::m_boostMultiplier) @@ -76,9 +76,8 @@ void CEditorPreferencesPage_ViewportCamera::Reflect(AZ::SerializeContext& serial ->Field("OrbitYawRotationInverted", &CameraMovementSettings::m_orbitYawRotationInverted) ->Field("PanInvertedX", &CameraMovementSettings::m_panInvertedX) ->Field("PanInvertedY", &CameraMovementSettings::m_panInvertedY) - ->Field("DefaultPositionX", &CameraMovementSettings::m_defaultCameraPositionX) - ->Field("DefaultPositionY", &CameraMovementSettings::m_defaultCameraPositionY) - ->Field("DefaultPositionZ", &CameraMovementSettings::m_defaultCameraPositionZ); + ->Field("DefaultPosition", &CameraMovementSettings::m_defaultPosition) + ->Field("DefaultOrbitDistance", &CameraMovementSettings::m_defaultOrbitDistance); serialize.Class() ->Version(2) @@ -159,14 +158,12 @@ void CEditorPreferencesPage_ViewportCamera::Reflect(AZ::SerializeContext& serial AZ::Edit::UIHandlers::CheckBox, &CameraMovementSettings::m_captureCursorLook, "Camera Capture Look Cursor", "Should the cursor be captured (hidden) while performing free look") ->DataElement( - AZ::Edit::UIHandlers::SpinBox, &CameraMovementSettings::m_defaultCameraPositionX, "Default X Camera Position", - "Default X Camera Position when a level is opened") + AZ::Edit::UIHandlers::Vector3, &CameraMovementSettings::m_defaultPosition, "Default Camera Position", + "Default Camera Position when a level is first opened") ->DataElement( - AZ::Edit::UIHandlers::SpinBox, &CameraMovementSettings::m_defaultCameraPositionY, "Default Y Camera Position", - "Default Y Camera Position when a level is opened") - ->DataElement( - AZ::Edit::UIHandlers::SpinBox, &CameraMovementSettings::m_defaultCameraPositionZ, "Default Z Camera Position", - "Default Z Camera Position when a level is opened"); + AZ::Edit::UIHandlers::SpinBox, &CameraMovementSettings::m_defaultOrbitDistance, "Default Orbit Distance", + "The default distance to orbit about when there is no entity selected") + ->Attribute(AZ::Edit::Attributes::Min, minValue); editContext->Class("Camera Input Settings", "") ->DataElement( @@ -283,12 +280,8 @@ void CEditorPreferencesPage_ViewportCamera::OnApply() SandboxEditor::SetCameraOrbitYawRotationInverted(m_cameraMovementSettings.m_orbitYawRotationInverted); SandboxEditor::SetCameraPanInvertedX(m_cameraMovementSettings.m_panInvertedX); SandboxEditor::SetCameraPanInvertedY(m_cameraMovementSettings.m_panInvertedY); - SandboxEditor::SetDefaultCameraEditorPosition( - AZ::Vector3( - m_cameraMovementSettings.m_defaultCameraPositionX, - m_cameraMovementSettings.m_defaultCameraPositionY, - m_cameraMovementSettings.m_defaultCameraPositionZ - )); + SandboxEditor::SetCameraDefaultEditorPosition(m_cameraMovementSettings.m_defaultPosition); + SandboxEditor::SetCameraDefaultOrbitDistance(m_cameraMovementSettings.m_defaultOrbitDistance); SandboxEditor::SetCameraTranslateForwardChannelId(m_cameraInputSettings.m_translateForwardChannelId); SandboxEditor::SetCameraTranslateBackwardChannelId(m_cameraInputSettings.m_translateBackwardChannelId); @@ -325,11 +318,8 @@ void CEditorPreferencesPage_ViewportCamera::InitializeSettings() m_cameraMovementSettings.m_orbitYawRotationInverted = SandboxEditor::CameraOrbitYawRotationInverted(); m_cameraMovementSettings.m_panInvertedX = SandboxEditor::CameraPanInvertedX(); m_cameraMovementSettings.m_panInvertedY = SandboxEditor::CameraPanInvertedY(); - - AZ::Vector3 defaultCameraPosition = SandboxEditor::DefaultEditorCameraPosition(); - m_cameraMovementSettings.m_defaultCameraPositionX = defaultCameraPosition.GetX(); - m_cameraMovementSettings.m_defaultCameraPositionY = defaultCameraPosition.GetY(); - m_cameraMovementSettings.m_defaultCameraPositionZ = defaultCameraPosition.GetZ(); + m_cameraMovementSettings.m_defaultPosition = SandboxEditor::CameraDefaultEditorPosition(); + m_cameraMovementSettings.m_defaultOrbitDistance = SandboxEditor::CameraDefaultOrbitDistance(); m_cameraInputSettings.m_translateForwardChannelId = SandboxEditor::CameraTranslateForwardChannelId().GetName(); m_cameraInputSettings.m_translateBackwardChannelId = SandboxEditor::CameraTranslateBackwardChannelId().GetName(); diff --git a/Code/Editor/EditorPreferencesPageViewportCamera.h b/Code/Editor/EditorPreferencesPageViewportCamera.h index a2705bfd24..41816de51c 100644 --- a/Code/Editor/EditorPreferencesPageViewportCamera.h +++ b/Code/Editor/EditorPreferencesPageViewportCamera.h @@ -9,9 +9,12 @@ #pragma once #include "Include/IPreferencesPage.h" + +#include #include #include #include + #include inline AZ::Crc32 EditorPropertyVisibility(const bool enabled) @@ -43,6 +46,7 @@ private: { AZ_TYPE_INFO(CameraMovementSettings, "{60B8C07E-5F48-4171-A50B-F45558B5CCA1}") + AZ::Vector3 m_defaultPosition; float m_translateSpeed; float m_rotateSpeed; float m_scrollSpeed; @@ -50,16 +54,14 @@ private: float m_panSpeed; float m_boostMultiplier; float m_rotateSmoothness; - bool m_rotateSmoothing; float m_translateSmoothness; - bool m_translateSmoothing; + float m_defaultOrbitDistance; bool m_captureCursorLook; bool m_orbitYawRotationInverted; bool m_panInvertedX; bool m_panInvertedY; - float m_defaultCameraPositionX; - float m_defaultCameraPositionY; - float m_defaultCameraPositionZ; + bool m_rotateSmoothing; + bool m_translateSmoothing; AZ::Crc32 RotateSmoothingVisibility() const { diff --git a/Code/Editor/EditorViewportSettings.cpp b/Code/Editor/EditorViewportSettings.cpp index 8f2be1de6c..ae188c7d98 100644 --- a/Code/Editor/EditorViewportSettings.cpp +++ b/Code/Editor/EditorViewportSettings.cpp @@ -38,6 +38,7 @@ namespace SandboxEditor constexpr AZStd::string_view CameraTranslateSmoothingSetting = "/Amazon/Preferences/Editor/Camera/TranslateSmoothing"; constexpr AZStd::string_view CameraRotateSmoothingSetting = "/Amazon/Preferences/Editor/Camera/RotateSmoothing"; constexpr AZStd::string_view CameraCaptureCursorLookSetting = "/Amazon/Preferences/Editor/Camera/CaptureCursorLook"; + constexpr AZStd::string_view CameraDefaultOrbitDistanceSetting = "/Amazon/Preferences/Editor/Camera/DefaultOrbitDistance"; constexpr AZStd::string_view CameraTranslateForwardIdSetting = "/Amazon/Preferences/Editor/Camera/CameraTranslateForwardId"; constexpr AZStd::string_view CameraTranslateBackwardIdSetting = "/Amazon/Preferences/Editor/Camera/CameraTranslateBackwardId"; constexpr AZStd::string_view CameraTranslateLeftIdSetting = "/Amazon/Preferences/Editor/Camera/CameraTranslateLeftId"; @@ -114,15 +115,15 @@ namespace SandboxEditor return AZStd::make_unique(); } - AZ::Vector3 DefaultEditorCameraPosition() + AZ::Vector3 CameraDefaultEditorPosition() { - float xPosition = aznumeric_cast(GetRegistry(CameraDefaultStartingPositionX, 0.0)); - float yPosition = aznumeric_cast(GetRegistry(CameraDefaultStartingPositionY, -10.0)); - float zPosition = aznumeric_cast(GetRegistry(CameraDefaultStartingPositionZ, 4.0)); - return AZ::Vector3(xPosition, yPosition, zPosition); + return AZ::Vector3( + aznumeric_cast(GetRegistry(CameraDefaultStartingPositionX, 0.0)), + aznumeric_cast(GetRegistry(CameraDefaultStartingPositionY, -10.0)), + aznumeric_cast(GetRegistry(CameraDefaultStartingPositionZ, 4.0))); } - void SetDefaultCameraEditorPosition(const AZ::Vector3 defaultCameraPosition) + void SetCameraDefaultEditorPosition(const AZ::Vector3& defaultCameraPosition) { SetRegistry(CameraDefaultStartingPositionX, defaultCameraPosition.GetX()); SetRegistry(CameraDefaultStartingPositionY, defaultCameraPosition.GetY()); @@ -359,6 +360,16 @@ namespace SandboxEditor SetRegistry(CameraCaptureCursorLookSetting, capture); } + float CameraDefaultOrbitDistance() + { + return aznumeric_cast(GetRegistry(CameraDefaultOrbitDistanceSetting, 20.0)); + } + + void SetCameraDefaultOrbitDistance(const float distance) + { + SetRegistry(CameraDefaultOrbitDistanceSetting, distance); + } + AzFramework::InputChannelId CameraTranslateForwardChannelId() { return AzFramework::InputChannelId( diff --git a/Code/Editor/EditorViewportSettings.h b/Code/Editor/EditorViewportSettings.h index 9c3f0a46e5..fe1253ed0c 100644 --- a/Code/Editor/EditorViewportSettings.h +++ b/Code/Editor/EditorViewportSettings.h @@ -33,9 +33,6 @@ namespace SandboxEditor //! event will fire when a value in the settings registry (editorpreferences.setreg) is modified. SANDBOX_API AZStd::unique_ptr CreateEditorViewportSettingsCallbacks(); - SANDBOX_API AZ::Vector3 DefaultEditorCameraPosition(); - SANDBOX_API void SetDefaultCameraEditorPosition(AZ::Vector3 defaultCameraPosition); - SANDBOX_API AZ::u64 MaxItemsShownInAssetBrowserSearch(); SANDBOX_API void SetMaxItemsShownInAssetBrowserSearch(AZ::u64 numberOfItemsShown); @@ -105,6 +102,12 @@ namespace SandboxEditor SANDBOX_API bool CameraCaptureCursorForLook(); SANDBOX_API void SetCameraCaptureCursorForLook(bool capture); + SANDBOX_API AZ::Vector3 CameraDefaultEditorPosition(); + SANDBOX_API void SetCameraDefaultEditorPosition(const AZ::Vector3& position); + + SANDBOX_API float CameraDefaultOrbitDistance(); + SANDBOX_API void SetCameraDefaultOrbitDistance(float distance); + SANDBOX_API AzFramework::InputChannelId CameraTranslateForwardChannelId(); SANDBOX_API void SetCameraTranslateForwardChannelId(AZStd::string_view cameraTranslateForwardId); diff --git a/Code/Editor/EditorViewportWidget.cpp b/Code/Editor/EditorViewportWidget.cpp index 8e6983a9d7..290f0fd17f 100644 --- a/Code/Editor/EditorViewportWidget.cpp +++ b/Code/Editor/EditorViewportWidget.cpp @@ -620,9 +620,9 @@ void EditorViewportWidget::OnEditorNotifyEvent(EEditorNotifyEvent event) break; case eNotify_OnEndNewScene: - PopDisableRendering(); - { + PopDisableRendering(); + Matrix34 viewTM; viewTM.SetIdentity(); @@ -638,9 +638,9 @@ void EditorViewportWidget::OnEditorNotifyEvent(EEditorNotifyEvent event) break; case eNotify_OnEndTerrainCreate: - PopDisableRendering(); - { + PopDisableRendering(); + Matrix34 viewTM; viewTM.SetIdentity(); @@ -2021,10 +2021,6 @@ void EditorViewportWidget::SetDefaultCamera() GetViewManager()->SetCameraObjectId(GUID_NULL); SetName(m_defaultViewName); - // Set the default Editor Camera position. - m_defaultViewTM.SetTranslation(Vec3(m_editorViewportSettings.DefaultEditorCameraPosition())); - SetViewTM(m_defaultViewTM); - // Synchronize the configured editor viewport FOV to the default camera if (m_viewPane) { @@ -2041,6 +2037,10 @@ void EditorViewportWidget::SetDefaultCamera() atomViewportRequests->PushView(contextName, m_defaultView); } + // Set the default Editor Camera position. + m_defaultViewTM.SetTranslation(Vec3(m_editorViewportSettings.DefaultEditorCameraPosition())); + SetViewTM(m_defaultViewTM); + PostCameraSet(); } @@ -2527,7 +2527,7 @@ bool EditorViewportSettings::StickySelectEnabled() const AZ::Vector3 EditorViewportSettings::DefaultEditorCameraPosition() const { - return SandboxEditor::DefaultEditorCameraPosition(); + return SandboxEditor::CameraDefaultEditorPosition(); } AZ_CVAR_EXTERNED(bool, ed_previewGameInFullscreen_once); diff --git a/Code/Editor/Include/IFileUtil.h b/Code/Editor/Include/IFileUtil.h index e179f892d9..4d5f6e23c4 100644 --- a/Code/Editor/Include/IFileUtil.h +++ b/Code/Editor/Include/IFileUtil.h @@ -114,9 +114,7 @@ struct IFileUtil 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 void EditTextFile(const char* txtFile, int line = 0, ETextFileType fileType = FILE_TYPE_SCRIPT) = 0; virtual void EditTextureFile(const char* txtureFile, bool bUseGameFolder) = 0; //! dcc filename calculation and extraction sub-routines diff --git a/Code/Editor/Util/FileUtil.cpp b/Code/Editor/Util/FileUtil.cpp index baca69d628..36c1879407 100644 --- a/Code/Editor/Util/FileUtil.cpp +++ b/Code/Editor/Util/FileUtil.cpp @@ -23,9 +23,9 @@ // AzCore #include #include +#include // AzFramework -#include // AzQtComponents #include @@ -62,84 +62,6 @@ CAutoRestorePrimaryCDRoot::~CAutoRestorePrimaryCDRoot() 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) { @@ -205,7 +127,7 @@ void CFileUtil::EditTextFile(const char* txtFile, int line, IFileUtil::ETextFile { QString file = txtFile; - QString fullPathName = Path::GamePathToFullPath(file); + QString fullPathName = Path::GamePathToFullPath(file); ExtractFile(fullPathName); QString cmd(fullPathName); #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) diff --git a/Code/Editor/Util/FileUtil.h b/Code/Editor/Util/FileUtil.h index 5820c32081..fc0fc942fe 100644 --- a/Code/Editor/Util/FileUtil.h +++ b/Code/Editor/Util/FileUtil.h @@ -25,14 +25,9 @@ public: 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 void EditTextFile(const char* txtFile, int line = 0, IFileUtil::ETextFileType fileType = IFileUtil::FILE_TYPE_SCRIPT); 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 static bool CalculateDccFilename(const QString& assetFilename, QString& dccFilename); diff --git a/Code/Editor/Util/FileUtil_impl.cpp b/Code/Editor/Util/FileUtil_impl.cpp index 0dd3a0ca87..28090d28d5 100644 --- a/Code/Editor/Util/FileUtil_impl.cpp +++ b/Code/Editor/Util/FileUtil_impl.cpp @@ -20,21 +20,11 @@ void CFileUtil_impl::ShowInExplorer(const QString& 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) { 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) { CFileUtil::EditTextureFile(txtureFile, bUseGameFolder); diff --git a/Code/Editor/Util/FileUtil_impl.h b/Code/Editor/Util/FileUtil_impl.h index 04d9e829b9..aa8d0bf3b5 100644 --- a/Code/Editor/Util/FileUtil_impl.h +++ b/Code/Editor/Util/FileUtil_impl.h @@ -36,9 +36,7 @@ public: void ShowInExplorer(const QString& path) override; - bool CompileLuaFile(const char* luaFilename) 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; //! dcc filename calculation and extraction sub-routines diff --git a/Code/Editor/Util/PathUtil.cpp b/Code/Editor/Util/PathUtil.cpp index ca3481ddae..8b745ee1bc 100644 --- a/Code/Editor/Util/PathUtil.cpp +++ b/Code/Editor/Util/PathUtil.cpp @@ -14,7 +14,6 @@ #include #include #include // for ebus events -#include #include #include @@ -175,9 +174,8 @@ namespace Path ////////////////////////////////////////////////////////////////////////// QString GetEngineRootPath() { - const char* engineRoot; - EBUS_EVENT_RESULT(engineRoot, AzFramework::ApplicationRequests::Bus, GetEngineRoot); - return QString(engineRoot); + const AZ::IO::FixedMaxPathString engineRoot = AZ::Utils::GetEnginePath(); + return QString::fromUtf8(engineRoot.c_str(), static_cast(engineRoot.size())); } ////////////////////////////////////////////////////////////////////////// diff --git a/Code/Editor/WelcomeScreen/WelcomeScreenDialog.cpp b/Code/Editor/WelcomeScreen/WelcomeScreenDialog.cpp index 17f576b5ee..89dfcaffd1 100644 --- a/Code/Editor/WelcomeScreen/WelcomeScreenDialog.cpp +++ b/Code/Editor/WelcomeScreen/WelcomeScreenDialog.cpp @@ -25,8 +25,6 @@ #include -// AzFramework -#include // AzToolsFramework #include @@ -173,9 +171,6 @@ void WelcomeScreenDialog::SetRecentFileList(RecentFileList* pList) m_pRecentList = pList; - const char* engineRoot; - EBUS_EVENT_RESULT(engineRoot, AzFramework::ApplicationRequests::Bus, GetEngineRoot); - auto projectPath = AZ::Utils::GetProjectPath(); QString gamePath{projectPath.c_str()}; Path::ConvertSlashToBackSlash(gamePath); diff --git a/Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp b/Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp index ad7e44c2ae..bd225cf634 100644 --- a/Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp +++ b/Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp @@ -485,16 +485,6 @@ namespace AZ constexpr bool executeRegDumpCommands = false; 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_CommandLine(*m_settingsRegistry, m_commandLine, executeRegDumpCommands); SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*m_settingsRegistry); @@ -614,7 +604,8 @@ namespace AZ { 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(); return nullptr; @@ -1180,6 +1171,24 @@ namespace AZ return ReflectionEnvironment::GetReflectionManager() ? ReflectionEnvironment::GetReflectionManager()->GetReflectContext() : 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 //========================================================================= @@ -1485,27 +1494,6 @@ namespace AZ } } - //========================================================================= - // CalculateExecutablePath - //========================================================================= - void ComponentApplication::CalculateExecutablePath() - { - m_exeDirectory = Utils::GetExecutableDirectory(); - } - - void ComponentApplication::CalculateAppRoot() - { - if (AZStd::optional 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) { // No special parsing of the Module Path is done by the Component Application anymore diff --git a/Code/Framework/AzCore/AzCore/Component/ComponentApplication.h b/Code/Framework/AzCore/AzCore/Component/ComponentApplication.h index 6df93aff4e..4e551b6c47 100644 --- a/Code/Framework/AzCore/AzCore/Component/ComponentApplication.h +++ b/Code/Framework/AzCore/AzCore/Component/ComponentApplication.h @@ -221,13 +221,10 @@ namespace AZ BehaviorContext* GetBehaviorContext() override; /// Returns the json registration context that has been registered with the app, if there is one. 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. - 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. - const char* GetExecutableFolder() const override { return m_exeDirectory.c_str(); } + const char* GetExecutableFolder() const override; ////////////////////////////////////////////////////////////////////////// /// TickRequestBus @@ -352,15 +349,6 @@ namespace AZ /// Adds system components requested by modules and the application to the system entity. 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 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. IAllocatorAllocate* m_osAllocator{ nullptr }; 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_projectNameChangedHandler; diff --git a/Code/Framework/AzCore/AzCore/Component/ComponentApplicationBus.h b/Code/Framework/AzCore/AzCore/Component/ComponentApplicationBus.h index 0c0977384a..feefa95973 100644 --- a/Code/Framework/AzCore/AzCore/Component/ComponentApplicationBus.h +++ b/Code/Framework/AzCore/AzCore/Component/ComponentApplicationBus.h @@ -175,10 +175,6 @@ namespace AZ //! the serializers used by the best-effort json serialization. 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. //! @return a pointer to the engine path. virtual const char* GetEngineRoot() const = 0; diff --git a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp index 3668ab14fd..975217a131 100644 --- a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp +++ b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp @@ -266,7 +266,8 @@ namespace AZ::SettingsRegistryMergeUtils // 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 - AZ::IO::FixedMaxPath projectRoot = FindProjectRoot(settingsRegistry); + AZ::IO::FixedMaxPath projectRoot; + settingsRegistry.Get(projectRoot.Native(), FilePathKey_ProjectPath); if (projectRoot.empty()) { return {}; @@ -668,7 +669,7 @@ namespace AZ::SettingsRegistryMergeUtils // NOTE: We make the project-path in the BootstrapSettingsRootKey absolute first 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()) { if (projectPath.IsRelative()) @@ -693,6 +694,7 @@ namespace AZ::SettingsRegistryMergeUtils R"(Project path isn't set in the Settings Registry at "%.*s".)" " Project-related filepaths will be set relative to the executable directory\n", AZ_STRING_ARG(projectPathKey)); + projectPath = exePath; registry.Set(FilePathKey_ProjectPath, exePath.Native()); } diff --git a/Code/Framework/AzCore/AzCore/StringFunc/StringFunc.cpp b/Code/Framework/AzCore/AzCore/StringFunc/StringFunc.cpp index ff30291a70..a953d53c33 100644 --- a/Code/Framework/AzCore/AzCore/StringFunc/StringFunc.cpp +++ b/Code/Framework/AzCore/AzCore/StringFunc/StringFunc.cpp @@ -1427,26 +1427,36 @@ namespace AZ namespace AssetPath { - void CalculateBranchToken(const AZStd::string& appRootPath, AZStd::string& token) + namespace Internal { - // Normalize the token to prepare for CRC32 calculation - AZStd::string normalized = appRootPath; + AZ::u32 CalculateBranchTokenHash(AZStd::string_view engineRootPath) + { + // Normalize the token to prepare for CRC32 calculation + auto NormalizeEnginePath = [](const char element) -> char + { + // Substitute path separators with '_' and lower case + return element == AZ::IO::WindowsPathSeparator || element == AZ::IO::PosixPathSeparator + ? '_' : static_cast(std::tolower(element)); + }; - // Strip out any trailing path separators - AZ::StringFunc::Strip(normalized, AZ_CORRECT_FILESYSTEM_SEPARATOR_STRING AZ_WRONG_FILESYSTEM_SEPARATOR_STRING,false, false, true); + // Trim off trailing path separators + engineRootPath = RStrip(engineRootPath, AZ_CORRECT_AND_WRONG_FILESYSTEM_SEPARATOR); + AZ::IO::FixedMaxPathString enginePath; + AZStd::transform(engineRootPath.begin(), engineRootPath.end(), + AZStd::back_inserter(enginePath), AZStd::move(NormalizeEnginePath)); - // Lower case always - AZStd::to_lower(normalized.begin(), normalized.end()); - - // Substitute path separators with '_' - AZStd::replace(normalized.begin(), normalized.end(), '\\', '_'); - AZStd::replace(normalized.begin(), normalized.end(), '/', '_'); - - // Perform the CRC32 calculation - const AZ::Crc32 branchTokenCrc(normalized.c_str(), normalized.size(), true); - char branchToken[12]; - azsnprintf(branchToken, AZ_ARRAY_SIZE(branchToken), "0x%08X", static_cast(branchTokenCrc)); - token = AZStd::string(branchToken); + // Perform the CRC32 calculation + constexpr bool forceLowercase = true; + return static_cast(AZ::Crc32(enginePath.c_str(), enginePath.size(), forceLowercase)); + } + } + 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)); } } diff --git a/Code/Framework/AzCore/AzCore/StringFunc/StringFunc.h b/Code/Framework/AzCore/AzCore/StringFunc/StringFunc.h index 1e651afc93..55236a0fff 100644 --- a/Code/Framework/AzCore/AzCore/StringFunc/StringFunc.h +++ b/Code/Framework/AzCore/AzCore/StringFunc/StringFunc.h @@ -485,10 +485,11 @@ namespace AZ //! CalculateBranchToken /*! 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 */ - 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); } ////////////////////////////////////////////////////////////////////////// diff --git a/Code/Framework/AzCore/AzCore/UnitTest/MockComponentApplication.h b/Code/Framework/AzCore/AzCore/UnitTest/MockComponentApplication.h index 8f069da9dd..190fa09cb7 100644 --- a/Code/Framework/AzCore/AzCore/UnitTest/MockComponentApplication.h +++ b/Code/Framework/AzCore/AzCore/UnitTest/MockComponentApplication.h @@ -41,7 +41,6 @@ namespace UnitTest MOCK_METHOD0(GetSerializeContext, AZ::SerializeContext* ()); MOCK_METHOD0(GetJsonRegistrationContext, AZ::JsonRegistrationContext* ()); MOCK_METHOD0(GetBehaviorContext, AZ::BehaviorContext* ()); - MOCK_CONST_METHOD0(GetAppRoot, const char* ()); MOCK_CONST_METHOD0(GetEngineRoot, const char* ()); MOCK_CONST_METHOD0(GetExecutableFolder, const char* ()); MOCK_CONST_METHOD1(QueryApplicationType, void(AZ::ApplicationTypeQuery&)); diff --git a/Code/Framework/AzCore/Tests/BehaviorContextFixture.h b/Code/Framework/AzCore/Tests/BehaviorContextFixture.h index 1895f2e35b..3c6d48add7 100644 --- a/Code/Framework/AzCore/Tests/BehaviorContextFixture.h +++ b/Code/Framework/AzCore/Tests/BehaviorContextFixture.h @@ -59,7 +59,6 @@ namespace UnitTest AZ::SerializeContext* GetSerializeContext() override { return nullptr; } AZ::BehaviorContext* GetBehaviorContext() override { return m_behaviorContext; } AZ::JsonRegistrationContext* GetJsonRegistrationContext() override { return nullptr; } - const char* GetAppRoot() const override { return nullptr; } const char* GetEngineRoot() const override { return nullptr; } const char* GetExecutableFolder() const override { return nullptr; } void EnumerateEntities(const EntityCallback& /*callback*/) override {} diff --git a/Code/Framework/AzCore/Tests/Components.cpp b/Code/Framework/AzCore/Tests/Components.cpp index 55b1c193b3..013d998c52 100644 --- a/Code/Framework/AzCore/Tests/Components.cpp +++ b/Code/Framework/AzCore/Tests/Components.cpp @@ -1060,26 +1060,21 @@ namespace UnitTest /** * UserSettingsComponent test */ - class UserSettingsTestApp - : public ComponentApplication - , public UserSettingsFileLocatorBus::Handler - { - public: - void SetExecutableFolder(const char* path) - { - m_exeDirectory = path; - } - + class UserSettingsTestApp + : public ComponentApplication + , public UserSettingsFileLocatorBus::Handler + { + public: AZStd::string ResolveFilePath(u32 providerId) override { AZStd::string filePath; 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) { - filePath = (m_exeDirectory / "LocalUserSettings.xml").String(); + filePath = (AZ::IO::Path(GetTestFolderPath()) / "LocalUserSettings.xml").Native(); } return filePath; } @@ -1117,7 +1112,6 @@ namespace UnitTest ComponentApplication::Descriptor appDesc; appDesc.m_memoryBlocksByteSize = 10 * 1024 * 1024; Entity* systemEntity = app.Create(appDesc); - app.SetExecutableFolder(GetTestFolderPath().c_str()); app.UserSettingsFileLocatorBus::Handler::BusConnect(); // Make sure user settings file does not exist at this point diff --git a/Code/Framework/AzCore/Tests/Serialization.cpp b/Code/Framework/AzCore/Tests/Serialization.cpp index f1d5edc490..219744a480 100644 --- a/Code/Framework/AzCore/Tests/Serialization.cpp +++ b/Code/Framework/AzCore/Tests/Serialization.cpp @@ -1240,7 +1240,6 @@ namespace UnitTest SerializeContext* GetSerializeContext() override { return m_serializeContext.get(); } BehaviorContext* GetBehaviorContext() override { return nullptr; } JsonRegistrationContext* GetJsonRegistrationContext() override { return nullptr; } - const char* GetAppRoot() const override { return nullptr; } const char* GetEngineRoot() const override { return nullptr; } const char* GetExecutableFolder() const override { return nullptr; } void EnumerateEntities(const EntityCallback& /*callback*/) override {} diff --git a/Code/Framework/AzFramework/AzFramework/API/ApplicationAPI.h b/Code/Framework/AzFramework/AzFramework/API/ApplicationAPI.h index 1c5db0a82b..e536082d61 100644 --- a/Code/Framework/AzFramework/AzFramework/API/ApplicationAPI.h +++ b/Code/Framework/AzFramework/AzFramework/API/ApplicationAPI.h @@ -67,12 +67,6 @@ namespace AzFramework /// Make path relative to the provided root. 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. virtual const CommandLine* GetCommandLine() { return nullptr; } diff --git a/Code/Framework/AzFramework/AzFramework/Application/Application.cpp b/Code/Framework/AzFramework/AzFramework/Application/Application.cpp index 323e834413..768ef1f0b6 100644 --- a/Code/Framework/AzFramework/AzFramework/Application/Application.cpp +++ b/Code/Framework/AzFramework/AzFramework/Application/Application.cpp @@ -69,6 +69,7 @@ #include #include #include +#include #include "Application.h" #include @@ -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() { if (m_isStarted) @@ -318,6 +312,8 @@ namespace AzFramework AzFramework::SurfaceData::SurfaceTagWeight::Reflect(context); AzFramework::SurfaceData::SurfacePoint::Reflect(context); AzFramework::Terrain::TerrainDataRequests::Reflect(context); + Physics::HeightfieldProviderRequests::Reflect(context); + Physics::HeightMaterialPoint::Reflect(context); if (AZ::SerializeContext* serializeContext = azrtti_cast(context)) { @@ -394,11 +390,6 @@ namespace AzFramework outModules.emplace_back(aznew AzFrameworkModule()); } - const char* Application::GetAppRoot() const - { - return m_appRoot.c_str(); - } - const char* Application::GetCurrentConfigurationName() const { #if defined(_RELEASE) @@ -434,19 +425,19 @@ namespace AzFramework void Application::ResolveEnginePath(AZStd::string& engineRelativePath) const { - AZ::IO::FixedMaxPath fullPath = m_engineRoot / engineRelativePath; + auto fullPath = AZ::IO::FixedMaxPath(GetEngineRoot()) / engineRelativePath; engineRelativePath = fullPath.String(); } 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) { - 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(type)); - } - } - struct DeprecatedAliasesKeyVisitor : AZ::SettingsRegistryInterface::Visitor { diff --git a/Code/Framework/AzFramework/AzFramework/Application/Application.h b/Code/Framework/AzFramework/AzFramework/Application/Application.h index c6b1dfeaae..a318ede4a2 100644 --- a/Code/Framework/AzFramework/AzFramework/Application/Application.h +++ b/Code/Framework/AzFramework/AzFramework/Application/Application.h @@ -95,8 +95,6 @@ namespace AzFramework ////////////////////////////////////////////////////////////////////////// //! 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 CalculateBranchTokenForEngineRoot(AZStd::string& token) const override; bool IsPrefabSystemEnabled() const override; @@ -146,8 +144,6 @@ namespace AzFramework */ void SetFileIOAliases(); - void PreModuleLoad() override; - ////////////////////////////////////////////////////////////////////////// //! AZ::ComponentApplication void RegisterCoreComponents() override; @@ -181,13 +177,7 @@ namespace AzFramework bool m_ownsConsole = false; bool m_exitMainLoopRequested = false; - - enum class RootPathType - { - AppRoot, - EngineRoot - }; - void SetRootPath(RootPathType type, const char* source); + }; } // namespace AzFramework diff --git a/Code/Framework/AzFramework/AzFramework/Physics/HeightfieldProviderBus.cpp b/Code/Framework/AzFramework/AzFramework/Physics/HeightfieldProviderBus.cpp new file mode 100644 index 0000000000..38fac9655c --- /dev/null +++ b/Code/Framework/AzFramework/AzFramework/Physics/HeightfieldProviderBus.cpp @@ -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 +#include +#include + +namespace Physics +{ + void HeightfieldProviderRequests::Reflect(AZ::ReflectContext* context) + { + if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) + { + behaviorContext->EBus("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(context)) + { + behaviorContext->Class()->Attribute(AZ::Script::Attributes::Category, "Physics"); + } + } + +} // namespace Physics diff --git a/Code/Framework/AzFramework/AzFramework/Physics/HeightfieldProviderBus.h b/Code/Framework/AzFramework/AzFramework/Physics/HeightfieldProviderBus.h index 73523ee1ba..da361f0a2b 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/HeightfieldProviderBus.h +++ b/Code/Framework/AzFramework/AzFramework/Physics/HeightfieldProviderBus.h @@ -26,10 +26,25 @@ namespace Physics 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. 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. uint16_t m_padding{ 0 }; //!< available for future use. + }; //! An interface to provide heightfield values. @@ -37,6 +52,8 @@ namespace Physics : public AZ::ComponentBus { public: + static void Reflect(AZ::ReflectContext* context); + //! Returns the distance between each height in the map. //! @return Vector containing Column Spacing, Rows Spacing. virtual AZ::Vector2 GetHeightfieldGridSpacing() const = 0; @@ -46,11 +63,27 @@ namespace Physics //! @param numRows contains the size of the grid in the y direction. 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. //! @param minHeightBounds contains the minimum 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; + //! 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. //! This is provided separately from the shape AABB because the heightfield might choose to modify the AABB bounds. //! @return AABB of the heightfield. diff --git a/Code/Framework/AzFramework/AzFramework/Physics/Material.cpp b/Code/Framework/AzFramework/AzFramework/Physics/Material.cpp index 222bc48dda..d78d4e0943 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/Material.cpp +++ b/Code/Framework/AzFramework/AzFramework/Physics/Material.cpp @@ -360,6 +360,11 @@ namespace Physics ->Field("MaterialId", &Physics::MaterialId::m_id) ; } + + if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) + { + behaviorContext->Class()->Attribute(AZ::Script::Attributes::Category, "Physics"); + } } MaterialId MaterialId::Create() diff --git a/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.cpp b/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.cpp index ddee63e191..26c9db64aa 100644 --- a/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.cpp +++ b/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.cpp @@ -806,12 +806,20 @@ namespace AzFramework [[maybe_unused]] float scrollDelta, [[maybe_unused]] float deltaTime) { + const auto pivot = m_pivotFn(); + + if (!pivot.has_value()) + { + EndActivation(); + return targetCamera; + } + if (Beginning()) { // as the camera starts, record the camera we would like to end up as - m_nextCamera.m_offset = m_offsetFn(m_pivotFn().GetDistance(targetCamera.Translation())); + m_nextCamera.m_offset = m_offsetFn(pivot.value().GetDistance(targetCamera.Translation())); const auto angles = - EulerAngles(AZ::Matrix3x3::CreateFromMatrix3x4(AZ::Matrix3x4::CreateLookAt(targetCamera.Translation(), m_pivotFn()))); + EulerAngles(AZ::Matrix3x3::CreateFromMatrix3x4(AZ::Matrix3x4::CreateLookAt(targetCamera.Translation(), pivot.value()))); m_nextCamera.m_pitch = angles.GetX(); m_nextCamera.m_yaw = angles.GetZ(); m_nextCamera.m_pivot = targetCamera.m_pivot; diff --git a/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.h b/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.h index 2b7cc3ea9e..2d02bb0b6d 100644 --- a/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.h +++ b/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.h @@ -651,7 +651,7 @@ namespace AzFramework class FocusCameraInput : public CameraInput { public: - using PivotFn = AZStd::function; + using PivotFn = AZStd::function()>; FocusCameraInput(const InputChannelId& focusChannelId, FocusOffsetFn offsetFn); diff --git a/Code/Framework/AzFramework/AzFramework/azframework_files.cmake b/Code/Framework/AzFramework/AzFramework/azframework_files.cmake index e03d166cfc..13d9003031 100644 --- a/Code/Framework/AzFramework/AzFramework/azframework_files.cmake +++ b/Code/Framework/AzFramework/AzFramework/azframework_files.cmake @@ -229,6 +229,7 @@ set(FILES Physics/Configuration/SystemConfiguration.h Physics/Configuration/SystemConfiguration.cpp Physics/HeightfieldProviderBus.h + Physics/HeightfieldProviderBus.cpp Physics/SimulatedBodies/RigidBody.h Physics/SimulatedBodies/RigidBody.cpp Physics/SimulatedBodies/StaticRigidBody.h diff --git a/Code/Framework/AzFramework/Platform/Mac/AzFramework/Asset/AssetSystemComponentHelper_Mac.cpp b/Code/Framework/AzFramework/Platform/Mac/AzFramework/Asset/AssetSystemComponentHelper_Mac.cpp index c6f481b65b..a3381dabb8 100644 --- a/Code/Framework/AzFramework/Platform/Mac/AzFramework/Asset/AssetSystemComponentHelper_Mac.cpp +++ b/Code/Framework/AzFramework/Platform/Mac/AzFramework/Asset/AssetSystemComponentHelper_Mac.cpp @@ -10,6 +10,8 @@ #include #include +#include + #include #include @@ -24,14 +26,20 @@ namespace AzFramework::AssetSystem::Platform AZ::IO::FixedMaxPath assetProcessorPath{ executableDirectory }; // In Mac the Editor and game is within a bundle, so the path to the sibling app // has to go up from the Contents/MacOS folder the binary is in - assetProcessorPath /= "../../../AssetProcessor.app"; + assetProcessorPath /= "../../../AssetProcessor.app/Contents/MacOS/AssetProcessor"; assetProcessorPath = assetProcessorPath.LexicallyNormal(); if (!AZ::IO::SystemFile::Exists(assetProcessorPath.c_str())) { - // Check for existence of one under a "bin" directory, i.e. engineRoot is an SDK structure. - assetProcessorPath = - AZ::IO::FixedMaxPath{engineRoot} / "bin" / AZ_TRAIT_OS_PLATFORM_NAME / AZ_BUILD_CONFIGURATION_TYPE / "AssetProcessor.app"; + if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr) + { + if (AZ::IO::FixedMaxPath installedBinariesPath; + settingsRegistry->Get(installedBinariesPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_InstalledBinaryFolder)) + { + // Check for existence of one under a "bin" directory, i.e. engineRoot is an SDK structure. + assetProcessorPath = AZ::IO::FixedMaxPath{ engineRoot } / installedBinariesPath / "AssetProcessor.app/Contents/MacOS/AssetProcessor"; + } + } if (!AZ::IO::SystemFile::Exists(assetProcessorPath.c_str())) { @@ -39,23 +47,21 @@ namespace AzFramework::AssetSystem::Platform } } - auto fullLaunchCommand = AZ::IO::FixedMaxPathString::format(R"(open -g "%s" --args --start-hidden)", assetProcessorPath.c_str()); + AZStd::string commandLineParams; // Add the engine path to the launch command if not empty if (!engineRoot.empty()) { - fullLaunchCommand += R"( --engine-path=")"; - fullLaunchCommand += engineRoot; - fullLaunchCommand += '"'; + commandLineParams += AZStd::string::format("\"--engine-path=\"%s\"\"", engineRoot.data()); } - // Add the active project path to the launch command if not empty if (!projectPath.empty()) { - fullLaunchCommand += R"( --project-path=")"; - fullLaunchCommand += projectPath; - fullLaunchCommand += '"'; + commandLineParams += AZStd::string::format(" \"--regset=/Amazon/AzCore/Bootstrap/project_path=\"%s\"\"", projectPath.data()); } - return system(fullLaunchCommand.c_str()) == 0; + AzFramework::ProcessLauncher::ProcessLaunchInfo processLaunchInfo; + processLaunchInfo.m_processExecutableString = AZStd::move(assetProcessorPath.Native()); + processLaunchInfo.m_commandlineParameters = commandLineParams; + return AzFramework::ProcessLauncher::LaunchUnwatchedProcess(processLaunchInfo); } } diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotification.cpp b/Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotification.cpp index 8831bef89c..11fd1e60d8 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotification.cpp +++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotification.cpp @@ -27,7 +27,6 @@ namespace AzQtComponents setProperty("HasNoWindowDecorations", true); setAttribute(Qt::WA_ShowWithoutActivating); - setAttribute(Qt::WA_DeleteOnClose); m_borderRadius = toastConfiguration.m_borderRadius; if (m_borderRadius > 0) diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotification.h b/Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotification.h index 4343f37df4..81b1cb4055 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotification.h +++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotification.h @@ -31,7 +31,6 @@ namespace AzQtComponents { Q_OBJECT public: - AZ_CLASS_ALLOCATOR(ToastNotification, AZ::SystemAllocator, 0); ToastNotification(QWidget* parent, const ToastConfiguration& toastConfiguration); virtual ~ToastNotification(); @@ -73,7 +72,7 @@ namespace AzQtComponents AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING AZStd::chrono::milliseconds m_fadeDuration; - AZStd::unique_ptr m_ui; + QScopedPointer m_ui; AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING }; } // namespace AzQtComponents diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotificationConfiguration.h b/Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotificationConfiguration.h index 5ace9d1be2..2db374640e 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotificationConfiguration.h +++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotificationConfiguration.h @@ -27,7 +27,6 @@ namespace AzQtComponents class AZ_QT_COMPONENTS_API ToastConfiguration { public: - AZ_CLASS_ALLOCATOR(ToastConfiguration, AZ::SystemAllocator, 0); ToastConfiguration(ToastType toastType, const QString& title, const QString& description); bool m_closeOnClick = true; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Asset/AssetUtils.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Asset/AssetUtils.cpp index 139bbb563c..6fd881dbb9 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Asset/AssetUtils.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Asset/AssetUtils.cpp @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include #include #include @@ -205,7 +205,7 @@ namespace AzToolsFramework::AssetUtils return platformConfigFilePathsAdded; } - AZStd::vector GetConfigFiles(AZStd::string_view engineRoot, AZStd::string_view assetRoot, AZStd::string_view projectPath, + AZStd::vector GetConfigFiles(AZStd::string_view engineRoot, AZStd::string_view projectPath, bool addPlatformConfigs, bool addGemsConfigs, AZ::SettingsRegistryInterface* settingsRegistry) { constexpr const char* AssetProcessorGamePlatformConfigFileName = "AssetProcessorGamePlatformConfig.ini"; @@ -232,14 +232,13 @@ namespace AzToolsFramework::AssetUtils Internal::AddGemConfigFiles(gemInfoList, configFiles); } - AZ::IO::Path assetRootDir(assetRoot); - assetRootDir /= projectPath; + AZ::IO::Path projectRoot(projectPath); - AZ::IO::Path projectConfigFile = assetRootDir / AssetProcessorGamePlatformConfigFileName; + AZ::IO::Path projectConfigFile = projectRoot / AssetProcessorGamePlatformConfigFileName; configFiles.push_back(projectConfigFile); // Add a file entry for the Project AssetProcessor setreg file - projectConfigFile = assetRootDir / AssetProcessorGamePlatformConfigSetreg; + projectConfigFile = projectRoot / AssetProcessorGamePlatformConfigSetreg; configFiles.push_back(projectConfigFile); return configFiles; @@ -251,10 +250,10 @@ namespace AzToolsFramework::AssetUtils AZStd::vector tokens; AZ::StringFunc::Tokenize(relPathFromRoot.c_str(), tokens, AZ_CORRECT_FILESYSTEM_SEPARATOR_STRING); - AZStd::string validatedPath; + AZ::IO::FixedMaxPath validatedPath; if (rootPath.empty()) { - AzFramework::ApplicationRequests::Bus::BroadcastResult(validatedPath, &AzFramework::ApplicationRequests::GetEngineRoot); + validatedPath = AZ::Utils::GetEnginePath(); } else { @@ -299,10 +298,7 @@ namespace AzToolsFramework::AssetUtils break; } - AZStd::string absoluteFilePath; - AZ::StringFunc::Path::ConstructFull(validatedPath.c_str(), element.c_str(), absoluteFilePath); - - validatedPath = absoluteFilePath; // go one step deeper. + validatedPath /= element; // go one step deeper. } if (success) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Asset/AssetUtils.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Asset/AssetUtils.h index 39aab4d3fb..31ee9dcf60 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Asset/AssetUtils.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Asset/AssetUtils.h @@ -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, //! 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. - AZStd::vector GetConfigFiles(AZStd::string_view engineRoot, AZStd::string_view assetRoot, AZStd::string_view projectPath, + AZStd::vector GetConfigFiles(AZStd::string_view engineRoot, AZStd::string_view projectPath, 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. diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h index 4048156b60..ee1ec49456 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h @@ -40,9 +40,9 @@ namespace AzToolsFramework QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; QModelIndex parent(const QModelIndex& child) const override; QModelIndex sibling(int row, int column, const QModelIndex& idx) const override; + int rowCount(const QModelIndex& parent = QModelIndex()) const override; protected: - int rowCount(const QModelIndex& parent = QModelIndex()) const override; QVariant headerData(int section, Qt::Orientation orientation, int role /* = Qt::DisplayRole */) const override; //////////////////////////////////////////////////////////////////// @@ -55,7 +55,7 @@ namespace AzToolsFramework private slots: void SourceDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight); private: - AZ::u64 m_numberOfItemsDisplayed = 0; + AZ::u64 m_numberOfItemsDisplayed = 50; int m_displayedItemsCounter = 0; QPointer m_filterModel; QMap m_indexMap; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Thumbnails/SourceThumbnail.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Thumbnails/SourceThumbnail.cpp index a5206c9608..4869e0d09f 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Thumbnails/SourceThumbnail.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Thumbnails/SourceThumbnail.cpp @@ -9,11 +9,11 @@ #include #include #include +#include #include #include #include #include -#include #include namespace AzToolsFramework @@ -113,11 +113,9 @@ namespace AzToolsFramework if (iconPathToUse.isEmpty()) { - const char* engineRoot = nullptr; - AzFramework::ApplicationRequests::Bus::BroadcastResult(engineRoot, &AzFramework::ApplicationRequests::GetEngineRoot); - AZ_Assert(engineRoot, "Engine Root not initialized"); - AZStd::string iconPath = AZStd::string::format("%s%s", engineRoot, DefaultFileIconPath); - iconPathToUse = iconPath.c_str(); + AZ::IO::FixedMaxPath engineRoot = AZ::Utils::GetEnginePath(); + AZ_Assert(!engineRoot.empty(), "Engine Root not initialized"); + iconPathToUse = (engineRoot / DefaultFileIconPath).c_str(); } m_pixmap.load(iconPathToUse); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityHelpers.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityHelpers.cpp index 28d6e2bc08..3e256430a2 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityHelpers.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityHelpers.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -468,6 +469,18 @@ namespace AzToolsFramework EntityIdList children; 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; AZ::EntityId sortEntityId = GetEntityIdForSortInfo(parentId); EditorEntitySortRequestBus::EventResult(entityChildOrder, sortEntityId, &EditorEntitySortRequestBus::Events::GetChildEntityOrderArray); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntitySortComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntitySortComponent.cpp index b747469f4d..6936397187 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntitySortComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntitySortComponent.cpp @@ -11,6 +11,8 @@ #include #include #include +#include +#include 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) { AZ_PROFILE_FUNCTION(AzToolsFramework); + + if (m_ignoreIncomingOrderChanges) + { + return true; + } + auto entityItr = m_childEntityOrderCache.find(entityId); if (entityItr == m_childEntityOrderCache.end()) { @@ -198,6 +206,12 @@ namespace AzToolsFramework bool EditorEntitySortComponent::RemoveChildEntity(const AZ::EntityId& entityId) { AZ_PROFILE_FUNCTION(AzToolsFramework); + + if (m_ignoreIncomingOrderChanges) + { + return true; + } + auto entityItr = m_childEntityOrderCache.find(entityId); 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() { // 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 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); } @@ -264,10 +297,20 @@ namespace AzToolsFramework // This is a special case for certain EditorComponents only! EditorEntitySortRequestBus::Handler::BusConnect(GetEntityId()); EditorEntityContextNotificationBus::Handler::BusConnect(); + AzToolsFramework::Prefab::PrefabPublicNotificationBus::Handler::BusConnect(); } 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 EditorEntitySortNotificationBus::Event(GetEntityId(), &EditorEntitySortNotificationBus::Events::ChildEntityOrderArrayUpdated); } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntitySortComponent.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntitySortComponent.h index d728191c64..806e903c96 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntitySortComponent.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntitySortComponent.h @@ -10,6 +10,7 @@ #include "EditorEntitySortBus.h" #include #include +#include #include namespace AzToolsFramework @@ -20,6 +21,7 @@ namespace AzToolsFramework : public AzToolsFramework::Components::EditorComponentBase , public EditorEntitySortRequestBus::Handler , public EditorEntityContextNotificationBus::Handler + , public AzToolsFramework::Prefab::PrefabPublicNotificationBus::Handler { public: AZ_COMPONENT(EditorEntitySortComponent, "{6EA1E03D-68B2-466D-97F7-83998C8C27F0}", EditorComponentBase); @@ -45,6 +47,9 @@ namespace AzToolsFramework // EditorEntityContextNotificationBus::Handler void OnEntityStreamLoadSuccess() override; ////////////////////////////////////////////////////////////////////////// + + void OnPrefabInstancePropagationBegin() override; + void OnPrefabInstancePropagationEnd() override; private: void MarkDirtyAndSendChangedEvent(); 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 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 diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp index 5ac9f772dc..32600853fd 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp @@ -1144,6 +1144,10 @@ namespace AzToolsFramework AZ::EntityId firstEntityIdToDelete = entityIdsNoFocusContainer[0]; 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 // cannot delete an instance from itself. diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Thumbnails/SourceControlThumbnail.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Thumbnails/SourceControlThumbnail.cpp index 655fd27a0f..c2636d6d52 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Thumbnails/SourceControlThumbnail.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Thumbnails/SourceControlThumbnail.cpp @@ -6,10 +6,10 @@ * */ -#include +#include +#include #include #include -#include namespace AzToolsFramework { @@ -68,12 +68,12 @@ namespace AzToolsFramework SourceControlThumbnail::SourceControlThumbnail(SharedThumbnailKey key) : Thumbnail(key) { - const char* engineRoot = nullptr; - AzFramework::ApplicationRequests::Bus::BroadcastResult(engineRoot, &AzFramework::ApplicationRequests::GetEngineRoot); - AZ_Assert(engineRoot, "Engine Root not initialized"); + AZ::IO::FixedMaxPath engineRoot = AZ::Utils::GetEnginePath(); + AZ_Assert(!engineRoot.empty(), "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(); } @@ -90,8 +90,8 @@ namespace AzToolsFramework AZ_Assert(sourceControlKey, "Incorrect key type, excpected SourceControlThumbnailKey"); AZStd::string myFileName(sourceControlKey->GetFileName()); - AzFramework::StringFunc::Path::Normalize(myFileName); - if (AzFramework::StringFunc::Equal(myFileName.c_str(), filename)) + AZ::StringFunc::Path::Normalize(myFileName); + if (AZ::StringFunc::Equal(myFileName.c_str(), filename)) { Update(); } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Notifications/ToastNotificationsView.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Notifications/ToastNotificationsView.cpp index a88711a9ed..277d3fa3c5 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Notifications/ToastNotificationsView.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Notifications/ToastNotificationsView.cpp @@ -129,7 +129,7 @@ namespace AzToolsFramework ToastId ToastNotificationsView::CreateToastNotification(const AzQtComponents::ToastConfiguration& toastConfiguration) { - AzQtComponents::ToastNotification* notification = aznew AzQtComponents::ToastNotification(parentWidget(), toastConfiguration); + AzQtComponents::ToastNotification* notification = new AzQtComponents::ToastNotification(this, toastConfiguration); ToastId toastId = AZ::Entity::MakeId(); m_notifications[toastId] = notification; diff --git a/Code/Framework/AzToolsFramework/Tests/ComponentAddRemove.cpp b/Code/Framework/AzToolsFramework/Tests/ComponentAddRemove.cpp index 9d6c8a4bff..06353b17c5 100644 --- a/Code/Framework/AzToolsFramework/Tests/ComponentAddRemove.cpp +++ b/Code/Framework/AzToolsFramework/Tests/ComponentAddRemove.cpp @@ -1116,7 +1116,6 @@ namespace UnitTest SerializeContext* GetSerializeContext() override { return m_serializeContext.get(); } BehaviorContext* GetBehaviorContext() override { return nullptr; } JsonRegistrationContext* GetJsonRegistrationContext() override { return nullptr; } - const char* GetAppRoot() const override { return nullptr; } const char* GetEngineRoot() const override { return nullptr; } const char* GetExecutableFolder() const override { return nullptr; } void EnumerateEntities(const EntityCallback& /*callback*/) override {} diff --git a/Code/Framework/AzToolsFramework/Tests/UI/AssetBrowserTests.cpp b/Code/Framework/AzToolsFramework/Tests/UI/AssetBrowserTests.cpp new file mode 100644 index 0000000000..ae5ea5f0ae --- /dev/null +++ b/Code/Framework/AzToolsFramework/Tests/UI/AssetBrowserTests.cpp @@ -0,0 +1,344 @@ +/* + * 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace UnitTest +{ + // Test fixture for the AssetBrowser model that uses a QAbstractItemModelTester to validate the state of the model + // when QAbstractItemModel signals fire. Tests will exit with a fatal error if an invalid state is detected. + class AssetBrowserTest + : public ToolsApplicationFixture + , public testing::WithParamInterface + { + protected: + enum class AssetEntryType + { + Root, + Folder, + Source, + Product + }; + + enum class FolderType + { + Root, + File + }; + + void SetUpEditorFixtureImpl() override; + void TearDownEditorFixtureImpl() override; + + //! Creates a Mock Scan Folder + void AddScanFolder(AZ::s64 folderID, AZStd::string folderPath, AZStd::string displayName, FolderType folderType = FolderType::File); + + //! Creates a Source entry from a mock file + AZ::Uuid CreateSourceEntry( + AZ::s64 fileID, AZ::s64 parentFolderID, AZStd::string filename, AssetEntryType sourceType = AssetEntryType::Source); + + //! Creates a product from a given sourceEntry + void CreateProduct(AZ::s64 productID, AZ::Uuid sourceUuid, AZStd::string productName); + + void SetupAssetBrowser(); + void PrintModel(const QAbstractItemModel* model, AZStd::function printer); + QModelIndex GetModelIndex(const QAbstractItemModel* model, int targetDepth, int row = 0); + AZStd::shared_ptr GetRootEntry(); + AZStd::vector GetVectorFromFormattedString(const QString& formattedString); + + protected: + QString m_assetBrowserHierarchy = QString(); + + AZStd::unique_ptr m_searchWidget; + AZStd::unique_ptr m_assetBrowserComponent; + + AZStd::unique_ptr m_filterModel; + AZStd::unique_ptr m_tableModel; + + AZStd::unique_ptr m_modelTesterAssetBrowser; + AZStd::unique_ptr m_modelTesterFilterModel; + AZStd::unique_ptr m_modelTesterTableModel; + + QVector m_folderIds = { 13, 14, 15 }; + QVector m_sourceIDs = { 1, 2, 3, 4, 5 }; + QVector m_productIDs = { 1, 2, 3, 4, 5 }; + }; + + void AssetBrowserTest::SetUpEditorFixtureImpl() + { + GetApplication()->RegisterComponentDescriptor(AzToolsFramework::EditorEntityContextComponent::CreateDescriptor()); + + m_assetBrowserComponent = AZStd::make_unique(); + m_assetBrowserComponent->Activate(); + + m_filterModel = AZStd::make_unique(); + m_tableModel = AZStd::make_unique(); + + m_filterModel->setSourceModel(m_assetBrowserComponent->GetAssetBrowserModel()); + m_tableModel->setSourceModel(m_filterModel.get()); + + m_modelTesterAssetBrowser = AZStd::make_unique(m_assetBrowserComponent->GetAssetBrowserModel()); + m_modelTesterFilterModel = AZStd::make_unique(m_filterModel.get()); + m_modelTesterTableModel = AZStd::make_unique(m_tableModel.get()); + m_searchWidget = AZStd::make_unique(); + + // Setup String filters + m_searchWidget->Setup(true, true); + m_filterModel->SetFilter(m_searchWidget->GetFilter()); + + SetupAssetBrowser(); + } + + void AssetBrowserTest::TearDownEditorFixtureImpl() + { + m_modelTesterAssetBrowser.reset(); + m_modelTesterFilterModel.reset(); + m_modelTesterTableModel.reset(); + + m_tableModel.reset(); + m_filterModel.reset(); + m_assetBrowserComponent->Deactivate(); + + m_assetBrowserComponent.reset(); + m_searchWidget.reset(); + } + + void AssetBrowserTest::AddScanFolder( + AZ::s64 folderID, AZStd::string folderPath, AZStd::string displayName, FolderType folderType /*= FolderType::File*/) + { + AzToolsFramework::AssetDatabase::ScanFolderDatabaseEntry scanFolder = AzToolsFramework::AssetDatabase::ScanFolderDatabaseEntry(); + scanFolder.m_scanFolderID = folderID; + scanFolder.m_scanFolder = folderPath; + scanFolder.m_displayName = displayName; + scanFolder.m_isRoot = folderType == FolderType::Root; + GetRootEntry()->AddScanFolder(scanFolder); + } + + AZ::Uuid AssetBrowserTest::CreateSourceEntry( + AZ::s64 fileID, AZ::s64 parentFolderID, AZStd::string filename, AssetEntryType sourceType /*= AssetEntryType::Source*/) + { + AzToolsFramework::AssetDatabase::FileDatabaseEntry entry = AzToolsFramework::AssetDatabase::FileDatabaseEntry(); + entry.m_scanFolderPK = parentFolderID; + entry.m_fileID = fileID; + entry.m_fileName = filename; + entry.m_isFolder = sourceType == AssetEntryType::Folder; + GetRootEntry()->AddFile(entry); + + if (!entry.m_isFolder) + { + AzToolsFramework::AssetBrowser::SourceWithFileID entrySource = AzToolsFramework::AssetBrowser::SourceWithFileID(); + entrySource.first = entry.m_fileID; + entrySource.second = AzToolsFramework::AssetDatabase::SourceDatabaseEntry(); + entrySource.second.m_scanFolderPK = parentFolderID; + entrySource.second.m_sourceName = filename; + entrySource.second.m_sourceID = fileID; + entrySource.second.m_sourceGuid = AZ::Uuid::CreateRandom(); + + GetRootEntry()->AddSource(entrySource); + + return entrySource.second.m_sourceGuid; + } + + return AZ::Uuid::CreateNull(); + } + + void AssetBrowserTest::CreateProduct(AZ::s64 productID, AZ::Uuid sourceUuid, AZStd::string productName) + { + AzToolsFramework::AssetBrowser::ProductWithUuid product = AzToolsFramework::AssetBrowser::ProductWithUuid(); + product.first = sourceUuid; + product.second = AzToolsFramework::AssetDatabase::ProductDatabaseEntry(); + product.second.m_productID = productID; + + product.second.m_subID = aznumeric_cast(productID); + product.second.m_productName = productName; + + GetRootEntry()->AddProduct(product); + } + + void AssetBrowserTest::SetupAssetBrowser() + { + // RootEntries : 1 | Folders : 4 | SourceEntries : 5 | ProductEntries : 9 + m_assetBrowserHierarchy = R"( + D: + \ + dev + o3de + GameProject + Assets + Source_1 + Product_1_1 + Product_1_0 + Source_0 + Product_0_3 + Product_0_2 + Product_0_1 + Product_0_0 + Scripts + Source_3 + Source_2 + Product_2_2 + Product_2_1 + Product_2_0 + Misc + Source_4 + Product_4_2 + Product_4_1 + Product_4_0 )"; + + namespace AzAssetBrowser = AzToolsFramework::AssetBrowser; + + AddScanFolder(m_folderIds.at(2), "D:/dev/o3de/GameProject/Misc", "Misc"); + AZ::Uuid sourceUuid_4 = CreateSourceEntry(m_sourceIDs.at(4), m_folderIds.at(2), "Source_4"); + CreateProduct(m_productIDs.at(0), sourceUuid_4, "Product_4_0"); + CreateProduct(m_productIDs.at(1), sourceUuid_4, "Product_4_1"); + CreateProduct(m_productIDs.at(2), sourceUuid_4, "Product_4_2"); + + AddScanFolder(m_folderIds.at(1), "D:/dev/o3de/GameProject/Scripts", "Scripts"); + + AZ::Uuid sourceUuid_2 = CreateSourceEntry(m_sourceIDs.at(2), m_folderIds.at(1), "Source_2"); + CreateProduct(m_productIDs.at(0), sourceUuid_2, "Product_2_0"); + CreateProduct(m_productIDs.at(1), sourceUuid_2, "Product_2_1"); + CreateProduct(m_productIDs.at(2), sourceUuid_2, "Product_2_2"); + + CreateSourceEntry(m_sourceIDs.at(3), m_folderIds.at(1), "Source_3"); + + AddScanFolder(m_folderIds.at(0), "D:/dev/o3de/GameProject/Assets", "Assets"); + + AZ::Uuid sourceUuid_0 = CreateSourceEntry(m_sourceIDs.at(0), m_folderIds.at(0), "Source_0"); + CreateProduct(m_productIDs.at(0), sourceUuid_0, "Product_0_0"); + CreateProduct(m_productIDs.at(1), sourceUuid_0, "Product_0_1"); + CreateProduct(m_productIDs.at(2), sourceUuid_0, "Product_0_2"); + CreateProduct(m_productIDs.at(3), sourceUuid_0, "Product_0_3"); + + AZ::Uuid sourceUuid_1 = CreateSourceEntry(m_sourceIDs.at(1), m_folderIds.at(0), "Source_1"); + CreateProduct(m_productIDs.at(0), sourceUuid_1, "Product_1_0"); + CreateProduct(m_productIDs.at(1), sourceUuid_1, "Product_1_1"); + } + + void AssetBrowserTest::PrintModel(const QAbstractItemModel* model, AZStd::function printer) + { + AZStd::deque> indices; + indices.push_back({ model->index(0, 0), 0 }); + while (!indices.empty()) + { + auto [index, depth] = indices.front(); + indices.pop_front(); + + QString indentString; + for (int i = 0; i < depth; ++i) + { + indentString += " "; + } + const QString message = indentString + index.data(Qt::DisplayRole).toString(); + printer(message); + + for (int i = 0; i < model->rowCount(index); ++i) + { + indices.emplace_front(model->index(i, 0, index), depth + 1); + } + } + } + + QModelIndex AssetBrowserTest::GetModelIndex(const QAbstractItemModel* model, int targetDepth, int row) + { + AZStd::deque> indices; + indices.push_back({ model->index(0, 0), 0 }); + while (!indices.empty()) + { + auto [index, depth] = indices.front(); + indices.pop_front(); + + for (int i = 0; i < model->rowCount(index); ++i) + { + if (depth + 1 == targetDepth && row == i) + { + return model->index(i, 0, index); + } + indices.emplace_front(model->index(i, 0, index), depth + 1); + } + } + return QModelIndex(); + } + + AZStd::shared_ptr AssetBrowserTest::GetRootEntry() + { + return m_assetBrowserComponent->GetAssetBrowserModel()->GetRootEntry(); + } + + AZStd::vector AssetBrowserTest::GetVectorFromFormattedString(const QString& formattedString) + { + AZStd::vector hierarchySections; + QStringList splittedList = formattedString.split('\n', Qt::SkipEmptyParts); + + for (auto& str : splittedList) + { + str.replace(" ", ""); + hierarchySections.push_back(str); + } + return hierarchySections; + } + + TEST_F(AssetBrowserTest, CheckCorrectNumberOfEntriesInTableView) + { + m_filterModel->FilterUpdatedSlotImmediate(); + const int tableViewRowcount = m_tableModel->rowCount(); + + // RowCount should be 17 -> 5 SourceEntries + 12 ProductEntries) + EXPECT_EQ(tableViewRowcount, 17); + } + + TEST_F(AssetBrowserTest, CheckCorrectNumberOfEntriesInTableViewAfterStringFilter) + { + /* + *-Source_1 + * | + * |-product_1_0 + * |-product_1_1 + * + * + * Matching entries = 3 + */ + + // Apply string filter + m_searchWidget->SetTextFilter(QString("source_1")); + m_filterModel->FilterUpdatedSlotImmediate(); + + const int tableViewRowcount = m_tableModel->rowCount(); + EXPECT_EQ(tableViewRowcount, 3); + } + + TEST_F(AssetBrowserTest, CheckScanFolderAddition) + { + EXPECT_EQ(m_assetBrowserComponent->GetAssetBrowserModel()->rowCount(), 1); + const int newFolderId = 20; + AddScanFolder(newFolderId, "E:/TestFolder/TestFolder2", "TestFolder"); + + // Since the folder is empty it shouldn't be added to the model. + EXPECT_EQ(m_assetBrowserComponent->GetAssetBrowserModel()->rowCount(), 1); + + CreateSourceEntry(123, newFolderId, "DummyFile"); + + // When we add a file to the folder it should be added to the model + EXPECT_EQ(m_assetBrowserComponent->GetAssetBrowserModel()->rowCount(), 2); + } + +} // namespace UnitTest diff --git a/Code/Framework/AzToolsFramework/Tests/UI/EntityPropertyEditorTests.cpp b/Code/Framework/AzToolsFramework/Tests/UI/EntityPropertyEditorTests.cpp index c24bab9299..a3c6666a40 100644 --- a/Code/Framework/AzToolsFramework/Tests/UI/EntityPropertyEditorTests.cpp +++ b/Code/Framework/AzToolsFramework/Tests/UI/EntityPropertyEditorTests.cpp @@ -36,11 +36,6 @@ namespace UnitTest : public ComponentApplication { public: - void SetExecutableFolder(const char* path) - { - m_exeDirectory = path; - } - void SetSettingsRegistrySpecializations(SettingsRegistryInterface::Specializations& specializations) override { ComponentApplication::SetSettingsRegistrySpecializations(specializations); diff --git a/Code/Framework/AzToolsFramework/Tests/aztoolsframeworktests_files.cmake b/Code/Framework/AzToolsFramework/Tests/aztoolsframeworktests_files.cmake index 008c09188b..d597c9b570 100644 --- a/Code/Framework/AzToolsFramework/Tests/aztoolsframeworktests_files.cmake +++ b/Code/Framework/AzToolsFramework/Tests/aztoolsframeworktests_files.cmake @@ -123,6 +123,7 @@ set(FILES UI/EntityIdQLineEditTests.cpp UI/EntityOutlinerTests.cpp UI/EntityPropertyEditorTests.cpp + UI/AssetBrowserTests.cpp UndoStack.cpp Viewport/ClusterTests.cpp Viewport/ViewportEditorModeTests.cpp diff --git a/Code/LauncherUnified/launcher_generator.cmake b/Code/LauncherUnified/launcher_generator.cmake index 5c9ee68e27..550a67bc49 100644 --- a/Code/LauncherUnified/launcher_generator.cmake +++ b/Code/LauncherUnified/launcher_generator.cmake @@ -76,6 +76,12 @@ foreach(project_name project_path IN ZIP_LISTS LY_PROJECTS_TARGET_NAME LY_PROJEC Legacy::CrySystem ) + if(PAL_TRAIT_BUILD_SERVER_SUPPORTED) + set(server_runtime_dependencies + Legacy::CrySystem + ) + endif() + endif() ################################################################################ diff --git a/Code/Legacy/CryCommon/ISystem.h b/Code/Legacy/CryCommon/ISystem.h index 948977d02a..dd29209b24 100644 --- a/Code/Legacy/CryCommon/ISystem.h +++ b/Code/Legacy/CryCommon/ISystem.h @@ -1121,8 +1121,8 @@ inline ISystem* GetISystem() // Description: // This function must be called once by each module at the beginning, to setup global pointers. -extern "C" AZ_DLL_EXPORT void ModuleInitISystem(ISystem* pSystem, const char* moduleName); -extern "C" AZ_DLL_EXPORT void ModuleShutdownISystem(ISystem* pSystem); +void ModuleInitISystem(ISystem* pSystem, const char* moduleName); +void ModuleShutdownISystem(ISystem* pSystem); extern "C" AZ_DLL_EXPORT void InjectEnvironment(void* env); extern "C" AZ_DLL_EXPORT void DetachEnvironment(); diff --git a/Code/Legacy/CryCommon/platform_impl.cpp b/Code/Legacy/CryCommon/platform_impl.cpp index a68a5150db..845a1101a4 100644 --- a/Code/Legacy/CryCommon/platform_impl.cpp +++ b/Code/Legacy/CryCommon/platform_impl.cpp @@ -74,7 +74,7 @@ void InitCRTHandlers() {} ////////////////////////////////////////////////////////////////////////// // This is an entry to DLL initialization function that must be called for each loaded module ////////////////////////////////////////////////////////////////////////// -extern "C" AZ_DLL_EXPORT void ModuleInitISystem(ISystem* pSystem, [[maybe_unused]] const char* moduleName) +void ModuleInitISystem(ISystem* pSystem, [[maybe_unused]] const char* moduleName) { if (gEnv) // Already registered. { @@ -96,7 +96,7 @@ extern "C" AZ_DLL_EXPORT void ModuleInitISystem(ISystem* pSystem, [[maybe_unused } // if pSystem } -extern "C" AZ_DLL_EXPORT void ModuleShutdownISystem([[maybe_unused]] ISystem* pSystem) +void ModuleShutdownISystem([[maybe_unused]] ISystem* pSystem) { // Unregister with AZ environment. AZ::Environment::Detach(); diff --git a/Code/Legacy/CrySystem/SystemInit.cpp b/Code/Legacy/CrySystem/SystemInit.cpp index ac0683cf1b..99cc92ee6a 100644 --- a/Code/Legacy/CrySystem/SystemInit.cpp +++ b/Code/Legacy/CrySystem/SystemInit.cpp @@ -173,8 +173,6 @@ void CryEngineSignalHandler(int signal) ////////////////////////////////////////////////////////////////////////// #if defined(WIN32) || defined(LINUX) || defined(APPLE) -# define DLL_MODULE_INIT_ISYSTEM "ModuleInitISystem" -# define DLL_MODULE_SHUTDOWN_ISYSTEM "ModuleShutdownISystem" # define DLL_INITFUNC_RENDERER "PackageRenderConstructor" # define DLL_INITFUNC_SOUND "CreateSoundSystem" # define DLL_INITFUNC_FONT "CreateCryFontInterface" @@ -188,8 +186,6 @@ void CryEngineSignalHandler(int signal) #if defined(AZ_RESTRICTED_SECTION_IMPLEMENTED) #undef AZ_RESTRICTED_SECTION_IMPLEMENTED #else -# define DLL_MODULE_INIT_ISYSTEM (LPCSTR)2 -# define DLL_MODULE_SHUTDOWN_ISYSTEM (LPCSTR)3 # define DLL_INITFUNC_RENDERER (LPCSTR)1 # define DLL_INITFUNC_RENDERER (LPCSTR)1 # define DLL_INITFUNC_SOUND (LPCSTR)1 @@ -445,18 +441,6 @@ AZStd::unique_ptr CSystem::LoadDLL(const char* dllName) return handle; } - ////////////////////////////////////////////////////////////////////////// - // After loading DLL initialize it by calling ModuleInitISystem - ////////////////////////////////////////////////////////////////////////// - AZStd::string moduleName = PathUtil::GetFileName(dllName); - - typedef void*(*PtrFunc_ModuleInitISystem)(ISystem* pSystem, const char* moduleName); - PtrFunc_ModuleInitISystem pfnModuleInitISystem = handle->GetFunction(DLL_MODULE_INIT_ISYSTEM); - if (pfnModuleInitISystem) - { - pfnModuleInitISystem(this, moduleName.c_str()); - } - return handle; } @@ -497,13 +481,6 @@ void CSystem::ShutdownModuleLibraries() #if !defined(AZ_MONOLITHIC_BUILD) for (auto iterator = m_moduleDLLHandles.begin(); iterator != m_moduleDLLHandles.end(); ++iterator) { - typedef void*( * PtrFunc_ModuleShutdownISystem )(ISystem* pSystem); - - PtrFunc_ModuleShutdownISystem pfnModuleShutdownISystem = iterator->second->GetFunction(DLL_MODULE_SHUTDOWN_ISYSTEM); - if (pfnModuleShutdownISystem) - { - pfnModuleShutdownISystem(this); - } if (iterator->second->IsLoaded()) { iterator->second->Unload(); diff --git a/Code/Legacy/CrySystem/XConsole.cpp b/Code/Legacy/CrySystem/XConsole.cpp index 9f60a9dd8d..22c61d3f22 100644 --- a/Code/Legacy/CrySystem/XConsole.cpp +++ b/Code/Legacy/CrySystem/XConsole.cpp @@ -333,11 +333,6 @@ void CXConsole::Init(ISystem* pSystem) m_nLoadingBackTexID = -1; - if (gEnv->IsDedicated()) - { - m_bConsoleActive = true; - } - REGISTER_COMMAND("ConsoleShow", &ConsoleShow, VF_NULL, "Opens the console"); REGISTER_COMMAND("ConsoleHide", &ConsoleHide, VF_NULL, "Closes the console"); diff --git a/Code/Tools/AssetBundler/source/utils/GUIApplicationManager.cpp b/Code/Tools/AssetBundler/source/utils/GUIApplicationManager.cpp index b17832526f..17e621f7d6 100644 --- a/Code/Tools/AssetBundler/source/utils/GUIApplicationManager.cpp +++ b/Code/Tools/AssetBundler/source/utils/GUIApplicationManager.cpp @@ -346,9 +346,7 @@ namespace AssetBundler } // Determine the enabled platforms - const char* appRoot = nullptr; - AzFramework::ApplicationRequests::Bus::BroadcastResult(appRoot, &AzFramework::ApplicationRequests::GetAppRoot); - m_enabledPlatforms = GetEnabledPlatformFlags(GetEngineRoot(), appRoot, AZ::Utils::GetProjectPath().c_str()); + m_enabledPlatforms = GetEnabledPlatformFlags(GetEngineRoot(), AZStd::string_view(AZ::Utils::GetProjectPath())); // Determine which Gems are enabled for the current project if (!AzFramework::GetGemsInfo(m_gemInfoList, *m_settingsRegistry)) diff --git a/Code/Tools/AssetBundler/source/utils/applicationManager.cpp b/Code/Tools/AssetBundler/source/utils/applicationManager.cpp index 0388570fdd..2df9e64ac5 100644 --- a/Code/Tools/AssetBundler/source/utils/applicationManager.cpp +++ b/Code/Tools/AssetBundler/source/utils/applicationManager.cpp @@ -1401,7 +1401,6 @@ namespace AssetBundler // If no platform was specified, defaulting to platforms specified in the asset processor config files AzFramework::PlatformFlags platformFlags = GetEnabledPlatformFlags( - AZStd::string_view{ AZ::Utils::GetEnginePath() }, AZStd::string_view{ AZ::Utils::GetEnginePath() }, AZStd::string_view{ AZ::Utils::GetProjectPath() }); [[maybe_unused]] auto platformsString = AzFramework::PlatformHelper::GetCommaSeparatedPlatformList(platformFlags); diff --git a/Code/Tools/AssetBundler/source/utils/utils.cpp b/Code/Tools/AssetBundler/source/utils/utils.cpp index 6daf3c571b..7e56f5450e 100644 --- a/Code/Tools/AssetBundler/source/utils/utils.cpp +++ b/Code/Tools/AssetBundler/source/utils/utils.cpp @@ -377,7 +377,6 @@ namespace AssetBundler AzFramework::PlatformFlags GetEnabledPlatformFlags( AZStd::string_view engineRoot, - AZStd::string_view assetRoot, AZStd::string_view projectPath) { auto settingsRegistry = AZ::SettingsRegistry::Get(); @@ -387,7 +386,7 @@ namespace AssetBundler 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); AzFramework::PlatformFlags platformFlags = AzFramework::PlatformFlags::Platform_NONE; for (const auto& enabledPlatform : enabledPlatformList) diff --git a/Code/Tools/AssetBundler/source/utils/utils.h b/Code/Tools/AssetBundler/source/utils/utils.h index bfdf252014..0986d70ca8 100644 --- a/Code/Tools/AssetBundler/source/utils/utils.h +++ b/Code/Tools/AssetBundler/source/utils/utils.h @@ -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. AzFramework::PlatformFlags GetEnabledPlatformFlags( AZStd::string_view enginePath, - AZStd::string_view assetRoot, AZStd::string_view projectPath); QJsonObject ReadJson(const AZStd::string& filePath); diff --git a/Code/Tools/AssetBundler/tests/UtilsTests.cpp b/Code/Tools/AssetBundler/tests/UtilsTests.cpp index 560d399613..60fc79579b 100644 --- a/Code/Tools/AssetBundler/tests/UtilsTests.cpp +++ b/Code/Tools/AssetBundler/tests/UtilsTests.cpp @@ -67,7 +67,7 @@ namespace AssetBundler void NormalizePathKeepCase(AZStd::string& /*path*/) override {} void CalculateBranchTokenForEngineRoot(AZStd::string& /*token*/) const override {} - const char* GetEngineRoot() const override + const char* GetTempDir() const { return m_tempDir->GetDirectory(); } @@ -83,7 +83,7 @@ namespace AssetBundler TEST_F(MockUtilsTest, DISABLED_TestFilePath_StartsWithAFileSeparator_Valid) { 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 = absoluteFilePath.LexicallyNormal(); @@ -95,7 +95,7 @@ namespace AssetBundler TEST_F(MockUtilsTest, TestFilePath_RelativePath_Valid) { 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()); EXPECT_EQ(AZ::IO::PathView{ filePath.AbsolutePath() }, absoluteFilePath); } @@ -107,8 +107,8 @@ namespace AssetBundler AZ::IO::Path relFilePath = "Foo\\Foo.xml"; AZ::IO::Path wrongCaseRelFilePath = "Foo\\foo.xml"; - AZ::IO::Path correctAbsoluteFilePath = (AZ::IO::Path(GetEngineRoot()) / relFilePath).LexicallyNormal(); - AZ::IO::Path wrongCaseAbsoluteFilePath = (AZ::IO::Path(GetEngineRoot()) / wrongCaseRelFilePath).LexicallyNormal(); + AZ::IO::Path correctAbsoluteFilePath = (AZ::IO::Path(GetTempDir()) / relFilePath).LexicallyNormal(); + AZ::IO::Path wrongCaseAbsoluteFilePath = (AZ::IO::Path(GetTempDir()) / wrongCaseRelFilePath).LexicallyNormal(); AZ::IO::HandleType fileHandle = AZ::IO::InvalidHandle; 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) { 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); EXPECT_TRUE(filePath.IsValid()); @@ -132,8 +132,8 @@ namespace AssetBundler { AZStd::string relFilePath = "Foo\\Foo.xml"; AZStd::string wrongCaseRelFilePath = "Foo\\foo.xml"; - AZ::IO::Path correctAbsoluteFilePath = (AZ::IO::Path(GetEngineRoot()) / relFilePath).LexicallyNormal(); - AZ::IO::Path wrongCaseAbsoluteFilePath = (AZ::IO::Path(GetEngineRoot()) / wrongCaseRelFilePath).LexicallyNormal(); + AZ::IO::Path correctAbsoluteFilePath = (AZ::IO::Path(GetTempDir()) / relFilePath).LexicallyNormal(); + AZ::IO::Path wrongCaseAbsoluteFilePath = (AZ::IO::Path(GetTempDir()) / wrongCaseRelFilePath).LexicallyNormal(); AZ::IO::HandleType fileHandle = AZ::IO::InvalidHandle; AZ::IO::FileIOBase::GetInstance()->Open(correctAbsoluteFilePath.c_str(), AZ::IO::OpenMode::ModeWrite | AZ::IO::OpenMode::ModeCreatePath, fileHandle); diff --git a/Code/Tools/AssetBundler/tests/applicationManagerTests.cpp b/Code/Tools/AssetBundler/tests/applicationManagerTests.cpp index 07eae67a81..fd587195af 100644 --- a/Code/Tools/AssetBundler/tests/applicationManagerTests.cpp +++ b/Code/Tools/AssetBundler/tests/applicationManagerTests.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -84,10 +85,9 @@ namespace AssetBundler // in the unit tests. AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize); - const char* engineRoot = nullptr; - AzFramework::ApplicationRequests::Bus::BroadcastResult(engineRoot, &AzFramework::ApplicationRequests::GetEngineRoot); - ASSERT_TRUE(engineRoot) << "Unable to locate engine root.\n"; - AzFramework::StringFunc::Path::Join(engineRoot, RelativeTestFolder, m_data->m_testEngineRoot); + AZ::IO::FixedMaxPath engineRoot = AZ::Utils::GetEnginePath(); + ASSERT_TRUE(!engineRoot.empty()) << "Unable to locate engine root.\n"; + m_data->m_testEngineRoot = (engineRoot / RelativeTestFolder).String(); m_data->m_localFileIO = aznew AZ::IO::LocalFileIO(); m_data->m_priorFileIO = AZ::IO::FileIOBase::GetInstance(); @@ -150,7 +150,8 @@ namespace AssetBundler 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 expectedFlags = AzFramework::PlatformFlags::Platform_ANDROID | AzFramework::PlatformFlags::Platform_IOS | AzFramework::PlatformFlags::Platform_PROVO | hostPlatformFlag; ASSERT_EQ(platformFlags, expectedFlags); diff --git a/Code/Tools/AssetProcessor/native/tests/AssetProcessorTest.h b/Code/Tools/AssetProcessor/native/tests/AssetProcessorTest.h index 719b04610c..8f26155fd3 100644 --- a/Code/Tools/AssetProcessor/native/tests/AssetProcessorTest.h +++ b/Code/Tools/AssetProcessor/native/tests/AssetProcessorTest.h @@ -25,7 +25,7 @@ namespace AssetProcessor : public ::testing::Test { protected: - UnitTestUtils::AssertAbsorber* m_errorAbsorber; + AZStd::unique_ptr m_errorAbsorber{}; FileStatePassthrough m_fileStateCache; void SetUp() override @@ -40,7 +40,7 @@ namespace AssetProcessor m_ownsSysAllocator = true; AZ::AllocatorInstance::Create(); } - m_errorAbsorber = new UnitTestUtils::AssertAbsorber(); + m_errorAbsorber = AZStd::make_unique(); m_application = AZStd::make_unique(); @@ -62,8 +62,8 @@ namespace AssetProcessor AssetUtilities::ResetAssetRoot(); m_application.reset(); - delete m_errorAbsorber; - m_errorAbsorber = nullptr; + m_errorAbsorber.reset(); + if (m_ownsSysAllocator) { AZ::AllocatorInstance::Destroy(); diff --git a/Code/Tools/AssetProcessor/native/tests/platformconfiguration/platformconfigurationtests.cpp b/Code/Tools/AssetProcessor/native/tests/platformconfiguration/platformconfigurationtests.cpp index 69397745f1..e519f48ce5 100644 --- a/Code/Tools/AssetProcessor/native/tests/platformconfiguration/platformconfigurationtests.cpp +++ b/Code/Tools/AssetProcessor/native/tests/platformconfiguration/platformconfigurationtests.cpp @@ -52,11 +52,12 @@ TEST_F(PlatformConfigurationUnitTests, TestFailReadConfigFile_BadPlatform) using namespace AssetProcessor; 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"); ASSERT_TRUE(configRoot); UnitTestPlatformConfiguration config; 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); } @@ -67,11 +68,12 @@ TEST_F(PlatformConfigurationUnitTests, TestFailReadConfigFile_NoPlatform) using namespace AssetProcessor; 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"); ASSERT_TRUE(configRoot); UnitTestPlatformConfiguration config; 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); } @@ -81,11 +83,12 @@ TEST_F(PlatformConfigurationUnitTests, TestFailReadConfigFile_NoScanFolders) using namespace AssetProcessor; 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"); ASSERT_TRUE(configRoot); UnitTestPlatformConfiguration config; 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); } @@ -95,11 +98,12 @@ TEST_F(PlatformConfigurationUnitTests, TestFailReadConfigFile_BrokenRecognizers) using namespace AssetProcessor; 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"); ASSERT_TRUE(configRoot); UnitTestPlatformConfiguration config; 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); } @@ -109,11 +113,12 @@ TEST_F(PlatformConfigurationUnitTests, TestFailReadConfigFile_Regular_Platforms) using namespace AssetProcessor; 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"); ASSERT_TRUE(configRoot); UnitTestPlatformConfiguration config; 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); // verify the data. @@ -322,12 +327,13 @@ TEST_F(PlatformConfigurationUnitTests, TestFailReadConfigFile_RegularScanfolder) using namespace AssetProcessor; 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"); ASSERT_TRUE(configRoot); UnitTestPlatformConfiguration config; m_absorber.Clear(); 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(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; 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"); ASSERT_TRUE(configRoot); UnitTestPlatformConfiguration config; 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(config.GetScanFolderCount(), 5); @@ -402,11 +409,12 @@ TEST_F(PlatformConfigurationUnitTests, TestFailReadConfigFile_RegularExcludes) using namespace AssetProcessor; 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"); ASSERT_TRUE(configRoot); UnitTestPlatformConfiguration config; 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_TRUE(config.IsFileExcluded("blahblah/$tmp_01.test")); @@ -427,11 +435,12 @@ TEST_F(PlatformConfigurationUnitTests, TestFailReadConfigFile_Recognizers) #endif 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"); ASSERT_TRUE(configRoot); UnitTestPlatformConfiguration config; 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); const AssetProcessor::RecognizerContainer& recogs = config.GetAssetRecognizerContainer(); @@ -518,12 +527,13 @@ TEST_F(PlatformConfigurationUnitTests, TestFailReadConfigFile_Overrides) using namespace AzToolsFramework::AssetSystem; using namespace AssetProcessor; 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"); ASSERT_TRUE(configRoot); UnitTestPlatformConfiguration config; 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); const AssetProcessor::RecognizerContainer& recogs = config.GetAssetRecognizerContainer(); @@ -625,11 +635,12 @@ TEST_F(PlatformConfigurationUnitTests, ReadCheckServer_FromConfig_Valid) using namespace AssetProcessor; 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"); ASSERT_TRUE(configRoot); UnitTestPlatformConfiguration config; 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); const AssetProcessor::RecognizerContainer& recogs = config.GetAssetRecognizerContainer(); @@ -674,11 +685,12 @@ TEST_F(PlatformConfigurationUnitTests, Test_MetaFileTypes_AssetImporterExtension using namespace AssetProcessor; 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"); ASSERT_TRUE(configRoot); UnitTestPlatformConfiguration config; 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_TRUE(config.MetaDataFileTypesCount() == 2); diff --git a/Code/Tools/AssetProcessor/native/utilities/PlatformConfiguration.cpp b/Code/Tools/AssetProcessor/native/utilities/PlatformConfiguration.cpp index 9a60bf3110..483d5a46aa 100644 --- a/Code/Tools/AssetProcessor/native/utilities/PlatformConfiguration.cpp +++ b/Code/Tools/AssetProcessor/native/utilities/PlatformConfiguration.cpp @@ -749,7 +749,7 @@ namespace AssetProcessor } AZStd::vector configFiles = AzToolsFramework::AssetUtils::GetConfigFiles(absoluteSystemRoot.toUtf8().constData(), - absoluteAssetRoot.toUtf8().constData(), projectPath.toUtf8().constData(), + projectPath.toUtf8().constData(), addPlatformConfigs, addGemsConfigs && !noGemScanFolders, settingsRegistry); // First Merge all Engine, Gem and Project specific AssetProcessor*Config.setreg/.inifiles diff --git a/Code/Tools/BundleLauncher/O3DE_SDK_Launcher.cpp b/Code/Tools/BundleLauncher/O3DE_SDK_Launcher.cpp index 0c362ac829..4dd7e184e9 100644 --- a/Code/Tools/BundleLauncher/O3DE_SDK_Launcher.cpp +++ b/Code/Tools/BundleLauncher/O3DE_SDK_Launcher.cpp @@ -49,6 +49,12 @@ int main(int argc, char* argv[]) AZStd::unique_ptr shellProcess(AzFramework::ProcessWatcher::LaunchProcess(shellProcessLaunch, AzFramework::ProcessCommunicationType::COMMUNICATOR_TYPE_NONE)); shellProcess->WaitForProcessToExit(120); shellProcess.reset(); + + parameters = AZStd::string::format("-c \"%s/scripts/o3de.sh register --this-engine\"", enginePath.c_str()); + shellProcessLaunch.m_commandlineParameters = parameters; + shellProcess.reset(AzFramework::ProcessWatcher::LaunchProcess(shellProcessLaunch, AzFramework::ProcessCommunicationType::COMMUNICATOR_TYPE_NONE)); + shellProcess->WaitForProcessToExit(120); + shellProcess.reset(); AZ::IO::FixedMaxPath projectManagerPath = installedBinariesFolder/"o3de.app"/"Contents"/"MacOS"/"o3de"; AzFramework::ProcessLauncher::ProcessLaunchInfo processLaunchInfo; diff --git a/Code/Tools/ProjectManager/Platform/Linux/ProjectUtils_linux.cpp b/Code/Tools/ProjectManager/Platform/Linux/ProjectUtils_linux.cpp index 0d66009d90..e901d807b4 100644 --- a/Code/Tools/ProjectManager/Platform/Linux/ProjectUtils_linux.cpp +++ b/Code/Tools/ProjectManager/Platform/Linux/ProjectUtils_linux.cpp @@ -10,6 +10,8 @@ #include #include +#include + namespace O3DE::ProjectManager { namespace ProjectUtils @@ -94,5 +96,10 @@ namespace O3DE::ProjectManager QProcessEnvironment::systemEnvironment(), QObject::tr("Running get_python script...")); } + + AZ::IO::FixedMaxPath GetEditorDirectory() + { + return AZ::Utils::GetExecutableDirectory(); + } } // namespace ProjectUtils } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Platform/Mac/ProjectUtils_mac.cpp b/Code/Tools/ProjectManager/Platform/Mac/ProjectUtils_mac.cpp index e36f6cd0c8..b768200398 100644 --- a/Code/Tools/ProjectManager/Platform/Mac/ProjectUtils_mac.cpp +++ b/Code/Tools/ProjectManager/Platform/Mac/ProjectUtils_mac.cpp @@ -11,6 +11,9 @@ #include #include +#include +#include + namespace O3DE::ProjectManager { namespace ProjectUtils @@ -104,5 +107,35 @@ namespace O3DE::ProjectManager QProcessEnvironment::systemEnvironment(), QObject::tr("Running get_python script...")); } + + AZ::IO::FixedMaxPath GetEditorDirectory() + { + AZ::IO::FixedMaxPath executableDirectory = AZ::Utils::GetExecutableDirectory(); + AZ::IO::FixedMaxPath editorPath{ executableDirectory }; + editorPath /= "../../../Editor.app/Contents/MacOS"; + editorPath = editorPath.LexicallyNormal(); + if (!AZ::IO::SystemFile::IsDirectory(editorPath.c_str())) + { + if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr) + { + if (AZ::IO::FixedMaxPath installedBinariesPath; + settingsRegistry->Get(installedBinariesPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_InstalledBinaryFolder)) + { + if (AZ::IO::FixedMaxPath engineRootFolder; + settingsRegistry->Get(engineRootFolder.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder)) + { + editorPath = engineRootFolder / installedBinariesPath / "Editor.app/Contents/MacOS"; + } + } + } + + if (!AZ::IO::SystemFile::IsDirectory(editorPath.c_str())) + { + AZ_Error("ProjectManager", false, "Unable to find the Editor app bundle!"); + } + } + + return editorPath; + } } // namespace ProjectUtils } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Platform/Windows/ProjectUtils_windows.cpp b/Code/Tools/ProjectManager/Platform/Windows/ProjectUtils_windows.cpp index 831529d5e4..871f8e9567 100644 --- a/Code/Tools/ProjectManager/Platform/Windows/ProjectUtils_windows.cpp +++ b/Code/Tools/ProjectManager/Platform/Windows/ProjectUtils_windows.cpp @@ -14,6 +14,8 @@ #include #include +#include + namespace O3DE::ProjectManager { namespace ProjectUtils @@ -139,5 +141,10 @@ namespace O3DE::ProjectManager QProcessEnvironment::systemEnvironment(), QObject::tr("Running get_python script...")); } + + AZ::IO::FixedMaxPath GetEditorDirectory() + { + return AZ::Utils::GetExecutableDirectory(); + } } // namespace ProjectUtils } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/CreateProjectCtrl.cpp b/Code/Tools/ProjectManager/Source/CreateProjectCtrl.cpp index 51e1163713..6b19e839d7 100644 --- a/Code/Tools/ProjectManager/Source/CreateProjectCtrl.cpp +++ b/Code/Tools/ProjectManager/Source/CreateProjectCtrl.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -47,9 +48,14 @@ namespace O3DE::ProjectManager m_gemCatalogScreen = new GemCatalogScreen(this); m_stack->addWidget(m_gemCatalogScreen); + + m_gemRepoScreen = new GemRepoScreen(this); + m_stack->addWidget(m_gemRepoScreen); + vLayout->addWidget(m_stack); connect(m_gemCatalogScreen, &ScreenWidget::ChangeScreenRequest, this, &CreateProjectCtrl::OnChangeScreenRequest); + connect(m_gemRepoScreen, &GemRepoScreen::OnRefresh, m_gemCatalogScreen, &GemCatalogScreen::Refresh); // When there are multiple project templates present, we re-gather the gems when changing the selected the project template. connect(m_newProjectSettingsScreen, &NewProjectSettingsScreen::OnTemplateSelectionChanged, this, [=](int oldIndex, [[maybe_unused]] int newIndex) @@ -89,6 +95,9 @@ namespace O3DE::ProjectManager buttons->setObjectName("footer"); vLayout->addWidget(buttons); + m_primaryButton = buttons->addButton(tr("Create Project"), QDialogButtonBox::ApplyRole); + connect(m_primaryButton, &QPushButton::clicked, this, &CreateProjectCtrl::HandlePrimaryButton); + #ifdef TEMPLATE_GEM_CONFIGURATION_ENABLED connect(m_newProjectSettingsScreen, &ScreenWidget::ChangeScreenRequest, this, &CreateProjectCtrl::OnChangeScreenRequest); @@ -100,8 +109,6 @@ namespace O3DE::ProjectManager Update(); #endif // TEMPLATE_GEM_CONFIGURATION_ENABLED - m_primaryButton = buttons->addButton(tr("Create Project"), QDialogButtonBox::ApplyRole); - connect(m_primaryButton, &QPushButton::clicked, this, &CreateProjectCtrl::HandlePrimaryButton); setLayout(vLayout); } @@ -122,6 +129,9 @@ namespace O3DE::ProjectManager // Gather the enabled gems from the default project template when starting the create new project workflow. ReinitGemCatalogForSelectedTemplate(); + + // make sure the gem repo has the latest details + m_gemRepoScreen->Reinit(); } void CreateProjectCtrl::HandleBackButton() @@ -160,12 +170,21 @@ namespace O3DE::ProjectManager { m_header->setSubTitle(tr("Configure project with Gems")); m_secondaryButton->setVisible(false); + m_primaryButton->setVisible(true); + } + else if (m_stack->currentWidget() == m_gemRepoScreen) + { + m_header->setSubTitle(tr("Gem Repositories")); + m_secondaryButton->setVisible(true); + m_secondaryButton->setText(tr("Back")); + m_primaryButton->setVisible(false); } else { m_header->setSubTitle(tr("Enter Project Details")); m_secondaryButton->setVisible(true); m_secondaryButton->setText(tr("Configure Gems")); + m_primaryButton->setVisible(true); } } @@ -175,6 +194,10 @@ namespace O3DE::ProjectManager { HandleSecondaryButton(); } + else if (screen == ProjectManagerScreen::GemRepos) + { + NextScreen(); + } else { emit ChangeScreenRequest(screen); @@ -230,6 +253,12 @@ namespace O3DE::ProjectManager { if (m_newProjectSettingsScreen->Validate()) { + if (!m_gemCatalogScreen->GetDownloadController()->IsDownloadQueueEmpty()) + { + QMessageBox::critical(this, tr("Gems downloading"), tr("You must wait for gems to finish downloading before continuing.")); + return; + } + ProjectInfo projectInfo = m_newProjectSettingsScreen->GetProjectInfo(); QString projectTemplatePath = m_newProjectSettingsScreen->GetProjectTemplatePath(); diff --git a/Code/Tools/ProjectManager/Source/CreateProjectCtrl.h b/Code/Tools/ProjectManager/Source/CreateProjectCtrl.h index 58f4758edd..b43e0e2858 100644 --- a/Code/Tools/ProjectManager/Source/CreateProjectCtrl.h +++ b/Code/Tools/ProjectManager/Source/CreateProjectCtrl.h @@ -23,6 +23,7 @@ namespace O3DE::ProjectManager QT_FORWARD_DECLARE_CLASS(ScreenHeader) QT_FORWARD_DECLARE_CLASS(NewProjectSettingsScreen) QT_FORWARD_DECLARE_CLASS(GemCatalogScreen) + QT_FORWARD_DECLARE_CLASS(GemRepoScreen) class CreateProjectCtrl : public ScreenWidget @@ -64,6 +65,7 @@ namespace O3DE::ProjectManager NewProjectSettingsScreen* m_newProjectSettingsScreen = nullptr; GemCatalogScreen* m_gemCatalogScreen = nullptr; + GemRepoScreen* m_gemRepoScreen = nullptr; }; } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/EngineScreenCtrl.cpp b/Code/Tools/ProjectManager/Source/EngineScreenCtrl.cpp index f30a8e0daa..9d9110922f 100644 --- a/Code/Tools/ProjectManager/Source/EngineScreenCtrl.cpp +++ b/Code/Tools/ProjectManager/Source/EngineScreenCtrl.cpp @@ -39,6 +39,10 @@ namespace O3DE::ProjectManager m_tabWidget->addTab(m_engineSettingsScreen, tr("General")); m_tabWidget->addTab(m_gemRepoScreen, tr("Gem Repositories")); + + // when tab changes, notify the current screen so it can refresh + connect(m_tabWidget, &QTabWidget::currentChanged, this, &EngineScreenCtrl::TabChanged); + topBarHLayout->addWidget(m_tabWidget); vLayout->addWidget(topBarFrameWidget); @@ -46,6 +50,11 @@ namespace O3DE::ProjectManager setLayout(vLayout); } + void EngineScreenCtrl::TabChanged([[maybe_unused]] int index) + { + NotifyCurrentScreen(); + } + ProjectManagerScreen EngineScreenCtrl::GetScreenEnum() { return ProjectManagerScreen::UpdateProject; @@ -71,6 +80,15 @@ namespace O3DE::ProjectManager return false; } + void EngineScreenCtrl::NotifyCurrentScreen() + { + ScreenWidget* screen = reinterpret_cast(m_tabWidget->currentWidget()); + if (screen) + { + screen->NotifyCurrentScreen(); + } + } + void EngineScreenCtrl::GoToScreen(ProjectManagerScreen screen) { if (screen == m_engineSettingsScreen->GetScreenEnum()) diff --git a/Code/Tools/ProjectManager/Source/EngineScreenCtrl.h b/Code/Tools/ProjectManager/Source/EngineScreenCtrl.h index b7142ba226..cf0d2a24d0 100644 --- a/Code/Tools/ProjectManager/Source/EngineScreenCtrl.h +++ b/Code/Tools/ProjectManager/Source/EngineScreenCtrl.h @@ -30,6 +30,10 @@ namespace O3DE::ProjectManager bool IsTab() override; bool ContainsScreen(ProjectManagerScreen screen) override; void GoToScreen(ProjectManagerScreen screen) override; + void NotifyCurrentScreen() override; + + public slots: + void TabChanged(int index); QTabWidget* m_tabWidget = nullptr; EngineSettingsScreen* m_engineSettingsScreen = nullptr; diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp index b0729c0047..bd0a6e9bc3 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp @@ -152,7 +152,7 @@ namespace O3DE::ProjectManager } else { - tagContainer->Update(ConvertFromModelIndices(tagIndices)); + tagContainer->Update(GetTagsFromModelIndices(tagIndices)); label->setText(QString("%1 %2").arg(tagIndices.size()).arg(tagIndices.size() == 1 ? singularTitle : pluralTitle)); widget->show(); } @@ -237,7 +237,7 @@ namespace O3DE::ProjectManager // Gem name, progress string, cancel QHBoxLayout* nameProgressLayout = new QHBoxLayout(newGemDownloadWidget); - TagWidget* newTag = new TagWidget(gemName, newGemDownloadWidget); + TagWidget* newTag = new TagWidget({gemName, gemName}, newGemDownloadWidget); nameProgressLayout->addWidget(newTag); QLabel* progress = new QLabel(tr("Queued"), newGemDownloadWidget); progress->setObjectName("DownloadProgressLabel"); @@ -311,15 +311,15 @@ namespace O3DE::ProjectManager GemDownloadRemoved(gemName); // update the list to remove the gem that has finished } - QStringList CartOverlayWidget::ConvertFromModelIndices(const QVector& gems) const + QVector CartOverlayWidget::GetTagsFromModelIndices(const QVector& gems) const { - QStringList gemNames; - gemNames.reserve(gems.size()); + QVector tags; + tags.reserve(gems.size()); for (const QModelIndex& modelIndex : gems) { - gemNames.push_back(GemModel::GetDisplayName(modelIndex)); + tags.push_back({ GemModel::GetDisplayName(modelIndex), GemModel::GetName(modelIndex) }); } - return gemNames; + return tags; } CartButton::CartButton(GemModel* gemModel, DownloadController* downloadController, QWidget* parent) diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.h index a8f50a5338..f3242d6db7 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.h @@ -43,7 +43,7 @@ namespace O3DE::ProjectManager void GemDownloadComplete(const QString& gemName, bool success); private: - QStringList ConvertFromModelIndices(const QVector& gems) const; + QVector GetTagsFromModelIndices(const QVector& gems) const; using GetTagIndicesCallback = AZStd::function()>; void CreateGemSection(const QString& singularTitle, const QString& pluralTitle, GetTagIndicesCallback getTagIndices); diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp index f3e1da011a..732f4813a2 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp @@ -23,6 +23,7 @@ #include #include #include +#include namespace O3DE::ProjectManager { @@ -32,6 +33,9 @@ namespace O3DE::ProjectManager m_gemModel = new GemModel(this); m_proxyModel = new GemSortFilterProxyModel(m_gemModel, this); + // default to sort by gem name + m_proxyModel->setSortRole(GemModel::RoleName); + QVBoxLayout* vLayout = new QVBoxLayout(); vLayout->setMargin(0); vLayout->setSpacing(0); @@ -45,6 +49,7 @@ namespace O3DE::ProjectManager connect(m_gemModel, &GemModel::gemStatusChanged, this, &GemCatalogScreen::OnGemStatusChanged); connect(m_headerWidget, &GemCatalogHeaderWidget::OpenGemsRepo, this, &GemCatalogScreen::HandleOpenGemRepo); connect(m_headerWidget, &GemCatalogHeaderWidget::AddGem, this, &GemCatalogScreen::OnAddGemClicked); + connect(m_downloadController, &DownloadController::Done, this, &GemCatalogScreen::OnGemDownloadResult); QHBoxLayout* hLayout = new QHBoxLayout(); hLayout->setMargin(0); @@ -54,7 +59,7 @@ namespace O3DE::ProjectManager m_gemInspector = new GemInspector(m_gemModel, this); m_gemInspector->setFixedWidth(240); - connect(m_gemInspector, &GemInspector::TagClicked, this, &GemCatalogScreen::SelectGem); + connect(m_gemInspector, &GemInspector::TagClicked, [=](const Tag& tag) { SelectGem(tag.id); }); QWidget* filterWidget = new QWidget(this); filterWidget->setFixedWidth(240); @@ -82,11 +87,20 @@ namespace O3DE::ProjectManager void GemCatalogScreen::ReinitForProject(const QString& projectPath) { + m_projectPath = projectPath; m_gemModel->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); m_proxyModel->ResetFilters(); + m_proxyModel->sort(/*column=*/0); if (m_filterWidget) { @@ -144,11 +158,80 @@ namespace O3DE::ProjectManager { m_gemModel->AddGem(gemInfoResult.GetValue()); m_gemModel->UpdateGemDependencies(); + m_proxyModel->sort(/*column=*/0); } } } } + void GemCatalogScreen::Refresh() + { + QHash gemInfoHash; + + // create a hash with the gem name as key + const AZ::Outcome, AZStd::string>& allGemInfosResult = PythonBindingsInterface::Get()->GetAllGemInfos(m_projectPath); + if (allGemInfosResult.IsSuccess()) + { + const QVector& gemInfos = allGemInfosResult.GetValue(); + for (const GemInfo& gemInfo : gemInfos) + { + gemInfoHash.insert(gemInfo.m_name, gemInfo); + } + } + + // add all the gem repos into the hash + const AZ::Outcome, AZStd::string>& allRepoGemInfosResult = PythonBindingsInterface::Get()->GetAllGemRepoGemsInfos(); + if (allRepoGemInfosResult.IsSuccess()) + { + const QVector& allRepoGemInfos = allRepoGemInfosResult.GetValue(); + for (const GemInfo& gemInfo : allRepoGemInfos) + { + if (!gemInfoHash.contains(gemInfo.m_name)) + { + gemInfoHash.insert(gemInfo.m_name, gemInfo); + } + } + } + + // remove gems from the model that no longer exist in the hash and are not project dependencies + int i = 0; + while (i < m_gemModel->rowCount()) + { + QModelIndex index = m_gemModel->index(i,0); + QString gemName = m_gemModel->GetName(index); + const bool gemFound = gemInfoHash.contains(gemName); + if (!gemFound && !m_gemModel->IsAdded(index) && !m_gemModel->IsAddedDependency(index)) + { + m_gemModel->removeRow(i); + } + else + { + if (!gemFound && (m_gemModel->IsAdded(index) || m_gemModel->IsAddedDependency(index))) + { + const QString error = tr("Gem %1 was removed or unregistered, but is still used by the project.").arg(gemName); + AZ_Warning("Project Manager", false, error.toUtf8().constData()); + QMessageBox::warning(this, tr("Gem not found"), error.toUtf8().constData()); + } + + gemInfoHash.remove(gemName); + i++; + } + } + + // add all gems remaining in the hash that were not removed + for(auto iter = gemInfoHash.begin(); iter != gemInfoHash.end(); ++iter) + { + m_gemModel->AddGem(iter.value()); + } + + m_gemModel->UpdateGemDependencies(); + m_proxyModel->sort(/*column=*/0); + + // temporary, until we can refresh filter counts + m_proxyModel->ResetFilters(); + m_filterWidget->ResetAllFilters(); + } + void GemCatalogScreen::OnGemStatusChanged(const QString& gemName, uint32_t numChangedDependencies) { if (m_notificationsEnabled) @@ -237,20 +320,22 @@ namespace O3DE::ProjectManager void GemCatalogScreen::FillModel(const QString& projectPath) { - AZ::Outcome, AZStd::string> allGemInfosResult = PythonBindingsInterface::Get()->GetAllGemInfos(projectPath); + m_projectPath = projectPath; + + const AZ::Outcome, AZStd::string>& allGemInfosResult = PythonBindingsInterface::Get()->GetAllGemInfos(projectPath); if (allGemInfosResult.IsSuccess()) { // Add all available gems to the model. - const QVector allGemInfos = allGemInfosResult.GetValue(); + const QVector& allGemInfos = allGemInfosResult.GetValue(); for (const GemInfo& gemInfo : allGemInfos) { m_gemModel->AddGem(gemInfo); } - AZ::Outcome, AZStd::string> allRepoGemInfosResult = PythonBindingsInterface::Get()->GetAllGemRepoGemsInfos(); + const AZ::Outcome, AZStd::string>& allRepoGemInfosResult = PythonBindingsInterface::Get()->GetAllGemRepoGemsInfos(); if (allRepoGemInfosResult.IsSuccess()) { - const QVector allRepoGemInfos = allRepoGemInfosResult.GetValue(); + const QVector& allRepoGemInfos = allRepoGemInfosResult.GetValue(); for (const GemInfo& gemInfo : allRepoGemInfos) { // do not add gems that have already been downloaded @@ -269,10 +354,10 @@ namespace O3DE::ProjectManager m_notificationsEnabled = false; // Gather enabled gems for the given project. - auto enabledGemNamesResult = PythonBindingsInterface::Get()->GetEnabledGemNames(projectPath); + const auto& enabledGemNamesResult = PythonBindingsInterface::Get()->GetEnabledGemNames(projectPath); if (enabledGemNamesResult.IsSuccess()) { - const QVector enabledGemNames = enabledGemNamesResult.GetValue(); + const QVector& enabledGemNames = enabledGemNamesResult.GetValue(); for (const AZStd::string& enabledGemName : enabledGemNames) { const QModelIndex modelIndex = m_gemModel->FindIndexByNameString(enabledGemName.c_str()); @@ -332,12 +417,24 @@ namespace O3DE::ProjectManager for (const QModelIndex& modelIndex : toBeAdded) { - const QString gemPath = GemModel::GetPath(modelIndex); + const QString& gemPath = GemModel::GetPath(modelIndex); + + // make sure any remote gems we added were downloaded successfully + if (GemModel::GetGemOrigin(modelIndex) == GemInfo::Remote && GemModel::GetDownloadStatus(modelIndex) != GemInfo::Downloaded) + { + QMessageBox::critical( + nullptr, "Cannot add gem that isn't downloaded", + tr("Cannot add gem %1 to project because it isn't downloaded yet or failed to download.") + .arg(GemModel::GetDisplayName(modelIndex))); + + return EnableDisableGemsResult::Failed; + } + const AZ::Outcome result = pythonBindings->AddGemToProject(gemPath, projectPath); if (!result.IsSuccess()) { - QMessageBox::critical(nullptr, "Operation failed", - QString("Cannot add gem %1 to project.\n\nError:\n%2").arg(GemModel::GetDisplayName(modelIndex), result.GetError().c_str())); + QMessageBox::critical(nullptr, "Failed to add gem to project", + tr("Cannot add gem %1 to project.

Error:
%2").arg(GemModel::GetDisplayName(modelIndex), result.GetError().c_str())); return EnableDisableGemsResult::Failed; } @@ -355,8 +452,8 @@ namespace O3DE::ProjectManager const AZ::Outcome result = pythonBindings->RemoveGemFromProject(gemPath, projectPath); if (!result.IsSuccess()) { - QMessageBox::critical(nullptr, "Operation failed", - QString("Cannot remove gem %1 from project.\n\nError:\n%2").arg(GemModel::GetDisplayName(modelIndex), result.GetError().c_str())); + QMessageBox::critical(nullptr, "Failed to remove gem from project", + tr("Cannot remove gem %1 from project.

Error:
%2").arg(GemModel::GetDisplayName(modelIndex), result.GetError().c_str())); return EnableDisableGemsResult::Failed; } @@ -367,23 +464,35 @@ namespace O3DE::ProjectManager void GemCatalogScreen::HandleOpenGemRepo() { - QVector gemsToBeAdded = m_gemModel->GatherGemsToBeAdded(true); - QVector gemsToBeRemoved = m_gemModel->GatherGemsToBeRemoved(true); + emit ChangeScreenRequest(ProjectManagerScreen::GemRepos); + } - if (!gemsToBeAdded.empty() || !gemsToBeRemoved.empty()) + void GemCatalogScreen::OnGemDownloadResult(const QString& gemName, bool succeeded) + { + if (succeeded) { - QMessageBox::StandardButton warningResult = QMessageBox::warning( - nullptr, "Pending Changes", - "There are some unsaved changes to the gem selection,
they will be lost if you change screens.
Are you sure?", - QMessageBox::No | QMessageBox::Yes); - - if (warningResult != QMessageBox::Yes) + // refresh the information for downloaded gems + const AZ::Outcome, AZStd::string>& allGemInfosResult = PythonBindingsInterface::Get()->GetAllGemInfos(m_projectPath); + if (allGemInfosResult.IsSuccess()) { - return; + // we should find the gem name now in all gem infos + for (const GemInfo& gemInfo : allGemInfosResult.GetValue()) + { + if (gemInfo.m_name == gemName) + { + QModelIndex index = m_gemModel->FindIndexByNameString(gemName); + if (index.isValid()) + { + m_gemModel->setData(index, GemInfo::Downloaded, GemModel::RoleDownloadStatus); + m_gemModel->setData(index, gemInfo.m_path, GemModel::RolePath); + m_gemModel->setData(index, gemInfo.m_path, GemModel::RoleDirectoryLink); + } + + return; + } + } } } - - emit ChangeScreenRequest(ProjectManagerScreen::GemRepos); } ProjectManagerScreen GemCatalogScreen::GetScreenEnum() diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h index 55fbb1befc..da6d2efa7b 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h @@ -49,6 +49,8 @@ namespace O3DE::ProjectManager void OnGemStatusChanged(const QString& gemName, uint32_t numChangedDependencies); void OnAddGemClicked(); void SelectGem(const QString& gemName); + void OnGemDownloadResult(const QString& gemName, bool succeeded = true); + void Refresh(); protected: void hideEvent(QHideEvent* event) override; @@ -75,5 +77,6 @@ namespace O3DE::ProjectManager DownloadController* m_downloadController = nullptr; bool m_notificationsEnabled = true; QSet m_gemsToRegisterWithProject; + QString m_projectPath = nullptr; }; } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.cpp index b0b8cca29a..1bcfc6ce9d 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.cpp @@ -106,10 +106,10 @@ namespace O3DE::ProjectManager } // Depending gems - QStringList dependingGems = m_model->GetDependingGemNames(modelIndex); - if (!dependingGems.isEmpty()) + const QVector& dependingGemTags = m_model->GetDependingGemTags(modelIndex); + if (!dependingGemTags.isEmpty()) { - m_dependingGems->Update(tr("Depending Gems"), tr("The following Gems will be automatically enabled with this Gem."), dependingGems); + m_dependingGems->Update(tr("Depending Gems"), tr("The following Gems will be automatically enabled with this Gem."), dependingGemTags); m_dependingGems->show(); } else @@ -120,7 +120,8 @@ namespace O3DE::ProjectManager // Additional information 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_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->show(); @@ -222,7 +223,7 @@ namespace O3DE::ProjectManager // Depending gems m_dependingGems = new GemsSubWidget(); - connect(m_dependingGems, &GemsSubWidget::TagClicked, this, [=](const QString& tag){ emit TagClicked(tag); }); + connect(m_dependingGems, &GemsSubWidget::TagClicked, this, [=](const Tag& tag){ emit TagClicked(tag); }); m_mainLayout->addWidget(m_dependingGems); m_mainLayout->addSpacing(20); diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.h index c6548527ab..9a6ad84dea 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.h @@ -44,7 +44,7 @@ namespace O3DE::ProjectManager inline constexpr static const char* s_textColor = "#DDDDDD"; signals: - void TagClicked(const QString& tag); + void TagClicked(const Tag& tag); private slots: void OnSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected); diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp index 3f57aebf71..88c54de0b3 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp @@ -18,6 +18,7 @@ namespace O3DE::ProjectManager : QStandardItemModel(parent) { m_selectionModel = new QItemSelectionModel(this, parent); + connect(this, &QAbstractItemModel::rowsAboutToBeRemoved, this, &GemModel::OnRowsAboutToBeRemoved); } QItemSelectionModel* GemModel::GetSelectionModel() const @@ -64,7 +65,6 @@ namespace O3DE::ProjectManager appendRow(item); const QModelIndex modelIndex = index(rowCount()-1, 0); - m_nameToIndexMap[gemInfo.m_displayName] = modelIndex; m_nameToIndexMap[gemInfo.m_name] = modelIndex; } @@ -177,18 +177,6 @@ namespace O3DE::ProjectManager return {}; } - void GemModel::FindGemDisplayNamesByNameStrings(QStringList& inOutGemNames) - { - for (QString& name : inOutGemNames) - { - QModelIndex modelIndex = FindIndexByNameString(name); - if (modelIndex.isValid()) - { - name = GetDisplayName(modelIndex); - } - } - } - QStringList GemModel::GetDependingGems(const QModelIndex& modelIndex) { return modelIndex.data(RoleDependingGems).toStringList(); @@ -208,16 +196,23 @@ namespace O3DE::ProjectManager } } - QStringList GemModel::GetDependingGemNames(const QModelIndex& modelIndex) + QVector GemModel::GetDependingGemTags(const QModelIndex& modelIndex) { - QStringList result = GetDependingGems(modelIndex); - if (result.isEmpty()) + QVector tags; + + QStringList dependingGemNames = GetDependingGems(modelIndex); + tags.reserve(dependingGemNames.size()); + + for (QString& gemName : dependingGemNames) { - return {}; + const QModelIndex& dependingIndex = FindIndexByNameString(gemName); + if (dependingIndex.isValid()) + { + tags.push_back({ GetDisplayName(dependingIndex), GetName(dependingIndex) }); + } } - FindGemDisplayNamesByNameStrings(result); - return result; + return tags; } QString GemModel::GetVersion(const QModelIndex& modelIndex) @@ -372,6 +367,16 @@ namespace O3DE::ProjectManager gemModel->emit gemStatusChanged(gemName, numChangedDependencies); } + void GemModel::OnRowsAboutToBeRemoved(const QModelIndex& parent, int first, int last) + { + for (int i = first; i <= last; ++i) + { + QModelIndex modelIndex = index(i, 0, parent); + const QString& gemName = GetName(modelIndex); + m_nameToIndexMap.remove(gemName); + } + } + void GemModel::SetIsAddedDependency(QAbstractItemModel& model, const QModelIndex& modelIndex, bool isAdded) { model.setData(modelIndex, isAdded, RoleIsAddedDependency); diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h index 56594ce794..e25a1c7703 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h @@ -10,6 +10,7 @@ #if !defined(Q_MOC_RUN) #include +#include #include #include #include @@ -26,12 +27,39 @@ namespace O3DE::ProjectManager explicit GemModel(QObject* parent = nullptr); QItemSelectionModel* GetSelectionModel() const; + enum UserRole + { + RoleName = Qt::UserRole, + RoleDisplayName, + RoleCreator, + RoleGemOrigin, + RolePlatforms, + RoleSummary, + RoleWasPreviouslyAdded, + RoleWasPreviouslyAddedDependency, + RoleIsAdded, + RoleIsAddedDependency, + RoleDirectoryLink, + RoleDocLink, + RoleDependingGems, + RoleVersion, + RoleLastUpdated, + RoleBinarySize, + RoleFeatures, + RoleTypes, + RolePath, + RoleRequirement, + RoleDownloadStatus, + RoleLicenseText, + RoleLicenseLink + }; + void AddGem(const GemInfo& gemInfo); void Clear(); void UpdateGemDependencies(); QModelIndex FindIndexByNameString(const QString& nameString) const; - QStringList GetDependingGemNames(const QModelIndex& modelIndex); + QVector GetDependingGemTags(const QModelIndex& modelIndex); bool HasDependentGems(const QModelIndex& modelIndex) const; static QString GetName(const QModelIndex& modelIndex); @@ -82,38 +110,13 @@ namespace O3DE::ProjectManager signals: void gemStatusChanged(const QString& gemName, uint32_t numChangedDependencies); + protected slots: + void OnRowsAboutToBeRemoved(const QModelIndex& parent, int first, int last); + private: - void FindGemDisplayNamesByNameStrings(QStringList& inOutGemNames); void GetAllDependingGems(const QModelIndex& modelIndex, QSet& inOutGems); QStringList GetDependingGems(const QModelIndex& modelIndex); - enum UserRole - { - RoleName = Qt::UserRole, - RoleDisplayName, - RoleCreator, - RoleGemOrigin, - RolePlatforms, - RoleSummary, - RoleWasPreviouslyAdded, - RoleWasPreviouslyAddedDependency, - RoleIsAdded, - RoleIsAddedDependency, - RoleDirectoryLink, - RoleDocLink, - RoleDependingGems, - RoleVersion, - RoleLastUpdated, - RoleBinarySize, - RoleFeatures, - RoleTypes, - RolePath, - RoleRequirement, - RoleDownloadStatus, - RoleLicenseText, - RoleLicenseLink - }; - QHash m_nameToIndexMap; QItemSelectionModel* m_selectionModel = nullptr; QHash> m_gemDependencyMap; diff --git a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoInspector.cpp b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoInspector.cpp index 6655aef86d..f816e86733 100644 --- a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoInspector.cpp +++ b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoInspector.cpp @@ -86,7 +86,7 @@ namespace O3DE::ProjectManager } // Included Gems - m_includedGems->Update(tr("Included Gems"), "", m_model->GetIncludedGemNames(modelIndex)); + m_includedGems->Update(tr("Included Gems"), "", m_model->GetIncludedGemTags(modelIndex)); m_mainWidget->adjustSize(); m_mainWidget->show(); diff --git a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoModel.cpp b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoModel.cpp index 7a9617e6c1..6189b6d8bf 100644 --- a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoModel.cpp +++ b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoModel.cpp @@ -103,17 +103,17 @@ namespace O3DE::ProjectManager return modelIndex.data(RoleIncludedGems).toStringList(); } - QStringList GemRepoModel::GetIncludedGemNames(const QModelIndex& modelIndex) + QVector GemRepoModel::GetIncludedGemTags(const QModelIndex& modelIndex) { - QStringList gemNames; - QVector gemInfos = GetIncludedGemInfos(modelIndex); - + QVector tags; + const QVector& gemInfos = GetIncludedGemInfos(modelIndex); + tags.reserve(gemInfos.size()); for (const GemInfo& gemInfo : gemInfos) { - gemNames.append(gemInfo.m_displayName); + tags.append({ gemInfo.m_displayName, gemInfo.m_name }); } - return gemNames; + return tags; } QVector GemRepoModel::GetIncludedGemInfos(const QModelIndex& modelIndex) diff --git a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoModel.h b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoModel.h index f36b66ca48..66fe972a95 100644 --- a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoModel.h +++ b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoModel.h @@ -40,7 +40,7 @@ namespace O3DE::ProjectManager static QString GetPath(const QModelIndex& modelIndex); static QStringList GetIncludedGemPaths(const QModelIndex& modelIndex); - static QStringList GetIncludedGemNames(const QModelIndex& modelIndex); + static QVector GetIncludedGemTags(const QModelIndex& modelIndex); static QVector GetIncludedGemInfos(const QModelIndex& modelIndex); static bool IsEnabled(const QModelIndex& modelIndex); diff --git a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.cpp b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.cpp index 0ddfe41434..794635a3e3 100644 --- a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.cpp +++ b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.cpp @@ -52,6 +52,11 @@ namespace O3DE::ProjectManager Reinit(); } + void GemRepoScreen::NotifyCurrentScreen() + { + Reinit(); + } + void GemRepoScreen::Reinit() { m_gemRepoModel->clear(); @@ -91,6 +96,7 @@ namespace O3DE::ProjectManager if (addGemRepoResult) { Reinit(); + emit OnRefresh(); } else { @@ -116,6 +122,7 @@ namespace O3DE::ProjectManager if (removeGemRepoResult) { Reinit(); + emit OnRefresh(); } else { @@ -130,6 +137,7 @@ namespace O3DE::ProjectManager { bool refreshResult = PythonBindingsInterface::Get()->RefreshAllGemRepos(); Reinit(); + emit OnRefresh(); if (!refreshResult) { @@ -146,6 +154,7 @@ namespace O3DE::ProjectManager if (refreshResult.IsSuccess()) { Reinit(); + emit OnRefresh(); } else { diff --git a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.h b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.h index 46a733362a..eed9a5ec4a 100644 --- a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.h +++ b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.h @@ -28,6 +28,7 @@ namespace O3DE::ProjectManager class GemRepoScreen : public ScreenWidget { + Q_OBJECT public: explicit GemRepoScreen(QWidget* parent = nullptr); ~GemRepoScreen() = default; @@ -37,12 +38,18 @@ namespace O3DE::ProjectManager GemRepoModel* GetGemRepoModel() const { return m_gemRepoModel; } + void NotifyCurrentScreen() override; + + signals: + void OnRefresh(); + public slots: void HandleAddRepoButton(); void HandleRemoveRepoButton(const QModelIndex& modelIndex); void HandleRefreshAllButton(); void HandleRefreshRepoButton(const QModelIndex& modelIndex); + private: void FillModel(); QFrame* CreateNoReposContent(); diff --git a/Code/Tools/ProjectManager/Source/GemsSubWidget.cpp b/Code/Tools/ProjectManager/Source/GemsSubWidget.cpp index 8b7b183008..2572a39db3 100644 --- a/Code/Tools/ProjectManager/Source/GemsSubWidget.cpp +++ b/Code/Tools/ProjectManager/Source/GemsSubWidget.cpp @@ -33,14 +33,14 @@ namespace O3DE::ProjectManager m_layout->addWidget(m_textLabel); m_tagWidget = new TagContainerWidget(); - connect(m_tagWidget, &TagContainerWidget::TagClicked, this, [=](const QString& tag){ emit TagClicked(tag); }); + connect(m_tagWidget, &TagContainerWidget::TagClicked, this, [=](const Tag& tag){ emit TagClicked(tag); }); m_layout->addWidget(m_tagWidget); } - void GemsSubWidget::Update(const QString& title, const QString& text, const QStringList& gemNames) + void GemsSubWidget::Update(const QString& title, const QString& text, const QVector& tags) { m_titleLabel->setText(title); m_textLabel->setText(text); - m_tagWidget->Update(gemNames); + m_tagWidget->Update(tags); } } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemsSubWidget.h b/Code/Tools/ProjectManager/Source/GemsSubWidget.h index a9fabf5e92..5e670b930a 100644 --- a/Code/Tools/ProjectManager/Source/GemsSubWidget.h +++ b/Code/Tools/ProjectManager/Source/GemsSubWidget.h @@ -26,10 +26,10 @@ namespace O3DE::ProjectManager public: GemsSubWidget(QWidget* parent = nullptr); - void Update(const QString& title, const QString& text, const QStringList& gemNames); + void Update(const QString& title, const QString& text, const QVector& tags); signals: - void TagClicked(const QString& tag); + void TagClicked(const Tag& tag); private: QLabel* m_titleLabel = nullptr; diff --git a/Code/Tools/ProjectManager/Source/ProjectUtils.h b/Code/Tools/ProjectManager/Source/ProjectUtils.h index 1fdf76913e..890d50d2de 100644 --- a/Code/Tools/ProjectManager/Source/ProjectUtils.h +++ b/Code/Tools/ProjectManager/Source/ProjectUtils.h @@ -14,6 +14,7 @@ #include #include +#include #include namespace O3DE::ProjectManager @@ -67,7 +68,8 @@ namespace O3DE::ProjectManager AZ::Outcome GetProjectBuildPath(const QString& projectPath); AZ::Outcome OpenCMakeGUI(const QString& projectPath); AZ::Outcome RunGetPythonScript(const QString& enginePath); - + + AZ::IO::FixedMaxPath GetEditorDirectory(); } // namespace ProjectUtils } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ProjectsScreen.cpp b/Code/Tools/ProjectManager/Source/ProjectsScreen.cpp index cf42da88fa..f86a689e59 100644 --- a/Code/Tools/ProjectManager/Source/ProjectsScreen.cpp +++ b/Code/Tools/ProjectManager/Source/ProjectsScreen.cpp @@ -392,11 +392,11 @@ namespace O3DE::ProjectManager { if (!WarnIfInBuildQueue(projectPath)) { - AZ::IO::FixedMaxPath executableDirectory = AZ::Utils::GetExecutableDirectory(); + AZ::IO::FixedMaxPath executableDirectory = ProjectUtils::GetEditorDirectory(); AZStd::string executableFilename = "Editor"; AZ::IO::FixedMaxPath editorExecutablePath = executableDirectory / (executableFilename + AZ_TRAIT_OS_EXECUTABLE_EXTENSION); auto cmdPath = AZ::IO::FixedMaxPathString::format( - "%s -regset=\"/Amazon/AzCore/Bootstrap/project_path=%s\"", editorExecutablePath.c_str(), + "%s --regset=\"/Amazon/AzCore/Bootstrap/project_path=%s\"", editorExecutablePath.c_str(), projectPath.toStdString().c_str()); AzFramework::ProcessLauncher::ProcessLaunchInfo processLaunchInfo; diff --git a/Code/Tools/ProjectManager/Source/PythonBindings.cpp b/Code/Tools/ProjectManager/Source/PythonBindings.cpp index 905139a4f2..021066e1c7 100644 --- a/Code/Tools/ProjectManager/Source/PythonBindings.cpp +++ b/Code/Tools/ProjectManager/Source/PythonBindings.cpp @@ -53,6 +53,8 @@ namespace Platform #define Py_To_String(obj) pybind11::str(obj).cast().c_str() #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() +#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_Path(value) m_pathlib.attr("Path")(value.toStdString()) @@ -705,7 +707,9 @@ namespace O3DE::ProjectManager // optional gemInfo.m_displayName = Py_To_String_Optional(data, "display_name", gemInfo.m_name); 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_creator = Py_To_String_Optional(data, "origin", ""); gemInfo.m_documentationLink = Py_To_String_Optional(data, "documentation_url", ""); @@ -1175,6 +1179,7 @@ namespace O3DE::ProjectManager QString_To_Py_String(gemName), // gem name pybind11::none(), // destination path false, // skip auto register + false, // force pybind11::cpp_function( [this, gemProgressCallback](int progress) { diff --git a/Code/Tools/ProjectManager/Source/TagWidget.cpp b/Code/Tools/ProjectManager/Source/TagWidget.cpp index 39231ace4b..007f0839d1 100644 --- a/Code/Tools/ProjectManager/Source/TagWidget.cpp +++ b/Code/Tools/ProjectManager/Source/TagWidget.cpp @@ -12,15 +12,16 @@ namespace O3DE::ProjectManager { - TagWidget::TagWidget(const QString& text, QWidget* parent) - : QLabel(text, parent) + TagWidget::TagWidget(const Tag& tag, QWidget* parent) + : QLabel(tag.text, parent) + , m_tag(tag) { setObjectName("TagWidget"); } void TagWidget::mousePressEvent([[maybe_unused]] QMouseEvent* event) { - emit(TagClicked(text())); + emit TagClicked(m_tag); } TagContainerWidget::TagContainerWidget(QWidget* parent) @@ -39,20 +40,34 @@ namespace O3DE::ProjectManager void TagContainerWidget::Update(const QStringList& tags) { - FlowLayout* flowLayout = static_cast(layout()); + Clear(); - // remove old tags + foreach (const QString& tag, tags) + { + TagWidget* tagWidget = new TagWidget({tag, tag}); + connect(tagWidget, &TagWidget::TagClicked, this, [=](const Tag& clickedTag){ emit TagClicked(clickedTag); }); + layout()->addWidget(tagWidget); + } + } + + void TagContainerWidget::Update(const QVector& tags) + { + Clear(); + + foreach (const Tag& tag, tags) + { + TagWidget* tagWidget = new TagWidget(tag); + connect(tagWidget, &TagWidget::TagClicked, this, [=](const Tag& clickedTag){ emit TagClicked(clickedTag); }); + layout()->addWidget(tagWidget); + } + } + + void TagContainerWidget::Clear() + { QLayoutItem* layoutItem = nullptr; while ((layoutItem = layout()->takeAt(0)) != nullptr) { layoutItem->widget()->deleteLater(); } - - foreach (const QString& tag, tags) - { - TagWidget* tagWidget = new TagWidget(tag); - connect(tagWidget, &TagWidget::TagClicked, this, [=](const QString& tag){ emit TagClicked(tag); }); - flowLayout->addWidget(tagWidget); - } } } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/TagWidget.h b/Code/Tools/ProjectManager/Source/TagWidget.h index 7b4a5b1aaa..fce6eaf863 100644 --- a/Code/Tools/ProjectManager/Source/TagWidget.h +++ b/Code/Tools/ProjectManager/Source/TagWidget.h @@ -10,12 +10,19 @@ #if !defined(Q_MOC_RUN) #include -#include #include +#include +#include #endif namespace O3DE::ProjectManager { + struct Tag + { + QString text; + QString id; + }; + // Single tag class TagWidget : public QLabel @@ -23,14 +30,17 @@ namespace O3DE::ProjectManager Q_OBJECT // AUTOMOC public: - explicit TagWidget(const QString& text, QWidget* parent = nullptr); + explicit TagWidget(const Tag& id, QWidget* parent = nullptr); ~TagWidget() = default; signals: - void TagClicked(const QString& tag); + void TagClicked(const Tag& tag); protected: void mousePressEvent(QMouseEvent* event) override; + + private: + Tag m_tag; }; // Widget containing multiple tags, automatically wrapping based on the size @@ -43,9 +53,13 @@ namespace O3DE::ProjectManager explicit TagContainerWidget(QWidget* parent = nullptr); ~TagContainerWidget() = default; + void Update(const QVector& tags); void Update(const QStringList& tags); signals: - void TagClicked(const QString& tag); + void TagClicked(const Tag& tag); + + private: + void Clear(); }; } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp b/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp index 1c8f9a6931..fb16484961 100644 --- a/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp +++ b/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp @@ -7,6 +7,7 @@ */ #include +#include #include #include #include @@ -39,10 +40,10 @@ namespace O3DE::ProjectManager m_updateSettingsScreen = new UpdateProjectSettingsScreen(); m_gemCatalogScreen = new GemCatalogScreen(); + m_gemRepoScreen = new GemRepoScreen(this); - connect(m_gemCatalogScreen, &ScreenWidget::ChangeScreenRequest, this, [this](ProjectManagerScreen screen){ - emit ChangeScreenRequest(screen); - }); + connect(m_gemCatalogScreen, &ScreenWidget::ChangeScreenRequest, this, &UpdateProjectCtrl::OnChangeScreenRequest); + connect(m_gemRepoScreen, &GemRepoScreen::OnRefresh, m_gemCatalogScreen, &GemCatalogScreen::Refresh); m_stack = new QStackedWidget(this); m_stack->setObjectName("body"); @@ -69,6 +70,7 @@ namespace O3DE::ProjectManager m_stack->addWidget(topBarFrameWidget); m_stack->addWidget(m_gemCatalogScreen); + m_stack->addWidget(m_gemRepoScreen); QDialogButtonBox* backNextButtons = new QDialogButtonBox(); backNextButtons->setObjectName("footer"); @@ -100,6 +102,22 @@ namespace O3DE::ProjectManager // Gather the available gems that will be shown in the gem catalog. m_gemCatalogScreen->ReinitForProject(m_projectInfo.m_path); + + // make sure the gem repo has the latest repo details + m_gemRepoScreen->Reinit(); + } + + void UpdateProjectCtrl::OnChangeScreenRequest(ProjectManagerScreen screen) + { + if (screen == ProjectManagerScreen::GemRepos) + { + m_stack->setCurrentWidget(m_gemRepoScreen); + Update(); + } + else + { + emit ChangeScreenRequest(screen); + } } void UpdateProjectCtrl::HandleGemsButton() @@ -145,6 +163,7 @@ namespace O3DE::ProjectManager QMessageBox::critical(this, tr("Gems downloading"), tr("You must wait for gems to finish downloading before continuing.")); return; } + // Enable or disable the gems that got adjusted in the gem catalog and apply them to the given project. const GemCatalogScreen::EnableDisableGemsResult result = m_gemCatalogScreen->EnableDisableGemsForProject(m_projectInfo.m_path); if (result == GemCatalogScreen::EnableDisableGemsResult::Failed) @@ -181,18 +200,26 @@ namespace O3DE::ProjectManager void UpdateProjectCtrl::Update() { - if (m_stack->currentIndex() == ScreenOrder::Gems) + if (m_stack->currentIndex() == ScreenOrder::GemRepos) + { + m_header->setTitle(QString(tr("Edit Project Settings: \"%1\"")).arg(m_projectInfo.GetProjectDisplayName())); + m_header->setSubTitle(QString(tr("Gem Repositories"))); + m_nextButton->setVisible(false); + } + else if (m_stack->currentIndex() == ScreenOrder::Gems) { m_header->setTitle(QString(tr("Edit Project Settings: \"%1\"")).arg(m_projectInfo.GetProjectDisplayName())); m_header->setSubTitle(QString(tr("Configure Gems"))); m_nextButton->setText(tr("Save")); + m_nextButton->setVisible(true); } else { m_header->setTitle(""); m_header->setSubTitle(QString(tr("Edit Project Settings: \"%1\"")).arg(m_projectInfo.GetProjectDisplayName())); m_nextButton->setText(tr("Save")); + m_nextButton->setVisible(true); } } diff --git a/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.h b/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.h index 3321fad638..ee6fc792f2 100644 --- a/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.h +++ b/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.h @@ -22,9 +22,11 @@ namespace O3DE::ProjectManager QT_FORWARD_DECLARE_CLASS(ScreenHeader) QT_FORWARD_DECLARE_CLASS(UpdateProjectSettingsScreen) QT_FORWARD_DECLARE_CLASS(GemCatalogScreen) + QT_FORWARD_DECLARE_CLASS(GemRepoScreen) class UpdateProjectCtrl : public ScreenWidget { + Q_OBJECT public: explicit UpdateProjectCtrl(QWidget* parent = nullptr); ~UpdateProjectCtrl() = default; @@ -37,6 +39,7 @@ namespace O3DE::ProjectManager void HandleBackButton(); void HandleNextButton(); void HandleGemsButton(); + void OnChangeScreenRequest(ProjectManagerScreen screen); void UpdateCurrentProject(const QString& projectPath); private: @@ -47,13 +50,15 @@ namespace O3DE::ProjectManager enum ScreenOrder { Settings, - Gems + Gems, + GemRepos }; ScreenHeader* m_header = nullptr; QStackedWidget* m_stack = nullptr; UpdateProjectSettingsScreen* m_updateSettingsScreen = nullptr; GemCatalogScreen* m_gemCatalogScreen = nullptr; + GemRepoScreen* m_gemRepoScreen = nullptr; QPushButton* m_backButton = nullptr; QPushButton* m_nextButton = nullptr; diff --git a/Code/Tools/PythonBindingsExample/source/Application.cpp b/Code/Tools/PythonBindingsExample/source/Application.cpp index ab1d1b5acf..cfb28d8e09 100644 --- a/Code/Tools/PythonBindingsExample/source/Application.cpp +++ b/Code/Tools/PythonBindingsExample/source/Application.cpp @@ -39,7 +39,6 @@ namespace PythonBindingsExample AzToolsFramework::EditorPythonConsoleNotificationBus::Handler::BusConnect(); // prepare the Python binding gem(s) - CalculateExecutablePath(); Start(Descriptor()); AZ::SerializeContext* context; diff --git a/Code/Tools/SceneAPI/SceneCore/Tests/Containers/SceneBehaviorTests.cpp b/Code/Tools/SceneAPI/SceneCore/Tests/Containers/SceneBehaviorTests.cpp index 3bf75e2d9b..0478e6cdb2 100644 --- a/Code/Tools/SceneAPI/SceneCore/Tests/Containers/SceneBehaviorTests.cpp +++ b/Code/Tools/SceneAPI/SceneCore/Tests/Containers/SceneBehaviorTests.cpp @@ -368,7 +368,6 @@ namespace AZ::SceneAPI::Containers MOCK_METHOD0(GetSerializeContext, AZ::SerializeContext* ()); MOCK_METHOD0(GetJsonRegistrationContext, AZ::JsonRegistrationContext* ()); MOCK_METHOD0(GetBehaviorContext, AZ::BehaviorContext* ()); - MOCK_CONST_METHOD0(GetAppRoot, const char*()); MOCK_CONST_METHOD0(GetEngineRoot, const char*()); MOCK_CONST_METHOD0(GetExecutableFolder, const char* ()); MOCK_CONST_METHOD1(QueryApplicationType, void(AZ::ApplicationTypeQuery&)); diff --git a/Code/Tools/SceneAPI/SceneUI/CommonWidgets/ProcessingOverlayWidget.cpp b/Code/Tools/SceneAPI/SceneUI/CommonWidgets/ProcessingOverlayWidget.cpp index bc9c4679df..a1326b0c82 100644 --- a/Code/Tools/SceneAPI/SceneUI/CommonWidgets/ProcessingOverlayWidget.cpp +++ b/Code/Tools/SceneAPI/SceneUI/CommonWidgets/ProcessingOverlayWidget.cpp @@ -63,7 +63,7 @@ namespace AZ } ProcessingOverlayWidget::ProcessingOverlayWidget(UI::OverlayWidget* overlay, Layout layout, Uuid traceTag) - : QWidget() + : QWidget(nullptr, Qt::Tool | Qt::WindowStaysOnTopHint) , m_traceTag(traceTag) , ui(new Ui::ProcessingOverlayWidget()) , m_overlay(overlay) diff --git a/Code/Tools/SerializeContextTools/Converter.cpp b/Code/Tools/SerializeContextTools/Converter.cpp index 6a8bebdbfc..294bf941ac 100644 --- a/Code/Tools/SerializeContextTools/Converter.cpp +++ b/Code/Tools/SerializeContextTools/Converter.cpp @@ -202,8 +202,6 @@ namespace AZ bool skipSystem = commandLine->HasSwitch("skipsystem"); bool isDryRun = commandLine->HasSwitch("dryrun"); - const char* appRoot = const_cast(application).GetAppRoot(); - PathDocumentContainer documents; bool result = true; const AZStd::string& filePath = application.GetConfigFilePath(); @@ -230,7 +228,7 @@ namespace AZ } 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) { if (classId == azrtti_typeid()) @@ -238,7 +236,7 @@ namespace AZ if (!skipSystem) { result = ConvertSystemSettings(documents, *reinterpret_cast(classPtr), - configurationName, sourceGameFolder, appRoot) && result; + configurationName, sourceGameFolder) && result; } // 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, - 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 }; memoryFilePath /= "Registry"; diff --git a/Code/Tools/SerializeContextTools/Converter.h b/Code/Tools/SerializeContextTools/Converter.h index 6c8f6c70fb..7d30ca2a3a 100644 --- a/Code/Tools/SerializeContextTools/Converter.h +++ b/Code/Tools/SerializeContextTools/Converter.h @@ -43,7 +43,7 @@ namespace AZ using PathDocumentContainer = AZStd::vector; 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, const AZStd::string& configurationName, const AZ::IO::PathView& projectFolder, const JsonSerializerSettings& convertSettings, const JsonDeserializerSettings& verifySettings); diff --git a/Gems/AWSClientAuth/Code/Tests/AWSClientAuthGemMock.h b/Gems/AWSClientAuth/Code/Tests/AWSClientAuthGemMock.h index 1d2e8bad42..19314035c4 100644 --- a/Gems/AWSClientAuth/Code/Tests/AWSClientAuthGemMock.h +++ b/Gems/AWSClientAuth/Code/Tests/AWSClientAuthGemMock.h @@ -608,7 +608,6 @@ namespace AWSClientAuthUnitTest AZ::Entity* FindEntity(const AZ::EntityId&) override { return nullptr; } AZ::BehaviorContext* GetBehaviorContext() override { return nullptr; } const char* GetExecutableFolder() const override { return nullptr; } - const char* GetAppRoot() const override { return nullptr; } const char* GetEngineRoot() const override { return nullptr; } void EnumerateEntities(const EntityCallback& /*callback*/) override {} void QueryApplicationType(AZ::ApplicationTypeQuery& /*appType*/) const override {} diff --git a/Gems/AssetValidation/Code/Source/AssetValidationSystemComponent.cpp b/Gems/AssetValidation/Code/Source/AssetValidationSystemComponent.cpp index 56f28856af..8f8fb48212 100644 --- a/Gems/AssetValidation/Code/Source/AssetValidationSystemComponent.cpp +++ b/Gems/AssetValidation/Code/Source/AssetValidationSystemComponent.cpp @@ -439,13 +439,6 @@ namespace AssetValidation bool GetDefaultSeedListFiles(AZStd::vector& 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(); AZ::SettingsRegistryInterface::FixedValueString gameFolder; auto projectKey = AZ::SettingsRegistryInterface::FixedValueString::format("%s/project_path", AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey); @@ -509,30 +502,28 @@ namespace AssetValidation AZ::Outcome AssetValidationSystemComponent::LoadSeedList(const char* seedPath, AZStd::string& seedListPath) { - AZStd::string absoluteSeedPath = seedPath; + AZ::IO::Path absoluteSeedPath = seedPath; if (AZ::StringFunc::Path::IsRelative(seedPath)) { - const char* appRoot = nullptr; - AzFramework::ApplicationRequests::Bus::BroadcastResult(appRoot, &AzFramework::ApplicationRequests::GetEngineRoot); + AZ::IO::FixedMaxPath engineRoot = AZ::Utils::GetEnginePath(); - if (!appRoot) + if (engineRoot.empty()) { 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; - 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())); } - seedListPath = absoluteSeedPath; + seedListPath = AZStd::move(absoluteSeedPath.Native()); return AZ::Success(seedList); } diff --git a/Gems/AssetValidation/Code/Tests/AssetValidationTestShared.h b/Gems/AssetValidation/Code/Tests/AssetValidationTestShared.h index c4872ab425..8da3ae1b34 100644 --- a/Gems/AssetValidation/Code/Tests/AssetValidationTestShared.h +++ b/Gems/AssetValidation/Code/Tests/AssetValidationTestShared.h @@ -150,13 +150,13 @@ struct AssetValidationTest auto projectPathKey = AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/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); // Set the engine root to the temporary directory and re-update the runtime file paths auto enginePathKey = AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/engine_path"; - m_registry.Set(enginePathKey, GetEngineRoot()); + m_registry.Set(enginePathKey, m_tempDir.GetDirectory()); AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(m_registry); } } @@ -176,11 +176,6 @@ struct AssetValidationTest AZ_Assert(false, "Not implemented"); } - const char* GetEngineRoot() const override - { - return m_tempDir.GetDirectory(); - } - void SetUp() override { using namespace ::testing; diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Include/Atom/ImageProcessing/ImageObject.h b/Gems/Atom/Asset/ImageProcessingAtom/Code/Include/Atom/ImageProcessing/ImageObject.h index 5796a0c84d..475c72c0eb 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Include/Atom/ImageProcessing/ImageObject.h +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Include/Atom/ImageProcessing/ImageObject.h @@ -100,13 +100,6 @@ namespace ImageProcessingAtom //compare whether two images are same. return true if they are same. virtual bool CompareImage(const IImageObjectPtr otherImage) const = 0; - // Writes this image to file used for runtime, overwrites any existing file. - // It may write alpha image as attached image into the same file - // outFilePaths will save filenames finally saved to since the image might be split and saved to multiple files - virtual bool SaveImage(const char* filename, IImageObjectPtr alphaImage, AZStd::vector& outFilePaths) const = 0; - virtual bool SaveImage(AZ::IO::SystemFileStream& out) const = 0; - virtual bool SaveMipToFile(AZ::u32 mip, const AZStd::string& filename) const = 0; - //get total image data size in memory of all mipmaps. Not includs header and flags. virtual AZ::u32 GetTextureMemory() const = 0; @@ -135,9 +128,6 @@ namespace ImageProcessingAtom // The algorithm is based on the Frequency Domain Normal Mapping implementation presented by Neubelt and Pettineo at Siggraph 2013. virtual void GlossFromNormals(bool hasAuthoredGloss) = 0; - //convert gloss map from legacy distribution to new one. New World is still using legacy gloss map. - virtual void ConvertLegacyGloss() = 0; - //clear image with color virtual void ClearColor(float r, float g, float b, float a) = 0; }; diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/BuilderSettings/PresetSettings.cpp b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/BuilderSettings/PresetSettings.cpp index 3812b86026..046c4faa87 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/BuilderSettings/PresetSettings.cpp +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/BuilderSettings/PresetSettings.cpp @@ -45,10 +45,7 @@ namespace ImageProcessingAtom ->Field("MinTextureSize", &PresetSettings::m_minTextureSize) ->Field("IsPowerOf2", &PresetSettings::m_isPowerOf2) ->Field("SizeReduceLevel", &PresetSettings::m_sizeReduceLevel) - ->Field("IsColorChart", &PresetSettings::m_isColorChart) - ->Field("HighPassMip", &PresetSettings::m_highPassMip) ->Field("GlossFromNormal", &PresetSettings::m_glossFromNormals) - ->Field("UseLegacyGloss", &PresetSettings::m_isLegacyGloss) ->Field("MipRenormalize", &PresetSettings::m_isMipRenormalize) ->Field("NumberResidentMips", &PresetSettings::m_numResidentMips) ->Field("Swizzle", &PresetSettings::m_swizzle) @@ -200,10 +197,7 @@ namespace ImageProcessingAtom m_maxTextureSize == other.m_maxTextureSize && m_isPowerOf2 == other.m_isPowerOf2 && m_sizeReduceLevel == other.m_sizeReduceLevel && - m_isColorChart == other.m_isColorChart && - m_highPassMip == other.m_highPassMip && m_glossFromNormals == other.m_glossFromNormals && - m_isLegacyGloss == other.m_isLegacyGloss && m_swizzle == other.m_swizzle && m_isMipRenormalize == other.m_isMipRenormalize && m_numResidentMips == other.m_numResidentMips; @@ -239,10 +233,7 @@ namespace ImageProcessingAtom m_maxTextureSize = other.m_maxTextureSize; m_isPowerOf2 = other.m_isPowerOf2; m_sizeReduceLevel = other.m_sizeReduceLevel; - m_isColorChart = other.m_isColorChart; - m_highPassMip = other.m_highPassMip; m_glossFromNormals = other.m_glossFromNormals; - m_isLegacyGloss = other.m_isLegacyGloss; m_swizzle = other.m_swizzle; m_isMipRenormalize = other.m_isMipRenormalize; m_numResidentMips = other.m_numResidentMips; diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/BuilderSettings/PresetSettings.h b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/BuilderSettings/PresetSettings.h index 941437bbf4..3dc223cf80 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/BuilderSettings/PresetSettings.h +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/BuilderSettings/PresetSettings.h @@ -84,16 +84,7 @@ namespace ImageProcessingAtom //settings for mipmap generation. it's null if this preset disable mipmap. AZStd::unique_ptr m_mipmapSetting; - - //some specific settings - // "colorchart". This is to indicate if need to extract color chart from the image and output the color chart data. - // This is very specific usage for cryEngine. Check ColorChart.cpp for better explanation. - bool m_isColorChart = false; - - //"highpass". Defines which mip level is subtracted when applying the high pass filter - //this is only used for terrain asset. we might remove it later since it can be done with source image directly - AZ::u32 m_highPassMip = 0; - + //"glossfromnormals". Bake normal variance into smoothness stored in alpha channel AZ::u32 m_glossFromNormals = 0; @@ -109,10 +100,6 @@ namespace ImageProcessingAtom //that add up to 64K or lower AZ::u8 m_numResidentMips = 0; - //legacy options might be removed later - //"glosslegacydist". If the gloss map use legacy distribution. NW is still using legacy dist - bool m_isLegacyGloss = false; - //"swizzle". need to be 4 character and each character need to be one of "rgba01" AZStd::string m_swizzle; diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Converters/ColorChart.cpp b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Converters/ColorChart.cpp deleted file mode 100644 index b6e9cf4c8c..0000000000 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Converters/ColorChart.cpp +++ /dev/null @@ -1,307 +0,0 @@ -/* - * 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 -#include - -namespace ImageProcessingAtom -{ - const int COLORCHART_IMAGE_WIDTH = 78; - const int COLORCHART_IMAGE_HEIGHT = 66; - - // color chart in cry engine is a special image data, with size 78x66, you may see in game screenshot which is defined by a rectangle - // area with a yellow-black dash line boarder - // Create color chart function is to read that block of image data and convert it to a color table then save it to another image - // with size 256x16. - - class C3dLutColorChart - { - public: - C3dLutColorChart() {} - ~C3dLutColorChart() {}; - - //generate default color chart data - void GenerateDefault(); - - //generate color chart data from input image - bool GenerateFromInput(IImageObjectPtr image); - - //ouput the color chart data to an image object - IImageObjectPtr GenerateChartImage(); - - protected: - //extract color chart data from specified location in an image - void ExtractFromImageAt(IImageObjectPtr pImg, AZ::u32 x, AZ::u32 y); - - //find color chart location in an image - static bool FindColorChart(const IImageObjectPtr pImg, AZ::u32& outLocX, AZ::u32& outLocY); - - //if there is a color chart at specified location - static bool IsColorChartAt(AZ::u32 x, AZ::u32 y, void* pData, AZ::u32 pitch); - - private: - enum EPrimaryShades - { - ePS_Red = 16, - ePS_Green = 16, - ePS_Blue = 16, - - ePS_NumColors = ePS_Red * ePS_Green * ePS_Blue - }; - - struct SColor - { - unsigned char r, g, b, _padding; - }; - - typedef AZStd::vector ColorMapping; - - ColorMapping m_mapping; - }; - - void C3dLutColorChart::GenerateDefault() - { - m_mapping.reserve(ePS_NumColors); - - for (int b = 0; b < ePS_Blue; ++b) - { - for (int g = 0; g < ePS_Green; ++g) - { - for (int r = 0; r < ePS_Red; ++r) - { - SColor col; - col.r = static_cast(255 * r / (ePS_Red)); - col.g = static_cast(255 * g / (ePS_Green)); - col.b = static_cast(255 * b / (ePS_Blue)); - int l = 255 - (col.r * 3 + col.g * 6 + col.b) / 10; - col.r = col.g = col.b = (unsigned char)l; - m_mapping.push_back(col); - } - } - } - } - - //find color chart location in a image - bool C3dLutColorChart::FindColorChart(const IImageObjectPtr pImg, AZ::u32& outLocX, AZ::u32& outLocY) - { - const AZ::u32 width = pImg->GetWidth(0); - const AZ::u32 height = pImg->GetHeight(0); - - //the origin image is too small to have a color chart - if (width < COLORCHART_IMAGE_WIDTH || height < COLORCHART_IMAGE_HEIGHT) - { - return false; - } - - AZ::u8* pData; - AZ::u32 pitch; - pImg->GetImagePointer(0, pData, pitch); - - //check all the posible start location on whether there might be a color chart - for (AZ::u32 y = 0; y <= height - COLORCHART_IMAGE_HEIGHT; ++y) - { - for (AZ::u32 x = 0; x <= width - COLORCHART_IMAGE_WIDTH; ++x) - { - if (IsColorChartAt(x, y, pData, pitch)) - { - outLocX = x; - outLocY = y; - return true; - } - } - } - - return false; - } - - bool C3dLutColorChart::GenerateFromInput(IImageObjectPtr image) - { - AZ::u32 outLocX, outLocY; - if (FindColorChart(image, outLocX, outLocY)) - { - ExtractFromImageAt(image, outLocX, outLocY); - return true; - } - return false; - } - - IImageObjectPtr C3dLutColorChart::GenerateChartImage() - { - IImageObjectPtr image(IImageObject::CreateImage(ePS_Red* ePS_Blue, ePS_Green, 1, ePixelFormat_R8G8B8A8)); - - { - AZ::u8* pData; - AZ::u32 pitch; - image->GetImagePointer(0, pData, pitch); - - size_t nSlicePitch = (pitch / ePS_Blue); - AZ::u32 src = 0; - for (int b = 0; b < ePS_Blue; ++b) - { - for (int g = 0; g < ePS_Green; ++g) - { - AZ::u8* p = pData + g * pitch + b * nSlicePitch; - for (int r = 0; r < ePS_Red; ++r) - { - const SColor& c = m_mapping[src]; - p[0] = c.r; - p[1] = c.g; - p[2] = c.b; - p[3] = 255; - ++src; - p += 4; - } - } - } - } - - return image; - } - - void C3dLutColorChart::ExtractFromImageAt(IImageObjectPtr image, AZ::u32 x, AZ::u32 y) - { - int ox = x + 1; - int oy = y + 1; - - AZ::u8* pData; - AZ::u32 pitch; - image->GetImagePointer(0, pData, pitch); - - m_mapping.reserve(ePS_NumColors); - - for (int b = 0; b < ePS_Blue; ++b) - { - int px = ox + ePS_Red * (b % 4); - int py = oy + ePS_Green * (b / 4); - - for (int g = 0; g < ePS_Green; ++g) - { - for (int r = 0; r < ePS_Red; ++r) - { - AZ::u8* p = pData + pitch * (py + g) + (px + r) * 4; - - SColor col; - col.r = p[0]; - col.g = p[1]; - col.b = p[2]; - m_mapping.push_back(col); - } - } - } - } - - //check if image data at location x and y could be a color chart - //based on if the boarder is dash lines with two pixel each segement - //the idea and implementation are both coming from CryEngine. - bool C3dLutColorChart::IsColorChartAt(AZ::u32 x, AZ::u32 y, void* pData, AZ::u32 pitch) - { - struct Color - { - private: - int c[3]; - - public: - Color(AZ::u32 x, AZ::u32 y, void* pPixels, AZ::u32 pitch) - { - const uint8* p = (const uint8*)pPixels + pitch * y + x * 4; - c[0] = p[0]; - c[1] = p[1]; - c[2] = p[2]; - } - - bool isSimilar(const Color& a, int maxDiff) const - { - return - abs(a.c[0] - c[0]) <= maxDiff && - abs(a.c[1] - c[1]) <= maxDiff && - abs(a.c[2] - c[2]) <= maxDiff; - } - }; - - const Color colorRef[2] = - { - Color(x, y, pData, pitch), - Color(x + 2, y, pData, pitch) - }; - - // We require two colors of the border to be at least a bit different - if (colorRef[0].isSimilar(colorRef[1], 15)) - { - return false; - } - - static const int kMaxDiff = 3; - - int refIdx = 0; - //rectangle's top - for (int i = 0; i < COLORCHART_IMAGE_WIDTH; i += 2) - { - if (!colorRef[refIdx].isSimilar(Color(x + i, y, pData, pitch), kMaxDiff) || - !colorRef[refIdx].isSimilar(Color(x + i + 1, y, pData, pitch), kMaxDiff)) - { - return false; - } - refIdx ^= 1; - } - - refIdx = 0; - //left - for (int i = 0; i < COLORCHART_IMAGE_HEIGHT; i += 2) - { - if (!colorRef[refIdx].isSimilar(Color(x, y + i, pData, pitch), kMaxDiff) || - !colorRef[refIdx].isSimilar(Color(x, y + i + 1, pData, pitch), kMaxDiff)) - { - return false; - } - refIdx ^= 1; - } - - refIdx = 0; - //right - for (int i = 0; i < COLORCHART_IMAGE_HEIGHT; i += 2) - { - if (!colorRef[refIdx].isSimilar(Color(x + COLORCHART_IMAGE_WIDTH - 1, y + i, pData, pitch), kMaxDiff) || - !colorRef[refIdx].isSimilar(Color(x + COLORCHART_IMAGE_WIDTH - 1, y + i + 1, pData, pitch), kMaxDiff)) - { - return false; - } - refIdx ^= 1; - } - - refIdx = 0; - //bottom - for (int i = 0; i < COLORCHART_IMAGE_WIDTH; i += 2) - { - if (!colorRef[refIdx].isSimilar(Color(x + i, y + COLORCHART_IMAGE_HEIGHT - 1, pData, pitch), kMaxDiff) || - !colorRef[refIdx].isSimilar(Color(x + i + 1, y + COLORCHART_IMAGE_HEIGHT - 1, pData, pitch), kMaxDiff)) - { - return false; - } - refIdx ^= 1; - } - - return true; - } - - - void ImageToProcess::CreateColorChart() - { - C3dLutColorChart colorChart; - - //get color chart data from source image. - if (!colorChart.GenerateFromInput(m_img)) - { - //if load from image failed then generate default color data - colorChart.GenerateDefault(); - } - - //save color chart data to an image and save as current - m_img = colorChart.GenerateChartImage(); - } -} diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Converters/Cubemap.cpp b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Converters/Cubemap.cpp index 4f13c6f9bf..d373f399e1 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Converters/Cubemap.cpp +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Converters/Cubemap.cpp @@ -547,7 +547,7 @@ namespace ImageProcessingAtom } //generate box filtered source image mip chain - IImageObjectPtr mippedSourceImage(IImageObject::CreateImage(outWidth, outHeight, maxMipCount, ePixelFormat_R32G32B32A32F)); + IImageObjectPtr mippedSourceImage(IImageObject::CreateImage(outWidth, outHeight, maxMipCount, srcPixelFormat)); mippedSourceImage->CopyPropertiesFrom(m_image->Get()); for (int iSide = 0; iSide < 6; ++iSide) diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Converters/HighPass.cpp b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Converters/HighPass.cpp deleted file mode 100644 index e2071fb586..0000000000 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Converters/HighPass.cpp +++ /dev/null @@ -1,100 +0,0 @@ -/* - * 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 -#include -#include -#include -#include -#include - -namespace ImageProcessingAtom -{ - // higher mip level is subtracted by lower mip level when applying the [cheap] high pass filter - void ImageToProcess::CreateHighPass(AZ::u32 dwMipDown) - { - //no need to convert if mip go down 0 - if (dwMipDown == 0) - { - return; - } - - const EPixelFormat ePixelFormat = m_img->GetPixelFormat(); - - if (ePixelFormat != ePixelFormat_R32G32B32A32F) - { - AZ_Assert(false, "You need convert the orginal image to ePixelFormat_R32G32B32A32F before call this function"); - return; - } - - - AZ::u32 dwWidth, dwHeight, dwMips; - dwWidth = m_img->GetWidth(0); - dwHeight = m_img->GetHeight(0); - dwMips = m_img->GetMipCount(); - - if (dwMipDown >= dwMips) - { - AZ_Warning("Image Processing", false, "CreateHighPass can't go down %i MIP levels for high pass as there are not\ - enough MIP levels available, going down by %i instead", dwMipDown, dwMips - 1); - dwMipDown = dwMips - 1; - } - - IImageObjectPtr newImage(IImageObject::CreateImage(dwWidth, dwHeight, dwMips, ePixelFormat)); - newImage->CopyPropertiesFrom(m_img); - - IPixelOperationPtr pixelOp = CreatePixelOperation(ePixelFormat); - AZ::u32 pixelBytes = CPixelFormats::GetInstance().GetPixelFormatInfo(ePixelFormat)->bitsPerBlock / 8; - - AZ::u32 dstMips = newImage->GetMipCount(); - for (AZ::u32 dstMip = 0; dstMip < dwMipDown; ++dstMip) - { - // linear interpolation - FilterImage(MipGenType::triangle, MipGenEvalType::sum, 0.0f, 0.0f, m_img, dwMipDown, newImage, dstMip, NULL, NULL); - - //substraction - AZ::u8* srcPixelBuf; - AZ::u32 srcPitch; - m_img->GetImagePointer(dstMip, srcPixelBuf, srcPitch); - AZ::u8* dstPixelBuf; - AZ::u32 dstPitch; - newImage->GetImagePointer(dstMip, dstPixelBuf, dstPitch); - const AZ::u32 pixelCount = newImage->GetPixelCount(dstMip); - - for (AZ::u32 i = 0; i < pixelCount; ++i, srcPixelBuf += pixelBytes, dstPixelBuf += pixelBytes) - { - float r1, g1, b1, a1, r2, g2, b2, a2; - pixelOp->GetRGBA(srcPixelBuf, r1, g1, b1, a1); - pixelOp->GetRGBA(dstPixelBuf, r2, g2, b2, a2); - - r2 = AZ::GetClamp(r1 - r2 + 0.5f, 0.0f, 1.0f); - g2 = AZ::GetClamp(g1 - g2 + 0.5f, 0.0f, 1.0f); - b2 = AZ::GetClamp(b1 - b2 + 0.5f, 0.0f, 1.0f); - a2 = AZ::GetClamp(a1 - a2 + 0.5f, 0.0f, 1.0f); - pixelOp->SetRGBA(dstPixelBuf, r2, g2, b2, a2); - } - } - - // mips below the chosen highpass mip are grey - for (AZ::u32 dstMip = dwMipDown; dstMip < dstMips; ++dstMip) - { - AZ::u8* dstPixelBuf; - AZ::u32 dstPitch; - newImage->GetImagePointer(dstMip, dstPixelBuf, dstPitch); - const AZ::u32 pixelCount = newImage->GetPixelCount(dstMip); - - for (AZ::u32 i = 0; i < pixelCount; ++i, dstPixelBuf += pixelBytes) - { - pixelOp->SetRGBA(dstPixelBuf, 0.5f, 0.5f, 0.5f, 1.0f); - } - } - - m_img = newImage; - } -} // namespace ImageProcessingAtom diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Editor/PresetInfoPopup.cpp b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Editor/PresetInfoPopup.cpp index a5b942fa58..fe5703ceb6 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Editor/PresetInfoPopup.cpp +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Editor/PresetInfoPopup.cpp @@ -82,10 +82,7 @@ namespace ImageProcessingAtomEditor presetInfoText += "\n"; presetInfoText += QString("Suppress Engine Reduce: %1\n").arg(presetSettings->m_suppressEngineReduce ? "True" : "False"); presetInfoText += QString("Discard Alpha: %1\n").arg(presetSettings->m_discardAlpha ? "True" : "False"); - presetInfoText += QString("Is Color Chart: %1\n").arg(presetSettings->m_isColorChart ? "True" : "False"); - presetInfoText += QString("High Pass Mip: %1\n").arg(presetSettings->m_highPassMip); presetInfoText += QString("Gloss From Normal: %1\n").arg(presetSettings->m_glossFromNormals); - presetInfoText += QString("Use Legacy Gloss: %1\n").arg(presetSettings->m_isLegacyGloss ? "True" : "False"); presetInfoText += QString("Mip Re-normalize: %1\n").arg(presetSettings->m_isMipRenormalize ? "True" : "False"); presetInfoText += QString("Resident Mips Number: %1\n").arg(presetSettings->m_numResidentMips); presetInfoText += QString("Swizzle: %1\n").arg(presetSettings->m_swizzle.c_str()); diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/ImageBuilderComponent.cpp b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/ImageBuilderComponent.cpp index a489c8da5e..90d45ae65a 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/ImageBuilderComponent.cpp +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/ImageBuilderComponent.cpp @@ -74,7 +74,7 @@ namespace ImageProcessingAtom builderDescriptor.m_busId = azrtti_typeid(); builderDescriptor.m_createJobFunction = AZStd::bind(&ImageBuilderWorker::CreateJobs, &m_imageBuilder, AZStd::placeholders::_1, AZStd::placeholders::_2); builderDescriptor.m_processJobFunction = AZStd::bind(&ImageBuilderWorker::ProcessJob, &m_imageBuilder, AZStd::placeholders::_1, AZStd::placeholders::_2); - builderDescriptor.m_version = 25; // [ATOM-16575] + builderDescriptor.m_version = 26; // [ATOM-15086] builderDescriptor.m_analysisFingerprint = ImageProcessingAtom::BuilderSettingManager::Instance()->GetAnalysisFingerprint(); m_imageBuilder.BusConnect(builderDescriptor.m_busId); AssetBuilderSDK::AssetBuilderBus::Broadcast(&AssetBuilderSDK::AssetBuilderBusTraits::RegisterBuilderInformation, builderDescriptor); diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageConvert.cpp b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageConvert.cpp index 977359e4f6..5b23009cff 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageConvert.cpp +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageConvert.cpp @@ -46,7 +46,6 @@ namespace ImageProcessingAtom enum ConvertStep { StepValidateInput = 0, - StepGenerateColorChart, StepConvertToLinear, StepSwizzle, StepCubemapLayout, @@ -55,9 +54,7 @@ namespace ImageProcessingAtom StepMipmap, StepGlossFromNormal, StepPostNormalize, - StepCreateHighPass, StepConvertOutputColorSpace, - StepAlphaImage, StepConvertPixelFormat, StepSaveToFile, StepAll @@ -66,7 +63,6 @@ namespace ImageProcessingAtom [[maybe_unused]] const char ProcessStepNames[StepAll][64] = { "ValidateInput", - "GenerateColorChart", "ConvertToLinear", "Swizzle", "CubemapLayout", @@ -75,9 +71,7 @@ namespace ImageProcessingAtom "Mipmap", "GlossFromNormal", "PostNormalize", - "CreateHighPass", "ConvertOutputColorSpace", - "AlphaImage", "ConvertPixelFormat", "SaveToFile", }; @@ -94,11 +88,6 @@ namespace ImageProcessingAtom return nullptr; } - IImageObjectPtr ImageConvertProcess::GetOutputAlphaImage() - { - return m_alphaImage; - } - IImageObjectPtr ImageConvertProcess::GetOutputIBLSpecularCubemap() { return m_iblSpecularCubemapImage; @@ -180,6 +169,58 @@ namespace ImageProcessingAtom m_image = new ImageToProcess(IImageObjectPtr(m_input->m_inputImage->Clone(mipsToClone))); } + break; + case StepConvertToLinear: + // convert to linear space and the output image pixel format should be rgba32f + ConvertToLinear(); + break; + case StepSwizzle: + { + // swizzle if swizzle was set or decard alpha + bool swizzleWasSet = m_input->m_presetSetting.m_swizzle.size() >= 4; + if (swizzleWasSet || m_input->m_presetSetting.m_discardAlpha) + { + AZStd::string swizzle = "rgba"; + if (swizzleWasSet) + { + swizzle = m_input->m_presetSetting.m_swizzle.substr(0, 4); + } + + if (m_input->m_presetSetting.m_discardAlpha) + { + swizzle[3] = '1'; + } + + m_image->Get()->Swizzle(swizzle.c_str()); + if (!m_input->m_presetSetting.m_discardAlpha) + { + m_alphaContent = EAlphaContent::eAlphaContent_Absent; + } + else + { + m_alphaContent = m_image->Get()->GetAlphaContent(); + } + } + } + break; + case StepCubemapLayout: + // convert cubemap image's layout to vertical strip used in game. + if (IsConvertToCubemap()) + { + if (!m_image->ConvertCubemapLayout(CubemapLayoutVertical)) + { + m_image->Set(nullptr); + } + } + break; + case StepPreNormalize: + // normalize base image before mipmap generation if glossfromnormals is enabled and require normalize + if (m_input->m_presetSetting.m_isMipRenormalize && m_input->m_presetSetting.m_glossFromNormals) + { + // Normalize the base mip map. This has to be done explicitly because we need to disable mip renormalization to + // preserve the normal length when deriving the normal variance + m_image->Get()->NormalizeVectors(0, 1); + } break; case StepGenerateIBL: if (IsConvertToCubemap()) @@ -204,56 +245,6 @@ namespace ImageProcessingAtom m_isFinished = true; } break; - case StepGenerateColorChart: - // GenerateColorChart. - if (m_input->m_presetSetting.m_isColorChart) - { - // Convert to uncompressed format if it's compressed format. For example, loaded from DDS file. - if (!CPixelFormats::GetInstance().IsPixelFormatUncompressed(m_image->Get()->GetPixelFormat())) - { - m_image->ConvertFormat(ePixelFormat_R32G32B32A32F); - } - - m_image->CreateColorChart(); - } - break; - case StepConvertToLinear: - // convert to linear space and the output image pixel format should be rgba32f - ConvertToLinear(); - break; - case StepSwizzle: - // convert texture format. - if (m_input->m_presetSetting.m_swizzle.size() >= 4) - { - m_image->Get()->Swizzle(m_input->m_presetSetting.m_swizzle.substr(0, 4).c_str()); - m_alphaContent = m_image->Get()->GetAlphaContent(); - } - - // convert gloss map (alhpa channel) from legacy distribution to new one - if (m_input->m_presetSetting.m_isLegacyGloss) - { - m_image->Get()->ConvertLegacyGloss(); - } - break; - case StepCubemapLayout: - // convert cubemap image's layout to vertical strip used in game. - if (IsConvertToCubemap()) - { - if (!m_image->ConvertCubemapLayout(CubemapLayoutVertical)) - { - m_image->Set(nullptr); - } - } - break; - case StepPreNormalize: - // normalize base image before mipmap generation if glossfromnormals is enabled and require normalize - if (m_input->m_presetSetting.m_isMipRenormalize && m_input->m_presetSetting.m_glossFromNormals) - { - // Normalize the base mip map. This has to be done explicitly because we need to disable mip renormalization to - // preserve the normal length when deriving the normal variance - m_image->Get()->NormalizeVectors(0, 1); - } - break; case StepMipmap: // generate mipmaps if (IsConvertToCubemap()) @@ -304,20 +295,10 @@ namespace ImageProcessingAtom m_image->Get()->AddImageFlags(EIF_RenormalizedTexture); } break; - case StepCreateHighPass: - if (m_input->m_presetSetting.m_highPassMip > 0) - { - m_image->CreateHighPass(m_input->m_presetSetting.m_highPassMip); - } - break; case StepConvertOutputColorSpace: // convert image from linear space to desired output color space ConvertToOuputColorSpace(); break; - case StepAlphaImage: - // save alpha channel to separate image if it's needed - CreateAlphaImage(); - break; case StepConvertPixelFormat: // convert pixel format ConvertPixelformat(); @@ -411,12 +392,6 @@ namespace ImageProcessingAtom return; } - // don't do any reduce for color chart - if (presetSettings->m_isColorChart) - { - return; - } - // get suitable size for dest pixel format CPixelFormats::GetInstance().GetSuitableImageSize(presetSettings->m_pixelFormat, inputWidth, inputHeight, outWidth, outHeight); @@ -510,52 +485,6 @@ namespace ImageProcessingAtom return true; } - void ImageConvertProcess::CreateAlphaImage() - { - // if alpha content doesn't have alpha or we need to discard alpha, skip - // we won't create alpha image for cubemap too - if (m_alphaContent == EAlphaContent::eAlphaContent_Absent - || m_alphaContent == EAlphaContent::eAlphaContent_OnlyWhite - || m_input->m_presetSetting.m_discardAlpha || IsConvertToCubemap()) - { - return; - } - - // if dest format could save alpha, skip too - if (!CPixelFormats::GetInstance().IsPixelFormatWithoutAlpha(m_input->m_presetSetting.m_pixelFormat)) - { - return; - } - - // now create alpha image - ImageToProcess alphaImage(m_image->Get()); - alphaImage.ConvertFormat(ePixelFormat_A8); - - // validate pixelformatalpha - if (CPixelFormats::GetInstance().IsFormatSingleChannel(m_input->m_presetSetting.m_pixelFormatAlpha)) - { - alphaImage.ConvertFormat(m_input->m_presetSetting.m_pixelFormatAlpha); - } - else - { - //For ASTC compression we need to clear out the alpha to get accurate rgb compression. - if (IsASTCFormat(m_input->m_presetSetting.m_pixelFormat)) - { - alphaImage.ConvertFormat(ePixelFormat_R8G8B8X8); - alphaImage.ConvertFormat(m_input->m_presetSetting.m_pixelFormatAlpha); - } - else - { - AZ_Assert(false, "PixelFormatAlpha only supports single channel pixel formats or ASTC formats"); - } - } - - // get final result and save it to member variable for later use - m_alphaImage = alphaImage.Get(); - - m_image->Get()->AddImageFlags(EIF_AttachedAlpha); - } - // pixel format conversion bool ImageConvertProcess::ConvertPixelformat() { @@ -575,12 +504,6 @@ namespace ImageProcessingAtom m_image->GetCompressOption().rgbWeight = m_input->m_presetSetting.GetColorWeight(); m_image->GetCompressOption().discardAlpha = m_input->m_presetSetting.m_discardAlpha; - //For ASTC compression we need to clear out the alpha to get accurate rgb compression. - if(m_alphaImage && IsASTCFormat(m_input->m_presetSetting.m_pixelFormat)) - { - m_image->GetCompressOption().discardAlpha = true; - } - m_image->ConvertFormat(m_input->m_presetSetting.m_pixelFormat); return true; @@ -762,7 +685,6 @@ namespace ImageProcessingAtom if (ImageProcess##PrivateName::DoesSupport(m_input->m_platform)) \ { \ ImageProcess##PrivateName::PrepareImageForExport(m_image->Get()); \ - ImageProcess##PrivateName::PrepareImageForExport(m_alphaImage); \ } AZ_TOOLS_EXPAND_FOR_RESTRICTED_PLATFORMS #undef AZ_RESTRICTED_PLATFORM_EXPANSION diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageConvert.h b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageConvert.h index eaf05c3280..fe57b1a89f 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageConvert.h +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageConvert.h @@ -115,7 +115,6 @@ namespace ImageProcessingAtom //get output images IImageObjectPtr GetOutputImage(); - IImageObjectPtr GetOutputAlphaImage(); IImageObjectPtr GetOutputIBLSpecularCubemap(); IImageObjectPtr GetOutputIBLDiffuseCubemap(); @@ -131,8 +130,6 @@ namespace ImageProcessingAtom //for alpha //to indicate the current alpha channel content EAlphaContent m_alphaContent; - //An image object to hold alpha channel in a separate image - IImageObjectPtr m_alphaImage; //output results of IBL cubemap generation, used in unit tests IImageObjectPtr m_iblSpecularCubemapImage; @@ -171,9 +168,6 @@ namespace ImageProcessingAtom //convert to output color space before compression bool ConvertToOuputColorSpace(); - //create alpha image if it's needed - void CreateAlphaImage(); - //pixel format convertion/compression bool ConvertPixelformat(); diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageConvertJob.cpp b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageConvertJob.cpp index 1465e7993f..0e4521ab24 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageConvertJob.cpp +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageConvertJob.cpp @@ -108,17 +108,14 @@ namespace ImageProcessingAtom } IImageObjectPtr outputImage = m_process->GetOutputImage(); - IImageObjectPtr outputImageAlpha = m_process->GetOutputAlphaImage(); m_output->SetOutputImage(outputImage, ImageConvertOutput::Base); - m_output->SetOutputImage(outputImageAlpha, ImageConvertOutput::Alpha); if (!IsJobCancelled()) { // For preview, combine image output with alpha if any m_output->SetProgress(1.0f / static_cast(m_previewProcessStep)); - IImageObjectPtr combinedImage = MergeOutputImageForPreview(outputImage, outputImageAlpha); - m_output->SetOutputImage(combinedImage, ImageConvertOutput::Preview); + m_output->SetOutputImage(outputImage, ImageConvertOutput::Preview); } m_output->SetReady(true); diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageFlags.h b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageFlags.h index 99e752c90a..d25533cccd 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageFlags.h +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageFlags.h @@ -20,7 +20,7 @@ namespace ImageProcessingAtom const static AZ::u32 EIF_Greyscale = 0x8; // hint for the engine (e.g. greyscale light beams can be applied to shadow mask), can be for DXT1 because compression artfacts don't count as color const static AZ::u32 EIF_SupressEngineReduce = 0x10; // info for the engine: don't reduce texture resolution on this texture const static AZ::u32 EIF_UNUSED_BIT = 0x40; // Free to use - const static AZ::u32 EIF_AttachedAlpha = 0x400; // info for the engine: it's a texture with attached alpha channel + const static AZ::u32 EIF_AttachedAlpha = 0x400; // deprecated: info for the engine: it's a texture with attached alpha channel const static AZ::u32 EIF_SRGBRead = 0x800; // info for the engine: if gamma corrected rendering is on, this texture requires SRGBRead (it's not stored in linear) const static AZ::u32 EIF_DontResize = 0x8000; // info for the engine: for dds textures that shouldn't be resized const static AZ::u32 EIF_RenormalizedTexture = 0x10000; // info for the engine: for dds textures that have renormalized color range diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageObjectImpl.cpp b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageObjectImpl.cpp index 8504f5e074..465d992ef6 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageObjectImpl.cpp +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageObjectImpl.cpp @@ -316,130 +316,6 @@ namespace ImageProcessingAtom m_mips.clear(); } - //note: there are some unreasonable parts of the save files formats for cry textures. We might need to rethink about - // it for new renderer - bool CImageObject::SaveImage(const char* filename, IImageObjectPtr alphaImage, AZStd::vector& outFilePaths) const - { - AZ::IO::SystemFile file; - file.Open(filename, AZ::IO::SystemFile::SF_OPEN_CREATE | AZ::IO::SystemFile::SF_OPEN_CREATE_PATH | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY); - - AZ::IO::SystemFileStream fileSaveStream(&file, true); - if (!fileSaveStream.IsOpen()) - { - AZ_Warning("Image Processing", false, "%s: failed to create file %s", __FUNCTION__, filename); - return false; - } - - if (alphaImage) - { - AZ_Assert(HasImageFlags(EIF_AttachedAlpha), "attached alpha image flag wasn't set"); - AZ_Assert(!alphaImage->HasImageFlags(EIF_AttachedAlpha), "alpha image shouldn't have attached alpha image flag"); - - // inherit cubemap and decal image flags to attached alpha image - alphaImage->AddImageFlags(GetImageFlags() & (EIF_Cubemap - | EIF_Decal | EIF_Splitted)); - alphaImage->SetNumPersistentMips(m_numPersistentMips); - } - - bool bOk = SaveImage(fileSaveStream); - bool hasSplitFlag = HasImageFlags(EIF_Splitted); - - //append alpha image data in the end if there is no split - if (bOk && alphaImage && !hasSplitFlag) - { - //4 bytes extension tag, 4 bytes attached alpha tag, then 4 bytes of chunk size - fileSaveStream.Write(sizeof(FOURCC_CExt), &FOURCC_CExt); // marker for the start of O3DE Extended data - fileSaveStream.Write(sizeof(FOURCC_AttC), &FOURCC_AttC); // Attached Channel chunk - - uint32_t size = 0; - uint32_t sizeBytes = sizeof(size); - fileSaveStream.Write(sizeBytes, &size); //size of attached chunk - - //save alpha image and get the size - AZ::IO::SizeType startPos = fileSaveStream.GetCurPos(); - bOk = alphaImage->SaveImage(fileSaveStream); - AZ::IO::SizeType endPos = fileSaveStream.GetCurPos(); - size = static_cast(endPos - startPos); - - //move back to beginning of chunk and write chunk size then move back to end - fileSaveStream.Seek(startPos - sizeBytes, AZ::IO::GenericStream::ST_SEEK_BEGIN); - fileSaveStream.Write(sizeBytes, &size); - fileSaveStream.Seek(endPos, AZ::IO::GenericStream::ST_SEEK_BEGIN); - - // marker for the end of O3DE Extended data - fileSaveStream.Write(sizeof(FOURCC_CEnd), &FOURCC_CEnd); - } - - if (!bOk) - { - AZ::IO::SystemFile::Delete(filename); - return false; - } - - // It's important to maintain the product output sequence. Asset Database/Browser will use the first product to determine the source type! - outFilePaths.push_back(filename); - - // save stand alone products - if (hasSplitFlag) - { - // alpha - if (alphaImage) - { - AZStd::string alphaFile = AZStd::string::format("%s.a", filename); - - AZ::IO::SystemFile outAlphaFile; - outAlphaFile.Open(alphaFile.c_str(), AZ::IO::SystemFile::SF_OPEN_CREATE | AZ::IO::SystemFile::SF_OPEN_CREATE_PATH | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY); - - AZ::IO::SystemFileStream alphaFileSaveStream(&outAlphaFile, true); - - if (alphaFileSaveStream.IsOpen()) - { - alphaImage->SaveImage(alphaFileSaveStream); - outFilePaths.push_back(alphaFile); - } - else - { - AZ_Warning("Image Processing", false, "%s: failed to create file %s", __FUNCTION__, alphaFile.c_str()); - } - } - - // mips - AZ::u32 numStreamable = GetMipCount() - m_numPersistentMips; - for (AZ::u32 mip = 0; mip < numStreamable; mip++) - { - AZ::u32 nameIdx = numStreamable - mip; - AZStd::string mipFileName = AZStd::string::format("%s.%d", filename, nameIdx); - SaveMipToFile(mip, mipFileName); - outFilePaths.push_back(mipFileName); - if (alphaImage) - { - AZStd::string mipAlphaFileName = mipFileName + "a"; - alphaImage->SaveMipToFile(mip, mipAlphaFileName); - outFilePaths.push_back(mipAlphaFileName); - } - } - } - - return bOk; - } - - bool CImageObject::SaveMipToFile(AZ::u32 mip, const AZStd::string& filename) const - { - AZ::IO::SystemFile saveFile; - saveFile.Open(filename.c_str(), AZ::IO::SystemFile::SF_OPEN_CREATE | AZ::IO::SystemFile::SF_OPEN_CREATE_PATH | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY); - - AZ::IO::SystemFileStream saveFileStream(&saveFile, true); - - if (!saveFileStream.IsOpen()) - { - AZ_Warning("Image Processing", false, "%s: failed to create file %s", __FUNCTION__, filename.c_str()); - return false; - } - - saveFileStream.Write(GetMipBufSize(mip), m_mips[mip]->m_pData); - return true; - } - float CImageObject::CalculateAverageBrightness() const { //if it's compressed format, return a default value @@ -642,63 +518,6 @@ namespace ImageProcessingAtom return true; } - bool CImageObject::SaveImage(AZ::IO::SystemFileStream& saveFileStream) const - { - DDS_FILE_DESC_LEGACY desc; - DDS_HEADER_DXT10 exthead; - - desc.dwMagic = FOURCC_DDS; - - if (!BuildSurfaceHeader(desc.header)) - { - return false; - } - - if (desc.header.IsDX10Ext() && !BuildSurfaceExtendedHeader(exthead)) - { - return false; - } - - saveFileStream.Write(sizeof(desc), &desc); - - if (desc.header.IsDX10Ext()) - { - saveFileStream.Write(sizeof(exthead), &exthead); - } - - AZ::u32 faces = 1; - - //for cubemap. export each face and its mipmap - if (HasImageFlags(EIF_Cubemap)) - { - faces = 6; - } - - AZ::u32 mipStart = 0; - if (HasImageFlags(EIF_Splitted)) - { - if (m_numPersistentMips < m_mips.size()) - { - mipStart = (AZ::u32)m_mips.size() - m_numPersistentMips; - } - else - { - AZ_Assert(false, "numPersistentMips wasn't setup correctly"); - } - } - - for (AZ::u32 face = 0; face < faces; face++) - { - for (AZ::u32 mip = mipStart; mip < m_mips.size(); ++mip) - { - const MipLevel& level = *m_mips[mip]; - AZ::u32 faceBufSize = level.m_pitch * level.m_rowCount / faces; - saveFileStream.Write(faceBufSize, level.m_pData + faceBufSize * face); - } - } - return true; - } - void CImageObject::GetExtent(AZ::u32& width, AZ::u32& height, AZ::u32& mipCount) const { mipCount = (AZ::u32)m_mips.size(); @@ -953,35 +772,4 @@ namespace ImageProcessingAtom } } } - - void CImageObject::ConvertLegacyGloss() - { - if (!(CPixelFormats::GetInstance().IsPixelFormatUncompressed(m_pixelFormat))) - { - AZ_Assert(false, "%s function only works with uncompressed pixel format", __FUNCTION__); - return; - } - - //create pixel operation function - IPixelOperationPtr pixelOp = CreatePixelOperation(m_pixelFormat); - - //get count of bytes per pixel - AZ::u32 pixelBytes = CPixelFormats::GetInstance().GetPixelFormatInfo(m_pixelFormat)->bitsPerBlock / 8; - - const AZ::u32 mips = (AZ::u32)m_mips.size(); - float color[4]; - for (AZ::u32 mip = 0; mip < mips; ++mip) - { - AZ::u8* pixelBuf = m_mips[mip]->m_pData; - const AZ::u32 pixelCount = GetPixelCount(mip); - - for (AZ::u32 i = 0; i < pixelCount; ++i, pixelBuf += pixelBytes) - { - pixelOp->GetRGBA(pixelBuf, color[0], color[1], color[2], color[3]); - // Convert from (1 - s * 0.7)^6 to (1 - s)^2 - color[3] = 1 - pow(1.0f - color[3] * 0.7f, 3.0f); - pixelOp->SetRGBA(pixelBuf, color[0], color[1], color[2], color[3]); - } - } - } } // namespace ImageProcessingAtom diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageObjectImpl.h b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageObjectImpl.h index c8b8ced496..7fa02f464e 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageObjectImpl.h +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageObjectImpl.h @@ -57,10 +57,6 @@ namespace ImageProcessingAtom bool CompareImage(const IImageObjectPtr otherImage) const override; - bool SaveImage(const char* filename, IImageObjectPtr alphaImage, AZStd::vector& outFilePaths) const override; - bool SaveImage(AZ::IO::SystemFileStream& out) const override; - bool SaveMipToFile(AZ::u32 mip, const AZStd::string& filename) const override; - uint32_t GetTextureMemory() const override; EAlphaContent GetAlphaContent() const override; @@ -79,7 +75,6 @@ namespace ImageProcessingAtom void SetNumPersistentMips(AZ::u32 nMips) override; void GlossFromNormals(bool hasAuthoredGloss) override; - void ConvertLegacyGloss() override; void ClearColor(float r, float g, float b, float a) override; //end virtual functions from IImageObject diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageToProcess.h b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageToProcess.h index e6fe6ce142..ed0b21b56c 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageToProcess.h +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageToProcess.h @@ -66,13 +66,6 @@ namespace ImageProcessingAtom bool GammaToLinearRGBA32F(bool bDeGamma); void LinearToGamma(); - // --------------------------------------------------------------------------------- - // Tools for A32B32G32R32F - - void CreateHighPass(uint32 dwMipDown); - - void CreateColorChart(); - //convert various original cubemap layouts to new layout bool ConvertCubemapLayout(CubemapLayoutType newLayout); }; diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Tests/ImageProcessing_Test.cpp b/Gems/Atom/Asset/ImageProcessingAtom/Code/Tests/ImageProcessing_Test.cpp index 3e0bbd89eb..d0029ffc86 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Tests/ImageProcessing_Test.cpp +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Tests/ImageProcessing_Test.cpp @@ -111,7 +111,6 @@ namespace UnitTest AZ::SerializeContext* GetSerializeContext() override { return m_context.get(); } AZ::BehaviorContext* GetBehaviorContext() override { return nullptr; } AZ::JsonRegistrationContext* GetJsonRegistrationContext() override { return m_jsonRegistrationContext.get(); } - const char* GetAppRoot() const override { return nullptr; } const char* GetEngineRoot() const override { return nullptr; } const char* GetExecutableFolder() const override { return nullptr; } void EnumerateEntities(const AZ::ComponentApplicationRequests::EntityCallback& /*callback*/) override {} @@ -988,7 +987,6 @@ namespace UnitTest ASSERT_TRUE(process->IsSucceed()); SaveImageToFile(process->GetOutputImage(), "rgb", 10); - SaveImageToFile(process->GetOutputAlphaImage(), "alpha", 10); process->GetAppendOutputProducts(outProducts); diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/imageprocessing_files.cmake b/Gems/Atom/Asset/ImageProcessingAtom/Code/imageprocessing_files.cmake index aad400518d..a6fe09bfa6 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/imageprocessing_files.cmake +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/imageprocessing_files.cmake @@ -103,8 +103,6 @@ set(FILES Source/Converters/ConvertPixelFormat.cpp Source/Converters/Cubemap.h Source/Converters/Cubemap.cpp - Source/Converters/ColorChart.cpp - Source/Converters/HighPass.cpp Source/Converters/Histogram.cpp Source/Converters/Histogram.h ../External/CubeMapGen/CBBoxInt32.cpp diff --git a/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.cpp b/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.cpp index c232d4bba7..84fc58718c 100644 --- a/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.cpp +++ b/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.cpp @@ -272,6 +272,7 @@ namespace AZ // Create and register a scene with all available feature processors RPI::SceneDescriptor sceneDesc; + sceneDesc.m_nameId = AZ::Name("Main"); AZ::RPI::ScenePtr atomScene = RPI::Scene::CreateScene(sceneDesc); atomScene->EnableAllFeatureProcessors(); atomScene->Activate(); diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ForwardPass.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ForwardPass.azsl index a53dab7a01..8f58c6dd33 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ForwardPass.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ForwardPass.azsl @@ -153,7 +153,7 @@ struct StandardMaterialInputs float2 m_vertexUv[UvSetCount]; float3x3 m_uvMatrix; - float m_normal; + float3 m_normal; float3 m_tangents[UvSetCount]; float3 m_bitangents[UvSetCount]; diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/BackLighting.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/BackLighting.azsli index a4a747d42c..83cf79ed61 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/BackLighting.azsli +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/BackLighting.azsli @@ -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" // 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)) * - saturate(dot(-surface.normal, dirToLight)) * lightIntensity * shadowRatio; + float litRatio = 1.0 - shadowRatio; + if (litRatio) + { + result = TransmissionKernel(surface.transmission.thickness * transmissionParams.w, rcp(transmissionParams.xyz)) * + saturate(dot(-surface.normal, dirToLight)) * lightIntensity * litRatio; + } + break; } diff --git a/Gems/Atom/Feature/Common/Code/Source/LuxCore/LuxCoreRenderer.cpp b/Gems/Atom/Feature/Common/Code/Source/LuxCore/LuxCoreRenderer.cpp index c753079d5b..ef3f33d1c3 100644 --- a/Gems/Atom/Feature/Common/Code/Source/LuxCore/LuxCoreRenderer.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/LuxCore/LuxCoreRenderer.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -295,14 +296,12 @@ namespace AZ } // Run luxcoreui.exe - AZStd::string luxCoreExeFullPath; - AzFramework::ApplicationRequests::Bus::BroadcastResult(luxCoreExeFullPath, &AzFramework::ApplicationRequests::GetAppRoot); - luxCoreExeFullPath = luxCoreExeFullPath + AZ_TRAIT_LUXCORE_EXEPATH; - AzFramework::StringFunc::Path::Normalize(luxCoreExeFullPath); + AZ::IO::FixedMaxPath luxCoreExeFullPath = AZ::Utils::GetEnginePath(); + luxCoreExeFullPath /= AZ_TRAIT_LUXCORE_EXEPATH; AZStd::string commandLine = "-o " + AZStd::string(resolvedPath) + "/render.cfg"; - LuxCoreUI::LaunchLuxCoreUI(luxCoreExeFullPath, commandLine); + LuxCoreUI::LaunchLuxCoreUI(luxCoreExeFullPath.String(), commandLine); } } } diff --git a/Gems/Atom/Feature/Common/Code/Source/LuxCore/LuxCoreTexture.cpp b/Gems/Atom/Feature/Common/Code/Source/LuxCore/LuxCoreTexture.cpp index aa906a06f7..1fd8f0d91c 100644 --- a/Gems/Atom/Feature/Common/Code/Source/LuxCore/LuxCoreTexture.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/LuxCore/LuxCoreTexture.cpp @@ -32,7 +32,7 @@ namespace AZ { if (m_rtPipeline) { - AZ::RPI::RPISystemInterface::Get()->GetDefaultScene()->RemoveRenderPipeline(m_rtPipeline->GetId()); + m_rtPipeline->RemoveFromScene(); m_rtPipeline = nullptr; } @@ -111,8 +111,12 @@ namespace AZ parentPass->SetSourceTexture(m_texture, RHI::Format::R8G8B8A8_UNORM); break; } - - AZ::RPI::RPISystemInterface::Get()->GetDefaultScene()->AddRenderPipeline(m_rtPipeline); + + const auto mainScene = AZ::RPI::RPISystemInterface::Get()->GetSceneByName(AZ::Name("RPI")); + if (mainScene) + { + mainScene->AddRenderPipeline(m_rtPipeline); + } } bool LuxCoreTexture::IsIBLTexture() diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/RPISystem.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/RPISystem.h index 4914e4b6fe..9138c0b418 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/RPISystem.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/RPISystem.h @@ -70,7 +70,8 @@ namespace AZ void InitializeSystemAssets() override; void RegisterScene(ScenePtr scene) override; void UnregisterScene(ScenePtr scene) override; - ScenePtr GetScene(const SceneId& sceneId) const override; + Scene* GetScene(const SceneId& sceneId) const override; + Scene* GetSceneByName(const AZ::Name& name) const override; ScenePtr GetDefaultScene() const override; RenderPipelinePtr GetRenderPipelineForWindow(AzFramework::NativeWindowHandle windowHandle) override; Data::Asset GetCommonShaderAssetForSrgs() const override; @@ -96,8 +97,7 @@ namespace AZ // SystemTickBus::OnTick void OnSystemTick() override; - // Fill system time and game time information for simulation or rendering - void FillTickTimeInfo(); + float GetCurrentTime(); // The set of core asset handlers registered by the system. AZStd::vector> m_assetHandlers; @@ -123,7 +123,8 @@ namespace AZ // The job policy used for feature processor's rendering prepare RHI::JobPolicy m_prepareRenderJobPolicy = RHI::JobPolicy::Parallel; - TickTimeInfo m_tickTime; + ScriptTimePoint m_startTime; + float m_currentSimulationTime = 0.0f; RPISystemDescriptor m_descriptor; diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/RPISystemInterface.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/RPISystemInterface.h index fe81596bd7..3f30d498cf 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/RPISystemInterface.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/RPISystemInterface.h @@ -13,6 +13,7 @@ #include +#include #include namespace AZ @@ -46,11 +47,14 @@ namespace AZ //! Unregister a scene from RPISystem. The scene won't be simulated or rendered. virtual void UnregisterScene(ScenePtr scene) = 0; - // [GFX TODO] to be removed when we have scene setup in AZ Core - virtual ScenePtr GetDefaultScene() const = 0; - + //! Deprecated. Use GetSceneByName(name), GetSceneForEntityContextId(entityContextId) or Scene::GetSceneForEntityId(AZ::EntityId entityId) instead + AZ_DEPRECATED(virtual ScenePtr GetDefaultScene() const = 0;, "This method has been deprecated. Please use GetSceneByName(name), GetSceneForEntityContextId(entityContextId) or Scene::GetSceneForEntityId(AZ::EntityId entityId) instead."); + //! Get scene by using scene id. - virtual ScenePtr GetScene(const SceneId& sceneId) const = 0; + virtual Scene* GetScene(const SceneId& sceneId) const = 0; + + //! Get scene by using scene name. + virtual Scene* GetSceneByName(const AZ::Name& name) const = 0; //! Get the render pipeline created for a window virtual RenderPipelinePtr GetRenderPipelineForWindow(AzFramework::NativeWindowHandle windowHandle) = 0; diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/RenderPipeline.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/RenderPipeline.h index fcf812cc38..6cba0c774f 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/RenderPipeline.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/RenderPipeline.h @@ -32,7 +32,6 @@ namespace AZ namespace RPI { class Scene; - struct TickTimeInfo; class ShaderResourceGroup; class AnyAsset; class WindowContext; @@ -203,7 +202,7 @@ namespace AZ void OnRemovedFromScene(Scene* scene); // Called when this pipeline is about to be rendered - void OnStartFrame(const TickTimeInfo& tick); + void OnStartFrame(float time); // Called when the rendering of current frame is finished. void OnFrameEnd(); diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Scene.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Scene.h index fc81368331..1411a8d996 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Scene.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Scene.h @@ -48,14 +48,6 @@ namespace AZ // Callback function to modify values of a ShaderResourceGroup using ShaderResourceGroupCallback = AZStd::function; - //! A structure for ticks which contains system time and game time. - struct TickTimeInfo - { - float m_currentGameTime; - float m_gameDeltaTime = 0; - }; - - class Scene final : public SceneRequestBus::Handler { @@ -80,6 +72,9 @@ namespace AZ //! Gets the RPI::Scene for a given entityContextId. //! May return nullptr if there is no RPI::Scene created for that entityContext. static Scene* GetSceneForEntityContextId(AzFramework::EntityContextId entityContextId); + + //! Gets the RPI::Scene for a given entityId. + static Scene* GetSceneForEntityId(AZ::EntityId entityId); ~Scene(); @@ -135,6 +130,8 @@ namespace AZ const SceneId& GetId() const; + AZ::Name GetName() const; + //! Set default pipeline by render pipeline ID. //! It returns true if the default render pipeline was set from the input ID. //! If the specified render pipeline doesn't exist in this scene then it won't do anything and returns false. @@ -174,12 +171,14 @@ namespace AZ // Cpu simulation which runs all active FeatureProcessor Simulate() functions. // @param jobPolicy if it's JobPolicy::Parallel, the function will spawn a job thread for each FeatureProcessor's simulation. - void Simulate(const TickTimeInfo& tickInfo, RHI::JobPolicy jobPolicy); + // @param simulationTime the number of seconds since the application started + void Simulate(RHI::JobPolicy jobPolicy, float simulationTime); // Collect DrawPackets from FeatureProcessors // @param jobPolicy if it's JobPolicy::Parallel, the function will spawn a job thread for each FeatureProcessor's // PrepareRender. - void PrepareRender(const TickTimeInfo& tickInfo, RHI::JobPolicy jobPolicy); + // @param simulationTime the number of seconds since the application started; this is the same time value that was passed to Simulate() + void PrepareRender(RHI::JobPolicy jobPolicy, float simulationTime); // Function called when the current frame is finished rendering. void OnFrameEnd(); @@ -245,6 +244,9 @@ namespace AZ // The uuid to identify this scene. SceneId m_id; + // Scene's name which is set at initialization. Can be empty + AZ::Name m_name; + bool m_activated = false; bool m_taskGraphActive = false; // update during tick, to ensure it only changes on frame boundaries @@ -259,6 +261,7 @@ namespace AZ // Registry which allocates draw filter tag for RenderPipeline RHI::Ptr m_drawFilterTagRegistry; + RHI::ShaderInputConstantIndex m_timeInputIndex; float m_simulationTime; }; @@ -286,13 +289,10 @@ namespace AZ template FeatureProcessorType* Scene::GetFeatureProcessorForEntity(AZ::EntityId entityId) { - // Find the entity context for the entity ID. - AzFramework::EntityContextId entityContextId = AzFramework::EntityContextId::CreateNull(); - AzFramework::EntityIdContextQueryBus::EventResult(entityContextId, entityId, &AzFramework::EntityIdContextQueryBus::Events::GetOwningContextId); - - if (!entityContextId.IsNull()) + RPI::Scene* renderScene = GetSceneForEntityId(entityId); + if (renderScene) { - return GetFeatureProcessorForEntityContextId(entityContextId); + return renderScene->GetFeatureProcessor(); } return nullptr; }; diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/System/SceneDescriptor.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/System/SceneDescriptor.h index 2072568d59..eb7a2b6cb7 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/System/SceneDescriptor.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/System/SceneDescriptor.h @@ -9,6 +9,7 @@ #pragma once #include +#include #include #include @@ -25,6 +26,9 @@ namespace AZ //! List of feature processors which the scene will initially enable. AZStd::vector m_featureProcessorNames; + + //! A name used as scene id. It can be used to search a registered scene via RPISystemInterface::GetScene() + AZ::Name m_nameId; }; } // namespace RPI } // namespace AZ diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp index 348f0aa57a..ac6b10694e 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp @@ -699,9 +699,7 @@ namespace AZ m_parentScene = parentScene; AZ_Assert(m_visScene == nullptr, "IVisibilityScene already created for this RPI::Scene"); - char sceneIdBuf[40] = ""; - m_parentScene->GetId().ToString(sceneIdBuf); - AZ::Name visSceneName(AZStd::string::format("RenderCullScene[%s]", sceneIdBuf)); + AZ::Name visSceneName(AZStd::string::format("RenderCullScene[%s]", m_parentScene->GetName().GetCStr())); m_visScene = AZ::Interface::Get()->CreateVisibilityScene(visSceneName); #ifdef AZ_CULL_DEBUG_ENABLED diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/RPISystem.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/RPISystem.cpp index eb74a81e3e..2b32503838 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/RPISystem.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/RPISystem.cpp @@ -159,6 +159,11 @@ namespace AZ AZ_Assert(false, "Scene was already registered"); return; } + else if (!scene->GetName().IsEmpty() && scene->GetName() == sceneItem->GetName()) + { + // only report a warning if there is a scene with duplicated name + AZ_Warning("RPISystem", false, "There is a registered scene with same name [%s]", scene->GetName().GetCStr()); + } } m_scenes.push_back(scene); @@ -177,11 +182,35 @@ namespace AZ AZ_Assert(false, "Can't unregister scene which wasn't registered"); } - ScenePtr RPISystem::GetScene(const SceneId& sceneId) const + Scene* RPISystem::GetScene(const SceneId& sceneId) const { for (const auto& scene : m_scenes) { if (scene->GetId() == sceneId) + { + return scene.get(); + } + } + return nullptr; + } + + Scene* RPISystem::GetSceneByName(const AZ::Name& name) const + { + for (const auto& scene : m_scenes) + { + if (scene->GetName() == name) + { + return scene.get(); + } + } + return nullptr; + } + + ScenePtr RPISystem::GetDefaultScene() const + { + for (const auto& scene : m_scenes) + { + if (scene->GetName() == AZ::Name("Main")) { return scene; } @@ -189,16 +218,6 @@ namespace AZ return nullptr; } - ScenePtr RPISystem::GetDefaultScene() const - { - if (m_scenes.size() > 0) - { - return m_scenes[0]; - } - return nullptr; - } - - RenderPipelinePtr RPISystem::GetRenderPipelineForWindow(AzFramework::NativeWindowHandle windowHandle) { RenderPipelinePtr renderPipeline; @@ -249,21 +268,23 @@ namespace AZ AssetInitBus::Broadcast(&AssetInitBus::Events::PostLoadInit); - // Update tick time info - FillTickTimeInfo(); + m_currentSimulationTime = GetCurrentTime(); for (auto& scene : m_scenes) { - scene->Simulate(m_tickTime, m_simulationJobPolicy); + scene->Simulate(m_simulationJobPolicy, m_currentSimulationTime); } } - void RPISystem::FillTickTimeInfo() + float RPISystem::GetCurrentTime() { - AZ::TickRequestBus::BroadcastResult(m_tickTime.m_gameDeltaTime, &AZ::TickRequestBus::Events::GetTickDeltaTime); - ScriptTimePoint currentTime; - AZ::TickRequestBus::BroadcastResult(currentTime, &AZ::TickRequestBus::Events::GetTimeAtCurrentTick); - m_tickTime.m_currentGameTime = static_cast(currentTime.GetSeconds()); + ScriptTimePoint timeAtCurrentTick; + AZ::TickRequestBus::BroadcastResult(timeAtCurrentTick, &AZ::TickRequestBus::Events::GetTimeAtCurrentTick); + + // We subtract the start time to maximize precision of the time value, since we will be converting it to a float. + double currentTime = timeAtCurrentTick.GetSeconds() - m_startTime.GetSeconds(); + + return aznumeric_cast(currentTime); } void RPISystem::RenderTick() @@ -282,7 +303,7 @@ namespace AZ // [GFX TODO] We may parallel scenes' prepare render. for (auto& scenePtr : m_scenes) { - scenePtr->PrepareRender(m_tickTime, m_prepareRenderJobPolicy); + scenePtr->PrepareRender(m_prepareRenderJobPolicy, m_currentSimulationTime); } m_rhiSystem.FrameUpdate( diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/RenderPipeline.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/RenderPipeline.cpp index 6378a249a4..8d4303f187 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/RenderPipeline.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/RenderPipeline.cpp @@ -375,7 +375,7 @@ namespace AZ m_scene->RemoveRenderPipeline(m_nameId); } - void RenderPipeline::OnStartFrame([[maybe_unused]] const TickTimeInfo& tick) + void RenderPipeline::OnStartFrame([[maybe_unused]] float time) { AZ_PROFILE_SCOPE(RPI, "RenderPipeline: OnStartFrame"); diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp index c5d82c6f29..b27e3518e1 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp @@ -44,8 +44,13 @@ namespace AZ { auto shaderAsset = RPISystemInterface::Get()->GetCommonShaderAssetForSrgs(); scene->m_srg = ShaderResourceGroup::Create(shaderAsset, sceneSrgLayout->GetName()); + + // Set value for constants defined in SceneTimeSrg.azsli + scene->m_timeInputIndex = scene->m_srg->FindShaderInputConstantIndex(Name{ "m_time" }); } - + + scene->m_name = sceneDescriptor.m_nameId; + return ScenePtr(scene); } @@ -83,10 +88,23 @@ namespace AZ return nullptr; } + Scene* Scene::GetSceneForEntityId(AZ::EntityId entityId) + { + // Find the entity context for the entity ID. + AzFramework::EntityContextId entityContextId = AzFramework::EntityContextId::CreateNull(); + AzFramework::EntityIdContextQueryBus::EventResult(entityContextId, entityId, &AzFramework::EntityIdContextQueryBus::Events::GetOwningContextId); + + if (!entityContextId.IsNull()) + { + return GetSceneForEntityContextId(entityContextId); + } + return nullptr; + } + Scene::Scene() { - m_id = Uuid::CreateRandom(); + m_id = AZ::Uuid::CreateRandom(); m_cullingScene = aznew CullingScene(); SceneRequestBus::Handler::BusConnect(m_id); m_drawFilterTagRegistry = RHI::DrawFilterTagRegistry::Create(); @@ -299,7 +317,6 @@ namespace AZ // Force to update the lookup table since adding render pipeline would effect any pipeline states created before pass system tick RebuildPipelineStatesLookup(); - AZ_Assert(!m_id.IsNull(), "RPI::Scene needs to have a valid uuid."); SceneNotificationBus::Event(m_id, &SceneNotification::OnRenderPipelineAdded, pipeline); } @@ -396,11 +413,11 @@ namespace AZ //[GFX TODO]: the completion job should start here } - void Scene::Simulate([[maybe_unused]] const TickTimeInfo& tickInfo, RHI::JobPolicy jobPolicy) + void Scene::Simulate(RHI::JobPolicy jobPolicy, float simulationTime) { AZ_PROFILE_SCOPE(RPI, "Scene: Simulate"); - m_simulationTime = tickInfo.m_currentGameTime; + m_simulationTime = simulationTime; // If previous simulation job wasn't done, wait for it to finish. if (m_taskGraphActive) @@ -469,11 +486,9 @@ namespace AZ { if (m_srg) { - // Set value for constants defined in SceneTimeSrg.azsli - RHI::ShaderInputConstantIndex timeIndex = m_srg->FindShaderInputConstantIndex(Name{ "m_time" }); - if (timeIndex.IsValid()) + if (m_timeInputIndex.IsValid()) { - m_srg->SetConstant(timeIndex, m_simulationTime); + m_srg->SetConstant(m_timeInputIndex, m_simulationTime); } // signal any handlers to update values for their partial scene srg @@ -606,7 +621,7 @@ namespace AZ WaitAndCleanCompletionJob(finalizeDrawListsCompletion); } - void Scene::PrepareRender(const TickTimeInfo& tickInfo, RHI::JobPolicy jobPolicy) + void Scene::PrepareRender(RHI::JobPolicy jobPolicy, float simulationTime) { AZ_PROFILE_SCOPE(RPI, "Scene: PrepareRender"); @@ -630,7 +645,7 @@ namespace AZ if (pipeline->NeedsRender()) { activePipelines.push_back(pipeline); - pipeline->OnStartFrame(tickInfo); + pipeline->OnStartFrame(simulationTime); } } } @@ -785,6 +800,11 @@ namespace AZ { return m_id; } + + AZ::Name Scene::GetName() const + { + return m_name; + } bool Scene::SetDefaultRenderPipeline(const RenderPipelineId& pipelineId) { diff --git a/Gems/Atom/RPI/Code/Tests.Builders/BuilderTestFixture.h b/Gems/Atom/RPI/Code/Tests.Builders/BuilderTestFixture.h index 31c1bc6715..02e69e732c 100644 --- a/Gems/Atom/RPI/Code/Tests.Builders/BuilderTestFixture.h +++ b/Gems/Atom/RPI/Code/Tests.Builders/BuilderTestFixture.h @@ -46,7 +46,6 @@ namespace UnitTest bool DeleteEntity(const AZ::EntityId&) override { return false; } AZ::Entity* FindEntity(const AZ::EntityId&) 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* GetExecutableFolder() const override { return nullptr; } void EnumerateEntities(const EntityCallback& /*callback*/) override {} diff --git a/Gems/Atom/RPI/Code/Tests/Common/AssetManagerTestFixture.h b/Gems/Atom/RPI/Code/Tests/Common/AssetManagerTestFixture.h index ee3ad94d4f..fb88e62617 100644 --- a/Gems/Atom/RPI/Code/Tests/Common/AssetManagerTestFixture.h +++ b/Gems/Atom/RPI/Code/Tests/Common/AssetManagerTestFixture.h @@ -44,7 +44,6 @@ namespace UnitTest AZ::Entity* FindEntity(const AZ::EntityId&) override { return nullptr; } AZ::BehaviorContext* GetBehaviorContext() 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* GetExecutableFolder() const override { return nullptr; } void EnumerateEntities(const EntityCallback& /*callback*/) override {} diff --git a/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/015_SubsurfaceScattering_Transmission_Thin.material b/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/015_SubsurfaceScattering_Transmission_Thin.material new file mode 100644 index 0000000000..ee9bb1f4a5 --- /dev/null +++ b/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/015_SubsurfaceScattering_Transmission_Thin.material @@ -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 + ] + } + } +} \ No newline at end of file diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/InspectorPropertyGroupWidget.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/InspectorPropertyGroupWidget.cpp index 6b9aee17f7..af71954737 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/InspectorPropertyGroupWidget.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/InspectorPropertyGroupWidget.cpp @@ -43,7 +43,7 @@ namespace AtomToolsFramework m_propertyEditor->Setup(context, instanceNotificationHandler, false); m_propertyEditor->AddInstance(instance, instanceClassId, nullptr, instanceToCompare); m_propertyEditor->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); - m_propertyEditor->InvalidateAll(); + m_propertyEditor->QueueInvalidation(AzToolsFramework::PropertyModificationRefreshLevel::Refresh_EntireTree); m_layout->addWidget(m_propertyEditor); setLayout(m_layout); diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/PreviewRenderer/PreviewRenderer.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/PreviewRenderer/PreviewRenderer.cpp index 2b39a87623..27d468e64b 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/PreviewRenderer/PreviewRenderer.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/PreviewRenderer/PreviewRenderer.cpp @@ -43,6 +43,7 @@ namespace AtomToolsFramework &PreviewerFeatureProcessorProviderBus::Handler::GetRequiredFeatureProcessors, featureProcessors); AZ::RPI::SceneDescriptor sceneDesc; + sceneDesc.m_nameId = AZ::Name("PreviewRenderer"); sceneDesc.m_featureProcessorNames.assign(featureProcessors.begin(), featureProcessors.end()); m_scene = AZ::RPI::Scene::CreateScene(sceneDesc); diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Util/Util.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Util/Util.cpp index be112345a9..d3bce34af3 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Util/Util.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Util/Util.cpp @@ -133,29 +133,12 @@ namespace AtomToolsFramework bool LaunchTool(const QString& baseName, const QString& extension, const QStringList& arguments) { - const char* engineRoot = nullptr; - AzFramework::ApplicationRequests::Bus::BroadcastResult(engineRoot, &AzFramework::ApplicationRequests::GetEngineRoot); - AZ_Assert(engineRoot != nullptr, "AzFramework::ApplicationRequests::GetEngineRoot failed"); + AZ::IO::FixedMaxPath engineRoot = AZ::Utils::GetEnginePath(); + AZ_Assert(!engineRoot.empty(), "Cannot query Engine Path"); - char binFolderName[AZ_MAX_PATH_LEN] = {}; - AZ::Utils::GetExecutablePathReturnType ret = AZ::Utils::GetExecutablePath(binFolderName, AZ_MAX_PATH_LEN); + AZ::IO::FixedMaxPath launchPath = AZ::IO::FixedMaxPath(AZ::Utils::GetExecutableDirectory()) + / (baseName + extension).toUtf8().constData(); - // If it contains the filename, zero out the last path separator character... - 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); + return QProcess::startDetached(launchPath.c_str(), arguments, engineRoot.c_str()); } } diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/MaterialEditorViewportInputController.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/MaterialEditorViewportInputController.cpp index 932e2e7436..1784405ffa 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/MaterialEditorViewportInputController.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/MaterialEditorViewportInputController.cpp @@ -279,9 +279,9 @@ namespace MaterialEditor // reset environment AZ::Transform iblTransform = AZ::Transform::CreateIdentity(); AZ::TransformBus::Event(m_iblEntityId, &AZ::TransformBus::Events::SetLocalTM, iblTransform); + const AZ::Matrix4x4 rotationMatrix = AZ::Matrix4x4::CreateIdentity(); - AZ::RPI::ScenePtr scene = AZ::RPI::RPISystemInterface::Get()->GetDefaultScene(); - auto skyBoxFeatureProcessorInterface = scene->GetFeatureProcessor(); + auto skyBoxFeatureProcessorInterface = AZ::RPI::Scene::GetFeatureProcessorForEntity(m_iblEntityId); skyBoxFeatureProcessorInterface->SetCubemapRotationMatrix(rotationMatrix); if (m_behavior) diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/RotateEnvironmentBehavior.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/RotateEnvironmentBehavior.cpp index 21d7dee51b..17fb3f3e52 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/RotateEnvironmentBehavior.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/RotateEnvironmentBehavior.cpp @@ -25,8 +25,7 @@ namespace MaterialEditor m_iblEntityId, &MaterialEditorViewportInputControllerRequestBus::Handler::GetIblEntityId); AZ_Assert(m_iblEntityId.IsValid(), "Failed to find m_iblEntityId"); - AZ::RPI::ScenePtr scene = AZ::RPI::RPISystemInterface::Get()->GetDefaultScene(); - m_skyBoxFeatureProcessorInterface = scene->GetFeatureProcessor(); + m_skyBoxFeatureProcessorInterface = AZ::RPI::Scene::GetFeatureProcessorForEntity(m_iblEntityId); } void RotateEnvironmentBehavior::TickInternal(float x, float y, float z) diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportRenderer.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportRenderer.cpp index 6d6fd377e3..4ad87492e3 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportRenderer.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportRenderer.cpp @@ -67,6 +67,7 @@ namespace MaterialEditor // Create and register a scene with all available feature processors AZ::RPI::SceneDescriptor sceneDesc; + sceneDesc.m_nameId = AZ::Name("MaterialViewport"); m_scene = AZ::RPI::Scene::CreateScene(sceneDesc); m_scene->EnableAllFeatureProcessors(); diff --git a/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiPassTree.inl b/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiPassTree.inl index 55e457926a..5a8aaf7ded 100644 --- a/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiPassTree.inl +++ b/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiPassTree.inl @@ -19,11 +19,11 @@ #include -#include #include #include #include +#include #ifndef SCRIPTABLE_IMGUI #define Scriptable_ImGui ImGui @@ -334,11 +334,10 @@ namespace AZ::Render if (m_engineRoot.empty()) { - const char* engineRoot = nullptr; - AzFramework::ApplicationRequests::Bus::BroadcastResult(engineRoot, &AzFramework::ApplicationRequests::GetEngineRoot); - if (engineRoot) + AZ::IO::FixedMaxPathString engineRoot = AZ::Utils::GetEnginePath(); + if (!engineRoot.empty()) { - m_engineRoot = AZStd::string(engineRoot); + m_engineRoot = AZStd::string_view(engineRoot); } } diff --git a/Gems/AtomContent/ReferenceMaterials/Tools/Launch_Cmd.bat b/Gems/AtomContent/ReferenceMaterials/Tools/Launch_Cmd.bat index d100c9ddc7..0b94be5bea 100644 --- a/Gems/AtomContent/ReferenceMaterials/Tools/Launch_Cmd.bat +++ b/Gems/AtomContent/ReferenceMaterials/Tools/Launch_Cmd.bat @@ -1,4 +1,6 @@ @echo off +:: Keep changes local +SETLOCAL enableDelayedExpansion REM REM Copyright (c) Contributors to the Open 3D Engine Project @@ -13,7 +15,7 @@ REM :: Puts you in the CMD within the dev environment :: Set up window -TITLE O3DE Asset Gem Cmd +TITLE O3DE DCC Scripting Interface Cmd :: Use obvious color to prevent confusion (Grey with Yellow Text) COLOR 8E @@ -21,15 +23,12 @@ COLOR 8E cd %~dp0 PUSHD %~dp0 -:: Keep changes local -SETLOCAL enableDelayedExpansion - CALL %~dp0\Project_Env.bat echo. echo _____________________________________________________________________ echo. -echo ~ O3DE Asset Gem CMD ... +echo ~ O3DE %O3DE_PROJECT% Asset Gem CMD ... echo _____________________________________________________________________ echo. diff --git a/Gems/AtomContent/ReferenceMaterials/Tools/Launch_Maya.bat b/Gems/AtomContent/ReferenceMaterials/Tools/Launch_Maya.bat index b9a6b399f3..0af25905a0 100644 --- a/Gems/AtomContent/ReferenceMaterials/Tools/Launch_Maya.bat +++ b/Gems/AtomContent/ReferenceMaterials/Tools/Launch_Maya.bat @@ -1,6 +1,3 @@ -:: Launches maya wityh a bunch of local hooks for Lumberyard -:: ToDo: move all of this to a .json data driven boostrapping system - @echo off REM @@ -37,7 +34,7 @@ echo DCCSI_MAYA_VERSION = %DCCSI_MAYA_VERSION% IF EXIST "%~dp0Project_Env.bat" CALL %~dp0Project_Env.bat echo ________________________________ -echo Launching Maya %DCCSI_MAYA_VERSION% for Lumberyard... +echo Launching Maya %DCCSI_MAYA_VERSION% for O3DE: %O3DE_PROJECT%... :::: Set Maya native project acess to this project ::set MAYA_PROJECT=%LY_PROJECT% @@ -47,8 +44,10 @@ echo Launching Maya %DCCSI_MAYA_VERSION% for Lumberyard... Set MAYA_VP2_DEVICE_OVERRIDE = VirtualDeviceDx11 :: Default to the right version of Maya if we can detect it... and launch -IF EXIST "%MAYA_LOCATION%\bin\Maya.exe" ( - start "" "%MAYA_LOCATION%\bin\Maya.exe" %* +echo MAYA_BIN_PATH = %MAYA_BIN_PATH% + +IF EXIST "%MAYA_BIN_PATH%\Maya.exe" ( + start "" "%MAYA_BIN_PATH%\Maya.exe" %* ) ELSE ( Where maya.exe 2> NUL IF ERRORLEVEL 1 ( diff --git a/Gems/AtomContent/ReferenceMaterials/Tools/Project_Env.bat b/Gems/AtomContent/ReferenceMaterials/Tools/Project_Env.bat index 6e2c8b5914..ddf934d206 100644 --- a/Gems/AtomContent/ReferenceMaterials/Tools/Project_Env.bat +++ b/Gems/AtomContent/ReferenceMaterials/Tools/Project_Env.bat @@ -29,23 +29,23 @@ PUSHD %~dp0 set ABS_PATH=%~dp0 :: project name as a str tag -IF "%LY_PROJECT_NAME%"=="" ( - for %%I in ("%~dp0.") do for %%J in ("%%~dpI.") do set LY_PROJECT_NAME=%%~nxJ +IF "%O3DE_PROJECT%"=="" ( + for %%I in ("%~dp0.") do for %%J in ("%%~dpI.") do set O3DE_PROJECT=%%~nxJ ) echo. echo _____________________________________________________________________ echo. -echo ~ Setting up O3DE %LY_PROJECT_NAME% Environment ... +echo ~ Setting up O3DE %O3DE_PROJECT% Environment ... echo _____________________________________________________________________ echo. -echo LY_PROJECT_NAME = %LY_PROJECT_NAME% +echo O3DE_PROJECT = %O3DE_PROJECT% :: if the user has set up a custom env call it :: this should allow the user to locally -:: set env hooks like LY_DEV or LY_PROJECT +:: set env hooks like O3DE_DEV or O3DE_PROJECT_PATH IF EXIST "%~dp0User_Env.bat" CALL %~dp0User_Env.bat -echo LY_DEV = %LY_DEV% +echo O3DE_DEV = %O3DE_DEV% :: Constant Vars (Global) :: global debug flag (propogates) @@ -74,23 +74,20 @@ echo DCCSI_LOGLEVEL = %DCCSI_LOGLEVEL% IF "%DCCSI_MAYA_VERSION%"=="" (set DCCSI_MAYA_VERSION=2020) echo DCCSI_MAYA_VERSION = %DCCSI_MAYA_VERSION% -:: LY_PROJECT is ideally treated as a full path in the env launchers +:: O3DE_PROJECT_PATH is ideally treated as a full path in the env launchers :: do to changes in o3de, external engine/project/gem folder structures, etc. -IF "%LY_PROJECT%"=="" ( - for %%i in ("%~dp0..") do set "LY_PROJECT=%%~fi" +IF "%O3DE_PROJECT_PATH%"=="" ( + for %%i in ("%~dp0..") do set "O3DE_PROJECT_PATH=%%~fi" ) -echo LY_PROJECT = %LY_PROJECT% +echo O3DE_PROJECT_PATH = %O3DE_PROJECT_PATH% -:: this is here for archaic reasons, WILL DEPRECATE -IF "%LY_PROJECT_PATH%"=="" (set LY_PROJECT_PATH=%LY_PROJECT%) -echo LY_PROJECT_PATH = %LY_PROJECT_PATH% +:: Change to root O3DE dev dir +IF "%O3DE_DEV%"=="" echo ~ You must set O3DE_DEV in a User_Env.bat to match your local engine repo! +IF "%O3DE_DEV%"=="" echo ~ Using default O3DE_DEV=C:\Depot\o3de-engine +IF "%O3DE_DEV%"=="" (set O3DE_DEV=C:\Depot\o3de-engine) +echo O3DE_DEV = %O3DE_DEV% -:: Change to root Lumberyard dev dir -:: You must set this in a User_Env.bat to match youe engine repo location! -IF "%LY_DEV%"=="" (set LY_DEV=C:\Depot\o3de-engine) -echo LY_DEV = %LY_DEV% - -CALL %LY_DEV%\Gems\AtomLyIntegration\TechnicalArt\DccScriptingInterface\Launchers\Windows\Env_Maya.bat +CALL %O3DE_DEV%\Gems\AtomLyIntegration\TechnicalArt\DccScriptingInterface\Tools\Dev\Windows\Env_Maya.bat :: Restore original directory popd diff --git a/Gems/AtomContent/Sponza/Tools/Launch_Cmd.bat b/Gems/AtomContent/Sponza/Tools/Launch_Cmd.bat index 99c2c12c51..0b94be5bea 100644 --- a/Gems/AtomContent/Sponza/Tools/Launch_Cmd.bat +++ b/Gems/AtomContent/Sponza/Tools/Launch_Cmd.bat @@ -1,4 +1,6 @@ @echo off +:: Keep changes local +SETLOCAL enableDelayedExpansion REM REM Copyright (c) Contributors to the Open 3D Engine Project @@ -21,15 +23,12 @@ COLOR 8E cd %~dp0 PUSHD %~dp0 -:: Keep changes local -SETLOCAL enableDelayedExpansion - CALL %~dp0\Project_Env.bat echo. echo _____________________________________________________________________ echo. -echo ~ LY DCC Scripting Interface CMD ... +echo ~ O3DE %O3DE_PROJECT% Asset Gem CMD ... echo _____________________________________________________________________ echo. diff --git a/Gems/AtomContent/Sponza/Tools/Launch_Maya.bat b/Gems/AtomContent/Sponza/Tools/Launch_Maya.bat index d774adf79b..0af25905a0 100644 --- a/Gems/AtomContent/Sponza/Tools/Launch_Maya.bat +++ b/Gems/AtomContent/Sponza/Tools/Launch_Maya.bat @@ -34,7 +34,7 @@ echo DCCSI_MAYA_VERSION = %DCCSI_MAYA_VERSION% IF EXIST "%~dp0Project_Env.bat" CALL %~dp0Project_Env.bat echo ________________________________ -echo Launching Maya %DCCSI_MAYA_VERSION% for Lumberyard... +echo Launching Maya %DCCSI_MAYA_VERSION% for O3DE: %O3DE_PROJECT%... :::: Set Maya native project acess to this project ::set MAYA_PROJECT=%LY_PROJECT% @@ -44,8 +44,10 @@ echo Launching Maya %DCCSI_MAYA_VERSION% for Lumberyard... Set MAYA_VP2_DEVICE_OVERRIDE = VirtualDeviceDx11 :: Default to the right version of Maya if we can detect it... and launch -IF EXIST "%MAYA_LOCATION%\bin\Maya.exe" ( - start "" "%MAYA_LOCATION%\bin\Maya.exe" %* +echo MAYA_BIN_PATH = %MAYA_BIN_PATH% + +IF EXIST "%MAYA_BIN_PATH%\Maya.exe" ( + start "" "%MAYA_BIN_PATH%\Maya.exe" %* ) ELSE ( Where maya.exe 2> NUL IF ERRORLEVEL 1 ( diff --git a/Gems/AtomContent/Sponza/Tools/Project_Env.bat b/Gems/AtomContent/Sponza/Tools/Project_Env.bat index 6e2c8b5914..ddf934d206 100644 --- a/Gems/AtomContent/Sponza/Tools/Project_Env.bat +++ b/Gems/AtomContent/Sponza/Tools/Project_Env.bat @@ -29,23 +29,23 @@ PUSHD %~dp0 set ABS_PATH=%~dp0 :: project name as a str tag -IF "%LY_PROJECT_NAME%"=="" ( - for %%I in ("%~dp0.") do for %%J in ("%%~dpI.") do set LY_PROJECT_NAME=%%~nxJ +IF "%O3DE_PROJECT%"=="" ( + for %%I in ("%~dp0.") do for %%J in ("%%~dpI.") do set O3DE_PROJECT=%%~nxJ ) echo. echo _____________________________________________________________________ echo. -echo ~ Setting up O3DE %LY_PROJECT_NAME% Environment ... +echo ~ Setting up O3DE %O3DE_PROJECT% Environment ... echo _____________________________________________________________________ echo. -echo LY_PROJECT_NAME = %LY_PROJECT_NAME% +echo O3DE_PROJECT = %O3DE_PROJECT% :: if the user has set up a custom env call it :: this should allow the user to locally -:: set env hooks like LY_DEV or LY_PROJECT +:: set env hooks like O3DE_DEV or O3DE_PROJECT_PATH IF EXIST "%~dp0User_Env.bat" CALL %~dp0User_Env.bat -echo LY_DEV = %LY_DEV% +echo O3DE_DEV = %O3DE_DEV% :: Constant Vars (Global) :: global debug flag (propogates) @@ -74,23 +74,20 @@ echo DCCSI_LOGLEVEL = %DCCSI_LOGLEVEL% IF "%DCCSI_MAYA_VERSION%"=="" (set DCCSI_MAYA_VERSION=2020) echo DCCSI_MAYA_VERSION = %DCCSI_MAYA_VERSION% -:: LY_PROJECT is ideally treated as a full path in the env launchers +:: O3DE_PROJECT_PATH is ideally treated as a full path in the env launchers :: do to changes in o3de, external engine/project/gem folder structures, etc. -IF "%LY_PROJECT%"=="" ( - for %%i in ("%~dp0..") do set "LY_PROJECT=%%~fi" +IF "%O3DE_PROJECT_PATH%"=="" ( + for %%i in ("%~dp0..") do set "O3DE_PROJECT_PATH=%%~fi" ) -echo LY_PROJECT = %LY_PROJECT% +echo O3DE_PROJECT_PATH = %O3DE_PROJECT_PATH% -:: this is here for archaic reasons, WILL DEPRECATE -IF "%LY_PROJECT_PATH%"=="" (set LY_PROJECT_PATH=%LY_PROJECT%) -echo LY_PROJECT_PATH = %LY_PROJECT_PATH% +:: Change to root O3DE dev dir +IF "%O3DE_DEV%"=="" echo ~ You must set O3DE_DEV in a User_Env.bat to match your local engine repo! +IF "%O3DE_DEV%"=="" echo ~ Using default O3DE_DEV=C:\Depot\o3de-engine +IF "%O3DE_DEV%"=="" (set O3DE_DEV=C:\Depot\o3de-engine) +echo O3DE_DEV = %O3DE_DEV% -:: Change to root Lumberyard dev dir -:: You must set this in a User_Env.bat to match youe engine repo location! -IF "%LY_DEV%"=="" (set LY_DEV=C:\Depot\o3de-engine) -echo LY_DEV = %LY_DEV% - -CALL %LY_DEV%\Gems\AtomLyIntegration\TechnicalArt\DccScriptingInterface\Launchers\Windows\Env_Maya.bat +CALL %O3DE_DEV%\Gems\AtomLyIntegration\TechnicalArt\DccScriptingInterface\Tools\Dev\Windows\Env_Maya.bat :: Restore original directory popd diff --git a/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomBridgeSystemComponent.cpp b/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomBridgeSystemComponent.cpp index b439ac475c..d822b16486 100644 --- a/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomBridgeSystemComponent.cpp +++ b/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomBridgeSystemComponent.cpp @@ -108,7 +108,7 @@ namespace AZ { m_dynamicDrawManager.reset(); AZ::RPI::ViewportContextManagerNotificationsBus::Handler::BusDisconnect(); - RPI::Scene* scene = RPI::RPISystemInterface::Get()->GetDefaultScene().get(); + RPI::Scene* scene = AZ::RPI::Scene::GetSceneForEntityContextId(m_entityContextId); // Check if scene is emptry since scene might be released already when running AtomSampleViewer if (scene) { @@ -157,9 +157,9 @@ namespace AZ void AtomBridgeSystemComponent::OnBootstrapSceneReady(AZ::RPI::Scene* bootstrapScene) { - AZ_UNUSED(bootstrapScene); // Make default AtomDebugDisplayViewportInterface - AZStd::shared_ptr mainEntityDebugDisplay = AZStd::make_shared(AzFramework::g_defaultSceneEntityDebugDisplayId); + AZStd::shared_ptr mainEntityDebugDisplay = + AZStd::make_shared(AzFramework::g_defaultSceneEntityDebugDisplayId, bootstrapScene); m_activeViewportsList[AzFramework::g_defaultSceneEntityDebugDisplayId] = mainEntityDebugDisplay; } diff --git a/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomDebugDisplayViewportInterface.cpp b/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomDebugDisplayViewportInterface.cpp index 39d8933863..12e4ccd8ad 100644 --- a/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomDebugDisplayViewportInterface.cpp +++ b/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomDebugDisplayViewportInterface.cpp @@ -256,12 +256,11 @@ namespace AZ::AtomBridge viewportContextPtr->ConnectSceneChangedHandler(m_sceneChangeHandler); } - AtomDebugDisplayViewportInterface::AtomDebugDisplayViewportInterface(uint32_t defaultInstanceAddress) + AtomDebugDisplayViewportInterface::AtomDebugDisplayViewportInterface(uint32_t defaultInstanceAddress, RPI::Scene* scene) { ResetRenderState(); m_viewportId = defaultInstanceAddress; m_defaultInstance = true; - RPI::Scene* scene = RPI::RPISystemInterface::Get()->GetDefaultScene().get(); InitInternal(scene, nullptr); } @@ -800,7 +799,8 @@ namespace AZ::AtomBridge const float startAngle = DegToRad(startAngleDegrees); const float stopAngle = DegToRad(sweepAngleDegrees) + startAngle; SingleColorDynamicSizeLineHelper lines(1+static_cast(sweepAngleDegrees/angularStepDegrees)); - AZ::Vector3 radiusV3 = AZ::Vector3(radius); + float aspectRadius = radius / GetAspectRatio(); + AZ::Vector3 radiusV3 = AZ::Vector3(aspectRadius, radius, radius); AZ::Vector3 pos = AZ::Vector3(center.GetX(), center.GetY(), z); CreateAxisAlignedArc( lines, diff --git a/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomDebugDisplayViewportInterface.h b/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomDebugDisplayViewportInterface.h index 07f4efdf50..5f902c5884 100644 --- a/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomDebugDisplayViewportInterface.h +++ b/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomDebugDisplayViewportInterface.h @@ -124,7 +124,7 @@ namespace AZ::AtomBridge AZ_RTTI(AtomDebugDisplayViewportInterface, "{09AF6A46-0100-4FBF-8F94-E6B221322D14}", AzFramework::DebugDisplayRequestBus::Handler); explicit AtomDebugDisplayViewportInterface(AZ::RPI::ViewportContextPtr viewportContextPtr); - explicit AtomDebugDisplayViewportInterface(uint32_t defaultInstanceAddress); + explicit AtomDebugDisplayViewportInterface(uint32_t defaultInstanceAddress, RPI::Scene* scene); ~AtomDebugDisplayViewportInterface(); void ResetRenderState(); diff --git a/Gems/AtomLyIntegration/AtomFont/Code/Include/AtomLyIntegration/AtomFont/FFont.h b/Gems/AtomLyIntegration/AtomFont/Code/Include/AtomLyIntegration/AtomFont/FFont.h index 2cc8a67cfa..ff6ad7c151 100644 --- a/Gems/AtomLyIntegration/AtomFont/Code/Include/AtomLyIntegration/AtomFont/FFont.h +++ b/Gems/AtomLyIntegration/AtomFont/Code/Include/AtomLyIntegration/AtomFont/FFont.h @@ -133,18 +133,6 @@ namespace AZ typedef std::vector FontEffects; typedef FontEffects::iterator FontEffectsIterator; - struct FontPipelineStateMapKey - { - AZ::RPI::SceneId m_sceneId; // which scene pipeline state is attached to (via Render Pipeline) - AZ::RHI::DrawListTag m_drawListTag; // which render pass this pipeline draws in by default - - bool operator<(const FontPipelineStateMapKey& other) const - { - return m_sceneId < other.m_sceneId - || (m_sceneId == other.m_sceneId && m_drawListTag < other.m_drawListTag); - } - }; - struct FontShaderData { AZ::RHI::ShaderInputNameIndex m_imageInputIndex = "m_texture"; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/CMakeLists.txt b/Gems/AtomLyIntegration/CommonFeatures/Code/CMakeLists.txt index 95331a2f3f..7b685ece13 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/CMakeLists.txt +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/CMakeLists.txt @@ -111,6 +111,7 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS) RUNTIME_DEPENDENCIES Gem::Atom_RPI.Editor Gem::Atom_Feature_Common.Editor + Gem::AtomToolsFramework.Editor Legacy::EditorCommon ) diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentController.cpp index ee322c8afd..e844219811 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentController.cpp @@ -48,11 +48,7 @@ namespace AZ void DiffuseGlobalIlluminationComponentController::Activate(EntityId entityId) { - AZ_UNUSED(entityId); - - const RPI::Scene* scene = AZ::RPI::RPISystemInterface::Get()->GetDefaultScene().get(); - m_featureProcessor = scene->GetFeatureProcessor(); - + m_featureProcessor = AZ::RPI::Scene::GetFeatureProcessorForEntity(entityId); OnConfigChanged(); } diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Grid/GridComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Grid/GridComponentController.cpp index b4131894f4..2611021359 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Grid/GridComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Grid/GridComponentController.cpp @@ -79,7 +79,7 @@ namespace AZ m_entityId = entityId; m_dirty = true; - RPI::ScenePtr scene = RPI::RPISystemInterface::Get()->GetDefaultScene(); + RPI::Scene* scene = RPI::Scene::GetSceneForEntityId(m_entityId); if (scene) { AZ::RPI::SceneNotificationBus::Handler::BusConnect(scene->GetId()); diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponentController.cpp index e86c909121..a0577c6240 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponentController.cpp @@ -357,8 +357,7 @@ namespace AZ void DisplayMapperComponentController::OnConfigChanged() { // Register the configuration with the AcesDisplayMapperFeatureProcessor for this scene. - const AZ::RPI::Scene* scene = AZ::RPI::RPISystemInterface::Get()->GetDefaultScene().get(); - DisplayMapperFeatureProcessorInterface* fp = scene->GetFeatureProcessor(); + DisplayMapperFeatureProcessorInterface* fp = AZ::RPI::Scene::GetFeatureProcessorForEntity(m_entityId); DisplayMapperConfigurationDescriptor desc; desc.m_operationType = m_configuration.m_displayMapperOperation; desc.m_ldrGradingLutEnabled = m_configuration.m_ldrColorGradingLutEnabled; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SkinnedMesh/SkinnedMeshDebugDisplay.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SkinnedMesh/SkinnedMeshDebugDisplay.h index e789b6dc80..5b060198dc 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SkinnedMesh/SkinnedMeshDebugDisplay.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SkinnedMesh/SkinnedMeshDebugDisplay.h @@ -48,7 +48,7 @@ namespace AZ // CVar for toggling the display of the scene stats int r_skinnedMeshDisplaySceneStats = 0; // SceneId to query for the stats - RPI::SceneId m_sceneId = RPI::SceneId::CreateNull(); + RPI::SceneId m_sceneId; }; }// namespace Render }// namespace AZ diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRenderer.cpp b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRenderer.cpp index faa033956a..eb99b8171a 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRenderer.cpp +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRenderer.cpp @@ -60,6 +60,7 @@ namespace EMStudio // Create and register a scene with all available feature processors AZ::RPI::SceneDescriptor sceneDesc; + sceneDesc.m_nameId = AZ::Name("AnimViewport"); m_scene = AZ::RPI::Scene::CreateScene(sceneDesc); m_scene->EnableAllFeatureProcessors(); @@ -213,8 +214,7 @@ namespace EMStudio AZ::TransformBus::Event(m_iblEntity->GetId(), &AZ::TransformBus::Events::SetLocalTM, iblTransform); const AZ::Matrix4x4 rotationMatrix = AZ::Matrix4x4::CreateIdentity(); - AZ::RPI::ScenePtr scene = AZ::RPI::RPISystemInterface::Get()->GetDefaultScene(); - auto skyBoxFeatureProcessorInterface = scene->GetFeatureProcessor(); + auto skyBoxFeatureProcessorInterface = m_scene->GetFeatureProcessor(); skyBoxFeatureProcessorInterface->SetCubemapRotationMatrix(rotationMatrix); } diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Env_Maya.bat b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Env_Maya.bat index 5e3c600124..0ed46e9400 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Env_Maya.bat +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Env_Maya.bat @@ -46,7 +46,7 @@ echo DCCSI_PY_VERSION_RELEASE = %DCCSI_PY_VERSION_RELEASE% echo DCCSI_MAYA_VERSION = %DCCSI_MAYA_VERSION% :::: Set Maya native project acess to this project -IF "%MAYA_PROJECT%"=="" (set MAYA_PROJECT=%O3DE_PROJECT%) +IF "%MAYA_PROJECT%"=="" (set MAYA_PROJECT=%O3DE_PROJECT_PATH%) echo MAYA_PROJECT = %MAYA_PROJECT% :: maya sdk path diff --git a/Gems/AudioSystem/Code/Source/Editor/ImplementationManager.cpp b/Gems/AudioSystem/Code/Source/Editor/ImplementationManager.cpp index bee5c1dd51..f339ff8bb3 100644 --- a/Gems/AudioSystem/Code/Source/Editor/ImplementationManager.cpp +++ b/Gems/AudioSystem/Code/Source/Editor/ImplementationManager.cpp @@ -9,8 +9,6 @@ #include -#include - #include #include #include @@ -32,10 +30,6 @@ bool CImplementationManager::LoadImplementation() // release the loaded implementation (if any) Release(); - const char* engineRoot = nullptr; - AzFramework::ApplicationRequests::Bus::BroadcastResult(engineRoot, &AzFramework::ApplicationRequests::GetEngineRoot); - AZ_Assert(engineRoot != nullptr, "Unable to communicate with AzFramework::ApplicationRequests::Bus"); - AudioControlsEditor::EditorImplPluginEventBus::Broadcast(&AudioControlsEditor::EditorImplPluginEventBus::Events::InitializeEditorImplPlugin); } else diff --git a/Gems/Blast/Code/Source/Components/BlastSystemComponent.cpp b/Gems/Blast/Code/Source/Components/BlastSystemComponent.cpp index 00629eb65d..4c0f15463a 100644 --- a/Gems/Blast/Code/Source/Components/BlastSystemComponent.cpp +++ b/Gems/Blast/Code/Source/Components/BlastSystemComponent.cpp @@ -255,19 +255,22 @@ namespace Blast BlastFamilyComponentRequestBus::Broadcast( &BlastFamilyComponentRequests::FillDebugRenderBuffer, buffer, m_debugRenderMode); - // This is a system component, and thus is not associated with a specific scene, so use the default scene + // This is a system component, and thus is not associated with a specific scene, so use the bootstrap scene // for the debug drawing - const auto defaultScene = AZ::RPI::RPISystemInterface::Get()->GetDefaultScene(); - auto drawQueue = AZ::RPI::AuxGeomFeatureProcessorInterface::GetDrawQueueForScene(defaultScene); - - for (DebugLine& line : buffer.m_lines) + const auto mainScene = AZ::RPI::RPISystemInterface::Get()->GetSceneByName(AZ::Name("Main")); + if (mainScene) { - AZ::RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments drawArguments; - drawArguments.m_verts = &line.m_p0; - drawArguments.m_vertCount = 2; - drawArguments.m_colors = &line.m_color; - drawArguments.m_colorCount = 1; - drawQueue->DrawLines(drawArguments); + auto drawQueue = AZ::RPI::AuxGeomFeatureProcessorInterface::GetDrawQueueForScene(mainScene); + + for (DebugLine& line : buffer.m_lines) + { + AZ::RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments drawArguments; + drawArguments.m_verts = &line.m_p0; + drawArguments.m_vertCount = 2; + drawArguments.m_colors = &line.m_color; + drawArguments.m_colorCount = 1; + drawQueue->DrawLines(drawArguments); + } } } } diff --git a/Gems/EditorPythonBindings/Code/Source/PythonSystemComponent.cpp b/Gems/EditorPythonBindings/Code/Source/PythonSystemComponent.cpp index 876ef19803..fe9156013f 100644 --- a/Gems/EditorPythonBindings/Code/Source/PythonSystemComponent.cpp +++ b/Gems/EditorPythonBindings/Code/Source/PythonSystemComponent.cpp @@ -597,11 +597,10 @@ namespace EditorPythonBindings { AZStd::unordered_set pyPackageSites(pythonPathStack.begin(), pythonPathStack.end()); - const char* engineRoot = nullptr; - AzFramework::ApplicationRequests::Bus::BroadcastResult(engineRoot, &AzFramework::ApplicationRequests::GetEngineRoot); + AZ::IO::FixedMaxPath engineRoot = AZ::Utils::GetEnginePath(); // set PYTHON_HOME - AZStd::string pyBasePath = Platform::GetPythonHomePath(PY_PACKAGE, engineRoot); + AZStd::string pyBasePath = Platform::GetPythonHomePath(PY_PACKAGE, engineRoot.c_str()); if (!AZ::IO::SystemFile::Exists(pyBasePath.c_str())) { AZ_Warning("python", false, "Python home path must exist! path:%s", pyBasePath.c_str()); diff --git a/Gems/EditorPythonBindings/Code/Tests/PythonTestingUtility.h b/Gems/EditorPythonBindings/Code/Tests/PythonTestingUtility.h index 143863b4c8..e2b3d1eadd 100644 --- a/Gems/EditorPythonBindings/Code/Tests/PythonTestingUtility.h +++ b/Gems/EditorPythonBindings/Code/Tests/PythonTestingUtility.h @@ -135,10 +135,6 @@ namespace UnitTest void NormalizePath(AZStd::string& ) override {} void NormalizePathKeepCase(AZStd::string& ) override {} void CalculateBranchTokenForEngineRoot(AZStd::string& ) const override {} - // Gets the engine root path for testing - const char* GetEngineRoot() const override { return m_engineRoot.c_str(); } - // Retrieves the app root path for testing - const char* GetAppRoot() const override { return m_engineRoot.c_str(); } AZ::ComponentApplication m_app; AZStd::unique_ptr m_fileIOHelper; diff --git a/Gems/LmbrCentral/Code/Tests/Builders/SliceBuilderTests.cpp b/Gems/LmbrCentral/Code/Tests/Builders/SliceBuilderTests.cpp index 73ef7143c8..0965991dfa 100644 --- a/Gems/LmbrCentral/Code/Tests/Builders/SliceBuilderTests.cpp +++ b/Gems/LmbrCentral/Code/Tests/Builders/SliceBuilderTests.cpp @@ -311,7 +311,6 @@ namespace UnitTest SerializeContext* GetSerializeContext() override { return m_serializeContext; } BehaviorContext* GetBehaviorContext() override { return nullptr; } JsonRegistrationContext* GetJsonRegistrationContext() override { return nullptr; } - const char* GetAppRoot() const override { return nullptr; } const char* GetEngineRoot() const override { return nullptr; } const char* GetExecutableFolder() const override { return nullptr; } void EnumerateEntities(const EntityCallback& /*callback*/) override {} diff --git a/Gems/LyShine/Code/Source/Draw2d.cpp b/Gems/LyShine/Code/Source/Draw2d.cpp index b0539900e2..3476c530b2 100644 --- a/Gems/LyShine/Code/Source/Draw2d.cpp +++ b/Gems/LyShine/Code/Source/Draw2d.cpp @@ -65,7 +65,7 @@ CDraw2d::~CDraw2d() } //////////////////////////////////////////////////////////////////////////////////////////////////// -void CDraw2d::OnBootstrapSceneReady([[maybe_unused]] AZ::RPI::Scene* bootstrapScene) +void CDraw2d::OnBootstrapSceneReady(AZ::RPI::Scene* bootstrapScene) { // At this point the RPI is ready for use @@ -74,16 +74,16 @@ void CDraw2d::OnBootstrapSceneReady([[maybe_unused]] AZ::RPI::Scene* bootstrapSc AZ::Data::Instance shader = AZ::RPI::LoadCriticalShader(shaderFilepath); // Set scene to be associated with the dynamic draw context - AZ::RPI::ScenePtr scene; + AZ::RPI::Scene* scene = nullptr; if (m_viewportContext) { // Use scene associated with the specified viewport context - scene = m_viewportContext->GetRenderScene(); + scene = m_viewportContext->GetRenderScene().get(); } else { - // No viewport context specified, use default scene - scene = AZ::RPI::RPISystemInterface::Get()->GetDefaultScene(); + // No viewport context specified, use main scene + scene = bootstrapScene; } AZ_Assert(scene != nullptr, "Attempting to create a DynamicDrawContext for a viewport context that has not been associated with a scene yet."); @@ -113,7 +113,7 @@ void CDraw2d::OnBootstrapSceneReady([[maybe_unused]] AZ::RPI::Scene* bootstrapSc else { // Render target support is disabled - m_dynamicDraw->SetOutputScope(scene.get()); + m_dynamicDraw->SetOutputScope(scene); } m_dynamicDraw->EndInit(); diff --git a/Gems/LyShine/Code/Source/LyShine.cpp b/Gems/LyShine/Code/Source/LyShine.cpp index 2cee4e92eb..19c6e4281e 100644 --- a/Gems/LyShine/Code/Source/LyShine.cpp +++ b/Gems/LyShine/Code/Source/LyShine.cpp @@ -653,12 +653,12 @@ void CLyShine::OnRenderTick() } //////////////////////////////////////////////////////////////////////////////////////////////////// -void CLyShine::OnBootstrapSceneReady([[maybe_unused]] AZ::RPI::Scene* bootstrapScene) +void CLyShine::OnBootstrapSceneReady(AZ::RPI::Scene* bootstrapScene) { // Load cursor if its path was set before RPI was initialized LoadUiCursor(); - LyShinePassDataRequestBus::Handler::BusConnect(AZ::RPI::RPISystemInterface::Get()->GetDefaultScene()->GetId()); + LyShinePassDataRequestBus::Handler::BusConnect(bootstrapScene->GetId()); } //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/Gems/LyShine/Code/Source/UiRenderer.cpp b/Gems/LyShine/Code/Source/UiRenderer.cpp index 3111f31615..98b376ec8b 100644 --- a/Gems/LyShine/Code/Source/UiRenderer.cpp +++ b/Gems/LyShine/Code/Source/UiRenderer.cpp @@ -52,7 +52,7 @@ bool UiRenderer::IsReady() return m_isRPIReady; } -void UiRenderer::OnBootstrapSceneReady([[maybe_unused]] AZ::RPI::Scene* bootstrapScene) +void UiRenderer::OnBootstrapSceneReady(AZ::RPI::Scene* bootstrapScene) { // At this point the RPI is ready for use @@ -64,16 +64,17 @@ void UiRenderer::OnBootstrapSceneReady([[maybe_unused]] AZ::RPI::Scene* bootstra if (m_viewportContext) { // Create a new scene based on the user specified viewport context - m_scene = CreateScene(m_viewportContext); + m_ownedScene = CreateScene(m_viewportContext); + m_scene = m_ownedScene.get(); } else { // No viewport context specified, use default scene - m_scene = AZ::RPI::RPISystemInterface::Get()->GetDefaultScene(); + m_scene = bootstrapScene; } // Create a dynamic draw context for UI Canvas drawing for the scene - m_dynamicDraw = CreateDynamicDrawContext(m_scene, uiShader); + m_dynamicDraw = CreateDynamicDrawContext(uiShader); if (m_dynamicDraw) { @@ -93,6 +94,7 @@ AZ::RPI::ScenePtr UiRenderer::CreateScene(AZStd::shared_ptrEnableAllFeatureProcessors(); // LYSHINE_ATOM_TODO - have a UI pipeline and enable only needed fps @@ -116,7 +118,6 @@ AZ::RPI::ScenePtr UiRenderer::CreateScene(AZStd::shared_ptr UiRenderer::CreateDynamicDrawContext( - AZ::RPI::ScenePtr scene, AZ::Data::Instance uiShader) { // Find the pass that renders the UI canvases after the rtt passes @@ -144,7 +145,7 @@ AZ::RHI::Ptr UiRenderer::CreateDynamicDrawContext( else { // Render target support is disabled - dynamicDraw->SetOutputScope(m_scene.get()); + dynamicDraw->SetOutputScope(m_scene); } dynamicDraw->EndInit(); diff --git a/Gems/LyShine/Code/Source/UiRenderer.h b/Gems/LyShine/Code/Source/UiRenderer.h index 3be3c832d3..0e15d41907 100644 --- a/Gems/LyShine/Code/Source/UiRenderer.h +++ b/Gems/LyShine/Code/Source/UiRenderer.h @@ -152,7 +152,6 @@ private: // member functions //! Create a dynamic draw context for this renderer AZ::RHI::Ptr CreateDynamicDrawContext( - AZ::RPI::ScenePtr scene, AZ::Data::Instance uiShader); //! Bind the global white texture for all the texture units we use @@ -175,7 +174,8 @@ protected: // attributes // Set by user when viewport context is not the main/default viewport AZStd::shared_ptr m_viewportContext; - AZ::RPI::ScenePtr m_scene; + AZ::RPI::ScenePtr m_ownedScene; + AZ::RPI::Scene* m_scene = nullptr; #ifndef _RELEASE int m_debugTextureDataRecordLevel = 0; diff --git a/Gems/Multiplayer/Code/Tests/CommonBenchmarkSetup.h b/Gems/Multiplayer/Code/Tests/CommonBenchmarkSetup.h index 3c3d77e011..238e652b52 100644 --- a/Gems/Multiplayer/Code/Tests/CommonBenchmarkSetup.h +++ b/Gems/Multiplayer/Code/Tests/CommonBenchmarkSetup.h @@ -41,7 +41,6 @@ namespace Multiplayer AZ::SerializeContext* GetSerializeContext() override { return {}; } AZ::BehaviorContext* GetBehaviorContext() override { return {}; } AZ::JsonRegistrationContext* GetJsonRegistrationContext() override { return {}; } - const char* GetAppRoot() const override { return {}; } const char* GetEngineRoot() const override { return {}; } const char* GetExecutableFolder() const override { return {}; } void QueryApplicationType([[maybe_unused]] AZ::ApplicationTypeQuery& appType) const override {} diff --git a/Gems/Multiplayer/Code/Tests/MockInterfaces.h b/Gems/Multiplayer/Code/Tests/MockInterfaces.h index 8cebf280b9..8fd32bd79b 100644 --- a/Gems/Multiplayer/Code/Tests/MockInterfaces.h +++ b/Gems/Multiplayer/Code/Tests/MockInterfaces.h @@ -146,7 +146,6 @@ namespace UnitTest MOCK_METHOD0(GetSerializeContext, AZ::SerializeContext* ()); MOCK_METHOD0(GetBehaviorContext, AZ::BehaviorContext* ()); MOCK_METHOD0(GetJsonRegistrationContext, AZ::JsonRegistrationContext* ()); - MOCK_CONST_METHOD0(GetAppRoot, const char* ()); MOCK_CONST_METHOD0(GetEngineRoot, const char* ()); MOCK_CONST_METHOD0(GetExecutableFolder, const char* ()); MOCK_METHOD0(GetDrillerManager, AZ::Debug::DrillerManager* ()); diff --git a/Gems/NvCloth/Assets/Objects/cloth/Environment/cloth_blinds.fbx.assetinfo b/Gems/NvCloth/Assets/Objects/cloth/Environment/Cloth_Blinds.fbx.assetinfo similarity index 100% rename from Gems/NvCloth/Assets/Objects/cloth/Environment/cloth_blinds.fbx.assetinfo rename to Gems/NvCloth/Assets/Objects/cloth/Environment/Cloth_Blinds.fbx.assetinfo diff --git a/Gems/NvCloth/Assets/Objects/cloth/Environment/cloth_blinds_broken.fbx.assetinfo b/Gems/NvCloth/Assets/Objects/cloth/Environment/Cloth_Blinds_Broken.fbx.assetinfo similarity index 100% rename from Gems/NvCloth/Assets/Objects/cloth/Environment/cloth_blinds_broken.fbx.assetinfo rename to Gems/NvCloth/Assets/Objects/cloth/Environment/Cloth_Blinds_Broken.fbx.assetinfo diff --git a/Gems/NvCloth/Assets/Objects/cloth/Environment/cloth_locked_corners_four.fbx.assetinfo b/Gems/NvCloth/Assets/Objects/cloth/Environment/Cloth_Locked_Corners_Four.fbx.assetinfo similarity index 100% rename from Gems/NvCloth/Assets/Objects/cloth/Environment/cloth_locked_corners_four.fbx.assetinfo rename to Gems/NvCloth/Assets/Objects/cloth/Environment/Cloth_Locked_Corners_Four.fbx.assetinfo diff --git a/Gems/NvCloth/Assets/Objects/cloth/Environment/cloth_locked_corners_two.fbx.assetinfo b/Gems/NvCloth/Assets/Objects/cloth/Environment/Cloth_Locked_Corners_Two.fbx.assetinfo similarity index 100% rename from Gems/NvCloth/Assets/Objects/cloth/Environment/cloth_locked_corners_two.fbx.assetinfo rename to Gems/NvCloth/Assets/Objects/cloth/Environment/Cloth_Locked_Corners_Two.fbx.assetinfo diff --git a/Gems/NvCloth/Assets/Objects/cloth/Environment/cloth_locked_edge.fbx.assetinfo b/Gems/NvCloth/Assets/Objects/cloth/Environment/Cloth_Locked_Edge.fbx.assetinfo similarity index 100% rename from Gems/NvCloth/Assets/Objects/cloth/Environment/cloth_locked_edge.fbx.assetinfo rename to Gems/NvCloth/Assets/Objects/cloth/Environment/Cloth_Locked_Edge.fbx.assetinfo diff --git a/Gems/PhysX/Code/Mocks/PhysX/MockPhysXHeightfieldProviderComponent.h b/Gems/PhysX/Code/Mocks/PhysX/MockPhysXHeightfieldProviderComponent.h index 7e500881c0..d462c30158 100644 --- a/Gems/PhysX/Code/Mocks/PhysX/MockPhysXHeightfieldProviderComponent.h +++ b/Gems/PhysX/Code/Mocks/PhysX/MockPhysXHeightfieldProviderComponent.h @@ -69,6 +69,10 @@ namespace UnitTest MOCK_CONST_METHOD1(UpdateHeights, AZStd::vector(const AZ::Aabb& dirtyRegion)); MOCK_CONST_METHOD1(UpdateHeightsAndMaterials, AZStd::vector(const AZ::Aabb& dirtyRegion)); MOCK_CONST_METHOD0(GetHeightfieldAabb, AZ::Aabb()); + MOCK_CONST_METHOD0(GetHeightfieldMinHeight, float()); + MOCK_CONST_METHOD0(GetHeightfieldMaxHeight, float()); + MOCK_CONST_METHOD0(GetHeightfieldGridColumns, int32_t()); + MOCK_CONST_METHOD0(GetHeightfieldGridRows, int32_t()); }; } // namespace UnitTest diff --git a/Gems/PhysX/Code/Source/Debug/PhysXDebug.cpp b/Gems/PhysX/Code/Source/Debug/PhysXDebug.cpp index bc9265a92a..fb8e61289d 100644 --- a/Gems/PhysX/Code/Source/Debug/PhysXDebug.cpp +++ b/Gems/PhysX/Code/Source/Debug/PhysXDebug.cpp @@ -13,7 +13,7 @@ #include #include #include -#include +#include namespace PhysX { @@ -108,8 +108,7 @@ namespace PhysX AzFramework::StringFunc::Append(filename, m_config.m_pvdConfigurationData.m_fileName.c_str()); AzFramework::StringFunc::Append(filename, ".pxd2"); - AZStd::string rootDirectory; - AZ::ComponentApplicationBus::BroadcastResult(rootDirectory, &AZ::ComponentApplicationRequests::GetAppRoot); + AZStd::string rootDirectory{ AZStd::string_view(AZ::Utils::GetEnginePath()) }; // Create the full filepath. AZStd::string safeFilePath; diff --git a/Gems/Prefab/PrefabBuilder/PrefabBuilderTests.cpp b/Gems/Prefab/PrefabBuilder/PrefabBuilderTests.cpp index d33b453c01..2ed05b4dfe 100644 --- a/Gems/Prefab/PrefabBuilder/PrefabBuilderTests.cpp +++ b/Gems/Prefab/PrefabBuilder/PrefabBuilderTests.cpp @@ -102,7 +102,9 @@ namespace UnitTest prefabBuilderComponent.Activate(); AZStd::vector jobProducts; - auto&& prefabDom = prefabSystemComponentInterface->FindTemplateDom(parentInstance->GetTemplateId()); + // Make a copy of the template DOM, as the prefab system still owns the existing template + AzToolsFramework::Prefab::PrefabDom prefabDom; + prefabDom.CopyFrom(prefabSystemComponentInterface->FindTemplateDom(parentInstance->GetTemplateId()), prefabDom.GetAllocator(), false); ASSERT_TRUE(prefabBuilderComponent.ProcessPrefab({AZ::Crc32("pc")}, "parent.prefab", "unused", AZ::Uuid(), prefabDom, jobProducts)); diff --git a/Gems/Profiler/Code/Source/CpuProfilerImpl.cpp b/Gems/Profiler/Code/Source/CpuProfilerImpl.cpp index c88afdecd0..f0d39d48e4 100644 --- a/Gems/Profiler/Code/Source/CpuProfilerImpl.cpp +++ b/Gems/Profiler/Code/Source/CpuProfilerImpl.cpp @@ -282,7 +282,7 @@ namespace Profiler m_stackLevel = 0; m_cachedTimeRegionMap.clear(); m_timeRegionStack.clear(); - m_cachedTimeRegions.clear(); + ResetCachedData(); } timeRegion.m_stackDepth = aznumeric_cast(m_stackLevel); @@ -329,8 +329,21 @@ namespace Profiler { return; } - // Add an entry to the cached region - m_cachedTimeRegions.push_back(timeRegionCached); + // Add an entry to the cached region. Discard excess data in case there is too much to handle. + if (m_cachedTimeRegions.size() < TimeRegionStackSize) + { + m_cachedTimeRegions.push_back(timeRegionCached); + } + // Warn only once per thread if the cached data limit has been reached. + else if (!m_cachedDataLimitReached) + { + AZ_Warning( + "Profiler", false, + "Limit for profiling data has been reached by thread %i. Excess data will be discarded. Considering moving or reducing " + "profiler markers to prevent data loss.", + m_executingThreadId); + m_cachedDataLimitReached = true; + } // If the stack is empty, add it to the local cache map. Only gets called when the stack is empty // NOTE: this is where the largest overhead will be, but due to it only being called when the stack is empty @@ -354,7 +367,7 @@ namespace Profiler } // Clear the cached regions - m_cachedTimeRegions.clear(); + ResetCachedData(); } } @@ -371,10 +384,17 @@ namespace Profiler m_cachedTimeRegionMap.clear(); m_hitSizeLimitMap.clear(); } + m_cachedTimeRegionMutex.unlock(); } } + void CpuTimingLocalStorage::ResetCachedData() + { + m_cachedTimeRegions.clear(); + m_cachedDataLimitReached = false; + } + // --- CpuProfilingStatisticsSerializer --- CpuProfilingStatisticsSerializer::CpuProfilingStatisticsSerializer(const AZStd::ring_buffer& continuousData) diff --git a/Gems/Profiler/Code/Source/CpuProfilerImpl.h b/Gems/Profiler/Code/Source/CpuProfilerImpl.h index 1046b72cff..6611fbd1e5 100644 --- a/Gems/Profiler/Code/Source/CpuProfilerImpl.h +++ b/Gems/Profiler/Code/Source/CpuProfilerImpl.h @@ -50,6 +50,9 @@ namespace Profiler // Tries to flush the map to the passed parameter, only if the thread's mutex is unlocked void TryFlushCachedMap(CpuProfiler::ThreadTimeRegionMap& cachedRegionMap); + // Clears m_cachedTimeRegions and resets m_cachedDataLimitReached flag. + void ResetCachedData(); + AZStd::thread_id m_executingThreadId; // Keeps track of the current thread's stack depth uint32_t m_stackLevel = 0u; @@ -75,6 +78,9 @@ namespace Profiler // Keep track of the regions that have hit the size limit so we don't have to lock to check AZStd::map m_hitSizeLimitMap; + + // Keeps track of the first time cached data limit was reached. + bool m_cachedDataLimitReached = false; }; //! CpuProfiler will keep track of the registered threads, and diff --git a/Gems/ScriptCanvas/Code/Tests/ScriptCanvasBuilderTests.cpp b/Gems/ScriptCanvas/Code/Tests/ScriptCanvasBuilderTests.cpp index cba14feac6..33982bbcd7 100644 --- a/Gems/ScriptCanvas/Code/Tests/ScriptCanvasBuilderTests.cpp +++ b/Gems/ScriptCanvas/Code/Tests/ScriptCanvasBuilderTests.cpp @@ -89,7 +89,6 @@ protected: AZ::SerializeContext* GetSerializeContext() override { return m_serializeContext; } AZ::BehaviorContext* GetBehaviorContext() 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* GetExecutableFolder() const override { return nullptr; } void EnumerateEntities(const AZ::ComponentApplicationRequests::EntityCallback& /*callback*/) override {} diff --git a/Gems/ScriptCanvasTesting/Code/Source/Framework/ScriptCanvasTestFixture.h b/Gems/ScriptCanvasTesting/Code/Source/Framework/ScriptCanvasTestFixture.h index f45b74d4f1..6e38f71eec 100644 --- a/Gems/ScriptCanvasTesting/Code/Source/Framework/ScriptCanvasTestFixture.h +++ b/Gems/ScriptCanvasTesting/Code/Source/Framework/ScriptCanvasTestFixture.h @@ -91,14 +91,6 @@ namespace ScriptCanvasTests AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance(); AZ_Assert(fileIO, "SC unit tests require filehandling"); - if (!fileIO->GetAlias("@engroot@")) - { - const char* engineRoot = nullptr; - AzFramework::ApplicationRequests::Bus::BroadcastResult(engineRoot, &AzFramework::ApplicationRequests::GetEngineRoot); - AZ_Assert(engineRoot, "null engine root"); - fileIO->SetAlias("@engroot@", engineRoot); - } - s_setupSucceeded = fileIO->GetAlias("@engroot@") != nullptr; AZ::TickBus::AllowFunctionQueuing(true); diff --git a/Gems/Terrain/Assets/Shaders/Terrain/TerrainCommon.azsli b/Gems/Terrain/Assets/Shaders/Terrain/TerrainCommon.azsli index 72c1af953c..cd680f721a 100644 --- a/Gems/Terrain/Assets/Shaders/Terrain/TerrainCommon.azsli +++ b/Gems/Terrain/Assets/Shaders/Terrain/TerrainCommon.azsli @@ -218,9 +218,10 @@ float4x4 GetObject_WorldMatrix() float GetHeight(float2 origUv) { - float2 uv = clamp(origUv + (ObjectSrg::m_terrainData.m_uvStep * 0.5f), 0.0f, 1.0f); - float height = 0.0f; + float2 halfStep = ObjectSrg::m_terrainData.m_uvStep * 0.5; + float2 uv = origUv * (1.0 - ObjectSrg::m_terrainData.m_uvStep) + halfStep; + float height = 0.0f; if (o_useTerrainSmoothing) { float2 textureSize; diff --git a/Gems/Terrain/Code/Source/Components/TerrainHeightGradientListComponent.cpp b/Gems/Terrain/Code/Source/Components/TerrainHeightGradientListComponent.cpp index 231d5abc28..e9c9874af5 100644 --- a/Gems/Terrain/Code/Source/Components/TerrainHeightGradientListComponent.cpp +++ b/Gems/Terrain/Code/Source/Components/TerrainHeightGradientListComponent.cpp @@ -151,24 +151,32 @@ namespace Terrain { float maxSample = 0.0f; terrainExists = false; - - GradientSignal::GradientSampleParams params(AZ::Vector3(inPosition.GetX(), inPosition.GetY(), 0.0f)); - - // Right now, when the list contains multiple entries, we will use the highest point from each gradient. - // This is needed in part because gradients don't really have world bounds, so they exist everywhere but generally have a value - // of 0 outside their data bounds if they're using bounded data. We should examine the possibility of extending the gradient API - // to provide actual bounds so that it's possible to detect if the gradient even 'exists' in an area, at which point we could just - // make this list a prioritized list from top to bottom for any points that overlap. - for (auto& gradientId : m_configuration.m_gradientEntities) + AZ_WarningOnce("Terrain", !m_isRequestInProgress, "Detected cyclic dependences with terrain height entity references"); + if (!m_isRequestInProgress) { - // If gradients ever provide bounds, or if we add a value threshold in this component, it would be possible for terrain - // to *not* exist at a specific point. - terrainExists = true; + m_isRequestInProgress = true; + GradientSignal::GradientSampleParams params(AZ::Vector3(inPosition.GetX(), inPosition.GetY(), 0.0f)); - float sample = 0.0f; - GradientSignal::GradientRequestBus::EventResult( - sample, gradientId, &GradientSignal::GradientRequestBus::Events::GetValue, params); - maxSample = AZ::GetMax(maxSample, sample); + // Right now, when the list contains multiple entries, we will use the highest point from each gradient. + // This is needed in part because gradients don't really have world bounds, so they exist everywhere but generally have a value + // of 0 outside their data bounds if they're using bounded data. We should examine the possibility of extending the gradient + // API to provide actual bounds so that it's possible to detect if the gradient even 'exists' in an area, at which point we + // could just make this list a prioritized list from top to bottom for any points that overlap. + for (auto& gradientId : m_configuration.m_gradientEntities) + { + if (gradientId.IsValid()) + { + // If gradients ever provide bounds, or if we add a value threshold in this component, it would be possible for terrain + // to *not* exist at a specific point. + terrainExists = true; + + float sample = 0.0f; + GradientSignal::GradientRequestBus::EventResult( + sample, gradientId, &GradientSignal::GradientRequestBus::Events::GetValue, params); + maxSample = AZ::GetMax(maxSample, sample); + } + } + m_isRequestInProgress = false; } const float height = AZ::Lerp(m_cachedShapeBounds.GetMin().GetZ(), m_cachedShapeBounds.GetMax().GetZ(), maxSample); diff --git a/Gems/Terrain/Code/Source/Components/TerrainHeightGradientListComponent.h b/Gems/Terrain/Code/Source/Components/TerrainHeightGradientListComponent.h index 6c3fd7b820..b5b1a44192 100644 --- a/Gems/Terrain/Code/Source/Components/TerrainHeightGradientListComponent.h +++ b/Gems/Terrain/Code/Source/Components/TerrainHeightGradientListComponent.h @@ -91,6 +91,9 @@ namespace Terrain AZ::Vector2 m_cachedHeightQueryResolution{ 1.0f, 1.0f }; AZ::Aabb m_cachedShapeBounds; + // prevent recursion in case user attaches cyclic dependences + mutable bool m_isRequestInProgress{ false }; + LmbrCentral::DependencyMonitor m_dependencyMonitor; }; } diff --git a/Gems/Terrain/Code/Source/Components/TerrainPhysicsColliderComponent.cpp b/Gems/Terrain/Code/Source/Components/TerrainPhysicsColliderComponent.cpp index e9c235c1bd..6c76e90fd0 100644 --- a/Gems/Terrain/Code/Source/Components/TerrainPhysicsColliderComponent.cpp +++ b/Gems/Terrain/Code/Source/Components/TerrainPhysicsColliderComponent.cpp @@ -185,12 +185,28 @@ namespace Terrain void TerrainPhysicsColliderComponent::GetHeightfieldHeightBounds(float& minHeightBounds, float& maxHeightBounds) const { - AZ::Aabb heightfieldAabb = GetHeightfieldAabb(); + const AZ::Aabb heightfieldAabb = GetHeightfieldAabb(); // Because our terrain heights are relative to the center of the bounding box, the min and max allowable heights are also // relative to the center. They are also clamped to the size of the bounding box. - minHeightBounds = -(heightfieldAabb.GetZExtent() / 2.0f); maxHeightBounds = heightfieldAabb.GetZExtent() / 2.0f; + minHeightBounds = -maxHeightBounds; + } + + float TerrainPhysicsColliderComponent::GetHeightfieldMinHeight() const + { + float minHeightBounds{ 0.0f }; + float maxHeightBounds{ 0.0f }; + GetHeightfieldHeightBounds(minHeightBounds, maxHeightBounds); + return minHeightBounds; + } + + float TerrainPhysicsColliderComponent::GetHeightfieldMaxHeight() const + { + float minHeightBounds{ 0.0f }; + float maxHeightBounds{ 0.0f }; + GetHeightfieldHeightBounds(minHeightBounds, maxHeightBounds); + return maxHeightBounds; } AZ::Transform TerrainPhysicsColliderComponent::GetHeightfieldTransform() const @@ -199,9 +215,7 @@ namespace Terrain AZ::Vector3 translate; AZ::TransformBus::EventResult(translate, GetEntityId(), &AZ::TransformBus::Events::GetWorldTranslation); - AZ::Transform transform = AZ::Transform::CreateTranslation(translate); - - return transform; + return AZ::Transform::CreateTranslation(translate); } void TerrainPhysicsColliderComponent::GenerateHeightsInBounds(AZStd::vector& heights) const @@ -298,6 +312,24 @@ namespace Terrain numRows = aznumeric_cast((bounds.GetMax().GetY() - bounds.GetMin().GetY()) / gridResolution.GetY()); } + int32_t TerrainPhysicsColliderComponent::GetHeightfieldGridColumns() const + { + int32_t numColumns{ 0 }; + int32_t numRows{ 0 }; + + GetHeightfieldGridSize(numColumns, numRows); + return numColumns; + } + + int32_t TerrainPhysicsColliderComponent::GetHeightfieldGridRows() const + { + int32_t numColumns{ 0 }; + int32_t numRows{ 0 }; + + GetHeightfieldGridSize(numColumns, numRows); + return numRows; + } + AZStd::vector TerrainPhysicsColliderComponent::GetMaterialList() const { return AZStd::vector(); diff --git a/Gems/Terrain/Code/Source/Components/TerrainPhysicsColliderComponent.h b/Gems/Terrain/Code/Source/Components/TerrainPhysicsColliderComponent.h index e268223689..6462909c89 100644 --- a/Gems/Terrain/Code/Source/Components/TerrainPhysicsColliderComponent.h +++ b/Gems/Terrain/Code/Source/Components/TerrainPhysicsColliderComponent.h @@ -58,7 +58,11 @@ namespace Terrain // HeightfieldProviderRequestsBus AZ::Vector2 GetHeightfieldGridSpacing() const override; void GetHeightfieldGridSize(int32_t& numColumns, int32_t& numRows) const override; + int32_t GetHeightfieldGridColumns() const override; + int32_t GetHeightfieldGridRows() const override; void GetHeightfieldHeightBounds(float& minHeightBounds, float& maxHeightBounds) const override; + float GetHeightfieldMinHeight() const override; + float GetHeightfieldMaxHeight() const override; AZ::Aabb GetHeightfieldAabb() const override; AZ::Transform GetHeightfieldTransform() const override; AZStd::vector GetMaterialList() const override; diff --git a/Gems/Terrain/Code/Source/Components/TerrainWorldDebuggerComponent.cpp b/Gems/Terrain/Code/Source/Components/TerrainWorldDebuggerComponent.cpp index e140129563..79fd5509f2 100644 --- a/Gems/Terrain/Code/Source/Components/TerrainWorldDebuggerComponent.cpp +++ b/Gems/Terrain/Code/Source/Components/TerrainWorldDebuggerComponent.cpp @@ -218,6 +218,12 @@ namespace Terrain AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult( queryResolution, &AzFramework::Terrain::TerrainDataRequests::GetTerrainHeightQueryResolution); + // Take the dirty region and adjust the Z values to the world min/max so that even if the dirty region falls outside the current + // world bounds, we still update the wireframe accordingly. + AZ::Aabb dirtyRegion2D = AZ::Aabb::CreateFromMinMaxValues( + dirtyRegion.GetMin().GetX(), dirtyRegion.GetMin().GetY(), worldBounds.GetMin().GetZ(), + dirtyRegion.GetMax().GetX(), dirtyRegion.GetMax().GetY(), worldBounds.GetMax().GetZ()); + // Calculate the world size of each sector. Note that this size actually ends at the last point, not the last square. // So for example, the sector size for 3 points will go from (*--*--*) even though it will be used to draw (*--*--*--). const float xSectorSize = (queryResolution.GetX() * SectorSizeInGridPoints); @@ -230,7 +236,7 @@ namespace Terrain // If we haven't cached anything before, or if the world bounds has changed, clear our cache structure and repopulate it // with WireframeSector entries with the proper AABB sizes. - if (!m_wireframeBounds.IsValid() || !dirtyRegion.IsValid() || !m_wireframeBounds.IsClose(worldBounds)) + if (!m_wireframeBounds.IsValid() || !dirtyRegion2D.IsValid() || !m_wireframeBounds.IsClose(worldBounds)) { m_wireframeBounds = worldBounds; @@ -266,7 +272,7 @@ namespace Terrain // For each sector, if it overlaps with the dirty region, clear it out and recache the wireframe line data. for (auto& sector : m_wireframeSectors) { - if (dirtyRegion.IsValid() && !dirtyRegion.Overlaps(sector.m_aabb)) + if (dirtyRegion2D.IsValid() && !dirtyRegion2D.Overlaps(sector.m_aabb)) { continue; } diff --git a/Gems/Terrain/Code/Source/Components/TerrainWorldRendererComponent.cpp b/Gems/Terrain/Code/Source/Components/TerrainWorldRendererComponent.cpp index 9cd9ce9fb2..5afaedc517 100644 --- a/Gems/Terrain/Code/Source/Components/TerrainWorldRendererComponent.cpp +++ b/Gems/Terrain/Code/Source/Components/TerrainWorldRendererComponent.cpp @@ -46,6 +46,7 @@ namespace Terrain ->EnumAttribute(TerrainWorldRendererConfig::WorldSize::_4096Meters, "4 Kilometers") ->EnumAttribute(TerrainWorldRendererConfig::WorldSize::_8192Meters, "8 Kilometers") ->EnumAttribute(TerrainWorldRendererConfig::WorldSize::_16384Meters, "16 Kilometers") + ->Attribute(AZ::Edit::Attributes::Visibility, false) // Keeping invisible until it's hooked up under the hood ; } } diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.cpp b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.cpp index 73f1c3967c..55713e696a 100644 --- a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.cpp +++ b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.cpp @@ -417,10 +417,10 @@ namespace Terrain m_areaData.m_rebuildSectors = false; m_sectorData.clear(); - const float xFirstPatchStart = terrainBounds.GetMin().GetX() - fmod(terrainBounds.GetMin().GetX(), GridMeters); - const float xLastPatchStart = terrainBounds.GetMax().GetX() - fmod(terrainBounds.GetMax().GetX(), GridMeters); - const float yFirstPatchStart = terrainBounds.GetMin().GetY() - fmod(terrainBounds.GetMin().GetY(), GridMeters); - const float yLastPatchStart = terrainBounds.GetMax().GetY() - fmod(terrainBounds.GetMax().GetY(), GridMeters); + const float xFirstPatchStart = AZStd::floorf(terrainBounds.GetMin().GetX() / GridMeters) * GridMeters; + const float xLastPatchStart = AZStd::floorf(terrainBounds.GetMax().GetX() / GridMeters) * GridMeters; + const float yFirstPatchStart = AZStd::floorf(terrainBounds.GetMin().GetY() / GridMeters) * GridMeters; + const float yLastPatchStart = AZStd::floorf(terrainBounds.GetMax().GetY() / GridMeters) * GridMeters; const auto& materialAsset = m_materialInstance->GetAsset(); const auto& shaderAsset = materialAsset->GetMaterialTypeAsset()->GetShaderAssetForObjectSrg(); @@ -603,7 +603,7 @@ namespace Terrain // For every distance doubling beyond a minDistanceForLod0, we only need half the mesh density. Each LOD // is exactly half the resolution of the last. - const float lodForCamera = floorf(AZ::GetMax(0.0f, log2f(sectorDistance / minDistanceForLod0))); + const float lodForCamera = AZStd::floorf(AZ::GetMax(0.0f, log2f(sectorDistance / minDistanceForLod0))); // All cameras should render the same LOD so effects like shadows are consistent. lodChoice = AZ::GetMin(lodChoice, aznumeric_cast(lodForCamera)); diff --git a/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.cpp b/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.cpp index 8d39340b06..20041e2fc8 100644 --- a/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.cpp +++ b/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.cpp @@ -222,20 +222,31 @@ float TerrainSystem::GetHeightSynchronous(float x, float y, Sampler sampler, boo float TerrainSystem::GetTerrainAreaHeight(float x, float y, bool& terrainExists) const { - AZ::Vector3 inPosition((float)x, (float)y, m_currentSettings.m_worldBounds.GetMin().GetZ()); - float height = m_currentSettings.m_worldBounds.GetMin().GetZ(); + const float worldMin = m_currentSettings.m_worldBounds.GetMin().GetZ(); + AZ::Vector3 inPosition(x, y, worldMin); + float height = worldMin; + terrainExists = false; AZStd::shared_lock lock(m_areaMutex); - for (auto& [areaId, areaBounds] : m_registeredAreas) + for (auto& [areaId, areaData] : m_registeredAreas) { - inPosition.SetZ(areaBounds.GetMin().GetZ()); - if (areaBounds.Contains(inPosition)) + const float areaMin = areaData.m_areaBounds.GetMin().GetZ(); + inPosition.SetZ(areaMin); + if (areaData.m_areaBounds.Contains(inPosition)) { AZ::Vector3 outPosition; Terrain::TerrainAreaHeightRequestBus::Event( areaId, &Terrain::TerrainAreaHeightRequestBus::Events::GetHeight, inPosition, outPosition, terrainExists); height = outPosition.GetZ(); + if (!terrainExists) + { + // If the terrain height provider doesn't have any data, then check the area's "use ground plane" setting. + // If it's set, then create a default ground plane by saying terrain exists at the minimum height for the area. + // Otherwise, we'll set the height at the terrain world minimum and say it doesn't exist. + terrainExists = areaData.m_useGroundPlane; + height = areaData.m_useGroundPlane ? areaMin : worldMin; + } break; } } @@ -395,12 +406,12 @@ AZ::EntityId TerrainSystem::FindBestAreaEntityAtPosition(float x, float y, AZ::A AZStd::shared_lock lock(m_areaMutex); // The areas are sorted into priority order: the first area that contains inPosition is the most suitable. - for (const auto& [areaId, areaBounds] : m_registeredAreas) + for (const auto& [areaId, areaData] : m_registeredAreas) { - inPosition.SetZ(areaBounds.GetMin().GetZ()); - if (areaBounds.Contains(inPosition)) + inPosition.SetZ(areaData.m_areaBounds.GetMin().GetZ()); + if (areaData.m_areaBounds.Contains(inPosition)) { - bounds = areaBounds; + bounds = areaData.m_areaBounds; return areaId; } } @@ -548,7 +559,12 @@ void TerrainSystem::RegisterArea(AZ::EntityId areaId) AZStd::unique_lock lock(m_areaMutex); AZ::Aabb aabb = AZ::Aabb::CreateNull(); LmbrCentral::ShapeComponentRequestsBus::EventResult(aabb, areaId, &LmbrCentral::ShapeComponentRequestsBus::Events::GetEncompassingAabb); - m_registeredAreas[areaId] = aabb; + + // Cache off whether or not this layer spawner should have a default ground plane when no other terrain height data exists. + bool useGroundPlane = false; + Terrain::TerrainSpawnerRequestBus::EventResult(useGroundPlane, areaId, &Terrain::TerrainSpawnerRequestBus::Events::GetUseGroundPlane); + + m_registeredAreas[areaId] = { aabb, useGroundPlane }; m_dirtyRegion.AddAabb(aabb); m_terrainHeightDirty = true; m_terrainSurfacesDirty = true; @@ -565,10 +581,10 @@ void TerrainSystem::UnregisterArea(AZ::EntityId areaId) m_registeredAreas, [areaId, this](const auto& item) { - auto const& [entityId, aabb] = item; + auto const& [entityId, areaData] = item; if (areaId == entityId) { - m_dirtyRegion.AddAabb(aabb); + m_dirtyRegion.AddAabb(areaData.m_areaBounds); m_terrainHeightDirty = true; m_terrainSurfacesDirty = true; return true; @@ -585,10 +601,10 @@ void TerrainSystem::RefreshArea(AZ::EntityId areaId, AzFramework::Terrain::Terra auto areaAabb = m_registeredAreas.find(areaId); - AZ::Aabb oldAabb = (areaAabb != m_registeredAreas.end()) ? areaAabb->second : AZ::Aabb::CreateNull(); + AZ::Aabb oldAabb = (areaAabb != m_registeredAreas.end()) ? areaAabb->second.m_areaBounds : AZ::Aabb::CreateNull(); AZ::Aabb newAabb = AZ::Aabb::CreateNull(); LmbrCentral::ShapeComponentRequestsBus::EventResult(newAabb, areaId, &LmbrCentral::ShapeComponentRequestsBus::Events::GetEncompassingAabb); - m_registeredAreas[areaId] = newAabb; + m_registeredAreas[areaId].m_areaBounds = newAabb; AZ::Aabb expandedAabb = oldAabb; expandedAabb.AddAabb(newAabb); diff --git a/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.h b/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.h index 022cd218cc..c6bd19fdda 100644 --- a/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.h +++ b/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.h @@ -168,7 +168,14 @@ namespace Terrain bool m_terrainSurfacesDirty = false; AZ::Aabb m_dirtyRegion; + // Cached data for each terrain area to use when looking up terrain data. + struct TerrainAreaData + { + AZ::Aabb m_areaBounds{ AZ::Aabb::CreateNull() }; + bool m_useGroundPlane{ false }; + }; + mutable AZStd::shared_mutex m_areaMutex; - AZStd::map m_registeredAreas; + AZStd::map m_registeredAreas; }; } // namespace Terrain diff --git a/Tools/LyTestTools/ly_test_tools/o3de/editor_test.py b/Tools/LyTestTools/ly_test_tools/o3de/editor_test.py index 781977cf33..392838d180 100644 --- a/Tools/LyTestTools/ly_test_tools/o3de/editor_test.py +++ b/Tools/LyTestTools/ly_test_tools/o3de/editor_test.py @@ -634,6 +634,7 @@ class EditorTestSuite(): else: test_result = Result.Fail.create(test_spec, output, editor_log_content) except WaitTimeoutError: + output = editor.get_output() editor.kill() editor_log_content = editor_utils.retrieve_editor_log_content(run_id, log_name, workspace) test_result = Result.Timeout.create(test_spec, output, test_spec.timeout, editor_log_content) diff --git a/cmake/Projects.cmake b/cmake/Projects.cmake index 2c42533e35..13161c9e0f 100644 --- a/cmake/Projects.cmake +++ b/cmake/Projects.cmake @@ -183,7 +183,9 @@ if("${CMAKE_INSTALL_CONFIG_NAME}" MATCHES "^([Rr][Ee][Ll][Ee][Aa][Ss][Ee])$") cmake_path(GET gem_source_path_setreg FILENAME setreg_filename) list(APPEND artifacts_to_remove "${cache_product_path}/registry/${setreg_filename}") endforeach() - file(REMOVE ${artifacts_to_remove}) + if (artifacts_to_remove) + file(REMOVE ${artifacts_to_remove}) + endif() endif() ]=]) diff --git a/scripts/o3de/o3de/download.py b/scripts/o3de/o3de/download.py index 98f5d051a4..9e576d64b5 100644 --- a/scripts/o3de/o3de/download.py +++ b/scripts/o3de/o3de/download.py @@ -20,6 +20,7 @@ import sys import urllib.parse import urllib.request import zipfile +from datetime import datetime from o3de import manifest, repo, utils, validation, register @@ -88,10 +89,9 @@ def get_downloadable(engine_name: str = None, search_func = lambda manifest_json_data: repo.search_repo(manifest_json_data, engine_name, project_name, gem_name, template_name) return repo.search_o3de_object(manifest_json, o3de_object_uris, search_func) - def download_o3de_object(object_name: str, default_folder_name: str, dest_path: str or pathlib.Path, object_type: str, downloadable_kwarg_key, skip_auto_register: bool, - download_progress_callback = None) -> int: + force_overwrite: bool, download_progress_callback = None) -> int: download_path = manifest.get_o3de_cache_folder() / default_folder_name / object_name download_path.mkdir(parents=True, exist_ok=True) @@ -125,8 +125,15 @@ def download_o3de_object(object_name: str, default_folder_name: str, dest_path: logger.error(f'Destination path cannot be empty.') return 1 if dest_path.exists(): - logger.error(f'Destination path {dest_path} already exists.') - return 1 + if not force_overwrite: + logger.error(f'Destination path {dest_path} already exists.') + return 1 + else: + try: + shutil.rmtree(dest_path) + except OSError: + logger.error(f'Could not remove existing destination path {dest_path}.') + return 1 dest_path.mkdir(exist_ok=True) @@ -149,38 +156,119 @@ def download_o3de_object(object_name: str, default_folder_name: str, dest_path: def download_engine(engine_name: str, dest_path: str or pathlib.Path, skip_auto_register: bool, + force_overwrite: bool, download_progress_callback = None) -> int: - return download_o3de_object(engine_name, 'engines', dest_path, 'engine', 'engine_name', skip_auto_register, download_progress_callback) + return download_o3de_object(engine_name, + 'engines', + dest_path, + 'engine', + 'engine_name', + skip_auto_register, + force_overwrite, + download_progress_callback) def download_project(project_name: str, dest_path: str or pathlib.Path, skip_auto_register: bool, + force_overwrite: bool, download_progress_callback = None) -> int: - return download_o3de_object(project_name, 'projects', dest_path, 'project', 'project_name', skip_auto_register, download_progress_callback) + return download_o3de_object(project_name, + 'projects', + dest_path, + 'project', + 'project_name', + skip_auto_register, + force_overwrite, + download_progress_callback) def download_gem(gem_name: str, dest_path: str or pathlib.Path, skip_auto_register: bool, + force_overwrite: bool, download_progress_callback = None) -> int: - return download_o3de_object(gem_name, 'gems', dest_path, 'gem', 'gem_name', skip_auto_register, download_progress_callback) + return download_o3de_object(gem_name, + 'gems', + dest_path, + 'gem', + 'gem_name', + skip_auto_register, + force_overwrite, + download_progress_callback) def download_template(template_name: str, dest_path: str or pathlib.Path, skip_auto_register: bool, + force_overwrite: bool, download_progress_callback = None) -> int: - return download_o3de_object(template_name, 'templates', dest_path, 'template', 'template_name', skip_auto_register, download_progress_callback) + return download_o3de_object(template_name, + 'templates', + dest_path, + 'template', + 'template_name', + skip_auto_register, + force_overwrite, + download_progress_callback) def download_restricted(restricted_name: str, dest_path: str or pathlib.Path, skip_auto_register: bool, + force_overwrite: bool, download_progress_callback = None) -> int: - return download_o3de_object(restricted_name, 'restricted', dest_path, 'restricted', 'restricted_name', skip_auto_register, download_progress_callback) + return download_o3de_object(restricted_name, + 'restricted', + dest_path, + 'restricted', + 'restricted_name', + skip_auto_register, + force_overwrite, + download_progress_callback) +def is_o3de_object_update_available(object_name: str, downloadable_kwarg_key, local_last_updated: str) -> bool: + downloadable_object_data = get_downloadable(**{downloadable_kwarg_key : object_name}) + if not downloadable_object_data: + logger.error(f'Downloadable o3de object {object_name} not found.') + return False + + try: + repo_copy_updated_string = downloadable_object_data['last_updated'] + except KeyError: + logger.warn(f'last_updated field not found for {object_name}.') + return False + + try: + local_last_updated_time = datetime.fromisoformat(local_last_updated) + except ValueError: + logger.warn(f'last_updated field has incorrect format for local copy of {downloadable_kwarg_key} {object_name}.') + # Possible that an earlier version did not have this field so still want to check against cached downloadable version + local_last_updated_time = datetime.min + + try: + repo_copy_updated_date = datetime.fromisoformat(repo_copy_updated_string) + except ValueError: + logger.error(f'last_updated field in incorrect format for repository copy of {downloadable_kwarg_key} {object_name}.') + return False + + return repo_copy_updated_date > local_last_updated_time + +def is_o3de_engine_update_available(engine_name: str, local_last_updated: str): + return is_o3de_object_update_available(engine_name, 'engine_name', local_last_updated) + +def is_o3de_project_update_available(project_name: str, local_last_updated: str): + return is_o3de_object_update_available(project_name, 'project_name', local_last_updated) + +def is_o3de_gem_update_available(gem_name: str, local_last_updated: str): + return is_o3de_object_update_available(gem_name, 'gem_name', local_last_updated) + +def is_o3de_template_update_available(template_name: str, local_last_updated: str): + return is_o3de_object_update_available(template_name, 'template_name', local_last_updated) + +def is_o3de_restricted_update_available(restricted_name: str, local_last_updated: str): + return is_o3de_object_update_available(restricted_name, 'restricted_name', local_last_updated) def _run_download(args: argparse) -> int: if args.override_home_folder: @@ -189,19 +277,23 @@ def _run_download(args: argparse) -> int: if args.engine_name: return download_engine(args.engine_name, args.dest_path, - args.skip_auto_register) + args.skip_auto_register, + args.force) elif args.project_name: return download_project(args.project_name, args.dest_path, - args.skip_auto_register) + args.skip_auto_register, + args.force) elif args.gem_name: return download_gem(args.gem_name, args.dest_path, - args.skip_auto_register) + args.skip_auto_register, + args.force) elif args.template_name: return download_template(args.template_name, args.dest_path, - args.skip_auto_register) + args.skip_auto_register, + args.force) return 1 @@ -230,6 +322,9 @@ def add_parser_args(parser): parser.add_argument('-sar', '--skip-auto-register', action='store_true', required=False, default=False, help = 'Skip the automatic registration of new object download') + parser.add_argument('-f', '--force', action='store_true', required=False, + default=False, + help = 'Force overwrite the current object') parser.add_argument('-ohf', '--override-home-folder', type=str, required=False, help='By default the home folder is the user folder, override it to this folder.') diff --git a/scripts/o3de/o3de/utils.py b/scripts/o3de/o3de/utils.py index 8663502a3f..56f9ae4bcd 100755 --- a/scripts/o3de/o3de/utils.py +++ b/scripts/o3de/o3de/utils.py @@ -117,15 +117,23 @@ def backup_folder(folder: str or pathlib.Path) -> None: if backup_folder_name.is_dir(): renamed = True -def download_file(parsed_uri, download_path: pathlib.Path, download_progress_callback = None) -> int: +def download_file(parsed_uri, download_path: pathlib.Path, force_overwrite, download_progress_callback = None) -> int: """ :param parsed_uri: uniform resource identifier to zip file to download :param download_path: location path on disk to download file :download_progress_callback: callback called with the download progress as a percentage, returns true to request to cancel the download """ if download_path.is_file(): - logger.warn(f'File already downloaded to {download_path}.') - elif parsed_uri.scheme in ['http', 'https', 'ftp', 'ftps']: + if not force_overwrite: + logger.warn(f'File already downloaded to {download_path}.') + else: + try: + shutil.rmtree(download_path) + except OSError: + logger.error(f'Could not remove existing download path {download_path}.') + return 1 + + if parsed_uri.scheme in ['http', 'https', 'ftp', 'ftps']: with urllib.request.urlopen(parsed_uri.geturl()) as s: download_file_size = 0 try: diff --git a/system_windows_pc.cfg b/system_windows_pc.cfg index aa53ed9323..e34c35e581 100644 --- a/system_windows_pc.cfg +++ b/system_windows_pc.cfg @@ -15,3 +15,4 @@ r_ShadersAllowCompilation = 1 -- Localization Settings sys_localization_format=0 +log_RemoteConsoleAllowedAddresses=127.0.0.1