material_editor_utils.py 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  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. import azlmbr.materialeditor will fail with a ModuleNotFound error when using this script with Editor.exe
  6. This is because azlmbr.materialeditor only binds to MaterialEditor.exe and not Editor.exe
  7. You need to launch this script with MaterialEditor.exe in order for azlmbr.materialeditor to appear.
  8. """
  9. import os
  10. import sys
  11. import time
  12. import azlmbr.atom
  13. import azlmbr.atomtools as atomtools
  14. import azlmbr.materialeditor as materialeditor
  15. import azlmbr.bus as bus
  16. def is_close(actual, expected, buffer=sys.float_info.min):
  17. """
  18. :param actual: actual value
  19. :param expected: expected value
  20. :param buffer: acceptable variation from expected
  21. :return: bool
  22. """
  23. return abs(actual - expected) < buffer
  24. def compare_colors(color1, color2, buffer=0.00001):
  25. """
  26. Compares the red, green and blue properties of a color allowing a slight variance of buffer
  27. :param color1: first color to compare
  28. :param color2: second color
  29. :param buffer: allowed variance in individual color value
  30. :return: bool
  31. """
  32. return (
  33. is_close(color1.r, color2.r, buffer)
  34. and is_close(color1.g, color2.g, buffer)
  35. and is_close(color1.b, color2.b, buffer)
  36. )
  37. def open_material(file_path):
  38. """
  39. :return: uuid of material document opened
  40. """
  41. return azlmbr.atomtools.AtomToolsDocumentSystemRequestBus(bus.Broadcast, "OpenDocument", file_path)
  42. def is_open(document_id):
  43. """
  44. :return: bool
  45. """
  46. return azlmbr.atomtools.AtomToolsDocumentRequestBus(bus.Event, "IsOpen", document_id)
  47. def save_document(document_id):
  48. """
  49. :return: bool success
  50. """
  51. return azlmbr.atomtools.AtomToolsDocumentSystemRequestBus(bus.Broadcast, "SaveDocument", document_id)
  52. def save_document_as_copy(document_id, target_path):
  53. """
  54. :return: bool success
  55. """
  56. return azlmbr.atomtools.AtomToolsDocumentSystemRequestBus(
  57. bus.Broadcast, "SaveDocumentAsCopy", document_id, target_path
  58. )
  59. def save_document_as_child(document_id, target_path):
  60. """
  61. :return: bool success
  62. """
  63. return azlmbr.atomtools.AtomToolsDocumentSystemRequestBus(
  64. bus.Broadcast, "SaveDocumentAsChild", document_id, target_path
  65. )
  66. def save_all():
  67. """
  68. :return: bool success
  69. """
  70. return azlmbr.atomtools.AtomToolsDocumentSystemRequestBus(bus.Broadcast, "SaveAllDocuments")
  71. def close_document(document_id):
  72. """
  73. :return: bool success
  74. """
  75. return azlmbr.atomtools.AtomToolsDocumentSystemRequestBus(bus.Broadcast, "CloseDocument", document_id)
  76. def close_all_documents():
  77. """
  78. :return: bool success
  79. """
  80. return azlmbr.atomtools.AtomToolsDocumentSystemRequestBus(bus.Broadcast, "CloseAllDocuments")
  81. def close_all_except_selected(document_id):
  82. """
  83. :return: bool success
  84. """
  85. return azlmbr.atomtools.AtomToolsDocumentSystemRequestBus(bus.Broadcast, "CloseAllDocumentsExcept", document_id)
  86. def get_property(document_id, property_name):
  87. """
  88. :return: property value or invalid value if the document is not open or the property_name can't be found
  89. """
  90. return azlmbr.atomtools.AtomToolsDocumentRequestBus(bus.Event, "GetPropertyValue", document_id, property_name)
  91. def set_property(document_id, property_name, value):
  92. azlmbr.atomtools.AtomToolsDocumentRequestBus(bus.Event, "SetPropertyValue", document_id, property_name, value)
  93. def is_pane_visible(pane_name):
  94. """
  95. :return: bool
  96. """
  97. return atomtools.AtomToolsWindowRequestBus(bus.Broadcast, "IsDockWidgetVisible", pane_name)
  98. def set_pane_visibility(pane_name, value):
  99. atomtools.AtomToolsWindowRequestBus(bus.Broadcast, "SetDockWidgetVisible", pane_name, value)
  100. def select_lighting_config(config_name):
  101. azlmbr.materialeditor.MaterialViewportRequestBus(azlmbr.bus.Broadcast, "SelectLightingPresetByName", config_name)
  102. def set_grid_enable_disable(value):
  103. azlmbr.materialeditor.MaterialViewportRequestBus(azlmbr.bus.Broadcast, "SetGridEnabled", value)
  104. def get_grid_enable_disable():
  105. """
  106. :return: bool
  107. """
  108. return azlmbr.materialeditor.MaterialViewportRequestBus(azlmbr.bus.Broadcast, "GetGridEnabled")
  109. def set_shadowcatcher_enable_disable(value):
  110. azlmbr.materialeditor.MaterialViewportRequestBus(azlmbr.bus.Broadcast, "SetShadowCatcherEnabled", value)
  111. def get_shadowcatcher_enable_disable():
  112. """
  113. :return: bool
  114. """
  115. return azlmbr.materialeditor.MaterialViewportRequestBus(azlmbr.bus.Broadcast, "GetShadowCatcherEnabled")
  116. def select_model_config(configname):
  117. azlmbr.materialeditor.MaterialViewportRequestBus(azlmbr.bus.Broadcast, "SelectModelPresetByName", configname)
  118. def wait_for_condition(function, timeout_in_seconds=1.0):
  119. # type: (function, float) -> bool
  120. """
  121. Function to run until it returns True or timeout is reached
  122. the function can have no parameters and
  123. waiting idle__wait_* is handled here not in the function
  124. :param function: a function that returns a boolean indicating a desired condition is achieved
  125. :param timeout_in_seconds: when reached, function execution is abandoned and False is returned
  126. """
  127. with Timeout(timeout_in_seconds) as t:
  128. while True:
  129. try:
  130. azlmbr.atomtools.general.idle_wait_frames(1)
  131. except Exception:
  132. print("WARNING: Couldn't wait for frame")
  133. if t.timed_out:
  134. return False
  135. ret = function()
  136. if not isinstance(ret, bool):
  137. raise TypeError("return value for wait_for_condition function must be a bool")
  138. if ret:
  139. return True
  140. class Timeout:
  141. # type: (float) -> None
  142. """
  143. contextual timeout
  144. :param seconds: float seconds to allow before timed_out is True
  145. """
  146. def __init__(self, seconds):
  147. self.seconds = seconds
  148. def __enter__(self):
  149. self.die_after = time.time() + self.seconds
  150. return self
  151. def __exit__(self, type, value, traceback):
  152. pass
  153. @property
  154. def timed_out(self):
  155. return time.time() > self.die_after
  156. screenshotsFolder = os.path.join(azlmbr.paths.products, "Screenshots")
  157. class ScreenshotHelper:
  158. """
  159. A helper to capture screenshots and wait for them.
  160. """
  161. def __init__(self, idle_wait_frames_callback):
  162. super().__init__()
  163. self.done = False
  164. self.capturedScreenshot = False
  165. self.max_frames_to_wait = 60
  166. self.idle_wait_frames_callback = idle_wait_frames_callback
  167. def capture_screenshot_blocking(self, filename):
  168. """
  169. Capture a screenshot and block the execution until the screenshot has been written to the disk.
  170. """
  171. self.handler = azlmbr.atom.FrameCaptureNotificationBusHandler()
  172. self.handler.connect()
  173. self.handler.add_callback("OnCaptureFinished", self.on_screenshot_captured)
  174. self.done = False
  175. self.capturedScreenshot = False
  176. success = azlmbr.atom.FrameCaptureRequestBus(azlmbr.bus.Broadcast, "CaptureScreenshot", filename)
  177. if success:
  178. self.wait_until_screenshot()
  179. print("Screenshot taken.")
  180. else:
  181. print("screenshot failed")
  182. return self.capturedScreenshot
  183. def on_screenshot_captured(self, parameters):
  184. # the parameters come in as a tuple
  185. if parameters[0]:
  186. print("screenshot saved: {}".format(parameters[1]))
  187. self.capturedScreenshot = True
  188. else:
  189. print("screenshot failed: {}".format(parameters[1]))
  190. self.done = True
  191. self.handler.disconnect()
  192. def wait_until_screenshot(self):
  193. frames_waited = 0
  194. while self.done == False:
  195. self.idle_wait_frames_callback(1)
  196. if frames_waited > self.max_frames_to_wait:
  197. print("timeout while waiting for the screenshot to be written")
  198. self.handler.disconnect()
  199. break
  200. else:
  201. frames_waited = frames_waited + 1
  202. print("(waited {} frames)".format(frames_waited))
  203. def capture_screenshot(file_path):
  204. return ScreenshotHelper(azlmbr.atomtools.general.idle_wait_frames).capture_screenshot_blocking(
  205. os.path.join(file_path)
  206. )