diff --git a/VoxelIsometricRenderer/Terrain4.Designer.cs b/VoxelIsometricRenderer/Terrain4.Designer.cs index fc83a7d..3916c32 100644 --- a/VoxelIsometricRenderer/Terrain4.Designer.cs +++ b/VoxelIsometricRenderer/Terrain4.Designer.cs @@ -34,7 +34,7 @@ // // Display // - this.Display.BackColor = System.Drawing.Color.Aquamarine; + this.Display.BackColor = System.Drawing.Color.Black; this.Display.Location = new System.Drawing.Point(12, 12); this.Display.Name = "Display"; this.Display.Size = new System.Drawing.Size(100, 50); diff --git a/VoxelIsometricRenderer/Terrain4.cs b/VoxelIsometricRenderer/Terrain4.cs index 3b36c34..bcb3bd5 100644 --- a/VoxelIsometricRenderer/Terrain4.cs +++ b/VoxelIsometricRenderer/Terrain4.cs @@ -7,6 +7,7 @@ using System.Drawing; using System.Drawing.Drawing2D; using System.Linq; using System.Linq.Expressions; +using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -23,6 +24,8 @@ namespace VoxelIsometricRenderer public static float scale = 2f; public static int FPS = 0; public static int Frames = 0; + public static double TotalRamUsage = 0; + public static double RamUsage = 0; public Terrain4() { InitializeComponent(); @@ -49,36 +52,19 @@ namespace VoxelIsometricRenderer TextureManager.AddBitmap(Resources.PinkSquare); TextureManager.AddBitmap(Resources.OrangeSquare); TextureManager.AddBitmap(Resources.ColourEdges); - Voxels.Add(new Voxel(0, 0, 0, 0, 0, 0)); - Voxels.Add(new Voxel(9, 9, 9, 9, 9, 9)); - Voxels.Add(new Voxel(10, 10, 10, 10, 10, 10)); - Voxels.Add(new Voxel(11, 11, 11, 11, 11, 11)); - Voxels.Add(new Voxel(12, 12, 12, 12, 12, 12)); - Voxels.Add(new Voxel(3, 3, 3, 3, 3, 3)); - Voxels.Add(new Voxel(1, 1, 1, 1, 2, 3)); - Voxels.Add(new Voxel(4, 4, 4, 4, 6, 6)); - Voxels.Add(new Voxel(5, 5, 5, 5, 6, 6)); - Voxels.Add(new Voxel(7, 7, 7, 7, 7, 7)); - Voxels.Add(new Voxel(17, 18, 19, 20, 21, 22)); - Voxels.Add(new Voxel(23, 23, 23, 23, 23, 23)); - Voxels.Add(new Voxel(17, 18, 19, 20, -1, -1)); - Voxels.Add(new Voxel(23, 23, 23, 23, -1, -1)); - Voxels.Add(new Voxel(-1, -1, -1, -1, 21, 22)); - Voxels.Add(new Voxel(-1, -1, -1, -1, 23, 23)); - Voxels.Add(new Voxel(1, 13, 13, 13, 13, -1, -1)); - Voxels.Add(new Voxel(1, 14, 14, 14,14, -1, -1)); - Voxels.Add(new Voxel(1, 15, 15, 15,15, -1, -1)); - Voxels.Add(new Voxel(1, 16, 16, 16,16, -1, -1)); - Random rnd = new Random(); - for(int i = 0; i < 25; i++) - { - Voxels.Add(new Voxel(rnd.Next(0,TextureManager.textures.Count - 1), rnd.Next(0, TextureManager.textures.Count - 1), rnd.Next(0, TextureManager.textures.Count - 1), rnd.Next(0, TextureManager.textures.Count - 1), rnd.Next(0, TextureManager.textures.Count - 1), rnd.Next(0, TextureManager.textures.Count - 1))); - } TextureManager.GenerateVoxelFaces(); - for (int i = 0; i < Voxels.Count; i++) - { - //TextureManager.CreateModel(Voxels[i]); + Chunk TestChunk = ChunkRegistry.IndexChunk(0, 0, 0); + Random rnd = new Random(); + for (int i = 0; i < 16; i++) { + for (int j = 0; j < 16; j++) { + for (int k = 0; k < 16; k++) { + if (i < 10 && k < 10 && rnd.Next(0, 3) > 0) TestChunk.CreateBlock(new int[] { rnd.Next(0, 13), rnd.Next(0, 13), rnd.Next(0, 13), rnd.Next(0, 13), rnd.Next(0, 13), rnd.Next(0, 13) }, 0, new BlockState(false, BlockType.Ground, true), i, j, k); + else TestChunk.CreateAir(i, j, k); + } + } } + TestChunk.GenerateLightMap(); + TestChunk.CalculateCull(); MainThread = new Thread(Runtime); MainThread.SetApartmentState(ApartmentState.STA); MainThread.Start(); @@ -97,28 +83,16 @@ namespace VoxelIsometricRenderer { LightingFrameTime = Process.GetCurrentProcess().TotalProcessorTime.TotalMilliseconds; LightingManager.UpdateLighting(); - LightingManager.sunAngles[0] += 5f; - LightingManager.sunAngles[1] += 1f; + //LightingManager.sunAngles[0] += 5f; + // LightingManager.sunAngles[1] += 1f; } if (Process.GetCurrentProcess().TotalProcessorTime.TotalMilliseconds - SecondFrameTime > 1000) { + TotalRamUsage = Math.Round(System.Diagnostics.Process.GetCurrentProcess().WorkingSet64 / 1000000.0); + RamUsage = Math.Round(GC.GetTotalMemory(false) / 1000000.0); SecondFrameTime = Process.GetCurrentProcess().TotalProcessorTime.TotalMilliseconds; FPS = Frames; Frames = 0; } - if (up && LightingManager.AmbientColouring[0] < 1.5f) - { - //LightingManager.AmbientColouring[0] += 0.01f; - //LightingManager.AmbientColouring[1] += 0.01f; - //LightingManager.AmbientColouring[2] += 0.01f; - } - else if (up) up = false; - else if (LightingManager.AmbientColouring[0] > 0.5f) - { - //LightingManager.AmbientColouring[0] -= 0.01f; - //LightingManager.AmbientColouring[1] -= 0.01f; - //LightingManager.AmbientColouring[2] -= 0.01f; - } - else up = true; } } private void Draw(object sender, PaintEventArgs e) @@ -127,20 +101,9 @@ namespace VoxelIsometricRenderer g.PixelOffsetMode = PixelOffsetMode.Half; g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor; g.ScaleTransform(scale, scale); - int x = 0; - int y = 0; - foreach(Voxel voxel in Voxels) - { - voxel.Draw(g,2 + x,2 + y); - x += 36; - if ((x+32) * scale > Width) - { - y += 36; - x = 0; - } - } + ChunkRegistry.RenderChunk(g, 0); g.ResetTransform(); - g.DrawString(String.Format("FPS: {0}", FPS), SystemFonts.DefaultFont, Brushes.Red, new PointF(0, 0)); + g.DrawString(String.Format("FPS: {0} Ram Usage: {9}MB Ram Allocated: {8}MB\nSunValue: {1}\nMoonValue:{3}\nLightDirection:{4}\nAmbientColourGrading: R{5} G{6} B{7}\nSunAngles:{2}", FPS, LightingManager.SkyLightMetrics[0], String.Format(" x={0}° y={1}°", LightingManager.sunAngles[0], LightingManager.sunAngles[1]), LightingManager.SkyLightMetrics[2], LightingManager.SunAngle, LightingManager.SkyLightMetrics[3], LightingManager.SkyLightMetrics[4], LightingManager.SkyLightMetrics[5], TotalRamUsage, RamUsage), SystemFonts.DefaultFont, Brushes.Red, new PointF(0, 0)); Frames++; } public void ResizeDisplay() @@ -164,6 +127,22 @@ namespace VoxelIsometricRenderer { if (e.KeyCode == Keys.Up) scale += 0.25f; if (e.KeyCode == Keys.Down) scale -= 0.25f; + if (e.KeyCode == Keys.W) + { + LightingManager.sunAngles[1] += 10f; + } + if (e.KeyCode == Keys.S) + { + LightingManager.sunAngles[1] -= 10f; + } + if (e.KeyCode == Keys.A) + { + LightingManager.sunAngles[0] += 10f; + } + if (e.KeyCode == Keys.D) + { + LightingManager.sunAngles[0] += 10f; + } } private void KeyUpEvent(object sender, KeyEventArgs e) diff --git a/VoxelIsometricRenderer/Voxel.cs b/VoxelIsometricRenderer/Voxel.cs index a3d84ec..602bb41 100644 --- a/VoxelIsometricRenderer/Voxel.cs +++ b/VoxelIsometricRenderer/Voxel.cs @@ -3,10 +3,14 @@ using System.Collections.Generic; using System.Drawing; using System.Drawing.Imaging; using System.Linq; +using System.Runtime.InteropServices; +using System.Runtime.Remoting.Messaging; using System.Security.AccessControl; using System.Security.Policy; using System.Text; using System.Threading.Tasks; +using System.Windows.Forms; +using System.Windows.Forms.VisualStyles; using VoxelIsometricRenderer.Properties; namespace VoxelIsometricRenderer @@ -22,19 +26,163 @@ namespace VoxelIsometricRenderer SouthWest, South } + public enum LightShape + { + Cylinder, + Sphere, + Cube, + Cuboid + } + public class LightSource + { + int RangeX; + int RangeY; + int RangeZ; + int XWorldPos; + int YWorldPos; + int ZWorldPos; + LightShape Shape; + byte[,,] LightLevels; + float[] LightLevelModifier = new float[] { 1.5f, 1.5f, 1.5f }; + int Brightness = 64; + public static bool IsWithinCircle(int Radius, int CentreX, int CentreY, int posX, int PosY) + { + return LightingManager.Square(posX - CentreX) + LightingManager.Square(PosY - CentreY) < LightingManager.Square(Radius); + } + public static bool IsWithinSphere(int Radius, int CentreX, int CentreY, int CentreZ, int posX, int PosY, int PosZ) + { + return LightingManager.Square(posX - CentreX) + LightingManager.Square(PosY - CentreY) + LightingManager.Square(PosZ - CentreZ) < LightingManager.Square(Radius); + } + public LightSource(LightShape shape, int RangeX, int RangeY, int RangeZ, int XWorldPos, int YWorldPos, int ZWorldPos,int Brightness) + { + this.Shape = shape; + this.RangeX = RangeX; + this.RangeY = RangeY; + this.RangeZ = RangeZ; + this.XWorldPos = XWorldPos; + this.YWorldPos = YWorldPos; + this.ZWorldPos = ZWorldPos; + this.Brightness = Brightness; + SetUpLightLevels(); + } + public byte CalculateLightLevel(int CentreX, int CentreY, int CentreZ, int PosX, int PosY, int PosZ, int RangeX, int RangeY, int RangeZ, int MaxLevel) + { + int[] Distances = new int[] { CentreX - PosX, CentreY - PosY, CentreZ - PosZ}; + int[] Centres = new int[] { CentreX, CentreY, CentreZ }; + float[] LightLevel = new float[] {255.0f/ RangeX * (MaxLevel / 255.0f), 255.0f/ RangeY * (MaxLevel / 255.0f), 255.0f/ RangeZ * (MaxLevel / 255.0f) }; + float TotalLightLevel = 0; + int Dimensions = 0; + for (int i = 0; i < 3; i++) + { + Distances[i] = Distances[i] > 0 ? Distances[i] : -Distances[i]; + } + LightLevel = new float[] { LightLevel[0] * (float)Distances[0], LightLevel[1] * (float)Distances[1], LightLevel[2] * (float)Distances[2] }; + for (int i = 0; i < 3; i++) + { + if (Centres[i] >= 0) + { + Dimensions++; + TotalLightLevel += LightLevel[i]; + } + } + TotalLightLevel /= Dimensions; + return (byte)Math.Min(Math.Max(TotalLightLevel,0),255); + } + private void SetUpLightLevels() + { + LightLevels = new byte[RangeX*2, RangeY*2, RangeZ*2]; + int CentreX = LightLevels.GetLength(0) / 2; + int CentreY = LightLevels.GetLength(1) / 2; + int CentreZ = LightLevels.GetLength(2) / 2; + for (int x = 0; x < LightLevels.GetLength(0); x++) + { + for (int y = 0; y < LightLevels.GetLength(1); y++) + { + for (int z = 0; z < LightLevels.GetLength(2); z++) + { + switch (Shape) + { + case LightShape.Sphere: + if (IsWithinSphere((RangeX + RangeY + RangeZ)/3, CentreX, CentreY, CentreZ, x, y, z)){ + LightLevels[x, y, z] = CalculateLightLevel(CentreX, CentreY, CentreZ, x, y, z, RangeX, RangeY, RangeZ,Brightness); + } + break; + case LightShape.Cylinder: + if (IsWithinCircle((RangeX + RangeY) / 2, CentreX, CentreZ, x, z)) + { + LightLevels[x, y, z] = CalculateLightLevel(CentreX, -1, CentreZ, x, y, z, RangeX, RangeY, RangeZ, Brightness); + } + break; + default: + LightLevels[x, y, z] = CalculateLightLevel(CentreX, CentreY, CentreZ, x, y, z, RangeX, RangeY, RangeZ, Brightness); + break; + } + } + } + + } + } + public LightSource(LightShape shape, int Range, int XWorldPos, int YWorldPos, int ZWorldPos,int Brightness) + { + this.Shape = shape; + this.RangeX = Range; + this.RangeY = Range; + this.RangeZ = Range; + this.XWorldPos = XWorldPos; + this.YWorldPos = YWorldPos; + this.ZWorldPos = ZWorldPos; + this.Brightness = Brightness; + SetUpLightLevels(); + } + public LightSource(LightShape shape, int Range, int XWorldPos, int YWorldPos, int ZWorldPos, float[] RGBLightLevels, int Brightness) + { + this.Shape = shape; + this.RangeX = Range; + this.RangeY = Range; + this.RangeZ = Range; + this.XWorldPos = XWorldPos; + this.YWorldPos = YWorldPos; + this.ZWorldPos = ZWorldPos; + this.LightLevelModifier = RGBLightLevels; + this.Brightness = Brightness; + SetUpLightLevels(); + } + public LightSource( LightShape shape, int RangeX, int RangeY, int RangeZ, int XWorldPos, int YWorldPos, int ZWorldPos, float[] RGBLightLevels, int Brightness) + { + this.Shape = shape; + this.RangeX = RangeX; + this.RangeY = RangeY; + this.RangeZ = RangeZ; + this.XWorldPos = XWorldPos; + this.YWorldPos = YWorldPos; + this.ZWorldPos = ZWorldPos; + this.LightLevelModifier = RGBLightLevels; + this.Brightness = Brightness; + SetUpLightLevels(); + } + } public static class LightingManager { public static float[] AmbientColouring = new float[] { 1.0f, 1.0f, 1.0f }; - public static float[] AmbientMinimums = new float[] { 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f }; + public static float[] AmbientMinimums = new float[] { 0.05f, 0.05f, 0.05f, 0.05f, 0.05f, 0.05f }; public static float[] DefaultValues = new float[] { 0.6f, 0.6f, 0.6f, 0.6f, 1.0f, 0.4f }; - public static float[] SunModifiedValues = new float[] { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }; - public static float[] sunAngles = new float[] { 180, 0 }; + public static float[] SkyLightModifiedValues = new float[] { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }; + public static float[] SunLightColourModifier = new float[] { 1.1f, 1.0f, 0.8f }; + public static float[] MoonLightColourModifier = new float[] { 0.5f, 0.6f, 0.85f }; + public static float[] sunAngles = new float[] { 135, 0 }; + public static float[] SkyLightMetrics = new float[] { 0, 0, 0, 0,0,0 }; + public static List Lights = new List(); public static LightDirection SunAngle = LightDirection.North; + public static float MaxBloom = 1.05f; + public static float[] RenderLightOnCube(int WorldX, int WorldY, int WorldZ, int face) + { + return new float[] { 0, 0, 0 }; + } public static void UpdateLighting() { - float[] AngleRanges = new float[] { 0,45, 90, 135, 180, 225, 270, 315 }; - float[] AngleOffsets = new float[] { 0,180, 90, 270, -1, -1}; + float[] AngleRanges = new float[] { 0, 45, 90, 135, 180, 225, 270, 315 }; + float[] AngleOffsets = new float[] { 0, 180, 90, 270, -1, -1 }; LightDirection[] directions = new LightDirection[] { LightDirection.North, LightDirection.NorthEast, LightDirection.East, LightDirection.SouthEast, LightDirection.South, LightDirection.SouthWest, LightDirection.West, LightDirection.NorthWest }; if (sunAngles[0] > 360) sunAngles[0] -= 360; if (sunAngles[1] > 360) sunAngles[1] -= 360; @@ -42,6 +190,8 @@ namespace VoxelIsometricRenderer if (sunAngles[1] <= -360) sunAngles[1] += 360; float CurrentAngle = 0; int AngleIndex = 0; + float MoonAngle = (float)((1 + Math.Sin(Math.PI * (sunAngles[1] - 60) / 180)) / 2.0); + float SunLightAngle = (float)((1 + Math.Cos(Math.PI * sunAngles[1] / 180)) / 1.8); for (int i = 0; i < AngleRanges.Length; i++) { if (sunAngles[0] < AngleRanges[i]) @@ -56,16 +206,25 @@ namespace VoxelIsometricRenderer AngleIndex = AngleIndex - 1 >= 0 ? AngleIndex - 1 : AngleRanges.Length - 1; } SunAngle = directions[AngleIndex]; - for (int i = 0; i < SunModifiedValues.Length - 2; i++) + for (int i = 0; i < SkyLightModifiedValues.Length - 2; i++) { - SunModifiedValues[i] = (float)((1 + Math.Cos(Math.PI * (sunAngles[0] + AngleOffsets[i]) / 180)/ 2.0) * (float)( 1 + Math.Sin(Math.PI * (sunAngles[1] + 45) / 180))/ 2.0); + SkyLightModifiedValues[i] = (float)((1 + Math.Cos(Math.PI * (sunAngles[0] + AngleOffsets[i]) / 180) / 2.0) * (float)Math.Min(Math.Max((1 + Math.Cos(Math.PI * (sunAngles[1] + 45) / 180)) / 2.0, MoonAngle/2),0.9)); } - for (int i = SunModifiedValues.Length - 2; i < SunModifiedValues.Length; i++) + for (int i = SkyLightModifiedValues.Length - 2; i < SkyLightModifiedValues.Length; i++) { - SunModifiedValues[i] = (float)Math.Max((1 + Math.Cos(Math.PI * (sunAngles[1]) / 180)) / 2.0, 0.3f); + SkyLightModifiedValues[i] = (float)Math.Min(Math.Max(SunLightAngle, MoonAngle/2) + (MoonAngle/ 4),MaxBloom); } + for (int i = 0; i < AmbientColouring.Length; i++) + { + AmbientColouring[i] = SunLightColourModifier[i] * SunLightAngle + MoonLightColourModifier[i] * MoonAngle; + SkyLightMetrics[3 + i] = AmbientColouring[i]; + } + AmbientColouring[2] -= sunAngles[1] > 180 || sunAngles[1] < 90 ? Square(Square((float)(Math.Max(Math.Cos(Math.PI * (sunAngles[1] + 45) / 180), 0)))) / 2.0f : 0;//(float)(Math.Max(Math.Cos(Math.PI * (sunAngles[1] + 45) / 180),0)); + AmbientColouring[1] -= sunAngles[1] > 180 || sunAngles[1] < 90 ? Square(Square((float)(Math.Max(Math.Cos(Math.PI * (sunAngles[1] + 45) / 180), 0))) / 3.0f) : 0;// (float)(Math.Max(Math.Cos(Math.PI * (sunAngles[1] + 45) / 180),0))/2.0f; + SkyLightMetrics[0] = (float)Math.Max(SunLightAngle, 0.1f) + (float)(MoonAngle / 5); + SkyLightMetrics[2] = MoonAngle; } - private static float Square(float ValueIn) + public static float Square(float ValueIn) { return ValueIn * ValueIn; } @@ -93,30 +252,32 @@ namespace VoxelIsometricRenderer if (faces[i] >= 0 && GeneratedFaceTextures[faces[i], i].Height > ImageHeight) ImageHeight = GeneratedFaceTextures[faces[i], i].Height; } Bitmap GeneratedModel = new Bitmap(ImageWidth, ImageHeight); - voxel.Draw(Graphics.FromImage(GeneratedModel),0,0); + voxel.Draw(Graphics.FromImage(GeneratedModel),0,0,0,0,0,0); voxel.SetPreCompiledModel(PreCompiledModels.Count); PreCompiledModels.Add(GeneratedModel); GeneratedModel.Save("Model" + voxel.GetModelNum() + ".png"); } - public static float GetSunlightValue(int face) + public static float GetSunlightValue(int face, int ShadowLevel) { - return LightingManager.SunModifiedValues[face] * LightingManager.DefaultValues[face]; + return (LightingManager.SkyLightModifiedValues[face] * LightingManager.DefaultValues[face])/(float)ShadowLevel; } public static float GetShadingValue(float LightValue, int RGBIndex, int FaceIndex) { return (float)Math.Max(LightValue * LightingManager.AmbientColouring[RGBIndex], LightingManager.AmbientMinimums[FaceIndex]); } - public static ImageAttributes GetShadow(int face) + public static ImageAttributes GetShadow(int face) { return GetShadow(face, 0, 0,0,0); } + public static ImageAttributes GetShadow(int face, int WorldX, int WorldY, int WorldZ, int ShadowLevel) { ImageAttributes imageAttributes; float[][] colorMatrixElements; + float[] Lights = LightingManager.RenderLightOnCube(WorldX, WorldY, WorldZ, face); ColorMatrix colorMatrix; switch (face) { case 0: colorMatrixElements = new float[][]{ - new float[] { GetShadingValue(GetSunlightValue(face), 0,face), 0, 0, 0, 0}, // red - new float[] {0, GetShadingValue(GetSunlightValue(face), 1, face), 0, 0, 0}, // green - new float[] {0, 0, GetShadingValue(GetSunlightValue(face), 2, face), 0, 0}, // blue + new float[] { GetShadingValue(GetSunlightValue(face,ShadowLevel), 0,face), 0, 0, 0, 0}, // red + new float[] {0, GetShadingValue(GetSunlightValue(face,ShadowLevel), 1, face), 0, 0, 0}, // green + new float[] {0, 0, GetShadingValue(GetSunlightValue(face,ShadowLevel), 2, face), 0, 0}, // blue new float[] {0, 0, 0, 1, 0}, // alpha new float[] {0, 0, 0, 0, 1}}; // three translations of 0.2 @@ -126,9 +287,9 @@ namespace VoxelIsometricRenderer return imageAttributes; case 1: colorMatrixElements = new float[][]{ - new float[] { GetShadingValue(GetSunlightValue(face), 0,face), 0, 0, 0, 0}, // red - new float[] {0, GetShadingValue(GetSunlightValue(face), 1, face), 0, 0, 0}, // green - new float[] {0, 0, GetShadingValue(GetSunlightValue(face), 2, face), 0, 0}, // blue + new float[] { GetShadingValue(GetSunlightValue(face,ShadowLevel), 0,face), 0, 0, 0, 0}, // red + new float[] {0, GetShadingValue(GetSunlightValue(face,ShadowLevel), 1, face), 0, 0, 0}, // green + new float[] {0, 0, GetShadingValue(GetSunlightValue(face,ShadowLevel), 2, face), 0, 0}, // blue new float[] {0, 0, 0, 1, 0}, // alpha new float[] {0, 0, 0, 0, 1}}; // three translations of 0.2 @@ -138,9 +299,9 @@ namespace VoxelIsometricRenderer return imageAttributes; case 2: colorMatrixElements = new float[][]{ - new float[] { GetShadingValue(GetSunlightValue(face), 0,face), 0, 0, 0, 0}, // red - new float[] {0, GetShadingValue(GetSunlightValue(face), 1, face), 0, 0, 0}, // green - new float[] {0, 0, GetShadingValue(GetSunlightValue(face), 2, face), 0, 0}, // blue + new float[] { GetShadingValue(GetSunlightValue(face,ShadowLevel), 0,face), 0, 0, 0, 0}, // red + new float[] {0, GetShadingValue(GetSunlightValue(face,ShadowLevel), 1, face), 0, 0, 0}, // green + new float[] {0, 0, GetShadingValue(GetSunlightValue(face,ShadowLevel), 2, face), 0, 0}, // blue new float[] {0, 0, 0, 1, 0}, // alpha new float[] {0, 0, 0, 0, 1}}; // three translations of 0.2 @@ -150,9 +311,9 @@ namespace VoxelIsometricRenderer return imageAttributes; case 3: colorMatrixElements = new float[][]{ - new float[] { GetShadingValue(GetSunlightValue(face), 0,face), 0, 0, 0, 0}, // red - new float[] {0, GetShadingValue(GetSunlightValue(face), 1, face), 0, 0, 0}, // green - new float[] {0, 0, GetShadingValue(GetSunlightValue(face), 2, face), 0, 0}, // blue + new float[] { GetShadingValue(GetSunlightValue(face,ShadowLevel), 0,face), 0, 0, 0, 0}, // red + new float[] {0, GetShadingValue(GetSunlightValue(face,ShadowLevel), 1, face), 0, 0, 0}, // green + new float[] {0, 0, GetShadingValue(GetSunlightValue(face,ShadowLevel), 2, face), 0, 0}, // blue new float[] {0, 0, 0, 1, 0}, // alpha new float[] {0, 0, 0, 0, 1}}; // three translations of 0.2 @@ -162,9 +323,9 @@ namespace VoxelIsometricRenderer return imageAttributes; case 4: colorMatrixElements = new float[][]{ - new float[] { GetShadingValue(GetSunlightValue(face), 0,face), 0, 0, 0, 0}, // red - new float[] {0, GetShadingValue(GetSunlightValue(face), 1, face), 0, 0, 0}, // green - new float[] {0, 0, GetShadingValue(GetSunlightValue(face), 2, face), 0, 0}, // blue + new float[] { GetShadingValue(GetSunlightValue(face,ShadowLevel), 0,face), 0, 0, 0, 0}, // red + new float[] {0, GetShadingValue(GetSunlightValue(face,ShadowLevel), 1, face), 0, 0, 0}, // green + new float[] {0, 0, GetShadingValue(GetSunlightValue(face,ShadowLevel), 2, face), 0, 0}, // blue new float[] {0, 0, 0, 1, 0}, // alpha new float[] {0, 0, 0, 0, 1}}; // three translations of 0.2 @@ -174,9 +335,9 @@ namespace VoxelIsometricRenderer return imageAttributes; case 5: colorMatrixElements = new float[][]{ - new float[] { GetShadingValue(GetSunlightValue(face), 0,face), 0, 0, 0, 0}, // red - new float[] {0, GetShadingValue(GetSunlightValue(face), 1, face), 0, 0, 0}, // green - new float[] {0, 0, GetShadingValue(GetSunlightValue(face), 2, face), 0, 0}, // blue + new float[] { GetShadingValue(GetSunlightValue(face,ShadowLevel), 0,face), 0, 0, 0, 0}, // red + new float[] {0, GetShadingValue(GetSunlightValue(face,ShadowLevel), 1, face), 0, 0, 0}, // green + new float[] {0, 0, GetShadingValue(GetSunlightValue(face,ShadowLevel), 2, face), 0, 0}, // blue new float[] {0, 0, 0, 1, 0}, // alpha new float[] {0, 0, -0, 0, 1}}; // three translations of 0.2 @@ -430,6 +591,7 @@ namespace VoxelIsometricRenderer int[] DrawOrder = new int[] { 5, 0, 3, 1, 2, 4 }; int ModelType = 0; int ModelNum = -1; + bool Cull = false; public int[] getFaces() { return faces; @@ -450,12 +612,18 @@ namespace VoxelIsometricRenderer { return VoxelNum; } - public Voxel(int ModelType,int NorthFace, int SouthFace, int EastFace, int WestFace, int UpFace, int DownFace) + public Voxel(int ModelType,int NorthFace, int SouthFace, int EastFace, int WestFace, int UpFace, int DownFace, bool cull) { this.ModelType = ModelType; faces = new int[] { NorthFace, SouthFace, EastFace, WestFace, UpFace, DownFace }; VoxelNum = TextureManager.VoxelCount; TextureManager.VoxelCount++; + if (cull) SetCullFaces(false,true,false,false, false, false); + Cull = cull; + } + public void SetCullFaces(bool up, bool down, bool West, bool East,bool north, bool south) + { + DrawOrder = new int[] { down?-1:5, north?-1:0, West?-1:3, south?-1:1, East?-1:2, up?-1:4 }; } public void SetPreCompiledModel(int ModelNum) { @@ -467,10 +635,10 @@ namespace VoxelIsometricRenderer VoxelNum = TextureManager.VoxelCount; TextureManager.VoxelCount++; } - virtual public void Draw(Graphics g, int x, int y) + virtual public void Draw(Graphics g, int x, int y, int WorldX, int WorldY, int WorldZ, int ShadowLevel) { for (int i = 0; i < DrawOrder.Length; i++) { - if (faces[DrawOrder[i]] >= 0) + if (DrawOrder[i] > 0 && DrawOrder[i] < 6 && faces[DrawOrder[i]] >= 0) { if (ModelNum == -1) { @@ -479,7 +647,7 @@ namespace VoxelIsometricRenderer { case 0: Side = GetRenderedSide(DrawOrder[i]); - g.DrawImage(Side, new Rectangle(x, y, Side.Width, Side.Height), 0, 0, Side.Width, Side.Height, GraphicsUnit.Pixel, TextureManager.GetShadow(DrawOrder[i])); + g.DrawImage(Side, new Rectangle(x, y, Side.Width, Side.Height), 0, 0, Side.Width, Side.Height, GraphicsUnit.Pixel, TextureManager.GetShadow(DrawOrder[i], WorldX, WorldY,WorldZ, ShadowLevel)); break; default: @@ -487,23 +655,23 @@ namespace VoxelIsometricRenderer { case 0: Side = GetRenderedSide(0); - g.DrawImage(Side, new Rectangle(x - (Side.Width / 4), y, Side.Width, Side.Height),0,0,Side.Width,Side.Height, GraphicsUnit.Pixel, TextureManager.GetShadow(0)); + g.DrawImage(Side, new Rectangle(x - (Side.Width / 4), y, Side.Width, Side.Height),0,0,Side.Width,Side.Height, GraphicsUnit.Pixel, TextureManager.GetShadow(0, WorldX, WorldY, WorldZ, ShadowLevel)); break; case 1: Side = GetRenderedSide(1); - g.DrawImage(Side, new Rectangle(x + (Side.Width / 4), y - (Side.Height / 4), Side.Width, Side.Height),0,0,Side.Width,Side.Height, GraphicsUnit.Pixel, TextureManager.GetShadow(1)); + g.DrawImage(Side, new Rectangle(x + (Side.Width / 4), y - (Side.Height / 4), Side.Width, Side.Height),0,0,Side.Width,Side.Height, GraphicsUnit.Pixel, TextureManager.GetShadow(1, WorldX, WorldY, WorldZ, ShadowLevel)); break; case 2: Side = GetRenderedSide(2); - g.DrawImage(Side, new Rectangle(x - (Side.Width / 4), y - (Side.Height / 4), Side.Width, Side.Height),0,0,Side.Width,Side.Height, GraphicsUnit.Pixel, TextureManager.GetShadow(2)); + g.DrawImage(Side, new Rectangle(x - (Side.Width / 4), y - (Side.Height / 4), Side.Width, Side.Height),0,0,Side.Width,Side.Height, GraphicsUnit.Pixel, TextureManager.GetShadow(2, WorldX, WorldY, WorldZ, ShadowLevel)); break; case 3: Side = GetRenderedSide(3); - g.DrawImage(Side, new Rectangle( x + (Side.Width / 4),y,Side.Width,Side.Height),0,0,Side.Width,Side.Height, GraphicsUnit.Pixel, TextureManager.GetShadow(3)); + g.DrawImage(Side, new Rectangle( x + (Side.Width / 4),y,Side.Width,Side.Height),0,0,Side.Width,Side.Height, GraphicsUnit.Pixel, TextureManager.GetShadow(3, WorldX, WorldY, WorldZ, ShadowLevel)); break; default: Side = GetRenderedSide(DrawOrder[i]); - g.DrawImage(Side, new Rectangle(x, y, Side.Width, Side.Height), 0, 0, Side.Width, Side.Height, GraphicsUnit.Pixel, TextureManager.GetShadow(DrawOrder[i])); + g.DrawImage(Side, new Rectangle(x, y, Side.Width, Side.Height), 0, 0, Side.Width, Side.Height, GraphicsUnit.Pixel, TextureManager.GetShadow(DrawOrder[i], WorldX, WorldY, WorldZ, ShadowLevel)); break; } break; @@ -577,4 +745,319 @@ namespace VoxelIsometricRenderer faces[5] = Texture; } } + public static class VoxelRegistry + { + public static List Voxels = new List(); + public static int IndexVoxel(Voxel voxel) + { + if(Voxels.Contains(voxel)) return Voxels.IndexOf(voxel); + Voxels.Add(voxel); + return Voxels.IndexOf(Voxels.Last()); + } + public static int VoxelExists(Voxel voxel) + { + return Voxels.Contains(voxel) ? Voxels.IndexOf(voxel) : -1; + } + public static Voxel FetchVoxel(int VoxelIndex) + { + return Voxels[VoxelIndex]; + } + } + public enum BlockType + { + Plant, + Ground, + Wood, + Metal, + BuildMaterial, + Gas, + Fluid + } + public class BlockState + { + bool TickUpdate = false; + BlockType BlockType; + bool BlocksLight = false; + + public BlockState(bool TickUpdate, BlockType blocktype, bool blocksLight) + { + this.TickUpdate = TickUpdate; + this.BlockType = blocktype; + BlocksLight = blocksLight; + } + public bool hasBlocksLight() { return BlocksLight; } + public BlockType GetBlockType() { return BlockType; } + public bool Ticks() { return TickUpdate; } + } + public static class BlockstateRegistry + { + public static List Blockstates = new List(); + public static int GetBlockstateIndex(BlockState blockstate) + { + if (!Blockstates.Contains(blockstate)) Blockstates.Add(blockstate); + return Blockstates.IndexOf(blockstate); + } + public static BlockState GetBlockstate(int index) + { + return Blockstates[index]; + } + public static void SetBlockstate(int index, BlockState blockstate) + { + Blockstates[index] = blockstate; + } + } + public class Chunk + { + public ListDrawList = new List(); + public ListDrawPositions = new List(); + public int[,,] BlockReg; + public bool[,,] Culled; + public int[] ChunkPos; + public int ChunkID; + public int[,,] SkyLightHeightMap; + public Chunk(int X, int Y, int Z, int Width, int Height, int Length, int ChunkID) + { + this.ChunkID = ChunkID; + BlockReg = new int[Width,Height,Length]; + Culled = new bool[Width,Height,Length]; + SkyLightHeightMap = new int[Width,Height,Length]; + ChunkPos = new int[] { X, Y, Z }; + } + public void CalculateCull() + { + DrawList = new List(); + int CulledCount = 0; + for (int x = 0; x < Culled.GetLength(0); x++) + { + for (int y = 0; y < Culled.GetLength(1); y++) + { + for (int z = 0; z < Culled.GetLength(2); z++) + { + if (!(x == Culled.GetLength(0) - 1 || x < Culled.GetLength(0) - 1 && (BlockReg[x + 1, y, z] != -1 && !BlockRegistry.FetchBlockState(BlockReg[x + 1, y, z]).hasBlocksLight() || BlockReg[x + 1, y, z] == -1)) + && !(x > 0 && (BlockReg[x - 1, y, z] != -1 && !BlockRegistry.FetchBlockState(BlockReg[x - 1, y, z]).hasBlocksLight() || BlockReg[x - 1, y, z] == -1)) + && !(y == Culled.GetLength(1) - 1 || y < Culled.GetLength(1) - 1 && (BlockReg[x, y + 1, z] != -1 && !BlockRegistry.FetchBlockState(BlockReg[x, y + 1, z]).hasBlocksLight() || BlockReg[x, y + 1, z] == -1)) + && !(y > 0 && (BlockReg[x, y - 1, z] != -1 && !BlockRegistry.FetchBlockState(BlockReg[x, y - 1, z]).hasBlocksLight() || BlockReg[x, y - 1, z] == -1)) + && !(z == Culled.GetLength(2) - 1 || z < Culled.GetLength(2) - 1 && (BlockReg[x, y, z + 1] != -1 && !BlockRegistry.FetchBlockState(BlockReg[x, y, z + 1]).hasBlocksLight() || BlockReg[x, y, z + 1] == -1)) + && !(z > 0 && (BlockReg[x, y, z - 1] != -1 && !BlockRegistry.FetchBlockState(BlockReg[x, y, z - 1]).hasBlocksLight() || BlockReg[x, y, z - 1] == -1))) + { + Culled[x, y, z] = true; + CulledCount++; + } + } + } + } + for (int x = 0; x < BlockReg.GetLength(0); x++) + { + for (int y = 0; y < BlockReg.GetLength(1); y++) + { + for (int z = 0; z < BlockReg.GetLength(1); z++) + { + if (Culled[x,y,z] == false && BlockReg[x, y, z] != -1) + { + DrawList.Add(BlockReg[x,y,z]); + DrawPositions.Add(new int[] { x, y, z }); + } + } + } + } + + } + public void GenerateLightMap() + { + for (int x = 0;x < SkyLightHeightMap.GetLength(0); x++) + { + for(int z = 0; z < SkyLightHeightMap.GetLength(1); z++) + { + int Y = 1; + for (int y = SkyLightHeightMap.GetLength(1) - 1; y > 0; y--) + { + SkyLightHeightMap[x, y, z] = Y; + if (BlockReg[x,y,z] != -1 && BlockRegistry.FetchBlockState(BlockReg[x, y, z]).hasBlocksLight()) + { + Y++; + } + } + } + } + } + public void RenderBlocks(Graphics g) + { + if (DrawList.Count == 0) + { + for (int x = 0; x < BlockReg.GetLength(0); x++) + { + for (int y = 0; y < BlockReg.GetLength(1); y++) + { + for (int z = 0; z < BlockReg.GetLength(1); z++) + { + if (BlockReg[x, y, z] != -1 && Culled[x, y, z] == false) + { + BlockRegistry.RenderBlock(g, BlockReg[x, y, z], x + ChunkPos[0] * ChunkRegistry.FetchDimensions()[0], y + ChunkPos[1] * ChunkRegistry.FetchDimensions()[1], z + ChunkPos[1] * ChunkRegistry.FetchDimensions()[1], SkyLightHeightMap[x, y, z]); + } + } + } + } + } + else + { + for (int i = 0; i < DrawList.Count; i++) + { + BlockRegistry.RenderBlock(g, DrawList[i], DrawPositions[i][0] + ChunkPos[0] * ChunkRegistry.FetchDimensions()[0], DrawPositions[i][1] + ChunkPos[1] * ChunkRegistry.FetchDimensions()[1], DrawPositions[i][2] + ChunkPos[1] * ChunkRegistry.FetchDimensions()[1], SkyLightHeightMap[DrawPositions[i][0], DrawPositions[i][1], DrawPositions[i][2]]); + } + } + } + public void CreateAir(int X, int Y, int Z) + { + BlockReg[X, Y, Z] = -1; + } + public void CreateBlock(int[] Faces, int RenderType, BlockState blockState, int X, int Y, int Z) + { + Voxel voxel = new Voxel(RenderType, Faces[0], Faces[1], Faces[2], Faces[3], Faces[4], Faces[5],blockState.hasBlocksLight()); + int index = VoxelRegistry.VoxelExists(voxel); + if (index == -1) + { + index = VoxelRegistry.IndexVoxel(voxel); + } + Block newBlock = new Block(index, BlockstateRegistry.GetBlockstateIndex(blockState), ChunkID); + int x = X + ChunkPos[0] * ChunkRegistry.FetchDimensions()[0]; + int y = Y + ChunkPos[1] * ChunkRegistry.FetchDimensions()[1]; + int z = Z + ChunkPos[2] * ChunkRegistry.FetchDimensions()[2]; + int BlockID = BlockRegistry.CheckInBlock(newBlock, x, y, z); + BlockReg[X, Y, Z] = BlockID; + } + public Block FetchBlock(int x, int y, int z) + { + return BlockRegistry.FetchBlock(BlockReg[x,y,z]); + } + public void IndexBlock(int BlockIndex, int x, int y, int z) + { + BlockReg[x,y,z] = BlockIndex; + } + public string FetchPosString() + { + return String.Format("x={0};y={1};z={2}", ChunkPos[0], ChunkPos[1], ChunkPos[2]); + } + public int[] FetchPos() + { + return ChunkPos; + } + public int[,,] FetchAllBlocks() + { + return BlockReg; + } + public int FetchBlockReg(int x, int y, int z) + { + return BlockReg[x, y, z]; + } + } + + public static class ChunkRegistry + { + static int ChunkWidth = 16; + static int ChunkHeight = 16; + static int ChunkLength = 16; + static List Chunks = new List(); + static List Chunk_Pos_Lookup = new List(); + public static Chunk IndexChunk(int x, int y, int z) + { + Chunk newChunk = new Chunk(x, y, z, ChunkWidth, ChunkHeight, ChunkLength, Chunks.Count); + Chunks.Add(newChunk); + Chunk_Pos_Lookup.Add(String.Format("x={0};y={1};z={2}", x, y, z)); + return newChunk; + } + public static Chunk FetchChunk(string pos_lookup) + { + if (!Chunk_Pos_Lookup.Contains(pos_lookup)) return null; + return Chunks[Chunk_Pos_Lookup.IndexOf(pos_lookup)]; + } + public static Chunk FetchChunk(int index) + { + return Chunks[index]; + } + public static void RenderChunk(Graphics g,int chunkIndex) + { + Chunks[chunkIndex].RenderBlocks(g); + } + public static int FetchChunkID(string pos_lookup) + { + if (!Chunk_Pos_Lookup.Contains(pos_lookup)) return -1; + return Chunk_Pos_Lookup.IndexOf(pos_lookup); + } + public static int[] FetchDimensions() + { + return new int[] {ChunkWidth, ChunkHeight, ChunkLength}; + } + } + public static class BlockRegistry + { + static List blocks = new List(); + static List block_pos_lookup = new List(); + static int BlockSize = 32; + public static BlockState FetchBlockState(int BlockID) + { + return BlockstateRegistry.GetBlockstate(blocks[BlockID].FetchBlockState()); + } + public static Block FetchBlock(int BlockID) + { + return blocks[BlockID]; + } + public static int FetchBlockIDFromPos(string pos_lookup) + { + if (!block_pos_lookup.Contains(pos_lookup)) return -1; + return block_pos_lookup.IndexOf(pos_lookup); + } + public static int[] GetDrawLocation(int x, int y, int z, int offsetX, int offsetY, int offsetZ) + { + return new int[]{(int)((((double)(x + offsetX) * 0.5) + ((double)(z + offsetZ) * -0.5))*(double)BlockSize), + (int)((((double)(x + offsetX) * 0.5) + ((double)(z + offsetZ) * 0.5) - (offsetY + (y)))*((double)BlockSize/2.0)) }; + } + public static void RenderBlock(Graphics g, int BlockID,int WorldX, int WorldY, int WorldZ, int ShadowLevel) + { + int[] location = GetDrawLocation(WorldX, WorldY, WorldZ, 0, 0, 0); + VoxelRegistry.FetchVoxel(blocks[BlockID].FetchVoxelID()).Draw(g, location[0] + 300, location[1] + 300, WorldX, WorldY, WorldZ, ShadowLevel); + } + public static int CheckInBlock(Block block,int x, int y, int z) + { + blocks.Add(block); + block_pos_lookup.Add(String.Format("x={0};y={1};z={2}",x,y,z)); + return blocks.IndexOf(blocks.Last()); + } + public static string FetchBlockString(int x, int y, int z) + { + return String.Format("x={0};y={1};z={2}", x, y, z); + } + } + public class Block + { + int VoxelID; + int BlockstateID; + int ChunkID; + bool render; + public Block(int voxelID, int blockstateID, int chunkID) + { + VoxelID = voxelID; + BlockstateID = blockstateID; + ChunkID = chunkID; + } + public int FetchVoxelID() + { + return VoxelID; + } + public int FetchChunkID() + { + return ChunkID; + } + public int FetchBlockState() + { + return BlockstateID; + } + public void SetRender(bool render) + { + this.render = render; + } + public bool Renders() + { + return render; + } + } }