ScriptableRendererDataEditor.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. using System;
  2. using System.Text.RegularExpressions;
  3. using UnityEngine;
  4. using UnityEngine.Rendering.Universal;
  5. using UnityEngine.Scripting.APIUpdating;
  6. namespace UnityEditor.Rendering.Universal
  7. {
  8. [CustomEditor(typeof(ScriptableRendererData), true)]
  9. [MovedFrom("UnityEditor.Rendering.LWRP")] public class ScriptableRendererDataEditor : Editor
  10. {
  11. class Styles
  12. {
  13. public static readonly GUIContent RenderFeatures =
  14. new GUIContent("Renderer Features",
  15. "Features to include in this renderer.\nTo add or remove features, use the plus and minus at the bottom of this box.");
  16. public static readonly GUIContent PassNameField =
  17. new GUIContent("Name", "Render pass name. This name is the name displayed in Frame Debugger.");
  18. public static readonly GUIContent MissingFeature = new GUIContent("Missing RendererFeature",
  19. "Missing reference, due to compilation issues or missing files. you can attempt auto fix or choose to remove the feature.");
  20. public static GUIStyle BoldLabelSimple;
  21. static Styles()
  22. {
  23. BoldLabelSimple = new GUIStyle(EditorStyles.label);
  24. BoldLabelSimple.fontStyle = FontStyle.Bold;
  25. }
  26. }
  27. private SerializedProperty m_RenderPasses;
  28. private SerializedProperty m_RenderPassMap;
  29. private SerializedProperty m_FalseBool;
  30. private bool m_SaveAsset;
  31. [SerializeField] private bool falseBool = false;
  32. private void OnEnable()
  33. {
  34. m_RenderPasses = serializedObject.FindProperty(nameof(ScriptableRendererData.m_RendererFeatures));
  35. m_RenderPassMap = serializedObject.FindProperty(nameof(ScriptableRendererData.m_RendererFeatureMap));
  36. var editorObj = new SerializedObject(this);
  37. m_FalseBool = editorObj.FindProperty(nameof(falseBool));
  38. }
  39. public override void OnInspectorGUI()
  40. {
  41. if(m_RenderPasses == null)
  42. OnEnable();
  43. serializedObject.Update();
  44. DrawRendererFeatureList();
  45. if(serializedObject.hasModifiedProperties)
  46. serializedObject.ApplyModifiedProperties();
  47. if (m_SaveAsset)
  48. {
  49. m_SaveAsset = false;
  50. EditorUtility.SetDirty(target);
  51. AssetDatabase.SaveAssets();
  52. }
  53. }
  54. private void DrawRendererFeatureList()
  55. {
  56. EditorGUILayout.LabelField(Styles.RenderFeatures, EditorStyles.boldLabel);
  57. EditorGUILayout.Space();
  58. if (m_RenderPasses.arraySize == 0)
  59. {
  60. EditorGUILayout.HelpBox("No Renderer Features added", MessageType.Info);
  61. }
  62. else
  63. {
  64. //Draw List
  65. CoreEditorUtils.DrawSplitter();
  66. for (int i = 0; i < m_RenderPasses.arraySize; i++)
  67. {
  68. var prop = m_RenderPasses.GetArrayElementAtIndex(i);
  69. DrawRendererFeature(i, ref prop);
  70. CoreEditorUtils.DrawSplitter();
  71. }
  72. }
  73. EditorGUILayout.Space();
  74. //Add renderer
  75. if (GUILayout.Button("Add Renderer Feature", EditorStyles.miniButton))
  76. {
  77. AddPassMenu();
  78. }
  79. }
  80. private void DrawRendererFeature(int index, ref SerializedProperty prop)
  81. {
  82. var obj = prop.objectReferenceValue;
  83. var title = ObjectNames.GetInspectorTitle(obj);
  84. if (obj != null)
  85. {
  86. var editor = CreateEditor(obj);
  87. var serializedFeature = new SerializedObject(obj);
  88. // Foldout header
  89. EditorGUI.BeginChangeCheck();
  90. var displayContent = CoreEditorUtils.DrawHeaderToggle(
  91. title,
  92. prop,
  93. serializedFeature.FindProperty("m_Active"),
  94. pos => OnContextClick(pos, index)
  95. );
  96. if (EditorGUI.EndChangeCheck())
  97. m_SaveAsset = true;
  98. // ObjectEditor
  99. if (displayContent)
  100. {
  101. EditorGUI.BeginChangeCheck();
  102. var propertyName = serializedFeature.FindProperty("m_Name");
  103. propertyName.stringValue = ValidateName(EditorGUILayout.DelayedTextField(Styles.PassNameField, propertyName.stringValue));
  104. if (EditorGUI.EndChangeCheck())
  105. m_SaveAsset = true;
  106. editor.DrawDefaultInspector();
  107. }
  108. //Save the changed data
  109. if (!serializedFeature.hasModifiedProperties) return;
  110. serializedFeature.ApplyModifiedProperties();
  111. m_SaveAsset = true;
  112. }
  113. else
  114. {
  115. CoreEditorUtils.DrawHeaderToggle(
  116. Styles.MissingFeature,
  117. prop,
  118. m_FalseBool,
  119. pos => OnContextClick(pos, index)
  120. );
  121. m_FalseBool.boolValue = false; // always make sure false bool is false
  122. EditorGUILayout.HelpBox(Styles.MissingFeature.tooltip, MessageType.Error);
  123. if (GUILayout.Button("Attempt Fix", EditorStyles.miniButton))
  124. {
  125. var data = target as ScriptableRendererData;
  126. data.ValidateRendererFeatures();
  127. }
  128. }
  129. }
  130. private void OnContextClick(Vector2 position, int id)
  131. {
  132. var menu = new GenericMenu();
  133. if (id == 0)
  134. menu.AddDisabledItem(EditorGUIUtility.TrTextContent("Move Up"));
  135. else
  136. menu.AddItem(EditorGUIUtility.TrTextContent("Move Up"), false, () => MoveComponent(id, -1));
  137. if (id == m_RenderPasses.arraySize - 1)
  138. menu.AddDisabledItem(EditorGUIUtility.TrTextContent("Move Down"));
  139. else
  140. menu.AddItem(EditorGUIUtility.TrTextContent("Move Down"), false, () => MoveComponent(id, 1));
  141. menu.AddSeparator(string.Empty);
  142. menu.AddItem(EditorGUIUtility.TrTextContent("Remove"), false, () => RemoveComponent(id));
  143. menu.DropDown(new Rect(position, Vector2.zero));
  144. }
  145. private void AddPassMenu()
  146. {
  147. var menu = new GenericMenu();
  148. var types = TypeCache.GetTypesDerivedFrom<ScriptableRendererFeature>();
  149. foreach (Type type in types)
  150. {
  151. string path = GetMenuNameFromType(type);
  152. menu.AddItem(new GUIContent(path), false, AddComponent, type.Name);
  153. }
  154. menu.ShowAsContext();
  155. }
  156. private void AddComponent(object type)
  157. {
  158. serializedObject.Update();
  159. var component = CreateInstance((string)type);
  160. component.name = $"New{(string)type}";
  161. Undo.RegisterCreatedObjectUndo(component, "Add Renderer Feature");
  162. // Store this new effect as a sub-asset so we can reference it safely afterwards
  163. // Only when we're not dealing with an instantiated asset
  164. if (EditorUtility.IsPersistent(target))
  165. AssetDatabase.AddObjectToAsset(component, target);
  166. AssetDatabase.TryGetGUIDAndLocalFileIdentifier(component, out var guid, out long localId);
  167. // Grow the list first, then add - that's how serialized lists work in Unity
  168. m_RenderPasses.arraySize++;
  169. var componentProp = m_RenderPasses.GetArrayElementAtIndex(m_RenderPasses.arraySize - 1);
  170. componentProp.objectReferenceValue = component;
  171. // Update GUID Map
  172. m_RenderPassMap.arraySize++;
  173. var guidProp = m_RenderPassMap.GetArrayElementAtIndex(m_RenderPassMap.arraySize - 1);
  174. guidProp.longValue = localId;
  175. serializedObject.ApplyModifiedProperties();
  176. // Force save / refresh
  177. if (EditorUtility.IsPersistent(target))
  178. {
  179. m_SaveAsset = true;
  180. }
  181. serializedObject.ApplyModifiedProperties();
  182. }
  183. private void RemoveComponent(int id)
  184. {
  185. var property = m_RenderPasses.GetArrayElementAtIndex(id);
  186. var component = property.objectReferenceValue;
  187. property.objectReferenceValue = null;
  188. Undo.SetCurrentGroupName(component == null ? "Remove Renderer Feature" : $"Remove {component.name}");
  189. // remove the array index itself from the list
  190. m_RenderPasses.DeleteArrayElementAtIndex(id);
  191. m_RenderPassMap.DeleteArrayElementAtIndex(id);
  192. serializedObject.ApplyModifiedProperties();
  193. // Destroy the setting object after ApplyModifiedProperties(). If we do it before, redo
  194. // actions will be in the wrong order and the reference to the setting object in the
  195. // list will be lost.
  196. if (component != null) { Undo.DestroyObjectImmediate(component); }
  197. // Force save / refresh
  198. m_SaveAsset = true;
  199. }
  200. private void MoveComponent(int id, int offset)
  201. {
  202. Undo.SetCurrentGroupName("Move Render Feature");
  203. serializedObject.Update();
  204. m_RenderPasses.MoveArrayElement(id, id + offset);
  205. m_RenderPassMap.MoveArrayElement(id, id + offset);
  206. serializedObject.ApplyModifiedProperties();
  207. // Force save / refresh
  208. m_SaveAsset = true;
  209. }
  210. private string GetMenuNameFromType(Type type)
  211. {
  212. var path = type.Name;
  213. if (type.Namespace != null)
  214. {
  215. if (type.Namespace.Contains("Experimental"))
  216. path += " (Experimental)";
  217. }
  218. // Inserts blank space in between camel case strings
  219. return Regex.Replace(Regex.Replace(path, "([a-z])([A-Z])", "$1 $2", RegexOptions.Compiled),
  220. "([A-Z])([A-Z][a-z])", "$1 $2", RegexOptions.Compiled);
  221. }
  222. private string ValidateName(string name)
  223. {
  224. name = Regex.Replace(name, @"[^a-zA-Z0-9 ]", "");
  225. return name;
  226. }
  227. }
  228. }