diff --git a/EXITCODE.cs b/EXITCODE.cs
index 19baba1..4d6e61a 100644
--- a/EXITCODE.cs
+++ b/EXITCODE.cs
@@ -9,9 +9,18 @@ namespace BuildPlate_Editor
     public enum EXITCODE : int
     {
         Normal = 0,
+
         OpenGL_LowVersion = 1,
-        World_Load_TextureArray = 4,
-        World_ReLoad_TextureArray = 5,
+
+        TexturesFile_Existance = 2, // doesn't exist
+        TexturesFile_Path = 3,
+
+        Failed_BuildPlate_Existance = 4, // doesn't exist
+        Failed_BuildPlate_Extension = 5,
+        Failed_BuildPlate_Parse = 6,
+
+        World_Load_TextureArray = 7,
+        World_ReLoad_TextureArray = 8,
         World_Render_Block = 10,
         World_Unknown = 20,
     }
diff --git a/Program.cs b/Program.cs
index e6f62d2..e72a02c 100644
--- a/Program.cs
+++ b/Program.cs
@@ -18,12 +18,8 @@ class Program
         public static Window Window;
         public static string baseDir;
 
-        static void Main(string[] args)
+        static int Main(string[] args)
         {
-            // this will run on console close
-            handler = new ConsoleEventDelegate(ConsoleEventCallback);
-            SetConsoleCtrlHandler(handler, true);
-
             // Get base path (.exe location)
             string myExecutable = Assembly.GetEntryAssembly().Location;
 
@@ -36,7 +32,7 @@ static void Main(string[] args)
                 Console.WriteLine("You can also do this for .plate and .plate64");
                 Console.WriteLine("Press any key to continue...");
                 Console.ReadKey(true);
-                return;
+                return (int)EXITCODE.Normal;
             }
 
             baseDir = Path.GetDirectoryName(myExecutable) + "\\";
@@ -72,7 +68,7 @@ static void Main(string[] args)
                 }
             }
             else
-                Console.WriteLine("Ok :(, I won't aks again");
+                Console.WriteLine("Ok :(, I won't ask again");
 
             File.WriteAllBytes(baseDir + "askedForDefault", new byte[0]);
 
@@ -82,21 +78,21 @@ static void Main(string[] args)
                 Console.WriteLine("texturesPath.txt doen't exist");
                 Console.WriteLine($"Extract mce resource pack 2 times and set path to {{path}}/textures/blocks/");
                 Console.ReadKey(true);
-                return;
+                return (int)EXITCODE.TexturesFile_Existance;
             }
             string texturesPath = File.ReadAllText(baseDir + "texturesPath.txt");
             if (texturesPath == string.Empty || !Directory.Exists(texturesPath)) {
                 Console.WriteLine($"path inside texturesPath.txt ({texturesPath}) doesn't exist");
                 Console.WriteLine($"Extract mce resource pack 2 times and set path to {{path}}/textures/blocks/");
                 Console.ReadKey(true);
-                return;
+                return (int)EXITCODE.TexturesFile_Path;
             }
             char lastChar = texturesPath[texturesPath.Length - 1];
             if (lastChar != '\\' && lastChar != '/')
                 texturesPath += '/';
             World.textureBasePath = texturesPath;
 #if DEBUG
-            World.targetFilePath = @"C:\Users\Tomas\Desktop\Project Earth\Api\data\buildplates\buildplate (1).json";
+            World.targetFilePath = @"C:\Users\Tomas\Desktop\Project Earth\Api\data\buildplates\c0eb3037-94c1-4a85-b0d7-7b8375c6f1b1.json";
             //World.targetFilePath = @"C:\Users\Tomas\Desktop\Project Earth\Api\data\buildplates\411398a6-5810-43a0-824c-27a9077a9ac3.json"; // fancy
             //World.targetFilePath = @"C:\Users\Tomas\Desktop\Project Earth\Api\data\buildplates\c0eb3037-94c1-4a85-b0d7-7b8375c6f1b1.json"; // redstone
             //World.targetFilePath = @"C:\Users\Tomas\Desktop\Project Earth\Api\data\buildplates\c0f771f0-a5d6-4acc-bd35-055e52548a20.json"; // igloo super fancy secret base
@@ -107,23 +103,24 @@ static void Main(string[] args)
             if (args != null && args.Length > 0 && File.Exists(string.Join(" ", args)) && string.Join(" ", args).Split('.').Length > 1) {
                 string truePath = new FileInfo(string.Join(" ", args)).FullName;
                 Console.WriteLine(truePath);
-                string extension = truePath.Split('.').Last();
-                if (extension == "json" || extension == "plate" || extension == "plate64") {
+                string _extension = truePath.Split('.').Last();
+                if (_extension == "json" || _extension == "plate" || _extension == "plate64") {
                     World.targetFilePath = truePath;
                     goto check;
                 }
             }
 
             string buildPlate = Console.ReadLine();
+            string extension = Path.GetExtension(buildPlate);
             if (!File.Exists(buildPlate)) {
                 Console.WriteLine($"build plate \"{buildPlate}\" doesn't exist");
                 Console.ReadKey(true);
-                return;
-            } else if (Path.GetExtension(buildPlate) == ".json" || Path.GetExtension(buildPlate) == ".plate" 
-                || Path.GetExtension(buildPlate) == ".plate64") {
+                return (int)EXITCODE.Failed_BuildPlate_Existance;
+            } else if (extension != ".json" && extension != ".plate" 
+                && extension != ".plate64") {
                 Console.WriteLine($"\"{Path.GetExtension(buildPlate)}\" isn't valid buildplate extension, valid: .json, .plate, .plate64");
                 Console.ReadKey(true);
-                return;
+                return (int)EXITCODE.Failed_BuildPlate_Extension;
             }
             World.targetFilePath = buildPlate;
 #endif
@@ -135,7 +132,7 @@ static void Main(string[] args)
                 Console.WriteLine($"Couldn't parse \"{World.targetFilePath}\", Make sure it's valid build plate file.");
                 Console.WriteLine("Press any key to exit...");
                 Console.ReadKey(true);
-                return;
+                return (int)EXITCODE.Failed_BuildPlate_Parse;
             }
 
             Window = new Window();
