暫無描述
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

UniversalRenderPipelineCameraEditor.cs 23KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Reflection;
  5. using UnityEditor.Overlays;
  6. using UnityEditor.SceneManagement;
  7. using UnityEditorInternal;
  8. using UnityEngine;
  9. using UnityEngine.Rendering;
  10. using UnityEngine.Rendering.Universal;
  11. namespace UnityEditor.Rendering.Universal
  12. {
  13. using Styles = UniversalRenderPipelineCameraUI.Styles;
  14. [CustomEditor(typeof(Camera))]
  15. [SupportedOnRenderPipeline(typeof(UniversalRenderPipelineAsset))]
  16. [CanEditMultipleObjects]
  17. class UniversalRenderPipelineCameraEditor : Editor
  18. {
  19. ReorderableList m_LayerList;
  20. CameraEditor.Settings m_Settings;
  21. protected CameraEditor.Settings settings => m_Settings ??= new CameraEditor.Settings(serializedObject);
  22. public Camera camera => target as Camera;
  23. static Camera selectedCameraInStack;
  24. List<Camera> validCameras = new List<Camera>();
  25. List<Camera> m_TypeErrorCameras = new List<Camera>();
  26. List<Camera> m_NotSupportedOverlayCameras = new List<Camera>();
  27. List<Camera> m_IncompatibleCameras = new List<Camera>();
  28. List<(Camera, UniversalRenderPipelineSerializedCamera)> m_OutputWarningCameras = new();
  29. UniversalRenderPipelineSerializedCamera m_SerializedCamera;
  30. public void OnEnable()
  31. {
  32. settings.OnEnable();
  33. selectedCameraInStack = null;
  34. m_SerializedCamera = new UniversalRenderPipelineSerializedCamera(serializedObject, settings);
  35. validCameras.Clear();
  36. m_TypeErrorCameras.Clear();
  37. m_NotSupportedOverlayCameras.Clear();
  38. m_IncompatibleCameras.Clear();
  39. m_OutputWarningCameras.Clear();
  40. UpdateCameras();
  41. Undo.undoRedoPerformed += ReconstructReferenceToAdditionalDataSO;
  42. }
  43. void ReconstructReferenceToAdditionalDataSO()
  44. {
  45. OnDisable();
  46. OnEnable();
  47. }
  48. void UpdateCameras()
  49. {
  50. m_SerializedCamera.Refresh();
  51. m_LayerList = new ReorderableList(m_SerializedCamera.serializedObject, m_SerializedCamera.cameras, true, true, true, true)
  52. {
  53. drawHeaderCallback = rect => EditorGUI.LabelField(rect, Styles.cameras),
  54. drawElementCallback = DrawElementCallback,
  55. onSelectCallback = SelectElement,
  56. onRemoveCallback = RemoveCamera,
  57. onCanRemoveCallback = CanRemoveCamera,
  58. onAddDropdownCallback = AddCameraToCameraList
  59. };
  60. }
  61. bool CanRemoveCamera(ReorderableList list) => m_SerializedCamera.numCameras > 0;
  62. void RemoveCamera(ReorderableList list)
  63. {
  64. // As multi selection is disabled, selectedIndices will only return 1 element, remove that element from the list
  65. if (list.selectedIndices.Any())
  66. {
  67. m_SerializedCamera.cameras.DeleteArrayElementAtIndex(list.selectedIndices.First());
  68. }
  69. else
  70. {
  71. // Nothing selected, remove the last item on the list
  72. ReorderableList.defaultBehaviours.DoRemoveButton(list);
  73. }
  74. // Force update the list as removed camera could been there
  75. m_TypeErrorCameras.Clear();
  76. m_NotSupportedOverlayCameras.Clear();
  77. m_IncompatibleCameras.Clear();
  78. m_OutputWarningCameras.Clear();
  79. }
  80. void SelectElement(ReorderableList list)
  81. {
  82. var element = m_SerializedCamera.cameras.GetArrayElementAtIndex(list.index);
  83. selectedCameraInStack = element.objectReferenceValue as Camera;
  84. if (Event.current.clickCount == 2)
  85. {
  86. Selection.activeObject = selectedCameraInStack;
  87. }
  88. EditorGUIUtility.PingObject(selectedCameraInStack);
  89. }
  90. void DrawElementCallback(Rect rect, int index, bool isActive, bool isFocused)
  91. {
  92. rect.height = EditorGUIUtility.singleLineHeight;
  93. rect.y += 1;
  94. (Camera camera, UniversalRenderPipelineSerializedCamera serializedCamera) overlayCamera = m_SerializedCamera[index];
  95. Camera cam = overlayCamera.camera;
  96. if (cam != null)
  97. {
  98. var baseAdditionalData = camera.GetUniversalAdditionalCameraData();
  99. bool outputWarning = false;
  100. // Checking if the Base Camera and the overlay cameras are of the same type.
  101. // If not, we report an error.
  102. var overlayAdditionalData = cam.GetUniversalAdditionalCameraData();
  103. var type = overlayAdditionalData.renderType;
  104. GUIContent errorContent = EditorGUIUtility.TrTextContent(type.GetName()); ;
  105. var renderer = overlayAdditionalData.scriptableRenderer;
  106. if (baseAdditionalData.scriptableRenderer.GetType() != renderer.GetType())
  107. {
  108. if (!m_IncompatibleCameras.Contains(cam))
  109. {
  110. m_IncompatibleCameras.Add(cam);
  111. }
  112. errorContent = EditorGUIUtility.TrTextContent("",
  113. $"Only cameras with compatible renderer types can be stacked. " +
  114. $"The camera: {cam.name} are using the renderer {renderer.GetType().Name}, " +
  115. $"but the base camera: {camera.name} are using {baseAdditionalData.scriptableRenderer.GetType().Name}. Will skip rendering", CoreEditorStyles.iconFail);
  116. }
  117. else if (m_IncompatibleCameras.Contains(cam))
  118. {
  119. m_IncompatibleCameras.Remove(cam);
  120. }
  121. // Check if the renderer on the camera we are checking does indeed support overlay camera
  122. // This can fail due to changing the renderer in the UI to a renderer that does not support overlay cameras
  123. // The UI will not stop you from changing the renderer sadly so this will have to tell the user that the
  124. // entry in the stack now is invalid.
  125. else if (!renderer.SupportsCameraStackingType(CameraRenderType.Overlay))
  126. {
  127. if (!m_NotSupportedOverlayCameras.Contains(cam))
  128. {
  129. m_NotSupportedOverlayCameras.Add(cam);
  130. }
  131. errorContent = EditorGUIUtility.TrTextContent("",
  132. $"The camera: {cam.name} is using a renderer of type {renderer.GetType().Name} which does not support Overlay cameras in it's current state.", CoreEditorStyles.iconFail);
  133. }
  134. else if (m_NotSupportedOverlayCameras.Contains(cam))
  135. {
  136. m_NotSupportedOverlayCameras.Remove(cam);
  137. }
  138. else if (type != CameraRenderType.Overlay)
  139. {
  140. if (!m_TypeErrorCameras.Contains(cam))
  141. {
  142. m_TypeErrorCameras.Add(cam);
  143. }
  144. errorContent = EditorGUIUtility.TrTextContent(type.GetName(), $"Stack can only contain Overlay cameras. The camera: {cam.name} " +
  145. $"has a type {type} that is not supported. Will skip rendering.",
  146. CoreEditorStyles.iconFail);
  147. }
  148. else if (m_TypeErrorCameras.Contains(cam))
  149. {
  150. m_TypeErrorCameras.Remove(cam);
  151. }
  152. if (IsStackCameraOutputDirty(cam, overlayCamera.serializedCamera))
  153. {
  154. outputWarning = true;
  155. if (!m_OutputWarningCameras.Exists(c => c.Item1 == cam))
  156. {
  157. m_OutputWarningCameras.Add((cam, overlayCamera.serializedCamera));
  158. }
  159. }
  160. else
  161. {
  162. m_OutputWarningCameras.RemoveAll(c => c.Item1 == cam);
  163. }
  164. GUIContent nameContent =
  165. outputWarning ?
  166. EditorGUIUtility.TrTextContent(cam.name, "Output properties do not match base camera", CoreEditorStyles.iconWarn) :
  167. EditorGUIUtility.TrTextContent(cam.name);
  168. EditorGUI.BeginProperty(rect, GUIContent.none, m_SerializedCamera.cameras.GetArrayElementAtIndex(index));
  169. var labelWidth = EditorGUIUtility.labelWidth;
  170. EditorGUIUtility.labelWidth -= 20f;
  171. using (var iconSizeScope = new EditorGUIUtility.IconSizeScope(new Vector2(rect.height, rect.height)))
  172. {
  173. EditorGUI.LabelField(rect, nameContent, errorContent);
  174. }
  175. // Printing if Post Processing is on or not.
  176. var isPostActive = cam.GetUniversalAdditionalCameraData().renderPostProcessing;
  177. if (isPostActive)
  178. {
  179. Rect selectRect = new Rect(rect.width - 20, rect.y, 50, EditorGUIUtility.singleLineHeight);
  180. EditorGUI.LabelField(selectRect, "PP");
  181. }
  182. EditorGUI.EndProperty();
  183. EditorGUIUtility.labelWidth = labelWidth;
  184. }
  185. else
  186. {
  187. camera.GetUniversalAdditionalCameraData().UpdateCameraStack();
  188. // Need to clean out the errorCamera list here.
  189. m_TypeErrorCameras.Clear();
  190. m_OutputWarningCameras.Clear();
  191. }
  192. }
  193. // Modified version of StageHandle.FindComponentsOfType<T>()
  194. // This version more closely represents unity object referencing restrictions.
  195. // I added these restrictions:
  196. // - Can not reference scene object outside scene
  197. // - Can not reference cross scenes
  198. // - Can reference child objects if it is prefab
  199. Camera[] FindCamerasToReference(GameObject gameObject)
  200. {
  201. var scene = gameObject.scene;
  202. var inScene = !EditorUtility.IsPersistent(camera) || scene.IsValid();
  203. var inPreviewScene = EditorSceneManager.IsPreviewScene(scene) && scene.IsValid();
  204. var inCurrentScene = !EditorUtility.IsPersistent(camera) && scene.IsValid();
  205. Camera[] cameras = Resources.FindObjectsOfTypeAll<Camera>();
  206. List<Camera> result = new List<Camera>();
  207. if (!inScene)
  208. {
  209. foreach (var camera in cameras)
  210. {
  211. if (camera.transform.IsChildOf(gameObject.transform))
  212. result.Add(camera);
  213. }
  214. }
  215. else if (inPreviewScene)
  216. {
  217. foreach (var camera in cameras)
  218. {
  219. if (camera.gameObject.scene == scene)
  220. result.Add(camera);
  221. }
  222. }
  223. else if (inCurrentScene)
  224. {
  225. foreach (var camera in cameras)
  226. {
  227. if (!EditorUtility.IsPersistent(camera) && !EditorSceneManager.IsPreviewScene(camera.gameObject.scene) && camera.gameObject.scene == scene)
  228. result.Add(camera);
  229. }
  230. }
  231. return result.ToArray();
  232. }
  233. void AddCameraToCameraList(Rect rect, ReorderableList list)
  234. {
  235. // Need to do clear the list here otherwise the menu just fills up with more and more entries
  236. validCameras.Clear();
  237. // Need to get the base renderer here first
  238. var renderer = camera.GetUniversalAdditionalCameraData().scriptableRenderer.GetType();
  239. var allCameras = FindCamerasToReference(camera.gameObject);
  240. foreach (var camera in allCameras)
  241. {
  242. var component = camera.GetUniversalAdditionalCameraData();
  243. if (component != null)
  244. {
  245. if (component.renderType == CameraRenderType.Overlay &&
  246. component.scriptableRenderer.GetType() == renderer)
  247. {
  248. validCameras.Add(camera);
  249. }
  250. }
  251. }
  252. var names = new GUIContent[validCameras.Count];
  253. for (int i = 0; i < validCameras.Count; ++i)
  254. {
  255. names[i] = new GUIContent((i + 1) + " " + validCameras[i].name);
  256. }
  257. if (!validCameras.Any())
  258. {
  259. names = new GUIContent[1];
  260. names[0] = new GUIContent("No Overlay Cameras exist.");
  261. }
  262. EditorUtility.DisplayCustomMenu(rect, names, -1, AddCameraToCameraListMenuSelected, null);
  263. }
  264. void AddCameraToCameraListMenuSelected(object userData, string[] options, int selected)
  265. {
  266. if (!validCameras.Any())
  267. return;
  268. m_SerializedCamera.cameras.InsertArrayElementAtIndex(m_SerializedCamera.numCameras);
  269. m_SerializedCamera.cameras.GetArrayElementAtIndex(m_SerializedCamera.numCameras - 1).objectReferenceValue = validCameras[selected];
  270. m_SerializedCamera.serializedAdditionalDataObject.ApplyModifiedProperties();
  271. m_SerializedCamera.Refresh();
  272. (Camera camera, UniversalRenderPipelineSerializedCamera serializedCamera) overlayCamera = m_SerializedCamera[m_SerializedCamera.numCameras - 1];
  273. UpdateStackCameraOutput(overlayCamera.camera, overlayCamera.serializedCamera);
  274. }
  275. public void OnDisable()
  276. {
  277. Undo.undoRedoPerformed -= ReconstructReferenceToAdditionalDataSO;
  278. }
  279. // IsPreset is an internal API - lets reuse the usable part of this function
  280. // 93 is a "magic number" and does not represent a combination of other flags here
  281. internal static bool IsPresetEditor(UnityEditor.Editor editor)
  282. {
  283. return (int)((editor.target as Component).gameObject.hideFlags) == 93;
  284. }
  285. public override void OnInspectorGUI()
  286. {
  287. var rpAsset = UniversalRenderPipeline.asset;
  288. if (rpAsset == null)
  289. {
  290. base.OnInspectorGUI();
  291. return;
  292. }
  293. m_SerializedCamera.Update();
  294. if (IsPresetEditor(this))
  295. {
  296. UniversalRenderPipelineCameraUI.PresetInspector.Draw(m_SerializedCamera, this);
  297. }
  298. else
  299. {
  300. UniversalRenderPipelineCameraUI.Inspector.Draw(m_SerializedCamera, this);
  301. }
  302. m_SerializedCamera.Apply();
  303. }
  304. private void UpdateStackCameraToOverlay()
  305. {
  306. var additionalCameraData = selectedCameraInStack.GetUniversalAdditionalCameraData();
  307. if (additionalCameraData == null)
  308. return;
  309. if (additionalCameraData.renderType == CameraRenderType.Base)
  310. {
  311. Undo.RecordObject(additionalCameraData, Styles.inspectorOverlayCameraText);
  312. additionalCameraData.renderType = CameraRenderType.Overlay;
  313. EditorUtility.SetDirty(additionalCameraData);
  314. }
  315. }
  316. private void UpdateStackCameraOutput(Camera cam, UniversalRenderPipelineSerializedCamera serializedCamera)
  317. {
  318. if ((CameraRenderType)serializedCamera.cameraType.intValue == CameraRenderType.Base)
  319. return;
  320. serializedCamera.Update();
  321. Undo.RecordObject(camera, Styles.inspectorOverlayCameraText);
  322. bool isChanged = false;
  323. // Force same render texture
  324. RenderTexture targetTexture = settings.targetTexture.objectReferenceValue as RenderTexture;
  325. if (cam.targetTexture != targetTexture)
  326. {
  327. cam.targetTexture = targetTexture;
  328. isChanged = true;
  329. }
  330. // Force same hdr
  331. bool allowHDR = settings.HDR.boolValue;
  332. if (cam.allowHDR != allowHDR)
  333. {
  334. cam.allowHDR = allowHDR;
  335. isChanged = true;
  336. }
  337. // Force same mssa
  338. bool allowMSSA = settings.allowMSAA.boolValue;
  339. if (cam.allowMSAA != allowMSSA)
  340. {
  341. cam.allowMSAA = allowMSSA;
  342. isChanged = true;
  343. }
  344. // Force same viewport rect
  345. Rect rect = settings.normalizedViewPortRect.rectValue;
  346. if (cam.rect != rect)
  347. {
  348. cam.rect = settings.normalizedViewPortRect.rectValue;
  349. isChanged = true;
  350. }
  351. // Force same dynamic resolution
  352. bool allowDynamicResolution = settings.allowDynamicResolution.boolValue;
  353. if (serializedCamera.allowDynamicResolution.boolValue != allowDynamicResolution)
  354. {
  355. cam.allowDynamicResolution = allowDynamicResolution;
  356. isChanged = true;
  357. }
  358. // Force same target display
  359. int targetDisplay = settings.targetDisplay.intValue;
  360. if (cam.targetDisplay != targetDisplay)
  361. {
  362. cam.targetDisplay = targetDisplay;
  363. isChanged = true;
  364. }
  365. #if ENABLE_VR && ENABLE_XR_MODULE
  366. // Force same target display
  367. int selectedValue = !m_SerializedCamera.allowXRRendering.boolValue ? 0 : 1;
  368. int overlayCameraSelectedValue = !serializedCamera.allowXRRendering.boolValue ? 0 : 1;
  369. if (overlayCameraSelectedValue != selectedValue)
  370. {
  371. serializedCamera.allowXRRendering.boolValue = selectedValue == 1;
  372. isChanged = true;
  373. }
  374. #endif
  375. if (isChanged)
  376. {
  377. EditorUtility.SetDirty(cam);
  378. serializedCamera.Apply();
  379. }
  380. }
  381. private bool IsStackCameraOutputDirty(Camera cam, UniversalRenderPipelineSerializedCamera serializedCamera)
  382. {
  383. serializedCamera.Update();
  384. // Force same render texture
  385. RenderTexture targetTexture = settings.targetTexture.objectReferenceValue as RenderTexture;
  386. if (cam.targetTexture != targetTexture)
  387. return true;
  388. // Force same hdr
  389. bool allowHDR = settings.HDR.boolValue;
  390. if (cam.allowHDR != allowHDR)
  391. return true;
  392. // Force same mssa
  393. bool allowMSSA = settings.allowMSAA.boolValue;
  394. if (cam.allowMSAA != allowMSSA)
  395. return true;
  396. // Force same viewport rect
  397. Rect rect = settings.normalizedViewPortRect.rectValue;
  398. if (cam.rect != rect)
  399. return true;
  400. // Force same dynamic resolution
  401. bool allowDynamicResolution = settings.allowDynamicResolution.boolValue;
  402. if (serializedCamera.allowDynamicResolution.boolValue != allowDynamicResolution)
  403. return true;
  404. // Force same target display
  405. int targetDisplay = settings.targetDisplay.intValue;
  406. if (cam.targetDisplay != targetDisplay)
  407. return true;
  408. #if ENABLE_VR && ENABLE_XR_MODULE
  409. // Force same target display
  410. int selectedValue = !m_SerializedCamera.allowXRRendering.boolValue ? 0 : 1;
  411. int overlayCameraSelectedValue = !serializedCamera.allowXRRendering.boolValue ? 0 : 1;
  412. if (overlayCameraSelectedValue != selectedValue)
  413. return true;
  414. #endif
  415. return false;
  416. }
  417. internal void DrawStackSettings()
  418. {
  419. if (m_SerializedCamera.cameras.hasMultipleDifferentValues)
  420. {
  421. EditorGUILayout.HelpBox("Cannot multi edit stack of multiple cameras.", MessageType.Info);
  422. EditorGUILayout.EndFoldoutHeaderGroup();
  423. return;
  424. }
  425. bool cameraStackingAvailable = m_SerializedCamera
  426. .camerasAdditionalData
  427. .All(c => c.scriptableRenderer?.SupportsCameraStackingType(CameraRenderType.Base) ?? false);
  428. if (!cameraStackingAvailable)
  429. {
  430. EditorGUILayout.HelpBox("The renderer used by this camera doesn't support camera stacking. Only Base camera will render.", MessageType.Warning);
  431. return;
  432. }
  433. EditorGUILayout.Space();
  434. m_LayerList.DoLayoutList();
  435. m_SerializedCamera.Apply();
  436. EditorGUI.indentLevel--;
  437. bool oldRichTextSupport = EditorStyles.helpBox.richText;
  438. EditorStyles.helpBox.richText = true;
  439. if (selectedCameraInStack != null)
  440. {
  441. if (m_IncompatibleCameras.Any())
  442. {
  443. if (m_IncompatibleCameras.Contains(selectedCameraInStack))
  444. {
  445. var message = "This camera does not use the same type of renderer as the Base camera.";
  446. EditorGUILayout.HelpBox(message, MessageType.Error);
  447. }
  448. }
  449. if (m_NotSupportedOverlayCameras.Any())
  450. {
  451. if (m_NotSupportedOverlayCameras.Contains(selectedCameraInStack))
  452. {
  453. var message = "This camera uses a renderer which does not support Overlays in it's current state.";
  454. EditorGUILayout.HelpBox(message, MessageType.Error);
  455. }
  456. }
  457. if (m_TypeErrorCameras.Any())
  458. {
  459. if (m_TypeErrorCameras.Contains(selectedCameraInStack))
  460. {
  461. var message = "The type of this Camera must be Overlay render type.";
  462. CoreEditorUtils.DrawFixMeBox(message, MessageType.Error, UpdateStackCameraToOverlay);
  463. }
  464. }
  465. if (m_OutputWarningCameras.Any())
  466. {
  467. var camIndex = m_OutputWarningCameras.FindIndex(c => c.Item1 == selectedCameraInStack);
  468. if (camIndex != -1)
  469. {
  470. var message = "The output properties of this Camera do not match the output properties.";
  471. if ((CameraRenderType)m_OutputWarningCameras[camIndex].Item2.cameraType.intValue == CameraRenderType.Base)
  472. {
  473. EditorGUILayout.HelpBox(message, MessageType.Warning);
  474. }
  475. else
  476. {
  477. CoreEditorUtils.DrawFixMeBox(message, MessageType.Warning,
  478. () => UpdateStackCameraOutput(m_OutputWarningCameras[camIndex].Item1, m_OutputWarningCameras[camIndex].Item2));
  479. }
  480. }
  481. }
  482. }
  483. EditorStyles.helpBox.richText = oldRichTextSupport;
  484. EditorGUI.indentLevel++;
  485. EditorGUILayout.Space();
  486. }
  487. }
  488. }