From 779edb7fe5fd54ab67c1acb2439522a5c13eded4 Mon Sep 17 00:00:00 2001 From: kritin Date: Thu, 30 Sep 2021 16:43:15 -0700 Subject: [PATCH 01/65] Sample Editor test for QA Automation project Signed-off-by: kritin --- .../EditorScripts/Sample_Editor_Tests.py | 143 ++++++++++++++++++ .../editor/TestSuite_Main_Optimized.py | 3 + 2 files changed, 146 insertions(+) create mode 100644 AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Sample_Editor_Tests.py diff --git a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Sample_Editor_Tests.py b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Sample_Editor_Tests.py new file mode 100644 index 0000000000..746c0a936d --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Sample_Editor_Tests.py @@ -0,0 +1,143 @@ +""" +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 + +Test Case: Creating tests for basic editor feature like creating a level, adding new entities, modifying the entities etc. +""" + + +class Tests: + create_level = ("Level created and loaded successfully", "Failed to create the level") + open_level = ("Level loaded successfully", "Failed to load the level") + create_entity = ("Parent entity created successfully", "Failed to create a parent entity") + set_entity_name = ("Entity name set successfully", "Failed to set entity name") + delete_entity = ("Parent entity deleted successfully", "Failed to delete parent entity") + game_mode_enter = ("Game Mode entered successfully", "Failed to enter the game mode") + game_mode_exit = ("Game mode exited successfully", "Failed to exit game mode") + create_child_entity = ("Child entity created successfully", "Failed to create a child entity") + delete_child_entity = ("Child entity deleted successfully", "Failed to delete child entity") + add_mesh_component = ("Mesh component added successfully", "Failed to add mesh component") + found_component_typeId = ("Found component typeId", "Unable to find component TypeId") + remove_mesh_component = ("Mesh component removed successfully", "Failed to remove mesh component") + + +def sample_editor_tests(): + """ + Performing basic test in editor + 01. create_level if it does not exist else 02. open exiting level + + 03. create parent entity and set name + 04. create child entity + 05. delete child entity + 06. add mesh component + 07. remove mesh component + 08. enter game mode + 09. exit game_mode + 10. delete parent entity + 11. save level + + """ + import os + from editor_python_test_tools.utils import Report + from editor_python_test_tools.utils import TestHelper as helper + import editor_python_test_tools.hydra_editor_utils as hydra + + import azlmbr.math as math + import azlmbr.asset as asset + import azlmbr.bus as bus + import azlmbr.editor as editor + import azlmbr.entity as entity + import azlmbr.legacy.general as general + import azlmbr.object + + def search_entity(entity_to_search, entity_name): + """ + + :param entity_to_search: entity to be searched + :param entity_name: name of the entity used in the set command + :return: True if entity id exists in the entity_list + False if entity id does not exist in the entity_list + """ + entity_list = [] + entity_search_filter = entity.SearchFilter() + entity_search_filter.names = entity_name + entity_list = entity.SearchBus(bus.Broadcast, 'SearchEntities', entity_search_filter) + if entity_list: + if entity_to_search in entity_list: + return True + return False + return False + + # 01. create_level + + test_level = 'Simple' + general.open_level_no_prompt(test_level) + Report.result(Tests.create_level, general.get_current_level_name() == test_level) + + # 02. load existing level - skipping this since this could alter existing level that other test depends on + + # 03. create_entity and set name + # Delete any exiting entity and Create a new Entity at the root level + search_filter = azlmbr.entity.SearchFilter() + all_entities = entity.SearchBus(azlmbr.bus.Broadcast, "SearchEntities", search_filter) + editor.ToolsApplicationRequestBus(bus.Broadcast, "DeleteEntities", all_entities) + parent_entity = editor.ToolsApplicationRequestBus(bus.Broadcast, "CreateNewEntity", entity.EntityId()) + Report.result(Tests.create_entity, parent_entity.IsValid()) + + # Setting a new name + parent_entity_name = "Parent_1" + editor.EditorEntityAPIBus(bus.Event, 'SetName', parent_entity, parent_entity_name) + Report.result(Tests.set_entity_name, + editor.EditorEntityInfoRequestBus(bus.Event, 'GetName', parent_entity) == parent_entity_name) + + # 04. Creating child Entity and setting name to above created parent entity + child_1_entity = editor.ToolsApplicationRequestBus(bus.Broadcast, 'CreateNewEntity', parent_entity) + Report.result(Tests.create_child_entity, child_1_entity.IsValid()) + child_entity_name = "Child_1" + editor.EditorEntityAPIBus(bus.Event, 'SetName', child_1_entity, child_entity_name) + Report.result(Tests.set_entity_name, + editor.EditorEntityInfoRequestBus(bus.Event, 'GetName', child_1_entity) == child_entity_name) + + # 05. delete_Child_entity + editor.ToolsApplicationRequestBus(bus.Broadcast, 'DeleteEntityById', child_1_entity) + Report.result(Tests.delete_entity, search_entity(child_1_entity, "Child_1") == False) + + # 06. add mesh component to parent entity + type_id_list = editor.EditorComponentAPIBus(bus.Broadcast, 'FindComponentTypeIdsByEntityType', ["Mesh"], + entity.EntityType().Game) + if type_id_list is not None: + component_outcome = editor.EditorComponentAPIBus(bus.Broadcast, 'AddComponentsOfType', parent_entity, + type_id_list) + Report.result(Tests.add_mesh_component, component_outcome.IsSuccess()) + else: + Report.result(Tests.found_component_typeId, type_id_list is not None) + + # 09. remove mesh component + outcome_get_component = editor.EditorComponentAPIBus(bus.Broadcast, 'GetComponentOfType', parent_entity, + type_id_list[0]) + if outcome_get_component.IsSuccess(): + component_entity_pair = outcome_get_component.GetValue() + editor.EditorComponentAPIBus(bus.Broadcast, 'RemoveComponents', [component_entity_pair]) + component_exists = editor.EditorComponentAPIBus(bus.Broadcast, 'HasComponentOfType', parent_entity, + type_id_list[0]) + mesh_test = True + if component_exists: + mesh_test = False + Report.result(Tests.remove_mesh_component, mesh_test) + else: + Report.result(Tests.found_component_typeId, outcome_get_component.IsSuccess()) + + # 10. delete parent entity + editor.ToolsApplicationRequestBus(azlmbr.bus.Broadcast, 'DeleteEntityById', parent_entity) + Report.result(Tests.delete_entity, search_entity(parent_entity, "Parent_1") == False) + + # Close editor without saving + editor.EditorToolsApplicationRequestBus(bus.Broadcast, 'ExitNoPrompt') + + +if __name__ == "__main__": + from editor_python_test_tools.utils import Report + + Report.start_test(sample_editor_tests) diff --git a/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Main_Optimized.py b/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Main_Optimized.py index 9c0b99daff..9aef8a8b2c 100644 --- a/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Main_Optimized.py +++ b/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Main_Optimized.py @@ -73,3 +73,6 @@ class TestAutomationAutoTestMode(EditorTestSuite): @pytest.mark.skip(reason="Times out due to dialogs failing to dismiss: LYN-4208") class test_Menus_FileMenuOptions_Work(EditorSharedTest): from .EditorScripts import Menus_FileMenuOptions as test_module + + class test_Sample_Editor_Tests(EditorSharedTest): + from .EditorScripts import Sample_Editor_Tests as test_module \ No newline at end of file From b7e3a63faea416a22ba8f3a7d13dc22728bfd64a Mon Sep 17 00:00:00 2001 From: kritin Date: Mon, 4 Oct 2021 23:18:31 -0700 Subject: [PATCH 02/65] responded to code reviews Signed-off-by: kritin --- ...flows_ExistingLevel_EntityComponentCRUD.py | 159 ++++++++++++++++++ .../editor/TestSuite_Main_Optimized.py | 3 +- 2 files changed, 161 insertions(+), 1 deletion(-) create mode 100644 AutomatedTesting/Gem/PythonTests/editor/EditorScripts/BasicEditorWorkflows_ExistingLevel_EntityComponentCRUD.py diff --git a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/BasicEditorWorkflows_ExistingLevel_EntityComponentCRUD.py b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/BasicEditorWorkflows_ExistingLevel_EntityComponentCRUD.py new file mode 100644 index 0000000000..451b3a6714 --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/BasicEditorWorkflows_ExistingLevel_EntityComponentCRUD.py @@ -0,0 +1,159 @@ +""" +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: + load_level = ( + "Level loaded successfully", + "Failed to load the level" + ) + create_entity = ( + "Parent entity created successfully", + "Failed to create a parent entity" + ) + set_entity_name = ( + "Entity name set successfully", + "Failed to set entity name" + ) + delete_entity = ( + "Parent Entity deleted successfully", + "Failed to delete parent entity" + ) + create_child_entity = ( + "Child entity created successfully", + "Failed to create a child entity" + ) + delete_child_entity = ( + "Child entity deleted successfully", + "Failed to delete child entity" + ) + add_mesh_component = ( + "Mesh component added successfully", + "Failed to add mesh component" + ) + found_component_typeId = ( + "Found component typeId", + "Unable to find component TypeId" + ) + remove_mesh_component = ( + "Mesh component removed successfully", + "Failed to remove mesh component" + ) + + +def BasicEditorWorkflows_ExistingLevel_EntityComponentCRUD(): + """ + Performing basic test in editor + 01. Load exiting level + 02. create parent entity and set name + 03. create child entity and set a name + 04. delete child entity + 05. add mesh component to parent entity + 06. remove mesh component + 07. delete parent entity + Close editor without saving + """ + import os + from editor_python_test_tools.utils import Report + from editor_python_test_tools.utils import TestHelper as helper + import editor_python_test_tools.hydra_editor_utils as hydra + + import azlmbr.math as math + import azlmbr.asset as asset + import azlmbr.bus as bus + import azlmbr.editor as editor + import azlmbr.entity as entity + import azlmbr.legacy.general as general + import azlmbr.object + + def search_entity(entity_to_search, entity_name): + """ + + :param entity_to_search: entity to be searched + :param entity_name: name of the entity used in the set command + :return: True if entity id exists in the entity_list + False if entity id does not exist in the entity_list + """ + entity_list = [] + entity_search_filter = entity.SearchFilter() + entity_search_filter.names = entity_name + entity_list = entity.SearchBus(bus.Broadcast, 'SearchEntities', entity_search_filter) + if entity_list: + if entity_to_search in entity_list: + return True + return False + return False + + # 01. load an existing level + + test_level = 'Simple' + general.open_level_no_prompt(test_level) + Report.result(Tests.load_level, general.get_current_level_name() == test_level) + + # 02. create parent entity and set name + # Delete any exiting entity and Create a new Entity at the root level + search_filter = azlmbr.entity.SearchFilter() + all_entities = entity.SearchBus(azlmbr.bus.Broadcast, "SearchEntities", search_filter) + editor.ToolsApplicationRequestBus(bus.Broadcast, "DeleteEntities", all_entities) + parent_entity = editor.ToolsApplicationRequestBus(bus.Broadcast, "CreateNewEntity", entity.EntityId()) + Report.result(Tests.create_entity, parent_entity.IsValid()) + + # Setting a new name + parent_entity_name = "Parent_1" + editor.EditorEntityAPIBus(bus.Event, 'SetName', parent_entity, parent_entity_name) + Report.result(Tests.set_entity_name, + editor.EditorEntityInfoRequestBus(bus.Event, 'GetName', parent_entity) == parent_entity_name) + + # 03. Create child Entity to above created parent entity and set a name + child_1_entity = editor.ToolsApplicationRequestBus(bus.Broadcast, 'CreateNewEntity', parent_entity) + Report.result(Tests.create_child_entity, child_1_entity.IsValid()) + child_entity_name = "Child_1" + editor.EditorEntityAPIBus(bus.Event, 'SetName', child_1_entity, child_entity_name) + Report.result(Tests.set_entity_name, + editor.EditorEntityInfoRequestBus(bus.Event, 'GetName', child_1_entity) == child_entity_name) + + # 04. delete_Child_entity + editor.ToolsApplicationRequestBus(bus.Broadcast, 'DeleteEntityById', child_1_entity) + Report.result(Tests.delete_child_entity, search_entity(child_1_entity, "Child_1") == False) + + # 05. add mesh component to parent entity + type_id_list = editor.EditorComponentAPIBus(bus.Broadcast, 'FindComponentTypeIdsByEntityType', ["Mesh"], + entity.EntityType().Game) + if type_id_list is not None: + component_outcome = editor.EditorComponentAPIBus(bus.Broadcast, 'AddComponentsOfType', parent_entity, + type_id_list) + Report.result(Tests.add_mesh_component, component_outcome.IsSuccess()) + else: + Report.result(Tests.found_component_typeId, type_id_list is not None) + + # 06. remove mesh component + outcome_get_component = editor.EditorComponentAPIBus(bus.Broadcast, 'GetComponentOfType', parent_entity, + type_id_list[0]) + if outcome_get_component.IsSuccess(): + component_entity_pair = outcome_get_component.GetValue() + editor.EditorComponentAPIBus(bus.Broadcast, 'RemoveComponents', [component_entity_pair]) + component_exists = editor.EditorComponentAPIBus(bus.Broadcast, 'HasComponentOfType', parent_entity, + type_id_list[0]) + mesh_test = True + if component_exists: + mesh_test = False + Report.result(Tests.remove_mesh_component, mesh_test) + else: + Report.result(Tests.found_component_typeId, outcome_get_component.IsSuccess()) + + # 7. delete parent entity + editor.ToolsApplicationRequestBus(azlmbr.bus.Broadcast, 'DeleteEntityById', parent_entity) + Report.result(Tests.delete_entity, search_entity(parent_entity, "Parent_1") == False) + + # Close editor without saving + editor.EditorToolsApplicationRequestBus(bus.Broadcast, 'ExitNoPrompt') + + +if __name__ == "__main__": + from editor_python_test_tools.utils import Report + + Report.start_test(BasicEditorWorkflows_ExistingLevel_EntityComponentCRUD) diff --git a/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Main_Optimized.py b/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Main_Optimized.py index 9aef8a8b2c..755f75216b 100644 --- a/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Main_Optimized.py +++ b/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Main_Optimized.py @@ -73,6 +73,7 @@ class TestAutomationAutoTestMode(EditorTestSuite): @pytest.mark.skip(reason="Times out due to dialogs failing to dismiss: LYN-4208") class test_Menus_FileMenuOptions_Work(EditorSharedTest): from .EditorScripts import Menus_FileMenuOptions as test_module + class test_Sample_Editor_Tests(EditorSharedTest): - from .EditorScripts import Sample_Editor_Tests as test_module \ No newline at end of file + from .EditorScripts import BasicEditorWorkflows_ExistingLevel_EntityComponentCRUD as test_module From f1fe2912e45ba6e493ba0fee3e6edd4876ccfc20 Mon Sep 17 00:00:00 2001 From: kritin Date: Tue, 5 Oct 2021 14:54:07 -0700 Subject: [PATCH 03/65] responded to code reviews Signed-off-by: kritin --- ...flows_ExistingLevel_EntityComponentCRUD.py | 80 +++------- .../EditorScripts/Sample_Editor_Tests.py | 143 ------------------ .../editor/TestSuite_Main_Optimized.py | 2 +- 3 files changed, 20 insertions(+), 205 deletions(-) delete mode 100644 AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Sample_Editor_Tests.py diff --git a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/BasicEditorWorkflows_ExistingLevel_EntityComponentCRUD.py b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/BasicEditorWorkflows_ExistingLevel_EntityComponentCRUD.py index 451b3a6714..803b9e9a11 100644 --- a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/BasicEditorWorkflows_ExistingLevel_EntityComponentCRUD.py +++ b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/BasicEditorWorkflows_ExistingLevel_EntityComponentCRUD.py @@ -53,40 +53,19 @@ def BasicEditorWorkflows_ExistingLevel_EntityComponentCRUD(): 03. create child entity and set a name 04. delete child entity 05. add mesh component to parent entity - 06. remove mesh component 07. delete parent entity Close editor without saving """ import os from editor_python_test_tools.utils import Report - from editor_python_test_tools.utils import TestHelper as helper - import editor_python_test_tools.hydra_editor_utils as hydra + from editor_python_test_tools.editor_entity_utils import EditorEntity - import azlmbr.math as math - import azlmbr.asset as asset import azlmbr.bus as bus import azlmbr.editor as editor import azlmbr.entity as entity import azlmbr.legacy.general as general import azlmbr.object - def search_entity(entity_to_search, entity_name): - """ - - :param entity_to_search: entity to be searched - :param entity_name: name of the entity used in the set command - :return: True if entity id exists in the entity_list - False if entity id does not exist in the entity_list - """ - entity_list = [] - entity_search_filter = entity.SearchFilter() - entity_search_filter.names = entity_name - entity_list = entity.SearchBus(bus.Broadcast, 'SearchEntities', entity_search_filter) - if entity_list: - if entity_to_search in entity_list: - return True - return False - return False # 01. load an existing level @@ -94,60 +73,39 @@ def BasicEditorWorkflows_ExistingLevel_EntityComponentCRUD(): general.open_level_no_prompt(test_level) Report.result(Tests.load_level, general.get_current_level_name() == test_level) + # 02. create parent entity and set name # Delete any exiting entity and Create a new Entity at the root level + search_filter = azlmbr.entity.SearchFilter() all_entities = entity.SearchBus(azlmbr.bus.Broadcast, "SearchEntities", search_filter) editor.ToolsApplicationRequestBus(bus.Broadcast, "DeleteEntities", all_entities) - parent_entity = editor.ToolsApplicationRequestBus(bus.Broadcast, "CreateNewEntity", entity.EntityId()) - Report.result(Tests.create_entity, parent_entity.IsValid()) + parent_entity = EditorEntity.create_editor_entity("Parent_1") + Report.result(Tests.create_entity, parent_entity.exists()) - # Setting a new name - parent_entity_name = "Parent_1" - editor.EditorEntityAPIBus(bus.Event, 'SetName', parent_entity, parent_entity_name) - Report.result(Tests.set_entity_name, - editor.EditorEntityInfoRequestBus(bus.Event, 'GetName', parent_entity) == parent_entity_name) # 03. Create child Entity to above created parent entity and set a name - child_1_entity = editor.ToolsApplicationRequestBus(bus.Broadcast, 'CreateNewEntity', parent_entity) - Report.result(Tests.create_child_entity, child_1_entity.IsValid()) - child_entity_name = "Child_1" - editor.EditorEntityAPIBus(bus.Event, 'SetName', child_1_entity, child_entity_name) - Report.result(Tests.set_entity_name, - editor.EditorEntityInfoRequestBus(bus.Event, 'GetName', child_1_entity) == child_entity_name) + + child_1_entity = EditorEntity.create_editor_entity("Child_1", parent_entity.id ) + Report.result(Tests.create_child_entity, child_1_entity.exists()) + # 04. delete_Child_entity - editor.ToolsApplicationRequestBus(bus.Broadcast, 'DeleteEntityById', child_1_entity) - Report.result(Tests.delete_child_entity, search_entity(child_1_entity, "Child_1") == False) + + child_1_entity.delete() + Report.result(Tests.delete_child_entity, not child_1_entity.exists()) + # 05. add mesh component to parent entity - type_id_list = editor.EditorComponentAPIBus(bus.Broadcast, 'FindComponentTypeIdsByEntityType', ["Mesh"], - entity.EntityType().Game) - if type_id_list is not None: - component_outcome = editor.EditorComponentAPIBus(bus.Broadcast, 'AddComponentsOfType', parent_entity, - type_id_list) - Report.result(Tests.add_mesh_component, component_outcome.IsSuccess()) - else: - Report.result(Tests.found_component_typeId, type_id_list is not None) - # 06. remove mesh component - outcome_get_component = editor.EditorComponentAPIBus(bus.Broadcast, 'GetComponentOfType', parent_entity, - type_id_list[0]) - if outcome_get_component.IsSuccess(): - component_entity_pair = outcome_get_component.GetValue() - editor.EditorComponentAPIBus(bus.Broadcast, 'RemoveComponents', [component_entity_pair]) - component_exists = editor.EditorComponentAPIBus(bus.Broadcast, 'HasComponentOfType', parent_entity, - type_id_list[0]) - mesh_test = True - if component_exists: - mesh_test = False - Report.result(Tests.remove_mesh_component, mesh_test) - else: - Report.result(Tests.found_component_typeId, outcome_get_component.IsSuccess()) + parent_entity.add_component("Mesh") + Report.result(Tests.add_mesh_component, parent_entity.has_component("Mesh")) + # 7. delete parent entity - editor.ToolsApplicationRequestBus(azlmbr.bus.Broadcast, 'DeleteEntityById', parent_entity) - Report.result(Tests.delete_entity, search_entity(parent_entity, "Parent_1") == False) + + parent_entity.delete() + Report.result(Tests.delete_entity, not parent_entity.exists()) # Close editor without saving editor.EditorToolsApplicationRequestBus(bus.Broadcast, 'ExitNoPrompt') diff --git a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Sample_Editor_Tests.py b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Sample_Editor_Tests.py deleted file mode 100644 index 746c0a936d..0000000000 --- a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Sample_Editor_Tests.py +++ /dev/null @@ -1,143 +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 - -Test Case: Creating tests for basic editor feature like creating a level, adding new entities, modifying the entities etc. -""" - - -class Tests: - create_level = ("Level created and loaded successfully", "Failed to create the level") - open_level = ("Level loaded successfully", "Failed to load the level") - create_entity = ("Parent entity created successfully", "Failed to create a parent entity") - set_entity_name = ("Entity name set successfully", "Failed to set entity name") - delete_entity = ("Parent entity deleted successfully", "Failed to delete parent entity") - game_mode_enter = ("Game Mode entered successfully", "Failed to enter the game mode") - game_mode_exit = ("Game mode exited successfully", "Failed to exit game mode") - create_child_entity = ("Child entity created successfully", "Failed to create a child entity") - delete_child_entity = ("Child entity deleted successfully", "Failed to delete child entity") - add_mesh_component = ("Mesh component added successfully", "Failed to add mesh component") - found_component_typeId = ("Found component typeId", "Unable to find component TypeId") - remove_mesh_component = ("Mesh component removed successfully", "Failed to remove mesh component") - - -def sample_editor_tests(): - """ - Performing basic test in editor - 01. create_level if it does not exist else 02. open exiting level - - 03. create parent entity and set name - 04. create child entity - 05. delete child entity - 06. add mesh component - 07. remove mesh component - 08. enter game mode - 09. exit game_mode - 10. delete parent entity - 11. save level - - """ - import os - from editor_python_test_tools.utils import Report - from editor_python_test_tools.utils import TestHelper as helper - import editor_python_test_tools.hydra_editor_utils as hydra - - import azlmbr.math as math - import azlmbr.asset as asset - import azlmbr.bus as bus - import azlmbr.editor as editor - import azlmbr.entity as entity - import azlmbr.legacy.general as general - import azlmbr.object - - def search_entity(entity_to_search, entity_name): - """ - - :param entity_to_search: entity to be searched - :param entity_name: name of the entity used in the set command - :return: True if entity id exists in the entity_list - False if entity id does not exist in the entity_list - """ - entity_list = [] - entity_search_filter = entity.SearchFilter() - entity_search_filter.names = entity_name - entity_list = entity.SearchBus(bus.Broadcast, 'SearchEntities', entity_search_filter) - if entity_list: - if entity_to_search in entity_list: - return True - return False - return False - - # 01. create_level - - test_level = 'Simple' - general.open_level_no_prompt(test_level) - Report.result(Tests.create_level, general.get_current_level_name() == test_level) - - # 02. load existing level - skipping this since this could alter existing level that other test depends on - - # 03. create_entity and set name - # Delete any exiting entity and Create a new Entity at the root level - search_filter = azlmbr.entity.SearchFilter() - all_entities = entity.SearchBus(azlmbr.bus.Broadcast, "SearchEntities", search_filter) - editor.ToolsApplicationRequestBus(bus.Broadcast, "DeleteEntities", all_entities) - parent_entity = editor.ToolsApplicationRequestBus(bus.Broadcast, "CreateNewEntity", entity.EntityId()) - Report.result(Tests.create_entity, parent_entity.IsValid()) - - # Setting a new name - parent_entity_name = "Parent_1" - editor.EditorEntityAPIBus(bus.Event, 'SetName', parent_entity, parent_entity_name) - Report.result(Tests.set_entity_name, - editor.EditorEntityInfoRequestBus(bus.Event, 'GetName', parent_entity) == parent_entity_name) - - # 04. Creating child Entity and setting name to above created parent entity - child_1_entity = editor.ToolsApplicationRequestBus(bus.Broadcast, 'CreateNewEntity', parent_entity) - Report.result(Tests.create_child_entity, child_1_entity.IsValid()) - child_entity_name = "Child_1" - editor.EditorEntityAPIBus(bus.Event, 'SetName', child_1_entity, child_entity_name) - Report.result(Tests.set_entity_name, - editor.EditorEntityInfoRequestBus(bus.Event, 'GetName', child_1_entity) == child_entity_name) - - # 05. delete_Child_entity - editor.ToolsApplicationRequestBus(bus.Broadcast, 'DeleteEntityById', child_1_entity) - Report.result(Tests.delete_entity, search_entity(child_1_entity, "Child_1") == False) - - # 06. add mesh component to parent entity - type_id_list = editor.EditorComponentAPIBus(bus.Broadcast, 'FindComponentTypeIdsByEntityType', ["Mesh"], - entity.EntityType().Game) - if type_id_list is not None: - component_outcome = editor.EditorComponentAPIBus(bus.Broadcast, 'AddComponentsOfType', parent_entity, - type_id_list) - Report.result(Tests.add_mesh_component, component_outcome.IsSuccess()) - else: - Report.result(Tests.found_component_typeId, type_id_list is not None) - - # 09. remove mesh component - outcome_get_component = editor.EditorComponentAPIBus(bus.Broadcast, 'GetComponentOfType', parent_entity, - type_id_list[0]) - if outcome_get_component.IsSuccess(): - component_entity_pair = outcome_get_component.GetValue() - editor.EditorComponentAPIBus(bus.Broadcast, 'RemoveComponents', [component_entity_pair]) - component_exists = editor.EditorComponentAPIBus(bus.Broadcast, 'HasComponentOfType', parent_entity, - type_id_list[0]) - mesh_test = True - if component_exists: - mesh_test = False - Report.result(Tests.remove_mesh_component, mesh_test) - else: - Report.result(Tests.found_component_typeId, outcome_get_component.IsSuccess()) - - # 10. delete parent entity - editor.ToolsApplicationRequestBus(azlmbr.bus.Broadcast, 'DeleteEntityById', parent_entity) - Report.result(Tests.delete_entity, search_entity(parent_entity, "Parent_1") == False) - - # Close editor without saving - editor.EditorToolsApplicationRequestBus(bus.Broadcast, 'ExitNoPrompt') - - -if __name__ == "__main__": - from editor_python_test_tools.utils import Report - - Report.start_test(sample_editor_tests) diff --git a/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Main_Optimized.py b/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Main_Optimized.py index 755f75216b..afc52f962d 100644 --- a/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Main_Optimized.py +++ b/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Main_Optimized.py @@ -75,5 +75,5 @@ class TestAutomationAutoTestMode(EditorTestSuite): from .EditorScripts import Menus_FileMenuOptions as test_module - class test_Sample_Editor_Tests(EditorSharedTest): + class test_BasicEditorWorkflows_ExistingLevel_EntityComponentCRUD(EditorSharedTest): from .EditorScripts import BasicEditorWorkflows_ExistingLevel_EntityComponentCRUD as test_module From f009e06cab8f1af7e0c74a24421b97b38955e517 Mon Sep 17 00:00:00 2001 From: kritin Date: Wed, 6 Oct 2021 13:20:48 -0700 Subject: [PATCH 04/65] responding to code reviews Signed-off-by: kritin --- ...flows_ExistingLevel_EntityComponentCRUD.py | 22 +++---------------- 1 file changed, 3 insertions(+), 19 deletions(-) diff --git a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/BasicEditorWorkflows_ExistingLevel_EntityComponentCRUD.py b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/BasicEditorWorkflows_ExistingLevel_EntityComponentCRUD.py index 803b9e9a11..39cacf9af5 100644 --- a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/BasicEditorWorkflows_ExistingLevel_EntityComponentCRUD.py +++ b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/BasicEditorWorkflows_ExistingLevel_EntityComponentCRUD.py @@ -53,10 +53,9 @@ def BasicEditorWorkflows_ExistingLevel_EntityComponentCRUD(): 03. create child entity and set a name 04. delete child entity 05. add mesh component to parent entity - 07. delete parent entity - Close editor without saving + 06. delete parent entity """ - import os + from editor_python_test_tools.utils import Report from editor_python_test_tools.editor_entity_utils import EditorEntity @@ -66,50 +65,35 @@ def BasicEditorWorkflows_ExistingLevel_EntityComponentCRUD(): import azlmbr.legacy.general as general import azlmbr.object - # 01. load an existing level - test_level = 'Simple' general.open_level_no_prompt(test_level) Report.result(Tests.load_level, general.get_current_level_name() == test_level) - # 02. create parent entity and set name # Delete any exiting entity and Create a new Entity at the root level - search_filter = azlmbr.entity.SearchFilter() all_entities = entity.SearchBus(azlmbr.bus.Broadcast, "SearchEntities", search_filter) editor.ToolsApplicationRequestBus(bus.Broadcast, "DeleteEntities", all_entities) parent_entity = EditorEntity.create_editor_entity("Parent_1") Report.result(Tests.create_entity, parent_entity.exists()) - # 03. Create child Entity to above created parent entity and set a name - child_1_entity = EditorEntity.create_editor_entity("Child_1", parent_entity.id ) Report.result(Tests.create_child_entity, child_1_entity.exists()) - # 04. delete_Child_entity - child_1_entity.delete() Report.result(Tests.delete_child_entity, not child_1_entity.exists()) - # 05. add mesh component to parent entity - parent_entity.add_component("Mesh") Report.result(Tests.add_mesh_component, parent_entity.has_component("Mesh")) - - # 7. delete parent entity - + # 06. delete parent entity parent_entity.delete() Report.result(Tests.delete_entity, not parent_entity.exists()) - # Close editor without saving - editor.EditorToolsApplicationRequestBus(bus.Broadcast, 'ExitNoPrompt') - if __name__ == "__main__": from editor_python_test_tools.utils import Report From f43b3b9fbefa56b0500da096b900809c0dedeb23 Mon Sep 17 00:00:00 2001 From: Mikhail Naumov Date: Thu, 7 Oct 2021 20:35:23 -0500 Subject: [PATCH 05/65] Fixing crash creating new level when simulate mode is on Signed-off-by: Mikhail Naumov --- Code/Editor/CryEdit.cpp | 10 ++++++++++ .../AzFramework/Spawnable/RootSpawnableInterface.h | 4 ++++ .../AzFramework/Spawnable/SpawnableSystemComponent.cpp | 9 +++++++-- .../AzFramework/Spawnable/SpawnableSystemComponent.h | 1 + 4 files changed, 22 insertions(+), 2 deletions(-) diff --git a/Code/Editor/CryEdit.cpp b/Code/Editor/CryEdit.cpp index 4aa3d22114..d2a448bd34 100644 --- a/Code/Editor/CryEdit.cpp +++ b/Code/Editor/CryEdit.cpp @@ -57,6 +57,7 @@ AZ_POP_DISABLE_WARNING #include #include #include +#include // AzToolsFramework #include @@ -3019,6 +3020,15 @@ CCryEditApp::ECreateLevelResult CCryEditApp::CreateLevel(const QString& levelNam bool bIsDocModified = GetIEditor()->GetDocument()->IsModified(); OnSwitchPhysics(); GetIEditor()->GetDocument()->SetModifiedFlag(bIsDocModified); + + if (usePrefabSystemForLevels) + { + auto* rootSpawnableInterface = AzFramework::RootSpawnableInterface::Get(); + if (rootSpawnableInterface) + { + rootSpawnableInterface->ProcessSpawnableQueue(); + } + } } const QScopedValueRollback rollback(m_creatingNewLevel); diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/RootSpawnableInterface.h b/Code/Framework/AzFramework/AzFramework/Spawnable/RootSpawnableInterface.h index bb1f137ba2..72a3031e3e 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/RootSpawnableInterface.h +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/RootSpawnableInterface.h @@ -61,6 +61,10 @@ namespace AzFramework //! be deleted and the spawnable asset to be released. This call is automatically done when //! AssignRootSpawnable is called while a root spawnable is assigned. virtual void ReleaseRootSpawnable() = 0; + //! Force processing all SpawnableEntitiesManager requests immediately + //! This is useful when loading a different level while SpawnableEntitiesManager still has + //! pending requests + virtual void ProcessSpawnableQueue() = 0; }; using RootSpawnableInterface = AZ::Interface; diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableSystemComponent.cpp b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableSystemComponent.cpp index f6130c9e31..af41fdd6ba 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableSystemComponent.cpp +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableSystemComponent.cpp @@ -45,8 +45,7 @@ namespace AzFramework void SpawnableSystemComponent::OnTick(float /*deltaTime*/, AZ::ScriptTimePoint /*time*/) { - m_entitiesManager.ProcessQueue( - SpawnableEntitiesManager::CommandQueuePriority::High | SpawnableEntitiesManager::CommandQueuePriority::Regular); + ProcessSpawnableQueue(); RootSpawnableNotificationBus::ExecuteQueuedEvents(); } @@ -121,6 +120,12 @@ namespace AzFramework m_rootSpawnableId = AZ::Data::AssetId(); } + void SpawnableSystemComponent::ProcessSpawnableQueue() + { + m_entitiesManager.ProcessQueue( + SpawnableEntitiesManager::CommandQueuePriority::High | SpawnableEntitiesManager::CommandQueuePriority::Regular); + } + void SpawnableSystemComponent::OnRootSpawnableAssigned([[maybe_unused]] AZ::Data::Asset rootSpawnable, [[maybe_unused]] uint32_t generation) { diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableSystemComponent.h b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableSystemComponent.h index 5b5fb1b7ee..74e255d624 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableSystemComponent.h +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableSystemComponent.h @@ -75,6 +75,7 @@ namespace AzFramework uint64_t AssignRootSpawnable(AZ::Data::Asset rootSpawnable) override; void ReleaseRootSpawnable() override; + void ProcessSpawnableQueue() override; // // RootSpawnbleNotificationBus From 2034cd3053d6d8af08135ded0cdb38c06381c15f Mon Sep 17 00:00:00 2001 From: sweeneys Date: Fri, 8 Oct 2021 15:36:52 -0700 Subject: [PATCH 06/65] Platform manager and sanity tests for Linux Signed-off-by: sweeneys --- Tools/LyTestTools/ly_test_tools/__init__.py | 6 +- .../managers/abstract_resource_locator.py | 2 +- .../_internal/managers/platforms/linux.py | 3 - .../environment/process_utils.py | 14 ++ .../launchers/platforms/linux/launcher.py | 28 ++- .../ly_test_tools/o3de/asset_processor.py | 170 +++++++++--------- .../o3de/asset_processor_utils.py | 19 +- Tools/LyTestTools/tests/integ/sanity_tests.py | 3 +- .../tests/unit/test_asset_processor.py | 14 +- 9 files changed, 149 insertions(+), 110 deletions(-) diff --git a/Tools/LyTestTools/ly_test_tools/__init__.py b/Tools/LyTestTools/ly_test_tools/__init__.py index 58340342ab..972e97d488 100755 --- a/Tools/LyTestTools/ly_test_tools/__init__.py +++ b/Tools/LyTestTools/ly_test_tools/__init__.py @@ -16,7 +16,7 @@ ALL_PLATFORM_OPTIONS = ['android', 'ios', 'linux', 'mac', 'windows'] ALL_LAUNCHER_OPTIONS = ['android', 'base', 'linux', 'mac', 'windows', 'windows_editor', 'windows_dedicated', 'windows_generic'] ANDROID = False IOS = False # Not implemented - see SPEC-2505 -LINUX = sys.platform.startswith('linux') # Not implemented - see SPEC-2501 +LINUX = sys.platform.startswith('linux') MAC = sys.platform.startswith('darwin') WINDOWS = sys.platform.startswith('win') @@ -54,9 +54,11 @@ elif LINUX: HOST_OS_PLATFORM = 'linux' HOST_OS_EDITOR = 'linux_editor' HOST_OS_DEDICATED_SERVER = 'linux_dedicated' - from ly_test_tools.launchers.platforms.linux.launcher import (LinuxLauncher, LinuxEditor, DedicatedLinuxLauncher) + HOST_OS_GENERIC_EXECUTABLE = 'linux_generic' + from ly_test_tools.launchers.platforms.linux.launcher import (LinuxLauncher, LinuxEditor, DedicatedLinuxLauncher, LinuxGenericLauncher) LAUNCHERS['linux'] = LinuxLauncher LAUNCHERS['linux_editor'] = LinuxEditor LAUNCHERS['linux_dedicated'] = DedicatedLinuxLauncher + LAUNCHERS['linux_generic'] = LinuxGenericLauncher else: logger.warning(f'WARNING: LyTestTools only supports Windows, Mac, and Linux. Unexpectedly detected HOST_OS_PLATFORM: "{HOST_OS_PLATFORM}".') diff --git a/Tools/LyTestTools/ly_test_tools/_internal/managers/abstract_resource_locator.py b/Tools/LyTestTools/ly_test_tools/_internal/managers/abstract_resource_locator.py index 77d58e3a48..fccc94bdcc 100755 --- a/Tools/LyTestTools/ly_test_tools/_internal/managers/abstract_resource_locator.py +++ b/Tools/LyTestTools/ly_test_tools/_internal/managers/abstract_resource_locator.py @@ -265,7 +265,7 @@ class AbstractResourceLocator(object): Return path to AssetProcessor's log file using the project bin dir :return: path to 'AP_Gui.log' file in folder """ - return os.path.join(self.ap_log_dir(), 'AP_Gui.log') + return os.path.join(self.ap_log_dir(), 'AP_GUI.log') def project_cache(self): """ diff --git a/Tools/LyTestTools/ly_test_tools/_internal/managers/platforms/linux.py b/Tools/LyTestTools/ly_test_tools/_internal/managers/platforms/linux.py index cdbd379183..fea60cc9cb 100644 --- a/Tools/LyTestTools/ly_test_tools/_internal/managers/platforms/linux.py +++ b/Tools/LyTestTools/ly_test_tools/_internal/managers/platforms/linux.py @@ -23,9 +23,6 @@ class _LinuxResourceManager(AbstractResourceLocator): """ Override for locating resources in a Linux operating system running LyTestTools. """ - def __init__(self, build_directory: str, project: str): - pass - def platform_config_file(self): """ :return: path to the platform config file diff --git a/Tools/LyTestTools/ly_test_tools/environment/process_utils.py b/Tools/LyTestTools/ly_test_tools/environment/process_utils.py index 1c0be299a8..79fc4f420a 100755 --- a/Tools/LyTestTools/ly_test_tools/environment/process_utils.py +++ b/Tools/LyTestTools/ly_test_tools/environment/process_utils.py @@ -465,3 +465,17 @@ def close_windows_process(pid, timeout=20, raise_on_missing=False): # Wait for asyncronous termination waiter.wait_for(lambda: pid not in psutil.pids(), timeout=timeout, exc=TimeoutError(f"Process {pid} never terminated")) + + +def get_display_env(): + """ + Fetches environment variables with an appropriate display (monitor) configured, + useful for subprocess calls to UI applications + :return: A dictionary containing environment variables (per os.environ) + """ + env = os.environ.copy() + if not ly_test_tools.WINDOWS: + if 'DISPLAY' not in env.keys(): + # assume Display 1 is available in another session + env['DISPLAY'] = ':1' + return env diff --git a/Tools/LyTestTools/ly_test_tools/launchers/platforms/linux/launcher.py b/Tools/LyTestTools/ly_test_tools/launchers/platforms/linux/launcher.py index 210519d197..afee522daf 100644 --- a/Tools/LyTestTools/ly_test_tools/launchers/platforms/linux/launcher.py +++ b/Tools/LyTestTools/ly_test_tools/launchers/platforms/linux/launcher.py @@ -13,6 +13,7 @@ import subprocess import ly_test_tools.environment.waiter import ly_test_tools.launchers.exceptions +import ly_test_tools.environment.process_utils as process_utils from ly_test_tools.launchers.platforms.base import Launcher from ly_test_tools.launchers.exceptions import TeardownError, ProcessNotStartedError @@ -68,7 +69,7 @@ class LinuxLauncher(Launcher): """ command = [self.binary_path()] + self.args self._tmpout = TemporaryFile() - self._proc = subprocess.Popen(command, stdout=self._tmpout, stderr=self._tmpout, universal_newlines=True) + self._proc = subprocess.Popen(command, stdout=self._tmpout, stderr=self._tmpout, universal_newlines=True, env=process_utils.get_display_env()) log.debug(f"Started Linux Launcher with command: {command}") def get_output(self, encoding="utf-8"): @@ -222,3 +223,28 @@ class LinuxEditor(LinuxLauncher): """ assert self.workspace.project is not None return os.path.join(self.workspace.paths.build_directory(), "Editor") + + +class LinuxGenericLauncher(LinuxLauncher): + + def __init__(self, build, exe_file_name, args=None): + super(LinuxLauncher, self).__init__(build, args) + self.exe_file_name = exe_file_name + self.expected_executable_path = os.path.join( + self.workspace.paths.build_directory(), f"{self.exe_file_name}") + + if not os.path.exists(self.expected_executable_path): + raise ProcessNotStartedError( + f"Unable to locate executable '{self.exe_file_name}' " + f"in path: '{self.expected_executable_path}'") + + def binary_path(self): + """ + Return full path to the executable file for this build's configuration and project + Relies on the build_directory() in self.workspace.paths to be accurate + + :return: full path to the given exe file + """ + assert self.workspace.project is not None, ( + 'Project cannot be NoneType - please specify a project name string.') + return self.expected_executable_path diff --git a/Tools/LyTestTools/ly_test_tools/o3de/asset_processor.py b/Tools/LyTestTools/ly_test_tools/o3de/asset_processor.py index 57454930eb..0623715350 100644 --- a/Tools/LyTestTools/ly_test_tools/o3de/asset_processor.py +++ b/Tools/LyTestTools/ly_test_tools/o3de/asset_processor.py @@ -7,21 +7,22 @@ SPDX-License-Identifier: Apache-2.0 OR MIT A class to control functionality of Lumberyard's asset processor. The class manages a workspace's asset processor and asset configurations. """ -import os -import datetime import logging -import subprocess -import socket -import time -import tempfile -import shutil -import stat -from typing import List, Tuple +import os import psutil +import shutil +import socket +import stat +import subprocess +import tempfile +import time + +from typing import List, Tuple import ly_test_tools -import ly_test_tools.environment.waiter as waiter import ly_test_tools.environment.file_system as file_system +import ly_test_tools.environment.process_utils as process_utils +import ly_test_tools.environment.waiter as waiter import ly_test_tools.o3de.pipeline_utils as utils from ly_test_tools.o3de.ap_log_parser import APLogParser @@ -177,24 +178,26 @@ class AssetProcessor(object): """ Read the a port chosen by AP from the log """ - start_time = time.time() - read_port_timeout = 10 - while (time.time() - start_time) < read_port_timeout: + port = None + + def _get_port_from_log(): + nonlocal port if not os.path.exists(self._workspace.paths.ap_gui_log()): - logger.debug(f"Log at {self._workspace.paths.ap_gui_log()} doesn't exist, sleeping") - else: - log = APLogParser(self._workspace.paths.ap_gui_log()) - if len(log.runs): - try: - port = log.runs[-1][port_type] - if port: - logger.info(f"Read port type {port_type} : {port}") - return port - except Exception: # intentionally broad - pass - time.sleep(1) - logger.warning(f"Failed to read port type {port_type}") - return 0 + return False + + log = APLogParser(self._workspace.paths.ap_gui_log()) + if len(log.runs): + try: + port = log.runs[-1][port_type] + logger.debug(f"Read port type {port_type} : {port}") + return True + except Exception as ex: # intentionally broad + logger.debug("Failed to read port from file", exc_info=ex) + return False + + err = AssetProcessorError(f"Failed to read port type {port_type} from {self._workspace.paths.ap_gui_log()}") + waiter.wait_for(_get_port_from_log, timeout=10, exc=err) + return port def set_control_connection(self, connection): self._control_connection = connection @@ -206,13 +209,8 @@ class AssetProcessor(object): """ if not self._control_connection: control_timeout = 60 - try: - return self.connect_socket("Control Connection", self.read_control_port, - set_port_method=self.set_control_connection, timeout=control_timeout) - except AssetProcessorError as e: - # We dont want a failure of our test socket connection to fail the entire test automatically. - logger.error(f"Failed to connect control socket with error {e}") - pass + return self.connect_socket("Control Connection", self.read_control_port, + set_port_method=self.set_control_connection, timeout=control_timeout) return True, None def using_temp_workspace(self): @@ -227,34 +225,40 @@ class AssetProcessor(object): :param set_port_method: If set, method to call with the established connection :param timeout: Max seconds to attempt connection for """ - - connection_timeout = timeout connect_port = read_port_method() - logger.debug(f"Waiting for connection to AP {port_name}: {host}:{connect_port}, " - f"{connection_timeout} seconds remaining") - start_time = time.time() - while (time.time() - start_time) < connection_timeout: + logger.debug(f"Attempting to for connect to AP {port_name}: {host}:{connect_port} for {timeout} seconds") + + def _attempt_connection(): + nonlocal connect_port + if self._ap_proc.poll() is not None: + raise AssetProcessorError(f"Asset processor exited early with errorcode: {self._ap_proc.returncode}") + connection_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - connection_socket.settimeout(10.0) + connection_socket.settimeout(timeout) try: connection_socket.connect((host, connect_port)) logger.debug(f"Connection to AP {port_name} was successful") if set_port_method is not None: set_port_method(connection_socket) - return True, None - except Exception: # Purposefully broad - # Short delay to prevent immediate failure due to slower starting applications such as debug builds - time.sleep(0.01) + return True + except Exception as ex: # Purposefully broad + logger.debug(f"Failed to connect to {host}:{connect_port}", exc_info=ex) if not connect_port or not self.using_temp_workspace(): # If we're not using a temp workspace with a fresh log it's possible we're reading a port from # a previous run and the log just hasn't written yet, we need to keep checking the log for a new # port to use - new_connect_port = read_port_method() - if new_connect_port != connect_port: - logger.debug( - f"Read new connect port for {port_name}: {host}:{new_connect_port}") - connect_port = new_connect_port - raise AssetProcessorError(f"Could not connect to AP {port_name}") + try: + new_connect_port = read_port_method() + if new_connect_port != connect_port: + logger.debug(f"Found new connect port for {port_name}: {host}:{new_connect_port}") + connect_port = new_connect_port + except Exception as read_exception: # Purposefully broad + logger.debug(f"Failed to read port data", exc_info=read_exception) + return False + + err = AssetProcessorError(f"Could not connect to AP {port_name} on {host}:{connect_port}") + waiter.wait_for(_attempt_connection, timeout=timeout, exc=err) + return True, None def stop(self, timeout=60): """ @@ -436,15 +440,14 @@ class AssetProcessor(object): extra_params=None, add_gem_scan_folders=None, add_config_scan_folders=None, decode=True, expect_failure=False, quitonidle=False, connect_to_ap=False, accept_input=True, run_until_idle=True, scan_folder_pattern=None): - ap_path = self._workspace.paths.asset_processor() + ap_path = os.path.abspath(self._workspace.paths.asset_processor()) + ap_exe_path = os.path.dirname(ap_path) extra_gui_params = [] if quitonidle: extra_gui_params.append("--quitonidle") if accept_input: extra_gui_params.append("--acceptInput") - ap_exe_path = os.path.dirname(self._workspace.paths.asset_processor()) - logger.info("Starting asset processor") if self.process_exists(): logger.error("Asset processor already started. Stop first") @@ -483,20 +486,30 @@ class AssetProcessor(object): logger.warning(f"Cannot capture output when leaving AP connection open.") logger.info(f"Launching AP with command: {command}") - self._ap_proc = subprocess.Popen(command, cwd=ap_exe_path) + try: + self._ap_proc = subprocess.Popen(command, cwd=ap_exe_path, env=process_utils.get_display_env()) - if accept_input and not quitonidle: - self.connect_control() + if accept_input: + self.connect_control() - if connect_to_ap: - self.connect_listen() + if connect_to_ap: + self.connect_listen() - if quitonidle: - waiter.wait_for(lambda: not self.process_exists(), timeout=timeout) - elif run_until_idle and accept_input: - if not self.wait_for_idle(): - return False, None - return True, None + if quitonidle: + waiter.wait_for(lambda: not self.process_exists(), timeout=timeout, + exc=AssetProcessorError(f"Failed to quit on idle within {timeout} seconds")) + elif run_until_idle and accept_input: + if not self.wait_for_idle(): + return False, None + return True, None + except BaseException as be: # purposefully broad + logger.exception("Exception while starting Asset Processor", be) + # clean up to avoid leaking open AP process to future tests + try: + self._ap_proc.kill() + except Exception as ex: + logger.exception("Ignoring exception while trying to terminate Asset Processor", ex) + raise # raise whatever prompted us to clean up def connect_listen(self, timeout=DEFAULT_TIMEOUT_SECONDS): # Wait for the AP we launched to be ready to accept a connection @@ -574,21 +587,17 @@ class AssetProcessor(object): expect_failure=False): """ In case of a timeout, the asset processor and associated processes are killed and the function returns False. - + :param timeout: seconds to wait before aborting :param capture_output = Capture output which will be returned in the second of the return pair :param decode: decode byte strings from captured output to utf-8 :param expect_failure: asset processing is expected to fail, so don't error on a failure, and assert on no failure. """ logger.info(f"Launching AP with command: {command}") - start = datetime.datetime.now() - try: - duration = datetime.timedelta(seconds=timeout) - except TypeError: - logger.warning("Cannot set timeout value of '{}' seconds, defaulting to {} hours".format( - timeout, DEFAULT_TIMEOUT_HOURS)) - duration = datetime.timedelta(hours=DEFAULT_TIMEOUT_HOURS) - timeout = duration.total_seconds() + start = time.time() + if type(timeout) not in [int, float] or timeout < 1: + logger.warning(f"Invalid timeout {timeout} - defaulting to {DEFAULT_TIMEOUT_SECONDS} seconds") + timeout = DEFAULT_TIMEOUT_SECONDS run_result = subprocess.run(command, close_fds=True, timeout=timeout, capture_output=capture_output) output_list = None @@ -609,8 +618,7 @@ class AssetProcessor(object): elif expect_failure: logger.error(f"{command} was expected to fail, but instead ran without failure.") return True, output_list - logger.info( - f"{command} completed successfully in {(datetime.datetime.now() - start).seconds} seconds") + logger.info(f"{command} completed successfully in {time.time() - start} seconds") return True, output_list def set_failure_log_folder(self, log_root): @@ -743,14 +751,14 @@ class AssetProcessor(object): :return: Absolute path of added scan folder """ if os.path.isabs(folder_name): - if not folder_name in self._override_scan_folders: + if folder_name not in self._override_scan_folders: self._override_scan_folders.append(folder_name) logger.info(f'Adding override scan folder {folder_name}') return folder_name else: if not self._temp_asset_root: - logger.warning(f"Can't create scan folder, no temporary asset workspace has been created") - return + logger.warning(f"Can not create scan folder, no temporary asset workspace has been created") + return "" scan_folder = os.path.join(self._temp_asset_root if self._temp_asset_root else self._workspace.paths.engine_root(), folder_name) if not os.path.isdir(scan_folder): @@ -802,9 +810,9 @@ class AssetProcessor(object): if not use_current_root: self.create_temp_asset_root() test_asset_root = os.path.join(self._temp_asset_root, self._workspace.project if relative_asset_root is None - else relative_asset_root) + else relative_asset_root) test_folder = os.path.join(test_asset_root, function_name if existing_function_name is None - else existing_function_name) + else existing_function_name) if not os.path.isdir(test_folder): os.makedirs(test_folder) if add_scan_folder: diff --git a/Tools/LyTestTools/ly_test_tools/o3de/asset_processor_utils.py b/Tools/LyTestTools/ly_test_tools/o3de/asset_processor_utils.py index 34ea5f4eb8..f30ed2f233 100644 --- a/Tools/LyTestTools/ly_test_tools/o3de/asset_processor_utils.py +++ b/Tools/LyTestTools/ly_test_tools/o3de/asset_processor_utils.py @@ -9,8 +9,7 @@ import logging import os import subprocess -import ly_test_tools -from ly_test_tools.environment.process_utils import kill_processes_named as kill_processes_named +import ly_test_tools.environment.process_utils as process_utils logger = logging.getLogger(__name__) @@ -23,7 +22,7 @@ def start_asset_processor(bin_dir): :return: A subprocess.Popen object for the AssetProcessor process. """ os.chdir(bin_dir) - asset_processor = subprocess.Popen(['AssetProcessor.exe']) + asset_processor = subprocess.Popen(['AssetProcessor'], env=process_utils.get_display_env()) return_code = asset_processor.poll() if return_code is not None and return_code != 0: @@ -40,11 +39,9 @@ def kill_asset_processor(): :return: None """ - - kill_processes_named('AssetProcessor_tmp', ignore_extensions=True) - kill_processes_named('AssetProcessor', ignore_extensions=True) - kill_processes_named('AssetProcessorBatch', ignore_extensions=True) - kill_processes_named('AssetBuilder', ignore_extensions=True) - kill_processes_named('rc', ignore_extensions=True) - kill_processes_named('Lua Editor', ignore_extensions=True) - + process_utils.kill_processes_named('AssetProcessor_tmp', ignore_extensions=True) + process_utils.kill_processes_named('AssetProcessor', ignore_extensions=True) + process_utils.kill_processes_named('AssetProcessorBatch', ignore_extensions=True) + process_utils.kill_processes_named('AssetBuilder', ignore_extensions=True) + process_utils.kill_processes_named('rc', ignore_extensions=True) + process_utils.kill_processes_named('Lua Editor', ignore_extensions=True) diff --git a/Tools/LyTestTools/tests/integ/sanity_tests.py b/Tools/LyTestTools/tests/integ/sanity_tests.py index e27398c477..ed77d7b8db 100755 --- a/Tools/LyTestTools/tests/integ/sanity_tests.py +++ b/Tools/LyTestTools/tests/integ/sanity_tests.py @@ -58,12 +58,11 @@ class TestAutomatedTestingProject(object): # Call the game client executable with launcher.start(): # Wait for the process to exist - waiter.wait_for(lambda: process_utils.process_exists(f"{project}.GameLauncher.exe", ignore_extensions=True)) + waiter.wait_for(lambda: process_utils.process_exists(f"{project}.GameLauncher", ignore_extensions=True)) finally: # Clean up processes after the test is finished process_utils.kill_processes_named(names=process_utils.LY_PROCESS_KILL_LIST, ignore_extensions=True) - @pytest.mark.skipif(not ly_test_tools.WINDOWS, reason="Editor currently only functions on Windows") def test_StartEditor_Sanity(self, project): """ The `test_StartEditor_Sanity` test function is similar to the previous example with minor adjustments. A diff --git a/Tools/LyTestTools/tests/unit/test_asset_processor.py b/Tools/LyTestTools/tests/unit/test_asset_processor.py index f487cc8537..aabbf2f2f5 100755 --- a/Tools/LyTestTools/tests/unit/test_asset_processor.py +++ b/Tools/LyTestTools/tests/unit/test_asset_processor.py @@ -58,10 +58,8 @@ class TestAssetProcessor(object): under_test.start(connect_to_ap=True) assert under_test._ap_proc is not None - mock_popen.assert_called_once_with([mock_ap_path, '--zeroAnalysisMode', - f'--regset="/Amazon/AzCore/Bootstrap/project_path={mock_project_path}"', - '--logDir', under_test.log_root(), - '--acceptInput', '--platforms', 'bar'], cwd=os.path.dirname(mock_ap_path)) + mock_popen.assert_called_once() + assert '--zeroAnalysisMode' in mock_popen.call_args[0][0] mock_connect.assert_called() @mock.patch('ly_test_tools._internal.managers.workspace.AbstractWorkspaceManager') @@ -114,7 +112,7 @@ class TestAssetProcessor(object): assert result mock_run.assert_called_once_with([apb_path, - f'--regset="/Amazon/AzCore/Bootstrap/project_path={mock_project_path}"', + f'--regset="/Amazon/AzCore/Bootstrap/project_path={mock_project_path}"', '--logDir', under_test.log_root()], close_fds=True, capture_output=False, timeout=1) @@ -150,10 +148,8 @@ class TestAssetProcessor(object): result, _ = under_test.batch_process(None, False) assert not result - mock_run.assert_called_once_with([apb_path, - f'--regset="/Amazon/AzCore/Bootstrap/project_path={mock_project_path}"', - '--logDir', under_test.log_root()], - close_fds=True, capture_output=False, timeout=28800.0) + mock_run.assert_called_once() + assert f'--regset="/Amazon/AzCore/Bootstrap/project_path={mock_project_path}"' in mock_run.call_args[0][0] @mock.patch('ly_test_tools._internal.managers.workspace.AbstractWorkspaceManager') From afa8bb92264c2928ea40420267714c83c3d3b723 Mon Sep 17 00:00:00 2001 From: Michael Pollind Date: Sat, 9 Oct 2021 21:10:08 -0700 Subject: [PATCH 07/65] chore: update intersect and improve documentation Signed-off-by: Michael Pollind --- .../AzCore/AzCore/Math/IntersectSegment.cpp | 33 +- .../AzCore/AzCore/Math/IntersectSegment.h | 648 ++++++++++-------- .../AzCore/AzCore/Math/IntersectSegment.inl | 102 +++ .../AzCore/AzCore/azcore_files.cmake | 1 + 4 files changed, 485 insertions(+), 299 deletions(-) create mode 100644 Code/Framework/AzCore/AzCore/Math/IntersectSegment.inl diff --git a/Code/Framework/AzCore/AzCore/Math/IntersectSegment.cpp b/Code/Framework/AzCore/AzCore/Math/IntersectSegment.cpp index a7a0d5197e..2a00412689 100644 --- a/Code/Framework/AzCore/AzCore/Math/IntersectSegment.cpp +++ b/Code/Framework/AzCore/AzCore/Math/IntersectSegment.cpp @@ -157,7 +157,7 @@ Intersect::IntersectSegmentTriangle( // TestSegmentAABBOrigin // [10/21/2009] //========================================================================= -int +bool AZ::Intersect::TestSegmentAABBOrigin(const Vector3& midPoint, const Vector3& halfVector, const Vector3& aabbExtends) { const Vector3 EPSILON(0.001f); // \todo this is slow load move to a const @@ -168,7 +168,7 @@ AZ::Intersect::TestSegmentAABBOrigin(const Vector3& midPoint, const Vector3& hal // Try world coordinate axes as separating axes if (!absMidpoint.IsLessEqualThan(absHalfMidpoint)) { - return 0; + return false; } // Add in an epsilon term to counteract arithmetic errors when segment is @@ -188,11 +188,11 @@ AZ::Intersect::TestSegmentAABBOrigin(const Vector3& midPoint, const Vector3& hal Vector3 ead(ey * adz + ez * ady, ex * adz + ez * adx, ex * ady + ey * adx); if (!absMDCross.IsLessEqualThan(ead)) { - return 0; + return false; } // No separating axis found; segment must be overlapping AABB - return 1; + return true; } @@ -200,7 +200,7 @@ AZ::Intersect::TestSegmentAABBOrigin(const Vector3& midPoint, const Vector3& hal // IntersectRayAABB // [10/21/2009] //========================================================================= -int +RayAABBIsectTypes AZ::Intersect::IntersectRayAABB( const Vector3& rayStart, const Vector3& dir, const Vector3& dirRCP, const Aabb& aabb, float& tStart, float& tEnd, Vector3& startNormal /*, Vector3& inter*/) @@ -352,11 +352,14 @@ AZ::Intersect::IntersectRayAABB( return ISECT_RAY_AABB_ISECT; } + + + //========================================================================= // IntersectRayAABB2 // [2/18/2011] //========================================================================= -int +RayAABBIsectTypes AZ::Intersect::IntersectRayAABB2(const Vector3& rayStart, const Vector3& dirRCP, const Aabb& aabb, float& start, float& end) { float tmin, tmax, tymin, tymax, tzmin, tzmax; @@ -1166,7 +1169,7 @@ int AZ::Intersect::IntersectRayObb(const Vector3& rayOrigin, const Vector3& rayD // IntersectSegmentCylinder // [10/21/2009] //========================================================================= -int +CylinderIsectTypes AZ::Intersect::IntersectSegmentCylinder( const Vector3& sa, const Vector3& dir, const Vector3& p, const Vector3& q, const float r, float& t) { @@ -1225,7 +1228,7 @@ AZ::Intersect::IntersectSegmentCylinder( return RR_ISECT_RAY_CYL_NONE; // No real roots; no intersection } t = (-b - Sqrt(discr)) / a; - int result = RR_ISECT_RAY_CYL_PQ; // default along the PQ segment + CylinderIsectTypes result = RR_ISECT_RAY_CYL_PQ; // default along the PQ segment if (md + t * nd < 0.0f) { @@ -1294,7 +1297,7 @@ AZ::Intersect::IntersectSegmentCylinder( // IntersectSegmentCapsule // [10/21/2009] //========================================================================= -int +CapsuleIsectTypes AZ::Intersect::IntersectSegmentCapsule(const Vector3& sa, const Vector3& dir, const Vector3& p, const Vector3& q, const float r, float& t) { int result = IntersectSegmentCylinder(sa, dir, p, q, r, t); @@ -1361,7 +1364,7 @@ AZ::Intersect::IntersectSegmentCapsule(const Vector3& sa, const Vector3& dir, co // IntersectSegmentPolyhedron // [10/21/2009] //========================================================================= -int +bool AZ::Intersect::IntersectSegmentPolyhedron( const Vector3& sa, const Vector3& sBA, const Plane p[], int numPlanes, float& tfirst, float& tlast, int& iFirstPlane, int& iLastPlane) @@ -1388,7 +1391,7 @@ AZ::Intersect::IntersectSegmentPolyhedron( // If so, return "no intersection" if segment lies outside plane if (dist < 0.0f) { - return 0; + return false; } } else @@ -1417,7 +1420,7 @@ AZ::Intersect::IntersectSegmentPolyhedron( // Exit with "no intersection" if intersection becomes empty if (tfirst > tlast) { - return 0; + return false; } } } @@ -1425,11 +1428,11 @@ AZ::Intersect::IntersectSegmentPolyhedron( //DBG_Assert(iFirstPlane!=-1&&iLastPlane!=-1,("We have some bad border case to have only one plane, fix this function!")); if (iFirstPlane == -1 && iLastPlane == -1) { - return 0; + return false; } // A nonzero logical intersection, so the segment intersects the polyhedron - return 1; + return true; } //========================================================================= @@ -1442,7 +1445,7 @@ AZ::Intersect::ClosestSegmentSegment( const Vector3& segment2Start, const Vector3& segment2End, float& segment1Proportion, float& segment2Proportion, Vector3& closestPointSegment1, Vector3& closestPointSegment2, - float epsilon /*= 1e-4f*/ ) + float epsilon) { const Vector3 segment1 = segment1End - segment1Start; const Vector3 segment2 = segment2End - segment2Start; diff --git a/Code/Framework/AzCore/AzCore/Math/IntersectSegment.h b/Code/Framework/AzCore/AzCore/Math/IntersectSegment.h index df3d5e10fb..7be35c5ae6 100644 --- a/Code/Framework/AzCore/AzCore/Math/IntersectSegment.h +++ b/Code/Framework/AzCore/AzCore/Math/IntersectSegment.h @@ -5,257 +5,262 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ -#ifndef AZCORE_MATH_SEGMENT_INTERSECTION_H -#define AZCORE_MATH_SEGMENT_INTERSECTION_H +#pragma once -#include #include #include #include - -/// \file isect_segment.h +#include namespace AZ { namespace Intersect { - //! LineToPointDistanceTime computes the time of the shortest distance from point 'p' to segment (s1,s2). - //! To calculate the point of intersection: - //! P = s1 + u (s2 - s1) - //! @param s1 segment start point - //! @param s2 segment end point - //! @param p point to find the closest time to. - //! @return time (on the segment) for the shortest distance from 'p' to (s1,s2) [0.0f (s1),1.0f (s2)] - inline float LineToPointDistanceTime(const Vector3& s1, const Vector3& s21, const Vector3& p) - { - // so u = (p.x - s1.x)*(s2.x - s1.x) + (p.y - s1.y)*(s2.y - s1.y) + (p.z-s1.z)*(s2.z-s1.z) / |s2-s1|^2 - return s21.Dot(p - s1) / s21.Dot(s21); - } + /** + * LineToPointDistanceTime computes the time of the shortest distance from point 'p' to segment (s1,s2). + * To calculate the point of intersection: + * P = s1 + u (s2 - s1) + * @param s1 segment start point + * @param s2 segment end point + * @param p point to find the closest time to. + * @return time (on the segment) for the shortest distance from 'p' to (s1,s2) [0.0f (s1),1.0f (s2)] + */ + float LineToPointDistanceTime(const Vector3& s1, const Vector3& s21, const Vector3& p); - //! LineToPointDistance computes the closest point to 'p' from a segment (s1,s2). - //! @param s1 segment start point - //! @param s2 segment end point - //! @param p point to find the closest time to. - //! @param u time (on the segment) for the shortest distance from 'p' to (s1,s2) [0.0f (s1),1.0f (s2)] - //! @return the closest point - inline Vector3 LineToPointDistance(const Vector3& s1, const Vector3& s2, const Vector3& p, float& u) - { - const Vector3 s21 = s2 - s1; - // we assume seg1 and seg2 are NOT coincident - AZ_MATH_ASSERT(!s21.IsClose(Vector3(0.0f), 1e-4f), "OK we agreed that we will pass valid segments! (s1 != s2)"); + /** + * LineToPointDistance computes the closest point to 'p' from a segment (s1,s2). + * @param s1 segment start point + * @param s2 segment end point + * @param p point to find the closest time to. + * @param u time (on the segment) for the shortest distance from 'p' to (s1,s2) [0.0f (s1),1.0f (s2)] + * @return the closest point + */ + Vector3 LineToPointDistance(const Vector3& s1, const Vector3& s2, const Vector3& p, float& u); - u = LineToPointDistanceTime(s1, s21, p); - - return s1 + u * s21; - } - - //! Given segment pq and triangle abc (CCW), returns whether segment intersects - //! triangle and if so, also returns the barycentric coordinates (u,v,w) - //! of the intersection point. - //! @param p segment start point - //! @param q segment end point - //! @param a triangle point 1 - //! @param b triangle point 2 - //! @param c triangle point 3 - //! @param normal at the intersection point. - //! @param t time of intersection along the segment [0.0 (p), 1.0 (q)] - //! @return 1 if the segment intersects the triangle otherwise 0 + /** + * Given segment pq and triangle abc (CCW), returns whether segment intersects + * triangle and if so, also returns the barycentric coordinates (u,v,w) + * of the intersection point. + * + * @param p segment start point + * @param q segment end point + * @param a triangle point 1 + * @param b triangle point 2 + * @param c triangle point 3 + * @param normal at the intersection point. + * @param t time of intersection along the segment [0.0 (p), 1.0 (q)] + * @return true if the segments intersects the triangle otherwise false + */ int IntersectSegmentTriangleCCW( - const Vector3& p, const Vector3& q, const Vector3& a, const Vector3& b, const Vector3& c, - /*float &u, float &v, float &w,*/ Vector3& normal, float& t); + const Vector3& p, const Vector3& q, const Vector3& a, const Vector3& b, const Vector3& c, Vector3& normal, float& t); - //! Same as \ref IntersectSegmentTriangleCCW without respecting the triangle (a,b,c) vertex order (double sided). + /** + * Same as \ref IntersectSegmentTriangleCCW without respecting the triangle (a,b,c) vertex order (double sided). + * + * @param p segment start point + * @param q segment end point + * @param a triangle point 1 + * @param b triangle point 2 + * @param c triangle point 3 + * @param normal at the intersection point; + * @param t time of intersection along the segment [0.0 (p), 1.0 (q)] + * @return true if the segments intersects the triangle otherwise false + */ int IntersectSegmentTriangle( - const Vector3& p, const Vector3& q, const Vector3& a, const Vector3& b, const Vector3& c, - /*float &u, float &v, float &w,*/ Vector3& normal, float& t); + const Vector3& p, const Vector3& q, const Vector3& a, const Vector3& b, const Vector3& c, Vector3& normal, float& t); //! Ray aabb intersection result types. - enum RayAABBIsectTypes + enum RayAABBIsectTypes : AZ::s32 { - ISECT_RAY_AABB_NONE = 0, ///< no intersection - ISECT_RAY_AABB_SA_INSIDE, ///< the ray starts inside the aabb - ISECT_RAY_AABB_ISECT, ///< intersects along the PQ segment + ISECT_RAY_AABB_NONE = 0, ///< no intersection + ISECT_RAY_AABB_SA_INSIDE, ///< the ray starts inside the aabb + ISECT_RAY_AABB_ISECT, ///< intersects along the PQ segment }; - //! Intersect ray R(t) = rayStart + t*d against AABB a. When intersecting, - //! return intersection distance tmin and point q of intersection. - //! @param rayStart ray starting point - //! @param dir ray direction and length (dir = rayEnd - rayStart) - //! @param dirRCP 1/dir (reciprocal direction - we cache this result very often so we don't need to compute it multiple times, otherwise just use dir.GetReciprocal()) - //! @param aabb Axis aligned bounding box to intersect against - //! @param tStart time on ray of the first intersection [0,1] or 0 if the ray starts inside the aabb - check the return value - //! @param tEnd time of the of the second intersection [0,1] (it can be > 1 if intersects after the rayEnd) - //! @param startNormal normal at the start point. - //! @return \ref RayAABBIsectTypes - int IntersectRayAABB( - const Vector3& rayStart, const Vector3& dir, const Vector3& dirRCP, const Aabb& aabb, - float& tStart, float& tEnd, Vector3& startNormal /*, Vector3& inter*/); + /** + * Intersect ray R(t) = rayStart + t*d against AABB a. When intersecting, + * return intersection distance tmin and point q of intersection. + * @param rayStart ray starting point + * @param dir ray direction and length (dir = rayEnd - rayStart) + * @param dirRCP 1/dir (reciprocal direction - we cache this result very often so we don't need to compute it multiple times, + * otherwise just use dir.GetReciprocal()) + * @param aabb Axis aligned bounding box to intersect against + * @param tStart time on ray of the first intersection [0,1] or 0 if the ray starts inside the aabb - check the return value + * @param tEnd time of the of the second intersection [0,1] (it can be > 1 if intersects after the rayEnd) + * @param startNormal normal at the start point. + * @return \ref RayAABBIsectTypes + */ + RayAABBIsectTypes IntersectRayAABB( + const Vector3& rayStart, + const Vector3& dir, + const Vector3& dirRCP, + const Aabb& aabb, + float& tStart, + float& tEnd, + Vector3& startNormal /*, Vector3& inter*/); - //! Intersect ray against AABB. - //! @param rayStart ray starting point. - //! @param dir ray reciprocal direction. - //! @param aabb Axis aligned bounding box to intersect against. - //! @param start length on ray of the first intersection. - //! @param end length of the of the second intersection. - //! @return \ref RayAABBIsectTypes In this faster version than IntersectRayAABB we return only ISECT_RAY_AABB_NONE and ISECT_RAY_AABB_ISECT. - //! You can check yourself for that case. - int IntersectRayAABB2( - const Vector3& rayStart, const Vector3& dirRCP, const Aabb& aabb, - float& start, float& end); + /** + * Intersect ray against AABB. + * + * @param rayStart ray starting point. + * @param dir ray reciprocal direction. + * @param aabb Axis aligned bounding box to intersect against. + * @param start length on ray of the first intersection. + * @param end length of the of the second intersection. + * @return \ref RayAABBIsectTypes In this faster version than IntersectRayAABB we return only ISECT_RAY_AABB_NONE and ISECT_RAY_AABB_ISECT. You can check yourself for that case. + */ + RayAABBIsectTypes IntersectRayAABB2(const Vector3& rayStart, const Vector3& dirRCP, const Aabb& aabb, float& start, float& end); - //! Clip a ray to an aabb. return true if ray was clipped. The ray - //! can be inside so don't use the result if the ray intersect the box. - inline int ClipRayWithAabb( - const Aabb& aabb, Vector3& rayStart, Vector3& rayEnd, float& tClipStart, float& tClipEnd) - { - Vector3 startNormal; - float tStart, tEnd; - Vector3 dirLen = rayEnd - rayStart; - if (IntersectRayAABB(rayStart, dirLen, dirLen.GetReciprocal(), aabb, tStart, tEnd, startNormal) != ISECT_RAY_AABB_NONE) - { - // clip the ray with the box - if (tStart > 0.0f) - { - rayStart = rayStart + tStart * dirLen; - tClipStart = tStart; - } - if (tEnd < 1.0f) - { - rayEnd = rayStart + tEnd * dirLen; - tClipEnd = tEnd; - } + /** + * Clip a ray to an aabb. return true if ray was clipped. The ray + * can be inside so don't use the result if the ray intersect the box. + * + * @param aabb bounds + * @param rayStart the start of the ray + * @param rayEnd the end of the ray + * @param tClipStart[out] The proportion where the ray enterts the aabb + * @param tClipEnd[out] The proportion where the ray exits the aabb + * @return true ray was clipped else false + */ + bool ClipRayWithAabb(const Aabb& aabb, Vector3& rayStart, Vector3& rayEnd, float& tClipStart, float& tClipEnd); - return 1; - } + /** + * Test segment and aabb where the segment is defined by midpoint + * midPoint = (p1-p0) * 0.5f and half vector halfVector = p1 - midPoint. + * the aabb is at the origin and defined by half extents only. + * + * @param midPoint midpoint of a line segment + * @param halfVector half vector of an aabb + * @param aabbExtends the extends of a bounded box + * @return 1 if the intersect, otherwise 0. + */ + bool TestSegmentAABBOrigin(const Vector3& midPoint, const Vector3& halfVector, const Vector3& aabbExtends); - return 0; - } - - //! Test segment and aabb where the segment is defined by midpoint - //! midPoint = (p1-p0) * 0.5f and half vector halfVector = p1 - midPoint. - //! the aabb is at the origin and defined by half extents only. - //! @return 1 if the intersect, otherwise 0. - int TestSegmentAABBOrigin(const Vector3& midPoint, const Vector3& halfVector, const Vector3& aabbExtends); - - //! Test if segment specified by points p0 and p1 intersects AABB. \ref TestSegmentAABBOrigin - //! @return 1 if the segment and AABB intersect, otherwise 0. - inline int TestSegmentAABB(const Vector3& p0, const Vector3& p1, const Aabb& aabb) - { - Vector3 e = aabb.GetExtents(); - Vector3 d = p1 - p0; - Vector3 m = p0 + p1 - aabb.GetMin() - aabb.GetMax(); - - return TestSegmentAABBOrigin(m, d, e); - } + /** + * Test if segment specified by points p0 and p1 intersects AABB. \ref TestSegmentAABBOrigin + * + * @param p0 point 1 + * @param p1 point 2 + * @param aabb bounded box + * @return true if the segment and AABB intersect, otherwise false. + */ + bool TestSegmentAABB(const Vector3& p0, const Vector3& p1, const Aabb& aabb); //! Ray sphere intersection result types. - enum SphereIsectTypes + enum SphereIsectTypes : AZ::s32 { ISECT_RAY_SPHERE_SA_INSIDE = -1, // the ray starts inside the cylinder - ISECT_RAY_SPHERE_NONE, // no intersection - ISECT_RAY_SPHERE_ISECT, // along the PQ segment + ISECT_RAY_SPHERE_NONE, // no intersection + ISECT_RAY_SPHERE_ISECT, // along the PQ segment }; - //! IntersectRaySphereOrigin - //! return time t>=0 but not limited, so if you check a segment make sure - //! t <= segmentLen - //! @param rayStart ray start point - //! @param rayDirNormalized ray direction normalized. - //! @param shereRadius sphere radius - //! @param time of closest intersection [0,+INF] in relation to the normalized direction. - //! @return \ref SphereIsectTypes - AZ_INLINE int IntersectRaySphereOrigin( - const Vector3& rayStart, const Vector3& rayDirNormalized, - const float sphereRadius, float& t) - { - Vector3 m = rayStart; - float b = m.Dot(rayDirNormalized); - float c = m.Dot(m) - sphereRadius * sphereRadius; + /** + * IntersectRaySphereOrigin + * return time t>=0 but not limited, so if you check a segment make sure + * t <= segmentLen + * @param rayStart ray start point + * @param rayDirNormalized ray direction normalized. + * @param shereRadius sphere radius + * @param time of closest intersection [0,+INF] in relation to the normalized direction. + * @return \ref SphereIsectTypes + **/ + SphereIsectTypes IntersectRaySphereOrigin( + const Vector3& rayStart, const Vector3& rayDirNormalized, const float sphereRadius, float& t); - // Exit if r's origin outside s (c > 0)and r pointing away from s (b > 0) - if (c > 0.0f && b > 0.0f) - { - return ISECT_RAY_SPHERE_NONE; - } - float discr = b * b - c; - // A negative discriminant corresponds to ray missing sphere - if (discr < 0.0f) - { - return ISECT_RAY_SPHERE_NONE; - } + /** + * Intersect ray (rayStart,rayDirNormalized) and sphere (sphereCenter,sphereRadius) \ref IntersectRaySphereOrigin + * + * @param rayStart + * @param rayDirNormalized + * @param sphereCenter + * @param sphereRadius + * @param t + * @return int + */ + SphereIsectTypes IntersectRaySphere( + const Vector3& rayStart, const Vector3& rayDirNormalized, const Vector3& sphereCenter, const float sphereRadius, float& t); - // Ray now found to intersect sphere, compute smallest t value of intersection - t = -b - Sqrt(discr); - - // If t is negative, ray started inside sphere so clamp t to zero - if (t < 0.0f) - { - // t = 0.0f; - return ISECT_RAY_SPHERE_SA_INSIDE; // no hit if inside - } - //q = p + t * d; - return ISECT_RAY_SPHERE_ISECT; - } - - //! Intersect ray (rayStart,rayDirNormalized) and sphere (sphereCenter,sphereRadius) \ref IntersectRaySphereOrigin - inline int IntersectRaySphere( - const Vector3& rayStart, const Vector3& rayDirNormalized, const Vector3& sphereCenter, const float sphereRadius, float& t) - { - return IntersectRaySphereOrigin(rayStart - sphereCenter, rayDirNormalized, sphereRadius, t); - } - - //! @param rayOrigin The origin of the ray to test. - //! @param rayDir The direction of the ray to test. It has to be unit length. - //! @param diskCenter Center point of the disk - //! @param diskRadius Radius of the disk - //! @param diskNormal A normal perpendicular to the disk - //! @param[out] t If returning 1 (indicating a hit), this contains distance from rayOrigin along the normalized rayDir that the hit occured at. - //! @return The number of intersecting points. + /** + * @param rayOrigin The origin of the ray to test. + * @param rayDir The direction of the ray to test. It has to be unit length. + * @param diskCenter Center point of the disk + * @param diskRadius Radius of the disk + * @param diskNormal A normal perpendicular to the disk + * @param[out] t If returning 1 (indicating a hit), this contains distance from rayOrigin along the normalized rayDir + * that the hit occured at. + * @return The number of intersecting points. + **/ int IntersectRayDisk( - const Vector3& rayOrigin, const Vector3& rayDir, const Vector3& diskCenter, const float diskRadius, const AZ::Vector3& diskNormal, float& t); + const Vector3& rayOrigin, + const Vector3& rayDir, + const Vector3& diskCenter, + const float diskRadius, + const AZ::Vector3& diskNormal, + float& t); - //! If there is only one intersecting point, the coefficient is stored in \ref t1. - //! @param rayOrigin The origin of the ray to test. - //! @param rayDir The direction of the ray to test. It has to be unit length. - //! @param cylinderEnd1 The center of the circle on one end of the cylinder. - //! @param cylinderDir The direction pointing from \ref cylinderEnd1 to the other end of the cylinder. It has to be unit length. - //! @param cylinderHeight The distance between two centers of the circles on two ends of the cylinder respectively. - //! @param[out] t1 A possible coefficient in the ray's explicit equation from which an intersecting point is calculated as "rayOrigin + t1 * rayDir". - //! @param[out] t2 A possible coefficient in the ray's explicit equation from which an intersecting point is calculated as "rayOrigin + t2 * rayDir". - //! @return The number of intersecting points. + /** + * If there is only one intersecting point, the coefficient is stored in \ref t1. + * @param rayOrigin The origin of the ray to test. + * @param rayDir The direction of the ray to test. It has to be unit length. + * @param cylinderEnd1 The center of the circle on one end of the cylinder. + * @param cylinderDir The direction pointing from \ref cylinderEnd1 to the other end of the cylinder. It has to be unit + * length. + * @param cylinderHeight The distance between two centers of the circles on two ends of the cylinder respectively. + * @param[out] t1 A possible coefficient in the ray's explicit equation from which an intersecting point is calculated + * as "rayOrigin + t1 * rayDir". + * @param[out] t2 A possible coefficient in the ray's explicit equation from which an intersecting point is calculated + * as "rayOrigin + t2 * rayDir". + * @return The number of intersecting points. + **/ int IntersectRayCappedCylinder( - const Vector3& rayOrigin, const Vector3& rayDir, - const Vector3& cylinderEnd1, const Vector3& cylinderDir, float cylinderHeight, float cylinderRadius, - float& t1, float& t2); + const Vector3& rayOrigin, + const Vector3& rayDir, + const Vector3& cylinderEnd1, + const Vector3& cylinderDir, + float cylinderHeight, + float cylinderRadius, + float& t1, + float& t2); - //! If there is only one intersecting point, the coefficient is stored in \ref t1. - //! @param rayOrigin The origin of the ray to test. - //! @param rayDir The direction of the ray to test. It has to be unit length. - //! @param coneApex The apex of the cone. - //! @param coneDir The unit-length direction from the apex to the base. - //! @param coneHeight The height of the cone, from the apex to the base. - //! @param coneBaseRadius The radius of the cone base circle. - //! @param[out] t1 A possible coefficient in the ray's explicit equation from which an intersecting point is calculated as "rayOrigin + t1 * rayDir". - //! @param[out] t2 A possible coefficient in the ray's explicit equation from which an intersecting point is calculated as "rayOrigin + t2 * rayDir". - //! @return The number of intersecting points. + /** + * If there is only one intersecting point, the coefficient is stored in \ref t1. + * @param rayOrigin The origin of the ray to test. + * @param rayDir The direction of the ray to test. It has to be unit length. + * @param coneApex The apex of the cone. + * @param coneDir The unit-length direction from the apex to the base. + * @param coneHeight The height of the cone, from the apex to the base. + * @param coneBaseRadius The radius of the cone base circle. + * @param[out] t1 A possible coefficient in the ray's explicit equation from which an intersecting point is calculated + * as "rayOrigin + t1 * rayDir". + * @param[out] t2 A possible coefficient in the ray's explicit equation from which an intersecting point is calculated + * as "rayOrigin + t2 * rayDir". + * @return The number of intersecting points. + **/ int IntersectRayCone( - const Vector3& rayOrigin, const Vector3& rayDir, - const Vector3& coneApex, const Vector3& coneDir, float coneHeight, float coneBaseRadius, - float& t1, float& t2); + const Vector3& rayOrigin, + const Vector3& rayDir, + const Vector3& coneApex, + const Vector3& coneDir, + float coneHeight, + float coneBaseRadius, + float& t1, + float& t2); - //! Test intersection between a ray and a plane in 3D. - //! @param rayOrigin The origin of the ray to test intersection with. - //! @param rayDir The direction of the ray to test intersection with. - //! @param planePos A point on the plane to test intersection with. - //! @param planeNormal The normal of the plane to test intersection with. - //! @param t[out] The coefficient in the ray's explicit equation from which the intersecting point is calculated as "rayOrigin + t * rayDirection". - //! @return The number of intersection point. + /** + * Test intersection between a ray and a plane in 3D. + * @param rayOrigin The origin of the ray to test intersection with. + * @param rayDir The direction of the ray to test intersection with. + * @param planePos A point on the plane to test intersection with. + * @param planeNormal The normal of the plane to test intersection with. + * @param t[out] The coefficient in the ray's explicit equation from which the intersecting point is calculated as "rayOrigin + *+ t * rayDirection". + * @return The number of intersection point. + **/ int IntersectRayPlane( - const Vector3& rayOrigin, const Vector3& rayDir, const Vector3& planePos, - const Vector3& planeNormal, float& t); + const Vector3& rayOrigin, const Vector3& rayDir, const Vector3& planePos, const Vector3& planeNormal, float& t); //! Test intersection between a ray and a two-sided quadrilateral defined by four points in 3D. - //! The four points that define the quadrilateral could be passed in with either counter clock-wise + //! The four points that define the quadrilateral could be passed in with either counter clock-wise //! winding or clock-wise winding. //! @param rayOrigin The origin of the ray to test intersection with. //! @param rayDir The direction of the ray to test intersection with. @@ -263,105 +268,180 @@ namespace AZ //! @param vertexB One of the four points that define the quadrilateral. //! @param vertexC One of the four points that define the quadrilateral. //! @param vertexD One of the four points that define the quadrilateral. - //! @param t[out] The coefficient in the ray's explicit equation from which the intersecting point is calculated as "rayOrigin + t * rayDirection". + //! @param t[out] The coefficient in the ray's explicit equation from which the intersecting point is calculated as "rayOrigin + + //! t * rayDirection". //! @return The number of intersection point. int IntersectRayQuad( - const Vector3& rayOrigin, const Vector3& rayDir, const Vector3& vertexA, - const Vector3& vertexB, const Vector3& vertexC, const Vector3& vertexD, float& t); - - //! Test intersection between a ray and an oriented box in 3D. - //! @param rayOrigin The origin of the ray to test intersection with. - //! @param rayDir The direction of the ray to test intersection with. - //! @param boxCenter The position of the center of the box. - //! @param boxAxis1 An axis along one dimension of the oriented box. - //! @param boxAxis2 An axis along one dimension of the oriented box. - //! @param boxAxis3 An axis along one dimension of the oriented box. - //! @param boxHalfExtent1 The half extent of the box on the dimension of \ref boxAxis1. - //! @param boxHalfExtent2 The half extent of the box on the dimension of \ref boxAxis2. - //! @param boxHalfExtent3 The half extent of the box on the dimension of \ref boxAxis3. - //! @param t[out] The coefficient in the ray's explicit equation from which the intersecting point is calculated as "rayOrigin + t * rayDirection". - //! @return 1 if there is an intersection, 0 otherwise. - int IntersectRayBox( - const Vector3& rayOrigin, const Vector3& rayDir, const Vector3& boxCenter, const Vector3& boxAxis1, - const Vector3& boxAxis2, const Vector3& boxAxis3, float boxHalfExtent1, float boxHalfExtent2, float boxHalfExtent3, + const Vector3& rayOrigin, + const Vector3& rayDir, + const Vector3& vertexA, + const Vector3& vertexB, + const Vector3& vertexC, + const Vector3& vertexD, float& t); - //! Test intersection between a ray and an OBB. - //! @param rayOrigin The origin of the ray to test intersection with. - //! @param rayDir The direction of the ray to test intersection with. - //! @param obb The OBB to test for intersection with the ray. - //! @param t[out] The coefficient in the ray's explicit equation from which the intersecting point is calculated as "rayOrigin + t * rayDirection". - //! @return 1 if there is an intersection, 0 otherwise. + /** Test intersection between a ray and an oriented box in 3D. + * @param rayOrigin The origin of the ray to test intersection with. + * @param rayDir The direction of the ray to test intersection with. + * @param boxCenter The position of the center of the box. + * @param boxAxis1 An axis along one dimension of the oriented box. + * @param boxAxis2 An axis along one dimension of the oriented box. + * @param boxAxis3 An axis along one dimension of the oriented box. + * @param boxHalfExtent1 The half extent of the box on the dimension of \ref boxAxis1. + * @param boxHalfExtent2 The half extent of the box on the dimension of \ref boxAxis2. + * @param boxHalfExtent3 The half extent of the box on the dimension of \ref boxAxis3. + * @param t[out] The coefficient in the ray's explicit equation from which the intersecting point is calculated as "rayOrigin + + * t * rayDirection". + * @return 1 if there is an intersection, 0 otherwise. + **/ + int IntersectRayBox( + const Vector3& rayOrigin, + const Vector3& rayDir, + const Vector3& boxCenter, + const Vector3& boxAxis1, + const Vector3& boxAxis2, + const Vector3& boxAxis3, + float boxHalfExtent1, + float boxHalfExtent2, + float boxHalfExtent3, + float& t); + + /** + * Test intersection between a ray and an OBB. + * @param rayOrigin The origin of the ray to test intersection with. + * @param rayDir The direction of the ray to test intersection with. + * @param obb The OBB to test for intersection with the ray. + * @param t[out] The coefficient in the ray's explicit equation from which the intersecting point is calculated as "rayOrigin + t * + * rayDirection". + * @return 1 if there is an intersection, 0 otherwise. + */ int IntersectRayObb(const Vector3& rayOrigin, const Vector3& rayDir, const Obb& obb, float& t); //! Ray cylinder intersection types. - enum CylinderIsectTypes + enum CylinderIsectTypes : AZ::s32 { RR_ISECT_RAY_CYL_SA_INSIDE = -1, // the ray starts inside the cylinder - RR_ISECT_RAY_CYL_NONE, // no intersection - RR_ISECT_RAY_CYL_PQ, // along the PQ segment - RR_ISECT_RAY_CYL_P_SIDE, // on the P side - RR_ISECT_RAY_CYL_Q_SIDE, // on the Q side + RR_ISECT_RAY_CYL_NONE, // no intersection + RR_ISECT_RAY_CYL_PQ, // along the PQ segment + RR_ISECT_RAY_CYL_P_SIDE, // on the P side + RR_ISECT_RAY_CYL_Q_SIDE, // on the Q side }; - //! Intersect segment S(t)=sa+t(dir), 0<=t<=1 against cylinder specified by p, q and r. - int IntersectSegmentCylinder( - const Vector3& sa, const Vector3& dir, const Vector3& p, const Vector3& q, - const float r, float& t); + /** + * Reference: Real-Time Collision Detection - 5.3.7 Intersecting Ray or Segment Against Cylinder + * Intersect segment S(t)=sa+t(dir), 0<=t<=1 against cylinder specified by p, q and r. + * + * @param sa point + * @param dir magnitude along sa + * @param p center point of side 1 cylinder + * @param q center point of side 2 cylinder + * @param r radius of cylinder + * @param t[out] proporition along line semgnet + * @return CylinderIsectTypes + */ + CylinderIsectTypes IntersectSegmentCylinder( + const Vector3& sa, const Vector3& dir, const Vector3& p, const Vector3& q, const float r, float& t); //! Capsule ray intersect types. enum CapsuleIsectTypes { ISECT_RAY_CAPSULE_SA_INSIDE = -1, // the ray starts inside the cylinder ISECT_RAY_CAPSULE_NONE, // no intersection - ISECT_RAY_CAPSULE_PQ, // along the PQ segment - ISECT_RAY_CAPSULE_P_SIDE, // on the P side - ISECT_RAY_CAPSULE_Q_SIDE, // on the Q side + ISECT_RAY_CAPSULE_PQ, // along the PQ segment + ISECT_RAY_CAPSULE_P_SIDE, // on the P side + ISECT_RAY_CAPSULE_Q_SIDE, // on the Q side }; - //! This is a quick implementation of segment capsule based on segment cylinder \ref IntersectSegmentCylinder - //! segment sphere intersection. We can optimize it a lot once we fix the ray - //! cylinder intersection. - int IntersectSegmentCapsule( - const Vector3& sa, const Vector3& dir, const Vector3& p, - const Vector3& q, const float r, float& t); + /** + * This is a quick implementation of segment capsule based on segment cylinder \ref IntersectSegmentCylinder + * segment sphere intersection. We can optimize it a lot once we fix the ray + * cylinder intersection. + */ + CapsuleIsectTypes IntersectSegmentCapsule( + const Vector3& sa, const Vector3& dir, const Vector3& p, const Vector3& q, const float r, float& t); - //! Intersect segment S(t)=A+t(B-A), 0<=t<=1 against convex polyhedron specified - //! by the n halfspaces defined by the planes p[]. On exit tfirst and tlast - //! define the intersection, if any. - int IntersectSegmentPolyhedron( - const Vector3& sa, const Vector3& sBA, const Plane p[], int numPlanes, - float& tfirst, float& tlast, int& iFirstPlane, int& iLastPlane); + /** + * Intersect segment S(t)=A+t(B-A), 0<=t<=1 against convex polyhedron specified + * by the n halfspaces defined by the planes p[]. On exit tfirst and tlast + * define the intersection, if any. + */ + bool IntersectSegmentPolyhedron( + const Vector3& sa, + const Vector3& sBA, + const Plane p[], + int numPlanes, + float& tfirst, + float& tlast, + int& iFirstPlane, + int& iLastPlane); - //! Calculate the line segment closestPointSegment1<->closestPointSegment2 that is the shortest route between - //! two segments segment1Start<->segment1End and segment2Start<->segment2End. Also calculate the values of segment1Proportion and segment2Proportion where - //! closestPointSegment1 = segment1Start + (segment1Proportion * (segment1End - segment1Start)) - //! closestPointSegment2 = segment2Start + (segment2Proportion * (segment2End - segment2Start)) - //! If segments are parallel returns a solution. + /** + * Calculate the line segment closestPointSegment1<->closestPointSegment2 that is the shortest route between + * two segments segment1Start<->segment1End and segment2Start<->segment2End. Also calculate the values of segment1Proportion and + * segment2Proportion where closestPointSegment1 = segment1Start + (segment1Proportion * (segment1End - segment1Start)) + * closestPointSegment2 = segment2Start + (segment2Proportion * (segment2End - segment2Start)) + * If segments are parallel returns a solution. + * @param segment1Start start of segment 1. + * @param segment1End end of segment 1. + * @param segment2Start start of segment 2. + * @param segment2End end of segment 2. + * @param segment1Proportion[out] the proporition along segment 1 [0..1] + * @param segment2Proportion[out] the proporition along segment 2 [0..1] + * @param closestPointSegment1[out] closest point on segment 1. + * @param closestPointSegment2[out] closest point on segment 2. + * @param epsilon the minimum square distance where a line segment can be treated as a single point. + */ void ClosestSegmentSegment( - const Vector3& segment1Start, const Vector3& segment1End, - const Vector3& segment2Start, const Vector3& segment2End, - float& segment1Proportion, float& segment2Proportion, - Vector3& closestPointSegment1, Vector3& closestPointSegment2, + const Vector3& segment1Start, + const Vector3& segment1End, + const Vector3& segment2Start, + const Vector3& segment2End, + float& segment1Proportion, + float& segment2Proportion, + Vector3& closestPointSegment1, + Vector3& closestPointSegment2, float epsilon = 1e-4f); - //! Calculate the line segment closestPointSegment1<->closestPointSegment2 that is the shortest route between - //! two segments segment1Start<->segment1End and segment2Start<->segment2End. - //! If segments are parallel returns a solution. + /** + * Calculate the line segment closestPointSegment1<->closestPointSegment2 that is the shortest route between + * two segments segment1Start<->segment1End and segment2Start<->segment2End. + * If segments are parallel returns a solution. + * + * @param segment1Start start of segment 1. + * @param segment1End end of segment 1. + * @param segment2Start start of segment 2. + * @param segment2End end of segment 2. + * @param closestPointSegment1[out] closest point on segment 1. + * @param closestPointSegment2[out] closest point on segment 2. + * @param epsilon the minimum square distance where a line segment can be treated as a single point. + */ void ClosestSegmentSegment( - const Vector3& segment1Start, const Vector3& segment1End, - const Vector3& segment2Start, const Vector3& segment2End, - Vector3& closestPointSegment1, Vector3& closestPointSegment2, + const Vector3& segment1Start, + const Vector3& segment1End, + const Vector3& segment2Start, + const Vector3& segment2End, + Vector3& closestPointSegment1, + Vector3& closestPointSegment2, float epsilon = 1e-4f); - //! Calculate the point (closestPointOnSegment) that is the closest point on - //! segment segmentStart/segmentEnd to point. Also calculate the value of proportion where - //! closestPointOnSegment = segmentStart + (proportion * (segmentEnd - segmentStart)) + /** + * Calculate the point (closestPointOnSegment) that is the closest point on + * segment segmentStart/segmentEnd to point. Also calculate the value of proportion where + * closestPointOnSegment = segmentStart + (proportion * (segmentEnd - segmentStart)) + * + * @param point the point to test + * @param segmentStart the start of the segment + * @param segmentEnd the end of the segment + * @param proportion[out] the proportion of the segment L(t) = (end - start) * t + * @param closestPointOnSegment[out] the point along the line segment + */ void ClosestPointSegment( - const Vector3& point, const Vector3& segmentStart, const Vector3& segmentEnd, - float& proportion, Vector3& closestPointOnSegment); - } -} + const Vector3& point, + const Vector3& segmentStart, + const Vector3& segmentEnd, + float& proportion, + Vector3& closestPointOnSegment); + } // namespace Intersect +} // namespace AZ -#endif // AZCORE_MATH_SEGMENT_INTERSECTION_H -#pragma once +#include diff --git a/Code/Framework/AzCore/AzCore/Math/IntersectSegment.inl b/Code/Framework/AzCore/AzCore/Math/IntersectSegment.inl new file mode 100644 index 0000000000..b9f5139923 --- /dev/null +++ b/Code/Framework/AzCore/AzCore/Math/IntersectSegment.inl @@ -0,0 +1,102 @@ +/* + * 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 + * + */ +#pragma once + +namespace AZ +{ + namespace Intersect + { + AZ_MATH_INLINE bool ClipRayWithAabb(const Aabb& aabb, Vector3& rayStart, Vector3& rayEnd, float& tClipStart, float& tClipEnd) + { + Vector3 startNormal; + float tStart, tEnd; + Vector3 dirLen = rayEnd - rayStart; + if (IntersectRayAABB(rayStart, dirLen, dirLen.GetReciprocal(), aabb, tStart, tEnd, startNormal) != ISECT_RAY_AABB_NONE) + { + // clip the ray with the box + if (tStart > 0.0f) + { + rayStart = rayStart + tStart * dirLen; + tClipStart = tStart; + } + if (tEnd < 1.0f) + { + rayEnd = rayStart + tEnd * dirLen; + tClipEnd = tEnd; + } + + return true; + } + + return false; + } + + AZ_MATH_INLINE SphereIsectTypes + IntersectRaySphereOrigin(const Vector3& rayStart, const Vector3& rayDirNormalized, const float sphereRadius, float& t) + { + Vector3 m = rayStart; + float b = m.Dot(rayDirNormalized); + float c = m.Dot(m) - sphereRadius * sphereRadius; + + // Exit if r's origin outside s (c > 0)and r pointing away from s (b > 0) + if (c > 0.0f && b > 0.0f) + { + return ISECT_RAY_SPHERE_NONE; + } + float discr = b * b - c; + // A negative discriminant corresponds to ray missing sphere + if (discr < 0.0f) + { + return ISECT_RAY_SPHERE_NONE; + } + + // Ray now found to intersect sphere, compute smallest t value of intersection + t = -b - Sqrt(discr); + + // If t is negative, ray started inside sphere so clamp t to zero + if (t < 0.0f) + { + // t = 0.0f; + return ISECT_RAY_SPHERE_SA_INSIDE; // no hit if inside + } + // q = p + t * d; + return ISECT_RAY_SPHERE_ISECT; + } + + AZ_MATH_INLINE SphereIsectTypes IntersectRaySphere(const Vector3& rayStart, const Vector3& rayDirNormalized, const Vector3& sphereCenter, const float sphereRadius, float& t) + { + return IntersectRaySphereOrigin(rayStart - sphereCenter, rayDirNormalized, sphereRadius, t); + } + + AZ_MATH_INLINE Vector3 LineToPointDistance(const Vector3& s1, const Vector3& s2, const Vector3& p, float& u) + { + const Vector3 s21 = s2 - s1; + // we assume seg1 and seg2 are NOT coincident + AZ_MATH_ASSERT(!s21.IsClose(Vector3(0.0f), 1e-4f), "OK we agreed that we will pass valid segments! (s1 != s2)"); + + u = LineToPointDistanceTime(s1, s21, p); + + return s1 + u * s21; + } + + AZ_MATH_INLINE float LineToPointDistanceTime(const Vector3& s1, const Vector3& s21, const Vector3& p) + { + // so u = (p.x - s1.x)*(s2.x - s1.x) + (p.y - s1.y)*(s2.y - s1.y) + (p.z-s1.z)*(s2.z-s1.z) / |s2-s1|^2 + return s21.Dot(p - s1) / s21.Dot(s21); + } + + AZ_MATH_INLINE bool TestSegmentAABB(const Vector3& p0, const Vector3& p1, const Aabb& aabb) + { + Vector3 e = aabb.GetExtents(); + Vector3 d = p1 - p0; + Vector3 m = p0 + p1 - aabb.GetMin() - aabb.GetMax(); + + return TestSegmentAABBOrigin(m, d, e); + } + } // namespace Intersect +} // namespace AZ diff --git a/Code/Framework/AzCore/AzCore/azcore_files.cmake b/Code/Framework/AzCore/AzCore/azcore_files.cmake index 6675958247..4d95ddf098 100644 --- a/Code/Framework/AzCore/AzCore/azcore_files.cmake +++ b/Code/Framework/AzCore/AzCore/azcore_files.cmake @@ -282,6 +282,7 @@ set(FILES Math/Internal/VertexContainer.inl Math/InterpolationSample.h Math/IntersectPoint.h + Math/IntersectSegment.inl Math/IntersectSegment.cpp Math/IntersectSegment.h Math/MathIntrinsics.h From 1c3b293cd36da71dd5c34fc71521d9a81446b885 Mon Sep 17 00:00:00 2001 From: Michael Pollind Date: Mon, 11 Oct 2021 07:28:34 -0700 Subject: [PATCH 08/65] fix comments replace /** with //! Signed-off-by: Michael Pollind --- .../AzCore/AzCore/Math/IntersectSegment.h | 454 ++++++++---------- .../AzCore/AzCore/Math/IntersectSegment.inl | 1 - 2 files changed, 204 insertions(+), 251 deletions(-) diff --git a/Code/Framework/AzCore/AzCore/Math/IntersectSegment.h b/Code/Framework/AzCore/AzCore/Math/IntersectSegment.h index 7be35c5ae6..ecb0d7acc9 100644 --- a/Code/Framework/AzCore/AzCore/Math/IntersectSegment.h +++ b/Code/Framework/AzCore/AzCore/Math/IntersectSegment.h @@ -16,56 +16,47 @@ namespace AZ { namespace Intersect { - /** - * LineToPointDistanceTime computes the time of the shortest distance from point 'p' to segment (s1,s2). - * To calculate the point of intersection: - * P = s1 + u (s2 - s1) - * @param s1 segment start point - * @param s2 segment end point - * @param p point to find the closest time to. - * @return time (on the segment) for the shortest distance from 'p' to (s1,s2) [0.0f (s1),1.0f (s2)] - */ + //! LineToPointDistanceTime computes the time of the shortest distance from point 'p' to segment (s1,s2). + //! To calculate the point of intersection: + //! P = s1 + u (s2 - s1) + //! @param s1 segment start point + //! @param s2 segment end point + //! @param p point to find the closest time to. + //! @return time (on the segment) for the shortest distance from 'p' to (s1,s2) [0.0f (s1),1.0f (s2)] float LineToPointDistanceTime(const Vector3& s1, const Vector3& s21, const Vector3& p); - /** - * LineToPointDistance computes the closest point to 'p' from a segment (s1,s2). - * @param s1 segment start point - * @param s2 segment end point - * @param p point to find the closest time to. - * @param u time (on the segment) for the shortest distance from 'p' to (s1,s2) [0.0f (s1),1.0f (s2)] - * @return the closest point - */ + //! LineToPointDistance computes the closest point to 'p' from a segment (s1,s2). + //! @param s1 segment start point + //! @param s2 segment end point + //! @param p point to find the closest time to. + //! @param u time (on the segment) for the shortest distance from 'p' to (s1,s2) [0.0f (s1),1.0f (s2)] + //! @return the closest point Vector3 LineToPointDistance(const Vector3& s1, const Vector3& s2, const Vector3& p, float& u); - /** - * Given segment pq and triangle abc (CCW), returns whether segment intersects - * triangle and if so, also returns the barycentric coordinates (u,v,w) - * of the intersection point. - * - * @param p segment start point - * @param q segment end point - * @param a triangle point 1 - * @param b triangle point 2 - * @param c triangle point 3 - * @param normal at the intersection point. - * @param t time of intersection along the segment [0.0 (p), 1.0 (q)] - * @return true if the segments intersects the triangle otherwise false - */ + //! Given segment pq and triangle abc (CCW), returns whether segment intersects + //! triangle and if so, also returns the barycentric coordinates (u,v,w) + //! of the intersection point. + //! + //! @param p segment start point + //! @param q segment end point + //! @param a triangle point 1 + //! @param b triangle point 2 + //! @param c triangle point 3 + //! @param normal at the intersection point. + //! @param t time of intersection along the segment [0.0 (p), 1.0 (q)] + //! @return true if the segments intersects the triangle otherwise false int IntersectSegmentTriangleCCW( const Vector3& p, const Vector3& q, const Vector3& a, const Vector3& b, const Vector3& c, Vector3& normal, float& t); - /** - * Same as \ref IntersectSegmentTriangleCCW without respecting the triangle (a,b,c) vertex order (double sided). - * - * @param p segment start point - * @param q segment end point - * @param a triangle point 1 - * @param b triangle point 2 - * @param c triangle point 3 - * @param normal at the intersection point; - * @param t time of intersection along the segment [0.0 (p), 1.0 (q)] - * @return true if the segments intersects the triangle otherwise false - */ + //! Same as \ref IntersectSegmentTriangleCCW without respecting the triangle (a,b,c) vertex order (double sided). + //! //! @param p segment start point + //! @param q segment end point + //! @param a triangle point 1 + //! @param b triangle point 2 + //! @param c triangle point 3 + //! @param normal at the intersection point; + //! @param t time of intersection along the segment [0.0 (p), 1.0 (q)] + //! @return true if the segments intersects the triangle otherwise false int IntersectSegmentTriangle( const Vector3& p, const Vector3& q, const Vector3& a, const Vector3& b, const Vector3& c, Vector3& normal, float& t); @@ -77,19 +68,17 @@ namespace AZ ISECT_RAY_AABB_ISECT, ///< intersects along the PQ segment }; - /** - * Intersect ray R(t) = rayStart + t*d against AABB a. When intersecting, - * return intersection distance tmin and point q of intersection. - * @param rayStart ray starting point - * @param dir ray direction and length (dir = rayEnd - rayStart) - * @param dirRCP 1/dir (reciprocal direction - we cache this result very often so we don't need to compute it multiple times, - * otherwise just use dir.GetReciprocal()) - * @param aabb Axis aligned bounding box to intersect against - * @param tStart time on ray of the first intersection [0,1] or 0 if the ray starts inside the aabb - check the return value - * @param tEnd time of the of the second intersection [0,1] (it can be > 1 if intersects after the rayEnd) - * @param startNormal normal at the start point. - * @return \ref RayAABBIsectTypes - */ + //! Intersect ray R(t) = rayStart + t*d against AABB a. When intersecting, + //! return intersection distance tmin and point q of intersection. + //! @param rayStart ray starting point + //! @param dir ray direction and length (dir = rayEnd - rayStart) + //! @param dirRCP 1/dir (reciprocal direction - we cache this result very often so we don't need to compute it multiple times, + //! otherwise just use dir.GetReciprocal()) + //! @param aabb Axis aligned bounding box to intersect against + //! @param tStart time on ray of the first intersection [0,1] or 0 if the ray starts inside the aabb - check the return value + //! @param tEnd time of the of the second intersection [0,1] (it can be > 1 if intersects after the rayEnd) + //! @param startNormal normal at the start point. + //! @return \ref RayAABBIsectTypes RayAABBIsectTypes IntersectRayAABB( const Vector3& rayStart, const Vector3& dir, @@ -99,51 +88,43 @@ namespace AZ float& tEnd, Vector3& startNormal /*, Vector3& inter*/); - /** - * Intersect ray against AABB. - * - * @param rayStart ray starting point. - * @param dir ray reciprocal direction. - * @param aabb Axis aligned bounding box to intersect against. - * @param start length on ray of the first intersection. - * @param end length of the of the second intersection. - * @return \ref RayAABBIsectTypes In this faster version than IntersectRayAABB we return only ISECT_RAY_AABB_NONE and ISECT_RAY_AABB_ISECT. You can check yourself for that case. - */ + //! Intersect ray against AABB. + //! + //! @param rayStart ray starting point. + //! @param dir ray reciprocal direction. + //! @param aabb Axis aligned bounding box to intersect against. + //! @param start length on ray of the first intersection. + //! @param end length of the of the second intersection. + //! @return \ref RayAABBIsectTypes In this faster version than IntersectRayAABB we return only ISECT_RAY_AABB_NONE and ISECT_RAY_AABB_ISECT. You can check yourself for that case. RayAABBIsectTypes IntersectRayAABB2(const Vector3& rayStart, const Vector3& dirRCP, const Aabb& aabb, float& start, float& end); - /** - * Clip a ray to an aabb. return true if ray was clipped. The ray - * can be inside so don't use the result if the ray intersect the box. - * - * @param aabb bounds - * @param rayStart the start of the ray - * @param rayEnd the end of the ray - * @param tClipStart[out] The proportion where the ray enterts the aabb - * @param tClipEnd[out] The proportion where the ray exits the aabb - * @return true ray was clipped else false - */ + //! Clip a ray to an aabb. return true if ray was clipped. The ray + //! can be inside so don't use the result if the ray intersect the box. + //! + //! @param aabb bounds + //! @param rayStart the start of the ray + //! @param rayEnd the end of the ray + //! @param tClipStart[out] The proportion where the ray enterts the aabb + //! @param tClipEnd[out] The proportion where the ray exits the aabb + //! @return true ray was clipped else false bool ClipRayWithAabb(const Aabb& aabb, Vector3& rayStart, Vector3& rayEnd, float& tClipStart, float& tClipEnd); - /** - * Test segment and aabb where the segment is defined by midpoint - * midPoint = (p1-p0) * 0.5f and half vector halfVector = p1 - midPoint. - * the aabb is at the origin and defined by half extents only. - * - * @param midPoint midpoint of a line segment - * @param halfVector half vector of an aabb - * @param aabbExtends the extends of a bounded box - * @return 1 if the intersect, otherwise 0. - */ + //! Test segment and aabb where the segment is defined by midpoint + //! midPoint = (p1-p0) * 0.5f and half vector halfVector = p1 - midPoint. + //! the aabb is at the origin and defined by half extents only. + //! + //! @param midPoint midpoint of a line segment + //! @param halfVector half vector of an aabb + //! @param aabbExtends the extends of a bounded box + //! @return 1 if the intersect, otherwise 0. bool TestSegmentAABBOrigin(const Vector3& midPoint, const Vector3& halfVector, const Vector3& aabbExtends); - /** - * Test if segment specified by points p0 and p1 intersects AABB. \ref TestSegmentAABBOrigin - * - * @param p0 point 1 - * @param p1 point 2 - * @param aabb bounded box - * @return true if the segment and AABB intersect, otherwise false. - */ + //! Test if segment specified by points p0 and p1 intersects AABB. \ref TestSegmentAABBOrigin + //! + //! @param p0 point 1 + //! @param p1 point 2 + //! @param aabb bounded box + //! @return true if the segment and AABB intersect, otherwise false. bool TestSegmentAABB(const Vector3& p0, const Vector3& p1, const Aabb& aabb); //! Ray sphere intersection result types. @@ -154,42 +135,36 @@ namespace AZ ISECT_RAY_SPHERE_ISECT, // along the PQ segment }; - /** - * IntersectRaySphereOrigin - * return time t>=0 but not limited, so if you check a segment make sure - * t <= segmentLen - * @param rayStart ray start point - * @param rayDirNormalized ray direction normalized. - * @param shereRadius sphere radius - * @param time of closest intersection [0,+INF] in relation to the normalized direction. - * @return \ref SphereIsectTypes - **/ + //! IntersectRaySphereOrigin + //! return time t>=0 but not limited, so if you check a segment make sure + //! t <= segmentLen + //! @param rayStart ray start point + //! @param rayDirNormalized ray direction normalized. + //! @param shereRadius sphere radius + //! @param time of closest intersection [0,+INF] in relation to the normalized direction. + //! @return \ref SphereIsectTypes SphereIsectTypes IntersectRaySphereOrigin( const Vector3& rayStart, const Vector3& rayDirNormalized, const float sphereRadius, float& t); - /** - * Intersect ray (rayStart,rayDirNormalized) and sphere (sphereCenter,sphereRadius) \ref IntersectRaySphereOrigin - * - * @param rayStart - * @param rayDirNormalized - * @param sphereCenter - * @param sphereRadius - * @param t - * @return int - */ + //! Intersect ray (rayStart,rayDirNormalized) and sphere (sphereCenter,sphereRadius) \ref IntersectRaySphereOrigin + //! + //! @param rayStart + //! @param rayDirNormalized + //! @param sphereCenter + //! @param sphereRadius + //! @param t + //! @return SphereIsectTypes SphereIsectTypes IntersectRaySphere( const Vector3& rayStart, const Vector3& rayDirNormalized, const Vector3& sphereCenter, const float sphereRadius, float& t); - /** - * @param rayOrigin The origin of the ray to test. - * @param rayDir The direction of the ray to test. It has to be unit length. - * @param diskCenter Center point of the disk - * @param diskRadius Radius of the disk - * @param diskNormal A normal perpendicular to the disk - * @param[out] t If returning 1 (indicating a hit), this contains distance from rayOrigin along the normalized rayDir - * that the hit occured at. - * @return The number of intersecting points. - **/ + //! @param rayOrigin The origin of the ray to test. + //! @param rayDir The direction of the ray to test. It has to be unit length. + //! @param diskCenter Center point of the disk + //! @param diskRadius Radius of the disk + //! @param diskNormal A normal perpendicular to the disk + //! @param[out] t If returning 1 (indicating a hit), this contains distance from rayOrigin along the normalized rayDir + //! that the hit occured at. + //! @return The number of intersecting points. int IntersectRayDisk( const Vector3& rayOrigin, const Vector3& rayDir, @@ -198,20 +173,18 @@ namespace AZ const AZ::Vector3& diskNormal, float& t); - /** - * If there is only one intersecting point, the coefficient is stored in \ref t1. - * @param rayOrigin The origin of the ray to test. - * @param rayDir The direction of the ray to test. It has to be unit length. - * @param cylinderEnd1 The center of the circle on one end of the cylinder. - * @param cylinderDir The direction pointing from \ref cylinderEnd1 to the other end of the cylinder. It has to be unit - * length. - * @param cylinderHeight The distance between two centers of the circles on two ends of the cylinder respectively. - * @param[out] t1 A possible coefficient in the ray's explicit equation from which an intersecting point is calculated - * as "rayOrigin + t1 * rayDir". - * @param[out] t2 A possible coefficient in the ray's explicit equation from which an intersecting point is calculated - * as "rayOrigin + t2 * rayDir". - * @return The number of intersecting points. - **/ + //! If there is only one intersecting point, the coefficient is stored in \ref t1. + //! @param rayOrigin The origin of the ray to test. + //! @param rayDir The direction of the ray to test. It has to be unit length. + //! @param cylinderEnd1 The center of the circle on one end of the cylinder. + //! @param cylinderDir The direction pointing from \ref cylinderEnd1 to the other end of the cylinder. It has to be unit + //! length. + //! @param cylinderHeight The distance between two centers of the circles on two ends of the cylinder respectively. + //! @param[out] t1 A possible coefficient in the ray's explicit equation from which an intersecting point is calculated + //! as "rayOrigin + t1 * rayDir". + //! @param[out] t2 A possible coefficient in the ray's explicit equation from which an intersecting point is calculated + //! as "rayOrigin + t2 * rayDir". + //! @return The number of intersecting points. int IntersectRayCappedCylinder( const Vector3& rayOrigin, const Vector3& rayDir, @@ -222,20 +195,18 @@ namespace AZ float& t1, float& t2); - /** - * If there is only one intersecting point, the coefficient is stored in \ref t1. - * @param rayOrigin The origin of the ray to test. - * @param rayDir The direction of the ray to test. It has to be unit length. - * @param coneApex The apex of the cone. - * @param coneDir The unit-length direction from the apex to the base. - * @param coneHeight The height of the cone, from the apex to the base. - * @param coneBaseRadius The radius of the cone base circle. - * @param[out] t1 A possible coefficient in the ray's explicit equation from which an intersecting point is calculated - * as "rayOrigin + t1 * rayDir". - * @param[out] t2 A possible coefficient in the ray's explicit equation from which an intersecting point is calculated - * as "rayOrigin + t2 * rayDir". - * @return The number of intersecting points. - **/ + //! If there is only one intersecting point, the coefficient is stored in \ref t1. + //! @param rayOrigin The origin of the ray to test. + //! @param rayDir The direction of the ray to test. It has to be unit length. + //! @param coneApex The apex of the cone. + //! @param coneDir The unit-length direction from the apex to the base. + //! @param coneHeight The height of the cone, from the apex to the base. + //! @param coneBaseRadius The radius of the cone base circle. + //! @param[out] t1 A possible coefficient in the ray's explicit equation from which an intersecting point is calculated + //! as "rayOrigin + t1 * rayDir". + //! @param[out] t2 A possible coefficient in the ray's explicit equation from which an intersecting point is calculated + //! as "rayOrigin + t2 * rayDir". + //! @return The number of intersecting points. int IntersectRayCone( const Vector3& rayOrigin, const Vector3& rayDir, @@ -246,16 +217,13 @@ namespace AZ float& t1, float& t2); - /** - * Test intersection between a ray and a plane in 3D. - * @param rayOrigin The origin of the ray to test intersection with. - * @param rayDir The direction of the ray to test intersection with. - * @param planePos A point on the plane to test intersection with. - * @param planeNormal The normal of the plane to test intersection with. - * @param t[out] The coefficient in the ray's explicit equation from which the intersecting point is calculated as "rayOrigin - *+ t * rayDirection". - * @return The number of intersection point. - **/ + //! Test intersection between a ray and a plane in 3D. + //! @param rayOrigin The origin of the ray to test intersection with. + //! @param rayDir The direction of the ray to test intersection with. + //! @param planePos A point on the plane to test intersection with. + //! @param planeNormal The normal of the plane to test intersection with. + //! @param t[out] The coefficient in the ray's explicit equation from which the intersecting point is calculated as "rayOrigin + t * rayDirection". + //! @return The number of intersection point. int IntersectRayPlane( const Vector3& rayOrigin, const Vector3& rayDir, const Vector3& planePos, const Vector3& planeNormal, float& t); @@ -268,8 +236,7 @@ namespace AZ //! @param vertexB One of the four points that define the quadrilateral. //! @param vertexC One of the four points that define the quadrilateral. //! @param vertexD One of the four points that define the quadrilateral. - //! @param t[out] The coefficient in the ray's explicit equation from which the intersecting point is calculated as "rayOrigin + - //! t * rayDirection". + //! @param t[out] The coefficient in the ray's explicit equation from which the intersecting point is calculated as "rayOrigin + t * rayDirection". //! @return The number of intersection point. int IntersectRayQuad( const Vector3& rayOrigin, @@ -280,20 +247,19 @@ namespace AZ const Vector3& vertexD, float& t); - /** Test intersection between a ray and an oriented box in 3D. - * @param rayOrigin The origin of the ray to test intersection with. - * @param rayDir The direction of the ray to test intersection with. - * @param boxCenter The position of the center of the box. - * @param boxAxis1 An axis along one dimension of the oriented box. - * @param boxAxis2 An axis along one dimension of the oriented box. - * @param boxAxis3 An axis along one dimension of the oriented box. - * @param boxHalfExtent1 The half extent of the box on the dimension of \ref boxAxis1. - * @param boxHalfExtent2 The half extent of the box on the dimension of \ref boxAxis2. - * @param boxHalfExtent3 The half extent of the box on the dimension of \ref boxAxis3. - * @param t[out] The coefficient in the ray's explicit equation from which the intersecting point is calculated as "rayOrigin + - * t * rayDirection". - * @return 1 if there is an intersection, 0 otherwise. - **/ + //! Test intersection between a ray and an oriented box in 3D. + //! + //! @param rayOrigin The origin of the ray to test intersection with. + //! @param rayDir The direction of the ray to test intersection with. + //! @param boxCenter The position of the center of the box. + //! @param boxAxis1 An axis along one dimension of the oriented box. + //! @param boxAxis2 An axis along one dimension of the oriented box. + //! @param boxAxis3 An axis along one dimension of the oriented box. + //! @param boxHalfExtent1 The half extent of the box on the dimension of \ref boxAxis1. + //! @param boxHalfExtent2 The half extent of the box on the dimension of \ref boxAxis2. + //! @param boxHalfExtent3 The half extent of the box on the dimension of \ref boxAxis3. + //! @param t[out] The coefficient in the ray's explicit equation from which the intersecting point is calculated as "rayOrigin + t * rayDirection". + //! @return 1 if there is an intersection, 0 otherwise. int IntersectRayBox( const Vector3& rayOrigin, const Vector3& rayDir, @@ -306,15 +272,14 @@ namespace AZ float boxHalfExtent3, float& t); - /** - * Test intersection between a ray and an OBB. - * @param rayOrigin The origin of the ray to test intersection with. - * @param rayDir The direction of the ray to test intersection with. - * @param obb The OBB to test for intersection with the ray. - * @param t[out] The coefficient in the ray's explicit equation from which the intersecting point is calculated as "rayOrigin + t * - * rayDirection". - * @return 1 if there is an intersection, 0 otherwise. - */ + //! Test intersection between a ray and an OBB. + //! + //! @param rayOrigin The origin of the ray to test intersection with. + //! @param rayDir The direction of the ray to test intersection with. + //! @param obb The OBB to test for intersection with the ray. + //! @param t[out] The coefficient in the ray's explicit equation from which the intersecting point is calculated as "rayOrigin + t * + //! rayDirection". + //! @return 1 if there is an intersection, 0 otherwise. int IntersectRayObb(const Vector3& rayOrigin, const Vector3& rayDir, const Obb& obb, float& t); //! Ray cylinder intersection types. @@ -327,18 +292,16 @@ namespace AZ RR_ISECT_RAY_CYL_Q_SIDE, // on the Q side }; - /** - * Reference: Real-Time Collision Detection - 5.3.7 Intersecting Ray or Segment Against Cylinder - * Intersect segment S(t)=sa+t(dir), 0<=t<=1 against cylinder specified by p, q and r. - * - * @param sa point - * @param dir magnitude along sa - * @param p center point of side 1 cylinder - * @param q center point of side 2 cylinder - * @param r radius of cylinder - * @param t[out] proporition along line semgnet - * @return CylinderIsectTypes - */ + //! Reference: Real-Time Collision Detection - 5.3.7 Intersecting Ray or Segment Against Cylinder + //! Intersect segment S(t)=sa+t(dir), 0<=t<=1 against cylinder specified by p, q and r. + //! + //! @param sa point + //! @param dir magnitude along sa + //! @param p center point of side 1 cylinder + //! @param q center point of side 2 cylinder + //! @param r radius of cylinder + //! @param t[out] proporition along line semgnet + //! @return CylinderIsectTypes CylinderIsectTypes IntersectSegmentCylinder( const Vector3& sa, const Vector3& dir, const Vector3& p, const Vector3& q, const float r, float& t); @@ -352,19 +315,16 @@ namespace AZ ISECT_RAY_CAPSULE_Q_SIDE, // on the Q side }; - /** - * This is a quick implementation of segment capsule based on segment cylinder \ref IntersectSegmentCylinder - * segment sphere intersection. We can optimize it a lot once we fix the ray - * cylinder intersection. - */ + //! This is a quick implementation of segment capsule based on segment cylinder \ref IntersectSegmentCylinder + //! segment sphere intersection. We can optimize it a lot once we fix the ray + //! cylinder intersection. + //! CapsuleIsectTypes IntersectSegmentCapsule( const Vector3& sa, const Vector3& dir, const Vector3& p, const Vector3& q, const float r, float& t); - /** - * Intersect segment S(t)=A+t(B-A), 0<=t<=1 against convex polyhedron specified - * by the n halfspaces defined by the planes p[]. On exit tfirst and tlast - * define the intersection, if any. - */ + //! Intersect segment S(t)=A+t(B-A), 0<=t<=1 against convex polyhedron specified + //! by the n halfspaces defined by the planes p[]. On exit tfirst and tlast + //! define the intersection, if any. bool IntersectSegmentPolyhedron( const Vector3& sa, const Vector3& sBA, @@ -375,22 +335,20 @@ namespace AZ int& iFirstPlane, int& iLastPlane); - /** - * Calculate the line segment closestPointSegment1<->closestPointSegment2 that is the shortest route between - * two segments segment1Start<->segment1End and segment2Start<->segment2End. Also calculate the values of segment1Proportion and - * segment2Proportion where closestPointSegment1 = segment1Start + (segment1Proportion * (segment1End - segment1Start)) - * closestPointSegment2 = segment2Start + (segment2Proportion * (segment2End - segment2Start)) - * If segments are parallel returns a solution. - * @param segment1Start start of segment 1. - * @param segment1End end of segment 1. - * @param segment2Start start of segment 2. - * @param segment2End end of segment 2. - * @param segment1Proportion[out] the proporition along segment 1 [0..1] - * @param segment2Proportion[out] the proporition along segment 2 [0..1] - * @param closestPointSegment1[out] closest point on segment 1. - * @param closestPointSegment2[out] closest point on segment 2. - * @param epsilon the minimum square distance where a line segment can be treated as a single point. - */ + //! Calculate the line segment closestPointSegment1<->closestPointSegment2 that is the shortest route between + //! two segments segment1Start<->segment1End and segment2Start<->segment2End. Also calculate the values of segment1Proportion and + //! segment2Proportion where closestPointSegment1 = segment1Start + (segment1Proportion * (segment1End - segment1Start)) + //! closestPointSegment2 = segment2Start + (segment2Proportion * (segment2End - segment2Start)) + //! If segments are parallel returns a solution. + //! @param segment1Start start of segment 1. + //! @param segment1End end of segment 1. + //! @param segment2Start start of segment 2. + //! @param segment2End end of segment 2. + //! @param segment1Proportion[out] the proporition along segment 1 [0..1] + //! @param segment2Proportion[out] the proporition along segment 2 [0..1] + //! @param closestPointSegment1[out] closest point on segment 1. + //! @param closestPointSegment2[out] closest point on segment 2. + //! @param epsilon the minimum square distance where a line segment can be treated as a single point. void ClosestSegmentSegment( const Vector3& segment1Start, const Vector3& segment1End, @@ -402,19 +360,17 @@ namespace AZ Vector3& closestPointSegment2, float epsilon = 1e-4f); - /** - * Calculate the line segment closestPointSegment1<->closestPointSegment2 that is the shortest route between - * two segments segment1Start<->segment1End and segment2Start<->segment2End. - * If segments are parallel returns a solution. - * - * @param segment1Start start of segment 1. - * @param segment1End end of segment 1. - * @param segment2Start start of segment 2. - * @param segment2End end of segment 2. - * @param closestPointSegment1[out] closest point on segment 1. - * @param closestPointSegment2[out] closest point on segment 2. - * @param epsilon the minimum square distance where a line segment can be treated as a single point. - */ + //! Calculate the line segment closestPointSegment1<->closestPointSegment2 that is the shortest route between + //! two segments segment1Start<->segment1End and segment2Start<->segment2End. + //! If segments are parallel returns a solution. + //! + //! @param segment1Start start of segment 1. + //! @param segment1End end of segment 1. + //! @param segment2Start start of segment 2. + //! @param segment2End end of segment 2. + //! @param closestPointSegment1[out] closest point on segment 1. + //! @param closestPointSegment2[out] closest point on segment 2. + //! @param epsilon the minimum square distance where a line segment can be treated as a single point. void ClosestSegmentSegment( const Vector3& segment1Start, const Vector3& segment1End, @@ -424,17 +380,15 @@ namespace AZ Vector3& closestPointSegment2, float epsilon = 1e-4f); - /** - * Calculate the point (closestPointOnSegment) that is the closest point on - * segment segmentStart/segmentEnd to point. Also calculate the value of proportion where - * closestPointOnSegment = segmentStart + (proportion * (segmentEnd - segmentStart)) - * - * @param point the point to test - * @param segmentStart the start of the segment - * @param segmentEnd the end of the segment - * @param proportion[out] the proportion of the segment L(t) = (end - start) * t - * @param closestPointOnSegment[out] the point along the line segment - */ + //! Calculate the point (closestPointOnSegment) that is the closest point on + //! segment segmentStart/segmentEnd to point. Also calculate the value of proportion where + //! closestPointOnSegment = segmentStart + (proportion * (segmentEnd - segmentStart)) + //! + //! @param point the point to test + //! @param segmentStart the start of the segment + //! @param segmentEnd the end of the segment + //! @param proportion[out] the proportion of the segment L(t) = (end - start) * t + //! @param closestPointOnSegment[out] the point along the line segment void ClosestPointSegment( const Vector3& point, const Vector3& segmentStart, diff --git a/Code/Framework/AzCore/AzCore/Math/IntersectSegment.inl b/Code/Framework/AzCore/AzCore/Math/IntersectSegment.inl index b9f5139923..b570b6a182 100644 --- a/Code/Framework/AzCore/AzCore/Math/IntersectSegment.inl +++ b/Code/Framework/AzCore/AzCore/Math/IntersectSegment.inl @@ -5,7 +5,6 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ -#pragma once namespace AZ { From 45def7440986934ed8cea07db62306506c262ad8 Mon Sep 17 00:00:00 2001 From: sweeneys Date: Mon, 11 Oct 2021 11:11:40 -0700 Subject: [PATCH 09/65] Fix extension ignoring between Windows and Linux Signed-off-by: sweeneys --- .../environment/process_utils.py | 88 ++++++++++++------- Tools/LyTestTools/tests/integ/sanity_tests.py | 4 +- .../tests/unit/test_process_utils.py | 51 +++++++++-- 3 files changed, 101 insertions(+), 42 deletions(-) diff --git a/Tools/LyTestTools/ly_test_tools/environment/process_utils.py b/Tools/LyTestTools/ly_test_tools/environment/process_utils.py index 79fc4f420a..f8245495fa 100755 --- a/Tools/LyTestTools/ly_test_tools/environment/process_utils.py +++ b/Tools/LyTestTools/ly_test_tools/environment/process_utils.py @@ -31,45 +31,59 @@ def kill_processes_named(names, ignore_extensions=False): Kills all processes with a given name :param names: string process name, or list of strings of process name - :param ignore_extensions: ignore trailing file extension + :param ignore_extensions: ignore trailing file extensions. By default 'abc.exe' will not match 'abc'. Note that + enabling this will cause 'abc.exe' to match 'abc', 'abc.bat', and 'abc.sh', though 'abc.GameLauncher.exe' + will not match 'abc.DedicatedServer' """ if not names: return - names = [names] if isinstance(names, str) else names + name_set = set() + if isinstance(names, str): + name_set.add(names) + else: + name_set.update(names) if ignore_extensions: - names = [_remove_extension(name) for name in names] + # both exact matches and extensionless + stripped_names = set() + for name in name_set: + stripped_names.add(_remove_extension(name)) + name_set.update(stripped_names) # remove any blank names, which may empty the list - names = list(filter(lambda x: not x.isspace(), names)) - if not names: + name_set = set(filter(lambda x: not x.isspace(), name_set)) + if not name_set: return - logger.info(f"Killing all processes named {names}") - process_list_to_kill = [] + logger.info(f"Killing all processes named {name_set}") + process_set_to_kill = set() for process in _safe_get_processes(['name', 'pid']): try: proc_name = process.name() except psutil.AccessDenied: - logger.info(f"Process {process} permissions error during kill_processes_named()", exc_info=True) + logger.warning(f"Process {process} permissions error during kill_processes_named()", exc_info=True) continue except psutil.ProcessLookupError: - logger.debug(f"Process {process} could not be killed during kill_processes_named() and was likely already stopped", exc_info=True) + logger.debug(f"Process {process} could not be killed during kill_processes_named() and was likely already " + f"stopped", exc_info=True) continue except psutil.NoSuchProcess: logger.debug(f"Process '{process}' was active when list of processes was requested but it was not found " f"during kill_processes_named()", exc_info=True) continue + if proc_name in name_set: + logger.debug(f"Found process with name {proc_name}.") + process_set_to_kill.add(process) + if ignore_extensions: - proc_name = _remove_extension(proc_name) + extensionless_name = _remove_extension(proc_name) + if extensionless_name in name_set: + process_set_to_kill.add(process) - if proc_name in names: - logger.debug(f"Found process with name {proc_name}. Attempting to kill...") - process_list_to_kill.append(process) - - _safe_kill_process_list(process_list_to_kill) + if process_set_to_kill: + _safe_kill_processes(process_set_to_kill) def kill_processes_started_from(path): @@ -90,7 +104,7 @@ def kill_processes_started_from(path): if process_path.lower().startswith(path.lower()): process_list.append(process) - _safe_kill_process_list(process_list) + _safe_kill_processes(process_list) else: logger.warning(f"Path:'{path}' not found") @@ -118,7 +132,7 @@ def kill_processes_with_name_not_started_from(name, path): logger.info("%s -> %s" % (os.path.dirname(process_path.lower()), path)) proccesses_to_kill.append(process) - _safe_kill_process_list(proccesses_to_kill) + _safe_kill_processes(proccesses_to_kill) else: logger.warning(f"Path:'{path}' not found") @@ -151,10 +165,12 @@ def process_exists(name, ignore_extensions=False): :return: A boolean determining whether the process is alive or not """ name = name.lower() - if ignore_extensions: - name = _remove_extension(name) if name.isspace(): return False + + if ignore_extensions: + name_extensionless = _remove_extension(name) + for process in _safe_get_processes(["name"]): try: proc_name = process.name().lower() @@ -165,10 +181,17 @@ def process_exists(name, ignore_extensions=False): except psutil.AccessDenied as e: logger.info(f"Permissions issue on {process} during process_exists check", exc_info=True) continue - if ignore_extensions: - proc_name = _remove_extension(proc_name) - if proc_name == name: + + if proc_name == name: # abc.exe matches abc.exe return True + if ignore_extensions: + proc_name_extensionless = _remove_extension(proc_name) + if proc_name_extensionless == name: # abc matches abc.exe + return True + if proc_name == name_extensionless: # abc.exe matches abc + return True + # don't check proc_name_extensionless against name_extensionless: abc.exe and abc.exe are already tested, + # however xyz.Gamelauncher should not match xyz.DedicatedServer return False @@ -341,17 +364,14 @@ def _safe_kill_process(proc): except Exception: # purposefully broad logger.warning("Unexpected exception while terminating process", exc_info=True) -def _safe_kill_process_list(proc_list): + +def _safe_kill_processes(processes): """ Kills a given process without raising an error - :param proc_list: The process list to kill + :param processes: An iterable of processes to kill """ - - def on_terminate(proc): - print(f"process '{proc.name()}' with id '{proc.pid}' terminated with exit code {proc.returncode}") - - for proc in proc_list: + for proc in processes: try: logger.info(f"Terminating process '{proc.name()}' with id '{proc.pid}'") proc.kill() @@ -360,12 +380,14 @@ def _safe_kill_process_list(proc_list): except psutil.NoSuchProcess: logger.debug("Termination request ignored, process was already terminated during iteration", exc_info=True) except Exception: # purposefully broad - logger.warning("Unexpected exception while terminating process", exc_info=True) + logger.warning("Unexpected exception ignored while terminating process", exc_info=True) + def on_terminate(proc): + logger.info(f"process '{proc.name()}' with id '{proc.pid}' terminated with exit code {proc.returncode}") try: - psutil.wait_procs(proc_list, timeout=30, callback=on_terminate) + psutil.wait_procs(processes, timeout=30, callback=on_terminate) except Exception: # purposefully broad - logger.warning("Unexpected exception while waiting for process to terminate", exc_info=True) + logger.warning("Unexpected exception while waiting for processes to terminate", exc_info=True) def _terminate_and_confirm_dead(proc): @@ -383,7 +405,7 @@ def _terminate_and_confirm_dead(proc): def _remove_extension(filename): """ - Returns a file name without its extension + Returns a file name without its extension, if any is present :param filename: The name of a file :return: The name of the file without the extension diff --git a/Tools/LyTestTools/tests/integ/sanity_tests.py b/Tools/LyTestTools/tests/integ/sanity_tests.py index ed77d7b8db..2e46d822c7 100755 --- a/Tools/LyTestTools/tests/integ/sanity_tests.py +++ b/Tools/LyTestTools/tests/integ/sanity_tests.py @@ -58,7 +58,7 @@ class TestAutomatedTestingProject(object): # Call the game client executable with launcher.start(): # Wait for the process to exist - waiter.wait_for(lambda: process_utils.process_exists(f"{project}.GameLauncher", ignore_extensions=True)) + waiter.wait_for(lambda: process_utils.process_exists(f"{project}.GameLauncher.exe", ignore_extensions=True)) finally: # Clean up processes after the test is finished process_utils.kill_processes_named(names=process_utils.LY_PROCESS_KILL_LIST, ignore_extensions=True) @@ -85,7 +85,7 @@ class TestAutomatedTestingProject(object): # Call the Editor executable with editor.start(): # Wait for the process to exist - waiter.wait_for(lambda: process_utils.process_exists("Editor", ignore_extensions=True)) + waiter.wait_for(lambda: process_utils.process_exists("Editor.exe", ignore_extensions=True)) finally: # Clean up processes after the test is finished process_utils.kill_processes_named(names=process_utils.LY_PROCESS_KILL_LIST, ignore_extensions=True) diff --git a/Tools/LyTestTools/tests/unit/test_process_utils.py b/Tools/LyTestTools/tests/unit/test_process_utils.py index de40fb8e34..bd6a79fb09 100755 --- a/Tools/LyTestTools/tests/unit/test_process_utils.py +++ b/Tools/LyTestTools/tests/unit/test_process_utils.py @@ -223,7 +223,7 @@ class TestCloseWindowsProcess(unittest.TestCase): mock_enum.assert_called_once() -class Test(unittest.TestCase): +class TestProcessMatching(unittest.TestCase): @mock.patch("ly_test_tools.environment.process_utils._safe_get_processes") def test_ProcExists_HasExtension_Found(self, mock_get_proc): @@ -261,18 +261,55 @@ class Test(unittest.TestCase): self.assertTrue(result) proc_mock.name.assert_called() - @mock.patch('ly_test_tools.environment.process_utils._safe_kill_process', mock.MagicMock) + @mock.patch('ly_test_tools.environment.process_utils._safe_kill_processes') @mock.patch('ly_test_tools.environment.process_utils._safe_get_processes') - def test_KillProcNamed_MockKill_SilentSuccess(self, mock_get_proc): + def test_KillProcNamed_ExactMatch_Killed(self, mock_get_proc, mock_kill_proc): + name = "dummy.exe" + proc_mock = mock.MagicMock() + proc_mock.name.return_value = name + mock_get_proc.return_value = [proc_mock] + + process_utils.kill_processes_named("dummy.exe", ignore_extensions=False) + mock_kill_proc.assert_called() + proc_mock.name.assert_called() + + @mock.patch('ly_test_tools.environment.process_utils._safe_kill_processes') + @mock.patch('ly_test_tools.environment.process_utils._safe_get_processes') + def test_KillProcNamed_NearMatch_Ignore(self, mock_get_proc, mock_kill_proc): + name = "dummy.exe" + proc_mock = mock.MagicMock() + proc_mock.name.return_value = name + mock_get_proc.return_value = [proc_mock] + + process_utils.kill_processes_named("dummy", ignore_extensions=False) + mock_kill_proc.assert_not_called() + proc_mock.name.assert_called() + + @mock.patch('ly_test_tools.environment.process_utils._safe_kill_processes') + @mock.patch('ly_test_tools.environment.process_utils._safe_get_processes') + def test_KillProcNamed_NearMatchIgnoreExtension_Kill(self, mock_get_proc, mock_kill_proc): name = "dummy.exe" proc_mock = mock.MagicMock() proc_mock.name.return_value = name mock_get_proc.return_value = [proc_mock] process_utils.kill_processes_named("dummy", ignore_extensions=True) + mock_kill_proc.assert_called() proc_mock.name.assert_called() - @mock.patch('ly_test_tools.environment.process_utils._safe_kill_process', mock.MagicMock) + @mock.patch('ly_test_tools.environment.process_utils._safe_kill_processes') + @mock.patch('ly_test_tools.environment.process_utils._safe_get_processes') + def test_KillProcNamed_ExactMatchIgnoreExtension_Killed(self, mock_get_proc, mock_kill_proc): + name = "dummy.exe" + proc_mock = mock.MagicMock() + proc_mock.name.return_value = name + mock_get_proc.return_value = [proc_mock] + + process_utils.kill_processes_named("dummy.exe", ignore_extensions=True) + mock_kill_proc.assert_called() + proc_mock.name.assert_called() + + @mock.patch('ly_test_tools.environment.process_utils._safe_kill_processes', mock.MagicMock) @mock.patch('ly_test_tools.environment.process_utils._safe_get_processes') @mock.patch('os.path.exists') def test_KillProcFrom_MockKill_SilentSuccess(self, mock_path, mock_get_proc): @@ -293,7 +330,7 @@ class Test(unittest.TestCase): mock_kill.assert_called() - @mock.patch('ly_test_tools.environment.process_utils._safe_kill_process', mock.MagicMock) + @mock.patch('ly_test_tools.environment.process_utils._safe_kill_processes', mock.MagicMock) @mock.patch('psutil.Process') def test_KillProcPid_NoProc_SilentPass(self, mock_psutil): mock_proc = mock.MagicMock() @@ -302,7 +339,7 @@ class Test(unittest.TestCase): process_utils.kill_process_with_pid(1) - @mock.patch('ly_test_tools.environment.process_utils._safe_kill_process', mock.MagicMock) + @mock.patch('ly_test_tools.environment.process_utils._safe_kill_processes', mock.MagicMock) @mock.patch('psutil.Process') def test_KillProcPidRaiseOnMissing_NoProc_Raises(self, mock_psutil): mock_proc = mock.MagicMock() @@ -339,7 +376,7 @@ class Test(unittest.TestCase): mock_wait_procs.side_effect = psutil.PermissionError() proc_mock = mock.MagicMock() - process_utils._safe_kill_process_list(proc_mock) + process_utils._safe_kill_processes(proc_mock) mock_wait_procs.assert_called() mock_log_warn.assert_called() From 7af448c9b72aed8402e92822e403be57ddca4b0a Mon Sep 17 00:00:00 2001 From: Mikhail Naumov Date: Mon, 11 Oct 2021 20:15:56 -0500 Subject: [PATCH 10/65] PR feedback Signed-off-by: Mikhail Naumov --- .../AzFramework/Spawnable/SpawnableSystemComponent.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableSystemComponent.cpp b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableSystemComponent.cpp index af41fdd6ba..957786c6df 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableSystemComponent.cpp +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableSystemComponent.cpp @@ -166,6 +166,8 @@ namespace AzFramework void SpawnableSystemComponent::Deactivate() { + ProcessSpawnableQueue(); + m_registryChangeHandler.Disconnect(); AZ::TickBus::Handler::BusDisconnect(); From 02d8596d875614316a720820e12db38e02661cfe Mon Sep 17 00:00:00 2001 From: Michael Pollind Date: Mon, 11 Oct 2021 20:24:41 -0700 Subject: [PATCH 11/65] chore: improject documentation for IntersectSegment - change return of IntersectRayDisk to bool - change return of IntersectRayBox to bool - move [out] after @param Signed-off-by: Michael Pollind --- .../AzCore/AzCore/Math/IntersectSegment.cpp | 31 ++- .../AzCore/AzCore/Math/IntersectSegment.h | 217 +++++++++--------- 2 files changed, 124 insertions(+), 124 deletions(-) diff --git a/Code/Framework/AzCore/AzCore/Math/IntersectSegment.cpp b/Code/Framework/AzCore/AzCore/Math/IntersectSegment.cpp index 2a00412689..1bf1e41cb7 100644 --- a/Code/Framework/AzCore/AzCore/Math/IntersectSegment.cpp +++ b/Code/Framework/AzCore/AzCore/Math/IntersectSegment.cpp @@ -352,9 +352,6 @@ AZ::Intersect::IntersectRayAABB( return ISECT_RAY_AABB_ISECT; } - - - //========================================================================= // IntersectRayAABB2 // [2/18/2011] @@ -411,7 +408,7 @@ AZ::Intersect::IntersectRayAABB2(const Vector3& rayStart, const Vector3& dirRCP, return ISECT_RAY_AABB_ISECT; } -int AZ::Intersect::IntersectRayDisk( +bool AZ::Intersect::IntersectRayDisk( const Vector3& rayOrigin, const Vector3& rayDir, const Vector3& diskCenter, const float diskRadius, const Vector3& diskNormal, float& t) { // First intersect with the plane of the disk @@ -424,10 +421,10 @@ int AZ::Intersect::IntersectRayDisk( if (pointOnPlane.GetDistance(diskCenter) < diskRadius) { t = planeIntersectionDistance; - return 1; + return true; } } - return 0; + return false; } // Reference: Real-Time Collision Detection - 5.3.7 Intersecting Ray or Segment Against Cylinder, and the book's errata. @@ -1015,7 +1012,7 @@ int AZ::Intersect::IntersectRayQuad( } // reference: Real-Time Collision Detection, 5.3.3 Intersecting Ray or Segment Against Box -int AZ::Intersect::IntersectRayBox( +bool AZ::Intersect::IntersectRayBox( const Vector3& rayOrigin, const Vector3& rayDir, const Vector3& boxCenter, const Vector3& boxAxis1, const Vector3& boxAxis2, const Vector3& boxAxis3, float boxHalfExtent1, float boxHalfExtent2, float boxHalfExtent3, float& t) { @@ -1047,7 +1044,7 @@ int AZ::Intersect::IntersectRayBox( // If the ray is parallel to the slab and the ray origin is outside, return no intersection. if (tp < 0.0f || tn < 0.0f) { - return 0; + return false; } } else @@ -1068,7 +1065,7 @@ int AZ::Intersect::IntersectRayBox( tmax = AZ::GetMin(tmax, t2); if (tmin > tmax) { - return 0; + return false; } } @@ -1088,7 +1085,7 @@ int AZ::Intersect::IntersectRayBox( // If the ray is parallel to the slab and the ray origin is outside, return no intersection. if (tp < 0.0f || tn < 0.0f) { - return 0; + return false; } } else @@ -1109,7 +1106,7 @@ int AZ::Intersect::IntersectRayBox( tmax = AZ::GetMin(tmax, t2); if (tmin > tmax) { - return 0; + return false; } } @@ -1129,7 +1126,7 @@ int AZ::Intersect::IntersectRayBox( // If the ray is parallel to the slab and the ray origin is outside, return no intersection. if (tp < 0.0f || tn < 0.0f) { - return 0; + return false; } } else @@ -1150,15 +1147,15 @@ int AZ::Intersect::IntersectRayBox( tmax = AZ::GetMin(tmax, t2); if (tmin > tmax) { - return 0; + return false; } } t = (isRayOriginInsideBox ? tmax : tmin); - return 1; + return true; } -int AZ::Intersect::IntersectRayObb(const Vector3& rayOrigin, const Vector3& rayDir, const Obb& obb, float& t) +bool AZ::Intersect::IntersectRayObb(const Vector3& rayOrigin, const Vector3& rayDir, const Obb& obb, float& t) { return AZ::Intersect::IntersectRayBox(rayOrigin, rayDir, obb.GetPosition(), obb.GetAxisX(), obb.GetAxisY(), obb.GetAxisZ(), @@ -1366,11 +1363,11 @@ AZ::Intersect::IntersectSegmentCapsule(const Vector3& sa, const Vector3& dir, co //========================================================================= bool AZ::Intersect::IntersectSegmentPolyhedron( - const Vector3& sa, const Vector3& sBA, const Plane p[], int numPlanes, + const Vector3& sa, const Vector3& dir, const Plane p[], int numPlanes, float& tfirst, float& tlast, int& iFirstPlane, int& iLastPlane) { // Compute direction vector for the segment - Vector3 d = /*b - a*/ sBA; + Vector3 d = /*b - a*/ dir; // Set initial interval to being the whole segment. For a ray, tlast should be // set to +RR_FLT_MAX. For a line, additionally tfirst should be set to -RR_FLT_MAX tfirst = 0.0f; diff --git a/Code/Framework/AzCore/AzCore/Math/IntersectSegment.h b/Code/Framework/AzCore/AzCore/Math/IntersectSegment.h index ecb0d7acc9..71c39fb53d 100644 --- a/Code/Framework/AzCore/AzCore/Math/IntersectSegment.h +++ b/Code/Framework/AzCore/AzCore/Math/IntersectSegment.h @@ -28,15 +28,14 @@ namespace AZ //! LineToPointDistance computes the closest point to 'p' from a segment (s1,s2). //! @param s1 segment start point //! @param s2 segment end point - //! @param p point to find the closest time to. - //! @param u time (on the segment) for the shortest distance from 'p' to (s1,s2) [0.0f (s1),1.0f (s2)] + //! @param p point to find the closest time to. + //! @param u time (on the segment) for the shortest distance from 'p' to (s1,s2) [0.0f (s1),1.0f (s2)] //! @return the closest point Vector3 LineToPointDistance(const Vector3& s1, const Vector3& s2, const Vector3& p, float& u); //! Given segment pq and triangle abc (CCW), returns whether segment intersects //! triangle and if so, also returns the barycentric coordinates (u,v,w) //! of the intersection point. - //! //! @param p segment start point //! @param q segment end point //! @param a triangle point 1 @@ -49,7 +48,7 @@ namespace AZ const Vector3& p, const Vector3& q, const Vector3& a, const Vector3& b, const Vector3& c, Vector3& normal, float& t); //! Same as \ref IntersectSegmentTriangleCCW without respecting the triangle (a,b,c) vertex order (double sided). - //! //! @param p segment start point + //! @param p segment start point //! @param q segment end point //! @param a triangle point 1 //! @param b triangle point 2 @@ -86,41 +85,38 @@ namespace AZ const Aabb& aabb, float& tStart, float& tEnd, - Vector3& startNormal /*, Vector3& inter*/); + Vector3& startNormal); //! Intersect ray against AABB. - //! //! @param rayStart ray starting point. //! @param dir ray reciprocal direction. //! @param aabb Axis aligned bounding box to intersect against. //! @param start length on ray of the first intersection. //! @param end length of the of the second intersection. - //! @return \ref RayAABBIsectTypes In this faster version than IntersectRayAABB we return only ISECT_RAY_AABB_NONE and ISECT_RAY_AABB_ISECT. You can check yourself for that case. + //! @return \ref RayAABBIsectTypes In this faster version than IntersectRayAABB we return only ISECT_RAY_AABB_NONE and + //! ISECT_RAY_AABB_ISECT. You can check yourself for that case. RayAABBIsectTypes IntersectRayAABB2(const Vector3& rayStart, const Vector3& dirRCP, const Aabb& aabb, float& start, float& end); //! Clip a ray to an aabb. return true if ray was clipped. The ray //! can be inside so don't use the result if the ray intersect the box. - //! //! @param aabb bounds //! @param rayStart the start of the ray //! @param rayEnd the end of the ray - //! @param tClipStart[out] The proportion where the ray enterts the aabb - //! @param tClipEnd[out] The proportion where the ray exits the aabb + //! @param[out] tClipStart The proportion where the ray enterts the aabb + //! @param[out] tClipEnd The proportion where the ray exits the aabb //! @return true ray was clipped else false bool ClipRayWithAabb(const Aabb& aabb, Vector3& rayStart, Vector3& rayEnd, float& tClipStart, float& tClipEnd); //! Test segment and aabb where the segment is defined by midpoint //! midPoint = (p1-p0) * 0.5f and half vector halfVector = p1 - midPoint. //! the aabb is at the origin and defined by half extents only. - //! //! @param midPoint midpoint of a line segment //! @param halfVector half vector of an aabb //! @param aabbExtends the extends of a bounded box - //! @return 1 if the intersect, otherwise 0. + //! @return true if the intersect, otherwise false. bool TestSegmentAABBOrigin(const Vector3& midPoint, const Vector3& halfVector, const Vector3& aabbExtends); //! Test if segment specified by points p0 and p1 intersects AABB. \ref TestSegmentAABBOrigin - //! //! @param p0 point 1 //! @param p1 point 2 //! @param aabb bounded box @@ -130,9 +126,9 @@ namespace AZ //! Ray sphere intersection result types. enum SphereIsectTypes : AZ::s32 { - ISECT_RAY_SPHERE_SA_INSIDE = -1, // the ray starts inside the cylinder - ISECT_RAY_SPHERE_NONE, // no intersection - ISECT_RAY_SPHERE_ISECT, // along the PQ segment + ISECT_RAY_SPHERE_SA_INSIDE = -1, //!< the ray starts inside the cylinder + ISECT_RAY_SPHERE_NONE, //!< no intersection + ISECT_RAY_SPHERE_ISECT, //!< along the PQ segment }; //! IntersectRaySphereOrigin @@ -147,25 +143,26 @@ namespace AZ const Vector3& rayStart, const Vector3& rayDirNormalized, const float sphereRadius, float& t); //! Intersect ray (rayStart,rayDirNormalized) and sphere (sphereCenter,sphereRadius) \ref IntersectRaySphereOrigin - //! - //! @param rayStart - //! @param rayDirNormalized - //! @param sphereCenter - //! @param sphereRadius - //! @param t - //! @return SphereIsectTypes + //! @param rayStart the start of the ray + //! @param rayDirNormalized the direction of the ray normalized + //! @param sphereCenter the center of the sphere + //! @param sphereRadius radius of the sphere + //! @param[out] t coefficient in the ray's explicit equation from which an + //! intersecting point is calculated as "rayOrigin + t1 * rayDir". + //! @return SphereIsectTypes SphereIsectTypes IntersectRaySphere( const Vector3& rayStart, const Vector3& rayDirNormalized, const Vector3& sphereCenter, const float sphereRadius, float& t); - //! @param rayOrigin The origin of the ray to test. - //! @param rayDir The direction of the ray to test. It has to be unit length. - //! @param diskCenter Center point of the disk - //! @param diskRadius Radius of the disk - //! @param diskNormal A normal perpendicular to the disk - //! @param[out] t If returning 1 (indicating a hit), this contains distance from rayOrigin along the normalized rayDir + //! Intersect ray (rayStarty, rayDirNormalized) and disk (center, radius, normal) + //! @param rayOrigin The origin of the ray to test. + //! @param rayDir The direction of the ray to test. It has to be unit length. + //! @param diskCenter Center point of the disk + //! @param diskRadius Radius of the disk + //! @param diskNormal A normal perpendicular to the disk + //! @param[out] t If returning 1 (indicating a hit), this contains distance from rayOrigin along the normalized rayDir //! that the hit occured at. - //! @return The number of intersecting points. - int IntersectRayDisk( + //! @return false if not interesecting and true if intersecting + bool IntersectRayDisk( const Vector3& rayOrigin, const Vector3& rayDir, const Vector3& diskCenter, @@ -174,17 +171,14 @@ namespace AZ float& t); //! If there is only one intersecting point, the coefficient is stored in \ref t1. - //! @param rayOrigin The origin of the ray to test. - //! @param rayDir The direction of the ray to test. It has to be unit length. - //! @param cylinderEnd1 The center of the circle on one end of the cylinder. - //! @param cylinderDir The direction pointing from \ref cylinderEnd1 to the other end of the cylinder. It has to be unit - //! length. - //! @param cylinderHeight The distance between two centers of the circles on two ends of the cylinder respectively. - //! @param[out] t1 A possible coefficient in the ray's explicit equation from which an intersecting point is calculated - //! as "rayOrigin + t1 * rayDir". - //! @param[out] t2 A possible coefficient in the ray's explicit equation from which an intersecting point is calculated - //! as "rayOrigin + t2 * rayDir". - //! @return The number of intersecting points. + //! @param rayOrigin The origin of the ray to test. + //! @param rayDir The direction of the ray to test. It has to be unit length. + //! @param cylinderEnd1 The center of the circle on one end of the cylinder. + //! @param cylinderDir The direction pointing from \ref cylinderEnd1 to the other end of the cylinder. It has to be unit length. + //! @param cylinderHeight The distance between two centers of the circles on two ends of the cylinder respectively. + //! @param[out] t1 A possible coefficient in the ray's explicit equation from which an intersecting point is calculated as "rayOrigin + t1 * rayDir". + //! @param[out] t2 A possible coefficient in the ray's explicit equation from which an intersecting point is calculated as "rayOrigin + t2 * rayDir". + //! @return The number of intersecting points. int IntersectRayCappedCylinder( const Vector3& rayOrigin, const Vector3& rayDir, @@ -196,17 +190,15 @@ namespace AZ float& t2); //! If there is only one intersecting point, the coefficient is stored in \ref t1. - //! @param rayOrigin The origin of the ray to test. - //! @param rayDir The direction of the ray to test. It has to be unit length. - //! @param coneApex The apex of the cone. - //! @param coneDir The unit-length direction from the apex to the base. - //! @param coneHeight The height of the cone, from the apex to the base. - //! @param coneBaseRadius The radius of the cone base circle. - //! @param[out] t1 A possible coefficient in the ray's explicit equation from which an intersecting point is calculated - //! as "rayOrigin + t1 * rayDir". - //! @param[out] t2 A possible coefficient in the ray's explicit equation from which an intersecting point is calculated - //! as "rayOrigin + t2 * rayDir". - //! @return The number of intersecting points. + //! @param rayOrigin The origin of the ray to test. + //! @param rayDir The direction of the ray to test. It has to be unit length. + //! @param coneApex The apex of the cone. + //! @param coneDir The unit-length direction from the apex to the base. + //! @param coneHeight The height of the cone, from the apex to the base. + //! @param coneBaseRadius The radius of the cone base circle. + //! @param[out] t1 A possible coefficient in the ray's explicit equation from which an intersecting point is calculated as "rayOrigin + t1 * rayDir". + //! @param[out] t2 A possible coefficient in the ray's explicit equation from which an intersecting point is calculated as "rayOrigin + t2 * rayDir". + //! @return The number of intersecting points. int IntersectRayCone( const Vector3& rayOrigin, const Vector3& rayDir, @@ -218,11 +210,11 @@ namespace AZ float& t2); //! Test intersection between a ray and a plane in 3D. - //! @param rayOrigin The origin of the ray to test intersection with. - //! @param rayDir The direction of the ray to test intersection with. - //! @param planePos A point on the plane to test intersection with. - //! @param planeNormal The normal of the plane to test intersection with. - //! @param t[out] The coefficient in the ray's explicit equation from which the intersecting point is calculated as "rayOrigin + t * rayDirection". + //! @param rayOrigin The origin of the ray to test intersection with. + //! @param rayDir The direction of the ray to test intersection with. + //! @param planePos A point on the plane to test intersection with. + //! @param planeNormal The normal of the plane to test intersection with. + //! @param[out] t The coefficient in the ray's explicit equation from which the intersecting point is calculated as "rayOrigin + t * rayDirection". //! @return The number of intersection point. int IntersectRayPlane( const Vector3& rayOrigin, const Vector3& rayDir, const Vector3& planePos, const Vector3& planeNormal, float& t); @@ -230,13 +222,14 @@ namespace AZ //! Test intersection between a ray and a two-sided quadrilateral defined by four points in 3D. //! The four points that define the quadrilateral could be passed in with either counter clock-wise //! winding or clock-wise winding. - //! @param rayOrigin The origin of the ray to test intersection with. - //! @param rayDir The direction of the ray to test intersection with. - //! @param vertexA One of the four points that define the quadrilateral. - //! @param vertexB One of the four points that define the quadrilateral. - //! @param vertexC One of the four points that define the quadrilateral. - //! @param vertexD One of the four points that define the quadrilateral. - //! @param t[out] The coefficient in the ray's explicit equation from which the intersecting point is calculated as "rayOrigin + t * rayDirection". + //! @param rayOrigin The origin of the ray to test intersection with. + //! @param rayDir The direction of the ray to test intersection with. + //! @param vertexA One of the four points that define the quadrilateral. + //! @param vertexB One of the four points that define the quadrilateral. + //! @param vertexC One of the four points that define the quadrilateral. + //! @param vertexD One of the four points that define the quadrilateral. + //! @param[out] t The coefficient in the ray's explicit equation from which the + //! intersecting point is calculated as "rayOrigin + t * rayDirection". //! @return The number of intersection point. int IntersectRayQuad( const Vector3& rayOrigin, @@ -248,19 +241,18 @@ namespace AZ float& t); //! Test intersection between a ray and an oriented box in 3D. - //! - //! @param rayOrigin The origin of the ray to test intersection with. - //! @param rayDir The direction of the ray to test intersection with. - //! @param boxCenter The position of the center of the box. - //! @param boxAxis1 An axis along one dimension of the oriented box. - //! @param boxAxis2 An axis along one dimension of the oriented box. - //! @param boxAxis3 An axis along one dimension of the oriented box. - //! @param boxHalfExtent1 The half extent of the box on the dimension of \ref boxAxis1. - //! @param boxHalfExtent2 The half extent of the box on the dimension of \ref boxAxis2. - //! @param boxHalfExtent3 The half extent of the box on the dimension of \ref boxAxis3. - //! @param t[out] The coefficient in the ray's explicit equation from which the intersecting point is calculated as "rayOrigin + t * rayDirection". - //! @return 1 if there is an intersection, 0 otherwise. - int IntersectRayBox( + //! @param rayOrigin The origin of the ray to test intersection with. + //! @param rayDir The direction of the ray to test intersection with. + //! @param boxCenter The position of the center of the box. + //! @param boxAxis1 An axis along one dimension of the oriented box. + //! @param boxAxis2 An axis along one dimension of the oriented box. + //! @param boxAxis3 An axis along one dimension of the oriented box. + //! @param boxHalfExtent1 The half extent of the box on the dimension of \ref boxAxis1. + //! @param boxHalfExtent2 The half extent of the box on the dimension of \ref boxAxis2. + //! @param boxHalfExtent3 The half extent of the box on the dimension of \ref boxAxis3. + //! @param[out] t The coefficient in the ray's explicit equation from which the intersecting point is calculated as "rayOrigin + t * rayDirection". + //! @return true if there is an intersection, false otherwise. + bool IntersectRayBox( const Vector3& rayOrigin, const Vector3& rayDir, const Vector3& boxCenter, @@ -273,23 +265,21 @@ namespace AZ float& t); //! Test intersection between a ray and an OBB. - //! //! @param rayOrigin The origin of the ray to test intersection with. //! @param rayDir The direction of the ray to test intersection with. //! @param obb The OBB to test for intersection with the ray. - //! @param t[out] The coefficient in the ray's explicit equation from which the intersecting point is calculated as "rayOrigin + t * - //! rayDirection". - //! @return 1 if there is an intersection, 0 otherwise. - int IntersectRayObb(const Vector3& rayOrigin, const Vector3& rayDir, const Obb& obb, float& t); + //! @param[out] t The coefficient in the ray's explicit equation from which the intersecting point is calculated as "rayOrigin + t * rayDirection". + //! @return true if there is an intersection, false otherwise. + bool IntersectRayObb(const Vector3& rayOrigin, const Vector3& rayDir, const Obb& obb, float& t); //! Ray cylinder intersection types. enum CylinderIsectTypes : AZ::s32 { - RR_ISECT_RAY_CYL_SA_INSIDE = -1, // the ray starts inside the cylinder - RR_ISECT_RAY_CYL_NONE, // no intersection - RR_ISECT_RAY_CYL_PQ, // along the PQ segment - RR_ISECT_RAY_CYL_P_SIDE, // on the P side - RR_ISECT_RAY_CYL_Q_SIDE, // on the Q side + RR_ISECT_RAY_CYL_SA_INSIDE = -1, //!< the ray starts inside the cylinder + RR_ISECT_RAY_CYL_NONE, //!< no intersection + RR_ISECT_RAY_CYL_PQ, //!< along the PQ segment + RR_ISECT_RAY_CYL_P_SIDE, //!< on the P side + RR_ISECT_RAY_CYL_Q_SIDE, //!< on the Q side }; //! Reference: Real-Time Collision Detection - 5.3.7 Intersecting Ray or Segment Against Cylinder @@ -300,7 +290,7 @@ namespace AZ //! @param p center point of side 1 cylinder //! @param q center point of side 2 cylinder //! @param r radius of cylinder - //! @param t[out] proporition along line semgnet + //! @param[out] t proporition along line segment //! @return CylinderIsectTypes CylinderIsectTypes IntersectSegmentCylinder( const Vector3& sa, const Vector3& dir, const Vector3& p, const Vector3& q, const float r, float& t); @@ -308,26 +298,41 @@ namespace AZ //! Capsule ray intersect types. enum CapsuleIsectTypes { - ISECT_RAY_CAPSULE_SA_INSIDE = -1, // the ray starts inside the cylinder - ISECT_RAY_CAPSULE_NONE, // no intersection - ISECT_RAY_CAPSULE_PQ, // along the PQ segment - ISECT_RAY_CAPSULE_P_SIDE, // on the P side - ISECT_RAY_CAPSULE_Q_SIDE, // on the Q side + ISECT_RAY_CAPSULE_SA_INSIDE = -1, //!< the ray starts inside the cylinder + ISECT_RAY_CAPSULE_NONE, //!< no intersection + ISECT_RAY_CAPSULE_PQ, //!< along the PQ segment + ISECT_RAY_CAPSULE_P_SIDE, //!< on the P side + ISECT_RAY_CAPSULE_Q_SIDE, //!< on the Q side }; //! This is a quick implementation of segment capsule based on segment cylinder \ref IntersectSegmentCylinder //! segment sphere intersection. We can optimize it a lot once we fix the ray //! cylinder intersection. - //! + //! @param sa the beginning of the line segment + //! @param dir the direction and length of the segment + //! @param p center point of side 1 capsule + //! @param q center point of side 1 capsule + //! @param r the radius of the capsule + //! @param[out] t proporition along line segment + //! @return CapsuleIsectTypes CapsuleIsectTypes IntersectSegmentCapsule( const Vector3& sa, const Vector3& dir, const Vector3& p, const Vector3& q, const float r, float& t); //! Intersect segment S(t)=A+t(B-A), 0<=t<=1 against convex polyhedron specified //! by the n halfspaces defined by the planes p[]. On exit tfirst and tlast //! define the intersection, if any. + //! @param sa the beggining of the line segment + //! @param dir the direction and length of the segment + //! @param p planes that compose a convex ponvex polyhedron + //! @param numPlanes number of planes + //! @param[out] tfirst proportion along the line segment where the line enters + //! @param[out] tlast proportion along the line segment where the line exits + //! @param[out] iFirstPlane the plane where the line enters + //! @param[out] iLastPlane the plane where the line exits + //! @return true if intersects else false bool IntersectSegmentPolyhedron( const Vector3& sa, - const Vector3& sBA, + const Vector3& dir, const Plane p[], int numPlanes, float& tfirst, @@ -335,7 +340,7 @@ namespace AZ int& iFirstPlane, int& iLastPlane); - //! Calculate the line segment closestPointSegment1<->closestPointSegment2 that is the shortest route between + //! Calculate the line segment closestPointSegment1<->closestPointSegment2 that is the shortest route between //! two segments segment1Start<->segment1End and segment2Start<->segment2End. Also calculate the values of segment1Proportion and //! segment2Proportion where closestPointSegment1 = segment1Start + (segment1Proportion * (segment1End - segment1Start)) //! closestPointSegment2 = segment2Start + (segment2Proportion * (segment2End - segment2Start)) @@ -344,10 +349,10 @@ namespace AZ //! @param segment1End end of segment 1. //! @param segment2Start start of segment 2. //! @param segment2End end of segment 2. - //! @param segment1Proportion[out] the proporition along segment 1 [0..1] - //! @param segment2Proportion[out] the proporition along segment 2 [0..1] - //! @param closestPointSegment1[out] closest point on segment 1. - //! @param closestPointSegment2[out] closest point on segment 2. + //! @param[out] segment1Proportion the proporition along segment 1 [0..1] + //! @param[out] segment2Proportion the proporition along segment 2 [0..1] + //! @param[out] closestPointSegment1 closest point on segment 1. + //! @param[out] closestPointSegment2 closest point on segment 2. //! @param epsilon the minimum square distance where a line segment can be treated as a single point. void ClosestSegmentSegment( const Vector3& segment1Start, @@ -363,13 +368,12 @@ namespace AZ //! Calculate the line segment closestPointSegment1<->closestPointSegment2 that is the shortest route between //! two segments segment1Start<->segment1End and segment2Start<->segment2End. //! If segments are parallel returns a solution. - //! //! @param segment1Start start of segment 1. //! @param segment1End end of segment 1. //! @param segment2Start start of segment 2. //! @param segment2End end of segment 2. - //! @param closestPointSegment1[out] closest point on segment 1. - //! @param closestPointSegment2[out] closest point on segment 2. + //! @param[out] closestPointSegment1 closest point on segment 1. + //! @param[out] closestPointSegment2 closest point on segment 2. //! @param epsilon the minimum square distance where a line segment can be treated as a single point. void ClosestSegmentSegment( const Vector3& segment1Start, @@ -383,12 +387,11 @@ namespace AZ //! Calculate the point (closestPointOnSegment) that is the closest point on //! segment segmentStart/segmentEnd to point. Also calculate the value of proportion where //! closestPointOnSegment = segmentStart + (proportion * (segmentEnd - segmentStart)) - //! //! @param point the point to test //! @param segmentStart the start of the segment //! @param segmentEnd the end of the segment - //! @param proportion[out] the proportion of the segment L(t) = (end - start) * t - //! @param closestPointOnSegment[out] the point along the line segment + //! @param[out] proportion the proportion of the segment L(t) = (end - start) * t + //! @param[out] closestPointOnSegment the point along the line segment void ClosestPointSegment( const Vector3& point, const Vector3& segmentStart, From 8487373b0cf1fe02210a083a9548cfc8ad4b7b79 Mon Sep 17 00:00:00 2001 From: Guthrie Adams Date: Tue, 12 Oct 2021 14:41:26 -0500 Subject: [PATCH 12/65] Updated thumbnail notification bus to use const QPixmap& Signed-off-by: Guthrie Adams --- .../AzToolsFramework/Thumbnails/ThumbnailerBus.h | 2 +- .../Code/Source/Thumbnail/ImageThumbnail.cpp | 2 +- .../ImageProcessingAtom/Code/Source/Thumbnail/ImageThumbnail.h | 2 +- .../CommonFeatures/Code/Source/Material/MaterialThumbnail.cpp | 2 +- .../CommonFeatures/Code/Source/Material/MaterialThumbnail.h | 2 +- .../CommonFeatures/Code/Source/Mesh/MeshThumbnail.cpp | 2 +- .../CommonFeatures/Code/Source/Mesh/MeshThumbnail.h | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Thumbnails/ThumbnailerBus.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Thumbnails/ThumbnailerBus.h index 6f074da878..acd8ba3966 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Thumbnails/ThumbnailerBus.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Thumbnails/ThumbnailerBus.h @@ -90,7 +90,7 @@ namespace AzToolsFramework typedef SharedThumbnailKey BusIdType; //! notify product thumbnail that the data is ready - virtual void ThumbnailRendered(QPixmap& thumbnailImage) = 0; + virtual void ThumbnailRendered(const QPixmap& thumbnailImage) = 0; //! notify product thumbnail that the thumbnail failed to render virtual void ThumbnailFailedToRender() = 0; }; diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Thumbnail/ImageThumbnail.cpp b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Thumbnail/ImageThumbnail.cpp index 084e38a2db..cdd63dca18 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Thumbnail/ImageThumbnail.cpp +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Thumbnail/ImageThumbnail.cpp @@ -67,7 +67,7 @@ namespace ImageProcessingAtom m_renderWait.acquire(); } - void ImageThumbnail::ThumbnailRendered(QPixmap& thumbnailImage) + void ImageThumbnail::ThumbnailRendered(const QPixmap& thumbnailImage) { m_pixmap = thumbnailImage; m_renderWait.release(); diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Thumbnail/ImageThumbnail.h b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Thumbnail/ImageThumbnail.h index 45fd178285..eadbfca945 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Thumbnail/ImageThumbnail.h +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Thumbnail/ImageThumbnail.h @@ -34,7 +34,7 @@ namespace ImageProcessingAtom ~ImageThumbnail() override; //! AzToolsFramework::ThumbnailerRendererNotificationBus::Handler overrides... - void ThumbnailRendered(QPixmap& thumbnailImage) override; + void ThumbnailRendered(const QPixmap& thumbnailImage) override; void ThumbnailFailedToRender() override; protected: diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/MaterialThumbnail.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/MaterialThumbnail.cpp index 0723859ff5..2931b647e5 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/MaterialThumbnail.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/MaterialThumbnail.cpp @@ -54,7 +54,7 @@ namespace AZ AzFramework::AssetCatalogEventBus::Handler::BusDisconnect(); } - void MaterialThumbnail::ThumbnailRendered(QPixmap& thumbnailImage) + void MaterialThumbnail::ThumbnailRendered(const QPixmap& thumbnailImage) { m_pixmap = thumbnailImage; m_renderWait.release(); diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/MaterialThumbnail.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/MaterialThumbnail.h index dba922a1b2..8f2053ba67 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/MaterialThumbnail.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/MaterialThumbnail.h @@ -36,7 +36,7 @@ namespace AZ ~MaterialThumbnail() override; //! AzToolsFramework::ThumbnailerRendererNotificationBus::Handler overrides... - void ThumbnailRendered(QPixmap& thumbnailImage) override; + void ThumbnailRendered(const QPixmap& thumbnailImage) override; void ThumbnailFailedToRender() override; protected: diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshThumbnail.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshThumbnail.cpp index 658d420a16..845197d939 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshThumbnail.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshThumbnail.cpp @@ -55,7 +55,7 @@ namespace AZ AzFramework::AssetCatalogEventBus::Handler::BusDisconnect(); } - void MeshThumbnail::ThumbnailRendered(QPixmap& thumbnailImage) + void MeshThumbnail::ThumbnailRendered(const QPixmap& thumbnailImage) { m_pixmap = thumbnailImage; m_renderWait.release(); diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshThumbnail.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshThumbnail.h index 2975b6950c..872e1029d2 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshThumbnail.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshThumbnail.h @@ -35,7 +35,7 @@ namespace AZ ~MeshThumbnail() override; //! AzToolsFramework::ThumbnailerRendererNotificationBus::Handler overrides... - void ThumbnailRendered(QPixmap& thumbnailImage) override; + void ThumbnailRendered(const QPixmap& thumbnailImage) override; void ThumbnailFailedToRender() override; protected: From 4c5906a8d46f0f69ed590407396368d47cd0d79b Mon Sep 17 00:00:00 2001 From: Scott Murray Date: Tue, 12 Oct 2021 13:21:55 -0700 Subject: [PATCH 13/65] PostFX Shape Weight Modifier component P0 parallel test Signed-off-by: Scott Murray --- .../Atom/TestSuite_Main_Optimized.py | 4 + ...mponents_PostFxShapeWeightModifierAdded.py | 211 ++++++++++++++++++ 2 files changed, 215 insertions(+) create mode 100644 AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFxShapeWeightModifierAdded.py diff --git a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_Optimized.py b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_Optimized.py index 31b4cb687d..447a4ebac9 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_Optimized.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_Optimized.py @@ -67,5 +67,9 @@ class TestAutomation(EditorTestSuite): class AtomEditorComponents_PostFXLayerAdded(EditorSharedTest): from Atom.tests import hydra_AtomEditorComponents_PostFXLayerAdded as test_module + @pytest.mark.test_case_id("C36525665") + class AtomEditorComponents_PostFXShapeWeightModifierAdded(EditorSharedTest): + from Atom.tests import hydra_AtomEditorComponents_PostFxShapeWeightModifierAdded as test_module + class ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges(EditorSharedTest): from Atom.tests import hydra_ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges as test_module diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFxShapeWeightModifierAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFxShapeWeightModifierAdded.py new file mode 100644 index 0000000000..be139f83b6 --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFxShapeWeightModifierAdded.py @@ -0,0 +1,211 @@ +""" +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: + creation_undo = ( + "UNDO Entity creation success", + "UNDO Entity creation failed") + creation_redo = ( + "REDO Entity creation success", + "REDO Entity creation failed") + postfx_shape_weight_creation = ( + "PostFx Shape Weight Modifier Entity successfully created", + "PostFx Shape Weight Modifier Entity failed to be created") + postfx_shape_weight_component = ( + "Entity has a PostFx Shape Weight Modifier component", + "Entity failed to find PostFx Shape Weight Modifier component") + postfx_shape_weight_disabled = ( + "PostFx Shape Weight Modifier component disabled", + "PostFx Shape Weight Modifier component was not disabled.") + postfx_layer_component = ( + "Entity has an Actor component", + "Entity did not have an Actor component") + shape_component = ( + "Entity has a Tube Shape component", + "Entity did not have a Tube Shape component") + shape_undo = ( + "Entity shape component add undone", + "Entity shape component undo failed to remove shape") + postfx_shape_weight_enabled = ( + "PostFx Shape Weight Modifier component enabled", + "PostFx Shape Weight Modifier component was not enabled.") + enter_game_mode = ( + "Entered game mode", + "Failed to enter game mode") + exit_game_mode = ( + "Exited game mode", + "Couldn't exit game mode") + is_visible = ( + "Entity is visible", + "Entity was not visible") + is_hidden = ( + "Entity is hidden", + "Entity was not hidden") + entity_deleted = ( + "Entity deleted", + "Entity was not deleted") + deletion_undo = ( + "UNDO deletion success", + "UNDO deletion failed") + deletion_redo = ( + "REDO deletion success", + "REDO deletion failed") + + +def AtomEditorComponents_postfx_shape_weight_AddedToEntity(): + """ + Summary: + Tests the PostFx Shape Weight Modifier component can be added to an entity and has the expected functionality. + + Test setup: + - Wait for Editor idle loop. + - Open the "Base" level. + + Expected Behavior: + The component can be added, used in game mode, hidden/shown, deleted, and has accurate required components. + Creation and deletion undo/redo should also work. + + Test Steps: + 1) Create a PostFx Shape Weight Modifier entity with no components. + 2) Add a PostFx Shape Weight Modifier component to PostFx Shape Weight Modifier entity. + 3) UNDO the entity creation and component addition. + 4) REDO the entity creation and component addition. + 5) Verify PostFx Shape Weight Modifier component not enabled. + 6) Add PostFX Layer component since it is required by the PostFx Shape Weight Modifier component. + 7) Verify PostFx Shape Weight Modifier component is NOT enabled since it also requires a shape. + 8) Add a required shape looping over a list and checking if it enables PostFX Shape Weight Modifier. + 9) Undo to remove each added shape and verify PostFX Shape Weight Modifier is not enabled + 10) Verify PostFx Shape Weight Modifier component is enabled by adding Spline and Tube Shape componen. + 11) Enter/Exit game mode. + 12) Test IsHidden. + 13) Test IsVisible. + 14) Delete PostFx Shape Weight Modifier entity. + 15) UNDO deletion. + 16) REDO deletion. + 17) Look for errors. + + :return: None + """ + + import azlmbr.legacy.general as general + + from editor_python_test_tools.editor_entity_utils import EditorEntity + from editor_python_test_tools.utils import Report, Tracer, TestHelper + + with Tracer() as error_tracer: + # Test setup begins. + # Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level. + TestHelper.init_idle() + TestHelper.open_level("", "Base") + + # Test steps begin. + # 1. Create a PostFx Shape Weight Modifier entity with no components. + postfx_shape_weight_name = "PostFX Shape Weight Modifier" + postfx_shape_weight_entity = EditorEntity.create_editor_entity(postfx_shape_weight_name) + Report.critical_result(Tests.postfx_shape_weight_creation, postfx_shape_weight_entity.exists()) + + # 2. Add a PostFx Shape Weight Modifier component to PostFx Shape Weight Modifier entity. + postfx_shape_weight_component = postfx_shape_weight_entity.add_component(postfx_shape_weight_name) + Report.critical_result( + Tests.postfx_shape_weight_component, + postfx_shape_weight_entity.has_component(postfx_shape_weight_name)) + + # 3. UNDO the entity creation and component addition. + # -> UNDO component addition. + general.undo() + # -> UNDO naming entity. + general.undo() + # -> UNDO selecting entity. + general.undo() + # -> UNDO entity creation. + general.undo() + general.idle_wait_frames(1) + Report.result(Tests.creation_undo, not postfx_shape_weight_entity.exists()) + + # 4. REDO the entity creation and component addition. + # -> REDO entity creation. + general.redo() + # -> REDO selecting entity. + general.redo() + # -> REDO naming entity. + general.redo() + # -> REDO component addition. + general.redo() + general.idle_wait_frames(1) + Report.result(Tests.creation_redo, postfx_shape_weight_entity.exists()) + + # 5. Verify PostFx Shape Weight Modifier component not enabled. + Report.result(Tests.postfx_shape_weight_disabled, not postfx_shape_weight_component.is_enabled()) + + # 6. Add PostFX Layer component since it is required by the PostFx Shape Weight Modifier component. + postfx_layer_name = "PostFX Layer" + postfx_shape_weight_entity.add_component(postfx_layer_name) + Report.result(Tests.postfx_layer_component, postfx_shape_weight_entity.has_component(postfx_layer_name)) + + # 7. Verify PostFx Shape Weight Modifier component not enabled because shape is also required. + Report.result(Tests.postfx_shape_weight_disabled, not postfx_shape_weight_component.is_enabled()) + + # 8. Add remove each shape to test if the PostFX Shape Weight Modifier is enabled by having a required shape + for shape in ['Axis Aligned Box Shape', 'Box Shape', 'Capsule Shape', 'Compound Shape', 'Cylinder Shape', + 'Disk Shape', 'Polygon Prism Shape', 'Quad Shape', 'Sphere Shape', 'Vegetation Reference Shape']: + postfx_shape_weight_entity.add_component(shape) + test_shape = ( + f"Entity has a {shape} component", + f"Entity did not have a {shape} component") + Report.result(test_shape, postfx_shape_weight_entity.has_component(shape)) + + #Check if required shape allows PostFX Shape Weight Modifier to be enabled + Report.result(Tests.postfx_shape_weight_enabled, postfx_shape_weight_component.is_enabled()) + + # 9. UNDO component addition and check that PostFX Shape Weight Modifier is not enabled + general.undo() + general.idle_wait_frames(1) + Report.result(Tests.shape_undo, not postfx_shape_weight_entity.has_component(shape)) + Report.result(Tests.postfx_shape_weight_disabled, not postfx_shape_weight_component.is_enabled()) + + # 10. Add Tube Shape and Spline to fulfil the required shape component + postfx_shape_weight_entity.add_components(['Spline', 'Tube Shape']) + Report.result(Tests.shape_component, postfx_shape_weight_entity.has_component('Tube Shape')) + Report.result(Tests.postfx_shape_weight_enabled, postfx_shape_weight_component.is_enabled()) + + # 11. Enter/Exit game mode. + TestHelper.enter_game_mode(Tests.enter_game_mode) + general.idle_wait_frames(1) + TestHelper.exit_game_mode(Tests.exit_game_mode) + + # 12. Test IsHidden. + postfx_shape_weight_entity.set_visibility_state(False) + Report.result(Tests.is_hidden, postfx_shape_weight_entity.is_hidden() is True) + + # 13. Test IsVisible. + postfx_shape_weight_entity.set_visibility_state(True) + general.idle_wait_frames(1) + Report.result(Tests.is_visible, postfx_shape_weight_entity.is_visible() is True) + + # 14. Delete PostFx Shape Weight Modifier entity. + postfx_shape_weight_entity.delete() + Report.result(Tests.entity_deleted, not postfx_shape_weight_entity.exists()) + + # 15. UNDO deletion. + general.undo() + Report.result(Tests.deletion_undo, postfx_shape_weight_entity.exists()) + + # 16. REDO deletion. + general.redo() + Report.result(Tests.deletion_redo, not postfx_shape_weight_entity.exists()) + + # 17. Look for errors or asserts. + TestHelper.wait_for_condition(lambda: error_tracer.has_errors or error_tracer.has_asserts, 1.0) + for error_info in error_tracer.errors: + Report.info(f"Error: {error_info.filename} {error_info.function} | {error_info.message}") + for assert_info in error_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(AtomEditorComponents_postfx_shape_weight_AddedToEntity) From f7e08d1c4bcfc983fc8d5f035b03a81673bf00a6 Mon Sep 17 00:00:00 2001 From: Guthrie Adams Date: Tue, 12 Oct 2021 15:36:19 -0500 Subject: [PATCH 14/65] =?UTF-8?q?PropertyAssetCtrl=20and=20ThumbnailProper?= =?UTF-8?q?tyCtrl=20support=20custom=20thumbnail=20images=20=E2=80=A2=20Pr?= =?UTF-8?q?opertyAssetCtrl=20was=20previously=20extended=20with=20Thumbnai?= =?UTF-8?q?lPropertyCtrl=20to=20optionally=20display=20a=20thumbnail=20and?= =?UTF-8?q?=20floating=20zoomed=20in=20preview=20of=20the=20selected=20ass?= =?UTF-8?q?et.=20=E2=80=A2=20This=20change=20allows=20overriding=20the=20i?= =?UTF-8?q?mage=20that=20comes=20from=20the=20thumbnail=20system=20with=20?= =?UTF-8?q?a=20custom=20image=20provided=20as=20an=20attribute.=20The=20cu?= =?UTF-8?q?stom=20image=20can=20be=20specified=20as=20either=20a=20file=20?= =?UTF-8?q?path=20or=20a=20buffer=20containing=20a=20serialized=20QPixmap.?= =?UTF-8?q?=20=E2=80=A2=20This=20will=20be=20used=20by=20the=20material=20?= =?UTF-8?q?system=20in=20the=20editor=20to=20provide=20a=20dynamically=20r?= =?UTF-8?q?endered=20image=20of=20the=20material=20with=20property=20overr?= =?UTF-8?q?ides=20applied=20so=20that=20the=20image=20will=20update=20as?= =?UTF-8?q?=20the=20user=20customizes=20their=20material.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Guthrie Adams --- .../UI/PropertyEditor/PropertyAssetCtrl.cpp | 57 +++++++- .../UI/PropertyEditor/PropertyAssetCtrl.hxx | 5 + .../PropertyEditor/ThumbnailPropertyCtrl.cpp | 131 +++++++++++------- .../UI/PropertyEditor/ThumbnailPropertyCtrl.h | 25 +++- 4 files changed, 161 insertions(+), 57 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyAssetCtrl.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyAssetCtrl.cpp index ba84e662c1..7f3b9e61fd 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyAssetCtrl.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyAssetCtrl.cpp @@ -28,6 +28,9 @@ AZ_PUSH_DISABLE_WARNING(4244 4251, "-Wunknown-warning-option") #include #include #include +#include +#include +#include AZ_POP_DISABLE_WARNING #include @@ -1230,6 +1233,16 @@ namespace AzToolsFramework return m_showThumbnailDropDownButton; } + void PropertyAssetCtrl::SetCustomThumbnailEnabled(bool enabled) + { + m_thumbnail->SetCustomThumbnailEnabled(enabled); + } + + void PropertyAssetCtrl::SetCustomThumbnailPixmap(const QPixmap& pixmap) + { + m_thumbnail->SetCustomThumbnailPixmap(pixmap); + } + void PropertyAssetCtrl::SetThumbnailCallback(EditCallbackType* editNotifyCallback) { m_thumbnailCallback = editNotifyCallback; @@ -1356,15 +1369,27 @@ namespace AzToolsFramework GUI->SetClearNotifyCallback(nullptr); } } - else if (attrib == AZ_CRC("BrowseIcon", 0x507d7a4f)) + else if (attrib == AZ_CRC_CE("BrowseIcon")) { AZStd::string iconPath; - attrValue->Read(iconPath); - - if (!iconPath.empty()) + if (attrValue->Read(iconPath) && !iconPath.empty()) { GUI->SetBrowseButtonIcon(QIcon(iconPath.c_str())); } + else + { + // A QPixmap object can't be assigned directly via an attribute. + // This allows dynamic icon data to be supplied as a buffer containing a serialized QPixmap. + AZStd::vector pixmapBuffer; + if (attrValue->Read>(pixmapBuffer) && !pixmapBuffer.empty()) + { + QByteArray pixmapBytes(pixmapBuffer.data(), aznumeric_cast(pixmapBuffer.size())); + QDataStream stream(&pixmapBytes, QIODevice::ReadOnly); + QPixmap pixmap; + stream >> pixmap; + GUI->SetBrowseButtonIcon(pixmap); + } + } } else if (attrib == AZ_CRC_CE("BrowseButtonEnabled")) { @@ -1390,6 +1415,30 @@ namespace AzToolsFramework GUI->SetShowThumbnail(showThumbnail); } } + else if (attrib == AZ_CRC_CE("ThumbnailIcon")) + { + AZStd::string iconPath; + if (attrValue->Read(iconPath) && !iconPath.empty()) + { + GUI->SetCustomThumbnailEnabled(true); + GUI->SetCustomThumbnailPixmap(QPixmap::fromImage(QImage(iconPath.c_str()))); + } + else + { + // A QPixmap object can't be assigned directly via an attribute. + // This allows dynamic icon data to be supplied as a buffer containing a serialized QPixmap. + AZStd::vector pixmapBuffer; + if (attrValue->Read>(pixmapBuffer) && !pixmapBuffer.empty()) + { + QByteArray pixmapBytes(pixmapBuffer.data(), aznumeric_cast(pixmapBuffer.size())); + QDataStream stream(&pixmapBytes, QIODevice::ReadOnly); + QPixmap pixmap; + stream >> pixmap; + GUI->SetCustomThumbnailEnabled(true); + GUI->SetCustomThumbnailPixmap(pixmap); + } + } + } else if (attrib == AZ_CRC_CE("ThumbnailCallback")) { PropertyAssetCtrl::EditCallbackType* func = azdynamic_cast(attrValue->GetAttribute()); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyAssetCtrl.hxx b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyAssetCtrl.hxx index cc4aff5649..0b98278bc5 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyAssetCtrl.hxx +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyAssetCtrl.hxx @@ -217,12 +217,17 @@ namespace AzToolsFramework void SetHideProductFilesInAssetPicker(bool hide); bool GetHideProductFilesInAssetPicker() const; + // Enable and configure a thumbnail widget that displays an asset preview and dropdown arrow for a dropdown menu void SetShowThumbnail(bool enable); bool GetShowThumbnail() const; void SetShowThumbnailDropDownButton(bool enable); bool GetShowThumbnailDropDownButton() const; void SetThumbnailCallback(EditCallbackType* editNotifyCallback); + // If enabled, replaces the thumbnail widget content with a custom pixmap + void SetCustomThumbnailEnabled(bool enabled); + void SetCustomThumbnailPixmap(const QPixmap& pixmap); + void SetSelectedAssetID(const AZ::Data::AssetId& newID); void SetCurrentAssetType(const AZ::Data::AssetType& newType); void SetSelectedAssetID(const AZ::Data::AssetId& newID, const AZ::Data::AssetType& newType); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ThumbnailPropertyCtrl.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ThumbnailPropertyCtrl.cpp index c458f7c47e..d8ddee6b76 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ThumbnailPropertyCtrl.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ThumbnailPropertyCtrl.cpp @@ -7,75 +7,117 @@ */ #include -AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // 4251: 'QRawFont::d': class 'QExplicitlySharedDataPointer' needs to have dll-interface to be used by clients of class 'QRawFont' - // 4800: 'QTextEngine *const ': forcing value to bool 'true' or 'false' (performance warning) -#include -#include -#include -#include -#include -#include + +// 4251: 'QRawFont::d': class 'QExplicitlySharedDataPointer' needs to have dll-interface to be used by clients of class +// 'QRawFont' 4800: 'QTextEngine *const ': forcing value to bool 'true' or 'false' (performance warning) +AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") #include +#include +#include +#include +#include +#include +#include AZ_POP_DISABLE_WARNING #include "ThumbnailPropertyCtrl.h" namespace AzToolsFramework { - ThumbnailPropertyCtrl::ThumbnailPropertyCtrl(QWidget* parent) : QWidget(parent) { - QHBoxLayout* pLayout = new QHBoxLayout(); - pLayout->setContentsMargins(0, 0, 0, 0); - pLayout->setSpacing(0); - m_thumbnail = new Thumbnailer::ThumbnailWidget(this); m_thumbnail->setFixedSize(QSize(24, 24)); + m_thumbnailEnlarged = new Thumbnailer::ThumbnailWidget(this); + m_thumbnailEnlarged->setFixedSize(QSize(180, 180)); + m_thumbnailEnlarged->setWindowFlags(Qt::Window | Qt::FramelessWindowHint); + + m_customThumbnail = new QLabel(this); + m_customThumbnail->setFixedSize(QSize(24, 24)); + m_customThumbnail->setScaledContents(true); + + m_customThumbnailEnlarged = new QLabel(this); + m_customThumbnailEnlarged->setFixedSize(QSize(180, 180)); + m_customThumbnailEnlarged->setWindowFlags(Qt::Window | Qt::FramelessWindowHint); + m_customThumbnailEnlarged->setScaledContents(true); + m_dropDownArrow = new AspectRatioAwarePixmapWidget(this); m_dropDownArrow->setPixmap(QPixmap(":/stylesheet/img/triangle0.png")); m_dropDownArrow->setFixedSize(QSize(8, 24)); - ShowDropDownArrow(false); m_emptyThumbnail = new QLabel(this); m_emptyThumbnail->setPixmap(QPixmap(":/stylesheet/img/line.png")); m_emptyThumbnail->setFixedSize(QSize(24, 24)); - pLayout->addWidget(m_emptyThumbnail); + QHBoxLayout* pLayout = new QHBoxLayout(); + pLayout->setContentsMargins(0, 0, 0, 0); + pLayout->setSpacing(0); pLayout->addWidget(m_thumbnail); + pLayout->addWidget(m_customThumbnail); + pLayout->addWidget(m_emptyThumbnail); pLayout->addSpacing(4); pLayout->addWidget(m_dropDownArrow); pLayout->addSpacing(4); - setLayout(pLayout); + + ShowDropDownArrow(false); + UpdateVisibility(); } void ThumbnailPropertyCtrl::SetThumbnailKey(Thumbnailer::SharedThumbnailKey key, const char* contextName) { - m_key = key; - m_emptyThumbnail->setVisible(false); - m_thumbnail->SetThumbnailKey(key, contextName); + if (m_customThumbnailEnabled) + { + ClearThumbnail(); + } + else + { + m_key = key; + m_thumbnail->SetThumbnailKey(m_key, contextName); + m_thumbnailEnlarged->SetThumbnailKey(m_key, contextName); + } + UpdateVisibility(); } void ThumbnailPropertyCtrl::ClearThumbnail() { - m_emptyThumbnail->setVisible(true); + m_key.clear(); m_thumbnail->ClearThumbnail(); + m_thumbnailEnlarged->ClearThumbnail(); + UpdateVisibility(); } void ThumbnailPropertyCtrl::ShowDropDownArrow(bool visible) { - if (visible) - { - setFixedSize(QSize(40, 24)); - } - else - { - setFixedSize(QSize(24, 24)); - } + setFixedSize(QSize(visible ? 40 : 24, 24)); m_dropDownArrow->setVisible(visible); } + void ThumbnailPropertyCtrl::SetCustomThumbnailEnabled(bool enabled) + { + m_customThumbnailEnabled = enabled; + UpdateVisibility(); + } + + void ThumbnailPropertyCtrl::SetCustomThumbnailPixmap(const QPixmap& pixmap) + { + m_customThumbnail->setPixmap(pixmap); + m_customThumbnailEnlarged->setPixmap(pixmap); + UpdateVisibility(); + } + + void ThumbnailPropertyCtrl::UpdateVisibility() + { + m_thumbnail->setVisible(m_key && !m_customThumbnailEnabled); + m_thumbnailEnlarged->setVisible(false); + + m_customThumbnail->setVisible(m_customThumbnailEnabled); + m_customThumbnailEnlarged->setVisible(false); + + m_emptyThumbnail->setVisible(!m_key && !m_customThumbnailEnabled); + } + bool ThumbnailPropertyCtrl::event(QEvent* e) { if (isEnabled()) @@ -83,7 +125,7 @@ namespace AzToolsFramework if (e->type() == QEvent::MouseButtonPress) { emit clicked(); - return true; //ignore + return true; // ignore } } @@ -94,37 +136,32 @@ namespace AzToolsFramework { QPainter p(this); QRect targetRect(QPoint(), QSize(40, 24)); - p.fillRect(targetRect, QColor(17, 17, 17)); // #111111 + p.fillRect(targetRect, QColor("#111111")); QWidget::paintEvent(e); } void ThumbnailPropertyCtrl::enterEvent(QEvent* e) { m_dropDownArrow->setPixmap(QPixmap(":/stylesheet/img/triangle0_highlighted.png")); - if (!m_thumbnailEnlarged && m_key) - { - QPoint position = mapToGlobal(pos() - QPoint(185, 0)); - QSize size(180, 180); - m_thumbnailEnlarged.reset(new Thumbnailer::ThumbnailWidget()); - m_thumbnailEnlarged->setFixedSize(size); - m_thumbnailEnlarged->move(position); - m_thumbnailEnlarged->setWindowFlags(Qt::Window | Qt::FramelessWindowHint); - m_thumbnailEnlarged->SetThumbnailKey(m_key); - m_thumbnailEnlarged->raise(); - m_thumbnailEnlarged->show(); - } + const QPoint offset(-m_thumbnailEnlarged->width() - 5, -m_thumbnailEnlarged->height() / 2 + m_thumbnail->height() / 2); + + m_thumbnailEnlarged->move(mapToGlobal(pos()) + offset); + m_thumbnailEnlarged->raise(); + m_thumbnailEnlarged->setVisible(m_key && !m_customThumbnailEnabled); + + m_customThumbnailEnlarged->move(mapToGlobal(pos()) + offset); + m_customThumbnailEnlarged->raise(); + m_customThumbnailEnlarged->setVisible(m_customThumbnailEnabled); QWidget::enterEvent(e); } void ThumbnailPropertyCtrl::leaveEvent(QEvent* e) { m_dropDownArrow->setPixmap(QPixmap(":/stylesheet/img/triangle0.png")); - if (m_thumbnailEnlarged) - { - m_thumbnailEnlarged.reset(); - } + m_thumbnailEnlarged->setVisible(false); + m_customThumbnailEnlarged->setVisible(false); QWidget::leaveEvent(e); } -} +} // namespace AzToolsFramework #include "UI/PropertyEditor/moc_ThumbnailPropertyCtrl.cpp" diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ThumbnailPropertyCtrl.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ThumbnailPropertyCtrl.h index 93f703c4c5..b1ce78b601 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ThumbnailPropertyCtrl.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ThumbnailPropertyCtrl.h @@ -1,5 +1,3 @@ -#pragma once - /* * 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. @@ -8,6 +6,8 @@ * */ +#pragma once + #if !defined(Q_MOC_RUN) #include #include @@ -35,25 +35,38 @@ namespace AzToolsFramework //! Call this to set what thumbnail widget will display void SetThumbnailKey(Thumbnailer::SharedThumbnailKey key, const char* contextName = "Default"); + //! Remove current thumbnail void ClearThumbnail(); + //! Display a clickble dropdown arrow next to the thumbnail void ShowDropDownArrow(bool visible); - bool event(QEvent* e) override; + //! Override the thumbnail widget with a custom image + void SetCustomThumbnailEnabled(bool enabled); + + //! Assign a custom image to dispsy in place of thumbnail + void SetCustomThumbnailPixmap(const QPixmap& pixmap); Q_SIGNALS: void clicked(); - protected: + private: + void UpdateVisibility(); + + bool event(QEvent* e) override; void paintEvent(QPaintEvent* e) override; void enterEvent(QEvent* e) override; void leaveEvent(QEvent* e) override; - private: Thumbnailer::SharedThumbnailKey m_key; Thumbnailer::ThumbnailWidget* m_thumbnail = nullptr; - QScopedPointer m_thumbnailEnlarged; + Thumbnailer::ThumbnailWidget* m_thumbnailEnlarged = nullptr; + + QLabel* m_customThumbnail = nullptr; + QLabel* m_customThumbnailEnlarged = nullptr; + bool m_customThumbnailEnabled = false; + QLabel* m_emptyThumbnail = nullptr; AspectRatioAwarePixmapWidget* m_dropDownArrow = nullptr; }; From c35b0e5eface4f68c85190a2fd1d3c639e9ed8ed Mon Sep 17 00:00:00 2001 From: Scott Murray Date: Tue, 12 Oct 2021 13:46:02 -0700 Subject: [PATCH 15/65] fix a test string to indicate PostFX Layer Signed-off-by: Scott Murray --- ...dra_AtomEditorComponents_PostFxShapeWeightModifierAdded.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFxShapeWeightModifierAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFxShapeWeightModifierAdded.py index be139f83b6..fe6362c9b7 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFxShapeWeightModifierAdded.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFxShapeWeightModifierAdded.py @@ -22,8 +22,8 @@ class Tests: "PostFx Shape Weight Modifier component disabled", "PostFx Shape Weight Modifier component was not disabled.") postfx_layer_component = ( - "Entity has an Actor component", - "Entity did not have an Actor component") + "Entity has a PostFX Layer component", + "Entity did not have an PostFX Layer component") shape_component = ( "Entity has a Tube Shape component", "Entity did not have a Tube Shape component") From 4f539b0eb7c123bb4e35bbc144db14479ad52924 Mon Sep 17 00:00:00 2001 From: Guthrie Adams Date: Tue, 12 Oct 2021 16:17:19 -0500 Subject: [PATCH 16/65] fixed comments Signed-off-by: Guthrie Adams --- .../UI/PropertyEditor/ThumbnailPropertyCtrl.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ThumbnailPropertyCtrl.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ThumbnailPropertyCtrl.h index b1ce78b601..ffd8234190 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ThumbnailPropertyCtrl.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ThumbnailPropertyCtrl.h @@ -39,13 +39,13 @@ namespace AzToolsFramework //! Remove current thumbnail void ClearThumbnail(); - //! Display a clickble dropdown arrow next to the thumbnail + //! Display a clickable dropdown arrow next to the thumbnail void ShowDropDownArrow(bool visible); //! Override the thumbnail widget with a custom image void SetCustomThumbnailEnabled(bool enabled); - //! Assign a custom image to dispsy in place of thumbnail + //! Assign a custom image to display in place of thumbnail void SetCustomThumbnailPixmap(const QPixmap& pixmap); Q_SIGNALS: From 80db67e90a1aa44c954d3814eaefd12c345db2b3 Mon Sep 17 00:00:00 2001 From: Artur K <96597+nemerle@users.noreply.github.com> Date: Wed, 13 Oct 2021 00:06:15 +0200 Subject: [PATCH 17/65] Remove many unused variables and unused setting files (#4607) * Remove many unused variables and unused setting files Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com> * Remove a few more dead config vars Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com> * fix android test_ConfigureSettings_DefaultValues_SetsValues Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com> --- Assets/Editor/MapScreenshotSettings.xml | 13 -- Assets/Editor/UserTools.xml | 2 - Assets/Engine/Config/AutoTestChain.cfg | 11 -- Assets/Engine/Config/AutoTestTimeDemo.cfg | 10 -- Assets/Engine/Config/AutotestPlaythrough.cfg | 9 +- .../Config/CVarGroups/sys_spec_Full.cfg | 97 ----------- .../CVarGroups/sys_spec_GameEffects.cfg | 8 - .../CVarGroups/sys_spec_ObjectDetail.cfg | 160 ------------------ .../Config/CVarGroups/sys_spec_Particles.cfg | 94 ---------- .../Config/CVarGroups/sys_spec_Physics.cfg | 100 ----------- .../CVarGroups/sys_spec_PostProcessing.cfg | 114 ------------- .../Config/CVarGroups/sys_spec_Quality.cfg | 98 ----------- .../Config/CVarGroups/sys_spec_Shading.cfg | 120 ------------- .../Config/CVarGroups/sys_spec_Shadows.cfg | 116 ------------- .../Config/CVarGroups/sys_spec_Sound.cfg | 17 -- .../Config/CVarGroups/sys_spec_Texture.cfg | 94 ---------- .../CVarGroups/sys_spec_TextureResolution.cfg | 52 ------ .../CVarGroups/sys_spec_VolumetricEffects.cfg | 26 --- .../Config/CVarGroups/sys_spec_Water.cfg | 81 --------- Assets/Engine/Config/aidebug.cfg | 3 - Assets/Engine/Config/artprof.cfg | 14 +- Assets/Engine/Config/artprof_user.cfg | 3 - Assets/Engine/Config/benchmark_cpu.cfg | 10 -- Assets/Engine/Config/benchmark_gpu.cfg | 12 +- Assets/Engine/Config/mgpu.cfg | 6 - Assets/Engine/Config/multiplayer.cfg | 77 --------- Assets/Engine/Config/multiplayer_console.cfg | 29 ---- Assets/Engine/Config/recording.cfg | 60 ------- Assets/Engine/Config/singleplayer.cfg | 15 -- Assets/Engine/Config/sketch_off.cfg | 24 --- Assets/Engine/Config/sketch_on.cfg | 24 --- .../Engine/Config/spec/android_MaliT760.cfg | 68 -------- Assets/Engine/Config/spec/android_high.cfg | 58 ------- .../Config/spec/android_high_nogmem.cfg | 53 ------ Assets/Engine/Config/spec/android_low.cfg | 69 -------- Assets/Engine/Config/spec/android_medium.cfg | 74 -------- .../Engine/Config/spec/android_veryhigh.cfg | 55 ------ Assets/Engine/Config/spec/ios_high.cfg | 90 ---------- Assets/Engine/Config/spec/ios_low.cfg | 93 ---------- Assets/Engine/Config/spec/ios_medium.cfg | 91 ---------- Assets/Engine/Config/spec/ios_veryhigh.cfg | 92 ---------- Assets/Engine/Config/spec/osx_metal_high.cfg | 38 ----- Assets/Engine/Config/spec/osx_metal_low.cfg | 37 ---- .../Engine/Config/spec/osx_metal_medium.cfg | 37 ---- .../Engine/Config/spec/osx_metal_veryhigh.cfg | 37 ---- Assets/Engine/Config/spec/pc_high.cfg | 1 - Assets/Engine/Config/spec/pc_low.cfg | 1 - Assets/Engine/Config/spec/pc_medium.cfg | 1 - Assets/Engine/Config/spec/pc_veryhigh.cfg | 1 - Assets/Engine/Config/statoscope.cfg | 9 - Assets/Engine/Config/vr.cfg | 26 --- Assets/Engine/SeedAssetList.seed | 152 ----------------- .../Atom/atom_utils/atom_component_helper.py | 1 - ...GPUTest_AtomFeatureIntegrationBenchmark.py | 1 - .../tests/hydra_GPUTest_BasicLevelSetup.py | 1 - .../editor_python_test_tools/utils.py | 1 - .../screenshot_utils.py | 4 - Code/Legacy/CrySystem/SystemInit.cpp | 21 --- .../Code/Source/Processing/ImageFlags.h | 2 +- .../Source/LYCommonMenu/ImGuiLYCommonMenu.cpp | 14 -- .../launchers/platforms/android/launcher.py | 7 - .../launchers/platforms/linux/launcher.py | 1 - .../launchers/platforms/win/launcher.py | 2 - .../tests/unit/test_launcher_android.py | 2 +- cmake/Tools/layout_tool.py | 39 ++--- editor.cfg | 21 --- system_android_android.cfg | 30 +--- system_ios_ios.cfg | 29 ---- system_linux_pc.cfg | 22 --- system_mac_mac.cfg | 21 --- system_windows_pc.cfg | 20 --- 71 files changed, 21 insertions(+), 2800 deletions(-) delete mode 100644 Assets/Engine/Config/CVarGroups/sys_spec_GameEffects.cfg delete mode 100644 Assets/Engine/Config/CVarGroups/sys_spec_ObjectDetail.cfg delete mode 100644 Assets/Engine/Config/CVarGroups/sys_spec_Particles.cfg delete mode 100644 Assets/Engine/Config/CVarGroups/sys_spec_Physics.cfg delete mode 100644 Assets/Engine/Config/CVarGroups/sys_spec_PostProcessing.cfg delete mode 100644 Assets/Engine/Config/CVarGroups/sys_spec_Quality.cfg delete mode 100644 Assets/Engine/Config/CVarGroups/sys_spec_Shading.cfg delete mode 100644 Assets/Engine/Config/CVarGroups/sys_spec_Shadows.cfg delete mode 100644 Assets/Engine/Config/CVarGroups/sys_spec_Sound.cfg delete mode 100644 Assets/Engine/Config/CVarGroups/sys_spec_Texture.cfg delete mode 100644 Assets/Engine/Config/CVarGroups/sys_spec_TextureResolution.cfg delete mode 100644 Assets/Engine/Config/CVarGroups/sys_spec_VolumetricEffects.cfg delete mode 100644 Assets/Engine/Config/CVarGroups/sys_spec_Water.cfg delete mode 100644 Assets/Engine/Config/sketch_off.cfg delete mode 100644 Assets/Engine/Config/sketch_on.cfg delete mode 100644 Assets/Engine/Config/spec/osx_metal_high.cfg delete mode 100644 Assets/Engine/Config/spec/osx_metal_low.cfg delete mode 100644 Assets/Engine/Config/spec/osx_metal_medium.cfg delete mode 100644 Assets/Engine/Config/spec/osx_metal_veryhigh.cfg diff --git a/Assets/Editor/MapScreenshotSettings.xml b/Assets/Editor/MapScreenshotSettings.xml index 01f90db4b3..88a8a49aef 100644 --- a/Assets/Editor/MapScreenshotSettings.xml +++ b/Assets/Editor/MapScreenshotSettings.xml @@ -1,18 +1,5 @@ - - - - - - - - - - - - - diff --git a/Assets/Editor/UserTools.xml b/Assets/Editor/UserTools.xml index 8f971cbd5c..4cc197afbe 100644 --- a/Assets/Editor/UserTools.xml +++ b/Assets/Editor/UserTools.xml @@ -7,8 +7,6 @@ - - diff --git a/Assets/Engine/Config/AutoTestChain.cfg b/Assets/Engine/Config/AutoTestChain.cfg index 8c6fae9020..4164acb271 100644 --- a/Assets/Engine/Config/AutoTestChain.cfg +++ b/Assets/Engine/Config/AutoTestChain.cfg @@ -1,16 +1,5 @@ ConsoleHide -g_godMode=1 sys_warnings=0 con_showonload=1 -i_forcefeedback=0 -g_infiniteammo=1 -e_ObjectLayersActivation=0 -e_ObjectLayersActivationPhysics=0 -g_flashrenderingduringloading=0 sys_maxfps=0 r_vsync=0 -p_max_substeps=1 -demo_file=autotest -demo_num_runs=0 -demo_quit=1 -demo_ai=1 diff --git a/Assets/Engine/Config/AutoTestTimeDemo.cfg b/Assets/Engine/Config/AutoTestTimeDemo.cfg index 687158eda9..664ea94939 100644 --- a/Assets/Engine/Config/AutoTestTimeDemo.cfg +++ b/Assets/Engine/Config/AutoTestTimeDemo.cfg @@ -1,15 +1,5 @@ ConsoleHide -g_godMode=1 -g_infiniteammo=1 r_displayinfo=1 s_profiling=1 sys_maxfps=0 -e_ObjectLayersActivation=0 -e_ObjectLayersActivationPhysics=0 - -demo_file=autotest -demo_num_runs=2 -demo_quit=1 -demo_savestats=1 -demo_profile=-1 demo diff --git a/Assets/Engine/Config/AutotestPlaythrough.cfg b/Assets/Engine/Config/AutotestPlaythrough.cfg index f453097972..664ea94939 100644 --- a/Assets/Engine/Config/AutotestPlaythrough.cfg +++ b/Assets/Engine/Config/AutotestPlaythrough.cfg @@ -1,12 +1,5 @@ ConsoleHide -g_godMode=1 -g_infiniteammo=1 r_displayinfo=1 s_profiling=1 sys_maxfps=0 -demo_file=playthru -demo_num_runs=2 -demo_quit=1 -demo_savestats=1 -demo_profile=-1 -demo \ No newline at end of file +demo diff --git a/Assets/Engine/Config/CVarGroups/sys_spec_Full.cfg b/Assets/Engine/Config/CVarGroups/sys_spec_Full.cfg index 626dce3ee4..04fb56f3ca 100644 --- a/Assets/Engine/Config/CVarGroups/sys_spec_Full.cfg +++ b/Assets/Engine/Config/CVarGroups/sys_spec_Full.cfg @@ -11,113 +11,16 @@ ; default of this CVarGroup = 7 -sys_spec_ObjectDetail=7 -sys_spec_Shading=7 -sys_spec_VolumetricEffects=7 -sys_spec_Shadows=7 -sys_spec_Texture=7 -sys_spec_Physics=7 -sys_spec_PostProcessing=7 -sys_spec_Particles=7 -sys_spec_Sound=7 -sys_spec_Water=7 -sys_spec_GameEffects=7 -sys_spec_light=7 - [1] -sys_spec_ObjectDetail=1 -sys_spec_Shading=1 -sys_spec_VolumetricEffects=1 -sys_spec_Shadows=1 -sys_spec_Texture=1 -sys_spec_Physics=1 -sys_spec_PostProcessing=1 -sys_spec_Particles=1 -sys_spec_Sound=1 -sys_spec_Water=1 -sys_spec_GameEffects=1 -sys_spec_light=1 [2] -sys_spec_ObjectDetail=2 -sys_spec_Shading=2 -sys_spec_VolumetricEffects=2 -sys_spec_Shadows=2 -sys_spec_Texture=2 -sys_spec_Physics=2 -sys_spec_PostProcessing=2 -sys_spec_Particles=2 -sys_spec_Sound=2 -sys_spec_Water=2 -sys_spec_GameEffects=2 -sys_spec_light=2 [3] -sys_spec_ObjectDetail=3 -sys_spec_Shading=3 -sys_spec_VolumetricEffects=3 -sys_spec_Shadows=3 -sys_spec_Texture=3 -sys_spec_Physics=3 -sys_spec_PostProcessing=3 -sys_spec_Particles=3 -sys_spec_Sound=3 -sys_spec_Water=3 -sys_spec_GameEffects=3 -sys_spec_light=3 [4] -sys_spec_ObjectDetail=4 -sys_spec_Shading=4 -sys_spec_VolumetricEffects=4 -sys_spec_Shadows=4 -sys_spec_Texture=4 -sys_spec_Physics=4 -sys_spec_PostProcessing=4 -sys_spec_Particles=4 -sys_spec_Sound=4 -sys_spec_Water=4 -sys_spec_GameEffects=4 -sys_spec_light=4 [5] -sys_spec_ObjectDetail=5 -sys_spec_Shading=5 -sys_spec_VolumetricEffects=5 -sys_spec_Shadows=5 -sys_spec_Texture=5 -sys_spec_Physics=5 -sys_spec_PostProcessing=5 -sys_spec_Particles=5 -sys_spec_Sound=5 -sys_spec_Water=5 -sys_spec_GameEffects=5 -sys_spec_light=5 [6] -sys_spec_ObjectDetail=6 -sys_spec_Shading=6 -sys_spec_VolumetricEffects=6 -sys_spec_Shadows=6 -sys_spec_Texture=6 -sys_spec_Physics=6 -sys_spec_PostProcessing=6 -sys_spec_Particles=6 -sys_spec_Sound=6 -sys_spec_Water=6 -sys_spec_GameEffects=6 -sys_spec_light=6 [8] -sys_spec_ObjectDetail=8 -sys_spec_Shading=8 -sys_spec_VolumetricEffects=8 -sys_spec_Shadows=8 -sys_spec_Texture=8 -sys_spec_Physics=8 -sys_spec_PostProcessing=8 -sys_spec_Particles=8 -sys_spec_Sound=8 -sys_spec_Water=8 -sys_spec_GameEffects=8 -sys_spec_light=8 diff --git a/Assets/Engine/Config/CVarGroups/sys_spec_GameEffects.cfg b/Assets/Engine/Config/CVarGroups/sys_spec_GameEffects.cfg deleted file mode 100644 index 34e058d47d..0000000000 --- a/Assets/Engine/Config/CVarGroups/sys_spec_GameEffects.cfg +++ /dev/null @@ -1,8 +0,0 @@ -[default] -; default of this CVarGroup -= 7 - -mfx_Timeout = 0.01 - - - diff --git a/Assets/Engine/Config/CVarGroups/sys_spec_ObjectDetail.cfg b/Assets/Engine/Config/CVarGroups/sys_spec_ObjectDetail.cfg deleted file mode 100644 index a2996f7ded..0000000000 --- a/Assets/Engine/Config/CVarGroups/sys_spec_ObjectDetail.cfg +++ /dev/null @@ -1,160 +0,0 @@ -[default] -; default of this CVarGroup -= 7 - -ca_AttachmentCullingRation=360 -es_DebrisLifetimeScale=1 -e_CoverageBufferReproj=6 -e_DecalsAllowGameDecals=1 -e_DecalsLifeTimeScale=2 -e_DecalsOverlapping=1 -e_Dissolve=2 -e_LightQuality=3 -e_LodMin=0 -e_LodRatio=20 -e_MaxViewDistSpecLerp=1 -e_MergedMeshesInstanceDist=1.0 -e_MergedMeshesPool=8192 -e_ObjQuality=3 -e_OcclusionCullingViewDistRatio=1 -e_ProcVegetation=1 -e_StatObjBufferRenderTasks=1 -e_StreamCgf=0 -e_TerrainLodRatio=1 -e_TerrainOcclusionCullingMaxDist=200 -e_Tessellation=0 -e_VegetationMinSize=0 -e_ViewDistMin=10 -e_ViewDistRatio=100 -e_ViewDistRatioCustom=100 -e_ViewDistRatioDetail=100 -e_ViewDistRatioLights=50 -e_ViewDistRatioVegetation=100 -r_DrawNearZRange=0.12 -r_FlaresTessellationRatio=1 -r_SilhouettePOM=0 -r_usezpass=2 - -[1] -ca_AttachmentCullingRation=145 -es_DebrisLifetimeScale=0.6 -e_DecalsLifeTimeScale=1 -e_Dissolve=0 -e_LightQuality=1 -e_LodRatio=10 -e_MaxViewDistSpecLerp=0.5 -e_ObjQuality=1 -e_TerrainOcclusionCullingMaxDist=130 -e_VegetationMinSize=0.5 -e_ViewDistRatioCustom=60 -e_ViewDistRatioDetail=25 -e_ViewDistRatioLights=25 -e_ViewDistRatioVegetation=21 -r_FlaresTessellationRatio=0.25 -r_usezpass=1 -e_ProcVegetation=0 -e_ViewDistRatio=50 - -[2] -ca_AttachmentCullingRation=145 -es_DebrisLifetimeScale=0.6 -e_DecalsLifeTimeScale=1 -e_Dissolve=0 -e_LightQuality=2 -e_LodRatio=10 -e_MaxViewDistSpecLerp=0.5 -e_ObjQuality=2 -e_TerrainOcclusionCullingMaxDist=130 -e_VegetationMinSize=0.5 -e_ViewDistRatioCustom=60 -e_ViewDistRatioDetail=25 -e_ViewDistRatioLights=25 -e_ViewDistRatioVegetation=50 -r_FlaresTessellationRatio=0.25 -r_usezpass=1 -e_ProcVegetation=1 -e_ViewDistRatio=50 - -[3] -ca_AttachmentCullingRation=145 -es_DebrisLifetimeScale=0.6 -e_DecalsLifeTimeScale=1 -e_Dissolve=0 -e_LightQuality=3 -e_LodRatio=10 -e_MaxViewDistSpecLerp=0.5 -e_ObjQuality=3 -e_TerrainOcclusionCullingMaxDist=130 -e_VegetationMinSize=0.5 -e_ViewDistRatioCustom=60 -e_ViewDistRatioDetail=25 -e_ViewDistRatioLights=25 -e_ViewDistRatioVegetation=50 -r_FlaresTessellationRatio=0.25 -r_usezpass=1 -e_ProcVegetation=1 -e_ViewDistRatio=75 - -[4] -ca_AttachmentCullingRation=145 -es_DebrisLifetimeScale=0.6 -e_DecalsLifeTimeScale=1 -e_Dissolve=0 -e_LightQuality=4 -e_LodRatio=10 -e_MaxViewDistSpecLerp=0.5 -e_ObjQuality=4 -e_TerrainOcclusionCullingMaxDist=130 -e_VegetationMinSize=0.5 -e_ViewDistRatioCustom=60 -e_ViewDistRatioDetail=25 -e_ViewDistRatioLights=25 -e_ViewDistRatioVegetation=50 -r_FlaresTessellationRatio=0.25 -r_usezpass=1 -e_ProcVegetation=1 -e_ViewDistRatio=75 - -[5] -ca_AttachmentCullingRation=145 -es_DebrisLifetimeScale=0.6 -e_DecalsLifeTimeScale=1 -e_LightQuality=1 -e_LodRatio=10 -e_MaxViewDistSpecLerp=0.5 -e_ObjQuality=1 -e_TerrainOcclusionCullingMaxDist=130 -e_VegetationMinSize=0.5 -e_ViewDistRatio=50 -e_ViewDistRatioCustom=60 -e_ViewDistRatioDetail=25 -e_ViewDistRatioLights=25 -e_ViewDistRatioVegetation=50 -r_FlaresTessellationRatio=0.25 - -[6] -ca_AttachmentCullingRation=300 -es_DebrisLifetimeScale=0.8 -e_LightQuality=2 -e_LodRatio=15 -e_ObjQuality=2 -e_ViewDistRatio=75 -e_ViewDistRatioDetail=35 -e_ViewDistRatioVegetation=75 - -[8] -ca_AttachmentCullingRation=400 -e_LightQuality=4 -e_LodRatio=40 -e_MergedMeshesInstanceDist=2.0 -e_MergedMeshesPool=16384 -e_ObjQuality=4 -e_TerrainLodRatio=0.5 -e_Tessellation=1 -e_ViewDistRatio=125 -e_ViewDistRatioCustom=125 -e_ViewDistRatioDetail=125 -e_ViewDistRatioLights=75 -e_ViewDistRatioVegetation=125 -r_DrawNearZRange = 0.08 -r_SilhouettePOM=1 \ No newline at end of file diff --git a/Assets/Engine/Config/CVarGroups/sys_spec_Particles.cfg b/Assets/Engine/Config/CVarGroups/sys_spec_Particles.cfg deleted file mode 100644 index 0a02170b97..0000000000 --- a/Assets/Engine/Config/CVarGroups/sys_spec_Particles.cfg +++ /dev/null @@ -1,94 +0,0 @@ -[default] -; default of this CVarGroup -= 7 - -e_ParticlesGI=1 -e_ParticlesPreload=0 -e_ParticlesMaxScreenFill=128 -e_ParticlesMinDrawPixels=1 -e_ParticlesMotionBlur=0 -e_ParticlesObjectCollisions=2 -e_ParticlesQuality=3 -e_ParticlesSortQuality=0 -e_ParticlesPoolSize=16384 -r_ParticlesHalfRes=0 -r_ParticlesTessellation=1 -r_ParticlesInstanceVertices=1 -r_ParticlesGpuMaxEmitCount=10000 - -[1] -e_ParticlesGI=0 -e_ParticlesPreload=1 -e_ParticlesMaxScreenFill=16 -e_ParticlesMinDrawPixels=2 -e_ParticlesObjectCollisions=1 -e_ParticlesQuality=2 -e_ParticlesPoolSize=4096 -r_ParticlesHalfRes=1 -r_ParticlesTessellation=0 -r_ParticlesInstanceVertices=0 -r_ParticlesGpuMaxEmitCount=10 - - -[2] -e_ParticlesGI=0 -e_ParticlesPreload=1 -e_ParticlesMaxScreenFill=16 -e_ParticlesMinDrawPixels=2 -e_ParticlesObjectCollisions=1 -e_ParticlesQuality=2 -e_ParticlesPoolSize=4096 -r_ParticlesHalfRes=1 -r_ParticlesTessellation=0 -r_ParticlesInstanceVertices=0 -r_ParticlesGpuMaxEmitCount=100 - - -[3] -e_ParticlesGI=0 -e_ParticlesPreload=1 -e_ParticlesMaxScreenFill=16 -e_ParticlesMinDrawPixels=2 -e_ParticlesObjectCollisions=1 -e_ParticlesQuality=2 -e_ParticlesPoolSize=4096 -r_ParticlesHalfRes=1 -r_ParticlesTessellation=0 -r_ParticlesInstanceVertices=0 -r_ParticlesGpuMaxEmitCount=500 - - -[4] -e_ParticlesGI=0 -e_ParticlesPreload=1 -e_ParticlesMaxScreenFill=16 -e_ParticlesMinDrawPixels=2 -e_ParticlesObjectCollisions=1 -e_ParticlesQuality=2 -e_ParticlesPoolSize=4096 -r_ParticlesHalfRes=1 -r_ParticlesTessellation=0 -r_ParticlesInstanceVertices=0 -r_ParticlesGpuMaxEmitCount=1000 - - -[5] -e_ParticlesMaxScreenFill=32 -e_ParticlesMinDrawPixels=1.5 -e_ParticlesObjectCollisions=1 -e_ParticlesQuality=1 -r_ParticlesHalfRes=1 -r_ParticlesTessellation=0 -r_ParticlesGpuMaxEmitCount=3000 - -[6] -e_ParticlesMaxScreenFill=64 -e_ParticlesObjectCollisions=1 -e_ParticlesQuality=2 -r_ParticlesGpuMaxEmitCount=5000 - -[8] -e_ParticlesMaxScreenFill=160 -e_ParticlesMotionBlur=1 -e_ParticlesQuality=4 -r_ParticlesGpuMaxEmitCount=1048576 \ No newline at end of file diff --git a/Assets/Engine/Config/CVarGroups/sys_spec_Physics.cfg b/Assets/Engine/Config/CVarGroups/sys_spec_Physics.cfg deleted file mode 100644 index 15d9fc61e3..0000000000 --- a/Assets/Engine/Config/CVarGroups/sys_spec_Physics.cfg +++ /dev/null @@ -1,100 +0,0 @@ -[default] -; default of this CVarGroup -= 7 - -es_MaxPhysDist=100 -es_MaxPhysDistInvisible=25 -e_CullVegActivation=50 -e_FoliageWindActivationDist=25 -e_PhysMinCellSize=4 -e_PhysOceanCell=0.5 -e_PhysProxyTriLimit=10000 -g_breakage_mem_limit=0 -g_breakage_particles_limit=160 -g_no_secondary_breaking=0 -g_tree_cut_reuse_dist=0 -p_gravity_z=-13 -p_max_entity_cells=300000 -p_max_MC_iters=6000 -p_max_object_splashes=3 -p_max_substeps=5 -p_max_substeps_large_group=5 -p_num_bodies_large_group=100 -p_splash_dist0=7 -p_splash_dist1=30 -p_splash_force0=10 -p_splash_force1=100 -p_splash_vel0=4.5 -p_splash_vel1=10 -v_vehicle_quality=4 - -[1] -es_MaxPhysDistInvisible=15 -e_CullVegActivation=30 -e_FoliageWindActivationDist=10 -e_PhysMinCellSize=16 -e_PhysOceanCell=1 -g_breakage_mem_limit=2000 -g_breakage_particles_limit=40 -g_no_secondary_breaking=1 -g_tree_cut_reuse_dist=1 -p_max_entity_cells=75000 -p_max_MC_iters=2000 -p_max_substeps=2 - -[2] -es_MaxPhysDistInvisible=15 -e_CullVegActivation=30 -e_FoliageWindActivationDist=10 -e_PhysMinCellSize=16 -e_PhysOceanCell=1 -g_breakage_mem_limit=2000 -g_breakage_particles_limit=40 -g_no_secondary_breaking=1 -g_tree_cut_reuse_dist=1 -p_max_entity_cells=75000 -p_max_MC_iters=2000 -p_max_substeps=2 - -[3] -es_MaxPhysDistInvisible=15 -e_CullVegActivation=30 -e_FoliageWindActivationDist=10 -e_PhysMinCellSize=16 -e_PhysOceanCell=1 -g_breakage_mem_limit=2000 -g_breakage_particles_limit=40 -g_no_secondary_breaking=1 -g_tree_cut_reuse_dist=1 -p_max_entity_cells=75000 -p_max_MC_iters=2000 -p_max_substeps=2 - -[4] -es_MaxPhysDistInvisible=15 -e_CullVegActivation=30 -e_FoliageWindActivationDist=10 -e_PhysMinCellSize=16 -e_PhysOceanCell=1 -g_breakage_mem_limit=2000 -g_breakage_particles_limit=40 -g_no_secondary_breaking=1 -g_tree_cut_reuse_dist=1 -p_max_entity_cells=75000 -p_max_MC_iters=2000 -p_max_substeps=2 - -[5] -es_MaxPhysDist=50 -es_MaxPhysDistInvisible=15 -e_CullVegActivation=30 -e_FoliageWindActivationDist=10 -e_PhysOceanCell=1 -g_breakage_particles_limit=80 -g_tree_cut_reuse_dist=0.35 -p_max_MC_iters=4000 -p_max_substeps=2 - -[6] - -[8] diff --git a/Assets/Engine/Config/CVarGroups/sys_spec_PostProcessing.cfg b/Assets/Engine/Config/CVarGroups/sys_spec_PostProcessing.cfg deleted file mode 100644 index 9d2cd3754d..0000000000 --- a/Assets/Engine/Config/CVarGroups/sys_spec_PostProcessing.cfg +++ /dev/null @@ -1,114 +0,0 @@ -[default] -; default of this CVarGroup -= 7 - -r_PostProcessEffects=1 -q_ShaderHDR=2 -q_ShaderPostProcess=2 -r_ChromaticAberration=0 -r_ColorGradingChartsCache=0 -r_DepthOfField=2 -r_Flares=1 -r_HDRBloomQuality=2 -r_MotionBlur=2 -r_MotionBlurMaxViewDist=100000 -r_MotionBlurQuality=1 -r_MotionBlurShutterSpeed=125 -r_Rain=2 -r_RainMaxViewDist_Deferred=150 -r_Sharpening=0 -r_Snow=2 -r_SunShafts=2 -r_TranspDepthFixup=1 -r_ToneMapTechnique=0 -r_ToneMapExposureType=0 -r_HDRBloom=1 -r_ColorGrading=1 -r_ColorSpace=0 - -[1] -q_ShaderHDR=1 -q_ShaderPostProcess=1 -r_ColorGrading=0 -r_DepthOfField=0 -r_Flares=0 -r_HDRBloom=0 -r_HDRBloomQuality=0 -r_MotionBlur=0 -r_MotionBlurMaxViewDist=16 -r_MotionBlurQuality=0 -r_Rain=1 -r_RainMaxViewDist_Deferred=40 -r_Snow=1 -r_SunShafts=0 -r_TranspDepthFixup=0 -r_ToneMapTechnique=3 -r_ToneMapExposureType=1 -r_ColorSpace=2 - - -[2] -q_ShaderHDR=1 -q_ShaderPostProcess=1 -r_ColorGradingChartsCache=4 -r_DepthOfField=1 -r_Flares=0 -r_HDRBloomQuality=0 -r_MotionBlur=0 -r_MotionBlurMaxViewDist=16 -r_MotionBlurQuality=0 -r_Rain=1 -r_RainMaxViewDist_Deferred=40 -r_Snow=1 -r_SunShafts=1 -r_TranspDepthFixup=0 - - -[3] -q_ShaderHDR=1 -q_ShaderPostProcess=2 -r_ColorGradingChartsCache=4 -r_DepthOfField=1 -r_HDRBloomQuality=1 -r_MotionBlur=0 -r_MotionBlurMaxViewDist=16 -r_MotionBlurQuality=0 -r_Rain=1 -r_RainMaxViewDist_Deferred=40 -r_Snow=1 -r_SunShafts=1 -r_TranspDepthFixup=0 - -[4] -q_ShaderHDR=1 -q_ShaderPostProcess=2 -r_ColorGradingChartsCache=4 -r_DepthOfField=1 -r_HDRBloomQuality=1 -r_MotionBlur=0 -r_MotionBlurMaxViewDist=16 -r_MotionBlurQuality=0 -r_Rain=1 -r_RainMaxViewDist_Deferred=40 -r_Snow=1 -r_SunShafts=1 -r_TranspDepthFixup=0 - -[5] -q_ShaderHDR=1 -q_ShaderPostProcess=1 -r_ColorGradingChartsCache=4 -r_MotionBlurMaxViewDist=16 -r_MotionBlurQuality=0 -r_RainMaxViewDist_Deferred=40 -r_TranspDepthFixup=0 - -[6] -q_ShaderHDR=1 -q_ShaderPostProcess=1 -r_RainMaxViewDist_Deferred=100 - -[8] -q_ShaderHDR=3 -q_ShaderPostProcess=3 -r_MotionBlurQuality=2 diff --git a/Assets/Engine/Config/CVarGroups/sys_spec_Quality.cfg b/Assets/Engine/Config/CVarGroups/sys_spec_Quality.cfg deleted file mode 100644 index cf1bdf5184..0000000000 --- a/Assets/Engine/Config/CVarGroups/sys_spec_Quality.cfg +++ /dev/null @@ -1,98 +0,0 @@ -[default] -; default of this CVarGroup -= 7 - -q_ShaderGeneral=2 -q_ShaderMetal=2 -q_ShaderGlass=2 -q_ShaderVegetation=2 -q_ShaderIce=2 -q_ShaderTerrain=2 -q_ShaderShadow=2 -q_ShaderFX=2 -q_ShaderSky=2 -q_Renderer=2 - -[1] -q_ShaderGeneral=1 -q_ShaderMetal=1 -q_ShaderGlass=1 -q_ShaderVegetation=1 -q_ShaderIce=1 -q_ShaderTerrain=1 -q_ShaderShadow=1 -q_ShaderFX=1 -q_ShaderSky=1 -q_Renderer=1 - -[2] -q_ShaderGeneral=1 -q_ShaderMetal=1 -q_ShaderGlass=1 -q_ShaderVegetation=1 -q_ShaderIce=1 -q_ShaderTerrain=1 -q_ShaderShadow=1 -q_ShaderFX=1 -q_ShaderSky=1 -q_Renderer=1 - -[3] -q_ShaderGeneral=1 -q_ShaderMetal=1 -q_ShaderGlass=1 -q_ShaderVegetation=1 -q_ShaderIce=1 -q_ShaderTerrain=1 -q_ShaderShadow=1 -q_ShaderFX=1 -q_ShaderSky=1 -q_Renderer=2 - -[4] -q_ShaderGeneral=1 -q_ShaderMetal=1 -q_ShaderGlass=1 -q_ShaderVegetation=1 -q_ShaderIce=1 -q_ShaderTerrain=1 -q_ShaderShadow=1 -q_ShaderFX=1 -q_ShaderSky=1 -q_Renderer=2 - -[5] -q_ShaderGeneral=1 -q_ShaderMetal=1 -q_ShaderGlass=1 -q_ShaderVegetation=1 -q_ShaderIce=1 -q_ShaderTerrain=1 -q_ShaderShadow=1 -q_ShaderFX=1 -q_ShaderSky=1 -q_Renderer=1 - -[6] -q_ShaderGeneral=1 -q_ShaderMetal=1 -q_ShaderGlass=1 -q_ShaderVegetation=1 -q_ShaderIce=1 -q_ShaderTerrain=1 -q_ShaderShadow=1 -q_ShaderFX=1 -q_ShaderSky=1 -q_Renderer=1 - -[8] -q_ShaderGeneral=3 -q_ShaderMetal=3 -q_ShaderGlass=3 -q_ShaderVegetation=3 -q_ShaderIce=3 -q_ShaderTerrain=3 -q_ShaderShadow=3 -q_ShaderFX=3 -q_ShaderSky=3 -q_Renderer=3 diff --git a/Assets/Engine/Config/CVarGroups/sys_spec_Shading.cfg b/Assets/Engine/Config/CVarGroups/sys_spec_Shading.cfg deleted file mode 100644 index eb379a8583..0000000000 --- a/Assets/Engine/Config/CVarGroups/sys_spec_Shading.cfg +++ /dev/null @@ -1,120 +0,0 @@ -[default] -; default of this CVarGroup -= 7 - -e_CacheNearestCubePicking=1 -e_DynamicLightsMaxEntityLights=16 -e_GI=1 -e_LightVolumes=1 -e_SkyUpdateRate=1 -e_TerrainAo=0 -e_VegetationUseTerrainColor=1 -r_DeferredShadingDepthBoundsTest=1 -r_DeferredShadingTiled=2 -r_DeferredShadingTiledHairQuality=1 -r_deferredShadingFilterGBuffer=0 -r_DeferredShadingSSS=1 -r_AntialiasingMode=3 -r_DetailDistance=8 -r_EnvTexUpdateInterval=0.05 -r_Refraction=1 -r_RefractionPartialResolves=2 -r_ssdo=1 -r_ssdoHalfRes=2 -r_ssdoColorBleeding=1 -r_SSReflections=1 -r_SSReflHalfRes=1 -r_VisAreaClipLightsPerPixel=1 -sys_spec_Quality=7 - -[1] -e_DynamicLightsMaxEntityLights=2 -e_GI=0 -e_SkyUpdateRate=0.5 -e_VegetationUseTerrainColor=0 -r_DeferredShadingTiled=0 -r_DeferredShadingTiledHairQuality=0 -r_DeferredShadingSSS=0 -r_AntialiasingMode=0 -r_DetailDistance=4 -r_EnvTexUpdateInterval=0.075 -r_Refraction=0 -r_RefractionPartialResolves=0 -r_ssdo=0 -r_ssdoHalfRes=1 -r_ssdoColorBleeding=0 -r_SSReflections=0 -sys_spec_Quality=1 - -[2] -e_DynamicLightsMaxEntityLights=2 -e_GI=0 -e_SkyUpdateRate=0.5 -e_VegetationUseTerrainColor=0 -r_DeferredShadingTiled=0 -r_DeferredShadingTiledHairQuality=0 -r_DeferredShadingSSS=0 -r_AntialiasingMode=0 -r_DetailDistance=4 -r_EnvTexUpdateInterval=0.075 -r_RefractionPartialResolves=0 -r_ssdo=0 -r_ssdoHalfRes=1 -r_ssdoColorBleeding=0 -r_SSReflections=0 -sys_spec_Quality=2 - -[3] -e_DynamicLightsMaxEntityLights=2 -e_GI=0 -e_SkyUpdateRate=0.5 -e_VegetationUseTerrainColor=0 -r_DeferredShadingTiled=0 -r_DeferredShadingTiledHairQuality=0 -r_DeferredShadingSSS=0 -r_AntialiasingMode=0 -r_DetailDistance=4 -r_EnvTexUpdateInterval=0.075 -r_RefractionPartialResolves=0 -r_ssdo=0 -r_ssdoColorBleeding=0 -r_SSReflections=0 -sys_spec_Quality=3 - -[4] -e_DynamicLightsMaxEntityLights=2 -e_GI=0 -e_SkyUpdateRate=0.5 -e_VegetationUseTerrainColor=0 -r_DeferredShadingTiled=0 -r_DeferredShadingTiledHairQuality=0 -r_DeferredShadingSSS=0 -r_AntialiasingMode=0 -r_DetailDistance=4 -r_EnvTexUpdateInterval=0.075 -r_RefractionPartialResolves=0 -r_ssdo=1 -r_ssdoHalfRes=1 -r_ssdoColorBleeding=0 -r_SSReflections=0 -sys_spec_Quality=4 - -[5] -e_DynamicLightsMaxEntityLights=7 -e_GI=0 -e_SkyUpdateRate=0.5 -r_DetailDistance=4 -r_EnvTexUpdateInterval=0.075 -r_SSReflections=0 -r_DeferredShadingTiledHairQuality=0 -r_DeferredShadingSSS=0 -sys_spec_Quality=5 - -[6] -e_DynamicLightsMaxEntityLights=11 -sys_spec_Quality=6 - -[8] -r_DeferredShadingTiledHairQuality=2 -r_SSReflHalfRes=0 -sys_spec_Quality=8 diff --git a/Assets/Engine/Config/CVarGroups/sys_spec_Shadows.cfg b/Assets/Engine/Config/CVarGroups/sys_spec_Shadows.cfg deleted file mode 100644 index d43c28c09d..0000000000 --- a/Assets/Engine/Config/CVarGroups/sys_spec_Shadows.cfg +++ /dev/null @@ -1,116 +0,0 @@ -[default] -; default of this CVarGroup -= 7 - -e_GsmLodsNum=5 -e_GsmRange=3 -e_ParticlesShadows=1 -e_Shadows=1 -e_ShadowsBlendCascades=1 -e_ShadowsClouds=1 -e_ShadowsCastViewDistRatio=1 -e_ShadowsLodBiasFixed=0 -e_ShadowsMaxTexRes=1024 -e_ShadowsOnAlphaBlend=0 -e_ShadowsPoolSize=4096 -e_ShadowsResScale=4 -e_ShadowsTessellateCascades=1 -e_ShadowsTessellateDLights=0 -e_ShadowsUpdateViewDistRatio=256 -r_DrawNearShadows=1 -r_FogShadows=2 -r_FogShadowsWater=0 -r_ShadowJittering=2.5 -r_ShadowPoolMaxFrames=30 -r_ShadowPoolMaxTimeslicedUpdatesPerFrame=100 -r_ShadowsPCFiltering=1 -r_ShadowsCache=4 -r_ShadowsCacheFormat=1 -r_ShadowsCacheResolutions=6324,4214 -r_ShadowsUseClipVolume=1 -e_ObjShadowCastSpec=3 - -[1] -e_GsmLodsNum=3 -e_ParticlesShadows=0 -e_ShadowsBlendCascades=0 -e_ShadowsCastViewDistRatio=0.8 -e_ShadowsLodBiasFixed=1 -e_ShadowsMaxTexRes=512 -r_DrawNearShadows=0 -r_FogShadows=0 -r_ShadowJittering=0 -r_ShadowsCacheFormat=0 -r_ShadowsCacheResolutions=3162,2107 -e_ObjShadowCastSpec=1 -e_ShadowsPoolSize=1024 - -[2] -e_GsmLodsNum=4 -e_ParticlesShadows=0 -e_ShadowsBlendCascades=0 -e_ShadowsCastViewDistRatio=0.8 -e_ShadowsLodBiasFixed=1 -e_ShadowsMaxTexRes=512 -r_DrawNearShadows=0 -r_FogShadows=0 -r_ShadowJittering=0 -r_ShadowsCacheFormat=0 -r_ShadowsCacheResolutions=3162,2107 -e_ObjShadowCastSpec=1 -e_ShadowsPoolSize=1024 - -[3] -e_GsmLodsNum=4 -e_ParticlesShadows=0 -e_ShadowsBlendCascades=0 -e_ShadowsCastViewDistRatio=0.8 -e_ShadowsLodBiasFixed=1 -e_ShadowsMaxTexRes=512 -r_DrawNearShadows=0 -r_FogShadows=0 -r_ShadowJittering=0 -r_ShadowsCacheFormat=0 -r_ShadowsCacheResolutions=3162,2107 -e_ObjShadowCastSpec=1 -e_ShadowsPoolSize=1024 - -[4] -e_GsmLodsNum=4 -e_ParticlesShadows=0 -e_ShadowsBlendCascades=0 -e_ShadowsCastViewDistRatio=0.8 -e_ShadowsLodBiasFixed=1 -e_ShadowsMaxTexRes=512 -r_DrawNearShadows=0 -r_FogShadows=0 -r_ShadowJittering=0 -r_ShadowsCacheFormat=0 -r_ShadowsCacheResolutions=3162,2107 -e_ObjShadowCastSpec=1 -e_ShadowsPoolSize=1024 - -[5] -e_GsmLodsNum=4 -e_ParticlesShadows=0 -e_ShadowsBlendCascades=0 -e_ShadowsCastViewDistRatio=0.8 -e_ShadowsLodBiasFixed=1 -e_ShadowsMaxTexRes=512 -r_FogShadows=0 -r_ShadowJittering=1 -e_ObjShadowCastSpec=1 -r_ShadowsCacheResolutions=3162,2107 - -[6] -r_ShadowJittering=1 -e_ObjShadowCastSpec=2 - -[8] -r_FogShadows=1 -r_FogShadowsWater=1 -r_ShadowPoolMaxFrames=0 -r_ShadowPoolMaxTimeslicedUpdatesPerFrame=100 -e_ObjShadowCastSpec=4 -r_ShadowsCache=5 -r_ShadowsCacheResolutions=4214 diff --git a/Assets/Engine/Config/CVarGroups/sys_spec_Sound.cfg b/Assets/Engine/Config/CVarGroups/sys_spec_Sound.cfg deleted file mode 100644 index 42bd4c7f02..0000000000 --- a/Assets/Engine/Config/CVarGroups/sys_spec_Sound.cfg +++ /dev/null @@ -1,17 +0,0 @@ -[default] -; default of this CVarGroup -= 7 - -[1] - -[2] - -[3] - -[4] - -[5] - -[6] - -[8] diff --git a/Assets/Engine/Config/CVarGroups/sys_spec_Texture.cfg b/Assets/Engine/Config/CVarGroups/sys_spec_Texture.cfg deleted file mode 100644 index 0e02c41ab2..0000000000 --- a/Assets/Engine/Config/CVarGroups/sys_spec_Texture.cfg +++ /dev/null @@ -1,94 +0,0 @@ -[default] -; default of this CVarGroup -= 7 - -e_TerrainTextureStreamingPoolItemsNum=64 -r_DynTexAtlasCloudsMaxSize=32 -r_DynTexAtlasSpritesMaxSize=32 -r_DynTexMaxSize=80 -r_EnvCMResolution=2 -r_EnvTexResolution=3 -r_ImposterRatio=1 -r_TexAtlasSize=2048 -r_TexMaxAnisotropy=4 -r_TexMinAnisotropy=4 -r_TexNoAnisoAlphaTest=0 - -[1] -e_TerrainTextureStreamingPoolItemsNum=16 -r_DynTexAtlasCloudsMaxSize=8 -r_DynTexAtlasSpritesMaxSize=8 -r_DynTexMaxSize=20 -r_EnvCMResolution=0 -r_EnvTexResolution=1 -r_ImposterRatio=2 -r_TexAtlasSize=512 -r_TexMaxAnisotropy=2 -r_TexMinAnisotropy=2 -r_TexNoAnisoAlphaTest=1 - -[2] -e_TerrainTextureStreamingPoolItemsNum=16 -r_DynTexAtlasCloudsMaxSize=8 -r_DynTexAtlasSpritesMaxSize=8 -r_DynTexMaxSize=20 -r_EnvCMResolution=0 -r_EnvTexResolution=1 -r_ImposterRatio=2 -r_TexAtlasSize=512 -r_TexMaxAnisotropy=2 -r_TexMinAnisotropy=2 -r_TexNoAnisoAlphaTest=1 - -[3] -e_TerrainTextureStreamingPoolItemsNum=16 -r_DynTexAtlasCloudsMaxSize=8 -r_DynTexAtlasSpritesMaxSize=8 -r_DynTexMaxSize=20 -r_EnvCMResolution=0 -r_EnvTexResolution=1 -r_ImposterRatio=2 -r_TexAtlasSize=512 -r_TexMaxAnisotropy=2 -r_TexMinAnisotropy=2 -r_TexNoAnisoAlphaTest=1 - -[4] -e_TerrainTextureStreamingPoolItemsNum=16 -r_DynTexAtlasCloudsMaxSize=8 -r_DynTexAtlasSpritesMaxSize=8 -r_DynTexMaxSize=20 -r_EnvCMResolution=0 -r_EnvTexResolution=1 -r_ImposterRatio=2 -r_TexAtlasSize=512 -r_TexMaxAnisotropy=2 -r_TexMinAnisotropy=2 -r_TexNoAnisoAlphaTest=1 - -[5] -r_DynTexAtlasCloudsMaxSize=24 -r_DynTexAtlasSpritesMaxSize=16 -r_DynTexMaxSize=50 -r_EnvCMResolution=0 -r_EnvTexResolution=1 -r_ImposterRatio=2 -r_TexAtlasSize=512 -r_TexMaxAnisotropy=2 -r_TexMinAnisotropy=2 -r_TexNoAnisoAlphaTest=1 - -[6] -r_DynTexAtlasCloudsMaxSize=24 -r_DynTexAtlasSpritesMaxSize=16 -r_DynTexMaxSize=60 -r_EnvCMResolution=1 -r_EnvTexResolution=2 -r_ImposterRatio=1.5 -r_TexMaxAnisotropy=8 -r_TexMinAnisotropy=8 -r_TexNoAnisoAlphaTest=1 - -[8] -r_TexMaxAnisotropy=16 -r_TexMinAnisotropy=16 diff --git a/Assets/Engine/Config/CVarGroups/sys_spec_TextureResolution.cfg b/Assets/Engine/Config/CVarGroups/sys_spec_TextureResolution.cfg deleted file mode 100644 index 6b6c11ddf6..0000000000 --- a/Assets/Engine/Config/CVarGroups/sys_spec_TextureResolution.cfg +++ /dev/null @@ -1,52 +0,0 @@ -[default] -; dummy default for this CVarGroup (will be auto initialized during streaming system init or overridden by user via system.cfg) -= 0 - -; VRAM 1.0 GB -r_TexturesStreaming=1 -r_TexturesStreamingMipBias=0 -r_TexturesstreamingMinUsableMips=8 -r_TexturesStreamingSkipMips=2 -r_TexturesStreamPoolSize=256 - -[1] -; VRAM 1.0 GB -r_TexturesStreaming=0 -r_TexturesStreamingSkipMips=0 -r_TexturesStreamPoolSize=384 - -[2] -; VRAM 1.0 GB -r_TexturesStreaming=0 -r_TexturesStreamingSkipMips=0 -r_TexturesStreamPoolSize=384 - -[3] -; VRAM 1.0 GB -r_TexturesStreaming=0 -r_TexturesStreamingSkipMips=0 -r_TexturesStreamPoolSize=384 - -[4] -; VRAM 1.0 GB -r_TexturesStreaming=0 -r_TexturesStreamingSkipMips=0 -r_TexturesStreamPoolSize=384 - -[5] -; VRAM 1.0 GB - -[6] -; VRAM 1.5 GB -r_TexturesStreamingSkipMips=1 -r_TexturesStreamPoolSize=512 - -[7] -; VRAM 2.0 GB -r_TexturesStreamingSkipMips=0 -r_TexturesStreamPoolSize=640 - -[8] -; VRAM 3.0 GB -r_TexturesStreamingSkipMips=0 -r_TexturesStreamPoolSize=1536 \ No newline at end of file diff --git a/Assets/Engine/Config/CVarGroups/sys_spec_VolumetricEffects.cfg b/Assets/Engine/Config/CVarGroups/sys_spec_VolumetricEffects.cfg deleted file mode 100644 index b6fdfec33c..0000000000 --- a/Assets/Engine/Config/CVarGroups/sys_spec_VolumetricEffects.cfg +++ /dev/null @@ -1,26 +0,0 @@ -[default] -; default of this CVarGroup -= 7 - -e_Clouds=1 -r_Beams=1 - -[1] -r_Beams=0 - -[2] -r_Beams=0 - -[3] -r_Beams=0 - -[4] -r_Beams=0 - -[5] -r_Beams=0 - -[6] -r_Beams=0 - -[8] diff --git a/Assets/Engine/Config/CVarGroups/sys_spec_Water.cfg b/Assets/Engine/Config/CVarGroups/sys_spec_Water.cfg deleted file mode 100644 index 720339dc98..0000000000 --- a/Assets/Engine/Config/CVarGroups/sys_spec_Water.cfg +++ /dev/null @@ -1,81 +0,0 @@ -[default] -; default of this CVarGroup -= 7 - -e_WaterOceanFFT=1 -e_WaterTessellationAmount=10 -e_WaterTessellationSwathWidth=10 -q_ShaderWater=2 -r_WaterCaustics=1 -r_WaterReflections=1 -r_WaterReflectionsQuality=4 -r_WaterReflectionsMinVisiblePixelsUpdate=0.05 -r_WaterTessellationHW=0 -r_WaterUpdateDistance=0.2 -r_WaterUpdateFactor=0.0 -r_WaterVolumeCaustics=0 -r_WaterVolumeCausticsDensity=256 -r_WaterVolumeCausticsMaxDist=35 -r_WaterVolumeCausticsRes=1024 -r_WaterVolumeCausticsSnapFactor=1 - -[1] -e_WaterTessellationAmount=20 -q_ShaderWater=1 -r_WaterReflectionsQuality=0 -r_WaterUpdateDistance=1 -r_WaterUpdateFactor=0.1 -r_WaterVolumeCausticsDensity=64 -r_WaterVolumeCausticsMaxDist=20 -r_WaterVolumeCausticsRes=384 - -[2] -e_WaterTessellationAmount=20 -q_ShaderWater=1 -r_WaterReflectionsQuality=0 -r_WaterUpdateDistance=1 -r_WaterUpdateFactor=0.1 -r_WaterVolumeCausticsDensity=64 -r_WaterVolumeCausticsMaxDist=20 -r_WaterVolumeCausticsRes=384 - -[3] -e_WaterTessellationAmount=20 -q_ShaderWater=1 -r_WaterReflectionsQuality=0 -r_WaterUpdateDistance=1 -r_WaterUpdateFactor=0.05 -r_WaterVolumeCausticsDensity=64 -r_WaterVolumeCausticsMaxDist=20 -r_WaterVolumeCausticsRes=384 - -[4] -e_WaterTessellationAmount=20 -q_ShaderWater=1 -r_WaterReflectionsQuality=4 -r_WaterUpdateDistance=1 -r_WaterUpdateFactor=0.01 -r_WaterVolumeCausticsDensity=64 -r_WaterVolumeCausticsMaxDist=20 -r_WaterVolumeCausticsRes=384 - -[5] -e_WaterTessellationAmount=20 -q_ShaderWater=1 -r_WaterUpdateDistance=1 -r_WaterUpdateFactor=0.1 -r_WaterVolumeCausticsDensity=64 -r_WaterVolumeCausticsMaxDist=20 -r_WaterVolumeCausticsRes=384 - -[6] -r_WaterUpdateDistance=1 -r_WaterUpdateFactor=0.05 -r_WaterVolumeCausticsDensity=128 -r_WaterVolumeCausticsMaxDist=25 -r_WaterVolumeCausticsRes=512 - -[8] -e_WaterTessellationAmount=85 -r_WaterTessellationHW=1 -r_WaterVolumeCaustics=1 diff --git a/Assets/Engine/Config/aidebug.cfg b/Assets/Engine/Config/aidebug.cfg index f8134c7461..b0eafaaa45 100644 --- a/Assets/Engine/Config/aidebug.cfg +++ b/Assets/Engine/Config/aidebug.cfg @@ -1,4 +1 @@ ai_DebugDraw = 1 -ai_DebugDrawNavigation = 1 -ai_DrawPath all -ai_debugMNMAgentType MediumSizedCharacters \ No newline at end of file diff --git a/Assets/Engine/Config/artprof.cfg b/Assets/Engine/Config/artprof.cfg index 2e1cf55a54..92b91623bf 100644 --- a/Assets/Engine/Config/artprof.cfg +++ b/Assets/Engine/Config/artprof.cfg @@ -1,7 +1,6 @@ ; Setup useful cvars for artists profiling GPU cost ; Once in level (e.g. map c3mp_rooftop_gardens from the frontend/cmdline), in the console: ; exec artprof.cfg -; Be sure also to set r_shadersAsyncActivation=0 in your user.cfg (or copy artprof_user.cfg -> user.cfg) ; used to allow loading of loose shaders sys_pakPriority=0 @@ -9,19 +8,8 @@ sys_pakPriority=0 ; because it's annoying and not relevant for artists sys_pakLogInvalidFileAccess=0 -; disable fog volumes and particles as they can be misleading with r_measureOverdraw 4 -e_fogVolumes=0 -e_particles=0 - ; for convenience -g_infiniteSuitEnergy=1 -g_infiniteAmmo=1 -g_timelimit=0 + bind o "r_measureOverdraw 0" bind p "r_measureOverdraw 4" -bind k "r_artProfile 0" -bind l "r_artProfile 1" - -; loading into an MP level with the map command stops you looking up and down unless you have a weapon -i_giveitem scar diff --git a/Assets/Engine/Config/artprof_user.cfg b/Assets/Engine/Config/artprof_user.cfg index 673436f6f3..265021e120 100644 --- a/Assets/Engine/Config/artprof_user.cfg +++ b/Assets/Engine/Config/artprof_user.cfg @@ -1,6 +1,3 @@ -; disable async shader activation as it crashes when r_shadersAllowCompilation=1 -r_shadersAsyncActivation=0 - ; enable shader compiliation for r_measureOverdraw 4 r_shadersAllowCompilation=1 diff --git a/Assets/Engine/Config/benchmark_cpu.cfg b/Assets/Engine/Config/benchmark_cpu.cfg index c75619107f..bbb3c9db95 100644 --- a/Assets/Engine/Config/benchmark_cpu.cfg +++ b/Assets/Engine/Config/benchmark_cpu.cfg @@ -1,13 +1,3 @@ -demo_restart_level = 2 -g_godMode=1 -g_infiniteammo=1 r_displayinfo=1 -demo_file = autotest -demo_ai = 1 -demo_num_runs = 4 -demo_quit = 1 -hud_startPaused=0 sys_maxfps=0 -e_ObjectLayersActivation=0 -sys_flash = 0 r_vsync = 0 diff --git a/Assets/Engine/Config/benchmark_gpu.cfg b/Assets/Engine/Config/benchmark_gpu.cfg index 7653b32d86..f0242d64d1 100644 --- a/Assets/Engine/Config/benchmark_gpu.cfg +++ b/Assets/Engine/Config/benchmark_gpu.cfg @@ -1,13 +1,3 @@ -demo_restart_level = 1 -g_godMode = 1 -g_infiniteammo = 1 r_displayinfo = 2 -demo_file = timedemo_short -demo_ai = 0 -demo_num_runs = 2 -demo_quit = 1 --- hud_startPaused = 0 sys_maxfps = -1 --- e_ObjectLayersActivation = 0 --- sys_flash = 0 -r_vsync = 0 \ No newline at end of file +r_vsync = 0 diff --git a/Assets/Engine/Config/mgpu.cfg b/Assets/Engine/Config/mgpu.cfg index 93da7fdfd0..e69de29bb2 100644 --- a/Assets/Engine/Config/mgpu.cfg +++ b/Assets/Engine/Config/mgpu.cfg @@ -1,6 +0,0 @@ -r_ColorGradingChartsCache = 0 -r_waterupdateFactor = 0 -r_PostProcessHUD3DCache = 0 -e_gsmcache = 0 -r_ConditionalRendering = 0 -e_gicache = 0 \ No newline at end of file diff --git a/Assets/Engine/Config/multiplayer.cfg b/Assets/Engine/Config/multiplayer.cfg index eafa434f8e..e69de29bb2 100644 --- a/Assets/Engine/Config/multiplayer.cfg +++ b/Assets/Engine/Config/multiplayer.cfg @@ -1,77 +0,0 @@ -ag_turnSpeedParamScale=0.0 -aim_assistFalloffDistance=200 -aim_assistInputForFullFollow_Ironsight=0.20 -aim_assistMaxDistance=255 -aim_assistMaxDistance_ironsight=255 -aim_assistMinTurnScale=0.5 -aim_assistMinTurnScale_ironsight=0.5 -aim_assistSlowDisableDistance=255 -aim_assistSlowFalloffStartDistance=200 -aim_assistSlowThresholdOuter=2.5 -aim_assiststrength=0.7 -aim_assiststrength_ironsight=0.75 -br_breakmaxworldsize=511 -cl_sensitivityControllerMP=0.6 -cl_shallowWaterSpeedMulPlayer=1.0 -controller_multiplier_x=3 -controller_multiplier_z=4 -g_actorViewDistRatio=255 - --- This forces broken trees to have spherical inertia, which makes them harder to rotate around their vertical axis. -g_breakageMinAxisInertia=1.0 -g_glassAutoShatterMinArea=0.5 - -g_distanceForceNoIk=35 -g_fpDbaManagementEnable=0 -g_godMode=0 -g_highlightingMaxDistanceToHighlightSquared=625 -g_hitDeathReactions_streaming=2 -g_mp_as_DefendersMaxHealth=150 -g_multiplayerDefault=1 - --- Overriden in GameSDK\Difficulty\*.cfg -g_playerLowHealthThreshold=20 -g_playerMidHealthThreshold=60 - -g_spawn_vistable_numLineTestsPerFrame=3 -g_telemetryConfig="MP" -g_telemetrySampleRateBandwidth=3 -g_telemetrySampleRateMemory=2 -g_telemetrySampleRatePerformance=1 -g_VTOLInsideBoundsScaleX=0.6 -g_VTOLInsideBoundsScaleY=1 -net_breakage_sync_entities=0 -net_enable_tfrc=0 -net_log=1 - --- Make consoles match the PC gravity -p_gravity_z="-13" - --- Sanity check for physics RepositionEntity -p_max_entity_cells=10000 - -pl_impulseEnabled=1 -pl_jump_maxTimerValue=0.0 -pl_jump_quickPressThresh=0.12 -pl_melee.angle_limit_from_behind=70 -pl_melee.impulses_enable=1 -pl_melee.melee_snap_angle_limit=45 -pl_melee.melee_snap_end_position_range=1.5 -pl_melee.melee_snap_move_speed_multiplier=10 -pl_melee.melee_snap_target_select_range=3.5 -pl_melee.mp_knockback_strength_hor=2 -pl_melee.mp_melee_system=1 -pl_melee.mp_victim_screenfx_blendout_duration=0.25 -pl_melee.mp_victim_screenfx_duration=0.1 -pl_nanovision_timetodrain=8 -pl_nanovision_timetorecharge=16 -pl_pickAndThrow.chargedThrowAutoAimConeSize=10 -pl_pickAndThrow.complexMelee_snap_angle_limit=25 -pl_sliding_control_mp.deceleration_speed=4 -pl_sliding_control_mp.max_downhill_acceleration=15 -pl_sliding_control_mp.min_speed=4 -pl_sliding_control_mp.min_speed_threshold=5 -pl_stealthKill_aimVsSpineLerp=0.65 -pl_stealthKill_useExtendedRange=1 -p_splash_vel0=0.5 -sv_bandwidth=2147483647 \ No newline at end of file diff --git a/Assets/Engine/Config/multiplayer_console.cfg b/Assets/Engine/Config/multiplayer_console.cfg index 0bed002497..e69de29bb2 100644 --- a/Assets/Engine/Config/multiplayer_console.cfg +++ b/Assets/Engine/Config/multiplayer_console.cfg @@ -1,29 +0,0 @@ -e_GI=0 -r_DeferredShadingIndexedAmbient=0 -e_ParticlesObjectCollisions=0 -g_distanceForceNoLegRaycasts=0.00001 -g_telemetryDisplaySessionId=1 -g_breakageNoDebrisCollisions=1 - -; Breakage throttling -; These are explained in ActionGame.cpp -; -g_glassAutoShatterOnExplosions=1 -g_glassNoDecals=1 -g_glassMaxPanesToBreakPerFrame=2 -g_breakageTreeMax=100 -g_breakageTreeInc=101 -g_breakageTreeDec=25 -g_breakageTreeIncGlass=51 - -sys_PakInMemoryPakSizeLimit=25 - -r_TexturesStreamPoolSecondarySize=35 -r_MotionBlur=1 - -e_CoverageBufferReproj=2 - -osm_enabled = 1 - -g_waterHitOnly = 1 -p_max_object_splashes=1 diff --git a/Assets/Engine/Config/recording.cfg b/Assets/Engine/Config/recording.cfg index 42a0dfd9bd..931dbf0858 100644 --- a/Assets/Engine/Config/recording.cfg +++ b/Assets/Engine/Config/recording.cfg @@ -1,63 +1,3 @@ --- added this lines for proper 360 deg panorama renderings - -e_ScreenShotFileFormat = jpg -demo_fixed_timestep = 60 -s_SoundEnable = 0 r_DisplayInfo = 0 -e_PanoramaScreenShotHeight = 720 -e_PanoramaScreenShotWidth = 10053 c_shakeMult = 0 -demo_ai = 1 -r_MotionBlur = 0 - --- enables full water reflection --- e_DebugMask = 2 -- e_DebugMask not allowed here - --- set weapong lighting effect to 0 to prevent flickering bug because of too much dynamic lights in the scene - - sys_spec = 2 -r_WaterRefractions = 1 -r_WaterReflections = 1 -r_WaterUpdateFactor = 0.01 -r_WaterReflections_ForceParticles = 0 - -r_EnvCMResolution = 2 -r_EnvTexResolution = 3 -r_EnvTexUpdateInterval = 0.05 -e_Decals = 1 -e_DecalsLifeTimeScale = 2 -ca_EnableDecals = 1 -e_LodRatio = 10 -e_ViewDistRatio = 55 -e_Lods = 1 -e_VegetationMinSize = 0 -r_CloudsUpdateAlways = 0 - -e_DynamicLightsMaxEntityLights = 3 -r_DepthOfField = 1 ---r_MotionBlur = 1 -r_Flares = 1 -r_checkSunVis = 1 -r_Coronas = 1 -r_CoronaFade = 0.1625 -r_UseEdgeAA = 1 -e_Clouds = 1 - -r_TexResolution = 0 -r_TexBumpResolution = 0 -r_DetailTextures = 1 -r_DetailNumLayers = 1 -r_DetailDistance = 8 - -e_ParticlesLod = 0.9 - -r_ShadowBlur = 3 -e_ShadowsMaxTexRes = 1024 -r_ShadowJittering = 1 -e_Shadows = 1 -e_VegetationBending = 1 -ai_UpdateAllAlways = 1 -r_refraction = 1 -r_sunshafts = 1 -r_ImposterRatio = 1 diff --git a/Assets/Engine/Config/singleplayer.cfg b/Assets/Engine/Config/singleplayer.cfg index fc7a4f4c05..e69de29bb2 100644 --- a/Assets/Engine/Config/singleplayer.cfg +++ b/Assets/Engine/Config/singleplayer.cfg @@ -1,15 +0,0 @@ -ai_CompatibilityMode=crysis2 -ai_BurstWhileMovingDestinationRange=9999.0 - -g_telemetryConfig=SP - -net_inactivitytimeout=3600 -net_inactivitytimeoutDevmode=3600 - -pl_movement.nonCombat_heavy_weapon_speed_scale=1.0 - --- BLM - Don't override game rules. The template project only has DummyRules. --- Furthermore, multiplayer.cfg doesn't set sv_gamerules, so the inconsistency --- is likely to create bugs. ---sv_gamerules=SinglePlayer -ca_StreamCHR=1 \ No newline at end of file diff --git a/Assets/Engine/Config/sketch_off.cfg b/Assets/Engine/Config/sketch_off.cfg deleted file mode 100644 index 6b0562daab..0000000000 --- a/Assets/Engine/Config/sketch_off.cfg +++ /dev/null @@ -1,24 +0,0 @@ -r_UseZPass = 1 -r_GeomInstancing = 1 - -e_Fog = 1 -e_Clouds = 1 -e_Decals = 1 -e_TerrainDetailMaterials = 1 -e_Dissolve = 1 -e_TerrainAo = 1 - -r_WaterReflections = 1 - -e_Shadows = 1 -e_VegetationBending = 1 - -r_PostProcessEffects = 1 -r_Flares = 1 -r_Beams = 1 -r_Glow = 1 - -r_DetailTextures = 1 - -r_refraction = 1 -r_sunshafts = 1 \ No newline at end of file diff --git a/Assets/Engine/Config/sketch_on.cfg b/Assets/Engine/Config/sketch_on.cfg deleted file mode 100644 index 6759b5d4c8..0000000000 --- a/Assets/Engine/Config/sketch_on.cfg +++ /dev/null @@ -1,24 +0,0 @@ -r_UseZPass = 0 -r_GeomInstancing = 0 - -e_Fog = 0 -e_Clouds = 0 -e_Decals = 0 -e_TerrainDetailMaterials = 0 -e_Dissolve = 0 -e_TerrainAo = 0 - -r_WaterReflections = 0 - -e_Shadows = 0 -e_VegetationBending = 0 - -r_PostProcessEffects = 0 -r_Flares = 0 -r_Beams = 0 -r_Glow = 0 - -r_DetailTextures = 0 - -r_refraction = 0 -r_sunshafts = 0 \ No newline at end of file diff --git a/Assets/Engine/Config/spec/android_MaliT760.cfg b/Assets/Engine/Config/spec/android_MaliT760.cfg index f951e61eef..fd7c0a349d 100644 --- a/Assets/Engine/Config/spec/android_MaliT760.cfg +++ b/Assets/Engine/Config/spec/android_MaliT760.cfg @@ -1,76 +1,8 @@ -sys_spec_Full=2 - -- Cap frame rate at 30fps sys_maxfps=30 r_vsync=0 --- Disable gmem for this device because it causes a crash -r_EnableGMEMPath=0 - --- Default of 3 allocates all shaders (potentially >150 MB) --- 1 is most memory efficient but definitely causes hitches when converting HLSL --- shaders. Recommend 1 during dev, and 3 with optimized caches for release. -r_ShadersPreactivate=1 - -sys_job_system_max_worker=2 sys_streaming_in_blocks=1 sys_streaming_memory_budget=512 --- This allows the generation of reflections for the ocean water. Without it, the water looks really dark. -e_recursion=1 -e_CheckOcclusion=1 - -r_Fur=0 -az_Asset_EnableAsyncMeshLoading=0 - ------------------------- --- Misc. memory buffers ------------------------- -e_GeomCacheBufferSize=0 -e_CheckOcclusionQueueSize=512 -e_CheckOcclusionOutputQueueSize=1024 - ------------------------- --- Animation ------------------------- -ca_MemoryDefragPoolSize=33554432 -ca_StreamCHR=1 - ------------------------- --- sys_spec_objectdetail ------------------------- -e_Dissolve=2 -e_LodRatio=5 -e_ViewDistRatioDetail=19 -e_ViewDistRatioVegetation=21 - - ------------------------- --- sys_spec_postprocessing ------------------------- -r_HDRBloom=0 -r_SunShafts=0 -r_ToneMapTechnique=3 -r_ToneMapExposureType=1 -r_ColorSpace=2 - ------------------------- --- sys_spec_shading ------------------------- -r_VisAreaClipLightsPerPixel=0 - ------------------------- --- sys_spec_textureresolution ------------------------- -r_TexturesstreamingMinUsableMips=7 - - - --- Due to performance issues with incremental cached shadow map updates, enable this to prevent us from culling every object in the world vs each cached shadow map each frame. --- If no objects are present in the level this will eliminate the need to clear the massive cached textures each frame. --- Set to 2 to allow distance-based updates along with script-based updates for the cached shadow maps -e_ShadowsCacheRequireManualUpdate = 2 - ---Use an optimized pixel format for the lighting rendertargets during the lighting pass. -r_DeferredShadingLBuffersFmt = 2 \ No newline at end of file diff --git a/Assets/Engine/Config/spec/android_high.cfg b/Assets/Engine/Config/spec/android_high.cfg index fc46adc491..0ab8ebc37b 100644 --- a/Assets/Engine/Config/spec/android_high.cfg +++ b/Assets/Engine/Config/spec/android_high.cfg @@ -1,65 +1,7 @@ -sys_spec_Full=3 - -- Cap frame rate at 30fps sys_maxfps=30 r_vsync=0 --- Enable framebufferfetch(256bpp) or pls if applicable -r_EnableGMEMPath=2 - --- Skip the native upscale as a second upscale already occurs -r_SkipNativeUpscale=1 - --- Default of 3 allocates all shaders (potentially >150 MB) --- 1 is most memory efficient but definitely causes hitches when converting HLSL --- shaders. Recommend 1 during dev, and 3 with optimized caches for release. -r_ShadersPreactivate=1 - -sys_job_system_max_worker=2 sys_streaming_in_blocks=1 sys_streaming_memory_budget=512 - -e_CheckOcclusion=1 - -r_Fur=0 -az_Asset_EnableAsyncMeshLoading=0 - ------------------------- --- Misc. memory buffers ------------------------- -e_GeomCacheBufferSize=0 -e_CheckOcclusionQueueSize=512 -e_CheckOcclusionOutputQueueSize=1024 - ------------------------- --- Animation ------------------------- -ca_MemoryDefragPoolSize=33554432 -ca_StreamCHR=1 - ------------------------- --- sys_spec_objectdetail ------------------------- -e_Dissolve=2 -e_LodRatio=5 -e_ViewDistRatioDetail=19 - - ------------------------- --- sys_spec_shading ------------------------- -r_VisAreaClipLightsPerPixel=0 - --- Due to performance issues with incremental cached shadow map updates, enable this to prevent us from culling every object in the world vs each cached shadow map each frame. --- If no objects are present in the level this will eliminate the need to clear the massive cached textures each frame. --- Set to 2 to allow distance-based updates along with script-based updates for the cached shadow maps -e_ShadowsCacheRequireManualUpdate = 2 - -r_ClearGMEMGBuffer=1 - --- Sort ligths since we have limited space in the shadowmap pool texture -r_DeferredShadingSortLights = 3 - ---Use an optimized pixel format for the lighting rendertargets during the lighting pass. -r_DeferredShadingLBuffersFmt = 2 \ No newline at end of file diff --git a/Assets/Engine/Config/spec/android_high_nogmem.cfg b/Assets/Engine/Config/spec/android_high_nogmem.cfg index ac718a57ee..fd7c0a349d 100644 --- a/Assets/Engine/Config/spec/android_high_nogmem.cfg +++ b/Assets/Engine/Config/spec/android_high_nogmem.cfg @@ -1,61 +1,8 @@ -sys_spec_Full=3 - -- Cap frame rate at 30fps sys_maxfps=30 r_vsync=0 --- Disabling gmem for this configuration -r_EnableGMEMPath=0 - --- Skip the native upscale as a second upscale already occurs -r_SkipNativeUpscale=1 - --- Default of 3 allocates all shaders (potentially >150 MB) --- 1 is most memory efficient but definitely causes hitches when converting HLSL --- shaders. Recommend 1 during dev, and 3 with optimized caches for release. -r_ShadersPreactivate=1 - -sys_job_system_max_worker=2 sys_streaming_in_blocks=1 sys_streaming_memory_budget=512 -e_CheckOcclusion=1 - -r_Fur=0 -az_Asset_EnableAsyncMeshLoading=0 - ------------------------- --- Misc. memory buffers ------------------------- -e_GeomCacheBufferSize=0 -e_CheckOcclusionQueueSize=512 -e_CheckOcclusionOutputQueueSize=1024 - ------------------------- --- Animation ------------------------- -ca_MemoryDefragPoolSize=33554432 -ca_StreamCHR=1 - ------------------------- --- sys_spec_objectdetail ------------------------- -e_Dissolve=2 -e_LodRatio=5 -e_ViewDistRatioDetail=19 - - ------------------------- --- sys_spec_shading ------------------------- -r_VisAreaClipLightsPerPixel=0 - --- Due to performance issues with incremental cached shadow map updates, enable this to prevent us from culling every object in the world vs each cached shadow map each frame. --- If no objects are present in the level this will eliminate the need to clear the massive cached textures each frame. --- Set to 2 to allow distance-based updates along with script-based updates for the cached shadow maps -e_ShadowsCacheRequireManualUpdate = 2 - ---Use an optimized pixel format for the lighting rendertargets during the lighting pass. -r_DeferredShadingLBuffersFmt = 2 - diff --git a/Assets/Engine/Config/spec/android_low.cfg b/Assets/Engine/Config/spec/android_low.cfg index 6f2a876877..0ab8ebc37b 100644 --- a/Assets/Engine/Config/spec/android_low.cfg +++ b/Assets/Engine/Config/spec/android_low.cfg @@ -1,76 +1,7 @@ -sys_spec_Full=1 - -- Cap frame rate at 30fps sys_maxfps=30 r_vsync=0 --- Enable framebufferfetch(256bpp) or pls if applicable -r_EnableGMEMPath=1 - --- Skip the native upscale as a second upscale already occurs -r_SkipNativeUpscale=1 - --- Default of 3 allocates all shaders (potentially >150 MB) --- 1 is most memory efficient but definitely causes hitches when converting HLSL --- shaders. Recommend 1 during dev, and 3 with optimized caches for release. -r_ShadersPreactivate=1 - -sys_job_system_max_worker=2 sys_streaming_in_blocks=1 sys_streaming_memory_budget=512 - --- This allows the generation of reflections for the ocean water. Without it, the water looks really dark. -e_recursion=0 -e_CheckOcclusion=1 - -r_Fur=0 - --- Water occlusion queries crash in some OpenGL ES 3.0 devices -e_HwOcclusionCullingWater = 0 -az_Asset_EnableAsyncMeshLoading=0 - ------------------------- --- Misc. memory buffers ------------------------- -e_GeomCacheBufferSize=0 -e_CheckOcclusionQueueSize=512 -e_CheckOcclusionOutputQueueSize=1024 - ------------------------- --- Animation ------------------------- -ca_MemoryDefragPoolSize=33554432 -ca_StreamCHR=1 - ------------------------- --- sys_spec_objectdetail ------------------------- -e_Dissolve=2 -e_LodRatio=5 -e_ViewDistRatioDetail=19 - - ------------------------- --- sys_spec_shading ------------------------- -r_VisAreaClipLightsPerPixel=0 - - ------------------------- --- sys_spec_textureresolution ------------------------- -r_TexturesstreamingMinUsableMips=6 - - --- Due to performance issues with incremental cached shadow map updates, enable this to prevent us from culling every object in the world vs each cached shadow map each frame. --- If no objects are present in the level this will eliminate the need to clear the massive cached textures each frame. --- Set to 2 to allow distance-based updates along with script-based updates for the cached shadow maps -e_ShadowsCacheRequireManualUpdate = 2 - --- Sort ligths since we have limited space in the shadowmap pool texture -r_DeferredShadingSortLights = 3 -r_ClearGMEMGBuffer=1 - ---Use an optimized pixel format for the lighting rendertargets during the lighting pass. -r_DeferredShadingLBuffersFmt = 2 \ No newline at end of file diff --git a/Assets/Engine/Config/spec/android_medium.cfg b/Assets/Engine/Config/spec/android_medium.cfg index f9d776efa7..0ab8ebc37b 100644 --- a/Assets/Engine/Config/spec/android_medium.cfg +++ b/Assets/Engine/Config/spec/android_medium.cfg @@ -1,81 +1,7 @@ -sys_spec_Full=2 - -- Cap frame rate at 30fps sys_maxfps=30 r_vsync=0 --- Enable framebufferfetch(256bpp) or pls if applicable -r_EnableGMEMPath=1 - --- Skip the native upscale as a second upscale already occurs -r_SkipNativeUpscale=1 - --- Default of 3 allocates all shaders (potentially >150 MB) --- 1 is most memory efficient but definitely causes hitches when converting HLSL --- shaders. Recommend 1 during dev, and 3 with optimized caches for release. -r_ShadersPreactivate=1 - -sys_job_system_max_worker=2 sys_streaming_in_blocks=1 sys_streaming_memory_budget=512 - --- This allows the generation of reflections for the ocean water. Without it, the water looks really dark. -e_recursion=1 -e_CheckOcclusion=1 - -r_Fur=0 -az_Asset_EnableAsyncMeshLoading=0 - ------------------------- --- Misc. memory buffers ------------------------- -e_GeomCacheBufferSize=0 -e_CheckOcclusionQueueSize=512 -e_CheckOcclusionOutputQueueSize=1024 - ------------------------- --- Animation ------------------------- -ca_MemoryDefragPoolSize=33554432 -ca_StreamCHR=1 - ------------------------- --- sys_spec_objectdetail ------------------------- -e_Dissolve=2 -e_LodRatio=5 -e_ViewDistRatioDetail=19 -e_ViewDistRatioVegetation=21 - - ------------------------- --- sys_spec_postprocessing ------------------------- -r_HDRBloom=0 -r_SunShafts=0 -r_ToneMapExposureType=1 -r_ColorSpace=2 - ------------------------- --- sys_spec_shading ------------------------- -r_VisAreaClipLightsPerPixel=0 - ------------------------- --- sys_spec_textureresolution ------------------------- -r_TexturesstreamingMinUsableMips=7 - --- Due to performance issues with incremental cached shadow map updates, enable this to prevent us from culling every object in the world vs each cached shadow map each frame. --- If no objects are present in the level this will eliminate the need to clear the massive cached textures each frame. --- Set to 2 to allow distance-based updates along with script-based updates for the cached shadow maps -e_ShadowsCacheRequireManualUpdate = 2 - -r_ClearGMEMGBuffer=1 - --- Sort ligths since we have limited space in the shadowmap pool texture -r_DeferredShadingSortLights = 3 - ---Use an optimized pixel format for the lighting rendertargets during the lighting pass. -r_DeferredShadingLBuffersFmt = 2 \ No newline at end of file diff --git a/Assets/Engine/Config/spec/android_veryhigh.cfg b/Assets/Engine/Config/spec/android_veryhigh.cfg index d4f8e9cd47..d58e319706 100644 --- a/Assets/Engine/Config/spec/android_veryhigh.cfg +++ b/Assets/Engine/Config/spec/android_veryhigh.cfg @@ -1,60 +1,5 @@ -sys_spec_Full=4 - -- Cap frame rate at 30fps sys_maxfps=30 r_vsync=0 - --- Enable framebufferfetch(256bpp) or pls if applicable -r_EnableGMEMPath=1 - --- Skip the native upscale as a second upscale already occurs -r_SkipNativeUpscale=1 - --- Default of 3 allocates all shaders (potentially >150 MB) --- 1 is most memory efficient but definitely causes hitches when converting HLSL --- shaders. Recommend 1 during dev, and 3 with optimized caches for release. -r_ShadersPreactivate=1 - -sys_job_system_max_worker=2 sys_streaming_in_blocks=1 - sys_streaming_memory_budget=512 - -e_CheckOcclusion=1 - -r_Fur=0 -az_Asset_EnableAsyncMeshLoading=0 - ------------------------- --- Misc. memory buffers ------------------------- -e_GeomCacheBufferSize=0 -e_CheckOcclusionQueueSize=512 -e_CheckOcclusionOutputQueueSize=1024 - ------------------------- --- Animation ------------------------- -ca_MemoryDefragPoolSize=33554432 -ca_StreamCHR=1 - ------------------------- --- sys_spec_objectdetail ------------------------- -e_Dissolve=2 -e_LodRatio=5 -e_ViewDistRatioDetail=19 - - ------------------------- --- sys_spec_shading ------------------------- -r_VisAreaClipLightsPerPixel=0 - -r_ClearGMEMGBuffer=1 - --- Sort ligths since we have limited space in the shadowmap pool texture -r_DeferredShadingSortLights = 3 - ---Use an optimized pixel format for the lighting rendertargets during the lighting pass. -r_DeferredShadingLBuffersFmt = 2 \ No newline at end of file diff --git a/Assets/Engine/Config/spec/ios_high.cfg b/Assets/Engine/Config/spec/ios_high.cfg index 67fa99ca04..d0cf5b3bc5 100644 --- a/Assets/Engine/Config/spec/ios_high.cfg +++ b/Assets/Engine/Config/spec/ios_high.cfg @@ -1,100 +1,10 @@ -sys_spec_Full=3 - -- Cap frame rate at 30fps sys_maxfps=30 r_vsync=1 --- Enable framebufferfetch or pls if applicable -r_EnableGMEMPath=1 - --- Skip the native upscale as a second upscale occurs on Metal Present -r_SkipNativeUpscale=1 - --- Default of 3 allocates all shaders (potentially >150 MB) --- 1 is most memory efficient but definitely causes hitches when converting HLSL --- shaders. Recommend 1 during dev, and 3 with optimized caches for release. -r_ShadersPreactivate=1 - - ------------------------- --- Job System ------------------------- -sys_job_system_enable=0 -sys_job_system_max_worker=1 - ------------------------ -- Streaming ------------------------ sys_streaming_in_blocks=1 sys_streaming_memory_budget=512 ------------------------- --- General Rendering ------------------------- -r_Flush=0 --- Enabling this will clear the GMEM buffer before the z-pass -r_ClearGMEMGBuffer=2 -r_Fur=0 - ------------------------- --- VisArea / Portals ------------------------- -e_PortalsBlend=0 -r_GMEMVisAreasBlendWeight=0.5 - ------------------------- --- Misc. memory buffers ------------------------- -e_AutoPrecacheCgf=2 -e_AutoPrecacheTerrainAndProcVeget=1 -e_GeomCacheBufferSize=0 -e_CheckOcclusionQueueSize=512 -e_CheckOcclusionOutputQueueSize=2048 - ------------------------- --- Animation ------------------------- -ca_MemoryDefragPoolSize=32 -ca_StreamCHR=1 - ------------------------- --- sys_spec_water ------------------------- -e_WaterOcean=2 -e_WaterVolumes=2 -e_WaterOceanBottom=0 - ------------------------- --- batching ------------------------- -r_Batching = 1 -r_BatchType = 0 - ------------------------- --- geom instancing ------------------------- -r_GeomInstancing=1 -r_GeomInstancingThreshold=5 - ------------------------- --- Upscaling ------------------------- ---0 point, 1 bilinear, 2 bicubic, 3 lanczos -r_UpscalingQuality=1 - ------------------------- --- Geometry Cache ------------------------- -e_GeomCaches=0 - - --- Due to performance issues with incremental cached shadow map updates, enable this to prevent us from culling every object in the world vs each cached shadow map each frame. --- If no objects are present in the level this will eliminate the need to clear the massive cached textures each frame. --- Set to 2 to allow distance-based updates along with script-based updates for the cached shadow maps -e_ShadowsCacheRequireManualUpdate = 2 - --- Sort ligths since we have limited space in the shadowmap pool texture -r_DeferredShadingSortLights = 3 - ---Use an optimized pixel format for the lighting rendertargets during the lighting pass. -r_DeferredShadingLBuffersFmt = 2 \ No newline at end of file diff --git a/Assets/Engine/Config/spec/ios_low.cfg b/Assets/Engine/Config/spec/ios_low.cfg index 7870d65bd5..d0cf5b3bc5 100644 --- a/Assets/Engine/Config/spec/ios_low.cfg +++ b/Assets/Engine/Config/spec/ios_low.cfg @@ -1,103 +1,10 @@ -sys_spec_Full=1 - -- Cap frame rate at 30fps sys_maxfps=30 r_vsync=1 --- Enable framebufferfetch or pls if applicable -r_EnableGMEMPath=1 - --- Skip the native upscale as a second upscale occurs on Metal Present -r_SkipNativeUpscale=1 - --- Default of 3 allocates all shaders (potentially >150 MB) --- 1 is most memory efficient but definitely causes hitches when converting HLSL --- shaders. Recommend 1 during dev, and 3 with optimized caches for release. -r_ShadersPreactivate=1 - - ------------------------- --- Job System ------------------------- -sys_job_system_enable=0 -sys_job_system_max_worker=1 - ------------------------ -- Streaming ------------------------ sys_streaming_in_blocks=1 sys_streaming_memory_budget=512 ------------------------- --- General Rendering ------------------------- -r_Flush=0 --- Enabling this will clear the GMEM buffer before the z-pass -r_ClearGMEMGBuffer=2 -r_Fur=0 - ------------------------- --- VisArea / Portals ------------------------- -e_PortalsBlend=0 -r_GMEMVisAreasBlendWeight=0.5 - ------------------------- --- Misc. memory buffers ------------------------- -e_AutoPrecacheCgf=2 -e_AutoPrecacheTerrainAndProcVeget=1 -e_GeomCacheBufferSize=0 -e_CheckOcclusionQueueSize=512 -e_CheckOcclusionOutputQueueSize=1024 - ------------------------- --- Animation ------------------------- -ca_MemoryDefragPoolSize=32 -ca_StreamCHR=1 - ------------------------- --- sys_spec_water ------------------------- -e_WaterOcean=2 -e_WaterVolumes=2 -e_WaterOceanBottom=0 - - ------------------------- --- batching ------------------------- -r_Batching = 1 -r_BatchType = 0 - ------------------------- --- geom instancing ------------------------- -r_GeomInstancing=1 -r_GeomInstancingThreshold=5 - ------------------------- --- Upscaling ------------------------- ---0 point, 1 bilinear, 2 bicubic, 3 lanczos -r_UpscalingQuality=1 - ------------------------- --- Geometry Cache ------------------------- -e_GeomCaches=0 - - - - --- Due to performance issues with incremental cached shadow map updates, enable this to prevent us from culling every object in the world vs each cached shadow map each frame. --- If no objects are present in the level this will eliminate the need to clear the massive cached textures each frame. --- Set to 2 to allow distance-based updates along with script-based updates for the cached shadow maps -e_ShadowsCacheRequireManualUpdate = 2 - --- Sort ligths since we have limited space in the shadowmap pool texture -r_DeferredShadingSortLights = 3 - ---Use an optimized pixel format for the lighting rendertargets during the lighting pass. -r_DeferredShadingLBuffersFmt = 2 \ No newline at end of file diff --git a/Assets/Engine/Config/spec/ios_medium.cfg b/Assets/Engine/Config/spec/ios_medium.cfg index 2eaecdef16..d0cf5b3bc5 100644 --- a/Assets/Engine/Config/spec/ios_medium.cfg +++ b/Assets/Engine/Config/spec/ios_medium.cfg @@ -1,101 +1,10 @@ -sys_spec_Full=2 - -- Cap frame rate at 30fps sys_maxfps=30 r_vsync=1 --- Enable framebufferfetch or pls if applicable -r_EnableGMEMPath=1 - --- Skip the native upscale as a second upscale occurs on Metal Present -r_SkipNativeUpscale=1 - --- Default of 3 allocates all shaders (potentially >150 MB) --- 1 is most memory efficient but definitely causes hitches when converting HLSL --- shaders. Recommend 1 during dev, and 3 with optimized caches for release. -r_ShadersPreactivate=1 - - ------------------------- --- Job System ------------------------- -sys_job_system_enable=0 -sys_job_system_max_worker=1 - ------------------------ -- Streaming ------------------------ sys_streaming_in_blocks=1 sys_streaming_memory_budget=512 ------------------------- --- General Rendering ------------------------- -r_Flush=0 --- Enabling this will clear the GMEM buffer before the z-pass -r_ClearGMEMGBuffer=2 -r_Fur=0 - ------------------------- --- VisArea / Portals ------------------------- -e_PortalsBlend=0 -r_GMEMVisAreasBlendWeight=0.5 - ------------------------- --- Misc. memory buffers ------------------------- -e_AutoPrecacheCgf=2 -e_AutoPrecacheTerrainAndProcVeget=1 -e_GeomCacheBufferSize=0 -e_CheckOcclusionQueueSize=512 -e_CheckOcclusionOutputQueueSize=1024 - ------------------------- --- Animation ------------------------- -ca_MemoryDefragPoolSize=32 -ca_StreamCHR=1 - ------------------------- --- sys_spec_water ------------------------- -e_WaterOcean=2 -e_WaterVolumes=2 -e_WaterOceanBottom=0 - - ------------------------- --- batching ------------------------- -r_Batching = 1 -r_BatchType = 0 - ------------------------- --- geom instancing ------------------------- -r_GeomInstancing=1 -r_GeomInstancingThreshold=5 - ------------------------- --- Upscaling ------------------------- ---0 point, 1 bilinear, 2 bicubic, 3 lanczos -r_UpscalingQuality=1 - ------------------------- --- Geometry Cache ------------------------- -e_GeomCaches=0 - - --- Due to performance issues with incremental cached shadow map updates, enable this to prevent us from culling every object in the world vs each cached shadow map each frame. --- If no objects are present in the level this will eliminate the need to clear the massive cached textures each frame. --- Set to 2 to allow distance-based updates along with script-based updates for the cached shadow maps -e_ShadowsCacheRequireManualUpdate = 2 - --- Sort ligths since we have limited space in the shadowmap pool texture -r_DeferredShadingSortLights = 3 - ---Use an optimized pixel format for the lighting rendertargets during the lighting pass. -r_DeferredShadingLBuffersFmt = 2 \ No newline at end of file diff --git a/Assets/Engine/Config/spec/ios_veryhigh.cfg b/Assets/Engine/Config/spec/ios_veryhigh.cfg index 53937a77bf..d0cf5b3bc5 100644 --- a/Assets/Engine/Config/spec/ios_veryhigh.cfg +++ b/Assets/Engine/Config/spec/ios_veryhigh.cfg @@ -1,102 +1,10 @@ -sys_spec_Full=4 - -- Cap frame rate at 30fps sys_maxfps=30 r_vsync=1 --- Enable framebufferfetch or pls if applicable -r_EnableGMEMPath=1 - --- Skip the native upscale as a second upscale occurs on Metal Present -r_SkipNativeUpscale=1 - --- Default of 3 allocates all shaders (potentially >150 MB) --- 1 is most memory efficient but definitely causes hitches when converting HLSL --- shaders. Recommend 1 during dev, and 3 with optimized caches for release. -r_ShadersPreactivate=1 - - ------------------------- --- Job System ------------------------- -sys_job_system_enable=0 -sys_job_system_max_worker=1 - ------------------------ -- Streaming ------------------------ sys_streaming_in_blocks=1 sys_streaming_memory_budget=512 ------------------------- --- General Rendering ------------------------- -r_Flush=0 --- Enabling this will clear the GMEM buffer before the z-pass -r_ClearGMEMGBuffer=2 -r_Fur=0 - ------------------------- --- VisArea / Portals ------------------------- -e_PortalsBlend=0 -r_GMEMVisAreasBlendWeight=0.5 - ------------------------- --- Misc. memory buffers ------------------------- -e_AutoPrecacheCgf=2 -e_AutoPrecacheTerrainAndProcVeget=1 -e_GeomCacheBufferSize=0 -e_CheckOcclusionQueueSize=512 -e_CheckOcclusionOutputQueueSize=2048 - ------------------------- --- Animation ------------------------- -ca_MemoryDefragPoolSize=32 -ca_StreamCHR=1 - ------------------------- --- sys_spec_water ------------------------- -e_WaterOcean=2 -e_WaterVolumes=2 -e_WaterOceanBottom=0 - - ------------------------- --- batching ------------------------- -r_Batching = 1 -r_BatchType = 0 - ------------------------- --- geom instancing ------------------------- -r_GeomInstancing=1 -r_GeomInstancingThreshold=5 - ------------------------- --- Upscaling ------------------------- ---0 point, 1 bilinear, 2 bicubic, 3 lanczos -r_UpscalingQuality=1 - ------------------------- --- Geometry Cache ------------------------- -e_GeomCaches=0 - - - --- Due to performance issues with incremental cached shadow map updates, enable this to prevent us from culling every object in the world vs each cached shadow map each frame. --- If no objects are present in the level this will eliminate the need to clear the massive cached textures each frame. --- Set to 2 to allow distance-based updates along with script-based updates for the cached shadow maps -e_ShadowsCacheRequireManualUpdate = 2 - --- Sort ligths since we have limited space in the shadowmap pool texture -r_DeferredShadingSortLights = 3 - ---Use an optimized pixel format for the lighting rendertargets during the lighting pass. -r_DeferredShadingLBuffersFmt = 2 \ No newline at end of file diff --git a/Assets/Engine/Config/spec/osx_metal_high.cfg b/Assets/Engine/Config/spec/osx_metal_high.cfg deleted file mode 100644 index 8689e23c51..0000000000 --- a/Assets/Engine/Config/spec/osx_metal_high.cfg +++ /dev/null @@ -1,38 +0,0 @@ - -sys_spec_Full=7 -r_ShadersMETAL=1 - --- Default of 3 allocates all shaders (potentially >150 MB) --- 1 is most memory efficient but definitely causes hitches when converting HLSL --- shaders. Recommend 1 during dev, and 3 with optimized caches for release. -r_ShadersPreactivate=1 - --- Skip the native upscale as a second upscale occurs on Metal Present -r_SkipNativeUpscale=1 - ------------------------- --- sys_spec_postprocessing ------------------------- -r_SunShafts=1 - ------------------------- --- sys_spec_shading ------------------------- -r_DeferredShadingTiled=0 -r_RefractionPartialResolves=0 -e_GI = 0 - - -r_Fur=2 - - ------------------------- --- Upscaling ------------------------- ---0 point, 1 bilinear, 2 bicubic, 3 lanczos -r_UpscalingQuality=1 - --- Due to performance issues with incremental cached shadow map updates, enable this to prevent us from culling every object in the world vs each cached shadow map each frame. --- If no objects are present in the level this will eliminate the need to clear the massive cached textures each frame. --- Set to 2 to allow distance-based updates along with script-based updates for the cached shadow maps -e_ShadowsCacheRequireManualUpdate = 2 diff --git a/Assets/Engine/Config/spec/osx_metal_low.cfg b/Assets/Engine/Config/spec/osx_metal_low.cfg deleted file mode 100644 index a3084d48c5..0000000000 --- a/Assets/Engine/Config/spec/osx_metal_low.cfg +++ /dev/null @@ -1,37 +0,0 @@ - -sys_spec_Full=5 -r_ShadersMETAL=1 - --- Default of 3 allocates all shaders (potentially >150 MB) --- 1 is most memory efficient but definitely causes hitches when converting HLSL --- shaders. Recommend 1 during dev, and 3 with optimized caches for release. -r_ShadersPreactivate=1 - --- Skip the native upscale as a second upscale occurs on Metal Present -r_SkipNativeUpscale=1 - ------------------------- --- sys_spec_postprocessing ------------------------- -r_SunShafts=1 - ------------------------- --- sys_spec_shading ------------------------- -r_DeferredShadingTiled=0 -r_RefractionPartialResolves=0 -e_GI=0 - -r_Fur=2 - - ------------------------- --- Upscaling ------------------------- ---0 point, 1 bilinear, 2 bicubic, 3 lanczos -r_UpscalingQuality=1 - --- Due to performance issues with incremental cached shadow map updates, enable this to prevent us from culling every object in the world vs each cached shadow map each frame. --- If no objects are present in the level this will eliminate the need to clear the massive cached textures each frame. --- Set to 2 to allow distance-based updates along with script-based updates for the cached shadow maps -e_ShadowsCacheRequireManualUpdate = 2 diff --git a/Assets/Engine/Config/spec/osx_metal_medium.cfg b/Assets/Engine/Config/spec/osx_metal_medium.cfg deleted file mode 100644 index 6477bbb217..0000000000 --- a/Assets/Engine/Config/spec/osx_metal_medium.cfg +++ /dev/null @@ -1,37 +0,0 @@ - -sys_spec_Full=6 -r_ShadersMETAL=1 - --- Default of 3 allocates all shaders (potentially >150 MB) --- 1 is most memory efficient but definitely causes hitches when converting HLSL --- shaders. Recommend 1 during dev, and 3 with optimized caches for release. -r_ShadersPreactivate=1 - --- Skip the native upscale as a second upscale occurs on Metal Present -r_SkipNativeUpscale=1 - ------------------------- --- sys_spec_postprocessing ------------------------- -r_SunShafts=1 - ------------------------- --- sys_spec_shading ------------------------- -r_DeferredShadingTiled=0 -r_RefractionPartialResolves=0 -e_GI = 0 - -r_Fur=2 - - ------------------------- --- Upscaling ------------------------- ---0 point, 1 bilinear, 2 bicubic, 3 lanczos -r_UpscalingQuality=1 - --- Due to performance issues with incremental cached shadow map updates, enable this to prevent us from culling every object in the world vs each cached shadow map each frame. --- If no objects are present in the level this will eliminate the need to clear the massive cached textures each frame. --- Set to 2 to allow distance-based updates along with script-based updates for the cached shadow maps -e_ShadowsCacheRequireManualUpdate = 2 diff --git a/Assets/Engine/Config/spec/osx_metal_veryhigh.cfg b/Assets/Engine/Config/spec/osx_metal_veryhigh.cfg deleted file mode 100644 index 899c198f31..0000000000 --- a/Assets/Engine/Config/spec/osx_metal_veryhigh.cfg +++ /dev/null @@ -1,37 +0,0 @@ - -sys_spec_Full=8 -r_ShadersMETAL=1 - --- Default of 3 allocates all shaders (potentially >150 MB) --- 1 is most memory efficient but definitely causes hitches when converting HLSL --- shaders. Recommend 1 during dev, and 3 with optimized caches for release. -r_ShadersPreactivate=1 - --- Skip the native upscale as a second upscale occurs on Metal Present -r_SkipNativeUpscale=1 - ------------------------- --- sys_spec_postprocessing ------------------------- -r_SunShafts=1 - ------------------------- --- sys_spec_shading ------------------------- -r_DeferredShadingTiled=0 -r_RefractionPartialResolves=0 -e_GI = 0 - -r_Fur=2 - - ------------------------- --- Upscaling ------------------------- ---0 point, 1 bilinear, 2 bicubic, 3 lanczos -r_UpscalingQuality=1 - --- Due to performance issues with incremental cached shadow map updates, enable this to prevent us from culling every object in the world vs each cached shadow map each frame. --- If no objects are present in the level this will eliminate the need to clear the massive cached textures each frame. --- Set to 2 to allow distance-based updates along with script-based updates for the cached shadow maps -e_ShadowsCacheRequireManualUpdate = 2 diff --git a/Assets/Engine/Config/spec/pc_high.cfg b/Assets/Engine/Config/spec/pc_high.cfg index a62ed25a6a..e69de29bb2 100644 --- a/Assets/Engine/Config/spec/pc_high.cfg +++ b/Assets/Engine/Config/spec/pc_high.cfg @@ -1 +0,0 @@ -sys_spec_Full = 7 \ No newline at end of file diff --git a/Assets/Engine/Config/spec/pc_low.cfg b/Assets/Engine/Config/spec/pc_low.cfg index 068446e151..e69de29bb2 100644 --- a/Assets/Engine/Config/spec/pc_low.cfg +++ b/Assets/Engine/Config/spec/pc_low.cfg @@ -1 +0,0 @@ -sys_spec_Full = 5 diff --git a/Assets/Engine/Config/spec/pc_medium.cfg b/Assets/Engine/Config/spec/pc_medium.cfg index 0d08b956b4..e69de29bb2 100644 --- a/Assets/Engine/Config/spec/pc_medium.cfg +++ b/Assets/Engine/Config/spec/pc_medium.cfg @@ -1 +0,0 @@ -sys_spec_Full = 6 \ No newline at end of file diff --git a/Assets/Engine/Config/spec/pc_veryhigh.cfg b/Assets/Engine/Config/spec/pc_veryhigh.cfg index 33959937e8..e69de29bb2 100644 --- a/Assets/Engine/Config/spec/pc_veryhigh.cfg +++ b/Assets/Engine/Config/spec/pc_veryhigh.cfg @@ -1 +0,0 @@ -sys_spec_Full = 8 diff --git a/Assets/Engine/Config/statoscope.cfg b/Assets/Engine/Config/statoscope.cfg index d1d18c813c..ed2089bd67 100644 --- a/Assets/Engine/Config/statoscope.cfg +++ b/Assets/Engine/Config/statoscope.cfg @@ -1,12 +1,3 @@ profile=-1 -profile_allthreads=1 -i_forcefeedback=0 -g_godmode=1 log_verbosity=-1 sys_pakLogInvalidFileAccess=1 -e_StatoscopeDataGroups=fgmrtudlipny -e_StatoscopeFilenameUseBuildInfo=0 -e_StatoscopeFilenameUseMap=1 -e_StatoscopeMinFuncLengthMs=0.1 -e_StatoscopeMaxNumFuncsPerFrame=60 -e_StatoscopeScreenshotCapturePeriod=5 diff --git a/Assets/Engine/Config/vr.cfg b/Assets/Engine/Config/vr.cfg index 989f55f345..80d4b7ce81 100644 --- a/Assets/Engine/Config/vr.cfg +++ b/Assets/Engine/Config/vr.cfg @@ -3,39 +3,13 @@ ----------------------------------------------- r_width = 960 r_height = 1080 -r_backbufferWidth = 1920 -r_backbufferHeight = 1080 - ------------------------------------------- -- Set the system Spec (Medium) ------------------------------------------- sys_spec = 2 - -------------------------------------------- --- HMD related -------------------------------------------- -r_overrideDXGIoutput = 0 -r_stereodevice = 100 -r_stereomode = 1 -r_stereooutput = 7 -r_minimizeLatency = 1 -hmd_low_persistence = 1 -r_stereoScaleCoefficient = 1 - - ------------------------------------------- -- Set some video optimisations ------------------------------------------- r_vsync = 0 -r_MotionBlur = 0 -r_ssdoHalfRes = 3 -r_Refraction = 0 -r_DeferredShadingTiled = 0 -r_CBufferUseNativeDepth = 0 - -------------------------------------------- --- Hide the hud -------------------------------------------- ---hud_hide = 1 diff --git a/Assets/Engine/SeedAssetList.seed b/Assets/Engine/SeedAssetList.seed index 579fd3c444..aafbffbe8f 100644 --- a/Assets/Engine/SeedAssetList.seed +++ b/Assets/Engine/SeedAssetList.seed @@ -48,22 +48,6 @@ - - - - - - - - - - - - - - - - @@ -432,110 +416,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -616,38 +496,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_component_helper.py b/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_component_helper.py index 821e0acfdb..11832f8846 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_component_helper.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_component_helper.py @@ -113,7 +113,6 @@ def create_basic_atom_level(level_name): general.close_pane("Error Log") general.idle_wait(1.0) general.run_console("r_displayInfo=0") - general.run_console("r_antialiasingmode=0") general.idle_wait(1.0) # Delete all existing entities & create default_level entity diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_AtomFeatureIntegrationBenchmark.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_AtomFeatureIntegrationBenchmark.py index 92199bf196..fbd9f3459a 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_AtomFeatureIntegrationBenchmark.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_AtomFeatureIntegrationBenchmark.py @@ -66,7 +66,6 @@ def run(): general.close_pane("Error Log") general.idle_wait(1.0) general.run_console("r_displayInfo=0") - general.run_console("r_antialiasingmode=0") general.idle_wait(1.0) return True diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_BasicLevelSetup.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_BasicLevelSetup.py index ac28e67fa1..93e78a7c0a 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_BasicLevelSetup.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_BasicLevelSetup.py @@ -82,7 +82,6 @@ def run(): general.close_pane("Error Log") general.idle_wait(1.0) general.run_console("r_displayInfo=0") - general.run_console("r_antialiasingmode=0") general.idle_wait(1.0) return True diff --git a/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/utils.py b/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/utils.py index ef3048d8d4..1b094b1cfa 100644 --- a/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/utils.py +++ b/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/utils.py @@ -149,7 +149,6 @@ class TestHelper: general.idle_wait(1.0) general.idle_wait(1.0) general.run_console("r_displayInfo=0") - general.run_console("r_antialiasingmode=0") general.idle_wait(1.0) diff --git a/AutomatedTesting/Gem/PythonTests/automatedtesting_shared/screenshot_utils.py b/AutomatedTesting/Gem/PythonTests/automatedtesting_shared/screenshot_utils.py index 49cf17855f..22fa3aca0d 100755 --- a/AutomatedTesting/Gem/PythonTests/automatedtesting_shared/screenshot_utils.py +++ b/AutomatedTesting/Gem/PythonTests/automatedtesting_shared/screenshot_utils.py @@ -187,7 +187,3 @@ def prepare_for_screenshot_compare(remote_console_instance): """ wait_for(lambda: _retry_command(remote_console_instance, 'r_displayinfo 0', '$3r_DisplayInfo = $60 $5[DUMPTODISK, RESTRICTEDMODE]$4')) - wait_for(lambda: _retry_command(remote_console_instance, 'r_antialiasingmode 0', - '$3r_AntialiasingMode = $60 $5[]$4')) - wait_for(lambda: _retry_command(remote_console_instance, 'e_WaterOcean 0', - '$3e_WaterOcean = $60 $5[]$4')) diff --git a/Code/Legacy/CrySystem/SystemInit.cpp b/Code/Legacy/CrySystem/SystemInit.cpp index 6de10f3855..ee1c498a9a 100644 --- a/Code/Legacy/CrySystem/SystemInit.cpp +++ b/Code/Legacy/CrySystem/SystemInit.cpp @@ -1273,17 +1273,6 @@ AZ_POP_DISABLE_WARNING //Load config files ////////////////////////////////////////////////////////////////////////// - int curSpecVal = 0; - ICVar* pSysSpecCVar = gEnv->pConsole->GetCVar("r_GraphicsQuality"); - if (gEnv->pSystem->IsDevMode()) - { - if (pSysSpecCVar && pSysSpecCVar->GetFlags() & VF_WASINCONFIG) - { - curSpecVal = pSysSpecCVar->GetIVal(); - pSysSpecCVar->SetFlags(pSysSpecCVar->GetFlags() | VF_SYSSPEC_OVERWRITE); - } - } - // tools may not interact with @user@ if (!gEnv->IsInToolMode()) { @@ -1293,16 +1282,6 @@ AZ_POP_DISABLE_WARNING } } - // If sys spec variable was specified, is not 0, and we are in devmode restore the value from before loading game.cfg - // This enables setting of a specific sys_spec outside menu and game.cfg - if (gEnv->pSystem->IsDevMode()) - { - if (pSysSpecCVar && curSpecVal && curSpecVal != pSysSpecCVar->GetIVal()) - { - pSysSpecCVar->Set(curSpecVal); - } - } - { // We have to load this file again since first time we did it without devmode LoadConfiguration(m_systemConfigName.c_str()); diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageFlags.h b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageFlags.h index 96393f4a1f..99e752c90a 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageFlags.h +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageFlags.h @@ -22,7 +22,7 @@ namespace ImageProcessingAtom 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_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 with r_TexResolution + 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 const static AZ::u32 EIF_CafeNative = 0x20000; // info for the engine: native Cafe texture format const static AZ::u32 EIF_RestrictedPlatformONative = 0x40000; // native tiled texture for restrict platform O diff --git a/Gems/ImGui/Code/Source/LYCommonMenu/ImGuiLYCommonMenu.cpp b/Gems/ImGui/Code/Source/LYCommonMenu/ImGuiLYCommonMenu.cpp index 28a5a30e9c..81ec4b17c8 100644 --- a/Gems/ImGui/Code/Source/LYCommonMenu/ImGuiLYCommonMenu.cpp +++ b/Gems/ImGui/Code/Source/LYCommonMenu/ImGuiLYCommonMenu.cpp @@ -199,20 +199,6 @@ namespace ImGui } } - // Lod Min - static ICVar* eLodMinCVAR = gEnv->pConsole->GetCVar("e_LodMin"); - if (eLodMinCVAR) - { - int minLodValue = eLodMinCVAR->GetIVal(); - int dragIntVal = minLodValue; - ImGui::Text("e_LodMin: %d ( Force a lowest LOD level )", minLodValue); - ImGui::SliderInt("##LodMin", &dragIntVal, 0, 5); - if (dragIntVal != minLodValue) - { - eLodMinCVAR->Set(dragIntVal); - } - } - // Texel Density static ICVar* eTexelDensityCVAR = gEnv->pConsole->GetCVar("e_texeldensity"); if (eTexelDensityCVAR) diff --git a/Tools/LyTestTools/ly_test_tools/launchers/platforms/android/launcher.py b/Tools/LyTestTools/ly_test_tools/launchers/platforms/android/launcher.py index 42adee91ea..75eaa65e94 100755 --- a/Tools/LyTestTools/ly_test_tools/launchers/platforms/android/launcher.py +++ b/Tools/LyTestTools/ly_test_tools/launchers/platforms/android/launcher.py @@ -208,13 +208,6 @@ class AndroidLauncher(Launcher): with self.android_vfs_setreg_path.open('w') as android_vfs_setreg: json.dump(vfs_settings, android_vfs_setreg, indent=4) - self.workspace.settings.modify_platform_setting('r_AssetProcessorShaderCompiler', 1) - self.workspace.settings.modify_platform_setting('r_ShadersAsyncCompiling', 0) - self.workspace.settings.modify_platform_setting('r_ShadersRemoteCompiler', 1) - self.workspace.settings.modify_platform_setting('r_ShadersAllowCompilation', 1) - self.workspace.settings.modify_platform_setting('r_ShadersAsyncActivation', 0) - self.workspace.settings.modify_platform_setting('r_ShaderCompilerServer', '127.0.0.1') - self.workspace.settings.modify_platform_setting('r_ShaderCompilerPort', '61453') self.workspace.settings.modify_platform_setting("log_RemoteConsoleAllowedAddresses", '127.0.0.1') def launch(self): diff --git a/Tools/LyTestTools/ly_test_tools/launchers/platforms/linux/launcher.py b/Tools/LyTestTools/ly_test_tools/launchers/platforms/linux/launcher.py index 210519d197..047e4fdeef 100644 --- a/Tools/LyTestTools/ly_test_tools/launchers/platforms/linux/launcher.py +++ b/Tools/LyTestTools/ly_test_tools/launchers/platforms/linux/launcher.py @@ -172,7 +172,6 @@ class LinuxLauncher(Launcher): self.args.append('--regset="/Amazon/AzCore/Bootstrap/wait_for_connect=1"') self.args.append(f'--regset="/Amazon/AzCore/Bootstrap/allowed_list={host_ip}"') - self.workspace.settings.modify_platform_setting("r_AssetProcessorShaderCompiler", 1) self.workspace.settings.modify_platform_setting("r_ShaderCompilerServer", host_ip) self.workspace.settings.modify_platform_setting("log_RemoteConsoleAllowedAddresses", host_ip) diff --git a/Tools/LyTestTools/ly_test_tools/launchers/platforms/win/launcher.py b/Tools/LyTestTools/ly_test_tools/launchers/platforms/win/launcher.py index 4f904b0a40..d18431ba82 100755 --- a/Tools/LyTestTools/ly_test_tools/launchers/platforms/win/launcher.py +++ b/Tools/LyTestTools/ly_test_tools/launchers/platforms/win/launcher.py @@ -172,8 +172,6 @@ class WinLauncher(Launcher): self.args.append('--regset="/Amazon/AzCore/Bootstrap/wait_for_connect=1"') self.args.append(f'--regset="/Amazon/AzCore/Bootstrap/allowed_list={host_ip}"') - self.workspace.settings.modify_platform_setting("r_AssetProcessorShaderCompiler", 1) - self.workspace.settings.modify_platform_setting("r_ShaderCompilerServer", host_ip) self.workspace.settings.modify_platform_setting("log_RemoteConsoleAllowedAddresses", host_ip) diff --git a/Tools/LyTestTools/tests/unit/test_launcher_android.py b/Tools/LyTestTools/tests/unit/test_launcher_android.py index d2f700e35e..d1adccb096 100755 --- a/Tools/LyTestTools/tests/unit/test_launcher_android.py +++ b/Tools/LyTestTools/tests/unit/test_launcher_android.py @@ -238,7 +238,7 @@ class TestAndroidLauncher: launcher = ly_test_tools.launchers.AndroidLauncher(mock_workspace, ["dummy"]) launcher.configure_settings() - assert mock_workspace.settings.modify_platform_setting.call_count == 8 + assert mock_workspace.settings.modify_platform_setting.call_count == 1 @mock.patch('ly_test_tools.launchers.platforms.base.Launcher._config_ini_to_dict') @mock.patch('ly_test_tools.environment.process_utils.check_output') diff --git a/cmake/Tools/layout_tool.py b/cmake/Tools/layout_tool.py index 62e3922770..8093f72e44 100755 --- a/cmake/Tools/layout_tool.py +++ b/cmake/Tools/layout_tool.py @@ -120,10 +120,7 @@ def verify_layout(layout_dir, platform_name, project_path, asset_mode, asset_typ warning_count += _warn(f"'system_{platform_name_lower}_{asset_type}.cfg' is missing from {str(layout_path)}") system_config_values = None else: - system_config_values = common.get_config_file_values(str(platform_system_cfg_file), ['r_ShadersRemoteCompiler', - 'r_ShadersAllowCompilation', - 'r_AssetProcessorShaderCompiler', - 'r_ShaderCompilerServer']) + system_config_values = common.get_config_file_values(str(platform_system_cfg_file), []) if bootstrap_values: @@ -146,24 +143,23 @@ def verify_layout(layout_dir, platform_name, project_path, asset_mode, asset_typ elif system_config_values is not None: - shaders_remote_compiler = system_config_values.get('r_ShadersRemoteCompiler') or '0' + shaders_remote_compiler = '0' asset_processor_shader_compiler = system_config_values.get('r_AssetProcessorShaderCompiler') or '0' - shader_compiler_server = system_config_values.get('r_ShaderCompilerServer') or LOCAL_HOST + shader_compiler_server = LOCAL_HOST shaders_allow_compilation = system_config_values.get('r_ShadersAllowCompilation') def _validate_remote_shader_settings(): - if shader_compiler_server == LOCAL_HOST: - if asset_processor_shader_compiler != '1': - return _warn(f"Connection to the remote shader compiler (r_ShaderCompilerServer) is not properly " - f"set in system_{platform_name_lower}_{asset_type}.cfg. If it is set to {LOCAL_HOST}, then " - f"r_AssetProcessorShaderCompiler must be set to 1.") + if asset_processor_shader_compiler != '1': + return _warn(f"Connection to the remote shader compiler (r_ShaderCompilerServer) is not properly " + f"set in system_{platform_name_lower}_{asset_type}.cfg. If it is set to {LOCAL_HOST}, then " + f"r_AssetProcessorShaderCompiler must be set to 1.") - else: - if _validate_remote_ap(remote_ip, remote_connect, False) > 0: - return _warn(f"The system_{platform_name_lower}_{asset_type}.cfg file is configured to connect to the" - f" shader compiler server through the remote connection to the Asset Processor.") + else: + if _validate_remote_ap(remote_ip, remote_connect, False) > 0: + return _warn(f"The system_{platform_name_lower}_{asset_type}.cfg file is configured to connect to the" + f" shader compiler server through the remote connection to the Asset Processor.") return 0 # Validation steps based on the asset mode @@ -181,16 +177,9 @@ def verify_layout(layout_dir, platform_name, project_path, asset_mode, asset_typ warning_count += _warn("No pak files found for PAK mode deployment") # Check if the shader paks are set if has_shader_pak: - # If the shader paks are set, make sure that the remote shader compiler connection settings are set - # or that it is going through AP - if shaders_remote_compiler == '1': - warning_count += _warn(f"Shader paks are set for project {project_name} but remote shader compiling " - f"(r_ShadersRemoteCompiler) is still enabled " - f"for it in system_{platform_name_lower}_{asset_type}.cfg.") - else: - # Since we are not connecting to the shader compiler, also make sure bootstrap is not configured to - # connect to Asset Processor remotely - warning_count += _validate_remote_ap(remote_ip, remote_connect, False) + # Since we are not connecting to the shader compiler, also make sure bootstrap is not configured to + # connect to Asset Processor remotely + warning_count += _validate_remote_ap(remote_ip, remote_connect, False) if shaders_allow_compilation is not None and shaders_allow_compilation == '1': warning_count += _warn(f"Shader paks are set for project {project_name} but shader compiling " diff --git a/editor.cfg b/editor.cfg index 843a52824d..a8719590c5 100644 --- a/editor.cfg +++ b/editor.cfg @@ -1,33 +1,12 @@ -- Settings stored here are only used in the Editor --- Disable the Missing Asset Resolver by default -ed_MissingAssetResolver = 0 -e_ShadowsCache=0 -r_MotionBlur=0 -r_HDRVignetting=0 --- For feature-test compatibility -mn_FatalErrorOnInvalidEntity=0 - -- Do not warn on Pak file access issues sys_PakWarnOnPakAccessFailures=0 --- By default, disable any possible stereo output that might have been enabled via a GEM/other config file so that --- the editor does not startup in stereo mode (which prevents actually editing the environment) -r_StereoMode=0 -r_StereoOutput=0 - -- When editing terrain in the editor, the highest-detail octree nodes for any edited sector will be rendered until -- the level is exported and saved, which can cause an artificial increase in the number of nodes that can get -- queued for visibility checks. These numbers need to be set high enough to account for those increases. --- The CheckOcclusionQueueSize should be at least (terrain height * terrain width) / (32 * 32) in size. --- (Each queue entry is 64 bytes of RAM) -e_CheckOcclusionQueueSize=32768 - --- The CheckOcclusionOutputQueueSize should be at least double the above queue size for safety. --- (Each queue entry is 64 bytes of RAM) -e_CheckOcclusionOutputQueueSize=65536 - -- Enable warnings when asset loads take longer than the given millisecond threshold cl_assetLoadWarningEnable=true cl_assetLoadWarningMsThreshold=100 diff --git a/system_android_android.cfg b/system_android_android.cfg index d5bfddeb73..10ab95abc4 100644 --- a/system_android_android.cfg +++ b/system_android_android.cfg @@ -6,40 +6,14 @@ sys_PakLogInvalidFileAccess=1 r_WidthAndHeightAsFractionOfScreenSize=1.0 r_MaxWidth=1280 r_MaxHeight=1080 - r_fullscreen=0 +r_ShadersAllowCompilation=1 -- Enable to prevent log spam, can cause missed messages -- log_spamdelay=1 --- DXGL ---r_Batching=0 - --- Enabling aync shader compiling causes a hang on some Android devices. -r_ShadersAsyncCompiling=0 -r_ShadersRemoteCompiler=1 -r_ShadersAllowCompilation=1 -r_ShadersAsyncActivation=0 - --- Use the Asset Processor to route requests to compile shaders. This enables the Asset Processor --- to forward the request to the real shader compiler server. Your device is no --- longer required to be on the same network as the shader compiler server, as long as the device can contact the Asset Processor: -r_AssetProcessorShaderCompiler=1 - --- If you run your device locally on the same computer as the shader compiler server, you can use 127.0.0.1. --- To connect to a shader compiler server that runs on another computer, change localhost to the IP address of that computer (61453 is the default port): -r_ShaderCompilerServer=127.0.0.1 - --- For Shader Compiler server running on other machines - 61453 is the default port -r_ShaderCompilerPort=61453 - --- Spec level: 0 = auto, 1 = low, 2 = medium, 3 = high, 4 = very high. -r_GraphicsQuality = 2 - -s_FileCacheManagerSize=262144 - -- Remote console inclusion list log_RemoteConsoleAllowedAddresses=127.0.0.1 -- Localization Settings -sys_localization_format=0 \ No newline at end of file +sys_localization_format=0 diff --git a/system_ios_ios.cfg b/system_ios_ios.cfg index 190ad4a121..abf08d0209 100644 --- a/system_ios_ios.cfg +++ b/system_ios_ios.cfg @@ -3,26 +3,7 @@ ------------------------ r_FullScreen=1 - r_ShadersAllowCompilation=1 -r_ShadersAsyncActivation=0 -r_ShadersAsyncCompiling=0 -r_ShadersRemoteCompiler=1 -r_ShadersUseLLVMDirectXCompiler=1 - --- Spec level: 0 = auto, 1 = low, 2 = medium, 3 = high, 4 = very high. -r_GraphicsQuality = 0 - --- Use the Asset Processor to route requests to compile shaders. This enables the Asset Processor --- to forward the request to the real shader compiler server. Your device is no --- longer required to be on the same network as the shader compiler server, as long as the device can contact the Asset Processor: -r_AssetProcessorShaderCompiler=1 - --- If you run your device locally on the same computer as the shader compiler server, you can use 127.0.0.1. --- To connect to a shader compiler server that runs on another computer, change localhost to the IP address of that computer (61453 is the default port): -r_ShaderCompilerServer=127.0.0.1 - ---r_ShaderCompilerPort=61453 ------------------------ -- System @@ -39,16 +20,6 @@ sys_physics_CPU=0 -- log_spamdelay=1 log_IncludeTime=1 ------------------------- --- Audio ------------------------- -s_FileCacheManagerSize=262144 ------------------------- --- Auxiliary Geometry ------------------------- -r_enableAuxGeom=0 -r_auxGeom=0 - -- Remote console inclusion list log_RemoteConsoleAllowedAddresses=127.0.0.1 diff --git a/system_linux_pc.cfg b/system_linux_pc.cfg index 614bc44b49..5fec6b8dfd 100644 --- a/system_linux_pc.cfg +++ b/system_linux_pc.cfg @@ -10,8 +10,6 @@ sys_PakLogInvalidFileAccess = 0 -- Remote console inclusion list log_RemoteConsoleAllowedAddresses=127.0.0.1 -gm_disconnectDetection = 1 - -- Localization Settings sys_localization_format=0 @@ -20,27 +18,7 @@ r_width = 1280 r_height = 720 r_fullscreen = 0 -r_ShadersAsyncCompiling = 3 -r_ShadersAsyncActivation = 3 -r_ShadersAsyncMaxThreads = 16 -r_ShadersRemoteCompiler = 1 r_ShadersAllowCompilation = 1 --- Use the Asset Processor to route requests to compile shaders. This enables the Asset Processor --- to forward the request to the real shader compiler server. Your device is no --- longer required to be on the same network as the shader compiler server, as long as the device can contact the Asset Processor: -r_AssetProcessorShaderCompiler=0 - --- If you run your device locally on the same computer as the shader compiler server, you can use 127.0.0.1. --- To connect to a shader compiler server that runs on another computer, change localhost to the IP address of that computer (61453 is the default port): ---r_ShaderCompilerServer=127.0.0.1 ---r_ShaderCompilerPort = 61453 - --- Spec level: 0 = auto, 1 = low, 2 = medium, 3 = high, 4 = very high. Autodetection not yet implemented for this platform. -r_GraphicsQuality = 1 - --- Texture streaming not supported on opengl -r_TexturesStreaming=0 - -- Display FPS r_displayInfo = 3 diff --git a/system_mac_mac.cfg b/system_mac_mac.cfg index 22fbca6e1e..ef145c890e 100644 --- a/system_mac_mac.cfg +++ b/system_mac_mac.cfg @@ -3,9 +3,6 @@ sys_float_exceptions=0 log_IncludeTime=1 sys_PakLogInvalidFileAccess=0 --- Spec level: 0 = auto, 1 = low, 2 = medium, 3 = high, 4 = very high. Currently overwritten by hardcoded value for this platform. -r_GraphicsQuality = 0 - r_width=1280 r_height=720 r_fullscreen=0 @@ -13,25 +10,7 @@ r_fullscreen=0 -- Enable to prevent log spam, can cause missed messages -- log_spamdelay=1 --- DXGL -r_Batching=0 - -r_ShadersAsyncCompiling=0 -r_ShadersRemoteCompiler=1 r_ShadersAllowCompilation=1 -r_ShadersAsyncActivation=0 -r_ShadersUseLLVMDirectXCompiler=1 - --- Use the Asset Processor to route requests to compile shaders. This enables the Asset Processor --- to forward the request to the real shader compiler server. Your device is no --- longer required to be on the same network as the shader compiler server, as long as the device can contact the Asset Processor: -r_AssetProcessorShaderCompiler=1 - --- If you run your device locally on the same computer as the shader compiler server, you can use 127.0.0.1. --- To connect to a shader compiler server that runs on another computer, change localhost to the IP address of that computer (61453 is the default port): -r_ShaderCompilerServer=127.0.0.1 - ---r_ShaderCompilerPort=61453 -- Remote console inclusion list log_RemoteConsoleAllowedAddresses=127.0.0.1 diff --git a/system_windows_pc.cfg b/system_windows_pc.cfg index 05210e02e0..aa53ed9323 100644 --- a/system_windows_pc.cfg +++ b/system_windows_pc.cfg @@ -11,27 +11,7 @@ r_fullscreen = 0 -- Enable to prevent log spam, can cause missed messages -- log_spamdelay=1 -r_ShadersAsyncCompiling = 3 -r_ShadersAsyncActivation = 3 -r_ShadersAsyncMaxThreads = 16 -r_ShadersRemoteCompiler = 0 r_ShadersAllowCompilation = 1 --- Use the Asset Processor to route requests to compile shaders. This enables the Asset Processor --- to forward the request to the real shader compiler server. Your device is no --- longer required to be on the same network as the shader compiler server, as long as the device can contact the Asset Processor: -r_AssetProcessorShaderCompiler=0 - --- If you run your device locally on the same computer as the shader compiler server, you can use 127.0.0.1. --- To connect to a shader compiler server that runs on another computer, change localhost to the IP address of that computer (61453 is the default port): -r_ShaderCompilerServer=127.0.0.1 ---r_ShaderCompilerPort = 61453 - --- Spec level: 0 = auto, 1 = low, 2 = medium, 3 = high, 4 = very high. Autodetection not yet implemented for this platform. -r_GraphicsQuality = 4 - ---r_driver=GL ---r_ShadersGL4=1 - -- Localization Settings sys_localization_format=0 From 70636572ff259873a19dd6f8c384b3025c399df1 Mon Sep 17 00:00:00 2001 From: Steve Pham <82231385+spham-amzn@users.noreply.github.com> Date: Tue, 12 Oct 2021 15:08:07 -0700 Subject: [PATCH 18/65] Fix casing of editor_xml filenames to match casing in .ly files (#4640) Signed-off-by: Steve Pham --- Code/Editor/CryEditDoc.cpp | 4 ++-- Code/Editor/Util/XmlArchive.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Code/Editor/CryEditDoc.cpp b/Code/Editor/CryEditDoc.cpp index 75bd0ad270..4944c3bff5 100644 --- a/Code/Editor/CryEditDoc.cpp +++ b/Code/Editor/CryEditDoc.cpp @@ -1273,7 +1273,7 @@ bool CCryEditDoc::SaveLevel(const QString& filename) if (savedEntities) { AZ_PROFILE_SCOPE(AzToolsFramework, "CCryEditDoc::SaveLevel Updated PakFile levelEntities.editor_xml"); - pakFile.UpdateFile("LevelEntities.editor_xml", entitySaveBuffer.begin(), static_cast(entitySaveBuffer.size())); + pakFile.UpdateFile("levelentities.editor_xml", entitySaveBuffer.begin(), static_cast(entitySaveBuffer.size())); // Save XML archive to pak file. bool bSaved = xmlAr.SaveToPak(Path::GetPath(tempSaveFile), pakFile); @@ -1501,7 +1501,7 @@ bool CCryEditDoc::LoadEntitiesFromLevel(const QString& levelPakFile) bool pakOpened = pakSystem->OpenPack(levelPakFile.toUtf8().data()); if (pakOpened) { - const QString entityFilename = Path::GetPath(levelPakFile) + "LevelEntities.editor_xml"; + const QString entityFilename = Path::GetPath(levelPakFile) + "levelentities.editor_xml"; CCryFile entitiesFile; if (entitiesFile.Open(entityFilename.toUtf8().data(), "rt")) diff --git a/Code/Editor/Util/XmlArchive.cpp b/Code/Editor/Util/XmlArchive.cpp index 18c3fc8e63..3a965fb239 100644 --- a/Code/Editor/Util/XmlArchive.cpp +++ b/Code/Editor/Util/XmlArchive.cpp @@ -119,7 +119,7 @@ bool CXmlArchive::SaveToPak([[maybe_unused]] const QString& levelPath, CPakFile& _smart_ptr pXmlStrData = root->getXMLData(5000000); // Save xml file. - QString xmlFilename = "Level.editor_xml"; + QString xmlFilename = "level.editor_xml"; pakFile.UpdateFile(xmlFilename.toUtf8().data(), (void*)pXmlStrData->GetString(), static_cast(pXmlStrData->GetStringLength())); if (pakFile.GetArchive()) @@ -134,7 +134,7 @@ bool CXmlArchive::SaveToPak([[maybe_unused]] const QString& levelPath, CPakFile& ////////////////////////////////////////////////////////////////////////// bool CXmlArchive::LoadFromPak(const QString& levelPath, CPakFile& pakFile) { - QString xmlFilename = QDir(levelPath).absoluteFilePath("Level.editor_xml"); + QString xmlFilename = QDir(levelPath).absoluteFilePath("level.editor_xml"); root = XmlHelpers::LoadXmlFromFile(xmlFilename.toUtf8().data()); if (!root) { From 3b78132833f7537e7f5c107fa38b13987cd91f31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20Semp=C3=A9?= <58790905+lsemp3d@users.noreply.github.com> Date: Tue, 12 Oct 2021 15:45:41 -0700 Subject: [PATCH 19/65] Split StyleHelper.h into a header and source file (#4614) * Split StyleHelper.h into a header and source file Signed-off-by: lsemp3d <58790905+lsemp3d@users.noreply.github.com> * Missing file from commit Signed-off-by: lsemp3d <58790905+lsemp3d@users.noreply.github.com> * Removed incorrect path Signed-off-by: lsemp3d <58790905+lsemp3d@users.noreply.github.com> * Fixed includes Signed-off-by: lsemp3d <58790905+lsemp3d@users.noreply.github.com> * Removed unnecessary comment Signed-off-by: lsemp3d <58790905+lsemp3d@users.noreply.github.com> * Missing include Signed-off-by: lsemp3d <58790905+lsemp3d@users.noreply.github.com> --- .../GraphCanvas/Styling/StyleHelper.cpp | 470 +++++++++++++++++ .../GraphCanvas/Styling/StyleHelper.h | 480 ++---------------- .../Code/graphcanvas_staticlib_files.cmake | 1 + .../ContainerWizard/ContainerWizard.cpp | 1 - .../Tools/UpgradeTool/UpgradeHelper.ui | 3 - 5 files changed, 510 insertions(+), 445 deletions(-) create mode 100644 Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Styling/StyleHelper.cpp diff --git a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Styling/StyleHelper.cpp b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Styling/StyleHelper.cpp new file mode 100644 index 0000000000..6313d6ec17 --- /dev/null +++ b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Styling/StyleHelper.cpp @@ -0,0 +1,470 @@ +/* + * 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 GraphCanvas +{ + namespace Styling + { + + StyleHelper::StyleHelper(const AZ::EntityId& styledEntity) + { + SetStyle(styledEntity); + } + + StyleHelper::StyleHelper(const AZ::EntityId& realStyledEntity, const AZStd::string& virtualChildElement) + { + SetStyle(realStyledEntity, virtualChildElement); + } + + StyleHelper::~StyleHelper() + { + ReleaseStyle(); + } + + void StyleHelper::OnStylesUnloaded() + { + ReleaseStyle(); + } + + void StyleHelper::SetEditorId(const EditorId& editorId) + { + if (m_editorId != editorId) + { + ReleaseStyle(); + m_editorId = editorId; + + RegisterStyleSheetBus(m_editorId); + } + } + + void StyleHelper::SetScene(const AZ::EntityId& sceneId) + { + m_scene = sceneId; + + EditorId editorId; + SceneRequestBus::EventResult(editorId, m_scene, &SceneRequests::GetEditorId); + SetEditorId(editorId); + } + + void StyleHelper::SetStyle(const AZ::EntityId& styledEntity) + { + ReleaseStyle(); + + m_styledEntity = styledEntity; + + AZ::EntityId sceneId; + SceneMemberRequestBus::EventResult(sceneId, m_styledEntity, &SceneMemberRequests::GetScene); + if (!sceneId.IsValid()) + { + return; + } + + SetScene(sceneId); + + for (const auto& selector : m_styleSelectors) + { + StyledEntityRequestBus::Event(m_styledEntity, &StyledEntityRequests::AddSelectorState, selector.c_str()); + } + + UpdateStyle(); + } + + void StyleHelper::SetStyle(const AZStd::string& style) + { + ReleaseStyle(); + + m_deleteStyledEntity = true; + + PseudoElementFactoryRequestBus::BroadcastResult(m_styledEntity, &PseudoElementFactoryRequests::CreateStyleEntity, style); + + for (const auto& selector : m_styleSelectors) + { + StyledEntityRequestBus::Event(m_styledEntity, &StyledEntityRequests::AddSelectorState, selector.c_str()); + } + + // TODO: remove/replace OnSceneSet and fix any systems/components listening for that event. + SceneMemberNotificationBus::Event(m_styledEntity, &SceneMemberNotifications::OnSceneSet, m_scene); + + UpdateStyle(); + + static bool enableDiagnostic = false; + if (enableDiagnostic) + { + AZStd::string description; + StyleRequestBus::EventResult(description, m_style, &StyleRequests::GetDescription); + qDebug() << description.c_str(); + } + } + + void StyleHelper::SetStyle(const AZ::EntityId& parentStyledEntity, const AZStd::string& virtualChildElement) + { + ReleaseStyle(); + + m_deleteStyledEntity = true; + + AZ::EntityId sceneId; + SceneMemberRequestBus::EventResult(sceneId, parentStyledEntity, &SceneMemberRequests::GetScene); + + SetScene(sceneId); + + PseudoElementFactoryRequestBus::BroadcastResult(m_styledEntity, &PseudoElementFactoryRequests::CreateVirtualChild, parentStyledEntity, virtualChildElement); + + for (const auto& selector : m_styleSelectors) + { + StyledEntityRequestBus::Event(m_styledEntity, &StyledEntityRequests::AddSelectorState, selector.c_str()); + } + + UpdateStyle(); + + static bool enableDiagnostic = false; + if (enableDiagnostic) + { + AZStd::string description; + StyleRequestBus::EventResult(description, m_style, &StyleRequests::GetDescription); + qDebug() << description.c_str(); + } + + } + + void StyleHelper::RemoveAttributeOverride(Styling::Attribute attribute) + { + m_attributeOverride.erase(attribute); + } + + bool StyleHelper::HasAttribute(Styling::Attribute attribute) const + { + bool hasAttribute = (m_attributeOverride.find(attribute) != m_attributeOverride.end()); + + if (!hasAttribute) + { + StyleRequestBus::EventResult(hasAttribute, m_style, &StyleRequests::HasAttribute, static_cast(attribute)); + } + + return hasAttribute; + } + + QColor StyleHelper::GetColor(Styling::Attribute color, QColor defaultValue /*= QColor()*/) const + { + return GetAttribute(color, defaultValue); + } + + QFont StyleHelper::GetFont() const + { + QFont font; + QFontInfo info(font); + info.pixelSize(); + + font.setFamily(GetAttribute(Attribute::FontFamily, font.family())); + font.setPixelSize(GetAttribute(Attribute::FontSize, info.pixelSize())); + font.setWeight(GetAttribute(Attribute::FontWeight, font.weight())); + font.setStyle(GetAttribute(Attribute::FontStyle, font.style())); + font.setCapitalization(GetAttribute(Attribute::FontVariant, font.capitalization())); + + return font; + } + + QString StyleHelper::GetFontStyleSheet() const + { + QFont font = GetFont(); + QColor color = GetColor(Styling::Attribute::Color); + + QStringList fields; + + fields.push_back(QString("color: rgba(%1,%2,%3,%4)").arg(color.red()).arg(color.green()).arg(color.blue()).arg(color.alpha())); + + fields.push_back(QString("font-family: %1").arg(font.family())); + fields.push_back(QString("font-size: %1px").arg(font.pixelSize())); + + if (font.bold()) + { + fields.push_back("font-weight: bold"); + } + + switch (font.style()) + { + case QFont::StyleNormal: + break; + case QFont::StyleItalic: + fields.push_back("font-style: italic"); + break; + case QFont::StyleOblique: + fields.push_back("font-style: italic"); + break; + } + + const bool underline = font.underline(); + const bool strikeOut = font.strikeOut(); + + if (underline && strikeOut) + { + fields.push_back("text-decoration: underline line-through"); + } + else if (underline) + { + fields.push_back("text-decoration: underline"); + } + else if (strikeOut) + { + fields.push_back("text-decoration: line-through"); + } + + return fields.join("; "); + } + + QPen StyleHelper::GetPen(Styling::Attribute width, Styling::Attribute style, Styling::Attribute color, Styling::Attribute cap, bool cosmetic /*= false*/) const + { + QPen pen; + pen.setColor(GetAttribute(color, QColor(Qt::black))); + pen.setWidth(GetAttribute(width, 1)); + pen.setStyle(GetAttribute(style, Qt::SolidLine)); + pen.setCapStyle(GetAttribute(cap, Qt::SquareCap)); + pen.setCosmetic(cosmetic); + + return pen; + } + + QPen StyleHelper::GetBorder() const + { + return GetPen(Styling::Attribute::BorderWidth, Styling::Attribute::BorderStyle, Styling::Attribute::BorderColor, Styling::Attribute::CapStyle); + } + + QBrush StyleHelper::GetBrush(Styling::Attribute color, QBrush defaultValue /*= QBrush()*/) const + { + return GetAttribute(color, defaultValue); + } + + QSizeF StyleHelper::GetSize(QSizeF defaultSize) const + { + return{ + GetAttribute(Styling::Attribute::Width, defaultSize.width()), + GetAttribute(Styling::Attribute::Height, defaultSize.height()) + }; + } + + QSizeF StyleHelper::GetMinimumSize(QSizeF defaultSize /*= QSizeF(0,0)*/) const + { + return QSizeF(GetAttribute(Styling::Attribute::MinWidth, defaultSize.width()), GetAttribute(Styling::Attribute::MinHeight, defaultSize.height())); + } + + QSizeF StyleHelper::GetMaximumSize(QSizeF defaultSize /*= QSizeF(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX)*/) const + { + return QSizeF(GetAttribute(Styling::Attribute::MaxWidth, defaultSize.width()), GetAttribute(Styling::Attribute::MaxHeight, defaultSize.height())); + } + + QMarginsF StyleHelper::GetMargins(QMarginsF defaultMargins /*= QMarginsF()*/) const + { + bool hasMargin = false; + StyleRequestBus::EventResult(hasMargin, m_style, &StyleRequests::HasAttribute, static_cast(Styling::Attribute::Margin)); + + if (hasMargin) + { + qreal defaultMargin = GetAttribute(Styling::Attribute::Margin, 0); + defaultMargins = QMarginsF(defaultMargin, defaultMargin, defaultMargin, defaultMargin); + } + + return QMarginsF( + GetAttribute(Styling::Attribute::Margin/*TODO Left*/, defaultMargins.left()), + GetAttribute(Styling::Attribute::Margin/*TODO Top*/, defaultMargins.top()), + GetAttribute(Styling::Attribute::Margin/*TODO Right*/, defaultMargins.right()), + GetAttribute(Styling::Attribute::Margin/*TODO Bottom*/, defaultMargins.bottom()) + ); + } + + bool StyleHelper::HasTextAlignment() const + { + return HasAttribute(Styling::Attribute::TextAlignment) || HasAttribute(Styling::Attribute::TextVerticalAlignment); + } + + Qt::Alignment StyleHelper::GetTextAlignment(Qt::Alignment defaultAlignment) const + { + bool horizontalAlignment = HasAttribute(Styling::Attribute::TextAlignment); + bool verticalAlignment = HasAttribute(Styling::Attribute::TextVerticalAlignment); + + if (horizontalAlignment || verticalAlignment) + { + Qt::Alignment alignment = GetAttribute(Styling::Attribute::TextAlignment, Qt::AlignmentFlag::AlignLeft); + alignment = alignment | GetAttribute(Styling::Attribute::TextVerticalAlignment, Qt::AlignmentFlag::AlignTop); + + return alignment; + } + + return defaultAlignment; + } + + void StyleHelper::AddSelector(const AZStd::string_view& selector) + { + auto insertResult = m_styleSelectors.insert(AZStd::string(selector)); + + if (insertResult.second && m_styledEntity.IsValid()) + { + StyledEntityRequestBus::Event(m_styledEntity, &StyledEntityRequests::AddSelectorState, selector.data()); + + UpdateStyle(); + } + } + + void StyleHelper::RemoveSelector(const AZStd::string_view& selector) + { + AZStd::size_t elements = m_styleSelectors.erase(selector); + + if (elements > 0) + { + StyledEntityRequestBus::Event(m_styledEntity, &StyledEntityRequests::RemoveSelectorState, selector.data()); + + UpdateStyle(); + } + } + + GraphCanvas::CandyStripeConfiguration StyleHelper::GetCandyStripeConfiguration() const + { + CandyStripeConfiguration config; + + config.m_initialOffset = GetAttribute(Styling::Attribute::StripeOffset, 0); + config.m_maximumSize = GetAttribute(Styling::Attribute::MaximumStripeSize, 10); + + if (config.m_maximumSize <= 0) + { + config.m_maximumSize = 1; + } + + config.m_minStripes = GetAttribute(Styling::Attribute::MinimumStripes, 2); + + if (config.m_minStripes <= 0) + { + config.m_minStripes = 1; + } + + config.m_stripeAngle = GetAttribute(Styling::Attribute::StripeAngle, 60); + + if (config.m_stripeAngle > 90) + { + config.m_stripeAngle = 89; + } + else if (config.m_stripeAngle < -90) + { + config.m_stripeAngle = -89; + } + + if (!HasAttribute(Styling::Attribute::StripeColor)) + { + QColor backgroundColor = GetAttribute(Styling::Attribute::BackgroundColor, QColor(0, 0, 0)); + + config.m_stripeColor = backgroundColor.darker(); + + int totalDifference = 0; + + totalDifference += backgroundColor.red() - config.m_stripeColor.red(); + totalDifference += backgroundColor.green() - config.m_stripeColor.green(); + totalDifference += backgroundColor.blue() - config.m_stripeColor.blue(); + + if (totalDifference < 150) + { + config.m_stripeColor = backgroundColor.lighter(); + } + } + else + { + config.m_stripeColor = GetAttribute(Styling::Attribute::StripeColor, QColor(0, 0, 0)); + } + + return config; + } + + GraphCanvas::PatternedFillGenerator StyleHelper::GetPatternedFillGenerator() const + { + PatternedFillGenerator generator; + generator.m_editorId = m_editorId; + + generator.m_id = GetAttribute(Styling::Attribute::PatternTemplate, QString()).toUtf8().data(); + + if (HasAttribute(Styling::Attribute::PatternPalettes)) + { + AZStd::string paletteStrings = GetAttribute(Styling::Attribute::PatternPalettes, QString()).toUtf8().data(); + + AzFramework::StringFunc::Tokenize(paletteStrings.c_str(), generator.m_palettes, ','); + } + else + { + QColor backgroundColor = GetAttribute(Styling::Attribute::BackgroundColor, QColor(0, 0, 0)); + + QColor patternColor = backgroundColor.darker(); + + int totalDifference = 0; + + totalDifference += backgroundColor.red() - patternColor.red(); + totalDifference += backgroundColor.green() - patternColor.green(); + totalDifference += backgroundColor.blue() - patternColor.blue(); + + if (totalDifference < 150) + { + patternColor = backgroundColor.lighter(); + } + + generator.m_colors.push_back(patternColor); + } + + generator.m_configuration = GetPatternFillConfiguration(); + + return generator; + } + + GraphCanvas::PatternFillConfiguration StyleHelper::GetPatternFillConfiguration() const + { + PatternFillConfiguration configuration; + + configuration.m_minimumTileRepetitions = GetAttribute(Styling::Attribute::MinimumRepetitions, 1); + configuration.m_evenRowOffsetPercent = GetAttribute(Styling::Attribute::EvenOffsetPercent, 0.0f); + configuration.m_oddRowOffsetPercent = GetAttribute(Styling::Attribute::OddOffsetPercent, 0.0f); + + return configuration; + } + + void StyleHelper::PopulatePaletteConfiguration(PaletteIconConfiguration& configuration) const + { + AZStd::string stylePalette; + StyledEntityRequestBus::EventResult(stylePalette, m_styledEntity, &StyledEntityRequests::GetFullStyleElement); + + if (!stylePalette.empty()) + { + configuration.SetColorPalette(stylePalette); + } + } + + void StyleHelper::UpdateStyle() + { + ReleaseStyle(false); + StyleManagerRequestBus::EventResult(m_style, m_editorId, &StyleManagerRequests::ResolveStyles, m_styledEntity); + } + + void StyleHelper::ReleaseStyle(bool destroyChildElement /*= true*/) + { + if (m_style.IsValid()) + { + if (m_deleteStyledEntity && destroyChildElement) + { + m_deleteStyledEntity = false; + AZ::ComponentApplicationBus::Broadcast(&AZ::ComponentApplicationRequests::DeleteEntity, m_styledEntity); + } + AZ::ComponentApplicationBus::Broadcast(&AZ::ComponentApplicationRequests::DeleteEntity, m_style); + + m_style.SetInvalid(); + } + } + + void StyleHelper::RegisterStyleSheetBus(const EditorId& editorId) + { + StyleManagerNotificationBus::Handler::BusDisconnect(); + StyleManagerNotificationBus::Handler::BusConnect(editorId); + } + +} +} diff --git a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Styling/StyleHelper.h b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Styling/StyleHelper.h index cc7429f8fc..b500d4b197 100644 --- a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Styling/StyleHelper.h +++ b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Styling/StyleHelper.h @@ -8,7 +8,8 @@ #pragma once -AZ_PUSH_DISABLE_WARNING(4251 4800 4244, "-Wunknown-warning-option") +#if !defined(Q_MOC_RUN) + #include #include #include @@ -16,7 +17,6 @@ AZ_PUSH_DISABLE_WARNING(4251 4800 4244, "-Wunknown-warning-option") #include #include #include -AZ_POP_DISABLE_WARNING #include #include @@ -32,6 +32,8 @@ AZ_POP_DISABLE_WARNING #include #include +#endif + namespace GraphCanvas { namespace Styling @@ -45,124 +47,49 @@ namespace GraphCanvas AZ_CLASS_ALLOCATOR(StyleHelper, AZ::SystemAllocator, 0); StyleHelper() = default; - - StyleHelper(const AZ::EntityId& styledEntity) - { - SetStyle(styledEntity); - } - - StyleHelper(const AZ::EntityId& realStyledEntity, const AZStd::string& virtualChildElement) - { - SetStyle(realStyledEntity, virtualChildElement); - } - - virtual ~StyleHelper() - { - ReleaseStyle(); - } + StyleHelper(const AZ::EntityId& styledEntity); + StyleHelper(const AZ::EntityId& realStyledEntity, const AZStd::string& virtualChildElement); + virtual ~StyleHelper(); // StyleManagerNotificationBus - void OnStylesUnloaded() override - { - ReleaseStyle(); - } + void OnStylesUnloaded() override; //// - void SetEditorId(const EditorId& editorId) - { - if (m_editorId != editorId) - { - ReleaseStyle(); - m_editorId = editorId; + void SetEditorId(const EditorId& editorId); + void SetScene(const AZ::EntityId& sceneId); - RegisterStyleSheetBus(m_editorId); - } - } + void SetStyle(const AZStd::string& style); + void SetStyle(const AZ::EntityId& styledEntity); + void SetStyle(const AZ::EntityId& parentStyledEntity, const AZStd::string& virtualChildElement); - // TODO: Get rid of this and m_scene once the OnSceneSet notification is removed (see below). - void SetScene(const AZ::EntityId& sceneId) - { - m_scene = sceneId; + bool HasAttribute(Styling::Attribute attribute) const; + void RemoveAttributeOverride(Styling::Attribute attribute); - EditorId editorId; - SceneRequestBus::EventResult(editorId, m_scene, &SceneRequests::GetEditorId); - SetEditorId(editorId); - } + QColor GetColor(Styling::Attribute color, QColor defaultValue = QColor()) const; + QFont GetFont() const; - void SetStyle(const AZ::EntityId& styledEntity) - { - ReleaseStyle(); + //! Helper method which constructs a stylesheet based on the calculated font style. + //! We need this too pass along to certain Qt widgets because we use our own custom style parsing system. + QString GetFontStyleSheet() const; - m_styledEntity = styledEntity; + QPen GetPen(Styling::Attribute width, Styling::Attribute style, Styling::Attribute color, Styling::Attribute cap, bool cosmetic = false) const; + QPen GetBorder() const; + QBrush GetBrush(Styling::Attribute color, QBrush defaultValue = QBrush()) const; + QSizeF GetSize(QSizeF defaultSize) const; + QSizeF GetMinimumSize(QSizeF defaultSize = QSizeF(0,0)) const; + QSizeF GetMaximumSize(QSizeF defaultSize = QSizeF(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX)) const; + QMarginsF GetMargins(QMarginsF defaultMargins = QMarginsF()) const; - AZ::EntityId sceneId; - SceneMemberRequestBus::EventResult(sceneId, m_styledEntity, &SceneMemberRequests::GetScene); - if (!sceneId.IsValid()) - { - return; - } + bool HasTextAlignment() const; + Qt::Alignment GetTextAlignment(Qt::Alignment defaultAlignment) const; - SetScene(sceneId); + void AddSelector(const AZStd::string_view& selector); + void RemoveSelector(const AZStd::string_view& selector); - for (const auto& selector : m_styleSelectors) - { - StyledEntityRequestBus::Event(m_styledEntity, &StyledEntityRequests::AddSelectorState, selector.c_str()); - } - - UpdateStyle(); - -#if 0 - AZStd::string description; - StyleRequestBus::EventResult(description, m_style, &StyleRequests::GetDescription); - qDebug() << description.c_str(); -#endif - } - - void SetStyle(const AZStd::string& style) - { - ReleaseStyle(); - - m_deleteStyledEntity = true; - - PseudoElementFactoryRequestBus::BroadcastResult(m_styledEntity, &PseudoElementFactoryRequests::CreateStyleEntity, style); - - for (const auto& selector : m_styleSelectors) - { - StyledEntityRequestBus::Event(m_styledEntity, &StyledEntityRequests::AddSelectorState, selector.c_str()); - } - - // TODO: remove/replace OnSceneSet and fix any systems/components listening for that event. - SceneMemberNotificationBus::Event(m_styledEntity, &SceneMemberNotifications::OnSceneSet, m_scene); - - UpdateStyle(); - } - - void SetStyle(const AZ::EntityId& parentStyledEntity, const AZStd::string& virtualChildElement) - { - ReleaseStyle(); - - m_deleteStyledEntity = true; - - AZ::EntityId sceneId; - SceneMemberRequestBus::EventResult(sceneId, parentStyledEntity, &SceneMemberRequests::GetScene); - - SetScene(sceneId); - - PseudoElementFactoryRequestBus::BroadcastResult(m_styledEntity, &PseudoElementFactoryRequests::CreateVirtualChild, parentStyledEntity, virtualChildElement); - - for (const auto& selector : m_styleSelectors) - { - StyledEntityRequestBus::Event(m_styledEntity, &StyledEntityRequests::AddSelectorState, selector.c_str()); - } - - UpdateStyle(); - -#if 0 - AZStd::string description; - StyleRequestBus::EventResult(description, m_style, &StyleRequests::GetDescription); - qDebug() << description.c_str(); -#endif - } + CandyStripeConfiguration GetCandyStripeConfiguration() const; + PatternedFillGenerator GetPatternedFillGenerator() const; + PatternFillConfiguration GetPatternFillConfiguration() const; + void PopulatePaletteConfiguration(PaletteIconConfiguration& configuration) const; template void AddAttributeOverride(Styling::Attribute attribute, const Value& defaultValue = Value()) @@ -170,23 +97,6 @@ namespace GraphCanvas m_attributeOverride[attribute] = QVariant(defaultValue); } - void RemoveAttributeOverride(Styling::Attribute attribute) - { - m_attributeOverride.erase(attribute); - } - - bool HasAttribute(Styling::Attribute attribute) const - { - bool hasAttribute = (m_attributeOverride.find(attribute) != m_attributeOverride.end()); - - if (!hasAttribute) - { - StyleRequestBus::EventResult(hasAttribute, m_style, &StyleRequests::HasAttribute, static_cast(attribute)); - } - - return hasAttribute; - } - template Value GetAttribute(Styling::Attribute attribute, const Value& defaultValue = Value()) const { @@ -202,7 +112,7 @@ namespace GraphCanvas { bool hasAttribute = false; auto rawAttribute = static_cast(attribute); - + StyleRequestBus::EventResult(hasAttribute, m_style, &StyleRequests::HasAttribute, rawAttribute); if (hasAttribute) @@ -217,323 +127,11 @@ namespace GraphCanvas return retVal; } - QColor GetColor(Styling::Attribute color, QColor defaultValue = QColor()) const - { - return GetAttribute(color, defaultValue); - } - - QFont GetFont() const - { - QFont font; - QFontInfo info(font); - info.pixelSize(); - - font.setFamily(GetAttribute(Attribute::FontFamily, font.family())); - font.setPixelSize(GetAttribute(Attribute::FontSize, info.pixelSize())); - font.setWeight(GetAttribute(Attribute::FontWeight, font.weight())); - font.setStyle(GetAttribute(Attribute::FontStyle, font.style())); - font.setCapitalization(GetAttribute(Attribute::FontVariant, font.capitalization())); - - return font; - } - - //! Helper method which constructs a stylesheet based on the calculated font style. - //! We need this too pass along to certain Qt widgets because we use our own custom style parsing system. - QString GetFontStyleSheet() const - { - QFont font = GetFont(); - QColor color = GetColor(Styling::Attribute::Color); - - QStringList fields; - - fields.push_back(QString("color: rgba(%1,%2,%3,%4)").arg(color.red()).arg(color.green()).arg(color.blue()).arg(color.alpha())); - - fields.push_back(QString("font-family: %1").arg(font.family())); - fields.push_back(QString("font-size: %1px").arg(font.pixelSize())); - - if (font.bold()) - { - fields.push_back("font-weight: bold"); - } - - switch (font.style()) - { - case QFont::StyleNormal: - break; - case QFont::StyleItalic: - fields.push_back("font-style: italic"); - break; - case QFont::StyleOblique: - fields.push_back("font-style: italic"); - break; - } - - const bool underline = font.underline(); - const bool strikeOut = font.strikeOut(); - - if (underline && strikeOut) - { - fields.push_back("text-decoration: underline line-through"); - } - else if (underline) - { - fields.push_back("text-decoration: underline"); - } - else if (strikeOut) - { - fields.push_back("text-decoration: line-through"); - } - - return fields.join("; "); - } - - QPen GetPen(Styling::Attribute width, Styling::Attribute style, Styling::Attribute color, Styling::Attribute cap, bool cosmetic = false) const - { - QPen pen; - pen.setColor(GetAttribute(color, QColor(Qt::black))); - pen.setWidth(GetAttribute(width, 1)); - pen.setStyle(GetAttribute(style, Qt::SolidLine)); - pen.setCapStyle(GetAttribute(cap, Qt::SquareCap)); - pen.setCosmetic(cosmetic); - - return pen; - } - - QPen GetBorder() const - { - return GetPen(Styling::Attribute::BorderWidth, Styling::Attribute::BorderStyle, Styling::Attribute::BorderColor, Styling::Attribute::CapStyle); - } - - QBrush GetBrush(Styling::Attribute color, QBrush defaultValue = QBrush()) const - { - return GetAttribute(color, defaultValue); - } - - QSizeF GetSize(QSizeF defaultSize) const - { - return{ - GetAttribute(Styling::Attribute::Width, defaultSize.width()), - GetAttribute(Styling::Attribute::Height, defaultSize.height()) - }; - } - - QSizeF GetMinimumSize(QSizeF defaultSize = QSizeF(0,0)) const - { - return QSizeF(GetAttribute(Styling::Attribute::MinWidth, defaultSize.width()), GetAttribute(Styling::Attribute::MinHeight, defaultSize.height())); - } - - QSizeF GetMaximumSize(QSizeF defaultSize = QSizeF(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX)) const - { - return QSizeF(GetAttribute(Styling::Attribute::MaxWidth, defaultSize.width()), GetAttribute(Styling::Attribute::MaxHeight, defaultSize.height())); - } - - QMarginsF GetMargins(QMarginsF defaultMargins = QMarginsF()) const - { - bool hasMargin = false; - StyleRequestBus::EventResult(hasMargin, m_style, &StyleRequests::HasAttribute, static_cast(Styling::Attribute::Margin)); - - if (hasMargin) - { - qreal defaultMargin = GetAttribute(Styling::Attribute::Margin, 0); - defaultMargins = QMarginsF(defaultMargin, defaultMargin, defaultMargin, defaultMargin); - } - - return QMarginsF( - GetAttribute(Styling::Attribute::Margin/*TODO Left*/, defaultMargins.left()), - GetAttribute(Styling::Attribute::Margin/*TODO Top*/, defaultMargins.top()), - GetAttribute(Styling::Attribute::Margin/*TODO Right*/, defaultMargins.right()), - GetAttribute(Styling::Attribute::Margin/*TODO Bottom*/, defaultMargins.bottom()) - ); - } - - bool HasTextAlignment() const - { - return HasAttribute(Styling::Attribute::TextAlignment) || HasAttribute(Styling::Attribute::TextVerticalAlignment); - } - - Qt::Alignment GetTextAlignment(Qt::Alignment defaultAlignment) const - { - bool horizontalAlignment = HasAttribute(Styling::Attribute::TextAlignment); - bool verticalAlignment = HasAttribute(Styling::Attribute::TextVerticalAlignment); - - if (horizontalAlignment || verticalAlignment) - { - Qt::Alignment alignment = GetAttribute(Styling::Attribute::TextAlignment, Qt::AlignmentFlag::AlignLeft); - alignment = alignment | GetAttribute(Styling::Attribute::TextVerticalAlignment, Qt::AlignmentFlag::AlignTop); - - return alignment; - } - - return defaultAlignment; - } - - void AddSelector(const AZStd::string_view& selector) - { - auto insertResult = m_styleSelectors.insert(AZStd::string(selector)); - - if (insertResult.second && m_styledEntity.IsValid()) - { - StyledEntityRequestBus::Event(m_styledEntity, &StyledEntityRequests::AddSelectorState, selector.data()); - - UpdateStyle(); - } - } - - void RemoveSelector(const AZStd::string_view& selector) - { - AZStd::size_t elements = m_styleSelectors.erase(selector); - - if (elements > 0) - { - StyledEntityRequestBus::Event(m_styledEntity, &StyledEntityRequests::RemoveSelectorState, selector.data()); - - UpdateStyle(); - } - } - - CandyStripeConfiguration GetCandyStripeConfiguration() const - { - CandyStripeConfiguration config; - - config.m_initialOffset = GetAttribute(Styling::Attribute::StripeOffset, 0); - config.m_maximumSize = GetAttribute(Styling::Attribute::MaximumStripeSize, 10); - - if (config.m_maximumSize <= 0) - { - config.m_maximumSize = 1; - } - - config.m_minStripes = GetAttribute(Styling::Attribute::MinimumStripes, 2); - - if (config.m_minStripes <= 0) - { - config.m_minStripes = 1; - } - - config.m_stripeAngle = GetAttribute(Styling::Attribute::StripeAngle, 60); - - if (config.m_stripeAngle > 90) - { - config.m_stripeAngle = 89; - } - else if (config.m_stripeAngle < -90) - { - config.m_stripeAngle = -89; - } - - if (!HasAttribute(Styling::Attribute::StripeColor)) - { - QColor backgroundColor = GetAttribute(Styling::Attribute::BackgroundColor, QColor(0,0,0)); - - config.m_stripeColor = backgroundColor.darker(); - - int totalDifference = 0; - - totalDifference += backgroundColor.red() - config.m_stripeColor.red(); - totalDifference += backgroundColor.green() - config.m_stripeColor.green(); - totalDifference += backgroundColor.blue() - config.m_stripeColor.blue(); - - if (totalDifference < 150) - { - config.m_stripeColor = backgroundColor.lighter(); - } - } - else - { - config.m_stripeColor = GetAttribute(Styling::Attribute::StripeColor, QColor(0, 0, 0)); - } - - return config; - } - - PatternedFillGenerator GetPatternedFillGenerator() const - { - PatternedFillGenerator generator; - generator.m_editorId = m_editorId; - - generator.m_id = GetAttribute(Styling::Attribute::PatternTemplate, QString()).toUtf8().data(); - - if (HasAttribute(Styling::Attribute::PatternPalettes)) - { - AZStd::string paletteStrings = GetAttribute(Styling::Attribute::PatternPalettes, QString()).toUtf8().data(); - - AzFramework::StringFunc::Tokenize(paletteStrings.c_str(), generator.m_palettes, ','); - } - else - { - QColor backgroundColor = GetAttribute(Styling::Attribute::BackgroundColor, QColor(0, 0, 0)); - - QColor patternColor = backgroundColor.darker(); - - int totalDifference = 0; - - totalDifference += backgroundColor.red() - patternColor.red(); - totalDifference += backgroundColor.green() - patternColor.green(); - totalDifference += backgroundColor.blue() - patternColor.blue(); - - if (totalDifference < 150) - { - patternColor = backgroundColor.lighter(); - } - - generator.m_colors.push_back(patternColor); - } - - generator.m_configuration = GetPatternFillConfiguration(); - - return generator; - } - - PatternFillConfiguration GetPatternFillConfiguration() const - { - PatternFillConfiguration configuration; - - configuration.m_minimumTileRepetitions = GetAttribute(Styling::Attribute::MinimumRepetitions, 1); - configuration.m_evenRowOffsetPercent = GetAttribute(Styling::Attribute::EvenOffsetPercent, 0.0f); - configuration.m_oddRowOffsetPercent = GetAttribute(Styling::Attribute::OddOffsetPercent, 0.0f); - - return configuration; - } - - void PopulatePaletteConfiguration(PaletteIconConfiguration& configuration) const - { - AZStd::string stylePalette; - StyledEntityRequestBus::EventResult(stylePalette, m_styledEntity, &StyledEntityRequests::GetFullStyleElement); - - if (!stylePalette.empty()) - { - configuration.SetColorPalette(stylePalette); - } - } - private: - void UpdateStyle() - { - ReleaseStyle(false); - StyleManagerRequestBus::EventResult(m_style, m_editorId, &StyleManagerRequests::ResolveStyles, m_styledEntity); - } - - void ReleaseStyle(bool destroyChildElement = true) - { - if (m_style.IsValid()) - { - if (m_deleteStyledEntity && destroyChildElement) - { - m_deleteStyledEntity = false; - AZ::ComponentApplicationBus::Broadcast(&AZ::ComponentApplicationRequests::DeleteEntity, m_styledEntity); - } - AZ::ComponentApplicationBus::Broadcast(&AZ::ComponentApplicationRequests::DeleteEntity, m_style); - - m_style.SetInvalid(); - } - } - - void RegisterStyleSheetBus(const EditorId& editorId) - { - StyleManagerNotificationBus::Handler::BusDisconnect(); - StyleManagerNotificationBus::Handler::BusConnect(editorId); - } + void UpdateStyle(); + void ReleaseStyle(bool destroyChildElement = true); + void RegisterStyleSheetBus(const EditorId& editorId); EditorId m_editorId; AZ::EntityId m_scene; diff --git a/Gems/GraphCanvas/Code/graphcanvas_staticlib_files.cmake b/Gems/GraphCanvas/Code/graphcanvas_staticlib_files.cmake index 58a28484f7..bedc6329d5 100644 --- a/Gems/GraphCanvas/Code/graphcanvas_staticlib_files.cmake +++ b/Gems/GraphCanvas/Code/graphcanvas_staticlib_files.cmake @@ -91,6 +91,7 @@ set(FILES StaticLib/GraphCanvas/Styling/Style.cpp StaticLib/GraphCanvas/Styling/Style.h StaticLib/GraphCanvas/Styling/StyleHelper.h + StaticLib/GraphCanvas/Styling/StyleHelper.cpp StaticLib/GraphCanvas/Styling/StyleManager.cpp StaticLib/GraphCanvas/Styling/StyleManager.h StaticLib/GraphCanvas/Types/ComponentSaveDataInterface.h diff --git a/Gems/ScriptCanvas/Code/Editor/View/Dialogs/ContainerWizard/ContainerWizard.cpp b/Gems/ScriptCanvas/Code/Editor/View/Dialogs/ContainerWizard/ContainerWizard.cpp index 940a2626f5..259efef392 100644 --- a/Gems/ScriptCanvas/Code/Editor/View/Dialogs/ContainerWizard/ContainerWizard.cpp +++ b/Gems/ScriptCanvas/Code/Editor/View/Dialogs/ContainerWizard/ContainerWizard.cpp @@ -226,7 +226,6 @@ namespace ScriptCanvasEditor auto dataTypeIter = m_containerDataTypeSets.find(workingCrc); if (dataTypeIter == m_containerDataTypeSets.end()) { - // No idea wtf do here we've managed to put ourselves into an invalid state AZ_Error("ScriptCanvas", false, "Unknown partial type found in Container Creation. Aborting."); close(); break; diff --git a/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/UpgradeHelper.ui b/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/UpgradeHelper.ui index 15998bd83b..b5965c9746 100644 --- a/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/UpgradeHelper.ui +++ b/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/UpgradeHelper.ui @@ -184,8 +184,5 @@ - - - From 1cb26a31f8d51087ae586b356e6a3de72e6e1487 Mon Sep 17 00:00:00 2001 From: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> Date: Tue, 12 Oct 2021 15:53:38 -0700 Subject: [PATCH 20/65] LYN-7195 + LYN-7185 + LYN-5301 | Hide viewport helpers for entities out of focus + selection shortcut adjustments (#4615) * Light refactoring of selection logic. Only draw helpers for selectable entities according to Editor Focus Mode and Container Entity systems. Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> * When Escape is pressed, clear the Prefab Focus. Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> * Alter Ctrl+A and Ctrl+Shift+I to take editor focus mode and container entity behaviors into account. Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> * Remove redundant comments and reduce footprint of tests. Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> * Introduce loop protection, as GetParentId is known to loop in some situations possibly causing timeouts. Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> --- .../ContainerEntityInterface.h | 3 ++ .../ContainerEntitySystemComponent.cpp | 45 +++++++++++++++++++ .../ContainerEntitySystemComponent.h | 1 + .../Entity/EditorEntityHelpers.cpp | 35 +++++++++++++-- .../UI/Prefab/PrefabIntegrationManager.cpp | 7 +++ .../UI/Prefab/PrefabIntegrationManager.h | 12 +++-- .../ViewportSelection/EditorHelpers.cpp | 27 +++++++++-- .../ViewportSelection/EditorHelpers.h | 12 +++++ 8 files changed, 130 insertions(+), 12 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ContainerEntity/ContainerEntityInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ContainerEntity/ContainerEntityInterface.h index 95940e6dd6..2d7d9dc511 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ContainerEntity/ContainerEntityInterface.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ContainerEntity/ContainerEntityInterface.h @@ -65,6 +65,9 @@ namespace AzToolsFramework //! @return An error message if any container was registered for the context, success otherwise. virtual ContainerEntityOperationResult Clear(AzFramework::EntityContextId entityContextId) = 0; + //! Returns true if one of the ancestors of entityId is a closed container entity. + virtual bool IsUnderClosedContainerEntity(AZ::EntityId entityId) const = 0; + }; } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ContainerEntity/ContainerEntitySystemComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ContainerEntity/ContainerEntitySystemComponent.cpp index c5649c56df..61b257a189 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ContainerEntity/ContainerEntitySystemComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ContainerEntity/ContainerEntitySystemComponent.cpp @@ -102,8 +102,17 @@ namespace AzToolsFramework AZ::EntityId ContainerEntitySystemComponent::FindHighestSelectableEntity(AZ::EntityId entityId) const { + if (!entityId.IsValid()) + { + return entityId; + } + + // Return the highest closed container, or the entity if none is found. AZ::EntityId highestSelectableEntityId = entityId; + // Skip the queried entity, as we only want to check its ancestors. + AZ::TransformBus::EventResult(entityId, entityId, &AZ::TransformBus::Events::GetParentId); + // Go up the hierarchy until you hit the root while (entityId.IsValid()) { @@ -152,4 +161,40 @@ namespace AzToolsFramework return AZ::Success(); } + bool ContainerEntitySystemComponent::IsUnderClosedContainerEntity(AZ::EntityId entityId) const + { + if (!entityId.IsValid()) + { + return false; + } + + // Skip the queried entity, as we only want to check its ancestors. + AZ::TransformBus::EventResult(entityId, entityId, &AZ::TransformBus::Events::GetParentId); + + // Go up the hierarchy until you hit the root. + while (entityId.IsValid()) + { + if (!IsContainerOpen(entityId)) + { + // One of the ancestors is a container and it's closed. + return true; + } + + AZ::EntityId parentId; + AZ::TransformBus::EventResult(parentId, entityId, &AZ::TransformBus::Events::GetParentId); + + if (parentId == entityId) + { + // In some circumstances, querying a root level entity with GetParentId will return + // the entity itself instead of an invalid entityId. + break; + } + + entityId = parentId; + } + + // All ancestors are either regular entities or open containers. + return false; + } + } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ContainerEntity/ContainerEntitySystemComponent.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ContainerEntity/ContainerEntitySystemComponent.h index 44261979ef..7a11e05096 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ContainerEntity/ContainerEntitySystemComponent.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ContainerEntity/ContainerEntitySystemComponent.h @@ -48,6 +48,7 @@ namespace AzToolsFramework bool IsContainerOpen(AZ::EntityId entityId) const override; AZ::EntityId FindHighestSelectableEntity(AZ::EntityId entityId) const override; ContainerEntityOperationResult Clear(AzFramework::EntityContextId entityContextId) override; + bool IsUnderClosedContainerEntity(AZ::EntityId entityId) const override; // EditorEntityContextNotificationBus overrides ... void OnEntityStreamLoadSuccess() override; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityHelpers.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityHelpers.cpp index 7789070209..8d40162f52 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityHelpers.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityHelpers.cpp @@ -16,9 +16,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -588,15 +590,40 @@ namespace AzToolsFramework { AZ_PROFILE_FUNCTION(AzToolsFramework); + // Detect if the Entity is Visible bool visible = false; EditorEntityInfoRequestBus::EventResult( visible, entityId, &EditorEntityInfoRequestBus::Events::IsVisible); - bool locked = false; - EditorEntityInfoRequestBus::EventResult( - locked, entityId, &EditorEntityInfoRequestBus::Events::IsLocked); + if (!visible) + { + return false; + } - return visible && !locked; + // Detect if the Entity is Locked + bool locked = false; + EditorEntityInfoRequestBus::EventResult(locked, entityId, &EditorEntityInfoRequestBus::Events::IsLocked); + + if (locked) + { + return false; + } + + // Detect if the Entity is part of the Editor Focus + if (auto focusModeInterface = AZ::Interface::Get(); + !focusModeInterface->IsInFocusSubTree(entityId)) + { + return false; + } + + // Detect if the Entity is a descendant of a closed container + if (auto containerEntityInterface = AZ::Interface::Get(); + containerEntityInterface->IsUnderClosedContainerEntity(entityId)) + { + return false; + } + + return true; } static void SetEntityLockStateRecursively( diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp index d4b227c4ff..9bd377e3e9 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp @@ -137,6 +137,7 @@ namespace AzToolsFramework } EditorContextMenuBus::Handler::BusConnect(); + EditorEventsBus::Handler::BusConnect(); PrefabInstanceContainerNotificationBus::Handler::BusConnect(); AZ::Interface::Register(this); AssetBrowser::AssetBrowserSourceDropBus::Handler::BusConnect(s_prefabFileExtension); @@ -147,6 +148,7 @@ namespace AzToolsFramework AssetBrowser::AssetBrowserSourceDropBus::Handler::BusDisconnect(); AZ::Interface::Unregister(this); PrefabInstanceContainerNotificationBus::Handler::BusDisconnect(); + EditorEventsBus::Handler::BusDisconnect(); EditorContextMenuBus::Handler::BusDisconnect(); } @@ -313,6 +315,11 @@ namespace AzToolsFramework } } + void PrefabIntegrationManager::OnEscape() + { + s_prefabFocusInterface->FocusOnOwningPrefab(AZ::EntityId()); + } + void PrefabIntegrationManager::HandleSourceFileType(AZStd::string_view sourceFilePath, AZ::EntityId parentId, AZ::Vector3 position) const { auto instantiatePrefabOutcome = s_prefabPublicInterface->InstantiatePrefab(sourceFilePath, parentId, position); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.h index 696c05991c..6788af31e9 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.h @@ -51,6 +51,7 @@ namespace AzToolsFramework class PrefabIntegrationManager final : public EditorContextMenuBus::Handler + , public EditorEventsBus::Handler , public AssetBrowser::AssetBrowserSourceDropBus::Handler , public PrefabInstanceContainerNotificationBus::Handler , public PrefabIntegrationInterface @@ -64,19 +65,22 @@ namespace AzToolsFramework static void Reflect(AZ::ReflectContext* context); - // EditorContextMenuBus... + // EditorContextMenuBus overrides ... int GetMenuPosition() const override; AZStd::string GetMenuIdentifier() const override; void PopulateEditorGlobalContextMenu(QMenu* menu, const AZ::Vector2& point, int flags) override; - // EntityOutlinerSourceDropHandlingBus... + // EditorEventsBus overrides ... + void OnEscape(); + + // EntityOutlinerSourceDropHandlingBus overrides ... void HandleSourceFileType(AZStd::string_view sourceFilePath, AZ::EntityId parentId, AZ::Vector3 position) const override; - // PrefabInstanceContainerNotificationBus... + // PrefabInstanceContainerNotificationBus overrides ... void OnPrefabComponentActivate(AZ::EntityId entityId) override; void OnPrefabComponentDeactivate(AZ::EntityId entityId) override; - // PrefabIntegrationInterface... + // PrefabIntegrationInterface overrides ... AZ::EntityId CreateNewEntityAtPosition(const AZ::Vector3& position, AZ::EntityId parentId) override; int ExecuteClosePrefabDialog(TemplateId templateId) override; void ExecuteSavePrefabDialog(TemplateId templateId, bool useSaveAllPrefabsPreference) override; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.cpp index 3ce350287f..bb718d0dc9 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.cpp @@ -187,15 +187,14 @@ namespace AzToolsFramework } // Verify if the entity Id corresponds to an entity that is focused; if not, halt selection. - if (!m_focusModeInterface->IsInFocusSubTree(entityIdUnderCursor)) + if (!IsSelectableAccordingToFocusMode(entityIdUnderCursor)) { return AZ::EntityId(); } // Container Entity support - if the entity that is being selected is part of a closed container, // change the selection to the container instead. - ContainerEntityInterface* containerEntityInterface = AZ::Interface::Get(); - if (containerEntityInterface) + if (ContainerEntityInterface* containerEntityInterface = AZ::Interface::Get()) { return containerEntityInterface->FindHighestSelectableEntity(entityIdUnderCursor); } @@ -217,7 +216,7 @@ namespace AzToolsFramework { const AZ::EntityId entityId = m_entityDataCache->GetVisibleEntityId(entityCacheIndex); - if (!m_entityDataCache->IsVisibleEntityVisible(entityCacheIndex)) + if (!m_entityDataCache->IsVisibleEntityVisible(entityCacheIndex) || !IsSelectableInViewport(entityId)) { continue; } @@ -263,4 +262,24 @@ namespace AzToolsFramework } } } + + bool EditorHelpers::IsSelectableInViewport(AZ::EntityId entityId) + { + return IsSelectableAccordingToFocusMode(entityId) && IsSelectableAccordingToContainerEntities(entityId); + } + + bool EditorHelpers::IsSelectableAccordingToFocusMode(AZ::EntityId entityId) + { + return m_focusModeInterface->IsInFocusSubTree(entityId); + } + + bool EditorHelpers::IsSelectableAccordingToContainerEntities(AZ::EntityId entityId) + { + if (ContainerEntityInterface* containerEntityInterface = AZ::Interface::Get()) + { + return !containerEntityInterface->IsUnderClosedContainerEntity(entityId); + } + + return true; + } } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.h index a6a78a4e61..909a231635 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.h @@ -58,7 +58,19 @@ namespace AzToolsFramework AzFramework::DebugDisplayRequests& debugDisplay, const AZStd::function& showIconCheck); + //! Returns whether the entityId can be selected in the viewport according + //! to the current Editor Focus Mode and Container Entity setup. + bool IsSelectableInViewport(AZ::EntityId entityId); + private: + //! Returns whether the entityId can be selected in the viewport according + //! to the current Editor Focus Mode setup. + bool IsSelectableAccordingToFocusMode(AZ::EntityId entityId); + + //! Returns whether the entityId can be selected in the viewport according + //! to the current Container Entityu setup. + bool IsSelectableAccordingToContainerEntities(AZ::EntityId entityId); + const EditorVisibleEntityDataCache* m_entityDataCache = nullptr; //!< Entity Data queried by the EditorHelpers. const FocusModeInterface* m_focusModeInterface = nullptr; }; From 8d97f75e427cd8f4fa63b6e7236c7fd1bea28a5f Mon Sep 17 00:00:00 2001 From: rgba16f <82187279+rgba16f@users.noreply.github.com> Date: Tue, 12 Oct 2021 23:38:01 +0000 Subject: [PATCH 21/65] Fixes to allow cmake to find files inside the render doc linux tar Signed-off-by: rgba16f <82187279+rgba16f@users.noreply.github.com> --- Gems/Atom/RHI/3rdParty/Findrenderdoc.cmake | 4 +++- Gems/Atom/RHI/3rdParty/Platform/Linux/renderdoc_linux.cmake | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Gems/Atom/RHI/3rdParty/Findrenderdoc.cmake b/Gems/Atom/RHI/3rdParty/Findrenderdoc.cmake index f8f17392b3..4fc54b9733 100644 --- a/Gems/Atom/RHI/3rdParty/Findrenderdoc.cmake +++ b/Gems/Atom/RHI/3rdParty/Findrenderdoc.cmake @@ -10,6 +10,8 @@ ly_add_external_target( NAME renderdoc 3RDPARTY_ROOT_DIRECTORY "${LY_RENDERDOC_PATH}" VERSION - INCLUDE_DIRECTORIES . + INCLUDE_DIRECTORIES + . + include COMPILE_DEFINITIONS USE_RENDERDOC ) diff --git a/Gems/Atom/RHI/3rdParty/Platform/Linux/renderdoc_linux.cmake b/Gems/Atom/RHI/3rdParty/Platform/Linux/renderdoc_linux.cmake index a74d250901..6225cc292a 100644 --- a/Gems/Atom/RHI/3rdParty/Platform/Linux/renderdoc_linux.cmake +++ b/Gems/Atom/RHI/3rdParty/Platform/Linux/renderdoc_linux.cmake @@ -6,4 +6,4 @@ # # -set(RENDERDOC_RUNTIME_DEPENDENCIES "${BASE_PATH}/librenderdoc.so") +set(RENDERDOC_RUNTIME_DEPENDENCIES "${BASE_PATH}/lib/librenderdoc.so") From ccd60513f1776231ba3dc1ed1fd512b3d29e972d Mon Sep 17 00:00:00 2001 From: Steve Pham <82231385+spham-amzn@users.noreply.github.com> Date: Tue, 12 Oct 2021 18:34:55 -0700 Subject: [PATCH 22/65] Fix prefab close dialog Editor crash on Linux (#4623) Signed-off-by: Steve Pham --- .../AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp index 9bd377e3e9..24cb71499e 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp @@ -1400,7 +1400,7 @@ namespace AzToolsFramework AZStd::unique_ptr PrefabIntegrationManager::ConstructUnsavedPrefabsCard(TemplateId templateId) { - FlowLayout* unsavedPrefabsLayout = new FlowLayout(AzToolsFramework::GetActiveWindow()); + FlowLayout* unsavedPrefabsLayout = new FlowLayout(nullptr); AZStd::set dirtyTemplatePaths = s_prefabSystemComponentInterface->GetDirtyTemplatePaths(templateId); From 63da5847c105092ebe23d73aefbbfd4b4d1ea086 Mon Sep 17 00:00:00 2001 From: Michael Pollind Date: Tue, 12 Oct 2021 20:15:29 -0700 Subject: [PATCH 23/65] chore: correct documentation and correct method return. - change return for IntersectSegmentTriangleCCW to bool - change return for IntersectSegmentTriangle to bool Signed-off-by: Michael Pollind --- .../AzCore/AzCore/Math/IntersectSegment.cpp | 24 +- .../AzCore/AzCore/Math/IntersectSegment.h | 238 +++++++++--------- 2 files changed, 130 insertions(+), 132 deletions(-) diff --git a/Code/Framework/AzCore/AzCore/Math/IntersectSegment.cpp b/Code/Framework/AzCore/AzCore/Math/IntersectSegment.cpp index 1bf1e41cb7..5d13acc34c 100644 --- a/Code/Framework/AzCore/AzCore/Math/IntersectSegment.cpp +++ b/Code/Framework/AzCore/AzCore/Math/IntersectSegment.cpp @@ -15,7 +15,7 @@ using namespace Intersect; // IntersectSegmentTriangleCCW // [10/21/2009] //========================================================================= -int Intersect::IntersectSegmentTriangleCCW( +bool Intersect::IntersectSegmentTriangleCCW( const Vector3& p, const Vector3& q, const Vector3& a, const Vector3& b, const Vector3& c, /*float &u, float &v, float &w,*/ Vector3& normal, float& t) { @@ -34,7 +34,7 @@ int Intersect::IntersectSegmentTriangleCCW( float d = qp.Dot(normal); if (d <= 0.0f) { - return 0; + return false; } // Compute intersection t value of pq with plane of triangle. A ray @@ -46,7 +46,7 @@ int Intersect::IntersectSegmentTriangleCCW( // range segment check t[0,1] (it this case [0,d]) if (t < 0.0f || t > d) { - return 0; + return false; } // Compute barycentric coordinate components and test if within bounds @@ -54,12 +54,12 @@ int Intersect::IntersectSegmentTriangleCCW( v = ac.Dot(e); if (v < 0.0f || v > d) { - return 0; + return false; } w = -ab.Dot(e); if (w < 0.0f || v + w > d) { - return 0; + return false; } // Segment/ray intersects triangle. Perform delayed division and @@ -72,14 +72,14 @@ int Intersect::IntersectSegmentTriangleCCW( normal.Normalize(); - return 1; + return true; } //========================================================================= // IntersectSegmentTriangle // [10/21/2009] //========================================================================= -int +bool Intersect::IntersectSegmentTriangle( const Vector3& p, const Vector3& q, const Vector3& a, const Vector3& b, const Vector3& c, /*float &u, float &v, float &w,*/ Vector3& normal, float& t) @@ -111,7 +111,7 @@ Intersect::IntersectSegmentTriangle( // so either have a parallel ray or our normal is flipped if (d >= -Constants::FloatEpsilon) { - return 0; // parallel + return false; // parallel } d = -d; e = ap.Cross(qp); @@ -125,19 +125,19 @@ Intersect::IntersectSegmentTriangle( // range segment check t[0,1] (it this case [0,d]) if (t < 0.0f || t > d) { - return 0; + return false; } // Compute barycentric coordinate components and test if within bounds v = ac.Dot(e); if (v < 0.0f || v > d) { - return 0; + return false; } w = -ab.Dot(e); if (w < 0.0f || v + w > d) { - return 0; + return false; } // Segment/ray intersects the triangle. Perform delayed division and @@ -150,7 +150,7 @@ Intersect::IntersectSegmentTriangle( normal.Normalize(); - return 1; + return true; } //========================================================================= diff --git a/Code/Framework/AzCore/AzCore/Math/IntersectSegment.h b/Code/Framework/AzCore/AzCore/Math/IntersectSegment.h index 71c39fb53d..523069987f 100644 --- a/Code/Framework/AzCore/AzCore/Math/IntersectSegment.h +++ b/Code/Framework/AzCore/AzCore/Math/IntersectSegment.h @@ -17,46 +17,45 @@ namespace AZ namespace Intersect { //! LineToPointDistanceTime computes the time of the shortest distance from point 'p' to segment (s1,s2). - //! To calculate the point of intersection: - //! P = s1 + u (s2 - s1) - //! @param s1 segment start point - //! @param s2 segment end point - //! @param p point to find the closest time to. - //! @return time (on the segment) for the shortest distance from 'p' to (s1,s2) [0.0f (s1),1.0f (s2)] + //! To calculate the point of intersection: P = s1 + u (s2 - s1) + //! @param s1 Segment start point. + //! @param s2 Segment end point. + //! @param p Point to find the closest time to. + //! @return Time (on the segment) for the shortest distance from 'p' to (s1,s2) [0.0f (s1),1.0f (s2)] float LineToPointDistanceTime(const Vector3& s1, const Vector3& s21, const Vector3& p); //! LineToPointDistance computes the closest point to 'p' from a segment (s1,s2). - //! @param s1 segment start point - //! @param s2 segment end point - //! @param p point to find the closest time to. - //! @param u time (on the segment) for the shortest distance from 'p' to (s1,s2) [0.0f (s1),1.0f (s2)] - //! @return the closest point + //! @param s1 Segment start point + //! @param s2 Segment end point + //! @param p Point to find the closest time to. + //! @param u Time (on the segment) for the shortest distance from 'p' to (s1,s2) [0.0f (s1),1.0f (s2)] + //! @return The closest point Vector3 LineToPointDistance(const Vector3& s1, const Vector3& s2, const Vector3& p, float& u); //! Given segment pq and triangle abc (CCW), returns whether segment intersects //! triangle and if so, also returns the barycentric coordinates (u,v,w) //! of the intersection point. - //! @param p segment start point - //! @param q segment end point - //! @param a triangle point 1 - //! @param b triangle point 2 - //! @param c triangle point 3 - //! @param normal at the intersection point. - //! @param t time of intersection along the segment [0.0 (p), 1.0 (q)] - //! @return true if the segments intersects the triangle otherwise false - int IntersectSegmentTriangleCCW( + //! @param p Segment start point. + //! @param q Segment end point. + //! @param a Triangle point 1. + //! @param b Triangle point 2. + //! @param c Triangle point 3. + //! @param normal At the intersection point. + //! @param t Time of intersection along the segment [0.0 (p), 1.0 (q)]. + //! @return true if the segments intersects the triangle otherwise false. + bool IntersectSegmentTriangleCCW( const Vector3& p, const Vector3& q, const Vector3& a, const Vector3& b, const Vector3& c, Vector3& normal, float& t); //! Same as \ref IntersectSegmentTriangleCCW without respecting the triangle (a,b,c) vertex order (double sided). - //! @param p segment start point - //! @param q segment end point - //! @param a triangle point 1 - //! @param b triangle point 2 - //! @param c triangle point 3 - //! @param normal at the intersection point; - //! @param t time of intersection along the segment [0.0 (p), 1.0 (q)] - //! @return true if the segments intersects the triangle otherwise false - int IntersectSegmentTriangle( + //! @param p Segment start point. + //! @param q Segment end point. + //! @param a Triangle point 1. + //! @param b Triangle point 2. + //! @param c Triangle point 3. + //! @param normal At the intersection point. + //! @param t Time of intersection along the segment [0.0 (p), 1.0 (q)]. + //! @return True if the segments intersects the triangle otherwise false. + bool IntersectSegmentTriangle( const Vector3& p, const Vector3& q, const Vector3& a, const Vector3& b, const Vector3& c, Vector3& normal, float& t); //! Ray aabb intersection result types. @@ -69,14 +68,14 @@ namespace AZ //! Intersect ray R(t) = rayStart + t*d against AABB a. When intersecting, //! return intersection distance tmin and point q of intersection. - //! @param rayStart ray starting point - //! @param dir ray direction and length (dir = rayEnd - rayStart) + //! @param rayStart Ray starting point + //! @param dir Ray direction and length (dir = rayEnd - rayStart) //! @param dirRCP 1/dir (reciprocal direction - we cache this result very often so we don't need to compute it multiple times, //! otherwise just use dir.GetReciprocal()) //! @param aabb Axis aligned bounding box to intersect against - //! @param tStart time on ray of the first intersection [0,1] or 0 if the ray starts inside the aabb - check the return value - //! @param tEnd time of the of the second intersection [0,1] (it can be > 1 if intersects after the rayEnd) - //! @param startNormal normal at the start point. + //! @param tStart Time on ray of the first intersection [0,1] or 0 if the ray starts inside the aabb - check the return value + //! @param tEnd Time of the of the second intersection [0,1] (it can be > 1 if intersects after the rayEnd) + //! @param startNormal Normal at the start point. //! @return \ref RayAABBIsectTypes RayAABBIsectTypes IntersectRayAABB( const Vector3& rayStart, @@ -88,66 +87,66 @@ namespace AZ Vector3& startNormal); //! Intersect ray against AABB. - //! @param rayStart ray starting point. - //! @param dir ray reciprocal direction. + //! @param rayStart Ray starting point. + //! @param dir Ray reciprocal direction. //! @param aabb Axis aligned bounding box to intersect against. - //! @param start length on ray of the first intersection. - //! @param end length of the of the second intersection. + //! @param start Length on ray of the first intersection. + //! @param end Length of the of the second intersection. //! @return \ref RayAABBIsectTypes In this faster version than IntersectRayAABB we return only ISECT_RAY_AABB_NONE and //! ISECT_RAY_AABB_ISECT. You can check yourself for that case. RayAABBIsectTypes IntersectRayAABB2(const Vector3& rayStart, const Vector3& dirRCP, const Aabb& aabb, float& start, float& end); //! Clip a ray to an aabb. return true if ray was clipped. The ray //! can be inside so don't use the result if the ray intersect the box. - //! @param aabb bounds - //! @param rayStart the start of the ray - //! @param rayEnd the end of the ray - //! @param[out] tClipStart The proportion where the ray enterts the aabb - //! @param[out] tClipEnd The proportion where the ray exits the aabb - //! @return true ray was clipped else false + //! @param aabb Bounds to test against. + //! @param rayStart The start of the ray. + //! @param rayEnd The end of the ray. + //! @param[out] tClipStart The proportion where the ray enters the \ref Aabb. + //! @param[out] tClipEnd The proportion where the ray exits the \ref Aabb. + //! @return True if the ray was clipped, otherwise false. bool ClipRayWithAabb(const Aabb& aabb, Vector3& rayStart, Vector3& rayEnd, float& tClipStart, float& tClipEnd); //! Test segment and aabb where the segment is defined by midpoint //! midPoint = (p1-p0) * 0.5f and half vector halfVector = p1 - midPoint. //! the aabb is at the origin and defined by half extents only. - //! @param midPoint midpoint of a line segment - //! @param halfVector half vector of an aabb - //! @param aabbExtends the extends of a bounded box - //! @return true if the intersect, otherwise false. + //! @param midPoint Midpoint of a line segment. + //! @param halfVector Half vector of an aabb. + //! @param aabbExtends The extends of a bounded box. + //! @return True if the segment and AABB intersect, otherwise false bool TestSegmentAABBOrigin(const Vector3& midPoint, const Vector3& halfVector, const Vector3& aabbExtends); - //! Test if segment specified by points p0 and p1 intersects AABB. \ref TestSegmentAABBOrigin - //! @param p0 point 1 - //! @param p1 point 2 - //! @param aabb bounded box - //! @return true if the segment and AABB intersect, otherwise false. + //! Test if segment specified by points p0 and p1 intersects AABB. \ref TestSegmentAABBOrigin. + //! @param p0 Segment start point. + //! @param p1 Segment end point. + //! @param aabb Bounded box to test against. + //! @return True if the segment and AABB intersect, otherwise false. bool TestSegmentAABB(const Vector3& p0, const Vector3& p1, const Aabb& aabb); //! Ray sphere intersection result types. enum SphereIsectTypes : AZ::s32 { - ISECT_RAY_SPHERE_SA_INSIDE = -1, //!< the ray starts inside the cylinder - ISECT_RAY_SPHERE_NONE, //!< no intersection - ISECT_RAY_SPHERE_ISECT, //!< along the PQ segment + ISECT_RAY_SPHERE_SA_INSIDE = -1, //!< The ray starts inside the cylinder + ISECT_RAY_SPHERE_NONE, //!< No intersection + ISECT_RAY_SPHERE_ISECT, //!< Along the PQ segment }; //! IntersectRaySphereOrigin //! return time t>=0 but not limited, so if you check a segment make sure - //! t <= segmentLen - //! @param rayStart ray start point + //! t <= segmentLen. + //! @param rayStart ray start point. //! @param rayDirNormalized ray direction normalized. - //! @param shereRadius sphere radius + //! @param shereRadius Radius of sphere at origin. //! @param time of closest intersection [0,+INF] in relation to the normalized direction. - //! @return \ref SphereIsectTypes + //! @return \ref SphereIsectTypes. SphereIsectTypes IntersectRaySphereOrigin( const Vector3& rayStart, const Vector3& rayDirNormalized, const float sphereRadius, float& t); //! Intersect ray (rayStart,rayDirNormalized) and sphere (sphereCenter,sphereRadius) \ref IntersectRaySphereOrigin - //! @param rayStart the start of the ray - //! @param rayDirNormalized the direction of the ray normalized - //! @param sphereCenter the center of the sphere - //! @param sphereRadius radius of the sphere - //! @param[out] t coefficient in the ray's explicit equation from which an + //! @param rayStart The start of the ray. + //! @param rayDirNormalized The direction of the ray normalized. + //! @param sphereCenter The center of the sphere. + //! @param sphereRadius Radius of the sphere. + //! @param[out] t Coefficient in the ray's explicit equation from which an //! intersecting point is calculated as "rayOrigin + t1 * rayDir". //! @return SphereIsectTypes SphereIsectTypes IntersectRaySphere( @@ -156,12 +155,12 @@ namespace AZ //! Intersect ray (rayStarty, rayDirNormalized) and disk (center, radius, normal) //! @param rayOrigin The origin of the ray to test. //! @param rayDir The direction of the ray to test. It has to be unit length. - //! @param diskCenter Center point of the disk - //! @param diskRadius Radius of the disk - //! @param diskNormal A normal perpendicular to the disk + //! @param diskCenter Center point of the disk. + //! @param diskRadius Radius of the disk. + //! @param diskNormal A normal perpendicular to the disk. //! @param[out] t If returning 1 (indicating a hit), this contains distance from rayOrigin along the normalized rayDir //! that the hit occured at. - //! @return false if not interesecting and true if intersecting + //! @return False if not interesecting and true if intersecting bool IntersectRayDisk( const Vector3& rayOrigin, const Vector3& rayDir, @@ -215,7 +214,7 @@ namespace AZ //! @param planePos A point on the plane to test intersection with. //! @param planeNormal The normal of the plane to test intersection with. //! @param[out] t The coefficient in the ray's explicit equation from which the intersecting point is calculated as "rayOrigin + t * rayDirection". - //! @return The number of intersection point. + //! @return The number of intersection point. int IntersectRayPlane( const Vector3& rayOrigin, const Vector3& rayDir, const Vector3& planePos, const Vector3& planeNormal, float& t); @@ -230,7 +229,7 @@ namespace AZ //! @param vertexD One of the four points that define the quadrilateral. //! @param[out] t The coefficient in the ray's explicit equation from which the //! intersecting point is calculated as "rayOrigin + t * rayDirection". - //! @return The number of intersection point. + //! @return The number of intersection point. int IntersectRayQuad( const Vector3& rayOrigin, const Vector3& rayDir, @@ -269,7 +268,7 @@ namespace AZ //! @param rayDir The direction of the ray to test intersection with. //! @param obb The OBB to test for intersection with the ray. //! @param[out] t The coefficient in the ray's explicit equation from which the intersecting point is calculated as "rayOrigin + t * rayDirection". - //! @return true if there is an intersection, false otherwise. + //! @return True if there is an intersection, false otherwise. bool IntersectRayObb(const Vector3& rayOrigin, const Vector3& rayDir, const Obb& obb, float& t); //! Ray cylinder intersection types. @@ -284,13 +283,12 @@ namespace AZ //! Reference: Real-Time Collision Detection - 5.3.7 Intersecting Ray or Segment Against Cylinder //! Intersect segment S(t)=sa+t(dir), 0<=t<=1 against cylinder specified by p, q and r. - //! - //! @param sa point - //! @param dir magnitude along sa - //! @param p center point of side 1 cylinder - //! @param q center point of side 2 cylinder - //! @param r radius of cylinder - //! @param[out] t proporition along line segment + //! @param sa The initial point. + //! @param dir Magnitude and direction for sa. + //! @param p Center point of side 1 cylinder. + //! @param q Center point of side 2 cylinder. + //! @param r Radius of cylinder. + //! @param[out] t Proporition along line segment. //! @return CylinderIsectTypes CylinderIsectTypes IntersectSegmentCylinder( const Vector3& sa, const Vector3& dir, const Vector3& p, const Vector3& q, const float r, float& t); @@ -298,22 +296,22 @@ namespace AZ //! Capsule ray intersect types. enum CapsuleIsectTypes { - ISECT_RAY_CAPSULE_SA_INSIDE = -1, //!< the ray starts inside the cylinder - ISECT_RAY_CAPSULE_NONE, //!< no intersection - ISECT_RAY_CAPSULE_PQ, //!< along the PQ segment - ISECT_RAY_CAPSULE_P_SIDE, //!< on the P side - ISECT_RAY_CAPSULE_Q_SIDE, //!< on the Q side + ISECT_RAY_CAPSULE_SA_INSIDE = -1, //!< The ray starts inside the cylinder + ISECT_RAY_CAPSULE_NONE, //!< No intersection + ISECT_RAY_CAPSULE_PQ, //!< Along the PQ segment + ISECT_RAY_CAPSULE_P_SIDE, //!< On the P side + ISECT_RAY_CAPSULE_Q_SIDE, //!< On the Q side }; //! This is a quick implementation of segment capsule based on segment cylinder \ref IntersectSegmentCylinder //! segment sphere intersection. We can optimize it a lot once we fix the ray //! cylinder intersection. - //! @param sa the beginning of the line segment - //! @param dir the direction and length of the segment - //! @param p center point of side 1 capsule - //! @param q center point of side 1 capsule - //! @param r the radius of the capsule - //! @param[out] t proporition along line segment + //! @param sa The beginning of the line segment. + //! @param dir The direction and length of the segment. + //! @param p Center point of side 1 capsule. + //! @param q Center point of side 1 capsule. + //! @param r The radius of the capsule. + //! @param[out] t Proporition along line segment. //! @return CapsuleIsectTypes CapsuleIsectTypes IntersectSegmentCapsule( const Vector3& sa, const Vector3& dir, const Vector3& p, const Vector3& q, const float r, float& t); @@ -321,15 +319,15 @@ namespace AZ //! Intersect segment S(t)=A+t(B-A), 0<=t<=1 against convex polyhedron specified //! by the n halfspaces defined by the planes p[]. On exit tfirst and tlast //! define the intersection, if any. - //! @param sa the beggining of the line segment - //! @param dir the direction and length of the segment - //! @param p planes that compose a convex ponvex polyhedron - //! @param numPlanes number of planes - //! @param[out] tfirst proportion along the line segment where the line enters - //! @param[out] tlast proportion along the line segment where the line exits - //! @param[out] iFirstPlane the plane where the line enters - //! @param[out] iLastPlane the plane where the line exits - //! @return true if intersects else false + //! @param sa The beggining of the line segment. + //! @param dir The direction and length of the segment. + //! @param p Planes that compose a convex ponvex polyhedron. + //! @param numPlanes number of planes. + //! @param[out] tfirst Proportion along the line segment where the line enters. + //! @param[out] tlast Proportion along the line segment where the line exits. + //! @param[out] iFirstPlane The plane where the line enters. + //! @param[out] iLastPlane The plane where the line exits. + //! @return True if intersects else false. bool IntersectSegmentPolyhedron( const Vector3& sa, const Vector3& dir, @@ -345,15 +343,15 @@ namespace AZ //! segment2Proportion where closestPointSegment1 = segment1Start + (segment1Proportion * (segment1End - segment1Start)) //! closestPointSegment2 = segment2Start + (segment2Proportion * (segment2End - segment2Start)) //! If segments are parallel returns a solution. - //! @param segment1Start start of segment 1. - //! @param segment1End end of segment 1. - //! @param segment2Start start of segment 2. - //! @param segment2End end of segment 2. - //! @param[out] segment1Proportion the proporition along segment 1 [0..1] - //! @param[out] segment2Proportion the proporition along segment 2 [0..1] - //! @param[out] closestPointSegment1 closest point on segment 1. - //! @param[out] closestPointSegment2 closest point on segment 2. - //! @param epsilon the minimum square distance where a line segment can be treated as a single point. + //! @param segment1Start Start of segment 1. + //! @param segment1End End of segment 1. + //! @param segment2Start Start of segment 2. + //! @param segment2End End of segment 2. + //! @param[out] segment1Proportion The proporition along segment 1 [0..1] + //! @param[out] segment2Proportion The proporition along segment 2 [0..1] + //! @param[out] closestPointSegment1 Closest point on segment 1. + //! @param[out] closestPointSegment2 Closest point on segment 2. + //! @param epsilon The minimum square distance where a line segment can be treated as a single point. void ClosestSegmentSegment( const Vector3& segment1Start, const Vector3& segment1End, @@ -368,13 +366,13 @@ namespace AZ //! Calculate the line segment closestPointSegment1<->closestPointSegment2 that is the shortest route between //! two segments segment1Start<->segment1End and segment2Start<->segment2End. //! If segments are parallel returns a solution. - //! @param segment1Start start of segment 1. - //! @param segment1End end of segment 1. - //! @param segment2Start start of segment 2. - //! @param segment2End end of segment 2. - //! @param[out] closestPointSegment1 closest point on segment 1. - //! @param[out] closestPointSegment2 closest point on segment 2. - //! @param epsilon the minimum square distance where a line segment can be treated as a single point. + //! @param segment1Start Start of segment 1. + //! @param segment1End End of segment 1. + //! @param segment2Start Start of segment 2. + //! @param segment2End End of segment 2. + //! @param[out] closestPointSegment1 Closest point on segment 1. + //! @param[out] closestPointSegment2 Closest point on segment 2. + //! @param epsilon The minimum square distance where a line segment can be treated as a single point. void ClosestSegmentSegment( const Vector3& segment1Start, const Vector3& segment1End, @@ -387,11 +385,11 @@ namespace AZ //! Calculate the point (closestPointOnSegment) that is the closest point on //! segment segmentStart/segmentEnd to point. Also calculate the value of proportion where //! closestPointOnSegment = segmentStart + (proportion * (segmentEnd - segmentStart)) - //! @param point the point to test - //! @param segmentStart the start of the segment - //! @param segmentEnd the end of the segment - //! @param[out] proportion the proportion of the segment L(t) = (end - start) * t - //! @param[out] closestPointOnSegment the point along the line segment + //! @param point The point to test + //! @param segmentStart The start of the segment + //! @param segmentEnd The end of the segment + //! @param[out] proportion The proportion of the segment L(t) = (end - start) * t + //! @param[out] closestPointOnSegment The point along the line segment void ClosestPointSegment( const Vector3& point, const Vector3& segmentStart, From 2b79abb8c450fefa2932b4676d4b9fccf37156a1 Mon Sep 17 00:00:00 2001 From: Benjamin Jillich <43751992+amzn-jillich@users.noreply.github.com> Date: Wed, 13 Oct 2021 09:13:19 +0200 Subject: [PATCH 24/65] =?UTF-8?q?EMotion=20FX:=20A=20parameter=E2=80=99s?= =?UTF-8?q?=20text=20box=20of=20Blend=20Space=202D=20and=201D=20Motions=20?= =?UTF-8?q?only=20takes=20one=20numerical=20input=20value=20including=20fr?= =?UTF-8?q?actions=20(#4626)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The on value changed event was recevied too often - with every newly typed character. As we're updating elements outside of the value spinbox, we need to wait for the actual done editing signal. This can be achieved by not tracking the keyboard for the spinbox. Signed-off-by: Benjamin Jillich --- .../Editor/PropertyWidgets/BlendSpaceMotionContainerHandler.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Gems/EMotionFX/Code/Source/Editor/PropertyWidgets/BlendSpaceMotionContainerHandler.cpp b/Gems/EMotionFX/Code/Source/Editor/PropertyWidgets/BlendSpaceMotionContainerHandler.cpp index b534603be6..8cee46133e 100644 --- a/Gems/EMotionFX/Code/Source/Editor/PropertyWidgets/BlendSpaceMotionContainerHandler.cpp +++ b/Gems/EMotionFX/Code/Source/Editor/PropertyWidgets/BlendSpaceMotionContainerHandler.cpp @@ -55,6 +55,7 @@ namespace EMotionFX m_spinboxX->setDecimals(4); m_spinboxX->setRange(-FLT_MAX, FLT_MAX); m_spinboxX->setProperty("motionId", motionId.c_str()); + m_spinboxX->setKeyboardTracking(false); layoutX->addWidget(m_spinboxX); layout->addLayout(layoutX, row, column); @@ -76,6 +77,7 @@ namespace EMotionFX m_spinboxY->setDecimals(4); m_spinboxY->setRange(-FLT_MAX, FLT_MAX); m_spinboxY->setProperty("motionId", motionId.c_str()); + m_spinboxX->setKeyboardTracking(false); layoutY->addWidget(m_spinboxY); layout->addLayout(layoutY, row, column); From da1fde8314e58641bcb21ca76987c29a754fc610 Mon Sep 17 00:00:00 2001 From: Benjamin Jillich <43751992+amzn-jillich@users.noreply.github.com> Date: Wed, 13 Oct 2021 09:57:15 +0200 Subject: [PATCH 25/65] ImGui histogram container improvements (#4630) * Added auto scale mode that uses a running average to expand and shrink the visible vertical range. * Added option to set the move direction and either push new values to the front of the buffer (left) and make the histogram move to the right or the inverse. * Changed the camera monitor to use auto scaling as well as the current one was not showing anything because of an outlier. * Pre-fill the histogram with zeros so that the first pushed sample moves in without horizontal scaling effects which made it hard to read out any information from the histogram. Signed-off-by: Benjamin Jillich --- .../Include/LYImGuiUtils/HistogramContainer.h | 40 +++++++-- .../LYCommonMenu/ImGuiLYCameraMonitor.cpp | 20 ++--- .../LYImGuiUtils/HistogramContainer.cpp | 90 ++++++++++++++++--- 3 files changed, 120 insertions(+), 30 deletions(-) diff --git a/Gems/ImGui/Code/Include/LYImGuiUtils/HistogramContainer.h b/Gems/ImGui/Code/Include/LYImGuiUtils/HistogramContainer.h index 101b123bb5..6129c3ec08 100644 --- a/Gems/ImGui/Code/Include/LYImGuiUtils/HistogramContainer.h +++ b/Gems/ImGui/Code/Include/LYImGuiUtils/HistogramContainer.h @@ -18,8 +18,7 @@ namespace ImGui namespace LYImGuiUtils { /** - * A small class to help manage values for an ImGui Histogram ( ImGui doesn't want to manage the values itself ). - * Nothing crazy, just helps reduce boiler plate if you are ImGui::PlotHistogram()'ing + * A small class to help manage values for an ImGui Histogram (ImGui is not managing values itself). */ class HistogramContainer { @@ -40,9 +39,24 @@ namespace ImGui // Static Type to String function static const char* ViewTypeToString(ViewType viewType); + //! Horizontal move direction of the histogram when pushing new values. + enum MoveDirection : AZ::u8 + { + PushLeftMoveRight = 0, //! Push new values to the front of the buffer, which corresponds to the left side, and make the histogram move to the right. + PushRightMoveLeft = 1, //! Push new values to the back of the buffer, which corresponds to the right side, and make the histogram move to the left. + }; + + //! Mode determining the min and max values for the visible range of the vertical axis for the histogram. + enum ScaleMode : AZ::u8 + { + NoAutoScale = 0, //! Use the min and max values given by Init() as visible range. + AutoExpand = 1, //! Expand scale in case a sample is out of the current bounds. Does only expand the scale but not decrease it back again. + AutoScale = 2, //! Use a running average to expand and shrink the visible range. + }; + // Do all of the set up via Init - void Init(const char* histogramName, int maxValueCountSize, ViewType viewType, bool displayOverlays, float minScale, float maxScale - , bool autoExpandScale, bool startCollapsed = false, bool drawMostRecentValue = true); + void Init(const char* histogramName, int maxValueCountSize, ViewType viewType, bool displayOverlays, float minScale, float maxScale, + ScaleMode scaleMode = AutoScale, bool startCollapsed = false, bool drawMostRecentValue = true); // How many values are in the container currently int GetSize() { return static_cast(m_values.size()); } @@ -50,9 +64,6 @@ namespace ImGui // What is the max size of the container int GetMaxSize() { return m_maxSize; } - // Set the Max Size and clear the container - void SetMaxSize(int size) { m_values.clear(); m_maxSize = size; } - // Push a value to this histogram container void PushValue(float val); @@ -65,7 +76,18 @@ namespace ImGui // Draw this histogram with ImGui void Draw(float histogramWidth, float histogramHeight); + //! Adjust the scale mode to determine the min and max values for the visible range of the vertical axis for the histogram. + void SetScaleMode(ScaleMode scaleMode) { m_scaleMode = scaleMode; } + + //! Adjust the horizontal move direction of the histogram when pushing new values. + void SetMoveDirection(MoveDirection moveDirection) { m_moveDirection = moveDirection; } + + //! Calculate the min and maximum values for the present samples. + void CalcMinMaxValues(float& outMin, float& outMax); + private: + // Set the Max Size and clear the container + void SetMaxSize(int size); AZStd::string m_histogramName; AZStd::deque m_values; @@ -73,8 +95,10 @@ namespace ImGui ViewType m_viewType = ViewType::Histogram; float m_minScale; float m_maxScale; + MoveDirection m_moveDirection = PushLeftMoveRight; //! Specify if values will be added on the left and the histogram moves right or the other way around. bool m_dispalyOverlays; - bool m_autoExpandScale; + ScaleMode m_scaleMode; //! Determines if the vertical range of the histogram will be manually specified, auto-expanded or automatically scaled based on the samples. + float m_autoScaleSpeed = 0.05f; //! Indicates how fast the min max values and the visible vertical range are adapting to new samples. bool m_collapsed; bool m_drawMostRecentValueText; }; diff --git a/Gems/ImGui/Code/Source/LYCommonMenu/ImGuiLYCameraMonitor.cpp b/Gems/ImGui/Code/Source/LYCommonMenu/ImGuiLYCameraMonitor.cpp index 1df74903e6..e079a01de1 100644 --- a/Gems/ImGui/Code/Source/LYCommonMenu/ImGuiLYCameraMonitor.cpp +++ b/Gems/ImGui/Code/Source/LYCommonMenu/ImGuiLYCameraMonitor.cpp @@ -34,13 +34,13 @@ namespace ImGui ImGuiCameraMonitorRequestBus::Handler::BusConnect(); // Init Histogram Containers - m_dofMinZHisto.Init( "DOF Min Z", 120, LYImGuiUtils::HistogramContainer::ViewType::Histogram, true, 0.0f, 0.0f, true); - m_dofMinZBlendMultHisto.Init( "DOF Min Z Blend Mult", 120, LYImGuiUtils::HistogramContainer::ViewType::Histogram, true, 50.0f, 50.0f, true); - m_dofMinZScaleHisto.Init( "DOF Min Z Scale", 120, LYImGuiUtils::HistogramContainer::ViewType::Histogram, true, 50.0f, 50.0f, true); + m_dofMinZHisto.Init( "DOF Min Z", 120, LYImGuiUtils::HistogramContainer::ViewType::Histogram, true, 0.0f, 0.0f); + m_dofMinZBlendMultHisto.Init( "DOF Min Z Blend Mult", 120, LYImGuiUtils::HistogramContainer::ViewType::Histogram, true, 50.0f, 50.0f); + m_dofMinZScaleHisto.Init( "DOF Min Z Scale", 120, LYImGuiUtils::HistogramContainer::ViewType::Histogram, true, 50.0f, 50.0f); - m_globalActiveCamInfo.m_fovHisto.Init( "FOV", 120, LYImGuiUtils::HistogramContainer::ViewType::Lines, true, 50.0f, 50.0f, true); - m_globalActiveCamInfo.m_facingVectorDeltaHisto.Init( "Facing Vec Frame Delta", 120, LYImGuiUtils::HistogramContainer::ViewType::Histogram, true, 0.0f, 0.0f, true); - m_globalActiveCamInfo.m_positionDeltaHisto.Init( "Position Frame Delta", 120, LYImGuiUtils::HistogramContainer::ViewType::Histogram, true, 0.0f, 0.0f, true); + m_globalActiveCamInfo.m_fovHisto.Init( "FOV", 120, LYImGuiUtils::HistogramContainer::ViewType::Lines, true, 50.0f, 50.0f); + m_globalActiveCamInfo.m_facingVectorDeltaHisto.Init( "Facing Vec Frame Delta", 120, LYImGuiUtils::HistogramContainer::ViewType::Histogram, true, 0.0f, 0.0f); + m_globalActiveCamInfo.m_positionDeltaHisto.Init( "Position Frame Delta", 120, LYImGuiUtils::HistogramContainer::ViewType::Histogram, true, 0.0f, 0.0f); } void ImGuiLYCameraMonitor::Shutdown() @@ -214,7 +214,7 @@ namespace ImGui } // save this cam off as the current one m_currentCamera = newCamId; - + // create a new empty CameraInfo in the queue m_cameraHistory.push_front(CameraInfo()); @@ -224,9 +224,9 @@ namespace ImGui AZ::ComponentApplicationBus::BroadcastResult(newCam.m_camName, &AZ::ComponentApplicationBus::Events::GetEntityName, m_currentCamera); newCam.m_activeTime = 0.0f; newCam.m_activeFrames = 0; - newCam.m_fovHisto.Init( "FOV", 120, LYImGuiUtils::HistogramContainer::ViewType::Lines, true, 50.0f, 50.0f, true); - newCam.m_facingVectorDeltaHisto.Init( "Facing Vec Frame Delta", 120, LYImGuiUtils::HistogramContainer::ViewType::Histogram, true, 0.0f, 0.0f, true); - newCam.m_positionDeltaHisto.Init( "Position Frame Delta", 120, LYImGuiUtils::HistogramContainer::ViewType::Histogram, true, 0.0f, 0.0f, true); + newCam.m_fovHisto.Init( "FOV", 120, LYImGuiUtils::HistogramContainer::ViewType::Lines, true, 50.0f, 50.0f); + newCam.m_facingVectorDeltaHisto.Init( "Facing Vec Frame Delta", 120, LYImGuiUtils::HistogramContainer::ViewType::Histogram, true, 0.0f, 0.0f); + newCam.m_positionDeltaHisto.Init( "Position Frame Delta", 120, LYImGuiUtils::HistogramContainer::ViewType::Histogram, true, 0.0f, 0.0f); // reset a few variables on the global camera info m_globalActiveCamInfo.m_camId = newCam.m_camId; diff --git a/Gems/ImGui/Code/Source/LYImGuiUtils/HistogramContainer.cpp b/Gems/ImGui/Code/Source/LYImGuiUtils/HistogramContainer.cpp index e3fd3f2054..1915cc3721 100644 --- a/Gems/ImGui/Code/Source/LYImGuiUtils/HistogramContainer.cpp +++ b/Gems/ImGui/Code/Source/LYImGuiUtils/HistogramContainer.cpp @@ -16,45 +16,92 @@ namespace ImGui { namespace LYImGuiUtils { - void HistogramContainer::Init(const char* histogramName, int maxValueCountSize, ViewType viewType, bool displayOverlays, float minScale, float maxScale - , bool autoExpandScale, bool startCollapsed/* = false*/, bool drawMostRecentValue/* = true*/) + void HistogramContainer::Init(const char* histogramName, int maxValueCountSize, ViewType viewType, bool displayOverlays, float minScale, float maxScale, + ScaleMode scaleMode, bool startCollapsed/* = false*/, bool drawMostRecentValue/* = true*/) { m_histogramName = histogramName; m_minScale = minScale; m_maxScale = maxScale; m_viewType = viewType; m_dispalyOverlays = displayOverlays; - m_autoExpandScale = autoExpandScale; + m_scaleMode = scaleMode; m_collapsed = startCollapsed; m_drawMostRecentValueText = drawMostRecentValue; SetMaxSize(maxValueCountSize); } - void HistogramContainer::PushValue(float val) + void HistogramContainer::SetMaxSize(int size) + { + m_values.resize(size); + m_maxSize = size; + + // Pre-fill the histogram with zeros so that the bars do not fill the space and + // scale horizontally when there are not enough samples yet. + for (float& value : m_values) + { + value = 0.0f; + } + } + + void HistogramContainer::PushValue(float value) { if (m_maxSize == 0) { return; } + if (m_values.size() == m_maxSize) { - m_values.pop_back(); + if (m_moveDirection == PushLeftMoveRight) + { + m_values.pop_back(); + } + else + { + m_values.pop_front(); + } } else if (m_values.size() > m_maxSize) { m_values.erase(m_values.begin() + (m_maxSize - 1), m_values.end()); } - m_values.push_front(val); - if (m_autoExpandScale) + if (m_moveDirection == PushLeftMoveRight) { - if (val < m_minScale) + m_values.push_front(value); + } + else + { + m_values.push_back(value); + } + + switch (m_scaleMode) + { + case AutoExpand: { - m_minScale = val; + if (value < m_minScale) + { + m_minScale = value; + } + else if (value > m_maxScale) + { + m_maxScale = value; + } + break; } - else if (val > m_maxScale) + case AutoScale: { - m_maxScale = val; + float min = 0.0f; + float max = 0.0f; + CalcMinMaxValues(min, max); + + m_minScale = AZ::Lerp(m_minScale, min, m_autoScaleSpeed); + m_maxScale = AZ::Lerp(m_maxScale, max, m_autoScaleSpeed); + break; + } + default: + { + break; } } } @@ -86,7 +133,6 @@ namespace ImGui ImGui::DragInt("History Size", &m_maxSize, 1, 1, 1000, "%f"); ImGui::DragFloat("Max Scale", &m_maxScale, 0.0001f, -100.0f, 100.0f); ImGui::DragFloat("Min Scale", &m_minScale, 0.0001f, -100.0f, 100.0f); - ImGui::Checkbox("Auto Expand Scale", &m_autoExpandScale); ImGui::EndPopup(); } @@ -157,6 +203,26 @@ namespace ImGui return "Lines"; } } + + void HistogramContainer::CalcMinMaxValues(float& outMin, float& outMax) + { + // Use the manually set min and max scale values in case there are no samples. + if (m_values.empty()) + { + outMin = m_minScale; + outMax = m_maxScale; + return; + } + + outMin = +AZ::Constants::FloatMax; + outMax = -AZ::Constants::FloatMax; + + for (const float x : m_values) + { + outMin = AZ::GetMin(outMin, x); + outMax = AZ::GetMax(outMax, x); + } + } } } #endif // #ifdef IMGUI_ENABLED From 48a74ca93d7df475a5ffbcfba45ad0fd95fa2604 Mon Sep 17 00:00:00 2001 From: hultonha <82228511+hultonha@users.noreply.github.com> Date: Wed, 13 Oct 2021 10:18:21 +0100 Subject: [PATCH 26/65] Remove clearFocus from EditorTransformComponentSelection duplicate Entity (#4571) * remove clearFocus from EditorTransformComponentSelection duplicate entity Signed-off-by: hultonha * remove extra unneeded calls to RequestWrite Signed-off-by: hultonha * update PrefabPublicHandler to use SetSelectedEntities Signed-off-by: hultonha --- .../Prefab/PrefabPublicHandler.cpp | 7 ++-- .../UI/PropertyEditor/PropertyIntCtrlCommon.h | 5 +-- .../UI/PropertyEditor/PropertyIntSpinCtrl.hxx | 5 --- .../EditorTransformComponentSelection.cpp | 41 ++++++++----------- 4 files changed, 23 insertions(+), 35 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp index 038f36eac9..b5f61b33bf 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp @@ -257,9 +257,10 @@ namespace AzToolsFramework // Select Container Entity { - auto selectionUndo = aznew SelectionCommand({ containerEntityId }, "Select Prefab Container Entity"); + const EntityIdList selectedEntities = EntityIdList{ containerEntityId }; + auto selectionUndo = aznew SelectionCommand(selectedEntities, "Select Prefab Container Entity"); selectionUndo->SetParent(undoBatch.GetUndoBatch()); - ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequestBus::Events::RunRedoSeparately, selectionUndo); + ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequestBus::Events::SetSelectedEntities, selectedEntities); } } @@ -1097,7 +1098,7 @@ namespace AzToolsFramework // Select the duplicated entities/instances auto selectionUndo = aznew SelectionCommand(duplicatedEntityAndInstanceIds, "Select Duplicated Entities/Instances"); selectionUndo->SetParent(undoBatch.GetUndoBatch()); - ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequestBus::Events::RunRedoSeparately, selectionUndo); + ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequestBus::Events::SetSelectedEntities, duplicatedEntityAndInstanceIds); } return AZ::Success(); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyIntCtrlCommon.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyIntCtrlCommon.h index eda8b482a1..01675e0044 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyIntCtrlCommon.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyIntCtrlCommon.h @@ -38,7 +38,7 @@ namespace AzToolsFramework static bool UnsignedToolTip(QWidget* widget, QString& toolTipString); }; - //! Base class for integer widget handlers to provide functionality independant + //! Base class for integer widget handlers to provide functionality independent //! of widget type. //! @tparam ValueType The integer primitive type of the handler. //! @tparam PropertyControl The widget type of the handler. @@ -167,8 +167,7 @@ namespace AzToolsFramework PropertyControl* newCtrl = aznew PropertyControl(pParent); this->connect(newCtrl, &PropertyControl::valueChanged, this, [newCtrl]() { - EBUS_EVENT(PropertyEditorGUIMessages::Bus, RequestWrite, newCtrl); - AzToolsFramework::PropertyEditorGUIMessages::Bus::Broadcast(&PropertyEditorGUIMessages::Bus::Handler::RequestWrite, newCtrl); + AzToolsFramework::PropertyEditorGUIMessages::Bus::Broadcast(&PropertyEditorGUIMessages::Bus::Events::RequestWrite, newCtrl); }); // note: Qt automatically disconnects objects from each other when either end is destroyed, no need to worry about delete. diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyIntSpinCtrl.hxx b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyIntSpinCtrl.hxx index 1e1e6a9592..fef4639a9f 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyIntSpinCtrl.hxx +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyIntSpinCtrl.hxx @@ -98,11 +98,6 @@ namespace AzToolsFramework QWidget* IntSpinBoxHandler::CreateGUI(QWidget* parent) { PropertyIntSpinCtrl* newCtrl = static_cast(BaseHandler::CreateGUI(parent)); - this->connect(newCtrl, &PropertyIntSpinCtrl::valueChanged, [newCtrl]() - { - AzToolsFramework::PropertyEditorGUIMessages::Bus::Broadcast(&PropertyEditorGUIMessages::Bus::Handler::RequestWrite, newCtrl); - }); - return newCtrl; } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp index 7015a25ce1..6ee5c97636 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp @@ -1113,7 +1113,7 @@ namespace AzToolsFramework }); m_boxSelect.InstallLeftMouseUp( - [this, entityBoxSelectData]() + [this, entityBoxSelectData] { entityBoxSelectData->m_boxSelectSelectionCommand->UpdateSelection(EntityIdVectorFromContainer(m_selectedEntityIds)); @@ -2171,7 +2171,7 @@ namespace AzToolsFramework // lock selection AddAction( m_actions, { QKeySequence(Qt::Key_L) }, LockSelection, LockSelectionTitle, LockSelectionDesc, - [lockUnlock]() + [lockUnlock] { lockUnlock(true); }); @@ -2179,7 +2179,7 @@ namespace AzToolsFramework // unlock selection AddAction( m_actions, { QKeySequence(Qt::CTRL + Qt::Key_L) }, UnlockSelection, LockSelectionTitle, LockSelectionDesc, - [lockUnlock]() + [lockUnlock] { lockUnlock(false); }); @@ -2209,7 +2209,7 @@ namespace AzToolsFramework // hide selection AddAction( m_actions, { QKeySequence(Qt::Key_H) }, HideSelection, HideSelectionTitle, HideSelectionDesc, - [showHide]() + [showHide] { showHide(false); }); @@ -2217,7 +2217,7 @@ namespace AzToolsFramework // show selection AddAction( m_actions, { QKeySequence(Qt::CTRL + Qt::Key_H) }, ShowSelection, HideSelectionTitle, HideSelectionDesc, - [showHide]() + [showHide] { showHide(true); }); @@ -2225,7 +2225,7 @@ namespace AzToolsFramework // unlock all entities in the level/scene AddAction( m_actions, { QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_L) }, UnlockAll, UnlockAllTitle, UnlockAllDesc, - []() + [] { AZ_PROFILE_FUNCTION(AzToolsFramework); @@ -2242,14 +2242,14 @@ namespace AzToolsFramework // show all entities in the level/scene AddAction( m_actions, { QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_H) }, ShowAll, ShowAllTitle, ShowAllDesc, - []() + [] { AZ_PROFILE_FUNCTION(AzToolsFramework); ScopedUndoBatch undoBatch(ShowAllEntitiesUndoRedoDesc); EnumerateEditorEntities( - [](AZ::EntityId entityId) + [](const AZ::EntityId entityId) { ScopedUndoBatch::MarkEntityDirty(entityId); SetEntityVisibility(entityId, true); @@ -2259,7 +2259,7 @@ namespace AzToolsFramework // select all entities in the level/scene AddAction( m_actions, { QKeySequence(Qt::CTRL + Qt::Key_A) }, SelectAll, SelectAllTitle, SelectAllDesc, - [this]() + [this] { AZ_PROFILE_FUNCTION(AzToolsFramework); @@ -2299,7 +2299,7 @@ namespace AzToolsFramework // invert current selection AddAction( m_actions, { QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_I) }, InvertSelect, InvertSelectionTitle, InvertSelectionDesc, - [this]() + [this] { AZ_PROFILE_FUNCTION(AzToolsFramework); @@ -2346,17 +2346,10 @@ namespace AzToolsFramework // duplicate selection AddAction( m_actions, { QKeySequence(Qt::CTRL + Qt::Key_D) }, DuplicateSelect, DuplicateTitle, DuplicateDesc, - []() + [] { AZ_PROFILE_FUNCTION(AzToolsFramework); - // Clear Widget selection - Prevents issues caused by cloning entities while a property in the Reflected Property Editor - // is being edited. - if (QApplication::focusWidget()) - { - QApplication::focusWidget()->clearFocus(); - } - ScopedUndoBatch undoBatch(DuplicateUndoRedoDesc); auto selectionCommand = AZStd::make_unique(EntityIdList(), DuplicateUndoRedoDesc); selectionCommand->SetParent(undoBatch.GetUndoBatch()); @@ -2371,7 +2364,7 @@ namespace AzToolsFramework // delete selection AddAction( m_actions, { QKeySequence(Qt::Key_Delete) }, DeleteSelect, DeleteTitle, DeleteDesc, - [this]() + [this] { AZ_PROFILE_FUNCTION(AzToolsFramework); @@ -2388,21 +2381,21 @@ namespace AzToolsFramework AddAction( m_actions, { QKeySequence(Qt::Key_Space) }, EditEscaspe, "", "", - [this]() + [this] { DeselectEntities(); }); AddAction( m_actions, { QKeySequence(Qt::Key_P) }, EditPivot, TogglePivotTitleEditMenu, TogglePivotDesc, - [this]() + [this] { ToggleCenterPivotSelection(); }); AddAction( m_actions, { QKeySequence(Qt::Key_R) }, EditReset, ResetEntityTransformTitle, ResetEntityTransformDesc, - [this]() + [this] { switch (m_mode) { @@ -2427,7 +2420,7 @@ namespace AzToolsFramework AddAction( m_actions, { QKeySequence(Qt::Key_U) }, ViewportUiVisible, "Toggle Viewport UI", "Hide/Show Viewport UI", - [this]() + [this] { SetAllViewportUiVisible(!m_viewportUiVisible); }); @@ -3236,7 +3229,7 @@ namespace AzToolsFramework QAction* action = menu->addAction(QObject::tr(TogglePivotTitleRightClick)); QObject::connect( action, &QAction::triggered, action, - [this]() + [this] { ToggleCenterPivotSelection(); }); From 5bf7330f3505d4ac7837766317fa8ed7ab44ad96 Mon Sep 17 00:00:00 2001 From: amzn-sean <75276488+amzn-sean@users.noreply.github.com> Date: Wed, 13 Oct 2021 12:50:11 +0100 Subject: [PATCH 27/65] made some physX tests less flaky (#4632) Joints_BallLeadFollowerCollide - changed the idle_wait to a wait_for_condition of the lead and follower colliding with a 10 second timeout. It was a 2 second timeout which is on the edge of the time from start to collision (~1.5sec). Joints_HingeNoLimitsConstrained - now measures the angle the joint is at and waits for the follower to raise up and over the lead, or fail after a 10sec timeout. This use to 'catch' the follower joint in a force region and check the position of the follower to determine pass/fail. Signed-off-by: amzn-sean <75276488+amzn-sean@users.noreply.github.com> --- .../joints/Joints_BallLeadFollowerCollide.py | 14 +---- .../joints/Joints_HingeNoLimitsConstrained.py | 59 +++++++++++++++---- .../Joints_HingeNoLimitsConstrained.ly | 4 +- 3 files changed, 54 insertions(+), 23 deletions(-) diff --git a/AutomatedTesting/Gem/PythonTests/Physics/tests/joints/Joints_BallLeadFollowerCollide.py b/AutomatedTesting/Gem/PythonTests/Physics/tests/joints/Joints_BallLeadFollowerCollide.py index 0e8d7ea255..1209cb6caf 100644 --- a/AutomatedTesting/Gem/PythonTests/Physics/tests/joints/Joints_BallLeadFollowerCollide.py +++ b/AutomatedTesting/Gem/PythonTests/Physics/tests/joints/Joints_BallLeadFollowerCollide.py @@ -52,9 +52,6 @@ def Joints_BallLeadFollowerCollide(): from editor_python_test_tools.utils import Report from editor_python_test_tools.utils import TestHelper as helper - import azlmbr.legacy.general as general - import azlmbr.bus - from JointsHelper import JointEntityCollisionAware # Helper Entity class - self.collided flag is set when instance receives collision event. @@ -75,17 +72,12 @@ def Joints_BallLeadFollowerCollide(): lead = Entity("lead") follower = Entity("follower") - # 4) Wait for several seconds - general.idle_wait(2.0) # wait for lead and follower to move + # 4) Wait for collision between lead and follower or timeout + Report.critical_result(Tests.check_collision_happened, helper.wait_for_condition(lambda: lead.collided and follower.collided, 10.0)) - # 5) Check to see if lead and follower behaved as expected - Report.critical_result(Tests.check_collision_happened, lead.collided and follower.collided) - - # 6) Exit Game Mode + # 5) Exit Game Mode helper.exit_game_mode(Tests.exit_game_mode) - - if __name__ == "__main__": from editor_python_test_tools.utils import Report Report.start_test(Joints_BallLeadFollowerCollide) diff --git a/AutomatedTesting/Gem/PythonTests/Physics/tests/joints/Joints_HingeNoLimitsConstrained.py b/AutomatedTesting/Gem/PythonTests/Physics/tests/joints/Joints_HingeNoLimitsConstrained.py index 0870a807fc..0b3f88b4f6 100644 --- a/AutomatedTesting/Gem/PythonTests/Physics/tests/joints/Joints_HingeNoLimitsConstrained.py +++ b/AutomatedTesting/Gem/PythonTests/Physics/tests/joints/Joints_HingeNoLimitsConstrained.py @@ -52,11 +52,13 @@ def Joints_HingeNoLimitsConstrained(): """ import os import sys + import math from editor_python_test_tools.utils import Report from editor_python_test_tools.utils import TestHelper as helper import azlmbr.legacy.general as general import azlmbr.bus + import azlmbr.math as azmath import JointsHelper from JointsHelper import JointEntity @@ -84,28 +86,65 @@ def Joints_HingeNoLimitsConstrained(): Report.info_vector3(lead.position, "lead initial position:") Report.info_vector3(follower.position, "follower initial position:") leadInitialPosition = lead.position - followerInitialPosition = follower.position - # 4) Wait for several seconds - general.idle_wait(4.0) # wait for lead and follower to move + # 4) Wait for the follower to move above and over the lead or Timeout + normalizedStartPos = JointsHelper.getRelativeVector(lead.position, follower.position) + normalizedStartPos = normalizedStartPos.GetNormalizedSafe() + + class WaitCondition: + ANGLE_CHECKPOINT_1 = math.radians(90) + ANGLE_CHECKPOINT_2 = math.radians(200) + + angleAchieved = 0.0 + followerMovedAbove90Deg = False #this is expected to be true to pass the test + followerMovedAbove200Deg = False #this is expected to be true to pass the test + + jointNormal = azmath.Vector3(0.0, -1.0, 0.0) # the joint rotates around the y axis + def checkConditionMet(self): + #calculate the current follower-lead vector + normalVec = JointsHelper.getRelativeVector(lead.position, follower.position) + normalVec = normalVec.GetNormalizedSafe() + + #triple product and get the angle + tripleProduct = normalizedStartPos.dot(normalVec.cross(self.jointNormal)) + currentAngle = math.acos(normalizedStartPos.Dot(normalVec)) + if tripleProduct < 0: + currentAngle = (2*math.pi) - currentAngle + + #if the angle is now less then last time, it is no longer rising, so end the test. + if currentAngle < self.angleAchieved: + return True + + #once we're passed the final check point, end the test + if currentAngle > self.ANGLE_CHECKPOINT_2: + self.followerMovedAbove200Deg = True + return True + + self.angleAchieved = currentAngle + self.followerMovedAbove90Deg = currentAngle > self.ANGLE_CHECKPOINT_1 + return False + + def isFollowerPositionCorrect(self): + return self.followerMovedAbove90Deg and self.followerMovedAbove200Deg + + waitCondition = WaitCondition() + + MAX_WAIT_TIME = 10.0 #seconds + conditionMet = helper.wait_for_condition(lambda: waitCondition.checkConditionMet(), MAX_WAIT_TIME) # 5) Check to see if lead and follower behaved as expected - Report.info_vector3(lead.position, "lead position after 1 second:") - Report.info_vector3(follower.position, "follower position after 1 second:") + Report.info_vector3(lead.position, "lead position after test run:") + Report.info_vector3(follower.position, "follower position after test run:") leadPositionDelta = lead.position.Subtract(leadInitialPosition) leadRemainedStill = JointsHelper.vector3SmallerThanScalar(leadPositionDelta, FLOAT_EPSILON) Report.critical_result(Tests.check_lead_position, leadRemainedStill) - followerSwingedOverLead = (follower.position.x < leadInitialPosition.x and - follower.position.z > leadInitialPosition.z) - Report.critical_result(Tests.check_follower_position, followerSwingedOverLead) + Report.critical_result(Tests.check_follower_position, conditionMet and waitCondition.isFollowerPositionCorrect()) # 6) Exit Game Mode helper.exit_game_mode(Tests.exit_game_mode) - - if __name__ == "__main__": from editor_python_test_tools.utils import Report Report.start_test(Joints_HingeNoLimitsConstrained) diff --git a/AutomatedTesting/Levels/Physics/Joints_HingeNoLimitsConstrained/Joints_HingeNoLimitsConstrained.ly b/AutomatedTesting/Levels/Physics/Joints_HingeNoLimitsConstrained/Joints_HingeNoLimitsConstrained.ly index 86dd941423..9babf84ba4 100644 --- a/AutomatedTesting/Levels/Physics/Joints_HingeNoLimitsConstrained/Joints_HingeNoLimitsConstrained.ly +++ b/AutomatedTesting/Levels/Physics/Joints_HingeNoLimitsConstrained/Joints_HingeNoLimitsConstrained.ly @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:817bd8dd22e185136418b60fba5b3552993687515d3d5ae96791f2c3be907b92 -size 7233 +oid sha256:60276c07b45a734e4f71d695278167ea61e884f8b513906168c9642078ad5954 +size 6045 From 3af07e511764e7d617cfa9b13647405a6954e61f Mon Sep 17 00:00:00 2001 From: Adi Bar-Lev <82479970+Adi-Amazon@users.noreply.github.com> Date: Wed, 13 Oct 2021 09:37:47 -0400 Subject: [PATCH 28/65] Hair - creating parent class for raster pass as prep for ShortCut technique (#4560) Signed-off-by: Adi-Amazon --- .../Code/Passes/HairGeometryRasterPass.cpp | 301 ++++++++++++++++++ .../Code/Passes/HairGeometryRasterPass.h | 112 +++++++ .../Code/Passes/HairPPLLRasterPass.cpp | 269 +--------------- .../Code/Passes/HairPPLLRasterPass.h | 70 +--- Gems/AtomTressFX/Hair_files.cmake | 6 + 5 files changed, 434 insertions(+), 324 deletions(-) create mode 100644 Gems/AtomTressFX/Code/Passes/HairGeometryRasterPass.cpp create mode 100644 Gems/AtomTressFX/Code/Passes/HairGeometryRasterPass.h diff --git a/Gems/AtomTressFX/Code/Passes/HairGeometryRasterPass.cpp b/Gems/AtomTressFX/Code/Passes/HairGeometryRasterPass.cpp new file mode 100644 index 0000000000..7af5903cf3 --- /dev/null +++ b/Gems/AtomTressFX/Code/Passes/HairGeometryRasterPass.cpp @@ -0,0 +1,301 @@ +/* + * 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 +#include +#include + +namespace AZ +{ + namespace Render + { + namespace Hair + { + + // --- Creation & Initialization --- + RPI::Ptr HairGeometryRasterPass::Create(const RPI::PassDescriptor& descriptor) + { + RPI::Ptr pass = aznew HairGeometryRasterPass(descriptor); + return pass; + } + + HairGeometryRasterPass::HairGeometryRasterPass(const RPI::PassDescriptor& descriptor) + : RasterPass(descriptor), + m_passDescriptor(descriptor) + { + // For inherited classes, override this method and set the proper path. + // Example: "Shaders/hairrenderingfillppll.azshader" + SetShaderPath("dummyShaderPath"); + } + + bool HairGeometryRasterPass::AcquireFeatureProcessor() + { + if (m_featureProcessor) + { + return true; + } + + RPI::Scene* scene = GetScene(); + if (scene) + { + m_featureProcessor = scene->GetFeatureProcessor(); + } + else + { + return false; + } + + if (!m_featureProcessor) + { + AZ_Warning("Hair Gem", false, + "HairGeometryRasterPass [%s] - Failed to retrieve Hair feature processor from the scene", + GetName().GetCStr()); + return false; + } + return true; + } + + void HairGeometryRasterPass::InitializeInternal() + { + if (GetScene()) + { + RasterPass::InitializeInternal(); + } + } + + bool HairGeometryRasterPass::IsEnabled() const + { + return (RPI::RasterPass::IsEnabled() && m_initialized) ? true : false; + } + + bool HairGeometryRasterPass::LoadShaderAndPipelineState() + { + RPI::ShaderReloadNotificationBus::Handler::BusDisconnect(); + + const RPI::RasterPassData* passData = RPI::PassUtils::GetPassData(m_passDescriptor); + + // If we successfully retrieved our custom data, use it to set the DrawListTag + if (!passData) + { + AZ_Error("Hair Gem", false, "Missing pass raster data"); + return false; + } + + // Load Shader + const char* shaderFilePath = m_shaderPath.c_str(); + Data::Asset shaderAsset = + RPI::AssetUtils::LoadAssetByProductPath(shaderFilePath, RPI::AssetUtils::TraceLevel::Error); + + if (!shaderAsset.GetId().IsValid()) + { + AZ_Error("Hair Gem", false, "Invalid shader asset for shader '%s'!", shaderFilePath); + return false; + } + + m_shader = RPI::Shader::FindOrCreate(shaderAsset); + if (m_shader == nullptr) + { + AZ_Error("Hair Gem", false, "Pass failed to load shader '%s'!", shaderFilePath); + return false; + } + + // Per Pass Srg + { + // Using 'PerPass' naming since currently RasterPass assumes that the pass Srg is always named 'PassSrg' + // [To Do] - RasterPass should use srg slot index and not name - currently this will + // result in a crash in one of the Atom existing MSAA passes that requires further dive. + // m_shaderResourceGroup = UtilityClass::CreateShaderResourceGroup(m_shader, "HairPerPassSrg", "Hair Gem"); + m_shaderResourceGroup = UtilityClass::CreateShaderResourceGroup(m_shader, "PassSrg", "Hair Gem"); + if (!m_shaderResourceGroup) + { + AZ_Error("Hair Gem", false, "Failed to create the per pass srg"); + return false; + } + } + + const RPI::ShaderVariant& shaderVariant = m_shader->GetVariant(RPI::ShaderAsset::RootShaderVariantStableId); + RHI::PipelineStateDescriptorForDraw pipelineStateDescriptor; + shaderVariant.ConfigurePipelineState(pipelineStateDescriptor); + + RPI::Scene* scene = GetScene(); + if (!scene) + { + AZ_Error("Hair Gem", false, "Scene could not be acquired" ); + return false; + } + RHI::DrawListTag drawListTag = m_shader->GetDrawListTag(); + scene->ConfigurePipelineState(drawListTag, pipelineStateDescriptor); + + pipelineStateDescriptor.m_renderAttachmentConfiguration = GetRenderAttachmentConfiguration(); + pipelineStateDescriptor.m_inputStreamLayout.SetTopology(AZ::RHI::PrimitiveTopology::TriangleList); + pipelineStateDescriptor.m_inputStreamLayout.Finalize(); + + m_pipelineState = m_shader->AcquirePipelineState(pipelineStateDescriptor); + if (!m_pipelineState) + { + AZ_Error("Hair Gem", false, "Pipeline state could not be acquired"); + return false; + } + + RPI::ShaderReloadNotificationBus::Handler::BusConnect(shaderAsset.GetId()); + + m_initialized = true; + return true; + } + + void HairGeometryRasterPass::SchedulePacketBuild(HairRenderObject* hairObject) + { + m_newRenderObjects.insert(hairObject); + } + + bool HairGeometryRasterPass::BuildDrawPacket(HairRenderObject* hairObject) + { + if (!m_initialized) + { + return false; + } + + RHI::DrawPacketBuilder::DrawRequest drawRequest; + drawRequest.m_listTag = m_drawListTag; + drawRequest.m_pipelineState = m_pipelineState; +// drawRequest.m_streamBufferViews = // no explicit vertex buffer. shader is using the srg buffers + drawRequest.m_stencilRef = 0; + drawRequest.m_sortKey = 0; + + // Seems that the PerView and PerScene are gathered through RenderPass::CollectSrgs() + // The PerPass is gathered through the RasterPass::m_shaderResourceGroup + AZStd::lock_guard lock(m_mutex); + + return hairObject->BuildPPLLDrawPacket(drawRequest); + } + + bool HairGeometryRasterPass::AddDrawPackets(AZStd::list>& hairRenderObjects) + { + bool overallSuccess = true; + + if (!m_currentView && + (!(m_currentView = GetView()) || !m_currentView->HasDrawListTag(m_drawListTag))) + { + m_currentView = nullptr; // set it to nullptr to prevent further attempts this frame + AZ_Warning("Hair Gem", false, "AddDrawPackets: failed to acquire or match the DrawListTag - check that your pass and shader tag name match"); + return false; + } + + for (auto& renderObject : hairRenderObjects) + { + const RHI::DrawPacket* drawPacket = renderObject->GetFillDrawPacket(); + if (!drawPacket) + { // might not be an error - the object might have just been added and the DrawPacket is + // scheduled to be built when the render frame begins + AZ_Warning("Hair Gem", !m_newRenderObjects.empty(), "HairGeometryRasterPass - DrawPacket wasn't built"); + overallSuccess = false; + continue; + } + + m_currentView->AddDrawPacket(drawPacket); + } + return overallSuccess; + } + + void HairGeometryRasterPass::FrameBeginInternal(FramePrepareParams params) + { + { + AZStd::lock_guard lock(m_mutex); + if (!m_initialized && AcquireFeatureProcessor()) + { + LoadShaderAndPipelineState(); + m_featureProcessor->ForceRebuildRenderData(); + } + } + + if (!m_initialized) + { + return; + } + + // Bind the Per Object resources and trigger the RHI validation that will use attachment + // for its validation. The attachments are invalidated outside the render begin/end frame. + for (HairRenderObject* newObject : m_newRenderObjects) + { + newObject->BindPerObjectSrgForRaster(); + BuildDrawPacket(newObject); + } + + // Clear the new added objects - BuildDrawPacket should only be carried out once per + // object/shader lifetime + m_newRenderObjects.clear(); + + // Refresh current view every frame + if (!(m_currentView = GetView()) || !m_currentView->HasDrawListTag(m_drawListTag)) + { + m_currentView = nullptr; // set it to null if view exists but no tag match + AZ_Warning("Hair Gem", false, "FrameBeginInternal: failed to acquire or match the DrawListTag - check that your pass and shader tag name match"); + return; + } + + RPI::RasterPass::FrameBeginInternal(params); + } + + void HairGeometryRasterPass::CompileResources(const RHI::FrameGraphCompileContext& context) + { + AZ_PROFILE_FUNCTION(AzRender); + + if (!m_featureProcessor) + { + return; + } + + // Compilation of remaining srgs will be done by the parent class + RPI::RasterPass::CompileResources(context); + } + + void HairGeometryRasterPass::BuildShaderAndRenderData() + { + AZStd::lock_guard lock(m_mutex); + m_initialized = false; // make sure we initialize it even if not in this frame + if (AcquireFeatureProcessor()) + { + LoadShaderAndPipelineState(); + m_featureProcessor->ForceRebuildRenderData(); + } + } + + void HairGeometryRasterPass::OnShaderReinitialized([[maybe_unused]] const RPI::Shader & shader) + { + BuildShaderAndRenderData(); + } + + void HairGeometryRasterPass::OnShaderAssetReinitialized([[maybe_unused]] const Data::Asset& shaderAsset) + { + BuildShaderAndRenderData(); + } + + void HairGeometryRasterPass::OnShaderVariantReinitialized([[maybe_unused]] const AZ::RPI::ShaderVariant& shaderVariant) + { + BuildShaderAndRenderData(); + } + } // namespace Hair + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomTressFX/Code/Passes/HairGeometryRasterPass.h b/Gems/AtomTressFX/Code/Passes/HairGeometryRasterPass.h new file mode 100644 index 0000000000..a226d2c294 --- /dev/null +++ b/Gems/AtomTressFX/Code/Passes/HairGeometryRasterPass.h @@ -0,0 +1,112 @@ +/* + * 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 + * + */ +#pragma once + +#include + +#include + +#include +#include +#include +#include + +namespace AZ +{ + namespace RHI + { + struct DrawItem; + } + + namespace Render + { + namespace Hair + { + class HairRenderObject; + class HairFeatureProcessor; + + //! A HairGeometryRasterPass is used for the render of the hair geometries. This is the base + //! class that can be inherited - for example by the HiarPPLLRasterPass and have only the specific + //! class data handling added on top. + class HairGeometryRasterPass + : public RPI::RasterPass + , private RPI::ShaderReloadNotificationBus::Handler + { + AZ_RPI_PASS(HairGeometryRasterPass); + + public: + AZ_RTTI(HairGeometryRasterPass, "{0F07360A-A286-4060-8C62-137AFFA50561}", RasterPass); + AZ_CLASS_ALLOCATOR(HairGeometryRasterPass, SystemAllocator, 0); + + //! Creates a HairGeometryRasterPass + static RPI::Ptr Create(const RPI::PassDescriptor& descriptor); + + bool AddDrawPackets(AZStd::list>& hairObjects); + + //! The following will be called when an object was added or shader has been compiled + void SchedulePacketBuild(HairRenderObject* hairObject); + + Data::Instance GetShader() { return m_shader; } + + void SetFeatureProcessor(HairFeatureProcessor* featureProcessor) + { + m_featureProcessor = featureProcessor; + } + + virtual bool IsEnabled() const override; + + protected: + explicit HairGeometryRasterPass(const RPI::PassDescriptor& descriptor); + + // ShaderReloadNotificationBus::Handler overrides... + void OnShaderReinitialized(const RPI::Shader& shader) override; + void OnShaderAssetReinitialized(const Data::Asset& shaderAsset) override; + void OnShaderVariantReinitialized(const AZ::RPI::ShaderVariant& shaderVariant) override; + + void SetShaderPath(const char* shaderPath) { m_shaderPath = shaderPath; } + bool LoadShaderAndPipelineState(); + bool AcquireFeatureProcessor(); + void BuildShaderAndRenderData(); + bool BuildDrawPacket(HairRenderObject* hairObject); + + // Pass behavior overrides + void InitializeInternal() override; +// void BuildInternal() override; + void FrameBeginInternal(FramePrepareParams params) override; + + // Scope producer functions... + void CompileResources(const RHI::FrameGraphCompileContext& context) override; + + protected: + HairFeatureProcessor* m_featureProcessor = nullptr; + + // The shader that will be used by the pass + Data::Instance m_shader = nullptr; + + // Override the following in the inherited class + AZStd::string m_shaderPath = "dummyShaderPath"; + + // To help create the pipeline state + RPI::PassDescriptor m_passDescriptor; + + const RHI::PipelineState* m_pipelineState = nullptr; + RPI::ViewPtr m_currentView = nullptr; + + AZStd::mutex m_mutex; + + //! List of new render objects introduced this frame so that their in order to identify + //! that their PerObject (dynamic) Srg needs binding to the resources. + //! Done once per every new object introduced / requires update. + AZStd::unordered_set m_newRenderObjects; + + bool m_initialized = false; + }; + + } // namespace Hair + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomTressFX/Code/Passes/HairPPLLRasterPass.cpp b/Gems/AtomTressFX/Code/Passes/HairPPLLRasterPass.cpp index 746a3c2640..a677a929a2 100644 --- a/Gems/AtomTressFX/Code/Passes/HairPPLLRasterPass.cpp +++ b/Gems/AtomTressFX/Code/Passes/HairPPLLRasterPass.cpp @@ -34,61 +34,25 @@ namespace AZ namespace Hair { - // --- Creation & Initialization --- + HairPPLLRasterPass::HairPPLLRasterPass(const RPI::PassDescriptor& descriptor) + : HairGeometryRasterPass(descriptor) + { + SetShaderPath("Shaders/hairrenderingfillppll.azshader"); + } + RPI::Ptr HairPPLLRasterPass::Create(const RPI::PassDescriptor& descriptor) { RPI::Ptr pass = aznew HairPPLLRasterPass(descriptor); return pass; } - HairPPLLRasterPass::HairPPLLRasterPass(const RPI::PassDescriptor& descriptor) - : RasterPass(descriptor), - m_passDescriptor(descriptor) - { - } - - HairPPLLRasterPass::~HairPPLLRasterPass() - { - } - - bool HairPPLLRasterPass::AcquireFeatureProcessor() - { - if (m_featureProcessor) - { - return true; - } - - RPI::Scene* scene = GetScene(); - if (scene) - { - m_featureProcessor = scene->GetFeatureProcessor(); - } - else - { - return false; - } - - if (!m_featureProcessor) - { - AZ_Warning("Hair Gem", false, - "HairPPLLRasterPass [%s] - Failed to retrieve Hair feature processor from the scene", - GetName().GetCStr()); - return false; - } - return true; - } - - void HairPPLLRasterPass::InitializeInternal() - { - if (GetScene()) - { - RasterPass::InitializeInternal(); - } - } - + //! This method is used for attaching the PPLL data buffer which is a transient buffer. + //! It is done this ways because Atom doesn't support transient structured buffers declaration + //! via Pass yet. + //! Once supported, this will be done via data driven code and the method can be removed. void HairPPLLRasterPass::BuildInternal() { - RasterPass::BuildInternal(); + RasterPass::BuildInternal(); // change this to call parent if the method exists if (!AcquireFeatureProcessor()) { @@ -104,217 +68,6 @@ namespace AZ AttachBufferToSlot(Name{ "PerPixelLinkedList" }, m_featureProcessor->GetPerPixelListBuffer()); } - bool HairPPLLRasterPass::IsEnabled() const - { - return (RPI::RasterPass::IsEnabled() && m_initialized) ? true : false; - } - - bool HairPPLLRasterPass::LoadShaderAndPipelineState() - { - RPI::ShaderReloadNotificationBus::Handler::BusDisconnect(); - - const RPI::RasterPassData* passData = RPI::PassUtils::GetPassData(m_passDescriptor); - - // If we successfully retrieved our custom data, use it to set the DrawListTag - if (!passData) - { - AZ_Error("Hair Gem", false, "Missing pass raster data"); - return false; - } - - // Load Shader - const char* shaderFilePath = "Shaders/hairrenderingfillppll.azshader"; - Data::Asset shaderAsset = - RPI::AssetUtils::LoadAssetByProductPath(shaderFilePath, RPI::AssetUtils::TraceLevel::Error); - - if (!shaderAsset.GetId().IsValid()) - { - AZ_Error("Hair Gem", false, "Invalid shader asset for shader '%s'!", shaderFilePath); - return false; - } - - m_shader = RPI::Shader::FindOrCreate(shaderAsset); - if (m_shader == nullptr) - { - AZ_Error("Hair Gem", false, "Pass failed to load shader '%s'!", shaderFilePath); - return false; - } - - // Per Pass Srg - { - // Using 'PerPass' naming since currently RasterPass assumes that the pass Srg is always named 'PassSrg' - // [To Do] - RasterPass should use srg slot index and not name - currently this will - // result in a crash in one of the Atom existing MSAA passes that requires further dive. - // m_shaderResourceGroup = UtilityClass::CreateShaderResourceGroup(m_shader, "HairPerPassSrg", "Hair Gem"); - m_shaderResourceGroup = UtilityClass::CreateShaderResourceGroup(m_shader, "PassSrg", "Hair Gem"); - if (!m_shaderResourceGroup) - { - AZ_Error("Hair Gem", false, "Failed to create the per pass srg"); - return false; - } - } - - const RPI::ShaderVariant& shaderVariant = m_shader->GetVariant(RPI::ShaderAsset::RootShaderVariantStableId); - RHI::PipelineStateDescriptorForDraw pipelineStateDescriptor; - shaderVariant.ConfigurePipelineState(pipelineStateDescriptor); - - RPI::Scene* scene = GetScene(); - if (!scene) - { - AZ_Error("Hair Gem", false, "Scene could not be acquired" ); - return false; - } - RHI::DrawListTag drawListTag = m_shader->GetDrawListTag(); - scene->ConfigurePipelineState(drawListTag, pipelineStateDescriptor); - - pipelineStateDescriptor.m_renderAttachmentConfiguration = GetRenderAttachmentConfiguration(); - pipelineStateDescriptor.m_inputStreamLayout.SetTopology(AZ::RHI::PrimitiveTopology::TriangleList); - pipelineStateDescriptor.m_inputStreamLayout.Finalize(); - - m_pipelineState = m_shader->AcquirePipelineState(pipelineStateDescriptor); - if (!m_pipelineState) - { - AZ_Error("Hair Gem", false, "Pipeline state could not be acquired"); - return false; - } - - RPI::ShaderReloadNotificationBus::Handler::BusConnect(shaderAsset.GetId()); - - m_initialized = true; - return true; - } - - void HairPPLLRasterPass::SchedulePacketBuild(HairRenderObject* hairObject) - { - m_newRenderObjects.insert(hairObject); - } - - bool HairPPLLRasterPass::BuildDrawPacket(HairRenderObject* hairObject) - { - if (!m_initialized) - { - return false; - } - - RHI::DrawPacketBuilder::DrawRequest drawRequest; - drawRequest.m_listTag = m_drawListTag; - drawRequest.m_pipelineState = m_pipelineState; -// drawRequest.m_streamBufferViews = // no explicit vertex buffer. shader is using the srg buffers - drawRequest.m_stencilRef = 0; - drawRequest.m_sortKey = 0; - - // Seems that the PerView and PerScene are gathered through RenderPass::CollectSrgs() - // The PerPass is gathered through the RasterPass::m_shaderResourceGroup - AZStd::lock_guard lock(m_mutex); - - return hairObject->BuildPPLLDrawPacket(drawRequest); - } - - bool HairPPLLRasterPass::AddDrawPackets(AZStd::list>& hairRenderObjects) - { - bool overallSuccess = true; - - if (!m_currentView && - (!(m_currentView = GetView()) || !m_currentView->HasDrawListTag(m_drawListTag))) - { - m_currentView = nullptr; // set it to nullptr to prevent further attempts this frame - AZ_Warning("Hair Gem", false, "HairPPLLRasterPass failed to acquire or match the DrawListTag - check that your pass and shader tag name match"); - return false; - } - - for (auto& renderObject : hairRenderObjects) - { - const RHI::DrawPacket* drawPacket = renderObject->GetFillDrawPacket(); - if (!drawPacket) - { // might not be an error - the object might have just been added and the DrawPacket is - // scheduled to be built when the render frame begins - AZ_Warning("Hair Gem", !m_newRenderObjects.empty(), "HairPPLLRasterPass - DrawPacket wasn't built"); - overallSuccess = false; - continue; - } - - m_currentView->AddDrawPacket(drawPacket); - } - return overallSuccess; - } - - void HairPPLLRasterPass::FrameBeginInternal(FramePrepareParams params) - { - { - AZStd::lock_guard lock(m_mutex); - if (!m_initialized && AcquireFeatureProcessor()) - { - LoadShaderAndPipelineState(); - m_featureProcessor->ForceRebuildRenderData(); - } - } - - if (!m_initialized) - { - return; - } - - // Bind the Per Object resources and trigger the RHI validation that will use attachment - // for its validation. The attachments are invalidated outside the render begin/end frame. - for (HairRenderObject* newObject : m_newRenderObjects) - { - newObject->BindPerObjectSrgForRaster(); - BuildDrawPacket(newObject); - } - - // Clear the new added objects - BuildDrawPacket should only be carried out once per - // object/shader lifetime - m_newRenderObjects.clear(); - - // Refresh current view every frame - if (!(m_currentView = GetView()) || !m_currentView->HasDrawListTag(m_drawListTag)) - { - m_currentView = nullptr; // set it to null if view exists but no tag match - AZ_Warning("Hair Gem", false, "HairPPLLRasterPass failed to acquire or match the DrawListTag - check that your pass and shader tag name match"); - return; - } - - RPI::RasterPass::FrameBeginInternal(params); - } - - void HairPPLLRasterPass::CompileResources(const RHI::FrameGraphCompileContext& context) - { - AZ_PROFILE_FUNCTION(AzRender); - - if (!m_featureProcessor) - { - return; - } - - // Compilation of remaining srgs will be done by the parent class - RPI::RasterPass::CompileResources(context); - } - - void HairPPLLRasterPass::BuildShaderAndRenderData() - { - AZStd::lock_guard lock(m_mutex); - m_initialized = false; // make sure we initialize it even if not in this frame - if (AcquireFeatureProcessor()) - { - LoadShaderAndPipelineState(); - m_featureProcessor->ForceRebuildRenderData(); - } - } - - void HairPPLLRasterPass::OnShaderReinitialized([[maybe_unused]] const RPI::Shader & shader) - { - BuildShaderAndRenderData(); - } - - void HairPPLLRasterPass::OnShaderAssetReinitialized([[maybe_unused]] const Data::Asset& shaderAsset) - { - BuildShaderAndRenderData(); - } - - void HairPPLLRasterPass::OnShaderVariantReinitialized([[maybe_unused]] const AZ::RPI::ShaderVariant& shaderVariant) - { - BuildShaderAndRenderData(); - } } // namespace Hair } // namespace Render } // namespace AZ diff --git a/Gems/AtomTressFX/Code/Passes/HairPPLLRasterPass.h b/Gems/AtomTressFX/Code/Passes/HairPPLLRasterPass.h index ef7167897f..bf90fe0521 100644 --- a/Gems/AtomTressFX/Code/Passes/HairPPLLRasterPass.h +++ b/Gems/AtomTressFX/Code/Passes/HairPPLLRasterPass.h @@ -7,14 +7,7 @@ */ #pragma once -#include - -#include - -#include -#include -#include -#include +#include namespace AZ { @@ -27,11 +20,8 @@ namespace AZ { namespace Hair { - class HairRenderObject; - class HairFeatureProcessor; - //! A HairPPLLRasterPass is used for the hair fragments fill render after the data - //! went through the skinning and simulation passes. + //! went through the skinning and simulation passes. //! The output of this pass is the general list of fragment data that can now be //! traversed for depth resolve and lighting. //! The Fill pass uses the following Srgs: @@ -41,74 +31,22 @@ namespace AZ //! - HairDynamicDataSrg (PerObjectSrg) - shared buffers views for this hair object only. //! - PerViewSrg and PerSceneSrg - as per the data from Atom. class HairPPLLRasterPass - : public RPI::RasterPass - , private RPI::ShaderReloadNotificationBus::Handler + : public HairGeometryRasterPass { AZ_RPI_PASS(HairPPLLRasterPass); public: - AZ_RTTI(HairPPLLRasterPass, "{6614D7DD-24EE-4A2B-B314-7C035E2FB3C4}", RasterPass); + AZ_RTTI(HairPPLLRasterPass, "{6614D7DD-24EE-4A2B-B314-7C035E2FB3C4}", HairGeometryRasterPass); AZ_CLASS_ALLOCATOR(HairPPLLRasterPass, SystemAllocator, 0); - virtual ~HairPPLLRasterPass(); //! Creates a HairPPLLRasterPass static RPI::Ptr Create(const RPI::PassDescriptor& descriptor); - bool AddDrawPackets(AZStd::list>& hairObjects); - - //! The following will be called when an object was added or shader has been compiled - void SchedulePacketBuild(HairRenderObject* hairObject); - - Data::Instance GetShader() { return m_shader; } - - void SetFeatureProcessor(HairFeatureProcessor* featureProcessor) - { - m_featureProcessor = featureProcessor; - } - - virtual bool IsEnabled() const override; protected: - // ShaderReloadNotificationBus::Handler overrides... - void OnShaderReinitialized(const RPI::Shader& shader) override; - void OnShaderAssetReinitialized(const Data::Asset& shaderAsset) override; - void OnShaderVariantReinitialized(const AZ::RPI::ShaderVariant& shaderVariant) override; - - private: explicit HairPPLLRasterPass(const RPI::PassDescriptor& descriptor); - bool LoadShaderAndPipelineState(); - bool AcquireFeatureProcessor(); - void BuildShaderAndRenderData(); - bool BuildDrawPacket(HairRenderObject* hairObject); - // Pass behavior overrides - void InitializeInternal() override; void BuildInternal() override; - void FrameBeginInternal(FramePrepareParams params) override; - - // Scope producer functions... - void CompileResources(const RHI::FrameGraphCompileContext& context) override; - - private: - HairFeatureProcessor* m_featureProcessor = nullptr; - - // The shader that will be used by the pass - Data::Instance m_shader = nullptr; - - // To help create the pipeline state - RPI::PassDescriptor m_passDescriptor; - - const RHI::PipelineState* m_pipelineState = nullptr; - RPI::ViewPtr m_currentView = nullptr; - - AZStd::mutex m_mutex; - - //! List of new render objects introduced this frame so that their - //! Per Object (dynamic) Srg should be bound to the resources. - //! Done once per every new object introduced / requires update. - AZStd::unordered_set m_newRenderObjects; - - bool m_initialized = false; }; } // namespace Hair diff --git a/Gems/AtomTressFX/Hair_files.cmake b/Gems/AtomTressFX/Hair_files.cmake index c726d931f7..000abeb559 100644 --- a/Gems/AtomTressFX/Hair_files.cmake +++ b/Gems/AtomTressFX/Hair_files.cmake @@ -61,10 +61,16 @@ set(FILES #) # #set(atom_hair_passes + # The simulation pass class shared by all simulation / skinning compute passes Code/Passes/HairSkinningComputePass.h Code/Passes/HairSkinningComputePass.cpp + # Base class of all geometry raster passes + Code/Passes/HairGeometryRasterPass.h + Code/Passes/HairGeometryRasterPass.cpp + # PPLL rendering technique - geometry raster pass Code/Passes/HairPPLLRasterPass.h Code/Passes/HairPPLLRasterPass.cpp + # PP full screen resolve pass Code/Passes/HairPPLLResolvePass.h Code/Passes/HairPPLLResolvePass.cpp #) From 97e9f4dc7d7ce343c4a64547d30dc91d274c0277 Mon Sep 17 00:00:00 2001 From: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> Date: Wed, 13 Oct 2021 09:38:36 -0500 Subject: [PATCH 29/65] [LYN-6838] Various Monolithic shutdown fixes for the GameLauncher (#4564) * Added a stateless allocator which uses AZ_OS_MALLOC/AZ_OS_FREE to allocate memory for objects in static memory. Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Updated the Maestro and LyShine Anim Nodes to use the stateless_allocator for its static containers. This prevents crashes in static de-init due to the SystemAllocator being destroyed at that poitn Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Updated the EBus AllocatorType to use the EBusEnvironmentAllocator Because the EBus Context resides in static memory, the SystemAllocator lifetime is shorter than the EBus Context. This results in shutdown crashes in monolithic builds due to all of the gem modules being linked in as static libraries and the EBus context now destructing at the point of the executable static de-init, instead of the module de-init, where the SystemAllocator would still be around. Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Fixed an assortment of shutdown issues due to deleting objects after AZ allocators are no longer available Fixed the NameDictionary IsReady() function to not assert when the dictionary when invoked after the environment variable it was stored in was destroyed. Updated the NameData destructor to check that the NameDictionary IsReady() before attempting to remove itself from the dictionary Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Fixed NameDictionary destory workflow, to reset the EnvironmentVariable instance Updated the EnvironmentVariable instance to store the NameDictionary as a value. Added a rvalue reference `Set` function overload to the EnvironmentVariable class to support move only types. Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Clang 6.0.0 build fixes The C++17 std::launder feature isn't available in that compiler version Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> --- Code/Framework/AzCore/AzCore/EBus/EBus.h | 6 +- .../AzCore/AzCore/Memory/AllocatorManager.h | 4 +- .../AzCore/AzCore/Module/Environment.h | 45 ++++----- .../AzCore/AzCore/Name/Internal/NameData.cpp | 5 +- .../AzCore/AzCore/Name/NameDictionary.cpp | 24 ++--- .../AzCore/AzCore/Name/NameDictionary.h | 21 +++-- .../AzCore/AzCore/std/allocator_stateless.cpp | 94 +++++++++++++++++++ .../AzCore/AzCore/std/allocator_stateless.h | 61 ++++++++++++ .../AzCore/AzCore/std/azstd_files.cmake | 2 + .../AzCore/AzCore/std/createdestroy.h | 6 +- Code/Legacy/CryCommon/IMovieSystem.h | 5 +- Code/Legacy/CryCommon/StlUtils.h | 8 ++ .../DecalTextureArrayFeatureProcessor.cpp | 4 +- .../ProjectedShadowFeatureProcessor.cpp | 2 +- .../RHI/Vulkan/Code/Source/RHI/Instance.cpp | 19 +++- .../RHI/Vulkan/Code/Source/RHI/Instance.h | 1 + .../Code/Source/RHI/SystemComponent.cpp | 1 + .../Source/AudioSystemGemSystemComponent.cpp | 3 + Gems/ImGui/Code/Source/ImGuiManager.cpp | 7 +- Gems/LyShine/Code/Source/Animation/AnimNode.h | 3 +- .../Source/Animation/UiAnimationSystem.cpp | 33 ++++--- .../Code/Source/Cinematics/AnimPostFXNode.cpp | 3 +- Gems/Maestro/Code/Source/Cinematics/Movie.cpp | 35 ++++--- 23 files changed, 302 insertions(+), 90 deletions(-) create mode 100644 Code/Framework/AzCore/AzCore/std/allocator_stateless.cpp create mode 100644 Code/Framework/AzCore/AzCore/std/allocator_stateless.h diff --git a/Code/Framework/AzCore/AzCore/EBus/EBus.h b/Code/Framework/AzCore/AzCore/EBus/EBus.h index a3dc10b103..67cffb4e41 100644 --- a/Code/Framework/AzCore/AzCore/EBus/EBus.h +++ b/Code/Framework/AzCore/AzCore/EBus/EBus.h @@ -77,9 +77,11 @@ namespace AZ public: /** * Allocator used by the EBus. - * The default setting is AZStd::allocator, which uses AZ::SystemAllocator. + * The default setting is Internal EBusEnvironmentAllocator + * EBus code stores their Context instances in static memory + * Therfore the configured allocator must last as long as the EBus in a module */ - using AllocatorType = AZStd::allocator; + using AllocatorType = AZ::Internal::EBusEnvironmentAllocator; /** * Defines how many handlers can connect to an address on the EBus diff --git a/Code/Framework/AzCore/AzCore/Memory/AllocatorManager.h b/Code/Framework/AzCore/AzCore/Memory/AllocatorManager.h index be0ab4a0a0..14dec68ad1 100644 --- a/Code/Framework/AzCore/AzCore/Memory/AllocatorManager.h +++ b/Code/Framework/AzCore/AzCore/Memory/AllocatorManager.h @@ -34,7 +34,9 @@ namespace AZ friend IAllocator; friend class AllocatorBase; friend class Debug::AllocationRecords; - friend class AZ::Internal::EnvironmentVariableHolder; + template friend constexpr auto AZStd::construct_at(T*, Args&&... args) + ->AZStd::enable_if_t()) T(AZStd::forward(args)...))>>, T*>; + template constexpr friend void AZStd::destroy_at(T*); public: typedef AZStd::function OutOfMemoryCBType; diff --git a/Code/Framework/AzCore/AzCore/Module/Environment.h b/Code/Framework/AzCore/AzCore/Module/Environment.h index e87ff81446..a2dcfbd8fa 100644 --- a/Code/Framework/AzCore/AzCore/Module/Environment.h +++ b/Code/Framework/AzCore/AzCore/Module/Environment.h @@ -251,16 +251,15 @@ namespace AZ class EnvironmentVariableHolder : public EnvironmentVariableHolderBase { - void ConstructImpl(const AZStd::true_type& /* AZStd::has_trivial_constructor */) - { - memset(&m_value, 0, sizeof(T)); - } - template - void ConstructImpl(const AZStd::false_type& /* AZStd::has_trivial_constructor */, Args&&... args) + void ConstructImpl(Args&&... args) { - // Construction of non-trivial types is left up to the type's constructor. - new(&m_value) T(AZStd::forward(args)...); + // Use std::launder to ensure that the compiler treats the T* reinterpret_cast as a new object + #if __cpp_lib_launder + AZStd::construct_at(std::launder(reinterpret_cast(&m_value)), AZStd::forward(args)...); + #else + AZStd::construct_at(reinterpret_cast(&m_value), AZStd::forward(args)...); + #endif } static void DestructDispatchNoLock(EnvironmentVariableHolderBase *base, DestroyTarget selfDestruct) { @@ -274,10 +273,12 @@ namespace AZ AZ_Assert(self->m_isConstructed, "Variable is not constructed. Please check your logic and guard if needed!"); self->m_isConstructed = false; self->m_moduleOwner = nullptr; - if constexpr(!AZStd::is_trivially_destructible_v) - { - reinterpret_cast(&self->m_value)->~T(); - } + // Use std::launder to ensure that the compiler treats the T* reinterpret_cast as a new object + #if __cpp_lib_launder + AZStd::destroy_at(std::launder(reinterpret_cast(&self->m_value))); + #else + AZStd::destroy_at(reinterpret_cast(&self->m_value)); + #endif } public: EnvironmentVariableHolder(u32 guid, bool isOwnershipTransfer, Environment::AllocatorInterface* allocator) @@ -303,24 +304,13 @@ namespace AZ UnregisterAndDestroy(DestructDispatchNoLock, moduleRelease); } - void Construct() - { - AZStd::lock_guard lock(m_mutex); - if (!m_isConstructed) - { - ConstructImpl(AZStd::is_trivially_constructible{}); - m_isConstructed = true; - m_moduleOwner = Environment::GetModuleId(); - } - } - template void Construct(Args&&... args) { AZStd::lock_guard lock(m_mutex); if (!m_isConstructed) { - ConstructImpl(typename AZStd::false_type(), AZStd::forward(args)...); + ConstructImpl(AZStd::forward(args)...); m_isConstructed = true; m_moduleOwner = Environment::GetModuleId(); } @@ -333,7 +323,7 @@ namespace AZ } // variable storage - typename AZStd::aligned_storage::value>::type m_value; + AZStd::aligned_storage_for_t m_value; static int s_moduleUseCount; }; @@ -468,6 +458,11 @@ namespace AZ Get() = value; } + void Set(T&& value) + { + Get() = AZStd::move(value); + } + explicit operator bool() const { return IsValid(); diff --git a/Code/Framework/AzCore/AzCore/Name/Internal/NameData.cpp b/Code/Framework/AzCore/AzCore/Name/Internal/NameData.cpp index 0086e68c6d..574b0bcc7e 100644 --- a/Code/Framework/AzCore/AzCore/Name/Internal/NameData.cpp +++ b/Code/Framework/AzCore/AzCore/Name/Internal/NameData.cpp @@ -42,7 +42,10 @@ namespace AZ AZ_Assert(m_useCount > 0, "m_useCount is already 0!"); if (m_useCount.fetch_sub(1) == 1) { - AZ::NameDictionary::Instance().TryReleaseName(hash); + if (AZ::NameDictionary::IsReady()) + { + AZ::NameDictionary::Instance().TryReleaseName(hash); + } } } } diff --git a/Code/Framework/AzCore/AzCore/Name/NameDictionary.cpp b/Code/Framework/AzCore/AzCore/Name/NameDictionary.cpp index 8e8011add9..3047a2894e 100644 --- a/Code/Framework/AzCore/AzCore/Name/NameDictionary.cpp +++ b/Code/Framework/AzCore/AzCore/Name/NameDictionary.cpp @@ -21,23 +21,18 @@ namespace AZ namespace NameDictionaryInternal { - static AZ::EnvironmentVariable s_instance = nullptr; + static AZ::EnvironmentVariable s_instance = nullptr; } void NameDictionary::Create() { using namespace NameDictionaryInternal; - AZ_Assert(!s_instance || !s_instance.Get(), "NameDictionary already created!"); + AZ_Assert(!s_instance, "NameDictionary already created!"); if (!s_instance) { - s_instance = AZ::Environment::CreateVariable(NameDictionaryInstanceName); - } - - if (!s_instance.Get()) - { - s_instance.Set(aznew NameDictionary()); + s_instance = AZ::Environment::CreateVariable(NameDictionaryInstanceName); } } @@ -46,8 +41,7 @@ namespace AZ using namespace NameDictionaryInternal; AZ_Assert(s_instance, "NameDictionary not created!"); - delete (*s_instance); - *s_instance = nullptr; + s_instance.Reset(); } bool NameDictionary::IsReady() @@ -56,10 +50,10 @@ namespace AZ if (!s_instance) { - s_instance = Environment::FindVariable(NameDictionaryInstanceName); + s_instance = Environment::FindVariable(NameDictionaryInstanceName); } - return s_instance && *s_instance; + return s_instance.IsConstructed(); } NameDictionary& NameDictionary::Instance() @@ -68,12 +62,12 @@ namespace AZ if (!s_instance) { - s_instance = Environment::FindVariable(NameDictionaryInstanceName); + s_instance = Environment::FindVariable(NameDictionaryInstanceName); } - AZ_Assert(s_instance && *s_instance, "NameDictionary has not been initialized yet."); + AZ_Assert(s_instance.IsConstructed(), "NameDictionary has not been initialized yet."); - return *(*s_instance); + return *s_instance; } NameDictionary::NameDictionary() diff --git a/Code/Framework/AzCore/AzCore/Name/NameDictionary.h b/Code/Framework/AzCore/AzCore/Name/NameDictionary.h index fa13dbd682..8f9af4be3a 100644 --- a/Code/Framework/AzCore/AzCore/Name/NameDictionary.h +++ b/Code/Framework/AzCore/AzCore/Name/NameDictionary.h @@ -16,7 +16,7 @@ #include #include -namespace MaterialEditor +namespace MaterialEditor { class MaterialEditorCoreComponent; } @@ -34,14 +34,14 @@ namespace AZ { class NameData; }; - + //! Maintains a list of unique strings for Name objects. //! The main benefit of the Name system is very fast string equality comparison, because every - //! unique name has a unique ID. The NameDictionary's purpose is to guarantee name IDs do not + //! unique name has a unique ID. The NameDictionary's purpose is to guarantee name IDs do not //! collide. It also saves memory by removing duplicate strings. //! - //! Benchmarks have shown that creating a new Name object can be quite slow when the name doesn't - //! already exist in the NameDictionary, but is comparable to creating an AZStd::string for names + //! Benchmarks have shown that creating a new Name object can be quite slow when the name doesn't + //! already exist in the NameDictionary, but is comparable to creating an AZStd::string for names //! that already exist. class NameDictionary final { @@ -51,7 +51,10 @@ namespace AZ friend Name; friend Internal::NameData; friend UnitTest::NameDictionaryTester; - + template friend constexpr auto AZStd::construct_at(T*, Args&&... args) + -> AZStd::enable_if_t()) T(AZStd::forward(args)...))>>, T*>; + template constexpr friend void AZStd::destroy_at(T*); + public: static void Create(); @@ -62,7 +65,7 @@ namespace AZ //! Makes a Name from the provided raw string. If an entry already exists in the dictionary, it is shared. //! Otherwise, it is added to the internal dictionary. - //! + //! //! @param name The name to resolve against the dictionary. //! @return A Name instance holding a dictionary entry associated with the provided raw string. Name MakeName(AZStd::string_view name); @@ -84,13 +87,13 @@ namespace AZ // Attempts to release the name from the dictionary, but checks to make sure // a reference wasn't taken by another thread. void TryReleaseName(Name::Hash hash); - + ////////////////////////////////////////////////////////////////////////// // Calculates a hash for the provided name string. // Does not attempt to resolve hash collisions; that is handled elsewhere. Name::Hash CalcHash(AZStd::string_view name); - + AZStd::unordered_map m_dictionary; mutable AZStd::shared_mutex m_sharedMutex; }; diff --git a/Code/Framework/AzCore/AzCore/std/allocator_stateless.cpp b/Code/Framework/AzCore/AzCore/std/allocator_stateless.cpp new file mode 100644 index 0000000000..5806cc485c --- /dev/null +++ b/Code/Framework/AzCore/AzCore/std/allocator_stateless.cpp @@ -0,0 +1,94 @@ +/* + * 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 AZStd +{ + stateless_allocator::stateless_allocator(const char* name) + : m_name(name) {} + + const char* stateless_allocator::get_name() const + { + return m_name; + } + + void stateless_allocator::set_name(const char* name) + { + m_name = name; + } + + auto stateless_allocator::allocate(size_type byteSize) -> pointer_type + { + return allocate(byteSize, AZ_DEFAULT_ALIGNMENT, 0); + } + + auto stateless_allocator::allocate(size_type byteSize, size_type alignment, int) -> pointer_type + { + pointer_type address = AZ_OS_MALLOC(byteSize, alignment); + + if (address == nullptr) + { + AZ_Error("Memory", false, "stateless_allocator ran out of system memory!\n"); + } + + return address; + } + + void stateless_allocator::deallocate(pointer_type ptr, size_type) + { + AZ_OS_FREE(ptr); + } + + void stateless_allocator::deallocate(pointer_type ptr, size_type, size_type) + { + AZ_OS_FREE(ptr); + } + + auto stateless_allocator::max_size() const -> size_type + { + return AZ_CORE_MAX_ALLOCATOR_SIZE; + } + + stateless_allocator stateless_allocator::select_on_container_copy_construction() const + { + return *this; + } + + auto stateless_allocator::resize(pointer_type, size_type) -> size_type + { + return 0; + } + + bool stateless_allocator::is_lock_free() + { + return false; + } + + bool stateless_allocator::is_stale_read_allowed() + { + return false; + } + + bool stateless_allocator::is_delayed_recycling() + { + return false; + } + + // comparison operators + bool operator==(const stateless_allocator&, const stateless_allocator&) + { + return true; + } + + bool operator!=(const stateless_allocator&, const stateless_allocator&) + { + return false; + } +} diff --git a/Code/Framework/AzCore/AzCore/std/allocator_stateless.h b/Code/Framework/AzCore/AzCore/std/allocator_stateless.h new file mode 100644 index 0000000000..b73c680c32 --- /dev/null +++ b/Code/Framework/AzCore/AzCore/std/allocator_stateless.h @@ -0,0 +1,61 @@ +/* + * 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 + * + */ + +#pragma once + +#include +#include +#include + +namespace AZStd +{ + class stateless_allocator + { + public: + + AZ_TYPE_INFO(stateless_allocator, "{E4976C53-0B20-4F39-8D41-0A76F59A7D68}"); + + using value_type = uint8_t; + using pointer_type = void*; + using size_type = size_t; + using difference_type = ptrdiff_t; + using allow_memory_leaks = AZStd::true_type; + + stateless_allocator(const char* name = "AZStd::stateless_allocator"); + stateless_allocator(const stateless_allocator& rhs) = default; + + stateless_allocator& operator=(const stateless_allocator& rhs) = default; + + const char* get_name() const; + void set_name(const char* name); + + pointer_type allocate(size_type byteSize); + pointer_type allocate(size_type byteSize, size_type alignment, int flags = 0); + void deallocate(pointer_type ptr, size_type alignment); + void deallocate(pointer_type ptr, size_type byteSize, size_type alignment); + + // max_size actually returns the true maximum size of a single allocation + size_type max_size() const; + + // Returns a copy of the allocator + stateless_allocator select_on_container_copy_construction() const; + + //! extensions + size_type resize(pointer_type ptr, size_type newSize); + + bool is_lock_free(); + bool is_stale_read_allowed(); + bool is_delayed_recycling(); + + private: + const char* m_name; + }; + + bool operator==(const stateless_allocator& left, const stateless_allocator& right); + bool operator!=(const stateless_allocator& left, const stateless_allocator& right); +} diff --git a/Code/Framework/AzCore/AzCore/std/azstd_files.cmake b/Code/Framework/AzCore/AzCore/std/azstd_files.cmake index 08e72c5649..2746489f8c 100644 --- a/Code/Framework/AzCore/AzCore/std/azstd_files.cmake +++ b/Code/Framework/AzCore/AzCore/std/azstd_files.cmake @@ -12,6 +12,8 @@ set(FILES allocator.h allocator_ref.h allocator_stack.h + allocator_stateless.cpp + allocator_stateless.h allocator_static.h allocator_traits.h any.h diff --git a/Code/Framework/AzCore/AzCore/std/createdestroy.h b/Code/Framework/AzCore/AzCore/std/createdestroy.h index 0fa6e4ed40..355374a13a 100644 --- a/Code/Framework/AzCore/AzCore/std/createdestroy.h +++ b/Code/Framework/AzCore/AzCore/std/createdestroy.h @@ -20,7 +20,7 @@ namespace AZStd { - // alias std::pointer_traits into the AZStd::namespace + // alias std::pointer_traits into the AZStd::namespace using std::pointer_traits; //! Bring the names of uninitialized_default_construct and @@ -229,7 +229,7 @@ namespace AZStd //! `new (declval()) T(declval()...)` is well-formed template constexpr auto construct_at(T* ptr, Args&&... args) - -> enable_if_t()) T(AZStd::forward(args)...))>>, T*> + -> enable_if_t()) T(AZStd::forward(args)...))>>, T*> { return ::new (ptr) T(AZStd::forward(args)...); } @@ -487,7 +487,7 @@ namespace AZStd { //! Implements the C++17 uninitialized_move function //! The functions accepts two input iterators and an output iterator - //! It performs an AZStd::move on each in in the range of the input iterator + //! It performs an AZStd::move on each in in the range of the input iterator //! and stores the result in location pointed by the output iterator template ForwardIt uninitialized_move(InputIt first, InputIt last, ForwardIt result) diff --git a/Code/Legacy/CryCommon/IMovieSystem.h b/Code/Legacy/CryCommon/IMovieSystem.h index 6cc5a06c8f..4da08d3bbf 100644 --- a/Code/Legacy/CryCommon/IMovieSystem.h +++ b/Code/Legacy/CryCommon/IMovieSystem.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -181,7 +182,7 @@ public: private: AnimParamType m_type; - AZStd::string m_name; + AZStd::basic_string, AZStd::stateless_allocator> m_name; }; namespace AZStd @@ -617,7 +618,7 @@ public: , valueType(_valueType) , flags(_flags) {}; - AZStd::string name; // parameter name. + AZStd::basic_string, AZStd::stateless_allocator> name; // parameter name. CAnimParamType paramType; // parameter id. AnimValueType valueType; // value type, defines type of track to use for animating this parameter. ESupportedParamFlags flags; // combination of flags from ESupportedParamFlags. diff --git a/Code/Legacy/CryCommon/StlUtils.h b/Code/Legacy/CryCommon/StlUtils.h index 4aafd4dd0e..f5128a564f 100644 --- a/Code/Legacy/CryCommon/StlUtils.h +++ b/Code/Legacy/CryCommon/StlUtils.h @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -491,6 +492,13 @@ namespace stl return type; } + //! Specialization of string to const char cast. + template <> + inline const char* constchar_cast(const AZStd::basic_string, AZStd::stateless_allocator>& type) + { + return type.c_str(); + } + //! Specialization of string to const char cast. template <> inline const char* constchar_cast(const AZStd::string& type) diff --git a/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArrayFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArrayFeatureProcessor.cpp index 472dbff5c8..e9bc1a1277 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArrayFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArrayFeatureProcessor.cpp @@ -357,7 +357,7 @@ namespace AZ // Texture2DArray m_decalTextureArrayNormalMaps0; // Texture2DArray m_decalTextureArrayNormalMaps1; // Texture2DArray m_decalTextureArrayNormalMaps2; - static const AZStd::array ShaderNames = { "m_decalTextureArrayDiffuse", + static constexpr AZStd::array ShaderNames = { "m_decalTextureArrayDiffuse", "m_decalTextureArrayNormalMaps" }; for (int mapType = 0; mapType < DecalMapType_Num; ++mapType) @@ -365,7 +365,7 @@ namespace AZ for (int texArrayIdx = 0; texArrayIdx < NumTextureArrays; ++texArrayIdx) { const RHI::ShaderResourceGroupLayout* viewSrgLayout = RPI::RPISystemInterface::Get()->GetViewSrgLayout().get(); - const AZStd::string baseName = ShaderNames[mapType] + AZStd::to_string(texArrayIdx); + const AZStd::string baseName = AZStd::string(ShaderNames[mapType]) + AZStd::to_string(texArrayIdx); m_decalTextureArrayIndices[texArrayIdx][mapType] = viewSrgLayout->FindShaderInputImageIndex(Name(baseName.c_str())); AZ_Warning( diff --git a/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.cpp index c0202c7c74..6452b312c8 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.cpp @@ -346,7 +346,7 @@ namespace AZ::Render void ProjectedShadowFeatureProcessor::CacheEsmShadowmapsPass(const AZStd::vector& validPipelineIds) { - static const Name LightTypeName = Name("projected"); + const Name LightTypeName = Name("projected"); const auto* passSystem = RPI::PassSystemInterface::Get(); const AZStd::vector passes = passSystem->GetPassesForTemplateName(Name("EsmShadowmapsTemplate")); diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Instance.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Instance.cpp index c2fe16b551..2dce5d85f3 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Instance.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Instance.cpp @@ -16,12 +16,27 @@ namespace AZ { static const uint32_t s_minVulkanSupportedVersion = VK_API_VERSION_1_0; + static EnvironmentVariable s_vulkanInstance; + static constexpr const char* s_vulkanInstanceKey = "VulkanInstance"; + Instance& Instance::GetInstance() { - static Instance s_instance; - return s_instance; + if (!s_vulkanInstance) + { + s_vulkanInstance = Environment::FindVariable(s_vulkanInstanceKey); + if (!s_vulkanInstance) + { + s_vulkanInstance = Environment::CreateVariable(s_vulkanInstanceKey); + } + } + + return s_vulkanInstance.Get(); } + void Instance::Reset() + { + s_vulkanInstance.Reset(); + } Instance::~Instance() { diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Instance.h b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Instance.h index 40a6951c2d..efa8d36d2b 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Instance.h +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Instance.h @@ -34,6 +34,7 @@ namespace AZ }; static Instance& GetInstance(); + static void Reset(); ~Instance(); bool Init(const Descriptor& descriptor); void Shutdown(); diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/SystemComponent.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/SystemComponent.cpp index 36e0c2b2f5..0d401003d9 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/SystemComponent.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/SystemComponent.cpp @@ -101,6 +101,7 @@ namespace AZ RHI::FactoryManagerBus::Broadcast(&RHI::FactoryManagerRequest::UnregisterFactory, this); Instance::GetInstance().Shutdown(); + Instance::Reset(); } Name SystemComponent::GetName() diff --git a/Gems/AudioSystem/Code/Source/AudioSystemGemSystemComponent.cpp b/Gems/AudioSystem/Code/Source/AudioSystemGemSystemComponent.cpp index b1e7e3ac18..1af927a7e7 100644 --- a/Gems/AudioSystem/Code/Source/AudioSystemGemSystemComponent.cpp +++ b/Gems/AudioSystem/Code/Source/AudioSystemGemSystemComponent.cpp @@ -90,6 +90,9 @@ namespace AudioSystemGem AudioSystemGemSystemComponent::~AudioSystemGemSystemComponent() { + // The audio system uses the Audio::AudioSystemAllocator + // so it needs to be deleted before the allocator is shutdown + m_audioSystem.reset(); Audio::Platform::ShutdownAudioAllocators(); } diff --git a/Gems/ImGui/Code/Source/ImGuiManager.cpp b/Gems/ImGui/Code/Source/ImGuiManager.cpp index 6fc1ea8b71..d631e2f83e 100644 --- a/Gems/ImGui/Code/Source/ImGuiManager.cpp +++ b/Gems/ImGui/Code/Source/ImGuiManager.cpp @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -31,11 +32,11 @@ using namespace AzFramework; using namespace ImGui; // Wheel Delta const value. -const constexpr uint32_t IMGUI_WHEEL_DELTA = 120; // From WinUser.h, for Linux +static const constexpr uint32_t IMGUI_WHEEL_DELTA = 120; // From WinUser.h, for Linux // Typedef and local static map to hold LyInput->ImGui Nav mappings ( filled up in Initialize() ) -typedef AZStd::pair LyButtonImGuiNavIndexPair; -typedef AZStd::unordered_map LyButtonImGuiNavIndexMap; +using LyButtonImGuiNavIndexPair = AZStd::pair; +using LyButtonImGuiNavIndexMap = AZStd::fixed_unordered_map; static LyButtonImGuiNavIndexMap s_lyInputToImGuiNavIndexMap; /** diff --git a/Gems/LyShine/Code/Source/Animation/AnimNode.h b/Gems/LyShine/Code/Source/Animation/AnimNode.h index 0076276204..4d139e99ca 100644 --- a/Gems/LyShine/Code/Source/Animation/AnimNode.h +++ b/Gems/LyShine/Code/Source/Animation/AnimNode.h @@ -13,6 +13,7 @@ #include #include "UiAnimationSystem.h" +#include /*! @@ -39,7 +40,7 @@ public: , valueType(_valueType) , flags(_flags) {}; - AZStd::string name; // parameter name. + AZStd::basic_string, AZStd::stateless_allocator> name; // parameter name. CUiAnimParamType paramType; // parameter id. EUiAnimValue valueType; // value type, defines type of track to use for animating this parameter. ESupportedParamFlags flags; // combination of flags from ESupportedParamFlags. diff --git a/Gems/LyShine/Code/Source/Animation/UiAnimationSystem.cpp b/Gems/LyShine/Code/Source/Animation/UiAnimationSystem.cpp index 012eaacd1b..cab6ae3fa6 100644 --- a/Gems/LyShine/Code/Source/Animation/UiAnimationSystem.cpp +++ b/Gems/LyShine/Code/Source/Animation/UiAnimationSystem.cpp @@ -14,9 +14,11 @@ #include "UiAnimSerialize.h" #include +#include +#include +#include #include -#include #include #include #include @@ -25,22 +27,31 @@ #include ////////////////////////////////////////////////////////////////////////// -// Serialization for anim nodes & param types -#define REGISTER_NODE_TYPE(name) assert(g_animNodeEnumToStringMap.find(eUiAnimNodeType_ ## name) == g_animNodeEnumToStringMap.end()); \ - g_animNodeEnumToStringMap[eUiAnimNodeType_ ## name] = AZ_STRINGIZE(name); \ - g_animNodeStringToEnumMap[AZStd::string(AZ_STRINGIZE(name))] = eUiAnimNodeType_ ## name; +namespace +{ + using UiAnimParamSystemString = AZStd::basic_string, AZStd::stateless_allocator>; -#define REGISTER_PARAM_TYPE(name) assert(g_animParamEnumToStringMap.find(eUiAnimParamType_ ## name) == g_animParamEnumToStringMap.end()); \ + template > + using UiAnimSystemOrderedMap = AZStd::map; + template , typename EqualKey = AZStd::equal_to> + using UiAnimSystemUnorderedMap = AZStd::unordered_map; +} +// Serialization for anim nodes & param types +#define REGISTER_NODE_TYPE(name) assert(g_animNodeEnumToStringMap.contains(eUiAnimNodeType_ ## name)); \ + g_animNodeEnumToStringMap[eUiAnimNodeType_ ## name] = AZ_STRINGIZE(name); \ + g_animNodeStringToEnumMap[UiAnimParamSystemString(AZ_STRINGIZE(name))] = eUiAnimNodeType_ ## name; + +#define REGISTER_PARAM_TYPE(name) assert(g_animParamEnumToStringMap.contains(eUiAnimParamType_ ## name)); \ g_animParamEnumToStringMap[eUiAnimParamType_ ## name] = AZ_STRINGIZE(name); \ - g_animParamStringToEnumMap[AZStd::string(AZ_STRINGIZE(name))] = eUiAnimParamType_ ## name; + g_animParamStringToEnumMap[UiAnimParamSystemString(AZ_STRINGIZE(name))] = eUiAnimParamType_ ## name; namespace { - AZStd::unordered_map g_animNodeEnumToStringMap; - StaticInstance >> g_animNodeStringToEnumMap; + UiAnimSystemUnorderedMap g_animNodeEnumToStringMap; + UiAnimSystemOrderedMap> g_animNodeStringToEnumMap; - AZStd::unordered_map g_animParamEnumToStringMap; - StaticInstance >> g_animParamStringToEnumMap; + UiAnimSystemUnorderedMap g_animParamEnumToStringMap; + UiAnimSystemOrderedMap> g_animParamStringToEnumMap; // If you get an assert in this function, it means two node types have the same enum value. void RegisterNodeTypes() diff --git a/Gems/Maestro/Code/Source/Cinematics/AnimPostFXNode.cpp b/Gems/Maestro/Code/Source/Cinematics/AnimPostFXNode.cpp index 7e32301b71..b535033351 100644 --- a/Gems/Maestro/Code/Source/Cinematics/AnimPostFXNode.cpp +++ b/Gems/Maestro/Code/Source/Cinematics/AnimPostFXNode.cpp @@ -8,6 +8,7 @@ #include +#include #include "AnimPostFXNode.h" #include "AnimSplineTrack.h" #include "CompoundSplineTrack.h" @@ -38,7 +39,7 @@ public: virtual void GetDefault(bool& val) const = 0; virtual void GetDefault(Vec4& val) const = 0; - AZStd::string m_name; + AZStd::basic_string, AZStd::stateless_allocator> m_name; protected: virtual ~CControlParamBase(){} diff --git a/Gems/Maestro/Code/Source/Cinematics/Movie.cpp b/Gems/Maestro/Code/Source/Cinematics/Movie.cpp index 3654af52dd..9c4356560d 100644 --- a/Gems/Maestro/Code/Source/Cinematics/Movie.cpp +++ b/Gems/Maestro/Code/Source/Cinematics/Movie.cpp @@ -8,6 +8,9 @@ #include +#include +#include +#include #include #include #include "Movie.h" @@ -73,22 +76,32 @@ static SMovieSequenceAutoComplete s_movieSequenceAutoComplete; #endif ////////////////////////////////////////////////////////////////////////// -// Serialization for anim nodes & param types -#define REGISTER_NODE_TYPE(name) assert(g_animNodeEnumToStringMap.find(AnimNodeType::name) == g_animNodeEnumToStringMap.end()); \ - g_animNodeEnumToStringMap[AnimNodeType::name] = AZ_STRINGIZE(name); \ - g_animNodeStringToEnumMap[AZStd::string(AZ_STRINGIZE(name))] = AnimNodeType::name; +namespace +{ + using AnimParamSystemString = AZStd::basic_string, AZStd::stateless_allocator>; -#define REGISTER_PARAM_TYPE(name) assert(g_animParamEnumToStringMap.find(AnimParamType::name) == g_animParamEnumToStringMap.end()); \ - g_animParamEnumToStringMap[AnimParamType::name] = AZ_STRINGIZE(name); \ - g_animParamStringToEnumMap[AZStd::string(AZ_STRINGIZE(name))] = AnimParamType::name; + template > + using AnimSystemOrderedMap = AZStd::map; + template , typename EqualKey = AZStd::equal_to> + using AnimSystemUnorderedMap = AZStd::unordered_map; +} + +// Serialization for anim nodes & param types +#define REGISTER_NODE_TYPE(name) assert(g_animNodeEnumToStringMap.contains(AnimNodeType::name)); \ + g_animNodeEnumToStringMap[AnimNodeType::name] = AZ_STRINGIZE(name); \ + g_animNodeStringToEnumMap[AnimParamSystemString(AZ_STRINGIZE(name))] = AnimNodeType::name; + +#define REGISTER_PARAM_TYPE(name) assert(g_animParamEnumToStringMap.contains(AnimParamType::name)); \ + g_animParamEnumToStringMap[AnimParamType::name] = AZ_STRINGIZE(name); \ + g_animParamStringToEnumMap[AnimParamSystemString(AZ_STRINGIZE(name))] = AnimParamType::name; namespace { - AZStd::unordered_map g_animNodeEnumToStringMap; - StaticInstance >> g_animNodeStringToEnumMap; + AnimSystemUnorderedMap g_animNodeEnumToStringMap; + AnimSystemOrderedMap> g_animNodeStringToEnumMap; - AZStd::unordered_map g_animParamEnumToStringMap; - StaticInstance >> g_animParamStringToEnumMap; + AnimSystemUnorderedMap g_animParamEnumToStringMap; + AnimSystemOrderedMap> g_animParamStringToEnumMap; // If you get an assert in this function, it means two node types have the same enum value. void RegisterNodeTypes() From b6a1f62dec2c248606905bdc931ee4d795c148c2 Mon Sep 17 00:00:00 2001 From: Benjamin Jillich <43751992+amzn-jillich@users.noreply.github.com> Date: Wed, 13 Oct 2021 18:20:48 +0200 Subject: [PATCH 30/65] EMotion FX: Fix for broken workspaces after asset path change (#4662) Some days ago an impactful change deprecated @assets@ and @root@ keywords from paths which our workspaces used. Some workspaces used these and were broken. This change makes them backward compatible and loads the assets correctly again. It also auto fixes broken paths that were present in some of the saved workspaces + making sure new workspaces are saved correctly. Signed-off-by: Benjamin Jillich --- .../EMotionStudio/EMStudioSDK/Source/Workspace.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/Workspace.cpp b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/Workspace.cpp index ea7c92b742..cada8c7f59 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/Workspace.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/Workspace.cpp @@ -105,7 +105,9 @@ namespace EMStudio } } - commandString = AZStd::string::format("%s -filename \"%s\"", command, resultFileName.c_str()); + AZStd::string resultFilenameString = resultFileName.c_str(); + AzFramework::StringFunc::AssetDatabasePath::Normalize(resultFilenameString); + commandString = AZStd::string::format("%s -filename \"%s\"", command, resultFilenameString.c_str()); if (additionalParameters) { @@ -428,7 +430,13 @@ namespace EMStudio continue; } - AzFramework::StringFunc::Replace(commands[i], "@products@/", assetCacheFolder.c_str(), true /* case sensitive */); + AzFramework::StringFunc::Replace(commands[i], "@products@", assetCacheFolder.c_str()); + AzFramework::StringFunc::Replace(commands[i], "@assets@", assetCacheFolder.c_str()); + AzFramework::StringFunc::Replace(commands[i], "@root@", assetCacheFolder.c_str()); + AzFramework::StringFunc::Replace(commands[i], "@projectplatformcache@", assetCacheFolder.c_str()); + AzFramework::StringFunc::Replace(commands[i], "//", AZ_CORRECT_FILESYSTEM_SEPARATOR_STRING); + AzFramework::StringFunc::Replace(commands[i], "\\\\", AZ_CORRECT_FILESYSTEM_SEPARATOR_STRING); + AzFramework::StringFunc::Replace(commands[i], "/\\", AZ_CORRECT_FILESYSTEM_SEPARATOR_STRING); // add the command to the command group commandGroup->AddCommandString(commands[i]); From 150060441eccb98318febe17d3915f0781823b19 Mon Sep 17 00:00:00 2001 From: jromnoa <80134229+jromnoa@users.noreply.github.com> Date: Wed, 13 Oct 2021 09:50:44 -0700 Subject: [PATCH 31/65] Re-add pytest.mark.test_case_id markers (#4621) * add some test case id markers Signed-off-by: jromnoa * re-added pytest.mark.test_case_id markers for test cases Signed-off-by: jromnoa * re-add directional light log lines that were missing and re-add directional light test case id Signed-off-by: jromnoa --- .../Gem/PythonTests/Atom/TestSuite_Main.py | 44 ++++++++++++++----- .../PythonTests/Atom/TestSuite_Main_GPU.py | 4 ++ .../Atom/TestSuite_Main_GPU_Optimized.py | 1 + .../Gem/PythonTests/Atom/TestSuite_Sandbox.py | 1 + 4 files changed, 39 insertions(+), 11 deletions(-) diff --git a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main.py b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main.py index e4a77ca4ec..3403c938a8 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main.py @@ -23,20 +23,20 @@ TEST_DIRECTORY = os.path.join(os.path.dirname(__file__), "tests") class TestAtomEditorComponentsMain(object): """Holds tests for Atom components.""" + @pytest.mark.test_case_id("C32078118") # Decal + @pytest.mark.test_case_id("C32078119") # DepthOfField + @pytest.mark.test_case_id("C32078120") # Directional Light + @pytest.mark.test_case_id("C32078121") # Exposure Control + @pytest.mark.test_case_id("C32078115") # Global Skylight (IBL) + @pytest.mark.test_case_id("C32078125") # Physical Sky + @pytest.mark.test_case_id("C32078127") # PostFX Layer + @pytest.mark.test_case_id("C32078131") # PostFX Radius Weight Modifier + @pytest.mark.test_case_id("C32078117") # Light + @pytest.mark.test_case_id("C36525660") # Display Mapper def test_AtomEditorComponents_AddedToEntity(self, request, editor, level, workspace, project, launcher_platform): """ Please review the hydra script run by this test for more specific test info. - Tests the following Atom components and verifies all "expected_lines" appear in Editor.log: - 1. Display Mapper - 2. Light - 3. PostFX Radius Weight Modifier - 4. PostFX Layer - 5. Physical Sky - 6. Global Skylight (IBL) - 7. Exposure Control - 8. Directional Light - 9. DepthOfField - 10. Decal + Tests the Atom components & verifies all "expected_lines" appear in Editor.log """ cfg_args = [level] @@ -69,6 +69,18 @@ class TestAtomEditorComponentsMain(object): "DepthOfField_test: Entity deleted: True", "DepthOfField_test: UNDO entity deletion works: True", "DepthOfField_test: REDO entity deletion works: True", + # Directional Light Component + "Directional Light Entity successfully created", + "Directional Light_test: Component added to the entity: True", + "Directional Light_test: Component removed after UNDO: True", + "Directional Light_test: Component added after REDO: True", + "Directional Light_test: Entered game mode: True", + "Directional Light_test: Exit game mode: True", + "Directional Light_test: Entity is hidden: True", + "Directional Light_test: Entity is shown: True", + "Directional Light_test: Entity deleted: True", + "Directional Light_test: UNDO entity deletion works: True", + "Directional Light_test: REDO entity deletion works: True", # Exposure Control Component "Exposure Control Entity successfully created", "Exposure Control_test: Component added to the entity: True", @@ -180,6 +192,7 @@ class TestAtomEditorComponentsMain(object): cfg_args=cfg_args, ) + @pytest.mark.test_case_id("C34525095") def test_AtomEditorComponents_LightComponent( self, request, editor, workspace, project, launcher_platform, level): """ @@ -266,6 +279,15 @@ class TestMaterialEditorBasicTests(object): request.addfinalizer(teardown) @pytest.mark.parametrize("exe_file_name", ["MaterialEditor"]) + @pytest.mark.test_case_id("C34448113") # Creating a New Asset. + @pytest.mark.test_case_id("C34448114") # Opening an Existing Asset. + @pytest.mark.test_case_id("C34448115") # Closing Selected Material. + @pytest.mark.test_case_id("C34448116") # Closing All Materials. + @pytest.mark.test_case_id("C34448117") # Closing all but Selected Material. + @pytest.mark.test_case_id("C34448118") # Saving Material. + @pytest.mark.test_case_id("C34448119") # Saving as a New Material. + @pytest.mark.test_case_id("C34448120") # Saving as a Child Material. + @pytest.mark.test_case_id("C34448121") # Saving all Open Materials. def test_MaterialEditorBasicTests( self, request, workspace, project, launcher_platform, generic_launcher, exe_file_name): diff --git a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_GPU.py b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_GPU.py index 220623af8b..74b064a555 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_GPU.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_GPU.py @@ -73,6 +73,7 @@ def create_screenshots_archive(screenshot_path): class TestAllComponentsIndepthTests(object): @pytest.mark.parametrize("screenshot_name", ["AtomBasicLevelSetup.ppm"]) + @pytest.mark.test_case_id("C34603773") def test_BasicLevelSetup_SetsUpLevel( self, request, editor, workspace, project, launcher_platform, level, screenshot_name): """ @@ -115,6 +116,7 @@ class TestAllComponentsIndepthTests(object): create_screenshots_archive(screenshot_directory) + @pytest.mark.test_case_id("C34525095") def test_LightComponent_ScreenshotMatchesGoldenImage( self, request, editor, workspace, project, launcher_platform, level): """ @@ -225,6 +227,8 @@ class TestMaterialEditor(object): pytest.param("-rhi=Vulkan", ["Registering vulkan RHI"]) ]) @pytest.mark.parametrize("exe_file_name", ["MaterialEditor"]) + @pytest.mark.test_case_id("C30973986") # Material Editor Launching in Dx12 + @pytest.mark.test_case_id("C30973987") # Material Editor Launching in Vulkan def test_MaterialEditorLaunch_AllRHIOptionsSucceed( self, request, workspace, project, launcher_platform, generic_launcher, exe_file_name, cfg_args, expected_lines): diff --git a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_GPU_Optimized.py b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_GPU_Optimized.py index ef572d6e5c..568768e12e 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_GPU_Optimized.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_GPU_Optimized.py @@ -23,6 +23,7 @@ class TestAutomation(EditorTestSuite): # Remove -autotest_mode from global_extra_cmdline_args since we need rendering for these tests. global_extra_cmdline_args = ["-BatchMode"] # Default is ["-BatchMode", "-autotest_mode"] + @pytest.mark.test_case_id("C34603773") class AtomGPU_BasicLevelSetup_SetsUpLevel(EditorSharedTest): use_null_renderer = False # Default is True screenshot_name = "AtomBasicLevelSetup.ppm" diff --git a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Sandbox.py b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Sandbox.py index 79caf26784..58e5d00ff2 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Sandbox.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Sandbox.py @@ -30,6 +30,7 @@ class TestAtomEditorComponentsSandbox(object): class TestAtomEditorComponentsMain(object): """Holds tests for Atom components.""" + @pytest.mark.test_case_id("C32078128") def test_AtomEditorComponents_ReflectionProbeAddedToEntity( self, request, editor, level, workspace, project, launcher_platform): """ From 56f0ea68a678ce7990ea616d31464da50d0a3e37 Mon Sep 17 00:00:00 2001 From: brianherrera Date: Wed, 13 Oct 2021 10:29:53 -0700 Subject: [PATCH 32/65] Add step to verify disk is in an offline state Some Windows configs will automatically set new drives as online causing diskpart setup script to fail. Signed-off-by: brianherrera --- .../build/bootstrap/incremental_build_util.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/scripts/build/bootstrap/incremental_build_util.py b/scripts/build/bootstrap/incremental_build_util.py index 5c77559085..a33eb2af80 100644 --- a/scripts/build/bootstrap/incremental_build_util.py +++ b/scripts/build/bootstrap/incremental_build_util.py @@ -252,6 +252,18 @@ def find_snapshot_id(ec2_client, snapshot_hint, repository_name, project, pipeli snapshot_id = snapshot['SnapshotId'] return snapshot_id + +def offline_drive(disk_number=1): + """Use diskpart to offline a Windows drive""" + with tempfile.NamedTemporaryFile(delete=False) as f: + f.write(f""" + select disk {disk_number} + offline disk + """.encode('utf-8')) + subprocess.run(['diskpart', '/s', f.name]) + os.unlink(f.name) + + def create_volume(ec2_client, availability_zone, snapshot_hint, repository_name, project, pipeline, branch, platform, build_type, disk_size, disk_type): # The actual EBS default calculation for IOps is a floating point number, the closest approxmiation is 4x of the disk size for simplicity mount_name = get_mount_name(repository_name, project, pipeline, branch, platform, build_type) @@ -310,6 +322,10 @@ def create_volume(ec2_client, availability_zone, snapshot_hint, repository_name, def mount_volume_to_device(created): print('Mounting volume...') if os.name == 'nt': + # Verify drive is in an offline state. + # Some Windows configs will automatically set new drives as online causing diskpart setup script to fail. + offline_drive() + f = tempfile.NamedTemporaryFile(delete=False) f.write(""" select disk 1 From ac8201a2faebfd361dadbfa96c527895426dfa2d Mon Sep 17 00:00:00 2001 From: brianherrera Date: Wed, 13 Oct 2021 10:34:03 -0700 Subject: [PATCH 33/65] Update disk setup step to use context manager Signed-off-by: brianherrera --- .../build/bootstrap/incremental_build_util.py | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/scripts/build/bootstrap/incremental_build_util.py b/scripts/build/bootstrap/incremental_build_util.py index a33eb2af80..493d8a9249 100644 --- a/scripts/build/bootstrap/incremental_build_util.py +++ b/scripts/build/bootstrap/incremental_build_util.py @@ -325,24 +325,22 @@ def mount_volume_to_device(created): # Verify drive is in an offline state. # Some Windows configs will automatically set new drives as online causing diskpart setup script to fail. offline_drive() - - f = tempfile.NamedTemporaryFile(delete=False) - f.write(""" - select disk 1 - online disk - attribute disk clear readonly - """.encode('utf-8')) # assume disk # for now + + with tempfile.NamedTemporaryFile(delete=False) as f: + f.write(""" + select disk 1 + online disk + attribute disk clear readonly + """.encode('utf-8')) # assume disk # for now if created: print('Creating filesystem on new volume') f.write("""create partition primary - select partition 1 - format quick fs=ntfs - assign - active - """.encode('utf-8')) - - f.close() + select partition 1 + format quick fs=ntfs + assign + active + """.encode('utf-8')) subprocess.call(['diskpart', '/s', f.name]) From 3c7357da479acd50fb19e0619a776c6ab00069fb Mon Sep 17 00:00:00 2001 From: brianherrera Date: Wed, 13 Oct 2021 10:35:42 -0700 Subject: [PATCH 34/65] Update existing unmount step to use new offline function Signed-off-by: brianherrera --- scripts/build/bootstrap/incremental_build_util.py | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/scripts/build/bootstrap/incremental_build_util.py b/scripts/build/bootstrap/incremental_build_util.py index 493d8a9249..6117fe0180 100644 --- a/scripts/build/bootstrap/incremental_build_util.py +++ b/scripts/build/bootstrap/incremental_build_util.py @@ -391,14 +391,7 @@ def unmount_volume_from_device(): print('Unmounting EBS volume from device...') if os.name == 'nt': kill_processes(MOUNT_PATH + 'workspace') - f = tempfile.NamedTemporaryFile(delete=False) - f.write(""" - select disk 1 - offline disk - """.encode('utf-8')) - f.close() - subprocess.call('diskpart /s %s' % f.name) - os.unlink(f.name) + offline_drive() else: kill_processes(MOUNT_PATH) subprocess.call(['umount', '-f', MOUNT_PATH]) From 26d53690b95dfec555b1caf8c6df6b3c239b6835 Mon Sep 17 00:00:00 2001 From: Junbo Liang <68558268+junbo75@users.noreply.github.com> Date: Wed, 13 Oct 2021 11:34:13 -0700 Subject: [PATCH 35/65] [GameLift][FlexMatch] Add client side change for match acceptance (#4634) * [GameLift][FlexMatch] Add client side change for match acceptance required Signed-off-by: Junbo Liang --- .../AWSGameLiftClientLocalTicketTracker.cpp | 2 + .../Source/AWSGameLiftClientManager.cpp | 46 +++++++- .../Source/AWSGameLiftClientManager.h | 2 + .../AWSGameLiftAcceptMatchActivity.cpp | 76 +++++++++++++ .../Activity/AWSGameLiftAcceptMatchActivity.h | 31 ++++++ .../AWSGameLiftStartMatchmakingActivity.cpp | 4 + .../AWSGameLiftStartMatchmakingActivity.h | 3 - .../AWSGameLiftStopMatchmakingActivity.cpp | 3 + .../AWSGameLiftStopMatchmakingActivity.h | 3 - ...WSGameLiftClientLocalTicketTrackerTest.cpp | 38 +++++++ .../Tests/AWSGameLiftClientManagerTest.cpp | 104 ++++++++++++++++++ .../Tests/AWSGameLiftClientMocks.h | 21 ++++ .../AWSGameLiftAcceptMatchActivityTest.cpp | 77 +++++++++++++ ...WSGameLiftStartMatchmakingActivityTest.cpp | 2 + ...AWSGameLiftStopMatchmakingActivityTest.cpp | 2 + .../awsgamelift_client_files.cmake | 2 + .../awsgamelift_client_tests_files.cmake | 1 + 17 files changed, 409 insertions(+), 8 deletions(-) create mode 100644 Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftAcceptMatchActivity.cpp create mode 100644 Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftAcceptMatchActivity.h create mode 100644 Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/Activity/AWSGameLiftAcceptMatchActivityTest.cpp diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientLocalTicketTracker.cpp b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientLocalTicketTracker.cpp index b619a10d4a..af0cf1c35c 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientLocalTicketTracker.cpp +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientLocalTicketTracker.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -109,6 +110,7 @@ namespace AWSGameLift else if (ticket.GetStatus() == Aws::GameLift::Model::MatchmakingConfigurationStatus::REQUIRES_ACCEPTANCE) { // broadcast acceptance requires to player + AzFramework::MatchAcceptanceNotificationBus::Broadcast(&AzFramework::MatchAcceptanceNotifications::OnMatchAcceptance); } else { diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientManager.cpp b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientManager.cpp index 91e76380eb..224f16481e 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientManager.cpp +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientManager.cpp @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -125,12 +126,53 @@ namespace AWSGameLift void AWSGameLiftClientManager::AcceptMatch(const AzFramework::AcceptMatchRequest& acceptMatchRequest) { - AZ_UNUSED(acceptMatchRequest); + if (AcceptMatchActivity::ValidateAcceptMatchRequest(acceptMatchRequest)) + { + const AWSGameLiftAcceptMatchRequest& gameliftStartMatchmakingRequest = + static_cast(acceptMatchRequest); + AcceptMatchHelper(gameliftStartMatchmakingRequest); + } } void AWSGameLiftClientManager::AcceptMatchAsync(const AzFramework::AcceptMatchRequest& acceptMatchRequest) { - AZ_UNUSED(acceptMatchRequest); + if (!AcceptMatchActivity::ValidateAcceptMatchRequest(acceptMatchRequest)) + { + AzFramework::MatchmakingAsyncRequestNotificationBus::Broadcast( + &AzFramework::MatchmakingAsyncRequestNotifications::OnAcceptMatchAsyncComplete); + return; + } + + const AWSGameLiftAcceptMatchRequest& gameliftStartMatchmakingRequest = static_cast(acceptMatchRequest); + + AZ::JobContext* jobContext = nullptr; + AWSCore::AWSCoreRequestBus::BroadcastResult(jobContext, &AWSCore::AWSCoreRequests::GetDefaultJobContext); + AZ::Job* acceptMatchJob = AZ::CreateJobFunction( + [this, gameliftStartMatchmakingRequest]() + { + AcceptMatchHelper(gameliftStartMatchmakingRequest); + + AzFramework::MatchmakingAsyncRequestNotificationBus::Broadcast( + &AzFramework::MatchmakingAsyncRequestNotifications::OnAcceptMatchAsyncComplete); + }, + true, jobContext); + + acceptMatchJob->Start(); + } + + void AWSGameLiftClientManager::AcceptMatchHelper(const AWSGameLiftAcceptMatchRequest& acceptMatchRequest) + { + auto gameliftClient = AZ::Interface::Get()->GetGameLiftClient(); + + AZStd::string response; + if (!gameliftClient) + { + AZ_Error(AWSGameLiftClientManagerName, false, AWSGameLiftClientMissingErrorMessage); + } + else + { + AcceptMatchActivity::AcceptMatch(*gameliftClient, acceptMatchRequest); + } } AZStd::string AWSGameLiftClientManager::CreateSession(const AzFramework::CreateSessionRequest& createSessionRequest) diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientManager.h b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientManager.h index ba81196b07..1f32b69f75 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientManager.h +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientManager.h @@ -15,6 +15,7 @@ namespace AWSGameLift { + struct AWSGameLiftAcceptMatchRequest; struct AWSGameLiftCreateSessionRequest; struct AWSGameLiftCreateSessionOnQueueRequest; struct AWSGameLiftJoinSessionRequest; @@ -158,6 +159,7 @@ namespace AWSGameLift void LeaveSession() override; private: + void AcceptMatchHelper(const AWSGameLiftAcceptMatchRequest& createSessionRequest); AZStd::string CreateSessionHelper(const AWSGameLiftCreateSessionRequest& createSessionRequest); AZStd::string CreateSessionOnQueueHelper(const AWSGameLiftCreateSessionOnQueueRequest& createSessionOnQueueRequest); bool JoinSessionHelper(const AWSGameLiftJoinSessionRequest& joinSessionRequest); diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftAcceptMatchActivity.cpp b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftAcceptMatchActivity.cpp new file mode 100644 index 0000000000..25ba328fd0 --- /dev/null +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftAcceptMatchActivity.cpp @@ -0,0 +1,76 @@ +/* + * 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.AcceptMatch + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include + +#include +#include + +#include +#include + +namespace AWSGameLift +{ + namespace AcceptMatchActivity + { + Aws::GameLift::Model::AcceptMatchRequest BuildAWSGameLiftAcceptMatchRequest( + const AWSGameLiftAcceptMatchRequest& acceptMatchRequest) + { + Aws::GameLift::Model::AcceptMatchRequest request; + request.SetAcceptanceType(acceptMatchRequest.m_acceptMatch ? + Aws::GameLift::Model::AcceptanceType::ACCEPT : Aws::GameLift::Model::AcceptanceType::REJECT); + + Aws::Vector playerIds; + for (const AZStd::string& playerId : acceptMatchRequest.m_playerIds) + { + playerIds.emplace_back(playerId.c_str()); + } + request.SetPlayerIds(playerIds); + + if (!acceptMatchRequest.m_ticketId.empty()) + { + request.SetTicketId(acceptMatchRequest.m_ticketId.c_str()); + } + + AZ_TracePrintf(AWSGameLiftAcceptMatchActivityName, "Built AcceptMatchRequest with TicketId=%s", request.GetTicketId().c_str()); + + return request; + } + + void AcceptMatch(const Aws::GameLift::GameLiftClient& gameliftClient, + const AWSGameLiftAcceptMatchRequest& AcceptMatchRequest) + { + AZ_TracePrintf(AWSGameLiftAcceptMatchActivityName, "Requesting AcceptMatch against Amazon GameLift service ..."); + + Aws::GameLift::Model::AcceptMatchRequest request = BuildAWSGameLiftAcceptMatchRequest(AcceptMatchRequest); + auto AcceptMatchOutcome = gameliftClient.AcceptMatch(request); + + if (AcceptMatchOutcome.IsSuccess()) + { + AZ_TracePrintf(AWSGameLiftAcceptMatchActivityName, "AcceptMatch request against Amazon GameLift service is complete"); + } + else + { + AZ_Error(AWSGameLiftAcceptMatchActivityName, false, AWSGameLiftErrorMessageTemplate, + AcceptMatchOutcome.GetError().GetExceptionName().c_str(), AcceptMatchOutcome.GetError().GetMessage().c_str()); + } + } + + bool ValidateAcceptMatchRequest(const AzFramework::AcceptMatchRequest& AcceptMatchRequest) + { + auto gameliftAcceptMatchRequest = azrtti_cast(&AcceptMatchRequest); + bool isValid = gameliftAcceptMatchRequest && + (gameliftAcceptMatchRequest->m_playerIds.size() > 0) && + (!gameliftAcceptMatchRequest->m_ticketId.empty()); + + AZ_Error(AWSGameLiftAcceptMatchActivityName, isValid, AWSGameLiftAcceptMatchRequestInvalidErrorMessage); + + return isValid; + } + } // namespace AcceptMatchActivity +} // namespace AWSGameLift diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftAcceptMatchActivity.h b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftAcceptMatchActivity.h new file mode 100644 index 0000000000..d5f28f92e2 --- /dev/null +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftAcceptMatchActivity.h @@ -0,0 +1,31 @@ +/* + * 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 + * + */ + +#pragma once + +#include + +#include + +namespace AWSGameLift +{ + namespace AcceptMatchActivity + { + static constexpr const char AWSGameLiftAcceptMatchActivityName[] = "AWSGameLiftAcceptMatchActivity"; + static constexpr const char AWSGameLiftAcceptMatchRequestInvalidErrorMessage[] = "Invalid GameLift AcceptMatch request."; + + // Build AWS GameLift AcceptMatchRequest by using AWSGameLiftAcceptMatchRequest + Aws::GameLift::Model::AcceptMatchRequest BuildAWSGameLiftAcceptMatchRequest(const AWSGameLiftAcceptMatchRequest& AcceptMatchRequest); + + // Create AcceptMatchRequest and make a AcceptMatch call through GameLift client + void AcceptMatch(const Aws::GameLift::GameLiftClient& gameliftClient, const AWSGameLiftAcceptMatchRequest& AcceptMatchRequest); + + // Validate AcceptMatchRequest and check required request parameters + bool ValidateAcceptMatchRequest(const AzFramework::AcceptMatchRequest& AcceptMatchRequest); + } // namespace AcceptMatchActivity +} // namespace AWSGameLift diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftStartMatchmakingActivity.cpp b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftStartMatchmakingActivity.cpp index 2b2a32f74b..e535ea0c55 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftStartMatchmakingActivity.cpp +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftStartMatchmakingActivity.cpp @@ -5,12 +5,16 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ + #include #include #include #include +#include +#include + namespace AWSGameLift { namespace StartMatchmakingActivity diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftStartMatchmakingActivity.h b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftStartMatchmakingActivity.h index 5f8c4558f4..db814c14b2 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftStartMatchmakingActivity.h +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftStartMatchmakingActivity.h @@ -10,9 +10,7 @@ #include -#include #include -#include namespace AWSGameLift { @@ -25,7 +23,6 @@ namespace AWSGameLift Aws::GameLift::Model::StartMatchmakingRequest BuildAWSGameLiftStartMatchmakingRequest(const AWSGameLiftStartMatchmakingRequest& startMatchmakingRequest); // Create StartMatchmakingRequest and make a StartMatchmaking call through GameLift client - // Will also start polling the matchmaking ticket when get success outcome from GameLift client AZStd::string StartMatchmaking(const Aws::GameLift::GameLiftClient& gameliftClient, const AWSGameLiftStartMatchmakingRequest& startMatchmakingRequest); // Validate StartMatchmakingRequest and check required request parameters diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftStopMatchmakingActivity.cpp b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftStopMatchmakingActivity.cpp index 204923fc02..b427d0323a 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftStopMatchmakingActivity.cpp +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftStopMatchmakingActivity.cpp @@ -11,6 +11,9 @@ #include #include +#include +#include + namespace AWSGameLift { namespace StopMatchmakingActivity diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftStopMatchmakingActivity.h b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftStopMatchmakingActivity.h index 9bfeb86343..0820f2c05e 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftStopMatchmakingActivity.h +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftStopMatchmakingActivity.h @@ -10,9 +10,7 @@ #include -#include #include -#include namespace AWSGameLift { @@ -25,7 +23,6 @@ namespace AWSGameLift Aws::GameLift::Model::StopMatchmakingRequest BuildAWSGameLiftStopMatchmakingRequest(const AWSGameLiftStopMatchmakingRequest& stopMatchmakingRequest); // Create StopMatchmakingRequest and make a StopMatchmaking call through GameLift client - // Will also stop polling the matchmaking ticket when get success outcome from GameLift client void StopMatchmaking(const Aws::GameLift::GameLiftClient& gameliftClient, const AWSGameLiftStopMatchmakingRequest& stopMatchmakingRequest); // Validate StopMatchmakingRequest and check required request parameters diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientLocalTicketTrackerTest.cpp b/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientLocalTicketTrackerTest.cpp index 6506d70704..4dc4dd85f6 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientLocalTicketTrackerTest.cpp +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientLocalTicketTrackerTest.cpp @@ -351,3 +351,41 @@ TEST_F(AWSGameLiftClientLocalTicketTrackerTest, StartPolling_CallAndTicketComple WaitForProcessFinish(); ASSERT_TRUE(m_gameliftClientTicketTracker->IsTrackerIdle()); } + +TEST_F(AWSGameLiftClientLocalTicketTrackerTest, StartPolling_RequiresAcceptanceAndTicketCompleteAtLast_ProcessContinuesAndStop) +{ + Aws::GameLift::Model::MatchmakingTicket ticket1; + ticket1.SetStatus(Aws::GameLift::Model::MatchmakingConfigurationStatus::REQUIRES_ACCEPTANCE); + + Aws::GameLift::Model::DescribeMatchmakingResult result1; + result1.AddTicketList(ticket1); + Aws::GameLift::Model::DescribeMatchmakingOutcome outcome1(result1); + + Aws::GameLift::Model::GameSessionConnectionInfo connectionInfo; + connectionInfo.SetIpAddress("DummyIpAddress"); + connectionInfo.SetPort(123); + connectionInfo.AddMatchedPlayerSessions( + Aws::GameLift::Model::MatchedPlayerSession().WithPlayerId("player1").WithPlayerSessionId("playersession1")); + + Aws::GameLift::Model::MatchmakingTicket ticket2; + ticket2.SetStatus(Aws::GameLift::Model::MatchmakingConfigurationStatus::COMPLETED); + ticket2.SetGameSessionConnectionInfo(connectionInfo); + + Aws::GameLift::Model::DescribeMatchmakingResult result2; + result2.AddTicketList(ticket2); + Aws::GameLift::Model::DescribeMatchmakingOutcome outcome2(result2); + + EXPECT_CALL(*m_gameliftClientMockPtr, DescribeMatchmaking(::testing::_)) + .WillOnce(::testing::Return(outcome1)) + .WillOnce(::testing::Return(outcome2)); + + MatchAcceptanceNotificationsHandlerMock handlerMock1; + EXPECT_CALL(handlerMock1, OnMatchAcceptance()).Times(1); + + SessionHandlingClientRequestsMock handlerMock2; + EXPECT_CALL(handlerMock2, RequestPlayerJoinSession(::testing::_)).Times(1).WillOnce(::testing::Return(true)); + + m_gameliftClientTicketTracker->StartPolling("ticket1", "player1"); + WaitForProcessFinish(); + ASSERT_TRUE(m_gameliftClientTicketTracker->IsTrackerIdle()); +} diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientManagerTest.cpp b/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientManagerTest.cpp index 2fa332df72..3b7c7ee827 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientManagerTest.cpp +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientManagerTest.cpp @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -1005,3 +1006,106 @@ TEST_F(AWSGameLiftClientManagerTest, StopMatchmakingAsync_CallWithValidRequest_G m_gameliftClientManager->StopMatchmakingAsync(request); AZ_TEST_STOP_TRACE_SUPPRESSION(1); // capture 1 error message } + +TEST_F(AWSGameLiftClientManagerTest, AcceptMatch_CallWithoutClientSetup_GetError) +{ + AZ_TEST_START_TRACE_SUPPRESSION; + m_gameliftClientManager->ConfigureGameLiftClient(""); + AWSGameLiftAcceptMatchRequest request; + request.m_acceptMatch = true; + request.m_playerIds = { DummyPlayerId }; + request.m_ticketId = DummyMatchmakingTicketId; + + m_gameliftClientManager->AcceptMatch(request); + AZ_TEST_STOP_TRACE_SUPPRESSION(2); // capture 2 error message +} +TEST_F(AWSGameLiftClientManagerTest, AcceptMatch_CallWithInvalidRequest_GetError) +{ + AZ_TEST_START_TRACE_SUPPRESSION; + m_gameliftClientManager->AcceptMatch(AzFramework::AcceptMatchRequest()); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); // capture 1 error message +} + +TEST_F(AWSGameLiftClientManagerTest, AcceptMatch_CallWithValidRequest_Success) +{ + AWSGameLiftAcceptMatchRequest request; + request.m_acceptMatch = true; + request.m_playerIds = { DummyPlayerId }; + request.m_ticketId = DummyMatchmakingTicketId; + + Aws::GameLift::Model::AcceptMatchResult result; + Aws::GameLift::Model::AcceptMatchResult outcome(result); + EXPECT_CALL(*m_gameliftClientMockPtr, AcceptMatch(::testing::_)).Times(1).WillOnce(::testing::Return(outcome)); + + m_gameliftClientManager->AcceptMatch(request); +} + +TEST_F(AWSGameLiftClientManagerTest, AcceptMatch_CallWithValidRequest_GetError) +{ + AWSGameLiftAcceptMatchRequest request; + request.m_acceptMatch = true; + request.m_playerIds = { DummyPlayerId }; + request.m_ticketId = DummyMatchmakingTicketId; + + Aws::Client::AWSError error; + Aws::GameLift::Model::AcceptMatchOutcome outcome(error); + + EXPECT_CALL(*m_gameliftClientMockPtr, AcceptMatch(::testing::_)).Times(1).WillOnce(::testing::Return(outcome)); + AZ_TEST_START_TRACE_SUPPRESSION; + m_gameliftClientManager->AcceptMatch(request); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); // capture 1 error message +} + +TEST_F(AWSGameLiftClientManagerTest, AcceptMatchAsync_CallWithInvalidRequest_GetNotificationWithError) +{ + AWSGameLiftAcceptMatchRequest request; + + MatchmakingAsyncRequestNotificationsHandlerMock matchmakingHandlerMock; + EXPECT_CALL(matchmakingHandlerMock, OnAcceptMatchAsyncComplete()).Times(1); + + AZ_TEST_START_TRACE_SUPPRESSION; + m_gameliftClientManager->AcceptMatchAsync(request); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); // capture 1 error message +} + +TEST_F(AWSGameLiftClientManagerTest, AcceptMatchAsync_CallWithValidRequest_GetNotification) +{ + AWSCoreRequestsHandlerMock handlerMock; + EXPECT_CALL(handlerMock, GetDefaultJobContext()).Times(1).WillOnce(::testing::Return(m_jobContext.get())); + + AWSGameLiftAcceptMatchRequest request; + request.m_acceptMatch = true; + request.m_playerIds = { DummyPlayerId }; + request.m_ticketId = DummyMatchmakingTicketId; + + Aws::GameLift::Model::AcceptMatchResult result; + Aws::GameLift::Model::AcceptMatchOutcome outcome(result); + EXPECT_CALL(*m_gameliftClientMockPtr, AcceptMatch(::testing::_)).Times(1).WillOnce(::testing::Return(outcome)); + + MatchmakingAsyncRequestNotificationsHandlerMock matchmakingHandlerMock; + EXPECT_CALL(matchmakingHandlerMock, OnAcceptMatchAsyncComplete()).Times(1); + + m_gameliftClientManager->AcceptMatchAsync(request); +} + +TEST_F(AWSGameLiftClientManagerTest, AcceptMatchAsync_CallWithValidRequest_GetNotificationWithError) +{ + AWSCoreRequestsHandlerMock handlerMock; + EXPECT_CALL(handlerMock, GetDefaultJobContext()).Times(1).WillOnce(::testing::Return(m_jobContext.get())); + + AWSGameLiftAcceptMatchRequest request; + request.m_acceptMatch = true; + request.m_playerIds = { DummyPlayerId }; + request.m_ticketId = DummyMatchmakingTicketId; + + Aws::Client::AWSError error; + Aws::GameLift::Model::AcceptMatchOutcome outcome(error); + EXPECT_CALL(*m_gameliftClientMockPtr, AcceptMatch(::testing::_)).Times(1).WillOnce(::testing::Return(outcome)); + + MatchmakingAsyncRequestNotificationsHandlerMock matchmakingHandlerMock; + EXPECT_CALL(matchmakingHandlerMock, OnAcceptMatchAsyncComplete()).Times(1); + + AZ_TEST_START_TRACE_SUPPRESSION; + m_gameliftClientManager->AcceptMatchAsync(request); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); // capture 1 error message +} diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientMocks.h b/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientMocks.h index 964b6a21c2..6ba05747aa 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientMocks.h +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientMocks.h @@ -9,6 +9,7 @@ #pragma once #include +#include #include #include #include @@ -18,6 +19,8 @@ #include #include #include +#include +#include #include #include #include @@ -46,6 +49,7 @@ public: { } + MOCK_CONST_METHOD1(AcceptMatch, Model::AcceptMatchOutcome(const Model::AcceptMatchRequest&)); MOCK_CONST_METHOD1(CreateGameSession, Model::CreateGameSessionOutcome(const Model::CreateGameSessionRequest&)); MOCK_CONST_METHOD1(CreatePlayerSession, Model::CreatePlayerSessionOutcome(const Model::CreatePlayerSessionRequest&)); MOCK_CONST_METHOD1(DescribeMatchmaking, Model::DescribeMatchmakingOutcome(const Model::DescribeMatchmakingRequest&)); @@ -74,6 +78,23 @@ public: MOCK_METHOD0(OnStopMatchmakingAsyncComplete, void()); }; +class MatchAcceptanceNotificationsHandlerMock + : public AzFramework::MatchAcceptanceNotificationBus::Handler +{ +public: + MatchAcceptanceNotificationsHandlerMock() + { + AzFramework::MatchAcceptanceNotificationBus::Handler::BusConnect(); + } + + ~MatchAcceptanceNotificationsHandlerMock() + { + AzFramework::MatchAcceptanceNotificationBus::Handler::BusDisconnect(); + } + + MOCK_METHOD0(OnMatchAcceptance, void()); +}; + class SessionAsyncRequestNotificationsHandlerMock : public AzFramework::SessionAsyncRequestNotificationBus::Handler { diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/Activity/AWSGameLiftAcceptMatchActivityTest.cpp b/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/Activity/AWSGameLiftAcceptMatchActivityTest.cpp new file mode 100644 index 0000000000..af78ca8c0c --- /dev/null +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/Activity/AWSGameLiftAcceptMatchActivityTest.cpp @@ -0,0 +1,77 @@ +/* + * 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 + +using namespace AWSGameLift; + +using AWSGameLiftAcceptMatchActivityTest = AWSGameLiftClientFixture; + +TEST_F(AWSGameLiftAcceptMatchActivityTest, BuildAWSGameLiftAcceptMatchRequest_Call_GetExpectedResult) +{ + AWSGameLiftAcceptMatchRequest request; + request.m_acceptMatch = true; + request.m_ticketId = "dummyTicketId"; + request.m_playerIds = { "dummyPlayerId" }; + + auto awsRequest = AcceptMatchActivity::BuildAWSGameLiftAcceptMatchRequest(request); + + EXPECT_EQ(awsRequest.GetAcceptanceType(), Aws::GameLift::Model::AcceptanceType::ACCEPT); + EXPECT_TRUE(strcmp(awsRequest.GetTicketId().c_str(), request.m_ticketId.c_str()) == 0); + EXPECT_EQ(awsRequest.GetPlayerIds().size(), request.m_playerIds.size()); + EXPECT_TRUE(strcmp(awsRequest.GetPlayerIds().begin()->c_str(), request.m_playerIds.begin()->c_str()) == 0); +} + +TEST_F(AWSGameLiftAcceptMatchActivityTest, ValidateAcceptMatchRequest_CallWithBaseType_GetFalseResult) +{ + AZ_TEST_START_TRACE_SUPPRESSION; + auto result = AcceptMatchActivity::ValidateAcceptMatchRequest(AzFramework::AcceptMatchRequest()); + EXPECT_FALSE(result); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); // capture 1 error message +} + +TEST_F(AWSGameLiftAcceptMatchActivityTest, ValidateAcceptMatchRequest_CallWithoutTicketId_GetFalseResult) +{ + AWSGameLiftAcceptMatchRequest request; + request.m_acceptMatch = true; + request.m_playerIds = { "dummyPlayerId" }; + + AZ_TEST_START_TRACE_SUPPRESSION; + auto result = AcceptMatchActivity::ValidateAcceptMatchRequest(request); + EXPECT_FALSE(result); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); // capture 1 error message +} + +TEST_F(AWSGameLiftAcceptMatchActivityTest, ValidateAcceptMatchRequest_CallWithoutPlayerIds_GetFalseResult) +{ + AWSGameLiftAcceptMatchRequest request; + request.m_acceptMatch = true; + request.m_playerIds = { "dummyPlayerId" }; + + AZ_TEST_START_TRACE_SUPPRESSION; + auto result = AcceptMatchActivity::ValidateAcceptMatchRequest(request); + EXPECT_FALSE(result); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); // capture 1 error message +} + +TEST_F(AWSGameLiftAcceptMatchActivityTest, ValidateAcceptMatchRequest_CallWithValidAttributes_GetTrueResult) +{ + + AWSGameLiftAcceptMatchRequest request; + request.m_acceptMatch = true; + request.m_ticketId = "dummyTicketId"; + request.m_playerIds = { "dummyPlayerId" }; + + auto result = AcceptMatchActivity::ValidateAcceptMatchRequest(request); + EXPECT_TRUE(result); +} diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/Activity/AWSGameLiftStartMatchmakingActivityTest.cpp b/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/Activity/AWSGameLiftStartMatchmakingActivityTest.cpp index 706aec04f2..3eed6b0448 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/Activity/AWSGameLiftStartMatchmakingActivityTest.cpp +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/Activity/AWSGameLiftStartMatchmakingActivityTest.cpp @@ -9,6 +9,8 @@ #include #include +#include + using namespace AWSGameLift; using AWSGameLiftStartMatchmakingActivityTest = AWSGameLiftClientFixture; diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/Activity/AWSGameLiftStopMatchmakingActivityTest.cpp b/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/Activity/AWSGameLiftStopMatchmakingActivityTest.cpp index 6b53ed5055..ba0e40c7e6 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/Activity/AWSGameLiftStopMatchmakingActivityTest.cpp +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/Activity/AWSGameLiftStopMatchmakingActivityTest.cpp @@ -9,6 +9,8 @@ #include #include +#include + using namespace AWSGameLift; using AWSGameLiftStopMatchmakingActivityTest = AWSGameLiftClientFixture; diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/awsgamelift_client_files.cmake b/Gems/AWSGameLift/Code/AWSGameLiftClient/awsgamelift_client_files.cmake index 3122ff2261..0930c42370 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/awsgamelift_client_files.cmake +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/awsgamelift_client_files.cmake @@ -18,6 +18,8 @@ set(FILES Include/Request/IAWSGameLiftRequests.h Source/Activity/AWSGameLiftActivityUtils.cpp Source/Activity/AWSGameLiftActivityUtils.h + Source/Activity/AWSGameLiftAcceptMatchActivity.cpp + Source/Activity/AWSGameLiftAcceptMatchActivity.h Source/Activity/AWSGameLiftCreateSessionActivity.cpp Source/Activity/AWSGameLiftCreateSessionActivity.h Source/Activity/AWSGameLiftCreateSessionOnQueueActivity.cpp diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/awsgamelift_client_tests_files.cmake b/Gems/AWSGameLift/Code/AWSGameLiftClient/awsgamelift_client_tests_files.cmake index f8ef40d5df..3e73dd8a0a 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/awsgamelift_client_tests_files.cmake +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/awsgamelift_client_tests_files.cmake @@ -7,6 +7,7 @@ # set(FILES + Tests/Activity/AWSGameLiftAcceptMatchActivityTest.cpp Tests/Activity/AWSGameLiftCreateSessionActivityTest.cpp Tests/Activity/AWSGameLiftCreateSessionOnQueueActivityTest.cpp Tests/Activity/AWSGameLiftJoinSessionActivityTest.cpp From afdf35a925d987c16d8399540f04d62212376d2c Mon Sep 17 00:00:00 2001 From: Guthrie Adams Date: Wed, 13 Oct 2021 13:41:31 -0500 Subject: [PATCH 36/65] updated material editor skybox and changed property icons Signed-off-by: Guthrie Adams --- .../Code/Source/Inspector/Icons/blank.png | 3 +++ .../Inspector/Icons/changed_property.svg | 5 +++++ .../Code/Source/Inspector/InspectorWidget.qrc | 2 ++ .../Code/Source/Window/Icons/skybox.svg | 21 ++++++------------- .../MaterialInspector/MaterialInspector.cpp | 4 ++-- .../Window/ToolBar/MaterialEditorToolBar.cpp | 12 +++++------ .../EditorMaterialComponentInspector.cpp | 4 ++-- 7 files changed, 26 insertions(+), 25 deletions(-) create mode 100644 Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/Icons/blank.png create mode 100644 Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/Icons/changed_property.svg diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/Icons/blank.png b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/Icons/blank.png new file mode 100644 index 0000000000..d040fa2e14 --- /dev/null +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/Icons/blank.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:81b5fa1f978888c3be8a40fce20455668df2723a77587aeb7039f8bf74bdd0e3 +size 119 diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/Icons/changed_property.svg b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/Icons/changed_property.svg new file mode 100644 index 0000000000..c33e340a54 --- /dev/null +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/Icons/changed_property.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/InspectorWidget.qrc b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/InspectorWidget.qrc index 81a962801d..76733c52ed 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/InspectorWidget.qrc +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/InspectorWidget.qrc @@ -2,5 +2,7 @@ Icons/group_closed.png Icons/group_open.png + Icons/blank.png + Icons/changed_property.svg diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/Icons/skybox.svg b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/Icons/skybox.svg index 83df996198..a79bebdd46 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/Icons/skybox.svg +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/Icons/skybox.svg @@ -1,15 +1,6 @@ - - - - icon / Environmental / Sky Highlight - Created with Sketch. - - - - - - - - - - \ No newline at end of file + + + + + + diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialInspector/MaterialInspector.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialInspector/MaterialInspector.cpp index 025de21d31..e99f456653 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialInspector/MaterialInspector.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialInspector/MaterialInspector.cpp @@ -101,9 +101,9 @@ namespace MaterialEditor { if (IsInstanceNodePropertyModifed(node)) { - return ":/PropertyEditor/Resources/changed_data_item.png"; + return ":/Icons/changed_property.svg"; } - return ":/PropertyEditor/Resources/blank.png"; + return ":/Icons/blank.png"; } void MaterialInspector::AddOverviewGroup() diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/ToolBar/MaterialEditorToolBar.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/ToolBar/MaterialEditorToolBar.cpp index 1e189168da..10442e0c27 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/ToolBar/MaterialEditorToolBar.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/ToolBar/MaterialEditorToolBar.cpp @@ -88,18 +88,18 @@ namespace MaterialEditor toneMappingButton->setVisible(true); addWidget(toneMappingButton); - // Add model combo box - auto modelPresetComboBox = new ModelPresetComboBox(this); - modelPresetComboBox->setSizeAdjustPolicy(QComboBox::SizeAdjustPolicy::AdjustToContents); - modelPresetComboBox->view()->setMinimumWidth(200); - addWidget(modelPresetComboBox); - // Add lighting preset combo box auto lightingPresetComboBox = new LightingPresetComboBox(this); lightingPresetComboBox->setSizeAdjustPolicy(QComboBox::SizeAdjustPolicy::AdjustToContents); lightingPresetComboBox->view()->setMinimumWidth(200); addWidget(lightingPresetComboBox); + // Add model combo box + auto modelPresetComboBox = new ModelPresetComboBox(this); + modelPresetComboBox->setSizeAdjustPolicy(QComboBox::SizeAdjustPolicy::AdjustToContents); + modelPresetComboBox->view()->setMinimumWidth(200); + addWidget(modelPresetComboBox); + MaterialViewportNotificationBus::Handler::BusConnect(); } diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.cpp index 665adcd568..815f14aba7 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.cpp @@ -521,9 +521,9 @@ namespace AZ { if (IsInstanceNodePropertyModifed(node)) { - return ":/PropertyEditor/Resources/changed_data_item.png"; + return ":/Icons/changed_property.svg"; } - return ":/PropertyEditor/Resources/blank.png"; + return ":/Icons/blank.png"; } bool MaterialPropertyInspector::SaveMaterial() const From f1fa0acbb483cd7a917a2051a922c0d6bdac6e20 Mon Sep 17 00:00:00 2001 From: sweeneys Date: Wed, 13 Oct 2021 11:44:15 -0700 Subject: [PATCH 37/65] Stabilize asset processor immediately exiting Signed-off-by: sweeneys --- Tools/LyTestTools/ly_test_tools/o3de/asset_processor.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Tools/LyTestTools/ly_test_tools/o3de/asset_processor.py b/Tools/LyTestTools/ly_test_tools/o3de/asset_processor.py index 0623715350..682fcd3560 100644 --- a/Tools/LyTestTools/ly_test_tools/o3de/asset_processor.py +++ b/Tools/LyTestTools/ly_test_tools/o3de/asset_processor.py @@ -488,6 +488,9 @@ class AssetProcessor(object): logger.info(f"Launching AP with command: {command}") try: self._ap_proc = subprocess.Popen(command, cwd=ap_exe_path, env=process_utils.get_display_env()) + time.sleep(1) + if self._ap_proc.poll() is not None: + raise AssetProcessorError(f"AssetProcessor immediately quit with errorcode {self._ap_proc.returncode}") if accept_input: self.connect_control() @@ -506,10 +509,11 @@ class AssetProcessor(object): logger.exception("Exception while starting Asset Processor", be) # clean up to avoid leaking open AP process to future tests try: - self._ap_proc.kill() + if self._ap_proc: + self._ap_proc.kill() except Exception as ex: logger.exception("Ignoring exception while trying to terminate Asset Processor", ex) - raise # raise whatever prompted us to clean up + raise be # raise whatever prompted us to clean up def connect_listen(self, timeout=DEFAULT_TIMEOUT_SECONDS): # Wait for the AP we launched to be ready to accept a connection From da3a39a6a040abddc6267a7750a65e20da4d878b Mon Sep 17 00:00:00 2001 From: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> Date: Wed, 13 Oct 2021 12:37:07 -0700 Subject: [PATCH 38/65] LYN-7121 | Focus Mode - Make editing a prefab an undoable operation (#4582) * Refactor the PrefabFocusInterface to differentiate between Public and Internal functions. Introduce PrefabFocusUndo nodes to allow undoing Prefab Edit operations. Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> * Fix selection code to avoid warning message Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> * Removed changed property from PrefabFocusUndo node Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> * Switch from size == 0 to empty in EntityOutlinerWidget::OnSelectionChanged Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> * condense if check on Prefab Edit context menu item setup Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> * Correct interface usage in PrefabIntegrationManager (interface was renamed to public) Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> * Remove rej file that was included by mistake Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> * Fix missing interface initialization in PrefabFocusTests Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> --- .../Prefab/PrefabFocusHandler.cpp | 74 ++++++++++++++----- .../Prefab/PrefabFocusHandler.h | 10 ++- .../Prefab/PrefabFocusInterface.h | 20 +---- .../Prefab/PrefabFocusPublicInterface.h | 53 +++++++++++++ .../Prefab/PrefabFocusUndo.cpp | 52 +++++++++++++ .../AzToolsFramework/Prefab/PrefabFocusUndo.h | 39 ++++++++++ .../UI/Outliner/EntityOutlinerWidget.cpp | 3 +- .../UI/Prefab/PrefabIntegrationManager.cpp | 27 +++---- .../UI/Prefab/PrefabIntegrationManager.h | 4 +- .../UI/Prefab/PrefabUiHandler.cpp | 16 ++-- .../UI/Prefab/PrefabUiHandler.h | 4 +- .../Prefab/PrefabViewportFocusPathHandler.cpp | 14 ++-- .../Prefab/PrefabViewportFocusPathHandler.h | 4 +- .../aztoolsframework_files.cmake | 3 + .../Prefab/PrefabFocus/PrefabFocusTests.cpp | 43 ++++++----- 15 files changed, 268 insertions(+), 98 deletions(-) create mode 100644 Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusPublicInterface.h create mode 100644 Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusUndo.cpp create mode 100644 Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusUndo.h diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp index fcec1edd54..a79b9eb73d 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp @@ -8,12 +8,14 @@ #include +#include #include #include #include #include #include #include +#include namespace AzToolsFramework::Prefab { @@ -28,10 +30,12 @@ namespace AzToolsFramework::Prefab EditorEntityContextNotificationBus::Handler::BusConnect(); AZ::Interface::Register(this); + AZ::Interface::Register(this); } PrefabFocusHandler::~PrefabFocusHandler() { + AZ::Interface::Unregister(this); AZ::Interface::Unregister(this); EditorEntityContextNotificationBus::Handler::BusDisconnect(); } @@ -61,6 +65,44 @@ namespace AzToolsFramework::Prefab } PrefabFocusOperationResult PrefabFocusHandler::FocusOnOwningPrefab(AZ::EntityId entityId) + { + // Initialize Undo Batch object + ScopedUndoBatch undoBatch("Edit Prefab"); + + // Clear selection + { + const EntityIdList selectedEntities = EntityIdList{}; + auto selectionUndo = aznew SelectionCommand(selectedEntities, "Clear Selection"); + selectionUndo->SetParent(undoBatch.GetUndoBatch()); + ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequestBus::Events::SetSelectedEntities, selectedEntities); + } + + // Edit Prefab + { + auto editUndo = aznew PrefabFocusUndo("Edit Prefab"); + editUndo->Capture(entityId); + editUndo->SetParent(undoBatch.GetUndoBatch()); + ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequestBus::Events::RunRedoSeparately, editUndo); + } + + return AZ::Success(); + } + + PrefabFocusOperationResult PrefabFocusHandler::FocusOnPathIndex([[maybe_unused]] AzFramework::EntityContextId entityContextId, int index) + { + if (index < 0 || index >= m_instanceFocusVector.size()) + { + return AZ::Failure(AZStd::string("Prefab Focus Handler: Invalid index on FocusOnPathIndex.")); + } + + InstanceOptionalReference focusedInstance = m_instanceFocusVector[index]; + + FocusOnOwningPrefab(focusedInstance->get().GetContainerEntityId()); + + return AZ::Success(); + } + + PrefabFocusOperationResult PrefabFocusHandler::FocusOnPrefabInstanceOwningEntityId(AZ::EntityId entityId) { InstanceOptionalReference focusedInstance; @@ -85,18 +127,6 @@ namespace AzToolsFramework::Prefab return FocusOnPrefabInstance(focusedInstance); } - PrefabFocusOperationResult PrefabFocusHandler::FocusOnPathIndex([[maybe_unused]] AzFramework::EntityContextId entityContextId, int index) - { - if (index < 0 || index >= m_instanceFocusVector.size()) - { - return AZ::Failure(AZStd::string("Prefab Focus Handler: Invalid index on FocusOnPathIndex.")); - } - - InstanceOptionalReference focusedInstance = m_instanceFocusVector[index]; - - return FocusOnPrefabInstance(focusedInstance); - } - PrefabFocusOperationResult PrefabFocusHandler::FocusOnPrefabInstance(InstanceOptionalReference focusedInstance) { if (!focusedInstance.has_value()) @@ -122,17 +152,10 @@ namespace AzToolsFramework::Prefab if (focusedInstance->get().GetParentInstance() != AZStd::nullopt) { containerEntityId = focusedInstance->get().GetContainerEntityId(); - - // Select the container entity - AzToolsFramework::SelectEntity(containerEntityId); } else { containerEntityId = AZ::EntityId(); - - // Clear the selection - AzToolsFramework::SelectEntities({}); - } // Focus on the descendants of the container entity @@ -161,6 +184,17 @@ namespace AzToolsFramework::Prefab return m_focusedInstance; } + AZ::EntityId PrefabFocusHandler::GetFocusedPrefabContainerEntityId([[maybe_unused]] AzFramework::EntityContextId entityContextId) const + { + if (!m_focusedInstance.has_value()) + { + // PrefabFocusHandler has not been initialized yet. + return AZ::EntityId(); + } + + return m_focusedInstance->get().GetContainerEntityId(); + } + bool PrefabFocusHandler::IsOwningPrefabBeingFocused(AZ::EntityId entityId) const { if (!m_focusedInstance.has_value()) @@ -200,7 +234,7 @@ namespace AzToolsFramework::Prefab m_instanceFocusVector.clear(); // Focus on the root prefab (AZ::EntityId() will default to it) - FocusOnOwningPrefab(AZ::EntityId()); + FocusOnPrefabInstanceOwningEntityId(AZ::EntityId()); } void PrefabFocusHandler::RefreshInstanceFocusList() diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h index 2f631f772d..80b7a6859c 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h @@ -13,6 +13,7 @@ #include #include #include +#include #include namespace AzToolsFramework @@ -28,6 +29,7 @@ namespace AzToolsFramework::Prefab //! Handles Prefab Focus mode, determining which prefab file entity changes will target. class PrefabFocusHandler final : private PrefabFocusInterface + , private PrefabFocusPublicInterface , private EditorEntityContextNotificationBus::Handler { public: @@ -39,10 +41,14 @@ namespace AzToolsFramework::Prefab void Initialize(); // PrefabFocusInterface overrides ... - PrefabFocusOperationResult FocusOnOwningPrefab(AZ::EntityId entityId) override; - PrefabFocusOperationResult FocusOnPathIndex(AzFramework::EntityContextId entityContextId, int index) override; + PrefabFocusOperationResult FocusOnPrefabInstanceOwningEntityId(AZ::EntityId entityId) override; TemplateId GetFocusedPrefabTemplateId(AzFramework::EntityContextId entityContextId) const override; InstanceOptionalReference GetFocusedPrefabInstance(AzFramework::EntityContextId entityContextId) const override; + + // PrefabFocusPublicInterface overrides ... + PrefabFocusOperationResult FocusOnOwningPrefab(AZ::EntityId entityId) override; + PrefabFocusOperationResult FocusOnPathIndex(AzFramework::EntityContextId entityContextId, int index) override; + AZ::EntityId GetFocusedPrefabContainerEntityId(AzFramework::EntityContextId entityContextId) const override; bool IsOwningPrefabBeingFocused(AZ::EntityId entityId) const override; const AZ::IO::Path& GetPrefabFocusPath(AzFramework::EntityContextId entityContextId) const override; const int GetPrefabFocusPathLength(AzFramework::EntityContextId entityContextId) const override; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusInterface.h index 1c0f4f85e9..25c83b89bc 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusInterface.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusInterface.h @@ -20,7 +20,7 @@ namespace AzToolsFramework::Prefab { using PrefabFocusOperationResult = AZ::Outcome; - //! Interface to handle operations related to the Prefab Focus system. + //! Interface to handle internal operations related to the Prefab Focus system. class PrefabFocusInterface { public: @@ -28,29 +28,13 @@ namespace AzToolsFramework::Prefab //! Set the focused prefab instance to the owning instance of the entityId provided. //! @param entityId The entityId of the entity whose owning instance we want the prefab system to focus on. - virtual PrefabFocusOperationResult FocusOnOwningPrefab(AZ::EntityId entityId) = 0; - - //! Set the focused prefab instance to the instance at position index of the current path. - //! @param index The index of the instance in the current path that we want the prefab system to focus on. - virtual PrefabFocusOperationResult FocusOnPathIndex(AzFramework::EntityContextId entityContextId, int index) = 0; + virtual PrefabFocusOperationResult FocusOnPrefabInstanceOwningEntityId(AZ::EntityId entityId) = 0; //! Returns the template id of the instance the prefab system is focusing on. virtual TemplateId GetFocusedPrefabTemplateId(AzFramework::EntityContextId entityContextId) const = 0; //! Returns a reference to the instance the prefab system is focusing on. virtual InstanceOptionalReference GetFocusedPrefabInstance(AzFramework::EntityContextId entityContextId) const = 0; - - //! Returns whether the entity belongs to the instance that is being focused on, or one of its descendants. - //! @param entityId The entityId of the queried entity. - //! @return true if the entity belongs to the focused instance or one of its descendants, false otherwise. - virtual bool IsOwningPrefabBeingFocused(AZ::EntityId entityId) const = 0; - - //! Returns the path from the root instance to the currently focused instance. - //! @return A path composed from the names of the container entities for the instance path. - virtual const AZ::IO::Path& GetPrefabFocusPath(AzFramework::EntityContextId entityContextId) const = 0; - - //! Returns the size of the path to the currently focused instance. - virtual const int GetPrefabFocusPathLength(AzFramework::EntityContextId entityContextId) const = 0; }; } // namespace AzToolsFramework::Prefab diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusPublicInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusPublicInterface.h new file mode 100644 index 0000000000..86e476b56f --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusPublicInterface.h @@ -0,0 +1,53 @@ +/* + * 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 + * + */ + +#pragma once + +#include +#include + +#include + +#include +#include + +namespace AzToolsFramework::Prefab +{ + using PrefabFocusOperationResult = AZ::Outcome; + + //! Public Interface for external systems to utilize the Prefab Focus system. + class PrefabFocusPublicInterface + { + public: + AZ_RTTI(PrefabFocusPublicInterface, "{53EE1D18-A41F-4DB1-9B73-9448F425722E}"); + + //! Set the focused prefab instance to the owning instance of the entityId provided. Supports undo/redo. + //! @param entityId The entityId of the entity whose owning instance we want the prefab system to focus on. + virtual PrefabFocusOperationResult FocusOnOwningPrefab(AZ::EntityId entityId) = 0; + + //! Set the focused prefab instance to the instance at position index of the current path. Supports undo/redo. + //! @param index The index of the instance in the current path that we want the prefab system to focus on. + virtual PrefabFocusOperationResult FocusOnPathIndex(AzFramework::EntityContextId entityContextId, int index) = 0; + + //! Returns the entity id of the container entity for the instance the prefab system is focusing on. + virtual AZ::EntityId GetFocusedPrefabContainerEntityId(AzFramework::EntityContextId entityContextId) const = 0; + + //! Returns whether the entity belongs to the instance that is being focused on, or one of its descendants. + //! @param entityId The entityId of the queried entity. + //! @return true if the entity belongs to the focused instance or one of its descendants, false otherwise. + virtual bool IsOwningPrefabBeingFocused(AZ::EntityId entityId) const = 0; + + //! Returns the path from the root instance to the currently focused instance. + //! @return A path composed from the names of the container entities for the instance path. + virtual const AZ::IO::Path& GetPrefabFocusPath(AzFramework::EntityContextId entityContextId) const = 0; + + //! Returns the size of the path to the currently focused instance. + virtual const int GetPrefabFocusPathLength(AzFramework::EntityContextId entityContextId) const = 0; + }; + +} // namespace AzToolsFramework::Prefab diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusUndo.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusUndo.cpp new file mode 100644 index 0000000000..e5f664ea38 --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusUndo.cpp @@ -0,0 +1,52 @@ +/* + * 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 + +namespace AzToolsFramework::Prefab +{ + PrefabFocusUndo::PrefabFocusUndo(const AZStd::string& undoOperationName) + : UndoSystem::URSequencePoint(undoOperationName) + { + m_prefabFocusInterface = AZ::Interface::Get(); + AZ_Assert(m_prefabFocusInterface, "PrefabFocusUndo - Failed to grab prefab focus interface"); + + m_prefabFocusPublicInterface = AZ::Interface::Get(); + AZ_Assert(m_prefabFocusPublicInterface, "PrefabFocusUndo - Failed to grab prefab focus public interface"); + } + + bool PrefabFocusUndo::Changed() const + { + return true; + } + + void PrefabFocusUndo::Capture(AZ::EntityId entityId) + { + auto entityContextId = AzFramework::EntityContextId::CreateNull(); + EditorEntityContextRequestBus::BroadcastResult(entityContextId, &EditorEntityContextRequests::GetEditorEntityContextId); + + m_beforeEntityId = m_prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(entityContextId); + m_afterEntityId = entityId; + } + + void PrefabFocusUndo::Undo() + { + m_prefabFocusInterface->FocusOnPrefabInstanceOwningEntityId(m_beforeEntityId); + } + + void PrefabFocusUndo::Redo() + { + m_prefabFocusInterface->FocusOnPrefabInstanceOwningEntityId(m_afterEntityId); + } + +} // namespace AzToolsFramework::Prefab diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusUndo.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusUndo.h new file mode 100644 index 0000000000..3b257b6547 --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusUndo.h @@ -0,0 +1,39 @@ +/* + * 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 + * + */ + +#pragma once + +#include +#include + +namespace AzToolsFramework::Prefab +{ + class PrefabFocusInterface; + class PrefabFocusPublicInterface; + + //! Undo node for prefab focus change operations. + class PrefabFocusUndo + : public UndoSystem::URSequencePoint + { + public: + explicit PrefabFocusUndo(const AZStd::string& undoOperationName); + + bool Changed() const override; + void Capture(AZ::EntityId entityId); + + void Undo() override; + void Redo() override; + + protected: + PrefabFocusInterface* m_prefabFocusInterface = nullptr; + PrefabFocusPublicInterface* m_prefabFocusPublicInterface = nullptr; + + AZ::EntityId m_beforeEntityId; + AZ::EntityId m_afterEntityId; + }; +} // namespace AzToolsFramework::Prefab diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerWidget.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerWidget.cpp index 459b39a290..3b48cc4967 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerWidget.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerWidget.cpp @@ -324,7 +324,8 @@ namespace AzToolsFramework // Currently, the first behavior is implemented. void EntityOutlinerWidget::OnSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected) { - if (m_selectionChangeInProgress || !m_enableSelectionUpdates) + if (m_selectionChangeInProgress || !m_enableSelectionUpdates + || (selected.empty() && deselected.empty())) { return; } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp index 24cb71499e..6ffa3aab69 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp @@ -24,11 +24,11 @@ #include #include #include -#include +#include +#include +#include #include #include -#include -#include #include #include #include @@ -39,7 +39,6 @@ #include #include - #include #include #include @@ -56,14 +55,13 @@ #include #include - namespace AzToolsFramework { namespace Prefab { ContainerEntityInterface* PrefabIntegrationManager::s_containerEntityInterface = nullptr; EditorEntityUiInterface* PrefabIntegrationManager::s_editorEntityUiInterface = nullptr; - PrefabFocusInterface* PrefabIntegrationManager::s_prefabFocusInterface = nullptr; + PrefabFocusPublicInterface* PrefabIntegrationManager::s_prefabFocusPublicInterface = nullptr; PrefabLoaderInterface* PrefabIntegrationManager::s_prefabLoaderInterface = nullptr; PrefabPublicInterface* PrefabIntegrationManager::s_prefabPublicInterface = nullptr; PrefabSystemComponentInterface* PrefabIntegrationManager::s_prefabSystemComponentInterface = nullptr; @@ -129,10 +127,10 @@ namespace AzToolsFramework return; } - s_prefabFocusInterface = AZ::Interface::Get(); - if (s_prefabFocusInterface == nullptr) + s_prefabFocusPublicInterface = AZ::Interface::Get(); + if (s_prefabFocusPublicInterface == nullptr) { - AZ_Assert(false, "Prefab - could not get PrefabFocusInterface on PrefabIntegrationManager construction."); + AZ_Assert(false, "Prefab - could not get PrefabFocusPublicInterface on PrefabIntegrationManager construction."); return; } @@ -247,12 +245,8 @@ namespace AzToolsFramework if (s_prefabPublicInterface->IsInstanceContainerEntity(selectedEntity)) { // Edit Prefab - if (prefabWipFeaturesEnabled) + if (prefabWipFeaturesEnabled && !s_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(selectedEntity)) { - bool beingEdited = s_prefabFocusInterface->IsOwningPrefabBeingFocused(selectedEntity); - - if (!beingEdited) - { QAction* editAction = menu->addAction(QObject::tr("Edit Prefab")); editAction->setToolTip(QObject::tr("Edit the prefab in focus mode.")); @@ -261,7 +255,6 @@ namespace AzToolsFramework }); itemWasShown = true; - } } // Save Prefab @@ -317,7 +310,7 @@ namespace AzToolsFramework void PrefabIntegrationManager::OnEscape() { - s_prefabFocusInterface->FocusOnOwningPrefab(AZ::EntityId()); + s_prefabFocusPublicInterface->FocusOnOwningPrefab(AZ::EntityId()); } void PrefabIntegrationManager::HandleSourceFileType(AZStd::string_view sourceFilePath, AZ::EntityId parentId, AZ::Vector3 position) const @@ -490,7 +483,7 @@ namespace AzToolsFramework void PrefabIntegrationManager::ContextMenu_EditPrefab(AZ::EntityId containerEntity) { - s_prefabFocusInterface->FocusOnOwningPrefab(containerEntity); + s_prefabFocusPublicInterface->FocusOnOwningPrefab(containerEntity); } void PrefabIntegrationManager::ContextMenu_SavePrefab(AZ::EntityId containerEntity) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.h index 6788af31e9..e8c10c150a 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.h @@ -30,7 +30,7 @@ namespace AzToolsFramework namespace Prefab { - class PrefabFocusInterface; + class PrefabFocusPublicInterface; class PrefabLoaderInterface; //! Structure for saving/retrieving user settings related to prefab workflows. @@ -144,7 +144,7 @@ namespace AzToolsFramework static ContainerEntityInterface* s_containerEntityInterface; static EditorEntityUiInterface* s_editorEntityUiInterface; - static PrefabFocusInterface* s_prefabFocusInterface; + static PrefabFocusPublicInterface* s_prefabFocusPublicInterface; static PrefabLoaderInterface* s_prefabLoaderInterface; static PrefabPublicInterface* s_prefabPublicInterface; static PrefabSystemComponentInterface* s_prefabSystemComponentInterface; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.cpp index 7d1f3485aa..00522b29dc 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.cpp @@ -10,7 +10,7 @@ #include -#include +#include #include #include @@ -35,10 +35,10 @@ namespace AzToolsFramework return; } - m_prefabFocusInterface = AZ::Interface::Get(); - if (m_prefabFocusInterface == nullptr) + m_prefabFocusPublicInterface = AZ::Interface::Get(); + if (m_prefabFocusPublicInterface == nullptr) { - AZ_Assert(false, "PrefabUiHandler - could not get PrefabFocusInterface on PrefabUiHandler construction."); + AZ_Assert(false, "PrefabUiHandler - could not get PrefabFocusPublicInterface on PrefabUiHandler construction."); return; } } @@ -83,7 +83,7 @@ namespace AzToolsFramework QIcon PrefabUiHandler::GenerateItemIcon(AZ::EntityId entityId) const { - if (m_prefabFocusInterface->IsOwningPrefabBeingFocused(entityId)) + if (m_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(entityId)) { return QIcon(m_prefabEditIconPath); } @@ -105,7 +105,7 @@ namespace AzToolsFramework const bool hasVisibleChildren = index.data(EntityOutlinerListModel::ExpandedRole).value() && index.model()->hasChildren(index); QColor backgroundColor = m_prefabCapsuleColor; - if (m_prefabFocusInterface->IsOwningPrefabBeingFocused(entityId)) + if (m_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(entityId)) { backgroundColor = m_prefabCapsuleEditColor; } @@ -191,7 +191,7 @@ namespace AzToolsFramework const bool isLastColumn = descendantIndex.column() == EntityOutlinerListModel::ColumnLockToggle; QColor borderColor = m_prefabCapsuleColor; - if (m_prefabFocusInterface->IsOwningPrefabBeingFocused(entityId)) + if (m_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(entityId)) { borderColor = m_prefabCapsuleEditColor; } @@ -329,7 +329,7 @@ namespace AzToolsFramework if (prefabWipFeaturesEnabled) { // Focus on this prefab - m_prefabFocusInterface->FocusOnOwningPrefab(entityId); + m_prefabFocusPublicInterface->FocusOnOwningPrefab(entityId); } } } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.h index bb7168f409..7c68d9fd95 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.h @@ -15,7 +15,7 @@ namespace AzToolsFramework namespace Prefab { - class PrefabFocusInterface; + class PrefabFocusPublicInterface; class PrefabPublicInterface; }; @@ -39,7 +39,7 @@ namespace AzToolsFramework void OnDoubleClick(AZ::EntityId entityId) const override; private: - Prefab::PrefabFocusInterface* m_prefabFocusInterface = nullptr; + Prefab::PrefabFocusPublicInterface* m_prefabFocusPublicInterface = nullptr; Prefab::PrefabPublicInterface* m_prefabPublicInterface = nullptr; static bool IsLastVisibleChild(const QModelIndex& parent, const QModelIndex& child); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.cpp index 6b0de5dc53..21ada94184 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.cpp @@ -8,7 +8,7 @@ #include -#include +#include namespace AzToolsFramework::Prefab { @@ -31,8 +31,8 @@ namespace AzToolsFramework::Prefab void PrefabViewportFocusPathHandler::Initialize(AzQtComponents::BreadCrumbs* breadcrumbsWidget, QToolButton* backButton) { // Get reference to the PrefabFocusInterface handler - m_prefabFocusInterface = AZ::Interface::Get(); - if (m_prefabFocusInterface == nullptr) + m_prefabFocusPublicInterface = AZ::Interface::Get(); + if (m_prefabFocusPublicInterface == nullptr) { AZ_Assert(false, "Prefab - could not get PrefabFocusInterface on PrefabViewportFocusPathHandler construction."); return; @@ -46,7 +46,7 @@ namespace AzToolsFramework::Prefab connect(m_breadcrumbsWidget, &AzQtComponents::BreadCrumbs::linkClicked, this, [&](const QString&, int linkIndex) { - m_prefabFocusInterface->FocusOnPathIndex(m_editorEntityContextId, linkIndex); + m_prefabFocusPublicInterface->FocusOnPathIndex(m_editorEntityContextId, linkIndex); } ); @@ -54,9 +54,9 @@ namespace AzToolsFramework::Prefab connect(m_backButton, &QToolButton::clicked, this, [&]() { - if (int length = m_prefabFocusInterface->GetPrefabFocusPathLength(m_editorEntityContextId); length > 1) + if (int length = m_prefabFocusPublicInterface->GetPrefabFocusPathLength(m_editorEntityContextId); length > 1) { - m_prefabFocusInterface->FocusOnPathIndex(m_editorEntityContextId, length - 2); + m_prefabFocusPublicInterface->FocusOnPathIndex(m_editorEntityContextId, length - 2); } } ); @@ -65,7 +65,7 @@ namespace AzToolsFramework::Prefab void PrefabViewportFocusPathHandler::OnPrefabFocusChanged() { // Push new Path - m_breadcrumbsWidget->pushPath(m_prefabFocusInterface->GetPrefabFocusPath(m_editorEntityContextId).c_str()); + m_breadcrumbsWidget->pushPath(m_prefabFocusPublicInterface->GetPrefabFocusPath(m_editorEntityContextId).c_str()); } } // namespace AzToolsFramework::Prefab diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.h index ce7744fb1b..a97db60e34 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.h @@ -19,7 +19,7 @@ namespace AzToolsFramework::Prefab { - class PrefabFocusInterface; + class PrefabFocusPublicInterface; class PrefabViewportFocusPathHandler : public PrefabFocusNotificationBus::Handler @@ -40,6 +40,6 @@ namespace AzToolsFramework::Prefab AzFramework::EntityContextId m_editorEntityContextId = AzFramework::EntityContextId::CreateNull(); - PrefabFocusInterface* m_prefabFocusInterface = nullptr; + PrefabFocusPublicInterface* m_prefabFocusPublicInterface = nullptr; }; } // namespace AzToolsFramework::Prefab diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake b/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake index 37559564db..8dec7ab611 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake @@ -646,6 +646,9 @@ set(FILES Prefab/PrefabFocusHandler.cpp Prefab/PrefabFocusInterface.h Prefab/PrefabFocusNotificationBus.h + Prefab/PrefabFocusPublicInterface.h + Prefab/PrefabFocusUndo.h + Prefab/PrefabFocusUndo.cpp Prefab/PrefabIdTypes.h Prefab/PrefabLoader.h Prefab/PrefabLoader.cpp diff --git a/Code/Framework/AzToolsFramework/Tests/Prefab/PrefabFocus/PrefabFocusTests.cpp b/Code/Framework/AzToolsFramework/Tests/Prefab/PrefabFocus/PrefabFocusTests.cpp index 72489b07a1..86c73e72e5 100644 --- a/Code/Framework/AzToolsFramework/Tests/Prefab/PrefabFocus/PrefabFocusTests.cpp +++ b/Code/Framework/AzToolsFramework/Tests/Prefab/PrefabFocus/PrefabFocusTests.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include namespace UnitTest @@ -72,6 +73,9 @@ namespace UnitTest m_prefabFocusInterface = AZ::Interface::Get(); ASSERT_TRUE(m_prefabFocusInterface != nullptr); + m_prefabFocusPublicInterface = AZ::Interface::Get(); + ASSERT_TRUE(m_prefabFocusPublicInterface != nullptr); + AzToolsFramework::EditorEntityContextRequestBus::BroadcastResult( m_editorEntityContextId, &AzToolsFramework::EditorEntityContextRequestBus::Events::GetEditorEntityContextId); @@ -91,6 +95,7 @@ namespace UnitTest AZStd::unique_ptr m_rootInstance; PrefabFocusInterface* m_prefabFocusInterface = nullptr; + PrefabFocusPublicInterface* m_prefabFocusPublicInterface = nullptr; AzFramework::EntityContextId m_editorEntityContextId = AzFramework::EntityContextId::CreateNull(); inline static const char* CityEntityName = "City"; @@ -105,7 +110,7 @@ namespace UnitTest { // Verify FocusOnOwningPrefab works when passing the container entity of the root prefab. { - m_prefabFocusInterface->FocusOnOwningPrefab(m_instanceMap[CityEntityName]->GetContainerEntityId()); + m_prefabFocusPublicInterface->FocusOnOwningPrefab(m_instanceMap[CityEntityName]->GetContainerEntityId()); EXPECT_EQ( m_prefabFocusInterface->GetFocusedPrefabTemplateId(m_editorEntityContextId), m_instanceMap[CityEntityName]->GetTemplateId()); @@ -120,7 +125,7 @@ namespace UnitTest { // Verify FocusOnOwningPrefab works when passing a nested entity of the root prefab. { - m_prefabFocusInterface->FocusOnOwningPrefab(m_entityMap[CityEntityName]->GetId()); + m_prefabFocusPublicInterface->FocusOnOwningPrefab(m_entityMap[CityEntityName]->GetId()); EXPECT_EQ( m_prefabFocusInterface->GetFocusedPrefabTemplateId(m_editorEntityContextId), m_instanceMap[CityEntityName]->GetTemplateId()); @@ -135,7 +140,7 @@ namespace UnitTest { // Verify FocusOnOwningPrefab works when passing the container entity of a nested prefab. { - m_prefabFocusInterface->FocusOnOwningPrefab(m_instanceMap[CarEntityName]->GetContainerEntityId()); + m_prefabFocusPublicInterface->FocusOnOwningPrefab(m_instanceMap[CarEntityName]->GetContainerEntityId()); EXPECT_EQ( m_prefabFocusInterface->GetFocusedPrefabTemplateId(m_editorEntityContextId), m_instanceMap[CarEntityName]->GetTemplateId()); @@ -149,7 +154,7 @@ namespace UnitTest { // Verify FocusOnOwningPrefab works when passing a nested entity of the a nested prefab. { - m_prefabFocusInterface->FocusOnOwningPrefab(m_entityMap[Passenger1EntityName]->GetId()); + m_prefabFocusPublicInterface->FocusOnOwningPrefab(m_entityMap[Passenger1EntityName]->GetId()); EXPECT_EQ( m_prefabFocusInterface->GetFocusedPrefabTemplateId(m_editorEntityContextId), m_instanceMap[CarEntityName]->GetTemplateId()); @@ -169,7 +174,7 @@ namespace UnitTest prefabEditorEntityOwnershipInterface->GetRootPrefabInstance(); EXPECT_TRUE(rootPrefabInstance.has_value()); - m_prefabFocusInterface->FocusOnOwningPrefab(AZ::EntityId()); + m_prefabFocusPublicInterface->FocusOnOwningPrefab(AZ::EntityId()); EXPECT_EQ( m_prefabFocusInterface->GetFocusedPrefabTemplateId(m_editorEntityContextId), rootPrefabInstance->get().GetTemplateId()); @@ -183,10 +188,10 @@ namespace UnitTest { // Verify IsOwningPrefabBeingFocused returns true for all entities in a focused prefab (container/nested) { - m_prefabFocusInterface->FocusOnOwningPrefab(m_instanceMap[CityEntityName]->GetContainerEntityId()); + m_prefabFocusPublicInterface->FocusOnOwningPrefab(m_instanceMap[CityEntityName]->GetContainerEntityId()); - EXPECT_TRUE(m_prefabFocusInterface->IsOwningPrefabBeingFocused(m_instanceMap[CityEntityName]->GetContainerEntityId())); - EXPECT_TRUE(m_prefabFocusInterface->IsOwningPrefabBeingFocused(m_entityMap[CityEntityName]->GetId())); + EXPECT_TRUE(m_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(m_instanceMap[CityEntityName]->GetContainerEntityId())); + EXPECT_TRUE(m_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(m_entityMap[CityEntityName]->GetId())); } } @@ -194,13 +199,13 @@ namespace UnitTest { // Verify IsOwningPrefabBeingFocused returns false for all entities not in a focused prefab (ancestors/descendants) { - m_prefabFocusInterface->FocusOnOwningPrefab(m_instanceMap[StreetEntityName]->GetContainerEntityId()); + m_prefabFocusPublicInterface->FocusOnOwningPrefab(m_instanceMap[StreetEntityName]->GetContainerEntityId()); - EXPECT_TRUE(m_prefabFocusInterface->IsOwningPrefabBeingFocused(m_instanceMap[StreetEntityName]->GetContainerEntityId())); - EXPECT_FALSE(m_prefabFocusInterface->IsOwningPrefabBeingFocused(m_instanceMap[CityEntityName]->GetContainerEntityId())); - EXPECT_FALSE(m_prefabFocusInterface->IsOwningPrefabBeingFocused(m_entityMap[CityEntityName]->GetId())); - EXPECT_FALSE(m_prefabFocusInterface->IsOwningPrefabBeingFocused(m_instanceMap[CarEntityName]->GetContainerEntityId())); - EXPECT_FALSE(m_prefabFocusInterface->IsOwningPrefabBeingFocused(m_entityMap[Passenger1EntityName]->GetId())); + EXPECT_TRUE(m_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(m_instanceMap[StreetEntityName]->GetContainerEntityId())); + EXPECT_FALSE(m_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(m_instanceMap[CityEntityName]->GetContainerEntityId())); + EXPECT_FALSE(m_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(m_entityMap[CityEntityName]->GetId())); + EXPECT_FALSE(m_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(m_instanceMap[CarEntityName]->GetContainerEntityId())); + EXPECT_FALSE(m_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(m_entityMap[Passenger1EntityName]->GetId())); } } @@ -208,12 +213,12 @@ namespace UnitTest { // Verify IsOwningPrefabBeingFocused returns false for all entities not in a focused prefab (siblings) { - m_prefabFocusInterface->FocusOnOwningPrefab(m_instanceMap[SportsCarEntityName]->GetContainerEntityId()); + m_prefabFocusPublicInterface->FocusOnOwningPrefab(m_instanceMap[SportsCarEntityName]->GetContainerEntityId()); - EXPECT_TRUE(m_prefabFocusInterface->IsOwningPrefabBeingFocused(m_instanceMap[SportsCarEntityName]->GetContainerEntityId())); - EXPECT_TRUE(m_prefabFocusInterface->IsOwningPrefabBeingFocused(m_entityMap[Passenger2EntityName]->GetId())); - EXPECT_FALSE(m_prefabFocusInterface->IsOwningPrefabBeingFocused(m_instanceMap[CarEntityName]->GetContainerEntityId())); - EXPECT_FALSE(m_prefabFocusInterface->IsOwningPrefabBeingFocused(m_entityMap[Passenger1EntityName]->GetId())); + EXPECT_TRUE(m_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(m_instanceMap[SportsCarEntityName]->GetContainerEntityId())); + EXPECT_TRUE(m_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(m_entityMap[Passenger2EntityName]->GetId())); + EXPECT_FALSE(m_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(m_instanceMap[CarEntityName]->GetContainerEntityId())); + EXPECT_FALSE(m_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(m_entityMap[Passenger1EntityName]->GetId())); } } From 10ab1a369fed6767a371b6afa89371ba6cb9ef01 Mon Sep 17 00:00:00 2001 From: AMZN-nggieber <52797929+AMZN-nggieber@users.noreply.github.com> Date: Wed, 13 Oct 2021 12:52:44 -0700 Subject: [PATCH 39/65] Adds Download status info UI to Gem Catalog (#4602) * Adds Download status info UI to Gem Catalog Signed-off-by: nggieber * Removed test code Signed-off-by: nggieber * Remove unused variable Signed-off-by: nggieber * Addressed PR feedback Signed-off-by: nggieber * Fixed Open3DEngine spelling Signed-off-by: nggieber --- .../ProjectManager/Resources/Download.svg | 3 + .../Resources/ProjectManager.qrc | 4 +- .../Resources/{FeatureTagClose.svg => X.svg} | 0 .../ProjectManager/Resources/in_progress.gif | 3 + .../Source/GemCatalog/GemFilterTagWidget.cpp | 2 +- .../Source/GemCatalog/GemInfo.cpp | 48 ++++++++---- .../Source/GemCatalog/GemInfo.h | 15 +++- .../Source/GemCatalog/GemItemDelegate.cpp | 77 ++++++++++++++++++- .../Source/GemCatalog/GemItemDelegate.h | 23 ++++-- .../Source/GemCatalog/GemListHeaderWidget.cpp | 4 +- .../Source/GemCatalog/GemListView.cpp | 15 +++- .../Source/GemCatalog/GemModel.cpp | 11 +++ .../Source/GemCatalog/GemModel.h | 5 +- .../ProjectManager/Source/PythonBindings.cpp | 16 +++- 14 files changed, 194 insertions(+), 32 deletions(-) create mode 100644 Code/Tools/ProjectManager/Resources/Download.svg rename Code/Tools/ProjectManager/Resources/{FeatureTagClose.svg => X.svg} (100%) create mode 100644 Code/Tools/ProjectManager/Resources/in_progress.gif diff --git a/Code/Tools/ProjectManager/Resources/Download.svg b/Code/Tools/ProjectManager/Resources/Download.svg new file mode 100644 index 0000000000..c2b0c2ce3c --- /dev/null +++ b/Code/Tools/ProjectManager/Resources/Download.svg @@ -0,0 +1,3 @@ + + + diff --git a/Code/Tools/ProjectManager/Resources/ProjectManager.qrc b/Code/Tools/ProjectManager/Resources/ProjectManager.qrc index 30bcc1ace5..aeaf9a9248 100644 --- a/Code/Tools/ProjectManager/Resources/ProjectManager.qrc +++ b/Code/Tools/ProjectManager/Resources/ProjectManager.qrc @@ -34,9 +34,11 @@ Warning.svg Backgrounds/DefaultBackground.jpg Backgrounds/FtueBackground.jpg - FeatureTagClose.svg + X.svg Refresh.svg Edit.svg Delete.svg + Download.svg + in_progress.gif diff --git a/Code/Tools/ProjectManager/Resources/FeatureTagClose.svg b/Code/Tools/ProjectManager/Resources/X.svg similarity index 100% rename from Code/Tools/ProjectManager/Resources/FeatureTagClose.svg rename to Code/Tools/ProjectManager/Resources/X.svg diff --git a/Code/Tools/ProjectManager/Resources/in_progress.gif b/Code/Tools/ProjectManager/Resources/in_progress.gif new file mode 100644 index 0000000000..eb392a9b89 --- /dev/null +++ b/Code/Tools/ProjectManager/Resources/in_progress.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:64985a78205da45f4bb92b040c348d96fe7cd7277549c1f79c430469a0d3bab7 +size 166393 diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterTagWidget.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterTagWidget.cpp index 138880f44e..69a883d169 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterTagWidget.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterTagWidget.cpp @@ -33,7 +33,7 @@ namespace O3DE::ProjectManager m_closeButton = new QPushButton(); m_closeButton->setFlat(true); - m_closeButton->setIcon(QIcon(":/FeatureTagClose.svg")); + m_closeButton->setIcon(QIcon(":/X.svg")); m_closeButton->setIconSize(QSize(12, 12)); m_closeButton->setStyleSheet("QPushButton { background-color: transparent; border: 0px }"); layout->addWidget(m_closeButton); diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemInfo.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemInfo.cpp index 771d644617..cbdcf64162 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemInfo.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemInfo.cpp @@ -8,6 +8,8 @@ #include "GemInfo.h" +#include + namespace O3DE::ProjectManager { GemInfo::GemInfo(const QString& name, const QString& creator, const QString& summary, Platforms platforms, bool isAdded) @@ -29,17 +31,17 @@ namespace O3DE::ProjectManager switch (platform) { case Android: - return "Android"; + return QObject::tr("Android"); case iOS: - return "iOS"; + return QObject::tr("iOS"); case Linux: - return "Linux"; + return QObject::tr("Linux"); case macOS: - return "macOS"; + return QObject::tr("macOS"); case Windows: - return "Windows"; + return QObject::tr("Windows"); default: - return ""; + return QObject::tr(""); } } @@ -48,13 +50,13 @@ namespace O3DE::ProjectManager switch (type) { case Asset: - return "Asset"; + return QObject::tr("Asset"); case Code: - return "Code"; + return QObject::tr("Code"); case Tool: - return "Tool"; + return QObject::tr("Tool"); default: - return ""; + return QObject::tr(""); } } @@ -62,15 +64,33 @@ namespace O3DE::ProjectManager { switch (origin) { - case Open3DEEngine: - return "Open 3D Engine"; + case Open3DEngine: + return QObject::tr("Open 3D Engine"); case Local: - return "Local"; + return QObject::tr("Local"); + case Remote: + return QObject::tr("Remote"); default: - return ""; + return QObject::tr(""); } } + QString GemInfo::GetDownloadStatusString(DownloadStatus status) + { + switch (status) + { + case NotDownloaded: + return QObject::tr("Not Downloaded"); + case Downloading: + return QObject::tr("Downloading"); + case Downloaded: + return QObject::tr("Downloaded"); + case UnknownDownloadStatus: + default: + return QObject::tr(""); + } + }; + bool GemInfo::IsPlatformSupported(Platform platform) const { return (m_platforms & platform); diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemInfo.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemInfo.h index 311eeb93f6..8c6d40505a 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemInfo.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemInfo.h @@ -44,13 +44,23 @@ namespace O3DE::ProjectManager enum GemOrigin { - Open3DEEngine = 1 << 0, + Open3DEngine = 1 << 0, Local = 1 << 1, - NumGemOrigins = 2 + Remote = 1 << 2, + NumGemOrigins = 3 }; Q_DECLARE_FLAGS(GemOrigins, GemOrigin) static QString GetGemOriginString(GemOrigin origin); + enum DownloadStatus + { + UnknownDownloadStatus = -1, + NotDownloaded, + Downloading, + Downloaded, + }; + static QString GetDownloadStatusString(DownloadStatus status); + GemInfo() = default; GemInfo(const QString& name, const QString& creator, const QString& summary, Platforms platforms, bool isAdded); bool IsPlatformSupported(Platform platform) const; @@ -68,6 +78,7 @@ namespace O3DE::ProjectManager QString m_summary = "No summary provided."; Platforms m_platforms; Types m_types; //! Asset and/or Code and/or Tool + DownloadStatus m_downloadStatus = UnknownDownloadStatus; QStringList m_features; QString m_requirement; QString m_directoryLink; diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.cpp index 2c7f17db32..e15c4b3b39 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.cpp @@ -10,6 +10,7 @@ #include #include #include + #include #include #include @@ -20,6 +21,7 @@ #include #include #include +#include namespace O3DE::ProjectManager { @@ -32,6 +34,11 @@ namespace O3DE::ProjectManager AddPlatformIcon(GemInfo::Linux, ":/Linux.svg"); AddPlatformIcon(GemInfo::macOS, ":/macOS.svg"); AddPlatformIcon(GemInfo::Windows, ":/Windows.svg"); + + SetStatusIcon(m_notDownloadedPixmap, ":/Download.svg"); + SetStatusIcon(m_unknownStatusPixmap, ":/X.svg"); + + m_downloadingMovie = new QMovie(":/in_progress.gif"); } void GemItemDelegate::AddPlatformIcon(GemInfo::Platform platform, const QString& iconPath) @@ -41,6 +48,25 @@ namespace O3DE::ProjectManager m_platformIcons.insert(platform, QIcon(iconPath).pixmap(static_cast(static_cast(s_platformIconSize) * aspectRatio), s_platformIconSize)); } + void GemItemDelegate::SetStatusIcon(QPixmap& m_iconPixmap, const QString& iconPath) + { + QPixmap pixmap(iconPath); + float aspectRatio = static_cast(pixmap.width()) / pixmap.height(); + int xScaler = s_statusIconSize; + int yScaler = s_statusIconSize; + + if (aspectRatio > 1.0f) + { + yScaler = static_cast(1.0f / aspectRatio * s_statusIconSize); + } + else if (aspectRatio < 1.0f) + { + xScaler = static_cast(aspectRatio * s_statusIconSize); + } + + m_iconPixmap = QPixmap(QIcon(iconPath).pixmap(xScaler, yScaler)); + } + void GemItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& modelIndex) const { if (!modelIndex.isValid()) @@ -56,6 +82,8 @@ namespace O3DE::ProjectManager QRect fullRect, itemRect, contentRect; CalcRects(options, fullRect, itemRect, contentRect); + QRect buttonRect = CalcButtonRect(contentRect); + QFont standardFont(options.font); standardFont.setPixelSize(static_cast(s_fontSize)); QFontMetrics standardFontMetrics(standardFont); @@ -114,7 +142,8 @@ namespace O3DE::ProjectManager const QRect summaryRect = CalcSummaryRect(contentRect, hasTags); DrawText(summary, painter, summaryRect, standardFont); - DrawButton(painter, contentRect, modelIndex); + DrawDownloadStatusIcon(painter, contentRect, buttonRect, modelIndex); + DrawButton(painter, buttonRect, modelIndex); DrawPlatformIcons(painter, contentRect, modelIndex); DrawFeatureTags(painter, contentRect, featureTags, standardFont, summaryRect); @@ -270,7 +299,7 @@ namespace O3DE::ProjectManager QRect GemItemDelegate::CalcButtonRect(const QRect& contentRect) const { - const QPoint topLeft = QPoint(contentRect.right() - s_buttonWidth - s_itemMargins.right(), contentRect.top() + contentRect.height() / 2 - s_buttonHeight / 2); + const QPoint topLeft = QPoint(contentRect.right() - s_buttonWidth, contentRect.center().y() - s_buttonHeight / 2); const QSize size = QSize(s_buttonWidth, s_buttonHeight); return QRect(topLeft, size); } @@ -378,10 +407,9 @@ namespace O3DE::ProjectManager painter->restore(); } - void GemItemDelegate::DrawButton(QPainter* painter, const QRect& contentRect, const QModelIndex& modelIndex) const + void GemItemDelegate::DrawButton(QPainter* painter, const QRect& buttonRect, const QModelIndex& modelIndex) const { painter->save(); - const QRect buttonRect = CalcButtonRect(contentRect); QPoint circleCenter; if (GemModel::IsAdded(modelIndex)) @@ -427,4 +455,45 @@ namespace O3DE::ProjectManager return QString(); } + + void GemItemDelegate::DrawDownloadStatusIcon(QPainter* painter, const QRect& contentRect, const QRect& buttonRect, const QModelIndex& modelIndex) const + { + const GemInfo::DownloadStatus downloadStatus = GemModel::GetDownloadStatus(modelIndex); + + // Show no icon if gem is already downloaded + if (downloadStatus == GemInfo::DownloadStatus::Downloaded) + { + return; + } + + QPixmap currentFrame; + const QPixmap* statusPixmap; + if (downloadStatus == GemInfo::DownloadStatus::Downloading) + { + if (m_downloadingMovie->state() != QMovie::Running) + { + m_downloadingMovie->start(); + emit MovieStartedPlaying(m_downloadingMovie); + } + + currentFrame = m_downloadingMovie->currentPixmap(); + currentFrame = currentFrame.scaled(s_statusIconSize, s_statusIconSize); + statusPixmap = ¤tFrame; + } + else if (downloadStatus == GemInfo::DownloadStatus::NotDownloaded) + { + statusPixmap = &m_notDownloadedPixmap; + } + else + { + statusPixmap = &m_unknownStatusPixmap; + } + + QSize statusSize = statusPixmap->size(); + + painter->drawPixmap( + buttonRect.left() - s_statusButtonSpacing - statusSize.width(), + contentRect.center().y() - statusSize.height() / 2, + *statusPixmap); + } } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.h index 52b5a4f58e..c013be0d9e 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.h @@ -49,13 +49,13 @@ namespace O3DE::ProjectManager // Margin and borders inline constexpr static QMargins s_itemMargins = QMargins(/*left=*/16, /*top=*/8, /*right=*/16, /*bottom=*/8); // Item border distances - inline constexpr static QMargins s_contentMargins = QMargins(/*left=*/20, /*top=*/12, /*right=*/15, /*bottom=*/12); // Distances of the elements within an item to the item borders + inline constexpr static QMargins s_contentMargins = QMargins(/*left=*/20, /*top=*/12, /*right=*/20, /*bottom=*/12); // Distances of the elements within an item to the item borders inline constexpr static int s_borderWidth = 4; // Button - inline constexpr static int s_buttonWidth = 55; - inline constexpr static int s_buttonHeight = 18; - inline constexpr static int s_buttonBorderRadius = 9; + inline constexpr static int s_buttonWidth = 32; + inline constexpr static int s_buttonHeight = 16; + inline constexpr static int s_buttonBorderRadius = s_buttonHeight / 2; inline constexpr static int s_buttonCircleRadius = s_buttonBorderRadius - 2; inline constexpr static qreal s_buttonFontSize = 10.0; @@ -65,6 +65,9 @@ namespace O3DE::ProjectManager inline constexpr static int s_featureTagBorderMarginY = 3; inline constexpr static int s_featureTagSpacing = 7; + signals: + void MovieStartedPlaying(const QMovie* playingMovie) const; + protected: bool editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& modelIndex) override; bool helpEvent(QHelpEvent* event, QAbstractItemView* view, const QStyleOptionViewItem& option, const QModelIndex& index) override; @@ -74,9 +77,10 @@ namespace O3DE::ProjectManager QRect CalcButtonRect(const QRect& contentRect) const; QRect CalcSummaryRect(const QRect& contentRect, bool hasTags) const; void DrawPlatformIcons(QPainter* painter, const QRect& contentRect, const QModelIndex& modelIndex) const; - void DrawButton(QPainter* painter, const QRect& contentRect, const QModelIndex& modelIndex) const; + void DrawButton(QPainter* painter, const QRect& buttonRect, const QModelIndex& modelIndex) const; void DrawFeatureTags(QPainter* painter, const QRect& contentRect, const QStringList& featureTags, const QFont& standardFont, const QRect& summaryRect) const; void DrawText(const QString& text, QPainter* painter, const QRect& rect, const QFont& standardFont) const; + void DrawDownloadStatusIcon(QPainter* painter, const QRect& contentRect, const QRect& buttonRect, const QModelIndex& modelIndex) const; QAbstractItemModel* m_model = nullptr; @@ -85,5 +89,14 @@ namespace O3DE::ProjectManager void AddPlatformIcon(GemInfo::Platform platform, const QString& iconPath); inline constexpr static int s_platformIconSize = 12; QHash m_platformIcons; + + // Status icons + void SetStatusIcon(QPixmap& m_iconPixmap, const QString& iconPath); + inline constexpr static int s_statusIconSize = 16; + inline constexpr static int s_statusButtonSpacing = 5; + + QPixmap m_unknownStatusPixmap; + QPixmap m_notDownloadedPixmap; + QMovie* m_downloadingMovie = nullptr; }; } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemListHeaderWidget.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemListHeaderWidget.cpp index ab51c7511c..10ff31f33b 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemListHeaderWidget.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemListHeaderWidget.cpp @@ -103,11 +103,11 @@ namespace O3DE::ProjectManager QSpacerItem* horizontalSpacer = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum); columnHeaderLayout->addSpacerItem(horizontalSpacer); - QLabel* gemSelectedLabel = new QLabel(tr("Selected")); + QLabel* gemSelectedLabel = new QLabel(tr("Status")); gemSelectedLabel->setObjectName("GemCatalogHeaderLabel"); columnHeaderLayout->addWidget(gemSelectedLabel); - columnHeaderLayout->addSpacing(65); + columnHeaderLayout->addSpacing(72); vLayout->addLayout(columnHeaderLayout); } diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemListView.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemListView.cpp index f5b54a364b..cfdf7fa5b3 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemListView.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemListView.cpp @@ -9,6 +9,8 @@ #include #include +#include + namespace O3DE::ProjectManager { GemListView::GemListView(QAbstractItemModel* model, QItemSelectionModel* selectionModel, QWidget* parent) @@ -19,6 +21,17 @@ namespace O3DE::ProjectManager setModel(model); setSelectionModel(selectionModel); - setItemDelegate(new GemItemDelegate(model, this)); + GemItemDelegate* itemDelegate = new GemItemDelegate(model, this); + + connect(itemDelegate, &GemItemDelegate::MovieStartedPlaying, [=](const QMovie* playingMovie) + { + // Force redraw when movie is playing so animation is smooth + connect(playingMovie, &QMovie::frameChanged, this, [=] + { + this->viewport()->repaint(); + }); + }); + + setItemDelegate(itemDelegate); } } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp index c8911de360..35491f4ddd 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp @@ -48,6 +48,7 @@ namespace O3DE::ProjectManager item->setData(gemInfo.m_features, RoleFeatures); item->setData(gemInfo.m_path, RolePath); item->setData(gemInfo.m_requirement, RoleRequirement); + item->setData(gemInfo.m_downloadStatus, RoleDownloadStatus); appendRow(item); @@ -132,6 +133,11 @@ namespace O3DE::ProjectManager return static_cast(modelIndex.data(RoleTypes).toInt()); } + GemInfo::DownloadStatus GemModel::GetDownloadStatus(const QModelIndex& modelIndex) + { + return static_cast(modelIndex.data(RoleDownloadStatus).toInt()); + } + QString GemModel::GetSummary(const QModelIndex& modelIndex) { return modelIndex.data(RoleSummary).toString(); @@ -373,6 +379,11 @@ namespace O3DE::ProjectManager return previouslyAdded && !added; } + void GemModel::SetDownloadStatus(QAbstractItemModel& model, const QModelIndex& modelIndex, GemInfo::DownloadStatus status) + { + model.setData(modelIndex, status, RoleDownloadStatus); + } + bool GemModel::HasRequirement(const QModelIndex& modelIndex) { return !modelIndex.data(RoleRequirement).toString().isEmpty(); diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h index ef2d1a903d..0d1c225f74 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h @@ -40,6 +40,7 @@ namespace O3DE::ProjectManager static GemInfo::GemOrigin GetGemOrigin(const QModelIndex& modelIndex); static GemInfo::Platforms GetPlatforms(const QModelIndex& modelIndex); static GemInfo::Types GetTypes(const QModelIndex& modelIndex); + static GemInfo::DownloadStatus GetDownloadStatus(const QModelIndex& modelIndex); static QString GetSummary(const QModelIndex& modelIndex); static QString GetDirectoryLink(const QModelIndex& modelIndex); static QString GetDocLink(const QModelIndex& modelIndex); @@ -64,6 +65,7 @@ namespace O3DE::ProjectManager static bool NeedsToBeRemoved(const QModelIndex& modelIndex, bool includeDependencies = false); static bool HasRequirement(const QModelIndex& modelIndex); static void UpdateDependencies(QAbstractItemModel& model, const QModelIndex& modelIndex); + static void SetDownloadStatus(QAbstractItemModel& model, const QModelIndex& modelIndex, GemInfo::DownloadStatus status); bool DoGemsToBeAddedHaveRequirements() const; bool HasDependentGemsToRemove() const; @@ -101,7 +103,8 @@ namespace O3DE::ProjectManager RoleFeatures, RoleTypes, RolePath, - RoleRequirement + RoleRequirement, + RoleDownloadStatus }; QHash m_nameToIndexMap; diff --git a/Code/Tools/ProjectManager/Source/PythonBindings.cpp b/Code/Tools/ProjectManager/Source/PythonBindings.cpp index 83f93630ac..d91f08c73e 100644 --- a/Code/Tools/ProjectManager/Source/PythonBindings.cpp +++ b/Code/Tools/ProjectManager/Source/PythonBindings.cpp @@ -668,7 +668,21 @@ namespace O3DE::ProjectManager if (gemInfo.m_creator.contains("Open 3D Engine")) { - gemInfo.m_gemOrigin = GemInfo::GemOrigin::Open3DEEngine; + gemInfo.m_gemOrigin = GemInfo::GemOrigin::Open3DEngine; + } + else if (gemInfo.m_creator.contains("Amazon Web Services")) + { + gemInfo.m_gemOrigin = GemInfo::GemOrigin::Local; + } + else if (data.contains("origin")) + { + gemInfo.m_gemOrigin = GemInfo::GemOrigin::Remote; + } + + // As long Base Open3DEngine gems are installed before first startup non-remote gems will be downloaded + if (gemInfo.m_gemOrigin != GemInfo::GemOrigin::Remote) + { + gemInfo.m_downloadStatus = GemInfo::DownloadStatus::Downloaded; } if (data.contains("user_tags")) From 718fc97bb66d07a0f04c41c100783c5c8a6adbec Mon Sep 17 00:00:00 2001 From: Scott Murray Date: Wed, 13 Oct 2021 12:57:31 -0700 Subject: [PATCH 40/65] changes from review feedback Signed-off-by: Scott Murray --- ...mponents_PostFxShapeWeightModifierAdded.py | 23 ++++++++----------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFxShapeWeightModifierAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFxShapeWeightModifierAdded.py index fe6362c9b7..0ba7038c47 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFxShapeWeightModifierAdded.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFxShapeWeightModifierAdded.py @@ -24,12 +24,9 @@ class Tests: postfx_layer_component = ( "Entity has a PostFX Layer component", "Entity did not have an PostFX Layer component") - shape_component = ( + tube_shape_component = ( "Entity has a Tube Shape component", "Entity did not have a Tube Shape component") - shape_undo = ( - "Entity shape component add undone", - "Entity shape component undo failed to remove shape") postfx_shape_weight_enabled = ( "PostFx Shape Weight Modifier component enabled", "PostFx Shape Weight Modifier component was not enabled.") @@ -78,8 +75,8 @@ def AtomEditorComponents_postfx_shape_weight_AddedToEntity(): 6) Add PostFX Layer component since it is required by the PostFx Shape Weight Modifier component. 7) Verify PostFx Shape Weight Modifier component is NOT enabled since it also requires a shape. 8) Add a required shape looping over a list and checking if it enables PostFX Shape Weight Modifier. - 9) Undo to remove each added shape and verify PostFX Shape Weight Modifier is not enabled - 10) Verify PostFx Shape Weight Modifier component is enabled by adding Spline and Tube Shape componen. + 9) Undo to remove each added shape and verify PostFX Shape Weight Modifier is not enabled. + 10) Verify PostFx Shape Weight Modifier component is enabled by adding Spline and Tube Shape component. 11) Enter/Exit game mode. 12) Test IsHidden. 13) Test IsVisible. @@ -146,10 +143,10 @@ def AtomEditorComponents_postfx_shape_weight_AddedToEntity(): postfx_shape_weight_entity.add_component(postfx_layer_name) Report.result(Tests.postfx_layer_component, postfx_shape_weight_entity.has_component(postfx_layer_name)) - # 7. Verify PostFx Shape Weight Modifier component not enabled because shape is also required. + # 7. Verify PostFx Shape Weight Modifier component is NOT enabled since it also requires a shape. Report.result(Tests.postfx_shape_weight_disabled, not postfx_shape_weight_component.is_enabled()) - # 8. Add remove each shape to test if the PostFX Shape Weight Modifier is enabled by having a required shape + # 8. Add a required shape looping over a list and checking if it enables PostFX Shape Weight Modifier. for shape in ['Axis Aligned Box Shape', 'Box Shape', 'Capsule Shape', 'Compound Shape', 'Cylinder Shape', 'Disk Shape', 'Polygon Prism Shape', 'Quad Shape', 'Sphere Shape', 'Vegetation Reference Shape']: postfx_shape_weight_entity.add_component(shape) @@ -158,18 +155,16 @@ def AtomEditorComponents_postfx_shape_weight_AddedToEntity(): f"Entity did not have a {shape} component") Report.result(test_shape, postfx_shape_weight_entity.has_component(shape)) - #Check if required shape allows PostFX Shape Weight Modifier to be enabled + # Check if required shape allows PostFX Shape Weight Modifier to be enabled Report.result(Tests.postfx_shape_weight_enabled, postfx_shape_weight_component.is_enabled()) - # 9. UNDO component addition and check that PostFX Shape Weight Modifier is not enabled + # 9. Undo to remove each added shape and verify PostFX Shape Weight Modifier is not enabled. general.undo() - general.idle_wait_frames(1) - Report.result(Tests.shape_undo, not postfx_shape_weight_entity.has_component(shape)) Report.result(Tests.postfx_shape_weight_disabled, not postfx_shape_weight_component.is_enabled()) - # 10. Add Tube Shape and Spline to fulfil the required shape component + # 10. Verify PostFx Shape Weight Modifier component is enabled by adding Spline and Tube Shape component. postfx_shape_weight_entity.add_components(['Spline', 'Tube Shape']) - Report.result(Tests.shape_component, postfx_shape_weight_entity.has_component('Tube Shape')) + Report.result(Tests.tube_shape_component, postfx_shape_weight_entity.has_component('Tube Shape')) Report.result(Tests.postfx_shape_weight_enabled, postfx_shape_weight_component.is_enabled()) # 11. Enter/Exit game mode. From e7d7720d020104b169b19fb9bbe323bca8b5804c Mon Sep 17 00:00:00 2001 From: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Wed, 13 Oct 2021 15:14:14 -0500 Subject: [PATCH 41/65] Procedural Prefabs: Don't activate prefab when saving to manifest (#4663) * Generate prefab group dom without activating prefab Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> * Fix bad merge Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> --- .../PrefabGroup/PrefabGroupBehavior.cpp | 20 +++---------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroupBehavior.cpp b/Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroupBehavior.cpp index 13828e5f8c..44a95c1b6b 100644 --- a/Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroupBehavior.cpp +++ b/Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroupBehavior.cpp @@ -124,24 +124,10 @@ namespace AZ::SceneAPI::Behaviors return {}; } - // create instance to update the asset hints - auto instance = prefabSystemComponentInterface->InstantiatePrefab(templateId); - if (!instance) - { - AZ_Error("prefab", false, "PrefabGroup(%s) Could not instantiate prefab", prefabGroup->GetName().c_str()); - return {}; - } - - auto* instanceToTemplateInterface = AZ::Interface::Get(); - if (!instanceToTemplateInterface) - { - AZ_Error("prefab", false, "Could not get InstanceToTemplateInterface"); - return {}; - } - - // fill out a JSON DOM + const rapidjson::Document& generatedInstanceDom = prefabSystemComponentInterface->FindTemplateDom(templateId); auto proceduralPrefab = AZStd::make_unique(rapidjson::kObjectType); - instanceToTemplateInterface->GenerateDomForInstance(*proceduralPrefab.get(), *instance.get()); + proceduralPrefab->CopyFrom(generatedInstanceDom, proceduralPrefab->GetAllocator(), true); + return proceduralPrefab; } From 29b62c7b842f77872ec9467aeabb14685e5d53cc Mon Sep 17 00:00:00 2001 From: amzn-phist <52085794+amzn-phist@users.noreply.github.com> Date: Wed, 13 Oct 2021 15:22:25 -0500 Subject: [PATCH 42/65] Various updates to get pak builds working (#4552) * Various updates to get pak builds working -Fix basing config file merges off engine root. -Merge command-line in relelase to make sure they override defaults. -Fix nullptrs. -Exclude more paths from being sent to bootstrap setreg. Signed-off-by: amzn-phist <52085794+amzn-phist@users.noreply.github.com> * Reverting a change that caused some test failures. Signed-off-by: amzn-phist <52085794+amzn-phist@users.noreply.github.com> * Change tabs to spaces Signed-off-by: amzn-phist <52085794+amzn-phist@users.noreply.github.com> --- .../AzGameFramework/Application/GameApplication.cpp | 2 ++ .../Code/Source/Grid/GridComponentController.cpp | 4 ++++ .../Code/Source/SkyBox/HDRiSkyboxComponentController.cpp | 2 +- Registry/setregbuilder.assetprocessor.setreg | 7 ++++++- 4 files changed, 13 insertions(+), 2 deletions(-) diff --git a/Code/Framework/AzGameFramework/AzGameFramework/Application/GameApplication.cpp b/Code/Framework/AzGameFramework/AzGameFramework/Application/GameApplication.cpp index 462de43262..0cce93d751 100644 --- a/Code/Framework/AzGameFramework/AzGameFramework/Application/GameApplication.cpp +++ b/Code/Framework/AzGameFramework/AzGameFramework/Application/GameApplication.cpp @@ -96,6 +96,8 @@ namespace AzGameFramework AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_CommandLine(registry, m_commandLine, false); AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_ProjectUserRegistry(registry, AZ_TRAIT_OS_PLATFORM_CODENAME, specializations, &scratchBuffer); AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_CommandLine(registry, m_commandLine, true); +#else + AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_CommandLine(registry, m_commandLine, false); #endif // Update the Runtime file paths in case the "{BootstrapSettingsRootKey}/assets" key was overriden by a setting registry AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(registry); diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Grid/GridComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Grid/GridComponentController.cpp index 52a4cd4343..b4131894f4 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Grid/GridComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Grid/GridComponentController.cpp @@ -175,6 +175,10 @@ namespace AZ void GridComponentController::OnBeginPrepareRender() { auto* auxGeomFP = AZ::RPI::Scene::GetFeatureProcessorForEntity(m_entityId); + if (!auxGeomFP) + { + return; + } if (auto auxGeom = auxGeomFP->GetDrawQueue()) { BuildGrid(); diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SkyBox/HDRiSkyboxComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SkyBox/HDRiSkyboxComponentController.cpp index b71ba1e148..171c5e417f 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SkyBox/HDRiSkyboxComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SkyBox/HDRiSkyboxComponentController.cpp @@ -65,7 +65,7 @@ namespace AZ m_featureProcessorInterface = RPI::Scene::GetFeatureProcessorForEntity(entityId); // only activate if there is no other skybox activate - if (!m_featureProcessorInterface->IsEnabled()) + if (m_featureProcessorInterface && !m_featureProcessorInterface->IsEnabled()) { m_featureProcessorInterface->SetSkyboxMode(SkyBoxMode::Cubemap); m_featureProcessorInterface->Enable(true); diff --git a/Registry/setregbuilder.assetprocessor.setreg b/Registry/setregbuilder.assetprocessor.setreg index 00e6e2f7f8..d67b6047f6 100644 --- a/Registry/setregbuilder.assetprocessor.setreg +++ b/Registry/setregbuilder.assetprocessor.setreg @@ -20,8 +20,13 @@ "Excludes": [ "/Amazon/AzCore/Runtime", + "/Amazon/AzCore/Bootstrap/engine_path", "/Amazon/AzCore/Bootstrap/project_path", - "/O3DE/Runtime", + "/Amazon/AzCore/Bootstrap/project_cache_path", + "/Amazon/AzCore/Bootstrap/project_user_path", + "/Amazon/AzCore/Bootstrap/project_log_path", + "/Amazon/Project/Settings/Build/project_build_path", + "/O3DE/Runtime" ] } } From 3934ac24e00135d79a01101c8f590ffac088e521 Mon Sep 17 00:00:00 2001 From: allisaurus <34254888+allisaurus@users.noreply.github.com> Date: Wed, 13 Oct 2021 13:25:19 -0700 Subject: [PATCH 43/65] Add field titles, tooltips to AWSClientAuth AWSCognitoUserManagementRequestBus nodes (#4613) Signed-off-by: Stanko --- .../Source/AWSClientAuthSystemComponent.cpp | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/Gems/AWSClientAuth/Code/Source/AWSClientAuthSystemComponent.cpp b/Gems/AWSClientAuth/Code/Source/AWSClientAuthSystemComponent.cpp index dbde19aad6..92c01dab19 100644 --- a/Gems/AWSClientAuth/Code/Source/AWSClientAuthSystemComponent.cpp +++ b/Gems/AWSClientAuth/Code/Source/AWSClientAuthSystemComponent.cpp @@ -33,7 +33,7 @@ namespace AWSClientAuth AZ::SerializeContext* serialize = azrtti_cast(context); if (serialize) { - serialize->Class()->Version(1); + serialize->Class()->Version(2); if (AZ::EditContext* ec = serialize->GetEditContext()) { @@ -105,12 +105,22 @@ namespace AWSClientAuth behaviorContext->EBus("AWSCognitoUserManagementRequestBus") ->Attribute(AZ::Script::Attributes::Category, SerializeComponentName) ->Event("Initialize", &AWSCognitoUserManagementRequestBus::Events::Initialize) - ->Event("EmailSignUpAsync", &AWSCognitoUserManagementRequestBus::Events::EmailSignUpAsync) - ->Event("PhoneSignUpAsync", &AWSCognitoUserManagementRequestBus::Events::PhoneSignUpAsync) - ->Event("ConfirmSignUpAsync", &AWSCognitoUserManagementRequestBus::Events::ConfirmSignUpAsync) - ->Event("ForgotPasswordAsync", &AWSCognitoUserManagementRequestBus::Events::ForgotPasswordAsync) - ->Event("ConfirmForgotPasswordAsync", &AWSCognitoUserManagementRequestBus::Events::ConfirmForgotPasswordAsync) - ->Event("EnableMFAAsync", &AWSCognitoUserManagementRequestBus::Events::EnableMFAAsync); + ->Event( + "EmailSignUpAsync", &AWSCognitoUserManagementRequestBus::Events::EmailSignUpAsync, + { { { "Username", "The client's username" }, { "Password", "The client's password" }, { "Email", "The email address used to sign up" } } }) + ->Event( + "PhoneSignUpAsync", &AWSCognitoUserManagementRequestBus::Events::PhoneSignUpAsync, + { { { "Username", "The client's username" }, { "Password", "The client's password" }, { "Phone number", "The phone number used to sign up" } } }) + ->Event( + "ConfirmSignUpAsync", &AWSCognitoUserManagementRequestBus::Events::ConfirmSignUpAsync, + { { { "Username", "The client's username" }, { "Confirmation code", "The client's confirmation code" } } }) + ->Event( + "ForgotPasswordAsync", &AWSCognitoUserManagementRequestBus::Events::ForgotPasswordAsync, + { { { "Username", "The client's username" } } }) + ->Event( + "ConfirmForgotPasswordAsync", &AWSCognitoUserManagementRequestBus::Events::ConfirmForgotPasswordAsync, + { { { "Username", "The client's username" }, { "Confirmation code", "The client's confirmation code" }, { "New password", "The new password for the client" } } }) + ->Event("EnableMFAAsync", &AWSCognitoUserManagementRequestBus::Events::EnableMFAAsync, { { { "Access token", "The MFA access token" } } }); behaviorContext->EBus("AuthenticationProviderNotificationBus") From 3c3cde99bed5f589c7522243b029e9f79baa949d Mon Sep 17 00:00:00 2001 From: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> Date: Wed, 13 Oct 2021 13:33:59 -0700 Subject: [PATCH 44/65] LYN-7189 | Outliner - Disable context menu if right clicking on disabled entity (#4651) * Don't allow right clicking on a non selectable entity in the Outliner Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> * Also check that the index is valid to still allow the right click context menu to appear on empty areas of the widget. Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> * Change if check to a more readable bool. Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> --- .../AzToolsFramework/UI/Outliner/EntityOutlinerWidget.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerWidget.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerWidget.cpp index 3b48cc4967..4d53102edb 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerWidget.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerWidget.cpp @@ -553,6 +553,13 @@ namespace AzToolsFramework return; } + // Do not display the context menu if the item under the mouse cursor is not selectable. + if (const QModelIndex& index = m_gui->m_objectTree->indexAt(pos); index.isValid() + && (index.flags() & Qt::ItemIsSelectable) == 0) + { + return; + } + QMenu* contextMenu = new QMenu(this); // Populate global context menu. From 4625e6d315fae6b343604d3e9654745bd352ee52 Mon Sep 17 00:00:00 2001 From: brianherrera Date: Wed, 13 Oct 2021 13:35:44 -0700 Subject: [PATCH 45/65] Fix indentation Signed-off-by: brianherrera --- .../build/bootstrap/incremental_build_util.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/scripts/build/bootstrap/incremental_build_util.py b/scripts/build/bootstrap/incremental_build_util.py index 6117fe0180..68e764e56f 100644 --- a/scripts/build/bootstrap/incremental_build_util.py +++ b/scripts/build/bootstrap/incremental_build_util.py @@ -333,14 +333,14 @@ def mount_volume_to_device(created): attribute disk clear readonly """.encode('utf-8')) # assume disk # for now - if created: - print('Creating filesystem on new volume') - f.write("""create partition primary - select partition 1 - format quick fs=ntfs - assign - active - """.encode('utf-8')) + if created: + print('Creating filesystem on new volume') + f.write("""create partition primary + select partition 1 + format quick fs=ntfs + assign + active + """.encode('utf-8')) subprocess.call(['diskpart', '/s', f.name]) From 5ba30f9a3746af4550ba4b0bcd89519e5375dfac Mon Sep 17 00:00:00 2001 From: Nicholas Lawson <70027408+lawsonamzn@users.noreply.github.com> Date: Wed, 13 Oct 2021 13:46:20 -0700 Subject: [PATCH 46/65] Update tiff to the new package revision (fixes IOS TIFF package) (#4638) The new tiff package works for all platforms, including android and IOS, which had issues before. Android - was missing tiff.h ios - wrong minimum ios revision --- Code/Editor/CMakeLists.txt | 2 +- Code/Legacy/CrySystem/CMakeLists.txt | 2 +- Gems/Atom/Asset/ImageProcessingAtom/Code/CMakeLists.txt | 2 +- cmake/3rdParty/Platform/Android/BuiltInPackages_android.cmake | 2 +- cmake/3rdParty/Platform/Linux/BuiltInPackages_linux.cmake | 2 +- cmake/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake | 2 +- cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake | 2 +- cmake/3rdParty/Platform/iOS/BuiltInPackages_ios.cmake | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Code/Editor/CMakeLists.txt b/Code/Editor/CMakeLists.txt index 2ea0aa1a74..bdfac373eb 100644 --- a/Code/Editor/CMakeLists.txt +++ b/Code/Editor/CMakeLists.txt @@ -102,7 +102,7 @@ ly_add_target( 3rdParty::Qt::Gui 3rdParty::Qt::Widgets 3rdParty::Qt::Concurrent - 3rdParty::tiff + 3rdParty::TIFF 3rdParty::squish-ccr 3rdParty::AWSNativeSDK::STS Legacy::CryCommon diff --git a/Code/Legacy/CrySystem/CMakeLists.txt b/Code/Legacy/CrySystem/CMakeLists.txt index 4c1f0d9b82..ebfc866f4a 100644 --- a/Code/Legacy/CrySystem/CMakeLists.txt +++ b/Code/Legacy/CrySystem/CMakeLists.txt @@ -27,7 +27,7 @@ ly_add_target( 3rdParty::expat 3rdParty::lz4 3rdParty::md5 - 3rdParty::tiff + 3rdParty::TIFF 3rdParty::zstd Legacy::CryCommon Legacy::CrySystem.XMLBinary diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/CMakeLists.txt b/Gems/Atom/Asset/ImageProcessingAtom/Code/CMakeLists.txt index 836f173632..982ec43715 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/CMakeLists.txt +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/CMakeLists.txt @@ -64,7 +64,7 @@ ly_add_target( 3rdParty::Qt::Gui 3rdParty::astc-encoder 3rdParty::squish-ccr - 3rdParty::tiff + 3rdParty::TIFF 3rdParty::ISPCTexComp 3rdParty::ilmbase AZ::AzFramework diff --git a/cmake/3rdParty/Platform/Android/BuiltInPackages_android.cmake b/cmake/3rdParty/Platform/Android/BuiltInPackages_android.cmake index c1a770d1ff..9dbdbbd8aa 100644 --- a/cmake/3rdParty/Platform/Android/BuiltInPackages_android.cmake +++ b/cmake/3rdParty/Platform/Android/BuiltInPackages_android.cmake @@ -16,7 +16,7 @@ ly_associate_package(PACKAGE_NAME zstd-1.35-multiplatform TARGETS zst ly_associate_package(PACKAGE_NAME glad-2.0.0-beta-rev2-multiplatform TARGETS glad PACKAGE_HASH ff97ee9664e97d0854b52a3734c2289329d9f2b4cd69478df6d0ca1f1c9392ee) ly_associate_package(PACKAGE_NAME lux_core-2.2-rev5-multiplatform TARGETS lux_core PACKAGE_HASH c8c13cf7bc351643e1abd294d0841b24dee60e51647dff13db7aec396ad1e0b5) # platform-specific: -ly_associate_package(PACKAGE_NAME tiff-4.2.0.15-rev2-android TARGETS tiff PACKAGE_HASH 252b99e5886ec59fdccf38603c1399dd3fc02d878641aba35a7f8d2504065a06) +ly_associate_package(PACKAGE_NAME tiff-4.2.0.15-rev4-android TARGETS TIFF PACKAGE_HASH 2c62cdf34a8ee6c7eb091d05d98f60b4da7634c74054d4dbb8736886182f4589) ly_associate_package(PACKAGE_NAME freetype-2.10.4.16-android TARGETS freetype PACKAGE_HASH df9e4d559ea0f03b0666b48c79813b1cd4d9624429148a249865de9f5c2c11cd) ly_associate_package(PACKAGE_NAME AWSNativeSDK-1.9.50-rev1-android TARGETS AWSNativeSDK PACKAGE_HASH 33771499f9080cbaab613459927e52911e68f94fa356397885e85005efbd1490) ly_associate_package(PACKAGE_NAME Lua-5.3.5-rev5-android TARGETS Lua PACKAGE_HASH 1f638e94a17a87fe9e588ea456d5893876094b4db191234380e4c4eb9e06c300) diff --git a/cmake/3rdParty/Platform/Linux/BuiltInPackages_linux.cmake b/cmake/3rdParty/Platform/Linux/BuiltInPackages_linux.cmake index 8e4a3ef828..d6fdc5cd9b 100644 --- a/cmake/3rdParty/Platform/Linux/BuiltInPackages_linux.cmake +++ b/cmake/3rdParty/Platform/Linux/BuiltInPackages_linux.cmake @@ -23,7 +23,7 @@ ly_associate_package(PACKAGE_NAME xxhash-0.7.4-rev1-multiplatform # platform-specific: ly_associate_package(PACKAGE_NAME AWSGameLiftServerSDK-3.4.1-rev1-linux TARGETS AWSGameLiftServerSDK PACKAGE_HASH a8149a95bd100384af6ade97e2b21a56173740d921e6c3da8188cd51554d39af) -ly_associate_package(PACKAGE_NAME tiff-4.2.0.15-rev2-linux TARGETS tiff PACKAGE_HASH 19791da0a370470a6c187199f97c2c46efcc2d89146e2013775fb3600fd7317d) +ly_associate_package(PACKAGE_NAME tiff-4.2.0.15-rev3-linux TARGETS TIFF PACKAGE_HASH 2377f48b2ebc2d1628d9f65186c881544c92891312abe478a20d10b85877409a) ly_associate_package(PACKAGE_NAME freetype-2.10.4.16-linux TARGETS freetype PACKAGE_HASH 3f10c703d9001ecd2bb51a3bd003d3237c02d8f947ad0161c0252fdc54cbcf97) ly_associate_package(PACKAGE_NAME AWSNativeSDK-1.7.167-rev6-linux TARGETS AWSNativeSDK PACKAGE_HASH 490291e4c8057975c3ab86feb971b8a38871c58bac5e5d86abdd1aeb7141eec4) ly_associate_package(PACKAGE_NAME Lua-5.3.5-rev5-linux TARGETS Lua PACKAGE_HASH 1adc812abe3dd0dbb2ca9756f81d8f0e0ba45779ac85bf1d8455b25c531a38b0) diff --git a/cmake/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake b/cmake/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake index 631c42784a..41df718b71 100644 --- a/cmake/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake +++ b/cmake/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake @@ -25,7 +25,7 @@ ly_associate_package(PACKAGE_NAME xxhash-0.7.4-rev1-multiplatform # platform-specific: ly_associate_package(PACKAGE_NAME DirectXShaderCompilerDxc-1.6.2104-o3de-rev3-mac TARGETS DirectXShaderCompilerDxc PACKAGE_HASH 3f77367dbb0342136ec4ebbd44bc1fedf7198089a0f83c5631248530769b2be6) ly_associate_package(PACKAGE_NAME SPIRVCross-2021.04.29-rev1-mac TARGETS SPIRVCross PACKAGE_HASH 78c6376ed2fd195b9b1f5fb2b56e5267a32c3aa21fb399e905308de470eb4515) -ly_associate_package(PACKAGE_NAME tiff-4.2.0.15-rev2-mac TARGETS tiff PACKAGE_HASH b6f3040319f5bfe465d7e3f9b12ceed0dc951e66e05562beaac1c8da3b1b5d3f) +ly_associate_package(PACKAGE_NAME tiff-4.2.0.15-rev3-mac TARGETS TIFF PACKAGE_HASH c2615ccdadcc0e1d6c5ed61e5965c4d3a82193d206591b79b805c3b3ff35a4bf) ly_associate_package(PACKAGE_NAME freetype-2.10.4.16-mac TARGETS freetype PACKAGE_HASH f159b346ac3251fb29cb8dd5f805c99b0015ed7fdb3887f656945ca701a61d0d) ly_associate_package(PACKAGE_NAME AWSNativeSDK-1.7.167-rev5-mac TARGETS AWSNativeSDK PACKAGE_HASH ffb890bd9cf23afb429b9214ad9bac1bf04696f07a0ebb93c42058c482ab2f01) ly_associate_package(PACKAGE_NAME Lua-5.3.5-rev6-mac TARGETS Lua PACKAGE_HASH b9079fd35634774c9269028447562c6b712dbc83b9c64975c095fd423ff04c08) diff --git a/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake b/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake index 428e5e9526..cccd2591e8 100644 --- a/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake +++ b/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake @@ -26,7 +26,7 @@ ly_associate_package(PACKAGE_NAME xxhash-0.7.4-rev1-multiplatform ly_associate_package(PACKAGE_NAME AWSGameLiftServerSDK-3.4.1-rev1-windows TARGETS AWSGameLiftServerSDK PACKAGE_HASH a0586b006e4def65cc25f388de17dc475e417dc1e6f9d96749777c88aa8271b0) ly_associate_package(PACKAGE_NAME DirectXShaderCompilerDxc-1.6.2104-o3de-rev3-windows TARGETS DirectXShaderCompilerDxc PACKAGE_HASH 803e10b94006b834cbbdd30f562a8ddf04174c2cb6956c8399ec164ef8418d1f) ly_associate_package(PACKAGE_NAME SPIRVCross-2021.04.29-rev1-windows TARGETS SPIRVCross PACKAGE_HASH 7d601ea9d625b1d509d38bd132a1f433d7e895b16adab76bac6103567a7a6817) -ly_associate_package(PACKAGE_NAME tiff-4.2.0.15-rev2-windows TARGETS tiff PACKAGE_HASH ff03464ca460fc34a8406b2a0c548ad221b10e40480b0abb954f1e649c20bad0) +ly_associate_package(PACKAGE_NAME tiff-4.2.0.15-rev3-windows TARGETS TIFF PACKAGE_HASH c6000a906e6d2a0816b652e93dfbeab41c9ed73cdd5a613acd53e553d0510b60) ly_associate_package(PACKAGE_NAME freetype-2.10.4.16-windows TARGETS freetype PACKAGE_HASH 9809255f1c59b07875097aa8d8c6c21c97c47a31fb35e30f2bb93188e99a85ff) ly_associate_package(PACKAGE_NAME AWSNativeSDK-1.7.167-rev4-windows TARGETS AWSNativeSDK PACKAGE_HASH a900e80f7259e43aed5c847afee2599ada37f29db70505481397675bcbb6c76c) ly_associate_package(PACKAGE_NAME Lua-5.3.5-rev5-windows TARGETS Lua PACKAGE_HASH 136faccf1f73891e3fa3b95f908523187792e56f5b92c63c6a6d7e72d1158d40) diff --git a/cmake/3rdParty/Platform/iOS/BuiltInPackages_ios.cmake b/cmake/3rdParty/Platform/iOS/BuiltInPackages_ios.cmake index abfba29e5a..8042c888c7 100644 --- a/cmake/3rdParty/Platform/iOS/BuiltInPackages_ios.cmake +++ b/cmake/3rdParty/Platform/iOS/BuiltInPackages_ios.cmake @@ -17,7 +17,7 @@ ly_associate_package(PACKAGE_NAME glad-2.0.0-beta-rev2-multiplatform TARGETS gla ly_associate_package(PACKAGE_NAME lux_core-2.2-rev5-multiplatform TARGETS lux_core PACKAGE_HASH c8c13cf7bc351643e1abd294d0841b24dee60e51647dff13db7aec396ad1e0b5) # platform-specific: -ly_associate_package(PACKAGE_NAME tiff-4.2.0.15-rev2-ios TARGETS tiff PACKAGE_HASH d864beb0c955a55f28c2a993843afb2ecf6e01519ddfc857cedf34fc5db68d49) +ly_associate_package(PACKAGE_NAME tiff-4.2.0.15-rev3-ios TARGETS TIFF PACKAGE_HASH e9067e88649fb6e93a926d9ed38621a9fae360a2e6f6eb24ebca63c1bc7761ea) ly_associate_package(PACKAGE_NAME freetype-2.10.4.16-ios TARGETS freetype PACKAGE_HASH 3ac3c35e056ae4baec2e40caa023d76a7a3320895ef172b6655e9261b0dc2e29) ly_associate_package(PACKAGE_NAME AWSNativeSDK-1.7.167-rev4-ios TARGETS AWSNativeSDK PACKAGE_HASH d10e7496ca705577032821011beaf9f2507689f23817bfa0ed4d2a2758afcd02) ly_associate_package(PACKAGE_NAME Lua-5.3.5-rev5-ios TARGETS Lua PACKAGE_HASH c2d3c4e67046c293049292317a7d60fdb8f23effeea7136aefaef667163e5ffe) From b792ff3d33002c121e083562f476bacbb1c8fce0 Mon Sep 17 00:00:00 2001 From: brianherrera Date: Wed, 13 Oct 2021 14:12:18 -0700 Subject: [PATCH 47/65] Fix command formatting Signed-off-by: brianherrera --- scripts/build/bootstrap/incremental_build_util.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/build/bootstrap/incremental_build_util.py b/scripts/build/bootstrap/incremental_build_util.py index 68e764e56f..ff243ab02b 100644 --- a/scripts/build/bootstrap/incremental_build_util.py +++ b/scripts/build/bootstrap/incremental_build_util.py @@ -335,7 +335,8 @@ def mount_volume_to_device(created): if created: print('Creating filesystem on new volume') - f.write("""create partition primary + f.write(""" + create partition primary select partition 1 format quick fs=ntfs assign From 2b7262f566f3e27ee644c0c38de045bc93fcee6e Mon Sep 17 00:00:00 2001 From: sweeneys Date: Wed, 13 Oct 2021 14:14:01 -0700 Subject: [PATCH 48/65] Mock additional popen calls Signed-off-by: sweeneys --- Tools/LyTestTools/tests/unit/test_asset_processor.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Tools/LyTestTools/tests/unit/test_asset_processor.py b/Tools/LyTestTools/tests/unit/test_asset_processor.py index aabbf2f2f5..743ca9a2e4 100755 --- a/Tools/LyTestTools/tests/unit/test_asset_processor.py +++ b/Tools/LyTestTools/tests/unit/test_asset_processor.py @@ -45,6 +45,7 @@ class TestAssetProcessor(object): @mock.patch('subprocess.Popen') @mock.patch('ly_test_tools.o3de.asset_processor.AssetProcessor.connect_socket') @mock.patch('ly_test_tools.o3de.asset_processor.ASSET_PROCESSOR_PLATFORM_MAP', {'foo': 'bar'}) + @mock.patch('time.sleep', mock.MagicMock()) def test_Start_NoneRunning_ProcStarted(self, mock_connect, mock_popen, mock_workspace): mock_ap_path = 'mock_ap_path' mock_workspace.asset_processor_platform = 'foo' @@ -54,6 +55,9 @@ class TestAssetProcessor(object): under_test = ly_test_tools.o3de.asset_processor.AssetProcessor(mock_workspace) under_test.enable_asset_processor_platform = mock.MagicMock() under_test.wait_for_idle = mock.MagicMock() + mock_proc_object = mock.MagicMock() + mock_proc_object.poll.return_value = None + mock_popen.return_value = mock_proc_object under_test.start(connect_to_ap=True) From f7e2d07a4be6554141956246120d0a76f224b183 Mon Sep 17 00:00:00 2001 From: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> Date: Wed, 13 Oct 2021 14:17:09 -0700 Subject: [PATCH 49/65] LYN-7333 | Fix multiple selection by dragging to take focus mode and containers into account. (#4620) * Change FocusModeNotificationBus's OnEditorFocusChanged arguments to also pass the previous focus root entity id. Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> * Add focus mode and container entity states to the visibility cache for the viewport. Use that data to correctly select entities when a rect is dragged on the viewport. Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> * Minor code adjustments Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> * Minor fixes and optimizations Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> --- .../FocusMode/FocusModeNotificationBus.h | 6 +- .../FocusMode/FocusModeSystemComponent.cpp | 3 +- .../UI/Outliner/EntityOutlinerTreeView.cpp | 3 +- .../UI/Outliner/EntityOutlinerTreeView.hxx | 2 +- .../EditorTransformComponentSelection.cpp | 2 +- .../EditorVisibleEntityDataCache.cpp | 112 +++++++++++++++++- .../EditorVisibleEntityDataCache.h | 24 ++-- 7 files changed, 136 insertions(+), 16 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeNotificationBus.h b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeNotificationBus.h index ab8629ac85..81f1f7bb3b 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeNotificationBus.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeNotificationBus.h @@ -28,8 +28,10 @@ namespace AzToolsFramework ////////////////////////////////////////////////////////////////////////// //! Triggered when the editor focus is changed to a different entity. - //! @param entityId The entity the focus has been moved to. - virtual void OnEditorFocusChanged(AZ::EntityId entityId) = 0; + //! @param previousFocusEntityId The entity the focus has been moved from. + //! @param newFocusEntityId The entity the focus has been moved to. + virtual void OnEditorFocusChanged( + [[maybe_unused]] AZ::EntityId previousFocusEntityId, [[maybe_unused]] AZ::EntityId newFocusEntityId) {} protected: ~FocusModeNotifications() = default; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp index af518afd66..f592c471d0 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp @@ -71,8 +71,9 @@ namespace AzToolsFramework return; } + AZ::EntityId previousFocusEntityId = m_focusRoot; m_focusRoot = entityId; - FocusModeNotificationBus::Broadcast(&FocusModeNotifications::OnEditorFocusChanged, m_focusRoot); + FocusModeNotificationBus::Broadcast(&FocusModeNotifications::OnEditorFocusChanged, previousFocusEntityId, m_focusRoot); if (auto tracker = AZ::Interface::Get(); tracker != nullptr) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.cpp index 4364ae1efc..d3138f5139 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.cpp @@ -313,7 +313,8 @@ namespace AzToolsFramework StyledTreeView::StartCustomDrag(indexListSorted, supportedActions); } - void EntityOutlinerTreeView::OnEditorFocusChanged([[maybe_unused]] AZ::EntityId entityId) + void EntityOutlinerTreeView::OnEditorFocusChanged( + [[maybe_unused]] AZ::EntityId previousFocusEntityId, [[maybe_unused]] AZ::EntityId newFocusEntityId) { viewport()->repaint(); } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.hxx b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.hxx index 2ddbaaafa9..5d76ec6db2 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.hxx +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.hxx @@ -64,7 +64,7 @@ namespace AzToolsFramework void leaveEvent(QEvent* event) override; // FocusModeNotificationBus overrides ... - void OnEditorFocusChanged(AZ::EntityId entityId) override; + void OnEditorFocusChanged(AZ::EntityId previousFocusEntityId, AZ::EntityId newFocusEntityId) override; //! Renders the left side of the item: appropriate background, branch lines, icons. void drawBranches(QPainter* painter, const QRect& rect, const QModelIndex& index) const override; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp index 6ee5c97636..d5a0595049 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp @@ -407,7 +407,7 @@ namespace AzToolsFramework const AzFramework::CameraState cameraState = GetCameraState(viewportId); for (size_t entityCacheIndex = 0; entityCacheIndex < entityDataCache.VisibleEntityDataCount(); ++entityCacheIndex) { - if (entityDataCache.IsVisibleEntityLocked(entityCacheIndex) || !entityDataCache.IsVisibleEntityVisible(entityCacheIndex)) + if (!entityDataCache.IsVisibleEntitySelectableInViewport(entityCacheIndex)) { continue; } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorVisibleEntityDataCache.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorVisibleEntityDataCache.cpp index babc6f972c..328cfc3ae5 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorVisibleEntityDataCache.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorVisibleEntityDataCache.cpp @@ -9,7 +9,9 @@ #include "EditorVisibleEntityDataCache.h" #include +#include #include +#include #include #include @@ -21,13 +23,23 @@ namespace AzToolsFramework using ComponentEntityAccentType = Components::EditorSelectionAccentSystemComponent::ComponentEntityAccentType; EntityData() = default; - EntityData(AZ::EntityId entityId, const AZ::Transform& worldFromLocal, bool locked, bool visible, bool selected, bool iconHidden); + EntityData( + AZ::EntityId entityId, + const AZ::Transform& worldFromLocal, + bool locked, + bool visible, + bool inFocus, + bool descendantOfClosedContainer, + bool selected, + bool iconHidden); AZ::Transform m_worldFromLocal; AZ::EntityId m_entityId; ComponentEntityAccentType m_accent = ComponentEntityAccentType::None; bool m_locked = false; bool m_visible = true; + bool m_inFocus = true; + bool m_descendantOfClosedContainer = false; bool m_selected = false; bool m_iconHidden = false; }; @@ -57,12 +69,16 @@ namespace AzToolsFramework const AZ::Transform& worldFromLocal, const bool locked, const bool visible, + const bool inFocus, + const bool descendantOfClosedContainer, const bool selected, const bool iconHidden) : m_worldFromLocal(worldFromLocal) , m_entityId(entityId) , m_locked(locked) , m_visible(visible) + , m_inFocus(inFocus) + , m_descendantOfClosedContainer(descendantOfClosedContainer) , m_selected(selected) , m_iconHidden(iconHidden) { @@ -106,6 +122,18 @@ namespace AzToolsFramework bool locked = false; EditorEntityInfoRequestBus::EventResult(locked, entityId, &EditorEntityInfoRequestBus::Events::IsLocked); + bool inFocus = false; + if (auto focusModeInterface = AZ::Interface::Get()) + { + inFocus = focusModeInterface->IsInFocusSubTree(entityId); + } + + bool descendantOfClosedContainer = false; + if (ContainerEntityInterface* containerEntityInterface = AZ::Interface::Get()) + { + descendantOfClosedContainer = containerEntityInterface->IsUnderClosedContainerEntity(entityId); + } + bool iconHidden = false; EditorEntityIconComponentRequestBus::EventResult( iconHidden, entityId, &EditorEntityIconComponentRequests::IsEntityIconHiddenInViewport); @@ -113,7 +141,7 @@ namespace AzToolsFramework AZ::Transform worldFromLocal = AZ::Transform::CreateIdentity(); AZ::TransformBus::EventResult(worldFromLocal, entityId, &AZ::TransformBus::Events::GetWorldTM); - return { entityId, worldFromLocal, locked, visible, IsSelected(entityId), iconHidden }; + return { entityId, worldFromLocal, locked, visible, inFocus, descendantOfClosedContainer, IsSelected(entityId), iconHidden }; } EditorVisibleEntityDataCache::EditorVisibleEntityDataCache() @@ -126,10 +154,17 @@ namespace AzToolsFramework EntitySelectionEvents::Bus::Router::BusRouterConnect(); EditorEntityIconComponentNotificationBus::Router::BusRouterConnect(); ToolsApplicationNotificationBus::Handler::BusConnect(); + + AzFramework::EntityContextId editorEntityContextId = AzToolsFramework::GetEntityContextId(); + + ContainerEntityNotificationBus::Handler::BusConnect(editorEntityContextId); + FocusModeNotificationBus::Handler::BusConnect(editorEntityContextId); } EditorVisibleEntityDataCache::~EditorVisibleEntityDataCache() { + FocusModeNotificationBus::Handler::BusDisconnect(); + ContainerEntityNotificationBus::Handler::BusDisconnect(); ToolsApplicationNotificationBus::Handler::BusDisconnect(); EditorEntityIconComponentNotificationBus::Router::BusRouterDisconnect(); EntitySelectionEvents::Bus::Router::BusRouterDisconnect(); @@ -260,7 +295,10 @@ namespace AzToolsFramework bool EditorVisibleEntityDataCache::IsVisibleEntitySelectableInViewport(size_t index) const { - return m_impl->m_visibleEntityDatas[index].m_visible && !m_impl->m_visibleEntityDatas[index].m_locked; + return m_impl->m_visibleEntityDatas[index].m_visible + && !m_impl->m_visibleEntityDatas[index].m_locked + && m_impl->m_visibleEntityDatas[index].m_inFocus + && !m_impl->m_visibleEntityDatas[index].m_descendantOfClosedContainer; } AZStd::optional EditorVisibleEntityDataCache::GetVisibleEntityIndexFromId(const AZ::EntityId entityId) const @@ -371,4 +409,72 @@ namespace AzToolsFramework m_impl->m_visibleEntityDatas[entityIndex.value()].m_iconHidden = iconHidden; } } + + void EditorVisibleEntityDataCache::OnContainerEntityStatusChanged(AZ::EntityId entityId, [[maybe_unused]] bool open) + { + // Get container descendants + AzToolsFramework::EntityIdList descendantIds; + AZ::TransformBus::EventResult(descendantIds, entityId, &AZ::TransformBus::Events::GetAllDescendants); + + // Update cached values + if (auto containerEntityInterface = AZ::Interface::Get()) + { + for (AZ::EntityId descendantId : descendantIds) + { + if (AZStd::optional entityIndex = GetVisibleEntityIndexFromId(descendantId)) + { + m_impl->m_visibleEntityDatas[entityIndex.value()].m_descendantOfClosedContainer = + containerEntityInterface->IsUnderClosedContainerEntity(descendantId); + } + } + } + } + + void EditorVisibleEntityDataCache::OnEditorFocusChanged(AZ::EntityId previousFocusEntityId, AZ::EntityId newFocusEntityId) + { + if (previousFocusEntityId.IsValid() && newFocusEntityId.IsValid()) + { + // Get previous focus root descendants + AzToolsFramework::EntityIdList previousDescendantIds; + AZ::TransformBus::EventResult(previousDescendantIds, previousFocusEntityId, &AZ::TransformBus::Events::GetAllDescendants); + + // Get new focus root descendants + AzToolsFramework::EntityIdList newDescendantIds; + AZ::TransformBus::EventResult(newDescendantIds, newFocusEntityId, &AZ::TransformBus::Events::GetAllDescendants); + + // Merge EntityId Lists to avoid refreshing values twice + AzToolsFramework::EntityIdSet descendantsSet; + descendantsSet.insert(previousFocusEntityId); + descendantsSet.insert(newFocusEntityId); + descendantsSet.insert(previousDescendantIds.begin(), previousDescendantIds.end()); + descendantsSet.insert(newDescendantIds.begin(), newDescendantIds.end()); + + // Update cached values + if (auto focusModeInterface = AZ::Interface::Get()) + { + for (const AZ::EntityId& descendantId : descendantsSet) + { + if (AZStd::optional entityIndex = GetVisibleEntityIndexFromId(descendantId)) + { + m_impl->m_visibleEntityDatas[entityIndex.value()].m_inFocus = focusModeInterface->IsInFocusSubTree(descendantId); + } + } + } + } + else + { + // If either focus was the invalid entity, refresh all entities. + if (auto focusModeInterface = AZ::Interface::Get()) + { + for (size_t entityIndex = 0; entityIndex < m_impl->m_visibleEntityDatas.size(); ++entityIndex) + { + if (AZ::EntityId descendantId = GetVisibleEntityId(entityIndex); descendantId.IsValid()) + { + m_impl->m_visibleEntityDatas[entityIndex].m_inFocus = focusModeInterface->IsInFocusSubTree(descendantId); + } + } + } + } + } + } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorVisibleEntityDataCache.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorVisibleEntityDataCache.h index b34defc25b..16fa1b6d14 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorVisibleEntityDataCache.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorVisibleEntityDataCache.h @@ -11,6 +11,8 @@ #include #include #include +#include +#include #include #include #include @@ -28,6 +30,8 @@ namespace AzToolsFramework , private EntitySelectionEvents::Bus::Router , private EditorEntityIconComponentNotificationBus::Router , private ToolsApplicationNotificationBus::Handler + , private ContainerEntityNotificationBus::Handler + , private FocusModeNotificationBus::Handler { public: EditorVisibleEntityDataCache(); @@ -58,28 +62,34 @@ namespace AzToolsFramework void AddEntityIds(const EntityIdList& entityIds); private: - // ToolsApplicationNotificationBus + // ToolsApplicationNotificationBus overrides ... void AfterUndoRedo() override; - // EditorEntityVisibilityNotificationBus + // EditorEntityVisibilityNotificationBus overrides ... void OnEntityVisibilityChanged(bool visibility) override; - // EditorEntityLockComponentNotificationBus + // EditorEntityLockComponentNotificationBus overrides ... void OnEntityLockChanged(bool locked) override; - // TransformNotificationBus + // TransformNotificationBus overrides ... void OnTransformChanged(const AZ::Transform& local, const AZ::Transform& world) override; - // EditorComponentSelectionNotificationsBus + // EditorComponentSelectionNotificationsBus overrides ... void OnAccentTypeChanged(EntityAccentType accent) override; - // EntitySelectionEvents::Bus + // EntitySelectionEvents::Bus overrides ... void OnSelected() override; void OnDeselected() override; - // EditorEntityIconComponentNotificationBus + // EditorEntityIconComponentNotificationBus overrides ... void OnEntityIconChanged(const AZ::Data::AssetId& entityIconAssetId) override; + // ContainerEntityNotificationBus overrides ... + void OnContainerEntityStatusChanged(AZ::EntityId entityId, bool open) override; + + // FocusModeNotificationBus overrides ... + void OnEditorFocusChanged(AZ::EntityId previousFocusEntityId, AZ::EntityId newFocusEntityId) override; + class EditorVisibleEntityDataCacheImpl; AZStd::unique_ptr m_impl; //!< Internal representation of entity data cache. }; From f83c8bcb5aa17754c42f85e9471c42f08ec9225e Mon Sep 17 00:00:00 2001 From: Chris Galvan Date: Wed, 13 Oct 2021 16:28:56 -0500 Subject: [PATCH 50/65] Added gem template for custom tool in C++. Signed-off-by: Chris Galvan --- Templates/CustomTool/Template/CMakeLists.txt | 22 + .../Code/${NameLower}_editor_files.cmake | 15 + .../${NameLower}_editor_shared_files.cmake | 11 + .../${NameLower}_editor_tests_files.cmake | 11 + .../Template/Code/${NameLower}_files.cmake | 14 + .../Code/${NameLower}_shared_files.cmake | 11 + .../Code/${NameLower}_tests_files.cmake | 11 + .../CustomTool/Template/Code/CMakeLists.txt | 168 ++++++++ .../Code/Include/${Name}/${Name}Bus.h | 40 ++ .../Android/${NameLower}_android_files.cmake | 15 + .../${NameLower}_shared_android_files.cmake | 15 + .../Code/Platform/Android/PAL_android.cmake | 11 + .../Linux/${NameLower}_linux_files.cmake | 15 + .../${NameLower}_shared_linux_files.cmake | 15 + .../Code/Platform/Linux/PAL_linux.cmake | 11 + .../Platform/Mac/${NameLower}_mac_files.cmake | 15 + .../Mac/${NameLower}_shared_mac_files.cmake | 15 + .../Template/Code/Platform/Mac/PAL_mac.cmake | 11 + .../${NameLower}_shared_windows_files.cmake | 15 + .../Windows/${NameLower}_windows_files.cmake | 15 + .../Code/Platform/Windows/PAL_windows.cmake | 11 + .../Platform/iOS/${NameLower}_ios_files.cmake | 15 + .../iOS/${NameLower}_shared_ios_files.cmake | 15 + .../Template/Code/Platform/iOS/PAL_ios.cmake | 11 + .../Template/Code/Source/${Name}.qrc | 5 + .../Code/Source/${Name}EditorModule.cpp | 55 +++ .../Source/${Name}EditorSystemComponent.cpp | 78 ++++ .../Source/${Name}EditorSystemComponent.h | 45 +++ .../Template/Code/Source/${Name}Module.cpp | 25 ++ .../Code/Source/${Name}ModuleInterface.h | 45 +++ .../Code/Source/${Name}SystemComponent.cpp | 92 +++++ .../Code/Source/${Name}SystemComponent.h | 56 +++ .../Template/Code/Source/${Name}Widget.cpp | 44 ++ .../Template/Code/Source/${Name}Widget.h | 28 ++ .../Template/Code/Source/toolbar_icon.svg | 1 + .../Template/Code/Tests/${Name}EditorTest.cpp | 13 + .../Template/Code/Tests/${Name}Test.cpp | 13 + .../Platform/Android/android_gem.cmake | 8 + .../Platform/Android/android_gem.json | 3 + .../Template/Platform/Linux/linux_gem.cmake | 8 + .../Template/Platform/Linux/linux_gem.json | 3 + .../Template/Platform/Mac/mac_gem.cmake | 8 + .../Template/Platform/Mac/mac_gem.json | 3 + .../Platform/Windows/windows_gem.cmake | 8 + .../Platform/Windows/windows_gem.json | 3 + .../Template/Platform/iOS/ios_gem.cmake | 8 + .../Template/Platform/iOS/ios_gem.json | 3 + Templates/CustomTool/Template/gem.json | 17 + Templates/CustomTool/Template/preview.png | 3 + Templates/CustomTool/template.json | 382 ++++++++++++++++++ engine.json | 1 + 51 files changed, 1466 insertions(+) create mode 100644 Templates/CustomTool/Template/CMakeLists.txt create mode 100644 Templates/CustomTool/Template/Code/${NameLower}_editor_files.cmake create mode 100644 Templates/CustomTool/Template/Code/${NameLower}_editor_shared_files.cmake create mode 100644 Templates/CustomTool/Template/Code/${NameLower}_editor_tests_files.cmake create mode 100644 Templates/CustomTool/Template/Code/${NameLower}_files.cmake create mode 100644 Templates/CustomTool/Template/Code/${NameLower}_shared_files.cmake create mode 100644 Templates/CustomTool/Template/Code/${NameLower}_tests_files.cmake create mode 100644 Templates/CustomTool/Template/Code/CMakeLists.txt create mode 100644 Templates/CustomTool/Template/Code/Include/${Name}/${Name}Bus.h create mode 100644 Templates/CustomTool/Template/Code/Platform/Android/${NameLower}_android_files.cmake create mode 100644 Templates/CustomTool/Template/Code/Platform/Android/${NameLower}_shared_android_files.cmake create mode 100644 Templates/CustomTool/Template/Code/Platform/Android/PAL_android.cmake create mode 100644 Templates/CustomTool/Template/Code/Platform/Linux/${NameLower}_linux_files.cmake create mode 100644 Templates/CustomTool/Template/Code/Platform/Linux/${NameLower}_shared_linux_files.cmake create mode 100644 Templates/CustomTool/Template/Code/Platform/Linux/PAL_linux.cmake create mode 100644 Templates/CustomTool/Template/Code/Platform/Mac/${NameLower}_mac_files.cmake create mode 100644 Templates/CustomTool/Template/Code/Platform/Mac/${NameLower}_shared_mac_files.cmake create mode 100644 Templates/CustomTool/Template/Code/Platform/Mac/PAL_mac.cmake create mode 100644 Templates/CustomTool/Template/Code/Platform/Windows/${NameLower}_shared_windows_files.cmake create mode 100644 Templates/CustomTool/Template/Code/Platform/Windows/${NameLower}_windows_files.cmake create mode 100644 Templates/CustomTool/Template/Code/Platform/Windows/PAL_windows.cmake create mode 100644 Templates/CustomTool/Template/Code/Platform/iOS/${NameLower}_ios_files.cmake create mode 100644 Templates/CustomTool/Template/Code/Platform/iOS/${NameLower}_shared_ios_files.cmake create mode 100644 Templates/CustomTool/Template/Code/Platform/iOS/PAL_ios.cmake create mode 100644 Templates/CustomTool/Template/Code/Source/${Name}.qrc create mode 100644 Templates/CustomTool/Template/Code/Source/${Name}EditorModule.cpp create mode 100644 Templates/CustomTool/Template/Code/Source/${Name}EditorSystemComponent.cpp create mode 100644 Templates/CustomTool/Template/Code/Source/${Name}EditorSystemComponent.h create mode 100644 Templates/CustomTool/Template/Code/Source/${Name}Module.cpp create mode 100644 Templates/CustomTool/Template/Code/Source/${Name}ModuleInterface.h create mode 100644 Templates/CustomTool/Template/Code/Source/${Name}SystemComponent.cpp create mode 100644 Templates/CustomTool/Template/Code/Source/${Name}SystemComponent.h create mode 100644 Templates/CustomTool/Template/Code/Source/${Name}Widget.cpp create mode 100644 Templates/CustomTool/Template/Code/Source/${Name}Widget.h create mode 100644 Templates/CustomTool/Template/Code/Source/toolbar_icon.svg create mode 100644 Templates/CustomTool/Template/Code/Tests/${Name}EditorTest.cpp create mode 100644 Templates/CustomTool/Template/Code/Tests/${Name}Test.cpp create mode 100644 Templates/CustomTool/Template/Platform/Android/android_gem.cmake create mode 100644 Templates/CustomTool/Template/Platform/Android/android_gem.json create mode 100644 Templates/CustomTool/Template/Platform/Linux/linux_gem.cmake create mode 100644 Templates/CustomTool/Template/Platform/Linux/linux_gem.json create mode 100644 Templates/CustomTool/Template/Platform/Mac/mac_gem.cmake create mode 100644 Templates/CustomTool/Template/Platform/Mac/mac_gem.json create mode 100644 Templates/CustomTool/Template/Platform/Windows/windows_gem.cmake create mode 100644 Templates/CustomTool/Template/Platform/Windows/windows_gem.json create mode 100644 Templates/CustomTool/Template/Platform/iOS/ios_gem.cmake create mode 100644 Templates/CustomTool/Template/Platform/iOS/ios_gem.json create mode 100644 Templates/CustomTool/Template/gem.json create mode 100644 Templates/CustomTool/Template/preview.png create mode 100644 Templates/CustomTool/template.json diff --git a/Templates/CustomTool/Template/CMakeLists.txt b/Templates/CustomTool/Template/CMakeLists.txt new file mode 100644 index 0000000000..b19ea2edce --- /dev/null +++ b/Templates/CustomTool/Template/CMakeLists.txt @@ -0,0 +1,22 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +set(o3de_gem_path ${CMAKE_CURRENT_LIST_DIR}) +set(o3de_gem_json ${o3de_gem_path}/gem.json) +o3de_read_json_key(o3de_gem_name ${o3de_gem_json} "gem_name") +o3de_restricted_path(${o3de_gem_json} o3de_gem_restricted_path) + +ly_get_list_relative_pal_filename(pal_dir ${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME} "${o3de_gem_restricted_path}" ${o3de_gem_path} ${o3de_gem_name}) + +# Now that we have the platform abstraction layer (PAL) folder for this folder, thats where we will find the +# project cmake for this platform. +include(${pal_dir}/${PAL_PLATFORM_NAME_LOWERCASE}_gem.cmake) + +ly_add_external_target_path(${CMAKE_CURRENT_LIST_DIR}/3rdParty) + +add_subdirectory(Code) diff --git a/Templates/CustomTool/Template/Code/${NameLower}_editor_files.cmake b/Templates/CustomTool/Template/Code/${NameLower}_editor_files.cmake new file mode 100644 index 0000000000..d73efffa2e --- /dev/null +++ b/Templates/CustomTool/Template/Code/${NameLower}_editor_files.cmake @@ -0,0 +1,15 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +set(FILES + Source/${Name}EditorSystemComponent.cpp + Source/${Name}EditorSystemComponent.h + Source/${Name}Widget.cpp + Source/${Name}Widget.h + Source/${Name}.qrc +) diff --git a/Templates/CustomTool/Template/Code/${NameLower}_editor_shared_files.cmake b/Templates/CustomTool/Template/Code/${NameLower}_editor_shared_files.cmake new file mode 100644 index 0000000000..2d4ceae97d --- /dev/null +++ b/Templates/CustomTool/Template/Code/${NameLower}_editor_shared_files.cmake @@ -0,0 +1,11 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +set(FILES + Source/${Name}EditorModule.cpp +) diff --git a/Templates/CustomTool/Template/Code/${NameLower}_editor_tests_files.cmake b/Templates/CustomTool/Template/Code/${NameLower}_editor_tests_files.cmake new file mode 100644 index 0000000000..ff45c2fc1c --- /dev/null +++ b/Templates/CustomTool/Template/Code/${NameLower}_editor_tests_files.cmake @@ -0,0 +1,11 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +set(FILES + Tests/${Name}EditorTest.cpp +) diff --git a/Templates/CustomTool/Template/Code/${NameLower}_files.cmake b/Templates/CustomTool/Template/Code/${NameLower}_files.cmake new file mode 100644 index 0000000000..b7d6d37bdf --- /dev/null +++ b/Templates/CustomTool/Template/Code/${NameLower}_files.cmake @@ -0,0 +1,14 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +set(FILES + Include/${Name}/${Name}Bus.h + Source/${Name}ModuleInterface.h + Source/${Name}SystemComponent.cpp + Source/${Name}SystemComponent.h +) diff --git a/Templates/CustomTool/Template/Code/${NameLower}_shared_files.cmake b/Templates/CustomTool/Template/Code/${NameLower}_shared_files.cmake new file mode 100644 index 0000000000..b85916191c --- /dev/null +++ b/Templates/CustomTool/Template/Code/${NameLower}_shared_files.cmake @@ -0,0 +1,11 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +set(FILES + Source/${Name}Module.cpp +) diff --git a/Templates/CustomTool/Template/Code/${NameLower}_tests_files.cmake b/Templates/CustomTool/Template/Code/${NameLower}_tests_files.cmake new file mode 100644 index 0000000000..adcfe2645f --- /dev/null +++ b/Templates/CustomTool/Template/Code/${NameLower}_tests_files.cmake @@ -0,0 +1,11 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +set(FILES + Tests/${Name}Test.cpp +) diff --git a/Templates/CustomTool/Template/Code/CMakeLists.txt b/Templates/CustomTool/Template/Code/CMakeLists.txt new file mode 100644 index 0000000000..f5cda477fc --- /dev/null +++ b/Templates/CustomTool/Template/Code/CMakeLists.txt @@ -0,0 +1,168 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +# Currently we are in the Code folder: ${CMAKE_CURRENT_LIST_DIR} +# Get the platform specific folder ${pal_dir} for the current folder: ${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME} +# Note: ly_get_list_relative_pal_filename will take care of the details for us, as this may be a restricted platform +# in which case it will see if that platform is present here or in the restricted folder. +# i.e. It could here in our gem : Gems/${Name}/Code/Platform/ or +# //Gems/${Name}/Code +ly_get_list_relative_pal_filename(pal_dir ${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME} ${o3de_gem_restricted_path} ${o3de_gem_path} ${o3de_gem_name}) + +# Now that we have the platform abstraction layer (PAL) folder for this folder, thats where we will find the +# traits for this platform. Traits for a platform are defines for things like whether or not something in this gem +# is supported by this platform. +include(${pal_dir}/PAL_${PAL_PLATFORM_NAME_LOWERCASE}.cmake) + +# Add the ${Name}.Static target +# Note: We include the common files and the platform specific files which are set in ${NameLower}_common_files.cmake +# and in ${pal_dir}/${NameLower}_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake +ly_add_target( + NAME ${Name}.Static STATIC + NAMESPACE Gem + FILES_CMAKE + ${NameLower}_files.cmake + ${pal_dir}/${NameLower}_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake + INCLUDE_DIRECTORIES + PUBLIC + Include + PRIVATE + Source + BUILD_DEPENDENCIES + PUBLIC + AZ::AzCore + AZ::AzFramework +) + +# Here add ${Name} target, it depends on the ${Name}.Static +ly_add_target( + NAME ${Name} ${PAL_TRAIT_MONOLITHIC_DRIVEN_MODULE_TYPE} + NAMESPACE Gem + FILES_CMAKE + ${NameLower}_shared_files.cmake + ${pal_dir}/${NameLower}_shared_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake + INCLUDE_DIRECTORIES + PUBLIC + Include + PRIVATE + Source + BUILD_DEPENDENCIES + PRIVATE + Gem::${Name}.Static +) + +# By default, we will specify that the above target ${Name} would be used by +# Client and Server type targets when this gem is enabled. If you don't want it +# active in Clients or Servers by default, delete one of both of the following lines: +ly_create_alias(NAME ${Name}.Clients NAMESPACE Gem TARGETS Gem::${Name}) +ly_create_alias(NAME ${Name}.Servers NAMESPACE Gem TARGETS Gem::${Name}) + +# If we are on a host platform, we want to add the host tools targets like the ${Name}.Editor target which +# will also depend on ${Name}.Static +if(PAL_TRAIT_BUILD_HOST_TOOLS) + ly_add_target( + NAME ${Name}.Editor.Static STATIC + NAMESPACE Gem + AUTOMOC + AUTORCC + FILES_CMAKE + ${NameLower}_editor_files.cmake + INCLUDE_DIRECTORIES + PRIVATE + Source + PUBLIC + Include + BUILD_DEPENDENCIES + PUBLIC + AZ::AzToolsFramework + Gem::${Name}.Static + ) + + ly_add_target( + NAME ${Name}.Editor GEM_MODULE + NAMESPACE Gem + AUTOMOC + FILES_CMAKE + ${NameLower}_editor_shared_files.cmake + INCLUDE_DIRECTORIES + PRIVATE + Source + PUBLIC + Include + BUILD_DEPENDENCIES + PUBLIC + Gem::${Name}.Editor.Static + ) + + # By default, we will specify that the above target ${Name} would be used by + # Tool and Builder type targets when this gem is enabled. If you don't want it + # active in Tools or Builders by default, delete one of both of the following lines: + ly_create_alias(NAME ${Name}.Tools NAMESPACE Gem TARGETS Gem::${Name}.Editor) + ly_create_alias(NAME ${Name}.Builders NAMESPACE Gem TARGETS Gem::${Name}.Editor) + + +endif() + +################################################################################ +# Tests +################################################################################ +# See if globally, tests are supported +if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) + # We globally support tests, see if we support tests on this platform for ${Name}.Static + if(PAL_TRAIT_${NameUpper}_TEST_SUPPORTED) + # We support ${Name}.Tests on this platform, add ${Name}.Tests target which depends on ${Name}.Static + ly_add_target( + NAME ${Name}.Tests ${PAL_TRAIT_TEST_TARGET_TYPE} + NAMESPACE Gem + FILES_CMAKE + ${NameLower}_files.cmake + ${NameLower}_tests_files.cmake + INCLUDE_DIRECTORIES + PRIVATE + Tests + Source + BUILD_DEPENDENCIES + PRIVATE + AZ::AzTest + AZ::AzFramework + Gem::${Name}.Static + ) + + # Add ${Name}.Tests to googletest + ly_add_googletest( + NAME Gem::${Name}.Tests + ) + endif() + + # If we are a host platform we want to add tools test like editor tests here + if(PAL_TRAIT_BUILD_HOST_TOOLS) + # We are a host platform, see if Editor tests are supported on this platform + if(PAL_TRAIT_${NameUpper}_EDITOR_TEST_SUPPORTED) + # We support ${Name}.Editor.Tests on this platform, add ${Name}.Editor.Tests target which depends on ${Name}.Editor + ly_add_target( + NAME ${Name}.Editor.Tests ${PAL_TRAIT_TEST_TARGET_TYPE} + NAMESPACE Gem + FILES_CMAKE + ${NameLower}_editor_tests_files.cmake + INCLUDE_DIRECTORIES + PRIVATE + Tests + Source + BUILD_DEPENDENCIES + PRIVATE + AZ::AzTest + Gem::${Name}.Editor + ) + + # Add ${Name}.Editor.Tests to googletest + ly_add_googletest( + NAME Gem::${Name}.Editor.Tests + ) + endif() + endif() +endif() diff --git a/Templates/CustomTool/Template/Code/Include/${Name}/${Name}Bus.h b/Templates/CustomTool/Template/Code/Include/${Name}/${Name}Bus.h new file mode 100644 index 0000000000..d09bb2b009 --- /dev/null +++ b/Templates/CustomTool/Template/Code/Include/${Name}/${Name}Bus.h @@ -0,0 +1,40 @@ +// {BEGIN_LICENSE} +/* + * 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 + * + */ +// {END_LICENSE} + +#pragma once + +#include +#include + +namespace ${SanitizedCppName} +{ + class ${SanitizedCppName}Requests + { + public: + AZ_RTTI(${SanitizedCppName}Requests, "{${Random_Uuid}}"); + virtual ~${SanitizedCppName}Requests() = default; + // Put your public methods here + }; + + class ${SanitizedCppName}BusTraits + : public AZ::EBusTraits + { + public: + ////////////////////////////////////////////////////////////////////////// + // EBusTraits overrides + static constexpr AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; + static constexpr AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; + ////////////////////////////////////////////////////////////////////////// + }; + + using ${SanitizedCppName}RequestBus = AZ::EBus<${SanitizedCppName}Requests, ${SanitizedCppName}BusTraits>; + using ${SanitizedCppName}Interface = AZ::Interface<${SanitizedCppName}Requests>; + +} // namespace ${SanitizedCppName} diff --git a/Templates/CustomTool/Template/Code/Platform/Android/${NameLower}_android_files.cmake b/Templates/CustomTool/Template/Code/Platform/Android/${NameLower}_android_files.cmake new file mode 100644 index 0000000000..5b6da14a20 --- /dev/null +++ b/Templates/CustomTool/Template/Code/Platform/Android/${NameLower}_android_files.cmake @@ -0,0 +1,15 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +# Platform specific files for Android +# i.e. ../Source/Android/${Name}Android.cpp +# ../Source/Android/${Name}Android.h +# ../Include/Android/${Name}Android.h + +set(FILES +) diff --git a/Templates/CustomTool/Template/Code/Platform/Android/${NameLower}_shared_android_files.cmake b/Templates/CustomTool/Template/Code/Platform/Android/${NameLower}_shared_android_files.cmake new file mode 100644 index 0000000000..5b6da14a20 --- /dev/null +++ b/Templates/CustomTool/Template/Code/Platform/Android/${NameLower}_shared_android_files.cmake @@ -0,0 +1,15 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +# Platform specific files for Android +# i.e. ../Source/Android/${Name}Android.cpp +# ../Source/Android/${Name}Android.h +# ../Include/Android/${Name}Android.h + +set(FILES +) diff --git a/Templates/CustomTool/Template/Code/Platform/Android/PAL_android.cmake b/Templates/CustomTool/Template/Code/Platform/Android/PAL_android.cmake new file mode 100644 index 0000000000..49dfe71f53 --- /dev/null +++ b/Templates/CustomTool/Template/Code/Platform/Android/PAL_android.cmake @@ -0,0 +1,11 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +set(PAL_TRAIT_${NameUpper}_SUPPORTED TRUE) +set(PAL_TRAIT_${NameUpper}_TEST_SUPPORTED TRUE) +set(PAL_TRAIT_${NameUpper}_EDITOR_TEST_SUPPORTED TRUE) diff --git a/Templates/CustomTool/Template/Code/Platform/Linux/${NameLower}_linux_files.cmake b/Templates/CustomTool/Template/Code/Platform/Linux/${NameLower}_linux_files.cmake new file mode 100644 index 0000000000..2f58a2e6f5 --- /dev/null +++ b/Templates/CustomTool/Template/Code/Platform/Linux/${NameLower}_linux_files.cmake @@ -0,0 +1,15 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +# Platform specific files for Linux +# i.e. ../Source/Linux/${Name}Linux.cpp +# ../Source/Linux/${Name}Linux.h +# ../Include/Linux/${Name}Linux.h + +set(FILES +) diff --git a/Templates/CustomTool/Template/Code/Platform/Linux/${NameLower}_shared_linux_files.cmake b/Templates/CustomTool/Template/Code/Platform/Linux/${NameLower}_shared_linux_files.cmake new file mode 100644 index 0000000000..2f58a2e6f5 --- /dev/null +++ b/Templates/CustomTool/Template/Code/Platform/Linux/${NameLower}_shared_linux_files.cmake @@ -0,0 +1,15 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +# Platform specific files for Linux +# i.e. ../Source/Linux/${Name}Linux.cpp +# ../Source/Linux/${Name}Linux.h +# ../Include/Linux/${Name}Linux.h + +set(FILES +) diff --git a/Templates/CustomTool/Template/Code/Platform/Linux/PAL_linux.cmake b/Templates/CustomTool/Template/Code/Platform/Linux/PAL_linux.cmake new file mode 100644 index 0000000000..0abcd887e8 --- /dev/null +++ b/Templates/CustomTool/Template/Code/Platform/Linux/PAL_linux.cmake @@ -0,0 +1,11 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +set(PAL_TRAIT_${NameUpper}_SUPPORTED TRUE) +set(PAL_TRAIT_${NameUpper}_TEST_SUPPORTED TRUE) +set(PAL_TRAIT_${NameUpper}_EDITOR_TEST_SUPPORTED TRUE) \ No newline at end of file diff --git a/Templates/CustomTool/Template/Code/Platform/Mac/${NameLower}_mac_files.cmake b/Templates/CustomTool/Template/Code/Platform/Mac/${NameLower}_mac_files.cmake new file mode 100644 index 0000000000..1cf737a2f1 --- /dev/null +++ b/Templates/CustomTool/Template/Code/Platform/Mac/${NameLower}_mac_files.cmake @@ -0,0 +1,15 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +# Platform specific files for Mac +# i.e. ../Source/Mac/${Name}Mac.cpp +# ../Source/Mac/${Name}Mac.h +# ../Include/Mac/${Name}Mac.h + +set(FILES +) diff --git a/Templates/CustomTool/Template/Code/Platform/Mac/${NameLower}_shared_mac_files.cmake b/Templates/CustomTool/Template/Code/Platform/Mac/${NameLower}_shared_mac_files.cmake new file mode 100644 index 0000000000..1cf737a2f1 --- /dev/null +++ b/Templates/CustomTool/Template/Code/Platform/Mac/${NameLower}_shared_mac_files.cmake @@ -0,0 +1,15 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +# Platform specific files for Mac +# i.e. ../Source/Mac/${Name}Mac.cpp +# ../Source/Mac/${Name}Mac.h +# ../Include/Mac/${Name}Mac.h + +set(FILES +) diff --git a/Templates/CustomTool/Template/Code/Platform/Mac/PAL_mac.cmake b/Templates/CustomTool/Template/Code/Platform/Mac/PAL_mac.cmake new file mode 100644 index 0000000000..0abcd887e8 --- /dev/null +++ b/Templates/CustomTool/Template/Code/Platform/Mac/PAL_mac.cmake @@ -0,0 +1,11 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +set(PAL_TRAIT_${NameUpper}_SUPPORTED TRUE) +set(PAL_TRAIT_${NameUpper}_TEST_SUPPORTED TRUE) +set(PAL_TRAIT_${NameUpper}_EDITOR_TEST_SUPPORTED TRUE) \ No newline at end of file diff --git a/Templates/CustomTool/Template/Code/Platform/Windows/${NameLower}_shared_windows_files.cmake b/Templates/CustomTool/Template/Code/Platform/Windows/${NameLower}_shared_windows_files.cmake new file mode 100644 index 0000000000..712aad1207 --- /dev/null +++ b/Templates/CustomTool/Template/Code/Platform/Windows/${NameLower}_shared_windows_files.cmake @@ -0,0 +1,15 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +# Platform specific files for Windows +# i.e. ../Source/Windows/${Name}Windows.cpp +# ../Source/Windows/${Name}Windows.h +# ../Include/Windows/${Name}Windows.h + +set(FILES +) diff --git a/Templates/CustomTool/Template/Code/Platform/Windows/${NameLower}_windows_files.cmake b/Templates/CustomTool/Template/Code/Platform/Windows/${NameLower}_windows_files.cmake new file mode 100644 index 0000000000..712aad1207 --- /dev/null +++ b/Templates/CustomTool/Template/Code/Platform/Windows/${NameLower}_windows_files.cmake @@ -0,0 +1,15 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +# Platform specific files for Windows +# i.e. ../Source/Windows/${Name}Windows.cpp +# ../Source/Windows/${Name}Windows.h +# ../Include/Windows/${Name}Windows.h + +set(FILES +) diff --git a/Templates/CustomTool/Template/Code/Platform/Windows/PAL_windows.cmake b/Templates/CustomTool/Template/Code/Platform/Windows/PAL_windows.cmake new file mode 100644 index 0000000000..0abcd887e8 --- /dev/null +++ b/Templates/CustomTool/Template/Code/Platform/Windows/PAL_windows.cmake @@ -0,0 +1,11 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +set(PAL_TRAIT_${NameUpper}_SUPPORTED TRUE) +set(PAL_TRAIT_${NameUpper}_TEST_SUPPORTED TRUE) +set(PAL_TRAIT_${NameUpper}_EDITOR_TEST_SUPPORTED TRUE) \ No newline at end of file diff --git a/Templates/CustomTool/Template/Code/Platform/iOS/${NameLower}_ios_files.cmake b/Templates/CustomTool/Template/Code/Platform/iOS/${NameLower}_ios_files.cmake new file mode 100644 index 0000000000..61efde11c2 --- /dev/null +++ b/Templates/CustomTool/Template/Code/Platform/iOS/${NameLower}_ios_files.cmake @@ -0,0 +1,15 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +# Platform specific files for iOS +# i.e. ../Source/iOS/${Name}iOS.cpp +# ../Source/iOS/${Name}iOS.h +# ../Include/iOS/${Name}iOS.h + +set(FILES +) diff --git a/Templates/CustomTool/Template/Code/Platform/iOS/${NameLower}_shared_ios_files.cmake b/Templates/CustomTool/Template/Code/Platform/iOS/${NameLower}_shared_ios_files.cmake new file mode 100644 index 0000000000..61efde11c2 --- /dev/null +++ b/Templates/CustomTool/Template/Code/Platform/iOS/${NameLower}_shared_ios_files.cmake @@ -0,0 +1,15 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +# Platform specific files for iOS +# i.e. ../Source/iOS/${Name}iOS.cpp +# ../Source/iOS/${Name}iOS.h +# ../Include/iOS/${Name}iOS.h + +set(FILES +) diff --git a/Templates/CustomTool/Template/Code/Platform/iOS/PAL_ios.cmake b/Templates/CustomTool/Template/Code/Platform/iOS/PAL_ios.cmake new file mode 100644 index 0000000000..0abcd887e8 --- /dev/null +++ b/Templates/CustomTool/Template/Code/Platform/iOS/PAL_ios.cmake @@ -0,0 +1,11 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +set(PAL_TRAIT_${NameUpper}_SUPPORTED TRUE) +set(PAL_TRAIT_${NameUpper}_TEST_SUPPORTED TRUE) +set(PAL_TRAIT_${NameUpper}_EDITOR_TEST_SUPPORTED TRUE) \ No newline at end of file diff --git a/Templates/CustomTool/Template/Code/Source/${Name}.qrc b/Templates/CustomTool/Template/Code/Source/${Name}.qrc new file mode 100644 index 0000000000..90d7695b88 --- /dev/null +++ b/Templates/CustomTool/Template/Code/Source/${Name}.qrc @@ -0,0 +1,5 @@ + + + toolbar_icon.svg + + diff --git a/Templates/CustomTool/Template/Code/Source/${Name}EditorModule.cpp b/Templates/CustomTool/Template/Code/Source/${Name}EditorModule.cpp new file mode 100644 index 0000000000..0027af011a --- /dev/null +++ b/Templates/CustomTool/Template/Code/Source/${Name}EditorModule.cpp @@ -0,0 +1,55 @@ +// {BEGIN_LICENSE} +/* + * 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 + * + */ +// {END_LICENSE} + +#include <${Name}ModuleInterface.h> +#include <${Name}EditorSystemComponent.h> + +void Init${SanitizedCppName}Resources() +{ + // We must register our Qt resources (.qrc file) since this is being loaded from a separate module (gem) + Q_INIT_RESOURCE(${SanitizedCppName}); +} + +namespace ${SanitizedCppName} +{ + class ${SanitizedCppName}EditorModule + : public ${SanitizedCppName}ModuleInterface + { + public: + AZ_RTTI(${SanitizedCppName}EditorModule, "${ModuleClassId}", ${SanitizedCppName}ModuleInterface); + AZ_CLASS_ALLOCATOR(${SanitizedCppName}EditorModule, AZ::SystemAllocator, 0); + + ${SanitizedCppName}EditorModule() + { + Init${SanitizedCppName}Resources(); + + // Push results of [MyComponent]::CreateDescriptor() into m_descriptors here. + // Add ALL components descriptors associated with this gem to m_descriptors. + // This will associate the AzTypeInfo information for the components with the the SerializeContext, BehaviorContext and EditContext. + // This happens through the [MyComponent]::Reflect() function. + m_descriptors.insert(m_descriptors.end(), { + ${SanitizedCppName}EditorSystemComponent::CreateDescriptor(), + }); + } + + /** + * Add required SystemComponents to the SystemEntity. + * Non-SystemComponents should not be added here + */ + AZ::ComponentTypeList GetRequiredSystemComponents() const override + { + return AZ::ComponentTypeList { + azrtti_typeid<${SanitizedCppName}EditorSystemComponent>(), + }; + } + }; +}// namespace ${SanitizedCppName} + +AZ_DECLARE_MODULE_CLASS(Gem_${SanitizedCppName}, ${SanitizedCppName}::${SanitizedCppName}EditorModule) diff --git a/Templates/CustomTool/Template/Code/Source/${Name}EditorSystemComponent.cpp b/Templates/CustomTool/Template/Code/Source/${Name}EditorSystemComponent.cpp new file mode 100644 index 0000000000..f12fa04929 --- /dev/null +++ b/Templates/CustomTool/Template/Code/Source/${Name}EditorSystemComponent.cpp @@ -0,0 +1,78 @@ +// {BEGIN_LICENSE} +/* + * 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 + * + */ +// {END_LICENSE} + +#include + +#include + +#include <${Name}Widget.h> +#include <${Name}EditorSystemComponent.h> + +namespace ${SanitizedCppName} +{ + void ${SanitizedCppName}EditorSystemComponent::Reflect(AZ::ReflectContext* context) + { + if (auto serializeContext = azrtti_cast(context)) + { + serializeContext->Class<${SanitizedCppName}EditorSystemComponent, ${SanitizedCppName}SystemComponent>() + ->Version(0); + } + } + + ${SanitizedCppName}EditorSystemComponent::${SanitizedCppName}EditorSystemComponent() = default; + + ${SanitizedCppName}EditorSystemComponent::~${SanitizedCppName}EditorSystemComponent() = default; + + void ${SanitizedCppName}EditorSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) + { + BaseSystemComponent::GetProvidedServices(provided); + provided.push_back(AZ_CRC_CE("${SanitizedCppName}EditorService")); + } + + void ${SanitizedCppName}EditorSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) + { + BaseSystemComponent::GetIncompatibleServices(incompatible); + incompatible.push_back(AZ_CRC_CE("${SanitizedCppName}EditorService")); + } + + void ${SanitizedCppName}EditorSystemComponent::GetRequiredServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& required) + { + BaseSystemComponent::GetRequiredServices(required); + } + + void ${SanitizedCppName}EditorSystemComponent::GetDependentServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& dependent) + { + BaseSystemComponent::GetDependentServices(dependent); + } + + void ${SanitizedCppName}EditorSystemComponent::Activate() + { + ${SanitizedCppName}SystemComponent::Activate(); + AzToolsFramework::EditorEvents::Bus::Handler::BusConnect(); + } + + void ${SanitizedCppName}EditorSystemComponent::Deactivate() + { + AzToolsFramework::EditorEvents::Bus::Handler::BusDisconnect(); + ${SanitizedCppName}SystemComponent::Deactivate(); + } + + void ${SanitizedCppName}EditorSystemComponent::NotifyRegisterViews() + { + AzToolsFramework::ViewPaneOptions options; + options.paneRect = QRect(100, 100, 500, 400); + options.showOnToolsToolbar = true; + options.toolbarIcon = ":/${Name}/toolbar_icon.svg"; + + // Register our custom widget as a dockable tool with the Editor + AzToolsFramework::RegisterViewPane<${SanitizedCppName}Widget>("${Name}", "Tools", options); + } + +} // namespace ${SanitizedCppName} diff --git a/Templates/CustomTool/Template/Code/Source/${Name}EditorSystemComponent.h b/Templates/CustomTool/Template/Code/Source/${Name}EditorSystemComponent.h new file mode 100644 index 0000000000..bbeac97da3 --- /dev/null +++ b/Templates/CustomTool/Template/Code/Source/${Name}EditorSystemComponent.h @@ -0,0 +1,45 @@ +// {BEGIN_LICENSE} +/* + * 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 + * + */ +// {END_LICENSE} + +#pragma once + +#include <${Name}SystemComponent.h> + +#include + +namespace ${SanitizedCppName} +{ + /// System component for ${SanitizedCppName} editor + class ${SanitizedCppName}EditorSystemComponent + : public ${SanitizedCppName}SystemComponent + , private AzToolsFramework::EditorEvents::Bus::Handler + { + using BaseSystemComponent = ${SanitizedCppName}SystemComponent; + public: + AZ_COMPONENT(${SanitizedCppName}EditorSystemComponent, "${EditorSysCompClassId}", BaseSystemComponent); + static void Reflect(AZ::ReflectContext* context); + + ${SanitizedCppName}EditorSystemComponent(); + ~${SanitizedCppName}EditorSystemComponent(); + + private: + static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided); + static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible); + static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required); + static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent); + + // AZ::Component + void Activate() override; + void Deactivate() override; + + // AzToolsFramework::EditorEventsBus overrides ... + void NotifyRegisterViews() override; + }; +} // namespace ${SanitizedCppName} diff --git a/Templates/CustomTool/Template/Code/Source/${Name}Module.cpp b/Templates/CustomTool/Template/Code/Source/${Name}Module.cpp new file mode 100644 index 0000000000..0a6e8bde3c --- /dev/null +++ b/Templates/CustomTool/Template/Code/Source/${Name}Module.cpp @@ -0,0 +1,25 @@ +// {BEGIN_LICENSE} +/* + * 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 + * + */ +// {END_LICENSE} + +#include <${Name}ModuleInterface.h> +#include <${Name}SystemComponent.h> + +namespace ${SanitizedCppName} +{ + class ${SanitizedCppName}Module + : public ${SanitizedCppName}ModuleInterface + { + public: + AZ_RTTI(${SanitizedCppName}Module, "${ModuleClassId}", ${SanitizedCppName}ModuleInterface); + AZ_CLASS_ALLOCATOR(${SanitizedCppName}Module, AZ::SystemAllocator, 0); + }; +}// namespace ${SanitizedCppName} + +AZ_DECLARE_MODULE_CLASS(Gem_${SanitizedCppName}, ${SanitizedCppName}::${SanitizedCppName}Module) diff --git a/Templates/CustomTool/Template/Code/Source/${Name}ModuleInterface.h b/Templates/CustomTool/Template/Code/Source/${Name}ModuleInterface.h new file mode 100644 index 0000000000..925632491a --- /dev/null +++ b/Templates/CustomTool/Template/Code/Source/${Name}ModuleInterface.h @@ -0,0 +1,45 @@ +// {BEGIN_LICENSE} +/* + * 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 + * + */ +// {END_LICENSE} + +#include +#include +#include <${Name}SystemComponent.h> + +namespace ${SanitizedCppName} +{ + class ${SanitizedCppName}ModuleInterface + : public AZ::Module + { + public: + AZ_RTTI(${SanitizedCppName}ModuleInterface, "{${Random_Uuid}}", AZ::Module); + AZ_CLASS_ALLOCATOR(${SanitizedCppName}ModuleInterface, AZ::SystemAllocator, 0); + + ${SanitizedCppName}ModuleInterface() + { + // Push results of [MyComponent]::CreateDescriptor() into m_descriptors here. + // Add ALL components descriptors associated with this gem to m_descriptors. + // This will associate the AzTypeInfo information for the components with the the SerializeContext, BehaviorContext and EditContext. + // This happens through the [MyComponent]::Reflect() function. + m_descriptors.insert(m_descriptors.end(), { + ${SanitizedCppName}SystemComponent::CreateDescriptor(), + }); + } + + /** + * Add required SystemComponents to the SystemEntity. + */ + AZ::ComponentTypeList GetRequiredSystemComponents() const override + { + return AZ::ComponentTypeList{ + azrtti_typeid<${SanitizedCppName}SystemComponent>(), + }; + } + }; +}// namespace ${SanitizedCppName} diff --git a/Templates/CustomTool/Template/Code/Source/${Name}SystemComponent.cpp b/Templates/CustomTool/Template/Code/Source/${Name}SystemComponent.cpp new file mode 100644 index 0000000000..cb4d58418e --- /dev/null +++ b/Templates/CustomTool/Template/Code/Source/${Name}SystemComponent.cpp @@ -0,0 +1,92 @@ +// {BEGIN_LICENSE} +/* + * 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 + * + */ +// {END_LICENSE} + +#include <${Name}SystemComponent.h> + +#include +#include +#include + +namespace ${SanitizedCppName} +{ + void ${SanitizedCppName}SystemComponent::Reflect(AZ::ReflectContext* context) + { + if (AZ::SerializeContext* serialize = azrtti_cast(context)) + { + serialize->Class<${SanitizedCppName}SystemComponent, AZ::Component>() + ->Version(0) + ; + + if (AZ::EditContext* ec = serialize->GetEditContext()) + { + ec->Class<${SanitizedCppName}SystemComponent>("${SanitizedCppName}", "[Description of functionality provided by this System Component]") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("System")) + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ; + } + } + } + + void ${SanitizedCppName}SystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) + { + provided.push_back(AZ_CRC_CE("${SanitizedCppName}Service")); + } + + void ${SanitizedCppName}SystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) + { + incompatible.push_back(AZ_CRC_CE("${SanitizedCppName}Service")); + } + + void ${SanitizedCppName}SystemComponent::GetRequiredServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& required) + { + } + + void ${SanitizedCppName}SystemComponent::GetDependentServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& dependent) + { + } + + ${SanitizedCppName}SystemComponent::${SanitizedCppName}SystemComponent() + { + if (${SanitizedCppName}Interface::Get() == nullptr) + { + ${SanitizedCppName}Interface::Register(this); + } + } + + ${SanitizedCppName}SystemComponent::~${SanitizedCppName}SystemComponent() + { + if (${SanitizedCppName}Interface::Get() == this) + { + ${SanitizedCppName}Interface::Unregister(this); + } + } + + void ${SanitizedCppName}SystemComponent::Init() + { + } + + void ${SanitizedCppName}SystemComponent::Activate() + { + ${SanitizedCppName}RequestBus::Handler::BusConnect(); + AZ::TickBus::Handler::BusConnect(); + } + + void ${SanitizedCppName}SystemComponent::Deactivate() + { + AZ::TickBus::Handler::BusDisconnect(); + ${SanitizedCppName}RequestBus::Handler::BusDisconnect(); + } + + void ${SanitizedCppName}SystemComponent::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time) + { + } + +} // namespace ${SanitizedCppName} diff --git a/Templates/CustomTool/Template/Code/Source/${Name}SystemComponent.h b/Templates/CustomTool/Template/Code/Source/${Name}SystemComponent.h new file mode 100644 index 0000000000..5495d18e48 --- /dev/null +++ b/Templates/CustomTool/Template/Code/Source/${Name}SystemComponent.h @@ -0,0 +1,56 @@ +// {BEGIN_LICENSE} +/* + * 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 + * + */ +// {END_LICENSE} + +#pragma once + +#include +#include +#include <${Name}/${Name}Bus.h> + +namespace ${SanitizedCppName} +{ + class ${SanitizedCppName}SystemComponent + : public AZ::Component + , protected ${SanitizedCppName}RequestBus::Handler + , public AZ::TickBus::Handler + { + public: + AZ_COMPONENT(${SanitizedCppName}SystemComponent, "${SysCompClassId}"); + + static void Reflect(AZ::ReflectContext* context); + + static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided); + static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible); + static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required); + static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent); + + ${SanitizedCppName}SystemComponent(); + ~${SanitizedCppName}SystemComponent(); + + protected: + //////////////////////////////////////////////////////////////////////// + // ${SanitizedCppName}RequestBus interface implementation + + //////////////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////////////// + // AZ::Component interface implementation + void Init() override; + void Activate() override; + void Deactivate() override; + //////////////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////////////// + // AZTickBus interface implementation + void OnTick(float deltaTime, AZ::ScriptTimePoint time) override; + //////////////////////////////////////////////////////////////////////// + }; + +} // namespace ${SanitizedCppName} diff --git a/Templates/CustomTool/Template/Code/Source/${Name}Widget.cpp b/Templates/CustomTool/Template/Code/Source/${Name}Widget.cpp new file mode 100644 index 0000000000..bd6dd6c86a --- /dev/null +++ b/Templates/CustomTool/Template/Code/Source/${Name}Widget.cpp @@ -0,0 +1,44 @@ +// {BEGIN_LICENSE} +/* + * 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 + * + */ +// {END_LICENSE} + +#include + +#include +#include + +#include <${Name}Widget.h> + +namespace ${SanitizedCppName} +{ + ${SanitizedCppName}Widget::${SanitizedCppName}Widget(QWidget* parent) + : QWidget(parent) + { + setWindowTitle(QObject::tr("${Name}")); + + QVBoxLayout* mainLayout = new QVBoxLayout(this); + + QLabel* introLabel = new QLabel(QObject::tr("Put your cool stuff here!"), this); + mainLayout->addWidget(introLabel, 0, Qt::AlignCenter); + + QString helpText = QString( + "For help getting started, visit the UI Development documentation
or come ask a question in the sig-ui-ux channel on Discord"); + + QLabel* helpLabel = new QLabel(this); + helpLabel->setTextFormat(Qt::RichText); + helpLabel->setText(helpText); + helpLabel->setOpenExternalLinks(true); + + mainLayout->addWidget(helpLabel, 0, Qt::AlignCenter); + + setLayout(mainLayout); + } +} + +#include diff --git a/Templates/CustomTool/Template/Code/Source/${Name}Widget.h b/Templates/CustomTool/Template/Code/Source/${Name}Widget.h new file mode 100644 index 0000000000..4d0c86d043 --- /dev/null +++ b/Templates/CustomTool/Template/Code/Source/${Name}Widget.h @@ -0,0 +1,28 @@ +// {BEGIN_LICENSE} +/* + * 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 + * + */ +// {END_LICENSE} + +#pragma once + +#if !defined(Q_MOC_RUN) +#include + +#include +#endif + +namespace ${SanitizedCppName} +{ + class ${SanitizedCppName}Widget + : public QWidget + { + Q_OBJECT + public: + explicit ${SanitizedCppName}Widget(QWidget* parent = nullptr); + }; +} diff --git a/Templates/CustomTool/Template/Code/Source/toolbar_icon.svg b/Templates/CustomTool/Template/Code/Source/toolbar_icon.svg new file mode 100644 index 0000000000..59de66961c --- /dev/null +++ b/Templates/CustomTool/Template/Code/Source/toolbar_icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Templates/CustomTool/Template/Code/Tests/${Name}EditorTest.cpp b/Templates/CustomTool/Template/Code/Tests/${Name}EditorTest.cpp new file mode 100644 index 0000000000..9b84575fa0 --- /dev/null +++ b/Templates/CustomTool/Template/Code/Tests/${Name}EditorTest.cpp @@ -0,0 +1,13 @@ +// {BEGIN_LICENSE} +/* + * 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 + * + */ +// {END_LICENSE} + +#include + +AZ_UNIT_TEST_HOOK(DEFAULT_UNIT_TEST_ENV); diff --git a/Templates/CustomTool/Template/Code/Tests/${Name}Test.cpp b/Templates/CustomTool/Template/Code/Tests/${Name}Test.cpp new file mode 100644 index 0000000000..9b84575fa0 --- /dev/null +++ b/Templates/CustomTool/Template/Code/Tests/${Name}Test.cpp @@ -0,0 +1,13 @@ +// {BEGIN_LICENSE} +/* + * 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 + * + */ +// {END_LICENSE} + +#include + +AZ_UNIT_TEST_HOOK(DEFAULT_UNIT_TEST_ENV); diff --git a/Templates/CustomTool/Template/Platform/Android/android_gem.cmake b/Templates/CustomTool/Template/Platform/Android/android_gem.cmake new file mode 100644 index 0000000000..063b3be9ac --- /dev/null +++ b/Templates/CustomTool/Template/Platform/Android/android_gem.cmake @@ -0,0 +1,8 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + diff --git a/Templates/CustomTool/Template/Platform/Android/android_gem.json b/Templates/CustomTool/Template/Platform/Android/android_gem.json new file mode 100644 index 0000000000..23bbb28e66 --- /dev/null +++ b/Templates/CustomTool/Template/Platform/Android/android_gem.json @@ -0,0 +1,3 @@ +{ + "Tags": ["Android"], +} \ No newline at end of file diff --git a/Templates/CustomTool/Template/Platform/Linux/linux_gem.cmake b/Templates/CustomTool/Template/Platform/Linux/linux_gem.cmake new file mode 100644 index 0000000000..063b3be9ac --- /dev/null +++ b/Templates/CustomTool/Template/Platform/Linux/linux_gem.cmake @@ -0,0 +1,8 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + diff --git a/Templates/CustomTool/Template/Platform/Linux/linux_gem.json b/Templates/CustomTool/Template/Platform/Linux/linux_gem.json new file mode 100644 index 0000000000..d08fbf53ba --- /dev/null +++ b/Templates/CustomTool/Template/Platform/Linux/linux_gem.json @@ -0,0 +1,3 @@ +{ + "Tags": ["Linux"] +} \ No newline at end of file diff --git a/Templates/CustomTool/Template/Platform/Mac/mac_gem.cmake b/Templates/CustomTool/Template/Platform/Mac/mac_gem.cmake new file mode 100644 index 0000000000..063b3be9ac --- /dev/null +++ b/Templates/CustomTool/Template/Platform/Mac/mac_gem.cmake @@ -0,0 +1,8 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + diff --git a/Templates/CustomTool/Template/Platform/Mac/mac_gem.json b/Templates/CustomTool/Template/Platform/Mac/mac_gem.json new file mode 100644 index 0000000000..d42b6f8186 --- /dev/null +++ b/Templates/CustomTool/Template/Platform/Mac/mac_gem.json @@ -0,0 +1,3 @@ +{ + "Tags": ["Mac"] +} \ No newline at end of file diff --git a/Templates/CustomTool/Template/Platform/Windows/windows_gem.cmake b/Templates/CustomTool/Template/Platform/Windows/windows_gem.cmake new file mode 100644 index 0000000000..063b3be9ac --- /dev/null +++ b/Templates/CustomTool/Template/Platform/Windows/windows_gem.cmake @@ -0,0 +1,8 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + diff --git a/Templates/CustomTool/Template/Platform/Windows/windows_gem.json b/Templates/CustomTool/Template/Platform/Windows/windows_gem.json new file mode 100644 index 0000000000..a052f1e05a --- /dev/null +++ b/Templates/CustomTool/Template/Platform/Windows/windows_gem.json @@ -0,0 +1,3 @@ +{ + "Tags": ["Windows"] +} \ No newline at end of file diff --git a/Templates/CustomTool/Template/Platform/iOS/ios_gem.cmake b/Templates/CustomTool/Template/Platform/iOS/ios_gem.cmake new file mode 100644 index 0000000000..063b3be9ac --- /dev/null +++ b/Templates/CustomTool/Template/Platform/iOS/ios_gem.cmake @@ -0,0 +1,8 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + diff --git a/Templates/CustomTool/Template/Platform/iOS/ios_gem.json b/Templates/CustomTool/Template/Platform/iOS/ios_gem.json new file mode 100644 index 0000000000..b2dab56d05 --- /dev/null +++ b/Templates/CustomTool/Template/Platform/iOS/ios_gem.json @@ -0,0 +1,3 @@ +{ + "Tags": ["iOS"] +} \ No newline at end of file diff --git a/Templates/CustomTool/Template/gem.json b/Templates/CustomTool/Template/gem.json new file mode 100644 index 0000000000..518d831e0f --- /dev/null +++ b/Templates/CustomTool/Template/gem.json @@ -0,0 +1,17 @@ +{ + "gem_name": "${Name}", + "display_name": "${Name}", + "license": "What license ${Name} uses goes here: i.e. https://opensource.org/licenses/MIT", + "origin": "The primary repo for ${Name} goes here: i.e. http://www.mydomain.com", + "type": "Code", + "summary": "A short description of ${Name}.", + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "${Name}" + ], + "icon_path": "preview.png", + "requirements": "", + "restricted_name": "gems" +} diff --git a/Templates/CustomTool/Template/preview.png b/Templates/CustomTool/Template/preview.png new file mode 100644 index 0000000000..0f393ac886 --- /dev/null +++ b/Templates/CustomTool/Template/preview.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7ac9dd09bde78f389e3725ac49d61eff109857e004840bc0bc3881739df9618d +size 2217 diff --git a/Templates/CustomTool/template.json b/Templates/CustomTool/template.json new file mode 100644 index 0000000000..e3221db106 --- /dev/null +++ b/Templates/CustomTool/template.json @@ -0,0 +1,382 @@ +{ + "template_name": "CustomTool", + "origin": "The primary repo for CustomTool goes here: i.e. http://www.mydomain.com", + "license": "What license CustomTool uses goes here: i.e. https://opensource.org/licenses/MIT", + "display_name": "CustomTool", + "summary": "A gem template for a custom tool in C++ that gets registered with the Editor.", + "canonical_tags": [], + "user_tags": [ + "CustomTool" + ], + "icon_path": "preview.png", + "copyFiles": [ + { + "file": "CMakeLists.txt", + "origin": "CMakeLists.txt", + "isTemplated": false, + "isOptional": false + }, + { + "file": "Code/${NameLower}_editor_files.cmake", + "origin": "Code/${NameLower}_editor_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/${NameLower}_editor_shared_files.cmake", + "origin": "Code/${NameLower}_editor_shared_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/${NameLower}_editor_tests_files.cmake", + "origin": "Code/${NameLower}_editor_tests_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/${NameLower}_files.cmake", + "origin": "Code/${NameLower}_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/${NameLower}_shared_files.cmake", + "origin": "Code/${NameLower}_shared_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/${NameLower}_tests_files.cmake", + "origin": "Code/${NameLower}_tests_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/CMakeLists.txt", + "origin": "Code/CMakeLists.txt", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Include/${Name}/${Name}Bus.h", + "origin": "Code/Include/${Name}/${Name}Bus.h", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Android/${NameLower}_android_files.cmake", + "origin": "Code/Platform/Android/${NameLower}_android_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Android/${NameLower}_shared_android_files.cmake", + "origin": "Code/Platform/Android/${NameLower}_shared_android_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Android/PAL_android.cmake", + "origin": "Code/Platform/Android/PAL_android.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Linux/${NameLower}_linux_files.cmake", + "origin": "Code/Platform/Linux/${NameLower}_linux_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Linux/${NameLower}_shared_linux_files.cmake", + "origin": "Code/Platform/Linux/${NameLower}_shared_linux_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Linux/PAL_linux.cmake", + "origin": "Code/Platform/Linux/PAL_linux.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Mac/${NameLower}_mac_files.cmake", + "origin": "Code/Platform/Mac/${NameLower}_mac_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Mac/${NameLower}_shared_mac_files.cmake", + "origin": "Code/Platform/Mac/${NameLower}_shared_mac_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Mac/PAL_mac.cmake", + "origin": "Code/Platform/Mac/PAL_mac.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Windows/${NameLower}_shared_windows_files.cmake", + "origin": "Code/Platform/Windows/${NameLower}_shared_windows_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Windows/${NameLower}_windows_files.cmake", + "origin": "Code/Platform/Windows/${NameLower}_windows_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Windows/PAL_windows.cmake", + "origin": "Code/Platform/Windows/PAL_windows.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/iOS/${NameLower}_ios_files.cmake", + "origin": "Code/Platform/iOS/${NameLower}_ios_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/iOS/${NameLower}_shared_ios_files.cmake", + "origin": "Code/Platform/iOS/${NameLower}_shared_ios_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/iOS/PAL_ios.cmake", + "origin": "Code/Platform/iOS/PAL_ios.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Source/${Name}.qrc", + "origin": "Code/Source/${Name}.qrc", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Source/${Name}EditorModule.cpp", + "origin": "Code/Source/${Name}EditorModule.cpp", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Source/${Name}EditorSystemComponent.cpp", + "origin": "Code/Source/${Name}EditorSystemComponent.cpp", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Source/${Name}EditorSystemComponent.h", + "origin": "Code/Source/${Name}EditorSystemComponent.h", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Source/${Name}Module.cpp", + "origin": "Code/Source/${Name}Module.cpp", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Source/${Name}ModuleInterface.h", + "origin": "Code/Source/${Name}ModuleInterface.h", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Source/${Name}SystemComponent.cpp", + "origin": "Code/Source/${Name}SystemComponent.cpp", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Source/${Name}SystemComponent.h", + "origin": "Code/Source/${Name}SystemComponent.h", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Source/${Name}Widget.cpp", + "origin": "Code/Source/${Name}Widget.cpp", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Source/${Name}Widget.h", + "origin": "Code/Source/${Name}Widget.h", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Source/toolbar_icon.svg", + "origin": "Code/Source/toolbar_icon.svg", + "isTemplated": false, + "isOptional": false + }, + { + "file": "Code/Tests/${Name}EditorTest.cpp", + "origin": "Code/Tests/${Name}EditorTest.cpp", + "isTemplated": false, + "isOptional": false + }, + { + "file": "Code/Tests/${Name}Test.cpp", + "origin": "Code/Tests/${Name}Test.cpp", + "isTemplated": false, + "isOptional": false + }, + { + "file": "Platform/Android/android_gem.cmake", + "origin": "Platform/Android/android_gem.cmake", + "isTemplated": false, + "isOptional": false + }, + { + "file": "Platform/Android/android_gem.json", + "origin": "Platform/Android/android_gem.json", + "isTemplated": false, + "isOptional": false + }, + { + "file": "Platform/Linux/linux_gem.cmake", + "origin": "Platform/Linux/linux_gem.cmake", + "isTemplated": false, + "isOptional": false + }, + { + "file": "Platform/Linux/linux_gem.json", + "origin": "Platform/Linux/linux_gem.json", + "isTemplated": false, + "isOptional": false + }, + { + "file": "Platform/Mac/mac_gem.cmake", + "origin": "Platform/Mac/mac_gem.cmake", + "isTemplated": false, + "isOptional": false + }, + { + "file": "Platform/Mac/mac_gem.json", + "origin": "Platform/Mac/mac_gem.json", + "isTemplated": false, + "isOptional": false + }, + { + "file": "Platform/Windows/windows_gem.cmake", + "origin": "Platform/Windows/windows_gem.cmake", + "isTemplated": false, + "isOptional": false + }, + { + "file": "Platform/Windows/windows_gem.json", + "origin": "Platform/Windows/windows_gem.json", + "isTemplated": false, + "isOptional": false + }, + { + "file": "Platform/iOS/ios_gem.cmake", + "origin": "Platform/iOS/ios_gem.cmake", + "isTemplated": false, + "isOptional": false + }, + { + "file": "Platform/iOS/ios_gem.json", + "origin": "Platform/iOS/ios_gem.json", + "isTemplated": false, + "isOptional": false + }, + { + "file": "gem.json", + "origin": "gem.json", + "isTemplated": true, + "isOptional": false + }, + { + "file": "preview.png", + "origin": "preview.png", + "isTemplated": false, + "isOptional": false + } + ], + "createDirectories": [ + { + "dir": "Assets", + "origin": "Assets" + }, + { + "dir": "Code", + "origin": "Code" + }, + { + "dir": "Code/Include", + "origin": "Code/Include" + }, + { + "dir": "Code/Include/${Name}", + "origin": "Code/Include/${Name}" + }, + { + "dir": "Code/Platform", + "origin": "Code/Platform" + }, + { + "dir": "Code/Platform/Android", + "origin": "Code/Platform/Android" + }, + { + "dir": "Code/Platform/Linux", + "origin": "Code/Platform/Linux" + }, + { + "dir": "Code/Platform/Mac", + "origin": "Code/Platform/Mac" + }, + { + "dir": "Code/Platform/Windows", + "origin": "Code/Platform/Windows" + }, + { + "dir": "Code/Platform/iOS", + "origin": "Code/Platform/iOS" + }, + { + "dir": "Code/Source", + "origin": "Code/Source" + }, + { + "dir": "Code/Tests", + "origin": "Code/Tests" + }, + { + "dir": "Platform", + "origin": "Platform" + }, + { + "dir": "Platform/Android", + "origin": "Platform/Android" + }, + { + "dir": "Platform/Linux", + "origin": "Platform/Linux" + }, + { + "dir": "Platform/Mac", + "origin": "Platform/Mac" + }, + { + "dir": "Platform/Windows", + "origin": "Platform/Windows" + }, + { + "dir": "Platform/iOS", + "origin": "Platform/iOS" + } + ] +} diff --git a/engine.json b/engine.json index 2b56e255fc..3d522ce175 100644 --- a/engine.json +++ b/engine.json @@ -92,6 +92,7 @@ "templates": [ "Templates/AssetGem", "Templates/DefaultGem", + "Templates/CustomTool", "Templates/DefaultProject", "Templates/MinimalProject" ] From 80dcc34e6fab19c89c6286b60668a81543009f14 Mon Sep 17 00:00:00 2001 From: amzn-phist <52085794+amzn-phist@users.noreply.github.com> Date: Wed, 13 Oct 2021 16:46:30 -0500 Subject: [PATCH 51/65] Add "Registry" folders as scan folders (#4583) * Add "Registry" folders as scan folders For projects, Gems, and Engine, add the "Registry" folder as scan folders. Signed-off-by: amzn-phist <52085794+amzn-phist@users.noreply.github.com> * Change params for adding scan folder Also add trailing newlines to setreg files. Signed-off-by: amzn-phist <52085794+amzn-phist@users.noreply.github.com> * Adjust the priority order for project templates Scan folder orders were way too high, they should be very low to become highest priority order. Signed-off-by: amzn-phist <52085794+amzn-phist@users.noreply.github.com> * Removes change of default assets folder to Assets Need to revert this change so it can be done separately. Renamed the scan folder to 'Project/Assets' to prep for Assets folder change later on. Signed-off-by: amzn-phist <52085794+amzn-phist@users.noreply.github.com> * Update platform configuration test to pass Adjusted expectation of scan folder count from 1 to 2 per Gem for 'Assets' and 'Registry' now. Signed-off-by: amzn-phist <52085794+amzn-phist@users.noreply.github.com> --- .../AzFramework/AzFramework/Gem/GemInfo.h | 4 +++ .../platformconfigurationtests.cpp | 14 +++++---- .../utilities/PlatformConfiguration.cpp | 18 ++++++++++++ Registry/AssetProcessorPlatformConfig.setreg | 7 ++++- .../Registry/assets_scan_folders.setreg | 29 ++++++++++++------- .../Registry/assets_scan_folders.setreg | 29 ++++++++++++------- 6 files changed, 72 insertions(+), 29 deletions(-) diff --git a/Code/Framework/AzFramework/AzFramework/Gem/GemInfo.h b/Code/Framework/AzFramework/AzFramework/Gem/GemInfo.h index 1f300af770..3435d810fd 100644 --- a/Code/Framework/AzFramework/AzFramework/Gem/GemInfo.h +++ b/Code/Framework/AzFramework/AzFramework/Gem/GemInfo.h @@ -29,6 +29,10 @@ namespace AzFramework AZStd::vector m_absoluteSourcePaths; //!< Where the gem's source path folder are located(as an absolute path) static constexpr const char* GetGemAssetFolder() { return "Assets"; } + static constexpr const char* GetGemRegistryFolder() + { + return "Registry"; + } }; //! Returns a list of GemInfo of all the gems that are active for the for the specified game project. diff --git a/Code/Tools/AssetProcessor/native/tests/platformconfiguration/platformconfigurationtests.cpp b/Code/Tools/AssetProcessor/native/tests/platformconfiguration/platformconfigurationtests.cpp index 6f07901e27..69397745f1 100644 --- a/Code/Tools/AssetProcessor/native/tests/platformconfiguration/platformconfigurationtests.cpp +++ b/Code/Tools/AssetProcessor/native/tests/platformconfiguration/platformconfigurationtests.cpp @@ -590,20 +590,22 @@ TEST_F(PlatformConfigurationUnitTests, Test_GemHandling) AssetUtilities::ResetAssetRoot(); - ASSERT_EQ(2, config.GetScanFolderCount()); + ASSERT_EQ(4, config.GetScanFolderCount()); EXPECT_FALSE(config.GetScanFolderAt(0).IsRoot()); EXPECT_TRUE(config.GetScanFolderAt(0).RecurseSubFolders()); // the first one is a game gem, so its order should be above 1 but below 100. EXPECT_GE(config.GetScanFolderAt(0).GetOrder(), 100); EXPECT_EQ(0, config.GetScanFolderAt(0).ScanPath().compare(expectedScanFolder, Qt::CaseInsensitive)); - // for each gem, there are currently 1 scan folder, the gem assets folder, with no output prefix + // for each gem, there are currently 2 scan folders: + // The Gem's 'Assets' folder + // The Gem's 'Registry' folder expectedScanFolder = tempPath.absoluteFilePath("Gems/LmbrCentral/v2/Assets"); - EXPECT_FALSE(config.GetScanFolderAt(1).IsRoot() ); - EXPECT_TRUE(config.GetScanFolderAt(1).RecurseSubFolders()); - EXPECT_GT(config.GetScanFolderAt(1).GetOrder(), config.GetScanFolderAt(0).GetOrder()); - EXPECT_EQ(0, config.GetScanFolderAt(1).ScanPath().compare(expectedScanFolder, Qt::CaseInsensitive)); + EXPECT_FALSE(config.GetScanFolderAt(2).IsRoot() ); + EXPECT_TRUE(config.GetScanFolderAt(2).RecurseSubFolders()); + EXPECT_GT(config.GetScanFolderAt(2).GetOrder(), config.GetScanFolderAt(0).GetOrder()); + EXPECT_EQ(0, config.GetScanFolderAt(2).ScanPath().compare(expectedScanFolder, Qt::CaseInsensitive)); } TEST_F(PlatformConfigurationUnitTests, Test_MetaFileTypes) diff --git a/Code/Tools/AssetProcessor/native/utilities/PlatformConfiguration.cpp b/Code/Tools/AssetProcessor/native/utilities/PlatformConfiguration.cpp index 52df8e901d..4a033cf6ca 100644 --- a/Code/Tools/AssetProcessor/native/utilities/PlatformConfiguration.cpp +++ b/Code/Tools/AssetProcessor/native/utilities/PlatformConfiguration.cpp @@ -1582,6 +1582,24 @@ namespace AssetProcessor gemOrder, /*scanFolderId*/ 0, /*canSaveNewAssets*/ true)); // Users can create assets like slices in Gem asset folders. + + // Now add another scan folder on Gem/GemName/Registry... + gemFolder = gemDir.absoluteFilePath(AzFramework::GemInfo::GetGemRegistryFolder()); + gemFolder = AssetUtilities::NormalizeDirectoryPath(gemFolder); + + assetBrowserDisplayName = AzFramework::GemInfo::GetGemRegistryFolder(); + portableKey = QString("gemregistry-%1").arg(gemNameAsUuid); + gemOrder++; + + AZ_TracePrintf(AssetProcessor::DebugChannel, "Adding GEM registry folder for monitoring / scanning: %s.\n", gemFolder.toUtf8().data()); + AddScanFolder(ScanFolderInfo( + gemFolder, + assetBrowserDisplayName, + portableKey, + isRoot, + isRecursive, + platforms, + gemOrder)); } } } diff --git a/Registry/AssetProcessorPlatformConfig.setreg b/Registry/AssetProcessorPlatformConfig.setreg index 3e90b063b5..ddc465c201 100644 --- a/Registry/AssetProcessorPlatformConfig.setreg +++ b/Registry/AssetProcessorPlatformConfig.setreg @@ -106,7 +106,7 @@ // "exclude": "mac" // } - "ScanFolder Game": { + "ScanFolder Project/Assets": { "watch": "@PROJECTROOT@", "display": "@PROJECTNAME@", "recursive": 1, @@ -129,6 +129,11 @@ "order": 30000, "include": "tools,renderer" }, + "ScanFolder Engine/Registry": { + "watch": "@ENGINEROOT@/Registry", + "recursive": 1, + "order": 40000 + }, // Excludes files that match the pattern or glob // if you use a pattern, remember to escape your backslashes (\\) diff --git a/Templates/DefaultProject/Template/Registry/assets_scan_folders.setreg b/Templates/DefaultProject/Template/Registry/assets_scan_folders.setreg index a42f65efb4..05f6314da4 100644 --- a/Templates/DefaultProject/Template/Registry/assets_scan_folders.setreg +++ b/Templates/DefaultProject/Template/Registry/assets_scan_folders.setreg @@ -1,14 +1,21 @@ { - "Amazon": - { - "${Name}.Assets": - { - "SourcePaths": - [ - "Assets", - "ShaderLib", - "Shaders" - ] + "Amazon": { + "AssetProcessor": { + "ScanFolder Project/ShaderLib": { + "watch": "@PROJECTROOT@/ShaderLib", + "recursive": 1, + "order": 1 + }, + "ScanFolder Project/Shaders": { + "watch": "@PROJECTROOT@/Shaders", + "recurisve": 1, + "order": 2 + }, + "ScanFolder Project/Registry": { + "watch": "@PROJECTROOT@/Registry", + "recursive": 1, + "order": 3 + } } } -} \ No newline at end of file +} diff --git a/Templates/MinimalProject/Template/Registry/assets_scan_folders.setreg b/Templates/MinimalProject/Template/Registry/assets_scan_folders.setreg index a42f65efb4..05f6314da4 100644 --- a/Templates/MinimalProject/Template/Registry/assets_scan_folders.setreg +++ b/Templates/MinimalProject/Template/Registry/assets_scan_folders.setreg @@ -1,14 +1,21 @@ { - "Amazon": - { - "${Name}.Assets": - { - "SourcePaths": - [ - "Assets", - "ShaderLib", - "Shaders" - ] + "Amazon": { + "AssetProcessor": { + "ScanFolder Project/ShaderLib": { + "watch": "@PROJECTROOT@/ShaderLib", + "recursive": 1, + "order": 1 + }, + "ScanFolder Project/Shaders": { + "watch": "@PROJECTROOT@/Shaders", + "recurisve": 1, + "order": 2 + }, + "ScanFolder Project/Registry": { + "watch": "@PROJECTROOT@/Registry", + "recursive": 1, + "order": 3 + } } } -} \ No newline at end of file +} From 937c2b2e88c325046bc0e6933af1f169b55af406 Mon Sep 17 00:00:00 2001 From: Ken Pruiksma Date: Wed, 13 Oct 2021 16:58:23 -0500 Subject: [PATCH 52/65] Terrain FP supports macro material component (#4587) * Adding basic support for TerrainMacroMaterialNotificationBus. Tracking macro materials in list. Signed-off-by: Ken Pruiksma (cherry picked from commit b4773454334de940d730620ffff300b46d6c611d) * Adding bus connection Signed-off-by: Ken Pruiksma (cherry picked from commit 66c99f503adb24f4be4f81716b544202d8e237d9) * Additions to indexed data vector to allow for getting an index from the data or deleting data with a reference to the data itself instead of the index. Additions to the feature processor for tracking macro material indices in sectors. Signed-off-by: Ken Pruiksma (cherry picked from commit 06365dbde5454e18e5fdf941f03b17b0d632027c) * Macro materials updating which macro materials are used in which sectors. Correctly handling construction / destruction. Signed-off-by: Ken Pruiksma * Updating the terrain macro material type to have the correct properties and also not attempt to render. Refactored some of the update loop in the TerrainFP to only rebuild the sectors when necessary, otherwise just update the srgs. Signed-off-by: Ken Pruiksma * Fixed up macro material to not try to actually render anything with shaders or hook to any shader data Terrain FP now pulls data pulling from macro material instance to use in the terrain material Various bug fixes around when terrain sectors needed reprocessing Signed-off-by: Ken Pruiksma * Adding basic support for TerrainMacroMaterialNotificationBus. Tracking macro materials in list. Signed-off-by: Ken Pruiksma (cherry picked from commit b4773454334de940d730620ffff300b46d6c611d) * Adding bus connection Signed-off-by: Ken Pruiksma (cherry picked from commit 66c99f503adb24f4be4f81716b544202d8e237d9) * Additions to indexed data vector to allow for getting an index from the data or deleting data with a reference to the data itself instead of the index. Additions to the feature processor for tracking macro material indices in sectors. Signed-off-by: Ken Pruiksma (cherry picked from commit 06365dbde5454e18e5fdf941f03b17b0d632027c) * Macro materials updating which macro materials are used in which sectors. Correctly handling construction / destruction. Signed-off-by: Ken Pruiksma * Updating the terrain macro material type to have the correct properties and also not attempt to render. Refactored some of the update loop in the TerrainFP to only rebuild the sectors when necessary, otherwise just update the srgs. Signed-off-by: Ken Pruiksma * Fixed up macro material to not try to actually render anything with shaders or hook to any shader data Terrain FP now pulls data pulling from macro material instance to use in the terrain material Various bug fixes around when terrain sectors needed reprocessing Signed-off-by: Ken Pruiksma * Constify all the things Signed-off-by: Ken Pruiksma * Updates from PR review. Signed-off-by: Ken Pruiksma --- .../Atom/Feature/Utils/IndexedDataVector.h | 2 + .../Atom/Feature/Utils/IndexedDataVector.inl | 20 + .../Terrain/DefaultPbrTerrain.material | 6 - .../Materials/Terrain/PbrTerrain.materialtype | 77 --- .../Terrain/TerrainMacroMaterial.materialtype | 63 +- .../Shaders/Terrain/TerrainCommon.azsli | 26 +- .../Terrain/TerrainPBR_ForwardPass.azsl | 34 +- .../TerrainFeatureProcessor.cpp | 561 ++++++++++++++---- .../TerrainRenderer/TerrainFeatureProcessor.h | 83 ++- 9 files changed, 606 insertions(+), 266 deletions(-) diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Utils/IndexedDataVector.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Utils/IndexedDataVector.h index 37835bd6a8..82cc1e7d50 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Utils/IndexedDataVector.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Utils/IndexedDataVector.h @@ -30,6 +30,7 @@ namespace AZ::Render void Clear(); IndexType GetFreeSlotIndex(); void RemoveIndex(IndexType index); + void RemoveData(DataType* data); DataType& GetData(IndexType index); const DataType& GetData(IndexType index) const; @@ -42,6 +43,7 @@ namespace AZ::Render const AZStd::vector& GetIndexVector() const; IndexType GetRawIndex(IndexType index) const; + IndexType GetIndexForData(const DataType* data) const; private: constexpr static size_t InitialReservedSize = 128; diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Utils/IndexedDataVector.inl b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Utils/IndexedDataVector.inl index 076caad7f4..581186dbcc 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Utils/IndexedDataVector.inl +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Utils/IndexedDataVector.inl @@ -83,6 +83,16 @@ namespace AZ::Render m_indices.at(index) = m_firstFreeSlot; m_firstFreeSlot = index; } + + template + inline void IndexedDataVector::RemoveData(DataType* data) + { + IndexType indexForData = GetIndexForData(data); + if (indexForData != NoFreeSlot) + { + RemoveIndex(indexForData); + } + } template inline DataType& IndexedDataVector::GetData(IndexType index) @@ -131,4 +141,14 @@ namespace AZ::Render { return m_indices.at(index); } + + template + IndexType IndexedDataVector::GetIndexForData(const DataType* data) const + { + if (data >= &m_data.front() && data <= &m_data.back()) + { + return m_dataToIndices.at(data - &m_data.front()); + } + return NoFreeSlot; + } } // namespace AZ::Render diff --git a/Gems/Terrain/Assets/Materials/Terrain/DefaultPbrTerrain.material b/Gems/Terrain/Assets/Materials/Terrain/DefaultPbrTerrain.material index c0de5969b2..d2acc2516a 100644 --- a/Gems/Terrain/Assets/Materials/Terrain/DefaultPbrTerrain.material +++ b/Gems/Terrain/Assets/Materials/Terrain/DefaultPbrTerrain.material @@ -4,12 +4,6 @@ "parentMaterial": "", "propertyLayoutVersion": 1, "properties": { - "macroColor": { - "useTexture": false - }, - "macroNormal": { - "useTexture": false - }, "baseColor": { "color": [ 0.18, 0.18, 0.18 ], "useTexture": false diff --git a/Gems/Terrain/Assets/Materials/Terrain/PbrTerrain.materialtype b/Gems/Terrain/Assets/Materials/Terrain/PbrTerrain.materialtype index 191fa7a731..ec04412fe6 100644 --- a/Gems/Terrain/Assets/Materials/Terrain/PbrTerrain.materialtype +++ b/Gems/Terrain/Assets/Materials/Terrain/PbrTerrain.materialtype @@ -133,67 +133,6 @@ } } ], - "macroColor": [ - { - "id": "textureMap", - "displayName": "Texture", - "description": "Macro color texture map", - "type": "Image", - "connection": { - "type": "ShaderInput", - "id": "m_macroColorMap" - } - }, - { - "id": "useTexture", - "displayName": "Use Texture", - "description": "Whether to use the texture.", - "type": "Bool", - "defaultValue": true - } - - ], - "macroNormal": [ - { - "id": "textureMap", - "displayName": "Texture", - "description": "Macro normal texture map", - "type": "Image", - "connection": { - "type": "ShaderInput", - "id": "m_macroNormalMap" - } - }, - { - "id": "useTexture", - "displayName": "Use Texture", - "description": "Whether to use the texture.", - "type": "Bool", - "defaultValue": true - }, - { - "id": "flipX", - "displayName": "Flip X Channel", - "description": "Flip tangent direction for this normal map.", - "type": "Bool", - "defaultValue": false, - "connection": { - "type": "ShaderInput", - "id": "m_flipMacroNormalX" - } - }, - { - "id": "flipY", - "displayName": "Flip Y Channel", - "description": "Flip bitangent direction for this normal map.", - "type": "Bool", - "defaultValue": false, - "connection": { - "type": "ShaderInput", - "id": "m_flipMacroNormalY" - } - } - ], "baseColor": [ { "id": "color", @@ -380,22 +319,6 @@ } ], "functors": [ - { - "type": "UseTexture", - "args": { - "textureProperty": "macroColor.textureMap", - "useTextureProperty": "macroColor.useTexture", - "shaderOption": "o_macroColor_useTexture" - } - }, - { - "type": "UseTexture", - "args": { - "textureProperty": "macroNormal.textureMap", - "useTextureProperty": "macroNormal.useTexture", - "shaderOption": "o_macroNormal_useTexture" - } - }, { "type": "UseTexture", "args": { diff --git a/Gems/Terrain/Assets/Materials/Terrain/TerrainMacroMaterial.materialtype b/Gems/Terrain/Assets/Materials/Terrain/TerrainMacroMaterial.materialtype index 0a4f6d0229..3cdab8da10 100644 --- a/Gems/Terrain/Assets/Materials/Terrain/TerrainMacroMaterial.materialtype +++ b/Gems/Terrain/Assets/Materials/Terrain/TerrainMacroMaterial.materialtype @@ -4,42 +4,59 @@ "version": 1, "groups": [ { - "id": "settings", - "displayName": "Settings" + "name": "baseColor", + "displayName": "Base Color", + "description": "Properties for configuring the surface reflected color for dielectrics or reflectance values for metals." + }, + { + "name": "normal", + "displayName": "Normal", + "description": "Properties related to configuring surface normal." } ], "properties": { - "macroColor": [ + "baseColor": [ { - "id": "useTexture", - "displayName": "Use Texture", - "description": "Whether to use the texture.", - "type": "Bool", - "defaultValue": true + "name": "textureMap", + "displayName": "Texture", + "description": "Base color of the macro material", + "type": "Image" } - ], - "macroNormal": [ + "normal": [ { - "id": "useTexture", - "displayName": "Use Texture", - "description": "Whether to use the texture.", + "name": "textureMap", + "displayName": "Texture", + "description": "Texture for defining surface normal direction. These will override normals generated from the geometry.", + "type": "Image" + }, + { + "name": "flipX", + "displayName": "Flip X Channel", + "description": "Flip tangent direction for this normal map.", "type": "Bool", - "defaultValue": true + "defaultValue": false + }, + { + "name": "flipY", + "displayName": "Flip Y Channel", + "description": "Flip bitangent direction for this normal map.", + "type": "Bool", + "defaultValue": false + }, + { + "name": "factor", + "displayName": "Factor", + "description": "Strength factor for scaling the values", + "type": "Float", + "defaultValue": 1.0, + "min": 0.0, + "softMax": 2.0 } ] } }, "shaders": [ - { - "file": "../../Shaders/Terrain/TerrainPBR_ForwardPass.shader" - }, - { - "file": "../../Shaders/Terrain/Terrain_Shadowmap.shader" - }, - { - "file": "../../Shaders/Terrain/Terrain_DepthPass.shader" - } ], "functors": [ ] diff --git a/Gems/Terrain/Assets/Shaders/Terrain/TerrainCommon.azsli b/Gems/Terrain/Assets/Shaders/Terrain/TerrainCommon.azsli index 95d1a73bbd..72c1af953c 100644 --- a/Gems/Terrain/Assets/Shaders/Terrain/TerrainCommon.azsli +++ b/Gems/Terrain/Assets/Shaders/Terrain/TerrainCommon.azsli @@ -26,8 +26,24 @@ ShaderResourceGroup ObjectSrg : SRG_PerObject float m_heightScale; }; + struct MacroMaterialData + { + float2 m_uvMin; + float2 m_uvMax; + float m_normalFactor; + bool m_flipNormalX; + bool m_flipNormalY; + uint m_mapsInUse; + }; + TerrainData m_terrainData; + MacroMaterialData m_macroMaterialData[4]; + uint m_macroMaterialCount; + + Texture2D m_macroColorMap[4]; + Texture2D m_macroNormalMap[4]; + // The below shouldn't be in this SRG but needs to be for now because the lighting functions depend on them. //! Reflection Probe (smallest probe volume that overlaps the object position) @@ -101,14 +117,6 @@ ShaderResourceGroup TerrainMaterialSrg : SRG_PerMaterial MaxAnisotropy = 16; }; - // Macro Color - Texture2D m_macroColorMap; - - // Macro normal - Texture2D m_macroNormalMap; - bool m_flipMacroNormalX; - bool m_flipMacroNormalY; - // Base Color float3 m_baseColor; float m_baseColorFactor; @@ -130,8 +138,6 @@ ShaderResourceGroup TerrainMaterialSrg : SRG_PerMaterial } option bool o_useTerrainSmoothing = false; -option bool o_macroColor_useTexture = true; -option bool o_macroNormal_useTexture = true; option bool o_baseColor_useTexture = true; option bool o_specularF0_useTexture = true; option bool o_normal_useTexture = true; diff --git a/Gems/Terrain/Assets/Shaders/Terrain/TerrainPBR_ForwardPass.azsl b/Gems/Terrain/Assets/Shaders/Terrain/TerrainPBR_ForwardPass.azsl index fedd19a498..91e367500b 100644 --- a/Gems/Terrain/Assets/Shaders/Terrain/TerrainPBR_ForwardPass.azsl +++ b/Gems/Terrain/Assets/Shaders/Terrain/TerrainPBR_ForwardPass.azsl @@ -68,7 +68,6 @@ VSOutput TerrainPBR_MainPassVS(VertexInput IN) ForwardPassOutput TerrainPBR_MainPassPS(VSOutput IN) { // ------- Surface ------- - Surface surface; // Position @@ -83,12 +82,32 @@ ForwardPassOutput TerrainPBR_MainPassPS(VSOutput IN) // ------- Normal ------- float3 macroNormal = IN.m_normal; - if (o_macroNormal_useTexture) - { - macroNormal = GetNormalInputTS(TerrainMaterialSrg::m_macroNormalMap, TerrainMaterialSrg::m_sampler, - origUv, TerrainMaterialSrg::m_flipMacroNormalX, TerrainMaterialSrg::m_flipMacroNormalY, CreateIdentity3x3(), true, 1.0); - } + // ------- Macro Color / Normal ------- + float3 macroColor = TerrainMaterialSrg::m_baseColor.rgb; + [unroll] for (uint i = 0; i < 4; ++i) + { + float2 macroUvMin = ObjectSrg::m_macroMaterialData[i].m_uvMin; + float2 macroUvMax = ObjectSrg::m_macroMaterialData[i].m_uvMax; + float2 macroUv = lerp(macroUvMin, macroUvMax, IN.m_uv); + if (macroUv.x >= 0.0 && macroUv.x <= 1.0 && macroUv.y >= 0.0 && macroUv.y <= 1.0) + { + if ((ObjectSrg::m_macroMaterialData[i].m_mapsInUse & 1) > 0) + { + macroColor = GetBaseColorInput(ObjectSrg::m_macroColorMap[i], TerrainMaterialSrg::m_sampler, macroUv, macroColor, true); + } + if ((ObjectSrg::m_macroMaterialData[i].m_mapsInUse & 2) > 0) + { + bool flipX = ObjectSrg::m_macroMaterialData[i].m_flipNormalX; + bool flipY = ObjectSrg::m_macroMaterialData[i].m_flipNormalY; + bool factor = ObjectSrg::m_macroMaterialData[i].m_normalFactor; + macroNormal = GetNormalInputTS(ObjectSrg::m_macroNormalMap[i], TerrainMaterialSrg::m_sampler, + macroUv, flipX, flipY, CreateIdentity3x3(), true, factor); + } + break; + } + } + float3 detailNormal = GetNormalInputTS(TerrainMaterialSrg::m_normalMap, TerrainMaterialSrg::m_sampler, detailUv, TerrainMaterialSrg::m_flipNormalX, TerrainMaterialSrg::m_flipNormalY, CreateIdentity3x3(), o_normal_useTexture, TerrainMaterialSrg::m_normalFactor); @@ -97,9 +116,6 @@ ForwardPassOutput TerrainPBR_MainPassPS(VSOutput IN) surface.normal = normalize(surface.normal); surface.vertexNormal = normalize(IN.m_normal); - // ------- Macro Color ------- - float3 macroColor = GetBaseColorInput(TerrainMaterialSrg::m_macroColorMap, TerrainMaterialSrg::m_sampler, origUv, TerrainMaterialSrg::m_baseColor.rgb, o_baseColor_useTexture); - // ------- Base Color ------- float3 detailColor = GetBaseColorInput(TerrainMaterialSrg::m_baseColorMap, TerrainMaterialSrg::m_sampler, detailUv, TerrainMaterialSrg::m_baseColor.rgb, o_baseColor_useTexture); float3 blendedColor = BlendBaseColor(lerp(detailColor, TerrainMaterialSrg::m_baseColor.rgb, detailFactor), macroColor, TerrainMaterialSrg::m_baseColorFactor, o_baseColorTextureBlendMode, o_baseColor_useTexture); diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.cpp b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.cpp index 560fe1eb60..d7300cdc48 100644 --- a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.cpp +++ b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.cpp @@ -49,13 +49,25 @@ namespace Terrain namespace MaterialInputs { + // Terrain material static const char* const HeightmapImage("settings.heightmapImage"); + + // Macro material + static const char* const MacroColorTextureMap("baseColor.textureMap"); + static const char* const MacroNormalTextureMap("normal.textureMap"); + static const char* const MacroNormalFlipX("normal.flipX"); + static const char* const MacroNormalFlipY("normal.flipY"); + static const char* const MacroNormalFactor("normal.factor"); } namespace ShaderInputs { static const char* const ModelToWorld("m_modelToWorld"); static const char* const TerrainData("m_terrainData"); + static const char* const MacroMaterialData("m_macroMaterialData"); + static const char* const MacroMaterialCount("m_macroMaterialCount"); + static const char* const MacroColorMap("m_macroColorMap"); + static const char* const MacroNormalMap("m_macroNormalMap"); } @@ -71,8 +83,6 @@ namespace Terrain void TerrainFeatureProcessor::Activate() { - m_areaData = {}; - m_dirtyRegion = AZ::Aabb::CreateNull(); Initialize(); AzFramework::Terrain::TerrainDataNotificationBus::Handler::BusConnect(); } @@ -94,6 +104,10 @@ namespace Terrain { AZ_Error("TerrainFeatureProcessor", false, "No per-object ShaderResourceGroup found on terrain material."); } + else + { + PrepareMaterialData(); + } } } ); @@ -107,11 +121,17 @@ namespace Terrain void TerrainFeatureProcessor::Deactivate() { + TerrainMacroMaterialNotificationBus::Handler::BusDisconnect(); AzFramework::Terrain::TerrainDataNotificationBus::Handler::BusDisconnect(); AZ::RPI::MaterialReloadNotificationBus::Handler::BusDisconnect(); m_patchModel = {}; m_areaData = {}; + m_dirtyRegion = AZ::Aabb::CreateNull(); + m_sectorData.clear(); + m_macroMaterials.Clear(); + m_materialAssetLoader = {}; + m_materialInstance = {}; } void TerrainFeatureProcessor::Render(const AZ::RPI::FeatureProcessor::RenderPacket& packet) @@ -126,7 +146,7 @@ namespace Terrain void TerrainFeatureProcessor::OnTerrainDataChanged(const AZ::Aabb& dirtyRegion, TerrainDataChangedMask dataChangedMask) { - if (dataChangedMask != TerrainDataChangedMask::HeightData && dataChangedMask != TerrainDataChangedMask::Settings) + if ((dataChangedMask & (TerrainDataChangedMask::HeightData | TerrainDataChangedMask::Settings)) == 0) { return; } @@ -140,14 +160,21 @@ namespace Terrain m_dirtyRegion.AddAabb(regionToUpdate); m_dirtyRegion.Clamp(worldBounds); - AZ::Transform transform = AZ::Transform::CreateTranslation(worldBounds.GetCenter()); + const AZ::Transform transform = AZ::Transform::CreateTranslation(worldBounds.GetCenter()); AZ::Vector2 queryResolution = AZ::Vector2(1.0f); AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult( queryResolution, &AzFramework::Terrain::TerrainDataRequests::GetTerrainHeightQueryResolution); + // Sectors need to be rebuilt if the world bounds change in the x/y, or the sample spacing changes. + m_areaData.m_rebuildSectors = m_areaData.m_rebuildSectors || + m_areaData.m_terrainBounds.GetMin().GetX() != worldBounds.GetMin().GetX() || + m_areaData.m_terrainBounds.GetMin().GetY() != worldBounds.GetMin().GetY() || + m_areaData.m_terrainBounds.GetMax().GetX() != worldBounds.GetMax().GetX() || + m_areaData.m_terrainBounds.GetMax().GetY() != worldBounds.GetMax().GetY() || + m_areaData.m_sampleSpacing != queryResolution.GetX(); + m_areaData.m_transform = transform; - m_areaData.m_heightScale = worldBounds.GetZExtent(); m_areaData.m_terrainBounds = worldBounds; m_areaData.m_heightmapImageWidth = aznumeric_cast(worldBounds.GetXExtent() / queryResolution.GetX()); m_areaData.m_heightmapImageHeight = aznumeric_cast(worldBounds.GetYExtent() / queryResolution.GetY()); @@ -155,7 +182,93 @@ namespace Terrain m_areaData.m_updateHeight = aznumeric_cast(m_dirtyRegion.GetYExtent() / queryResolution.GetY()); // Currently query resolution is multidimensional but the rendering system only supports this changing in one dimension. m_areaData.m_sampleSpacing = queryResolution.GetX(); - m_areaData.m_propertiesDirty = true; + m_areaData.m_heightmapUpdated = true; + } + + void TerrainFeatureProcessor::OnTerrainMacroMaterialCreated(AZ::EntityId entityId, MaterialInstance material, const AZ::Aabb& region) + { + MacroMaterialData& materialData = FindOrCreateMacroMaterial(entityId); + materialData.m_bounds = region; + + UpdateMacroMaterialData(materialData, material); + + // Update all sectors in region. + ForOverlappingSectors(materialData.m_bounds, + [&](SectorData& sectorData) { + if (sectorData.m_macroMaterials.size() < sectorData.m_macroMaterials.max_size()) + { + sectorData.m_macroMaterials.push_back(m_macroMaterials.GetIndexForData(&materialData)); + } + } + ); + } + + void TerrainFeatureProcessor::OnTerrainMacroMaterialChanged(AZ::EntityId entityId, MaterialInstance macroMaterial) + { + if (macroMaterial) + { + MacroMaterialData& data = FindOrCreateMacroMaterial(entityId); + UpdateMacroMaterialData(data, macroMaterial); + } + else + { + RemoveMacroMaterial(entityId); + } + } + + void TerrainFeatureProcessor::OnTerrainMacroMaterialRegionChanged(AZ::EntityId entityId, [[maybe_unused]] const AZ::Aabb& oldRegion, const AZ::Aabb& newRegion) + { + MacroMaterialData& materialData = FindOrCreateMacroMaterial(entityId); + for (SectorData& sectorData : m_sectorData) + { + bool overlapsOld = sectorData.m_aabb.Overlaps(materialData.m_bounds); + bool overlapsNew = sectorData.m_aabb.Overlaps(newRegion); + if (overlapsOld && !overlapsNew) + { + // Remove the macro material from this sector + for (uint16_t& idx : sectorData.m_macroMaterials) + { + if (m_macroMaterials.GetData(idx).m_entityId == entityId) + { + idx = sectorData.m_macroMaterials.back(); + sectorData.m_macroMaterials.pop_back(); + } + } + } + else if (overlapsNew && !overlapsOld) + { + // Add the macro material to this sector + if (sectorData.m_macroMaterials.size() < MaxMaterialsPerSector) + { + sectorData.m_macroMaterials.push_back(m_macroMaterials.GetIndexForData(&materialData)); + } + } + } + m_areaData.m_macroMaterialsUpdated = true; + materialData.m_bounds = newRegion; + } + + void TerrainFeatureProcessor::OnTerrainMacroMaterialDestroyed(AZ::EntityId entityId) + { + MacroMaterialData* materialData = FindMacroMaterial(entityId); + + if (materialData) + { + uint16_t destroyedMaterialIndex = m_macroMaterials.GetIndexForData(materialData); + ForOverlappingSectors(materialData->m_bounds, + [&](SectorData& sectorData) { + for (uint16_t& idx : sectorData.m_macroMaterials) + { + if (idx == destroyedMaterialIndex) + { + idx = sectorData.m_macroMaterials.back(); + sectorData.m_macroMaterials.pop_back(); + } + } + }); + } + + m_areaData.m_macroMaterialsUpdated = true; } void TerrainFeatureProcessor::UpdateTerrainData() @@ -165,9 +278,9 @@ namespace Terrain uint32_t width = m_areaData.m_updateWidth; uint32_t height = m_areaData.m_updateHeight; const AZ::Aabb& worldBounds = m_areaData.m_terrainBounds; - float queryResolution = m_areaData.m_sampleSpacing; + const float queryResolution = m_areaData.m_sampleSpacing; - AZ::RHI::Size worldSize = AZ::RHI::Size(m_areaData.m_heightmapImageWidth, m_areaData.m_heightmapImageHeight, 1); + const AZ::RHI::Size worldSize = AZ::RHI::Size(m_areaData.m_heightmapImageWidth, m_areaData.m_heightmapImageHeight, 1); if (!m_areaData.m_heightmapImage || m_areaData.m_heightmapImage->GetDescriptor().m_size != worldSize) { @@ -176,7 +289,7 @@ namespace Terrain height = worldSize.m_height; m_dirtyRegion = worldBounds; - AZ::Data::Instance imagePool = AZ::RPI::ImageSystemInterface::Get()->GetSystemAttachmentPool(); + const AZ::Data::Instance imagePool = AZ::RPI::ImageSystemInterface::Get()->GetSystemAttachmentPool(); AZ::RHI::ImageDescriptor imageDescriptor = AZ::RHI::ImageDescriptor::Create2D( AZ::RHI::ImageBindFlags::ShaderRead, width, height, AZ::RHI::Format::R16_UNORM ); @@ -210,9 +323,9 @@ namespace Terrain AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT, &terrainExists); - float clampedHeight = AZ::GetClamp((terrainHeight - worldBounds.GetMin().GetZ()) / worldBounds.GetExtents().GetZ(), 0.0f, 1.0f); - float expandedHeight = AZStd::roundf(clampedHeight * AZStd::numeric_limits::max()); - uint16_t uint16Height = aznumeric_cast(expandedHeight); + const float clampedHeight = AZ::GetClamp((terrainHeight - worldBounds.GetMin().GetZ()) / worldBounds.GetExtents().GetZ(), 0.0f, 1.0f); + const float expandedHeight = AZStd::roundf(clampedHeight * AZStd::numeric_limits::max()); + const uint16_t uint16Height = aznumeric_cast(expandedHeight); pixels.push_back(uint16Height); } @@ -241,118 +354,248 @@ namespace Terrain m_dirtyRegion = AZ::Aabb::CreateNull(); } + void TerrainFeatureProcessor::PrepareMaterialData() + { + const auto layout = m_materialInstance->GetAsset()->GetObjectSrgLayout(); + + m_modelToWorldIndex = layout->FindShaderInputConstantIndex(AZ::Name(ShaderInputs::ModelToWorld)); + AZ_Error(TerrainFPName, m_modelToWorldIndex.IsValid(), "Failed to find shader input constant %s.", ShaderInputs::ModelToWorld); + + m_terrainDataIndex = layout->FindShaderInputConstantIndex(AZ::Name(ShaderInputs::TerrainData)); + AZ_Error(TerrainFPName, m_terrainDataIndex.IsValid(), "Failed to find shader input constant %s.", ShaderInputs::TerrainData); + + m_macroMaterialDataIndex = layout->FindShaderInputConstantIndex(AZ::Name(ShaderInputs::MacroMaterialData)); + AZ_Error(TerrainFPName, m_macroMaterialDataIndex.IsValid(), "Failed to find shader input constant %s.", ShaderInputs::MacroMaterialData); + + m_macroMaterialCountIndex = layout->FindShaderInputConstantIndex(AZ::Name(ShaderInputs::MacroMaterialCount)); + AZ_Error(TerrainFPName, m_macroMaterialCountIndex.IsValid(), "Failed to find shader input constant %s.", ShaderInputs::MacroMaterialCount); + + m_macroColorMapIndex = layout->FindShaderInputImageIndex(AZ::Name(ShaderInputs::MacroColorMap)); + AZ_Error(TerrainFPName, m_macroColorMapIndex.IsValid(), "Failed to find shader input constant %s.", ShaderInputs::MacroColorMap); + + m_macroNormalMapIndex = layout->FindShaderInputImageIndex(AZ::Name(ShaderInputs::MacroNormalMap)); + AZ_Error(TerrainFPName, m_macroNormalMapIndex.IsValid(), "Failed to find shader input constant %s.", ShaderInputs::MacroNormalMap); + + m_heightmapPropertyIndex = m_materialInstance->GetMaterialPropertiesLayout()->FindPropertyIndex(AZ::Name(MaterialInputs::HeightmapImage)); + AZ_Error(TerrainFPName, m_heightmapPropertyIndex.IsValid(), "Failed to find material input constant %s.", MaterialInputs::HeightmapImage); + + TerrainMacroMaterialRequestBus::EnumerateHandlers( + [&](TerrainMacroMaterialRequests* handler) + { + MaterialInstance macroMaterial; + AZ::Aabb bounds; + handler->GetTerrainMacroMaterialData(macroMaterial, bounds); + AZ::EntityId entityId = *(Terrain::TerrainMacroMaterialRequestBus::GetCurrentBusId()); + OnTerrainMacroMaterialCreated(entityId, macroMaterial, bounds); + return true; + } + ); + TerrainMacroMaterialNotificationBus::Handler::BusConnect(); + } + + void TerrainFeatureProcessor::UpdateMacroMaterialData(MacroMaterialData& macroMaterialData, MaterialInstance material) + { + // Since we're using an actual macro material instance for now, get the values from it that we care about. + const auto materialLayout = material->GetMaterialPropertiesLayout(); + + const AZ::RPI::MaterialPropertyIndex macroColorTextureMapIndex = materialLayout->FindPropertyIndex(AZ::Name(MaterialInputs::MacroColorTextureMap)); + AZ_Error(TerrainFPName, macroColorTextureMapIndex.IsValid(), "Failed to find shader input constant %s.", MaterialInputs::MacroColorTextureMap); + + const AZ::RPI::MaterialPropertyIndex macroNormalTextureMapIndex = materialLayout->FindPropertyIndex(AZ::Name(MaterialInputs::MacroNormalTextureMap)); + AZ_Error(TerrainFPName, macroNormalTextureMapIndex.IsValid(), "Failed to find shader input constant %s.", MaterialInputs::MacroNormalTextureMap); + + const AZ::RPI::MaterialPropertyIndex macroNormalFlipXIndex = materialLayout->FindPropertyIndex(AZ::Name(MaterialInputs::MacroNormalFlipX)); + AZ_Error(TerrainFPName, macroNormalFlipXIndex.IsValid(), "Failed to find shader input constant %s.", MaterialInputs::MacroNormalFlipX); + + const AZ::RPI::MaterialPropertyIndex macroNormalFlipYIndex = materialLayout->FindPropertyIndex(AZ::Name(MaterialInputs::MacroNormalFlipY)); + AZ_Error(TerrainFPName, macroNormalFlipYIndex.IsValid(), "Failed to find shader input constant %s.", MaterialInputs::MacroNormalFlipY); + + const AZ::RPI::MaterialPropertyIndex macroNormalFactorIndex = materialLayout->FindPropertyIndex(AZ::Name(MaterialInputs::MacroNormalFactor)); + AZ_Error(TerrainFPName, macroNormalFactorIndex.IsValid(), "Failed to find shader input constant %s.", MaterialInputs::MacroNormalFactor); + + macroMaterialData.m_colorImage = material->GetPropertyValue(macroColorTextureMapIndex).GetValue>(); + macroMaterialData.m_normalImage = material->GetPropertyValue(macroNormalTextureMapIndex).GetValue>(); + macroMaterialData.m_normalFlipX = material->GetPropertyValue(macroNormalFlipXIndex).GetValue(); + macroMaterialData.m_normalFlipY = material->GetPropertyValue(macroNormalFlipYIndex).GetValue(); + macroMaterialData.m_normalFactor = material->GetPropertyValue(macroNormalFactorIndex).GetValue(); + + if (macroMaterialData.m_bounds.IsValid()) + { + m_areaData.m_macroMaterialsUpdated = true; + } + } + void TerrainFeatureProcessor::ProcessSurfaces(const FeatureProcessor::RenderPacket& process) { AZ_PROFILE_FUNCTION(AzRender); + + const AZ::Aabb& terrainBounds = m_areaData.m_terrainBounds; - if (!m_areaData.m_terrainBounds.IsValid()) + if (!terrainBounds.IsValid()) { return; } - - if (m_areaData.m_propertiesDirty && m_materialInstance && m_materialInstance->CanCompile()) + + if (m_materialInstance && m_materialInstance->CanCompile()) { - UpdateTerrainData(); - - m_areaData.m_propertiesDirty = false; - m_sectorData.clear(); - - AZ::RPI::MaterialPropertyIndex heightmapPropertyIndex = - m_materialInstance->GetMaterialPropertiesLayout()->FindPropertyIndex(AZ::Name(MaterialInputs::HeightmapImage)); - AZ_Error(TerrainFPName, heightmapPropertyIndex.IsValid(), "Failed to find material input constant %s.", MaterialInputs::HeightmapImage); - AZ::Data::Instance heightmapImage = m_areaData.m_heightmapImage; - m_materialInstance->SetPropertyValue(heightmapPropertyIndex, heightmapImage); - m_materialInstance->Compile(); - - const auto layout = m_materialInstance->GetAsset()->GetObjectSrgLayout(); - - m_modelToWorldIndex = layout->FindShaderInputConstantIndex(AZ::Name(ShaderInputs::ModelToWorld)); - AZ_Error(TerrainFPName, m_modelToWorldIndex.IsValid(), "Failed to find shader input constant %s.", ShaderInputs::ModelToWorld); - - m_terrainDataIndex = layout->FindShaderInputConstantIndex(AZ::Name(ShaderInputs::TerrainData)); - AZ_Error(TerrainFPName, m_terrainDataIndex.IsValid(), "Failed to find shader input constant %s.", ShaderInputs::TerrainData); - - float xFirstPatchStart = - m_areaData.m_terrainBounds.GetMin().GetX() - fmod(m_areaData.m_terrainBounds.GetMin().GetX(), GridMeters); - float xLastPatchStart = m_areaData.m_terrainBounds.GetMax().GetX() - fmod(m_areaData.m_terrainBounds.GetMax().GetX(), GridMeters); - float yFirstPatchStart = - m_areaData.m_terrainBounds.GetMin().GetY() - fmod(m_areaData.m_terrainBounds.GetMin().GetY(), GridMeters); - float yLastPatchStart = m_areaData.m_terrainBounds.GetMax().GetY() - fmod(m_areaData.m_terrainBounds.GetMax().GetY(), GridMeters); - - for (float yPatch = yFirstPatchStart; yPatch <= yLastPatchStart; yPatch += GridMeters) + if (m_areaData.m_rebuildSectors) { - for (float xPatch = xFirstPatchStart; xPatch <= xLastPatchStart; xPatch += GridMeters) + // Something about the whole world changed, so the sectors need to be rebuilt + + 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 auto& materialAsset = m_materialInstance->GetAsset(); + const auto& shaderAsset = materialAsset->GetMaterialTypeAsset()->GetShaderAssetForObjectSrg(); + + for (float yPatch = yFirstPatchStart; yPatch <= yLastPatchStart; yPatch += GridMeters) { - const auto& materialAsset = m_materialInstance->GetAsset(); - auto& shaderAsset = materialAsset->GetMaterialTypeAsset()->GetShaderAssetForObjectSrg(); - auto objectSrg = AZ::RPI::ShaderResourceGroup::Create(shaderAsset, materialAsset->GetObjectSrgLayout()->GetName()); - if (!objectSrg) + for (float xPatch = xFirstPatchStart; xPatch <= xLastPatchStart; xPatch += GridMeters) { - AZ_Warning("TerrainFeatureProcessor", false, "Failed to create a new shader resource group, skipping."); - continue; - } - - { // Update SRG - - AZStd::array uvMin = { 0.0f, 0.0f }; - AZStd::array uvMax = { 1.0f, 1.0f }; - - uvMin[0] = (float)((xPatch - m_areaData.m_terrainBounds.GetMin().GetX()) / m_areaData.m_terrainBounds.GetXExtent()); - uvMin[1] = (float)((yPatch - m_areaData.m_terrainBounds.GetMin().GetY()) / m_areaData.m_terrainBounds.GetYExtent()); - - uvMax[0] = - (float)(((xPatch + GridMeters) - m_areaData.m_terrainBounds.GetMin().GetX()) / m_areaData.m_terrainBounds.GetXExtent()); - uvMax[1] = - (float)(((yPatch + GridMeters) - m_areaData.m_terrainBounds.GetMin().GetY()) / m_areaData.m_terrainBounds.GetYExtent()); - - AZStd::array uvStep = + auto objectSrg = AZ::RPI::ShaderResourceGroup::Create(shaderAsset, materialAsset->GetObjectSrgLayout()->GetName()); + if (!objectSrg) { - 1.0f / m_areaData.m_heightmapImageWidth, 1.0f / m_areaData.m_heightmapImageHeight, - }; - - AZ::Transform transform = m_areaData.m_transform; - transform.SetTranslation(xPatch, yPatch, m_areaData.m_transform.GetTranslation().GetZ()); - - AZ::Matrix3x4 matrix3x4 = AZ::Matrix3x4::CreateFromTransform(transform); - - objectSrg->SetConstant(m_modelToWorldIndex, matrix3x4); - - ShaderTerrainData terrainDataForSrg; - terrainDataForSrg.m_sampleSpacing = m_areaData.m_sampleSpacing; - terrainDataForSrg.m_heightScale = m_areaData.m_heightScale; - terrainDataForSrg.m_uvMin = uvMin; - terrainDataForSrg.m_uvMax = uvMax; - terrainDataForSrg.m_uvStep = uvStep; - objectSrg->SetConstant(m_terrainDataIndex, terrainDataForSrg); - - objectSrg->Compile(); - } - - m_sectorData.push_back(); - SectorData& sectorData = m_sectorData.back(); - - for (auto& lod : m_patchModel->GetLods()) - { - AZ::RPI::ModelLod& modelLod = *lod.get(); - sectorData.m_drawPackets.emplace_back(modelLod, 0, m_materialInstance, objectSrg); - AZ::RPI::MeshDrawPacket& drawPacket = sectorData.m_drawPackets.back(); - - // set the shader option to select forward pass IBL specular if necessary - if (!drawPacket.SetShaderOption(AZ::Name("o_meshUseForwardPassIBLSpecular"), AZ::RPI::ShaderOptionValue{ false })) - { - AZ_Warning("MeshDrawPacket", false, "Failed to set o_meshUseForwardPassIBLSpecular on mesh draw packet"); + AZ_Warning("TerrainFeatureProcessor", false, "Failed to create a new shader resource group, skipping."); + continue; } - uint8_t stencilRef = AZ::Render::StencilRefs::UseDiffuseGIPass | AZ::Render::StencilRefs::UseIBLSpecularPass; - drawPacket.SetStencilRef(stencilRef); - drawPacket.Update(*GetParentScene(), true); + + m_sectorData.push_back(); + SectorData& sectorData = m_sectorData.back(); + + for (auto& lod : m_patchModel->GetLods()) + { + AZ::RPI::ModelLod& modelLod = *lod.get(); + sectorData.m_drawPackets.emplace_back(modelLod, 0, m_materialInstance, objectSrg); + AZ::RPI::MeshDrawPacket& drawPacket = sectorData.m_drawPackets.back(); + + // set the shader option to select forward pass IBL specular if necessary + if (!drawPacket.SetShaderOption(AZ::Name("o_meshUseForwardPassIBLSpecular"), AZ::RPI::ShaderOptionValue{ false })) + { + AZ_Warning("MeshDrawPacket", false, "Failed to set o_meshUseForwardPassIBLSpecular on mesh draw packet"); + } + const uint8_t stencilRef = AZ::Render::StencilRefs::UseDiffuseGIPass | AZ::Render::StencilRefs::UseIBLSpecularPass; + drawPacket.SetStencilRef(stencilRef); + drawPacket.Update(*GetParentScene(), true); + } + + sectorData.m_aabb = + AZ::Aabb::CreateFromMinMax( + AZ::Vector3(xPatch, yPatch, terrainBounds.GetMin().GetZ()), + AZ::Vector3(xPatch + GridMeters, yPatch + GridMeters, terrainBounds.GetMax().GetZ()) + ); + sectorData.m_srg = objectSrg; + } + } + + if (m_areaData.m_macroMaterialsUpdated) + { + // sectors were rebuilt, so any cached macro material data needs to be regenerated + for (SectorData& sectorData : m_sectorData) + { + for (MacroMaterialData& macroMaterialData : m_macroMaterials.GetDataVector()) + { + if (macroMaterialData.m_bounds.Overlaps(sectorData.m_aabb)) + { + sectorData.m_macroMaterials.push_back(m_macroMaterials.GetIndexForData(¯oMaterialData)); + if (sectorData.m_macroMaterials.size() == MaxMaterialsPerSector) + { + break; + } + } + } + } + } + } + + if (m_areaData.m_heightmapUpdated) + { + UpdateTerrainData(); + + const AZ::Data::Instance heightmapImage = m_areaData.m_heightmapImage; + m_materialInstance->SetPropertyValue(m_heightmapPropertyIndex, heightmapImage); + m_materialInstance->Compile(); + } + + if (m_areaData.m_heightmapUpdated || m_areaData.m_macroMaterialsUpdated) + { + // Currently when anything in the heightmap changes we're updating all the srgs, but this could probably + // be optimized to only update the srgs that changed. + + m_areaData.m_heightmapUpdated = false; + m_areaData.m_macroMaterialsUpdated = false; + + for (SectorData& sectorData : m_sectorData) + { + ShaderTerrainData terrainDataForSrg; + + const float xPatch = sectorData.m_aabb.GetMin().GetX(); + const float yPatch = sectorData.m_aabb.GetMin().GetY(); + + terrainDataForSrg.m_uvMin = { + (xPatch - terrainBounds.GetMin().GetX()) / terrainBounds.GetXExtent(), + (yPatch - terrainBounds.GetMin().GetY()) / terrainBounds.GetYExtent() + }; + + terrainDataForSrg.m_uvMax = { + ((xPatch + GridMeters) - terrainBounds.GetMin().GetX()) / terrainBounds.GetXExtent(), + ((yPatch + GridMeters) - terrainBounds.GetMin().GetY()) / terrainBounds.GetYExtent() + }; + + terrainDataForSrg.m_uvStep = + { + 1.0f / m_areaData.m_heightmapImageWidth, + 1.0f / m_areaData.m_heightmapImageHeight, + }; + + AZ::Transform transform = m_areaData.m_transform; + transform.SetTranslation(xPatch, yPatch, m_areaData.m_transform.GetTranslation().GetZ()); + + terrainDataForSrg.m_sampleSpacing = m_areaData.m_sampleSpacing; + terrainDataForSrg.m_heightScale = terrainBounds.GetZExtent(); + + sectorData.m_srg->SetConstant(m_terrainDataIndex, terrainDataForSrg); + + AZStd::array macroMaterialData; + for (uint32_t i = 0; i < sectorData.m_macroMaterials.size(); ++i) + { + const MacroMaterialData& materialData = m_macroMaterials.GetData(sectorData.m_macroMaterials.at(i)); + ShaderMacroMaterialData& shaderData = macroMaterialData.at(i); + const AZ::Aabb& materialBounds = materialData.m_bounds; + + shaderData.m_uvMin = { + (xPatch - materialBounds.GetMin().GetX()) / materialBounds.GetXExtent(), + (yPatch - materialBounds.GetMin().GetY()) / materialBounds.GetYExtent() + }; + shaderData.m_uvMax = { + ((xPatch + GridMeters) - materialBounds.GetMin().GetX()) / materialBounds.GetXExtent(), + ((yPatch + GridMeters) - materialBounds.GetMin().GetY()) / materialBounds.GetYExtent() + }; + shaderData.m_normalFactor = materialData.m_normalFactor; + shaderData.m_flipNormalX = materialData.m_normalFlipX; + shaderData.m_flipNormalY = materialData.m_normalFlipY; + + const AZ::RHI::ImageView* colorImageView = materialData.m_colorImage ? materialData.m_colorImage->GetImageView() : nullptr; + sectorData.m_srg->SetImageView(m_macroColorMapIndex, colorImageView, i); + + const AZ::RHI::ImageView* normalImageView = materialData.m_normalImage ? materialData.m_normalImage->GetImageView() : nullptr; + sectorData.m_srg->SetImageView(m_macroNormalMapIndex, normalImageView, i); + + // set flags for which images are used. + shaderData.m_mapsInUse = (colorImageView ? ColorImageUsed : 0) | (normalImageView ? NormalImageUsed : 0); } - sectorData.m_aabb = - AZ::Aabb::CreateFromMinMax( - AZ::Vector3(xPatch, yPatch, m_areaData.m_terrainBounds.GetMin().GetZ()), - AZ::Vector3(xPatch + GridMeters, yPatch + GridMeters, m_areaData.m_terrainBounds.GetMax().GetZ()) - ); - sectorData.m_srg = objectSrg; + sectorData.m_srg->SetConstantArray(m_macroMaterialDataIndex, macroMaterialData); + sectorData.m_srg->SetConstant(m_macroMaterialCountIndex, aznumeric_cast(sectorData.m_macroMaterials.size())); + + const AZ::Matrix3x4 matrix3x4 = AZ::Matrix3x4::CreateFromTransform(transform); + sectorData.m_srg->SetConstant(m_modelToWorldIndex, matrix3x4); + + sectorData.m_srg->Compile(); } } } @@ -366,12 +609,20 @@ namespace Terrain { if ((view->GetUsageFlags() & AZ::RPI::View::UsageFlags::UsageCamera) > 0) { - AZ::Vector3 cameraPosition = view->GetCameraTransform().GetTranslation(); - AZ::Vector2 cameraPositionXY = AZ::Vector2(cameraPosition.GetX(), cameraPosition.GetY()); - AZ::Vector2 sectorCenterXY = AZ::Vector2(sectorData.m_aabb.GetCenter().GetX(), sectorData.m_aabb.GetCenter().GetY()); + const AZ::Vector3 cameraPosition = view->GetCameraTransform().GetTranslation(); + const AZ::Vector2 cameraPositionXY = AZ::Vector2(cameraPosition.GetX(), cameraPosition.GetY()); + const AZ::Vector2 sectorCenterXY = AZ::Vector2(sectorData.m_aabb.GetCenter().GetX(), sectorData.m_aabb.GetCenter().GetY()); - float sectorDistance = sectorCenterXY.GetDistance(cameraPositionXY); - float lodForCamera = floorf(AZ::GetMax(0.0f, log2f(sectorDistance / (GridMeters * 4.0f)))); + const float sectorDistance = sectorCenterXY.GetDistance(cameraPositionXY); + + // This will be configurable later + const float minDistanceForLod0 = (GridMeters * 4.0f); + + // 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))); + + // All cameras should render the same LOD so effects like shadows are consistent. lodChoice = AZ::GetMin(lodChoice, aznumeric_cast(lodForCamera)); } } @@ -382,7 +633,7 @@ namespace Terrain AZ::Frustum viewFrustum = AZ::Frustum::CreateFromMatrixColumnMajor(view->GetWorldToClipMatrix()); if (viewFrustum.IntersectAabb(sectorData.m_aabb) != AZ::IntersectResult::Exterior) { - uint8_t lodToRender = AZ::GetMin(lodChoice, aznumeric_cast(sectorData.m_drawPackets.size() - 1)); + const uint8_t lodToRender = AZ::GetMin(lodChoice, aznumeric_cast(sectorData.m_drawPackets.size() - 1)); view->AddDrawPacket(sectorData.m_drawPackets.at(lodToRender).GetRHIDrawPacket()); } } @@ -395,9 +646,8 @@ namespace Terrain patchdata.m_uvs.clear(); patchdata.m_indices.clear(); - uint16_t gridVertices = gridSize + 1; // For m_gridSize quads, (m_gridSize + 1) vertices are needed. - size_t size = gridVertices * gridVertices; - size *= size; + const uint16_t gridVertices = gridSize + 1; // For m_gridSize quads, (m_gridSize + 1) vertices are needed. + const size_t size = gridVertices * gridVertices; patchdata.m_positions.reserve(size); patchdata.m_uvs.reserve(size); @@ -417,10 +667,10 @@ namespace Terrain { for (uint16_t x = 0; x < gridSize; ++x) { - uint16_t topLeft = y * gridVertices + x; - uint16_t topRight = topLeft + 1; - uint16_t bottomLeft = (y + 1) * gridVertices + x; - uint16_t bottomRight = bottomLeft + 1; + const uint16_t topLeft = y * gridVertices + x; + const uint16_t topRight = topLeft + 1; + const uint16_t bottomLeft = (y + 1) * gridVertices + x; + const uint16_t bottomRight = bottomLeft + 1; patchdata.m_indices.emplace_back(topLeft); patchdata.m_indices.emplace_back(topRight); @@ -469,14 +719,14 @@ namespace Terrain PatchData patchData; InitializeTerrainPatch(gridSize, gridSpacing, patchData); - auto positionBufferViewDesc = AZ::RHI::BufferViewDescriptor::CreateTyped(0, aznumeric_cast(patchData.m_positions.size()), AZ::RHI::Format::R32G32_FLOAT); - auto positionsOutcome = CreateBufferAsset(patchData.m_positions.data(), positionBufferViewDesc, "TerrainPatchPositions"); + const auto positionBufferViewDesc = AZ::RHI::BufferViewDescriptor::CreateTyped(0, aznumeric_cast(patchData.m_positions.size()), AZ::RHI::Format::R32G32_FLOAT); + const auto positionsOutcome = CreateBufferAsset(patchData.m_positions.data(), positionBufferViewDesc, "TerrainPatchPositions"); - auto uvBufferViewDesc = AZ::RHI::BufferViewDescriptor::CreateTyped(0, aznumeric_cast(patchData.m_uvs.size()), AZ::RHI::Format::R32G32_FLOAT); - auto uvsOutcome = CreateBufferAsset(patchData.m_uvs.data(), uvBufferViewDesc, "TerrainPatchUvs"); + const auto uvBufferViewDesc = AZ::RHI::BufferViewDescriptor::CreateTyped(0, aznumeric_cast(patchData.m_uvs.size()), AZ::RHI::Format::R32G32_FLOAT); + const auto uvsOutcome = CreateBufferAsset(patchData.m_uvs.data(), uvBufferViewDesc, "TerrainPatchUvs"); - auto indexBufferViewDesc = AZ::RHI::BufferViewDescriptor::CreateTyped(0, aznumeric_cast(patchData.m_indices.size()), AZ::RHI::Format::R16_UINT); - auto indicesOutcome = CreateBufferAsset(patchData.m_indices.data(), indexBufferViewDesc, "TerrainPatchIndices"); + const auto indexBufferViewDesc = AZ::RHI::BufferViewDescriptor::CreateTyped(0, aznumeric_cast(patchData.m_indices.size()), AZ::RHI::Format::R16_UINT); + const auto indicesOutcome = CreateBufferAsset(patchData.m_indices.data(), indexBufferViewDesc, "TerrainPatchIndices"); if (!positionsOutcome.IsSuccess() || !uvsOutcome.IsSuccess() || !indicesOutcome.IsSuccess()) { @@ -514,7 +764,7 @@ namespace Terrain return success; } - void TerrainFeatureProcessor::OnMaterialReinitialized([[maybe_unused]] const AZ::Data::Instance& material) + void TerrainFeatureProcessor::OnMaterialReinitialized([[maybe_unused]] const MaterialInstance& material) { for (auto& sectorData : m_sectorData) { @@ -530,4 +780,57 @@ namespace Terrain // This will control the max rendering size. Actual terrain size can be much // larger but this will limit how much is rendered. } + + TerrainFeatureProcessor::MacroMaterialData* TerrainFeatureProcessor::FindMacroMaterial(AZ::EntityId entityId) + { + for (MacroMaterialData& data : m_macroMaterials.GetDataVector()) + { + if (data.m_entityId == entityId) + { + return &data; + } + } + return nullptr; + } + + TerrainFeatureProcessor::MacroMaterialData& TerrainFeatureProcessor::FindOrCreateMacroMaterial(AZ::EntityId entityId) + { + MacroMaterialData* dataPtr = FindMacroMaterial(entityId); + if (dataPtr != nullptr) + { + return *dataPtr; + } + + const uint16_t slotId = m_macroMaterials.GetFreeSlotIndex(); + AZ_Assert(slotId != m_macroMaterials.NoFreeSlot, "Ran out of indices for macro materials"); + + MacroMaterialData& data = m_macroMaterials.GetData(slotId); + data.m_entityId = entityId; + return data; + } + + void TerrainFeatureProcessor::RemoveMacroMaterial(AZ::EntityId entityId) + { + for (MacroMaterialData& data : m_macroMaterials.GetDataVector()) + { + if (data.m_entityId == entityId) + { + m_macroMaterials.RemoveData(&data); + return; + } + } + AZ_Assert(false, "Entity Id not found in m_macroMaterials.") + } + + template + void TerrainFeatureProcessor::ForOverlappingSectors(const AZ::Aabb& bounds, Callback callback) + { + for (SectorData& sectorData : m_sectorData) + { + if (sectorData.m_aabb.Overlaps(bounds)) + { + callback(sectorData); + } + } + } } diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.h b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.h index d8df9b328c..d9f15f2d47 100644 --- a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.h +++ b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.h @@ -11,11 +11,13 @@ #include #include +#include #include #include #include #include +#include namespace AZ::RPI { @@ -25,6 +27,7 @@ namespace AZ::RPI } class Material; class Model; + class StreamingImage; } namespace Terrain @@ -33,6 +36,7 @@ namespace Terrain : public AZ::RPI::FeatureProcessor , private AZ::RPI::MaterialReloadNotificationBus::Handler , private AzFramework::Terrain::TerrainDataNotificationBus::Handler + , private TerrainMacroMaterialNotificationBus::Handler { public: AZ_RTTI(TerrainFeatureProcessor, "{D7DAC1F9-4A9F-4D3C-80AE-99579BF8AB1C}", AZ::RPI::FeatureProcessor); @@ -52,6 +56,15 @@ namespace Terrain void SetWorldSize(AZ::Vector2 sizeInMeters); private: + + using MaterialInstance = AZ::Data::Instance; + static constexpr uint32_t MaxMaterialsPerSector = 4; + + enum MacroMaterialFlags + { + ColorImageUsed = 0b01, + NormalImageUsed = 0b10, + }; struct ShaderTerrainData // Must align with struct in Object Srg { @@ -61,7 +74,17 @@ namespace Terrain float m_sampleSpacing; float m_heightScale; }; - + + struct ShaderMacroMaterialData + { + AZStd::array m_uvMin; + AZStd::array m_uvMax; + float m_normalFactor; + uint32_t m_flipNormalX{ 0 }; // bool in shader + uint32_t m_flipNormalY{ 0 }; // bool in shader + uint32_t m_mapsInUse{ 0b00 }; // 0b01 = color, 0b10 = normal + }; + struct VertexPosition { float m_posx; @@ -81,21 +104,56 @@ namespace Terrain AZStd::vector m_indices; }; + struct SectorData + { + AZ::Data::Instance m_srg; // Hold on to ref so it's not dropped + AZ::Aabb m_aabb; + AZStd::fixed_vector m_drawPackets; + AZStd::fixed_vector m_macroMaterials; + }; + + struct MacroMaterialData + { + AZ::EntityId m_entityId; + AZ::Aabb m_bounds = AZ::Aabb::CreateNull(); + + AZ::Data::Instance m_colorImage; + AZ::Data::Instance m_normalImage; + bool m_normalFlipX{ false }; + bool m_normalFlipY{ false }; + float m_normalFactor{ 0.0f }; + }; + // AZ::RPI::MaterialReloadNotificationBus::Handler overrides... - void OnMaterialReinitialized(const AZ::Data::Instance& material) override; + void OnMaterialReinitialized(const MaterialInstance& material) override; // AzFramework::Terrain::TerrainDataNotificationBus overrides... void OnTerrainDataDestroyBegin() override; void OnTerrainDataChanged(const AZ::Aabb& dirtyRegion, TerrainDataChangedMask dataChangedMask) override; + // TerrainMacroMaterialNotificationBus overrides... + void OnTerrainMacroMaterialCreated(AZ::EntityId entityId, MaterialInstance material, const AZ::Aabb& region) override; + void OnTerrainMacroMaterialChanged(AZ::EntityId entityId, MaterialInstance material) override; + void OnTerrainMacroMaterialRegionChanged(AZ::EntityId entityId, const AZ::Aabb& oldRegion, const AZ::Aabb& newRegion) override; + void OnTerrainMacroMaterialDestroyed(AZ::EntityId entityId) override; + void Initialize(); void InitializeTerrainPatch(uint16_t gridSize, float gridSpacing, PatchData& patchdata); bool InitializePatchModel(); void UpdateTerrainData(); + void PrepareMaterialData(); + void UpdateMacroMaterialData(MacroMaterialData& macroMaterialData, MaterialInstance material); void ProcessSurfaces(const FeatureProcessor::RenderPacket& process); + MacroMaterialData* FindMacroMaterial(AZ::EntityId entityId); + MacroMaterialData& FindOrCreateMacroMaterial(AZ::EntityId entityId); + void RemoveMacroMaterial(AZ::EntityId entityId); + + template + void ForOverlappingSectors(const AZ::Aabb& bounds, Callback callback); + AZ::Outcome> CreateBufferAsset( const void* data, const AZ::RHI::BufferViewDescriptor& bufferViewDescriptor, const AZStd::string& bufferName); @@ -105,10 +163,15 @@ namespace Terrain static constexpr float GridMeters{ GridSpacing * GridSize }; AZStd::unique_ptr m_materialAssetLoader; - AZ::Data::Instance m_materialInstance; + MaterialInstance m_materialInstance; AZ::RHI::ShaderInputConstantIndex m_modelToWorldIndex; AZ::RHI::ShaderInputConstantIndex m_terrainDataIndex; + AZ::RHI::ShaderInputConstantIndex m_macroMaterialDataIndex; + AZ::RHI::ShaderInputConstantIndex m_macroMaterialCountIndex; + AZ::RHI::ShaderInputImageIndex m_macroColorMapIndex; + AZ::RHI::ShaderInputImageIndex m_macroNormalMapIndex; + AZ::RPI::MaterialPropertyIndex m_heightmapPropertyIndex; AZ::Data::Instance m_patchModel; @@ -117,26 +180,22 @@ namespace Terrain { AZ::Transform m_transform{ AZ::Transform::CreateIdentity() }; AZ::Aabb m_terrainBounds{ AZ::Aabb::CreateNull() }; - float m_heightScale{ 0.0f }; AZ::Data::Instance m_heightmapImage; uint32_t m_heightmapImageWidth{ 0 }; uint32_t m_heightmapImageHeight{ 0 }; uint32_t m_updateWidth{ 0 }; uint32_t m_updateHeight{ 0 }; - bool m_propertiesDirty{ true }; float m_sampleSpacing{ 0.0f }; + bool m_heightmapUpdated{ true }; + bool m_macroMaterialsUpdated{ true }; + bool m_rebuildSectors{ true }; }; TerrainAreaData m_areaData; AZ::Aabb m_dirtyRegion{ AZ::Aabb::CreateNull() }; - struct SectorData - { - AZ::Data::Instance m_srg; // Hold on to ref so it's not dropped - AZ::Aabb m_aabb; - AZStd::fixed_vector m_drawPackets; - }; - AZStd::vector m_sectorData; + + AZ::Render::IndexedDataVector m_macroMaterials; }; } From 2474ffc5a1fb7ed5a6d5df753034e196fc9732f0 Mon Sep 17 00:00:00 2001 From: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> Date: Wed, 13 Oct 2021 16:59:46 -0500 Subject: [PATCH 53/65] Fixed contains check in the Global AnimNode Map macros (#4674) Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> --- Gems/LyShine/Code/Source/Animation/UiAnimationSystem.cpp | 4 ++-- Gems/Maestro/Code/Source/Cinematics/Movie.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Gems/LyShine/Code/Source/Animation/UiAnimationSystem.cpp b/Gems/LyShine/Code/Source/Animation/UiAnimationSystem.cpp index cab6ae3fa6..ab45042b4c 100644 --- a/Gems/LyShine/Code/Source/Animation/UiAnimationSystem.cpp +++ b/Gems/LyShine/Code/Source/Animation/UiAnimationSystem.cpp @@ -37,11 +37,11 @@ namespace using UiAnimSystemUnorderedMap = AZStd::unordered_map; } // Serialization for anim nodes & param types -#define REGISTER_NODE_TYPE(name) assert(g_animNodeEnumToStringMap.contains(eUiAnimNodeType_ ## name)); \ +#define REGISTER_NODE_TYPE(name) assert(!g_animNodeEnumToStringMap.contains(eUiAnimNodeType_ ## name)); \ g_animNodeEnumToStringMap[eUiAnimNodeType_ ## name] = AZ_STRINGIZE(name); \ g_animNodeStringToEnumMap[UiAnimParamSystemString(AZ_STRINGIZE(name))] = eUiAnimNodeType_ ## name; -#define REGISTER_PARAM_TYPE(name) assert(g_animParamEnumToStringMap.contains(eUiAnimParamType_ ## name)); \ +#define REGISTER_PARAM_TYPE(name) assert(!g_animParamEnumToStringMap.contains(eUiAnimParamType_ ## name)); \ g_animParamEnumToStringMap[eUiAnimParamType_ ## name] = AZ_STRINGIZE(name); \ g_animParamStringToEnumMap[UiAnimParamSystemString(AZ_STRINGIZE(name))] = eUiAnimParamType_ ## name; diff --git a/Gems/Maestro/Code/Source/Cinematics/Movie.cpp b/Gems/Maestro/Code/Source/Cinematics/Movie.cpp index 9c4356560d..2a9a231177 100644 --- a/Gems/Maestro/Code/Source/Cinematics/Movie.cpp +++ b/Gems/Maestro/Code/Source/Cinematics/Movie.cpp @@ -87,11 +87,11 @@ namespace } // Serialization for anim nodes & param types -#define REGISTER_NODE_TYPE(name) assert(g_animNodeEnumToStringMap.contains(AnimNodeType::name)); \ +#define REGISTER_NODE_TYPE(name) assert(!g_animNodeEnumToStringMap.contains(AnimNodeType::name)); \ g_animNodeEnumToStringMap[AnimNodeType::name] = AZ_STRINGIZE(name); \ g_animNodeStringToEnumMap[AnimParamSystemString(AZ_STRINGIZE(name))] = AnimNodeType::name; -#define REGISTER_PARAM_TYPE(name) assert(g_animParamEnumToStringMap.contains(AnimParamType::name)); \ +#define REGISTER_PARAM_TYPE(name) assert(!g_animParamEnumToStringMap.contains(AnimParamType::name)); \ g_animParamEnumToStringMap[AnimParamType::name] = AZ_STRINGIZE(name); \ g_animParamStringToEnumMap[AnimParamSystemString(AZ_STRINGIZE(name))] = AnimParamType::name; From 6823ea22740e8c26b5d417772cdf75db2c8e4fed Mon Sep 17 00:00:00 2001 From: Vincent Liu <5900509+onecent1101@users.noreply.github.com> Date: Wed, 13 Oct 2021 16:59:37 -0700 Subject: [PATCH 54/65] Add GameLift matchmaking backfill server support (#4622) * Add GameLift matchmaking backfill server support Signed-off-by: onecent1101 --- .../AzFramework/Session/SessionConfig.cpp | 3 + .../AzFramework/Session/SessionConfig.h | 3 + .../Session/SessionNotifications.h | 5 + .../Code/AWSGameLiftClient/CMakeLists.txt | 6 +- .../AWSGameLiftStartMatchmakingRequest.h | 30 +- .../AWSGameLiftClientSystemComponent.cpp | 1 + .../AWSGameLiftSearchSessionsActivity.cpp | 1 + .../AWSGameLiftStartMatchmakingActivity.cpp | 5 +- .../AWSGameLiftStartMatchmakingRequest.cpp | 45 +- .../Tests/AWSGameLiftClientManagerTest.cpp | 7 +- ...WSGameLiftStartMatchmakingActivityTest.cpp | 13 +- .../awsgamelift_client_files.cmake | 2 + .../Include/AWSGameLiftPlayer.h | 44 ++ .../Source/AWSGameLiftPlayer.cpp | 58 ++ .../Code/AWSGameLiftServer/CMakeLists.txt | 3 + .../Request/IAWSGameLiftServerRequests.h | 21 +- .../Source/AWSGameLiftServerManager.cpp | 378 ++++++++++++- .../Source/AWSGameLiftServerManager.h | 75 ++- .../Source/GameLiftServerSDKWrapper.cpp | 19 + .../Source/GameLiftServerSDKWrapper.h | 19 + .../Tests/AWSGameLiftServerManagerTest.cpp | 516 ++++++++++++++++++ .../Tests/AWSGameLiftServerMocks.h | 23 + .../awsgamelift_server_files.cmake | 2 + .../Source/MultiplayerSystemComponent.cpp | 6 + .../Code/Source/MultiplayerSystemComponent.h | 1 + 25 files changed, 1194 insertions(+), 92 deletions(-) create mode 100644 Gems/AWSGameLift/Code/AWSGameLiftCommon/Include/AWSGameLiftPlayer.h create mode 100644 Gems/AWSGameLift/Code/AWSGameLiftCommon/Source/AWSGameLiftPlayer.cpp diff --git a/Code/Framework/AzFramework/AzFramework/Session/SessionConfig.cpp b/Code/Framework/AzFramework/AzFramework/Session/SessionConfig.cpp index 0c879a930f..0856ddeed6 100644 --- a/Code/Framework/AzFramework/AzFramework/Session/SessionConfig.cpp +++ b/Code/Framework/AzFramework/AzFramework/Session/SessionConfig.cpp @@ -22,6 +22,7 @@ namespace AzFramework ->Field("terminationTime", &SessionConfig::m_terminationTime) ->Field("creatorId", &SessionConfig::m_creatorId) ->Field("sessionProperties", &SessionConfig::m_sessionProperties) + ->Field("matchmakingData", &SessionConfig::m_matchmakingData) ->Field("sessionId", &SessionConfig::m_sessionId) ->Field("sessionName", &SessionConfig::m_sessionName) ->Field("dnsName", &SessionConfig::m_dnsName) @@ -46,6 +47,8 @@ namespace AzFramework "CreatorId", "A unique identifier for a player or entity creating the session.") ->DataElement(AZ::Edit::UIHandlers::Default, &AzFramework::SessionConfig::m_sessionProperties, "SessionProperties", "A collection of custom properties for a session.") + ->DataElement(AZ::Edit::UIHandlers::Default, &AzFramework::SessionConfig::m_matchmakingData, + "MatchmakingData", "The matchmaking process information that was used to create the session.") ->DataElement(AZ::Edit::UIHandlers::Default, &AzFramework::SessionConfig::m_sessionId, "SessionId", "A unique identifier for the session.") ->DataElement(AZ::Edit::UIHandlers::Default, &AzFramework::SessionConfig::m_sessionName, diff --git a/Code/Framework/AzFramework/AzFramework/Session/SessionConfig.h b/Code/Framework/AzFramework/AzFramework/Session/SessionConfig.h index cfd6aa7c8b..45e40c2f29 100644 --- a/Code/Framework/AzFramework/AzFramework/Session/SessionConfig.h +++ b/Code/Framework/AzFramework/AzFramework/Session/SessionConfig.h @@ -35,6 +35,9 @@ namespace AzFramework // A collection of custom properties for a session. AZStd::unordered_map m_sessionProperties; + + // The matchmaking process information that was used to create the session. + AZStd::string m_matchmakingData; // A unique identifier for the session. AZStd::string m_sessionId; diff --git a/Code/Framework/AzFramework/AzFramework/Session/SessionNotifications.h b/Code/Framework/AzFramework/AzFramework/Session/SessionNotifications.h index 7788c2d030..902500fe9a 100644 --- a/Code/Framework/AzFramework/AzFramework/Session/SessionNotifications.h +++ b/Code/Framework/AzFramework/AzFramework/Session/SessionNotifications.h @@ -41,6 +41,11 @@ namespace AzFramework // OnDestroySessionBegin is fired at the beginning of session termination // @return The result of all OnDestroySessionBegin notifications virtual bool OnDestroySessionBegin() = 0; + + // OnUpdateSessionBegin is fired at the beginning of session update + // @param sessionConfig The properties to describe a session + // @param updateReason The reason for session update + virtual void OnUpdateSessionBegin(const SessionConfig& sessionConfig, const AZStd::string& updateReason) = 0; }; using SessionNotificationBus = AZ::EBus; } // namespace AzFramework diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/CMakeLists.txt b/Gems/AWSGameLift/Code/AWSGameLiftClient/CMakeLists.txt index 3eb5eafab0..1fab09e9f4 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/CMakeLists.txt +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/CMakeLists.txt @@ -15,10 +15,11 @@ ly_add_target( awsgamelift_client_files.cmake INCLUDE_DIRECTORIES PUBLIC + ../AWSGameLiftCommon/Include Include PRIVATE - Source ../AWSGameLiftCommon/Source + Source COMPILE_DEFINITIONS PRIVATE ${awsgameliftclient_compile_definition} @@ -78,10 +79,11 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) awsgamelift_client_tests_files.cmake INCLUDE_DIRECTORIES PRIVATE + ../AWSGameLiftCommon/Include + ../AWSGameLiftCommon/Source Include Tests Source - ../AWSGameLiftCommon/Source BUILD_DEPENDENCIES PRIVATE AZ::AzCore diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Include/Request/AWSGameLiftStartMatchmakingRequest.h b/Gems/AWSGameLift/Code/AWSGameLiftClient/Include/Request/AWSGameLiftStartMatchmakingRequest.h index d734daa808..ec3719c6d3 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Include/Request/AWSGameLiftStartMatchmakingRequest.h +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Include/Request/AWSGameLiftStartMatchmakingRequest.h @@ -10,36 +10,12 @@ #include #include - #include +#include + namespace AWSGameLift { - //! AWSGameLiftPlayerInformation - //! Information on each player to be matched - //! This information must include a player ID, and may contain player attributes and latency data to be used in the matchmaking process - //! After a successful match, Player objects contain the name of the team the player is assigned to - struct AWSGameLiftPlayerInformation - { - AZ_RTTI(AWSGameLiftPlayerInformation, "{B62C118E-C55D-4903-8ECB-E58E8CA613C4}"); - static void Reflect(AZ::ReflectContext* context); - - AWSGameLiftPlayerInformation() = default; - virtual ~AWSGameLiftPlayerInformation() = default; - - // A map of region names to latencies in millseconds, that indicates - // the amount of latency that a player experiences when connected to AWS Regions - AZStd::unordered_map m_latencyInMs; - // A collection of key:value pairs containing player information for use in matchmaking - // Player attribute keys must match the playerAttributes used in a matchmaking rule set - // Example: {"skill": "{\"N\": \"23\"}", "gameMode": "{\"S\": \"deathmatch\"}"} - AZStd::unordered_map m_playerAttributes; - // A unique identifier for a player - AZStd::string m_playerId; - // Name of the team that the player is assigned to in a match - AZStd::string m_team; - }; - //! AWSGameLiftStartMatchmakingRequest //! GameLift start matchmaking request which corresponds to Amazon GameLift //! Uses FlexMatch to create a game match for a group of players based on custom matchmaking rules @@ -57,6 +33,6 @@ namespace AWSGameLift // Name of the matchmaking configuration to use for this request AZStd::string m_configurationName; // Information on each player to be matched - AZStd::vector m_players; + AZStd::vector m_players; }; } // namespace AWSGameLift diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientSystemComponent.cpp b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientSystemComponent.cpp index ea5fff99a3..ed7830ce57 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientSystemComponent.cpp +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientSystemComponent.cpp @@ -210,6 +210,7 @@ namespace AWSGameLift ->Property("SessionId", BehaviorValueProperty(&AzFramework::SessionConfig::m_sessionId)) ->Property("SessionName", BehaviorValueProperty(&AzFramework::SessionConfig::m_sessionName)) ->Property("SessionProperties", BehaviorValueProperty(&AzFramework::SessionConfig::m_sessionProperties)) + ->Property("MatchmakingData", BehaviorValueProperty(&AzFramework::SessionConfig::m_matchmakingData)) ->Property("Status", BehaviorValueProperty(&AzFramework::SessionConfig::m_status)) ->Property("StatusReason", BehaviorValueProperty(&AzFramework::SessionConfig::m_statusReason)) ->Property("TerminationTime", BehaviorValueProperty(&AzFramework::SessionConfig::m_terminationTime)) diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftSearchSessionsActivity.cpp b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftSearchSessionsActivity.cpp index 3d29fa2b71..5f0b6fd012 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftSearchSessionsActivity.cpp +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftSearchSessionsActivity.cpp @@ -105,6 +105,7 @@ namespace AWSGameLift session.m_status = AWSGameLiftSessionStatusNames[(int)gameSession.GetStatus()]; session.m_statusReason = AWSGameLiftSessionStatusReasons[(int)gameSession.GetStatusReason()]; session.m_terminationTime = gameSession.GetTerminationTime().Millis(); + session.m_matchmakingData = gameSession.GetMatchmakerData().c_str(); // TODO: Update the AWS Native SDK to get the new game session attributes. //session.m_dnsName = gameSession.GetDnsName(); diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftStartMatchmakingActivity.cpp b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftStartMatchmakingActivity.cpp index e535ea0c55..00a7773491 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftStartMatchmakingActivity.cpp +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftStartMatchmakingActivity.cpp @@ -10,6 +10,7 @@ #include #include +#include #include #include @@ -29,7 +30,7 @@ namespace AWSGameLift } Aws::Vector players; - for (const AWSGameLiftPlayerInformation& playerInfo : startMatchmakingRequest.m_players) + for (const AWSGameLiftPlayer& playerInfo : startMatchmakingRequest.m_players) { Aws::GameLift::Model::Player player; if (!playerInfo.m_playerId.empty()) @@ -109,7 +110,7 @@ namespace AWSGameLift if (isValid) { - for (const AWSGameLiftPlayerInformation& playerInfo : gameliftStartMatchmakingRequest->m_players) + for (const AWSGameLiftPlayer& playerInfo : gameliftStartMatchmakingRequest->m_players) { isValid &= !playerInfo.m_playerId.empty(); isValid &= AWSGameLiftActivityUtils::ValidatePlayerAttributes(playerInfo.m_playerAttributes); diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Request/AWSGameLiftStartMatchmakingRequest.cpp b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Request/AWSGameLiftStartMatchmakingRequest.cpp index 03d802fd36..31e9c5eff7 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Request/AWSGameLiftStartMatchmakingRequest.cpp +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Request/AWSGameLiftStartMatchmakingRequest.cpp @@ -14,53 +14,10 @@ namespace AWSGameLift { - void AWSGameLiftPlayerInformation::Reflect(AZ::ReflectContext* context) - { - if (auto serializeContext = azrtti_cast(context)) - { - serializeContext->Class() - ->Version(0) - ->Field("latencyInMs", &AWSGameLiftPlayerInformation::m_latencyInMs) - ->Field("playerAttributes", &AWSGameLiftPlayerInformation::m_playerAttributes) - ->Field("playerId", &AWSGameLiftPlayerInformation::m_playerId) - ->Field("team", &AWSGameLiftPlayerInformation::m_team); - - if (AZ::EditContext* editContext = serializeContext->GetEditContext()) - { - editContext->Class("AWSGameLiftPlayerInformation", "") - ->ClassElement(AZ::Edit::ClassElements::EditorData, "") - ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) - ->DataElement( - AZ::Edit::UIHandlers::Default, &AWSGameLiftPlayerInformation::m_latencyInMs, "LatencyInMs", - "A set of values, expressed in milliseconds, that indicates the amount of latency that" - "a player experiences when connected to AWS Regions") - ->DataElement( - AZ::Edit::UIHandlers::Default, &AWSGameLiftPlayerInformation::m_playerAttributes, "PlayerAttributes", - "A collection of key:value pairs containing player information for use in matchmaking") - ->DataElement( - AZ::Edit::UIHandlers::Default, &AWSGameLiftPlayerInformation::m_playerId, "PlayerId", - "A unique identifier for a player") - ->DataElement( - AZ::Edit::UIHandlers::Default, &AWSGameLiftPlayerInformation::m_team, "Team", - "Name of the team that the player is assigned to in a match"); - } - } - - if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) - { - behaviorContext->Class("AWSGameLiftPlayerInformation") - ->Attribute(AZ::Script::Attributes::Storage, AZ::Script::Attributes::StorageType::Value) - ->Property("LatencyInMs", BehaviorValueProperty(&AWSGameLiftPlayerInformation::m_latencyInMs)) - ->Property("PlayerAttributes", BehaviorValueProperty(&AWSGameLiftPlayerInformation::m_playerAttributes)) - ->Property("PlayerId", BehaviorValueProperty(&AWSGameLiftPlayerInformation::m_playerId)) - ->Property("Team", BehaviorValueProperty(&AWSGameLiftPlayerInformation::m_team)); - } - } - void AWSGameLiftStartMatchmakingRequest::Reflect(AZ::ReflectContext* context) { AzFramework::StartMatchmakingRequest::Reflect(context); - AWSGameLiftPlayerInformation::Reflect(context); + AWSGameLiftPlayer::Reflect(context); if (auto serializeContext = azrtti_cast(context)) { diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientManagerTest.cpp b/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientManagerTest.cpp index 3b7c7ee827..1c3e8726fd 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientManagerTest.cpp +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientManagerTest.cpp @@ -208,6 +208,7 @@ protected: sessionConfig.m_terminationTime = 0; sessionConfig.m_creatorId = "dummyCreatorId"; sessionConfig.m_sessionProperties["dummyKey"] = "dummyValue"; + sessionConfig.m_matchmakingData = "dummyMatchmakingData"; sessionConfig.m_sessionId = "dummyGameSessionId"; sessionConfig.m_sessionName = "dummyGameSessionName"; sessionConfig.m_ipAddress = "dummyIpAddress"; @@ -232,7 +233,7 @@ protected: request.m_configurationName = "dummyConfiguration"; request.m_ticketId = DummyMatchmakingTicketId; - AWSGameLiftPlayerInformation player; + AWSGameLiftPlayer player; player.m_playerAttributes["dummy"] = "{\"N\": \"1\"}"; player.m_playerId = DummyPlayerId; player.m_latencyInMs["us-east-1"] = 10; @@ -813,7 +814,7 @@ TEST_F(AWSGameLiftClientManagerTest, StartMatchmaking_CallWithInvalidRequest_Get { AWSGameLiftStartMatchmakingRequest request; request.m_configurationName = "dummyConfiguration"; - AWSGameLiftPlayerInformation player; + AWSGameLiftPlayer player; player.m_playerAttributes["dummy"] = "{\"A\": \"1\"}"; request.m_players.emplace_back(player); @@ -855,7 +856,7 @@ TEST_F(AWSGameLiftClientManagerTest, StartMatchmakingAsync_CallWithInvalidReques { AWSGameLiftStartMatchmakingRequest request; request.m_configurationName = "dummyConfiguration"; - AWSGameLiftPlayerInformation player; + AWSGameLiftPlayer player; player.m_playerAttributes["dummy"] = "{\"A\": \"1\"}"; request.m_players.emplace_back(player); diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/Activity/AWSGameLiftStartMatchmakingActivityTest.cpp b/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/Activity/AWSGameLiftStartMatchmakingActivityTest.cpp index 3eed6b0448..6da932828c 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/Activity/AWSGameLiftStartMatchmakingActivityTest.cpp +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/Activity/AWSGameLiftStartMatchmakingActivityTest.cpp @@ -8,6 +8,7 @@ #include #include +#include #include @@ -21,7 +22,7 @@ TEST_F(AWSGameLiftStartMatchmakingActivityTest, BuildAWSGameLiftStartMatchmaking request.m_configurationName = "dummyConfiguration"; request.m_ticketId = "dummyTicketId"; - AWSGameLiftPlayerInformation player; + AWSGameLiftPlayer player; player.m_playerAttributes["dummy"] = "{\"S\": \"test\"}"; player.m_playerId = "dummyPlayerId"; player.m_team = "dummyTeam"; @@ -58,7 +59,7 @@ TEST_F(AWSGameLiftStartMatchmakingActivityTest, ValidateStartMatchmakingRequest_ AWSGameLiftStartMatchmakingRequest request; request.m_ticketId = "dummyTicketId"; - AWSGameLiftPlayerInformation player; + AWSGameLiftPlayer player; player.m_playerAttributes["dummy"] = "{\"S\": \"test\"}"; player.m_playerId = "dummyPlayerId"; player.m_team = "dummyTeam"; @@ -89,7 +90,7 @@ TEST_F(AWSGameLiftStartMatchmakingActivityTest, ValidateStartMatchmakingRequest_ request.m_configurationName = "dummyConfiguration"; request.m_ticketId = "dummyTicketId"; - AWSGameLiftPlayerInformation player; + AWSGameLiftPlayer player; player.m_playerAttributes["dummy"] = "{\"S\": \"test\"}"; player.m_team = "dummyTeam"; player.m_latencyInMs["us-east-1"] = 10; @@ -107,7 +108,7 @@ TEST_F(AWSGameLiftStartMatchmakingActivityTest, ValidateStartMatchmakingRequest_ request.m_configurationName = "dummyConfiguration"; request.m_ticketId = "dummyTicketId"; - AWSGameLiftPlayerInformation player; + AWSGameLiftPlayer player; player.m_playerAttributes["dummy"] = "{\"A\": \"test\"}"; player.m_playerId = "dummyPlayerId"; player.m_team = "dummyTeam"; @@ -125,7 +126,7 @@ TEST_F(AWSGameLiftStartMatchmakingActivityTest, ValidateStartMatchmakingRequest_ AWSGameLiftStartMatchmakingRequest request; request.m_configurationName = "dummyConfiguration"; - AWSGameLiftPlayerInformation player; + AWSGameLiftPlayer player; player.m_playerAttributes["dummy"] = "{\"S\": \"test\"}"; player.m_playerId = "dummyPlayerId"; player.m_team = "dummyTeam"; @@ -142,7 +143,7 @@ TEST_F(AWSGameLiftStartMatchmakingActivityTest, ValidateStartMatchmakingRequest_ request.m_ticketId = "dummyTicketId"; request.m_configurationName = "dummyConfiguration"; - AWSGameLiftPlayerInformation player; + AWSGameLiftPlayer player; player.m_playerAttributes["dummy"] = "{\"S\": \"test\"}"; player.m_playerId = "dummyPlayerId"; player.m_team = "dummyTeam"; diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/awsgamelift_client_files.cmake b/Gems/AWSGameLift/Code/AWSGameLiftClient/awsgamelift_client_files.cmake index 0930c42370..cd54414cc1 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/awsgamelift_client_files.cmake +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/awsgamelift_client_files.cmake @@ -7,6 +7,8 @@ # set(FILES + ../AWSGameLiftCommon/Include/AWSGameLiftPlayer.h + ../AWSGameLiftCommon/Source/AWSGameLiftPlayer.cpp ../AWSGameLiftCommon/Source/AWSGameLiftSessionConstants.h Include/Request/AWSGameLiftAcceptMatchRequest.h Include/Request/AWSGameLiftCreateSessionOnQueueRequest.h diff --git a/Gems/AWSGameLift/Code/AWSGameLiftCommon/Include/AWSGameLiftPlayer.h b/Gems/AWSGameLift/Code/AWSGameLiftCommon/Include/AWSGameLiftPlayer.h new file mode 100644 index 0000000000..1fbdadc8bf --- /dev/null +++ b/Gems/AWSGameLift/Code/AWSGameLiftCommon/Include/AWSGameLiftPlayer.h @@ -0,0 +1,44 @@ +/* + * 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 + * + */ + +#pragma once + +#include +#include +#include + +namespace AWSGameLift +{ + //! AWSGameLiftPlayer + //! Information on each player to be matched + //! This information must include a player ID, and may contain player attributes and latency data to be used in the matchmaking process + //! After a successful match, Player objects contain the name of the team the player is assigned to + struct AWSGameLiftPlayer + { + AZ_RTTI(AWSGameLiftPlayer, "{B62C118E-C55D-4903-8ECB-E58E8CA613C4}"); + static void Reflect(AZ::ReflectContext* context); + + AWSGameLiftPlayer() = default; + virtual ~AWSGameLiftPlayer() = default; + + // A map of region names to latencies in millseconds, that indicates + // the amount of latency that a player experiences when connected to AWS Regions + AZStd::unordered_map m_latencyInMs; + + // A collection of key:value pairs containing player information for use in matchmaking + // Player attribute keys must match the playerAttributes used in a matchmaking rule set + // Example: {"skill": "{\"N\": 23}", "gameMode": "{\"S\": \"deathmatch\"}"} + AZStd::unordered_map m_playerAttributes; + + // A unique identifier for a player + AZStd::string m_playerId; + + // Name of the team that the player is assigned to in a match + AZStd::string m_team; + }; +} // namespace AWSGameLift diff --git a/Gems/AWSGameLift/Code/AWSGameLiftCommon/Source/AWSGameLiftPlayer.cpp b/Gems/AWSGameLift/Code/AWSGameLiftCommon/Source/AWSGameLiftPlayer.cpp new file mode 100644 index 0000000000..80a3915a6b --- /dev/null +++ b/Gems/AWSGameLift/Code/AWSGameLiftCommon/Source/AWSGameLiftPlayer.cpp @@ -0,0 +1,58 @@ +/* + * 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 + +namespace AWSGameLift +{ + void AWSGameLiftPlayer::Reflect(AZ::ReflectContext* context) + { + if (auto serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Version(0) + ->Field("latencyInMs", &AWSGameLiftPlayer::m_latencyInMs) + ->Field("playerAttributes", &AWSGameLiftPlayer::m_playerAttributes) + ->Field("playerId", &AWSGameLiftPlayer::m_playerId) + ->Field("team", &AWSGameLiftPlayer::m_team); + + if (AZ::EditContext* editContext = serializeContext->GetEditContext()) + { + editContext->Class("AWSGameLiftPlayer", "") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) + ->DataElement( + AZ::Edit::UIHandlers::Default, &AWSGameLiftPlayer::m_latencyInMs, "LatencyInMs", + "A set of values, expressed in milliseconds, that indicates the amount of latency that" + "a player experiences when connected to AWS Regions") + ->DataElement( + AZ::Edit::UIHandlers::Default, &AWSGameLiftPlayer::m_playerAttributes, "PlayerAttributes", + "A collection of key:value pairs containing player information for use in matchmaking") + ->DataElement( + AZ::Edit::UIHandlers::Default, &AWSGameLiftPlayer::m_playerId, "PlayerId", + "A unique identifier for a player") + ->DataElement( + AZ::Edit::UIHandlers::Default, &AWSGameLiftPlayer::m_team, "Team", + "Name of the team that the player is assigned to in a match"); + } + } + + if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) + { + behaviorContext->Class("AWSGameLiftPlayer") + ->Attribute(AZ::Script::Attributes::Storage, AZ::Script::Attributes::StorageType::Value) + ->Property("LatencyInMs", BehaviorValueProperty(&AWSGameLiftPlayer::m_latencyInMs)) + ->Property("PlayerAttributes", BehaviorValueProperty(&AWSGameLiftPlayer::m_playerAttributes)) + ->Property("PlayerId", BehaviorValueProperty(&AWSGameLiftPlayer::m_playerId)) + ->Property("Team", BehaviorValueProperty(&AWSGameLiftPlayer::m_team)); + } + } +} // namespace AWSGameLift diff --git a/Gems/AWSGameLift/Code/AWSGameLiftServer/CMakeLists.txt b/Gems/AWSGameLift/Code/AWSGameLiftServer/CMakeLists.txt index 798c6a6a25..d05ec46b52 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftServer/CMakeLists.txt +++ b/Gems/AWSGameLift/Code/AWSGameLiftServer/CMakeLists.txt @@ -17,6 +17,7 @@ ly_add_target( awsgamelift_server_files.cmake INCLUDE_DIRECTORIES PUBLIC + ../AWSGameLiftCommon/Include Include PRIVATE ../AWSGameLiftCommon/Source @@ -54,6 +55,8 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) awsgamelift_server_tests_files.cmake INCLUDE_DIRECTORIES PRIVATE + ../AWSGameLiftCommon/Include + ../AWSGameLiftCommon/Source Tests Source BUILD_DEPENDENCIES diff --git a/Gems/AWSGameLift/Code/AWSGameLiftServer/Include/Request/IAWSGameLiftServerRequests.h b/Gems/AWSGameLift/Code/AWSGameLiftServer/Include/Request/IAWSGameLiftServerRequests.h index e5b14319a8..777086e633 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftServer/Include/Request/IAWSGameLiftServerRequests.h +++ b/Gems/AWSGameLift/Code/AWSGameLiftServer/Include/Request/IAWSGameLiftServerRequests.h @@ -9,9 +9,11 @@ #pragma once #include -#include +#include +#include #include -#include + +#include namespace AWSGameLift { @@ -26,8 +28,21 @@ namespace AWSGameLift virtual ~IAWSGameLiftServerRequests() = default; //! Notify GameLift that the server process is ready to host a game session. - //! @return Whether the ProcessReady notification is sent to GameLift. + //! @return True if the ProcessReady notification is sent to GameLift successfully, false otherwise virtual bool NotifyGameLiftProcessReady() = 0; + + //! Sends a request to find new players for open slots in a game session created with FlexMatch. + //! @param ticketId Unique identifier for match backfill request ticket + //! @param players A set of data representing all players who are currently in the game session, + //! if not provided, system will use lazy loaded game session data which is not guaranteed to + //! be accurate (no latency data either) + //! @return True if StartMatchBackfill succeeds, false otherwise + virtual bool StartMatchBackfill(const AZStd::string& ticketId, const AZStd::vector& players) = 0; + + //! Cancels an active match backfill request that was created with StartMatchBackfill + //! @param ticketId Unique identifier of the backfill request ticket to be canceled + //! @return True if StopMatchBackfill succeeds, false otherwise + virtual bool StopMatchBackfill(const AZStd::string& ticketId) = 0; }; // IAWSGameLiftServerRequests EBus wrapper for scripting diff --git a/Gems/AWSGameLift/Code/AWSGameLiftServer/Source/AWSGameLiftServerManager.cpp b/Gems/AWSGameLift/Code/AWSGameLiftServer/Source/AWSGameLiftServerManager.cpp index e739933ab2..70d0063b61 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftServer/Source/AWSGameLiftServerManager.cpp +++ b/Gems/AWSGameLift/Code/AWSGameLiftServer/Source/AWSGameLiftServerManager.cpp @@ -17,6 +17,9 @@ #include #include #include +#include +#include +#include #include #include @@ -112,6 +115,7 @@ namespace AWSGameLift { propertiesOutput = propertiesOutput.substr(0, propertiesOutput.size() - 1); // Trim last comma to fit array format } + sessionConfig.m_matchmakingData = gameSession.GetMatchmakerData().c_str(); sessionConfig.m_sessionId = gameSession.GetGameSessionId().c_str(); sessionConfig.m_ipAddress = gameSession.GetIpAddress().c_str(); sessionConfig.m_maxPlayer = gameSession.GetMaximumPlayerSessionCount(); @@ -133,6 +137,276 @@ namespace AWSGameLift return sessionConfig; } + bool AWSGameLiftServerManager::BuildServerMatchBackfillPlayer( + const AWSGameLiftPlayer& player, Aws::GameLift::Server::Model::Player& outBackfillPlayer) + { + outBackfillPlayer.SetPlayerId(player.m_playerId.c_str()); + outBackfillPlayer.SetTeam(player.m_team.c_str()); + for (auto latencyPair : player.m_latencyInMs) + { + outBackfillPlayer.AddLatencyInMs(latencyPair.first.c_str(), latencyPair.second); + } + + for (auto attributePair : player.m_playerAttributes) + { + Aws::GameLift::Server::Model::AttributeValue playerAttribute; + rapidjson::Document attributeDocument; + rapidjson::ParseResult parseResult = attributeDocument.Parse(attributePair.second.c_str()); + // player attribute json content should always be a single member object + if (parseResult && attributeDocument.IsObject() && attributeDocument.MemberCount() == 1) + { + if ((attributeDocument.HasMember(AWSGameLiftMatchmakingPlayerAttributeSTypeName) || + attributeDocument.HasMember(AWSGameLiftMatchmakingPlayerAttributeSServerTypeName)) && + attributeDocument.MemberBegin()->value.IsString()) + { + playerAttribute = Aws::GameLift::Server::Model::AttributeValue( + attributeDocument.MemberBegin()->value.GetString()); + } + else if ((attributeDocument.HasMember(AWSGameLiftMatchmakingPlayerAttributeNTypeName) || + attributeDocument.HasMember(AWSGameLiftMatchmakingPlayerAttributeNServerTypeName)) && + attributeDocument.MemberBegin()->value.IsNumber()) + { + playerAttribute = Aws::GameLift::Server::Model::AttributeValue( + attributeDocument.MemberBegin()->value.GetDouble()); + } + else if ((attributeDocument.HasMember(AWSGameLiftMatchmakingPlayerAttributeSDMTypeName) || + attributeDocument.HasMember(AWSGameLiftMatchmakingPlayerAttributeSDMServerTypeName)) && + attributeDocument.MemberBegin()->value.IsObject()) + { + playerAttribute = Aws::GameLift::Server::Model::AttributeValue::ConstructStringDoubleMap(); + for (auto iter = attributeDocument.MemberBegin()->value.MemberBegin(); + iter != attributeDocument.MemberBegin()->value.MemberEnd(); iter++) + { + if (iter->name.IsString() && iter->value.IsNumber()) + { + playerAttribute.AddStringAndDouble(iter->name.GetString(), iter->value.GetDouble()); + } + else + { + AZ_Error(AWSGameLiftServerManagerName, false, AWSGameLiftMatchmakingPlayerAttributeInvalidErrorMessage, + player.m_playerId.c_str(), "String double map key must be string type and value must be number type"); + return false; + } + } + } + else if ((attributeDocument.HasMember(AWSGameLiftMatchmakingPlayerAttributeSLTypeName) || + attributeDocument.HasMember(AWSGameLiftMatchmakingPlayerAttributeSLServerTypeName)) && + attributeDocument.MemberBegin()->value.IsArray()) + { + playerAttribute = Aws::GameLift::Server::Model::AttributeValue::ConstructStringList(); + for (auto iter = attributeDocument.MemberBegin()->value.Begin(); + iter != attributeDocument.MemberBegin()->value.End(); iter++) + { + if (iter->IsString()) + { + playerAttribute.AddString(iter->GetString()); + } + else + { + AZ_Error(AWSGameLiftServerManagerName, false, AWSGameLiftMatchmakingPlayerAttributeInvalidErrorMessage, + player.m_playerId.c_str(), "String list element must be string type"); + return false; + } + } + } + else + { + AZ_Error(AWSGameLiftServerManagerName, false, AWSGameLiftMatchmakingPlayerAttributeInvalidErrorMessage, + player.m_playerId.c_str(), "S, N, SDM or SLM is expected as attribute type."); + return false; + } + } + else + { + AZ_Error(AWSGameLiftServerManagerName, false, AWSGameLiftMatchmakingPlayerAttributeInvalidErrorMessage, + player.m_playerId.c_str(), rapidjson::GetParseError_En(parseResult.Code())); + return false; + } + outBackfillPlayer.AddPlayerAttribute(attributePair.first.c_str(), playerAttribute); + } + return true; + } + + AZStd::vector AWSGameLiftServerManager::GetActiveServerMatchBackfillPlayers() + { + AZStd::vector activePlayers; + // Keep processing only when game session has matchmaking data + if (IsMatchmakingDataValid()) + { + auto activePlayerSessions = GetActivePlayerSessions(); + for (auto playerSession : activePlayerSessions) + { + AWSGameLiftPlayer player; + if (BuildActiveServerMatchBackfillPlayer(playerSession.GetPlayerId().c_str(), player)) + { + activePlayers.push_back(player); + } + } + } + return activePlayers; + } + + bool AWSGameLiftServerManager::IsMatchmakingDataValid() + { + return m_matchmakingData.IsObject() && + m_matchmakingData.HasMember(AWSGameLiftMatchmakingConfigurationKeyName) && + m_matchmakingData.HasMember(AWSGameLiftMatchmakingTeamsKeyName); + } + + AZStd::vector AWSGameLiftServerManager::GetActivePlayerSessions() + { + Aws::GameLift::Server::Model::DescribePlayerSessionsRequest describeRequest; + describeRequest.SetGameSessionId(m_gameSession.GetGameSessionId()); + describeRequest.SetPlayerSessionStatusFilter( + Aws::GameLift::Server::Model::PlayerSessionStatusMapper::GetNameForPlayerSessionStatus( + Aws::GameLift::Server::Model::PlayerSessionStatus::ACTIVE)); + int maxPlayerSession = m_gameSession.GetMaximumPlayerSessionCount(); + + AZStd::vector activePlayerSessions; + if (maxPlayerSession <= AWSGameLiftDescribePlayerSessionsPageSize) + { + describeRequest.SetLimit(maxPlayerSession); + auto outcome = m_gameLiftServerSDKWrapper->DescribePlayerSessions(describeRequest); + if (outcome.IsSuccess()) + { + for (auto playerSession : outcome.GetResult().GetPlayerSessions()) + { + activePlayerSessions.push_back(playerSession); + } + } + else + { + AZ_Error(AWSGameLiftServerManagerName, false, AWSGameLiftDescribePlayerSessionsErrorMessage, + outcome.GetError().GetErrorMessage().c_str()); + } + } + else + { + describeRequest.SetLimit(AWSGameLiftDescribePlayerSessionsPageSize); + while (true) + { + auto outcome = m_gameLiftServerSDKWrapper->DescribePlayerSessions(describeRequest); + if (outcome.IsSuccess()) + { + for (auto playerSession : outcome.GetResult().GetPlayerSessions()) + { + activePlayerSessions.push_back(playerSession); + } + if (outcome.GetResult().GetNextToken().empty()) + { + break; + } + else + { + describeRequest.SetNextToken(outcome.GetResult().GetNextToken()); + } + } + else + { + activePlayerSessions.clear(); + AZ_Error(AWSGameLiftServerManagerName, false, AWSGameLiftDescribePlayerSessionsErrorMessage, + outcome.GetError().GetErrorMessage().c_str()); + break; + } + } + } + return activePlayerSessions; + } + + bool AWSGameLiftServerManager::BuildActiveServerMatchBackfillPlayer(const AZStd::string& playerId, AWSGameLiftPlayer& outPlayer) + { + // As data is from GameLift service, assume it is always in correct format + rapidjson::Value& teams = m_matchmakingData[AWSGameLiftMatchmakingTeamsKeyName]; + + // Iterate through teams to find target player + for (rapidjson::SizeType teamIndex = 0; teamIndex < teams.Size(); ++teamIndex) + { + rapidjson::Value& players = teams[teamIndex][AWSGameLiftMatchmakingPlayersKeyName]; + + // Iterate through players under the team to find target player + for (rapidjson::SizeType playerIndex = 0; playerIndex < players.Size(); ++playerIndex) + { + if (std::strcmp(players[playerIndex][AWSGameLiftMatchmakingPlayerIdKeyName].GetString(), playerId.c_str()) == 0) + { + outPlayer.m_playerId = playerId; + outPlayer.m_team = teams[teamIndex][AWSGameLiftMatchmakingTeamNameKeyName].GetString(); + // Get player attributes if target player has + if (players[playerIndex].HasMember(AWSGameLiftMatchmakingPlayerAttributesKeyName)) + { + BuildServerMatchBackfillPlayerAttributes( + players[playerIndex][AWSGameLiftMatchmakingPlayerAttributesKeyName], outPlayer); + } + } + else + { + return false; + } + } + } + return true; + } + + void AWSGameLiftServerManager::BuildServerMatchBackfillPlayerAttributes( + const rapidjson::Value& playerAttributes, AWSGameLiftPlayer& outPlayer) + { + for (auto iter = playerAttributes.MemberBegin(); iter != playerAttributes.MemberEnd(); iter++) + { + AZStd::string attributeName = iter->name.GetString(); + + rapidjson::StringBuffer jsonStringBuffer; + rapidjson::Writer writer(jsonStringBuffer); + iter->value[AWSGameLiftMatchmakingPlayerAttributeValueKeyName].Accept(writer); + AZStd::string attributeType = iter->value[AWSGameLiftMatchmakingPlayerAttributeTypeKeyName].GetString(); + AZStd::string attributeValue = AZStd::string::format("{\"%s\": %s}", + attributeType.c_str(), jsonStringBuffer.GetString()); + + outPlayer.m_playerAttributes.emplace(attributeName, attributeValue); + } + } + + bool AWSGameLiftServerManager::BuildStartMatchBackfillRequest( + const AZStd::string& ticketId, + const AZStd::vector& players, + Aws::GameLift::Server::Model::StartMatchBackfillRequest& outRequest) + { + outRequest.SetGameSessionArn(m_gameSession.GetGameSessionId()); + outRequest.SetMatchmakingConfigurationArn(m_matchmakingData[AWSGameLiftMatchmakingConfigurationKeyName].GetString()); + if (!ticketId.empty()) + { + outRequest.SetTicketId(ticketId.c_str()); + } + + AZStd::vector requestPlayers(players); + if (players.size() == 0) + { + requestPlayers = GetActiveServerMatchBackfillPlayers(); + } + for (auto player : requestPlayers) + { + Aws::GameLift::Server::Model::Player backfillPlayer; + if (BuildServerMatchBackfillPlayer(player, backfillPlayer)) + { + outRequest.AddPlayer(backfillPlayer); + } + else + { + return false; + } + } + return true; + } + + void AWSGameLiftServerManager::BuildStopMatchBackfillRequest( + const AZStd::string& ticketId, Aws::GameLift::Server::Model::StopMatchBackfillRequest& outRequest) + { + outRequest.SetGameSessionArn(m_gameSession.GetGameSessionId()); + outRequest.SetMatchmakingConfigurationArn(m_matchmakingData[AWSGameLiftMatchmakingConfigurationKeyName].GetString()); + if (!ticketId.empty()) + { + outRequest.SetTicketId(ticketId.c_str()); + } + } + AZ::IO::Path AWSGameLiftServerManager::GetExternalSessionCertificate() { // TODO: Add support to get TLS cert file path @@ -238,7 +512,7 @@ namespace AWSGameLift Aws::GameLift::Server::ProcessParameters processReadyParameter = Aws::GameLift::Server::ProcessParameters( AZStd::bind(&AWSGameLiftServerManager::OnStartGameSession, this, AZStd::placeholders::_1), - AZStd::bind(&AWSGameLiftServerManager::OnUpdateGameSession, this), + AZStd::bind(&AWSGameLiftServerManager::OnUpdateGameSession, this, AZStd::placeholders::_1), AZStd::bind(&AWSGameLiftServerManager::OnProcessTerminate, this), AZStd::bind(&AWSGameLiftServerManager::OnHealthCheck, this), desc.m_port, Aws::GameLift::Server::LogParameters(logPaths)); @@ -260,6 +534,7 @@ namespace AWSGameLift void AWSGameLiftServerManager::OnStartGameSession(const Aws::GameLift::Server::Model::GameSession& gameSession) { + UpdateGameSessionData(gameSession); AzFramework::SessionConfig sessionConfig = BuildSessionConfig(gameSession); bool createSessionResult = true; @@ -311,10 +586,19 @@ namespace AWSGameLift return m_serverSDKInitialized && healthCheckResult; } - void AWSGameLiftServerManager::OnUpdateGameSession() + void AWSGameLiftServerManager::OnUpdateGameSession(const Aws::GameLift::Server::Model::UpdateGameSession& updateGameSession) { - // TODO: Perform game-specific tasks to prep for newly matched players - return; + Aws::GameLift::Server::Model::UpdateReason updateReason = updateGameSession.GetUpdateReason(); + if (updateReason == Aws::GameLift::Server::Model::UpdateReason::MATCHMAKING_DATA_UPDATED) + { + UpdateGameSessionData(updateGameSession.GetGameSession()); + } + AzFramework::SessionConfig sessionConfig = BuildSessionConfig(updateGameSession.GetGameSession()); + + AzFramework::SessionNotificationBus::Broadcast( + &AzFramework::SessionNotifications::OnUpdateSessionBegin, + sessionConfig, + Aws::GameLift::Server::Model::UpdateReasonMapper::GetNameForUpdateReason(updateReason).c_str()); } bool AWSGameLiftServerManager::RemoveConnectedPlayer(uint32_t playerConnectionId, AZStd::string& outPlayerSessionId) @@ -340,6 +624,92 @@ namespace AWSGameLift m_gameLiftServerSDKWrapper = AZStd::move(gameLiftServerSDKWrapper); } + bool AWSGameLiftServerManager::StartMatchBackfill(const AZStd::string& ticketId, const AZStd::vector& players) + { + if (!m_serverSDKInitialized) + { + AZ_Error(AWSGameLiftServerManagerName, false, AWSGameLiftServerSDKNotInitErrorMessage); + return false; + } + + if (!IsMatchmakingDataValid()) + { + AZ_Error(AWSGameLiftServerManagerName, false, AWSGameLiftMatchmakingDataMissingErrorMessage); + return false; + } + + Aws::GameLift::Server::Model::StartMatchBackfillRequest request; + if (!BuildStartMatchBackfillRequest(ticketId, players, request)) + { + return false; + } + + AZ_TracePrintf(AWSGameLiftServerManagerName, "Starting match backfill %s ...", ticketId.c_str()); + auto outcome = m_gameLiftServerSDKWrapper->StartMatchBackfill(request); + if (!outcome.IsSuccess()) + { + AZ_Error(AWSGameLiftServerManagerName, false, AWSGameLiftStartMatchBackfillErrorMessage, + outcome.GetError().GetErrorMessage().c_str()); + return false; + } + else + { + AZ_TracePrintf(AWSGameLiftServerManagerName, "StartMatchBackfill request against Amazon GameLift service is complete."); + return true; + } + } + + bool AWSGameLiftServerManager::StopMatchBackfill(const AZStd::string& ticketId) + { + if (!m_serverSDKInitialized) + { + AZ_Error(AWSGameLiftServerManagerName, false, AWSGameLiftServerSDKNotInitErrorMessage); + return false; + } + + if (!IsMatchmakingDataValid()) + { + AZ_Error(AWSGameLiftServerManagerName, false, AWSGameLiftMatchmakingDataMissingErrorMessage); + return false; + } + + Aws::GameLift::Server::Model::StopMatchBackfillRequest request; + BuildStopMatchBackfillRequest(ticketId, request); + + AZ_TracePrintf(AWSGameLiftServerManagerName, "Stopping match backfill %s ...", ticketId.c_str()); + auto outcome = m_gameLiftServerSDKWrapper->StopMatchBackfill(request); + if (!outcome.IsSuccess()) + { + AZ_Error(AWSGameLiftServerManagerName, false, AWSGameLiftStopMatchBackfillErrorMessage, + outcome.GetError().GetErrorMessage().c_str()); + return false; + } + else + { + AZ_TracePrintf(AWSGameLiftServerManagerName, "StopMatchBackfill request against Amazon GameLift service is complete."); + return true; + } + } + + void AWSGameLiftServerManager::UpdateGameSessionData(const Aws::GameLift::Server::Model::GameSession& gameSession) + { + AZ_TracePrintf(AWSGameLiftServerManagerName, "Lazy loading game session and matchmaking data from Amazon GameLift service ..."); + m_gameSession = Aws::GameLift::Server::Model::GameSession(gameSession); + if (m_gameSession.GetMatchmakerData().empty()) + { + m_matchmakingData.Parse("{}"); + } + else + { + rapidjson::ParseResult parseResult = m_matchmakingData.Parse(m_gameSession.GetMatchmakerData().c_str()); + if (!parseResult) + { + AZ_Error(AWSGameLiftServerManagerName, false, + AWSGameLiftMatchmakingDataInvalidErrorMessage, rapidjson::GetParseError_En(parseResult.Code())); + } + } + } + bool AWSGameLiftServerManager::ValidatePlayerJoinSession(const AzFramework::PlayerConnectionConfig& playerConnectionConfig) { uint32_t playerConnectionId = playerConnectionConfig.m_playerConnectionId; diff --git a/Gems/AWSGameLift/Code/AWSGameLiftServer/Source/AWSGameLiftServerManager.h b/Gems/AWSGameLift/Code/AWSGameLiftServer/Source/AWSGameLiftServerManager.h index c0f1c00bea..fa2f783eca 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftServer/Source/AWSGameLiftServerManager.h +++ b/Gems/AWSGameLift/Code/AWSGameLiftServer/Source/AWSGameLiftServerManager.h @@ -11,11 +11,15 @@ #include #include +#include +#include #include #include #include #include #include + +#include #include namespace AWSGameLift @@ -66,6 +70,36 @@ namespace AWSGameLift "Invalid player connection config, player connection id: %d, player session id: %s"; static constexpr const char AWSGameLiftServerRemovePlayerSessionErrorMessage[] = "Failed to notify GameLift that the player with the player session id %s has disconnected from the server process. ErrorMessage: %s"; + static constexpr const char AWSGameLiftMatchmakingDataInvalidErrorMessage[] = + "Failed to parse GameLift matchmaking data. ErrorMessage: %s"; + static constexpr const char AWSGameLiftMatchmakingDataMissingErrorMessage[] = + "GameLift matchmaking data is missing or invalid to parse."; + static constexpr const char AWSGameLiftMatchmakingPlayerAttributeInvalidErrorMessage[] = + "Failed to build player %s attributes. ErrorMessage: %s"; + static constexpr const char AWSGameLiftDescribePlayerSessionsErrorMessage[] = + "Failed to describe player sessions. ErrorMessage: %s"; + static constexpr const char AWSGameLiftStartMatchBackfillErrorMessage[] = + "Failed to start match backfill. ErrorMessage: %s"; + static constexpr const char AWSGameLiftStopMatchBackfillErrorMessage[] = + "Failed to stop match backfill. ErrorMessage: %s"; + + static constexpr const char AWSGameLiftMatchmakingConfigurationKeyName[] = "matchmakingConfigurationArn"; + static constexpr const char AWSGameLiftMatchmakingTeamsKeyName[] = "teams"; + static constexpr const char AWSGameLiftMatchmakingTeamNameKeyName[] = "name"; + static constexpr const char AWSGameLiftMatchmakingPlayersKeyName[] = "players"; + static constexpr const char AWSGameLiftMatchmakingPlayerIdKeyName[] = "playerId"; + static constexpr const char AWSGameLiftMatchmakingPlayerAttributesKeyName[] = "attributes"; + static constexpr const char AWSGameLiftMatchmakingPlayerAttributeTypeKeyName[] = "attributeType"; + static constexpr const char AWSGameLiftMatchmakingPlayerAttributeValueKeyName[] = "valueAttribute"; + static constexpr const char AWSGameLiftMatchmakingPlayerAttributeSTypeName[] = "S"; + static constexpr const char AWSGameLiftMatchmakingPlayerAttributeSServerTypeName[] = "STRING"; + static constexpr const char AWSGameLiftMatchmakingPlayerAttributeNTypeName[] = "N"; + static constexpr const char AWSGameLiftMatchmakingPlayerAttributeNServerTypeName[] = "NUMBER"; + static constexpr const char AWSGameLiftMatchmakingPlayerAttributeSLTypeName[] = "SL"; + static constexpr const char AWSGameLiftMatchmakingPlayerAttributeSLServerTypeName[] = "STRING_LIST"; + static constexpr const char AWSGameLiftMatchmakingPlayerAttributeSDMTypeName[] = "SDM"; + static constexpr const char AWSGameLiftMatchmakingPlayerAttributeSDMServerTypeName[] = "STRING_DOUBLE_MAP"; + static constexpr const uint16_t AWSGameLiftDescribePlayerSessionsPageSize = 30; AWSGameLiftServerManager(); virtual ~AWSGameLiftServerManager(); @@ -78,6 +112,8 @@ namespace AWSGameLift // AWSGameLiftServerRequestBus interface implementation bool NotifyGameLiftProcessReady() override; + bool StartMatchBackfill(const AZStd::string& ticketId, const AZStd::vector& players) override; + bool StopMatchBackfill(const AZStd::string& ticketId) override; // ISessionHandlingProviderRequests interface implementation void HandleDestroySession() override; @@ -92,18 +128,48 @@ namespace AWSGameLift //! Add connected player session id. bool AddConnectedPlayer(const AzFramework::PlayerConnectionConfig& playerConnectionConfig); + //! Get active server player data from lazy loaded game session for server match backfill + AZStd::vector GetActiveServerMatchBackfillPlayers(); + + //! Update local game session data to latest one + void UpdateGameSessionData(const Aws::GameLift::Server::Model::GameSession& gameSession); + private: //! Build the serverProcessDesc with appropriate server port number and log paths. GameLiftServerProcessDesc BuildGameLiftServerProcessDesc(); + //! Build active server player data from lazy loaded game session based on player id + bool BuildActiveServerMatchBackfillPlayer(const AZStd::string& playerId, AWSGameLiftPlayer& outPlayer); + + //! Build server player attribute data from lazy load matchmaking data + void BuildServerMatchBackfillPlayerAttributes(const rapidjson::Value& playerAttributes, AWSGameLiftPlayer& outPlayer); + + //! Build server player data for server match backfill + bool BuildServerMatchBackfillPlayer(const AWSGameLiftPlayer& player, Aws::GameLift::Server::Model::Player& outBackfillPlayer); + + //! Build start match backfill request for StartMatchBackfill operation + bool BuildStartMatchBackfillRequest( + const AZStd::string& ticketId, + const AZStd::vector& players, + Aws::GameLift::Server::Model::StartMatchBackfillRequest& outRequest); + + //! Build stop match backfill request for StopMatchBackfill operation + void BuildStopMatchBackfillRequest(const AZStd::string& ticketId, Aws::GameLift::Server::Model::StopMatchBackfillRequest& outRequest); + //! Build session config by using AWS GameLift Server GameSession Model. AzFramework::SessionConfig BuildSessionConfig(const Aws::GameLift::Server::Model::GameSession& gameSession); + //! Check whether matchmaking data is in proper format + bool IsMatchmakingDataValid(); + + //! Fetch active player sessions in game session. + AZStd::vector GetActivePlayerSessions(); + //! Callback function that the GameLift service invokes to activate a new game session. void OnStartGameSession(const Aws::GameLift::Server::Model::GameSession& gameSession); //! Callback function that the GameLift service invokes to pass an updated game session object to the server process. - void OnUpdateGameSession(); + void OnUpdateGameSession(const Aws::GameLift::Server::Model::UpdateGameSession& updateGameSession); //! Callback function that the server process or GameLift service invokes to force the server process to shut down. void OnProcessTerminate(); @@ -125,5 +191,12 @@ namespace AWSGameLift using PlayerConnectionId = uint32_t; using PlayerSessionId = AZStd::string; AZStd::unordered_map m_connectedPlayers; + + // Lazy loaded game session and matchmaking data + Aws::GameLift::Server::Model::GameSession m_gameSession; + // Matchmaking data contains a unique match ID, it identifies the matchmaker that created the match + // and describes the teams, team assignments, and players. + // Reference https://docs.aws.amazon.com/gamelift/latest/flexmatchguide/match-server.html#match-server-data + rapidjson::Document m_matchmakingData; }; } // namespace AWSGameLift diff --git a/Gems/AWSGameLift/Code/AWSGameLiftServer/Source/GameLiftServerSDKWrapper.cpp b/Gems/AWSGameLift/Code/AWSGameLiftServer/Source/GameLiftServerSDKWrapper.cpp index 7e216f33be..d51c8eb8cb 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftServer/Source/GameLiftServerSDKWrapper.cpp +++ b/Gems/AWSGameLift/Code/AWSGameLiftServer/Source/GameLiftServerSDKWrapper.cpp @@ -22,6 +22,12 @@ namespace AWSGameLift return Aws::GameLift::Server::ActivateGameSession(); } + Aws::GameLift::DescribePlayerSessionsOutcome GameLiftServerSDKWrapper::DescribePlayerSessions( + const Aws::GameLift::Server::Model::DescribePlayerSessionsRequest& describePlayerSessionsRequest) + { + return Aws::GameLift::Server::DescribePlayerSessions(describePlayerSessionsRequest); + } + Aws::GameLift::Server::InitSDKOutcome GameLiftServerSDKWrapper::InitSDK() { return Aws::GameLift::Server::InitSDK(); @@ -69,4 +75,17 @@ namespace AWSGameLift { return Aws::GameLift::Server::RemovePlayerSession(playerSessionId.c_str()); } + + Aws::GameLift::StartMatchBackfillOutcome GameLiftServerSDKWrapper::StartMatchBackfill( + const Aws::GameLift::Server::Model::StartMatchBackfillRequest& startMatchBackfillRequest) + { + return Aws::GameLift::Server::StartMatchBackfill(startMatchBackfillRequest); + } + + Aws::GameLift::GenericOutcome GameLiftServerSDKWrapper::StopMatchBackfill( + const Aws::GameLift::Server::Model::StopMatchBackfillRequest& stopMatchBackfillRequest) + { + return Aws::GameLift::Server::StopMatchBackfill(stopMatchBackfillRequest); + } + } // namespace AWSGameLift diff --git a/Gems/AWSGameLift/Code/AWSGameLiftServer/Source/GameLiftServerSDKWrapper.h b/Gems/AWSGameLift/Code/AWSGameLiftServer/Source/GameLiftServerSDKWrapper.h index a656087206..e56366d75a 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftServer/Source/GameLiftServerSDKWrapper.h +++ b/Gems/AWSGameLift/Code/AWSGameLiftServer/Source/GameLiftServerSDKWrapper.h @@ -33,6 +33,14 @@ namespace AWSGameLift //! @return Returns a generic outcome consisting of success or failure with an error message. virtual Aws::GameLift::GenericOutcome ActivateGameSession(); + //! Retrieves player session data, including settings, session metadata, and player data. + //! Use this action to get information for a single player session, + //! for all player sessions in a game session, or for all player sessions associated with a single player ID. + //! @param describePlayerSessionsRequest The request object describing which player sessions to retrieve. + //! @return If successful, returns a DescribePlayerSessionsOutcome object containing a set of player session objects that fit the request parameters. + virtual Aws::GameLift::DescribePlayerSessionsOutcome DescribePlayerSessions( + const Aws::GameLift::Server::Model::DescribePlayerSessionsRequest& describePlayerSessionsRequest); + //! Initializes the GameLift SDK. //! Should be called when the server starts, before any GameLift-dependent initialization happens. //! @return If successful, returns an InitSdkOutcome object indicating that the server process is ready to call ProcessReady(). @@ -56,5 +64,16 @@ namespace AWSGameLift //! @param playerSessionId Unique ID issued by the Amazon GameLift service in response to a call to the AWS SDK Amazon GameLift API action CreatePlayerSession. //! @return Returns a generic outcome consisting of success or failure with an error message. virtual Aws::GameLift::GenericOutcome RemovePlayerSession(const AZStd::string& playerSessionId); + + //! Sends a request to find new players for open slots in a game session created with FlexMatch. + //! When the match has been successfully, backfilled updated matchmaker data will be sent to the OnUpdateGameSession callback. + //! @param startMatchBackfillRequest This data type is used to send a matchmaking backfill request. + //! @return Returns a StartMatchBackfillOutcome object with the match backfill ticket or failure with an error message. + virtual Aws::GameLift::StartMatchBackfillOutcome StartMatchBackfill(const Aws::GameLift::Server::Model::StartMatchBackfillRequest& startMatchBackfillRequest); + + //! Cancels an active match backfill request that was created with StartMatchBackfill + //! @param stopMatchBackfillRequest This data type is used to cancel a matchmaking backfill request. + //! @return Returns a generic outcome consisting of success or failure with an error message. + virtual Aws::GameLift::GenericOutcome StopMatchBackfill(const Aws::GameLift::Server::Model::StopMatchBackfillRequest& stopMatchBackfillRequest); }; } // namespace AWSGameLift diff --git a/Gems/AWSGameLift/Code/AWSGameLiftServer/Tests/AWSGameLiftServerManagerTest.cpp b/Gems/AWSGameLift/Code/AWSGameLiftServer/Tests/AWSGameLiftServerManagerTest.cpp index 2c8929b297..d172734ec0 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftServer/Tests/AWSGameLiftServerManagerTest.cpp +++ b/Gems/AWSGameLift/Code/AWSGameLiftServer/Tests/AWSGameLiftServerManagerTest.cpp @@ -16,6 +16,136 @@ namespace UnitTest { + static constexpr const char TEST_SERVER_MATCHMAKING_DATA[] = +R"({ + "matchId":"testmatchid", + "matchmakingConfigurationArn":"testmatchconfig", + "teams":[ + {"name":"testteam", + "players":[ + {"playerId":"testplayer", + "attributes":{ + "skills":{ + "attributeType":"STRING_DOUBLE_MAP", + "valueAttribute":{"test1":10.0,"test2":20.0,"test3":30.0,"test4":40.0} + }, + "mode":{ + "attributeType":"STRING", + "valueAttribute":"testmode" + }, + "level":{ + "attributeType":"NUMBER", + "valueAttribute":10.0 + }, + "items":{ + "attributeType":"STRING_LIST", + "valueAttribute":["test1","test2","test3"] + } + }} + ]} + ] +})"; + + Aws::GameLift::Server::Model::StartMatchBackfillRequest GetTestStartMatchBackfillRequest() + { + Aws::GameLift::Server::Model::StartMatchBackfillRequest request; + request.SetMatchmakingConfigurationArn("testmatchconfig"); + Aws::GameLift::Server::Model::Player player; + player.SetPlayerId("testplayer"); + player.SetTeam("testteam"); + player.AddPlayerAttribute("mode", Aws::GameLift::Server::Model::AttributeValue("testmode")); + player.AddPlayerAttribute("level", Aws::GameLift::Server::Model::AttributeValue(10.0)); + auto sdmValue = Aws::GameLift::Server::Model::AttributeValue::ConstructStringDoubleMap(); + sdmValue.AddStringAndDouble("test1", 10.0); + player.AddPlayerAttribute("skills", sdmValue); + auto slValue = Aws::GameLift::Server::Model::AttributeValue::ConstructStringList(); + slValue.AddString("test1"); + player.AddPlayerAttribute("items", slValue); + player.AddLatencyInMs("testregion", 10); + request.AddPlayer(player); + request.SetTicketId("testticket"); + return request; + } + + AWSGameLiftPlayer GetTestGameLiftPlayer() + { + AWSGameLiftPlayer player; + player.m_team = "testteam"; + player.m_playerId = "testplayer"; + player.m_playerAttributes.emplace("mode", "{\"S\": \"testmode\"}"); + player.m_playerAttributes.emplace("level", "{\"N\": 10.0}"); + player.m_playerAttributes.emplace("skills", "{\"SDM\": {\"test1\":10.0}}"); + player.m_playerAttributes.emplace("items", "{\"SL\": [\"test1\"]}"); + player.m_latencyInMs.emplace("testregion", 10); + return player; + } + + MATCHER_P(StartMatchBackfillRequestMatcher, expectedRequest, "") + { + // Custome matcher for checking the SearchSessionsResponse type argument. + AZ_UNUSED(result_listener); + if (strcmp(arg.GetGameSessionArn().c_str(), expectedRequest.GetGameSessionArn().c_str()) != 0) + { + return false; + } + if (strcmp(arg.GetMatchmakingConfigurationArn().c_str(), expectedRequest.GetMatchmakingConfigurationArn().c_str()) != 0) + { + return false; + } + if (strcmp(arg.GetTicketId().c_str(), expectedRequest.GetTicketId().c_str()) != 0) + { + return false; + } + if (arg.GetPlayers().size() != expectedRequest.GetPlayers().size()) + { + return false; + } + for (int playerIndex = 0; playerIndex < expectedRequest.GetPlayers().size(); playerIndex++) + { + auto actualPlayerAttributes = arg.GetPlayers()[playerIndex].GetPlayerAttributes(); + auto expectedPlayerAttributes = expectedRequest.GetPlayers()[playerIndex].GetPlayerAttributes(); + if (actualPlayerAttributes.size() != expectedPlayerAttributes.size()) + { + return false; + } + for (auto attributePair : expectedPlayerAttributes) + { + if (actualPlayerAttributes.find(attributePair.first) == actualPlayerAttributes.end()) + { + return false; + } + if (!(attributePair.second.GetType() == actualPlayerAttributes[attributePair.first].GetType() && + (attributePair.second.GetS() == actualPlayerAttributes[attributePair.first].GetS() || + attributePair.second.GetN() == actualPlayerAttributes[attributePair.first].GetN() || + attributePair.second.GetSL() == actualPlayerAttributes[attributePair.first].GetSL() || + attributePair.second.GetSDM() == actualPlayerAttributes[attributePair.first].GetSDM()))) + { + return false; + } + } + + auto actualLatencies = arg.GetPlayers()[playerIndex].GetLatencyInMs(); + auto expectedLatencies = expectedRequest.GetPlayers()[playerIndex].GetLatencyInMs(); + if (actualLatencies.size() != expectedLatencies.size()) + { + return false; + } + for (auto latencyPair : expectedLatencies) + { + if (actualLatencies.find(latencyPair.first) == actualLatencies.end()) + { + return false; + } + if (latencyPair.second != actualLatencies[latencyPair.first]) + { + return false; + } + } + } + + return true; + } + class SessionNotificationsHandlerMock : public AzFramework::SessionNotificationBus::Handler { @@ -33,6 +163,7 @@ namespace UnitTest MOCK_METHOD0(OnSessionHealthCheck, bool()); MOCK_METHOD1(OnCreateSessionBegin, bool(const AzFramework::SessionConfig&)); MOCK_METHOD0(OnDestroySessionBegin, bool()); + MOCK_METHOD2(OnUpdateSessionBegin, void(const AzFramework::SessionConfig&, const AZStd::string&)); }; class GameLiftServerManagerTest @@ -228,6 +359,64 @@ namespace UnitTest AZ_TEST_STOP_TRACE_SUPPRESSION(1); } + TEST_F(GameLiftServerManagerTest, OnUpdateGameSession_TriggerWithUnknownReason_OnUpdateSessionBeginGetCalledOnce) + { + m_serverManager->InitializeGameLiftServerSDK(); + m_serverManager->NotifyGameLiftProcessReady(); + SessionNotificationsHandlerMock handlerMock; + EXPECT_CALL(handlerMock, OnUpdateSessionBegin(testing::_, testing::_)).Times(1); + + m_serverManager->m_gameLiftServerSDKWrapperMockPtr->m_onUpdateGameSessionFunc( + Aws::GameLift::Server::Model::UpdateGameSession( + Aws::GameLift::Server::Model::GameSession(), + Aws::GameLift::Server::Model::UpdateReason::UNKNOWN, + "testticket")); + } + + TEST_F(GameLiftServerManagerTest, OnUpdateGameSession_TriggerWithEmptyMatchmakingData_OnUpdateSessionBeginGetCalledOnce) + { + m_serverManager->InitializeGameLiftServerSDK(); + m_serverManager->NotifyGameLiftProcessReady(); + SessionNotificationsHandlerMock handlerMock; + EXPECT_CALL(handlerMock, OnUpdateSessionBegin(testing::_, testing::_)).Times(1); + + m_serverManager->m_gameLiftServerSDKWrapperMockPtr->m_onUpdateGameSessionFunc( + Aws::GameLift::Server::Model::UpdateGameSession( + Aws::GameLift::Server::Model::GameSession(), + Aws::GameLift::Server::Model::UpdateReason::MATCHMAKING_DATA_UPDATED, + "testticket")); + } + + TEST_F(GameLiftServerManagerTest, OnUpdateGameSession_TriggerWithValidMatchmakingData_OnUpdateSessionBeginGetCalledOnce) + { + m_serverManager->InitializeGameLiftServerSDK(); + m_serverManager->NotifyGameLiftProcessReady(); + SessionNotificationsHandlerMock handlerMock; + EXPECT_CALL(handlerMock, OnUpdateSessionBegin(testing::_, testing::_)).Times(1); + + Aws::GameLift::Server::Model::GameSession gameSession; + gameSession.SetMatchmakerData(TEST_SERVER_MATCHMAKING_DATA); + m_serverManager->m_gameLiftServerSDKWrapperMockPtr->m_onUpdateGameSessionFunc( + Aws::GameLift::Server::Model::UpdateGameSession( + gameSession, Aws::GameLift::Server::Model::UpdateReason::MATCHMAKING_DATA_UPDATED, "testticket")); + } + + TEST_F(GameLiftServerManagerTest, OnUpdateGameSession_TriggerWithInvalidMatchmakingData_OnUpdateSessionBeginGetCalledOnce) + { + m_serverManager->InitializeGameLiftServerSDK(); + m_serverManager->NotifyGameLiftProcessReady(); + SessionNotificationsHandlerMock handlerMock; + EXPECT_CALL(handlerMock, OnUpdateSessionBegin(testing::_, testing::_)).Times(1); + + Aws::GameLift::Server::Model::GameSession gameSession; + gameSession.SetMatchmakerData("{invalid}"); + AZ_TEST_START_TRACE_SUPPRESSION; + m_serverManager->m_gameLiftServerSDKWrapperMockPtr->m_onUpdateGameSessionFunc( + Aws::GameLift::Server::Model::UpdateGameSession( + gameSession, Aws::GameLift::Server::Model::UpdateReason::MATCHMAKING_DATA_UPDATED, "testticket")); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); + } + TEST_F(GameLiftServerManagerTest, ValidatePlayerJoinSession_CallWithInvalidConnectionConfig_GetFalseResultAndExpectedErrorLog) { AZ_TEST_START_TRACE_SUPPRESSION; @@ -425,4 +614,331 @@ namespace UnitTest } AZ_TEST_STOP_TRACE_SUPPRESSION(testThreadNumber - 1); // The player is only disconnected once. } + + TEST_F(GameLiftServerManagerTest, UpdateGameSessionData_CallWithInvalidMatchmakingData_GetExpectedError) + { + AZ_TEST_START_TRACE_SUPPRESSION; + m_serverManager->SetupTestMatchmakingData("{invalid}"); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); + } + + TEST_F(GameLiftServerManagerTest, GetActiveServerMatchBackfillPlayers_CallWithInvalidMatchmakingData_GetEmptyResult) + { + AZ_TEST_START_TRACE_SUPPRESSION; + m_serverManager->SetupTestMatchmakingData("{invalid}"); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); + + auto actualResult = m_serverManager->GetTestServerMatchBackfillPlayers(); + EXPECT_TRUE(actualResult.empty()); + } + + TEST_F(GameLiftServerManagerTest, GetActiveServerMatchBackfillPlayers_CallWithEmptyMatchmakingData_GetEmptyResult) + { + m_serverManager->SetupTestMatchmakingData(""); + + auto actualResult = m_serverManager->GetTestServerMatchBackfillPlayers(); + EXPECT_TRUE(actualResult.empty()); + } + + TEST_F(GameLiftServerManagerTest, GetActiveServerMatchBackfillPlayers_CallButDescribePlayerError_GetEmptyResult) + { + m_serverManager->SetupTestMatchmakingData(TEST_SERVER_MATCHMAKING_DATA); + + Aws::GameLift::GameLiftError error; + Aws::GameLift::DescribePlayerSessionsOutcome errorOutcome(error); + EXPECT_CALL(*(m_serverManager->m_gameLiftServerSDKWrapperMockPtr), DescribePlayerSessions(testing::_)) + .Times(1) + .WillOnce(Return(errorOutcome)); + + AZ_TEST_START_TRACE_SUPPRESSION; + auto actualResult = m_serverManager->GetTestServerMatchBackfillPlayers(); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); + EXPECT_TRUE(actualResult.empty()); + } + + TEST_F(GameLiftServerManagerTest, GetActiveServerMatchBackfillPlayers_CallButNoActivePlayer_GetEmptyResult) + { + m_serverManager->SetupTestMatchmakingData(TEST_SERVER_MATCHMAKING_DATA); + + Aws::GameLift::Server::Model::DescribePlayerSessionsResult result; + Aws::GameLift::DescribePlayerSessionsOutcome successOutcome(result); + EXPECT_CALL(*(m_serverManager->m_gameLiftServerSDKWrapperMockPtr), DescribePlayerSessions(testing::_)) + .Times(1) + .WillOnce(Return(successOutcome)); + + auto actualResult = m_serverManager->GetTestServerMatchBackfillPlayers(); + EXPECT_TRUE(actualResult.empty()); + } + + TEST_F(GameLiftServerManagerTest, GetActiveServerMatchBackfillPlayers_CallWithValidMatchmakingData_GetExpectedResult) + { + m_serverManager->SetupTestMatchmakingData(TEST_SERVER_MATCHMAKING_DATA); + + Aws::GameLift::Server::Model::PlayerSession playerSession; + playerSession.SetPlayerId("testplayer"); + Aws::GameLift::Server::Model::DescribePlayerSessionsResult result; + result.AddPlayerSessions(playerSession); + Aws::GameLift::DescribePlayerSessionsOutcome successOutcome(result); + EXPECT_CALL(*(m_serverManager->m_gameLiftServerSDKWrapperMockPtr), DescribePlayerSessions(testing::_)) + .Times(1) + .WillOnce(Return(successOutcome)); + + auto actualResult = m_serverManager->GetTestServerMatchBackfillPlayers(); + EXPECT_TRUE(actualResult.size() == 1); + EXPECT_TRUE(actualResult[0].m_team == "testteam"); + EXPECT_TRUE(actualResult[0].m_playerId == "testplayer"); + EXPECT_TRUE(actualResult[0].m_playerAttributes.size() == 4); + } + + TEST_F(GameLiftServerManagerTest, GetActiveServerMatchBackfillPlayers_CallWithMultiDescribePlayerButError_GetEmptyResult) + { + m_serverManager->SetupTestMatchmakingData(TEST_SERVER_MATCHMAKING_DATA, 50); + + Aws::GameLift::GameLiftError error; + Aws::GameLift::DescribePlayerSessionsOutcome errorOutcome(error); + EXPECT_CALL(*(m_serverManager->m_gameLiftServerSDKWrapperMockPtr), DescribePlayerSessions(testing::_)) + .Times(1) + .WillOnce(Return(errorOutcome)); + + AZ_TEST_START_TRACE_SUPPRESSION; + auto actualResult = m_serverManager->GetTestServerMatchBackfillPlayers(); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); + EXPECT_TRUE(actualResult.empty()); + } + + TEST_F(GameLiftServerManagerTest, GetActiveServerMatchBackfillPlayers_CallWithMultiDescribePlayer_GetExpectedResult) + { + m_serverManager->SetupTestMatchmakingData(TEST_SERVER_MATCHMAKING_DATA, 50); + + Aws::GameLift::Server::Model::PlayerSession playerSession1; + playerSession1.SetPlayerId("testplayer"); + Aws::GameLift::Server::Model::DescribePlayerSessionsResult result1; + result1.AddPlayerSessions(playerSession1); + result1.SetNextToken("testtoken"); + Aws::GameLift::DescribePlayerSessionsOutcome successOutcome1(result1); + + Aws::GameLift::Server::Model::PlayerSession playerSession2; + playerSession2.SetPlayerId("playernotinmatch"); + Aws::GameLift::Server::Model::DescribePlayerSessionsResult result2; + result2.AddPlayerSessions(playerSession2); + Aws::GameLift::DescribePlayerSessionsOutcome successOutcome2(result2); + EXPECT_CALL(*(m_serverManager->m_gameLiftServerSDKWrapperMockPtr), DescribePlayerSessions(testing::_)) + .WillOnce(Return(successOutcome1)) + .WillOnce(Return(successOutcome2)); + + auto actualResult = m_serverManager->GetTestServerMatchBackfillPlayers(); + EXPECT_TRUE(actualResult.size() == 1); + EXPECT_TRUE(actualResult[0].m_team == "testteam"); + EXPECT_TRUE(actualResult[0].m_playerId == "testplayer"); + EXPECT_TRUE(actualResult[0].m_playerAttributes.size() == 4); + } + + TEST_F(GameLiftServerManagerTest, StartMatchBackfill_SDKNotInitialized_GetExpectedError) + { + AZ_TEST_START_TRACE_SUPPRESSION; + auto actualResult = m_serverManager->StartMatchBackfill("testticket", {}); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); + EXPECT_FALSE(actualResult); + } + + TEST_F(GameLiftServerManagerTest, StartMatchBackfill_CallWithEmptyMatchmakingData_GetExpectedError) + { + m_serverManager->InitializeGameLiftServerSDK(); + m_serverManager->SetupTestMatchmakingData(""); + + AZ_TEST_START_TRACE_SUPPRESSION; + auto actualResult = m_serverManager->StartMatchBackfill("testticket", {}); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); + EXPECT_FALSE(actualResult); + } + + TEST_F(GameLiftServerManagerTest, StartMatchBackfill_CallWithInvalidPlayerAttribute_GetExpectedError) + { + m_serverManager->InitializeGameLiftServerSDK(); + m_serverManager->SetupTestMatchmakingData(TEST_SERVER_MATCHMAKING_DATA); + + AWSGameLiftPlayer testPlayer = GetTestGameLiftPlayer(); + testPlayer.m_playerAttributes.clear(); + testPlayer.m_playerAttributes.emplace("invalidattribute", "{invalid}"); + + AZ_TEST_START_TRACE_SUPPRESSION; + auto actualResult = m_serverManager->StartMatchBackfill("testticket", { testPlayer }); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); + EXPECT_FALSE(actualResult); + } + + TEST_F(GameLiftServerManagerTest, StartMatchBackfill_CallWithWrongPlayerAttributeType_GetExpectedError) + { + m_serverManager->InitializeGameLiftServerSDK(); + m_serverManager->SetupTestMatchmakingData(TEST_SERVER_MATCHMAKING_DATA); + + AWSGameLiftPlayer testPlayer = GetTestGameLiftPlayer(); + testPlayer.m_playerAttributes.clear(); + testPlayer.m_playerAttributes.emplace("invalidattribute", "{\"SDM\": [\"test1\"]}"); + + AZ_TEST_START_TRACE_SUPPRESSION; + auto actualResult = m_serverManager->StartMatchBackfill("testticket", { testPlayer }); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); + EXPECT_FALSE(actualResult); + } + + TEST_F(GameLiftServerManagerTest, StartMatchBackfill_CallWithUnexpectedPlayerAttributeType_GetExpectedError) + { + m_serverManager->InitializeGameLiftServerSDK(); + m_serverManager->SetupTestMatchmakingData(TEST_SERVER_MATCHMAKING_DATA); + + AWSGameLiftPlayer testPlayer = GetTestGameLiftPlayer(); + testPlayer.m_playerAttributes.clear(); + testPlayer.m_playerAttributes.emplace("invalidattribute", "{\"UNEXPECTED\": [\"test1\"]}"); + + AZ_TEST_START_TRACE_SUPPRESSION; + auto actualResult = m_serverManager->StartMatchBackfill("testticket", { testPlayer }); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); + EXPECT_FALSE(actualResult); + } + + TEST_F(GameLiftServerManagerTest, StartMatchBackfill_CallWithWrongSLPlayerAttributeValue_GetExpectedError) + { + m_serverManager->InitializeGameLiftServerSDK(); + m_serverManager->SetupTestMatchmakingData(TEST_SERVER_MATCHMAKING_DATA); + + AWSGameLiftPlayer testPlayer = GetTestGameLiftPlayer(); + testPlayer.m_playerAttributes.clear(); + testPlayer.m_playerAttributes.emplace("invalidattribute", "{\"SL\": [10.0]}"); + + AZ_TEST_START_TRACE_SUPPRESSION; + auto actualResult = m_serverManager->StartMatchBackfill("testticket", { testPlayer }); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); + EXPECT_FALSE(actualResult); + } + + TEST_F(GameLiftServerManagerTest, StartMatchBackfill_CallWithWrongSDMPlayerAttributeValue_GetExpectedError) + { + m_serverManager->InitializeGameLiftServerSDK(); + m_serverManager->SetupTestMatchmakingData(TEST_SERVER_MATCHMAKING_DATA); + + AWSGameLiftPlayer testPlayer = GetTestGameLiftPlayer(); + testPlayer.m_playerAttributes.clear(); + testPlayer.m_playerAttributes.emplace("invalidattribute", "{\"SDM\": {10.0: \"test1\"}}"); + + AZ_TEST_START_TRACE_SUPPRESSION; + auto actualResult = m_serverManager->StartMatchBackfill("testticket", { testPlayer }); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); + EXPECT_FALSE(actualResult); + } + + TEST_F(GameLiftServerManagerTest, StartMatchBackfill_CallWithValidPlayersData_GetExpectedResult) + { + m_serverManager->InitializeGameLiftServerSDK(); + m_serverManager->SetupTestMatchmakingData(TEST_SERVER_MATCHMAKING_DATA); + + Aws::GameLift::Server::Model::StartMatchBackfillResult backfillResult; + Aws::GameLift::StartMatchBackfillOutcome backfillSuccessOutcome(backfillResult); + Aws::GameLift::Server::Model::StartMatchBackfillRequest request = GetTestStartMatchBackfillRequest(); + + EXPECT_CALL(*(m_serverManager->m_gameLiftServerSDKWrapperMockPtr), StartMatchBackfill(StartMatchBackfillRequestMatcher(request))) + .Times(1) + .WillOnce(Return(backfillSuccessOutcome)); + + AWSGameLiftPlayer testPlayer = GetTestGameLiftPlayer(); + auto actualResult = m_serverManager->StartMatchBackfill("testticket", {testPlayer}); + EXPECT_TRUE(actualResult); + } + + TEST_F(GameLiftServerManagerTest, StartMatchBackfill_CallWithoutGivingPlayersData_GetExpectedResult) + { + m_serverManager->InitializeGameLiftServerSDK(); + m_serverManager->SetupTestMatchmakingData(TEST_SERVER_MATCHMAKING_DATA); + + Aws::GameLift::Server::Model::PlayerSession playerSession; + playerSession.SetPlayerId("testplayer"); + Aws::GameLift::Server::Model::DescribePlayerSessionsResult result; + result.AddPlayerSessions(playerSession); + Aws::GameLift::DescribePlayerSessionsOutcome successOutcome(result); + EXPECT_CALL(*(m_serverManager->m_gameLiftServerSDKWrapperMockPtr), DescribePlayerSessions(testing::_)) + .Times(1) + .WillOnce(Return(successOutcome)); + + Aws::GameLift::Server::Model::StartMatchBackfillResult backfillResult; + Aws::GameLift::StartMatchBackfillOutcome backfillSuccessOutcome(backfillResult); + EXPECT_CALL(*(m_serverManager->m_gameLiftServerSDKWrapperMockPtr), StartMatchBackfill(testing::_)) + .Times(1) + .WillOnce(Return(backfillSuccessOutcome)); + + auto actualResult = m_serverManager->StartMatchBackfill("testticket", {}); + EXPECT_TRUE(actualResult); + } + + TEST_F(GameLiftServerManagerTest, StartMatchBackfill_CallButStartBackfillFail_GetExpectedError) + { + m_serverManager->InitializeGameLiftServerSDK(); + m_serverManager->SetupTestMatchmakingData(TEST_SERVER_MATCHMAKING_DATA); + + Aws::GameLift::Server::Model::PlayerSession playerSession; + playerSession.SetPlayerId("testplayer"); + Aws::GameLift::Server::Model::DescribePlayerSessionsResult result; + result.AddPlayerSessions(playerSession); + Aws::GameLift::DescribePlayerSessionsOutcome successOutcome(result); + EXPECT_CALL(*(m_serverManager->m_gameLiftServerSDKWrapperMockPtr), DescribePlayerSessions(testing::_)) + .Times(1) + .WillOnce(Return(successOutcome)); + + Aws::GameLift::GameLiftError error; + Aws::GameLift::StartMatchBackfillOutcome errorOutcome(error); + EXPECT_CALL(*(m_serverManager->m_gameLiftServerSDKWrapperMockPtr), StartMatchBackfill(testing::_)) + .Times(1) + .WillOnce(Return(errorOutcome)); + + AZ_TEST_START_TRACE_SUPPRESSION; + auto actualResult = m_serverManager->StartMatchBackfill("testticket", {}); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); + EXPECT_FALSE(actualResult); + } + + TEST_F(GameLiftServerManagerTest, StopMatchBackfill_SDKNotInitialized_GetExpectedError) + { + AZ_TEST_START_TRACE_SUPPRESSION; + auto actualResult = m_serverManager->StopMatchBackfill("testticket"); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); + EXPECT_FALSE(actualResult); + } + + TEST_F(GameLiftServerManagerTest, StopMatchBackfill_CallWithEmptyMatchmakingData_GetExpectedError) + { + m_serverManager->InitializeGameLiftServerSDK(); + m_serverManager->SetupTestMatchmakingData(""); + + AZ_TEST_START_TRACE_SUPPRESSION; + auto actualResult = m_serverManager->StopMatchBackfill("testticket"); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); + EXPECT_FALSE(actualResult); + } + + TEST_F(GameLiftServerManagerTest, StopMatchBackfill_CallAndSuccessOutcome_GetExpectedResult) + { + m_serverManager->InitializeGameLiftServerSDK(); + m_serverManager->SetupTestMatchmakingData(TEST_SERVER_MATCHMAKING_DATA); + + EXPECT_CALL(*(m_serverManager->m_gameLiftServerSDKWrapperMockPtr), StopMatchBackfill(testing::_)) + .Times(1) + .WillOnce(Return(Aws::GameLift::GenericOutcome(nullptr))); + + auto actualResult = m_serverManager->StopMatchBackfill("testticket"); + EXPECT_TRUE(actualResult); + } + + TEST_F(GameLiftServerManagerTest, StopMatchBackfill_CallButErrorOutcome_GetExpectedError) + { + m_serverManager->InitializeGameLiftServerSDK(); + m_serverManager->SetupTestMatchmakingData(TEST_SERVER_MATCHMAKING_DATA); + + EXPECT_CALL(*(m_serverManager->m_gameLiftServerSDKWrapperMockPtr), StopMatchBackfill(testing::_)) + .Times(1) + .WillOnce(Return(Aws::GameLift::GenericOutcome())); + + AZ_TEST_START_TRACE_SUPPRESSION; + auto actualResult = m_serverManager->StopMatchBackfill("testticket"); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); + EXPECT_FALSE(actualResult); + } } // namespace UnitTest diff --git a/Gems/AWSGameLift/Code/AWSGameLiftServer/Tests/AWSGameLiftServerMocks.h b/Gems/AWSGameLift/Code/AWSGameLiftServer/Tests/AWSGameLiftServerMocks.h index 24336680b0..7ab9c51cd1 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftServer/Tests/AWSGameLiftServerMocks.h +++ b/Gems/AWSGameLift/Code/AWSGameLiftServer/Tests/AWSGameLiftServerMocks.h @@ -8,6 +8,7 @@ #pragma once +#include #include #include #include @@ -40,17 +41,25 @@ namespace UnitTest MOCK_METHOD1(AcceptPlayerSession, GenericOutcome(const std::string&)); MOCK_METHOD0(ActivateGameSession, GenericOutcome()); + MOCK_METHOD1(DescribePlayerSessions, DescribePlayerSessionsOutcome( + const Aws::GameLift::Server::Model::DescribePlayerSessionsRequest&)); MOCK_METHOD0(InitSDK, Server::InitSDKOutcome()); MOCK_METHOD1(ProcessReady, GenericOutcome(const Server::ProcessParameters& processParameters)); MOCK_METHOD0(ProcessEnding, GenericOutcome()); MOCK_METHOD1(RemovePlayerSession, GenericOutcome(const AZStd::string& playerSessionId)); MOCK_METHOD0(GetTerminationTime, AZStd::string()); + MOCK_METHOD1(StartMatchBackfill, StartMatchBackfillOutcome( + const Aws::GameLift::Server::Model::StartMatchBackfillRequest&)); + MOCK_METHOD1(StopMatchBackfill, GenericOutcome( + const Aws::GameLift::Server::Model::StopMatchBackfillRequest&)); + GenericOutcome ProcessReadyMock(const Server::ProcessParameters& processParameters) { m_healthCheckFunc = processParameters.getOnHealthCheck(); m_onStartGameSessionFunc = processParameters.getOnStartGameSession(); m_onProcessTerminateFunc = processParameters.getOnProcessTerminate(); + m_onUpdateGameSessionFunc = processParameters.getOnUpdateGameSession(); GenericOutcome successOutcome(nullptr); return successOutcome; @@ -59,6 +68,7 @@ namespace UnitTest AZStd::function m_healthCheckFunc; AZStd::function m_onProcessTerminateFunc; AZStd::function m_onStartGameSessionFunc; + AZStd::function m_onUpdateGameSessionFunc; }; class AWSGameLiftServerManagerMock @@ -78,12 +88,25 @@ namespace UnitTest m_gameLiftServerSDKWrapperMockPtr = nullptr; } + void SetupTestMatchmakingData(const AZStd::string& matchmakingData, int maxPlayer = 10) + { + m_testGameSession.SetMatchmakerData(matchmakingData.c_str()); + m_testGameSession.SetMaximumPlayerSessionCount(maxPlayer); + UpdateGameSessionData(m_testGameSession); + } + bool AddConnectedTestPlayer(const AzFramework::PlayerConnectionConfig& playerConnectionConfig) { return AddConnectedPlayer(playerConnectionConfig); } + AZStd::vector GetTestServerMatchBackfillPlayers() + { + return GetActiveServerMatchBackfillPlayers(); + } + NiceMock* m_gameLiftServerSDKWrapperMockPtr; + Aws::GameLift::Server::Model::GameSession m_testGameSession; }; class AWSGameLiftServerSystemComponentMock diff --git a/Gems/AWSGameLift/Code/AWSGameLiftServer/awsgamelift_server_files.cmake b/Gems/AWSGameLift/Code/AWSGameLiftServer/awsgamelift_server_files.cmake index 95b5e1d2d2..9039c9943e 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftServer/awsgamelift_server_files.cmake +++ b/Gems/AWSGameLift/Code/AWSGameLiftServer/awsgamelift_server_files.cmake @@ -7,6 +7,8 @@ # set(FILES + ../AWSGameLiftCommon/Include/AWSGameLiftPlayer.h + ../AWSGameLiftCommon/Source/AWSGameLiftPlayer.cpp ../AWSGameLiftCommon/Source/AWSGameLiftSessionConstants.h Include/Request/IAWSGameLiftServerRequests.h Source/AWSGameLiftServerManager.cpp diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp index 35174e31fb..4fad5697c7 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp @@ -308,6 +308,12 @@ namespace Multiplayer return true; } + void MultiplayerSystemComponent::OnUpdateSessionBegin(const AzFramework::SessionConfig& sessionConfig, const AZStd::string& updateReason) + { + AZ_UNUSED(sessionConfig); + AZ_UNUSED(updateReason); + } + void MultiplayerSystemComponent::OnTick(float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time) { const AZ::TimeMs deltaTimeMs = aznumeric_cast(static_cast(deltaTime * 1000.0f)); diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h index e46bbb59bc..ef1fb0da5b 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h @@ -70,6 +70,7 @@ namespace Multiplayer bool OnSessionHealthCheck() override; bool OnCreateSessionBegin(const AzFramework::SessionConfig& sessionConfig) override; bool OnDestroySessionBegin() override; + void OnUpdateSessionBegin(const AzFramework::SessionConfig& sessionConfig, const AZStd::string& updateReason) override; //! @} //! AZ::TickBus::Handler overrides. From 922a9914438b55464984c330c6ac8a8496a5febf Mon Sep 17 00:00:00 2001 From: jromnoa <80134229+jromnoa@users.noreply.github.com> Date: Wed, 13 Oct 2021 21:15:29 -0700 Subject: [PATCH 55/65] change expected_lines to something that appears in both AR + locally (#4680) Signed-off-by: jromnoa --- AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_GPU.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_GPU.py b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_GPU.py index 74b064a555..381f266fab 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_GPU.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_GPU.py @@ -148,7 +148,7 @@ class TestAllComponentsIndepthTests(object): golden_image_path = os.path.join(golden_images_directory(), golden_image) golden_images.append(golden_image_path) - expected_lines = ["Light component tests completed."] + expected_lines = ["spot_light Controller|Configuration|Shadows|Shadowmap size: SUCCESS"] unexpected_lines = [ "Trace::Assert", "Trace::Error", From 5a204dc80b65c322be7fb038cd9c5783cadf4533 Mon Sep 17 00:00:00 2001 From: Michael Pollind Date: Wed, 13 Oct 2021 21:26:03 -0700 Subject: [PATCH 56/65] chore: remove equality from boolean expression Signed-off-by: Michael Pollind --- .../AzToolsFramework/Picking/Manipulators/ManipulatorBounds.cpp | 2 +- Gems/LmbrCentral/Code/Source/Shape/BoxShape.cpp | 2 +- Gems/LmbrCentral/Code/Source/Shape/DiskShape.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Picking/Manipulators/ManipulatorBounds.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Picking/Manipulators/ManipulatorBounds.cpp index 3a7fffb3be..4c7afa2275 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Picking/Manipulators/ManipulatorBounds.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Picking/Manipulators/ManipulatorBounds.cpp @@ -116,7 +116,7 @@ namespace AzToolsFramework { return AZ::Intersect::IntersectRayBox( rayOrigin, rayDirection, m_center, m_axis1, m_axis2, m_axis3, m_halfExtents.GetX(), m_halfExtents.GetY(), - m_halfExtents.GetZ(), rayIntersectionDistance) > 0; + m_halfExtents.GetZ(), rayIntersectionDistance); } void ManipulatorBoundBox::SetShapeData(const BoundRequestShapeBase& shapeData) diff --git a/Gems/LmbrCentral/Code/Source/Shape/BoxShape.cpp b/Gems/LmbrCentral/Code/Source/Shape/BoxShape.cpp index a776a01ccc..4f6089ba16 100644 --- a/Gems/LmbrCentral/Code/Source/Shape/BoxShape.cpp +++ b/Gems/LmbrCentral/Code/Source/Shape/BoxShape.cpp @@ -166,7 +166,7 @@ namespace LmbrCentral return intersection; } - const bool intersection = AZ::Intersect::IntersectRayObb(src, dir, m_intersectionDataCache.m_obb, distance) > 0; + const bool intersection = AZ::Intersect::IntersectRayObb(src, dir, m_intersectionDataCache.m_obb, distance); return intersection; } diff --git a/Gems/LmbrCentral/Code/Source/Shape/DiskShape.cpp b/Gems/LmbrCentral/Code/Source/Shape/DiskShape.cpp index 8eaf03457f..1c4a94ced3 100644 --- a/Gems/LmbrCentral/Code/Source/Shape/DiskShape.cpp +++ b/Gems/LmbrCentral/Code/Source/Shape/DiskShape.cpp @@ -153,7 +153,7 @@ namespace LmbrCentral m_intersectionDataCache.UpdateIntersectionParams(m_currentTransform, m_diskShapeConfig); return AZ::Intersect::IntersectRayDisk( - src, dir, m_intersectionDataCache.m_position, m_intersectionDataCache.m_radius, m_intersectionDataCache.m_normal, distance) > 0; + src, dir, m_intersectionDataCache.m_position, m_intersectionDataCache.m_radius, m_intersectionDataCache.m_normal, distance); } void DiskShape::DiskIntersectionDataCache::UpdateIntersectionParamsImpl( From 5967b419a29805c1db276eb33704fff9ca4a3a67 Mon Sep 17 00:00:00 2001 From: Chris Burel Date: Thu, 14 Oct 2021 07:45:44 -0700 Subject: [PATCH 57/65] Only enable the Keyboard device in the XcbKeyboard unit tests (#4682) This prevents other input devices from interfering with the expected calls that the Keyboard tests should make. Signed-off-by: Chris Burel --- .../Input/System/InputSystemComponent.cpp | 20 +++++++++ .../Xcb/AzFramework/XcbNativeWindow.cpp | 1 + .../Xcb/XcbInputDeviceKeyboardTests.cpp | 42 ++++++++++--------- .../Platform/Common/Xcb/XcbTestApplication.h | 38 +++++++++++++++++ .../Xcb/azframework_xcb_tests_files.cmake | 1 + 5 files changed, 82 insertions(+), 20 deletions(-) create mode 100644 Code/Framework/AzFramework/Tests/Platform/Common/Xcb/XcbTestApplication.h diff --git a/Code/Framework/AzFramework/AzFramework/Input/System/InputSystemComponent.cpp b/Code/Framework/AzFramework/AzFramework/Input/System/InputSystemComponent.cpp index 33690f2246..14fb7ea5eb 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/System/InputSystemComponent.cpp +++ b/Code/Framework/AzFramework/AzFramework/Input/System/InputSystemComponent.cpp @@ -20,6 +20,7 @@ #include #include #include +#include //////////////////////////////////////////////////////////////////////////////////////////////////// namespace AzFramework @@ -190,6 +191,25 @@ namespace AzFramework //////////////////////////////////////////////////////////////////////////////////////////////// void InputSystemComponent::Activate() { + const auto* settingsRegistry = AZ::SettingsRegistry::Get(); + if (settingsRegistry) + { + AZ::u64 value = 0; + if (settingsRegistry->Get(value, "/O3DE/InputSystem/MouseMovementSampleRateHertz")) + { + m_mouseMovementSampleRateHertz = aznumeric_caster(value); + } + if (settingsRegistry->Get(value, "/O3DE/InputSystem/GamepadsEnabled")) + { + m_gamepadsEnabled = aznumeric_caster(value); + } + settingsRegistry->Get(m_keyboardEnabled, "/O3DE/InputSystem/KeyboardEnabled"); + settingsRegistry->Get(m_motionEnabled, "/O3DE/InputSystem/MotionEnabled"); + settingsRegistry->Get(m_mouseEnabled, "/O3DE/InputSystem/MouseEnabled"); + settingsRegistry->Get(m_touchEnabled, "/O3DE/InputSystem/TouchEnabled"); + settingsRegistry->Get(m_virtualKeyboardEnabled, "/O3DE/InputSystem/VirtualKeyboardEnabled"); + } + // Create all enabled input devices CreateEnabledInputDevices(); diff --git a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbNativeWindow.cpp b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbNativeWindow.cpp index 6ab97fd616..43f7e7140f 100644 --- a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbNativeWindow.cpp +++ b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbNativeWindow.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include diff --git a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/XcbInputDeviceKeyboardTests.cpp b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/XcbInputDeviceKeyboardTests.cpp index 2b1f21ede7..76d00eda50 100644 --- a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/XcbInputDeviceKeyboardTests.cpp +++ b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/XcbInputDeviceKeyboardTests.cpp @@ -12,7 +12,6 @@ #include -#include #include #include #include @@ -20,6 +19,7 @@ #include "Matchers.h" #include "Actions.h" #include "XcbBaseTestFixture.h" +#include "XcbTestApplication.h" template xcb_generic_event_t MakeEvent(T event) @@ -33,6 +33,7 @@ namespace AzFramework class XcbInputDeviceKeyboardTests : public XcbBaseTestFixture { + public: void SetUp() override { using testing::Return; @@ -123,6 +124,15 @@ namespace AzFramework static constexpr xcb_keycode_t s_keycodeForAKey{38}; static constexpr xcb_keycode_t s_keycodeForShiftLKey{50}; + + XcbTestApplication m_application{ + /*enabledGamepadsCount=*/0, + /*keyboardEnabled=*/true, + /*motionEnabled=*/false, + /*mouseEnabled=*/false, + /*touchEnabled=*/false, + /*virtualKeyboardEnabled=*/false + }; }; class InputTextNotificationListener @@ -195,27 +205,23 @@ namespace AzFramework EXPECT_CALL(m_interface, xkb_state_key_get_one_sym(&m_xkbState, s_keycodeForAKey)) .Times(2); - Application application; - application.Start({}, {}); - AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize); + m_application.Start(); const InputChannel* inputChannel = InputChannelRequests::FindInputChannel(InputDeviceKeyboard::Key::AlphanumericA); ASSERT_TRUE(inputChannel); EXPECT_THAT(inputChannel->GetState(), Eq(InputChannel::State::Idle)); - application.PumpSystemEventLoopUntilEmpty(); - application.TickSystem(); - application.Tick(); + m_application.PumpSystemEventLoopUntilEmpty(); + m_application.TickSystem(); + m_application.Tick(); EXPECT_THAT(inputChannel->GetState(), Eq(InputChannel::State::Began)); - application.PumpSystemEventLoopUntilEmpty(); - application.TickSystem(); - application.Tick(); + m_application.PumpSystemEventLoopUntilEmpty(); + m_application.TickSystem(); + m_application.Tick(); EXPECT_THAT(inputChannel->GetState(), Eq(InputChannel::State::Ended)); - - application.Stop(); } TEST_F(XcbInputDeviceKeyboardTests, TextEnteredFromXcbKeyPressEvents) @@ -420,17 +426,13 @@ namespace AzFramework EXPECT_CALL(textListener, OnInputTextEvent(StrEq("a"), _)).Times(1); EXPECT_CALL(textListener, OnInputTextEvent(StrEq("A"), _)).Times(1); - Application application; - application.Start({}, {}); - AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize); + m_application.Start(); for (int i = 0; i < 4; ++i) { - application.PumpSystemEventLoopUntilEmpty(); - application.TickSystem(); - application.Tick(); + m_application.PumpSystemEventLoopUntilEmpty(); + m_application.TickSystem(); + m_application.Tick(); } - - application.Stop(); } } // namespace AzFramework diff --git a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/XcbTestApplication.h b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/XcbTestApplication.h new file mode 100644 index 0000000000..3035a9de74 --- /dev/null +++ b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/XcbTestApplication.h @@ -0,0 +1,38 @@ +/* + * 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 + * + */ + +#pragma once + +#include +#include +#include + +namespace AzFramework +{ + class XcbTestApplication + : public Application + { + public: + XcbTestApplication(AZ::u64 enabledGamepadsCount, bool keyboardEnabled, bool motionEnabled, bool mouseEnabled, bool touchEnabled, bool virtualKeyboardEnabled) + { + auto* settingsRegistry = AZ::SettingsRegistry::Get(); + settingsRegistry->Set("/O3DE/InputSystem/GamepadsEnabled", enabledGamepadsCount); + settingsRegistry->Set("/O3DE/InputSystem/KeyboardEnabled", keyboardEnabled); + settingsRegistry->Set("/O3DE/InputSystem/MotionEnabled", motionEnabled); + settingsRegistry->Set("/O3DE/InputSystem/MouseEnabled", mouseEnabled); + settingsRegistry->Set("/O3DE/InputSystem/TouchEnabled", touchEnabled); + settingsRegistry->Set("/O3DE/InputSystem/VirtualKeyboardEnabled", virtualKeyboardEnabled); + } + + void Start(const Descriptor& descriptor = {}, const StartupParameters& startupParameters = {}) override + { + Application::Start(descriptor, startupParameters); + AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize); + } + }; +} // namespace AzFramework diff --git a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/azframework_xcb_tests_files.cmake b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/azframework_xcb_tests_files.cmake index 762d5d74fe..147fd2bfe1 100644 --- a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/azframework_xcb_tests_files.cmake +++ b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/azframework_xcb_tests_files.cmake @@ -17,4 +17,5 @@ set(FILES XcbBaseTestFixture.cpp XcbBaseTestFixture.h XcbInputDeviceKeyboardTests.cpp + XcbTestApplication.h ) From 8d7b03c8592ccc219f4de0629f8bed2386ba4774 Mon Sep 17 00:00:00 2001 From: Steve Pham <82231385+spham-amzn@users.noreply.github.com> Date: Thu, 14 Oct 2021 08:27:43 -0700 Subject: [PATCH 58/65] Implement open in file browser for Linux (#4677) * Add support to open folder browser for Linux * PAL'ified DesktopUtilities.cpp Signed-off-by: Steve Pham --- .../Platform/Linux/platform_linux_files.cmake | 1 + .../Platform/Mac/platform_mac_files.cmake | 1 + .../Windows/platform_windows_files.cmake | 1 + .../AzQtComponents/azqtcomponents_files.cmake | 1 - .../Utilities/DesktopUtilities_Linux.cpp | 49 +++++++++++++++++++ .../Utilities/DesktopUtilities_Mac.cpp} | 23 --------- .../Utilities/DesktopUtilities_Windows.cpp | 35 +++++++++++++ 7 files changed, 87 insertions(+), 24 deletions(-) create mode 100644 Code/Framework/AzQtComponents/Platform/Linux/AzQtComponents/Utilities/DesktopUtilities_Linux.cpp rename Code/Framework/AzQtComponents/{AzQtComponents/Utilities/DesktopUtilities.cpp => Platform/Mac/AzQtComponents/Utilities/DesktopUtilities_Mac.cpp} (67%) create mode 100644 Code/Framework/AzQtComponents/Platform/Windows/AzQtComponents/Utilities/DesktopUtilities_Windows.cpp diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Platform/Linux/platform_linux_files.cmake b/Code/Framework/AzQtComponents/AzQtComponents/Platform/Linux/platform_linux_files.cmake index ec71d7b508..f292c29c3c 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Platform/Linux/platform_linux_files.cmake +++ b/Code/Framework/AzQtComponents/AzQtComponents/Platform/Linux/platform_linux_files.cmake @@ -12,6 +12,7 @@ set(FILES ../../Utilities/QtWindowUtilities_linux.cpp ../../Utilities/ScreenGrabber_linux.cpp ../../../Platform/Linux/AzQtComponents/Components/StyledDockWidget_Linux.cpp + ../../../Platform/Linux/AzQtComponents/Utilities/DesktopUtilities_Linux.cpp ../../../Platform/Linux/AzQtComponents/AzQtComponents_Traits_Linux.h ../../../Platform/Linux/AzQtComponents/AzQtComponents_Traits_Platform.h ) diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Platform/Mac/platform_mac_files.cmake b/Code/Framework/AzQtComponents/AzQtComponents/Platform/Mac/platform_mac_files.cmake index 18f9479289..4addf53599 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Platform/Mac/platform_mac_files.cmake +++ b/Code/Framework/AzQtComponents/AzQtComponents/Platform/Mac/platform_mac_files.cmake @@ -12,6 +12,7 @@ set(FILES ../../Utilities/QtWindowUtilities_mac.mm ../../Utilities/ScreenGrabber_mac.mm ../../../Platform/Mac/AzQtComponents/Components/StyledDockWidget_Mac.cpp + ../../../Platform/Mac/AzQtComponents/Utilities/DesktopUtilities_Mac.cpp ../../../Platform/Mac/AzQtComponents/AzQtComponents_Traits_Mac.h ../../../Platform/Mac/AzQtComponents/AzQtComponents_Traits_Platform.h ) diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Platform/Windows/platform_windows_files.cmake b/Code/Framework/AzQtComponents/AzQtComponents/Platform/Windows/platform_windows_files.cmake index ca4145ef35..bf0d0bb785 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Platform/Windows/platform_windows_files.cmake +++ b/Code/Framework/AzQtComponents/AzQtComponents/Platform/Windows/platform_windows_files.cmake @@ -9,6 +9,7 @@ set(FILES ../../natvis/qt.natvis ../../../Platform/Windows/AzQtComponents/Utilities/HandleDpiAwareness_Windows.cpp + ../../../Platform/Windows/AzQtComponents/Utilities/DesktopUtilities_Windows.cpp ../../Utilities/MouseHider_win.cpp ../../Utilities/QtWindowUtilities_win.cpp ../../Utilities/ScreenGrabber_win.cpp diff --git a/Code/Framework/AzQtComponents/AzQtComponents/azqtcomponents_files.cmake b/Code/Framework/AzQtComponents/AzQtComponents/azqtcomponents_files.cmake index 8219ab04b2..4c343b852c 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/azqtcomponents_files.cmake +++ b/Code/Framework/AzQtComponents/AzQtComponents/azqtcomponents_files.cmake @@ -271,7 +271,6 @@ set(FILES Utilities/ColorUtilities.h Utilities/Conversions.h Utilities/Conversions.cpp - Utilities/DesktopUtilities.cpp Utilities/DesktopUtilities.h Utilities/HandleDpiAwareness.cpp Utilities/HandleDpiAwareness.h diff --git a/Code/Framework/AzQtComponents/Platform/Linux/AzQtComponents/Utilities/DesktopUtilities_Linux.cpp b/Code/Framework/AzQtComponents/Platform/Linux/AzQtComponents/Utilities/DesktopUtilities_Linux.cpp new file mode 100644 index 0000000000..e4ddaceec7 --- /dev/null +++ b/Code/Framework/AzQtComponents/Platform/Linux/AzQtComponents/Utilities/DesktopUtilities_Linux.cpp @@ -0,0 +1,49 @@ +/* + * 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 + +namespace AzQtComponents +{ + void ShowFileOnDesktop(const QString& path) + { + const char* defaultNautilusPath = "/usr/bin/nautilus"; + const char* defaultXdgOpenPath = "/usr/bin/xdg-open"; + + // Determine if Nautilus (for Gnome Desktops) is available because it supports opening the file manager + // and selecting a specific file + bool nautilusAvailable = QFileInfo(defaultNautilusPath).exists(); + + QFileInfo pathInfo(path); + if (pathInfo.isDir()) + { + QProcess::startDetached(defaultXdgOpenPath, { path }); + } + else + { + if (nautilusAvailable) + { + QProcess::startDetached(defaultNautilusPath, { "--select", path }); + } + else + { + QDir parentDir { pathInfo.dir() }; + QProcess::startDetached(defaultXdgOpenPath, { parentDir.path() }); + } + } + } + + QString fileBrowserActionName() + { + const char* exploreActionName = "Open in file browser"; + return QObject::tr(exploreActionName); + } +} diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Utilities/DesktopUtilities.cpp b/Code/Framework/AzQtComponents/Platform/Mac/AzQtComponents/Utilities/DesktopUtilities_Mac.cpp similarity index 67% rename from Code/Framework/AzQtComponents/AzQtComponents/Utilities/DesktopUtilities.cpp rename to Code/Framework/AzQtComponents/Platform/Mac/AzQtComponents/Utilities/DesktopUtilities_Mac.cpp index c210f17136..5920dc76c6 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Utilities/DesktopUtilities.cpp +++ b/Code/Framework/AzQtComponents/Platform/Mac/AzQtComponents/Utilities/DesktopUtilities_Mac.cpp @@ -15,21 +15,6 @@ namespace AzQtComponents { void ShowFileOnDesktop(const QString& path) { -#if defined(AZ_PLATFORM_WINDOWS) - - // Launch explorer at the path provided - QStringList args; - if (!QFileInfo(path).isDir()) - { - // Folders are just opened, files are selected - args << "/select,"; - } - args << QDir::toNativeSeparators(path); - - QProcess::startDetached("explorer", args); - -#else - if (QFileInfo(path).isDir()) { QProcess::startDetached("/usr/bin/osascript", { "-e", @@ -43,19 +28,11 @@ namespace AzQtComponents QProcess::startDetached("/usr/bin/osascript", { "-e", QStringLiteral("tell application \"Finder\" to activate") }); - -#endif } QString fileBrowserActionName() { -#ifdef AZ_PLATFORM_WINDOWS - const char* exploreActionName = "Open in Explorer"; -#elif defined(AZ_PLATFORM_MAC) const char* exploreActionName = "Open in Finder"; -#else - const char* exploreActionName = "Open in file browser"; -#endif return QObject::tr(exploreActionName); } } diff --git a/Code/Framework/AzQtComponents/Platform/Windows/AzQtComponents/Utilities/DesktopUtilities_Windows.cpp b/Code/Framework/AzQtComponents/Platform/Windows/AzQtComponents/Utilities/DesktopUtilities_Windows.cpp new file mode 100644 index 0000000000..796e2c0ccf --- /dev/null +++ b/Code/Framework/AzQtComponents/Platform/Windows/AzQtComponents/Utilities/DesktopUtilities_Windows.cpp @@ -0,0 +1,35 @@ +/* + * 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 + +namespace AzQtComponents +{ + void ShowFileOnDesktop(const QString& path) + { + // Launch explorer at the path provided + QStringList args; + if (!QFileInfo(path).isDir()) + { + // Folders are just opened, files are selected + args << "/select,"; + } + args << QDir::toNativeSeparators(path); + + QProcess::startDetached("explorer", args); + } + + QString fileBrowserActionName() + { + const char* exploreActionName = "Open in Explorer"; + return QObject::tr(exploreActionName); + } +} From 999ec261c9dcf060ed99555a740e7566f4701df6 Mon Sep 17 00:00:00 2001 From: Steve Pham <82231385+spham-amzn@users.noreply.github.com> Date: Thu, 14 Oct 2021 08:28:36 -0700 Subject: [PATCH 59/65] Update linux AP connection settings to connect to remote AP and wait for the connection (#4685) Signed-off-by: Steve Pham --- Registry/bootstrap.setreg | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Registry/bootstrap.setreg b/Registry/bootstrap.setreg index 3a15d59450..b2bcdd7f3f 100644 --- a/Registry/bootstrap.setreg +++ b/Registry/bootstrap.setreg @@ -17,7 +17,7 @@ "remote_port": 45643, "connect_to_remote": 0, "windows_connect_to_remote": 1, - "linux_connect_to_remote": 0, + "linux_connect_to_remote": 1, "provo_connect_to_remote": 1, "salem_connect_to_remote": 0, "jasper_connect_to_remote": 0, @@ -29,7 +29,7 @@ "salem_wait_for_connect": 0, "jasper_wait_for_connect": 0, "windows_wait_for_connect": 1, - "linux_wait_for_connect": 0, + "linux_wait_for_connect": 1, "android_wait_for_connect": 0, "ios_wait_for_connect": 0, "mac_wait_for_connect": 0, From 2dd00e298313b58c4ec9d4e3c023e4aa44ed5152 Mon Sep 17 00:00:00 2001 From: Tommy Walton <82672795+amzn-tommy@users.noreply.github.com> Date: Thu, 14 Oct 2021 08:58:50 -0700 Subject: [PATCH 60/65] Move wrinkle mask data out of default object srg (#4578) * Refactored depth, shadow, and motion vector shaders to support custom object srgs. Added a new skin object srg and moved wrinklemask data out of the default object srg. Added a new minimal morph target asset for testing wrinkle masks to AutomatedTesting. Signed-off-by: Tommy Walton * Fix copyright header Signed-off-by: Tommy Walton --- .../DisplayWrinkleMaskBlendValues.material | 13 +++ .../Objects/MorphTargets/morphActor.fbx | 3 + .../Morph1_wrinklemask.tif | 3 + .../Morph2_wriklemask.tif | 3 + .../Morph3_wrinklemask.tif | 3 + .../Objects/MorphTargets/morphAnimation.fbx | 3 + .../Common/Assets/Materials/Types/Skin.azsl | 2 +- .../Assets/Materials/Types/Skin.materialtype | 6 +- .../Atom/Features/PBR/DefaultObjectSrg.azsli | 10 --- .../Atom/Features/Skin/SkinObjectSrg.azsli | 81 ++++++++++++++++++ .../Assets/Shaders/Depth/DepthPass.azsl | 27 +----- .../Shaders/Depth/DepthPassCommon.azsli | 36 ++++++++ .../Assets/Shaders/Depth/DepthPassSkin.azsl | 12 +++ .../Assets/Shaders/Depth/DepthPassSkin.shader | 24 ++++++ .../MotionVector/MeshMotionVector.azsl | 74 +---------------- .../MotionVector/MeshMotionVectorCommon.azsli | 83 +++++++++++++++++++ .../MotionVector/MeshMotionVectorSkin.azsl | 12 +++ .../MotionVector/MeshMotionVectorSkin.shader | 24 ++++++ .../Assets/Shaders/Shadow/Shadowmap.azsl | 24 +----- .../Shaders/Shadow/ShadowmapCommon.azsli | 33 ++++++++ .../Assets/Shaders/Shadow/ShadowmapSkin.azsl | 12 +++ .../Shaders/Shadow/ShadowmapSkin.shader | 26 ++++++ 22 files changed, 381 insertions(+), 133 deletions(-) create mode 100644 AutomatedTesting/Objects/MorphTargets/DisplayWrinkleMaskBlendValues.material create mode 100644 AutomatedTesting/Objects/MorphTargets/morphActor.fbx create mode 100644 AutomatedTesting/Objects/MorphTargets/morphActor_wrinklemasks/Morph1_wrinklemask.tif create mode 100644 AutomatedTesting/Objects/MorphTargets/morphActor_wrinklemasks/Morph2_wriklemask.tif create mode 100644 AutomatedTesting/Objects/MorphTargets/morphActor_wrinklemasks/Morph3_wrinklemask.tif create mode 100644 AutomatedTesting/Objects/MorphTargets/morphAnimation.fbx create mode 100644 Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Skin/SkinObjectSrg.azsli create mode 100644 Gems/Atom/Feature/Common/Assets/Shaders/Depth/DepthPassCommon.azsli create mode 100644 Gems/Atom/Feature/Common/Assets/Shaders/Depth/DepthPassSkin.azsl create mode 100644 Gems/Atom/Feature/Common/Assets/Shaders/Depth/DepthPassSkin.shader create mode 100644 Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/MeshMotionVectorCommon.azsli create mode 100644 Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/MeshMotionVectorSkin.azsl create mode 100644 Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/MeshMotionVectorSkin.shader create mode 100644 Gems/Atom/Feature/Common/Assets/Shaders/Shadow/ShadowmapCommon.azsli create mode 100644 Gems/Atom/Feature/Common/Assets/Shaders/Shadow/ShadowmapSkin.azsl create mode 100644 Gems/Atom/Feature/Common/Assets/Shaders/Shadow/ShadowmapSkin.shader diff --git a/AutomatedTesting/Objects/MorphTargets/DisplayWrinkleMaskBlendValues.material b/AutomatedTesting/Objects/MorphTargets/DisplayWrinkleMaskBlendValues.material new file mode 100644 index 0000000000..878b3ac39f --- /dev/null +++ b/AutomatedTesting/Objects/MorphTargets/DisplayWrinkleMaskBlendValues.material @@ -0,0 +1,13 @@ +{ + "description": "", + "materialType": "Materials/Types/Skin.materialtype", + "parentMaterial": "", + "propertyLayoutVersion": 3, + "properties": { + "wrinkleLayers": { + "count": 3, + "enable": true, + "showBlendValues": true + } + } +} diff --git a/AutomatedTesting/Objects/MorphTargets/morphActor.fbx b/AutomatedTesting/Objects/MorphTargets/morphActor.fbx new file mode 100644 index 0000000000..ffb75e680e --- /dev/null +++ b/AutomatedTesting/Objects/MorphTargets/morphActor.fbx @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:53e17ec8155911c8b42e85436130f600bd6dddd8931a8ccb1b2f8a9f8674cc85 +size 45104 diff --git a/AutomatedTesting/Objects/MorphTargets/morphActor_wrinklemasks/Morph1_wrinklemask.tif b/AutomatedTesting/Objects/MorphTargets/morphActor_wrinklemasks/Morph1_wrinklemask.tif new file mode 100644 index 0000000000..3bc18cf450 --- /dev/null +++ b/AutomatedTesting/Objects/MorphTargets/morphActor_wrinklemasks/Morph1_wrinklemask.tif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0da56a05daa0ec1c476cfe25ca6d3b65267c98886cf33408f6e852fb325a8e2c +size 198084 diff --git a/AutomatedTesting/Objects/MorphTargets/morphActor_wrinklemasks/Morph2_wriklemask.tif b/AutomatedTesting/Objects/MorphTargets/morphActor_wrinklemasks/Morph2_wriklemask.tif new file mode 100644 index 0000000000..39e70f5acf --- /dev/null +++ b/AutomatedTesting/Objects/MorphTargets/morphActor_wrinklemasks/Morph2_wriklemask.tif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e3537fbe9205731a242251c525a67bbb5f3b8f5307537f1dc0c318b5b885ce52 +size 198112 diff --git a/AutomatedTesting/Objects/MorphTargets/morphActor_wrinklemasks/Morph3_wrinklemask.tif b/AutomatedTesting/Objects/MorphTargets/morphActor_wrinklemasks/Morph3_wrinklemask.tif new file mode 100644 index 0000000000..43e19f0d5f --- /dev/null +++ b/AutomatedTesting/Objects/MorphTargets/morphActor_wrinklemasks/Morph3_wrinklemask.tif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bd794d5dd4b749c3275bfab79b9b5ae3f8e007d3e6741c0566c9c2d3931123bf +size 198112 diff --git a/AutomatedTesting/Objects/MorphTargets/morphAnimation.fbx b/AutomatedTesting/Objects/MorphTargets/morphAnimation.fbx new file mode 100644 index 0000000000..c0dcc007dc --- /dev/null +++ b/AutomatedTesting/Objects/MorphTargets/morphAnimation.fbx @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:45ded862987a64061deffd8e4c9aa1dff4eec3bcff5f7b505679f1959e8ae137 +size 51440 diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.azsl index 05059aba6f..523353f8fa 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.azsl @@ -9,7 +9,7 @@ #include "Skin_Common.azsli" // SRGs -#include +#include #include // Pass Output diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.materialtype b/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.materialtype index 0969cc36e8..e4da9c6022 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.materialtype +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.materialtype @@ -973,15 +973,15 @@ "tag": "ForwardPass" }, { - "file": "Shaders/Shadow/Shadowmap.shader", + "file": "Shaders/Shadow/ShadowmapSkin.shader", "tag": "Shadowmap" }, { - "file": "Shaders/Depth/DepthPass.shader", + "file": "Shaders/Depth/DepthPassSkin.shader", "tag": "DepthPass" }, { - "file": "Shaders/MotionVector/MeshMotionVector.shader", + "file": "Shaders/MotionVector/MeshMotionVectorSkin.shader", "tag": "MeshMotionVector" } ], diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/DefaultObjectSrg.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/DefaultObjectSrg.azsli index 7a2d2ee428..10526597cb 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/DefaultObjectSrg.azsli +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/DefaultObjectSrg.azsli @@ -27,16 +27,6 @@ ShaderResourceGroup ObjectSrg : SRG_PerObject return SceneSrg::GetObjectToWorldInverseTransposeMatrix(m_objectId); } - //[GFX TODO][ATOM-15280] Move wrinkle mask data from the default object srg into something specific to the Skin shader - uint m_wrinkle_mask_count; - float4 m_wrinkle_mask_weights[4]; - Texture2D m_wrinkle_masks[16]; - - float GetWrinkleMaskWeight(uint index) - { - return m_wrinkle_mask_weights[index / 4][index % 4]; - } - //! Reflection Probe (smallest probe volume that overlaps the object position) struct ReflectionProbeData { diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Skin/SkinObjectSrg.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Skin/SkinObjectSrg.azsli new file mode 100644 index 0000000000..d0766c295d --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Skin/SkinObjectSrg.azsli @@ -0,0 +1,81 @@ +/* + * 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 + * + */ + +#pragma once + +#include + +ShaderResourceGroup ObjectSrg : SRG_PerObject +{ + uint m_objectId; + + //! Returns the matrix for transforming points from Object Space to World Space. + float4x4 GetWorldMatrix() + { + return SceneSrg::GetObjectToWorldMatrix(m_objectId); + } + + //! Returns the inverse-transpose of the world matrix. + //! Commonly used to transform normals while supporting non-uniform scale. + float3x3 GetWorldMatrixInverseTranspose() + { + return SceneSrg::GetObjectToWorldInverseTransposeMatrix(m_objectId); + } + + uint m_wrinkle_mask_count; + float4 m_wrinkle_mask_weights[4]; + Texture2D m_wrinkle_masks[16]; + + float GetWrinkleMaskWeight(uint index) + { + return m_wrinkle_mask_weights[index / 4][index % 4]; + } + + //! Reflection Probe (smallest probe volume that overlaps the object position) + struct ReflectionProbeData + { + row_major float3x4 m_modelToWorld; + row_major float3x4 m_modelToWorldInverse; // does not include extents + float3 m_outerObbHalfLengths; + float3 m_innerObbHalfLengths; + float m_padding; + bool m_useReflectionProbe; + bool m_useParallaxCorrection; + }; + + ReflectionProbeData m_reflectionProbeData; + TextureCube m_reflectionProbeCubeMap; + + float4x4 GetReflectionProbeWorldMatrix() + { + float4x4 modelToWorld = float4x4( + float4(1, 0, 0, 0), + float4(0, 1, 0, 0), + float4(0, 0, 1, 0), + float4(0, 0, 0, 1)); + + modelToWorld[0] = m_reflectionProbeData.m_modelToWorld[0]; + modelToWorld[1] = m_reflectionProbeData.m_modelToWorld[1]; + modelToWorld[2] = m_reflectionProbeData.m_modelToWorld[2]; + return modelToWorld; + } + + float4x4 GetReflectionProbeWorldMatrixInverse() + { + float4x4 modelToWorldInverse = float4x4( + float4(1, 0, 0, 0), + float4(0, 1, 0, 0), + float4(0, 0, 1, 0), + float4(0, 0, 0, 1)); + + modelToWorldInverse[0] = m_reflectionProbeData.m_modelToWorldInverse[0]; + modelToWorldInverse[1] = m_reflectionProbeData.m_modelToWorldInverse[1]; + modelToWorldInverse[2] = m_reflectionProbeData.m_modelToWorldInverse[2]; + return modelToWorldInverse; + } +} diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/Depth/DepthPass.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/Depth/DepthPass.azsl index 8eb657f8f5..876f4b1a61 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/Depth/DepthPass.azsl +++ b/Gems/Atom/Feature/Common/Assets/Shaders/Depth/DepthPass.azsl @@ -6,30 +6,7 @@ * */ -#include #include +#include -struct VSInput -{ - float3 m_position : POSITION; -}; - -struct VSDepthOutput -{ - float4 m_position : SV_Position; -}; - -VSDepthOutput DepthPassVS(VSInput IN) -{ - VSDepthOutput OUT; - - float4x4 objectToWorld = ObjectSrg::GetWorldMatrix(); - float4 worldPosition = mul(objectToWorld, float4(IN.m_position, 1.0)); - OUT.m_position = mul(ViewSrg::m_viewProjectionMatrix, worldPosition); - - return OUT; -} - - - - +// Use the depth pass shader with the default object srg diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/Depth/DepthPassCommon.azsli b/Gems/Atom/Feature/Common/Assets/Shaders/Depth/DepthPassCommon.azsli new file mode 100644 index 0000000000..f658dd13da --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Shaders/Depth/DepthPassCommon.azsli @@ -0,0 +1,36 @@ +/* + * 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 + * + */ + +#pragma once + +#include + +struct VSInput +{ + float3 m_position : POSITION; +}; + +struct VSDepthOutput +{ + float4 m_position : SV_Position; +}; + +VSDepthOutput DepthPassVS(VSInput IN) +{ + VSDepthOutput OUT; + + float4x4 objectToWorld = ObjectSrg::GetWorldMatrix(); + float4 worldPosition = mul(objectToWorld, float4(IN.m_position, 1.0)); + OUT.m_position = mul(ViewSrg::m_viewProjectionMatrix, worldPosition); + + return OUT; +} + + + + diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/Depth/DepthPassSkin.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/Depth/DepthPassSkin.azsl new file mode 100644 index 0000000000..0947be6cf8 --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Shaders/Depth/DepthPassSkin.azsl @@ -0,0 +1,12 @@ +/* + * 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 + +// Use the depth pass shader with the skin object srg diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/Depth/DepthPassSkin.shader b/Gems/Atom/Feature/Common/Assets/Shaders/Depth/DepthPassSkin.shader new file mode 100644 index 0000000000..de6c989223 --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Shaders/Depth/DepthPassSkin.shader @@ -0,0 +1,24 @@ +{ + "Source" : "DepthPassSkin", + + "DepthStencilState" : { + "Depth" : { "Enable" : true, "CompareFunc" : "GreaterEqual" } + }, + + "CompilerHints" : { + "DisableOptimizations" : false + }, + + "ProgramSettings" : + { + "EntryPoints": + [ + { + "name": "DepthPassVS", + "type" : "Vertex" + } + ] + }, + + "DrawList" : "depth" +} diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/MeshMotionVector.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/MeshMotionVector.azsl index 11cf20dcaf..a14d07194f 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/MeshMotionVector.azsl +++ b/Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/MeshMotionVector.azsl @@ -6,77 +6,7 @@ * */ -#include -#include - #include -#include +#include -struct VSInput -{ - float3 m_position : POSITION; - - // This gets set automatically by the system at runtime only if it's available. - // There is a soft naming convention that associates this with o_prevPosition_isBound, which will be set to true whenever m_optional_prevPosition is available. - // (search "m_optional_" in ShaderVariantAssetBuilder for details on the naming convention). - // [GFX TODO][ATOM-14475]: Come up with a more elegant way to associate the isBound flag with the input stream. - // Vertex position of last frame to capture small scale motion due to vertex animation - float3 m_optional_prevPosition : POSITIONT; -}; - -struct VSOutput -{ - float4 m_position : SV_Position; - float3 m_worldPos : TEXCOORD0; - float3 m_worldPosPrev: TEXCOORD1; -}; - -struct PSOutput -{ - float2 m_motion : SV_Target0; -}; - -// Indicates whether the vertex input struct's "m_optional_prevPosition" is bound. If false, it is not safe to read from m_optional_prevPosition. -// This option gets set automatically by the system at runtime; there is a soft naming convention that associates it with m_optional_prevPosition. -// (search "m_optional_" in ShaderVariantAssetBuilder for details on the naming convention). -// [GFX TODO][ATOM-14475]: Come up with a more elegant way to associate the isBound flag with the input stream. -option bool o_prevPosition_isBound; - -VSOutput MainVS(VSInput IN) -{ - VSOutput OUT; - - OUT.m_worldPos = mul(SceneSrg::GetObjectToWorldMatrix(ObjectSrg::m_objectId), float4(IN.m_position, 1.0)).xyz; - OUT.m_position = mul(ViewSrg::m_viewProjectionMatrix, float4(OUT.m_worldPos, 1.0)); - - if (o_prevPosition_isBound) - { - OUT.m_worldPosPrev = mul(SceneSrg::GetObjectToWorldMatrixPrev(ObjectSrg::m_objectId), float4(IN.m_optional_prevPosition, 1.0)).xyz; - } - else - { - OUT.m_worldPosPrev = mul(SceneSrg::GetObjectToWorldMatrixPrev(ObjectSrg::m_objectId), float4(IN.m_position, 1.0)).xyz; - } - - return OUT; -} - -PSOutput MainPS(VSOutput IN) -{ - PSOutput OUT; - - // Current clip position - float4 clipPos = mul(ViewSrg::m_viewProjectionMatrix, float4(IN.m_worldPos, 1.0)); - - // Reprojected last frame's clip position, for skinned mesh it also implies last key frame - float4 clipPosPrev = mul(ViewSrg::m_viewProjectionPrevMatrix, float4(IN.m_worldPosPrev, 1.0)); - - float2 motion = (clipPos.xy / clipPos.w - clipPosPrev.xy / clipPosPrev.w) * 0.5; - - OUT.m_motion = motion; - - // Flip y to line up with uv coordinates - OUT.m_motion.y = -OUT.m_motion.y; - - return OUT; -} +// Use the mesh motion vector with the default object srg diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/MeshMotionVectorCommon.azsli b/Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/MeshMotionVectorCommon.azsli new file mode 100644 index 0000000000..c934b056db --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/MeshMotionVectorCommon.azsli @@ -0,0 +1,83 @@ +/* + * 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 + * + */ + +#pragma once + +#include +#include + +#include + +struct VSInput +{ + float3 m_position : POSITION; + + // This gets set automatically by the system at runtime only if it's available. + // There is a soft naming convention that associates this with o_prevPosition_isBound, which will be set to true whenever m_optional_prevPosition is available. + // (search "m_optional_" in ShaderVariantAssetBuilder for details on the naming convention). + // [GFX TODO][ATOM-14475]: Come up with a more elegant way to associate the isBound flag with the input stream. + // Vertex position of last frame to capture small scale motion due to vertex animation + float3 m_optional_prevPosition : POSITIONT; +}; + +struct VSOutput +{ + float4 m_position : SV_Position; + float3 m_worldPos : TEXCOORD0; + float3 m_worldPosPrev: TEXCOORD1; +}; + +struct PSOutput +{ + float2 m_motion : SV_Target0; +}; + +// Indicates whether the vertex input struct's "m_optional_prevPosition" is bound. If false, it is not safe to read from m_optional_prevPosition. +// This option gets set automatically by the system at runtime; there is a soft naming convention that associates it with m_optional_prevPosition. +// (search "m_optional_" in ShaderVariantAssetBuilder for details on the naming convention). +// [GFX TODO][ATOM-14475]: Come up with a more elegant way to associate the isBound flag with the input stream. +option bool o_prevPosition_isBound; + +VSOutput MainVS(VSInput IN) +{ + VSOutput OUT; + + OUT.m_worldPos = mul(SceneSrg::GetObjectToWorldMatrix(ObjectSrg::m_objectId), float4(IN.m_position, 1.0)).xyz; + OUT.m_position = mul(ViewSrg::m_viewProjectionMatrix, float4(OUT.m_worldPos, 1.0)); + + if (o_prevPosition_isBound) + { + OUT.m_worldPosPrev = mul(SceneSrg::GetObjectToWorldMatrixPrev(ObjectSrg::m_objectId), float4(IN.m_optional_prevPosition, 1.0)).xyz; + } + else + { + OUT.m_worldPosPrev = mul(SceneSrg::GetObjectToWorldMatrixPrev(ObjectSrg::m_objectId), float4(IN.m_position, 1.0)).xyz; + } + + return OUT; +} + +PSOutput MainPS(VSOutput IN) +{ + PSOutput OUT; + + // Current clip position + float4 clipPos = mul(ViewSrg::m_viewProjectionMatrix, float4(IN.m_worldPos, 1.0)); + + // Reprojected last frame's clip position, for skinned mesh it also implies last key frame + float4 clipPosPrev = mul(ViewSrg::m_viewProjectionPrevMatrix, float4(IN.m_worldPosPrev, 1.0)); + + float2 motion = (clipPos.xy / clipPos.w - clipPosPrev.xy / clipPosPrev.w) * 0.5; + + OUT.m_motion = motion; + + // Flip y to line up with uv coordinates + OUT.m_motion.y = -OUT.m_motion.y; + + return OUT; +} diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/MeshMotionVectorSkin.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/MeshMotionVectorSkin.azsl new file mode 100644 index 0000000000..fc089ac7a3 --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/MeshMotionVectorSkin.azsl @@ -0,0 +1,12 @@ +/* + * 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 + +// Use the mesh motion vector with the skin object srg diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/MeshMotionVectorSkin.shader b/Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/MeshMotionVectorSkin.shader new file mode 100644 index 0000000000..9a50e4e2cf --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/MeshMotionVectorSkin.shader @@ -0,0 +1,24 @@ +{ + "Source" : "MeshMotionVectorSkin", + + "DepthStencilState" : { + "Depth" : { "Enable" : true, "CompareFunc" : "GreaterEqual" } + }, + + "DrawList" : "motion", + + "ProgramSettings": + { + "EntryPoints": + [ + { + "name": "MainVS", + "type": "Vertex" + }, + { + "name": "MainPS", + "type": "Fragment" + } + ] + } +} diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/Shadow/Shadowmap.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/Shadow/Shadowmap.azsl index 713ff7c393..7a958a73ca 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/Shadow/Shadowmap.azsl +++ b/Gems/Atom/Feature/Common/Assets/Shaders/Shadow/Shadowmap.azsl @@ -6,27 +6,7 @@ * */ -#include -#include #include +#include -struct VertexInput -{ - float3 m_position : POSITION; -}; - -struct VertexOutput -{ - float4 m_position : SV_Position; -}; - -VertexOutput MainVS(VertexInput input) -{ - const float4x4 worldMatrix = ObjectSrg::GetWorldMatrix(); - VertexOutput output; - - const float3 worldPosition = mul(worldMatrix, float4(input.m_position, 1.0)).xyz; - output.m_position = mul(ViewSrg::m_viewProjectionMatrix, float4(worldPosition, 1.0)); - - return output; -} +// Use the shadowmap shader with the default object srg diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/Shadow/ShadowmapCommon.azsli b/Gems/Atom/Feature/Common/Assets/Shaders/Shadow/ShadowmapCommon.azsli new file mode 100644 index 0000000000..213fb18dc4 --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Shaders/Shadow/ShadowmapCommon.azsli @@ -0,0 +1,33 @@ +/* + * 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 + * + */ + +#pragma once + +#include +#include + +struct VertexInput +{ + float3 m_position : POSITION; +}; + +struct VertexOutput +{ + float4 m_position : SV_Position; +}; + +VertexOutput MainVS(VertexInput input) +{ + const float4x4 worldMatrix = ObjectSrg::GetWorldMatrix(); + VertexOutput output; + + const float3 worldPosition = mul(worldMatrix, float4(input.m_position, 1.0)).xyz; + output.m_position = mul(ViewSrg::m_viewProjectionMatrix, float4(worldPosition, 1.0)); + + return output; +} diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/Shadow/ShadowmapSkin.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/Shadow/ShadowmapSkin.azsl new file mode 100644 index 0000000000..6f0d8e1a31 --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Shaders/Shadow/ShadowmapSkin.azsl @@ -0,0 +1,12 @@ +/* + * 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 + +// Use the shadowmap shader with the skin object srg diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/Shadow/ShadowmapSkin.shader b/Gems/Atom/Feature/Common/Assets/Shaders/Shadow/ShadowmapSkin.shader new file mode 100644 index 0000000000..14c1352c08 --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Shaders/Shadow/ShadowmapSkin.shader @@ -0,0 +1,26 @@ +{ + "Source" : "ShadowmapSkin", + + "DepthStencilState" : { + "Depth" : { "Enable" : true, "CompareFunc" : "LessEqual" } + }, + + "DrawList" : "shadow", + + "RasterState" : + { + "depthBias" : "10", + "depthBiasSlopeScale" : "4" + }, + + "ProgramSettings": + { + "EntryPoints": + [ + { + "name": "MainVS", + "type": "Vertex" + } + ] + } +} From 1fc69aa9c51dfc25ee26abac90c3202ea35b1e14 Mon Sep 17 00:00:00 2001 From: Chris Galvan Date: Thu, 14 Oct 2021 11:15:21 -0500 Subject: [PATCH 61/65] Set EDITOR_TEST_SUPPORTED to false for Android/iOS in the template. Signed-off-by: Chris Galvan --- .../CustomTool/Template/Code/Platform/Android/PAL_android.cmake | 2 +- Templates/CustomTool/Template/Code/Platform/iOS/PAL_ios.cmake | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Templates/CustomTool/Template/Code/Platform/Android/PAL_android.cmake b/Templates/CustomTool/Template/Code/Platform/Android/PAL_android.cmake index 49dfe71f53..90d1caccf4 100644 --- a/Templates/CustomTool/Template/Code/Platform/Android/PAL_android.cmake +++ b/Templates/CustomTool/Template/Code/Platform/Android/PAL_android.cmake @@ -8,4 +8,4 @@ set(PAL_TRAIT_${NameUpper}_SUPPORTED TRUE) set(PAL_TRAIT_${NameUpper}_TEST_SUPPORTED TRUE) -set(PAL_TRAIT_${NameUpper}_EDITOR_TEST_SUPPORTED TRUE) +set(PAL_TRAIT_${NameUpper}_EDITOR_TEST_SUPPORTED FALSE) diff --git a/Templates/CustomTool/Template/Code/Platform/iOS/PAL_ios.cmake b/Templates/CustomTool/Template/Code/Platform/iOS/PAL_ios.cmake index 0abcd887e8..332f4469b6 100644 --- a/Templates/CustomTool/Template/Code/Platform/iOS/PAL_ios.cmake +++ b/Templates/CustomTool/Template/Code/Platform/iOS/PAL_ios.cmake @@ -8,4 +8,4 @@ set(PAL_TRAIT_${NameUpper}_SUPPORTED TRUE) set(PAL_TRAIT_${NameUpper}_TEST_SUPPORTED TRUE) -set(PAL_TRAIT_${NameUpper}_EDITOR_TEST_SUPPORTED TRUE) \ No newline at end of file +set(PAL_TRAIT_${NameUpper}_EDITOR_TEST_SUPPORTED FALSE) \ No newline at end of file From 63ece6e3ca035087b9ee7e31151aefefef0f975c Mon Sep 17 00:00:00 2001 From: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Thu, 14 Oct 2021 11:54:13 -0500 Subject: [PATCH 62/65] Change Asset Hint fixup code to not request assets be queued for load. (#4664) Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> --- .../AzToolsFramework/AzToolsFramework/Prefab/PrefabDomUtils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabDomUtils.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabDomUtils.cpp index ea0fa55256..a84d6bf706 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabDomUtils.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabDomUtils.cpp @@ -262,7 +262,7 @@ namespace AzToolsFramework if (assetId.IsValid()) { - asset.Create(assetId, true); + asset.Create(assetId, false); } } }; From c7e690706404ce745cf3fce9c6d92d82d5b905db Mon Sep 17 00:00:00 2001 From: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> Date: Thu, 14 Oct 2021 13:03:19 -0500 Subject: [PATCH 63/65] Flipped y value on uv so that the macro material lines up with the corresponding height data. (#4701) Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> --- .../Code/Source/TerrainRenderer/TerrainFeatureProcessor.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.cpp b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.cpp index d7300cdc48..8c85e21490 100644 --- a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.cpp +++ b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.cpp @@ -567,13 +567,15 @@ namespace Terrain ShaderMacroMaterialData& shaderData = macroMaterialData.at(i); const AZ::Aabb& materialBounds = materialData.m_bounds; + // Use reverse coordinates (1 - y) for the y direction so that the lower left corner of the macro material images + // map to the lower left corner in world space. This will match up with the height uv coordinate mapping. shaderData.m_uvMin = { (xPatch - materialBounds.GetMin().GetX()) / materialBounds.GetXExtent(), - (yPatch - materialBounds.GetMin().GetY()) / materialBounds.GetYExtent() + 1.0f - ((yPatch - materialBounds.GetMin().GetY()) / materialBounds.GetYExtent()) }; shaderData.m_uvMax = { ((xPatch + GridMeters) - materialBounds.GetMin().GetX()) / materialBounds.GetXExtent(), - ((yPatch + GridMeters) - materialBounds.GetMin().GetY()) / materialBounds.GetYExtent() + 1.0f - (((yPatch + GridMeters) - materialBounds.GetMin().GetY()) / materialBounds.GetYExtent()) }; shaderData.m_normalFactor = materialData.m_normalFactor; shaderData.m_flipNormalX = materialData.m_normalFlipX; From 16a7b896ee27a4a2814361ef9b1e3d0e7fb6572c Mon Sep 17 00:00:00 2001 From: Steve Pham <82231385+spham-amzn@users.noreply.github.com> Date: Thu, 14 Oct 2021 12:58:53 -0700 Subject: [PATCH 64/65] Fix to prevent using legacy windows based logic to create a Path on Linx (#4704) Signed-off-by: Steve Pham --- Code/Editor/Util/FileUtil.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Code/Editor/Util/FileUtil.cpp b/Code/Editor/Util/FileUtil.cpp index eeb6912acf..baca69d628 100644 --- a/Code/Editor/Util/FileUtil.cpp +++ b/Code/Editor/Util/FileUtil.cpp @@ -1195,7 +1195,7 @@ bool CFileUtil::IsFileExclusivelyAccessable(const QString& strFilePath) ////////////////////////////////////////////////////////////////////////// bool CFileUtil::CreatePath(const QString& strPath) { -#if defined(AZ_PLATFORM_MAC) +#if !AZ_TRAIT_OS_USE_WINDOWS_FILE_PATHS bool pathCreated = true; QString cleanPath = QDir::cleanPath(strPath); @@ -1252,7 +1252,7 @@ bool CFileUtil::CreatePath(const QString& strPath) } return true; -#endif +#endif // !AZ_TRAIT_OS_USE_WINDOWS_FILE_PATHS } ////////////////////////////////////////////////////////////////////////// From bcf3980de6295a1cb522f20e8482c3222625542c Mon Sep 17 00:00:00 2001 From: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> Date: Thu, 14 Oct 2021 15:32:34 -0700 Subject: [PATCH 65/65] LYN-7191 + LYN-7194 | Adjust Prefab operations to conform with Prefab Focus/Edit workflows. (#4684) * Disable ability to delete container entity of focused prefab. Default entity creation to parent to container entity of focused prefab. Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> * Disable detach and duplicate operations for the container of the focused prefab. Update the context menu accordingly. Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> * Fix spacing Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> * Address minor issues from PR (error message, optimization in RetrieveAndSortPrefabEntitiesAndInstances). Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> --- .../Prefab/PrefabPublicHandler.cpp | 76 +++++++++++++------ .../Prefab/PrefabPublicHandler.h | 4 +- .../UI/Prefab/PrefabIntegrationManager.cpp | 56 +++++++++----- .../UI/Prefab/PrefabUiHandler.cpp | 6 -- 4 files changed, 91 insertions(+), 51 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp index b5f61b33bf..081655a166 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp @@ -43,6 +43,12 @@ namespace AzToolsFramework m_instanceToTemplateInterface = AZ::Interface::Get(); AZ_Assert(m_instanceToTemplateInterface, "PrefabPublicHandler - Could not retrieve instance of InstanceToTemplateInterface"); + m_prefabFocusInterface = AZ::Interface::Get(); + AZ_Assert(m_prefabFocusInterface, "Could not get PrefabFocusInterface on PrefabPublicHandler construction."); + + m_prefabFocusPublicInterface = AZ::Interface::Get(); + AZ_Assert(m_prefabFocusPublicInterface, "Could not get PrefabFocusPublicInterface on PrefabPublicHandler construction."); + m_prefabLoaderInterface = AZ::Interface::Get(); AZ_Assert(m_prefabLoaderInterface, "Could not get PrefabLoaderInterface on PrefabPublicHandler construction."); @@ -552,6 +558,13 @@ namespace AzToolsFramework PrefabEntityResult PrefabPublicHandler::CreateEntity(AZ::EntityId parentId, const AZ::Vector3& position) { + // If the parent is invalid, parent to the container of the currently focused prefab. + if (!parentId.IsValid()) + { + AzFramework::EntityContextId editorEntityContextId = AzToolsFramework::GetEntityContextId(); + parentId = m_prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(editorEntityContextId); + } + InstanceOptionalReference owningInstanceOfParentEntity = GetOwnerInstanceByEntityId(parentId); if (!owningInstanceOfParentEntity) { @@ -968,13 +981,13 @@ namespace AzToolsFramework return AZ::Failure(AZStd::string("No entities to duplicate.")); } - const EntityIdList entityIdsNoLevelInstance = GenerateEntityIdListWithoutLevelInstance(entityIds); - if (entityIdsNoLevelInstance.empty()) + const EntityIdList entityIdsNoFocusContainer = GenerateEntityIdListWithoutFocusedInstanceContainer(entityIds); + if (entityIdsNoFocusContainer.empty()) { - return AZ::Failure(AZStd::string("No entities to duplicate because only instance selected is the level instance.")); + return AZ::Failure(AZStd::string("No entities to duplicate because only instance selected is the container entity of the focused instance.")); } - if (!EntitiesBelongToSameInstance(entityIdsNoLevelInstance)) + if (!EntitiesBelongToSameInstance(entityIdsNoFocusContainer)) { return AZ::Failure(AZStd::string("Cannot duplicate multiple entities belonging to different instances with one operation." "Change your selection to contain entities in the same instance.")); @@ -982,7 +995,7 @@ namespace AzToolsFramework // We've already verified the entities are all owned by the same instance, // so we can just retrieve our instance from the first entity in the list. - AZ::EntityId firstEntityIdToDuplicate = entityIdsNoLevelInstance[0]; + AZ::EntityId firstEntityIdToDuplicate = entityIdsNoFocusContainer[0]; InstanceOptionalReference commonOwningInstance = GetOwnerInstanceByEntityId(firstEntityIdToDuplicate); if (!commonOwningInstance.has_value()) { @@ -1002,7 +1015,7 @@ namespace AzToolsFramework // This will cull out any entities that have ancestors in the list, since we will end up duplicating // the full nested hierarchy with what is returned from RetrieveAndSortPrefabEntitiesAndInstances - AzToolsFramework::EntityIdSet duplicationSet = AzToolsFramework::GetCulledEntityHierarchy(entityIdsNoLevelInstance); + AzToolsFramework::EntityIdSet duplicationSet = AzToolsFramework::GetCulledEntityHierarchy(entityIdsNoFocusContainer); AZ_PROFILE_FUNCTION(AzToolsFramework); @@ -1106,19 +1119,21 @@ namespace AzToolsFramework PrefabOperationResult PrefabPublicHandler::DeleteFromInstance(const EntityIdList& entityIds, bool deleteDescendants) { - const EntityIdList entityIdsNoLevelInstance = GenerateEntityIdListWithoutLevelInstance(entityIds); + // Remove the container entity of the focused prefab from the list, if it is included. + const EntityIdList entityIdsNoFocusContainer = GenerateEntityIdListWithoutFocusedInstanceContainer(entityIds); - if (entityIdsNoLevelInstance.empty()) + if (entityIdsNoFocusContainer.empty()) { return AZ::Success(); } - if (!EntitiesBelongToSameInstance(entityIdsNoLevelInstance)) + // All entities in this list need to belong to the same prefab instance for the operation to be valid. + if (!EntitiesBelongToSameInstance(entityIdsNoFocusContainer)) { return AZ::Failure(AZStd::string("Cannot delete multiple entities belonging to different instances with one operation.")); } - AZ::EntityId firstEntityIdToDelete = entityIdsNoLevelInstance[0]; + AZ::EntityId firstEntityIdToDelete = entityIdsNoFocusContainer[0]; InstanceOptionalReference commonOwningInstance = GetOwnerInstanceByEntityId(firstEntityIdToDelete); // If the first entity id is a container entity id, then we need to mark its parent as the common owning instance because you @@ -1128,8 +1143,15 @@ namespace AzToolsFramework commonOwningInstance = commonOwningInstance->get().GetParentInstance(); } + // We only allow explicit deletions for entities inside the currently focused prefab. + AzFramework::EntityContextId editorEntityContextId = AzToolsFramework::GetEntityContextId(); + if (&m_prefabFocusInterface->GetFocusedPrefabInstance(editorEntityContextId)->get() != &commonOwningInstance->get()) + { + return AZ::Failure(AZStd::string("Cannot delete entities belonging to an instance that is not being edited.")); + } + // Retrieve entityList from entityIds - EntityList inputEntityList = EntityIdListToEntityList(entityIdsNoLevelInstance); + EntityList inputEntityList = EntityIdListToEntityList(entityIdsNoFocusContainer); AZ_PROFILE_FUNCTION(AzToolsFramework); @@ -1186,7 +1208,7 @@ namespace AzToolsFramework } else { - for (AZ::EntityId entityId : entityIdsNoLevelInstance) + for (AZ::EntityId entityId : entityIdsNoFocusContainer) { InstanceOptionalReference owningInstance = m_instanceEntityMapperInterface->FindOwningInstance(entityId); // If this is the container entity, it actually represents the instance so get its owner @@ -1227,9 +1249,12 @@ namespace AzToolsFramework return AZ::Failure(AZStd::string("Cannot detach Prefab Instance with invalid container entity.")); } - if (IsLevelInstanceContainerEntity(containerEntityId)) + auto editorEntityContextId = AzFramework::EntityContextId::CreateNull(); + EditorEntityContextRequestBus::BroadcastResult(editorEntityContextId, &EditorEntityContextRequests::GetEditorEntityContextId); + + if (containerEntityId == m_prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(editorEntityContextId)) { - return AZ::Failure(AZStd::string("Cannot detach level Prefab Instance.")); + return AZ::Failure(AZStd::string("Cannot detach focused Prefab Instance.")); } InstanceOptionalReference owningInstance = GetOwnerInstanceByEntityId(containerEntityId); @@ -1452,9 +1477,14 @@ namespace AzToolsFramework AZStd::queue entityQueue; + auto editorEntityContextId = AzFramework::EntityContextId::CreateNull(); + EditorEntityContextRequestBus::BroadcastResult(editorEntityContextId, &EditorEntityContextRequests::GetEditorEntityContextId); + + AZ::EntityId focusedPrefabContainerEntityId = + m_prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(editorEntityContextId); for (auto inputEntity : inputEntities) { - if (inputEntity && !IsLevelInstanceContainerEntity(inputEntity->GetId())) + if (inputEntity && inputEntity->GetId() != focusedPrefabContainerEntityId) { entityQueue.push(inputEntity); } @@ -1548,19 +1578,19 @@ namespace AzToolsFramework return AZ::Success(); } - EntityIdList PrefabPublicHandler::GenerateEntityIdListWithoutLevelInstance( + EntityIdList PrefabPublicHandler::GenerateEntityIdListWithoutFocusedInstanceContainer( const EntityIdList& entityIds) const { - EntityIdList outEntityIds; - outEntityIds.reserve(entityIds.size()); // Actual size could be smaller. + EntityIdList outEntityIds(entityIds); - for (const AZ::EntityId& entityId : entityIds) + AzFramework::EntityContextId editorEntityContextId = AzToolsFramework::GetEntityContextId(); + AZ::EntityId focusedInstanceContainerEntityId = m_prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(editorEntityContextId); + + if (auto iter = AZStd::find(outEntityIds.begin(), outEntityIds.end(), focusedInstanceContainerEntityId); iter != outEntityIds.end()) { - if (!IsLevelInstanceContainerEntity(entityId)) - { - outEntityIds.emplace_back(entityId); - } + outEntityIds.erase(iter); } + return outEntityIds; } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h index f3c0e67d46..a9dadc3336 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h @@ -74,7 +74,7 @@ namespace AzToolsFramework Instance& commonRootEntityOwningInstance, EntityList& outEntities, AZStd::vector& outInstances) const; - EntityIdList GenerateEntityIdListWithoutLevelInstance(const EntityIdList& entityIds) const; + EntityIdList GenerateEntityIdListWithoutFocusedInstanceContainer(const EntityIdList& entityIds) const; InstanceOptionalReference GetOwnerInstanceByEntityId(AZ::EntityId entityId) const; bool EntitiesBelongToSameInstance(const EntityIdList& entityIds) const; @@ -187,6 +187,8 @@ namespace AzToolsFramework InstanceEntityMapperInterface* m_instanceEntityMapperInterface = nullptr; InstanceToTemplateInterface* m_instanceToTemplateInterface = nullptr; + PrefabFocusInterface* m_prefabFocusInterface = nullptr; + PrefabFocusPublicInterface* m_prefabFocusPublicInterface = nullptr; PrefabLoaderInterface* m_prefabLoaderInterface = nullptr; PrefabSystemComponentInterface* m_prefabSystemComponentInterface = nullptr; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp index 6ffa3aab69..16e3c047b5 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -175,12 +176,16 @@ namespace AzToolsFramework AzFramework::ApplicationRequests::Bus::BroadcastResult( prefabWipFeaturesEnabled, &AzFramework::ApplicationRequests::ArePrefabWipFeaturesEnabled); + auto editorEntityContextId = AzFramework::EntityContextId::CreateNull(); + EditorEntityContextRequestBus::BroadcastResult(editorEntityContextId, &EditorEntityContextRequests::GetEditorEntityContextId); + // Create Prefab { if (!selectedEntities.empty()) { - // Hide if the only selected entity is the Level Container - if (selectedEntities.size() > 1 || !s_prefabPublicInterface->IsLevelInstanceContainerEntity(selectedEntities[0])) + // Hide if the only selected entity is the Focused Instance Container + if (selectedEntities.size() > 1 || + selectedEntities[0] != s_prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(editorEntityContextId)) { bool layerInSelection = false; @@ -247,14 +252,14 @@ namespace AzToolsFramework // Edit Prefab if (prefabWipFeaturesEnabled && !s_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(selectedEntity)) { - QAction* editAction = menu->addAction(QObject::tr("Edit Prefab")); - editAction->setToolTip(QObject::tr("Edit the prefab in focus mode.")); + QAction* editAction = menu->addAction(QObject::tr("Edit Prefab")); + editAction->setToolTip(QObject::tr("Edit the prefab in focus mode.")); - QObject::connect(editAction, &QAction::triggered, editAction, [selectedEntity] { - ContextMenu_EditPrefab(selectedEntity); - }); + QObject::connect(editAction, &QAction::triggered, editAction, [selectedEntity] { + ContextMenu_EditPrefab(selectedEntity); + }); - itemWasShown = true; + itemWasShown = true; } // Save Prefab @@ -283,8 +288,9 @@ namespace AzToolsFramework QAction* deleteAction = menu->addAction(QObject::tr("Delete")); QObject::connect(deleteAction, &QAction::triggered, deleteAction, [] { ContextMenu_DeleteSelected(); }); - if (selectedEntities.size() == 0 || - (selectedEntities.size() == 1 && s_prefabPublicInterface->IsLevelInstanceContainerEntity(selectedEntities[0]))) + + if (selectedEntities.empty() || + (selectedEntities.size() == 1 && selectedEntities[0] == s_prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(editorEntityContextId))) { deleteAction->setDisabled(true); } @@ -292,17 +298,17 @@ namespace AzToolsFramework // Detach Prefab if (selectedEntities.size() == 1) { - AZ::EntityId selectedEntity = selectedEntities[0]; + AZ::EntityId selectedEntityId = selectedEntities[0]; - if (s_prefabPublicInterface->IsInstanceContainerEntity(selectedEntity) && - !s_prefabPublicInterface->IsLevelInstanceContainerEntity(selectedEntity)) + if (s_prefabPublicInterface->IsInstanceContainerEntity(selectedEntityId) && + selectedEntityId != s_prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(editorEntityContextId)) { QAction* detachPrefabAction = menu->addAction(QObject::tr("Detach Prefab...")); QObject::connect( detachPrefabAction, &QAction::triggered, detachPrefabAction, - [selectedEntity] + [selectedEntityId] { - ContextMenu_DetachPrefab(selectedEntity); + ContextMenu_DetachPrefab(selectedEntityId); }); } } @@ -331,13 +337,21 @@ namespace AzToolsFramework QWidget* activeWindow = QApplication::activeWindow(); const AZStd::string prefabFilesPath = "@projectroot@/Prefabs"; - // Remove Level entity if it's part of the list - - auto levelContainerIter = - AZStd::find(selectedEntities.begin(), selectedEntities.end(), s_prefabPublicInterface->GetLevelInstanceContainerEntityId()); - if (levelContainerIter != selectedEntities.end()) + // Remove focused instance container entity if it's part of the list + auto editorEntityContextId = AzFramework::EntityContextId::CreateNull(); + EditorEntityContextRequestBus::BroadcastResult(editorEntityContextId, &EditorEntityContextRequests::GetEditorEntityContextId); + + auto focusedContainerIter = AZStd::find( + selectedEntities.begin(), selectedEntities.end(), + s_prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(editorEntityContextId)); + if (focusedContainerIter != selectedEntities.end()) { - selectedEntities.erase(levelContainerIter); + selectedEntities.erase(focusedContainerIter); + } + + if (selectedEntities.empty()) + { + return; } // Set default folder for prefabs diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.cpp index 00522b29dc..ccad85e32b 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.cpp @@ -178,12 +178,6 @@ namespace AzToolsFramework AZ::EntityId entityId(index.data(EntityOutlinerListModel::EntityIdRole).value()); - // We hide the root instance container entity from the Outliner, so avoid drawing its full container on children - if (m_prefabPublicInterface->IsLevelInstanceContainerEntity(entityId)) - { - return; - } - const QTreeView* outlinerTreeView(qobject_cast(option.widget)); const int ancestorLeft = outlinerTreeView->visualRect(index).left() + (m_prefabBorderThickness / 2) - 1; const int curveRectSize = m_prefabCapsuleRadius * 2;