From 2e314595acfb31cdf9023c6de50c77d33efabd3e Mon Sep 17 00:00:00 2001 From: "NGnius (Graham)" Date: Sun, 17 May 2020 23:17:44 -0400 Subject: [PATCH] Refactor and implement img to text block importing --- Pixi/Common/BlockInfo.cs | 17 +++ Pixi/Images/ImageCommands.cs | 214 +++++++++++++++++++++++++++ Pixi/Images/PixelUtility.cs | 168 ++++++++++++++++++++++ Pixi/Pixi.csproj | 34 ++++- Pixi/PixiPlugin.cs | 270 +---------------------------------- Pixi/Robots/CubeUtility.cs | 7 + Pixi/Robots/RobotCommands.cs | 7 + README.md | 3 +- 8 files changed, 450 insertions(+), 270 deletions(-) create mode 100644 Pixi/Common/BlockInfo.cs create mode 100644 Pixi/Images/ImageCommands.cs create mode 100644 Pixi/Images/PixelUtility.cs create mode 100644 Pixi/Robots/CubeUtility.cs create mode 100644 Pixi/Robots/RobotCommands.cs diff --git a/Pixi/Common/BlockInfo.cs b/Pixi/Common/BlockInfo.cs new file mode 100644 index 0000000..e887038 --- /dev/null +++ b/Pixi/Common/BlockInfo.cs @@ -0,0 +1,17 @@ +using System; + +using GamecraftModdingAPI.Blocks; + +namespace Pixi.Common +{ + public struct BlockInfo + { + public BlockIDs block; + + public BlockColors color; + + public byte darkness; + + public bool visible; + } +} diff --git a/Pixi/Images/ImageCommands.cs b/Pixi/Images/ImageCommands.cs new file mode 100644 index 0000000..16ae848 --- /dev/null +++ b/Pixi/Images/ImageCommands.cs @@ -0,0 +1,214 @@ +using System; +using System.IO; +using System.Text; +using System.Security.Cryptography; + +using UnityEngine; +using Unity.Mathematics; +using Svelto.ECS.Experimental; +using Svelto.ECS; + +using GamecraftModdingAPI.Blocks; +using GamecraftModdingAPI.Commands; +using GamecraftModdingAPI.Players; +using GamecraftModdingAPI.Utility; +using GamecraftModdingAPI; + +using Pixi.Common; + +namespace Pixi.Images +{ + public static class ImageCommands + { + public const uint PIXEL_WARNING_THRESHOLD = 25_000; + // hash length to display after Pixi in text block id field + public const uint HASH_LENGTH = 6; + + private static double blockSize = 0.2; + + private static uint thiccness = 1; + + public static void CreateThiccCommand() + { + CommandBuilder.Builder() + .Name("PixiThicc") + .Description("Set the image thickness for Pixi2D. Use this if you'd like add depth to a 2D image after importing. (Pixi)") + .Action((d) => { + if (d > 0) + { + thiccness = (uint)d; + } + else Logging.CommandLogError(""); + }) + .Build(); + } + + public static void CreateImportCommand() + { + CommandBuilder.Builder() + .Name("Pixi2D") + .Description("Converts an image to blocks. Larger images will freeze your game until conversion completes. (Pixi)") + .Action(Pixelate2DFile) + .Build(); + } + + public static void CreateTextCommand() + { + CommandBuilder.Builder() + .Name("PixiText") + .Description("Converts an image to coloured text in a new text block. Larger images may cause save issues. (Pixi)") + .Action(Pixelate2DFileToTextBlock) + .Build(); + } + + public static void CreateTextConsoleCommand() + { + CommandBuilder.Builder() + .Name("PixiConsole") + .Description("Converts an image to a ChangeTextBlockCommand in a new console block. The first parameter is the image filepath and the second parameter is the text block id. Larger images may cause save issues. (Pixi)") + .Action(Pixelate2DFileToCommand) + .Build(); + } + + public static void Pixelate2DFile(string filepath) + { + // Load image file and convert to Gamecraft blocks + Texture2D img = new Texture2D(64, 64); + // load file into texture + try + { + byte[] imgData = File.ReadAllBytes(filepath); + img.LoadImage(imgData); + } + catch (Exception e) + { + Logging.CommandLogError($"Failed to load picture data. Reason: {e.Message}"); + Logging.MetaLog(e.Message + "\n" + e.StackTrace); + return; + } + Logging.CommandLog($"Image size: {img.width}x{img.height}"); + float3 position = new Player(PlayerType.Local).Position; + uint blockCount = 0; + position.x += 1f; + position.y += (float)blockSize; + float zero_y = position.y; + // convert the image to blocks + // this groups same-colored pixels in the same column into a single block to reduce the block count + // any further pixel-grouping optimisations (eg 2D grouping) risk increasing conversion time higher than O(x*y) + for (int x = 0; x < img.width; x++) + { + BlockInfo qVoxel = new BlockInfo + { + block = BlockIDs.AbsoluteMathsBlock, // impossible canvas block + color = BlockColors.Default, + darkness = 10, + visible = false, + }; + float3 scale = new float3(1, 1, thiccness); + position.x += (float)(blockSize); + for (int y = 0; y < img.height; y++) + { + //position.y += (float)blockSize; + Color pixel = img.GetPixel(x, y); + BlockInfo qPixel = PixelUtility.QuantizePixel(pixel); + if (qPixel.darkness != qVoxel.darkness + || qPixel.color != qVoxel.color + || qPixel.visible != qVoxel.visible + || qPixel.block != qVoxel.block) + { + if (y != 0) + { + if (qVoxel.visible) + { + position.y = zero_y + (float)((y * blockSize + (y - scale.y) * blockSize) / 2); + Block.PlaceNew(qVoxel.block, position, color: qVoxel.color, darkness: qVoxel.darkness, scale: scale); + blockCount++; + } + scale = new float3(1, 1, thiccness); + } + qVoxel = qPixel; + } + else + { + scale.y += 1; + } + + } + if (qVoxel.visible) + { + position.y = zero_y + (float)((img.height * blockSize + (img.height - scale.y) * blockSize) / 2); + Block.PlaceNew(qVoxel.block, position, color: qVoxel.color, darkness: qVoxel.darkness, scale: scale); + blockCount++; + } + //position.y = zero_y; + } + Logging.CommandLog($"Placed {img.width}x{img.height} image beside you ({blockCount} blocks total)"); + Logging.MetaLog($"Saved {(img.width * img.height) - blockCount} blocks ({img.width * img.height / blockCount}x) while placing {filepath}"); + } + + public static void Pixelate2DFileToTextBlock(string filepath) + { + // Thanks to TheGreenGoblin for the idea (and the working Python implementation for reference) + // Load image file and convert to Gamecraft blocks + Texture2D img = new Texture2D(64, 64); + // load file into texture + try + { + byte[] imgData = File.ReadAllBytes(filepath); + img.LoadImage(imgData); + } + catch (Exception e) + { + Logging.CommandLogError($"Failed to load picture data. Reason: {e.Message}"); + Logging.MetaLog(e.Message + "\n" + e.StackTrace); + return; + } + float3 position = new Player(PlayerType.Local).Position; + position.x += 1f; + position.y += (float)blockSize; + string text = PixelUtility.TextureToString(img); + TextBlock textBlock = TextBlock.PlaceNew(position, scale: new float3(Mathf.Ceil(img.width / 16), Mathf.Ceil(img.height / 16), 1)); + textBlock.Text = text; + byte[] textHash; + using (HashAlgorithm hasher = SHA256.Create()) + textHash = hasher.ComputeHash(Encoding.UTF8.GetBytes(text)); + string textId = "Pixi_"; + // every byte converts to 2 hexadecimal characters so hash length needs to be halved + for (int i = 0; i < HASH_LENGTH/2 && i < textHash.Length; i++) + { + textId += textHash[i].ToString("X2"); + } + textBlock.TextBlockId = textId; + } + + public static void Pixelate2DFileToCommand(string filepath, string textBlockId) + { + // Thanks to Nullpersonan for the idea + // Load image file and convert to Gamecraft blocks + Texture2D img = new Texture2D(64, 64); + // load file into texture + try + { + byte[] imgData = File.ReadAllBytes(filepath); + img.LoadImage(imgData); + } + catch (Exception e) + { + Logging.CommandLogError($"Failed to load picture data. Reason: {e.Message}"); + Logging.MetaLog(e.Message + "\n" + e.StackTrace); + return; + } + float3 position = new Player(PlayerType.Local).Position; + position.x += 1f; + position.y += (float)blockSize; + float zero_y = position.y; + string text = PixelUtility.TextureToString(img); // conversion + ConsoleBlock console = ConsoleBlock.PlaceNew(position); + // set console's command + console.Command = "ChangeTextBlockCommand"; + console.Arg1 = textBlockId; + console.Arg2 = text; + console.Arg3 = ""; + } + } +} diff --git a/Pixi/Images/PixelUtility.cs b/Pixi/Images/PixelUtility.cs new file mode 100644 index 0000000..e66717d --- /dev/null +++ b/Pixi/Images/PixelUtility.cs @@ -0,0 +1,168 @@ +using System; +using System.Runtime.CompilerServices; +using System.Text; + +using UnityEngine; + +using GamecraftModdingAPI.Blocks; +using GamecraftModdingAPI.Utility; + +using Pixi.Common; + +namespace Pixi.Images +{ + public static class PixelUtility + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static BlockInfo QuantizePixel(Color pixel) + { + BlockColors color = BlockColors.Default; + int darkness = 0; + bool force = false; +#if DEBUG + Logging.MetaLog($"Color (r:{pixel.r}, g:{pixel.g}, b:{pixel.b})"); +#endif + if (Mathf.Abs(pixel.r - pixel.g) <= pixel.r * 0.1f && Mathf.Abs(pixel.r - pixel.b) <= pixel.r * 0.1f) + { + color = BlockColors.White; + darkness = (int)(10 - ((pixel.r + pixel.g + pixel.b) * 3.5)); + //Logging.MetaDebugLog($"Color (r:{pixel.r}, g:{pixel.g}, b:{pixel.b})"); + } + else if (pixel.r >= pixel.g && pixel.r >= pixel.b) + { + // Red is highest + if ((pixel.r - pixel.g) > pixel.r * 0.65 && (pixel.r - pixel.b) > pixel.r * 0.55) + { + // Red is much higher than other pixels + darkness = (int)(9 - (pixel.r * 8.01)); + color = BlockColors.Red; + } + else if ((pixel.g - pixel.b) > pixel.g * 0.25) + { + // Green is much higher than blue + if ((pixel.r - pixel.g) < pixel.r * 0.8) + { + darkness = (int)(10 - ((pixel.r * 2.1 + pixel.g) * 2.1)); + color = BlockColors.Orange; + } + else + { + darkness = (int)(10 - ((pixel.r * 2.1 + pixel.g) * 2.2)); + color = BlockColors.Yellow; + } + + } + else if ((pixel.b - pixel.g) > pixel.b * 0.3) + { + // Blue is much higher than green + darkness = (int)(10 - ((pixel.r + pixel.b) * 5.0)); + color = BlockColors.Purple; + } + else + { + // Green is close strength to blue + darkness = (int)(10 - ((pixel.r * 2.1 + pixel.g + pixel.b) * 2.5)); + color = darkness < 6 ? BlockColors.Pink : BlockColors.Orange; + force = true; + } + } + else if (pixel.g >= pixel.r && pixel.g >= pixel.b) + { + // Green is highest + if ((pixel.g - pixel.r) > pixel.g * 0.6 && (pixel.g - pixel.b) > pixel.g * 0.48) + { + // Green is much higher than other pixels + darkness = (int)(10 - (pixel.g * 10.1)); + color = BlockColors.Green; + } + else if ((pixel.r - pixel.b) > pixel.r * 0.3) + { + // Red is much higher than blue + darkness = (int)(10 - ((pixel.r + pixel.g) * 5.1)); + color = BlockColors.Yellow; + } + else if ((pixel.b - pixel.r) > pixel.b * 0.2) + { + // Blue is much higher than red + darkness = (int)(9 - ((pixel.g + pixel.b) * 5.1)); + color = BlockColors.Aqua; + } + else + { + // Red is close strength to blue + darkness = (int)(10 - ((pixel.r + pixel.g * 2.2 + pixel.b) * 2.9)); + color = BlockColors.Lime; + } + } + else if (pixel.b >= pixel.g && pixel.b >= pixel.r) + { + // Blue is highest + if ((pixel.b - pixel.g) > pixel.b * 0.6 && (pixel.b - pixel.r) > pixel.b * 0.6) + { + // Blue is much higher than other pixels + darkness = (int)(10 - (pixel.b * 10.1)); + color = BlockColors.Blue; + } + else if ((pixel.g - pixel.r) > pixel.g * 0.3) + { + // Green is much higher than red + darkness = (int)(10 - ((pixel.g + pixel.b) * 5.1)); + if (darkness == 4 || darkness == 5) darkness = 0; + else if (darkness < 3) darkness = 4; + color = BlockColors.Aqua; + } + else if ((pixel.r - pixel.g) > pixel.r * 0.3) + { + // Red is much higher than green + darkness = (int)(10 - ((pixel.r + pixel.b) * 5.0)); + color = BlockColors.Purple; + } + else + { + // Green is close strength to red + darkness = (int)(10 - ((pixel.r + pixel.g + pixel.b * 2.2) * 3.0)); + color = BlockColors.Aqua; + } + } + // level 9 is not darker than lvl 8 + if (darkness > 8 && !force) darkness = 8; + // darkness 0 is the most saturated (it's not just the lightest) + if (darkness < 0) darkness = 0; + + BlockInfo result = new BlockInfo + { + block = pixel.a > 0.75 ? BlockIDs.AluminiumCube : BlockIDs.GlassCube, + color = color, + darkness = (byte)darkness, + visible = pixel.a > 0.5f, + }; +#if DEBUG + Logging.MetaLog($"Quantized {color} (b:{result.block} d:{result.darkness} v:{result.visible})"); +#endif + return result; + } + + public static string HexPixel(Color pixel) + { + return "#"+ColorUtility.ToHtmlStringRGBA(pixel); + } + + public static string TextureToString(Texture2D img) + { + StringBuilder imgString = new StringBuilder(""); + for (int y = img.height-1; y >= 0 ; y--) // text origin is top right, but img origin is bottom right + { + for (int x = 0; x < img.width; x++) + { + Color pixel = img.GetPixel(x, y); + imgString.Append("\u25a0"); + } + imgString.Append("
"); + } + imgString.Append(""); + return imgString.ToString(); + } + } +} diff --git a/Pixi/Pixi.csproj b/Pixi/Pixi.csproj index e8e3808..8dcfb60 100644 --- a/Pixi/Pixi.csproj +++ b/Pixi/Pixi.csproj @@ -3,17 +3,13 @@ net472 true - 0.2.0 + 0.3.0 NGnius MIT https://git.exmods.org/NGnius/Pixi en-CA - - - - @@ -781,7 +777,33 @@ ..\..\ref\Gamecraft_Data\Managed\VisualProfiler.dll + + ..\..\ref\Plugins\GamecraftModdingAPI.dll + + + ..\..\ref\Plugins\GamecraftModdingAPI.dll + + + ..\..\ref\Plugins\GamecraftModdingAPI.dll + + + ..\..\ref\Plugins\GamecraftModdingAPI.dll + + + ..\..\ref\Plugins\GamecraftModdingAPI.dll + + + ..\..\ref\Plugins\GamecraftModdingAPI.dll + + + ..\..\ref\Plugins\GamecraftModdingAPI.dll + + + ..\..\ref\Plugins\GamecraftModdingAPI.dll + + + ..\..\ref\Plugins\GamecraftModdingAPI.dll + - diff --git a/Pixi/PixiPlugin.cs b/Pixi/PixiPlugin.cs index 15990c3..13680d7 100644 --- a/Pixi/PixiPlugin.cs +++ b/Pixi/PixiPlugin.cs @@ -13,23 +13,18 @@ using GamecraftModdingAPI.Utility; using GamecraftModdingAPI.Blocks; using GamecraftModdingAPI.Players; +using Pixi.Images; + namespace Pixi { public class PixiPlugin : IPlugin // the Illusion Plugin Architecture (IPA) will ignore classes that don't implement IPlugin' { - private const uint PIXEL_WARNING_THRESHOLD = 25_000; - public string Name { get; } = Assembly.GetExecutingAssembly().GetName().Name; // Pixi // To change the name, change the project's name public string Version { get; } = Assembly.GetExecutingAssembly().GetName().Version.ToString(); // 0.1.0 (for now) // To change the version, change #.#.# in Pixi.csproj - private uint width = 64; - private uint height = 64; - - private double blockSize = 0.2; - // called when Gamecraft shuts down public void OnApplicationQuit() { @@ -48,28 +43,11 @@ namespace Pixi // check out the modding API docs here: https://mod.exmods.org/ // Initialize Pixi mod - // create SimpleCustomCommandEngine for 2D image importing - SimpleCustomCommandEngine pixelate2DCommand = new SimpleCustomCommandEngine( - pixelate2DFile, // command action - "Pixi2D", // command name (used to invoke it in the console) - "Converts an image to blocks.\nLarger images will freeze your game until conversion completes. (Pixi)" // command description (displayed when help command is executed) - ); - - SimpleCustomCommandEngine pixelate3DCommand = new SimpleCustomCommandEngine( - pixelate3DFile, // command action - "Pixi3D", // command name (used to invoke it in the console) - "Converts a 3D model to blocks.\nLarger models will freeze your game until conversion completes. (Pixi)" // command description (displayed when help command is executed) - ); - - SimpleCustomCommandEngine scaleCommand = new SimpleCustomCommandEngine( - setScale, // command action - "PixiScale", // command name (used to invoke it in the console) - "Sets the image scale factor for Pixi2D.\nBigger images take longer to convert. (Pixi)" // command description (displayed when help command is executed) - ); - - // register commands so the modding API knows about it - CommandManager.AddCommand(pixelate2DCommand); - CommandManager.AddCommand(scaleCommand); + // 2D image functionality + ImageCommands.CreateThiccCommand(); + ImageCommands.CreateImportCommand(); + ImageCommands.CreateTextCommand(); + ImageCommands.CreateTextConsoleCommand(); GamecraftModdingAPI.Utility.Logging.LogDebug($"{Name} has started up"); } @@ -83,239 +61,5 @@ namespace Pixi public void OnLevelWasLoaded(int level) { } // called after a level is loaded public void OnUpdate() { } // called once per rendered frame (frame update) - - // pixelation methods - - private void pixelate2DFile(string filepath) - { - // Load image file and convert to Gamecraft blocks - Texture2D img = new Texture2D((int)width, (int)height); - // load file into texture - try - { - byte[] imgData = File.ReadAllBytes(filepath); - img.LoadImage(imgData); - } - catch (Exception e) - { - Logging.CommandLogError($"Failed to load picture data. Reason: {e.Message}"); - Logging.MetaLog(e.Message + "\n" + e.StackTrace); - return; - } - float3 position = new Player(PlayerType.Local).Position; - uint blockCount = 0; - position.x += 1f; - //position.y += 1f; - float zero_y = position.y; - // convert the image to blocks - // this groups same-colored pixels in the same column into a single block to reduce the block count - // any further pixel-grouping optimisations (eg 2D grouping) risk increasing conversion time higher than O(x*y) - for (int x = 0; x < width; x++) - { - QuantizedPixel qVoxel = new QuantizedPixel - { - block = BlockIDs.AbsoluteMathsBlock, // impossible canvas block - color = BlockColors.Default, - darkness = 10, - visible = false, - }; - float3 scale = new float3(1, 1, 1); - position.x += (float)(blockSize); - for (int y = 0; y < height; y++) - { - //position.y += (float)blockSize; - Color pixel = img.GetPixel(x, y); - QuantizedPixel qPixel = quantizeColor(pixel); - if (qPixel.darkness != qVoxel.darkness - || qPixel.color != qVoxel.color - || qPixel.visible != qVoxel.visible - || qPixel.block != qVoxel.block) - { - if (y != 0) - { - if (qVoxel.visible) - { - position.y = zero_y + (float)((y * blockSize + (y - scale.y) * blockSize) / 2); - Block.PlaceNew(qVoxel.block, position, color: qVoxel.color, darkness: qVoxel.darkness, scale: scale); - blockCount++; - } - scale = new float3(1, 1, 1); - } - qVoxel = qPixel; - } - else - { - scale.y += 1; - } - - } - if (qVoxel.visible) - { - position.y = zero_y + (float)((height * blockSize + (height - scale.y) * blockSize) / 2); - Block.PlaceNew(qVoxel.block, position, color: qVoxel.color, darkness: qVoxel.darkness, scale: scale); - blockCount++; - } - //position.y = zero_y; - } - Logging.CommandLog($"Placed {width}x{height} image beside you ({blockCount} blocks total)"); - Logging.MetaLog($"Saved {(width * height) - blockCount} blocks ({width * height / blockCount}x) while placing {filepath}"); - } - - private void setScale(uint _width, uint _height) - { - width = _width; - height = _height; - if (width * height > PIXEL_WARNING_THRESHOLD) - { - Logging.CommandLogWarning($"That's a lot of pixels ({width * height}px)!\nImporting large images may freeze your game for a long time."); - } - Logging.CommandLog($"Pixi image size set to {width}x{height}"); - } - - private void pixelate3DFile(string filepath) - { - // TODO? - Logging.CommandLogError("Oh no you found this command!\nCommand functionality not implemented (yet)"); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private QuantizedPixel quantizeColor(Color pixel) - { - BlockColors color = BlockColors.Default; - int darkness = 0; - bool force = false; -#if DEBUG - Logging.MetaLog($"Color (r:{pixel.r}, g:{pixel.g}, b:{pixel.b})"); -#endif - if (Mathf.Abs(pixel.r - pixel.g) <= pixel.r * 0.1f && Mathf.Abs(pixel.r - pixel.b) <= pixel.r * 0.1f) - { - color = BlockColors.White; - darkness = (int)(10 - ((pixel.r + pixel.g + pixel.b) * 3.5)); - //Logging.MetaDebugLog($"Color (r:{pixel.r}, g:{pixel.g}, b:{pixel.b})"); - } - else if (pixel.r >= pixel.g && pixel.r >= pixel.b) - { - // Red is highest - if ((pixel.r - pixel.g) > pixel.r * 0.65 && (pixel.r - pixel.b) > pixel.r * 0.55) - { - // Red is much higher than other pixels - darkness = (int)(9 - (pixel.r * 8.01)); - color = BlockColors.Red; - } - else if ((pixel.g - pixel.b) > pixel.g * 0.25) - { - // Green is much higher than blue - if ((pixel.r - pixel.g) < pixel.r * 0.8) - { - darkness = (int)(10 - ((pixel.r * 2.1 + pixel.g) * 2.1)); - color = BlockColors.Orange; - } - else - { - darkness = (int)(10 - ((pixel.r * 2.1 + pixel.g) * 2.2)); - color = BlockColors.Yellow; - } - - } - else if ((pixel.b - pixel.g) > pixel.b * 0.3) - { - // Blue is much higher than green - darkness = (int)(10 - ((pixel.r + pixel.b) * 5.0)); - color = BlockColors.Purple; - } - else - { - // Green is close strength to blue - darkness = (int)(10 - ((pixel.r * 2.1 + pixel.g + pixel.b) * 2.5)); - color = darkness < 6 ? BlockColors.Pink : BlockColors.Orange; - force = true; - } - } - else if (pixel.g >= pixel.r && pixel.g >= pixel.b) - { - // Green is highest - if ((pixel.g - pixel.r) > pixel.g * 0.6 && (pixel.g - pixel.b) > pixel.g * 0.48) - { - // Green is much higher than other pixels - darkness = (int)(10 - (pixel.g * 10.1)); - color = BlockColors.Green; - } - else if ((pixel.r - pixel.b) > pixel.r * 0.3) - { - // Red is much higher than blue - darkness = (int)(10 - ((pixel.r + pixel.g) * 5.1)); - color = BlockColors.Yellow; - } - else if ((pixel.b - pixel.r) > pixel.b * 0.2) - { - // Blue is much higher than red - darkness = (int)(9 - ((pixel.g + pixel.b) * 5.1)); - color = BlockColors.Aqua; - } - else - { - // Red is close strength to blue - darkness = (int)(10 - ((pixel.r + pixel.g * 2.2 + pixel.b) * 2.9)); - color = BlockColors.Lime; - } - } - else if (pixel.b >= pixel.g && pixel.b >= pixel.r) - { - // Blue is highest - if ((pixel.b - pixel.g) > pixel.b * 0.6 && (pixel.b - pixel.r) > pixel.b * 0.6) - { - // Blue is much higher than other pixels - darkness = (int)(10 - (pixel.b * 10.1)); - color = BlockColors.Blue; - } - else if ((pixel.g - pixel.r) > pixel.g * 0.3) - { - // Green is much higher than red - darkness = (int)(10 - ((pixel.g + pixel.b) * 5.1)); - if (darkness == 4 || darkness == 5) darkness = 0; - else if (darkness < 3) darkness = 4; - color = BlockColors.Aqua; - } - else if ((pixel.r - pixel.g) > pixel.r * 0.3) - { - // Red is much higher than green - darkness = (int)(10 - ((pixel.r + pixel.b) * 5.0)); - color = BlockColors.Purple; - } - else - { - // Green is close strength to red - darkness = (int)(10 - ((pixel.r + pixel.g + pixel.b * 2.2) * 3.0)); - color = BlockColors.Aqua; - } - } - // level 9 is not darker than lvl 8 - if (darkness > 8 && !force) darkness = 8; - // darkness 0 is the most saturated (it's not just the lightest) - if (darkness < 0) darkness = 0; - - QuantizedPixel result = new QuantizedPixel - { - block = pixel.a > 0.75 ? BlockIDs.AluminiumCube : BlockIDs.GlassCube, - color = color, - darkness = (byte)darkness, - visible = pixel.a > 0.5f, - }; -#if DEBUG - Logging.MetaLog($"Quantized {color} (b:{result.block} d:{result.darkness} v:{result.visible})"); -#endif - return result; - } - } - - internal struct QuantizedPixel - { - public BlockIDs block; - - public BlockColors color; - - public byte darkness; - - public bool visible; } } \ No newline at end of file diff --git a/Pixi/Robots/CubeUtility.cs b/Pixi/Robots/CubeUtility.cs new file mode 100644 index 0000000..dff02d2 --- /dev/null +++ b/Pixi/Robots/CubeUtility.cs @@ -0,0 +1,7 @@ +using System; +namespace Pixi.Robots +{ + public static class CubeUtility + { + } +} diff --git a/Pixi/Robots/RobotCommands.cs b/Pixi/Robots/RobotCommands.cs new file mode 100644 index 0000000..bdaabcf --- /dev/null +++ b/Pixi/Robots/RobotCommands.cs @@ -0,0 +1,7 @@ +using System; +namespace Pixi.Robots +{ + public static class RobotCommands + { + } +} diff --git a/README.md b/README.md index c5265f2..2f663fe 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,8 @@ Think of it like automatic pixel art. ## Installation -To install the Pixi mod, copy the build's `Pixi.dll` into the `Plugins` folder in Gamecraft's main folder. +To install the Pixi mod, copy `Pixi.dll` (from the latest release) into the `Plugins` folder in Gamecraft's main folder. +You'll also need [GamecraftModdingAPI](https://git.exmods.org/modtainers/GamecraftModdingAPI) installed and Gamecraft patched with [GCIPA](https://git.exmods.org/modtainers/GCIPA/releases). ## Usage