using System; using UnityEngine; using UnityEngine.Rendering.Universal; namespace UnityEditor.Rendering.Universal { using CED = CoreEditorDrawer; static partial class UniversalRenderPipelineCameraUI { [URPHelpURL("urp/camera-component-reference")] public enum Expandable { /// Projection Projection = 1 << 0, /// Physical Physical = 1 << 1, /// Output Output = 1 << 2, /// Orthographic Orthographic = 1 << 3, /// RenderLoop RenderLoop = 1 << 4, /// Rendering Rendering = 1 << 5, /// Environment Environment = 1 << 6, /// Stack Stack = 1 << 7, } public enum ExpandableAdditional { /// Rendering Rendering = 1 << 0, } static readonly ExpandedState k_ExpandedState = new(Expandable.Projection, "URP"); static readonly AdditionalPropertiesState k_ExpandedAdditionalState = new(0, "URP"); public static readonly CED.IDrawer SectionProjectionSettings = CED.FoldoutGroup( CameraUI.Styles.projectionSettingsHeaderContent, Expandable.Projection, k_ExpandedState, FoldoutOption.Indent, CED.Group( DrawerProjection ), PhysicalCamera.Drawer ); public static readonly CED.IDrawer SectionStackSettings = CED.Conditional( (serialized, editor) => (CameraRenderType)serialized.cameraType.intValue == CameraRenderType.Base, CED.FoldoutGroup(Styles.stackSettingsText, Expandable.Stack, k_ExpandedState, FoldoutOption.Indent, CED.Group(DrawerStackCameras))); public static readonly CED.IDrawer[] Inspector = { CED.Group( PrepareTileOnlyModeWarning, DrawerCameraType ), SectionProjectionSettings, Rendering.Drawer, SectionStackSettings, Environment.Drawer, Output.Drawer }; static void DrawerProjection(UniversalRenderPipelineSerializedCamera p, Editor owner) { var camera = p.serializedObject.targetObject as Camera; bool pixelPerfectEnabled = camera.TryGetComponent(out var pixelPerfectCamera) && pixelPerfectCamera.enabled; if (pixelPerfectEnabled) EditorGUILayout.HelpBox(Styles.pixelPerfectInfo, MessageType.Info); #if XR_MANAGEMENT_4_0_1_OR_NEWER && ENABLE_VR && ENABLE_XR_MODULE if (p.baseCameraSettings.orthographic.boolValue && p.allowXRRendering.boolValue) { var buildTargetGroup = BuildPipeline.GetBuildTargetGroup(EditorUserBuildSettings.activeBuildTarget); var buildTargetSettings = XR.Management.XRGeneralSettingsPerBuildTarget.XRGeneralSettingsForBuildTarget(buildTargetGroup); if (buildTargetSettings != null && buildTargetSettings.Manager != null && buildTargetSettings.Manager.activeLoaders.Count > 0) { EditorGUILayout.HelpBox("Orthographic projection is not supported in XR. Please change the Camera Projection setting to Perspective to avoid rendering issues", MessageType.Warning); } } #endif using (new EditorGUI.DisabledGroupScope(pixelPerfectEnabled)) CameraUI.Drawer_Projection(p, owner); } static void DrawerCameraType(UniversalRenderPipelineSerializedCamera p, Editor owner) { int selectedRenderer = p.renderer.intValue; ScriptableRenderer scriptableRenderer = UniversalRenderPipeline.asset.GetRenderer(selectedRenderer); EditorGUI.BeginChangeCheck(); CameraRenderType originalCamType = (CameraRenderType)p.cameraType.intValue; CameraRenderType camType = scriptableRenderer.SupportsCameraStackingType(CameraRenderType.Overlay) ? originalCamType : CameraRenderType.Base; EditorGUI.BeginDisabledGroup(scriptableRenderer.SupportedCameraStackingTypes() == 0); camType = (CameraRenderType)EditorGUILayout.EnumPopup( Styles.cameraType, camType, e => scriptableRenderer.SupportsCameraStackingType((CameraRenderType)e), false ); EditorGUI.EndDisabledGroup(); if (EditorGUI.EndChangeCheck() || camType != originalCamType) { p.cameraType.intValue = (int)camType; if (camType == CameraRenderType.Overlay) { p.baseCameraSettings.clearFlags.intValue = (int)CameraClearFlags.Nothing; } } EditorGUILayout.Space(); } static void DrawerStackCameras(UniversalRenderPipelineSerializedCamera p, Editor owner) { if (owner is UniversalRenderPipelineCameraEditor cameraEditor) { cameraEditor.DrawStackSettings(); DisplayTileOnlyModeWarning(p.cameras, p => p.arraySize != 0, Styles.cameraStackLabelForTileOnlyMode, p); } } struct TileOnlyModeInfos { public readonly bool enabled; public readonly string rendererName; public readonly ScriptableRendererData assetToOpen; public TileOnlyModeInfos(string rendererName, ScriptableRendererData assetToOpen) { enabled = true; this.rendererName = rendererName; this.assetToOpen = assetToOpen; } } static TileOnlyModeInfos lastTileOnlyModeInfos; static void PrepareTileOnlyModeWarning(UniversalRenderPipelineSerializedCamera serialized, Editor owner) { // Rules: // - mono selection: Display warning if RendererData's Tile-Only Mode is enabled (with 'Open' behaviour) // - multi selection: // - Display warning if Tile-Only Mode is enabled on all RendererData's // - Only have 'Open' behaviour if all RendererData are the same lastTileOnlyModeInfos = default; // Note: UniversalRenderPipeline.asset should not be null or this inspector would not be shown. // Just in case off though: if (UniversalRenderPipeline.asset == null) return; bool HasTileOnlyModeAtIndex(int index, out ScriptableRendererData rendererData) => UniversalRenderPipeline.asset.TryGetRendererData(index, out rendererData) && rendererData is UniversalRendererData universalData && universalData.tileOnlyMode; // If impacted section are not opened, early exit if (!(k_ExpandedState[Expandable.Rendering] || k_ExpandedState[Expandable.Stack])) return; ScriptableRendererData rendererData = null; if (!serialized.renderer.hasMultipleDifferentValues) { if (!HasTileOnlyModeAtIndex(serialized.renderer.intValue, out rendererData)) return; lastTileOnlyModeInfos = new TileOnlyModeInfos($"'{rendererData.name}'", assetToOpen: rendererData); return; } bool targetSameAsset = true; var firstAdditionalData = (UniversalAdditionalCameraData)serialized.serializedAdditionalDataObject.targetObjects[0]; if (!HasTileOnlyModeAtIndex(firstAdditionalData.rendererIndex, out rendererData)) return; using var o = StringBuilderPool.Get(out var sb); sb.Append("'"); sb.Append(rendererData.name); sb.Append("'"); for (int i = 1; i < serialized.serializedAdditionalDataObject.targetObjects.Length; ++i) { var additionalCameraData = (UniversalAdditionalCameraData)serialized.serializedAdditionalDataObject.targetObjects[i]; if (!HasTileOnlyModeAtIndex(additionalCameraData.rendererIndex, out var otherRenderer)) return; targetSameAsset &= rendererData == otherRenderer; sb.Append(", '"); sb.Append(otherRenderer.name); sb.Append("'"); } lastTileOnlyModeInfos = new TileOnlyModeInfos(sb.ToString(), assetToOpen: targetSameAsset ? rendererData : null); } static void DisplayTileOnlyModeWarning(SerializedProperty prop, Func shouldDisplayWarning, GUIContent label, UniversalRenderPipelineSerializedCamera serialized) { if (!lastTileOnlyModeInfos.enabled || prop == null || shouldDisplayWarning == null || prop.hasMultipleDifferentValues || !shouldDisplayWarning(prop)) return; if (lastTileOnlyModeInfos.assetToOpen != null) CoreEditorUtils.DrawFixMeBox( string.Format(Styles.formatterTileOnlyMode, label == null ? prop.displayName : label.text, lastTileOnlyModeInfos.rendererName), MessageType.Warning, "Open", () => AssetDatabase.OpenAsset(lastTileOnlyModeInfos.assetToOpen)); else EditorGUILayout.HelpBox( string.Format(Styles.formatterTileOnlyMode, label == null ? prop.displayName : label.text, lastTileOnlyModeInfos.rendererName), MessageType.Warning); } } }