private void OnEnable () { source = Selection.activeObject as Texture2D; rgbFillMode = (RGBFillMode)EditorPrefs.GetInt(rgbModeKey); insideDistance = EditorPrefs.GetFloat(insideDistanceKey, 3f); outsideDistance = EditorPrefs.GetFloat(outsideDistanceKey, 3f); postProcessDistance = EditorPrefs.GetFloat(postProcessDistanceKey); }
void OnEnable() { source = Selection.activeObject as Texture2D; rgbFillMode = (RGBFillMode)EditorPrefs.GetInt(rgbModeKey); insideDistance = EditorPrefs.GetFloat(insideDistanceKey, 3f); outsideDistance = EditorPrefs.GetFloat(outsideDistanceKey, 3f); postProcessDistance = EditorPrefs.GetFloat(postProcessDistanceKey); }
/// <summary> /// Fill a texture with a signed distance field generated from the alpha channel of a source texture. /// </summary> /// <param name="source"> /// Source texture. Alpha values of 1 are considered inside, values of 0 are considered outside, and any other values are considered /// to be on the edge. Must be readable. /// </param> /// <param name="destination"> /// Destination texture. Must be the same size as the source texture. Must be readable. /// The texture change does not get applied automatically, you need to do that yourself. /// </param> /// <param name="maxInside"> /// Maximum pixel distance measured inside the edge, resulting in an alpha value of 1. /// If set to or below 0, everything inside will have an alpha value of 1. /// </param> /// <param name="maxOutside"> /// Maximum pixel distance measured outside the edge, resulting in an alpha value of 0. /// If set to or below 0, everything outside will have an alpha value of 0. /// </param> /// <param name="postProcessDistance"> /// Pixel distance from the edge within which pixels will be post-processed using the edge gradient. /// </param> /// <param name="rgbMode"> /// How to fill the destination texture's RGB channels. /// </param> public static void Generate( Texture2D source, Texture2D destination, float maxInside, float maxOutside, float postProcessDistance, RGBFillMode rgbMode) { if (source.height != destination.height || source.width != destination.width) { Debug.LogError("Source and destination textures must be the same size."); return; } width = source.width; height = source.height; pixels = new Pixel[width, height]; int x, y; float scale; Color c = rgbMode == RGBFillMode.White ? Color.white : Color.black; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { pixels[x, y] = new Pixel(); } } if (maxInside > 0f) { for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { pixels[x, y].alpha = 1f - source.GetPixel(x, y).a; } } ComputeEdgeGradients(); GenerateDistanceTransform(); if (postProcessDistance > 0f) { PostProcess(postProcessDistance); } scale = 1f / maxInside; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { c.a = Mathf.Clamp01(pixels[x, y].distance * scale); destination.SetPixel(x, y, c); } } } if (maxOutside > 0f) { for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { pixels[x, y].alpha = source.GetPixel(x, y).a; } } ComputeEdgeGradients(); GenerateDistanceTransform(); if (postProcessDistance > 0f) { PostProcess(postProcessDistance); } scale = 1f / maxOutside; if (maxInside > 0f) { for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { c.a = 0.5f + (destination.GetPixel(x, y).a - Mathf.Clamp01(pixels[x, y].distance * scale)) * 0.5f; destination.SetPixel(x, y, c); } } } else { for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { c.a = Mathf.Clamp01(1f - pixels[x, y].distance * scale); destination.SetPixel(x, y, c); } } } } if (rgbMode == RGBFillMode.Distance) { for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { c = destination.GetPixel(x, y); c.r = c.a; c.g = c.a; c.b = c.a; destination.SetPixel(x, y, c); } } } else if (rgbMode == RGBFillMode.Source) { for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { c = source.GetPixel(x, y); c.a = destination.GetPixel(x, y).a; destination.SetPixel(x, y, c); } } } pixels = null; }
/// <summary> /// Fill a texture with a signed distance field generated from the alpha channel of a source texture. /// </summary> /// <param name="source"> /// Source texture. Alpha values of 1 are considered inside, values of 0 are considered outside, and any other values are considered /// to be on the edge. Must be readable. /// </param> /// <param name="destination"> /// Destination texture. Must be the same size as the source texture. Must be readable. /// The texture change does not get applied automatically, you need to do that yourself. /// </param> /// <param name="maxInside"> /// Maximum pixel distance measured inside the edge, resulting in an alpha value of 1. /// If set to or below 0, everything inside will have an alpha value of 1. /// </param> /// <param name="maxOutside"> /// Maximum pixel distance measured outside the edge, resulting in an alpha value of 0. /// If set to or below 0, everything outside will have an alpha value of 0. /// </param> /// <param name="postProcessDistance"> /// Pixel distance from the edge within which pixels will be post-processed using the edge gradient. /// </param> /// <param name="rgbMode"> /// How to fill the destination texture's RGB channels. /// </param> public static void Generate( Image <Rgba32> source, Image <Rgba32> destination, float maxInside, float maxOutside, float postProcessDistance, RGBFillMode rgbMode) { if (source.Height != destination.Height || source.Width != destination.Width) { Console.Out.WriteLine("Source and destination textures must be the same size."); return; } width = source.Width; height = source.Height; pixels = new Pixel[width, height]; int x, y; float scale; Rgba32 c = rgbMode == RGBFillMode.White ? Color.White : Color.Black; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { pixels[x, y] = new Pixel(); } } if (maxInside > 0f) { for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { pixels[x, y].alpha = source.GetPixel(x, y).R / 255f; } } ComputeEdgeGradients(); GenerateDistanceTransform(); if (postProcessDistance > 0f) { PostProcess(postProcessDistance); } scale = 1f / maxInside; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { c.A = (byte)(255f * Math.Clamp(pixels[x, y].distance * scale, 0, 1)); destination.SetPixel(x, y, c); } } } if (maxOutside > 0f) { for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { pixels[x, y].alpha = 1f - source.GetPixel(x, y).R / 255f; } } ComputeEdgeGradients(); GenerateDistanceTransform(); if (postProcessDistance > 0f) { PostProcess(postProcessDistance); } scale = 1f / maxOutside; if (maxInside > 0f) { for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { c.A = (byte)(255f * (0.5f + (destination.GetPixel(x, y).A / 255f - Math.Clamp(pixels[x, y].distance * scale, 0, 1)) * 0.5f)); destination.SetPixel(x, y, c); } } } else { for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { c.A = (byte)(255f * Math.Clamp(1f - pixels[x, y].distance * scale, 0, 1)); destination.SetPixel(x, y, c); } } } } if (rgbMode == RGBFillMode.Distance) { for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { c = destination.GetPixel(x, y); c.R = c.A; c.G = c.A; c.B = c.A; destination.SetPixel(x, y, c); } } } else if (rgbMode == RGBFillMode.Source) { for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { c = source.GetPixel(x, y); c.A = destination.GetPixel(x, y).A; destination.SetPixel(x, y, c); } } } pixels = null; }
void OnGUI() { GUILayout.BeginArea(new Rect(2f, 2f, 220f, position.height - 4f)); EditorGUI.BeginChangeCheck(); source = (Texture2D)EditorGUILayout.ObjectField(sourceTextureContent, source, typeof(Texture2D), false); if (EditorGUI.EndChangeCheck()) { DestroyImmediate(destination); allowSave = false; } EditorGUI.BeginChangeCheck(); rgbFillMode = (RGBFillMode)EditorGUILayout.EnumPopup(rgbFillModeContent, rgbFillMode); if (EditorGUI.EndChangeCheck()) { EditorPrefs.SetInt(rgbModeKey, (int)rgbFillMode); allowSave = false; } EditorGUI.BeginChangeCheck(); insideDistance = EditorGUILayout.FloatField(insideContent, insideDistance); if (EditorGUI.EndChangeCheck()) { EditorPrefs.SetFloat(insideDistanceKey, insideDistance); allowSave = false; } EditorGUI.BeginChangeCheck(); outsideDistance = EditorGUILayout.FloatField(outsideContent, outsideDistance); if (EditorGUI.EndChangeCheck()) { EditorPrefs.SetFloat(outsideDistanceKey, outsideDistance); allowSave = false; } EditorGUI.BeginChangeCheck(); postProcessDistance = EditorGUILayout.FloatField(postprocessContent, postProcessDistance); if (EditorGUI.EndChangeCheck()) { EditorPrefs.SetFloat(postProcessDistanceKey, postProcessDistance); allowSave = false; } if (source != null && GUILayout.Button("Generate")) { Generate(); } if (allowSave && GUILayout.Button("Save PNG file")) { SaveTexture(); } GUILayout.EndArea(); if (destination != null) { scrollPosition = GUI.BeginScrollView( new Rect(224f, 2f, position.width - 226f, position.height - 4f), scrollPosition, new Rect(0f, 0f, destination.width, destination.height)); EditorGUI.DrawTextureAlpha(new Rect(0f, 0f, destination.width, destination.height), destination); GUI.EndScrollView(); } }
private void OnGUI () { GUILayout.BeginArea(new Rect(2f, 2f, 220f, position.height - 4f)); EditorGUI.BeginChangeCheck(); source = (Texture2D)EditorGUILayout.ObjectField(sourceTextureContent, source, typeof(Texture2D), false); if (EditorGUI.EndChangeCheck()) { DestroyImmediate(destination); allowSave = false; } EditorGUI.BeginChangeCheck(); rgbFillMode = (RGBFillMode)EditorGUILayout.EnumPopup(rgbFillModeContent, rgbFillMode); if (EditorGUI.EndChangeCheck()) { EditorPrefs.SetInt(rgbModeKey, (int)rgbFillMode); allowSave = false; } EditorGUI.BeginChangeCheck(); insideDistance = EditorGUILayout.FloatField(insideContent, insideDistance); if (EditorGUI.EndChangeCheck()) { EditorPrefs.SetFloat(insideDistanceKey, insideDistance); allowSave = false; } EditorGUI.BeginChangeCheck(); outsideDistance = EditorGUILayout.FloatField(outsideContent, outsideDistance); if (EditorGUI.EndChangeCheck()) { EditorPrefs.SetFloat(outsideDistanceKey, outsideDistance); allowSave = false; } EditorGUI.BeginChangeCheck(); postProcessDistance = EditorGUILayout.FloatField(postprocessContent, postProcessDistance); if (EditorGUI.EndChangeCheck()) { EditorPrefs.SetFloat(postProcessDistanceKey, postProcessDistance); allowSave = false; } if (source != null && GUILayout.Button("Generate")) { Generate(); } if (allowSave && GUILayout.Button("Save PNG file")) { string filePath = EditorUtility.SaveFilePanel( "Save Signed Distance Field", new FileInfo(AssetDatabase.GetAssetPath(source)).DirectoryName, source.name + " SDF", "png"); if (filePath.Length > 0) { File.WriteAllBytes(filePath, destination.EncodeToPNG()); AssetDatabase.Refresh(); } } GUILayout.EndArea(); if (destination != null) { scrollPosition = GUI.BeginScrollView( new Rect(224f, 2f, position.width - 226f, position.height - 4f), scrollPosition, new Rect(0f, 0f, destination.width, destination.height)); EditorGUI.DrawTextureAlpha(new Rect(0f, 0f, destination.width, destination.height), destination); GUI.EndScrollView(); } }
private void OnGUI() { GUILayout.BeginArea(new Rect(2f, 2f, 220f, position.height - 4f)); EditorGUI.BeginChangeCheck(); source = (Texture2D)EditorGUILayout.ObjectField(sourceTextureContent, source, typeof(Texture2D), false); if (EditorGUI.EndChangeCheck()) { DestroyImmediate(destination); allowSave = false; } EditorGUI.BeginChangeCheck(); rgbFillMode = (RGBFillMode)EditorGUILayout.EnumPopup(rgbFillModeContent, rgbFillMode); if (EditorGUI.EndChangeCheck()) { EditorPrefs.SetInt(rgbModeKey, (int)rgbFillMode); allowSave = false; } EditorGUI.BeginChangeCheck(); insideDistance = EditorGUILayout.FloatField(insideContent, insideDistance); if (EditorGUI.EndChangeCheck()) { EditorPrefs.SetFloat(insideDistanceKey, insideDistance); allowSave = false; } EditorGUI.BeginChangeCheck(); outsideDistance = EditorGUILayout.FloatField(outsideContent, outsideDistance); if (EditorGUI.EndChangeCheck()) { EditorPrefs.SetFloat(outsideDistanceKey, outsideDistance); allowSave = false; } EditorGUI.BeginChangeCheck(); postProcessDistance = EditorGUILayout.FloatField(postprocessContent, postProcessDistance); if (EditorGUI.EndChangeCheck()) { EditorPrefs.SetFloat(postProcessDistanceKey, postProcessDistance); allowSave = false; } if (source != null && GUILayout.Button("Generate")) { Generate(); } if (allowSave && GUILayout.Button("Save PNG file")) { string filePath = EditorUtility.SaveFilePanel( "Save Signed Distance Field", new FileInfo(AssetDatabase.GetAssetPath(source)).DirectoryName, source.name + " SDF", "png"); if (filePath.Length > 0) { File.WriteAllBytes(filePath, destination.EncodeToPNG()); AssetDatabase.Refresh(); } } GUILayout.EndArea(); if (destination != null) { scrollPosition = GUI.BeginScrollView( new Rect(224f, 2f, position.width - 226f, position.height - 4f), scrollPosition, new Rect(0f, 0f, destination.width, destination.height)); EditorGUI.DrawTextureAlpha(new Rect(0f, 0f, destination.width, destination.height), destination); GUI.EndScrollView(); } }