@@ -148,6 +145,8 @@ static void Main(string[] args)
                 else if (int.TryParse(version.Split(' ')[0].Split('.')[1], out int subVer) && mainVer == 4 && subVer < 5)
                     LowVersion();
             Window.Run(60d);
+
+            return (int)EXITCODE.Normal;
         }
 
         private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
@@ -167,19 +166,5 @@ private static void LowVersion()
             if (Console.ReadKey(true).Key != ConsoleKey.Enter)
                 Util.Exit(EXITCODE.OpenGL_LowVersion);
         }
-
-        // Handle on close event
-        static bool ConsoleEventCallback(int eventType)
-        {
-            if (eventType == 2) { // on exit
-                Util.Exit(EXITCODE.Normal);
-            }
-            return false;
-        }
-        static ConsoleEventDelegate handler;   // Keeps it from getting garbage collected
-                                               
-        private delegate bool ConsoleEventDelegate(int eventType);
-        [DllImport("kernel32.dll", SetLastError = true)]
-        private static extern bool SetConsoleCtrlHandler(ConsoleEventDelegate callback, bool add);
     }
 }
diff --git a/Util.cs b/Util.cs
index 6792531..3a6267c 100644
--- a/Util.cs
+++ b/Util.cs
@@ -252,6 +252,91 @@ public static Vector2 PixelToGL(Vector2i pos)
 
         public static char ToLower(this char c) => c.ToString().ToLower()[0];
 
+        public static void SetXLow(this Vector2[] array, float x)
+        {
+            for (int i = 0; i < array.Length; i++)
+                if (array[i].X < 0.5f)
+                    array[i] = new Vector2(x, array[i].Y);
+        }
+        public static void SetXHigh(this Vector2[] array, float x)
+        {
+            for (int i = 0; i < array.Length; i++)
+                if (array[i].X >= 0.5f)
+                    array[i] = new Vector2(x, array[i].Y);
+        }
+        public static void SetYLow(this Vector2[] array, float y)
+        {
+            for (int i = 0; i < array.Length; i++)
+                if (array[i].Y < 0.5f)
+                    array[i] = new Vector2(array[i].X, y);
+        }
+        public static void SetYHigh(this Vector2[] array, float y)
+        {
+            for (int i = 0; i < array.Length; i++)
+                if (array[i].Y >= 0.5f)
+                    array[i] = new Vector2(array[i].X, y);
+        }
+
+        public static void SetXLow(this Vector3[] array, float x)
+        {
+            for (int i = 0; i < array.Length; i++)
+                if (array[i].X < 0.5f)
+                    array[i] = new Vector3(x, array[i].Y, array[i].Z);
+        }
+        public static void SetXHigh(this Vector3[] array, float x)
+        {
+            for (int i = 0; i < array.Length; i++)
+                if (array[i].X >= 0.5f)
+                    array[i] = new Vector3(x, array[i].Y, array[i].Z);
+        }
+        public static void SetZLow(this Vector3[] array, float z)
+        {
+            for (int i = 0; i < array.Length; i++)
+                if (array[i].Z < 0.5f)
+                    array[i] = new Vector3(array[i].X, array[i].Y, z);
+        }
+        public static void SetZHigh(this Vector3[] array, float z)
+        {
+            for (int i = 0; i < array.Length; i++)
+                if (array[i].Z >= 0.5f)
+                    array[i] = new Vector3(array[i].X, array[i].Y, z);
+        }
+
+        public static void FlipX(this Vector2[] array)
+        {
+            float low = 0f;
+            float high = 1f;
+
+            for (int i = 0; i < array.Length; i++)
+                if (array[i].X < 0.5f)
+                    low = array[i].X;
+                else
+                    high = array[i].X;
+
+            for (int i = 0; i < array.Length; i++)
+                if (array[i].X < 0.5f)
+                    array[i] = new Vector2(high, array[i].Y);
+                else
+                    array[i] = new Vector2(low, array[i].Y);
+        }
+        public static void FlipY(this Vector2[] array)
+        {
+            float low = 0f;
+            float high = 1f;
+
+            for (int i = 0; i < array.Length; i++)
+                if (array[i].Y < 0.5f)
+                    low = array[i].Y;
+                else
+                    high = array[i].Y;
+
+            for (int i = 0; i < array.Length; i++)
+                if (array[i].Y < 0.5f)
+                    array[i] = new Vector2(array[i].X, high);
+                else
+                    array[i] = new Vector2(array[i].X, low);
+        }
+
         // Set Foregroun
         [DllImport("user32.dll")]
         [return: MarshalAs(UnmanagedType.Bool)]
