Rename to final folder name

This commit is contained in:
AMZN-AlexOteiza
2021-09-10 14:29:17 +02:00
parent ddd662f6b9
commit 5441ab4bac
160 changed files with 0 additions and 0 deletions
@@ -0,0 +1,222 @@
"""
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 : C5959760
# Test Case Title : Check that force region (capsule) exerts point force
# fmt: off
class Tests:
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
box_entity_found = ("Box was found in game", "Box COULD NOT be found in game")
capsule_entity_found = ("Capsule was found in game", "Capsule COULD NOT be found in game")
box_pos_found = ("Box position found", "Box position not found")
capsule_pos_found = ("Capsule position found", "Capsule position not found")
force_region_entered = ("Force region entered", "Force region never entered")
force_exertion_predicted = ("Force exerted was predictable", "The force exerted WAS NOT predicted")
box_fell = ("Box fell", "The box did not fall")
box_was_pushed_x_z = ("Box moved positive X, Z", "Box DID NOT move in positive X, Z direction")
box_no_y_movement = ("Box had no substantial Y movement", "Box HAD substantial Y movement")
capsule_no_move = ("Capsule did not move", "Capsule DID move")
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
time_out = ("Test did not time out", "Test DID time out")
# fmt: on
def ForceRegion_CapsuleShapedForce():
# type: () -> None
"""
Summary:
Runs an automated test to ensure point force from a capsule force region is exerted on rigid body objects.
Level Description:
A cube (entity: Box) set above a capsule force region (entity: Capsule). The Capsule was assigned point force
with magnitude set to 1000. The Box has been set for "gravity enabled"
Expected behavior:
The Box will fall (due to gravity) into the Capsule's force region. The force region should exert the point
force on the Box, applying a positive X and Z force of substantial magnitude.
Test Steps:
1) Loads the level / Enters game mode
2) Retrieve entities
3) Ensures that the test objects (Box and Capsule) are located
3.5) set up variables and handlers for monitoring results
4) Waits for the box to fall into the force region
or for time out if something unexpected happens
5) Logs results
6) Closes the 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.
: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
# Global constants
CLOSE_ENOUGH = 0.001
TIME_OUT = 1.5
# Base class
class EntityBase:
def __init__(self, name):
self.name = name
self.id = None
self.initial_pos = None
self.current_pos = None
# Box child class of EntityBase
class Box(EntityBase):
def __init__(self, name):
EntityBase.__init__(self, name)
self.triggered_pos = None
self.fell = False
self.force_observed = False
def check_for_fall(self):
FALL_BUFFER = 0.2
if not self.fell:
self.fell = (
self.initial_pos.z > self.current_pos.z + FALL_BUFFER
and abs(self.initial_pos.x - self.current_pos.x) < CLOSE_ENOUGH
and abs(self.initial_pos.y - self.current_pos.y) < CLOSE_ENOUGH
)
return self.fell
def check_for_force(self):
FORCE_BUFFER = 0.2
if not self.force_observed:
self.force_observed = (
self.current_pos.z > self.triggered_pos.z + FORCE_BUFFER
and self.current_pos.x > self.triggered_pos.x
and abs(self.triggered_pos.y - self.current_pos.y) < CLOSE_ENOUGH
)
return self.force_observed
# Force Region child class of EntityBase
class ForceRegion(EntityBase):
def __init__(self, name):
EntityBase.__init__(self, name)
self.expected_force_magnitude = None
self.actual_force_vector = None
self.actual_force_magnitude = None
self.forced_entity = None
self.triggered = False
# 1) Open level / Enter game mode
helper.init_idle()
helper.open_level("Physics", "ForceRegion_CapsuleShapedForce")
helper.enter_game_mode(Tests.enter_game_mode)
# 2) Retrieve entities
box = Box("Box")
box.id = general.find_game_entity(box.name)
capsule = ForceRegion("Capsule")
capsule.id = general.find_game_entity(capsule.name)
Report.critical_result(Tests.box_entity_found, box.id.IsValid())
Report.critical_result(Tests.capsule_entity_found, capsule.id.IsValid())
# 3) Log positions for Box and Capsule
box.initial_pos = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", box.id)
capsule.initial_pos = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", capsule.id)
box.current_pos = box.initial_pos
capsule.current_pos = capsule.initial_pos
# validate and print positions to confirm objects were found
Report.critical_result(Tests.box_pos_found, box.initial_pos is not None and not box.initial_pos.IsZero())
Report.critical_result(
Tests.capsule_pos_found, capsule.initial_pos is not None and not capsule.initial_pos.IsZero()
)
capsule.expected_force_magnitude = azlmbr.physics.ForcePointRequestBus(azlmbr.bus.Event, "GetMagnitude", capsule.id)
# 3.5) set up handler
# Force Region Event Handler
def on_force_calculated(args):
# Only store data for first force region calculation
if not capsule.triggered and capsule.id.Equal(args[0]):
capsule.triggered = True
capsule.forced_entity = args[1]
capsule.actual_force_vector = args[2]
capsule.actual_force_magnitude = args[3]
if capsule.forced_entity.Equal(box.id):
box.triggered_pos = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", box.id)
Report.info("Force Region exerted force on {}".format(box.name))
# Assign the handler
handler = azlmbr.physics.ForceRegionNotificationBusHandler()
handler.connect(None)
handler.add_callback("OnCalculateNetForce", on_force_calculated)
def done_collecting_results():
# Update entity positions
capsule.current_pos = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", capsule.id)
box.current_pos = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", box.id)
# Check for three "test complete" conditions
# ! Careful ordering for logic short circuiting. DO NOT SWAP ORDER !
return box.check_for_fall() and capsule.triggered and box.check_for_force()
# 4) wait for force region entry or time out
test_completed = helper.wait_for_condition(done_collecting_results, TIME_OUT)
Report.critical_result(Tests.time_out, test_completed)
# 5) Report findings
Report.result(Tests.box_fell, box.fell)
Report.result(Tests.force_region_entered, capsule.triggered)
Report.result(
Tests.force_exertion_predicted,
abs(capsule.expected_force_magnitude - capsule.actual_force_magnitude) < CLOSE_ENOUGH,
)
Report.result(Tests.box_was_pushed_x_z, box.force_observed)
Report.result(Tests.box_no_y_movement, abs(box.initial_pos.y - box.current_pos.y) < CLOSE_ENOUGH)
Report.result(Tests.capsule_no_move, capsule.initial_pos.IsClose(capsule.current_pos))
# Collected Data Dump
Report.info("******* Collected Data *******")
Report.info("Entity: {}".format(box.name))
Report.info_vector3(box.initial_pos, " Initial Position:")
Report.info_vector3(box.triggered_pos, " Trigger Position:")
Report.info_vector3(box.current_pos, " Final Position:")
Report.info(" Fell: {}".format(box.fell))
Report.info(" Force Observed: {}".format(box.force_observed))
Report.info("******************************")
Report.info("Entity: {}".format(capsule.name))
Report.info_vector3(capsule.initial_pos, " Initial Position:")
Report.info_vector3(capsule.current_pos, " Final Position:")
Report.info(" Expected Force Magnitude: {:.2f}".format(capsule.expected_force_magnitude))
Report.info_vector3(capsule.actual_force_vector, " Actual Force Vector:", capsule.actual_force_magnitude)
Report.info(" Triggered: {}".format(capsule.triggered))
Report.info(
" Triggered Entity: {}".format(
azlmbr.entity.GameEntityContextRequestBus(azlmbr.bus.Broadcast, "GetEntityName", capsule.forced_entity)
)
)
Report.info("******************************")
# 6) Exit Game mode
helper.exit_game_mode(Tests.exit_game_mode)
Report.info("*** FINISHED TEST ***")
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(ForceRegion_CapsuleShapedForce)
@@ -0,0 +1,282 @@
"""
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 : C12868578
# Test Case Title : Check that World space and local space force direction doesn't affect magnitude of force exerted
# fmt: off
class Tests:
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
entity_position = ("All entities in good relative position", "Not all entities in correct position")
sphere_collisions = ("All spheres collided with Force Regions", "Not All spheres collided")
initial_velocity = ("Spheres started moving correctly", "Spheres not moving correctly")
velocity_updated = ("Sphere velocities updated", "Sphere velocities didn't update")
# Z Direction
sphere_0_found = ("sphere_0 is found", "sphere_0 is not found")
sphere_1_found = ("sphere_1 is found", "sphere_1 is not found")
force_region_0_found = ("force_region_0 is found", "force_region_0 is not found")
force_region_1_found = ("force_region_1 is found", "force_region_1 is not found")
local_force_mag_z = ("z-axis Local Space force magnitude valid", "z-axis Local Space force magnitude invalid")
local_force_dir_z = ("z-axis Local Space force direction valid", "z-axis Local Space force direction invalid")
world_force_mag_z = ("z-axis World Space force magnitude valid", "z-axis World Space force magnitude invalid")
world_force_dir_z = ("z-axis World Space force direction valid", "z-axis World Space force direction invalid")
# X Direction
sphere_2_found = ("sphere_2 is found", "sphere_2 is not found")
sphere_3_found = ("sphere_3 is found", "sphere_3 is not found")
force_region_2_found = ("force_region_2 is found", "force_region_2 is not found")
force_region_3_found = ("force_region_3 is found", "force_region_3 is not found")
local_force_mag_x = ("x-axis Local Space force magnitude valid", "x-axis Local Space force magnitude invalid")
local_force_dir_x = ("x-axis Local Space force direction valid", "x-axis Local Space force direction invalid")
world_force_mag_x = ("x-axis World Space force magnitude valid", "x-axis World Space force magnitude invalid")
world_force_dir_x = ("x-axis World Space force direction valid", "x-axis World Space force direction invalid")
# Y Direction
sphere_4_found = ("sphere_4 is found", "sphere_4 is not found")
sphere_5_found = ("sphere_5 is found", "sphere_5 is not found")
force_region_4_found = ("force_region_4 is found", "force_region_4 is not found")
force_region_5_found = ("force_region_5 is found", "force_region_5 is not found")
local_force_mag_y = ("y-axis Local Space force magnitude valid", "y-axis Local Space force magnitude invalid")
local_force_dir_y = ("y-axis Local Space force direction valid", "y-axis Local Space force direction invalid")
world_force_mag_y = ("y-axis World Space force magnitude valid", "y-axis World Space force magnitude invalid")
world_force_dir_y = ("y-axis World Space force direction valid", "y-axis World Space force direction invalid")
# fmt: on
def ForceRegion_DirectionHasNoAffectOnTotalForce():
"""
Summary: Check that world and local space force direction should not affect magnitude of force exerted on entity.
Level Description:
sphere_0 - Directly above force_region_0 with velocity of 10.0 in the negative z direction; has sphere shape
collider, rigid body, and sphere shape
sphere_1 - Directly above force_region_1 with velocity of 10.0 in the negative z direction; has sphere shape
collider, rigid body, and sphere shape
force_region_0 - Directly below sphere_0 with world space force of magnitude 100.0 and direction vector of
<0.0,0.0,999.0>; has box shape collider and force region
force_region_1 - Directly below sphere_1 with local space force of magnitude 100.0 and direction vector of
<0.0,0.0,999.0>; has box shape collider and force region
Expected Behavior: Both spheres bounce off of there respective force regions with a force of magnitude that is close
to 100.0 in positive z direction. The direction is normalized from the manual entered direction input.
Test Steps:
1) Open Level
2) Enter Game Mode
3) Set up and validate entities
4) Wait for collision
5) Wait for velocities to become positive
6) Log and validate results
7) Exit Game Mode
8) 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.
: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
# Constants
FLOAT_THRESHOLD = sys.float_info.epsilon
TIMEOUT = 1
MAGNITUDE_THRESHOLD = 0.1
FORCE_VECTOR_THRESHOLD = 0.001
# Helper Functions
class Entity:
def __init__(self, name):
# type (str, hex) -> None
self.id = general.find_game_entity(name)
self.name = name
self.collision_happened = False
# ID validation
self.found = Tests.__dict__[self.name + "_found"]
Report.critical_result(self.found, self.id.isValid())
@property
def position(self):
# type () -> Vector3
return azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", self.id)
class Sphere(Entity):
def __init__(self, name, axis, force_region):
Entity.__init__(self, name)
self.paired_force_region = force_region
self.axis = axis
self.force_vector = None
self.force_magnitude = None
# Set Handler
self.handler = azlmbr.physics.ForceRegionNotificationBusHandler()
self.handler.connect(None)
self.handler.add_callback("OnCalculateNetForce", self.on_calculate_net_force)
# Report initial values
Report.info_vector3(self.position, "{} initial position: ".format(self.name))
Report.info_vector3(self.velocity, "{} initial velocity: ".format(self.name))
@property
def velocity(self):
# type () -> Vector3
return azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetLinearVelocity", self.id)
@property
def is_moving_in_positive_direction(self):
# type () -> bool
# A List of the attribute names for the velocity (Vector3)
axis = ["x", "y", "z"]
# Finds the index in the list of the attribute in which the sphere is moving in
index = axis.index(self.axis)
# Checking that we are moving along that axis
moving_component = getattr(self.velocity, axis[index]) > 0.0
# Getting rid of moving axis from list
axis.pop(index)
# Checking that the sphere is not moving along either of the remaining two axis.
stationary_components = (
abs(getattr(self.velocity, axis[0])) < FLOAT_THRESHOLD
and abs(getattr(self.velocity, axis[1])) < FLOAT_THRESHOLD
)
return moving_component and stationary_components
def report_values(self):
# type () -> None
# Reports final position and velocity information
Report.info_vector3(self.position, "{} final position: ".format(self.name))
Report.info_vector3(self.velocity, "{} final velocity: ".format(self.name))
def on_calculate_net_force(self, args):
# type (list) -> None
# Flips the collision happened boolean for the sphere object and prints the force values.
if self.paired_force_region.id.Equal(args[0]) and self.id.equal(args[1]) and not self.collision_happened:
self.collision_happened = True
self.force_vector = args[2]
self.force_magnitude = args[3]
# Report force vector information
Report.info_vector3(self.force_vector, "{} had following force vector applied".format(self.name))
Report.info("{} is the applied force magnitude".format(self.force_magnitude))
def validate_local_force_results(sphere):
# type (Sphere) -> None
local_force_direction = Tests.__dict__["local_force_dir_{}".format(sphere.axis)]
local_force_magnitude = Tests.__dict__["local_force_mag_{}".format(sphere.axis)]
Report.result(local_force_direction, check_applied_force_vector(sphere.force_vector))
force_region_magnitude = azlmbr.physics.ForceLocalSpaceRequestBus(azlmbr.bus.Event,"GetMagnitude", sphere.paired_force_region.id)
print(force_region_magnitude)
print("LOOKKKK ABOVE!")
Report.result(local_force_magnitude, abs(sphere.force_magnitude - force_region_magnitude) < MAGNITUDE_THRESHOLD)
def validate_world_force_results(sphere):
# type (Sphere) -> None
world_force_direction = Tests.__dict__["world_force_dir_{}".format(sphere.axis)]
world_force_magnitude = Tests.__dict__["world_force_mag_{}".format(sphere.axis)]
Report.result(world_force_direction, check_applied_force_vector(sphere.force_vector))
force_region_magnitude = azlmbr.physics.ForceWorldSpaceRequestBus(azlmbr.bus.Event,"GetMagnitude", sphere.paired_force_region.id)
Report.result(world_force_magnitude, abs(sphere.force_magnitude - force_region_magnitude) < MAGNITUDE_THRESHOLD)
def check_pair_position(sphere):
# type (Sphere) -> bool
# Ensures sphere lines up with its associated force region
force_region_position = sphere.paired_force_region.position
axis = ["x", "y", "z"]
index = axis.index(sphere.axis)
offset_component = getattr(force_region_position, axis[index]) < getattr(sphere.position, axis[index])
axis.pop(index)
zero_components = (
abs(getattr(force_region_position, axis[0]) - getattr(sphere.position, axis[0])) < FLOAT_THRESHOLD
and abs(getattr(force_region_position, axis[1]) - getattr(sphere.position, axis[1])) < FLOAT_THRESHOLD
)
return offset_component and zero_components
def check_applied_force_vector(vector):
# type (Sphere) -> bool
# Ensures the force vector is within expected threshold. The components of the vector can either be 0 or 1
axis = ["x", "y", "z"]
return all(
[
True
for component in axis
if abs(getattr(vector, component) - 1.00) < FORCE_VECTOR_THRESHOLD
or abs(getattr(vector, component)) < FORCE_VECTOR_THRESHOLD
]
)
# Main Script
helper.init_idle()
# 1) Open Level
helper.open_level("Physics", "ForceRegion_DirectionHasNoAffectOnTotalForce")
# 2) Enter Game Mode
helper.enter_game_mode(Tests.enter_game_mode)
# 3) Set up and validate entities
force_region_0 = Entity("force_region_0")
force_region_1 = Entity("force_region_1")
force_region_2 = Entity("force_region_2")
force_region_3 = Entity("force_region_3")
force_region_4 = Entity("force_region_4")
force_region_5 = Entity("force_region_5")
sphere_0 = Sphere("sphere_0", "z", force_region_0)
sphere_1 = Sphere("sphere_1", "z", force_region_1)
sphere_2 = Sphere("sphere_2", "x", force_region_2)
sphere_3 = Sphere("sphere_3", "x", force_region_3)
sphere_4 = Sphere("sphere_4", "y", force_region_4)
sphere_5 = Sphere("sphere_5", "y", force_region_5)
sphere_list = [sphere_0, sphere_1, sphere_2, sphere_3, sphere_4, sphere_5]
local_force_list = [sphere_1, sphere_3, sphere_5]
world_force_list = [sphere_0, sphere_2, sphere_4]
Report.critical_result(
Tests.entity_position, all([check_pair_position(sphere) for sphere in sphere_list])
)
Report.critical_result(
Tests.initial_velocity,
all([not sphere.is_moving_in_positive_direction for sphere in sphere_list]),
)
# 4) Wait for collision
Report.critical_result(
Tests.sphere_collisions,
helper.wait_for_condition(
lambda: all([sphere.collision_happened for sphere in sphere_list]), TIMEOUT
),
)
# 5) Wait for velocities to become positive
Report.critical_result(
Tests.velocity_updated,
helper.wait_for_condition(
lambda: all([sphere.is_moving_in_positive_direction for sphere in sphere_list]), TIMEOUT
),
)
# 6) Log and validate results
[validate_local_force_results(sphere) for sphere in local_force_list]
[validate_world_force_results(sphere) for sphere in world_force_list]
[sphere.report_values() for sphere in sphere_list]
# 7) Exit Game Mode
helper.exit_game_mode(Tests.exit_game_mode)
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(ForceRegion_DirectionHasNoAffectOnTotalForce)
@@ -0,0 +1,246 @@
"""
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 : C6321601
# Test Case Title : Check that very high values of direction axes of forces do not throw error
# fmt: off
class Tests():
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
find_terrain = ("Terrain found", "Terrain not found")
find_sphere_world_space = ("Sphere above world force region found", "Sphere above world force region not found")
find_sphere_local_space = ("Sphere above local force region found", "Sphere above local force region not found")
find_sphere_point = ("Sphere above point force region found", "Sphere above point force region not found")
find_sphere_simple_drag = ("Sphere above simple drag force region found", "Sphere above simple drag force region not found")
find_sphere_linear_damping = ("Sphere above linear damping force region found", "Sphere above linear damping force region not found")
find_forcevol_world_space = ("World force region found", "World force region not found")
find_forcevol_local_space = ("Local force region found", "Local force region not found")
find_forcevol_point = ("Point force region found", "Point force region not found")
find_forcevol_simple_drag = ("Simple drag force region found", "Simple drag force region not found")
find_forcevol_linear_damping = ("Linear damping force region found", "Linear damping force region not found")
world_force_magnitude = ("World force magnitude equal to expected magnitude", "World force magnitude not equal to expected magnitude")
world_force_direction = ("World force direction equal to expected direction", "World force direction not equal to expected direction")
local_force_magnitude = ("Local force magnitude equal to expected magnitude", "Local force magnitude not equal to expected magnitude")
local_force_direction = ("Local force direction equal to expected direction", "Local force direction not equal to expected direction")
point_force_magnitude = ("Point force magnitude equal to expected magnitude", "Point force magnitude not equal to expected magnitude")
simp_drag_density = ("Simple Drag force density equal to expected value", "Simple Drag force density not equal to expected value")
lin_damp_damping = ("Linear Damping force damping equal to expected value", "Linear Damping force damping not equal to expected value")
error_not_found = ("Error not found", "Error found")
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
# fmt: on
def ForceRegion_HighValuesDirectionAxesWorkWithNoError():
"""
Summary:
Check that very high values of direction axes of forces do not throw error.
Level Description:
Sphere_World_Space, Sphere_Local_Space, Sphere_Point, Sphere_Simple_Drag, Sphere_Linear_Damping
(Entities) Entities with components:
- Physx Collider (Sphere shaped with radius 1.0)
- Mesh(Prmitive sphere mesh)
- PhysX Rigid Body Physics
Below are the entities with common components
- PhysX Collider (Trigger enabled)
- PhysX Force Region(Visible and Debug Forces enabled)
They differ in Force Region Force Type with the following properties:
1) ForceVol_World_Space
Type - World Space - Direction(0.0, 0.0, 999999.0) - Magnitude(999999.0)
2) ForceVol_Local_Space
Type - Local Space - Direction(0.0, 0.0, 999999.0) - Magnitude(999999.0)
3) ForceVol_Point
Type - Point - Magnitude(999999.0)
4) ForceVol_Simple_Drag
Type - Simple Drag - Region Density(999.0)
5) ForceVol_Linear_Damping
Type - Linear Damping - Damping(99.0)
Each sphere is placed above its corresponding force regions.
Each of the force regions are seperated by some distance
Expected Behavior:
The given force should be applied as it is without any error on the Sphere.
We are verifying if the force being applied on each sphere is equal to the expected value
Test Steps:
1) Open level
2) Enter game mode
3) Retrieve and validate entities
4) Add force region handler and validate the forces
5) Exit game mode
6) Close the editor
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Aed 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 sys
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
from editor_python_test_tools.utils import Tracer
import azlmbr.legacy.general as general
import azlmbr.bus
import azlmbr.math as lymath
# Constants
TOLERANCE_PERCENT = 0.001
EXPECTED_DIRECTION = lymath.Vector3(0.0, 0.0, 1.0)
EXPECTED_DAMPING = 99.0
EXPECTED_DENSITY = 400.0
CLOSE_THRESHOLD = 0.0001
class SphereForceRegion:
def __init__(self, force_name):
self.force_name = force_name
self.sphere_name = "Sphere_{}".format(force_name)
self.force_region_name = "ForceVol_{}".format(force_name)
self.sphere_id = general.find_game_entity(self.sphere_name)
self.force_region_id = general.find_game_entity(self.force_region_name)
self.in_force_region = False
self.validate_entities()
def validate_entities(self):
Report.result(Tests.__dict__["find_{}".format(self.sphere_name.lower())], self.sphere_id.IsValid())
Report.result(
Tests.__dict__["find_{}".format(self.force_region_name.lower())], self.force_region_id.IsValid()
)
def validate_world_space_force(args):
Report.info("Validating world space force...")
world_expected_magnitude = azlmbr.physics.ForceWorldSpaceRequestBus(
azlmbr.bus.Event, "GetMagnitude", regions[0].force_region_id
)
world_actual_magnitude = args[3]
Report.info(
"Actual Magnitude: {}\t Expected Magnitude: {}".format(world_actual_magnitude, world_expected_magnitude)
)
Report.result(
Tests.world_force_magnitude,
abs(world_actual_magnitude - world_expected_magnitude) < TOLERANCE_PERCENT * world_expected_magnitude,
)
world_actual_direction = args[2]
Report.result(Tests.world_force_direction, world_actual_direction.IsClose(EXPECTED_DIRECTION, CLOSE_THRESHOLD))
def validate_local_space_force(args):
Report.info("Validating local space force...")
local_expected_magnitude = azlmbr.physics.ForceLocalSpaceRequestBus(
azlmbr.bus.Event, "GetMagnitude", regions[1].force_region_id
)
local_actual_magnitude = args[3]
Report.info(
"Actual Magnitude: {}\t Expected Magnitude: {}".format(local_actual_magnitude, local_expected_magnitude)
)
Report.result(
Tests.local_force_magnitude,
abs(local_actual_magnitude - local_expected_magnitude) < TOLERANCE_PERCENT * local_expected_magnitude,
)
local_actual_direction = args[2]
Report.result(Tests.local_force_direction, local_actual_direction.IsClose(EXPECTED_DIRECTION, CLOSE_THRESHOLD))
def validate_point_force(args):
Report.info("Validating Point space force...")
point_expected_magnitude = azlmbr.physics.ForcePointRequestBus(
azlmbr.bus.Event, "GetMagnitude", regions[2].force_region_id
)
point_actual_magnitude = args[3]
Report.info(
"Actual Magnitude: {}\t Expected Magnitude: {}".format(point_actual_magnitude, point_expected_magnitude)
)
Report.result(
Tests.point_force_magnitude,
abs(point_actual_magnitude - point_expected_magnitude) < TOLERANCE_PERCENT * point_expected_magnitude,
)
def validate_simple_drag_force(args):
Report.info("Validating Simple Drag force...")
simp_drag_density = azlmbr.physics.ForceSimpleDragRequestBus(
azlmbr.bus.Event, "GetDensity", regions[3].force_region_id
)
Report.info("Density: {}\t Expected Density: {}".format(simp_drag_density, EXPECTED_DENSITY))
Report.result(Tests.simp_drag_density, simp_drag_density == EXPECTED_DENSITY)
def validate_linear_damping_force(args):
Report.info("Validating Linear Damping force...")
lin_damp_damping = azlmbr.physics.ForceLinearDampingRequestBus(
azlmbr.bus.Event, "GetDamping", regions[4].force_region_id
)
Report.info("Damping: {}\t Expected Damping: {}".format(lin_damp_damping, EXPECTED_DAMPING))
Report.result(Tests.lin_damp_damping, lin_damp_damping == EXPECTED_DAMPING)
def on_calc_net_force(args):
"""
Args:
args[0] - force region entity
args[1] - entity entering
args[2] - vector
args[3] - magnitude
"""
for index, region in enumerate(regions):
if args[0].Equal(region.force_region_id) and args[1].Equal(region.sphere_id) and not region.in_force_region:
region.in_force_region = True
force_validations[index][1](args)
helper.init_idle()
with Tracer() as entity_error_tracer:
def has_physx_error():
return entity_error_tracer.has_errors
# 1) Open level
helper.open_level("Physics", "ForceRegion_HighValuesDirectionAxesWorkWithNoError")
# 2) Enter game mode
helper.enter_game_mode(Tests.enter_game_mode)
# 3) Retrieve and validate entities
# Terrain
terrain_id = general.find_game_entity("Terrain")
Report.result(Tests.find_terrain, terrain_id.IsValid())
force_validations = (
("World_Space", validate_world_space_force),
("Local_Space", validate_local_space_force),
("Point", validate_point_force),
("Simple_Drag", validate_simple_drag_force),
("Linear_Damping", validate_linear_damping_force),
)
regions = []
for item in force_validations:
regions.append(SphereForceRegion(item[0]))
# 4) Add force region handler and validate the forces
force_notification_handler = azlmbr.physics.ForceRegionNotificationBusHandler()
force_notification_handler.connect(None)
force_notification_handler.add_callback("OnCalculateNetForce", on_calc_net_force)
# Wait for 3 secs, because there is a known bug identified and filed in
# JIRA LY-107677
# The error "[Error] Huge object being added to a COctreeNode, name: 'MeshComponentRenderNode', objBox:"
# will show (if occured) in about 3 sec into the game mode.
helper.wait_for_condition(has_physx_error, 3.0)
# 5) Exit game mode
helper.exit_game_mode(Tests.exit_game_mode)
Report.result(Tests.error_not_found, not has_physx_error())
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(ForceRegion_HighValuesDirectionAxesWorkWithNoError)
@@ -0,0 +1,165 @@
# coding=utf-8
"""
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 : C5959764
# Test Case Title : Check that rigid body (Cube) gets impulse from force region
# fmt: off
class Tests:
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
find_cube = ("Entity Cube found", "Cube not found")
find_force_region = ("Force Region found", "Force Region not found")
cube_gained_height = ("Cube went up", "Cube didn't go up")
force_region_success = ("Force Region impulsed Cube", "Force Region didn't impulse Cube")
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
tests_completed = ("Tests completed", "Tests did not complete")
# fmt: on
def ForceRegion_ImpulsesBoxShapedRigidBody():
"""
This run() function will open a a level and validate that a Cube gets impulsed by a force region.
It does this by:
1) Open level
2) Enters Game mode
3) Finds the entities in the scene
4) Gets the position of the Cube
5) Listens for Cube to enter the force region
6) Gets the vector and magnitude when Cube is in force region
7) Lets the Cube travel up
8) Gets new position of Cube
9) Validate the results
10) Exits game mode and editor
"""
# Setup path
import os, 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
class Cube:
id = None
gained_height = False # Did the Cube gain height
z_end_position = 0
z_start_position = 0
# Listen for Force Region events
class RegionData:
def __init__(self, ID):
self.force_region_id = ID
self.force_region_entered = False
self.force_vector = None
self.force_magnitude_on_cube = 0 # Magnitude applied on cube
self.force_region_magnitude = 0 # Magnitude value for the force region set in the editor
self.force_region_magnitude_range = (
0.01
) # Delta value allowed between magnitude force_magnitude_on_cube and force_region_magnitude
def force_region_in_range(self):
return abs(self.force_magnitude_on_cube - self.force_region_magnitude) < 0.01 # 0.01 for buffer room
def ifVectorClose(vec1, vec2):
return abs(vec1 - vec2) < 0.01 # 0.01 For buffer room for dif in the two vectors.
helper.init_idle()
# *****Variables*****
TIME_OUT = 4.0 # Seconds
UPWARD_Z_VECTOR = 1.0
# Open level
helper.open_level("Physics", "ForceRegion_ImpulsesBoxShapedRigidBody")
# Enter game mode
helper.enter_game_mode(Tests.enter_game_mode)
# Get Entities
Cube.id = general.find_game_entity("Cube")
Report.critical_result(Tests.find_cube, Cube.id.IsValid())
RegionObject = RegionData(general.find_game_entity("Force Region"))
Report.critical_result(Tests.find_force_region, RegionObject.force_region_id.IsValid())
# Set values for cube and force region
Cube.z_start_position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldZ", Cube.id)
RegionObject.force_region_magnitude = azlmbr.physics.ForcePointRequestBus(
azlmbr.bus.Event, "GetMagnitude", RegionObject.force_region_id
)
Report.info("Cube start z position = {}".format(Cube.z_start_position))
# Force Region Event Handler
def on_calculate_net_force(args):
"""
Called when there is a collision in the level
Args:
args[0] - force region entity
args[1] - entity entering
args[2] - vector
args[3] - magnitude
"""
assert RegionObject.force_region_id.Equal(args[0])
vect = args[2]
mag = args[3]
if not RegionObject.force_region_entered:
RegionObject.force_region_entered = True
RegionObject.force_vector = vect
RegionObject.force_magnitude_on_cube = mag
Report.info("Force Region entered")
# Assign the handler
handler = azlmbr.physics.ForceRegionNotificationBusHandler()
handler.connect(None)
handler.add_callback("OnCalculateNetForce", on_calculate_net_force)
# Give cube time to travel. Exit when cube is done moving or time runs out.
def test_completed():
Cube.z_end_position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldZ", Cube.id)
Cube.gained_height = Cube.z_end_position > (Cube.z_start_position + 0.5) # 0.5 for buffer
# Validate if cube gained height and entered force region
if Cube.gained_height and RegionObject.force_region_entered:
Report.success(Tests.cube_gained_height)
return True
return False
# Wait for test to complete
test_is_completed = helper.wait_for_condition(test_completed, TIME_OUT)
if not test_is_completed:
Report.info("The test timed out, check log for possible solutions or adjust the time out time")
Report.failure(Tests.tests_completed)
else:
# Did Force Region succeed. True if vector z is close to 1 and force region magnitude is close to magnitude applied on cube
force_region_result = (
ifVectorClose(RegionObject.force_vector.z, UPWARD_Z_VECTOR) and RegionObject.force_region_in_range()
)
Report.result(Tests.force_region_success, force_region_result)
Report.success(Tests.tests_completed)
# Report test info to log
Report.info("******* FINAL ENTITY INFORMATION *********")
Report.info("Cube Entered force region = {}".format(RegionObject.force_region_entered))
Report.info("Start Z Position = {} End Z Position = {}".format(Cube.z_start_position, Cube.z_end_position))
Report.info("Cube Gained height = {}".format(Cube.gained_height))
Report.info("Vector = {} Magnitude = {}".format(RegionObject.force_vector.z, RegionObject.force_magnitude_on_cube))
# Exit game mode and close the editor
Report.result(Tests.tests_completed, test_is_completed)
helper.exit_game_mode(Tests.exit_game_mode)
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(ForceRegion_ImpulsesBoxShapedRigidBody)
@@ -0,0 +1,169 @@
# coding=utf-8
"""
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 : C5959764
# Test Case Title : Check that rigid body (Capsule) gets impulse from force region
# fmt: off
class Tests:
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
find_capsule = ("Entity Capsule found", "Capsule not found")
find_force_region = ("Force Region found", "Force Region not found")
capsule_gained_height = ("Capsule went up", "Capsule didn't go up")
force_region_success = ("Force Region impulsed Capsule", "Force Region didn't impulse Capsule")
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
tests_completed = ("Tests completed", "Tests did not complete")
# fmt: on
def ForceRegion_ImpulsesCapsuleShapedRigidBody():
"""
This run() function will open a a level and validate that a Capsule gets impulsed by a force region.
It does this by:
1) Open level
2) Enters Game mode
3) Finds the entities in the scene
4) Gets the position of the Capsule
5) Listens for Capsule to enter the force region
6) Gets the vector and magnitude when Capsule is in force region
7) Lets the Capsule travel up
8) Gets new position of Capsule
9) Validate the results
10) Exits game mode and editor
"""
# Setup path
import os, 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
class Capsule:
id = None
gained_height = False # Did the Capsule gain height
z_end_position = 0
z_start_position = 0
# Listen for Force Region events
class RegionData:
def __init__(self, ID):
self.force_region_id = ID
self.force_region_entered = False
self.force_vector = None
self.force_magnitude_on_capsule = 0 # Magnitude applied on Capsule
self.force_region_magnitude = 0 # Magnitude value for the force region set in the editor
self.force_region_magnitude_range = (
0.01
) # Delta value allowed between magnitude force_magnitude_on_capsule and force_region_magnitude
def force_region_in_range(self):
return abs(self.force_magnitude_on_capsule - self.force_region_magnitude) < 0.01 # 0.01 for buffer room
def ifVectorClose(vec1, vec2):
return abs(vec1 - vec2) < 0.01 # 0.01 For buffer room for dif in the two vectors.
helper.init_idle()
# *****Variables*****
TIME_OUT = 4.0 # Seconds
UPWARD_Z_VECTOR = 1.0
# Open level
helper.open_level("Physics", "ForceRegion_ImpulsesCapsuleShapedRigidBody")
# Enter game mode
helper.enter_game_mode(Tests.enter_game_mode)
# Get Entities
Capsule.id = general.find_game_entity("Capsule")
Report.critical_result(Tests.find_capsule, Capsule.id.IsValid())
RegionObject = RegionData(general.find_game_entity("Force Region"))
Report.critical_result(Tests.find_force_region, RegionObject.force_region_id.IsValid())
# Set values for Capsule and force region
Capsule.z_start_position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldZ", Capsule.id)
RegionObject.force_region_magnitude = azlmbr.physics.ForcePointRequestBus(
azlmbr.bus.Event, "GetMagnitude", RegionObject.force_region_id
)
Report.info("Capsule start z position = {}".format(Capsule.z_start_position))
# Force Region Event Handler
def on_calculate_net_force(args):
"""
Called when there is a collision in the level
Args:
args[0] - force region entity
args[1] - entity entering
args[2] - vector
args[3] - magnitude
"""
assert RegionObject.force_region_id.Equal(args[0])
vect = args[2]
mag = args[3]
if not RegionObject.force_region_entered:
RegionObject.force_region_entered = True
RegionObject.force_vector = vect
RegionObject.force_magnitude_on_capsule = mag
Report.info("Force Region entered")
# Assign the handler
handler = azlmbr.physics.ForceRegionNotificationBusHandler()
handler.connect(None)
handler.add_callback("OnCalculateNetForce", on_calculate_net_force)
# Give Capsule time to travel. Exit when Capsule is done moving or time runs out.
def test_completed():
Capsule.z_end_position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldZ", Capsule.id)
Capsule.gained_height = Capsule.z_end_position > (Capsule.z_start_position + 0.5) # 0.5 for buffer
# Validate if Capsule gained height and entered force region
if Capsule.gained_height and RegionObject.force_region_entered:
Report.success(Tests.capsule_gained_height)
return True
return False
# Wait for test to complete
test_is_completed = helper.wait_for_condition(test_completed, TIME_OUT)
if not test_is_completed:
Report.info("The test timed out, check log for possible solutions or adjust the time out time")
Report.failure(Tests.tests_completed)
else:
# Did Force Region succeed. True if vector z is close to 1 and force region magnitude is close to magnitude applied on Capsule
force_region_result = (
ifVectorClose(RegionObject.force_vector.z, UPWARD_Z_VECTOR) and RegionObject.force_region_in_range()
)
Report.result(Tests.force_region_success, force_region_result)
Report.success(Tests.tests_completed)
# Report test info to logSS
Report.info("******* FINAL ENTITY INFORMATION *********")
Report.info("Capsule Entered force region = {}".format(RegionObject.force_region_entered))
Report.info("Start Z Position = {} End Z Position = {}".format(Capsule.z_start_position, Capsule.z_end_position))
Report.info("Capsule Gained height = {}".format(Capsule.gained_height))
Report.info(
"Vector = {} Magnitude = {}".format(RegionObject.force_vector.z, RegionObject.force_magnitude_on_capsule)
)
# Exit game mode and close the editor
Report.result(Tests.tests_completed, test_is_completed)
helper.exit_game_mode(Tests.exit_game_mode)
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(ForceRegion_ImpulsesCapsuleShapedRigidBody)
@@ -0,0 +1,171 @@
# coding=utf-8
"""
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 : C5959765
# Test Case Title : Check that rigid body (asset) gets impulse from force region
# fmt: off
class Tests:
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
find_asset = ("Entity asset found", "Asset not found")
find_force_region = ("Force Region found", "Force Region not found")
asset_gained_height = ("Asset went up", "Asset didn't go up")
force_region_success = ("Force Region impulsed asset", "Force Region didn't impulse asset")
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
tests_completed = ("Tests completed", "Tests did not complete")
# fmt: on
def ForceRegion_ImpulsesPxMeshShapedRigidBody():
"""
# This run() function will open a a level and validate that a asset gets impulsed by a force region.
# It does this by:
# 1) Open level
# 2) Enters Game mode
# 3) Finds the entities in the scene
# 4) Set values for Asset and force region
# 5) Listens for asset to enter the force region
# 6) Gets the vector and magnitude when asset is in force region
# 7) Lets the asset travel up
# 8) Validate if Asset gained height and entered force region
# 9) Checks if test completed
# 10) Exits game mode and editor
# Level setup: Sedan asset above force region
# First Asset: Name = "Sedan" This entity should drop vertically, collide with force region, and be shot up
# First force region: Name = "Force Region" Should shoot Sedan entity up upon entry
"""
# Setup path
import os, 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
class Asset:
id = None
gained_height = False # Did the Asset gain height
z_end_position = 0
z_start_position = 0
# Listen for Force Region events
class RegionData:
def __init__(self, ID):
self.force_region_id = ID
self.force_region_entered = False
self.force_vector = None
self.force_magnitude_on_asset = 0 # Magnitude applied on Asset
self.force_region_magnitude = 0 # Magnitude value for the force region set in the editor
self.force_region_magnitude_range = (
0.01
) # Delta value allowed between magnitude force_magnitude_on_asset and force_region_magnitude
def force_region_in_range(self):
return abs(self.force_magnitude_on_asset - self.force_region_magnitude) < 0.01 # 0.01 for buffer room
def ifVectorAxisClose(vec1, vec2):
return abs(vec1 - vec2) < 0.01 # 0.01 For buffer room for dif in the two vectors.
helper.init_idle()
# *****Variables*****
TIME_OUT = 4.0 # Seconds
UPWARD_Z_VECTOR = 1.0
# 1) Open level
helper.open_level("Physics", "ForceRegion_ImpulsesPxMeshShapedRigidBody")
# 2) Enters Game mode
helper.enter_game_mode(Tests.enter_game_mode)
# 3) Finds the entities in the scene
Asset.id = general.find_game_entity("Sedan")
Report.critical_result(Tests.find_asset, Asset.id.IsValid())
RegionObject = RegionData(general.find_game_entity("Force Region"))
Report.critical_result(Tests.find_force_region, RegionObject.force_region_id.IsValid())
# 4) Set values for Asset and force region
Asset.z_start_position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldZ", Asset.id)
RegionObject.force_region_magnitude = azlmbr.physics.ForcePointRequestBus(
azlmbr.bus.Event, "GetMagnitude", RegionObject.force_region_id
)
Report.info("Asset start z position = {}".format(Asset.z_start_position))
# 5) Listens for asset to enter the force region
def on_calculate_net_force(args):
"""
Called when there is a collision in the level
Args:
args[0] - force region entity
args[1] - entity entering
args[2] - vector
args[3] - magnitude
"""
# 6) Gets the vector and magnitude when asset is in force region
vect = args[2]
mag = args[3]
if RegionObject.force_region_id.Equal(args[0]) and not RegionObject.force_region_entered:
RegionObject.force_region_entered = True
RegionObject.force_vector = vect
RegionObject.force_magnitude_on_asset = mag
Report.info("Force Region entered")
# Assign the handler
handler = azlmbr.physics.ForceRegionNotificationBusHandler()
handler.connect(None)
handler.add_callback("OnCalculateNetForce", on_calculate_net_force)
def test_completed():
# test_completed() will return a bool saying if all the Necessary actions in the test have been completed.
# Necessary Actions: 1) Asset entered Force Region 2) Asset end_height > start_height
Asset.z_end_position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldZ", Asset.id)
Asset.gained_height = Asset.z_end_position > (Asset.z_start_position + 0.5) # 0.5 for buffer
# 8) Validate if Asset gained height and entered force region
if Asset.gained_height and RegionObject.force_region_entered:
Report.success(Tests.asset_gained_height)
return True
return False
# 7) Lets the asset travel up
test_is_completed = helper.wait_for_condition(test_completed, TIME_OUT)
# 9) Checks if test completed
if not test_is_completed:
Report.info("The test timed out, check log for possible solutions or adjust the time out time")
Report.failure(Tests.tests_completed)
else:
# Did Force Region succeed. True if vector z is close to 1 and force region magnitude is close to magnitude applied on Asset
force_region_result = (
ifVectorAxisClose(RegionObject.force_vector.z, UPWARD_Z_VECTOR) and RegionObject.force_region_in_range()
)
Report.result(Tests.force_region_success, force_region_result)
Report.success(Tests.tests_completed)
# Report test info to log
Report.info("******* FINAL ENTITY INFORMATION *********")
Report.info("Asset Entered force region = {}".format(RegionObject.force_region_entered))
Report.info("Start Z Position = {} End Z Position = {}".format(Asset.z_start_position, Asset.z_end_position))
Report.info("Asset Gained height = {}".format(Asset.gained_height))
Report.info("Vector = {} Magnitude = {}".format(RegionObject.force_vector.z, RegionObject.force_magnitude_on_asset))
# 10) Exit game mode and close the editor
Report.result(Tests.tests_completed, test_is_completed)
helper.exit_game_mode(Tests.exit_game_mode)
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(ForceRegion_ImpulsesPxMeshShapedRigidBody)
@@ -0,0 +1,268 @@
"""
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 : C5932042
# Test Case Title : Check that force region exerts linear damping force on rigid bodies
# fmt: off
class Tests:
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
sphere_validated = ("Sphere entity validated", "Sphere entity NOT validated")
force_region_validated = ("Force Region validated", "Force Region NOT validated")
trigger_validated = ("Trigger entity validated", "Trigger entity NOT validated")
sphere_pos_found = ("Sphere position found", "Sphere position NOT found")
force_region_pos_found = ("Force Region position found", "Force Region position NOT found")
trigger_pos_found = ("Trigger position found", "Trigger position NOT found")
level_setup = ("Level looks set up right", "Level NOT set up right")
damping_force_entered = ("Sphere entered linear damping region", "Linear damping region never entered")
damping_force_expected = ("Damping force was as expected", "Damping force differed from expected")
sphere_slowed = ("Sphere slowed down", "Sphere DID NOT slow down")
sphere_stopped = ("Sphere entity stopped", "Sphere entity DID NOT stop")
force_region_no_move = ("Force Region did not move", "Fore Region DID move")
trigger_no_move = ("Trigger did not move", "Trigger DID move")
timed_out = ("The test did not time out", "The test TIMED OUT")
trigger_not_triggered = ("The Trigger was not triggered", "The Trigger WAS triggered")
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
# fmt: on
def ForceRegion_LinearDampingForceOnRigidBodies():
# type: () -> None
"""
Summary:
Runs an automated test to ensure linear damping is exerted on rigid body objects from force regions.
Level Description:
A sphere (entity: Sphere) in positioned above a large cube force region (entity: force_region_entity) who is
assigned a linear damping force with damping of 10.0. The Sphere has gravity enabled, and is positioned high
enough for gravity to accelerate it faster than the maximum velocity inside the linear damping force region.
Expected Behavior:
When game mode is entered, gravity should accelerate the Sphere downward. The velocity of the Sphere should peak
right before it enters force_region_entity. Upon entering the force region, the Sphere should noticeably slow
down. The slower velocity should have a substantially larger (less negative) velocity.
Test Steps:
0) Define useful classes and constants
1) Loads the level / Enters game mode
2) Retrieve and validate entities
3) Ensures that the test object (Sphere) is located
3.5) Set up event handlers
4) Execute test until exit condition met
5) Logs results
5.5) Dump all collected data to log
6) Close the 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.
: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.math as azmath
# Entity base class: Handles basic entity data
class EntityBase:
def __init__(self, name):
self.name = name
self.id = None
self.initial_pos = None
self.current_pos = None
# Specific Sphere class
class Sphere(EntityBase):
def __init__(self, name):
EntityBase.__init__(self, name)
self.initial_velocity = None
self.initial_velocity_magnitude = None
self.current_velocity = None
self.slowed = False
self.stopped = False
def check_for_stop(self):
if not self.stopped:
self.current_velocity = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetLinearVelocity", self.id)
self.slowed = self.initial_velocity_magnitude > self.current_velocity.GetLength() + (
0.5 * self.initial_velocity_magnitude
)
self.stopped = self.current_velocity.IsZero()
return self.stopped
# Specific Force Region class
class ForceRegion(EntityBase):
def __init__(self, name):
EntityBase.__init__(self, name)
self.entered = False
self.object_entered = None
self.expected_force_direction = None
self.actual_force_vector = None
self.actual_force_magnitude = None
self.handler = None
# Specific Trigger class
class Trigger(EntityBase):
def __init__(self, name):
EntityBase.__init__(self, name)
self.triggered = False
self.triggering_obj = None
self.handler = None
# Constants
CLOSE_ENOUGH = 0.001
TIME_OUT = 3.0
INITIAL_VELOCITY = azmath.Vector3(0.0, 0.0, -10.0)
# 1) Open level / Enter game mode
helper.init_idle()
helper.open_level("Physics", "ForceRegion_LinearDampingForceOnRigidBodies")
helper.enter_game_mode(Tests.enter_game_mode)
# 2) Retrieve and validate entities
sphere = Sphere("Sphere")
sphere.id = general.find_game_entity(sphere.name)
force_region = ForceRegion("ForceRegion")
force_region.id = general.find_game_entity(force_region.name)
trigger = Trigger("Trigger")
trigger.id = general.find_game_entity(trigger.name)
Report.critical_result(Tests.sphere_validated, sphere.id.IsValid())
Report.critical_result(Tests.force_region_validated, force_region.id.IsValid())
Report.critical_result(Tests.trigger_validated, trigger.id.IsValid())
# 3) Log Entities' positions and initial data
sphere.initial_pos = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", sphere.id)
force_region.initial_pos = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", force_region.id)
trigger.initial_pos = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", trigger.id)
Report.critical_result(Tests.sphere_pos_found, sphere.initial_pos is not None and not sphere.initial_pos.IsZero())
Report.critical_result(
Tests.force_region_pos_found, force_region.initial_pos is not None and not force_region.initial_pos.IsZero()
)
Report.critical_result(
Tests.trigger_pos_found, trigger.initial_pos is not None and not trigger.initial_pos.IsZero()
)
sphere.initial_velocity = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetLinearVelocity", sphere.id)
level_correct = (
(abs(sphere.initial_pos.y - force_region.initial_pos.y) < CLOSE_ENOUGH)
and (abs(sphere.initial_pos.y - trigger.initial_pos.y) < CLOSE_ENOUGH)
and (abs(sphere.initial_pos.x - force_region.initial_pos.x) < CLOSE_ENOUGH)
and (abs(sphere.initial_pos.x - trigger.initial_pos.x) < CLOSE_ENOUGH)
and (sphere.initial_pos.z > force_region.initial_pos.z > trigger.initial_pos.z)
and sphere.initial_velocity.IsClose(INITIAL_VELOCITY, CLOSE_ENOUGH)
)
Report.critical_result(Tests.level_setup, level_correct)
sphere.current_pos = sphere.initial_pos
force_region.current_pos = force_region.initial_pos
trigger.current_pos = trigger.initial_pos
force_region.expected_force_direction = sphere.initial_velocity.MultiplyFloat(-1.0)
force_region.expected_force_direction.Normalize()
sphere.current_velocity = sphere.initial_velocity
sphere.initial_velocity_magnitude = sphere.initial_velocity.GetLength()
# 3.5) Set up variables and handler for observing force region interaction
def done_collecting_results():
# Update current positions
sphere.current_pos = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", sphere.id)
force_region.current_pos = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", force_region.id)
trigger.current_pos = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", trigger.id)
return force_region.entered and sphere.check_for_stop()
# Force Region Event Handler
def on_calc_net_force(args):
if args[0].Equal(force_region.id):
if args[1].Equal(sphere.id):
if not force_region.entered:
force_region.entered = True
force_region.object_entered = sphere
force_region.actual_force_vector = args[2]
force_region.actual_force_magnitude = args[3]
Report.info("Entity: {} entered entity: {}'s volume".format(sphere.name, force_region.name))
def on_trigger_entered(args):
if args[0].Equal(sphere.id):
trigger.triggered = True
trigger.triggering_obj = sphere
# Assign event handlers
force_region.handler = azlmbr.physics.ForceRegionNotificationBusHandler()
force_region.handler.connect(None)
force_region.handler.add_callback("OnCalculateNetForce", on_calc_net_force)
trigger.handler = azlmbr.physics.TriggerNotificationBusHandler()
trigger.handler.connect(trigger.id)
trigger.handler.add_callback("OnTriggerEnter", on_trigger_entered)
# 4) Execute test until exit condition is met
Report.critical_result(Tests.timed_out, helper.wait_for_condition(done_collecting_results, TIME_OUT))
# 5) Log results
Report.result(Tests.damping_force_entered, force_region.entered)
Report.result(
Tests.damping_force_expected,
force_region.actual_force_vector.IsClose(force_region.expected_force_direction, CLOSE_ENOUGH),
)
Report.result(Tests.sphere_slowed, sphere.slowed)
Report.result(Tests.sphere_stopped, sphere.stopped)
Report.result(Tests.trigger_not_triggered, not trigger.triggered)
Report.result(Tests.force_region_no_move, force_region.initial_pos.IsClose(force_region.current_pos, CLOSE_ENOUGH))
Report.result(Tests.trigger_no_move, trigger.initial_pos.IsClose(trigger.current_pos, CLOSE_ENOUGH))
# 5.5) Collected Data Dump
Report.info(" ********** Collected Data ***************")
Report.info("{}:".format(sphere.name))
Report.info_vector3(sphere.initial_pos, " Initial position:")
Report.info_vector3(sphere.current_pos, " Final position:")
Report.info_vector3(sphere.initial_velocity, " Initial velocity:")
Report.info_vector3(sphere.current_velocity, " Final velocity:")
Report.info(" Slowed: {}".format(sphere.slowed))
Report.info(" Stopped: {}".format(sphere.stopped))
Report.info("***********************************")
Report.info("{}:".format(force_region.name))
Report.info_vector3(force_region.initial_pos, " Initial position:")
Report.info_vector3(force_region.current_pos, " Final position:")
Report.info_vector3(force_region.expected_force_direction, " Expected Force Direction:")
Report.info_vector3(
force_region.actual_force_vector, " Actual Force Direction:", force_region.actual_force_magnitude
)
Report.info(" Entered: {}".format(force_region.entered))
Report.info(" Object Entered: {}".format(force_region.object_entered.name))
Report.info("***********************************")
Report.info("{}:".format(trigger.name))
Report.info_vector3(trigger.initial_pos, " Initial position:")
Report.info_vector3(trigger.current_pos, " Final position:")
Report.info(" Triggered: {}".format(trigger.triggered))
Report.info(" Triggering Object: {}".format(trigger.triggering_obj))
Report.info("***********************************")
# 6) Exit Game mode
helper.exit_game_mode(Tests.exit_game_mode)
Report.info("*** FINISHED TEST ***")
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(ForceRegion_LinearDampingForceOnRigidBodies)
@@ -0,0 +1,160 @@
"""
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 : C5932041
# Test Case Title : Check that force region exerts local space force on rigid bodies
# Sphere drops and is acted upon in an upward and positive x-ward direction by a force
# with a magnitude close to the assigned force region magnitude when it reaches the force region.
# fmt: off
class Tests:
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
find_sphere = ("Sphere entity found", "Sphere entity not found")
find_box = ("Box entity found", "Box entity not found")
sphere_pos_found = ("Sphere position found", "Sphere position not found")
sphere_velocity_found = ("Sphere has downward velocity", "Sphere does not have downward velocity")
box_pos_found = ("Box position found", "Box position not found")
force_region_entered = ("Force region entered", "Force region never entered")
force_x_component_detected = ("Force x-component detected on the Sphere", "Force x-component was not detected on the Sphere")
force_z_component_detected = ("Force z-component detected on the Sphere", "Force z-component was not detected on the Sphere")
force_y_component_not_detected = ("Force y-component not detected on the Sphere", "Force y-component was detected on the Sphere")
force_magnitude_detected = ("Force magnitude detected on the Sphere", "Force magnitude was not detected on the Sphere")
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
# fmt: on
def ForceRegion_LocalSpaceForceOnRigidBodies():
"""
Summary:
Runs an automated test to ensure that when a rigid body enters a force region a local space force is exerted.
Level Description:
Box (entity) - suspended above terrain at 45 degree angle with Direction Z = 1.0, magnitude = 1000,
and gravity disabled; contains box mesh, PhysX Collider, and PhysX Force Region
Sphere (entity) - suspended above Box with slight x-axis offset, initial velocity in negative z direction,
gravity disabled; contains sphere mesh, PhysX Rigid Body, PhysX Collider
Expected Behavior:
When game mode is entered, the Sphere entity will travel torward the terrain.
It will reach the force region of the Box entity and be imbued with a net force in the x and z directions.
Test Steps:
1) Open level
2) Enter game mode
3) Retrieve entities
4) Log Sphere velocity and positions for Sphere and Box
5) Set up handler and variables
6) Wait for force region entry or time out
7) Look for positive x, zero y, positive z force with a valid magnitude, and report findings
8) Exit game mode
9) Close the 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.
: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
# Constants
TIMEOUT = 2.0
TOLERANCE = 1
MAGNITUDE = 1000 # Magnitude assigned to the force region
FORCE_Y_TOLERANCE = sys.float_info.epsilon
helper.init_idle()
# 1) Open level
helper.open_level("Physics", "ForceRegion_LocalSpaceForceOnRigidBodies")
# 2) Enter game mode
helper.enter_game_mode(Tests.enter_game_mode)
# 3) Retrieve entities
sphere_id = general.find_game_entity("Sphere")
box_id = general.find_game_entity("Box")
Report.critical_result(Tests.find_sphere, sphere_id.isValid())
Report.critical_result(Tests.find_box, box_id.isValid())
# 4) Log Sphere velocity and positions for Sphere and Box
sphere_pos = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", sphere_id)
box_pos = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", sphere_id)
# Validate and print positions and sphere velocity
sphere_pos_found = sphere_pos is not None and sphere_pos.x != 0 and sphere_pos.y != 0 and sphere_pos.z != 0
Report.critical_result(Tests.sphere_pos_found, sphere_pos_found)
Report.info_vector3(sphere_pos, "Sphere Position:")
sphere_velocity = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetLinearVelocity", sphere_id)
Report.critical_result(Tests.sphere_velocity_found, sphere_velocity.z < 0)
Report.info_vector3(sphere_velocity, "Sphere Initial Velocity:")
box_pos_found = box_pos is not None and box_pos.x != 0 and box_pos.y != 0 and box_pos.z != 0
Report.critical_result(Tests.box_pos_found, box_pos_found)
Report.info_vector3(box_pos, "Box Position:")
# 5) Set up handler and variables
class RegionData:
force_region_entered = False
force_vector = None
force_magnitude = 0
# Force Region Event Handler
def on_force_region_entered(args):
region_id = args[0]
object_id = args[1]
force_vector = args[2]
force_magnitude = args[3]
if region_id.Equal(box_id) and object_id.Equal(sphere_id):
if not RegionData.force_region_entered:
RegionData.force_region_entered = True
RegionData.force_vector = force_vector
RegionData.force_magnitude = force_magnitude
Report.info("Force Region entered")
# Assign the handler
handler = azlmbr.physics.ForceRegionNotificationBusHandler()
handler.connect(None)
handler.add_callback("OnCalculateNetForce", on_force_region_entered)
# 6) Wait for force region entry or time out
helper.wait_for_condition(lambda: RegionData.force_region_entered, TIMEOUT)
# 7) Look for positive x, positive z force with a valid magnitude, and report findings
force_x_component_detected = RegionData.force_vector.x > 0
force_z_component_detected = RegionData.force_vector.z > 0
force_y_component_detected = abs(RegionData.force_vector.y) > FORCE_Y_TOLERANCE
force_magnitude_detected = abs(RegionData.force_magnitude - MAGNITUDE) < TOLERANCE
Report.result(Tests.force_region_entered, RegionData.force_region_entered)
Report.info_vector3(RegionData.force_vector, "Force vector detected", RegionData.force_magnitude)
Report.result(Tests.force_x_component_detected, force_x_component_detected)
Report.result(Tests.force_z_component_detected, force_z_component_detected)
Report.result(Tests.force_y_component_not_detected, not force_y_component_detected)
Report.result(Tests.force_magnitude_detected, force_magnitude_detected)
# 8) Exit game mode
helper.exit_game_mode(Tests.exit_game_mode)
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(ForceRegion_LocalSpaceForceOnRigidBodies)
@@ -0,0 +1,162 @@
"""
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 : C5968760
# Test Case Title : Check moving force region changes net force
# fmt: off
class Tests():
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
find_sphere = ("SphereRigidBody found", "SphereRigidBody not found")
find_force_region = ("ForceRegionBox is found", "ForceRegionBox is not found")
sphere_dropped = ("Sphere dropped down", "Sphere did not drop down")
sphere_bounced = ("Sphere bounced to its left", "Sphere did not bounce to its left")
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
# fmt: on
def ForceRegion_MovingForceRegionChangesNetForce():
"""
Summary:
Check moving force region changes net force.
Level Description:
The SphereRigidBody entity is placed above the ForceRegionBox entity.
ForceRegionBox (entity) - Entity with PhysX Force Region, Mesh, PhysX Collider
SphereRigidBody (entity) - Entity with PhysX Rigid body, Mesh and collider components
Expected Behavior:
We are checking if the ball falls down initially and then moving the force region to the right to verify if the
ball bounces off to the left when it collides with the force region.
Test Steps:
1) Open level
2) Enter game mode
3) Retrieve and validate entities
4) Get the initial position of the Sphere (rigid body)
5) Move the object to right (X - direction) and rotate in Y - direction
6) Check if the ball is falling down
7) Add force region notification handler
8) Wait till the ball enters the force region
9) Check if the ball has bounced and moved left
10) Exit game mode
11) Close the 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.
: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
# Constants
TIMEOUT = 3.0 # wait a maximum of 3 seconds
SPHERE_RADIUS = 0.5
TRANSLATION_OFFSET = 0.2
ROTATION_OFFSET = -0.005
class Sphere:
id = None
initial_position = None
current_position = None
z_at_collision = None
in_force_region = False
bounced = False
class ForceRegion:
id = None
translation_position = None
rotation_position = None
def sphere_bounced():
Sphere.current_position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", Sphere.id)
bounced_up = Sphere.current_position.z > Sphere.z_at_collision + SPHERE_RADIUS
bounced_left = Sphere.current_position.x < Sphere.initial_position.x
Sphere.bounced = bounced_left and bounced_up
return Sphere.bounced
helper.init_idle()
# 1) Open level
helper.open_level("Physics", "ForceRegion_MovingForceRegionChangesNetForce")
# 2) Enter game mode
helper.enter_game_mode(Tests.enter_game_mode)
# 3) Retrieve and validate entities
Sphere.id = general.find_game_entity("SphereRigidBody")
Report.critical_result(Tests.find_sphere, Sphere.id.IsValid())
ForceRegion.id = general.find_game_entity("ForceRegionBox")
Report.critical_result(Tests.find_force_region, ForceRegion.id.IsValid())
# 4) Get the initial position of the Sphere (rigid body)
Sphere.initial_position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", Sphere.id)
# 5) Move the object to right (X - direction) and rotate in Y - direction
ForceRegion.translation_position = azlmbr.components.TransformBus(
azlmbr.bus.Event, "GetWorldTranslation", ForceRegion.id
)
azlmbr.components.TransformBus(
azlmbr.bus.Event, "SetWorldX", ForceRegion.id, (ForceRegion.translation_position.x + TRANSLATION_OFFSET)
)
# Rotation in y direction anti clockwise
ForceRegion.rotation_position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldRotation", ForceRegion.id)
azlmbr.components.TransformBus(
azlmbr.bus.Event, "RotateAroundLocalY", ForceRegion.id, (ForceRegion.rotation_position.y + ROTATION_OFFSET)
)
Report.info("The force region has been repositioned")
# 6) Check if the ball is falling down
Sphere.current_position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", Sphere.id)
sphere_dropped = Sphere.current_position.z < (Sphere.initial_position.z + SPHERE_RADIUS)
Report.critical_result(Tests.sphere_dropped, sphere_dropped)
# 7) Add force region notification handler
def on_force_region_entered(args):
region_id = args[0]
object_id = args[1]
if region_id.Equal(ForceRegion.id) and object_id.Equal(Sphere.id):
if not Sphere.in_force_region:
Sphere.in_force_region = True
Report.info("Force Region entered")
force_notification_handler = azlmbr.physics.ForceRegionNotificationBusHandler()
force_notification_handler.connect(None)
force_notification_handler.add_callback("OnCalculateNetForce", on_force_region_entered)
# 8) Wait till the ball enters the force region
helper.wait_for_condition(lambda: Sphere.in_force_region, TIMEOUT)
# sphere z position when it entered force region
Sphere.z_at_collision = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", Sphere.id).z
# 9) Check if the ball has bounced and moved left
# wait frames till the ball bounces
helper.wait_for_condition(sphere_bounced, TIMEOUT)
Report.result(Tests.sphere_bounced, Sphere.bounced)
# 10) Exit game mode
helper.exit_game_mode(Tests.exit_game_mode)
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(ForceRegion_MovingForceRegionChangesNetForce)
@@ -0,0 +1,177 @@
"""
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 : C5968759
# Test Case Title : Check nested force regions exert forces simultaneously on rigid body
# fmt: off
class Tests:
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
find_vertical_sphere = ("Vertical sphere found", "Vertical sphere not found")
find_angled_sphere = ("Angled sphere found", "Angled sphere not found")
find_point_force_region = ("Point force region found", "Force Region not found")
find_angled_force_region = ("Angled force region found", "Angled force region not found")
vertical_entered_force_region = ("Vertical Sphere actions completed", "Vertical Sphere actions not completed")
timed_out = ("Test did not time out", "Test TIMED OUT")
angled_sphere_enter_force_region = ("Angled Sphere Entered Force Region", "Angled Sphere didn't enter Force Region")
vertical_sphere_fell_vertically = ("Vertical Sphere fell vertically", "Vertical Sphere didn't fall vertically")
angled_sphere_fell_at_angle = ("Angled Sphere fell at an angle", "Angled Sphere didn't fall at an angle")
vertical_sphere_slowed = ("Vertical Sphere slowed", "Vertical Sphere not slowed")
angled_sphere_slowed = ("Angled Sphere slowed", "Angled Sphere not slowed")
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
# fmt: on
import os, sys
def ForceRegion_MultipleComponentsCombineForces():
"""
Run() will open a a level and validate that the spheres are affected by the force regions as expected.
Expected Results: Both spheres fall into the force regions and are slowed. One of the spheres also falls at an angle
It does this by:
--> Opens level and enter game mode
--> Finds the entities in the scene
--> Listens for spheres to enter the force regions
--> Set Spheres start position and velocity
--> Listen for spheres to exit force regions
--> Set Spheres end position and velocity
--> Validate the results
--> Exits game mode and editor
Level Description: Two spheres floating above 2 force regions.
Sphere: 1 Name = "Sphere_vertical_drop" This sphere should fall vertically
Sphere: 2 Name = "Sphere_angled_drop" This sphere should fall at an angle
First force region: Name = "Force Region Point" Applies point force along the X axis to only the second sphere
Second force region: Name = "Force Region Simple Drag" Applies a drag force on both spheres
Setup path
"""
import azlmbr.legacy.general as general
import azlmbr.bus as bus
import azlmbr.physics
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
# Constants
TIME_OUT = 6.0 # Second to wait before timing out
POSITION_TOLERANCE = 0.1
def is_close_XY_position(vec1, vec2):
return abs(vec1.x - vec2.x) < POSITION_TOLERANCE and abs(vec1.y - vec2.y) < POSITION_TOLERANCE
# Holds details about the sphere
class Sphere:
def __init__(self, sphere_id, sphere_name):
self.name = sphere_name
self.id = sphere_id
self.start_position = None
self.end_position = None
self.start_velocity = None
self.end_velocity = None
self.entered_force_region = False
self.exited_force_region = False
def get_position(self):
return azlmbr.components.TransformBus(bus.Event, "GetWorldTranslation", self.id)
def get_velocity(self):
return azlmbr.physics.RigidBodyRequestBus(bus.Event, "GetLinearVelocity", self.id)
# 1) Opens level with spheres above a force region
helper.init_idle()
helper.open_level("Physics", "ForceRegion_MultipleComponentsCombineForces")
helper.enter_game_mode(Tests.enter_game_mode)
# 2) Finds the entities in the scene
sphere_vertical = Sphere(general.find_game_entity("Sphere_vertical_drop"), "Sphere Vertical")
Report.critical_result(Tests.find_vertical_sphere, sphere_vertical.id.IsValid())
sphere_angled = Sphere(general.find_game_entity("Sphere_angled_drop"), "Sphere Angled")
Report.critical_result(Tests.find_angled_sphere, sphere_angled.id.IsValid())
point_force_region_id = general.find_game_entity("Force Region Point")
Report.critical_result(Tests.find_point_force_region, point_force_region_id.IsValid())
simple_drag_force_region_id = general.find_game_entity("Force Region Simple Drag")
Report.critical_result(Tests.find_angled_force_region, simple_drag_force_region_id.IsValid())
# ******** Handler Functions ********
# Called if Sphere enters force region
def on_trigger_begin(args):
other_id = args[0]
# 4) Gets start position and velocity of spheres
if other_id.Equal(sphere_vertical.id) and sphere_vertical.entered_force_region is False:
Report.info("Trigger Entered")
sphere_vertical.entered_force_region = True
sphere_vertical.start_position = sphere_vertical.get_position()
sphere_vertical.start_velocity = sphere_vertical.get_velocity()
Report.result(Tests.vertical_entered_force_region, sphere_vertical.entered_force_region)
elif other_id.Equal(sphere_angled.id) and sphere_angled.entered_force_region is False:
sphere_angled.entered_force_region = True
sphere_angled.start_position = sphere_angled.get_position()
sphere_angled.start_velocity = sphere_angled.get_velocity()
Report.result(Tests.angled_sphere_enter_force_region, sphere_angled.entered_force_region)
def on_trigger_exit(args):
# 4) Gets end position and velocity of spheres
other_id = args[0]
if other_id.Equal(sphere_vertical.id):
Report.info("Trigger exited")
sphere_vertical.end_position = sphere_vertical.get_position()
sphere_vertical.end_velocity = sphere_vertical.get_velocity()
sphere_vertical.exited_force_region = True
elif other_id.Equal(sphere_angled.id):
Report.info("Trigger exited")
sphere_angled.end_position = sphere_angled.get_position()
sphere_angled.end_velocity = sphere_angled.get_velocity()
sphere_angled.exited_force_region = True
# 3) Listens for spheres to enter the force regions
# Create a handler for each force region
point_force_region_handler = azlmbr.physics.TriggerNotificationBusHandler()
point_force_region_handler.connect(point_force_region_id)
point_force_region_handler.add_callback("OnTriggerEnter", on_trigger_begin)
point_force_region_handler.add_callback("OnTriggerExit", on_trigger_exit)
simple_drag_force_region_handler = azlmbr.physics.TriggerNotificationBusHandler()
simple_drag_force_region_handler.connect(simple_drag_force_region_id)
simple_drag_force_region_handler.add_callback("OnTriggerEnter", on_trigger_begin)
simple_drag_force_region_handler.add_callback("OnTriggerExit", on_trigger_exit)
Report.result(Tests.timed_out, helper.wait_for_condition(lambda: sphere_angled.exited_force_region and
sphere_vertical.exited_force_region, TIME_OUT))
sphere_vertical_slowed_by_force_region = sphere_vertical.end_velocity.z > sphere_vertical.start_velocity.z
sphere_angled_slowed_by_force_region = sphere_angled.end_velocity.z > sphere_angled.start_velocity.z
sphere_angled_fell_at_expected_angle = sphere_angled.end_position.x > sphere_angled.start_position.x + POSITION_TOLERANCE
sphere_vertical_fell_at_expected_angle = is_close_XY_position(sphere_vertical.end_position, sphere_vertical.start_position)
Report.result(Tests.angled_sphere_fell_at_angle, sphere_angled_fell_at_expected_angle)
Report.result(Tests.vertical_sphere_fell_vertically, sphere_vertical_fell_at_expected_angle)
Report.result(Tests.angled_sphere_slowed, sphere_angled_slowed_by_force_region)
Report.result(Tests.vertical_sphere_slowed, sphere_vertical_slowed_by_force_region)
# 8) Exit Game mode
helper.exit_game_mode(Tests.exit_game_mode)
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(ForceRegion_MultipleComponentsCombineForces)
@@ -0,0 +1,233 @@
"""
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 : C5959810
# Test Case Title : Check that multiple forces in single force region create correct net force
# fmt: off
class Tests:
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
sphere_exists = ("Sphere has been found", "Sphere has not been found")
force_region_exists = ("Force Region has been found", "Force Region has not been found")
sphere_position_found = ("Sphere position found", "Sphere position not found")
force_region_position_found = ("Force Region position found", "Force Region position not found")
orientation_before_collision = ("Sphere is above Force Region", "Sphere is not above Force Region")
sphere_velocity_found = ("Sphere velocity found", "Sphere velocity not found")
sphere_velocity_before_collision = ("Sphere has valid initial velocity", "Sphere initial velocity not valid")
collision = ("Collision has occurred", "No collision has occurred")
force_applied = ("Forces were combined correctly", "Forces were not applied correctly")
new_velocity_applied = ("Sphere velocity has been updated", "Sphere velocity was never updated")
orientation_after_collision = ("Sphere is above and to the left", "Entity orientation is not valid")
sphere_velocity_post_collision = ("Sphere velocity valid post-collision", "Sphere velocity no longer valid")
force_region_has_not_moved = ("Force Region has not moved", "Force Region has somehow moved")
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
# fmt: on
def ForceRegion_MultipleForcesInSameComponentCombineForces():
# type: () -> None
"""
Summary: Runs an automated test to ensure that separate forces in a force region add correctly
Level Description:
Sphere - Placed directly above the force region with an initial velocity in the -z direction;
has PhysX Rigid Body, sphere shaped PhysX Collider, Sphere Shape
Force Region - Placed directly under the sphere, has a point, and world space force in the
z and negative x direction respectively; has PhysX Force Region, box shaped PhysX Collider
Expected Behavior: Sphere collides with force region and is sent in the negative x and positive z direction
Test Steps:
1) Load Level
2) Enter Game Mode
3) Find Entities
4) Validate initial positions and velocity
5) Set up handler
6) Wait for Sphere collision with Force Region
7) Validate and Log Results
8) Exit Game Mode
9) 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.
: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
# Constants
TIMEOUT = 1
FLOAT_THRESHOLD = sys.float_info.epsilon
# Helper Functions
class Collision:
happened = False
force_vector = None
force_magnitude = None
velocity_now_updated = False
class Entity:
def __init__(self, name):
self.id = general.find_game_entity(name)
self.name = name
self.initial_velocity = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetLinearVelocity", self.id)
self.final_velocity = None
self.initial_position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", self.id)
self.final_position = None
def get_final_position_and_velocity(self):
self.final_velocity = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetLinearVelocity", self.id)
self.final_position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", self.id)
def report_sphere_values(self):
Report.info_vector3(self.initial_position, "{} initial position: ".format(self.name))
Report.info_vector3(self.initial_velocity, "{} initial velocity: ".format(self.name))
Report.info_vector3(self.final_position, "{} final position: ".format(self.name))
Report.info_vector3(self.final_velocity, "{} final velocity: ".format(self.name))
def report_force_region_values(self):
Report.info_vector3(self.initial_position, "{} initial position: ".format(self.name))
Report.info_vector3(self.final_position, "{} final position: ".format(self.name))
def validate_positions(collision_happened, sphere_position, force_region_position):
if collision_happened:
result = sphere_position.x < force_region_position.x
else:
result = abs(sphere_position.x - force_region_position.x) < FLOAT_THRESHOLD
return (
result
and sphere_position.z > force_region_position.z
and abs(sphere_position.y - force_region_position.y) < FLOAT_THRESHOLD
)
def validate_sphere_velocity(collision_happened, sphere_velocity_vector):
if collision_happened:
x_result = sphere_velocity_vector.x < 0
z_result = sphere_velocity_vector.z > 0
else:
x_result = abs(sphere_velocity_vector.x) < FLOAT_THRESHOLD
z_result = sphere_velocity_vector.z < 0
return x_result and z_result and abs(sphere_velocity_vector.y) < FLOAT_THRESHOLD
def vector_valid(vector, can_be_zero):
if can_be_zero:
return vector != None
else:
return vector != None and not vector.IsZero()
def on_collision_begin(args):
assert force_region.id.Equal(args[0])
if sphere.id.equal(args[1]):
Collision.happened = True
Report.info("Collision has begun")
if vector_valid(args[2], False):
Collision.force_vector = args[2]
Collision.force_magnitude = args[3]
def force_valid(vector, magnitude):
return magnitude > 0 and vector.x < 0 and vector.z > 0 and abs(vector.y) < FLOAT_THRESHOLD
def velocity_update_check():
current_velocity_vector = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetLinearVelocity", sphere.id)
velocity_updated = (
current_velocity_vector.x < sphere.initial_velocity.x
and current_velocity_vector.z > sphere.initial_velocity.z
)
if velocity_updated:
Collision.velocity_now_updated = True
return velocity_updated
# Main Script
helper.init_idle()
# 1) Load Level
helper.open_level("physics", "ForceRegion_MultipleForcesInSameComponentCombineForces")
# 2) Enter Game Mode
helper.enter_game_mode(Tests.enter_game_mode)
# 3) Find Entities
sphere = Entity("Sphere")
force_region = Entity("Force_Region")
Report.critical_result(Tests.sphere_exists, sphere.id.isValid())
Report.critical_result(Tests.force_region_exists, force_region.id.isValid())
# 4) Validate initial positions and velocity
# Position validation
Report.critical_result(Tests.sphere_position_found, vector_valid(sphere.initial_position, False))
Report.critical_result(Tests.force_region_position_found, vector_valid(force_region.initial_position, False))
# Velocity validation
Report.critical_result(Tests.sphere_velocity_found, vector_valid(sphere.initial_velocity, False))
# Value validation
Report.critical_result(
Tests.orientation_before_collision,
validate_positions(Collision.happened, sphere.initial_position, force_region.initial_position),
)
Report.critical_result(
Tests.sphere_velocity_before_collision, validate_sphere_velocity(Collision.happened, sphere.initial_velocity)
)
# 5) Set up handler
handler = azlmbr.physics.ForceRegionNotificationBusHandler()
handler.connect(None)
handler.add_callback("OnCalculateNetForce", on_collision_begin)
# 6) Wait for Sphere collision with Force Region and Velocity application
helper.wait_for_condition(lambda: Collision.happened, TIMEOUT)
helper.wait_for_condition(velocity_update_check, TIMEOUT)
# 7) Validate and Log Results
sphere.get_final_position_and_velocity()
force_region.get_final_position_and_velocity()
# Value validation
Report.result(Tests.new_velocity_applied, Collision.velocity_now_updated)
Report.result(Tests.collision, Collision.happened)
Report.result(Tests.force_applied, force_valid(Collision.force_vector, Collision.force_magnitude))
Report.result(
Tests.orientation_after_collision,
validate_positions(Collision.happened, sphere.final_position, force_region.final_position),
)
Report.result(
Tests.sphere_velocity_post_collision, validate_sphere_velocity(Collision.happened, sphere.final_velocity)
)
Report.result(
Tests.force_region_has_not_moved,
force_region.final_position.Subtract(force_region.initial_position).IsZero(FLOAT_THRESHOLD),
)
# Value logging
sphere.report_sphere_values()
force_region.report_force_region_values()
Report.info_vector3(Collision.force_vector, "Applied Force: ", Collision.force_magnitude)
# 8) Exit Game Mode
helper.exit_game_mode(Tests.exit_game_mode)
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(ForceRegion_MultipleForcesInSameComponentCombineForces)
@@ -0,0 +1,168 @@
"""
Copyright (c) Contributors to the Open 3D Engine Project.
For complete copyright and license terms please see the LICENSE at the root of this distribution.
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
# Test case ID : C15845879
# Test Case Title : Check that linear damping with high values do not make the object to quiver
# fmt: off
class Tests:
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
sphere_found = ("Found sphere", "Did not find sphere")
force_region_found = ("Found force region", "Did not find force region")
check_relative_position = ("Sphere is above force region", "Sphere isn't above force region")
sphere_moving_down = ("Sphere heading to force region", "Sphere has invalid initial velocity")
sphere_entered_force_region = ("Sphere has entered force region", "Sphere never entered force region")
sphere_stopped_moving = ("Sphere final velocity is zero", "Sphere final velocity invalid")
sphere_still_above_force_region = ("Sphere still above force region", "Sphere not above force region")
no_quiver = ("Sphere is not quivering", "Sphere quivering in force region")
# fmt: on
def ForceRegion_NoQuiverOnHighLinearDampingForce():
"""
Summary: Check that linear damping with high values do not make the object to quiver
Level Description:
sphere - Starts above the force_region entity with initial velocity in the negative z direction and
gravity disabbled; has physx collider in sphere shape, physx rigid body, and sphere shape
force_region - Sits below sphere entity, has linear damping force set at 100 and region has scaling
(5,5,5); has physx collider in box shape and physx force region
Expected Behavior: Sphere falls into force region and is stuck by the damping force. It specifically should
not quiver up and down.
Test Steps:
1) Open Level
2) Enter Game Mode
3) Create and Validate Entities
4) Setup handler and wait for sphere to enter force region
5) Validate the Sphere remains in Force Region
6) Check to see if the sphere is quivering
7) Exit Game Mode
8) 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.
: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
# Constants
FLOAT_THRESHOLD = sys.float_info.epsilon
TIMEOUT = 1
VELOCITY_THRESHOLD = 0.01
QUIVER_THRESHOLD = 0.01
SLOWDOWN_FRAMES = 30
SPHERE_STOP_OFFSET = 3.5
# Helper Functions
class Entity:
def __init__(self, name):
self.id = general.find_game_entity(name)
self.name = name
self.force_region_id = None
self.entered_force_region = False
self.quiver_reference = None
Report.critical_result(Tests.__dict__[self.name + "_found"], self.id.isValid())
@property
def position(self):
# type () -> Vector3
return azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", self.id)
@property
def velocity(self):
# type () -> Vector3
return azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetLinearVelocity", self.id)
@property
def is_moving_up(self):
# type () -> bool
return (
abs(self.velocity.x) < FLOAT_THRESHOLD
and abs(self.velocity.y) < FLOAT_THRESHOLD
and self.velocity.z > 0.0
)
def set_handler(self):
self.handler = azlmbr.physics.ForceRegionNotificationBusHandler()
self.handler.connect(None)
self.handler.add_callback("OnCalculateNetForce", self.on_calculate_net_force)
def on_calculate_net_force(self, args):
# type (list) -> None
# Flips the collision happened boolean for the sphere object and prints the force values.
if self.force_region_id.Equal(args[0]) and self.id.Equal(args[1]) and not self.entered_force_region:
self.entered_force_region = True
def sphere_not_quivering():
# type () -> bool
# Returns False if sphere "quivers" from its initial position, True if it stays close to it's original position
return abs(sphere.position.z - sphere.quiver_reference) > QUIVER_THRESHOLD
def sphere_above_force_region(sphere_position, force_region_position):
# type () -> bool
return (
abs(sphere_position.x - force_region_position.x) < FLOAT_THRESHOLD
and abs(sphere_position.y - force_region_position.y) < FLOAT_THRESHOLD
and sphere_position.z > force_region_position.z
)
# Main Script
helper.init_idle()
# 1) Open Level
helper.open_level("Physics", "ForceRegion_NoQuiverOnHighLinearDampingForce")
# 2) Enter Game Mode
helper.enter_game_mode(Tests.enter_game_mode)
# 3) Create and Validate Entities
sphere = Entity("sphere")
force_region = Entity("force_region")
sphere.force_region_id = force_region.id
Report.critical_result(Tests.sphere_moving_down, not sphere.is_moving_up)
Report.critical_result(
Tests.check_relative_position, sphere_above_force_region(sphere.position, force_region.position)
)
# 4) Setup handler and wait for sphere to enter force region
sphere.set_handler()
Report.critical_result(
Tests.sphere_entered_force_region, helper.wait_for_condition(lambda: sphere.entered_force_region, TIMEOUT)
)
# 5) Validate the Sphere remains in Force Region
# Must wait for the sphere to slow down
Report.result(Tests.sphere_stopped_moving, helper.wait_for_condition(lambda: sphere.velocity.IsZero(VELOCITY_THRESHOLD), TIMEOUT))
# Force region has scaling (5,5,5). Thus the upper edge of the force region is 2.5m above the transform. With proper offset we can
# see that sphere is stuck on top of the force region and did not bounce off.
Report.result(Tests.sphere_still_above_force_region, (sphere.position.z - force_region.position.z) < SPHERE_STOP_OFFSET)
# 6) Check to see if the sphere is quivering
sphere.quiver_reference = sphere.position.z
Report.result(Tests.no_quiver, not helper.wait_for_condition(sphere_not_quivering, TIMEOUT))
# 7) Exit Game Mode
helper.exit_game_mode(Tests.exit_game_mode)
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(ForceRegion_NoQuiverOnHighLinearDampingForce)
@@ -0,0 +1,198 @@
"""
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 : C6090547
# Test Case Title : Check that force regions in parent and child entities work together.
# fmt: off
class Tests():
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
find_sphere = ("Sphere found", "Sphere not found")
find_parent_force_region = ("Parent Force Region found", "Parent Force Region not found")
find_child_force_region = ("Child Force Region found", "Child Force Region not found")
find_trigger_box = ("Trigger Box found", "Trigger Box not found")
sphere_gravity_disabled = ("Sphere gravity disabled", "Sphere gravity not disabled")
parent_force_region_direction = ("Parent Force Region is in positive x direction", "Parent Force Region is not in positive x direction")
child_force_region_direction = ("Child Force Region is in positive y direction", "Child Force Region is not in positive y direction")
parent_force_on_sphere = ("Parent Force Region applied total force on sphere", "Parent Force Region did not apply total force on sphere")
child_force_on_sphere = ("Child Force Region applied total force on sphere", "Child Force Region did not apply total force on sphere")
sphere_enters_trigger = ("Sphere entered Trigger", "Sphere did not enter Trigger before Timeout")
sphere_exits_trigger = ("Sphere exited Trigger", "Sphere did not exit Trigger before Timeout")
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
# fmt: on
def ForceRegion_ParentChildForcesCombineForces():
"""
Summary:
Runs an automated test to ensure that force regions in parent and child entities work together.
Level Description:
Parent Force Region (entity) - contains PhysX Force Region with world space force which has positive X force
Magnitude with Direction (1, 0, 0) and PhysX Collider (box shape).
Child Force Region (entity) - contains PhysX Force Region with world space force which has positive Y force
Magnitude with Direction (0, 1, 0) and PhysX Collider (box shape).
Sphere (entity) - contains a Sphere mesh, PhysX Collider (sphere shape) and PhysX Rigid Body.
Sphere located at the low (x, y) corner of where the force regions overlap.
Trigger Box (entity) - contains PhysX Collider (box shape)
trigger box placed in the (1, 1, 0) direction from the sphere at the opposite end of
the force region overlap.
Parent and Child force regions are placed above the terrain as two overlapping sheets.
Expected Behavior:
When game mode is entered, the Sphere should accelerate evenly in the positive (x, y) direction and it should move
as much in x as it does in y. Sphere should pass through Trigger Box.
Test Steps:
1) Open level
2) Enter game mode
3) Retrieve and validate entities
4) Make sure gravity is off from the start
5) Make sure the parent entity is set as the parent of the child entity in level
6) Make sure parent and child force regions are in correct directions
7) Check parent and child force regions each exert its force on sphere
8) Verify sphere passes through trigger box
9) Exit game mode
10) Close the 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.
:return: None
"""
import os
import sys
import azlmbr.legacy.general as general
import azlmbr.bus
import azlmbr.math as lymath
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
# Constants
TIMEOUT = 3.0
X_DIRECTION = lymath.Vector3(1.0, 0.0, 0.0)
Y_DIRECTION = lymath.Vector3(0.0, 1.0, 0.0)
EXPECTED_MAGNITUDE = 100.0
TOLERANCE = 0.1
helper.init_idle()
# 1) Open level
helper.open_level("Physics", "ForceRegion_ParentChildForcesCombineForces")
# 2) Enter game mode
helper.enter_game_mode(Tests.enter_game_mode)
# 3) Retrieve and validate entities
sphere_id = general.find_game_entity("Sphere")
Report.critical_result(Tests.find_sphere, sphere_id.IsValid())
parent_id = general.find_game_entity("Parent Force Region")
Report.critical_result(Tests.find_parent_force_region, parent_id.IsValid())
child_id = general.find_game_entity("Child Force Region")
Report.critical_result(Tests.find_child_force_region, child_id.IsValid())
trigger_box_id = general.find_game_entity("Trigger Box")
Report.critical_result(Tests.find_trigger_box, trigger_box_id.IsValid())
# 4) Make sure gravity is off from the start
is_gravity_enabled = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "IsGravityEnabled", sphere_id)
Report.critical_result(Tests.sphere_gravity_disabled, not is_gravity_enabled)
# 5) Make sure the parent entity is set as the parent of the child entity in level
id = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetParentId", child_id)
if id.Equal(parent_id):
Report.info("parent and child force regions are in correct position")
# 6) Make sure parent and child force regions are in correct directions
dir_parent = azlmbr.physics.ForceWorldSpaceRequestBus(azlmbr.bus.Event, "GetDirection", parent_id)
dir_child = azlmbr.physics.ForceWorldSpaceRequestBus(azlmbr.bus.Event, "GetDirection", child_id)
Report.info_vector3(dir_parent, "Parent force region direction : ")
Report.info_vector3(dir_child, "Child force region direction : ")
Report.critical_result(Tests.parent_force_region_direction, dir_parent.IsClose(X_DIRECTION, TOLERANCE))
Report.critical_result(Tests.child_force_region_direction, dir_child.IsClose(Y_DIRECTION, TOLERANCE))
# 7) Check parent and child force regions each exert its force on sphere
class NetForceMagnitude:
parent_force_region_magnitude = 0
child_force_region_magnitude = 0
def on_calc_net_force(args):
"""
args[0] - force region entity
args[1] - entity entering
args[2] - vector
args[3] - magnitude
"""
force_region_id = args[0]
entering_entity = args[1]
if entering_entity.Equal(sphere_id):
if force_region_id.Equal(parent_id):
NetForceMagnitude.parent_force_region_magnitude = args[3]
elif force_region_id.Equal(child_id):
NetForceMagnitude.child_force_region_magnitude = args[3]
force_notification_handler = azlmbr.physics.ForceRegionNotificationBusHandler()
force_notification_handler.connect(None)
force_notification_handler.add_callback("OnCalculateNetForce", on_calc_net_force)
helper.wait_for_condition(lambda : NetForceMagnitude.parent_force_region_magnitude != 0 and NetForceMagnitude.child_force_region_magnitude != 0, 1.0)
Report.info("Parent Force Region Magnitude on Sphere : {}".format(NetForceMagnitude.parent_force_region_magnitude))
Report.info("Child Force Region Magnitude on Sphere : {}".format(NetForceMagnitude.child_force_region_magnitude))
Report.critical_result(
Tests.parent_force_on_sphere,
abs(EXPECTED_MAGNITUDE - NetForceMagnitude.parent_force_region_magnitude) < TOLERANCE,
)
Report.critical_result(
Tests.child_force_on_sphere,
abs(EXPECTED_MAGNITUDE - NetForceMagnitude.child_force_region_magnitude) < TOLERANCE,
)
# 8) Verify sphere passes through trigger box
class Trigger:
on_entered = False
on_exited = False
def on_trigger_enter(args):
other_id = args[0]
if other_id.Equal(sphere_id):
Trigger.on_entered = True
def on_trigger_exit(args):
other_id = args[0]
if other_id.Equal(sphere_id):
Trigger.on_exited = True
handler = azlmbr.physics.TriggerNotificationBusHandler()
handler.connect(trigger_box_id)
handler.add_callback("OnTriggerEnter", on_trigger_enter)
handler.add_callback("OnTriggerExit", on_trigger_exit)
# Check sphere enters trigger box
helper.wait_for_condition(lambda: Trigger.on_entered, TIMEOUT)
Report.critical_result(Tests.sphere_enters_trigger, Trigger.on_entered)
# Check sphere exits trigger box
helper.wait_for_condition(lambda: Trigger.on_exited, TIMEOUT)
Report.result(Tests.sphere_exits_trigger, Trigger.on_exited)
# 9) Exit game mode
helper.exit_game_mode(Tests.exit_game_mode)
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(ForceRegion_ParentChildForcesCombineForces)
@@ -0,0 +1,188 @@
"""
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 : C5932044
# Test Case Title : Check that force region exerts point force on rigid bodies
# fmt: off
class Tests():
enter_game_mode = ("Entered game mode", "Failed to enter game mode" )
find_ball = ("Ball entity found", "Ball entity not found" )
find_box = ("Box entity found", "Box entity not found" )
gravity_works = ("Ball fell", "Ball did not fall" )
ball_entered_force_region = ("Ball entered force region", "Ball did not enter force region before timeout" )
ball_exited_force_region = ("Ball exited force region", "Ball did not exit force region before timeout" )
net_force_magnitude = ("The net force magnitude on ball is close to expected value", "The net force magnitude on ball is not close to expected value")
ball_moved_up = ("Ball moved up", "Ball did not move up" )
ball_moved_right = ("Ball moved right", "Ball did not move right" )
ball_not_moved_y = ("Ball did not move in the y direction", "Ball moved in the y direction" )
exit_game_mode = ("Exited game mode", "Couldn't exit game mode" )
# fmt: on
def ForceRegion_PointForceOnRigidBodies():
"""
Summary:
Runs an automated test to ensure that that a force region exerts point force on rigid bodies
Level Description:
RigidBody (entity) - a sphere suspended above and to the right the a force region with gravity enabled;
contains a sphere mesh, PhysX Collider (sphere shape), and PhysX RigidBody
ForceRegion (entity) - contains box mesh, PhysX Collider (Box shape), and PhysX RigidBody
Expected Behavior:
When game mode is entered, the ball entity will experience gravity and fall toward the upper right edge (+z, +x) of
the force region. The force region applies a point force to the ball, sending it upwards (+z) and to the right (+x)
Test Steps:
1) Open level
2) Enter game mode
3) Retrieve entities
4) Get the starting x & z position of the ball
5) Check that the ball falls (gravity check)
6) Check that the ball enters the trigger area
7) Get the magnitude of the collision
8) Check that the ball exits the trigger area
9) Verify that the magnitude of the collision is as expected
10) Check that the ball is moving up and to the right
11) Exit game mode
12) Close the 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.
: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
class Ball:
start_position_x = None
start_position_z = None
fell = False
# Constants
TIMEOUT = 2.0
MAGNITUDE_TOLERANCE = 0.2 # Magnitudes must be within this amount in order to be valid
NO_MOTION_Y_TOLERANCE = sys.float_info.epsilon # Motion in the y axis must be below this in order to be valid
helper.init_idle()
# 1) Open level
helper.open_level("Physics", "ForceRegion_PointForceOnRigidBodies")
# 2) Enter game mode
helper.enter_game_mode(Tests.enter_game_mode)
# 3) Retrieve entities
ball_id = general.find_game_entity("RigidBody")
Report.critical_result(Tests.find_ball, ball_id.IsValid())
box_id = general.find_game_entity("ForceRegion")
Report.critical_result(Tests.find_box, box_id.IsValid())
# 4) Get the starting x & z position of the ball
Ball.start_position_x = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldX", ball_id)
Ball.start_position_z = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldZ", ball_id)
Report.info("Ball start X: {}".format(Ball.start_position_x))
Report.info("Ball start Z: {}".format(Ball.start_position_z))
# 5) Check that the ball falls (gravity check)
def ball_falls():
if not Ball.fell:
ball_position_z = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldZ", ball_id)
if (ball_position_z - Ball.start_position_z) < 0.0:
Report.info("Ball position is now lower than the starting position")
Ball.fell = True
return Ball.fell
helper.wait_for_condition(ball_falls, TIMEOUT)
Report.result(Tests.gravity_works, Ball.fell)
# 6) Check that the ball enters the trigger area
class ForceRegionTrigger:
entered = False
exited = False
def on_enter(args):
other_id = args[0]
if other_id.Equal(ball_id):
Report.info("Trigger entered")
ForceRegionTrigger.entered = True
def on_exit(args):
other_id = args[0]
if other_id.Equal(ball_id):
Report.info("Trigger exited")
ForceRegionTrigger.exited = True
handler = azlmbr.physics.TriggerNotificationBusHandler()
handler.connect(box_id)
handler.add_callback("OnTriggerEnter", on_enter)
handler.add_callback("OnTriggerExit", on_exit)
helper.wait_for_condition(lambda: ForceRegionTrigger.entered, TIMEOUT)
Report.result(Tests.ball_entered_force_region, ForceRegionTrigger.entered)
# 7) Get the magnitude of the collision
class NetForceMagnitude:
value = 0
def on_calc_net_force(args):
"""
args[0] - force region entity
args[1] - entity entering
args[2] - vector
args[3] - magnitude
"""
force_magnitude = args[3]
NetForceMagnitude.value = force_magnitude
force_notification_handler = azlmbr.physics.ForceRegionNotificationBusHandler()
force_notification_handler.connect(None)
force_notification_handler.add_callback("OnCalculateNetForce", on_calc_net_force)
# 8) Check that the ball exits the trigger area
helper.wait_for_condition(lambda: ForceRegionTrigger.exited, TIMEOUT)
Report.result(Tests.ball_exited_force_region, ForceRegionTrigger.exited)
# 9) Verify that the magnitude of the collision is as expected
def is_close_float(a, b, tolerance):
return abs(b - a) < tolerance
force_region_magnitude = azlmbr.physics.ForcePointRequestBus(azlmbr.bus.Event, "GetMagnitude", box_id)
Report.info(
"NetForce magnitude is {}, Force Region magnitude is {}".format(NetForceMagnitude.value, force_region_magnitude)
)
Report.result(
Tests.net_force_magnitude, is_close_float(NetForceMagnitude.value, force_region_magnitude, MAGNITUDE_TOLERANCE)
)
# 10) Check that the ball is moving up and to the right
linear_velocity = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetLinearVelocity", ball_id)
Report.info_vector3(linear_velocity, "Ball linear velocity")
Report.result(Tests.ball_moved_up, linear_velocity.z > 0)
Report.result(Tests.ball_moved_right, linear_velocity.x > 0)
Report.result(Tests.ball_not_moved_y, abs(linear_velocity.y) < NO_MOTION_Y_TOLERANCE)
# 11) Exit game mode
helper.exit_game_mode(Tests.exit_game_mode)
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(ForceRegion_PointForceOnRigidBodies)
@@ -0,0 +1,404 @@
"""
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 : C5959808
# Test Case Title : Verify Force Region Position Offset
# fmt:off
class Tests:
# General tests
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
test_completed = ("The test successfully completed", "The test timed out")
# ***** Entities found *****
# Force Regions
force_region_x_found = ("Force Region for X axis test was found", "Force Region for X axis test was NOT found")
force_region_y_found = ("Force Region for Y axis test was found", "Force Region for Y axis test was NOT found")
force_region_z_found = ("Force Region for Z axis test was found", "Force Region for Z axis test was NOT found")
# Force Region Pass Boxes
force_region_pass_box_x_found = ("Force Region Pass Box for X axis test was found", "Force Region Pass Box for X axis test was NOT found")
force_region_pass_box_y_found = ("Force Region Pass Box for Y axis test was found", "Force Region Pass Box for Y axis test was NOT found")
force_region_pass_box_z_found = ("Force Region Pass Box for Z axis test was found", "Force Region Pass Box for Z axis test was NOT found")
# External Pass Boxes
external_pass_box_x_found = ("External Pass Box for X axis test was found", "External Pass Box for X axis test was NOT found")
external_pass_box_y_found = ("External Pass Box for Y axis test was found", "External Pass Box for Y axis test was NOT found")
external_pass_box_z_found = ("External Pass Box for Z axis test was found", "External Pass Box for Z axis test was NOT found")
# Force Region Fail Boxes
force_region_fail_box_x_found = ("Force Region Fail Box for X axis test was found", "Force Region Fail Box for X axis test was NOT found")
force_region_fail_box_y_found = ("Force Region Fail Box for Y axis test was found", "Force Region Fail Box for Y axis test was NOT found")
force_region_fail_box_z_found = ("Force Region Fail Box for Z axis test was found", "Force Region Fail Box for Z axis test was NOT found")
# External Fail Boxes
external_fail_box_x_found = ("External Fail Box for X axis test was found", "External Fail Box for X axis test was NOT found")
external_fail_box_y_found = ("External Fail Box for Y axis test was found", "External Fail Box for Y axis test was NOT found")
external_fail_box_z_found = ("External Fail Box for Z axis test was found", "External Fail Box for Z axis test was NOT found")
# Pass spheres
sphere_pass_x_found = ("Pass Sphere for X axis test was found", "Pass Sphere for X axis test was NOT found")
sphere_pass_y_found = ("Pass Sphere for Y axis test was found", "Pass Sphere for Y axis test was NOT found")
sphere_pass_z_found = ("Pass Sphere for Z axis test was found", "Pass Sphere for Z axis test was NOT found")
# Bounce Spheres
sphere_bounce_x_found = ("Bounce Sphere for X axis test was found", "Bounce Sphere for X axis test was NOT found")
sphere_bounce_y_found = ("Bounce Sphere for Y axis test was found", "Bounce Sphere for Y axis test was NOT found")
sphere_bounce_z_found = ("Bounce Sphere for Z axis test was found", "Bounce Sphere for Z axis test was NOT found")
# ****** Entities' results ******
# Force Regions
force_region_x_mag_result = ("Force Region for X axis magnitude exerted was as expected", "Force Region for X axis magnitude exerted was NOT as expected")
force_region_y_mag_result = ("Force Region for Y axis magnitude exerted was as expected", "Force Region for Y axis magnitude exerted was NOT as expected")
force_region_z_mag_result = ("Force Region for Z axis magnitude exerted was as expected", "Force Region for Z axis magnitude exerted was NOT as expected")
force_region_x_norm_result = ("Force Region for X axis normal exerted was as expected", "Force Region for X axis normal exerted was NOT as expected")
force_region_y_norm_result = ("Force Region for Y axis normal exerted was as expected", "Force Region for Y axis normal exerted was NOT as expected")
force_region_z_norm_result = ("Force Region for Z axis normal exerted was as expected", "Force Region for Z axis normal exerted was NOT as expected")
# Force Region Pass Boxes
force_region_pass_box_x_result = ("Force Region Pass Box for X axis collided with exactly one sphere", "Force Region Pass Box for X axis DID NOT collide with exactly one sphere")
force_region_pass_box_y_result = ("Force Region Pass Box for Y axis collided with exactly one sphere", "Force Region Pass Box for Y axis DID NOT collide with exactly one sphere")
force_region_pass_box_z_result = ("Force Region Pass Box for Z axis collided with exactly one sphere", "Force Region Pass Box for Z axis DID NOT collide with exactly one sphere")
# External Pass Boxes
external_pass_box_x_result = ("External Pass Box for X axis collided with exactly one sphere", "External Pass Box for X axis DID NOT collide with exactly one sphere")
external_pass_box_y_result = ("External Pass Box for Y axis collided with exactly one sphere", "External Pass Box for Y axis DID NOT collide with exactly one sphere")
external_pass_box_z_result = ("External Pass Box for Z axis collided with exactly one sphere", "External Pass Box for Z axis DID NOT collide with exactly one sphere")
# Force Region Fail Boxes
force_region_fail_box_x_result = ("Force Region Fail Box for X axis collided with no spheres", "Force Region Fail Box for X axis DID collide with a sphere")
force_region_fail_box_y_result = ("Force Region Fail Box for Y axis collided with no spheres", "Force Region Fail Box for Y axis DID collide with a sphere")
force_region_fail_box_z_result = ("Force Region Fail Box for Z axis collided with no spheres", "Force Region Fail Box for Z axis DID collide with a sphere")
# External Fail Boxes
external_fail_box_x_result = ("External Fail Box for X axis collided with no spheres", "External Fail Box for X axis DID collide with a sphere")
external_fail_box_y_result = ("External Fail Box for Y axis collided with no spheres", "External Fail Box for Y axis DID collide with a sphere")
external_fail_box_z_result = ("External Fail Box for Z axis collided with no spheres", "External Fail Box for Z axis DID collide with a sphere")
# Pass spheres
sphere_pass_x_result = ("Pass Sphere for X axis collided with expected Box", "Pass Sphere for X axis DID NOT collide with expected Box")
sphere_pass_y_result = ("Pass Sphere for Y axis collided with expected Box", "Pass Sphere for Y axis DID NOT collide with expected Box")
sphere_pass_z_result = ("Pass Sphere for Z axis collided with expected Box", "Pass Sphere for Z axis DID NOT collide with expected Box")
# Bounce Spheres
sphere_bounce_x_result = ("Bounce Sphere for X axis collided with expected Box", "Bounce Sphere for X axis DID NOT collide with expected Box")
sphere_bounce_y_result = ("Bounce Sphere for Y axis collided with expected Box", "Bounce Sphere for Y axis DID NOT collide with expected Box")
sphere_bounce_z_result = ("Bounce Sphere for Z axis collided with expected Box", "Bounce Sphere for Z axis DID NOT collide with expected Box")
# fmt:on
@staticmethod
# Test tuple accessor via string
def get_test(test_name):
if test_name in Tests.__dict__:
return Tests.__dict__[test_name]
else:
return None
def ForceRegion_PositionOffset():
"""
Summary:
Force Region positional offset is tested for each of the 3 axises (X, Y, and Z). Each axis's test has one
ForceRegion, two spheres and four boxes. By monitoring which box each sphere collides with we can validate the
integrity of the ForceRegions positional offset.
Level Description:
Each axis's test has the following entities:
one force region - set for point force and with it's collider set offset (on the axis in test).
two spheres - one positioned near the transform of the force region, one positioned near the [offset] collider for
the force region
four boxes - One box is positioned inside the force region's transform, one inside the force region's [offset]
collider. The other two boxes are positioned behind the two spheres (relative to the direction they will be
initially traveling)
Expected Behavior:
All three axises' tests run in parallel. when the tests begin, the spheres should move toward their expected
force regions. The spheres positioned to collide with their region's [offset] collider should be forced backwards
before entering the force region and collide with the box behind it. The spheres positioned by their force region's
transforms should pass straight into the transform and collide with the box inside the transform.
The boxes inside the Force Regions' [offset] colliders and the boxes behind the spheres set to move into the Force
Regions' transforms should not register any collisions.
Steps:
1) Open level and enter game mode
2) Set up tests and variables
3) Wait for test results (or time out)
(Report results)
4) Exit game mode and close the editor
: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.math as azmath
import azlmbr.bus
import azlmbr
# Constants
CLOSE_ENOUGH = 0.01 # Close enough threshold for comparing floats
TIME_OUT = 2.0 # Time out (in seconds) until test is aborted
FORCE_MAGNITUDE = 1000.0 # Point force magnitude for Force Regions
SPEED = 3.0 # Initial speed (in m/s) of the moving spheres.
# Full list for all spheres. Used for EntityId look up in event handlers
all_spheres = []
# Entity base class handles very general entity initialization
# Should be treated as a "virtual" class and all implementing child
# classes should implement a "self.result()" function referenced in EntityBase::report(self)
class EntityBase:
def __init__(self, name):
# type: (str) -> None
self.name = name
self.print_list = []
self.id = general.find_game_entity(name)
found_test = Tests.get_test(name + "_found")
Report.critical_result(found_test, self.id.IsValid())
# Reports this entity's result. Implicitly calls "get" on result.
# Subclasses implement their own definition of a successful result
def report(self):
# type: () -> None
result_test = Tests.get_test(self.name + "_result")
Report.result(result_test, self.result())
# Prints the print queue (with decorated header) if not empty
def print_log(self):
# type: () -> None
if self.print_list:
Report.info("*********** {} **********".format(self))
for line in self.print_list:
Report.info(line)
Report.info("")
# Quick string cast, returns entity name
def __str__(self):
# type: () -> str
return self.name
# ForceRegion handles all the data and behavior associated with a ForceRegion (for this test)
# They simply wait for a Sphere to collide with them. On collision they store the calculated force
# magnitude for verification.
class ForceRegion(EntityBase):
def __init__(self, name, magnitude):
# type: (str, float) -> None
EntityBase.__init__(self, name)
self.expected_magnitude = magnitude
self.actual_magnitude = None
self.expected_normal = None
self.actual_normal = None
# Set point force Magnitude
azlmbr.physics.ForcePointRequestBus(azlmbr.bus.Event, "SetMagnitude", self.id, magnitude)
# Set up handler
self.handler = azlmbr.physics.ForceRegionNotificationBusHandler()
self.handler.connect(None)
self.handler.add_callback("OnCalculateNetForce", self.on_calc_force)
# Callback function for OnCalculateNetForce event
def on_calc_force(self, args):
# type: ([EntityId, EntityId, azmath.Vector3, float]) -> None
if self.id.Equal(args[0]) and self.actual_magnitude is None:
for sphere in all_spheres:
if sphere.id.Equal(args[1]):
# Log event in print queue (for me and for the sphere)
self.print_list.append("Exerting force on {}:".format(sphere))
sphere.print_list.append("Force exerted by {}".format(self))
# Save calculated data to be compared later
self.actual_normal = args[2]
self.actual_magnitude = args[3]
self.expected_normal = sphere.initial_velocity.GetNormalizedSafe().Unary()
# Add expected/actual to print queue
self.print_list.append("Force Vector: ")
self.print_list.append(
" Expected: ({:.2f}, {:.2f}, {:.2f})".format(
self.expected_normal.x, self.expected_normal.y, self.expected_normal.z
)
)
self.print_list.append(
" Actual: ({:.2f}, {:.2f}, {:.2f})".format(
self.actual_normal.x, self.actual_normal.y, self.actual_normal.z
)
)
self.print_list.append("Force Magnitude: ")
self.print_list.append(" Expected: {}".format(self.expected_magnitude))
self.print_list.append(" Actual: {:.2f}".format(self.actual_magnitude))
# EntityBase::report() overload.
# Force regions have 2 test tuples to report on
def report(self):
magnitude_test = Tests.get_test(self.name + "_mag_result")
normal_test = Tests.get_test(self.name + "_norm_result")
Report.result(magnitude_test, self.magnitude_result())
Report.result(normal_test, self.normal_result())
# Test result calculations
# Used in EntityBase for reporting results
def result(self):
# type: () -> bool
return self.magnitude_result() and self.normal_result()
def magnitude_result(self):
# type: () -> bool
return (
self.actual_magnitude is not None
and abs(self.actual_magnitude - self.expected_magnitude) < CLOSE_ENOUGH
)
def normal_result(self):
# type: () -> bool
return (
self.actual_normal is not None
and self.expected_normal is not None
and self.expected_normal.IsClose(self.actual_normal, CLOSE_ENOUGH)
)
# Spheres are the objects that test the force regions. They store an expected collision entity and an
# actual collision entity
class Sphere(EntityBase):
def __init__(self, name, initial_velocity, expected_collision):
# type: (str, azmath.Vector3, EntityBase, bool) -> None
EntityBase.__init__(self, name)
self.initial_velocity = initial_velocity
azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "SetLinearVelocity", self.id, initial_velocity)
self.print_list.append(
"Initial velocity: ({:.2f}, {:.2f}, {:.2f})".format(
initial_velocity.x, initial_velocity.y, initial_velocity.z
)
)
self.expected_collision = expected_collision
self.print_list.append("Expected Collision: {}".format(expected_collision))
self.actual_collision = None
self.active = True
self.force_normal = None
# Registers a collision with this sphere. Saves a reference to the colliding entity for processing later.
# Deactivate self after collision is registered.
def collide(self, collision_entity):
# type: (EntityBase) -> None
# Log the event
self.print_list.append("Collided with {}".format(collision_entity))
self.actual_collision = collision_entity
# Deactivate self
azlmbr.entity.GameEntityContextRequestBus(azlmbr.bus.Broadcast, "DeactivateGameEntity", self.id)
self.active = False
# Calculates result
# Used in EntityBase for reporting results
def result(self):
if self.actual_collision is None:
return False
else:
return self.expected_collision.id.Equal(self.actual_collision.id)
# Box entities wait for a collision with a sphere as a means of validation the force region's offset
# worked according to plan.
class Box(EntityBase):
def __init__(self, name, expected_sphere_collisions):
# type: (str, int) -> None
EntityBase.__init__(self, name)
self.spheres_collided = 0
self.expected_sphere_collisions = expected_sphere_collisions
# Set up handler
self.handler = azlmbr.physics.CollisionNotificationBusHandler()
self.handler.connect(self.id)
self.handler.add_callback("OnCollisionBegin", self.on_collision_begin)
# Callback function for OnCollisionBegin event
def on_collision_begin(self, args):
for sphere in all_spheres:
if sphere.id.Equal(args[0]):
# Log event
self.print_list.append("Collided with {}".format(sphere))
# Register collision with sphere
sphere.collide(self)
self.spheres_collided += 1 # Count collisions for validation later
break
# Calculates test result
# Used in EntityBase for reporting results
def result(self):
return self.spheres_collided == self.expected_sphere_collisions
# Manages the entities required to run the test for one axis (X, Y, or Z)
class AxisTest:
def __init__(self, axis, init_velocity):
# type: (str, azmath.Vector3) -> None
self.name = axis + " axis test"
self.force_region = ForceRegion("force_region_" + axis, FORCE_MAGNITUDE)
self.spheres = [
Sphere("sphere_pass_" + axis, init_velocity, Box("force_region_pass_box_" + axis, 1)),
Sphere("sphere_bounce_" + axis, init_velocity, Box("external_pass_box_" + axis, 1)),
]
self.boxes = [
Box("external_fail_box_" + axis, 0),
Box("force_region_fail_box_" + axis, 0)
] + [
# Gets the Boxes passed to spheres on init
sphere.expected_collision for sphere in self.spheres
]
# Full list for all entities this test is responsible for
self.all_entities = self.boxes + self.spheres + [self.force_region]
# Add spheres to global "lookup" list
all_spheres.extend(self.spheres)
# Checks for all entities' test passing conditions
def passed(self):
return all([e.result() for e in self.all_entities])
# Returns true when this test has completed (i.e. when the spheres have collided and are deactivated)
def completed(self):
return all([not sphere.active for sphere in self.spheres])
# Reports results for all entities in this test
def report(self):
Report.info("::::::::::::::::::::::::::::: {} Results :::::::::::::::::::::::::::::".format(self.name))
for entity in self.all_entities:
entity.report()
# Prints the logs for all entities in this test
def print_log(self):
Report.info("::::::::::::::::::::::::::::: {} Log :::::::::::::::::::::::::::::".format(self.name))
for entity in self.all_entities:
entity.print_log()
# *********** Execution Code ***********
# 1) Open level
helper.init_idle()
helper.open_level("Physics", "ForceRegion_PositionOffset")
helper.enter_game_mode(Tests.enter_game_mode)
# 2) Variable set up
# Initial velocities for the three different directions spheres will be moving
x_vel = azmath.Vector3(SPEED, 0.0, 0.0)
y_vel = azmath.Vector3(0.0, SPEED, 0.0)
z_vel = azmath.Vector3(0.0, 0.0, SPEED)
# The three tests, one for each axis
axis_tests = [
AxisTest("x", z_vel), # Spheres move in Z direction when testing X axis offset
AxisTest("y", x_vel), # Spheres move in X direction when testing Y axis offset
AxisTest("z", y_vel), # Spheres move in Y direction when testing Z axis offset
]
# 3) Wait for test results or time out
Report.result(
Tests.test_completed, helper.wait_for_condition(
lambda: all([test.completed() for test in axis_tests]), TIME_OUT
)
)
# Report results
for test in axis_tests:
test.report()
# Print entity print queues for each failed test
for test in axis_tests:
if not test.passed():
test.print_log()
# 4) Exit game mode and close editor
helper.exit_game_mode(Tests.exit_game_mode)
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(ForceRegion_PositionOffset)
@@ -0,0 +1,138 @@
"""
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 : C5959761
# Test Case Title : Check that force region (physics asset) exerts point force
# fmt: off
class Tests():
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
find_ball = ("Ball entity found", "Ball entity not found")
find_sedan = ("Sedan entity found", "Sedan entity not found")
ball_fell = ("The ball fell", "The ball did not fall")
ball_enters_force_region = ("Ball entered force region", "Ball did not enter force region")
ball_exits_force_region = ("Ball exited force region", "Ball did not exit force region")
ball_moved_up = ("Ball moved up", "Ball did not move up")
ball_moved_forward = ("Ball moved forward", "Ball did not move forward")
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
# fmt: on
def ForceRegion_PxMeshShapedForce():
"""
Summary:
Runs an automated test to ensure that PhysX force regions with physics assets exert point force on rigid bodies
Level Description:
A ball is suspended over a force region with a physics asset mesh (sedan). The ball is offset 1 unit towards the
hood of the sedan (In the Y direction)
Ball (entity) - Sphere shaped PhysX Collider; PhysX Rigid body with gravity enabled
Sedan (entity) - Sedan shaped PhysX Collider; PhysX Force Region with a point force (magnitude 1000.0)
Expected Behavior:
The ball should fall once game mode is entered and bounce off the force region down and to the right.
Test Steps:
1) Open level
2) Enter game mode
3) Validate entities
4) Wait for ball to fall
5) Wait for ball to enter force region
6) Wait for ball to exit force region
7) Validate velocity vector
8) Exit game mode
9) Close the 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.
:return: None
"""
import os
import sys
import azlmbr
import azlmbr.legacy.general as general
import azlmbr.bus as bus
import azlmbr.math
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
TIMEOUT = 2.0
class Ball:
def __init__(self, name):
self.id = general.find_game_entity(name)
self.entered_force_region = False
self.exited_force_region = False
def get_velocity(self):
return azlmbr.physics.RigidBodyRequestBus(bus.Event, "GetLinearVelocity", self.id)
def is_falling(self):
return self.get_velocity().z < 0.0
helper.init_idle()
# 1) Open level
helper.open_level("Physics", "ForceRegion_PxMeshShapedForce")
# 2) Enter game mode
helper.enter_game_mode(Tests.enter_game_mode)
# 3) Validate entities
ball = Ball("Ball")
Report.critical_result(Tests.find_ball, ball.id.IsValid())
sedan_id = general.find_game_entity("Sedan")
Report.critical_result(Tests.find_sedan, sedan_id.IsValid())
# 4) Wait for ball to fall
def on_trigger_enter(args):
other_id = args[0]
if other_id.Equal(ball.id):
ball.entered_force_region = True
def on_trigger_exit(args):
other_id = args[0]
if other_id.Equal(ball.id):
ball.exited_force_region = True
handler = azlmbr.physics.TriggerNotificationBusHandler()
handler.connect(sedan_id)
handler.add_callback("OnTriggerEnter", on_trigger_enter)
handler.add_callback("OnTriggerExit", on_trigger_exit)
Report.critical_result(Tests.ball_fell, helper.wait_for_condition(ball.is_falling, TIMEOUT))
# 5) Wait for ball to enter force region
helper.wait_for_condition(lambda: ball.entered_force_region, TIMEOUT)
Report.critical_result(Tests.ball_enters_force_region, ball.entered_force_region)
# 6) Wait for ball to exit force region
helper.wait_for_condition(lambda: ball.exited_force_region, TIMEOUT)
Report.critical_result(Tests.ball_exits_force_region, ball.exited_force_region)
# 7) Validate velocity vector
velocity = ball.get_velocity()
Report.result(Tests.ball_moved_up, velocity.z > 0.0)
Report.result(Tests.ball_moved_forward, velocity.y > 0.0)
# 8) Exit game mode
helper.exit_game_mode(Tests.exit_game_mode)
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(ForceRegion_PxMeshShapedForce)
@@ -0,0 +1,404 @@
"""
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 : C5959809
# Test Case Title : Verify Force Region Rotational Offset
# fmt:off
class Tests:
# General tests
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
test_completed = ("The test successfully completed", "The test timed out")
# ***** Entities found *****
# Force Regions
force_region_x_found = ("Force Region for X axis test was found", "Force Region for X axis test was NOT found")
force_region_y_found = ("Force Region for Y axis test was found", "Force Region for Y axis test was NOT found")
force_region_z_found = ("Force Region for Z axis test was found", "Force Region for Z axis test was NOT found")
# Force Region Pass Boxes
force_region_pass_box_x_found = ("Force Region Pass Box for X axis test was found", "Force Region Pass Box for X axis test was NOT found")
force_region_pass_box_y_found = ("Force Region Pass Box for Y axis test was found", "Force Region Pass Box for Y axis test was NOT found")
force_region_pass_box_z_found = ("Force Region Pass Box for Z axis test was found", "Force Region Pass Box for Z axis test was NOT found")
# External Pass Boxes
external_pass_box_x_found = ("External Pass Box for X axis test was found", "External Pass Box for X axis test was NOT found")
external_pass_box_y_found = ("External Pass Box for Y axis test was found", "External Pass Box for Y axis test was NOT found")
external_pass_box_z_found = ("External Pass Box for Z axis test was found", "External Pass Box for Z axis test was NOT found")
# Force Region Fail Boxes
force_region_fail_box_x_found = ("Force Region Fail Box for X axis test was found", "Force Region Fail Box for X axis test was NOT found")
force_region_fail_box_y_found = ("Force Region Fail Box for Y axis test was found", "Force Region Fail Box for Y axis test was NOT found")
force_region_fail_box_z_found = ("Force Region Fail Box for Z axis test was found", "Force Region Fail Box for Z axis test was NOT found")
# External Fail Boxes
external_fail_box_x_found = ("External Fail Box for X axis test was found", "External Fail Box for X axis test was NOT found")
external_fail_box_y_found = ("External Fail Box for Y axis test was found", "External Fail Box for Y axis test was NOT found")
external_fail_box_z_found = ("External Fail Box for Z axis test was found", "External Fail Box for Z axis test was NOT found")
# Pass spheres
sphere_pass_x_found = ("Pass Sphere for X axis test was found", "Pass Sphere for X axis test was NOT found")
sphere_pass_y_found = ("Pass Sphere for Y axis test was found", "Pass Sphere for Y axis test was NOT found")
sphere_pass_z_found = ("Pass Sphere for Z axis test was found", "Pass Sphere for Z axis test was NOT found")
# Bounce Spheres
sphere_bounce_x_found = ("Bounce Sphere for X axis test was found", "Bounce Sphere for X axis test was NOT found")
sphere_bounce_y_found = ("Bounce Sphere for Y axis test was found", "Bounce Sphere for Y axis test was NOT found")
sphere_bounce_z_found = ("Bounce Sphere for Z axis test was found", "Bounce Sphere for Z axis test was NOT found")
# ****** Entities' results ******
# Force Regions
force_region_x_mag_result = ("Force Region for X axis magnitude exerted was as expected", "Force Region for X axis magnitude exerted was NOT as expected")
force_region_y_mag_result = ("Force Region for Y axis magnitude exerted was as expected", "Force Region for Y axis magnitude exerted was NOT as expected")
force_region_z_mag_result = ("Force Region for Z axis magnitude exerted was as expected", "Force Region for Z axis magnitude exerted was NOT as expected")
force_region_x_norm_result = ("Force Region for X axis normal exerted was as expected", "Force Region for X axis normal exerted was NOT as expected")
force_region_y_norm_result = ("Force Region for Y axis normal exerted was as expected", "Force Region for Y axis normal exerted was NOT as expected")
force_region_z_norm_result = ("Force Region for Z axis normal exerted was as expected", "Force Region for Z axis normal exerted was NOT as expected")
# Force Region Pass Boxes
force_region_pass_box_x_result = ("Force Region Pass Box for X axis collided with exactly one sphere", "Force Region Pass Box for X axis DID NOT collide with exactly one sphere")
force_region_pass_box_y_result = ("Force Region Pass Box for Y axis collided with exactly one sphere", "Force Region Pass Box for Y axis DID NOT collide with exactly one sphere")
force_region_pass_box_z_result = ("Force Region Pass Box for Z axis collided with exactly one sphere", "Force Region Pass Box for Z axis DID NOT collide with exactly one sphere")
# External Pass Boxes
external_pass_box_x_result = ("External Pass Box for X axis collided with exactly one sphere", "External Pass Box for X axis DID NOT collide with exactly one sphere")
external_pass_box_y_result = ("External Pass Box for Y axis collided with exactly one sphere", "External Pass Box for Y axis DID NOT collide with exactly one sphere")
external_pass_box_z_result = ("External Pass Box for Z axis collided with exactly one sphere", "External Pass Box for Z axis DID NOT collide with exactly one sphere")
# Force Region Fail Boxes
force_region_fail_box_x_result = ("Force Region Fail Box for X axis collided with no spheres", "Force Region Fail Box for X axis DID collide with a sphere")
force_region_fail_box_y_result = ("Force Region Fail Box for Y axis collided with no spheres", "Force Region Fail Box for Y axis DID collide with a sphere")
force_region_fail_box_z_result = ("Force Region Fail Box for Z axis collided with no spheres", "Force Region Fail Box for Z axis DID collide with a sphere")
# External Fail Boxes
external_fail_box_x_result = ("External Fail Box for X axis collided with no spheres", "External Fail Box for X axis DID collide with a sphere")
external_fail_box_y_result = ("External Fail Box for Y axis collided with no spheres", "External Fail Box for Y axis DID collide with a sphere")
external_fail_box_z_result = ("External Fail Box for Z axis collided with no spheres", "External Fail Box for Z axis DID collide with a sphere")
# Pass spheres
sphere_pass_x_result = ("Pass Sphere for X axis collided with expected Box", "Pass Sphere for X axis DID NOT collide with expected Box")
sphere_pass_y_result = ("Pass Sphere for Y axis collided with expected Box", "Pass Sphere for Y axis DID NOT collide with expected Box")
sphere_pass_z_result = ("Pass Sphere for Z axis collided with expected Box", "Pass Sphere for Z axis DID NOT collide with expected Box")
# Bounce Spheres
sphere_bounce_x_result = ("Bounce Sphere for X axis collided with expected Box", "Bounce Sphere for X axis DID NOT collide with expected Box")
sphere_bounce_y_result = ("Bounce Sphere for Y axis collided with expected Box", "Bounce Sphere for Y axis DID NOT collide with expected Box")
sphere_bounce_z_result = ("Bounce Sphere for Z axis collided with expected Box", "Bounce Sphere for Z axis DID NOT collide with expected Box")
# fmt:on
@staticmethod
# Test tuple accessor via string
def get_test(test_name):
if test_name in Tests.__dict__:
return Tests.__dict__[test_name]
else:
return None
def ForceRegion_RotationalOffset():
"""
Summary:
Force Region rotational offset is tested for each of the 3 axises (X, Y, and Z). Each axis's test has one
ForceRegion, two spheres and four boxes. By monitoring which box each sphere collides with we can validate the
integrity of the ForceRegions rotational offset.
Level Description:
Each axis's test has the following entities:
one force region - set for point force and with it's collider rotationally offset (on the axis in test).
two spheres - one positioned near the transform of the force region, one positioned near the [offset] collider for
the force region
four boxes - One box is positioned inside the force region's transform, one inside the force region's [offset]
collider. The other two boxes are positioned behind the two spheres (relative to the direction they will be
initially traveling)
Expected Behavior:
All three axises' tests run in parallel. when the tests begin, the spheres should move toward their expected
force regions. The spheres positioned to collide with their region's [offset] collider should be forced backwards
before entering the force region and collide with the box behind it. The spheres positioned by their force region's
transforms should pass straight into the transform and collide with the box inside the transform.
The boxes inside the Force Regions' [offset] colliders and the boxes behind the spheres set to move into the Force
Regions' transforms should not register any collisions.
Steps:
1) Open level and enter game mode
2) Set up tests and variables
3) Wait for test results (or time out)
(Report results)
4) Exit game mode and close the editor
: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.math as azmath
import azlmbr.bus
import azlmbr
# Constants
CLOSE_ENOUGH = 0.01 # Close enough threshold for comparing floats
TIME_OUT = 2.0 # Time out (in seconds) until test is aborted
FORCE_MAGNITUDE = 1000.0 # Point force magnitude for Force Regions
SPEED = 3.0 # Initial speed (in m/s) of the moving spheres.
# Full list for all spheres. Used for EntityId look up in event handlers
all_spheres = []
# Entity base class handles very general entity initialization
# Should be treated as a "virtual" class and all implementing child
# classes should implement a "self.result()" function referenced in EntityBase::report(self)
class EntityBase:
def __init__(self, name):
# type: (str) -> None
self.name = name
self.print_list = []
self.id = general.find_game_entity(name)
found_test = Tests.get_test(name + "_found")
Report.critical_result(found_test, self.id.IsValid())
# Reports this entity's result. Implicitly calls "get" on result.
# Subclasses implement their own definition of a successful result
def report(self):
# type: () -> None
result_test = Tests.get_test(self.name + "_result")
Report.result(result_test, self.result())
# Prints the print queue (with decorated header) if not empty
def print_log(self):
# type: () -> None
if self.print_list:
Report.info("*********** {} **********".format(self))
for line in self.print_list:
Report.info(line)
Report.info("")
# Quick string cast, returns entity name
def __str__(self):
# type: () -> str
return self.name
# ForceRegion handles all the data and behavior associated with a ForceRegion (for this test)
# They simply wait for a Sphere to collide with them. On collision they store the calculated force
# magnitude for verification.
class ForceRegion(EntityBase):
def __init__(self, name, magnitude):
# type: (str, float) -> None
EntityBase.__init__(self, name)
self.expected_magnitude = magnitude
self.actual_magnitude = None
self.expected_normal = None
self.actual_normal = None
# Set point force Magnitude
azlmbr.physics.ForcePointRequestBus(azlmbr.bus.Event, "SetMagnitude", self.id, magnitude)
# Set up handler
self.handler = azlmbr.physics.ForceRegionNotificationBusHandler()
self.handler.connect(None)
self.handler.add_callback("OnCalculateNetForce", self.on_calc_force)
# Callback function for OnCalculateNetForce event
def on_calc_force(self, args):
# type: ([EntityId, EntityId, azmath.Vector3, float]) -> None
if self.id.Equal(args[0]) and self.actual_magnitude is None:
for sphere in all_spheres:
if sphere.id.Equal(args[1]):
# Log event in print queue (for me and for the sphere)
self.print_list.append("Exerting force on {}:".format(sphere))
sphere.print_list.append("Force exerted by {}".format(self))
# Save calculated data to be compared later
self.actual_normal = args[2]
self.actual_magnitude = args[3]
pos = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", self.id)
sphere_pos = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", sphere.id)
self.expected_normal = sphere_pos.Subtract(pos).GetNormalizedSafe()
# Add expected/actual to print queue
self.print_list.append("Force Vector: ")
self.print_list.append(
" Expected: ({:.2f}, {:.2f}, {:.2f})".format(
self.expected_normal.x, self.expected_normal.y, self.expected_normal.z
)
)
self.print_list.append(
" Actual: ({:.2f}, {:.2f}, {:.2f})".format(
self.actual_normal.x, self.actual_normal.y, self.actual_normal.z
)
)
self.print_list.append("Force Magnitude: ")
self.print_list.append(" Expected: {}".format(self.expected_magnitude))
self.print_list.append(" Actual: {:.2f}".format(self.actual_magnitude))
# EntityBase::report() overload.
# Force regions have 2 test tuples to report on
def report(self):
magnitude_test = Tests.get_test(self.name + "_mag_result")
normal_test = Tests.get_test(self.name + "_norm_result")
Report.result(magnitude_test, self.magnitude_result())
Report.result(normal_test, self.normal_result())
# Test result calculations
# Used in EntityBase for reporting results
def result(self):
# type: () -> bool
return self.magnitude_result() and self.normal_result()
def magnitude_result(self):
# type: () -> bool
return (
self.actual_magnitude is not None
and abs(self.actual_magnitude - self.expected_magnitude) < CLOSE_ENOUGH
)
def normal_result(self):
# type: () -> bool
return (
self.actual_normal is not None
and self.expected_normal is not None
and self.expected_normal.IsClose(self.actual_normal, CLOSE_ENOUGH)
)
# Spheres are the objects that test the force regions. They store an expected collision entity and an
# actual collision entity
class Sphere(EntityBase):
def __init__(self, name, initial_velocity, expected_collision):
# type: (str, azmath.Vector3, EntityBase) -> None
EntityBase.__init__(self, name)
self.initial_velocity = initial_velocity
azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "SetLinearVelocity", self.id, initial_velocity)
self.print_list.append(
"Initial velocity: ({:.2f}, {:.2f}, {:.2f})".format(
initial_velocity.x, initial_velocity.y, initial_velocity.z
)
)
self.expected_collision = expected_collision
self.print_list.append("Expected Collision: {}".format(expected_collision))
self.actual_collision = None
self.active = True
self.force_normal = None
# Registers a collision with this sphere. Saves a reference to the colliding entity for processing later.
# Deactivate self after collision is registered.
def collide(self, collision_entity):
# type: (EntityBase) -> None
# Log the event
self.print_list.append("Collided with {}".format(collision_entity))
self.actual_collision = collision_entity
# Deactivate self
azlmbr.entity.GameEntityContextRequestBus(azlmbr.bus.Broadcast, "DeactivateGameEntity",
self.id)
self.active = False
# Calculates result
# Used in EntityBase for reporting results
def result(self):
if self.actual_collision is None:
return False
else:
return self.expected_collision.id.Equal(self.actual_collision.id)
# Box entities wait for a collision with a sphere as a means of validation the force region's offset
# worked according to plan.
class Box(EntityBase):
def __init__(self, name, expected_sphere_collisions):
# type: (str, int) -> None
EntityBase.__init__(self, name)
self.spheres_collided = 0
self.expected_sphere_collisions = expected_sphere_collisions
# Set up handler
self.handler = azlmbr.physics.CollisionNotificationBusHandler()
self.handler.connect(self.id)
self.handler.add_callback("OnCollisionBegin", self.on_collision_begin)
# Callback function for OnCollisionBegin event
def on_collision_begin(self, args):
for sphere in all_spheres:
if sphere.id.Equal(args[0]):
# Log event
self.print_list.append("Collided with {}".format(sphere))
# Register collision with sphere
sphere.collide(self)
self.spheres_collided += 1 # Count collisions for validation later
break
# Calculates test result
# Used in EntityBase for reporting results
def result(self):
return self.spheres_collided == self.expected_sphere_collisions
# Manages the entities required to run the test for one axis (X, Y, or Z)
class AxisTest:
def __init__(self, axis, init_velocity):
# type: (str, azmath.Vector3) -> None
self.name = axis + " axis test"
self.force_region = ForceRegion("force_region_" + axis, FORCE_MAGNITUDE)
self.spheres = [
Sphere("sphere_pass_" + axis, init_velocity, Box("force_region_pass_box_" + axis, 1)),
Sphere("sphere_bounce_" + axis, init_velocity, Box("external_pass_box_" + axis, 1)),
]
self.boxes = [
Box("external_fail_box_" + axis, 0),
Box("force_region_fail_box_" + axis, 0)
] + [
sphere.expected_collision for sphere in self.spheres
# Gets the Boxes passed to spheres on init
]
# Full list for all entities this test is responsible for
self.all_entities = self.boxes + self.spheres + [self.force_region]
# Add spheres to global "lookup" list
all_spheres.extend(self.spheres)
# Checks for all entities' test passing conditions
def passed(self):
return all([e.result() for e in self.all_entities])
# Returns true when this test has completed (i.e. when the spheres have collided and are deactivated)
def completed(self):
return all([not sphere.active for sphere in self.spheres])
# Reports results for all entities in this test
def report(self):
Report.info("::::::::::::::::::::::::::::: {} Results :::::::::::::::::::::::::::::".format(self.name))
for entity in self.all_entities:
entity.report()
# Prints the logs for all entities in this test
def print_log(self):
Report.info("::::::::::::::::::::::::::::: {} Log :::::::::::::::::::::::::::::".format(self.name))
for entity in self.all_entities:
entity.print_log()
# *********** Execution Code ***********
# 1) Open level
helper.init_idle()
helper.open_level("Physics", "ForceRegion_RotationalOffset")
helper.enter_game_mode(Tests.enter_game_mode)
# 2) Variable set up
# Initial velocities for the three different directions spheres will be moving
x_vel = azmath.Vector3(SPEED, 0.0, 0.0)
y_vel = azmath.Vector3(0.0, SPEED, 0.0)
z_vel = azmath.Vector3(0.0, 0.0, SPEED)
# The three tests, one for each axis
axis_tests = [AxisTest("x", x_vel), AxisTest("y", y_vel), AxisTest("z", z_vel)]
# 3) Wait for test results or time out
Report.result(
Tests.test_completed, helper.wait_for_condition(
lambda: all([test.completed() for test in axis_tests]), TIME_OUT
)
)
# Report results
for test in axis_tests:
test.report()
# Print entity print queues for each failed test
for test in axis_tests:
if not test.passed():
test.print_log()
# 4) Exit game mode and close editor
helper.exit_game_mode(Tests.exit_game_mode)
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(ForceRegion_RotationalOffset)
@@ -0,0 +1,141 @@
# coding=utf-8
"""
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 : C5932043
# Test Case Title : Check that force region exerts simple drag force on rigid bodies
# fmt: off
class Tests:
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
find_sphere = ("Sphere found", "Sphere not found")
find_force_region = ("Force Region found", "Force Region not found")
# entity_actions_success refers to if the entity completed all expected actions in the test. For this test, this
# will be, did the Sphere enter and exit the force region
entity_actions_success = ("Entity actions completed", "Entity actions not completed")
sphere_lost_height = ("Sphere went down", "Sphere didn't go down")
force_region_slows = ("Force Region slowed Sphere", "Force Region didn't slow Sphere")
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
# fmt: on
def ForceRegion_SimpleDragForceOnRigidBodies():
# This run() function will open a a level and validate that the force region slows down a spheres fall.
# It does this by:
# 1) Opens level with sphere above a force region
# 2) Enters Game mode
# 3) Finds the entities in the scene
# 4) Listens for sphere to enter the force region
# 5) Gets z velocity and position of sphere
# 6) Listens for sphere to exit force region
# 7) Gets new velocity and position of sphere
# 8) Validate the results
# 9) Exits game mode and editor
# Setup path
import os, 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
# Holds details about the sphere
class Sphere:
id = None
start_velocity_z = 0.0
end_velocity_z = 0.0
sphere_start_z_position = 0.0
sphere_end_z_position = 0.0
entered_force_region = False
exited_force_region = False
TIME_OUT = 4.0 # Time given to test to complete.
helper.init_idle()
# 1) Open level
helper.open_level("Physics", "ForceRegion_SimpleDragForceOnRigidBodies")
# 2) Enter game mode
helper.enter_game_mode(Tests.enter_game_mode)
# 3) Get Entities
Sphere.id = general.find_game_entity("Sphere")
Report.result(Tests.find_sphere, Sphere.id.IsValid())
force_region_id = general.find_game_entity("Force Region")
Report.result(Tests.find_force_region, force_region_id.IsValid())
# Called if Sphere enters force region
def on_trigger_begin(args):
other_id = args[0]
if other_id.Equal(Sphere.id):
Report.info("Entered force region")
Sphere.entered_force_region = True
# 5) Gets z velocity and position of sphere
Sphere.start_velocity_z = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetLinearVelocity", Sphere.id).z
Sphere.sphere_start_z_position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldZ", Sphere.id)
Report.info(
"Sphere Start Z position = {} Z Start Velocity = {}".format(
Sphere.sphere_start_z_position, Sphere.start_velocity_z
)
)
# Called when sphere exits force region
def on_trigger_end(args):
other_id = args[0]
if other_id.Equal(Sphere.id):
Report.info("Exited force region")
Sphere.exited_force_region = True
# 7) Gets new velocity and position of sphere
Sphere.end_velocity_z = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetLinearVelocity", Sphere.id).z
Sphere.sphere_end_z_position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldZ", Sphere.id)
Report.info(
"Sphere End Z position = {} Sphere End Z Velocity = {}".format(
Sphere.sphere_end_z_position, Sphere.end_velocity_z
)
)
handler = azlmbr.physics.TriggerNotificationBusHandler()
handler.connect(force_region_id)
# 4) Listens for sphere to enter the force region
handler.add_callback("OnTriggerEnter", on_trigger_begin)
# 6) Listens for sphere to exit force region
handler.add_callback("OnTriggerExit", on_trigger_end)
# Wait until all entities in scene are done performing their actions or test times out.
def done_with_entity_actions():
return Sphere.entered_force_region and Sphere.exited_force_region
test_completed = helper.wait_for_condition(done_with_entity_actions, TIME_OUT)
Report.result(Tests.entity_actions_success, test_completed)
# 8) Validate the results
if test_completed:
# Did Sphere fall
sphere_descended = Sphere.sphere_end_z_position + 0.5 < Sphere.sphere_start_z_position # 0.5 for buffer
Report.result(Tests.sphere_lost_height, sphere_descended)
# Did Force Region slow down sphere's falling
# Note: The faster a sphere falls, the greater its negative/downward velocity will be. Adding 1.0 for buffer.
force_region_result = round(Sphere.end_velocity_z, 2) > round(Sphere.start_velocity_z, 2) + 1.0
Report.result(Tests.force_region_slows, force_region_result)
# 9) Exits game mode and editor
helper.exit_game_mode(Tests.exit_game_mode)
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(ForceRegion_SimpleDragForceOnRigidBodies)
@@ -0,0 +1,137 @@
"""
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 : C6090546
# Test Case Title : Check that a force region slice can be saved and instantiated
# fmt: off
class Tests():
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
find_sphere = ("SphereRigidBody found", "SphereRigidBody not found")
find_force_region = ("ForceRegionSliceEntity found", "ForceRegionSliceEntity not found")
sphere_dropped = ("Sphere dropped down", "Sphere did not drop down")
sphere_bounced = ("Sphere bounced up vertically", "Sphere did not bounce up vertically")
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
# fmt: on
def ForceRegion_SliceFileInstantiates():
"""
Summary:
Check that a force region slice can be saved and instantiated
Level Description:
The SphereRigidBody entity is placed above the ForceRegionBox entity
ForceRegionSliceEntity (entity) - Slice Asset which is imported from .slice file of another level which has
an entity with force region component.
SphereRigidBody (entity) - Entity with PhysX Rigid body, Mesh and collider components.
The SphereRigidBody is placed above the ForceRegionEntity.
Expected Behavior:
Sphere drops and bounces vertically up from force region.
We are checking if the ball started falling down from its initial position and then verifying if it has bounced up
its initial position after entering into the force region.
Test Steps:
1) Open level
2) Enter game mode
3) Retrieve and validate entities
4) Get the initial position of the Sphere (rigid body)
5) Check if the ball is falling down
6) Add trigger notification handler
7) Wait till the ball enters the force region
8) Check if the ball has bounced vertically up
9) Exit game mode
10) Close the 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.
: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
# Constants
TIMEOUT = 3.0 # wait a maximum of 3 seconds
SPHERE_RADIUS = 0.5
class Sphere:
id = None
initial_position = None
current_position = None
in_force_region = False
bounced = False
class ForceRegion:
id = None
def sphere_bounced():
Sphere.current_position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", Sphere.id)
Sphere.bounced = Sphere.current_position.z > (Sphere.initial_position.z + SPHERE_RADIUS)
return Sphere.bounced
def on_trigger_enter(args):
if args[0].Equal(Sphere.id):
Sphere.in_force_region = True
helper.init_idle()
# 1) Open level
helper.open_level("Physics", "ForceRegion_SliceFileInstantiates")
# 2) Enter game mode
helper.enter_game_mode(Tests.enter_game_mode)
# 3) Retrieve and validate entities
Sphere.id = general.find_game_entity("SphereRigidBody")
Report.critical_result(Tests.find_sphere, Sphere.id.IsValid())
ForceRegion.id = general.find_game_entity("ForceRegionSliceEntity")
Report.critical_result(Tests.find_force_region, ForceRegion.id.IsValid())
# 4) Get the initial position of the Sphere (rigid body)
Sphere.initial_position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", Sphere.id)
# 5) Check if the ball is falling down
Sphere.current_position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", Sphere.id)
sphere_dropped = Sphere.current_position.z < (Sphere.initial_position.z + SPHERE_RADIUS)
Report.critical_result(Tests.sphere_dropped, sphere_dropped)
# 6) Add trigger notification handler
handler = azlmbr.physics.TriggerNotificationBusHandler()
handler.connect(ForceRegion.id)
handler.add_callback("OnTriggerEnter", on_trigger_enter)
# 7) Wait till the ball enters the force region
helper.wait_for_condition(lambda: Sphere.in_force_region, TIMEOUT)
# 8) Check if the ball has bounced vertically up
# wait till the ball bounces
helper.wait_for_condition(sphere_bounced, TIMEOUT)
Report.critical_result(Tests.sphere_bounced, Sphere.bounced)
# 9) Exit game mode
helper.exit_game_mode(Tests.exit_game_mode)
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(ForceRegion_SliceFileInstantiates)
@@ -0,0 +1,142 @@
"""
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 : C12905527
# Test Case Title : Check that deviation occurring in Force Magnitude due to Values in Force direction is not large
# fmt: off
class Tests():
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
find_force_region = ("Force region was found", "Force region was not found")
find_sphere = ("Sphere was found", "Sphere was not found")
sphere_entered_region = ("Sphere entered force region", "Sphere did not enter force region")
sphere_exited_region = ("Sphere exited force region", "Sphere did not exit force region")
force_magnitude_close = ("The net force magnitude was close to the expected value", "The net force magnitude was not close to the expected value")
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
# fmt: on
def ForceRegion_SmallMagnitudeDeviationOnLargeForces():
"""
Summary:
Runs an automated test to ensure that the calculated net force magnitude is close to the configured value
Level Description:
A sphere (Sphere) is positioned above a force region (ForceRegion)
Sphere has a sphere PhysX collider and PhysX Rigid Body. Gravity is disabled, and it has an initial velocity of
2 m/s in the Z direction.
ForceRegion has a box PhysX collider and PhysX Force Region. Magnitude on the force region is set to 1,000,000.0
Expected Behavior:
The sphere enters and exits the force region. on_calc_net_force returns a value close to 1,000,000.0
Test Steps:
1) Open level
2) Enter game mode
3) Validate entities
4) Wait for the sphere to enter and exit the force region
5) Check the calculated net force against what we expect
6) Exit game mode
7) Close the 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.
: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
import azlmbr.legacy.general as general
EXPECTED_MAGNITUDE = 1000000.0
PERMISSIBLE_ERROR = 0.001 # +/- 0.1%
TIMEOUT = 1.0
class Sphere:
def __init__(self, name):
self.name = name
self.id = general.find_game_entity(name)
self.entered_force_region = False
self.exited_force_region = False
self.net_force_magnitude = 0
def on_trigger_enter(args):
Report.info("triggered")
other_id = args[0]
if other_id.Equal(sphere.id):
sphere.entered_force_region = True
def on_trigger_exit(args):
other_id = args[0]
if other_id.Equal(sphere.id):
sphere.exited_force_region = True
def on_calc_net_force(args):
other_id = args[1]
force_magnitude = args[3]
if other_id.Equal(sphere.id):
sphere.net_force_magnitude = force_magnitude
helper.init_idle()
# 1) Open level
Report.info(general.get_current_level_name())
helper.open_level("Physics", "ForceRegion_SmallMagnitudeDeviationOnLargeForces")
# 2) Enter game mode
helper.enter_game_mode(Tests.enter_game_mode)
# 3) Validate entities
sphere = Sphere("Sphere")
force_region_id = general.find_game_entity("ForceRegion")
Report.critical_result(Tests.find_sphere, sphere.id.IsValid())
Report.critical_result(Tests.find_force_region, force_region_id.IsValid())
# Create handlers
trigger_handler = azlmbr.physics.TriggerNotificationBusHandler()
trigger_handler.connect(force_region_id)
trigger_handler.add_callback("OnTriggerEnter", on_trigger_enter)
trigger_handler.add_callback("OnTriggerExit", on_trigger_exit)
net_force_handler = azlmbr.physics.ForceRegionNotificationBusHandler()
net_force_handler.connect(None)
net_force_handler.add_callback("OnCalculateNetForce", on_calc_net_force)
# 4) Wait for the sphere to enter and exit the force region
Report.result(Tests.sphere_entered_region, helper.wait_for_condition(lambda: sphere.entered_force_region, TIMEOUT))
Report.result(Tests.sphere_exited_region, helper.wait_for_condition(lambda: sphere.exited_force_region, TIMEOUT))
# 5) Check the calculated net force against what we expect
absolute_difference = abs(sphere.net_force_magnitude - EXPECTED_MAGNITUDE)
error = absolute_difference / EXPECTED_MAGNITUDE
net_force_was_close = error < PERMISSIBLE_ERROR
Report.result(Tests.force_magnitude_close, net_force_was_close)
if not net_force_was_close:
Report.info(
"\nExpected Magnitude: {}"
"\nActual Magnitude: {}"
"\nPermissible Error: {}"
"\nMeasured Error: {}".format(EXPECTED_MAGNITUDE, sphere.net_force_magnitude, PERMISSIBLE_ERROR, error)
)
# 6) Exit game mode
helper.exit_game_mode(Tests.exit_game_mode)
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(ForceRegion_SmallMagnitudeDeviationOnLargeForces)
@@ -0,0 +1,149 @@
"""
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 : C5959759
# Test Case Title : Check that force region (sphere) exerts point force
# fmt: off
class Tests:
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
find_cube = ("Entity Cube found", "Cube not found")
find_force_region = ("Entity force region found", "Force region not found")
gravity_works = ("Cube falls", "Cube did not fall")
sphere_gravity_enabled = ("Gravity is enabled on the cube", "Gravity is not enabled on the cube")
force_region_trigger = ("Cube entered and exited force region", "Cube did not enter adn exit force region")
force_calculated = ("OnCalculateNetForce calculated", "OnCalculateNetForce did not get calculated")
force_x_vector = ("Force x vector is positive", "Force x vector is not positive")
force_y_vector = ("Force y vector is positive", "Force y vector is not positive")
point_force_magnitude_value = ("Magnitude is set to 1000", "Magnitude is not set to 1000")
point_force_magnitude = ("Calculated magnitude is greater than set magnitude", "Calculated magnitude is not greater than set magnitude")
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
# fmt: on
def ForceRegion_SphereShapedForce():
"""
Level Setup
The level consists of a sherical force region with a point force of 1000.
A RigidBody cube with mass 1kg is positioned above the force region at an offset.
On entering game mode the cube will fall into the spherical point force region.
This should cause the cube to bounce off at considerable velocity.
We validate the point force observed is as expected
"""
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
EXPECTED_MAGNITUDE = 1000.0
NEGATIVE_VELOCITY = -0.001
TOLERANCE = 0.001
# 1) Open level and enter game mode
helper.init_idle()
helper.open_level("Physics", "ForceRegion_SphereShapedForce")
helper.enter_game_mode(Tests.enter_game_mode)
# 2) Retrieve entities
cube_id = general.find_game_entity("CubeRigidBody")
Report.critical_result(Tests.find_cube, cube_id.IsValid())
sphere_force_region = general.find_game_entity("SphereForceRegion")
Report.critical_result(Tests.find_force_region, sphere_force_region.IsValid())
# 3) Gravity works
gravity_enabled = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "IsGravityEnabled", cube_id)
Report.result(Tests.sphere_gravity_enabled, gravity_enabled)
def is_going_down():
vel = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetLinearVelocity", cube_id)
return vel.z < NEGATIVE_VELOCITY
helper.wait_for_condition(is_going_down, 1.0)
cube_linear_velocity = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetLinearVelocity", cube_id)
Report.info("Cube z velocity from gravity: {}".format(cube_linear_velocity.z))
Report.result(Tests.gravity_works, cube_linear_velocity.z < NEGATIVE_VELOCITY)
# 4) Listen to trigger events and OnCalculateNetForce notification
class SphereForceRegion:
enter = False
exit = False
def on_enter(args):
other_id = args[0]
if other_id.Equal(cube_id):
Report.info("Cube touched spherical force region")
SphereForceRegion.enter = True
def on_exit(args):
other_id = args[0]
if other_id.Equal(cube_id):
Report.info("Cube touched spherical force region")
SphereForceRegion.exit = True
handler = azlmbr.physics.TriggerNotificationBusHandler()
handler.connect(sphere_force_region)
handler.add_callback("OnTriggerEnter", on_enter)
handler.add_callback("OnTriggerExit", on_exit)
class NetForce:
vector = None
magnitude = 0
def on_calc_net_force(args):
"""
args[0] - force region entity
args[1] - entity entering
args[2] - vector
args[3] - magnitude
"""
entering_entity = args[1]
if entering_entity.Equal(cube_id):
vector = args[2]
magnitude = args[3]
Report.info_vector3(vector, "Net Force vector", magnitude)
NetForce.vector = vector
NetForce.magnitude = magnitude
force_notification_handler = azlmbr.physics.ForceRegionNotificationBusHandler()
force_notification_handler.connect(None)
force_notification_handler.add_callback("OnCalculateNetForce", on_calc_net_force)
def force_region_enter_and_exit():
return SphereForceRegion.enter and SphereForceRegion.exit
helper.wait_for_condition(force_region_enter_and_exit, 3.0)
# 5) Report results
Report.result(Tests.force_region_trigger, force_region_enter_and_exit())
force_region_magnitude = azlmbr.physics.ForcePointRequestBus(azlmbr.bus.Event, "GetMagnitude", sphere_force_region)
Report.info("Force region magnitude = {}".format(force_region_magnitude))
# explicit check that the value is as expected
# set at level creation
Report.result(Tests.point_force_magnitude_value, force_region_magnitude == EXPECTED_MAGNITUDE)
# prevent the test from hanging if the force vector is not set
if NetForce.vector:
Report.success(Tests.force_calculated)
Report.result(Tests.force_x_vector, NetForce.vector.x > 0)
Report.result(Tests.force_y_vector, NetForce.vector.z > 0)
else:
Report.failure(Tests.force_calculated)
outcome = abs(NetForce.magnitude - force_region_magnitude) < TOLERANCE
Report.result(Tests.point_force_magnitude, outcome)
# 6) Exit game mode
helper.exit_game_mode(Tests.exit_game_mode)
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(ForceRegion_SphereShapedForce)
@@ -0,0 +1,161 @@
"""
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 : C5932045
# Test Case Title : Check that force region exerts spline follow force on rigid bodies
# fmt: off
class Tests():
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
find_sphere = ("Sphere entity found", "Sphere entity not found")
find_force_region = ("Force region found", "Force region not found")
find_trigger_0 = ("Trigger0 entity found", "Trigger0 entity not found")
find_trigger_1 = ("Trigger1 entity found", "Trigger1 entity not found")
find_trigger_2 = ("Trigger2 entity found", "Trigger2 entity not found")
find_trigger_3 = ("Trigger3 entity found", "Trigger3 entity not found")
triggers_positioned_apart = ("All triggers were positioned apart", "All triggers were not positioned apart")
sphere_fell = ("The sphere fell", "The sphere did not fall")
sphere_entered_force_region = ("The sphere entered the force region", "The sphere did not enter the force region before timeout")
sphere_reached_trigger0 = ("The sphere reached Trigger0", "The sphere did not reach Trigger0 before timeout")
sphere_reached_trigger1 = ("The sphere reached Trigger1", "The sphere did not reach Trigger1 before timeout")
sphere_reached_trigger2 = ("The sphere reached Trigger2", "The sphere did not reach Trigger2 before timeout")
sphere_reached_trigger3 = ("The sphere reached Trigger3", "The sphere did not reach Trigger3 before timeout")
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
# fmt: on
def ForceRegion_SplineForceOnRigidBodies():
"""
Summary:
Runs an automated test to ensure that a PhysX force region exerts spline follow force on rigid bodies
Level Description:
A sphere entity is positioned above a force region entity
The sphere has a PhysX collider (sphere) and rigidbody component with default values
The force region entity has a PhysX collider (Box) and force region with a 'spline follow' force.
It also has a spline component with 4 nodes. Each node is connected linearly in the following pattern:
[0]___
___[1]
[2]___
[3]
There are 4 trigger entities, each with a PhysX collider (sphere). They are positioned at each node.
Expected Behavior:
The sphere will fall into the force region and begin to follow the spline. It will visit each node in order.
Test Steps:
1) Open level
2) Enter game mode
3) Find entities
4) Verify triggers are apart
5) Drop the sphere
6) Wait for sphere to complete path
7) Exit game mode
8) Close the 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.
: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 itertools
import azlmbr
import azlmbr.legacy.general as general
import azlmbr.bus as bus
TIMEOUT = 5
MIN_TRIGGER_DISTANCE = 2
class Sphere:
def __init__(self, name):
self.name = name
self.id = general.find_game_entity(name)
def get_velocity(self):
return azlmbr.physics.RigidBodyRequestBus(bus.Event, "GetLinearVelocity", self.id)
def is_falling(self):
return self.get_velocity().z < 0.0
class Trigger:
def __init__(self, name, valid_test, triggered_test):
self.name = name
self.id = general.find_game_entity(name)
self.valid_test = valid_test
self.triggered_test = triggered_test
self.triggered = False
self.create_handler()
def get_position(self):
return azlmbr.components.TransformBus(bus.Event, "GetWorldTranslation", self.id)
def on_trigger_enter(self, args):
self.triggered = True
def create_handler(self):
self.handler = azlmbr.physics.TriggerNotificationBusHandler()
self.handler.connect(self.id)
self.handler.add_callback("OnTriggerEnter", self.on_trigger_enter)
def are_apart(position1, position2, distance):
return position1.GetDistance(position2) >= distance
helper.init_idle()
# 1) Open level
helper.open_level("Physics", "ForceRegion_SplineForceOnRigidBodies")
# 2) Enter game mode
helper.enter_game_mode(Tests.enter_game_mode)
# 3) Find entities
sphere = Sphere("Sphere")
Report.critical_result(Tests.find_sphere, sphere.id.IsValid())
force_region = Trigger("ForceRegion", Tests.find_force_region, Tests.sphere_entered_force_region)
trigger0 = Trigger("Trigger0", Tests.find_trigger_0, Tests.sphere_reached_trigger0)
trigger1 = Trigger("Trigger1", Tests.find_trigger_1, Tests.sphere_reached_trigger1)
trigger2 = Trigger("Trigger2", Tests.find_trigger_2, Tests.sphere_reached_trigger2)
trigger3 = Trigger("Trigger3", Tests.find_trigger_3, Tests.sphere_reached_trigger3)
all_triggers = (force_region, trigger0, trigger1, trigger2, trigger3)
for trigger in all_triggers:
Report.critical_result(trigger.valid_test, trigger.id.IsValid())
# 4) Verify triggers are apart
all_triggers_apart = True
for combination in itertools.combinations(all_triggers, 2):
if not are_apart(combination[0].get_position(), combination[1].get_position(), MIN_TRIGGER_DISTANCE):
all_triggers_apart = False
Report.critical_result(Tests.triggers_positioned_apart, all_triggers_apart)
# 5) Drop the sphere
Report.result(Tests.sphere_fell, helper.wait_for_condition(sphere.is_falling, TIMEOUT))
# 6) Wait for sphere to complete path
for trigger in all_triggers:
Report.result(trigger.triggered_test, helper.wait_for_condition(lambda: trigger.triggered, TIMEOUT))
# 7) Exit game mode
helper.exit_game_mode(Tests.exit_game_mode)
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(ForceRegion_SplineForceOnRigidBodies)
@@ -0,0 +1,166 @@
"""
Copyright (c) Contributors to the Open 3D Engine Project.
For complete copyright and license terms please see the LICENSE at the root of this distribution.
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
# Test case ID : C12868580
# Test Case Title : Check that spline follow force works if transform components of entity are altered
# fmt: off
class Tests():
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
find_sphere = ("Sphere entity found", "Sphere entity not found")
find_force_region = ("Force region found", "Force region not found")
find_trigger_0 = ("Trigger0 entity found", "Trigger0 entity not found")
find_trigger_1 = ("Trigger1 entity found", "Trigger1 entity not found")
find_trigger_2 = ("Trigger2 entity found", "Trigger2 entity not found")
find_trigger_3 = ("Trigger3 entity found", "Trigger3 entity not found")
triggers_positioned_apart = ("All triggers were positioned apart", "All triggers were not positioned apart")
sphere_entered_force_region = ("The sphere entered the force region", "The sphere did not enter the force region before timeout")
sphere_reached_trigger0 = ("The sphere reached Trigger0", "The sphere did not reach Trigger0 before timeout")
sphere_reached_trigger1 = ("The sphere reached Trigger1", "The sphere did not reach Trigger1 before timeout")
sphere_reached_trigger2 = ("The sphere reached Trigger2", "The sphere did not reach Trigger2 before timeout")
sphere_reached_trigger3 = ("The sphere reached Trigger3", "The sphere did not reach Trigger3 before timeout")
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
# fmt: on
def ForceRegion_SplineRegionWithModifiedTransform():
"""
Summary:
Runs an automated test to ensure that a PhysX force region correctly exerts spline follow force on rigid bodies when
its transform component has been modified
Level Description:
A sphere entity is positioned outside (in the +x direction) a force region entity.
The sphere has a PhysX collider (sphere) and rigidbody component with gravity disabled, and an initial velocity of
-3 m/s (in the x direction)
The force region entity has a PhysX collider (Box) and force region with a 'spline follow' force.
It also has a bezier spline component with 4 nodes. Each node is connected in a meandering path through the region.
[3]~~~[2]
)
O -> [0]~~~[1]
(sphere)
The force region is transformed 45 degrees around the Z axis, and scaled by 2 units in the X, Y, and Z directions.
There are also 4 trigger entities, each with a PhysX collider (sphere). They are positioned at each node.
Expected Behavior:
The sphere will enter into the force region and begin to follow the spline. It will visit each node in order.
Test Steps:
1) Open level
2) Enter game mode
3) Find entities
4) Verify triggers are apart
5) Wait for sphere to complete path
6) Exit game mode
7) Close the 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.
: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 itertools
import azlmbr
import azlmbr.legacy.general as general
import azlmbr.bus as bus
# region Constants
TIMEOUT = 5.0
MIN_TRIGGER_DISTANCE = 2.0
# endregion
# region Entity Classes
class Sphere:
def __init__(self, name):
self.name = name
self.id = general.find_game_entity(name)
def get_velocity(self):
return azlmbr.physics.RigidBodyRequestBus(bus.Event, "GetLinearVelocity", self.id)
class Trigger:
def __init__(self, name, valid_test, triggered_test):
self.name = name
self.id = general.find_game_entity(name)
self.valid_test = valid_test
self.triggered_test = triggered_test
self.triggered = False
self.create_handler()
def get_position(self):
return azlmbr.components.TransformBus(bus.Event, "GetWorldTranslation", self.id)
def on_trigger_enter(self, args):
self.triggered = True
def create_handler(self):
self.handler = azlmbr.physics.TriggerNotificationBusHandler()
self.handler.connect(self.id)
self.handler.add_callback("OnTriggerEnter", self.on_trigger_enter)
# endregion
# region Helper Functions
def are_apart(position1, position2, distance):
return position1.GetDistance(position2) >= distance
# endregion
helper.init_idle()
# 1) Open level
helper.open_level("Physics", "ForceRegion_SplineRegionWithModifiedTransform")
# 2) Enter game mode
helper.enter_game_mode(Tests.enter_game_mode)
# 3) Find Entities
sphere = Sphere("Sphere")
Report.critical_result(Tests.find_sphere, sphere.id.IsValid())
force_region = Trigger("ForceRegion", Tests.find_force_region, Tests.sphere_entered_force_region)
trigger0 = Trigger("Trigger0", Tests.find_trigger_0, Tests.sphere_reached_trigger0)
trigger1 = Trigger("Trigger1", Tests.find_trigger_1, Tests.sphere_reached_trigger1)
trigger2 = Trigger("Trigger2", Tests.find_trigger_2, Tests.sphere_reached_trigger2)
trigger3 = Trigger("Trigger3", Tests.find_trigger_3, Tests.sphere_reached_trigger3)
all_triggers = (force_region, trigger0, trigger1, trigger2, trigger3)
for trigger in all_triggers:
Report.critical_result(trigger.valid_test, trigger.id.IsValid())
# 4) Verify triggers are apart
all_triggers_apart = True
for trigger_a, trigger_b in itertools.combinations(all_triggers, 2):
if not are_apart(trigger_a.get_position(), trigger_b.get_position(), MIN_TRIGGER_DISTANCE):
Report.info("{} was not far enough away from {}".format(trigger_a.name, trigger_b.name))
all_triggers_apart = False
Report.critical_result(Tests.triggers_positioned_apart, all_triggers_apart)
# 5) Wait for sphere to complete path
for trigger in all_triggers:
Report.result(trigger.triggered_test, helper.wait_for_condition(lambda: trigger.triggered, TIMEOUT))
# 6) Exit game mode
helper.exit_game_mode(Tests.exit_game_mode)
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(ForceRegion_SplineRegionWithModifiedTransform)
@@ -0,0 +1,82 @@
"""
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 : C12905528
Test Case Title : Check that user is warned if non-trigger collider component is used with force region
"""
# fmt: off
class Tests():
create_test_entity = ("Entity created successfully", "Failed to create Entity")
add_physx_force_region = ("PhysX Force Region component added", "Failed to add PhysX Force Region component")
add_physx_collider = ("PhysX Collider component added", "Failed to add PhysX Collider component")
warnings_found = ("Warnings found in logs", "No warnings found in logs")
# fmt: on
def ForceRegion_WithNonTriggerColliderWarning():
"""
Summary:
Create entity with PhysX Force Region component. Check that user is warned if new PhysX Collider component is
added to Entity.
Expected Behavior:
User is warned by message in the console that the PhysX Collider component was not marked as a trigger
Test Steps:
1) Load the empty level
2) Create test entity
3) Add PhysX Force Region component
4) Start the Tracer to catch any errors and warnings
5) Add PhysX Collider component to the Entity
6) Verify there is warning in the logs
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
"""
import azlmbr.legacy.general as general
from editor_python_test_tools.editor_entity_utils import EditorEntity
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
from editor_python_test_tools.utils import Tracer
helper.init_idle()
# 1) Load the empty level
helper.open_level("Physics", "Base")
# 2) Create test entity
test_entity = EditorEntity.create_editor_entity("TestEntity")
Report.result(Tests.create_test_entity, test_entity.id.IsValid())
# 3) Add PhysX Force Region component
test_entity.add_component("PhysX Force Region")
Report.result(Tests.add_physx_force_region, test_entity.has_component("PhysX Force Region"))
# 4) Start the Tracer to catch any errors and warnings
Report.info("Starting warning monitoring")
with Tracer() as section_tracer:
# 5) Add the PhysX Collider component
test_entity.add_component("PhysX Collider")
Report.result(Tests.add_physx_collider, test_entity.has_component("PhysX Collider"))
general.idle_wait_frames(1)
Report.info("Ending warning monitoring")
# ) Verify there is warning in the logs
success_condition = section_tracer.has_warnings
# Checking if warning exist and the exact warning is caught in the expected lines in Test file
Report.result(Tests.warnings_found, success_condition)
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(ForceRegion_WithNonTriggerColliderWarning)
@@ -0,0 +1,182 @@
"""
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 : C5932040
# Test Case Title : Check that force region exerts world space force on rigid bodies
# fmt: off
class Tests():
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
find_ball = ("Ball entity found", "Ball entity not found")
find_box = ("Box entity found", "Box entity not found")
gravity_works = ("Ball fell", "Ball did not fall")
ball_triggers_force_region = ("Ball triggered force region", "Ball did not trigger force region")
net_force_magnitude = ("The net force magnitude on the ball is close to expected value", "The net force magnitude on the ball is not close to expected value")
ball_moved_up = ("Ball moved up", "Ball did not move up before timeout occurred")
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
# fmt: on
def is_close_float(a, b, factor):
return abs(b - a) < factor
def ForceRegion_WorldSpaceForceOnRigidBodies():
"""
Summary:
A ball is suspended over a box shaped force region.
The ball drops and is forced upward by the world space force of the force region
Level Description:
Ball (entity) - Sphere shaped Mesh; Sphere shaped PhysX Collider; PhysX Rigid Body
ForceRegion (entity) - Cube shaped Mesh; Cube shaped PhysX Collider; PhysX Force Region with world space force
Expected Behavior:
The level opens and enters game mode. At this time the ball will fall towards the force region.
When it collides, it will be launched upwards. Then the game mode will exit and the editor will close.
Test Steps:
1) Open level
2) Enter game mode
3) Find the entities
4) Get starting position of the ball
5) Check that gravity works and ball falls
6) Check that the ball enters the trigger area of force region
7) Get the magnitude of the collision
8) Check that the ball moved up
9) Verify that the magnitude of the collision is as expected
10) Exit game mode
11) Close the 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.
: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
class Ball:
start_position_z = None
fell = False
# Constants
TIMEOUT = 2.0
BALL_MIN_MOVED_UP = 5 # Minimum amount to indicate Z movement
MAGNITUDE_TOLERANCE = 0.26 # Force region magnitude tolerance
helper.init_idle()
# 1) Open level
helper.open_level("Physics", "ForceRegion_WorldSpaceForceOnRigidBodies")
# 2) Enter game mode
helper.enter_game_mode(Tests.enter_game_mode)
# 3) Retrieve entities
ball_id = general.find_game_entity("Ball")
Report.critical_result(Tests.find_ball, ball_id.IsValid())
box_id = general.find_game_entity("ForceRegion")
Report.critical_result(Tests.find_box, box_id.IsValid())
# 4) Get the starting z position of the ball
Ball.start_position_z = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldZ", ball_id)
Report.info("Starting Height of the ball: {}".format(Ball.start_position_z))
# 5) Check that gravity works and the ball falls
def ball_falls():
if not Ball.fell:
ball_after_frame_z = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldZ", ball_id)
if (ball_after_frame_z - Ball.start_position_z) < 0.0:
Report.info("Ball position is now lower than the starting position")
Ball.fell = True
return Ball.fell
helper.wait_for_condition(ball_falls, TIMEOUT)
Report.result(Tests.gravity_works, Ball.fell)
# 6) Check that the ball enters the trigger area
class ForceRegionTrigger:
entered = False
exited = False
def on_enter(args):
other_id = args[0]
if other_id.Equal(ball_id):
Report.info("Trigger entered")
ForceRegionTrigger.entered = True
def on_exit(args):
other_id = args[0]
if other_id.Equal(ball_id):
Report.info("Trigger exited")
ForceRegionTrigger.exited = True
handler = azlmbr.physics.TriggerNotificationBusHandler()
handler.connect(box_id)
handler.add_callback("OnTriggerEnter", on_enter)
handler.add_callback("OnTriggerExit", on_exit)
helper.wait_for_condition(lambda: ForceRegionTrigger.entered, TIMEOUT)
Report.result(Tests.ball_triggers_force_region, ForceRegionTrigger.entered)
# 7) Get the magnitude of the collision
class NetForceMagnitude:
value = 0
def on_calc_net_force(args):
"""
args[0] - force region entity
args[1] - entity entering
args[2] - vector
args[3] - magnitude
"""
force_magnitude = args[3]
NetForceMagnitude.value = force_magnitude
force_notification_handler = azlmbr.physics.ForceRegionNotificationBusHandler()
force_notification_handler.connect(None)
force_notification_handler.add_callback("OnCalculateNetForce", on_calc_net_force)
def ball_moved_up():
ball_z = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldZ", ball_id)
return ball_z > Ball.start_position_z + BALL_MIN_MOVED_UP
# 8) Check that the ball moved up
if helper.wait_for_condition(lambda: ball_moved_up and ForceRegionTrigger.exited, TIMEOUT):
Report.success(Tests.ball_moved_up)
else:
Report.failure(Tests.ball_moved_up)
# 9) Verify that the magnitude of the collision is as expected
force_region_magnitude = azlmbr.physics.ForceWorldSpaceRequestBus(azlmbr.bus.Event, "GetMagnitude", box_id)
Report.info(
"NetForce magnitude is {}, Force Region magnitude is {}".format(NetForceMagnitude.value, force_region_magnitude)
)
Report.result(
Tests.net_force_magnitude, is_close_float(NetForceMagnitude.value, force_region_magnitude, MAGNITUDE_TOLERANCE)
)
# 10) Exit game mode
helper.exit_game_mode(Tests.exit_game_mode)
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(ForceRegion_WorldSpaceForceOnRigidBodies)
@@ -0,0 +1,241 @@
"""
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 : C6090552
Test Case Title : Check that force region exerts linear damping force on rigid bodies (negative test)
"""
# fmt: off
class Tests:
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
ball_exists = ("The Ball entity was found", "The Ball entity was not found")
forceregion_exists = ("The ForceRegion entity was found", "The ForceRegion entity was not found")
terrain_exists = ("The Terrain entity was found", "The Terrain entity was not found")
ball_over_force_region = ("The ball is over the force region", "The ball is not over the force region")
gravity_enabled_on_ball = ("The ball has gravity enabled", "The ball gravity failed to enable")
ball_entered_force_region = ("The ball entered the force region", "The ball did not enter the force region before timeout")
ball_fell_through_region = ("The ball fell through the force region", "The ball did not exit force region below enter location")
enter_exit_distance_close = ("The ball has passed through the entire force region", "Ball did not move the height of the force region")
ball_hit_ground = ("The ball collided with the PhysX Terrain", "The ball did not collide with the PhysX Terrain before timeout")
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
# fmt: on
def ForceRegion_ZeroLinearDampingDoesNothing():
"""
Summary:
A ball is suspended over a force region with a linear damping value of zero and a PhysX Terrain.
Level Description:
Ball - (entity): Mesh: Sphere shaped
PhysX Rigid Body: Gravity is enabled; Default settings
PhysX Collider: Sphere shape; offset (0.0, 0.0, 0.5); draw collider checked
ForceRegion - (entity): Box Shape: Game view checked; Shape dimensions (3.0, 3.0, 3.0)
PhysX Force Region: Linear damping force: 0.0
PhysX Collider: Box shape; Box dimensions (3.0, 3.0, 3.0); Trigger checked;
Draw collider checked
Terrain - (entity): PhysX Terrain: Default settings
Expected Behavior:
When game mode is entered, the ball will fall down toward the force region. It will enter the region and fall
straight through as if the region did not exist because the linear damping is set to zero. It will then
collide with the PhysX Terrain.
Test Steps
1) Open level
2) Enter game mode
3) Find/setup entities and handlers
4) Check that the ball is over the force region
5) Wait for ball to hit the ground
6) Report if the ball entered the force region, fell through it, and collided with terrain in the alloted time
7) Exit game mode and close the 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.
:return: None
"""
import os
import sys
import azlmbr.legacy.general as general
import azlmbr.bus
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
helper.init_idle()
TIMEOUT_SECONDS = 3.0
X_Y_Z_TOLERANCE = 1.5
REGION_HEIGHT = 3.0
TERRAIN_HEIGHT = 32.0
def is_close(value1, value2, tolerance):
"""
() -> bool
Absolute value of difference of values being less than or equal to the tolerance
"""
return abs(value1 - value2) <= tolerance
def is_at_least(value1, value2, minimum):
"""
() -> bool
Subtraction of value2 from value1 being greater than or equal to the minimum
"""
return (value1 - value2) >= minimum
class Entity(object):
def __init__(self, name):
self.name = name
self.id = general.find_game_entity(self.name)
Report.critical_result(Tests.__dict__[self.name.lower() + "_exists"], self.id.IsValid())
def get_location(self):
# () -> Vector3
return azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", self.id)
class Ball(Entity):
def __init__(self):
super(Ball, self).__init__("Ball")
self.location = self.get_location()
self.enter_region_location = None
self.exit_region_location = None
self.enable_gravity()
self.check_gravity_is_on()
def enable_gravity(self):
azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "SetGravityEnabled", self.id)
def check_gravity_is_on(self):
gravity_status = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "IsGravityEnabled", self.id)
Report.critical_result(Tests.__dict__["gravity_enabled_on_" + self.name.lower()], gravity_status)
def check_alignment(self, region):
# () -> bool
Report.info_vector3(self.location, "Location of Ball: ")
Report.info_vector3(region.location, "Location of ForceRegion: ")
aligned = True
if not is_close(self.location.x, region.location.x, X_Y_Z_TOLERANCE):
aligned = False
Report.info("The ball is not aligned in the X axis.")
if not is_close(self.location.y, region.location.y, X_Y_Z_TOLERANCE):
aligned = False
Report.info("The ball is not aligned in the Y axis.")
if (self.location.z - region.location.z) < X_Y_Z_TOLERANCE:
aligned = False
Report.info("The ball is not high enough to enter the force region.")
if self.location.z < TERRAIN_HEIGHT:
aligned = False
Report.info("The ball is below the terrain.")
return aligned
def did_ball_fall_in_region_as_expected(self):
# () -> bool
if not self.exit_region_location:
Report.info("The ball did not exit the force region in time.")
return False
return (self.enter_region_location.z - self.exit_region_location.z) > X_Y_Z_TOLERANCE
def check_enter_vs_exit_locations(self):
"""
Check that the ball enter location is above the exit location (ball successfully fell through the force
region) and check that the distance between the enter/exit locations is at least the height of the region
NOTE: The ball will "enter" the force region when the bottom edge of its collision box enters the region and
will "exit" when it is completely outside the force region (top edge leaves region)
"""
Report.info_vector3(self.enter_region_location, "Ball entering location: ")
Report.info_vector3(self.exit_region_location, "Ball exiting location: ")
Report.result(Tests.ball_fell_through_region, self.did_ball_fall_in_region_as_expected())
Report.result(
Tests.enter_exit_distance_close,
is_at_least(self.enter_region_location.z, self.exit_region_location.z, REGION_HEIGHT),
)
class ForceRegion(Entity):
def __init__(self, ball):
super(ForceRegion, self).__init__("ForceRegion")
self.ball = ball # This creates a reference point to access the ball without looking in global scope
self.location = self.get_location()
self.ball_entered_trigger_area = False
self.trigger_handler = azlmbr.physics.TriggerNotificationBusHandler()
self.connect_trigger_handler()
def on_trigger_enter(self, args):
other_id = args[0]
if other_id.Equal(self.ball.id):
Report.info("Ball has entered the force region trigger area.")
self.ball_entered_trigger_area = True
self.ball.enter_region_location = self.ball.get_location()
def on_trigger_exit(self, args):
other_id = args[0]
if other_id.Equal(self.ball.id):
Report.info("Ball has exited the force region trigger area.")
self.ball.exit_region_location = self.ball.get_location()
def connect_trigger_handler(self):
self.trigger_handler.connect(self.id)
self.trigger_handler.add_callback("OnTriggerEnter", self.on_trigger_enter)
self.trigger_handler.add_callback("OnTriggerExit", self.on_trigger_exit)
class Terrain(Entity):
def __init__(self, ball):
super(Terrain, self).__init__("Terrain")
self.ball = ball # This creates a reference point to access the ball without looking in global scope
self.ball_collided = False
self.collision_handler = azlmbr.physics.CollisionNotificationBusHandler()
self.connect_collision_handler()
def on_collision_begin(self, args):
other_id = args[0]
if other_id.Equal(self.ball.id):
Report.info("Ball struck PhysX Terrain")
self.ball_collided = True
def connect_collision_handler(self):
self.collision_handler.connect(self.id)
self.collision_handler.add_callback("OnCollisionBegin", self.on_collision_begin)
# 1) Open level
helper.open_level("Physics", "ForceRegion_ZeroLinearDampingDoesNothing")
# 2) Enter game mode
helper.enter_game_mode(Tests.enter_game_mode)
# 3) Find/setup entities and handlers
ball = Ball()
region = ForceRegion(ball)
terrain = Terrain(ball)
# 4) Check that the ball is over the force region
Report.critical_result(Tests.ball_over_force_region, ball.check_alignment(region))
# 5) Wait for ball to hit the ground
helper.wait_for_condition(lambda: terrain.ball_collided, TIMEOUT_SECONDS)
# 6) Report if the ball entered the force region, fell through it, and collided with terrain in the alloted time
Report.result(Tests.ball_entered_force_region, region.ball_entered_trigger_area)
ball.check_enter_vs_exit_locations()
Report.result(Tests.ball_hit_ground, terrain.ball_collided)
# 7) Exit game mode and close the editor
helper.exit_game_mode(Tests.exit_game_mode)
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(ForceRegion_ZeroLinearDampingDoesNothing)
@@ -0,0 +1,242 @@
"""
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 : C6090551
Test Case Title : Check that force region exerts local space force on rigid bodies (negative test)
"""
# fmt: off
class Tests:
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
ball_exists = ("The Ball entity was found", "The Ball entity was not found")
forceregion_exists = ("The ForceRegion entity was found", "The ForceRegion entity was not found")
terrain_exists = ("The Terrain entity was found", "The Terrain entity was not found")
ball_over_force_region = ("The ball is over the force region", "The ball is not over the force region")
gravity_enabled_on_ball = ("The ball has gravity enabled", "The ball gravity failed to enable")
ball_entered_force_region = ("The ball entered the force region", "The ball did not enter the force region before timeout")
ball_fell_through_region = ("The ball fell through the force region", "The ball did not exit force region below enter location")
enter_exit_distance_close = ("The ball has passed through the entire force region", "Ball did not move the height of the force region")
ball_hit_ground = ("The ball collided with the PhysX Terrain", "The ball did not collide with the PhysX Terrain before timeout")
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
# fmt: on
def ForceRegion_ZeroLocalSpaceForceDoesNothing():
"""
Summary:
A ball is suspended over a box shaped force region and a PhysX Terrain. The force is a local space force
pointed in the positive Z direction with a magnitude of 10.
Level Description:
Ball - (entity): Mesh: Sphere shaped
PhysX Rigid Body: Gravity is enabled; Default settings
PhysX Collider: Sphere shape; offset (0.0, 0.0, 0.5); draw collider checked
ForceRegion - (entity): Box Shape: Game view checked; Shape dimensions (3.0, 3.0, 3.0)
PhysX Force Region: Local Space force; direction (0.0, 0.0, 1.0); magnitude 10.0
PhysX Collider: Box shape; Box dimensions (3.0, 3.0, 3.0); Trigger checked;
Draw collider checked
Terrain - (entity): PhysX Terrain: Default settings
Expected Behavior:
When game mode is entered, the ball entity will fall due to gravity and enter the force region.
The region has a very weak force and therefore the ball will fall through the force region,
where it will then collide with the PhysX Terrain.
Test Steps:
1) Open level
2) Enter game mode
3) Find/setup entities and handlers
4) Check that the ball is over the force region
5) Wait for ball to hit the ground
6) Report if the ball entered the force region, fell through it, and collided with terrain in the alloted time
7) Exit game mode and close the 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.
:return: None
"""
import os
import sys
import azlmbr.legacy.general as general
import azlmbr.bus
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
helper.init_idle()
TIMEOUT_SECONDS = 3.0
X_Y_Z_TOLERANCE = 1.5
REGION_HEIGHT = 3.0
TERRAIN_HEIGHT = 32.0
def is_close(value1, value2, tolerance):
"""
() -> bool
Absolute value of difference of values being less than or equal to the tolerance
"""
return abs(value1 - value2) <= tolerance
def is_at_least(value1, value2, minimum):
"""
() -> bool
Subtraction of value2 from value1 being greater than or equal to the minimum
"""
return (value1 - value2) >= minimum
class Entity(object):
def __init__(self, name):
self.name = name
self.id = general.find_game_entity(self.name)
Report.critical_result(Tests.__dict__[self.name.lower() + "_exists"], self.id.IsValid())
def get_location(self):
# () -> Vector3
return azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", self.id)
class Ball(Entity):
def __init__(self):
super(Ball, self).__init__("Ball")
self.location = self.get_location()
self.enter_region_location = None
self.exit_region_location = None
self.enable_gravity()
self.check_gravity_is_on()
def enable_gravity(self):
azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "SetGravityEnabled", self.id)
def check_gravity_is_on(self):
gravity_status = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "IsGravityEnabled", self.id)
Report.critical_result(Tests.__dict__["gravity_enabled_on_" + self.name.lower()], gravity_status)
def check_alignment(self, region):
# () -> bool
Report.info_vector3(self.location, "Location of Ball: ")
Report.info_vector3(region.location, "Location of ForceRegion: ")
aligned = True
if not is_close(self.location.x, region.location.x, X_Y_Z_TOLERANCE):
aligned = False
Report.info("The ball is not aligned in the X axis.")
if not is_close(self.location.y, region.location.y, X_Y_Z_TOLERANCE):
aligned = False
Report.info("The ball is not aligned in the Y axis.")
if (self.location.z - region.location.z) < X_Y_Z_TOLERANCE:
aligned = False
Report.info("The ball is not high enough to enter the force region.")
if self.location.z < TERRAIN_HEIGHT:
aligned = False
Report.info("The ball is below the terrain.")
return aligned
def did_ball_fall_in_region_as_expected(self):
# () -> bool
if not self.exit_region_location:
Report.info("The ball did not exit the force region in time.")
return False
return (self.enter_region_location.z - self.exit_region_location.z) > X_Y_Z_TOLERANCE
def check_enter_vs_exit_locations(self):
"""
Check that the ball enter location is above the exit location (ball successfully fell through the force
region) and check that the distance between the enter/exit locations is at least the height of the region
NOTE: The ball will "enter" the force region when the bottom edge of its collision box enters the region and
will "exit" when it is completely outside the force region (top edge leaves region)
"""
Report.info_vector3(self.enter_region_location, "Ball entering location: ")
Report.info_vector3(self.exit_region_location, "Ball exiting location: ")
Report.result(Tests.ball_fell_through_region, self.did_ball_fall_in_region_as_expected())
Report.result(
Tests.enter_exit_distance_close,
is_at_least(self.enter_region_location.z, self.exit_region_location.z, REGION_HEIGHT),
)
class ForceRegion(Entity):
def __init__(self, ball):
super(ForceRegion, self).__init__("ForceRegion")
self.ball = ball # This creates a reference point to access the ball without looking in global scope
self.location = self.get_location()
self.ball_entered_trigger_area = False
self.trigger_handler = azlmbr.physics.TriggerNotificationBusHandler()
self.connect_trigger_handler()
def on_trigger_enter(self, args):
other_id = args[0]
if other_id.Equal(self.ball.id):
Report.info("Ball has entered the force region trigger area.")
self.ball_entered_trigger_area = True
self.ball.enter_region_location = self.ball.get_location()
def on_trigger_exit(self, args):
other_id = args[0]
if other_id.Equal(self.ball.id):
Report.info("Ball has exited the force region trigger area.")
self.ball.exit_region_location = self.ball.get_location()
def connect_trigger_handler(self):
self.trigger_handler.connect(self.id)
self.trigger_handler.add_callback("OnTriggerEnter", self.on_trigger_enter)
self.trigger_handler.add_callback("OnTriggerExit", self.on_trigger_exit)
class Terrain(Entity):
def __init__(self, ball):
super(Terrain, self).__init__("Terrain")
self.ball = ball # This creates a reference point to access the ball without looking in global scope
self.ball_collided = False
self.collision_handler = azlmbr.physics.CollisionNotificationBusHandler()
self.connect_collision_handler()
def on_collision_begin(self, args):
other_id = args[0]
if other_id.Equal(self.ball.id):
Report.info("Ball struck PhysX Terrain")
self.ball_collided = True
def connect_collision_handler(self):
self.collision_handler.connect(self.id)
self.collision_handler.add_callback("OnCollisionBegin", self.on_collision_begin)
# 1) Open level
helper.open_level("Physics", "ForceRegion_ZeroLocalSpaceForceDoesNothing")
# 2) Enter game mode
helper.enter_game_mode(Tests.enter_game_mode)
# 3) Find/setup entities and handlers
ball = Ball()
region = ForceRegion(ball)
terrain = Terrain(ball)
# 4) Check that the ball is over the force region
Report.critical_result(Tests.ball_over_force_region, ball.check_alignment(region))
# 5) Wait for ball to hit the ground
helper.wait_for_condition(lambda: terrain.ball_collided, TIMEOUT_SECONDS)
# 6) Report if the ball entered the force region, fell through it, and collided with terrain in the alloted time
Report.result(Tests.ball_entered_force_region, region.ball_entered_trigger_area)
ball.check_enter_vs_exit_locations()
Report.result(Tests.ball_hit_ground, terrain.ball_collided)
# 7) Exit game mode and close the editor
helper.exit_game_mode(Tests.exit_game_mode)
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(ForceRegion_ZeroLocalSpaceForceDoesNothing)
@@ -0,0 +1,242 @@
"""
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 : C6090554
Test Case Title : Check that force region exerts point force on rigid bodies (negative test)
"""
# fmt: off
class Tests:
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
ball_exists = ("The Ball entity was found", "The Ball entity was not found")
forceregion_exists = ("The ForceRegion entity was found", "The ForceRegion entity was not found")
terrain_exists = ("The Terrain entity was found", "The Terrain entity was not found")
ball_over_force_region = ("The ball is over the force region", "The ball is not over the force region")
gravity_enabled_on_ball = ("The ball has gravity enabled", "The ball gravity failed to enable")
ball_entered_force_region = ("The ball entered the force region", "The ball did not enter the force region before timeout")
ball_fell_through_region = ("The ball fell through the force region", "The ball did not exit force region below enter location")
enter_exit_distance_close = ("The ball has passed through the entire force region", "Ball did not move the height of the force region")
ball_hit_ground = ("The ball collided with the PhysX Terrain", "The ball did not collide with the PhysX Terrain before timeout")
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
# fmt: on
def ForceRegion_ZeroPointForceDoesNothing():
"""
Summary:
A ball is suspended over a box shaped force region and a PhysX Terrain. The force is a point force
pointed outward from center with a magnitude of 10.
Level Description:
Ball - (entity): Mesh: Sphere shaped
PhysX Rigid Body: Gravity is enabled; Default settings
PhysX Collider: Sphere shape; offset (0.0, 0.0, 0.5); draw collider checked
ForceRegion - (entity): Box Shape: Game view checked; Shape dimensions (3.0, 3.0, 3.0)
PhysX Force Region: Point force; magnitude 10.0
PhysX Collider: Box shape; Box dimensions (3.0, 3.0, 3.0); Trigger checked;
Draw collider checked
Terrain - (entity): PhysX Terrain: Default settings
Expected Behavior:
When game mode is entered, the ball entity will fall due to gravity and enter the force region.
The region has a very weak force and therefore the ball will fall through the force region,
where it will then collide with the PhysX Terrain.
Test Steps:
1) Open level
2) Enter game mode
3) Find/setup entities and handlers
4) Check that the ball is over the force region
5) Wait for ball to hit the ground
6) Report if the ball entered the force region, fell through it, and collided with terrain in the alloted time
7) Exit game mode and close the 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.
:return: None
"""
import os
import sys
import azlmbr.legacy.general as general
import azlmbr.bus
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
helper.init_idle()
TIMEOUT_SECONDS = 3.0
X_Y_Z_TOLERANCE = 1.5
REGION_HEIGHT = 3.0
TERRAIN_HEIGHT = 32.0
def is_close(value1, value2, tolerance):
"""
() -> bool
Absolute value of difference of values being less than or equal to the tolerance
"""
return abs(value1 - value2) <= tolerance
def is_at_least(value1, value2, minimum):
"""
() -> bool
Subtraction of value2 from value1 being greater than or equal to the minimum
"""
return (value1 - value2) >= minimum
class Entity(object):
def __init__(self, name):
self.name = name
self.id = general.find_game_entity(self.name)
Report.critical_result(Tests.__dict__[self.name.lower() + "_exists"], self.id.IsValid())
def get_location(self):
# () -> Vector3
return azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", self.id)
class Ball(Entity):
def __init__(self):
super(Ball, self).__init__("Ball")
self.location = self.get_location()
self.enter_region_location = None
self.exit_region_location = None
self.enable_gravity()
self.check_gravity_is_on()
def enable_gravity(self):
azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "SetGravityEnabled", self.id)
def check_gravity_is_on(self):
gravity_status = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "IsGravityEnabled", self.id)
Report.critical_result(Tests.__dict__["gravity_enabled_on_" + self.name.lower()], gravity_status)
def check_alignment(self, region):
# () -> bool
Report.info_vector3(self.location, "Location of Ball: ")
Report.info_vector3(region.location, "Location of ForceRegion: ")
aligned = True
if not is_close(self.location.x, region.location.x, X_Y_Z_TOLERANCE):
aligned = False
Report.info("The ball is not aligned in the X axis.")
if not is_close(self.location.y, region.location.y, X_Y_Z_TOLERANCE):
aligned = False
Report.info("The ball is not aligned in the Y axis.")
if (self.location.z - region.location.z) < X_Y_Z_TOLERANCE:
aligned = False
Report.info("The ball is not high enough to enter the force region.")
if self.location.z < TERRAIN_HEIGHT:
aligned = False
Report.info("The ball is below the terrain.")
return aligned
def did_ball_fall_in_region_as_expected(self):
# () -> bool
if not self.exit_region_location:
Report.info("The ball did not exit the force region in time.")
return False
return (self.enter_region_location.z - self.exit_region_location.z) > X_Y_Z_TOLERANCE
def check_enter_vs_exit_locations(self):
"""
Check that the ball enter location is above the exit location (ball successfully fell through the force
region) and check that the distance between the enter/exit locations is at least the height of the region
NOTE: The ball will "enter" the force region when the bottom edge of its collision box enters the region and
will "exit" when it is completely outside the force region (top edge leaves region)
"""
Report.info_vector3(self.enter_region_location, "Ball entering location: ")
Report.info_vector3(self.exit_region_location, "Ball exiting location: ")
Report.result(Tests.ball_fell_through_region, self.did_ball_fall_in_region_as_expected())
Report.result(
Tests.enter_exit_distance_close,
is_at_least(self.enter_region_location.z, self.exit_region_location.z, REGION_HEIGHT),
)
class ForceRegion(Entity):
def __init__(self, ball):
super(ForceRegion, self).__init__("ForceRegion")
self.ball = ball # This creates a reference point to access the ball without looking in global scope
self.location = self.get_location()
self.ball_entered_trigger_area = False
self.trigger_handler = azlmbr.physics.TriggerNotificationBusHandler()
self.connect_trigger_handler()
def on_trigger_enter(self, args):
other_id = args[0]
if other_id.Equal(self.ball.id):
Report.info("Ball has entered the force region trigger area.")
self.ball_entered_trigger_area = True
self.ball.enter_region_location = self.ball.get_location()
def on_trigger_exit(self, args):
other_id = args[0]
if other_id.Equal(self.ball.id):
Report.info("Ball has exited the force region trigger area.")
self.ball.exit_region_location = self.ball.get_location()
def connect_trigger_handler(self):
self.trigger_handler.connect(self.id)
self.trigger_handler.add_callback("OnTriggerEnter", self.on_trigger_enter)
self.trigger_handler.add_callback("OnTriggerExit", self.on_trigger_exit)
class Terrain(Entity):
def __init__(self, ball):
super(Terrain, self).__init__("Terrain")
self.ball = ball # This creates a reference point to access the ball without looking in global scope
self.ball_collided = False
self.collision_handler = azlmbr.physics.CollisionNotificationBusHandler()
self.connect_collision_handler()
def on_collision_begin(self, args):
other_id = args[0]
if other_id.Equal(self.ball.id):
Report.info("Ball struck PhysX Terrain")
self.ball_collided = True
def connect_collision_handler(self):
self.collision_handler.connect(self.id)
self.collision_handler.add_callback("OnCollisionBegin", self.on_collision_begin)
# 1) Open level
helper.open_level("Physics", "ForceRegion_ZeroPointForceDoesNothing")
# 2) Enter game mode
helper.enter_game_mode(Tests.enter_game_mode)
# 3) Find/setup entities and handlers
ball = Ball()
region = ForceRegion(ball)
terrain = Terrain(ball)
# 4) Check that the ball is over the force region
Report.critical_result(Tests.ball_over_force_region, ball.check_alignment(region))
# 5) Wait for ball to hit the ground
helper.wait_for_condition(lambda: terrain.ball_collided, TIMEOUT_SECONDS)
# 6) Report if the ball entered the force region, fell through it, and collided with terrain in the alloted time
Report.result(Tests.ball_entered_force_region, region.ball_entered_trigger_area)
ball.check_enter_vs_exit_locations()
Report.result(Tests.ball_hit_ground, terrain.ball_collided)
# 7) Exit game mode and close the editor
helper.exit_game_mode(Tests.exit_game_mode)
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(ForceRegion_ZeroPointForceDoesNothing)
@@ -0,0 +1,173 @@
"""
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 : C6090553
# Test Case Title : Check that force region exerts simple drag force on rigid bodies (negative test)
# fmt: off
class Tests():
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
find_ball = ("Ball found", "Ball not found")
find_force_region = ("Force Region found", "Force Region not found")
ball_gravity_disabled = ("Ball gravity disabled", "Ball gravity not disabled")
ball_fell = ("The ball fell", "The ball did not fall")
ball_enters_force_region = ("Ball entered force region", "Ball did not enter force region")
ball_exits_force_region = ("Ball exited force region", "Ball did not exit force region")
force_region_slows_ball = ("Force Region did not slow ball", "Force Region slows ball")
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
# fmt: on
def ForceRegion_ZeroSimpleDragForceDoesNothing():
"""
Summary:
Runs an automated test to ensure that force region exerts simple drag force on rigid bodies(negative test).
Level Description:
Ball (entity) - contains a sphere mesh, PhysX Collider (sphere shape) and PhysX RigidBody. Ball is
placed above force region
Force Region (entity) - contains Physx Force Region with Simple Drag force with Region Density as 0 and
PhysX Collider (box shape)
Expected Behavior:
When game mode is entered, Sphere falls through force region as though force region doesn't exist because
region density for the simple drag force is zero and has no effect on the dropping sphere.
Test Steps:
1) Open level
2) Enter Game mode
3) Validate the entities in the scene
4) Check ball gravity is disabled or not
5) Get initial velocity of ball
6) Wait for ball to enter force region
7) Gets z velocity and position of ball
8) Wait for ball to exit force region
9) Gets new velocity and position of ball
10) Check that the ball does not slow due to the force region
11) Exits game mode and 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.
:return: None
"""
import os
import sys
import azlmbr.legacy.general as general
import azlmbr.bus
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
# Holds details about the ball
class Ball:
id = None
initial_velocity_z = 0.0
start_velocity_z = 0.0
end_velocity_z = 0.0
ball_start_z_position = 0.0
ball_end_z_position = 0.0
entered_force_region = False
exited_force_region = False
ball_fell_down = False
ball_slows = False
def on_trigger_enter(args):
other_id = args[0]
if other_id.Equal(Ball.id):
Ball.entered_force_region = True
def on_trigger_exit(args):
other_id = args[0]
if other_id.Equal(Ball.id):
Ball.exited_force_region = True
# Constants
TIMEOUT = 2.0
CLOSE_ENOUGH_THRESHOLD = 0.01
helper.init_idle()
# 1) Open level
helper.open_level("Physics", "ForceRegion_ZeroSimpleDragForceDoesNothing")
# 2) Enter game mode
helper.enter_game_mode(Tests.enter_game_mode)
# 3) Validate the entities in the scene
Ball.id = general.find_game_entity("Sphere")
Report.critical_result(Tests.find_ball, Ball.id.IsValid())
force_region_id = general.find_game_entity("Force Region")
Report.critical_result(Tests.find_force_region, force_region_id.IsValid())
# 4) Check ball gravity is disabled or not
gravity_enabled = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "IsGravityEnabled", Ball.id)
Report.critical_result(Tests.ball_gravity_disabled, not gravity_enabled)
# 5) Get initial velocity of ball
# Ball linear velocity is set at (0, 0, -5) in the level
Ball.initial_velocity_z = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetLinearVelocity", Ball.id).z
Report.info("Ball initial velocity = {}".format(Ball.initial_velocity_z))
# 6) Wait for ball to enter force region
handler = azlmbr.physics.TriggerNotificationBusHandler()
handler.connect(force_region_id)
handler.add_callback("OnTriggerEnter", on_trigger_enter)
handler.add_callback("OnTriggerExit", on_trigger_exit)
helper.wait_for_condition(lambda: Ball.entered_force_region, TIMEOUT)
Report.critical_result(Tests.ball_enters_force_region, Ball.entered_force_region)
# 7) Gets z velocity and position of ball
Ball.start_velocity_z = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetLinearVelocity", Ball.id).z
Ball.ball_start_z_position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldZ", Ball.id)
Report.info(
"Ball Start Z position = {} Ball Start Z Velocity = {}".format(
Ball.ball_start_z_position, Ball.start_velocity_z
)
)
# 8) Wait for ball to exit force region
helper.wait_for_condition(lambda: Ball.exited_force_region, TIMEOUT)
Report.critical_result(Tests.ball_exits_force_region, Ball.exited_force_region)
# 9) Gets new velocity and position of ball
Ball.end_velocity_z = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetLinearVelocity", Ball.id).z
Ball.ball_end_z_position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldZ", Ball.id)
Report.info(
"Ball End Z position = {} Ball End Z Velocity = {}".format(Ball.ball_end_z_position, Ball.end_velocity_z)
)
# 10) Check that the ball does not slow due to the force region
# Check ball fell down or not
if (Ball.ball_end_z_position - CLOSE_ENOUGH_THRESHOLD) < Ball.ball_start_z_position:
Ball.ball_fell_down = True
Report.critical_result(Tests.ball_fell, Ball.ball_fell_down)
# Ball initial velocity is -5.0. Check that the ball does not slow down in force region
if ((Ball.end_velocity_z - Ball.initial_velocity_z) < CLOSE_ENOUGH_THRESHOLD) and (
(Ball.start_velocity_z - Ball.initial_velocity_z) < CLOSE_ENOUGH_THRESHOLD
):
Ball.ball_slows = True
Report.critical_result(Tests.force_region_slows_ball, Ball.ball_slows)
# 11) Exits game mode and editor
helper.exit_game_mode(Tests.exit_game_mode)
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(ForceRegion_ZeroSimpleDragForceDoesNothing)
@@ -0,0 +1,175 @@
"""
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 : C6090555
# Test Case Title : Check that force region exerts spline follow force on rigid bodies(negative test)
# fmt: off
class Tests():
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
find_sphere = ("Sphere entity found", "Sphere entity not found")
find_force_region = ("Force region found", "Force region not found")
find_triggers = ("All triggers are found", "All triggers are not found")
sphere_fell = ("The sphere fell", "The sphere did not fall")
sphere_entered_force_region = ("The sphere entered the force region", "The sphere did not enter the force region before timeout")
sphere_exited_force_region = ("The sphere exited the force region", "The sphere did not exit the force region before timeout")
sphere_drops_force_region = ("Sphere drops through the force region", "Sphere did not drop through the force region due to spline force")
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
# fmt: on
def ForceRegion_ZeroSplineForceDoesNothing():
"""
Summary:
Runs an automated test to ensure that a PhysX force region exerts spline follow force on rigid bodies(negative test)
Level Description:
A sphere entity is positioned above a force region entity
The sphere has a PhysX collider (sphere) and rigidbody component with default values
The force region entity has a PhysX collider (Box) and force region with a 'spline follow' force.
It also has a spline component with 4 nodes. Each node is connected linearly in the following pattern:
___[0]
[1] ___
___ [2]
[3]
There are 4 trigger entities, each with a PhysX collider (sphere). They are positioned at each node.
Expected Behavior:
The Sphere drops through the force region as though spline follow force does not exist.
Test Steps:
1) Open level
2) Enter game mode
3) Retrieve and Validate entities
4) Get position of spline and sphere
5) Wait till the sphere drops
6) Get z position of sphere when it enters and exits from trigger area
7) Verify sphere drops through force region without spline force effect
8) Exit game mode
9) Close the 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.
: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
import azlmbr.legacy.general as general
import azlmbr.bus
# Constants
TIMEOUT = 3
CLOSE_ENOUGH = 0.001
class Sphere:
id = None
start_position_z = None
fell = False
entered_force_region = False
exited_force_region = False
def on_trigger_enter(args):
other_id = args[0]
if other_id.Equal(Sphere.id):
Sphere.entered_force_region = True
def on_trigger_exit(args):
other_id = args[0]
if other_id.Equal(Sphere.id):
Sphere.exited_force_region = True
helper.init_idle()
# 1) Open level
helper.open_level("Physics", "ForceRegion_ZeroSplineForceDoesNothing")
# 2) Enter game mode
helper.enter_game_mode(Tests.enter_game_mode)
# 3) Retrieve and Validate entities
Sphere.id = general.find_game_entity("Sphere")
Report.critical_result(Tests.find_sphere, Sphere.id.IsValid())
force_region_id = general.find_game_entity("Force Region")
Report.critical_result(Tests.find_force_region, force_region_id.IsValid())
all_triggers = ("Trigger0", "Trigger1", "Trigger2", "Trigger3")
all_triggers_found = True
for trigger in all_triggers:
trigger_id = general.find_game_entity(trigger)
if not trigger_id.IsValid():
all_triggers_found = False
Report.critical_result(Tests.find_triggers, all_triggers_found)
# 4) Get z position of spline and sphere
# All triggers are arranged at each node in spline. Getting z position of Trigger3 is same as z position of spline
spline_z_position = azlmbr.components.TransformBus(
azlmbr.bus.Event, "GetWorldZ", general.find_game_entity("Trigger3")
)
Report.info("Spline z position is : {}".format(spline_z_position))
Sphere.start_position_z = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldZ", Sphere.id)
Report.info("Sphere z position is : {}".format(Sphere.start_position_z))
# 5) Wait till the sphere drops
def sphere_fell():
if not Sphere.fell:
sphere_position_z = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldZ", Sphere.id)
if sphere_position_z < (Sphere.start_position_z - CLOSE_ENOUGH):
Report.info("Sphere position is lower than the starting position now")
Sphere.fell = True
return Sphere.fell
helper.wait_for_condition(sphere_fell, TIMEOUT)
Report.critical_result(Tests.sphere_fell, Sphere.fell)
# 6) Get position of sphere when it enters and exits from trigger area
# Wait for ball to enter force region
handler = azlmbr.physics.TriggerNotificationBusHandler()
handler.connect(force_region_id)
handler.add_callback("OnTriggerEnter", on_trigger_enter)
helper.wait_for_condition(lambda: Sphere.entered_force_region, TIMEOUT)
Report.critical_result(Tests.sphere_entered_force_region, Sphere.entered_force_region)
sphere_start_position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", Sphere.id)
Report.info_vector3(sphere_start_position, "Sphere start position in Force Region")
# Wait for ball to exit force region
handler.add_callback("OnTriggerExit", on_trigger_exit)
helper.wait_for_condition(lambda: Sphere.exited_force_region, TIMEOUT)
Report.critical_result(Tests.sphere_exited_force_region, Sphere.exited_force_region)
sphere_end_position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", Sphere.id)
Report.info_vector3(sphere_end_position, "Sphere end position in Force Region")
# 7) Verify sphere drops through force region without spline force effect
if (
((sphere_start_position.x - sphere_end_position.x) < CLOSE_ENOUGH) and
((sphere_start_position.y - sphere_end_position.y) < CLOSE_ENOUGH) and
(sphere_end_position.z < (spline_z_position - CLOSE_ENOUGH))
):
Sphere.sphere_drops = True
Report.critical_result(Tests.sphere_drops_force_region, Sphere.sphere_drops)
# 8) Exit game mode
helper.exit_game_mode(Tests.exit_game_mode)
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(ForceRegion_ZeroSplineForceDoesNothing)
@@ -0,0 +1,242 @@
"""
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 : C6090550
Test Case Title : Check that force region exerts world space force on rigid bodies (negative test)
"""
# fmt: off
class Tests:
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
ball_exists = ("The Ball entity was found", "The Ball entity was not found")
forceregion_exists = ("The ForceRegion entity was found", "The ForceRegion entity was not found")
terrain_exists = ("The Terrain entity was found", "The Terrain entity was not found")
ball_over_force_region = ("The ball is over the force region", "The ball is not over the force region")
gravity_enabled_on_ball = ("The ball has gravity enabled", "The ball gravity failed to enable")
ball_entered_force_region = ("The ball entered the force region", "The ball did not enter the force region before timeout")
ball_fell_through_region = ("The ball fell through the force region", "The ball did not exit force region below enter location")
enter_exit_distance_close = ("The ball has passed through the entire force region", "Ball did not move the height of the force region")
ball_hit_ground = ("The ball collided with the PhysX Terrain", "The ball did not collide with the PhysX Terrain before timeout")
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
# fmt: on
def ForceRegion_ZeroWorldSpaceForceDoesNothing():
"""
Summary:
A ball is suspended above a cube shaped force region and a PhysX Terrain. The force is a world space force
pointed in the positive Z direction with a magnitude of 10.
Level Description:
Ball - (entity): Mesh: Sphere shaped
PhysX Rigid Body: Gravity is enabled; Default settings
PhysX Collider: Sphere shape; offset (0.0, 0.0, 0.5); draw collider checked
ForceRegion - (entity): Box Shape: Game view checked; Shape dimensions (3.0, 3.0, 3.0)
PhysX Force Region: World Space force; direction (0.0, 0.0, 1.0); magnitude 10.0
PhysX Collider: Box shape; Box dimensions (3.0, 3.0, 3.0); Trigger checked;
Draw collider checked
Terrain - (entity): PhysX Terrain: Default settings
Expected Behavior:
When game mode is entered, the ball entity will fall due to gravity and enter the force region.
The region has a very weak force and therefore the ball will fall through the force region,
where it will then collide with the PhysX Terrain.
Test Steps:
1) Open level
2) Enter game mode
3) Find/setup entities and handlers
4) Check that the ball is over the force region
5) Wait for ball to hit the ground
6) Report if the ball entered the force region, fell through it, and collided with terrain in the alloted time
7) Exit game mode and close the 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.
:return: None
"""
import os
import sys
import azlmbr.legacy.general as general
import azlmbr.bus
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
helper.init_idle()
TIMEOUT_SECONDS = 3.0
X_Y_Z_TOLERANCE = 1.5
REGION_HEIGHT = 3.0
TERRAIN_HEIGHT = 32.0
def is_close(value1, value2, tolerance):
"""
() -> bool
Absolute value of difference of values being less than or equal to the tolerance
"""
return abs(value1 - value2) <= tolerance
def is_at_least(value1, value2, minimum):
"""
() -> bool
Subtraction of value2 from value1 being greater than or equal to the minimum
"""
return (value1 - value2) >= minimum
class Entity(object):
def __init__(self, name):
self.name = name
self.id = general.find_game_entity(self.name)
Report.critical_result(Tests.__dict__[self.name.lower() + "_exists"], self.id.IsValid())
def get_location(self):
# () -> Vector3
return azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", self.id)
class Ball(Entity):
def __init__(self):
super(Ball, self).__init__("Ball")
self.location = self.get_location()
self.enter_region_location = None
self.exit_region_location = None
self.enable_gravity()
self.check_gravity_is_on()
def enable_gravity(self):
azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "SetGravityEnabled", self.id)
def check_gravity_is_on(self):
gravity_status = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "IsGravityEnabled", self.id)
Report.critical_result(Tests.__dict__["gravity_enabled_on_" + self.name.lower()], gravity_status)
def check_alignment(self, region):
# () -> bool
Report.info_vector3(self.location, "Location of Ball: ")
Report.info_vector3(region.location, "Location of ForceRegion: ")
aligned = True
if not is_close(self.location.x, region.location.x, X_Y_Z_TOLERANCE):
aligned = False
Report.info("The ball is not aligned in the X axis.")
if not is_close(self.location.y, region.location.y, X_Y_Z_TOLERANCE):
aligned = False
Report.info("The ball is not aligned in the Y axis.")
if (self.location.z - region.location.z) < X_Y_Z_TOLERANCE:
aligned = False
Report.info("The ball is not high enough to enter the force region.")
if self.location.z < TERRAIN_HEIGHT:
aligned = False
Report.info("The ball is below the terrain.")
return aligned
def did_ball_fall_in_region_as_expected(self):
# () -> bool
if not self.exit_region_location:
Report.info("The ball did not exit the force region in time.")
return False
return (self.enter_region_location.z - self.exit_region_location.z) > X_Y_Z_TOLERANCE
def check_enter_vs_exit_locations(self):
"""
Check that the ball enter location is above the exit location (ball successfully fell through the force
region) and check that the distance between the enter/exit locations is at least the height of the region
NOTE: The ball will "enter" the force region when the bottom edge of its collision box enters the region and
will "exit" when it is completely outside the force region (top edge leaves region)
"""
Report.info_vector3(self.enter_region_location, "Ball entering location: ")
Report.info_vector3(self.exit_region_location, "Ball exiting location: ")
Report.result(Tests.ball_fell_through_region, self.did_ball_fall_in_region_as_expected())
Report.result(
Tests.enter_exit_distance_close,
is_at_least(self.enter_region_location.z, self.exit_region_location.z, REGION_HEIGHT),
)
class ForceRegion(Entity):
def __init__(self, ball):
super(ForceRegion, self).__init__("ForceRegion")
self.ball = ball # This creates a reference point to access the ball without looking in global scope
self.location = self.get_location()
self.ball_entered_trigger_area = False
self.trigger_handler = azlmbr.physics.TriggerNotificationBusHandler()
self.connect_trigger_handler()
def on_trigger_enter(self, args):
other_id = args[0]
if other_id.Equal(self.ball.id):
Report.info("Ball has entered the force region trigger area.")
self.ball_entered_trigger_area = True
self.ball.enter_region_location = self.ball.get_location()
def on_trigger_exit(self, args):
other_id = args[0]
if other_id.Equal(self.ball.id):
Report.info("Ball has exited the force region trigger area.")
self.ball.exit_region_location = self.ball.get_location()
def connect_trigger_handler(self):
self.trigger_handler.connect(self.id)
self.trigger_handler.add_callback("OnTriggerEnter", self.on_trigger_enter)
self.trigger_handler.add_callback("OnTriggerExit", self.on_trigger_exit)
class Terrain(Entity):
def __init__(self, ball):
super(Terrain, self).__init__("Terrain")
self.ball = ball # This creates a reference point to access the ball without looking in global scope
self.ball_collided = False
self.collision_handler = azlmbr.physics.CollisionNotificationBusHandler()
self.connect_collision_handler()
def on_collision_begin(self, args):
other_id = args[0]
if other_id.Equal(self.ball.id):
Report.info("Ball struck PhysX Terrain")
self.ball_collided = True
def connect_collision_handler(self):
self.collision_handler.connect(self.id)
self.collision_handler.add_callback("OnCollisionBegin", self.on_collision_begin)
# 1) Open level
helper.open_level("Physics", "ForceRegion_ZeroWorldSpaceForceDoesNothing")
# 2) Enter game mode
helper.enter_game_mode(Tests.enter_game_mode)
# 3) Find/setup entities and handlers
ball = Ball()
region = ForceRegion(ball)
terrain = Terrain(ball)
# 4) Check that the ball is over the force region
Report.critical_result(Tests.ball_over_force_region, ball.check_alignment(region))
# 5) Wait for ball to hit the ground
helper.wait_for_condition(lambda: terrain.ball_collided, TIMEOUT_SECONDS)
# 6) Report if the ball entered the force region, fell through it, and collided with terrain in the alloted time
Report.result(Tests.ball_entered_force_region, region.ball_entered_trigger_area)
ball.check_enter_vs_exit_locations()
Report.result(Tests.ball_hit_ground, terrain.ball_collided)
# 7) Exit game mode and close the editor
helper.exit_game_mode(Tests.exit_game_mode)
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(ForceRegion_ZeroWorldSpaceForceDoesNothing)