Rasagar/Library/PackageCache/com.unity.visualeffectgraph/Editor/Inspector/VFXSlotContainerEditor.cs
2024-08-26 23:07:20 +03:00

500 lines
22 KiB
C#

using System;
using System.Linq;
using System.Collections.Generic;
using UnityEditor;
using UnityEditor.Overlays;
using UnityEditor.Experimental;
using UnityEngine;
using UnityEngine.VFX;
using UnityEditor.VFX;
using UnityEditor.VFX.UI;
using UnityObject = UnityEngine.Object;
using System.Reflection;
using UnityEngine.Profiling;
using UnityEngine.Rendering;
[CustomEditor(typeof(VFXModel), true)]
[CanEditMultipleObjects]
class VFXSlotContainerEditor : Editor
{
protected virtual SerializedProperty FindProperty(VFXSetting setting)
{
return serializedObject.FindProperty(setting.field.Name);
}
struct NameNType
{
public string name;
public Type type;
public override int GetHashCode()
{
return name.GetHashCode() * 23 + type.GetHashCode();
}
}
public virtual SerializedProperty DoInspectorGUI()
{
var slotContainer = targets[0] as VFXModel;
List<VFXSetting> settingFields = slotContainer.GetSettings(false, VFXSettingAttribute.VisibleFlags.InInspector).ToList();
for (int i = 1; i < targets.Length; ++i)
{
IEnumerable<VFXSetting> otherSettingFields = (targets[i] as VFXModel).GetSettings(false, VFXSettingAttribute.VisibleFlags.InInspector).ToArray();
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 })));
settingFields.RemoveAll(t => excluded.Any(u => u.name == t.name));
}
SerializedProperty modifiedSetting = null;
foreach (var prop in settingFields.Select(t => new KeyValuePair<VFXSetting, SerializedProperty>(t, FindProperty(t))).Where(t => t.Value != null))
{
var fieldInfo = prop.Key.field;
var disabledScope = new EditorGUI.DisabledScope(prop.Key.visibility.HasFlag(VFXSettingAttribute.VisibleFlags.ReadOnly));
EditorGUI.BeginChangeCheck();
var stringAttribute = fieldInfo.GetCustomAttributes<StringProviderAttribute>(true).ToArray();
var rangeAttribute = fieldInfo.GetCustomAttributes<RangeAttribute>(false).FirstOrDefault();
if (stringAttribute.Length > 0)
{
var strings = StringPropertyRM.FindStringProvider(slotContainer, stringAttribute)();
int selected = prop.Value.hasMultipleDifferentValues ? -1 : System.Array.IndexOf(strings, prop.Value.stringValue);
int result = EditorGUILayout.Popup(ObjectNames.NicifyVariableName(prop.Value.name), selected, strings);
if (result != selected)
{
prop.Value.stringValue = strings[result];
}
}
else if (fieldInfo.FieldType.IsEnum && fieldInfo.FieldType.GetCustomAttributes(typeof(FlagsAttribute), false).Length == 0)
{
GUIContent[] enumNames = null;
int[] enumValues = null;
Array enums = Enum.GetValues(fieldInfo.FieldType);
List<int> values = new List<int>(enums.Length);
for (int i = 0; i < enums.Length; ++i)
{
values.Add((int)enums.GetValue(i));
}
foreach (var target in targets)
{
VFXModel targetIte = target as VFXModel;
var filteredValues = targetIte.GetFilteredOutEnumerators(fieldInfo.Name);
if (filteredValues != null)
foreach (int val in filteredValues)
values.Remove(val);
}
enumNames = values.Select(t => new GUIContent(ObjectNames.NicifyVariableName(Enum.GetName(fieldInfo.FieldType, t)))).ToArray();
enumValues = values.ToArray();
HeaderAttribute attr = fieldInfo.GetCustomAttributes<HeaderAttribute>().FirstOrDefault();
if (attr != null)
GUILayout.Label(attr.header, EditorStyles.boldLabel);
EditorGUILayout.IntPopup(prop.Value, enumNames, enumValues);
}
else if (fieldInfo.FieldType == typeof(uint) && fieldInfo.Name == "decalLayer")
{
if (!GraphicsSettings.isScriptableRenderPipelineEnabled || target == null)
continue;
var guiContent = CreateGUIContentWithTooltip(fieldInfo, prop);
prop.Value.uintValue = EditorGUILayout.RenderingLayerMaskField(guiContent, prop.Value.uintValue);
}
else if (fieldInfo.FieldType == typeof(int)
&& rangeAttribute != null
&& fieldInfo.GetCustomAttributes<DelayedAttribute>().Any())
{
//Workaround: Range & Delayed attribute are incompatible, avoid the slider usage to keep the delayed behavior
var guiContent = CreateGUIContentWithTooltip(fieldInfo, prop);
var newValue = EditorGUILayout.DelayedIntField(guiContent, prop.Value.intValue);
if (EditorGUI.EndChangeCheck())
{
newValue = Mathf.Clamp(newValue, (int)rangeAttribute.min, (int)rangeAttribute.max);
prop.Value.intValue = newValue;
modifiedSetting = prop.Value;
}
continue;
}
else
{
bool visibleChildren = EditorGUILayout.PropertyField(prop.Value);
if (visibleChildren)
{
SerializedProperty childProp = prop.Value.Copy();
while (childProp != null && childProp.NextVisible(visibleChildren) && childProp.propertyPath.StartsWith(prop.Value.propertyPath + "."))
{
visibleChildren = EditorGUILayout.PropertyField(childProp);
}
}
}
if (EditorGUI.EndChangeCheck())
{
modifiedSetting = prop.Value;
}
disabledScope.Dispose();
}
return modifiedSetting;
}
private static GUIContent CreateGUIContentWithTooltip(FieldInfo fieldInfo, KeyValuePair<VFXSetting, SerializedProperty> prop)
{
var tooltipAttribute = fieldInfo.GetCustomAttributes<TooltipAttribute>().FirstOrDefault();
GUIContent guiContent;
if (tooltipAttribute != null)
guiContent = new GUIContent(ObjectNames.NicifyVariableName(prop.Value.name),
tooltipAttribute.tooltip);
else
guiContent = new GUIContent(ObjectNames.NicifyVariableName(prop.Value.name));
return guiContent;
}
IGizmoController m_CurrentController;
static VFXSlotContainerEditor s_EffectUi;
[Overlay(typeof(SceneView), k_OverlayId, k_DisplayName)]
internal class SceneViewVFXSlotContainerOverlay : IMGUIOverlay, ITransientOverlay
{
private struct GizmoInfo : IComparable<GizmoInfo>
{
public GizmoInfo(VFXView view, IGizmoController controller, IGizmoable gizmo)
{
this.view = view;
this.controller = controller;
this.gizmo = gizmo;
}
public VFXView view { get; }
public IGizmoController controller { get; }
public IGizmoable gizmo { get; }
public int CompareTo(GizmoInfo other)
{
if (view.attachedComponent == null)
return int.MinValue;
return string.Compare(view.attachedComponent.name, other.view.attachedComponent.name, StringComparison.OrdinalIgnoreCase);
}
}
const string k_OverlayId = "Scene View/Visual Effect Model";
const string k_DisplayName = "Visual Effect Model";
static readonly List<GizmoInfo> s_AllGizmosInfo = new();
static string[] s_Entries;
static bool s_HasGizmos;
static int currentIndex;
public static void UpdateFromVFXView(VFXView vfxView, List<IGizmoController> controllers)
{
Profiler.BeginSample("SceneViewVFXSlotContainerOverlay.UpdateFromVFXView");
try
{
for (int i = s_AllGizmosInfo.Count - 1; i >= 0; i--)
{
var gizmo = s_AllGizmosInfo[i];
if (gizmo.view == vfxView && (controllers == null || controllers.All(x => x != gizmo.controller)))
{
s_AllGizmosInfo.RemoveAt(i);
}
}
if (controllers != null)
{
var index = s_AllGizmosInfo.TakeWhile(x => x.view != vfxView).Count();
foreach (var controller in controllers)
{
controller.CollectGizmos();
if (s_AllGizmosInfo.All(x => x.view != vfxView || x.controller != controller))
{
s_AllGizmosInfo.AddRange(controller.gizmoables.Select(x => new GizmoInfo(vfxView, controller, x)));
}
var currentGizmo = controller.gizmoables.ElementAtOrDefault(currentIndex - index);
if (currentGizmo != null)
{
controller.DrawGizmos(vfxView.attachedComponent);
}
index += controller.gizmoables.Count;
}
}
s_HasGizmos = s_AllGizmosInfo.Count > 0;
}
finally
{
Profiler.EndSample();
}
}
public bool visible => s_HasGizmos;
public override void OnGUI()
{
Profiler.BeginSample("SceneViewVFXSlotContainerOverlay.OnGUI");
try
{
if (s_AllGizmosInfo.Count > 0)
{
s_AllGizmosInfo.Sort();
GUILayout.BeginHorizontal();
try
{
// Cleanup closed views
for (int i = s_AllGizmosInfo.Count - 1; i >= 0; i--)
{
var gizmo = s_AllGizmosInfo[i];
if (gizmo.view == null)
{
s_AllGizmosInfo.RemoveAt(i);
}
}
if (s_AllGizmosInfo.Count == 0)
{
return;
}
if (s_Entries?.Length != s_AllGizmosInfo.Count)
{
s_Entries = new string[s_AllGizmosInfo.Count];
}
for (var i = 0; i < s_AllGizmosInfo.Count; i++)
{
var entry = s_AllGizmosInfo[i];
s_Entries[i] = $"{entry.view.controller.name}, {(string.IsNullOrEmpty(entry.gizmo.name) ? ((VFXController<VFXModel>)entry.controller).name : entry.gizmo.name)}";
}
currentIndex = Math.Clamp(currentIndex, 0, s_Entries.Length - 1);
GUI.enabled = true;
currentIndex = EditorGUILayout.Popup(currentIndex, s_Entries, GUILayout.Height(20));
var currentGizmo = s_AllGizmosInfo[currentIndex];
currentGizmo.controller.currentGizmoable = currentGizmo.gizmo;
var component = currentGizmo.view.attachedComponent;
var gizmoError = currentGizmo.controller.GetGizmoError(component);
if (gizmoError != GizmoError.None)
{
var content = Contents.GetGizmoErrorContent(gizmoError);
GUILayout.Label(content, Styles.warningStyle, GUILayout.Width(19), GUILayout.Height(18));
}
else
{
if (GUILayout.Button(Contents.gizmoFrame, Styles.frameButtonStyle, GUILayout.Width(16), GUILayout.Height(16)))
{
var b = currentGizmo.controller.GetGizmoBounds(component);
var sceneView = SceneView.lastActiveSceneView;
if (b.size.sqrMagnitude > Mathf.Epsilon && sceneView)
sceneView.Frame(b, false);
}
}
}
finally
{
GUILayout.EndHorizontal();
}
}
}
finally
{
Profiler.EndSample();
}
}
}
public override void OnInspectorGUI()
{
serializedObject.Update();
var referenceModel = serializedObject.targetObject as VFXModel;
var resource = referenceModel.GetResource();
GUI.enabled = resource != null ? resource.IsAssetEditable() : true;
SerializedProperty modifiedProperty = DoInspectorGUI();
if (modifiedProperty != null && modifiedProperty.serializedObject.ApplyModifiedProperties())
{
foreach (VFXModel slotContainer in modifiedProperty.serializedObject.targetObjects)
{
// notify that something changed.
slotContainer.OnSettingModified(slotContainer.GetSetting(modifiedProperty.propertyPath));
slotContainer.Invalidate(VFXModel.InvalidationCause.kSettingChanged);
}
}
serializedObject.ApplyModifiedProperties();
}
public class Contents
{
public static GUIContent name = EditorGUIUtility.TrTextContent("Name");
public static GUIContent type = EditorGUIUtility.TrTextContent("Type");
public static GUIContent mode = EditorGUIUtility.TrTextContent("Mode");
private static Texture2D warningIcon = EditorGUIUtility.LoadIcon(EditorResources.iconsPath + "console.warnicon.sml.png");
public static GUIContent gizmoWarningDefault = EditorGUIUtility.TrIconContent(warningIcon, "The gizmo value is indeterminate.");
public static GUIContent gizmoWarningHasLinkIndeterminate = EditorGUIUtility.TrIconContent(warningIcon, "The gizmo state is indeterminate because the value relies on an indeterminate evaluation.");
public static GUIContent gizmoWarningNeedComponent = EditorGUIUtility.TrIconContent(warningIcon, "Local values require a target GameObject to display");
public static GUIContent gizmoWarningNeedExplicitSpace = EditorGUIUtility.TrIconContent(warningIcon, "The gizmo value needs an explicit Local or World space.");
public static GUIContent gizmoWarningNotAvailable = EditorGUIUtility.TrIconContent(warningIcon, "There isn't any gizmo available.");
public static GUIContent gizmoFrame = EditorGUIUtility.TrTextContent("", "Frame Gizmo in scene");
public static GUIContent GetGizmoErrorContent(GizmoError gizmoError)
{
var content = Contents.gizmoWarningDefault;
if (gizmoError.HasFlag(GizmoError.HasLinkIndeterminate))
{
content = Contents.gizmoWarningHasLinkIndeterminate;
}
else if (gizmoError.HasFlag(GizmoError.NeedComponent))
{
content = Contents.gizmoWarningNeedComponent;
}
else if (gizmoError.HasFlag(GizmoError.NeedExplicitSpace))
{
content = Contents.gizmoWarningNeedExplicitSpace;
}
else if (gizmoError.HasFlag(GizmoError.NotAvailable))
{
content = Contents.gizmoWarningNotAvailable;
}
return content;
}
}
public class Styles
{
public static GUIStyle header;
public static GUIStyle cell;
public static GUIStyle foldout;
public static GUIStyle spawnStyle;
public static GUIStyle particleStyle;
public static GUIStyle particleStripeStyle;
public static GUIStyle meshStyle;
public static GUIStyle warningStyle;
public static GUIStyle frameButtonStyle;
static Styles()
{
warningStyle = new GUIStyle(); // margin are steup so that the warning takes the same space as the frame button
warningStyle.margin.top = 1;
warningStyle.margin.bottom = 1;
warningStyle.margin.left = 2;
warningStyle.margin.right = 1;
warningStyle.alignment = TextAnchor.MiddleLeft;
frameButtonStyle = new GUIStyle();
frameButtonStyle.normal.background = EditorGUIUtility.LoadIconForSkin(EditorResources.iconsPath + "ViewToolZoom.png", EditorGUIUtility.skinIndex);
frameButtonStyle.active.background = EditorGUIUtility.LoadIconForSkin(EditorResources.iconsPath + "ViewToolZoom On.png", EditorGUIUtility.skinIndex);
frameButtonStyle.normal.background.filterMode = FilterMode.Trilinear;
frameButtonStyle.active.background.filterMode = FilterMode.Trilinear;
frameButtonStyle.alignment = TextAnchor.MiddleLeft;
header = new GUIStyle(EditorStyles.toolbarButton);
header.fontStyle = FontStyle.Bold;
header.alignment = TextAnchor.MiddleLeft;
cell = new GUIStyle(EditorStyles.toolbarButton);
var bg = cell.onActive.background;
cell.active.background = bg;
cell.onActive.background = bg;
cell.normal.background = bg;
cell.onNormal.background = bg;
cell.focused.background = bg;
cell.onFocused.background = bg;
cell.hover.background = bg;
cell.onHover.background = bg;
cell.alignment = TextAnchor.MiddleLeft;
foldout = new GUIStyle(EditorStyles.foldout);
foldout.fontStyle = FontStyle.Bold;
spawnStyle = new GUIStyle(GUI.skin.label);
spawnStyle.fontSize = 20;
spawnStyle.normal.textColor = new Color(0f, 1f, 0.5607843f);
spawnStyle.hover.textColor = spawnStyle.normal.textColor;
particleStyle = new GUIStyle(spawnStyle);
particleStyle.normal.textColor = new Color(1f, 0.7372549f, 0.1294118f);
particleStyle.hover.textColor = particleStyle.normal.textColor;
particleStripeStyle = new GUIStyle(spawnStyle);
particleStripeStyle.normal.textColor = new Color(1f, 0.6666667f, 0.4196078f);
particleStripeStyle.hover.textColor = particleStripeStyle.normal.textColor;
meshStyle = new GUIStyle(spawnStyle);
meshStyle.normal.textColor = new Color(0.231f, 0.369f, 0.573f);
meshStyle.hover.textColor = meshStyle.normal.textColor;
}
static Dictionary<VFXValueType, Color> valueTypeColors = new Dictionary<VFXValueType, Color>()
{
{ VFXValueType.Boolean, new Color32(125, 110, 191, 255) },
{ VFXValueType.ColorGradient, new Color32(130, 223, 226, 255) },
{ VFXValueType.Curve, new Color32(130, 223, 226, 255) },
{ VFXValueType.Float, new Color32(130, 223, 226, 255) },
{ VFXValueType.Float2, new Color32(154, 239, 146, 255) },
{ VFXValueType.Float3, new Color32(241, 250, 151, 255) },
{ VFXValueType.Float4, new Color32(246, 199, 239, 255) },
{ VFXValueType.Int32, new Color32(125, 110, 191, 255) },
{ VFXValueType.Matrix4x4, new Color32(118, 118, 118, 255) },
{ VFXValueType.Mesh, new Color32(130, 223, 226, 255) },
{ VFXValueType.None, new Color32(118, 118, 118, 255) },
{ VFXValueType.Spline, new Color32(130, 223, 226, 255) },
{ VFXValueType.Texture2D, new Color32(250, 137, 137, 255) },
{ VFXValueType.Texture2DArray, new Color32(250, 137, 137, 255) },
{ VFXValueType.Texture3D, new Color32(250, 137, 137, 255) },
{ VFXValueType.TextureCube, new Color32(250, 137, 137, 255) },
{ VFXValueType.TextureCubeArray, new Color32(250, 137, 137, 255) },
{ VFXValueType.CameraBuffer, new Color32(250, 137, 137, 255) },
{ VFXValueType.Buffer, new Color32(250, 137, 137, 255) },
{ VFXValueType.Uint32, new Color32(125, 110, 191, 255) },
};
internal static void DataTypeLabel(Rect r, string Label, VFXValueType type, GUIStyle style)
{
Color backup = GUI.color;
GUI.color = valueTypeColors[type];
GUI.Label(r, Label, style);
GUI.color = backup;
}
internal static void DataTypeLabel(string Label, VFXValueType type, GUIStyle style, params GUILayoutOption[] options)
{
Color backup = GUI.color;
GUI.color = valueTypeColors[type];
GUILayout.Label(Label, style, options);
GUI.color = backup;
}
internal static void AttributeModeLabel(string Label, VFXAttributeMode mode, GUIStyle style, params GUILayoutOption[] options)
{
Color backup = GUI.color;
var c = new Color32(160, 160, 160, 255);
if ((mode & VFXAttributeMode.Read) != 0)
c.b = 255;
if ((mode & VFXAttributeMode.Write) != 0)
c.r = 255;
if ((mode & VFXAttributeMode.ReadSource) != 0)
c.g = 255;
GUI.color = c;
GUILayout.Label(Label, style, options);
GUI.color = backup;
}
public static void Row(GUIStyle style, params string[] labels)
{
using (new EditorGUILayout.HorizontalScope())
{
foreach (string label in labels)
EditorGUILayout.LabelField(label, style);
}
}
}
}