using System; 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 { public enum LightDirection { North, NorthEast, NorthWest, East, West, SouthEast, 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.0f, 1.0f, 1.0f }; int Brightness = 64; public void SetPosition(int[] pos) { XWorldPos = pos[0]; YWorldPos = pos[1]; ZWorldPos = pos[2]; } public int[] GetPosition() { return new int[] { XWorldPos, YWorldPos, ZWorldPos }; } public int[] GetRadius() { return new int[] {RangeX,RangeY, RangeZ }; } public int GetAverageRadius() { return (RangeX + RangeY + RangeZ) / 3; } public float[] GetRGBLightLevels(int level) { float Multiplier = (float)level / 255.0f; return new float[] { LightLevelModifier[0] * Multiplier, LightLevelModifier[1] * Multiplier, LightLevelModifier[2] * Multiplier }; } public int GetLightLevel(int RelativeBlockX, int RelativeBlockY, int RelativeBlockZ, int face, bool BlocksLight) { int CentreX = LightLevels.GetLength(0) / 2; int CentreY = LightLevels.GetLength(1) / 2; int CentreZ = LightLevels.GetLength(2) / 2; int LightLevel = 0; if(RelativeBlockX >= 0 && RelativeBlockX < LightLevels.GetLength(0) && RelativeBlockY >= 0 && RelativeBlockY < LightLevels.GetLength(1) && RelativeBlockZ >= 0 && RelativeBlockZ < LightLevels.GetLength(2)) { LightLevel = LightLevels[RelativeBlockX,RelativeBlockY,RelativeBlockZ]; float DistanceX = RelativeBlockX - CentreX; float DistanceZ = RelativeBlockZ - CentreZ; double CorrectLightLevel =Math.Atan2(RelativeBlockX - CentreX, RelativeBlockZ - CentreZ); int DirectionX = 0; if (BlocksLight) { switch (face) { case 0: CorrectLightLevel += (Math.PI * 0 / 180); if (CentreZ > RelativeBlockZ) { DirectionX = 180; } if (CentreZ == RelativeBlockZ) { DirectionX = 90; } if (CentreZ < RelativeBlockZ) { DirectionX = 0; } break; case 1: CorrectLightLevel += (Math.PI * 180 / 180); if (CentreZ < RelativeBlockZ) { DirectionX = 180; } if (CentreZ == RelativeBlockZ) { DirectionX = 90; } if (CentreZ > RelativeBlockZ) { DirectionX = 0; } break; case 2: CorrectLightLevel += (Math.PI * 90 / 180); if (CentreX < RelativeBlockX) { DirectionX = 180; } if (CentreX == RelativeBlockX) { DirectionX = 90; } if (CentreX > RelativeBlockX) { DirectionX = 0; } break; case 3: CorrectLightLevel += (Math.PI * 270 / 180); if (CentreX > RelativeBlockX) { DirectionX = 180; } if (CentreX == RelativeBlockX) { DirectionX = 90; } if (CentreX < RelativeBlockX) { DirectionX = 0; } break; case 4: if (CentreY < RelativeBlockY) { DirectionX = 180; } if(CentreY == RelativeBlockY) { DirectionX = 90; } if(CentreY > RelativeBlockY) { DirectionX = 0; } break; case 5: if (CentreY > RelativeBlockY) { DirectionX = 180; } if (CentreY == RelativeBlockY) { DirectionX = 90; } if (CentreY < RelativeBlockY) { DirectionX = 0; } break; } /* if (CentreZ < RelativeBlockZ && RelativeBlockZ > RelativeBlockX) { for (int i = RelativeBlockZ; i < LightLevels.GetLength(2); i++) { LightLevels[RelativeBlockX, RelativeBlockY, i] = 0; } } else if (CentreZ > RelativeBlockZ && RelativeBlockZ < RelativeBlockX) { for (int i = RelativeBlockZ; i >= 0; i--) { LightLevels[RelativeBlockX, RelativeBlockY, i] = 0; } } else { if (CentreX < RelativeBlockX && RelativeBlockX > RelativeBlockZ) { for (int i = RelativeBlockX; i < LightLevels.GetLength(0); i++) { LightLevels[i, RelativeBlockY, RelativeBlockZ] = 0; } } else if (CentreX > RelativeBlockX && RelativeBlockX < RelativeBlockZ) { for (int i = RelativeBlockZ; i >= 0; i--) { LightLevels[i, RelativeBlockY, RelativeBlockZ] = 0; } } }*/ LightLevel = face > 3? (int)(LightLevel * ((1 + Math.Cos(Math.PI * DirectionX / 180)) / 2)) : (int)(LightLevel * ((1 + Math.Cos(CorrectLightLevel)) / 2)); //LightLevel = (int)(LightLevel * ((1 + Math.Cos(Math.PI * DirectionX / 180)) / 2)); // MessageBox.Show(LightLevel + ""); } } return LightLevel; } 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]; } //if (PosY < CentreY && PosX == CentreX && PosZ == CentreZ) { MessageBox.Show(Distances[1] + ""); } //if (RangeX >= Distances[0] && PosY == CentreX && PosZ == CentreZ) MessageBox.Show(String.Format("LightLevel: {0}\nRange:{1}\nDistance:{2}\nRange/Distance:{3}", LightLevel[0], RangeX, Distances[0], RangeX / (float)Distances[0])); LightLevel = new float[] { RangeX >= Distances[0] ? LightLevel[0] * (RangeX / ((float)Distances[0] > 0 ? (float)Distances[0] : 1)) : 0, RangeY >= Distances[1] ? LightLevel[1] * (RangeY / ((float)Distances[1] > 0 ? (float)Distances[1] : 1)) : 0, RangeZ >= Distances[2] ? LightLevel[2] * (RangeZ / ((float)Distances[2] > 0 ? (float)Distances[2] : 1)) : 0 }; 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.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[] 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[] GetLightsOnCube(int WorldX, int WorldY, int WorldZ, int face, bool BlocksLight) { float[] RGBLevels = new float[] { 0, 0, 0 }; int LightCount = 0; Lights.ForEach(Light => { int DistX = WorldX - (Light.GetPosition()[0] - Light.GetRadius()[0]); int DistY = WorldY - (Light.GetPosition()[1] - Light.GetRadius()[1]); int DistZ = WorldZ - (Light.GetPosition()[2] - Light.GetRadius()[2]); if (DistX >= 0 && DistY >= 0 && DistZ >= 0 && DistX - Light.GetRadius()[0] <= 0 && DistY - Light.GetRadius()[1] <= 0 && DistZ - Light.GetRadius()[2] <= 0) { //MessageBox.Show("X: " + DistX + " Y: " + DistY + " Z: " + DistZ); float[] LightRGB = new float[3]; LightCount++; int LightLevel = Light.GetLightLevel(DistX, DistY, DistZ, face, BlocksLight); LightRGB = Light.GetRGBLightLevels(LightLevel); RGBLevels[0] += LightRGB[0]; RGBLevels[1] += LightRGB[1]; RGBLevels[2] += LightRGB[2]; } }); return new float[] { RGBLevels[0] != 0 ? RGBLevels[0]/LightCount : 0, RGBLevels[1] != 0 ? RGBLevels[1]/LightCount : 0, RGBLevels[2] != 0 ? RGBLevels[2]/LightCount : 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 }; 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; if (sunAngles[0] <= -360) sunAngles[0] += 360; if (sunAngles[1] <= -360) sunAngles[1] += 360; float CurrentAngle = 0; int AngleIndex = 0; float MoonAngle = (float)((1 + Math.Sin(Math.PI * (sunAngles[1] - 90) / 180)) / 2.0); float SunLightAngle = (float)((1 + Math.Cos(Math.PI * sunAngles[1] / 180)) / 2.0); for (int i = 0; i < AngleRanges.Length; i++) { if (sunAngles[0] < AngleRanges[i]) { CurrentAngle = AngleRanges[i]; AngleIndex = i; break; } } if (Square(CurrentAngle - sunAngles[0]) > Square(sunAngles[0] - AngleRanges[AngleIndex - 1 >= 0 ? AngleIndex - 1 : AngleRanges.Length - 1])) { AngleIndex = AngleIndex - 1 >= 0 ? AngleIndex - 1 : AngleRanges.Length - 1; } SunAngle = directions[AngleIndex]; for (int i = 0; i < SkyLightModifiedValues.Length - 2; i++) { 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 = SkyLightModifiedValues.Length - 2; i < SkyLightModifiedValues.Length; i++) { SkyLightModifiedValues[i] = (float)Math.Min(SunLightAngle + (MoonAngle/ 2),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)SunLightAngle + (float)(MoonAngle / 5); SkyLightMetrics[2] = MoonAngle; } public static float Square(float ValueIn) { return ValueIn * ValueIn; } } public static class TextureManager { public static List textures = new List() {Resources._16xDefaultTile}; public static List PreCompiledModels = new List(); public static Bitmap[,] GeneratedFaceTextures = new Bitmap[0,0]; public static int VoxelCount = 0; public static int VoxelSize = 32; public static float RenderScale = 2.0f; public static Bitmap GetModel(int modelNum) { return PreCompiledModels[modelNum]; } public static void CreateModel(Voxel voxel) { if (GeneratedFaceTextures.GetLength(1) < 6) GenerateVoxelFaces(); int ImageWidth = 0; int ImageHeight = 0; int[] faces = voxel.getFaces(); int[] DrawOrder = voxel.GetDrawOrder(); for(int i = 0; i < faces.Length; i++) { if (faces[i] >= 0 && GeneratedFaceTextures[faces[i], i].Width > ImageWidth) ImageWidth = GeneratedFaceTextures[faces[i], i].Width; 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,0,0,0,0,false); voxel.SetPreCompiledModel(PreCompiledModels.Count); PreCompiledModels.Add(GeneratedModel); GeneratedModel.Save("Model" + voxel.GetModelNum() + ".png"); } public static float GetSunlightValue(int face, int ShadowLevel) { return (LightingManager.SkyLightModifiedValues[face] * LightingManager.DefaultValues[face]) / (float)ShadowLevel; } public static float GetShadingValue(float LightValue, int RGBIndex, int FaceIndex) { return GetShadingValue(LightValue, RGBIndex, FaceIndex, new float[] { 0, 0, 0 }); } public static float GetShadingValue(float LightValue, int RGBIndex, int FaceIndex, float[] Lights) { return ((float)Math.Max(LightValue * LightingManager.AmbientColouring[RGBIndex], LightingManager.AmbientMinimums[FaceIndex])) + Lights[RGBIndex]; } public static ImageAttributes GetShadow(int face) { return GetShadow(face, 0, 0,0,0, false); } public static ImageAttributes GetShadow(int face, int WorldX, int WorldY, int WorldZ, int ShadowLevel, bool BlocksLight) { ImageAttributes imageAttributes; float[][] colorMatrixElements; float[] Lights = LightingManager.GetLightsOnCube(WorldX, WorldY, WorldZ, face, BlocksLight); ColorMatrix colorMatrix; switch (face) { case 0: colorMatrixElements = new float[][]{ new float[] { GetShadingValue(GetSunlightValue(face,ShadowLevel), 0,face,Lights), 0, 0, 0, 0}, // red new float[] {0, GetShadingValue(GetSunlightValue(face,ShadowLevel), 1, face, Lights), 0, 0, 0}, // green new float[] {0, 0, GetShadingValue(GetSunlightValue(face,ShadowLevel), 2, face, Lights), 0, 0}, // blue new float[] {0, 0, 0, 1, 0}, // alpha new float[] {0, 0, 0, 0, 1}}; // three translations of 0.2 colorMatrix = new ColorMatrix(colorMatrixElements); imageAttributes = new ImageAttributes(); imageAttributes.SetColorMatrix(colorMatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap); return imageAttributes; case 1: colorMatrixElements = new float[][]{ new float[] { GetShadingValue(GetSunlightValue(face,ShadowLevel), 0,face, Lights), 0, 0, 0, 0}, // red new float[] {0, GetShadingValue(GetSunlightValue(face,ShadowLevel), 1, face, Lights), 0, 0, 0}, // green new float[] {0, 0, GetShadingValue(GetSunlightValue(face,ShadowLevel), 2, face, Lights), 0, 0}, // blue new float[] {0, 0, 0, 1, 0}, // alpha new float[] {0, 0, 0, 0, 1}}; // three translations of 0.2 colorMatrix = new ColorMatrix(colorMatrixElements); imageAttributes = new ImageAttributes(); imageAttributes.SetColorMatrix( colorMatrix,ColorMatrixFlag.Default,ColorAdjustType.Bitmap); return imageAttributes; case 2: colorMatrixElements = new float[][]{ new float[] { GetShadingValue(GetSunlightValue(face,ShadowLevel), 0,face, Lights), 0, 0, 0, 0}, // red new float[] {0, GetShadingValue(GetSunlightValue(face,ShadowLevel), 1, face, Lights), 0, 0, 0}, // green new float[] {0, 0, GetShadingValue(GetSunlightValue(face,ShadowLevel), 2, face, Lights), 0, 0}, // blue new float[] {0, 0, 0, 1, 0}, // alpha new float[] {0, 0, 0, 0, 1}}; // three translations of 0.2 colorMatrix = new ColorMatrix(colorMatrixElements); imageAttributes = new ImageAttributes(); imageAttributes.SetColorMatrix(colorMatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap); return imageAttributes; case 3: colorMatrixElements = new float[][]{ new float[] { GetShadingValue(GetSunlightValue(face,ShadowLevel), 0,face, Lights), 0, 0, 0, 0}, // red new float[] {0, GetShadingValue(GetSunlightValue(face,ShadowLevel), 1, face, Lights), 0, 0, 0}, // green new float[] {0, 0, GetShadingValue(GetSunlightValue(face,ShadowLevel), 2, face, Lights), 0, 0}, // blue new float[] {0, 0, 0, 1, 0}, // alpha new float[] {0, 0, 0, 0, 1}}; // three translations of 0.2 colorMatrix = new ColorMatrix(colorMatrixElements); imageAttributes = new ImageAttributes(); imageAttributes.SetColorMatrix(colorMatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap); return imageAttributes; case 4: colorMatrixElements = new float[][]{ new float[] { GetShadingValue(GetSunlightValue(face,ShadowLevel), 0,face, Lights), 0, 0, 0, 0}, // red new float[] {0, GetShadingValue(GetSunlightValue(face,ShadowLevel), 1, face, Lights), 0, 0, 0}, // green new float[] {0, 0, GetShadingValue(GetSunlightValue(face,ShadowLevel), 2, face, Lights), 0, 0}, // blue new float[] {0, 0, 0, 1, 0}, // alpha new float[] {0, 0, 0, 0, 1}}; // three translations of 0.2 colorMatrix = new ColorMatrix(colorMatrixElements); imageAttributes = new ImageAttributes(); imageAttributes.SetColorMatrix(colorMatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap); return imageAttributes; case 5: colorMatrixElements = new float[][]{ new float[] { GetShadingValue(GetSunlightValue(face,ShadowLevel), 0,face, Lights), 0, 0, 0, 0}, // red new float[] {0, GetShadingValue(GetSunlightValue(face,ShadowLevel), 1, face, Lights), 0, 0, 0}, // green new float[] {0, 0, GetShadingValue(GetSunlightValue(face,ShadowLevel), 2, face, Lights), 0, 0}, // blue new float[] {0, 0, 0, 1, 0}, // alpha new float[] {0, 0, -0, 0, 1}}; // three translations of 0.2 colorMatrix = new ColorMatrix(colorMatrixElements); imageAttributes = new ImageAttributes(); imageAttributes.SetColorMatrix(colorMatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap); return imageAttributes; default: imageAttributes = new ImageAttributes(); return imageAttributes; } } public static Point IntegerCalculateIsometricProjection(int width, int height, int X, int Y, int Z, int Corner) { switch (Corner) { case 0: return new Point(width / 2 + (int)((X * 1) + (Z * -1)), height / 2 + (int)Math.Ceiling((X * 0.5f) + (Z * 0.5f) - (Y / 3) * 2)); case 1: return new Point(width / 2 + (int)((X * 1) + (Z * -1)), height / 2 + (int)Math.Floor((X * 0.5f) + (Z * 0.5f) - (Y / 3) * 2)); case 2: return new Point(-1 + width / 2 + (int)((X * 1) + (Z * -1)), height / 2 + (int)Math.Floor((X * 0.5f) + (Z * 0.5f) - (Y / 3) * 2)); default: return new Point(-1 + width / 2 + (int)((X * 1) + (Z * -1)), height / 2 + (int)Math.Ceiling((X * 0.5f) + (Z * 0.5f) - (Y / 3) * 2)); } } public static Point IntegerCalculateIsometricProjection(int width, int height, int X, int Y, int Z) { return new Point(width / 2 + (int)((X * 1) + (Z * -1)), height / 2 + (int)((X * 0.5f) + (Z * 0.5f) - (Y / 3) * 2)); } public static PointF CalculateIsometricProjection(double[] Position, int width, int height, int X, int Y, int Z) { return new PointF(width / 2 + (float)(((/*Globals.CameraX*/ +X + Position[0]) * 0.5) + ((/*Globals.CameraZ +*/ Z + Position[1]) * -0.5)), height / 2 + (float)(((/*Globals.CameraX*/ +X + Position[0]) * 0.25f) + ((/*Globals.CameraZ*/ +Z + Position[1]) * 0.25f) - (((/*Globals.CameraY*/ +Position[2]) + (Y / 3) * 2)))); } public static Bitmap GetVoxelFaceBitmap(int index, int face) { if (GeneratedFaceTextures.GetLength(1) < 6) GenerateVoxelFaces(); return GeneratedFaceTextures[index,face]; } public static Bitmap GetBitmap(int index) { return textures[index]; } public static void AddBitmap(Bitmap texture) { textures.Add(texture); } public static void GenerateVoxelFaces() { GeneratedFaceTextures = new Bitmap[textures.Count, 6]; for (int i = 0; i < textures.Count; i++) { for (int face = 0; face < 6; face++) { Bitmap SkewedTexture = new Bitmap(textures[i].Width * 2, textures[i].Height * 2); int y = 0;// textures[i].Height; switch (face) { case 0: y = 1; for(int x = 0; x < textures[i].Width; x++) { for(int ypx = 0; ypx < textures[i].Height; ypx++) { SkewedTexture.SetPixel(textures[i].Width+ x, y + ypx, textures[i].GetPixel(x, ypx)); } if (x % 2 != 0) y++; } y = 0; break; case 1: for (int x = textures[i].Width - 1; x >= 0 ; x--) { for (int ypx = 0; ypx < textures[i].Height; ypx++) { SkewedTexture.SetPixel(x, SkewedTexture.Height - (y + ypx + 1), textures[i].GetPixel(x, textures[i].Height - 1 - ypx)); } if (x % 2 == 0) y++; } y = 0; break; case 2: for (int x = 0; x < textures[i].Width; x++) { for (int ypx = 0; ypx < textures[i].Height; ypx++) { SkewedTexture.SetPixel(textures[i].Width + x, SkewedTexture.Height - (y + ypx + 1), textures[i].GetPixel(x, textures[i].Height - 1 - ypx)); } if (x % 2 != 0) y++; } y = 0; break; case 3: y = 1; for (int x = textures[i].Width - 1; x >= 0; x--) { for (int ypx = 0; ypx < textures[i].Height; ypx++) { SkewedTexture.SetPixel(x, y + ypx, textures[i].GetPixel(x, ypx)); } if (x % 2 == 0) y++; } y = 0; break; case 4: Bitmap[] Textures = new Bitmap[4]; Bitmap MergedTextures = new Bitmap(SkewedTexture.Width, SkewedTexture.Height); for (int l = 0; l < 4; l++) { Textures[l] = new Bitmap(SkewedTexture.Width, SkewedTexture.Height); } for(int l = 0; l < 4; l++) { for (int x = 0; x < textures[i].Width; x++) { for (int ypx = 0; ypx < textures[i].Height; ypx++) { Point PixelLocation = IntegerCalculateIsometricProjection(textures[i].Width * 2, 0, x, 0, ypx,l); PixelLocation.X = Math.Max(Math.Min(PixelLocation.X, SkewedTexture.Width - 1), 0); PixelLocation.Y = Math.Max(Math.Min(PixelLocation.Y, SkewedTexture.Height - 1), 0); Textures[l].SetPixel(PixelLocation.X, PixelLocation.Y, textures[i].GetPixel(x, ypx)); } } } for (int l = 0; l < 4; l++) { int Startx = 0; int Starty = 0; int endX = Textures[l].Width; int endY = Textures[l].Height; switch (l) { case 1: Startx = Textures[l].Width / 2; Starty = 0; endX = Textures[l].Width; endY = Textures[l].Height/4; break; case 0: Startx = Textures[l].Width / 2; Starty = Textures[l].Height / 4; endX = Textures[l].Width; endY = Textures[l].Height/2; break; case 2: Startx = 0; Starty = 0; endX = Textures[l].Width/2; endY = Textures[l].Height / 4 ; break; case 3: Startx = 0; Starty = Textures[l].Height / 4; endX = Textures[l].Width/2; endY = Textures[l].Height/2; break; } for(int x = Startx; x < endX; x++) { for (int ypx = Starty; ypx < endY;ypx++) { MergedTextures.SetPixel(x, ypx, Textures[l].GetPixel(x, ypx)); } } } SkewedTexture = MergedTextures; y = 0; break; case 5: Textures = new Bitmap[4]; MergedTextures = new Bitmap(SkewedTexture.Width, SkewedTexture.Height); for (int l = 0; l < 4; l++) { Textures[l] = new Bitmap(SkewedTexture.Width, SkewedTexture.Height); } for (int l = 0; l < 4; l++) { for (int x = 0; x < textures[i].Width; x++) { for (int ypx = 0; ypx < textures[i].Height; ypx++) { Point PixelLocation = IntegerCalculateIsometricProjection(textures[i].Width * 2, 0, x, 0, ypx, l); PixelLocation.X = Math.Max(Math.Min(PixelLocation.X, SkewedTexture.Width - 1), 0); PixelLocation.Y = Math.Max(Math.Min(textures[i].Height + PixelLocation.Y, SkewedTexture.Height - 1), 0); Textures[l].SetPixel(PixelLocation.X, PixelLocation.Y, textures[i].GetPixel(x, ypx)); } } } for (int l = 0; l < 4; l++) { int Startx = 0; int Starty = 0; int endX = Textures[l].Width; int endY = Textures[l].Height; switch (l) { case 1: Startx = Textures[l].Width / 2; Starty = Textures[l].Height / 2; endX = Textures[l].Width; endY = (Textures[l].Height / 4) * 3; break; case 0: Startx = Textures[l].Width / 2; Starty = (Textures[l].Height / 4) * 3; endX = Textures[l].Width; endY = Textures[l].Height; break; case 2: Startx = 0; Starty = Textures[l].Height / 2; endX = Textures[l].Width / 2; endY = (Textures[l].Height / 4) * 3; break; case 3: Startx = 0; Starty = (Textures[l].Height / 4)*3; endX = Textures[l].Width / 2; endY = Textures[l].Height; break; } for (int x = Startx; x < endX; x++) { for (int ypx = Starty; ypx < endY; ypx++) { MergedTextures.SetPixel(x, ypx, Textures[l].GetPixel(x, ypx)); } } } SkewedTexture = MergedTextures; y = 0; break; } GeneratedFaceTextures[i, face] = SkewedTexture; } } } } public class Voxel { int VoxelNum; int[] faces = new int[6]; int[] DrawOrder = new int[] { 5, 0, 3, 1, 2, 4 }; int ModelType = 0; int ModelNum = -1; bool Cull = false; public int[] getFaces() { return faces; } public int[] GetDrawOrder() { return DrawOrder; } public int GetModelNum() { return ModelNum; } public int GetModelType() { return ModelType; } public int GetVoxelNum() { return VoxelNum; } 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) { this.ModelNum = ModelNum; } public Voxel(int NorthFace, int SouthFace, int EastFace, int WestFace, int UpFace, int DownFace) { faces = new int[] {NorthFace, SouthFace, EastFace, WestFace, UpFace, DownFace}; VoxelNum = TextureManager.VoxelCount; TextureManager.VoxelCount++; } virtual public void Draw(Graphics g, int x, int y, int WorldX, int WorldY, int WorldZ, int ShadowLevel, bool BlocksLight) { for (int i = 0; i < DrawOrder.Length; i++) { if (DrawOrder[i] > 0 && DrawOrder[i] < 6 && faces[DrawOrder[i]] >= 0) { if (ModelNum == -1) { Bitmap Side; switch (ModelType) { 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], WorldX, WorldY,WorldZ, ShadowLevel, BlocksLight)); break; default: switch (DrawOrder[i]) { 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, WorldX, WorldY, WorldZ, ShadowLevel, BlocksLight)); 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, WorldX, WorldY, WorldZ, ShadowLevel, BlocksLight)); 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, WorldX, WorldY, WorldZ, ShadowLevel, BlocksLight)); 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, WorldX, WorldY, WorldZ, ShadowLevel, BlocksLight)); 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], WorldX, WorldY, WorldZ, ShadowLevel, BlocksLight)); break; } break; } } else { g.DrawImage(TextureManager.GetModel(ModelNum), x, y); } } } } public Bitmap GetRenderedSide(int FaceIndex) { return TextureManager.GetVoxelFaceBitmap(faces[FaceIndex], FaceIndex); } public int GetFace(int index) { return faces[index]; } public int GetNorthFace() { return faces[0]; } public int GetSouthFace() { return faces[1]; } public int GetEastFace() { return faces[2]; } public int GetWestFace() { return faces[3]; } public int GetUpFace() { return faces[4]; } public int GetDownFace() { return faces[5]; } public void SetFace(int face, int Texture) { faces[face] = Texture; } public void SetNorthFace(int Texture) { faces[0] = Texture; } public void SetSouthFace(int Texture) { faces[1] = Texture; } public void SetEastFace(int Texture) { faces[2] = Texture; } public void SetWestFace(int Texture) { faces[3] = Texture; } public void SetUpFace(int Texture) { faces[4] = Texture; } public void SetDownFace(int Texture) { 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, float offsetX, float offsetY, float 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) { RenderBlock(g, BlockID, WorldX, WorldY, WorldZ, ShadowLevel, false); } public static void RenderBlock(Graphics g, int BlockID,int WorldX, int WorldY, int WorldZ, int ShadowLevel, bool player) { int[] locationWOffset = GetDrawLocation(WorldX, WorldY, WorldZ, PlayerObject.GetCameraPos()[0], PlayerObject.GetCameraPos()[1], PlayerObject.GetCameraPos()[2]); int [] location = GetDrawLocation(WorldX, WorldY, WorldZ, 0,0,0); VoxelRegistry.FetchVoxel(blocks[BlockID].FetchVoxelID()).Draw(g, (player? location[0] : locationWOffset[0])+ (int)(Terrain4.width / (2 * TextureManager.RenderScale)), (player ? location[1] : locationWOffset[1]) + (int)(Terrain4.height / (2 * TextureManager.RenderScale)), WorldX, WorldY, WorldZ, ShadowLevel, FetchBlockState(BlockID).hasBlocksLight()); } 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; } } }