diff --git a/SDFMapCreator/ImageUtil.cs b/SDFMapCreator/ImageUtil.cs index c9c218d..75839e4 100644 --- a/SDFMapCreator/ImageUtil.cs +++ b/SDFMapCreator/ImageUtil.cs @@ -7,10 +7,6 @@ namespace SDFMapCreator; public static class ImageUtil { public static T[,] LoadImage(string path) where T : struct, IEquatable { var image = SixLabors.ImageSharp.Image.Load(path); - using var image16 = image as Image ?? throw new NotSupportedException($"Image format not supported"); - int width = image.Width; - int height = image.Height; - //result = new T[image.Width, image.Height]; return image switch { Image img => img.ProcessPixelsRgba64(), Image img => img.ProcessPixelsRgb24(), @@ -21,6 +17,7 @@ public static class ImageUtil { } static T[,] ProcessPixelsRgba64(this Image image) where T : struct, IEquatable { + var max = 65535f; int width = image.Width; int height = image.Height; var result = new T[image.Width, image.Height]; @@ -31,16 +28,16 @@ public static class ImageUtil { for (int x = 0; x < width; x++) { switch (result) { case float[,] f: - f[x, y] = span[x].R; + f[x, y] = span[x].R / max; break; case Vector2[,] f: - f[x, y] = new(span[x].R, span[x].G); + f[x, y] = new(span[x].R / max, span[x].G / max); break; case Vector3[,] f: - f[x, y] = new(span[x].R, span[x].G, span[x].B); + f[x, y] = new(span[x].R / max, span[x].G / max, span[x].B / max); break; case Vector4[,] f: - f[x, y] = new(span[x].R, span[x].G, span[x].B, 1f); + f[x, y] = new(span[x].R / max, span[x].G / max, span[x].B / max, span[x].A / max); break; } } @@ -50,6 +47,7 @@ public static class ImageUtil { } static T[,] ProcessPixelsRgb24(this Image image) where T : struct, IEquatable { + var max = 255f; int width = image.Width; int height = image.Height; var result = new T[image.Width, image.Height]; @@ -60,16 +58,16 @@ public static class ImageUtil { for (int x = 0; x < width; x++) { switch (result) { case float[,] f: - f[x, y] = span[x].R; + f[x, y] = span[x].R / max; break; case Vector2[,] f: - f[x, y] = new(span[x].R, span[x].G); + f[x, y] = new(span[x].R / max, span[x].G / max); break; case Vector3[,] f: - f[x, y] = new(span[x].R, span[x].G, span[x].B); + f[x, y] = new(span[x].R / max, span[x].G / max, span[x].B / max); break; case Vector4[,] f: - f[x, y] = new(span[x].R, span[x].G, span[x].B, 1f); + f[x, y] = new(span[x].R / max, span[x].G / max, span[x].B / max, 1f); break; } } @@ -79,6 +77,7 @@ public static class ImageUtil { } static T[,] ProcessPixelsRgba32(this Image image) where T : struct, IEquatable { + var max = 255f; int width = image.Width; int height = image.Height; var result = new T[image.Width, image.Height]; @@ -89,16 +88,16 @@ public static class ImageUtil { for (int x = 0; x < width; x++) { switch (result) { case float[,] f: - f[x, y] = span[x].R; + f[x, y] = span[x].R / max; break; case Vector2[,] f: - f[x, y] = new(span[x].R, span[x].G); + f[x, y] = new(span[x].R / max, span[x].G / max); break; case Vector3[,] f: - f[x, y] = new(span[x].R, span[x].G, span[x].B); + f[x, y] = new(span[x].R / max, span[x].G / max, span[x].B / max); break; case Vector4[,] f: - f[x, y] = new(span[x].R, span[x].G, span[x].B, 1f); + f[x, y] = new(span[x].R / max, span[x].G / max, span[x].B / max, span[x].A / max); break; } } @@ -108,27 +107,27 @@ public static class ImageUtil { } static T[,] ProcessPixelsRgb48(this Image image) where T : struct, IEquatable { - using var image16 = image as Image ?? throw new NotSupportedException($"Image format not supported"); + var max = 65535f; int width = image.Width; int height = image.Height; var result = new T[image.Width, image.Height]; - image16.ProcessPixelRows(accessor => { + image.ProcessPixelRows(accessor => { //we use Y as the row index and X as the column index for (int y = 0; y < height; y++) { var span = accessor.GetRowSpan(y); for (int x = 0; x < width; x++) { switch (result) { case float[,] f: - f[x, y] = span[x].R; + f[x, y] = span[x].R / max; break; case Vector2[,] f: - f[x, y] = new(span[x].R, span[x].G); + f[x, y] = new(span[x].R / max, span[x].G / max); break; case Vector3[,] f: - f[x, y] = new(span[x].R, span[x].G, span[x].B); + f[x, y] = new(span[x].R / max, span[x].G / max, span[x].B / max); break; case Vector4[,] f: - f[x, y] = new(span[x].R, span[x].G, span[x].B, 1f); + f[x, y] = new(span[x].R / max, span[x].G / max, span[x].B / max, 1f); break; } } @@ -155,16 +154,16 @@ public static class ImageUtil { for (int x = 0; x < width; x++) { switch (array) { case float[,] f: - span[x] = new Rgb48((ushort)f[x, y], (ushort)f[x, y], (ushort)f[x,y]); + span[x] = new Rgb48((ushort)(f[x, y] * 65535), (ushort)(f[x, y] * 65535), (ushort)(f[x,y] * 65535)); break; case Vector2[,] f: - span[x] = new Rgb48((ushort)f[x,y].X, (ushort)f[x,y].Y, 0); + span[x] = new Rgb48((ushort)(f[x,y].X * 65535), (ushort)(f[x,y].Y * 65535), 0); break; case Vector3[,] f: - span[x] = new Rgb48((ushort)f[x,y].X, (ushort)f[x,y].Y, (ushort)f[x,y].Z); + span[x] = new Rgb48((ushort)(f[x,y].X * 65535), (ushort)(f[x,y].Y * 65535), (ushort)(f[x,y].Z * 65535)); break; case Vector4[,] f: - span[x] = new Rgb48((ushort)f[x,y].X, (ushort)f[x,y].Y, (ushort)f[x,y].Z); + span[x] = new Rgb48((ushort)(f[x,y].X * 65535), (ushort)(f[x,y].Y * 65535), (ushort)(f[x,y].Z * 65535)); break; } } diff --git a/SDFMapCreator/Program.cs b/SDFMapCreator/Program.cs index 9f4680b..31c6890 100644 --- a/SDFMapCreator/Program.cs +++ b/SDFMapCreator/Program.cs @@ -4,7 +4,7 @@ using System.Runtime.CompilerServices; using SDFMapCreator; public class Program { - private const float MAX = 65535f; + private const float MAX = 1f; private const float MIN = 0f; private static readonly int MAX_THREADS = Environment.ProcessorCount - 2; private const bool outputMasks = true; @@ -24,10 +24,23 @@ public class Program { var imagesPath = "images"; + for (int i = 0; i < 8; i++) { var pixels = ImageUtil.LoadImage($"./{imagesPath}{Path.DirectorySeparatorChar}{i + 1:00}.png"); Images.Add(new(pixels, pixels.GetLength(0), pixels.GetLength(1))); } + + /* + var pixels = ImageUtil.LoadImage($"./sphereempty.png"); + Images.Add(new(pixels, pixels.GetLength(0), pixels.GetLength(1))); + pixels = ImageUtil.LoadImage($"./spherehalf.png"); + Images.Add(new(pixels, pixels.GetLength(0), pixels.GetLength(1))); + pixels = ImageUtil.LoadImage($"./spherecut.png"); + Images.Add(new(pixels, pixels.GetLength(0), pixels.GetLength(1))); + pixels = ImageUtil.LoadImage($"./spherefull.png"); + Images.Add(new(pixels, pixels.GetLength(0), pixels.GetLength(1))); + */ + //check if all the images in Images are the same resolution if (Images.Select(img => (img.Width, img.Height)).Distinct().Count() > 1) { @@ -61,7 +74,7 @@ public class Program { for (var i = 0; i < ImageMasks.Count; i++) { var mask = ImageMasks[i]; SDFs.Add(SDF(mask)); - if (outputSDFs) mask.Mask.SaveImage($"sdf{i}.png"); + if (outputSDFs) SDFs[i].SDF.SaveImage($"sdf{i}.png"); } Console.WriteLine("Creating gradients..."); @@ -75,13 +88,14 @@ public class Program { // generate final image var finalImage = new Vector3[width, height]; var currStep = 0f; - var stepIncrement = MAX / (Gradients.Count + 1); + var stepIncrement = MAX / (Gradients.Count); for (var i = 0; i < Gradients.Count; i++) { var mask = ImageMasks[i + 1]; var gradient = Gradients[i]; + Console.WriteLine($"Applying gradient {i}..., {currStep} -> {currStep + stepIncrement}"); for (var x = 0; x < mask.Mask.GetLength(0); x++) { for (var y = 0; y < mask.Mask.GetLength(1); y++) { - if (mask.Mask[x, y].X == 0 || mask.Mask[x, y].Y > 0) continue; + if (mask.Mask[x, y].X == 0) continue; finalImage[x, y] = new(Remap(gradient[x, y].X, MAX, MIN, currStep, currStep + stepIncrement)); } } @@ -116,7 +130,7 @@ public class Program { }); sw.Stop(); Console.WriteLine( - $"\nEdge pixels: {maskData.Edges.Count} | {maskData.Edges.Count / sw.ElapsedMilliseconds} pixels/s\n Time: {sw.Elapsed.TotalSeconds:F4}s"); + $"\nEdge pixels: {maskData.Edges.Count} | {maskData.Edges.Count / (sw.ElapsedMilliseconds+1)} pixels/s\n Time: {sw.Elapsed.TotalSeconds:F4}s"); } static Vector3[,] Gradient(TransitionMaskData mask, SDFData sdfA, SDFData sdfB) { @@ -167,16 +181,20 @@ public class Program { //convert 1D index to 2D index var x = (int)(i % width); var y = (int)(i / width); - Vector2 p = new(x, y); + Vector2 p = new(x/(float)width, y/(float)height); //get the pixel position as a Vector2 - float minDist = MAX; //initialize the minimum distance to the maximum possible value + float minDist = MAX + 1; //initialize the minimum distance to the maximum possible value //loop through all the pixels in the mask foreach (var edge in mask.Edges) { - float dist = Vector2.DistanceSquared(p, edge); - if (dist < minDist) minDist = mask.Mask[x, y].X == 0 ? dist : dist; + Vector2 edgeNorm = new(edge.X / (float)width, edge.Y / (float)height); + float dist = Vector2.DistanceSquared(p, edgeNorm); + if (dist < minDist) minDist = dist; } + if (minDist > MAX) + minDist = MAX; + temp[x, y] = new(minDist); if (minDist > AbsMax) AbsMax = minDist; iterCount++; diff --git a/SDFMapCreator/SDFMapCreator.csproj b/SDFMapCreator/SDFMapCreator.csproj index e0fb3ee..8872035 100644 --- a/SDFMapCreator/SDFMapCreator.csproj +++ b/SDFMapCreator/SDFMapCreator.csproj @@ -55,6 +55,18 @@ Always + + Always + + + Always + + + Always + + + Always + diff --git a/SDFMapCreator/spherecut32.png b/SDFMapCreator/spherecut32.png new file mode 100644 index 0000000..8f688c9 Binary files /dev/null and b/SDFMapCreator/spherecut32.png differ diff --git a/SDFMapCreator/sphereempty.png b/SDFMapCreator/sphereempty.png new file mode 100644 index 0000000..9291ad0 Binary files /dev/null and b/SDFMapCreator/sphereempty.png differ diff --git a/SDFMapCreator/spherefull32.png b/SDFMapCreator/spherefull32.png new file mode 100644 index 0000000..d46df9e Binary files /dev/null and b/SDFMapCreator/spherefull32.png differ diff --git a/SDFMapCreator/spherehalf32.png b/SDFMapCreator/spherehalf32.png new file mode 100644 index 0000000..30b01e0 Binary files /dev/null and b/SDFMapCreator/spherehalf32.png differ