427 lines
19 KiB
C#
427 lines
19 KiB
C#
using SpriteStacker.Properties;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Drawing;
|
|
using System.Drawing.Imaging;
|
|
using System.Linq;
|
|
using System.Runtime.Versioning;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using System.Windows.Forms;
|
|
|
|
namespace SpriteStacker
|
|
{
|
|
public enum RenderMode
|
|
{
|
|
Editing,
|
|
Rendering
|
|
}
|
|
public class RenderConfig
|
|
{
|
|
public float ShadowR = 0.0f;
|
|
public float ShadowG = 0.0f;
|
|
public float ShadowB = 0.0f;
|
|
public float ShadowA = 1.0f;
|
|
public float ModifyR = 0.0f;
|
|
public float ModifyG = 0.0f;
|
|
public float ModifyB = 0.0f;
|
|
public float ModifyA = 0.0f;
|
|
float[][] colorMatrixElements = {
|
|
new float[] {1.0f, 0, 0, 0, 0},//R
|
|
new float[] {0,1.0f, 0, 0, 0},//G
|
|
new float[] {0, 0, 1.0f, 0, 0},//B
|
|
new float[] {0, 0, 0, 1f, 0},
|
|
new float[] {0.0f, 0.0f, 0.0f, 0f, 1}};
|
|
public bool Interpolate = false;
|
|
public bool PixelPerfect = true;
|
|
|
|
public RenderConfig(float ShadowR, float ShadowG, float ShadowB, float ShadowA, float ModifyR, float ModifyG, float ModifyB, float ModifyA, bool Interpolate, bool PixelPerfect) {
|
|
this.ShadowR = ShadowR;
|
|
this.ShadowG = ShadowG;
|
|
this.ShadowB = ShadowB;
|
|
this.ShadowA = ShadowA;
|
|
this.ModifyR = ModifyR;
|
|
this.ModifyG = ModifyG;
|
|
this.ModifyB = ModifyB;
|
|
this.ModifyA = ModifyA;
|
|
this.Interpolate = Interpolate;
|
|
this.PixelPerfect = PixelPerfect;
|
|
colorMatrixElements = new float[][]{
|
|
new float[] { ShadowR, 0, 0, 0, 0 },
|
|
new float[] { 0, ShadowG, 0, 0, 0 },
|
|
new float[] { 0, 0, ShadowB, 0, 0 },
|
|
new float[] { 0, 0, 0, ShadowA, 0 },
|
|
new float[] { ModifyR, ModifyG, ModifyB, ModifyA, 1 }}
|
|
;
|
|
}
|
|
|
|
public float[][] GetColorMatrix() { return colorMatrixElements; }
|
|
}
|
|
public static class Config
|
|
{
|
|
public static Color BasePlateColour = Color.FromArgb(255, 255, 255, 255);
|
|
public static int BackgroundTile = 2;
|
|
public static List<Font> customFonts = new List<Font>();
|
|
public static int FontSize = 16;
|
|
public static int RenderIndex = 0;
|
|
public static RenderMode renderMode = RenderMode.Editing;
|
|
public static int RenderResolution = 1;
|
|
public static RenderConfig ShadowRenderConfig = new RenderConfig(0f,0f,0.15f,1.0f,0f,0f,0f,0f,false,true);
|
|
public static RenderConfig ShadowLayerConfig = new RenderConfig(1.0f, 1.0f, 1.0f, 1.0f, 0f, 0f, 0f, -0.6f, false, true);
|
|
public static int SunAngle = 0;
|
|
}
|
|
public class ColourPalette
|
|
{
|
|
int PaletteSize = 24;
|
|
public Rectangle Bounds;
|
|
Rectangle[] ColourTiles;
|
|
Color[] colors;
|
|
int TileSize = 2;
|
|
int hoverIndex = -1;
|
|
int SelectedIndex = 0;
|
|
public ColourPalette(int paletteSize, Color[] colors)
|
|
{
|
|
PaletteSize = paletteSize;
|
|
this.colors = colors;
|
|
}
|
|
public Color getSelectedColour() { return colors[SelectedIndex]; }
|
|
public void SelectColour()
|
|
{
|
|
if (hoverIndex != -1)
|
|
{
|
|
SelectedIndex = hoverIndex;
|
|
Camera.SelectedColor = colors[SelectedIndex];
|
|
}
|
|
}
|
|
public void CalculateSize()
|
|
{
|
|
Bounds = new Rectangle(0,Camera.Height/8, 7 * (int)(TileSize * Camera.scale), 5);
|
|
int HorizontalTiles = (int)(Bounds.Width / (TileSize * Camera.scale)) - 2;
|
|
ColourTiles = new Rectangle[PaletteSize];
|
|
int x = 0;
|
|
int y = 0;
|
|
for (int i = 0; i < PaletteSize; i++)
|
|
{
|
|
ColourTiles[i] = new Rectangle(Bounds.X + (int)Camera.scale + (int)((TileSize * Camera.scale * (x + 1)) / (HorizontalTiles)) + (int)(TileSize * Camera.scale * x),Bounds.Y + (int)(TileSize * Camera.scale * (y + 2)) / HorizontalTiles + (int)(TileSize * Camera.scale * y), (int)(TileSize * Camera.scale), (int)(TileSize * Camera.scale));
|
|
x++;
|
|
if (x >= HorizontalTiles)
|
|
{
|
|
x = 0;
|
|
y++;
|
|
}
|
|
if (y * (int)(TileSize * Camera.scale) >= Bounds.Height)
|
|
{
|
|
Bounds.Height = (y + 10) * (int)(TileSize * Camera.scale);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void SetHover(Point MouseLocation)
|
|
{
|
|
bool Hovering = false; ;
|
|
for (int i = 0; i < ColourTiles.Length; i++)
|
|
{
|
|
if (ColourTiles[i].Contains(MouseLocation))
|
|
{
|
|
Hovering = true;
|
|
hoverIndex = i;
|
|
}
|
|
}
|
|
if (!Hovering) hoverIndex = -1;
|
|
}
|
|
public void Draw(Graphics g)
|
|
{
|
|
g.ResetTransform();
|
|
g.FillRectangle(Brushes.White, Bounds);
|
|
g.DrawRectangle(new Pen(new SolidBrush(Color.Wheat), Camera.scale),Bounds);
|
|
|
|
for (int i = 0; i < PaletteSize; i++)
|
|
{
|
|
g.ResetTransform();
|
|
if (i == hoverIndex)
|
|
{
|
|
g.ScaleTransform(1.2f, 1.2f);
|
|
g.TranslateTransform(-(int)(ColourTiles[i].Left * 0.2), -(int)(ColourTiles[i].Top* 0.2));
|
|
}
|
|
g.FillRectangle(new SolidBrush(colors[i]), ColourTiles[i]);
|
|
g.DrawRectangle(new Pen(i == SelectedIndex ? Brushes.Blue : Brushes.Black), ColourTiles[i]);
|
|
|
|
}
|
|
}
|
|
}
|
|
public enum BrushType
|
|
{
|
|
Square,
|
|
Circle,
|
|
Spray
|
|
}
|
|
public static class Camera
|
|
{
|
|
public static float ViewAngle = 1.0f;
|
|
public static int CanvasWidth = 25;
|
|
public static int CanvasHeight = 25;
|
|
public static int BrushX = 0;
|
|
public static int BrushY = 0;
|
|
public static int LocX = 0;
|
|
public static int LocY = 50;
|
|
public static float Rotation = 0;
|
|
public static int BrushSize = 1;
|
|
public static float scale = 10;
|
|
public static int Width;
|
|
public static int Height;
|
|
public static int SelectedLayer = 0;
|
|
public static Color SelectedColor = Color.Yellow;
|
|
public static BrushType type = BrushType.Square;
|
|
public static double CircleIntensity =1;
|
|
public static int SprayIntensity =1;
|
|
public static int BrushIndex = 0;
|
|
public static bool EditMode = false;
|
|
|
|
public static int square(int a)
|
|
{
|
|
return a * a;
|
|
}
|
|
public static bool isWithinCircle(int x, int y, int radius, int a, int b)
|
|
{
|
|
if (radius <= 2) return true;
|
|
return square(x - a) + square(y - b) < square(radius)/ CircleIntensity;
|
|
}
|
|
}
|
|
public class Voxel
|
|
{
|
|
Color VoxelColor;
|
|
public Voxel(Color voxelColor)
|
|
{
|
|
VoxelColor = voxelColor;
|
|
}
|
|
public Color GetColor() { return VoxelColor; }
|
|
public Color[] GetShadedColors(int[] Shadows)
|
|
{
|
|
Color[] sides = new Color[6];
|
|
for (int i = 0; i < 6; i++)
|
|
{
|
|
sides[i] = Color.FromArgb(Math.Max(Math.Min(VoxelColor.R + Shadows[i], 255), 0), Math.Max(Math.Min(VoxelColor.G + Shadows[i], 255), 0), Math.Max(Math.Min(VoxelColor.B + Shadows[i], 255), 0));
|
|
}
|
|
return sides;
|
|
}
|
|
public Color[] GetShadedColors()
|
|
{
|
|
Color[] sides = new Color[6];
|
|
int[] Shadows = new int[6] { 0, 10, 0, 10, 20, -20 };
|
|
for (int i = 0; i < 6; i++)
|
|
{
|
|
sides[i] = Color.FromArgb(Math.Max(Math.Min(VoxelColor.R + Shadows[i], 255), 0), Math.Max(Math.Min(VoxelColor.G + Shadows[i], 255), 0), Math.Max(Math.Min(VoxelColor.B + Shadows[i], 255), 0));
|
|
}
|
|
return sides;
|
|
}
|
|
}
|
|
public class Model
|
|
{
|
|
public List<Bitmap> LayerImages = new List<Bitmap>();
|
|
List<Voxel[,]> ModelData = new List<Voxel[,]>();
|
|
public int Width;
|
|
public int Length;
|
|
public List<Rectangle> Bounds = new List<Rectangle>();
|
|
public Bitmap RenderedModel;
|
|
public Model(int Width, int Length)
|
|
{
|
|
this.Width = Width;
|
|
this.Length = Length;
|
|
RenderedModel = new Bitmap(Width * 2, Length * 2);
|
|
AddLayer();
|
|
}
|
|
public void SetCurrentLayer(int Layer)
|
|
{
|
|
if(Layer >= ModelData.Count)
|
|
{
|
|
AddLayers(Layer + 1 - ModelData.Count);
|
|
}
|
|
Camera.SelectedLayer = Layer;
|
|
}
|
|
public void AddLayers(int layerCount)
|
|
{
|
|
for (int i = 0; i < layerCount; i++)
|
|
{
|
|
AddLayer();
|
|
}
|
|
}
|
|
public void SetBitmapLayers(Bitmap[] images)
|
|
{
|
|
LayerImages = images.ToList();
|
|
ModelData.Clear();
|
|
Bounds.Clear();
|
|
for(int i = 0;i < LayerImages.Count; i++)
|
|
{
|
|
Width = LayerImages[i].Width;
|
|
Length = LayerImages[i].Height;
|
|
ModelData.Add(new Voxel[Width, Length]);
|
|
Bounds.Add(new Rectangle(new Point(-(int)(Width * Camera.scale) / 2, -(int)(Length * Camera.scale) / 2), new Size((int)(Width * Camera.scale), (int)(Length * Camera.scale))));
|
|
for (int x = 0; x < LayerImages[i].Width; x++)
|
|
{
|
|
for(int y = 0; y < LayerImages[i].Height; y++)
|
|
{
|
|
ModelData[i][x,y] = new Voxel(LayerImages[i].GetPixel(x,y));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
public void UpdateBounds()
|
|
{
|
|
for (int i = 0; i < ModelData.Count; i++)
|
|
{
|
|
Bounds[i] = new Rectangle(new Point(-(int)(Width * Camera.scale) / 2, -(int)(Length * Camera.scale) / 2), new Size((int)(Width * Camera.scale), (int)(Length * Camera.scale)));
|
|
}
|
|
}
|
|
public void AddLayer()
|
|
{
|
|
LayerImages.Add(new Bitmap(Width, Length));
|
|
ModelData.Add(new Voxel[Width,Length]);
|
|
Bounds.Add(new Rectangle(new Point(-(int)(Width * Camera.scale) / 2, -(int)(Length * Camera.scale) / 2), new Size((int)(Width * Camera.scale), (int)(Length * Camera.scale))));
|
|
Bitmap Render = new Bitmap(Width * 2, (Length + (int)(ModelData.Count * Camera.ViewAngle)) * 2);
|
|
}
|
|
public void RenderModel(Graphics R, int RenderResolution)
|
|
{
|
|
RenderResolution = Math.Max(RenderResolution, 1);
|
|
Bitmap Render = new Bitmap((Width * 2 * RenderResolution), (Length + (int)(ModelData.Count * Camera.ViewAngle)) * 2 * RenderResolution);
|
|
Bitmap Shadows = new Bitmap((Width * 2 * RenderResolution), (Length + (int)(ModelData.Count * Camera.ViewAngle)) * 2 * RenderResolution);
|
|
Graphics G = Graphics.FromImage(Render);
|
|
Graphics S = Graphics.FromImage(Shadows);
|
|
G.ResetTransform();
|
|
G.TranslateTransform(Render.Width/2, Render.Height/2);
|
|
G.RotateTransform(Camera.Rotation);
|
|
float AngleScale = Camera.ViewAngle * RenderResolution;
|
|
if (!Config.ShadowLayerConfig.Interpolate)
|
|
{
|
|
G.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None;
|
|
G.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
|
|
}
|
|
if (Config.ShadowLayerConfig.PixelPerfect) G.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.Half;
|
|
if (Config.ShadowRenderConfig.PixelPerfect)
|
|
{
|
|
S.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.Half;
|
|
}
|
|
if (!Config.ShadowRenderConfig.Interpolate)
|
|
{
|
|
S.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None;
|
|
S.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
|
|
}
|
|
ColorMatrix colorMatrix = new ColorMatrix(Config.ShadowRenderConfig.GetColorMatrix());
|
|
ImageAttributes imageAttributes = new ImageAttributes();
|
|
imageAttributes.SetColorMatrix(colorMatrix,ColorMatrixFlag.Default,ColorAdjustType.Bitmap);
|
|
for (int i = 0; i < ModelData.Count; i++)
|
|
{
|
|
S.ResetTransform();
|
|
S.TranslateTransform(Render.Width / 2 - ((i * RenderResolution / 1.25f) * (float)Math.Cos((Camera.Rotation + Config.SunAngle) * Math.PI / 180.0)), Render.Height / 2 - (((i / 1.25f) * (float)Math.Sin((Camera.Rotation + Config.SunAngle) * Math.PI / 180.0)) * AngleScale));
|
|
S.RotateTransform(Camera.Rotation);
|
|
S.DrawImage(LayerImages[i], new Rectangle(-(LayerImages[i].Width / 2) * RenderResolution, -(LayerImages[i].Height / 2) * RenderResolution, LayerImages[i].Width * RenderResolution, LayerImages[i].Height * RenderResolution)
|
|
, 0, 0, LayerImages[i].Width, LayerImages[i].Height, GraphicsUnit.Pixel, imageAttributes);
|
|
}
|
|
colorMatrix = new ColorMatrix(Config.ShadowLayerConfig.GetColorMatrix());
|
|
imageAttributes = new ImageAttributes();
|
|
imageAttributes.SetColorMatrix(colorMatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);
|
|
G.ResetTransform();
|
|
G.DrawImage(Shadows, new Rectangle(0,0,Shadows.Width,Shadows.Height),0,0,Shadows.Width,Shadows.Height,GraphicsUnit.Pixel,imageAttributes);
|
|
for (int i = 0; i < ModelData.Count; i++)
|
|
{
|
|
G.ResetTransform();
|
|
G.TranslateTransform(Render.Width / 2, Render.Height / 2 - (i * AngleScale));
|
|
G.RotateTransform(Camera.Rotation);
|
|
G.DrawImage(LayerImages[i], new Rectangle(-(LayerImages[i].Width / 2) * RenderResolution, -(LayerImages[i].Height / 2) * RenderResolution, LayerImages[i].Width * RenderResolution, LayerImages[i].Height * RenderResolution));
|
|
}
|
|
int X = Camera.LocX + Camera.Width / 2;
|
|
int Y = Camera.Height / 2 + Camera.LocY;
|
|
R.ResetTransform();
|
|
R.TranslateTransform(X, Y);
|
|
R.RotateTransform(Camera.Rotation);
|
|
R.FillRectangle(new SolidBrush(Config.BasePlateColour), Bounds[0]);
|
|
int difference = Render.Height - Render.Width;
|
|
R.ResetTransform();
|
|
R.TranslateTransform(X, Y);
|
|
R.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
|
|
R.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.Half;
|
|
R.DrawImage(Render,( -Render.Width / 2)/ RenderResolution * Camera.scale, (-(Render.Width + difference) / 2)/ RenderResolution * Camera.scale, Render.Width/ RenderResolution * Camera.scale, Render.Height/ RenderResolution * Camera.scale);
|
|
RenderedModel = Render;
|
|
}
|
|
public void DrawSpriteStack(Graphics G)
|
|
{
|
|
int X = Camera.LocX + Camera.Width / 2;
|
|
int Y = Camera.Height / 2 + Camera.LocY;
|
|
G.ResetTransform();
|
|
G.TranslateTransform(X, Y);
|
|
G.RotateTransform(Camera.Rotation);
|
|
G.FillRectangle(new SolidBrush(Config.BasePlateColour), Bounds[0]);
|
|
float AngleScale = Camera.ViewAngle * Camera.scale;
|
|
G.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
|
|
G.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.Half;
|
|
for (int i = 0; i < ModelData.Count; i++)
|
|
{
|
|
if (!Camera.EditMode || Camera.EditMode && i != Camera.SelectedLayer)
|
|
{
|
|
G.ResetTransform();
|
|
G.TranslateTransform(X, Y - (i * AngleScale));
|
|
G.RotateTransform(Camera.Rotation);
|
|
if (Camera.SelectedLayer == i)
|
|
{
|
|
TextureBrush TileBrush = new TextureBrush(Resources.Background);
|
|
TileBrush.ScaleTransform((1/32.0f) * Camera.scale * (Config.BackgroundTile / 2), (1 / 32.0f) * Camera.scale * (Config.BackgroundTile / 2));
|
|
G.FillRectangle(TileBrush, Bounds[Camera.SelectedLayer]);
|
|
G.DrawRectangle(new Pen(Brushes.Black), Bounds[i]);
|
|
G.DrawRectangle(new Pen(new SolidBrush(Camera.SelectedColor)), new Rectangle(new Point(-(int)(Width * Camera.scale) / 2 + (int)(Camera.BrushX * Camera.scale) - (int)(Math.Floor(Camera.BrushSize / 2.0) * Camera.scale), -(int)(Length * Camera.scale) / 2 + (int)(Camera.BrushY * Camera.scale) - (int)(Math.Floor(Camera.BrushSize / 2.0) * Camera.scale)), new Size((int)(Camera.BrushSize * Camera.scale), (int)(Camera.BrushSize * Camera.scale))));
|
|
}
|
|
G.DrawImage(LayerImages[i], Bounds[i]);
|
|
}
|
|
}
|
|
if (Camera.EditMode)
|
|
{
|
|
G.ResetTransform();
|
|
G.TranslateTransform(X, Y);
|
|
TextureBrush TileBrush = new TextureBrush(Resources.OpaqueBackground);
|
|
TileBrush.ScaleTransform((1 / 32.0f) * Camera.scale * (Config.BackgroundTile / 2), (1 / 32.0f) * Camera.scale * (Config.BackgroundTile / 2));
|
|
G.FillRectangle(TileBrush, Bounds[Camera.SelectedLayer]);
|
|
G.DrawImage(LayerImages[Camera.SelectedLayer], Bounds[Camera.SelectedLayer]);
|
|
G.DrawRectangle(new Pen(Brushes.Black), Bounds[Camera.SelectedLayer]);
|
|
G.DrawRectangle(new Pen(new SolidBrush(Camera.SelectedColor)), new Rectangle(new Point(-(int)(Width * Camera.scale) / 2 + (int)(Camera.BrushX * Camera.scale) - (int)(Math.Floor(Camera.BrushSize / 2.0) * Camera.scale), -(int)(Length * Camera.scale) / 2 + (int)(Camera.BrushY * Camera.scale) - (int)(Math.Floor(Camera.BrushSize / 2.0) * Camera.scale)), new Size((int)(Camera.BrushSize * Camera.scale), (int)(Camera.BrushSize * Camera.scale))));
|
|
|
|
}
|
|
}
|
|
public void DrawSelectedLayerToImage()
|
|
{
|
|
Bitmap newImage = new Bitmap(Width, Length);
|
|
for (int x = 0; x < ModelData[Camera.SelectedLayer].GetLength(0); x++)
|
|
{
|
|
for (int y = 0; y < ModelData[Camera.SelectedLayer].GetLength(1); y++)
|
|
{
|
|
if (ModelData[Camera.SelectedLayer][x, y] != null)
|
|
{
|
|
newImage.SetPixel(x, y, ModelData[Camera.SelectedLayer][x, y].GetColor());
|
|
}
|
|
}
|
|
}
|
|
LayerImages[Camera.SelectedLayer] = newImage;
|
|
}
|
|
public void DrawLayersToImages()
|
|
{
|
|
for (int i = 0; i < ModelData.Count; i++)
|
|
{
|
|
Bitmap newImage = new Bitmap(Width, Length);
|
|
for (int x = 0; x < ModelData[i].GetLength(0); x++)
|
|
{
|
|
for (int y = 0; y < ModelData[i].GetLength(1); y++)
|
|
{
|
|
if (ModelData[i][x, y] != null)
|
|
{
|
|
newImage.SetPixel(x, y, ModelData[i][x, y].GetColor());
|
|
}
|
|
}
|
|
}
|
|
LayerImages[i] = newImage;
|
|
}
|
|
}
|
|
public void SetData(int LocX, int LocY, int Layer, Voxel voxel)
|
|
{
|
|
if (Layer >= ModelData.Count) AddLayer();
|
|
ModelData[Layer][LocX,LocY] = voxel;
|
|
}
|
|
}
|
|
}
|