3
0

hydra_AtomEditorComponents_MaterialAdded.py 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  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. material_creation = (
  14. "Material Entity successfully created",
  15. "P0: Material Entity failed to be created")
  16. material_component = (
  17. "Entity has a Material component",
  18. "P0: Entity failed to find Material component")
  19. material_disabled = (
  20. "Material component disabled",
  21. "P0: Material component was not disabled.")
  22. actor_component = (
  23. "Entity has an Actor component",
  24. "P0: Entity did not have an Actor component")
  25. actor_undo = (
  26. "Entity Actor component gone",
  27. "P0: Entity Actor component add failed to undo")
  28. mesh_component = (
  29. "Entity has a Mesh component",
  30. "P0: Entity did not have a Mesh component")
  31. material_enabled = (
  32. "Material component enabled",
  33. "P0: Material component was not enabled.")
  34. enter_game_mode = (
  35. "Entered game mode",
  36. "P0: Failed to enter game mode")
  37. exit_game_mode = (
  38. "Exited game mode",
  39. "P0: Couldn't exit game mode")
  40. is_visible = (
  41. "Entity is visible",
  42. "P0: Entity was not visible")
  43. is_hidden = (
  44. "Entity is hidden",
  45. "P0: Entity was not hidden")
  46. entity_deleted = (
  47. "Entity deleted",
  48. "P0: Entity was not deleted")
  49. deletion_undo = (
  50. "UNDO deletion success",
  51. "P0: UNDO deletion failed")
  52. deletion_redo = (
  53. "REDO deletion success",
  54. "P0: REDO deletion failed")
  55. model_asset = (
  56. "Model Asset set",
  57. "P1: Model Asset not set")
  58. default_material = (
  59. "Default Material set to metal_gold.azmaterial",
  60. "P1: Default Material was not set as expected")
  61. model_material_count = (
  62. "Model Material count is 5 as expected",
  63. "P1: Model Material count did not reach 5 within timeout")
  64. model_material_label = (
  65. "Model Material zero index is labeled lambert0",
  66. "P1: Model Material index zero does not have the expected label")
  67. model_material_asset_path = (
  68. "Asset path of the Material Model index zero is as expected",
  69. "P1: Material Model index zero Asset path is not as expected")
  70. enable_lod_materials = (
  71. "Enable LOD Materials property set to True",
  72. "P1: Enable LOD Materials property was not set correctly ")
  73. lod_material_count = (
  74. "LOD Material count is 5 as expected",
  75. "P1: LOD Material count did not reach 5 within timeout")
  76. lod_material_label = (
  77. "LOD Material zero index is labeled lambert0",
  78. "P1: LOD Material index zero does not have the expected label")
  79. lod_material_asset_path = (
  80. "Asset path of LOD Material index zero first item is as expected",
  81. "P1: Asset path of LOD Material index zero first item is not as expected")
  82. lod_material_set = (
  83. "LOD material zero is set to metal_gold.azmaterial id",
  84. "P1: LOD material zero was not property set as expected")
  85. controler_materials_is_empty_container = (
  86. "Controller|Materials is a container property and is empty",
  87. "P1: Controller|Materials is not a container or is not empty as expected")
  88. clear_default_material = (
  89. "Default Material cleared with MaterialComponentRequestBus",
  90. "P1: Default Material FAILED to clear with MaterialComponentRequestBus. does not contain empty AssetId.")
  91. def AtomEditorComponents_Material_AddedToEntity():
  92. """
  93. Summary:
  94. Tests the Material component can be added to an entity and has the expected functionality.
  95. Test setup:
  96. - Wait for Editor idle loop.
  97. - Open the "Base" level.
  98. Expected Behavior:
  99. The component can be added, used in game mode, hidden/shown, deleted, and has accurate required components.
  100. Creation and deletion undo/redo should also work.
  101. Test Steps:
  102. 1) Create a Material entity with no components.
  103. 2) Add a Material component to Material entity.
  104. 3) UNDO the entity creation and component addition.
  105. 4) REDO the entity creation and component addition.
  106. 5) Verify Material component not enabled.
  107. 6) Add Actor component since it is required by the Material component.
  108. 7) Verify Material component is enabled.
  109. 8) Remove Actor component
  110. 9) Verify Material component not enabled.
  111. 10) Add Mesh component since it is required by the Material component.
  112. 11) Verify Material component is enabled.
  113. 12) Set a model asset to Mesh component
  114. 13) Wait for Model Materials to indicate count 5 terminate early if container fails to reflect correct count
  115. 14) Get the slot zero material from the container property Model Materials (materials defined in the fbx)
  116. 15) Enable the use of LOD materials
  117. 16) Wait for LOD Materials to indicate count 5 terminate early if container fails to reflect correct count
  118. 17) Get the slot zero list from LOD Material
  119. 18) Override the slot zero LOD Material asset using EditorMaterialComponentSlot method SetAssetId
  120. 19) Clear the LOD Material override and confirm the active asset is the default value
  121. 20) Set the Default Material asset to an override using set component property by path
  122. 21) Clear the assignment of a default material using MaterialComponentRequestBus and confirm null asset
  123. 22) Set the Default Material asset using MaterialComponentRequestBus
  124. 23) Enter/Exit game mode.
  125. 24) Test IsHidden.
  126. 25) Test IsVisible.
  127. 26) Delete Material entity.
  128. 27) UNDO deletion.
  129. 28) REDO deletion.
  130. 29) Look for errors or asserts.
  131. :return: None
  132. """
  133. import os
  134. import azlmbr.legacy.general as general
  135. import azlmbr.bus as bus
  136. import azlmbr.render as render
  137. from editor_python_test_tools.asset_utils import Asset
  138. from editor_python_test_tools.editor_entity_utils import EditorEntity
  139. from editor_python_test_tools.utils import Report, Tracer, TestHelper
  140. from Atom.atom_utils.atom_constants import AtomComponentProperties
  141. with Tracer() as error_tracer:
  142. # Test setup begins.
  143. # Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level.
  144. TestHelper.init_idle()
  145. TestHelper.open_level("Graphics", "base_empty")
  146. # Test steps begin.
  147. # 1. Create a Material entity with no components.
  148. material_entity = EditorEntity.create_editor_entity(AtomComponentProperties.material())
  149. Report.critical_result(Tests.material_creation, material_entity.exists())
  150. # 2. Add a Material component to Material entity.
  151. material_component = material_entity.add_component(AtomComponentProperties.material())
  152. Report.critical_result(
  153. Tests.material_component,
  154. material_entity.has_component(AtomComponentProperties.material()))
  155. # 3. UNDO the entity creation and component addition.
  156. # -> UNDO component addition.
  157. general.undo()
  158. # -> UNDO naming entity.
  159. general.undo()
  160. # -> UNDO selecting entity.
  161. general.undo()
  162. # -> UNDO entity creation.
  163. general.undo()
  164. general.idle_wait_frames(1)
  165. Report.result(Tests.creation_undo, not material_entity.exists())
  166. # 4. REDO the entity creation and component addition.
  167. # -> REDO entity creation.
  168. general.redo()
  169. # -> REDO selecting entity.
  170. general.redo()
  171. # -> REDO naming entity.
  172. general.redo()
  173. # -> REDO component addition.
  174. general.redo()
  175. general.idle_wait_frames(1)
  176. Report.result(Tests.creation_redo, material_entity.exists())
  177. # 5. Verify Material component not enabled.
  178. Report.result(Tests.material_disabled, not material_component.is_enabled())
  179. # 6. Add Actor component since it is required by the Material component.
  180. actor_component = material_entity.add_component(AtomComponentProperties.actor())
  181. Report.result(Tests.actor_component, material_entity.has_component(AtomComponentProperties.actor()))
  182. # 7. Verify Material component is enabled.
  183. Report.result(Tests.material_enabled, material_component.is_enabled())
  184. # 8. Remove actor component.
  185. actor_component.remove()
  186. general.idle_wait_frames(1)
  187. Report.result(Tests.actor_undo, not material_entity.has_component(AtomComponentProperties.actor()))
  188. # 9. Verify Material component not enabled.
  189. Report.result(Tests.material_disabled, not material_component.is_enabled())
  190. # 10. Add Mesh component since it is required by the Material component.
  191. mesh_component = material_entity.add_component(AtomComponentProperties.mesh())
  192. Report.result(Tests.mesh_component, material_entity.has_component(AtomComponentProperties.mesh()))
  193. # 11. Verify Material component is enabled.
  194. Report.result(Tests.material_enabled, material_component.is_enabled())
  195. # 12. Set a model asset to Mesh component
  196. # Set a simple model to ensure that the more complex model will load cleanly
  197. model_path = os.path.join('objects', 'cube.fbx.azmodel')
  198. model = Asset.find_asset_by_path(model_path)
  199. mesh_component.set_component_property_value(AtomComponentProperties.mesh('Model Asset'), model.id)
  200. general.idle_wait_frames(1)
  201. # Update model asset to a model with 5 LOD materials
  202. model_path = os.path.join('testdata', 'objects', 'modelhotreload', 'sphere_5lods.fbx.azmodel')
  203. model = Asset.find_asset_by_path(model_path)
  204. mesh_component.set_component_property_value(AtomComponentProperties.mesh('Model Asset'), model.id)
  205. general.idle_wait_frames(1)
  206. Report.result(
  207. Tests.model_asset,
  208. mesh_component.get_component_property_value(AtomComponentProperties.mesh('Model Asset')) == model.id)
  209. # 13. Wait for Model Materials to indicate count 5 terminate early if container fails to reflect correct count
  210. Report.critical_result(Tests.model_material_count, TestHelper.wait_for_condition(
  211. lambda: (material_component.get_container_count(
  212. AtomComponentProperties.material('Model Materials')) == 5), timeout_in_seconds=5))
  213. # 14. Get the slot zero material from the container property Model Materials (materials defined in the fbx)
  214. item = material_component.get_container_item(AtomComponentProperties.material('Model Materials'), key=0)
  215. # item is an EditorMaterialComponentSlot which defines a number of method interfaces found in
  216. # .\o3de\Gems\AtomLyIntegration\CommonFeatures\Code\Source\Material\EditorMaterialComponentSlot.cpp
  217. label = item.GetLabel()
  218. Report.result(Tests.model_material_label, label == 'lambert0')
  219. # Asset path for lambert0 is 'objects/sphere_5lods_lambert0_11781189446760285338.fbx.azmaterial'; numbers may vary
  220. default_asset = Asset(item.GetDefaultAssetId())
  221. default_asset_path = default_asset.get_path()
  222. Report.result(
  223. Tests.model_material_asset_path,
  224. default_asset_path.startswith('testdata/objects/modelhotreload/sphere_5lods_lambert0_'))
  225. # 15. Enable the use of LOD materials
  226. material_component.set_component_property_value(AtomComponentProperties.material('Enable LOD Materials'), True)
  227. Report.result(
  228. Tests.enable_lod_materials,
  229. material_component.get_component_property_value(
  230. AtomComponentProperties.material('Enable LOD Materials')) is True)
  231. # 16. Wait for LOD Materials to indicate count 5 terminate early if container fails to reflect correct count
  232. Report.critical_result(Tests.lod_material_count, TestHelper.wait_for_condition(
  233. lambda: (material_component.get_container_count(
  234. AtomComponentProperties.material('LOD Materials')) == 5), timeout_in_seconds=5))
  235. # 17. Get the slot zero list from LOD Material
  236. item = material_component.get_container_item(
  237. AtomComponentProperties.material('LOD Materials'), key=0) # list of EditorMaterialComponentSlot
  238. active_asset = Asset(item[0].GetActiveAssetId())
  239. active_asset_path = active_asset.get_path()
  240. Report.result(
  241. Tests.lod_material_asset_path,
  242. active_asset_path.startswith('testdata/objects/modelhotreload/sphere_5lods_lambert0_'))
  243. # Setup a material for overrides in further testing
  244. material_path = os.path.join('materials', 'presets', 'pbr', 'metal_gold.azmaterial')
  245. metal_gold = Asset.find_asset_by_path(material_path)
  246. # 18. Override the slot zero LOD Material asset using EditorMaterialComponentSlot method SetAssetId
  247. item[0].SetAssetId(metal_gold.id)
  248. # check override with EditorMaterialComponentSlot method GetActiveAssetId
  249. Report.result(Tests.lod_material_set, item[0].GetActiveAssetId() == metal_gold.id)
  250. # FindMaterialAsignementId expects slot index and slot label (index for default material is -1, slot 0 is 0)
  251. assignment_id = render.MaterialComponentRequestBus(
  252. bus.Event, "FindMaterialAssignmentId", material_entity.id, 0, 'lambert0')
  253. # check override with ebus call
  254. Report.result(Tests.lod_material_set,
  255. render.MaterialComponentRequestBus(
  256. bus.Event, "GetMaterialAssetId", material_entity.id, assignment_id) == metal_gold.id)
  257. # 19. Clear the LOD Material override and confirm the active asset is the default value
  258. item[0].Clear()
  259. general.idle_wait_frames(1)
  260. active_asset = Asset(item[0].GetActiveAssetId())
  261. active_asset_path = active_asset.get_path()
  262. Report.result(
  263. Tests.lod_material_asset_path,
  264. active_asset_path.startswith('testdata/objects/modelhotreload/sphere_5lods_lambert0_'))
  265. # 20. Set the Default Material asset to an override using set component property by path
  266. material_component.set_component_property_value(
  267. AtomComponentProperties.material('Material Asset'), metal_gold.id)
  268. Report.result(
  269. Tests.default_material,
  270. material_component.get_component_property_value(
  271. AtomComponentProperties.material('Material Asset')) == metal_gold.id)
  272. # 21. Clear the assignment of a default material using MaterialComponentRequestBus and confirm null asset
  273. render.MaterialComponentRequestBus(bus.Event, "ClearMaterialAssetIdOnDefaultSlot", material_entity.id)
  274. default_material_asset_id = render.MaterialComponentRequestBus(
  275. bus.Event, "GetMaterialAssetIdOnDefaultSlot", material_entity.id)
  276. Report.result(Tests.clear_default_material, default_material_asset_id == azlmbr.asset.AssetId())
  277. # 22. Set the Default Material asset using MaterialComponentRequestBus
  278. render.MaterialComponentRequestBus(
  279. bus.Event, "SetMaterialAssetIdOnDefaultSlot", material_entity.id, metal_gold.id)
  280. default_material_asset_id = render.MaterialComponentRequestBus(
  281. bus.Event, "GetMaterialAssetIdOnDefaultSlot", material_entity.id)
  282. Report.result(Tests.default_material, default_material_asset_id == metal_gold.id)
  283. # 23. Enter/Exit game mode.
  284. TestHelper.enter_game_mode(Tests.enter_game_mode)
  285. general.idle_wait_frames(1)
  286. TestHelper.exit_game_mode(Tests.exit_game_mode)
  287. # 24. Test IsHidden.
  288. material_entity.set_visibility_state(False)
  289. Report.result(Tests.is_hidden, material_entity.is_hidden() is True)
  290. # 25. Test IsVisible.
  291. material_entity.set_visibility_state(True)
  292. general.idle_wait_frames(1)
  293. Report.result(Tests.is_visible, material_entity.is_visible() is True)
  294. # 26. Delete Material entity.
  295. material_entity.delete()
  296. Report.result(Tests.entity_deleted, not material_entity.exists())
  297. # 27. UNDO deletion.
  298. general.undo()
  299. general.idle_wait_frames(1)
  300. Report.result(Tests.deletion_undo, material_entity.exists())
  301. # 28. REDO deletion.
  302. general.redo()
  303. general.idle_wait_frames(1)
  304. Report.result(Tests.deletion_redo, not material_entity.exists())
  305. # 29. Look for errors or asserts.
  306. TestHelper.wait_for_condition(lambda: error_tracer.has_errors or error_tracer.has_asserts, 1.0)
  307. for error_info in error_tracer.errors:
  308. Report.info(f"Error: {error_info.filename} {error_info.function} | {error_info.message}")
  309. for assert_info in error_tracer.asserts:
  310. Report.info(f"Assert: {assert_info.filename} {assert_info.function} | {assert_info.message}")
  311. if __name__ == "__main__":
  312. from editor_python_test_tools.utils import Report
  313. Report.start_test(AtomEditorComponents_Material_AddedToEntity)