123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580 |
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Reflection;
- using UnityEditor.Overlays;
- using UnityEditor.SceneManagement;
- using UnityEditorInternal;
- using UnityEngine;
- using UnityEngine.Rendering;
- using UnityEngine.Rendering.Universal;
-
- namespace UnityEditor.Rendering.Universal
- {
- using Styles = UniversalRenderPipelineCameraUI.Styles;
-
- [CustomEditor(typeof(Camera))]
- [SupportedOnRenderPipeline(typeof(UniversalRenderPipelineAsset))]
- [CanEditMultipleObjects]
- class UniversalRenderPipelineCameraEditor : Editor
- {
- ReorderableList m_LayerList;
-
- CameraEditor.Settings m_Settings;
- protected CameraEditor.Settings settings => m_Settings ??= new CameraEditor.Settings(serializedObject);
-
- public Camera camera => target as Camera;
- static Camera selectedCameraInStack;
-
- List<Camera> validCameras = new List<Camera>();
- List<Camera> m_TypeErrorCameras = new List<Camera>();
- List<Camera> m_NotSupportedOverlayCameras = new List<Camera>();
- List<Camera> m_IncompatibleCameras = new List<Camera>();
- List<(Camera, UniversalRenderPipelineSerializedCamera)> m_OutputWarningCameras = new();
-
- UniversalRenderPipelineSerializedCamera m_SerializedCamera;
-
- public void OnEnable()
- {
- settings.OnEnable();
- selectedCameraInStack = null;
- m_SerializedCamera = new UniversalRenderPipelineSerializedCamera(serializedObject, settings);
-
- validCameras.Clear();
- m_TypeErrorCameras.Clear();
- m_NotSupportedOverlayCameras.Clear();
- m_IncompatibleCameras.Clear();
- m_OutputWarningCameras.Clear();
-
- UpdateCameras();
-
- Undo.undoRedoPerformed += ReconstructReferenceToAdditionalDataSO;
- }
-
- void ReconstructReferenceToAdditionalDataSO()
- {
- OnDisable();
- OnEnable();
- }
-
- void UpdateCameras()
- {
- m_SerializedCamera.Refresh();
-
- m_LayerList = new ReorderableList(m_SerializedCamera.serializedObject, m_SerializedCamera.cameras, true, true, true, true)
- {
- drawHeaderCallback = rect => EditorGUI.LabelField(rect, Styles.cameras),
- drawElementCallback = DrawElementCallback,
- onSelectCallback = SelectElement,
- onRemoveCallback = RemoveCamera,
- onCanRemoveCallback = CanRemoveCamera,
- onAddDropdownCallback = AddCameraToCameraList
- };
- }
-
- bool CanRemoveCamera(ReorderableList list) => m_SerializedCamera.numCameras > 0;
-
- void RemoveCamera(ReorderableList list)
- {
- // As multi selection is disabled, selectedIndices will only return 1 element, remove that element from the list
- if (list.selectedIndices.Any())
- {
- m_SerializedCamera.cameras.DeleteArrayElementAtIndex(list.selectedIndices.First());
- }
- else
- {
- // Nothing selected, remove the last item on the list
- ReorderableList.defaultBehaviours.DoRemoveButton(list);
- }
-
- // Force update the list as removed camera could been there
- m_TypeErrorCameras.Clear();
- m_NotSupportedOverlayCameras.Clear();
- m_IncompatibleCameras.Clear();
- m_OutputWarningCameras.Clear();
- }
-
- void SelectElement(ReorderableList list)
- {
- var element = m_SerializedCamera.cameras.GetArrayElementAtIndex(list.index);
- selectedCameraInStack = element.objectReferenceValue as Camera;
- if (Event.current.clickCount == 2)
- {
- Selection.activeObject = selectedCameraInStack;
- }
-
- EditorGUIUtility.PingObject(selectedCameraInStack);
- }
-
- void DrawElementCallback(Rect rect, int index, bool isActive, bool isFocused)
- {
- rect.height = EditorGUIUtility.singleLineHeight;
- rect.y += 1;
-
- (Camera camera, UniversalRenderPipelineSerializedCamera serializedCamera) overlayCamera = m_SerializedCamera[index];
- Camera cam = overlayCamera.camera;
-
- if (cam != null)
- {
- var baseAdditionalData = camera.GetUniversalAdditionalCameraData();
- bool outputWarning = false;
-
- // Checking if the Base Camera and the overlay cameras are of the same type.
- // If not, we report an error.
- var overlayAdditionalData = cam.GetUniversalAdditionalCameraData();
- var type = overlayAdditionalData.renderType;
-
- GUIContent errorContent = EditorGUIUtility.TrTextContent(type.GetName()); ;
-
-
- var renderer = overlayAdditionalData.scriptableRenderer;
-
- if (baseAdditionalData.scriptableRenderer.GetType() != renderer.GetType())
- {
- if (!m_IncompatibleCameras.Contains(cam))
- {
- m_IncompatibleCameras.Add(cam);
- }
-
- errorContent = EditorGUIUtility.TrTextContent("",
- $"Only cameras with compatible renderer types can be stacked. " +
- $"The camera: {cam.name} are using the renderer {renderer.GetType().Name}, " +
- $"but the base camera: {camera.name} are using {baseAdditionalData.scriptableRenderer.GetType().Name}. Will skip rendering", CoreEditorStyles.iconFail);
- }
- else if (m_IncompatibleCameras.Contains(cam))
- {
- m_IncompatibleCameras.Remove(cam);
- }
-
- // Check if the renderer on the camera we are checking does indeed support overlay camera
- // This can fail due to changing the renderer in the UI to a renderer that does not support overlay cameras
- // The UI will not stop you from changing the renderer sadly so this will have to tell the user that the
- // entry in the stack now is invalid.
- else if (!renderer.SupportsCameraStackingType(CameraRenderType.Overlay))
- {
- if (!m_NotSupportedOverlayCameras.Contains(cam))
- {
- m_NotSupportedOverlayCameras.Add(cam);
- }
-
- errorContent = EditorGUIUtility.TrTextContent("",
- $"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);
- }
- else if (m_NotSupportedOverlayCameras.Contains(cam))
- {
- m_NotSupportedOverlayCameras.Remove(cam);
- }
-
- else if (type != CameraRenderType.Overlay)
- {
- if (!m_TypeErrorCameras.Contains(cam))
- {
- m_TypeErrorCameras.Add(cam);
- }
- errorContent = EditorGUIUtility.TrTextContent(type.GetName(), $"Stack can only contain Overlay cameras. The camera: {cam.name} " +
- $"has a type {type} that is not supported. Will skip rendering.",
- CoreEditorStyles.iconFail);
- }
- else if (m_TypeErrorCameras.Contains(cam))
- {
- m_TypeErrorCameras.Remove(cam);
- }
-
- if (IsStackCameraOutputDirty(cam, overlayCamera.serializedCamera))
- {
- outputWarning = true;
- if (!m_OutputWarningCameras.Exists(c => c.Item1 == cam))
- {
- m_OutputWarningCameras.Add((cam, overlayCamera.serializedCamera));
- }
- }
- else
- {
-
- m_OutputWarningCameras.RemoveAll(c => c.Item1 == cam);
- }
-
-
- GUIContent nameContent =
- outputWarning ?
- EditorGUIUtility.TrTextContent(cam.name, "Output properties do not match base camera", CoreEditorStyles.iconWarn) :
- EditorGUIUtility.TrTextContent(cam.name);
-
- EditorGUI.BeginProperty(rect, GUIContent.none, m_SerializedCamera.cameras.GetArrayElementAtIndex(index));
- var labelWidth = EditorGUIUtility.labelWidth;
- EditorGUIUtility.labelWidth -= 20f;
-
- using (var iconSizeScope = new EditorGUIUtility.IconSizeScope(new Vector2(rect.height, rect.height)))
- {
- EditorGUI.LabelField(rect, nameContent, errorContent);
- }
-
- // Printing if Post Processing is on or not.
- var isPostActive = cam.GetUniversalAdditionalCameraData().renderPostProcessing;
- if (isPostActive)
- {
- Rect selectRect = new Rect(rect.width - 20, rect.y, 50, EditorGUIUtility.singleLineHeight);
-
- EditorGUI.LabelField(selectRect, "PP");
- }
- EditorGUI.EndProperty();
-
- EditorGUIUtility.labelWidth = labelWidth;
- }
- else
- {
- camera.GetUniversalAdditionalCameraData().UpdateCameraStack();
-
- // Need to clean out the errorCamera list here.
- m_TypeErrorCameras.Clear();
- m_OutputWarningCameras.Clear();
- }
- }
-
- // Modified version of StageHandle.FindComponentsOfType<T>()
- // This version more closely represents unity object referencing restrictions.
- // I added these restrictions:
- // - Can not reference scene object outside scene
- // - Can not reference cross scenes
- // - Can reference child objects if it is prefab
- Camera[] FindCamerasToReference(GameObject gameObject)
- {
- var scene = gameObject.scene;
-
- var inScene = !EditorUtility.IsPersistent(camera) || scene.IsValid();
- var inPreviewScene = EditorSceneManager.IsPreviewScene(scene) && scene.IsValid();
- var inCurrentScene = !EditorUtility.IsPersistent(camera) && scene.IsValid();
-
- Camera[] cameras = Resources.FindObjectsOfTypeAll<Camera>();
- List<Camera> result = new List<Camera>();
- if (!inScene)
- {
- foreach (var camera in cameras)
- {
- if (camera.transform.IsChildOf(gameObject.transform))
- result.Add(camera);
- }
- }
- else if (inPreviewScene)
- {
- foreach (var camera in cameras)
- {
- if (camera.gameObject.scene == scene)
- result.Add(camera);
- }
- }
- else if (inCurrentScene)
- {
- foreach (var camera in cameras)
- {
- if (!EditorUtility.IsPersistent(camera) && !EditorSceneManager.IsPreviewScene(camera.gameObject.scene) && camera.gameObject.scene == scene)
- result.Add(camera);
- }
- }
-
- return result.ToArray();
- }
-
- void AddCameraToCameraList(Rect rect, ReorderableList list)
- {
- // Need to do clear the list here otherwise the menu just fills up with more and more entries
- validCameras.Clear();
- // Need to get the base renderer here first
- var renderer = camera.GetUniversalAdditionalCameraData().scriptableRenderer.GetType();
- var allCameras = FindCamerasToReference(camera.gameObject);
- foreach (var camera in allCameras)
- {
- var component = camera.GetUniversalAdditionalCameraData();
- if (component != null)
- {
- if (component.renderType == CameraRenderType.Overlay &&
- component.scriptableRenderer.GetType() == renderer)
- {
- validCameras.Add(camera);
- }
- }
- }
-
- var names = new GUIContent[validCameras.Count];
- for (int i = 0; i < validCameras.Count; ++i)
- {
- names[i] = new GUIContent((i + 1) + " " + validCameras[i].name);
- }
-
- if (!validCameras.Any())
- {
- names = new GUIContent[1];
- names[0] = new GUIContent("No Overlay Cameras exist.");
- }
- EditorUtility.DisplayCustomMenu(rect, names, -1, AddCameraToCameraListMenuSelected, null);
- }
-
- void AddCameraToCameraListMenuSelected(object userData, string[] options, int selected)
- {
- if (!validCameras.Any())
- return;
-
- m_SerializedCamera.cameras.InsertArrayElementAtIndex(m_SerializedCamera.numCameras);
- m_SerializedCamera.cameras.GetArrayElementAtIndex(m_SerializedCamera.numCameras - 1).objectReferenceValue = validCameras[selected];
- m_SerializedCamera.serializedAdditionalDataObject.ApplyModifiedProperties();
-
- m_SerializedCamera.Refresh();
-
- (Camera camera, UniversalRenderPipelineSerializedCamera serializedCamera) overlayCamera = m_SerializedCamera[m_SerializedCamera.numCameras - 1];
- UpdateStackCameraOutput(overlayCamera.camera, overlayCamera.serializedCamera);
- }
-
- public void OnDisable()
- {
- Undo.undoRedoPerformed -= ReconstructReferenceToAdditionalDataSO;
- }
-
- // IsPreset is an internal API - lets reuse the usable part of this function
- // 93 is a "magic number" and does not represent a combination of other flags here
- internal static bool IsPresetEditor(UnityEditor.Editor editor)
- {
- return (int)((editor.target as Component).gameObject.hideFlags) == 93;
- }
-
- public override void OnInspectorGUI()
- {
- var rpAsset = UniversalRenderPipeline.asset;
- if (rpAsset == null)
- {
- base.OnInspectorGUI();
- return;
- }
-
- m_SerializedCamera.Update();
-
- if (IsPresetEditor(this))
- {
- UniversalRenderPipelineCameraUI.PresetInspector.Draw(m_SerializedCamera, this);
- }
- else
- {
- UniversalRenderPipelineCameraUI.Inspector.Draw(m_SerializedCamera, this);
- }
-
- m_SerializedCamera.Apply();
- }
-
- private void UpdateStackCameraToOverlay()
- {
- var additionalCameraData = selectedCameraInStack.GetUniversalAdditionalCameraData();
- if (additionalCameraData == null)
- return;
-
- if (additionalCameraData.renderType == CameraRenderType.Base)
- {
- Undo.RecordObject(additionalCameraData, Styles.inspectorOverlayCameraText);
- additionalCameraData.renderType = CameraRenderType.Overlay;
- EditorUtility.SetDirty(additionalCameraData);
- }
- }
-
- private void UpdateStackCameraOutput(Camera cam, UniversalRenderPipelineSerializedCamera serializedCamera)
- {
- if ((CameraRenderType)serializedCamera.cameraType.intValue == CameraRenderType.Base)
- return;
-
- serializedCamera.Update();
- Undo.RecordObject(camera, Styles.inspectorOverlayCameraText);
-
- bool isChanged = false;
-
- // Force same render texture
- RenderTexture targetTexture = settings.targetTexture.objectReferenceValue as RenderTexture;
- if (cam.targetTexture != targetTexture)
- {
- cam.targetTexture = targetTexture;
- isChanged = true;
- }
-
- // Force same hdr
- bool allowHDR = settings.HDR.boolValue;
- if (cam.allowHDR != allowHDR)
- {
- cam.allowHDR = allowHDR;
- isChanged = true;
- }
-
- // Force same mssa
- bool allowMSSA = settings.allowMSAA.boolValue;
- if (cam.allowMSAA != allowMSSA)
- {
- cam.allowMSAA = allowMSSA;
- isChanged = true;
- }
-
- // Force same viewport rect
- Rect rect = settings.normalizedViewPortRect.rectValue;
- if (cam.rect != rect)
- {
- cam.rect = settings.normalizedViewPortRect.rectValue;
- isChanged = true;
- }
-
- // Force same dynamic resolution
- bool allowDynamicResolution = settings.allowDynamicResolution.boolValue;
- if (serializedCamera.allowDynamicResolution.boolValue != allowDynamicResolution)
- {
- cam.allowDynamicResolution = allowDynamicResolution;
- isChanged = true;
- }
-
- // Force same target display
- int targetDisplay = settings.targetDisplay.intValue;
- if (cam.targetDisplay != targetDisplay)
- {
- cam.targetDisplay = targetDisplay;
- isChanged = true;
- }
-
- #if ENABLE_VR && ENABLE_XR_MODULE
- // Force same target display
- int selectedValue = !m_SerializedCamera.allowXRRendering.boolValue ? 0 : 1;
- int overlayCameraSelectedValue = !serializedCamera.allowXRRendering.boolValue ? 0 : 1;
- if (overlayCameraSelectedValue != selectedValue)
- {
- serializedCamera.allowXRRendering.boolValue = selectedValue == 1;
- isChanged = true;
- }
- #endif
-
- if (isChanged)
- {
- EditorUtility.SetDirty(cam);
- serializedCamera.Apply();
- }
- }
-
- private bool IsStackCameraOutputDirty(Camera cam, UniversalRenderPipelineSerializedCamera serializedCamera)
- {
- serializedCamera.Update();
-
- // Force same render texture
- RenderTexture targetTexture = settings.targetTexture.objectReferenceValue as RenderTexture;
- if (cam.targetTexture != targetTexture)
- return true;
-
- // Force same hdr
- bool allowHDR = settings.HDR.boolValue;
- if (cam.allowHDR != allowHDR)
- return true;
-
- // Force same mssa
- bool allowMSSA = settings.allowMSAA.boolValue;
- if (cam.allowMSAA != allowMSSA)
- return true;
-
- // Force same viewport rect
- Rect rect = settings.normalizedViewPortRect.rectValue;
- if (cam.rect != rect)
- return true;
-
- // Force same dynamic resolution
- bool allowDynamicResolution = settings.allowDynamicResolution.boolValue;
- if (serializedCamera.allowDynamicResolution.boolValue != allowDynamicResolution)
- return true;
-
- // Force same target display
- int targetDisplay = settings.targetDisplay.intValue;
- if (cam.targetDisplay != targetDisplay)
- return true;
-
- #if ENABLE_VR && ENABLE_XR_MODULE
- // Force same target display
- int selectedValue = !m_SerializedCamera.allowXRRendering.boolValue ? 0 : 1;
- int overlayCameraSelectedValue = !serializedCamera.allowXRRendering.boolValue ? 0 : 1;
- if (overlayCameraSelectedValue != selectedValue)
- return true;
- #endif
-
- return false;
- }
-
- internal void DrawStackSettings()
- {
- if (m_SerializedCamera.cameras.hasMultipleDifferentValues)
- {
- EditorGUILayout.HelpBox("Cannot multi edit stack of multiple cameras.", MessageType.Info);
- EditorGUILayout.EndFoldoutHeaderGroup();
- return;
- }
-
- bool cameraStackingAvailable = m_SerializedCamera
- .camerasAdditionalData
- .All(c => c.scriptableRenderer?.SupportsCameraStackingType(CameraRenderType.Base) ?? false);
-
- if (!cameraStackingAvailable)
- {
- EditorGUILayout.HelpBox("The renderer used by this camera doesn't support camera stacking. Only Base camera will render.", MessageType.Warning);
- return;
- }
-
- EditorGUILayout.Space();
-
- m_LayerList.DoLayoutList();
- m_SerializedCamera.Apply();
-
- EditorGUI.indentLevel--;
-
- bool oldRichTextSupport = EditorStyles.helpBox.richText;
- EditorStyles.helpBox.richText = true;
-
- if (selectedCameraInStack != null)
- {
- if (m_IncompatibleCameras.Any())
- {
- if (m_IncompatibleCameras.Contains(selectedCameraInStack))
- {
- var message = "This camera does not use the same type of renderer as the Base camera.";
- EditorGUILayout.HelpBox(message, MessageType.Error);
- }
- }
-
- if (m_NotSupportedOverlayCameras.Any())
- {
- if (m_NotSupportedOverlayCameras.Contains(selectedCameraInStack))
- {
- var message = "This camera uses a renderer which does not support Overlays in it's current state.";
- EditorGUILayout.HelpBox(message, MessageType.Error);
- }
- }
-
- if (m_TypeErrorCameras.Any())
- {
- if (m_TypeErrorCameras.Contains(selectedCameraInStack))
- {
- var message = "The type of this Camera must be Overlay render type.";
- CoreEditorUtils.DrawFixMeBox(message, MessageType.Error, UpdateStackCameraToOverlay);
- }
- }
-
- if (m_OutputWarningCameras.Any())
- {
- var camIndex = m_OutputWarningCameras.FindIndex(c => c.Item1 == selectedCameraInStack);
- if (camIndex != -1)
- {
- var message = "The output properties of this Camera do not match the output properties.";
- if ((CameraRenderType)m_OutputWarningCameras[camIndex].Item2.cameraType.intValue == CameraRenderType.Base)
- {
- EditorGUILayout.HelpBox(message, MessageType.Warning);
- }
- else
- {
- CoreEditorUtils.DrawFixMeBox(message, MessageType.Warning,
- () => UpdateStackCameraOutput(m_OutputWarningCameras[camIndex].Item1, m_OutputWarningCameras[camIndex].Item2));
- }
- }
- }
- }
-
- EditorStyles.helpBox.richText = oldRichTextSupport;
- EditorGUI.indentLevel++;
-
- EditorGUILayout.Space();
- }
- }
- }
|