From b8bf5486a46b8d79797eb840b6c9c4d33f41d444 Mon Sep 17 00:00:00 2001 From: dmcdiar Date: Tue, 5 Oct 2021 19:50:54 -0700 Subject: [PATCH 1/6] Removed the ScatterDistanceOutput texture from the EnvironmentCubeMapForwardMSAAPass. Removed unused Srgs from the ReflectionCompositePass. Signed-off-by: dmcdiar --- .../Passes/EnvironmentCubeMapForwardMSAA.pass | 40 ------------------- .../Reflections/ReflectionComposite.azsl | 3 -- 2 files changed, 43 deletions(-) diff --git a/Gems/Atom/Feature/Common/Assets/Passes/EnvironmentCubeMapForwardMSAA.pass b/Gems/Atom/Feature/Common/Assets/Passes/EnvironmentCubeMapForwardMSAA.pass index cd52ce946b..877ae489c0 100644 --- a/Gems/Atom/Feature/Common/Assets/Passes/EnvironmentCubeMapForwardMSAA.pass +++ b/Gems/Atom/Feature/Common/Assets/Passes/EnvironmentCubeMapForwardMSAA.pass @@ -147,22 +147,6 @@ }, "LoadAction": "Clear" } - }, - { - "Name": "ScatterDistanceOutput", - "SlotType": "Output", - "ScopeAttachmentUsage": "RenderTarget", - "LoadStoreAction": { - "ClearValue": { - "Value": [ - 0.0, - 0.0, - 0.0, - 0.0 - ] - }, - "LoadAction": "Clear" - } } ], "ImageAttachments": [ @@ -257,23 +241,6 @@ "AssetRef": { "FilePath": "Textures/BRDFTexture.attimage" } - }, - { - "Name": "ScatterDistanceImage", - "SizeSource": { - "Source": { - "Pass": "Parent", - "Attachment": "Output" - } - }, - "MultisampleSource": { - "Pass": "This", - "Attachment": "DepthStencilInputOutput" - }, - "ImageDescriptor": { - "Format": "R11G11B10_FLOAT", - "SharedQueueMask": "Graphics" - } } ], "Connections": [ @@ -318,13 +285,6 @@ "Pass": "This", "Attachment": "BRDFTexture" } - }, - { - "LocalSlot": "ScatterDistanceOutput", - "AttachmentRef": { - "Pass": "This", - "Attachment": "ScatterDistanceImage" - } } ] } diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionComposite.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionComposite.azsl index 806337499c..207da9e857 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionComposite.azsl +++ b/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionComposite.azsl @@ -15,7 +15,6 @@ // box-filtered from the MSAA sub-pixels of the reflection texture. #include -#include #include #include @@ -26,8 +25,6 @@ ShaderResourceGroup PassSrg : SRG_PerPass Texture2DMS m_reflection; } -#include - // Vertex Shader VSOutput MainVS(VSInput input) { From 02b6b1dbf4d9889b55d4c11e049aa5b1804c9897 Mon Sep 17 00:00:00 2001 From: jromnoa <80134229+jromnoa@users.noreply.github.com> Date: Tue, 5 Oct 2021 20:05:06 -0700 Subject: [PATCH 2/6] First in set of PRs for adding parallel GPU test runs to Atom tests. (#4276) * add remaining hydra code for test to run using new parallel test classes and interfaces * fix return value of Secondary Grid Spacing property test for Grid Entity * Add `or error_tracer.has_asserts` call for better error catching * make `Tests.viewport_set` test check a `Report.critical_result()` check instead of `Report.result()` check * add updates from PR feedback: makes constants for re-used component name strings, move test verifications into the Report() call instead of setting first then calling Report(), use builtin math.isclose function over custom function * remove after_level_load() and split it into several function calls, add screenshot check, cleanup the GPU script and fix the rendering not displaying by setting global_extra_cmdline_args = [] * fixes the viewport Test check and splits up the helper functions into multiple functions * add comment for global_extra_cmdline_args update in test and rename the zip file to f'screenshots_{formatted_timestamp}.zip' Signed-off-by: jromnoa Co-authored-by: smurly --- .../Gem/PythonTests/Atom/CMakeLists.txt | 14 + .../Atom/TestSuite_Main_GPU_Optimized.py | 43 +++ .../Atom/atom_utils/atom_component_helper.py | 50 ++- .../tests/hydra_AtomGPU_BasicLevelSetup.py | 292 ++++++++++++++++++ .../editor_entity_utils.py | 16 + .../editor_python_test_tools/utils.py | 55 +++- 6 files changed, 453 insertions(+), 17 deletions(-) create mode 100644 AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_GPU_Optimized.py create mode 100644 AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomGPU_BasicLevelSetup.py diff --git a/AutomatedTesting/Gem/PythonTests/Atom/CMakeLists.txt b/AutomatedTesting/Gem/PythonTests/Atom/CMakeLists.txt index 02aaa42597..0d6fe4c8fa 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/CMakeLists.txt +++ b/AutomatedTesting/Gem/PythonTests/Atom/CMakeLists.txt @@ -65,4 +65,18 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_BUILD_TESTS_SUPPORTED AND AutomatedT COMPONENT Atom ) + ly_add_pytest( + NAME AutomatedTesting::Atom_TestSuite_Main_GPU_Optimized + TEST_SUITE main + TEST_REQUIRES gpu + TEST_SERIAL + TIMEOUT 1200 + PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuite_Main_GPU_Optimized.py + RUNTIME_DEPENDENCIES + AssetProcessor + AutomatedTesting.Assets + Editor + COMPONENT + Atom + ) endif() diff --git a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_GPU_Optimized.py b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_GPU_Optimized.py new file mode 100644 index 0000000000..ef572d6e5c --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_GPU_Optimized.py @@ -0,0 +1,43 @@ +""" +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 +""" +import os + +import pytest + +import ly_test_tools.environment.file_system as file_system +from ly_test_tools.o3de.editor_test import EditorSharedTest, EditorTestSuite +from ly_test_tools.image.screenshot_compare_qssim import qssim as compare_screenshots +from .atom_utils.atom_component_helper import create_screenshots_archive, golden_images_directory + +DEFAULT_SUBFOLDER_PATH = 'user/PythonTests/Automated/Screenshots' + + +@pytest.mark.xfail(reason="Optimized tests are experimental, we will enable xfail and monitor them temporarily.") +@pytest.mark.parametrize("project", ["AutomatedTesting"]) +@pytest.mark.parametrize("launcher_platform", ['windows_editor']) +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"] + + class AtomGPU_BasicLevelSetup_SetsUpLevel(EditorSharedTest): + use_null_renderer = False # Default is True + screenshot_name = "AtomBasicLevelSetup.ppm" + test_screenshots = [] # Gets set by setup() + screenshot_directory = "" # Gets set by setup() + + # Clear existing test screenshots before starting test. + def setup(self, workspace): + screenshot_directory = os.path.join(workspace.paths.project(), DEFAULT_SUBFOLDER_PATH) + test_screenshots = [os.path.join(screenshot_directory, self.screenshot_name)] + file_system.delete(test_screenshots, True, True) + + from Atom.tests import hydra_AtomGPU_BasicLevelSetup as test_module + + golden_images = [os.path.join(golden_images_directory(), screenshot_name)] + for test_screenshot, golden_screenshot in zip(test_screenshots, golden_images): + compare_screenshots(test_screenshot, golden_screenshot) + create_screenshots_archive(screenshot_directory) 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 58b72ef01a..821e0acfdb 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_component_helper.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_component_helper.py @@ -3,13 +3,54 @@ Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright SPDX-License-Identifier: Apache-2.0 OR MIT -File to assist with common hydra component functions used across various Atom tests. """ +import datetime import os +import zipfile -from editor_python_test_tools.editor_test_helper import EditorTestHelper -helper = EditorTestHelper(log_prefix="Atom_EditorTestHelper") +def create_screenshots_archive(screenshot_path): + """ + Creates a new zip file archive at archive_path containing all files listed within archive_path. + :param screenshot_path: location containing the files to archive, the zip archive file will also be saved here. + :return: None, but creates a new zip file archive inside path containing all of the files inside archive_path. + """ + files_to_archive = [] + + # Search for .png and .ppm files to add to the zip archive file. + for (folder_name, sub_folders, file_names) in os.walk(screenshot_path): + for file_name in file_names: + if file_name.endswith(".png") or file_name.endswith(".ppm"): + file_path = os.path.join(folder_name, file_name) + files_to_archive.append(file_path) + + # Setup variables for naming the zip archive file. + timestamp = datetime.datetime.now().timestamp() + formatted_timestamp = datetime.datetime.utcfromtimestamp(timestamp).strftime("%Y-%m-%d_%H-%M-%S") + screenshots_file = os.path.join(screenshot_path, f'screenshots_{formatted_timestamp}.zip') + + # Write all of the valid .png and .ppm files to the archive file. + with zipfile.ZipFile(screenshots_file, 'w', compression=zipfile.ZIP_DEFLATED, allowZip64=True) as zip_archive: + for file_path in files_to_archive: + file_name = os.path.basename(file_path) + zip_archive.write(file_path, file_name) + + +def golden_images_directory(): + """ + Uses this file location to return the valid location for golden image files. + :return: The path to the golden_images directory, but raises an IOError if the golden_images directory is missing. + """ + current_file_directory = os.path.join(os.path.dirname(__file__)) + golden_images_dir = os.path.join(current_file_directory, '..', 'golden_images') + + if not os.path.exists(golden_images_dir): + raise IOError( + f'golden_images" directory was not found at path "{golden_images_dir}"' + f'Please add a "golden_images" directory inside: "{current_file_directory}"' + ) + + return golden_images_dir def create_basic_atom_level(level_name): @@ -31,6 +72,9 @@ def create_basic_atom_level(level_name): import azlmbr.object import editor_python_test_tools.hydra_editor_utils as hydra + from editor_python_test_tools.editor_test_helper import EditorTestHelper + + helper = EditorTestHelper(log_prefix="Atom_EditorTestHelper") # Create a new level. new_level_name = level_name diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomGPU_BasicLevelSetup.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomGPU_BasicLevelSetup.py new file mode 100644 index 0000000000..92c555127a --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomGPU_BasicLevelSetup.py @@ -0,0 +1,292 @@ +""" +Copyright (c) Contributors to the Open 3D Engine Project. +For complete copyright and license terms please see the LICENSE at the root of this distribution. + +SPDX-License-Identifier: Apache-2.0 OR MIT +""" + + +# fmt: off +class Tests : + camera_component_added = ("Camera component was added", "Camera component wasn't added") + camera_fov_set = ("Camera component FOV property set", "Camera component FOV property wasn't set") + directional_light_component_added = ("Directional Light component added", "Directional Light component wasn't added") + enter_game_mode = ("Entered game mode", "Failed to enter game mode") + exit_game_mode = ("Exited game mode", "Couldn't exit game mode") + global_skylight_component_added = ("Global Skylight (IBL) component added", "Global Skylight (IBL) component wasn't added") + global_skylight_diffuse_image_set = ("Global Skylight Diffuse Image property set", "Global Skylight Diffuse Image property wasn't set") + global_skylight_specular_image_set = ("Global Skylight Specular Image property set", "Global Skylight Specular Image property wasn't set") + ground_plane_material_asset_set = ("Ground Plane Material Asset was set", "Ground Plane Material Asset wasn't set") + ground_plane_material_component_added = ("Ground Plane Material component added", "Ground Plane Material component wasn't added") + ground_plane_mesh_asset_set = ("Ground Plane Mesh Asset property was set", "Ground Plane Mesh Asset property wasn't set") + hdri_skybox_component_added = ("HDRi Skybox component added", "HDRi Skybox component wasn't added") + hdri_skybox_cubemap_texture_set = ("HDRi Skybox Cubemap Texture property set", "HDRi Skybox Cubemap Texture property wasn't set") + mesh_component_added = ("Mesh component added", "Mesh component wasn't added") + no_assert_occurred = ("No asserts detected", "Asserts were detected") + no_error_occurred = ("No errors detected", "Errors were detected") + secondary_grid_spacing = ("Secondary Grid Spacing set", "Secondary Grid Spacing not set") + sphere_material_component_added = ("Sphere Material component added", "Sphere Material component wasn't added") + sphere_material_set = ("Sphere Material Asset was set", "Sphere Material Asset wasn't set") + sphere_mesh_asset_set = ("Sphere Mesh Asset was set", "Sphere Mesh Asset wasn't set") + viewport_set = ("Viewport set to correct size", "Viewport not set to correct size") +# fmt: on + + +def AtomGPU_BasicLevelSetup_SetsUpLevel(): + """ + Summary: + Sets up a level to match the AtomBasicLevelSetup.ppm golden image then takes a screenshot to verify the setup. + + Test setup: + - Wait for Editor idle loop. + - Open the "Base" level. + + Expected Behavior: + The scene can be setup for a basic level. + The test screenshot matches the appearance of the AtomBasicLevelSetup.ppm golden image. + + Test Steps: + 1. Close error windows and display helpers then update the viewport size. + 2. Create Default Level Entity. + 3. Create Grid Entity as a child entity of the Default Level Entity. + 4. Add Grid component to Grid Entity and set Secondary Grid Spacing. + 5. Create Global Skylight (IBL) Entity as a child entity of the Default Level Entity. + 6. Add HDRi Skybox component to the Global Skylight (IBL) Entity. + 7. Add Global Skylight (IBL) component to the Global Skylight (IBL) Entity. + 8. Set the Cubemap Texture property of the HDRi Skybox component. + 9. Set the Diffuse Image property of the Global Skylight (IBL) component. + 10. Set the Specular Image property of the Global Skylight (IBL) component. + 11. Create a Ground Plane Entity with a Material component that is a child entity of the Default Level Entity. + 12. Set the Material Asset property of the Material component for the Ground Plane Entity. + 13. Add the Mesh component to the Ground Plane Entity and set the Mesh component Mesh Asset property. + 14. Create a Directional Light Entity as a child entity of the Default Level Entity. + 15. Add Directional Light component to Directional Light Entity and set entity rotation. + 16. Create a Sphere Entity as a child entity of the Default Level Entity then add a Material component. + 17. Set the Material Asset property of the Material component for the Sphere Entity. + 18. Add Mesh component to Sphere Entity and set the Mesh Asset property for the Mesh component. + 19. Create a Camera Entity as a child entity of the Default Level Entity then add a Camera component. + 20. Set the Camera Entity rotation value and set the Camera component Field of View value. + 21. Enter game mode. + 22. Take screenshot. + 23. Exit game mode. + 24. Look for errors. + + :return: None + """ + + import os + from math import isclose + + import azlmbr.asset as asset + import azlmbr.bus as bus + import azlmbr.legacy.general as general + import azlmbr.math as math + import azlmbr.paths + + from editor_python_test_tools.editor_entity_utils import EditorEntity + from editor_python_test_tools.utils import Report, Tracer, TestHelper as helper + + from Atom.atom_utils.screenshot_utils import ScreenshotHelper + + MATERIAL_COMPONENT_NAME = "Material" + MESH_COMPONENT_NAME = "Mesh" + SCREENSHOT_NAME = "AtomBasicLevelSetup" + SCREEN_WIDTH = 1280 + SCREEN_HEIGHT = 720 + DEGREE_RADIAN_FACTOR = 0.0174533 + + def initial_viewport_setup(screen_width, screen_height): + general.set_viewport_size(screen_width, screen_height) + general.update_viewport() + result = isclose( + a=general.get_viewport_size().x, b=SCREEN_WIDTH, rel_tol=0.1) and isclose( + a=general.get_viewport_size().y, b=SCREEN_HEIGHT, rel_tol=0.1) + + return result + + with Tracer() as error_tracer: + # Test setup begins. + # Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level. + helper.init_idle() + helper.open_level("", "Base") + + # Test steps begin. + # 1. Close error windows and display helpers then update the viewport size. + helper.close_error_windows() + helper.close_display_helpers() + general.update_viewport() + Report.critical_result(Tests.viewport_set, initial_viewport_setup(SCREEN_WIDTH, SCREEN_HEIGHT)) + + # 2. Create Default Level Entity. + default_level_entity_name = "Default Level" + default_level_entity = EditorEntity.create_editor_entity_at( + math.Vector3(0.0, 0.0, 0.0), default_level_entity_name) + + # 3. Create Grid Entity as a child entity of the Default Level Entity. + grid_name = "Grid" + grid_entity = EditorEntity.create_editor_entity(grid_name, default_level_entity.id) + + # 4. Add Grid component to Grid Entity and set Secondary Grid Spacing. + grid_component = grid_entity.add_component(grid_name) + secondary_grid_spacing_property = "Controller|Configuration|Secondary Grid Spacing" + secondary_grid_spacing_value = 1.0 + grid_component.set_component_property_value(secondary_grid_spacing_property, secondary_grid_spacing_value) + secondary_grid_spacing_set = grid_component.get_component_property_value( + secondary_grid_spacing_property) == secondary_grid_spacing_value + Report.result(Tests.secondary_grid_spacing, secondary_grid_spacing_set) + + # 5. Create Global Skylight (IBL) Entity as a child entity of the Default Level Entity. + global_skylight_name = "Global Skylight (IBL)" + global_skylight_entity = EditorEntity.create_editor_entity(global_skylight_name, default_level_entity.id) + + # 6. Add HDRi Skybox component to the Global Skylight (IBL) Entity. + hdri_skybox_name = "HDRi Skybox" + hdri_skybox_component = global_skylight_entity.add_component(hdri_skybox_name) + Report.result(Tests.hdri_skybox_component_added, global_skylight_entity.has_component(hdri_skybox_name)) + + # 7. Add Global Skylight (IBL) component to the Global Skylight (IBL) Entity. + global_skylight_component = global_skylight_entity.add_component(global_skylight_name) + Report.result(Tests.global_skylight_component_added, global_skylight_entity.has_component(global_skylight_name)) + + # 8. Set the Cubemap Texture property of the HDRi Skybox component. + global_skylight_image_asset_path = os.path.join( + "LightingPresets", "greenwich_park_02_4k_iblskyboxcm_iblspecular.exr.streamingimage") + global_skylight_image_asset = asset.AssetCatalogRequestBus( + bus.Broadcast, "GetAssetIdByPath", global_skylight_image_asset_path, math.Uuid(), False) + hdri_skybox_cubemap_texture_property = "Controller|Configuration|Cubemap Texture" + hdri_skybox_component.set_component_property_value( + hdri_skybox_cubemap_texture_property, global_skylight_image_asset) + Report.result( + Tests.hdri_skybox_cubemap_texture_set, + hdri_skybox_component.get_component_property_value( + hdri_skybox_cubemap_texture_property) == global_skylight_image_asset) + + # 9. Set the Diffuse Image property of the Global Skylight (IBL) component. + # Re-use the same image that was used in the previous test step. + global_skylight_diffuse_image_property = "Controller|Configuration|Diffuse Image" + global_skylight_component.set_component_property_value( + global_skylight_diffuse_image_property, global_skylight_image_asset) + Report.result( + Tests.global_skylight_diffuse_image_set, + global_skylight_component.get_component_property_value( + global_skylight_diffuse_image_property) == global_skylight_image_asset) + + # 10. Set the Specular Image property of the Global Skylight (IBL) component. + # Re-use the same image that was used in the previous test step. + global_skylight_specular_image_property = "Controller|Configuration|Specular Image" + global_skylight_component.set_component_property_value( + global_skylight_specular_image_property, global_skylight_image_asset) + global_skylight_specular_image_set = global_skylight_component.get_component_property_value( + global_skylight_specular_image_property) + Report.result( + Tests.global_skylight_specular_image_set, global_skylight_specular_image_set == global_skylight_image_asset) + + # 11. Create a Ground Plane Entity with a Material component that is a child entity of the Default Level Entity. + ground_plane_name = "Ground Plane" + ground_plane_entity = EditorEntity.create_editor_entity(ground_plane_name, default_level_entity.id) + ground_plane_material_component = ground_plane_entity.add_component(MATERIAL_COMPONENT_NAME) + Report.result( + Tests.ground_plane_material_component_added, ground_plane_entity.has_component(MATERIAL_COMPONENT_NAME)) + + # 12. Set the Material Asset property of the Material component for the Ground Plane Entity. + ground_plane_entity.set_local_uniform_scale(32.0) + ground_plane_material_asset_path = os.path.join("Materials", "Presets", "PBR", "metal_chrome.azmaterial") + ground_plane_material_asset = asset.AssetCatalogRequestBus( + bus.Broadcast, "GetAssetIdByPath", ground_plane_material_asset_path, math.Uuid(), False) + ground_plane_material_asset_property = "Default Material|Material Asset" + ground_plane_material_component.set_component_property_value( + ground_plane_material_asset_property, ground_plane_material_asset) + Report.result( + Tests.ground_plane_material_asset_set, + ground_plane_material_component.get_component_property_value( + ground_plane_material_asset_property) == ground_plane_material_asset) + + # 13. Add the Mesh component to the Ground Plane Entity and set the Mesh component Mesh Asset property. + ground_plane_mesh_component = ground_plane_entity.add_component(MESH_COMPONENT_NAME) + Report.result(Tests.mesh_component_added, ground_plane_entity.has_component(MESH_COMPONENT_NAME)) + ground_plane_mesh_asset_path = os.path.join("Objects", "plane.azmodel") + ground_plane_mesh_asset = asset.AssetCatalogRequestBus( + bus.Broadcast, "GetAssetIdByPath", ground_plane_mesh_asset_path, math.Uuid(), False) + ground_plane_mesh_asset_property = "Controller|Configuration|Mesh Asset" + ground_plane_mesh_component.set_component_property_value( + ground_plane_mesh_asset_property, ground_plane_mesh_asset) + Report.result( + Tests.ground_plane_mesh_asset_set, + ground_plane_mesh_component.get_component_property_value( + ground_plane_mesh_asset_property) == ground_plane_mesh_asset) + + # 14. Create a Directional Light Entity as a child entity of the Default Level Entity. + directional_light_name = "Directional Light" + directional_light_entity = EditorEntity.create_editor_entity_at( + math.Vector3(0.0, 0.0, 10.0), directional_light_name, default_level_entity.id) + + # 15. Add Directional Light component to Directional Light Entity and set entity rotation. + directional_light_entity.add_component(directional_light_name) + directional_light_entity_rotation = math.Vector3(DEGREE_RADIAN_FACTOR * -90.0, 0.0, 0.0) + directional_light_entity.set_local_rotation(directional_light_entity_rotation) + Report.result( + Tests.directional_light_component_added, directional_light_entity.has_component(directional_light_name)) + + # 16. Create a Sphere Entity as a child entity of the Default Level Entity then add a Material component. + sphere_entity = EditorEntity.create_editor_entity_at( + math.Vector3(0.0, 0.0, 1.0), "Sphere", default_level_entity.id) + sphere_material_component = sphere_entity.add_component(MATERIAL_COMPONENT_NAME) + Report.result(Tests.sphere_material_component_added, sphere_entity.has_component(MATERIAL_COMPONENT_NAME)) + + # 17. Set the Material Asset property of the Material component for the Sphere Entity. + sphere_material_asset_path = os.path.join("Materials", "Presets", "PBR", "metal_brass_polished.azmaterial") + sphere_material_asset = asset.AssetCatalogRequestBus( + bus.Broadcast, "GetAssetIdByPath", sphere_material_asset_path, math.Uuid(), False) + sphere_material_asset_property = "Default Material|Material Asset" + sphere_material_component.set_component_property_value(sphere_material_asset_property, sphere_material_asset) + Report.result(Tests.sphere_material_set, sphere_material_component.get_component_property_value( + sphere_material_asset_property) == sphere_material_asset) + + # 18. Add Mesh component to Sphere Entity and set the Mesh Asset property for the Mesh component. + sphere_mesh_component = sphere_entity.add_component(MESH_COMPONENT_NAME) + sphere_mesh_asset_path = os.path.join("Models", "sphere.azmodel") + sphere_mesh_asset = asset.AssetCatalogRequestBus( + bus.Broadcast, "GetAssetIdByPath", sphere_mesh_asset_path, math.Uuid(), False) + sphere_mesh_asset_property = "Controller|Configuration|Mesh Asset" + sphere_mesh_component.set_component_property_value(sphere_mesh_asset_property, sphere_mesh_asset) + Report.result(Tests.sphere_mesh_asset_set, sphere_mesh_component.get_component_property_value( + sphere_mesh_asset_property) == sphere_mesh_asset) + + # 19. Create a Camera Entity as a child entity of the Default Level Entity then add a Camera component. + camera_name = "Camera" + camera_entity = EditorEntity.create_editor_entity_at( + math.Vector3(5.5, -12.0, 9.0), camera_name, default_level_entity.id) + camera_component = camera_entity.add_component(camera_name) + Report.result(Tests.camera_component_added, camera_entity.has_component(camera_name)) + + # 20. Set the Camera Entity rotation value and set the Camera component Field of View value. + camera_entity_rotation = math.Vector3( + DEGREE_RADIAN_FACTOR * -27.0, DEGREE_RADIAN_FACTOR * -12.0, DEGREE_RADIAN_FACTOR * 25.0) + camera_entity.set_local_rotation(camera_entity_rotation) + camera_fov_property = "Controller|Configuration|Field of view" + camera_fov_value = 60.0 + camera_component.set_component_property_value(camera_fov_property, camera_fov_value) + azlmbr.camera.EditorCameraViewRequestBus(azlmbr.bus.Event, "ToggleCameraAsActiveView", camera_entity.id) + Report.result(Tests.camera_fov_set, camera_component.get_component_property_value( + camera_fov_property) == camera_fov_value) + + # 21. Enter game mode. + helper.enter_game_mode(Tests.enter_game_mode) + helper.wait_for_condition(function=lambda: general.is_in_game_mode(), timeout_in_seconds=4.0) + + # 22. Take screenshot. + ScreenshotHelper(general.idle_wait_frames).capture_screenshot_blocking(f"{SCREENSHOT_NAME}.ppm") + + # 23. Exit game mode. + helper.exit_game_mode(Tests.exit_game_mode) + helper.wait_for_condition(function=lambda: not general.is_in_game_mode(), timeout_in_seconds=4.0) + + # 24. Look for errors. + helper.wait_for_condition(lambda: error_tracer.has_errors or error_tracer.has_asserts, 1.0) + Report.result(Tests.no_assert_occurred, not error_tracer.has_asserts) + Report.result(Tests.no_error_occurred, not error_tracer.has_errors) + + +if __name__ == "__main__": + from editor_python_test_tools.utils import Report + Report.start_test(AtomGPU_BasicLevelSetup_SetsUpLevel) diff --git a/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/editor_entity_utils.py b/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/editor_entity_utils.py index 154b5730d7..783f71e06c 100644 --- a/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/editor_entity_utils.py +++ b/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/editor_entity_utils.py @@ -361,3 +361,19 @@ class EditorEntity: :return: True if "isVisible" is enabled, False otherwise. """ return editor.EditorEntityInfoRequestBus(bus.Event, "IsVisible", self.id) + + def set_local_uniform_scale(self, scale_float) -> None: + """ + Sets the "SetLocalUniformScale" value on the entity. + :param scale_float: value for "SetLocalUniformScale" to set to. + :return: None + """ + azlmbr.components.TransformBus(azlmbr.bus.Event, "SetLocalUniformScale", self.id, scale_float) + + def set_local_rotation(self, vector3_rotation) -> None: + """ + Sets the "SetLocalRotation" value on the entity. + :param vector3_rotation: The math.Vector3 value to use for rotation on the entity (uses radians). + :return: None + """ + azlmbr.components.TransformBus(azlmbr.bus.Event, "SetLocalRotation", self.id, vector3_rotation) 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 cc217f81da..ef3048d8d4 100644 --- a/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/utils.py +++ b/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/utils.py @@ -4,18 +4,18 @@ For complete copyright and license terms please see the LICENSE at the root of t SPDX-License-Identifier: Apache-2.0 OR MIT """ + +import json +import math import os import time -import math +import traceback +from typing import Callable, Tuple import azlmbr import azlmbr.legacy.general as general import azlmbr.debug -import json -import traceback - -from typing import Callable, Tuple class FailFast(Exception): """ @@ -127,6 +127,31 @@ class TestHelper: if ret: return True + @staticmethod + def close_error_windows(): + """ + Closes Error Report and Error Log windows that block focus if they are visible. + :return: None + """ + if general.is_pane_visible("Error Report"): + general.close_pane("Error Report") + if general.is_pane_visible("Error Log"): + general.close_pane("Error Log") + + @staticmethod + def close_display_helpers(): + """ + Closes helper gizmos, anti-aliasing, and FPS meters. + :return: None + """ + if general.is_helpers_shown(): + general.toggle_helpers() + 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) + class Timeout: # type: (float) -> None @@ -149,6 +174,7 @@ class Timeout: def timed_out(self): return time.time() > self.die_after + class Report: _results = [] _exception = None @@ -290,8 +316,8 @@ class Report: Report.info(" x: {:.2f}, y: {:.2f}, z: {:.2f}".format(vector3.x, vector3.y, vector3.z)) if magnitude is not None: Report.info(" magnitude: {:.2f}".format(magnitude)) - - + + ''' Utility for scope tracing errors and warnings. Usage: @@ -303,7 +329,7 @@ Usage: Report.result(Tests.warnings_not_found_in_section, not section_tracer.has_warnings) -''' +''' class Tracer: def __init__(self): self.warnings = [] @@ -349,10 +375,10 @@ class Tracer: self.line = args[1] self.function = args[2] self.message = args[3] - + def __str__(self): return f"Assert: [{self.filename}:{self.function}:{self.line}]: {self.message}" - + def __repr__(self): return f"[Assert: {self.message}]" @@ -360,21 +386,21 @@ class Tracer: def __init__(self, args): self.window = args[0] self.message = args[1] - + def _on_warning(self, args): warningInfo = Tracer.WarningInfo(args) self.warnings.append(warningInfo) Report.info("Tracer caught Warning: %s" % warningInfo.message) self.has_warnings = True return False - + def _on_error(self, args): errorInfo = Tracer.ErrorInfo(args) self.errors.append(errorInfo) Report.info("Tracer caught Error: %s" % errorInfo.message) self.has_errors = True return False - + def _on_assert(self, args): assertInfo = Tracer.AssertInfo(args) self.asserts.append(assertInfo) @@ -436,6 +462,7 @@ class AngleHelper: def vector3_str(vector3): return "(x: {:.2f}, y: {:.2f}, z: {:.2f})".format(vector3.x, vector3.y, vector3.z) - + + def aabb_str(aabb): return "[Min: %s, Max: %s]" % (vector3_str(aabb.min), vector3_str(aabb.max)) From 0cd1f20d39493ab6bf37285f1a37a5de95b8835a Mon Sep 17 00:00:00 2001 From: Benjamin Jillich <43751992+amzn-jillich@users.noreply.github.com> Date: Wed, 6 Oct 2021 09:35:55 +0200 Subject: [PATCH 3/6] EMotion FX: Simple LOD Component to use Atom LOD override (#4464) The LOD level for skinned meshes is determined in the render phase, which is after the animation update and is also specific to the camera/target Atom renders to, which means that different render targets could end up using different LOD levels depending on how much screen space the character's bounding box takes. In order to make sure that all transformations are calculated by EMotion FX that the skinned mesh Atom wants to render are available, the Simple LOD Component takes ownership of the LOD level and uses the Atom LOD override. That way Atom adapts to the LOD level determined by the Simple LOD component and makes sure the skeletal matches with the geometry LOD levels. Signed-off-by: Benjamin Jillich --- Gems/EMotionFX/Code/CMakeLists.txt | 1 + .../Components/SimpleLODComponent.cpp | 42 +++++++++++++++++-- .../Components/SimpleLODComponent.h | 5 ++- 3 files changed, 43 insertions(+), 5 deletions(-) diff --git a/Gems/EMotionFX/Code/CMakeLists.txt b/Gems/EMotionFX/Code/CMakeLists.txt index ed5447ba25..dace02b2ae 100644 --- a/Gems/EMotionFX/Code/CMakeLists.txt +++ b/Gems/EMotionFX/Code/CMakeLists.txt @@ -36,6 +36,7 @@ ly_add_target( PUBLIC AZ::AtomCore Gem::Atom_RPI.Public + Gem::AtomLyIntegration_CommonFeatures.Static Gem::LmbrCentral COMPILE_DEFINITIONS PUBLIC diff --git a/Gems/EMotionFX/Code/Source/Integration/Components/SimpleLODComponent.cpp b/Gems/EMotionFX/Code/Source/Integration/Components/SimpleLODComponent.cpp index 5db0fae842..aa17058adf 100644 --- a/Gems/EMotionFX/Code/Source/Integration/Components/SimpleLODComponent.cpp +++ b/Gems/EMotionFX/Code/Source/Integration/Components/SimpleLODComponent.cpp @@ -142,12 +142,31 @@ namespace EMotionFX { ActorComponentNotificationBus::Handler::BusConnect(GetEntityId()); AZ::TickBus::Handler::BusConnect(); + + // Remember the lod type and level so that we can set it back to the previous one on deactivation of the component. + AZ::Render::MeshComponentRequestBus::EventResult(m_previousLodType, + GetEntityId(), + &AZ::Render::MeshComponentRequestBus::Events::GetLodType); + + if (m_actorInstance) + { + m_previousLodLevel = m_actorInstance->GetLODLevel(); + } } void SimpleLODComponent::Deactivate() { AZ::TickBus::Handler::BusDisconnect(); ActorComponentNotificationBus::Handler::BusDisconnect(); + + AZ::Render::MeshComponentRequestBus::Event(GetEntityId(), + &AZ::Render::MeshComponentRequestBus::Events::SetLodType, + m_previousLodType); + + if (m_actorInstance) + { + m_actorInstance->SetLODLevel(m_previousLodLevel); + } } void SimpleLODComponent::OnActorInstanceCreated(EMotionFX::ActorInstance* actorInstance) @@ -183,7 +202,7 @@ namespace EMotionFX return max - 1; } - void SimpleLODComponent::UpdateLodLevelByDistance(EMotionFX::ActorInstance * actorInstance, const Configuration& configuration, AZ::EntityId entityId) + void SimpleLODComponent::UpdateLodLevelByDistance(EMotionFX::ActorInstance* actorInstance, const Configuration& configuration, AZ::EntityId entityId) { if (actorInstance) { @@ -201,15 +220,30 @@ namespace EMotionFX AZ::RPI::ViewportContextPtr defaultViewportContext = viewportContextManager->GetViewportContextByName(viewportContextManager->GetDefaultViewportContextName()); const float distance = worldPos.GetDistance(defaultViewportContext->GetCameraTransform().GetTranslation()); - const size_t lodByDistance = GetLodByDistance(configuration.m_lodDistances, distance); - actorInstance->SetLODLevel(lodByDistance); + const size_t requestedLod = GetLodByDistance(configuration.m_lodDistances, distance); + actorInstance->SetLODLevel(requestedLod); if (configuration.m_enableLodSampling) { - const float animGraphSampleRate = configuration.m_lodSampleRates[lodByDistance]; + const float animGraphSampleRate = configuration.m_lodSampleRates[requestedLod]; const float updateRateInSeconds = animGraphSampleRate > 0.0f ? 1.0f / animGraphSampleRate : 0.0f; actorInstance->SetMotionSamplingRate(updateRateInSeconds); } + + // Disable the automatic mesh LOD level adjustment based on screen space in case a simple LOD component is present. + // The simple LOD component overrides the mesh LOD level and syncs the skeleton with the mesh LOD level. + AZ::Render::MeshComponentRequestBus::Event(entityId, + &AZ::Render::MeshComponentRequestBus::Events::SetLodType, + AZ::RPI::Cullable::LodType::SpecificLod); + + // When setting the actor instance LOD level, a change is just requested and with the next update it will get applied. + // This means that the current LOD level might differ from the requested one. We need to sync the Atom LOD level with the + // current LOD level of the actor instance to avoid skinning artifacts. The requested LOD level will be present and applied + // the following frame. + const size_t currentLod = actorInstance->GetLODLevel(); + AZ::Render::MeshComponentRequestBus::Event(entityId, + &AZ::Render::MeshComponentRequestBus::Events::SetLodOverride, + static_cast(currentLod)); } } } // namespace integration diff --git a/Gems/EMotionFX/Code/Source/Integration/Components/SimpleLODComponent.h b/Gems/EMotionFX/Code/Source/Integration/Components/SimpleLODComponent.h index 96bebdc660..a00918e7c0 100644 --- a/Gems/EMotionFX/Code/Source/Integration/Components/SimpleLODComponent.h +++ b/Gems/EMotionFX/Code/Source/Integration/Components/SimpleLODComponent.h @@ -17,7 +17,7 @@ #include #include - +#include namespace EMotionFX { @@ -93,6 +93,9 @@ namespace EMotionFX Configuration m_configuration; // Component configuration. EMotionFX::ActorInstance* m_actorInstance; // Associated actor instance (retrieved from Actor Component). + + AZ::RPI::Cullable::LodType m_previousLodType = AZ::RPI::Cullable::LodType::Default; + size_t m_previousLodLevel = 0; }; } // namespace Integration From 606ba45cf2c5d9902cd3d42a7bc99154894bceb4 Mon Sep 17 00:00:00 2001 From: moraaar Date: Wed, 6 Oct 2021 09:46:04 +0100 Subject: [PATCH 4/6] Fixed PhysX and Blast creation of default config files (#4497) - Avoid saving blast global configuration unnecessarily every time the editor is opened. - Fixed how to obtain the default physics material library. The relative path needs to be from the project folder, not the asset folder inside the project. It was also missing saving the physx configuration after a the default library is assigned to it. - Fixed asset id for physics material asset 'assets/physics/surfacetypemateriallibrary.physmaterial' Signed-off-by: moraaar moraaar@amazon.com --- ...llider_AddingNewGroupWorks.setreg_override | 2 +- ...er_CollisionGroupsWorkflow.setreg_override | 2 +- ...fCollidingLayersNotCollide.setreg_override | 2 +- ...onGroupSameLayerNotCollide.setreg_override | 2 +- ...roupSameCustomLayerCollide.setreg_override | 2 +- .../Registry/physxsystemconfiguration.setreg | 2 +- .../Components/BlastSystemComponent.cpp | 14 +++++++--- .../Source/Components/BlastSystemComponent.h | 1 + .../Components/EditorSystemComponent.cpp | 26 ++++++++++++++----- Gems/PhysX/Code/Source/System/PhysXSystem.cpp | 6 +++-- Gems/PhysX/Code/Source/SystemComponent.cpp | 24 ++++++++++++++--- 11 files changed, 63 insertions(+), 20 deletions(-) diff --git a/AutomatedTesting/Registry/physx_overrides/Collider_AddingNewGroupWorks.setreg_override b/AutomatedTesting/Registry/physx_overrides/Collider_AddingNewGroupWorks.setreg_override index 7065f0dfeb..209e9a9feb 100644 --- a/AutomatedTesting/Registry/physx_overrides/Collider_AddingNewGroupWorks.setreg_override +++ b/AutomatedTesting/Registry/physx_overrides/Collider_AddingNewGroupWorks.setreg_override @@ -109,7 +109,7 @@ }, "MaterialLibrary": { "assetId": { - "guid": "{62446378-67F8-5E49-AC31-761DD5942695}" + "guid": "{7CDF49C3-91A2-5C4E-B642-6D1AEC80E70E}" }, "loadBehavior": "QueueLoad", "assetHint": "assets/physics/surfacetypemateriallibrary.physmaterial" diff --git a/AutomatedTesting/Registry/physx_overrides/Collider_CollisionGroupsWorkflow.setreg_override b/AutomatedTesting/Registry/physx_overrides/Collider_CollisionGroupsWorkflow.setreg_override index b82acaf0ae..65f15f8554 100644 --- a/AutomatedTesting/Registry/physx_overrides/Collider_CollisionGroupsWorkflow.setreg_override +++ b/AutomatedTesting/Registry/physx_overrides/Collider_CollisionGroupsWorkflow.setreg_override @@ -121,7 +121,7 @@ }, "MaterialLibrary": { "assetId": { - "guid": "{62446378-67F8-5E49-AC31-761DD5942695}" + "guid": "{7CDF49C3-91A2-5C4E-B642-6D1AEC80E70E}" }, "loadBehavior": "QueueLoad", "assetHint": "assets/physics/surfacetypemateriallibrary.physmaterial" diff --git a/AutomatedTesting/Registry/physx_overrides/Collider_DiffCollisionGroupDiffCollidingLayersNotCollide.setreg_override b/AutomatedTesting/Registry/physx_overrides/Collider_DiffCollisionGroupDiffCollidingLayersNotCollide.setreg_override index b82acaf0ae..65f15f8554 100644 --- a/AutomatedTesting/Registry/physx_overrides/Collider_DiffCollisionGroupDiffCollidingLayersNotCollide.setreg_override +++ b/AutomatedTesting/Registry/physx_overrides/Collider_DiffCollisionGroupDiffCollidingLayersNotCollide.setreg_override @@ -121,7 +121,7 @@ }, "MaterialLibrary": { "assetId": { - "guid": "{62446378-67F8-5E49-AC31-761DD5942695}" + "guid": "{7CDF49C3-91A2-5C4E-B642-6D1AEC80E70E}" }, "loadBehavior": "QueueLoad", "assetHint": "assets/physics/surfacetypemateriallibrary.physmaterial" diff --git a/AutomatedTesting/Registry/physx_overrides/Collider_NoneCollisionGroupSameLayerNotCollide.setreg_override b/AutomatedTesting/Registry/physx_overrides/Collider_NoneCollisionGroupSameLayerNotCollide.setreg_override index b82acaf0ae..65f15f8554 100644 --- a/AutomatedTesting/Registry/physx_overrides/Collider_NoneCollisionGroupSameLayerNotCollide.setreg_override +++ b/AutomatedTesting/Registry/physx_overrides/Collider_NoneCollisionGroupSameLayerNotCollide.setreg_override @@ -121,7 +121,7 @@ }, "MaterialLibrary": { "assetId": { - "guid": "{62446378-67F8-5E49-AC31-761DD5942695}" + "guid": "{7CDF49C3-91A2-5C4E-B642-6D1AEC80E70E}" }, "loadBehavior": "QueueLoad", "assetHint": "assets/physics/surfacetypemateriallibrary.physmaterial" diff --git a/AutomatedTesting/Registry/physx_overrides/Collider_SameCollisionGroupSameCustomLayerCollide.setreg_override b/AutomatedTesting/Registry/physx_overrides/Collider_SameCollisionGroupSameCustomLayerCollide.setreg_override index b82acaf0ae..65f15f8554 100644 --- a/AutomatedTesting/Registry/physx_overrides/Collider_SameCollisionGroupSameCustomLayerCollide.setreg_override +++ b/AutomatedTesting/Registry/physx_overrides/Collider_SameCollisionGroupSameCustomLayerCollide.setreg_override @@ -121,7 +121,7 @@ }, "MaterialLibrary": { "assetId": { - "guid": "{62446378-67F8-5E49-AC31-761DD5942695}" + "guid": "{7CDF49C3-91A2-5C4E-B642-6D1AEC80E70E}" }, "loadBehavior": "QueueLoad", "assetHint": "assets/physics/surfacetypemateriallibrary.physmaterial" diff --git a/AutomatedTesting/Registry/physxsystemconfiguration.setreg b/AutomatedTesting/Registry/physxsystemconfiguration.setreg index 83aad307a6..2ade83d769 100644 --- a/AutomatedTesting/Registry/physxsystemconfiguration.setreg +++ b/AutomatedTesting/Registry/physxsystemconfiguration.setreg @@ -103,7 +103,7 @@ }, "MaterialLibrary": { "assetId": { - "guid": "{62446378-67F8-5E49-AC31-761DD5942695}" + "guid": "{7CDF49C3-91A2-5C4E-B642-6D1AEC80E70E}" }, "loadBehavior": "QueueLoad", "assetHint": "assets/physics/surfacetypemateriallibrary.physmaterial" diff --git a/Gems/Blast/Code/Source/Components/BlastSystemComponent.cpp b/Gems/Blast/Code/Source/Components/BlastSystemComponent.cpp index 21ec1a4603..b7ee805b3c 100644 --- a/Gems/Blast/Code/Source/Components/BlastSystemComponent.cpp +++ b/Gems/Blast/Code/Source/Components/BlastSystemComponent.cpp @@ -286,8 +286,11 @@ namespace Blast DefaultConfigurationPath, globalConfiguration); AZ_Warning("Blast", loaded, "Failed to load Blast configuration, initializing with default configs."); - SetGlobalConfiguration(globalConfiguration); - SaveConfiguration(); + ApplyGlobalConfiguration(globalConfiguration); + if (!loaded) + { + SaveConfiguration(); + } } void BlastSystemComponent::SaveConfiguration() @@ -394,8 +397,13 @@ namespace Blast void BlastSystemComponent::SetGlobalConfiguration(const BlastGlobalConfiguration& globalConfiguration) { - m_configuration = globalConfiguration; + ApplyGlobalConfiguration(globalConfiguration); SaveConfiguration(); + } + + void BlastSystemComponent::ApplyGlobalConfiguration(const BlastGlobalConfiguration& globalConfiguration) + { + m_configuration = globalConfiguration; { AZ::Data::Asset& materialLibrary = m_configuration.m_materialLibrary; diff --git a/Gems/Blast/Code/Source/Components/BlastSystemComponent.h b/Gems/Blast/Code/Source/Components/BlastSystemComponent.h index a43bc17aca..8f6c200870 100644 --- a/Gems/Blast/Code/Source/Components/BlastSystemComponent.h +++ b/Gems/Blast/Code/Source/Components/BlastSystemComponent.h @@ -93,6 +93,7 @@ namespace Blast void InitPhysics(); void DeactivatePhysics(); + void ApplyGlobalConfiguration(const BlastGlobalConfiguration& materialLibrary); void RegisterCommands(); // Internal helper functions & classes diff --git a/Gems/PhysX/Code/Editor/Source/Components/EditorSystemComponent.cpp b/Gems/PhysX/Code/Editor/Source/Components/EditorSystemComponent.cpp index f65768cec9..a5b6430591 100644 --- a/Gems/PhysX/Code/Editor/Source/Components/EditorSystemComponent.cpp +++ b/Gems/PhysX/Code/Editor/Source/Components/EditorSystemComponent.cpp @@ -8,7 +8,9 @@ #include "EditorSystemComponent.h" #include +#include #include +#include #include #include #include @@ -23,7 +25,7 @@ namespace PhysX { - constexpr const char* DefaultAssetFilePath = "Physics/SurfaceTypeMaterialLibrary"; + constexpr const char* DefaultAssetFilePath = "Assets/Physics/SurfaceTypeMaterialLibrary"; constexpr const char* TemplateAssetFilename = "PhysX/TemplateMaterialLibrary"; static AZStd::optional> GetMaterialLibraryTemplate() @@ -67,7 +69,7 @@ namespace PhysX assetId, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetIdByPath, relativePath.c_str(), assetType, true /*autoRegisterIfNotFound*/); AZ::Data::Asset newAsset = - AZ::Data::AssetManager::Instance().GetAsset(assetId, assetType, AZ::Data::AssetLoadBehavior::Default); + AZ::Data::AssetManager::Instance().FindOrCreateAsset(assetId, assetType, AZ::Data::AssetLoadBehavior::Default); if (auto* newMaterialLibraryData = azrtti_cast(newAsset.GetData())) { @@ -138,6 +140,14 @@ namespace PhysX if (auto retrievedMaterialLibrary = RetrieveDefaultMaterialLibrary()) { physxSystem->UpdateMaterialLibrary(retrievedMaterialLibrary.value()); + + // After setting the default material library, save the physx configuration. + auto saveCallback = []([[maybe_unused]] const PhysXSystemConfiguration& config, [[maybe_unused]] PhysXSettingsRegistryManager::Result result) + { + AZ_Warning("PhysX", result == PhysXSettingsRegistryManager::Result::Success, + "Unable to save the PhysX configuration after setting default material library."); + }; + physxSystem->GetSettingsRegistryManager().SaveSystemConfiguration(physxSystem->GetPhysXConfiguration(), saveCallback); } } } @@ -236,11 +246,15 @@ namespace PhysX if (!resultAssetId.IsValid()) { // No file for the default material library, create it - const char* assetRoot = AZ::IO::FileIOBase::GetInstance()->GetAlias("@projectsourceassets@"); - AZStd::string fullPath; - AzFramework::StringFunc::Path::ConstructFull(assetRoot, DefaultAssetFilePath, assetExtension.c_str(), fullPath); + AZ::IO::Path fullPath; + if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr) + { + settingsRegistry->Get(fullPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectPath); + } + fullPath /= DefaultAssetFilePath; + fullPath.ReplaceExtension(AZ::IO::PathView(assetExtension)); - if (auto materialLibraryOpt = CreateMaterialLibrary(fullPath, relativePath)) + if (auto materialLibraryOpt = CreateMaterialLibrary(fullPath.Native(), relativePath)) { return materialLibraryOpt; } diff --git a/Gems/PhysX/Code/Source/System/PhysXSystem.cpp b/Gems/PhysX/Code/Source/System/PhysXSystem.cpp index ebdfc5d417..cc21285f8c 100644 --- a/Gems/PhysX/Code/Source/System/PhysXSystem.cpp +++ b/Gems/PhysX/Code/Source/System/PhysXSystem.cpp @@ -511,10 +511,12 @@ namespace PhysX materialLibrary.BlockUntilLoadComplete(); - AZ_Warning("PhysX", (materialLibrary.GetData() != nullptr), + const bool loadedSuccessfully = materialLibrary.GetData() != nullptr && !materialLibrary.IsError(); + + AZ_Warning("PhysX", loadedSuccessfully, "LoadDefaultMaterialLibrary: Default Material Library asset data is invalid."); - return materialLibrary.GetData() != nullptr && !materialLibrary.IsError(); + return loadedSuccessfully; } //TEMP -- until these are fully moved over here diff --git a/Gems/PhysX/Code/Source/SystemComponent.cpp b/Gems/PhysX/Code/Source/SystemComponent.cpp index 42c78d2c1d..f56ad43079 100644 --- a/Gems/PhysX/Code/Source/SystemComponent.cpp +++ b/Gems/PhysX/Code/Source/SystemComponent.cpp @@ -458,7 +458,13 @@ namespace PhysX { const PhysXSystemConfiguration defaultConfig = PhysXSystemConfiguration::CreateDefault(); m_physXSystem->Initialize(&defaultConfig); - registryManager.SaveSystemConfiguration(defaultConfig, {}); + + auto saveCallback = []([[maybe_unused]] const PhysXSystemConfiguration& config, [[maybe_unused]] PhysXSettingsRegistryManager::Result result) + { + AZ_Warning("PhysX", result == PhysXSettingsRegistryManager::Result::Success, + "Unable to save the default PhysX configuration."); + }; + registryManager.SaveSystemConfiguration(defaultConfig, saveCallback); } //Load the DefaultSceneConfig @@ -471,7 +477,13 @@ namespace PhysX { const AzPhysics::SceneConfiguration defaultConfig = AzPhysics::SceneConfiguration::CreateDefault(); m_physXSystem->UpdateDefaultSceneConfiguration(defaultConfig); - registryManager.SaveDefaultSceneConfiguration(defaultConfig, {}); + + auto saveCallback = []([[maybe_unused]] const AzPhysics::SceneConfiguration& config, [[maybe_unused]] PhysXSettingsRegistryManager::Result result) + { + AZ_Warning("PhysX", result == PhysXSettingsRegistryManager::Result::Success, + "Unable to save the default Scene configuration."); + }; + registryManager.SaveDefaultSceneConfiguration(defaultConfig, saveCallback); } //load the debug configuration and initialize the PhysX debug interface @@ -486,7 +498,13 @@ namespace PhysX { const Debug::DebugConfiguration defaultConfig = Debug::DebugConfiguration::CreateDefault(); debug->Initialize(defaultConfig); - registryManager.SaveDebugConfiguration(defaultConfig, {}); + + auto saveCallback = []([[maybe_unused]] const Debug::DebugConfiguration& config, [[maybe_unused]] PhysXSettingsRegistryManager::Result result) + { + AZ_Warning("PhysX", result == PhysXSettingsRegistryManager::Result::Success, + "Unable to save the default PhysX Debug configuration."); + }; + registryManager.SaveDebugConfiguration(defaultConfig, saveCallback); } } } From 5cee9b43b7a6413b5459dac6ce3e52e6d37c9a97 Mon Sep 17 00:00:00 2001 From: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Wed, 6 Oct 2021 07:14:38 -0500 Subject: [PATCH 5/6] Squashed commit of Procedural Prefab work (#4481) * Squashed commit of the following: commit 964a45ead662f502ff0d63ae3528a9aa18a760f4 Merge: 8d4c1dee78 799ab8585b Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Fri Oct 1 16:16:47 2021 -0500 Merge branch 'development' into Feature_LY-5384_ProceduralPrefabs Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> # Conflicts: # Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.h commit 8d4c1dee782a1b82ded14d11f7fe879c865980a7 Author: jackalbe <23512001+jackalbe@users.noreply.github.com> Date: Fri Oct 1 15:49:22 2021 -0500 fixing non-unity build Signed-off-by: jackalbe <23512001+jackalbe@users.noreply.github.com> commit e83431b3be58f36a875b5187c03cd67368d91726 Author: jackalbe <23512001+jackalbe@users.noreply.github.com> Date: Fri Oct 1 12:42:38 2021 -0500 fixing Gems/Multiplayer/Code/Source/Components/NetworkCharacterComponent.cpp:172:28: error: member access into incomplete type 'AZ::BehaviorContext' Signed-off-by: jackalbe <23512001+jackalbe@users.noreply.github.com> commit b0523867d9605aff67710f4ab6030f327cd5558f Author: jackalbe <23512001+jackalbe@users.noreply.github.com> Date: Fri Oct 1 10:23:56 2021 -0500 fix for error: unused variable 'targetInstanceRef' Signed-off-by: jackalbe <23512001+jackalbe@users.noreply.github.com> commit 387c42ac1a4268ff8b2701c0c914e384b355e629 Merge: d87b41997e 0fb821a44b Author: jackalbe <23512001+jackalbe@users.noreply.github.com> Date: Fri Oct 1 10:00:46 2021 -0500 Merge branch 'Feature_LY-5384_ProceduralPrefabs' of https://github.com/aws-lumberyard-dev/o3de into Feature_LY-5384_ProceduralPrefabs commit d87b41997eec9a6b0d03c1040901904d68b873fb Author: jackalbe <23512001+jackalbe@users.noreply.github.com> Date: Thu Sep 30 18:03:38 2021 -0500 fixing non-unity build Signed-off-by: jackalbe <23512001+jackalbe@users.noreply.github.com> commit 0fb821a44b788ab1cca61dce7c1fbdbedc2f37c0 Author: Jackson <23512001+jackalbe@users.noreply.github.com> Date: Thu Sep 30 15:43:50 2021 -0500 adding header for validation Signed-off-by: Jackson <23512001+jackalbe@users.noreply.github.com> commit 30f5135f63286ce8f752df5787937f9543589cb5 Merge: 2d9e1b9f16 103dc6cfcf Author: Jackson <23512001+jackalbe@users.noreply.github.com> Date: Thu Sep 30 11:20:15 2021 -0500 Merge branch 'development' into Feature_LY-5384_ProceduralPrefabs added a few headers as well # Conflicts: # Code/Framework/AzToolsFramework/AzToolsFramework/AzToolsFrameworkModule.cpp Signed-off-by: Jackson <23512001+jackalbe@users.noreply.github.com> commit 2d9e1b9f16f8861df92c58f0f83974859e615b1f Merge: 39ee7a8a80 af84e71638 Author: Allen Jackson <23512001+jackalbe@users.noreply.github.com> Date: Wed Sep 29 14:22:39 2021 -0500 Merge pull request #244 from aws-lumberyard-dev/feature_lyn5880_procprefab_tooling_updates {lyn5880} adding Instantiate Procedural Prefab to the Editor commit af84e716384de048c8555fe5ccdc293e885896f9 Author: jackalbe <23512001+jackalbe@users.noreply.github.com> Date: Wed Sep 29 14:21:09 2021 -0500 updated based on feeback Signed-off-by: jackalbe <23512001+jackalbe@users.noreply.github.com> commit 9c83f6086203e14becb60af5ae937e8e609eb9ed Author: jackalbe <23512001+jackalbe@users.noreply.github.com> Date: Wed Sep 29 11:35:30 2021 -0500 small include tweak Signed-off-by: jackalbe <23512001+jackalbe@users.noreply.github.com> commit 11ac99a87097621796af79329bf9d9344155049e Author: jackalbe <23512001+jackalbe@users.noreply.github.com> Date: Wed Sep 29 10:08:53 2021 -0500 moved the seg reg key to the CPP file removed the Queue Load Signed-off-by: jackalbe <23512001+jackalbe@users.noreply.github.com> commit 39ee7a8a803a032652b122b73fba7007abbdbf88 Merge: 0fc7d5f361 8b4f5ded51 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Wed Sep 29 08:24:57 2021 -0500 Merge pull request #241 from aws-lumberyard-dev/Feature_LY-5384_ProceduralPrefabs_PythonExample Add example prefab script and FBX with 2 meshes to test it commit 941f6a00d1a6222f10acfcd55a2017be6352f723 Author: jackalbe <23512001+jackalbe@users.noreply.github.com> Date: Tue Sep 28 16:07:47 2021 -0500 make sure the AZ::IO::SystemFile::Exists() before returning fullPath Signed-off-by: jackalbe <23512001+jackalbe@users.noreply.github.com> commit 8b4f5ded510d8c6ef47a2d2380fa49c7f6e1fd4e Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Tue Sep 28 14:03:11 2021 -0500 Move sceneJobHandler reset out of exception block. Add more info to error messages Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 0c82937fcd90d0c606c330f6d3e4cec8eca7edb3 Author: jackalbe <23512001+jackalbe@users.noreply.github.com> Date: Tue Sep 28 13:04:37 2021 -0500 {lyn5880} adding Instantiate Procedural Prefab to the Editor Signed-off-by: jackalbe <23512001+jackalbe@users.noreply.github.com> commit 0fc7d5f3610f95dcdd97614a282b9f4eabfc93dc Merge: ea90e321d7 8ca6acc67d Author: Allen Jackson <23512001+jackalbe@users.noreply.github.com> Date: Tue Sep 28 09:23:54 2021 -0500 Merge pull request #235 from aws-lumberyard-dev/feature_lyn5394_procprefab_asset {ly5395} adding asset loading logic for procedural prefabs commit 8ca6acc67dcbd375df9813acbf0062a8a5c7809a Author: jackalbe <23512001+jackalbe@users.noreply.github.com> Date: Mon Sep 27 13:52:21 2021 -0500 added AZ::Prefab::PrefabGroupAssetHandler::s_Extension optimized headers Signed-off-by: jackalbe <23512001+jackalbe@users.noreply.github.com> commit e446aaa4e9951e474f832299f8149142dbf6e85f Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Mon Sep 27 09:12:40 2021 -0500 Remove some whitespace Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit ea90e321d737cd7bafbe617f9b5fbbeae3c4a7e9 Merge: f4c9fc50c3 5ae3c67cc7 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Mon Sep 27 09:04:58 2021 -0500 Merge pull request #238 from aws-lumberyard-dev/Feature_LY-5384_ProceduralPrefabs_PythonScript Fixes to support writing a python script to generate a prefab commit b69ebbae17826b59f3f4fb675c57be5582acf628 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Fri Sep 24 17:51:17 2021 -0500 Use raise_error instead of print Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 407b8d804841f0ba87a0c82405c9d1319435e2da Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Fri Sep 24 17:47:20 2021 -0500 Add scene_mesh_to_prefab.py example ProceduralPrefab script and multiple_mesh_one_material FBX which uses the script Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 5ae3c67cc70603ec70f53c03ee716982b55b759a Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Fri Sep 24 14:50:47 2021 -0500 Test entity cleanup Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 55da78dda5ef9bc558b65bf99d551ffebd38acef Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Fri Sep 24 14:50:28 2021 -0500 Make CastWithTypeName only return true if the object can be successfully cast Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 9f2e85bb691a86910d14477f94b9e631ee343e0b Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Fri Sep 24 14:49:28 2021 -0500 Remove RemoveAllTemplates API from scripting API and use prefab system interface version instead Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 41d46d1f00a16243d3fffda32186fcd7964db78a Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Fri Sep 24 13:19:51 2021 -0500 Store watch folder in scene so source relative path can be calculated Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit f4c9fc50c3ac4c3fc68e98d78b72c4f1f571b516 Merge: de2612b3b9 8bd3c0acdd Author: Allen Jackson <23512001+jackalbe@users.noreply.github.com> Date: Thu Sep 23 14:10:31 2021 -0500 Merge pull request #239 from aws-lumberyard-dev/fix_LY5384_script_processing_rule {ly5384} script processing rule behavior more stateless commit 8bd3c0acdd874d6421d25cc77c80da9906afefc2 Author: Jackson <23512001+jackalbe@users.noreply.github.com> Date: Wed Sep 22 15:19:30 2021 -0500 {ly5384} script processing rule beahvior more stateless Made the script processing rule beahvior more stateless so that the script name needs to be discovered each time. Disconnet from the bus after each scene script builder usage. Before it would be possible that the same script can be run more than once for each asset. Signed-off-by: Jackson <23512001+jackalbe@users.noreply.github.com> commit 7afd9d4a9911adb1dc665361a09486e6852ba4f8 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Wed Sep 22 13:27:12 2021 -0500 Update scene_data.py to latest PrefabGroup format Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 74d1ba8853d62b75786d68a6bdeb1bfb2ca52346 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Wed Sep 22 13:26:27 2021 -0500 Fix GetNodeContent to return a GraphObjectProxy wrapping a nullptr instead of just returning a nullptr which causes issues for python Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit ca4127353e139f9d853784ca6a74e5deeb82d6f9 Author: jackalbe <23512001+jackalbe@users.noreply.github.com> Date: Tue Sep 21 17:48:15 2021 -0500 AZ::JsonSerializationUtils update Signed-off-by: jackalbe <23512001+jackalbe@users.noreply.github.com> commit 30a76be51c37c1718e9b215f33b172a10bd74f08 Author: jackalbe <23512001+jackalbe@users.noreply.github.com> Date: Tue Sep 21 17:21:52 2021 -0500 revert odd README.md merge issue added alias for PrefabBuilder.Tools Signed-off-by: jackalbe <23512001+jackalbe@users.noreply.github.com> commit 6c83d47d51898bcdc17c10578f0198bccd09c834 Merge: 46cb4c2a87 de2612b3b9 Author: jackalbe <23512001+jackalbe@users.noreply.github.com> Date: Tue Sep 21 16:52:58 2021 -0500 Merge branch 'Feature_LY-5384_ProceduralPrefabs' into feature_lyn5394_procprefab_asset # Conflicts: # Gems/Prefab/PrefabBuilder/CMakeLists.txt Signed-off-by: jackalbe <23512001+jackalbe@users.noreply.github.com> commit 46cb4c2a8711f1adad22be24420922365707c409 Author: jackalbe <23512001+jackalbe@users.noreply.github.com> Date: Tue Sep 21 16:43:19 2021 -0500 added ProceduralPrefabAssetTest to cover basics for ProceduralPrefabAsset Signed-off-by: jackalbe <23512001+jackalbe@users.noreply.github.com> commit de2612b3b9a28ef130fc92d1c9d68c90790cf132 Merge: f03bbb236e 3117c54657 Author: Allen Jackson <23512001+jackalbe@users.noreply.github.com> Date: Tue Sep 21 16:28:59 2021 -0500 Merge pull request #232 from aws-lumberyard-dev/fix_ly5384_ProceduralPrefabs_linux_compile {ly5384} Fixing Linux build issues. commit 3117c54657cb21ae2ef200dbfd1cd046c617089d Merge: 15fddd1795 f03bbb236e Author: Allen Jackson <23512001+jackalbe@users.noreply.github.com> Date: Tue Sep 21 16:28:48 2021 -0500 Merge branch 'Feature_LY-5384_ProceduralPrefabs' into fix_ly5384_ProceduralPrefabs_linux_compile commit f03bbb236eab3458fc433d35f9fb84dae88922d6 Merge: f297aa232a fccf900982 Author: Allen Jackson <23512001+jackalbe@users.noreply.github.com> Date: Tue Sep 21 15:52:07 2021 -0500 Merge pull request #233 from aws-lumberyard-dev/fix_ly5384_ProceduralPrefabs_merge_fix fixing an API merge compile error commit fccf9009829b182254064ba17ab3b6e7d44919fa Author: jackalbe <23512001+jackalbe@users.noreply.github.com> Date: Tue Sep 21 15:50:58 2021 -0500 fixing an API merge compile error Signed-off-by: jackalbe <23512001+jackalbe@users.noreply.github.com> commit 762743b54744258007d8f124be95654ee6f18533 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Tue Sep 21 10:10:46 2021 -0500 Make sure EntityUtilityComponent is loaded in AssetBuilderApplication Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 8c4ab65598e3ebb3a0ae621feb22ef5b57e7de27 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Tue Sep 21 10:09:03 2021 -0500 Clean up entities and templates after python script is done Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 50a1f2a1a4f09cbd3e6256a210cd14a0fdb5b815 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Mon Sep 20 14:05:12 2021 -0500 ScriptProcessorRuleBehavior resets entity context Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 51a6af053d95e90e582a25ae51c5730a7e2b0973 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Tue Sep 21 10:40:37 2021 -0500 Add add_prefab_group Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit f297aa232a2cd9ad5583b3e2b4aa1ce793c07d92 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Tue Sep 21 11:53:25 2021 -0500 Fix merge compile issue Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit fc40f5e75efbb87382bf6227966f2f905ccf6d75 Author: jackalbe <23512001+jackalbe@users.noreply.github.com> Date: Tue Sep 21 11:18:44 2021 -0500 {ly5395} adding asset loading logic for procedural prefabs * enabling the Prefab gem for tool work * enabling prefab gem for AutomatedTesting * AssetTypeInfoHandler for procedural prefab * EnableCatalogForAsset for procedural prefab * RegisterHandler for AssetManager Signed-off-by: jackalbe <23512001+jackalbe@users.noreply.github.com> commit 7a2250db337fbfc693e3cf57458eb56f419b32d6 Merge: c1f3e14304 751a0fab4f Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Tue Sep 21 10:32:37 2021 -0500 Merge remote-tracking branch 'origin/Feature_LY-5384_ProceduralPrefabs' into origin_Feature_LY-5384_ProceduralPrefabs Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 12440233ccd987d41c248df57cee913ebb2ae2f6 Merge: 751a0fab4f f8d39e2671 Author: jackalbe <23512001+jackalbe@users.noreply.github.com> Date: Tue Sep 21 10:25:50 2021 -0500 Merge remote-tracking branch 'origin' into feature_lyn5394_procprefab_asset commit c1f3e143048a914ab2d89146685ca5fe409dda27 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Mon Sep 20 14:03:53 2021 -0500 Fix merge issue Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 1990ec370df81d4b77b4646553c2b13dce18c638 Merge: 23d02ed416 fc8697edd5 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Tue Sep 21 10:21:10 2021 -0500 Merge branch 'development' into origin_Feature_LY-5384_ProceduralPrefabs Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> # Conflicts: # Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp commit 751a0fab4f932ec3d7683e2d46a187eefc7addbf Merge: 23d02ed416 7b8d5629dd Author: Allen Jackson <23512001+jackalbe@users.noreply.github.com> Date: Tue Sep 21 10:10:06 2021 -0500 Merge pull request #229 from aws-lumberyard-dev/feature_lyn5394_procprefab_asset {lyn5394} adding ProceduralPrefabAsset to AZ Tools Framework commit 15fddd1795ff07e8d6ce8840a2d675713b63655c Author: jackalbe <23512001+jackalbe@users.noreply.github.com> Date: Tue Sep 21 10:04:06 2021 -0500 {ly5384} Fixing Linux build issues. * symbols "struct FindComponent" and "AZ::Component* FindComponent()" defined in the same scope, renamed function to FindComponentHelper * wrapped the AZ::ComponentId return for both cases Signed-off-by: jackalbe <23512001+jackalbe@users.noreply.github.com> commit 23d02ed4165a1db9e738057aad0b7613be8105da Merge: 0a31e39a25 0f3680a996 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Tue Sep 21 09:44:30 2021 -0500 Merge pull request #228 from aws-lumberyard-dev/Feature_LY-5384_ProceduralPrefabs_Misc Reflect Prefab/Entity constants and add failure unit tests commit 7b8d5629dd04ca1ed75c9828dbb0949f12eb2ca3 Merge: 78fe2cec6f 0a31e39a25 Author: Allen Jackson <23512001+jackalbe@users.noreply.github.com> Date: Thu Sep 16 17:37:29 2021 -0500 Merge branch 'Feature_LY-5384_ProceduralPrefabs' into feature_lyn5394_procprefab_asset commit 78fe2cec6fbcea9217d0b23ec056e19f144fc9a9 Author: Jackson <23512001+jackalbe@users.noreply.github.com> Date: Thu Sep 16 17:27:57 2021 -0500 Updated PrefabBuilder to point to new asset type for the procedural prefab Signed-off-by: Jackson <23512001+jackalbe@users.noreply.github.com> commit 4da4e026582c8e7bf0fe9c7c7b39f15189c67b78 Author: Jackson <23512001+jackalbe@users.noreply.github.com> Date: Thu Sep 16 16:56:13 2021 -0500 {lyn5394} adding Prefab/Procedural/ProceduralPrefabAsset to AZ Tools Framework Signed-off-by: Jackson <23512001+jackalbe@users.noreply.github.com> commit c2fb7b9ccb4b7d4e8080b8830d4ceeb66cd3972c Merge: 30de326dfb a56daadc45 Author: Allen Jackson <23512001+jackalbe@users.noreply.github.com> Date: Thu Sep 16 16:45:03 2021 -0500 Merge pull request #210 from aws-lumberyard-dev/feature_lyn5393_proc_prefab_behavior {LYN5393} Adding Prefab Group Behavior to output Procedural Prefab Signed-off-by: Jackson <23512001+jackalbe@users.noreply.github.com> commit 0a31e39a25d36c63827424adc883e873aee20b71 Merge: 30de326dfb a56daadc45 Author: Allen Jackson <23512001+jackalbe@users.noreply.github.com> Date: Thu Sep 16 16:45:03 2021 -0500 Merge pull request #210 from aws-lumberyard-dev/feature_lyn5393_proc_prefab_behavior {LYN5393} Adding Prefab Group Behavior to output Procedural Prefab commit 0f3680a9968cbbbcb9558811f8dbd287a97447ae Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Thu Sep 16 13:53:22 2021 -0500 Add failure tests and some test cleanup Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 834eab4c4bf151dfb9e49eb0a5e1f20486b6c05c Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Thu Sep 16 13:34:03 2021 -0500 Reflect InvalidTemplateId, fix reflection for InvalidComponentId Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 30de326dfbc28bbb9481bf282eb35c427469c847 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Thu Sep 16 10:55:16 2021 -0500 Fix merge issues Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 4bcf6b7b4f2d6285f244226b065bff65ba565094 Merge: 20c5cd7259 28fec42242 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Thu Sep 16 10:28:26 2021 -0500 Merge branch 'Feature_LY-5384_ProceduralPrefabs_EntityManagement' into Feature_LY-5384_ProceduralPrefabs Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 20c5cd7259235d26f1aa11e2d61ac6b8a57b8638 Merge: 050e26d609 6845942fa4 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Thu Sep 16 10:24:44 2021 -0500 Merge pull request #200 from aws-lumberyard-dev/Feature_LY-5384_ProceduralPrefabs_CreatePrefab API Update: Wrap PrefabSystemComponentInterface behavior commit 6845942fa419b2b6d5a0103f8d30e6515948310e Merge: 58a51c738e 65da78dcc2 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Thu Sep 16 10:07:33 2021 -0500 Merge pull request #208 from aws-lumberyard-dev/Feature_LY-5384_ProceduralPrefabs_SavePrefabToString API Update: AzToolsFramework::Prefab::PrefabLoaderInterface commit 65da78dcc22c4251696ebb75680928ecf83d6733 Merge: e0c5e060ab 58a51c738e Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Thu Sep 16 10:07:06 2021 -0500 Merge branch 'Feature_LY-5384_ProceduralPrefabs_CreatePrefab' into Feature_LY-5384_ProceduralPrefabs_SavePrefabToString Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> # Conflicts: # Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp # Code/Framework/AzToolsFramework/Tests/Prefab/PrefabScriptingTests.cpp commit 28fec422426640540bd21b2275c7b7a90e4f8e71 Merge: c1b8b5190f d825305202 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Thu Sep 16 09:58:36 2021 -0500 Merge pull request #212 from aws-lumberyard-dev/Feature_LY-5384_ProceduralPrefabs_ComponentManagement API Update: Editor Entity Component Functions commit d825305202d5a02d60dd20fb5d0cc0ecb73f562a Merge: c280964b98 88fa6983d1 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Thu Sep 16 09:58:09 2021 -0500 Merge pull request #218 from aws-lumberyard-dev/Feature_LY-5384_ProceduralPrefabs_ComponentExplore API Update: Editor Component JSON Helper Functions commit a56daadc45e5ad8511aa5160c3afc245ca10fb83 Author: jackalbe <23512001+jackalbe@users.noreply.github.com> Date: Wed Sep 15 16:48:41 2021 -0500 m_prefabGroupBehavior.release() -> m_prefabGroupBehavior.reset() adding error messages for the prefab processing fixed typo Signed-off-by: jackalbe <23512001+jackalbe@users.noreply.github.com> commit c1a03351f55f9e8f5161c0ef0288c8e64050e399 Author: jackalbe <23512001+jackalbe@users.noreply.github.com> Date: Wed Sep 15 16:32:42 2021 -0500 reduced the JSON for the testing framework to tree types only Signed-off-by: jackalbe <23512001+jackalbe@users.noreply.github.com> commit 0008866baa7882ec106e134049e2bd7b95113796 Author: jackalbe <23512001+jackalbe@users.noreply.github.com> Date: Wed Sep 15 16:25:00 2021 -0500 enable ProceduralPrefabAsset JsonRegistrationContext Signed-off-by: jackalbe <23512001+jackalbe@users.noreply.github.com> commit 58a51c738e1bbd5bcad27cf1c0add4760bb8cffa Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Wed Sep 15 14:31:59 2021 -0500 Remove unneeded include Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit e0c5e060ab8a9cb2cfce4c083f276ab064aa08ea Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Wed Sep 15 14:20:04 2021 -0500 Cleanup whitespace Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 88fa6983d19ac3a0d7c4073ea390c36794d2603b Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Wed Sep 15 13:49:20 2021 -0500 FindMatchingComponents now returns a vector of ComponentDetails which includes base class info (non-recursive) Fixed a memory leak Moved const to header Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit b57a9d4261aea85a8ddb1036307d8ad296cac859 Author: jackalbe <23512001+jackalbe@users.noreply.github.com> Date: Wed Sep 15 10:49:32 2021 -0500 updated the DOM logic for the asset loading sake Signed-off-by: jackalbe <23512001+jackalbe@users.noreply.github.com> commit f55ee9f5eabbeac998028f027e1c8adf55419a9d Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Tue Sep 14 13:23:13 2021 -0500 Update CreateStringPermissive to stop when enough data has been collected. Update unit tests. Fixed out of bounds behavior in EntityUtilityComponent usage of CreateStringPermissive. Updated AssetTreeFilterModel to cap the string length Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 6d6707dea8cd072261d65e0a508d1179df3892d4 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Tue Sep 14 10:55:38 2021 -0500 Rename GetComponentJson to GetComponentDefaultJson. Clean up GetComponentTypeIdFromName Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit f4380d37a40ada353f7156b0d06b7ab60b72a151 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Tue Sep 14 10:29:04 2021 -0500 Move scripting ebus and handler into separate files Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 572cb58f854aa2368b18f8cf70154d471dfac047 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Fri Sep 10 20:27:41 2021 -0400 Renamed SearchComponents to FindMatchingComponents Added missing printf formatting Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 050e26d60931700e226a4d1a5d02d6a1d4c69915 Merge: 542bdfc5d7 c1b8b5190f Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Fri Sep 10 09:31:08 2021 -0400 Merge pull request #201 from aws-lumberyard-dev/Feature_LY-5384_ProceduralPrefabs_EntityManagement API Update: Editor Entity Management - Add some comments and error handling commit 875d5dc466b65d893a44e11aa9467c45cf7994e9 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Fri Sep 10 09:13:43 2021 -0400 Move bus and handler code into separate files Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit a2d94e24fb2cd1993835f17e7129000c00926cf7 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Thu Sep 9 17:25:35 2021 -0400 Add comments Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 16f507377f4f04c88028833f69327bd11b401b87 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Thu Sep 9 17:16:33 2021 -0400 Add error messages Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 2e6d7d40dc731989128fbe829141057b3558c88b Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Thu Sep 9 16:54:20 2021 -0400 Cleanup code Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 963891eaeb8d616bb071f2a7b50cdabd994241a2 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Fri Sep 3 10:48:40 2021 -0500 Add and update unit tests Reflect APIs to behavior context Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 664be8eca54293407e5ce2d631375d907c1bb2f6 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Thu Sep 2 13:00:02 2021 -0500 Add Search and Component json output APIs Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit e94fe64f366009d06b889954d292c37ea4f96fe2 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Thu Sep 9 13:37:44 2021 -0400 Make bus handler private Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 345a1b0d5edef99d5a8ea2bce2f4f3478652a4f1 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Thu Sep 9 12:30:13 2021 -0400 Address feedback Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit c280964b983635bed9e8bbfb94b2188403287d4c Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Thu Sep 9 10:12:49 2021 -0500 Address feedback Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 3b819ee827fc4c19312f2d2bc5788e2cc36de4f4 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Fri Sep 3 16:06:10 2021 -0500 Update unit test to verify components on saved prefab Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 0b63c0e316ec621ed17edf36109b7cd1d50ef606 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Fri Sep 3 12:52:46 2021 -0500 Expand test Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit ea59416626d9b43599cf8847585e599f8fa5fd22 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Wed Sep 1 17:32:14 2021 -0500 Add error handling Add component creation Add error testing Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 9d0f2ae33eadba0216bd245157b94692792b1a86 Author: Jackson <23512001+jackalbe@users.noreply.github.com> Date: Wed Sep 1 17:31:28 2021 -0500 enabling ProductDependencies test again Signed-off-by: Jackson <23512001+jackalbe@users.noreply.github.com> commit 15d1e4730df31e71e7e3ee6d4914f19b233c1b7f Author: Jackson <23512001+jackalbe@users.noreply.github.com> Date: Wed Sep 1 17:23:26 2021 -0500 Merge branch 'Feature_LY-5384_ProceduralPrefabs' into feature_lyn5393_proc_prefab_behavior # Conflicts: # Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroupTests.cpp # Gems/Prefab/PrefabBuilder/prefabbuilder_files.cmake # Gems/Prefab/PrefabBuilder/prefabbuilder_tests_files.cmake Signed-off-by: Jackson <23512001+jackalbe@users.noreply.github.com> commit ce2e4602bde26fbb34b43c73f820a97b9e229b5a Merge: 5ad0aac747 542bdfc5d7 Author: Jackson <23512001+jackalbe@users.noreply.github.com> Date: Wed Sep 1 17:14:07 2021 -0500 Merge branch 'Feature_LY-5384_ProceduralPrefabs' into feature_lyn5393_proc_prefab_behavior # Conflicts: # Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroupTests.cpp # Gems/Prefab/PrefabBuilder/prefabbuilder_files.cmake # Gems/Prefab/PrefabBuilder/prefabbuilder_tests_files.cmake Signed-off-by: Jackson <23512001+jackalbe@users.noreply.github.com> commit 542bdfc5d762c07938f9a0f8e729070f1df9edfc Merge: 3e7564c944 4899f67986 Author: Allen Jackson <23512001+jackalbe@users.noreply.github.com> Date: Wed Sep 1 17:09:10 2021 -0500 Merge pull request #207 from aws-lumberyard-dev/feature_lyn5392_proc_prefab_group {lyn5392} Adding PrefabGroup scene manifest rule commit 4899f67986992b7eeb5cc3b750493ed21ec7b4f3 Author: Jackson <23512001+jackalbe@users.noreply.github.com> Date: Wed Sep 1 16:33:24 2021 -0500 removing unneeded AZStd::move() calls Signed-off-by: Jackson <23512001+jackalbe@users.noreply.github.com> commit 5ad0aac7472d24bb85990d36399a466288ecfdbc Author: Jackson <23512001+jackalbe@users.noreply.github.com> Date: Wed Sep 1 16:17:52 2021 -0500 cleaned up the code finalized the unit tests fixed the code based on the tests... FTW! Signed-off-by: Jackson <23512001+jackalbe@users.noreply.github.com> commit 24f289dd31292c4b9f5e5848ddc94efd986b4341 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Wed Sep 1 13:06:20 2021 -0500 Add FindComponentByTypeName and UpdateComponentForEntity APIs Add unit tests Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit c1b8b5190f973d2429731308f50c7c962d64301e Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Wed Sep 1 14:02:17 2021 -0500 Fix bus connect that should have been a bus disconnect Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit fde870d6c92e131875ec6aadda2365203a538379 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Wed Sep 1 12:57:04 2021 -0500 Fix up includes Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 8ceddd0efc90d33bc34b5287176497356e57abf9 Author: jackalbe <23512001+jackalbe@users.noreply.github.com> Date: Wed Sep 1 08:43:23 2021 -0500 WIP Signed-off-by: jackalbe <23512001+jackalbe@users.noreply.github.com> commit 3cc9135d87efde24430dbf1cd112bcf9b95ecaa0 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Tue Aug 31 14:44:37 2021 -0500 Rename files Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit f056b3d9578ae2f49dbc172a5929306047620be6 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Tue Aug 31 14:14:34 2021 -0500 Remove 'editor' from bus/component name Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 0fb7a0788e879eb9dc241291f05f0acffc0b0156 Author: jackalbe <23512001+jackalbe@users.noreply.github.com> Date: Tue Aug 31 09:18:37 2021 -0500 {lyn5392} Adding PrefabGroup scene manifest rule * Adding PrefabGroup abstraction and concrete classes to fill out in a scene manifest * has reflections for serialization & behavior * testing the behavior using Lua * testing the serialization using JSON Signed-off-by: jackalbe <23512001+jackalbe@users.noreply.github.com> commit b1756307bff9f86ae0ae1354bae8f470d19b4487 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Mon Aug 30 17:52:18 2021 -0500 Reflect SaveTemplateToString Add unit test Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit ddd2bb89041c5fb8b6add2cfc02c454baab9a7d6 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Mon Aug 30 12:46:17 2021 -0500 Add warning/error messages, update module Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit e839c1cbcc340859fc6ee5cff2d802660944194b Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Mon Aug 30 11:17:25 2021 -0500 Add some comments and error handling Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 347f787cc88405541889e842151de81a70598dd1 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Fri Aug 27 17:16:45 2021 -0500 Fix line endings Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit be26ab1cb16221ba879c10652b430ea31547d868 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Fri Aug 27 15:28:00 2021 -0500 Add CreatePrefabTemplate Add unit test Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 3e7564c944497b7b6cce94dbce486ed8c4561f33 Merge: 5a3c289fac 07841ee749 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Mon Aug 30 10:20:20 2021 -0500 Merge pull request #199 from aws-lumberyard-dev/Feature_LY-5384_ProceduralPrefabs_EntityManagement Add editor entity creation and unit test commit 07841ee749d2a1bc22352cbec3076ac087348676 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Fri Aug 27 16:44:57 2021 -0500 Fix line endings Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 35bc3b89cd1fcc126114ea7d61cdd21e88699080 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Fri Aug 27 15:03:35 2021 -0500 Setup CreateEditorReadyEntity to use a custom entity context Add unit test Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 82519b15510a4ec1b2daf4d883a2c4c3d9c0a347 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Fri Aug 27 09:29:20 2021 -0500 Add EditorEntityUtilityComponent for managing entities from the behavior context Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit f8d39e267162db5fb2091982a7e54bc57ed108bb Merge: 43603cad5e 575faa4443 Author: Chris Galvan Date: Wed Aug 11 15:29:33 2021 -0500 Merge pull request #3049 from aws-lumberyard-dev/cgalvan/gitflow_210811 Merged stabilization/2106 to main commit 575faa4443823ed239d609692847fbd781beebcb Merge: 43603cad5e 4b817a6483 Author: Chris Galvan Date: Wed Aug 11 14:13:27 2021 -0500 Merge remote-tracking branch 'upstream/stabilization/2106' into cgalvan/gitflow_210811 Signed-off-by: Chris Galvan commit 43603cad5e715aadef47b76e24867dd41347d163 Merge: d9cce28a53 bb52475ce8 Author: Terry Michaels Date: Mon Jul 19 14:55:51 2021 -0500 Merge pull request #2271 from aws-lumberyard-dev/Foundation/miterenc/ContributingUpdate Updating CONTRIBUTING.md commit bb52475ce8c37c29b35c81b379befe5a75db1de7 Author: Terry Michaels Date: Mon Jul 19 14:55:14 2021 -0500 Updated text to be more descriptive Signed-off-by: Terry Michaels commit 697dfad486c69beb5ef40cbf6478d6ccd8753cd7 Author: Terry Michaels Date: Mon Jul 19 14:27:24 2021 -0500 Fixed typo Signed-off-by: Terry Michaels commit 650e1ab44d86664b6deab7f6cebeda40c3e1361e Author: Terry Michaels Date: Mon Jul 19 14:19:46 2021 -0500 Updating CONTRIBUTING.md Signed-off-by: Terry Michaels commit d9cce28a5387c7f5b59041869086547905f1e345 Merge: e7f787572e 486ba58628 Author: Chris Galvan Date: Mon Jul 12 14:06:57 2021 -0500 Merge pull request #2096 from aws-lumberyard-dev/cgalvan/gitflow_210712_main Merged stabilization/2106 to main commit 486ba58628488c211a6140f26caa421c38be3f0f Merge: e7f787572e 7cfde884d9 Author: Chris Galvan Date: Mon Jul 12 11:12:41 2021 -0500 Merged stabilization/2106 to development; Resolved merge conflicts Signed-off-by: Chris Galvan commit e7f787572e805c413115265e5873fb2425e2f41b Author: Nicholas Lawson <70027408+lawsonamzn@users.noreply.github.com> Date: Tue Jul 6 08:03:35 2021 -0700 Updates licenses to APACHE-2.0 OR MIT (#1685) Not to be committed before 7/6/2021 Signed-off-by: lawsonamzn <70027408+lawsonamzn@users.noreply.github.com> commit 837e1c737059a819d01ef4ebed7fadae42248dd8 Merge: d30de01752 efcbe2c4a1 Author: Chris Galvan Date: Fri Jul 2 12:11:27 2021 -0500 Merge pull request #1764 from aws-lumberyard-dev/cgalvan/gitflow_210702 Merged stabilization/2106 to main commit efcbe2c4a1e909182dfbd5394f9322513ba91115 Merge: d30de01752 0c43493e29 Author: Chris Galvan Date: Fri Jul 2 10:20:42 2021 -0500 Merge remote-tracking branch 'upstream/stabilization/2106' into cgalvan/gitflow_210702 Signed-off-by: Chris Galvan commit d30de01752b7eaed1f16e2163b1a75c915f061eb Author: Alex Peterson <26804013+AMZN-alexpete@users.noreply.github.com> Date: Mon Jun 28 11:20:36 2021 -0700 Updating LFS config to new endpoint (#1624) Signed-off-by: AMZN-alexpete Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> * fix for "warning C4100: 'outputValueTypeId': unreferenced formal" Signed-off-by: jackalbe <23512001+jackalbe@users.noreply.github.com> * Material Editor: Added alternate skybox toggle to the toolbar Signed-off-by: Guthrie Adams Signed-off-by: jackalbe <23512001+jackalbe@users.noreply.github.com> * Squashed commit of the following: commit 964a45ead662f502ff0d63ae3528a9aa18a760f4 Merge: 8d4c1dee78 799ab8585b Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Fri Oct 1 16:16:47 2021 -0500 Merge branch 'development' into Feature_LY-5384_ProceduralPrefabs Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> # Conflicts: # Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.h commit 8d4c1dee782a1b82ded14d11f7fe879c865980a7 Author: jackalbe <23512001+jackalbe@users.noreply.github.com> Date: Fri Oct 1 15:49:22 2021 -0500 fixing non-unity build Signed-off-by: jackalbe <23512001+jackalbe@users.noreply.github.com> commit e83431b3be58f36a875b5187c03cd67368d91726 Author: jackalbe <23512001+jackalbe@users.noreply.github.com> Date: Fri Oct 1 12:42:38 2021 -0500 fixing Gems/Multiplayer/Code/Source/Components/NetworkCharacterComponent.cpp:172:28: error: member access into incomplete type 'AZ::BehaviorContext' Signed-off-by: jackalbe <23512001+jackalbe@users.noreply.github.com> commit b0523867d9605aff67710f4ab6030f327cd5558f Author: jackalbe <23512001+jackalbe@users.noreply.github.com> Date: Fri Oct 1 10:23:56 2021 -0500 fix for error: unused variable 'targetInstanceRef' Signed-off-by: jackalbe <23512001+jackalbe@users.noreply.github.com> commit 387c42ac1a4268ff8b2701c0c914e384b355e629 Merge: d87b41997e 0fb821a44b Author: jackalbe <23512001+jackalbe@users.noreply.github.com> Date: Fri Oct 1 10:00:46 2021 -0500 Merge branch 'Feature_LY-5384_ProceduralPrefabs' of https://github.com/aws-lumberyard-dev/o3de into Feature_LY-5384_ProceduralPrefabs commit d87b41997eec9a6b0d03c1040901904d68b873fb Author: jackalbe <23512001+jackalbe@users.noreply.github.com> Date: Thu Sep 30 18:03:38 2021 -0500 fixing non-unity build Signed-off-by: jackalbe <23512001+jackalbe@users.noreply.github.com> commit 0fb821a44b788ab1cca61dce7c1fbdbedc2f37c0 Author: Jackson <23512001+jackalbe@users.noreply.github.com> Date: Thu Sep 30 15:43:50 2021 -0500 adding header for validation Signed-off-by: Jackson <23512001+jackalbe@users.noreply.github.com> commit 30f5135f63286ce8f752df5787937f9543589cb5 Merge: 2d9e1b9f16 103dc6cfcf Author: Jackson <23512001+jackalbe@users.noreply.github.com> Date: Thu Sep 30 11:20:15 2021 -0500 Merge branch 'development' into Feature_LY-5384_ProceduralPrefabs added a few headers as well # Conflicts: # Code/Framework/AzToolsFramework/AzToolsFramework/AzToolsFrameworkModule.cpp Signed-off-by: Jackson <23512001+jackalbe@users.noreply.github.com> commit 2d9e1b9f16f8861df92c58f0f83974859e615b1f Merge: 39ee7a8a80 af84e71638 Author: Allen Jackson <23512001+jackalbe@users.noreply.github.com> Date: Wed Sep 29 14:22:39 2021 -0500 Merge pull request #244 from aws-lumberyard-dev/feature_lyn5880_procprefab_tooling_updates {lyn5880} adding Instantiate Procedural Prefab to the Editor commit af84e716384de048c8555fe5ccdc293e885896f9 Author: jackalbe <23512001+jackalbe@users.noreply.github.com> Date: Wed Sep 29 14:21:09 2021 -0500 updated based on feeback Signed-off-by: jackalbe <23512001+jackalbe@users.noreply.github.com> commit 9c83f6086203e14becb60af5ae937e8e609eb9ed Author: jackalbe <23512001+jackalbe@users.noreply.github.com> Date: Wed Sep 29 11:35:30 2021 -0500 small include tweak Signed-off-by: jackalbe <23512001+jackalbe@users.noreply.github.com> commit 11ac99a87097621796af79329bf9d9344155049e Author: jackalbe <23512001+jackalbe@users.noreply.github.com> Date: Wed Sep 29 10:08:53 2021 -0500 moved the seg reg key to the CPP file removed the Queue Load Signed-off-by: jackalbe <23512001+jackalbe@users.noreply.github.com> commit 39ee7a8a803a032652b122b73fba7007abbdbf88 Merge: 0fc7d5f361 8b4f5ded51 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Wed Sep 29 08:24:57 2021 -0500 Merge pull request #241 from aws-lumberyard-dev/Feature_LY-5384_ProceduralPrefabs_PythonExample Add example prefab script and FBX with 2 meshes to test it commit 941f6a00d1a6222f10acfcd55a2017be6352f723 Author: jackalbe <23512001+jackalbe@users.noreply.github.com> Date: Tue Sep 28 16:07:47 2021 -0500 make sure the AZ::IO::SystemFile::Exists() before returning fullPath Signed-off-by: jackalbe <23512001+jackalbe@users.noreply.github.com> commit 8b4f5ded510d8c6ef47a2d2380fa49c7f6e1fd4e Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Tue Sep 28 14:03:11 2021 -0500 Move sceneJobHandler reset out of exception block. Add more info to error messages Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 0c82937fcd90d0c606c330f6d3e4cec8eca7edb3 Author: jackalbe <23512001+jackalbe@users.noreply.github.com> Date: Tue Sep 28 13:04:37 2021 -0500 {lyn5880} adding Instantiate Procedural Prefab to the Editor Signed-off-by: jackalbe <23512001+jackalbe@users.noreply.github.com> commit 0fc7d5f3610f95dcdd97614a282b9f4eabfc93dc Merge: ea90e321d7 8ca6acc67d Author: Allen Jackson <23512001+jackalbe@users.noreply.github.com> Date: Tue Sep 28 09:23:54 2021 -0500 Merge pull request #235 from aws-lumberyard-dev/feature_lyn5394_procprefab_asset {ly5395} adding asset loading logic for procedural prefabs commit 8ca6acc67dcbd375df9813acbf0062a8a5c7809a Author: jackalbe <23512001+jackalbe@users.noreply.github.com> Date: Mon Sep 27 13:52:21 2021 -0500 added AZ::Prefab::PrefabGroupAssetHandler::s_Extension optimized headers Signed-off-by: jackalbe <23512001+jackalbe@users.noreply.github.com> commit e446aaa4e9951e474f832299f8149142dbf6e85f Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Mon Sep 27 09:12:40 2021 -0500 Remove some whitespace Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit ea90e321d737cd7bafbe617f9b5fbbeae3c4a7e9 Merge: f4c9fc50c3 5ae3c67cc7 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Mon Sep 27 09:04:58 2021 -0500 Merge pull request #238 from aws-lumberyard-dev/Feature_LY-5384_ProceduralPrefabs_PythonScript Fixes to support writing a python script to generate a prefab commit b69ebbae17826b59f3f4fb675c57be5582acf628 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Fri Sep 24 17:51:17 2021 -0500 Use raise_error instead of print Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 407b8d804841f0ba87a0c82405c9d1319435e2da Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Fri Sep 24 17:47:20 2021 -0500 Add scene_mesh_to_prefab.py example ProceduralPrefab script and multiple_mesh_one_material FBX which uses the script Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 5ae3c67cc70603ec70f53c03ee716982b55b759a Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Fri Sep 24 14:50:47 2021 -0500 Test entity cleanup Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 55da78dda5ef9bc558b65bf99d551ffebd38acef Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Fri Sep 24 14:50:28 2021 -0500 Make CastWithTypeName only return true if the object can be successfully cast Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 9f2e85bb691a86910d14477f94b9e631ee343e0b Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Fri Sep 24 14:49:28 2021 -0500 Remove RemoveAllTemplates API from scripting API and use prefab system interface version instead Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 41d46d1f00a16243d3fffda32186fcd7964db78a Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Fri Sep 24 13:19:51 2021 -0500 Store watch folder in scene so source relative path can be calculated Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit f4c9fc50c3ac4c3fc68e98d78b72c4f1f571b516 Merge: de2612b3b9 8bd3c0acdd Author: Allen Jackson <23512001+jackalbe@users.noreply.github.com> Date: Thu Sep 23 14:10:31 2021 -0500 Merge pull request #239 from aws-lumberyard-dev/fix_LY5384_script_processing_rule {ly5384} script processing rule behavior more stateless commit 8bd3c0acdd874d6421d25cc77c80da9906afefc2 Author: Jackson <23512001+jackalbe@users.noreply.github.com> Date: Wed Sep 22 15:19:30 2021 -0500 {ly5384} script processing rule beahvior more stateless Made the script processing rule beahvior more stateless so that the script name needs to be discovered each time. Disconnet from the bus after each scene script builder usage. Before it would be possible that the same script can be run more than once for each asset. Signed-off-by: Jackson <23512001+jackalbe@users.noreply.github.com> commit 7afd9d4a9911adb1dc665361a09486e6852ba4f8 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Wed Sep 22 13:27:12 2021 -0500 Update scene_data.py to latest PrefabGroup format Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 74d1ba8853d62b75786d68a6bdeb1bfb2ca52346 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Wed Sep 22 13:26:27 2021 -0500 Fix GetNodeContent to return a GraphObjectProxy wrapping a nullptr instead of just returning a nullptr which causes issues for python Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit ca4127353e139f9d853784ca6a74e5deeb82d6f9 Author: jackalbe <23512001+jackalbe@users.noreply.github.com> Date: Tue Sep 21 17:48:15 2021 -0500 AZ::JsonSerializationUtils update Signed-off-by: jackalbe <23512001+jackalbe@users.noreply.github.com> commit 30a76be51c37c1718e9b215f33b172a10bd74f08 Author: jackalbe <23512001+jackalbe@users.noreply.github.com> Date: Tue Sep 21 17:21:52 2021 -0500 revert odd README.md merge issue added alias for PrefabBuilder.Tools Signed-off-by: jackalbe <23512001+jackalbe@users.noreply.github.com> commit 6c83d47d51898bcdc17c10578f0198bccd09c834 Merge: 46cb4c2a87 de2612b3b9 Author: jackalbe <23512001+jackalbe@users.noreply.github.com> Date: Tue Sep 21 16:52:58 2021 -0500 Merge branch 'Feature_LY-5384_ProceduralPrefabs' into feature_lyn5394_procprefab_asset # Conflicts: # Gems/Prefab/PrefabBuilder/CMakeLists.txt Signed-off-by: jackalbe <23512001+jackalbe@users.noreply.github.com> commit 46cb4c2a8711f1adad22be24420922365707c409 Author: jackalbe <23512001+jackalbe@users.noreply.github.com> Date: Tue Sep 21 16:43:19 2021 -0500 added ProceduralPrefabAssetTest to cover basics for ProceduralPrefabAsset Signed-off-by: jackalbe <23512001+jackalbe@users.noreply.github.com> commit de2612b3b9a28ef130fc92d1c9d68c90790cf132 Merge: f03bbb236e 3117c54657 Author: Allen Jackson <23512001+jackalbe@users.noreply.github.com> Date: Tue Sep 21 16:28:59 2021 -0500 Merge pull request #232 from aws-lumberyard-dev/fix_ly5384_ProceduralPrefabs_linux_compile {ly5384} Fixing Linux build issues. commit 3117c54657cb21ae2ef200dbfd1cd046c617089d Merge: 15fddd1795 f03bbb236e Author: Allen Jackson <23512001+jackalbe@users.noreply.github.com> Date: Tue Sep 21 16:28:48 2021 -0500 Merge branch 'Feature_LY-5384_ProceduralPrefabs' into fix_ly5384_ProceduralPrefabs_linux_compile commit f03bbb236eab3458fc433d35f9fb84dae88922d6 Merge: f297aa232a fccf900982 Author: Allen Jackson <23512001+jackalbe@users.noreply.github.com> Date: Tue Sep 21 15:52:07 2021 -0500 Merge pull request #233 from aws-lumberyard-dev/fix_ly5384_ProceduralPrefabs_merge_fix fixing an API merge compile error commit fccf9009829b182254064ba17ab3b6e7d44919fa Author: jackalbe <23512001+jackalbe@users.noreply.github.com> Date: Tue Sep 21 15:50:58 2021 -0500 fixing an API merge compile error Signed-off-by: jackalbe <23512001+jackalbe@users.noreply.github.com> commit 762743b54744258007d8f124be95654ee6f18533 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Tue Sep 21 10:10:46 2021 -0500 Make sure EntityUtilityComponent is loaded in AssetBuilderApplication Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 8c4ab65598e3ebb3a0ae621feb22ef5b57e7de27 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Tue Sep 21 10:09:03 2021 -0500 Clean up entities and templates after python script is done Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 50a1f2a1a4f09cbd3e6256a210cd14a0fdb5b815 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Mon Sep 20 14:05:12 2021 -0500 ScriptProcessorRuleBehavior resets entity context Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 51a6af053d95e90e582a25ae51c5730a7e2b0973 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Tue Sep 21 10:40:37 2021 -0500 Add add_prefab_group Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit f297aa232a2cd9ad5583b3e2b4aa1ce793c07d92 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Tue Sep 21 11:53:25 2021 -0500 Fix merge compile issue Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit fc40f5e75efbb87382bf6227966f2f905ccf6d75 Author: jackalbe <23512001+jackalbe@users.noreply.github.com> Date: Tue Sep 21 11:18:44 2021 -0500 {ly5395} adding asset loading logic for procedural prefabs * enabling the Prefab gem for tool work * enabling prefab gem for AutomatedTesting * AssetTypeInfoHandler for procedural prefab * EnableCatalogForAsset for procedural prefab * RegisterHandler for AssetManager Signed-off-by: jackalbe <23512001+jackalbe@users.noreply.github.com> commit 7a2250db337fbfc693e3cf57458eb56f419b32d6 Merge: c1f3e14304 751a0fab4f Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Tue Sep 21 10:32:37 2021 -0500 Merge remote-tracking branch 'origin/Feature_LY-5384_ProceduralPrefabs' into origin_Feature_LY-5384_ProceduralPrefabs Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 12440233ccd987d41c248df57cee913ebb2ae2f6 Merge: 751a0fab4f f8d39e2671 Author: jackalbe <23512001+jackalbe@users.noreply.github.com> Date: Tue Sep 21 10:25:50 2021 -0500 Merge remote-tracking branch 'origin' into feature_lyn5394_procprefab_asset commit c1f3e143048a914ab2d89146685ca5fe409dda27 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Mon Sep 20 14:03:53 2021 -0500 Fix merge issue Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 1990ec370df81d4b77b4646553c2b13dce18c638 Merge: 23d02ed416 fc8697edd5 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Tue Sep 21 10:21:10 2021 -0500 Merge branch 'development' into origin_Feature_LY-5384_ProceduralPrefabs Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> # Conflicts: # Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp commit 751a0fab4f932ec3d7683e2d46a187eefc7addbf Merge: 23d02ed416 7b8d5629dd Author: Allen Jackson <23512001+jackalbe@users.noreply.github.com> Date: Tue Sep 21 10:10:06 2021 -0500 Merge pull request #229 from aws-lumberyard-dev/feature_lyn5394_procprefab_asset {lyn5394} adding ProceduralPrefabAsset to AZ Tools Framework commit 15fddd1795ff07e8d6ce8840a2d675713b63655c Author: jackalbe <23512001+jackalbe@users.noreply.github.com> Date: Tue Sep 21 10:04:06 2021 -0500 {ly5384} Fixing Linux build issues. * symbols "struct FindComponent" and "AZ::Component* FindComponent()" defined in the same scope, renamed function to FindComponentHelper * wrapped the AZ::ComponentId return for both cases Signed-off-by: jackalbe <23512001+jackalbe@users.noreply.github.com> commit 23d02ed4165a1db9e738057aad0b7613be8105da Merge: 0a31e39a25 0f3680a996 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Tue Sep 21 09:44:30 2021 -0500 Merge pull request #228 from aws-lumberyard-dev/Feature_LY-5384_ProceduralPrefabs_Misc Reflect Prefab/Entity constants and add failure unit tests commit 7b8d5629dd04ca1ed75c9828dbb0949f12eb2ca3 Merge: 78fe2cec6f 0a31e39a25 Author: Allen Jackson <23512001+jackalbe@users.noreply.github.com> Date: Thu Sep 16 17:37:29 2021 -0500 Merge branch 'Feature_LY-5384_ProceduralPrefabs' into feature_lyn5394_procprefab_asset commit 78fe2cec6fbcea9217d0b23ec056e19f144fc9a9 Author: Jackson <23512001+jackalbe@users.noreply.github.com> Date: Thu Sep 16 17:27:57 2021 -0500 Updated PrefabBuilder to point to new asset type for the procedural prefab Signed-off-by: Jackson <23512001+jackalbe@users.noreply.github.com> commit 4da4e026582c8e7bf0fe9c7c7b39f15189c67b78 Author: Jackson <23512001+jackalbe@users.noreply.github.com> Date: Thu Sep 16 16:56:13 2021 -0500 {lyn5394} adding Prefab/Procedural/ProceduralPrefabAsset to AZ Tools Framework Signed-off-by: Jackson <23512001+jackalbe@users.noreply.github.com> commit c2fb7b9ccb4b7d4e8080b8830d4ceeb66cd3972c Merge: 30de326dfb a56daadc45 Author: Allen Jackson <23512001+jackalbe@users.noreply.github.com> Date: Thu Sep 16 16:45:03 2021 -0500 Merge pull request #210 from aws-lumberyard-dev/feature_lyn5393_proc_prefab_behavior {LYN5393} Adding Prefab Group Behavior to output Procedural Prefab Signed-off-by: Jackson <23512001+jackalbe@users.noreply.github.com> commit 0a31e39a25d36c63827424adc883e873aee20b71 Merge: 30de326dfb a56daadc45 Author: Allen Jackson <23512001+jackalbe@users.noreply.github.com> Date: Thu Sep 16 16:45:03 2021 -0500 Merge pull request #210 from aws-lumberyard-dev/feature_lyn5393_proc_prefab_behavior {LYN5393} Adding Prefab Group Behavior to output Procedural Prefab commit 0f3680a9968cbbbcb9558811f8dbd287a97447ae Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Thu Sep 16 13:53:22 2021 -0500 Add failure tests and some test cleanup Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 834eab4c4bf151dfb9e49eb0a5e1f20486b6c05c Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Thu Sep 16 13:34:03 2021 -0500 Reflect InvalidTemplateId, fix reflection for InvalidComponentId Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 30de326dfbc28bbb9481bf282eb35c427469c847 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Thu Sep 16 10:55:16 2021 -0500 Fix merge issues Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 4bcf6b7b4f2d6285f244226b065bff65ba565094 Merge: 20c5cd7259 28fec42242 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Thu Sep 16 10:28:26 2021 -0500 Merge branch 'Feature_LY-5384_ProceduralPrefabs_EntityManagement' into Feature_LY-5384_ProceduralPrefabs Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 20c5cd7259235d26f1aa11e2d61ac6b8a57b8638 Merge: 050e26d609 6845942fa4 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Thu Sep 16 10:24:44 2021 -0500 Merge pull request #200 from aws-lumberyard-dev/Feature_LY-5384_ProceduralPrefabs_CreatePrefab API Update: Wrap PrefabSystemComponentInterface behavior commit 6845942fa419b2b6d5a0103f8d30e6515948310e Merge: 58a51c738e 65da78dcc2 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Thu Sep 16 10:07:33 2021 -0500 Merge pull request #208 from aws-lumberyard-dev/Feature_LY-5384_ProceduralPrefabs_SavePrefabToString API Update: AzToolsFramework::Prefab::PrefabLoaderInterface commit 65da78dcc22c4251696ebb75680928ecf83d6733 Merge: e0c5e060ab 58a51c738e Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Thu Sep 16 10:07:06 2021 -0500 Merge branch 'Feature_LY-5384_ProceduralPrefabs_CreatePrefab' into Feature_LY-5384_ProceduralPrefabs_SavePrefabToString Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> # Conflicts: # Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp # Code/Framework/AzToolsFramework/Tests/Prefab/PrefabScriptingTests.cpp commit 28fec422426640540bd21b2275c7b7a90e4f8e71 Merge: c1b8b5190f d825305202 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Thu Sep 16 09:58:36 2021 -0500 Merge pull request #212 from aws-lumberyard-dev/Feature_LY-5384_ProceduralPrefabs_ComponentManagement API Update: Editor Entity Component Functions commit d825305202d5a02d60dd20fb5d0cc0ecb73f562a Merge: c280964b98 88fa6983d1 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Thu Sep 16 09:58:09 2021 -0500 Merge pull request #218 from aws-lumberyard-dev/Feature_LY-5384_ProceduralPrefabs_ComponentExplore API Update: Editor Component JSON Helper Functions commit a56daadc45e5ad8511aa5160c3afc245ca10fb83 Author: jackalbe <23512001+jackalbe@users.noreply.github.com> Date: Wed Sep 15 16:48:41 2021 -0500 m_prefabGroupBehavior.release() -> m_prefabGroupBehavior.reset() adding error messages for the prefab processing fixed typo Signed-off-by: jackalbe <23512001+jackalbe@users.noreply.github.com> commit c1a03351f55f9e8f5161c0ef0288c8e64050e399 Author: jackalbe <23512001+jackalbe@users.noreply.github.com> Date: Wed Sep 15 16:32:42 2021 -0500 reduced the JSON for the testing framework to tree types only Signed-off-by: jackalbe <23512001+jackalbe@users.noreply.github.com> commit 0008866baa7882ec106e134049e2bd7b95113796 Author: jackalbe <23512001+jackalbe@users.noreply.github.com> Date: Wed Sep 15 16:25:00 2021 -0500 enable ProceduralPrefabAsset JsonRegistrationContext Signed-off-by: jackalbe <23512001+jackalbe@users.noreply.github.com> commit 58a51c738e1bbd5bcad27cf1c0add4760bb8cffa Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Wed Sep 15 14:31:59 2021 -0500 Remove unneeded include Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit e0c5e060ab8a9cb2cfce4c083f276ab064aa08ea Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Wed Sep 15 14:20:04 2021 -0500 Cleanup whitespace Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 88fa6983d19ac3a0d7c4073ea390c36794d2603b Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Wed Sep 15 13:49:20 2021 -0500 FindMatchingComponents now returns a vector of ComponentDetails which includes base class info (non-recursive) Fixed a memory leak Moved const to header Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit b57a9d4261aea85a8ddb1036307d8ad296cac859 Author: jackalbe <23512001+jackalbe@users.noreply.github.com> Date: Wed Sep 15 10:49:32 2021 -0500 updated the DOM logic for the asset loading sake Signed-off-by: jackalbe <23512001+jackalbe@users.noreply.github.com> commit f55ee9f5eabbeac998028f027e1c8adf55419a9d Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Tue Sep 14 13:23:13 2021 -0500 Update CreateStringPermissive to stop when enough data has been collected. Update unit tests. Fixed out of bounds behavior in EntityUtilityComponent usage of CreateStringPermissive. Updated AssetTreeFilterModel to cap the string length Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 6d6707dea8cd072261d65e0a508d1179df3892d4 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Tue Sep 14 10:55:38 2021 -0500 Rename GetComponentJson to GetComponentDefaultJson. Clean up GetComponentTypeIdFromName Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit f4380d37a40ada353f7156b0d06b7ab60b72a151 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Tue Sep 14 10:29:04 2021 -0500 Move scripting ebus and handler into separate files Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 572cb58f854aa2368b18f8cf70154d471dfac047 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Fri Sep 10 20:27:41 2021 -0400 Renamed SearchComponents to FindMatchingComponents Added missing printf formatting Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 050e26d60931700e226a4d1a5d02d6a1d4c69915 Merge: 542bdfc5d7 c1b8b5190f Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Fri Sep 10 09:31:08 2021 -0400 Merge pull request #201 from aws-lumberyard-dev/Feature_LY-5384_ProceduralPrefabs_EntityManagement API Update: Editor Entity Management - Add some comments and error handling commit 875d5dc466b65d893a44e11aa9467c45cf7994e9 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Fri Sep 10 09:13:43 2021 -0400 Move bus and handler code into separate files Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit a2d94e24fb2cd1993835f17e7129000c00926cf7 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Thu Sep 9 17:25:35 2021 -0400 Add comments Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 16f507377f4f04c88028833f69327bd11b401b87 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Thu Sep 9 17:16:33 2021 -0400 Add error messages Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 2e6d7d40dc731989128fbe829141057b3558c88b Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Thu Sep 9 16:54:20 2021 -0400 Cleanup code Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 963891eaeb8d616bb071f2a7b50cdabd994241a2 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Fri Sep 3 10:48:40 2021 -0500 Add and update unit tests Reflect APIs to behavior context Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 664be8eca54293407e5ce2d631375d907c1bb2f6 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Thu Sep 2 13:00:02 2021 -0500 Add Search and Component json output APIs Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit e94fe64f366009d06b889954d292c37ea4f96fe2 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Thu Sep 9 13:37:44 2021 -0400 Make bus handler private Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 345a1b0d5edef99d5a8ea2bce2f4f3478652a4f1 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Thu Sep 9 12:30:13 2021 -0400 Address feedback Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit c280964b983635bed9e8bbfb94b2188403287d4c Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Thu Sep 9 10:12:49 2021 -0500 Address feedback Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 3b819ee827fc4c19312f2d2bc5788e2cc36de4f4 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Fri Sep 3 16:06:10 2021 -0500 Update unit test to verify components on saved prefab Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 0b63c0e316ec621ed17edf36109b7cd1d50ef606 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Fri Sep 3 12:52:46 2021 -0500 Expand test Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit ea59416626d9b43599cf8847585e599f8fa5fd22 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Wed Sep 1 17:32:14 2021 -0500 Add error handling Add component creation Add error testing Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 9d0f2ae33eadba0216bd245157b94692792b1a86 Author: Jackson <23512001+jackalbe@users.noreply.github.com> Date: Wed Sep 1 17:31:28 2021 -0500 enabling ProductDependencies test again Signed-off-by: Jackson <23512001+jackalbe@users.noreply.github.com> commit 15d1e4730df31e71e7e3ee6d4914f19b233c1b7f Author: Jackson <23512001+jackalbe@users.noreply.github.com> Date: Wed Sep 1 17:23:26 2021 -0500 Merge branch 'Feature_LY-5384_ProceduralPrefabs' into feature_lyn5393_proc_prefab_behavior # Conflicts: # Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroupTests.cpp # Gems/Prefab/PrefabBuilder/prefabbuilder_files.cmake # Gems/Prefab/PrefabBuilder/prefabbuilder_tests_files.cmake Signed-off-by: Jackson <23512001+jackalbe@users.noreply.github.com> commit ce2e4602bde26fbb34b43c73f820a97b9e229b5a Merge: 5ad0aac747 542bdfc5d7 Author: Jackson <23512001+jackalbe@users.noreply.github.com> Date: Wed Sep 1 17:14:07 2021 -0500 Merge branch 'Feature_LY-5384_ProceduralPrefabs' into feature_lyn5393_proc_prefab_behavior # Conflicts: # Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroupTests.cpp # Gems/Prefab/PrefabBuilder/prefabbuilder_files.cmake # Gems/Prefab/PrefabBuilder/prefabbuilder_tests_files.cmake Signed-off-by: Jackson <23512001+jackalbe@users.noreply.github.com> commit 542bdfc5d762c07938f9a0f8e729070f1df9edfc Merge: 3e7564c944 4899f67986 Author: Allen Jackson <23512001+jackalbe@users.noreply.github.com> Date: Wed Sep 1 17:09:10 2021 -0500 Merge pull request #207 from aws-lumberyard-dev/feature_lyn5392_proc_prefab_group {lyn5392} Adding PrefabGroup scene manifest rule commit 4899f67986992b7eeb5cc3b750493ed21ec7b4f3 Author: Jackson <23512001+jackalbe@users.noreply.github.com> Date: Wed Sep 1 16:33:24 2021 -0500 removing unneeded AZStd::move() calls Signed-off-by: Jackson <23512001+jackalbe@users.noreply.github.com> commit 5ad0aac7472d24bb85990d36399a466288ecfdbc Author: Jackson <23512001+jackalbe@users.noreply.github.com> Date: Wed Sep 1 16:17:52 2021 -0500 cleaned up the code finalized the unit tests fixed the code based on the tests... FTW! Signed-off-by: Jackson <23512001+jackalbe@users.noreply.github.com> commit 24f289dd31292c4b9f5e5848ddc94efd986b4341 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Wed Sep 1 13:06:20 2021 -0500 Add FindComponentByTypeName and UpdateComponentForEntity APIs Add unit tests Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit c1b8b5190f973d2429731308f50c7c962d64301e Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Wed Sep 1 14:02:17 2021 -0500 Fix bus connect that should have been a bus disconnect Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit fde870d6c92e131875ec6aadda2365203a538379 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Wed Sep 1 12:57:04 2021 -0500 Fix up includes Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 8ceddd0efc90d33bc34b5287176497356e57abf9 Author: jackalbe <23512001+jackalbe@users.noreply.github.com> Date: Wed Sep 1 08:43:23 2021 -0500 WIP Signed-off-by: jackalbe <23512001+jackalbe@users.noreply.github.com> commit 3cc9135d87efde24430dbf1cd112bcf9b95ecaa0 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Tue Aug 31 14:44:37 2021 -0500 Rename files Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit f056b3d9578ae2f49dbc172a5929306047620be6 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Tue Aug 31 14:14:34 2021 -0500 Remove 'editor' from bus/component name Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 0fb7a0788e879eb9dc241291f05f0acffc0b0156 Author: jackalbe <23512001+jackalbe@users.noreply.github.com> Date: Tue Aug 31 09:18:37 2021 -0500 {lyn5392} Adding PrefabGroup scene manifest rule * Adding PrefabGroup abstraction and concrete classes to fill out in a scene manifest * has reflections for serialization & behavior * testing the behavior using Lua * testing the serialization using JSON Signed-off-by: jackalbe <23512001+jackalbe@users.noreply.github.com> commit b1756307bff9f86ae0ae1354bae8f470d19b4487 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Mon Aug 30 17:52:18 2021 -0500 Reflect SaveTemplateToString Add unit test Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit ddd2bb89041c5fb8b6add2cfc02c454baab9a7d6 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Mon Aug 30 12:46:17 2021 -0500 Add warning/error messages, update module Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit e839c1cbcc340859fc6ee5cff2d802660944194b Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Mon Aug 30 11:17:25 2021 -0500 Add some comments and error handling Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 347f787cc88405541889e842151de81a70598dd1 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Fri Aug 27 17:16:45 2021 -0500 Fix line endings Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit be26ab1cb16221ba879c10652b430ea31547d868 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Fri Aug 27 15:28:00 2021 -0500 Add CreatePrefabTemplate Add unit test Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 3e7564c944497b7b6cce94dbce486ed8c4561f33 Merge: 5a3c289fac 07841ee749 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Mon Aug 30 10:20:20 2021 -0500 Merge pull request #199 from aws-lumberyard-dev/Feature_LY-5384_ProceduralPrefabs_EntityManagement Add editor entity creation and unit test commit 07841ee749d2a1bc22352cbec3076ac087348676 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Fri Aug 27 16:44:57 2021 -0500 Fix line endings Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 35bc3b89cd1fcc126114ea7d61cdd21e88699080 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Fri Aug 27 15:03:35 2021 -0500 Setup CreateEditorReadyEntity to use a custom entity context Add unit test Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 82519b15510a4ec1b2daf4d883a2c4c3d9c0a347 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Fri Aug 27 09:29:20 2021 -0500 Add EditorEntityUtilityComponent for managing entities from the behavior context Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit f8d39e267162db5fb2091982a7e54bc57ed108bb Merge: 43603cad5e 575faa4443 Author: Chris Galvan Date: Wed Aug 11 15:29:33 2021 -0500 Merge pull request #3049 from aws-lumberyard-dev/cgalvan/gitflow_210811 Merged stabilization/2106 to main commit 575faa4443823ed239d609692847fbd781beebcb Merge: 43603cad5e 4b817a6483 Author: Chris Galvan Date: Wed Aug 11 14:13:27 2021 -0500 Merge remote-tracking branch 'upstream/stabilization/2106' into cgalvan/gitflow_210811 Signed-off-by: Chris Galvan commit 43603cad5e715aadef47b76e24867dd41347d163 Merge: d9cce28a53 bb52475ce8 Author: Terry Michaels Date: Mon Jul 19 14:55:51 2021 -0500 Merge pull request #2271 from aws-lumberyard-dev/Foundation/miterenc/ContributingUpdate Updating CONTRIBUTING.md commit bb52475ce8c37c29b35c81b379befe5a75db1de7 Author: Terry Michaels Date: Mon Jul 19 14:55:14 2021 -0500 Updated text to be more descriptive Signed-off-by: Terry Michaels commit 697dfad486c69beb5ef40cbf6478d6ccd8753cd7 Author: Terry Michaels Date: Mon Jul 19 14:27:24 2021 -0500 Fixed typo Signed-off-by: Terry Michaels commit 650e1ab44d86664b6deab7f6cebeda40c3e1361e Author: Terry Michaels Date: Mon Jul 19 14:19:46 2021 -0500 Updating CONTRIBUTING.md Signed-off-by: Terry Michaels commit d9cce28a5387c7f5b59041869086547905f1e345 Merge: e7f787572e 486ba58628 Author: Chris Galvan Date: Mon Jul 12 14:06:57 2021 -0500 Merge pull request #2096 from aws-lumberyard-dev/cgalvan/gitflow_210712_main Merged stabilization/2106 to main commit 486ba58628488c211a6140f26caa421c38be3f0f Merge: e7f787572e 7cfde884d9 Author: Chris Galvan Date: Mon Jul 12 11:12:41 2021 -0500 Merged stabilization/2106 to development; Resolved merge conflicts Signed-off-by: Chris Galvan commit e7f787572e805c413115265e5873fb2425e2f41b Author: Nicholas Lawson <70027408+lawsonamzn@users.noreply.github.com> Date: Tue Jul 6 08:03:35 2021 -0700 Updates licenses to APACHE-2.0 OR MIT (#1685) Not to be committed before 7/6/2021 Signed-off-by: lawsonamzn <70027408+lawsonamzn@users.noreply.github.com> commit 837e1c737059a819d01ef4ebed7fadae42248dd8 Merge: d30de01752 efcbe2c4a1 Author: Chris Galvan Date: Fri Jul 2 12:11:27 2021 -0500 Merge pull request #1764 from aws-lumberyard-dev/cgalvan/gitflow_210702 Merged stabilization/2106 to main commit efcbe2c4a1e909182dfbd5394f9322513ba91115 Merge: d30de01752 0c43493e29 Author: Chris Galvan Date: Fri Jul 2 10:20:42 2021 -0500 Merge remote-tracking branch 'upstream/stabilization/2106' into cgalvan/gitflow_210702 Signed-off-by: Chris Galvan commit d30de01752b7eaed1f16e2163b1a75c915f061eb Author: Alex Peterson <26804013+AMZN-alexpete@users.noreply.github.com> Date: Mon Jun 28 11:20:36 2021 -0700 Updating LFS config to new endpoint (#1624) Signed-off-by: AMZN-alexpete Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Signed-off-by: jackalbe <23512001+jackalbe@users.noreply.github.com> * fix for "warning C4100: 'outputValueTypeId': unreferenced formal" Signed-off-by: jackalbe <23512001+jackalbe@users.noreply.github.com> * Squashed commit of the following: commit dbd3526517bcb6553402cbc0af1f02e1f68e0707 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Mon Oct 4 14:30:56 2021 -0500 Increased scene manifest max size to size_t::max to match default json size limit Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit ea4a9ffd23feb3beeadc9bf6ca765c96980e9e6d Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Fri Oct 1 19:15:25 2021 -0500 Switch to querying cache location from Settings Registry Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 1c197996132625b8e26856fc92a30f9862d9dfdb Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Fri Oct 1 14:42:32 2021 -0500 Update to look in cache for generated manifest instead of source folder Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit cf3c32791fd71dc48066f9783c8859d386370b95 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Thu Sep 30 11:09:40 2021 -0500 Added generated manifest to dependency tracking. Updated unit tests Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 56cb0d27982e61c2cf123b765def8b5c9e21b021 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Wed Sep 29 15:51:52 2021 -0500 Moved manifest size const to header and used that in scene builder Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 7c8016a0ba6000090b29371f253b7906c9f3d141 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Wed Sep 29 15:21:06 2021 -0500 Add doc comment Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit 45fd5473f5ed6fe4fe93f8d629857852bcbe5e03 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Wed Sep 29 15:11:36 2021 -0500 Clean up code and add unit test Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> commit d0e610cad380e2278bc18552a4d1c71c64a5d339 Author: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Wed Sep 29 13:21:58 2021 -0500 Source dependency reporting WIP Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> # Conflicts: # Gems/SceneProcessing/Code/Source/SceneBuilder/SceneBuilderWorker.cpp (cherry picked from commit d6464dcee20ea4e6f2d40f0d3301f24be5f4dee6) Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> * Add comment on unit test globals, fix indentation Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> * Cleaned up scene_mesh_to_prefab.py Added comments Removed raise_error function Made mesh_group_name cleanup more comprehensive Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> * fix for "warning C4100: 'message': unreferenced formal" Signed-off-by: jackalbe <23512001+jackalbe@users.noreply.github.com> Co-authored-by: jackalbe <23512001+jackalbe@users.noreply.github.com> Co-authored-by: Guthrie Adams --- .../Editor/Scripts/scene_mesh_to_prefab.py | 162 ++++++++ AutomatedTesting/Gem/Code/enabled_gems.cmake | 3 +- .../FBXTestTexture.png | 3 + .../multiple_mesh_one_material.fbx | 3 + .../multiple_mesh_one_material.fbx.assetinfo | 8 + Code/Framework/AzCore/AzCore/Math/Uuid.cpp | 12 +- Code/Framework/AzCore/AzCore/Math/Uuid.h | 3 +- Code/Framework/AzCore/Tests/UUIDTests.cpp | 23 ++ .../AzFramework/Entity/BehaviorEntity.cpp | 2 + .../Application/ToolsApplication.cpp | 4 +- .../AzToolsFrameworkModule.cpp | 2 + .../Entity/EntityUtilityComponent.cpp | 351 ++++++++++++++++++ .../Entity/EntityUtilityComponent.h | 90 +++++ .../FocusMode/FocusModeInterface.h | 1 + .../AzToolsFramework/Prefab/PrefabLoader.cpp | 48 ++- .../AzToolsFramework/Prefab/PrefabLoader.h | 3 + .../Prefab/PrefabLoaderInterface.h | 1 - .../Prefab/PrefabLoaderScriptingBus.h | 45 +++ .../Prefab/PrefabSystemComponent.cpp | 20 +- .../Prefab/PrefabSystemComponent.h | 7 +- .../Prefab/PrefabSystemComponentInterface.h | 3 +- .../Prefab/PrefabSystemScriptingBus.h | 38 ++ .../Prefab/PrefabSystemScriptingHandler.cpp | 75 ++++ .../Prefab/PrefabSystemScriptingHandler.h | 39 ++ .../Procedural/ProceduralPrefabAsset.cpp | 144 +++++++ .../Prefab/Procedural/ProceduralPrefabAsset.h | 90 +++++ .../Prefab/ScriptingPrefabLoader.cpp | 37 ++ .../Prefab/ScriptingPrefabLoader.h | 42 +++ .../UI/Prefab/PrefabIntegrationManager.cpp | 73 ++++ .../UI/Prefab/PrefabIntegrationManager.h | 2 + .../aztoolsframework_files.cmake | 10 + .../Entity/EntityUtilityComponentTests.cpp | 252 +++++++++++++ .../Tests/Prefab/PrefabScriptingTests.cpp | 173 +++++++++ .../Prefab/ProceduralPrefabAssetTests.cpp | 135 +++++++ .../Tests/aztoolsframeworktests_files.cmake | 3 + .../AssetBuilder/AssetBuilderApplication.cpp | 2 + .../native/ui/AssetTreeFilterModel.cpp | 5 +- .../SceneCore/Containers/GraphObjectProxy.cpp | 2 +- .../SceneAPI/SceneCore/Containers/Scene.cpp | 11 + .../SceneAPI/SceneCore/Containers/Scene.h | 4 + .../SceneCore/Containers/SceneGraph.cpp | 2 +- .../SceneCore/Containers/SceneManifest.cpp | 3 +- .../SceneCore/Containers/SceneManifest.h | 2 + .../SceneCore/Events/AssetImportRequest.cpp | 27 +- .../SceneCore/Events/AssetImportRequest.h | 19 + .../Import/ManifestImportRequestHandler.cpp | 8 +- .../Import/ManifestImportRequestHandler.h | 1 + .../Behaviors/ScriptProcessorRuleBehavior.cpp | 119 +++--- .../Behaviors/ScriptProcessorRuleBehavior.h | 8 +- Gems/Prefab/PrefabBuilder/CMakeLists.txt | 9 +- .../PrefabBuilder/PrefabBuilderModule.cpp | 4 +- .../Prefab/PrefabBuilder/PrefabBuilderTests.h | 1 + .../PrefabBuilder/PrefabGroup/IPrefabGroup.h | 25 ++ .../PrefabGroup/PrefabBehaviorTests.cpp | 161 ++++++++ .../PrefabGroup/PrefabBehaviorTests.inl | 104 ++++++ .../PrefabBuilder/PrefabGroup/PrefabGroup.cpp | 136 +++++++ .../PrefabBuilder/PrefabGroup/PrefabGroup.h | 64 ++++ .../PrefabGroup/PrefabGroupBehavior.cpp | 251 +++++++++++++ .../PrefabGroup/PrefabGroupBehavior.h | 55 +++ .../PrefabGroup/PrefabGroupTests.cpp | 161 ++++++++ .../PrefabGroup/ProceduralAssetHandler.cpp | 185 +++++++++ .../PrefabGroup/ProceduralAssetHandler.h | 39 ++ .../Prefab/PrefabBuilder/PrefabGroupTests.cpp | 162 ++++++++ .../PrefabBuilder/prefabbuilder_files.cmake | 7 + .../prefabbuilder_tests_files.cmake | 3 + .../Editor/Scripts/scene_api/scene_data.py | 9 + .../SceneBuilder/SceneBuilderWorker.cpp | 96 +++++ .../Source/SceneBuilder/SceneBuilderWorker.h | 5 + .../Tests/SceneBuilder/SceneBuilderTests.cpp | 200 ++++++++++ 69 files changed, 3699 insertions(+), 98 deletions(-) create mode 100644 AutomatedTesting/Editor/Scripts/scene_mesh_to_prefab.py create mode 100644 AutomatedTesting/multiple_mesh_one_material/FBXTestTexture.png create mode 100644 AutomatedTesting/multiple_mesh_one_material/multiple_mesh_one_material.fbx create mode 100644 AutomatedTesting/multiple_mesh_one_material/multiple_mesh_one_material.fbx.assetinfo create mode 100644 Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EntityUtilityComponent.cpp create mode 100644 Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EntityUtilityComponent.h create mode 100644 Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoaderScriptingBus.h create mode 100644 Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemScriptingBus.h create mode 100644 Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemScriptingHandler.cpp create mode 100644 Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemScriptingHandler.h create mode 100644 Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Procedural/ProceduralPrefabAsset.cpp create mode 100644 Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Procedural/ProceduralPrefabAsset.h create mode 100644 Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/ScriptingPrefabLoader.cpp create mode 100644 Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/ScriptingPrefabLoader.h create mode 100644 Code/Framework/AzToolsFramework/Tests/Entity/EntityUtilityComponentTests.cpp create mode 100644 Code/Framework/AzToolsFramework/Tests/Prefab/PrefabScriptingTests.cpp create mode 100644 Code/Framework/AzToolsFramework/Tests/Prefab/ProceduralPrefabAssetTests.cpp create mode 100644 Gems/Prefab/PrefabBuilder/PrefabGroup/IPrefabGroup.h create mode 100644 Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabBehaviorTests.cpp create mode 100644 Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabBehaviorTests.inl create mode 100644 Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroup.cpp create mode 100644 Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroup.h create mode 100644 Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroupBehavior.cpp create mode 100644 Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroupBehavior.h create mode 100644 Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroupTests.cpp create mode 100644 Gems/Prefab/PrefabBuilder/PrefabGroup/ProceduralAssetHandler.cpp create mode 100644 Gems/Prefab/PrefabBuilder/PrefabGroup/ProceduralAssetHandler.h create mode 100644 Gems/Prefab/PrefabBuilder/PrefabGroupTests.cpp diff --git a/AutomatedTesting/Editor/Scripts/scene_mesh_to_prefab.py b/AutomatedTesting/Editor/Scripts/scene_mesh_to_prefab.py new file mode 100644 index 0000000000..d5a7acfe91 --- /dev/null +++ b/AutomatedTesting/Editor/Scripts/scene_mesh_to_prefab.py @@ -0,0 +1,162 @@ +# +# 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 +# +# +import os, traceback, binascii, sys, json, pathlib +import azlmbr.math +import azlmbr.bus + +# +# SceneAPI Processor +# + + +def log_exception_traceback(): + exc_type, exc_value, exc_tb = sys.exc_info() + data = traceback.format_exception(exc_type, exc_value, exc_tb) + print(str(data)) + +def get_mesh_node_names(sceneGraph): + import azlmbr.scene as sceneApi + import azlmbr.scene.graph + from scene_api import scene_data as sceneData + + meshDataList = [] + node = sceneGraph.get_root() + children = [] + paths = [] + + while node.IsValid(): + # store children to process after siblings + if sceneGraph.has_node_child(node): + children.append(sceneGraph.get_node_child(node)) + + nodeName = sceneData.SceneGraphName(sceneGraph.get_node_name(node)) + paths.append(nodeName.get_path()) + + # store any node that has mesh data content + nodeContent = sceneGraph.get_node_content(node) + if nodeContent.CastWithTypeName('MeshData'): + if sceneGraph.is_node_end_point(node) is False: + if (len(nodeName.get_path())): + meshDataList.append(sceneData.SceneGraphName(sceneGraph.get_node_name(node))) + + # advance to next node + if sceneGraph.has_node_sibling(node): + node = sceneGraph.get_node_sibling(node) + elif children: + node = children.pop() + else: + node = azlmbr.scene.graph.NodeIndex() + + return meshDataList, paths + +def update_manifest(scene): + import json + import uuid, os + import azlmbr.scene as sceneApi + import azlmbr.scene.graph + from scene_api import scene_data as sceneData + + graph = sceneData.SceneGraph(scene.graph) + # Get a list of all the mesh nodes, as well as all the nodes + mesh_name_list, all_node_paths = get_mesh_node_names(graph) + scene_manifest = sceneData.SceneManifest() + + clean_filename = scene.sourceFilename.replace('.', '_') + + # Compute the filename of the scene file + source_basepath = scene.watchFolder + source_relative_path = os.path.dirname(os.path.relpath(clean_filename, source_basepath)) + source_filename_only = os.path.basename(clean_filename) + + created_entities = [] + + # Loop every mesh node in the scene + for activeMeshIndex in range(len(mesh_name_list)): + mesh_name = mesh_name_list[activeMeshIndex] + mesh_path = mesh_name.get_path() + # Create a unique mesh group name using the filename + node name + mesh_group_name = '{}_{}'.format(source_filename_only, mesh_name.get_name()) + # Remove forbidden filename characters from the name since this will become a file on disk later + mesh_group_name = "".join(char for char in mesh_group_name if char not in "|<>:\"/?*\\") + # Add the MeshGroup to the manifest and give it a unique ID + mesh_group = scene_manifest.add_mesh_group(mesh_group_name) + mesh_group['id'] = '{' + str(uuid.uuid5(uuid.NAMESPACE_DNS, source_filename_only + mesh_path)) + '}' + # Set our current node as the only node that is included in this MeshGroup + scene_manifest.mesh_group_select_node(mesh_group, mesh_path) + + # Explicitly remove all other nodes to prevent implicit inclusions + for node in all_node_paths: + if node != mesh_path: + scene_manifest.mesh_group_unselect_node(mesh_group, node) + + # Create an editor entity + entity_id = azlmbr.entity.EntityUtilityBus(azlmbr.bus.Broadcast, "CreateEditorReadyEntity", mesh_group_name) + # Add an EditorMeshComponent to the entity + editor_mesh_component = azlmbr.entity.EntityUtilityBus(azlmbr.bus.Broadcast, "GetOrAddComponentByTypeName", entity_id, "AZ::Render::EditorMeshComponent") + # Set the ModelAsset assetHint to the relative path of the input asset + the name of the MeshGroup we just created + the azmodel extension + # The MeshGroup we created will be output as a product in the asset's path named mesh_group_name.azmodel + # The assetHint will be converted to an AssetId later during prefab loading + json_update = json.dumps({ + "Controller": { "Configuration": { "ModelAsset": { + "assetHint": os.path.join(source_relative_path, mesh_group_name) + ".azmodel" }}} + }); + # Apply the JSON above to the component we created + result = azlmbr.entity.EntityUtilityBus(azlmbr.bus.Broadcast, "UpdateComponentForEntity", entity_id, editor_mesh_component, json_update) + + if not result: + raise RuntimeError("UpdateComponentForEntity failed") + + # Keep track of the entity we set up, we'll add them all to the prefab we're creating later + created_entities.append(entity_id) + + # Create a prefab with all our entities + prefab_filename = source_filename_only + ".prefab" + created_template_id = azlmbr.prefab.PrefabSystemScriptingBus(azlmbr.bus.Broadcast, "CreatePrefab", created_entities, prefab_filename) + + if created_template_id == azlmbr.prefab.InvalidTemplateId: + raise RuntimeError("CreatePrefab {} failed".format(prefab_filename)) + + # Convert the prefab to a JSON string + output = azlmbr.prefab.PrefabLoaderScriptingBus(azlmbr.bus.Broadcast, "SaveTemplateToString", created_template_id) + + if output.IsSuccess(): + jsonString = output.GetValue() + uuid = azlmbr.math.Uuid_CreateRandom().ToString() + jsonResult = json.loads(jsonString) + # Add a PrefabGroup to the manifest and store the JSON on it + scene_manifest.add_prefab_group(source_filename_only, uuid, jsonResult) + else: + raise RuntimeError("SaveTemplateToString failed for template id {}, prefab {}".format(created_template_id, prefab_filename)) + + # Convert the manifest to a JSON string and return it + new_manifest = scene_manifest.export() + + return new_manifest + +sceneJobHandler = None + +def on_update_manifest(args): + try: + scene = args[0] + return update_manifest(scene) + except RuntimeError as err: + print (f'ERROR - {err}') + log_exception_traceback() + + global sceneJobHandler + sceneJobHandler = None + +# try to create SceneAPI handler for processing +try: + import azlmbr.scene as sceneApi + if (sceneJobHandler == None): + sceneJobHandler = sceneApi.ScriptBuildingNotificationBusHandler() + sceneJobHandler.connect() + sceneJobHandler.add_callback('OnUpdateManifest', on_update_manifest) +except: + sceneJobHandler = None diff --git a/AutomatedTesting/Gem/Code/enabled_gems.cmake b/AutomatedTesting/Gem/Code/enabled_gems.cmake index 0d281661b9..30740a489d 100644 --- a/AutomatedTesting/Gem/Code/enabled_gems.cmake +++ b/AutomatedTesting/Gem/Code/enabled_gems.cmake @@ -21,7 +21,6 @@ set(ENABLED_GEMS QtForPython PythonAssetBuilder Metastream - Camera EMotionFX AtomTressFX @@ -53,6 +52,6 @@ set(ENABLED_GEMS AWSCore AWSClientAuth AWSMetrics - + PrefabBuilder AudioSystem ) diff --git a/AutomatedTesting/multiple_mesh_one_material/FBXTestTexture.png b/AutomatedTesting/multiple_mesh_one_material/FBXTestTexture.png new file mode 100644 index 0000000000..4f52364cb3 --- /dev/null +++ b/AutomatedTesting/multiple_mesh_one_material/FBXTestTexture.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:827a63985273229050bf4f63030bcc666f045091fe81cf8157a9ca23b40074b6 +size 3214 diff --git a/AutomatedTesting/multiple_mesh_one_material/multiple_mesh_one_material.fbx b/AutomatedTesting/multiple_mesh_one_material/multiple_mesh_one_material.fbx new file mode 100644 index 0000000000..d9deb899f0 --- /dev/null +++ b/AutomatedTesting/multiple_mesh_one_material/multiple_mesh_one_material.fbx @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d8d24963e6e8765205bc79cbe2304fc39f1245ee75249e2834a71c96c3cab824 +size 22700 diff --git a/AutomatedTesting/multiple_mesh_one_material/multiple_mesh_one_material.fbx.assetinfo b/AutomatedTesting/multiple_mesh_one_material/multiple_mesh_one_material.fbx.assetinfo new file mode 100644 index 0000000000..fe18a16cb5 --- /dev/null +++ b/AutomatedTesting/multiple_mesh_one_material/multiple_mesh_one_material.fbx.assetinfo @@ -0,0 +1,8 @@ +{ + "values": [ + { + "$type": "ScriptProcessorRule", + "scriptFilename": "Editor/Scripts/scene_mesh_to_prefab.py" + } + ] +} \ No newline at end of file diff --git a/Code/Framework/AzCore/AzCore/Math/Uuid.cpp b/Code/Framework/AzCore/AzCore/Math/Uuid.cpp index d875e28a1e..410d235a37 100644 --- a/Code/Framework/AzCore/AzCore/Math/Uuid.cpp +++ b/Code/Framework/AzCore/AzCore/Math/Uuid.cpp @@ -135,18 +135,12 @@ namespace AZ { stringLength = strlen(uuidString); } - if (stringLength > MaxPermissiveStringSize) - { - if (!skipWarnings) - { - AZ_Warning("Math", false, "Can't create UUID from string length %zu over maximum %zu", stringLength, MaxPermissiveStringSize); - } - return Uuid::CreateNull(); - } + size_t newLength{ 0 }; char createString[MaxPermissiveStringSize]; - for (size_t curPos = 0; curPos < stringLength; ++curPos) + // Loop until we get to the end of the string OR stop once we've accumulated a full UUID string worth of data + for (size_t curPos = 0; curPos < stringLength && newLength < ValidUuidStringLength; ++curPos) { char curChar = uuidString[curPos]; switch (curChar) diff --git a/Code/Framework/AzCore/AzCore/Math/Uuid.h b/Code/Framework/AzCore/AzCore/Math/Uuid.h index ea920e45c8..6b77e7ec3e 100644 --- a/Code/Framework/AzCore/AzCore/Math/Uuid.h +++ b/Code/Framework/AzCore/AzCore/Math/Uuid.h @@ -42,8 +42,9 @@ namespace AZ //VER_AZ_RANDOM_CRC32 = 6, // 0 1 1 0 }; + static constexpr int ValidUuidStringLength = 32; /// Number of characters (data only, no extra formatting) in a valid UUID string static const size_t MaxStringBuffer = 39; /// 32 Uuid + 4 dashes + 2 brackets + 1 terminate - + Uuid() {} Uuid(const char* string, size_t stringLength = 0) { *this = CreateString(string, stringLength); } diff --git a/Code/Framework/AzCore/Tests/UUIDTests.cpp b/Code/Framework/AzCore/Tests/UUIDTests.cpp index a18dc33c4f..64b9048a62 100644 --- a/Code/Framework/AzCore/Tests/UUIDTests.cpp +++ b/Code/Framework/AzCore/Tests/UUIDTests.cpp @@ -247,4 +247,27 @@ namespace UnitTest Uuid right = Uuid::CreateStringPermissive(permissiveStr1); EXPECT_EQ(left, right); } + + TEST_F(UuidTests, CreateStringPermissive_StringWithExtraData_Succeeds) + { + const char uuidStr[] = "{34D44249-E599-4B30-811F-4215C2DEA269}"; + Uuid left = Uuid::CreateString(uuidStr); + + const char permissiveStr[] = "0x34D44249-0xE5994B30-0x811F4215-0xC2DEA269 Hello World"; + Uuid right = Uuid::CreateStringPermissive(permissiveStr); + EXPECT_EQ(left, right); + + } + + TEST_F(UuidTests, CreateStringPermissive_StringWithLotsOfExtraData_Succeeds) + { + const char uuidStr[] = "{34D44249-E599-4B30-811F-4215C2DEA269}"; + Uuid left = Uuid::CreateString(uuidStr); + + const char permissiveStr[] = "0x34D44249-0xE5994B30-0x811F4215-0xC2DEA269 Hello World this is a really long string " + "with lots of extra data to make sure we can parse a long string without failing as long as the uuid is in " + "the beginning of the string then we should succeed"; + Uuid right = Uuid::CreateStringPermissive(permissiveStr); + EXPECT_EQ(left, right); + } } diff --git a/Code/Framework/AzFramework/AzFramework/Entity/BehaviorEntity.cpp b/Code/Framework/AzFramework/AzFramework/Entity/BehaviorEntity.cpp index 725f71a3c9..5d82cf488a 100644 --- a/Code/Framework/AzFramework/AzFramework/Entity/BehaviorEntity.cpp +++ b/Code/Framework/AzFramework/AzFramework/Entity/BehaviorEntity.cpp @@ -133,6 +133,8 @@ namespace AzFramework behaviorContext->Class("Entity") ->Attribute(AZ::Script::Attributes::Storage, AZ::Script::Attributes::StorageType::Value) ->Attribute(AZ::Script::Attributes::ConstructorOverride, &Internal::BehaviorEntityScriptConstructor) + ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) + ->Attribute(AZ::Script::Attributes::Module, "entity") ->Constructor() ->Constructor() ->Constructor() diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.cpp index c54735fe91..fbd066ec6e 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.cpp @@ -69,6 +69,7 @@ #include #include #include +#include #include AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option") // 4251: 'QFileInfo::d_ptr': class 'QSharedDataPointer' needs to have dll-interface to be used by clients of class 'QFileInfo' @@ -271,7 +272,8 @@ namespace AzToolsFramework azrtti_typeid(), azrtti_typeid(), azrtti_typeid(), - azrtti_typeid() + azrtti_typeid(), + azrtti_typeid() }); return components; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AzToolsFrameworkModule.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AzToolsFrameworkModule.cpp index 05d67d9d71..d2a88df544 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AzToolsFrameworkModule.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AzToolsFrameworkModule.cpp @@ -53,6 +53,7 @@ #include #include #include +#include AZ_DEFINE_BUDGET(AzToolsFramework); @@ -71,6 +72,7 @@ namespace AzToolsFramework Components::EditorSelectionAccentSystemComponent::CreateDescriptor(), EditorEntityContextComponent::CreateDescriptor(), EditorEntityFixupComponent::CreateDescriptor(), + EntityUtilityComponent::CreateDescriptor(), ContainerEntitySystemComponent::CreateDescriptor(), FocusModeSystemComponent::CreateDescriptor(), SliceMetadataEntityContextComponent::CreateDescriptor(), diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EntityUtilityComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EntityUtilityComponent.cpp new file mode 100644 index 0000000000..7fb998f012 --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EntityUtilityComponent.cpp @@ -0,0 +1,351 @@ +/* + * 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 + +namespace AzToolsFramework +{ + void ComponentDetails::Reflect(AZ::ReflectContext* context) + { + if (auto serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Field("TypeInfo", &ComponentDetails::m_typeInfo) + ->Field("BaseClasses", &ComponentDetails::m_baseClasses); + + serializeContext->RegisterGenericType>(); + } + + if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) + { + behaviorContext->Class() + ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) + ->Attribute(AZ::Script::Attributes::Module, "entity") + ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All) + ->Property("TypeInfo", BehaviorValueProperty(&ComponentDetails::m_typeInfo)) + ->Property("BaseClasses", BehaviorValueProperty(&ComponentDetails::m_baseClasses)) + ->Method("__repr__", [](const ComponentDetails& obj) + { + std::ostringstream result; + bool first = true; + + for (const auto& baseClass : obj.m_baseClasses) + { + if (!first) + { + result << ", "; + } + + first = false; + result << baseClass.c_str(); + } + + return AZStd::string::format("%s, Base Classes: <%s>", obj.m_typeInfo.c_str(), result.str().c_str()); + }) + ->Attribute(AZ::Script::Attributes::Operator, AZ::Script::Attributes::OperatorType::ToString); + } + } + + AZ::EntityId EntityUtilityComponent::CreateEditorReadyEntity(const AZStd::string& entityName) + { + auto* newEntity = m_entityContext->CreateEntity(entityName.c_str()); + + if (!newEntity) + { + AZ_Error("EditorEntityUtility", false, "Failed to create new entity %s", entityName.c_str()); + return AZ::EntityId(); + } + + AzToolsFramework::EditorEntityContextRequestBus::Broadcast( + &AzToolsFramework::EditorEntityContextRequestBus::Events::AddRequiredComponents, *newEntity); + + newEntity->Init(); + auto newEntityId = newEntity->GetId(); + + m_createdEntities.emplace_back(newEntityId); + + return newEntityId; + } + + AZ::TypeId GetComponentTypeIdFromName(const AZStd::string& typeName) + { + // Try to create a TypeId first. We won't show any warnings if this fails as the input might be a class name instead + AZ::TypeId typeId = AZ::TypeId::CreateStringPermissive(typeName.data()); + + // If the typeId is null, try a lookup by class name + if (typeId.IsNull()) + { + AZ::SerializeContext* serializeContext = nullptr; + AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext); + + auto typeNameCrc = AZ::Crc32(typeName.data()); + auto typeUuidList = serializeContext->FindClassId(typeNameCrc); + + // TypeId is invalid or class name is invalid + if (typeUuidList.empty()) + { + AZ_Error("EntityUtilityComponent", false, "Provided type %s is either an invalid TypeId or does not match any class names", typeName.c_str()); + return AZ::TypeId::CreateNull(); + } + + typeId = typeUuidList[0]; + } + + return typeId; + } + + AZ::Component* FindComponentHelper(AZ::EntityId entityId, const AZ::TypeId& typeId, AZ::ComponentId componentId, bool createComponent = false) + { + AZ::Entity* entity = nullptr; + AZ::ComponentApplicationBus::BroadcastResult(entity, &AZ::ComponentApplicationBus::Events::FindEntity, entityId); + + if (!entity) + { + AZ_Error("EntityUtilityComponent", false, "Invalid entityId %s", entityId.ToString().c_str()); + return nullptr; + } + + AZ::Component* component = nullptr; + if (componentId != AZ::InvalidComponentId) + { + component = entity->FindComponent(componentId); + } + else + { + component = entity->FindComponent(typeId); + } + + if (!component && createComponent) + { + component = entity->CreateComponent(typeId); + } + + if (!component) + { + AZ_Error( + "EntityUtilityComponent", false, "Failed to find component (%s) on entity %s (%s)", + componentId != AZ::InvalidComponentId ? AZStd::to_string(componentId).c_str() + : typeId.ToString().c_str(), + entityId.ToString().c_str(), + entity->GetName().c_str()); + return nullptr; + } + + return component; + } + + AzFramework::BehaviorComponentId EntityUtilityComponent::GetOrAddComponentByTypeName(AZ::EntityId entityId, const AZStd::string& typeName) + { + AZ::TypeId typeId = GetComponentTypeIdFromName(typeName); + + if (typeId.IsNull()) + { + return AzFramework::BehaviorComponentId(AZ::InvalidComponentId); + } + + AZ::Component* component = FindComponentHelper(entityId, typeId, AZ::InvalidComponentId, true); + + return component ? AzFramework::BehaviorComponentId(component->GetId()) : + AzFramework::BehaviorComponentId(AZ::InvalidComponentId); + } + + bool EntityUtilityComponent::UpdateComponentForEntity(AZ::EntityId entityId, AzFramework::BehaviorComponentId componentId, const AZStd::string& json) + { + if (!componentId.IsValid()) + { + AZ_Error("EntityUtilityComponent", false, "Invalid componentId passed to UpdateComponentForEntity"); + return false; + } + + AZ::Component* component = FindComponentHelper(entityId, AZ::TypeId::CreateNull(), componentId); + + if (!component) + { + return false; + } + + using namespace AZ::JsonSerializationResult; + + AZ::JsonDeserializerSettings settings = AZ::JsonDeserializerSettings{}; + settings.m_reporting = []([[maybe_unused]] AZStd::string_view message, ResultCode result, AZStd::string_view) -> auto + { + if (result.GetProcessing() == Processing::Halted) + { + AZ_Error("EntityUtilityComponent", false, "JSON %s\n", message.data()); + } + else if (result.GetOutcome() > Outcomes::PartialDefaults) + { + AZ_Warning("EntityUtilityComponent", false, "JSON %s\n", message.data()); + } + return result; + }; + + rapidjson::Document doc; + doc.Parse(json.data(), json.size()); + ResultCode resultCode = AZ::JsonSerialization::Load(*component, doc, settings); + + return resultCode.GetProcessing() != Processing::Halted; + } + + AZStd::string EntityUtilityComponent::GetComponentDefaultJson(const AZStd::string& typeName) + { + AZ::TypeId typeId = GetComponentTypeIdFromName(typeName); + + if (typeId.IsNull()) + { + // GetComponentTypeIdFromName already does error handling + return ""; + } + + AZ::SerializeContext* serializeContext = nullptr; + AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext); + + const AZ::SerializeContext::ClassData* classData = serializeContext->FindClassData(typeId); + + if (!classData) + { + AZ_Error("EntityUtilityComponent", false, "Failed to find ClassData for typeId %s (%s)", typeId.ToString().c_str(), typeName.c_str()); + return ""; + } + + void* component = classData->m_factory->Create("Component"); + rapidjson::Document document; + AZ::JsonSerializerSettings settings; + settings.m_keepDefaults = true; + + auto resultCode = AZ::JsonSerialization::Store(document, document.GetAllocator(), component, nullptr, typeId, settings); + + // Clean up the allocated component ASAP, we don't need it anymore + classData->m_factory->Destroy(component); + + if (resultCode.GetProcessing() == AZ::JsonSerializationResult::Processing::Halted) + { + AZ_Error("EntityUtilityComponent", false, "Failed to serialize component to json (%s): %s", + typeName.c_str(), resultCode.ToString(typeName).c_str()) + return ""; + } + + AZStd::string jsonString; + AZ::Outcome outcome = AZ::JsonSerializationUtils::WriteJsonString(document, jsonString); + + if (!outcome.IsSuccess()) + { + AZ_Error("EntityUtilityComponent", false, "Failed to write component json to string: %s", outcome.GetError().c_str()); + return ""; + } + + return jsonString; + } + + AZStd::vector EntityUtilityComponent::FindMatchingComponents(const AZStd::string& searchTerm) + { + AZ::SerializeContext* serializeContext = nullptr; + AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext); + + if (m_typeInfo.empty()) + { + serializeContext->EnumerateDerived( + [this, serializeContext](const AZ::SerializeContext::ClassData* classData, const AZ::Uuid& /*typeId*/) + { + auto& typeInfo = m_typeInfo.emplace_back(classData->m_typeId, classData->m_name, AZStd::vector{}); + + serializeContext->EnumerateBase( + [&typeInfo](const AZ::SerializeContext::ClassData* classData, const AZ::Uuid&) + { + if (classData) + { + AZStd::get<2>(typeInfo).emplace_back(classData->m_name); + } + return true; + }, + classData->m_typeId); + + return true; + }); + } + + AZStd::vector matches; + + for (const auto& [typeId, typeName, baseClasses] : m_typeInfo) + { + if (AZStd::wildcard_match(searchTerm, typeName)) + { + ComponentDetails details; + details.m_typeInfo = AZStd::string::format("%s %s", typeId.ToString().c_str(), typeName.c_str()); + details.m_baseClasses = baseClasses; + + matches.emplace_back(AZStd::move(details)); + } + } + + return matches; + } + + void EntityUtilityComponent::ResetEntityContext() + { + for (AZ::EntityId entityId : m_createdEntities) + { + m_entityContext->DestroyEntityById(entityId); + } + + m_createdEntities.clear(); + m_entityContext->ResetContext(); + } + + void EntityUtilityComponent::Reflect(AZ::ReflectContext* context) + { + ComponentDetails::Reflect(context); + + if (auto* serializeContext = azrtti_cast(context)) + { + serializeContext->Class(); + } + + if (auto* behaviorContext = azrtti_cast(context)) + { + behaviorContext->ConstantProperty("InvalidComponentId", BehaviorConstant(AZ::InvalidComponentId)) + ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) + ->Attribute(AZ::Script::Attributes::Category, "Entity") + ->Attribute(AZ::Script::Attributes::Module, "entity"); + + behaviorContext->EBus("EntityUtilityBus") + ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) + ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All) + ->Attribute(AZ::Script::Attributes::Category, "Entity") + ->Attribute(AZ::Script::Attributes::Module, "entity") + ->Event("CreateEditorReadyEntity", &EntityUtilityBus::Events::CreateEditorReadyEntity) + ->Event("GetOrAddComponentByTypeName", &EntityUtilityBus::Events::GetOrAddComponentByTypeName) + ->Event("UpdateComponentForEntity", &EntityUtilityBus::Events::UpdateComponentForEntity) + ->Event("FindMatchingComponents", &EntityUtilityBus::Events::FindMatchingComponents) + ->Event("GetComponentDefaultJson", &EntityUtilityBus::Events::GetComponentDefaultJson) + ; + } + } + + void EntityUtilityComponent::Activate() + { + m_entityContext = AZStd::make_unique(UtilityEntityContextId); + m_entityContext->InitContext(); + EntityUtilityBus::Handler::BusConnect(); + } + + void EntityUtilityComponent::Deactivate() + { + EntityUtilityBus::Handler::BusDisconnect(); + m_entityContext = nullptr; + } +} diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EntityUtilityComponent.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EntityUtilityComponent.h new file mode 100644 index 0000000000..af7c353163 --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EntityUtilityComponent.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace AzToolsFramework +{ + struct ComponentDetails + { + AZ_TYPE_INFO(AzToolsFramework::ComponentDetails, "{107D8379-4AD4-4547-BEE1-184B120F23E9}"); + + static void Reflect(AZ::ReflectContext* context); + + AZStd::string m_typeInfo; + AZStd::vector m_baseClasses; + }; + + // This ebus is intended to provide behavior-context friendly APIs to create and manage entities + struct EntityUtilityTraits : AZ::EBusTraits + { + AZ_RTTI(AzToolsFramework::EntityUtilityTraits, "{A6305CAE-C825-43F9-A44D-E503910912AF}"); + + virtual ~EntityUtilityTraits() = default; + + static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; + static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; + + // Creates an entity with the default editor components attached and initializes the entity + virtual AZ::EntityId CreateEditorReadyEntity(const AZStd::string& entityName) = 0; + + virtual AzFramework::BehaviorComponentId GetOrAddComponentByTypeName(AZ::EntityId entity, const AZStd::string& typeName) = 0; + + virtual bool UpdateComponentForEntity(AZ::EntityId entity, AzFramework::BehaviorComponentId component, const AZStd::string& json) = 0; + + // Gets a JSON string containing describing the default serialization state of the specified component + virtual AZStd::string GetComponentDefaultJson(const AZStd::string& typeName) = 0; + + // Returns a list of matching component type names. Supports wildcard search terms + virtual AZStd::vector FindMatchingComponents(const AZStd::string& searchTerm) = 0; + + virtual void ResetEntityContext() = 0; + }; + + using EntityUtilityBus = AZ::EBus; + + struct EntityUtilityComponent : AZ::Component + , EntityUtilityBus::Handler + { + inline const static AZ::Uuid UtilityEntityContextId = AZ::Uuid("{9C277B88-E79E-4F8A-BAFF-A4C175BD565F}"); + + AZ_COMPONENT(EntityUtilityComponent, "{47205907-A0EA-4FFF-A620-04D20C04A379}"); + + AZ::EntityId CreateEditorReadyEntity(const AZStd::string& entityName) override; + AzFramework::BehaviorComponentId GetOrAddComponentByTypeName(AZ::EntityId entity, const AZStd::string& typeName) override; + bool UpdateComponentForEntity(AZ::EntityId entity, AzFramework::BehaviorComponentId component, const AZStd::string& json) override; + AZStd::string GetComponentDefaultJson(const AZStd::string& typeName) override; + AZStd::vector FindMatchingComponents(const AZStd::string& searchTerm) override; + void ResetEntityContext() override; + + static void Reflect(AZ::ReflectContext* context); + + protected: + void Activate() override; + void Deactivate() override; + + // Our own entity context. This API is intended mostly for use in Asset Builders where there is no editor context + // Additionally, an entity context is needed when using the Behavior Entity class + AZStd::unique_ptr m_entityContext; + + // TypeId, TypeName, Vector + AZStd::vector>> m_typeInfo; + + // Keep track of the entities we create so they can be reset + AZStd::vector m_createdEntities; + }; +}; // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeInterface.h index 75e3bab60f..5455e8d773 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeInterface.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeInterface.h @@ -10,6 +10,7 @@ #include #include +#include #include #include diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoader.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoader.cpp index 09439724cd..07bda45c8a 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoader.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoader.cpp @@ -50,17 +50,19 @@ namespace AzToolsFramework auto settingsRegistry = AZ::SettingsRegistry::Get(); AZ_Assert(settingsRegistry, "Settings registry is not set"); - + [[maybe_unused]] bool result = settingsRegistry->Get(m_projectPathWithOsSeparator.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectPath); AZ_Warning("Prefab", result, "Couldn't retrieve project root path"); m_projectPathWithSlashSeparator = AZ::IO::Path(m_projectPathWithOsSeparator.Native(), '/').MakePreferred(); AZ::Interface::Register(this); + m_scriptingPrefabLoader.Connect(this); } void PrefabLoader::UnregisterPrefabLoaderInterface() { + m_scriptingPrefabLoader.Disconnect(); AZ::Interface::Unregister(this); } @@ -568,7 +570,7 @@ namespace AzToolsFramework (pathStr.find_first_of(AZ_FILESYSTEM_INVALID_CHARACTERS) == AZStd::string::npos) && (pathStr.back() != '\\' && pathStr.back() != '/'); } - + AZ::IO::Path PrefabLoader::GetFullPath(AZ::IO::PathView path) { AZ::IO::Path pathWithOSSeparator = AZ::IO::Path(path).MakePreferred(); @@ -596,26 +598,38 @@ namespace AzToolsFramework { // The asset system provided us with a valid root folder and relative path, so return it. fullPath = AZ::IO::Path(rootFolder) / assetInfo.m_relativePath; + return fullPath; } else { - // If for some reason the Asset system couldn't provide a relative path, provide some fallback logic. - - // Check to see if the AssetProcessor is ready. If it *is* and we didn't get a path, print an error then follow - // the fallback logic. If it's *not* ready, we're probably either extremely early in a tool startup flow or inside - // a unit test, so just execute the fallback logic without an error. - [[maybe_unused]] bool assetProcessorReady = false; - AzFramework::AssetSystemRequestBus::BroadcastResult( - assetProcessorReady, &AzFramework::AssetSystemRequestBus::Events::AssetProcessorIsReady); - - AZ_Error( - "Prefab", !assetProcessorReady, "Full source path for '%.*s' could not be determined. Using fallback logic.", - AZ_STRING_ARG(path.Native())); - - // If a relative path was passed in, make it relative to the project root. - fullPath = AZ::IO::Path(m_projectPathWithOsSeparator).Append(pathWithOSSeparator); + // attempt to find the absolute from the Cache folder + AZStd::string assetRootFolder; + if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr) + { + settingsRegistry->Get(assetRootFolder, AZ::SettingsRegistryMergeUtils::FilePathKey_CacheRootFolder); + } + fullPath = AZ::IO::Path(assetRootFolder) / path; + if (fullPath.IsAbsolute() && AZ::IO::SystemFile::Exists(fullPath.c_str())) + { + return fullPath; + } } + // If for some reason the Asset system couldn't provide a relative path, provide some fallback logic. + + // Check to see if the AssetProcessor is ready. If it *is* and we didn't get a path, print an error then follow + // the fallback logic. If it's *not* ready, we're probably either extremely early in a tool startup flow or inside + // a unit test, so just execute the fallback logic without an error. + [[maybe_unused]] bool assetProcessorReady = false; + AzFramework::AssetSystemRequestBus::BroadcastResult( + assetProcessorReady, &AzFramework::AssetSystemRequestBus::Events::AssetProcessorIsReady); + + AZ_Error( + "Prefab", !assetProcessorReady, "Full source path for '%.*s' could not be determined. Using fallback logic.", + AZ_STRING_ARG(path.Native())); + + // If a relative path was passed in, make it relative to the project root. + fullPath = AZ::IO::Path(m_projectPathWithOsSeparator).Append(pathWithOSSeparator); return fullPath; } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoader.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoader.h index 15f201e027..da2c3f3161 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoader.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoader.h @@ -15,6 +15,7 @@ #include #include #include +#include namespace AZ { @@ -114,6 +115,7 @@ namespace AzToolsFramework void SetSaveAllPrefabsPreference(SaveAllPrefabsPreference saveAllPrefabsPreference) override; private: + /** * Copies the template dom provided and manipulates it into the proper format to be saved to disk. * @param templateRef The template whose dom we want to transform into the proper format to be saved to disk. @@ -177,6 +179,7 @@ namespace AzToolsFramework AZStd::optional> StoreTemplateIntoFileFormat(TemplateId templateId); PrefabSystemComponentInterface* m_prefabSystemComponentInterface = nullptr; + ScriptingPrefabLoader m_scriptingPrefabLoader; AZ::IO::Path m_projectPathWithOsSeparator; AZ::IO::Path m_projectPathWithSlashSeparator; }; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoaderInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoaderInterface.h index a428f8a7b9..b4586ad5bf 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoaderInterface.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoaderInterface.h @@ -99,7 +99,6 @@ namespace AzToolsFramework // Generates a new path static AZ::IO::Path GeneratePath(); }; - } // namespace Prefab } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoaderScriptingBus.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoaderScriptingBus.h new file mode 100644 index 0000000000..6e9d64b147 --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoaderScriptingBus.h @@ -0,0 +1,45 @@ +/* + * 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 +{ + namespace Prefab + { + // Ebus for script-friendly APIs for the prefab loader + struct PrefabLoaderScriptingTraits : AZ::EBusTraits + { + AZ_TYPE_INFO(PrefabLoaderScriptingTraits, "{C344B7D8-8299-48C9-8450-26E1332EA011}"); + + virtual ~PrefabLoaderScriptingTraits() = default; + + static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; + static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; + + /** + * Saves a Prefab Template into the provided output string. + * Converts Prefab Template form into .prefab form by collapsing nested Template info + * into a source path and patches. + * @param templateId Id of the template to be saved + * @return Will contain the serialized template json on success + */ + virtual AZ::Outcome SaveTemplateToString(TemplateId templateId) = 0; + }; + + using PrefabLoaderScriptingBus = AZ::EBus; + + } // namespace Prefab +} // namespace AzToolsFramework + diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp index 896e40d929..b1fdf9784d 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -36,12 +37,14 @@ namespace AzToolsFramework m_instanceToTemplatePropagator.RegisterInstanceToTemplateInterface(); m_prefabPublicHandler.RegisterPrefabPublicHandlerInterface(); m_prefabPublicRequestHandler.Connect(); + m_prefabSystemScriptingHandler.Connect(this); AZ::SystemTickBus::Handler::BusConnect(); } void PrefabSystemComponent::Deactivate() { AZ::SystemTickBus::Handler::BusDisconnect(); + m_prefabSystemScriptingHandler.Disconnect(); m_prefabPublicRequestHandler.Disconnect(); m_prefabPublicHandler.UnregisterPrefabPublicHandlerInterface(); m_instanceToTemplatePropagator.UnregisterInstanceToTemplateInterface(); @@ -58,13 +61,24 @@ namespace AzToolsFramework AzToolsFramework::Prefab::PrefabConversionUtils::EditorInfoRemover::Reflect(context); PrefabPublicRequestHandler::Reflect(context); PrefabLoader::Reflect(context); + PrefabSystemScriptingHandler::Reflect(context); - AZ::SerializeContext* serialize = azrtti_cast(context); - if (serialize) + if (AZ::SerializeContext* serialize = azrtti_cast(context)) { serialize->Class()->Version(1); } + if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) + { + + behaviorContext->EBus("PrefabLoaderScriptingBus") + ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) + ->Attribute(AZ::Script::Attributes::Module, "prefab") + ->Attribute(AZ::Script::Attributes::Category, "Prefab") + ->Event("SaveTemplateToString", &PrefabLoaderScriptingBus::Events::SaveTemplateToString); + ; + } + AZ::JsonRegistrationContext* jsonRegistration = azrtti_cast(context); if (jsonRegistration) { @@ -145,7 +159,7 @@ namespace AzToolsFramework newInstance->SetTemplateId(newTemplateId); } } - + void PrefabSystemComponent::PropagateTemplateChanges(TemplateId templateId, bool immediate, InstanceOptionalReference instanceToExclude) { UpdatePrefabInstances(templateId, immediate, instanceToExclude); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.h index f640eb2f1b..480bb83121 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.h @@ -27,6 +27,7 @@ #include #include #include +#include namespace AZ { @@ -219,7 +220,7 @@ namespace AzToolsFramework const AZStd::vector& entities, AZStd::vector>&& instancesToConsume, AZ::IO::PathView filePath, AZStd::unique_ptr containerEntity = nullptr, InstanceOptionalReference parent = AZStd::nullopt, bool shouldCreateLinks = true) override; - + PrefabDom& FindTemplateDom(TemplateId templateId) override; /** @@ -244,7 +245,7 @@ namespace AzToolsFramework private: AZ_DISABLE_COPY_MOVE(PrefabSystemComponent); - + /** * Builds a new Prefab Template out of entities and instances and returns the first instance comprised of * these entities and instances. @@ -412,6 +413,8 @@ namespace AzToolsFramework // Handler of the public Prefab requests. PrefabPublicRequestHandler m_prefabPublicRequestHandler; + + PrefabSystemScriptingHandler m_prefabSystemScriptingHandler; }; } // namespace Prefab } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponentInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponentInterface.h index 54ca951841..66d85ccee9 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponentInterface.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponentInterface.h @@ -78,8 +78,7 @@ namespace AzToolsFramework AZStd::unique_ptr containerEntity = nullptr, InstanceOptionalReference parent = AZStd::nullopt, bool shouldCreateLinks = true) = 0; }; - - + } // namespace Prefab } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemScriptingBus.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemScriptingBus.h new file mode 100644 index 0000000000..f83bd2c6f1 --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemScriptingBus.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 +#include +#include +#include +#include + +namespace AzToolsFramework +{ + namespace Prefab + { + // Bus that exposes a script-friendly interface to the PrefabSystemComponent + struct PrefabSystemScriptingEbusTraits : AZ::EBusTraits + { + using MutexType = AZ::NullMutex; + static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; + static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; + + virtual TemplateId CreatePrefabTemplate( + const AZStd::vector& entityIds, const AZStd::string& filePath) = 0; + }; + + using PrefabSystemScriptingBus = AZ::EBus; + + } // namespace Prefab +} // namespace AzToolsFramework + diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemScriptingHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemScriptingHandler.cpp new file mode 100644 index 0000000000..d34e2cfc92 --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemScriptingHandler.cpp @@ -0,0 +1,75 @@ +/* + * 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 +{ + void PrefabSystemScriptingHandler::Reflect(AZ::ReflectContext* context) + { + if (auto behaviorContext = azrtti_cast(context)) + { + behaviorContext->ConstantProperty("InvalidTemplateId", BehaviorConstant(InvalidTemplateId)) + ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) + ->Attribute(AZ::Script::Attributes::Module, "prefab") + ->Attribute(AZ::Script::Attributes::Category, "Prefab"); + + behaviorContext->EBus("PrefabSystemScriptingBus") + ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) + ->Attribute(AZ::Script::Attributes::Module, "prefab") + ->Attribute(AZ::Script::Attributes::Category, "Prefab") + ->Event("CreatePrefab", &PrefabSystemScriptingBus::Events::CreatePrefabTemplate); + } + } + + void PrefabSystemScriptingHandler::Connect(PrefabSystemComponentInterface* prefabSystemComponentInterface) + { + AZ_Assert(prefabSystemComponentInterface != nullptr, "prefabSystemComponentInterface must not be null"); + m_prefabSystemComponentInterface = prefabSystemComponentInterface; + PrefabSystemScriptingBus::Handler::BusConnect(); + } + + void PrefabSystemScriptingHandler::Disconnect() + { + PrefabSystemScriptingBus::Handler::BusDisconnect(); + } + + TemplateId PrefabSystemScriptingHandler::CreatePrefabTemplate(const AZStd::vector& entityIds, const AZStd::string& filePath) + { + AZStd::vector entities; + + for (const auto& entityId : entityIds) + { + AZ::Entity* entity = nullptr; + AZ::ComponentApplicationBus::BroadcastResult(entity, &AZ::ComponentApplicationBus::Events::FindEntity, entityId); + + AZ_Warning( + "PrefabSystemComponent", entity, "EntityId %s was not found and will not be added to the prefab", + entityId.ToString().c_str()); + + if (entity) + { + entities.push_back(entity); + } + } + + auto prefab = m_prefabSystemComponentInterface->CreatePrefab(entities, {}, AZ::IO::PathView(AZStd::string_view(filePath))); + + if (!prefab) + { + AZ_Error("PrefabSystemComponenent", false, "Failed to create prefab %s", filePath.c_str()); + return InvalidTemplateId; + } + + return prefab->GetTemplateId(); + } +} diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemScriptingHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemScriptingHandler.h new file mode 100644 index 0000000000..809690bf35 --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemScriptingHandler.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 + +namespace AzToolsFramework +{ + namespace Prefab + { + class PrefabSystemScriptingHandler + : PrefabSystemScriptingBus::Handler + { + public: + static void Reflect(AZ::ReflectContext* context); + + PrefabSystemScriptingHandler() = default; + + void Connect(PrefabSystemComponentInterface* prefabSystemComponentInterface); + void Disconnect(); + + private: + AZ_DISABLE_COPY(PrefabSystemScriptingHandler); + + ////////////////////////////////////////////////////////////////////////// + // PrefabSystemScriptingBus implementation + TemplateId CreatePrefabTemplate(const AZStd::vector& entityIds, const AZStd::string& filePath) override; + ////////////////////////////////////////////////////////////////////////// + + PrefabSystemComponentInterface* m_prefabSystemComponentInterface = nullptr; + }; + } // namespace Prefab +} // namespace AzToolsFramework + diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Procedural/ProceduralPrefabAsset.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Procedural/ProceduralPrefabAsset.cpp new file mode 100644 index 0000000000..465d83e94b --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Procedural/ProceduralPrefabAsset.cpp @@ -0,0 +1,144 @@ +/* + * 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 AZ::Prefab +{ + static constexpr const char s_useProceduralPrefabsKey[] = "/O3DE/Preferences/Prefabs/UseProceduralPrefabs"; + + // ProceduralPrefabAsset + + ProceduralPrefabAsset::ProceduralPrefabAsset(const AZ::Data::AssetId& assetId) + : AZ::Data::AssetData(assetId) + , m_templateId(AzToolsFramework::Prefab::InvalidTemplateId) + { + } + + void ProceduralPrefabAsset::Reflect(AZ::ReflectContext* context) + { + PrefabDomData::Reflect(context); + + if (auto* serializeContext = azrtti_cast(context); serializeContext != nullptr) + { + serializeContext->Class() + ->Version(1) + ->Field("Template Name", &ProceduralPrefabAsset::m_templateName) + ->Field("Template ID", &ProceduralPrefabAsset::m_templateId); + } + } + + const AZStd::string& ProceduralPrefabAsset::GetTemplateName() const + { + return m_templateName; + } + + void ProceduralPrefabAsset::SetTemplateName(AZStd::string templateName) + { + m_templateName = AZStd::move(templateName); + } + + AzToolsFramework::Prefab::TemplateId ProceduralPrefabAsset::GetTemplateId() const + { + return m_templateId; + } + + void ProceduralPrefabAsset::SetTemplateId(AzToolsFramework::Prefab::TemplateId templateId) + { + m_templateId = templateId; + } + + bool ProceduralPrefabAsset::UseProceduralPrefabs() + { + bool useProceduralPrefabs = false; + bool result = AZ::SettingsRegistry::Get()->GetObject(useProceduralPrefabs, s_useProceduralPrefabsKey); + return result && useProceduralPrefabs; + } + + // PrefabDomData + + void PrefabDomData::Reflect(AZ::ReflectContext* context) + { + if (auto* jsonContext = azrtti_cast(context)) + { + jsonContext->Serializer()->HandlesType(); + } + + AZ::SerializeContext* serializeContext = azrtti_cast(context); + if (serializeContext) + { + serializeContext->Class() + ->Version(1); + } + } + + void PrefabDomData::CopyValue(const rapidjson::Value& inputValue) + { + m_prefabDom.CopyFrom(inputValue, m_prefabDom.GetAllocator()); + } + + const AzToolsFramework::Prefab::PrefabDom& PrefabDomData::GetValue() const + { + return m_prefabDom; + } + + // PrefabDomDataJsonSerializer + + AZ::JsonSerializationResult::Result PrefabDomDataJsonSerializer::Load( + void* outputValue, + [[maybe_unused]] const AZ::Uuid& outputValueTypeId, + const rapidjson::Value& inputValue, + AZ::JsonDeserializerContext& context) + { + AZ_Assert(outputValueTypeId == azrtti_typeid(), + "PrefabDomDataJsonSerializer Load against output typeID that was not PrefabDomData"); + AZ_Assert(outputValue, "PrefabDomDataJsonSerializer Load against null output"); + + namespace JSR = AZ::JsonSerializationResult; + JSR::ResultCode result(JSR::Tasks::ReadField); + + if (inputValue.IsObject() == false) + { + result.Combine(context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Missing, "Missing object")); + return context.Report(result, "Prefab should be an object."); + } + + if (inputValue.MemberCount() < 1) + { + result.Combine(context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Missing, "Missing members")); + return context.Report(result, "Prefab should have multiple members."); + } + + auto* outputVariable = reinterpret_cast(outputValue); + outputVariable->CopyValue(inputValue); + return context.Report(result, "Loaded procedural prefab"); + } + + AZ::JsonSerializationResult::Result PrefabDomDataJsonSerializer::Store( + rapidjson::Value& outputValue, + const void* inputValue, + [[maybe_unused]] const void* defaultValue, + [[maybe_unused]] const AZ::Uuid& valueTypeId, + AZ::JsonSerializerContext& context) + { + AZ_Assert(inputValue, "Input value for PrefabDomDataJsonSerializer can't be null."); + AZ_Assert(azrtti_typeid() == valueTypeId, + "Unable to Serialize because the provided type is not PrefabGroup::PrefabDomData."); + + const PrefabDomData* prefabDomData = reinterpret_cast(inputValue); + + namespace JSR = AZ::JsonSerializationResult; + JSR::ResultCode result(JSR::Tasks::WriteValue); + outputValue.SetObject(); + outputValue.CopyFrom(prefabDomData->GetValue(), context.GetJsonAllocator()); + return context.Report(result, "Stored procedural prefab"); + } +} diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Procedural/ProceduralPrefabAsset.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Procedural/ProceduralPrefabAsset.h new file mode 100644 index 0000000000..dd57389b7b --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Procedural/ProceduralPrefabAsset.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ +#pragma once + +#include +#include +#include +#include + +namespace AZ::Prefab +{ + //! A wrapper around the JSON DOM type so that the assets can read in and write out + //! JSON directly since Prefabs are JSON serialized entity-component data + class PrefabDomData final + { + public: + AZ_RTTI(PrefabDomData, "{C73A3360-D772-4D41-9118-A039BF9340C1}"); + AZ_CLASS_ALLOCATOR(PrefabDomData, AZ::SystemAllocator, 0); + + PrefabDomData() = default; + ~PrefabDomData() = default; + + static void Reflect(AZ::ReflectContext* context); + + void CopyValue(const rapidjson::Value& inputValue); + const AzToolsFramework::Prefab::PrefabDom& GetValue() const; + + private: + AzToolsFramework::Prefab::PrefabDom m_prefabDom; + }; + + //! Registered to help read/write JSON for the PrefabDomData::m_prefabDom + class PrefabDomDataJsonSerializer final + : public AZ::BaseJsonSerializer + { + public: + AZ_RTTI(PrefabDomDataJsonSerializer, "{9FC48652-A00B-4EFA-8FD9-345A8E625439}", BaseJsonSerializer); + AZ_CLASS_ALLOCATOR(PrefabDomDataJsonSerializer, AZ::SystemAllocator, 0); + + ~PrefabDomDataJsonSerializer() override = default; + + AZ::JsonSerializationResult::Result Load( + void* outputValue, + const AZ::Uuid& outputValueTypeId, + const rapidjson::Value& inputValue, + AZ::JsonDeserializerContext& context) override; + + AZ::JsonSerializationResult::Result Store( + rapidjson::Value& outputValue, + const void* inputValue, + const void* defaultValue, + const AZ::Uuid& valueTypeId, + AZ::JsonSerializerContext& context) override; + }; + + //! An asset type to register templates into the Prefab system so that they + //! can instantiate like Authored Prefabs + class ProceduralPrefabAsset + : public AZ::Data::AssetData + { + public: + AZ_CLASS_ALLOCATOR(ProceduralPrefabAsset, AZ::SystemAllocator, 0); + AZ_RTTI(ProceduralPrefabAsset, "{9B7C8459-471E-4EAD-A363-7990CC4065A9}", AZ::Data::AssetData); + + static bool UseProceduralPrefabs(); + + ProceduralPrefabAsset(const AZ::Data::AssetId& assetId = AZ::Data::AssetId()); + ~ProceduralPrefabAsset() override = default; + ProceduralPrefabAsset(const ProceduralPrefabAsset& rhs) = delete; + ProceduralPrefabAsset& operator=(const ProceduralPrefabAsset& rhs) = delete; + + const AZStd::string& GetTemplateName() const; + void SetTemplateName(AZStd::string templateName); + + AzToolsFramework::Prefab::TemplateId GetTemplateId() const; + void SetTemplateId(AzToolsFramework::Prefab::TemplateId templateId); + + static void Reflect(AZ::ReflectContext* context); + + private: + AZStd::string m_templateName; + AzToolsFramework::Prefab::TemplateId m_templateId; + }; + +} diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/ScriptingPrefabLoader.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/ScriptingPrefabLoader.cpp new file mode 100644 index 0000000000..bee8a8bc3b --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/ScriptingPrefabLoader.cpp @@ -0,0 +1,37 @@ +/* + * 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 + +namespace AzToolsFramework::Prefab +{ + void ScriptingPrefabLoader::Connect(PrefabLoaderInterface* prefabLoaderInterface) + { + AZ_Assert(prefabLoaderInterface, "prefabLoaderInterface must not be null"); + + m_prefabLoaderInterface = prefabLoaderInterface; + PrefabLoaderScriptingBus::Handler::BusConnect(); + } + + void ScriptingPrefabLoader::Disconnect() + { + PrefabLoaderScriptingBus::Handler::BusDisconnect(); + } + + AZ::Outcome ScriptingPrefabLoader::SaveTemplateToString(TemplateId templateId) + { + AZStd::string json; + + if (m_prefabLoaderInterface->SaveTemplateToString(templateId, json)) + { + return AZ::Success(json); + } + + return AZ::Failure(); + } +} // namespace AzToolsFramework::Prefab diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/ScriptingPrefabLoader.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/ScriptingPrefabLoader.h new file mode 100644 index 0000000000..ec6219909e --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/ScriptingPrefabLoader.h @@ -0,0 +1,42 @@ +/* + * 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 +{ + namespace Prefab + { + /** + * The Scripting Prefab Loader handles scripting-friendly API requests for the prefab loader + */ + class ScriptingPrefabLoader + : private PrefabLoaderScriptingBus::Handler + { + public: + AZ_CLASS_ALLOCATOR(ScriptingPrefabLoader, AZ::SystemAllocator, 0); + AZ_RTTI(ScriptingPrefabLoader, "{ABC3C989-4D4F-41E7-B25B-B0FEF97177E6}"); + + void Connect(PrefabLoaderInterface* prefabLoaderInterface); + void Disconnect(); + + private: + + ////////////////////////////////////////////////////////////////////////// + // PrefabLoaderRequestBus implementation + AZ::Outcome SaveTemplateToString(TemplateId templateId) override; + ////////////////////////////////////////////////////////////////////////// + + PrefabLoaderInterface* m_prefabLoaderInterface = nullptr; + }; + } // namespace Prefab +} // namespace AzToolsFramework + diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp index 07dcd2ab20..d4b227c4ff 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -25,6 +26,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -218,6 +222,16 @@ namespace AzToolsFramework instantiateAction, &QAction::triggered, instantiateAction, [] { ContextMenu_InstantiatePrefab(); }); } + // Instantiate Procedural Prefab + if (AZ::Prefab::ProceduralPrefabAsset::UseProceduralPrefabs()) + { + QAction* action = menu->addAction(QObject::tr("Instantiate Procedural Prefab...")); + action->setToolTip(QObject::tr("Instantiates a procedural prefab file in a prefab.")); + + QObject::connect( + action, &QAction::triggered, action, [] { ContextMenu_InstantiateProceduralPrefab(); }); + } + menu->addSeparator(); bool itemWasShown = false; @@ -435,6 +449,38 @@ namespace AzToolsFramework } } + void PrefabIntegrationManager::ContextMenu_InstantiateProceduralPrefab() + { + AZStd::string prefabAssetPath; + bool hasUserForProceduralPrefabAsset = QueryUserForProceduralPrefabAsset(prefabAssetPath); + + if (hasUserForProceduralPrefabAsset) + { + AZ::EntityId parentId; + AZ::Vector3 position = AZ::Vector3::CreateZero(); + + EntityIdList selectedEntities; + ToolsApplicationRequestBus::BroadcastResult(selectedEntities, &ToolsApplicationRequests::GetSelectedEntities); + if (selectedEntities.size() == 1) + { + parentId = selectedEntities.front(); + AZ::TransformBus::EventResult(position, parentId, &AZ::TransformInterface::GetWorldTranslation); + } + else + { + // otherwise return since it needs to be inside an authored prefab + return; + } + + // Instantiating from context menu always puts the instance at the root level + auto createPrefabOutcome = s_prefabPublicInterface->InstantiatePrefab(prefabAssetPath, parentId, position); + if (!createPrefabOutcome.IsSuccess()) + { + WarnUserOfError("Prefab Instantiation Error", createPrefabOutcome.GetError()); + } + } + } + void PrefabIntegrationManager::ContextMenu_EditPrefab(AZ::EntityId containerEntity) { s_prefabFocusInterface->FocusOnOwningPrefab(containerEntity); @@ -690,6 +736,33 @@ namespace AzToolsFramework return true; } + bool PrefabIntegrationManager::QueryUserForProceduralPrefabAsset(AZStd::string& outPrefabAssetPath) + { + using namespace AzToolsFramework; + auto selection = AssetBrowser::AssetSelectionModel::AssetTypeSelection(azrtti_typeid()); + EditorRequests::Bus::Broadcast(&AzToolsFramework::EditorRequests::BrowseForAssets, selection); + + if (!selection.IsValid()) + { + return false; + } + + auto product = azrtti_cast(selection.GetResult()); + if (product == nullptr) + { + return false; + } + + outPrefabAssetPath = product->GetRelativePath(); + + auto asset = AZ::Data::AssetManager::Instance().GetAsset( + product->GetAssetId(), + azrtti_typeid(), + AZ::Data::AssetLoadBehavior::Default); + + return asset.BlockUntilLoadComplete() != AZ::Data::AssetData::AssetStatus::Error; + } + void PrefabIntegrationManager::WarnUserOfError(AZStd::string_view title, AZStd::string_view message) { QWidget* activeWindow = QApplication::activeWindow(); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.h index 44e30b2013..696c05991c 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.h @@ -91,6 +91,7 @@ namespace AzToolsFramework // Context menu item handlers static void ContextMenu_CreatePrefab(AzToolsFramework::EntityIdList selectedEntities); static void ContextMenu_InstantiatePrefab(); + static void ContextMenu_InstantiateProceduralPrefab(); static void ContextMenu_EditPrefab(AZ::EntityId containerEntity); static void ContextMenu_SavePrefab(AZ::EntityId containerEntity); static void ContextMenu_DeleteSelected(); @@ -101,6 +102,7 @@ namespace AzToolsFramework const AZStd::string& suggestedName, const char* initialTargetDirectory, AZ::u32 prefabUserSettingsId, QWidget* activeWindow, AZStd::string& outPrefabName, AZStd::string& outPrefabFilePath); static bool QueryUserForPrefabFilePath(AZStd::string& outPrefabFilePath); + static bool QueryUserForProceduralPrefabAsset(AZStd::string& outPrefabAssetPath); static void WarnUserOfError(AZStd::string_view title, AZStd::string_view message); // Path and filename generation diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake b/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake index 0866b225c1..ff7fe58f1e 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake @@ -152,6 +152,8 @@ set(FILES Entity/SliceEditorEntityOwnershipService.h Entity/SliceEditorEntityOwnershipService.cpp Entity/SliceEditorEntityOwnershipServiceBus.h + Entity/EntityUtilityComponent.h + Entity/EntityUtilityComponent.cpp Fingerprinting/TypeFingerprinter.h Fingerprinting/TypeFingerprinter.cpp FocusMode/FocusModeInterface.h @@ -646,9 +648,15 @@ set(FILES Prefab/PrefabLoader.h Prefab/PrefabLoader.cpp Prefab/PrefabLoaderInterface.h + Prefab/PrefabLoaderScriptingBus.h + Prefab/ScriptingPrefabLoader.h + Prefab/ScriptingPrefabLoader.cpp Prefab/PrefabSystemComponent.h Prefab/PrefabSystemComponent.cpp Prefab/PrefabSystemComponentInterface.h + Prefab/PrefabSystemScriptingBus.h + Prefab/PrefabSystemScriptingHandler.h + Prefab/PrefabSystemScriptingHandler.cpp Prefab/Instance/Instance.h Prefab/Instance/Instance.cpp Prefab/Instance/InstanceSerializer.h @@ -671,6 +679,8 @@ set(FILES Prefab/Instance/TemplateInstanceMapperInterface.h Prefab/Link/Link.h Prefab/Link/Link.cpp + Prefab/Procedural/ProceduralPrefabAsset.h + Prefab/Procedural/ProceduralPrefabAsset.cpp Prefab/PrefabPublicHandler.h Prefab/PrefabPublicHandler.cpp Prefab/PrefabPublicInterface.h diff --git a/Code/Framework/AzToolsFramework/Tests/Entity/EntityUtilityComponentTests.cpp b/Code/Framework/AzToolsFramework/Tests/Entity/EntityUtilityComponentTests.cpp new file mode 100644 index 0000000000..fce44d4833 --- /dev/null +++ b/Code/Framework/AzToolsFramework/Tests/Entity/EntityUtilityComponentTests.cpp @@ -0,0 +1,252 @@ +/* + * 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 + +namespace UnitTest +{ + // Global variables for communicating between Lua test code and C++ + AZ::EntityId g_globalEntityId = AZ::EntityId{}; + AZStd::string g_globalString = ""; + AzFramework::BehaviorComponentId g_globalComponentId = {}; + AZStd::vector g_globalComponentDetails = {}; + bool g_globalBool = false; + + class EntityUtilityComponentTests + : public ToolsApplicationFixture + { + void InitProperties() + { + AZ::ComponentApplicationRequests* componentApplicationRequests = AZ::Interface::Get(); + + ASSERT_NE(componentApplicationRequests, nullptr); + + auto behaviorContext = componentApplicationRequests->GetBehaviorContext(); + + ASSERT_NE(behaviorContext, nullptr); + + behaviorContext->Property("g_globalEntityId", BehaviorValueProperty(&g_globalEntityId)); + behaviorContext->Property("g_globalString", BehaviorValueProperty(&g_globalString)); + behaviorContext->Property("g_globalComponentId", BehaviorValueProperty(&g_globalComponentId)); + behaviorContext->Property("g_globalBool", BehaviorValueProperty(&g_globalBool)); + behaviorContext->Property("g_globalComponentDetails", BehaviorValueProperty(&g_globalComponentDetails)); + + g_globalEntityId = AZ::EntityId{}; + g_globalString = AZStd::string{}; + g_globalComponentId = AzFramework::BehaviorComponentId{}; + g_globalBool = false; + g_globalComponentDetails = AZStd::vector{}; + } + + void SetUpEditorFixtureImpl() override + { + InitProperties(); + } + + void TearDownEditorFixtureImpl() override + { + g_globalString.set_capacity(0); // Free all memory + g_globalComponentDetails.set_capacity(0); + } + }; + + TEST_F(EntityUtilityComponentTests, CreateEntity) + { + AZ::ScriptContext sc; + auto behaviorContext = AZ::Interface::Get()->GetBehaviorContext(); + + sc.BindTo(behaviorContext); + sc.Execute(R"LUA( + g_globalEntityId = EntityUtilityBus.Broadcast.CreateEditorReadyEntity("test") + my_entity = Entity(g_globalEntityId) + g_globalString = my_entity:GetName() + )LUA"); + + EXPECT_NE(g_globalEntityId, AZ::EntityId{}); + EXPECT_STREQ(g_globalString.c_str(), "test"); + + AZ::Entity* entity = AZ::Interface::Get()->FindEntity(g_globalEntityId); + + ASSERT_NE(entity, nullptr); + + // Test cleaning up, make sure the entity is destroyed + AzToolsFramework::EntityUtilityBus::Broadcast(&AzToolsFramework::EntityUtilityBus::Events::ResetEntityContext); + + entity = AZ::Interface::Get()->FindEntity(g_globalEntityId); + + ASSERT_EQ(entity, nullptr); + } + + TEST_F(EntityUtilityComponentTests, CreateEntityEmptyName) + { + AZ::ScriptContext sc; + auto behaviorContext = AZ::Interface::Get()->GetBehaviorContext(); + + sc.BindTo(behaviorContext); + sc.Execute(R"LUA( + g_globalEntityId = EntityUtilityBus.Broadcast.CreateEditorReadyEntity("") + )LUA"); + + EXPECT_NE(g_globalEntityId, AZ::EntityId{}); + + AZ::Entity* entity = AZ::Interface::Get()->FindEntity(g_globalEntityId); + + ASSERT_NE(entity, nullptr); + } + + TEST_F(EntityUtilityComponentTests, FindComponent) + { + AZ::ScriptContext sc; + auto behaviorContext = AZ::Interface::Get()->GetBehaviorContext(); + + sc.BindTo(behaviorContext); + sc.Execute(R"LUA( + ent_id = EntityUtilityBus.Broadcast.CreateEditorReadyEntity("test") + g_globalComponentId = EntityUtilityBus.Broadcast.GetOrAddComponentByTypeName(ent_id, "27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0 TransformComponent") + )LUA"); + + EXPECT_TRUE(g_globalComponentId.IsValid()); + } + + TEST_F(EntityUtilityComponentTests, InvalidComponentName) + { + AZ::ScriptContext sc; + auto behaviorContext = AZ::Interface::Get()->GetBehaviorContext(); + + sc.BindTo(behaviorContext); + AZ_TEST_START_TRACE_SUPPRESSION; + sc.Execute(R"LUA( + ent_id = EntityUtilityBus.Broadcast.CreateEditorReadyEntity("test") + g_globalComponentId = EntityUtilityBus.Broadcast.GetOrAddComponentByTypeName(ent_id, "ThisIsNotAComponent-Error") + )LUA"); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); + EXPECT_FALSE(g_globalComponentId.IsValid()); + } + + TEST_F(EntityUtilityComponentTests, InvalidComponentId) + { + AZ::ScriptContext sc; + auto behaviorContext = AZ::Interface::Get()->GetBehaviorContext(); + + sc.BindTo(behaviorContext); + AZ_TEST_START_TRACE_SUPPRESSION; + sc.Execute(R"LUA( + ent_id = EntityUtilityBus.Broadcast.CreateEditorReadyEntity("test") + g_globalComponentId = EntityUtilityBus.Broadcast.GetOrAddComponentByTypeName(ent_id, "{1234-hello-world-this-is-not-an-id}") + )LUA"); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); // Should get 1 error stating the type id is not valid + EXPECT_FALSE(g_globalComponentId.IsValid()); + } + + TEST_F(EntityUtilityComponentTests, CreateComponent) + { + AZ::ScriptContext sc; + auto behaviorContext = AZ::Interface::Get()->GetBehaviorContext(); + + sc.BindTo(behaviorContext); + sc.Execute(R"LUA( + ent_id = EntityUtilityBus.Broadcast.CreateEditorReadyEntity("test") + g_globalComponentId = EntityUtilityBus.Broadcast.GetOrAddComponentByTypeName(ent_id, "ScriptEditorComponent") + )LUA"); + + EXPECT_TRUE(g_globalComponentId.IsValid()); + } + + TEST_F(EntityUtilityComponentTests, UpdateComponent) + { + AZ::ScriptContext sc; + auto behaviorContext = AZ::Interface::Get()->GetBehaviorContext(); + + sc.BindTo(behaviorContext); + sc.Execute(R"LUA( + g_globalEntityId = EntityUtilityBus.Broadcast.CreateEditorReadyEntity("test") + comp_id = EntityUtilityBus.Broadcast.GetOrAddComponentByTypeName(g_globalEntityId, "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent") + json_update = [[ + { + "Transform Data": { "Rotate": [0.0, 0.1, 180.0] } + } + ]] + g_globalBool = EntityUtilityBus.Broadcast.UpdateComponentForEntity(g_globalEntityId, comp_id, json_update); + )LUA"); + + EXPECT_TRUE(g_globalBool); + EXPECT_NE(g_globalEntityId, AZ::EntityId(AZ::EntityId::InvalidEntityId)); + + AZ::Entity* entity = AZ::Interface::Get()->FindEntity(g_globalEntityId); + + auto* transformComponent = entity->FindComponent(); + + ASSERT_NE(transformComponent, nullptr); + + AZ::Vector3 localRotation = transformComponent->GetLocalRotationQuaternion().GetEulerDegrees(); + + EXPECT_EQ(localRotation, AZ::Vector3(.0f, 0.1f, 180.0f)); + } + + TEST_F(EntityUtilityComponentTests, GetComponentJson) + { + AZ::ScriptContext sc; + auto behaviorContext = AZ::Interface::Get()->GetBehaviorContext(); + + sc.BindTo(behaviorContext); + sc.Execute(R"LUA( + g_globalString = EntityUtilityBus.Broadcast.GetComponentDefaultJson("ScriptEditorComponent") + )LUA"); + + EXPECT_STRNE(g_globalString.c_str(), ""); + } + + TEST_F(EntityUtilityComponentTests, GetComponentJsonDoesNotExist) + { + AZ::ScriptContext sc; + auto behaviorContext = AZ::Interface::Get()->GetBehaviorContext(); + + sc.BindTo(behaviorContext); + AZ_TEST_START_TRACE_SUPPRESSION; + sc.Execute(R"LUA( + g_globalString = EntityUtilityBus.Broadcast.GetComponentDefaultJson("404") + )LUA"); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); // 1 error: Failed to find component id for type name 404 + + EXPECT_STREQ(g_globalString.c_str(), ""); + } + + TEST_F(EntityUtilityComponentTests, SearchComponents) + { + AZ::ScriptContext sc; + auto behaviorContext = AZ::Interface::Get()->GetBehaviorContext(); + + sc.BindTo(behaviorContext); + sc.Execute(R"LUA( + g_globalComponentDetails = EntityUtilityBus.Broadcast.FindMatchingComponents("Transform*") + )LUA"); + + // There should be 2 transform components + EXPECT_EQ(g_globalComponentDetails.size(), 2); + } + + TEST_F(EntityUtilityComponentTests, SearchComponentsNotFound) + { + AZ::ScriptContext sc; + auto behaviorContext = AZ::Interface::Get()->GetBehaviorContext(); + + sc.BindTo(behaviorContext); + sc.Execute(R"LUA( + g_globalComponentDetails = EntityUtilityBus.Broadcast.FindMatchingComponents("404") + )LUA"); + + EXPECT_EQ(g_globalComponentDetails.size(), 0); + } +} diff --git a/Code/Framework/AzToolsFramework/Tests/Prefab/PrefabScriptingTests.cpp b/Code/Framework/AzToolsFramework/Tests/Prefab/PrefabScriptingTests.cpp new file mode 100644 index 0000000000..7e61c50629 --- /dev/null +++ b/Code/Framework/AzToolsFramework/Tests/Prefab/PrefabScriptingTests.cpp @@ -0,0 +1,173 @@ +/* + * 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 UnitTest +{ + TemplateId g_globalTemplateId = {}; + AZStd::string g_globalPrefabString = ""; + + class PrefabScriptingTest : public PrefabTestFixture + { + void InitProperties() const + { + AZ::ComponentApplicationRequests* componentApplicationRequests = AZ::Interface::Get(); + + ASSERT_NE(componentApplicationRequests, nullptr); + + auto behaviorContext = componentApplicationRequests->GetBehaviorContext(); + + ASSERT_NE(behaviorContext, nullptr); + + behaviorContext->Property("g_globalTemplateId", BehaviorValueProperty(&g_globalTemplateId)); + behaviorContext->Property("g_globalPrefabString", BehaviorValueProperty(&g_globalPrefabString)); + + g_globalTemplateId = TemplateId{}; + g_globalPrefabString = AZStd::string{}; + } + + void SetUpEditorFixtureImpl() override + { + InitProperties(); + } + + void TearDownEditorFixtureImpl() override + { + g_globalPrefabString.set_capacity(0); // Free all memory + } + }; + + TEST_F(PrefabScriptingTest, PrefabScripting_CreatePrefab) + { + AZ::ScriptContext sc; + auto behaviorContext = AZ::Interface::Get()->GetBehaviorContext(); + + sc.BindTo(behaviorContext); + sc.Execute(R"LUA( + my_id = EntityUtilityBus.Broadcast.CreateEditorReadyEntity("test") + entities = vector_EntityId() + entities:push_back(my_id) + g_globalTemplateId = PrefabSystemScriptingBus.Broadcast.CreatePrefab(entities, "test.prefab") + )LUA"); + + EXPECT_NE(g_globalTemplateId, TemplateId{}); + + auto prefabSystemComponentInterface = AZ::Interface::Get(); + + ASSERT_NE(prefabSystemComponentInterface, nullptr); + + TemplateReference templateRef = prefabSystemComponentInterface->FindTemplate(g_globalTemplateId); + + EXPECT_TRUE(templateRef); + } + + TEST_F(PrefabScriptingTest, PrefabScripting_CreatePrefab_NoEntities) + { + AZ::ScriptContext sc; + auto behaviorContext = AZ::Interface::Get()->GetBehaviorContext(); + + sc.BindTo(behaviorContext); + sc.Execute(R"LUA( + my_id = EntityUtilityBus.Broadcast.CreateEditorReadyEntity("test") + entities = vector_EntityId() + g_globalTemplateId = PrefabSystemScriptingBus.Broadcast.CreatePrefab(entities, "test.prefab") + )LUA"); + + EXPECT_NE(g_globalTemplateId, TemplateId{}); + + auto prefabSystemComponentInterface = AZ::Interface::Get(); + + ASSERT_NE(prefabSystemComponentInterface, nullptr); + + TemplateReference templateRef = prefabSystemComponentInterface->FindTemplate(g_globalTemplateId); + + EXPECT_TRUE(templateRef); + } + + TEST_F(PrefabScriptingTest, PrefabScripting_CreatePrefab_NoPath) + { + AZ::ScriptContext sc; + auto behaviorContext = AZ::Interface::Get()->GetBehaviorContext(); + + sc.BindTo(behaviorContext); + AZ_TEST_START_TRACE_SUPPRESSION; + sc.Execute(R"LUA( + my_id = EntityUtilityBus.Broadcast.CreateEditorReadyEntity("test") + entities = vector_EntityId() + template_id = PrefabSystemScriptingBus.Broadcast.CreatePrefab(entities, "") + )LUA"); + /* + error: PrefabSystemComponent::CreateTemplateFromInstance - Attempted to create a prefab template from an instance without a source file path. Unable to proceed. + error: Failed to create a Template associated with file path during CreatePrefab. + error: Failed to create prefab + */ + AZ_TEST_STOP_TRACE_SUPPRESSION(3); + } + + TEST_F(PrefabScriptingTest, PrefabScripting_SaveToString) + { + AZ::ScriptContext sc; + auto behaviorContext = AZ::Interface::Get()->GetBehaviorContext(); + + sc.BindTo(behaviorContext); + sc.Execute(R"LUA( + my_id = EntityUtilityBus.Broadcast.CreateEditorReadyEntity("test") + entities = vector_EntityId() + entities:push_back(my_id) + template_id = PrefabSystemScriptingBus.Broadcast.CreatePrefab(entities, "test.prefab") + my_result = PrefabLoaderScriptingBus.Broadcast.SaveTemplateToString(template_id) + + if my_result:IsSuccess() then + g_globalPrefabString = my_result:GetValue() + end + )LUA"); + + auto prefabSystemComponentInterface = AZ::Interface::Get(); + prefabSystemComponentInterface->RemoveAllTemplates(); + + EXPECT_STRNE(g_globalPrefabString.c_str(), ""); + TemplateId templateFromString = AZ::Interface::Get()->LoadTemplateFromString(g_globalPrefabString); + + EXPECT_NE(templateFromString, InvalidTemplateId); + + // Create another entity for comparison purposes + AZ::EntityId entityId; + AzToolsFramework::EntityUtilityBus::BroadcastResult( + entityId, &AzToolsFramework::EntityUtilityBus::Events::CreateEditorReadyEntity, "test"); + + AZ::Entity* testEntity = AZ::Interface::Get()->FindEntity(entityId); + + // Instantiate the prefab we saved + AZStd::unique_ptr instance = prefabSystemComponentInterface->InstantiatePrefab(templateFromString); + + EXPECT_NE(instance, nullptr); + + AZStd::vector loadedEntities; + + // Get the entities from the instance + instance->GetConstEntities( + [&loadedEntities](const AZ::Entity& entity) + { + loadedEntities.push_back(&entity); + return true; + }); + + // Make sure the instance has an entity with the same number of components as our test entity + EXPECT_EQ(loadedEntities.size(), 1); + EXPECT_EQ(loadedEntities[0]->GetComponents().size(), testEntity->GetComponents().size()); + + g_globalPrefabString.set_capacity(0); // Free all memory + } + +} diff --git a/Code/Framework/AzToolsFramework/Tests/Prefab/ProceduralPrefabAssetTests.cpp b/Code/Framework/AzToolsFramework/Tests/Prefab/ProceduralPrefabAssetTests.cpp new file mode 100644 index 0000000000..0b9e73bbac --- /dev/null +++ b/Code/Framework/AzToolsFramework/Tests/Prefab/ProceduralPrefabAssetTests.cpp @@ -0,0 +1,135 @@ +/* + * 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 + +namespace UnitTest +{ + class ProceduralPrefabAssetTest + : public PrefabTestFixture + { + void SetUpEditorFixtureImpl() override + { + AZ::ComponentApplicationRequests* componentApplicationRequests = AZ::Interface::Get(); + ASSERT_NE(componentApplicationRequests, nullptr); + + auto* behaviorContext = componentApplicationRequests->GetBehaviorContext(); + ASSERT_NE(behaviorContext, nullptr); + + auto* jsonRegistrationContext = componentApplicationRequests->GetJsonRegistrationContext(); + ASSERT_NE(jsonRegistrationContext, nullptr); + + auto* serializeContext = componentApplicationRequests->GetSerializeContext(); + ASSERT_NE(serializeContext, nullptr); + + AZ::Prefab::ProceduralPrefabAsset::Reflect(serializeContext); + AZ::Prefab::ProceduralPrefabAsset::Reflect(behaviorContext); + AZ::Prefab::ProceduralPrefabAsset::Reflect(jsonRegistrationContext); + } + + void TearDownEditorFixtureImpl() override + { + AZ::ComponentApplicationRequests* componentApplicationRequests = AZ::Interface::Get(); + componentApplicationRequests->GetJsonRegistrationContext()->EnableRemoveReflection(); + AZ::Prefab::ProceduralPrefabAsset::Reflect(componentApplicationRequests->GetJsonRegistrationContext()); + } + }; + + TEST_F(ProceduralPrefabAssetTest, ReflectContext_AccessMethods_Works) + { + AZ::ComponentApplicationRequests* componentApplicationRequests = AZ::Interface::Get(); + + auto* serializeContext = componentApplicationRequests->GetSerializeContext(); + EXPECT_TRUE(!serializeContext->CreateAny(azrtti_typeid()).empty()); + EXPECT_TRUE(!serializeContext->CreateAny(azrtti_typeid()).empty()); + + auto* jsonRegistrationContext = componentApplicationRequests->GetJsonRegistrationContext(); + EXPECT_TRUE(jsonRegistrationContext->GetSerializerForSerializerType(azrtti_typeid())); + } + + TEST_F(ProceduralPrefabAssetTest, ProceduralPrefabAsset_AccessMethods_Works) + { + const auto templateId = TemplateId(1); + const auto prefabString = "fake.prefab"; + + AZ::Prefab::ProceduralPrefabAsset asset{}; + asset.SetTemplateId(templateId); + EXPECT_EQ(asset.GetTemplateId(), templateId); + + asset.SetTemplateName(prefabString); + EXPECT_EQ(asset.GetTemplateName(), prefabString); + } + + TEST_F(ProceduralPrefabAssetTest, PrefabDomData_AccessMethods_Works) + { + AzToolsFramework::Prefab::PrefabDom dom; + dom.SetObject(); + dom.AddMember("boolValue", true, dom.GetAllocator()); + + AZ::Prefab::PrefabDomData prefabDomData; + prefabDomData.CopyValue(dom); + + const AzToolsFramework::Prefab::PrefabDom& result = prefabDomData.GetValue(); + EXPECT_TRUE(result.HasMember("boolValue")); + EXPECT_TRUE(result.FindMember("boolValue")->value.GetBool()); + } + + TEST_F(ProceduralPrefabAssetTest, PrefabDomDataJsonSerializer_Load_Works) + { + AZ::Prefab::PrefabDomData prefabDomData; + + AzToolsFramework::Prefab::PrefabDom dom; + dom.SetObject(); + dom.AddMember("member", "value", dom.GetAllocator()); + + AZ::Prefab::PrefabDomDataJsonSerializer prefabDomDataJsonSerializer; + AZ::JsonDeserializerSettings settings; + settings.m_reporting = [](auto, auto, auto) + { + AZ::JsonSerializationResult::ResultCode result(AZ::JsonSerializationResult::Tasks::ReadField); + return result; + }; + AZ::JsonDeserializerContext context{ settings }; + + auto result = prefabDomDataJsonSerializer.Load(&prefabDomData, azrtti_typeid(prefabDomData), dom, context); + EXPECT_EQ(result.GetResultCode().GetOutcome(), AZ::JsonSerializationResult::Outcomes::DefaultsUsed); + EXPECT_TRUE(prefabDomData.GetValue().HasMember("member")); + EXPECT_STREQ(prefabDomData.GetValue().FindMember("member")->value.GetString(), "value"); + } + + TEST_F(ProceduralPrefabAssetTest, PrefabDomDataJsonSerializer_Store_Works) + { + AzToolsFramework::Prefab::PrefabDom dom; + dom.SetObject(); + dom.AddMember("member", "value", dom.GetAllocator()); + + AZ::Prefab::PrefabDomData prefabDomData; + prefabDomData.CopyValue(dom); + + AZ::Prefab::PrefabDomDataJsonSerializer prefabDomDataJsonSerializer; + AzToolsFramework::Prefab::PrefabDom outputValue; + AZ::JsonSerializerSettings settings; + settings.m_reporting = [](auto, auto, auto) + { + AZ::JsonSerializationResult::ResultCode result(AZ::JsonSerializationResult::Tasks::WriteValue); + return result; + }; + AZ::JsonSerializerContext context{ settings, outputValue.GetAllocator() }; + auto result = prefabDomDataJsonSerializer.Store(outputValue, &prefabDomData, nullptr, azrtti_typeid(prefabDomData), context); + EXPECT_EQ(result.GetResultCode().GetOutcome(), AZ::JsonSerializationResult::Outcomes::DefaultsUsed); + EXPECT_TRUE(outputValue.HasMember("member")); + EXPECT_STREQ(outputValue.FindMember("member")->value.GetString(), "value"); + } +} diff --git a/Code/Framework/AzToolsFramework/Tests/aztoolsframeworktests_files.cmake b/Code/Framework/AzToolsFramework/Tests/aztoolsframeworktests_files.cmake index 11ea5a0b38..87d522c4a6 100644 --- a/Code/Framework/AzToolsFramework/Tests/aztoolsframeworktests_files.cmake +++ b/Code/Framework/AzToolsFramework/Tests/aztoolsframeworktests_files.cmake @@ -27,6 +27,7 @@ set(FILES Entity/EditorEntityHelpersTests.cpp Entity/EditorEntitySearchComponentTests.cpp Entity/EditorEntitySelectionTests.cpp + Entity/EntityUtilityComponentTests.cpp EntityIdQLabelTests.cpp EntityInspectorTests.cpp EntityOwnershipService/EntityOwnershipServiceTestFixture.cpp @@ -91,6 +92,8 @@ set(FILES Prefab/SpawnableSortEntitiesTestFixture.cpp Prefab/SpawnableSortEntitiesTestFixture.h Prefab/SpawnableSortEntitiesTests.cpp + Prefab/PrefabScriptingTests.cpp + Prefab/ProceduralPrefabAssetTests.cpp PropertyIntCtrlCommonTests.h PropertyIntSliderCtrlTests.cpp PropertyIntSpinCtrlTests.cpp diff --git a/Code/Tools/AssetProcessor/AssetBuilder/AssetBuilderApplication.cpp b/Code/Tools/AssetProcessor/AssetBuilder/AssetBuilderApplication.cpp index 9c9c517020..da2edcc51f 100644 --- a/Code/Tools/AssetProcessor/AssetBuilder/AssetBuilderApplication.cpp +++ b/Code/Tools/AssetProcessor/AssetBuilder/AssetBuilderApplication.cpp @@ -36,6 +36,7 @@ #include #include #include +#include namespace AssetBuilder { @@ -80,6 +81,7 @@ AZ::ComponentTypeList AssetBuilderApplication::GetRequiredSystemComponents() con azrtti_typeid(), azrtti_typeid(), azrtti_typeid(), + azrtti_typeid(), }); return components; diff --git a/Code/Tools/AssetProcessor/native/ui/AssetTreeFilterModel.cpp b/Code/Tools/AssetProcessor/native/ui/AssetTreeFilterModel.cpp index 50a472c44d..c33fc59d97 100644 --- a/Code/Tools/AssetProcessor/native/ui/AssetTreeFilterModel.cpp +++ b/Code/Tools/AssetProcessor/native/ui/AssetTreeFilterModel.cpp @@ -62,7 +62,10 @@ namespace AssetProcessor { searchStr = searchStr.mid(0, subidPos); } - AZ::Uuid filterAsUuid = AZ::Uuid::CreateStringPermissive(searchStr.toUtf8(), 0); + + // Cap the string to some reasonable length, we don't want to try parsing an entire book + size_t cappedStringLength = searchStr.length() > 60 ? 60 : searchStr.length(); + AZ::Uuid filterAsUuid = AZ::Uuid::CreateStringPermissive(searchStr.toUtf8(), cappedStringLength); return DescendantMatchesFilter(*assetTreeItem, filter, filterAsUuid); } diff --git a/Code/Tools/SceneAPI/SceneCore/Containers/GraphObjectProxy.cpp b/Code/Tools/SceneAPI/SceneCore/Containers/GraphObjectProxy.cpp index ab260eba4d..1efec493c1 100644 --- a/Code/Tools/SceneAPI/SceneCore/Containers/GraphObjectProxy.cpp +++ b/Code/Tools/SceneAPI/SceneCore/Containers/GraphObjectProxy.cpp @@ -194,8 +194,8 @@ namespace AZ if (baseClass) { m_behaviorClass = behaviorClass; + return true; } - return true; } return false; } diff --git a/Code/Tools/SceneAPI/SceneCore/Containers/Scene.cpp b/Code/Tools/SceneAPI/SceneCore/Containers/Scene.cpp index b6c7eef90f..31b74405d2 100644 --- a/Code/Tools/SceneAPI/SceneCore/Containers/Scene.cpp +++ b/Code/Tools/SceneAPI/SceneCore/Containers/Scene.cpp @@ -47,6 +47,16 @@ namespace AZ return m_sourceGuid; } + void Scene::SetWatchFolder(const AZStd::string& watchFolder) + { + m_watchFolder = watchFolder; + } + + const AZStd::string& Scene::GetWatchFolder() const + { + return m_watchFolder; + } + void Scene::SetManifestFilename(const AZStd::string& name) { m_manifestFilename = name; @@ -111,6 +121,7 @@ namespace AZ ->Property("sourceGuid", BehaviorValueGetter(&Scene::m_sourceGuid), nullptr) ->Property("graph", BehaviorValueGetter(&Scene::m_graph), nullptr) ->Property("manifest", BehaviorValueGetter(&Scene::m_manifest), nullptr) + ->Property("watchFolder", BehaviorValueGetter(&Scene::m_watchFolder), nullptr) ->Constant("SceneOrientation_YUp", BehaviorConstant(SceneOrientation::YUp)) ->Constant("SceneOrientation_ZUp", BehaviorConstant(SceneOrientation::ZUp)) ->Constant("SceneOrientation_XUp", BehaviorConstant(SceneOrientation::XUp)) diff --git a/Code/Tools/SceneAPI/SceneCore/Containers/Scene.h b/Code/Tools/SceneAPI/SceneCore/Containers/Scene.h index 49320c4592..0185c23178 100644 --- a/Code/Tools/SceneAPI/SceneCore/Containers/Scene.h +++ b/Code/Tools/SceneAPI/SceneCore/Containers/Scene.h @@ -34,6 +34,9 @@ namespace AZ const AZStd::string& GetSourceFilename() const; const Uuid& GetSourceGuid() const; + void SetWatchFolder(const AZStd::string& watchFolder); + const AZStd::string& GetWatchFolder() const; + void SetManifestFilename(const AZStd::string& name); void SetManifestFilename(AZStd::string&& name); const AZStd::string& GetManifestFilename() const; @@ -59,6 +62,7 @@ namespace AZ AZStd::string m_name; AZStd::string m_manifestFilename; AZStd::string m_sourceFilename; + AZStd::string m_watchFolder; Uuid m_sourceGuid; SceneGraph m_graph; SceneManifest m_manifest; diff --git a/Code/Tools/SceneAPI/SceneCore/Containers/SceneGraph.cpp b/Code/Tools/SceneAPI/SceneCore/Containers/SceneGraph.cpp index cae0dc45d5..3d00dccec3 100644 --- a/Code/Tools/SceneAPI/SceneCore/Containers/SceneGraph.cpp +++ b/Code/Tools/SceneAPI/SceneCore/Containers/SceneGraph.cpp @@ -97,7 +97,7 @@ namespace AZ GraphObjectProxy* proxy = aznew GraphObjectProxy(graphObject); return proxy; } - return nullptr; + return aznew GraphObjectProxy(nullptr); }); } } diff --git a/Code/Tools/SceneAPI/SceneCore/Containers/SceneManifest.cpp b/Code/Tools/SceneAPI/SceneCore/Containers/SceneManifest.cpp index 664c5f1272..8e3f06b4cb 100644 --- a/Code/Tools/SceneAPI/SceneCore/Containers/SceneManifest.cpp +++ b/Code/Tools/SceneAPI/SceneCore/Containers/SceneManifest.cpp @@ -36,8 +36,7 @@ namespace AZ { namespace Containers { - //! Protects from allocating too much memory. The choice of a 5MB threshold is arbitrary. - const size_t MaxSceneManifestFileSizeInBytes = 5 * 1024 * 1024; + const char ErrorWindowName[] = "SceneManifest"; diff --git a/Code/Tools/SceneAPI/SceneCore/Containers/SceneManifest.h b/Code/Tools/SceneAPI/SceneCore/Containers/SceneManifest.h index 4386d4fd8c..f3838096ad 100644 --- a/Code/Tools/SceneAPI/SceneCore/Containers/SceneManifest.h +++ b/Code/Tools/SceneAPI/SceneCore/Containers/SceneManifest.h @@ -41,6 +41,8 @@ namespace AZ AZ_RTTI(SceneManifest, "{9274AD17-3212-4651-9F3B-7DCCB080E467}"); + static constexpr size_t MaxSceneManifestFileSizeInBytes = AZStd::numeric_limits::max(); + virtual ~SceneManifest(); static AZStd::shared_ptr SceneManifestConstDataConverter( diff --git a/Code/Tools/SceneAPI/SceneCore/Events/AssetImportRequest.cpp b/Code/Tools/SceneAPI/SceneCore/Events/AssetImportRequest.cpp index a182e4f02d..c98fa63916 100644 --- a/Code/Tools/SceneAPI/SceneCore/Events/AssetImportRequest.cpp +++ b/Code/Tools/SceneAPI/SceneCore/Events/AssetImportRequest.cpp @@ -6,6 +6,7 @@ * */ +#include #include #include #include @@ -73,6 +74,10 @@ namespace AZ { } + void AssetImportRequest::GetGeneratedManifestExtension(AZStd::string& /*result*/) + { + } + void AssetImportRequest::GetSupportedFileExtensions(AZStd::unordered_set& /*extensions*/) { } @@ -103,14 +108,34 @@ namespace AZ AZ_UNUSED(value); } + void AssetImportRequest::GetManifestDependencyPaths(AZStd::vector&) + { + } + AZStd::shared_ptr AssetImportRequest::LoadSceneFromVerifiedPath(const AZStd::string& assetFilePath, const Uuid& sourceGuid, - RequestingApplication requester, const Uuid& loadingComponentUuid) + RequestingApplication requester, const Uuid& loadingComponentUuid) { AZStd::string sceneName; AzFramework::StringFunc::Path::GetFileName(assetFilePath.c_str(), sceneName); AZStd::shared_ptr scene = AZStd::make_shared(AZStd::move(sceneName)); AZ_Assert(scene, "Unable to create new scene for asset importing."); + Data::AssetInfo assetInfo; + AZStd::string watchFolder; + bool result = false; + AzToolsFramework::AssetSystemRequestBus::BroadcastResult(result, &AzToolsFramework::AssetSystemRequestBus::Events::GetSourceInfoBySourceUUID, sourceGuid, assetInfo, watchFolder); + + if (result) + { + scene->SetWatchFolder(watchFolder); + } + else + { + AZ_Error( + "AssetImportRequest", false, "Failed to get watch folder for source %s", + sourceGuid.ToString().c_str()); + } + // Unique pointer, will deactivate and clean up once going out of scope. SceneCore::EntityConstructor::EntityPointer loaders = SceneCore::EntityConstructor::BuildEntity("Scene Loading", loadingComponentUuid); diff --git a/Code/Tools/SceneAPI/SceneCore/Events/AssetImportRequest.h b/Code/Tools/SceneAPI/SceneCore/Events/AssetImportRequest.h index 8b6e119f99..a2446c5ff8 100644 --- a/Code/Tools/SceneAPI/SceneCore/Events/AssetImportRequest.h +++ b/Code/Tools/SceneAPI/SceneCore/Events/AssetImportRequest.h @@ -78,6 +78,8 @@ namespace AZ virtual void GetSupportedFileExtensions(AZStd::unordered_set& extensions); //! Gets the file extension for the manifest. virtual void GetManifestExtension(AZStd::string& result); + //! Gets the file extension for the generated manifest. + virtual void GetGeneratedManifestExtension(AZStd::string& result); //! Before asset loading starts this is called to allow for any required initialization. virtual ProcessingResult PrepareForAssetLoading(Containers::Scene& scene, RequestingApplication requester); @@ -97,6 +99,23 @@ namespace AZ // Get scene processing project setting: UseCustomNormal virtual void AreCustomNormalsUsed(bool & value); + /*! + Optional method for reporting source file dependencies that may exist in the scene manifest + Paths is a vector of JSON Path strings, relative to the IRule object + For example, the following path: /scriptFilename + Would match with this manifest: + + { + "values": [ + { + "$type": "Test", + "scriptFilename": "file.py" + } + ] + } + */ + virtual void GetManifestDependencyPaths(AZStd::vector& paths); + //! Utility function to load an asset and manifest from file by using the EBus functions above. //! @param assetFilePath The absolute path to the source file (not the manifest). //! @param sourceGuid The guid assigned to the source file (not the manifest). diff --git a/Code/Tools/SceneAPI/SceneCore/Import/ManifestImportRequestHandler.cpp b/Code/Tools/SceneAPI/SceneCore/Import/ManifestImportRequestHandler.cpp index 39045da5b9..f7d3f073b9 100644 --- a/Code/Tools/SceneAPI/SceneCore/Import/ManifestImportRequestHandler.cpp +++ b/Code/Tools/SceneAPI/SceneCore/Import/ManifestImportRequestHandler.cpp @@ -56,8 +56,14 @@ namespace AZ result = s_extension; } + void ManifestImportRequestHandler::GetGeneratedManifestExtension(AZStd::string& result) + { + result = s_extension; + result.append(s_generated); + } + Events::LoadingResult ManifestImportRequestHandler::LoadAsset(Containers::Scene& scene, const AZStd::string& path, - const Uuid& /*guid*/, RequestingApplication /*requester*/) + const Uuid& /*guid*/, RequestingApplication /*requester*/) { AZStd::string manifestPath = path + s_extension; diff --git a/Code/Tools/SceneAPI/SceneCore/Import/ManifestImportRequestHandler.h b/Code/Tools/SceneAPI/SceneCore/Import/ManifestImportRequestHandler.h index 7659f6908f..00c802cb6b 100644 --- a/Code/Tools/SceneAPI/SceneCore/Import/ManifestImportRequestHandler.h +++ b/Code/Tools/SceneAPI/SceneCore/Import/ManifestImportRequestHandler.h @@ -31,6 +31,7 @@ namespace AZ static void Reflect(ReflectContext* context); void GetManifestExtension(AZStd::string& result) override; + void GetGeneratedManifestExtension(AZStd::string& result) override; Events::LoadingResult LoadAsset(Containers::Scene& scene, const AZStd::string& path, const Uuid& guid, RequestingApplication requester) override; diff --git a/Code/Tools/SceneAPI/SceneData/Behaviors/ScriptProcessorRuleBehavior.cpp b/Code/Tools/SceneAPI/SceneData/Behaviors/ScriptProcessorRuleBehavior.cpp index 2e6503d195..8d413b6038 100644 --- a/Code/Tools/SceneAPI/SceneData/Behaviors/ScriptProcessorRuleBehavior.cpp +++ b/Code/Tools/SceneAPI/SceneData/Behaviors/ScriptProcessorRuleBehavior.cpp @@ -19,6 +19,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -99,6 +102,7 @@ namespace AZ::SceneAPI::Behaviors { AZStd::string result; CallResult(result, FN_OnUpdateManifest, scene); + ScriptBuildingNotificationBusHandler::BusDisconnect(); return result; } @@ -110,6 +114,7 @@ namespace AZ::SceneAPI::Behaviors { ExportProductList result; CallResult(result, FN_OnPrepareForExport, scene, outputDirectory, platformIdentifier, productList); + ScriptBuildingNotificationBusHandler::BusDisconnect(); return result; } @@ -168,21 +173,9 @@ namespace AZ::SceneAPI::Behaviors UnloadPython(); } - bool ScriptProcessorRuleBehavior::LoadPython(const AZ::SceneAPI::Containers::Scene& scene) + bool ScriptProcessorRuleBehavior::LoadPython(const AZ::SceneAPI::Containers::Scene& scene, AZStd::string& scriptPath) { - if (m_editorPythonEventsInterface && !m_scriptFilename.empty()) - { - return true; - } - - // get project folder - auto settingsRegistry = AZ::SettingsRegistry::Get(); - AZ::IO::FixedMaxPath projectPath; - if (!settingsRegistry->Get(projectPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectPath)) - { - return false; - } - + int scriptDiscoveryAttempts = 0; const AZ::SceneAPI::Containers::SceneManifest& manifest = scene.GetManifest(); auto view = Containers::MakeDerivedFilterView(manifest.GetValueStorage()); for (const auto& scriptItem : view) @@ -194,9 +187,21 @@ namespace AZ::SceneAPI::Behaviors continue; } + ++scriptDiscoveryAttempts; + // check for file exist via absolute path if (!IO::FileIOBase::GetInstance()->Exists(scriptFilename.c_str())) { + // get project folder + auto settingsRegistry = AZ::SettingsRegistry::Get(); + AZ::IO::FixedMaxPath projectPath; + if (!settingsRegistry->Get(projectPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectPath)) + { + AZ_Error("scene", false, "With (%s) could not find Project Path during script discovery.", + scene.GetManifestFilename().c_str()); + return false; + } + // check for script in the project folder AZ::IO::FixedMaxPath projectScriptPath = projectPath / scriptFilename; if (!IO::FileIOBase::GetInstance()->Exists(projectScriptPath.c_str())) @@ -209,32 +214,47 @@ namespace AZ::SceneAPI::Behaviors scriptFilename = AZStd::move(projectScriptPath); } - // lazy load the Python interface - auto editorPythonEventsInterface = AZ::Interface::Get(); - if (editorPythonEventsInterface->IsPythonActive() == false) - { - const bool silenceWarnings = false; - if (editorPythonEventsInterface->StartPython(silenceWarnings) == false) - { - editorPythonEventsInterface = nullptr; - } - } + scriptPath = scriptFilename.c_str(); + break; + } - // both Python and the script need to be ready - if (editorPythonEventsInterface == nullptr || scriptFilename.empty()) - { - AZ_Warning("scene", false,"The scene manifest (%s) attempted to use script(%s) but Python is not enabled;" - "please add the EditorPythonBinding gem & PythonAssetBuilder gem to your project.", - scene.GetManifestFilename().c_str(), scriptFilename.c_str()); + if (scriptPath.empty()) + { + AZ_Warning("scene", scriptDiscoveryAttempts == 0, + "The scene manifest (%s) attempted to use script rule, but no script file path could be found.", + scene.GetManifestFilename().c_str()); + return false; + } - return false; - } - - m_editorPythonEventsInterface = editorPythonEventsInterface; - m_scriptFilename = scriptFilename.c_str(); + // already prepared the Python VM? + if (m_editorPythonEventsInterface) + { return true; } - return false; + + // lazy load the Python interface + auto editorPythonEventsInterface = AZ::Interface::Get(); + if (editorPythonEventsInterface->IsPythonActive() == false) + { + const bool silenceWarnings = false; + if (editorPythonEventsInterface->StartPython(silenceWarnings) == false) + { + editorPythonEventsInterface = nullptr; + } + } + + // both Python and the script need to be ready + if (editorPythonEventsInterface == nullptr) + { + AZ_Warning("scene", false, + "The scene manifest (%s) attempted to prepare Python but Python can not start", + scene.GetManifestFilename().c_str()); + + return false; + } + + m_editorPythonEventsInterface = editorPythonEventsInterface; + return true; } void ScriptProcessorRuleBehavior::UnloadPython() @@ -251,11 +271,13 @@ namespace AZ::SceneAPI::Behaviors { using namespace AzToolsFramework; - auto executeCallback = [this, &context]() + AZStd::string scriptPath; + + auto executeCallback = [&context, &scriptPath]() { // set up script's hook callback EditorPythonRunnerRequestBus::Broadcast(&EditorPythonRunnerRequestBus::Events::ExecuteByFilename, - m_scriptFilename.c_str()); + scriptPath.c_str()); // call script's callback to allow extra products ExportProductList extraProducts; @@ -279,7 +301,7 @@ namespace AZ::SceneAPI::Behaviors } }; - if (LoadPython(context.GetScene())) + if (LoadPython(context.GetScene(), scriptPath)) { EditorPythonConsoleNotificationHandler logger; m_editorPythonEventsInterface->ExecuteWithLock(executeCallback); @@ -306,23 +328,19 @@ namespace AZ::SceneAPI::Behaviors { using namespace AzToolsFramework; - // This behavior persists on the same AssetBuilder. Clear the script file name so that if - // this builder processes a scene file with a script file name, and then later processes - // a scene without a script file name, it won't run the old script on the new scene. - m_scriptFilename.clear(); - if (action != ManifestAction::Update) { return Events::ProcessingResult::Ignored; } - if (LoadPython(scene)) + AZStd::string scriptPath; + if (LoadPython(scene, scriptPath)) { AZStd::string manifestUpdate; - auto executeCallback = [this, &scene, &manifestUpdate]() + auto executeCallback = [&scene, &manifestUpdate, &scriptPath]() { EditorPythonRunnerRequestBus::Broadcast(&EditorPythonRunnerRequestBus::Events::ExecuteByFilename, - m_scriptFilename.c_str()); + scriptPath.c_str()); ScriptBuildingNotificationBus::BroadcastResult(manifestUpdate, &ScriptBuildingNotificationBus::Events::OnUpdateManifest, scene); @@ -331,6 +349,9 @@ namespace AZ::SceneAPI::Behaviors EditorPythonConsoleNotificationHandler logger; m_editorPythonEventsInterface->ExecuteWithLock(executeCallback); + EntityUtilityBus::Broadcast(&EntityUtilityBus::Events::ResetEntityContext); + AZ::Interface::Get()->RemoveAllTemplates(); + // attempt to load the manifest string back to a JSON-scene-manifest auto sceneManifestLoader = AZStd::make_unique(); auto loadOutcome = sceneManifestLoader->LoadFromString(manifestUpdate); @@ -347,4 +368,8 @@ namespace AZ::SceneAPI::Behaviors return Events::ProcessingResult::Ignored; } + void ScriptProcessorRuleBehavior::GetManifestDependencyPaths(AZStd::vector& paths) + { + paths.emplace_back("/scriptFilename"); + } } // namespace AZ diff --git a/Code/Tools/SceneAPI/SceneData/Behaviors/ScriptProcessorRuleBehavior.h b/Code/Tools/SceneAPI/SceneData/Behaviors/ScriptProcessorRuleBehavior.h index fa70a12b0d..a9ddf88df9 100644 --- a/Code/Tools/SceneAPI/SceneData/Behaviors/ScriptProcessorRuleBehavior.h +++ b/Code/Tools/SceneAPI/SceneData/Behaviors/ScriptProcessorRuleBehavior.h @@ -37,6 +37,7 @@ namespace AZ::SceneAPI::Behaviors , public Events::AssetImportRequestBus::Handler { public: + AZ_COMPONENT(ScriptProcessorRuleBehavior, "{24054E73-1B92-43B0-AC13-174B2F0E3F66}", SceneCore::BehaviorComponent); ~ScriptProcessorRuleBehavior() override = default; @@ -44,22 +45,21 @@ namespace AZ::SceneAPI::Behaviors SCENE_DATA_API void Activate() override; SCENE_DATA_API void Deactivate() override; static void Reflect(ReflectContext* context); - + // AssetImportRequestBus::Handler SCENE_DATA_API Events::ProcessingResult UpdateManifest( Containers::Scene& scene, ManifestAction action, RequestingApplication requester) override; - + SCENE_DATA_API void GetManifestDependencyPaths(AZStd::vector& paths) override; protected: - bool LoadPython(const AZ::SceneAPI::Containers::Scene& scene); + bool LoadPython(const AZ::SceneAPI::Containers::Scene& scene, AZStd::string& scriptPath); void UnloadPython(); bool DoPrepareForExport(Events::PreExportEventContext& context); private: AzToolsFramework::EditorPythonEventsInterface* m_editorPythonEventsInterface = nullptr; - AZStd::string m_scriptFilename; struct ExportEventHandler; AZStd::shared_ptr m_exportEventHandler; diff --git a/Gems/Prefab/PrefabBuilder/CMakeLists.txt b/Gems/Prefab/PrefabBuilder/CMakeLists.txt index 450b8d0408..9aedbd3d99 100644 --- a/Gems/Prefab/PrefabBuilder/CMakeLists.txt +++ b/Gems/Prefab/PrefabBuilder/CMakeLists.txt @@ -13,6 +13,9 @@ endif() ly_add_target( NAME PrefabBuilder.Static STATIC NAMESPACE Gem + INCLUDE_DIRECTORIES + PRIVATE + . FILES_CMAKE prefabbuilder_files.cmake BUILD_DEPENDENCIES @@ -20,6 +23,9 @@ ly_add_target( AZ::AzCore AZ::AzToolsFramework AZ::AssetBuilderSDK + AZ::SceneCore + AZ::SceneData + 3rdParty::RapidJSON ) ly_add_target( @@ -35,7 +41,8 @@ ly_add_target( Gem::PrefabBuilder.Static ) -# the prefab builder only needs to be active in builders +# create an alias for the tool version +ly_create_alias(NAME PrefabBuilder.Tools NAMESPACE Gem TARGETS Gem::PrefabBuilder.Builders) # we automatically add this gem, if it is present, to all our known set of builder applications: ly_enable_gems(GEMS PrefabBuilder) diff --git a/Gems/Prefab/PrefabBuilder/PrefabBuilderModule.cpp b/Gems/Prefab/PrefabBuilder/PrefabBuilderModule.cpp index 762f459d9d..2bf7026549 100644 --- a/Gems/Prefab/PrefabBuilder/PrefabBuilderModule.cpp +++ b/Gems/Prefab/PrefabBuilder/PrefabBuilderModule.cpp @@ -8,6 +8,7 @@ #include #include +#include namespace AZ::Prefab { @@ -22,7 +23,8 @@ namespace AZ::Prefab : Module() { m_descriptors.insert(m_descriptors.end(), { - PrefabBuilderComponent::CreateDescriptor() + PrefabBuilderComponent::CreateDescriptor(), + AZ::SceneAPI::Behaviors::PrefabGroupBehavior::CreateDescriptor() }); } }; diff --git a/Gems/Prefab/PrefabBuilder/PrefabBuilderTests.h b/Gems/Prefab/PrefabBuilder/PrefabBuilderTests.h index 3aea173ba4..2aec317e69 100644 --- a/Gems/Prefab/PrefabBuilder/PrefabBuilderTests.h +++ b/Gems/Prefab/PrefabBuilder/PrefabBuilderTests.h @@ -13,6 +13,7 @@ #include #include #include +#include namespace UnitTest { diff --git a/Gems/Prefab/PrefabBuilder/PrefabGroup/IPrefabGroup.h b/Gems/Prefab/PrefabBuilder/PrefabGroup/IPrefabGroup.h new file mode 100644 index 0000000000..c2f8313b95 --- /dev/null +++ b/Gems/Prefab/PrefabBuilder/PrefabGroup/IPrefabGroup.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ +#pragma once + +#include +#include +#include + +namespace AZ::SceneAPI::DataTypes +{ + class IPrefabGroup + : public ISceneNodeGroup + { + public: + AZ_RTTI(IPrefabGroup, "{7E50FAEF-3379-4521-99C5-B428FDEE3B7B}", ISceneNodeGroup); + + ~IPrefabGroup() override = default; + virtual AzToolsFramework::Prefab::PrefabDomConstReference GetPrefabDomRef() const = 0; + }; +} diff --git a/Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabBehaviorTests.cpp b/Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabBehaviorTests.cpp new file mode 100644 index 0000000000..856aba979b --- /dev/null +++ b/Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabBehaviorTests.cpp @@ -0,0 +1,161 @@ +/* + * 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 UnitTest +{ + class PrefabBehaviorTests + : public PrefabBuilderTests + { + public: + static void SetUpTestCase() + { + // Allocator needed by SceneCore + if (!AZ::AllocatorInstance().IsReady()) + { + AZ::AllocatorInstance().Create(); + } + AZ::SceneAPI::SceneCoreStandaloneAllocator::Initialize(AZ::Environment::GetInstance()); + } + + static void TearDownTestCase() + { + AZ::SceneAPI::SceneCoreStandaloneAllocator::TearDown(); + AZ::AllocatorInstance().Destroy(); + } + + void SetUp() override + { + PrefabBuilderTests::SetUp(); + m_prefabGroupBehavior = AZStd::make_unique(); + m_prefabGroupBehavior->Activate(); + + // Mocking the asset system replacing the AssetSystem::AssetSystemComponent + AZ::Entity* systemEntity = m_app.FindEntity(AZ::SystemEntityId); + systemEntity->FindComponent()->Deactivate(); + using namespace testing; + ON_CALL(m_assetSystemRequestMock, GetSourceInfoBySourcePath(_, _, _)).WillByDefault([](auto* path, auto& info, auto&) + { + return PrefabBehaviorTests::OnGetSourceInfoBySourcePath(path, info); + }); + m_assetSystemRequestMock.BusConnect(); + } + + void TearDown() override + { + m_assetSystemRequestMock.BusDisconnect(); + + m_prefabGroupBehavior->Deactivate(); + m_prefabGroupBehavior.reset(); + + PrefabBuilderTests::TearDown(); + } + + static bool OnGetSourceInfoBySourcePath(AZStd::string_view sourcePath, AZ::Data::AssetInfo& assetInfo) + { + if (sourcePath == AZStd::string_view("mock")) + { + assetInfo.m_assetId = AZ::Uuid::CreateRandom(); + assetInfo.m_assetType = azrtti_typeid(); + assetInfo.m_relativePath = "mock/path"; + assetInfo.m_sizeBytes = 0; + } + return true; + } + + struct TestPreExportEventContext + { + TestPreExportEventContext() + : m_scene("test_context") + { + using namespace AZ::SceneAPI::Events; + m_preExportEventContext = AZStd::make_unique(m_productList, m_outputDirectory, m_scene, "mock"); + } + + void SetOutputDirectory(AZStd::string outputDirectory) + { + using namespace AZ::SceneAPI::Events; + m_outputDirectory = AZStd::move(outputDirectory); + m_preExportEventContext = AZStd::make_unique(m_productList, m_outputDirectory, m_scene, "mock"); + } + + AZStd::unique_ptr m_preExportEventContext; + AZ::SceneAPI::Events::ExportProductList m_productList; + AZStd::string m_outputDirectory; + AZ::SceneAPI::Containers::Scene m_scene; + }; + + AZStd::unique_ptr m_prefabGroupBehavior; + testing::NiceMock m_assetSystemRequestMock; + }; + + TEST_F(PrefabBehaviorTests, PrefabBehavior_EmptyContextIgnored_Works) + { + auto context = TestPreExportEventContext{}; + + auto result = AZ::SceneAPI::Events::ProcessingResult::Failure; + AZ::SceneAPI::Events::CallProcessorBus::BroadcastResult( + result, + &AZ::SceneAPI::Events::CallProcessorBus::Events::Process, + context.m_preExportEventContext.get()); + + EXPECT_EQ(result, AZ::SceneAPI::Events::ProcessingResult::Ignored); + } + + TEST_F(PrefabBehaviorTests, PrefabBehavior_SimplePrefab_Works) + { + auto context = TestPreExportEventContext{}; + + // check for the file at /mock/fake_prefab.procprefab + AZ::Test::ScopedAutoTempDirectory tempDir; + context.SetOutputDirectory(tempDir.GetDirectory()); + + auto jsonOutcome = AZ::JsonSerializationUtils::ReadJsonString(Data::jsonPrefab); + ASSERT_TRUE(jsonOutcome); + + auto prefabGroup = AZStd::make_shared(); + prefabGroup.get()->SetId(AZ::Uuid::CreateRandom()); + prefabGroup.get()->SetName("fake_prefab"); + prefabGroup.get()->SetPrefabDom(AZStd::move(jsonOutcome.GetValue())); + context.m_scene.GetManifest().AddEntry(prefabGroup); + context.m_scene.SetSource("mock", AZ::Uuid::CreateRandom()); + + auto result = AZ::SceneAPI::Events::ProcessingResult::Failure; + AZ::SceneAPI::Events::CallProcessorBus::BroadcastResult( + result, + &AZ::SceneAPI::Events::CallProcessorBus::Events::Process, + context.m_preExportEventContext.get()); + + EXPECT_EQ(result, AZ::SceneAPI::Events::ProcessingResult::Success); + + AZStd::string pathStr; + AzFramework::StringFunc::Path::ConstructFull(tempDir.GetDirectory(), "mock/fake_prefab.procprefab", pathStr, true); + if (!AZ::IO::SystemFile::Exists(pathStr.c_str())) + { + AZ_Warning("testing", false, "The product asset (%s) is missing", pathStr.c_str()); + } + } +} diff --git a/Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabBehaviorTests.inl b/Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabBehaviorTests.inl new file mode 100644 index 0000000000..b39f782a1e --- /dev/null +++ b/Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabBehaviorTests.inl @@ -0,0 +1,104 @@ +/* + * 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 + * + */ + +namespace UnitTest +{ + namespace Data + { + const char* jsonPrefab = R"JSON( + { + "ContainerEntity": { + "Id": "ContainerEntity", + "Name": "test_template_1", + "Components": { + "Component_[12122553907433030840]": { + "$type": "EditorVisibilityComponent", + "Id": 12122553907433030840 + }, + "Component_[5666150279650800686]": { + "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent", + "Id": 5666150279650800686, + "Parent Entity": "" + }, + "Component_[8790726658974076423]": { + "$type": "EditorOnlyEntityComponent", + "Id": 8790726658974076423 + } + } + }, + "Entities": { + "Entity_[1588652751483]": { + "Id": "Entity_[1588652751483]", + "Name": "root", + "Components": { + "Component_[11872748096995986607]": { + "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent", + "Id": 11872748096995986607, + "Parent Entity": "ContainerEntity", + "Transform Data": { + "Rotate": [ + 0.0, + 0.10000000149011612, + 180.0 + ] + } + }, + "Component_[12138841758570858610]": { + "$type": "EditorVisibilityComponent", + "Id": 12138841758570858610 + }, + "Component_[15735658354806796004]": { + "$type": "EditorOnlyEntityComponent", + "Id": 15735658354806796004 + } + } + }, + "Entity_[1592947718779]": { + "Id": "Entity_[1592947718779]", + "Name": "cube", + "Components": { + "Component_[2505301170249328189]": { + "$type": "EditorOnlyEntityComponent", + "Id": 2505301170249328189 + }, + "Component_[3716170894544198343]": { + "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent", + "Id": 3716170894544198343, + "Parent Entity": "Entity_[1588652751483]" + }, + "Component_[5862175558847453681]": { + "$type": "EditorVisibilityComponent", + "Id": 5862175558847453681 + } + } + }, + "Entity_[1597242686075]": { + "Id": "Entity_[1597242686075]", + "Name": "cubeKid", + "Components": { + "Component_[10128771992421174485]": { + "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent", + "Id": 10128771992421174485, + "Parent Entity": "Entity_[1592947718779]" + }, + "Component_[14936165953779771344]": { + "$type": "EditorVisibilityComponent", + "Id": 14936165953779771344 + }, + "Component_[403416213715997356]": { + "$type": "EditorOnlyEntityComponent", + "Id": 403416213715997356 + } + } + } + } + } + )JSON"; + + } +} diff --git a/Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroup.cpp b/Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroup.cpp new file mode 100644 index 0000000000..f4dd37b86c --- /dev/null +++ b/Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroup.cpp @@ -0,0 +1,136 @@ +/* + * 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 + +namespace AZ::SceneAPI::SceneData +{ + // PrefabGroup + + PrefabGroup::PrefabGroup() + : m_id(Uuid::CreateNull()) + , m_name() + { + } + + const AZStd::string& PrefabGroup::GetName() const + { + return m_name; + } + + void PrefabGroup::SetName(AZStd::string name) + { + m_name = AZStd::move(name); + } + + const Uuid& PrefabGroup::GetId() const + { + return m_id; + } + + void PrefabGroup::SetId(Uuid id) + { + m_id = AZStd::move(id); + } + + Containers::RuleContainer& PrefabGroup::GetRuleContainer() + { + return m_rules; + } + + const Containers::RuleContainer& PrefabGroup::GetRuleContainerConst() const + { + return m_rules; + } + + DataTypes::ISceneNodeSelectionList& PrefabGroup::GetSceneNodeSelectionList() + { + return m_nodeSelectionList; + } + + const DataTypes::ISceneNodeSelectionList& PrefabGroup::GetSceneNodeSelectionList() const + { + return m_nodeSelectionList; + } + + void PrefabGroup::SetPrefabDom(AzToolsFramework::Prefab::PrefabDom prefabDom) + { + m_prefabDomData = AZStd::make_shared(); + m_prefabDomData->CopyValue(prefabDom); + } + + AzToolsFramework::Prefab::PrefabDomConstReference PrefabGroup::GetPrefabDomRef() const + { + if (m_prefabDomData) + { + return m_prefabDomData->GetValue(); + } + return {}; + } + + void PrefabGroup::Reflect(ReflectContext* context) + { + SerializeContext* serializeContext = azrtti_cast(context); + if (serializeContext) + { + serializeContext->Class() + ->Version(1); + + serializeContext->Class() + ->Version(1) + ->Field("name", &PrefabGroup::m_name) + ->Field("nodeSelectionList", &PrefabGroup::m_nodeSelectionList) + ->Field("rules", &PrefabGroup::m_rules) + ->Field("id", &PrefabGroup::m_id) + ->Field("prefabDomData", &PrefabGroup::m_prefabDomData); + } + + BehaviorContext* behaviorContext = azrtti_cast(context); + if (behaviorContext) + { + auto setPrefabDomData = [](PrefabGroup& self, const AZStd::string& json) + { + auto jsonOutcome = JsonSerializationUtils::ReadJsonString(json); + if (jsonOutcome.IsSuccess()) + { + self.SetPrefabDom(AZStd::move(jsonOutcome.GetValue())); + return true; + } + AZ_Error("prefab", false, "Set PrefabDom failed (%s)", jsonOutcome.GetError().c_str()); + return false; + }; + + auto getPrefabDomData = [](const PrefabGroup& self) -> AZStd::string + { + if (self.GetPrefabDomRef().has_value() == false) + { + return {}; + } + AZStd::string buffer; + JsonSerializationUtils::WriteJsonString(self.GetPrefabDomRef().value(), buffer); + return buffer; + }; + + behaviorContext->Class() + ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All) + ->Attribute(Script::Attributes::Scope, Script::Attributes::ScopeFlags::Common) + ->Attribute(Script::Attributes::Module, "prefab") + ->Property("name", BehaviorValueProperty(&PrefabGroup::m_name)) + ->Property("id", BehaviorValueProperty(&PrefabGroup::m_id)) + ->Property("prefabDomData", getPrefabDomData, setPrefabDomData); + } + } +} diff --git a/Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroup.h b/Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroup.h new file mode 100644 index 0000000000..e5c47186eb --- /dev/null +++ b/Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroup.h @@ -0,0 +1,64 @@ +/* + * 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 +#include +#include + +namespace AZ +{ + class ReflectContext; +} + +namespace AZ::SceneAPI::Containers +{ + class Scene; +} + +namespace AZ::SceneAPI::SceneData +{ + class PrefabGroup final + : public DataTypes::IPrefabGroup + { + public: + AZ_RTTI(PrefabGroup, "{99FE3C6F-5B55-4D8B-8013-2708010EC715}", DataTypes::IPrefabGroup); + AZ_CLASS_ALLOCATOR(PrefabGroup, SystemAllocator, 0); + + static void Reflect(AZ::ReflectContext* context); + + PrefabGroup(); + ~PrefabGroup() override = default; + + // DataTypes::IPrefabGroup + AzToolsFramework::Prefab::PrefabDomConstReference GetPrefabDomRef() const override; + const AZStd::string& GetName() const override; + const Uuid& GetId() const override; + Containers::RuleContainer& GetRuleContainer() override; + const Containers::RuleContainer& GetRuleContainerConst() const override; + DataTypes::ISceneNodeSelectionList& GetSceneNodeSelectionList() override; + const DataTypes::ISceneNodeSelectionList& GetSceneNodeSelectionList() const override; + + // Concrete API + void SetId(Uuid id); + void SetName(AZStd::string name); + void SetPrefabDom(AzToolsFramework::Prefab::PrefabDom prefabDom); + + private: + SceneNodeSelectionList m_nodeSelectionList; + Containers::RuleContainer m_rules; + AZStd::string m_name; + Uuid m_id; + AZStd::shared_ptr m_prefabDomData; + }; +} diff --git a/Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroupBehavior.cpp b/Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroupBehavior.cpp new file mode 100644 index 0000000000..13828e5f8c --- /dev/null +++ b/Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroupBehavior.cpp @@ -0,0 +1,251 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include + +namespace AZ::SceneAPI::Behaviors +{ + // + // ExportEventHandler + // + + struct PrefabGroupBehavior::ExportEventHandler final + : public AZ::SceneAPI::SceneCore::ExportingComponent + { + using PreExportEventContextFunction = AZStd::function; + PreExportEventContextFunction m_preExportEventContextFunction; + AZ::Prefab::PrefabGroupAssetHandler m_prefabGroupAssetHandler; + + ExportEventHandler() = delete; + + ExportEventHandler(PreExportEventContextFunction function) + : m_preExportEventContextFunction(AZStd::move(function)) + { + BindToCall(&ExportEventHandler::PrepareForExport); + AZ::SceneAPI::SceneCore::ExportingComponent::Activate(); + } + + ~ExportEventHandler() + { + AZ::SceneAPI::SceneCore::ExportingComponent::Deactivate(); + } + + Events::ProcessingResult PrepareForExport(Events::PreExportEventContext& context) + { + return m_preExportEventContextFunction(context); + } + }; + + // + // PrefabGroupBehavior + // + + void PrefabGroupBehavior::Activate() + { + m_exportEventHandler = AZStd::make_shared([this](auto& context) + { + return this->OnPrepareForExport(context); + }); + } + + void PrefabGroupBehavior::Deactivate() + { + m_exportEventHandler.reset(); + } + + AZStd::unique_ptr PrefabGroupBehavior::CreateProductAssetData(const SceneData::PrefabGroup* prefabGroup) const + { + using namespace AzToolsFramework::Prefab; + + auto* prefabLoaderInterface = AZ::Interface::Get(); + if (!prefabLoaderInterface) + { + AZ_Error("prefab", false, "Could not get PrefabLoaderInterface"); + return {}; + } + + // write to a UTF-8 string buffer + auto prefabDomRef = prefabGroup->GetPrefabDomRef(); + if (!prefabDomRef) + { + AZ_Error("prefab", false, "PrefabGroup(%s) missing PrefabDom", prefabGroup->GetName().c_str()); + return {}; + } + + const AzToolsFramework::Prefab::PrefabDom& prefabDom = prefabDomRef.value(); + rapidjson::StringBuffer sb; + rapidjson::Writer> writer(sb); + if (prefabDom.Accept(writer) == false) + { + AZ_Error("prefab", false, "Could not write PrefabGroup(%s) to JSON", prefabGroup->GetName().c_str()); + return {}; + } + + // validate the PrefabDom will make a valid Prefab template instance + auto templateId = prefabLoaderInterface->LoadTemplateFromString(sb.GetString(), prefabGroup->GetName().c_str()); + if (templateId == InvalidTemplateId) + { + AZ_Error("prefab", false, "PrefabGroup(%s) Could not write load template", prefabGroup->GetName().c_str()); + return {}; + } + + auto* prefabSystemComponentInterface = AZ::Interface::Get(); + if (!prefabSystemComponentInterface) + { + AZ_Error("prefab", false, "Could not get PrefabSystemComponentInterface"); + 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 + auto proceduralPrefab = AZStd::make_unique(rapidjson::kObjectType); + instanceToTemplateInterface->GenerateDomForInstance(*proceduralPrefab.get(), *instance.get()); + return proceduralPrefab; + } + + bool PrefabGroupBehavior::WriteOutProductAsset( + Events::PreExportEventContext& context, + const SceneData::PrefabGroup* prefabGroup, + const rapidjson::Document& doc) const + { + // Retrieve source asset info so we can get a string with the relative path to the asset + bool assetInfoResult; + Data::AssetInfo info; + AZStd::string watchFolder; + AzToolsFramework::AssetSystemRequestBus::BroadcastResult( + assetInfoResult, + &AzToolsFramework::AssetSystemRequestBus::Events::GetSourceInfoBySourcePath, + context.GetScene().GetSourceFilename().c_str(), + info, + watchFolder); + + AZ::IO::FixedMaxPath assetPath(info.m_relativePath); + assetPath.ReplaceFilename(prefabGroup->GetName().c_str()); + + AZStd::string filePath = AZ::SceneAPI::Utilities::FileUtilities::CreateOutputFileName( + assetPath.c_str(), + context.GetOutputDirectory(), + AZ::Prefab::PrefabGroupAssetHandler::s_Extension); + + AZ::IO::FileIOStream fileStream(filePath.c_str(), AZ::IO::OpenMode::ModeWrite); + if (fileStream.IsOpen() == false) + { + AZ_Error("prefab", false, "File path(%s) could not open for write", filePath.c_str()); + return false; + } + + // write to a UTF-8 string buffer + rapidjson::StringBuffer sb; + rapidjson::Writer> writer(sb); + if (doc.Accept(writer) == false) + { + AZ_Error("prefab", false, "PrefabGroup(%s) Could not buffer JSON", prefabGroup->GetName().c_str()); + return false; + } + + const auto bytesWritten = fileStream.Write(sb.GetSize(), sb.GetString()); + if (bytesWritten > 1) + { + AZ::u32 subId = AZ::Crc32(filePath.c_str()); + context.GetProductList().AddProduct( + filePath, + context.GetScene().GetSourceGuid(), + azrtti_typeid(), + {}, + AZStd::make_optional(subId)); + + return true; + } + return false; + } + + Events::ProcessingResult PrefabGroupBehavior::OnPrepareForExport(Events::PreExportEventContext& context) const + { + AZStd::vector prefabGroupCollection; + const Containers::SceneManifest& manifest = context.GetScene().GetManifest(); + + for (size_t i = 0; i < manifest.GetEntryCount(); ++i) + { + const auto* group = azrtti_cast(manifest.GetValue(i).get()); + if (group) + { + prefabGroupCollection.push_back(group); + } + } + + if (prefabGroupCollection.empty()) + { + return AZ::SceneAPI::Events::ProcessingResult::Ignored; + } + + for (const auto* prefabGroup : prefabGroupCollection) + { + auto result = CreateProductAssetData(prefabGroup); + if (!result) + { + return Events::ProcessingResult::Failure; + } + + if (WriteOutProductAsset(context, prefabGroup, *result.get()) == false) + { + return Events::ProcessingResult::Failure; + } + } + + return Events::ProcessingResult::Success; + } + + void PrefabGroupBehavior::Reflect(ReflectContext* context) + { + AZ::SceneAPI::SceneData::PrefabGroup::Reflect(context); + Prefab::ProceduralPrefabAsset::Reflect(context); + + SerializeContext* serializeContext = azrtti_cast(context); + if (serializeContext) + { + serializeContext->Class()->Version(1); + } + } +} diff --git a/Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroupBehavior.h b/Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroupBehavior.h new file mode 100644 index 0000000000..b57d30695a --- /dev/null +++ b/Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroupBehavior.h @@ -0,0 +1,55 @@ +/* + * 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 +#include +#include + +namespace AZ +{ + class ReflectContext; +} + +namespace AZ::SceneAPI::Events +{ + class PreExportEventContext; +} + +namespace AZ::SceneAPI::Behaviors +{ + class PrefabGroupBehavior + : public SceneCore::BehaviorComponent + { + public: + AZ_COMPONENT(PrefabGroupBehavior, "{13DC2819-CAC2-4977-91D7-C870087072AB}", SceneCore::BehaviorComponent); + + ~PrefabGroupBehavior() override = default; + + void Activate() override; + void Deactivate() override; + static void Reflect(ReflectContext* context); + + private: + Events::ProcessingResult OnPrepareForExport(Events::PreExportEventContext& context) const; + AZStd::unique_ptr CreateProductAssetData(const SceneData::PrefabGroup* prefabGroup) const; + + bool WriteOutProductAsset( + Events::PreExportEventContext& context, + const SceneData::PrefabGroup* prefabGroup, + const rapidjson::Document& doc) const; + + struct ExportEventHandler; + AZStd::shared_ptr m_exportEventHandler; + }; +} diff --git a/Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroupTests.cpp b/Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroupTests.cpp new file mode 100644 index 0000000000..9fb935ce9f --- /dev/null +++ b/Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroupTests.cpp @@ -0,0 +1,161 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include +#include +#include +#include +#include + +namespace UnitTest +{ + TEST_F(PrefabBuilderTests, PrefabGroup_FindsRequiredReflection_True) + { + using namespace AZ::SceneAPI; + auto* serializeContext = m_app.GetSerializeContext(); + ASSERT_NE(nullptr, serializeContext); + SceneData::PrefabGroup::Reflect(serializeContext); + SceneData::PrefabGroup::Reflect(m_app.GetJsonRegistrationContext()); + ASSERT_NE(nullptr, serializeContext->FindClassData(azrtti_typeid())); + ASSERT_NE(nullptr, serializeContext->FindClassData(azrtti_typeid())); + + auto findElementWithName = [](const AZ::SerializeContext::ClassData* classData, const char* name) + { + auto it = AZStd::find_if(classData->m_elements.begin(), classData->m_elements.end(), [name](const auto& element) + { + return strcmp(element.m_name, name) == 0; + }); + return it != classData->m_elements.end(); + }; + + auto* prefabGroupClassData = serializeContext->FindClassData(azrtti_typeid()); + EXPECT_TRUE(findElementWithName(prefabGroupClassData, "name")); + EXPECT_TRUE(findElementWithName(prefabGroupClassData, "nodeSelectionList")); + EXPECT_TRUE(findElementWithName(prefabGroupClassData, "rules")); + EXPECT_TRUE(findElementWithName(prefabGroupClassData, "id")); + EXPECT_TRUE(findElementWithName(prefabGroupClassData, "prefabDomData")); + + m_app.GetJsonRegistrationContext()->EnableRemoveReflection(); + SceneData::PrefabGroup::Reflect(m_app.GetJsonRegistrationContext()); + } + + TEST_F(PrefabBuilderTests, PrefabGroup_JsonWithPrefabArbitraryPrefab_Works) + { + namespace JSR = AZ::JsonSerializationResult; + using namespace AZ::SceneAPI; + auto* serializeContext = m_app.GetSerializeContext(); + ASSERT_NE(nullptr, serializeContext); + SceneData::PrefabGroup::Reflect(serializeContext); + AZ::Prefab::ProceduralPrefabAsset::Reflect(serializeContext); + SceneData::PrefabGroup::Reflect(m_app.GetJsonRegistrationContext()); + AZ::Prefab::ProceduralPrefabAsset::Reflect(m_app.GetJsonRegistrationContext()); + + // fill out a PrefabGroup using JSON + AZStd::string_view input = R"JSON( + { + "name" : "tester", + "id" : "{49698DBC-B447-49EF-9B56-25BB29342AFB}", + "prefabDomData" : {"foo": "bar"} + })JSON"; + + rapidjson::Document document; + document.Parse(input.data(), input.size()); + ASSERT_FALSE(document.HasParseError()); + + SceneData::PrefabGroup instancePrefabGroup; + EXPECT_EQ(AZ::JsonSerialization::Load(instancePrefabGroup, document).GetOutcome(), JSR::Outcomes::PartialDefaults); + + ASSERT_TRUE(instancePrefabGroup.GetPrefabDomRef().has_value()); + const AzToolsFramework::Prefab::PrefabDom& dom = instancePrefabGroup.GetPrefabDomRef().value(); + EXPECT_TRUE(dom.IsObject()); + EXPECT_TRUE(dom.GetObject().HasMember("foo")); + EXPECT_STREQ(dom.GetObject().FindMember("foo")->value.GetString(), "bar"); + EXPECT_STREQ(instancePrefabGroup.GetName().c_str(), "tester"); + EXPECT_STREQ(instancePrefabGroup.GetId().ToString().c_str(), "{49698DBC-B447-49EF-9B56-25BB29342AFB}"); + + m_app.GetJsonRegistrationContext()->EnableRemoveReflection(); + SceneData::PrefabGroup::Reflect(m_app.GetJsonRegistrationContext()); + AZ::Prefab::ProceduralPrefabAsset::Reflect(m_app.GetJsonRegistrationContext()); + } + + TEST_F(PrefabBuilderTests, PrefabGroup_InvalidPrefabJson_Detected) + { + using namespace AZ::SceneAPI; + + AZStd::string_view input = R"JSON( + { + bad json that will not parse + })JSON"; + + rapidjson::Document document; + document.Parse(input.data(), input.size()); + + SceneData::PrefabGroup prefabGroup; + prefabGroup.SetId(AZ::Uuid::CreateRandom()); + prefabGroup.SetName("tester"); + prefabGroup.SetPrefabDom(AZStd::move(document)); + + const AzToolsFramework::Prefab::PrefabDom& dom = prefabGroup.GetPrefabDomRef().value(); + EXPECT_TRUE(dom.IsNull()); + EXPECT_STREQ("tester", prefabGroup.GetName().c_str()); + } + + struct PrefabBuilderBehaviorTests + : public PrefabBuilderTests + { + void SetUp() override + { + using namespace AZ::SceneAPI; + + PrefabBuilderTests::SetUp(); + SceneData::PrefabGroup::Reflect(m_app.GetSerializeContext()); + SceneData::PrefabGroup::Reflect(m_app.GetBehaviorContext()); + SceneData::PrefabGroup::Reflect(m_app.GetJsonRegistrationContext()); + m_scriptContext = AZStd::make_unique(); + m_scriptContext->BindTo(m_app.GetBehaviorContext()); + } + + void TearDown() override + { + using namespace AZ::SceneAPI; + m_app.GetJsonRegistrationContext()->EnableRemoveReflection(); + SceneData::PrefabGroup::Reflect(m_app.GetJsonRegistrationContext()); + + m_scriptContext.reset(); + PrefabBuilderTests::TearDown(); + } + + void ExpectExecute(AZStd::string_view script) + { + EXPECT_TRUE(m_scriptContext->Execute(script.data())); + } + + AZStd::unique_ptr m_scriptContext; + }; + + TEST_F(PrefabBuilderBehaviorTests, PrefabGroup_PrefabGroupClass_Exists) + { + ExpectExecute("group = PrefabGroup()"); + ExpectExecute("assert(group)"); + ExpectExecute("assert(group.name)"); + ExpectExecute("assert(group.id)"); + ExpectExecute("assert(group.prefabDomData)"); + } + + TEST_F(PrefabBuilderBehaviorTests, PrefabGroup_PrefabGroupAssignment_Works) + { + ExpectExecute("group = PrefabGroup()"); + ExpectExecute("group.name = 'tester'"); + ExpectExecute("group.id = Uuid.CreateString('{AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE}', 0)"); + ExpectExecute("group.prefabDomData = '{\"foo\": \"bar\"}'"); + ExpectExecute("assert(group.name == 'tester')"); + ExpectExecute("assert(tostring(group.id) == '{AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE}')"); + ExpectExecute("assert(group.prefabDomData == '{\\n \"foo\": \"bar\"\\n}')"); + } +} diff --git a/Gems/Prefab/PrefabBuilder/PrefabGroup/ProceduralAssetHandler.cpp b/Gems/Prefab/PrefabBuilder/PrefabGroup/ProceduralAssetHandler.cpp new file mode 100644 index 0000000000..17ed4d9c0c --- /dev/null +++ b/Gems/Prefab/PrefabBuilder/PrefabGroup/ProceduralAssetHandler.cpp @@ -0,0 +1,185 @@ +/* + * 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 + +namespace AZ::Prefab +{ + // AssetTypeInfoHandler + + class PrefabGroupAssetHandler::AssetTypeInfoHandler final + : public AZ::AssetTypeInfoBus::Handler + { + public: + AZ_CLASS_ALLOCATOR(AssetTypeInfoHandler, AZ::SystemAllocator, 0); + AssetTypeInfoHandler(); + ~AssetTypeInfoHandler() override; + AZ::Data::AssetType GetAssetType() const override; + const char* GetAssetTypeDisplayName() const override; + const char* GetGroup() const override; + const char* GetBrowserIcon() const override; + void GetAssetTypeExtensions(AZStd::vector& extensions) override; + }; + + PrefabGroupAssetHandler::AssetTypeInfoHandler::AssetTypeInfoHandler() + { + AZ::AssetTypeInfoBus::Handler::BusConnect(azrtti_typeid()); + } + + PrefabGroupAssetHandler::AssetTypeInfoHandler::~AssetTypeInfoHandler() + { + AZ::AssetTypeInfoBus::Handler::BusDisconnect(azrtti_typeid()); + } + + AZ::Data::AssetType PrefabGroupAssetHandler::AssetTypeInfoHandler::GetAssetType() const + { + return azrtti_typeid(); + } + + const char* PrefabGroupAssetHandler::AssetTypeInfoHandler::GetAssetTypeDisplayName() const + { + return "Procedural Prefab"; + } + + const char* PrefabGroupAssetHandler::AssetTypeInfoHandler::GetGroup() const + { + return "Prefab"; + } + + const char* PrefabGroupAssetHandler::AssetTypeInfoHandler::GetBrowserIcon() const + { + return "Icons/Components/Box.png"; + } + + void PrefabGroupAssetHandler::AssetTypeInfoHandler::GetAssetTypeExtensions(AZStd::vector& extensions) + { + extensions.push_back(PrefabGroupAssetHandler::s_Extension); + } + + // PrefabGroupAssetHandler + + AZStd::string_view PrefabGroupAssetHandler::s_Extension{ "procprefab" }; + + PrefabGroupAssetHandler::PrefabGroupAssetHandler() + { + auto assetCatalog = AZ::Data::AssetCatalogRequestBus::FindFirstHandler(); + if (assetCatalog) + { + assetCatalog->EnableCatalogForAsset(azrtti_typeid()); + assetCatalog->AddExtension(s_Extension.data()); + } + if (AZ::Data::AssetManager::IsReady()) + { + AZ::Data::AssetManager::Instance().RegisterHandler(this, azrtti_typeid()); + } + m_assetTypeInfoHandler = AZStd::make_shared(); + } + + PrefabGroupAssetHandler::~PrefabGroupAssetHandler() + { + m_assetTypeInfoHandler.reset(); + if (AZ::Data::AssetManager::IsReady()) + { + AZ::Data::AssetManager::Instance().UnregisterHandler(this); + } + } + + AZ::Data::AssetData* PrefabGroupAssetHandler::CreateAsset([[maybe_unused]] const AZ::Data::AssetId& id, const AZ::Data::AssetType& type) + { + if (type != azrtti_typeid()) + { + AZ_Error("prefab", false, "Invalid asset type! Only handle 'ProceduralPrefabAsset'"); + return nullptr; + } + return aznew ProceduralPrefabAsset{}; + } + + void PrefabGroupAssetHandler::DestroyAsset(AZ::Data::AssetData* ptr) + { + // Note: the PrefabLoaderInterface will handle the lifetime of the Prefab Template + delete ptr; + } + + void PrefabGroupAssetHandler::GetHandledAssetTypes(AZStd::vector& assetTypes) + { + assetTypes.push_back(azrtti_typeid()); + } + + AZ::Data::AssetHandler::LoadResult PrefabGroupAssetHandler::LoadAssetData( + const AZ::Data::Asset& asset, + AZStd::shared_ptr stream, + [[maybe_unused]] const AZ::Data::AssetFilterCB& assetLoadFilterCB) + { + using namespace AzToolsFramework::Prefab; + + auto* proceduralPrefabAsset = asset.GetAs(); + if (!proceduralPrefabAsset) + { + AZ_Error("prefab", false, "This should be a ProceduralPrefabAsset type, as this is the only type we process!"); + return LoadResult::Error; + } + + AZStd::string buffer; + buffer.resize(stream->GetLoadedSize()); + stream->Read(stream->GetLoadedSize(), buffer.data()); + + auto jsonOutcome = AZ::JsonSerializationUtils::ReadJsonString(buffer); + if (jsonOutcome.IsSuccess() == false) + { + AZ_Error("prefab", false, "Asset JSON failed to compile %s", jsonOutcome.GetError().c_str()); + return LoadResult::Error; + } + const auto& jsonDoc = jsonOutcome.GetValue(); + + if (jsonDoc.IsObject() == false) + { + return LoadResult::Error; + } + + if (jsonDoc.FindMember("Source") == jsonDoc.MemberEnd()) + { + return LoadResult::Error; + } + const auto& templateName = jsonDoc["Source"]; + + AZStd::string stringJson; + auto stringOutcome = AZ::JsonSerializationUtils::WriteJsonString(jsonDoc, stringJson); + if (stringOutcome.IsSuccess() == false) + { + AZ_Error("prefab", false, "Could not write to JSON string %s", stringOutcome.GetError().c_str()); + return LoadResult::Error; + } + + // prepare the template + auto* prefabLoaderInterface = AZ::Interface::Get(); + if (!prefabLoaderInterface) + { + return LoadResult::Error; + } + + auto templateId = prefabLoaderInterface->LoadTemplateFromString(stringJson.data(), templateName.GetString()); + if (templateId == InvalidTemplateId) + { + return LoadResult::Error; + } + + proceduralPrefabAsset->SetTemplateId(templateId); + proceduralPrefabAsset->SetTemplateName(templateName.GetString()); + return LoadResult::LoadComplete; + } + + AZStd::unique_ptr s_PrefabGroupAssetHandler; +} + diff --git a/Gems/Prefab/PrefabBuilder/PrefabGroup/ProceduralAssetHandler.h b/Gems/Prefab/PrefabBuilder/PrefabGroup/ProceduralAssetHandler.h new file mode 100644 index 0000000000..301f8a467a --- /dev/null +++ b/Gems/Prefab/PrefabBuilder/PrefabGroup/ProceduralAssetHandler.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 +#include +#include + +namespace AZ::Prefab +{ + class PrefabGroupAssetHandler final + : public AZ::Data::AssetHandler + { + public: + AZ_CLASS_ALLOCATOR(PrefabGroupAssetHandler, AZ::SystemAllocator, 0); + PrefabGroupAssetHandler(); + ~PrefabGroupAssetHandler() override; + + static AZStd::string_view s_Extension; + + protected: + AZ::Data::AssetData* CreateAsset(const AZ::Data::AssetId& id, const AZ::Data::AssetType& type) override; + void DestroyAsset(AZ::Data::AssetData* ptr) override; + void GetHandledAssetTypes(AZStd::vector& assetTypes) override; + AZ::Data::AssetHandler::LoadResult LoadAssetData( + const AZ::Data::Asset& asset, + AZStd::shared_ptr stream, + const AZ::Data::AssetFilterCB& assetLoadFilterCB) override; + + class AssetTypeInfoHandler; + AZStd::shared_ptr m_assetTypeInfoHandler; + }; +} diff --git a/Gems/Prefab/PrefabBuilder/PrefabGroupTests.cpp b/Gems/Prefab/PrefabBuilder/PrefabGroupTests.cpp new file mode 100644 index 0000000000..a63c0caa69 --- /dev/null +++ b/Gems/Prefab/PrefabBuilder/PrefabGroupTests.cpp @@ -0,0 +1,162 @@ +/* + * 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 + +namespace UnitTest +{ + TEST_F(PrefabBuilderTests, PrefabGroup_FindsRequiredReflection_True) + { + using namespace AZ::SceneAPI; + auto* serializeContext = m_app.GetSerializeContext(); + ASSERT_NE(nullptr, serializeContext); + SceneData::PrefabGroup::Reflect(serializeContext); + ASSERT_NE(nullptr, serializeContext->FindClassData(azrtti_typeid())); + ASSERT_NE(nullptr, serializeContext->FindClassData(azrtti_typeid())); + + auto findElementWithName = [](const AZ::SerializeContext::ClassData* classData, const char* name) + { + auto it = AZStd::find_if(classData->m_elements.begin(), classData->m_elements.end(), [name](const auto& element) + { + return strcmp(element.m_name, name) == 0; + }); + return it != classData->m_elements.end(); + }; + + auto* prefabGroupClassData = serializeContext->FindClassData(azrtti_typeid()); + EXPECT_TRUE(findElementWithName(prefabGroupClassData, "name")); + EXPECT_TRUE(findElementWithName(prefabGroupClassData, "nodeSelectionList")); + EXPECT_TRUE(findElementWithName(prefabGroupClassData, "rules")); + EXPECT_TRUE(findElementWithName(prefabGroupClassData, "id")); + EXPECT_TRUE(findElementWithName(prefabGroupClassData, "prefabDomBuffer")); + } + + TEST_F(PrefabBuilderTests, PrefabGroup_JsonWithPrefabArbitraryPrefab_Works) + { + using namespace AZ::SceneAPI; + auto* serializeContext = m_app.GetSerializeContext(); + ASSERT_NE(nullptr, serializeContext); + SceneData::PrefabGroup::Reflect(serializeContext); + + // fill out a PrefabGroup using JSON + AZStd::string_view input = R"JSON( + { + "name" : "tester", + "id" : "{49698DBC-B447-49EF-9B56-25BB29342AFB}", + "prefabDomBuffer" : "{\"foo\":\"bar\"}" + })JSON"; + + rapidjson::Document document; + document.Parse(input.data(), input.size()); + ASSERT_FALSE(document.HasParseError()); + + SceneData::PrefabGroup instancePrefabGroup; + AZ::JsonSerialization::Load(instancePrefabGroup, document); + + const auto& dom = instancePrefabGroup.GetPrefabDom(); + EXPECT_TRUE(dom.GetObject().HasMember("foo")); + EXPECT_STREQ(dom.GetObject().FindMember("foo")->value.GetString(), "bar"); + EXPECT_STREQ(instancePrefabGroup.GetName().c_str(), "tester"); + EXPECT_STREQ( + instancePrefabGroup.GetId().ToString().c_str(), + "{49698DBC-B447-49EF-9B56-25BB29342AFB}"); + EXPECT_TRUE(instancePrefabGroup.GetPrefabDom().IsObject()); + } + + TEST_F(PrefabBuilderTests, PrefabGroup_InvalidPrefabJson_Detected) + { + using namespace AZ::SceneAPI; + + AZStd::string_view input = R"JSON( + { + bad json that will not parse + })JSON"; + + rapidjson::Document document; + document.Parse(input.data(), input.size()); + + SceneData::PrefabGroup prefabGroup; + prefabGroup.SetId(AZStd::move(AZ::Uuid::CreateRandom())); + prefabGroup.SetName(AZStd::move("tester")); + prefabGroup.SetPrefabDom(AZStd::move(document)); + + const auto& dom = prefabGroup.GetPrefabDom(); + EXPECT_TRUE(dom.IsNull()); + EXPECT_STREQ("tester", prefabGroup.GetName().c_str()); + } + + TEST_F(PrefabBuilderTests, PrefabGroup_InvalidPrefabJsonBuffer_Detected) + { + using namespace AZ::SceneAPI; + + AZStd::string_view inputJson = R"JSON( + { + bad json that will not parse + })JSON"; + + SceneData::PrefabGroup prefabGroup; + prefabGroup.SetId(AZStd::move(AZ::Uuid::CreateRandom())); + prefabGroup.SetName(AZStd::move("tester")); + prefabGroup.SetPrefabDomBuffer(std::move(inputJson)); + + const auto& dom = prefabGroup.GetPrefabDom(); + EXPECT_TRUE(dom.IsNull()); + EXPECT_STREQ("tester", prefabGroup.GetName().c_str()); + } + + struct PrefabBuilderBehaviorTests + : public PrefabBuilderTests + { + void SetUp() override + { + using namespace AZ::SceneAPI; + + PrefabBuilderTests::SetUp(); + SceneData::PrefabGroup::Reflect(m_app.GetSerializeContext()); + SceneData::PrefabGroup::Reflect(m_app.GetBehaviorContext()); + m_scriptContext = AZStd::make_unique(); + m_scriptContext->BindTo(m_app.GetBehaviorContext()); + } + + void TearDown() override + { + m_scriptContext.reset(); + PrefabBuilderTests::TearDown(); + } + + void ExpectExecute(AZStd::string_view script) + { + EXPECT_TRUE(m_scriptContext->Execute(script.data())); + } + + AZStd::unique_ptr m_scriptContext; + }; + + TEST_F(PrefabBuilderBehaviorTests, PrefabGroup_PrefabGroupClass_Exists) + { + ExpectExecute("group = PrefabGroup()"); + ExpectExecute("assert(group)"); + ExpectExecute("assert(group.name)"); + ExpectExecute("assert(group.id)"); + ExpectExecute("assert(group.prefabDomBuffer)"); + } + + TEST_F(PrefabBuilderBehaviorTests, PrefabGroup_PrefabGroupAssignment_Works) + { + ExpectExecute("group = PrefabGroup()"); + ExpectExecute("group.name = 'tester'"); + ExpectExecute("group.id = Uuid.CreateString('{AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE}', 0)"); + ExpectExecute("group.prefabDomBuffer = '{}'"); + ExpectExecute("assert(group.name == 'tester')"); + ExpectExecute("assert(tostring(group.id) == '{AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE}')"); + ExpectExecute("assert(group.prefabDomBuffer == '{}')"); + } +} diff --git a/Gems/Prefab/PrefabBuilder/prefabbuilder_files.cmake b/Gems/Prefab/PrefabBuilder/prefabbuilder_files.cmake index 9cdf90d951..4f3bf860be 100644 --- a/Gems/Prefab/PrefabBuilder/prefabbuilder_files.cmake +++ b/Gems/Prefab/PrefabBuilder/prefabbuilder_files.cmake @@ -9,4 +9,11 @@ set(FILES PrefabBuilderComponent.h PrefabBuilderComponent.cpp + PrefabGroup/IPrefabGroup.h + PrefabGroup/PrefabGroup.cpp + PrefabGroup/PrefabGroup.h + PrefabGroup/PrefabGroupBehavior.cpp + PrefabGroup/PrefabGroupBehavior.h + PrefabGroup/ProceduralAssetHandler.cpp + PrefabGroup/ProceduralAssetHandler.h ) diff --git a/Gems/Prefab/PrefabBuilder/prefabbuilder_tests_files.cmake b/Gems/Prefab/PrefabBuilder/prefabbuilder_tests_files.cmake index 3f1ae0388b..faf2f88b5e 100644 --- a/Gems/Prefab/PrefabBuilder/prefabbuilder_tests_files.cmake +++ b/Gems/Prefab/PrefabBuilder/prefabbuilder_tests_files.cmake @@ -9,4 +9,7 @@ set(FILES PrefabBuilderTests.h PrefabBuilderTests.cpp + PrefabGroup/PrefabGroupTests.cpp + PrefabGroup/PrefabBehaviorTests.cpp + PrefabGroup/PrefabBehaviorTests.inl ) diff --git a/Gems/PythonAssetBuilder/Editor/Scripts/scene_api/scene_data.py b/Gems/PythonAssetBuilder/Editor/Scripts/scene_api/scene_data.py index 2c63e5f90c..2578efeae2 100755 --- a/Gems/PythonAssetBuilder/Editor/Scripts/scene_api/scene_data.py +++ b/Gems/PythonAssetBuilder/Editor/Scripts/scene_api/scene_data.py @@ -104,6 +104,15 @@ class SceneManifest(): self.manifest['values'].append(meshGroup) return meshGroup + def add_prefab_group(self, name, id, json) -> dict: + prefabGroup = {} + prefabGroup['$type'] = '{99FE3C6F-5B55-4D8B-8013-2708010EC715} PrefabGroup' + prefabGroup['name'] = name + prefabGroup['id'] = id + prefabGroup['prefabDomData'] = json + self.manifest['values'].append(prefabGroup) + return prefabGroup + def mesh_group_select_node(self, meshGroup, nodeName): meshGroup['nodeSelectionList']['selectedNodes'].append(nodeName) diff --git a/Gems/SceneProcessing/Code/Source/SceneBuilder/SceneBuilderWorker.cpp b/Gems/SceneProcessing/Code/Source/SceneBuilder/SceneBuilderWorker.cpp index c1a1252c6b..1d3cd60e49 100644 --- a/Gems/SceneProcessing/Code/Source/SceneBuilder/SceneBuilderWorker.cpp +++ b/Gems/SceneProcessing/Code/Source/SceneBuilder/SceneBuilderWorker.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -34,6 +35,9 @@ #include #include +#include +#include +#include #include #include @@ -81,6 +85,93 @@ namespace SceneBuilder return m_cachedFingerprint.c_str(); } + void SceneBuilderWorker::PopulateSourceDependencies(const AZStd::string& manifestJson, AZStd::vector& sourceFileDependencies) + { + auto readJsonOutcome = AZ::JsonSerializationUtils::ReadJsonString(manifestJson); + AZStd::string errorMsg; + if (!readJsonOutcome.IsSuccess()) + { + // This may be an old format xml file. We don't have any dependencies in the old format so there's no point trying to parse an xml + return; + } + + rapidjson::Document document = readJsonOutcome.TakeValue(); + + auto manifestObject = document.GetObject(); + auto valuesIterator = manifestObject.FindMember("values"); + auto valuesArray = valuesIterator->value.GetArray(); + + AZStd::vector paths; + AZ::SceneAPI::Events::AssetImportRequestBus::Broadcast( + &AZ::SceneAPI::Events::AssetImportRequestBus::Events::GetManifestDependencyPaths, paths); + + for (const auto& value : valuesArray) + { + auto object = value.GetObject(); + + for (const auto& path : paths) + { + rapidjson::Pointer pointer(path.c_str()); + + auto dependencyValue = pointer.Get(object); + + if (dependencyValue && dependencyValue->IsString()) + { + AZStd::string dependency = dependencyValue->GetString(); + + sourceFileDependencies.emplace_back(AssetBuilderSDK::SourceFileDependency( + dependency, AZ::Uuid::CreateNull(), AssetBuilderSDK::SourceFileDependency::SourceFileDependencyType::Absolute)); + } + } + } + } + + bool SceneBuilderWorker::ManifestDependencyCheck(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response) + { + AZStd::string manifestExtension; + AZStd::string generatedManifestExtension; + + AZ::SceneAPI::Events::AssetImportRequestBus::Broadcast( + &AZ::SceneAPI::Events::AssetImportRequestBus::Events::GetManifestExtension, manifestExtension); + AZ::SceneAPI::Events::AssetImportRequestBus::Broadcast( + &AZ::SceneAPI::Events::AssetImportRequestBus::Events::GetGeneratedManifestExtension, generatedManifestExtension); + + if (manifestExtension.empty() || generatedManifestExtension.empty()) + { + AZ_Error("SceneBuilderWorker", false, "Failed to get scene manifest extension"); + return false; + } + + AZ::SettingsRegistryInterface::FixedValueString assetCacheRoot; + AZ::SettingsRegistry::Get()->Get(assetCacheRoot, AZ::SettingsRegistryMergeUtils::FilePathKey_CacheRootFolder); + + auto manifestPath = (AZ::IO::Path(request.m_watchFolder) / (request.m_sourceFile + manifestExtension)); + auto generatedManifestPath = (AZ::IO::Path(assetCacheRoot) / (request.m_sourceFile + generatedManifestExtension)); + + auto populateDependenciesFunc = [&response](const AZStd::string& path) + { + auto readFileOutcome = AZ::Utils::ReadFile(path, AZ::SceneAPI::Containers::SceneManifest::MaxSceneManifestFileSizeInBytes); + if (!readFileOutcome.IsSuccess()) + { + AZ_Error("SceneBuilderWorker", false, "%s", readFileOutcome.GetError().c_str()); + return; + } + + PopulateSourceDependencies(readFileOutcome.TakeValue(), response.m_sourceFileDependencyList); + }; + + if (AZ::IO::FileIOBase::GetInstance()->Exists(manifestPath.Native().c_str())) + { + populateDependenciesFunc(manifestPath.Native()); + } + else if (AZ::IO::FileIOBase::GetInstance()->Exists(generatedManifestPath.Native().c_str())) + { + populateDependenciesFunc(generatedManifestPath.Native()); + } + + return true; + } + void SceneBuilderWorker::CreateJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response) { // Check for shutdown @@ -118,6 +209,11 @@ namespace SceneBuilder sourceFileDependencyInfo.m_sourceDependencyType = AssetBuilderSDK::SourceFileDependency::SourceFileDependencyType::Wildcards; response.m_sourceFileDependencyList.push_back(sourceFileDependencyInfo); + if (!ManifestDependencyCheck(request, response)) + { + return; + } + response.m_result = AssetBuilderSDK::CreateJobsResultCode::Success; } diff --git a/Gems/SceneProcessing/Code/Source/SceneBuilder/SceneBuilderWorker.h b/Gems/SceneProcessing/Code/Source/SceneBuilder/SceneBuilderWorker.h index 3ecc657a3a..32456f9b2d 100644 --- a/Gems/SceneProcessing/Code/Source/SceneBuilder/SceneBuilderWorker.h +++ b/Gems/SceneProcessing/Code/Source/SceneBuilder/SceneBuilderWorker.h @@ -11,6 +11,7 @@ #include #include #include +#include namespace AssetBuilderSDK { @@ -45,11 +46,15 @@ namespace SceneBuilder public: ~SceneBuilderWorker() override = default; + void CreateJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response); void ProcessJob(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response); void ShutDown() override; const char* GetFingerprint() const; + static void PopulateSourceDependencies( + const AZStd::string& manifestJson, AZStd::vector& sourceFileDependencies); + static bool ManifestDependencyCheck(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response); static AZ::Uuid GetUUID(); void PopulateProductDependencies(const AZ::SceneAPI::Events::ExportProduct& exportProduct, const char* watchFolder, AssetBuilderSDK::JobProduct& jobProduct) const; diff --git a/Gems/SceneProcessing/Code/Tests/SceneBuilder/SceneBuilderTests.cpp b/Gems/SceneProcessing/Code/Tests/SceneBuilder/SceneBuilderTests.cpp index 5d0105f080..ba1a186e5a 100644 --- a/Gems/SceneProcessing/Code/Tests/SceneBuilder/SceneBuilderTests.cpp +++ b/Gems/SceneProcessing/Code/Tests/SceneBuilder/SceneBuilderTests.cpp @@ -11,11 +11,14 @@ #include #include #include +#include #include #include #include +#include #include #include +#include using namespace AZ; using namespace SceneBuilder; @@ -193,3 +196,200 @@ TEST_F(SceneBuilderTests, SceneBuilderWorker_ExportProductDependencies_ProductAn TestSuccessCase(exportProduct, expectedPathDependencies, { dependencyId }); } + +struct ImportHandler + : SceneAPI::Events::AssetImportRequestBus::Handler +{ + ImportHandler() + { + BusConnect(); + } + + ~ImportHandler() override + { + BusDisconnect(); + } + + void GetManifestDependencyPaths(AZStd::vector& paths) override + { + paths.emplace_back("/scriptFilename"); + paths.emplace_back("/layer1/layer2/0/target"); + } + + void GetManifestExtension(AZStd::string& result) override + { + result = ".test"; + } + + void GetGeneratedManifestExtension(AZStd::string& result) override + { + result = ".test.gen"; + } +}; + +using SourceDependencyTests = UnitTest::ScopedAllocatorSetupFixture; + +namespace SourceDependencyJson +{ + constexpr const char* TestJson = R"JSON( +{ + "values": [ + { + "$type": "Test1", + "scriptFilename": "a/test/path.png" + }, + { + "$type": "Test2", + "layer1" : { + "layer2" : [ + { + "target": "value.png", + "otherData": "value2.png" + }, + { + "target" : "wrong.png" + } + ] + } + } + ] +} + )JSON"; +} + +TEST_F(SourceDependencyTests, SourceDependencyTest) +{ + ImportHandler handler; + AZStd::vector dependencies; + + SceneBuilderWorker::PopulateSourceDependencies(SourceDependencyJson::TestJson, dependencies); + + ASSERT_EQ(dependencies.size(), 2); + ASSERT_STREQ(dependencies[0].m_sourceFileDependencyPath.c_str(), "a/test/path.png"); + ASSERT_STREQ(dependencies[1].m_sourceFileDependencyPath.c_str(), "value.png"); +} + +struct SettingsRegistryMock : AZ::Interface::Registrar +{ + bool Get(FixedValueString& result, AZStd::string_view) const override + { + result = "cache"; + return true; + } + + void SetApplyPatchSettings(const AZ::JsonApplyPatchSettings& /*applyPatchSettings*/) override{} + void GetApplyPatchSettings(AZ::JsonApplyPatchSettings& /*applyPatchSettings*/) override{} + + MOCK_CONST_METHOD1(GetType, Type (AZStd::string_view)); + MOCK_CONST_METHOD2(Visit, bool (Visitor&, AZStd::string_view)); + MOCK_CONST_METHOD2(Visit, bool (const VisitorCallback&, AZStd::string_view)); + MOCK_METHOD1(RegisterNotifier, NotifyEventHandler (const NotifyCallback&)); + MOCK_METHOD1(RegisterNotifier, NotifyEventHandler (NotifyCallback&&)); + MOCK_METHOD1(RegisterPreMergeEvent, PreMergeEventHandler (const PreMergeEventCallback&)); + MOCK_METHOD1(RegisterPreMergeEvent, PreMergeEventHandler (PreMergeEventCallback&&)); + MOCK_METHOD1(RegisterPostMergeEvent, PostMergeEventHandler (const PostMergeEventCallback&)); + MOCK_METHOD1(RegisterPostMergeEvent, PostMergeEventHandler (PostMergeEventCallback&&)); + MOCK_CONST_METHOD2(Get, bool (bool&, AZStd::string_view)); + MOCK_CONST_METHOD2(Get, bool (s64&, AZStd::string_view)); + MOCK_CONST_METHOD2(Get, bool (u64&, AZStd::string_view)); + MOCK_CONST_METHOD2(Get, bool (double&, AZStd::string_view)); + MOCK_CONST_METHOD2(Get, bool (AZStd::string&, AZStd::string_view)); + MOCK_CONST_METHOD3(GetObject, bool (void*, Uuid, AZStd::string_view)); + MOCK_METHOD2(Set, bool (AZStd::string_view, bool)); + MOCK_METHOD2(Set, bool (AZStd::string_view, s64)); + MOCK_METHOD2(Set, bool (AZStd::string_view, u64)); + MOCK_METHOD2(Set, bool (AZStd::string_view, double)); + MOCK_METHOD2(Set, bool (AZStd::string_view, AZStd::string_view)); + MOCK_METHOD2(Set, bool (AZStd::string_view, const char*)); + MOCK_METHOD3(SetObject, bool (AZStd::string_view, const void*, Uuid)); + MOCK_METHOD1(Remove, bool (AZStd::string_view)); + MOCK_METHOD3(MergeCommandLineArgument, bool (AZStd::string_view, AZStd::string_view, const CommandLineArgumentSettings&)); + MOCK_METHOD2(MergeSettings, bool (AZStd::string_view, Format)); + MOCK_METHOD4(MergeSettingsFile, bool (AZStd::string_view, Format, AZStd::string_view, AZStd::vector*)); + MOCK_METHOD5(MergeSettingsFolder, bool (AZStd::string_view, const Specializations&, AZStd::string_view, AZStd::string_view, AZStd::vector*)); + MOCK_METHOD1(SetUseFileIO, void (bool)); +}; + +struct SourceDependencyMockedIOTests : UnitTest::ScopedAllocatorSetupFixture + , UnitTest::SetRestoreFileIOBaseRAII +{ + SourceDependencyMockedIOTests() + : UnitTest::SetRestoreFileIOBaseRAII(m_ioMock) + { + + } + + void SetUp() override + { + using namespace ::testing; + + ON_CALL(m_ioMock, Open(_, _, _)) + .WillByDefault(Invoke( + [](auto, auto, IO::HandleType& handle) + { + handle = 1234; + return AZ::IO::Result(AZ::IO::ResultCode::Success); + })); + + ON_CALL(m_ioMock, Size(An(), _)).WillByDefault(Invoke([](auto, AZ::u64& size) + { + size = strlen(SourceDependencyJson::TestJson); + return AZ::IO::ResultCode::Success; + })); + + EXPECT_CALL(m_ioMock, Read(_, _, _, _, _)) + .WillRepeatedly(Invoke( + [](auto, void* buffer, auto, auto, AZ::u64* bytesRead) + { + memcpy(buffer, SourceDependencyJson::TestJson, strlen(SourceDependencyJson::TestJson)); + *bytesRead = strlen(SourceDependencyJson::TestJson); + return AZ::IO::ResultCode::Success; + })); + + EXPECT_CALL(m_ioMock, Close(_)).WillRepeatedly(Return(AZ::IO::ResultCode::Success)); + } + + IO::NiceFileIOBaseMock m_ioMock; +}; + +TEST_F(SourceDependencyMockedIOTests, RegularManifestHasPriority) +{ + ImportHandler handler; + SettingsRegistryMock settingsRegistry; + + AssetBuilderSDK::CreateJobsRequest request; + AssetBuilderSDK::CreateJobsResponse response; + + request.m_sourceFile = "file.fbx"; + + using namespace ::testing; + + AZStd::string genPath = AZStd::string("cache").append(1, AZ_TRAIT_OS_PATH_SEPARATOR).append("file.fbx.test.gen"); + + EXPECT_CALL(m_ioMock, Exists(StrEq("file.fbx.test"))).WillRepeatedly(Return(true)); + EXPECT_CALL(m_ioMock, Exists(StrEq(genPath.c_str()))).Times(Exactly(0)); + + ASSERT_TRUE(SceneBuilderWorker::ManifestDependencyCheck(request, response)); + ASSERT_EQ(response.m_sourceFileDependencyList.size(), 2); +} + +TEST_F(SourceDependencyMockedIOTests, GeneratedManifestTest) +{ + ImportHandler handler; + SettingsRegistryMock settingsRegistry; + + AssetBuilderSDK::CreateJobsRequest request; + AssetBuilderSDK::CreateJobsResponse response; + + request.m_sourceFile = "file.fbx"; + + using namespace ::testing; + + AZStd::string genPath = AZStd::string("cache").append(1, AZ_TRAIT_OS_PATH_SEPARATOR).append("file.fbx.test.gen"); + + EXPECT_CALL(m_ioMock, Exists(StrEq("file.fbx.test"))).WillRepeatedly(Return(false)); + EXPECT_CALL(m_ioMock, Exists(StrEq(genPath.c_str()))).WillRepeatedly(Return(true)); + + ASSERT_TRUE(SceneBuilderWorker::ManifestDependencyCheck(request, response)); + ASSERT_EQ(response.m_sourceFileDependencyList.size(), 2); +} From a40af98394c543d2ad736fd76f5325028351f9c2 Mon Sep 17 00:00:00 2001 From: hultonha <82228511+hultonha@users.noreply.github.com> Date: Wed, 6 Oct 2021 14:52:59 +0100 Subject: [PATCH 6/6] Add focus camera and custom camera input (#4493) * add focus camera and custom camera input Signed-off-by: hultonha * very minor tidy before publishing PR Signed-off-by: hultonha * small updates after PR feedback Signed-off-by: hultonha --- .../EditorModularViewportCameraComposer.cpp | 43 +++++++-- .../EditorModularViewportCameraComposer.h | 2 + .../EditorPreferencesPageViewportCamera.cpp | 12 ++- .../EditorPreferencesPageViewportCamera.h | 1 + Code/Editor/EditorViewportSettings.cpp | 11 +++ Code/Editor/EditorViewportSettings.h | 3 + .../AzFramework/Viewport/CameraInput.cpp | 92 +++++++++++++++++-- .../AzFramework/Viewport/CameraInput.h | 54 +++++++++++ 8 files changed, 198 insertions(+), 20 deletions(-) diff --git a/Code/Editor/EditorModularViewportCameraComposer.cpp b/Code/Editor/EditorModularViewportCameraComposer.cpp index 29cf348ab5..c492010336 100644 --- a/Code/Editor/EditorModularViewportCameraComposer.cpp +++ b/Code/Editor/EditorModularViewportCameraComposer.cpp @@ -95,6 +95,7 @@ namespace SandboxEditor cameras.AddCamera(m_firstPersonPanCamera); cameras.AddCamera(m_firstPersonTranslateCamera); cameras.AddCamera(m_firstPersonScrollCamera); + cameras.AddCamera(m_firstPersonFocusCamera); cameras.AddCamera(m_pivotCamera); }); @@ -111,6 +112,7 @@ namespace SandboxEditor viewportId, &AzToolsFramework::ViewportInteraction::ViewportMouseCursorRequestBus::Events::BeginCursorCapture); } }; + const auto showCursor = [viewportId = m_viewportId] { if (SandboxEditor::CameraCaptureCursorForLook()) @@ -172,19 +174,34 @@ namespace SandboxEditor return SandboxEditor::CameraScrollSpeed(); }; + const auto pivotFn = [] + { + // use the manipulator transform as the pivot point + AZStd::optional entityPivot; + AzToolsFramework::EditorTransformComponentSelectionRequestBus::EventResult( + entityPivot, AzToolsFramework::GetEntityContextId(), + &AzToolsFramework::EditorTransformComponentSelectionRequestBus::Events::GetManipulatorTransform); + + if (entityPivot.has_value()) + { + return entityPivot->GetTranslation(); + } + + // otherwise just use the identity + return AZ::Vector3::CreateZero(); + }; + + m_firstPersonFocusCamera = + AZStd::make_shared(SandboxEditor::CameraFocusChannelId(), AzFramework::FocusLook); + + m_firstPersonFocusCamera->SetPivotFn(pivotFn); + m_pivotCamera = AZStd::make_shared(SandboxEditor::CameraPivotChannelId()); m_pivotCamera->SetPivotFn( - []([[maybe_unused]] const AZ::Vector3& position, [[maybe_unused]] const AZ::Vector3& direction) + [pivotFn]([[maybe_unused]] const AZ::Vector3& position, [[maybe_unused]] const AZ::Vector3& direction) { - // use the manipulator transform as the pivot point - AZStd::optional entityPivot; - AzToolsFramework::EditorTransformComponentSelectionRequestBus::EventResult( - entityPivot, AzToolsFramework::GetEntityContextId(), - &AzToolsFramework::EditorTransformComponentSelectionRequestBus::Events::GetManipulatorTransform); - - // otherwise just use the identity - return entityPivot.value_or(AZ::Transform::CreateIdentity()).GetTranslation(); + return pivotFn(); }); m_pivotRotateCamera = AZStd::make_shared(SandboxEditor::CameraPivotLookChannelId()); @@ -244,11 +261,17 @@ namespace SandboxEditor return SandboxEditor::CameraPanInvertedY(); }; + m_pivotFocusCamera = + AZStd::make_shared(SandboxEditor::CameraFocusChannelId(), AzFramework::FocusPivot); + + m_pivotFocusCamera->SetPivotFn(pivotFn); + m_pivotCamera->m_pivotCameras.AddCamera(m_pivotRotateCamera); m_pivotCamera->m_pivotCameras.AddCamera(m_pivotTranslateCamera); m_pivotCamera->m_pivotCameras.AddCamera(m_pivotDollyScrollCamera); m_pivotCamera->m_pivotCameras.AddCamera(m_pivotDollyMoveCamera); m_pivotCamera->m_pivotCameras.AddCamera(m_pivotPanCamera); + m_pivotCamera->m_pivotCameras.AddCamera(m_pivotFocusCamera); } void EditorModularViewportCameraComposer::OnEditorModularViewportCameraComposerSettingsChanged() @@ -257,12 +280,14 @@ namespace SandboxEditor m_firstPersonTranslateCamera->SetTranslateCameraInputChannelIds(translateCameraInputChannelIds); m_firstPersonPanCamera->SetPanInputChannelId(SandboxEditor::CameraFreePanChannelId()); m_firstPersonRotateCamera->SetRotateInputChannelId(SandboxEditor::CameraFreeLookChannelId()); + m_firstPersonFocusCamera->SetFocusInputChannelId(SandboxEditor::CameraFocusChannelId()); m_pivotCamera->SetPivotInputChannelId(SandboxEditor::CameraPivotChannelId()); m_pivotTranslateCamera->SetTranslateCameraInputChannelIds(translateCameraInputChannelIds); m_pivotPanCamera->SetPanInputChannelId(SandboxEditor::CameraPivotPanChannelId()); m_pivotRotateCamera->SetRotateInputChannelId(SandboxEditor::CameraPivotLookChannelId()); m_pivotDollyMoveCamera->SetDollyInputChannelId(SandboxEditor::CameraPivotDollyChannelId()); + m_pivotFocusCamera->SetFocusInputChannelId(SandboxEditor::CameraFocusChannelId()); } void EditorModularViewportCameraComposer::OnViewportViewEntityChanged(const AZ::EntityId& viewEntityId) diff --git a/Code/Editor/EditorModularViewportCameraComposer.h b/Code/Editor/EditorModularViewportCameraComposer.h index e691ca1c89..90103dba43 100644 --- a/Code/Editor/EditorModularViewportCameraComposer.h +++ b/Code/Editor/EditorModularViewportCameraComposer.h @@ -42,12 +42,14 @@ namespace SandboxEditor AZStd::shared_ptr m_firstPersonPanCamera; AZStd::shared_ptr m_firstPersonTranslateCamera; AZStd::shared_ptr m_firstPersonScrollCamera; + AZStd::shared_ptr m_firstPersonFocusCamera; AZStd::shared_ptr m_pivotCamera; AZStd::shared_ptr m_pivotRotateCamera; AZStd::shared_ptr m_pivotTranslateCamera; AZStd::shared_ptr m_pivotDollyScrollCamera; AZStd::shared_ptr m_pivotDollyMoveCamera; AZStd::shared_ptr m_pivotPanCamera; + AZStd::shared_ptr m_pivotFocusCamera; AzFramework::ViewportId m_viewportId; }; diff --git a/Code/Editor/EditorPreferencesPageViewportCamera.cpp b/Code/Editor/EditorPreferencesPageViewportCamera.cpp index 55b631e1f6..b632163186 100644 --- a/Code/Editor/EditorPreferencesPageViewportCamera.cpp +++ b/Code/Editor/EditorPreferencesPageViewportCamera.cpp @@ -91,7 +91,8 @@ void CEditorPreferencesPage_ViewportCamera::Reflect(AZ::SerializeContext& serial ->Field("FreePan", &CameraInputSettings::m_freePanChannelId) ->Field("PivotLook", &CameraInputSettings::m_pivotLookChannelId) ->Field("PivotDolly", &CameraInputSettings::m_pivotDollyChannelId) - ->Field("PivotPan", &CameraInputSettings::m_pivotPanChannelId); + ->Field("PivotPan", &CameraInputSettings::m_pivotPanChannelId) + ->Field("Focus", &CameraInputSettings::m_focusChannelId); serialize.Class() ->Version(1) @@ -206,14 +207,17 @@ void CEditorPreferencesPage_ViewportCamera::Reflect(AZ::SerializeContext& serial ->DataElement( AZ::Edit::UIHandlers::ComboBox, &CameraInputSettings::m_pivotPanChannelId, "Pivot Pan", "Key/button to begin camera pivot pan") + ->Attribute(AZ::Edit::Attributes::StringList, &GetEditorInputNames) + ->DataElement( + AZ::Edit::UIHandlers::ComboBox, &CameraInputSettings::m_focusChannelId, "Focus", "Key/button to focus camera pivot") ->Attribute(AZ::Edit::Attributes::StringList, &GetEditorInputNames); editContext->Class("Viewport Preferences", "Viewport Preferences") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::Visibility, AZ_CRC("PropertyVisibility_ShowChildrenOnly", 0xef428f20)) ->DataElement( - AZ::Edit::UIHandlers::Default, &CEditorPreferencesPage_ViewportCamera::m_cameraMovementSettings, - "Camera Movement Settings", "Camera Movement Settings") + AZ::Edit::UIHandlers::Default, &CEditorPreferencesPage_ViewportCamera::m_cameraMovementSettings, "Camera Movement Settings", + "Camera Movement Settings") ->DataElement( AZ::Edit::UIHandlers::Default, &CEditorPreferencesPage_ViewportCamera::m_cameraInputSettings, "Camera Input Settings", "Camera Input Settings"); @@ -281,6 +285,7 @@ void CEditorPreferencesPage_ViewportCamera::OnApply() SandboxEditor::SetCameraPivotLookChannelId(m_cameraInputSettings.m_pivotLookChannelId); SandboxEditor::SetCameraPivotDollyChannelId(m_cameraInputSettings.m_pivotDollyChannelId); SandboxEditor::SetCameraPivotPanChannelId(m_cameraInputSettings.m_pivotPanChannelId); + SandboxEditor::SetCameraFocusChannelId(m_cameraInputSettings.m_focusChannelId); SandboxEditor::EditorModularViewportCameraComposerNotificationBus::Broadcast( &SandboxEditor::EditorModularViewportCameraComposerNotificationBus::Events::OnEditorModularViewportCameraComposerSettingsChanged); @@ -316,4 +321,5 @@ void CEditorPreferencesPage_ViewportCamera::InitializeSettings() m_cameraInputSettings.m_pivotLookChannelId = SandboxEditor::CameraPivotLookChannelId().GetName(); m_cameraInputSettings.m_pivotDollyChannelId = SandboxEditor::CameraPivotDollyChannelId().GetName(); m_cameraInputSettings.m_pivotPanChannelId = SandboxEditor::CameraPivotPanChannelId().GetName(); + m_cameraInputSettings.m_focusChannelId = SandboxEditor::CameraFocusChannelId().GetName(); } diff --git a/Code/Editor/EditorPreferencesPageViewportCamera.h b/Code/Editor/EditorPreferencesPageViewportCamera.h index 01dc4664f6..cd31141c4a 100644 --- a/Code/Editor/EditorPreferencesPageViewportCamera.h +++ b/Code/Editor/EditorPreferencesPageViewportCamera.h @@ -86,6 +86,7 @@ private: AZStd::string m_pivotLookChannelId; AZStd::string m_pivotDollyChannelId; AZStd::string m_pivotPanChannelId; + AZStd::string m_focusChannelId; }; CameraMovementSettings m_cameraMovementSettings; diff --git a/Code/Editor/EditorViewportSettings.cpp b/Code/Editor/EditorViewportSettings.cpp index fe8efecd17..c2539b008c 100644 --- a/Code/Editor/EditorViewportSettings.cpp +++ b/Code/Editor/EditorViewportSettings.cpp @@ -50,6 +50,7 @@ namespace SandboxEditor constexpr AZStd::string_view CameraPivotLookIdSetting = "/Amazon/Preferences/Editor/Camera/PivotLookId"; constexpr AZStd::string_view CameraPivotDollyIdSetting = "/Amazon/Preferences/Editor/Camera/PivotDollyId"; constexpr AZStd::string_view CameraPivotPanIdSetting = "/Amazon/Preferences/Editor/Camera/PivotPanId"; + constexpr AZStd::string_view CameraFocusIdSetting = "/Amazon/Preferences/Editor/Camera/FocusId"; template void SetRegistry(const AZStd::string_view setting, T&& value) @@ -462,4 +463,14 @@ namespace SandboxEditor { SetRegistry(CameraPivotPanIdSetting, cameraPivotPanId); } + + AzFramework::InputChannelId CameraFocusChannelId() + { + return AzFramework::InputChannelId(GetRegistry(CameraFocusIdSetting, AZStd::string("keyboard_key_alphanumeric_X")).c_str()); + } + + void SetCameraFocusChannelId(AZStd::string_view cameraFocusId) + { + SetRegistry(CameraFocusIdSetting, cameraFocusId); + } } // namespace SandboxEditor diff --git a/Code/Editor/EditorViewportSettings.h b/Code/Editor/EditorViewportSettings.h index d1271017c6..0253b01621 100644 --- a/Code/Editor/EditorViewportSettings.h +++ b/Code/Editor/EditorViewportSettings.h @@ -136,4 +136,7 @@ namespace SandboxEditor SANDBOX_API AzFramework::InputChannelId CameraPivotPanChannelId(); SANDBOX_API void SetCameraPivotPanChannelId(AZStd::string_view cameraPivotPanId); + + SANDBOX_API AzFramework::InputChannelId CameraFocusChannelId(); + SANDBOX_API void SetCameraFocusChannelId(AZStd::string_view cameraFocusId); } // namespace SandboxEditor diff --git a/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.cpp b/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.cpp index a3eb4d8329..13f5303eaf 100644 --- a/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.cpp +++ b/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.cpp @@ -623,15 +623,24 @@ namespace AzFramework { Camera nextCamera = targetCamera; - const auto pivotDirection = targetCamera.m_offset.GetNormalized(); - nextCamera.m_offset -= pivotDirection * delta; - const auto pivotDot = targetCamera.m_offset.Dot(nextCamera.m_offset); - const auto distance = nextCamera.m_offset.GetLength() * AZ::GetSign(pivotDot); - - const auto minDistance = 0.01f; - if (distance < minDistance || pivotDot < 0.0f) + // handle case where pivot and offset may be the same to begin with + // choose negative y-axis for offset to default to moving the camera backwards from the pivot (standard centered pivot behavior) + const auto pivotDirection = [&targetCamera] { - nextCamera.m_offset = pivotDirection * minDistance; + if (const auto offsetLength = targetCamera.m_offset.GetLength(); AZ::IsCloseMag(offsetLength, 0.0f)) + { + return -AZ::Vector3::CreateAxisY(); + } + else + { + return targetCamera.m_offset / offsetLength; + } + }(); + + nextCamera.m_offset -= pivotDirection * delta; + if (pivotDirection.Dot(nextCamera.m_offset) < 0.0f) + { + nextCamera.m_offset = pivotDirection * 0.001f; } return nextCamera; @@ -771,6 +780,73 @@ namespace AzFramework return camera; } + FocusCameraInput::FocusCameraInput(const InputChannelId& focusChannelId, FocusOffsetFn offsetFn) + : m_focusChannelId(focusChannelId) + , m_offsetFn(offsetFn) + { + } + + bool FocusCameraInput::HandleEvents( + const InputEvent& event, [[maybe_unused]] const ScreenVector& cursorDelta, [[maybe_unused]] float scrollDelta) + { + if (const auto* input = AZStd::get_if(&event)) + { + if (input->m_channelId == m_focusChannelId && input->m_state == InputChannel::State::Began) + { + BeginActivation(); + } + } + + return !Idle(); + } + + Camera FocusCameraInput::StepCamera( + const Camera& targetCamera, + [[maybe_unused]] const ScreenVector& cursorDelta, + [[maybe_unused]] float scrollDelta, + [[maybe_unused]] float deltaTime) + { + if (Beginning()) + { + // as the camera starts, record the camera we would like to end up as + m_nextCamera.m_offset = m_offsetFn(m_pivotFn().GetDistance(targetCamera.Translation())); + const auto angles = + EulerAngles(AZ::Matrix3x3::CreateFromMatrix3x4(AZ::Matrix3x4::CreateLookAt(targetCamera.Translation(), m_pivotFn()))); + m_nextCamera.m_pitch = angles.GetX(); + m_nextCamera.m_yaw = angles.GetZ(); + m_nextCamera.m_pivot = targetCamera.m_pivot; + } + + // end the behavior when the camera is in alignment + if (AZ::IsCloseMag(targetCamera.m_pitch, m_nextCamera.m_pitch) && AZ::IsCloseMag(targetCamera.m_yaw, m_nextCamera.m_yaw)) + { + EndActivation(); + } + + return m_nextCamera; + } + + void FocusCameraInput::SetPivotFn(PivotFn pivotFn) + { + m_pivotFn = AZStd::move(pivotFn); + } + + void FocusCameraInput::SetFocusInputChannelId(const InputChannelId& focusChannelId) + { + m_focusChannelId = focusChannelId; + } + + bool CustomCameraInput::HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, const float scrollDelta) + { + return m_handleEventsFn(*this, event, cursorDelta, scrollDelta); + } + + Camera CustomCameraInput::StepCamera( + const Camera& targetCamera, const ScreenVector& cursorDelta, const float scrollDelta, const float deltaTime) + { + return m_stepCameraFn(*this, targetCamera, cursorDelta, scrollDelta, deltaTime); + } + InputEvent BuildInputEvent(const InputChannel& inputChannel, const WindowSize& windowSize) { const auto& inputChannelId = inputChannel.GetInputChannelId(); diff --git a/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.h b/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.h index c44d951292..9b1988f5ec 100644 --- a/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.h +++ b/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.h @@ -617,6 +617,60 @@ namespace AzFramework return true; } + //! Callback to use for FocusCameraInput when a free look camera is being used. + //! @note This is when offset is zero. + inline AZ::Vector3 FocusLook(float) + { + return AZ::Vector3::CreateZero(); + } + + //! Callback to use for FocusCameraInput when a pivot camera is being used. + //! @note This is when offset is non zero. + inline AZ::Vector3 FocusPivot(const float length) + { + return AZ::Vector3::CreateAxisY(-length); + } + + using FocusOffsetFn = AZStd::function; + + //! A focus behavior to align the camera view to the position returned by the pivot function. + //! @note This only alters the camera orientation, the translation is unaffected. + class FocusCameraInput : public CameraInput + { + public: + using PivotFn = AZStd::function; + + FocusCameraInput(const InputChannelId& focusChannelId, FocusOffsetFn offsetFn); + + // CameraInput overrides ... + bool HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override; + Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override; + + //! Override the default behavior for how a pivot point is calculated. + void SetPivotFn(PivotFn pivotFn); + + void SetFocusInputChannelId(const InputChannelId& focusChannelId); + + private: + InputChannelId m_focusChannelId; //!< Input channel to begin the focus camera input. + Camera m_nextCamera; + PivotFn m_pivotFn; + FocusOffsetFn m_offsetFn; + }; + + //! Provides a CameraInput type that can be implemented without needing to create a new type deriving from CameraInput. + //! This can be very useful for specific use cases that are less generally applicable. + class CustomCameraInput : public CameraInput + { + public: + // CameraInput overrides ... + bool HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override; + Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override; + + AZStd::function m_handleEventsFn; + AZStd::function m_stepCameraFn; + }; + //! Map from a generic InputChannel event to a camera specific InputEvent. InputEvent BuildInputEvent(const InputChannel& inputChannel, const WindowSize& windowSize); } // namespace AzFramework