diff --git a/VoxelData.cs b/VoxelData.cs
index d2b18b5..9f66b4e 100644
--- a/VoxelData.cs
+++ b/VoxelData.cs
@@ -412,30 +412,123 @@ public static class Rail
 		}
 		public static class Cobweb
         {
-			public static readonly Vector3[] verts = new Vector3[8] {
-				new Vector3(0.0f, 0.0f, 0.0f), // 0
-				new Vector3(1.0f, 0.0f, 0.0f), // 1
-				new Vector3(1.0f, 1.0f, 0.0f), // 2
-				new Vector3(0.0f, 1.0f, 0.0f), // 3
-				new Vector3(0.0f, 0.0f, 1.0f), // 4
-				new Vector3(1.0f, 0.0f, 1.0f), // 5
-				new Vector3(1.0f, 1.0f, 1.0f), // 6
-				new Vector3(0.0f, 1.0f, 1.0f) //  7
-			};
 			public static readonly int[,] tris = new int[,] {
 				{0, 3, 5, 6},
 				{5, 6, 0, 3},
 				{1, 2, 4, 7},
 				{4, 7, 1, 2},
 			};
-			/*public static readonly int[,] voxelTris = new int[6, 4] {
-				{0, 3, 1, 2}, // Back Face
-				{5, 6, 4, 7}, // Front Face
-				{3, 7, 2, 6}, // Top Face
-				{1, 5, 0, 4}, // Bottom Face
-				{4, 7, 0, 3}, // Left Face
-				{1, 2, 5, 6} // Right Face
-			};*/
+		}
+		public static class Redstone
+		{
+			public const float DefaultOffset = 0.01f;
+			public const float toDot = 0.3125f;
+			public const float toDot2 = 1f - toDot;
+			public static readonly Vector3[] verts = new Vector3[]
+			{
+				new Vector3(0.0f, DefaultOffset, 0.0f), // 0
+				new Vector3(1.0f, DefaultOffset, 0.0f), // 1
+				new Vector3(0.0f, DefaultOffset, 1.0f), // 2
+				new Vector3(1.0f, DefaultOffset, 1.0f), // 3
+			};
+			public static readonly Vector3[] vertsSide = new Vector3[]
+			{
+				new Vector3(DefaultOffset, 0.0f, 0.0f),
+				new Vector3(DefaultOffset, 1.0f, 0.0f),
+				new Vector3(DefaultOffset, 0.0f, 1.0f),
+				new Vector3(DefaultOffset, 1.0f, 1.0f)
+			};
+			public static readonly int[,] tris = new int[2, 4] {
+				{0, 2, 1, 3}, // Top Face
+				{1, 3, 0, 2}, // Bottom Face
+			};
+			public static readonly int[,] trisSide = new int[2, 4] {
+				{2, 3, 0, 1},
+				{0, 1, 2, 3},
+			};
+			public static readonly Vector2[] uvsSide = new Vector2[4] {
+				new Vector2(1.0f, 0.0f),
+				new Vector2(0.0f, 0.0f),
+				new Vector2(1.0f, 1.0f),
+				new Vector2(0.0f, 1.0f)
+			};
+		}
+		public static class Lever_Base
+		{
+			public const float X = 0.3125f;
+			public const float X2 = 1f - X;
+			public const float Y = 0f;
+			public const float Y2 = 0.1875f;
+			public const float Z = 0.25f;
+			public const float Z2 = 1f - Z;
+			public static readonly Vector3[] verts = new Vector3[8] {
+				new Vector3(X, Y, Z), // 0
+				new Vector3(X2, Y, Z), // 1
+				new Vector3(X2, Y2, Z), // 2
+				new Vector3(X, Y2, Z), // 3
+				new Vector3(X, Y, Z2), // 4
+				new Vector3(X2, Y, Z2), // 5
+				new Vector3(X2, Y2, Z2), // 6
+				new Vector3(X, Y2, Z2) //  7
+			};
+			public static readonly Vector2[,] uvs = new Vector2[3,4] { // Z Y X
+				{
+					new Vector2(X, Y),
+					new Vector2(X, Y2),
+					new Vector2(X2, Y),
+					new Vector2(X2, Y2)
+				},
+				{
+					new Vector2(X, Z),
+					new Vector2(X, Z2),
+					new Vector2(X2, Z),
+					new Vector2(X2, Z2)
+				},
+				{
+					new Vector2(Z, Y),
+					new Vector2(Z, Y2),
+					new Vector2(Z2, Y),
+					new Vector2(Z2, Y2)
+				},
+			};
+		}
+		public static class Lever_Top
+		{
+			public const float X = 0.4375f;
+			public const float X2 = 1f - X;
+			public const float Y = 0f;
+			public const float Y2 = 0.625f;
+			public const float Y3 = Y2 - 0.125f;
+			public static readonly Vector3[] verts = new Vector3[8] {
+				new Vector3(X, Y, X), // 0
+				new Vector3(X2, Y, X), // 1
+				new Vector3(X2, Y2, X), // 2
+				new Vector3(X, Y2, X), // 3
+				new Vector3(X, Y, X2), // 4
+				new Vector3(X2, Y, X2), // 5
+				new Vector3(X2, Y2, X2), // 6
+				new Vector3(X, Y2, X2) //  7
+			};
+			public static readonly Vector2[,] uvs = new Vector2[3, 4] { // Z Y X
+				{
+					new Vector2(X, Y),
+					new Vector2(X, Y2),
+					new Vector2(X2, Y),
+					new Vector2(X2, Y2)
+				},
+				{
+					new Vector2(X, Y3),
+					new Vector2(X, Y2),
+					new Vector2(X2, Y3),
+					new Vector2(X2, Y2)
+				},
+				{
+					new Vector2(X, Y),
+					new Vector2(X, Y2),
+					new Vector2(X2, Y),
+					new Vector2(X2, Y2)
+				},
+			};
 		}
 	}
 }
diff --git a/World.cs b/World.cs
index 2b0b438..a8f3548 100644
--- a/World.cs
+++ b/World.cs
@@ -10,6 +10,7 @@
 using System.Text;
 using System.Threading.Tasks;
 using SystemPlus;
+using SystemPlus.Utils;
 using static BuildPlate_Editor.Util;
 
 namespace BuildPlate_Editor
@@ -62,7 +63,6 @@ public static class World
             { "hay_block", new []{ "hay_block_side" } },
             { "melon_block", new []{ "melon_side" } },
             { "pumpkin", new []{ "pumpkin_side" } },
-            { "redstone_wire", new []{ "redstone_dust_line" } },
             { "quartz_block", new []{ "quartz_block_side" } }, // todo
             { "redstone_dust_dot", new []{ "redstone_dust_cross" } },
             { "golden_rail", new []{ "powered_rail" } },
@@ -101,6 +101,7 @@ public static class World
             { "unlit_redstone_torch", new []{ "redstone_torch_off" } },
             { "cocoa", new []{ "cocoa_stage_2" } },
             { "reeds", new []{ "reeds.tga" } },
+            { "lever", new []{ "cobblestone", "lever" } },
             { "smooth_stone", new []{ "stone_slab_top" } }, // maybe not idk
             // terracotta
             { "white_glazed_terracotta", new []{ "glazed_terracotta_white" } },
@@ -121,6 +122,7 @@ public static class World
             // button
             { "jungle_button", new []{ "planks_jungle" } },
             { "wooden_button", new []{ "planks_oak" } },
+            { "birch_button", new []{ "planks_birch" } },
         });
 
         public static readonly Dictionary<string, Func<int, string[]>> specialTextureLoad = new Dictionary<string, Func<int, string[]>>() // texture => { data, return final texture}
