From af2e0a117d45b86722b828fa9a588961f172a7c4 Mon Sep 17 00:00:00 2001 From: sphrose <82213493+sphrose@users.noreply.github.com> Date: Tue, 30 Nov 2021 08:10:55 +0000 Subject: [PATCH 1/2] LYN-6357 Terrain Macro Material tests Signed-off-by: sphrose <82213493+sphrose@users.noreply.github.com> --- AutomatedTesting/Assets/Textures/image.png | 3 + AutomatedTesting/Assets/Textures/normal.png | 3 + .../Gem/PythonTests/Terrain/TestSuite_Main.py | 3 + .../TerrainMacroMaterialComponent.cpp | 3 +- .../TerrainMacroMaterialBus.cpp | 93 +++++++++++++++++++ .../TerrainRenderer/TerrainMacroMaterialBus.h | 6 +- .../Code/Tests/TerrainMacroMaterialTests.cpp | 86 +++++++++++++++++ Gems/Terrain/Code/terrain_files.cmake | 1 + Gems/Terrain/Code/terrain_tests_files.cmake | 1 + 9 files changed, 197 insertions(+), 2 deletions(-) create mode 100644 AutomatedTesting/Assets/Textures/image.png create mode 100644 AutomatedTesting/Assets/Textures/normal.png create mode 100644 Gems/Terrain/Code/Source/TerrainRenderer/TerrainMacroMaterialBus.cpp create mode 100644 Gems/Terrain/Code/Tests/TerrainMacroMaterialTests.cpp diff --git a/AutomatedTesting/Assets/Textures/image.png b/AutomatedTesting/Assets/Textures/image.png new file mode 100644 index 0000000000..2f558c634e --- /dev/null +++ b/AutomatedTesting/Assets/Textures/image.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:011454252e40c927343cce16296412f02f45d1f345c75c036651bdcca473bda5 +size 2672 diff --git a/AutomatedTesting/Assets/Textures/normal.png b/AutomatedTesting/Assets/Textures/normal.png new file mode 100644 index 0000000000..d3355e6699 --- /dev/null +++ b/AutomatedTesting/Assets/Textures/normal.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7bafcc4aefab827e1414e64bcdde235500b51392e52c9ccd588b2d7a24b865a0 +size 20214 diff --git a/AutomatedTesting/Gem/PythonTests/Terrain/TestSuite_Main.py b/AutomatedTesting/Gem/PythonTests/Terrain/TestSuite_Main.py index 786651713c..41b6774046 100644 --- a/AutomatedTesting/Gem/PythonTests/Terrain/TestSuite_Main.py +++ b/AutomatedTesting/Gem/PythonTests/Terrain/TestSuite_Main.py @@ -27,3 +27,6 @@ class TestAutomation(EditorTestSuite): class test_Terrain_SupportsPhysics(EditorSharedTest): from .EditorScripts import Terrain_SupportsPhysics as test_module + + class test_TerrainMacroMaterialComponent_MacroMaterialActivates(EditorSharedTest): + from .EditorScripts import TerrainMacroMaterialComponent_MacroMaterialActivates as test_module \ No newline at end of file diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/Components/TerrainMacroMaterialComponent.cpp b/Gems/Terrain/Code/Source/TerrainRenderer/Components/TerrainMacroMaterialComponent.cpp index a65cbad50b..304d5aecd4 100644 --- a/Gems/Terrain/Code/Source/TerrainRenderer/Components/TerrainMacroMaterialComponent.cpp +++ b/Gems/Terrain/Code/Source/TerrainRenderer/Components/TerrainMacroMaterialComponent.cpp @@ -93,7 +93,8 @@ namespace Terrain void TerrainMacroMaterialComponent::Reflect(AZ::ReflectContext* context) { TerrainMacroMaterialConfig::Reflect(context); - + TerrainMacroMaterialRequests::Reflect(context); + AZ::SerializeContext* serialize = azrtti_cast(context); if (serialize) { diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainMacroMaterialBus.cpp b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainMacroMaterialBus.cpp new file mode 100644 index 0000000000..b1c6f2d54b --- /dev/null +++ b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainMacroMaterialBus.cpp @@ -0,0 +1,93 @@ +/* + * 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 "TerrainMacroMaterialBus.h" +#include +#include + +namespace Terrain +{ + // Create a handler that can be accessed from Python scripts to receive terrain change notifications. + class TerrainMacroMaterialNotificationHandler final + : public Terrain::TerrainMacroMaterialNotificationBus::Handler + , public AZ::BehaviorEBusHandler + { + public: + AZ_EBUS_BEHAVIOR_BINDER( + TerrainMacroMaterialNotificationHandler, + "{B0ED8B29-0E0D-4567-BEAF-C842C4DB2700}", + AZ::SystemAllocator, + OnTerrainMacroMaterialCreated, + OnTerrainMacroMaterialChanged, + OnTerrainMacroMaterialRegionChanged, + OnTerrainMacroMaterialDestroyed); + + void OnTerrainMacroMaterialCreated( + [[maybe_unused]] AZ::EntityId macroMaterialEntity, + [[maybe_unused]] const Terrain::MacroMaterialData& macroMaterial) override + { + Call(FN_OnTerrainMacroMaterialCreated); + } + + void OnTerrainMacroMaterialChanged( + [[maybe_unused]] AZ::EntityId macroMaterialEntity, + [[maybe_unused]] const Terrain::MacroMaterialData& macroMaterial) override + { + Call(FN_OnTerrainMacroMaterialChanged); + } + + void OnTerrainMacroMaterialRegionChanged( + [[maybe_unused]] AZ::EntityId macroMaterialEntity, + [[maybe_unused]] const AZ::Aabb& oldRegion, + [[maybe_unused]] const AZ::Aabb& newRegion) override + { + Call(FN_OnTerrainMacroMaterialRegionChanged); + } + + void OnTerrainMacroMaterialDestroyed([[maybe_unused]] AZ::EntityId macroMaterialEntity) override + { + Call(FN_OnTerrainMacroMaterialDestroyed); + } + + static void Reflect(AZ::ReflectContext* context) + { + if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) + { + behaviorContext->EBus("TerrainMacroMaterialAutomationBus") + ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation) + ->Attribute(AZ::Script::Attributes::Module, "terrain") + ->Handler(); + } + } + }; + + void TerrainMacroMaterialRequests::Reflect(AZ::ReflectContext* context) + { + if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) + { + behaviorContext->EBus("TerrainMacroMaterialRequestBus") + ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) + ->Attribute(AZ::Script::Attributes::Category, "Terrain") + ->Attribute(AZ::Script::Attributes::Module, "terrain") + ->Event("GetTerrainMacroMaterialData", &Terrain::TerrainMacroMaterialRequestBus::Events::GetTerrainMacroMaterialData) + ; + + behaviorContext->EBus("TerrainMacroMaterialNotificationBus") + ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) + ->Attribute(AZ::Script::Attributes::Category, "Terrain") + ->Attribute(AZ::Script::Attributes::Module, "terrain") + ->Event("OnTerrainMacroMaterialCreated", &Terrain::TerrainMacroMaterialNotifications::OnTerrainMacroMaterialCreated) + ->Event("OnTerrainMacroMaterialChanged", &Terrain::TerrainMacroMaterialNotifications::OnTerrainMacroMaterialChanged) + ->Event("OnTerrainMacroMaterialRegionChanged", &Terrain::TerrainMacroMaterialNotifications::OnTerrainMacroMaterialRegionChanged) + ->Event("OnTerrainMacroMaterialDestroyed", &Terrain::TerrainMacroMaterialNotifications::OnTerrainMacroMaterialDestroyed) + ; + + Terrain::TerrainMacroMaterialNotificationHandler::Reflect(context); + } + } +} // namespace Terrain diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainMacroMaterialBus.h b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainMacroMaterialBus.h index af1e755b32..9a7151da56 100644 --- a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainMacroMaterialBus.h +++ b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainMacroMaterialBus.h @@ -16,8 +16,10 @@ namespace Terrain { - struct MacroMaterialData + struct MacroMaterialData final { + AZ_RTTI(MacroMaterialData, "{DC68E20A-3251-4E4E-8BC7-F6A2521FEF46}"); + AZ::EntityId m_entityId; AZ::Aabb m_bounds = AZ::Aabb::CreateNull(); @@ -35,6 +37,8 @@ namespace Terrain : public AZ::ComponentBus { public: + static void Reflect(AZ::ReflectContext* context); + //////////////////////////////////////////////////////////////////////// // EBusTraits using MutexType = AZStd::recursive_mutex; diff --git a/Gems/Terrain/Code/Tests/TerrainMacroMaterialTests.cpp b/Gems/Terrain/Code/Tests/TerrainMacroMaterialTests.cpp new file mode 100644 index 0000000000..a3b772035f --- /dev/null +++ b/Gems/Terrain/Code/Tests/TerrainMacroMaterialTests.cpp @@ -0,0 +1,86 @@ +/* + * 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 + +using ::testing::NiceMock; +using ::testing::AtLeast; +using ::testing::_; + + +class TerrainMacroMaterialComponentTest + : public ::testing::Test +{ +protected: + AZ::ComponentApplication m_app; + + UnitTest::MockAxisAlignedBoxShapeComponent* m_shapeComponent; + + void SetUp() override + { + AZ::ComponentApplication::Descriptor appDesc; + appDesc.m_memoryBlocksByteSize = 20 * 1024 * 1024; + appDesc.m_recordingMode = AZ::Debug::AllocationRecords::RECORD_NO_RECORDS; + appDesc.m_stackRecordLevels = 20; + + m_app.Create(appDesc); + } + + void TearDown() override + { + m_app.Destroy(); + } + + AZStd::unique_ptr CreateEntity() + { + auto entity = AZStd::make_unique(); + entity->Init(); + + return entity; + } +}; + +TEST_F(TerrainMacroMaterialComponentTest, MissingRequiredComponentsActivateFailure) +{ + auto entity = CreateEntity(); + + auto macroMaterialComponent = entity->CreateComponent(); + m_app.RegisterComponentDescriptor(macroMaterialComponent->CreateDescriptor()); + + const AZ::Entity::DependencySortOutcome sortOutcome = entity->EvaluateDependenciesGetDetails(); + EXPECT_FALSE(sortOutcome.IsSuccess()); + + entity.reset(); +} + +TEST_F(TerrainMacroMaterialComponentTest, RequiredComponentsPresentEntityActivateSuccess) +{ + auto entity = CreateEntity(); + + auto macroMaterialComponent = entity->CreateComponent(); + m_app.RegisterComponentDescriptor(macroMaterialComponent->CreateDescriptor()); + + auto shapeComponent = entity->CreateComponent(); + m_app.RegisterComponentDescriptor(shapeComponent->CreateDescriptor()); + + entity->Activate(); + EXPECT_EQ(entity->GetState(), AZ::Entity::State::Active); + + entity.reset(); +} diff --git a/Gems/Terrain/Code/terrain_files.cmake b/Gems/Terrain/Code/terrain_files.cmake index 893477e10d..3feacf6254 100644 --- a/Gems/Terrain/Code/terrain_files.cmake +++ b/Gems/Terrain/Code/terrain_files.cmake @@ -33,6 +33,7 @@ set(FILES Source/TerrainRenderer/TerrainFeatureProcessor.cpp Source/TerrainRenderer/TerrainFeatureProcessor.h Source/TerrainRenderer/TerrainAreaMaterialRequestBus.h + Source/TerrainRenderer/TerrainMacroMaterialBus.cpp Source/TerrainRenderer/TerrainMacroMaterialBus.h Source/TerrainSystem/TerrainSystem.cpp Source/TerrainSystem/TerrainSystem.h diff --git a/Gems/Terrain/Code/terrain_tests_files.cmake b/Gems/Terrain/Code/terrain_tests_files.cmake index 3e37e509a7..88de53f8cf 100644 --- a/Gems/Terrain/Code/terrain_tests_files.cmake +++ b/Gems/Terrain/Code/terrain_tests_files.cmake @@ -14,5 +14,6 @@ set(FILES Tests/SurfaceMaterialsListTest.cpp Tests/MockAxisAlignedBoxShapeComponent.h Tests/TerrainHeightGradientListTests.cpp + Tests/TerrainMacroMaterialTests.cpp Tests/TerrainSurfaceGradientListTests.cpp ) From 502a9e9e19b950157396947a4196db88a7621fd1 Mon Sep 17 00:00:00 2001 From: sphrose <82213493+sphrose@users.noreply.github.com> Date: Tue, 30 Nov 2021 08:13:45 +0000 Subject: [PATCH 2/2] missed file Signed-off-by: sphrose <82213493+sphrose@users.noreply.github.com> --- ...aterialComponent_MacroMaterialActivates.py | 166 ++++++++++++++++++ 1 file changed, 166 insertions(+) create mode 100644 AutomatedTesting/Gem/PythonTests/Terrain/EditorScripts/TerrainMacroMaterialComponent_MacroMaterialActivates.py diff --git a/AutomatedTesting/Gem/PythonTests/Terrain/EditorScripts/TerrainMacroMaterialComponent_MacroMaterialActivates.py b/AutomatedTesting/Gem/PythonTests/Terrain/EditorScripts/TerrainMacroMaterialComponent_MacroMaterialActivates.py new file mode 100644 index 0000000000..71ff02739a --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/Terrain/EditorScripts/TerrainMacroMaterialComponent_MacroMaterialActivates.py @@ -0,0 +1,166 @@ +""" +Copyright (c) Contributors to the Open 3D Engine Project. +For complete copyright and license terms please see the LICENSE at the root of this distribution. +SPDX-License-Identifier: Apache-2.0 OR MIT +""" + +class MacroMaterialTests: + setup_test = ( + "Setup successful", + "Setup failed" + ) + material_changed_not_called_when_inactive = ( + "OnTerrainMacroMaterialRegionChanged not called successfully", + "OnTerrainMacroMaterialRegionChanged called when component inactive." + ) + material_created = ( + "MaterialCreated called successfully", + "MaterialCreated failed" + ) + material_destroyed = ( + "MaterialDestroyed called successfully", + "MaterialDestroyed failed" + ) + material_recreated = ( + "MaterialCreated called successfully on second test", + "MaterialCreated failed on second test" + ) + material_changed_call_on_aabb_change = ( + "OnTerrainMacroMaterialRegionChanged called successfully", + "Timed out waiting for OnTerrainMacroMaterialRegionChanged" + ) + +def TerrainMacroMaterialComponent_MacroMaterialActivates(): + """ + Summary: + Load an empty level, create a MacroMaterialComponent and check assigning textures results in the correct callbacks. + :return: None + """ + + import os + import math as sys_math + + import azlmbr.legacy.general as general + import azlmbr.asset as asset + import azlmbr.bus as bus + import azlmbr.math as math + import azlmbr.terrain as terrain + import azlmbr.editor as editor + import azlmbr.vegetation as vegetation + import azlmbr.entity as EntityId + + import editor_python_test_tools.hydra_editor_utils as hydra + from editor_python_test_tools.utils import Report + from editor_python_test_tools.utils import TestHelper as helper + import editor_python_test_tools.pyside_utils as pyside_utils + from editor_python_test_tools.editor_entity_utils import EditorEntity + from editor_python_test_tools.asset_utils import Asset + + material_created_called = False + material_changed_called = False + material_region_changed_called = False + material_destroyed_called = False + + def create_entity_at(entity_name, components_to_add, x, y, z): + entity = EditorEntity.create_editor_entity_at([x, y, z], entity_name) + for component in components_to_add: + entity.add_component(component) + + return entity + + def on_macro_material_created(args): + nonlocal material_created_called + material_created_called = True + + def on_macro_material_changed(args): + nonlocal material_changed_called + material_changed_called = True + + def on_macro_material_region_changed(args): + nonlocal material_region_changed_called + material_region_changed_called = True + + def on_macro_material_destroyed(args): + nonlocal material_destroyed_called + material_destroyed_called = True + + helper.init_idle() + + # Open a level. + helper.open_level("Physics", "Base") + helper.wait_for_condition(lambda: general.get_current_level_name() == "Base", 2.0) + + general.idle_wait_frames(1) + + # Set up a handler to wait for notifications from the TerrainSystem. + handler = terrain.TerrainMacroMaterialAutomationBusHandler() + handler.connect() + handler.add_callback("OnTerrainMacroMaterialCreated", on_macro_material_created) + handler.add_callback("OnTerrainMacroMaterialChanged", on_macro_material_changed) + handler.add_callback("OnTerrainMacroMaterialRegionChanged", on_macro_material_region_changed) + handler.add_callback("OnTerrainMacroMaterialDestroyed", on_macro_material_destroyed) + + macro_material_entity = create_entity_at("macro", ["Terrain Macro Material", "Axis Aligned Box Shape"], 0.0, 0.0, 0.0) + + # Check that no macro material callbacks happened. It should be "inactive" as it has no assets assigned. + setup_success = not material_created_called and not material_changed_called and not material_region_changed_called and not material_destroyed_called + Report.result(MacroMaterialTests.setup_test, setup_success) + + # Find the aabb component. + aabb_component_type_id_type = azlmbr.editor.EditorComponentAPIBus(azlmbr.bus.Broadcast, 'FindComponentTypeIdsByEntityType', ["Axis Aligned Box Shape"], 0)[0] + aabb_component_id = azlmbr.editor.EditorComponentAPIBus(azlmbr.bus.Broadcast, 'GetComponentOfType', macro_material_entity.id, aabb_component_type_id_type).GetValue() + + # Change the aabb dimensions + material_region_changed_called = False + box_dimensions_path = "Axis Aligned Box Shape|Box Configuration|Dimensions" + editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", aabb_component_id, box_dimensions_path, math.Vector3(1.0, 1.0, 1.0)) + + # Check we don't receive a callback. The macro material component should be inactive as it has no images assigned. + general.idle_wait_frames(1) + Report.result(MacroMaterialTests.material_changed_not_called_when_inactive, material_region_changed_called == False) + + # Find the macro material component. + macro_material_id_type = azlmbr.editor.EditorComponentAPIBus(azlmbr.bus.Broadcast, 'FindComponentTypeIdsByEntityType', ["Terrain Macro Material"], 0)[0] + macro_material_component_id = azlmbr.editor.EditorComponentAPIBus(azlmbr.bus.Broadcast, 'GetComponentOfType', macro_material_entity.id, macro_material_id_type).GetValue() + + # Find a color image asset. + color_image_path = os.path.join("assets", "textures", "image.png.streamingimage") + color_image_asset = asset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", color_image_path, math.Uuid(), False) + + # Assign the image to the MacroMaterial component, which should result in a created message. + material_created_called = False + color_texture_path = "Configuration|Color Texture" + editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", macro_material_component_id, color_texture_path, color_image_asset) + + call_result = helper.wait_for_condition(lambda: material_created_called == True, 2.0) + Report.result(MacroMaterialTests.material_created, call_result) + + # Find a normal image asset. + normal_image_path = os.path.join("assets", "textures", "normal.png.streamingimage") + normal_image_asset = asset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", normal_image_path, math.Uuid(), False) + + # Assign the normal image to the MacroMaterial component, which should result in a created message. + material_created_called = False + material_destroyed_called = False + normal_texture_path = "Configuration|Normal Texture" + editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", macro_material_component_id, normal_texture_path, normal_image_asset) + + # Check the MacroMaterial was destroyed and recreated. + destroyed_call_result = helper.wait_for_condition(lambda: material_destroyed_called == True, 2.0) + Report.result(MacroMaterialTests.material_destroyed, destroyed_call_result) + + recreated_call_result = helper.wait_for_condition(lambda: material_created_called == True, 2.0) + Report.result(MacroMaterialTests.material_recreated, recreated_call_result) + + # Change the aabb dimensions. + box_dimensions_path = "Axis Aligned Box Shape|Box Configuration|Dimensions" + editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", aabb_component_id, box_dimensions_path, math.Vector3(1.0, 1.0, 1.0)) + + # Check that a callback is received. + region_changed_call_result = helper.wait_for_condition(lambda: material_region_changed_called == True, 2.0) + Report.result(MacroMaterialTests.material_changed_call_on_aabb_change, region_changed_call_result) + +if __name__ == "__main__": + + from editor_python_test_tools.utils import Report + Report.start_test(TerrainMacroMaterialComponent_MacroMaterialActivates) \ No newline at end of file