using UnityEngine; using UnityEditor; using System.Collections.Generic; using System.IO; using System.Reflection; using System; using UnityEditorInternal; namespace XSToonDynamicPenetration { public class XSGradientEditor : EditorWindow { public List gradients_index = new List(new int[1] { 0 }); public List gradients = new List(5); public Texture2D tex; private string finalFilePath; private bool isLinear = false; private bool manualMaterial = false; private enum Resolutions { Tiny64x8 = 64, Small128x8 = 128, Medium256x8 = 256, Large512x8 = 512 } private Resolutions res = Resolutions.Tiny64x8; public static Material focusedMat; private Material oldFocusedMat; private Texture oldTexture; private string rampProperty = "_Ramp"; private ReorderableList grad_index_reorderable; private bool reorder; private static GUIContent iconToolbarPlus; private static GUIContent iconToolbarMinus; private static GUIStyle preButton; private static GUIStyle buttonBackground; private bool changed; private int loadGradIndex; private XSMultiGradient xsmg; private Vector2 scrollPos; private bool dHelpText = false; //private bool dAdvanced = false; [MenuItem("Tools/Xiexe/XSToon/Gradient Editor")] static public void Init() { XSGradientEditor window = EditorWindow.GetWindow(false, "XSToon: Gradient Editor", true); window.minSize = new Vector2(450, 390); } public void OnGUI() { changed = false; if (focusedMat != null) { XSStyles.ShurikenHeader("Current Material: " + focusedMat.name); } else { XSStyles.ShurikenHeader("Current Material: None"); } if (preButton == null) { iconToolbarPlus = EditorGUIUtility.IconContent("Toolbar Plus", "Add Gradient"); iconToolbarMinus = EditorGUIUtility.IconContent("Toolbar Minus", "Remove Gradient"); preButton = new GUIStyle("RL FooterButton"); buttonBackground = new GUIStyle("RL Header"); } if (gradients.Count == 0) { gradients.Add(new Gradient()); gradients.Add(new Gradient()); gradients.Add(new Gradient()); gradients.Add(new Gradient()); gradients.Add(new Gradient()); } if (grad_index_reorderable == null) { makeReorderedList(); } GUILayout.BeginHorizontal(); GUILayout.FlexibleSpace(); Rect r = EditorGUILayout.GetControlRect(); float rightEdge = r.xMax; float leftEdge = rightEdge - 48f; r = new Rect(leftEdge, r.y, rightEdge - leftEdge, r.height); if (Event.current.type == EventType.Repaint) buttonBackground.Draw(r, false, false, false, false); leftEdge += 18f; EditorGUI.BeginDisabledGroup(gradients_index.Count == 5); bool addE = GUI.Button(new Rect(leftEdge + 4, r.y, 25, 13), iconToolbarPlus, preButton); EditorGUI.EndDisabledGroup(); EditorGUI.BeginDisabledGroup(gradients_index.Count == 1); bool removeE = GUI.Button(new Rect(leftEdge - 19, r.y, 25, 13), iconToolbarMinus, preButton); EditorGUI.EndDisabledGroup(); if (addE) { grad_index_reorderable.index++; int wat = 0; for (int i = 0; i < 5; i++) { if (!gradients_index.Contains(i)) { wat = i; break; } } gradients_index.Add(wat); changed = true; } if (removeE) { gradients_index.Remove(gradients_index[gradients_index.Count - 1]); grad_index_reorderable.index--; changed = true; } GUIStyle button = new GUIStyle(EditorStyles.miniButton); button.normal = !reorder ? EditorStyles.miniButton.normal : EditorStyles.miniButton.onNormal; if (GUILayout.Button(new GUIContent("Reorder", "Don't use Reorder if you want to undo a gradient change"), button, GUILayout.ExpandWidth(false))) { reorder = !reorder; } GUILayout.EndHorizontal(); SerializedObject serializedObject = new SerializedObject(this); if (reorder) { grad_index_reorderable.DoLayoutList(); } else { SerializedProperty colorGradients = serializedObject.FindProperty("gradients"); if (colorGradients.arraySize == 5) { for (int i = 0; i < gradients_index.Count; i++) { Rect _r = EditorGUILayout.GetControlRect(); _r.x += 16f; _r.width -= 2f + 16f; _r.height += 5f; _r.y += 2f + (3f * i); EditorGUI.PropertyField(_r, colorGradients.GetArrayElementAtIndex(gradients_index[i]), new GUIContent("")); } GUILayout.Space(Mathf.Lerp(9f, 24f, gradients_index.Count / 5f)); } } if (serializedObject.ApplyModifiedProperties()) changed = true; if (oldFocusedMat != focusedMat) { changed = true; if (this.oldTexture != null) { if (this.oldTexture == EditorGUIUtility.whiteTexture) this.oldTexture = null; oldFocusedMat.SetTexture(rampProperty, this.oldTexture); this.oldTexture = null; } oldFocusedMat = focusedMat; } Resolutions oldRes = res; res = (Resolutions)EditorGUILayout.EnumPopup("Resolution: ", res); if (oldRes != res) changed = true; int width = (int)res; int height = 30; if (gradients_index.Count == 1) { height = 8; } else { height = 150; } if (tex == null) { tex = new Texture2D(width, height, TextureFormat.RGBA32, false); } bool old_isLinear = isLinear; drawAdvancedOptions(); if (old_isLinear != isLinear) { changed = true; } if (manualMaterial) { focusedMat = (Material)EditorGUILayout.ObjectField(new GUIContent("", ""), focusedMat, typeof(Material), true); } if (focusedMat != null) { if (focusedMat.HasProperty("_Ramp")) { rampProperty = "_Ramp"; } else { rampProperty = EditorGUILayout.TextField("Ramp Property Name", rampProperty); if (!focusedMat.HasProperty(rampProperty)) { GUILayout.Label("Property not found!"); } } } if (changed) { updateTexture(width, height); if (focusedMat != null) { if (focusedMat.HasProperty(rampProperty)) { if (this.oldTexture == null) { if (focusedMat.GetTexture(rampProperty) == null) { this.oldTexture = EditorGUIUtility.whiteTexture; } else { this.oldTexture = focusedMat.GetTexture(rampProperty); } } tex.wrapMode = TextureWrapMode.Clamp; tex.Apply(false, false); focusedMat.SetTexture(rampProperty, tex); } } } XSStyles.Separator(); drawMGInputOutput(); if (GUILayout.Button("Save Ramp")) { finalFilePath = XSStyles.findAssetPath(finalFilePath); string path = EditorUtility.SaveFilePanel("Save Ramp as PNG", finalFilePath + "/Textures/Shadow Ramps/Generated", "gradient", "png"); if (path.Length != 0) { updateTexture(width, height); bool success = GenTexture(tex, path); if (success) { if (focusedMat != null) { string s = path.Substring(path.IndexOf("Assets")); Texture ramp = AssetDatabase.LoadAssetAtPath(s); if (ramp != null) { focusedMat.SetTexture(rampProperty, ramp); this.oldTexture = null; } } } } } drawHelpText(); } Gradient reflessGradient(Gradient old_grad) { Gradient grad = new Gradient(); grad.SetKeys(old_grad.colorKeys, old_grad.alphaKeys); grad.mode = old_grad.mode; return grad; } List reflessIndexes(List old_indexes) { List indexes = new List(); for (int i = 0; i < old_indexes.Count; i++) { indexes.Add(old_indexes[i]); } return indexes; } void makeReorderedList() { grad_index_reorderable = new ReorderableList(gradients_index, typeof(int), true, false, false, false); grad_index_reorderable.headerHeight = 0f; grad_index_reorderable.footerHeight = 0f; grad_index_reorderable.showDefaultBackground = true; grad_index_reorderable.drawElementCallback = (Rect rect, int index, bool isActive, bool isFocused) => { if (gradients.Count == 5) { Type editorGui = typeof(EditorGUI); MethodInfo mi = editorGui.GetMethod("GradientField", BindingFlags.NonPublic | BindingFlags.Static, null, new Type[2] { typeof(Rect), typeof(Gradient) }, null); mi.Invoke(this, new object[2] { rect, gradients[gradients_index[index]] }); if (Event.current.type == EventType.Repaint) { changed = true; } } }; grad_index_reorderable.onChangedCallback = (ReorderableList list) => { changed = true; }; } void OnDestroy() { if (focusedMat != null) { if (this.oldTexture != null) { if (this.oldTexture == EditorGUIUtility.whiteTexture) { this.oldTexture = null; } focusedMat.SetTexture(rampProperty, this.oldTexture); this.oldTexture = null; } focusedMat = null; } } void updateTexture(int width, int height) { tex = new Texture2D(width, height, TextureFormat.RGBA32, false); for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { if (gradients_index.Count != 1) { int gradNum = Mathf.FloorToInt(y / 30f); gradNum = Mathf.Abs(gradNum - 5) - 1; if (gradNum >= gradients_index.Count) { tex.SetPixel(x, y, Color.white); } else { if (gradients[gradients_index[gradNum]] != null) { Color grad_col = gradients[gradients_index[gradNum]].Evaluate((float)x / (float)width); tex.SetPixel(x, y, isLinear ? grad_col.gamma : grad_col); } else { tex.SetPixel(x, y, Color.white); } } } else { Color grad_col = gradients[gradients_index[0]].Evaluate((float)x / (float)width); tex.SetPixel(x, y, isLinear ? grad_col.gamma : grad_col); } } } } bool GenTexture(Texture2D tex, string path) { var pngData = tex.EncodeToPNG(); if (pngData != null) { File.WriteAllBytes(path, pngData); AssetDatabase.Refresh(); return ChangeImportSettings(path); } return false; } bool ChangeImportSettings(string path) { string s = path.Substring(path.LastIndexOf("Assets")); TextureImporter texture = (TextureImporter)TextureImporter.GetAtPath(s); if (texture != null) { texture.wrapMode = TextureWrapMode.Clamp; texture.maxTextureSize = 512; texture.mipmapEnabled = false; texture.textureCompression = TextureImporterCompression.Uncompressed; // texture.sRGBTexture = !isLinear; // We already do the conversion in tex.SetPixel texture.SaveAndReimport(); AssetDatabase.Refresh(); return true; // shadowRamp = (Texture)Resources.Load(path); // Debug.LogWarning(shadowRamp.ToString()); } else { Debug.Log("Asset Path is Null, can't set to Clamped.\n You'll need to do it manually."); } return false; } void drawMGInputOutput() { GUILayout.BeginHorizontal(); XSMultiGradient old_xsmg = xsmg; xsmg = (XSMultiGradient)EditorGUILayout.ObjectField("MultiGradient Preset", xsmg, typeof(XSMultiGradient), false, null); if (xsmg != old_xsmg) { if (xsmg != null) { this.gradients = xsmg.gradients; this.gradients_index = xsmg.order; makeReorderedList(); } else { List new_Grads = new List(); for (int i = 0; i < this.gradients.Count; i++) { new_Grads.Add(reflessGradient(this.gradients[i])); } this.gradients = new_Grads; this.gradients_index = reflessIndexes(this.gradients_index); makeReorderedList(); } changed = true; } if (GUILayout.Button("Save New", EditorStyles.miniButton, GUILayout.ExpandWidth(false))) { finalFilePath = XSStyles.findAssetPath(finalFilePath); string path = EditorUtility.SaveFilePanel("Save MultiGradient", (finalFilePath + "/Textures/Shadow Ramps/MGPresets"), "MultiGradient", "asset"); if (path.Length != 0) { path = path.Substring(Application.dataPath.Length - "Assets".Length); XSMultiGradient _xsmg = ScriptableObject.CreateInstance(); _xsmg.uniqueName = Path.GetFileNameWithoutExtension(path); foreach (Gradient grad in gradients) { _xsmg.gradients.Add(reflessGradient(grad)); } _xsmg.order.AddRange(gradients_index.ToArray()); xsmg = _xsmg; AssetDatabase.CreateAsset(_xsmg, path); this.gradients = xsmg.gradients; this.gradients_index = xsmg.order; makeReorderedList(); AssetDatabase.SaveAssets(); } } GUILayout.EndHorizontal(); } void drawAdvancedOptions() { GUILayout.BeginHorizontal(); isLinear = GUILayout.Toggle(isLinear, "Make Linear Texture"); manualMaterial = GUILayout.Toggle(manualMaterial, "Manual Material"); GUILayout.EndHorizontal(); } void drawHelpText() { XSStyles.Separator(); dHelpText = XSStyles.ShurikenFoldout("Information", dHelpText); if(dHelpText) { scrollPos = EditorGUILayout.BeginScrollView(scrollPos); XSStyles.HelpBox("You can use this to create a custom shadow ramp in realtime. \nIf you do not save, the ramp will be reverted back to what it was previously. \n\n - Click the Gradient box. \n - Choose resolution of the texture. \n - Save.", MessageType.Info); XSStyles.HelpBox("Ramp textures support up to 5 ramps in one texture. That means you can have up to 5 ramps on a single material. You will need to author a ramp mask to choose which ramp to sample from. \n\nA texture that is fully black would sample from the bottom ramp, a texture that is fully white would sample from the top ramp, and a texture that is half gray would sample from the middle ramp. \n\n A quick tip would be that you can sample from each of the 5 ramps with 0, 0.25, 0.5, 0.75, and 1 on the texture. \n\nThe order of the gradients on the UI is the order that they will be on the texture.", MessageType.Info); EditorGUILayout.EndScrollView(); } } } }