using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using UnityEditor; using UnityEditor.Rendering; using UnityEngine; using UnityEngine.Rendering; namespace HorizonBasedAmbientOcclusion.Universal { #if UNITY_2022_2_OR_NEWER [CustomEditor(typeof(HBAO))] #else [VolumeComponentEditor(typeof(HBAO))] #endif public class HBAOEditor : VolumeComponentEditor { private HBAO m_HBAO; private Texture2D m_HBAOTex; private GUIStyle m_SettingsGroupStyle; private GUIStyle m_TitleLabelStyle; private int m_SelectedPreset; private PropertyFetcher m_PropertyFetcher; // settings group private Dictionary> m_GroupFields; private readonly Dictionary m_Presets = new Dictionary() { { 0, HBAO.Preset.Normal }, { 1, HBAO.Preset.FastPerformance }, { 2, HBAO.Preset.FastestPerformance }, { 3, HBAO.Preset.Custom }, { 4, HBAO.Preset.HighQuality }, { 5, HBAO.Preset.HighestQuality } }; #if UNITY_2021_2_OR_NEWER public override bool hasAdditionalProperties => false; #else public override bool hasAdvancedMode => false; #endif public override void OnEnable() { base.OnEnable(); m_HBAO = (HBAO)target; m_HBAOTex = Resources.Load("hbao_urp"); //var o = new PropertyFetcher(serializedObject); m_PropertyFetcher = new PropertyFetcher(serializedObject); m_GroupFields = new Dictionary>(); var settings = m_HBAO.GetType().GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) .Where(t => t.FieldType.IsSubclassOf(typeof(VolumeParameter))) .Where(t => (t.IsPublic && t.GetCustomAttributes(typeof(NonSerializedAttribute), false).Length == 0) || (t.GetCustomAttributes(typeof(SerializeField), false).Length > 0)) .Where(t => t.GetCustomAttributes(typeof(HideInInspector), false).Length == 0) .Where(t => t.GetCustomAttributes(typeof(HBAO.SettingsGroup), false).Any()); foreach (var setting in settings) { //Debug.Log("setting name: " + setting.Name); foreach (var attr in setting.GetCustomAttributes(typeof(HBAO.SettingsGroup)) as IEnumerable) { if (!m_GroupFields.ContainsKey(attr)) m_GroupFields[attr] = new List(); m_GroupFields[attr].Add(setting); } } m_SelectedPreset = m_Presets.Values.ToList().IndexOf(m_HBAO.GetCurrentPreset()); } public override void OnInspectorGUI() { serializedObject.Update(); SetStyles(); EditorGUILayout.BeginVertical(); { // header GUILayout.Space(10.0f); GUILayout.Label(m_HBAOTex, m_TitleLabelStyle, GUILayout.ExpandWidth(true)); //if (m_HBAO.GetComponents()[0] != m_HBAO) //{ //GUILayout.Space(6.0f); //EditorGUILayout.HelpBox("This Post FX should be one of the first in your effect stack", MessageType.Info); //} Event e = Event.current; // settings groups foreach (var group in m_GroupFields) { GUILayout.Space(6.0f); Rect rect = GUILayoutUtility.GetRect(16f, 22f, m_SettingsGroupStyle); GUI.Box(rect, ObjectNames.NicifyVariableName(group.Key.GetType().Name), m_SettingsGroupStyle); if (e.type == EventType.MouseDown && rect.Contains(e.mousePosition)) { group.Key.isExpanded = !group.Key.isExpanded; e.Use(); } if (!group.Key.isExpanded) continue; // presets is a special case if (group.Key.GetType() == typeof(HBAO.Presets)) { GUILayout.Space(6.0f); m_SelectedPreset = GUILayout.SelectionGrid(m_SelectedPreset, m_Presets.Values.Select(x => ObjectNames.NicifyVariableName(x.ToString())).ToArray(), 3); GUILayout.Space(6.0f); if (GUILayout.Button("Apply Preset")) { Undo.RecordObject(target, "Apply Preset"); m_HBAO.ApplyPreset(m_Presets[m_SelectedPreset]); EditorUtility.SetDirty(target); /*if (!EditorApplication.isPlaying) { UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(UnityEngine.SceneManagement.SceneManager.GetActiveScene()); }*/ } continue; } foreach (var field in group.Value) { // warn about URP10+ required when mode is LitAO if (group.Key.GetType() == typeof(HBAO.GeneralSettings) && field.Name == "renderingPath") { #if UNITY_2021_2_OR_NEWER if (m_HBAO.mode.overrideState && m_HBAO.mode.value != HBAO.Mode.LitAO) { continue; // hides rendering path settings when not LitAO } #else continue; // hides rendering path before URP12 #endif } // hide resolution when deinterleaved HBAO is on if (group.Key.GetType() == typeof(HBAO.GeneralSettings) && field.Name == "resolution") { if (m_HBAO.deinterleaving.overrideState && m_HBAO.GetDeinterleaving() != HBAO.Deinterleaving.Disabled) { continue; } } // hide noise type when deinterleaved HBAO is on else if (group.Key.GetType() == typeof(HBAO.GeneralSettings) && field.Name == "noiseType") { if (m_HBAO.deinterleaving.overrideState && m_HBAO.GetDeinterleaving() != HBAO.Deinterleaving.Disabled) { continue; } } // warn about HBAO being disabled by default else if (group.Key.GetType() == typeof(HBAO.AOSettings) && field.Name == "radius") { if (m_HBAO.intensity.overrideState == false) { GUILayout.Space(6.0f); EditorGUILayout.HelpBox("The effect is disabled by default, you need to override intensity value in order to enable HBAO.", MessageType.Warning); } } // hide useMultiBounce setting when mode is LitAO else if (group.Key.GetType() == typeof(HBAO.AOSettings) && field.Name == "useMultiBounce") { if (m_HBAO.mode.overrideState && m_HBAO.mode.value == HBAO.Mode.LitAO) { continue; } } // hide multiBounceInfluence setting when not used or when mode is LitAO else if (group.Key.GetType() == typeof(HBAO.AOSettings) && field.Name == "multiBounceInfluence") { if (!m_HBAO.useMultiBounce.overrideState || !m_HBAO.UseMultiBounce() || (m_HBAO.mode.overrideState && m_HBAO.mode.value == HBAO.Mode.LitAO)) { continue; } } // hide directLightingStrength setting when mode is normal else if (group.Key.GetType() == typeof(HBAO.AOSettings) && field.Name == "directLightingStrength") { if (!m_HBAO.mode.overrideState || m_HBAO.mode.value == HBAO.Mode.Normal) { continue; } } // warn about distance falloff greater than max distance else if (group.Key.GetType() == typeof(HBAO.AOSettings) && field.Name == "perPixelNormals") { if ((m_HBAO.distanceFalloff.overrideState || m_HBAO.maxDistance.overrideState) && m_HBAO.GetAoDistanceFalloff() > m_HBAO.GetAoMaxDistance()) { GUILayout.Space(6.0f); EditorGUILayout.HelpBox("Distance Falloff shoudn't be greater than Max Distance.", MessageType.Warning); } } // warn about motion vectors not supported else if (group.Key.GetType() == typeof(HBAO.TemporalFilterSettings) && field.Name == "temporalFilterEnabled") { // For platforms not supporting motion vectors texture // https://docs.unity3d.com/ScriptReference/DepthTextureMode.MotionVectors.html if (!SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.RGHalf)) { GUILayout.Space(6.0f); EditorGUILayout.HelpBox("Motion vectors not supported on this platform...", MessageType.Warning); if (m_HBAO.IsTemporalFilterEnabled()) m_HBAO.EnableTemporalFilter(false); } else { #if !UNITY_2021_2_OR_NEWER GUILayout.Space(6.0f); EditorGUILayout.HelpBox("Requires proper motion vectors support available in 2021.2+", MessageType.Warning); if (m_HBAO.IsTemporalFilterEnabled()) m_HBAO.EnableTemporalFilter(false); #else if (IsVrRunning()) { GUILayout.Space(6.0f); EditorGUILayout.HelpBox("Not supported yet in VR...", MessageType.Warning); if (m_HBAO.IsTemporalFilterEnabled()) m_HBAO.EnableTemporalFilter(false); } #endif } } // warn about color bleeding not supported when doing LitAO else if (group.Key.GetType() == typeof(HBAO.ColorBleedingSettings) && field.Name == "colorBleedingEnabled") { if (m_HBAO.mode.overrideState && m_HBAO.mode.value == HBAO.Mode.LitAO) { GUILayout.Space(6.0f); EditorGUILayout.HelpBox("Color bleeding can't be used in LitAO mode as AO is being injected into the lighting.", MessageType.Warning); if (m_HBAO.IsColorBleedingEnabled()) m_HBAO.EnableColorBleeding(false); } } var parameter = Unpack(m_PropertyFetcher.Find(field.Name)); var displayName = parameter.displayName; var hasDisplayName = field.GetCustomAttributes(typeof(HBAO.ParameterDisplayName)).Any(); if (hasDisplayName) { var displayNameAttribute = field.GetCustomAttributes(typeof(HBAO.ParameterDisplayName)).First() as HBAO.ParameterDisplayName; displayName = displayNameAttribute.name; } PropertyField(parameter, new GUIContent(displayName)); } GUILayout.Space(6.0f); } } EditorGUILayout.EndVertical(); serializedObject.ApplyModifiedProperties(); } private void SetStyles() { // set banner label style m_TitleLabelStyle = new GUIStyle(GUI.skin.label); m_TitleLabelStyle.alignment = TextAnchor.MiddleCenter; m_TitleLabelStyle.contentOffset = new Vector2(0f, 0f); // get shuriken module title style GUIStyle skurikenModuleTitleStyle = "ShurikenModuleTitle"; // clone it as to not interfere with the original, and adjust it m_SettingsGroupStyle = new GUIStyle(skurikenModuleTitleStyle); m_SettingsGroupStyle.font = (new GUIStyle("Label")).font; m_SettingsGroupStyle.fontStyle = FontStyle.Bold; m_SettingsGroupStyle.border = new RectOffset(15, 7, 4, 4); m_SettingsGroupStyle.fixedHeight = 22; m_SettingsGroupStyle.contentOffset = new Vector2(10f, -2f); } List displays = new List(); private bool IsVrRunning() { bool vrIsRunning = false; displays.Clear(); SubsystemManager.GetSubsystems(displays); foreach (var displaySubsystem in displays) { if (displaySubsystem.running) { vrIsRunning = true; break; } } return vrIsRunning; } [VolumeParameterDrawer(typeof(HBAO.MinMaxFloatParameter))] public class MaxFloatParameterDrawer : VolumeParameterDrawer { public override bool OnGUI(SerializedDataParameter parameter, GUIContent title) { if (parameter.value.propertyType == SerializedPropertyType.Vector2) { var o = parameter.GetObjectRef(); var range = o.value; float x = range.x; float y = range.y; EditorGUI.BeginChangeCheck(); EditorGUILayout.MinMaxSlider(title, ref x, ref y, o.min, o.max); if (EditorGUI.EndChangeCheck()) { range.x = x; range.y = y; o.SetValue(new HBAO.MinMaxFloatParameter(range, o.min, o.max)); } return true; } else { EditorGUILayout.LabelField(title, "Use only with Vector2"); return false; } } } } }