@@ -872,6 +874,34 @@ public static class World
                     }
                 }
             },
+            { "redstone_wire", (int data) =>
+                {
+                    int power = data & 0b_1111;
+                    string crossName = $"redstone_dust_cross_{power}.png";
+                    string lineName = $"redstone_dust_line_{power}.png";
+                    Vector3 multiplier = Vector3.Lerp(new Vector3(0.25f, 0f, 0f), new Vector3(1f, 0f, 0f), power / 15f);
+                    
+                    DirectBitmap cross = DirectBitmap.Load(textureBasePath + "redstone_dust_cross.png", false);
+                    DirectBitmap line = DirectBitmap.Load(textureBasePath + "redstone_dust_line.png", false);
+                    for (int i = 0; i < cross.Data.Length; i++)
+                    {
+                        Color c = Color.FromArgb(cross.Data[i]);
+                        cross.Data[i] = Color.FromArgb(c.A, (byte)(((c.R / 255f) * multiplier.X) * 255f), (byte)(((c.G / 255f) * multiplier.Y) * 255f),
+                            (byte)(((c.B / 255f) * multiplier.Z) * 255f)).ToArgb();
+			        }
+                    for (int i = 0; i < line.Data.Length; i++)
+                    {
+                        Color c = Color.FromArgb(line.Data[i]);
+                        line.Data[i] = Color.FromArgb(c.A, (byte)(((c.R / 255f) * multiplier.X) * 255f), (byte)(((c.G / 255f) * multiplier.Y) * 255f),
+                            (byte)(((c.B / 255f) * multiplier.Z) * 255f)).ToArgb();
+                    }
+                    cross.Bitmap.Save(textureBasePath + crossName);
+                    line.Bitmap.Save(textureBasePath + lineName);
+                    cross.Dispose();
+                    line.Dispose();
+                    return new [] { crossName, lineName };
+                }
+            },
             // doors
             { "iron_door", (int data) =>
                 {
@@ -1058,6 +1088,8 @@ public static class World
             { "water", 22 },
             { "rail", 25 },
             { "web", 27 },
+            { "redstone_wire", 28 },
+            { "lever", 29 },
         };
 
         public static readonly bool[] RendererIsFullBlockLookUp = new bool[]
@@ -1090,6 +1122,7 @@ public static class World
             false, // rail
             false, // rail other
             false,
+            false, // redstone wire
         };
 
         public delegate void RenderBlock(Vector3 pos, Vector3i cp/*chunk pos, pre multiplied*/, int[] tex, int data, ref List<Vertex> vertices, ref List<uint> triangles);
@@ -1627,13 +1660,10 @@ public static class World
                     }
                     else if (dir == 1) {
                         mat = Matrix3.CreateRotationY(1.5708f); // 90 degrees
-                        offset.Z += 1f;
                     } else if (dir == 2) {
                         mat = Matrix3.CreateRotationY(3.14159f); // 180 degrees
-                        offset.Z += 1f;
                     } else {
                         mat = Matrix3.CreateRotationY(4.71239f); // 270 degrees
-                        offset.X += 1f;
                     }
 
                     uint tex = (uint)texA[0];
@@ -1641,15 +1671,15 @@ public static class World
                     for (int p = 0; p < 6; p++) {
                         uint firstVertIndex = (uint)verts.Count;
                         if (p == 2 || p == 3) { // top/bottom
-                            verts.Add(new Vertex(pos + VoxelData.Repeater.verts[VoxelData.voxelTris[p, 0]] * mat + offset, VoxelData.voxelUvs[0], tex));
-                            verts.Add(new Vertex(pos + VoxelData.Repeater.verts[VoxelData.voxelTris[p, 1]] * mat + offset, VoxelData.voxelUvs[1], tex));
-                            verts.Add(new Vertex(pos + VoxelData.Repeater.verts[VoxelData.voxelTris[p, 2]] * mat + offset, VoxelData.voxelUvs[2], tex));
-                            verts.Add(new Vertex(pos + VoxelData.Repeater.verts[VoxelData.voxelTris[p, 3]] * mat + offset, VoxelData.voxelUvs[3], tex));
+                            verts.Add(new Vertex(pos + (VoxelData.Repeater.verts[VoxelData.voxelTris[p, 0]] + offset) * mat, VoxelData.voxelUvs[0], tex));
+                            verts.Add(new Vertex(pos + (VoxelData.Repeater.verts[VoxelData.voxelTris[p, 1]] + offset) * mat, VoxelData.voxelUvs[1], tex));
+                            verts.Add(new Vertex(pos + (VoxelData.Repeater.verts[VoxelData.voxelTris[p, 2]] + offset) * mat, VoxelData.voxelUvs[2], tex));
+                            verts.Add(new Vertex(pos + (VoxelData.Repeater.verts[VoxelData.voxelTris[p, 3]] + offset) * mat, VoxelData.voxelUvs[3], tex));
                         } else {
-                            verts.Add(new Vertex(pos + VoxelData.Repeater.verts[VoxelData.voxelTris[p, 0]] * mat + offset, VoxelData.Repeater.sideUvs[0], tex));
-                            verts.Add(new Vertex(pos + VoxelData.Repeater.verts[VoxelData.voxelTris[p, 1]] * mat + offset, VoxelData.Repeater.sideUvs[1], tex));
-                            verts.Add(new Vertex(pos + VoxelData.Repeater.verts[VoxelData.voxelTris[p, 2]] * mat + offset, VoxelData.Repeater.sideUvs[2], tex));
-                            verts.Add(new Vertex(pos + VoxelData.Repeater.verts[VoxelData.voxelTris[p, 3]] * mat + offset, VoxelData.Repeater.sideUvs[3], tex));
+                            verts.Add(new Vertex(pos + (VoxelData.Repeater.verts[VoxelData.voxelTris[p, 0]] + offset) * mat, VoxelData.Repeater.sideUvs[0], tex));
+                            verts.Add(new Vertex(pos + (VoxelData.Repeater.verts[VoxelData.voxelTris[p, 1]] + offset) * mat, VoxelData.Repeater.sideUvs[1], tex));
+                            verts.Add(new Vertex(pos + (VoxelData.Repeater.verts[VoxelData.voxelTris[p, 2]] + offset) * mat, VoxelData.Repeater.sideUvs[2], tex));
+                            verts.Add(new Vertex(pos + (VoxelData.Repeater.verts[VoxelData.voxelTris[p, 3]] + offset) * mat, VoxelData.Repeater.sideUvs[3], tex));
                         }
                         tris.Add(firstVertIndex);
                         tris.Add(firstVertIndex + 1);
@@ -1659,25 +1689,63 @@ public static class World
                         tris.Add(firstVertIndex + 3);
                     }
 
