VFXSlotContainerEditor.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413
  1. using System;
  2. using System.Linq;
  3. using System.Collections;
  4. using System.Collections.Generic;
  5. using UnityEditor;
  6. using UnityEditor.Overlays;
  7. using UnityEditor.Experimental;
  8. using UnityEditor.SceneManagement;
  9. using UnityEngine;
  10. using UnityEngine.SceneManagement;
  11. using UnityEngine.VFX;
  12. using UnityEditor.VFX;
  13. using UnityEditor.VFX.UI;
  14. using UnityObject = UnityEngine.Object;
  15. using UnityEditorInternal;
  16. using System.Reflection;
  17. [CustomEditor(typeof(VFXModel), true)]
  18. [CanEditMultipleObjects]
  19. class VFXSlotContainerEditor : Editor
  20. {
  21. protected void OnEnable()
  22. {
  23. SceneView.duringSceneGui += OnSceneGUI;
  24. }
  25. protected void OnDisable()
  26. {
  27. SceneView.duringSceneGui -= OnSceneGUI;
  28. if (s_EffectUi == this)
  29. s_EffectUi = null;
  30. }
  31. protected virtual SerializedProperty FindProperty(VFXSetting setting)
  32. {
  33. return serializedObject.FindProperty(setting.field.Name);
  34. }
  35. struct NameNType
  36. {
  37. public string name;
  38. public Type type;
  39. public override int GetHashCode()
  40. {
  41. return name.GetHashCode() * 23 + type.GetHashCode();
  42. }
  43. }
  44. public virtual SerializedProperty DoInspectorGUI()
  45. {
  46. var slotContainer = targets[0] as VFXModel;
  47. List<VFXSetting> settingFields = slotContainer.GetSettings(false, VFXSettingAttribute.VisibleFlags.InInspector).ToList();
  48. for (int i = 1; i < targets.Length; ++i)
  49. {
  50. IEnumerable<VFXSetting> otherSettingFields = (targets[i] as VFXModel).GetSettings(false, VFXSettingAttribute.VisibleFlags.InInspector).ToArray();
  51. var excluded = new HashSet<NameNType>(settingFields.Select(t => new NameNType() { name = t.name, type = t.field.FieldType }).Except(otherSettingFields.Select(t => new NameNType() { name = t.name, type = t.field.FieldType })));
  52. settingFields.RemoveAll(t => excluded.Any(u => u.name == t.name));
  53. }
  54. SerializedProperty modifiedSetting = null;
  55. foreach (var prop in settingFields.Select(t => new KeyValuePair<VFXSetting, SerializedProperty>(t, FindProperty(t))).Where(t => t.Value != null))
  56. {
  57. var fieldInfo = prop.Key.field;
  58. EditorGUI.BeginChangeCheck();
  59. var stringAttribute = fieldInfo.GetCustomAttributes<StringProviderAttribute>(true);
  60. var rangeAttribute = fieldInfo.GetCustomAttributes<RangeAttribute>(false).FirstOrDefault();
  61. if (stringAttribute.Any())
  62. {
  63. var strings = StringPropertyRM.FindStringProvider(stringAttribute.ToArray())();
  64. int selected = prop.Value.hasMultipleDifferentValues ? -1 : System.Array.IndexOf(strings, prop.Value.stringValue);
  65. int result = EditorGUILayout.Popup(ObjectNames.NicifyVariableName(prop.Value.name), selected, strings);
  66. if (result != selected)
  67. {
  68. prop.Value.stringValue = strings[result];
  69. }
  70. }
  71. else if (fieldInfo.FieldType.IsEnum && fieldInfo.FieldType.GetCustomAttributes(typeof(FlagsAttribute), false).Length == 0)
  72. {
  73. GUIContent[] enumNames = null;
  74. int[] enumValues = null;
  75. Array enums = Enum.GetValues(fieldInfo.FieldType);
  76. List<int> values = new List<int>(enums.Length);
  77. for (int i = 0; i < enums.Length; ++i)
  78. {
  79. values.Add((int)enums.GetValue(i));
  80. }
  81. foreach (var target in targets)
  82. {
  83. VFXModel targetIte = target as VFXModel;
  84. var filteredValues = targetIte.GetFilteredOutEnumerators(fieldInfo.Name);
  85. if (filteredValues != null)
  86. foreach (int val in filteredValues)
  87. values.Remove(val);
  88. }
  89. enumNames = values.Select(t => new GUIContent(Enum.GetName(fieldInfo.FieldType, t))).ToArray();
  90. enumValues = values.ToArray();
  91. HeaderAttribute attr = fieldInfo.GetCustomAttributes<HeaderAttribute>().FirstOrDefault();
  92. if (attr != null)
  93. GUILayout.Label(attr.header, EditorStyles.boldLabel);
  94. EditorGUILayout.IntPopup(prop.Value, enumNames, enumValues);
  95. }
  96. else if (fieldInfo.FieldType == typeof(int)
  97. && rangeAttribute != null
  98. && fieldInfo.GetCustomAttributes<DelayedAttribute>().Any())
  99. {
  100. //Workaround: Range & Delayed attribute are incompatible, avoid the slider usage to keep the delayed behavior
  101. var newValue = EditorGUILayout.DelayedIntField(ObjectNames.NicifyVariableName(prop.Value.name), prop.Value.intValue);
  102. if (EditorGUI.EndChangeCheck())
  103. {
  104. newValue = Mathf.Clamp(newValue, (int)rangeAttribute.min, (int)rangeAttribute.max);
  105. prop.Value.intValue = newValue;
  106. modifiedSetting = prop.Value;
  107. }
  108. }
  109. else
  110. {
  111. bool visibleChildren = EditorGUILayout.PropertyField(prop.Value);
  112. if (visibleChildren)
  113. {
  114. SerializedProperty childProp = prop.Value.Copy();
  115. while (childProp != null && childProp.NextVisible(visibleChildren) && childProp.propertyPath.StartsWith(prop.Value.propertyPath + "."))
  116. {
  117. visibleChildren = EditorGUILayout.PropertyField(childProp);
  118. }
  119. }
  120. }
  121. if (EditorGUI.EndChangeCheck())
  122. {
  123. modifiedSetting = prop.Value;
  124. }
  125. }
  126. return modifiedSetting;
  127. }
  128. IGizmoController m_CurrentController;
  129. static VFXSlotContainerEditor s_EffectUi;
  130. [Overlay(typeof(SceneView), k_OverlayId, k_DisplayName)]
  131. class SceneViewVFXSlotContainerOverlay : IMGUIOverlay, ITransientOverlay
  132. {
  133. const string k_OverlayId = "Scene View/Visual Effect Model";
  134. const string k_DisplayName = "Visual Effect Model";
  135. public bool visible => s_EffectUi != null;
  136. public override void OnGUI()
  137. {
  138. if (s_EffectUi == null)
  139. return;
  140. s_EffectUi.SceneViewGUICallback();
  141. }
  142. }
  143. void OnSceneGUI(SceneView sv)
  144. {
  145. try // make sure we don't break the whole scene
  146. {
  147. var slotContainer = targets[0] as VFXModel;
  148. if (VFXViewWindow.currentWindow != null)
  149. {
  150. VFXView view = VFXViewWindow.currentWindow.graphView;
  151. if (view.controller != null && view.controller.model && view.controller.graph == slotContainer.GetGraph())
  152. {
  153. if (slotContainer is VFXParameter)
  154. {
  155. var controller = view.controller.GetParameterController(slotContainer as VFXParameter);
  156. m_CurrentController = controller;
  157. if (controller != null)
  158. controller.DrawGizmos(view.attachedComponent);
  159. }
  160. else
  161. {
  162. m_CurrentController = view.controller.GetNodeController(slotContainer, 0);
  163. }
  164. if (m_CurrentController != null)
  165. {
  166. m_CurrentController.DrawGizmos(view.attachedComponent);
  167. if (m_CurrentController.gizmoables.Count > 0)
  168. {
  169. s_EffectUi = this;
  170. }
  171. else
  172. {
  173. s_EffectUi = null;
  174. }
  175. }
  176. }
  177. else
  178. {
  179. m_CurrentController = null;
  180. }
  181. }
  182. }
  183. catch (System.Exception e)
  184. {
  185. Debug.LogException(e);
  186. }
  187. finally
  188. {
  189. }
  190. }
  191. internal virtual void SceneViewGUICallback()
  192. {
  193. if (m_CurrentController == null)
  194. return;
  195. var gizmoableAnchors = m_CurrentController.gizmoables;
  196. if (gizmoableAnchors.Count > 0)
  197. {
  198. int current = gizmoableAnchors.IndexOf(m_CurrentController.currentGizmoable);
  199. EditorGUI.BeginChangeCheck();
  200. GUILayout.BeginHorizontal();
  201. GUI.enabled = gizmoableAnchors.Count > 1;
  202. int result = EditorGUILayout.Popup(current, gizmoableAnchors.Select(t => t.name).ToArray());
  203. GUI.enabled = true;
  204. if (EditorGUI.EndChangeCheck() && result != current)
  205. {
  206. m_CurrentController.currentGizmoable = gizmoableAnchors[result];
  207. }
  208. var slotContainer = targets[0] as VFXModel;
  209. bool hasvfxViewOpened = VFXViewWindow.currentWindow != null && VFXViewWindow.currentWindow.graphView.controller != null && VFXViewWindow.currentWindow.graphView.controller.graph == slotContainer.GetGraph();
  210. if (m_CurrentController.gizmoIndeterminate)
  211. {
  212. GUILayout.Label(Contents.gizmoIndeterminateWarning, Styles.warningStyle, GUILayout.Width(19), GUILayout.Height(18));
  213. }
  214. else if (m_CurrentController.gizmoNeedsComponent && (!hasvfxViewOpened || VFXViewWindow.currentWindow.graphView.attachedComponent == null))
  215. {
  216. GUILayout.Label(Contents.gizmoLocalWarning, Styles.warningStyle, GUILayout.Width(19), GUILayout.Height(18));
  217. }
  218. else
  219. {
  220. if (GUILayout.Button(Contents.gizmoFrame, Styles.frameButtonStyle, GUILayout.Width(16), GUILayout.Height(16)))
  221. {
  222. if (m_CurrentController != null && VFXViewWindow.currentWindow != null)
  223. {
  224. VFXView view = VFXViewWindow.currentWindow.graphView;
  225. if (view.controller != null && view.controller.model && view.controller.graph == slotContainer.GetGraph())
  226. {
  227. Bounds b = m_CurrentController.GetGizmoBounds(view.attachedComponent);
  228. var sceneView = SceneView.lastActiveSceneView;
  229. if (b.size.sqrMagnitude > Mathf.Epsilon && sceneView)
  230. sceneView.Frame(b, false);
  231. }
  232. }
  233. }
  234. }
  235. GUILayout.EndHorizontal();
  236. }
  237. }
  238. public override void OnInspectorGUI()
  239. {
  240. serializedObject.Update();
  241. var referenceModel = serializedObject.targetObject as VFXModel;
  242. var resource = referenceModel.GetResource();
  243. GUI.enabled = resource != null ? resource.IsAssetEditable() : true;
  244. SerializedProperty modifiedProperty = DoInspectorGUI();
  245. if (modifiedProperty != null && modifiedProperty.serializedObject.ApplyModifiedProperties())
  246. {
  247. foreach (VFXModel slotContainer in modifiedProperty.serializedObject.targetObjects)
  248. {
  249. // notify that something changed.
  250. slotContainer.OnSettingModified(slotContainer.GetSetting(modifiedProperty.propertyPath));
  251. slotContainer.Invalidate(VFXModel.InvalidationCause.kSettingChanged);
  252. }
  253. }
  254. serializedObject.ApplyModifiedProperties();
  255. }
  256. public class Contents
  257. {
  258. public static GUIContent name = EditorGUIUtility.TrTextContent("Name");
  259. public static GUIContent type = EditorGUIUtility.TrTextContent("Type");
  260. public static GUIContent mode = EditorGUIUtility.TrTextContent("Mode");
  261. public static GUIContent gizmoLocalWarning = EditorGUIUtility.TrIconContent(EditorGUIUtility.LoadIcon(EditorResources.iconsPath + "console.warnicon.sml.png"), "Local values require a target GameObject to display");
  262. public static GUIContent gizmoIndeterminateWarning = EditorGUIUtility.TrIconContent(EditorGUIUtility.LoadIcon(EditorResources.iconsPath + "console.warnicon.sml.png"), "The gizmo value is indeterminate.");
  263. public static GUIContent gizmoFrame = EditorGUIUtility.TrTextContent("", "Frame Gizmo in scene");
  264. }
  265. public class Styles
  266. {
  267. public static GUIStyle header;
  268. public static GUIStyle cell;
  269. public static GUIStyle foldout;
  270. public static GUIStyle letter;
  271. public static GUIStyle warningStyle;
  272. public static GUIStyle frameButtonStyle;
  273. static Styles()
  274. {
  275. warningStyle = new GUIStyle(); // margin are steup so that the warning takes the same space as the frame button
  276. warningStyle.margin.top = 1;
  277. warningStyle.margin.bottom = 1;
  278. warningStyle.margin.left = 2;
  279. warningStyle.margin.right = 1;
  280. frameButtonStyle = new GUIStyle();
  281. frameButtonStyle.normal.background = EditorGUIUtility.LoadIconForSkin(EditorResources.iconsPath + "ViewToolZoom.png", EditorGUIUtility.skinIndex);
  282. frameButtonStyle.active.background = EditorGUIUtility.LoadIconForSkin(EditorResources.iconsPath + "ViewToolZoom On.png", EditorGUIUtility.skinIndex);
  283. frameButtonStyle.normal.background.filterMode = FilterMode.Trilinear;
  284. frameButtonStyle.active.background.filterMode = FilterMode.Trilinear;
  285. header = new GUIStyle(EditorStyles.toolbarButton);
  286. header.fontStyle = FontStyle.Bold;
  287. header.alignment = TextAnchor.MiddleLeft;
  288. cell = new GUIStyle(EditorStyles.toolbarButton);
  289. var bg = cell.onActive.background;
  290. cell.active.background = bg;
  291. cell.onActive.background = bg;
  292. cell.normal.background = bg;
  293. cell.onNormal.background = bg;
  294. cell.focused.background = bg;
  295. cell.onFocused.background = bg;
  296. cell.hover.background = bg;
  297. cell.onHover.background = bg;
  298. cell.alignment = TextAnchor.MiddleLeft;
  299. foldout = new GUIStyle(EditorStyles.foldout);
  300. foldout.fontStyle = FontStyle.Bold;
  301. letter = new GUIStyle(GUI.skin.label);
  302. letter.fontSize = 36;
  303. }
  304. static Dictionary<VFXValueType, Color> valueTypeColors = new Dictionary<VFXValueType, Color>()
  305. {
  306. { VFXValueType.Boolean, new Color32(125, 110, 191, 255) },
  307. { VFXValueType.ColorGradient, new Color32(130, 223, 226, 255) },
  308. { VFXValueType.Curve, new Color32(130, 223, 226, 255) },
  309. { VFXValueType.Float, new Color32(130, 223, 226, 255) },
  310. { VFXValueType.Float2, new Color32(154, 239, 146, 255) },
  311. { VFXValueType.Float3, new Color32(241, 250, 151, 255) },
  312. { VFXValueType.Float4, new Color32(246, 199, 239, 255) },
  313. { VFXValueType.Int32, new Color32(125, 110, 191, 255) },
  314. { VFXValueType.Matrix4x4, new Color32(118, 118, 118, 255) },
  315. { VFXValueType.Mesh, new Color32(130, 223, 226, 255) },
  316. { VFXValueType.None, new Color32(118, 118, 118, 255) },
  317. { VFXValueType.Spline, new Color32(130, 223, 226, 255) },
  318. { VFXValueType.Texture2D, new Color32(250, 137, 137, 255) },
  319. { VFXValueType.Texture2DArray, new Color32(250, 137, 137, 255) },
  320. { VFXValueType.Texture3D, new Color32(250, 137, 137, 255) },
  321. { VFXValueType.TextureCube, new Color32(250, 137, 137, 255) },
  322. { VFXValueType.TextureCubeArray, new Color32(250, 137, 137, 255) },
  323. { VFXValueType.CameraBuffer, new Color32(250, 137, 137, 255) },
  324. { VFXValueType.Uint32, new Color32(125, 110, 191, 255) },
  325. };
  326. internal static void DataTypeLabel(Rect r, string Label, VFXValueType type, GUIStyle style)
  327. {
  328. Color backup = GUI.color;
  329. GUI.color = valueTypeColors[type];
  330. GUI.Label(r, Label, style);
  331. GUI.color = backup;
  332. }
  333. internal static void DataTypeLabel(string Label, VFXValueType type, GUIStyle style, params GUILayoutOption[] options)
  334. {
  335. Color backup = GUI.color;
  336. GUI.color = valueTypeColors[type];
  337. GUILayout.Label(Label, style, options);
  338. GUI.color = backup;
  339. }
  340. internal static void AttributeModeLabel(string Label, VFXAttributeMode mode, GUIStyle style, params GUILayoutOption[] options)
  341. {
  342. Color backup = GUI.color;
  343. var c = new Color32(160, 160, 160, 255);
  344. if ((mode & VFXAttributeMode.Read) != 0)
  345. c.b = 255;
  346. if ((mode & VFXAttributeMode.Write) != 0)
  347. c.r = 255;
  348. if ((mode & VFXAttributeMode.ReadSource) != 0)
  349. c.g = 255;
  350. GUI.color = c;
  351. GUILayout.Label(Label, style, options);
  352. GUI.color = backup;
  353. }
  354. public static void Row(GUIStyle style, params string[] labels)
  355. {
  356. using (new EditorGUILayout.HorizontalScope())
  357. {
  358. foreach (string label in labels)
  359. EditorGUILayout.LabelField(label, style);
  360. }
  361. }
  362. }
  363. }