You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/AutomatedTesting/Gem/PythonTests/physics/C12868578_ForceRegion_Direc...

291 lines
14 KiB
Python

"""
Copyright (c) Contributors to the Open 3D Engine Project.
For complete copyright and license terms please see the LICENSE at the root of this distribution.
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
# Test case ID : 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 C12868578_ForceRegion_DirectionHasNoAffectOnMagnitude():
"""
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
import ImportPathHelper as imports
imports.init()
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", "C12868578_ForceRegion_DirectionHasNoAffectOnMagnitude")
# 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__":
import ImportPathHelper as imports
imports.init()
from editor_python_test_tools.utils import Report
Report.start_test(C12868578_ForceRegion_DirectionHasNoAffectOnMagnitude)