Material_RagdollBones.py 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  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 : C4925580
  7. # Test Case Title : Verify that Material can be assigned to Ragdoll Bones and they behave as per their material
  8. # fmt: off
  9. class Tests:
  10. enter_game_mode = ("Entered game mode", "Failed to enter game mode")
  11. terrain_found_valid = ("PhysX Terrain found and validated", "PhysX Terrain not found and validated")
  12. concrete_ragdoll_found_valid = ("Concrete Ragdoll found and validated", "Concrete Ragdoll not found and validated")
  13. rubber_ragdoll_found_valid = ("Rubber Ragdoll found and validated", "Rubber Ragdoll not found and validated")
  14. concrete_ragdoll_above_terrain = ("Concrete Ragdoll is above terrain", "Concrete Ragdoll is not above terrain")
  15. rubber_ragdoll_above_terrain = ("Rubber Ragdoll is above terrain", "Rubber Ragdoll is not above terrain")
  16. terrain_collision_detected = ("Collision was detected on a ragdoll with terrain", "Collision detection timed out")
  17. concrete_ragdoll_contacted_terrain = ("Concrete Ragdoll contacted terrain", "Concrete Ragdoll did not contact terrain")
  18. rubber_ragdoll_contacted_terrain = ("Rubber Ragdoll contacted terrain", "Rubber Ragdoll did not contact terrain")
  19. rubber_ragdoll_bounced_higher = ("Rubber Ragdoll bounced higher than Concrete Ragdoll", "Rubber Ragdoll did not bounce higher than Concrete Ragdoll")
  20. concrete_ragdoll_bounced_as_expected = ("Concrete ragdoll bounced to expected height", "Concrete ragdoll did not bounce to expected height")
  21. rubber_ragdoll_bounced_as_expected = ("Rubber ragdoll bounced to expected height", "Rubber ragdoll did not bounce to expected height")
  22. exit_game_mode = ("Exited game mode", "Failed to exit game mode")
  23. # fmt: on
  24. def Material_RagdollBones():
  25. """
  26. Summary:
  27. This script runs an automated test to verify that assigning material to the skeleton of an actor entity with PhysX
  28. ragdoll will cause the entity to behave according to the nature of the material.
  29. Level Description:
  30. Two ragdoll entities (entity: Concrete Ragdoll) and (entity: Rubber Ragdoll) are above a PhysX terrain (entity:
  31. PhysX Terrain). Each ragdoll has an actor, an animation graph, and a PhysX ragdoll component. Gravity is enabled for
  32. each joint which is present on the ragdolls. The ragdolls are identical except for their textures, skeleton
  33. materials, and x-positions. Concrete Ragdoll's texture is blue, while Rubber Ragdoll's texture is red. Concrete
  34. Ragdoll's skeleton material is concrete, while Rubber Ragdoll's skeleton material is rubber.
  35. Expected behavior:
  36. The ragdolls will fall and hit the terrain at the same time. The rubber ragdoll will bounce higher than the concrete
  37. ragdoll.
  38. Test Steps:
  39. 1) Open level and enter game mode
  40. 2) Retrieve and validate entities
  41. 3) Check that each ragdoll is above the terrain
  42. 4) Wait for the initial collision between a ragdoll and the terrain or timeout
  43. 5) Check for the maximum bounce height of each ragdoll for a given period of time
  44. 6) Verify that the rubber ragdoll bounced higher than the concrete ragdoll
  45. 7) Verify that each ragdoll bounced approximately to its expected maximum height
  46. 8) Exit game mode and close editor
  47. Note:
  48. - This test file must be called from the Open 3D Engine Editor command terminal
  49. - Any passed and failed tests are written to the Editor.log file.
  50. Parsing the file or running a log_monitor are required to observe the test results.
  51. :return: None
  52. """
  53. # Setup path
  54. import os
  55. import sys
  56. import azlmbr.legacy.general as general
  57. import azlmbr.bus
  58. import azlmbr.components
  59. import azlmbr.physics
  60. from editor_python_test_tools.utils import Report
  61. from editor_python_test_tools.utils import TestHelper as helper
  62. # Constants
  63. TIME_OUT_SECONDS = 3.0
  64. TERRAIN_START_Z = 32.0
  65. CONCRETE_EXPECTED_MAX_BOUNCE_HEIGHT = 0.039
  66. RUBBER_EXPECTED_MAX_BOUNCE_HEIGHT = 1.2
  67. TOLERANCE = 0.5
  68. class Entity:
  69. def __init__(self, name, found_valid_test):
  70. self.name = name
  71. self.id = general.find_game_entity(name)
  72. self.found_valid_test = found_valid_test
  73. class Ragdoll(Entity):
  74. def __init__(self, name, found_valid_test, target_terrain, above_terrain_test, contacted_terrain_test):
  75. Entity.__init__(self, name, found_valid_test)
  76. self.target_terrain = target_terrain
  77. self.above_terrain_test = above_terrain_test
  78. self.contacted_terrain_test = contacted_terrain_test
  79. self.contacted_terrain = False
  80. self.max_bounce_height = 0
  81. self.reached_max_bounce = False
  82. # Set up collision notification handler
  83. self.handler = azlmbr.physics.CollisionNotificationBusHandler()
  84. self.handler.connect(self.id)
  85. self.handler.add_callback("OnCollisionBegin", self.on_collision_begin)
  86. def get_z_position(self):
  87. z_position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldZ", self.id)
  88. return z_position
  89. # Set up collision detection with the terrain
  90. def on_collision_begin(self, args):
  91. other_id = args[0]
  92. if other_id.Equal(self.target_terrain.id):
  93. Report.info("{} collision began with {}".format(self.name, self.target_terrain.name))
  94. if not self.contacted_terrain:
  95. self.hit_terrain_z = self.get_z_position()
  96. self.contacted_terrain = True
  97. # 1) Open level and enter game mode
  98. helper.init_idle()
  99. helper.open_level("Physics", "Material_RagdollBones")
  100. helper.enter_game_mode(Tests.enter_game_mode)
  101. # 2) Retrieve and validate entities
  102. terrain = Entity("PhysX Terrain", Tests.terrain_found_valid)
  103. Report.critical_result(terrain.found_valid_test, terrain.id.IsValid())
  104. concrete_ragdoll = Ragdoll(
  105. "Concrete Ragdoll",
  106. Tests.concrete_ragdoll_found_valid,
  107. terrain,
  108. Tests.concrete_ragdoll_above_terrain,
  109. Tests.concrete_ragdoll_contacted_terrain,
  110. )
  111. rubber_ragdoll = Ragdoll(
  112. "Rubber Ragdoll",
  113. Tests.rubber_ragdoll_found_valid,
  114. terrain,
  115. Tests.rubber_ragdoll_above_terrain,
  116. Tests.rubber_ragdoll_contacted_terrain,
  117. )
  118. ragdolls = [concrete_ragdoll, rubber_ragdoll]
  119. for ragdoll in ragdolls:
  120. Report.critical_result(ragdoll.found_valid_test, ragdoll.id.IsValid())
  121. # 3) Check that each ragdoll is above the terrain
  122. Report.critical_result(ragdoll.above_terrain_test, ragdoll.get_z_position() > TERRAIN_START_Z)
  123. # 4) Wait for the initial collision between the ragdolls and the terrain or timeout
  124. terrain_collision_detected = helper.wait_for_condition(
  125. lambda: concrete_ragdoll.contacted_terrain and rubber_ragdoll.contacted_terrain, TIME_OUT_SECONDS
  126. )
  127. Report.critical_result(Tests.terrain_collision_detected, terrain_collision_detected)
  128. for ragdoll in ragdolls:
  129. Report.result(ragdoll.contacted_terrain_test, ragdoll.contacted_terrain)
  130. # 5) Check for the maximum bounce height of each ragdoll for a given period of time
  131. def check_for_max_bounce_heights(ragdolls):
  132. for ragdoll in ragdolls:
  133. if ragdoll.contacted_terrain:
  134. bounce_height = ragdoll.get_z_position() - ragdoll.hit_terrain_z
  135. if bounce_height >= ragdoll.max_bounce_height:
  136. ragdoll.max_bounce_height = bounce_height
  137. elif ragdoll.max_bounce_height > 0.0:
  138. ragdoll.reached_max_bounce = True
  139. return concrete_ragdoll.reached_max_bounce and rubber_ragdoll.reached_max_bounce
  140. helper.wait_for_condition(lambda: check_for_max_bounce_heights(ragdolls), TIME_OUT_SECONDS)
  141. for ragdoll in ragdolls:
  142. Report.info("{}'s maximum bounce height: {}".format(ragdoll.name, ragdoll.max_bounce_height))
  143. # 6) Verify that the rubber ragdoll bounced higher than the concrete ragdoll
  144. Report.result(
  145. Tests.rubber_ragdoll_bounced_higher, rubber_ragdoll.max_bounce_height > concrete_ragdoll.max_bounce_height
  146. )
  147. # 7) Verify that each ragdoll bounced approximately to its expected maximum height
  148. Report.result(
  149. Tests.concrete_ragdoll_bounced_as_expected,
  150. abs(concrete_ragdoll.max_bounce_height - CONCRETE_EXPECTED_MAX_BOUNCE_HEIGHT) < TOLERANCE,
  151. )
  152. Report.result(
  153. Tests.rubber_ragdoll_bounced_as_expected,
  154. abs(rubber_ragdoll.max_bounce_height - RUBBER_EXPECTED_MAX_BOUNCE_HEIGHT) < TOLERANCE,
  155. )
  156. # 8) Exit game mode and close editor
  157. helper.exit_game_mode(Tests.exit_game_mode)
  158. if __name__ == "__main__":
  159. from editor_python_test_tools.utils import Report
  160. Report.start_test(Material_RagdollBones)