+                    // first torch
                     offset = -Vector3.One / 2f;
+                    offset.Z += 0.3125f;
+
                     for (int p = 0; p < 6; p++) {
                         uint firstVertIndex = (uint)verts.Count;
                         if (p == 2) { // top
-                            verts.Add(new Vertex(pos + VoxelData.Torch.verts[VoxelData.voxelTris[p, 0]] + offset, VoxelData.Torch.uVsTop[0], torchTex));
-                            verts.Add(new Vertex(pos + VoxelData.Torch.verts[VoxelData.voxelTris[p, 1]] + offset, VoxelData.Torch.uVsTop[1], torchTex));
-                            verts.Add(new Vertex(pos + VoxelData.Torch.verts[VoxelData.voxelTris[p, 2]] + offset, VoxelData.Torch.uVsTop[2], torchTex));
-                            verts.Add(new Vertex(pos + VoxelData.Torch.verts[VoxelData.voxelTris[p, 3]] + offset, VoxelData.Torch.uVsTop[3], torchTex));
+                            verts.Add(new Vertex(pos + (VoxelData.Torch.verts[VoxelData.voxelTris[p, 0]] + offset) * mat, VoxelData.Torch.uVsTop[0], torchTex));
+                            verts.Add(new Vertex(pos + (VoxelData.Torch.verts[VoxelData.voxelTris[p, 1]] + offset) * mat, VoxelData.Torch.uVsTop[1], torchTex));
+                            verts.Add(new Vertex(pos + (VoxelData.Torch.verts[VoxelData.voxelTris[p, 2]] + offset) * mat, VoxelData.Torch.uVsTop[2], torchTex));
+                            verts.Add(new Vertex(pos + (VoxelData.Torch.verts[VoxelData.voxelTris[p, 3]] + offset) * mat, VoxelData.Torch.uVsTop[3], torchTex));
                         } else if (p == 3) { // bottom
-                            verts.Add(new Vertex(pos + VoxelData.Torch.verts[VoxelData.voxelTris[p, 0]] + offset, VoxelData.Torch.uVsBottom[0], torchTex));
-                            verts.Add(new Vertex(pos + VoxelData.Torch.verts[VoxelData.voxelTris[p, 1]] + offset, VoxelData.Torch.uVsBottom[1], torchTex));
-                            verts.Add(new Vertex(pos + VoxelData.Torch.verts[VoxelData.voxelTris[p, 2]] + offset, VoxelData.Torch.uVsBottom[2], torchTex));
-                            verts.Add(new Vertex(pos + VoxelData.Torch.verts[VoxelData.voxelTris[p, 3]] + offset, VoxelData.Torch.uVsBottom[3], torchTex));
+                            verts.Add(new Vertex(pos + (VoxelData.Torch.verts[VoxelData.voxelTris[p, 0]] + offset) * mat, VoxelData.Torch.uVsBottom[0], torchTex));
+                            verts.Add(new Vertex(pos + (VoxelData.Torch.verts[VoxelData.voxelTris[p, 1]] + offset) * mat, VoxelData.Torch.uVsBottom[1], torchTex));
+                            verts.Add(new Vertex(pos + (VoxelData.Torch.verts[VoxelData.voxelTris[p, 2]] + offset) * mat, VoxelData.Torch.uVsBottom[2], torchTex));
+                            verts.Add(new Vertex(pos + (VoxelData.Torch.verts[VoxelData.voxelTris[p, 3]] + offset) * mat, VoxelData.Torch.uVsBottom[3], torchTex));
                         }
                         else {
-                            verts.Add(new Vertex(pos + VoxelData.Torch.verts[VoxelData.voxelTris[p, 0]] + offset, VoxelData.Torch.uVsSide[0], torchTex));
-                            verts.Add(new Vertex(pos + VoxelData.Torch.verts[VoxelData.voxelTris[p, 1]] + offset, VoxelData.Torch.uVsSide[1], torchTex));
-                            verts.Add(new Vertex(pos + VoxelData.Torch.verts[VoxelData.voxelTris[p, 2]] + offset, VoxelData.Torch.uVsSide[2], torchTex));
-                            verts.Add(new Vertex(pos + VoxelData.Torch.verts[VoxelData.voxelTris[p, 3]] + offset, VoxelData.Torch.uVsSide[3], torchTex));
+                            verts.Add(new Vertex(pos + (VoxelData.Torch.verts[VoxelData.voxelTris[p, 0]] + offset) * mat, VoxelData.Torch.uVsSide[0], torchTex));
+                            verts.Add(new Vertex(pos + (VoxelData.Torch.verts[VoxelData.voxelTris[p, 1]] + offset) * mat, VoxelData.Torch.uVsSide[1], torchTex));
+                            verts.Add(new Vertex(pos + (VoxelData.Torch.verts[VoxelData.voxelTris[p, 2]] + offset) * mat, VoxelData.Torch.uVsSide[2], torchTex));
+                            verts.Add(new Vertex(pos + (VoxelData.Torch.verts[VoxelData.voxelTris[p, 3]] + offset) * mat, VoxelData.Torch.uVsSide[3], torchTex));
+                        }
+                        tris.Add(firstVertIndex);
+                        tris.Add(firstVertIndex + 1);
+                        tris.Add(firstVertIndex + 2);
+                        tris.Add(firstVertIndex + 2);
+                        tris.Add(firstVertIndex + 1);
+                        tris.Add(firstVertIndex + 3);
+                    }
+
+                    // second torch
+                    offset = -Vector3.One / 2f;
+                    offset.Z += 0.0625f;
+
+                    int delay = (data & 0b_1100) >> 2;
+
+                    offset.Z -= 0.125f * delay;
+
+                    for (int p = 0; p < 6; p++) {
+                        uint firstVertIndex = (uint)verts.Count;
+                        if (p == 2) { // top
+                            verts.Add(new Vertex(pos + (VoxelData.Torch.verts[VoxelData.voxelTris[p, 0]] + offset) * mat, VoxelData.Torch.uVsTop[0], torchTex));
+                            verts.Add(new Vertex(pos + (VoxelData.Torch.verts[VoxelData.voxelTris[p, 1]] + offset) * mat, VoxelData.Torch.uVsTop[1], torchTex));
+                            verts.Add(new Vertex(pos + (VoxelData.Torch.verts[VoxelData.voxelTris[p, 2]] + offset) * mat, VoxelData.Torch.uVsTop[2], torchTex));
+                            verts.Add(new Vertex(pos + (VoxelData.Torch.verts[VoxelData.voxelTris[p, 3]] + offset) * mat, VoxelData.Torch.uVsTop[3], torchTex));
+                        } else if (p == 3) { // bottom
+                            verts.Add(new Vertex(pos + (VoxelData.Torch.verts[VoxelData.voxelTris[p, 0]] + offset) * mat, VoxelData.Torch.uVsBottom[0], torchTex));
+                            verts.Add(new Vertex(pos + (VoxelData.Torch.verts[VoxelData.voxelTris[p, 1]] + offset) * mat, VoxelData.Torch.uVsBottom[1], torchTex));
+                            verts.Add(new Vertex(pos + (VoxelData.Torch.verts[VoxelData.voxelTris[p, 2]] + offset) * mat, VoxelData.Torch.uVsBottom[2], torchTex));
+                            verts.Add(new Vertex(pos + (VoxelData.Torch.verts[VoxelData.voxelTris[p, 3]] + offset) * mat, VoxelData.Torch.uVsBottom[3], torchTex));
+                        }
+                        else {
+                            verts.Add(new Vertex(pos + (VoxelData.Torch.verts[VoxelData.voxelTris[p, 0]] + offset) * mat, VoxelData.Torch.uVsSide[0], torchTex));
+                            verts.Add(new Vertex(pos + (VoxelData.Torch.verts[VoxelData.voxelTris[p, 1]] + offset) * mat, VoxelData.Torch.uVsSide[1], torchTex));
+                            verts.Add(new Vertex(pos + (VoxelData.Torch.verts[VoxelData.voxelTris[p, 2]] + offset) * mat, VoxelData.Torch.uVsSide[2], torchTex));
+                            verts.Add(new Vertex(pos + (VoxelData.Torch.verts[VoxelData.voxelTris[p, 3]] + offset) * mat, VoxelData.Torch.uVsSide[3], torchTex));
                         }
                         tris.Add(firstVertIndex);
                         tris.Add(firstVertIndex + 1);
@@ -2067,6 +2135,252 @@ public static class World
                     }
                 }
             },
