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/material/C4044457_Material_Restituti...

245 lines
11 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 : C4044457
# Test Case Title : Verify that when two objects with different materials collide, the restitution combine works
# fmt: off
class Tests():
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
find_ramp = ("Ramp entity found", "Ramp entity not found")
find_box_minimum = ("Box entity 'minimum' found", "Box entity 'minimum' not found")
find_box_multiply = ("Box entity 'multiply' found", "Box entity 'multiply' not found")
find_box_average = ("Box entity 'average' found", "Box entity 'average' not found")
find_box_maximum = ("Box entity 'maximum' found", "Box entity 'maximum' not found")
box_fell_minimum = ("Box 'minimum' fell", "Box 'minimum' did not fall")
box_fell_multiply = ("Box 'multiply' fell", "Box 'multiply' did not fall")
box_fell_average = ("Box 'average' fell", "Box 'average' did not fall")
box_fell_maximum = ("Box 'maximum' fell", "Box 'maximum' did not fall")
box_hit_ramp_minimum = ("Box 'minimum' hit the ramp", "Box 'minimum' did not hit the ramp before timeout")
box_hit_ramp_multiply = ("Box 'multiply' hit the ramp", "Box 'multiply' did not hit the ramp before timeout")
box_hit_ramp_average = ("Box 'average' hit the ramp", "Box 'average' did not hit the ramp before timeout")
box_hit_ramp_maximum = ("Box 'maximum' hit the ramp", "Box 'maximum' did not hit the ramp before timeout")
box_peaked_minimum = ("Box 'minimum' reached its max height", "Box 'minimum' did not reach its' max height before timeout")
box_peaked_multiply = ("Box 'multiply' reached its max height", "Box 'multiply' did not reach its' max height before timeout")
box_peaked_average = ("Box 'average' reached its max height", "Box 'average' did not reach its' max height before timeout")
box_peaked_maximum = ("Box 'maximum' reached its max height", "Box 'maximum' did not reach its' max height before timeout")
minimum_equals_multiply = ("Box 'minimum' and 'multiply' bounced equal heights", "Box 'minimum' and 'multiply' did not bounce equal heights")
distance_ordered = ("Boxes with greater restitution values bounced higher", "Boxes with greater restitution values did not bounce higher")
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
# fmt: on
def C4044457_Material_RestitutionCombine():
"""
Summary:
Level Description:
Four boxes sit above a horizontal 'ramp'. Gravity on each rigidbody component is set to disabled.
The boxes are identical, save for their physX material.
A new material library was created with 4 materials, minimum, multiply, average, and maximum.
Each material has its 'restitution combine' mode assigned as named; as well as the following properties:
dynamic friction: 0.1
static friction: 0.1
restitution: 0.1
An additional material was created for the ramp entity. It has the following properties:
dynamic friction: 1.0
static friction: 1.0
restitution: 1.0
friction combine: Average
Each box is assigned its corresponding material
Each box also has a PhysX box collider with default settings
Expected Behavior:
For each box, this script will enable gravity, then wait for the box to collide with the ramp.
It then measures the height of the bounce relative to when it first came in contact with the ramp.
When the z component of the box's velocity reaches zero (or below zero), the box latches its bounce height.
The box is then frozen in place and the steps run for the next box in the list.
Boxes with greater restitution combine mode retain more energy between collisions, therefore bouncing higher.
minimum: 0.1 vs 1 -> 0.1
multiply: 0.1 * 1 -> 0.1
average: (0.1 + 1) / 2 -> 0.55
maximum: 0.1 vs 1 -> 1
Test Steps:
1) Open level
2) Enter game mode
3) Find the ramp
For each box:
4) Find the box
5) Drop the box
6) Ensure the box collides with the ramp
7) Ensure the box reaches its peak height
8) Special case: assert that minimum and multiply bounce the same height
9) Assert that greater restitution combine modes bounce higher
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
import azlmbr.legacy.general as general
import azlmbr.bus as bus
import azlmbr.math as lymath
DISTANCE_TOLERANCE = 0.005
TIMEOUT = 5
class Box:
def __init__(self, name, valid_test, fell_test, hit_ramp_test, peaked_test):
self.name = name
self.id = general.find_game_entity(name)
self.start_position = self.get_position()
self.hit_ramp = False
self.hit_ramp_position = None
self.bounce_height = 0.0
self.valid_test = valid_test
self.fell_test = fell_test
self.hit_ramp_test = hit_ramp_test
self.peaked_test = peaked_test
self.set_gravity_enabled(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)
def set_velocity(self, value):
return azlmbr.physics.RigidBodyRequestBus(bus.Event, "SetLinearVelocity", self.id, value)
def set_gravity_enabled(self, value):
azlmbr.physics.RigidBodyRequestBus(bus.Event, "SetGravityEnabled", self.id, value)
def on_collision_begin(args):
other_id = args[0]
for box in all_boxes:
if box.id.Equal(other_id):
box.hit_ramp_position = box.get_position()
box.hit_ramp = True
def reached_max_height(box):
current_position = box.get_position()
current_height = current_position.z - box.hit_ramp_position.z
current_linear_velocity = box.get_velocity()
if current_linear_velocity.z > 0.0:
box.bounce_height = current_height
return False
else:
Report.info("Box {} reached {:.3f}M high".format(box.name, box.bounce_height))
return True
def is_falling(box):
return box.get_velocity().z < 0.0
def float_is_close(value, target, tolerance):
return abs(value - target) <= tolerance
helper.init_idle()
# 1) Open level
helper.open_level("Physics", "C4044457_Material_RestitutionCombine")
# 2) Enter game mode
helper.enter_game_mode(Tests.enter_game_mode)
# fmt: off
# Set up our boxes
box_minimum = Box(
name = "Minimum",
valid_test = Tests.find_box_minimum,
fell_test = Tests.box_fell_minimum,
hit_ramp_test = Tests.box_hit_ramp_minimum,
peaked_test = Tests.box_peaked_minimum,
)
box_multiply = Box(
name = "Multiply",
valid_test = Tests.find_box_multiply,
fell_test = Tests.box_fell_multiply,
hit_ramp_test = Tests.box_hit_ramp_multiply,
peaked_test = Tests.box_peaked_multiply,
)
box_average = Box(
name = "Average",
valid_test = Tests.find_box_average,
fell_test = Tests.box_fell_average,
hit_ramp_test = Tests.box_hit_ramp_average,
peaked_test = Tests.box_peaked_average,
)
box_maximum = Box(
name = "Maximum",
valid_test = Tests.find_box_maximum,
fell_test = Tests.box_fell_maximum,
hit_ramp_test = Tests.box_hit_ramp_maximum,
peaked_test = Tests.box_peaked_maximum,
)
all_boxes = (box_minimum, box_multiply, box_average, box_maximum)
# fmt: on
# 3) Find the ramp
ramp_id = general.find_game_entity("Ramp")
Report.critical_result(Tests.find_ramp, ramp_id.IsValid())
handler = azlmbr.physics.CollisionNotificationBusHandler()
handler.connect(ramp_id)
handler.add_callback("OnCollisionBegin", on_collision_begin)
for box in all_boxes:
Report.info("********Dropping Box {}********".format(box.name))
# 4) Find the box
Report.critical_result(box.valid_test, box.id.IsValid())
# 5) Drop the box
box.set_gravity_enabled(True)
Report.critical_result(box.fell_test, helper.wait_for_condition(lambda: is_falling(box), TIMEOUT))
# 6) Wait for the box to hit the ground
Report.result(box.hit_ramp_test, helper.wait_for_condition(lambda: box.hit_ramp, TIMEOUT))
# 7) Measure the bounce height
Report.result(box.peaked_test, helper.wait_for_condition(lambda: reached_max_height(box), TIMEOUT))
# Freeze the box so it does not interfere with the other boxes
box.set_velocity(lymath.Vector3(0.0, 0.0, 0.0))
box.set_gravity_enabled(False)
# 8) Special case: assert that minimum and multiply bounce the same height
boxes_are_close = float_is_close(box_minimum.bounce_height, box_multiply.bounce_height, DISTANCE_TOLERANCE)
Report.result(Tests.minimum_equals_multiply, boxes_are_close)
# 9) Assert that greater coefficients result in higher bounces
distance_ordered = (
boxes_are_close and box_minimum.bounce_height < box_average.bounce_height < box_maximum.bounce_height
)
Report.result(Tests.distance_ordered, distance_ordered)
# 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(C4044457_Material_RestitutionCombine)