123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361 |
- using System;
- using System.Collections.Generic;
- using System.Reflection;
- using System.Text.RegularExpressions;
- using UnityEngine;
- using UnityEngine.Rendering.Universal;
- using Object = UnityEngine.Object;
- namespace UnityEditor.Rendering.Universal
- {
- [CustomEditor(typeof(ScriptableRendererData), true)]
- public class ScriptableRendererDataEditor : Editor
- {
- class Styles
- {
- public static readonly GUIContent RenderFeatures =
- new GUIContent("Renderer Features",
- "A Renderer Feature is an asset that lets you add extra Render passes to a URP Renderer and configure their behavior.");
- public static readonly GUIContent PassNameField =
- new GUIContent("Name", "Render pass name. This name is the name displayed in Frame Debugger.");
- public static readonly GUIContent MissingFeature = new GUIContent("Missing RendererFeature",
- "Missing reference, due to compilation issues or missing files. you can attempt auto fix or choose to remove the feature.");
- public static GUIStyle BoldLabelSimple;
- static Styles()
- {
- BoldLabelSimple = new GUIStyle(EditorStyles.label);
- BoldLabelSimple.fontStyle = FontStyle.Bold;
- }
- }
- private SerializedProperty m_RendererFeatures;
- private SerializedProperty m_RendererFeaturesMap;
- private SerializedProperty m_FalseBool;
- [SerializeField] private bool falseBool = false;
- List<Editor> m_Editors = new List<Editor>();
- private void OnEnable()
- {
- m_RendererFeatures = serializedObject.FindProperty(nameof(ScriptableRendererData.m_RendererFeatures));
- m_RendererFeaturesMap = serializedObject.FindProperty(nameof(ScriptableRendererData.m_RendererFeatureMap));
- var editorObj = new SerializedObject(this);
- m_FalseBool = editorObj.FindProperty(nameof(falseBool));
- UpdateEditorList();
- }
- private void OnDisable()
- {
- ClearEditorsList();
- }
- public override void OnInspectorGUI()
- {
- if (m_RendererFeatures == null)
- OnEnable();
- else if (m_RendererFeatures.arraySize != m_Editors.Count)
- UpdateEditorList();
- serializedObject.Update();
- DrawRendererFeatureList();
- }
- private void DrawRendererFeatureList()
- {
- EditorGUILayout.LabelField(Styles.RenderFeatures, EditorStyles.boldLabel);
- EditorGUILayout.Space();
- if (m_RendererFeatures.arraySize == 0)
- {
- EditorGUILayout.HelpBox("No Renderer Features added", MessageType.Info);
- }
- else
- {
- //Draw List
- CoreEditorUtils.DrawSplitter();
- for (int i = 0; i < m_RendererFeatures.arraySize; i++)
- {
- SerializedProperty renderFeaturesProperty = m_RendererFeatures.GetArrayElementAtIndex(i);
- DrawRendererFeature(i, ref renderFeaturesProperty);
- CoreEditorUtils.DrawSplitter();
- }
- }
- EditorGUILayout.Space();
- //Add renderer
- if (GUILayout.Button("Add Renderer Feature", EditorStyles.miniButton))
- {
- AddPassMenu();
- }
- }
- private bool GetCustomTitle(Type type, out string title)
- {
- var isSingleFeature = type.GetCustomAttribute<DisallowMultipleRendererFeature>();
- if (isSingleFeature != null)
- {
- title = isSingleFeature.customTitle;
- return title != null;
- }
- title = null;
- return false;
- }
- private bool GetTooltip(Type type, out string tooltip)
- {
- var attribute = type.GetCustomAttribute<TooltipAttribute>();
- if (attribute != null)
- {
- tooltip = attribute.tooltip;
- return true;
- }
- tooltip = string.Empty;
- return false;
- }
- private void DrawRendererFeature(int index, ref SerializedProperty renderFeatureProperty)
- {
- Object rendererFeatureObjRef = renderFeatureProperty.objectReferenceValue;
- if (rendererFeatureObjRef != null)
- {
- bool hasChangedProperties = false;
- string title;
- bool hasCustomTitle = GetCustomTitle(rendererFeatureObjRef.GetType(), out title);
- if (!hasCustomTitle)
- {
- title = ObjectNames.GetInspectorTitle(rendererFeatureObjRef);
- }
- string tooltip;
- GetTooltip(rendererFeatureObjRef.GetType(), out tooltip);
- // Get the serialized object for the editor script & update it
- Editor rendererFeatureEditor = m_Editors[index];
- SerializedObject serializedRendererFeaturesEditor = rendererFeatureEditor.serializedObject;
- serializedRendererFeaturesEditor.Update();
- // Foldout header
- EditorGUI.BeginChangeCheck();
- SerializedProperty activeProperty = serializedRendererFeaturesEditor.FindProperty("m_Active");
- bool displayContent = CoreEditorUtils.DrawHeaderToggle(EditorGUIUtility.TrTextContent(title, tooltip), renderFeatureProperty, activeProperty, pos => OnContextClick(pos, index));
- hasChangedProperties |= EditorGUI.EndChangeCheck();
- // ObjectEditor
- if (displayContent)
- {
- if (!hasCustomTitle)
- {
- EditorGUI.BeginChangeCheck();
- SerializedProperty nameProperty = serializedRendererFeaturesEditor.FindProperty("m_Name");
- nameProperty.stringValue = ValidateName(EditorGUILayout.DelayedTextField(Styles.PassNameField, nameProperty.stringValue));
- if (EditorGUI.EndChangeCheck())
- {
- hasChangedProperties = true;
- // We need to update sub-asset name
- rendererFeatureObjRef.name = nameProperty.stringValue;
- AssetDatabase.SaveAssets();
- // Triggers update for sub-asset name change
- ProjectWindowUtil.ShowCreatedAsset(target);
- }
- }
- EditorGUI.BeginChangeCheck();
- rendererFeatureEditor.OnInspectorGUI();
- hasChangedProperties |= EditorGUI.EndChangeCheck();
- EditorGUILayout.Space(EditorGUIUtility.singleLineHeight);
- }
- // Apply changes and save if the user has modified any settings
- if (hasChangedProperties)
- {
- serializedRendererFeaturesEditor.ApplyModifiedProperties();
- serializedObject.ApplyModifiedProperties();
- ForceSave();
- }
- }
- else
- {
- CoreEditorUtils.DrawHeaderToggle(Styles.MissingFeature, renderFeatureProperty, m_FalseBool, pos => OnContextClick(pos, index));
- m_FalseBool.boolValue = false; // always make sure false bool is false
- EditorGUILayout.HelpBox(Styles.MissingFeature.tooltip, MessageType.Error);
- if (GUILayout.Button("Attempt Fix", EditorStyles.miniButton))
- {
- ScriptableRendererData data = target as ScriptableRendererData;
- data.ValidateRendererFeatures();
- }
- }
- }
- private void OnContextClick(Vector2 position, int id)
- {
- var menu = new GenericMenu();
- if (id == 0)
- menu.AddDisabledItem(EditorGUIUtility.TrTextContent("Move Up"));
- else
- menu.AddItem(EditorGUIUtility.TrTextContent("Move Up"), false, () => MoveComponent(id, -1));
- if (id == m_RendererFeatures.arraySize - 1)
- menu.AddDisabledItem(EditorGUIUtility.TrTextContent("Move Down"));
- else
- menu.AddItem(EditorGUIUtility.TrTextContent("Move Down"), false, () => MoveComponent(id, 1));
- menu.AddSeparator(string.Empty);
- menu.AddItem(EditorGUIUtility.TrTextContent("Remove"), false, () => RemoveComponent(id));
- menu.DropDown(new Rect(position, Vector2.zero));
- }
- private void AddPassMenu()
- {
- GenericMenu menu = new GenericMenu();
- TypeCache.TypeCollection types = TypeCache.GetTypesDerivedFrom<ScriptableRendererFeature>();
- foreach (Type type in types)
- {
- var data = target as ScriptableRendererData;
- if (data.DuplicateFeatureCheck(type))
- {
- continue;
- }
- string path = GetMenuNameFromType(type);
- menu.AddItem(new GUIContent(path), false, AddComponent, type.Name);
- }
- menu.ShowAsContext();
- }
- private void AddComponent(object type)
- {
- serializedObject.Update();
- ScriptableObject component = CreateInstance((string)type);
- component.name = $"{(string)type}";
- Undo.RegisterCreatedObjectUndo(component, "Add Renderer Feature");
- // Store this new effect as a sub-asset so we can reference it safely afterwards
- // Only when we're not dealing with an instantiated asset
- if (EditorUtility.IsPersistent(target))
- {
- AssetDatabase.AddObjectToAsset(component, target);
- }
- AssetDatabase.TryGetGUIDAndLocalFileIdentifier(component, out var guid, out long localId);
- // Grow the list first, then add - that's how serialized lists work in Unity
- m_RendererFeatures.arraySize++;
- SerializedProperty componentProp = m_RendererFeatures.GetArrayElementAtIndex(m_RendererFeatures.arraySize - 1);
- componentProp.objectReferenceValue = component;
- // Update GUID Map
- m_RendererFeaturesMap.arraySize++;
- SerializedProperty guidProp = m_RendererFeaturesMap.GetArrayElementAtIndex(m_RendererFeaturesMap.arraySize - 1);
- guidProp.longValue = localId;
- UpdateEditorList();
- serializedObject.ApplyModifiedProperties();
- // Force save / refresh
- if (EditorUtility.IsPersistent(target))
- {
- ForceSave();
- }
- serializedObject.ApplyModifiedProperties();
- }
- private void RemoveComponent(int id)
- {
- SerializedProperty property = m_RendererFeatures.GetArrayElementAtIndex(id);
- Object component = property.objectReferenceValue;
- property.objectReferenceValue = null;
- Undo.SetCurrentGroupName(component == null ? "Remove Renderer Feature" : $"Remove {component.name}");
- // remove the array index itself from the list
- m_RendererFeatures.DeleteArrayElementAtIndex(id);
- m_RendererFeaturesMap.DeleteArrayElementAtIndex(id);
- UpdateEditorList();
- serializedObject.ApplyModifiedProperties();
- // Destroy the setting object after ApplyModifiedProperties(). If we do it before, redo
- // actions will be in the wrong order and the reference to the setting object in the
- // list will be lost.
- if (component != null)
- {
- Undo.DestroyObjectImmediate(component);
- ScriptableRendererFeature feature = component as ScriptableRendererFeature;
- feature?.Dispose();
- }
- // Force save / refresh
- ForceSave();
- }
- private void MoveComponent(int id, int offset)
- {
- Undo.SetCurrentGroupName("Move Render Feature");
- serializedObject.Update();
- m_RendererFeatures.MoveArrayElement(id, id + offset);
- m_RendererFeaturesMap.MoveArrayElement(id, id + offset);
- UpdateEditorList();
- serializedObject.ApplyModifiedProperties();
- // Force save / refresh
- ForceSave();
- }
- private string GetMenuNameFromType(Type type)
- {
- string path;
- if (!GetCustomTitle(type, out path))
- {
- path = ObjectNames.NicifyVariableName(type.Name);
- }
- if (type.Namespace != null)
- {
- if (type.Namespace.Contains("Experimental"))
- path += " (Experimental)";
- }
- return path;
- }
- private string ValidateName(string name)
- {
- name = Regex.Replace(name, @"[^a-zA-Z0-9 ]", "");
- return name;
- }
- private void UpdateEditorList()
- {
- ClearEditorsList();
- for (int i = 0; i < m_RendererFeatures.arraySize; i++)
- {
- m_Editors.Add(CreateEditor(m_RendererFeatures.GetArrayElementAtIndex(i).objectReferenceValue));
- }
- }
- //To avoid leaking memory we destroy editors when we clear editors list
- private void ClearEditorsList()
- {
- for (int i = m_Editors.Count - 1; i >= 0; --i)
- {
- DestroyImmediate(m_Editors[i]);
- }
- m_Editors.Clear();
- }
- private void ForceSave()
- {
- EditorUtility.SetDirty(target);
- }
- }
- }
|