You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
362 lines
17 KiB
Python
362 lines
17 KiB
Python
"""
|
|
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
|
|
"""
|
|
|
|
|
|
# Test case ID : C3510644
|
|
# Test Case Title : Check that the collision layer and collision group of the terrain can be changed
|
|
# and the collision behavior of the terrain changes accordingly
|
|
|
|
|
|
# fmt: off
|
|
class Tests:
|
|
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
|
box_1_a_valid = ("Box 1 A has been validated", "Box 1 A COULD NOT be validated")
|
|
box_2_a_valid = ("Box 2 A has been validated", "Box 2 A COULD NOT be validated")
|
|
terrain_a_valid = ("Terrain A has been validated", "Terrain A COULD NOT be validated")
|
|
box_1_b_valid = ("Box 1 B has been validated", "Box 1 B COULD NOT be validated")
|
|
box_2_b_valid = ("Box 2 B has been validated", "Box 2 B COULD NOT be validated")
|
|
terrain_b_valid = ("Terrain B has been validated", "Terrain B COULD NOT be validated")
|
|
box_1_a_pos_found = ("Box 1 A position found", "Box 1 A position NOT found")
|
|
box_2_a_pos_found = ("Box 2 A position found", "Box 2 A position NOT found")
|
|
terrain_a_pos_found = ("Terrain A position found", "Terrain A position NOT found")
|
|
box_1_b_pos_found = ("Box 1 B position found", "Box 1 B position NOT found")
|
|
box_2_b_pos_found = ("Box 2 B position found", "Box 2 B position NOT found")
|
|
terrain_b_pos_found = ("Terrain B position found", "Terrain B position NOT found")
|
|
box_1_a_did_collide_with_terrain = ("Box 1 A did collide with terrain", "Box 1 A DID NOT collide with terrain")
|
|
box_1_a_did_not_pass_through_terrain = ("Box 1 A did not fall past the terrain", "Box 1 A DID fall past the terrain")
|
|
box_2_a_did_not_collide_with_terrain = ("Box 2 A did not collide with terrain", "Box 2 A DID collide with terrain")
|
|
box_2_a_did_pass_through_terrain = ("Box 2 A did fall past the terrain", "Box 2 A DID NOT fall past the terrain")
|
|
box_1_b_did_not_collide_with_terrain = ("Box 1 B did not collide with terrain", "Box 1 B DID collide with terrain")
|
|
box_1_b_did_pass_through_terrain = ("Box 1 B did fall past the terrain", "Box 1 B DID NOT fall past the terrain")
|
|
box_2_b_did_collide_with_terrain = ("Box 2 B did collide with terrain", "Box 2 B DID NOT collide with terrain")
|
|
box_2_b_did_not_pass_through_terrain = ("Box 2 B did not fall past the terrain", "Box 2 B DID fall past the terrain")
|
|
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
|
# fmt: on
|
|
|
|
|
|
def C3510644_Collider_CollisionGroups():
|
|
# type: () -> None
|
|
"""
|
|
Summary:
|
|
Runs an automated test to ensure PhysX collision groups dictate whether collisions happen or not.
|
|
The test has two phases (A and B) for testing collision groups under different circumstances. Phase A
|
|
is run first and upon success Phase B starts.
|
|
|
|
Level Description:
|
|
Entities can be divided into 2 groups for the two phases, A and B. Each phase has identical entities with exception
|
|
to Terrain, where Terrain_A has a collision group/layer set for demo_group1/demo1 and Terrain_B has a collision
|
|
group/layer set for demo_group2/demo2.
|
|
|
|
Each Phase has two boxes, Box_1 and Box_2, where each box has it's collision group/layer set to it's number
|
|
(1 or 2). Each box is positioned just above the Terrain with gravity enabled.
|
|
|
|
All entities for Phase B are deactivated by default. If Phase A is setup and executed successfully it's
|
|
entities are deactivated and Phase B's entities are activated and validated before running the Phase B test.
|
|
|
|
Expected behavior:
|
|
When Phase A starts, it's two boxes should fall toward the terrain. Once the boxes' behavior is validated the
|
|
entities from Phase A are deactivated and Phase B's entities are activated. Like in Phase A, the boxes in Phase B
|
|
should fall towards the terrain. If all goes as expected Box_1_A and Box_2_B should collide with teh terrain, and
|
|
Box_2A and Box_1_B should fall through the terrain.
|
|
|
|
Test Steps:
|
|
0) [Define helper classes and functions]
|
|
1) Load the level
|
|
2) Enter game mode
|
|
3) Retrieve and validate entities
|
|
4) Phase A
|
|
a) set up
|
|
b) execute test
|
|
c) log results (deactivate Phase A entities)
|
|
5) Phase B
|
|
a) set up (activate Phase B entities)
|
|
b) execute test
|
|
c) log results
|
|
6) close editor
|
|
|
|
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.
|
|
- The level for this test uses two PhysX Terrains and must be run with cmdline argument "-autotest_mode"
|
|
to suppress the warning for having multiple terrains.
|
|
|
|
:return: None
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
from editor_python_test_tools.utils import Report
|
|
from editor_python_test_tools.utils import TestHelper as helper
|
|
|
|
import azlmbr.legacy.general as general
|
|
import azlmbr.bus
|
|
import azlmbr
|
|
|
|
# ******* Helper Classes ********
|
|
|
|
# Phase A's test results
|
|
class PhaseATestData:
|
|
total_results = 2
|
|
box_1_collided = False
|
|
box_1_fell_through = True
|
|
box_2_collided = False
|
|
box_2_fell_through = False
|
|
box_1 = None
|
|
box_2 = None
|
|
terrain = None
|
|
box_1_pos = None
|
|
box_2_pos = None
|
|
terrain_pos = None
|
|
|
|
@staticmethod
|
|
# Quick check for validating results for Phase A
|
|
def valid():
|
|
return (
|
|
PhaseATestData.box_1_collided
|
|
and PhaseATestData.box_2_fell_through
|
|
and not PhaseATestData.box_1_fell_through
|
|
and not PhaseATestData.box_2_collided
|
|
)
|
|
|
|
# Phase B's test results
|
|
class PhaseBTestData:
|
|
total_results = 2
|
|
box_1_collided = False
|
|
box_1_fell_through = False
|
|
box_2_collided = False
|
|
box_2_fell_through = True
|
|
box_1 = None
|
|
box_2 = None
|
|
terrain = None
|
|
box_1_pos = None
|
|
box_2_pos = None
|
|
terrain_pos = None
|
|
|
|
@staticmethod
|
|
# Quick check for validating results for Phase B
|
|
def valid():
|
|
return (
|
|
not PhaseBTestData.box_1_collided
|
|
and not PhaseBTestData.box_2_fell_through
|
|
and PhaseBTestData.box_1_fell_through
|
|
and PhaseBTestData.box_2_collided
|
|
)
|
|
|
|
# **** Helper Functions ****
|
|
|
|
# ** Validation helpers **
|
|
|
|
# Attempts to validate an entity based on the name parameter
|
|
def validate_entity(entity_name, msg_tuple):
|
|
# type: (str, (str, str)) -> EntityId
|
|
entity_id = general.find_game_entity(entity_name)
|
|
Report.critical_result(msg_tuple, entity_id.IsValid())
|
|
return entity_id
|
|
|
|
# Attempts to retrieve an entity's initial position and logs result
|
|
def validate_initial_position(entity_id, msg_tuple):
|
|
# type: (EntityId, (str, str)) -> azlmbr.math.Vector3
|
|
# Attempts to validate and return the entity's initial position.
|
|
# logs the result to Report.result() using the tuple parameter
|
|
|
|
pos = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", entity_id)
|
|
valid = not (pos is None or pos.IsZero())
|
|
entity_name = azlmbr.entity.GameEntityContextRequestBus(azlmbr.bus.Broadcast, "GetEntityName", entity_id)
|
|
Report.critical_result(msg_tuple, valid)
|
|
Report.info_vector3(pos, "{} initial position:".format(entity_name))
|
|
return pos
|
|
|
|
# ** Phase completion checks checks **
|
|
|
|
# Checks if we are done collecting data for phase A
|
|
def done_collecting_results_a():
|
|
# type: () -> bool
|
|
|
|
# Update positions
|
|
PhaseATestData.box_1_pos = azlmbr.components.TransformBus(
|
|
azlmbr.bus.Event, "GetWorldTranslation", PhaseATestData.box_1
|
|
)
|
|
PhaseATestData.box_2_pos = azlmbr.components.TransformBus(
|
|
azlmbr.bus.Event, "GetWorldTranslation", PhaseATestData.box_2
|
|
)
|
|
|
|
# Check for boxes to fall through terrain
|
|
if PhaseATestData.box_1_pos.z < PhaseATestData.terrain_pos.z:
|
|
PhaseATestData.box_1_fell_through = True
|
|
else:
|
|
PhaseATestData.box_1_fell_through = False
|
|
|
|
if PhaseATestData.box_2_pos.z < PhaseATestData.terrain_pos.z:
|
|
PhaseATestData.box_2_fell_through = True
|
|
else:
|
|
PhaseATestData.box_2_fell_through = False
|
|
|
|
results = 0
|
|
if PhaseATestData.box_1_collided or PhaseATestData.box_1_fell_through:
|
|
results += 1
|
|
if PhaseATestData.box_2_collided or PhaseATestData.box_2_fell_through:
|
|
results += 1
|
|
return results == PhaseATestData.total_results
|
|
|
|
# Checks if we are done collecting data for phase B
|
|
def done_collecting_results_b():
|
|
# type: () -> bool
|
|
|
|
# Update positions
|
|
PhaseBTestData.box_1_pos = azlmbr.components.TransformBus(
|
|
azlmbr.bus.Event, "GetWorldTranslation", PhaseBTestData.box_1
|
|
)
|
|
PhaseBTestData.box_2_pos = azlmbr.components.TransformBus(
|
|
azlmbr.bus.Event, "GetWorldTranslation", PhaseBTestData.box_2
|
|
)
|
|
|
|
# Check for boxes to fall through terrain
|
|
if PhaseBTestData.box_1_pos.z < PhaseBTestData.terrain_pos.z:
|
|
PhaseBTestData.box_1_fell_through = True
|
|
else:
|
|
PhaseBTestData.box_1_fell_through = False
|
|
|
|
if PhaseBTestData.box_2_pos.z < PhaseBTestData.terrain_pos.z:
|
|
PhaseBTestData.box_2_fell_through = True
|
|
else:
|
|
PhaseBTestData.box_2_fell_through = False
|
|
|
|
results = 0
|
|
if PhaseBTestData.box_1_collided or PhaseBTestData.box_1_fell_through:
|
|
results += 1
|
|
if PhaseBTestData.box_2_collided or PhaseBTestData.box_2_fell_through:
|
|
results += 1
|
|
return results == PhaseBTestData.total_results
|
|
|
|
# **** Event Handlers ****
|
|
|
|
# Collision even handler for Phase A
|
|
def on_collision_begin_a(args):
|
|
# type: ([EntityId]) -> None
|
|
collider_id = args[0]
|
|
if (not PhaseATestData.box_1_collided) and PhaseATestData.box_1.Equal(collider_id):
|
|
Report.info("Box_1_A / Terrain_A collision detected")
|
|
PhaseATestData.box_1_collided = True
|
|
if (not PhaseATestData.box_2_collided) and PhaseATestData.box_2.Equal(collider_id):
|
|
Report.info("Box_2_A / Terrain_A collision detected")
|
|
PhaseATestData.box_2_collided = True
|
|
|
|
# Collision event handler for Phase B
|
|
def on_collision_begin_b(args):
|
|
# type: ([EntityId]) -> None
|
|
collider_id = args[0]
|
|
if (not PhaseBTestData.box_1_collided) and PhaseBTestData.box_1.Equal(collider_id):
|
|
Report.info("Box_1_B / Terrain_B collision detected")
|
|
PhaseBTestData.box_1_collided = True
|
|
if (not PhaseBTestData.box_2_collided) and PhaseBTestData.box_2.Equal(collider_id):
|
|
Report.info("Box_2_B / Terrain_B collision detected")
|
|
PhaseBTestData.box_2_collided = True
|
|
|
|
TIME_OUT = 1.5
|
|
|
|
# 1) Open level
|
|
helper.init_idle()
|
|
helper.open_level("Physics", "C3510644_Collider_CollisionGroups")
|
|
|
|
# 2) Enter game mode
|
|
helper.enter_game_mode(Tests.enter_game_mode)
|
|
|
|
# 3) Retrieve and validate entities
|
|
PhaseATestData.box_1 = validate_entity("Box_1_A", Tests.box_1_a_valid)
|
|
PhaseATestData.box_2 = validate_entity("Box_2_A", Tests.box_2_a_valid)
|
|
PhaseATestData.terrain = validate_entity("Terrain_Entity_A", Tests.terrain_a_valid)
|
|
PhaseBTestData.box_1 = validate_entity("Box_1_B", Tests.box_1_b_valid)
|
|
PhaseBTestData.box_2 = validate_entity("Box_2_B", Tests.box_2_b_valid)
|
|
PhaseBTestData.terrain = validate_entity("Terrain_Entity_B", Tests.terrain_b_valid)
|
|
|
|
# Make sure Phase B objects are disabled
|
|
azlmbr.entity.GameEntityContextRequestBus(azlmbr.bus.Broadcast, "DeactivateGameEntity", PhaseBTestData.box_1)
|
|
azlmbr.entity.GameEntityContextRequestBus(azlmbr.bus.Broadcast, "DeactivateGameEntity", PhaseBTestData.box_2)
|
|
azlmbr.entity.GameEntityContextRequestBus(azlmbr.bus.Broadcast, "DeactivateGameEntity", PhaseBTestData.terrain)
|
|
|
|
# 4) *********** Phase A *****************
|
|
|
|
# 4.a) ** Set Up **
|
|
|
|
Report.info(" **** Beginning Phase A **** ")
|
|
|
|
# Locate Phase A entities
|
|
PhaseATestData.box_1_pos = validate_initial_position(PhaseATestData.box_1, Tests.box_1_a_pos_found)
|
|
PhaseATestData.box_2_pos = validate_initial_position(PhaseATestData.box_2, Tests.box_2_a_pos_found)
|
|
PhaseATestData.terrain_pos = validate_initial_position(PhaseATestData.terrain, Tests.terrain_a_pos_found)
|
|
|
|
# Assign Phase A event handler
|
|
handler_a = azlmbr.physics.CollisionNotificationBusHandler()
|
|
handler_a.connect(PhaseATestData.terrain)
|
|
handler_a.add_callback("OnCollisionBegin", on_collision_begin_a)
|
|
|
|
# 4.b) Execute Phase A
|
|
if not helper.wait_for_condition(done_collecting_results_a, TIME_OUT):
|
|
Report.info("Phase A timed out: make sure the level is set up properly or adjust time out threshold")
|
|
|
|
# 4.c) Log results for Phase A
|
|
Report.result(Tests.box_1_a_did_collide_with_terrain, PhaseATestData.box_1_collided)
|
|
Report.result(Tests.box_1_a_did_not_pass_through_terrain, not PhaseATestData.box_1_fell_through)
|
|
Report.info_vector3(PhaseATestData.box_1_pos, "Box_1_A's final position:")
|
|
|
|
Report.result(Tests.box_2_a_did_pass_through_terrain, PhaseATestData.box_2_fell_through)
|
|
Report.result(Tests.box_2_a_did_not_collide_with_terrain, not PhaseATestData.box_2_collided)
|
|
Report.info_vector3(PhaseATestData.box_2_pos, "Box_2_A's final position:")
|
|
|
|
if not PhaseATestData.valid():
|
|
Report.info("Phase A failed test")
|
|
|
|
# Deactivate entities for Phase A
|
|
azlmbr.entity.GameEntityContextRequestBus(azlmbr.bus.Broadcast, "DeactivateGameEntity", PhaseATestData.box_1)
|
|
azlmbr.entity.GameEntityContextRequestBus(azlmbr.bus.Broadcast, "DeactivateGameEntity", PhaseATestData.box_2)
|
|
azlmbr.entity.GameEntityContextRequestBus(azlmbr.bus.Broadcast, "DeactivateGameEntity", PhaseATestData.terrain)
|
|
|
|
# 5) *********** Phase B *****************
|
|
|
|
# 5.a) ** Set Up **
|
|
Report.info(" *** Beginning Phase B *** ")
|
|
# Activate entities for Phase B
|
|
azlmbr.entity.GameEntityContextRequestBus(azlmbr.bus.Broadcast, "ActivateGameEntity", PhaseBTestData.box_1)
|
|
azlmbr.entity.GameEntityContextRequestBus(azlmbr.bus.Broadcast, "ActivateGameEntity", PhaseBTestData.box_2)
|
|
azlmbr.entity.GameEntityContextRequestBus(azlmbr.bus.Broadcast, "ActivateGameEntity", PhaseBTestData.terrain)
|
|
|
|
# Initialize positions for Phase B
|
|
PhaseBTestData.box_1_pos = validate_initial_position(PhaseBTestData.box_1, Tests.box_1_b_pos_found)
|
|
PhaseBTestData.box_2_pos = validate_initial_position(PhaseBTestData.box_2, Tests.box_2_b_pos_found)
|
|
PhaseBTestData.terrain_pos = validate_initial_position(PhaseBTestData.terrain, Tests.terrain_b_pos_found)
|
|
|
|
# Assign Phase B event handler
|
|
handler_b = azlmbr.physics.CollisionNotificationBusHandler()
|
|
handler_b.connect(PhaseBTestData.terrain)
|
|
handler_b.add_callback("OnCollisionBegin", on_collision_begin_b)
|
|
|
|
# 5.b) Execute Phase B
|
|
if not helper.wait_for_condition(done_collecting_results_b, TIME_OUT):
|
|
Report.info("Phase B timed out: make sure the level is set up properly or adjust time out threshold")
|
|
|
|
# 5.c) Log results for Phase B
|
|
Report.result(Tests.box_1_b_did_not_collide_with_terrain, not PhaseBTestData.box_1_collided)
|
|
Report.result(Tests.box_1_b_did_pass_through_terrain, PhaseBTestData.box_1_fell_through)
|
|
Report.info_vector3(PhaseBTestData.box_1_pos, "Box_1_B's final position:")
|
|
|
|
Report.result(Tests.box_2_b_did_not_pass_through_terrain, not PhaseBTestData.box_2_fell_through)
|
|
Report.result(Tests.box_2_b_did_collide_with_terrain, PhaseBTestData.box_2_collided)
|
|
Report.info_vector3(PhaseBTestData.box_2_pos, "Box_2_B's final position:")
|
|
|
|
if not PhaseBTestData.valid():
|
|
Report.info("Phase B failed test")
|
|
|
|
# 6) Exit Game mode
|
|
helper.exit_game_mode(Tests.exit_game_mode)
|
|
Report.info(" **** TEST FINISHED ****")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
from editor_python_test_tools.utils import Report
|
|
Report.start_test(C3510644_Collider_CollisionGroups)
|