merging latest development

Signed-off-by: kberg-amzn <karlberg@amazon.com>
This commit is contained in:
kberg-amzn
2021-09-20 13:28:57 -07:00
257 changed files with 8431 additions and 7127 deletions
@@ -81,7 +81,8 @@ def launch_and_validate_results(request, test_directory, editor, editor_script,
def launch_and_validate_results_launcher(launcher, level, remote_console_instance, expected_lines, null_renderer=True,
port_listener_timeout=120, log_monitor_timeout=300, remote_console_port=4600):
port_listener_timeout=120, log_monitor_timeout=300, remote_console_port=4600,
launch_ap=True):
"""
Runs the launcher with the specified level, and monitors Game.log for expected lines.
:param launcher: Configured launcher object to run test against.
@@ -92,6 +93,7 @@ def launch_and_validate_results_launcher(launcher, level, remote_console_instanc
:param port_listener_timeout: Timeout for verifying successful connection to Remote Console.
:param log_monitor_timeout: Timeout for monitoring for lines in Game.log
:param remote_console_port: The port used to communicate with the Remote Console.
:param launch_ap: Whether or not to launch AP. Defaults to True.
"""
def _check_for_listening_port(port):
@@ -110,7 +112,7 @@ def launch_and_validate_results_launcher(launcher, level, remote_console_instanc
launcher.args.extend(["-rhi=Null"])
# Start the Launcher
with launcher.start():
with launcher.start(launch_ap=launch_ap):
# Ensure Remote Console can be reached
waiter.wait_for(
@@ -14,8 +14,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_
NAME AutomatedTesting::DynamicVegetationTests_Main
TEST_SERIAL
TEST_SUITE main
PATH ${CMAKE_CURRENT_LIST_DIR}/dyn_veg
PYTEST_MARKS "not SUITE_sandbox and not SUITE_periodic and not SUITE_benchmark"
PATH ${CMAKE_CURRENT_LIST_DIR}/dyn_veg/TestSuite_Main.py
RUNTIME_DEPENDENCIES
AZ::AssetProcessor
Legacy::Editor
@@ -27,104 +26,33 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_
ly_add_pytest(
NAME AutomatedTesting::DynamicVegetationTests_Sandbox
NAME AutomatedTesting::DynamicVegetationTests_Periodic
TEST_SERIAL
TEST_SUITE sandbox
PATH ${CMAKE_CURRENT_LIST_DIR}/dyn_veg
PYTEST_MARKS "SUITE_sandbox"
TEST_SUITE periodic
PATH ${CMAKE_CURRENT_LIST_DIR}/dyn_veg/TestSuite_Periodic.py
RUNTIME_DEPENDENCIES
AZ::AssetProcessor
Legacy::Editor
AutomatedTesting.Assets
AutomatedTesting.GameLauncher
AutomatedTesting.Assets
COMPONENT
LargeWorlds
)
ly_add_pytest(
NAME AutomatedTesting::DynamicVegetationFilterTests_Periodic
NAME AutomatedTesting::DynamicVegetationTests_Main_Optimized
TEST_SERIAL
TEST_SUITE periodic
PATH ${CMAKE_CURRENT_LIST_DIR}/dyn_veg
PYTEST_MARKS "SUITE_periodic and dynveg_filter"
TEST_SUITE main
PATH ${CMAKE_CURRENT_LIST_DIR}/dyn_veg/TestSuite_Main_Optimized.py
RUNTIME_DEPENDENCIES
AZ::AssetProcessor
Legacy::Editor
AutomatedTesting.Assets
AZ::AssetProcessor
Legacy::Editor
AutomatedTesting.Assets
AutomatedTesting.GameLauncher
COMPONENT
LargeWorlds
)
ly_add_pytest(
NAME AutomatedTesting::DynamicVegetationModifierTests_Periodic
TEST_SERIAL
TEST_SUITE periodic
PATH ${CMAKE_CURRENT_LIST_DIR}/dyn_veg
PYTEST_MARKS "SUITE_periodic and dynveg_modifier"
RUNTIME_DEPENDENCIES
AZ::AssetProcessor
Legacy::Editor
AutomatedTesting.Assets
COMPONENT
LargeWorlds
)
ly_add_pytest(
NAME AutomatedTesting::DynamicVegetationRegressionTests_Periodic
TEST_SERIAL
TEST_SUITE periodic
PATH ${CMAKE_CURRENT_LIST_DIR}/dyn_veg
PYTEST_MARKS "SUITE_periodic and dynveg_regression"
RUNTIME_DEPENDENCIES
AZ::AssetProcessor
Legacy::Editor
AutomatedTesting.Assets
COMPONENT
LargeWorlds
)
ly_add_pytest(
NAME AutomatedTesting::DynamicVegetationAreaTests_Periodic
TEST_SERIAL
TEST_SUITE periodic
PATH ${CMAKE_CURRENT_LIST_DIR}/dyn_veg
PYTEST_MARKS "SUITE_periodic and dynveg_area"
RUNTIME_DEPENDENCIES
AZ::AssetProcessor
Legacy::Editor
AutomatedTesting.Assets
COMPONENT
LargeWorlds
)
ly_add_pytest(
NAME AutomatedTesting::DynamicVegetationMiscTests_Periodic
TEST_SERIAL
TEST_SUITE periodic
PATH ${CMAKE_CURRENT_LIST_DIR}/dyn_veg
PYTEST_MARKS "SUITE_periodic and dynveg_misc"
RUNTIME_DEPENDENCIES
AZ::AssetProcessor
Legacy::Editor
AutomatedTesting.Assets
COMPONENT
LargeWorlds
)
ly_add_pytest(
NAME AutomatedTesting::DynamicVegetationSurfaceTagTests_Periodic
TEST_SERIAL
TEST_SUITE periodic
PATH ${CMAKE_CURRENT_LIST_DIR}/dyn_veg
PYTEST_MARKS "SUITE_periodic and dynveg_surfacetagemitter"
RUNTIME_DEPENDENCIES
AZ::AssetProcessor
Legacy::Editor
AutomatedTesting.Assets
COMPONENT
LargeWorlds
)
## LandscapeCanvas ##
ly_add_pytest(
@@ -5,119 +5,119 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
"""
C4814463 - Altitude Filter overrides function as expected
C4847477 - Altitude Min/Max can be manually set
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.editor as editor
import azlmbr.legacy.general as general
import azlmbr.bus as bus
import azlmbr.math as math
import azlmbr.paths
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.editor_test_helper import EditorTestHelper
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
class Tests:
prefilter_instance_count = (
"Pre-filter instance counts are accurate",
"Unexpected number of pre-filter instances found"
)
postfilter_instance_count = (
"Post-filter instance counts are accurate",
"Unexpected number of post-filter instances found"
)
postfilter_overrides_instance_count = (
"Override instance counts are accurate",
"Unexpected number of override instances found"
)
class TestAltitudeFilterComponentAndOverrides(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="AltitudeFilterComponentAndOverrides", args=["level"])
def AltitudeFilter_ComponentAndOverrides_InstancesPlantAtSpecifiedAltitude():
"""
Summary:
A new level is created. A spawner entity is added, along with a planting surface at 32 on Z, and another at 36
on Z. An Altitude Filter is added to the spawner entity, and Altitude Min/Max values are set. Instance counts
are validated. The same test is then performed for Altitude Filter overrides.
def run_test(self):
"""
Summary:
A new level is created. A spawner entity is added, along with a planting surface at 32 on Z, and another at 36
on Z. An Altitude Filter is added to the spawner entity, and Altitude Min/Max values are set. Instance counts
are validated. The same test is then performed for Altitude Filter overrides.
Expected Behavior:
Instances are only spawned within the specified altitude ranges.
Expected Behavior:
Instances are only spawned within the specified altitude ranges.
Test Steps:
1) Open a simple level
2) Create an instance spawner entity
3) Create surfaces to plant on, one at 32 on Z and another at 36 on Z.
4) Initial instance counts pre-filter are verified.
5) Altitude Min/Max is set on the Vegetation Altitude Filter component.
6) Instance counts post-filter are verified.
7) Altitude Min/Max is set on descriptor overrides.
8) Instance counts post-filter are verified.
Test Steps:
1) Create a new level
2) Create an instance spawner entity
3) Create surfaces to plant on, one at 32 on Z and another at 36 on Z.
4) Initial instance counts pre-filter are verified.
5) Altitude Min/Max is set on the Vegetation Altitude Filter component.
6) Instance counts post-filter are verified.
7) Altitude Min/Max is set on descriptor overrides.
8) Instance counts post-filter are verified.
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
:return: None
"""
:return: None
"""
import os
# 1) Create a new, temporary level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=False,
)
import azlmbr.editor as editor
import azlmbr.legacy.general as general
import azlmbr.bus as bus
import azlmbr.math as math
# Set view of planting area for visual debugging
general.set_current_view_position(512.0, 500.0, 38.0)
general.set_current_view_rotation(-20.0, 0.0, 0.0)
import editor_python_test_tools.hydra_editor_utils as hydra
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
# 2) Create a new entity with required vegetation area components
center_point = math.Vector3(512.0, 512.0, 32.0)
asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
spawner_entity = dynveg.create_vegetation_area("Instance Spawner", center_point, 32.0, 32.0, 32.0, asset_path)
# 1) Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
# Add a Vegetation Altitude Filter
spawner_entity.add_component("Vegetation Altitude Filter")
# Set view of planting area for visual debugging
general.set_current_view_position(512.0, 500.0, 38.0)
general.set_current_view_rotation(-20.0, 0.0, 0.0)
# 3) Add surfaces to plant on
dynveg.create_surface_entity("Planting Surface", center_point, 32.0, 32.0, 1.0)
elevated_surface_center_point = math.Vector3(512.0, 512.0, 36.0)
dynveg.create_surface_entity("Planting Surface Elevated", elevated_surface_center_point, 32.0, 32.0, 1.0)
# 2) Create a new entity with required vegetation area components
center_point = math.Vector3(512.0, 512.0, 32.0)
asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
spawner_entity = dynveg.create_vegetation_area("Instance Spawner", center_point, 32.0, 32.0, 32.0, asset_path)
# Set instances to spawn on a center snap point to avoid unexpected instances around the edges of the box shape
veg_system_settings_component = hydra.add_level_component("Vegetation System Settings")
editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", veg_system_settings_component,
'Configuration|Area System Settings|Sector Point Snap Mode', 1)
# Add a Vegetation Altitude Filter
spawner_entity.add_component("Vegetation Altitude Filter")
# 4) Verify initial instance counts pre-filter
num_expected = (40 * 40) * 2 # 20 instances per 16m per side x 2 surfaces
spawner_success = self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0)
self.test_success = self.test_success and spawner_success
# 3) Add surfaces to plant on
dynveg.create_surface_entity("Planting Surface", center_point, 32.0, 32.0, 1.0)
elevated_surface_center_point = math.Vector3(512.0, 512.0, 36.0)
dynveg.create_surface_entity("Planting Surface Elevated", elevated_surface_center_point, 32.0, 32.0, 1.0)
# 5) Set min/max vegetation altitude, instances should now only appear between 35-37m on the Z-axis
spawner_entity.get_set_test(3, "Configuration|Altitude Min", 35)
spawner_entity.get_set_test(3, "Configuration|Altitude Max", 37)
# Set instances to spawn on a center snap point to avoid unexpected instances around the edges of the box shape
veg_system_settings_component = hydra.add_level_component("Vegetation System Settings")
editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", veg_system_settings_component,
'Configuration|Area System Settings|Sector Point Snap Mode', 1)
# 6) Validate expected instance counts
num_expected = 40 * 40 # Instances should now only plant on the elevated surface
altitude_min_max_success = self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0)
self.test_success = self.test_success and altitude_min_max_success
# 4) Verify initial instance counts pre-filter
num_expected = (40 * 40) * 2 # 20 instances per 16m per side x 2 surfaces
spawner_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0)
Report.result(Tests.prefilter_instance_count, spawner_success)
# Resize Spawner Entity's Box Shape component to allow monitoring for a different instance count
box_size = math.Vector3(16.0, 16.0, 16.0)
spawner_entity.get_set_test(1, "Box Shape|Box Configuration|Dimensions", box_size)
# 5) Set min/max vegetation altitude, instances should now only appear between 35-37m on the Z-axis
spawner_entity.get_set_test(3, "Configuration|Altitude Min", 35)
spawner_entity.get_set_test(3, "Configuration|Altitude Max", 37)
# 7) Allow overrides on Altitude Filter and set Altitude Filter Min/Max overrides on descriptor
spawner_entity.get_set_test(3, "Configuration|Allow Per-Item Overrides", True)
spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Altitude Filter|Override Enabled", True)
spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Altitude Filter|Min", 35)
spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Altitude Filter|Max", 37)
# 6) Validate expected instance counts
num_expected = 40 * 40 # Instances should now only plant on the elevated surface
altitude_min_max_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0)
Report.result(Tests.postfilter_instance_count, altitude_min_max_success)
# 8) Validate expected instances at specified elevations
num_expected = 20 * 20 # 20 instances per 16m per side
overrides_success = self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0)
self.test_success = self.test_success and overrides_success
# Resize Spawner Entity's Box Shape component to allow monitoring for a different instance count
box_size = math.Vector3(16.0, 16.0, 16.0)
spawner_entity.get_set_test(1, "Box Shape|Box Configuration|Dimensions", box_size)
# 7) Allow overrides on Altitude Filter and set Altitude Filter Min/Max overrides on descriptor
spawner_entity.get_set_test(3, "Configuration|Allow Per-Item Overrides", True)
spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Altitude Filter|Override Enabled", True)
spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Altitude Filter|Min", 35)
spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Altitude Filter|Max", 37)
# 8) Validate expected instances at specified elevations
num_expected = 20 * 20 # 20 instances per 16m per side
overrides_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0)
Report.result(Tests.postfilter_overrides_instance_count, overrides_success)
test = TestAltitudeFilterComponentAndOverrides()
test.run()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(AltitudeFilter_ComponentAndOverrides_InstancesPlantAtSpecifiedAltitude)
@@ -5,90 +5,88 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import sys
import azlmbr.bus as bus
import azlmbr.components as components
import azlmbr.editor as editor
import azlmbr.legacy.general as general
import azlmbr.math as math
import azlmbr.paths
sys.path.append(os.path.join(azlmbr.paths.devroot, "AutomatedTesting", "Gem", "PythonTests"))
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.editor_test_helper import EditorTestHelper
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
class Tests:
preprocess_instance_count = (
"Pre-process instance counts are accurate",
"Unexpected number of pre-process instances found"
)
postprocess_instance_count = (
"Post-process instance counts are accurate",
"Unexpected number of post-process instances found"
)
class TestAltitudeFilterFilterStageToggle(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="AltitudeFilter_FilterStageToggle", args=["level"])
def AltitudeFilter_FilterStageToggle():
"""
Summary:
Filter Stage toggle affects final vegetation position
def run_test(self):
"""
Summary:
Filter Stage toggle affects final vegetation position
Expected Result:
Vegetation instances plant differently depending on the Filter Stage setting.
PostProcess should cause some number of plants that appear above and below the desired altitude range to disappear.
Expected Result:
Vegetation instances plant differently depending on the Filter Stage setting.
PostProcess should cause some number of plants that appear above and below the desired altitude range to disappear.
:return: None
"""
:return: None
"""
import os
PREPROCESS_INSTANCE_COUNT = 44
POSTPROCESS_INSTANCE_COUNT = 34
import azlmbr.legacy.general as general
import azlmbr.math as math
# Create empty level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=False,
)
import editor_python_test_tools.hydra_editor_utils as hydra
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
general.set_current_view_position(512.0, 480.0, 38.0)
PREPROCESS_INSTANCE_COUNT = 44
POSTPROCESS_INSTANCE_COUNT = 34
# Create basic vegetation entity
position = math.Vector3(512.0, 512.0, 32.0)
asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
vegetation = dynveg.create_vegetation_area("vegetation", position, 16.0, 16.0, 16.0, asset_path)
# Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
general.set_current_view_position(512.0, 480.0, 38.0)
# Add a Vegetation Altitude Filter to the vegetation area entity
vegetation.add_component("Vegetation Altitude Filter")
# Create basic vegetation entity
position = math.Vector3(512.0, 512.0, 32.0)
asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
vegetation = dynveg.create_vegetation_area("vegetation", position, 16.0, 16.0, 16.0, asset_path)
# Create Surface for instances to plant on
dynveg.create_surface_entity("Surface_Entity_Parent", position, 16.0, 16.0, 1.0)
# Add a Vegetation Altitude Filter to the vegetation area entity
vegetation.add_component("Vegetation Altitude Filter")
# Add entity with Mesh to replicate creation of hills
hill_entity = dynveg.create_mesh_surface_entity_with_slopes("hill", position, 10.0)
# Create Surface for instances to plant on
dynveg.create_surface_entity("Surface_Entity_Parent", position, 16.0, 16.0, 1.0)
# Set a Min Altitude of 38 and Max of 40 in Vegetation Altitude Filter
vegetation.get_set_test(3, "Configuration|Altitude Min", 38.0)
vegetation.get_set_test(3, "Configuration|Altitude Max", 40.0)
# Add entity with Mesh to replicate creation of hills
hill_entity = dynveg.create_mesh_surface_entity_with_slopes("hill", position, 10.0)
# Create a new entity as a child of the vegetation area entity with Random Noise Gradient Generator, Gradient
# Transform Modifier, and Box Shape component
random_noise = hydra.Entity("random_noise")
random_noise.create_entity(position, ["Random Noise Gradient", "Gradient Transform Modifier", "Box Shape"])
random_noise.set_test_parent_entity(vegetation)
# Set a Min Altitude of 38 and Max of 40 in Vegetation Altitude Filter
vegetation.get_set_test(3, "Configuration|Altitude Min", 38.0)
vegetation.get_set_test(3, "Configuration|Altitude Max", 40.0)
# Add a Vegetation Position Modifier to the vegetation area entity.
vegetation.add_component("Vegetation Position Modifier")
# Create a new entity as a child of the vegetation area entity with Random Noise Gradient Generator, Gradient
# Transform Modifier, and Box Shape component
random_noise = hydra.Entity("random_noise")
random_noise.create_entity(position, ["Random Noise Gradient", "Gradient Transform Modifier", "Box Shape"])
random_noise.set_test_parent_entity(vegetation)
# Pin the Random Noise entity to the Gradient Entity Id field of the Position Modifier's Gradient X
vegetation.get_set_test(4, "Configuration|Position X|Gradient|Gradient Entity Id", random_noise.id)
# Add a Vegetation Position Modifier to the vegetation area entity.
vegetation.add_component("Vegetation Position Modifier")
# Toggle between PreProcess and PostProcess in Vegetation Altitude Filter
vegetation.get_set_test(3, "Configuration|Filter Stage", 1)
result = self.wait_for_condition(lambda: dynveg.validate_instance_count(position, 30.0, PREPROCESS_INSTANCE_COUNT), 2.0)
self.log(f"Vegetation instances count equal to expected value for PREPROCESS filter stage: {result}")
vegetation.get_set_test(3, "Configuration|Filter Stage", 2)
result = self.wait_for_condition(lambda: dynveg.validate_instance_count(position, 30.0, POSTPROCESS_INSTANCE_COUNT), 2.0)
self.log(f"Vegetation instances count equal to expected value for POSTPROCESS filter stage: {result}")
# Pin the Random Noise entity to the Gradient Entity Id field of the Position Modifier's Gradient X
vegetation.get_set_test(4, "Configuration|Position X|Gradient|Gradient Entity Id", random_noise.id)
# Toggle between PreProcess and PostProcess in Vegetation Altitude Filter
vegetation.get_set_test(3, "Configuration|Filter Stage", 1)
result = helper.wait_for_condition(lambda: dynveg.validate_instance_count(position, 30.0, PREPROCESS_INSTANCE_COUNT), 2.0)
Report.result(Tests.preprocess_instance_count, result)
vegetation.get_set_test(3, "Configuration|Filter Stage", 2)
result = helper.wait_for_condition(lambda: dynveg.validate_instance_count(position, 30.0, POSTPROCESS_INSTANCE_COUNT), 2.0)
Report.result(Tests.postprocess_instance_count, result)
test = TestAltitudeFilterFilterStageToggle()
test.run()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(AltitudeFilter_FilterStageToggle)
@@ -5,104 +5,105 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.editor as editor
import azlmbr.legacy.general as general
import azlmbr.bus as bus
import azlmbr.math as math
import azlmbr.paths
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.editor_test_helper import EditorTestHelper
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
class Tests:
prefilter_instance_count = (
"Pre-filter instance counts are accurate",
"Unexpected number of pre-filter instances found"
)
postfilter_instance_count = (
"Post-filter instance counts are accurate",
"Unexpected number of post-filter instances found"
)
class TestAltitudeFilterShapeSample(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="AltitudeFilterShapeSample", args=["level"])
def AltitudeFilter_ShapeSample_InstancesPlantAtSpecifiedAltitude():
"""
Summary:
A new level is created. A spawner entity is added, along with a planting surface at 32 on Z, and another at 36
on Z. An Altitude Filter is added to the spawner entity, and set to sample a shape entity. Instance counts are
validated.
def run_test(self):
"""
Summary:
A new level is created. A spawner entity is added, along with a planting surface at 32 on Z, and another at 36
on Z. An Altitude Filter is added to the spawner entity, and set to sample a shape entity. Instance counts are
validated.
Expected Behavior:
Instances are only spawned within the altitude range specified by the sampled shape.
Expected Behavior:
Instances are only spawned within the altitude range specified by the sampled shape.
Test Steps:
1) Open a simple level
2) Create an instance spawner entity
3) Create surfaces to plant on, one at 32 on Z and another at 36 on Z.
4) Initial instance counts pre-filter are verified.
5) A new entity with shape is created, an sampled on the Vegetation Altitude Filter.
6) Instance counts post-filter are verified.
Test Steps:
1) Create a new level
2) Create an instance spawner entity
3) Create surfaces to plant on, one at 32 on Z and another at 36 on Z.
4) Initial instance counts pre-filter are verified.
5) A new entity with shape is created, an sampled on the Vegetation Altitude Filter.
6) Instance counts post-filter are verified.
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
:return: None
"""
:return: None
"""
import os
# 1) Create a new, temporary level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=False,
)
import azlmbr.editor as editor
import azlmbr.legacy.general as general
import azlmbr.bus as bus
import azlmbr.math as math
# Set view of planting area for visual debugging
general.set_current_view_position(512.0, 500.0, 38.0)
general.set_current_view_rotation(-20.0, 0.0, 0.0)
import editor_python_test_tools.hydra_editor_utils as hydra
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
# 2) Create a new entity with required vegetation area components
center_point = math.Vector3(512.0, 512.0, 32.0)
asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
spawner_entity = dynveg.create_vegetation_area("Instance Spawner", center_point, 16.0, 16.0, 16.0, asset_path)
# 1) Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
# Add a Vegetation Altitude Filter
spawner_entity.add_component("Vegetation Altitude Filter")
# Set view of planting area for visual debugging
general.set_current_view_position(512.0, 500.0, 38.0)
general.set_current_view_rotation(-20.0, 0.0, 0.0)
# 3) Add surfaces to plant on
dynveg.create_surface_entity("Planting Surface", center_point, 32.0, 32.0, 1.0)
elevated_surface_center_point = math.Vector3(512.0, 512.0, 36.0)
dynveg.create_surface_entity("Planting Surface Elevated", elevated_surface_center_point, 32.0, 32.0, 1.0)
# 2) Create a new entity with required vegetation area components
center_point = math.Vector3(512.0, 512.0, 32.0)
asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
spawner_entity = dynveg.create_vegetation_area("Instance Spawner", center_point, 16.0, 16.0, 16.0, asset_path)
# Set instances to spawn on a center snap point to avoid unexpected instances around the edges of the box shape
veg_system_settings_component = hydra.add_level_component("Vegetation System Settings")
editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", veg_system_settings_component,
'Configuration|Area System Settings|Sector Point Snap Mode', 1)
# Add a Vegetation Altitude Filter
spawner_entity.add_component("Vegetation Altitude Filter")
# 4) Verify initial instance counts pre-filter
num_expected = (20 * 20) * 2 # 20 instances per 16m per side x 2 surfaces
spawner_success = self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0)
self.test_success = self.test_success and spawner_success
# 3) Add surfaces to plant on
dynveg.create_surface_entity("Planting Surface", center_point, 32.0, 32.0, 1.0)
elevated_surface_center_point = math.Vector3(512.0, 512.0, 36.0)
dynveg.create_surface_entity("Planting Surface Elevated", elevated_surface_center_point, 32.0, 32.0, 1.0)
# 5) Create a new entity with a shape at 36 on the Z-axis, and pin the entity to the Vegetation Altitude Filter
shape_sampler_center_point = math.Vector3(512.0, 512.0, 36.0)
shape_sampler = hydra.Entity("Shape Sampler")
shape_sampler.create_entity(
shape_sampler_center_point,
["Box Shape"]
)
if shape_sampler.id.IsValid():
print(f"'{shape_sampler.name}' created")
spawner_entity.get_set_test(3, 'Configuration|Pin To Shape Entity Id', shape_sampler.id)
# Set instances to spawn on a center snap point to avoid unexpected instances around the edges of the box shape
veg_system_settings_component = hydra.add_level_component("Vegetation System Settings")
editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", veg_system_settings_component,
'Configuration|Area System Settings|Sector Point Snap Mode', 1)
# 6) Validate expected instance counts
num_expected = 20 * 20 # Instances should now only plant on the elevated surface
sampler_success = self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0)
self.test_success = self.test_success and sampler_success
# 4) Verify initial instance counts pre-filter
num_expected = (20 * 20) * 2 # 20 instances per 16m per side x 2 surfaces
spawner_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0)
Report.result(Tests.prefilter_instance_count, spawner_success)
# 5) Create a new entity with a shape at 36 on the Z-axis, and pin the entity to the Vegetation Altitude Filter
shape_sampler_center_point = math.Vector3(512.0, 512.0, 36.0)
shape_sampler = hydra.Entity("Shape Sampler")
shape_sampler.create_entity(
shape_sampler_center_point,
["Box Shape"]
)
if shape_sampler.id.IsValid():
print(f"'{shape_sampler.name}' created")
spawner_entity.get_set_test(3, 'Configuration|Pin To Shape Entity Id', shape_sampler.id)
# 6) Validate expected instance counts
num_expected = 20 * 20 # Instances should now only plant on the elevated surface
sampler_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0)
Report.result(Tests.postfilter_instance_count, sampler_success)
test = TestAltitudeFilterShapeSample()
test.run()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(AltitudeFilter_ShapeSample_InstancesPlantAtSpecifiedAltitude)
@@ -1,125 +0,0 @@
"""
Copyright (c) Contributors to the Open 3D Engine Project.
For complete copyright and license terms please see the LICENSE at the root of this distribution.
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import sys
import azlmbr.math as math
import azlmbr.legacy.general as general
import azlmbr.slice as slice
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.asset as asset
import azlmbr.paths
sys.path.append(os.path.join(azlmbr.paths.devroot, "AutomatedTesting", "Gem", "PythonTests"))
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.editor_test_helper import EditorTestHelper
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
class TestAreaComponentsSliceCreationAndVisibilityToggle(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(
self, log_prefix="AreaComponentSlices_SliceCreationAndVisibilityToggle", args=["level"]
)
def run_test(self):
"""
Summary:
C2627900 Verifies if a slice containing the component can be created.
C2627905 A slice containing the Vegetation Layer Blender component can be created.
C2627904: Hiding a slice containing the component clears any visuals from the Viewport.
Expected Result:
C2627900, C2627905: Slice is created, and is properly processed in the Asset Processor.
C2627904: Vegetation area visuals are hidden from the Viewport.
:return: None
"""
def path_is_valid_asset(asset_path):
asset_id = asset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", asset_path, math.Uuid(), False)
return asset_id.invoke("IsValid")
# 1) Create empty level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=False,
)
general.set_current_view_position(512.0, 480.0, 38.0)
# 2) C2627900 Verifies if a slice containing the Vegetation Layer Spawner component can be created.
# 2.1) Create basic vegetation entity
position = math.Vector3(512.0, 512.0, 32.0)
asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
veg_1 = dynveg.create_vegetation_area("vegetation_1", position, 16.0, 16.0, 16.0, asset_path)
# 2.2) Create slice from the entity
slice_path = os.path.join("slices", "TestSlice_1.slice")
slice.SliceRequestBus(bus.Broadcast, "CreateNewSlice", veg_1.id, slice_path)
# 2.3) Verify if the slice has been created successfully
self.wait_for_condition(lambda: path_is_valid_asset(slice_path), 5.0)
self.log(
f"Slice has been created successfully (entity with spawner component): {path_is_valid_asset(slice_path)}"
)
# 3) C2627904: Hiding a slice containing the component clears any visuals from the Viewport
# 3.1) Create Surface for instances to plant on
dynveg.create_surface_entity("Surface_Entity", position, 16.0, 16.0, 1.0)
# 3.2) Initially verify instance count before hiding slice
self.wait_for_condition(lambda: dynveg.validate_instance_count(position, 16.0, 400))
self.log(
f"Vegetation plants initially when slice is shown: {dynveg.validate_instance_count(position, 16.0, 400)}"
)
# 3.3) Hide the slice and verify instance count
editor.EditorEntityAPIBus(bus.Event, "SetVisibilityState", veg_1.id, False)
self.wait_for_condition(lambda: dynveg.validate_instance_count(position, 16.0, 0))
self.log(f"Vegetation is cleared when slice is hidden: {dynveg.validate_instance_count(position, 16.0, 0)}")
# 3.4) Unhide the slice
editor.EditorEntityAPIBus(bus.Event, "SetVisibilityState", veg_1.id, True)
# 4) C2627905 A slice containing the Vegetation Layer Blender component can be created.
# 4.1) Create another vegetation entity to add to blender component
veg_2 = dynveg.create_vegetation_area("vegetation_2", position, 1.0, 1.0, 1.0, "")
# 4.2) Create entity with Vegetation Layer Blender
components_to_add = ["Box Shape", "Vegetation Layer Blender"]
blender_entity = hydra.Entity("blender_entity")
blender_entity.create_entity(position, components_to_add)
# 4.3) Pin both the vegetation areas to the blender entity
pte = hydra.get_property_tree(blender_entity.components[1])
path = "Configuration|Vegetation Areas"
pte.update_container_item(path, 0, veg_1.id)
pte.add_container_item(path, 1, veg_2.id)
# 4.4) Drag the simple vegetation areas under the Vegetation Layer Blender entity to create an entity hierarchy.
veg_1.set_test_parent_entity(blender_entity)
veg_2.set_test_parent_entity(blender_entity)
# 4.5) Create slice from blender entity
slice_path = os.path.join("slices", "TestSlice_2.slice")
slice.SliceRequestBus(bus.Broadcast, "CreateNewSlice", blender_entity.id, slice_path)
# 4.6) Verify if the slice has been created successfully
self.wait_for_condition(lambda: path_is_valid_asset(slice_path), 5.0)
self.log(
f"Slice has been created successfully (entity with blender component): {path_is_valid_asset(slice_path)}"
)
test = TestAreaComponentsSliceCreationAndVisibilityToggle()
test.run()
@@ -5,162 +5,167 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.legacy.general as general
import azlmbr.math as math
import azlmbr.paths
import azlmbr.vegetation as vegetation
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.editor_test_helper import EditorTestHelper
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
class Tests:
combined_instance_count_validation = (
"Combined instance counts are as expected",
"Found an unexpected number of instances"
)
replaced_asset_list_combined_instance_count_validation = (
"Combined instance counts are as expected after replacing an Asset List reference",
"Found an unexpected number of instances after replacing an Asset List reference"
)
removed_asset_lists_combined_instance_count_validation = (
"Instance counts are as expected after removing the referenced Asset Lists",
"Found an unexpected number of instances after removing the referenced Asset Lists"
)
class TestAssetListCombiner(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="AssetListCombiner_CombinedDescriptors", args=["level"])
def AssetListCombiner_CombinedDescriptorsExpressInConfiguredArea():
"""
Summary:
Combined descriptors appear as expected in a vegetation area. Also verifies remove/replace of assigned Asset
Lists.
def run_test(self):
"""
Summary:
Combined descriptors appear as expected in a vegetation area. Also verifies remove/replace of assigned Asset
Lists.
Expected Behavior:
Vegetation fills in the area using the assets assigned to both Vegetation Asset Lists.
Expected Behavior:
Vegetation fills in the area using the assets assigned to both Vegetation Asset Lists.
Test Steps:
1) Open a simple level
2) Create 3 entities with Vegetation Asset List components set to spawn different descriptors
3) Create a planting surface and add a Vegetation System Settings level component with instances set to spawn
on center instead of corner
4) Create a spawner using a Vegetation Asset List Combiner component and a Weight Selector, and disallow
spawning empty assets
5) Add 2 of the Asset List entities to the Vegetation Asset List Combiner component (PinkFlower and Empty)
6) Create a Constant Gradient entity as a child of the spawner entity, and a Dither Gradient Modifier entity
as a child of the Constant Gradient entity, and configure for a checkerboard pattern
7) Pin the Dither Gradient Entity to the Asset Weight Selector of the spawner entity
8) Validate instance count with configured Asset List Combiner
9) Replace the reference to the 2nd asset list on the Vegetation Asset List Combiner component and validate
instance count
10) Remove the referenced Asset Lists on the Asset List Combiner, Disable/Re-enable the Asset List
Combiner component to force a refresh, and validate instance count
Test Steps:
1) Create a new, temporary level
2) Create 3 entities with Vegetation Asset List components set to spawn different descriptors
3) Create a planting surface and add a Vegetation System Settings level component with instances set to spawn
on center instead of corner
4) Create a spawner using a Vegetation Asset List Combiner component and a Weight Selector, and disallow
spawning empty assets
5) Add 2 of the Asset List entities to the Vegetation Asset List Combiner component (PinkFlower and Empty)
6) Create a Constant Gradient entity as a child of the spawner entity, and a Dither Gradient Modifier entity
as a child of the Constant Gradient entity, and configure for a checkerboard pattern
7) Pin the Dither Gradient Entity to the Asset Weight Selector of the spawner entity
8) Validate instance count with configured Asset List Combiner
9) Replace the reference to the 2nd asset list on the Vegetation Asset List Combiner component and validate
instance count
10) Remove the referenced Asset Lists on the Asset List Combiner, Disable/Re-enable the Asset List
Combiner component to force a refresh, and validate instance count
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
:return: None
"""
:return: None
"""
import os
def create_asset_list_entity(name, center, dynamic_slice_asset_path):
asset_list_entity = hydra.Entity(name)
asset_list_entity.create_entity(
center,
["Vegetation Asset List"]
)
if asset_list_entity.id.IsValid():
print(f"'{asset_list_entity.name}' created")
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.legacy.general as general
import azlmbr.math as math
import azlmbr.vegetation as vegetation
# Set the Asset List to a Dynamic Slice spawner with a specific slice asset selected
dynamic_slice_spawner = vegetation.DynamicSliceInstanceSpawner()
dynamic_slice_spawner.SetSliceAssetPath(dynamic_slice_asset_path)
descriptor = hydra.get_component_property_value(asset_list_entity.components[0],
"Configuration|Embedded Assets|[0]")
descriptor.spawner = dynamic_slice_spawner
asset_list_entity.get_set_test(0, "Configuration|Embedded Assets|[0]", descriptor)
return asset_list_entity
import editor_python_test_tools.hydra_editor_utils as hydra
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
# 1) Create a new, temporary level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=False,
def create_asset_list_entity(name, center, dynamic_slice_asset_path):
asset_list_entity = hydra.Entity(name)
asset_list_entity.create_entity(
center,
["Vegetation Asset List"]
)
if asset_list_entity.id.IsValid():
print(f"'{asset_list_entity.name}' created")
# Set view of planting area for visual debugging
general.set_current_view_position(512.0, 500.0, 38.0)
general.set_current_view_rotation(-20.0, 0.0, 0.0)
# Set the Asset List to a Dynamic Slice spawner with a specific slice asset selected
dynamic_slice_spawner = vegetation.DynamicSliceInstanceSpawner()
dynamic_slice_spawner.SetSliceAssetPath(dynamic_slice_asset_path)
descriptor = hydra.get_component_property_value(asset_list_entity.components[0],
"Configuration|Embedded Assets|[0]")
descriptor.spawner = dynamic_slice_spawner
asset_list_entity.get_set_test(0, "Configuration|Embedded Assets|[0]", descriptor)
return asset_list_entity
# 2) Create 3 entities with Vegetation Asset List components set to spawn different descriptors
center_point = math.Vector3(512.0, 512.0, 32.0)
asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
asset_path2 = os.path.join("Slices", "PurpleFlower.dynamicslice")
asset_list_entity = create_asset_list_entity("Asset List 1", center_point, asset_path)
asset_list_entity2 = create_asset_list_entity("Asset List 2", center_point, None)
asset_list_entity3 = create_asset_list_entity("Asset List 3", center_point, asset_path2)
# 1) Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
# 3) Create a planting surface and add a Vegetation System Settings level component with instances set to spawn
# on center instead of corner
dynveg.create_surface_entity("Surface Entity", center_point, 32.0, 32.0, 1.0)
veg_system_settings_component = hydra.add_level_component("Vegetation System Settings")
editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", veg_system_settings_component,
'Configuration|Area System Settings|Sector Point Snap Mode', 1)
# Set view of planting area for visual debugging
general.set_current_view_position(512.0, 500.0, 38.0)
general.set_current_view_rotation(-20.0, 0.0, 0.0)
# 4) Create a spawner using a Vegetation Asset List Combiner component and a Weight Selector, and disallow
# spawning empty assets
spawner_entity = dynveg.create_vegetation_area("Spawner Entity", center_point, 16.0, 16.0, 16.0, None)
spawner_entity.remove_component("Vegetation Asset List")
spawner_entity.add_component("Vegetation Asset List Combiner")
spawner_entity.add_component("Vegetation Asset Weight Selector")
spawner_entity.get_set_test(0, "Configuration|Allow Empty Assets", False)
# 2) Create 3 entities with Vegetation Asset List components set to spawn different descriptors
center_point = math.Vector3(512.0, 512.0, 32.0)
asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
asset_path2 = os.path.join("Slices", "PurpleFlower.dynamicslice")
asset_list_entity = create_asset_list_entity("Asset List 1", center_point, asset_path)
asset_list_entity2 = create_asset_list_entity("Asset List 2", center_point, None)
asset_list_entity3 = create_asset_list_entity("Asset List 3", center_point, asset_path2)
# 5) Add the Asset List entities to the Vegetation Asset List Combiner component
asset_list_entities = [asset_list_entity.id, asset_list_entity2.id]
spawner_entity.get_set_test(2, "Configuration|Descriptor Providers", asset_list_entities)
# 3) Create a planting surface and add a Vegetation System Settings level component with instances set to spawn
# on center instead of corner
dynveg.create_surface_entity("Surface Entity", center_point, 32.0, 32.0, 1.0)
veg_system_settings_component = hydra.add_level_component("Vegetation System Settings")
editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", veg_system_settings_component,
'Configuration|Area System Settings|Sector Point Snap Mode', 1)
# 6) Create a Constant Gradient entity as a child of the spawner entity, and a Dither Gradient Modifier entity
# as a child of the Constant Gradient entity, and configure for a checkerboard pattern
components_to_add = ["Constant Gradient"]
constant_gradient_entity = hydra.Entity("Constant Gradient Entity")
constant_gradient_entity.create_entity(center_point, components_to_add, parent_id=spawner_entity.id)
constant_gradient_entity.get_set_test(0, "Configuration|Value", 0.5)
# 4) Create a spawner using a Vegetation Asset List Combiner component and a Weight Selector, and disallow
# spawning empty assets
spawner_entity = dynveg.create_vegetation_area("Spawner Entity", center_point, 16.0, 16.0, 16.0, None)
spawner_entity.remove_component("Vegetation Asset List")
spawner_entity.add_component("Vegetation Asset List Combiner")
spawner_entity.add_component("Vegetation Asset Weight Selector")
spawner_entity.get_set_test(0, "Configuration|Allow Empty Assets", False)
components_to_add = ["Dither Gradient Modifier"]
dither_gradient_entity = hydra.Entity("Dither Gradient Entity")
dither_gradient_entity.create_entity(center_point, components_to_add, parent_id=constant_gradient_entity.id)
dither_gradient_entity.get_set_test(0, "Configuration|Gradient|Gradient Entity Id", constant_gradient_entity.id)
# 5) Add the Asset List entities to the Vegetation Asset List Combiner component
asset_list_entities = [asset_list_entity.id, asset_list_entity2.id]
spawner_entity.get_set_test(2, "Configuration|Descriptor Providers", asset_list_entities)
# 7) Pin the Dither Gradient Entity to the Asset Weight Selector of the spawner entity
spawner_entity.get_set_test(3, "Configuration|Gradient|Gradient Entity Id", dither_gradient_entity.id)
# 6) Create a Constant Gradient entity as a child of the spawner entity, and a Dither Gradient Modifier entity
# as a child of the Constant Gradient entity, and configure for a checkerboard pattern
components_to_add = ["Constant Gradient"]
constant_gradient_entity = hydra.Entity("Constant Gradient Entity")
constant_gradient_entity.create_entity(center_point, components_to_add, parent_id=spawner_entity.id)
constant_gradient_entity.get_set_test(0, "Configuration|Value", 0.5)
# 8) Validate instance count. We should now have 200 instances in the spawner area as every other instance
# should be an empty asset which the spawner is set to disallow
num_expected = 20 * 20 / 2
success = self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id,
num_expected), 5.0)
self.test_success = success and self.test_success
components_to_add = ["Dither Gradient Modifier"]
dither_gradient_entity = hydra.Entity("Dither Gradient Entity")
dither_gradient_entity.create_entity(center_point, components_to_add, parent_id=constant_gradient_entity.id)
dither_gradient_entity.get_set_test(0, "Configuration|Gradient|Gradient Entity Id", constant_gradient_entity.id)
# 9) Replace the reference to the 2nd asset list on the Vegetation Asset List Combiner component and validate
# instance count. Should now be 400 instances as the empty spaces can now be claimed by the new descriptor
spawner_entity.get_set_test(2, "Configuration|Descriptor Providers|[1]", asset_list_entity3.id)
num_expected = 20 * 20
success = self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id,
num_expected), 5.0)
self.test_success = success and self.test_success
# 7) Pin the Dither Gradient Entity to the Asset Weight Selector of the spawner entity
spawner_entity.get_set_test(3, "Configuration|Gradient|Gradient Entity Id", dither_gradient_entity.id)
# 10) Remove the referenced Asset Lists on the Asset List Combiner, Disable/Re-enable the Asset List
# Combiner component to force a refresh, and validate instance count. We should now have 0 instances.
pte = hydra.get_property_tree(spawner_entity.components[2])
path = "Configuration|Descriptor Providers"
pte.reset_container(path)
# Component refresh is currently necessary due to container operations not causing a refresh (LY-120947)
editor.EditorComponentAPIBus(bus.Broadcast, "DisableComponents", [spawner_entity.components[2]])
editor.EditorComponentAPIBus(bus.Broadcast, "EnableComponents", [spawner_entity.components[2]])
num_expected = 0
success = self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id,
num_expected), 5.0)
self.test_success = success and self.test_success
# 8) Validate instance count. We should now have 200 instances in the spawner area as every other instance
# should be an empty asset which the spawner is set to disallow
num_expected = 20 * 20 / 2
success = helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id,
num_expected), 5.0)
Report.result(Tests.combined_instance_count_validation, success)
# 9) Replace the reference to the 2nd asset list on the Vegetation Asset List Combiner component and validate
# instance count. Should now be 400 instances as the empty spaces can now be claimed by the new descriptor
spawner_entity.get_set_test(2, "Configuration|Descriptor Providers|[1]", asset_list_entity3.id)
num_expected = 20 * 20
success = helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id,
num_expected), 5.0)
Report.result(Tests.replaced_asset_list_combined_instance_count_validation, success)
# 10) Remove the referenced Asset Lists on the Asset List Combiner, Disable/Re-enable the Asset List
# Combiner component to force a refresh, and validate instance count. We should now have 0 instances.
pte = hydra.get_property_tree(spawner_entity.components[2])
path = "Configuration|Descriptor Providers"
pte.reset_container(path)
# Component refresh is currently necessary due to container operations not causing a refresh (LY-120947)
editor.EditorComponentAPIBus(bus.Broadcast, "DisableComponents", [spawner_entity.components[2]])
editor.EditorComponentAPIBus(bus.Broadcast, "EnableComponents", [spawner_entity.components[2]])
num_expected = 0
success = helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id,
num_expected), 5.0)
Report.result(Tests.removed_asset_lists_combined_instance_count_validation, success)
test = TestAssetListCombiner()
test.run()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(AssetListCombiner_CombinedDescriptorsExpressInConfiguredArea)
@@ -5,112 +5,110 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
"""
C6269654: Vegetation areas using weight selectors properly distribute instances according to Sort By Weight setting
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.legacy.general as general
import azlmbr.math as math
import azlmbr.paths
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.editor_test_helper import EditorTestHelper
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
class Tests:
highest_weight_instance_count = (
"Found the expected number of instances when sorting by highest weight",
"Found an unexpected number of instances when sorting by highest weight"
)
lowest_weight_instance_count = (
"Found the expected number of instances when sorting by lowest weight",
"Found an unexpected number of instances when sorting by lowest weight"
)
class TestAssetWeightSelectorSortByWeight(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="AssetWeightSelector_SortByWeight", args=["level"])
def AssetWeightSelector_InstancesExpressBasedOnWeight():
"""
Summary:
Vegetation areas using weight selectors properly distribute instances according to Sort By Weight setting
def run_test(self):
"""
Summary:
Vegetation areas using weight selectors properly distribute instances according to Sort By Weight setting
Expected Behavior:
Vegetation is planted in the area according to the generated gradient pattern.
Higher weight assets are more likely to express when "Descending (highest first)" is selected.
Lower weight assets are more likely to express when "Ascending (lowest first)" is selected.
Expected Behavior:
Vegetation is planted in the area according to the generated gradient pattern.
Higher weight assets are more likely to express when "Descending (highest first)" is selected.
Lower weight assets are more likely to express when "Ascending (lowest first)" is selected.
Test Steps:
1) Open a simple level
2) Create instance spawner with 2 descriptors, one with an Empty Asset
3) Create a planting surface
4) Create a child entity of the instance spawner with a Constant Gradient component with default values (1.0)
5) Pin the child entity to Vegetation Asset Weight Selector of the instance spawner entity
6) Set first descriptor to a higher weight, and toggle off Allow Empty Assets
7) Validate instance count with initial setup/sort values
8) Change sort values and validate instance count
Test Steps:
1) Create new level
2) Create instance spawner with 2 descriptors, one with an Empty Asset
3) Create a planting surface
4) Create a child entity of the instance spawner with a Constant Gradient component with default values (1.0)
5) Pin the child entity to Vegetation Asset Weight Selector of the instance spawner entity
6) Set first descriptor to a higher weight, and toggle off Allow Empty Assets
7) Validate instance count with initial setup/sort values
8) Change sort values and validate instance count
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
:return: None
"""
:return: None
"""
# 1) Create a new, temporary level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=False,
)
import os
# Set view of planting area for visual debugging
general.set_current_view_position(512.0, 500.0, 38.0)
general.set_current_view_rotation(-20.0, 0.0, 0.0)
import azlmbr.legacy.general as general
import azlmbr.math as math
# 2) Create a new instance spawner entity with multiple Dynamic Slice Instance Spawner descriptors, one set to a
# valid slice entity, and one set to None
spawner_center_point = math.Vector3(512.0, 512.0, 32.0)
asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
spawner_entity = dynveg.create_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 16.0,
asset_path)
desc_asset = hydra.get_component_property_value(spawner_entity.components[2],
"Configuration|Embedded Assets")[0]
desc_list = [desc_asset, desc_asset]
spawner_entity.get_set_test(2, "Configuration|Embedded Assets", desc_list)
spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[1]|Instance|Slice Asset", None)
import editor_python_test_tools.hydra_editor_utils as hydra
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
# Add an Asset Weight Selector component to the spawner entity
spawner_entity.add_component("Vegetation Asset Weight Selector")
# 1) Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
# 3) Create a planting surface
dynveg.create_surface_entity("Planting Surface", spawner_center_point, 32.0, 32.0, 1.0)
# Set view of planting area for visual debugging
general.set_current_view_position(512.0, 500.0, 38.0)
general.set_current_view_rotation(-20.0, 0.0, 0.0)
# 4) Create a child entity of the spawner entity with a Constant Gradient component
components_to_add = ["Constant Gradient"]
gradient_entity = hydra.Entity("Gradient Entity")
gradient_entity.create_entity(spawner_center_point, components_to_add, parent_id=spawner_entity.id)
# 2) Create a new instance spawner entity with multiple Dynamic Slice Instance Spawner descriptors, one set to a
# valid slice entity, and one set to None
spawner_center_point = math.Vector3(512.0, 512.0, 32.0)
asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
spawner_entity = dynveg.create_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 16.0,
asset_path)
desc_asset = hydra.get_component_property_value(spawner_entity.components[2],
"Configuration|Embedded Assets")[0]
desc_list = [desc_asset, desc_asset]
spawner_entity.get_set_test(2, "Configuration|Embedded Assets", desc_list)
spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[1]|Instance|Slice Asset", None)
# 5) Pin the Constant Gradient to the Vegetation Asset Weight Selector
spawner_entity.get_set_test(3, 'Configuration|Gradient|Gradient Entity Id', gradient_entity.id)
# Add an Asset Weight Selector component to the spawner entity
spawner_entity.add_component("Vegetation Asset Weight Selector")
# 6) Set the first descriptor weight to a higher value and toggle off Allow Empty Assets on the Layer Spawner
# component
spawner_entity.get_set_test(2, 'Configuration|Embedded Assets|[0]|Weight', 50)
spawner_entity.get_set_test(0, 'Configuration|Allow Empty Assets', False)
# 3) Create a planting surface
dynveg.create_surface_entity("Planting Surface", spawner_center_point, 32.0, 32.0, 1.0)
# 7) Query for expected instances with default settings. We should have 0 instances with default Constant
# Gradient setup sorting by higher weight first
num_expected = 0
initial_success = self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0)
self.test_success = initial_success and self.test_success
# 4) Create a child entity of the spawner entity with a Constant Gradient component
components_to_add = ["Constant Gradient"]
gradient_entity = hydra.Entity("Gradient Entity")
gradient_entity.create_entity(spawner_center_point, components_to_add, parent_id=spawner_entity.id)
# 8) Sort by lowest weight first, and verify instance counts. We should now have 400 instances as the highest
# priority instance won't be allowed to claim space due to "Allow Empty Assets" being False
spawner_entity.get_set_test(3, 'Configuration|Sort By Weight', 1)
num_expected = 20 * 20
final_success = self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0)
self.test_success = final_success and self.test_success
# 5) Pin the Constant Gradient to the Vegetation Asset Weight Selector
spawner_entity.get_set_test(3, 'Configuration|Gradient|Gradient Entity Id', gradient_entity.id)
# 6) Set the first descriptor weight to a higher value and toggle off Allow Empty Assets on the Layer Spawner
# component
spawner_entity.get_set_test(2, 'Configuration|Embedded Assets|[0]|Weight', 50)
spawner_entity.get_set_test(0, 'Configuration|Allow Empty Assets', False)
# 7) Query for expected instances with default settings. We should have 0 instances with default Constant
# Gradient setup sorting by higher weight first
num_expected = 0
initial_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0)
Report.result(Tests.highest_weight_instance_count, initial_success)
# 8) Sort by lowest weight first, and verify instance counts. We should now have 400 instances as the highest
# priority instance won't be allowed to claim space due to "Allow Empty Assets" being False
spawner_entity.get_set_test(3, 'Configuration|Sort By Weight', 1)
num_expected = 20 * 20
final_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0)
Report.result(Tests.lowest_weight_instance_count, final_success)
test = TestAssetWeightSelectorSortByWeight()
test.run()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(AssetWeightSelector_InstancesExpressBasedOnWeight)
@@ -5,108 +5,118 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.editor as editor
import azlmbr.legacy.general as general
import azlmbr.bus as bus
import azlmbr.math as math
import azlmbr.paths
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.editor_test_helper import EditorTestHelper
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
class Tests:
initial_instance_counts = (
"Initial instance counts are as expected",
"Unexpected number of initial instances found"
)
instance_counts_1m = (
"Instance counts with 1 meters between instances are as expected",
"Unexpected number of instances found with 1 meters between instances"
)
instance_counts_2m = (
"Instance counts with 2 meters between instances are as expected",
"Unexpected number of instances found with 2 meters between instances"
)
instance_counts_16m = (
"Instance counts with 16 meters between instances are as expected",
"Unexpected number of instances found with 16 meters between instances"
)
class TestDistanceBetweenFilterComponentOverrides(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="DistanceBetweenFilterComponentOverrides", args=["level"])
def DistanceBetweenFilterOverrides_InstancesPlantAtSpecifiedRadius():
"""
Summary: Creates a level with a simple vegetation area. A Vegetation Distance Between Filter is
added and the min radius is changed as an override on the descriptor. Instance counts at specific points are
validated.
def run_test(self):
"""
Summary: Creates a level with a simple vegetation area. A Vegetation Distance Between Filter is
added and the min radius is changed as an override on the descriptor. Instance counts at specific points are
validated.
Test Steps:
1) Open a simple level
2) Create a vegetation area
3) Create a surface for planting
4) Add the Vegetation System Settings component and setup for the test
5-8) Add the Distance Between Filter, setup overrides on both the component and descriptor, and validate
expected instance counts with a few different Radius values
Test Steps:
1) Create a new level
2) Create a vegetation area
3) Create a surface for planting
4) Add the Vegetation System Settings component and setup for the test
5-8) Add the Distance Between Filter, setup overrides on both the component and descriptor, and validate
expected instance counts with a few different Radius values
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
:return: None
"""
:return: None
"""
instance_query_point_a = math.Vector3(512.5, 512.5, 32.0)
instance_query_point_b = math.Vector3(514.0, 512.5, 32.0)
instance_query_point_c = math.Vector3(515.0, 512.5, 32.0)
import os
# 1) Create a new, temporary level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=False,
)
import azlmbr.editor as editor
import azlmbr.legacy.general as general
import azlmbr.bus as bus
import azlmbr.math as math
general.set_current_view_position(512.0, 480.0, 38.0)
import editor_python_test_tools.hydra_editor_utils as hydra
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
# 2) Create a new entity with required vegetation area components
spawner_center_point = math.Vector3(520.0, 520.0, 32.0)
asset_path = os.path.join("Slices", "1m_cube.dynamicslice")
spawner_entity = dynveg.create_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 16.0,
asset_path)
instance_query_point_a = math.Vector3(512.5, 512.5, 32.0)
instance_query_point_b = math.Vector3(514.0, 512.5, 32.0)
instance_query_point_c = math.Vector3(515.0, 512.5, 32.0)
# 3) Create a surface to plant on
surface_center_point = math.Vector3(512.0, 512.0, 32.0)
dynveg.create_surface_entity("Planting Surface", surface_center_point, 128.0, 128.0, 1.0)
# 1) Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
# 4) Add a Vegetation System Settings Level component and set Sector Point Snap Mode to Center
veg_system_settings_component = hydra.add_level_component("Vegetation System Settings")
editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", veg_system_settings_component,
'Configuration|Area System Settings|Sector Point Snap Mode', 1)
editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", veg_system_settings_component,
'Configuration|Area System Settings|Sector Point Density', 16)
general.set_current_view_position(512.0, 480.0, 38.0)
# 5) Add a Vegetation Distance Between Filter, toggle overrides on both the component and descriptor,
# and verify initial instance counts are accurate
spawner_entity.add_component("Vegetation Distance Between Filter")
spawner_entity.get_set_test(3, "Configuration|Allow Per-Item Overrides", True)
spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Distance Between Filter (Radius)|Override Enabled", True)
num_expected = 16 * 16
initial_success = self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0)
self.test_success = self.test_success and initial_success
# 2) Create a new entity with required vegetation area components
spawner_center_point = math.Vector3(520.0, 520.0, 32.0)
asset_path = os.path.join("Slices", "1m_cube.dynamicslice")
spawner_entity = dynveg.create_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 16.0,
asset_path)
# 6) Change Radius Min to 1.0, refresh, and verify instance counts are accurate
spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Distance Between Filter (Radius)|Radius Min", 1.0)
point_a_success = self.wait_for_condition(lambda: dynveg.validate_instance_count(instance_query_point_a, 0.5, 1), 5.0)
point_b_success = self.wait_for_condition(lambda: dynveg.validate_instance_count(instance_query_point_b, 0.5, 0), 5.0)
point_c_success = self.wait_for_condition(lambda: dynveg.validate_instance_count(instance_query_point_c, 0.5, 1), 5.0)
self.test_success = self.test_success and point_a_success and point_b_success and point_c_success
# 3) Create a surface to plant on
surface_center_point = math.Vector3(512.0, 512.0, 32.0)
dynveg.create_surface_entity("Planting Surface", surface_center_point, 128.0, 128.0, 1.0)
# 7) Change Radius Min to 2.0, refresh, and verify instance counts are accurate
spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Distance Between Filter (Radius)|Radius Min", 2.0)
point_a_success = self.wait_for_condition(lambda: dynveg.validate_instance_count(instance_query_point_a, 0.5, 1), 5.0)
point_b_success = self.wait_for_condition(lambda: dynveg.validate_instance_count(instance_query_point_b, 0.5, 0), 5.0)
point_c_success = self.wait_for_condition(lambda: dynveg.validate_instance_count(instance_query_point_c, 0.5, 0), 5.0)
self.test_success = self.test_success and point_a_success and point_b_success and point_c_success
# 4) Add a Vegetation System Settings Level component and set Sector Point Snap Mode to Center
veg_system_settings_component = hydra.add_level_component("Vegetation System Settings")
editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", veg_system_settings_component,
'Configuration|Area System Settings|Sector Point Snap Mode', 1)
editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", veg_system_settings_component,
'Configuration|Area System Settings|Sector Point Density', 16)
# 8) Change Radius Min to 16.0, refresh, and verify instance counts are accurate, only a single instance should plant
spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Distance Between Filter (Radius)|Radius Min", 16.0)
num_expected_instances = 1
final_check_success = self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected_instances), 5.0)
self.test_success = self.test_success and final_check_success
# 5) Add a Vegetation Distance Between Filter, toggle overrides on both the component and descriptor,
# and verify initial instance counts are accurate
spawner_entity.add_component("Vegetation Distance Between Filter")
spawner_entity.get_set_test(3, "Configuration|Allow Per-Item Overrides", True)
spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Distance Between Filter (Radius)|Override Enabled", True)
num_expected = 16 * 16
initial_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0)
Report.result(Tests.initial_instance_counts, initial_success)
# 6) Change Radius Min to 1.0, refresh, and verify instance counts are accurate
spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Distance Between Filter (Radius)|Radius Min", 1.0)
point_a_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count(instance_query_point_a, 0.5, 1), 5.0)
point_b_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count(instance_query_point_b, 0.5, 0), 5.0)
point_c_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count(instance_query_point_c, 0.5, 1), 5.0)
Report.result(Tests.instance_counts_1m, point_a_success and point_b_success and point_c_success)
# 7) Change Radius Min to 2.0, refresh, and verify instance counts are accurate
spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Distance Between Filter (Radius)|Radius Min", 2.0)
point_a_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count(instance_query_point_a, 0.5, 1), 5.0)
point_b_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count(instance_query_point_b, 0.5, 0), 5.0)
point_c_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count(instance_query_point_c, 0.5, 0), 5.0)
Report.result(Tests.instance_counts_2m, point_a_success and point_b_success and point_c_success)
# 8) Change Radius Min to 16.0, refresh, and verify instance counts are accurate, only a single instance should plant
spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Distance Between Filter (Radius)|Radius Min", 16.0)
num_expected_instances = 1
final_check_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected_instances), 5.0)
Report.result(Tests.instance_counts_16m, final_check_success)
test = TestDistanceBetweenFilterComponentOverrides()
test.run()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(DistanceBetweenFilterOverrides_InstancesPlantAtSpecifiedRadius)
@@ -5,104 +5,113 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.editor as editor
import azlmbr.legacy.general as general
import azlmbr.bus as bus
import azlmbr.math as math
import azlmbr.paths
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.editor_test_helper import EditorTestHelper
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
class Tests:
initial_instance_counts = (
"Initial instance counts are as expected",
"Unexpected number of initial instances found"
)
instance_counts_1m = (
"Instance counts with 1 meters between instances are as expected",
"Unexpected number of instances found with 1 meters between instances"
)
instance_counts_2m = (
"Instance counts with 2 meters between instances are as expected",
"Unexpected number of instances found with 2 meters between instances"
)
instance_counts_16m = (
"Instance counts with 16 meters between instances are as expected",
"Unexpected number of instances found with 16 meters between instances"
)
class TestDistanceBetweenFilterComponent(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="DistanceBetweenFilterComponent", args=["level"])
def DistanceBetweenFilter_InstancesPlantAtSpecifiedRadius():
"""
Summary: Creates a level with a simple vegetation area. A Vegetation Distance Between Filter is
added and the min radius is changed. Instance counts at specific points are validated.
def run_test(self):
"""
Summary: Creates a level with a simple vegetation area. A Vegetation Distance Between Filter is
added and the min radius is changed. Instance counts at specific points are validated.
Test Steps:
1) Open a simple level
2) Create a vegetation area
3) Create a surface for planting
4) Add the Vegetation System Settings component and setup for the test
5-8) Add the Distance Between Filter, and validate expected instance counts with a few different Radius values
Test Steps:
1) Create a new level
2) Create a vegetation area
3) Create a surface for planting
4) Add the Vegetation System Settings component and setup for the test
5-8) Add the Distance Between Filter, and validate expected instance counts with a few different Radius values
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
:return: None
"""
:return: None
"""
instance_query_point_a = math.Vector3(512.5, 512.5, 32.0)
instance_query_point_b = math.Vector3(514.0, 512.5, 32.0)
instance_query_point_c = math.Vector3(515.0, 512.5, 32.0)
import os
# 1) Create a new, temporary level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=False,
)
import azlmbr.editor as editor
import azlmbr.legacy.general as general
import azlmbr.bus as bus
import azlmbr.math as math
general.set_current_view_position(512.0, 480.0, 38.0)
import editor_python_test_tools.hydra_editor_utils as hydra
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
# 2) Create a new entity with required vegetation area components
spawner_center_point = math.Vector3(520.0, 520.0, 32.0)
asset_path = os.path.join("Slices", "1m_cube.dynamicslice")
spawner_entity = dynveg.create_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 16.0,
asset_path)
instance_query_point_a = math.Vector3(512.5, 512.5, 32.0)
instance_query_point_b = math.Vector3(514.0, 512.5, 32.0)
instance_query_point_c = math.Vector3(515.0, 512.5, 32.0)
# 3) Create a surface to plant on
surface_center_point = math.Vector3(512.0, 512.0, 32.0)
dynveg.create_surface_entity("Planting Surface", surface_center_point, 128.0, 128.0, 1.0)
# 1) Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
# 4) Add a Vegetation System Settings Level component and set Sector Point Snap Mode to Center
veg_system_settings_component = hydra.add_level_component("Vegetation System Settings")
editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", veg_system_settings_component,
'Configuration|Area System Settings|Sector Point Snap Mode', 1)
editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", veg_system_settings_component,
'Configuration|Area System Settings|Sector Point Density', 16)
general.set_current_view_position(512.0, 480.0, 38.0)
# 5) Add a Vegetation Distance Between Filter and verify initial instance counts are accurate
spawner_entity.add_component("Vegetation Distance Between Filter")
num_expected = 16 * 16
num_expected = 16 * 16
initial_success = self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0)
self.test_success = self.test_success and initial_success
# 2) Create a new entity with required vegetation area components
spawner_center_point = math.Vector3(520.0, 520.0, 32.0)
asset_path = os.path.join("Slices", "1m_cube.dynamicslice")
spawner_entity = dynveg.create_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 16.0,
asset_path)
# 6) Change Radius Min to 1.0, refresh, and verify instance counts are accurate
spawner_entity.get_set_test(3, "Configuration|Radius Min", 1.0)
point_a_success = self.wait_for_condition(lambda: dynveg.validate_instance_count(instance_query_point_a, 0.5, 1), 5.0)
point_b_success = self.wait_for_condition(lambda: dynveg.validate_instance_count(instance_query_point_b, 0.5, 0), 5.0)
point_c_success = self.wait_for_condition(lambda: dynveg.validate_instance_count(instance_query_point_c, 0.5, 1), 5.0)
self.test_success = self.test_success and point_a_success and point_b_success and point_c_success
# 3) Create a surface to plant on
surface_center_point = math.Vector3(512.0, 512.0, 32.0)
dynveg.create_surface_entity("Planting Surface", surface_center_point, 128.0, 128.0, 1.0)
# 7) Change Radius Min to 2.0, refresh, and verify instance counts are accurate
spawner_entity.get_set_test(3, "Configuration|Radius Min", 2.0)
point_a_success = self.wait_for_condition(lambda: dynveg.validate_instance_count(instance_query_point_a, 0.5, 1), 5.0)
point_b_success = self.wait_for_condition(lambda: dynveg.validate_instance_count(instance_query_point_b, 0.5, 0), 5.0)
point_c_success = self.wait_for_condition(lambda: dynveg.validate_instance_count(instance_query_point_c, 0.5, 0), 5.0)
self.test_success = self.test_success and point_a_success and point_b_success and point_c_success
# 4) Add a Vegetation System Settings Level component and set Sector Point Snap Mode to Center
veg_system_settings_component = hydra.add_level_component("Vegetation System Settings")
editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", veg_system_settings_component,
'Configuration|Area System Settings|Sector Point Snap Mode', 1)
editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", veg_system_settings_component,
'Configuration|Area System Settings|Sector Point Density', 16)
# 8) Change Radius Min to 16.0, refresh, and verify instance counts are accurate
spawner_entity.get_set_test(3, "Configuration|Radius Min", 16.0)
num_expected_instances = 1
final_check_success = self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected_instances), 5.0)
self.test_success = final_check_success and self.test_success
# 5) Add a Vegetation Distance Between Filter and verify initial instance counts are accurate
spawner_entity.add_component("Vegetation Distance Between Filter")
num_expected = 16 * 16
initial_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0)
Report.result(Tests.initial_instance_counts, initial_success)
# 6) Change Radius Min to 1.0, refresh, and verify instance counts are accurate
spawner_entity.get_set_test(3, "Configuration|Radius Min", 1.0)
point_a_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count(instance_query_point_a, 0.5, 1), 5.0)
point_b_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count(instance_query_point_b, 0.5, 0), 5.0)
point_c_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count(instance_query_point_c, 0.5, 1), 5.0)
Report.result(Tests.instance_counts_1m, point_a_success and point_b_success and point_c_success)
# 7) Change Radius Min to 2.0, refresh, and verify instance counts are accurate
spawner_entity.get_set_test(3, "Configuration|Radius Min", 2.0)
point_a_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count(instance_query_point_a, 0.5, 1), 5.0)
point_b_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count(instance_query_point_b, 0.5, 0), 5.0)
point_c_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count(instance_query_point_c, 0.5, 0), 5.0)
Report.result(Tests.instance_counts_2m, point_a_success and point_b_success and point_c_success)
# 8) Change Radius Min to 16.0, refresh, and verify instance counts are accurate
spawner_entity.get_set_test(3, "Configuration|Radius Min", 16.0)
num_expected_instances = 1
final_check_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected_instances), 5.0)
Report.result(Tests.instance_counts_16m, final_check_success)
test = TestDistanceBetweenFilterComponent()
test.run()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(DistanceBetweenFilter_InstancesPlantAtSpecifiedRadius)
@@ -5,140 +5,178 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import sys
import azlmbr.legacy.general as general
import azlmbr.bus as bus
import azlmbr.math as math
import azlmbr.paths
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.editor_test_helper import EditorTestHelper
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
class BehaviorContextTests:
spawner_initialized = (
"Successfully initialized a Dynamic Slice Instance Spawner",
"Failed to initialize a Dynamic Slice Instance Spawner"
)
spawner_slice_asset_path_set = (
"Successfully set a Dynamic Slice asset path",
"Failed to set a Dynamic Slice asset path"
)
spawner_empty_slice_asset_path_set = (
"Successfully set an empty Dynamic Slice asset path",
"Failed to set an empty Dynamic Slice asset path"
)
desc_spawnertype_sets_spawner = (
"Setting spawnerType sets spawner too",
"Setting spawnerType failed to set spawner to expected value"
)
desc_spawner_sets_spawnertype = (
"Setting spawner sets spawnerType too",
"Setting spawner failed to set spawnerType to expected value"
)
class TestDynamicSliceInstanceSpawner(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="DynamicSliceInstanceSpawner", args=["level"])
class PropertyTreeTests:
entity_created = (
"Spawner entity created successfully",
"Failed to create spawner entity"
)
spawner_type_set = (
"Successfully set spawner type",
"Failed to set spawner type"
)
empty_instance_count_validation = (
"Expected number of empty instances planted",
"Unexpected number of empty instances planted"
)
no_instances_when_empty_disallowed = (
"No empty instances found when Empty Assets are not allowed",
"Unexpectedly found empty instances when Empty Assets are not allowed"
)
nonempty_asset_instance_count_validation = (
"Expected number of instances planted",
"Unexpected number of instances planted"
)
def run_test(self):
"""
Summary:
Test aspects of the DynamicSliceInstanceSpawner through the BehaviorContext and the Property Tree.
:return: None
"""
# 1) Open an empty level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=False,
def DynamicSliceInstanceSpawner_DynamicSliceSpawnerWorks():
"""
Summary:
Test aspects of the DynamicSliceInstanceSpawner through the BehaviorContext and the Property Tree.
:return: None
"""
import os
import azlmbr.legacy.general as general
import azlmbr.math as math
import editor_python_test_tools.hydra_editor_utils as hydra
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
# 1) Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
general.set_current_view_position(512.0, 480.0, 38.0)
# Grab the UUID that we need for creating an Dynamic Slice Instance Spawner
dynamic_slice_spawner_uuid = azlmbr.math.Uuid_CreateString('{BBA5CC1E-B4CA-4792-89F7-93711E98FBD1}', 0)
# Grab a path to a test dynamic slice asset
test_slice_asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
# 2) Test DynamicSliceInstanceSpawner BehaviorContext
behavior_context_test_success = True
dynamic_slice_spawner = azlmbr.vegetation.DynamicSliceInstanceSpawner()
behavior_context_test_success = behavior_context_test_success and (dynamic_slice_spawner is not None)
behavior_context_test_success = behavior_context_test_success and (dynamic_slice_spawner.typename == 'DynamicSliceInstanceSpawner')
Report.critical_result(BehaviorContextTests.spawner_initialized, behavior_context_test_success)
# Try to get/set the slice asset path with a valid asset
dynamic_slice_spawner.SetSliceAssetPath(test_slice_asset_path)
validate_path = dynamic_slice_spawner.GetSliceAssetPath()
# We expect the path to get lowercased and normalized with a forward slash, so we compare our result
# vs that instead of directly against test_slice_asset_path.
behavior_context_test_success = behavior_context_test_success and hydra.compare_values('slices/pinkflower.dynamicslice', validate_path, 'GetSliceAssetPath - valid')
Report.result(BehaviorContextTests.spawner_slice_asset_path_set, behavior_context_test_success)
# Try to get/set the slice asset path with an empty path
dynamic_slice_spawner.SetSliceAssetPath('')
validate_path = dynamic_slice_spawner.GetSliceAssetPath()
behavior_context_test_success = behavior_context_test_success and hydra.compare_values('', validate_path, 'GetSliceAssetPath - empty')
Report.result(BehaviorContextTests.spawner_empty_slice_asset_path_set, behavior_context_test_success)
Report.info(f'DynamicSliceInstanceSpawner() BehaviorContext test: {behavior_context_test_success}')
# 3) Test Descriptor BehaviorContext - setting spawnerType sets spawner too
spawner_type_test_success = True
descriptor = azlmbr.vegetation.Descriptor()
spawner_type_test_success = spawner_type_test_success and hydra.get_set_property_test(descriptor, 'spawnerType', dynamic_slice_spawner_uuid)
spawner_type_test_success = spawner_type_test_success and (descriptor.spawner.typename == 'DynamicSliceInstanceSpawner')
Report.result(BehaviorContextTests.desc_spawnertype_sets_spawner, spawner_type_test_success)
Report.info(f'Descriptor() BehaviorContext spawnerType test: {spawner_type_test_success}')
# 4) Test Descriptor BehaviorContext - setting spawner sets spawnerType too
spawner_test_success = True
descriptor = azlmbr.vegetation.Descriptor()
descriptor.spawner = dynamic_slice_spawner
spawner_test_success = spawner_test_success and (descriptor.spawnerType.Equal(dynamic_slice_spawner_uuid))
spawner_test_success = spawner_test_success and (descriptor.spawner.typename == 'DynamicSliceInstanceSpawner')
Report.result(BehaviorContextTests.desc_spawner_sets_spawnertype, spawner_test_success)
Report.info(f'Descriptor() BehaviorContext spawner test: {spawner_test_success}')
### Setup for Property Tree set of tests
# Create a new entity with required vegetation area components
spawner_entity = hydra.Entity("Veg Area")
spawner_entity.create_entity(
math.Vector3(512.0, 512.0, 32.0),
["Vegetation Layer Spawner", "Box Shape", "Vegetation Asset List"]
)
general.idle_wait(1.0)
general.set_current_view_position(512.0, 480.0, 38.0)
Report.critical_result(PropertyTreeTests.entity_created, spawner_entity.id.IsValid())
# Grab the UUID that we need for creating an Dynamic Slice Instance Spawner
dynamic_slice_spawner_uuid = azlmbr.math.Uuid_CreateString('{BBA5CC1E-B4CA-4792-89F7-93711E98FBD1}', 0)
# Resize the Box Shape component
new_box_dimensions = math.Vector3(16.0, 16.0, 16.0)
box_dimensions_path = "Box Shape|Box Configuration|Dimensions"
spawner_entity.get_set_test(1, box_dimensions_path, new_box_dimensions)
# Grab a path to a test dynamic slice asset
test_slice_asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
# Create a surface to plant on
dynveg.create_surface_entity("Surface Entity", math.Vector3(512.0, 512.0, 32.0), 1024.0, 1024.0, 1.0)
# 2) Test DynamicSliceInstanceSpawner BehaviorContext
behavior_context_test_success = True
dynamic_slice_spawner = azlmbr.vegetation.DynamicSliceInstanceSpawner()
behavior_context_test_success = behavior_context_test_success and (dynamic_slice_spawner is not None)
behavior_context_test_success = behavior_context_test_success and (dynamic_slice_spawner.typename == 'DynamicSliceInstanceSpawner')
# Try to get/set the slice asset path with a valid asset
dynamic_slice_spawner.SetSliceAssetPath(test_slice_asset_path)
validate_path = dynamic_slice_spawner.GetSliceAssetPath()
# We expect the path to get lowercased and normalized with a forward slash, so we compare our result
# vs that instead of directly against test_slice_asset_path.
behavior_context_test_success = behavior_context_test_success and hydra.compare_values('slices/pinkflower.dynamicslice', validate_path, 'GetSliceAssetPath - valid')
# Try to get/set the slice asset path with an empty path
dynamic_slice_spawner.SetSliceAssetPath('')
validate_path = dynamic_slice_spawner.GetSliceAssetPath()
behavior_context_test_success = behavior_context_test_success and hydra.compare_values('', validate_path, 'GetSliceAssetPath - empty')
self.test_success = self.test_success and behavior_context_test_success
self.log(f'DynamicSliceInstanceSpawner() BehaviorContext test: {behavior_context_test_success}')
# 5) Descriptor Property Tree test: spawner type can be set
# 3) Test Descriptor BehaviorContext - setting spawnerType sets spawner too
spawner_type_test_success = True
descriptor = azlmbr.vegetation.Descriptor()
spawner_type_test_success = spawner_type_test_success and hydra.get_set_property_test(descriptor, 'spawnerType', dynamic_slice_spawner_uuid)
spawner_type_test_success = spawner_type_test_success and (descriptor.spawner.typename == 'DynamicSliceInstanceSpawner')
self.test_success = self.test_success and spawner_type_test_success
self.log(f'Descriptor() BehaviorContext spawnerType test: {spawner_type_test_success}')
# - Validate the dynamic slice spawner type can be set correctly.
property_tree_success = True
property_tree_success = property_tree_success and spawner_entity.get_set_test(2, 'Configuration|Embedded Assets|[0]|Instance Spawner', dynamic_slice_spawner_uuid)
Report.result(PropertyTreeTests.spawner_type_set, property_tree_success)
# 4) Test Descriptor BehaviorContext - setting spawner sets spawnerType too
spawner_test_success = True
descriptor = azlmbr.vegetation.Descriptor()
descriptor.spawner = dynamic_slice_spawner
spawner_test_success = spawner_test_success and (descriptor.spawnerType.Equal(dynamic_slice_spawner_uuid))
spawner_test_success = spawner_test_success and (descriptor.spawner.typename == 'DynamicSliceInstanceSpawner')
self.test_success = self.test_success and spawner_test_success
self.log(f'Descriptor() BehaviorContext spawner test: {spawner_test_success}')
# This should result in 400 instances, since our box is 16 m x 16 m and by default the veg system plants
# 20 instances per 16 meters
spawner_entity.get_set_test(0, 'Configuration|Allow Empty Assets', True)
num_expected_instances = 20 * 20
property_tree_success = property_tree_success and helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected_instances), 5.0)
Report.result(PropertyTreeTests.empty_instance_count_validation, property_tree_success)
Report.info(f'Property Tree spawner type test: {property_tree_success}')
### Setup for Property Tree set of tests
# 6) Validate that the "Allow Empty Assets" setting affects the DynamicSliceInstanceSpawner
allow_empty_assets_success = True
# Since we have an empty slice path, we should have 0 instances once we disable 'Allow Empty Assets'
num_expected_instances = 0
allow_empty_assets_success = allow_empty_assets_success and spawner_entity.get_set_test(0, 'Configuration|Allow Empty Assets', False)
Report.info('Allow Empty Assets test:')
allow_empty_assets_success = allow_empty_assets_success and helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected_instances), 5.0)
Report.result(PropertyTreeTests.no_instances_when_empty_disallowed, allow_empty_assets_success)
Report.info(f'Allow Empty Assets test: {allow_empty_assets_success}')
# Create a new entity with required vegetation area components
spawner_entity = hydra.Entity("Veg Area")
spawner_entity.create_entity(
math.Vector3(512.0, 512.0, 32.0),
["Vegetation Layer Spawner", "Box Shape", "Vegetation Asset List"]
)
if (spawner_entity.id.IsValid()):
self.log(f"'{spawner_entity.name}' created")
# Resize the Box Shape component
new_box_dimensions = math.Vector3(16.0, 16.0, 16.0)
box_dimensions_path = "Box Shape|Box Configuration|Dimensions"
spawner_entity.get_set_test(1, box_dimensions_path, new_box_dimensions)
# Create a surface to plant on
dynveg.create_surface_entity("Surface Entity", math.Vector3(512.0, 512.0, 32.0), 1024.0, 1024.0, 1.0)
# 5) Descriptor Property Tree test: spawner type can be set
# - Validate the dynamic slice spawner type can be set correctly.
property_tree_success = True
property_tree_success = property_tree_success and spawner_entity.get_set_test(2, 'Configuration|Embedded Assets|[0]|Instance Spawner', dynamic_slice_spawner_uuid)
# This should result in 400 instances, since our box is 16 m x 16 m and by default the veg system plants
# 20 instances per 16 meters
spawner_entity.get_set_test(0, 'Configuration|Allow Empty Assets', True)
num_expected_instances = 20 * 20
property_tree_success = property_tree_success and self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected_instances), 5.0)
self.test_success = self.test_success and property_tree_success
self.log(f'Property Tree spawner type test: {property_tree_success}')
# 6) Validate that the "Allow Empty Assets" setting affects the DynamicSliceInstanceSpawner
allow_empty_assets_success = True
# Since we have an empty slice path, we should have 0 instances once we disable 'Allow Empty Assets'
num_expected_instances = 0
allow_empty_assets_success = allow_empty_assets_success and spawner_entity.get_set_test(0, 'Configuration|Allow Empty Assets', False)
self.log('Allow Empty Assets test:')
allow_empty_assets_success = allow_empty_assets_success and self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected_instances), 5.0)
self.test_success = self.test_success and allow_empty_assets_success
self.log(f'Allow Empty Assets test: {allow_empty_assets_success}')
# 7) Validate that with 'Allow Empty Assets' set to False, a non-empty slice asset gives us the number
# of instances we expect.
spawns_slices_success = True
num_expected_instances = 20 * 20
dynamic_slice_spawner.SetSliceAssetPath(test_slice_asset_path)
spawns_slices_success = spawns_slices_success and spawner_entity.get_set_test(0, 'Configuration|Allow Empty Assets', False)
descriptor = hydra.get_component_property_value(spawner_entity.components[2], 'Configuration|Embedded Assets|[0]')
descriptor.spawner = dynamic_slice_spawner
spawns_slices_success = spawns_slices_success and spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]", descriptor)
self.log('Spawn dynamic slices test:')
spawns_slices_success = spawns_slices_success and self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected_instances), 5.0)
self.test_success = self.test_success and spawns_slices_success
self.log(f'Spawn dynamic slices test: {spawns_slices_success}')
# 7) Validate that with 'Allow Empty Assets' set to False, a non-empty slice asset gives us the number
# of instances we expect.
spawns_slices_success = True
num_expected_instances = 20 * 20
dynamic_slice_spawner.SetSliceAssetPath(test_slice_asset_path)
spawns_slices_success = spawns_slices_success and spawner_entity.get_set_test(0, 'Configuration|Allow Empty Assets', False)
descriptor = hydra.get_component_property_value(spawner_entity.components[2], 'Configuration|Embedded Assets|[0]')
descriptor.spawner = dynamic_slice_spawner
spawns_slices_success = spawns_slices_success and spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]", descriptor)
Report.info('Spawn dynamic slices test:')
spawns_slices_success = spawns_slices_success and helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected_instances), 5.0)
Report.result(PropertyTreeTests.nonempty_asset_instance_count_validation, spawns_slices_success)
Report.info(f'Spawn dynamic slices test: {spawns_slices_success}')
test = TestDynamicSliceInstanceSpawner()
test.run()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(DynamicSliceInstanceSpawner_DynamicSliceSpawnerWorks)
@@ -5,99 +5,115 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.asset as asset
import azlmbr.components as components
import azlmbr.legacy.general as general
import azlmbr.bus as bus
import azlmbr.entity as entity
import azlmbr.editor as editor
import azlmbr.math as math
import azlmbr.paths
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.editor_test_helper import EditorTestHelper
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
class Tests:
level_created = (
"Successfully created level",
"Failed to create level"
)
spawner_entity_created = (
"Spawner entity created successfully",
"Failed to create spawner entity"
)
surface_entity_created = (
"Surface entity created successfully",
"Failed to create surface entity"
)
instance_count = (
"Found the expected number of instances",
"Found an unexpected number of instances"
)
saved_and_exported = (
"Saved and exported level successfully",
"Failed to save and export level"
)
class TestDynamicSliceInstanceSpawnerEmbeddedEditor(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="DynamicSliceInstanceSpawnerEmbeddedEditor", args=["level"])
def DynamicSliceInstanceSpawner_Embedded_E2E():
"""
Summary:
A new temporary level is created. Surface for planting is created. Simple vegetation area is created using
Dynamic Slice Instance Spawner type.
def run_test(self):
"""
Summary:
A new temporary level is created. Surface for planting is created. Simple vegetation area is created using
Dynamic Slice Instance Spawner type.
Expected Behavior:
Instances plant as expected in the assigned area.
Expected Behavior:
Instances plant as expected in the assigned area.
Test Steps:
1) Create level
2) Create a Vegetation Layer Spawner setup using Dynamic Slice Instance Spawner type assets
3) Create a surface to plant on
4) Verify expected instance counts
5) Add a camera component looking at the planting area for visual debugging
6) Save and export to engine
Test Steps:
1) Create level
2) Create a Vegetation Layer Spawner setup using Dynamic Slice Instance Spawner type assets
3) Create a surface to plant on
4) Verify expected instance counts
5) Add a camera component looking at the planting area for visual debugging
6) Save and export to engine
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
:return: None
"""
:return: None
"""
import os
# 1) Create a new, temporary level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=False,
)
import azlmbr.asset as asset
import azlmbr.legacy.general as general
import azlmbr.bus as bus
import azlmbr.components as components
import azlmbr.entity as entity
import azlmbr.math as math
import azlmbr.paths as paths
general.set_current_view_position(512.0, 480.0, 38.0)
import editor_python_test_tools.hydra_editor_utils as hydra
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
# 2) Create a new entity with required vegetation area components and Script Canvas component for launcher test
center_point = math.Vector3(512.0, 512.0, 32.0)
asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
spawner_entity = dynveg.create_vegetation_area("Instance Spawner", center_point, 16.0, 16.0, 1.0, asset_path)
spawner_entity.add_component("Script Canvas")
instance_counter_path = os.path.join("scriptcanvas", "instance_counter.scriptcanvas")
instance_counter_script = asset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", instance_counter_path,
math.Uuid(), False)
spawner_entity.get_set_test(3, "Script Canvas Asset|Script Canvas Asset", instance_counter_script)
# 1) Create a new, temporary level
lvl_name = "tmp_level"
helper.init_idle()
level_created = general.create_level_no_prompt(lvl_name, 1024, 1, 4096, False)
general.idle_wait(1.0)
Report.critical_result(Tests.level_created, level_created == 0)
general.set_current_view_position(512.0, 480.0, 38.0)
# 3) Create a surface to plant on
dynveg.create_surface_entity("Planting Surface", center_point, 128.0, 128.0, 1.0)
# 2) Create a new entity with required vegetation area components and Script Canvas component for launcher test
center_point = math.Vector3(512.0, 512.0, 32.0)
asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
spawner_entity = dynveg.create_vegetation_area("Instance Spawner", center_point, 16.0, 16.0, 1.0, asset_path)
spawner_entity.add_component("Script Canvas")
instance_counter_path = os.path.join("scriptcanvas", "instance_counter.scriptcanvas")
instance_counter_script = asset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", instance_counter_path,
math.Uuid(), False)
spawner_entity.get_set_test(3, "Script Canvas Asset|Script Canvas Asset", instance_counter_script)
Report.result(Tests.spawner_entity_created, spawner_entity.id.IsValid() and hydra.has_components(spawner_entity.id,
["Script Canvas"]))
# 4) Verify instance counts are accurate
general.idle_wait(3.0) # Allow a few seconds for instances to spawn
num_expected_instances = 20 * 20
box = azlmbr.shape.ShapeComponentRequestsBus(bus.Event, 'GetEncompassingAabb', spawner_entity.id)
num_found = azlmbr.areasystem.AreaSystemRequestBus(bus.Broadcast, 'GetInstanceCountInAabb', box)
self.log(f"Expected {num_expected_instances} instances - Found {num_found} instances")
self.test_success = self.test_success and num_found == num_expected_instances
# 3) Create a surface to plant on
surface_entity = dynveg.create_surface_entity("Planting Surface", center_point, 128.0, 128.0, 1.0)
Report.result(Tests.surface_entity_created, surface_entity.id.IsValid())
# 5) Move the default Camera entity for testing in the launcher
cam_position = math.Vector3(512.0, 500.0, 35.0)
search_filter = entity.SearchFilter()
search_filter.names = ["Camera"]
search_entity_ids = entity.SearchBus(bus.Broadcast, 'SearchEntities', search_filter)
components.TransformBus(bus.Event, "MoveEntity", search_entity_ids[0], cam_position)
# 4) Verify instance counts are accurate
num_expected_instances = 20 * 20
success = helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id,
num_expected_instances), 5.0)
Report.result(Tests.instance_count, success)
# 6) Save and export to engine
general.save_level()
general.idle_wait(1.0)
general.export_to_engine()
general.idle_wait(1.0)
# 5) Move the default Camera entity for testing in the launcher
cam_position = math.Vector3(512.0, 500.0, 35.0)
search_filter = entity.SearchFilter()
search_filter.names = ["Camera"]
search_entity_ids = entity.SearchBus(bus.Broadcast, 'SearchEntities', search_filter)
components.TransformBus(bus.Event, "MoveEntity", search_entity_ids[0], cam_position)
# 6) Save and export to engine
general.save_level()
general.export_to_engine()
pak_path = os.path.join(paths.devroot, "AutomatedTesting", "cache", "pc", "levels", lvl_name, "level.pak")
Report.result(Tests.saved_and_exported, os.path.exists(pak_path))
test = TestDynamicSliceInstanceSpawnerEmbeddedEditor()
test.run()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(DynamicSliceInstanceSpawner_Embedded_E2E)
@@ -5,122 +5,137 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.legacy.general as general
import azlmbr.asset as asset
import azlmbr.bus as bus
import azlmbr.components as components
import azlmbr.entity as entity
import azlmbr.editor as editor
import azlmbr.math as math
import azlmbr.paths
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.editor_test_helper import EditorTestHelper
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
class Tests:
level_created = (
"Successfully created level",
"Failed to create level"
)
spawner_entity_created = (
"Spawner entity created successfully",
"Failed to create spawner entity"
)
surface_entity_created = (
"Surface entity created successfully",
"Failed to create surface entity"
)
instance_count = (
"Found the expected number of instances",
"Found an unexpected number of instances"
)
saved_and_exported = (
"Saved and exported level successfully",
"Failed to save and export level"
)
class TestDynamicSliceInstanceSpawnerExternalEditor(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="DynamicSliceInstanceSpawnerExternalEditor", args=["level"])
def DynamicSliceInstanceSpawner_External_E2E():
"""
Summary:
A new temporary level is created. Surface for planting is created. Simple vegetation area is created using
Dynamic Slice Instance Spawner type using external assets.
def run_test(self):
"""
Summary:
A new temporary level is created. Surface for planting is created. Simple vegetation area is created using
Dynamic Slice Instance Spawner type using external assets.
Expected Behavior:
Instances plant as expected in the assigned area.
Expected Behavior:
Instances plant as expected in the assigned area.
Test Steps:
1) Create level
2) Create a Vegetation Layer Spawner setup using Dynamic Slice Instance Spawner type assets
3) Create a surface to plant on
4) Verify expected instance counts
5) Add a camera component looking at the planting area for visual debugging
6) Save and export to engine
Test Steps:
1) Create level
2) Create a Vegetation Layer Spawner setup using Dynamic Slice Instance Spawner type assets
3) Create a surface to plant on
4) Verify expected instance counts
5) Add a camera component looking at the planting area for visual debugging
6) Save and export to engine
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
:return: None
"""
:return: None
"""
import os
# 1) Create a new, temporary level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=False,
)
import azlmbr.asset as asset
import azlmbr.components as components
import azlmbr.editor as editor
import azlmbr.entity as entity
import azlmbr.legacy.general as general
import azlmbr.bus as bus
import azlmbr.math as math
import azlmbr.paths as paths
general.set_current_view_position(512.0, 480.0, 38.0)
import editor_python_test_tools.hydra_editor_utils as hydra
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
# 2) Create a new entity with required vegetation area components and switch the Vegetation Asset List Source
# Type to External
entity_position = math.Vector3(512.0, 512.0, 32.0)
veg_area_required_components = ["Vegetation Layer Spawner", "Box Shape", "Vegetation Asset List",
"Script Canvas"]
new_entity_id = editor.ToolsApplicationRequestBus(
bus.Broadcast, "CreateNewEntityAtPosition", entity_position, entity.EntityId()
)
if new_entity_id.IsValid():
self.log("Spawner entity created")
spawner_entity = hydra.Entity("Spawner Entity", new_entity_id)
spawner_entity.components = []
for component in veg_area_required_components:
spawner_entity.components.append(hydra.add_component(component, new_entity_id))
hydra.get_set_test(spawner_entity, 2, "Configuration|Source Type", 1)
# 1) Create a new, temporary level
lvl_name = "tmp_level"
helper.init_idle()
level_created = general.create_level_no_prompt(lvl_name, 1024, 1, 4096, False)
general.idle_wait(1.0)
Report.critical_result(Tests.level_created, level_created == 0)
general.set_current_view_position(512.0, 480.0, 38.0)
# Add a Script Canvas component with instance_counter script for launcher tests
instance_counter_path = os.path.join("scriptcanvas", "instance_counter.scriptcanvas")
instance_counter_script = asset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", instance_counter_path,
math.Uuid(), False)
spawner_entity.get_set_test(3, "Script Canvas Asset|Script Canvas Asset", instance_counter_script)
# 2) Create a new entity with required vegetation area components and switch the Vegetation Asset List Source
# Type to External
entity_position = math.Vector3(512.0, 512.0, 32.0)
veg_area_required_components = ["Vegetation Layer Spawner", "Box Shape", "Vegetation Asset List",
"Script Canvas"]
new_entity_id = editor.ToolsApplicationRequestBus(
bus.Broadcast, "CreateNewEntityAtPosition", entity_position, entity.EntityId()
)
spawner_entity = hydra.Entity("Spawner Entity", new_entity_id)
spawner_entity.components = []
for component in veg_area_required_components:
spawner_entity.components.append(hydra.add_component(component, new_entity_id))
hydra.get_set_test(spawner_entity, 2, "Configuration|Source Type", 1)
# Assign a Vegetation Descriptor List asset to the Vegetation Asset List component
descriptor_asset = asset.AssetCatalogRequestBus(
bus.Broadcast, "GetAssetIdByPath", os.path.join("Assets", "VegDescriptorLists", "flower_pink.vegdescriptorlist"), math.Uuid(),
False)
hydra.get_set_test(spawner_entity, 2, "Configuration|External Assets", descriptor_asset)
# Add a Script Canvas component with instance_counter script for launcher tests
instance_counter_path = os.path.join("scriptcanvas", "instance_counter.scriptcanvas")
instance_counter_script = asset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", instance_counter_path,
math.Uuid(), False)
spawner_entity.get_set_test(3, "Script Canvas Asset|Script Canvas Asset", instance_counter_script)
Report.result(Tests.spawner_entity_created, spawner_entity.id.IsValid() and hydra.has_components(spawner_entity.id,
["Script Canvas"]))
# Resize the Box Shape component
new_box_dimensions = math.Vector3(16.0, 16.0, 16.0)
box_dimensions_path = "Box Shape|Box Configuration|Dimensions"
hydra.get_set_test(spawner_entity, 1, box_dimensions_path, new_box_dimensions)
# Assign a Vegetation Descriptor List asset to the Vegetation Asset List component
descriptor_asset = asset.AssetCatalogRequestBus(
bus.Broadcast, "GetAssetIdByPath", os.path.join("Assets", "VegDescriptorLists", "flower_pink.vegdescriptorlist"), math.Uuid(),
False)
hydra.get_set_test(spawner_entity, 2, "Configuration|External Assets", descriptor_asset)
# 3) Create a surface to plant on
dynveg.create_surface_entity("Planting Surface", entity_position, 128.0, 128.0, 1.0)
# Resize the Box Shape component
new_box_dimensions = math.Vector3(16.0, 16.0, 16.0)
box_dimensions_path = "Box Shape|Box Configuration|Dimensions"
hydra.get_set_test(spawner_entity, 1, box_dimensions_path, new_box_dimensions)
# 4) Verify instance counts are accurate
general.idle_wait(3.0) # Allow a few seconds for instances to spawn
num_expected_instances = 20 * 20
box = azlmbr.shape.ShapeComponentRequestsBus(bus.Event, 'GetEncompassingAabb', spawner_entity.id)
num_found = azlmbr.areasystem.AreaSystemRequestBus(bus.Broadcast, 'GetInstanceCountInAabb', box)
self.log(f"Expected {num_expected_instances} instances - Found {num_found} instances")
self.test_success = self.test_success and num_found == num_expected_instances
# 3) Create a surface to plant on
surface_entity = dynveg.create_surface_entity("Planting Surface", entity_position, 128.0, 128.0, 1.0)
Report.result(Tests.surface_entity_created, surface_entity.id.IsValid())
# 5) Move the default Camera entity for testing in the launcher
cam_position = math.Vector3(512.0, 500.0, 35.0)
search_filter = entity.SearchFilter()
search_filter.names = ["Camera"]
search_entity_ids = entity.SearchBus(bus.Broadcast, 'SearchEntities', search_filter)
components.TransformBus(bus.Event, "MoveEntity", search_entity_ids[0], cam_position)
# 4) Verify instance counts are accurate
num_expected_instances = 20 * 20
success = helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id,
num_expected_instances), 5.0)
Report.result(Tests.instance_count, success)
# 6) Save and export to engine
general.save_level()
general.idle_wait(1.0)
general.export_to_engine()
general.idle_wait(1.0)
# 5) Move the default Camera entity for testing in the launcher
cam_position = math.Vector3(512.0, 500.0, 35.0)
search_filter = entity.SearchFilter()
search_filter.names = ["Camera"]
search_entity_ids = entity.SearchBus(bus.Broadcast, 'SearchEntities', search_filter)
components.TransformBus(bus.Event, "MoveEntity", search_entity_ids[0], cam_position)
# 6) Save and export to engine
general.save_level()
general.export_to_engine()
pak_path = os.path.join(paths.devroot, "AutomatedTesting", "cache", "pc", "levels", lvl_name, "level.pak")
Report.result(Tests.saved_and_exported, os.path.exists(pak_path))
test = TestDynamicSliceInstanceSpawnerExternalEditor()
test.run()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(DynamicSliceInstanceSpawner_External_E2E)
@@ -5,109 +5,131 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import sys
import azlmbr.legacy.general as general
import azlmbr.bus as bus
import azlmbr.math as math
import azlmbr.paths
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.editor_test_helper import EditorTestHelper
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
class BehaviorContextTests:
spawner_initialized = (
"Successfully initialized an Empty Instance Spawner",
"Failed to initialize an Empty Instance Spawner"
)
desc_spawnertype_sets_spawner = (
"Setting spawnerType sets spawner too",
"Setting spawnerType failed to set spawner to expected value"
)
desc_spawner_sets_spawnertype = (
"Setting spawner sets spawnerType too",
"Setting spawner failed to set spawnerType to expected value"
)
class TestEmptyInstanceSpawner(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="EmptyInstanceSpawner", args=["level"])
class PropertyTreeTests:
entity_created = (
"Spawner entity created successfully",
"Failed to create spawner entity"
)
spawner_type_set = (
"Successfully set spawner type",
"Failed to set spawner type"
)
empty_instance_count_validation = (
"Expected number of empty instances planted",
"Unexpected number of empty instances planted"
)
not_affected_by_allow_empty_assets = (
"Instance count unaffected by Allow Empty Assets toggle",
"Instance count was unexpectedly affected by Allow Empty Assets toggle"
)
def run_test(self):
"""
Summary:
Test aspects of the EmptyInstanceSpawner through the BehaviorContext and the Property Tree.
:return: None
"""
# 1) Open an empty level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=False,
def EmptyInstanceSpawner_EmptySpawnerWorks():
"""
Summary:
Test aspects of the EmptyInstanceSpawner through the BehaviorContext and the Property Tree.
:return: None
"""
import azlmbr.legacy.general as general
import azlmbr.math as math
import editor_python_test_tools.hydra_editor_utils as hydra
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
# 1) Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
general.set_current_view_position(512.0, 480.0, 38.0)
# Grab the UUID that we need for creating an Empty Spawner
empty_spawner_uuid = azlmbr.math.Uuid_CreateString('{23C40FD4-A55F-4BD3-BE5B-DC5423F217C2}', 0)
# 2) Test EmptyInstanceSpawner BehaviorContext
behavior_context_test_success = True
empty_spawner = azlmbr.vegetation.EmptyInstanceSpawner()
behavior_context_test_success = behavior_context_test_success and (empty_spawner is not None)
behavior_context_test_success = behavior_context_test_success and (empty_spawner.typename == 'EmptyInstanceSpawner')
Report.critical_result(BehaviorContextTests.spawner_initialized, behavior_context_test_success)
Report.info(f'EmptyInstanceSpawner() BehaviorContext test: {behavior_context_test_success}')
# 3) Test Descriptor BehaviorContext - setting spawnerType sets spawner too
spawner_type_test_success = True
descriptor = azlmbr.vegetation.Descriptor()
spawner_type_test_success = spawner_type_test_success and hydra.get_set_property_test(descriptor, 'spawnerType', empty_spawner_uuid)
spawner_type_test_success = spawner_type_test_success and (descriptor.spawner.typename == 'EmptyInstanceSpawner')
Report.result(BehaviorContextTests.desc_spawnertype_sets_spawner, spawner_type_test_success)
Report.info(f'Descriptor() BehaviorContext spawnerType test: {spawner_type_test_success}')
# 4) Test Descriptor BehaviorContext - setting spawner sets spawnerType too
spawner_test_success = True
descriptor = azlmbr.vegetation.Descriptor()
descriptor.spawner = empty_spawner
spawner_test_success = spawner_test_success and (descriptor.spawnerType.Equal(empty_spawner_uuid))
spawner_test_success = spawner_test_success and (descriptor.spawner.typename == 'EmptyInstanceSpawner')
Report.result(BehaviorContextTests.desc_spawner_sets_spawnertype, spawner_test_success)
Report.info(f'Descriptor() BehaviorContext spawner test: {spawner_test_success}')
### Setup for Property Tree set of tests
# Create a new entity with required vegetation area components
spawner_entity = hydra.Entity("Veg Area")
spawner_entity.create_entity(
math.Vector3(512.0, 512.0, 32.0),
["Vegetation Layer Spawner", "Box Shape", "Vegetation Asset List"]
)
general.idle_wait(1.0)
general.set_current_view_position(512.0, 480.0, 38.0)
Report.critical_result(PropertyTreeTests.entity_created, spawner_entity.id.IsValid())
# Grab the UUID that we need for creating an Empty Spawner
empty_spawner_uuid = azlmbr.math.Uuid_CreateString('{23C40FD4-A55F-4BD3-BE5B-DC5423F217C2}', 0)
# Resize the Box Shape component
new_box_dimensions = math.Vector3(16.0, 16.0, 16.0)
box_dimensions_path = "Box Shape|Box Configuration|Dimensions"
spawner_entity.get_set_test(1, box_dimensions_path, new_box_dimensions)
# 2) Test EmptyInstanceSpawner BehaviorContext
behavior_context_test_success = True
empty_spawner = azlmbr.vegetation.EmptyInstanceSpawner()
behavior_context_test_success = behavior_context_test_success and (empty_spawner is not None)
behavior_context_test_success = behavior_context_test_success and (empty_spawner.typename == 'EmptyInstanceSpawner')
self.test_success = self.test_success and behavior_context_test_success
self.log(f'EmptyInstanceSpawner() BehaviorContext test: {behavior_context_test_success}')
# Create a surface to plant on
dynveg.create_surface_entity("Surface Entity", math.Vector3(512.0, 512.0, 32.0), 1024.0, 1024.0, 1.0)
# 3) Test Descriptor BehaviorContext - setting spawnerType sets spawner too
spawner_type_test_success = True
descriptor = azlmbr.vegetation.Descriptor()
spawner_type_test_success = spawner_type_test_success and hydra.get_set_property_test(descriptor, 'spawnerType', empty_spawner_uuid)
spawner_type_test_success = spawner_type_test_success and (descriptor.spawner.typename == 'EmptyInstanceSpawner')
self.test_success = self.test_success and spawner_type_test_success
self.log(f'Descriptor() BehaviorContext spawnerType test: {spawner_type_test_success}')
# 5) Descriptor Property Tree test: spawner type can be set
# 4) Test Descriptor BehaviorContext - setting spawner sets spawnerType too
spawner_test_success = True
descriptor = azlmbr.vegetation.Descriptor()
descriptor.spawner = empty_spawner
spawner_test_success = spawner_test_success and (descriptor.spawnerType.Equal(empty_spawner_uuid))
spawner_test_success = spawner_test_success and (descriptor.spawner.typename == 'EmptyInstanceSpawner')
self.test_success = self.test_success and spawner_test_success
self.log(f'Descriptor() BehaviorContext spawner test: {spawner_test_success}')
# - Validate the empty spawner type can be set correctly.
property_tree_success = True
property_tree_success = property_tree_success and spawner_entity.get_set_test(2, 'Configuration|Embedded Assets|[0]|Instance Spawner', empty_spawner_uuid)
Report.result(PropertyTreeTests.spawner_type_set, property_tree_success)
### Setup for Property Tree set of tests
# This should result in 400 instances, since our box is 16 m x 16 m and by default the veg system plants
# 20 instances per 16 meters
num_expected_instances = 20 * 20
property_tree_success = property_tree_success and helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected_instances), 5.0)
Report.result(PropertyTreeTests.empty_instance_count_validation, property_tree_success)
Report.info(f'Property Tree spawner type test: {property_tree_success}')
# Create a new entity with required vegetation area components
spawner_entity = hydra.Entity("Veg Area")
spawner_entity.create_entity(
math.Vector3(512.0, 512.0, 32.0),
["Vegetation Layer Spawner", "Box Shape", "Vegetation Asset List"]
)
if spawner_entity.id.IsValid():
self.log(f"'{spawner_entity.name}' created")
# Resize the Box Shape component
new_box_dimensions = math.Vector3(16.0, 16.0, 16.0)
box_dimensions_path = "Box Shape|Box Configuration|Dimensions"
spawner_entity.get_set_test(1, box_dimensions_path, new_box_dimensions)
# Create a surface to plant on
dynveg.create_surface_entity("Surface Entity", math.Vector3(512.0, 512.0, 32.0), 1024.0, 1024.0, 1.0)
# 5) Descriptor Property Tree test: spawner type can be set
# - Validate the empty spawner type can be set correctly.
property_tree_success = True
property_tree_success = property_tree_success and spawner_entity.get_set_test(2, 'Configuration|Embedded Assets|[0]|Instance Spawner', empty_spawner_uuid)
# This should result in 400 instances, since our box is 16 m x 16 m and by default the veg system plants
# 20 instances per 16 meters
num_expected_instances = 20 * 20
property_tree_success = property_tree_success and self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected_instances), 5.0)
self.test_success = self.test_success and property_tree_success
self.log(f'Property Tree spawner type test: {property_tree_success}')
# 6) Validate that the "Allow Empty Assets" setting doesn't affect the EmptyInstanceSpawner
allow_empty_assets_success = True
spawner_entity.get_set_test(0, 'Configuration|Allow Empty Assets', False)
allow_empty_assets_success = allow_empty_assets_success and self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected_instances), 5.0)
self.test_success = self.test_success and allow_empty_assets_success
self.log(f'Allow Empty Assets test: {allow_empty_assets_success}')
# 6) Validate that the "Allow Empty Assets" setting doesn't affect the EmptyInstanceSpawner
allow_empty_assets_success = True
spawner_entity.get_set_test(0, 'Configuration|Allow Empty Assets', False)
allow_empty_assets_success = allow_empty_assets_success and helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected_instances), 5.0)
Report.result(PropertyTreeTests.not_affected_by_allow_empty_assets, allow_empty_assets_success)
Report.info(f'Allow Empty Assets test: {allow_empty_assets_success}')
test = TestEmptyInstanceSpawner()
test.run()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(EmptyInstanceSpawner_EmptySpawnerWorks)
@@ -5,111 +5,115 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.editor as editor
import azlmbr.legacy.general as general
import azlmbr.bus as bus
import azlmbr.math as math
import azlmbr.paths
import azlmbr.vegetation as vegetation
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.editor_test_helper import EditorTestHelper
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
class Tests:
initial_instance_count = (
"Initial instance count is as expected",
"Initial instance count does not match expected results"
)
layer_priority_instance_count = (
"Instance count is as expected after updating layer priorities",
"Instance count does not match expected results after updating layer priorities"
)
sub_priority_instance_count = (
"Instance count is as expected after updating sub priorities",
"Instance count does not match expected results after updating sub priorities"
)
class TestInstanceSpawnerPriority(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="InstanceSpawnerPriority", args=["level"])
def InstanceSpawnerPriority_LayerAndSubPriority():
"""
Summary:
A new level is created. An instance spawner area and blocker area are setup to overlap. Instance counts are
verified with the initial setup. Layer priority on the blocker area is set to lower than the instance spawner
area, and instance counts are re-verified.
def run_test(self):
"""
Summary:
A new level is created. An instance spawner area and blocker area are setup to overlap. Instance counts are
verified with the initial setup. Layer priority on the blocker area is set to lower than the instance spawner
area, and instance counts are re-verified.
Expected Behavior:
Vegetation areas with a higher Layer Priority plant over those with a lower Layer Priority
Expected Behavior:
Vegetation areas with a higher Layer Priority plant over those with a lower Layer Priority
Test Steps:
1) Open a simple level
2) Create overlapping instance spawner and blocker areas
3) Create a surface to plant on
4) Validate initial instance counts in the spawner area
5) Reduce the Layer Priority of the blocker area
6) Validate instance counts in the spawner area
Test Steps:
1) Create a new level
2) Create overlapping instance spawner and blocker areas
3) Create a surface to plant on
4) Validate initial instance counts in the spawner area
5) Reduce the Layer Priority of the blocker area
6) Validate instance counts in the spawner area
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
:return: None
"""
:return: None
"""
# 1) Create a new, temporary level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=False,
)
import os
# Set view of planting area for visual debugging
general.set_current_view_position(512.0, 500.0, 38.0)
general.set_current_view_rotation(-20.0, 0.0, 0.0)
import azlmbr.editor as editor
import azlmbr.legacy.general as general
import azlmbr.bus as bus
import azlmbr.math as math
# 2) Create overlapping areas: 1 instance spawner area, and 1 blocker area
spawner_center_point = math.Vector3(508.0, 508.0, 32.0)
asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
spawner_entity = dynveg.create_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 1.0,
asset_path)
blocker_center_point = math.Vector3(516.0, 516.0, 32.0)
blocker_entity = dynveg.create_blocker_area("Instance Blocker", blocker_center_point, 16.0, 16.0, 1.0)
import editor_python_test_tools.hydra_editor_utils as hydra
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
# 3) Create a surface for planting
planting_surface_center_point = math.Vector3(512.0, 512.0, 32.0)
dynveg.create_surface_entity("Planting Surface", planting_surface_center_point, 64.0, 64.0, 1.0)
# 1) Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
# Set instances to spawn on a center snap point to avoid unexpected instances around the edges of the box shape
veg_system_settings_component = hydra.add_level_component("Vegetation System Settings")
editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", veg_system_settings_component,
'Configuration|Area System Settings|Sector Point Snap Mode', 1)
# Set view of planting area for visual debugging
general.set_current_view_position(512.0, 500.0, 38.0)
general.set_current_view_rotation(-20.0, 0.0, 0.0)
# 4) Validate the expected instance count with initial setup. GetAreaProductCount is used as
# GetInstanceCountInAabb does not filter out blocked instances
num_expected = (20 * 20) - (10 * 10) # 20 instances per 16m per side minus 1 blocked quadrant
result = self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id,
num_expected), 5.0)
self.test_success = self.test_success and result
# 2) Create overlapping areas: 1 instance spawner area, and 1 blocker area
spawner_center_point = math.Vector3(508.0, 508.0, 32.0)
asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
spawner_entity = dynveg.create_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 1.0,
asset_path)
blocker_center_point = math.Vector3(516.0, 516.0, 32.0)
blocker_entity = dynveg.create_blocker_area("Instance Blocker", blocker_center_point, 16.0, 16.0, 1.0)
# 5) Change the Instance Spawner area to a higher layer priority than the Instance Blocker
blocker_entity.get_set_test(0, 'Configuration|Layer Priority', 0)
# 3) Create a surface for planting
planting_surface_center_point = math.Vector3(512.0, 512.0, 32.0)
dynveg.create_surface_entity("Planting Surface", planting_surface_center_point, 64.0, 64.0, 1.0)
# 6) Validate the expected instance count with changed area priorities
num_expected = 20 * 20 # 20 instances per 16m per side, no instances should be blocked at this point
result = self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id,
num_expected), 5.0)
self.test_success = self.test_success and result
# Set instances to spawn on a center snap point to avoid unexpected instances around the edges of the box shape
veg_system_settings_component = hydra.add_level_component("Vegetation System Settings")
editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", veg_system_settings_component,
'Configuration|Area System Settings|Sector Point Snap Mode', 1)
# 7) Revert Layer Priority changes so both areas are equal, and change Sub Priority to a higher value on the
# Instance Spawner area
blocker_entity.get_set_test(0, 'Configuration|Layer Priority', 1)
spawner_entity.get_set_test(0, 'Configuration|Sub Priority', 100)
blocker_entity.get_set_test(0, 'Configuration|Sub Priority', 1)
# 4) Validate the expected instance count with initial setup. GetAreaProductCount is used as
# GetInstanceCountInAabb does not filter out blocked instances
num_expected = (20 * 20) - (10 * 10) # 20 instances per 16m per side minus 1 blocked quadrant
result = helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id,
num_expected), 5.0)
Report.result(Tests.initial_instance_count, result)
# 8) Validate the expected instance count with changed area priorities
num_expected = 20 * 20 # 20 instances per 16m per side, no instances should be blocked at this point
result = self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id,
num_expected), 5.0)
self.test_success = self.test_success and result
# 5) Change the Instance Spawner area to a higher layer priority than the Instance Blocker
blocker_entity.get_set_test(0, 'Configuration|Layer Priority', 0)
# 6) Validate the expected instance count with changed area priorities
num_expected = 20 * 20 # 20 instances per 16m per side, no instances should be blocked at this point
result = helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id,
num_expected), 5.0)
Report.result(Tests.layer_priority_instance_count, result)
# 7) Revert Layer Priority changes so both areas are equal, and change Sub Priority to a higher value on the
# Instance Spawner area
blocker_entity.get_set_test(0, 'Configuration|Layer Priority', 1)
spawner_entity.get_set_test(0, 'Configuration|Sub Priority', 100)
blocker_entity.get_set_test(0, 'Configuration|Sub Priority', 1)
# 8) Validate the expected instance count with changed area priorities
num_expected = 20 * 20 # 20 instances per 16m per side, no instances should be blocked at this point
result = helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id,
num_expected), 5.0)
Report.result(Tests.sub_priority_instance_count, result)
test = TestInstanceSpawnerPriority()
test.run()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(InstanceSpawnerPriority_LayerAndSubPriority)
@@ -5,151 +5,161 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
"""
C2627906: A simple Vegetation Layer Blender area can be created
"""
import os
from math import radians
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.asset as asset
import azlmbr.areasystem as areasystem
import azlmbr.legacy.general as general
import azlmbr
import azlmbr.bus as bus
import azlmbr.components as components
import azlmbr.math as math
import azlmbr.entity as entity
import azlmbr.paths
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.editor_test_helper import EditorTestHelper
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
class Tests:
level_created = (
"Successfully created level",
"Failed to create level"
)
blender_entity_created = (
"Blender entity created successfully",
"Failed to create Blender entity"
)
instance_count = (
"Found the expected number of instances in the Blender area",
"Found an unexpected number of instances in the Blender area"
)
instances_blended = (
"Instances from each spawner are blended as expected",
"Found an unexpected number of instances from each spawner"
)
saved_and_exported = (
"Saved and exported level successfully",
"Failed to save and export level"
)
class TestVegLayerBlenderCreated(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="LayerBlender_E2E_Editor", args=["level"])
self.screenshot_count = 0
def LayerBlender_E2E_Editor():
"""
Summary:
A temporary level is loaded. Two vegetation areas with different meshes are added and then
pinned to a vegetation blender. Screenshots are taken in the editor normal mode and in game mode.
def run_test(self):
"""
Summary:
A temporary level is loaded. Two vegetation areas with different meshes are added and then
pinned to a vegetation blender. Screenshots are taken in the editor normal mode and in game mode.
Expected Behavior:
The specified assets plant in the specified blend area and are visible in the Viewport in
Edit Mode, Game Mode.
Expected Behavior:
The specified assets plant in the specified blend area and are visible in the Viewport in
Edit Mode, Game Mode.
Test Steps:
1) Create level
2) Create 2 vegetation areas with different meshes
3) Create Blender entity and pin the vegetation areas
4) Take screenshot in normal mode
5) Create a new entity with a Camera component for testing in the launcher
6) Save level and take screenshot in game mode
7) Export to engine
Test Steps:
1) Create level
2) Create 2 vegetation areas with different meshes
3) Create Blender entity and pin the vegetation areas
4) Take screenshot in normal mode
5) Create a new entity with a Camera component for testing in the launcher
6) Save level and take screenshot in game mode
7) Export to engine
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
:return: None
"""
:return: None
"""
import os
from math import radians
# 1) Create/prepare a new level and set an appropriate view of blender area
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=False,
)
import azlmbr.asset as asset
import azlmbr.areasystem as areasystem
import azlmbr.legacy.general as general
import azlmbr.paths as paths
import azlmbr.bus as bus
import azlmbr.components as components
import azlmbr.math as math
import azlmbr.entity as entity
general.set_current_view_position(500.49, 498.69, 46.66)
general.set_current_view_rotation(-42.05, 0.00, -36.33)
import editor_python_test_tools.hydra_editor_utils as hydra
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
# 2) Create 2 vegetation areas with different meshes
purple_position = math.Vector3(504.0, 512.0, 32.0)
purple_asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice")
spawner_entity_1 = dynveg.create_vegetation_area("Purple Spawner",
purple_position,
16.0, 16.0, 1.0,
purple_asset_path)
# 1) Create a new, temporary level
lvl_name = "tmp_level"
helper.init_idle()
level_created = general.create_level_no_prompt(lvl_name, 1024, 1, 4096, False)
general.idle_wait(1.0)
Report.critical_result(Tests.level_created, level_created == 0)
pink_position = math.Vector3(520.0, 512.0, 32.0)
pink_asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
spawner_entity_2 = dynveg.create_vegetation_area("Pink Spawner",
pink_position,
16.0, 16.0, 1.0,
pink_asset_path)
general.set_current_view_position(500.49, 498.69, 46.66)
general.set_current_view_rotation(-42.05, 0.00, -36.33)
base_position = math.Vector3(512.0, 512.0, 32.0)
dynveg.create_surface_entity("Surface Entity",
base_position,
16.0, 16.0, 1.0)
# 2) Create 2 vegetation areas with different meshes
purple_position = math.Vector3(504.0, 512.0, 32.0)
purple_asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice")
spawner_entity_1 = dynveg.create_vegetation_area("Purple Spawner",
purple_position,
16.0, 16.0, 1.0,
purple_asset_path)
hydra.add_level_component("Vegetation Debugger")
pink_position = math.Vector3(520.0, 512.0, 32.0)
pink_asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
spawner_entity_2 = dynveg.create_vegetation_area("Pink Spawner",
pink_position,
16.0, 16.0, 1.0,
pink_asset_path)
# 3) Create Blender entity and pin the vegetation areas. We also add and attach a Lua script to validate in the
# launcher for the follow-up test
blender_entity = hydra.Entity("Blender")
blender_entity.create_entity(
base_position,
["Box Shape", "Vegetation Layer Blender", "Lua Script"]
)
if blender_entity.id.IsValid():
print(f"'{blender_entity.name}' created")
base_position = math.Vector3(512.0, 512.0, 32.0)
dynveg.create_surface_entity("Surface Entity",
base_position,
16.0, 16.0, 1.0)
blender_entity.get_set_test(0, "Box Shape|Box Configuration|Dimensions", math.Vector3(16.0, 16.0, 1.0))
blender_entity.get_set_test(1, "Configuration|Vegetation Areas", [spawner_entity_1.id, spawner_entity_2.id])
instance_counter_path = os.path.join("luascripts", "instance_counter_blender.lua")
instance_counter_script = asset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", instance_counter_path,
math.Uuid(), False)
blender_entity.get_set_test(2, "Script properties|Asset", instance_counter_script)
hydra.add_level_component("Vegetation Debugger")
# 4) Verify instances in blender area are equally represented by both descriptors
# 3) Create Blender entity and pin the vegetation areas. We also add and attach a Lua script to validate in the
# launcher for the follow-up test
blender_entity = hydra.Entity("Blender")
blender_entity.create_entity(
base_position,
["Box Shape", "Vegetation Layer Blender", "Lua Script"]
)
Report.result(Tests.blender_entity_created, blender_entity.id.IsValid())
# Wait for instances to spawn
general.run_console('veg_debugClearAllAreas')
num_expected = 20 * 20
self.test_success = self.test_success and self.wait_for_condition(
lambda: dynveg.validate_instance_count(base_position, 8.0, num_expected), 5.0)
blender_entity.get_set_test(0, "Box Shape|Box Configuration|Dimensions", math.Vector3(16.0, 16.0, 1.0))
blender_entity.get_set_test(1, "Configuration|Vegetation Areas", [spawner_entity_1.id, spawner_entity_2.id])
instance_counter_path = os.path.join("luascripts", "instance_counter_blender.lua")
instance_counter_script = asset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", instance_counter_path,
math.Uuid(), False)
blender_entity.get_set_test(2, "Script properties|Asset", instance_counter_script)
if self.test_success:
box = math.Aabb_CreateCenterRadius(base_position, 8.0)
instances = areasystem.AreaSystemRequestBus(bus.Broadcast, 'GetInstancesInAabb', box)
pink_count = 0
purple_count = 0
for instance in instances:
purple_asset_path = purple_asset_path.replace("\\", "/").lower()
pink_asset_path = pink_asset_path.replace("\\", "/").lower()
if instance.descriptor.spawner.GetSliceAssetPath() == pink_asset_path:
pink_count += 1
elif instance.descriptor.spawner.GetSliceAssetPath() == purple_asset_path:
purple_count += 1
self.test_success = pink_count == purple_count and (pink_count + purple_count == num_expected) and self.test_success
# 4) Verify instances in blender area are equally represented by both descriptors
# 5) Move the default Camera entity for testing in the launcher
cam_position = math.Vector3(500.0, 500.0, 47.0)
cam_rot_degrees_vector = math.Vector3(radians(-55.0), radians(28.5), radians(-17.0))
search_filter = entity.SearchFilter()
search_filter.names = ["Camera"]
search_entity_ids = entity.SearchBus(bus.Broadcast, 'SearchEntities', search_filter)
components.TransformBus(bus.Event, "MoveEntity", search_entity_ids[0], cam_position)
azlmbr.components.TransformBus(bus.Event, "SetLocalRotation", search_entity_ids[0], cam_rot_degrees_vector)
# Wait for instances to spawn
general.run_console('veg_debugClearAllAreas')
num_expected = 20 * 20
success = helper.wait_for_condition(
lambda: dynveg.validate_instance_count(base_position, 8.0, num_expected), 5.0)
Report.critical_result(Tests.instance_count, success)
# 6) Save and export level
general.save_level()
general.idle_wait(1.0)
general.export_to_engine()
general.idle_wait(1.0)
box = math.Aabb_CreateCenterRadius(base_position, 8.0)
instances = areasystem.AreaSystemRequestBus(bus.Broadcast, 'GetInstancesInAabb', box)
pink_count = 0
purple_count = 0
for instance in instances:
purple_asset_path = purple_asset_path.replace("\\", "/").lower()
pink_asset_path = pink_asset_path.replace("\\", "/").lower()
if instance.descriptor.spawner.GetSliceAssetPath() == pink_asset_path:
pink_count += 1
elif instance.descriptor.spawner.GetSliceAssetPath() == purple_asset_path:
purple_count += 1
Report.result(Tests.instances_blended, pink_count == purple_count and (pink_count + purple_count == num_expected))
# 5) Move the default Camera entity for testing in the launcher
cam_position = math.Vector3(500.0, 500.0, 47.0)
cam_rot_degrees_vector = math.Vector3(radians(-55.0), radians(28.5), radians(-17.0))
search_filter = entity.SearchFilter()
search_filter.names = ["Camera"]
search_entity_ids = entity.SearchBus(bus.Broadcast, 'SearchEntities', search_filter)
components.TransformBus(bus.Event, "MoveEntity", search_entity_ids[0], cam_position)
azlmbr.components.TransformBus(bus.Event, "SetLocalRotation", search_entity_ids[0], cam_rot_degrees_vector)
# 6) Save and export to engine
general.save_level()
general.export_to_engine()
pak_path = os.path.join(paths.devroot, "AutomatedTesting", "cache", "pc", "levels", lvl_name, "level.pak")
Report.result(Tests.saved_and_exported, os.path.exists(pak_path))
test = TestVegLayerBlenderCreated()
test.run()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(LayerBlender_E2E_Editor)
@@ -5,102 +5,105 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import sys
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.math as math
import azlmbr.legacy.general as general
import azlmbr.paths
sys.path.append(os.path.join(azlmbr.paths.devroot, "AutomatedTesting", "Gem", "PythonTests"))
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.editor_test_helper import EditorTestHelper
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
class Tests:
initial_instance_count = (
"Initial instance count is as expected",
"Unexpected number of initial instances found"
)
blocked_instance_count = (
"Expected number of instances found after configuring Blocker",
"Unexpected number of instances found after configuring Blocker"
)
class TestLayerBlocker(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="LayerBlocker_InstancesBlocked", args=["level"])
def run_test(self):
"""
Summary:
An empty level is created. A Vegetation Layer Spawner area is configured. A Vegetation Layer Blocker area is
configured to block instances in the spawner area.
def LayerBlocker_InstancesBlockedInConfiguredArea():
"""
Summary:
An empty level is created. A Vegetation Layer Spawner area is configured. A Vegetation Layer Blocker area is
configured to block instances in the spawner area.
Expected Behavior:
Vegetation is blocked by the configured Blocker area.
Expected Behavior:
Vegetation is blocked by the configured Blocker area.
Test Steps:
1. A new level is created
2. Vegetation Layer Spawner area is created
3. Planting surface is created
4. Vegetation System Settings level component is added, and Snap Mode set to center to ensure expected instance
counts are accurate in the configured vegetation area
5. Initial instance counts pre-blocker are validated
6. A Vegetation Layer Blocker area is created, overlapping the spawner area
7. Post-blocker instance counts are validated
Test Steps:
1. A simple level is opened
2. Vegetation Layer Spawner area is created
3. Planting surface is created
4. Vegetation System Settings level component is added, and Snap Mode set to center to ensure expected instance
counts are accurate in the configured vegetation area
5. Initial instance counts pre-blocker are validated
6. A Vegetation Layer Blocker area is created, overlapping the spawner area
7. Post-blocker instance counts are validated
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
:return: None
"""
:return: None
"""
# 1) Create a new, temporary level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=False,
)
import os
# Set view of planting area for visual debugging
general.set_current_view_position(512.0, 500.0, 38.0)
general.set_current_view_rotation(-20.0, 0.0, 0.0)
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.math as math
import azlmbr.legacy.general as general
# 2) Create a new instance spawner entity
spawner_center_point = math.Vector3(512.0, 512.0, 32.0)
asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
spawner_entity = dynveg.create_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 16.0,
asset_path)
import editor_python_test_tools.hydra_editor_utils as hydra
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
# 3) Create surface for planting on
dynveg.create_surface_entity("Surface Entity", spawner_center_point, 32.0, 32.0, 1.0)
# 1) Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
# 4) Add a Vegetation System Settings Level component and set Sector Point Snap Mode to Center
veg_system_settings_component = hydra.add_level_component("Vegetation System Settings")
editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", veg_system_settings_component,
'Configuration|Area System Settings|Sector Point Snap Mode', 1)
# Set view of planting area for visual debugging
general.set_current_view_position(512.0, 500.0, 38.0)
general.set_current_view_rotation(-20.0, 0.0, 0.0)
# 5) Verify initial instance counts
num_expected = 20 * 20
success = self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id,
num_expected), 5.0)
self.test_success = success and self.test_success
# 2) Create a new instance spawner entity
spawner_center_point = math.Vector3(512.0, 512.0, 32.0)
asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
spawner_entity = dynveg.create_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 16.0,
asset_path)
# 6) Create a new Vegetation Layer Blocker area overlapping the spawner area
blocker_entity = hydra.Entity("Blocker Area")
blocker_entity.create_entity(
spawner_center_point,
["Vegetation Layer Blocker", "Box Shape"]
)
if blocker_entity.id.IsValid():
print(f"'{blocker_entity.name}' created")
blocker_entity.get_set_test(1, "Box Shape|Box Configuration|Dimensions",
math.Vector3(3.0, 3.0, 3.0))
# 3) Create surface for planting on
dynveg.create_surface_entity("Surface Entity", spawner_center_point, 32.0, 32.0, 1.0)
# 7) Validate instance counts post-blocker. 16 instances should now be blocked in the center of the spawner area
num_expected = (20 * 20) - 16
success = self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id,
num_expected), 5.0)
self.test_success = success and self.test_success
# 4) Add a Vegetation System Settings Level component and set Sector Point Snap Mode to Center
veg_system_settings_component = hydra.add_level_component("Vegetation System Settings")
editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", veg_system_settings_component,
'Configuration|Area System Settings|Sector Point Snap Mode', 1)
# 5) Verify initial instance counts
num_expected = 20 * 20
success = helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id,
num_expected), 5.0)
Report.result(Tests.initial_instance_count, success)
# 6) Create a new Vegetation Layer Blocker area overlapping the spawner area
blocker_entity = hydra.Entity("Blocker Area")
blocker_entity.create_entity(
spawner_center_point,
["Vegetation Layer Blocker", "Box Shape"]
)
if blocker_entity.id.IsValid():
print(f"'{blocker_entity.name}' created")
blocker_entity.get_set_test(1, "Box Shape|Box Configuration|Dimensions",
math.Vector3(3.0, 3.0, 3.0))
# 7) Validate instance counts post-blocker. 16 instances should now be blocked in the center of the spawner area
num_expected = (20 * 20) - 16
success = helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id,
num_expected), 5.0)
Report.result(Tests.blocked_instance_count, success)
test = TestLayerBlocker()
test.run()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(LayerBlocker_InstancesBlockedInConfiguredArea)
@@ -5,85 +5,83 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import sys
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.legacy.general as general
import azlmbr.math as math
import azlmbr.paths
sys.path.append(os.path.join(azlmbr.paths.devroot, "AutomatedTesting", "Gem", "PythonTests"))
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.editor_test_helper import EditorTestHelper
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
class Tests:
preprocess_instance_count = (
"Preprocess filter stage vegetation instance count is as expected",
"Preprocess filter stage instance count found an unexpected number of instances"
)
postprocess_instance_count = (
"Postprocess filter stage vegetation instance count is as expected",
"Postprocess filter stage instance count found an unexpected number of instances"
)
class TestLayerSpawnerFilterStageToggle(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="LayerSpawner_FilterStageToggle", args=["level"])
def LayerSpawner_FilterStageToggle():
"""
Summary:
Filter Stage toggle affects final vegetation position.
def run_test(self):
"""
Summary:
C4765973 Filter Stage toggle affects final vegetation position.
Expected Result:
Vegetation instances plant differently depending on the Filter Stage setting.
Expected Result:
Vegetation instances plant differently depending on the Filter Stage setting.
:return: None
"""
:return: None
"""
import os
PREPROCESS_INSTANCE_COUNT = 21
POSTPROCESS_INSTANCE_COUNT = 19
import azlmbr.legacy.general as general
import azlmbr.math as math
# Create empty level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=False,
)
import editor_python_test_tools.hydra_editor_utils as hydra
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
general.set_current_view_position(500.49, 498.69, 46.66)
general.set_current_view_rotation(-42.05, 0.00, -36.33)
PREPROCESS_INSTANCE_COUNT = 21
POSTPROCESS_INSTANCE_COUNT = 19
# Create a vegetation area with all needed components
position = math.Vector3(512.0, 512.0, 32.0)
asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
vegetation_entity = dynveg.create_vegetation_area("vegetation", position, 16.0, 16.0, 16.0, asset_path)
vegetation_entity.add_component("Vegetation Altitude Filter")
vegetation_entity.add_component("Vegetation Position Modifier")
# Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
# Create a child entity under vegetation area
child_entity = hydra.Entity("child_entity")
components_to_add = ["Random Noise Gradient", "Gradient Transform Modifier", "Box Shape"]
child_entity.create_entity(position, components_to_add, vegetation_entity.id)
general.set_current_view_position(500.49, 498.69, 46.66)
general.set_current_view_rotation(-42.05, 0.00, -36.33)
# Set the Gradient Id in X and Y direction
vegetation_entity.get_set_test(4, "Configuration|Position X|Gradient|Gradient Entity Id", child_entity.id)
vegetation_entity.get_set_test(4, "Configuration|Position Y|Gradient|Gradient Entity Id", child_entity.id)
# Create a vegetation area with all needed components
position = math.Vector3(512.0, 512.0, 32.0)
asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
vegetation_entity = dynveg.create_vegetation_area("vegetation", position, 16.0, 16.0, 16.0, asset_path)
vegetation_entity.add_component("Vegetation Altitude Filter")
vegetation_entity.add_component("Vegetation Position Modifier")
# Set the min and max values for Altitude Filter
vegetation_entity.get_set_test(3, "Configuration|Altitude Min", 34.0)
vegetation_entity.get_set_test(3, "Configuration|Altitude Max", 38.0)
# Create a child entity under vegetation area
child_entity = hydra.Entity("child_entity")
components_to_add = ["Random Noise Gradient", "Gradient Transform Modifier", "Box Shape"]
child_entity.create_entity(position, components_to_add, vegetation_entity.id)
# Add entity with Mesh to replicate creation of hills and a flat surface to plant on
dynveg.create_surface_entity("Flat Surface", position, 32.0, 32.0, 1.0)
hill_entity = dynveg.create_mesh_surface_entity_with_slopes("hill", position, 4.0)
# Set the Gradient Id in X and Y direction
vegetation_entity.get_set_test(4, "Configuration|Position X|Gradient|Gradient Entity Id", child_entity.id)
vegetation_entity.get_set_test(4, "Configuration|Position Y|Gradient|Gradient Entity Id", child_entity.id)
# Set the filter stage to preprocess and postprocess respectively and verify instance count
vegetation_entity.get_set_test(0, "Configuration|Filter Stage", 1)
self.wait_for_condition(lambda: dynveg.validate_instance_count(position, 16.0, PREPROCESS_INSTANCE_COUNT), 3.0)
result = dynveg.validate_instance_count(position, 16.0, PREPROCESS_INSTANCE_COUNT)
self.log(f"Preprocess filter stage vegetation instance count is as expected: {result}")
vegetation_entity.get_set_test(0, "Configuration|Filter Stage", 2)
self.wait_for_condition(lambda: dynveg.validate_instance_count(position, 16.0, POSTPROCESS_INSTANCE_COUNT), 3.0)
result = dynveg.validate_instance_count(position, 16.0, POSTPROCESS_INSTANCE_COUNT)
self.log(f"Postprocess filter vegetation instance stage count is as expected: {result}")
# Set the min and max values for Altitude Filter
vegetation_entity.get_set_test(3, "Configuration|Altitude Min", 34.0)
vegetation_entity.get_set_test(3, "Configuration|Altitude Max", 38.0)
# Add entity with Mesh to replicate creation of hills and a flat surface to plant on
dynveg.create_surface_entity("Flat Surface", position, 32.0, 32.0, 1.0)
hill_entity = dynveg.create_mesh_surface_entity_with_slopes("hill", position, 4.0)
# Set the filter stage to preprocess and postprocess respectively and verify instance count
vegetation_entity.get_set_test(0, "Configuration|Filter Stage", 1)
result = helper.wait_for_condition(lambda: dynveg.validate_instance_count(position, 16.0, PREPROCESS_INSTANCE_COUNT), 3.0)
Report.result(Tests.preprocess_instance_count, result)
vegetation_entity.get_set_test(0, "Configuration|Filter Stage", 2)
result = helper.wait_for_condition(lambda: dynveg.validate_instance_count(position, 16.0, POSTPROCESS_INSTANCE_COUNT), 3.0)
Report.result(Tests.postprocess_instance_count, result)
test = TestLayerSpawnerFilterStageToggle()
test.run()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(LayerSpawner_FilterStageToggle)
@@ -5,118 +5,116 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import sys
import azlmbr.math as math
import azlmbr.legacy.general as general
import azlmbr.paths
import azlmbr.surface_data as surface_data
import azlmbr.vegetation as vegetation
sys.path.append(os.path.join(azlmbr.paths.devroot, "AutomatedTesting", "Gem", "PythonTests"))
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.editor_test_helper import EditorTestHelper
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
class Tests:
inherit_behavior_checked = (
"Found no instances with Inherit Behavior checked as expected",
"Unexpectedly found instances with Inherit Behavior checked"
)
inherit_behavior_unchecked = (
"Found instances with Inherit Behavior unchecked as expected",
"Unexpectedly found no instances with Inherit Behavior unchecked"
)
class TestLayerSpawnerInheritBehavior(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="LayerSpawner_InheritBehavior", args=["level"])
def LayerSpawner_InheritBehaviorFlag():
"""
Summary:
Verifies if Inherit Behavior Flag works as expected.
def run_test(self):
"""
Summary:
C4762381 Verifies if Inherit Behavior Flag works as expected.
Expected Result:
The spawner with Inherit Behavior toggled off no longer obeys
Vegetation Surface Mask Filter of the Vegetation Layer Blender entity and plants on the surface.
Expected Result:
The spawner with Inherit Behavior toggled off no longer obeys
Vegetation Surface Mask Filter of the Vegetation Layer Blender entity and plants on the surface.
:return: None
"""
import os
:return: None
"""
import azlmbr.math as math
import azlmbr.legacy.general as general
import azlmbr.surface_data as surface_data
import azlmbr.vegetation as vegetation
SURFACE_TAG = "test_tag"
import editor_python_test_tools.hydra_editor_utils as hydra
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
def set_dynamic_slice_asset(entity_obj, component_index, dynamic_slice_asset_path):
dynamic_slice_spawner = vegetation.DynamicSliceInstanceSpawner()
dynamic_slice_spawner.SetSliceAssetPath(dynamic_slice_asset_path)
descriptor = hydra.get_component_property_value(
entity_obj.components[component_index], "Configuration|Embedded Assets|[0]"
)
descriptor.spawner = dynamic_slice_spawner
entity_obj.get_set_test(2, "Configuration|Embedded Assets|[0]", descriptor)
SURFACE_TAG = "test_tag"
# Create empty level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=False,
def set_dynamic_slice_asset(entity_obj, component_index, dynamic_slice_asset_path):
dynamic_slice_spawner = vegetation.DynamicSliceInstanceSpawner()
dynamic_slice_spawner.SetSliceAssetPath(dynamic_slice_asset_path)
descriptor = hydra.get_component_property_value(
entity_obj.components[component_index], "Configuration|Embedded Assets|[0]"
)
descriptor.spawner = dynamic_slice_spawner
entity_obj.get_set_test(2, "Configuration|Embedded Assets|[0]", descriptor)
general.set_current_view_position(512.0, 480.0, 38.0)
# Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
# Create Emitter entity and add the required components
position = math.Vector3(512.0, 512.0, 32.0)
emitter_entity = dynveg.create_surface_entity("emitter_entity", position, 16.0, 16.0, 1.0)
general.set_current_view_position(512.0, 480.0, 38.0)
# Add surface tag to the Surface Tag Emitter
tag = surface_data.SurfaceTag()
tag.SetTag(SURFACE_TAG)
pte = hydra.get_property_tree(emitter_entity.components[1])
path = "Configuration|Generated Tags"
pte.add_container_item(path, 0, tag)
emitter_entity.get_set_test(1, "Configuration|Generated Tags|[0]", tag)
# Create Emitter entity and add the required components
position = math.Vector3(512.0, 512.0, 32.0)
emitter_entity = dynveg.create_surface_entity("emitter_entity", position, 16.0, 16.0, 1.0)
# Create Blender entity and add required components
components_to_add = ["Box Shape", "Vegetation Layer Blender"]
blender_entity = hydra.Entity("blender_entity")
blender_entity.create_entity(position, components_to_add)
blender_entity.get_set_test(0, "Box Shape|Box Configuration|Dimensions", math.Vector3(16.0, 16.0, 1.0))
# Add surface tag to the Surface Tag Emitter
tag = surface_data.SurfaceTag()
tag.SetTag(SURFACE_TAG)
pte = hydra.get_property_tree(emitter_entity.components[1])
path = "Configuration|Generated Tags"
pte.add_container_item(path, 0, tag)
emitter_entity.get_set_test(1, "Configuration|Generated Tags|[0]", tag)
# Create Vegetation area and assign a valid asset
veg_1 = hydra.Entity("veg_1")
veg_1.create_entity(
position, ["Vegetation Layer Spawner", "Vegetation Reference Shape", "Vegetation Asset List"]
)
set_dynamic_slice_asset(veg_1, 2, os.path.join("Slices", "PinkFlower.dynamicslice"))
veg_1.get_set_test(1, "Configuration|Shape Entity Id", blender_entity.id)
# Create Blender entity and add required components
components_to_add = ["Box Shape", "Vegetation Layer Blender"]
blender_entity = hydra.Entity("blender_entity")
blender_entity.create_entity(position, components_to_add)
blender_entity.get_set_test(0, "Box Shape|Box Configuration|Dimensions", math.Vector3(16.0, 16.0, 1.0))
# Create second vegetation area and assign a valid asset
veg_2 = hydra.Entity("veg_2")
veg_2.create_entity(
position, ["Vegetation Layer Spawner", "Vegetation Reference Shape", "Vegetation Asset List"]
)
set_dynamic_slice_asset(veg_2, 2, os.path.join("Slices", "PurpleFlower.dynamicslice"))
veg_2.get_set_test(1, "Configuration|Shape Entity Id", blender_entity.id)
# Create Vegetation area and assign a valid asset
veg_1 = hydra.Entity("veg_1")
veg_1.create_entity(
position, ["Vegetation Layer Spawner", "Vegetation Reference Shape", "Vegetation Asset List"]
)
set_dynamic_slice_asset(veg_1, 2, os.path.join("Slices", "PinkFlower.dynamicslice"))
veg_1.get_set_test(1, "Configuration|Shape Entity Id", blender_entity.id)
# Assign the vegetation areas to the Blender entity
pte = hydra.get_property_tree(blender_entity.components[1])
path = "Configuration|Vegetation Areas"
pte.update_container_item(path, 0, veg_1.id)
pte.add_container_item(path, 1, veg_2.id)
# Create second vegetation area and assign a valid asset
veg_2 = hydra.Entity("veg_2")
veg_2.create_entity(
position, ["Vegetation Layer Spawner", "Vegetation Reference Shape", "Vegetation Asset List"]
)
set_dynamic_slice_asset(veg_2, 2, os.path.join("Slices", "PurpleFlower.dynamicslice"))
veg_2.get_set_test(1, "Configuration|Shape Entity Id", blender_entity.id)
# Add Vegetation Surface Mask Filter to the blender entity and add a Exclusion tag
tag = surface_data.SurfaceTag()
tag.SetTag(SURFACE_TAG)
blender_entity.add_component("Vegetation Surface Mask Filter")
pte = hydra.get_property_tree(blender_entity.components[2])
path = "Configuration|Exclusion|Surface Tags"
pte.add_container_item(path, 0, tag)
blender_entity.get_set_test(2, "Configuration|Exclusion|Surface Tags|[0]", tag)
# Assign the vegetation areas to the Blender entity
pte = hydra.get_property_tree(blender_entity.components[1])
path = "Configuration|Vegetation Areas"
pte.update_container_item(path, 0, veg_1.id)
pte.add_container_item(path, 1, veg_2.id)
# Toggle Inherit Behavior flag and verify vegetation instances
self.log(
f"Vegetation is not planted when Inherit Behavior flag is checked: {dynveg.validate_instance_count(position, 16.0, 0)}"
)
veg_1.get_set_test(0, "Configuration|Inherit Behavior", False)
self.wait_for_condition(lambda: dynveg.validate_instance_count(position, 16.0, 400), 2.0)
self.log(
f"Vegetation plant when Inherit Behavior flag is unchecked: {dynveg.validate_instance_count(position, 16.0, 400)}"
)
# Add Vegetation Surface Mask Filter to the blender entity and add a Exclusion tag
tag = surface_data.SurfaceTag()
tag.SetTag(SURFACE_TAG)
blender_entity.add_component("Vegetation Surface Mask Filter")
pte = hydra.get_property_tree(blender_entity.components[2])
path = "Configuration|Exclusion|Surface Tags"
pte.add_container_item(path, 0, tag)
blender_entity.get_set_test(2, "Configuration|Exclusion|Surface Tags|[0]", tag)
# Toggle Inherit Behavior flag and verify vegetation instances
flag_checked_instance_count = helper.wait_for_condition(lambda: dynveg.validate_instance_count(position, 16.0, 0), 2.0)
Report.result(Tests.inherit_behavior_checked, flag_checked_instance_count)
veg_1.get_set_test(0, "Configuration|Inherit Behavior", False)
flag_unchecked_instance_count = helper.wait_for_condition(lambda: dynveg.validate_instance_count(position, 16.0, 400), 2.0)
Report.result(Tests.inherit_behavior_unchecked, flag_unchecked_instance_count)
test = TestLayerSpawnerInheritBehavior()
test.run()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(LayerSpawner_InheritBehaviorFlag)
@@ -5,134 +5,128 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr
import azlmbr.legacy.general as general
import azlmbr.entity as EntityId
import azlmbr.math as math
def LayerSpawner_InstancesPlantInAllSupportedShapes():
"""
Summary:
The level is loaded and vegetation area is created. Then the Vegetation Reference Shape
component of vegetation area is pinned with entities of different shape components to check
if the vegetation plants in different shaped areas.
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.editor_test_helper import EditorTestHelper
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
Expected Behavior:
Vegetation properly plants in areas of any shape.
Test Steps:
1) Open a level
2) Create basic vegetation area entity and set the properties
3) Box Shape Entity: create, set properties and pin to vegetation
4) Capsule Shape Entity: create, set properties and pin to vegetation
5) Tube Shape Entity: create, set properties and pin to vegetation
6) Sphere Shape Entity: create, set properties and pin to vegetation
7) Cylinder Shape Entity: create, set properties and pin to vegetation
8) Prism Shape Entity: create, set properties and pin to vegetation
9) Compound Shape Entity: create, set properties and pin to vegetation
class TestLayerSpawner_AllShapesPlant(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="TestLayerSpawner_AllShapesPlant", args=["level"])
Note:
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
def run_test(self):
"""
Summary:
The level is loaded and vegetation area is created. Then the Vegetation Reference Shape
component of vegetation area is pinned with entities of different shape components to check
if the vegetation plants in different shaped areas.
:return: None
"""
Expected Behavior:
Vegetation properly plants in areas of any shape.
import os
Test Steps:
1) Create level
2) Create basic vegetation area entity and set the properties
3) Box Shape Entity: create, set properties and pin to vegetation
4) Capsule Shape Entity: create, set properties and pin to vegetation
5) Tube Shape Entity: create, set properties and pin to vegetation
6) Sphere Shape Entity: create, set properties and pin to vegetation
7) Cylinder Shape Entity: create, set properties and pin to vegetation
8) Prism Shape Entity: create, set properties and pin to vegetation
9) Compound Shape Entity: create, set properties and pin to vegetation
import azlmbr.legacy.general as general
import azlmbr.entity as EntityId
import azlmbr.math as math
Note:
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
import editor_python_test_tools.hydra_editor_utils as hydra
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
:return: None
"""
def pin_shape_and_check_count(entity_id, count):
hydra.get_set_test(vegetation, 2, "Configuration|Shape Entity Id", entity_id)
result = self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(vegetation.id,
count), 2.0)
self.test_success = self.test_success and result
# 1) Create level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=False,
def pin_shape_and_check_count(entity, count):
hydra.get_set_test(vegetation, 2, "Configuration|Shape Entity Id", entity.id)
result = helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(vegetation.id,
count), 2.0)
success = (
f"Found the expected number of instances in {entity.name} shape",
f"Unexpected number of instances found in {entity.name} shape"
)
Report.result(success, result)
# 2) Create basic vegetation area entity and set the properties
entity_position = math.Vector3(125.0, 136.0, 32.0)
asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice")
vegetation = dynveg.create_vegetation_area("Instance Spawner",
entity_position,
10.0, 10.0, 10.0,
asset_path)
vegetation.remove_component("Box Shape")
vegetation.add_component("Vegetation Reference Shape")
# 1) Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
# Create surface for planting on
dynveg.create_surface_entity("Surface Entity", entity_position, 60.0, 60.0, 1.0)
# 2) Create basic vegetation area entity and set the properties
entity_position = math.Vector3(125.0, 136.0, 32.0)
asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice")
vegetation = dynveg.create_vegetation_area("Instance Spawner",
entity_position,
10.0, 10.0, 10.0,
asset_path)
vegetation.remove_component("Box Shape")
vegetation.add_component("Vegetation Reference Shape")
# Adjust camera to be close to the vegetation entity
general.set_current_view_position(135.0, 102.0, 39.0)
general.set_current_view_rotation(-15.0, 0, 0)
# Create surface for planting on
dynveg.create_surface_entity("Surface Entity", entity_position, 60.0, 60.0, 1.0)
# 3) Box Shape Entity: create, set properties and pin to vegetation
box = hydra.Entity("box")
box.create_entity(math.Vector3(124.0, 126.0, 32.0), ["Box Shape"])
new_box_dimension = math.Vector3(10.0, 10.0, 1.0)
hydra.get_set_test(box, 0, "Box Shape|Box Configuration|Dimensions", new_box_dimension)
# This and subsequent counts are the number of "PurpleFlower" that spawn in the shape with given dimensions
pin_shape_and_check_count(box.id, 156)
# Adjust camera to be close to the vegetation entity
general.set_current_view_position(135.0, 102.0, 39.0)
general.set_current_view_rotation(-15.0, 0, 0)
# 4) Capsule Shape Entity: create, set properties and pin to vegetation
capsule = hydra.Entity("capsule")
capsule.create_entity(math.Vector3(120.0, 142.0, 32.0), ["Capsule Shape"])
hydra.get_set_test(capsule, 0, "Capsule Shape|Capsule Configuration|Height", 10.0)
hydra.get_set_test(capsule, 0, "Capsule Shape|Capsule Configuration|Radius", 2.0)
pin_shape_and_check_count(capsule.id, 20)
# 3) Box Shape Entity: create, set properties and pin to vegetation
box = hydra.Entity("Box")
box.create_entity(math.Vector3(124.0, 126.0, 32.0), ["Box Shape"])
new_box_dimension = math.Vector3(10.0, 10.0, 1.0)
hydra.get_set_test(box, 0, "Box Shape|Box Configuration|Dimensions", new_box_dimension)
# This and subsequent counts are the number of "PurpleFlower" that spawn in the shape with given dimensions
pin_shape_and_check_count(box, 156)
# 5) Tube Shape Entity: create, set properties and pin to vegetation
tube = hydra.Entity("tube")
tube.create_entity(math.Vector3(124.0, 136.0, 32.0), ["Tube Shape", "Spline"])
pin_shape_and_check_count(tube.id, 27)
# 4) Capsule Shape Entity: create, set properties and pin to vegetation
capsule = hydra.Entity("Capsule")
capsule.create_entity(math.Vector3(120.0, 142.0, 32.0), ["Capsule Shape"])
hydra.get_set_test(capsule, 0, "Capsule Shape|Capsule Configuration|Height", 10.0)
hydra.get_set_test(capsule, 0, "Capsule Shape|Capsule Configuration|Radius", 2.0)
pin_shape_and_check_count(capsule, 20)
# 6) Sphere Shape Entity: create, set properties and pin to vegetation
sphere = hydra.Entity("sphere")
sphere.create_entity(math.Vector3(112.0, 143.0, 32.0), ["Sphere Shape"])
hydra.get_set_test(sphere, 0, "Sphere Shape|Sphere Configuration|Radius", 5.0)
pin_shape_and_check_count(sphere.id, 122)
# 5) Tube Shape Entity: create, set properties and pin to vegetation
tube = hydra.Entity("Tube")
tube.create_entity(math.Vector3(124.0, 136.0, 32.0), ["Tube Shape", "Spline"])
pin_shape_and_check_count(tube, 27)
# 7) Cylinder Shape Entity: create, set properties and pin to vegetation
cylinder = hydra.Entity("cylinder")
cylinder.create_entity(math.Vector3(136.0, 143.0, 32.0), ["Cylinder Shape"])
hydra.get_set_test(cylinder, 0, "Cylinder Shape|Cylinder Configuration|Radius", 5.0)
hydra.get_set_test(cylinder, 0, "Cylinder Shape|Cylinder Configuration|Height", 5.0)
pin_shape_and_check_count(cylinder.id, 124)
# 6) Sphere Shape Entity: create, set properties and pin to vegetation
sphere = hydra.Entity("Sphere")
sphere.create_entity(math.Vector3(112.0, 143.0, 32.0), ["Sphere Shape"])
hydra.get_set_test(sphere, 0, "Sphere Shape|Sphere Configuration|Radius", 5.0)
pin_shape_and_check_count(sphere, 122)
# 8) Prism Shape Entity: create, set properties and pin to vegetation
polygon_prism = hydra.Entity("polygonprism")
polygon_prism.create_entity(math.Vector3(127.0, 142.0, 32.0), ["Polygon Prism Shape"])
pin_shape_and_check_count(polygon_prism.id, 20)
# 7) Cylinder Shape Entity: create, set properties and pin to vegetation
cylinder = hydra.Entity("Cylinder")
cylinder.create_entity(math.Vector3(136.0, 143.0, 32.0), ["Cylinder Shape"])
hydra.get_set_test(cylinder, 0, "Cylinder Shape|Cylinder Configuration|Radius", 5.0)
hydra.get_set_test(cylinder, 0, "Cylinder Shape|Cylinder Configuration|Height", 5.0)
pin_shape_and_check_count(cylinder, 124)
# 9) Compound Shape Entity: create, set properties and pin to vegetation
compound = hydra.Entity("Compound")
compound.create_entity(math.Vector3(125.0, 136.0, 32.0), ["Compound Shape"])
pte = hydra.get_property_tree(compound.components[0])
shapes = [box.id, capsule.id, tube.id, sphere.id, cylinder.id, polygon_prism.id]
for index in range(6):
pte.add_container_item("Configuration|Child Shape Entities", index, EntityId.EntityId())
for index, element in enumerate(shapes):
hydra.get_set_test(compound, 0, f"Configuration|Child Shape Entities|[{index}]", element)
pin_shape_and_check_count(compound.id, 469)
# 8) Prism Shape Entity: create, set properties and pin to vegetation
polygon_prism = hydra.Entity("Polygon Prism")
polygon_prism.create_entity(math.Vector3(127.0, 142.0, 32.0), ["Polygon Prism Shape"])
pin_shape_and_check_count(polygon_prism, 20)
# 9) Compound Shape Entity: create, set properties and pin to vegetation
compound = hydra.Entity("Compound")
compound.create_entity(math.Vector3(125.0, 136.0, 32.0), ["Compound Shape"])
pte = hydra.get_property_tree(compound.components[0])
shapes = [box.id, capsule.id, tube.id, sphere.id, cylinder.id, polygon_prism.id]
for index in range(6):
pte.add_container_item("Configuration|Child Shape Entities", index, EntityId.EntityId())
for index, element in enumerate(shapes):
hydra.get_set_test(compound, 0, f"Configuration|Child Shape Entities|[{index}]", element)
pin_shape_and_check_count(compound, 469)
test = TestLayerSpawner_AllShapesPlant()
test.run()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(LayerSpawner_InstancesPlantInAllSupportedShapes)
@@ -5,115 +5,131 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr
import azlmbr.legacy.general as general
import azlmbr.math as math
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
from editor_python_test_tools.editor_test_helper import EditorTestHelper
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
class Tests:
viewport_config_updated = (
"Viewport is now configured for test",
"Failed to configure viewport for test"
)
first_viewport_active_instance_count = (
"Expected number of instances found in left viewport",
"Unexpected number of instances found in left viewport"
)
second_viewport_inactive_instance_count = (
"No instances found in right viewport",
"Unexpectedly found instances in right viewport while not active"
)
first_viewport_inactive_instance_count = (
"No instances found in left viewport",
"Unexpectedly found instances in left viewport while not active"
)
second_viewport_active_instance_count = (
"Expected number of instances found in right viewport",
"Unexpected number of instances found in right viewport"
)
class TestLayerSpawnerInstanceCameraRefresh(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="LayerSpawner_InstanceCameraRefresh", args=["level"])
def LayerSpawner_InstancesRefreshUsingCorrectViewportCamera():
"""
Summary:
Test that the Dynamic Vegetation System is using the current Editor viewport camera as the center
of the spawn area for vegetation. To verify this, we create two separate Editor viewports pointed
at two different vegetation areas, and verify that as we switch between active viewports, only the
area directly underneath that viewport's camera has vegetation.
"""
def run_test(self):
"""
Summary:
Test that the Dynamic Vegetation System is using the current Editor viewport camera as the center
of the spawn area for vegetation. To verify this, we create two separate Editor viewports pointed
at two different vegetation areas, and verify that as we switch between active viewports, only the
area directly underneath that viewport's camera has vegetation.
"""
# Create an empty level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=128,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=False,
)
# Set up a test environment to validate that switching viewports correctly changes which camera
# the vegetation system uses.
# The test environment consists of the following:
# - two 32 x 32 x 1 box shapes located far apart that emit a surface with no tags
# - two 32 x 32 x 32 vegetation areas that place vegetation on the boxes
import os
# Initialize some constants for our test.
# The boxes are intentionally shifted by 0.5 meters to ensure that we get a predictable number
# of vegetation points. By default, vegetation plants on grid corners, so if our boxes are aligned
# with grid corner points, the right/bottom edges will include more points than we might intuitively expect.
# By shifting by 0.5 meters, the vegetation grid points don't fall on the box edges, making the total count
# more predictable.
first_entity_center_point = math.Vector3(0.5, 0.5, 100.0)
# The second box needs to be far enough away from the first that the vegetation system will never spawn instances
# in both at the same time.
second_entity_center_point = math.Vector3(1024.5, 1024.5, 100.0)
box_size = 32.0
surface_height = 1.0
# By default, vegetation spawns 20 instances per 16 meters, so for our box of 32 meters, we should have
# ((20 instances / 16 m) * 32 m) ^ 2 instances.
filled_vegetation_area_instance_count = (20 * 2) * (20 * 2)
import azlmbr.legacy.general as general
import azlmbr.math as math
# Change the Editor view to contain two viewports
general.set_view_pane_layout(1)
get_view_pane_layout_success = self.wait_for_condition(lambda: (general.get_view_pane_layout() == 1), 2)
get_viewport_count_success = self.wait_for_condition(lambda: (general.get_viewport_count() == 2), 2)
self.test_success = get_view_pane_layout_success and self.test_success
self.test_success = get_viewport_count_success and self.test_success
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
# Set the view in the first viewport to point down at the first box
general.set_active_viewport(0)
self.wait_for_condition(lambda: general.get_active_viewport() == 0, 2)
general.set_current_view_position(first_entity_center_point.x, first_entity_center_point.y,
first_entity_center_point.z + 30.0)
general.set_current_view_rotation(-85.0, 0.0, 0.0)
# Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
# Set the view in the second viewport to point down at the second box
general.set_active_viewport(1)
self.wait_for_condition(lambda: general.get_active_viewport() == 1, 2)
general.set_current_view_position(second_entity_center_point.x, second_entity_center_point.y,
second_entity_center_point.z + 30.0)
general.set_current_view_rotation(-85.0, 0.0, 0.0)
# Set up a test environment to validate that switching viewports correctly changes which camera
# the vegetation system uses.
# The test environment consists of the following:
# - two 32 x 32 x 1 box shapes located far apart that emit a surface with no tags
# - two 32 x 32 x 32 vegetation areas that place vegetation on the boxes
# Create the "flat surface" entities to use as our vegetation surfaces
first_surface_entity = dynveg.create_surface_entity("Surface 1", first_entity_center_point, box_size, box_size,
surface_height)
second_surface_entity = dynveg.create_surface_entity("Surface 2", second_entity_center_point, box_size, box_size,
surface_height)
# Initialize some constants for our test.
# The boxes are intentionally shifted by 0.5 meters to ensure that we get a predictable number
# of vegetation points. By default, vegetation plants on grid corners, so if our boxes are aligned
# with grid corner points, the right/bottom edges will include more points than we might intuitively expect.
# By shifting by 0.5 meters, the vegetation grid points don't fall on the box edges, making the total count
# more predictable.
first_entity_center_point = math.Vector3(0.5, 0.5, 100.0)
# The second box needs to be far enough away from the first that the vegetation system will never spawn instances
# in both at the same time.
second_entity_center_point = math.Vector3(1024.5, 1024.5, 100.0)
box_size = 32.0
surface_height = 1.0
# By default, vegetation spawns 20 instances per 16 meters, so for our box of 32 meters, we should have
# ((20 instances / 16 m) * 32 m) ^ 2 instances.
filled_vegetation_area_instance_count = (20 * 2) * (20 * 2)
# Create the two vegetation areas
test_slice_asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice")
first_veg_entity = dynveg.create_vegetation_area("Veg Area 1", first_entity_center_point, box_size, box_size,
box_size, test_slice_asset_path)
second_veg_entity = dynveg.create_vegetation_area("Veg Area 2", second_entity_center_point, box_size, box_size,
box_size, test_slice_asset_path)
# Change the Editor view to contain two viewports
general.set_view_pane_layout(1)
get_view_pane_layout_success = helper.wait_for_condition(lambda: (general.get_view_pane_layout() == 1), 2)
get_viewport_count_success = helper.wait_for_condition(lambda: (general.get_viewport_count() == 2), 2)
Report.critical_result(Tests.viewport_config_updated, get_view_pane_layout_success and get_viewport_count_success)
# When the first viewport is active, the first area should be full of instances, and the second should be empty
general.set_active_viewport(0)
viewport_0_success = self.wait_for_condition(lambda: dynveg.validate_instance_count(first_entity_center_point,
box_size / 2.0,
filled_vegetation_area_instance_count), 5)
self.test_success = viewport_0_success and self.test_success
viewport_1_success = self.wait_for_condition(lambda: dynveg.validate_instance_count(second_entity_center_point,
box_size / 2.0, 0), 5)
self.test_success = viewport_1_success and self.test_success
# Set the view in the first viewport to point down at the first box
general.set_active_viewport(0)
helper.wait_for_condition(lambda: general.get_active_viewport() == 0, 2)
general.set_current_view_position(first_entity_center_point.x, first_entity_center_point.y,
first_entity_center_point.z + 30.0)
general.set_current_view_rotation(-85.0, 0.0, 0.0)
# When the second viewport is active, the second area should be full of instances, and the first should be empty
general.set_active_viewport(1)
viewport_0_success = self.wait_for_condition(lambda: dynveg.validate_instance_count(first_entity_center_point,
box_size / 2.0, 0), 5)
self.test_success = viewport_0_success and self.test_success
viewport_1_success = self.wait_for_condition(lambda: dynveg.validate_instance_count(second_entity_center_point,
box_size / 2.0,
filled_vegetation_area_instance_count), 5)
self.test_success = viewport_1_success and self.test_success
# Set the view in the second viewport to point down at the second box
general.set_active_viewport(1)
helper.wait_for_condition(lambda: general.get_active_viewport() == 1, 2)
general.set_current_view_position(second_entity_center_point.x, second_entity_center_point.y,
second_entity_center_point.z + 30.0)
general.set_current_view_rotation(-85.0, 0.0, 0.0)
# Create the "flat surface" entities to use as our vegetation surfaces
first_surface_entity = dynveg.create_surface_entity("Surface 1", first_entity_center_point, box_size, box_size,
surface_height)
second_surface_entity = dynveg.create_surface_entity("Surface 2", second_entity_center_point, box_size, box_size,
surface_height)
# Create the two vegetation areas
test_slice_asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice")
first_veg_entity = dynveg.create_vegetation_area("Veg Area 1", first_entity_center_point, box_size, box_size,
box_size, test_slice_asset_path)
second_veg_entity = dynveg.create_vegetation_area("Veg Area 2", second_entity_center_point, box_size, box_size,
box_size, test_slice_asset_path)
# When the first viewport is active, the first area should be full of instances, and the second should be empty
general.set_active_viewport(0)
helper.wait_for_condition(lambda: general.get_active_viewport() == 0, 2)
viewport_0_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count(first_entity_center_point,
box_size / 2.0,
filled_vegetation_area_instance_count), 5)
viewport_1_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count(second_entity_center_point,
box_size / 2.0, 0), 5)
Report.result(Tests.first_viewport_active_instance_count, viewport_0_success)
Report.result(Tests.second_viewport_inactive_instance_count, viewport_1_success)
# When the second viewport is active, the second area should be full of instances, and the first should be empty
general.set_active_viewport(1)
helper.wait_for_condition(lambda: general.get_active_viewport() == 1, 2)
viewport_0_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count(first_entity_center_point,
box_size / 2.0, 0), 5)
Report.result(Tests.first_viewport_inactive_instance_count, viewport_0_success)
viewport_1_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count(second_entity_center_point,
box_size / 2.0,
filled_vegetation_area_instance_count), 5)
Report.result(Tests.second_viewport_active_instance_count, viewport_1_success)
test = TestLayerSpawnerInstanceCameraRefresh()
test.run()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(LayerSpawner_InstancesRefreshUsingCorrectViewportCamera)
@@ -5,92 +5,90 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os, sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.asset as asset
import azlmbr.bus as bus
import azlmbr.components as components
import azlmbr.editor as editor
import azlmbr.legacy.general as general
import azlmbr.math as math
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.editor_test_helper import EditorTestHelper
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
class Tests:
blocked_instance_count = (
"Instance count is as expected wih a Blocker setup",
"Found unexpected instances with a Blocker setup"
)
class test_MeshBlocker_InstancesBlockedByMesh(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="MeshBlocker_InstancesBlockedByMesh", args=["level"])
def MeshBlocker_InstancesBlockedByMesh():
"""
Summary:
Level is created. An entity with a vegetation spawner and entity with vegetation blocker (Mesh) component are
added. Finally, the instance counts are checked to verify expected numbers after blocker is applied.
def run_test(self):
"""
Summary:
Level is created. An entity with a vegetation spawner and entity with vegetation blocker (Mesh) component are
added. Finally, the instance counts are checked to verify expected numbers after blocker is applied.
Expected Behavior:
The vegetation planted in the Spawner area is blocked by the Mesh of the Vegetation Blocker Mesh component.
Expected Behavior:
The vegetation planted in the Spawner area is blocked by the Mesh of the Vegetation Blocker Mesh component.
Test Steps:
--> Open a level
--> Create Spawner Entity
--> Create Surface Entity to spawn vegetation instances on
--> Create Blocker Entity with cube mesh
--> Verify spawned vegetation instance counts
Test Steps:
--> Create level
--> Create Spawner Entity
--> Create Surface Entity to spawn vegetation instances on
--> Create Blocker Entity with cube mesh
--> Verify spawned vegetation instance counts
Note:
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
Note:
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
:return: None
"""
:return: None
"""
import os
# Create a new level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=False,
)
import azlmbr.asset as asset
import azlmbr.bus as bus
import azlmbr.components as components
import azlmbr.legacy.general as general
import azlmbr.math as math
general.set_current_view_position(500.49, 498.69, 46.66)
general.set_current_view_rotation(-42.05, 0.00, -36.33)
import editor_python_test_tools.hydra_editor_utils as hydra
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
# Create entity with components "Vegetation Layer Spawner", "Vegetation Asset List", "Box Shape"
entity_position = math.Vector3(512.0, 512.0, 32.0)
asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice")
spawner_entity = dynveg.create_vegetation_area("Instance Spawner",
entity_position,
10.0, 10.0, 10.0,
asset_path)
# Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
# Create surface entity to plant on
dynveg.create_surface_entity("Surface Entity", entity_position, 10.0, 10.0, 1.0)
general.set_current_view_position(500.49, 498.69, 46.66)
general.set_current_view_rotation(-42.05, 0.00, -36.33)
# Create blocker entity with cube mesh
mesh_type_id = azlmbr.globals.property.EditorMeshComponentTypeId
blocker_entity = hydra.Entity("Blocker Entity")
blocker_entity.create_entity(entity_position,
["Vegetation Layer Blocker (Mesh)"])
blocker_entity.add_component_of_type(mesh_type_id)
if blocker_entity.id.IsValid():
print(f"'{blocker_entity.name}' created")
cubeId = asset.AssetCatalogRequestBus(
bus.Broadcast, "GetAssetIdByPath", os.path.join("objects", "_primitives", "_box_1x1.azmodel"), math.Uuid(),
False)
blocker_entity.get_set_test(1, "Controller|Configuration|Mesh Asset", cubeId)
components.TransformBus(bus.Event, "SetLocalUniformScale", blocker_entity.id, 2.0)
# Create entity with components "Vegetation Layer Spawner", "Vegetation Asset List", "Box Shape"
entity_position = math.Vector3(512.0, 512.0, 32.0)
asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice")
spawner_entity = dynveg.create_vegetation_area("Instance Spawner",
entity_position,
10.0, 10.0, 10.0,
asset_path)
# Verify spawned instance counts are accurate after addition of Blocker Entity
num_expected = 160 # Number of "PurpleFlower"s that plant on a 10 x 10 surface minus 2m blocker cube
result = self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id,
num_expected), 2.0)
self.test_success = self.test_success and result
# Create surface entity to plant on
dynveg.create_surface_entity("Surface Entity", entity_position, 10.0, 10.0, 1.0)
# Create blocker entity with cube mesh
mesh_type_id = azlmbr.globals.property.EditorMeshComponentTypeId
blocker_entity = hydra.Entity("Blocker Entity")
blocker_entity.create_entity(entity_position,
["Vegetation Layer Blocker (Mesh)"])
blocker_entity.add_component_of_type(mesh_type_id)
if blocker_entity.id.IsValid():
print(f"'{blocker_entity.name}' created")
cubeId = asset.AssetCatalogRequestBus(
bus.Broadcast, "GetAssetIdByPath", os.path.join("objects", "_primitives", "_box_1x1.azmodel"), math.Uuid(),
False)
blocker_entity.get_set_test(1, "Controller|Configuration|Mesh Asset", cubeId)
components.TransformBus(bus.Event, "SetLocalUniformScale", blocker_entity.id, 2.0)
# Verify spawned instance counts are accurate after addition of Blocker Entity
num_expected = 160 # Number of "PurpleFlower"s that plant on a 10 x 10 surface minus 2m blocker cube
result = helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id,
num_expected), 2.0)
Report.result(Tests.blocked_instance_count, result)
test = test_MeshBlocker_InstancesBlockedByMesh()
test.run()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(MeshBlocker_InstancesBlockedByMesh)
@@ -5,104 +5,100 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import math as pymath
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr
import azlmbr.asset as asset
import azlmbr.bus as bus
import azlmbr.components as components
import azlmbr.editor as editor
import azlmbr.legacy.general as general
import azlmbr.math as math
class Tests:
blocked_instance_count = (
"Instance count is as expected wih a Blocker setup",
"Found unexpected instances with a Blocker setup"
)
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.editor_test_helper import EditorTestHelper
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
def MeshBlocker_InstancesBlockedByMeshHeightTuning():
"""
Summary:
A temporary level is created, then a simple vegetation area is created. A blocker area is created and it is
verified that the tuning of the height percent blocker setting works as expected.
Expected Behavior:
Vegetation is blocked only around the trunk of the tree, while it still plants under the areas covered by branches.
Test Steps:
1) Open a level
2) Create entity with components "Vegetation Layer Spawner", "Vegetation Asset List", "Box Shape"
3) Create surface entity
4) Create blocker entity with sphere mesh
5) Adjust the height Min/Max percentage values of blocker
6) Verify spawned instance counts are accurate after adjusting height Max percentage of Blocker Entity
Note:
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
:return: None
"""
import os
import math as pymath
import azlmbr
import azlmbr.asset as asset
import azlmbr.bus as bus
import azlmbr.components as components
import azlmbr.legacy.general as general
import azlmbr.math as math
import editor_python_test_tools.hydra_editor_utils as hydra
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
# 1) Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
general.set_current_view_position(500.49, 498.69, 46.66)
general.set_current_view_rotation(-42.05, 0.00, -36.33)
# 2) Create entity with components "Vegetation Layer Spawner", "Vegetation Asset List", "Box Shape"
entity_position = math.Vector3(512.0, 512.0, 32.0)
asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice")
spawner_entity = dynveg.create_vegetation_area("Instance Spawner",
entity_position,
10.0, 10.0, 10.0,
asset_path)
# 3) Create surface entity to plant on
dynveg.create_surface_entity("Surface Entity", entity_position, 10.0, 10.0, 1.0)
# 4) Create blocker entity with rotated cube mesh
y_rotation = pymath.radians(45.0)
mesh_type_id = azlmbr.globals.property.EditorMeshComponentTypeId
blocker_entity = hydra.Entity("Blocker Entity")
blocker_entity.create_entity(entity_position,
["Vegetation Layer Blocker (Mesh)"])
blocker_entity.add_component_of_type(mesh_type_id)
if blocker_entity.id.IsValid():
Report.info(f"'{blocker_entity.name}' created")
sphere_id = asset.AssetCatalogRequestBus(
bus.Broadcast, "GetAssetIdByPath", os.path.join("objects", "_primitives", "_box_1x1.azmodel"), math.Uuid(),
False)
blocker_entity.get_set_test(1, "Controller|Configuration|Mesh Asset", sphere_id)
components.TransformBus(bus.Event, "SetLocalUniformScale", blocker_entity.id, 5.0)
components.TransformBus(bus.Event, "SetLocalRotation", blocker_entity.id, math.Vector3(0.0, y_rotation, 0.0))
# 5) Adjust the height Max percentage values of blocker
blocker_entity.get_set_test(0, "Configuration|Mesh Height Percent Max", 0.8)
# 6) Verify spawned instance counts are accurate after adjusting height Max percentage of Blocker Entity
# The number of "PurpleFlower" instances that plant on a 10 x 10 surface minus those blocked by the rotated at
# 80% max height factored in.
num_expected = 127
result = helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id,
num_expected), 5.0)
Report.result(Tests.blocked_instance_count, result)
class test_MeshBlocker_InstancesBlockedByMeshHeightTuning(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="MeshBlocker_InstancesBlockedByMeshHeightTuning", args=["level"])
if __name__ == "__main__":
def run_test(self):
"""
Summary:
A temporary level is created, then a simple vegetation area is created. A blocker area is created and it is
verified that the tuning of the height percent blocker setting works as expected.
Expected Behavior:
Vegetation is blocked only around the trunk of the tree, while it still plants under the areas covered by branches.
Test Steps:
1) Create level
2) Create entity with components "Vegetation Layer Spawner", "Vegetation Asset List", "Box Shape"
3) Create surface entity
4) Create blocker entity with sphere mesh
5) Adjust the height Min/Max percentage values of blocker
6) Verify spawned instance counts are accurate after adjusting height Max percentage of Blocker Entity
Note:
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
:return: None
"""
# 1) Create level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=False,
)
general.set_current_view_position(500.49, 498.69, 46.66)
general.set_current_view_rotation(-42.05, 0.00, -36.33)
# 2) Create entity with components "Vegetation Layer Spawner", "Vegetation Asset List", "Box Shape"
entity_position = math.Vector3(512.0, 512.0, 32.0)
asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice")
spawner_entity = dynveg.create_vegetation_area("Instance Spawner",
entity_position,
10.0, 10.0, 10.0,
asset_path)
# 3) Create surface entity to plant on
dynveg.create_surface_entity("Surface Entity", entity_position, 10.0, 10.0, 1.0)
# 4) Create blocker entity with rotated cube mesh
y_rotation = pymath.radians(45.0)
mesh_type_id = azlmbr.globals.property.EditorMeshComponentTypeId
blocker_entity = hydra.Entity("Blocker Entity")
blocker_entity.create_entity(entity_position,
["Vegetation Layer Blocker (Mesh)"])
blocker_entity.add_component_of_type(mesh_type_id)
if blocker_entity.id.IsValid():
print(f"'{blocker_entity.name}' created")
sphere_id = asset.AssetCatalogRequestBus(
bus.Broadcast, "GetAssetIdByPath", os.path.join("objects", "_primitives", "_box_1x1.azmodel"), math.Uuid(),
False)
blocker_entity.get_set_test(1, "Controller|Configuration|Mesh Asset", sphere_id)
components.TransformBus(bus.Event, "SetLocalUniformScale", blocker_entity.id, 5.0)
components.TransformBus(bus.Event, "SetLocalRotation", blocker_entity.id, math.Vector3(0.0, y_rotation, 0.0))
# 5) Adjust the height Max percentage values of blocker
blocker_entity.get_set_test(0, "Configuration|Mesh Height Percent Max", 0.8)
# 6) Verify spawned instance counts are accurate after adjusting height Max percentage of Blocker Entity
# The number of "PurpleFlower" instances that plant on a 10 x 10 surface minus those blocked by the rotated at
# 80% max height factored in.
num_expected = 127
result = self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id,
num_expected), 5.0)
self.test_success = self.test_success and result
test = test_MeshBlocker_InstancesBlockedByMeshHeightTuning()
test.run()
from editor_python_test_tools.utils import Report
Report.start_test(MeshBlocker_InstancesBlockedByMeshHeightTuning)
@@ -5,93 +5,87 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.entity as EntityId
import azlmbr.math as math
import azlmbr.paths
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.editor_test_helper import EditorTestHelper
class Tests:
new_entity_created = (
"Successfully created new entity",
"Failed to create new entity"
)
emitter_disabled_before_mesh = (
"Mesh Surface Tag Emitter is disabled without a Mesh component",
"Mesh Surface Tag Emitter is unexpectedly enabled without a Mesh component"
)
emitter_enabled_after_mesh = (
"Mesh Surface Tag Emitter is enabled after adding a Mesh component",
"Mesh Surface Tag Emitter is unexpectedly disabled after adding a Mesh component"
)
class TestMeshSurfaceTagEmitter(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="MeshSurfaceTagEmitter_DependentOnMeshComponent", args=["level"])
def MeshSurfaceTagEmitter_DependentOnMeshComponent():
"""
Summary:
A New level is loaded. A New entity is created with component "Mesh Surface Tag Emitter". Adding a component
"Mesh" to the same entity.
def run_test(self):
"""
Summary:
A New level is loaded. A New entity is created with component "Mesh Surface Tag Emitter". Adding a component
"Mesh" to the same entity.
Expected Behavior:
Mesh Surface Tag Emitter is disabled until the required Mesh component is added to the entity.
Expected Behavior:
Mesh Surface Tag Emitter is disabled until the required Mesh component is added to the entity.
Test Steps:
1) Open level
2) Create a new entity with component "Mesh Surface Tag Emitter"
3) Make sure Mesh Surface Tag Emitter is disabled
4) Add Mesh to the same entity
5) Make sure Mesh Surface Tag Emitter is enabled after adding Mesh
Test Steps:
1) Open level
2) Create a new entity with component "Mesh Surface Tag Emitter"
3) Make sure Mesh Surface Tag Emitter is disabled
4) Add Mesh to the same entity
5) Make sure Mesh Surface Tag Emitter is enabled after adding Mesh
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
:return: None
"""
:return: None
"""
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.entity as EntityId
import azlmbr.math as math
def is_component_enabled(EntityComponentIdPair):
return editor.EditorComponentAPIBus(bus.Broadcast, "IsComponentEnabled", EntityComponentIdPair)
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
# 1) Open level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=False,
)
def is_component_enabled(EntityComponentIdPair):
return editor.EditorComponentAPIBus(bus.Broadcast, "IsComponentEnabled", EntityComponentIdPair)
# 2) Create a new entity with component "Mesh Surface Tag Emitter"
entity_position = math.Vector3(125.0, 136.0, 32.0)
component_to_add = "Mesh Surface Tag Emitter"
entity_id = editor.ToolsApplicationRequestBus(
bus.Broadcast, "CreateNewEntityAtPosition", entity_position, EntityId.EntityId()
)
meshentity = hydra.Entity("meshentity", entity_id)
meshentity.components = []
meshentity.components.append(hydra.add_component(component_to_add, entity_id))
if entity_id.IsValid():
print("New Entity Created")
# 1) Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
# 3) Make sure Mesh Surface Tag Emitter is disabled
is_enabled = is_component_enabled(meshentity.components[0])
self.test_success = self.test_success and not is_enabled
if not is_enabled:
print(f"{component_to_add} is Disabled")
elif is_enabled:
print(f"{component_to_add} is Enabled. But It should be disabled before adding Mesh")
# 2) Create a new entity with component "Mesh Surface Tag Emitter"
entity_position = math.Vector3(125.0, 136.0, 32.0)
component_to_add = "Mesh Surface Tag Emitter"
entity_id = editor.ToolsApplicationRequestBus(
bus.Broadcast, "CreateNewEntityAtPosition", entity_position, EntityId.EntityId()
)
meshentity = hydra.Entity("meshentity", entity_id)
meshentity.components = []
meshentity.components.append(hydra.add_component(component_to_add, entity_id))
Report.critical_result(Tests.new_entity_created, entity_id.IsValid())
# 4) Add Mesh to the same entity
component = "Mesh"
meshentity.components.append(hydra.add_component(component, entity_id))
# 3) Make sure Mesh Surface Tag Emitter is disabled
is_enabled = is_component_enabled(meshentity.components[0])
Report.result(Tests.emitter_disabled_before_mesh, not is_enabled)
# 5) Make sure Mesh Surface Tag Emitter is enabled after adding Mesh
is_enabled = is_component_enabled(meshentity.components[0])
self.test_success = self.test_success and is_enabled
if is_enabled:
print(f"{component_to_add} is Enabled")
elif not is_enabled:
print(f"{component_to_add} is Disabled. But It should be enabled after adding Mesh")
# 4) Add Mesh to the same entity
component = "Mesh"
meshentity.components.append(hydra.add_component(component, entity_id))
# 5) Make sure Mesh Surface Tag Emitter is enabled after adding Mesh
is_enabled = is_component_enabled(meshentity.components[0])
Report.result(Tests.emitter_enabled_after_mesh, is_enabled)
test = TestMeshSurfaceTagEmitter()
test.run()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(MeshSurfaceTagEmitter_DependentOnMeshComponent)
@@ -5,75 +5,71 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.math as math
import azlmbr.paths
import azlmbr.surface_data as surface_data
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.editor_test_helper import EditorTestHelper
class Tests:
add_surface_tag = (
"Surface Tag added successfully",
"Failed to add Surface Tag"
)
remove_surface_tag = (
"Successfully removed Surface Tag",
"Failed to remove Surface Tag"
)
class TestMeshSurfaceTagEmitter(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="MeshSurfaceTagEmitter_SurfaceTagsAddRemoveSucessfully",
args=["level"])
def MeshSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully():
"""
Summary:
An enity with Mesh Tag Emitter and a Mesh is added to the viewport to verify if we are able to
add/remove surface tags.
def run_test(self):
"""
Summary:
An enity with Mesh Tag Emitter and a Mesh is added to the viewport to verify if we are able to
add/remove surface tags.
Expected Behavior:
A new Surface Tag can be added and removed from the component.
Expected Behavior:
A new Surface Tag can be added and removed from the component.
Test Steps:
1) Open level
2) Create a new entity with components "Mesh Surface Tag Emitter", "Mesh"
3) Add/ remove Surface Tags
Test Steps:
1) Open level
2) Create a new entity with components "Mesh Surface Tag Emitter", "Mesh"
3) Add/ remove Surface Tags
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
:return: None
"""
:return: None
"""
import azlmbr.math as math
import azlmbr.surface_data as surface_data
# 1) Open level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=False,
)
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
# 2) Create a new entity with components "Mesh Surface Tag Emitter", "Mesh"
entity_position = math.Vector3(125.0, 136.0, 32.0)
components_to_add = ["Mesh Surface Tag Emitter", "Mesh"]
entity = hydra.Entity("entity")
entity.create_entity(entity_position, components_to_add)
# 1) Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
# 3) Add/ remove Surface Tags
tag = surface_data.SurfaceTag()
tag.SetTag("water")
pte = hydra.get_property_tree(entity.components[0])
path = "Configuration|Generated Tags"
pte.add_container_item(path, 0, tag)
success = self.wait_for_condition(lambda: pte.get_container_count(path).GetValue() == 1, 5.0)
self.test_success = self.test_success and success
print(f"Added SurfaceTag: container count is {pte.get_container_count(path).GetValue()}")
pte.remove_container_item(path, 0)
success = self.wait_for_condition(lambda: pte.get_container_count(path).GetValue() == 0, 5.0)
self.test_success = self.test_success and success
print(f"Removed SurfaceTag: container count is {pte.get_container_count(path).GetValue()}")
# 2) Create a new entity with components "Mesh Surface Tag Emitter", "Mesh"
entity_position = math.Vector3(125.0, 136.0, 32.0)
components_to_add = ["Mesh Surface Tag Emitter", "Mesh"]
entity = hydra.Entity("entity")
entity.create_entity(entity_position, components_to_add)
# 3) Add/ remove Surface Tags
tag = surface_data.SurfaceTag()
tag.SetTag("water")
pte = hydra.get_property_tree(entity.components[0])
path = "Configuration|Generated Tags"
pte.add_container_item(path, 0, tag)
success = helper.wait_for_condition(lambda: pte.get_container_count(path).GetValue() == 1, 5.0)
Report.result(Tests.add_surface_tag, success)
pte.remove_container_item(path, 0)
success = helper.wait_for_condition(lambda: pte.get_container_count(path).GetValue() == 0, 5.0)
Report.result(Tests.remove_surface_tag, success)
test = TestMeshSurfaceTagEmitter()
test.run()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(MeshSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully)
@@ -5,27 +5,30 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.asset as asset
import azlmbr.editor as editor
import azlmbr.legacy.general as general
import azlmbr.bus as bus
import azlmbr.math as math
def PhysXColliderSurfaceTagEmitter_E2E_Editor():
"""
Summary:
Test aspects of the PhysX Collider Surface Tag Emitter Component through the BehaviorContext and the Property
Tree.
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.editor_test_helper import EditorTestHelper
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
:return: None
"""
import os
class TestPhysXColliderSurfaceTagEmitter(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="PhysXColliderSurfaceTagEmitter_E2E_Editor", args=["level"])
import azlmbr.asset as asset
import azlmbr.editor as editor
import azlmbr.legacy.general as general
import azlmbr.bus as bus
import azlmbr.math as math
def validate_behavior_context(self):
import editor_python_test_tools.hydra_editor_utils as hydra
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
def validate_behavior_context():
# Verify that we can create the component through the BehaviorContext
behavior_context_test_success = True
test_component = azlmbr.surface_data.SurfaceDataColliderComponent()
@@ -38,163 +41,181 @@ class TestPhysXColliderSurfaceTagEmitter(EditorTestHelper):
provider_tag2 = azlmbr.surface_data.SurfaceTag('provider_tag2')
modifier_tag1 = azlmbr.surface_data.SurfaceTag('modifier_tag1')
modifier_tag2 = azlmbr.surface_data.SurfaceTag('modifier_tag2')
behavior_context_test_success = behavior_context_test_success and hydra.get_set_property_test(test_component,
'providerTags',
[provider_tag1,
provider_tag2])
behavior_context_test_success = behavior_context_test_success and hydra.get_set_property_test(test_component,
'modifierTags',
[modifier_tag1,
modifier_tag2])
self.log(f'SurfaceDataColliderComponent() BehaviorContext test: {behavior_context_test_success}')
behavior_context_test_success = behavior_context_test_success and hydra.get_set_property_test(
test_component,
'providerTags',
[provider_tag1,
provider_tag2])
behavior_context_test_success = behavior_context_test_success and hydra.get_set_property_test(
test_component,
'modifierTags',
[modifier_tag1,
modifier_tag2])
Report.info(f'SurfaceDataColliderComponent() BehaviorContext test: {behavior_context_test_success}')
return behavior_context_test_success
def run_test(self):
"""
Summary:
Test aspects of the PhysX Collider Surface Tag Emitter Component through the BehaviorContext and the Property Tree.
# Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
:return: None
"""
# Create an empty level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=False,
# Verify all of the BehaviorContext API:
behavior_context = (
"SurfaceDataColliderComponent() Behavior Context tests were successful",
"SurfaceDataColliderComponent() Behavior Context tests failed"
)
Report.result(behavior_context, validate_behavior_context())
# Set up a test environment to validate the PhysX Collider Surface Tag Emitter Component.
# The test environment will consist of the following:
# - a 32 x 32 x 1 box shape that emits a surface with no tags
# - a 32 x 32 x 32 vegetation area that will only place vegetation on surfaces with the 'test' tag
# With this setup, no vegetation will appear until a Surface Tag Emitter either emits new points with
# the correct tag, or modifies points on our box shape to emit the correct tag.
# Initialize some arbitrary constants for our test
entity_center_point = math.Vector3(512.0, 512.0, 100.0)
invalid_tag = azlmbr.surface_data.SurfaceTag('invalid')
surface_tag = azlmbr.surface_data.SurfaceTag('test')
test_box_size = 32.0
baseline_surface_height = 1.0
collider_radius = 4.0
collider_diameter = collider_radius * 2.0
# Set viewport view of area under test, and toggle helpers back on
general.set_current_view_position(512.0, 485.0, 110.0)
general.set_current_view_rotation(-35.0, 0.0, 0.0)
general.toggle_helpers()
# Create the "flat surface" entity to use as our baseline surface
dynveg.create_surface_entity("Baseline Surface", entity_center_point, 32.0, 32.0, 1.0)
# Create a new entity with required vegetation area components
asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
spawner_entity = dynveg.create_vegetation_area("Veg Area", entity_center_point, 32.0, 32.0, 32.0, asset_path)
# Add a Vegetation Surface Mask Filter component to the spawner entity and set it to include the "test" tag
spawner_entity.add_component("Vegetation Surface Mask Filter")
spawner_entity.get_set_test(3, "Configuration|Inclusion|Surface Tags", [surface_tag])
# At this point, there should be 0 instances within our entire veg area
initial_instance_count = (
"Found no instances as expected with initial setup",
"Unexpected found instances with initial setup"
)
initial_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count(entity_center_point, 16.0, 0),
5.0)
Report.result(initial_instance_count, initial_success)
# Create an entity with a PhysX Collider and our PhysX Collider Surface Tag Emitter
collider_entity_created = (
"Successfully created a Collider entity",
"Failed to create Collider entity"
)
collider_entity = hydra.Entity("Collider Surface")
collider_entity.create_entity(
entity_center_point,
["PhysX Collider", "PhysX Collider Surface Tag Emitter"]
)
Report.result(collider_entity_created, collider_entity.id.IsValid())
# Verify all of the BehaviorContext API:
self.test_success = self.test_success and self.validate_behavior_context()
# Set up the PhysX Collider so that each shape type (sphere, box, capsule) has the same test height.
hydra.get_set_test(collider_entity, 0, "Shape Configuration|Sphere|Radius", collider_radius)
hydra.get_set_test(collider_entity, 0, "Shape Configuration|Box|Dimensions", math.Vector3(collider_diameter,
collider_diameter,
collider_diameter))
hydra.get_set_test(collider_entity, 0, "Shape Configuration|Capsule|Height", collider_diameter)
# Set up a test environment to validate the PhysX Collider Surface Tag Emitter Component.
# The test environment will consist of the following:
# - a 32 x 32 x 1 box shape that emits a surface with no tags
# - a 32 x 32 x 32 vegetation area that will only place vegetation on surfaces with the 'test' tag
# With this setup, no vegetation will appear until a Surface Tag Emitter either emits new points with
# the correct tag, or modifies points on our box shape to emit the correct tag.
# Initialize some arbitrary constants for our test
entity_center_point = math.Vector3(512.0, 512.0, 100.0)
invalid_tag = azlmbr.surface_data.SurfaceTag('invalid')
surface_tag = azlmbr.surface_data.SurfaceTag('test')
test_box_size = 32.0
baseline_surface_height = 1.0
collider_radius = 4.0
collider_diameter = collider_radius * 2.0
# Set viewport view of area under test, and toggle helpers back on
general.set_current_view_position(512.0, 485.0, 110.0)
general.set_current_view_rotation(-35.0, 0.0, 0.0)
general.toggle_helpers()
# Create the "flat surface" entity to use as our baseline surface
dynveg.create_surface_entity("Baseline Surface", entity_center_point, 32.0, 32.0, 1.0)
# Create a new entity with required vegetation area components
asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
spawner_entity = dynveg.create_vegetation_area("Veg Area", entity_center_point, 32.0, 32.0, 32.0, asset_path)
# Add a Vegetation Surface Mask Filter component to the spawner entity and set it to include the "test" tag
spawner_entity.add_component("Vegetation Surface Mask Filter")
spawner_entity.get_set_test(3, "Configuration|Inclusion|Surface Tags", [surface_tag])
# At this point, there should be 0 instances within our entire veg area
initial_success = self.wait_for_condition(lambda: dynveg.validate_instance_count(entity_center_point, 16.0, 0),
5.0)
self.test_success = self.test_success and initial_success
# Create an entity with a PhysX Collider and our PhysX Collider Surface Tag Emitter
collider_entity = hydra.Entity("Collider Surface")
collider_entity.create_entity(
entity_center_point,
["PhysX Collider", "PhysX Collider Surface Tag Emitter"]
)
if collider_entity.id.IsValid():
self.log(f"'{collider_entity.name}' created")
# Set up the PhysX Collider so that each shape type (sphere, box, capsule) has the same test height.
hydra.get_set_test(collider_entity, 0, "Shape Configuration|Sphere|Radius", collider_radius)
hydra.get_set_test(collider_entity, 0, "Shape Configuration|Box|Dimensions", math.Vector3(collider_diameter,
collider_diameter,
collider_diameter))
hydra.get_set_test(collider_entity, 0, "Shape Configuration|Capsule|Height", collider_diameter)
# Run through each collider shape type (sphere, box, capsule) and verify the surface generation
# and surface modification of the PhysX Collision Surface Tag Emitter Component.
for collider_shape in range(0, 3):
hydra.get_set_test(collider_entity, 0, "Shape Configuration|Shape", collider_shape)
# Test: Generate a new surface on the collider.
# There should be one instance at the very top of the collider sphere, and none on the baseline surface
# (We use a small query box to only check for one placed instance point)
hydra.get_set_test(collider_entity, 1, "Configuration|Generated Tags", [surface_tag])
hydra.get_set_test(collider_entity, 1, "Configuration|Extended Tags", [invalid_tag])
top_point = math.Vector3(entity_center_point.x, entity_center_point.y, entity_center_point.z +
collider_radius)
baseline_surface_point = math.Vector3(entity_center_point.x, entity_center_point.y, entity_center_point.z +
(baseline_surface_height / 2.0))
top_point_success = self.wait_for_condition(lambda: dynveg.validate_instance_count(top_point, 0.25, 1), 5.0)
self.test_success = self.test_success and top_point_success
baseline_success = self.wait_for_condition(lambda: dynveg.validate_instance_count(baseline_surface_point,
0.25, 0), 5.0)
self.test_success = self.test_success and baseline_success
# Test: Modify an existing surface inside the collider.
# There should be no instances at the very top of the collider sphere, and one on the baseline surface
# within our query box.
# (We use a small query box to only check for one placed instance point)
hydra.get_set_test(collider_entity, 1, "Configuration|Generated Tags", [invalid_tag])
hydra.get_set_test(collider_entity, 1, "Configuration|Extended Tags", [surface_tag])
top_point_success = self.wait_for_condition(lambda: dynveg.validate_instance_count(top_point, 0.25, 0), 5.0)
self.test_success = self.test_success and top_point_success
baseline_success = self.wait_for_condition(lambda: dynveg.validate_instance_count(baseline_surface_point,
0.25, 1), 5.0)
self.test_success = self.test_success and baseline_success
# Setup collider entity with a PhysX Mesh
test_physx_mesh_asset_id = asset.AssetCatalogRequestBus(
bus.Broadcast, "GetAssetIdByPath", os.path.join("levels", "physics",
"Material_PerFaceMaterialGetsCorrectMaterial",
"test.pxmesh"), math.Uuid(), False)
# Remove/re-add component due to LYN-5496
collider_entity.remove_component("PhysX Collider")
collider_entity.add_component("PhysX Collider")
self.wait_for_condition(lambda: editor.EditorComponentAPIBus(bus.Broadcast, 'IsComponentEnabled',
collider_entity.components[1]), 5.0)
hydra.get_set_test(collider_entity, 1, "Shape Configuration|Shape", 7)
hydra.get_set_test(collider_entity, 1, "Shape Configuration|Asset|PhysX Mesh", test_physx_mesh_asset_id)
# Set the asset scale to match the test heights of the shapes tested
asset_scale = math.Vector3(1.0, 1.0, 9.0)
collider_entity.get_set_test(1, "Shape Configuration|Asset|Configuration|Asset Scale", asset_scale)
# Run through each collider shape type (sphere, box, capsule) and verify the surface generation
# and surface modification of the PhysX Collision Surface Tag Emitter Component.
for collider_shape in range(0, 3):
collider_shapes = {0: "Sphere", 1: "Box", 2: "Capsule"}
hydra.get_set_test(collider_entity, 0, "Shape Configuration|Shape", collider_shape)
# Test: Generate a new surface on the collider.
# There should be one instance at the very top of the collider mesh, and none on the baseline surface
# There should be one instance at the very top of the collider sphere, and none on the baseline surface
# (We use a small query box to only check for one placed instance point)
self.log("Starting PhysX Mesh Collider Test")
hydra.get_set_test(collider_entity, 0, "Configuration|Generated Tags", [surface_tag])
hydra.get_set_test(collider_entity, 0, "Configuration|Extended Tags", [invalid_tag])
top_point_success = self.wait_for_condition(lambda: dynveg.validate_instance_count(top_point, 0.25, 1), 5.0)
self.test_success = self.test_success and top_point_success
baseline_success = self.wait_for_condition(lambda: dynveg.validate_instance_count(baseline_surface_point,
0.25, 0), 5.0)
self.test_success = self.test_success and baseline_success
on_collider_top_point_count = (
f"Expected number of instances found on the top point for {collider_shapes[collider_shape]} shape",
f"Found an unexpected number of instances on the top point for {collider_shapes[collider_shape]} shape"
)
on_collider_baseline_count = (
f"Expected number of instances found on the baseline point for {collider_shapes[collider_shape]} shape",
f"Found an unexpected number of instances on the baseline point for {collider_shapes[collider_shape]} shape"
)
hydra.get_set_test(collider_entity, 1, "Configuration|Generated Tags", [surface_tag])
hydra.get_set_test(collider_entity, 1, "Configuration|Extended Tags", [invalid_tag])
top_point = math.Vector3(entity_center_point.x, entity_center_point.y, entity_center_point.z +
collider_radius)
baseline_surface_point = math.Vector3(entity_center_point.x, entity_center_point.y, entity_center_point.z +
(baseline_surface_height / 2.0))
top_point_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count(top_point, 0.25, 1), 5.0)
Report.result(on_collider_top_point_count, top_point_success)
baseline_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count(baseline_surface_point,
0.25, 0), 5.0)
Report.result(on_collider_baseline_count, baseline_success)
# Test: Modify an existing surface inside the collider.
# There should be no instances at the very top of the collider mesh, and none on the baseline surface within
# our query box as PhysX meshes are treated as hollow shells, not solid volumes.
# There should be no instances at the very top of the collider sphere, and one on the baseline surface
# within our query box.
# (We use a small query box to only check for one placed instance point)
hydra.get_set_test(collider_entity, 0, "Configuration|Generated Tags", [invalid_tag])
hydra.get_set_test(collider_entity, 0, "Configuration|Extended Tags", [surface_tag])
top_point_success = self.wait_for_condition(lambda: dynveg.validate_instance_count(top_point, 0.25, 0), 5.0)
self.test_success = self.test_success and top_point_success
baseline_success = self.wait_for_condition(lambda: dynveg.validate_instance_count(baseline_surface_point,
0.25, 0), 5.0)
self.test_success = self.test_success and baseline_success
hydra.get_set_test(collider_entity, 1, "Configuration|Generated Tags", [invalid_tag])
hydra.get_set_test(collider_entity, 1, "Configuration|Extended Tags", [surface_tag])
top_point_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count(top_point, 0.25, 0), 5.0)
Report.result(on_collider_top_point_count, top_point_success)
baseline_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count(baseline_surface_point,
0.25, 1), 5.0)
Report.result(on_collider_baseline_count, baseline_success)
# Setup collider entity with a PhysX Mesh
test_physx_mesh_asset_id = asset.AssetCatalogRequestBus(
bus.Broadcast, "GetAssetIdByPath", os.path.join("levels", "physics",
"Material_PerFaceMaterialGetsCorrectMaterial",
"test.pxmesh"), math.Uuid(), False)
collider_entity.remove_component("PhysX Collider")
collider_entity.add_component("PhysX Collider")
helper.wait_for_condition(lambda: editor.EditorComponentAPIBus(bus.Broadcast, 'IsComponentEnabled',
collider_entity.components[1]), 5.0)
hydra.get_set_test(collider_entity, 1, "Shape Configuration|Shape", 7)
hydra.get_set_test(collider_entity, 1, "Shape Configuration|Asset|PhysX Mesh", test_physx_mesh_asset_id)
# Set the asset scale to match the test heights of the shapes tested
asset_scale = math.Vector3(1.0, 1.0, 9.0)
collider_entity.get_set_test(1, "Shape Configuration|Asset|Configuration|Asset Scale", asset_scale)
# Test: Generate a new surface on the collider.
# There should be one instance at the very top of the collider mesh, and none on the baseline surface
# (We use a small query box to only check for one placed instance point)
Report.info("Starting PhysX Mesh Collider Test")
on_collider_top_point_count = (
f"Expected number of instances found on the top point for a PhysX Mesh",
f"Found an unexpected number of instances on the top point for a PhysX Mesh"
)
on_collider_baseline_count = (
f"Expected number of instances found on the baseline point for a PhysX Mesh",
f"Found an unexpected number of instances on the baseline point for a PhysX Mesh"
)
hydra.get_set_test(collider_entity, 0, "Configuration|Generated Tags", [surface_tag])
hydra.get_set_test(collider_entity, 0, "Configuration|Extended Tags", [invalid_tag])
top_point_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count(top_point, 0.25, 1), 5.0)
Report.result(on_collider_top_point_count, top_point_success)
baseline_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count(baseline_surface_point,
0.25, 0), 5.0)
Report.result(on_collider_baseline_count, baseline_success)
# Test: Modify an existing surface inside the collider.
# There should be no instances at the very top of the collider mesh, and none on the baseline surface within
# our query box as PhysX meshes are treated as hollow shells, not solid volumes.
# (We use a small query box to only check for one placed instance point)
hydra.get_set_test(collider_entity, 0, "Configuration|Generated Tags", [invalid_tag])
hydra.get_set_test(collider_entity, 0, "Configuration|Extended Tags", [surface_tag])
top_point_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count(top_point, 0.25, 0), 5.0)
Report.result(on_collider_top_point_count, top_point_success)
baseline_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count(baseline_surface_point,
0.25, 0), 5.0)
Report.result(on_collider_baseline_count, baseline_success)
test = TestPhysXColliderSurfaceTagEmitter()
test.run()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(PhysXColliderSurfaceTagEmitter_E2E_Editor)
@@ -5,136 +5,133 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.bus as bus
import azlmbr.legacy.general as general
import azlmbr.editor as editor
import azlmbr.math as math
import azlmbr.paths
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.editor_test_helper import EditorTestHelper
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
class Tests:
initial_instance_count = (
"Initial instance count is as expected",
"Found an unexpected number of initial instances"
)
autosnap_enabled_instance_count = (
"Found the expected number of instances with Auto Snap to Surface enabled",
"Found an unexpected number of instances with Auto Snap to Surface enabled"
)
autosnap_disabled_instance_count = (
"Found the expected number of instances with Auto Snap to Surface disabled",
"Found an unexpected number of instances with Auto Snap to Surface disabled"
)
class TestPositionModifierAutoSnapToSurface(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="PositionModifier_AutoSnapToSurface", args=["level"])
def PositionModifier_AutoSnapToSurfaceWorks():
"""
Summary:
Instance spawner is setup to plant on a spherical mesh. Offsets are set on the x-axis, and checks are performed
to ensure instances plant where expected depending on the toggle setting.
def run_test(self):
"""
Summary:
Instance spawner is setup to plant on a spherical mesh. Offsets are set on the x-axis, and checks are performed
to ensure instances plant where expected depending on the toggle setting.
Expected Behavior:
Offset instances snap to the expected surface when Auto Snap to Surface is enabled, and offset away from surface
when it is disabled.
Expected Behavior:
Offset instances snap to the expected surface when Auto Snap to Surface is enabled, and offset away from surface
when it is disabled.
Test Steps:
1) Open a simple level
2) Create a new entity with required vegetation area components and a Position Modifier
3) Create a spherical planting surface
4) Verify initial instance counts pre-filter
5) Create a child entity of the spawner entity with a Constant Gradient component and pin to spawner
6) Set the Position Modifier offset to 5 on the x-axis
7) Validate instance counts on top of and inside the sphere mesh with Auto Snap to Surface enabled
8) Validate instance counts on top of and inside the sphere mesh with Auto Snap to Surface disabled
Test Steps:
1) Create a new, temporary level
2) Create a new entity with required vegetation area components and a Position Modifier
3) Create a spherical planting surface
4) Verify initial instance counts pre-filter
5) Create a child entity of the spawner entity with a Constant Gradient component and pin to spawner
6) Set the Position Modifier offset to 5 on the x-axis
7) Validate instance counts on top of and inside the sphere mesh with Auto Snap to Surface enabled
8) Validate instance counts on top of and inside the sphere mesh with Auto Snap to Surface disabled
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
:return: None
"""
:return: None
"""
position_modifier_paths = ['Configuration|Position X|Range Min', 'Configuration|Position X|Range Max',
'Configuration|Position Y|Range Min', 'Configuration|Position Y|Range Max',
'Configuration|Position Z|Range Min', 'Configuration|Position Z|Range Max']
import os
# 1) Create a new, temporary level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=False,
)
import azlmbr.legacy.general as general
import azlmbr.math as math
# Set view of planting area for visual debugging
general.set_current_view_position(512.0, 500.0, 38.0)
general.set_current_view_rotation(-20.0, 0.0, 0.0)
import editor_python_test_tools.hydra_editor_utils as hydra
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
# 2) Create a new entity with required vegetation area components and a Position Modifier
spawner_center_point = math.Vector3(512.0, 512.0, 32.0)
asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
spawner_entity = dynveg.create_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 16.0,
asset_path)
position_modifier_paths = ['Configuration|Position X|Range Min', 'Configuration|Position X|Range Max',
'Configuration|Position Y|Range Min', 'Configuration|Position Y|Range Max',
'Configuration|Position Z|Range Min', 'Configuration|Position Z|Range Max']
# Add a Vegetation Position Modifier and set offset values to 0
spawner_entity.add_component("Vegetation Position Modifier")
for path in position_modifier_paths:
spawner_entity.get_set_test(3, path, 0)
# 1) Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
# 3) Create a spherical planting surface and a flat surface
flat_entity = dynveg.create_surface_entity("Flat Surface", spawner_center_point, 32.0, 32.0, 1.0)
hill_entity = dynveg.create_mesh_surface_entity_with_slopes("Planting Surface", spawner_center_point, 5.0)
# Set view of planting area for visual debugging
general.set_current_view_position(512.0, 500.0, 38.0)
general.set_current_view_rotation(-20.0, 0.0, 0.0)
# Disable the Flat Surface Box Shape component, and temporarily ignore initial instance counts due to LYN-2245
editor.EditorComponentAPIBus(bus.Broadcast, 'DisableComponents', [flat_entity.components[0]])
"""
# 4) Verify initial instance counts pre-filter
num_expected = 121
spawner_success = self.wait_for_condition(
lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0)
self.test_success = self.test_success and spawner_success
"""
# 2) Create a new entity with required vegetation area components and a Position Modifier
spawner_center_point = math.Vector3(512.0, 512.0, 32.0)
asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
spawner_entity = dynveg.create_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 16.0,
asset_path)
# 5) Create a child entity of the spawner entity with a Constant Gradient component and pin to spawner
components_to_add = ["Constant Gradient"]
gradient_entity = hydra.Entity("Gradient Entity")
gradient_entity.create_entity(spawner_center_point, components_to_add, parent_id=spawner_entity.id)
# Add a Vegetation Position Modifier and set offset values to 0
spawner_entity.add_component("Vegetation Position Modifier")
for path in position_modifier_paths:
spawner_entity.get_set_test(3, path, 0)
# Pin the Constant Gradient to the X axis of the spawner's Position Modifier component
spawner_entity.get_set_test(3, 'Configuration|Position X|Gradient|Gradient Entity Id', gradient_entity.id)
# 3) Create a spherical planting surface
hill_entity = dynveg.create_mesh_surface_entity_with_slopes("Planting Surface", spawner_center_point, 5.0)
# 6) Set the Position Modifier offset to 2.5 on the x-axis
spawner_entity.get_set_test(3, position_modifier_paths[0], 2.5)
spawner_entity.get_set_test(3, position_modifier_paths[1], 2.5)
# 4) Verify initial instance counts pre-filter
num_expected = 29
spawner_success = helper.wait_for_condition(
lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0)
Report.result(Tests.initial_instance_count, spawner_success)
# 7) Validate instance count at the top of the sphere mesh and inside the sphere mesh while Auto Snap to Surface
# is enabled
top_point = math.Vector3(512.0, 512.0, 37.0)
inside_point = math.Vector3(512.0, 512.0, 35.0)
radius = 0.5
num_expected = 1
self.log(f"Checking for instances in a {radius * 2}m area at {top_point.ToString()}")
top_success = self.wait_for_condition(lambda: dynveg.validate_instance_count(top_point, radius, num_expected),
5.0)
self.test_success = top_success and self.test_success
num_expected = 0
self.log(f"Checking for instances in a {radius * 2}m area at {inside_point.ToString()}")
inside_success = self.wait_for_condition(lambda: dynveg.validate_instance_count(inside_point, radius,
num_expected), 5.0)
self.test_success = inside_success and self.test_success
# 5) Create a child entity of the spawner entity with a Constant Gradient component and pin to spawner
components_to_add = ["Constant Gradient"]
gradient_entity = hydra.Entity("Gradient Entity")
gradient_entity.create_entity(spawner_center_point, components_to_add, parent_id=spawner_entity.id)
# 8) Toggle off Auto Snap to Surface. Instances should now plant inside the sphere and no longer on top
spawner_entity.get_set_test(3, "Configuration|Auto Snap To Surface", False)
num_expected = 0
self.log(f"Checking for instances in a {radius * 2}m area at {top_point.ToString()}")
top_success = self.wait_for_condition(lambda: dynveg.validate_instance_count(top_point, radius, num_expected),
5.0)
self.test_success = top_success and self.test_success
num_expected = 1
self.log(f"Checking for instances in a {radius * 2}m area at {inside_point.ToString()}")
inside_success = self.wait_for_condition(lambda: dynveg.validate_instance_count(inside_point, radius,
num_expected), 5.0)
self.test_success = inside_success and self.test_success
# Pin the Constant Gradient to the X axis of the spawner's Position Modifier component
spawner_entity.get_set_test(3, 'Configuration|Position X|Gradient|Gradient Entity Id', gradient_entity.id)
# 6) Set the Position Modifier offset to 2.5 on the x-axis
spawner_entity.get_set_test(3, position_modifier_paths[0], 2.5)
spawner_entity.get_set_test(3, position_modifier_paths[1], 2.5)
# 7) Validate instance count at the top of the sphere mesh and inside the sphere mesh while Auto Snap to Surface
# is enabled
top_point = math.Vector3(512.0, 512.0, 37.0)
inside_point = math.Vector3(512.0, 512.0, 35.0)
radius = 0.5
num_expected = 1
Report.info(f"Checking for instances in a {radius * 2}m area at {top_point.ToString()}")
top_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count(top_point, radius, num_expected),
5.0)
num_expected = 0
Report.info(f"Checking for instances in a {radius * 2}m area at {inside_point.ToString()}")
inside_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count(inside_point, radius,
num_expected), 5.0)
Report.result(Tests.autosnap_enabled_instance_count, top_success and inside_success)
# 8) Toggle off Auto Snap to Surface. Instances should now plant inside the sphere and no longer on top
spawner_entity.get_set_test(3, "Configuration|Auto Snap To Surface", False)
num_expected = 0
Report.info(f"Checking for instances in a {radius * 2}m area at {top_point.ToString()}")
top_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count(top_point, radius, num_expected),
5.0)
num_expected = 1
Report.info(f"Checking for instances in a {radius * 2}m area at {inside_point.ToString()}")
inside_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count(inside_point, radius,
num_expected), 5.0)
Report.result(Tests.autosnap_disabled_instance_count, top_success and inside_success)
test = TestPositionModifierAutoSnapToSurface()
test.run()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(PositionModifier_AutoSnapToSurfaceWorks)
@@ -5,159 +5,164 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import random
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.editor as editor
import azlmbr.legacy.general as general
import azlmbr.bus as bus
import azlmbr.math as math
import azlmbr.paths
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.editor_test_helper import EditorTestHelper
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
class Tests:
initial_instance_count = (
"Initial instance count is as expected",
"Found an unexpected number of initial instances"
)
position_offset = (
"Found instances at all expected locations with Position Modifier offsets configured",
"Failed to find all expected instances at all locations with Position Modifier offsets configured"
)
position_offset_overrides = (
"Found instances at all expected locations with Position Modifier offset overrides configured",
"Failed to find all expected instances at all locations with Position Modifier offset overrides configured"
)
class TestPositionModifierComponentAndOverrides(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="PositionModifierComponentAndOverrides_InstanceOffset", args=["level"])
def PositionModifier_ComponentAndOverrides_InstancesPlantAtSpecifiedOffsets():
"""
Summary: Range Min/Max in the Vegetation Position Modifier component and component overrides can be set for all
axes, and functions as expected when fed a gradient signal.
def run_test(self):
"""
Summary: Range Min/Max in the Vegetation Position Modifier component and component overrides can be set for all
axes, and functions as expected when fed a gradient signal.
Expected Behavior: Instances are offset by the specified amount.
Expected Behavior: Instances are offset by the specified amount.
Test Steps:
1) Open an existing level
2) Spawner area is setup with all necessary components
3) Surface for planting is created
4) Initial instance count validation pre-filter is performed
5) An entity with a Constant Gradient of 1 is added as a child to the spawner entity, and pinned to the Position
Modifier Gradient Entity Id fields
6) Sector size is adjusted on a Vegetation System Settings component to allow for offset instances to not fall
outside of the queried sector
7) Random offsets are set for each axis of the Position Modifier component, and instance counts are validated
8) Overrides are enabled on the Position Modifier component
9) Random offsets are set for each axis of the descriptor's Position Modifier overrides, and instance counts
are validated
Test Steps:
1) New test level is created
2) Spawner area is setup with all necessary components
3) Surface for planting is created
4) Initial instance count validation pre-filter is performed
5) An entity with a Constant Gradient of 1 is added as a child to the spawner entity, and pinned to the Position
Modifier Gradient Entity Id fields
6) Sector size is adjusted on a Vegetation System Settings component to allow for offset instances to not fall
outside of the queried sector
7) Random offsets are set for each axis of the Position Modifier component, and instance counts are validated
8) Overrides are enabled on the Position Modifier component
9) Random offsets are set for each axis of the descriptor's Position Modifier overrides, and instance counts
are validated
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
:return: None
"""
:return: None
"""
position_modifier_paths = ['Configuration|Position X|Range Min', 'Configuration|Position X|Range Max',
'Configuration|Position Y|Range Min', 'Configuration|Position Y|Range Max',
'Configuration|Position Z|Range Min', 'Configuration|Position Z|Range Max']
import os
import random
override_position_modifier_paths = ['Configuration|Embedded Assets|[0]|Position Modifier|Min X',
'Configuration|Embedded Assets|[0]|Position Modifier|Max X',
'Configuration|Embedded Assets|[0]|Position Modifier|Min Y',
'Configuration|Embedded Assets|[0]|Position Modifier|Max Y',
'Configuration|Embedded Assets|[0]|Position Modifier|Min Z',
'Configuration|Embedded Assets|[0]|Position Modifier|Max Z']
import azlmbr.editor as editor
import azlmbr.legacy.general as general
import azlmbr.bus as bus
import azlmbr.math as math
def generate_random_offset_list():
offset_list = []
while len(offset_list) < 10:
offset = round(random.uniform(-8.0, 8.0), 2)
if not -1.0 <= offset <= 1.0:
offset_list.append(offset)
print("List of values to test against = " + str(offset_list))
return offset_list
import editor_python_test_tools.hydra_editor_utils as hydra
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
def set_offset_and_verify_instance_counts(offset_to_test, center, is_override=False):
print(f"Starting test with an offset of {offset_to_test}")
# Set min/max values to the offset value
if not is_override:
for path in position_modifier_paths:
spawner_entity.get_set_test(3, path, offset_to_test)
else:
spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Position Modifier|Override Enabled",
True)
for path in override_position_modifier_paths:
spawner_entity.get_set_test(2, path, offset_to_test)
center_point = math.Vector3(center.x + offset_to_test, center.y + offset_to_test, center.z + offset_to_test)
radius = 0.5
print(f"Querying for instances in a {radius * 2}m area around {center_point.ToString()}")
offset_success = self.wait_for_condition(lambda: dynveg.validate_instance_count(center_point, radius, 1), 5.0)
offset_success2 = self.wait_for_condition(lambda: dynveg.validate_instance_count(center, radius, 0), 5.0)
self.test_success = offset_success and offset_success2 and self.test_success
position_modifier_paths = ['Configuration|Position X|Range Min', 'Configuration|Position X|Range Max',
'Configuration|Position Y|Range Min', 'Configuration|Position Y|Range Max',
'Configuration|Position Z|Range Min', 'Configuration|Position Z|Range Max']
# 1) Create a new, temporary level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=False,
)
override_position_modifier_paths = ['Configuration|Embedded Assets|[0]|Position Modifier|Min X',
'Configuration|Embedded Assets|[0]|Position Modifier|Max X',
'Configuration|Embedded Assets|[0]|Position Modifier|Min Y',
'Configuration|Embedded Assets|[0]|Position Modifier|Max Y',
'Configuration|Embedded Assets|[0]|Position Modifier|Min Z',
'Configuration|Embedded Assets|[0]|Position Modifier|Max Z']
# Set view of planting area for visual debugging
general.set_current_view_position(16.0, -5.0, 32.0)
def generate_random_offset_list():
offset_list = []
while len(offset_list) < 10:
offset = round(random.uniform(-8.0, 8.0), 2)
if not -1.0 <= offset <= 1.0:
offset_list.append(offset)
Report.info("List of values to test against = " + str(offset_list))
return offset_list
# 2) Create a new entity with required vegetation area components
spawner_center_point = math.Vector3(16.0, 16.0, 32.0)
asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
spawner_entity = dynveg.create_vegetation_area("Instance Spawner", spawner_center_point, 1.0, 1.0, 1.0, asset_path)
def set_offset_and_verify_instance_counts(offset_to_test, center, is_override=False):
Report.info(f"Starting test with an offset of {offset_to_test}")
# Set min/max values to the offset value
if not is_override:
for path in position_modifier_paths:
spawner_entity.get_set_test(3, path, offset_to_test)
else:
spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Position Modifier|Override Enabled",
True)
for path in override_position_modifier_paths:
spawner_entity.get_set_test(2, path, offset_to_test)
center_point = math.Vector3(center.x + offset_to_test, center.y + offset_to_test, center.z + offset_to_test)
radius = 0.5
Report.info(f"Querying for instances in a {radius * 2}m area around {center_point.ToString()}")
offset_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count(center_point, radius, 1), 5.0)
offset_success2 = helper.wait_for_condition(lambda: dynveg.validate_instance_count(center, radius, 0), 5.0)
return offset_success and offset_success2
# Add a Vegetation Position Modifier and set offset values to 0
spawner_entity.add_component("Vegetation Position Modifier")
for path in position_modifier_paths:
spawner_entity.get_set_test(3, path, 0)
# 1) Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
# 3) Add flat surface to plant on
dynveg.create_surface_entity("Planting Surface", spawner_center_point, 32.0, 32.0, 0.0)
# Set view of planting area for visual debugging
general.set_current_view_position(16.0, -5.0, 32.0)
# 4) Verify initial instance counts pre-filter
num_expected = 1 # Single instance planted
spawner_success = self.wait_for_condition(
lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0)
self.test_success = self.test_success and spawner_success
# 2) Create a new entity with required vegetation area components
spawner_center_point = math.Vector3(16.0, 16.0, 32.0)
asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
spawner_entity = dynveg.create_vegetation_area("Instance Spawner", spawner_center_point, 1.0, 1.0, 1.0, asset_path)
# 5) Create a child entity of the spawner entity with a Constant Gradient component
components_to_add = ["Constant Gradient"]
gradient_entity = hydra.Entity("Gradient Entity")
gradient_entity.create_entity(spawner_center_point, components_to_add, parent_id=spawner_entity.id)
# Add a Vegetation Position Modifier and set offset values to 0
spawner_entity.add_component("Vegetation Position Modifier")
for path in position_modifier_paths:
spawner_entity.get_set_test(3, path, 0)
# Pin the Constant Gradient to each axis of the Position Modifier
position_modifier_gradient_paths = ['Configuration|Position X|Gradient|Gradient Entity Id',
'Configuration|Position Y|Gradient|Gradient Entity Id',
'Configuration|Position Z|Gradient|Gradient Entity Id']
for path in position_modifier_gradient_paths:
spawner_entity.get_set_test(3, path, gradient_entity.id)
# 3) Add flat surface to plant on
dynveg.create_surface_entity("Planting Surface", spawner_center_point, 32.0, 32.0, 0.0)
# 6) Add a Vegetation System Settings Level component and change sector size to 32 sq meters so instances can
# offset to a greater range and still be validated
veg_system_settings_component = hydra.add_level_component("Vegetation System Settings")
editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", veg_system_settings_component,
"Configuration|Area System Settings|Sector Size In Meters", 32)
sector_size = hydra.get_component_property_value(veg_system_settings_component,
"Configuration|Area System Settings|Sector Size In Meters")
self.test_success = (sector_size == 32) and self.test_success
# 4) Verify initial instance counts pre-filter
num_expected = 1 # Single instance planted
spawner_success = helper.wait_for_condition(
lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0)
Report.result(Tests.initial_instance_count, spawner_success)
# 7) Set offsets on all axes and verify instance counts
offsets_to_test = generate_random_offset_list()
for offset in offsets_to_test:
if self.test_success:
set_offset_and_verify_instance_counts(offset, spawner_center_point)
# 5) Create a child entity of the spawner entity with a Constant Gradient component
components_to_add = ["Constant Gradient"]
gradient_entity = hydra.Entity("Gradient Entity")
gradient_entity.create_entity(spawner_center_point, components_to_add, parent_id=spawner_entity.id)
# 8) Toggle on allow overrides on the Position Modifier Component
spawner_entity.get_set_test(3, "Configuration|Allow Per-Item Overrides", True)
# Pin the Constant Gradient to each axis of the Position Modifier
position_modifier_gradient_paths = ['Configuration|Position X|Gradient|Gradient Entity Id',
'Configuration|Position Y|Gradient|Gradient Entity Id',
'Configuration|Position Z|Gradient|Gradient Entity Id']
for path in position_modifier_gradient_paths:
spawner_entity.get_set_test(3, path, gradient_entity.id)
# 9) Set offsets on all axes on descriptor overrides and verify instance counts
for offset in offsets_to_test:
if self.test_success:
set_offset_and_verify_instance_counts(offset, spawner_center_point, is_override=True)
# 6) Add a Vegetation System Settings Level component and change sector size to 32 sq meters so instances can
# offset to a greater range and still be validated
veg_system_settings_component = hydra.add_level_component("Vegetation System Settings")
editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", veg_system_settings_component,
"Configuration|Area System Settings|Sector Size In Meters", 32)
# 7) Set offsets on all axes and verify instance counts
offsets_to_test = generate_random_offset_list()
success = True
for offset in offsets_to_test:
success = success and set_offset_and_verify_instance_counts(offset, spawner_center_point)
Report.result(Tests.position_offset, success)
# 8) Toggle on allow overrides on the Position Modifier Component
spawner_entity.get_set_test(3, "Configuration|Allow Per-Item Overrides", True)
# 9) Set offsets on all axes on descriptor overrides and verify instance counts
success = True
for offset in offsets_to_test:
success = success and set_offset_and_verify_instance_counts(offset, spawner_center_point, is_override=True)
Report.result(Tests.position_offset_overrides, success)
test = TestPositionModifierComponentAndOverrides()
test.run()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(PositionModifier_ComponentAndOverrides_InstancesPlantAtSpecifiedOffsets)
@@ -5,139 +5,138 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
"""
C4814460: A level with simple vegetation is created. A child entity with required components is then created,
and pinned to the gradient entity id in Z direction for vegetation entity. The changes in vegetation area are observed.
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.legacy.general as general
import azlmbr.math as math
import azlmbr.bus as bus
import azlmbr.areasystem as areasystem
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.editor_test_helper import EditorTestHelper
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
class Tests:
gradient_entity_created = (
"Successfully created new Gradient entity",
"Failed to create Gradient entity"
)
non_override_rotation_check = (
"Instances are rotated at expected values after initial setup",
"Found unexpectedly rotated instances after initial setup"
)
override_rotation_check = (
"Instances are rotated at expected values after configuring overrides",
"Found unexpectedly rotated instances after configuring overrides"
)
class TestRotationModifierOverrides_InstancesRotateWithinRange(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="RotationModifierOverrides_InstancesRotateWithinRange", args=["level"])
def RotationModifierOverrides_InstancesRotateWithinRange():
"""
Summary:
A level with simple vegetation is created. A child entity with required components is then created,
and pinned to the gradient entity id in Z direction for vegetation entity. The changes in vegetation
area are observed.
def run_test(self):
"""
Summary:
A level with simple vegetation is created. A child entity with required components is then created,
and pinned to the gradient entity id in Z direction for vegetation entity. The changes in vegetation
area are observed.
Expected Behavior:
Vegetation instances all rotate randomly between 0-360 degrees on the Z-axis.
Expected Behavior:
Vegetation instances all rotate randomly between 0-360 degrees on the Z-axis.
Test Steps:
1) Open a new level
2) Create vegetation entity and add components
3) Set properties for vegetation entity
4) Create new child entity
5) Pin the child entity to vegetation entity as gradient entity id
6) Verify rotation without per-item overrides
7) Verify rotation with per-item overrides
Test Steps:
1) Create level
2) Create vegetation entity and add components
3) Set properties for vegetation entity
4) Create new child entity
5) Pin the child entity to vegetation entity as gradient entity id
6) Verify rotation without per-item overrides
7) Verify rotation with per-item overrides
Note:
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
Note:
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
:return: None
"""
:return: None
"""
import os
def get_expected_rotation(min, max, gradient_value):
return min + ((max - min) * gradient_value)
import azlmbr.legacy.general as general
import azlmbr.math as math
import azlmbr.bus as bus
import azlmbr.areasystem as areasystem
def validate_rotation(center, radius, num_expected, rot_degrees_vector):
# Verify that every instance in the given area has the expected rotation.
box = math.Aabb_CreateCenterRadius(center, radius)
instances = areasystem.AreaSystemRequestBus(bus.Broadcast, 'GetInstancesInAabb', box)
num_found = len(instances)
num_validated = 0
result = (num_found == num_expected)
print(f'instance count validation: {result} (found={num_found}, expected={num_expected})')
expected_rotation = math.Quaternion()
expected_rotation.SetFromEulerDegrees(rot_degrees_vector)
for instance in instances:
is_close = instance.rotation.IsClose(expected_rotation)
result = result and is_close
if is_close:
num_validated = num_validated + 1
#else:
# print(f'instance rotation validation: {is_close} (rotation={instance.rotation} expected={expected_rotation})')
print(f'instance rotation validation: {result} (num_validated={num_validated})')
return result
import editor_python_test_tools.hydra_editor_utils as hydra
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
# 1) Create level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=False,
)
general.set_current_view_position(512.0, 480.0, 38.0)
def get_expected_rotation(min, max, gradient_value):
return min + ((max - min) * gradient_value)
# 2) Create vegetation entity and add components
entity_position = math.Vector3(512.0, 512.0, 32.0)
asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice")
spawner_entity = dynveg.create_vegetation_area("Spawner Entity", entity_position, 16.0, 16.0, 16.0, asset_path)
spawner_entity.add_component("Vegetation Rotation Modifier")
# Our default vegetation settings places 20 instances per 16 meters, so we expect 20 * 20 total instances.
num_expected = 20 * 20
# This is technically twice as big as we need, but we want to make sure our query radius is large enough to discover every
# instance we've created.
area_radius = 16.0
def validate_rotation(center, radius, num_expected, rot_degrees_vector):
# Verify that every instance in the given area has the expected rotation.
box = math.Aabb_CreateCenterRadius(center, radius)
instances = areasystem.AreaSystemRequestBus(bus.Broadcast, 'GetInstancesInAabb', box)
num_found = len(instances)
num_validated = 0
result = (num_found == num_expected)
Report.info(f'instance count validation: {result} (found={num_found}, expected={num_expected})')
expected_rotation = math.Quaternion()
expected_rotation.SetFromEulerDegrees(rot_degrees_vector)
for instance in instances:
is_close = instance.rotation.IsClose(expected_rotation)
result = result and is_close
if is_close:
num_validated = num_validated + 1
Report.info(f'instance rotation validation: {result} (num_validated={num_validated})')
return result
# Create surface to spawn on
dynveg.create_surface_entity("Surface Entity", entity_position, 16.0, 16.0, 1.0)
# 1) Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
general.set_current_view_position(512.0, 480.0, 38.0)
# 3) Set properties for the rotation override on the descriptor, but don't set "allow overrides" on the rotation modifier yet.
spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Rotation Modifier|Override Enabled", True)
spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Rotation Modifier|Min Z", -70.0)
spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Rotation Modifier|Max Z", 30.0)
# 2) Create vegetation entity and add components
entity_position = math.Vector3(512.0, 512.0, 32.0)
asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice")
spawner_entity = dynveg.create_vegetation_area("Spawner Entity", entity_position, 16.0, 16.0, 16.0, asset_path)
spawner_entity.add_component("Vegetation Rotation Modifier")
# Our default vegetation settings places 20 instances per 16 meters, so we expect 20 * 20 total instances.
num_expected = 20 * 20
# This is technically twice as big as we need, but we want to make sure our query radius is large enough to discover
# every instance we've created.
area_radius = 16.0
# 4) Create new child entity with a constant gradient
constant_gradient_value = 0.25
gradient_entity = hydra.Entity("Gradient Entity")
gradient_entity.create_entity(
entity_position,
["Constant Gradient"],
parent_id=spawner_entity.id
)
if gradient_entity.id.IsValid():
self.log(f"'{gradient_entity.name}' created")
gradient_entity.get_set_test(0, "Configuration|Value", constant_gradient_value)
# Create surface to spawn on
dynveg.create_surface_entity("Surface Entity", entity_position, 16.0, 16.0, 1.0)
# 5) Pin the child entity to vegetation entity as gradient entity id
spawner_entity.get_set_test(3, "Configuration|Rotation Z|Gradient|Gradient Entity Id", gradient_entity.id)
# 3) Set properties for the rotation override on the descriptor, but don't set "allow overrides" on the rotation
# modifier yet
spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Rotation Modifier|Override Enabled", True)
spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Rotation Modifier|Min Z", -70.0)
spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Rotation Modifier|Max Z", 30.0)
# 6) Verify that without per-item overrides, the rotation matches the one calculated from the default rotation range.
general.idle_wait(1.0)
rotation_degrees = get_expected_rotation(-180.0, 180.0, constant_gradient_value)
rotation_success = self.wait_for_condition(
lambda: validate_rotation(entity_position, area_radius, num_expected, math.Vector3(0.0, 0.0, rotation_degrees)),
5.0)
self.test_success = self.test_success and rotation_success
# 4) Create new child entity with a constant gradient
constant_gradient_value = 0.25
gradient_entity = hydra.Entity("Gradient Entity")
gradient_entity.create_entity(
entity_position,
["Constant Gradient"],
parent_id=spawner_entity.id
)
Report.critical_result(Tests.gradient_entity_created, gradient_entity.id.IsValid())
gradient_entity.get_set_test(0, "Configuration|Value", constant_gradient_value)
# 7) Verify that with per-item overrides enabled, the rotation matches the one calculated from the override range.
spawner_entity.get_set_test(3, "Configuration|Allow Per-Item Overrides", True)
rotation_degrees = get_expected_rotation(-70.0, 30.0, constant_gradient_value)
rotation_success = self.wait_for_condition(
lambda: validate_rotation(entity_position, area_radius, num_expected, math.Vector3(0.0, 0.0, rotation_degrees)),
5.0)
self.test_success = self.test_success and rotation_success
# 5) Pin the child entity to vegetation entity as gradient entity id
spawner_entity.get_set_test(3, "Configuration|Rotation Z|Gradient|Gradient Entity Id", gradient_entity.id)
# 6) Verify that without per-item overrides, the rotation matches the one calculated from the default rotation range
general.idle_wait(1.0)
rotation_degrees = get_expected_rotation(-180.0, 180.0, constant_gradient_value)
rotation_success = helper.wait_for_condition(
lambda: validate_rotation(entity_position, area_radius, num_expected, math.Vector3(0.0, 0.0, rotation_degrees)),
5.0)
Report.result(Tests.non_override_rotation_check, rotation_success)
# 7) Verify that with per-item overrides enabled, the rotation matches the one calculated from the override range.
spawner_entity.get_set_test(3, "Configuration|Allow Per-Item Overrides", True)
rotation_degrees = get_expected_rotation(-70.0, 30.0, constant_gradient_value)
rotation_success = helper.wait_for_condition(
lambda: validate_rotation(entity_position, area_radius, num_expected, math.Vector3(0.0, 0.0, rotation_degrees)),
5.0)
Report.result(Tests.override_rotation_check, rotation_success)
test = TestRotationModifierOverrides_InstancesRotateWithinRange()
test.run()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(RotationModifierOverrides_InstancesRotateWithinRange)
@@ -5,207 +5,226 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.math as math
import azlmbr.legacy.general as general
import azlmbr.bus as bus
import azlmbr.areasystem as areasystem
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.editor_test_helper import EditorTestHelper
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
class Tests:
gradient_entity_created = (
"Successfully created new Gradient entity",
"Failed to create Gradient entity"
)
rotation_baseline = (
"Instances are not rotated after initial setup",
"Unexpectedly found rotated instances after initial setup"
)
x_axis_rotation_180 = (
"Instances rotated 180 degrees on X-axis as expected",
"Found unexpected instance rotation on X-axis"
)
x_axis_rotation_90 = (
"Instances rotated 90 degrees on X-axis as expected",
"Found unexpected instance rotation on X-axis"
)
y_axis_rotation_180 = (
"Instances rotated 180 degrees on Y-axis as expected",
"Found unexpected instance rotation on Y-axis"
)
y_axis_rotation_90 = (
"Instances rotated 90 degrees on Y-axis as expected",
"Found unexpected instance rotation on Y-axis"
)
z_axis_rotation_180 = (
"Instances rotated 180 degrees on Z-axis as expected",
"Found unexpected instance rotation on Z-axis"
)
z_axis_rotation_90 = (
"Instances rotated 90 degrees on Z-axis as expected",
"Found unexpected instance rotation on Z-axis"
)
class TestRotationModifier_InstancesRotateWithinRange(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="RotationModifier_InstancesRotateWithinRange", args=["level"])
def RotationModifier_InstancesRotateWithinRange():
"""
Summary: Range Min/Max in the Vegetation Rotation Modifier component can be set for all axes,
and functions as expected when fed a gradient signal
def run_test(self):
"""
Summary: Range Min/Max in the Vegetation Rotation Modifier component can be set for all axes,
and functions as expected when fed a gradient signal
Vegetation Entity: Set in the middle of the level it holds a child entity and the following components:
Vegetation Asset List
Box Shape (size: <10, 10, 10>)
Vegetation Layer Spawner
Vegetation Rotation Modifier
Rotation X (gradient: child, Range Min: Variable, Range Max: Variable)
Rotation Y (gradient: child, Range Min: Variable, Range Max: Variable)
Rotation Z (gradient: child, Range Min: Variable, Range Max: Variable)
Vegetation Entity: Set in the middle of the level it holds a child entity and the following components:
Vegetation Asset List
Box Shape (size: <10, 10, 10>)
Vegetation Layer Spawner
Vegetation Rotation Modifier
Rotation X (gradient: child, Range Min: Variable, Range Max: Variable)
Rotation Y (gradient: child, Range Min: Variable, Range Max: Variable)
Rotation Z (gradient: child, Range Min: Variable, Range Max: Variable)
Child Entity: Child to Vegetation Entity has the following components:
Box Shape (size: <10, 10, 10>)
Gradient Transform Modifier
Constant Gradient
Child Entity: Child to Vegetation Entity has the following components:
Box Shape (size: <10, 10, 10>)
Gradient Transform Modifier
Constant Gradient
Expected Behavior: The vegetation area adjusts rotation based on the Constant Gradient component
and the min and max values for each component. Min max of each axis is checked
Expected Behavior: The vegetation area adjusts rotation based on the Constant Gradient component
and the min and max values for each component. Min max of each axis is checked
Test Steps:
1) Create level
2) Set up vegetation entities
3) X-axis Check
4) Y-axis Check
5) Z-axis Check
Test Steps:
1) Open a new level
2) Set up vegetation entities
3) X-axis Check
4) Y-axis Check
5) Z-axis Check
Note:
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
Note:
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
:return: None
"""
:return: None
"""
# Test Constants
LEVEL_CENTER = math.Vector3(512.0, 512.0, 32.0)
constant_gradient_value = 0.15
import os
# Helper Functions
def change_range_max(axis, value):
spawner_entity.get_set_test(3, f"Configuration|Rotation {axis}|Range Max", value)
import azlmbr.math as math
import azlmbr.legacy.general as general
import azlmbr.bus as bus
import azlmbr.areasystem as areasystem
def change_range_min(axis, value):
spawner_entity.get_set_test(3, f"Configuration|Rotation {axis}|Range Min", value)
import editor_python_test_tools.hydra_editor_utils as hydra
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
def get_expected_rotation(min, max, gradient_value):
return min + ((max - min) * gradient_value)
# Test Constants
LEVEL_CENTER = math.Vector3(512.0, 512.0, 32.0)
constant_gradient_value = 0.15
def validate_rotation(center, radius, num_expected, rot_degrees_vector):
# Verify that every instance in the given area has the expected rotation.
box = math.Aabb_CreateCenterRadius(center, radius)
instances = areasystem.AreaSystemRequestBus(bus.Broadcast, 'GetInstancesInAabb', box)
num_found = len(instances)
result = (num_found == num_expected)
print(f'instance count validation: {result} (found={num_found}, expected={num_expected})')
expected_rotation = math.Quaternion()
expected_rotation.SetFromEulerDegrees(rot_degrees_vector)
for instance in instances:
result = result and instance.rotation.IsClose(expected_rotation)
print(f'instance rotation validation: {result} (rotation={instance.rotation} expected={expected_rotation})')
return result
# Helper Functions
def change_range_max(axis, value):
spawner_entity.get_set_test(3, f"Configuration|Rotation {axis}|Range Max", value)
def change_range_min(axis, value):
spawner_entity.get_set_test(3, f"Configuration|Rotation {axis}|Range Min", value)
# Main Script
# 1) Create Level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=False,
def get_expected_rotation(min, max, gradient_value):
return min + ((max - min) * gradient_value)
def validate_rotation(center, radius, num_expected, rot_degrees_vector):
# Verify that every instance in the given area has the expected rotation.
box = math.Aabb_CreateCenterRadius(center, radius)
instances = areasystem.AreaSystemRequestBus(bus.Broadcast, 'GetInstancesInAabb', box)
num_found = len(instances)
result = (num_found == num_expected)
Report.info(f'instance count validation: {result} (found={num_found}, expected={num_expected})')
expected_rotation = math.Quaternion()
expected_rotation.SetFromEulerDegrees(rot_degrees_vector)
for instance in instances:
result = result and instance.rotation.IsClose(expected_rotation)
Report.info(f'instance rotation validation: {result} (rotation={instance.rotation} expected={expected_rotation})')
return result
# Main Script
# 1) Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
general.set_current_view_position(512.0, 480.0, 38.0)
# 2) Set up vegetation entities
asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice")
spawner_entity = dynveg.create_vegetation_area("Spawner Entity", LEVEL_CENTER, 2.0, 2.0, 2.0, asset_path)
additional_components = [
"Vegetation Rotation Modifier"
]
for component in additional_components:
spawner_entity.add_component(component)
# Create surface to spawn vegetation on
dynveg.create_surface_entity("Surface Entity", LEVEL_CENTER, 10.0, 10.0, 1.0)
# Create Gradient Entity
gradient_entity = hydra.Entity("Gradient Entity")
gradient_entity.create_entity(
LEVEL_CENTER,
["Constant Gradient"],
parent_id=spawner_entity.id
)
Report.critical_result(Tests.gradient_entity_created, gradient_entity.id.IsValid())
gradient_entity.get_set_test(0, "Configuration|Value", constant_gradient_value)
# Vegetation Rotation Modifier
for axis in ["X", "Y", "Z"]:
spawner_entity.get_set_test(
3, f"Configuration|Rotation {axis}|Gradient|Gradient Entity Id", gradient_entity.id
)
general.set_current_view_position(512.0, 480.0, 38.0)
# 2) Set up vegetation entities
asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice")
spawner_entity = dynveg.create_vegetation_area("Spawner Entity", LEVEL_CENTER, 2.0, 2.0, 2.0, asset_path)
# Set up constants used across all the rotation checks
additional_components = [
"Vegetation Rotation Modifier"
]
for component in additional_components:
spawner_entity.add_component(component)
# Choose an area large enough to contain all of the instances we spawned.
area_center = LEVEL_CENTER
area_radius = 20.0
# We're spawning a 2x2 area, which will have 3 rows of 3 instances due to default vegetation system spacing, so
# we should have a total of 9 instances.
num_expected = 9
# Create surface to spawn vegetation on
dynveg.create_surface_entity("Surface Entity", LEVEL_CENTER, 10.0, 10.0, 1.0)
# 3) X-axis check
# baseline, verify that we initially have no rotation
change_range_min("Z", 0.0)
change_range_max("Z", 0.0)
rotation_success = helper.wait_for_condition(
lambda: validate_rotation(area_center, area_radius, num_expected, math.Vector3(0.0, 0.0, 0.0)), 5.0)
Report.result(Tests.rotation_baseline, rotation_success)
# Create Gradient Entity
gradient_entity = hydra.Entity("Gradient Entity")
gradient_entity.create_entity(
LEVEL_CENTER,
["Constant Gradient"],
parent_id=spawner_entity.id
)
if gradient_entity.id.IsValid():
self.log(f"'{gradient_entity.name}' created")
gradient_entity.get_set_test(0, "Configuration|Value", constant_gradient_value)
# Adjust x-axis range min / max to (-180, 0).
# Because we have a constant gradient of 0.25, our actual rotation should be (min + (max - min) * gradient),
# or (-180 + (0 - -180) * 0.25)
change_range_min("X", -180.0)
rotation_degrees = get_expected_rotation(-180.0, 0.0, constant_gradient_value)
rotation_success = helper.wait_for_condition(
lambda: validate_rotation(area_center, area_radius, num_expected, math.Vector3(rotation_degrees, 0.0, 0.0)),
5.0)
Report.result(Tests.x_axis_rotation_180, rotation_success)
# Vegetation Rotation Modifier
for axis in ["X", "Y", "Z"]:
spawner_entity.get_set_test(
3, f"Configuration|Rotation {axis}|Gradient|Gradient Entity Id", gradient_entity.id
)
# Set the min / max to (0, 90), with an expected result of (0 + (90 - 0) * 0.25)
change_range_min("X", 0.0)
change_range_max("X", 90.0)
rotation_degrees = get_expected_rotation(0.0, 90.0, constant_gradient_value)
rotation_success = helper.wait_for_condition(
lambda: validate_rotation(area_center, area_radius, num_expected, math.Vector3(rotation_degrees, 0.0, 0.0)),
5.0)
Report.result(Tests.x_axis_rotation_90, rotation_success)
change_range_max("X", 0.0)
# Set up constants used across all the rotation checks
# 4) Y-axis check
change_range_min("Y", -180.0)
rotation_degrees = get_expected_rotation(-180.0, 0.0, constant_gradient_value)
rotation_success = helper.wait_for_condition(
lambda: validate_rotation(area_center, area_radius, num_expected, math.Vector3(0.0, rotation_degrees, 0.0)),
5.0)
Report.result(Tests.y_axis_rotation_180, rotation_success)
# Choose an area large enough to contain all of the instances we spawned.
area_center = LEVEL_CENTER
area_radius = 20.0
# We're spawning a 2x2 area, which will have 3 rows of 3 instances due to default vegetation system spacing, so
# we should have a total of 9 instances.
num_expected = 9
# 3) X-axis check
general.idle_wait(3.0) # Allow mesh to load
change_range_min("Y", 0.0)
change_range_max("Y", 90.0)
rotation_degrees = get_expected_rotation(0.0, 90.0, constant_gradient_value)
rotation_success = helper.wait_for_condition(
lambda: validate_rotation(area_center, area_radius, num_expected, math.Vector3(0.0, rotation_degrees, 0.0)),
5.0)
Report.result(Tests.y_axis_rotation_90, rotation_success)
change_range_max("Y", 0.0)
# baseline, verify that we initially have no rotation
change_range_min("Z", 0.0)
change_range_max("Z", 0.0)
rotation_success = self.wait_for_condition(
lambda: validate_rotation(area_center, area_radius, num_expected, math.Vector3(0.0, 0.0, 0.0)),
5.0)
self.test_success = self.test_success and rotation_success
# 5) Z-axis check
change_range_min("Z", -180.0)
rotation_degrees = get_expected_rotation(-180.0, 0.0, constant_gradient_value)
rotation_success = helper.wait_for_condition(
lambda: validate_rotation(area_center, area_radius, num_expected, math.Vector3(0.0, 0.0, rotation_degrees)),
5.0)
Report.result(Tests.z_axis_rotation_180, rotation_success)
# Adjust x-axis range min / max to (-180, 0).
# Because we have a constant gradient of 0.25, our actual rotation should be (min + (max - min) * gradient),
# or (-180 + (0 - -180) * 0.25)
change_range_min("X", -180.0)
rotation_degrees = get_expected_rotation(-180.0, 0.0, constant_gradient_value)
rotation_success = self.wait_for_condition(
lambda: validate_rotation(area_center, area_radius, num_expected, math.Vector3(rotation_degrees, 0.0, 0.0)),
5.0)
self.test_success = self.test_success and rotation_success
# Set the min / max to (0, 90), with an expected result of (0 + (90 - 0) * 0.25)
change_range_min("X", 0.0)
change_range_max("X", 90.0)
rotation_degrees = get_expected_rotation(0.0, 90.0, constant_gradient_value)
rotation_success = self.wait_for_condition(
lambda: validate_rotation(area_center, area_radius, num_expected, math.Vector3(rotation_degrees, 0.0, 0.0)),
5.0)
self.test_success = self.test_success and rotation_success
change_range_max("X", 0.0)
# 4) Y-axis check
change_range_min("Y", -180.0)
rotation_degrees = get_expected_rotation(-180.0, 0.0, constant_gradient_value)
rotation_success = self.wait_for_condition(
lambda: validate_rotation(area_center, area_radius, num_expected, math.Vector3(0.0, rotation_degrees, 0.0)),
5.0)
self.test_success = self.test_success and rotation_success
change_range_min("Y", 0.0)
change_range_max("Y", 90.0)
rotation_degrees = get_expected_rotation(0.0, 90.0, constant_gradient_value)
rotation_success = self.wait_for_condition(
lambda: validate_rotation(area_center, area_radius, num_expected, math.Vector3(0.0, rotation_degrees, 0.0)),
5.0)
self.test_success = self.test_success and rotation_success
change_range_max("Y", 0.0)
# 5) Z-axis check
change_range_min("Z", -180.0)
rotation_degrees = get_expected_rotation(-180.0, 0.0, constant_gradient_value)
rotation_success = self.wait_for_condition(
lambda: validate_rotation(area_center, area_radius, num_expected, math.Vector3(0.0, 0.0, rotation_degrees)),
5.0)
self.test_success = self.test_success and rotation_success
change_range_min("Z", 0.0)
change_range_max("Z", 90.0)
rotation_degrees = get_expected_rotation(0.0, 90.0, constant_gradient_value)
rotation_success = self.wait_for_condition(
lambda: validate_rotation(area_center, area_radius, num_expected, math.Vector3(0.0, 0.0, rotation_degrees)),
5.0)
self.test_success = self.test_success and rotation_success
change_range_min("Z", 0.0)
change_range_max("Z", 90.0)
rotation_degrees = get_expected_rotation(0.0, 90.0, constant_gradient_value)
rotation_success = helper.wait_for_condition(
lambda: validate_rotation(area_center, area_radius, num_expected, math.Vector3(0.0, 0.0, rotation_degrees)),
5.0)
Report.result(Tests.z_axis_rotation_90, rotation_success)
test = TestRotationModifier_InstancesRotateWithinRange()
test.run()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(RotationModifier_InstancesRotateWithinRange)
@@ -5,153 +5,155 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.areasystem as areasystem
import azlmbr.bus as bus
import azlmbr.legacy.general as general
import azlmbr.math as math
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.editor_test_helper import EditorTestHelper
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
# Constants
CLOSE_ENOUGH_THRESHOLD = 0.01
class Tests:
gradient_entity_created = (
"Successfully created Gradient entity",
"Failed to create Gradient entity"
)
scale_values_set = (
"Scale Min and Scale Max are set to 0.1 and 1.0 in Vegetation Asset List",
"Scale Min and Scale Max are not set to 0.1 and 1.0 in Vegetation Asset List"
)
instance_count = (
"Found the expected number of instances",
"Found an unexpected number of instances"
)
instances_properly_scaled = (
"All instances scaled within appropriate range",
"Found instances scaled outside of the appropriate range"
)
class TestScaleModifierOverrides_InstancesProperlyScale(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="ScaleModifierOverrides_InstancesProperlyScale", args=["level"])
def ScaleModifierOverrides_InstancesProperlyScale():
"""
Summary:
A level is created, then as simple vegetation area is created. Vegetation Scale Modifier component is
added to the vegetation area. A new child entity is created with Random Noise Gradient Generator,
Gradient Transform Modifier, and Box Shape. Child entity is set as gradient entity id in Vegetation
Scale Modifier, and scale of instances is validated to fall within expected range.
def run_test(self):
"""
Summary:
A level is created, then as simple vegetation area is created. Vegetation Scale Modifier component is
added to the vegetation area. A new child entity is created with Random Noise Gradient Generator,
Gradient Transform Modifier, and Box Shape. Child entity is set as gradient entity id in Vegetation
Scale Modifier, and scale of instances is validated to fall within expected range.
Expected Behavior:
Vegetation instances have random scale between Range Min and Range Max applied.
Expected Behavior:
Vegetation instances have random scale between Range Min and Range Max applied.
Test Steps:
1) Open an existing level
2) Create a new entity with components "Vegetation Layer Spawner", "Vegetation Asset List", "Box Shape"
3) Set a valid mesh asset on the Vegetation Asset List
4) Add Vegetation Scale Modifier component to the vegetation and set the values
5) Toggle on Scale Modifier Override and verify Scale Min and Scale Max are set 0.1 and 1.0
6) Create a new child entity and add components
7) Add child entity as gradient entity id in Vegetation Scale Modifier
8) Validate scale of instances with a few different min/max override values
Test Steps:
1) Create level
2) Create a new entity with components "Vegetation Layer Spawner", "Vegetation Asset List", "Box Shape"
3) Set a valid mesh asset on the Vegetation Asset List
4) Add Vegetation Scale Modifier component to the vegetation and set the values
5) Toggle on Scale Modifier Override and verify Scale Min and Scale Max are set 0.1 and 1.0
6) Create a new child entity and add components
7) Add child entity as gradient entity id in Vegetation Scale Modifier
8) Validate scale of instances with a few different min/max override values
Note:
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
Note:
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
:return: None
"""
:return: None
"""
import os
def set_and_validate_scale(entity, min_scale, max_scale):
# Set Range Min/Max
entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Scale Modifier|Min", min_scale)
entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Scale Modifier|Max", max_scale)
import azlmbr.areasystem as areasystem
import azlmbr.bus as bus
import azlmbr.legacy.general as general
import azlmbr.math as math
# Clear all areas to force a refresh
general.run_console('veg_debugClearAllAreas')
import editor_python_test_tools.hydra_editor_utils as hydra
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
# Wait for instances to spawn
num_expected = 20 * 20
self.test_success = self.test_success and self.wait_for_condition(
lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0)
# Constants
CLOSE_ENOUGH_THRESHOLD = 0.01
# Validate scale values of instances
box = azlmbr.shape.ShapeComponentRequestsBus(bus.Event, 'GetEncompassingAabb', entity.id)
instances = areasystem.AreaSystemRequestBus(bus.Broadcast, 'GetInstancesInAabb', box)
if len(instances) == num_expected:
for instance in instances:
if min_scale <= instance.scale <= max_scale:
self.log("All instances scaled within appropriate range")
return True
self.log(f"Instance at {instance.position} scale is {instance.scale}. Expected between "
f"{min_scale}/{max_scale}")
return False
self.log(f"Failed to find all instances! Found {len(instances)}, expected {num_expected}.")
return False
def set_and_validate_scale(entity, min_scale, max_scale):
# Set Range Min/Max
entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Scale Modifier|Min", min_scale)
entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Scale Modifier|Max", max_scale)
# 1) Create level and set an appropriate view of spawner area
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=False,
# Refresh the planted instances
general.run_console("veg_DebugClearAllAreas")
# Check the initial instance count
num_expected = 20 * 20
success = helper.wait_for_condition(
lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0)
Report.critical_result(Tests.instance_count, success)
# Validate scale values of instances
box = azlmbr.shape.ShapeComponentRequestsBus(bus.Event, 'GetEncompassingAabb', entity.id)
instances = areasystem.AreaSystemRequestBus(bus.Broadcast, 'GetInstancesInAabb', box)
for instance in instances:
if min_scale <= instance.scale <= max_scale:
return True
return False
# 1) Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
general.set_current_view_position(500.49, 498.69, 46.66)
general.set_current_view_rotation(-42.05, 0.00, -36.33)
# 2) Create a new entity with components "Vegetation Layer Spawner", "Vegetation Asset List", "Box Shape"
entity_position = math.Vector3(512.0, 512.0, 32.0)
asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice")
spawner_entity = dynveg.create_vegetation_area("Spawner Entity", entity_position, 16.0, 16.0, 10.0, asset_path)
# Create a surface to plant on and add a Vegetation Debugger Level component to allow refreshes
dynveg.create_surface_entity("Surface Entity", entity_position, 20.0, 20.0, 1.0)
hydra.add_level_component("Vegetation Debugger")
# 4) Add Vegetation Scale Modifier component to the vegetation and set the values
spawner_entity.add_component("Vegetation Scale Modifier")
spawner_entity.get_set_test(3, "Configuration|Allow Per-Item Overrides", True)
# 5) Toggle on Scale Modifier Override and verify Scale Min and Scale Max are set 0.1 and 1.0
spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Scale Modifier|Override Enabled", True)
scale_min = float(
format(
(
hydra.get_component_property_value(
spawner_entity.components[2], "Configuration|Embedded Assets|[0]|Scale Modifier|Min"
)
),
".1f",
)
general.set_current_view_position(500.49, 498.69, 46.66)
general.set_current_view_rotation(-42.05, 0.00, -36.33)
# 2) Create a new entity with components "Vegetation Layer Spawner", "Vegetation Asset List", "Box Shape"
entity_position = math.Vector3(512.0, 512.0, 32.0)
asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice")
spawner_entity = dynveg.create_vegetation_area("Spawner Entity", entity_position, 16.0, 16.0, 10.0, asset_path)
# Create a surface to plant on and add a Vegetation Debugger Level component to allow refreshes
dynveg.create_surface_entity("Surface Entity", entity_position, 20.0, 20.0, 1.0)
hydra.add_level_component("Vegetation Debugger")
# 4) Add Vegetation Scale Modifier component to the vegetation and set the values
spawner_entity.add_component("Vegetation Scale Modifier")
spawner_entity.get_set_test(3, "Configuration|Allow Per-Item Overrides", True)
# 5) Toggle on Scale Modifier Override and verify Scale Min and Scale Max are set 0.1 and 1.0
spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Scale Modifier|Override Enabled", True)
scale_min = float(
format(
(
hydra.get_component_property_value(
spawner_entity.components[2], "Configuration|Embedded Assets|[0]|Scale Modifier|Min"
)
),
".1f",
)
)
scale_max = float(
format(
(
hydra.get_component_property_value(
spawner_entity.components[2], "Configuration|Embedded Assets|[0]|Scale Modifier|Max"
)
),
".1f",
)
scale_max = float(
format(
(
hydra.get_component_property_value(
spawner_entity.components[2], "Configuration|Embedded Assets|[0]|Scale Modifier|Max"
)
),
".1f",
)
)
if ((scale_max - 1.0) < CLOSE_ENOUGH_THRESHOLD) and ((scale_min - 0.1) < CLOSE_ENOUGH_THRESHOLD):
self.log("Scale Min and Scale Max are set to 0.1 and 1.0 in Vegetation Asset List")
else:
self.log("Scale Min and Scale Max are not set to 0.1 and 1.0 in Vegetation Asset List")
)
Report.result(Tests.scale_values_set, ((scale_max - 1.0) < CLOSE_ENOUGH_THRESHOLD) and
((scale_min - 0.1) < CLOSE_ENOUGH_THRESHOLD))
# 6) Create a new child entity and add components
gradient_entity = hydra.Entity("Gradient Entity")
gradient_entity.create_entity(
entity_position,
["Random Noise Gradient", "Gradient Transform Modifier", "Box Shape"],
parent_id=spawner_entity.id
)
if gradient_entity.id.IsValid():
self.log(f"'{gradient_entity.name}' created")
# 6) Create a new child entity and add components
gradient_entity = hydra.Entity("Gradient Entity")
gradient_entity.create_entity(
entity_position,
["Random Noise Gradient", "Gradient Transform Modifier", "Box Shape"],
parent_id=spawner_entity.id
)
Report.result(Tests.gradient_entity_created, gradient_entity.id.IsValid())
# 7) Add child entity as gradient entity id in Vegetation Scale Modifier
spawner_entity.get_set_test(3, "Configuration|Gradient|Gradient Entity Id", gradient_entity.id)
# 7) Add child entity as gradient entity id in Vegetation Scale Modifier
spawner_entity.get_set_test(3, "Configuration|Gradient|Gradient Entity Id", gradient_entity.id)
# 8) Validate instances are scaled properly via a few different Range Min/Max settings on the override
self.test_success = set_and_validate_scale(spawner_entity, 0.1, 1.0) and self.test_success
self.test_success = set_and_validate_scale(spawner_entity, 2.0, 2.5) and self.test_success
self.test_success = set_and_validate_scale(spawner_entity, 1.0, 5.0) and self.test_success
# 8) Validate instances are scaled properly via a few different Range Min/Max settings on the override
Report.result(Tests.instances_properly_scaled, set_and_validate_scale(spawner_entity, 0.1, 1.0))
Report.result(Tests.instances_properly_scaled, set_and_validate_scale(spawner_entity, 2.0, 2.5))
Report.result(Tests.instances_properly_scaled, set_and_validate_scale(spawner_entity, 1.0, 5.0))
test = TestScaleModifierOverrides_InstancesProperlyScale()
test.run()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(ScaleModifierOverrides_InstancesProperlyScale)
@@ -5,123 +5,123 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.areasystem as areasystem
import azlmbr.bus as bus
import azlmbr.legacy.general as general
import azlmbr.math as math
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.editor_test_helper import EditorTestHelper
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
class Tests:
gradient_entity_created = (
"Successfully created Gradient entity",
"Failed to create Gradient entity"
)
instance_count = (
"Found the expected number of instances",
"Found an unexpected number of instances"
)
instances_properly_scaled = (
"All instances scaled within appropriate range",
"Found instances scaled outside of the appropriate range"
)
class TestScaleModifier_InstancesProperlyScale(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="ScaleModifier_InstancesProperlyScale", args=["level"])
def ScaleModifier_InstancesProperlyScale():
"""
Summary:
A New level is created. A New entity is created with components Vegetation Layer Spawner, Vegetation Asset List,
Box Shape and Vegetation Scale Modifier. A New child entity is created with components Random Noise Gradient,
Gradient Transform Modifier, and Box Shape. Pin the Random Noise entity to the Gradient Entity Id field for
the Gradient group. Range Min and Range Max are set to few values and values are validated. Range Min and Range
Max are set to few other values and values are validated.
def run_test(self):
"""
Summary:
A New level is created. A New entity is created with components Vegetation Layer Spawner, Vegetation Asset List,
Box Shape and Vegetation Scale Modifier. A New child entity is created with components Random Noise Gradient,
Gradient Transform Modifier, and Box Shape. Pin the Random Noise entity to the Gradient Entity Id field for
the Gradient group. Range Min and Range Max are set to few values and values are validated. Range Min and Range
Max are set to few other values and values are validated.
Expected Behavior:
Vegetation instances are scaled within Range Min/Range Max.
Expected Behavior:
Vegetation instances are scaled within Range Min/Range Max.
Test Steps:
1) Open an existing level
2) Create a new entity with components Vegetation Layer Spawner, Vegetation Asset List, Box Shape and
Vegetation Scale Modifier
3) Create child entity with components Random Noise Gradient, Gradient Transform Modifier and Box Shape
4) Pin the Random Noise entity to the Gradient Entity Id field for the Gradient group.
5) Range Min/Max is set to few different values on the Vegetation Scale Modifier component and
scale of instances is validated
Test Steps:
1) Create level
2) Create a new entity with components Vegetation Layer Spawner, Vegetation Asset List, Box Shape and
Vegetation Scale Modifier
3) Create child entity with components Random Noise Gradient, Gradient Transform Modifier and Box Shape
4) Pin the Random Noise entity to the Gradient Entity Id field for the Gradient group.
5) Range Min/Max is set to few different values on the Vegetation Scale Modifier component and
scale of instances is validated
Note:
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
Note:
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
:return: None
"""
:return: None
"""
import os
def set_and_validate_scale(entity, min_scale, max_scale):
# Set Range Min/Max
entity.get_set_test(3, "Configuration|Range Min", min_scale)
entity.get_set_test(3, "Configuration|Range Max", max_scale)
import azlmbr.areasystem as areasystem
import azlmbr.bus as bus
import azlmbr.legacy.general as general
import azlmbr.math as math
# Clear all areas to force a refresh
general.run_console('veg_debugClearAllAreas')
import editor_python_test_tools.hydra_editor_utils as hydra
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
# Wait for instances to spawn
num_expected = 20 * 20
self.test_success = self.test_success and self.wait_for_condition(
lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0)
def set_and_validate_scale(entity, min_scale, max_scale):
# Set Range Min/Max
entity.get_set_test(3, "Configuration|Range Min", min_scale)
entity.get_set_test(3, "Configuration|Range Max", max_scale)
# Validate scale values of instances
box = azlmbr.shape.ShapeComponentRequestsBus(bus.Event, 'GetEncompassingAabb', entity.id)
instances = areasystem.AreaSystemRequestBus(bus.Broadcast, 'GetInstancesInAabb', box)
if len(instances) == num_expected:
for instance in instances:
if min_scale <= instance.scale <= max_scale:
self.log("All instances scaled within appropriate range")
return True
self.log(f"Instance at {instance.position} scale is {instance.scale}. Expected between "
f"{min_scale}/{max_scale}")
return False
self.log(f"Failed to find all instances! Found {len(instances)}, expected {num_expected}.")
return False
# Refresh the planted instances
general.run_console("veg_DebugClearAllAreas")
# 1) Create level and set an appropriate view of spawner area
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=False,
)
# Check the initial instance count
num_expected = 20 * 20
success = helper.wait_for_condition(
lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0)
Report.critical_result(Tests.instance_count, success)
general.set_current_view_position(500.49, 498.69, 46.66)
general.set_current_view_rotation(-42.05, 0.00, -36.33)
# Validate scale values of instances
box = azlmbr.shape.ShapeComponentRequestsBus(bus.Event, 'GetEncompassingAabb', entity.id)
instances = areasystem.AreaSystemRequestBus(bus.Broadcast, 'GetInstancesInAabb', box)
for instance in instances:
if min_scale <= instance.scale <= max_scale:
return True
return False
# 2) Create a new entity with components Vegetation Layer Spawner, Vegetation Asset List, Box Shape and
# Vegetation Scale Modifier
entity_position = math.Vector3(512.0, 512.0, 32.0)
asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice")
spawner_entity = dynveg.create_vegetation_area("Spawner Entity", entity_position, 16.0, 16.0, 16.0,
asset_path)
spawner_entity.add_component("Vegetation Scale Modifier")
# 1) Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
# Create a surface to plant on and add a Vegetation Debugger Level component to allow refreshes
dynveg.create_surface_entity("Surface Entity", entity_position, 20.0, 20.0, 1.0)
hydra.add_level_component("Vegetation Debugger")
general.set_current_view_position(500.49, 498.69, 46.66)
general.set_current_view_rotation(-42.05, 0.00, -36.33)
# 3) Create child entity with components Random Noise Gradient, Gradient Transform Modifier and Box Shape
gradient_entity = hydra.Entity("Gradient Entity")
gradient_entity.create_entity(
entity_position,
["Random Noise Gradient", "Gradient Transform Modifier", "Box Shape"],
parent_id=spawner_entity.id
)
if gradient_entity.id.IsValid():
self.log(f"'{gradient_entity.name}' created")
# 2) Create a new entity with components Vegetation Layer Spawner, Vegetation Asset List, Box Shape and
# Vegetation Scale Modifier
entity_position = math.Vector3(512.0, 512.0, 32.0)
asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice")
spawner_entity = dynveg.create_vegetation_area("Spawner Entity", entity_position, 16.0, 16.0, 16.0,
asset_path)
spawner_entity.add_component("Vegetation Scale Modifier")
# 4) Pin the Random Noise entity to the Gradient Entity Id field for the Gradient group.
spawner_entity.get_set_test(3, "Configuration|Gradient|Gradient Entity Id", gradient_entity.id)
# Create a surface to plant on and add a Vegetation Debugger Level component to allow refreshes
dynveg.create_surface_entity("Surface Entity", entity_position, 20.0, 20.0, 1.0)
hydra.add_level_component("Vegetation Debugger")
# 5) Set Range Min/Max on the Vegetation Scale Modifier component to diff values, and verify instance scale is
# within bounds
self.test_success = set_and_validate_scale(spawner_entity, 2.0, 4.0) and self.test_success
self.test_success = set_and_validate_scale(spawner_entity, 12.0, 40.0) and self.test_success
self.test_success = set_and_validate_scale(spawner_entity, 0.5, 2.5) and self.test_success
# 3) Create child entity with components Random Noise Gradient, Gradient Transform Modifier and Box Shape
gradient_entity = hydra.Entity("Gradient Entity")
gradient_entity.create_entity(
entity_position,
["Random Noise Gradient", "Gradient Transform Modifier", "Box Shape"],
parent_id=spawner_entity.id
)
Report.critical_result(Tests.gradient_entity_created, gradient_entity.id.IsValid())
# 4) Pin the Random Noise entity to the Gradient Entity Id field for the Gradient group.
spawner_entity.get_set_test(3, "Configuration|Gradient|Gradient Entity Id", gradient_entity.id)
# 5) Set Range Min/Max on the Vegetation Scale Modifier component to diff values, and verify instance scale is
# within bounds
Report.result(Tests.instances_properly_scaled, set_and_validate_scale(spawner_entity, 2.0, 4.0))
Report.result(Tests.instances_properly_scaled, set_and_validate_scale(spawner_entity, 12.0, 40.0))
Report.result(Tests.instances_properly_scaled, set_and_validate_scale(spawner_entity, 0.5, 2.5))
test = TestScaleModifier_InstancesProperlyScale()
test.run()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(ScaleModifier_InstancesProperlyScale)
@@ -0,0 +1,113 @@
"""
Copyright (c) Contributors to the Open 3D Engine Project.
For complete copyright and license terms please see the LICENSE at the root of this distribution.
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
class Tests:
instance_count_in_box_shape = (
"Only found instances in the configured Box Shape intersection area",
"Found instances outside of the configured Box Shape intersection area"
)
instance_count_in_cylinder_shape = (
"Only found instances in the configured Cylinder Shape intersection area",
"Found instances outside of the configured Cylinder Shape intersection area"
)
preprocess_instance_count = (
"Found the expected number of instances with preprocessing filter stage",
"Found an unexpected number of instances with preprocessing filter stage"
)
postprocess_instance_count = (
"Found the expected number of instances with postprocessing filter stage",
"Found an unexpected number of instances with postprocessing filter stage"
)
def ShapeIntersectionFilter_FilterStageToggle():
"""
Summary:
Filter Stage toggle affects final vegetation position
Expected Result:
Vegetation instances plant differently depending on the Filter Stage setting. With PreProcess, some vegetation
instances can appear on slopes outside the filtered values. With PostProcess, vegetation instances only appear on
the correct slope values.
:return: None
"""
import os
import azlmbr.math as math
import azlmbr.legacy.general as general
import editor_python_test_tools.hydra_editor_utils as hydra
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
# Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
general.set_current_view_position(512.0, 480.0, 38.0)
# Create basic vegetation entity
position = math.Vector3(512.0, 512.0, 32.0)
asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
vegetation = dynveg.create_vegetation_area("vegetation", position, 16.0, 16.0, 16.0, asset_path)
# Create Surface for instances to plant on
dynveg.create_surface_entity("Surface_Entity_Parent", position, 16.0, 16.0, 1.0)
# Add a Vegetation Shape Intersection Filter to the vegetation area entity
vegetation.add_component("Vegetation Shape Intersection Filter")
# Create a new entity as a child of the vegetation area entity with Box Shape
box = hydra.Entity("box")
box.create_entity(position, ["Box Shape"])
box.get_set_test(0, "Box Shape|Box Configuration|Dimensions", math.Vector3(8.0, 8.0, 1.0))
# Create a new entity as a child of the vegetation area entity with Cylinder Shape.
cylinder = hydra.Entity("cylinder")
cylinder.create_entity(position, ["Cylinder Shape"])
cylinder.get_set_test(0, "Cylinder Shape|Cylinder Configuration|Radius", 5.0)
cylinder.get_set_test(0, "Cylinder Shape|Cylinder Configuration|Height", 5.0)
box.set_test_parent_entity(vegetation)
cylinder.set_test_parent_entity(vegetation)
# On the Shape Intersection Filter component, click the crosshair button, and add child entities one by one
vegetation.get_set_test(3, "Configuration|Shape Entity Id", box.id)
result = helper.wait_for_condition(lambda: dynveg.validate_instance_count(position, 8.0, 100), 2.0)
Report.result(Tests.instance_count_in_box_shape, result)
vegetation.get_set_test(3, "Configuration|Shape Entity Id", cylinder.id)
result = helper.wait_for_condition(lambda: dynveg.validate_instance_count(position, 5.0, 100), 2.0)
Report.result(Tests.instance_count_in_cylinder_shape, result)
# Create a new entity as a child of the area entity with Random Noise Gradient, Gradient Transform Modifier,
# and Box Shape component
random_noise = hydra.Entity("random_noise")
random_noise.create_entity(position, ["Random Noise Gradient", "Gradient Transform Modifier", "Box Shape"])
random_noise.set_test_parent_entity(vegetation)
# Add a Vegetation Position Modifier to the vegetation area entity
vegetation.add_component("Vegetation Position Modifier")
# Pin the Random Noise entity to the Gradient Entity Id field of the Position Modifier's Gradient X
vegetation.get_set_test(4, "Configuration|Position X|Gradient|Gradient Entity Id", random_noise.id)
# Toggle between PreProcess and PostProcess
vegetation.get_set_test(3, "Configuration|Filter Stage", 1)
result = helper.wait_for_condition(lambda: dynveg.validate_instance_count(position, 5.0, 117), 2.0)
Report.result(Tests.preprocess_instance_count, result)
vegetation.get_set_test(3, "Configuration|Filter Stage", 2)
result = helper.wait_for_condition(lambda: dynveg.validate_instance_count(position, 5.0, 122), 2.0)
Report.result(Tests.postprocess_instance_count, result)
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(ShapeIntersectionFilter_FilterStageToggle)
@@ -5,124 +5,126 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
"""
C4874094: Shape reference can be replaced/removed
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.editor as editor
import azlmbr.legacy.general as general
import azlmbr.bus as bus
import azlmbr.math as math
import azlmbr.paths
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.editor_test_helper import EditorTestHelper
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
class Tests:
instance_count_in_box_shape = (
"Only found instances in the configured Box Shape intersection area",
"Found instances outside of the configured Box Shape intersection area"
)
instance_count_in_cylinder_shape = (
"Only found instances in the configured Cylinder Shape intersection area",
"Found instances outside of the configured Cylinder Shape intersection area"
)
unfiltered_instance_count = (
"Found instances in the entire Spawner area with no filter set",
"Failed to find all expected instances in the Spawner area with no filter set"
)
class TestShapeIntersectionFilter(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="ShapeIntersectionFilter_InstancePlanting", args=["level"])
def ShapeIntersectionFilter_InstancesPlantInAssignedShape():
"""
Summary:
A spawner area is created with a Vegetation Shape Intersection Filter. 2 different shape entities are created,
pinned to the Shape Intersection Filter, and instance counts are verified.
def run_test(self):
"""
Summary:
A spawner area is created with a Vegetation Shape Intersection Filter. 2 different shape entities are created,
pinned to the Shape Intersection Filter, and instance counts are verified.
Expected Behavior:
The Shape Entity Id reference can be successfully set/updated. Instances spawn only in the specified shape area.
Expected Behavior:
The Shape Entity Id reference can be successfully set/updated. Instances spawn only in the specified shape area.
Test Steps:
1) Open an existing level, and set view for visual debugging
2) Create an instance spawner and planting surface
3) Create child entity with Box Shape
4) Create child entity with Cylinder Shape
5) Assign the Intersection Filter to the Box Shape and validate instance counts
6) Assign the Intersection Filter to the Cylinder Shape and validate instance counts
7) Remove the shape reference on the Intersection Filter and validate instance counts
Test Steps:
1) Create a new level, and set view for visual debugging
2) Create an instance spawner and planting surface
3) Create child entity with Box Shape
4) Create child entity with Cylinder Shape
5) Assign the Intersection Filter to the Box Shape and validate instance counts
6) Assign the Intersection Filter to the Cylinder Shape and validate instance counts
7) Remove the shape reference on the Intersection Filter and validate instance counts
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
:return: None
"""
:return: None
"""
# 1) Create a new, temporary level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=False,
)
import os
# Set view of planting area for visual debugging
general.set_current_view_position(512.0, 500.0, 38.0)
general.set_current_view_rotation(-20.0, 0.0, 0.0)
import azlmbr.editor as editor
import azlmbr.legacy.general as general
import azlmbr.bus as bus
import azlmbr.math as math
# 2) Create a new entity with required vegetation area components and Vegetation Shape Intersection Filter
center_point = math.Vector3(512.0, 512.0, 32.0)
asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
spawner_entity = dynveg.create_vegetation_area("Instance Spawner", center_point, 16.0, 16.0, 1.0,
asset_path)
spawner_entity.add_component("Vegetation Shape Intersection Filter")
import editor_python_test_tools.hydra_editor_utils as hydra
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
# Create a planting surface
dynveg.create_surface_entity("Planting Surface", center_point, 32.0, 32.0, 1.0)
# 1) Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
# 3) Create a child entity with Box Shape
components_to_add = ["Box Shape"]
box_id = editor.ToolsApplicationRequestBus(bus.Broadcast, "CreateNewEntity", spawner_entity.id)
box = hydra.Entity("Box", box_id)
box.components = []
for component in components_to_add:
box.components.append(hydra.add_component(component, box_id))
new_box_dimension = math.Vector3(5.0, 5.0, 5.0)
hydra.get_set_test(box, 0, "Box Shape|Box Configuration|Dimensions", new_box_dimension)
# Set view of planting area for visual debugging
general.set_current_view_position(512.0, 500.0, 38.0)
general.set_current_view_rotation(-20.0, 0.0, 0.0)
# 4) Create a child entity with Cylinder Shape
components_to_add = ["Cylinder Shape"]
cylinder_id = editor.ToolsApplicationRequestBus(bus.Broadcast, "CreateNewEntity", spawner_entity.id)
cylinder = hydra.Entity("Cylinder", cylinder_id)
cylinder.components = []
for component in components_to_add:
cylinder.components.append(hydra.add_component(component, cylinder_id))
hydra.get_set_test(cylinder, 0, "Cylinder Shape|Cylinder Configuration|Radius", 5.0)
# 2) Create a new entity with required vegetation area components and Vegetation Shape Intersection Filter
center_point = math.Vector3(512.0, 512.0, 32.0)
asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
spawner_entity = dynveg.create_vegetation_area("Instance Spawner", center_point, 16.0, 16.0, 1.0,
asset_path)
spawner_entity.add_component("Vegetation Shape Intersection Filter")
# 5) Set the Intersection Filter's Shape Entity Id to the Box Shape entity
spawner_entity.get_set_test(3, "Configuration|Shape Entity Id", box_id)
# Create a planting surface
dynveg.create_surface_entity("Planting Surface", center_point, 32.0, 32.0, 1.0)
# Validate instance counts. Instances should only plant in the Box Shape area
num_expected = 49
success = self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id,
num_expected), 5.0)
self.test_success = success and self.test_success
# 6) Set the Intersection Filter's Shape Entity Id to the Cylinder Shape entity
spawner_entity.get_set_test(3, "Configuration|Shape Entity Id", cylinder_id)
# 3) Create a child entity with Box Shape
components_to_add = ["Box Shape"]
box_id = editor.ToolsApplicationRequestBus(bus.Broadcast, "CreateNewEntity", spawner_entity.id)
box = hydra.Entity("Box", box_id)
box.components = []
for component in components_to_add:
box.components.append(hydra.add_component(component, box_id))
new_box_dimension = math.Vector3(5.0, 5.0, 5.0)
hydra.get_set_test(box, 0, "Box Shape|Box Configuration|Dimensions", new_box_dimension)
# Validate instance counts. Instances should only plant in the Cylinder Shape area
num_expected = 121
success = self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id,
num_expected), 5.0)
self.test_success = success and self.test_success
# 4) Create a child entity with Cylinder Shape
components_to_add = ["Cylinder Shape"]
cylinder_id = editor.ToolsApplicationRequestBus(bus.Broadcast, "CreateNewEntity", spawner_entity.id)
cylinder = hydra.Entity("Cylinder", cylinder_id)
cylinder.components = []
for component in components_to_add:
cylinder.components.append(hydra.add_component(component, cylinder_id))
hydra.get_set_test(cylinder, 0, "Cylinder Shape|Cylinder Configuration|Radius", 5.0)
# 7) Clear the Intersection Filter's Shape Entity Id reference
spawner_entity.get_set_test(3, "Configuration|Shape Entity Id", None)
# 5) Set the Intersection Filter's Shape Entity Id to the Box Shape entity
spawner_entity.get_set_test(3, "Configuration|Shape Entity Id", box_id)
# Validate instance counts. Instances should now fill the entire spawner_entity's area
num_expected = 20 * 20
success = self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id,
num_expected), 5.0)
self.test_success = success and self.test_success
# Validate instance counts. Instances should only plant in the Box Shape area
num_expected = 49
success = helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id,
num_expected), 5.0)
Report.result(Tests.instance_count_in_box_shape, success)
# 6) Set the Intersection Filter's Shape Entity Id to the Cylinder Shape entity
spawner_entity.get_set_test(3, "Configuration|Shape Entity Id", cylinder_id)
# Validate instance counts. Instances should only plant in the Cylinder Shape area
num_expected = 121
success = helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id,
num_expected), 5.0)
Report.result(Tests.instance_count_in_cylinder_shape, success)
# 7) Clear the Intersection Filter's Shape Entity Id reference
spawner_entity.get_set_test(3, "Configuration|Shape Entity Id", None)
# Validate instance counts. Instances should now fill the entire spawner_entity's area
num_expected = 20 * 20
success = helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id,
num_expected), 5.0)
Report.result(Tests.unfiltered_instance_count, success)
test = TestShapeIntersectionFilter()
test.run()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(ShapeIntersectionFilter_InstancesPlantInAssignedShape)
@@ -5,121 +5,132 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
from math import radians
import sys
import azlmbr.areasystem as areasystem
import azlmbr.asset as asset
import azlmbr.bus as bus
import azlmbr.components as components
import azlmbr.legacy.general as general
import azlmbr.math as math
import azlmbr.paths
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.editor_test_helper import EditorTestHelper
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
class Tests:
surface_entity_created = (
"Successfully created Surface entity",
"Failed to create Surface entity"
)
instance_count = (
"Found the expected number of instances",
"Unexpected number of instances found"
)
instances_aligned_0 = (
"All instances are pointed straight up",
"Found instances not aligned to surface pointing straight up"
)
instances_aligned_1 = (
"All instances are planted perpendicularly to the surface",
"Found instances not aligned to surface perpendicularly"
)
class TestSlopeAlignmentModifierOverrides(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="SlopeAlignmentModifierOverrides", args=["level"])
def SlopeAlignmentModifierOverrides_InstanceSurfaceAlignment():
"""
Summary:
Verifies instances properly align to surfaces based on configuration of descriptor overrides of the
Vegetation Slope Alignment Modifier component.
def run_test(self):
"""
Summary:
C4814459 Verifies instances properly align to surfaces based on configuration of descriptor overrides of the
Vegetation Slope Alignment Modifier component.
:return: None
"""
:return: None
"""
import os
from math import radians
def verify_proper_alignment(instance, rot_degrees_vec):
expected_rotation = math.Quaternion()
expected_rotation.SetFromEulerDegrees(rot_degrees_vec)
if instance.alignment.IsClose(expected_rotation):
return True
self.log(f"Expected rotation of {expected_rotation}, Found {instance.alignment}")
return False
import azlmbr.areasystem as areasystem
import azlmbr.asset as asset
import azlmbr.bus as bus
import azlmbr.components as components
import azlmbr.legacy.general as general
import azlmbr.math as math
# Create empty level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=False,
)
import editor_python_test_tools.hydra_editor_utils as hydra
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
general.set_current_view_position(512.0, 480.0, 38.0)
def verify_proper_alignment(instance, rot_degrees_vec):
expected_rotation = math.Quaternion()
expected_rotation.SetFromEulerDegrees(rot_degrees_vec)
if instance.alignment.IsClose(expected_rotation):
return True
Report.info(f"Expected rotation of {expected_rotation}, Found {instance.alignment}")
return False
# Create a spawner entity setup with all needed components
center_point = math.Vector3(512.0, 512.0, 32.0)
asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
spawner_entity = dynveg.create_vegetation_area("Instance Spawner", center_point, 16.0, 16.0, 32.0, asset_path)
# Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
# Create a sloped mesh surface for the instances to plant on
center_point = math.Vector3(502.0, 512.0, 24.0)
mesh_asset_path = os.path.join("objects", "_primitives", "_box_1x1.azmodel")
mesh_asset = asset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", mesh_asset_path, math.Uuid(),
False)
rotation = math.Vector3(0.0, radians(45.0), 0.0)
surface_entity = hydra.Entity("Surface Entity")
surface_entity.create_entity(
center_point,
["Mesh", "Mesh Surface Tag Emitter"]
)
if surface_entity.id.IsValid():
print(f"'{surface_entity.name}' created")
hydra.get_set_test(surface_entity, 0, "Controller|Configuration|Mesh Asset", mesh_asset)
components.TransformBus(bus.Event, "SetLocalRotation", surface_entity.id, rotation)
components.TransformBus(bus.Event, "SetLocalUniformScale", surface_entity.id, 30.0)
general.set_current_view_position(512.0, 480.0, 38.0)
# Add a Vegetation Debugger component to allow refreshing instances
hydra.add_level_component("Vegetation Debugger")
# Create a spawner entity setup with all needed components
center_point = math.Vector3(512.0, 512.0, 32.0)
asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
spawner_entity = dynveg.create_vegetation_area("Instance Spawner", center_point, 16.0, 16.0, 32.0, asset_path)
# Add Vegetation Slope Alignment Modifier to the spawner entity and toggle on Allow Per-Item Overrides
spawner_entity.add_component("Vegetation Slope Alignment Modifier")
spawner_entity.get_set_test(3, "Configuration|Allow Per-Item Overrides", True)
# Create a sloped mesh surface for the instances to plant on
center_point = math.Vector3(502.0, 512.0, 24.0)
mesh_asset_path = os.path.join("objects", "_primitives", "_box_1x1.azmodel")
mesh_asset = asset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", mesh_asset_path, math.Uuid(),
False)
rotation = math.Vector3(0.0, radians(45.0), 0.0)
surface_entity = hydra.Entity("Surface Entity")
surface_entity.create_entity(
center_point,
["Mesh", "Mesh Surface Tag Emitter"]
)
Report.critical_result(Tests.surface_entity_created, surface_entity.id.IsValid())
hydra.get_set_test(surface_entity, 0, "Controller|Configuration|Mesh Asset", mesh_asset)
components.TransformBus(bus.Event, "SetLocalRotation", surface_entity.id, rotation)
components.TransformBus(bus.Event, "SetLocalUniformScale", surface_entity.id, 30.0)
# Toggle on Surface Slope Alignment Override Enabled on the Vegetation Asset List component
spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Surface Slope Alignment|Override Enabled",
True)
# Add a Vegetation Debugger component to allow refreshing instances
hydra.add_level_component("Vegetation Debugger")
# Set Surface Slope Alignment Override Min and Max to 0 and validate instance alignment
spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Surface Slope Alignment|Max", 0.0)
spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Surface Slope Alignment|Max", 0.0)
# Add Vegetation Slope Alignment Modifier to the spawner entity and toggle on Allow Per-Item Overrides
spawner_entity.add_component("Vegetation Slope Alignment Modifier")
spawner_entity.get_set_test(3, "Configuration|Allow Per-Item Overrides", True)
# Verify instances are have planted and are aligned to slope as expected
num_expected = 20 * 20
self.test_success = self.test_success and self.wait_for_condition(
lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0)
# Toggle on Surface Slope Alignment Override Enabled on the Vegetation Asset List component
spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Surface Slope Alignment|Override Enabled",
True)
box = azlmbr.shape.ShapeComponentRequestsBus(bus.Event, 'GetEncompassingAabb', spawner_entity.id)
instances = areasystem.AreaSystemRequestBus(bus.Broadcast, 'GetInstancesInAabb', box)
# Set Surface Slope Alignment Override Min and Max to 0 and validate instance alignment
spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Surface Slope Alignment|Max", 0.0)
spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Surface Slope Alignment|Max", 0.0)
if self.test_success and num_expected == len(instances):
for instance in instances:
self.test_success = verify_proper_alignment(instance,
math.Vector3(0.0, 0.0, 0.0)) and self.test_success
# Verify instances are have planted and are aligned to slope as expected
num_expected = 20 * 20
instances_planted = helper.wait_for_condition(
lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0)
Report.critical_result(Tests.instance_count, instances_planted)
# Set Surface Slope Alignment Min and Max to 1 and validate instance alignment
spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Surface Slope Alignment|Min", 1.0)
spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Surface Slope Alignment|Max", 1.0)
general.run_console('veg_debugClearAllAreas')
self.test_success = self.test_success and self.wait_for_condition(
lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0)
box = azlmbr.shape.ShapeComponentRequestsBus(bus.Event, 'GetEncompassingAabb', spawner_entity.id)
instances = areasystem.AreaSystemRequestBus(bus.Broadcast, 'GetInstancesInAabb', box)
box = azlmbr.shape.ShapeComponentRequestsBus(bus.Event, 'GetEncompassingAabb', spawner_entity.id)
instances = areasystem.AreaSystemRequestBus(bus.Broadcast, 'GetInstancesInAabb', box)
success = True
for instance in instances:
success = verify_proper_alignment(instance, math.Vector3(0.0, 0.0, 0.0))
Report.result(Tests.instances_aligned_0, success)
if self.test_success and num_expected == len(instances):
for instance in instances:
self.test_success = verify_proper_alignment(instance, math.Vector3(0.0, 45.0, 0.0)) and \
self.test_success
# Set Surface Slope Alignment Min and Max to 1 and validate instance alignment
spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Surface Slope Alignment|Min", 1.0)
spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Surface Slope Alignment|Max", 1.0)
general.run_console('veg_debugClearAllAreas')
instances_planted = helper.wait_for_condition(
lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0)
Report.critical_result(Tests.instance_count, instances_planted)
box = azlmbr.shape.ShapeComponentRequestsBus(bus.Event, 'GetEncompassingAabb', spawner_entity.id)
instances = areasystem.AreaSystemRequestBus(bus.Broadcast, 'GetInstancesInAabb', box)
success = True
for instance in instances:
success = verify_proper_alignment(instance, math.Vector3(0.0, 45.0, 0.0))
Report.result(Tests.instances_aligned_1, success)
test = TestSlopeAlignmentModifierOverrides()
test.run()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(SlopeAlignmentModifierOverrides_InstanceSurfaceAlignment)
@@ -5,127 +5,139 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
from math import radians
import sys
import azlmbr.areasystem as areasystem
import azlmbr.asset as asset
import azlmbr.bus as bus
import azlmbr.components as components
import azlmbr.editor as editor
import azlmbr.legacy.general as general
import azlmbr.math as math
import azlmbr.paths
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.editor_test_helper import EditorTestHelper
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
class Tests:
surface_entity_created = (
"Successfully created Surface entity",
"Failed to create Surface entity"
)
instance_count = (
"Found the expected number of instances",
"Unexpected number of instances found"
)
instances_aligned_1 = (
"All instances are planted perpendicularly to the surface",
"Found instances not aligned to surface perpendicularly"
)
instances_aligned_0 = (
"All instances are pointed straight up",
"Found instances not aligned to surface pointing straight up"
)
class TestSlopeAlignmentModifier(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="SlopeAlignmentModifier", args=["level"])
def SlopeAlignmentModifier_InstanceSurfaceAlignment():
"""
Summary:
Verifies instances properly align to surfaces based on configuration of the Vegetation Slope Alignment
Modifier.
def run_test(self):
"""
Summary:
C4896941 Verifies instances properly align to surfaces based on configuration of the Vegetation Slope Alignment
Modifier.
:return: None
"""
:return: None
"""
import os
from math import radians
def verify_proper_alignment(instance, rot_degrees_vec):
expected_rotation = math.Quaternion()
expected_rotation.SetFromEulerDegrees(rot_degrees_vec)
if instance.alignment.IsClose(expected_rotation):
return True
self.log(f"Expected rotation of {expected_rotation}, Found {instance.alignment}")
return False
import azlmbr.areasystem as areasystem
import azlmbr.asset as asset
import azlmbr.bus as bus
import azlmbr.components as components
import azlmbr.editor as editor
import azlmbr.legacy.general as general
import azlmbr.math as math
# Create empty level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=False,
)
import editor_python_test_tools.hydra_editor_utils as hydra
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
general.set_current_view_position(512.0, 480.0, 38.0)
def verify_proper_alignment(instance, rot_degrees_vec):
expected_rotation = math.Quaternion()
expected_rotation.SetFromEulerDegrees(rot_degrees_vec)
if instance.alignment.IsClose(expected_rotation):
return True
Report.info(f"Expected rotation of {expected_rotation}, Found {instance.alignment}")
return False
# Create a spawner entity setup with all needed components
center_point = math.Vector3(512.0, 512.0, 32.0)
asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
spawner_entity = dynveg.create_vegetation_area("Instance Spawner", center_point, 16.0, 16.0, 32.0, asset_path)
# Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
# Create a sloped mesh surface for the instances to plant on
center_point = math.Vector3(502.0, 512.0, 24.0)
mesh_asset_path = os.path.join("objects", "_primitives", "_box_1x1.azmodel")
mesh_asset = asset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", mesh_asset_path, math.Uuid(),
False)
rotation = math.Vector3(0.0, radians(45.0), 0.0)
surface_entity = hydra.Entity("Surface Entity")
surface_entity.create_entity(
center_point,
["Mesh", "Mesh Surface Tag Emitter"]
)
if surface_entity.id.IsValid():
print(f"'{surface_entity.name}' created")
hydra.get_set_test(surface_entity, 0, "Controller|Configuration|Mesh Asset", mesh_asset)
components.TransformBus(bus.Event, "SetLocalRotation", surface_entity.id, rotation)
components.TransformBus(bus.Event, "SetLocalUniformScale", surface_entity.id, 30.0)
general.set_current_view_position(512.0, 480.0, 38.0)
# Add a Vegetation Debugger component to allow refreshing instances
hydra.add_level_component("Vegetation Debugger")
# Create a spawner entity setup with all needed components
center_point = math.Vector3(512.0, 512.0, 32.0)
asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
spawner_entity = dynveg.create_vegetation_area("Instance Spawner", center_point, 16.0, 16.0, 32.0, asset_path)
# Add Vegetation Slope Alignment Modifier to the spawner entity
spawner_entity.add_component("Vegetation Slope Alignment Modifier")
# Create a sloped mesh surface for the instances to plant on
center_point = math.Vector3(502.0, 512.0, 24.0)
mesh_asset_path = os.path.join("objects", "_primitives", "_box_1x1.azmodel")
mesh_asset = asset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", mesh_asset_path, math.Uuid(),
False)
rotation = math.Vector3(0.0, radians(45.0), 0.0)
surface_entity = hydra.Entity("Surface Entity")
surface_entity.create_entity(
center_point,
["Mesh", "Mesh Surface Tag Emitter"]
)
Report.critical_result(Tests.surface_entity_created, surface_entity.id.IsValid())
hydra.get_set_test(surface_entity, 0, "Controller|Configuration|Mesh Asset", mesh_asset)
components.TransformBus(bus.Event, "SetLocalRotation", surface_entity.id, rotation)
components.TransformBus(bus.Event, "SetLocalUniformScale", surface_entity.id, 30.0)
# Set Alignment Coefficient Min/Max to 1 on the Slope Alignment Modifier
spawner_entity.get_set_test(3, "Configuration|Alignment Coefficient Min", 1.0)
spawner_entity.get_set_test(3, "Configuration|Alignment Coefficient Max", 1.0)
# Add a Vegetation Debugger component to allow refreshing instances
hydra.add_level_component("Vegetation Debugger")
# Create new child entity with a Constant Gradient
child_vegetation_id = editor.ToolsApplicationRequestBus(bus.Broadcast, "CreateNewEntity", spawner_entity.id)
child_vegetation = hydra.Entity("Child Vegetation Entity", child_vegetation_id)
components_to_add = ["Constant Gradient"]
child_vegetation.components = []
for component in components_to_add:
child_vegetation.components.append(hydra.add_component(component, child_vegetation_id))
# Add Vegetation Slope Alignment Modifier to the spawner entity
spawner_entity.add_component("Vegetation Slope Alignment Modifier")
# Reference the Constant Gradient on the Slope Alignment Modifier component
spawner_entity.get_set_test(3, "Configuration|Gradient|Gradient Entity Id", child_vegetation_id)
# Set Alignment Coefficient Min/Max to 1 on the Slope Alignment Modifier
spawner_entity.get_set_test(3, "Configuration|Alignment Coefficient Min", 1.0)
spawner_entity.get_set_test(3, "Configuration|Alignment Coefficient Max", 1.0)
# Verify instances are have planted and are aligned to slope as expected
num_expected = 20 * 20
self.test_success = self.test_success and self.wait_for_condition(
lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0)
# Create new child entity with a Constant Gradient
child_vegetation_id = editor.ToolsApplicationRequestBus(bus.Broadcast, "CreateNewEntity", spawner_entity.id)
child_vegetation = hydra.Entity("Child Vegetation Entity", child_vegetation_id)
components_to_add = ["Constant Gradient"]
child_vegetation.components = []
for component in components_to_add:
child_vegetation.components.append(hydra.add_component(component, child_vegetation_id))
box = azlmbr.shape.ShapeComponentRequestsBus(bus.Event, 'GetEncompassingAabb', spawner_entity.id)
instances = areasystem.AreaSystemRequestBus(bus.Broadcast, 'GetInstancesInAabb', box)
# Reference the Constant Gradient on the Slope Alignment Modifier component
spawner_entity.get_set_test(3, "Configuration|Gradient|Gradient Entity Id", child_vegetation_id)
if self.test_success and num_expected == len(instances):
for instance in instances:
self.test_success = verify_proper_alignment(instance, math.Vector3(0.0, 45.0, 0.0)) and \
self.test_success
# Verify instances are have planted and are aligned to slope as expected
num_expected = 20 * 20
instances_planted = helper.wait_for_condition(
lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0)
Report.critical_result(Tests.instance_count, instances_planted)
# Change Min/Max to 0.0 and verify proper alignment
spawner_entity.get_set_test(3, "Configuration|Alignment Coefficient Min", 0.0)
spawner_entity.get_set_test(3, "Configuration|Alignment Coefficient Max", 0.0)
general.run_console('veg_debugClearAllAreas')
self.test_success = self.test_success and self.wait_for_condition(
lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0)
box = azlmbr.shape.ShapeComponentRequestsBus(bus.Event, 'GetEncompassingAabb', spawner_entity.id)
instances = areasystem.AreaSystemRequestBus(bus.Broadcast, 'GetInstancesInAabb', box)
box = azlmbr.shape.ShapeComponentRequestsBus(bus.Event, 'GetEncompassingAabb', spawner_entity.id)
instances = areasystem.AreaSystemRequestBus(bus.Broadcast, 'GetInstancesInAabb', box)
success = True
for instance in instances:
success = verify_proper_alignment(instance, math.Vector3(0.0, 45.0, 0.0))
Report.result(Tests.instances_aligned_1, success)
if self.test_success and num_expected == len(instances):
for instance in instances:
self.test_success = verify_proper_alignment(instance, math.Vector3(0.0, 0.0, 0.0)) and self.test_success
# Change Min/Max to 0.0 and verify proper alignment
spawner_entity.get_set_test(3, "Configuration|Alignment Coefficient Min", 0.0)
spawner_entity.get_set_test(3, "Configuration|Alignment Coefficient Max", 0.0)
general.run_console('veg_debugClearAllAreas')
instances_planted = helper.wait_for_condition(
lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0)
Report.critical_result(Tests.instance_count, instances_planted)
box = azlmbr.shape.ShapeComponentRequestsBus(bus.Event, 'GetEncompassingAabb', spawner_entity.id)
instances = areasystem.AreaSystemRequestBus(bus.Broadcast, 'GetInstancesInAabb', box)
success = True
for instance in instances:
success = verify_proper_alignment(instance, math.Vector3(0.0, 0.0, 0.0))
Report.result(Tests.instances_aligned_0, success)
test = TestSlopeAlignmentModifier()
test.run()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(SlopeAlignmentModifier_InstanceSurfaceAlignment)
@@ -5,121 +5,122 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
"""
C4874096 - Slope Min/Max properties can be set, and properly affect planted vegetation
C4814464 - Slope Filter overrides function as expected
"""
import os
import sys
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.legacy.general as general
import azlmbr.math as math
import azlmbr.paths
sys.path.append(os.path.join(azlmbr.paths.devroot, "AutomatedTesting", "Gem", "PythonTests"))
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.editor_test_helper import EditorTestHelper
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
class Tests:
prefilter_instance_count = (
"Found the expected number of instances before applying the Slope Filter",
"Found an unexpected number of instances before applying the Slope Filter"
)
postfilter_instance_count = (
"Found the expected number of instances after applying the Slope Filter",
"Found an unexpected number of instances after applying the Slope Filter"
)
postfilter_overrides_instance_count = (
"Found the expected number of instances after applying descriptor overrides to the Slope Filter",
"Found an unexpected number of instances after applying descriptor overrides to the Slope Filter"
)
class TestSlopeFilterComponentAndOverrides(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="SlopeFilter_InstancesPlantOnValidSlope", args=["level"])
def SlopeFilter_ComponentAndOverrides_InstancesPlantOnValidSlopes():
"""
Summary:
An existing level is opened. A spawner entity is added, along with a flat planting surface at 32 on Z, and sphere
mesh at 38 on Z to provide a sloped surface. A Slope Filter is added to the spawner entity, and Slope Min/Max
values are set. Instance counts are validated. The same test is then performed for Slope Filter overrides.
def run_test(self):
"""
Summary:
A new level is created. A spawner entity is added, along with a flat planting surface at 32 on Z, and sphere
mesh at 38 on Z to provide a sloped surface. A Slope Filter is added to the spawner entity, and Slope Min/Max
values are set. Instance counts are validated. The same test is then performed for Slope Filter overrides.
Expected Behavior:
Instances plant only on surfaces that fall between the Slope Filter Min/Max settings
Expected Behavior:
Instances plant only on surfaces that fall between the Slope Filter Min/Max settings
Test Steps:
1) Open an existing level
2) Create an instance spawner entity
3) Create surfaces to plant on, one at 32 on Z and another sloped surface at 38 on Z.
4) Initial instance counts pre-filter are verified.
5) Slope Min/Max values are set on the Slope Filter component
6) Instance counts are validated
7) Setup for overrides tests
8) Slope Min/Max values are set on the descriptor overrides
9) Instance counts are validated
Test Steps:
1) Create a new level
2) Create an instance spawner entity
3) Create surfaces to plant on, one at 32 on Z and another sloped surface at 38 on Z.
4) Initial instance counts pre-filter are verified.
5) Slope Min/Max values are set on the Slope Filter component
6) Instance counts are validated
7) Setup for overrides tests
8) Slope Min/Max values are set on the descriptor overrides
9) Instance counts are validated
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
:return: None
"""
:return: None
"""
import os
# 1) Create a new, temporary level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=False,
)
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.legacy.general as general
import azlmbr.math as math
# Set view of planting area for visual debugging
general.set_current_view_position(512.0, 475.0, 38.0)
import editor_python_test_tools.hydra_editor_utils as hydra
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
# 2) Create a new entity with required vegetation area components
center_point = math.Vector3(512.0, 512.0, 32.0)
asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
spawner_entity = dynveg.create_vegetation_area("Instance Spawner", center_point, 32.0, 32.0, 32.0, asset_path)
# 1) Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
# Add a Vegetation Slope Filter
spawner_entity.add_component("Vegetation Slope Filter")
# Set view of planting area for visual debugging
general.set_current_view_position(512.0, 475.0, 38.0)
# 3) Add surfaces to plant on. This will include a flat surface and a sphere mesh to provide a sloped surface
dynveg.create_surface_entity("Planting Surface", center_point, 32.0, 32.0, 1.0)
sloped_surface_center = math.Vector3(512.0, 512.0, 38.0)
dynveg.create_mesh_surface_entity_with_slopes("Sloped Planting Surface", sloped_surface_center, 10.0)
# 2) Create a new entity with required vegetation area components
center_point = math.Vector3(512.0, 512.0, 32.0)
asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
spawner_entity = dynveg.create_vegetation_area("Instance Spawner", center_point, 32.0, 32.0, 32.0, asset_path)
# Set instances to spawn on a center snap point to avoid unexpected instances around the edges of the box shape
veg_system_settings_component = hydra.add_level_component("Vegetation System Settings")
editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", veg_system_settings_component,
'Configuration|Area System Settings|Sector Point Snap Mode', 1)
# Add a Vegetation Slope Filter
spawner_entity.add_component("Vegetation Slope Filter")
# 4) Validate instance counts pre-filter
num_expected_flat_surface = 40 * 40 # 20x20 instances per 16m
num_expected_slopes_pre_filter = 120 # Unfiltered planting on the top of the sphere mesh
num_expected = num_expected_flat_surface + num_expected_slopes_pre_filter
initial_success = self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(
spawner_entity.id, num_expected), 5.0)
self.test_success = initial_success and self.test_success
# 3) Add surfaces to plant on. This will include a flat surface and a sphere mesh to provide a sloped surface
dynveg.create_surface_entity("Planting Surface", center_point, 32.0, 32.0, 1.0)
sloped_surface_center = math.Vector3(512.0, 512.0, 38.0)
dynveg.create_mesh_surface_entity_with_slopes("Sloped Planting Surface", sloped_surface_center, 10.0)
# 5) Change Slope Min/Max on the Vegetation Slope Filter component
spawner_entity.get_set_test(3, "Configuration|Slope Min", 20)
spawner_entity.get_set_test(3, "Configuration|Slope Max", 45)
# Set instances to spawn on a center snap point to avoid unexpected instances around the edges of the box shape
veg_system_settings_component = hydra.add_level_component("Vegetation System Settings")
editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", veg_system_settings_component,
'Configuration|Area System Settings|Sector Point Snap Mode', 1)
# 6) Validate instance counts post-filter: instances should only plant on slopes between 20-45 degrees
num_expected_slopes_post_filter = 48
slope_min_max_success = self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(
spawner_entity.id, num_expected_slopes_post_filter), 5.0)
self.test_success = slope_min_max_success and self.test_success
# 4) Validate instance counts pre-filter
num_expected_flat_surface = 40 * 40 # 20x20 instances per 16m
num_expected_slopes_pre_filter = 120 # Unfiltered planting on the top of the sphere mesh
num_expected = num_expected_flat_surface + num_expected_slopes_pre_filter
initial_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(
spawner_entity.id, num_expected), 5.0)
Report.result(Tests.prefilter_instance_count, initial_success)
# 7) Setup for overrides on the Slope Filter component and the spawner entity's descriptor
spawner_entity.get_set_test(3, "Configuration|Allow Per-Item Overrides", True)
spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Slope Filter|Override Enabled", True)
# 5) Change Slope Min/Max on the Vegetation Slope Filter component
spawner_entity.get_set_test(3, "Configuration|Slope Min", 20)
spawner_entity.get_set_test(3, "Configuration|Slope Max", 45)
# 8) Set Slope Filter Min/Max overrides on the spawner entity's descriptor
spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Slope Filter|Min", 5)
spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Slope Filter|Max", 20)
# 6) Validate instance counts post-filter: instances should only plant on slopes between 20-45 degrees
num_expected_slopes_post_filter = 48
slope_min_max_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(
spawner_entity.id, num_expected_slopes_post_filter), 5.0)
Report.result(Tests.postfilter_instance_count, slope_min_max_success)
# 9) Validate instance counts post-filter: instances should only plant on slopes between 5-20 degrees
num_expected_slopes_post_filter_overrides = 12
overrides_min_max_success = self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(
spawner_entity.id, num_expected_slopes_post_filter_overrides), 5.0)
self.test_success = overrides_min_max_success and self.test_success
# 7) Setup for overrides on the Slope Filter component and the spawner entity's descriptor
spawner_entity.get_set_test(3, "Configuration|Allow Per-Item Overrides", True)
spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Slope Filter|Override Enabled", True)
# 8) Set Slope Filter Min/Max overrides on the spawner entity's descriptor
spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Slope Filter|Min", 5)
spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Slope Filter|Max", 20)
# 9) Validate instance counts post-filter: instances should only plant on slopes between 5-20 degrees
num_expected_slopes_post_filter_overrides = 12
overrides_min_max_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(
spawner_entity.id, num_expected_slopes_post_filter_overrides), 5.0)
Report.result(Tests.postfilter_overrides_instance_count, overrides_min_max_success)
test = TestSlopeFilterComponentAndOverrides()
test.run()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(SlopeFilter_ComponentAndOverrides_InstancesPlantOnValidSlopes)
@@ -1,103 +0,0 @@
"""
Copyright (c) Contributors to the Open 3D Engine Project.
For complete copyright and license terms please see the LICENSE at the root of this distribution.
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import sys
import azlmbr.math as math
import azlmbr.bus as bus
import azlmbr.paths
import azlmbr.editor as editor
import azlmbr.entity as EntityId
import azlmbr.components as components
import azlmbr.legacy.general as general
sys.path.append(os.path.join(azlmbr.paths.devroot, "AutomatedTesting", "Gem", "PythonTests"))
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.editor_test_helper import EditorTestHelper
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
class TestSlopeFilterFilterStageToggle(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="SlopeFilter_FilterStageToggle", args=["level"])
def run_test(self):
"""
Summary:
Filter Stage toggle affects final vegetation position
Expected Result:
Vegetation instances plant differently depending on the Filter Stage setting. With PreProcess, some vegetation instances can
appear on slopes outside the filtered values. With PostProcess, vegetation instances only appear on the correct slope values.
:return: None
"""
# Create empty level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=False,
)
general.set_current_view_position(512.0, 480.0, 38.0)
# Create basic vegetation entity
position = math.Vector3(512.0, 512.0, 32.0)
asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
vegetation = dynveg.create_vegetation_area("vegetation", position, 16.0, 16.0, 16.0, asset_path)
# Create Surface for instances to plant on
dynveg.create_surface_entity("Surface_Entity_Parent", position, 16.0, 16.0, 1.0)
# Add a Vegetation Shape Intersection Filter to the vegetation area entity
vegetation.add_component("Vegetation Shape Intersection Filter")
# Create a new entity as a child of the vegetation area entity with Box Shape
box = hydra.Entity("box")
box.create_entity(position, ["Box Shape"])
box.get_set_test(0, "Box Shape|Box Configuration|Dimensions", math.Vector3(8.0, 8.0, 1.0))
# Create a new entity as a child of the vegetation area entity with Cylinder Shape.
cylinder = hydra.Entity("cylinder")
cylinder.create_entity(position, ["Cylinder Shape"])
cylinder.get_set_test(0, "Cylinder Shape|Cylinder Configuration|Radius", 5.0)
cylinder.get_set_test(0, "Cylinder Shape|Cylinder Configuration|Height", 5.0)
box.set_test_parent_entity(vegetation)
cylinder.set_test_parent_entity(vegetation)
# # On the Vegetation Shape Intersection Filter component, click the crosshair button, and add child entities one by one
vegetation.get_set_test(3, "Configuration|Shape Entity Id", box.id)
result = self.wait_for_condition(lambda: dynveg.validate_instance_count(position, 8.0, 100), 2.0)
self.log(f"Vegetation plant only in the areas where the Box overlaps with the vegetation area's boundaries: {result}")
vegetation.get_set_test(3, "Configuration|Shape Entity Id", cylinder.id)
result = self.wait_for_condition(lambda: dynveg.validate_instance_count(position, 5.0, 100), 2.0)
self.log(f"Vegetation plant only in the areas where the Cylinder overlaps with the vegetation area's boundaries: {result}")
# Create a new entity as a child of the vegetation area entity with Random Noise Gradient Generator, Gradient Transform Modifier,
# and Box Shape component
random_noise = hydra.Entity("random_noise")
random_noise.create_entity(position, ["Random Noise Gradient", "Gradient Transform Modifier", "Box Shape"])
random_noise.set_test_parent_entity(vegetation)
# Add a Vegetation Position Modifier to the vegetation area entity
vegetation.add_component("Vegetation Position Modifier")
# Pin the Random Noise entity to the Gradient Entity Id field of the Position Modifier's Gradient X
vegetation.get_set_test(4, "Configuration|Position X|Gradient|Gradient Entity Id", random_noise.id)
# Toggle between PreProcess and PostProcess
vegetation.get_set_test(3, "Configuration|Filter Stage", 1)
result = self.wait_for_condition(lambda: dynveg.validate_instance_count(position, 5.0, 117), 2.0)
self.log(f"Vegetation instances count equal to expected value for PREPROCESS filter stage: {result}")
vegetation.get_set_test(3, "Configuration|Filter Stage", 2)
result = self.wait_for_condition(lambda: dynveg.validate_instance_count(position, 5.0, 122), 2.0)
self.log(f"Vegetation instances count equal to expected value for POSTPROCESS filter stage: {result}")
test = TestSlopeFilterFilterStageToggle()
test.run()
@@ -0,0 +1,126 @@
"""
Copyright (c) Contributors to the Open 3D Engine Project.
For complete copyright and license terms please see the LICENSE at the root of this distribution.
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
class Tests:
spawner_slice_created = (
"Spawner slice created successfully",
"Failed to create Spawner slice"
)
instance_count_unhidden = (
"Initial instance counts are as expected",
"Found an unexpected number of initial instances"
)
instance_count_hidden = (
"Instance counts upon hiding the Spawner slice are as expected",
"Unexpectedly found instances with the Spawner slice hidden"
)
blender_slice_created = (
"Blender slice created successfully",
"Failed to create Blender slice"
)
def SpawnerSlices_SliceCreationAndVisibilityToggleWorks():
"""
Summary:
C2627900 Verifies if a slice containing the component can be created.
C2627905 A slice containing the Vegetation Layer Blender component can be created.
C2627904: Hiding a slice containing the component clears any visuals from the Viewport.
Expected Result:
C2627900, C2627905: Slice is created, and is properly processed in the Asset Processor.
C2627904: Vegetation area visuals are hidden from the Viewport.
:return: None
"""
import os
import azlmbr.math as math
import azlmbr.legacy.general as general
import azlmbr.slice as slice
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.asset as asset
import editor_python_test_tools.hydra_editor_utils as hydra
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
def path_is_valid_asset(asset_path):
asset_id = asset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", asset_path, math.Uuid(), False)
return asset_id.invoke("IsValid")
# 1) Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
general.set_current_view_position(512.0, 480.0, 38.0)
# 2) C2627900 Verifies if a slice containing the Vegetation Layer Spawner component can be created.
# 2.1) Create basic vegetation entity
position = math.Vector3(512.0, 512.0, 32.0)
asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
veg_1 = dynveg.create_vegetation_area("vegetation_1", position, 16.0, 16.0, 16.0, asset_path)
# 2.2) Create slice from the entity
slice_path = os.path.join("slices", "TestSlice_1.slice")
slice.SliceRequestBus(bus.Broadcast, "CreateNewSlice", veg_1.id, slice_path)
# 2.3) Verify if the slice has been created successfully
spawner_slice_success = helper.wait_for_condition(lambda: path_is_valid_asset(slice_path), 5.0)
Report.result(Tests.spawner_slice_created, spawner_slice_success)
# 3) C2627904: Hiding a slice containing the component clears any visuals from the Viewport
# 3.1) Create Surface for instances to plant on
dynveg.create_surface_entity("Surface_Entity", position, 16.0, 16.0, 1.0)
# 3.2) Initially verify instance count before hiding slice
initial_count_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count(position, 16.0, 400), 5.0)
Report.result(Tests.instance_count_unhidden, initial_count_success)
# 3.3) Hide the slice and verify instance count
editor.EditorEntityAPIBus(bus.Event, "SetVisibilityState", veg_1.id, False)
hidden_instance_count = helper.wait_for_condition(lambda: dynveg.validate_instance_count(position, 16.0, 0), 5.0)
Report.result(Tests.instance_count_hidden, hidden_instance_count)
# 3.4) Unhide the slice
editor.EditorEntityAPIBus(bus.Event, "SetVisibilityState", veg_1.id, True)
# 4) C2627905 A slice containing the Vegetation Layer Blender component can be created.
# 4.1) Create another vegetation entity to add to blender component
veg_2 = dynveg.create_vegetation_area("vegetation_2", position, 1.0, 1.0, 1.0, "")
# 4.2) Create entity with Vegetation Layer Blender
components_to_add = ["Box Shape", "Vegetation Layer Blender"]
blender_entity = hydra.Entity("blender_entity")
blender_entity.create_entity(position, components_to_add)
# 4.3) Pin both the vegetation areas to the blender entity
pte = hydra.get_property_tree(blender_entity.components[1])
path = "Configuration|Vegetation Areas"
pte.update_container_item(path, 0, veg_1.id)
pte.add_container_item(path, 1, veg_2.id)
# 4.4) Drag the simple vegetation areas under the Vegetation Layer Blender entity to create an entity hierarchy.
veg_1.set_test_parent_entity(blender_entity)
veg_2.set_test_parent_entity(blender_entity)
# 4.5) Create slice from blender entity
slice_path = os.path.join("slices", "TestSlice_2.slice")
slice.SliceRequestBus(bus.Broadcast, "CreateNewSlice", blender_entity.id, slice_path)
# 4.6) Verify if the slice has been created successfully
blender_slice_success = helper.wait_for_condition(lambda: path_is_valid_asset(slice_path), 5.0)
Report.result(Tests.blender_slice_created, blender_slice_success)
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(SpawnerSlices_SliceCreationAndVisibilityToggleWorks)
@@ -5,99 +5,97 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.legacy.general as general
import azlmbr.math as math
class Tests:
editor_remains_stable = (
"Editor did not crash following rapid surface data updates",
"Editor crashed"
)
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
from editor_python_test_tools.editor_test_helper import EditorTestHelper
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
def SurfaceDataRefreshes_RemainsStable():
"""
Summary:
The Vegetation Area System can intermittently crash when updating surface data and moving the camera
around rapidly. The situation occurs across multiple frames - the surface data updates, which triggers a bunch
of sector updates getting added to the update queue. Then in a subsequent frame, there is no active vegetation
area or surface data updates, which triggers "delete all sectors". The "delete all" wasn't deleting entries from
the update queue, so any unprocessed updates would continue to get processed. If any of those updates referenced
a sector that no longer exists, because the camera changed position, then it would assert and crash.
To repro this bug, this test loads an empty level with a large box shape emitting a surface, and then runs a tight
loop of camera movements and "surface changed" events that invalidate all surface points. Because this is a timing
issue, there's no guarantee that the test below will successfully cause the condition to occur, but it successfully
crashed every time it was tested locally prior to the bugfix.
:return: None
"""
import azlmbr.legacy.general as general
import azlmbr.math as math
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
# 1) Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
world_center = math.Vector3(512.0, 512.0, 32.0)
# Add an entity with a 1024 x 1024 box centered at 512,512.
surface_entity = dynveg.create_surface_entity("Surface Data", world_center, 1024.0, 1024.0, 1.0)
# Move the camera to the world center
general.set_current_view_position(world_center.x, world_center.y, world_center.z)
# 2) Perform the test. Since the conditions are extremely timing related, and every machine
# running the test can have different timing conditions, we run through a set of different
# combinations to try and cause the crash under as many scenarios as possible
loops_per_surface_changed = [3, 5, 5]
loops_per_camera_reset = [20, 20, 20]
camera_speed_per_loop = [10.0, 10.0, 15.0]
# Setting test success to false to make sure the toggle at the end accurately conveys the loop being successful
test_success = False
# Loop through all our attempted timing test cases to cause the crash pretty consistently.
for test_case in range(0,3):
Report.info(f'Starting test case {test_case}')
Report.info(f'Loops per surface changed: {loops_per_surface_changed[test_case]}')
Report.info(f'Loops per camera reset: {loops_per_camera_reset[test_case]}')
Report.info(f'Camera speed per loop: {camera_speed_per_loop[test_case]}')
for test_counter in range(0, 100):
# Every N loops, invalidate the entire set of surface data. It's mostly just important for this
# not to happen *every* iteration, since we need the vegetation system to bounce between having
# dirty surface points that cause sectors to be refreshed, and having no dirty surface points or
# active surface areas to trigger a "delete all sectors" condition.
if (test_counter % loops_per_surface_changed[test_case]) == 0:
azlmbr.surface_data.SurfaceDataSystemNotificationBus(azlmbr.bus.Broadcast,
'OnSurfaceChanged',
surface_entity.id,
azlmbr.math.Aabb(),
azlmbr.math.Aabb())
# Move the camera back and forth along the X axis at just the right speed to invalidate sectors that are
# queued for updating but haven't updated yet, so that when they try to update they crash.
x_pos = world_center.x + ((test_counter % loops_per_camera_reset[test_case]) * camera_speed_per_loop[test_case])
general.set_current_view_position(x_pos, world_center.y, world_center.z)
Report.info(f'{test_counter}: {x_pos}')
# Give a little processing time each iteration.
general.idle_wait(0.01)
# If we haven't crashed, then we've succeeded.
test_success = True
Report.result(Tests.editor_remains_stable, test_success)
class TestSurfaceDataRefreshes_RemainsStable(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="SurfaceDataRefreshes_RemainsStable", args=["level"])
if __name__ == "__main__":
def run_test(self):
"""
Summary:
The Vegetation Area System can intermittently crash when updating surface data and moving the camera
around rapidly. The situation occurs across multiple frames - the surface data updates, which triggers a bunch
of sector updates getting added to the update queue. Then in a subsequent frame, there is no active vegetation
area or surface data updates, which triggers "delete all sectors". The "delete all" wasn't deleting entries from
the update queue, so any unprocessed updates would continue to get processed. If any of those updates referenced
a sector that no longer exists, because the camera changed position, then it would assert and crash.
To repro this bug, this test creates an empty level with a large box shape emitting a surface, and then runs a tight
loop of camera movements and "surface changed" events that invalidate all surface points. Because this is a timing
issue, there's no guarantee that the test below will successfully cause the condition to occur, but it successfully
crashed every time it was tested locally prior to the bugfix.
:return: None
"""
# 1) Create a test level with the needed test setup
self.test_success = self.create_level(
self.get_arg('level'),
heightmap_resolution=128,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=128,
use_terrain=False)
world_center = math.Vector3(512.0, 512.0, 32.0)
# Add an entity with a 1024 x 1024 box centered at 512,512.
surface_entity = dynveg.create_surface_entity("Surface Data", world_center, 1024.0, 1024.0, 1.0)
# Move the camera to the world center
general.set_current_view_position(world_center.x, world_center.y, world_center.z)
# 2) Perform the test. Since the conditions are extremely timing related, and every machine
# running the test can have different timing conditions, we run through a set of different
# combinations to try and cause the crash under as many scenarios as possible
loops_per_surface_changed = [3, 5, 5]
loops_per_camera_reset = [20, 20, 20]
camera_speed_per_loop = [10.0, 10.0, 15.0]
# Setting test success to false to make sure the toggle at the end accurately conveys the loop being successful
self.test_success = False
# Loop through all our attempted timing test cases to cause the crash pretty consistently.
for test_case in range(0,3):
self.log(f'Starting test case {test_case}')
self.log(f'Loops per surface changed: {loops_per_surface_changed[test_case]}')
self.log(f'Loops per camera reset: {loops_per_camera_reset[test_case]}')
self.log(f'Camera speed per loop: {camera_speed_per_loop[test_case]}')
for test_counter in range (0,100):
# Every N loops, invalidate the entire set of surface data. It's mostly just important for this
# not to happen *every* iteration, since we need the vegetation system to bounce between having
# dirty surface points that cause sectors to be refreshed, and having no dirty surface points or
# active surface areas to trigger a "delete all sectors" condition.
if (test_counter % loops_per_surface_changed[test_case]) == 0:
azlmbr.surface_data.SurfaceDataSystemNotificationBus(azlmbr.bus.Broadcast,
'OnSurfaceChanged',
surface_entity.id,
azlmbr.math.Aabb(),
azlmbr.math.Aabb())
# Move the camera back and forth along the X axis at just the right speed to invalidate sectors that are
# queued for updating but haven't updated yet, so that when they try to update they crash.
x_pos = world_center.x + ((test_counter % loops_per_camera_reset[test_case]) * camera_speed_per_loop[test_case])
general.set_current_view_position(x_pos, world_center.y, world_center.z)
self.log(f'{test_counter}: {x_pos}')
# Give a little processing time each iteration.
general.idle_wait(0.01)
# If we haven't crashed, then we've succeeded.
self.test_success = True
test = TestSurfaceDataRefreshes_RemainsStable()
test.run()
from editor_python_test_tools.utils import Report
Report.start_test(SurfaceDataRefreshes_RemainsStable)
@@ -5,153 +5,160 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
"""
C3711666: Multiple Descriptors with different Surface Mask Filter overrides plant as expected.
"""
import os
import sys
import azlmbr.legacy.general as general
import azlmbr.math as math
import azlmbr.paths
import azlmbr.surface_data as surface_data
sys.path.append(os.path.join(azlmbr.paths.devroot, "AutomatedTesting", "Gem", "PythonTests"))
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.editor_test_helper import EditorTestHelper
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
class Tests:
initial_surface_validation = (
"Found all expected instances on all surfaces with initial setup",
"Found an unexpected number of instances on all surfaces with initial setup"
)
surface_a_validation = (
"Found the expected number of instances on Surface A",
"Found an unexpected number of instances on Surface A"
)
surface_b_validation = (
"Found the expected number of instances on Surface B",
"Found an unexpected number of instances on Surface B"
)
surface_c_validation = (
"Found the expected number of instances on Surface C",
"Found an unexpected number of instances on Surface C"
)
class TestSurfaceMaskFilterMultipleOverrides(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="SurfaceMaskFilter_MultipleDescriptorOverrides", args=["level"])
def SurfaceMaskFilterOverrides_MultipleDescriptorOverridesPlantAsExpected():
"""
Summary:
A new level is created. An instance spawner with 3 descriptors is created. 3 planting surfaces of different
sizes are created and different surface tags are applied to each. Descriptor surface mask filter overrides are
set and instance counts are validated.
def run_test(self):
"""
Summary:
A new level is created. An instance spawner with 3 descriptors is created. 3 planting surfaces of different
sizes are created and different surface tags are applied to each. Descriptor surface mask filter overrides are
set and instance counts are validated.
Expected Behavior:
Instances plant on surfaces based on surface mask filter overrides.
Expected Behavior:
Instances plant on surfaces based on surface mask filter overrides.
Test Steps:
1) Open an existing level
2) An instance spawner with 3 descriptors is created, and a Surface Mask Filter is added to the entity
3) 3 surfaces of different sizes are created, and set to emit different tags
4) Pre-test validation of instances
5) Test 1 setup and validation: Inclusion tag matching surface a is set on a single descriptor
6) Test 2 setup and validation: Inclusion tag matching surface b is set on a single descriptor
7) Test 3 setup and validation: Inclusion tag matching surface c is set on a single descriptor
Test Steps:
1) A new level is created
2) An instance spawner with 3 descriptors is created, and a Surface Mask Filter is added to the entity
3) 3 surfaces of different sizes are created, and set to emit different tags
4) Pre-test validation of instances
5) Test 1 setup and validation: Inclusion tag matching surface a is set on a single descriptor
6) Test 2 setup and validation: Inclusion tag matching surface b is set on a single descriptor
7) Test 3 setup and validation: Inclusion tag matching surface c is set on a single descriptor
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
:return: None
"""
:return: None
"""
surface_tag_list = [surface_data.SurfaceTag("test_tag"), surface_data.SurfaceTag("test_tag2"),
surface_data.SurfaceTag("test_tag3")]
import os
# 1) Create a new, temporary level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=False,
)
import azlmbr.legacy.general as general
import azlmbr.math as math
import azlmbr.surface_data as surface_data
# Set view of planting area for visual debugging
general.set_current_view_position(512.0, 500.0, 38.0)
general.set_current_view_rotation(-20.0, 0.0, 0.0)
import editor_python_test_tools.hydra_editor_utils as hydra
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
# 2) Create a new instance spawner entity with multiple Dynamic Slice Instance Spawner descriptors
spawner_center_point = math.Vector3(512.0, 512.0, 32.0)
asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
spawner_entity = dynveg.create_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 16.0,
asset_path)
asset_list_component = spawner_entity.components[2]
desc_asset = hydra.get_component_property_value(asset_list_component,
"Configuration|Embedded Assets")[0]
desc_list = [desc_asset, desc_asset, desc_asset]
spawner_entity.get_set_test(2, "Configuration|Embedded Assets", desc_list)
# Add a Surface Mask Filter component to the spawner entity and toggle on Allow Overrides
spawner_entity.add_component("Vegetation Surface Mask Filter")
spawner_entity.get_set_test(3, "Configuration|Allow Per-Item Overrides", True)
surface_tag_list = [surface_data.SurfaceTag("test_tag"), surface_data.SurfaceTag("test_tag2"),
surface_data.SurfaceTag("test_tag3")]
# 3) Create 3 surfaces for planting, spaced out vertically, and set expected instance counts for each surface
surface_entity_a = dynveg.create_surface_entity("Surface Entity A", math.Vector3(512.0, 512.0, 32.0),
16.0, 16.0, 1.0)
num_expected_surface_a = 20 * 20 # 20x20 instances on a 16x16 meter surface
surface_entity_b = dynveg.create_surface_entity("Surface Entity B", math.Vector3(512.0, 512.0, 35.0),
12.0, 12.0, 1.0)
num_expected_surface_b = 15 * 15 # 15x15 instances on a 12x12 meter surface
surface_entity_c = dynveg.create_surface_entity("Surface Entity C", math.Vector3(512.0, 512.0, 38.0),
8.0, 8.0, 1.0)
num_expected_surface_c = 10 * 10 # 10x10 instances on a 8x8 meter surface
# 1) Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
# Set each surface to emit a different tag
surface_entity_a.get_set_test(1, "Configuration|Generated Tags", [surface_tag_list[0]])
surface_entity_b.get_set_test(1, "Configuration|Generated Tags", [surface_tag_list[1]])
surface_entity_c.get_set_test(1, "Configuration|Generated Tags", [surface_tag_list[2]])
# Set view of planting area for visual debugging
general.set_current_view_position(512.0, 500.0, 38.0)
general.set_current_view_rotation(-20.0, 0.0, 0.0)
# 4) Initial Validation: Validate instance count in the spawner area. Instances should plant on all surfaces
num_expected = num_expected_surface_a + num_expected_surface_b + num_expected_surface_c
initial_success = self.wait_for_condition(
lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0)
self.test_success = initial_success and self.test_success
# 2) Create a new instance spawner entity with multiple Dynamic Slice Instance Spawner descriptors
spawner_center_point = math.Vector3(512.0, 512.0, 32.0)
asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
spawner_entity = dynveg.create_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 16.0,
asset_path)
asset_list_component = spawner_entity.components[2]
desc_asset = hydra.get_component_property_value(asset_list_component,
"Configuration|Embedded Assets")[0]
desc_list = [desc_asset, desc_asset, desc_asset]
spawner_entity.get_set_test(2, "Configuration|Embedded Assets", desc_list)
# 5)
# Test #1 Setup: Set test_tag to inclusion list for descriptor 1. Set other descriptors to exclude all surfaces
# Add a Surface Mask Filter component to the spawner entity and toggle on Allow Overrides
spawner_entity.add_component("Vegetation Surface Mask Filter")
spawner_entity.get_set_test(3, "Configuration|Allow Per-Item Overrides", True)
# Toggle on Display Per-Item Overrides and Surface Mask Filter Override for each descriptor
for index in range(3):
spawner_entity.get_set_test(2, f"Configuration|Embedded Assets|[{index}]|Display Per-Item Overrides", True)
spawner_entity.get_set_test(2,
f"Configuration|Embedded Assets|[{index}]|Surface Mask Filter|Override Mode", 1)
# 3) Create 3 surfaces for planting, spaced out vertically, and set expected instance counts for each surface
surface_entity_a = dynveg.create_surface_entity("Surface Entity A", math.Vector3(512.0, 512.0, 32.0),
16.0, 16.0, 1.0)
num_expected_surface_a = 20 * 20 # 20x20 instances on a 16x16 meter surface
surface_entity_b = dynveg.create_surface_entity("Surface Entity B", math.Vector3(512.0, 512.0, 35.0),
12.0, 12.0, 1.0)
num_expected_surface_b = 15 * 15 # 15x15 instances on a 12x12 meter surface
surface_entity_c = dynveg.create_surface_entity("Surface Entity C", math.Vector3(512.0, 512.0, 38.0),
8.0, 8.0, 1.0)
num_expected_surface_c = 10 * 10 # 10x10 instances on a 8x8 meter surface
# Set each surface to emit a different tag
surface_entity_a.get_set_test(1, "Configuration|Generated Tags", [surface_tag_list[0]])
surface_entity_b.get_set_test(1, "Configuration|Generated Tags", [surface_tag_list[1]])
surface_entity_c.get_set_test(1, "Configuration|Generated Tags", [surface_tag_list[2]])
# 4) Initial Validation: Validate instance count in the spawner area. Instances should plant on all surfaces
num_expected = num_expected_surface_a + num_expected_surface_b + num_expected_surface_c
initial_success = helper.wait_for_condition(
lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0)
Report.result(Tests.initial_surface_validation, initial_success)
# 5)
# Test #1 Setup: Set test_tag to inclusion list for descriptor 1. Set other descriptors to exclude all surfaces
# Toggle on Display Per-Item Overrides and Surface Mask Filter Override for each descriptor
for index in range(3):
spawner_entity.get_set_test(2, f"Configuration|Embedded Assets|[{index}]|Display Per-Item Overrides", True)
spawner_entity.get_set_test(2,
"Configuration|Embedded Assets|[0]|Surface Mask Filter|Inclusion Tags",
[surface_tag_list[0]])
spawner_entity.get_set_test(2,
"Configuration|Embedded Assets|[1]|Surface Mask Filter|Exclusion Tags",
surface_tag_list)
spawner_entity.get_set_test(2,
"Configuration|Embedded Assets|[2]|Surface Mask Filter|Exclusion Tags",
surface_tag_list)
f"Configuration|Embedded Assets|[{index}]|Surface Mask Filter|Override Mode", 1)
# Test #1 Validation: Validate instance count. Should only plant on a single surface for 400 instances
test_1_success = self.wait_for_condition(
lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected_surface_a), 5.0)
self.test_success = test_1_success and self.test_success
spawner_entity.get_set_test(2,
"Configuration|Embedded Assets|[0]|Surface Mask Filter|Inclusion Tags",
[surface_tag_list[0]])
spawner_entity.get_set_test(2,
"Configuration|Embedded Assets|[1]|Surface Mask Filter|Exclusion Tags",
surface_tag_list)
spawner_entity.get_set_test(2,
"Configuration|Embedded Assets|[2]|Surface Mask Filter|Exclusion Tags",
surface_tag_list)
# 6)
# Test #2 Setup: Set test_tag2 to inclusion for descriptor 1.
spawner_entity.get_set_test(2,
"Configuration|Embedded Assets|[0]|Surface Mask Filter|Inclusion Tags",
[surface_tag_list[1]])
# Test #1 Validation: Validate instance count. Should only plant on a single surface for 400 instances
test_1_success = helper.wait_for_condition(
lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected_surface_a), 5.0)
Report.result(Tests.surface_a_validation, test_1_success)
# Test #2 Validation: Validate instance count. Should only plant on a single surface for 225 instances
test_2_success = self.wait_for_condition(
lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected_surface_b), 5.0)
self.test_success = test_2_success and self.test_success
# 6)
# Test #2 Setup: Set test_tag2 to inclusion for descriptor 1.
spawner_entity.get_set_test(2,
"Configuration|Embedded Assets|[0]|Surface Mask Filter|Inclusion Tags",
[surface_tag_list[1]])
# 7)
# Test #3 Setup: Set test_tag3 to inclusion for descriptor 1.
spawner_entity.get_set_test(2,
"Configuration|Embedded Assets|[0]|Surface Mask Filter|Inclusion Tags",
[surface_tag_list[2]])
# Test #2 Validation: Validate instance count. Should only plant on a single surface for 225 instances
test_2_success = helper.wait_for_condition(
lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected_surface_b), 5.0)
Report.result(Tests.surface_b_validation, test_2_success)
# Test #3 Validation: Validate instance count. Should only plant on a single surface for 100 instances
test_3_success = self.wait_for_condition(
lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected_surface_c), 5.0)
self.test_success = test_3_success and self.test_success
# 7)
# Test #3 Setup: Set test_tag3 to inclusion for descriptor 1.
spawner_entity.get_set_test(2,
"Configuration|Embedded Assets|[0]|Surface Mask Filter|Inclusion Tags",
[surface_tag_list[2]])
# Test #3 Validation: Validate instance count. Should only plant on a single surface for 100 instances
test_3_success = helper.wait_for_condition(
lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected_surface_c), 5.0)
Report.result(Tests.surface_c_validation, test_3_success)
test = TestSurfaceMaskFilterMultipleOverrides()
test.run()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(SurfaceMaskFilterOverrides_MultipleDescriptorOverridesPlantAsExpected)
@@ -5,66 +5,61 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import sys
import azlmbr.surface_data as surface_data
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
from editor_python_test_tools.editor_test_helper import EditorTestHelper
class Tests:
tags_same_value_equal = (
"Two Surface Tags of the same value evaluated as equal",
"Two Surface Tags of the same value unexpectedly evaluated as unequal"
)
tags_different_value_unequal = (
"Two Surface Tags of different values evaluated as unequal",
"Two Surface Tags of different values unexpectedly evaluated as equal"
)
class TestSurfaceMaskFilter_BasicSurfaceTagCreation(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="TestSurfaceMaskFilter_BasicSurfaceTagCreation", args=["level"])
def run_test(self):
"""
Summary:
Verifies basic surface tag value equality
def SurfaceMaskFilter_BasicSurfaceTagCreation():
"""
Summary:
Verifies basic surface tag value equality
Expected Behavior:
Surface tags of the same name are equal, and different names aren't.
Expected Behavior:
Surface tags of the same name are equal, and different names aren't.
Test Steps:
1) Open level
2) Create 2 new surface tags of identical names and verify they resolve as equal.
3) Create another new tag of a different name and verify they resolve as different.
Test Steps:
1) Open level
2) Create 2 new surface tags of identical names and verify they resolve as equal.
3) Create another new tag of a different name and verify they resolve as different.
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
:return: None
"""
self.log("SurfaceTag test started")
# Create a level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=False,
)
tag1 = surface_data.SurfaceTag()
tag2 = surface_data.SurfaceTag()
# Test 1: Verify that two tags with the same value are equal
tag1.SetTag('equal_test')
tag2.SetTag('equal_test')
self.log("SurfaceTag equal tag comparison is {} expected True".format(tag1.Equal(tag2)))
self.test_success = self.test_success and tag1.Equal(tag2)
# Test 2: Verify that two tags with different values are not equal
tag2.SetTag('not_equal_test')
self.log("SurfaceTag not equal tag comparison is {} expected False".format(tag1.Equal(tag2)))
self.test_success = self.test_success and not tag1.Equal(tag2)
self.log("SurfaceTag test finished")
:return: None
"""
import azlmbr.surface_data as surface_data
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
# Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
tag1 = surface_data.SurfaceTag()
tag2 = surface_data.SurfaceTag()
# Test 1: Verify that two tags with the same value are equal
tag1.SetTag('equal_test')
tag2.SetTag('equal_test')
Report.result(Tests.tags_same_value_equal, tag1.Equal(tag2))
# Test 2: Verify that two tags with different values are not equal
tag2.SetTag('not_equal_test')
Report.result(Tests.tags_different_value_unequal, not tag1.Equal(tag2))
test = TestSurfaceMaskFilter_BasicSurfaceTagCreation()
test.run()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(SurfaceMaskFilter_BasicSurfaceTagCreation)
@@ -4,147 +4,141 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
"""
C2561342: Exclusive Surface Masks tags function
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.areasystem as areasystem
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.legacy.general as general
import azlmbr.math as math
import azlmbr.shape as shape
import azlmbr.surface_data as surface_data
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.editor_test_helper import EditorTestHelper
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
class Tests:
default_exclusion_weight = (
"Found the expected number of instances with Exclusion Weight set to defaults",
"Found an unexpected number of instances with Exclusion Weight set to defaults"
)
exclusion_weight_below_one = (
"Found the expected number of instances with Exclusion Weight set below 1",
"Found an unexpected number of instances with Exclusion Weight set below 1"
)
class TestExclusiveSurfaceMasksTag(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="SurfaceMaskFilter_ExclusionList", args=["level"])
def SurfaceMaskFilter_ExclusionList():
"""
Summary:
New level is created and set up with surface shapes with varying surface tags. A simple vegetation area has been
created and Vegetation Surface Mask Filter component is added to entity with terrain hole exclusion tag.
def run_test(self):
"""
Summary:
New level is created and set up with surface shapes with varying surface tags. A simple vegetation area has been
created and Vegetation Surface Mask Filter component is added to entity with terrain hole exclusion tag.
Expected Behavior:
With default Exclusion settings, vegetation does not plant over the terrain holes.
With Exclusion Weight Max below 1.0, vegetation plants over the terrain holes.
Expected Behavior:
With default Exclusion settings, vegetation does not plant over the terrain holes.
With Exclusion Weight Max below 1.0, vegetation plants over the terrain holes.
Test Steps:
1) Open an existing level
2) Create entity with components "Vegetation Layer Spawner", "Vegetation Asset List", "Box Shape"
3) Add a Vegetation Surface Mask Filter component to the entity.
4) Create 2 surface entities to represent terrain and terrain hole surfaces
5) Add an Exclusion List tag to the component, and set it to terrainHole.
6) Check spawn count with default Exclusion Weights
7) Check spawn count with Exclusion Weight Max set below 1.0
Test Steps:
1) Create a new level.
2) Create entity with components "Vegetation Layer Spawner", "Vegetation Asset List", "Box Shape"
3) Add a Vegetation Surface Mask Filter component to the entity.
4) Create 2 surface entities to represent terrain and terrain hole surfaces
5) Add an Exclusion List tag to the component, and set it to terrainHole.
6) Check spawn count with default Exclusion Weights
7) Check spawn count with Exclusion Weight Max set below 1.0
Note:
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
Note:
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
:return: None
"""
:return: None
"""
def update_surface_tag_exclusion_list(Entity, component_index, surface_tag):
tag_list = [surface_data.SurfaceTag()]
import os
# assign list with one surface tag to exclusion list
hydra.get_set_test(Entity, component_index, "Configuration|Exclusion|Surface Tags", tag_list)
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.legacy.general as general
import azlmbr.math as math
import azlmbr.surface_data as surface_data
# set that one surface tag element to required surface tag
component = Entity.components[component_index]
path = "Configuration|Exclusion|Surface Tags|[0]|Surface Tag"
editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", component, path, surface_tag)
new_value = hydra.get_component_property_value(component, path)
import editor_python_test_tools.hydra_editor_utils as hydra
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
if new_value == surface_tag:
self.log(f"Exclusive surface mask filter of {surface_tag} is added successfully")
else:
self.log(f"Failed to add an Exclusive surface mask filter of {surface_tag}")
def update_surface_tag_exclusion_list(Entity, component_index, surface_tag):
tag_list = [surface_data.SurfaceTag()]
def update_generated_surface_tag(Entity, component_index, surface_tag):
tag_list = [surface_data.SurfaceTag()]
# assign list with one surface tag to exclusion list
hydra.get_set_test(Entity, component_index, "Configuration|Exclusion|Surface Tags", tag_list)
# assign list with one surface tag to Generated Tags list
hydra.get_set_test(Entity, component_index, "Configuration|Generated Tags", tag_list)
# set that one surface tag element to required surface tag
component = Entity.components[component_index]
path = "Configuration|Exclusion|Surface Tags|[0]|Surface Tag"
editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", component, path, surface_tag)
new_value = hydra.get_component_property_value(component, path)
# set that one surface tag element to required surface tag
component = Entity.components[component_index]
path = "Configuration|Generated Tags|[0]|Surface Tag"
editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", component, path, surface_tag)
new_value = hydra.get_component_property_value(component, path)
if new_value == surface_tag:
Report.info(f"Exclusive surface mask filter of {surface_tag} is added successfully")
else:
Report.info(f"Failed to add an Exclusive surface mask filter of {surface_tag}")
if new_value == surface_tag:
self.log(f"Generated surface tag of {surface_tag} is added successfully")
else:
self.log(f"Failed to add Generated surface tag of {surface_tag}")
def update_generated_surface_tag(Entity, component_index, surface_tag):
tag_list = [surface_data.SurfaceTag()]
# 1) Create a new level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=False,
)
# assign list with one surface tag to Generated Tags list
hydra.get_set_test(Entity, component_index, "Configuration|Generated Tags", tag_list)
general.set_current_view_position(512.0, 480.0, 38.0)
# set that one surface tag element to required surface tag
component = Entity.components[component_index]
path = "Configuration|Generated Tags|[0]|Surface Tag"
editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", component, path, surface_tag)
new_value = hydra.get_component_property_value(component, path)
# 2) Create entity with components "Vegetation Layer Spawner", "Vegetation Asset List", "Box Shape"
entity_position = math.Vector3(512.0, 512.0, 32.0)
asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice")
spawner_entity = dynveg.create_vegetation_area("Instance Spawner",
entity_position,
10.0, 10.0, 10.0,
asset_path)
if new_value == surface_tag:
Report.info(f"Generated surface tag of {surface_tag} is added successfully")
else:
Report.info(f"Failed to add Generated surface tag of {surface_tag}")
# 3) Add a Vegetation Surface Mask Filter component to the entity.
spawner_entity.add_component("Vegetation Surface Mask Filter")
# 1) Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
# 4) Create 2 surface entities to represent terrain and terrain hole surfaces
surface_tags: dict = {"terrainHole": 1327698037, "terrain": 3363197873}
entity_position = math.Vector3(510.0, 512.0, 32.0)
surface_entity_1 = dynveg.create_surface_entity("Surface Entity 1",
entity_position,
10.0, 10.0, 1.0)
update_generated_surface_tag(surface_entity_1, 1, surface_tags["terrainHole"])
general.set_current_view_position(512.0, 480.0, 38.0)
entity_position = math.Vector3(520.0, 512.0, 32.0)
surface_entity_2 = dynveg.create_surface_entity("Surface Entity 2",
entity_position,
10.0, 10.0, 1.0)
update_generated_surface_tag(surface_entity_2, 1, surface_tags["terrain"])
# 2) Create entity with components "Vegetation Layer Spawner", "Vegetation Asset List", "Box Shape"
entity_position = math.Vector3(512.0, 512.0, 32.0)
asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice")
spawner_entity = dynveg.create_vegetation_area("Instance Spawner",
entity_position,
10.0, 10.0, 10.0,
asset_path)
# 5) Add an Exclusion List tag to the component, and set it to "terrainHole".
update_surface_tag_exclusion_list(spawner_entity, 3, surface_tags["terrainHole"])
# 3) Add a Vegetation Surface Mask Filter component to the entity.
spawner_entity.add_component("Vegetation Surface Mask Filter")
# 6) Check spawn count with default Exclusion Weights
general.idle_wait(2.0) # Allow a few seconds for instances to spawn
num_expected_instances = 39
box = shape.ShapeComponentRequestsBus(bus.Event, 'GetEncompassingAabb', spawner_entity.id)
num_found = areasystem.AreaSystemRequestBus(bus.Broadcast, 'GetInstanceCountInAabb', box)
self.log(f"Expected {num_expected_instances} instances - Found {num_found} instances")
self.test_success = self.test_success and num_found == num_expected_instances
# 4) Create 2 surface entities to represent terrain and terrain hole surfaces
surface_tags: dict = {"terrainHole": 1327698037, "terrain": 3363197873}
entity_position = math.Vector3(510.0, 512.0, 32.0)
surface_entity_1 = dynveg.create_surface_entity("Surface Entity 1",
entity_position,
10.0, 10.0, 1.0)
update_generated_surface_tag(surface_entity_1, 1, surface_tags["terrainHole"])
# 7) Check spawn count with Exclusion Weight Max set below 1.0
hydra.get_set_test(spawner_entity, 3, "Configuration|Exclusion|Weight Max", 0.9)
general.idle_wait(2.0) # Allow a few seconds for instances to spawn
num_expected_instances = 169
num_found = areasystem.AreaSystemRequestBus(bus.Broadcast, 'GetInstanceCountInAabb', box)
self.log(f"Expected {num_expected_instances} instances - Found {num_found} instances")
self.test_success = self.test_success and num_found == num_expected_instances
entity_position = math.Vector3(520.0, 512.0, 32.0)
surface_entity_2 = dynveg.create_surface_entity("Surface Entity 2",
entity_position,
10.0, 10.0, 1.0)
update_generated_surface_tag(surface_entity_2, 1, surface_tags["terrain"])
# 5) Add an Exclusion List tag to the component, and set it to "terrainHole".
update_surface_tag_exclusion_list(spawner_entity, 3, surface_tags["terrainHole"])
# 6) Check spawn count with default Exclusion Weights
num_expected_instances = 39
success = helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id,
num_expected_instances), 2.0)
Report.result(Tests.default_exclusion_weight, success)
# 7) Check spawn count with Exclusion Weight Max set below 1.0
hydra.get_set_test(spawner_entity, 3, "Configuration|Exclusion|Weight Max", 0.9)
num_expected_instances = 169
success = helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id,
num_expected_instances), 2.0)
Report.result(Tests.exclusion_weight_below_one, success)
test = TestExclusiveSurfaceMasksTag()
test.run()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(SurfaceMaskFilter_ExclusionList)
@@ -4,148 +4,142 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
"""
C2561341: Inclusive Surface Masks tags function
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.areasystem as areasystem
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.legacy.general as general
import azlmbr.math as math
import azlmbr.shape as shape
import azlmbr.surface_data as surface_data
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.editor_test_helper import EditorTestHelper
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
class Tests:
default_inclusion_weight = (
"Found the expected number of instances with Inclusion Weight set to defaults",
"Found an unexpected number of instances with Inclusion Weight set to defaults"
)
inclusion_weight_below_one = (
"Found the expected number of instances with Inclusion Weight set below 1",
"Found an unexpected number of instances with Inclusion Weight set below 1"
)
class TestInclusiveSurfaceMasksTag(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="SurfaceMaskFilter_InclusionList", args=["level"])
def run_test(self):
"""
Summary:
New level is created and set up with surface shapes with varying surface tags. A simple vegetation area has been
created and Vegetation Surface Mask Filter component is added to entity with terrain hole inclusion tag.
def SurfaceMaskFilter_InclusionList():
"""
Summary:
New level is created and set up with surface shapes with varying surface tags. A simple vegetation area has been
created and Vegetation Surface Mask Filter component is added to entity with terrain hole inclusion tag.
Expected Behavior:
With default Inclusion Weights, vegetation draws over the terrain holes.
With Inclusion Weight Max set below 1.0, vegetation stops drawing over the terrain holes.
Expected Behavior:
With default Inclusion Weights, vegetation draws over the terrain holes.
With Inclusion Weight Max set below 1.0, vegetation stops drawing over the terrain holes.
Test Steps:
1) Create a new level
2) Create entity with components "Vegetation Layer Spawner", "Vegetation Asset List", "Box Shape"
3) Add a Vegetation Surface Mask Filter component to the entity.
4) Create 2 surface entities to represent terrain and terrain hole surfaces
5) Add an Inclusion List tag to the component, and set it to "terrainHole".
6) Check spawn count with default Inclusion Weights
7) Check spawn count with Inclusion Weight Max set below 1.0
Note:
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
Test Steps:
1) Open an existing level
2) Create entity with components "Vegetation Layer Spawner", "Vegetation Asset List", "Box Shape"
3) Add a Vegetation Surface Mask Filter component to the entity.
4) Create 2 surface entities to represent terrain and terrain hole surfaces
5) Add an Inclusion List tag to the component, and set it to "terrainHole".
6) Check spawn count with default Inclusion Weights
7) Check spawn count with Inclusion Weight Max set below 1.0
:return: None
"""
Note:
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
def update_surface_tag_inclusion_list(Entity, component_index, surface_tag):
tag_list = [surface_data.SurfaceTag()]
:return: None
"""
# assign list with one surface tag to inclusion list
hydra.get_set_test(Entity, component_index, "Configuration|Inclusion|Surface Tags", tag_list)
import os
# set that one surface tag element to required surface tag
component = Entity.components[component_index]
path = "Configuration|Inclusion|Surface Tags|[0]|Surface Tag"
editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", component, path, surface_tag)
new_value = hydra.get_component_property_value(component, path)
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.legacy.general as general
import azlmbr.math as math
import azlmbr.surface_data as surface_data
if new_value == surface_tag:
print("Inclusive surface mask filter of terrainHole is added successfully")
else:
print("Failed to add an Inclusive surface mask filter of terrainHole")
general.idle_wait(2.0)
import editor_python_test_tools.hydra_editor_utils as hydra
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
def update_generated_surface_tag(Entity, component_index, surface_tag):
tag_list = [surface_data.SurfaceTag()]
def update_surface_tag_inclusion_list(Entity, component_index, surface_tag):
tag_list = [surface_data.SurfaceTag()]
# assign list with one surface tag to Generated Tags list
hydra.get_set_test(Entity, component_index, "Configuration|Generated Tags", tag_list)
# assign list with one surface tag to inclusion list
hydra.get_set_test(Entity, component_index, "Configuration|Inclusion|Surface Tags", tag_list)
# set that one surface tag element to required surface tag
component = Entity.components[component_index]
path = "Configuration|Generated Tags|[0]|Surface Tag"
editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", component, path, surface_tag)
new_value = hydra.get_component_property_value(component, path)
# set that one surface tag element to required surface tag
component = Entity.components[component_index]
path = "Configuration|Inclusion|Surface Tags|[0]|Surface Tag"
editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", component, path, surface_tag)
new_value = hydra.get_component_property_value(component, path)
if new_value == surface_tag:
self.log(f"Generated surface tag of {surface_tag} is added successfully")
else:
self.log(f"Failed to add Generated surface tag of {surface_tag}")
if new_value == surface_tag:
Report.info("Inclusive surface mask filter of terrainHole is added successfully")
else:
Report.info("Failed to add an Inclusive surface mask filter of terrainHole")
# 1) Create a new level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=False,
)
def update_generated_surface_tag(Entity, component_index, surface_tag):
tag_list = [surface_data.SurfaceTag()]
general.set_current_view_position(512.0, 480.0, 38.0)
# assign list with one surface tag to Generated Tags list
hydra.get_set_test(Entity, component_index, "Configuration|Generated Tags", tag_list)
# 2) Create entity with components "Vegetation Layer Spawner", "Vegetation Asset List", "Box Shape"
entity_position = math.Vector3(512.0, 512.0, 32.0)
asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice")
spawner_entity = dynveg.create_vegetation_area("Instance Spawner",
entity_position,
10.0, 10.0, 10.0,
asset_path)
# set that one surface tag element to required surface tag
component = Entity.components[component_index]
path = "Configuration|Generated Tags|[0]|Surface Tag"
editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", component, path, surface_tag)
new_value = hydra.get_component_property_value(component, path)
# 3) Add a Vegetation Surface Mask Filter component to the entity.
spawner_entity.add_component("Vegetation Surface Mask Filter")
if new_value == surface_tag:
Report.info(f"Generated surface tag of {surface_tag} is added successfully")
else:
Report.info(f"Failed to add Generated surface tag of {surface_tag}")
# 4) Create 2 surface entities to represent terrain and terrain hole surfaces
surface_tags: dict = {"terrainHole": 1327698037, "terrain": 3363197873}
entity_position = math.Vector3(510.0, 512.0, 32.0)
surface_entity_1 = dynveg.create_surface_entity("Surface Entity 1",
entity_position,
10.0, 10.0, 1.0)
update_generated_surface_tag(surface_entity_1, 1, surface_tags["terrainHole"])
# 1) Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
entity_position = math.Vector3(520.0, 512.0, 32.0)
surface_entity_2 = dynveg.create_surface_entity("Surface Entity 2",
entity_position,
10.0, 10.0, 1.0)
update_generated_surface_tag(surface_entity_2, 1, surface_tags["terrain"])
general.set_current_view_position(512.0, 480.0, 38.0)
# 5) Add an Inclusion List tag to the component, and set it to "terrainHole".
update_surface_tag_inclusion_list(spawner_entity, 3, surface_tags["terrainHole"])
# 2) Create entity with components "Vegetation Layer Spawner", "Vegetation Asset List", "Box Shape"
entity_position = math.Vector3(512.0, 512.0, 32.0)
asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice")
spawner_entity = dynveg.create_vegetation_area("Instance Spawner",
entity_position,
10.0, 10.0, 10.0,
asset_path)
# 6) Check spawn count with default Inclusion Weights
general.idle_wait(2.0) # Allow a few seconds for instances to spawn
num_expected_instances = 130
box = shape.ShapeComponentRequestsBus(bus.Event, 'GetEncompassingAabb', spawner_entity.id)
num_found = areasystem.AreaSystemRequestBus(bus.Broadcast, 'GetInstanceCountInAabb', box)
self.log(f"Expected {num_expected_instances} instances - Found {num_found} instances")
self.test_success = self.test_success and num_found == num_expected_instances
# 3) Add a Vegetation Surface Mask Filter component to the entity.
spawner_entity.add_component("Vegetation Surface Mask Filter")
# 7) Check spawn count with Inclusion Weight Max set below 1.0
hydra.get_set_test(spawner_entity, 3, "Configuration|Inclusion|Weight Max", 0.9)
general.idle_wait(2.0) # Allow a few seconds for instances to update
num_expected_instances = 0
num_found = areasystem.AreaSystemRequestBus(bus.Broadcast, 'GetInstanceCountInAabb', box)
self.log(f"Expected {num_expected_instances} instances - Found {num_found} instances")
self.test_success = self.test_success and num_found == num_expected_instances
# 4) Create 2 surface entities to represent terrain and terrain hole surfaces
surface_tags: dict = {"terrainHole": 1327698037, "terrain": 3363197873}
entity_position = math.Vector3(510.0, 512.0, 32.0)
surface_entity_1 = dynveg.create_surface_entity("Surface Entity 1",
entity_position,
10.0, 10.0, 1.0)
update_generated_surface_tag(surface_entity_1, 1, surface_tags["terrainHole"])
entity_position = math.Vector3(520.0, 512.0, 32.0)
surface_entity_2 = dynveg.create_surface_entity("Surface Entity 2",
entity_position,
10.0, 10.0, 1.0)
update_generated_surface_tag(surface_entity_2, 1, surface_tags["terrain"])
# 5) Add an Inclusion List tag to the component, and set it to "terrainHole".
update_surface_tag_inclusion_list(spawner_entity, 3, surface_tags["terrainHole"])
# 6) Check spawn count with default Inclusion Weights
num_expected_instances = 130
success = helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id,
num_expected_instances), 2.0)
Report.result(Tests.default_inclusion_weight, success)
# 7) Check spawn count with Inclusion Weight Max set below 1.0
hydra.get_set_test(spawner_entity, 3, "Configuration|Inclusion|Weight Max", 0.9)
num_expected_instances = 0
success = helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id,
num_expected_instances), 2.0)
Report.result(Tests.inclusion_weight_below_one, success)
test = TestInclusiveSurfaceMasksTag()
test.run()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(SurfaceMaskFilter_InclusionList)
@@ -5,89 +5,90 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import sys
import azlmbr.math as math
import azlmbr.paths
import azlmbr.editor as editor
import azlmbr.bus as bus
import azlmbr.legacy.general as general
sys.path.append(os.path.join(azlmbr.paths.devroot, "AutomatedTesting", "Gem", "PythonTests"))
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.editor_test_helper import EditorTestHelper
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
class Tests:
initial_density_instance_count = (
"Found the expected number of instances with default sector density",
"Found an unexpected number of instances with default sector density"
)
configured_density_instance_count = (
"Found the expected number of instances with a sector density of 10",
"Found an unexpected number of instances with a sector density of 10"
)
class TestSystemSettingsSectorPointDensity(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="SystemSettings_SectorPointDensity", args=["level"])
def SystemSettings_SectorPointDensity():
"""
Summary:
Sector Point Density increases/reduces the number of vegetation points within a sector
def run_test(self):
"""
Summary:
Sector Point Density increases/reduces the number of vegetation points within a sector
Expected Result:
Default value for Sector Point Density is 20.
20 vegetation meshes appear on each side of the established vegetation area with the default value.
When altered, the specified number of vegetation meshes along a side of a vegetation area matches the value set
in Sector Point Density.
Expected Result:
Default value for Sector Point Density is 20.
20 vegetation meshes appear on each side of the established vegetation area with the default value.
When altered, the specified number of vegetation meshes along a side of a vegetation area matches the value set
in Sector Point Density.
:return: None
"""
:return: None
"""
import os
INSTANCE_COUNT_BEFORE_DENSITY_CHANGE = 400
INSTANCE_COUNT_AFTER_DENSITY_CHANGE = 100
import azlmbr.math as math
import azlmbr.editor as editor
import azlmbr.bus as bus
import azlmbr.legacy.general as general
# Create empty level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=False,
)
import editor_python_test_tools.hydra_editor_utils as hydra
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
general.set_current_view_position(512.0, 480.0, 38.0)
INSTANCE_COUNT_BEFORE_DENSITY_CHANGE = 400
INSTANCE_COUNT_AFTER_DENSITY_CHANGE = 100
# Create basic vegetation entity
position = math.Vector3(512.0, 512.0, 32.0)
asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
dynveg.create_vegetation_area("vegetation", position, 16.0, 16.0, 1.0, asset_path)
dynveg.create_surface_entity("Surface_Entity", position, 16.0, 16.0, 1.0)
# Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
# Count the number of vegetation meshes along one side of the new vegetation area. #
result = self.wait_for_condition(
lambda: dynveg.validate_instance_count(position, 8.0, INSTANCE_COUNT_BEFORE_DENSITY_CHANGE), 2.0
)
self.log(f"Vegetation instances count equal to expected value before changing sector point density: {result}")
general.set_current_view_position(512.0, 480.0, 38.0)
# Add the Vegetation Debugger component to the Level Inspector
veg_system_settings_component = hydra.add_level_component("Vegetation System Settings")
# Create basic vegetation entity
position = math.Vector3(512.0, 512.0, 32.0)
asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
dynveg.create_vegetation_area("vegetation", position, 16.0, 16.0, 1.0, asset_path)
dynveg.create_surface_entity("Surface_Entity", position, 16.0, 16.0, 1.0)
# Change Sector Point Density to 10
editor.EditorComponentAPIBus(
bus.Broadcast,
"SetComponentProperty",
veg_system_settings_component,
"Configuration|Area System Settings|Sector Point Snap Mode",
1,
)
editor.EditorComponentAPIBus(
bus.Broadcast,
"SetComponentProperty",
veg_system_settings_component,
"Configuration|Area System Settings|Sector Point Density",
10,
)
# Count the number of vegetation instances in the vegetation area
result = helper.wait_for_condition(lambda: dynveg.validate_instance_count(position, 8.0,
INSTANCE_COUNT_BEFORE_DENSITY_CHANGE), 2.0)
Report.result(Tests.initial_density_instance_count, result)
# Count the number of vegetation meshes along one side of the new vegetation area.
result = self.wait_for_condition(
lambda: dynveg.validate_instance_count(position, 8.0, INSTANCE_COUNT_AFTER_DENSITY_CHANGE), 2.0
)
self.log(f"Vegetation instances count equal to expected value after changing sector point density: {result}")
# Add the Vegetation Debugger component to the Level Inspector
veg_system_settings_component = hydra.add_level_component("Vegetation System Settings")
# Change Sector Point Density to 10
editor.EditorComponentAPIBus(
bus.Broadcast,
"SetComponentProperty",
veg_system_settings_component,
"Configuration|Area System Settings|Sector Point Snap Mode",
1,
)
editor.EditorComponentAPIBus(
bus.Broadcast,
"SetComponentProperty",
veg_system_settings_component,
"Configuration|Area System Settings|Sector Point Density",
10,
)
# Count the number of vegetation instances in the vegetation area
result = helper.wait_for_condition(lambda: dynveg.validate_instance_count(position, 8.0,
INSTANCE_COUNT_AFTER_DENSITY_CHANGE), 2.0)
Report.result(Tests.configured_density_instance_count, result)
test = TestSystemSettingsSectorPointDensity()
test.run()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(SystemSettings_SectorPointDensity)
@@ -5,88 +5,91 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import sys
import azlmbr.math as math
import azlmbr.paths
import azlmbr.editor as editor
import azlmbr.bus as bus
import azlmbr.legacy.general as general
sys.path.append(os.path.join(azlmbr.paths.devroot, "AutomatedTesting", "Gem", "PythonTests"))
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.editor_test_helper import EditorTestHelper
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
class Tests:
initial_sector_size_instance_count = (
"Found the expected number of instances with default sector size",
"Found an unexpected number of instances with default sector size"
)
configured_sector_size_instance_count = (
"Found the expected number of instances with a sector size of 10",
"Found an unexpected number of instances with a sector size of 10"
)
class TestSystemSettingsSectorSize(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="SystemSettings_SectorSize", args=["level"])
def SystemSettings_SectorSize():
"""
Summary:
Sector Size In Meters increases/reduces the size of a sector
def run_test(self):
"""
Summary:
Sector Size In Meters increases/reduces the size of a sector
Expected Result:
The number of spawned vegetation meshes inside the vegetation area is identical after updating the Sector Size
Expected Result:
The number of spawned vegetation meshes inside the vegetation area is identical after updating the Sector Size
:return: None
"""
:return: None
"""
import os
VEGETATION_INSTANCE_COUNT = 400
import azlmbr.math as math
import azlmbr.editor as editor
import azlmbr.bus as bus
import azlmbr.legacy.general as general
# Create empty level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=False,
)
import editor_python_test_tools.hydra_editor_utils as hydra
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
general.set_current_view_position(512.0, 480.0, 38.0)
VEGETATION_INSTANCE_COUNT = 400
# Create basic vegetation entity
position = math.Vector3(512.0, 512.0, 32.0)
asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
vegetation = dynveg.create_vegetation_area("vegetation", position, 16.0, 16.0, 1.0, asset_path)
dynveg.create_surface_entity("Surface_Entity", position, 16.0, 16.0, 1.0)
# Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
# Add the Vegetation Debugger component to the Level Inspector
veg_system_settings_component = hydra.add_level_component("Vegetation System Settings")
general.set_current_view_position(512.0, 480.0, 38.0)
# Count the number of vegetation meshes along one side of the new vegetation area.
result = self.wait_for_condition(
lambda: dynveg.validate_instance_count(position, 8.0, VEGETATION_INSTANCE_COUNT), 2.0
)
self.log(f"Vegetation instances count equal to expected value before changing sector size: {result}")
# Create basic vegetation entity
position = math.Vector3(512.0, 512.0, 32.0)
asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
vegetation = dynveg.create_vegetation_area("vegetation", position, 16.0, 16.0, 1.0, asset_path)
dynveg.create_surface_entity("Surface_Entity", position, 16.0, 16.0, 1.0)
# Change Sector Size in Meters to 10.
editor.EditorComponentAPIBus(
bus.Broadcast,
"SetComponentProperty",
veg_system_settings_component,
"Configuration|Area System Settings|Sector Point Snap Mode",
1,
)
editor.EditorComponentAPIBus(
bus.Broadcast,
"SetComponentProperty",
veg_system_settings_component,
"Configuration|Area System Settings|Sector Size In Meters",
10,
)
# Add the Vegetation Debugger component to the Level Inspector
veg_system_settings_component = hydra.add_level_component("Vegetation System Settings")
# Alter the Box Shape to be 10,10,1
vegetation.get_set_test(1, "Box Shape|Box Configuration|Dimensions", math.Vector3(10.0, 10.0, 1.0))
# Count the number of vegetation instances in the vegetation area
result = helper.wait_for_condition(
lambda: dynveg.validate_instance_count(position, 8.0, VEGETATION_INSTANCE_COUNT), 2.0
)
Report.result(Tests.initial_sector_size_instance_count, result)
# Count the number of vegetation meshes along one side of the new vegetation area.
result = self.wait_for_condition(
lambda: dynveg.validate_instance_count(position, 5.0, VEGETATION_INSTANCE_COUNT), 2.0
)
self.log(f"Vegetation instances count equal to expected value after changing sector size: {result}")
# Change Sector Size in Meters to 10.
editor.EditorComponentAPIBus(
bus.Broadcast,
"SetComponentProperty",
veg_system_settings_component,
"Configuration|Area System Settings|Sector Point Snap Mode",
1,
)
editor.EditorComponentAPIBus(
bus.Broadcast,
"SetComponentProperty",
veg_system_settings_component,
"Configuration|Area System Settings|Sector Size In Meters",
10,
)
# Alter the Box Shape to be 10,10,1
vegetation.get_set_test(1, "Box Shape|Box Configuration|Dimensions", math.Vector3(10.0, 10.0, 1.0))
# Count the number of vegetation instances in the vegetation area
result = helper.wait_for_condition(
lambda: dynveg.validate_instance_count(position, 5.0, VEGETATION_INSTANCE_COUNT), 2.0
)
Report.result(Tests.configured_sector_size_instance_count, result)
test = TestSystemSettingsSectorSize()
test.run()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(SystemSettings_SectorSize)
@@ -5,89 +5,85 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
"""
This script tests for regressions of "vegetation instances don't despawn correctly
when the camera moves beyond the range of all active vegetation areas".
This creates a new level and a vegetation area with 400 instances.
The expectation is that we will have 400 instances in that area when the camera is centered on it,
and 0 instances when the camera is moved sufficiently far away.
"""
import sys, os
import azlmbr.legacy.general as general
import azlmbr.math as math
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
from editor_python_test_tools.editor_test_helper import EditorTestHelper
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
class Tests:
instance_validation_close = (
"Instance count is as expected when within range of Spawner",
"Instance count was unexpected when within range of Spawner"
)
instance_validation_far = (
"No instances found when out of range of Spawner",
"Instances still found when out of range of Spawner"
)
class TestVegetationInstances_DespawnWhenOutOfRange(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix='VegetationInstances_DespawnWhenOutOfRange', args=['level'])
def VegetationInstances_DespawnWhenOutOfRange():
"""
Summary:
Verifies that vegetation instances properly spawn/despawn based on camera range.
def run_test(self):
"""
Summary:
Verifies that vegetation instances properly spawn/despawn based on camera range.
Expected Behavior:
Vegetation instances despawn when out of camera range.
Expected Behavior:
Vegetation instances despawn when out of camera range.
Test Steps:
1) Open a simple level
2) Create a simple vegetation area, and set the view position near the spawner. Verify instances plant.
3) Move the view position away from the spawner. Verify instances despawn.
Test Steps:
1) Create a new level
2) Create a simple vegetation area, and set the view position near the spawner. Verify instances plant.
3) Move the view position away from the spawner. Verify instances despawn.
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
:return: None
"""
:return: None
"""
import os
# Create a new level
self.test_success = self.create_level(
self.get_arg('level'),
heightmap_resolution=128,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=128,
use_terrain=False)
import azlmbr.legacy.general as general
import azlmbr.math as math
# Create vegetation layer spawner
world_center = math.Vector3(512.0, 512.0, 32.0)
asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice")
spawner_entity = dynveg.create_vegetation_area("Spawner Instance", world_center, 16.0, 16.0, 16.0, asset_path)
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
# Create a surface to spawn on
dynveg.create_surface_entity("Spawner Entity", world_center, 16.0, 16.0, 1.0)
# Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
# Get the root position of our veg area and use it to position our camera.
# This is useful both to ensure that vegetation is spawned where we're querying and to
# visually verify the number of instances in each box
position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", spawner_entity.id)
general.set_current_view_position(position.x, position.y, position.z + 30.0)
general.set_current_view_rotation(-90.0, 0.0, 0.0)
# Create vegetation layer spawner
world_center = math.Vector3(512.0, 512.0, 32.0)
asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice")
spawner_entity = dynveg.create_vegetation_area("Spawner Instance", world_center, 16.0, 16.0, 16.0, asset_path)
# When centered over the veg area, we expect to find 400 instances.
# (16x16 area, 20 points per 16 meters)
num_expected = 400
result = self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id,
num_expected), 2.0)
self.test_success = self.test_success and result
# Create a surface to spawn on
dynveg.create_surface_entity("Spawner Entity", world_center, 16.0, 16.0, 1.0)
# Move sufficiently far away from the veg area that it should all despawn.
general.set_current_view_position(position.x - 1000.0, position.y - 1000.0, position.z + 30.0)
# Get the root position of our veg area and use it to position our camera.
# This is useful both to ensure that vegetation is spawned where we're querying and to
# visually verify the number of instances in each box
position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", spawner_entity.id)
general.set_current_view_position(position.x, position.y, position.z + 30.0)
general.set_current_view_rotation(-90.0, 0.0, 0.0)
# We now expect to find 0 instances. If the bug exists, we will find 400 still.
num_expected = 0
result = self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id,
num_expected), 2.0)
self.test_success = self.test_success and result
# When centered over the veg area, we expect to find 400 instances.
# (16x16 area, 20 points per 16 meters)
num_expected = 400
result = helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id,
num_expected), 2.0)
Report.result(Tests.instance_validation_close, result)
# Move sufficiently far away from the veg area that it should all despawn.
general.set_current_view_position(position.x - 1000.0, position.y - 1000.0, position.z + 30.0)
# We now expect to find 0 instances. If the bug exists, we will find 400 still.
num_expected = 0
result = helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id,
num_expected), 2.0)
Report.result(Tests.instance_validation_far, result)
test = TestVegetationInstances_DespawnWhenOutOfRange()
test.run()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(VegetationInstances_DespawnWhenOutOfRange)
@@ -0,0 +1,27 @@
"""
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 sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)) + '/../../automatedtesting_shared')
from base import TestAutomationBase
@pytest.mark.SUITE_main
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
@pytest.mark.parametrize("project", ["AutomatedTesting"])
class TestAutomation(TestAutomationBase):
def test_DynamicSliceInstanceSpawner_DynamicSliceSpawnerWorks(self, request, workspace, editor, launcher_platform):
from .EditorScripts import DynamicSliceInstanceSpawner_DynamicSliceSpawnerWorks as test_module
self._run_test(request, workspace, editor, test_module)
def test_EmptyInstanceSpawner_EmptySpawnerWorks(self, request, workspace, editor, launcher_platform):
from .EditorScripts import EmptyInstanceSpawner_EmptySpawnerWorks as test_module
self._run_test(request, workspace, editor, test_module)
@@ -0,0 +1,172 @@
"""
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 EditorSingleTest, EditorSharedTest, EditorParallelTest, EditorTestSuite
@pytest.mark.xfail(reason="Optimized tests are experimental, we will enable xfail and monitor them temporarily.")
@pytest.mark.SUITE_main
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
@pytest.mark.parametrize("project", ["AutomatedTesting"])
class TestAutomation(EditorTestSuite):
class test_DynamicSliceInstanceSpawner_DynamicSliceSpawnerWorks(EditorParallelTest):
from .EditorScripts import DynamicSliceInstanceSpawner_DynamicSliceSpawnerWorks as test_module
class test_EmptyInstanceSpawner_EmptySpawnerWorks(EditorParallelTest):
from .EditorScripts import EmptyInstanceSpawner_EmptySpawnerWorks as test_module
class test_AltitudeFilter_ComponentAndOverrides_InstancesPlantAtSpecifiedAltitude(EditorParallelTest):
from .EditorScripts import AltitudeFilter_ComponentAndOverrides_InstancesPlantAtSpecifiedAltitude as test_module
class test_AltitudeFilter_ShapeSample_InstancesPlantAtSpecifiedAltitude(EditorParallelTest):
from .EditorScripts import AltitudeFilter_ShapeSample_InstancesPlantAtSpecifiedAltitude as test_module
class test_AltitudeFilter_FilterStageToggle(EditorParallelTest):
from .EditorScripts import AltitudeFilter_FilterStageToggle as test_module
class test_SpawnerSlices_SliceCreationAndVisibilityToggleWorks(EditorSingleTest):
# Custom teardown to remove slice asset created during test
def teardown(self, request, workspace, editor, editor_test_results, launcher_platform):
file_system.delete([os.path.join(workspace.paths.engine_root(), "AutomatedTesting", "slices",
"TestSlice_1.slice")], True, True)
file_system.delete([os.path.join(workspace.paths.engine_root(), "AutomatedTesting", "slices",
"TestSlice_2.slice")], True, True)
from .EditorScripts import SpawnerSlices_SliceCreationAndVisibilityToggleWorks as test_module
class test_AssetListCombiner_CombinedDescriptorsExpressInConfiguredArea(EditorParallelTest):
from .EditorScripts import AssetListCombiner_CombinedDescriptorsExpressInConfiguredArea as test_module
class test_AssetWeightSelector_InstancesExpressBasedOnWeight(EditorParallelTest):
from .EditorScripts import AssetWeightSelector_InstancesExpressBasedOnWeight as test_module
@pytest.mark.skip(reason="https://github.com/o3de/o3de/issues/4155")
class test_DistanceBetweenFilter_InstancesPlantAtSpecifiedRadius(EditorParallelTest):
from .EditorScripts import DistanceBetweenFilter_InstancesPlantAtSpecifiedRadius as test_module
@pytest.mark.skip(reason="https://github.com/o3de/o3de/issues/4155")
class test_DistanceBetweenFilterOverrides_InstancesPlantAtSpecifiedRadius(EditorParallelTest):
from .EditorScripts import DistanceBetweenFilterOverrides_InstancesPlantAtSpecifiedRadius as test_module
class test_SurfaceDataRefreshes_RemainsStable(EditorParallelTest):
from .EditorScripts import SurfaceDataRefreshes_RemainsStable as test_module
class test_VegetationInstances_DespawnWhenOutOfRange(EditorParallelTest):
from .EditorScripts import VegetationInstances_DespawnWhenOutOfRange as test_module
class test_InstanceSpawnerPriority_LayerAndSubPriority_HigherValuesPlantOverLower(EditorParallelTest):
from .EditorScripts import InstanceSpawnerPriority_LayerAndSubPriority as test_module
class test_LayerBlocker_InstancesBlockedInConfiguredArea(EditorParallelTest):
from .EditorScripts import LayerBlocker_InstancesBlockedInConfiguredArea as test_module
class test_LayerSpawner_InheritBehaviorFlag(EditorParallelTest):
from .EditorScripts import LayerSpawner_InheritBehaviorFlag as test_module
class test_LayerSpawner_InstancesPlantInAllSupportedShapes(EditorParallelTest):
from .EditorScripts import LayerSpawner_InstancesPlantInAllSupportedShapes as test_module
class test_LayerSpawner_FilterStageToggle(EditorParallelTest):
from .EditorScripts import LayerSpawner_FilterStageToggle as test_module
@pytest.mark.xfail(reason="https://github.com/o3de/o3de/issues/2038")
class test_LayerSpawner_InstancesRefreshUsingCorrectViewportCamera(EditorParallelTest):
from .EditorScripts import LayerSpawner_InstancesRefreshUsingCorrectViewportCamera as test_module
class test_MeshBlocker_InstancesBlockedByMesh(EditorParallelTest):
from .EditorScripts import MeshBlocker_InstancesBlockedByMesh as test_module
class test_MeshBlocker_InstancesBlockedByMeshHeightTuning(EditorParallelTest):
from .EditorScripts import MeshBlocker_InstancesBlockedByMeshHeightTuning as test_module
class test_MeshSurfaceTagEmitter_DependentOnMeshComponent(EditorParallelTest):
from .EditorScripts import MeshSurfaceTagEmitter_DependentOnMeshComponent as test_module
class test_MeshSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully(EditorParallelTest):
from .EditorScripts import MeshSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully as test_module
class test_PhysXColliderSurfaceTagEmitter_E2E_Editor(EditorParallelTest):
from .EditorScripts import PhysXColliderSurfaceTagEmitter_E2E_Editor as test_module
class test_PositionModifier_ComponentAndOverrides_InstancesPlantAtSpecifiedOffsets(EditorParallelTest):
from .EditorScripts import PositionModifier_ComponentAndOverrides_InstancesPlantAtSpecifiedOffsets as test_module
class test_PositionModifier_AutoSnapToSurfaceWorks(EditorParallelTest):
from .EditorScripts import PositionModifier_AutoSnapToSurfaceWorks as test_module
class test_RotationModifier_InstancesRotateWithinRange(EditorParallelTest):
from .EditorScripts import RotationModifier_InstancesRotateWithinRange as test_module
class test_RotationModifierOverrides_InstancesRotateWithinRange(EditorParallelTest):
from .EditorScripts import RotationModifierOverrides_InstancesRotateWithinRange as test_module
class test_ScaleModifier_InstancesProperlyScale(EditorParallelTest):
from .EditorScripts import ScaleModifier_InstancesProperlyScale as test_module
class test_ScaleModifierOverrides_InstancesProperlyScale(EditorParallelTest):
from .EditorScripts import ScaleModifierOverrides_InstancesProperlyScale as test_module
class test_ShapeIntersectionFilter_InstancesPlantInAssignedShape(EditorParallelTest):
from .EditorScripts import ShapeIntersectionFilter_InstancesPlantInAssignedShape as test_module
class test_ShapeIntersectionFilter_FilterStageToggle(EditorParallelTest):
from .EditorScripts import ShapeIntersectionFilter_FilterStageToggle as test_module
class test_SlopeAlignmentModifier_InstanceSurfaceAlignment(EditorParallelTest):
from .EditorScripts import SlopeAlignmentModifier_InstanceSurfaceAlignment as test_module
class test_SlopeAlignmentModifierOverrides_InstanceSurfaceAlignment(EditorParallelTest):
from .EditorScripts import SlopeAlignmentModifierOverrides_InstanceSurfaceAlignment as test_module
class test_SurfaceMaskFilter_BasicSurfaceTagCreation(EditorParallelTest):
from .EditorScripts import SurfaceMaskFilter_BasicSurfaceTagCreation as test_module
class test_SurfaceMaskFilter_ExclusiveSurfaceTags_Function(EditorParallelTest):
from .EditorScripts import SurfaceMaskFilter_ExclusionList as test_module
class test_SurfaceMaskFilter_InclusiveSurfaceTags_Function(EditorParallelTest):
from .EditorScripts import SurfaceMaskFilter_InclusionList as test_module
class test_SurfaceMaskFilterOverrides_MultipleDescriptorOverridesPlantAsExpected(EditorParallelTest):
from .EditorScripts import SurfaceMaskFilterOverrides_MultipleDescriptorOverridesPlantAsExpected as test_module
class test_SystemSettings_SectorPointDensity(EditorParallelTest):
from .EditorScripts import SystemSettings_SectorPointDensity as test_module
class test_SystemSettings_SectorSize(EditorParallelTest):
from .EditorScripts import SystemSettings_SectorSize as test_module
class test_SlopeFilter_ComponentAndOverrides_InstancesPlantOnValidSlopes(EditorParallelTest):
from .EditorScripts import SlopeFilter_ComponentAndOverrides_InstancesPlantOnValidSlope as test_module
class test_DynamicSliceInstanceSpawner_Embedded_E2E_Editor(EditorSingleTest):
from .EditorScripts import DynamicSliceInstanceSpawner_Embedded_E2E as test_module
# Custom teardown to remove test level created during test
def teardown(self, request, workspace, editor, editor_test_results, launcher_platform):
file_system.delete([os.path.join(workspace.paths.engine_root(), "AutomatedTesting", "Levels", "tmp_level")],
True, True)
class test_DynamicSliceInstanceSpawner_External_E2E_Editor(EditorSingleTest):
from .EditorScripts import DynamicSliceInstanceSpawner_External_E2E as test_module
# Custom teardown to remove test level created during test
def teardown(self, request, workspace, editor, editor_test_results, launcher_platform):
file_system.delete([os.path.join(workspace.paths.engine_root(), "AutomatedTesting", "Levels", "tmp_level")],
True, True)
class test_LayerBlender_E2E_Editor(EditorSingleTest):
from .EditorScripts import LayerBlender_E2E_Editor as test_module
# Custom teardown to remove test level created during test
def teardown(self, request, workspace, editor, editor_test_results, launcher_platform):
file_system.delete([os.path.join(workspace.paths.engine_root(), "AutomatedTesting", "Levels", "tmp_level")],
True, True)
@@ -0,0 +1,283 @@
"""
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 sys
import ly_test_tools.environment.waiter as waiter
import ly_test_tools.environment.file_system as file_system
import editor_python_test_tools.hydra_test_utils as hydra
from ly_remote_console.remote_console_commands import RemoteConsole as RemoteConsole
sys.path.append(os.path.dirname(os.path.abspath(__file__)) + '/../../automatedtesting_shared')
from base import TestAutomationBase
@pytest.fixture
def remove_test_slice(request, workspace, project):
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "slices", "TestSlice_1.slice")], True,
True)
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "slices", "TestSlice_2.slice")], True,
True)
def teardown():
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "slices", "TestSlice_1.slice")], True,
True)
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "slices", "TestSlice_2.slice")], True,
True)
request.addfinalizer(teardown)
@pytest.fixture
def remote_console_instance(request):
console = RemoteConsole()
def teardown():
if console.connected:
console.stop()
request.addfinalizer(teardown)
return console
@pytest.mark.SUITE_periodic
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
@pytest.mark.parametrize("project", ["AutomatedTesting"])
class TestAutomation(TestAutomationBase):
def test_AltitudeFilter_ComponentAndOverrides_InstancesPlantAtSpecifiedAltitude(self, request, workspace, editor, launcher_platform):
from .EditorScripts import AltitudeFilter_ComponentAndOverrides_InstancesPlantAtSpecifiedAltitude as test_module
self._run_test(request, workspace, editor, test_module)
def test_AltitudeFilter_ShapeSample_InstancesPlantAtSpecifiedAltitude(self, request, workspace, editor, launcher_platform):
from .EditorScripts import AltitudeFilter_ShapeSample_InstancesPlantAtSpecifiedAltitude as test_module
self._run_test(request, workspace, editor, test_module)
def test_AltitudeFilter_FilterStageToggle(self, request, workspace, editor, launcher_platform):
from .EditorScripts import AltitudeFilter_FilterStageToggle as test_module
self._run_test(request, workspace, editor, test_module)
def test_SpawnerSlices_SliceCreationAndVisibilityToggleWorks(self, request, workspace, editor, remove_test_slice, launcher_platform):
from .EditorScripts import SpawnerSlices_SliceCreationAndVisibilityToggleWorks as test_module
self._run_test(request, workspace, editor, test_module)
def test_AssetListCombiner_CombinedDescriptorsExpressInConfiguredArea(self, request, workspace, editor, launcher_platform):
from .EditorScripts import AssetListCombiner_CombinedDescriptorsExpressInConfiguredArea as test_module
self._run_test(request, workspace, editor, test_module)
def test_AssetWeightSelector_InstancesExpressBasedOnWeight(self, request, workspace, editor, launcher_platform):
from .EditorScripts import AssetWeightSelector_InstancesExpressBasedOnWeight as test_module
self._run_test(request, workspace, editor, test_module)
@pytest.mark.xfail(reason="https://github.com/o3de/o3de/issues/4155")
def test_DistanceBetweenFilter_InstancesPlantAtSpecifiedRadius(self, request, workspace, editor, launcher_platform):
from .EditorScripts import DistanceBetweenFilter_InstancesPlantAtSpecifiedRadius as test_module
self._run_test(request, workspace, editor, test_module)
@pytest.mark.xfail(reason="https://github.com/o3de/o3de/issues/4155")
def test_DistanceBetweenFilterOverrides_InstancesPlantAtSpecifiedRadius(self, request, workspace, editor, launcher_platform):
from .EditorScripts import DistanceBetweenFilterOverrides_InstancesPlantAtSpecifiedRadius as test_module
self._run_test(request, workspace, editor, test_module)
def test_SurfaceDataRefreshes_RemainsStable(self, request, workspace, editor, launcher_platform):
from .EditorScripts import SurfaceDataRefreshes_RemainsStable as test_module
self._run_test(request, workspace, editor, test_module)
def test_VegetationInstances_DespawnWhenOutOfRange(self, request, workspace, editor, launcher_platform):
from .EditorScripts import VegetationInstances_DespawnWhenOutOfRange as test_module
self._run_test(request, workspace, editor, test_module)
def test_InstanceSpawnerPriority_LayerAndSubPriority_HigherValuesPlantOverLower(self, request, workspace, editor, launcher_platform):
from .EditorScripts import InstanceSpawnerPriority_LayerAndSubPriority as test_module
self._run_test(request, workspace, editor, test_module)
def test_LayerBlocker_InstancesBlockedInConfiguredArea(self, request, workspace, editor, launcher_platform):
from .EditorScripts import LayerBlocker_InstancesBlockedInConfiguredArea as test_module
self._run_test(request, workspace, editor, test_module)
def test_LayerSpawner_InheritBehaviorFlag(self, request, workspace, editor, launcher_platform):
from .EditorScripts import LayerSpawner_InheritBehaviorFlag as test_module
self._run_test(request, workspace, editor, test_module)
def test_LayerSpawner_InstancesPlantInAllSupportedShapes(self, request, workspace, editor, launcher_platform):
from .EditorScripts import LayerSpawner_InstancesPlantInAllSupportedShapes as test_module
self._run_test(request, workspace, editor, test_module)
def test_LayerSpawner_FilterStageToggle(self, request, workspace, editor, launcher_platform):
from .EditorScripts import LayerSpawner_FilterStageToggle as test_module
self._run_test(request, workspace, editor, test_module)
@pytest.mark.xfail(reason="https://github.com/o3de/o3de/issues/2038")
def test_LayerSpawner_InstancesRefreshUsingCorrectViewportCamera(self, request, workspace, editor, launcher_platform):
from .EditorScripts import LayerSpawner_InstancesRefreshUsingCorrectViewportCamera as test_module
self._run_test(request, workspace, editor, test_module)
def test_MeshBlocker_InstancesBlockedByMesh(self, request, workspace, editor, launcher_platform):
from .EditorScripts import MeshBlocker_InstancesBlockedByMesh as test_module
self._run_test(request, workspace, editor, test_module)
def test_MeshBlocker_InstancesBlockedByMeshHeightTuning(self, request, workspace, editor, launcher_platform):
from .EditorScripts import MeshBlocker_InstancesBlockedByMeshHeightTuning as test_module
self._run_test(request, workspace, editor, test_module)
def test_MeshSurfaceTagEmitter_DependentOnMeshComponent(self, request, workspace, editor, launcher_platform):
from .EditorScripts import MeshSurfaceTagEmitter_DependentOnMeshComponent as test_module
self._run_test(request, workspace, editor, test_module)
def test_MeshSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully(self, request, workspace, editor, launcher_platform):
from .EditorScripts import MeshSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully as test_module
self._run_test(request, workspace, editor, test_module)
def test_PhysXColliderSurfaceTagEmitter_E2E_Editor(self, request, workspace, editor, launcher_platform):
from .EditorScripts import PhysXColliderSurfaceTagEmitter_E2E_Editor as test_module
self._run_test(request, workspace, editor, test_module)
def test_PositionModifier_ComponentAndOverrides_InstancesPlantAtSpecifiedOffsets(self, request, workspace, editor, launcher_platform):
from .EditorScripts import PositionModifier_ComponentAndOverrides_InstancesPlantAtSpecifiedOffsets as test_module
self._run_test(request, workspace, editor, test_module)
def test_PositionModifier_AutoSnapToSurfaceWorks(self, request, workspace, editor, launcher_platform):
from .EditorScripts import PositionModifier_AutoSnapToSurfaceWorks as test_module
self._run_test(request, workspace, editor, test_module)
def test_RotationModifier_InstancesRotateWithinRange(self, request, workspace, editor, launcher_platform):
from .EditorScripts import RotationModifier_InstancesRotateWithinRange as test_module
self._run_test(request, workspace, editor, test_module)
def test_RotationModifierOverrides_InstancesRotateWithinRange(self, request, workspace, editor, launcher_platform):
from .EditorScripts import RotationModifierOverrides_InstancesRotateWithinRange as test_module
self._run_test(request, workspace, editor, test_module)
def test_ScaleModifier_InstancesProperlyScale(self, request, workspace, editor, launcher_platform):
from .EditorScripts import ScaleModifier_InstancesProperlyScale as test_module
self._run_test(request, workspace, editor, test_module)
def test_ScaleModifierOverrides_InstancesProperlyScale(self, request, workspace, editor, launcher_platform):
from .EditorScripts import ScaleModifierOverrides_InstancesProperlyScale as test_module
self._run_test(request, workspace, editor, test_module)
def test_ShapeIntersectionFilter_InstancesPlantInAssignedShape(self, request, workspace, editor, launcher_platform):
from .EditorScripts import ShapeIntersectionFilter_InstancesPlantInAssignedShape as test_module
self._run_test(request, workspace, editor, test_module)
def test_ShapeIntersectionFilter_FilterStageToggle(self, request, workspace, editor, launcher_platform):
from .EditorScripts import ShapeIntersectionFilter_FilterStageToggle as test_module
self._run_test(request, workspace, editor, test_module)
def test_SlopeAlignmentModifier_InstanceSurfaceAlignment(self, request, workspace, editor, launcher_platform):
from .EditorScripts import SlopeAlignmentModifier_InstanceSurfaceAlignment as test_module
self._run_test(request, workspace, editor, test_module)
def test_SlopeAlignmentModifierOverrides_InstanceSurfaceAlignment(self, request, workspace, editor, launcher_platform):
from .EditorScripts import SlopeAlignmentModifierOverrides_InstanceSurfaceAlignment as test_module
self._run_test(request, workspace, editor, test_module)
def test_SurfaceMaskFilter_BasicSurfaceTagCreation(self, request, workspace, editor, launcher_platform):
from .EditorScripts import SurfaceMaskFilter_BasicSurfaceTagCreation as test_module
self._run_test(request, workspace, editor, test_module)
def test_SurfaceMaskFilter_ExclusiveSurfaceTags_Function(self, request, workspace, editor, launcher_platform):
from .EditorScripts import SurfaceMaskFilter_ExclusionList as test_module
self._run_test(request, workspace, editor, test_module)
def test_SurfaceMaskFilter_InclusiveSurfaceTags_Function(self, request, workspace, editor, launcher_platform):
from .EditorScripts import SurfaceMaskFilter_InclusionList as test_module
self._run_test(request, workspace, editor, test_module)
def test_SurfaceMaskFilterOverrides_MultipleDescriptorOverridesPlantAsExpected(self, request, workspace, editor, launcher_platform):
from .EditorScripts import SurfaceMaskFilterOverrides_MultipleDescriptorOverridesPlantAsExpected as test_module
self._run_test(request, workspace, editor, test_module)
def test_SystemSettings_SectorPointDensity(self, request, workspace, editor, launcher_platform):
from .EditorScripts import SystemSettings_SectorPointDensity as test_module
self._run_test(request, workspace, editor, test_module)
def test_SystemSettings_SectorSize(self, request, workspace, editor, launcher_platform):
from .EditorScripts import SystemSettings_SectorSize as test_module
self._run_test(request, workspace, editor, test_module)
def test_SlopeFilter_ComponentAndOverrides_InstancesPlantOnValidSlopes(self, request, workspace, editor, launcher_platform):
from .EditorScripts import SlopeFilter_ComponentAndOverrides_InstancesPlantOnValidSlope as test_module
self._run_test(request, workspace, editor, test_module)
@pytest.mark.SUITE_periodic
@pytest.mark.parametrize("project", ["AutomatedTesting"])
@pytest.mark.parametrize("level", ["tmp_level"])
class TestAutomationE2E(TestAutomationBase):
# The following tests must run in order, please do not move tests out of order
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
def test_DynamicSliceInstanceSpawner_Embedded_E2E_Editor(self, request, workspace, project, level, editor, launcher_platform):
# Ensure our test level does not already exist
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
from .EditorScripts import DynamicSliceInstanceSpawner_Embedded_E2E as test_module
self._run_test(request, workspace, editor, test_module)
@pytest.mark.parametrize("launcher_platform", ['windows'])
def test_DynamicSliceInstanceSpawner_Embedded_E2E_Launcher(self, workspace, launcher, level,
remote_console_instance, project, launcher_platform):
expected_lines = [
"Instances found in area = 400"
]
hydra.launch_and_validate_results_launcher(launcher, level, remote_console_instance, expected_lines, launch_ap=False)
# Cleanup our temp level
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
def test_DynamicSliceInstanceSpawner_External_E2E_Editor(self, request, workspace, project, level, editor, launcher_platform):
# Ensure our test level does not already exist
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
from .EditorScripts import DynamicSliceInstanceSpawner_External_E2E as test_module
self._run_test(request, workspace, editor, test_module)
@pytest.mark.parametrize("launcher_platform", ['windows'])
def test_DynamicSliceInstanceSpawner_External_E2E_Launcher(self, workspace, launcher, level,
remote_console_instance, project, launcher_platform):
expected_lines = [
"Instances found in area = 400"
]
hydra.launch_and_validate_results_launcher(launcher, level, remote_console_instance, expected_lines, launch_ap=False)
# Cleanup our temp level
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
def test_LayerBlender_E2E_Editor(self, request, workspace, project, level, editor, launcher_platform):
# Ensure our test level does not already exist
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
from .EditorScripts import LayerBlender_E2E_Editor as test_module
self._run_test(request, workspace, editor, test_module)
@pytest.mark.parametrize("launcher_platform", ['windows'])
@pytest.mark.xfail(reason="https://github.com/o3de/o3de/issues/4170")
def test_LayerBlender_E2E_Launcher(self, workspace, launcher, level,
remote_console_instance, project, launcher_platform):
launcher.args.extend(["-rhi=Null"])
launcher.start(launch_ap=False)
assert launcher.is_alive(), "Launcher failed to start"
# Wait for test script to quit the launcher. If wait_for returns exc, test was not successful
waiter.wait_for(lambda: not launcher.is_alive(), timeout=300)
# Verify launcher quit successfully and did not crash
ret_code = launcher.get_returncode()
assert ret_code == 0, "Test failed. See Game.log for details"
# Cleanup our temp level
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
@@ -1,111 +0,0 @@
"""
Copyright (c) Contributors to the Open 3D Engine Project.
For complete copyright and license terms please see the LICENSE at the root of this distribution.
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import pytest
import logging
# Bail on the test if ly_test_tools doesn't exist.
pytest.importorskip('ly_test_tools')
import ly_test_tools.environment.file_system as file_system
import editor_python_test_tools.hydra_test_utils as hydra
logger = logging.getLogger(__name__)
test_directory = os.path.join(os.path.dirname(__file__), 'EditorScripts')
@pytest.mark.parametrize("project", ["AutomatedTesting"])
@pytest.mark.parametrize("level", ["tmp_level"])
@pytest.mark.usefixtures("automatic_process_killer")
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
class TestAltitudeFilter(object):
@pytest.fixture(autouse=True)
def setup_teardown(self, request, workspace, project, level):
def teardown():
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
request.addfinalizer(teardown)
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
@pytest.mark.test_case_id('C4814463', 'C4847477')
@pytest.mark.SUITE_periodic
@pytest.mark.dynveg_filter
def test_AltitudeFilter_ComponentAndOverrides_InstancesPlantAtSpecifiedAltitude(self, request, editor, level,
launcher_platform):
expected_lines = [
"'Instance Spawner' created",
"'Planting Surface' created",
"'Planting Surface Elevated' created",
"instance count validation: True (found=3200, expected=3200)",
"instance count validation: True (found=1600, expected=1600)",
"instance count validation: True (found=400, expected=400)",
"AltitudeFilterComponentAndOverrides: result=SUCCESS"
]
hydra.launch_and_validate_results(
request,
test_directory,
editor,
"AltitudeFilter_ComponentAndOverrides_InstancesPlantAtSpecifiedAltitude.py",
expected_lines,
cfg_args=[level]
)
@pytest.mark.test_case_id("C4847476")
@pytest.mark.SUITE_periodic
@pytest.mark.dynveg_filter
def test_AltitudeFilter_ShapeSample_InstancesPlantAtSpecifiedAltitude(self, request, editor, level,
launcher_platform):
expected_lines = [
"'Instance Spawner' created",
"'Planting Surface' created",
"'Planting Surface Elevated' created",
"instance count validation: True (found=800, expected=800)",
"'Shape Sampler' created",
"instance count validation: True (found=400, expected=400)",
"AltitudeFilterShapeSample: result=SUCCESS"
]
hydra.launch_and_validate_results(
request,
test_directory,
editor,
"AltitudeFilter_ShapeSample_InstancesPlantAtSpecifiedAltitude.py",
expected_lines,
cfg_args=[level]
)
@pytest.mark.test_case_id("C4847478")
@pytest.mark.SUITE_periodic
@pytest.mark.dynveg_filter
@pytest.mark.xfail(reason="https://github.com/o3de/o3de/issues/2303")
def test_AltitudeFilter_FilterStageToggle(self, request, editor, level, workspace, launcher_platform):
cfg_args = [level]
expected_lines = [
"AltitudeFilter_FilterStageToggle: test started",
"AltitudeFilter_FilterStageToggle: Vegetation instances count equal to expected value for PREPROCESS filter stage: True",
"AltitudeFilter_FilterStageToggle: Vegetation instances count equal to expected value for POSTPROCESS filter stage: True",
"AltitudeFilter_FilterStageToggle: result=SUCCESS",
]
unexpected_lines = [
"AltitudeFilter_FilterStageToggle: Vegetation instances count equal to expected value for PREPROCESS filter stage: False",
"AltitudeFilter_FilterStageToggle: Vegetation instances count equal to expected value for POSTPROCESS filter stage: False",
]
hydra.launch_and_validate_results(
request,
test_directory,
editor,
"AltitudeFilter_FilterStageToggle.py",
expected_lines=expected_lines,
unexpected_lines=unexpected_lines,
cfg_args=cfg_args
)
@@ -1,72 +0,0 @@
"""
Copyright (c) Contributors to the Open 3D Engine Project.
For complete copyright and license terms please see the LICENSE at the root of this distribution.
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import pytest
import logging
# Bail on the test if ly_test_tools doesn't exist.
pytest.importorskip("ly_test_tools")
import ly_test_tools.environment.file_system as file_system
import editor_python_test_tools.hydra_test_utils as hydra
logger = logging.getLogger(__name__)
test_directory = os.path.join(os.path.dirname(__file__), "EditorScripts")
@pytest.mark.parametrize("project", ["AutomatedTesting"])
@pytest.mark.parametrize("level", ["tmp_level"])
@pytest.mark.usefixtures("automatic_process_killer")
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
class TestAreaComponents(object):
@pytest.fixture(autouse=True)
def setup_teardown(self, request, workspace, project, level):
# Cleanup our temp level
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
# Cleanup the test slices
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "slices", "TestSlice_1.slice")], True, True)
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "slices", "TestSlice_2.slice")], True, True)
def teardown():
# Cleanup our temp level
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
# Cleanup the test slices
file_system.delete(
[os.path.join(workspace.paths.engine_root(), project, "slices", "TestSlice_1.slice")], True, True
)
file_system.delete(
[os.path.join(workspace.paths.engine_root(), project, "slices", "TestSlice_2.slice")], True, True
)
request.addfinalizer(teardown)
@pytest.mark.test_case_id("C2627900", "C2627905", "C2627904")
@pytest.mark.SUITE_periodic
@pytest.mark.dynveg_misc
def test_AreaComponents_SliceCreationVisibilityToggleWorks(self, request, editor, level, workspace,
launcher_platform):
cfg_args = [level]
expected_lines = [
"AreaComponentSlices_SliceCreationAndVisibilityToggle: test started",
"AreaComponentSlices_SliceCreationAndVisibilityToggle: Slice has been created successfully (entity with spawner component): True",
"AreaComponentSlices_SliceCreationAndVisibilityToggle: Vegetation plants initially when slice is shown: True",
"AreaComponentSlices_SliceCreationAndVisibilityToggle: Vegetation is cleared when slice is hidden: True",
"AreaComponentSlices_SliceCreationAndVisibilityToggle: Slice has been created successfully (entity with blender component): True",
"AreaComponentSlices_SliceCreationAndVisibilityToggle: result=SUCCESS",
]
hydra.launch_and_validate_results(
request,
test_directory,
editor,
"AreaComponentSlices_SliceCreationAndVisibilityToggle.py",
expected_lines=expected_lines,
cfg_args=cfg_args
)
@@ -1,63 +0,0 @@
"""
Copyright (c) Contributors to the Open 3D Engine Project.
For complete copyright and license terms please see the LICENSE at the root of this distribution.
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import pytest
import logging
# Bail on the test if ly_test_tools doesn't exist.
pytest.importorskip("ly_test_tools")
import ly_test_tools.environment.file_system as file_system
import editor_python_test_tools.hydra_test_utils as hydra
logger = logging.getLogger(__name__)
test_directory = os.path.join(os.path.dirname(__file__), "EditorScripts")
@pytest.mark.parametrize('project', ['AutomatedTesting'])
@pytest.mark.parametrize('level', ['tmp_level'])
@pytest.mark.usefixtures("automatic_process_killer")
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
class TestAssetListCombiner(object):
@pytest.fixture(autouse=True)
def setup_teardown(self, request, workspace, project, level):
def teardown():
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
request.addfinalizer(teardown)
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
@pytest.mark.test_case_id("C4762374", "C4762373")
@pytest.mark.SUITE_periodic
@pytest.mark.dynveg_misc
def test_AssetListCombiner_CombinedDescriptorsExpressInConfiguredArea(self, request, editor, level,
launcher_platform):
expected_lines = [
"'Asset List 1' created",
"'Asset List 2' created",
"'Asset List 3' created",
"'Surface Entity' created",
"'Spawner Entity' created",
"Spawner Entity Configuration|Descriptor Providers: SUCCESS",
"Spawner Entity Configuration|Gradient|Gradient Entity Id: SUCCESS",
"instance count validation: True (found=200, expected=200.0)",
"Spawner Entity Configuration|Descriptor Providers|[1]: SUCCESS",
"instance count validation: True (found=400, expected=400)",
"instance count validation: True (found=0, expected=0)",
"AssetListCombiner_CombinedDescriptors: result=SUCCESS"
]
hydra.launch_and_validate_results(
request,
test_directory,
editor,
"AssetListCombiner_CombinedDescriptorsExpressInConfiguredArea.py",
expected_lines=expected_lines,
cfg_args=[level]
)
@@ -1,60 +0,0 @@
"""
Copyright (c) Contributors to the Open 3D Engine Project.
For complete copyright and license terms please see the LICENSE at the root of this distribution.
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
"""
C6269654: Vegetation areas using weight selectors properly distribute instances according to Sort By Weight setting
"""
import os
import pytest
import logging
# Bail on the test if ly_test_tools doesn't exist.
pytest.importorskip('ly_test_tools')
import ly_test_tools.environment.file_system as file_system
import editor_python_test_tools.hydra_test_utils as hydra
logger = logging.getLogger(__name__)
test_directory = os.path.join(os.path.dirname(__file__), 'EditorScripts')
@pytest.mark.parametrize("project", ["AutomatedTesting"])
@pytest.mark.parametrize("level", ["tmp_level"])
@pytest.mark.usefixtures("automatic_process_killer")
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
class TestAssetWeightSelector(object):
@pytest.fixture(autouse=True)
def setup_teardown(self, request, workspace, project, level):
def teardown():
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
request.addfinalizer(teardown)
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
@pytest.mark.test_case_id("C6269654", "C4762368")
@pytest.mark.SUITE_sandbox
@pytest.mark.dynveg_filter
def test_AssetWeightSelector_InstancesExpressBasedOnWeight(self, request, editor, level, launcher_platform):
expected_lines = [
"'Instance Spawner' created",
"Instance Spawner Configuration|Embedded Assets|[1]|Instance|Slice Asset: SUCCESS",
"'Planting Surface' created",
"Configuration|Embedded Assets|[0]|Weight set to 50.0",
"Instance Spawner Configuration|Allow Empty Assets: SUCCESS",
"AssetWeightSelector_SortByWeight: result=SUCCESS",
]
hydra.launch_and_validate_results(
request,
test_directory,
editor,
"AssetWeightSelector_InstancesExpressBasedOnWeight.py",
expected_lines,
cfg_args=[level]
)
@@ -1,78 +0,0 @@
"""
Copyright (c) Contributors to the Open 3D Engine Project.
For complete copyright and license terms please see the LICENSE at the root of this distribution.
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import pytest
import logging
# Bail on the test if ly_test_tools doesn't exist.
pytest.importorskip('ly_test_tools')
import ly_test_tools.environment.file_system as file_system
import editor_python_test_tools.hydra_test_utils as hydra
logger = logging.getLogger(__name__)
test_directory = os.path.join(os.path.dirname(__file__), 'EditorScripts')
@pytest.mark.parametrize("project", ["AutomatedTesting"])
@pytest.mark.parametrize("level", ["tmp_level"])
@pytest.mark.usefixtures("automatic_process_killer")
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
class TestDistanceBetweenFilter(object):
@pytest.fixture(autouse=True)
def setup_teardown(self, request, workspace, project, level):
def teardown():
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
request.addfinalizer(teardown)
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
@pytest.mark.test_case_id("C4851066")
@pytest.mark.SUITE_periodic
@pytest.mark.dynveg_filter
@pytest.mark.xfail(reason="https://github.com/o3de/o3de/issues/4155")
def test_DistanceBetweenFilter_InstancesPlantAtSpecifiedRadius(self, request, editor, level, launcher_platform):
expected_lines = [
"Configuration|Radius Min set to 1.0",
"Configuration|Radius Min set to 2.0",
"Configuration|Radius Min set to 16.0",
"DistanceBetweenFilterComponent: result=SUCCESS"
]
hydra.launch_and_validate_results(
request,
test_directory,
editor,
"DistanceBetweenFilter_InstancesPlantAtSpecifiedRadius.py",
expected_lines,
cfg_args=[level]
)
@pytest.mark.test_case_id("C4814458")
@pytest.mark.SUITE_periodic
@pytest.mark.dynveg_filter
@pytest.mark.xfail(reason="https://github.com/o3de/o3de/issues/4155")
def test_DistanceBetweenFilterOverrides_InstancesPlantAtSpecifiedRadius(self, request, editor, level,
launcher_platform):
expected_lines = [
"Configuration|Embedded Assets|[0]|Distance Between Filter (Radius)|Radius Min set to 1.0",
"Configuration|Embedded Assets|[0]|Distance Between Filter (Radius)|Radius Min set to 2.0",
"Configuration|Embedded Assets|[0]|Distance Between Filter (Radius)|Radius Min set to 16.0",
"DistanceBetweenFilterComponentOverrides: result=SUCCESS"
]
hydra.launch_and_validate_results(
request,
test_directory,
editor,
"DistanceBetweenFilterOverrides_InstancesPlantAtSpecifiedRadius.py",
expected_lines,
cfg_args=[level]
)
@@ -1,78 +0,0 @@
"""
Copyright (c) Contributors to the Open 3D Engine Project.
For complete copyright and license terms please see the LICENSE at the root of this distribution.
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import pytest
# Bail on the test if ly_test_tools doesn't exist.
pytest.importorskip('ly_test_tools')
import editor_python_test_tools.hydra_test_utils as hydra
import ly_test_tools.environment.file_system as file_system
test_directory = os.path.join(os.path.dirname(__file__), 'EditorScripts')
@pytest.mark.parametrize('project', ['AutomatedTesting'])
@pytest.mark.parametrize('level', ['tmp_level'])
@pytest.mark.usefixtures("automatic_process_killer")
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
class Test_DynVeg_Regressions(object):
@pytest.fixture(autouse=True)
def setup_teardown(self, request, workspace, project, level):
def teardown():
# delete temp level
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
# Setup - add the teardown finalizer
request.addfinalizer(teardown)
# Make sure the temp level doesn't already exist
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
@pytest.mark.test_case_id("C29470845")
@pytest.mark.SUITE_periodic
@pytest.mark.dynveg_regression
def test_SurfaceDataRefreshes_RemainsStable(self, request, editor, level, launcher_platform):
expected_lines = [
"SurfaceDataRefreshes_RemainsStable: test started",
"SurfaceDataRefreshes_RemainsStable: test finished",
"SurfaceDataRefreshes_RemainsStable: result=SUCCESS"
]
unexpected_lines = [
"Sector update mode is 'RebuildSurfaceCache' but sector doesn't exist"
]
hydra.launch_and_validate_results(
request,
test_directory,
editor,
'SurfaceDataRefreshes_RemainsStable.py',
expected_lines=expected_lines,
unexpected_lines=unexpected_lines,
cfg_args=[level]
)
@pytest.mark.SUITE_periodic
def test_VegetationInstances_DespawnWhenOutOfRange(self, request, editor, level, launcher_platform):
expected_lines = [
"VegetationInstances_DespawnWhenOutOfRange: test started",
"VegetationInstances_DespawnWhenOutOfRange: test finished",
"VegetationInstances_DespawnWhenOutOfRange: result=SUCCESS"
]
hydra.launch_and_validate_results(
request,
test_directory,
editor,
'VegetationInstances_DespawnWhenOutOfRange.py',
expected_lines=expected_lines,
cfg_args=[level]
)
@@ -1,140 +0,0 @@
"""
Copyright (c) Contributors to the Open 3D Engine Project.
For complete copyright and license terms please see the LICENSE at the root of this distribution.
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import pytest
import logging
# Bail on the test if ly_test_tools doesn't exist.
pytest.importorskip('ly_test_tools')
import ly_test_tools.environment.file_system as file_system
import ly_test_tools._internal.pytest_plugin as internal_plugin
import editor_python_test_tools.hydra_test_utils as hydra
from ly_remote_console.remote_console_commands import RemoteConsole as RemoteConsole
logger = logging.getLogger(__name__)
test_directory = os.path.join(os.path.dirname(__file__), 'EditorScripts')
@pytest.mark.parametrize('project', ['AutomatedTesting'])
@pytest.mark.parametrize('level', ['tmp_level'])
@pytest.mark.usefixtures("automatic_process_killer")
class TestDynamicSliceInstanceSpawner(object):
@pytest.fixture
def remote_console_instance(self, request):
console = RemoteConsole()
def teardown():
if console.connected:
console.stop()
request.addfinalizer(teardown)
return console
@pytest.mark.test_case_id("C28851763")
@pytest.mark.SUITE_main
@pytest.mark.dynveg_area
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
def test_DynamicSliceInstanceSpawner_DynamicSliceSpawnerWorks(self, request, editor, level, workspace, project,
launcher_platform):
# Skip test if running against Debug build
if "debug" in internal_plugin.build_directory:
pytest.skip("Does not execute against debug builds.")
# Ensure temp level does not already exist
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
cfg_args = [level]
expected_lines = [
"DynamicSliceInstanceSpawner: test started",
"DynamicSliceInstanceSpawner: test finished",
"DynamicSliceInstanceSpawner: result=SUCCESS"
]
hydra.launch_and_validate_results(request, test_directory, editor,
'DynamicSliceInstanceSpawner_DynamicSliceSpawnerWorks.py',
expected_lines=expected_lines, cfg_args=cfg_args)
# Cleanup our temp level
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
@pytest.mark.test_case_id('C2574330')
@pytest.mark.BAT
@pytest.mark.SUITE_periodic
@pytest.mark.dynveg_area
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
def test_DynamicSliceInstanceSpawner_Embedded_E2E_Editor(self, workspace, request, editor, level, project,
launcher_platform):
# Ensure temp level does not already exist
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
expected_lines = [
"'Instance Spawner' created",
"'Planting Surface' created",
"DynamicSliceInstanceSpawnerEmbeddedEditor: Expected 400 instances - Found 400 instances",
"DynamicSliceInstanceSpawnerEmbeddedEditor: result=SUCCESS"
]
hydra.launch_and_validate_results(request, test_directory, editor, "DynamicSliceInstanceSpawner_Embedded_E2E.py",
expected_lines, cfg_args=[level])
@pytest.mark.test_case_id('C2574330')
@pytest.mark.BAT
@pytest.mark.SUITE_periodic
@pytest.mark.dynveg_area
@pytest.mark.parametrize("launcher_platform", ['windows'])
def test_DynamicSliceInstanceSpawner_Embedded_E2E_Launcher(self, workspace, launcher, level,
remote_console_instance, project, launcher_platform):
expected_lines = [
"Instances found in area = 400"
]
hydra.launch_and_validate_results_launcher(launcher, level, remote_console_instance, expected_lines)
# Cleanup our temp level
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
@pytest.mark.test_case_id('C4762367')
@pytest.mark.SUITE_periodic
@pytest.mark.dynveg_area
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
def test_DynamicSliceInstanceSpawner_External_E2E_Editor(self, workspace, request, editor, level, project,
launcher_platform):
# Ensure temp level does not already exist
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
expected_lines = [
"Spawner entity created",
"'Planting Surface' created",
"DynamicSliceInstanceSpawnerExternalEditor: Expected 400 instances - Found 400 instances",
"DynamicSliceInstanceSpawnerExternalEditor: result=SUCCESS"
]
hydra.launch_and_validate_results(request, test_directory, editor, "DynamicSliceInstanceSpawner_External_E2E.py",
expected_lines, cfg_args=[level])
@pytest.mark.test_case_id('C4762367')
@pytest.mark.SUITE_periodic
@pytest.mark.dynveg_area
@pytest.mark.parametrize("launcher_platform", ['windows'])
def test_DynamicSliceInstanceSpawner_External_E2E_Launcher(self, workspace, launcher, level,
remote_console_instance, project, launcher_platform):
expected_lines = [
"Instances found in area = 400"
]
hydra.launch_and_validate_results_launcher(launcher, level, remote_console_instance, expected_lines)
# Cleanup our temp level
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
@@ -1,54 +0,0 @@
"""
Copyright (c) Contributors to the Open 3D Engine Project.
For complete copyright and license terms please see the LICENSE at the root of this distribution.
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import pytest
import logging
# Bail on the test if ly_test_tools doesn't exist.
pytest.importorskip('ly_test_tools')
import ly_test_tools.environment.file_system as file_system
import ly_test_tools._internal.pytest_plugin as internal_plugin
import editor_python_test_tools.hydra_test_utils as hydra
logger = logging.getLogger(__name__)
test_directory = os.path.join(os.path.dirname(__file__), 'EditorScripts')
@pytest.mark.parametrize('project', ['AutomatedTesting'])
@pytest.mark.parametrize('level', ['tmp_level'])
@pytest.mark.usefixtures("automatic_process_killer")
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
class TestEmptyInstanceSpawner(object):
@pytest.fixture(autouse=True)
def setup_teardown(self, request, workspace, project, level):
def teardown():
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
request.addfinalizer(teardown)
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
@pytest.mark.test_case_id("C28851762")
@pytest.mark.SUITE_main
@pytest.mark.dynveg_area
def test_EmptyInstanceSpawner_EmptySpawnerWorks(self, request, editor, level, launcher_platform):
# Skip test if running against Debug build
if "debug" in internal_plugin.build_directory:
pytest.skip("Does not execute against debug builds.")
cfg_args = [level]
expected_lines = [
"EmptyInstanceSpawner: test started",
"EmptyInstanceSpawner: test finished",
"EmptyInstanceSpawner: result=SUCCESS"
]
hydra.launch_and_validate_results(request, test_directory, editor, 'EmptyInstanceSpawner_EmptySpawnerWorks.py',
expected_lines=expected_lines, cfg_args=cfg_args)
@@ -1,63 +0,0 @@
"""
Copyright (c) Contributors to the Open 3D Engine Project.
For complete copyright and license terms please see the LICENSE at the root of this distribution.
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
"""
C5747383: Vegetation areas with a higher Layer Priority plant over those with a lower Layer Priority
C4762382: Vegetation areas with a higher Sub Priority plant over those with a lower Sub Priority
"""
import os
import pytest
import logging
# Bail on the test if ly_test_tools doesn't exist.
pytest.importorskip('ly_test_tools')
import ly_test_tools.environment.file_system as file_system
import editor_python_test_tools.hydra_test_utils as hydra
logger = logging.getLogger(__name__)
test_directory = os.path.join(os.path.dirname(__file__), 'EditorScripts')
@pytest.mark.parametrize("project", ["AutomatedTesting"])
@pytest.mark.parametrize("level", ["tmp_level"])
@pytest.mark.usefixtures("automatic_process_killer")
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
class TestInstanceSpawnerPriority(object):
@pytest.fixture(autouse=True)
def setup_teardown(self, request, workspace, project, level):
def teardown():
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
request.addfinalizer(teardown)
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
@pytest.mark.test_case_id("C5747383", "C4762382")
@pytest.mark.SUITE_periodic
@pytest.mark.dynveg_misc
def test_InstanceSpawnerPriority_LayerAndSubPriority_HigherValuesPlantOverLower(self, request, editor, level,
launcher_platform):
expected_lines = [
"'Instance Spawner' created",
"'Instance Blocker' created",
"'Planting Surface' created",
"Instance Blocker Configuration|Layer Priority: SUCCESS",
"Instance Spawner Configuration|Sub Priority: SUCCESS",
"Instance Blocker Configuration|Sub Priority: SUCCESS",
"InstanceSpawnerPriority: result=SUCCESS",
]
hydra.launch_and_validate_results(
request,
test_directory,
editor,
"InstanceSpawnerPriority_LayerAndSubPriority.py",
expected_lines,
cfg_args=[level]
)
@@ -1,104 +0,0 @@
"""
Copyright (c) Contributors to the Open 3D Engine Project.
For complete copyright and license terms please see the LICENSE at the root of this distribution.
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
"""
C2627906: A simple Vegetation Layer Blender area can be created.
The specified assets plant in the specified blend area and are visible in the Viewport in
Edit Mode, Game Mode.
"""
import os
import pytest
pytest.importorskip("ly_test_tools")
import ly_remote_console.remote_console_commands as remote_console_commands
import ly_test_tools.environment.waiter as waiter
import ly_test_tools.environment.file_system as file_system
import editor_python_test_tools.hydra_test_utils as hydra
test_directory = os.path.join(os.path.dirname(__file__), "EditorScripts")
remote_console_port = 4600
listener_timeout = 120
@pytest.mark.parametrize("project", ["AutomatedTesting"])
@pytest.mark.parametrize("level", ["tmp_level"])
@pytest.mark.usefixtures("automatic_process_killer")
class TestLayerBlender(object):
@pytest.fixture
def remote_console_instance(self, request):
console = remote_console_commands.RemoteConsole()
def teardown():
if console.connected:
console.stop()
request.addfinalizer(teardown)
return console
@pytest.mark.test_case_id("C2627906")
@pytest.mark.BAT
@pytest.mark.SUITE_periodic
@pytest.mark.dynveg_area
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
def test_LayerBlender_E2E_Editor(self, workspace, request, editor, project, level, launcher_platform):
# Make sure temp level doesn't already exist
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
expected_lines = [
"'Purple Spawner' created",
"'Pink Spawner' created",
"'Surface Entity' created",
"Entity has a Vegetation Layer Spawner component",
"Entity has a Vegetation Asset List component",
"Entity has a Box Shape component",
"Purple Spawner Box Shape|Box Configuration|Dimensions: SUCCESS",
"Pink Spawner Box Shape|Box Configuration|Dimensions: SUCCESS",
"Purple Spawner Configuration|Embedded Assets|[0]: SUCCESS",
"Pink Spawner Configuration|Embedded Assets|[0]: SUCCESS",
"'Blender' created",
"Entity has a Vegetation Layer Blender component",
"Entity has a Box Shape component",
"Blender Configuration|Vegetation Areas: SUCCESS",
"Blender Box Shape|Box Configuration|Dimensions: SUCCESS",
"LayerBlender_E2E_Editor: result=SUCCESS"
]
hydra.launch_and_validate_results(
request,
test_directory,
editor,
"LayerBlender_E2E_Editor.py",
expected_lines,
cfg_args=[level]
)
@pytest.mark.test_case_id("C2627906")
@pytest.mark.BAT
@pytest.mark.SUITE_periodic
@pytest.mark.dynveg_area
@pytest.mark.parametrize("launcher_platform", ['windows'])
def test_LayerBlender_E2E_Launcher(self, workspace, project, launcher, level, remote_console_instance,
launcher_platform):
launcher.args.extend(["-rhi=Null"])
launcher.start()
assert launcher.is_alive(), "Launcher failed to start"
# Wait for test script to quit the launcher. If wait_for returns exc, test was not successful
waiter.wait_for(lambda: not launcher.is_alive(), timeout=300)
# Verify launcher quit successfully and did not crash
ret_code = launcher.get_returncode()
assert ret_code == 0, "Test failed. See Game.log for details"
# Cleanup our temp level
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
@@ -1,56 +0,0 @@
"""
Copyright (c) Contributors to the Open 3D Engine Project.
For complete copyright and license terms please see the LICENSE at the root of this distribution.
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import pytest
import logging
# Bail on the test if ly_test_tools doesn't exist.
pytest.importorskip('ly_test_tools')
import ly_test_tools.environment.file_system as file_system
import editor_python_test_tools.hydra_test_utils as hydra
logger = logging.getLogger(__name__)
test_directory = os.path.join(os.path.dirname(__file__), 'EditorScripts')
@pytest.mark.parametrize("project", ["AutomatedTesting"])
@pytest.mark.parametrize("level", ["tmp_level"])
@pytest.mark.usefixtures("automatic_process_killer")
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
class TestLayerBlocker(object):
@pytest.fixture(autouse=True)
def setup_teardown(self, request, workspace, project, level):
def teardown():
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
request.addfinalizer(teardown)
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
@pytest.mark.test_case_id("C2793772")
@pytest.mark.SUITE_periodic
@pytest.mark.dynveg_area
def test_LayerBlocker_InstancesBlockedInConfiguredArea(self, request, editor, level, launcher_platform):
expected_lines = [
"'Instance Spawner' created",
"'Surface Entity' created",
"instance count validation: True (found=400, expected=400)",
"'Blocker Area' created",
"instance count validation: True (found=384, expected=384)",
"LayerBlocker_InstancesBlocked: result=SUCCESS"
]
hydra.launch_and_validate_results(
request,
test_directory,
editor,
"LayerBlocker_InstancesBlockedInConfiguredArea.py",
expected_lines,
cfg_args=[level]
)
@@ -1,142 +0,0 @@
"""
Copyright (c) Contributors to the Open 3D Engine Project.
For complete copyright and license terms please see the LICENSE at the root of this distribution.
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import pytest
import logging
# Bail on the test if ly_test_tools doesn't exist.
pytest.importorskip("ly_test_tools")
import ly_test_tools.environment.file_system as file_system
import editor_python_test_tools.hydra_test_utils as hydra
logger = logging.getLogger(__name__)
test_directory = os.path.join(os.path.dirname(__file__), "EditorScripts")
@pytest.mark.parametrize("project", ["AutomatedTesting"])
@pytest.mark.parametrize("level", ["tmp_level"])
@pytest.mark.usefixtures("automatic_process_killer")
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
class TestLayerSpawner(object):
@pytest.fixture(autouse=True)
def setup_teardown(self, request, workspace, project, level):
# Cleanup our temp level
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
def teardown():
# Cleanup our temp level
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
request.addfinalizer(teardown)
@pytest.mark.test_case_id("C4762381")
@pytest.mark.SUITE_periodic
@pytest.mark.dynveg_misc
def test_LayerSpawner_InheritBehaviorFlag(self, request, editor, level, workspace, launcher_platform):
expected_lines = [
"LayerSpawner_InheritBehavior: test started",
"LayerSpawner_InheritBehavior: Vegetation is not planted when Inherit Behavior flag is checked: True",
"LayerSpawner_InheritBehavior: Vegetation plant when Inherit Behavior flag is unchecked: True",
"LayerSpawner_InheritBehavior: result=SUCCESS",
]
hydra.launch_and_validate_results(
request,
test_directory,
editor,
"LayerSpawner_InheritBehaviorFlag.py",
expected_lines=expected_lines,
cfg_args=[level]
)
@pytest.mark.test_case_id("C2802020")
@pytest.mark.SUITE_periodic
@pytest.mark.dynveg_misc
def test_LayerSpawner_InstancesPlantInAllSupportedShapes(self, request, editor, level, launcher_platform):
expected_lines = [
"'Instance Spawner' created",
"'Surface Entity' created",
"Entity has a Vegetation Reference Shape component",
"Entity has a Box Shape component",
"box Box Shape|Box Configuration|Dimensions: SUCCESS",
"Entity has a Capsule Shape component",
"capsule Capsule Shape|Capsule Configuration|Height: SUCCESS",
"capsule Capsule Shape|Capsule Configuration|Radius: SUCCESS",
"Entity has a Tube Shape component",
"Entity has a Spline component",
"Entity has a Sphere Shape component",
"sphere Sphere Shape|Sphere Configuration|Radius: SUCCESS",
"Entity has a Cylinder Shape component",
"cylinder Cylinder Shape|Cylinder Configuration|Radius: SUCCESS",
"cylinder Cylinder Shape|Cylinder Configuration|Height: SUCCESS",
"Entity has a Polygon Prism Shape component",
"Entity has a Compound Shape component",
"Compound Configuration|Child Shape Entities|[0]: SUCCESS",
"Compound Configuration|Child Shape Entities|[1]: SUCCESS",
"Compound Configuration|Child Shape Entities|[2]: SUCCESS",
"Compound Configuration|Child Shape Entities|[3]: SUCCESS",
"Compound Configuration|Child Shape Entities|[4]: SUCCESS",
"Compound Configuration|Child Shape Entities|[5]: SUCCESS",
"Instance Spawner Configuration|Shape Entity Id: SUCCESS",
"TestLayerSpawner_AllShapesPlant: result=SUCCESS",
]
hydra.launch_and_validate_results(
request,
test_directory,
editor,
"LayerSpawner_InstancesPlantInAllSupportedShapes.py",
expected_lines,
cfg_args=[level]
)
@pytest.mark.test_case_id("C4765973")
@pytest.mark.SUITE_periodic
@pytest.mark.dynveg_misc
@pytest.mark.xfail(reason="https://github.com/o3de/o3de/issues/2303")
def test_LayerSpawner_FilterStageToggle(self, request, editor, level, workspace, launcher_platform):
expected_lines = [
"LayerSpawner_FilterStageToggle: test started",
"LayerSpawner_FilterStageToggle: Preprocess filter stage vegetation instance count is as expected: True",
"LayerSpawner_FilterStageToggle: Postprocess filter vegetation instance stage count is as expected: True",
"LayerSpawner_FilterStageToggle: result=SUCCESS",
]
hydra.launch_and_validate_results(
request,
test_directory,
editor,
"LayerSpawner_FilterStageToggle.py",
expected_lines=expected_lines,
cfg_args=[level]
)
@pytest.mark.test_case_id("C30000751")
@pytest.mark.SUITE_sandbox
@pytest.mark.dynveg_misc
@pytest.mark.skip # https://github.com/o3de/o3de/issues/2038
def test_LayerSpawner_InstancesRefreshUsingCorrectViewportCamera(self, request, editor, level, launcher_platform):
expected_lines = [
"LayerSpawner_InstanceCameraRefresh: test started",
"LayerSpawner_InstanceCameraRefresh: test finished",
"LayerSpawner_InstanceCameraRefresh: result=SUCCESS"
]
hydra.launch_and_validate_results(
request,
test_directory,
editor,
"LayerSpawner_InstancesRefreshUsingCorrectViewportCamera.py",
expected_lines,
cfg_args=[level],
null_renderer=False
)
@@ -1,89 +0,0 @@
"""
Copyright (c) Contributors to the Open 3D Engine Project.
For complete copyright and license terms please see the LICENSE at the root of this distribution.
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import logging
import os
import pytest
# Bail on the test if ly_test_tools doesn't exist.
pytest.importorskip('ly_test_tools')
import editor_python_test_tools.hydra_test_utils as hydra
import ly_test_tools.environment.file_system as file_system
test_directory = os.path.join(os.path.dirname(__file__), 'EditorScripts')
logger = logging.getLogger(__name__)
@pytest.mark.parametrize("project", ["AutomatedTesting"])
@pytest.mark.parametrize("level", ["tmp_level"])
@pytest.mark.usefixtures("automatic_process_killer")
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
class TestMeshBlocker(object):
@pytest.fixture(autouse=True)
def setup_teardown(self, request, workspace, editor, project, level):
pass
def teardown():
# delete temp level
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
# Setup - add the teardown finalizer
request.addfinalizer(teardown)
# Make sure the temp level doesn't already exist
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
"""
C3980834: A simple Vegetation Blocker Mesh can be created
"""
@pytest.mark.test_case_id("C3980834")
@pytest.mark.SUITE_periodic
@pytest.mark.dynveg_area
@pytest.mark.xfail # LYN-3273
def test_MeshBlocker_InstancesBlockedByMesh(self, request, editor, level, launcher_platform):
expected_lines = [
"'Instance Spawner' created",
"'Surface Entity' created",
"'Blocker Entity' created",
"instance count validation: True (found=160, expected=160)",
"MeshBlocker_InstancesBlockedByMesh: result=SUCCESS"
]
hydra.launch_and_validate_results(
request,
test_directory,
editor,
"MeshBlocker_InstancesBlockedByMesh.py",
expected_lines,
cfg_args=[level]
)
"""
C4766030: Mesh Height Percent Min/Max values can be set to fine tune the blocked area
"""
@pytest.mark.test_case_id("C4766030")
@pytest.mark.SUITE_periodic
@pytest.mark.dynveg_area
@pytest.mark.xfail # LYN-3273
def test_MeshBlocker_InstancesBlockedByMeshHeightTuning(self, request, editor, level, launcher_platform):
expected_lines = [
"'Instance Spawner' created",
"'Surface Entity' created",
"'Blocker Entity' created",
"Blocker Entity Configuration|Mesh Height Percent Max: SUCCESS",
"instance count validation: True (found=127, expected=127)",
"MeshBlocker_InstancesBlockedByMeshHeightTuning: result=SUCCESS",
]
hydra.launch_and_validate_results(
request,
test_directory,
editor,
"MeshBlocker_InstancesBlockedByMeshHeightTuning.py",
expected_lines,
cfg_args=[level]
)
@@ -1,82 +0,0 @@
"""
Copyright (c) Contributors to the Open 3D Engine Project.
For complete copyright and license terms please see the LICENSE at the root of this distribution.
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import pytest
import logging
# Bail on the test if ly_test_tools doesn't exist.
pytest.importorskip("ly_test_tools")
import ly_test_tools.environment.file_system as file_system
import editor_python_test_tools.hydra_test_utils as hydra
logger = logging.getLogger(__name__)
test_directory = os.path.join(os.path.dirname(__file__), "EditorScripts")
@pytest.mark.parametrize('project', ['AutomatedTesting'])
@pytest.mark.parametrize('level', ['tmp_level'])
@pytest.mark.usefixtures("automatic_process_killer")
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
class TestMeshSurfaceTagEmitter(object):
@pytest.fixture(autouse=True)
def setup_teardown(self, request, workspace, project, level):
def teardown():
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
request.addfinalizer(teardown)
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
@pytest.mark.test_case_id("C2908172")
@pytest.mark.SUITE_periodic
@pytest.mark.dynveg_surfacetagemitter
def test_MeshSurfaceTagEmitter_DependentOnMeshComponent(self, request, editor, level, launcher_platform):
expected_lines = [
"Entity has a Mesh Surface Tag Emitter component",
"New Entity Created",
"Mesh Surface Tag Emitter is Disabled",
"Entity has a Mesh component",
"Mesh Surface Tag Emitter is Enabled",
"MeshSurfaceTagEmitter_DependentOnMeshComponent: result=SUCCESS"
]
unexpected_lines = [
"Mesh Surface Tag Emitter is Enabled. But It should be disabled before adding Mesh",
"Mesh Surface Tag Emitter is Disabled. But It should be enabled after adding Mesh",
]
hydra.launch_and_validate_results(
request,
test_directory,
editor,
"MeshSurfaceTagEmitter_DependentOnMeshComponent.py",
expected_lines,
unexpected_lines,
cfg_args=[level]
)
@pytest.mark.test_case_id("C2908174")
@pytest.mark.SUITE_periodic
@pytest.mark.dynveg_surfacetagemitter
def test_MeshSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully(self, request, editor, level, launcher_platform):
expected_lines = [
"Added SurfaceTag: container count is 1",
"Removed SurfaceTag: container count is 0",
"MeshSurfaceTagEmitter_SurfaceTagsAddRemoveSucessfully: result=SUCCESS"
]
hydra.launch_and_validate_results(
request,
test_directory,
editor,
"MeshSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully.py",
expected_lines,
cfg_args=[level]
)
@@ -1,53 +0,0 @@
"""
Copyright (c) Contributors to the Open 3D Engine Project.
For complete copyright and license terms please see the LICENSE at the root of this distribution.
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import pytest
import logging
# Bail on the test if ly_test_tools doesn't exist.
pytest.importorskip("ly_test_tools")
import ly_test_tools.environment.file_system as file_system
import editor_python_test_tools.hydra_test_utils as hydra
logger = logging.getLogger(__name__)
test_directory = os.path.join(os.path.dirname(__file__), "EditorScripts")
@pytest.mark.parametrize('project', ['AutomatedTesting'])
@pytest.mark.parametrize('level', ['tmp_level'])
@pytest.mark.usefixtures("automatic_process_killer")
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
class TestPhysXColliderSurfaceTagEmitter(object):
@pytest.fixture(autouse=True)
def setup_teardown(self, request, workspace, project, level):
def teardown():
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
request.addfinalizer(teardown)
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
@pytest.mark.test_case_id("C29053640")
@pytest.mark.SUITE_periodic
@pytest.mark.dynveg_surfacetagemitter
def test_PhysXColliderSurfaceTagEmitter_E2E_Editor(self, request, editor, level, launcher_platform):
expected_lines = [
"PhysXColliderSurfaceTagEmitter_E2E_Editor: test started",
"PhysXColliderSurfaceTagEmitter_E2E_Editor: test finished",
"PhysXColliderSurfaceTagEmitter_E2E_Editor: result=SUCCESS"
]
hydra.launch_and_validate_results(
request,
test_directory,
editor,
"PhysXColliderSurfaceTagEmitter_E2E_Editor.py",
expected_lines=expected_lines,
cfg_args=[level]
)
@@ -1,79 +0,0 @@
"""
Copyright (c) Contributors to the Open 3D Engine Project.
For complete copyright and license terms please see the LICENSE at the root of this distribution.
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import pytest
import logging
# Bail on the test if ly_test_tools doesn't exist.
pytest.importorskip('ly_test_tools')
import ly_test_tools.environment.file_system as file_system
import editor_python_test_tools.hydra_test_utils as hydra
logger = logging.getLogger(__name__)
test_directory = os.path.join(os.path.dirname(__file__), 'EditorScripts')
@pytest.mark.parametrize("project", ["AutomatedTesting"])
@pytest.mark.parametrize("level", ["tmp_level"])
@pytest.mark.usefixtures("automatic_process_killer")
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
class TestPositionModifier(object):
@pytest.fixture(autouse=True)
def setup_teardown(self, request, workspace, project, level):
def teardown():
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
request.addfinalizer(teardown)
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
@pytest.mark.test_case_id("C4874099", "C4814461")
@pytest.mark.SUITE_periodic
@pytest.mark.dynveg_modifier
def test_PositionModifier_ComponentAndOverrides_InstancesPlantAtSpecifiedOffsets(self, request, editor, level,
launcher_platform):
expected_lines = [
"'Instance Spawner' created",
"Vegetation Position Modifier component was added to entity",
"'Planting Surface' created",
"Entity has a Constant Gradient component",
"PositionModifierComponentAndOverrides_InstanceOffset: result=SUCCESS",
]
hydra.launch_and_validate_results(
request,
test_directory,
editor,
"PositionModifier_ComponentAndOverrides_InstancesPlantAtSpecifiedOffsets.py",
expected_lines,
cfg_args=[level]
)
@pytest.mark.test_case_id("C4874100")
@pytest.mark.SUITE_periodic
@pytest.mark.dynveg_modifier
@pytest.mark.xfail(reason="https://github.com/o3de/o3de/issues/2303")
def test_PositionModifier_AutoSnapToSurfaceWorks(self, request, editor, level, launcher_platform):
expected_lines = [
"'Instance Spawner' created",
"'Planting Surface' created",
"Instance Spawner Configuration|Position X|Range Min: SUCCESS",
"Instance Spawner Configuration|Position X|Range Max: SUCCESS",
"PositionModifier_AutoSnapToSurface: result=SUCCESS"
]
hydra.launch_and_validate_results(
request,
test_directory,
editor,
"PositionModifier_AutoSnapToSurfaceWorks.py",
expected_lines,
cfg_args=[level]
)
@@ -1,97 +0,0 @@
"""
Copyright (c) Contributors to the Open 3D Engine Project.
For complete copyright and license terms please see the LICENSE at the root of this distribution.
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import logging
import os
import pytest
# Bail on the test if ly_test_tools doesn't exist.
pytest.importorskip("ly_test_tools")
import editor_python_test_tools.hydra_test_utils as hydra
import ly_test_tools.environment.file_system as file_system
logger = logging.getLogger(__name__)
test_directory = os.path.join(os.path.dirname(__file__), "EditorScripts")
@pytest.mark.parametrize("project", ["AutomatedTesting"])
@pytest.mark.parametrize("level", ["tmp_level"])
@pytest.mark.usefixtures("automatic_process_killer")
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
class TestRotationModifier(object):
@pytest.fixture(autouse=True)
def setup_teardown(self, request, workspace, project, level):
def teardown():
# delete temp level
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
# Setup - add the teardown finalizer
request.addfinalizer(teardown)
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
@pytest.mark.test_case_id("C4896922")
@pytest.mark.SUITE_periodic
@pytest.mark.dynveg_modifier
def test_RotationModifier_InstancesRotateWithinRange(self, request, editor, level, launcher_platform) -> None:
"""
Launches editor and run test script to test that rotation modifier works for all axis.
Manual test case: C4896922
"""
expected_lines = [
"'Spawner Entity' created",
"'Surface Entity' created",
"'Gradient Entity' created",
"Entity has a Vegetation Asset List component",
"Entity has a Vegetation Layer Spawner component",
"Entity has a Vegetation Rotation Modifier component",
"Entity has a Box Shape component",
"Entity has a Constant Gradient component",
"RotationModifier_InstancesRotateWithinRange: result=SUCCESS"
]
hydra.launch_and_validate_results(
request,
test_directory,
editor,
"RotationModifier_InstancesRotateWithinRange.py",
expected_lines,
cfg_args=[level]
)
@pytest.mark.test_case_id("C4814460")
@pytest.mark.SUITE_periodic
@pytest.mark.dynveg_modifier
def test_RotationModifierOverrides_InstancesRotateWithinRange(self, request, editor, level, launcher_platform) -> None:
expected_lines = [
"'Spawner Entity' created",
"'Surface Entity' created",
"'Gradient Entity' created",
"Entity has a Vegetation Layer Spawner component",
"Entity has a Vegetation Asset List component",
"Spawner Entity Box Shape|Box Configuration|Dimensions: SUCCESS",
"Entity has a Vegetation Rotation Modifier component",
"Spawner Entity Configuration|Embedded Assets|[0]|Rotation Modifier|Override Enabled: SUCCESS",
"Spawner Entity Configuration|Allow Per-Item Overrides: SUCCESS",
"Entity has a Constant Gradient component",
"Entity has a Box Shape component",
"Spawner Entity Configuration|Rotation Z|Gradient|Gradient Entity Id: SUCCESS",
"RotationModifierOverrides_InstancesRotateWithinRange: result=SUCCESS"
]
hydra.launch_and_validate_results(
request,
test_directory,
editor,
"RotationModifierOverrides_InstancesRotateWithinRange.py",
expected_lines,
cfg_args=[level]
)
@@ -1,91 +0,0 @@
"""
Copyright (c) Contributors to the Open 3D Engine Project.
For complete copyright and license terms please see the LICENSE at the root of this distribution.
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
"""
C4814462: Vegetation instances have random scale between 0.1 and 1.0 applied.
"""
import os
import pytest
# Bail on the test if ly_test_tools doesn't exist.
pytest.importorskip("ly_test_tools")
import editor_python_test_tools.hydra_test_utils as hydra
import ly_test_tools.environment.file_system as file_system
test_directory = os.path.join(os.path.dirname(__file__), "EditorScripts")
@pytest.mark.parametrize("project", ["AutomatedTesting"])
@pytest.mark.parametrize("level", ["tmp_level"])
@pytest.mark.usefixtures("automatic_process_killer")
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
class TestScaleOverrideWorksSuccessfully(object):
@pytest.fixture(autouse=True)
def setup_teardown(self, request, workspace, project, level):
def teardown():
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
request.addfinalizer(teardown)
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
@pytest.mark.test_case_id("C4814462")
@pytest.mark.SUITE_periodic
@pytest.mark.dynveg_modifier
def test_ScaleModifierOverrides_InstancesProperlyScale(self, request, editor, level, launcher_platform):
expected_lines = [
"'Spawner Entity' created",
"'Surface Entity' created",
"Entity has a Vegetation Scale Modifier component",
"'Gradient Entity' created",
"Scale Min and Scale Max are set to 0.1 and 1.0 in Vegetation Asset List",
"Entity has a Random Noise Gradient component",
"Entity has a Gradient Transform Modifier component",
"Entity has a Box Shape component",
"Spawner Entity Configuration|Gradient|Gradient Entity Id: SUCCESS",
"ScaleModifierOverrides_InstancesProperlyScale: result=SUCCESS"
]
unexpected_lines = ["Scale Min and Scale Max are not set to 0.1 and 1.0 in Vegetation Asset List"]
hydra.launch_and_validate_results(
request,
test_directory,
editor,
"ScaleModifierOverrides_InstancesProperlyScale.py",
expected_lines,
unexpected_lines,
cfg_args=[level]
)
@pytest.mark.test_case_id("C4896937")
@pytest.mark.SUITE_periodic
@pytest.mark.dynveg_modifier
def test_ScaleModifier_InstancesProperlyScale(self, request, editor, level, launcher_platform):
expected_lines = [
"'Spawner Entity' created",
"Entity has a Vegetation Scale Modifier component",
"'Surface Entity' created",
"'Gradient Entity' created",
"Spawner Entity Configuration|Gradient|Gradient Entity Id: SUCCESS",
"Spawner Entity Configuration|Range Min: SUCCESS",
"Spawner Entity Configuration|Range Max: SUCCESS",
"ScaleModifier_InstancesProperlyScale: result=SUCCESS"
]
hydra.launch_and_validate_results(
request,
test_directory,
editor,
"ScaleModifier_InstancesProperlyScale.py",
expected_lines,
cfg_args=[level]
)
@@ -1,56 +0,0 @@
"""
Copyright (c) Contributors to the Open 3D Engine Project.
For complete copyright and license terms please see the LICENSE at the root of this distribution.
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import pytest
import logging
# Bail on the test if ly_test_tools doesn't exist.
pytest.importorskip('ly_test_tools')
import ly_test_tools.environment.file_system as file_system
import editor_python_test_tools.hydra_test_utils as hydra
logger = logging.getLogger(__name__)
test_directory = os.path.join(os.path.dirname(__file__), 'EditorScripts')
@pytest.mark.parametrize("project", ["AutomatedTesting"])
@pytest.mark.parametrize("level", ["tmp_level"])
@pytest.mark.usefixtures("automatic_process_killer")
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
class TestShapeIntersectionFilter(object):
@pytest.fixture(autouse=True)
def setup_teardown(self, request, workspace, project, level):
def teardown():
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
request.addfinalizer(teardown)
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
@pytest.mark.test_case_id("C4874094")
@pytest.mark.SUITE_periodic
@pytest.mark.dynveg_filter
def test_ShapeIntersectionFilter_InstancesPlantInAssignedShape(self, request, editor, level, launcher_platform):
expected_lines = [
"'Instance Spawner' created",
"'Planting Surface' created",
"instance count validation: True (found=49, expected=49)",
"instance count validation: True (found=121, expected=121)",
"instance count validation: True (found=400, expected=400)",
"ShapeIntersectionFilter_InstancePlanting: result=SUCCESS"
]
hydra.launch_and_validate_results(
request,
test_directory,
editor,
"ShapeIntersectionFilter_InstancesPlantInAssignedShape.py",
expected_lines,
cfg_args=[level]
)
@@ -1,82 +0,0 @@
"""
Copyright (c) Contributors to the Open 3D Engine Project.
For complete copyright and license terms please see the LICENSE at the root of this distribution.
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
"""
C4896941 - Surface Alignment functions as expected
C4814459 - Surface Alignment overrides function as expected
"""
import os
import pytest
# Bail on the test if ly_test_tools doesn't exist.
pytest.importorskip("ly_test_tools")
import editor_python_test_tools.hydra_test_utils as hydra
import ly_test_tools.environment.file_system as file_system
test_directory = os.path.join(os.path.dirname(__file__), "EditorScripts")
@pytest.mark.parametrize("project", ["AutomatedTesting"])
@pytest.mark.parametrize("level", ["tmp_level"])
@pytest.mark.usefixtures("automatic_process_killer")
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
class TestSlopeAlignmentModifier(object):
@pytest.fixture(autouse=True)
def setup_teardown(self, request, workspace, project, level):
def teardown():
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
request.addfinalizer(teardown)
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
@pytest.mark.test_case_id("C4896941")
@pytest.mark.SUITE_periodic
@pytest.mark.dynveg_modifier
@pytest.mark.xfail(reason="https://github.com/o3de/o3de/issues/2303")
def test_SlopeAlignmentModifier_InstanceSurfaceAlignment(self, request, editor, level, launcher_platform):
expected_lines = [
"Vegetation Slope Alignment Modifier component was added to entity",
"Instance Spawner Configuration|Alignment Coefficient Min: SUCCESS",
"Constant Gradient component was added to entity",
"Instance Spawner Configuration|Gradient|Gradient Entity Id: SUCCESS",
"SlopeAlignmentModifier: result=SUCCESS"
]
hydra.launch_and_validate_results(
request,
test_directory,
editor,
"SlopeAlignmentModifier_InstanceSurfaceAlignment.py",
expected_lines,
cfg_args=[level]
)
@pytest.mark.test_case_id("C4814459")
@pytest.mark.SUITE_periodic
@pytest.mark.dynveg_modifier
@pytest.mark.xfail(reason="https://github.com/o3de/o3de/issues/2303")
def test_SlopeAlignmentModifierOverrides_InstanceSurfaceAlignment(self, request, editor, level, launcher_platform):
expected_lines = [
"Instance Spawner Configuration|Allow Per-Item Overrides: SUCCESS",
"Instance Spawner Configuration|Embedded Assets|[0]|Surface Slope Alignment|Override Enabled: SUCCESS",
"Instance Spawner Configuration|Embedded Assets|[0]|Surface Slope Alignment|Max: SUCCESS",
"Instance Spawner Configuration|Embedded Assets|[0]|Surface Slope Alignment|Min: SUCCESS",
"SlopeAlignmentModifierOverrides: result=SUCCESS"
]
hydra.launch_and_validate_results(
request,
test_directory,
editor,
"SlopeAlignmentModifierOverrides_InstanceSurfaceAlignment.py",
expected_lines,
cfg_args=[level]
)
@@ -1,96 +0,0 @@
"""
Copyright (c) Contributors to the Open 3D Engine Project.
For complete copyright and license terms please see the LICENSE at the root of this distribution.
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import pytest
import logging
# Bail on the test if ly_test_tools doesn't exist.
pytest.importorskip("ly_test_tools")
import ly_test_tools.environment.file_system as file_system
import editor_python_test_tools.hydra_test_utils as hydra
logger = logging.getLogger(__name__)
test_directory = os.path.join(os.path.dirname(__file__), "EditorScripts")
@pytest.mark.parametrize("project", ["AutomatedTesting"])
@pytest.mark.parametrize("level", ["tmp_level"])
@pytest.mark.usefixtures("automatic_process_killer")
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
class TestSlopeFilter(object):
@pytest.fixture(autouse=True)
def setup_teardown(self, request, workspace, project, level):
# Cleanup our temp level
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
def teardown():
# Cleanup our temp level
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
request.addfinalizer(teardown)
@pytest.mark.test_case_id("C4874097")
@pytest.mark.SUITE_periodic
@pytest.mark.dynveg_filter
def test_SlopeFilter_FilterStageToggle(self, request, editor, level, workspace, launcher_platform):
cfg_args = [level]
expected_lines = [
"SlopeFilter_FilterStageToggle: test started",
"SlopeFilter_FilterStageToggle: Vegetation plant only in the areas where the Box overlaps with the vegetation area's boundaries: True",
"SlopeFilter_FilterStageToggle: Vegetation plant only in the areas where the Cylinder overlaps with the vegetation area's boundaries: True",
"SlopeFilter_FilterStageToggle: Vegetation instances count equal to expected value for PREPROCESS filter stage: True",
"SlopeFilter_FilterStageToggle: Vegetation instances count equal to expected value for POSTPROCESS filter stage: True",
"SlopeFilter_FilterStageToggle: result=SUCCESS",
]
unexpected_lines = [
"SlopeFilter_FilterStageToggle: Vegetation plant only in the areas where the Box overlaps with the vegetation area's boundaries: False",
"SlopeFilter_FilterStageToggle: Vegetation plant only in the areas where the Cylinder overlaps with the vegetation area's boundaries: False",
"SlopeFilter_FilterStageToggle: Vegetation instances count equal to expected value for PREPROCESS filter stage: False",
"SlopeFilter_FilterStageToggle: Vegetation instances count equal to expected value for POSTPROCESS filter stage: False",
]
hydra.launch_and_validate_results(
request,
test_directory,
editor,
"SlopeFilter_FilterStageToggle.py",
expected_lines=expected_lines,
unexpected_lines=unexpected_lines,
cfg_args=cfg_args
)
@pytest.mark.test_case_id("C4814464", "C4874096")
@pytest.mark.SUITE_periodic
@pytest.mark.dynveg_filter
@pytest.mark.xfail(reason="https://github.com/o3de/o3de/issues/2303")
def test_SlopeFilter_ComponentAndOverrides_InstancesPlantOnValidSlopes(self, request, editor, level,
launcher_platform):
expected_lines = [
"'Instance Spawner' created",
"'Planting Surface' created",
"'Sloped Planting Surface' created",
"instance count validation: True (found=1720, expected=1720)",
"Instance Spawner Configuration|Slope Min: SUCCESS",
"Instance Spawner Configuration|Slope Max: SUCCESS",
"instance count validation: True (found=48, expected=48)",
"Instance Spawner Configuration|Embedded Assets|[0]|Slope Filter|Min: SUCCESS",
"Instance Spawner Configuration|Embedded Assets|[0]|Slope Filter|Max: SUCCESS",
"instance count validation: True (found=12, expected=12)",
"SlopeFilter_InstancesPlantOnValidSlope: result=SUCCESS"
]
hydra.launch_and_validate_results(
request,
test_directory,
editor,
"SlopeFilter_ComponentAndOverrides_InstancesPlantOnValidSlope.py",
expected_lines,
cfg_args=[level]
)
@@ -1,150 +0,0 @@
"""
Copyright (c) Contributors to the Open 3D Engine Project.
For complete copyright and license terms please see the LICENSE at the root of this distribution.
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import pytest
# Bail on the test if ly_test_tools doesn't exist.
pytest.importorskip("ly_test_tools")
import editor_python_test_tools.hydra_test_utils as hydra
import ly_test_tools.environment.file_system as file_system
test_directory = os.path.join(os.path.dirname(__file__), "EditorScripts")
@pytest.mark.parametrize("project", ["AutomatedTesting"])
@pytest.mark.parametrize("level", ["tmp_level"])
@pytest.mark.usefixtures("automatic_process_killer")
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
class TestSurfaceMaskFilter(object):
@pytest.fixture(autouse=True)
def setup_teardown(self, request, workspace, project, level):
def teardown():
# delete temp level
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
# Setup - add the teardown finalizer
request.addfinalizer(teardown)
# Make sure the temp level doesn't already exist
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
# Simple validation test to ensure that SurfaceTag can be created, set to a value, and compared to another SurfaceTag.
@pytest.mark.SUITE_periodic
@pytest.mark.dynveg_filter
def test_SurfaceMaskFilter_BasicSurfaceTagCreation(self, request, level, editor, launcher_platform):
expected_lines = [
"SurfaceTag test started",
"SurfaceTag equal tag comparison is True expected True",
"SurfaceTag not equal tag comparison is False expected False",
"SurfaceTag test finished",
"TestSurfaceMaskFilter_BasicSurfaceTagCreation: result=SUCCESS"
]
hydra.launch_and_validate_results(
request,
test_directory,
editor,
'SurfaceMaskFilter_BasicSurfaceTagCreation.py',
expected_lines=expected_lines,
cfg_args=[level]
)
@pytest.mark.test_case_id("C2561342")
@pytest.mark.SUITE_periodic
@pytest.mark.dynveg_filter
def test_SurfaceMaskFilter_ExclusiveSurfaceTags_Function(self, request, editor, level, launcher_platform):
expected_lines = [
"'Instance Spawner' created",
"Instance Spawner Box Shape|Box Configuration|Dimensions: SUCCESS",
"Instance Spawner Configuration|Embedded Assets|[0]: SUCCESS",
"'Surface Entity 1' created",
"Surface Entity 1 Box Shape|Box Configuration|Dimensions: SUCCESS",
"Surface Entity 1 Configuration|Generated Tags: SUCCESS",
"'Surface Entity 2' created",
"Surface Entity 2 Box Shape|Box Configuration|Dimensions: SUCCESS",
"Surface Entity 2 Configuration|Generated Tags: SUCCESS",
"SurfaceMaskFilter_ExclusionList: Expected 39 instances - Found 39 instances",
"Instance Spawner Configuration|Exclusion|Weight Max: SUCCESS",
"SurfaceMaskFilter_ExclusionList: Expected 169 instances - Found 169 instances",
"SurfaceMaskFilter_ExclusionList: result=SUCCESS"
]
unexpected_lines = ["Failed to add an Exclusive surface mask filter of terrainHole"]
hydra.launch_and_validate_results(
request,
test_directory,
editor,
"SurfaceMaskFilter_ExclusionList.py",
expected_lines,
unexpected_lines,
cfg_args=[level]
)
@pytest.mark.test_case_id("C2561341")
@pytest.mark.SUITE_periodic
@pytest.mark.dynveg_filter
def test_SurfaceMaskFilter_InclusiveSurfaceTags_Function(self, request, editor, level, launcher_platform):
expected_lines = [
"'Instance Spawner' created",
"Instance Spawner Box Shape|Box Configuration|Dimensions: SUCCESS",
"Instance Spawner Configuration|Embedded Assets|[0]: SUCCESS",
"'Surface Entity 1' created",
"Surface Entity 1 Box Shape|Box Configuration|Dimensions: SUCCESS",
"Surface Entity 1 Configuration|Generated Tags: SUCCESS",
"'Surface Entity 2' created",
"Surface Entity 2 Box Shape|Box Configuration|Dimensions: SUCCESS",
"Surface Entity 2 Configuration|Generated Tags: SUCCESS",
"SurfaceMaskFilter_InclusionList: Expected 130 instances - Found 130 instances",
"Instance Spawner Configuration|Inclusion|Weight Max: SUCCESS",
"SurfaceMaskFilter_InclusionList: Expected 0 instances - Found 0 instances",
"SurfaceMaskFilter_InclusionList: result=SUCCESS"
]
unexpected_lines = ["Failed to add an Inclusive surface mask filter of terrainHole"]
hydra.launch_and_validate_results(
request,
test_directory,
editor,
"SurfaceMaskFilter_InclusionList.py",
expected_lines,
unexpected_lines,
cfg_args=[level]
)
@pytest.mark.test_case_id("C3711666")
@pytest.mark.SUITE_periodic
@pytest.mark.dynveg_filter
def test_SurfaceMaskFilterOverrides_MultipleDescriptorOverridesPlantAsExpected(self, request, editor, level,
launcher_platform):
expected_lines = [
"'Instance Spawner' created",
"'Surface Entity A' created",
"'Surface Entity B' created",
"'Surface Entity C' created",
"instance count validation: True (found=725, expected=725)",
"instance count validation: True (found=400, expected=400)",
"instance count validation: True (found=225, expected=225)",
"instance count validation: True (found=100, expected=100)",
"SurfaceMaskFilter_MultipleDescriptorOverrides: result=SUCCESS"
]
hydra.launch_and_validate_results(
request,
test_directory,
editor,
"SurfaceMaskFilterOverrides_MultipleDescriptorOverridesPlantAsExpected.py",
expected_lines,
cfg_args=[level]
)
@@ -1,87 +0,0 @@
"""
Copyright (c) Contributors to the Open 3D Engine Project.
For complete copyright and license terms please see the LICENSE at the root of this distribution.
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import pytest
import logging
# Bail on the test if ly_test_tools doesn't exist.
pytest.importorskip("ly_test_tools")
import ly_test_tools.environment.file_system as file_system
import editor_python_test_tools.hydra_test_utils as hydra
logger = logging.getLogger(__name__)
test_directory = os.path.join(os.path.dirname(__file__), "EditorScripts")
@pytest.mark.parametrize("project", ["AutomatedTesting"])
@pytest.mark.parametrize("level", ["tmp_level"])
@pytest.mark.usefixtures("automatic_process_killer")
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
class TestSystemSettings(object):
@pytest.fixture(autouse=True)
def setup_teardown(self, request, workspace, project, level):
def teardown():
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
request.addfinalizer(teardown)
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
@pytest.mark.test_case_id("C2646869")
@pytest.mark.SUITE_periodic
@pytest.mark.dynveg_misc
def test_SystemSettings_SectorPointDensity(self, request, editor, level, launcher_platform):
expected_lines = [
"SystemSettings_SectorPointDensity: test started",
"SystemSettings_SectorPointDensity: Vegetation instances count equal to expected value before changing sector point density: True",
"SystemSettings_SectorPointDensity: Vegetation instances count equal to expected value after changing sector point density: True",
"SystemSettings_SectorPointDensity: result=SUCCESS",
]
unexpected_lines = [
"SystemSettings_SectorPointDensity: Vegetation instances count equal to expected value before changing sector point density: False",
"SystemSettings_SectorPointDensity: Vegetation instances count equal to expected value after changing sector point density: False",
]
hydra.launch_and_validate_results(
request,
test_directory,
editor,
"SystemSettings_SectorPointDensity.py",
expected_lines,
unexpected_lines=unexpected_lines,
cfg_args=[level]
)
@pytest.mark.test_case_id("C2646870")
@pytest.mark.SUITE_periodic
@pytest.mark.dynveg_misc
def test_SystemSettings_SectorSize(self, request, editor, level, launcher_platform):
expected_lines = [
"SystemSettings_SectorSize: test started",
"SystemSettings_SectorSize: Vegetation instances count equal to expected value before changing sector size: True",
"SystemSettings_SectorSize: Vegetation instances count equal to expected value after changing sector size: True",
"SystemSettings_SectorSize: result=SUCCESS",
]
unexpected_lines = [
"SystemSettings_SectorSize: Vegetation instances count equal to expected value before changing sector size: False",
"SystemSettings_SectorSize: Vegetation instances count equal to expected value after changing sector size: False",
]
hydra.launch_and_validate_results(
request,
test_directory,
editor,
"SystemSettings_SectorSize.py",
expected_lines,
unexpected_lines=unexpected_lines,
cfg_args=[level]
)
+1 -1
View File
@@ -29,11 +29,11 @@ include(cmake/PAL.cmake)
include(cmake/PALTools.cmake)
include(cmake/RuntimeDependencies.cmake)
include(cmake/Configurations.cmake) # Requires to be after PAL so we get platform variable definitions
include(cmake/Install.cmake)
include(cmake/Dependencies.cmake)
include(cmake/Deployment.cmake)
include(cmake/3rdParty.cmake)
include(cmake/LYPython.cmake)
include(cmake/Install.cmake)
include(cmake/LYWrappers.cmake)
include(cmake/Gems.cmake)
include(cmake/UnitTest.cmake)
@@ -140,9 +140,6 @@
<property name="horizontalScrollMode">
<enum>QAbstractItemView::ScrollPerPixel</enum>
</property>
<property name="showGrid">
<bool>false</bool>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
@@ -201,6 +198,11 @@
<header>AzToolsFramework/AssetBrowser/Search/SearchWidget.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>AzQtComponents::TableView</class>
<extends>QTreeView</extends>
<header>AzQtComponents/Components/Widgets/TableView.h</header>
</customwidget>
<customwidget>
<class>AzToolsFramework::AssetBrowser::AssetBrowserTreeView</class>
<extends>QTreeView</extends>
@@ -214,7 +216,7 @@
</customwidget>
<customwidget>
<class>AzToolsFramework::AssetBrowser::AssetBrowserTableView</class>
<extends>QTableView</extends>
<extends>AzQtComponents::TableView</extends>
<header>AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h</header>
</customwidget>
</customwidgets>
+12 -2
View File
@@ -8,11 +8,21 @@
#include "QtEditorApplication.h"
#ifdef PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
#include <AzFramework/API/ApplicationAPI_Linux.h>
#endif
namespace Editor
{
bool EditorQtApplication::nativeEventFilter(const QByteArray& , void* , long* )
bool EditorQtApplication::nativeEventFilter([[maybe_unused]] const QByteArray& eventType, void* message, long*)
{
// TODO_KDAB_LINUX
if (GetIEditor()->IsInGameMode())
{
#ifdef PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
AzFramework::LinuxXcbEventHandlerBus::Broadcast(&AzFramework::LinuxXcbEventHandler::HandleXcbEvent, static_cast<xcb_generic_event_t*>(message));
#endif
return true;
}
return false;
}
}
+14 -2
View File
@@ -34,10 +34,14 @@ namespace EditorInternal
: ToolsApplication(argc, argv)
{
EditorToolsApplicationRequests::Bus::Handler::BusConnect();
AzToolsFramework::ViewportInteraction::EditorModifierKeyRequestBus::Handler::BusConnect();
AzToolsFramework::ViewportInteraction::EditorViewportInputTimeNowRequestBus::Handler::BusConnect();
}
EditorToolsApplication::~EditorToolsApplication()
{
AzToolsFramework::ViewportInteraction::EditorViewportInputTimeNowRequestBus::Handler::BusDisconnect();
AzToolsFramework::ViewportInteraction::EditorModifierKeyRequestBus::Handler::BusDisconnect();
EditorToolsApplicationRequests::Bus::Handler::BusDisconnect();
Stop();
}
@@ -48,7 +52,6 @@ namespace EditorInternal
return m_StartupAborted;
}
void EditorToolsApplication::RegisterCoreComponents()
{
AzToolsFramework::ToolsApplication::RegisterCoreComponents();
@@ -274,5 +277,14 @@ namespace EditorInternal
Exit();
}
}
AzToolsFramework::ViewportInteraction::KeyboardModifiers EditorToolsApplication::QueryKeyboardModifiers()
{
return AzToolsFramework::ViewportInteraction::BuildKeyboardModifiers(QGuiApplication::queryKeyboardModifiers());
}
AZStd::chrono::milliseconds EditorToolsApplication::EditorViewportInputTimeNow()
{
const auto now = AZStd::chrono::high_resolution_clock::now();
return AZStd::chrono::time_point_cast<AZStd::chrono::milliseconds>(now).time_since_epoch();
}
} // namespace EditorInternal
+10
View File
@@ -7,7 +7,9 @@
*/
#pragma once
#include <AzToolsFramework/Application/ToolsApplication.h>
#include <AzToolsFramework/Viewport/ViewportMessages.h>
#include "Core/EditorMetricsPlainTextNameRegistration.h"
#include "EditorToolsApplicationAPI.h"
@@ -19,6 +21,8 @@ namespace EditorInternal
class EditorToolsApplication
: public AzToolsFramework::ToolsApplication
, public EditorToolsApplicationRequests::Bus::Handler
, public AzToolsFramework::ViewportInteraction::EditorModifierKeyRequestBus::Handler
, public AzToolsFramework::ViewportInteraction::EditorViewportInputTimeNowRequestBus::Handler
{
public:
EditorToolsApplication(int* argc, char*** argv);
@@ -44,6 +48,12 @@ namespace EditorInternal
void CreateReflectionManager() override;
void Reflect(AZ::ReflectContext* context) override;
// EditorModifierKeyRequestBus overrides ...
AzToolsFramework::ViewportInteraction::KeyboardModifiers QueryKeyboardModifiers() override;
// EditorViewportInputTimeNowRequestBus overrides ...
AZStd::chrono::milliseconds EditorViewportInputTimeNow() override;
protected:
// From EditorToolsApplicationRequests
bool OpenLevel(AZStd::string_view levelName) override;
+11 -8
View File
@@ -744,11 +744,15 @@ void EditorViewportWidget::RenderAll()
{
namespace AztfVi = AzToolsFramework::ViewportInteraction;
AztfVi::KeyboardModifiers keyboardModifiers;
AztfVi::EditorModifierKeyRequestBus::BroadcastResult(
keyboardModifiers, &AztfVi::EditorModifierKeyRequestBus::Events::QueryKeyboardModifiers);
m_debugDisplay->DepthTestOff();
m_manipulatorManager->DrawManipulators(
*m_debugDisplay, GetCameraState(),
BuildMouseInteractionInternal(
AztfVi::MouseButtons(AztfVi::TranslateMouseButtons(QGuiApplication::mouseButtons())), QueryKeyboardModifiers(),
AztfVi::MouseButtons(AztfVi::TranslateMouseButtons(QGuiApplication::mouseButtons())), keyboardModifiers,
BuildMousePick(WidgetToViewport(mapFromGlobal(QCursor::pos())))));
m_debugDisplay->DepthTestOn();
}
@@ -959,12 +963,13 @@ QWidget* EditorViewportWidget::GetWidgetForViewportContextMenu()
bool EditorViewportWidget::ShowingWorldSpace()
{
return QueryKeyboardModifiers().Shift();
}
namespace AztfVi = AzToolsFramework::ViewportInteraction;
AzToolsFramework::ViewportInteraction::KeyboardModifiers EditorViewportWidget::QueryKeyboardModifiers()
{
return AzToolsFramework::ViewportInteraction::BuildKeyboardModifiers(QGuiApplication::queryKeyboardModifiers());
AztfVi::KeyboardModifiers keyboardModifiers;
AztfVi::EditorModifierKeyRequestBus::BroadcastResult(
keyboardModifiers, &AztfVi::EditorModifierKeyRequestBus::Events::QueryKeyboardModifiers);
return keyboardModifiers.Shift();
}
void EditorViewportWidget::SetViewportId(int id)
@@ -1039,7 +1044,6 @@ void EditorViewportWidget::ConnectViewportInteractionRequestBus()
{
AzToolsFramework::ViewportInteraction::MainEditorViewportInteractionRequestBus::Handler::BusConnect(GetViewportId());
AzToolsFramework::ViewportInteraction::EditorEntityViewportInteractionRequestBus::Handler::BusConnect(GetViewportId());
AzToolsFramework::ViewportInteraction::EditorModifierKeyRequestBus::Handler::BusConnect();
m_viewportUi.ConnectViewportUiBus(GetViewportId());
AzFramework::InputSystemCursorConstraintRequestBus::Handler::BusConnect();
@@ -1050,7 +1054,6 @@ void EditorViewportWidget::DisconnectViewportInteractionRequestBus()
AzFramework::InputSystemCursorConstraintRequestBus::Handler::BusDisconnect();
m_viewportUi.DisconnectViewportUiBus();
AzToolsFramework::ViewportInteraction::EditorModifierKeyRequestBus::Handler::BusDisconnect();
AzToolsFramework::ViewportInteraction::EditorEntityViewportInteractionRequestBus::Handler::BusDisconnect();
AzToolsFramework::ViewportInteraction::MainEditorViewportInteractionRequestBus::Handler::BusDisconnect();
}
-4
View File
@@ -92,7 +92,6 @@ class SANDBOX_API EditorViewportWidget final
, private AzFramework::InputSystemCursorConstraintRequestBus::Handler
, private AzToolsFramework::ViewportInteraction::MainEditorViewportInteractionRequestBus::Handler
, private AzToolsFramework::ViewportInteraction::EditorEntityViewportInteractionRequestBus::Handler
, private AzToolsFramework::ViewportInteraction::EditorModifierKeyRequestBus::Handler
, private AzFramework::AssetCatalogEventBus::Handler
, private AZ::RPI::SceneNotificationBus::Handler
{
@@ -212,9 +211,6 @@ private:
// EditorEntityViewportInteractionRequestBus overrides ...
void FindVisibleEntities(AZStd::vector<AZ::EntityId>& visibleEntities) override;
// EditorModifierKeyRequestBus overrides ...
AzToolsFramework::ViewportInteraction::KeyboardModifiers QueryKeyboardModifiers() override;
// Camera::EditorCameraRequestBus overrides ...
void SetViewFromEntityPerspective(const AZ::EntityId& entityId) override;
void SetViewAndMovementLockFromEntityPerspective(const AZ::EntityId& entityId, bool lockCameraMovement) override;
+4 -2
View File
@@ -14,8 +14,10 @@
#define CRYINCLUDE_EDITOR_INCLUDE_IEDITORCLASSFACTORY_H
#pragma once
#include <CryCommon/platform.h>
#include <vector>
#include <QtCore/QString>
#include <AzCore/Math/Guid.h>
#define DEFINE_UUID(l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
static const GUID uuid() { return { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } }; }
@@ -34,7 +36,7 @@ struct IUnknown
#endif
#define __uuidof(T) T::uuid()
#if defined(AZ_PLATFORM_LINUX)
#if defined(AZ_PLATFORM_LINUX) || defined(AZ_PLATFORM_MAC)
# ifndef _REFGUID_DEFINED
# define _REFGUID_DEFINED
@@ -65,7 +67,7 @@ enum
};
#endif
#endif // defined(AZ_PLATFORM_LINUX)
#endif // defined(AZ_PLATFORM_LINUX) || defined(AZ_PLATFORM_MAC)
#include "SandboxAPI.h"
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
<true/>
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
</dict>
</plist>
+1 -24
View File
@@ -12,28 +12,5 @@ set_target_properties(Editor PROPERTIES
MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_LIST_DIR}/gui_info.plist
RESOURCE ${CMAKE_CURRENT_LIST_DIR}/Images.xcassets
XCODE_ATTRIBUTE_ASSETCATALOG_COMPILER_APPICON_NAME EditorAppIcon
ENTITLEMENT_FILE_PATH ${CMAKE_CURRENT_LIST_DIR}/EditorEntitlements.plist
)
# We cannot use ly_add_target here because we're already including this file from inside ly_add_target
# So we need to setup target, dependencies and install logic manually.
add_executable(EditorDummy Platform/Mac/main_dummy.cpp)
add_executable(AZ::EditorDummy ALIAS EditorDummy)
ly_target_link_libraries(EditorDummy
PRIVATE
AZ::AzCore
AZ::AzFramework)
ly_add_dependencies(Editor EditorDummy)
# Store the aliased target into a DIRECTORY property
set_property(DIRECTORY APPEND PROPERTY LY_DIRECTORY_TARGETS AZ::EditorDummy)
# Store the directory path in a GLOBAL property so that it can be accessed
# in the layout install logic. Skip if the directory has already been added
get_property(ly_all_target_directories GLOBAL PROPERTY LY_ALL_TARGET_DIRECTORIES)
if(NOT CMAKE_CURRENT_SOURCE_DIR IN_LIST ly_all_target_directories)
set_property(GLOBAL APPEND PROPERTY LY_ALL_TARGET_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR})
endif()
ly_install_add_install_path_setreg(Editor)
+1 -1
View File
@@ -3,7 +3,7 @@
<plist version="1.0">
<dict>
<key>CFBundleExecutable</key>
<string>EditorDummy</string>
<string>Editor</string>
<key>CFBundleIdentifier</key>
<string>org.O3DE.Editor</string>
<key>CFBundlePackageType</key>
-75
View File
@@ -1,75 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzCore/Component/ComponentApplication.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
#include <AzCore/Utils/Utils.h>
#include <AzFramework/Process/ProcessWatcher.h>
#include <cstdlib>
int main(int argc, char* argv[])
{
// Create a ComponentApplication to initialize the AZ::SystemAllocator and initialize the SettingsRegistry
AZ::ComponentApplication::Descriptor desc;
AZ::ComponentApplication application;
application.Create(desc);
AZStd::vector<AZStd::string> envVars;
const char* homePath = std::getenv("HOME");
envVars.push_back(AZStd::string::format("HOME=%s", homePath));
if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
{
const char* dyldLibPathOrig = std::getenv("DYLD_LIBRARY_PATH");
AZStd::string dyldSearchPath = AZStd::string::format("DYLD_LIBRARY_PATH=%s", dyldLibPathOrig);
if (AZ::IO::FixedMaxPath projectModulePath;
settingsRegistry->Get(projectModulePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectConfigurationBinPath))
{
dyldSearchPath.append(":");
dyldSearchPath.append(projectModulePath.c_str());
}
if (AZ::IO::FixedMaxPath installedBinariesFolder;
settingsRegistry->Get(installedBinariesFolder.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_InstalledBinaryFolder))
{
if (AZ::IO::FixedMaxPath engineRootFolder;
settingsRegistry->Get(engineRootFolder.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder))
{
installedBinariesFolder = engineRootFolder / installedBinariesFolder;
dyldSearchPath.append(":");
dyldSearchPath.append(installedBinariesFolder.c_str());
}
}
envVars.push_back(dyldSearchPath);
}
AZStd::string commandArgs;
for (int i = 1; i < argc; i++)
{
commandArgs.append(argv[i]);
commandArgs.append(" ");
}
AzFramework::ProcessLauncher::ProcessLaunchInfo processLaunchInfo;
AZ::IO::Path processPath{ AZ::IO::PathView(AZ::Utils::GetExecutableDirectory()) };
processPath /= "Editor";
processLaunchInfo.m_processExecutableString = AZStd::move(processPath.Native());
processLaunchInfo.m_commandlineParameters = commandArgs;
processLaunchInfo.m_environmentVariables = &envVars;
processLaunchInfo.m_showWindow = true;
AzFramework::ProcessLauncher::LaunchUnwatchedProcess(processLaunchInfo);
application.Destroy();
return 0;
}
@@ -51,4 +51,5 @@ ly_add_target(
AZ::AzCore
AZ::AzToolsFramework
AZ::AzQtComponents
Legacy::EditorCore
)
@@ -59,6 +59,20 @@ namespace AZStd
namespace AZ::Debug
{
// interface for externally defined profiler systems
class Profiler
{
public:
AZ_RTTI(Profiler, "{3E5D6329-72D1-41BA-9158-68A349D1A4D5}");
Profiler() = default;
virtual ~Profiler() = default;
// support for the extra macro args (e.g. format strings) will come in a later PR
virtual void BeginRegion(const Budget* budget, const char* eventName) = 0;
virtual void EndRegion(const Budget* budget) = 0;
};
class ProfileScope
{
public:
@@ -6,6 +6,8 @@
*
*/
#include <AzCore/Interface/Interface.h>
namespace AZ::Debug
{
template<typename... T>
@@ -22,9 +24,11 @@ namespace AZ::Debug
PIXBeginEvent(PIX_COLOR_INDEX(budget->Crc() & 0xff), eventName, args...);
#endif
budget->BeginProfileRegion();
// TODO: injecting instrumentation for other profilers
// NOTE: external profiler registration won't occur inline in a header necessarily in this manner, but the exact mechanism
// will be introduced in a future PR
if (auto profiler = AZ::Interface<Profiler>::Get(); profiler)
{
profiler->BeginRegion(budget, eventName);
}
#endif
}
@@ -39,6 +43,10 @@ namespace AZ::Debug
#if defined(USE_PIX)
PIXEndEvent();
#endif
if (auto profiler = AZ::Interface<Profiler>::Get(); profiler)
{
profiler->EndRegion(budget);
}
#endif
}
@@ -72,6 +72,7 @@ namespace AZ
{
if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
{
bool fileFound = false;
if (AZ::IO::FixedMaxPath projectModulePath;
settingsRegistry->Get(projectModulePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectConfigurationBinPath))
{
@@ -79,6 +80,23 @@ namespace AZ
if (AZ::IO::SystemFile::Exists(projectModulePath.c_str()))
{
m_fileName.assign(projectModulePath.c_str(), projectModulePath.Native().size());
fileFound = true;
}
}
if (!fileFound)
{
if (AZ::IO::FixedMaxPath installedBinariesPath;
settingsRegistry->Get(installedBinariesPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_InstalledBinaryFolder))
{
if (AZ::IO::FixedMaxPath engineRootFolder;
settingsRegistry->Get(engineRootFolder.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder))
{
installedBinariesPath = engineRootFolder / installedBinariesPath / fullFilePath;
if (AZ::IO::SystemFile::Exists(installedBinariesPath.c_str()))
{
m_fileName.assign(installedBinariesPath.c_str(), installedBinariesPath.Native().size());
}
}
}
}
}
@@ -9,6 +9,7 @@
#include <AzCore/Utils/Utils.h>
#include <cstdlib>
#include <pwd.h>
namespace AZ
{
@@ -39,6 +40,14 @@ namespace AZ
AZ::IO::FixedMaxPath path{homePath};
return path.Native();
}
struct passwd* pass = getpwuid(getuid());
if (pass)
{
AZ::IO::FixedMaxPath path{pass->pw_dir};
return path.Native();
}
return {};
}
@@ -181,7 +181,7 @@ namespace AZ::IO::ArchiveInternal
return 0;
}
nTotal = (AZStd::min)(nTotal, GetFileSize() - m_nCurSeek);
nTotal = AZStd::min<size_t>(nTotal, GetFileSize() - m_nCurSeek);
int64_t nReadBytes = GetFile()->ReadData(pDest, m_nCurSeek, nTotal);
if (nReadBytes == -1)
@@ -9,8 +9,19 @@
#include <AzFramework/Viewport/ClickDetector.h>
#include <AzFramework/Viewport/ScreenGeometry.h>
#include <AzCore/std/chrono/clocks.h>
namespace AzFramework
{
ClickDetector::ClickDetector()
{
m_timeNowFn = []
{
const auto now = AZStd::chrono::high_resolution_clock::now();
return AZStd::chrono::time_point_cast<AZStd::chrono::milliseconds>(now).time_since_epoch();
};
}
ClickDetector::ClickOutcome ClickDetector::DetectClick(const ClickEvent clickEvent, const ScreenVector& cursorDelta)
{
const auto previousDetectionState = m_detectionState;
@@ -26,11 +37,13 @@ namespace AzFramework
if (clickEvent == ClickEvent::Down)
{
const auto now = std::chrono::steady_clock::now();
const auto now = m_timeNowFn();
if (m_tryBeginTime)
{
const std::chrono::duration<float> diff = now - m_tryBeginTime.value();
if (diff.count() < m_doubleClickInterval)
using FloatingPointSeconds = AZStd::chrono::duration<float, AZStd::chrono::seconds::period>;
const auto diff = now - m_tryBeginTime.value();
if (FloatingPointSeconds(diff).count() < m_doubleClickInterval)
{
return ClickOutcome::Nil;
}
@@ -43,7 +56,8 @@ namespace AzFramework
}
else if (clickEvent == ClickEvent::Up)
{
const auto clickOutcome = [detectionState = m_detectionState] {
const auto clickOutcome = [detectionState = m_detectionState]
{
if (detectionState == DetectionState::WaitingForMove)
{
return ClickOutcome::Click;
@@ -66,4 +80,9 @@ namespace AzFramework
return ClickOutcome::Nil;
}
void ClickDetector::OverrideTimeNowFn(AZStd::function<AZStd::chrono::milliseconds()> timeNowFn)
{
m_timeNowFn = AZStd::move(timeNowFn);
}
} // namespace AzFramework
@@ -8,6 +8,7 @@
#pragma once
#include <AzCore/std/functional.h>
#include <AzCore/std/optional.h>
#include <chrono>
@@ -21,10 +22,9 @@ namespace AzFramework
//! (mouse down with movement and then mouse up).
class ClickDetector
{
//! Alias for recording time of mouse down events
using Time = std::chrono::time_point<std::chrono::steady_clock>;
public:
ClickDetector();
//! Internal representation of click event (map from external event for this when
//! calling DetectClick).
enum class ClickEvent
@@ -51,6 +51,10 @@ namespace AzFramework
void SetDoubleClickInterval(float doubleClickInterval);
//! Override the dead zone before a 'move' outcome will be triggered.
void SetDeadZone(float deadZone);
//! Override how the current time is retrieved.
//! This is helpful to override when it comes to simulating different passages of
//! time to avoid double click issues in tests for example.
void OverrideTimeNowFn(AZStd::function<AZStd::chrono::milliseconds()> timeNowFn);
private:
//! Internal state of ClickDetector based on incoming events.
@@ -65,7 +69,9 @@ namespace AzFramework
float m_deadZone = 2.0f; //!< How far to move before a click is cancelled (when Move will fire).
float m_doubleClickInterval = 0.4f; //!< Default double click interval, can be overridden.
DetectionState m_detectionState; //!< Internal state of ClickDetector.
AZStd::optional<Time> m_tryBeginTime; //!< Mouse down time (happens each mouse down, helps with double click handling).
//! Mouse down time (happens each mouse down, helps with double click handling).
AZStd::optional<AZStd::chrono::milliseconds> m_tryBeginTime;
AZStd::function<AZStd::chrono::milliseconds()> m_timeNowFn; //!< Interface to query the current time.
};
inline void ClickDetector::SetDoubleClickInterval(const float doubleClickInterval)
@@ -0,0 +1,293 @@
/*
* 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/std/typetraits/integral_constant.h>
#include <AzFramework/API/ApplicationAPI_Linux.h>
#include <AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h>
#define explicit ExplicitIsACXXKeyword
#include <xcb/xkb.h>
#undef explicit
#include <xkbcommon/xkbcommon-keysyms.h>
#include <xkbcommon/xkbcommon.h>
#include <xkbcommon/xkbcommon-x11.h>
namespace AzFramework
{
class InputDeviceKeyboardXcb
: public InputDeviceKeyboard::Implementation
, public LinuxXcbEventHandlerBus::Handler
{
public:
AZ_CLASS_ALLOCATOR(InputDeviceKeyboardXcb, AZ::SystemAllocator, 0);
using InputDeviceKeyboard::Implementation::Implementation;
InputDeviceKeyboardXcb(InputDeviceKeyboard& inputDevice)
: InputDeviceKeyboard::Implementation(inputDevice)
{
LinuxXcbEventHandlerBus::Handler::BusConnect();
auto* interface = AzFramework::LinuxXcbConnectionManagerInterface::Get();
if (!interface)
{
AZ_Warning("ApplicationLinux", false, "XCB interface not available");
return;
}
auto* connection = AzFramework::LinuxXcbConnectionManagerInterface::Get()->GetXcbConnection();
if (!connection)
{
AZ_Warning("ApplicationLinux", false, "XCB connection not available");
return;
}
AZStd::unique_ptr<xcb_xkb_use_extension_reply_t, DeleterForFreeFn<::std::free>> xkbUseExtensionReply{
xcb_xkb_use_extension_reply(connection, xcb_xkb_use_extension(connection, 1, 0), nullptr)
};
if (!xkbUseExtensionReply)
{
AZ_Warning("ApplicationLinux", false, "Failed to initialize the xkb extension");
return;
}
if (!xkbUseExtensionReply->supported)
{
AZ_Warning("ApplicationLinux", false, "The X server does not support the xkb extension");
return;
}
m_coreDeviceId = xkb_x11_get_core_keyboard_device_id(connection);
m_xkbContext.reset(xkb_context_new(XKB_CONTEXT_NO_FLAGS));
m_xkbKeymap.reset(xkb_x11_keymap_new_from_device(m_xkbContext.get(), connection, m_coreDeviceId, XKB_KEYMAP_COMPILE_NO_FLAGS));
m_xkbState.reset(xkb_x11_state_new_from_device(m_xkbKeymap.get(), connection, m_coreDeviceId));
m_initialized = true;
}
bool IsConnected() const override
{
return m_initialized;
}
bool HasTextEntryStarted() const override
{
return false;
}
void TextEntryStart(const InputDeviceKeyboard::VirtualKeyboardOptions& options) override
{
}
void TextEntryStop() override
{
}
void TickInputDevice() override
{
ProcessRawEventQueues();
}
void HandleXcbEvent(xcb_generic_event_t* event) override
{
if (!IsConnected())
{
return;
}
switch (event->response_type & ~0x80)
{
case XCB_KEY_PRESS:
{
auto* keyPress = reinterpret_cast<xcb_key_press_event_t*>(event);
const InputChannelId* key = InputChannelFromKeyEvent(keyPress->detail);
if (key)
{
QueueRawKeyEvent(*key, true);
}
break;
}
case XCB_KEY_RELEASE:
{
auto* keyRelease = reinterpret_cast<xcb_key_release_event_t*>(event);
const InputChannelId* key = InputChannelFromKeyEvent(keyRelease->detail);
if (key)
{
QueueRawKeyEvent(*key, false);
}
break;
}
}
}
private:
[[nodiscard]] const InputChannelId* InputChannelFromKeyEvent(xcb_keycode_t code) const
{
const xcb_keysym_t keysym = xkb_state_key_get_one_sym(m_xkbState.get(), code);
switch(keysym)
{
case XKB_KEY_0: return &InputDeviceKeyboard::Key::Alphanumeric0;
case XKB_KEY_1: return &InputDeviceKeyboard::Key::Alphanumeric1;
case XKB_KEY_2: return &InputDeviceKeyboard::Key::Alphanumeric2;
case XKB_KEY_3: return &InputDeviceKeyboard::Key::Alphanumeric3;
case XKB_KEY_4: return &InputDeviceKeyboard::Key::Alphanumeric4;
case XKB_KEY_5: return &InputDeviceKeyboard::Key::Alphanumeric5;
case XKB_KEY_6: return &InputDeviceKeyboard::Key::Alphanumeric6;
case XKB_KEY_7: return &InputDeviceKeyboard::Key::Alphanumeric7;
case XKB_KEY_8: return &InputDeviceKeyboard::Key::Alphanumeric8;
case XKB_KEY_9: return &InputDeviceKeyboard::Key::Alphanumeric9;
case XKB_KEY_A:
case XKB_KEY_a: return &InputDeviceKeyboard::Key::AlphanumericA;
case XKB_KEY_B:
case XKB_KEY_b: return &InputDeviceKeyboard::Key::AlphanumericB;
case XKB_KEY_C:
case XKB_KEY_c: return &InputDeviceKeyboard::Key::AlphanumericC;
case XKB_KEY_D:
case XKB_KEY_d: return &InputDeviceKeyboard::Key::AlphanumericD;
case XKB_KEY_E:
case XKB_KEY_e: return &InputDeviceKeyboard::Key::AlphanumericE;
case XKB_KEY_F:
case XKB_KEY_f: return &InputDeviceKeyboard::Key::AlphanumericF;
case XKB_KEY_G:
case XKB_KEY_g: return &InputDeviceKeyboard::Key::AlphanumericG;
case XKB_KEY_H:
case XKB_KEY_h: return &InputDeviceKeyboard::Key::AlphanumericH;
case XKB_KEY_I:
case XKB_KEY_i: return &InputDeviceKeyboard::Key::AlphanumericI;
case XKB_KEY_J:
case XKB_KEY_j: return &InputDeviceKeyboard::Key::AlphanumericJ;
case XKB_KEY_K:
case XKB_KEY_k: return &InputDeviceKeyboard::Key::AlphanumericK;
case XKB_KEY_L:
case XKB_KEY_l: return &InputDeviceKeyboard::Key::AlphanumericL;
case XKB_KEY_M:
case XKB_KEY_m: return &InputDeviceKeyboard::Key::AlphanumericM;
case XKB_KEY_N:
case XKB_KEY_n: return &InputDeviceKeyboard::Key::AlphanumericN;
case XKB_KEY_O:
case XKB_KEY_o: return &InputDeviceKeyboard::Key::AlphanumericO;
case XKB_KEY_P:
case XKB_KEY_p: return &InputDeviceKeyboard::Key::AlphanumericP;
case XKB_KEY_Q:
case XKB_KEY_q: return &InputDeviceKeyboard::Key::AlphanumericQ;
case XKB_KEY_R:
case XKB_KEY_r: return &InputDeviceKeyboard::Key::AlphanumericR;
case XKB_KEY_S:
case XKB_KEY_s: return &InputDeviceKeyboard::Key::AlphanumericS;
case XKB_KEY_T:
case XKB_KEY_t: return &InputDeviceKeyboard::Key::AlphanumericT;
case XKB_KEY_U:
case XKB_KEY_u: return &InputDeviceKeyboard::Key::AlphanumericU;
case XKB_KEY_V:
case XKB_KEY_v: return &InputDeviceKeyboard::Key::AlphanumericV;
case XKB_KEY_W:
case XKB_KEY_w: return &InputDeviceKeyboard::Key::AlphanumericW;
case XKB_KEY_X:
case XKB_KEY_x: return &InputDeviceKeyboard::Key::AlphanumericX;
case XKB_KEY_Y:
case XKB_KEY_y: return &InputDeviceKeyboard::Key::AlphanumericY;
case XKB_KEY_Z:
case XKB_KEY_z: return &InputDeviceKeyboard::Key::AlphanumericZ;
case XKB_KEY_BackSpace: return &InputDeviceKeyboard::Key::EditBackspace;
case XKB_KEY_Caps_Lock: return &InputDeviceKeyboard::Key::EditCapsLock;
case XKB_KEY_Return: return &InputDeviceKeyboard::Key::EditEnter;
case XKB_KEY_space: return &InputDeviceKeyboard::Key::EditSpace;
case XKB_KEY_Tab: return &InputDeviceKeyboard::Key::EditTab;
case XKB_KEY_Escape: return &InputDeviceKeyboard::Key::Escape;
case XKB_KEY_F1: return &InputDeviceKeyboard::Key::Function01;
case XKB_KEY_F2: return &InputDeviceKeyboard::Key::Function02;
case XKB_KEY_F3: return &InputDeviceKeyboard::Key::Function03;
case XKB_KEY_F4: return &InputDeviceKeyboard::Key::Function04;
case XKB_KEY_F5: return &InputDeviceKeyboard::Key::Function05;
case XKB_KEY_F6: return &InputDeviceKeyboard::Key::Function06;
case XKB_KEY_F7: return &InputDeviceKeyboard::Key::Function07;
case XKB_KEY_F8: return &InputDeviceKeyboard::Key::Function08;
case XKB_KEY_F9: return &InputDeviceKeyboard::Key::Function09;
case XKB_KEY_F10: return &InputDeviceKeyboard::Key::Function10;
case XKB_KEY_F11: return &InputDeviceKeyboard::Key::Function11;
case XKB_KEY_F12: return &InputDeviceKeyboard::Key::Function12;
case XKB_KEY_F13: return &InputDeviceKeyboard::Key::Function13;
case XKB_KEY_F14: return &InputDeviceKeyboard::Key::Function14;
case XKB_KEY_F15: return &InputDeviceKeyboard::Key::Function15;
case XKB_KEY_F16: return &InputDeviceKeyboard::Key::Function16;
case XKB_KEY_F17: return &InputDeviceKeyboard::Key::Function17;
case XKB_KEY_F18: return &InputDeviceKeyboard::Key::Function18;
case XKB_KEY_F19: return &InputDeviceKeyboard::Key::Function19;
case XKB_KEY_F20: return &InputDeviceKeyboard::Key::Function20;
case XKB_KEY_Alt_L: return &InputDeviceKeyboard::Key::ModifierAltL;
case XKB_KEY_Alt_R: return &InputDeviceKeyboard::Key::ModifierAltR;
case XKB_KEY_Control_L: return &InputDeviceKeyboard::Key::ModifierCtrlL;
case XKB_KEY_Control_R: return &InputDeviceKeyboard::Key::ModifierCtrlR;
case XKB_KEY_Shift_L: return &InputDeviceKeyboard::Key::ModifierShiftL;
case XKB_KEY_Shift_R: return &InputDeviceKeyboard::Key::ModifierShiftR;
case XKB_KEY_Super_L: return &InputDeviceKeyboard::Key::ModifierSuperL;
case XKB_KEY_Super_R: return &InputDeviceKeyboard::Key::ModifierSuperR;
case XKB_KEY_Down: return &InputDeviceKeyboard::Key::NavigationArrowDown;
case XKB_KEY_Left: return &InputDeviceKeyboard::Key::NavigationArrowLeft;
case XKB_KEY_Right: return &InputDeviceKeyboard::Key::NavigationArrowRight;
case XKB_KEY_Up: return &InputDeviceKeyboard::Key::NavigationArrowUp;
case XKB_KEY_Delete: return &InputDeviceKeyboard::Key::NavigationDelete;
case XKB_KEY_End: return &InputDeviceKeyboard::Key::NavigationEnd;
case XKB_KEY_Home: return &InputDeviceKeyboard::Key::NavigationHome;
case XKB_KEY_Insert: return &InputDeviceKeyboard::Key::NavigationInsert;
case XKB_KEY_Page_Down: return &InputDeviceKeyboard::Key::NavigationPageDown;
case XKB_KEY_Page_Up: return &InputDeviceKeyboard::Key::NavigationPageUp;
case XKB_KEY_Num_Lock: return &InputDeviceKeyboard::Key::NumLock;
case XKB_KEY_KP_0: return &InputDeviceKeyboard::Key::NumPad0;
case XKB_KEY_KP_1: return &InputDeviceKeyboard::Key::NumPad1;
case XKB_KEY_KP_2: return &InputDeviceKeyboard::Key::NumPad2;
case XKB_KEY_KP_3: return &InputDeviceKeyboard::Key::NumPad3;
case XKB_KEY_KP_4: return &InputDeviceKeyboard::Key::NumPad4;
case XKB_KEY_KP_5: return &InputDeviceKeyboard::Key::NumPad5;
case XKB_KEY_KP_6: return &InputDeviceKeyboard::Key::NumPad6;
case XKB_KEY_KP_7: return &InputDeviceKeyboard::Key::NumPad7;
case XKB_KEY_KP_8: return &InputDeviceKeyboard::Key::NumPad8;
case XKB_KEY_KP_9: return &InputDeviceKeyboard::Key::NumPad9;
case XKB_KEY_KP_Add: return &InputDeviceKeyboard::Key::NumPadAdd;
case XKB_KEY_KP_Decimal: return &InputDeviceKeyboard::Key::NumPadDecimal;
case XKB_KEY_KP_Divide: return &InputDeviceKeyboard::Key::NumPadDivide;
case XKB_KEY_KP_Enter: return &InputDeviceKeyboard::Key::NumPadEnter;
case XKB_KEY_KP_Multiply: return &InputDeviceKeyboard::Key::NumPadMultiply;
case XKB_KEY_KP_Subtract: return &InputDeviceKeyboard::Key::NumPadSubtract;
case XKB_KEY_apostrophe: return &InputDeviceKeyboard::Key::PunctuationApostrophe;
case XKB_KEY_backslash: return &InputDeviceKeyboard::Key::PunctuationBackslash;
case XKB_KEY_bracketleft: return &InputDeviceKeyboard::Key::PunctuationBracketL;
case XKB_KEY_bracketright: return &InputDeviceKeyboard::Key::PunctuationBracketR;
case XKB_KEY_comma: return &InputDeviceKeyboard::Key::PunctuationComma;
case XKB_KEY_equal: return &InputDeviceKeyboard::Key::PunctuationEquals;
case XKB_KEY_hyphen: return &InputDeviceKeyboard::Key::PunctuationHyphen;
case XKB_KEY_period: return &InputDeviceKeyboard::Key::PunctuationPeriod;
case XKB_KEY_semicolon: return &InputDeviceKeyboard::Key::PunctuationSemicolon;
case XKB_KEY_slash: return &InputDeviceKeyboard::Key::PunctuationSlash;
case XKB_KEY_grave:
case XKB_KEY_asciitilde: return &InputDeviceKeyboard::Key::PunctuationTilde;
case XKB_KEY_ISO_Group_Shift: return &InputDeviceKeyboard::Key::SupplementaryISO;
case XKB_KEY_Pause: return &InputDeviceKeyboard::Key::WindowsSystemPause;
case XKB_KEY_Print: return &InputDeviceKeyboard::Key::WindowsSystemPrint;
case XKB_KEY_Scroll_Lock: return &InputDeviceKeyboard::Key::WindowsSystemScrollLock;
default: return nullptr;
}
}
template<auto freeFn>
using DeleterForFreeFn = AZStd::integral_constant<decltype(freeFn), freeFn>;
AZStd::unique_ptr<xkb_context, DeleterForFreeFn<xkb_context_unref>> m_xkbContext;
AZStd::unique_ptr<xkb_keymap, DeleterForFreeFn<xkb_keymap_unref>> m_xkbKeymap;
AZStd::unique_ptr<xkb_state, DeleterForFreeFn<xkb_state_unref>> m_xkbState;
int m_coreDeviceId{-1};
bool m_initialized{false};
};
InputDeviceKeyboard::Implementation* InputDeviceKeyboard::Implementation::Create(InputDeviceKeyboard& inputDevice)
{
return aznew InputDeviceKeyboardXcb(inputDevice);
}
} // namespace AzFramework
@@ -62,8 +62,16 @@ namespace AzFramework
uint32_t eventMask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
const uint32_t interestedEvents =
XCB_EVENT_MASK_STRUCTURE_NOTIFY
| XCB_EVENT_MASK_BUTTON_PRESS
| XCB_EVENT_MASK_BUTTON_RELEASE
| XCB_EVENT_MASK_KEY_PRESS
| XCB_EVENT_MASK_KEY_RELEASE
| XCB_EVENT_MASK_POINTER_MOTION
;
uint32_t valueList[] = { xcbRootScreen->black_pixel,
XCB_EVENT_MASK_STRUCTURE_NOTIFY };
interestedEvents };
xcb_void_cookie_t xcbCheckResult;
@@ -11,10 +11,16 @@
if (${PAL_TRAIT_LINUX_WINDOW_MANAGER} STREQUAL "xcb")
find_library(XCB_LIBRARY xcb)
find_library(XCB_XKB_LIBRARY xcb-xkb)
find_library(XKBCOMMON_LIBRARY xkbcommon)
find_library(XKBCOMMON_X11_LIBRARY xkbcommon-x11)
set(LY_BUILD_DEPENDENCIES
PRIVATE
${XCB_LIBRARY}
${XKBCOMMON_LIBRARY}
${XKBCOMMON_X11_LIBRARY}
${XCB_XKB_LIBRARY}
)
set(LY_COMPILE_DEFINITIONS PUBLIC PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB)
@@ -25,7 +25,7 @@ set(FILES
AzFramework/Windowing/NativeWindow_Linux_xcb.h
AzFramework/Windowing/NativeWindow_Linux_xcb.cpp
../Common/Unimplemented/AzFramework/Input/Devices/Gamepad/InputDeviceGamepad_Unimplemented.cpp
../Common/Unimplemented/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard_Unimplemented.cpp
AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard_xcb.cpp
../Common/Unimplemented/AzFramework/Input/Devices/Motion/InputDeviceMotion_Unimplemented.cpp
../Common/Unimplemented/AzFramework/Input/Devices/Mouse/InputDeviceMouse_Unimplemented.cpp
../Common/Unimplemented/AzFramework/Input/Devices/Touch/InputDeviceTouch_Unimplemented.cpp
@@ -18,6 +18,7 @@ namespace AzManipulatorTestFramework
class ImmediateModeActionDispatcher
: public ActionDispatcher<ImmediateModeActionDispatcher>
, public AzToolsFramework::ViewportInteraction::EditorModifierKeyRequestBus::Handler
, public AzToolsFramework::ViewportInteraction::EditorViewportInputTimeNowRequestBus::Handler
{
using KeyboardModifier = AzToolsFramework::ViewportInteraction::KeyboardModifier;
using KeyboardModifiers = AzToolsFramework::ViewportInteraction::KeyboardModifiers;
@@ -50,6 +51,9 @@ namespace AzManipulatorTestFramework
// EditorModifierKeyRequestBus overrides ...
KeyboardModifiers QueryKeyboardModifiers() override;
// EditorViewportInputTimeNowRequestBus overrides ...
AZStd::chrono::milliseconds EditorViewportInputTimeNow() override;
protected:
// ActionDispatcher ...
void SetSnapToGridImpl(bool enabled) override;
@@ -79,6 +83,9 @@ namespace AzManipulatorTestFramework
mutable AZStd::unique_ptr<MouseInteractionEvent> m_event;
ManipulatorViewportInteraction& m_viewportManipulatorInteraction;
//! Current time that ticks up after each call to EditorViewportInputTimeNow.
AZStd::chrono::milliseconds m_timeNow = AZStd::chrono::milliseconds(0);
};
template<typename ActualT, typename ExpectedT>
@@ -106,4 +113,13 @@ namespace AzManipulatorTestFramework
{
return GetMouseInteractionEvent()->m_mouseInteraction.m_keyboardModifiers;
}
inline AZStd::chrono::milliseconds ImmediateModeActionDispatcher::EditorViewportInputTimeNow()
{
// step the time for each call to be greater than the minimum time required for a double click to register
// note: the time increment is very high to ensure any potential system changes to settings such as double
// click interval will not be impacted
m_timeNow += AZStd::chrono::milliseconds(10000);
return m_timeNow;
}
} // namespace AzManipulatorTestFramework
@@ -33,10 +33,12 @@ namespace AzManipulatorTestFramework
: m_viewportManipulatorInteraction(viewportManipulatorInteraction)
{
AzToolsFramework::ViewportInteraction::EditorModifierKeyRequestBus::Handler::BusConnect();
AzToolsFramework::ViewportInteraction::EditorViewportInputTimeNowRequestBus::Handler::BusConnect();
}
ImmediateModeActionDispatcher::~ImmediateModeActionDispatcher()
{
AzToolsFramework::ViewportInteraction::EditorViewportInputTimeNowRequestBus::Handler::BusDisconnect();
AzToolsFramework::ViewportInteraction::EditorModifierKeyRequestBus::Handler::BusDisconnect();
}
@@ -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
*
*/
#pragma once
#include <AzCore/Interface/Interface.h>
#include <AzCore/Outcome/Outcome.h>
#include <AzToolsFramework/API/ViewportEditorModeTrackerNotificationBus.h>
namespace AzToolsFramework
{
//! The AZ::Interface of the central editor mode tracker for all viewports.
class ViewportEditorModeTrackerInterface
{
public:
AZ_RTTI(ViewportEditorModeTrackerInterface, "{7D72A4F7-2147-4ED9-A315-E456A3BE3CF6}");
virtual ~ViewportEditorModeTrackerInterface() = default;
//! Activates the specified editor mode for the specified viewport.
virtual AZ::Outcome<void, AZStd::string> ActivateMode(
const ViewportEditorModeInfo& viewportEditorModeInfo, ViewportEditorMode mode) = 0;
//! Deactivates the specified editor mode for the specified viewport.
virtual AZ::Outcome<void, AZStd::string> DeactivateMode(
const ViewportEditorModeInfo& viewportEditorModeInfo, ViewportEditorMode mode) = 0;
//! Attempts to retrieve the editor mode state for the specified viewport, otherwise returns nullptr.
virtual const ViewportEditorModesInterface* GetViewportEditorModes(const ViewportEditorModeInfo& viewportEditorModeInfo) const = 0;
//! Returns the number of viewports currently being tracked.
virtual size_t GetTrackedViewportCount() const = 0;
//! Returns true if the specified viewport is being tracked, otherwise false.
virtual bool IsViewportModeTracked(const ViewportEditorModeInfo& viewportEditorModeInfo) const = 0;
};
} // namespace AzToolsFramework

Some files were not shown because too many files have changed in this diff Show More