using System; using System.Linq; using UnityEngine; using UnityEngine.Experimental.Rendering.Universal; using UnityEngine.Rendering.Universal; using System.Collections.Generic; using System.Reflection; namespace UnityEditor.Experimental.Rendering.Universal { [CustomPropertyDrawer(typeof(RenderObjects.RenderObjectsSettings), true)] internal class RenderObjectsPassFeatureEditor : PropertyDrawer { internal class Styles { public static float defaultLineSpace = EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; public static GUIContent callback = new GUIContent("Event", "Choose at which point this render pass is executed in the frame."); //Headers public static GUIContent filtersHeader = new GUIContent("Filters", "Settings that control which objects should be rendered."); public static GUIContent renderHeader = new GUIContent("Overrides", "Different parts of the rendering that you can choose to override."); //Filters public static GUIContent renderQueueFilter = new GUIContent("Queue", "Only render objects in the selected render queue range."); public static GUIContent layerMask = new GUIContent("Layer Mask", "Only render objects in a layer that match the given layer mask."); public static GUIContent shaderPassFilter = new GUIContent("LightMode Tags", "Controls which shader passes to render by filtering by LightMode tag."); //Render Options public static GUIContent overrideMaterial = new GUIContent("Material", "Choose an override material, every renderer will be rendered with this material."); public static GUIContent overrideMaterialPass = new GUIContent("Pass Index", "The pass index for the override material to use."); //Depth Settings public static GUIContent overrideDepth = new GUIContent("Depth", "Select this option to specify how this Renderer Feature affects or uses the values in the Depth buffer."); public static GUIContent writeDepth = new GUIContent("Write Depth", "Choose to write depth to the screen."); public static GUIContent depthState = new GUIContent("Depth Test", "Choose a new depth test function."); //Camera Settings public static GUIContent overrideCamera = new GUIContent("Camera", "Override camera matrices. Toggling this setting will make camera use perspective projection."); public static GUIContent cameraFOV = new GUIContent("Field Of View", "The camera's view angle measured in degrees along vertical axis."); public static GUIContent positionOffset = new GUIContent("Position Offset", "Position offset to apply to the original camera's position."); public static GUIContent restoreCamera = new GUIContent("Restore", "Restore to the original camera matrices after the execution of the render passes added by this feature."); } //Headers and layout private HeaderBool m_FiltersFoldout; private int m_FilterLines = 3; private HeaderBool m_RenderFoldout; private int m_MaterialLines = 2; private int m_DepthLines = 3; private int m_CameraLines = 4; // Serialized Properties private SerializedProperty m_Callback; private SerializedProperty m_PassTag; //Filter props private SerializedProperty m_FilterSettings; private SerializedProperty m_RenderQueue; private SerializedProperty m_LayerMask; private SerializedProperty m_ShaderPasses; //Render props private SerializedProperty m_OverrideMaterial; private SerializedProperty m_OverrideMaterialPass; //Depth props private SerializedProperty m_OverrideDepth; private SerializedProperty m_WriteDepth; private SerializedProperty m_DepthState; //Stencil props private SerializedProperty m_StencilSettings; //Caemra props private SerializedProperty m_CameraSettings; private SerializedProperty m_OverrideCamera; private SerializedProperty m_FOV; private SerializedProperty m_CameraOffset; private SerializedProperty m_RestoreCamera; private List m_properties = new List(); static bool FilterRenderPassEvent(int evt) => // Return all events higher or equal than before rendering prepasses evt >= (int)RenderPassEvent.BeforeRenderingPrePasses && // filter obsolete events typeof(RenderPassEvent).GetField(Enum.GetName(typeof(RenderPassEvent), evt))?.GetCustomAttribute(typeof(ObsoleteAttribute)) == null; // Return all render pass event names that match filterRenderPassEvent private GUIContent[] m_EventOptionNames = Enum.GetValues(typeof(RenderPassEvent)).Cast() .Where(FilterRenderPassEvent) .Select(x => new GUIContent(Enum.GetName(typeof(RenderPassEvent), x))).ToArray(); // Return all render pass event options that match filterRenderPassEvent private int[] m_EventOptionValues = Enum.GetValues(typeof(RenderPassEvent)).Cast() .Where(FilterRenderPassEvent).ToArray(); private void Init(SerializedProperty property) { //Header bools var key = $"{this.ToString().Split('.').Last()}.{property.serializedObject.targetObject.name}"; m_FiltersFoldout = new HeaderBool($"{key}.FiltersFoldout", true); m_RenderFoldout = new HeaderBool($"{key}.RenderFoldout"); m_Callback = property.FindPropertyRelative("Event"); m_PassTag = property.FindPropertyRelative("passTag"); //Filter props m_FilterSettings = property.FindPropertyRelative("filterSettings"); m_RenderQueue = m_FilterSettings.FindPropertyRelative("RenderQueueType"); m_LayerMask = m_FilterSettings.FindPropertyRelative("LayerMask"); m_ShaderPasses = m_FilterSettings.FindPropertyRelative("PassNames"); //Render options m_OverrideMaterial = property.FindPropertyRelative("overrideMaterial"); m_OverrideMaterialPass = property.FindPropertyRelative("overrideMaterialPassIndex"); //Depth props m_OverrideDepth = property.FindPropertyRelative("overrideDepthState"); m_WriteDepth = property.FindPropertyRelative("enableWrite"); m_DepthState = property.FindPropertyRelative("depthCompareFunction"); //Stencil m_StencilSettings = property.FindPropertyRelative("stencilSettings"); //Camera m_CameraSettings = property.FindPropertyRelative("cameraSettings"); m_OverrideCamera = m_CameraSettings.FindPropertyRelative("overrideCamera"); m_FOV = m_CameraSettings.FindPropertyRelative("cameraFieldOfView"); m_CameraOffset = m_CameraSettings.FindPropertyRelative("offset"); m_RestoreCamera = m_CameraSettings.FindPropertyRelative("restoreCamera"); m_properties.Add(property.serializedObject); } public override void OnGUI(Rect rect, SerializedProperty property, GUIContent label) { rect.height = EditorGUIUtility.singleLineHeight; EditorGUI.BeginChangeCheck(); EditorGUI.BeginProperty(rect, label, property); if (!m_properties.Contains(property.serializedObject)) { Init(property); } var passName = property.serializedObject.FindProperty("m_Name").stringValue; if (passName != m_PassTag.stringValue) { m_PassTag.stringValue = passName; property.serializedObject.ApplyModifiedProperties(); } //Forward Callbacks EditorGUI.BeginChangeCheck(); int selectedValue = EditorGUI.IntPopup(rect, Styles.callback, m_Callback.intValue, m_EventOptionNames, m_EventOptionValues); if (EditorGUI.EndChangeCheck()) m_Callback.intValue = selectedValue; rect.y += Styles.defaultLineSpace; DoFilters(ref rect); m_RenderFoldout.value = EditorGUI.Foldout(rect, m_RenderFoldout.value, Styles.renderHeader, true); SaveHeaderBool(m_RenderFoldout); rect.y += Styles.defaultLineSpace; if (m_RenderFoldout.value) { EditorGUI.indentLevel++; //Override material DoMaterialOverride(ref rect); rect.y += Styles.defaultLineSpace; //Override depth DoDepthOverride(ref rect); rect.y += Styles.defaultLineSpace; //Override stencil EditorGUI.PropertyField(rect, m_StencilSettings); rect.y += EditorGUI.GetPropertyHeight(m_StencilSettings); //Override camera DoCameraOverride(ref rect); rect.y += Styles.defaultLineSpace; EditorGUI.indentLevel--; } EditorGUI.EndProperty(); if (EditorGUI.EndChangeCheck()) property.serializedObject.ApplyModifiedProperties(); } void DoFilters(ref Rect rect) { m_FiltersFoldout.value = EditorGUI.Foldout(rect, m_FiltersFoldout.value, Styles.filtersHeader, true); SaveHeaderBool(m_FiltersFoldout); rect.y += Styles.defaultLineSpace; if (m_FiltersFoldout.value) { EditorGUI.indentLevel++; //Render queue filter EditorGUI.PropertyField(rect, m_RenderQueue, Styles.renderQueueFilter); rect.y += Styles.defaultLineSpace; //Layer mask EditorGUI.PropertyField(rect, m_LayerMask, Styles.layerMask); rect.y += Styles.defaultLineSpace; //Shader pass list EditorGUI.PropertyField(rect, m_ShaderPasses, Styles.shaderPassFilter, true); rect.y += EditorGUI.GetPropertyHeight(m_ShaderPasses); EditorGUI.indentLevel--; } } void DoMaterialOverride(ref Rect rect) { //Override material EditorGUI.PropertyField(rect, m_OverrideMaterial, Styles.overrideMaterial); if (m_OverrideMaterial.objectReferenceValue) { rect.y += Styles.defaultLineSpace; EditorGUI.indentLevel++; EditorGUI.BeginChangeCheck(); EditorGUI.PropertyField(rect, m_OverrideMaterialPass, Styles.overrideMaterialPass); if (EditorGUI.EndChangeCheck()) m_OverrideMaterialPass.intValue = Mathf.Max(0, m_OverrideMaterialPass.intValue); EditorGUI.indentLevel--; } } void DoDepthOverride(ref Rect rect) { EditorGUI.PropertyField(rect, m_OverrideDepth, Styles.overrideDepth); if (m_OverrideDepth.boolValue) { rect.y += Styles.defaultLineSpace; EditorGUI.indentLevel++; //Write depth EditorGUI.PropertyField(rect, m_WriteDepth, Styles.writeDepth); rect.y += Styles.defaultLineSpace; //Depth testing options EditorGUI.PropertyField(rect, m_DepthState, Styles.depthState); EditorGUI.indentLevel--; } } void DoCameraOverride(ref Rect rect) { EditorGUI.PropertyField(rect, m_OverrideCamera, Styles.overrideCamera); if (m_OverrideCamera.boolValue) { rect.y += Styles.defaultLineSpace; EditorGUI.indentLevel++; //FOV EditorGUI.Slider(rect, m_FOV, 4f, 179f, Styles.cameraFOV); rect.y += Styles.defaultLineSpace; //Offset vector var offset = m_CameraOffset.vector4Value; EditorGUI.BeginChangeCheck(); var newOffset = EditorGUI.Vector3Field(rect, Styles.positionOffset, new Vector3(offset.x, offset.y, offset.z)); if (EditorGUI.EndChangeCheck()) m_CameraOffset.vector4Value = new Vector4(newOffset.x, newOffset.y, newOffset.z, 1f); rect.y += Styles.defaultLineSpace; //Restore prev camera projections EditorGUI.PropertyField(rect, m_RestoreCamera, Styles.restoreCamera); rect.y += Styles.defaultLineSpace; EditorGUI.indentLevel--; } } public override float GetPropertyHeight(SerializedProperty property, GUIContent label) { float height = Styles.defaultLineSpace; Init(property); height += Styles.defaultLineSpace * (m_FiltersFoldout.value ? m_FilterLines : 1); height += m_FiltersFoldout.value ? EditorGUI.GetPropertyHeight(m_ShaderPasses) : 0; height += Styles.defaultLineSpace; // add line for overrides dropdown if (m_RenderFoldout.value) { height += Styles.defaultLineSpace * (m_OverrideMaterial.objectReferenceValue != null ? m_MaterialLines : 1); height += Styles.defaultLineSpace * (m_OverrideDepth.boolValue ? m_DepthLines : 1); height += EditorGUI.GetPropertyHeight(m_StencilSettings); height += Styles.defaultLineSpace * (m_OverrideCamera.boolValue ? m_CameraLines : 1); } return height; } private void SaveHeaderBool(HeaderBool boolObj) { EditorPrefs.SetBool(boolObj.key, boolObj.value); } class HeaderBool { public string key; public bool value; public HeaderBool(string _key, bool _default = false) { key = _key; if (EditorPrefs.HasKey(key)) value = EditorPrefs.GetBool(key); else value = _default; EditorPrefs.SetBool(key, value); } } } }