Test the Terrain World configuration changes when parameters are chan… (#6063)

* Test the Terrain World configuration changes when parameters are changed in the component

Signed-off-by: John Jones-Steele <82226755+jjjoness@users.noreply.github.com>

* Changes from PR

Signed-off-by: John Jones-Steele <82226755+jjjoness@users.noreply.github.com>

* Before merge

Signed-off-by: John Jones-Steele <82226755+jjjoness@users.noreply.github.com>

* Before merge 2

Signed-off-by: John Jones-Steele <82226755+jjjoness@users.noreply.github.com>

* Changes from PR

Signed-off-by: John Jones-Steele <82226755+jjjoness@users.noreply.github.com>

* Change from PR

Signed-off-by: John Jones-Steele <82226755+jjjoness@users.noreply.github.com>

* Change before merge

Signed-off-by: John Jones-Steele <82226755+jjjoness@users.noreply.github.com>

* Change before merge 2

Signed-off-by: John Jones-Steele <82226755+jjjoness@users.noreply.github.com>

* Change before merge 3

Signed-off-by: John Jones-Steele <82226755+jjjoness@users.noreply.github.com>

* Change after merge to fix conflicts

Signed-off-by: John Jones-Steele <82226755+jjjoness@users.noreply.github.com>

* Fix merge conflict

Signed-off-by: John Jones-Steele <82226755+jjjoness@users.noreply.github.com>

* Fix merge conflict

Signed-off-by: John Jones-Steele <82226755+jjjoness@users.noreply.github.com>

* CHanges from PR

Signed-off-by: John Jones-Steele <82226755+jjjoness@users.noreply.github.com>
This commit is contained in:
John Jones-Steele
2021-12-08 14:09:56 +00:00
committed by GitHub
parent 5eb058f679
commit 9e7d5fe52b
7 changed files with 294 additions and 29 deletions
@@ -46,6 +46,18 @@ def get_component_type_id(component_name):
return component_type_id
def get_level_component_type_id(component_name):
"""
Gets the component_type_id from a given component name
:param component_name: String of component name to search for
:return component type ID
"""
type_ids_list = editor.EditorComponentAPIBus(bus.Broadcast, 'FindComponentTypeIdsByEntityType', [component_name],
entity.EntityType().Level)
component_type_id = type_ids_list[0]
return component_type_id
def add_level_component(component_name):
"""
Adds the specified component to the Level Inspector
@@ -145,6 +157,20 @@ def get_component_property_value(component, component_propertyPath):
print(f'FAILURE: Could not get value from {component_propertyPath}')
return None
def set_component_property_value(component, component_propertyPath, value):
"""
Given a component name and component property path, set component property value
:param component: Component object to act on.
:param componentPropertyPath: String of component property. (e.g. 'Settings|Visible')
:param value: new value for the variable being changed in the component
"""
componentPropertyObj = editor.EditorComponentAPIBus(bus.Broadcast, 'SetComponentProperty', component,
component_propertyPath, value)
if componentPropertyObj.IsSuccess():
print(f'{component_propertyPath} set to {value}')
else:
print(f'FAILURE: Could not set value in {component_propertyPath}')
def get_property_tree(component):
"""
@@ -0,0 +1,168 @@
"""
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():
level_components_added = ("Level components added correctly", "Failed to create level components")
create_terrain_spawner_entity = ("Terrain_spawner_entity created successfully", "Failed to create terrain_spawner_entity")
create_height_provider_entity = ("Height_provider_entity created successfully", "Failed to create height_provider_entity")
bounds_max_changed = ("Terrain World Bounds Max changed successfully", "Failed to change Terrain World Bounds Max")
bounds_min_changed = ("Terrain World Bounds Min changed successfully", "Failed to change Terrain World Bounds Min")
height_query_changed = ("Terrain World Height Query Resolution changed successfully", "Failed to change Height Query Resolution")
box_dimensions_changed = ("Aabb dimensions changed successfully", "Failed to change Aabb dimensions")
shape_changed = ("Shape changed successfully", "Failed Shape change")
frequency_changed = ("Frequency changed successfully", "Failed Frequency change")
entity_added = ("Entity added successfully", "Failed Entity add")
terrain_exists = ("Terrain exists at the provided point", "Terrain does not exist at the provided point")
terrain_does_not_exist = ("Terrain does not exist at the provided point", "Terrain exists at the provided point")
values_not_the_same = ("The tested values are not the same", "The tested values are the same")
no_errors_and_warnings_found = ("No errors and warnings found", "Found errors and warnings")
#fmt: on
def Terrain_World_ConfigurationWorks():
"""
Summary:
Test the Terrain World configuration changes when parameters are changed in the component
Test Steps:
Expected Behavior:
The Editor is stable there are no warnings or errors.
Test Steps:
1) Start the Tracer to catch any errors and warnings
2) Load the base level
3) Load the level components
4) Create 2 test entities, one parent at 512.0, 512.0, 50.0 and one child at the default position and add the required components
5) Set the base Terrain World values
6) Change the Axis Aligned Box Shape dimensions
7) Set the Shape Reference to terrain_spawner_entity
8) Set the FastNoise Gradient frequency to 0.01
9) Set the Gradient List to height_provider_entity
10) Disable and Enable the Terrain Gradient List so that it is recognised
11) Check terrain exists at a known position in the world
12) Check terrain does not exist at a known position outside the world
13) Check height value is the expected one when query resolution is changed
"""
from editor_python_test_tools.editor_entity_utils import EditorEntity
from editor_python_test_tools.utils import TestHelper as helper, Report
from editor_python_test_tools.utils import Report, Tracer
import editor_python_test_tools.hydra_editor_utils as hydra
import azlmbr.math as azmath
import azlmbr.legacy.general as general
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.terrain as terrain
import math
SET_BOX_X_SIZE = 2048.0
SET_BOX_Y_SIZE = 2048.0
SET_BOX_Z_SIZE = 100.0
CLAMP = 1
helper.init_idle()
# 1) Start the Tracer to catch any errors and warnings
with Tracer() as section_tracer:
# 2) Load the level
helper.open_level("", "Base")
helper.wait_for_condition(lambda: general.get_current_level_name() == "Base", 2.0)
# 3) Load the level components
terrain_world_component = hydra.add_level_component("Terrain World")
terrain_world_renderer = hydra.add_level_component("Terrain World Renderer")
Report.critical_result(Tests.level_components_added,
terrain_world_component is not None and terrain_world_renderer is not None)
# 4) Create 2 test entities, one parent at 512.0, 512.0, 50.0 and one child at the default position and add the required components
entity1_components_to_add = ["Axis Aligned Box Shape", "Terrain Layer Spawner", "Terrain Height Gradient List", "Terrain Physics Heightfield Collider"]
entity2_components_to_add = ["Shape Reference", "Gradient Transform Modifier", "FastNoise Gradient"]
terrain_spawner_entity = hydra.Entity("TerrainEntity")
terrain_spawner_entity.create_entity(azmath.Vector3(512.0, 512.0, 50.0), entity1_components_to_add)
Report.result(Tests.create_terrain_spawner_entity, terrain_spawner_entity.id.IsValid())
height_provider_entity = hydra.Entity("HeightProviderEntity")
height_provider_entity.create_entity(azmath.Vector3(0.0, 0.0, 0.0), entity2_components_to_add,terrain_spawner_entity.id)
Report.result(Tests.create_height_provider_entity, height_provider_entity.id.IsValid())
# Give everything a chance to finish initializing.
general.idle_wait_frames(1)
# 5) Set the base Terrain World values
world_bounds_max = azmath.Vector3(1100.0, 1100.0, 1100.0)
world_bounds_min = azmath.Vector3(10.0, 10.0, 10.0)
height_query_resolution = azmath.Vector2(1.0, 1.0)
hydra.set_component_property_value(terrain_world_component, "Configuration|World Bounds (Max)", world_bounds_max)
hydra.set_component_property_value(terrain_world_component, "Configuration|World Bounds (Min)", world_bounds_min)
hydra.set_component_property_value(terrain_world_component, "Configuration|Height Query Resolution (m)", height_query_resolution)
world_max = hydra.get_component_property_value(terrain_world_component, "Configuration|World Bounds (Max)")
world_min = hydra.get_component_property_value(terrain_world_component, "Configuration|World Bounds (Min)")
world_query = hydra.get_component_property_value(terrain_world_component, "Configuration|Height Query Resolution (m)")
Report.result(Tests.bounds_max_changed, world_max == world_bounds_max)
Report.result(Tests.bounds_min_changed, world_min == world_bounds_min)
Report.result(Tests.height_query_changed, world_query == height_query_resolution)
# 6) Change the Axis Aligned Box Shape dimensions
box_dimensions = azmath.Vector3(SET_BOX_X_SIZE, SET_BOX_Y_SIZE, SET_BOX_Z_SIZE)
terrain_spawner_entity.get_set_test(0, "Axis Aligned Box Shape|Box Configuration|Dimensions", box_dimensions)
box_shape_dimensions = hydra.get_component_property_value(terrain_spawner_entity.components[0], "Axis Aligned Box Shape|Box Configuration|Dimensions")
Report.result(Tests.box_dimensions_changed, box_dimensions == box_shape_dimensions)
# 7) Set the Shape Reference to terrain_spawner_entity
height_provider_entity.get_set_test(0, "Configuration|Shape Entity Id", terrain_spawner_entity.id)
entityId = hydra.get_component_property_value(height_provider_entity.components[0], "Configuration|Shape Entity Id")
Report.result(Tests.shape_changed, entityId == terrain_spawner_entity.id)
# 8) Set the FastNoise Gradient frequency to 0.01
frequency = 0.01
height_provider_entity.get_set_test(2, "Configuration|Frequency", frequency)
frequencyVal = hydra.get_component_property_value(height_provider_entity.components[2], "Configuration|Frequency")
Report.result(Tests.frequency_changed, math.isclose(frequency, frequencyVal, abs_tol = 0.00001))
# 9) Set the Gradient List to height_provider_entity
propertyTree = hydra.get_property_tree(terrain_spawner_entity.components[2])
propertyTree.add_container_item("Configuration|Gradient Entities", 0, height_provider_entity.id)
checkID = propertyTree.get_container_item("Configuration|Gradient Entities", 0)
Report.result(Tests.entity_added, checkID.GetValue() == height_provider_entity.id)
general.idle_wait_frames(1)
# 10) Disable and Enable the Terrain Gradient List so that it is recognised, EnableComponents performs both actions.
editor.EditorComponentAPIBus(bus.Broadcast, 'EnableComponents', [terrain_spawner_entity.components[2]])
# 11) Check terrain exists at a known position in the world
terrainExists = not terrain.TerrainDataRequestBus(bus.Broadcast, 'GetIsHoleFromFloats', 10.0, 10.0, CLAMP)
Report.result(Tests.terrain_exists, terrainExists)
terrainExists = not terrain.TerrainDataRequestBus(bus.Broadcast, 'GetIsHoleFromFloats', 1100.0, 1100.0, CLAMP)
Report.result(Tests.terrain_exists, terrainExists)
# 12) Check terrain does not exist at a known position outside the world
terrainDoesNotExist = terrain.TerrainDataRequestBus(bus.Broadcast, 'GetIsHoleFromFloats', 1101.0, 1101.0, CLAMP)
Report.result(Tests.terrain_does_not_exist, terrainDoesNotExist)
terrainDoesNotExist = terrain.TerrainDataRequestBus(bus.Broadcast, 'GetIsHoleFromFloats', 9.0, 9.0, CLAMP)
Report.result(Tests.terrain_does_not_exist, terrainDoesNotExist)
# 13) Check height value is the expected one when query resolution is changed
testpoint = terrain.TerrainDataRequestBus(bus.Broadcast, 'GetHeightFromFloats', 10.5, 10.5, CLAMP)
height_query_resolution = azmath.Vector2(0.5, 0.5)
hydra.set_component_property_value(terrain_world_component, "Configuration|Height Query Resolution (m)", height_query_resolution)
general.idle_wait_frames(1)
testpoint2 = terrain.TerrainDataRequestBus(bus.Broadcast, 'GetHeightFromFloats', 10.5, 10.5, CLAMP)
Report.result(Tests.values_not_the_same, not math.isclose(testpoint, testpoint2, abs_tol = 0.000000001))
helper.wait_for_condition(lambda: section_tracer.has_errors or section_tracer.has_asserts, 1.0)
for error_info in section_tracer.errors:
Report.info(f"Error: {error_info.filename} {error_info.function} | {error_info.message}")
for assert_info in section_tracer.asserts:
Report.info(f"Assert: {assert_info.filename} {assert_info.function} | {assert_info.message}")
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(Terrain_World_ConfigurationWorks)
@@ -36,3 +36,6 @@ class TestAutomation(EditorTestSuite):
class test_TerrainMacroMaterialComponent_MacroMaterialActivates(EditorSharedTest):
from .EditorScripts import TerrainMacroMaterialComponent_MacroMaterialActivates as test_module
class test_TerrainWorld_ConfigurationWorks(EditorSharedTest):
from .EditorScripts import Terrain_World_ConfigurationWorks as test_module
@@ -63,25 +63,30 @@ namespace AzFramework::Terrain
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
->Attribute(AZ::Script::Attributes::Category, "Terrain")
->Attribute(AZ::Script::Attributes::Module, "terrain")
->Event("GetNormal", &AzFramework::Terrain::TerrainDataRequestBus::Events::GetNormal)
->Event("GetMaxSurfaceWeight", &AzFramework::Terrain::TerrainDataRequestBus::Events::GetMaxSurfaceWeight)
->Event("GetMaxSurfaceWeightFromVector2",
&AzFramework::Terrain::TerrainDataRequestBus::Events::GetMaxSurfaceWeightFromVector2)
->Event("GetSurfaceWeights", &AzFramework::Terrain::TerrainDataRequestBus::Events::GetSurfaceWeights)
->Event("GetSurfaceWeightsFromVector2",
&AzFramework::Terrain::TerrainDataRequestBus::Events::GetSurfaceWeightsFromVector2)
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
->Event("GetHeight", &AzFramework::Terrain::TerrainDataRequestBus::Events::BehaviorContextGetHeight)
->Event("GetHeightFromFloats", &AzFramework::Terrain::TerrainDataRequestBus::Events::BehaviorContextGetHeightFromFloats)
->Event("GetHeightFromVector2", &AzFramework::Terrain::TerrainDataRequestBus::Events::BehaviorContextGetHeightFromVector2)
->Event("GetNormal", &AzFramework::Terrain::TerrainDataRequestBus::Events::BehaviorContextGetNormal)
->Event("GetMaxSurfaceWeight", &AzFramework::Terrain::TerrainDataRequestBus::Events::BehaviorContextGetMaxSurfaceWeight)
->Event(
"GetMaxSurfaceWeightFromVector2",
&AzFramework::Terrain::TerrainDataRequestBus::Events::BehaviorContextGetMaxSurfaceWeightFromVector2)
->Event("GetSurfaceWeights", &AzFramework::Terrain::TerrainDataRequestBus::Events::BehaviorContextGetSurfaceWeights)
->Event(
"GetSurfaceWeightsFromVector2",
&AzFramework::Terrain::TerrainDataRequestBus::Events::BehaviorContextGetSurfaceWeightsFromVector2)
->Event("GetIsHole", &AzFramework::Terrain::TerrainDataRequestBus::Events::GetIsHole)
->Event("GetIsHoleFromFloats", &AzFramework::Terrain::TerrainDataRequestBus::Events::GetIsHoleFromFloats)
->Event("GetSurfacePoint", &AzFramework::Terrain::TerrainDataRequestBus::Events::BehaviorContextGetSurfacePoint)
->Event("GetSurfacePointFromVector2",
->Event(
"GetSurfacePointFromVector2",
&AzFramework::Terrain::TerrainDataRequestBus::Events::BehaviorContextGetSurfacePointFromVector2)
->Event("GetTerrainAabb", &AzFramework::Terrain::TerrainDataRequestBus::Events::GetTerrainAabb)
->Event("GetTerrainHeightQueryResolution",
->Event(
"GetTerrainHeightQueryResolution",
&AzFramework::Terrain::TerrainDataRequestBus::Events::GetTerrainHeightQueryResolution)
->Event("GetHeight", &AzFramework::Terrain::TerrainDataRequestBus::Events::GetHeightVal)
->Event("GetHeightFromVector2", &AzFramework::Terrain::TerrainDataRequestBus::Events::GetHeightValFromVector2)
->Event("GetHeightFromFloats", &AzFramework::Terrain::TerrainDataRequestBus::Events::GetHeightValFromFloats)
;
;
behaviorContext->EBus<AzFramework::Terrain::TerrainDataNotificationBus>("TerrainDataNotificationBus")
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
@@ -150,24 +150,49 @@ namespace AzFramework
GetSurfacePointFromVector2(inPosition, result, sampleFilter);
return result;
}
// Functions without the optional bool* parameter that can be used from Python tests.
float GetHeightVal(AZ::Vector3 position, Sampler sampler = Sampler::BILINEAR) const
// Private variations of the GetHeight.., GetNormal..., GetMaxSurfaceWeight..., GetSurfaceWeights... APIs
// exposed to BehaviorContext that does not use the terrainExists "out" parameter.
float BehaviorContextGetHeight(const AZ::Vector3& position, Sampler sampler = Sampler::BILINEAR)
{
bool terrainExists;
return GetHeight(position, sampler, &terrainExists);
return GetHeight(position, sampler, nullptr);
}
float GetHeightValFromVector2(AZ::Vector2 position, Sampler sampler = Sampler::BILINEAR) const
float BehaviorContextGetHeightFromVector2(const AZ::Vector2& position, Sampler sampler = Sampler::BILINEAR)
{
bool terrainExists;
return GetHeightFromVector2(position, sampler, &terrainExists);
return GetHeightFromVector2(position, sampler, nullptr);
}
float GetHeightValFromFloats(float x, float y, Sampler sampler = Sampler::BILINEAR) const
float BehaviorContextGetHeightFromFloats(float x, float y, Sampler sampler = Sampler::BILINEAR)
{
bool terrainExists;
return GetHeightFromFloats(x, y, sampler, &terrainExists);
return GetHeightFromFloats(x, y, sampler, nullptr);
}
AZ::Vector3 BehaviorContextGetNormal(const AZ::Vector3& position, Sampler sampleFilter = Sampler::BILINEAR)
{
return GetNormal(position, sampleFilter, nullptr);
}
SurfaceData::SurfaceTagWeight BehaviorContextGetMaxSurfaceWeight(
const AZ::Vector3& position, Sampler sampleFilter = Sampler::BILINEAR)
{
return GetMaxSurfaceWeight(position, sampleFilter, nullptr);
}
SurfaceData::SurfaceTagWeight BehaviorContextGetMaxSurfaceWeightFromVector2(
const AZ::Vector2& inPosition, Sampler sampleFilter = Sampler::DEFAULT)
{
return GetMaxSurfaceWeightFromVector2(inPosition, sampleFilter, nullptr);
}
SurfaceData::SurfaceTagWeightList BehaviorContextGetSurfaceWeights(
const AZ::Vector3& inPosition,
Sampler sampleFilter = Sampler::DEFAULT)
{
SurfaceData::SurfaceTagWeightList list;
GetSurfaceWeights(inPosition, list, sampleFilter, nullptr);
return list;
}
SurfaceData::SurfaceTagWeightList BehaviorContextGetSurfaceWeightsFromVector2(
const AZ::Vector2& inPosition,
Sampler sampleFilter = Sampler::DEFAULT)
{
SurfaceData::SurfaceTagWeightList list;
GetSurfaceWeightsFromVector2(inPosition, list, sampleFilter, nullptr);
return list;
}
};
using TerrainDataRequestBus = AZ::EBus<TerrainDataRequests>;
@@ -161,11 +161,32 @@ void TerrainSystem::ClampPosition(float x, float y, AZ::Vector2& outPosition, AZ
outPosition = (normalizedPosition - normalizedDelta) * m_currentSettings.m_heightQueryResolution;
}
bool TerrainSystem::InWorldBounds(float x, float y) const
{
const float zTestValue = m_currentSettings.m_worldBounds.GetMin().GetX() +
((m_currentSettings.m_worldBounds.GetMax().GetX() - m_currentSettings.m_worldBounds.GetMin().GetX()) / 2.0f);
const AZ::Vector3 testValue{ x, y, zTestValue };
if (m_currentSettings.m_worldBounds.Contains(testValue))
{
return true;
}
return false;
}
float TerrainSystem::GetHeightSynchronous(float x, float y, Sampler sampler, bool* terrainExistsPtr) const
{
bool terrainExists = false;
float height = m_currentSettings.m_worldBounds.GetMin().GetZ();
if (!InWorldBounds(x, y))
{
if (terrainExistsPtr)
{
*terrainExistsPtr = terrainExists;
return height;
}
}
AZStd::shared_lock<AZStd::shared_mutex> lock(m_areaMutex);
switch (sampler)
@@ -298,6 +319,14 @@ AZ::Vector3 TerrainSystem::GetNormalSynchronous(float x, float y, Sampler sample
AZ::Vector3 outNormal = AZ::Vector3::CreateAxisZ();
if (!InWorldBounds(x, y))
{
if (terrainExistsPtr)
{
*terrainExistsPtr = terrainExists;
return outNormal;
}
}
const AZ::Vector2 range = (m_currentSettings.m_heightQueryResolution / 2.0f);
const AZ::Vector2 left (x - range.GetX(), y);
const AZ::Vector2 right(x + range.GetX(), y);
@@ -334,14 +363,14 @@ AZ::Vector3 TerrainSystem::GetNormalFromFloats(float x, float y, Sampler sampler
return GetNormalSynchronous(x, y, sampler, terrainExistsPtr);
}
AzFramework::SurfaceData::SurfaceTagWeight TerrainSystem::GetMaxSurfaceWeight(
const AZ::Vector3& position, Sampler sampleFilter, bool* terrainExistsPtr) const
{
return GetMaxSurfaceWeightFromFloats(position.GetX(), position.GetY(), sampleFilter, terrainExistsPtr);
}
AzFramework::SurfaceData::SurfaceTagWeight TerrainSystem::GetMaxSurfaceWeightFromVector2(const AZ::Vector2& inPosition, Sampler sampleFilter, bool* terrainExistsPtr) const
AzFramework::SurfaceData::SurfaceTagWeight TerrainSystem::GetMaxSurfaceWeightFromVector2(
const AZ::Vector2& inPosition, Sampler sampleFilter, bool* terrainExistsPtr) const
{
return GetMaxSurfaceWeightFromFloats(inPosition.GetX(), inPosition.GetY(), sampleFilter, terrainExistsPtr);
}
@@ -356,6 +385,15 @@ AzFramework::SurfaceData::SurfaceTagWeight TerrainSystem::GetMaxSurfaceWeightFro
AzFramework::SurfaceData::SurfaceTagWeightList weightSet;
if (!InWorldBounds(x, y))
{
if (terrainExistsPtr)
{
*terrainExistsPtr = false;
return {};
}
}
GetOrderedSurfaceWeights(x, y, sampleFilter, weightSet, terrainExistsPtr);
if (weightSet.empty())
@@ -465,7 +503,6 @@ void TerrainSystem::GetSurfaceWeightsFromVector2(
Sampler sampleFilter,
bool* terrainExistsPtr) const
{
GetOrderedSurfaceWeights(inPosition.GetX(), inPosition.GetY(), sampleFilter, outSurfaceWeights, terrainExistsPtr);
}
@@ -138,6 +138,7 @@ namespace Terrain
private:
void ClampPosition(float x, float y, AZ::Vector2& outPosition, AZ::Vector2& normalizedDelta) const;
bool InWorldBounds(float x, float y) const;
AZ::EntityId FindBestAreaEntityAtPosition(float x, float y, AZ::Aabb& bounds) const;
void GetOrderedSurfaceWeights(