Material_StaticFriction.py 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  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 : C4044460
  7. # Test Case Title : Verify the functionality of static friction
  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_at_rest_zero = ("Box 'zero' began test motionless", "Box 'zero' did not begin test motionless")
  17. box_at_rest_low = ("Box 'low' began test motionless", "Box 'low' did not begin test motionless")
  18. box_at_rest_mid = ("Box 'mid' began test motionless", "Box 'mid' did not begin test motionless")
  19. box_at_rest_high = ("Box 'high' began test motionless", "Box 'high' did not begin test motionless")
  20. box_was_pushed_zero = ("Box 'zero' moved", "Box 'zero' did not move before timeout")
  21. box_was_pushed_low = ("Box 'low' moved", "Box 'low' did not move before timeout")
  22. box_was_pushed_mid = ("Box 'mid' moved", "Box 'mid' did not move before timeout")
  23. box_was_pushed_high = ("Box 'high' moved", "Box 'high' did not move before timeout")
  24. force_impulse_ordered = ("Boxes with greater static friction required greater impulses", "Boxes with greater static friction did not require greater impulses")
  25. exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
  26. # fmt: on
  27. def Material_StaticFriction():
  28. """
  29. Summary:
  30. Runs an automated test to ensure that greater static friction coefficient settings on a physX material results in
  31. rigidbodys (with that material) requiring a greater force in order to be set into motion
  32. Level Description:
  33. Four boxes sit on a horizontal 'ramp'. The boxes are identical, save for their physX material.
  34. A new material library was created with 4 materials and their static friction coefficient:
  35. zero_static_friction: 0.00
  36. low_static_friction: 0.50
  37. mid_static_friction: 1.00
  38. high_static_friction: 1.50
  39. Each material is identical otherwise
  40. Each box is assigned its corresponding friction material, the ramp is assigned low_static_friction
  41. The COM of the boxes is placed on the plane (0, 0, -0.5), so as to remove any torque moments and resulting rotations
  42. Expected Behavior:
  43. For each box, this script will apply a force impulse in the world X direction (starting at magnitude 0.0).
  44. Every frame, it checks if the box moved:
  45. If it didn't, we increase the magnitude slightly and try again
  46. If it did, the box retains the magnitude required to move it, and we move to the next box.
  47. Boxes with greater static friction coefficients should require greater forces in order to set them in motion.
  48. Test Steps:
  49. 1) Open level
  50. 2) Enter game mode
  51. 3) Find the ramp
  52. For each box:
  53. 4) Find the box
  54. 5) Ensure the box is stationary
  55. 6) Push the box until it moves
  56. 7) Assert that greater coefficients result in greater required force impulses
  57. 8) Exit game mode
  58. 9) Close the editor
  59. Note:
  60. - This test file must be called from the Open 3D Engine Editor command terminal
  61. - Any passed and failed tests are written to the Editor.log file.
  62. Parsing the file or running a log_monitor are required to observe the test results.
  63. :return: None
  64. """
  65. import os
  66. import sys
  67. from editor_python_test_tools.utils import Report
  68. from editor_python_test_tools.utils import TestHelper as helper
  69. import azlmbr
  70. import azlmbr.legacy.general as general
  71. import azlmbr.bus as bus
  72. import azlmbr.math as lymath
  73. FORCE_IMPULSE_INCREMENT = 0.005 # How much we increase the force every frame
  74. MIN_MOVE_DISTANCE = 0.02 # Distance magnitude that a box must travel in order to be considered moved
  75. STATIONARY_TOLERANCE = 0.0001 # Boxes must have velocities under this magnitude in order to be stationary
  76. TIMEOUT = 10
  77. class Box:
  78. def __init__(self, name, valid_test, stationary_test, moved_test):
  79. self.name = name
  80. self.id = general.find_game_entity(name)
  81. self.start_position = self.get_position()
  82. self.force_impulse = 0.0
  83. self.valid_test = valid_test
  84. self.stationary_test = stationary_test
  85. self.moved_test = moved_test
  86. def is_stationary(self):
  87. velocity = azlmbr.physics.RigidBodyRequestBus(bus.Event, "GetLinearVelocity", self.id)
  88. return vector_close_to_zero(velocity, STATIONARY_TOLERANCE)
  89. def get_position(self):
  90. return azlmbr.components.TransformBus(bus.Event, "GetWorldTranslation", self.id)
  91. def vector_close_to_zero(vector, tolerance):
  92. return abs(vector.x) <= tolerance and abs(vector.y) <= tolerance and abs(vector.z) <= tolerance
  93. def push(box):
  94. delta = box.start_position.Subtract(box.get_position())
  95. if vector_close_to_zero(delta, MIN_MOVE_DISTANCE):
  96. box.force_impulse += FORCE_IMPULSE_INCREMENT
  97. impulse_vector = lymath.Vector3(box.force_impulse, 0.0, 0.0)
  98. azlmbr.physics.RigidBodyRequestBus(bus.Event, "ApplyLinearImpulse", box.id, impulse_vector)
  99. return False
  100. else:
  101. Report.info("Box {} required force was {:.3f}".format(box.name, box.force_impulse))
  102. return True
  103. helper.init_idle()
  104. # 1) Open level
  105. helper.open_level("Physics", "Material_StaticFriction")
  106. # 2) Enter game mode
  107. helper.enter_game_mode(Tests.enter_game_mode)
  108. # fmt: off
  109. # Set up our boxes
  110. box_zero = Box(
  111. name = "Zero",
  112. valid_test = Tests.find_box_zero,
  113. stationary_test = Tests.box_at_rest_zero,
  114. moved_test = Tests.box_was_pushed_zero
  115. )
  116. box_low = Box(
  117. name = "Low",
  118. valid_test = Tests.find_box_low,
  119. stationary_test = Tests.box_at_rest_low,
  120. moved_test = Tests.box_was_pushed_low
  121. )
  122. box_mid = Box(
  123. name = "Mid",
  124. valid_test = Tests.find_box_mid,
  125. stationary_test = Tests.box_at_rest_mid,
  126. moved_test = Tests.box_was_pushed_mid
  127. )
  128. box_high = Box(
  129. name = "High",
  130. valid_test = Tests.find_box_high,
  131. stationary_test = Tests.box_at_rest_high,
  132. moved_test = Tests.box_was_pushed_high
  133. )
  134. all_boxes = (box_zero, box_low, box_mid, box_high)
  135. # fmt: on
  136. # 3) Find the ramp
  137. ramp_id = general.find_game_entity("Ramp")
  138. Report.critical_result(Tests.find_ramp, ramp_id.IsValid())
  139. for box in all_boxes:
  140. Report.info("********Pushing Box {}********".format(box.name))
  141. # 4) Find the box
  142. Report.critical_result(box.valid_test, box.id.IsValid())
  143. # 5) Ensure the box is stationary
  144. Report.result(box.stationary_test, box.is_stationary())
  145. # 6) Push the box until it moves
  146. Report.critical_result(box.moved_test, helper.wait_for_condition(lambda: push(box), TIMEOUT))
  147. # 7) Assert that greater coefficients result in greater required force impulses
  148. ordered_impulses = box_high.force_impulse > box_mid.force_impulse > box_low.force_impulse > box_zero.force_impulse
  149. Report.result(Tests.force_impulse_ordered, ordered_impulses)
  150. # 8) Exit game mode
  151. helper.exit_game_mode(Tests.exit_game_mode)
  152. if __name__ == "__main__":
  153. from editor_python_test_tools.utils import Report
  154. Report.start_test(Material_StaticFriction)