hydra_AtomEditorComponents_MeshAdded.py 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  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. class Tests:
  7. creation_undo = (
  8. "UNDO Entity creation success",
  9. "P0: UNDO Entity creation failed")
  10. creation_redo = (
  11. "REDO Entity creation success",
  12. "P0: REDO Entity creation failed")
  13. mesh_entity_creation = (
  14. "Mesh Entity successfully created",
  15. "P0: Mesh Entity failed to be created")
  16. mesh_component_added = (
  17. "Entity has a Mesh component",
  18. "P0: Entity failed to find Mesh component")
  19. model_asset_specified = (
  20. "Model Asset set",
  21. "P0: Model Asset not set")
  22. enter_game_mode = (
  23. "Entered game mode",
  24. "P0: Failed to enter game mode")
  25. exit_game_mode = (
  26. "Exited game mode",
  27. "P0: Couldn't exit game mode")
  28. is_visible = (
  29. "Entity is visible",
  30. "P0: Entity was not visible")
  31. is_hidden = (
  32. "Entity is hidden",
  33. "P0: Entity was not hidden")
  34. entity_deleted = (
  35. "Entity deleted",
  36. "P0: Entity was not deleted")
  37. deletion_undo = (
  38. "UNDO deletion success",
  39. "P0: UNDO deletion failed")
  40. deletion_redo = (
  41. "REDO deletion success",
  42. "P0: REDO deletion failed")
  43. mesh_sort_key = (
  44. "Mesh Sort key property set",
  45. "P1: Mesh Sort key property not correctly set")
  46. mesh_ray_tracing = (
  47. "Mesh Use ray tracing property set",
  48. "P1: Mesh Use ray tracing property not correctly set")
  49. mesh_cubemap = (
  50. "Mesh Exclude from reflection cubemaps property set",
  51. "P1: Mesh Exclude from reflection cubemaps property not correctly set")
  52. mesh_forward_pass = (
  53. "Mesh Use Forward Pass IBL Specular property set",
  54. "P1: Mesh Use Forward Pass IBL Specular property not correctly set")
  55. mesh_lod_type_screen_coverage = (
  56. "Mesh LOD type screen coverage set",
  57. "P1: Mesh LOD type screen coverage not correctly set")
  58. mesh_lod_type_specific_lod = (
  59. "Mesh LOD type Specific LOD set",
  60. "P1: Mesh LOD type Specific LOD not correctly set")
  61. mesh_lod_type_default = (
  62. "Mesh LOD type default set",
  63. "P1: Mesh LOD type default not correctly set")
  64. mesh_screen_coverage = (
  65. "Mesh Minimum Screen Coverage property set",
  66. "P1: Mesh Minimum Screen Coverage property not correctly set")
  67. mesh_quality_decay = (
  68. "Mesh Quality Decay Rate property set",
  69. "P1: Mesh Quality Decay Rate property not correctly set")
  70. mesh_lod_override = (
  71. "Mesh Lod Override property set",
  72. "P1: Mesh Lod Override property property not correctly set")
  73. has_material = (
  74. "Mesh entity has a material component",
  75. "P1: Mesh entity did not add a material component")
  76. mesh_component_removed = (
  77. "Mesh component removed successfully",
  78. "P1: Mesh component was not correctly removed from the entity")
  79. model_asset_is_optimized = (
  80. "tube.fbx.azmodel has <= 66 vertices",
  81. "P0: Model has not been fully optimized")
  82. model_different_bone_ids_same_position_should_weld_vertices = (
  83. "sameposition_differentboneIds_shouldnotweldvertices.fbx.azmodel has 48 vertices",
  84. "P0: Vertices were welded when they shouldnt be")
  85. def AtomEditorComponents_Mesh_AddedToEntity():
  86. """
  87. Summary:
  88. Tests the Mesh component can be added to an entity and has the expected functionality.
  89. Test setup:
  90. - Wait for Editor idle loop.
  91. - Open the "Base" level.
  92. Expected Behavior:
  93. The component can be added, used in game mode, hidden/shown, deleted, and has accurate required components.
  94. Creation and deletion undo/redo should also work.
  95. Test Steps:
  96. 1) Create a Mesh entity with no components.
  97. 2) Add a Mesh component to Mesh entity.
  98. 3) UNDO the entity creation and component addition.
  99. 4) REDO the entity creation and component addition.
  100. 5) Specify the Mesh component asset
  101. 6) Set Mesh component Sort Key property
  102. 7) Set Mesh component Use ray tracing property
  103. 8) Set Mesh component Exclude from reflection cubemaps property
  104. 9) Set Mesh component Use Forward Pass IBL Specular property
  105. 10) Set Mesh component LOD Type: Screen Coverage property
  106. 11) Set Mesh component Minimum Screen Coverage property
  107. 12) Set Mesh component Quality Decay Rate property
  108. 13) Set Mesh component LOD Type: Specific Lod property
  109. 14) Set Mesh component Lod Override property
  110. 15) Set Mesh component LOD Type: Default property
  111. 16) Set Mesh component Add Material Component and confirm a Material component added
  112. 17) Remove Mesh component then UNDO the remove
  113. 18) Enter/Exit game mode.
  114. 19) Test IsHidden.
  115. 20) Test IsVisible.
  116. 21) Verify that vertex welding is functioning
  117. 22) Verify that vertices with the same position but different joint ids aren't welded
  118. 23) Delete Mesh entity.
  119. 24) UNDO deletion.
  120. 25) REDO deletion.
  121. 26) Look for errors.
  122. :return: None
  123. """
  124. import os
  125. from PySide2 import QtWidgets
  126. import azlmbr.bus
  127. from functools import partial
  128. import azlmbr.legacy.general as general
  129. from Atom.atom_utils.atom_constants import (MESH_LOD_TYPE,
  130. AtomComponentProperties)
  131. import pyside_utils
  132. from editor_python_test_tools.asset_utils import Asset
  133. from editor_python_test_tools.editor_entity_utils import EditorEntity
  134. from editor_python_test_tools.utils import Report, TestHelper, Tracer
  135. class OnModelReadyHelper:
  136. def __init__(self):
  137. self.isModelReady = False
  138. def model_is_ready_predicate(self):
  139. """
  140. A predicate function what will be used in wait_for_condition.
  141. """
  142. return self.isModelReady
  143. def on_model_ready(self, parameters):
  144. self.isModelReady = True
  145. def wait_for_on_model_ready(self, entityId, mesh_component, model_id):
  146. self.isModelReady = False
  147. # Connect to the MeshNotificationBus
  148. # Listen for notifications when entities are created/deleted
  149. self.onModelReadyHandler = azlmbr.bus.NotificationHandler('MeshComponentNotificationBus')
  150. self.onModelReadyHandler.connect(entityId)
  151. self.onModelReadyHandler.add_callback('OnModelReady', self.on_model_ready)
  152. waitCondition = partial(self.model_is_ready_predicate)
  153. mesh_component.set_component_property_value(AtomComponentProperties.mesh('Model Asset'), model_id)
  154. if TestHelper.wait_for_condition(waitCondition, 20.0):
  155. return True
  156. else:
  157. return False
  158. with Tracer() as error_tracer:
  159. # Test setup begins.
  160. # Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level.
  161. TestHelper.init_idle()
  162. TestHelper.open_level("Graphics", "base_empty")
  163. # Test steps begin.
  164. # 1. Create a Mesh entity with no components.
  165. mesh_entity = EditorEntity.create_editor_entity(AtomComponentProperties.mesh())
  166. Report.critical_result(Tests.mesh_entity_creation, mesh_entity.exists())
  167. # 2. Add a Mesh component to Mesh entity.
  168. mesh_component = mesh_entity.add_component(AtomComponentProperties.mesh())
  169. Report.critical_result(
  170. Tests.mesh_component_added,
  171. mesh_entity.has_component(AtomComponentProperties.mesh()))
  172. # 3. UNDO the entity creation and component addition.
  173. # -> UNDO component addition.
  174. general.undo()
  175. # -> UNDO naming entity.
  176. general.undo()
  177. # -> UNDO selecting entity.
  178. general.undo()
  179. # -> UNDO entity creation.
  180. general.undo()
  181. general.idle_wait_frames(1)
  182. Report.result(Tests.creation_undo, not mesh_entity.exists())
  183. # 4. REDO the entity creation and component addition.
  184. # -> REDO entity creation.
  185. general.redo()
  186. # -> REDO selecting entity.
  187. general.redo()
  188. # -> REDO naming entity.
  189. general.redo()
  190. # -> REDO component addition.
  191. general.redo()
  192. general.idle_wait_frames(1)
  193. Report.result(Tests.creation_redo, mesh_entity.exists())
  194. # 5. Set Mesh component asset property
  195. model_path = os.path.join('testdata', 'objects', 'modelhotreload', 'sphere_5lods.fbx.azmodel')
  196. model = Asset.find_asset_by_path(model_path)
  197. mesh_component.set_component_property_value(AtomComponentProperties.mesh('Model Asset'), model.id)
  198. Report.result(Tests.model_asset_specified,
  199. mesh_component.get_component_property_value(
  200. AtomComponentProperties.mesh('Model Asset')) == model.id)
  201. # 6. Set Mesh component Sort Key property
  202. # This part of the test is currently disabled due to a bug.
  203. # It will be re-enabled in a future update once the bug is fixed.
  204. # mesh_component.set_component_property_value(
  205. # AtomComponentProperties.mesh('Sort Key'), value=23456789)
  206. # Report.result(Tests.mesh_sort_key,
  207. # mesh_component.get_component_property_value(
  208. # AtomComponentProperties.mesh('Sort Key')) == 23456789)
  209. # 7. Set Mesh component Use ray tracing property
  210. mesh_component.set_component_property_value(AtomComponentProperties.mesh('Use ray tracing'), value=False)
  211. Report.result(Tests.mesh_ray_tracing,
  212. mesh_component.get_component_property_value(
  213. AtomComponentProperties.mesh('Use ray tracing')) is False)
  214. # 8. Set Mesh component Exclude from reflection cubemaps property
  215. mesh_component.set_component_property_value(
  216. AtomComponentProperties.mesh('Exclude from reflection cubemaps'), value=True)
  217. Report.result(Tests.mesh_cubemap,
  218. mesh_component.get_component_property_value(
  219. AtomComponentProperties.mesh('Exclude from reflection cubemaps')) is True)
  220. # 9. Set Mesh component Use Forward Pass IBL Specular property
  221. mesh_component.set_component_property_value(
  222. AtomComponentProperties.mesh('Use Forward Pass IBL Specular'), value=True)
  223. Report.result(Tests.mesh_forward_pass,
  224. mesh_component.get_component_property_value(
  225. AtomComponentProperties.mesh('Use Forward Pass IBL Specular')) is True)
  226. # 10. Set Mesh component LOD Type: Screen Coverage property
  227. print(mesh_component.get_component_property_value(AtomComponentProperties.mesh('Lod Type')))
  228. mesh_component.set_component_property_value(
  229. AtomComponentProperties.mesh('Lod Type'), value=MESH_LOD_TYPE['screen coverage'])
  230. Report.result(Tests.mesh_lod_type_screen_coverage,
  231. mesh_component.get_component_property_value(
  232. AtomComponentProperties.mesh('Lod Type')) == MESH_LOD_TYPE['screen coverage'])
  233. # 11. Set Mesh component Minimum Screen Coverage property
  234. mesh_component.set_component_property_value(
  235. AtomComponentProperties.mesh('Minimum Screen Coverage'), value=1.0)
  236. Report.result(Tests.mesh_screen_coverage,
  237. mesh_component.get_component_property_value(
  238. AtomComponentProperties.mesh('Minimum Screen Coverage')) == 1.0)
  239. # 12. Set Mesh component Quality Decay Rate property
  240. mesh_component.set_component_property_value(
  241. AtomComponentProperties.mesh('Quality Decay Rate'), value=1.0)
  242. Report.result(Tests.mesh_quality_decay,
  243. mesh_component.get_component_property_value(
  244. AtomComponentProperties.mesh('Quality Decay Rate')) == 1.0)
  245. # 13. Set Mesh component LOD Type: Specific Lod property
  246. mesh_component.set_component_property_value(
  247. AtomComponentProperties.mesh('Lod Type'), value=MESH_LOD_TYPE['specific lod'])
  248. Report.result(Tests.mesh_lod_type_specific_lod,
  249. mesh_component.get_component_property_value(
  250. AtomComponentProperties.mesh('Lod Type')) == MESH_LOD_TYPE['specific lod'])
  251. # 14. Set Mesh component Lod Override property
  252. mesh_component.set_component_property_value(
  253. AtomComponentProperties.mesh('Lod Override'), value=2)
  254. Report.result(Tests.mesh_lod_override,
  255. mesh_component.get_component_property_value(
  256. AtomComponentProperties.mesh('Lod Override')) == 2)
  257. # 15. Set Mesh component LOD Type: Default property
  258. mesh_component.set_component_property_value(
  259. AtomComponentProperties.mesh('Lod Type'), value=MESH_LOD_TYPE['default'])
  260. Report.result(Tests.mesh_lod_type_default,
  261. mesh_component.get_component_property_value(
  262. AtomComponentProperties.mesh('Lod Type')) == MESH_LOD_TYPE['default'])
  263. # 16. Set Mesh component Add Material Component and confirm a Material component added
  264. # Make sure the Entity Inspector is open and trigger the "Add Material Component" button
  265. general.open_pane("Inspector")
  266. editor_window = pyside_utils.get_editor_main_window()
  267. entity_inspector = editor_window.findChild(QtWidgets.QDockWidget, "Inspector")
  268. add_material_component_button = pyside_utils.find_child_by_pattern(entity_inspector, "Add Material Component")
  269. add_material_component_button.click()
  270. Report.result(Tests.has_material, mesh_entity.has_component(AtomComponentProperties.material()))
  271. # 17. Remove Mesh component then UNDO the remove
  272. mesh_component.remove()
  273. general.idle_wait_frames(1)
  274. Report.result(Tests.mesh_component_removed, not mesh_entity.has_component(AtomComponentProperties.mesh()))
  275. general.undo()
  276. general.idle_wait_frames(1)
  277. Report.result(Tests.mesh_component_added, mesh_entity.has_component(AtomComponentProperties.mesh()))
  278. # 18. Enter/Exit game mode.
  279. TestHelper.enter_game_mode(Tests.enter_game_mode)
  280. general.idle_wait_frames(1)
  281. TestHelper.exit_game_mode(Tests.exit_game_mode)
  282. # 19. Test IsHidden.
  283. mesh_entity.set_visibility_state(False)
  284. Report.result(Tests.is_hidden, mesh_entity.is_hidden() is True)
  285. # 20. Test IsVisible.
  286. mesh_entity.set_visibility_state(True)
  287. general.idle_wait_frames(1)
  288. Report.result(Tests.is_visible, mesh_entity.is_visible() is True)
  289. # 21. Test that vertex welding is functioning
  290. model_path = os.path.join('testdata', 'objects', 'tube.fbx.azmodel')
  291. model = Asset.find_asset_by_path(model_path)
  292. onModelReadyHelper = OnModelReadyHelper()
  293. onModelReadyHelper.wait_for_on_model_ready(mesh_entity.id, mesh_component, model.id)
  294. Report.result(Tests.model_asset_specified,
  295. mesh_component.get_component_property_value(
  296. AtomComponentProperties.mesh('Model Asset')) == model.id)
  297. Report.result(Tests.model_asset_is_optimized,
  298. mesh_component.get_component_property_value(
  299. AtomComponentProperties.mesh('Vertex Count LOD0')) <= 66)
  300. # 22. Test that vertices with the same position but different boneId's aren't unintentionally welded
  301. model_path = os.path.join('testdata', 'objects', 'skinnedmesh', 'meshoptimization', 'sameposition_differentjointids_shouldnotweldvertices.fbx.azmodel')
  302. model = Asset.find_asset_by_path(model_path)
  303. onModelReadyHelper = OnModelReadyHelper()
  304. onModelReadyHelper.wait_for_on_model_ready(mesh_entity.id, mesh_component, model.id)
  305. Report.result(Tests.model_asset_specified,
  306. mesh_component.get_component_property_value(
  307. AtomComponentProperties.mesh('Model Asset')) == model.id)
  308. Report.result(Tests.model_different_bone_ids_same_position_should_weld_vertices,
  309. mesh_component.get_component_property_value(
  310. AtomComponentProperties.mesh('Vertex Count LOD0')) == 48)
  311. # 23. Delete Mesh entity.
  312. mesh_entity.delete()
  313. Report.result(Tests.entity_deleted, not mesh_entity.exists())
  314. # 24. UNDO deletion.
  315. general.undo()
  316. general.idle_wait_frames(1)
  317. Report.result(Tests.deletion_undo, mesh_entity.exists())
  318. # 25. REDO deletion.
  319. general.redo()
  320. general.idle_wait_frames(1)
  321. Report.result(Tests.deletion_redo, not mesh_entity.exists())
  322. # 26. Look for errors or asserts.
  323. TestHelper.wait_for_condition(lambda: error_tracer.has_errors or error_tracer.has_asserts, 1.0)
  324. for error_info in error_tracer.errors:
  325. Report.info(f"Error: {error_info.filename} {error_info.function} | {error_info.message}")
  326. for assert_info in error_tracer.asserts:
  327. Report.info(f"Assert: {assert_info.filename} {assert_info.function} | {assert_info.message}")
  328. if __name__ == "__main__":
  329. from editor_python_test_tools.utils import Report
  330. Report.start_test(AtomEditorComponents_Mesh_AddedToEntity)