Material_Restitution.py 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. """
  2. Copyright (c) Contributors to the Open 3D Engine Project.
  3. For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. SPDX-License-Identifier: Apache-2.0 OR MIT
  5. """
  6. # Test case ID : C4044461
  7. # Test Case Title : Verify the functionality of restitution
  8. # fmt: off
  9. class Tests():
  10. enter_game_mode = ("Entered game mode", "Failed to enter game mode")
  11. find_ramp = ("Ramp entity found", "Ramp entity not found")
  12. find_box_zero = ("Box entity 'zero' found", "Box entity 'zero' not found")
  13. find_box_low = ("Box entity 'low' found", "Box entity 'low' not found")
  14. find_box_mid = ("Box entity 'mid' found", "Box entity 'mid' not found")
  15. find_box_high = ("Box entity 'high' found", "Box entity 'high' not found")
  16. box_fell_zero = ("Box 'zero' fell", "Box 'zero' did not fall")
  17. box_fell_low = ("Box 'low' fell", "Box 'low' did not fall")
  18. box_fell_mid = ("Box 'mid' fell", "Box 'mid' did not fall")
  19. box_fell_high = ("Box 'high' fell", "Box 'high' did not fall")
  20. box_hit_ramp_zero = ("Box 'zero' hit the ramp", "Box 'zero' did not hit the ramp before timeout")
  21. box_hit_ramp_low = ("Box 'low' hit the ramp", "Box 'low' did not hit the ramp before timeout")
  22. box_hit_ramp_mid = ("Box 'mid' hit the ramp", "Box 'mid' did not hit the ramp before timeout")
  23. box_hit_ramp_high = ("Box 'high' hit the ramp", "Box 'high' did not hit the ramp before timeout")
  24. box_peaked_zero = ("Box 'zero' reached its max height", "Box 'zero' did not reach max height before timeout")
  25. box_peaked_low = ("Box 'low' reached its max height", "Box 'low' did not reach max height before timeout")
  26. box_peaked_mid = ("Box 'mid' reached its max height", "Box 'mid' did not reach max height before timeout")
  27. box_peaked_high = ("Box 'high' reached its max height", "Box 'high' did not reach max height before timeout")
  28. box_zero_did_not_bounce = ("Box 'zero' did not bounce", "Box 'zero' bounced - this should not happen")
  29. bounce_height_ordered = ("Boxes with greater restitution values bounced higher", "Boxes with greater restitution values did not bounce higher")
  30. exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
  31. # fmt: on
  32. def Material_Restitution():
  33. """
  34. Summary:
  35. Runs an automated test to ensure that greater restitution coefficient settings on a physX material results in
  36. rigid bodies (with that material) that bounce higher
  37. Level Description:
  38. Four boxes sit above a horizontal 'ramp'. Gravity on each rigid body component is set to disabled.
  39. The boxes are identical, save for their physX material.
  40. A new material library was created with 4 materials and their restitution coefficient:
  41. zero_restitution: 0.00
  42. low_restitution: 0.30
  43. mid_restitution: 0.60
  44. high_restitution: 1.00
  45. Each material is identical otherwise
  46. Each box is assigned its corresponding physX material
  47. Expected Behavior:
  48. For each box, this script will enable gravity, then wait for the box to collide with the ramp.
  49. It then measures the height of the bounce relative to when it first came in contact with the ramp.
  50. When the z component of the box's velocity reaches zero (or below zero), the box latches its bounce height.
  51. The box is then frozen in place and the steps run for the next box in the list.
  52. Boxes with greater restitution values should retain more energy between collisions, therefore bouncing higher
  53. Test Steps:
  54. 1) Open level
  55. 2) Enter game mode
  56. 3) Find the ramp
  57. For each box:
  58. 4) Find the box
  59. 5) Drop the box
  60. 6) Ensure the box collides with the ramp
  61. 7) Ensure the box reaches its peak height
  62. 8) Special case: assert that a box with zero restitution does not bounce
  63. 9) Assert that greater restitution coefficients result in higher bounces
  64. 10) Exit game mode
  65. 11) Close the editor
  66. Note:
  67. - This test file must be called from the Open 3D Engine Editor command terminal
  68. - Any passed and failed tests are written to the Editor.log file.
  69. Parsing the file or running a log_monitor are required to observe the test results.
  70. :return: None
  71. """
  72. import os
  73. import sys
  74. from editor_python_test_tools.utils import Report
  75. from editor_python_test_tools.utils import TestHelper as helper
  76. import azlmbr
  77. import azlmbr.legacy.general as general
  78. import azlmbr.bus as bus
  79. import azlmbr.math as lymath
  80. ZERO_RESTITUTION_BOUNCE_TOLERANCE = 0.001
  81. TIMEOUT = 5
  82. FALLING_TIMEOUT = 0.1
  83. class Box:
  84. def __init__(self, name, valid_test, fell_test, hit_ramp_test, peaked_test):
  85. self.name = name
  86. self.id = general.find_game_entity(name)
  87. self.hit_ramp = False
  88. self.hit_ramp_position = None
  89. self.bounce_height = 0.0
  90. self.valid_test = valid_test
  91. self.fell_test = fell_test
  92. self.hit_ramp_test = hit_ramp_test
  93. self.peaked_test = peaked_test
  94. self.set_gravity_enabled(False)
  95. def get_position(self):
  96. return azlmbr.components.TransformBus(bus.Event, "GetWorldTranslation", self.id)
  97. def get_velocity(self):
  98. return azlmbr.physics.RigidBodyRequestBus(bus.Event, "GetLinearVelocity", self.id)
  99. def set_velocity(self, value):
  100. return azlmbr.physics.RigidBodyRequestBus(bus.Event, "SetLinearVelocity", self.id, value)
  101. def set_gravity_enabled(self, value):
  102. azlmbr.physics.RigidBodyRequestBus(bus.Event, "SetGravityEnabled", self.id, value)
  103. def on_collision_begin(args):
  104. other_id = args[0]
  105. for box in all_boxes:
  106. if box.id.Equal(other_id):
  107. box.hit_ramp_position = box.get_position()
  108. box.hit_ramp = True
  109. def reached_max_height(box):
  110. current_position = box.get_position()
  111. current_height = current_position.z - box.hit_ramp_position.z
  112. current_linear_velocity = box.get_velocity()
  113. if current_linear_velocity.z > 0.0:
  114. box.bounce_height = current_height
  115. return False
  116. else:
  117. Report.info("Box {} reached {:.3f}M high".format(box.name, box.bounce_height))
  118. return True
  119. def is_falling(box):
  120. return box.get_velocity().z < 0.0
  121. helper.init_idle()
  122. # 1) Open level
  123. helper.open_level("Physics", "Material_Restitution")
  124. # 2) Enter game mode
  125. helper.enter_game_mode(Tests.enter_game_mode)
  126. # fmt: off
  127. # Set up our boxes
  128. box_zero = Box(
  129. name = "Zero",
  130. valid_test = Tests.find_box_zero,
  131. fell_test = Tests.box_fell_zero,
  132. hit_ramp_test = Tests.box_hit_ramp_zero,
  133. peaked_test = Tests.box_peaked_zero
  134. )
  135. box_low = Box(
  136. name = "Low",
  137. valid_test = Tests.find_box_low,
  138. fell_test = Tests.box_fell_low,
  139. hit_ramp_test = Tests.box_hit_ramp_low,
  140. peaked_test = Tests.box_peaked_low
  141. )
  142. box_mid = Box(
  143. name = "Mid",
  144. valid_test = Tests.find_box_mid,
  145. fell_test = Tests.box_fell_mid,
  146. hit_ramp_test = Tests.box_hit_ramp_mid,
  147. peaked_test = Tests.box_peaked_mid
  148. )
  149. box_high = Box(
  150. name = "High",
  151. valid_test = Tests.find_box_high,
  152. fell_test = Tests.box_fell_high,
  153. hit_ramp_test = Tests.box_hit_ramp_high,
  154. peaked_test = Tests.box_peaked_high
  155. )
  156. all_boxes = (box_zero, box_low, box_mid, box_high)
  157. # fmt:on
  158. # 3) Find the ramp
  159. ramp_id = general.find_game_entity("Ramp")
  160. Report.critical_result(Tests.find_ramp, ramp_id.IsValid())
  161. handler = azlmbr.physics.CollisionNotificationBusHandler()
  162. handler.connect(ramp_id)
  163. handler.add_callback("OnCollisionBegin", on_collision_begin)
  164. for box in all_boxes:
  165. Report.info("********Dropping Box {}********".format(box.name))
  166. # 4) Find the box
  167. Report.critical_result(box.valid_test, box.id.IsValid())
  168. # 5) Drop the box
  169. box.set_gravity_enabled(True)
  170. Report.critical_result(box.fell_test, helper.wait_for_condition(lambda: is_falling(box), FALLING_TIMEOUT))
  171. # 6) Wait for the box to hit the ramp
  172. Report.result(box.hit_ramp_test, helper.wait_for_condition(lambda: box.hit_ramp, TIMEOUT))
  173. # 7) Measure the bounce height
  174. Report.result(box.peaked_test, helper.wait_for_condition(lambda: reached_max_height(box), TIMEOUT))
  175. # Freeze the box so it does not interfere with the other boxes
  176. box.set_velocity(lymath.Vector3(0.0, 0.0, 0.0))
  177. box.set_gravity_enabled(False)
  178. # 8) Special case: Assert the a box with zero restitution did not bounce
  179. Report.result(Tests.box_zero_did_not_bounce, box_zero.bounce_height < ZERO_RESTITUTION_BOUNCE_TOLERANCE)
  180. # 9) Assert that greater restitution coefficients result in higher bounces
  181. ordered_bounces = box_high.bounce_height > box_mid.bounce_height > box_low.bounce_height > box_zero.bounce_height
  182. Report.result(Tests.bounce_height_ordered, ordered_bounces)
  183. # 10) Exit game mode
  184. helper.exit_game_mode(Tests.exit_game_mode)
  185. if __name__ == "__main__":
  186. from editor_python_test_tools.utils import Report
  187. Report.start_test(Material_Restitution)