Merge pull request #5659 from aws-lumberyard-dev/terrain/sphrose/macro_material_tests

LYN-6357 Terrain Macro Material tests
This commit is contained in:
sphrose
2021-12-07 14:11:23 +00:00
committed by GitHub
10 changed files with 362 additions and 2 deletions
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:011454252e40c927343cce16296412f02f45d1f345c75c036651bdcca473bda5
size 2672
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7bafcc4aefab827e1414e64bcdde235500b51392e52c9ccd588b2d7a24b865a0
size 20214
@@ -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)
@@ -34,3 +34,5 @@ class TestAutomation(EditorTestSuite):
class test_TerrainSystem_VegetationSpawnsOnTerrainSurfaces(EditorSharedTest):
from .EditorScripts import TerrainSystem_VegetationSpawnsOnTerrainSurfaces as test_module
class test_TerrainMacroMaterialComponent_MacroMaterialActivates(EditorSharedTest):
from .EditorScripts import TerrainMacroMaterialComponent_MacroMaterialActivates as test_module
@@ -93,7 +93,8 @@ namespace Terrain
void TerrainMacroMaterialComponent::Reflect(AZ::ReflectContext* context)
{
TerrainMacroMaterialConfig::Reflect(context);
TerrainMacroMaterialRequests::Reflect(context);
AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
{
@@ -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 <AzCore/Serialization/SerializeContext.h>
#include <AzCore/RTTI/BehaviorContext.h>
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<AZ::BehaviorContext*>(context))
{
behaviorContext->EBus<Terrain::TerrainMacroMaterialNotificationBus>("TerrainMacroMaterialAutomationBus")
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
->Attribute(AZ::Script::Attributes::Module, "terrain")
->Handler<TerrainMacroMaterialNotificationHandler>();
}
}
};
void TerrainMacroMaterialRequests::Reflect(AZ::ReflectContext* context)
{
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->EBus<Terrain::TerrainMacroMaterialRequestBus>("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<Terrain::TerrainMacroMaterialNotificationBus>("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
@@ -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;
@@ -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 <AzCore/Component/ComponentApplication.h>
#include <AzCore/Component/TransformBus.h>
#include <AzCore/Memory/MemoryComponent.h>
#include <AzFramework/Terrain/TerrainDataRequestBus.h>
#include <TerrainRenderer/Components/TerrainMacroMaterialComponent.h>
#include <LmbrCentral/Shape/BoxShapeComponentBus.h>
#include <AzTest/AzTest.h>
#include <Terrain/MockTerrain.h>
#include <MockAxisAlignedBoxShapeComponent.h>
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<AZ::Entity> CreateEntity()
{
auto entity = AZStd::make_unique<AZ::Entity>();
entity->Init();
return entity;
}
};
TEST_F(TerrainMacroMaterialComponentTest, MissingRequiredComponentsActivateFailure)
{
auto entity = CreateEntity();
auto macroMaterialComponent = entity->CreateComponent<Terrain::TerrainMacroMaterialComponent>();
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<Terrain::TerrainMacroMaterialComponent>();
m_app.RegisterComponentDescriptor(macroMaterialComponent->CreateDescriptor());
auto shapeComponent = entity->CreateComponent<UnitTest::MockAxisAlignedBoxShapeComponent>();
m_app.RegisterComponentDescriptor(shapeComponent->CreateDescriptor());
entity->Activate();
EXPECT_EQ(entity->GetState(), AZ::Entity::State::Active);
entity.reset();
}
+1
View File
@@ -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
@@ -14,5 +14,6 @@ set(FILES
Tests/SurfaceMaterialsListTest.cpp
Tests/MockAxisAlignedBoxShapeComponent.h
Tests/TerrainHeightGradientListTests.cpp
Tests/TerrainMacroMaterialTests.cpp
Tests/TerrainSurfaceGradientListTests.cpp
)