568 lines
22 KiB
C#
Executable file
568 lines
22 KiB
C#
Executable file
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using UnityEditor;
|
|
using UnityEngine;
|
|
|
|
//Made by Dreadrith#3238
|
|
//Version v1.1.0
|
|
//Discord: https://discord.gg/ZsPfrGn
|
|
//Github: https://github.com/Dreadrith/DreadScripts
|
|
//Gumroad: https://gumroad.com/dreadrith
|
|
//Ko-fi: https://ko-fi.com/dreadrith
|
|
|
|
namespace Poi.Tools
|
|
{
|
|
public class GradientFlood : EditorWindow
|
|
{
|
|
|
|
#region Automated Variables
|
|
private static Vector2 scroll;
|
|
private static float modifiedBoundRange;
|
|
private static Texture2D titleTexture;
|
|
#endregion
|
|
|
|
#region Input
|
|
public static Texture2D pathTexture;
|
|
public static Color startPixelsColor = Color.red;
|
|
public static Color limitPixelsColor = Color.white;
|
|
public static GradientType gradientType = GradientType.DataGradient;
|
|
|
|
public static Color tintColor = Color.white;
|
|
public static Gradient gradientColor = new Gradient();
|
|
|
|
public static float gradientDistribution = 1;
|
|
public static float startColorTolerance = 0.05f;
|
|
public static float limitColorTolerance = 0.05f;
|
|
public static float rangeLowerBound;
|
|
public static float rangeUpperBound = 255;
|
|
public static bool invertGradient;
|
|
public static bool loopGradient;
|
|
public static bool applyGradientAlpha;
|
|
#endregion
|
|
|
|
|
|
public enum GradientType
|
|
{
|
|
DataGradient,
|
|
TintedGradient,
|
|
GradientGradient
|
|
}
|
|
|
|
public enum _FloodBehavior
|
|
{
|
|
Side,
|
|
Diagonal,
|
|
SideAndDiagonal,
|
|
Horizontal,
|
|
Vertical,
|
|
Custom
|
|
}
|
|
|
|
public static bool foldoutFloodStep;
|
|
public static _FloodBehavior floodBehavior = _FloodBehavior.SideAndDiagonal;
|
|
public static List<FloodStep> floodSteps = new List<FloodStep>()
|
|
{
|
|
new FloodStep
|
|
(
|
|
new Vector2Int(1, 0),
|
|
new Vector2Int(-1, 0),
|
|
new Vector2Int(0, 1),
|
|
new Vector2Int(0, -1),
|
|
new Vector2Int(1, 1),
|
|
new Vector2Int(1, -1),
|
|
new Vector2Int(-1, 1),
|
|
new Vector2Int(-1, -1)
|
|
|
|
)
|
|
};
|
|
|
|
|
|
|
|
[MenuItem("Poi/Gradient Flood")]
|
|
private static void showWindow()
|
|
{
|
|
EditorWindow w = GetWindow<GradientFlood>(false, "Gradient Flood", true);
|
|
if (!titleTexture)
|
|
{
|
|
titleTexture = GetColors((Texture2D)EditorGUIUtility.IconContent("Texture2D Icon").image, 16, 16, out _);
|
|
titleTexture.Apply();
|
|
}
|
|
|
|
w.titleContent.image = titleTexture;
|
|
w.minSize = new Vector2(423, 253);
|
|
}
|
|
|
|
private void OnGUI()
|
|
{
|
|
scroll = EditorGUILayout.BeginScrollView(scroll);
|
|
GUIStyle centeredTitle = new GUIStyle("boldlabel") {alignment = TextAnchor.MiddleCenter, fontSize = 16};
|
|
using (new GUILayout.VerticalScope("helpbox"))
|
|
{
|
|
GUILayout.Label("Texture", centeredTitle);
|
|
using (new GUILayout.HorizontalScope())
|
|
{
|
|
GUILayout.FlexibleSpace();
|
|
EditorGUIUtility.labelWidth = 1;
|
|
pathTexture = (Texture2D) EditorGUILayout.ObjectField(string.Empty, pathTexture, typeof(Texture2D), false, GUILayout.Width(80), GUILayout.Height(80));
|
|
EditorGUIUtility.labelWidth = 0;
|
|
|
|
GUILayout.FlexibleSpace();
|
|
}
|
|
|
|
DrawFloodBehaviorGUI();
|
|
|
|
using (new GUILayout.HorizontalScope("box"))
|
|
gradientType = (GradientType)EditorGUILayout.EnumPopup("Gradient Type", gradientType);
|
|
|
|
|
|
switch (gradientType)
|
|
{
|
|
case GradientType.TintedGradient:
|
|
using (new GUILayout.HorizontalScope("box"))
|
|
tintColor = EditorGUILayout.ColorField("Tint", tintColor);
|
|
break;
|
|
case GradientType.GradientGradient:
|
|
using (new GUILayout.HorizontalScope("box"))
|
|
gradientColor = EditorGUILayout.GradientField("Gradient", gradientColor);
|
|
break;
|
|
}
|
|
|
|
|
|
if (gradientType > 0)
|
|
{
|
|
using (new GUILayout.HorizontalScope("box"))
|
|
{
|
|
GUILayout.Label("Color Range");
|
|
|
|
EditorGUI.BeginChangeCheck();
|
|
|
|
rangeLowerBound = EditorGUILayout.DelayedIntField((int) rangeLowerBound, GUI.skin.label, GUILayout.Width(28));
|
|
EditorGUILayout.MinMaxSlider(ref rangeLowerBound, ref rangeUpperBound, 0, 255);
|
|
|
|
rangeUpperBound = EditorGUILayout.DelayedIntField((int) rangeUpperBound, GUI.skin.label, GUILayout.Width(28));
|
|
|
|
if (EditorGUI.EndChangeCheck())
|
|
{
|
|
rangeUpperBound = Mathf.Clamp((int) rangeUpperBound, 0, 255);
|
|
rangeLowerBound = Mathf.Max(0, Mathf.Min(rangeUpperBound, rangeLowerBound));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
using (new GUILayout.HorizontalScope("box"))
|
|
{
|
|
using (new GUILayout.HorizontalScope())
|
|
GUILayout.Label("Tolerance");
|
|
|
|
using (new GUILayout.HorizontalScope())
|
|
{
|
|
EditorGUIUtility.labelWidth = 40;
|
|
startColorTolerance = EditorGUILayout.Slider("Start", startColorTolerance, 0, 1);
|
|
limitColorTolerance = EditorGUILayout.Slider("Limit", limitColorTolerance, 0, 1);
|
|
EditorGUIUtility.labelWidth = 0;
|
|
}
|
|
}
|
|
|
|
using (new GUILayout.HorizontalScope("box"))
|
|
{
|
|
invertGradient = EditorGUILayout.Toggle("Invert Gradient", invertGradient);
|
|
|
|
if (gradientType > 0) applyGradientAlpha = EditorGUILayout.Toggle("Apply Gradient Alpha", applyGradientAlpha);
|
|
}
|
|
|
|
using (new GUILayout.HorizontalScope("box"))
|
|
{
|
|
loopGradient = EditorGUILayout.Toggle("Loop Gradient", loopGradient);
|
|
if (loopGradient) gradientDistribution = EditorGUILayout.FloatField("Distribution", gradientDistribution);
|
|
}
|
|
|
|
using (new EditorGUI.DisabledScope(!pathTexture))
|
|
if (GUILayout.Button("Fill"))
|
|
GenerateFilledTexture(pathTexture);
|
|
|
|
}
|
|
Credit();
|
|
|
|
EditorGUILayout.EndScrollView();
|
|
}
|
|
|
|
private void DrawFloodBehaviorGUI()
|
|
{
|
|
using (var change = new EditorGUI.ChangeCheckScope())
|
|
{
|
|
using (new GUILayout.HorizontalScope("box"))
|
|
floodBehavior = (_FloodBehavior)EditorGUILayout.EnumPopup(new GUIContent("Flood Behavior", "The path to fill in the texture starting from the start pixels"), floodBehavior);
|
|
|
|
if (change.changed)
|
|
{
|
|
switch (floodBehavior)
|
|
{
|
|
case _FloodBehavior.Side:
|
|
floodSteps.Clear();
|
|
floodSteps.Add(new FloodStep
|
|
(
|
|
|
|
new Vector2Int(1, 0),
|
|
new Vector2Int(-1, 0),
|
|
new Vector2Int(0, 1),
|
|
new Vector2Int(0, -1)
|
|
|
|
));
|
|
break;
|
|
case _FloodBehavior.Diagonal:
|
|
floodSteps.Clear();
|
|
floodSteps.Add(new FloodStep
|
|
(
|
|
new Vector2Int(1, 1),
|
|
new Vector2Int(-1, -1),
|
|
new Vector2Int(1, -1),
|
|
new Vector2Int(-1, 1)
|
|
));
|
|
break;
|
|
case _FloodBehavior.SideAndDiagonal:
|
|
floodSteps.Clear();
|
|
floodSteps.Add(new FloodStep
|
|
(
|
|
|
|
new Vector2Int(1, 0),
|
|
new Vector2Int(-1, 0),
|
|
new Vector2Int(0, 1),
|
|
new Vector2Int(0, -1),
|
|
new Vector2Int(1, 1),
|
|
new Vector2Int(1, -1),
|
|
new Vector2Int(-1, 1),
|
|
new Vector2Int(-1, -1)
|
|
|
|
));
|
|
break;
|
|
case _FloodBehavior.Horizontal:
|
|
floodSteps.Clear();
|
|
floodSteps.Add(new FloodStep
|
|
(
|
|
new Vector2Int(1, 0),
|
|
new Vector2Int(-1, 0)
|
|
));
|
|
break;
|
|
case _FloodBehavior.Vertical:
|
|
floodSteps.Clear();
|
|
floodSteps.Add(new FloodStep
|
|
(
|
|
new Vector2Int(0, 1),
|
|
new Vector2Int(0, -1)
|
|
));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (floodBehavior != _FloodBehavior.Custom) return;
|
|
|
|
using (new GUILayout.VerticalScope("box"))
|
|
{
|
|
using (new GUILayout.VerticalScope("helpbox"))
|
|
{
|
|
foldoutFloodStep = EditorGUILayout.Foldout(foldoutFloodStep, "Flood Steps");
|
|
}
|
|
|
|
if (foldoutFloodStep)
|
|
{
|
|
EditorGUI.indentLevel++;
|
|
for (int i = 0; i < floodSteps.Count; i++)
|
|
{
|
|
DrawFloodStepGUI(floodSteps[i], i);
|
|
}
|
|
|
|
if (GUILayout.Button("Add Step"))
|
|
floodSteps.Add(new FloodStep());
|
|
EditorGUI.indentLevel--;
|
|
}
|
|
}
|
|
}
|
|
private void DrawFloodStepGUI(FloodStep step, int i)
|
|
{
|
|
using (new GUILayout.VerticalScope("box"))
|
|
{
|
|
using (new GUILayout.HorizontalScope("helpbox"))
|
|
{
|
|
step.foldout = EditorGUILayout.Foldout(step.foldout, $"Step {i}:");
|
|
|
|
using (new EditorGUI.DisabledGroupScope(floodSteps.Count == 1))
|
|
if (GUILayout.Button("X", GUILayout.Width(20), GUILayout.Height(18)))
|
|
{
|
|
floodSteps.RemoveAt(i);
|
|
goto Skip;
|
|
}
|
|
}
|
|
|
|
if (step.foldout)
|
|
{
|
|
EditorGUI.indentLevel++;
|
|
for (int j = 0; j < step.coordinates.Count; j++)
|
|
{
|
|
using (new GUILayout.HorizontalScope("box"))
|
|
{
|
|
step.coordinates[j] = EditorGUILayout.Vector2IntField(string.Empty, step.coordinates[j]);
|
|
if (GUILayout.Button("X", GUILayout.Width(20), GUILayout.Height(18)))
|
|
step.coordinates.RemoveAt(j);
|
|
}
|
|
|
|
|
|
}
|
|
if (GUILayout.Button("+", "toolbarbutton")) step.coordinates.Add(new Vector2Int());
|
|
|
|
EditorGUI.indentLevel--;
|
|
}
|
|
Skip:;
|
|
|
|
}
|
|
}
|
|
|
|
public static void GenerateFilledTexture(Texture2D texture)
|
|
{
|
|
if (!loopGradient) gradientDistribution = 1;
|
|
if (gradientType == GradientType.DataGradient)
|
|
{
|
|
rangeLowerBound = 1;
|
|
rangeUpperBound = 255;
|
|
}
|
|
|
|
int width = texture.width;
|
|
modifiedBoundRange = rangeUpperBound / 255 - rangeLowerBound / 255;
|
|
Texture2D gradientTexture = GetColors(texture, out Color[] ogColors);
|
|
GradientFillPixel[] gradientPixels = new GradientFillPixel[ogColors.Length];
|
|
|
|
Queue<GradientFillPixel> pixelsToExpand = new Queue<GradientFillPixel>();
|
|
|
|
for (int i = 0; i < ogColors.Length; i++)
|
|
{
|
|
gradientPixels[i] = new GradientFillPixel(ogColors[i], i);
|
|
if (!gradientPixels[i].isLimit && gradientPixels[i].isFilled) pixelsToExpand.Enqueue(gradientPixels[i]);
|
|
}
|
|
|
|
if (pixelsToExpand.Count == 0)
|
|
Debug.LogWarning("<color=red>[GPFiller]</color> No start pixels were found! If start pixels exist, try adjusting the color tolerance.");
|
|
|
|
int floodIndex = 0;
|
|
int currentStackCount = pixelsToExpand.Count;
|
|
int nextStackCount = 0;
|
|
|
|
bool IsValidFillIndex(int index, out GradientFillPixel pixelToFill)
|
|
{
|
|
if (index >= 0 && index < ogColors.Length)
|
|
{
|
|
pixelToFill = gradientPixels[index];
|
|
if (!pixelToFill.isLimit && !pixelToFill.isFilled)
|
|
{
|
|
pixelsToExpand.Enqueue(pixelToFill);
|
|
nextStackCount++;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
pixelToFill = null;
|
|
return false;
|
|
}
|
|
|
|
void FillIndex(GradientFillPixel fillerPixel, int index)
|
|
{
|
|
if (!IsValidFillIndex(index, out GradientFillPixel nextPixel)) return;
|
|
nextPixel.gradientValue = fillerPixel.gradientValue + gradientDistribution;
|
|
nextPixel.isFilled = true;
|
|
}
|
|
|
|
|
|
|
|
while (pixelsToExpand.Any())
|
|
{
|
|
FloodStep currentStep = floodSteps[floodIndex % floodSteps.Count];
|
|
var pixel = pixelsToExpand.Dequeue();
|
|
|
|
foreach (var coord in currentStep.coordinates)
|
|
{
|
|
int finalX = (pixel.arrayIndex % width) + coord.x;
|
|
if (finalX < 0 || finalX >= width) continue;
|
|
|
|
FillIndex(pixel, pixel.arrayIndex + coord.x + width * coord.y);
|
|
}
|
|
|
|
currentStackCount--;
|
|
if (currentStackCount == 0)
|
|
{
|
|
currentStackCount = nextStackCount;
|
|
nextStackCount = 0;
|
|
floodIndex++;
|
|
}
|
|
}
|
|
|
|
|
|
float maxGradientValue = gradientPixels.Max(p => p.gradientValue);
|
|
if (gradientType == GradientType.DataGradient) maxGradientValue /= 4;
|
|
|
|
for (int i = 0; i < ogColors.Length; i++)
|
|
{
|
|
var f = gradientPixels[i].GetFloatValue(maxGradientValue);
|
|
|
|
if (f == 0) ogColors[i] = Color.clear;
|
|
else
|
|
{
|
|
|
|
if (gradientType == GradientType.TintedGradient)
|
|
ogColors[i] = new Color(f * tintColor.r, f * tintColor.g, f * tintColor.b, gradientPixels[i].isLimit ? 1 : (applyGradientAlpha ? f : 1) * tintColor.a);
|
|
else if (gradientType == GradientType.GradientGradient)
|
|
{
|
|
Color gradColor = gradientColor.Evaluate(f);
|
|
gradColor.a = gradientPixels[i].isLimit ? 1 : (applyGradientAlpha ? f : 1) * gradColor.a;
|
|
ogColors[i] = gradColor;
|
|
}
|
|
else
|
|
{
|
|
if (f <= 1) ogColors[i] = new Color(f % 1, 0, 0, 0);
|
|
|
|
else if (f <= 2) ogColors[i] = new Color(1, f%1, 0, 0);
|
|
|
|
else if (f <= 3) ogColors[i] = new Color(1, 1, f % 1, 0);
|
|
|
|
else ogColors[i] = new Color(1, 1, 1, f % 1);
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
gradientTexture.SetPixels(ogColors);
|
|
gradientTexture.Apply();
|
|
|
|
string assetPath = AssetDatabase.GetAssetPath(pathTexture);
|
|
string ext = Path.GetExtension(assetPath);
|
|
byte[] data;
|
|
switch (ext)
|
|
{
|
|
case ".jpeg" when !applyGradientAlpha && gradientType != GradientType.DataGradient:
|
|
case ".jpg" when !applyGradientAlpha && gradientType != GradientType.DataGradient:
|
|
ext = ".jpg";
|
|
data = gradientTexture.EncodeToJPG(100);
|
|
break;
|
|
case ".tga":
|
|
data = gradientTexture.EncodeToTGA();
|
|
break;
|
|
default:
|
|
ext = ".png";
|
|
data = gradientTexture.EncodeToPNG();
|
|
break;
|
|
}
|
|
|
|
|
|
DestroyImmediate(gradientTexture);
|
|
|
|
string savePath = AssetDatabase.GenerateUniqueAssetPath($"{Path.GetDirectoryName(assetPath)}/{pathTexture.name}{ext}");
|
|
SaveTexture(data, savePath);
|
|
CopyTextureSettings(assetPath, savePath);
|
|
}
|
|
|
|
public static Texture2D GetColors(Texture2D texture, int width, int height, out Color[] Colors, bool unloadTempTexture = false)
|
|
{
|
|
//Thanks to
|
|
//https://gamedev.stackexchange.com/questions/92285/unity3d-resize-texture-without-corruption
|
|
texture.filterMode = FilterMode.Point;
|
|
RenderTexture rt = RenderTexture.GetTemporary(width, height);
|
|
|
|
rt.filterMode = FilterMode.Point;
|
|
RenderTexture.active = rt;
|
|
Graphics.Blit(texture, rt);
|
|
Texture2D newTexture = new Texture2D(width, height);
|
|
newTexture.ReadPixels(new Rect(0, 0, width, height), 0, 0);
|
|
Color[] myColors = newTexture.GetPixels();
|
|
RenderTexture.active = null;
|
|
/////////////////////
|
|
Colors = myColors;
|
|
if (unloadTempTexture)
|
|
{
|
|
DestroyImmediate(newTexture);
|
|
return null;
|
|
}
|
|
return newTexture;
|
|
}
|
|
|
|
public static Texture2D GetColors(Texture2D texture, out Color[] Colors, bool unloadTempTexture = false)
|
|
{
|
|
return GetColors(texture, texture.width, texture.height, out Colors, unloadTempTexture);
|
|
}
|
|
|
|
private static void CopyTextureSettings(string from, string to)
|
|
{
|
|
TextureImporter source = (TextureImporter)AssetImporter.GetAtPath(from);
|
|
TextureImporterSettings sourceSettings = new TextureImporterSettings();
|
|
source.ReadTextureSettings(sourceSettings);
|
|
|
|
TextureImporter destination = (TextureImporter)AssetImporter.GetAtPath(to);
|
|
destination.SetTextureSettings(sourceSettings);
|
|
destination.maxTextureSize = source.maxTextureSize;
|
|
destination.textureCompression = source.textureCompression;
|
|
destination.crunchedCompression = source.crunchedCompression;
|
|
destination.SaveAndReimport();
|
|
}
|
|
|
|
private static void SaveTexture(byte[] textureEncoding, string path)
|
|
{
|
|
using (System.IO.FileStream stream = System.IO.File.Create(path))
|
|
stream.Write(textureEncoding, 0, textureEncoding.Length);
|
|
AssetDatabase.Refresh();
|
|
EditorGUIUtility.PingObject(AssetDatabase.LoadMainAssetAtPath(path));
|
|
}
|
|
|
|
public class GradientFillPixel
|
|
{
|
|
public readonly bool isLimit;
|
|
public bool isFilled;
|
|
public float gradientValue;
|
|
public int arrayIndex;
|
|
|
|
public GradientFillPixel(Color pixelColor, int index)
|
|
{
|
|
isLimit = Mathf.Abs(pixelColor.r - limitPixelsColor.r) < limitColorTolerance &&
|
|
Mathf.Abs(pixelColor.g - limitPixelsColor.g) < limitColorTolerance &&
|
|
Mathf.Abs(pixelColor.b - limitPixelsColor.b) < limitColorTolerance;
|
|
|
|
isFilled = Mathf.Abs(pixelColor.r - startPixelsColor.r) < startColorTolerance &&
|
|
Mathf.Abs(pixelColor.g - startPixelsColor.g) < startColorTolerance &&
|
|
Mathf.Abs(pixelColor.b - startPixelsColor.b) < startColorTolerance;
|
|
|
|
gradientValue = (int)rangeLowerBound;
|
|
arrayIndex = index;
|
|
}
|
|
|
|
public float GetFloatValue(float maxGradientValue)
|
|
{
|
|
if (isLimit || !isFilled) return 0;
|
|
float floatValue = !loopGradient ? gradientValue / maxGradientValue : (gradientValue % 255 * (gradientType == GradientType.DataGradient ? 4 : 1)) / 255f;
|
|
float adjustedValue = floatValue * modifiedBoundRange + rangeLowerBound / 255;
|
|
return invertGradient ? (gradientType == GradientType.DataGradient ? 4 : 1) - adjustedValue : adjustedValue;
|
|
}
|
|
}
|
|
|
|
public class FloodStep
|
|
{
|
|
public List<Vector2Int> coordinates;
|
|
public bool foldout;
|
|
|
|
public FloodStep(params Vector2Int[] coordinates)
|
|
{
|
|
this.coordinates = coordinates.ToList();
|
|
}
|
|
}
|
|
|
|
|
|
private static void Credit()
|
|
{
|
|
using (new GUILayout.HorizontalScope())
|
|
{
|
|
GUILayout.FlexibleSpace();
|
|
if (GUILayout.Button("Made By Dreadrith#3238", "boldlabel"))
|
|
Application.OpenURL("https://linktr.ee/Dreadrith");
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
}
|