#if IGNORE_ACCESS_CHECKS // [ASMDEFEX] DO NOT REMOVE THIS LINE MANUALLY. using UnityEditor; using UnityEditor.UI; using UnityEngine; using System.Collections.Generic; using System.Linq; using System; using ShaderPropertyType = Coffee.UIExtensions.UIParticle.AnimatableProperty.ShaderPropertyType; namespace Coffee.UIExtensions { [CustomEditor(typeof(UIParticle))] [CanEditMultipleObjects] public class UIParticleEditor : GraphicEditor { class AnimatedPropertiesEditor { static readonly List s_ActiveNames = new List(); static readonly System.Text.StringBuilder s_Sb = new System.Text.StringBuilder(); public string name; public ShaderPropertyType type; static string CollectActiveNames(SerializedProperty sp, List result) { result.Clear(); for (int i = 0; i < sp.arraySize; i++) { result.Add(sp.GetArrayElementAtIndex(i).FindPropertyRelative("m_Name").stringValue); } s_Sb.Length = 0; if (result.Count == 0) { s_Sb.Append("Nothing"); } else { result.Aggregate(s_Sb, (a, b) => s_Sb.AppendFormat("{0}, ", b)); s_Sb.Length -= 2; } return s_Sb.ToString(); } public static void DrawAnimatableProperties(SerializedProperty sp, Material mat) { if (!mat || !mat.shader) return; bool isClicked = false; using (new EditorGUILayout.HorizontalScope(GUILayout.ExpandWidth(false))) { var r = EditorGUI.PrefixLabel(EditorGUILayout.GetControlRect(true), new GUIContent(sp.displayName, sp.tooltip)); isClicked = GUI.Button(r, CollectActiveNames(sp, s_ActiveNames), EditorStyles.popup); } if (isClicked) { GenericMenu gm = new GenericMenu(); gm.AddItem(new GUIContent("Nothing"), s_ActiveNames.Count == 0, () => { sp.ClearArray(); sp.serializedObject.ApplyModifiedProperties(); }); for (int i = 0; i < sp.arraySize; i++) { var p = sp.GetArrayElementAtIndex(i); var name = p.FindPropertyRelative("m_Name").stringValue; var type = (ShaderPropertyType) p.FindPropertyRelative("m_Type").intValue; AddMenu(gm, sp, new AnimatedPropertiesEditor() {name = name, type = type}, false); } for (int i = 0; i < ShaderUtil.GetPropertyCount(mat.shader); i++) { var pName = ShaderUtil.GetPropertyName(mat.shader, i); var type = (ShaderPropertyType) ShaderUtil.GetPropertyType(mat.shader, i); AddMenu(gm, sp, new AnimatedPropertiesEditor() {name = pName, type = type}, true); if (type == ShaderPropertyType.Texture) { AddMenu(gm, sp, new AnimatedPropertiesEditor() {name = pName + "_ST", type = ShaderPropertyType.Vector}, true); AddMenu(gm, sp, new AnimatedPropertiesEditor() {name = pName + "_HDR", type = ShaderPropertyType.Vector}, true); AddMenu(gm, sp, new AnimatedPropertiesEditor() {name = pName + "_TexelSize", type = ShaderPropertyType.Vector}, true); } } gm.ShowAsContext(); } } public static void AddMenu(GenericMenu menu, SerializedProperty sp, AnimatedPropertiesEditor property, bool add) { if (add && s_ActiveNames.Contains(property.name)) return; menu.AddItem(new GUIContent(string.Format("{0} ({1})", property.name, property.type)), s_ActiveNames.Contains(property.name), () => { var index = s_ActiveNames.IndexOf(property.name); if (0 <= index) { sp.DeleteArrayElementAtIndex(index); } else { sp.InsertArrayElementAtIndex(sp.arraySize); var p = sp.GetArrayElementAtIndex(sp.arraySize - 1); p.FindPropertyRelative("m_Name").stringValue = property.name; p.FindPropertyRelative("m_Type").intValue = (int) property.type; } sp.serializedObject.ApplyModifiedProperties(); }); } } //################################ // Constant or Static Members. //################################ static readonly GUIContent s_ContentParticleMaterial = new GUIContent("Particle Material", "The material for rendering particles"); static readonly GUIContent s_ContentTrailMaterial = new GUIContent("Trail Material", "The material for rendering particle trails"); static readonly List s_ParticleSystems = new List(); static readonly Color s_GizmoColor = new Color(1f, 0.7f, 0.7f, 0.9f); static readonly List s_MaskablePropertyNames = new List() { "_Stencil", "_StencilComp", "_StencilOp", "_StencilWriteMask", "_StencilReadMask", "_ColorMask", }; //################################ // Public/Protected Members. //################################ /// /// This function is called when the object becomes enabled and active. /// protected override void OnEnable() { base.OnEnable(); _spParticleSystem = serializedObject.FindProperty("m_ParticleSystem"); _spTrailParticle = serializedObject.FindProperty("m_TrailParticle"); _spScale = serializedObject.FindProperty("m_Scale"); _spIgnoreParent = serializedObject.FindProperty("m_IgnoreParent"); _spAnimatableProperties = serializedObject.FindProperty("m_AnimatableProperties"); _particles = targets.Cast().ToArray(); _shapeModuleUIs = null; var targetsGos = targets.Cast().Select(x => x.gameObject).ToArray(); _inspector = Resources.FindObjectsOfTypeAll() .FirstOrDefault(x => x.targets.Cast().Select(x => x.gameObject).SequenceEqual(targetsGos)); } /// /// Implement this function to make a custom inspector. /// public override void OnInspectorGUI() { serializedObject.Update(); EditorGUI.BeginDisabledGroup(true); EditorGUILayout.PropertyField(_spParticleSystem); EditorGUI.EndDisabledGroup(); EditorGUI.indentLevel++; var ps = _spParticleSystem.objectReferenceValue as ParticleSystem; if (ps) { var pr = ps.GetComponent(); var sp = new SerializedObject(pr).FindProperty("m_Materials"); EditorGUI.BeginChangeCheck(); { EditorGUILayout.PropertyField(sp.GetArrayElementAtIndex(0), s_ContentParticleMaterial); if (2 <= sp.arraySize) { EditorGUILayout.PropertyField(sp.GetArrayElementAtIndex(1), s_ContentTrailMaterial); } } if (EditorGUI.EndChangeCheck()) { sp.serializedObject.ApplyModifiedProperties(); } if (!Application.isPlaying && pr.enabled) { EditorGUILayout.HelpBox("UIParticles disable the RendererModule in ParticleSystem at runtime to prevent double rendering.", MessageType.Warning); } } EditorGUI.indentLevel--; EditorGUI.BeginDisabledGroup(true); EditorGUILayout.PropertyField(_spTrailParticle); EditorGUI.EndDisabledGroup(); var current = target as UIParticle; EditorGUILayout.PropertyField(_spIgnoreParent); EditorGUI.BeginDisabledGroup(!current.isRoot); EditorGUILayout.PropertyField(_spScale); EditorGUI.EndDisabledGroup(); // AnimatableProperties AnimatedPropertiesEditor.DrawAnimatableProperties(_spAnimatableProperties, current.material); current.GetComponentsInChildren(true, s_ParticleSystems); if (s_ParticleSystems.Any(x => x.GetComponent() == null)) { GUILayout.BeginHorizontal(); EditorGUILayout.HelpBox("There are child ParticleSystems that does not have a UIParticle component.\nAdd UIParticle component to them.", MessageType.Warning); GUILayout.BeginVertical(); if (GUILayout.Button("Fix")) { foreach (var p in s_ParticleSystems.Where(x => !x.GetComponent())) { p.gameObject.AddComponent(); } } GUILayout.EndVertical(); GUILayout.EndHorizontal(); } s_ParticleSystems.Clear(); if (current.maskable && current.material && current.material.shader) { var mat = current.material; var shader = mat.shader; foreach (var propName in s_MaskablePropertyNames) { if (!mat.HasProperty(propName)) { EditorGUILayout.HelpBox(string.Format("Shader {0} doesn't have '{1}' property. This graphic is not maskable.", shader.name, propName), MessageType.Warning); break; } } } serializedObject.ApplyModifiedProperties(); } //################################ // Private Members. //################################ SerializedProperty _spParticleSystem; SerializedProperty _spTrailParticle; SerializedProperty _spScale; SerializedProperty _spIgnoreParent; SerializedProperty _spAnimatableProperties; UIParticle[] _particles; ShapeModuleUI[] _shapeModuleUIs; ParticleSystemInspector _inspector; void OnSceneGUI() { _shapeModuleUIs = _shapeModuleUIs ?? _inspector?.m_ParticleEffectUI?.m_Emitters?.SelectMany(x => x.m_Modules).OfType()?.ToArray(); if (_shapeModuleUIs == null || _shapeModuleUIs.Length == 0 || _shapeModuleUIs[0].GetParticleSystem() != (target as UIParticle).cachedParticleSystem) return; Action postAction = () => { }; Color origin = ShapeModuleUI.s_GizmoColor.m_Color; Color originDark = ShapeModuleUI.s_GizmoColor.m_Color; ShapeModuleUI.s_GizmoColor.m_Color = s_GizmoColor; ShapeModuleUI.s_GizmoColor.m_OptionalDarkColor = s_GizmoColor; _particles .Distinct() .Select(x => new {canvas = x.canvas, ps = x.cachedParticleSystem, scale = x.scale}) .Where(x => x.ps && x.canvas) .ToList() .ForEach(x => { var trans = x.ps.transform; var hasChanged = trans.hasChanged; var localScale = trans.localScale; postAction += () => trans.localScale = localScale; trans.localScale = Vector3.Scale(localScale, x.canvas.rootCanvas.transform.localScale * x.scale); }); try { foreach (var ui in _shapeModuleUIs) ui.OnSceneViewGUI(); } catch { } postAction(); ShapeModuleUI.s_GizmoColor.m_Color = origin; ShapeModuleUI.s_GizmoColor.m_OptionalDarkColor = originDark; } } } #endif // [ASMDEFEX] DO NOT REMOVE THIS LINE MANUALLY.