#if UNITY_EDITOR using System.Collections.Generic; using UnityEditor; using UnityEngine; using UnityEngine.UI; namespace SpriteShadersUltimate { public class SSUShaderGUI : ShaderGUI { //Toggles: static bool showMasking = false; static bool showNoise = false; static bool showTime = false; //GUI Resources: GUIStyle labelStyle; GUIStyle labelCenterStyle; GUIStyle buttonStyle; static Color warnColor = new Color(1, 0.7f, 0.65f, 0.7f); static Color lightWarnColor = new Color(1f, 0.8f, 0.65f, 0.7f); static Color hintColor = new Color(1, 1, 1, 0.7f); Shader shader; Material defaultMaterial; GameObject gameObject; Dictionary allHints; ShaderHintSSU currentHint; //Internal: HashSet enabledShaders; HashSet toggleProperties; HashSet hidingProperties; HashSet enableProperties; HashSet alwaysHiddenProperties; HashSet linesAboveProperties; List shaderList; Dictionary shaderDictionary; Shader lastShader; bool newCategory; //Used to avoid drawing lines at the beginning of a new category. bool isDisabled; //Skip shaders. bool isHidden; //Used to skip hidden properties. bool skipGUI; //Skip GUI refresh after changing the shader. float shaderSpace; //Used to skip hidden properties. string spaceName; //Used to skip hidden properties. float shaderFading; //Used to skip hidden properties. int currentBoxColor; //Shader Rainbow Color int shaderAmount; //Counter int enabledAmount; //Counter int textureAmount; //Samples float performanceBenchmark; float lastTitleHeight; //For Category Lines Sprite lastSSF; //Sprite for last SSF. static int bakeStackAmount = 1; //Status: static Material[] materials; bool wasWarned; bool requiresFullRectMesh, requiresSpriteSheetFix, requiresInstancing, requiresTiling; public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] properties) { Initialize(materialEditor, properties); DrawStatus(materialEditor); if(skipGUI) { skipGUI = false; return; } //Properties: enabledShaders = new HashSet(); isDisabled = false; isHidden = false; EditorGUILayout.BeginVertical(); for (int n = 0; n < properties.Length; n++) { newCategory = false; MaterialProperty prop = properties[n]; //Always Skipped: if(alwaysHiddenProperties.Contains(prop.name)) { continue; } //Space Skipped: if (prop.name == "_PixelsPerUnit" && !(shaderSpace < 0.5f || (shaderSpace > 4.5f && shaderSpace < 5.5f))) continue; if (prop.name == "_ScreenWidthUnits" && !(shaderSpace > 5.5f)) continue; if ((prop.name == "_RectHeight" || prop.name == "_RectWidth") && !(shaderSpace > 4.5f && shaderSpace < 5.5f)) continue; //Fading Skipped: if (prop.name == "_FadingFade" && shaderFading < 0.5f) continue; if (prop.name == "_FadingMask" && Mathf.RoundToInt(shaderFading) != 2) continue; if ((prop.name == "_FadingPosition" || prop.name == "_FadingNoiseFactor") && Mathf.RoundToInt(shaderFading) != 4) continue; if ((prop.name == "_FadingWidth" || prop.name == "_FadingNoiseScale") && Mathf.RoundToInt(shaderFading) != 4 && Mathf.RoundToInt(shaderFading) != 3) continue; //Basic: if (prop.name == "_MetallicMap" && materials[0].GetFloat("_MetallicMapToggle") < 0.5f) continue; bool isKeyword = toggleProperties.Contains(prop.name); //Enable: if (enableProperties.Contains(prop.name)) { isDisabled = DrawShaderToggle(materialEditor, prop); isHidden = false; continue; //Dont display shader toggle twice. } else { if (isDisabled) { continue; //Skip disabled properties. } } string displayName = prop.displayName; //Categories: if (prop.name == "_MainTex") { DrawShaderCategory("Main"); displayName = "Sprite Texture"; } else if (prop.name == "_ShaderSpace") { DrawShaderCategory("Space"); shaderSpace = prop.floatValue; switch (Mathf.RoundToInt(shaderSpace)) { case (0): spaceName = "UV"; break; case (1): spaceName = "UV Raw"; break; case (2): spaceName = "Object"; requiresInstancing = true; break; case (3): spaceName = "Scaled Object"; requiresInstancing = true; break; case (4): spaceName = "World"; break; case (5): spaceName = "UI Rect"; break; case (6): spaceName = "Screen"; break; } } else if (prop.name == "_ToggleUnscaledTime") { DrawShaderCategory("Time", ref showTime); } else if (prop.name == "_ShaderFading") { //Space Hints: switch (Mathf.RoundToInt(shaderSpace)) { case (0): DisplayHint("- Uses the UV position with size adjustment."); break; case (1): DisplayHint("- Uses the raw UV position."); break; case (2): DisplayHint("- Uses the local position of the object."); break; case (3): DisplayHint("- Uses the local position with scale adjustment."); break; case (4): DisplayHint("- Uses the world position (good for scene effects)."); break; case (5): DisplayHint("- Use this for UI images."); DisplayHint("- The ImageSSU component auto-updates rect size."); //Add component: if (gameObject != null && gameObject.GetComponent() != null && gameObject.GetComponent() != null && gameObject.GetComponent() == null) { EditorGUILayout.Space(); GUI.color = new Color(0.8f, 1f, 0.8f); if (GUILayout.Button("Add Helper Component")) { if (gameObject.GetComponent() != null) { MonoBehaviour.DestroyImmediate(gameObject.GetComponent()); } gameObject.AddComponent(); } GUI.color = Color.white; } break; case (6): DisplayHint("- Use this for non-moving UI Images or fullscreen effects.."); break; } DrawShaderCategory("Fading"); shaderFading = prop.floatValue; } else if (prop.name == "_UberNoiseTexture") { DrawShaderCategory("Noise", ref showNoise); } else if (prop.name == "_VertexTintFirst") { DrawShaderCategory("Settings"); } else if (prop.name == "_IsText") { DrawShaderCategory("GUI Settings"); } else if (prop.name == "_StencilComp") { DrawShaderCategory("UI Masking", ref showMasking); } if (isKeyword && !newCategory && prop.name != "PixelSnap" && prop.name != "_UseUIAlphaClip" && prop.name != "_MetallicMapToggle") { //Space: if (prop.name == "_TogglePixelArt" && shaderSpace > 1.1f) { continue; } //GUI: if (prop.name == "_IsTextMeshPro" && materials[0].GetFloat("_IsText") < 0.5f) { isHidden = true; continue; } //Time: if(showTime == false) { if(prop.name == "_ToggleCustomTime" || prop.name == "_ToggleTimeSpeed" || prop.name == "_ToggleTimeFPS" || prop.name == "_ToggleTimeFrequency") { continue; } } //Lines: Lines(); //Hiding: if (hidingProperties.Contains(prop.name)) { isHidden = prop.floatValue < 0.5f; } } else if (isHidden) { if (prop.name == "_EnchantedLowColor" || prop.name == "_EnchantedHighColor" || prop.name == "_ShiftingColorA" || prop.name == "_ShiftingColorB") { //No Skipping } else { continue; //Skip hidden properties. } } else { if (prop.name == "_EnchantedLowColor" || prop.name == "_EnchantedHighColor" || prop.name == "_ShiftingColorA" || prop.name == "_ShiftingColorB") { continue; //Skipping if not Hidden } } //Lines Above: if (linesAboveProperties.Contains(prop.name)) { Lines(); } //Name: string[] splitName = displayName.Split(':'); if (splitName.Length > 1) { displayName = splitName[1]; if (displayName[0] == ' ') { displayName = displayName.Substring(1); } } GUIContent displayContent = new GUIContent(displayName, isKeyword ? "" : prop.name + " (C#)"); //Display: EditorGUILayout.BeginHorizontal(); if (prop.type == MaterialProperty.PropType.Texture) { prop.textureValue = (Texture)EditorGUILayout.ObjectField(displayContent, prop.textureValue, typeof(Texture), false, GUILayout.Height(50)); } else if (prop.type == MaterialProperty.PropType.Vector) { if(prop.name == "_SpriteSheetRect") { prop.vectorValue = EditorGUI.Vector4Field(PropertyLabel(displayContent), GUIContent.none, prop.vectorValue); } else { prop.vectorValue = EditorGUI.Vector2Field(PropertyLabel(displayContent), GUIContent.none, prop.vectorValue); } } else if (prop.type == MaterialProperty.PropType.Range) { bool isSlider = true; if (prop.name == "_FadingFade" && Mathf.RoundToInt(shaderFading) == 4) { isSlider = false; } if (isSlider) { prop.floatValue = EditorGUI.Slider(GetPropertySize(prop, isKeyword), displayContent, prop.floatValue, prop.rangeLimits.x, prop.rangeLimits.y); } else { prop.floatValue = EditorGUI.FloatField(GetPropertySize(prop, isKeyword), displayContent, prop.floatValue); } } else if (prop.type == MaterialProperty.PropType.Color) { EditorGUI.BeginChangeCheck(); EditorGUI.showMixedValue = prop.hasMixedValue; bool showEyedropper = false; bool showAlpha = false; bool showContrast = false; if (prop.name.StartsWith("_Recolor")) { showAlpha = true; showContrast = true; } if (prop.name == "_ColorReplaceFromColor") { showEyedropper = true; } Color color = EditorGUILayout.ColorField(displayContent, prop.colorValue, showEyedropper, showAlpha, true); if (showContrast) { Rect lastRect = GUILayoutUtility.GetLastRect(); lastRect.y -= 1; GUI.color = new Color(1, 1, 1, 0.5f); GUI.Label(lastRect, "" + displayContent.text + " | " + Mathf.RoundToInt(prop.colorValue.a * 200) + "% Contrast", labelStyle); GUI.color = Color.white; } EditorGUI.showMixedValue = false; if (EditorGUI.EndChangeCheck()) { prop.colorValue = color; } } else { materialEditor.ShaderProperty(GetPropertySize(prop, isKeyword), prop, displayContent); } if (!isKeyword) { ResetButton(prop); HelpButton(prop, displayContent); } EditorGUILayout.EndHorizontal(); //Hints: if (prop.name == "_ToggleUnscaledTime" && prop.floatValue > 0.5f) { DisplayHint("- Requires one active UnscaledTimeSSU component in your scene."); } if (prop.name == "_TimeFPS") { DisplayHint("- Limits shader animations to " + Mathf.RoundToInt(prop.floatValue) + " frames per second."); } if (prop.name == "_TimeRange") { DisplayHint("- Time flows back and forth."); } if(prop.name == "_VertexTintFirst" && prop.floatValue > 0.5f) { DisplayHint("- Applies the vertex tint before the applying shaders."); } if (prop.name == "_TilingFix" && prop.floatValue > 0.5f) { DisplayHint("- Fixes issues with tiling shaders."); DisplayHint("- This is only required on sprite sheets."); } if (prop.name == "_PixelPerfectSpace" && prop.floatValue > 0.5f) { DisplayHint("- Snaps the shader space to the sprite's pixels."); DisplayHint("- Only works with UV shader space."); DisplayHint("- Does not work well with Smooth Pixel Art, use one or the other."); } if (prop.name == "_PixelPerfectUV" && prop.floatValue > 0.5f) { DisplayHint("- Snaps the UV manipulation to the sprite's pixels."); DisplayHint("- This works best with Point No Filter and Pixel Snap."); DisplayHint("- Does not work well with Smooth Pixel Art, use one or the other."); } if (prop.name == "_BakedMaterial" && prop.floatValue > 0.5f) { DisplayHint("- Enabled while baking shaders to textures."); DisplayHint("- You can ignore this."); } if (prop.name == "_ForceAlpha" && prop.floatValue > 0.5f) { DisplayHint("- Forces the alpha to always be 100%."); DisplayHint("- You can ignore this."); } if(prop.name == "_WindLocalWind" && prop.floatValue < 0.5f) { DisplayHint("- Uses and requires WindManagerSSU for global wind."); } if (prop.name == "_WindMaxIntensity") { DisplayHint("- Uses local wind settings."); } if (prop.name == "_WindHighQualityNoise" && prop.floatValue > 0.5f) { DisplayHint("- Generates less repetitive noise, but costs more performance."); } if (prop.name == "_SpriteSheetRect") { DisplayHint("- Requires the SpriteSheetSSU component."); DisplayHint("- Fixes sprite sheet issues for shaders like the Wind shader."); Sprite targetSprite = null; EditorGUILayout.BeginHorizontal(); GUI.color = new Color(1, 1, 1, 0.7f); EditorGUILayout.PrefixLabel("- Manual Rect Calculation"); targetSprite = (Sprite) EditorGUILayout.ObjectField(targetSprite, typeof(Sprite), true); if (targetSprite != null) { foreach(Material mat in materials) { mat.SetVector("_SpriteSheetRect", SpriteSheetSSU.GetSheetVector(targetSprite)); } } EditorGUILayout.EndHorizontal(); GUI.color = Color.white; //Apply to Sprite Renderer: if (Selection.activeGameObject != null) { SpriteRenderer spriteRenderer = Selection.activeGameObject.GetComponent(); if(spriteRenderer != null && spriteRenderer.sharedMaterial == materials[0]) { if(spriteRenderer.HasPropertyBlock() && spriteRenderer.sprite != null && spriteRenderer.sprite != lastSSF) { lastSSF = spriteRenderer.sprite; MaterialPropertyBlock mpb = new MaterialPropertyBlock(); spriteRenderer.GetPropertyBlock(mpb); mpb.SetVector("_SpriteSheetRect", SpriteSheetSSU.GetSheetVector(lastSSF)); spriteRenderer.SetPropertyBlock(mpb); } } } } if (prop.name == "_WiggleFixedGroundToggle" && prop.floatValue > 0.5f) { DisplayHint("- Prevents the sprite from sliding on the ground."); } if(prop.name == "_TMPSmoothness") { DisplayHint("- Many distortion shaders will not work."); DisplayHint("- Bold markups will not work."); } //Other: if (currentHint != null) { if(currentHint.hints != null) { for(int s = 0; s < currentHint.hints.Count; s++) { HintText suffix = currentHint.hints[s]; if(suffix.property == prop.name) { DisplayHint(suffix.text); break; } } } if(currentHint.spaceHint != null && currentHint.spaceHint == prop.name) { DisplaySpaceHint(); } if (currentHint.lines != null) { for (int l = 0; l < currentHint.lines.Count; l++) { string line = currentHint.lines[l]; if (line == prop.name) { Lines(); } } } } } EditorGUILayout.EndVertical(); //Final Category Close: ShaderTitle("Close"); //Additional: AdditionalMaterialProperties(materialEditor); } void Initialize(MaterialEditor materialEditor, MaterialProperty[] properties) { gameObject = Selection.activeGameObject; if(allHints == null) { allHints = new Dictionary(); } if(enabledShaders == null) { enabledShaders = new HashSet(); } if(shaderList == null || shaderList.Count == 0) { shaderList = new List(); shaderList.Add("Sprite Shaders Ultimate/Standard SSU"); shaderList.Add("Sprite Shaders Ultimate/Additive SSU"); shaderList.Add("Sprite Shaders Ultimate/GUI SSU"); shaderList.Add("Sprite Shaders Ultimate/Additive GUI SSU"); shaderList.Add("Sprite Shaders Ultimate/2D Lit URP SSU"); shaderList.Add("Sprite Shaders Ultimate/3D Lit URP SSU"); shaderList.Add("Sprite Shaders Ultimate/3D Lit BuiltIn SSU"); shaderList.Add("Sprite Shaders Ultimate/Multiplicative SSU"); shaderList.Add("Sprite Shaders Ultimate/3D Lit Cutout URP SSU"); shaderList.Add("Sprite Shaders Ultimate/3D Lit Cutout BuiltIn SSU"); shaderDictionary = new Dictionary(); for(int n = 0; n < shaderList.Count; n++) { shaderDictionary.Add(shaderList[n], n); } } labelStyle = new GUIStyle(GUI.skin.label); labelStyle.richText = true; labelCenterStyle = new GUIStyle(labelStyle); labelCenterStyle.alignment = TextAnchor.MiddleCenter; buttonStyle = new GUIStyle(GUI.skin.button); buttonStyle.richText = true; buttonStyle.alignment = TextAnchor.MiddleCenter; shader = ((Material) materialEditor.target).shader; if (defaultMaterial == null || defaultMaterial.shader != shader) { defaultMaterial = new Material(shader); } if (lastShader != shader) { lastShader = shader; toggleProperties = new HashSet(); foreach (MaterialProperty prop in properties) { if (IsKeyword(prop)) { toggleProperties.Add(prop.name); } } hidingProperties = new HashSet(); foreach (MaterialProperty prop in properties) { if (prop.name.StartsWith("_Toggle") || prop.name.EndsWith("Toggle") || prop.name == "_IsTextMeshPro" || prop.name == "_WindLocalWind" || prop.name == "_WindIsParallax" || prop.name == "_SpriteSheetFix") { hidingProperties.Add(prop.name); } } enableProperties = new HashSet(); foreach (MaterialProperty prop in properties) { if (prop.name.StartsWith("_Enable")) { enableProperties.Add(prop.name); } } alwaysHiddenProperties = new HashSet(); alwaysHiddenProperties.Add("_AlphaTex"); alwaysHiddenProperties.Add("_texcoord"); alwaysHiddenProperties.Add("_EmissionColor"); alwaysHiddenProperties.Add("_AlphaCutoff"); linesAboveProperties = new HashSet(); linesAboveProperties.Add("_MaskMap"); linesAboveProperties.Add("_Smoothness"); linesAboveProperties.Add("_NormalMap"); linesAboveProperties.Add("_ShadowClip"); linesAboveProperties.Add("_AlphaClip"); } materials = new Material[materialEditor.targets.Length]; for(int m = 0; m < materials.Length; m++) { materials[m] = (Material) materialEditor.targets[m]; } } void DrawStatus(MaterialEditor materialEditor) { //Top: GUI.color = Color.white; EditorGUILayout.BeginVertical("Helpbox"); //Shaders Toolbar: int selected = -1; if(shaderDictionary.ContainsKey(shader.name)) { selected = shaderDictionary[shader.name]; } int newSelection = GUILayout.Toolbar(selected, new string[] { "Standard", "Additive", "GUI", "Additive GUI" }); int secondSelection = GUILayout.Toolbar(selected - 4, new string[] { "2D Lit URP", "3D Lit URP", "3D Lit Built-In", "Multiplicative" }); int thirdSelection = GUILayout.Toolbar(selected - 8, new string[] { "3D Lit Cutout URP", "3D Lit Cutout Built-In"}); if (secondSelection > -1 && secondSelection != selected - 4) { newSelection = secondSelection + 4; } if (thirdSelection > -1 && thirdSelection != selected - 8) { newSelection = thirdSelection + 8; } if (newSelection != selected) { Undo.RecordObjects(materials, "Changed SSU Shader"); skipGUI = true; foreach(Material mat in materials) { mat.shader = Shader.Find(shaderList[newSelection]); } } EditorGUILayout.Space(); //Status: GUI.color = new Color(0.7f, 1f, 0.7f, EditorGUIUtility.isProSkin ? 1f : 0.7f); EditorGUILayout.BeginVertical(GUI.skin.box); GUI.color = hintColor; EditorGUILayout.LabelField("- Enabled " + enabledAmount + " out of " + shaderAmount + " shaders.", labelStyle); //Fading: switch(Mathf.RoundToInt(shaderFading)) { case (0): //No Cost break; case (1): performanceBenchmark += 400; break; case (2): performanceBenchmark += 600; textureAmount++; break; case (3): performanceBenchmark += 950; textureAmount++; break; case (4): performanceBenchmark += 1150; textureAmount++; break; } //Performance: /*if(performanceBenchmark <= 1) { EditorGUILayout.LabelField("- GPU Performance: " + "" + Mathf.RoundToInt(performanceBenchmark * 0.1f) + " (same as unity's built-in shader)", labelStyle); } else { EditorGUILayout.LabelField("- GPU Performance: " + "" + Mathf.RoundToInt(performanceBenchmark * 0.1f) + "", labelStyle); } if (textureAmount == 1) { EditorGUILayout.LabelField("- Texture Samples: " + textureAmount + " (the sprite texture)", labelStyle); } else { EditorGUILayout.LabelField("- Texture Samples: " + textureAmount + "", labelStyle); }*/ EditorGUILayout.EndVertical(); wasWarned = false; //Color Space: if (PlayerSettings.colorSpace != ColorSpace.Linear) { StartWarning(true); EditorGUILayout.LabelField("- The default shader variables are adjusted for Linear color space.", labelStyle); EditorGUILayout.LabelField("- Switch to Linear for higher quality and better default settings.", labelStyle); if (GUILayout.Button("Switch to Linear (recommended)", buttonStyle)) { Undo.RecordObjects(materials, "Enabled Linear Color Space"); PlayerSettings.colorSpace = ColorSpace.Linear; } EditorGUILayout.EndVertical(); } //Conflict Warnings: if (enabledShaders.Contains("World Tiling") && enabledShaders.Contains("Screen Tiling")) { StartWarning(); EditorGUILayout.LabelField("- World and Screen Tiling aren't compatible.", labelStyle); EditorGUILayout.EndVertical(); } if (enabledShaders.Contains("Smooth Pixel Art") && enabledShaders.Contains("Gaussian Blur")) { StartWarning(); EditorGUILayout.LabelField("- Smooth Pixel Art and Gaussian Blur aren't compatible.", labelStyle); EditorGUILayout.EndVertical(); } //Texture Warnings: Texture activeTexture = GetTexture(materialEditor); Sprite activeSprite = GetSprite(); TextureImporter textureImporter = GetTextureImporter(activeTexture); if (textureImporter != null) { TextureImporterSettings textureSettings = new TextureImporterSettings(); textureImporter.ReadTextureSettings(textureSettings); if (textureImporter != null) { //Tiling and Sheet Conflict: if(requiresTiling && textureImporter.spriteImportMode == SpriteImportMode.Multiple && materials[0].GetFloat("_TilingFix") < 0.5f) { StartWarning(); EditorGUILayout.LabelField("- Tiling shaders cause problems with sprite sheets.", labelStyle); EditorGUILayout.LabelField("- Please enable Tiling Fix to fix these problems.", labelStyle); EditorGUILayout.LabelField("- Also make sure Sprite Sheet Fix is enabled.", labelStyle); if (GUILayout.Button("Enable Tiling Fix", buttonStyle)) { Undo.RecordObjects(materials, "Enabled Tiling Fix Option"); foreach (Material mat in materials) { mat.EnableKeyword("_TILINGFIX_ON"); mat.SetFloat("_TilingFix", 1f); } } EditorGUILayout.EndVertical(); } //Full Rect Mesh: if (requiresFullRectMesh && textureSettings.spriteMeshType != SpriteMeshType.FullRect) { StartWarning(); EditorGUILayout.LabelField("- Set Mesh Type to Full Rect to avoid pixel clipping.", labelStyle); if (GUILayout.Button("Set Mesh Type to Full Rect", buttonStyle)) { textureSettings.spriteMeshType = SpriteMeshType.FullRect; Undo.RecordObject(textureImporter, "Changed Mesh Type"); textureImporter.SetTextureSettings(textureSettings); textureImporter.SaveAndReimport(); } EditorGUILayout.EndVertical(); } //Tiling Fix: if (requiresTiling && textureSettings.wrapMode == TextureWrapMode.Clamp) { StartWarning(); EditorGUILayout.LabelField("- Some shaders require Wrap Mode set to Repeat.", labelStyle); EditorGUILayout.LabelField("- Tiling won't work properly without this setting.", labelStyle); if (GUILayout.Button("Set Wrap Mode to Repeat", buttonStyle)) { textureSettings.wrapMode = TextureWrapMode.Repeat; Undo.RecordObject(textureImporter, "Changed Wrap Mode"); textureImporter.SetTextureSettings(textureSettings); textureImporter.SaveAndReimport(); } EditorGUILayout.EndVertical(); } //Sprite Sheet Fix: if (enabledShaders.Contains("Wiggle") && materials[0].GetFloat("_WiggleFixedGroundToggle") > 0.5f) { requiresSpriteSheetFix = true; } if((requiresSpriteSheetFix || requiresTiling) && textureImporter.spriteImportMode == SpriteImportMode.Multiple && materials[0].GetFloat("_SpriteSheetFix") < 0.5f && activeSprite != null) { StartWarning(); EditorGUILayout.LabelField("- Some shaders require the Sprite Sheet Fix option.", labelStyle); if (GUILayout.Button("Enable Sprite Sheet Fix", buttonStyle)) { Undo.RecordObjects(materials, "Enabled Sprite Sheet Fix Option"); foreach (Material mat in materials) { mat.EnableKeyword("_SPRITESHEETFIX_ON"); mat.SetFloat("_SpriteSheetFix", 1f); mat.SetVector("_SpriteSheetRect", SpriteSheetSSU.GetSheetVector(activeSprite)); } } EditorGUILayout.EndVertical(); } //Sprite Sheet Component: if (materials[0].GetFloat("_SpriteSheetFix") > 0.5f && Selection.activeGameObject != null && Selection.activeGameObject.GetComponent() == false) { StartWarning(); EditorGUILayout.LabelField("- You might need the SpriteSheetSSU component.", labelStyle); EditorGUILayout.LabelField("- It updates the Rect variable depending on the Sprite.", labelStyle); if (GUILayout.Button("Add SpriteSheetSSU", buttonStyle)) { Undo.AddComponent(Selection.activeGameObject, typeof(SpriteSheetSSU)); } Material mat = materials[0]; if(activeSprite != null) { mat.SetVector("_SpriteSheetRect", SpriteSheetSSU.GetSheetVector(activeSprite)); } EditorGUILayout.EndVertical(); } //Instance Required: if (requiresInstancing && Selection.activeGameObject != null && Selection.activeGameObject.GetComponent() == null) { StartWarning(); if (Mathf.RoundToInt(shaderSpace) == 2 || Mathf.RoundToInt(shaderSpace) == 3) { EditorGUILayout.LabelField("- Object space requires unique material instances.", labelStyle); } else { EditorGUILayout.LabelField("- Some shaders require unique material instances.", labelStyle); } EditorGUILayout.LabelField("- Instantiate the material at runtime to fix this.", labelStyle); if (Selection.activeGameObject.GetComponent() != null) { EditorGUILayout.LabelField("- Modifying the material at runtime will instantiate it.", labelStyle); } bool multiple = false; if (enabledShaders.Contains("Wind")) { multiple = true; if (GUILayout.Button("Add InteractiveWindSSU", buttonStyle)) { Undo.AddComponent(Selection.activeGameObject, typeof(InteractiveWindSSU)); } } if (GUILayout.Button("Add MaterialInstancerSSU", buttonStyle)) { Undo.AddComponent(Selection.activeGameObject, typeof(MaterialInstancerSSU)); } if (multiple) { EditorGUILayout.LabelField("- You only need one of these components.", labelStyle); } EditorGUILayout.EndVertical(); } //Bilinear: if (enabledShaders.Contains("Smooth Pixel Art") && textureSettings.filterMode == FilterMode.Point) { StartWarning(); EditorGUILayout.LabelField("- Smooth Pixel Art requires Bilinear filtering.", labelStyle); if (GUILayout.Button("Set Filter Mode to Bilinear", buttonStyle)) { textureSettings.filterMode = FilterMode.Bilinear; Undo.RecordObject(textureImporter, "Changed Filter Mode"); textureImporter.SetTextureSettings(textureSettings); textureImporter.SaveAndReimport(); } EditorGUILayout.EndVertical(); } if(enabledShaders.Contains("Pixelate")) { if (textureSettings.mipmapEnabled) { StartWarning(); EditorGUILayout.LabelField("- Pixelate does not work with mip maps.", labelStyle); if (GUILayout.Button("Disable Mip Maps", buttonStyle)) { textureSettings.mipmapEnabled = false; Undo.RecordObject(textureImporter, "Disabled Mip Maps"); textureImporter.SetTextureSettings(textureSettings); textureImporter.SaveAndReimport(); } EditorGUILayout.EndVertical(); } } } } //WindParallaxSSU Required: if(enabledShaders.Contains("Wind") && materials[0].GetFloat("_WindIsParallax") > 0.5f && Selection.activeGameObject.GetComponent() == null) { StartWarning(); EditorGUILayout.LabelField("- Is Parallax requires the WindParallaxSSU component.", labelStyle); EditorGUILayout.LabelField("- This will fix any issues with moving the sprite.", labelStyle); if (GUILayout.Button("Add WindParallaxSSU", buttonStyle)) { Undo.AddComponent(Selection.activeGameObject, typeof(WindParallaxSSU)); } EditorGUILayout.EndVertical(); } if(!wasWarned) { GUI.color = new Color(0.7f, 1, 1f, EditorGUIUtility.isProSkin ? 1f : 0.7f); EditorGUILayout.BeginVertical(GUI.skin.box); GUI.color = hintColor; EditorGUILayout.LabelField("- No issues were detected.", labelStyle); EditorGUILayout.LabelField("- Detected issues will be displayed here.", labelStyle); EditorGUILayout.EndVertical(); } //Reset Requirements: requiresSpriteSheetFix = requiresFullRectMesh = requiresInstancing = requiresTiling = false; //Support Box: GUI.color = new Color(0.7f, 0.77f, 1f, EditorGUIUtility.isProSkin ? 1f : 0.7f); EditorGUILayout.BeginVertical(GUI.skin.box); GUI.color = Color.white; DisplaySupportInformation(); EditorGUILayout.EndVertical(); //Export Material Feature: GUI.color = new Color(0.9f, 0.7f, 1f, EditorGUIUtility.isProSkin ? 1f : 0.7f); EditorGUILayout.BeginVertical(GUI.skin.box); GUI.color = new Color(0.95f, 0.85f, 1, 1f); if (GUILayout.Button("Bake Shader to Texture and Export", buttonStyle)) { ExportSprite(materialEditor, bakeStackAmount); } bakeStackAmount = Mathf.Clamp(EditorGUILayout.IntField("Stack Amount", bakeStackAmount), 1, 20); GUI.color = hintColor; EditorGUILayout.LabelField("- Use a stack amount of 10 for smooth gaussian blur.", labelStyle); GUI.color = Color.white; EditorGUILayout.EndVertical(); EditorGUILayout.EndVertical(); } void StartWarning(bool isLight = false) { wasWarned = true; GUI.color = isLight ? lightWarnColor : warnColor; EditorGUILayout.BeginVertical(GUI.skin.box); } void AdditionalMaterialProperties(MaterialEditor materialEditor) { if (UnityEngine.Rendering.SupportedRenderingFeatures.active.editableMaterialRenderQueue) { EditorGUILayout.Space(); EditorGUILayout.Space(); materialEditor.RenderQueueField(); } } void DrawShaderCategory(string title) { newCategory = true; isHidden = false; EditorGUILayout.EndVertical(); EditorGUILayout.Space(); EditorGUILayout.BeginVertical("Helpbox"); GUI.color = new Color(1, 1, 1, 0.9f); GUILayout.Label("" + title + "", labelStyle); Lines(); } void DrawShaderCategory(string title, ref bool toggleVariable) { newCategory = true; isHidden = false; EditorGUILayout.EndVertical(); EditorGUILayout.Space(); EditorGUILayout.BeginVertical("Helpbox"); GUI.color = new Color(1, 1, 1, 0.9f); GUILayout.Label("" + title + "", labelStyle); //Toggle: Rect buttonRect = GUILayoutUtility.GetLastRect(); buttonRect.x += buttonRect.width - 20; buttonRect.width = 20; if(GUI.Button(buttonRect, !toggleVariable ? "▶" : "▼", buttonStyle)) { toggleVariable = !toggleVariable; } if(toggleVariable) { Lines(); } else { isHidden = true; } } void DisplayHint(string text) { GUI.color = hintColor; EditorGUILayout.LabelField(text, labelStyle); GUI.color = Color.white; } void DisplaySpaceHint() { GUI.color = hintColor; EditorGUILayout.LabelField("- Uses " + spaceName + " Space as configured above.", labelStyle); GUI.color = Color.white; } bool DrawShaderToggle(MaterialEditor materialEditor, MaterialProperty prop) { newCategory = true; isHidden = false; EditorGUILayout.EndVertical(); switch (prop.name) { case ("_EnableStrongTint"): ShaderTitle("Color"); break; case ("_EnableHologram"): ShaderTitle("Effect"); break; case ("_EnableFullAlphaDissolve"): ShaderTitle("Fading"); break; case ("_EnablePixelate"): ShaderTitle("Transformation"); break; case ("_EnableWind"): ShaderTitle("Interactive"); break; case ("_EnableCheckerboard"): ShaderTitle("Other"); break; } shaderAmount++; bool isEnabled = prop.floatValue > 0.5f; bool isCollapsed = prop.floatValue == 1.1f; //Box: Color color = Color.HSVToRGB((currentBoxColor * 0.05f) % 1, isEnabled ? 0.7f : 0.2f + (EditorGUIUtility.isProSkin ? 0.4f : 0), EditorGUIUtility.isProSkin ? (isEnabled ? 1.8f : 1f) : 1); color.a = isEnabled ? 1f : 0.7f; GUI.color = color; currentBoxColor++; EditorGUILayout.BeginVertical("Helpbox"); string shaderName = prop.displayName.Replace("Enable ", ""); EditorGUILayout.BeginHorizontal(); GUI.color = new Color(1, 1, 1, 0.5f); GUILayout.Label(shaderAmount + ".", GUILayout.Width(24f)); GUI.color = new Color(1, 1, 1, isEnabled ? 0.9f : 0.7f); GUILayout.Label("" + shaderName + "", labelStyle); //Track: if (isEnabled) { enabledShaders.Add(shaderName); } Rect toggleRect = GUILayoutUtility.GetLastRect(); //Enabled: currentHint = null; if (isEnabled) { enabledAmount++; string resourcePath = "SSU/Hints/" + shaderName; if (allHints.ContainsKey(resourcePath)) { currentHint = allHints[resourcePath]; } else { currentHint = Resources.Load(resourcePath); if (currentHint != null) { allHints.Add(resourcePath, currentHint); } } DrawShadow("" + shaderName + ""); } toggleRect.x += toggleRect.width -= 37; toggleRect.width = 37; materialEditor.ShaderProperty(toggleRect, prop, GUIContent.none); if (isEnabled) { Rect lastRect = GUILayoutUtility.GetLastRect(); lastRect.x += lastRect.width - 20; lastRect.width = 20; if (GUI.Button(lastRect, isCollapsed ? "▶" : "▼", buttonStyle)) { isCollapsed = !isCollapsed; if (isCollapsed) { prop.floatValue = 1.1f; } else { prop.floatValue = 1f; } } if (currentHint != null) { if (currentHint.requiresFullRectMesh) { requiresFullRectMesh = true; } if (currentHint.requiresSpriteSheetFix) { requiresSpriteSheetFix = true; } if (currentHint.requiresTiling) { requiresTiling = true; } if (currentHint.requiresInstancing) { requiresInstancing = true; } if (currentHint.benchmarkValue > 0) { performanceBenchmark += currentHint.benchmarkValue; } if (currentHint.textureSamples > 0) { textureAmount += currentHint.textureSamples; } if (currentHint.textureToggle != "" && currentHint.textureToggle != null && materials[0].GetFloat(currentHint.textureToggle) > 0.5f) { textureAmount++; } if (currentHint.textureToggleExtra != "" && currentHint.textureToggleExtra != null && materials[0].GetFloat(currentHint.textureToggleExtra) > 0.5f) { textureAmount++; } } } GUI.color = Color.white; EditorGUILayout.EndHorizontal(); if (isEnabled && !isCollapsed) { if (currentHint != null && currentHint.shaderDescription != null && currentHint.shaderDescription != "") { string[] descriptionLines = currentHint.shaderDescription.Split('\n'); for (int d = 0; d < descriptionLines.Length; d++) { DisplayHint(descriptionLines[d]); } string performanceText = "Relatively Lower"; if (currentHint.benchmarkValue > 600) { performanceText = currentHint.benchmarkValue < 1700 ? "Average" : "Relatively Higher"; } DisplayHint("- GPU Usage: " + performanceText + ""); } if (prop.name == "_EnableSmoothPixelArt") { Texture activeTexture = GetTexture(materialEditor); TextureImporter textureImporter = GetTextureImporter(activeTexture); if (textureImporter != null) { if (textureImporter.filterMode == FilterMode.Point) { GUI.color = warnColor; if (GUILayout.Button("Set " + activeTexture.name + "'s filter mode to Bilinear", buttonStyle)) { TextureImporterSettings textureSettings = new TextureImporterSettings(); textureImporter.ReadTextureSettings(textureSettings); textureSettings.filterMode = FilterMode.Bilinear; Undo.RecordObject(textureImporter, "Changed Filter Mode"); textureImporter.SetTextureSettings(textureSettings); textureImporter.SaveAndReimport(); } GUI.color = Color.white; } } } else { Lines(); /*if (prop.name == "_EnableRecolorPalette") { EditorGUILayout.BeginHorizontal(); if (GUILayout.Button("Generate Source")) { Texture activeTexture = GetTexture(materialEditor); if (activeTexture != null) { //Blit to Render Texture: RenderTexture rt = new RenderTexture(activeTexture.width, activeTexture.height, 0, RenderTextureFormat.ARGB32); Graphics.Blit(activeTexture, rt); //Read RenderTexture to Texture: Texture2D tex = new Texture2D(rt.width, rt.height, TextureFormat.ARGB32, false); tex.ReadPixels(new Rect(0, 0, rt.width, rt.height), 0, 0); tex.Apply(); //Get Colors: Dictionary colorWeights = new Dictionary(); for (int x = 0; x < tex.width; x++) { for (int y = 0; y < tex.height; y++) { Color pixel = paletteBilinear ? tex.GetPixelBilinear((float)x / (float)tex.width, (float)y / (float)tex.height) : tex.GetPixel(x, y); Vector3 colorKey = new Vector3(pixel.r, pixel.g, pixel.b); if (colorWeights.ContainsKey(colorKey)) { colorWeights[colorKey] += pixel.a; } else { colorWeights.Add(colorKey, pixel.a); } } EditorUtility.DisplayProgressBar("Generating Palette Texture", "Loading Pixels", 0f); } //Get Top Colors: List paletteColors = new List(); Dictionary colorDictionary = new Dictionary(); ; int totalColors = paletteSize * paletteSize; if(colorWeights.Count > totalColors) { while (paletteColors.Count < totalColors && colorWeights.Count > 0) { float highestValue = 0; Vector3 highestColor = Vector3.zero; foreach (KeyValuePair colorWeight in colorWeights) { if (highestValue < colorWeight.Value) { highestValue = colorWeight.Value; highestColor = colorWeight.Key; } } paletteColors.Add(highestColor); colorDictionary.Add(highestColor, paletteColors.Count - 1); colorWeights.Remove(highestColor); EditorUtility.DisplayProgressBar("Generating Palette Texture", "Sorting colors.", 0.2f); } } else { foreach (KeyValuePair colorWeight in colorWeights) { paletteColors.Add(colorWeight.Key); colorDictionary.Add(colorWeight.Key, paletteColors.Count - 1); } } /*Sort Top Colors: List sortedColors = new List(); sortedColors.Add(topColors[0]); topColors.RemoveAt(0); while (topColors.Count > 0) { float closestDistance = 100f; int closestIndex = -1; for (int c = 0; c < topColors.Count; c++) { float distance = (sortedColors[sortedColors.Count - 1] - topColors[c]).sqrMagnitude; if (distance < closestDistance) { closestDistance = distance; closestIndex = c; } } sortedColors.Add(topColors[closestIndex]); topColors.RemoveAt(closestIndex); EditorUtility.DisplayProgressBar("Generating Palette Texture", "Sorting color palette.", 0.5f); } //Creating Texture: for (int x = 0; x < tex.width; x++) { for (int y = 0; y < tex.height; y++) { Color pixel = paletteBilinear ? tex.GetPixelBilinear((float)x / (float)tex.width, (float)y / (float)tex.height) : tex.GetPixel(x, y); Vector3 colorKey = new Vector3(pixel.r, pixel.g, pixel.b); int colorIndex = 0; if (colorDictionary.ContainsKey(colorKey)) { colorIndex = colorDictionary[colorKey]; } else { float closestDistance = 100f; for (int c = 0; c < paletteColors.Count; c++) { float distance = (paletteColors[c] - colorKey).sqrMagnitude; if (distance < closestDistance) { closestDistance = distance; colorIndex = c; } } } float rValue = (float) (colorIndex % paletteSize) / paletteSize; float gValue = Mathf.Floor(colorIndex / paletteSize) / paletteSize; tex.SetPixel(x, y, new Color(rValue, gValue, 0, 1)); } EditorUtility.DisplayProgressBar("Generating Palette Texture", "Mapping colors.", 0.8f); } //Export: EditorUtility.ClearProgressBar(); string oldPath = AssetDatabase.GetAssetPath(activeTexture); if (oldPath == "") { oldPath = Application.dataPath + "/"; } string[] fileEnding = oldPath.Split('.'); string newPath = oldPath.Replace("." + fileEnding[fileEnding.Length - 1], ""); string folderPath = ""; string[] folders = oldPath.Split('/'); for (int n = 0; n < folders.Length - 1; n++) { folderPath += folders[n] + "/"; } string fileName = activeTexture.name; newPath = EditorUtility.SaveFilePanel("Save Texture", folderPath, activeTexture.name + " (Source)", "png"); if (newPath != "") { newPath = newPath.Substring(Application.dataPath.Length - 9); while (newPath.Length > 1 && newPath.StartsWith("Assets") == false) { newPath = newPath.Substring(1); } //Save Texture: byte[] bytes = tex.EncodeToPNG(); bool newFile = AssetDatabase.LoadAssetAtPath(newPath, typeof(Texture)) == null; if (newFile) { AssetDatabase.CopyAsset(oldPath, newPath); } System.IO.File.WriteAllBytes(newPath, bytes); if (newFile) { AssetDatabase.ImportAsset(newPath); } //Import new Texture: AssetDatabase.Refresh(); //Ping: EditorGUIUtility.PingObject(AssetDatabase.LoadAssetAtPath(newPath, typeof(Texture))); } //Clear: if (RenderTexture.active == rt) { RenderTexture.active = null; } Object.DestroyImmediate(rt); Object.DestroyImmediate(tex); } else { EditorUtility.DisplayDialog("No texture found.", "Select a SpriteRenderer or Image with this material.\nOr assign a texture to this material.", "Alright"); } } if (GUILayout.Button("Generate Palette")) { Texture activeTexture = GetTexture(materialEditor); if (activeTexture != null) { //Open Source: string currentFile = AssetDatabase.GetAssetPath(activeTexture); string[] splitPath = currentFile.Split('/'); string openPath = EditorUtility.OpenFilePanel("Open Source Texture", currentFile.Substring(0, currentFile.Length - splitPath[splitPath.Length - 1].Length), "png"); string[] openPathSplit = openPath.Split('/'); openPath = ""; bool ignore = true; for(int i = 0; i < openPathSplit.Length; i++) { if(openPathSplit[i] == "Assets") { ignore = false; } if(!ignore) { openPath += openPathSplit[i] + (i == openPathSplit.Length - 1 ? "" : "/"); } } Texture sourceTexture = AssetDatabase.LoadAssetAtPath(openPath); //Source Texture: if (sourceTexture != null) { RenderTexture sourceRT = new RenderTexture(sourceTexture.width, sourceTexture.height, 0, RenderTextureFormat.ARGB32); Graphics.Blit(sourceTexture, sourceRT); Texture2D sourceTex = new Texture2D(sourceRT.width, sourceRT.height, TextureFormat.ARGB32, false); sourceTex.ReadPixels(new Rect(0, 0, sourceRT.width, sourceRT.height), 0, 0); sourceTex.Apply(); //Recolor Texture RenderTexture rt = new RenderTexture(activeTexture.width, activeTexture.height, 0, RenderTextureFormat.ARGB32); Graphics.Blit(activeTexture, rt); Texture2D tex = new Texture2D(rt.width, rt.height, TextureFormat.ARGB32, false); tex.ReadPixels(new Rect(0, 0, rt.width, rt.height), 0, 0); tex.Apply(); //Creating Palette: Texture2D paletteTex = new Texture2D(paletteSize, paletteSize); for (int x = 0; x < tex.width; x++) { for (int y = 0; y < tex.height; y++) { Color pixel = paletteBilinear ? tex.GetPixelBilinear((float)x / (float)tex.width, (float)y / (float)tex.height) : tex.GetPixel(x, y); Color pixelSource = paletteBilinear ? sourceTex.GetPixelBilinear((float)x / (float)tex.width, (float)y / (float)tex.height) : tex.GetPixel(x, y); paletteTex.SetPixel(Mathf.RoundToInt(pixelSource.r * paletteSize), Mathf.RoundToInt(pixelSource.g * paletteSize), pixel); } EditorUtility.DisplayProgressBar("Generating Palette Texture", "Saving Pixels", 0f); } paletteTex.Apply(); //Export: EditorUtility.ClearProgressBar(); string oldPath = AssetDatabase.GetAssetPath(activeTexture); if (oldPath == "") { oldPath = Application.dataPath + "/"; } string[] fileEnding = oldPath.Split('.'); string newPath = oldPath.Replace("." + fileEnding[fileEnding.Length - 1], ""); string folderPath = ""; string[] folders = oldPath.Split('/'); for (int n = 0; n < folders.Length - 1; n++) { folderPath += folders[n] + "/"; } string fileName = activeTexture.name; newPath = EditorUtility.SaveFilePanel("Save Texture", folderPath, activeTexture.name + " (Source)", "png"); if (newPath != "") { newPath = newPath.Substring(Application.dataPath.Length - 9); while (newPath.Length > 1 && newPath.StartsWith("Assets") == false) { newPath = newPath.Substring(1); } //Save Texture: byte[] bytes = paletteTex.EncodeToPNG(); bool newFile = AssetDatabase.LoadAssetAtPath(newPath, typeof(Texture)) == null; if (newFile) { AssetDatabase.CopyAsset(oldPath, newPath); } System.IO.File.WriteAllBytes(newPath, bytes); if (newFile) { AssetDatabase.ImportAsset(newPath); } //Import new Texture: AssetDatabase.Refresh(); //Ping: EditorGUIUtility.PingObject(AssetDatabase.LoadAssetAtPath(newPath, typeof(Texture))); } //Clear: if (RenderTexture.active == rt || RenderTexture.active == sourceRT) { RenderTexture.active = null; } Object.DestroyImmediate(rt); Object.DestroyImmediate(tex); Object.DestroyImmediate(sourceRT); Object.DestroyImmediate(sourceTex); Object.DestroyImmediate(paletteTex); } } else { EditorUtility.DisplayDialog("No texture found.", "Select a SpriteRenderer or Image with this material.\nOr assign a texture to this material.", "Alright"); } } EditorGUILayout.EndHorizontal(); paletteSize = EditorGUILayout.IntField("Palette Colors", paletteSize); paletteBilinear = EditorGUILayout.Toggle("Bilinear Filter", paletteBilinear); GUI.color = hintColor; EditorGUILayout.LabelField("- Use these to easily generate palette textures.", labelStyle); Lines(); }*/ } } return !isEnabled || isCollapsed; } static Texture GetTexture(MaterialEditor materialEditor) { Texture activeTexture = null; if (Selection.activeGameObject != null) { SpriteRenderer spriteRenderer = Selection.activeGameObject.GetComponent(); if (spriteRenderer != null) { if(spriteRenderer.sprite != null) { activeTexture = spriteRenderer.sprite.texture; } } else { Image image = Selection.activeGameObject.GetComponent(); if (image != null && image.sprite != null) { activeTexture = image.sprite.texture; } } } else { foreach(Material mat in materials) { if (mat != null) { activeTexture = mat.GetTexture("_MainTex"); if(activeTexture != null) { break; } } } } return activeTexture; } public static TextureImporter GetTextureImporter(Texture activeTexture) { try { if (activeTexture != null) { string path = AssetDatabase.GetAssetPath(activeTexture); if (path != null) { TextureImporter textureImporter = (TextureImporter)AssetImporter.GetAtPath(path); return textureImporter; } } } catch { return null; } return null; } Sprite GetSprite() { if (Selection.activeGameObject != null) { SpriteRenderer spriteRenderer = Selection.activeGameObject.GetComponent(); if (spriteRenderer != null) { return spriteRenderer.sprite; } else { Image image = Selection.activeGameObject.GetComponent(); if (image != null) { return image.sprite; } } } return null; } void DrawShadow(string textContent) { for (int x = -1; x <= 1; x += 2) { for (int y = -1; y <= 1; y += 2) { Rect lastRect = GUILayoutUtility.GetLastRect(); lastRect.x += x; lastRect.y += y; GUI.color = new Color(1, 1, 1, 0.12f); EditorGUI.LabelField(lastRect, new GUIContent(textContent), labelStyle); } } GUI.color = Color.white; } void ShaderTitle(string title) { if(title != "Close") { if(title != "Color") { EditorGUILayout.Space(); EditorGUILayout.Space(); } EditorGUILayout.Space(); EditorGUILayout.Space(); GUI.color = new Color(1, 1, 1, 0.55f); EditorGUILayout.LabelField("" + title + "", labelStyle, GUILayout.Height(22)); GUI.color = new Color(1, 1, 1, 0.1f); for (int x = -1; x <= 1; x += 2) { for (int y = -1; y <= 1; y += 2) { Rect lastRect = GUILayoutUtility.GetLastRect(); lastRect.y += x; lastRect.x += y; EditorGUI.LabelField(lastRect, "" + title + "", labelStyle); } } GUI.color = Color.white; } float currentHeight = (title != "Close") ? GUILayoutUtility.GetLastRect().y : GUILayoutUtility.GetLastRect().y + 59; if (title == "Color") { shaderAmount = enabledAmount = currentBoxColor = 0; performanceBenchmark = 0f; textureAmount = 1; } else { Rect lineRect = GUILayoutUtility.GetLastRect(); lineRect.x -= 8; lineRect.width = 2; lineRect.y = lastTitleHeight + 4; lineRect.height = currentHeight - lastTitleHeight - 32; if(EditorGUIUtility.isProSkin) { GUI.color = new Color(1, 1, 1, 0.5f); lineRect.x -= 3; GUI.Box(lineRect, GUIContent.none, GUI.skin.verticalSlider); lineRect.x -= 1; GUI.Box(lineRect, GUIContent.none, GUI.skin.verticalSlider); } else { GUI.color = new Color(0, 0, 0, 0.3f); GUI.Box(lineRect, GUIContent.none); lineRect.x += 1; GUI.Box(lineRect, GUIContent.none); GUI.color = Color.white; } } lastTitleHeight = currentHeight; EditorGUILayout.Space(); } void ResetButton(MaterialProperty prop) { GUIContent resetButton = new GUIContent(); resetButton.text = "R"; resetButton.tooltip = "Resets this property."; if (prop.type == MaterialProperty.PropType.Color) { Color defaultValue = defaultMaterial.GetColor(prop.name); if (prop.colorValue == defaultValue) { GUI.enabled = false; } if (GUILayout.Button(resetButton, GUILayout.Width(20))) { prop.colorValue = defaultValue; } } else if (prop.type == MaterialProperty.PropType.Vector) { Vector4 defaultValue = defaultMaterial.GetVector(prop.name); if (prop.vectorValue == defaultValue) { GUI.enabled = false; } if (GUILayout.Button(resetButton, GUILayout.Width(20))) { prop.vectorValue = defaultValue; } } else if (prop.type == MaterialProperty.PropType.Float || prop.type == MaterialProperty.PropType.Range) { float defaultValue = defaultMaterial.GetFloat(prop.name); if (prop.floatValue == defaultValue) { GUI.enabled = false; } if (GUILayout.Button(resetButton, GUILayout.Width(20))) { prop.floatValue = defaultValue; } } else if (prop.type == MaterialProperty.PropType.Texture) { Texture defaultValue = defaultMaterial.GetTexture(prop.name); if (prop.textureValue == defaultValue) { GUI.enabled = false; } if (GUILayout.Button(resetButton, GUILayout.Width(20), GUILayout.Height(50))) { prop.textureValue = defaultValue; } } GUI.enabled = true; } void HelpButton(MaterialProperty prop, GUIContent displayContent) { GUIContent infoButton = new GUIContent(); infoButton.text = " C#"; infoButton.tooltip = "Useful information for programmers."; if (GUILayout.Button(infoButton, buttonStyle, GUILayout.Width(26), GUILayout.Height((prop.type == MaterialProperty.PropType.Texture) ? 50 : 20))) { CodingHelper.Open(displayContent, prop, shader, EditorGUIUtility.currentViewWidth); } } Rect PropertyLabel(GUIContent labelContent) { EditorGUILayout.LabelField(" "); Rect labelRect = GUILayoutUtility.GetLastRect(); Rect fieldRect = new Rect(labelRect); if (labelRect.width > 320) { labelRect.width -= 160; } else { labelRect.width = labelRect.width / 2; } fieldRect.width -= labelRect.width; fieldRect.x += labelRect.width; EditorGUI.PrefixLabel(labelRect, labelContent); return fieldRect; } Rect GetPropertySize(MaterialProperty prop, bool isKeyword) { EditorGUILayout.LabelField(" "); Rect fullRect = GUILayoutUtility.GetLastRect(); Rect labelRect = new Rect(fullRect); Rect fieldRect = new Rect(labelRect); if (labelRect.width > 320) { labelRect.width -= 160; } else { labelRect.width = labelRect.width / 2; } if(isKeyword) { labelRect.width -= 52; } fieldRect.width -= labelRect.width; fieldRect.x += labelRect.width; EditorGUIUtility.fieldWidth = fieldRect.width; EditorGUIUtility.labelWidth = labelRect.width; if (prop.type == MaterialProperty.PropType.Range) { EditorGUIUtility.fieldWidth = 50; EditorGUIUtility.labelWidth = labelRect.width; } return fullRect; } bool IsKeyword(MaterialProperty prop) { return IsKeyword(prop.name); } public static bool IsKeyword(string propertyName) { if (propertyName.StartsWith("_Toggle") || propertyName.EndsWith("Toggle") || propertyName.EndsWith("Invert") || propertyName == "PixelSnap" || propertyName == "_ShaderSpace" || propertyName == "_ShaderFading" || propertyName == "_BakedMaterial" || propertyName == "_SmokeVertexSeed" || propertyName == "_SpriteSheetFix" || propertyName == "_UseUIAlphaClip" || propertyName == "_IsText" || propertyName == "_IsTextMeshPro" || propertyName == "_TilingFix" || propertyName == "_ForceAlpha" || propertyName == "_VertexTintFirst" || propertyName == "_PixelPerfectSpace" || propertyName == "_PixelPerfectUV" || propertyName == "_WindLocalWind" || propertyName == "_WindHighQualityNoise" || propertyName == "_WindIsParallax" || propertyName == "_WindFlip" || propertyName == "_SquishFlip") { return true; } return false; } public static void Lines() { GUI.color = new Color(1, 1, 1, 0.5f); EditorGUILayout.LabelField("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ", GUILayout.Height(9)); GUI.color = new Color(1, 1, 1, 1); } public static void DisplaySupportInformation() { GUIStyle labelStyle = new GUIStyle(GUI.skin.label); labelStyle.richText = true; GUIStyle linkStyle = new GUIStyle(GUI.skin.label); linkStyle.richText = true; linkStyle.alignment = TextAnchor.MiddleLeft; linkStyle.normal.textColor = linkStyle.focused.textColor = linkStyle.hover.textColor = EditorGUIUtility.isProSkin ? new Color(0.8f, 0.9f, 1f, 1) : new Color(0.1f, 0.2f, 0.4f, 1); linkStyle.active.textColor = EditorGUIUtility.isProSkin ? new Color(0.6f, 0.8f, 1f, 1) : new Color(0.15f, 0.4f, 0.6f, 1); DisplayLink("Documentation", "https://ekincantas.com/sprite-shaders-ultimate/", "https://ekincantas.com/sprite-shaders-ultimate/", labelStyle, linkStyle); DisplayLink("Discord", "https://discord.gg/nWbRkN8Zxr", "https://discord.gg/nWbRkN8Zxr", labelStyle, linkStyle); DisplayLink("Email", "ekincantascontact@gmail.com", "", labelStyle, linkStyle); } private static void DisplayLink(string title, string link, string url, GUIStyle labelStyle, GUIStyle linkStyle) { GUI.color = new Color(1, 1, 1, 0.7f); EditorGUILayout.BeginHorizontal(); GUILayout.Label("" + title + ": ", labelStyle); Rect lastRect = GUILayoutUtility.GetLastRect(); lastRect.x += 100; lastRect.width -= 100; GUI.color = Color.white; if (url == "") { EditorGUI.SelectableLabel(lastRect, link, linkStyle); } else { if(GUI.Button(lastRect,link, linkStyle)) { Application.OpenURL(url); } } EditorGUILayout.EndHorizontal(); } public static void ExportSprite(MaterialEditor materialEditor, int count = 1) { Material material = (Material)materialEditor.target; Texture texture = GetTexture(materialEditor); if (material != null && texture != null) { SaveShadedTexture(material, texture, count); } else { EditorUtility.DisplayDialog("No texture found.", "Select a SpriteRenderer or Image with this material.\nOr assign a texture to this material.", "Alright"); } } public static void SaveShadedTexture(Material spriteMaterial, Texture spriteTexture, int count = 1) { //Enable Baked Material: Undo.RecordObject(spriteMaterial, "Switched baked material setting."); spriteMaterial.EnableKeyword("_BAKEDMATERIAL_ON"); spriteMaterial.SetFloat("_BakedMaterial", 1f); Texture2D tex = (Texture2D) spriteTexture; bool first = true; for (int n = 0; n < count; n++) { //Blit to Render Texture: RenderTexture rt = new RenderTexture(tex.width, tex.height, 0, RenderTextureFormat.ARGB32); Graphics.Blit(tex, rt, spriteMaterial); //Destroy Previous Texture: if (!first) { Object.DestroyImmediate(tex); } first = false; //Read RenderTexture to Texture: tex = new Texture2D(rt.width, rt.height, TextureFormat.ARGB32, false); tex.ReadPixels(new Rect(0, 0, rt.width, rt.height), 0, 0); tex.Apply(); //Clear RenderTexture: if (RenderTexture.active == rt) { RenderTexture.active = null; } Object.DestroyImmediate(rt); } //Disable Baked Material: spriteMaterial.DisableKeyword("_BAKEDMATERIAL_ON"); spriteMaterial.SetFloat("_BakedMaterial", 0f); //Get Paths: string oldPath = AssetDatabase.GetAssetPath(spriteTexture); if (oldPath == "") { oldPath = Application.dataPath + "/"; } string[] fileEnding = oldPath.Split('.'); string newPath = oldPath.Replace("." + fileEnding[fileEnding.Length - 1], ""); string folderPath = ""; string[] folders = oldPath.Split('/'); for (int n = 0; n < folders.Length - 1; n++) { folderPath += folders[n] + "/"; } string fileName = spriteTexture.name; int number = 1; while (AssetDatabase.LoadAssetAtPath(newPath + " (Baked " + number + ").png", typeof(Texture)) != null) { number++; } newPath = EditorUtility.SaveFilePanel("Save Texture", folderPath, spriteTexture.name + " (Baked " + number + ")", "png"); EditorGUILayout.BeginVertical(); EditorGUILayout.BeginVertical(); EditorGUILayout.BeginVertical(); if (newPath != "") { newPath = newPath.Substring(Application.dataPath.Length - 9); while (newPath.Length > 1 && newPath.StartsWith("Assets") == false) { newPath = newPath.Substring(1); } //Save Texture: byte[] bytes = tex.EncodeToPNG(); bool newFile = AssetDatabase.LoadAssetAtPath(newPath, typeof(Texture)) == null; if (newFile) { AssetDatabase.CopyAsset(oldPath, newPath); } System.IO.File.WriteAllBytes(newPath, bytes); if (newFile) { AssetDatabase.ImportAsset(newPath); } //Import new Texture: AssetDatabase.Refresh(); //Ping: EditorGUIUtility.PingObject(AssetDatabase.LoadAssetAtPath(newPath, typeof(Texture))); } //Clean Up: Object.DestroyImmediate(tex); } } } #endif