+            { 28, (Vector3 pos, Vector3i cp, int[] texA, int data, ref List<Vertex> vertices, ref List<uint> triangles) => // redstone wire
+                {
+                    uint crossTex = (uint)texA[0];
+                    uint wireTex = (uint)texA[1];
+
+                    Vector3i[] offsets = new Vector3i[4] { new Vector3i(-1, 0, 0), new Vector3i(1, 0, 0), new Vector3i(0, 0, -1), new Vector3i(0, 0, 1) };
+                    int[] presence = new int[4];
+                    int[] validRenderers = new int[] { 28, 14, 16, 23 };
+
+                    Vector3i iPos = (Vector3i)pos;
+
+                    for (int i = 0; i < 4; i++)
+                        if (GetRenderer(iPos + cp + offsets[i] + Vector3i.UnitY) == 28)
+                            presence[i] = 2;
+                        else if (GetRenderer(iPos + cp + offsets[i] - Vector3i.UnitY) == 28)
+                            presence[i] = 1;
+                        else {
+                            int r = GetRenderer(iPos + cp + offsets[i]);
+                            for (int j = 0; j < validRenderers.Length; j++)
+                                if (r == validRenderers[j]) {
+                                    presence[i] = 1;
+                                    break;
+                                }
+                        }
+
+                    // if connected to one side or 2 opposite sides, draw wire
+                    if (((presence[0] > 0 || presence[1] > 0) && presence[2] < 1 && presence[3] < 1)
+                        || ((presence[2] > 0 || presence[3] > 0) && presence[0] < 1 && presence[1] < 1)) {
+                        Vector3 offset = -Vector3.One / 2f;
+                        Matrix3 transform = Matrix3.Identity;
+                        if (presence[2] > 0 || presence[3] > 0)
+                            transform = Matrix3.CreateRotationY(1.5708f); // 90 degrees
+                        for (int p = 0; p < 2; p++) {
+                            uint firstVertIndex = (uint)vertices.Count;
+                            vertices.Add(new Vertex(pos + (VoxelData.Redstone.verts[VoxelData.Redstone.tris[p, 0]] + offset) * transform, VoxelData.voxelUvs[0], wireTex));
+                            vertices.Add(new Vertex(pos + (VoxelData.Redstone.verts[VoxelData.Redstone.tris[p, 1]] + offset) * transform, VoxelData.voxelUvs[1], wireTex));
+                            vertices.Add(new Vertex(pos + (VoxelData.Redstone.verts[VoxelData.Redstone.tris[p, 2]] + offset) * transform, VoxelData.voxelUvs[2], wireTex));
+                            vertices.Add(new Vertex(pos + (VoxelData.Redstone.verts[VoxelData.Redstone.tris[p, 3]] + offset) * transform, VoxelData.voxelUvs[3], wireTex));
+                            triangles.Add(firstVertIndex);
+                            triangles.Add(firstVertIndex + 1);
+                            triangles.Add(firstVertIndex + 2);
+                            triangles.Add(firstVertIndex + 2);
+                            triangles.Add(firstVertIndex + 1);
+                            triangles.Add(firstVertIndex + 3);
+                        }
+                    } else {
+                        Vector3[] verts = VoxelData.Redstone.verts.Cloned();
+                        Vector2[] uvs = VoxelData.voxelUvs.Cloned();
+
+                        // if not connected on a side, crop verts and uvs
+                        if (presence[0] < 1) {
+                            verts.SetXLow(VoxelData.Redstone.toDot);
+                            uvs.SetXLow(VoxelData.Redstone.toDot);
+                        }
+                        if (presence[1] < 1) {
+                            verts.SetXHigh(VoxelData.Redstone.toDot2);
+                            uvs.SetXHigh(VoxelData.Redstone.toDot2);
+                        }
+                        if (presence[2] < 1) {
+                            verts.SetZLow(VoxelData.Redstone.toDot);
+                            uvs.SetYLow(VoxelData.Redstone.toDot);
+                        }
+                        if (presence[3] < 1) {
+                            verts.SetZHigh(VoxelData.Redstone.toDot2);
+                            uvs.SetYHigh(VoxelData.Redstone.toDot2);
+                        }
+
+                        Vector3 offset = -Vector3.One / 2f;
+                        for (int p = 0; p < 2; p++) {
+                            uint firstVertIndex = (uint)vertices.Count;
+                            if (p == 1)
+                                uvs.FlipX();
+                            vertices.Add(new Vertex(pos + verts[VoxelData.Redstone.tris[p, 0]] + offset, uvs[0], crossTex));
+                            vertices.Add(new Vertex(pos + verts[VoxelData.Redstone.tris[p, 1]] + offset, uvs[1], crossTex));
+                            vertices.Add(new Vertex(pos + verts[VoxelData.Redstone.tris[p, 2]] + offset, uvs[2], crossTex));
+                            vertices.Add(new Vertex(pos + verts[VoxelData.Redstone.tris[p, 3]] + offset, uvs[3], crossTex));
+                            triangles.Add(firstVertIndex);
+                            triangles.Add(firstVertIndex + 1);
+                            triangles.Add(firstVertIndex + 2);
+                            triangles.Add(firstVertIndex + 2);
+                            triangles.Add(firstVertIndex + 1);
+                            triangles.Add(firstVertIndex + 3);
+                        }
+                    }
+
+                    // draw redstone on side of blocks
+                    for (int i = 0; i < presence.Length; i++)
+                    {
+                        if (presence[i] != 2)
+                            continue;
+                        Vector3 offset = -Vector3.One / 2f;
+                        Matrix3 transform = Matrix3.Identity;
+                        if (i == 1)
+                            transform = Matrix3.CreateRotationY(3.14159f); // 180 degrees
+                        else if (i == 2)
+                            transform = Matrix3.CreateRotationY(-1.5708f); // -90 degrees
+                        else if (i == 3)
+                            transform = Matrix3.CreateRotationY(1.5708f); // 90 degrees
+                        for (int p = 0; p < 2; p++) {
+                            uint firstVertIndex = (uint)vertices.Count;
+                            vertices.Add(new Vertex(pos + (VoxelData.Redstone.vertsSide[VoxelData.Redstone.trisSide[p, 0]] + offset) * transform, VoxelData.Redstone.uvsSide[0], wireTex));
+                            vertices.Add(new Vertex(pos + (VoxelData.Redstone.vertsSide[VoxelData.Redstone.trisSide[p, 1]] + offset) * transform, VoxelData.Redstone.uvsSide[1], wireTex));
+                            vertices.Add(new Vertex(pos + (VoxelData.Redstone.vertsSide[VoxelData.Redstone.trisSide[p, 2]] + offset) * transform, VoxelData.Redstone.uvsSide[2], wireTex));
+                            vertices.Add(new Vertex(pos + (VoxelData.Redstone.vertsSide[VoxelData.Redstone.trisSide[p, 3]] + offset) * transform, VoxelData.Redstone.uvsSide[3], wireTex));
+                            triangles.Add(firstVertIndex);
+                            triangles.Add(firstVertIndex + 1);
+                            triangles.Add(firstVertIndex + 2);
+                            triangles.Add(firstVertIndex + 2);
+                            triangles.Add(firstVertIndex + 1);
+                            triangles.Add(firstVertIndex + 3);
+                        }
+                    }
+                }
+            },
+            { 29, (Vector3 pos, Vector3i cp, int[] texA, int data, ref List<Vertex> verts, ref List<uint> tris) => // lever
+                {
+                    Vector3 offset = -Vector3.One / 2f;
+                    Matrix3 transform = Matrix3.Identity;
+                    uint stoneTex = (uint)texA[0];
+                    uint leverTex = (uint)texA[1];
+
+                    bool open = Convert.ToBoolean((data & 0b_1000) >> 3);
+                    int dir = data & 0b_0111;
+
+                    if (dir == 5 || dir == 6)
+                        transform = Matrix3.CreateRotationX(3.14159f); // 180 degrees
+                    
+                    if (dir == 1)
+                        transform *= Matrix3.CreateRotationX(1.5708f); // -90 degrees
+                    else if (dir == 2)
+                        transform *= Matrix3.CreateRotationX(-1.5708f); // 90 degrees
+                    else if (dir == 3)
+                        transform *= Matrix3.CreateRotationX(1.5708f); // 90 degrees
+                    else if (dir == 4)
+                        transform *= Matrix3.CreateRotationX(-1.5708f); // -90 degrees
+
+                    if (dir < 3 || dir == 6)
+                        transform *= Matrix3.CreateRotationY(1.5708f); // 90 degrees
+
+                    // when placed, facing towards player
+                    for (int p = 0; p < 6; p++) {
+                        uint firstVertIndex = (uint)verts.Count;
+                        int uvIndex = p / 2;
+                        verts.Add(new Vertex(pos + (VoxelData.Lever_Base.verts[VoxelData.voxelTris[p, 0]] + offset) * transform, VoxelData.Lever_Base.uvs[uvIndex, 0], stoneTex));
+                        verts.Add(new Vertex(pos + (VoxelData.Lever_Base.verts[VoxelData.voxelTris[p, 1]] + offset) * transform, VoxelData.Lever_Base.uvs[uvIndex, 1], stoneTex));
+                        verts.Add(new Vertex(pos + (VoxelData.Lever_Base.verts[VoxelData.voxelTris[p, 2]] + offset) * transform, VoxelData.Lever_Base.uvs[uvIndex, 2], stoneTex));
+                        verts.Add(new Vertex(pos + (VoxelData.Lever_Base.verts[VoxelData.voxelTris[p, 3]] + offset) * transform, VoxelData.Lever_Base.uvs[uvIndex, 3], stoneTex));
+                        tris.Add(firstVertIndex);
+                        tris.Add(firstVertIndex + 1);
+                        tris.Add(firstVertIndex + 2);
+                        tris.Add(firstVertIndex + 2);
+                        tris.Add(firstVertIndex + 1);
+                        tris.Add(firstVertIndex + 3);
+                    }
+
+                    if (dir >= 1 && dir <= 4) {
+                        transform = Matrix3.Identity;
+                        Vector3 offset2 = Vector3.Zero;
+
+                        if (open) {
+                            if (dir == 2 || dir == 4) {
+                                offset2.Y -= 0.25f;
+                                transform *= Matrix3.CreateRotationX(-0.5235f);
+                            } else {
+                                offset2.Y -= 0.25f;
+                                transform *= Matrix3.CreateRotationX(0.5235f);
+                            }
+                        }
+                        else {
+                            if (dir == 2 || dir == 4) {
+                                offset2.Y += 0.25f;
+                                transform *= Matrix3.CreateRotationX(0.5235f);
+                            } else {
+                                offset2.Y += 0.25f;
+                                transform *= Matrix3.CreateRotationX(-0.5235f);
+                            }
+                        }
+
+                        if (dir < 3)
+                            transform *= Matrix3.CreateRotationY(1.5708f); // 90 degrees
+
+                        if (dir == 1)
+                            transform *= Matrix3.CreateRotationZ(-1.5708f); // -90 degrees
+                        else if (dir == 2)
+                            transform *= Matrix3.CreateRotationZ(1.5708f); // 90 degrees
+                        else if (dir == 3)
+                            transform *= Matrix3.CreateRotationX(1.5708f); // 90 degrees
+                        else
+                            transform *= Matrix3.CreateRotationX(-1.5708f); // -90 degrees
+                        
+                        for (int p = 0; p < 6; p++) {
+                            uint firstVertIndex = (uint)verts.Count;
+                            int uvIndex = p / 2;
+                            verts.Add(new Vertex(pos + (VoxelData.Lever_Top.verts[VoxelData.voxelTris[p, 0]] + offset) * transform + offset2, VoxelData.Lever_Top.uvs[uvIndex, 0], leverTex));
+                            verts.Add(new Vertex(pos + (VoxelData.Lever_Top.verts[VoxelData.voxelTris[p, 1]] + offset) * transform + offset2, VoxelData.Lever_Top.uvs[uvIndex, 1], leverTex));
+                            verts.Add(new Vertex(pos + (VoxelData.Lever_Top.verts[VoxelData.voxelTris[p, 2]] + offset) * transform + offset2, VoxelData.Lever_Top.uvs[uvIndex, 2], leverTex));
+                            verts.Add(new Vertex(pos + (VoxelData.Lever_Top.verts[VoxelData.voxelTris[p, 3]] + offset) * transform + offset2, VoxelData.Lever_Top.uvs[uvIndex, 3], leverTex));
+                            tris.Add(firstVertIndex);
+                            tris.Add(firstVertIndex + 1);
+                            tris.Add(firstVertIndex + 2);
+                            tris.Add(firstVertIndex + 2);
+                            tris.Add(firstVertIndex + 1);
+                            tris.Add(firstVertIndex + 3);
+                        }
+                    } else {
+                        Vector3 offset2 = Vector3.Zero;
+                        if (open) {
+                            if (dir == 5)
+                                offset2.Z -= 0.25f;
+                            else
+                                offset2.Z += 0.25f;
+                            offset2 *= transform;
+                            transform = Matrix3.CreateRotationX(0.5235f);
+                        }
+                        else {
+                            if (dir == 5)
+                                offset2.Z += 0.25f;
+                            else
+                                offset2.Z -= 0.25f;
+                            offset2 *= transform;
+                            transform = Matrix3.CreateRotationX(-0.5235f);
+                        }
+
+                        if (dir == 0 || dir == 6)
+                            transform *= Matrix3.CreateRotationY(1.5708f); // 90 degrees
+
+                        if (dir == 5 || dir == 6)
+                            transform *= Matrix3.CreateRotationZ(3.14159f); // 180 degrees
+
+                        for (int p = 0; p < 6; p++) {
+                            uint firstVertIndex = (uint)verts.Count;
+                            int uvIndex = p / 2;
+                            verts.Add(new Vertex(pos + (VoxelData.Lever_Top.verts[VoxelData.voxelTris[p, 0]] + offset) * transform + offset2, VoxelData.Lever_Top.uvs[uvIndex, 0], leverTex));
+                            verts.Add(new Vertex(pos + (VoxelData.Lever_Top.verts[VoxelData.voxelTris[p, 1]] + offset) * transform + offset2, VoxelData.Lever_Top.uvs[uvIndex, 1], leverTex));
+                            verts.Add(new Vertex(pos + (VoxelData.Lever_Top.verts[VoxelData.voxelTris[p, 2]] + offset) * transform + offset2, VoxelData.Lever_Top.uvs[uvIndex, 2], leverTex));
+                            verts.Add(new Vertex(pos + (VoxelData.Lever_Top.verts[VoxelData.voxelTris[p, 3]] + offset) * transform + offset2, VoxelData.Lever_Top.uvs[uvIndex, 3], leverTex));
+                            tris.Add(firstVertIndex);
+                            tris.Add(firstVertIndex + 1);
+                            tris.Add(firstVertIndex + 2);
+                            tris.Add(firstVertIndex + 2);
+                            tris.Add(firstVertIndex + 1);
+                            tris.Add(firstVertIndex + 3);
+                        }
+                    }
+                }
+            },
         };
 
         public static BuildPlate plate;