Material_DefaultMaterialLibraryChangesWork.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  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 : C15096737
  7. # Test Case Title : Verify that a change in the default material library material information
  8. # affects all the materials that reference it, even non-defaulted
  9. # exactly like if the library was selected
  10. # fmt: off
  11. class Tests:
  12. # level
  13. enter_game_mode_0 = ("Entered game mode 0", "Failed to enter game mode 0")
  14. exit_game_mode_0 = ("Exited game mode 0", "Couldn't exit game mode 0")
  15. enter_game_mode_1 = ("Entered game mode 1", "Failed to enter game mode 1")
  16. exit_game_mode_1 = ("Exited game mode 1", "Couldn't exit game mode 1")
  17. # targets
  18. terrain_found = ("Terrain found in each test", "TERRAIN NOT FOUND in a test")
  19. target_character_rubber_found = ("target_character_rubber found in each test", "target_character_rubber NOT FOUND in a test")
  20. target_character_concrete_found = ("target_character_concrete found in each test", "target_character_concrete NOT FOUND in a test")
  21. # collider activity
  22. rubber_sphere_found = ("rubber_sphere found in each test", "rubber_sphere NOT FOUND in a test in a test")
  23. rubber_sphere_trigger_found = ("rubber_sphere_trigger found in each test", "rubber_sphere_trigger NOT FOUND in a test")
  24. rubber_sphere_collided = ("rubber_sphere collided in each test", "rubber_sphere DIDN'T COLLIDE in a test")
  25. concrete_sphere_found = ("concrete_sphere found in each test", "concrete_sphere NOT FOUND in a test")
  26. concrete_sphere_trigger_found = ("concrete_sphere_trigger found in each test", "concrete_sphere_trigger NOT FOUND in a test")
  27. concrete_sphere_collided = ("concrete_sphere collided in each test", "concrete_sphere DIDN'T COLLIDE in a test")
  28. character_rubber_found = ("character_rubber found in each test", "character_rubber NOT FOUND in a test")
  29. character_rubber_trigger_found = ("character_rubber_trigger found in each test", "character_rubber_trigger NOT FOUND in a test")
  30. character_rubber_collided = ("character_rubber collided in each test", "character_rubber DIDN'T COLLIDE in a test")
  31. character_concrete_found = ("character_concrete found in each test", "character_concrete NOT FOUND in a test")
  32. character_concrete_trigger_found = ("character_concrete_trigger found in each test", "character_concrete_trigger NOT FOUND in a test")
  33. character_concrete_collided = ("character_concrete collided in each test", "character_concrete DIDN'T COLLIDE in a test")
  34. terrain_rubber_found = ("terrain_rubber found in each test", "terrain_rubber NOT FOUND in a test")
  35. terrain_rubber_trigger_found = ("terrain_rubber_trigger found in each test", "terrain_rubber_trigger NOT FOUND in a test")
  36. terrain_rubber_collided = ("terrain_rubber collided in each test", "terrain_rubber DIDN'T COLLIDE in a test")
  37. terrain_concrete_found = ("terrain_concrete found in each test", "terrain_concrete NOT FOUND in a test")
  38. terrain_concrete_trigger_found = ("terrain_concrete_trigger found in each test", "terrain_concrete_trigger NOT FOUND in a test")
  39. terrain_concrete_collided = ("terrain_concrete collided in each test", "terrain_concrete DIDN'T COLLIDE in a test")
  40. ragdoll_rubber_found = ("ragdoll_rubber found in each test", "ragdoll_rubber NOT FOUND in a test")
  41. ragdoll_rubber_trigger_found = ("ragdoll_rubber_trigger found in each test", "ragdoll_rubber_trigger NOT FOUND in a test")
  42. ragdoll_rubber_collided = ("ragdoll_rubber collided in each test", "ragdoll_rubber DIDN'T COLLIDE in a test")
  43. ragdoll_concrete_found = ("ragdoll_concrete found in each test", "ragdoll_concrete NOT FOUND in a test")
  44. ragdoll_concrete_trigger_found = ("ragdoll_concrete_trigger found in each test", "ragdoll_concrete_trigger NOT FOUND in a test")
  45. ragdoll_concrete_collided = ("ragdoll_concrete collided in each test", "ragdoll_concrete DIDN'T COLLIDE in a test")
  46. # Verification
  47. material_library_updated = ("Default material library updated", "Default material library not updated")
  48. rubber_material_changed = ("Rubber material changed correctly", "Rubber didn't react correctly")
  49. concrete_material_changed = ("Concrete material changed correctly", "Concrete didn't react correctly")
  50. # fmt: on
  51. def Material_DefaultMaterialLibraryChangesWork():
  52. """
  53. Summary: Runs an automated test to verify that material selected in the default material library is applied to PhysX
  54. colliders, character controller, terrain texture layers and ragdolls and that material can respond to change.
  55. PhysX Config Description:
  56. A PhysX material library called all_ones is set as the default material library in PhysX Config File.
  57. The library has two materials surfaces: rubber with Restitution = 1.0, Restitution Combine = Maximum
  58. and concrete with Restitution = 0.0, Restitution combine = Multiply.
  59. The custom config file is loaded before editor is launched.
  60. Level Description:
  61. Consists of 4 sets of entities.
  62. Each entity has either rubber or concrete material assigned to it. Each entity has a corresponding trigger placed
  63. between the entity and its collision target entity (terrain or character controller).
  64. The entities, their triggers and their target are colored blue if they have rubber material, or red for concrete.
  65. Expected Behavior:
  66. The entities start their movement once the level is loaded. They should touch their corresponding triggers first,
  67. then collide with their target entity. The ones with rubber material are supposed to bounce back and touch the
  68. triggers. The ones with concrete material are supposed to stick to the target and stop moving, therefore not
  69. touching the triggers anymore. After the edits to material library the affect will be swapped.
  70. Main Script Steps:
  71. 1) Loads the level
  72. 2) Setup targets and colliders
  73. 3) Run Test 0
  74. 4) Edit Material Library
  75. 5) Run Test 1
  76. 6) Validate Results
  77. 7) Close editor
  78. Test Steps:
  79. 1) Enter Game Mode
  80. 2) Validate target Id's
  81. 3) Validate all Colliders and setup targets
  82. 4) Wait for Collision, Report Results
  83. 5) Allow Time to Hit trigger
  84. 6) Exit Game Mode
  85. """
  86. import os
  87. import sys
  88. from editor_python_test_tools.utils import Report
  89. from editor_python_test_tools.utils import TestHelper as helper
  90. import azlmbr.legacy.general as general
  91. import azlmbr.bus
  92. from Physmaterial_Editor import Physmaterial_Editor
  93. # Constants
  94. TIME_OUT = 2.0
  95. PROPAGATION_FRAMES = 180
  96. def get_test(entity_name, suffix):
  97. return Tests.__dict__[entity_name + suffix]
  98. # Base class for triggers, targets and colliders
  99. class Entity(object):
  100. # Global Holding Variable for test index
  101. current_test = None
  102. def __init__(self, name):
  103. self.name = name
  104. self.found_in_before_test = False
  105. # Validates entity ids reports if the ids are valid for both test cases
  106. # Fast fails if any id is invalid
  107. def validate_id(self):
  108. self.id = general.find_game_entity(self.name)
  109. if Entity.current_test == 0 and self.id.IsValid():
  110. self.found_in_before_test = True
  111. elif Entity.current_test == 1:
  112. Report.critical_result(get_test(self.name, "_found"), self.id.IsValid() and self.found_in_before_test)
  113. else:
  114. helper.fail_fast("{} was not found in test {}".format(self.name, Entity.current_test))
  115. @property
  116. def position(self):
  117. return azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", self.id)
  118. @property
  119. def velocity(self):
  120. return azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetLinearVelocity", self.id)
  121. class Collider(Entity):
  122. def __init__(self, name, target):
  123. Entity.__init__(self, name)
  124. self.target = target
  125. # Data holding variables
  126. self.collided_with_target_0 = False
  127. self.collided_with_target_1 = False
  128. self.hit_trigger_0 = False
  129. self.hit_trigger_1 = False
  130. # Initialized target collisions
  131. def setup_target(self):
  132. self.target.validate_id
  133. # Watch target for collision with collider
  134. self.collision_handler = azlmbr.physics.CollisionNotificationBusHandler()
  135. self.collision_handler.connect(self.id)
  136. self.collision_handler.add_callback("OnCollisionBegin", self.detect_collision_target)
  137. def activate_trigger(self):
  138. azlmbr.entity.GameEntityContextRequestBus(azlmbr.bus.Broadcast, "ActivateGameEntity", self.trigger.id)
  139. Report.info("{} activated".format(self.trigger.name))
  140. # Sets up trigger and activates it post-collision with target
  141. def setup_trigger(self):
  142. if Entity.current_test == 0:
  143. self.trigger = Entity(self.name + "_trigger")
  144. self.trigger.validate_id()
  145. self.activate_trigger()
  146. # Watch for collider entrance
  147. self.trigger.handler = azlmbr.physics.TriggerNotificationBusHandler()
  148. self.trigger.handler.connect(self.trigger.id)
  149. self.trigger.handler.add_callback("OnTriggerEnter", self.on_trigger_enter)
  150. def on_trigger_enter(self, args):
  151. if self.id.equal(args[0]) and not getattr(self, "hit_trigger_{}".format(Entity.current_test)):
  152. Report.info("{} entered {} in test {}".format(self.name, self.trigger.name, Entity.current_test))
  153. setattr(self, "hit_trigger_{}".format(Entity.current_test), True)
  154. def detect_collision_target(self, args):
  155. print("Collision_going_on")
  156. if self.target.id.equal(args[0]) and not getattr(self, "collided_with_target_{}".format(Entity.current_test)):
  157. Report.info("{} collided with {}".format(self.name, self.target.name))
  158. setattr(self, "collided_with_target_{}".format(Entity.current_test), True)
  159. self.setup_trigger()
  160. def edit_material_library():
  161. # Flips the Restitution values of rubber and concrete
  162. material_library = Physmaterial_Editor("all_ones_1.physmaterial")
  163. rubber_restitution = material_library.modify_material("rubber", "Restitution", 0)
  164. rubber_restitution_combine = material_library.modify_material("rubber", "RestitutionCombine", "Multiply")
  165. concrete_restitution = material_library.modify_material("concrete", "Restitution", 1)
  166. concrete_restitution_combine = material_library.modify_material("concrete", "RestitutionCombine", "Average")
  167. material_library.save_changes()
  168. return rubber_restitution and rubber_restitution_combine and concrete_restitution and concrete_restitution_combine
  169. def check_rubber_material_updated(rubber_colliders):
  170. # Checks that all rubber colliders hit the trigger on test 0 and not on test 1
  171. before_test_passed = all([collider.hit_trigger_0 for collider in rubber_colliders])
  172. after_test_passed = all([not collider.hit_trigger_1 for collider in rubber_colliders])
  173. return before_test_passed and after_test_passed
  174. def check_concrete_material_updated(concrete_colliders):
  175. # Checks that all concrete colliders didn't hit the trigger on test 0 and did on test 1
  176. before_test_passed = all([not collider.hit_trigger_0 for collider in concrete_colliders])
  177. after_test_passed = all([collider.hit_trigger_1 for collider in concrete_colliders])
  178. return before_test_passed and after_test_passed
  179. def test_run(index, all_colliders):
  180. Entity.current_test = index
  181. # 1) Enter Game Mode
  182. helper.enter_game_mode(get_test("enter_game_mode_", str(index)))
  183. # 2) Validate target Ids
  184. terrain.validate_id()
  185. target_character_concrete.validate_id()
  186. target_character_rubber.validate_id()
  187. # 3) Validate all Colliders and setup targets
  188. for collider in all_colliders:
  189. collider.validate_id()
  190. collider.setup_target()
  191. # 4) Wait for Collision, Report Results
  192. if not helper.wait_for_condition(lambda: all([getattr(collider, "collided_with_target_{}".format(index)) for collider in all_colliders]), TIME_OUT):
  193. failed_colliders = ", ".join([collider.name for collider in all_colliders if not getattr(collider, "collided_with_target_{}".format(index))])
  194. helper.fail_fast("A collision with target did not occur for these colliders: {}".format(failed_colliders))
  195. elif index == 1:
  196. for collider in all_colliders:
  197. Report.result(get_test(collider.name, "_collided"), collider.collided_with_target_0 and collider.collided_with_target_1)
  198. # 5) Allow time to hit trigger
  199. general.idle_wait_frames(PROPAGATION_FRAMES)
  200. # 6) Exit Game Mode
  201. helper.exit_game_mode(get_test("exit_game_mode_", str(index)))
  202. # Main Script
  203. helper.init_idle()
  204. # 1) Load the level
  205. helper.open_level("Physics", "Material_DefaultMaterialLibraryChangesWork")
  206. # 2) Setup targets and colliders
  207. terrain = Entity("terrain")
  208. target_character_rubber = Entity("target_character_rubber")
  209. target_character_concrete = Entity("target_character_concrete")
  210. rubber_sphere = Collider(name="rubber_sphere", target=terrain)
  211. concrete_sphere = Collider(name="concrete_sphere", target=terrain)
  212. character_rubber = Collider(name="character_rubber", target=target_character_rubber)
  213. character_concrete = Collider(name="character_concrete", target=target_character_concrete)
  214. terrain_rubber = Collider(name="terrain_rubber", target=terrain)
  215. terrain_concrete = Collider(name="terrain_concrete", target=terrain)
  216. ragdoll_rubber = Collider(name="ragdoll_rubber", target=terrain)
  217. ragdoll_concrete = Collider(name="ragdoll_concrete", target=terrain)
  218. rubber_test_entities = [rubber_sphere, character_rubber, terrain_rubber, ragdoll_rubber]
  219. concrete_test_entities = [concrete_sphere, character_concrete, terrain_concrete, ragdoll_concrete]
  220. test_entities = rubber_test_entities + concrete_test_entities
  221. # 3) Run test 0
  222. test_run(index=0, all_colliders=test_entities)
  223. # 4) Edit Material Library
  224. Report.critical_result(Tests.material_library_updated, edit_material_library())
  225. # Wait for material library changes to propagate
  226. general.idle_wait_frames(PROPAGATION_FRAMES)
  227. # 5) Run test 1
  228. test_run(index=1, all_colliders=test_entities)
  229. # 6) Validate Results
  230. Report.result(Tests.concrete_material_changed, check_concrete_material_updated(concrete_test_entities))
  231. Report.result(Tests.rubber_material_changed, check_rubber_material_updated(rubber_test_entities))
  232. if __name__ == "__main__":
  233. from editor_python_test_tools.utils import Report
  234. Report.start_test(Material_DefaultMaterialLibraryChangesWork)